| Server IP : 198.71.58.22 / Your IP : 216.73.216.72 Web Server : Apache/2.4.62 (AlmaLinux) OpenSSL/3.2.2 System : Linux localhost 5.14.0-570.33.2.el9_6.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Aug 14 07:37:35 EDT 2025 x86_64 User : root ( 0) PHP Version : 8.3.24 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /opt/automate/ |
Upload File : |
<?php
/**
* php wix_csv_so_importer_v2.php /path/to/wix.csv
*/
require __DIR__ . '/vendor/autoload.php';
// ---------- CONFIG ----------
$WP_LOAD = '/var/www/html/drjessicawacker.virtuopsdev.com/wp-load.php';
$TARGET_BLOG_ID = 2;
$TEMPLATE_POST_ID = 152;
$PUBLISH_STATUS = 'publish'; // 'draft' or 'publish'
$TAG_SLUG = 'publicresource';
$CSV_PATH = $argv[1] ?? '';
$TEXT_STYLE = 'font-family:Ubuntu, sans-serif; color:#123A49; font-size:16px; font-weight:700;';
// ---------- WP BOOT ----------
if (!file_exists($WP_LOAD)) { fwrite(STDERR,"Update \$WP_LOAD\n"); exit(1); }
require $WP_LOAD;
require_once ABSPATH.'wp-admin/includes/image.php';
require_once ABSPATH.'wp-admin/includes/file.php';
require_once ABSPATH.'wp-admin/includes/media.php';
if (is_multisite() && $TARGET_BLOG_ID) switch_to_blog($TARGET_BLOG_ID);
// Ensure tag exists
$tag = get_term_by('slug', $TAG_SLUG, 'post_tag');
if (!$tag) {
$t = wp_insert_term('PublicResource', 'post_tag', ['slug'=>$TAG_SLUG]);
if (is_wp_error($t)) { /* ignore */ }
}
// ---------- HELPERS ----------
function csv_rows($path) {
if (!$path || !file_exists($path)) throw new RuntimeException("CSV not found: $path");
$fh = fopen($path,'r'); if(!$fh) throw new RuntimeException("Open fail: $path");
$headers = array_map('trim', fgetcsv($fh,0,','));
$rows=[]; while(($r=fgetcsv($fh,0,','))!==false){ $row=[]; foreach($headers as $i=>$h){ $row[$h]=isset($r[$i])?trim($r[$i]):''; } $rows[]=$row; }
fclose($fh); return $rows;
}
function resolve_wix_image_url($val){
$val = trim($val);
if ($val === '') return '';
if (preg_match('~^https?://~i',$val)) return $val;
// image://v1/<token>.jpg/...
if (preg_match('~image://v1/([^/\s]+?\.(?:jpg|jpeg|png|webp|gif))~i',$val,$m)) {
return 'https://static.wixstatic.com/media/'.$m[1];
}
// Sometimes CSV stores just the token …~mv2.jpg
if (preg_match('~([0-9a-z_]+~mv2\.(?:jpg|jpeg|png|webp|gif))~i',$val,$m)) {
return 'https://static.wixstatic.com/media/'.$m[1];
}
return '';
}
function sideload_image_to_post($url,$post_id){
if (!$url) return 0;
$tmp = download_url($url, 30);
if (is_wp_error($tmp)) { error_log('download_url: '.$tmp->get_error_message()); return 0; }
$name = basename(parse_url($url, PHP_URL_PATH) ?: ('image-'.time().'.jpg'));
$file = ['name'=>$name,'tmp_name'=>$tmp];
$att = media_handle_sideload($file,$post_id);
if (is_wp_error($att)) { error_log('media_handle_sideload: '.$att->get_error_message()); @unlink($tmp); return 0; }
return (int)$att;
}
function load_template_panels($post_id){
$pan = get_post_meta($post_id,'panels_data',true);
$pan = is_array($pan) ? $pan : @maybe_unserialize($pan);
return is_array($pan) ? $pan : null;
}
function list_widgets($panels){
$out=[];
foreach(($panels['widgets']??[]) as $i=>$w){
$out[]=[
'i'=>$i,
'class'=>$w['panels_info']['class'] ?? '',
'id'=>$w['panels_info']['id'] ?? '',
'title'=> $w['title'] ?? '',
];
}
return $out;
}
function find_widget_by_title(&$panels,$title){
if (!$title) return -1;
$needle = strtolower($title);
foreach(($panels['widgets']??[]) as $i=>$w){
$t = strtolower($w['title'] ?? '');
if ($t === $needle) return $i;
}
return -1;
}
/* ---------- STRICT TYPE CHECKS ---------- */
function is_button_widget($w){
$cls = strtolower($w['panels_info']['class'] ?? '');
return strpos($cls, 'button') !== false || strpos($cls, 'siteorigin_widget_button_widget') !== false;
}
function is_image_widget($w){
$cls = strtolower($w['panels_info']['class'] ?? '');
return strpos($cls, 'image') !== false || strpos($cls, 'siteorigin_widget_image_widget') !== false;
}
function get_text_fields_present($w){
// Return which content-ish keys exist on this widget
$keys = ['content','text','editor','html','body','value','texteditor'];
$present = [];
foreach ($keys as $k) if (array_key_exists($k,$w)) $present[] = $k;
return $present;
}
function is_editor_like_widget($w){
$cls = strtolower($w['panels_info']['class'] ?? '');
if ($cls === '') return false;
if (is_button_widget($w) || is_image_widget($w)) return false; // never treat button/image as editor
// If it exposes a content-like field, and it's not a button/image, we accept it
$present = get_text_fields_present($w);
if (!empty($present)) return true;
// Otherwise check class name hints (SO Editor / WP Text / Custom HTML / Orion text)
if (strpos($cls, 'sow-editor') !== false) return true;
if (strpos($cls, 'siteorigin_widget_editor_widget') !== false) return true;
if (strpos($cls, 'wp_widget_text') !== false) return true;
if (strpos($cls, 'wp_widget_custom_html') !== false) return true;
if (strpos($cls, 'orion') !== false) return true;
return false;
}
/** Find widget by Title, but only if it’s editor-like (for text1/text2) */
function find_editor_widget_by_title(&$panels, $title){
if (!$title) return -1;
$needle = strtolower($title);
foreach (($panels['widgets'] ?? []) as $i => $w) {
$t = strtolower($w['title'] ?? '');
if ($t === $needle && is_editor_like_widget($w)) return $i;
}
return -1;
}
/** Update text/editor widget HTML (write to any content-ish keys present) */
function set_editor_html(&$widget, $html){
$present = get_text_fields_present($widget);
$touched = false;
foreach ($present as $k) {
// Avoid touching 'text' if this somehow is a button (extra safety)
if ($k === 'text' && is_button_widget($widget)) continue;
$widget[$k] = $html;
$touched = true;
}
if (!$touched) {
// Fallback: write to 'content'
$widget['content'] = $html;
}
// Normalize flags if present
if (array_key_exists('autop', $widget)) $widget['autop'] = true;
if (array_key_exists('filter', $widget)) $widget['filter'] = false;
return $widget;
}
function guess_widget_indexes(&$panels){
$map = ['text1'=>-1,'pic1'=>-1,'button1'=>-1,'text2'=>-1];
$editors=[];
foreach(($panels['widgets']??[]) as $i=>$w){
if (is_editor_like_widget($w)) $editors[] = $i;
elseif (is_image_widget($w) && $map['pic1']<0) $map['pic1']=$i;
elseif (is_button_widget($w) && $map['button1']<0) $map['button1']=$i;
}
if (isset($editors[0])) $map['text1'] = $editors[0];
if (isset($editors[1])) $map['text2'] = $editors[1];
return $map;
}
/** Try to find two editor widgets whose current content contains 'placeholder' */
function find_placeholders(&$panels){
$hit = [];
foreach(($panels['widgets']??[]) as $i=>$w){
if (!is_editor_like_widget($w)) continue;
foreach (get_text_fields_present($w) as $k){
$val = strtolower(trim(strip_tags((string)($w[$k] ?? ''))));
if ($val !== '' && strpos($val, 'placeholder') !== false) {
$hit[] = $i; break;
}
}
}
return array_values(array_unique($hit));
}
function set_image_attachment(&$widget,$att_id){ $widget['image']=(int)$att_id; $widget['size']=$widget['size']??'full'; return $widget; }
function set_button_url(&$widget,$url){ $widget['url']=esc_url_raw($url); return $widget; }
// ---------- MAIN ----------
if (!$CSV_PATH){ fwrite(STDERR,"Usage: php wix_csv_so_importer_v2.php /path/to/wix.csv\n"); exit(1); }
$tpl = load_template_panels($TEMPLATE_POST_ID);
if (!$tpl){ fwrite(STDERR,"Template #$TEMPLATE_POST_ID has no panels_data\n"); exit(1); }
$tplList = list_widgets($tpl);
fwrite(STDERR, "Template widgets:\n");
foreach($tplList as $w){ fwrite(STDERR, sprintf(" [%d] class=%s id=%s title=%s\n",$w['i'],$w['class'],$w['id'],$w['title'])); }
$rows = csv_rows($CSV_PATH);
$imported=0;
foreach($rows as $r){
$title = $r['Title'] ?? '';
$author = $r['author'] ?? '';
$description = $r['description'] ?? '';
$insight = $r['insight'] ?? '';
$picture_raw = $r['picture'] ?? '';
$url = $r['url'] ?? '';
$pic_url = resolve_wix_image_url($picture_raw);
// Create post (draft first)
$post_id = wp_insert_post([
'post_title' => $title ?: '(Untitled)',
'post_status' => 'draft',
'post_type' => 'post',
'post_content' => '',
]);
if (is_wp_error($post_id) || !$post_id){ fwrite(STDERR,"Create failed for \"$title\"\n"); continue; }
// Clone template panels
$pan = $tpl; // by value
// Prefer explicit Widget Titles (strict type checks for text)
$idx = [
'text1' => find_editor_widget_by_title($pan,'text1'),
'text2' => find_editor_widget_by_title($pan,'text2'),
'pic1' => find_widget_by_title($pan,'pic1'),
'button1' => find_widget_by_title($pan,'button1'),
];
// If text1/text2 not found by title, try placeholder-based mapping
if ($idx['text1'] < 0 || $idx['text2'] < 0) {
$ph = find_placeholders($pan);
if (count($ph) >= 2) {
if ($idx['text1'] < 0) $idx['text1'] = $ph[0];
if ($idx['text2'] < 0) $idx['text2'] = $ph[1];
}
}
// Fallbacks: pick first two editor-like widgets for text1/text2; image/button as seen
if ($idx['text1'] < 0 || $idx['text2'] < 0 || $idx['pic1'] < 0 || $idx['button1'] < 0) {
$guess = guess_widget_indexes($pan);
foreach ($idx as $k => $v) {
if ($v < 0 && $guess[$k] >= 0) $idx[$k] = $guess[$k];
}
}
// Safety: ensure text1/text2 are not button/image widgets
foreach (['text1','text2'] as $k){
$i = $idx[$k];
if ($i >= 0) {
$w = $pan['widgets'][$i];
if (is_button_widget($w) || is_image_widget($w)) $idx[$k] = -1;
}
}
// Compose HTML
$html_text1 = '<p style="'.esc_attr($TEXT_STYLE).'">'
. 'By:<br>'.esc_html($author).'<br>'
. esc_html($title).'<br>'
. nl2br(esc_html($description))
. '</p>';
$html_text2 = '<p style="'.esc_attr($TEXT_STYLE).'">'.nl2br(esc_html($insight)).'</p>';
// Image download → featured + pic1
$att_id = 0;
if ($pic_url){
$att_id = sideload_image_to_post($pic_url, $post_id);
if ($att_id) set_post_thumbnail($post_id, $att_id);
} else {
fwrite(STDERR,"[WARN] No usable image URL for \"$title\" (raw: $picture_raw)\n");
}
// Apply to widgets if found
if ($idx['text1'] >= 0) $pan['widgets'][$idx['text1']] = set_editor_html($pan['widgets'][$idx['text1']], $html_text1);
if ($idx['text2'] >= 0) $pan['widgets'][$idx['text2']] = set_editor_html($pan['widgets'][$idx['text2']], $html_text2);
if ($idx['pic1'] >= 0 && $att_id) $pan['widgets'][$idx['pic1']] = set_image_attachment($pan['widgets'][$idx['pic1']], $att_id);
if ($idx['button1'] >= 0 && $url) $pan['widgets'][$idx['button1']] = set_button_url($pan['widgets'][$idx['button1']], $url);
// Save panels
update_post_meta($post_id,'panels_data',$pan);
update_post_meta($post_id,'panels_used',true);
// Tag
wp_set_post_terms($post_id, [$TAG_SLUG], 'post_tag', true);
// Publish?
if ($PUBLISH_STATUS !== 'draft'){
wp_update_post(['ID'=>$post_id,'post_status'=>$PUBLISH_STATUS]);
}
$imported++;
echo "Created post #$post_id — \"$title\"\n";
if ($att_id) echo " • Featured image attachment #$att_id set\n";
echo get_permalink($post_id)."\n";
// Debug: show which classes were targeted (verify text1/text2 are editors)
$clsOf = function($i) use ($pan){ return $i>=0 ? strtolower($pan['widgets'][$i]['panels_info']['class'] ?? '(no-class)') : '(none)'; };
fwrite(STDERR, " Widgets idx/classes: "
."text1={$idx['text1']} ({$clsOf($idx['text1'])}) "
."text2={$idx['text2']} ({$clsOf($idx['text2'])}) "
."pic1={$idx['pic1']} ({$clsOf($idx['pic1'])}) "
."button1={$idx['button1']} ({$clsOf($idx['button1'])})\n");
}
// restore blog
if (is_multisite() && $TARGET_BLOG_ID) restore_current_blog();
echo "Done. Imported $imported row(s).\n";