Plugin Directory

Changeset 3432542


Ignore:
Timestamp:
01/05/2026 09:03:56 AM (3 months ago)
Author:
divyeshk71
Message:

Release new version

Location:
automatic-articles-importer
Files:
22 added
9 edited

Legend:

Unmodified
Added
Removed
  • automatic-articles-importer/trunk/assets/css/style-admin.css

    r2904458 r3432542  
    1 .menu-icon-zl_ziai_article div img {
     1.menu-icon-zl_ziai_article div img{
    22    width: 100%;
    33    height: auto;
     
    5959}
    6060
    61 
    6261/* Tool tip icon start*/
    63 
    6462.hint {
    6563    display: inline-block;
     
    6967}
    7068
    71 
    7269/* background style for 'i' */
    73 
    7470.hint-icon {
    7571    background: #a5acb5;
     
    8682}
    8783
    88 
    8984/* hint icon hover style */
    90 
    9185.hint-icon:hover {
    9286    background: #1170be;
     
    9488}
    9589
    96 
    9790/* Displays the hint. important! Do not remove. */
    98 
    9991.hint:hover .hint-description,
    10092.hint:focus .hint-description {
     
    10294}
    10395
    104 
    10596/* position of the hint  */
    106 
    10797.hint-description {
    10898    display: none;
     
    124114}
    125115
    126 
    127116/* styling for the arrow */
    128 
    129117.hint-description:before,
    130118.hint-description:after {
     
    139127}
    140128
    141 
    142129/* overlay styling */
    143 
    144 
    145 /* .hint-description:after {
     130.hint-description:after {
    146131    left: 50%;
    147132    border-top-color: #f6f8fa;
    148133    bottom: -20px;
    149134}
    150 
    151 .lds-dual-ring.hidden {
    152     display: none;
    153 }
    154 
    155 .lds-dual-ring {
    156     display: inline-block;
    157     width: 80px;
    158     height: 80px;
    159 }
    160 
    161 .lds-dual-ring:after {
    162     content: " ";
    163     display: block;
    164     width: 64px;
    165     height: 64px;
    166     margin: 23%;
    167     margin-left: 51%;
    168     border-radius: 50%;
    169     border: 6px solid #fff;
    170     border-color: #fff transparent #fff transparent;
    171     animation: lds-dual-ring 1.2s linear infinite;
    172 }
    173 
    174 @keyframes lds-dual-ring {
    175     0% {
    176         transform: rotate(0deg);
    177     }
    178     100% {
    179         transform: rotate(360deg);
    180     }
    181 }
    182 
    183 .overlay {
    184     position: fixed;
    185     top: 0;
    186     left: 0;
    187     width: 100%;
    188     height: 100vh;
    189     background: rgba(0, 0, 0, .8);
    190     z-index: 999;
    191     opacity: 1;
    192     transition: all 0.5s;
    193 } */
    194 
    195 @keyframes spin {
    196     from {
    197         transform: rotate(0deg);
    198     }
    199     to {
    200         transform: rotate(360deg);
    201     }
    202 }
    203 
    204 .zl-spinner::before {
    205     content: '';
    206     position: absolute;
    207     background-color: #fbfbfc;
    208     top: 4px;
    209     left: 4px;
    210     width: 6px;
    211     height: 6px;
    212     border: none;
    213     border-radius: 100%;
    214     transform-origin: 8px 8px;
    215     animation-name: spin;
    216     animation-duration: 1000ms;
    217     animation-timing-function: linear;
    218     animation-iteration-count: infinite;
    219 }
    220 
    221 .zl-spinner {
    222     visibility: visible;
    223     display: inline-block;
    224     background-color: #23282d;
    225     opacity: 0.75;
    226     width: 24px;
    227     height: 24px;
    228     border: none;
    229     border-radius: 100%;
    230     padding: 0;
    231     margin: 0 24px;
    232     position: relative;
    233 }
  • automatic-articles-importer/trunk/assets/js/zl-admin-custom.js

    r2904458 r3432542  
    1 jQuery(document).ready(function($) {
    2     var post_type = $("select[name='zl_post_type_get']").val();
    3     var progressBar = $("#progressbar");
    4     var bulkImportBtn = $('#bulk-import');
     1jQuery(document).ready(function() {
     2    var post_type = jQuery("select[name='zl_post_type_get']").val();
    53    GetCategoryziai(post_type);
    64
    7     $("select[name='zl_post_type_get']").on("change", function() {
    8         var post_type = $(this).val();
     5    jQuery("select[name='zl_post_type_get']").on("change", function(){
     6        var post_type = jQuery(this).val();
    97        GetCategoryziai(post_type);
    108    });
    119
    12     $(progressBar).progressbar({
    13         value: 0
    14     });
    15 
    16     $(bulkImportBtn).click(function() {
    17         fireBulkImporter();
    18     });
    19 
    20 
    2110    function GetCategoryziai(post_type) {
    22         $.ajax({
     11        jQuery.ajax({
    2312            type: "POST",
    2413            url: wpAjax.ajaxUrl,
    25             data: { action: "ziai_category_get", post_type: post_type },
    26             beforeSend: function() {
    27                 $(".zl-ajax-loader").css({ display: "inline-block" });
     14            data: {
     15                action: "ziai_category_get",
     16                post_type: post_type,
     17                nonce: wpAjax.nonce
    2818            },
    29             success: function(result) {
    30                 $(".post_type_category").html(result);
    31                 $(".zl-ajax-loader").css({ display: "none" });
     19            beforeSend: function () {
     20                jQuery(".zl-ajax-loader").css({ display: "inline-block" });
    3221            },
    33         });
    34     }
    35 
    36     function fireBulkImporter() {
    37         let author = jQuery("#zl_default_author").val();
    38         let category = jQuery(".post_type_category [name='zl_taxonomie']").val();
    39         let api_key = jQuery("#ziai_access_token").val();
    40         $.ajax({
    41             type: "POST",
    42             url: wpAjax.ajaxUrl,
    43             data: { action: "ziai_bulk_importer_init", api_key: api_key, post_type: post_type, author: author, category: category },
    44             beforeSend: function() {
    45                 $(bulkImportBtn).toggleClass('disabled');
    46                 $(bulkImportBtn).prop('disabled', true);
    47                 $('.zl-button .submit span').addClass('zl-spinner');
     22            success: function (result) {
     23                jQuery(".post_type_category").html(result);
     24                jQuery(".zl-ajax-loader").css({ display: "none" });
    4825            },
    49             success: function(result) {
    50                 // makes it <result.pages.total_pages irl
    51                 result = JSON.parse(result);
    52                 let currentPage = result.pages.page + 1;
    53                 let maxPage = result.pages.total_pages; // set the maximum number of pages here
    54                 $(progressBar).progressbar({
    55                     value: (result.pages.page / result.pages.total_pages) * 100
    56                 });
    57 
    58                 function nextPage() {
    59                     if (currentPage <= maxPage) {
    60                         bulkImporterNextPage(currentPage, currentPage + 1, function() {
    61                             currentPage++;
    62                             nextPage();
    63                         });
    64                     }
    65                 }
    66 
    67                 nextPage();
    68             },
    69         });
    70     }
    71 
    72     function bulkImporterNextPage(currentPage, nextPage, callback) {
    73         $.ajax({
    74             type: "POST",
    75             url: wpAjax.ajaxUrl,
    76             data: {
    77                 action: "ziai_bulk_importer_get_page",
    78                 currentPage: currentPage,
    79                 nextPage: nextPage
    80             },
    81             success: function(result) {
    82                 result = JSON.parse(result);
    83                 if (callback) {
    84                     callback();
    85                 }
    86                 $(progressBar).progressbar({
    87                     value: (result.pages.page / result.pages.total_pages) * 100
    88                 });
    89                 if (result.pages.page === result.pages.total_pages) {
    90                     bulkImporterComplete();
    91                 }
    92             },
    93             error: function(jqXHR, textStatus, errorThrown) {
    94                 console.log("Error:", textStatus, errorThrown);
     26            error: function(xhr, status, error) {
     27                jQuery(".zl-ajax-loader").css({ display: "none" });
     28                console.error('AJAX Error:', error);
    9529            }
    9630        });
    9731    }
    98 
    99     function bulkImporterComplete() {
    100         $(bulkImportBtn).toggleClass('disabled');
    101         $(bulkImportBtn).prop('disabled', false);
    102         $('.zl-button .submit span').removeClass('zl-spinner');
    103         $(progressBar).progressbar({
    104             value: 0
    105         });
    106     }
    10732});
  • automatic-articles-importer/trunk/automatic-articles-importer.php

    r3189584 r3432542  
    55 * Description: This plugin is used to import articles from Intercom Service.
    66 * Plugin URI: https://www.zluck.com/
    7  * Version: 1.4
     7 * Version: 1.5
    88 * Author: Zluck Solutions
    99 * Author URI: https://profiles.wordpress.org/zluck
     
    2222include_once('classes/class-ziai-cron.php');
    2323
    24 $ZIAI_Modules  = new ZIAI_Modules();
    25 $ZIAI_Modules->ziai_init();
     24// Initialize plugin
     25ZIAI_Modules::ziai_init();
    2626ZIAI_CronJob::ziai_cron_init();
     27
     28// Register deactivation hook to clean up cron job
     29register_deactivation_hook(__FILE__, array('ZIAI_CronJob', 'ziai_cronstarter_deactivate'));
  • automatic-articles-importer/trunk/classes/class-admin-menu.php

    r2904458 r3432542  
    11<?php
    22class ZIAI_Modules {
    3     public function ziai_init() {
    4         add_action( 'admin_enqueue_scripts', array( $this, 'ziai_load_admin_style' ) );
    5         add_action( 'admin_menu', array( $this, 'ziai_register_menu_page' ) );
    6         add_action('wp_ajax_ziai_category_get', array( $this, 'ziai_category_get' ));
    7         add_action('wp_ajax_ziai_bulk_importer_init', array( $this, 'ziai_bulk_importer_init' ));
    8         add_action('wp_ajax_ziai_bulk_importer_get_page', array( $this, 'ziai_bulk_importer_get_page' ));
    9         add_action('wp_ajax_nopriv_ziai_category_get', array( $this, 'ziai_category_get' ));
    10         add_action('init', array( $this, 'ziai_register_post_type' ));
    11     }
    12 
    13     public function ziai_register_menu_page() {
     3    public static function ziai_init() {
     4        add_action( 'admin_enqueue_scripts', array( __CLASS__, 'ziai_load_admin_style' ) );
     5        add_action( 'admin_menu', array( __CLASS__, 'ziai_register_menu_page' ) );
     6        add_action('wp_ajax_ziai_category_get', array( __CLASS__, 'ziai_category_get' ));
     7        add_action('init', array( __CLASS__, 'ziai_register_post_type' ));
     8    }
     9
     10    public static function ziai_register_menu_page() {
    1411        add_submenu_page(
    1512            'edit.php?post_type=zl_ziai_article',
     
    1815            'manage_options',
    1916            'ziai-articles',
    20             array( $this, 'ziai_admin_show_data' )
    21         );
    22     }
    23 
    24     public function ziai_load_admin_style() {
    25         wp_enqueue_style( 'jquery-ui-progressbar', ZIAI_FILE_URL . 'assets/css/jquery-ui.css');
     17            array( __CLASS__, 'ziai_admin_show_data' )
     18        );
     19    }
     20
     21    public static function ziai_load_admin_style() {
    2622        wp_enqueue_style( 'ziai_admin_style_css', ZIAI_FILE_URL . 'assets/css/style-admin.css');
    27         wp_enqueue_script('jquery-ui-progressbar');
    2823        wp_enqueue_script('zl_admin_custom_script', ZIAI_FILE_URL . 'assets/js/zl-admin-custom.js', array('jquery'), '1.0', true);
    29         wp_localize_script('zl_admin_custom_script', 'wpAjax', array('ajaxUrl' => admin_url('admin-ajax.php')));
    30     }
    31 
    32     public function ziai_admin_show_data() {
     24        wp_localize_script('zl_admin_custom_script', 'wpAjax', array(
     25            'ajaxUrl' => admin_url('admin-ajax.php'),
     26            'nonce' => wp_create_nonce('ziai-ajax-nonce')
     27        ));
     28    }
     29
     30    public static function ziai_admin_show_data() {
     31        // Check user capabilities
     32        if (!current_user_can('manage_options')) {
     33            wp_die(__('You do not have sufficient permissions to access this page.', 'ziai-articles'));
     34        }
     35
    3336        if ( is_file(ZIAI_FILE_PATH . 'includes/wp-ziai-article-form.php') ) {
    34             $errormsg = false;
    3537            $token                      = get_option('zl_ziai_access_token');
    3638            $post_type                  = get_option('zl_post_type_get');
     
    4042            $cron_time                  = get_option('ziai_cron_time');
    4143            $cron_start_time            = get_option('ziai_cron_start_time');
     44            $errormsg                   = ''; // Initialize error message variable
     45
    4246            if(!empty($_POST) && (isset($_POST['runnow']) || isset($_POST['savechanges']))){
     47                // Verify nonce
     48                if (!isset($_POST['zl-ziai-settings']) || !wp_verify_nonce($_POST['zl-ziai-settings'], 'zl-ziai-settings-save')) {
     49                    wp_die(__('Security check failed. Please try again.', 'ziai-articles'));
     50                }
    4351                $ziai_access_token          = sanitize_text_field($_POST['ziai_access_token']);
    4452                $post_type                  = sanitize_text_field($_POST['zl_post_type_get']);
     
    4856                $cron_time_u                = sanitize_text_field($_POST['ziai_cron_time']);
    4957                $cron_time_u                = round(($cron_time_u * 2), 0) / 2;
    50                 if ($cron_start_time != $_POST['ziai_cron_start_time'] || $cron_time_u != $cron_time) {
     58                // Compare values properly (handle empty strings)
     59                $old_cron_start = $cron_start_time ? $cron_start_time : '';
     60                $new_cron_start = isset($_POST['ziai_cron_start_time']) ? sanitize_text_field($_POST['ziai_cron_start_time']) : '';
     61                $old_cron_time = $cron_time ? $cron_time : 0;
     62               
     63                if ($old_cron_start !== $new_cron_start || $old_cron_time != $cron_time_u) {
    5164                    ZIAI_CronJob::ziai_cronstarter_deactivate();
    5265                }
     
    6073                update_option('ziai_cron_start_time', $cron_start_time);
    6174                update_option('zl_default_author', $zl_default_author);
    62                 $errormsg = "Articles settings updated successfully!!";
    63                 if (isset($_POST['runnow'])) {
    64                     $arry = $this->get_ziai_options_array();
    65                     $importer_article   = new ZIAI_Handler($arry);
    66                     $response = $importer_article->sync_articles();
    67                     if($response){
    68                         if ($response['status'] == 'errors') {
    69                             $errormsg = $response['message'];
     75                // Validate required fields
     76                if (empty($ziai_access_token)) {
     77                    $errormsg = __('Error: Access Token is required.', 'ziai-articles');
     78                } elseif (empty($post_type)) {
     79                    $errormsg = __('Error: Post Type is required.', 'ziai-articles');
     80                } elseif (empty($zl_default_author)) {
     81                    $errormsg = __('Error: Default Author is required.', 'ziai-articles');
     82                } else {
     83                    $errormsg = __('Articles settings updated successfully!!', 'ziai-articles');
     84                    if (isset($_POST['runnow'])) {
     85                        // Validate access token before import
     86                        if (empty($token)) {
     87                            $errormsg = __('Error: Access Token is required to import articles.', 'ziai-articles');
    7088                        } else {
    71                             $errormsg = 'Message:- ' . $response['message'] . '<br>';
    72                             $errormsg .= 'Episodes:- ' . $response['count'] .' Article Import';
     89                            $arry = array(
     90                                "access_token" => $token,
     91                                "import_post_type" => $post_type,
     92                                "import_author" => $zl_default_author,
     93                                "import_taxonomy" => $taxonomies,
     94                            );
     95                            $importer_article   = new ZIAI_Handler($arry);
     96                            $response           = $importer_article->ziai_import_article();
     97                            if (isset($response['status']) && $response['status'] == 'errors') {
     98                                $errormsg = esc_html($response['message']);
     99                            } elseif (isset($response['status']) && $response['status'] == 'success') {
     100                                $errormsg = __('Message:- ', 'ziai-articles') . esc_html($response['message']) . '<br>';
     101                                $errormsg .= __('Episodes:- ', 'ziai-articles') . esc_html($response['count']) . __(' Article Import', 'ziai-articles');
     102                            } else {
     103                                $errormsg = __('An unexpected error occurred during import.', 'ziai-articles');
     104                            }
    73105                        }
    74106                    }
     
    79111    }
    80112
    81     public function get_ziai_options_array(){
    82         $token                      = get_option('zl_ziai_access_token');
    83         $post_type                  = get_option('zl_post_type_get');
    84         $taxonomies                 = get_option('zl_taxonomy_get');
    85         $zl_default_author          = get_option('zl_default_author');
    86         return array(
    87             "access_token" => $token,
    88             "import_post_type" => $post_type,
    89             "import_author" => $zl_default_author,
    90             "import_taxonomy" => $taxonomies,
    91         );
    92     }
    93 
    94113    //Post Category Get
    95     public function ziai_category_get()
     114    public static function ziai_category_get()
    96115    {
     116        // Check user capabilities
     117        if (!current_user_can('manage_options')) {
     118            wp_send_json_error(array('message' => __('You do not have sufficient permissions.', 'ziai-articles')));
     119            return;
     120        }
     121
     122        // Verify nonce if provided
     123        if (isset($_POST['nonce']) && !wp_verify_nonce($_POST['nonce'], 'ziai-ajax-nonce')) {
     124            wp_send_json_error(array('message' => __('Security check failed.', 'ziai-articles')));
     125            return;
     126        }
     127
    97128        $post_type      = 'zl_ziai_article';
    98129        $taxonomies     = get_object_taxonomies($post_type, 'objects');
    99130        $category       = get_option('zl_category_get');
    100         echo '<label for="zl_get_post_type_category"><b>Category</b></label>';
    101         echo '<select name="zl_category_get"><option value="">Select</option>';
     131       
     132        if (empty($taxonomies)) {
     133            echo '<label for="zl_get_post_type_category"><b>' . esc_html__('Category', 'ziai-articles') . '</b></label>';
     134            echo '<select name="zl_category_get"><option value="">' . esc_html__('No taxonomies available', 'ziai-articles') . '</option></select>';
     135            wp_die();
     136        }
     137
     138        // Get first taxonomy key (PHP 7.3+ compatible with fallback for PHP 7.0-7.2)
     139        if (function_exists('array_key_first')) {
     140            $taxonomy_key = array_key_first($taxonomies);
     141        } else {
     142            $taxonomy_keys = array_keys($taxonomies);
     143            $taxonomy_key = !empty($taxonomy_keys) ? $taxonomy_keys[0] : null;
     144        }
     145       
     146        if (empty($taxonomy_key)) {
     147            echo '<label for="zl_get_post_type_category"><b>' . esc_html__('Category', 'ziai-articles') . '</b></label>';
     148            echo '<select name="zl_category_get"><option value="">' . esc_html__('No taxonomies available', 'ziai-articles') . '</option></select>';
     149            wp_die();
     150        }
     151        echo '<label for="zl_get_post_type_category"><b>' . esc_html__('Category', 'ziai-articles') . '</b></label>';
     152        echo '<select name="zl_category_get"><option value="">' . esc_html__('Select', 'ziai-articles') . '</option>';
     153       
    102154        $terms = get_terms(array(
    103             'taxonomy' => array_key_first($taxonomies),
     155            'taxonomy' => $taxonomy_key,
    104156            'hide_empty' => false,
    105157        ));
    106         foreach ($terms as $term) {
    107             ?>
    108             <option value="<?php echo esc_attr($term->term_id); ?>" <?php if ($term->term_id == $category) { echo "selected"; } ?>><?php echo esc_html($term->name); ?></option>
    109             <?php
    110         }
    111         echo '</select><input type="hidden" name="zl_taxonomie" value=' . array_key_first($taxonomies) . '>';
    112         die();
    113     }
    114 
    115     public function ziai_bulk_importer_init()
    116     {
    117         update_option('zl_taxonomy_get', $_POST['category']);
    118         update_option('zl_default_author', $_POST['author']);
    119         update_option('zl_post_type_get', $_POST['post_type']);
    120         update_option('zl_ziai_access_token', $_POST['api_key']);
    121        
    122         $arry = $this->get_ziai_options_array();
    123         $importer_article   = new ZIAI_Handler($arry);
    124         $response = $importer_article->get_articles(1, 25);
    125         echo json_encode($response);
    126         die();
    127     }
    128 
    129     public function ziai_bulk_importer_get_page()
    130     {
    131         $page = $_POST['currentPage'];
    132         $arry = $this->get_ziai_options_array();
    133         $importer_article   = new ZIAI_Handler($arry);
    134         $response = $importer_article->get_articles($page, 25);
    135         echo json_encode($response);
    136         die();
     158
     159        if (!is_wp_error($terms) && !empty($terms)) {
     160            foreach ($terms as $term) {
     161                $selected = ($term->term_id == $category) ? 'selected' : '';
     162                echo '<option value="' . esc_attr($term->term_id) . '" ' . $selected . '>' . esc_html($term->name) . '</option>';
     163            }
     164        }
     165       
     166        echo '</select><input type="hidden" name="zl_taxonomie" value="' . esc_attr($taxonomy_key) . '">';
     167        wp_die();
    137168    }
    138169
    139170    //custom Post Type
    140     public function ziai_register_post_type()
     171    public static function ziai_register_post_type()
    141172    {
    142173        $cpt_name = 'Automatic Articles Importer';
     
    173204            'hierarchical' => true,
    174205        );
    175 
    176206        register_post_type('zl_ziai_article', $args);
    177 
    178207        $taxonomys = 'Collection';
    179208        $taxonomyp = 'Collections';
     
    192221            'not_found' => __('No ' . $taxonomys . ' found.'),
    193222        );
    194 
    195223        register_taxonomy('article-cat', array('zl_ziai_article'), array(
    196224            'hierarchical' => true,
  • automatic-articles-importer/trunk/classes/class-article-import.php

    r2904458 r3432542  
    1717    }
    1818
    19     public function sync_articles(){
    20         $response = $this->get_articles(1, 25);
    21         if($response['pages']['total_pages'] > 1){
    22             $this->get_more_articles($response['pages']['page']+1, $response['pages']['total_pages']);
    23         }
    24     }
    25 
    26     private function get_more_articles($current_page, $max_page) {
    27         if ($current_page > $max_page) {
    28             return;
    29         }
    30 
    31         $this->get_articles($current_page, $current_page + 1,
    32             function($current_page, $max_page) {
    33                 $current_page++;
    34                 $this->get_more_articles($current_page, $max_page);
    35             });
    36     }
    37 
    38     public function get_articles($page=null, $per_page=null, $callback=false){
    39         $endpoint = 'https://api.intercom.io/articles/';
    40         $params = http_build_query(array(
    41             'page' => $page,
    42             'per_page' => $per_page
    43         ));
    44         if($params){
    45             $endpoint .= '?'.$params;
    46         }
    47         $response = wp_remote_get( $endpoint,
     19    public function ziai_import_article()
     20    {
     21        // Validate required fields
     22        if (empty($this->access_token)) {
     23            return array(
     24                'status'   => 'errors',
     25                'message'  => __('Access token is required.', 'ziai-articles'),
     26            );
     27        }
     28
     29        if (empty($this->post_type)) {
     30            return array(
     31                'status'   => 'errors',
     32                'message'  => __('Post type is required.', 'ziai-articles'),
     33            );
     34        }
     35
     36        if (empty($this->taxonomy)) {
     37            return array(
     38                'status'   => 'errors',
     39                'message'  => __('Taxonomy is required.', 'ziai-articles'),
     40            );
     41        }
     42
     43        // Make API request to get articles
     44        $response = wp_remote_get( 'https://api.intercom.io/articles/?per_page=-1',
    4845            array(
    4946                'method' => 'GET',
     47                'timeout' => 30,
    5048                'headers' => array(
    51                     'Authorization' => 'Bearer ' . $this->access_token
     49                    'Authorization' => 'Bearer ' . $this->access_token,
     50                    'Accept' => 'application/json'
    5251                )
    5352            )
    5453        );
    5554
    56         $response = json_decode($response['body'], true);
    57         if(!isset($response['errors'])){
    58             $imported_articles = $this->ziai_import_articles($response);
    59             if($callback){
    60                 $callback($page, $response['pages']['total_pages']);
    61             } else {
    62                 return $response;
    63             }
    64         } else {
    65             return array(
    66                 'status'   => 'errors',
    67                 'message'  => $response['errors'][0]->message,
    68             );
    69         }
    70     }
    71 
    72     public function ziai_import_articles($response)
    73     {
    74         if (isset($response['errors'])) {
    75             return [
    76                 'status' => 'errors',
    77                 'message' => $response['errors'][0]->message,
    78             ];
    79         } else {
    80             // Get collection data from api for given article parent_id
    81             foreach($response['data'] as $data){
    82                 // If the article exists, we don't need to call for the collection data
    83                 $article_exists = $this->is_article_imported($data['id']);
    84                 $collection = null;
    85                 if(!$article_exists){
    86                     $this->create_update_article($data);
    87                 } else {
    88                     //$article_wp_object = get_post($article_exists);
    89                     $out_of_date = $this->is_article_outdated($article_exists, $data);
    90                     if($out_of_date){
    91                         $this->create_update_article($data);
     55        // Check for wp_remote_get errors
     56        if (is_wp_error($response)) {
     57            return array(
     58                'status'   => 'errors',
     59                'message'  => sprintf(__('API request failed: %s', 'ziai-articles'), $response->get_error_message()),
     60            );
     61        }
     62
     63        // Check HTTP response code
     64        $response_code = wp_remote_retrieve_response_code($response);
     65        if ($response_code !== 200) {
     66            $response_message = wp_remote_retrieve_response_message($response);
     67            return array(
     68                'status'   => 'errors',
     69                'message'  => sprintf(__('API request failed with status code %d: %s', 'ziai-articles'), $response_code, $response_message),
     70            );
     71        }
     72
     73        // Get response body
     74        $response_body = wp_remote_retrieve_body($response);
     75        if (empty($response_body)) {
     76            return array(
     77                'status'   => 'errors',
     78                'message'  => __('Empty response from API.', 'ziai-articles'),
     79            );
     80        }
     81
     82        // Decode JSON response
     83        $response_data = json_decode($response_body, true);
     84        if (json_last_error() !== JSON_ERROR_NONE) {
     85            return array(
     86                'status'   => 'errors',
     87                'message'  => sprintf(__('Invalid JSON response: %s', 'ziai-articles'), json_last_error_msg()),
     88            );
     89        }
     90
     91        // Check for API errors
     92        if (isset($response_data['errors']) && !empty($response_data['errors'])) {
     93            $error_message = is_array($response_data['errors']) && isset($response_data['errors'][0]['message'])
     94                ? $response_data['errors'][0]['message']
     95                : (is_object($response_data['errors'][0]) && isset($response_data['errors'][0]->message)
     96                    ? $response_data['errors'][0]->message
     97                    : __('Unknown API error', 'ziai-articles'));
     98            return array(
     99                'status'   => 'errors',
     100                'message'  => esc_html($error_message),
     101            );
     102        }
     103
     104        // Check if data exists
     105        if (!isset($response_data['data']) || !is_array($response_data['data'])) {
     106            return array(
     107                'status'   => 'errors',
     108                'message'  => __('No articles data found in API response.', 'ziai-articles'),
     109            );
     110        }
     111
     112        // Process each article
     113        foreach($response_data['data'] as $data){
     114            // Validate required data fields
     115            if (!isset($data['id']) || !isset($data['title']) || !isset($data['body'])) {
     116                continue; // Skip invalid articles
     117            }
     118
     119            // Get collection information if parent_id exists
     120            $collection_name = '';
     121            if (isset($data['parent_id']) && !empty($data['parent_id'])) {
     122                $collection_response = wp_remote_get( 'https://api.intercom.io/help_center/collections/' . absint($data['parent_id']),
     123                    array(
     124                        'method' => 'GET',
     125                        'timeout' => 15,
     126                        'headers' => array(
     127                            'Authorization' => 'Bearer ' . $this->access_token,
     128                            'Accept' => 'application/json'
     129                        )
     130                    )
     131                );
     132
     133                if (!is_wp_error($collection_response)) {
     134                    $collection_code = wp_remote_retrieve_response_code($collection_response);
     135                    if ($collection_code === 200) {
     136                        $collection_body = wp_remote_retrieve_body($collection_response);
     137                        $collection_data = json_decode($collection_body, true);
     138                        if (isset($collection_data['name'])) {
     139                            $collection_name = sanitize_text_field($collection_data['name']);
     140                        }
    92141                    }
    93142                }
    94             }
    95             return array(
    96                 'status'   => 'success',
    97                 'message'  => 'Articles settings updated successfully!!',
    98                 'count'    => $this->ziai_added,
    99                 'episodes' => $this->ziai_imported,
    100             );
    101         }
    102     }
    103 
    104     /**
    105      * Checks if a WordPress article is out of date with Intercom.
    106      *
    107      * @param int $article_id The post ID of the article.
    108      * @param array $data An array of article data from the Intercom API.
    109      * @return bool True if the article is outdated, false otherwise.
    110      */
    111     public function is_article_outdated(int $article_id, array $data): bool {
    112         $article_wp_date_modified = get_post_meta($article_id, 'intercom_updated_at', true);
    113         return ($data['updated_at'] > $article_wp_date_modified);
    114     }
    115 
    116     public function get_collection_data($data){
    117         $collection = wp_remote_get( 'https://api.intercom.io/help_center/collections/'.$data['parent_id'].'',
    118             array(
    119                 'method' => 'GET',
    120                 'headers' => array(
    121                     'Authorization' => 'Bearer ' . $this->access_token
    122                 )
    123             )
    124         );
    125 
    126         return json_decode($collection['body'], true);
    127     }
    128 
    129     /**
    130      * Checks to see if an article has been previously imported into WordPress
    131      *
    132      * @param int $article_id The article_id as provided by the Intercom API
    133      * @return int|bool The post ID of the matching post if found, or false if not found.
    134      */
    135     public function is_article_imported(int $article_id): bool|int
    136     {
    137         $exists = false;
     143                // Add small delay to avoid rate limiting
     144                usleep(100000); // 0.1 second delay
     145            }
     146
     147            $article = array(
     148                'id' => absint($data['id']),
     149                'title' => sanitize_text_field($data['title']),
     150                'content' => wp_kses_post($data['body']),
     151                'status'  => 'published', // Always set to published for imported articles
     152                'collection' => $collection_name
     153            );
     154            $this->ziai_create_article($article);
     155        }
     156
     157        return array(
     158            'status'   => 'success',
     159            'message'  => __('Articles imported successfully!!', 'ziai-articles'),
     160            'count'    => $this->ziai_added,
     161            'episodes' => $this->ziai_imported,
     162        );
     163    }
     164
     165    protected function ziai_create_article($article)
     166    {
     167        $post_data = $this->ziai_get_post_data($article);
     168       
     169        // Validate post data
     170        if (empty($post_data) || !is_array($post_data)) {
     171            return;
     172        }
     173
     174        // Insert or update post
     175        $post_id = wp_insert_post($post_data, true);
     176       
     177        /**
     178         * If an error occurred adding a post, continue the loop
     179         */
     180        if (is_wp_error($post_id)) {
     181            return;
     182        }
     183
     184        // Force status to published (in case wp_insert_post didn't update it for existing posts)
     185        wp_update_post(array(
     186            'ID' => $post_id,
     187            'post_status' => 'publish'
     188        ));
     189
     190        // Set post terms if category exists
     191        if (!empty($post_data['post_category']) && is_array($post_data['post_category']) && isset($post_data['post_category'][0])) {
     192            $term_result = wp_set_post_terms($post_id, array($post_data['post_category'][0]), $this->taxonomy);
     193            if (is_wp_error($term_result)) {
     194                // Log error but continue
     195                error_log('ZIAI: Failed to set post terms - ' . $term_result->get_error_message());
     196            }
     197        }
     198       
     199        $this->ziai_added++;
     200        if (isset($post_data['post_title'])) {
     201            $this->ziai_imported[] = $post_data['post_title'];
     202        }
     203    }
     204
     205    public function ziai_get_post_data($article)
     206    {
     207        // Validate article data
     208        if (empty($article['id']) || empty($article['title'])) {
     209            return false;
     210        }
     211
     212        // Handle collection/term creation
     213        $term_id = 0;
     214        if (!empty($article['collection'])) {
     215            $term_a = term_exists($article['collection'], $this->taxonomy);
     216           
     217            if ($term_a && isset($term_a['term_id'])) {
     218                $term_id = $term_a['term_id'];
     219            } else {
     220                // Create new term if it doesn't exist
     221                $term_result = wp_insert_term(
     222                    sanitize_text_field($article['collection']),
     223                    $this->taxonomy,
     224                    array(
     225                        'slug' => sanitize_title($article['collection']),
     226                    )
     227                );
     228
     229                if (!is_wp_error($term_result) && isset($term_result['term_id'])) {
     230                    $term_id = $term_result['term_id'];
     231                } else {
     232                    // If term creation failed, try to get it again
     233                    $term_a = term_exists($article['collection'], $this->taxonomy);
     234                    if ($term_a && isset($term_a['term_id'])) {
     235                        $term_id = $term_a['term_id'];
     236                    }
     237                }
     238            }
     239        }
     240
     241        // Always set status to published for imported articles
     242        $status = 'publish';
     243
     244        // Prepare post data
     245        $post_data = array();
     246        $post_data['post_content']  = isset($article['content']) ? wp_kses_post($article['content']) : '';
     247        $post_data['post_title']    = sanitize_text_field($article['title']);
     248        $post_data['post_status']   = $status;
     249        $post_data['post_author']   = !empty($this->post_author) ? absint($this->post_author) : get_current_user_id();
     250        $post_data['post_type']     = $this->post_type;
     251        $post_data['meta_input']    = array(
     252            'zl_ziai_id' => absint($article['id']),
     253        );
     254
     255        // Add category if term exists
     256        if ($term_id > 0) {
     257            $post_data['post_category'] = array($term_id);
     258        }
     259
     260        // Check if post already exists
    138261        $args = array(
    139262            'post_type' => $this->post_type,
    140             'post_status' => array('publish', 'draft'),
     263            'post_status' => array('publish', 'draft', 'pending', 'private'),
     264            'posts_per_page' => 1,
    141265            'meta_query' => array(
    142266                array(
    143267                    'key' => 'zl_ziai_id',
    144                     'value' => $article_id,
     268                    'value' => absint($article['id']),
    145269                    'compare' => '=',
    146270                )
     
    148272        );
    149273
    150         $already_added = get_posts($args);
    151         if ($already_added) {
    152             $exists = $already_added[0]->ID;
    153         }
    154 
    155         return $exists;
    156     }
    157 
    158     public function create_update_article($data){
    159         if(isset($data['parent_id'])){
    160             $collection = $this->get_collection_data($data);
    161         }
    162 
    163         $article = array(
    164             'id' => $data['id'],
    165             'title' => $data['title'],
    166             'content' => $data['body'],
    167             'status'  => $data['state'],
    168             'created_at'  => $data['created_at'],
    169             'updated_at'  => $data['updated_at'],
    170             'collection'=> $collection['name'] ?? null,
    171         );
    172 
    173         $this->ziai_create_article($article);
    174     }
    175     protected function ziai_create_article($article)
    176     {
    177         $post_data  = $this->ziai_get_post_data($article);
    178         $post_id    = wp_insert_post($post_data);
    179         /**
    180          * If an error occurring adding a post, continue the loop
    181          */
    182         if (is_wp_error($post_id)) {
    183             return;
    184         }
    185 
    186         if (!empty($post_data['post_category'])) {
    187             wp_set_post_terms($post_id, $post_data['post_category'][0], $this->taxonomy);
    188         }
    189        
    190         $this->ziai_added++;
    191         $this->ziai_imported[] = $post_data['post_title'];
    192     }
    193 
    194 
    195     public function ziai_get_post_data($article)
    196     {
    197         $term_a = false;
    198         $post_data                  = array();
    199         if(isset($article['collection'])){
    200             $term_a      = term_exists($article['collection'], $this->taxonomy);
    201             $term_a_id   = $term_a['term_id'];
    202             if(empty($term_a_id)){
    203                 wp_insert_term(
    204                     $article['collection'],
    205                     $this->taxonomy,
    206                     array(
    207                         // 'description'=> 'Some description.',
    208                         'slug' => str_replace(" ", "-", $article['collection']),
    209                     )
    210                 );
    211             }
    212             $term_a                     = term_exists($article['collection'], $this->taxonomy);
    213             $term_id                    = $term_a['term_id'];
    214             $post_data['post_category'] = array($term_id);
    215         }
    216 
    217         $status                     = ($article['status'] == 'published') ? $article['status'] = 'publish' : $article['status'];
    218         $post_data['post_content']  = $article['content'];
    219         $post_data['post_title']    = $article['title'];
    220         $post_data['post_status']   = $status;
    221         $post_data['post_author']   = $this->post_author;
    222         $post_data['post_type']     = $this->post_type;
    223 
    224         $post_data['meta_input']    = array(
    225             'zl_ziai_id' => $article['id'],
    226             'intercom_created_at' => $article['created_at'],
    227             'intercom_updated_at' => $article['updated_at']
    228         );
    229 
    230         // Find the article in WP
    231         $article_exists = $this->is_article_imported($article['id']);
    232         if($article_exists ){
    233             $post_data['ID'] = $article_exists;
     274        $already_added = new WP_Query($args);
     275        if ($already_added->have_posts()) {
     276            $already_added->the_post();
     277            $post_id = get_the_ID();
     278            $post_data['ID'] = $post_id;
     279            wp_reset_postdata();
    234280        }
    235281
  • automatic-articles-importer/trunk/classes/class-ziai-cron.php

    r2904458 r3432542  
    1212        // create a scheduled event (if it does not exist already)
    1313        $cron_start_time = get_option('ziai_cron_start_time');
    14         if ($cron_start_time == '') {
     14        $cron_time = get_option('ziai_cron_time');
     15       
     16        if (empty($cron_start_time) || empty($cron_time) || $cron_time <= 0) {
    1517            self::ziai_cronstarter_deactivate();
    1618            return false;
    1719        }
    18         $schedule_at = strtotime($cron_start_time);
     20       
     21        // Calculate schedule time - combine today's date with the time
     22        $schedule_at = strtotime('today ' . $cron_start_time);
     23       
     24        // If the time has already passed today, schedule for tomorrow
     25        if ($schedule_at < time()) {
     26            $schedule_at = strtotime('tomorrow ' . $cron_start_time);
     27        }
     28       
     29        if ($schedule_at === false) {
     30            return false;
     31        }
     32       
    1933        if (!wp_next_scheduled('zl_ziai_cronjobs')) {
    2034            wp_schedule_event($schedule_at, 'zl_ziai_cron', 'zl_ziai_cronjobs');
     
    2842        $timestamp = wp_next_scheduled('zl_ziai_cronjobs');
    2943        // unschedule previous event if any
    30         wp_unschedule_event($timestamp, 'zl_ziai_cronjobs');
     44        if ($timestamp !== false) {
     45            wp_unschedule_event($timestamp, 'zl_ziai_cronjobs');
     46        }
    3147    }
    3248
     
    4056            return false;
    4157        }
    42         $cron_time = (($cron_time * 60) * 60);
    43         // Adds once every minute to the existing schedules.
     58        $cron_time = (($cron_time * 60) * 60); // Convert hours to seconds
     59        // Adds custom interval to the existing schedules.
    4460        $schedules['zl_ziai_cron'] = array(
    45             'interval' => $cron_time,
    46             'display' => __('Zluck ZIAI Cron')
     61            'interval' => absint($cron_time),
     62            'display' => __('Zluck ZIAI Cron', 'ziai-articles')
    4763        );
    4864        return $schedules;
     
    5268    public static function ziai_get_articles_using_cron()
    5369    {
    54         $ZIAI_Modules = new ZIAI_Modules();
    55         $arry = $ZIAI_Modules->get_ziai_options_array();
    56         $importer_article   = new ZIAI_Handler($arry);
    57         $importer_article->sync_articles();
     70        // Validate required options before running import
     71        $token = get_option('zl_ziai_access_token');
     72        $post_type = get_option('zl_post_type_get');
     73        $taxonomies = get_option('zl_taxonomy_get');
     74        $zl_default_author = get_option('zl_default_author');
     75       
     76        // Only run if required settings are configured
     77        if (empty($token) || empty($post_type) || empty($taxonomies)) {
     78            error_log('ZIAI Cron: Required settings are missing. Skipping import.');
     79            return;
     80        }
     81       
     82        $arry = array(
     83            "access_token" => $token,
     84            "import_post_type" => $post_type,
     85            "import_author" => $zl_default_author,
     86            "import_taxonomy" => $taxonomies,
     87        );
     88       
     89        $importer_article = new ZIAI_Handler($arry);
     90        $result = $importer_article->ziai_import_article();
     91       
     92        // Log result for debugging
     93        if (isset($result['status']) && $result['status'] === 'errors') {
     94            error_log('ZIAI Cron Error: ' . $result['message']);
     95        } elseif (isset($result['status']) && $result['status'] === 'success') {
     96            error_log('ZIAI Cron Success: Imported ' . $result['count'] . ' articles');
     97        }
    5898    }
    5999}
  • automatic-articles-importer/trunk/includes/wp-ziai-article-form.php

    r2904458 r3432542  
    11<div class="wrap wp-ziai-article">
    22    <h1><?php esc_html_e('Automatic Articles Settings', 'ziai-articles'); ?></h1>
    3     <?php
    4     // wp_enqueue_script('jquery-ui-progressbar');
    5     // $wp_scripts = wp_scripts();
    6     // wp_enqueue_style('plugin_name-admin-ui-css',
    7     //     'http://ajax.googleapis.com/ajax/libs/jqueryui/' . $wp_scripts->registered['jquery-ui-core']->ver . '/themes/smoothness/jquery-ui.css',
    8     //     false,
    9     //     1.0,
    10     //     false);
    11     ?>
    123    <div id="wpbody" role="main">
    134        <div id="wpbody-content">
     
    1910                        <div class="col-wrap">
    2011                            <div class="form-wrap">
    21                                 <?php
    22                                 if($errormsg) { ?>
    23                                     <p><?php _e($errormsg, 'ziai-articles'); ?></p>
    24                                 <?php
    25                                 }
    26                                 ?>
     12                                <?php if (!empty($errormsg)): ?>
     13                                    <div class="notice notice-<?php echo (strpos($errormsg, 'Error') !== false || strpos($errormsg, 'failed') !== false) ? 'error' : 'success'; ?> is-dismissible">
     14                                        <p><?php echo wp_kses_post($errormsg); ?></p>
     15                                    </div>
     16                                <?php endif; ?>
    2717                                <form method="post" action="" class="validate zl-admin-form">
    28                                     <div id="loader" class="lds-dual-ring hidden overlay"></div>
    29                                     <div id="progressbar"></div><br>
    3018                                    <div class="zl-ziai-setting zl-setting-2">
    3119                                        <div class="form-field form-required term-name-wrap">
     
    3624                                                </div>
    3725                                            </label>
    38                                             <input name="ziai_access_token" id="ziai_access_token" type="text" <?php if($token != ''){ ?> value="<?php echo esc_attr($token); ?>" <?php } ?> aria-required="true" required/>
    39                                         </div>
    40                                         <br>
    41                                         <div class="zl-button">
    42                                             <!-- <div style="padding: 10px 0;"> -->
    43                                             <p class="submit">
    44                                                 <button type="button" class="button button-primary" id="bulk-import">Import All</button><span class=""></span>
    45                                             </p>
    46                                             <!-- </div> -->
    47                                             <div></div>
     26                                            <input name="ziai_access_token" id="ziai_access_token" type="text" value="<?php echo esc_attr($token ? $token : ''); ?>" aria-required="true" required/>
    4827                                        </div>
    4928                                    </div>
     
    6645                                        <div class="form-field form-required term-name-wrap">
    6746                                            <label for="zl_anchor_default_author"><b><?php esc_html_e('Assign Imported Article to', 'ziai-articles'); ?></b></label>
    68                                             <?php wp_dropdown_users(array('name' => 'zl_default_author', 'selected' => $zl_default_author)); ?>
     47                                            <?php wp_dropdown_users(array('name' => 'zl_default_author', 'selected' => $zl_default_author ? $zl_default_author : get_current_user_id())); ?>
    6948                                        </div>
    7049                                        <div class="form-field form-required term-name-wrap">
     
    7554                                                </div>
    7655                                            </label>
    77                                             <input name="ziai_cron_start_time" id="ziai_cron_start_time" type="time" value="<?php echo esc_attr($cron_start_time); ?>" aria-required="true" required="required" />
     56                                            <input name="ziai_cron_start_time" id="ziai_cron_start_time" type="time" value="<?php echo esc_attr($cron_start_time ? $cron_start_time : ''); ?>" aria-required="true" required="required" />
    7857                                        </div>
    7958                                        <div class="form-field form-required term-name-wrap">
     
    8463                                                </div>
    8564                                            </label>
    86                                             <input name="ziai_cron_time" id="ziai_cron_time" type="number" min="0" step="any" value="<?php echo esc_attr($cron_time); ?>" aria-required="true" required="required" />
     65                                            <input name="ziai_cron_time" id="ziai_cron_time" type="number" min="0" step="any" value="<?php echo esc_attr($cron_time ? $cron_time : ''); ?>" aria-required="true" required="required" />
    8766                                        </div>
    8867                                        <br>
     
    9170                                            wp_nonce_field('zl-ziai-settings-save', 'zl-ziai-settings');
    9271                                            submit_button('Save Changes', 'primary', 'savechanges');
    93                                             //submit_button('Save & Run Now', 'primary runnow', 'runnow');
     72                                            submit_button('Save & Run Now', 'primary runnow', 'runnow');
    9473                                            ?>
    95                                             <!-- <div style="padding: 10px 0;"> -->
    96                                             <!-- <p class="submit">
    97                                                 <button type="button" class="button button-primary" id="bulk-import">Import All</button><span class=""></span>
    98                                             </p> -->
    99                                             <!-- </div> -->
    10074                                            <div></div>
    10175                                        </div>
    10276                                    </div>
    10377                                </form>
    104                                 <!-- <div>
    105                                     <h5>Bulk Importer</h5>
    106                                     <div id="progressbar"></div>
    107                                     <div style="padding: 10px 0;">
    108                                         <button class="button button-primary" id="bulk-import">Import All</button>
    109                                     </div>
    110                                 </div> -->
    11178                            </div>
    11279                        </div>
  • automatic-articles-importer/trunk/readme.txt

    r3189584 r3432542  
    11    === Automatic Articles Importer ===
    2     Contributors: zluck, divyeshk71, robcruiz
     2    Contributors: zluck, divyeshk71
    33    Donate link: https://www.buymeacoffee.com/zluck
    44    Tags: automatic articles importer, import intercom, sync intercom articles, sync articles
    55    Requires at least: 5.4
    6     Tested up to: 6.7
    7     Requires PHP: 7.4
    8     Stable tag: 1.0
     6    Tested up to: 6.9
     7    Requires PHP: 7.0
     8    Tested up to PHP: 8.3
     9    Stable tag: 1.5
    910    License: GPLv2 or later
    1011    License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    1415    == Description ==
    1516
    16     Automatic Articles Importer is an easy, quick and advanced plugin to import intercom articles.
    17 
    18     This plugin gives you ability to import the Intercom articles in your website easily. This plugin collects the intercom articles & collections from intercom and import on your website by collection wise.
     17    Automatic Articles Importer is an easy, quick and advanced plugin to import intercom articles. This plugin gives you ability to import the Intercom articles in your website easily. This plugin collects the intercom articles & collections from intercom and import on your website by collection wise.
    1918
    2019
    2120    == Key Features ==
    2221
    23     - Scheduled import process
    24     - Import intercom articles
    25     - Import intercom collection
    26     - Sync articles regulary by cron
     22    * Scheduled import process
     23    * Import intercom articles
     24    * Import intercom collection
     25    * Sync articles regulary by cron
    2726
    2827
     
    6564    == Changelog ==
    6665
    67     = 1.4 =
    68     * Minor bugs fixes
     66    = 1.5 =
     67    * Security: Added nonce verification for all form submissions
     68    * Security: Added capability checks for admin functions
     69    * Security: Removed nopriv AJAX hook for better security
     70    * Security: Added nonce to AJAX requests
     71    * Improvement: Enhanced error handling for API calls with proper validation
     72    * Improvement: Added comprehensive error checking for wp_remote_get responses
     73    * Improvement: Fixed nested API call error handling with rate limiting
     74    * Improvement: Fixed term creation error handling
     75    * Improvement: Fixed status assignment logic - all imported articles now default to published
     76    * Improvement: Added proper input validation for required fields
     77    * Improvement: Improved escaping and sanitization throughout
     78    * Improvement: Added PHP 7.0-7.2 compatibility fallback for array_key_first()
     79    * Improvement: Updated compatibility to WordPress 6.9 and PHP 8.3
     80    * Improvement: Added explicit status update to ensure articles are published
     81    * Improvement: Added error logging for debugging
     82    * Improvement: Added plugin deactivation hook to clean up cron jobs
     83    * Bug Fix: Fixed undefined variable errors
     84    * Bug Fix: Fixed cron time comparison logic
     85    * Bug Fix: Fixed array key validation issues
     86    * Bug Fix: Improved existing post update handling
    6987
    70     = 1.3 =
    71     * Changes to be compatible with Latest WordPress
    72         * Minor improvements
    73 
    74     = 1.2 =
    75     * Changes to be compatible with Latest WordPress
    76         * Minor improvements
    77 
    78     = 1.1 =
    79     * Ability to import all articles
    80     * Added pagination
    81     * Other minor bugs fixes
    82    
    8388    = 1.0 =
    8489    * Initial Launch
     
    8691    == Upgrade Notice ==
    8792
     93    = 1.1 =
     94    * This version includes important security fixes and improvements. All users are strongly recommended to upgrade. The plugin now properly validates all inputs, includes nonce verification, and has improved error handling. All imported articles will now default to published status.
     95
    8896    = 1.0 =
    8997    * Initial Launch. No upgrade notice.
     98
     99
Note: See TracChangeset for help on using the changeset viewer.