sync_page( $post_id, $elements ); } private function sync_page( $post_id, $elements ) { $seen_item_keys = array(); $this->walk_elements( $elements, $post_id, $seen_item_keys ); // Trash any videos previously synced from this page that no longer // have a matching repeater row (item removed, or widget deleted). $existing = get_posts( array( 'post_type' => ADDOSHVI_POST_TYPE, 'post_status' => array( 'publish', 'draft' ), 'posts_per_page' => -1, // No core API looks up posts by a custom meta value; scoped to this plugin's own addoshvi_video CPT, which only ever holds the handful of videos synced from this one page. 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query array( 'key' => '_addoshvi_source_page_id', 'value' => $post_id, ), ), 'fields' => 'ids', ) ); foreach ( $existing as $existing_id ) { $key = get_post_meta( $existing_id, '_addoshvi_source_item_key', true ); if ( '' === $key || ! in_array( $key, $seen_item_keys, true ) ) { wp_trash_post( $existing_id ); } } } private function walk_elements( $elements, $post_id, &$seen_item_keys ) { foreach ( (array) $elements as $element ) { if ( ! empty( $element['widgetType'] ) && 'addoshvi_shoppable_video' === $element['widgetType'] ) { $settings = isset( $element['settings'] ) && is_array( $element['settings'] ) ? $element['settings'] : array(); // v1.4.2 removed the "Which videos?" dropdown entirely — the // widget is curate-only now, and its own render() defaults an // unset video_select_mode to 'curate' to match. This must use // the same default, or a widget dropped onto a page *after* // v1.4.2 (which never gets this key set at all, since the // control that used to set it no longer exists) would never // get synced here. $mode = ! empty( $settings['video_select_mode'] ) ? $settings['video_select_mode'] : 'curate'; if ( 'curate' === $mode && ! empty( $settings['video_items'] ) ) { $widget_id = isset( $element['id'] ) ? $element['id'] : ''; foreach ( (array) $settings['video_items'] as $item ) { if ( empty( $item['video']['id'] ) ) { continue; // No video chosen yet in this row — nothing to sync. } $item_id = isset( $item['_id'] ) ? $item['_id'] : ''; $item_key = $post_id . ':' . $widget_id . ':' . $item_id; $seen_item_keys[] = $item_key; $this->sync_item( $post_id, $widget_id, $item, $item_key ); } } } if ( ! empty( $element['elements'] ) && is_array( $element['elements'] ) ) { $this->walk_elements( $element['elements'], $post_id, $seen_item_keys ); } } } private function sync_item( $post_id, $widget_id, $item, $item_key ) { $video_attachment_id = absint( $item['video']['id'] ); if ( ! $video_attachment_id || 0 !== strpos( (string) get_post_mime_type( $video_attachment_id ), 'video/' ) ) { return; } $cover_attachment_id = ! empty( $item['cover']['id'] ) ? absint( $item['cover']['id'] ) : 0; if ( $cover_attachment_id && 0 !== strpos( (string) get_post_mime_type( $cover_attachment_id ), 'image/' ) ) { $cover_attachment_id = 0; } $product_ids = array(); if ( ! empty( $item['products'] ) ) { foreach ( (array) $item['products'] as $pid ) { $pid = absint( $pid ); if ( $pid ) { $product_ids[] = $pid; } } } $title = ! empty( $item['item_title'] ) ? sanitize_text_field( $item['item_title'] ) : ''; if ( '' === $title ) { $attachment_title = get_the_title( $video_attachment_id ); $title = $attachment_title ? $attachment_title : sprintf( /* translators: %s: current date/time */ __( 'Video — %s', 'addonn-shoppable-video' ), current_time( 'Y-m-d H:i' ) ); } $existing = get_posts( array( 'post_type' => ADDOSHVI_POST_TYPE, 'post_status' => array( 'publish', 'draft', 'trash' ), 'posts_per_page' => 1, // No core API looks up a post by a custom meta value; scoped to this plugin's own addoshvi_video CPT and capped at posts_per_page => 1. 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query array( 'key' => '_addoshvi_source_item_key', 'value' => $item_key, ), ), 'fields' => 'ids', ) ); $existing_id = ! empty( $existing ) ? $existing[0] : 0; $postarr = array( 'post_type' => ADDOSHVI_POST_TYPE, 'post_status' => 'publish', 'post_title' => $title, ); if ( $existing_id ) { $postarr['ID'] = $existing_id; $new_id = wp_update_post( $postarr, true ); } else { $new_id = wp_insert_post( $postarr, true ); } if ( is_wp_error( $new_id ) || ! $new_id ) { return; } update_post_meta( $new_id, '_addoshvi_video_attachment_id', $video_attachment_id ); update_post_meta( $new_id, '_addoshvi_products', $product_ids ); update_post_meta( $new_id, '_addoshvi_whatsapp_enabled', ! empty( $item['whatsapp_enabled'] ) && 'yes' === $item['whatsapp_enabled'] ? '1' : '' ); update_post_meta( $new_id, '_addoshvi_whatsapp_number', ! empty( $item['whatsapp_number'] ) ? preg_replace( '/\D+/', '', $item['whatsapp_number'] ) : '' ); update_post_meta( $new_id, '_addoshvi_source_page_id', $post_id ); update_post_meta( $new_id, '_addoshvi_source_widget_id', $widget_id ); update_post_meta( $new_id, '_addoshvi_source_item_key', $item_key ); if ( $cover_attachment_id ) { set_post_thumbnail( $new_id, $cover_attachment_id ); } else { delete_post_thumbnail( $new_id ); } } }