cleanup_old_temp_images(); } // Activation hook. register_activation_hook( __FILE__, 'wp_agentic_writer_activate' ); /** * Plugin activation. * * @since 0.1.0 */ function wp_agentic_writer_activate() { // Set default options using model registry for consistency. $registry_defaults = WPAW_Model_Registry::get_activation_defaults(); $default_options = array( 'openrouter_api_key' => '', 'planning_model' => $registry_defaults['planning_model'], 'execution_model' => $registry_defaults['execution_model'], 'image_model' => $registry_defaults['image_model'], 'web_search_enabled' => false, 'search_engine' => 'auto', 'search_depth' => 'medium', 'cost_tracking_enabled' => true, 'enable_clarification_quiz' => true, 'clarity_confidence_threshold' => '0.6', 'chat_history_limit' => 20, 'preferred_languages' => array( 'auto', 'English', 'Indonesian' ), 'custom_languages' => array(), ); add_option( 'wp_agentic_writer_settings', $default_options ); // Set default custom models (separate option for custom models) $default_custom_models = array( array( 'id' => 'black-forest-labs/flux-1.1-pro', 'name' => 'FLUX 1.1 Pro', 'type' => 'image', ), array( 'id' => 'black-forest-labs/flux-pro', 'name' => 'FLUX Pro', 'type' => 'image', ), array( 'id' => 'recraft-ai/recraft-v3', 'name' => 'Recraft V3', 'type' => 'image', ), ); add_option( 'wp_agentic_writer_custom_models', $default_custom_models ); // Create cost tracking table. wp_agentic_writer_create_cost_table(); // Create image management tables. WP_Agentic_Writer_Image_Manager::get_instance()->create_tables(); // Create conversations table. require_once WP_AGENTIC_WRITER_DIR . 'includes/class-conversation-migration.php'; wpaw_create_conversations_table(); } /** * Create cost tracking table. * * @since 0.1.0 */ function wp_agentic_writer_create_cost_table() { global $wpdb; $table_name = $wpdb->prefix . 'wpaw_cost_tracking'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( id bigint(20) NOT NULL AUTO_INCREMENT, post_id bigint(20) NOT NULL, session_id varchar(32) DEFAULT '' NOT NULL, model varchar(255) NOT NULL, provider varchar(50) DEFAULT 'openrouter' NOT NULL, action varchar(50) NOT NULL, input_tokens int(11) NOT NULL, output_tokens int(11) NOT NULL, cost decimal(10,6) NOT NULL, status varchar(20) DEFAULT 'success' NOT NULL, created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, PRIMARY KEY (id), KEY post_id (post_id), KEY session_id (session_id), KEY provider (provider), KEY action (action), KEY created_at (created_at) ) $charset_collate;"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta( $sql ); } // Version-based table creation (runs on plugins_loaded to ensure tables exist). add_action( 'plugins_loaded', 'wp_agentic_writer_maybe_create_tables' ); /** * Create database tables if they don't exist or version is outdated. * * @since 0.1.0 */ function wp_agentic_writer_maybe_create_tables() { // Cost tracking table - controlled by main db version. $current_version = get_option( 'wpaw_db_version', '0' ); $required_version = '1.1.0'; if ( version_compare( $current_version, $required_version, '<' ) ) { // Create cost tracking table. wp_agentic_writer_create_cost_table(); // Create image management tables. WP_Agentic_Writer_Image_Manager::get_instance()->create_tables(); // Create conversations table (migrate if needed). if ( ! function_exists( 'wpaw_create_conversations_table' ) ) { require_once WP_AGENTIC_WRITER_DIR . 'includes/class-conversation-migration.php'; } wpaw_create_conversations_table(); // Update version. update_option( 'wpaw_db_version', $required_version ); } else { // Even if main version is current, still check conversation table independently. if ( ! function_exists( 'wpaw_create_conversations_table' ) ) { require_once WP_AGENTIC_WRITER_DIR . 'includes/class-conversation-migration.php'; } $conv_version = get_option( 'wpaw_conversations_db_version', '0' ); if ( version_compare( $conv_version, '0.1.4', '<' ) ) { wpaw_create_conversations_table(); } } } // Deactivation hook. register_deactivation_hook( __FILE__, 'wp_agentic_writer_deactivate' ); /** * Plugin deactivation. * * @since 0.1.0 */ function wp_agentic_writer_deactivate() { // Clear scheduled cron jobs. $timestamp = wp_next_scheduled( 'wpaw_cleanup_temp_images' ); if ( $timestamp ) { wp_unschedule_event( $timestamp, 'wpaw_cleanup_temp_images' ); } wp_clear_scheduled_hook( 'wpaw_cleanup_old_sessions' ); } // Uninstall hook. register_uninstall_hook( __FILE__, 'wp_agentic_writer_uninstall' ); /** * Plugin uninstall. * * Removes all plugin data including settings, database tables, * post meta, user meta, transients, scheduled events, and temp files. * * @since 0.1.0 */ function wp_agentic_writer_uninstall() { global $wpdb; // Delete options. delete_option( 'wp_agentic_writer_settings' ); delete_option( 'wpaw_db_version' ); delete_option( 'wpaw_conversations_db_version' ); delete_option( 'wp_agentic_writer_custom_models' ); // Delete post meta. $post_meta_keys = array( '_wpaw_plan', '_wpaw_chat_history', '_wpaw_memory', '_wpaw_post_config', '_wpaw_focus_keyword', '_wpaw_writing_status', '_wpaw_current_section', '_wpaw_sections_written', '_wpaw_writing_state_updated', '_wpaw_plan_id', '_wpaw_resume_token', ); foreach ( $post_meta_keys as $key ) { delete_post_meta_by_key( $key ); } // Delete user meta. $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE %s", 'wpaw_%' ) ); // Delete database tables. $tables = array( $wpdb->prefix . 'wpaw_cost_tracking', $wpdb->prefix . 'wpaw_images', $wpdb->prefix . 'wpaw_images_variants', $wpdb->prefix . 'wpaw_conversations', ); foreach ( $tables as $table ) { $wpdb->query( "DROP TABLE IF EXISTS {$table}" ); } // Clear transients. delete_transient( 'wpaw_openrouter_models' ); delete_transient( 'wpaw_openrouter_model_objects' ); delete_transient( 'wpaw_openrouter_model_ids' ); // Unschedule cron events. wp_clear_scheduled_hook( 'wpaw_cleanup_old_sessions' ); wp_clear_scheduled_hook( 'wpaw_daily_cleanup' ); // Delete temp image directory. $upload_dir = wp_upload_dir(); $temp_dir = $upload_dir['basedir'] . '/wpaw'; if ( file_exists( $temp_dir ) ) { $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $temp_dir, RecursiveDirectoryIterator::SKIP_DOTS ), RecursiveIteratorIterator::CHILD_FIRST ); foreach ( $files as $fileinfo ) { $todo = ( $fileinfo->isDir() ? 'rmdir' : 'unlink' ); $todo( $fileinfo->getRealPath() ); } rmdir( $temp_dir ); } }