| 1 |
<?php |
| 2 |
/** |
| 3 |
* Plugin Name: Koala AI |
| 4 |
* Description: Official integration by Koala AI that makes it easy to publish content from Koala AI Writer to WordPress. |
| 5 |
* Version: 1.0 |
| 6 |
* Author: Koala AI |
| 7 |
* Author URI: https://koala.sh |
| 8 |
* Text Domain: koala-ai |
| 9 |
* License: GPL-2.0+ |
| 10 |
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt |
| 11 |
*/ |
| 12 |
|
| 13 |
// If this file is called directly, abort. |
| 14 |
if ( ! defined( 'ABSPATH' ) ) { |
| 15 |
exit; // Exit if accessed directly |
| 16 |
} |
| 17 |
|
| 18 |
require_once __DIR__ . '/KoalaAI-API.php'; |
| 19 |
require_once __DIR__ . '/KoalaAI-ImageImporter.php'; |
| 20 |
|
| 21 |
if ( ! class_exists( 'KoalaAI' ) ) { |
| 22 |
class KoalaAI { |
| 23 |
const KOALA_AI_CONNECTION_ENDPOINT = 'https://koala.sh/wordpress-callback?uuid={secret_token}&url={url}'; |
| 24 |
const KOALA_AI_CONNECTION_OPTION = 'koala_ai_connection'; |
| 25 |
const KOALA_AI_SECRET_TOKEN_OPTION = 'koala_ai_secret_token'; |
| 26 |
const KOALA_AI_IMAGE_IMPORT_BATCH_SIZE = 5; |
| 27 |
const KOALA_AI_IMAGE_IMPORT_TRANSIENT = 'koala_ai_image_import_in_progress'; |
| 28 |
const KOALA_AI_IMAGE_IMPORT_LOG_OPTION = 'koala_ai_image_import_log'; |
| 29 |
const KOALA_AI_AUTO_IMPORT_OPTION = 'koala_ai_image_auto_import'; |
| 30 |
const KOALA_AI_POST_TYPES_OPTION = 'koala_ai_image_import_post_types'; |
| 31 |
const KOALA_AI_PROCESS_POST_HOOK = 'koala_ai_process_post_images'; |
| 32 |
const KOALA_AI_PROCESSING_MODE_OPTION = 'koala_ai_processing_mode'; |
| 33 |
const KOALA_AI_FIRST_IMAGE_AS_FEATURED_OPTION = 'koala_ai_first_image_as_featured'; |
| 34 |
|
| 35 |
/** |
| 36 |
* Initializes the Koala AI plugin by setting up the necessary actions. |
| 37 |
*/ |
| 38 |
public static function init() { |
| 39 |
add_action( 'admin_menu', array( 'KoalaAI', 'koala_ai_settings_menu' ) ); |
| 40 |
|
| 41 |
// Handle form submissions in admin context only |
| 42 |
add_action( 'admin_init', array( 'KoalaAI', 'handle_disconnection' ) ); |
| 43 |
add_action( 'admin_init', array( 'KoalaAI', 'handle_image_import_request' ) ); |
| 44 |
add_action( 'admin_init', array( 'KoalaAI', 'handle_settings_update' ) ); |
| 45 |
|
| 46 |
// Add schedule hook for image import background process |
| 47 |
add_action('koala_ai_process_image_import', array('KoalaAI', 'process_scheduled_image_import')); |
| 48 |
// Add save_post hook to schedule image processing on publish |
| 49 |
add_action('save_post', array('KoalaAI', 'process_images_on_publish'), 10, 3); |
| 50 |
// Add hook for background processing of individual posts |
| 51 |
add_action(self::KOALA_AI_PROCESS_POST_HOOK, array('KoalaAI', 'process_single_post_images'), 10, 1); |
| 52 |
|
| 53 |
// Activation redirect |
| 54 |
add_action('admin_init', array('KoalaAI', 'do_activation_redirect')); |
| 55 |
|
| 56 |
/** |
| 57 |
* Register REST endpoints |
| 58 |
*/ |
| 59 |
KoalaAI_API::register_api_endpoints(); |
| 60 |
} |
| 61 |
|
| 62 |
/** |
| 63 |
* Handles settings update form submission. |
| 64 |
*/ |
| 65 |
public static function handle_settings_update() { |
| 66 |
if ( isset( $_POST['update_koala_ai_settings'] ) && $_POST['update_koala_ai_settings'] === '1' ) { |
| 67 |
// Verify nonce for security |
| 68 |
if ( isset( $_POST['koala_ai_settings_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['koala_ai_settings_nonce'] ) ), 'koala_ai_settings_action' ) ) { |
| 69 |
// Update auto import setting |
| 70 |
$auto_import = isset( $_POST['koala_ai_auto_import'] ) ? 1 : 0; |
| 71 |
update_option( self::KOALA_AI_AUTO_IMPORT_OPTION, $auto_import ); |
| 72 |
|
| 73 |
// Update post types setting |
| 74 |
$post_types = isset( $_POST['koala_ai_post_types'] ) ? (array) wp_unslash($_POST['koala_ai_post_types']) : array(); |
| 75 |
$post_types = array_map( 'sanitize_text_field', $post_types ); |
| 76 |
update_option( self::KOALA_AI_POST_TYPES_OPTION, $post_types ); |
| 77 |
|
| 78 |
// Update processing mode setting |
| 79 |
$processing_mode = isset( $_POST['koala_ai_processing_mode'] ) ? sanitize_text_field( wp_unslash( $_POST['koala_ai_processing_mode'] ) ) : 'background'; |
| 80 |
update_option( self::KOALA_AI_PROCESSING_MODE_OPTION, $processing_mode ); |
| 81 |
|
| 82 |
// Update first image as featured setting |
| 83 |
$first_image_as_featured = isset( $_POST['koala_ai_first_image_as_featured'] ) ? 1 : 0; |
| 84 |
update_option( self::KOALA_AI_FIRST_IMAGE_AS_FEATURED_OPTION, $first_image_as_featured ); |
| 85 |
|
| 86 |
wp_safe_redirect( admin_url( 'admin.php?page=koala-ai&settings_updated=1' ) ); |
| 87 |
exit; |
| 88 |
} |
| 89 |
} |
| 90 |
} |
| 91 |
|
| 92 |
/** |
| 93 |
* Handles disconnection request from the user. |
| 94 |
*/ |
| 95 |
public static function handle_disconnection() { |
| 96 |
if ( isset( $_POST['disconnect'] ) && $_POST['disconnect'] === '1' ) { |
| 97 |
// Verify nonce for security in admin forms |
| 98 |
if ( isset( $_POST['koala_ai_disconnect_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['koala_ai_disconnect_nonce'] ) ), 'koala_ai_disconnect_action' ) ) { |
| 99 |
update_option( self::KOALA_AI_CONNECTION_OPTION, 0 ); |
| 100 |
update_option( self::KOALA_AI_SECRET_TOKEN_OPTION, self::generate_secret_token() ); |
| 101 |
wp_safe_redirect( admin_url( 'admin.php?page=koala-ai&disconnected=1' ) ); |
| 102 |
exit; |
| 103 |
} |
| 104 |
} |
| 105 |
} |
| 106 |
|
| 107 |
/** |
| 108 |
* Handles the image import request. |
| 109 |
*/ |
| 110 |
public static function handle_image_import_request() { |
| 111 |
if (isset($_POST['start_image_import']) && $_POST['start_image_import'] === '1') { |
| 112 |
// Verify nonce for security |
| 113 |
if (isset($_POST['koala_ai_image_import_nonce']) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['koala_ai_image_import_nonce'] ) ), 'koala_ai_image_import_action')) { |
| 114 |
// Check if an import is already in progress |
| 115 |
if (get_transient(self::KOALA_AI_IMAGE_IMPORT_TRANSIENT)) { |
| 116 |
wp_safe_redirect(admin_url('admin.php?page=koala-ai&import_already_running=1')); |
| 117 |
exit; |
| 118 |
} |
| 119 |
|
| 120 |
// Set transient to indicate import is in progress |
| 121 |
set_transient(self::KOALA_AI_IMAGE_IMPORT_TRANSIENT, true, HOUR_IN_SECONDS); |
| 122 |
|
| 123 |
// Clear previous log |
| 124 |
update_option(self::KOALA_AI_IMAGE_IMPORT_LOG_OPTION, array()); |
| 125 |
|
| 126 |
// Schedule the first batch to run immediately |
| 127 |
if (!wp_next_scheduled('koala_ai_process_image_import')) { |
| 128 |
wp_schedule_single_event(time(), 'koala_ai_process_image_import'); |
| 129 |
} |
| 130 |
|
| 131 |
wp_safe_redirect(admin_url('admin.php?page=koala-ai&import_started=1')); |
| 132 |
exit; |
| 133 |
} |
| 134 |
} |
| 135 |
} |
| 136 |
|
| 137 |
/** |
| 138 |
* Process a batch of posts for image import. |
| 139 |
*/ |
| 140 |
public static function process_scheduled_image_import() { |
| 141 |
// Check if import is in progress |
| 142 |
if (!get_transient(self::KOALA_AI_IMAGE_IMPORT_TRANSIENT)) { |
| 143 |
return; |
| 144 |
} |
| 145 |
|
| 146 |
// Get current log |
| 147 |
$log = get_option(self::KOALA_AI_IMAGE_IMPORT_LOG_OPTION, array()); |
| 148 |
$processed_post_ids = isset($log['processed_posts']) ? $log['processed_posts'] : array(); |
| 149 |
|
| 150 |
// Get selected post types from settings |
| 151 |
$image_import_post_types = get_option(self::KOALA_AI_POST_TYPES_OPTION, array('post', 'page')); |
| 152 |
|
| 153 |
// Query for posts that haven't been processed yet |
| 154 |
$args = array( |
| 155 |
'post_type' => $image_import_post_types, |
| 156 |
'post_status' => 'any', |
| 157 |
'posts_per_page' => self::KOALA_AI_IMAGE_IMPORT_BATCH_SIZE, |
| 158 |
'post__not_in' => $processed_post_ids, |
| 159 |
'orderby' => 'date', |
| 160 |
'order' => 'DESC', |
| 161 |
); |
| 162 |
|
| 163 |
$query = new WP_Query($args); |
| 164 |
|
| 165 |
if ($query->have_posts()) { |
| 166 |
while ($query->have_posts()) { |
| 167 |
$query->the_post(); |
| 168 |
$post_id = get_the_ID(); |
| 169 |
$processed_post_ids[] = $post_id; |
| 170 |
|
| 171 |
// Process this post's content |
| 172 |
$content = get_post_field('post_content', $post_id); |
| 173 |
$updated_content = KoalaAI_ImageImporter::process_content($content, $post_id); |
| 174 |
|
| 175 |
// If content was changed, update the post |
| 176 |
if ($content !== $updated_content) { |
| 177 |
wp_update_post(array( |
| 178 |
'ID' => $post_id, |
| 179 |
'post_content' => $updated_content |
| 180 |
)); |
| 181 |
|
| 182 |
// Log the post that had images updated |
| 183 |
if (!isset($log['updated_posts'])) { |
| 184 |
$log['updated_posts'] = array(); |
| 185 |
} |
| 186 |
$log['updated_posts'][] = array( |
| 187 |
'post_id' => $post_id, |
| 188 |
'title' => get_the_title($post_id), |
| 189 |
'time' => current_time('mysql') |
| 190 |
); |
| 191 |
} |
| 192 |
} |
| 193 |
|
| 194 |
wp_reset_postdata(); |
| 195 |
|
| 196 |
// Update log with processed posts |
| 197 |
$log['processed_posts'] = $processed_post_ids; |
| 198 |
$log['last_run'] = current_time('mysql'); |
| 199 |
update_option(self::KOALA_AI_IMAGE_IMPORT_LOG_OPTION, $log); |
| 200 |
|
| 201 |
// Schedule the next batch |
| 202 |
wp_schedule_single_event(time() + 10, 'koala_ai_process_image_import'); |
| 203 |
} else { |
| 204 |
// No more posts to process, mark as completed |
| 205 |
$log['status'] = 'completed'; |
| 206 |
$log['completed_time'] = current_time('mysql'); |
| 207 |
update_option(self::KOALA_AI_IMAGE_IMPORT_LOG_OPTION, $log); |
| 208 |
delete_transient(self::KOALA_AI_IMAGE_IMPORT_TRANSIENT); |
| 209 |
} |
| 210 |
} |
| 211 |
|
| 212 |
/** |
| 213 |
* Process images in post content when a post or page is published. |
| 214 |
* Either processes directly or schedules processing based on settings. |
| 215 |
* |
| 216 |
* @param int $post_ID Post ID. |
| 217 |
* @param WP_Post $post Post object. |
| 218 |
* @param bool $update Whether this is an existing post being updated. |
| 219 |
*/ |
| 220 |
public static function process_images_on_publish($post_ID, $post, $update) { |
| 221 |
// Check if auto-import is enabled |
| 222 |
$image_auto_import_enabled = get_option(self::KOALA_AI_AUTO_IMPORT_OPTION, 1); |
| 223 |
if (!$image_auto_import_enabled) { |
| 224 |
return; |
| 225 |
} |
| 226 |
|
| 227 |
// Get allowed post types |
| 228 |
$image_import_post_types = get_option(self::KOALA_AI_POST_TYPES_OPTION, array('post', 'page')); |
| 229 |
|
| 230 |
// Check if current post type is enabled for image import |
| 231 |
if (!in_array($post->post_type, $image_import_post_types)) { |
| 232 |
return; |
| 233 |
} |
| 234 |
|
| 235 |
// Only process if post is being published |
| 236 |
if ($post->post_status !== 'publish') { |
| 237 |
return; |
| 238 |
} |
| 239 |
|
| 240 |
// Check processing mode setting (default to background if not set) |
| 241 |
$processing_mode = get_option(self::KOALA_AI_PROCESSING_MODE_OPTION, 'background'); |
| 242 |
|
| 243 |
if ($processing_mode === 'immediate') { |
| 244 |
// Process immediately |
| 245 |
self::process_single_post_images($post_ID); |
| 246 |
} else { |
| 247 |
// Schedule the background task to process this post |
| 248 |
if (!wp_next_scheduled(self::KOALA_AI_PROCESS_POST_HOOK, array($post_ID))) { |
| 249 |
wp_schedule_single_event(time(), self::KOALA_AI_PROCESS_POST_HOOK, array($post_ID)); |
| 250 |
} |
| 251 |
} |
| 252 |
} |
| 253 |
|
| 254 |
/** |
| 255 |
* Process images for a single post in the background. |
| 256 |
* |
| 257 |
* @param int $post_ID Post ID to process. |
| 258 |
*/ |
| 259 |
public static function process_single_post_images($post_ID) { |
| 260 |
// Get the post |
| 261 |
$post = get_post($post_ID); |
| 262 |
if (!$post) { |
| 263 |
return; |
| 264 |
} |
| 265 |
|
| 266 |
$content = $post->post_content; |
| 267 |
$updated_content = KoalaAI_ImageImporter::process_content($content, $post_ID); |
| 268 |
|
| 269 |
if ($content !== $updated_content) { |
| 270 |
// Avoid infinite loops by removing the action temporarily |
| 271 |
remove_action('save_post', array('KoalaAI', 'process_images_on_publish'), 10); |
| 272 |
|
| 273 |
// Update the post with the new content |
| 274 |
wp_update_post(array( |
| 275 |
'ID' => $post_ID, |
| 276 |
'post_content' => $updated_content |
| 277 |
)); |
| 278 |
|
| 279 |
// Re-add the action |
| 280 |
add_action('save_post', array('KoalaAI', 'process_images_on_publish'), 10, 3); |
| 281 |
|
| 282 |
// Log the post processing if we're keeping logs |
| 283 |
$log = get_option(self::KOALA_AI_IMAGE_IMPORT_LOG_OPTION, array()); |
| 284 |
if (isset($log['updated_posts'])) { |
| 285 |
$log['updated_posts'][] = array( |
| 286 |
'post_id' => $post_ID, |
| 287 |
'title' => get_the_title($post_ID), |
| 288 |
'time' => current_time('mysql') |
| 289 |
); |
| 290 |
update_option(self::KOALA_AI_IMAGE_IMPORT_LOG_OPTION, $log); |
| 291 |
} |
| 292 |
} |
| 293 |
} |
| 294 |
|
| 295 |
/** |
| 296 |
* Adds the Koala AI Settings menu page to the WordPress admin menu. |
| 297 |
*/ |
| 298 |
public static function koala_ai_settings_menu() { |
| 299 |
add_menu_page( |
| 300 |
esc_html__( 'Koala AI Settings', 'koala-ai' ), |
| 301 |
esc_html__( 'Koala AI', 'koala-ai' ), |
| 302 |
'manage_options', |
| 303 |
'koala-ai', |
| 304 |
array( 'KoalaAI', 'koala_ai_settings_page' ), |
| 305 |
plugin_dir_url( __FILE__ ) . 'assets/logos/logo-small.png?12', |
| 306 |
80 |
| 307 |
); |
| 308 |
// Enqueue JS and CSS for our settings page |
| 309 |
add_action('admin_enqueue_scripts', function($hook) { |
| 310 |
if ($hook === 'toplevel_page_koala-ai') { |
| 311 |
// Enqueue admin CSS |
| 312 |
wp_enqueue_style( |
| 313 |
'koala-ai-admin', |
| 314 |
plugin_dir_url(__FILE__) . 'assets/css/koala-ai-admin.css', |
| 315 |
array(), |
| 316 |
'1.0' |
| 317 |
); |
| 318 |
|
| 319 |
// Enqueue image import JS |
| 320 |
wp_enqueue_script( |
| 321 |
'koala-ai-image-import', |
| 322 |
plugin_dir_url(__FILE__) . 'assets/js/koala-ai-image-import.js', |
| 323 |
array('jquery'), |
| 324 |
'1.0', |
| 325 |
true |
| 326 |
); |
| 327 |
|
| 328 |
// Enqueue admin JS |
| 329 |
wp_enqueue_script( |
| 330 |
'koala-ai-admin', |
| 331 |
plugin_dir_url(__FILE__) . 'assets/js/koala-ai-admin.js', |
| 332 |
array('jquery'), |
| 333 |
'1.0', |
| 334 |
true |
| 335 |
); |
| 336 |
|
| 337 |
// Set up data for JS |
| 338 |
wp_localize_script('koala-ai-image-import', 'KoalaAIImageImport', array( |
| 339 |
'restUrl' => esc_url_raw(rest_url('koala-ai/v1/')), |
| 340 |
'nonce' => wp_create_nonce('wp_rest'), |
| 341 |
)); |
| 342 |
|
| 343 |
// Set up data for admin JS |
| 344 |
wp_localize_script('koala-ai-admin', 'koalaAIData', array( |
| 345 |
'disconnectText' => esc_html__('Disconnect', 'koala-ai'), |
| 346 |
'disconnectConfirm' => esc_html__('Do you really want to disconnect the website? Your content synchronization will be stopped.', 'koala-ai'), |
| 347 |
)); |
| 348 |
} |
| 349 |
}); |
| 350 |
} |
| 351 |
|
| 352 |
/** |
| 353 |
* Renders the Koala AI settings page. |
| 354 |
*/ |
| 355 |
public static function koala_ai_settings_page() { |
| 356 |
$secret_token = self::get_secret_token(); |
| 357 |
$website_url = self::get_website_url(); |
| 358 |
$connection = self::is_connected() ? 'Connected' : 'Disconnected'; |
| 359 |
$import_log = get_option(self::KOALA_AI_IMAGE_IMPORT_LOG_OPTION, array()); |
| 360 |
$import_in_progress = get_transient(self::KOALA_AI_IMAGE_IMPORT_TRANSIENT); |
| 361 |
|
| 362 |
// Get auto import settings |
| 363 |
$image_auto_import_enabled = get_option(self::KOALA_AI_AUTO_IMPORT_OPTION, 1); |
| 364 |
$image_import_post_types = get_option(self::KOALA_AI_POST_TYPES_OPTION, array('post', 'page')); |
| 365 |
$processing_mode = get_option(self::KOALA_AI_PROCESSING_MODE_OPTION, 'background'); |
| 366 |
$first_image_as_featured_enabled = get_option(self::KOALA_AI_FIRST_IMAGE_AS_FEATURED_OPTION, 0); |
| 367 |
|
| 368 |
// Get all registered post types |
| 369 |
$post_types = array_filter( |
| 370 |
get_post_types(array('public' => true), 'objects'), |
| 371 |
function($post_type) { |
| 372 |
return $post_type->name !== 'attachment'; |
| 373 |
} |
| 374 |
); |
| 375 |
|
| 376 |
$connection_url = $connection === 'Disconnected' |
| 377 |
? str_replace( |
| 378 |
['{secret_token}', '{url}'], |
| 379 |
[esc_attr($secret_token), esc_attr(urlencode($website_url))], |
| 380 |
self::KOALA_AI_CONNECTION_ENDPOINT |
| 381 |
) |
| 382 |
: '#'; |
| 383 |
?> |
| 384 |
<h2><?php esc_html_e( 'Koala AI Settings', 'koala-ai' ); ?></h2> |
| 385 |
<div class="koala-content-container"> |
| 386 |
<form action="<?php echo esc_url( $connection_url ); ?>" method="post"> |
| 387 |
<p style="display: none;"> |
| 388 |
<label for="secret-token"><?php esc_html_e( 'Secret Token', 'koala-ai' ); ?></label> <br/> <input type="text" id="secret-token" name="secret-token" size="32" value="<?php echo esc_attr( $secret_token ); ?>" readonly> |
| 389 |
</p> |
| 390 |
<p> |
| 391 |
<label><?php esc_html_e( 'Connection Status: ', 'koala-ai' ); ?> <span style="color: <?php echo $connection === 'Disconnected' ? 'darkred' : 'darkgreen'; ?>; font-weight: bold"><?php echo esc_html( $connection ); ?></span></label> |
| 392 |
</p> |
| 393 |
<?php if ( $connection === 'Connected' ) { |
| 394 |
?> |
| 395 |
<input type="hidden" name="disconnect" value="1"> |
| 396 |
<?php wp_nonce_field('koala_ai_disconnect_action', 'koala_ai_disconnect_nonce'); ?> |
| 397 |
<?php } |
| 398 |
// Output submit button |
| 399 |
if ( $connection === 'Connected' ) { |
| 400 |
submit_button( esc_html__( 'Disconnect', 'koala-ai' ), 'primary', 'connection-button', false, array( 'onclick' => 'return confirm_disconnect()' ) ); |
| 401 |
} else { |
| 402 |
echo '<a href="' . esc_url( $connection_url ) . '" target="_blank" class="button button-primary">' . esc_html__( 'Connect', 'koala-ai' ) . '</a>'; |
| 403 |
echo '<a href="' . esc_url( admin_url( 'admin.php?page=koala-ai' ) ) . '" class="button" style="margin-left: 10px;">' . esc_html__( 'Refresh Status', 'koala-ai' ) . '</a>'; |
| 404 |
echo '<div class="connection-note">' . esc_html__( 'After connecting in Koala AI, please come back and click the Refresh Status button to update your connection status.', 'koala-ai' ) . '</div>'; |
| 405 |
} |
| 406 |
|
| 407 |
// Show disconnection message if needed |
| 408 |
if (isset($_GET['disconnected']) && $_GET['disconnected'] == '1') { |
| 409 |
echo '<div class="connection-note" style="border-left-color: #d63638;">' . esc_html__( 'Your WordPress site has been disconnected. For complete disconnection, please also remove this site from your integrations in your Koala AI account.', 'koala-ai' ) . '</div>'; |
| 410 |
} |
| 411 |
?> |
| 412 |
</form> |
| 413 |
</div> |
| 414 |
|
| 415 |
<h3 class="section-title"><?php esc_html_e('Automatic Image Import Settings', 'koala-ai'); ?></h3> |
| 416 |
<div class="koala-content-container"> |
| 417 |
|
| 418 |
<div class="koala-info-banner"> |
| 419 |
<p><?php esc_html_e('These settings control how Koala AI handles images in your content. When enabled, external images from Koala AI will be automatically downloaded to your WordPress media library, allowing them to be hosted directly on your server instead of the koala.sh domain.', 'koala-ai'); ?></p> |
| 420 |
</div> |
| 421 |
|
| 422 |
<?php if (isset($_GET['settings_updated']) && $_GET['settings_updated'] == '1') : ?> |
| 423 |
<div class="success-message"><?php esc_html_e('Your settings have been updated successfully.', 'koala-ai'); ?></div> |
| 424 |
<?php endif; ?> |
| 425 |
|
| 426 |
<form method="post" class="settings-form"> |
| 427 |
<p> |
| 428 |
<label> |
| 429 |
<input type="checkbox" name="koala_ai_auto_import" value="1" <?php checked($image_auto_import_enabled, 1); ?> id="koala_ai_auto_import"> |
| 430 |
<?php esc_html_e('Enable automatic image import when posts are saved (only applies to Published posts)', 'koala-ai'); ?> |
| 431 |
</label> |
| 432 |
</p> |
| 433 |
|
| 434 |
<div id="koala_ai_auto_import_options" style="margin-left: 24px; <?php echo $image_auto_import_enabled ? '' : 'display: none;'; ?>"> |
| 435 |
<p> |
| 436 |
<label><?php esc_html_e('Automatic Processing Mode:', 'koala-ai'); ?></label> |
| 437 |
</p> |
| 438 |
<div style="margin-left: 24px;"> |
| 439 |
<label style="display: block; margin-bottom: 8px;"> |
| 440 |
<input type="radio" name="koala_ai_processing_mode" value="background" <?php checked($processing_mode, 'background'); ?>> |
| 441 |
<?php esc_html_e('Background Processing (faster saving, processes soon after the post is saved)', 'koala-ai'); ?> |
| 442 |
</label> |
| 443 |
<label style="display: block;"> |
| 444 |
<input type="radio" name="koala_ai_processing_mode" value="immediate" <?php checked($processing_mode, 'immediate'); ?>> |
| 445 |
<?php esc_html_e('Immediate Processing (slower publishing, but ensures processing before completing save)', 'koala-ai'); ?> |
| 446 |
</label> |
| 447 |
</div> |
| 448 |
</div> |
| 449 |
|
| 450 |
<p> |
| 451 |
<label> |
| 452 |
<input type="checkbox" name="koala_ai_first_image_as_featured" value="1" <?php checked($first_image_as_featured_enabled, 1); ?>> |
| 453 |
<?php esc_html_e('Set the first image in content as the featured image for the post (only applies to posts without an existing featured image)', 'koala-ai'); ?> |
| 454 |
</label> |
| 455 |
</p> |
| 456 |
|
| 457 |
<p> |
| 458 |
<label><?php esc_html_e('Choose post types for processing:', 'koala-ai'); ?></label> |
| 459 |
</p> |
| 460 |
|
| 461 |
<div class="post-types-container"> |
| 462 |
<?php foreach ($post_types as $post_type) : ?> |
| 463 |
<label class="post-type-label"> |
| 464 |
<input type="checkbox" class="post-type-checkbox" name="koala_ai_post_types[]" value="<?php echo esc_attr($post_type->name); ?>" <?php checked(in_array($post_type->name, $image_import_post_types), true); ?>> |
| 465 |
<?php echo esc_html($post_type->labels->singular_name); ?> |
| 466 |
</label> |
| 467 |
<?php endforeach; ?> |
| 468 |
</div> |
| 469 |
|
| 470 |
<input type="hidden" name="update_koala_ai_settings" value="1"> |
| 471 |
<?php wp_nonce_field('koala_ai_settings_action', 'koala_ai_settings_nonce'); ?> |
| 472 |
<?php submit_button(esc_html__('Save Settings', 'koala-ai')); ?> |
| 473 |
</form> |
| 474 |
</div> |
| 475 |
|
| 476 |
<h3 class="section-title"><?php esc_html_e('Bulk Image Import Tool', 'koala-ai'); ?></h3> |
| 477 |
<div class="koala-content-container"> |
| 478 |
<div id="koala-ai-image-import-ui"> |
| 479 |
<div class="note-banner"> |
| 480 |
<p><?php esc_html_e('We recommend creating a backup of your website before using this tool as a best practice.', 'koala-ai'); ?></p> |
| 481 |
</div> |
| 482 |
<div class="koala-info-banner"> |
| 483 |
<p><?php esc_html_e('This tool allows you to import all external Koala AI images from existing content. Before using this tool:', 'koala-ai'); ?></p> |
| 484 |
<ol> |
| 485 |
<li><?php esc_html_e('Make sure to save your settings in the section above first', 'koala-ai'); ?></li> |
| 486 |
<li><?php esc_html_e('Only posts of the selected post types above will be processed', 'koala-ai'); ?></li> |
| 487 |
<li><?php esc_html_e('If you enabled the "Set first image as featured image" option, it will only apply to posts that don\'t already have a featured image', 'koala-ai'); ?></li> |
| 488 |
</ol> |
| 489 |
</div> |
| 490 |
<p><?php esc_html_e('This tool scans your content for images from koala.sh, downloads them to your WordPress media library, and updates your posts to use these local copies. The process runs in your browser and shows real-time progress.', 'koala-ai'); ?></p> |
| 491 |
<button id="koala-ai-start-btn" class="button button-primary"><?php esc_html_e('Start Bulk Image Import', 'koala-ai'); ?></button> |
| 492 |
<div id="koala-ai-progress-container" style="margin-top:20px;display:none;max-width:400px;"> |
| 493 |
<div style="background:#eee;border-radius:4px;overflow:hidden;height:24px;"> |
| 494 |
<div id="koala-ai-progress-bar" style="background:#2271b1;height:24px;width:0%;transition:width 0.3s;"></div> |
| 495 |
</div> |
| 496 |
<div id="koala-ai-progress-text" style="margin-top:8px;"></div> |
| 497 |
</div> |
| 498 |
<div id="koala-ai-status" style="margin-top:10px;"></div> |
| 499 |
</div> |
| 500 |
</div> |
| 501 |
|
| 502 |
<div class="koala-content-container" style="margin-top: 45px;">Visit <a href="https://koala.sh/?utm_source=wp_plugin_footer" target="_blank">Koala AI</a></div> |
| 503 |
<?php |
| 504 |
} |
| 505 |
|
| 506 |
/** |
| 507 |
* Gets the secret token for the current site. |
| 508 |
*/ |
| 509 |
public static function get_secret_token() { |
| 510 |
return get_option( self::KOALA_AI_SECRET_TOKEN_OPTION ); |
| 511 |
} |
| 512 |
|
| 513 |
/** |
| 514 |
* Gets the website URL for the current site. |
| 515 |
*/ |
| 516 |
public static function get_website_url() { |
| 517 |
return get_bloginfo('url'); |
| 518 |
} |
| 519 |
|
| 520 |
/** |
| 521 |
* Generate Secret Token |
| 522 |
*/ |
| 523 |
private static function generate_secret_token() { |
| 524 |
return wp_generate_uuid4(); |
| 525 |
} |
| 526 |
|
| 527 |
/** |
| 528 |
* Handles redirect to settings page after plugin activation. |
| 529 |
*/ |
| 530 |
public static function do_activation_redirect() { |
| 531 |
if (get_option('koala_ai_do_activation_redirect', false)) { |
| 532 |
delete_option('koala_ai_do_activation_redirect'); |
| 533 |
if (!isset($_GET['activate-multi'])) { // Prevent redirect on bulk activation |
| 534 |
wp_safe_redirect(admin_url('admin.php?page=koala-ai')); |
| 535 |
exit; |
| 536 |
} |
| 537 |
} |
| 538 |
} |
| 539 |
|
| 540 |
/** |
| 541 |
* Activates the Koala AI plugin by checking and setting the necessary options. |
| 542 |
*/ |
| 543 |
public static function activate() { |
| 544 |
// If secret token option is empty, set value. |
| 545 |
if ( ! self::get_secret_token() ) { |
| 546 |
update_option( self::KOALA_AI_SECRET_TOKEN_OPTION, self::generate_secret_token() ); |
| 547 |
} |
| 548 |
|
| 549 |
// Set default values for auto import settings |
| 550 |
if (get_option(self::KOALA_AI_AUTO_IMPORT_OPTION) === false) { |
| 551 |
update_option(self::KOALA_AI_AUTO_IMPORT_OPTION, 1); // Enabled by default |
| 552 |
} |
| 553 |
|
| 554 |
// Set default values for post types |
| 555 |
if (get_option(self::KOALA_AI_POST_TYPES_OPTION) === false) { |
| 556 |
update_option(self::KOALA_AI_POST_TYPES_OPTION, array('post', 'page')); |
| 557 |
} |
| 558 |
|
| 559 |
// Set default processing mode |
| 560 |
if (get_option(self::KOALA_AI_PROCESSING_MODE_OPTION) === false) { |
| 561 |
update_option(self::KOALA_AI_PROCESSING_MODE_OPTION, 'background'); // Background processing by default |
| 562 |
} |
| 563 |
|
| 564 |
// Set default first image as featured setting (disabled by default) |
| 565 |
if (get_option(self::KOALA_AI_FIRST_IMAGE_AS_FEATURED_OPTION) === false) { |
| 566 |
update_option(self::KOALA_AI_FIRST_IMAGE_AS_FEATURED_OPTION, 0); |
| 567 |
} |
| 568 |
|
| 569 |
// Set flag for activation redirect |
| 570 |
update_option('koala_ai_do_activation_redirect', true); |
| 571 |
} |
| 572 |
|
| 573 |
/** |
| 574 |
* Checks if the Koala AI connection is active. |
| 575 |
*/ |
| 576 |
public static function is_connected() { |
| 577 |
$connection_data = get_option( self::KOALA_AI_CONNECTION_OPTION ); |
| 578 |
return !empty($connection_data); |
| 579 |
} |
| 580 |
|
| 581 |
/** |
| 582 |
* Gets the stored integration ID if connected. |
| 583 |
* @return string|null |
| 584 |
*/ |
| 585 |
public static function get_integration_id() { |
| 586 |
$connection_data = get_option( self::KOALA_AI_CONNECTION_OPTION ); |
| 587 |
if (is_array($connection_data) && isset($connection_data['integration_id']) && !empty($connection_data['integration_id'])) { |
| 588 |
return $connection_data['integration_id']; |
| 589 |
} |
| 590 |
return null; |
| 591 |
} |
| 592 |
} |
| 593 |
|
| 594 |
KoalaAI::init(); |
| 595 |
} |
| 596 |
|
| 597 |
/** |
| 598 |
* Set secret_token when plugin is activated |
| 599 |
*/ |
| 600 |
register_activation_hook( __FILE__, array( 'KoalaAI', 'activate' ) ); |