','&'),array('\\u002d\\u002d','\\u003c','\\u003e','\\u0026'),$json);
$short=str_replace('core/','',$name);
if(empty($node['innerBlocks']))return '';
$content='';foreach($node['innerBlocks'] as $child)$content.=bkbg_template_serialize($child);
return ''.$content.'';
}
function bkbg_template_import($key,$progress=null){
$notify=function($percent,$message)use($progress){if(is_callable($progress))$progress($percent,$message);};
$notify(5,__('Downloading and verifying template…','blockenberg'));
$data=bkbg_template_data($key);if(is_wp_error($data))return $data;if(!$data||empty($data['blocks'])||!is_array($data['blocks']))return new WP_Error('missing_template','Template is not available.');
$valid=bkbg_template_validate_nodes($data['blocks']);if(is_wp_error($valid))return $valid;
$notify(20,__('Creating your draft…','blockenberg'));
$content='';foreach($data['blocks'] as $node)$content.=bkbg_template_serialize($node);
$id=wp_insert_post(wp_slash(array('post_type'=>'page','post_status'=>'draft','post_title'=>$data['title'].' — Blockenberg','post_name'=>wp_unique_post_slug('bkbg-'.$key,0,'publish','page',0),'post_content'=>$content)),true);
if(is_wp_error($id))return $id;
$total=bkbg_template_media_count($data);$done=0;
$notify(25,__('Preparing images…','blockenberg'));
$data=bkbg_template_prepare_media($data,$id,function()use(&$done,$total,$notify){$done++;$notify(25+(int)floor(65*$done/max(1,$total)),sprintf(__('Preparing images: %1$d of %2$d','blockenberg'),$done,$total));});
$notify(95,__('Saving editable blocks…','blockenberg'));$content='';foreach($data['blocks'] as $node)$content.=bkbg_template_serialize($node);
$updated=wp_update_post(wp_slash(array('ID'=>$id,'post_content'=>$content)),true);if(is_wp_error($updated))return $updated;
update_post_meta($id,'_bkbg_template_key',$key);update_post_meta($id,'_bkbg_template_canvas','1');
$notify(100,__('Your draft is ready.','blockenberg'));
return $id;
}
function bkbg_template_classes($a){
$class=$a['classes']??'';return implode(' ',array_map('sanitize_html_class',preg_split('/\s+/',trim($class))));
}
function bkbg_template_image($image){
$out='
';
}
function bkbg_template_override_css($a){
$id='bkbg-tpl-element-'.sanitize_html_class($a['uid']??'');$selector='.'. $id.' > .bkbg-tpl-widget-container';$out='';
foreach(array(''=>'','Tablet'=>'@media(max-width:1024px)','Mobile'=>'@media(max-width:767px)') as $suffix=>$media){
$props=array();foreach(array('fontSize'=>'font-size','lineHeight'=>'line-height','letterSpacing'=>'letter-spacing','textColor'=>'color','textAlign'=>'text-align','fontWeight'=>'font-weight') as $key=>$css){
$value=$a[$key.$suffix]??'';if($value==='')continue;
if(in_array($key,array('fontSize','letterSpacing'),true))$value=(float)$value.'px';
elseif($key==='lineHeight')$value=(float)$value;
elseif($key==='fontWeight')$value=(int)$value;
elseif($key==='textAlign'&&!in_array($value,array('left','center','right','justify'),true))continue;
elseif($key==='textColor'&&!preg_match('/^(#[a-f0-9]{3,8}|rgba?\([0-9.,%\s]+\)|[a-z]+)$/i',$value))continue;
$props[]=$css.':'.$value.'!important';
}
if($props){$rule=$selector.','.$selector.' .bkbg-tpl-heading-title,'.$selector.' .bkbg-catalog-styled-text-builder-items,'.$selector.' *{'.implode(';',$props).'}';$out.=$media?$media.'{'.$rule.'}':$rule;}
}
if(!empty($a['imageWidth']))$out.=$selector.' img{width:'.max(1,min(3000,(float)$a['imageWidth'])).'px!important}';
return $out?'':'';
}
function bkbg_template_render($a,$content,$block){
$name=$block->name;$classes=bkbg_template_classes($a);$uid=sanitize_html_class($a['uid']??'');
if($name==='blockenberg/template-page'){
$key=$a['templateKey']??'';if(!preg_match('/^[a-z0-9][a-z0-9-]{0,159}$/D',$key))return '';
return ''bkbg-template-page','data-bkbg-template'=>$key)).'>'.$content.'
';
}
if($name==='blockenberg/template-layout'){
$tag=($a['tag']??'')==='section'?'section':'div';$attrs=get_block_wrapper_attributes(array('class'=>$classes,'data-bkbg-id'=>$uid,'data-bkbg-type'=>$a['kind']??'','style'=>safecss_filter_attr($a['inlineStyle']??'')));
$overlay=!empty($a['overlay'])?'
':'';
return '<'.$tag.' '.$attrs.'>'.(($a['kind']??'')==='column'?'':bkbg_template_background($a).$overlay).''.(($a['kind']??'')==='column'?bkbg_template_background($a).$overlay:'').$content.'
'.bkbg_template_shapes($a).''.$tag.'>';
}
$body='';
if($name==='blockenberg/template-text')$body=wp_kses_post($a['content']??'');
elseif($name==='blockenberg/template-heading'){
$tag=in_array($a['headingTag']??'',array('h1','h2','h3','h4','h5','h6','p','div'),true)?$a['headingTag']:'h2';
$body='<'.$tag.' class="'.esc_attr($a['contentClasses']??'').'">'.wp_kses_post($a['content']??'').''.$tag.'>';
}elseif($name==='blockenberg/template-image'){
$body=bkbg_template_image($a['image']??array());if(!empty($a['link']))$body=''.$body.'';
}elseif($name==='blockenberg/template-button'){
$target=($a['target']??'')==='_blank'?' target="_blank" rel="noopener noreferrer"':'';
$icon=($a['buttonIcon']??'')==='play'?'':'';
$body='';
}elseif(in_array($name,array('blockenberg/template-styled-text','blockenberg/template-marquee'),true)){
$marquee=$name==='blockenberg/template-marquee';$items='';
foreach($a['segments']??array() as $segment){
$inner=isset($segment['image'])?bkbg_template_image($segment['image']):''.wp_kses_post($segment['text']??'').'';
$items.=''.$inner.'';
}
if(!$marquee)$body=''.$items.'
';
else $body='';
}
return '$classes,'data-bkbg-id'=>$uid)).'>
'.$body.'
'.bkbg_template_override_css($a);
}
add_action('init',function(){
$base=dirname(__DIR__,2);$url=plugins_url('', $base.'/blockenberg.php');
wp_register_script('bkbg-template-editor-styles',$url.'/assets/js/template-editor-styles.js',array(),filemtime($base.'/assets/js/template-editor-styles.js'),false);
wp_register_script('bkbg-template-pilot-schemas',$url.'/assets/js/template-pilot-schemas.js',array(),filemtime($base.'/assets/js/template-pilot-schemas.js'),false);
wp_register_script('bkbg-template-pilot-editor',$url.'/assets/js/template-pilot.js',array('wp-blocks','wp-block-editor','wp-element','wp-components','wp-i18n','bkbg-motion-editor','bkbg-template-pilot-schemas','bkbg-inspector-tabs','bkbg-template-editor-styles'),filemtime($base.'/assets/js/template-pilot.js'),false);
wp_register_style('bkbg-template-pilot',$url.'/assets/css/template-pilot.css',array(),filemtime($base.'/assets/css/template-pilot.css'));
wp_register_script('bkbg-template-pilot-front',$url.'/assets/js/template-pilot-front.js',array(),filemtime($base.'/assets/js/template-pilot-front.js'),true);
},5);
function bkbg_template_content_keys($blocks){
$keys=array();foreach($blocks as $block){
if(($block['blockName']??'')==='blockenberg/template-page')$keys[]=$block['attrs']['templateKey']??'';
if(!empty($block['innerBlocks']))$keys=array_merge($keys,bkbg_template_content_keys($block['innerBlocks']));
}return array_values(array_unique(array_filter($keys)));
}
add_action('enqueue_block_assets',function(){
$post=get_post();if(!$post&&is_admin()&&!empty($_GET['post']))$post=get_post(absint($_GET['post']));
if(!$post)return;
$visit=function($blocks)use(&$visit){foreach($blocks as $block){
if(($block['blockName']??'')==='blockenberg/template-page')bkbg_library_enqueue_template_style($block['attrs']['templateKey']??'', $block['attrs']['templateStyleUrl']??'');
if(!empty($block['innerBlocks']))$visit($block['innerBlocks']);
}};$visit(parse_blocks($post->post_content));
});
add_filter('template_include',function($template){
if(is_singular('page')&&get_post_meta(get_the_ID(),'_bkbg_template_canvas',true)==='1'&&has_block('blockenberg/template-page'))return __DIR__.'/template-canvas.php';
return $template;
});
add_action('admin_menu',function(){add_submenu_page('blockenberg',__('Template Library','blockenberg'),__('Template Library','blockenberg'),'manage_options','blockenberg-templates','bkbg_template_catalog_screen');},25);
require_once __DIR__.'/template-catalog-screen.php';
add_action('admin_post_bkbg_import_template',function(){
if(!current_user_can('manage_options'))wp_die('Insufficient permissions.');check_admin_referer('bkbg_import_template');
$id=bkbg_template_import(sanitize_key(wp_unslash($_POST['key']??'')));if(is_wp_error($id))wp_die(esc_html($id->get_error_message()));
wp_safe_redirect(admin_url('post.php?post='.(int)$id.'&action=edit'));exit;
});
/** Validate bundled block data before creating a draft. */
function bkbg_template_validate_nodes($nodes,$depth=0){
if($depth>40)return new WP_Error('template_depth','Template structure is too deeply nested.');
foreach($nodes as $node){
$name=$node['blockName']??'';
if(strpos($name,'blockenberg/template-')!==0||!WP_Block_Type_Registry::get_instance()->is_registered($name))return new WP_Error('template_block','Unknown template block.');
if(!is_array($node['attrs']??null)||!is_array($node['innerBlocks']??null))return new WP_Error('template_schema','Invalid template structure.');
$nested=bkbg_template_validate_nodes($node['innerBlocks'],$depth+1);if(is_wp_error($nested))return $nested;
}return true;
}
/** Images are imported only when a user imports a template, with source deduplication. */
function bkbg_template_media_count($value){
if(!is_array($value))return 0;
if(isset($value['src'])&&is_string($value['src'])&&$value['src']!==''){
$source=$value['src'];$token=strpos($source,'{{assets}}/')===0;$host=strtolower(wp_parse_url($source,PHP_URL_HOST)??'');
if(!$token&&!bkbg_library_asset_url_allowed($source)&&$host!=='kingaddons.com'&&!preg_match('/(^|\.)kingaddons\.com$/',$host))return 0;
$name=$token?basename(substr($source,11)):basename(wp_parse_url($source,PHP_URL_PATH)??'');$mime=wp_check_filetype($name)['type'];
return $mime&&strpos($mime,'image/')===0&&$mime!=='image/svg+xml'?1:0;
}
$count=0;foreach($value as $child)$count+=bkbg_template_media_count($child);return $count;
}
function bkbg_template_prepare_media($value,$post_id,$progress=null){
if(!is_array($value))return $value;
if(isset($value['src'])&&is_string($value['src'])&&$value['src']!==''){
$source=$value['src'];$token=strpos($source,'{{assets}}/')===0;
$host=strtolower(wp_parse_url($source,PHP_URL_HOST)??'');
if(!$token&&!bkbg_library_asset_url_allowed($source)&&$host!=='kingaddons.com'&&!preg_match('/(^|\.)kingaddons\.com$/',$host))return $value;
$name=$token?basename(substr($source,11)):basename(wp_parse_url($source,PHP_URL_PATH)??'');
$mime=wp_check_filetype($name)['type'];if(!$mime||strpos($mime,'image/')!==0||$mime==='image/svg+xml')return $value;
try {
$meta=$token?'_bkbg_template_asset':'_bkbg_template_source';$identity=$token?$name:hash('sha256',$source);
$ids=get_posts(array('post_type'=>'attachment','post_status'=>'inherit','fields'=>'ids','numberposts'=>1,'meta_key'=>$meta,'meta_value'=>$identity));
$id=$ids?(int)$ids[0]:0;
if(!$id){
if($token)$source=bkbg_library_assets_url().$name;
{
$response=wp_safe_remote_get($source,array('timeout'=>20,'limit_response_size'=>20*MB_IN_BYTES));
if(is_wp_error($response)||wp_remote_retrieve_response_code($response)!==200)return $value;
$bytes=wp_remote_retrieve_body($response);
if(!$bytes||strpos(strtolower(substr($bytes,0,100)),'$actual['type'],'post_title'=>'Blockenberg — '.$name,'post_status'=>'inherit'),$upload['file'],$post_id,true);
if(is_wp_error($id))return $value;
require_once ABSPATH.'wp-admin/includes/image.php';
wp_update_attachment_metadata($id,wp_generate_attachment_metadata($id,$upload['file']));
update_post_meta($id,$meta,$identity);update_post_meta($id,'_wp_attachment_image_alt',$value['alt']??'');
}
$value['id']=$id;$value['src']=wp_get_attachment_url($id);
// Responsive candidates must follow the new local attachment.
if(isset($value['srcset'])){$set=wp_get_attachment_image_srcset($id,'full');if($set)$value['srcset']=$set;else unset($value['srcset']);}
return $value;
} finally {if(is_callable($progress))$progress();}
}
foreach($value as &$child)$child=bkbg_template_prepare_media($child,$post_id,$progress);
return $value;
}
/** Stream actual completed import stages; the normal form remains a no-JS fallback. */
add_action('wp_ajax_bkbg_import_template',function(){
if(!current_user_can('manage_options'))wp_send_json_error(array('message'=>__('Insufficient permissions.','blockenberg')),403);
check_ajax_referer('bkbg_import_template','_wpnonce');
$key=sanitize_key(wp_unslash($_POST['key']??''));
while(ob_get_level()>0){if(!@ob_end_clean())break;}
nocache_headers();header('Content-Type: application/x-ndjson; charset=utf-8');header('X-Accel-Buffering: no');
$emit=function($data){echo wp_json_encode($data)."\n";flush();};
$id=bkbg_template_import($key,function($percent,$message)use($emit){$emit(array('type'=>'progress','percent'=>$percent,'message'=>$message));});
if(is_wp_error($id))$emit(array('type'=>'error','message'=>$id->get_error_message()));
else $emit(array('type'=>'complete','postId'=>(int)$id,'editUrl'=>admin_url('post.php?post='.(int)$id.'&action=edit')));
exit;
});