- Implement local backend AI provider with Ollama integration - Add Brave Search API integration for real-time search suggestions - Add image generation manager with multiple AI providers - Create hybrid provider system with local/cloud fallback - Add comprehensive settings UI with provider management - Implement Gutenberg sidebar with writing assistance controls - Add SEO schema generation for AI-generated content - Multiple provider support: OpenRouter, local backend, Codex
690 lines
20 KiB
PHP
690 lines
20 KiB
PHP
<?php
|
|
/**
|
|
* Image Manager Class
|
|
*
|
|
* Handles image generation, variant management, and WordPress Media integration.
|
|
*
|
|
* @package WP_Agentic_Writer
|
|
* @since 0.1.0
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Image Manager class.
|
|
*/
|
|
class WP_Agentic_Writer_Image_Manager {
|
|
|
|
/**
|
|
* Singleton instance.
|
|
*
|
|
* @var WP_Agentic_Writer_Image_Manager
|
|
*/
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* Get singleton instance.
|
|
*
|
|
* @return WP_Agentic_Writer_Image_Manager
|
|
*/
|
|
public static function get_instance() {
|
|
if ( null === self::$instance ) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
private function __construct() {
|
|
// Private constructor for singleton.
|
|
}
|
|
|
|
/**
|
|
* Create database tables on plugin activation.
|
|
*/
|
|
public function create_tables() {
|
|
global $wpdb;
|
|
|
|
$charset_collate = $wpdb->get_charset_collate();
|
|
|
|
// Table 1: wp_wpaw_images
|
|
$table_images = $wpdb->prefix . 'wpaw_images';
|
|
$sql_images = "CREATE TABLE IF NOT EXISTS `{$table_images}` (
|
|
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
|
`post_id` bigint(20) NOT NULL,
|
|
`agent_image_id` varchar(50) NOT NULL,
|
|
`placement` varchar(100) DEFAULT NULL,
|
|
`section_title` varchar(255) DEFAULT NULL,
|
|
`prompt_initial` text NOT NULL,
|
|
`alt_text_initial` text DEFAULT NULL,
|
|
`prompt_edited` text DEFAULT NULL,
|
|
`alt_text_edited` text DEFAULT NULL,
|
|
`attachment_id` bigint(20) DEFAULT NULL,
|
|
`status` varchar(30) DEFAULT 'pending',
|
|
`cost_estimate` decimal(10, 4) DEFAULT NULL,
|
|
`cost_actual` decimal(10, 4) DEFAULT NULL,
|
|
`image_model` varchar(100) DEFAULT NULL,
|
|
`created_at` datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
|
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL,
|
|
PRIMARY KEY (`id`),
|
|
KEY `idx_post` (`post_id`),
|
|
KEY `idx_agent_image_id` (`post_id`, `agent_image_id`),
|
|
KEY `idx_status` (`status`),
|
|
KEY `idx_created` (`created_at`)
|
|
) {$charset_collate};";
|
|
|
|
// Table 2: wp_wpaw_images_variants
|
|
$table_variants = $wpdb->prefix . 'wpaw_images_variants';
|
|
$sql_variants = "CREATE TABLE IF NOT EXISTS `{$table_variants}` (
|
|
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
|
`agentic_image_id` bigint(20) NOT NULL,
|
|
`post_id` bigint(20) NOT NULL,
|
|
`agent_image_id` varchar(50) NOT NULL,
|
|
`variant_number` int(11) DEFAULT 1,
|
|
`temp_file_path` varchar(500) NOT NULL,
|
|
`temp_file_url` varchar(500) NOT NULL,
|
|
`file_size` int(11) DEFAULT NULL,
|
|
`prompt_used` text DEFAULT NULL,
|
|
`image_model_used` varchar(100) DEFAULT NULL,
|
|
`generation_time` int(11) DEFAULT NULL,
|
|
`cost` decimal(10, 4) DEFAULT NULL,
|
|
`is_selected` tinyint(1) DEFAULT 0,
|
|
`selected_at` datetime DEFAULT NULL,
|
|
`status` varchar(30) DEFAULT 'temp',
|
|
`created_at` datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
|
`deleted_at` datetime DEFAULT NULL,
|
|
PRIMARY KEY (`id`),
|
|
KEY `idx_agentic_image` (`agentic_image_id`),
|
|
KEY `idx_post` (`post_id`),
|
|
KEY `idx_status` (`status`),
|
|
KEY `idx_created` (`created_at`)
|
|
) {$charset_collate};";
|
|
|
|
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
|
dbDelta( $sql_images );
|
|
dbDelta( $sql_variants );
|
|
|
|
// Create temp directory.
|
|
$this->create_temp_directory();
|
|
}
|
|
|
|
/**
|
|
* Create temp directory for image storage.
|
|
*/
|
|
private function create_temp_directory() {
|
|
$upload_dir = wp_upload_dir();
|
|
$temp_dir = $upload_dir['basedir'] . '/wpaw';
|
|
|
|
if ( ! file_exists( $temp_dir ) ) {
|
|
wp_mkdir_p( $temp_dir );
|
|
|
|
// Add .htaccess to prevent direct access.
|
|
$htaccess = $temp_dir . '/.htaccess';
|
|
if ( ! file_exists( $htaccess ) ) {
|
|
file_put_contents( $htaccess, "Options -Indexes\n" );
|
|
}
|
|
|
|
// Add index.php for security.
|
|
$index = $temp_dir . '/index.php';
|
|
if ( ! file_exists( $index ) ) {
|
|
file_put_contents( $index, "<?php // Silence is golden\n" );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Analyze article for optimal image placement.
|
|
*
|
|
* @param string $article_markdown Article content in markdown.
|
|
* @param int $post_id Post ID.
|
|
* @return array|WP_Error Placement data or error.
|
|
*/
|
|
public function analyze_article_for_images( $article_markdown, $post_id ) {
|
|
$settings = get_option( 'wp_agentic_writer_settings', array() );
|
|
$writing_model = $settings['writing_model'] ?? 'anthropic/claude-3.5-sonnet';
|
|
|
|
$system_prompt = "You are an expert content strategist analyzing articles for optimal image placement.
|
|
|
|
Your task: Identify 2-3 strategic locations where images would enhance understanding and engagement.
|
|
|
|
RULES:
|
|
1. Prioritize placement after introduction (hero image)
|
|
2. Consider complex sections that need visual aids
|
|
3. Look for opportunities before conclusions
|
|
4. Maximum 3 images per article
|
|
|
|
Return JSON:
|
|
{
|
|
\"recommended_image_count\": 3,
|
|
\"image_placement_points\": [
|
|
{
|
|
\"agent_image_id\": \"img_hero_1\",
|
|
\"placement\": \"after_introduction\",
|
|
\"section_title\": \"Introduction\",
|
|
\"image_type\": \"hero_dashboard\",
|
|
\"reasoning\": \"Sets visual tone for article\"
|
|
}
|
|
]
|
|
}";
|
|
|
|
$messages = array(
|
|
array(
|
|
'role' => 'user',
|
|
'content' => "Analyze this article for image placement:\n\n" . $article_markdown,
|
|
),
|
|
);
|
|
|
|
$provider = WP_Agentic_Writer_Provider_Manager::get_provider_for_task( 'planning' );
|
|
$response = $provider->chat( $messages, array( 'temperature' => 0.3 ), 'planning' );
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
return $response;
|
|
}
|
|
|
|
// Extract JSON from response.
|
|
$json_match = array();
|
|
if ( preg_match( '/\{[\s\S]*\}/m', $response['content'], $json_match ) ) {
|
|
$placement_data = json_decode( $json_match[0], true );
|
|
if ( JSON_ERROR_NONE === json_last_error() ) {
|
|
return $placement_data;
|
|
}
|
|
}
|
|
|
|
return new WP_Error( 'parse_error', 'Failed to parse placement analysis' );
|
|
}
|
|
|
|
/**
|
|
* Generate image prompts optimized for specific image model.
|
|
*
|
|
* @param string $article_markdown Article content.
|
|
* @param array $placement_data Placement analysis.
|
|
* @param int $post_id Post ID.
|
|
* @return array|WP_Error Image specifications or error.
|
|
*/
|
|
public function generate_image_prompts( $article_markdown, $placement_data, $post_id ) {
|
|
$settings = get_option( 'wp_agentic_writer_settings', array() );
|
|
$writing_model = $settings['writing_model'] ?? 'anthropic/claude-3.5-sonnet';
|
|
$image_model = $settings['image_model'] ?? 'openai/gpt-4o';
|
|
|
|
// Get model-specific prompt guidance.
|
|
$prompt_guidance = $this->get_prompt_guidance_for_model( $image_model );
|
|
|
|
$system_prompt = "You are an Image Prompt Engineer specializing in {$prompt_guidance['model_name']}.
|
|
|
|
TARGET MODEL: {$prompt_guidance['model_name']}
|
|
PROMPT LENGTH: {$prompt_guidance['prompt_length']}
|
|
COMPLEXITY: {$prompt_guidance['complexity']}
|
|
|
|
{$prompt_guidance['guidance']}
|
|
|
|
TEMPLATE: {$prompt_guidance['template']}
|
|
|
|
Generate precise, cost-efficient prompts that exploit this model's strengths.
|
|
|
|
Return JSON:
|
|
{
|
|
\"images\": [
|
|
{
|
|
\"agent_image_id\": \"img_hero_1\",
|
|
\"placement\": \"after_introduction\",
|
|
\"section_title\": \"Introduction\",
|
|
\"prompt\": \"[Model-optimized prompt]\",
|
|
\"alt\": \"Descriptive alt text\",
|
|
\"image_model\": \"{$image_model}\"
|
|
}
|
|
]
|
|
}";
|
|
|
|
$user_input = wp_json_encode(
|
|
array(
|
|
'article' => $article_markdown,
|
|
'placement_points' => $placement_data['image_placement_points'],
|
|
'image_count' => $placement_data['recommended_image_count'],
|
|
'target_image_model' => $image_model,
|
|
)
|
|
);
|
|
|
|
$messages = array(
|
|
array(
|
|
'role' => 'user',
|
|
'content' => "Generate image prompts:\n\n" . $user_input,
|
|
),
|
|
);
|
|
|
|
$provider = WP_Agentic_Writer_Provider_Manager::get_provider_for_task( 'planning' );
|
|
$response = $provider->chat( $messages, array( 'temperature' => 0.7 ), 'planning' );
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
return $response;
|
|
}
|
|
|
|
// Extract JSON.
|
|
$json_match = array();
|
|
if ( preg_match( '/\{[\s\S]*\}/m', $response['content'], $json_match ) ) {
|
|
$image_specs = json_decode( $json_match[0], true );
|
|
if ( JSON_ERROR_NONE === json_last_error() ) {
|
|
// Save to database.
|
|
$this->save_image_recommendations( $post_id, $image_specs['images'] );
|
|
return $image_specs;
|
|
}
|
|
}
|
|
|
|
return new WP_Error( 'parse_error', 'Failed to parse image prompts' );
|
|
}
|
|
|
|
/**
|
|
* Get prompt guidance for specific image model.
|
|
*
|
|
* @param string $image_model Image model ID.
|
|
* @return array Model configuration.
|
|
*/
|
|
private function get_prompt_guidance_for_model( $image_model ) {
|
|
$model_configs = array(
|
|
'black-forest-labs/flux.2-klein' => array(
|
|
'model_name' => 'FLUX.2 [klein]',
|
|
'prompt_length' => '1-2 sentences',
|
|
'complexity' => 'simple',
|
|
'guidance' => 'Keep prompts short and simple. Focus on main subject, key details, and style. Avoid complex scenes or technical specifications.',
|
|
'template' => 'Subject, key elements, style, color palette',
|
|
),
|
|
'sourceful/riverflow-v2-max' => array(
|
|
'model_name' => 'Riverflow V2 Max',
|
|
'prompt_length' => '3-4 sentences',
|
|
'complexity' => 'medium-detailed',
|
|
'guidance' => 'Include context, environment details, lighting style, and photographic specifications. Model excels at photorealism.',
|
|
'template' => 'Subject + context, environment details, lighting style, photography style, technical specs',
|
|
),
|
|
'black-forest-labs/flux.2-max' => array(
|
|
'model_name' => 'FLUX.2 [max]',
|
|
'prompt_length' => '4-6 sentences',
|
|
'complexity' => 'very-detailed-technical',
|
|
'guidance' => 'Use detailed technical vocabulary. Include exact materials, color codes (HEX), spatial relationships, and specifications.',
|
|
'template' => 'Technical foundation, main subject + action, environment, lighting + mood, style + aesthetics, technical specifications',
|
|
),
|
|
);
|
|
|
|
// Default to Riverflow if model not found.
|
|
return $model_configs[ $image_model ] ?? $model_configs['sourceful/riverflow-v2-max'];
|
|
}
|
|
|
|
/**
|
|
* Save image recommendations to database.
|
|
*
|
|
* @param int $post_id Post ID.
|
|
* @param array $images Image specifications.
|
|
*/
|
|
private function save_image_recommendations( $post_id, $images ) {
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'wpaw_images';
|
|
|
|
foreach ( $images as $image_spec ) {
|
|
$wpdb->insert(
|
|
$table,
|
|
array(
|
|
'post_id' => $post_id,
|
|
'agent_image_id' => $image_spec['agent_image_id'],
|
|
'placement' => $image_spec['placement'],
|
|
'section_title' => $image_spec['section_title'],
|
|
'prompt_initial' => $image_spec['prompt'],
|
|
'alt_text_initial' => $image_spec['alt'],
|
|
'image_model' => $image_spec['image_model'],
|
|
'status' => 'pending',
|
|
),
|
|
array( '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save single image recommendation to database.
|
|
*
|
|
* @param int $post_id Post ID.
|
|
* @param string $agent_image_id Unique image identifier.
|
|
* @param string $placement Placement location.
|
|
* @param string $section_title Section title.
|
|
* @param string $prompt Image prompt/description.
|
|
* @param string $alt_text Alt text for image.
|
|
* @return int|false Insert ID or false on failure.
|
|
*/
|
|
public function save_image_recommendation( $post_id, $agent_image_id, $placement, $section_title, $prompt, $alt_text ) {
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'wpaw_images';
|
|
|
|
$settings = get_option( 'wp_agentic_writer_settings', array() );
|
|
$image_model = $settings['image_model'] ?? 'openai/gpt-4o';
|
|
|
|
$result = $wpdb->insert(
|
|
$table,
|
|
array(
|
|
'post_id' => $post_id,
|
|
'agent_image_id' => $agent_image_id,
|
|
'placement' => $placement,
|
|
'section_title' => $section_title,
|
|
'prompt_initial' => $prompt,
|
|
'alt_text_initial' => $alt_text,
|
|
'image_model' => $image_model,
|
|
'status' => 'pending',
|
|
),
|
|
array( '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )
|
|
);
|
|
|
|
if ( $result ) {
|
|
return $wpdb->insert_id;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get image recommendations for a post.
|
|
*
|
|
* @param int $post_id Post ID.
|
|
* @return array Image recommendations.
|
|
*/
|
|
public function get_image_recommendations( $post_id ) {
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'wpaw_images';
|
|
|
|
$results = $wpdb->get_results(
|
|
$wpdb->prepare(
|
|
"SELECT * FROM {$table} WHERE post_id = %d ORDER BY created_at ASC",
|
|
$post_id
|
|
),
|
|
ARRAY_A
|
|
);
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Generate image variants.
|
|
*
|
|
* @param int $post_id Post ID.
|
|
* @param string $agent_image_id Agent image ID.
|
|
* @param string $prompt Image prompt.
|
|
* @param int $variant_count Number of variants to generate.
|
|
* @return array|WP_Error Generated variants or error.
|
|
*/
|
|
public function generate_image_variants( $post_id, $agent_image_id, $prompt, $variant_count = 2 ) {
|
|
$settings = get_option( 'wp_agentic_writer_settings', array() );
|
|
$image_model = $settings['image_model'] ?? 'openai/gpt-4o';
|
|
|
|
$provider = WP_Agentic_Writer_Provider_Manager::get_provider_for_task( 'image' );
|
|
|
|
$variants = array();
|
|
|
|
for ( $i = 1; $i <= $variant_count; $i++ ) {
|
|
$result = $provider->generate_image(
|
|
$prompt,
|
|
$image_model,
|
|
array(
|
|
'size' => '1024x576',
|
|
'quality' => 'hd',
|
|
'n' => 1,
|
|
)
|
|
);
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
return $result;
|
|
}
|
|
|
|
// Download image to temp directory.
|
|
$temp_file = $this->download_temp_image( $post_id, $agent_image_id, $result['url'], $i );
|
|
|
|
if ( is_wp_error( $temp_file ) ) {
|
|
return $temp_file;
|
|
}
|
|
|
|
// Save variant to database.
|
|
$variant_id = $this->save_variant(
|
|
$post_id,
|
|
$agent_image_id,
|
|
$i,
|
|
$temp_file,
|
|
$prompt,
|
|
$image_model,
|
|
$result
|
|
);
|
|
|
|
$variants[] = array(
|
|
'id' => $variant_id,
|
|
'variant_number' => $i,
|
|
'temp_file_url' => $temp_file['url'],
|
|
'cost' => $result['cost'],
|
|
'generation_time' => $result['generation_time'],
|
|
'image_model_used' => $image_model,
|
|
);
|
|
}
|
|
|
|
return $variants;
|
|
}
|
|
|
|
/**
|
|
* Download image to temp directory.
|
|
*
|
|
* @param int $post_id Post ID.
|
|
* @param string $agent_image_id Agent image ID.
|
|
* @param string $image_url Image URL.
|
|
* @param int $variant_number Variant number.
|
|
* @return array|WP_Error File info or error.
|
|
*/
|
|
private function download_temp_image( $post_id, $agent_image_id, $image_url, $variant_number ) {
|
|
$upload_dir = wp_upload_dir();
|
|
$temp_dir = $upload_dir['basedir'] . '/wpaw/' . $post_id;
|
|
|
|
if ( ! file_exists( $temp_dir ) ) {
|
|
wp_mkdir_p( $temp_dir );
|
|
}
|
|
|
|
// Download image.
|
|
$response = wp_remote_get( $image_url, array( 'timeout' => 30 ) );
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
return $response;
|
|
}
|
|
|
|
$image_data = wp_remote_retrieve_body( $response );
|
|
|
|
// Determine file extension from content type.
|
|
$content_type = wp_remote_retrieve_header( $response, 'content-type' );
|
|
$extension = 'jpg';
|
|
if ( strpos( $content_type, 'png' ) !== false ) {
|
|
$extension = 'png';
|
|
}
|
|
|
|
$filename = sprintf(
|
|
'%s_variant_%d_%d.%s',
|
|
$agent_image_id,
|
|
$variant_number,
|
|
time(),
|
|
$extension
|
|
);
|
|
|
|
$file_path = $temp_dir . '/' . $filename;
|
|
file_put_contents( $file_path, $image_data );
|
|
|
|
$file_url = $upload_dir['baseurl'] . '/wpaw/' . $post_id . '/' . $filename;
|
|
|
|
return array(
|
|
'path' => $file_path,
|
|
'url' => $file_url,
|
|
'size' => filesize( $file_path ),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Save variant to database.
|
|
*
|
|
* @param int $post_id Post ID.
|
|
* @param string $agent_image_id Agent image ID.
|
|
* @param int $variant_number Variant number.
|
|
* @param array $temp_file Temp file info.
|
|
* @param string $prompt Prompt used.
|
|
* @param string $image_model Image model used.
|
|
* @param array $generation_result Generation result.
|
|
* @return int Variant ID.
|
|
*/
|
|
private function save_variant( $post_id, $agent_image_id, $variant_number, $temp_file, $prompt, $image_model, $generation_result ) {
|
|
global $wpdb;
|
|
|
|
// Get agentic_image_id from wp_wpaw_images.
|
|
$table_images = $wpdb->prefix . 'wpaw_images';
|
|
$agentic_image_id = $wpdb->get_var(
|
|
$wpdb->prepare(
|
|
"SELECT id FROM {$table_images} WHERE post_id = %d AND agent_image_id = %s",
|
|
$post_id,
|
|
$agent_image_id
|
|
)
|
|
);
|
|
|
|
$table_variants = $wpdb->prefix . 'wpaw_images_variants';
|
|
|
|
$wpdb->insert(
|
|
$table_variants,
|
|
array(
|
|
'agentic_image_id' => $agentic_image_id,
|
|
'post_id' => $post_id,
|
|
'agent_image_id' => $agent_image_id,
|
|
'variant_number' => $variant_number,
|
|
'temp_file_path' => $temp_file['path'],
|
|
'temp_file_url' => $temp_file['url'],
|
|
'file_size' => $temp_file['size'],
|
|
'prompt_used' => $prompt,
|
|
'image_model_used' => $image_model,
|
|
'generation_time' => $generation_result['generation_time'],
|
|
'cost' => $generation_result['cost'],
|
|
'status' => 'temp',
|
|
),
|
|
array( '%d', '%d', '%s', '%d', '%s', '%s', '%d', '%s', '%s', '%d', '%f', '%s' )
|
|
);
|
|
|
|
return $wpdb->insert_id;
|
|
}
|
|
|
|
/**
|
|
* Commit image variant to WordPress Media Library.
|
|
*
|
|
* @param int $post_id Post ID.
|
|
* @param string $agent_image_id Agent image ID.
|
|
* @param int $variant_id Variant ID.
|
|
* @param string $alt_text Alt text.
|
|
* @return array|WP_Error Attachment info or error.
|
|
*/
|
|
public function commit_image_variant( $post_id, $agent_image_id, $variant_id, $alt_text ) {
|
|
global $wpdb;
|
|
|
|
// Get variant info.
|
|
$table_variants = $wpdb->prefix . 'wpaw_images_variants';
|
|
$variant = $wpdb->get_row(
|
|
$wpdb->prepare(
|
|
"SELECT * FROM {$table_variants} WHERE id = %d",
|
|
$variant_id
|
|
),
|
|
ARRAY_A
|
|
);
|
|
|
|
if ( ! $variant ) {
|
|
return new WP_Error( 'variant_not_found', 'Variant not found' );
|
|
}
|
|
|
|
// Upload to Media Library.
|
|
require_once ABSPATH . 'wp-admin/includes/image.php';
|
|
require_once ABSPATH . 'wp-admin/includes/file.php';
|
|
require_once ABSPATH . 'wp-admin/includes/media.php';
|
|
|
|
$file_array = array(
|
|
'name' => basename( $variant['temp_file_path'] ),
|
|
'tmp_name' => $variant['temp_file_path'],
|
|
);
|
|
|
|
$attachment_id = media_handle_sideload( $file_array, $post_id );
|
|
|
|
if ( is_wp_error( $attachment_id ) ) {
|
|
return $attachment_id;
|
|
}
|
|
|
|
// Set alt text.
|
|
update_post_meta( $attachment_id, '_wp_attachment_image_alt', sanitize_text_field( $alt_text ) );
|
|
|
|
// Update wp_wpaw_images table.
|
|
$table_images = $wpdb->prefix . 'wpaw_images';
|
|
$wpdb->update(
|
|
$table_images,
|
|
array(
|
|
'attachment_id' => $attachment_id,
|
|
'status' => 'committed',
|
|
),
|
|
array(
|
|
'post_id' => $post_id,
|
|
'agent_image_id' => $agent_image_id,
|
|
),
|
|
array( '%d', '%s' ),
|
|
array( '%d', '%s' )
|
|
);
|
|
|
|
// Mark variant as selected.
|
|
$wpdb->update(
|
|
$table_variants,
|
|
array(
|
|
'is_selected' => 1,
|
|
'selected_at' => current_time( 'mysql' ),
|
|
'status' => 'selected',
|
|
),
|
|
array( 'id' => $variant_id ),
|
|
array( '%d', '%s', '%s' ),
|
|
array( '%d' )
|
|
);
|
|
|
|
$attachment_url = wp_get_attachment_url( $attachment_id );
|
|
|
|
return array(
|
|
'attachment_id' => $attachment_id,
|
|
'attachment_url' => $attachment_url,
|
|
'alt' => $alt_text,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Cleanup old temp images (7+ days old).
|
|
*/
|
|
public function cleanup_old_temp_images() {
|
|
global $wpdb;
|
|
|
|
$table_variants = $wpdb->prefix . 'wpaw_images_variants';
|
|
|
|
// Get temp images older than 7 days.
|
|
$old_variants = $wpdb->get_results(
|
|
$wpdb->prepare(
|
|
"SELECT * FROM {$table_variants}
|
|
WHERE status = 'temp'
|
|
AND created_at < DATE_SUB(NOW(), INTERVAL %d DAY)",
|
|
7
|
|
),
|
|
ARRAY_A
|
|
);
|
|
|
|
foreach ( $old_variants as $variant ) {
|
|
// Delete file.
|
|
if ( file_exists( $variant['temp_file_path'] ) ) {
|
|
unlink( $variant['temp_file_path'] );
|
|
}
|
|
|
|
// Update status.
|
|
$wpdb->update(
|
|
$table_variants,
|
|
array(
|
|
'status' => 'auto_deleted',
|
|
'deleted_at' => current_time( 'mysql' ),
|
|
),
|
|
array( 'id' => $variant['id'] ),
|
|
array( '%s', '%s' ),
|
|
array( '%d' )
|
|
);
|
|
}
|
|
}
|
|
}
|