Changeset 3432542
- Timestamp:
- 01/05/2026 09:03:56 AM (3 months ago)
- Location:
- automatic-articles-importer
- Files:
-
- 22 added
- 9 edited
-
tags/1.5 (added)
-
tags/1.5/assets (added)
-
tags/1.5/assets/banner-1544x500.jpg (added)
-
tags/1.5/assets/banner-772x250.jpg (added)
-
tags/1.5/assets/css (added)
-
tags/1.5/assets/css/images (added)
-
tags/1.5/assets/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png (added)
-
tags/1.5/assets/css/jquery-ui.css (added)
-
tags/1.5/assets/css/style-admin.css (added)
-
tags/1.5/assets/icon-128x128.png (added)
-
tags/1.5/assets/icon-256x256.png (added)
-
tags/1.5/assets/js (added)
-
tags/1.5/assets/js/zl-admin-custom.js (added)
-
tags/1.5/assets/screenshot-1.png (added)
-
tags/1.5/automatic-articles-importer.php (added)
-
tags/1.5/classes (added)
-
tags/1.5/classes/class-admin-menu.php (added)
-
tags/1.5/classes/class-article-import.php (added)
-
tags/1.5/classes/class-ziai-cron.php (added)
-
tags/1.5/includes (added)
-
tags/1.5/includes/wp-ziai-article-form.php (added)
-
tags/1.5/readme.txt (added)
-
trunk/assets/css/style-admin.css (modified) (8 diffs)
-
trunk/assets/js/zl-admin-custom.js (modified) (1 diff)
-
trunk/assets/screenshot-1.png (modified) (previous)
-
trunk/automatic-articles-importer.php (modified) (2 diffs)
-
trunk/classes/class-admin-menu.php (modified) (8 diffs)
-
trunk/classes/class-article-import.php (modified) (2 diffs)
-
trunk/classes/class-ziai-cron.php (modified) (4 diffs)
-
trunk/includes/wp-ziai-article-form.php (modified) (7 diffs)
-
trunk/readme.txt (modified) (4 diffs)
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{ 2 2 width: 100%; 3 3 height: auto; … … 59 59 } 60 60 61 62 61 /* Tool tip icon start*/ 63 64 62 .hint { 65 63 display: inline-block; … … 69 67 } 70 68 71 72 69 /* background style for 'i' */ 73 74 70 .hint-icon { 75 71 background: #a5acb5; … … 86 82 } 87 83 88 89 84 /* hint icon hover style */ 90 91 85 .hint-icon:hover { 92 86 background: #1170be; … … 94 88 } 95 89 96 97 90 /* Displays the hint. important! Do not remove. */ 98 99 91 .hint:hover .hint-description, 100 92 .hint:focus .hint-description { … … 102 94 } 103 95 104 105 96 /* position of the hint */ 106 107 97 .hint-description { 108 98 display: none; … … 124 114 } 125 115 126 127 116 /* styling for the arrow */ 128 129 117 .hint-description:before, 130 118 .hint-description:after { … … 139 127 } 140 128 141 142 129 /* overlay styling */ 143 144 145 /* .hint-description:after { 130 .hint-description:after { 146 131 left: 50%; 147 132 border-top-color: #f6f8fa; 148 133 bottom: -20px; 149 134 } 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'); 1 jQuery(document).ready(function() { 2 var post_type = jQuery("select[name='zl_post_type_get']").val(); 5 3 GetCategoryziai(post_type); 6 4 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(); 9 7 GetCategoryziai(post_type); 10 8 }); 11 9 12 $(progressBar).progressbar({13 value: 014 });15 16 $(bulkImportBtn).click(function() {17 fireBulkImporter();18 });19 20 21 10 function GetCategoryziai(post_type) { 22 $.ajax({11 jQuery.ajax({ 23 12 type: "POST", 24 13 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 28 18 }, 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" }); 32 21 }, 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" }); 48 25 }, 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); 95 29 } 96 30 }); 97 31 } 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: 0105 });106 }107 32 }); -
automatic-articles-importer/trunk/automatic-articles-importer.php
r3189584 r3432542 5 5 * Description: This plugin is used to import articles from Intercom Service. 6 6 * Plugin URI: https://www.zluck.com/ 7 * Version: 1. 47 * Version: 1.5 8 8 * Author: Zluck Solutions 9 9 * Author URI: https://profiles.wordpress.org/zluck … … 22 22 include_once('classes/class-ziai-cron.php'); 23 23 24 $ZIAI_Modules = new ZIAI_Modules(); 25 $ZIAI_Modules->ziai_init();24 // Initialize plugin 25 ZIAI_Modules::ziai_init(); 26 26 ZIAI_CronJob::ziai_cron_init(); 27 28 // Register deactivation hook to clean up cron job 29 register_deactivation_hook(__FILE__, array('ZIAI_CronJob', 'ziai_cronstarter_deactivate')); -
automatic-articles-importer/trunk/classes/class-admin-menu.php
r2904458 r3432542 1 1 <?php 2 2 class 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() { 14 11 add_submenu_page( 15 12 'edit.php?post_type=zl_ziai_article', … … 18 15 'manage_options', 19 16 '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() { 26 22 wp_enqueue_style( 'ziai_admin_style_css', ZIAI_FILE_URL . 'assets/css/style-admin.css'); 27 wp_enqueue_script('jquery-ui-progressbar');28 23 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 33 36 if ( is_file(ZIAI_FILE_PATH . 'includes/wp-ziai-article-form.php') ) { 34 $errormsg = false;35 37 $token = get_option('zl_ziai_access_token'); 36 38 $post_type = get_option('zl_post_type_get'); … … 40 42 $cron_time = get_option('ziai_cron_time'); 41 43 $cron_start_time = get_option('ziai_cron_start_time'); 44 $errormsg = ''; // Initialize error message variable 45 42 46 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 } 43 51 $ziai_access_token = sanitize_text_field($_POST['ziai_access_token']); 44 52 $post_type = sanitize_text_field($_POST['zl_post_type_get']); … … 48 56 $cron_time_u = sanitize_text_field($_POST['ziai_cron_time']); 49 57 $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) { 51 64 ZIAI_CronJob::ziai_cronstarter_deactivate(); 52 65 } … … 60 73 update_option('ziai_cron_start_time', $cron_start_time); 61 74 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'); 70 88 } 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 } 73 105 } 74 106 } … … 79 111 } 80 112 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 94 113 //Post Category Get 95 public function ziai_category_get()114 public static function ziai_category_get() 96 115 { 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 97 128 $post_type = 'zl_ziai_article'; 98 129 $taxonomies = get_object_taxonomies($post_type, 'objects'); 99 130 $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 102 154 $terms = get_terms(array( 103 'taxonomy' => array_key_first($taxonomies),155 'taxonomy' => $taxonomy_key, 104 156 'hide_empty' => false, 105 157 )); 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(); 137 168 } 138 169 139 170 //custom Post Type 140 public function ziai_register_post_type()171 public static function ziai_register_post_type() 141 172 { 142 173 $cpt_name = 'Automatic Articles Importer'; … … 173 204 'hierarchical' => true, 174 205 ); 175 176 206 register_post_type('zl_ziai_article', $args); 177 178 207 $taxonomys = 'Collection'; 179 208 $taxonomyp = 'Collections'; … … 192 221 'not_found' => __('No ' . $taxonomys . ' found.'), 193 222 ); 194 195 223 register_taxonomy('article-cat', array('zl_ziai_article'), array( 196 224 'hierarchical' => true, -
automatic-articles-importer/trunk/classes/class-article-import.php
r2904458 r3432542 17 17 } 18 18 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', 48 45 array( 49 46 'method' => 'GET', 47 'timeout' => 30, 50 48 'headers' => array( 51 'Authorization' => 'Bearer ' . $this->access_token 49 'Authorization' => 'Bearer ' . $this->access_token, 50 'Accept' => 'application/json' 52 51 ) 53 52 ) 54 53 ); 55 54 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 } 92 141 } 93 142 } 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 138 261 $args = array( 139 262 'post_type' => $this->post_type, 140 'post_status' => array('publish', 'draft'), 263 'post_status' => array('publish', 'draft', 'pending', 'private'), 264 'posts_per_page' => 1, 141 265 'meta_query' => array( 142 266 array( 143 267 'key' => 'zl_ziai_id', 144 'value' => $article_id,268 'value' => absint($article['id']), 145 269 'compare' => '=', 146 270 ) … … 148 272 ); 149 273 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(); 234 280 } 235 281 -
automatic-articles-importer/trunk/classes/class-ziai-cron.php
r2904458 r3432542 12 12 // create a scheduled event (if it does not exist already) 13 13 $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) { 15 17 self::ziai_cronstarter_deactivate(); 16 18 return false; 17 19 } 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 19 33 if (!wp_next_scheduled('zl_ziai_cronjobs')) { 20 34 wp_schedule_event($schedule_at, 'zl_ziai_cron', 'zl_ziai_cronjobs'); … … 28 42 $timestamp = wp_next_scheduled('zl_ziai_cronjobs'); 29 43 // 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 } 31 47 } 32 48 … … 40 56 return false; 41 57 } 42 $cron_time = (($cron_time * 60) * 60); 43 // Adds once every minuteto the existing schedules.58 $cron_time = (($cron_time * 60) * 60); // Convert hours to seconds 59 // Adds custom interval to the existing schedules. 44 60 $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') 47 63 ); 48 64 return $schedules; … … 52 68 public static function ziai_get_articles_using_cron() 53 69 { 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 } 58 98 } 59 99 } -
automatic-articles-importer/trunk/includes/wp-ziai-article-form.php
r2904458 r3432542 1 1 <div class="wrap wp-ziai-article"> 2 2 <h1><?php esc_html_e('Automatic Articles Settings', 'ziai-articles'); ?></h1> 3 <?php4 // 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 ?>12 3 <div id="wpbody" role="main"> 13 4 <div id="wpbody-content"> … … 19 10 <div class="col-wrap"> 20 11 <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; ?> 27 17 <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>30 18 <div class="zl-ziai-setting zl-setting-2"> 31 19 <div class="form-field form-required term-name-wrap"> … … 36 24 </div> 37 25 </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/> 48 27 </div> 49 28 </div> … … 66 45 <div class="form-field form-required term-name-wrap"> 67 46 <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())); ?> 69 48 </div> 70 49 <div class="form-field form-required term-name-wrap"> … … 75 54 </div> 76 55 </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" /> 78 57 </div> 79 58 <div class="form-field form-required term-name-wrap"> … … 84 63 </div> 85 64 </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" /> 87 66 </div> 88 67 <br> … … 91 70 wp_nonce_field('zl-ziai-settings-save', 'zl-ziai-settings'); 92 71 submit_button('Save Changes', 'primary', 'savechanges'); 93 //submit_button('Save & Run Now', 'primary runnow', 'runnow');72 submit_button('Save & Run Now', 'primary runnow', 'runnow'); 94 73 ?> 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> -->100 74 <div></div> 101 75 </div> 102 76 </div> 103 77 </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> -->111 78 </div> 112 79 </div> -
automatic-articles-importer/trunk/readme.txt
r3189584 r3432542 1 1 === Automatic Articles Importer === 2 Contributors: zluck, divyeshk71 , robcruiz2 Contributors: zluck, divyeshk71 3 3 Donate link: https://www.buymeacoffee.com/zluck 4 4 Tags: automatic articles importer, import intercom, sync intercom articles, sync articles 5 5 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 9 10 License: GPLv2 or later 10 11 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 14 15 == Description == 15 16 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. 19 18 20 19 21 20 == Key Features == 22 21 23 -Scheduled import process24 -Import intercom articles25 -Import intercom collection26 -Sync articles regulary by cron22 * Scheduled import process 23 * Import intercom articles 24 * Import intercom collection 25 * Sync articles regulary by cron 27 26 28 27 … … 65 64 == Changelog == 66 65 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 69 87 70 = 1.3 =71 * Changes to be compatible with Latest WordPress72 * Minor improvements73 74 = 1.2 =75 * Changes to be compatible with Latest WordPress76 * Minor improvements77 78 = 1.1 =79 * Ability to import all articles80 * Added pagination81 * Other minor bugs fixes82 83 88 = 1.0 = 84 89 * Initial Launch … … 86 91 == Upgrade Notice == 87 92 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 88 96 = 1.0 = 89 97 * Initial Launch. No upgrade notice. 98 99
Note: See TracChangeset
for help on using the changeset viewer.