Plugin Directory

Changeset 3390455


Ignore:
Timestamp:
11/05/2025 12:42:16 PM (5 months ago)
Author:
purposego
Message:

Release version 1.2.2

Location:
clearpost-simple-ai-auto-post
Files:
5 edited
28 copied

Legend:

Unmodified
Added
Removed
  • clearpost-simple-ai-auto-post/tags/1.2.2/includes/ai-requests.php

    r3387025 r3390455  
    119119    } elseif ( $provider === 'anthropic' ) {
    120120        $tool_schema = array(
     121            'type'          => 'custom',
    121122            'name'          => 'json_responder',
    122123            'description'   => 'Responds with a JSON object containing a chat message and edited content.',
     
    157158            'model'       => $model ?: 'claude-sonnet-4-20250514',
    158159            'messages'    => $anthropic_messages,
    159             'tool_choice' => 'auto',
    160             'tools'       => array( array( 'type' => 'web_search' ), $tool_schema ),
     160            'tool_choice' => array( 'type' => 'tool', 'name' => 'json_responder' ),
     161            'tools'       => array( array( 'type' => 'web_search_20250305', 'name' => 'web_search' ), $tool_schema ),
    161162        );
    162163
  • clearpost-simple-ai-auto-post/tags/1.2.2/includes/generate.php

    r3387025 r3390455  
    8383    $post_data = array(
    8484        'post_title'   => ! empty( $args['title'] ) ? $args['title'] : $generated['title'],
    85         'post_content' => $generated['content'],
     85        'post_content' => saiap_prepare_post_content( $generated['content'] ),
    8686        'post_status'  => 'draft',
    8787        'post_type'    => $args['post_type'],
     
    154154        'edit_url' => get_edit_post_link( $post_id, '' ),
    155155    );
     156}
     157
     158/**
     159 * Normalize generated content for WordPress/Gutenberg display.
     160 * - If content already contains blocks or <p> tags, return as-is.
     161 * - Otherwise, convert plaintext newlines to paragraphs using wpautop and sanitize.
     162 *
     163 * @param string $raw Raw generated content
     164 * @return string Normalized HTML content
     165 */
     166function saiap_prepare_post_content( $raw ) {
     167    if ( ! is_string( $raw ) || $raw === '' ) {
     168        return '';
     169    }
     170    // If the content already contains block delimiters or existing paragraph tags, keep it
     171    if ( ( function_exists( 'has_blocks' ) && has_blocks( $raw ) ) ||
     172         strpos( $raw, '<!-- wp:' ) !== false ||
     173         strpos( $raw, '<p' ) !== false ) {
     174        return $raw;
     175    }
     176
     177    // Normalize line endings and convert to paragraphs
     178    $normalized = str_replace( array( "\r\n", "\r" ), "\n", $raw );
     179    $html       = wpautop( $normalized );
     180
     181    // Sanitize to allowed post HTML
     182    return wp_kses_post( $html );
    156183}
    157184
  • clearpost-simple-ai-auto-post/tags/1.2.2/includes/site-context.php

    r3387025 r3390455  
    465465    $context = saiap_get_site_context();
    466466
    467     $max_retries = 2;
    468     $retry_delay = 3; // seconds
    469 
    470     for ( $attempt = 0; $attempt <= $max_retries; $attempt++ ) {
    471         if ( $attempt > 0 ) {
    472             sleep( $retry_delay );
    473         }
    474 
    475         // Log the posts data being sent to API
    476         $posts_for_api = array_map(
    477             function ( $post ) {
    478                 return array(
    479                     'ID'           => $post['ID'],
    480                     'title'        => $post['title'],
    481                     'content'      => $post['content'],
    482                     'published_at' => $post['published_at'],
    483                     'modified_at'  => $post['modified_at'],
    484                     'post_type'    => $post['post_type'],
    485                     'taxonomy_data' => isset( $post['taxonomy_data'] ) ? $post['taxonomy_data'] : null,
    486                 );
    487             },
    488             $context['posts']
     467    // Prepare posts for API and collect present IDs
     468    $posts_for_api = array_map(
     469        function ( $post ) {
     470            return array(
     471                'ID'           => $post['ID'],
     472                'title'        => $post['title'],
     473                'content'      => $post['content'],
     474                'published_at' => $post['published_at'],
     475                'modified_at'  => $post['modified_at'],
     476                'post_type'    => $post['post_type'],
     477                'taxonomy_data' => isset( $post['taxonomy_data'] ) ? $post['taxonomy_data'] : null,
     478            );
     479        },
     480        $context['posts']
     481    );
     482    $present_post_ids = array_map(
     483        function( $p ) { return $p['ID']; },
     484        $posts_for_api
     485    );
     486
     487    // Chunk posts to keep request size low
     488    $chunk_size = 20; // heuristic; adjust as needed
     489    $chunks = array_chunk( $posts_for_api, $chunk_size );
     490
     491    // Send partial chunks (aggregate errors, don't early return)
     492    $partial_errors    = array();
     493    $successful_chunks = 0;
     494    $total_chunks      = count( $chunks );
     495    foreach ( $chunks as $chunk_index => $chunk_posts ) {
     496        $partial_body = wp_json_encode(
     497            array(
     498                'license_key' => $license_key,
     499                'domain'      => $context['site_data']['domain'],
     500                'site_data'   => array(
     501                    'homepage_content' => $context['site_data']['homepage_content'],
     502                    'site_description' => $context['site_data']['site_description'],
     503                ),
     504                'posts'       => $chunk_posts,
     505                'is_partial'  => true,
     506                // Include aggregated metadata for richer context on the premium side
     507                'post_types'  => $context['postTypes'],
     508                'taxonomies'  => $context['taxonomies'],
     509                'customContext' => isset( $context['customContext'] ) ? $context['customContext'] : null,
     510            )
    489511        );
    490        
    491        
    492512
    493513        $response = wp_remote_post(
     
    498518                    'Accept'       => 'application/json',
    499519                ),
    500                 'body'    => wp_json_encode(
    501                     array(
    502                         'license_key' => $license_key,
    503                         'domain'      => $context['site_data']['domain'],
    504                         'site_data'   => array(
    505                             'homepage_content' => $context['site_data']['homepage_content'],
    506                             'site_description' => $context['site_data']['site_description'],
    507                         ),
    508                         'posts'       => $posts_for_api,
    509                         // Include aggregated metadata for richer context on the premium side
    510                         'post_types'  => $context['postTypes'],
    511                         'taxonomies'  => $context['taxonomies'],
    512                         'customContext' => isset( $context['customContext'] ) ? $context['customContext'] : null,
    513                     )
    514                 ),
    515                 'timeout' => 60, // Longer timeout for full context update
     520                'body'    => $partial_body,
     521                'timeout' => 60,
    516522            )
    517523        );
    518524
    519         // Log response details
    520         if ( is_wp_error( $response ) ) {
    521 
    522         } else {
    523             $status_code = wp_remote_retrieve_response_code( $response );
    524             $headers     = wp_remote_retrieve_headers( $response );
    525 
    526             $body = wp_remote_retrieve_body( $response );
    527 
    528             $data = json_decode( $body, true );
    529         }
    530 
    531         if ( ! is_wp_error( $response ) ) {
    532             $status_code = wp_remote_retrieve_response_code( $response );
    533             if ( $status_code === 200 ) {
    534                 break; // Success, exit retry loop
    535             }
    536             if ( $status_code !== 503 && $status_code !== 404 ) {
    537                 break; // Don't retry on non-503/404 errors
    538             }
    539         }
    540     }
    541 
    542     if ( is_wp_error( $response ) ) {
    543         return new WP_Error( 'api_error', 'Failed to update context: ' . $response->get_error_message() );
    544     }
     525        if ( is_wp_error( $response ) ) {
     526            $partial_errors[] = array(
     527                'chunk'   => $chunk_index + 1,
     528                'total'   => $total_chunks,
     529                'message' => $response->get_error_message(),
     530                'status'  => null,
     531            );
     532            continue;
     533        }
     534
     535        $status_code = wp_remote_retrieve_response_code( $response );
     536        if ( $status_code !== 200 ) {
     537            $body_err     = wp_remote_retrieve_body( $response );
     538            $data_err     = json_decode( $body_err, true );
     539            $error_detail = ( $data_err && isset( $data_err['error'] ) ) ? $data_err['error'] : 'Server returned status code: ' . $status_code;
     540            $partial_errors[] = array(
     541                'chunk'   => $chunk_index + 1,
     542                'total'   => $total_chunks,
     543                'message' => $error_detail,
     544                'status'  => $status_code,
     545            );
     546            continue;
     547        }
     548
     549        $successful_chunks++;
     550    }
     551
     552    // Final prune request with IDs only
     553    $prune_body = wp_json_encode(
     554        array(
     555            'license_key'       => $license_key,
     556            'domain'            => $context['site_data']['domain'],
     557            'present_post_ids'  => $present_post_ids,
     558        )
     559    );
     560
     561    $response = wp_remote_post(
     562        'https://saiap.gopurposego.com/api/context/update',
     563        array(
     564            'headers' => array(
     565                'Content-Type' => 'application/json',
     566                'Accept'       => 'application/json',
     567            ),
     568            'body'    => $prune_body,
     569            'timeout' => 60,
     570        )
     571    );
     572
     573    if ( is_wp_error( $response ) ) {
     574        $aggregate_note = '';
     575        if ( ! empty( $partial_errors ) ) {
     576            $aggregate_note = ' Partial errors: ' . wp_json_encode( $partial_errors );
     577        }
     578        return new WP_Error( 'api_error', 'Failed to finalize context update: ' . $response->get_error_message() . $aggregate_note );
     579    }
    545580
    546581    $status_code = wp_remote_retrieve_response_code( $response );
     
    548583    $data        = json_decode( $body, true );
    549584
    550     if ( $status_code !== 200 ) {
    551         $error_message = 'Failed to update context. ';
    552         if ( $data && isset( $data['error'] ) ) {
    553             $error_message .= $data['error'];
    554         } else {
    555             $error_message .= 'Server returned status code: ' . $status_code;
    556         }
    557         return new WP_Error( 'api_error', $error_message );
    558     }
     585    if ( $status_code !== 200 ) {
     586        $error_message = 'Failed to finalize context update. ';
     587        if ( $data && isset( $data['error'] ) ) {
     588            $error_message .= $data['error'];
     589        } else {
     590            $error_message .= 'Server returned status code: ' . $status_code;
     591        }
     592        if ( ! empty( $partial_errors ) ) {
     593            $error_message .= ' Partial errors: ' . wp_json_encode( $partial_errors );
     594        }
     595        return new WP_Error( 'api_error', $error_message );
     596    }
     597
     598    // If all chunks failed but prune succeeded, surface aggregated error
     599    if ( $successful_chunks === 0 && $total_chunks > 0 ) {
     600        return new WP_Error( 'api_error', 'Failed to update context: all ' . $total_chunks . ' chunk(s) failed. Details: ' . wp_json_encode( $partial_errors ) );
     601    }
    559602
    560603    // Store the last update timestamp
     
    571614
    572615    // Set appropriate message based on changes and failures
    573     if ( ! $has_changes ) {
     616    if ( ! $has_changes ) {
    574617        $message = 'No changes detected. Site context remains the same.';
    575618    } else {
    576         $message = 'Site context updated successfully.';
    577 
    578         if ( isset( $data['syncResult'] ) && isset( $data['syncResult']['failedCount'] ) && $data['syncResult']['failedCount'] > 0 ) {
     619        $message = 'Site context updated successfully.';
     620
     621        if ( isset( $data['syncResult'] ) && isset( $data['syncResult']['failedCount'] ) && $data['syncResult']['failedCount'] > 0 ) {
    579622            $has_failures = true;
    580623            $message     .= ' However, ' . $data['syncResult']['failedCount'] . ' posts failed to sync.';
     
    582625    }
    583626
     627    // Reflect any transport-level chunk failures in the flags/message
     628    if ( ! empty( $partial_errors ) ) {
     629        $has_failures = true;
     630        $message     .= ' Some chunks failed during upload.';
     631    }
     632
    584633    // Update cache if context is in the response
    585634    if ( isset( $data['context'] ) ) {
     
    587636    }
    588637
    589     return array(
     638    return array(
    590639        'message'      => $message,
    591640        'has_failures' => $has_failures,
     
    593642        'sync_result'  => isset( $data['syncResult'] ) ? $data['syncResult'] : null,
    594643        'timestamp'    => $timestamp,
     644        'partial_errors' => ! empty( $partial_errors ) ? $partial_errors : null,
    595645    );
    596646}
  • clearpost-simple-ai-auto-post/tags/1.2.2/readme.txt

    r3387025 r3390455  
    44Requires at least: 5.0
    55Tested up to: 6.8
    6 Stable tag: 1.2.0
     6Stable tag: 1.2.2
    77Requires PHP: 7.2
    88License: GPLv2 or later
  • clearpost-simple-ai-auto-post/tags/1.2.2/simple-ai-auto-post.php

    r3387025 r3390455  
    44Description: Your AI Agent for SEO, in WordPress. An AI content marketer that knows your site, then schedules and generates posts every day.
    55Plugin URI: https://clearpostplugin.com/
    6 Version: 1.2.0
     6Version: 1.2.2
    77License: GPLv2 or later
    88License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    1818
    1919// Define plugin version
    20 define( 'SAIAP_VERSION', '1.2.0' );
     20define( 'SAIAP_VERSION', '1.2.2' );
    2121
    2222// Optionally enable Demo Mode (for internal testing only)
  • clearpost-simple-ai-auto-post/trunk/includes/ai-requests.php

    r3387025 r3390455  
    119119    } elseif ( $provider === 'anthropic' ) {
    120120        $tool_schema = array(
     121            'type'          => 'custom',
    121122            'name'          => 'json_responder',
    122123            'description'   => 'Responds with a JSON object containing a chat message and edited content.',
     
    157158            'model'       => $model ?: 'claude-sonnet-4-20250514',
    158159            'messages'    => $anthropic_messages,
    159             'tool_choice' => 'auto',
    160             'tools'       => array( array( 'type' => 'web_search' ), $tool_schema ),
     160            'tool_choice' => array( 'type' => 'tool', 'name' => 'json_responder' ),
     161            'tools'       => array( array( 'type' => 'web_search_20250305', 'name' => 'web_search' ), $tool_schema ),
    161162        );
    162163
  • clearpost-simple-ai-auto-post/trunk/includes/generate.php

    r3387025 r3390455  
    8383    $post_data = array(
    8484        'post_title'   => ! empty( $args['title'] ) ? $args['title'] : $generated['title'],
    85         'post_content' => $generated['content'],
     85        'post_content' => saiap_prepare_post_content( $generated['content'] ),
    8686        'post_status'  => 'draft',
    8787        'post_type'    => $args['post_type'],
     
    154154        'edit_url' => get_edit_post_link( $post_id, '' ),
    155155    );
     156}
     157
     158/**
     159 * Normalize generated content for WordPress/Gutenberg display.
     160 * - If content already contains blocks or <p> tags, return as-is.
     161 * - Otherwise, convert plaintext newlines to paragraphs using wpautop and sanitize.
     162 *
     163 * @param string $raw Raw generated content
     164 * @return string Normalized HTML content
     165 */
     166function saiap_prepare_post_content( $raw ) {
     167    if ( ! is_string( $raw ) || $raw === '' ) {
     168        return '';
     169    }
     170    // If the content already contains block delimiters or existing paragraph tags, keep it
     171    if ( ( function_exists( 'has_blocks' ) && has_blocks( $raw ) ) ||
     172         strpos( $raw, '<!-- wp:' ) !== false ||
     173         strpos( $raw, '<p' ) !== false ) {
     174        return $raw;
     175    }
     176
     177    // Normalize line endings and convert to paragraphs
     178    $normalized = str_replace( array( "\r\n", "\r" ), "\n", $raw );
     179    $html       = wpautop( $normalized );
     180
     181    // Sanitize to allowed post HTML
     182    return wp_kses_post( $html );
    156183}
    157184
  • clearpost-simple-ai-auto-post/trunk/includes/site-context.php

    r3387025 r3390455  
    465465    $context = saiap_get_site_context();
    466466
    467     $max_retries = 2;
    468     $retry_delay = 3; // seconds
    469 
    470     for ( $attempt = 0; $attempt <= $max_retries; $attempt++ ) {
    471         if ( $attempt > 0 ) {
    472             sleep( $retry_delay );
    473         }
    474 
    475         // Log the posts data being sent to API
    476         $posts_for_api = array_map(
    477             function ( $post ) {
    478                 return array(
    479                     'ID'           => $post['ID'],
    480                     'title'        => $post['title'],
    481                     'content'      => $post['content'],
    482                     'published_at' => $post['published_at'],
    483                     'modified_at'  => $post['modified_at'],
    484                     'post_type'    => $post['post_type'],
    485                     'taxonomy_data' => isset( $post['taxonomy_data'] ) ? $post['taxonomy_data'] : null,
    486                 );
    487             },
    488             $context['posts']
     467    // Prepare posts for API and collect present IDs
     468    $posts_for_api = array_map(
     469        function ( $post ) {
     470            return array(
     471                'ID'           => $post['ID'],
     472                'title'        => $post['title'],
     473                'content'      => $post['content'],
     474                'published_at' => $post['published_at'],
     475                'modified_at'  => $post['modified_at'],
     476                'post_type'    => $post['post_type'],
     477                'taxonomy_data' => isset( $post['taxonomy_data'] ) ? $post['taxonomy_data'] : null,
     478            );
     479        },
     480        $context['posts']
     481    );
     482    $present_post_ids = array_map(
     483        function( $p ) { return $p['ID']; },
     484        $posts_for_api
     485    );
     486
     487    // Chunk posts to keep request size low
     488    $chunk_size = 20; // heuristic; adjust as needed
     489    $chunks = array_chunk( $posts_for_api, $chunk_size );
     490
     491    // Send partial chunks (aggregate errors, don't early return)
     492    $partial_errors    = array();
     493    $successful_chunks = 0;
     494    $total_chunks      = count( $chunks );
     495    foreach ( $chunks as $chunk_index => $chunk_posts ) {
     496        $partial_body = wp_json_encode(
     497            array(
     498                'license_key' => $license_key,
     499                'domain'      => $context['site_data']['domain'],
     500                'site_data'   => array(
     501                    'homepage_content' => $context['site_data']['homepage_content'],
     502                    'site_description' => $context['site_data']['site_description'],
     503                ),
     504                'posts'       => $chunk_posts,
     505                'is_partial'  => true,
     506                // Include aggregated metadata for richer context on the premium side
     507                'post_types'  => $context['postTypes'],
     508                'taxonomies'  => $context['taxonomies'],
     509                'customContext' => isset( $context['customContext'] ) ? $context['customContext'] : null,
     510            )
    489511        );
    490        
    491        
    492512
    493513        $response = wp_remote_post(
     
    498518                    'Accept'       => 'application/json',
    499519                ),
    500                 'body'    => wp_json_encode(
    501                     array(
    502                         'license_key' => $license_key,
    503                         'domain'      => $context['site_data']['domain'],
    504                         'site_data'   => array(
    505                             'homepage_content' => $context['site_data']['homepage_content'],
    506                             'site_description' => $context['site_data']['site_description'],
    507                         ),
    508                         'posts'       => $posts_for_api,
    509                         // Include aggregated metadata for richer context on the premium side
    510                         'post_types'  => $context['postTypes'],
    511                         'taxonomies'  => $context['taxonomies'],
    512                         'customContext' => isset( $context['customContext'] ) ? $context['customContext'] : null,
    513                     )
    514                 ),
    515                 'timeout' => 60, // Longer timeout for full context update
     520                'body'    => $partial_body,
     521                'timeout' => 60,
    516522            )
    517523        );
    518524
    519         // Log response details
    520         if ( is_wp_error( $response ) ) {
    521 
    522         } else {
    523             $status_code = wp_remote_retrieve_response_code( $response );
    524             $headers     = wp_remote_retrieve_headers( $response );
    525 
    526             $body = wp_remote_retrieve_body( $response );
    527 
    528             $data = json_decode( $body, true );
    529         }
    530 
    531         if ( ! is_wp_error( $response ) ) {
    532             $status_code = wp_remote_retrieve_response_code( $response );
    533             if ( $status_code === 200 ) {
    534                 break; // Success, exit retry loop
    535             }
    536             if ( $status_code !== 503 && $status_code !== 404 ) {
    537                 break; // Don't retry on non-503/404 errors
    538             }
    539         }
    540     }
    541 
    542     if ( is_wp_error( $response ) ) {
    543         return new WP_Error( 'api_error', 'Failed to update context: ' . $response->get_error_message() );
    544     }
     525        if ( is_wp_error( $response ) ) {
     526            $partial_errors[] = array(
     527                'chunk'   => $chunk_index + 1,
     528                'total'   => $total_chunks,
     529                'message' => $response->get_error_message(),
     530                'status'  => null,
     531            );
     532            continue;
     533        }
     534
     535        $status_code = wp_remote_retrieve_response_code( $response );
     536        if ( $status_code !== 200 ) {
     537            $body_err     = wp_remote_retrieve_body( $response );
     538            $data_err     = json_decode( $body_err, true );
     539            $error_detail = ( $data_err && isset( $data_err['error'] ) ) ? $data_err['error'] : 'Server returned status code: ' . $status_code;
     540            $partial_errors[] = array(
     541                'chunk'   => $chunk_index + 1,
     542                'total'   => $total_chunks,
     543                'message' => $error_detail,
     544                'status'  => $status_code,
     545            );
     546            continue;
     547        }
     548
     549        $successful_chunks++;
     550    }
     551
     552    // Final prune request with IDs only
     553    $prune_body = wp_json_encode(
     554        array(
     555            'license_key'       => $license_key,
     556            'domain'            => $context['site_data']['domain'],
     557            'present_post_ids'  => $present_post_ids,
     558        )
     559    );
     560
     561    $response = wp_remote_post(
     562        'https://saiap.gopurposego.com/api/context/update',
     563        array(
     564            'headers' => array(
     565                'Content-Type' => 'application/json',
     566                'Accept'       => 'application/json',
     567            ),
     568            'body'    => $prune_body,
     569            'timeout' => 60,
     570        )
     571    );
     572
     573    if ( is_wp_error( $response ) ) {
     574        $aggregate_note = '';
     575        if ( ! empty( $partial_errors ) ) {
     576            $aggregate_note = ' Partial errors: ' . wp_json_encode( $partial_errors );
     577        }
     578        return new WP_Error( 'api_error', 'Failed to finalize context update: ' . $response->get_error_message() . $aggregate_note );
     579    }
    545580
    546581    $status_code = wp_remote_retrieve_response_code( $response );
     
    548583    $data        = json_decode( $body, true );
    549584
    550     if ( $status_code !== 200 ) {
    551         $error_message = 'Failed to update context. ';
    552         if ( $data && isset( $data['error'] ) ) {
    553             $error_message .= $data['error'];
    554         } else {
    555             $error_message .= 'Server returned status code: ' . $status_code;
    556         }
    557         return new WP_Error( 'api_error', $error_message );
    558     }
     585    if ( $status_code !== 200 ) {
     586        $error_message = 'Failed to finalize context update. ';
     587        if ( $data && isset( $data['error'] ) ) {
     588            $error_message .= $data['error'];
     589        } else {
     590            $error_message .= 'Server returned status code: ' . $status_code;
     591        }
     592        if ( ! empty( $partial_errors ) ) {
     593            $error_message .= ' Partial errors: ' . wp_json_encode( $partial_errors );
     594        }
     595        return new WP_Error( 'api_error', $error_message );
     596    }
     597
     598    // If all chunks failed but prune succeeded, surface aggregated error
     599    if ( $successful_chunks === 0 && $total_chunks > 0 ) {
     600        return new WP_Error( 'api_error', 'Failed to update context: all ' . $total_chunks . ' chunk(s) failed. Details: ' . wp_json_encode( $partial_errors ) );
     601    }
    559602
    560603    // Store the last update timestamp
     
    571614
    572615    // Set appropriate message based on changes and failures
    573     if ( ! $has_changes ) {
     616    if ( ! $has_changes ) {
    574617        $message = 'No changes detected. Site context remains the same.';
    575618    } else {
    576         $message = 'Site context updated successfully.';
    577 
    578         if ( isset( $data['syncResult'] ) && isset( $data['syncResult']['failedCount'] ) && $data['syncResult']['failedCount'] > 0 ) {
     619        $message = 'Site context updated successfully.';
     620
     621        if ( isset( $data['syncResult'] ) && isset( $data['syncResult']['failedCount'] ) && $data['syncResult']['failedCount'] > 0 ) {
    579622            $has_failures = true;
    580623            $message     .= ' However, ' . $data['syncResult']['failedCount'] . ' posts failed to sync.';
     
    582625    }
    583626
     627    // Reflect any transport-level chunk failures in the flags/message
     628    if ( ! empty( $partial_errors ) ) {
     629        $has_failures = true;
     630        $message     .= ' Some chunks failed during upload.';
     631    }
     632
    584633    // Update cache if context is in the response
    585634    if ( isset( $data['context'] ) ) {
     
    587636    }
    588637
    589     return array(
     638    return array(
    590639        'message'      => $message,
    591640        'has_failures' => $has_failures,
     
    593642        'sync_result'  => isset( $data['syncResult'] ) ? $data['syncResult'] : null,
    594643        'timestamp'    => $timestamp,
     644        'partial_errors' => ! empty( $partial_errors ) ? $partial_errors : null,
    595645    );
    596646}
  • clearpost-simple-ai-auto-post/trunk/readme.txt

    r3387025 r3390455  
    44Requires at least: 5.0
    55Tested up to: 6.8
    6 Stable tag: 1.2.0
     6Stable tag: 1.2.2
    77Requires PHP: 7.2
    88License: GPLv2 or later
  • clearpost-simple-ai-auto-post/trunk/simple-ai-auto-post.php

    r3387025 r3390455  
    44Description: Your AI Agent for SEO, in WordPress. An AI content marketer that knows your site, then schedules and generates posts every day.
    55Plugin URI: https://clearpostplugin.com/
    6 Version: 1.2.0
     6Version: 1.2.2
    77License: GPLv2 or later
    88License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    1818
    1919// Define plugin version
    20 define( 'SAIAP_VERSION', '1.2.0' );
     20define( 'SAIAP_VERSION', '1.2.2' );
    2121
    2222// Optionally enable Demo Mode (for internal testing only)
Note: See TracChangeset for help on using the changeset viewer.