'string','single'=>true,'show_in_rest'=>false,'revisions_enabled'=>post_type_supports($type,'revisions'),'auth_callback'=>static fn($allowed,$key,$id)=>current_user_can('edit_post',(int)$id)]); } } public function menu(): void { $this->screen=add_menu_page('Digiter Builder','Digiter Builder',PostTypes::menu_capability(),'digiter-builder',[$this,'screen'],'dashicons-layout',59); } public function screen(): void { if(!PostTypes::can_edit()) { wp_die(esc_html__('Access denied.','digiter-builder-lite')); } if(empty($_GET['post'])&&empty($_GET['editor'])){Commercial::dashboard();return;} echo '
'.esc_html(Language::text('Loading Digiter Builder…')).'
'; } public function admin_assets($hook): void { if($hook!==$this->screen||(empty($_GET['post'])&&empty($_GET['editor']))){return;} $this->editor_assets(absint($_GET['post']??0),false); } private function editor_assets(int $id,bool $frontend): void { Diagnostics::enqueue(); $locale=Language::wordpress_locale(); $switched=switch_to_locale($locale); try {$this->enqueue_editor_assets($id,$frontend);} finally {if($switched){restore_previous_locale();}} } private function enqueue_editor_assets(int $id,bool $frontend): void { wp_enqueue_media(); Modules::preview_assets(); NativeKit::enqueue(); wp_enqueue_editor(); wp_enqueue_style('digibl-editor',DIGIBL_URL.'assets/editor.css',[],DIGIBL_VERSION); wp_enqueue_style('digibl-studio-theme',DIGIBL_URL.'assets/studio-theme.css',['digibl-editor'],DIGIBL_VERSION); wp_enqueue_style('digibl-authoring',DIGIBL_URL.'assets/content-authoring.css',['digibl-editor'],DIGIBL_VERSION); wp_enqueue_style('digibl-pro-design',DIGIBL_URL.'assets/pro-design.css',['digibl-editor'],DIGIBL_VERSION); wp_enqueue_script('digibl-bootstrap',DIGIBL_URL.'assets/bootstrap.js',[],DIGIBL_VERSION,false); wp_enqueue_script('digibl-advanced',DIGIBL_URL.'assets/advanced-frontend.js',[],DIGIBL_VERSION,true); wp_enqueue_script('digibl-interactions',DIGIBL_URL.'assets/interactions.js',[],DIGIBL_VERSION,true); wp_enqueue_script('digibl-audio',DIGIBL_URL.'assets/audio.js',[],DIGIBL_VERSION,true); wp_enqueue_script('digibl-animation',DIGIBL_URL.'assets/animation.js',['digibl-audio'],DIGIBL_VERSION,true); wp_enqueue_script('digibl-front',DIGIBL_URL.'assets/frontend.js',['digibl-advanced','digibl-interactions','digibl-audio','digibl-animation'],DIGIBL_VERSION,true); wp_enqueue_script('digibl-icons',DIGIBL_URL.'assets/icon-picker.js',[],DIGIBL_VERSION,true); wp_enqueue_script('digibl-audio-studio',DIGIBL_URL.'assets/audio-studio.js',['digibl-audio'],DIGIBL_VERSION,true); Language::enqueue(); wp_enqueue_script('digibl-layout',DIGIBL_URL.'assets/layout-model.js',[],DIGIBL_VERSION,true); wp_enqueue_script('digibl-studio-ui',DIGIBL_URL.'assets/studio-ui.js',['digibl-blueprints'],DIGIBL_VERSION,true); wp_enqueue_script('digibl-blueprints',DIGIBL_URL.'assets/blueprints.js',[],DIGIBL_VERSION,true); wp_enqueue_script('digibl-image-upload',DIGIBL_URL.'assets/image-upload.js',[],DIGIBL_VERSION,true); wp_enqueue_script('digibl-designer',DIGIBL_URL.'assets/designer.js',['digibl-layout','digibl-image-upload'],DIGIBL_VERSION,true); wp_enqueue_script('digibl-authoring',DIGIBL_URL.'assets/content-authoring.js',['digibl-layout'],DIGIBL_VERSION,true); wp_enqueue_script('digibl-components',DIGIBL_URL.'assets/component-model.js',['digibl-layout'],DIGIBL_VERSION,true); wp_enqueue_script('digibl-button-gallery',DIGIBL_URL.'assets/button-gallery.js',[],DIGIBL_VERSION,true); wp_enqueue_style('digibl-bundled-fonts',DIGIBL_URL.'assets/fonts/bundled.css',[],DIGIBL_VERSION); wp_enqueue_style('digibl-font-picker',DIGIBL_URL.'assets/font-picker.css',['digibl-card-editor'],DIGIBL_VERSION); wp_enqueue_script('digibl-characters',DIGIBL_URL.'assets/characters.js',[],DIGIBL_VERSION,true); wp_enqueue_script('digibl-font-picker',DIGIBL_URL.'assets/font-picker.js',[],DIGIBL_VERSION,true); wp_enqueue_script('digibl-compact-type',DIGIBL_URL.'assets/compact-typography.js',['digibl-font-picker','digibl-characters'],DIGIBL_VERSION,true); wp_enqueue_script('digibl-field-type',DIGIBL_URL.'assets/field-typography.js',['digibl-compact-type'],DIGIBL_VERSION,true); wp_enqueue_script('digibl-card-editor',DIGIBL_URL.'assets/card-editor.js',['digibl-button-gallery','digibl-field-type','digibl-icons','digibl-audio-studio','digibl-image-upload'],DIGIBL_VERSION,true); wp_enqueue_style('digibl-card-editor',DIGIBL_URL.'assets/card-editor.css',['digibl-editor'],DIGIBL_VERSION); wp_enqueue_style('digibl-studio-tools',DIGIBL_URL.'assets/studio-tools.css',['digibl-editor'],DIGIBL_VERSION); wp_enqueue_style('digibl-workflow',DIGIBL_URL.'assets/workflow.css',['digibl-editor'],DIGIBL_VERSION); wp_enqueue_script('digibl-workflow',DIGIBL_URL.'assets/workflow.js',['digibl-layout'],DIGIBL_VERSION,true); $editorServices=[];foreach(['transport','appearance','document-model','history','public-api','global-styles','layout-controls','fields','components','visual-studios','saved-versions','preview-dom','preview','studio-tools'] as $service){$handle='digibl-editor-'.$service;$editorServices[]=$handle;wp_enqueue_script($handle,DIGIBL_URL.'assets/editor/'.$service.'.js',[],DIGIBL_VERSION,true);} wp_enqueue_script('digibl-editor',DIGIBL_URL.'assets/editor.js',array_merge($editorServices,['digibl-workflow','digibl-card-editor','digibl-bootstrap','digibl-icons','digibl-audio-studio','digibl-front','digibl-language','digibl-designer','digibl-blueprints','digibl-studio-ui','digibl-native-values','digibl-authoring','digibl-components','editor']),DIGIBL_VERSION,true); $publish_types=[];$types=[];foreach(PostTypes::all() as $type){$obj=get_post_type_object($type);if(current_user_can($obj->cap->create_posts)){$types[]=$type;}if(current_user_can($obj->cap->publish_posts)){$publish_types[]=$type;}} $pending_area=$id&&get_post_type($id)==='digibl_template'?(string)get_post_meta($id,'_digibl_pending_view_area',true):''; $view_area=$pending_area?:(!$id?(is_search()?'search':(is_404()?'404':((is_home()||is_archive())?'archive':''))):''); $view_label=$view_area?ThemeBuilder::area_label($view_area):''; $config=[ 'licenseActive'=>true, 'api'=>rest_url('digiter-builder/v1/'),'mediaApi'=>rest_url('wp/v2/media'),'nonce'=>wp_create_nonce('wp_rest'),'version'=>DIGIBL_VERSION, 'mediaUpload'=>['url'=>admin_url('admin-ajax.php'),'nonce'=>wp_create_nonce('media-form'),'maxBytes'=>wp_max_upload_size(), 'editorSupport'=>array_combine(['image/jpeg','image/png','image/gif','image/webp','image/avif'],array_map(static fn($mime)=>wp_image_editor_supports(['mime_type'=>$mime]),['image/jpeg','image/png','image/gif','image/webp','image/avif']))], 'styleGroups'=>StylePresets::groups(), 'styleControls'=>json_decode(file_get_contents(DIGIBL_PATH.'data/style-controls.json'),true), 'iconsUrl'=>DIGIBL_URL.'data/icons.json','assetBase'=>DIGIBL_URL, 'fontLibrary'=>['starter'=>array_values(FontLibrary::starter()),'system'=>FontLibrary::system(),'bundled'=>FontLibrary::bundled()], 'textFields'=>FieldTypography::catalog(), 'buttonLibrary'=>json_decode(file_get_contents(DIGIBL_PATH.'data/button-styles.json'),true), 'appearance'=>['presets'=>Appearance::PRESETS,'buttonPresets'=>Appearance::BUTTON_PRESETS,'default'=>Appearance::defaults(),'buttonDefault'=>Appearance::defaults_button()], 'globalColors'=>ThemeBuilder::colors(), 'menus'=>array_map(static fn($menu)=>['id'=>$menu->term_id,'name'=>$menu->name],wp_get_nav_menus()), 'modules'=>Document::modules(),'templates'=>[],'templateCatalog'=>true,'blank'=>Document::blank(), 'css'=>add_query_arg('ver',DIGIBL_VERSION,DIGIBL_URL.'assets/frontend.css'),'themeUrl'=>current_user_can('manage_options')?admin_url('admin.php?page=digibl-site-templates'):'', 'adminUrl'=>admin_url('admin.php?page=digiter-builder'), 'nativeUrl'=>current_user_can('manage_options')?admin_url('admin.php?page=digibl-native-kit'):'', 'nativeFields'=>array_values(array_filter(NativeFields::definitions(),static fn($d)=>$d['public'])), 'designerCss'=>add_query_arg('ver',DIGIBL_VERSION,DIGIBL_URL.'assets/designer.css'), 'studioUrl'=>$this->studio_url(), 'post'=>$id,'frontend'=>$frontend,'managed'=>$id?$this->owned($id):false, 'canStart'=>$id?$this->can_start($id):false,'canPrepare'=>$id?$this->can_prepare($id):false,'pageTitle'=>$id?get_the_title($id):(get_bloginfo('name').($view_label?' · '.$view_label:'')), 'returnUrl'=>$this->return_url($id),'sourceFrameUrl'=>$this->source_frame_url($this->return_url($id)),'nativeEditUrl'=>$id?get_edit_post_link($id,'raw'):'', 'pageContext'=>$id?get_post_type($id):(is_home()?'blog':(is_archive()?'archive':'site')), 'viewArea'=>$view_area,'viewLabel'=>$view_label,'canEditView'=>(bool)($view_area&¤t_user_can('manage_options')), 'locale'=>get_user_locale(),'user'=>get_current_user_id(), 'canManage'=>current_user_can('manage_options'),'componentTypes'=>Components::types(), 'author'=>wp_get_current_user()->display_name,'canUpload'=>current_user_can('upload_files'),'types'=>$types,'publishTypes'=>$publish_types, 'categories'=>array_map(static fn($c)=>['id'=>$c->term_id,'name'=>$c->name],get_categories(['hide_empty'=>false])), ]; // This is application data, not a translation dictionary. localize_script casts // top-level 0/false to strings, making the JavaScript string "0" truthy. wp_add_inline_script('digibl-editor','var DIGIBL_CONFIG = '.wp_json_encode($config,JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT).';','before'); } private function request_id($r): int { return absint($r->get_url_params()['id']??0); } private function err(string $code,string $msg,int $status=400) { return new \WP_Error('digibl_'.$code,$msg,['status'=>$status]); } private function owned(int $id): bool { return (bool)get_post_meta($id,'_digibl_draft',true) && in_array(get_post_type($id),PostTypes::all(),true); } private function has_external_builder(int $id): bool { return metadata_exists('post',$id,'_fl_builder_data') || metadata_exists('post',$id,'_fl_builder_draft') || (bool)get_post_meta($id,'_fl_builder_enabled',true) || metadata_exists('post',$id,'_elementor_data'); } private function can_prepare(int $id): bool { return in_array(get_post_type($id),PostTypes::all(),true) && current_user_can('edit_post',$id) && !in_array(get_post_status($id),['trash','auto-draft'],true) && !$this->has_external_builder($id); } private function can_start(int $id): bool { return $this->can_prepare($id) && trim((string)get_post_field('post_content',$id))===''; } private function can_begin(int $id): bool { return current_user_can('edit_post',$id) && ($this->owned($id) || $this->can_prepare($id)); } private function view_url(int $id): string { return in_array(get_post_status($id),['publish','private'],true)?get_permalink($id):add_query_arg('preview','true',get_permalink($id)); } public function builder_url(int $id): string { return add_query_arg(['digibl_builder'=>$id,'_digibl_nonce'=>wp_create_nonce('digibl_editor_'.$id)],$this->view_url($id)); } private function local_url(string $url): string { $origin=preg_replace('~^(https?://[^/]+).*$~','$1',home_url('/')); if(str_starts_with($url,'/')&&!str_starts_with($url,'//')){$url=$origin.$url;} $parts=wp_parse_url($url);$home=wp_parse_url(home_url('/')); if(!$parts||!in_array($parts['scheme']??'',['http','https'],true)||($parts['scheme']??'')!==($home['scheme']??'')||strtolower($parts['host']??'')!==strtolower($home['host']??'')||($parts['port']??null)!==($home['port']??null)||isset($parts['user'])||isset($parts['pass'])){return home_url('/');} return remove_query_arg(['digibl_builder','digibl_frame','digibl_view','digibl_canvas','digibl_return','digibl_panel','_digibl_nonce'],$url); } private function current_url(): string { $uri = isset($_SERVER['REQUEST_URI']) && is_string($_SERVER['REQUEST_URI']) ? esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])) : '/'; return $this->local_url(is_admin() ? home_url('/') : $uri); } private function return_url(int $id): string { $requested=isset($_GET['digibl_return']) && is_string($_GET['digibl_return']) ? esc_url_raw(wp_unslash($_GET['digibl_return'])) : ''; if(is_string($requested)&&$requested!==''){return $this->local_url($requested);} return $id?$this->view_url($id):$this->current_url(); } public function studio_url(): string { return add_query_arg(['digibl_builder'=>'studio','_digibl_nonce'=>wp_create_nonce('digibl_studio')],$this->current_url()); } private function source_frame_url(string $url): string { $url=$this->local_url($url); return add_query_arg(['digibl_view'=>1,'_digibl_nonce'=>wp_create_nonce('digibl_view_'.hash('sha256',$url))],$url); } private function source_revision(int $id): string { $p=get_post($id);if(!$p){return '';} return hash('sha256',wp_json_encode([$p->post_content,$p->post_title,$p->post_excerpt,$p->post_status,$p->post_modified_gmt,get_post_meta($id,'_digibl_published',true)])); } private function initial_document(int $id): array { $doc=Document::blank();$doc['title']=get_the_title($id);$doc['excerpt']=get_post_field('post_excerpt',$id);$doc['settings']['canvas']=false; return Document::clean($doc); } private function import_node(string $type,array $props,array $children,int &$counter): array { return ['id'=>'import_'.(++$counter),'type'=>$type,'bindings'=>[],'props'=>$props,'style'=>[],'slot'=>['label'=>'','required'=>[]],'children'=>$children]; } private function import_image(array $attrs,string $html=''): array { $id=absint($attrs['id']??0);$url=$id?(wp_get_attachment_image_url($id,'full')?:''):''; if(!$url && preg_match('~]+src=[\"\']([^\"\']+)~i',$html,$m)){$url=esc_url_raw(html_entity_decode($m[1],ENT_QUOTES,'UTF-8'));} $alt=is_scalar($attrs['alt']??null)?(string)$attrs['alt']:($id?(string)get_post_meta($id,'_wp_attachment_image_alt',true):''); $caption=is_scalar($attrs['caption']??null)?wp_strip_all_tags((string)$attrs['caption']):($id?(string)wp_get_attachment_caption($id):''); return ['id'=>$id,'url'=>$url,'alt'=>$alt,'caption'=>$caption,'ratio'=>'auto','fit'=>'contain','x'=>50,'y'=>50]; } private function convert_blocks(array $blocks,int &$counter): array { $nodes=[]; foreach($blocks as $block){ if(!is_array($block)){continue;}$name=(string)($block['blockName']??'');$attrs=is_array($block['attrs']??null)?$block['attrs']:[];$inner=is_array($block['innerBlocks']??null)?$block['innerBlocks']:[]; if($name==='core/columns' && $inner){ $columns=[];foreach(array_slice($inner,0,6) as $column){$children=$this->convert_blocks(is_array($column['innerBlocks']??null)?$column['innerBlocks']:[],$counter);$columns[]=$this->import_node('column',['gap'=>0,'vertical'=>'start'],$children,$counter);} if($columns){$count=count($columns);$nodes[]=$this->import_node('section',['columns'=>(string)$count,'structure'=>'columns','widths'=>implode(',',array_fill(0,$count,(string)round(100/$count,4)))],$columns,$counter);}continue; } if(in_array($name,['core/group','core/column'],true) && $inner){$nodes=array_merge($nodes,$this->convert_blocks($inner,$counter));continue;} if($name==='core/heading'){ $text=trim(wp_strip_all_tags(render_block($block)));if($text!==''){$level=max(1,min(4,absint($attrs['level']??2)));$nodes[]=$this->import_node('heading',['text'=>$text,'level'=>'h'.$level],[],$counter);}continue; } if($name==='core/image'){ $html=render_block($block);$image=$this->import_image($attrs,$html);if($image['url']){$nodes[]=$this->import_node('image',['image'=>$image],[],$counter);}continue; } if($name==='core/gallery'){ $images=[];foreach($inner as $child){if(($child['blockName']??'')!=='core/image'){continue;}$image=$this->import_image(is_array($child['attrs']??null)?$child['attrs']:[],render_block($child));if($image['url']){$images[]=$image;}} if($images){$nodes[]=$this->import_node('gallery',['images'=>$images,'columns'=>(string)min(4,max(2,count($images))),'ratio'=>'auto','layout'=>'grid','lightbox'=>'yes'],[],$counter);}continue; } if($name==='core/buttons' && $inner){$nodes=array_merge($nodes,$this->convert_blocks($inner,$counter));continue;} if($name==='core/button'){ $html=render_block($block);if(preg_match('~]+href=[\"\']([^\"\']+)[\"\'][^>]*>(.*?)~is',$html,$m)){$label=trim(wp_strip_all_tags($m[2]));$url=esc_url_raw(html_entity_decode($m[1],ENT_QUOTES,'UTF-8'));if($label!==''){$nodes[]=$this->import_node('button',['label'=>$label,'url'=>$url,'variant'=>'solid','target'=>'same','nofollow'=>'no'],[],$counter);}}continue; } if($name==='core/separator'){$nodes[]=$this->import_node('divider',['variant'=>'line'],[],$counter);continue;} if($name==='core/spacer'){$height=max(0,min(200,absint($attrs['height']??32)));$nodes[]=$this->import_node('spacer',['height'=>$height],[],$counter);continue;} if($name==='core/video'){ $url=is_scalar($attrs['src']??null)?esc_url_raw((string)$attrs['src']):'';if(!$url && !empty($attrs['id'])){$url=wp_get_attachment_url(absint($attrs['id']))?:'';}if($url){$nodes[]=$this->import_node('video',['url'=>$url,'caption'=>wp_strip_all_tags((string)($attrs['caption']??''))],[],$counter);}continue; } if($name==='core/audio'){ $url=is_scalar($attrs['src']??null)?esc_url_raw((string)$attrs['src']):'';if(!$url && !empty($attrs['id'])){$url=wp_get_attachment_url(absint($attrs['id']))?:'';}if($url){$nodes[]=$this->import_node('audio',['url'=>$url,'caption'=>wp_strip_all_tags((string)($attrs['caption']??''))],[],$counter);}continue; } if($name==='core/embed' && !empty($attrs['url'])){$nodes[]=$this->import_node('video',['url'=>esc_url_raw((string)$attrs['url']),'caption'=>wp_strip_all_tags((string)($attrs['caption']??''))],[],$counter);continue;} $html=trim((string)render_block($block)); if($html===''){if($inner){$nodes=array_merge($nodes,$this->convert_blocks($inner,$counter));}continue;} $nodes[]=$this->import_node('text',['content'=>wp_kses_post($html)],[],$counter); } return $nodes; } private function native_document(int $id): array { $doc=Document::blank();$doc['title']=get_the_title($id);$doc['excerpt']=(string)get_post_field('post_excerpt',$id);$doc['settings']['canvas']=false;$content=(string)get_post_field('post_content',$id);$counter=0; if(trim($content)!==''){ if(has_blocks($content)){$doc['nodes']=$this->convert_blocks(parse_blocks($content),$counter);} else{$doc['nodes'][]=$this->import_node('text',['content'=>wp_kses_post(wpautop($content))],[],$counter);} if(!$doc['nodes']){$doc['nodes'][]=$this->import_node('text',['content'=>wp_kses_post(do_blocks($content))],[],$counter);} } return Document::clean($doc); } private function source_document(int $id): array { return trim((string)get_post_field('post_content',$id))===''?$this->initial_document($id):$this->native_document($id); } private function source_id_from_url(string $url): int { $url=trim($url);if($url===''){return 0;} if(str_starts_with($url,'/')&&!str_starts_with($url,'//')){$url=rtrim(home_url('/'),'/').'/'.ltrim($url,'/');} $parts=wp_parse_url($url);$home=wp_parse_url(home_url('/')); if(!$parts||!in_array($parts['scheme']??'',['http','https'],true)||strtolower($parts['host']??'')!==strtolower($home['host']??'')||($parts['port']??null)!==($home['port']??null)){return 0;} $clean=remove_query_arg(['digibl_builder','digibl_frame','digibl_view','digibl_canvas','digibl_return','digibl_panel','_digibl_nonce'],$url); $id=url_to_postid($clean);if($id){return absint($id);} if(untrailingslashit($clean)===untrailingslashit(home_url('/'))){$front=absint(get_option('page_on_front'));if($front){return $front;}$posts=absint(get_option('page_for_posts'));if($posts){return $posts;}} return 0; } private function frame_url(int $id): string { return add_query_arg(['digibl_frame'=>$id,'_digibl_nonce'=>wp_create_nonce('digibl_editor_'.$id)],$this->view_url($id)); } public function front_access(int $id,string $nonce): bool { return $id>0 && in_array(get_post_type($id),PostTypes::all(),true) && !in_array(get_post_status($id),['trash','auto-draft'],true) && current_user_can('edit_post',$id) && (bool)wp_verify_nonce($nonce,'digibl_editor_'.$id); } public function front_editor(): void { if(!isset($_GET['digibl_builder']) && !isset($_GET['digibl_frame']) && !isset($_GET['digibl_view'])){return;} if(isset($_GET['digibl_view'])){ $nonce=is_string($_GET['_digibl_nonce']??null)?sanitize_text_field(wp_unslash($_GET['_digibl_nonce'])):''; if(!PostTypes::can_edit()||!wp_verify_nonce($nonce,'digibl_view_'.hash('sha256',$this->current_url()))){nocache_headers();wp_die('This page preview is not available.','Digiter Builder',['response'=>403]);} if(!defined('DONOTCACHEPAGE')){define('DONOTCACHEPAGE',true);} nocache_headers();header('X-Robots-Tag: noindex, nofollow, noarchive',true);header('X-Frame-Options: SAMEORIGIN',true);show_admin_bar(false);remove_action('template_redirect','redirect_canonical'); add_filter('post_class',static function($classes,$class,$post_id){$classes[]='dbs-source-id-'.absint($post_id);return $classes;},10,3); return; } $frame=isset($_GET['digibl_frame']);$raw=$_GET[$frame?'digibl_frame':'digibl_builder']; $id=is_scalar($raw)?absint($raw):0;$nonce=is_string($_GET['_digibl_nonce']??null)?sanitize_text_field(wp_unslash($_GET['_digibl_nonce'])):''; $studio=!$frame && $raw==='studio'; $allowed=$studio?(PostTypes::can_edit() && wp_verify_nonce($nonce,'digibl_studio')):$this->front_access($id,$nonce); if(!$allowed){nocache_headers();wp_die('This editor link has expired or you cannot edit this page. Reopen Digiter Builder from the page toolbar.','Digiter Builder',['response'=>403]);} if(!defined('DONOTCACHEPAGE')){define('DONOTCACHEPAGE',true);} nocache_headers();header('X-Robots-Tag: noindex, nofollow, noarchive',true);header('Referrer-Policy: same-origin',true); show_admin_bar(false);remove_action('template_redirect','redirect_canonical'); if($frame){ if((!$this->can_begin($id)) || get_queried_object_id()!==$id){wp_die('The editable page could not be loaded.','Digiter Builder',['response'=>403]);} $this->frame_id=$id; // Permit this authenticated frame on the same origin only. header('X-Frame-Options: SAMEORIGIN',true); return; } status_header(200);$this->editor_assets($id,true); require DIGIBL_PATH.'templates/editor.php';exit; } public function permission($r) { $id=$this->request_id($r); if(!$id || !$this->owned($id) || !current_user_can('edit_post',$id) || in_array(get_post_status($id),['trash','auto-draft'],true)) { return $this->err('access','You cannot edit this Digiter document.',403); } return true; } public function routes(): void { $editor=static fn()=>PostTypes::can_edit(); register_rest_route('digiter-builder/v1','/documents',[ ['methods'=>'GET','permission_callback'=>$editor,'callback'=>[$this,'list_documents']], ['methods'=>'POST','permission_callback'=>$editor,'callback'=>[$this,'create_document']], ]); register_rest_route('digiter-builder/v1','/documents/(?P\d+)',[ ['methods'=>'GET','permission_callback'=>[$this,'permission'],'callback'=>[$this,'get_document']], ['methods'=>'PUT','permission_callback'=>[$this,'permission'],'callback'=>[$this,'save_document']], ['methods'=>'DELETE','permission_callback'=>[$this,'permission'],'callback'=>[$this,'discard_created']], ]); register_rest_route('digiter-builder/v1','/documents/(?P\d+)/publish',['methods'=>'POST','permission_callback'=>[$this,'permission'],'callback'=>[$this,'publish_document']]); register_rest_route('digiter-builder/v1','/documents/(?P\d+)/analyze-design',['methods'=>'POST','permission_callback'=>fn($r)=>$this->can_begin($this->request_id($r)),'callback'=>[$this,'analyze_design']]); register_rest_route('digiter-builder/v1','/analyze-design',['methods'=>'POST','permission_callback'=>$editor,'callback'=>[$this,'analyze_design']]); register_rest_route('digiter-builder/v1','/documents/(?P\d+)/begin',['methods'=>'GET','permission_callback'=>fn($r)=>$this->can_begin($this->request_id($r)),'callback'=>[$this,'begin_document']]); register_rest_route('digiter-builder/v1','/documents/(?P\d+)/start',['methods'=>'POST','permission_callback'=>fn($r)=>$this->can_begin($this->request_id($r)),'callback'=>[$this,'start_document']]); register_rest_route('digiter-builder/v1','/documents/(?P\d+)/history',['methods'=>'GET','permission_callback'=>[$this,'permission'],'callback'=>[$this,'history']]); register_rest_route('digiter-builder/v1','/source-target',['methods'=>'POST','permission_callback'=>$editor,'callback'=>[$this,'source_target']]); register_rest_route('digiter-builder/v1','/site-view/(?Parchive|search|404)',['methods'=>'POST','permission_callback'=>static fn()=>current_user_can('manage_options'),'callback'=>[$this,'site_view_document']]); register_rest_route('digiter-builder/v1','/preview',['methods'=>'POST','permission_callback'=>$editor,'callback'=>[$this,'preview']]); register_rest_route('digiter-builder/v1','/catalog/templates',['methods'=>'GET','permission_callback'=>$editor,'callback'=>fn()=>$this->response(Document::templates())]); register_rest_route('digiter-builder/v1','/templates',[ ['methods'=>'GET','permission_callback'=>$editor,'callback'=>[$this,'saved_templates']], ['methods'=>'POST','permission_callback'=>$editor,'callback'=>[$this,'save_template']], ]); } public function list_documents($r) { $page=max(1,min(1000,absint($r->get_param('page')?:1))); $q=new \WP_Query(['post_type'=>PostTypes::all(),'post_status'=>['draft','pending','publish','private','future'],'posts_per_page'=>20,'paged'=>$page,'meta_key'=>'_digibl_draft','orderby'=>'modified','order'=>'DESC','s'=>sanitize_text_field($r->get_param('search')?:'')]); $out=[];foreach($q->posts as $p){if(!current_user_can('edit_post',$p->ID)){continue;}$env=$this->envelope($p->ID);$out[]=['id'=>$p->ID,'title'=>$env['doc']['title']?:$p->post_title,'type'=>$p->post_type,'status'=>$p->post_status,'modified'=>get_post_meta($p->ID,'_digibl_saved_at',true)?:$p->post_modified_gmt];} return $this->response(['documents'=>$out,'pages'=>$q->max_num_pages]); } public function create_document($r) { $type=$r->get_param('type')??'post';if(!is_string($type)||!in_array($type,PostTypes::all(),true)){return new \WP_Error('digibl_post_type','This content type has not enabled Digiter Builder.',['status'=>400]);}$obj=get_post_type_object($type); if(!current_user_can($obj->cap->create_posts)){return $this->err('create','You cannot create this content type.',403);} $doc=Document::clean($r->get_param('doc'));if(is_wp_error($doc)){return $doc;} $id=wp_insert_post(wp_slash(['post_type'=>$type,'post_status'=>'draft','post_title'=>$doc['title']?:'Untitled Digiter draft','post_author'=>get_current_user_id()]),true); if(is_wp_error($id)){return $id;} $env=['revision'=>wp_generate_uuid4(),'doc'=>$doc]; $ok=add_post_meta($id,'_digibl_draft',wp_slash(wp_json_encode($env)),true); if(!$ok){wp_delete_post($id,true);return $this->err('storage','The draft could not be created.',500);} update_post_meta($id,'_digibl_saved_at',gmdate('Y-m-d H:i:s')); update_post_meta($id,'_digibl_saved_by',get_current_user_id()); $token=wp_generate_uuid4();update_post_meta($id,'_digibl_creation_session',hash('sha256',get_current_user_id().':'.$token)); return $this->response(array_merge($this->payload($id),['creationToken'=>$token])); } private function envelope(int $id): array { $v=json_decode(get_post_meta($id,'_digibl_draft',true),true);return is_array($v)?$v:[]; } public function site_view_document($r) { $area=sanitize_key((string)($r->get_url_params()['area']??''));$label=ThemeBuilder::area_label($area);$name=trim(get_bloginfo('name').' — '.$label); $id=ThemeBuilder::prepare_view_template($area,$name);if(is_wp_error($id)){return $id;} return $this->response(['id'=>$id,'area'=>$area,'title'=>get_the_title($id),'builderUrl'=>$this->builder_url($id),'assigned'=>ThemeBuilder::assigned_source($area)===$id]); } public function source_target($r) { $id=absint($r->get_param('id'));if(!$id){$url=$r->get_param('url');$id=is_string($url)?$this->source_id_from_url($url):0;} $p=$id?get_post($id):null;if(!$p||!in_array($p->post_type,PostTypes::all(),true)||in_array($p->post_status,['trash','auto-draft'],true)||!current_user_can('edit_post',$id)){ return $this->response(['id'=>0,'editable'=>false,'message'=>'This part of the preview is not an editable WordPress post or page.']); } $owned=$this->owned($id);$external=$this->has_external_builder($id);$editable=$owned||$this->can_prepare($id); return $this->response(['id'=>$id,'title'=>get_the_title($id),'type'=>$p->post_type,'editable'=>$editable,'owned'=>$owned,'convertible'=>!$owned&&!$external,'editUrl'=>get_edit_post_link($id,'raw'),'builderUrl'=>$editable?$this->builder_url($id):'', 'message'=>$editable?'':($external?'This content is controlled by Beaver Builder or Elementor, so Studio will not overwrite it automatically.':'This content cannot be edited in Studio with your current permissions.')]); } public function begin_document($r) { $id=$this->request_id($r);if($this->owned($id)){return $this->response($this->payload($id));} $p=get_post($id);$obj=get_post_type_object($p->post_type); return $this->response(['id'=>$id,'doc'=>$this->source_document($id),'revision'=>'','sourceRevision'=>$this->source_revision($id),'virtual'=>true,'status'=>$p->post_status,'type'=>$p->post_type,'canPublish'=>current_user_can($obj->cap->publish_posts),'frameUrl'=>$this->frame_url($id),'url'=>$this->view_url($id),'editUrl'=>get_edit_post_link($id,'raw'),'publishedRevision'=>'']); } public function start_document($r) { $id=$this->request_id($r); return $this->locked($id,function()use($id,$r){ clean_post_cache($id); if(!$this->can_prepare($id)){return $this->err('start','This page cannot be prepared safely in Digiter Studio.',409);} if($this->owned($id)){return $r->get_param('doc')?$this->err('conflict','This page was started by another session. Reopen it before saving.',409):$this->response($this->payload($id));} if($r->get_param('sourceRevision')&&!hash_equals($this->source_revision($id),(string)$r->get_param('sourceRevision'))){return $this->err('conflict','The original page changed. Reopen it before saving.',409);} $doc=Document::clean($r->get_param('doc')?:$this->source_document($id));if(is_wp_error($doc)){return $doc;} if(!add_post_meta($id,'_digibl_draft',wp_slash(wp_json_encode(['revision'=>wp_generate_uuid4(),'doc'=>$doc])),true)){return $this->err('storage','The draft could not be started.',500);} $token=wp_generate_uuid4();update_post_meta($id,'_digibl_adoption_session',hash('sha256',get_current_user_id().':'.$token)); update_post_meta($id,'_digibl_adoption_source_revision',$this->source_revision($id)); update_post_meta($id,'_digibl_saved_at',gmdate('Y-m-d H:i:s')); update_post_meta($id,'_digibl_saved_by',get_current_user_id());return $this->response(array_merge($this->payload($id),['adoptionToken'=>$token])); }); } public function discard_created($r) { $id=$this->request_id($r); return $this->locked($id,function()use($id,$r){ $env=$this->envelope($id);$kind=$r->get_param('kind')==='adoption'?'adoption':'creation'; $stored=get_post_meta($id,'_digibl_'.$kind.'_session',true); if(!$stored||!hash_equals($stored,hash('sha256',get_current_user_id().':'.(string)$r->get_param('token')))){return $this->err('session','This page was not created in the current session.',403);} if(!hash_equals($env['revision'],(string)$r->get_param('revision'))||get_post_meta($id,'_digibl_published',true)){return $this->err('conflict','The page changed after this session. It was not discarded.',409);} if('adoption'===$kind){ $source=(string)get_post_meta($id,'_digibl_adoption_source_revision',true); if(!$source||!hash_equals($source,$this->source_revision($id))){return $this->err('conflict','The original page changed after Studio opened it. The original content was not touched.',409);} foreach(['_digibl_draft','_digibl_saved_at','_digibl_history','_digibl_adoption_session','_digibl_adoption_source_revision'] as $key){delete_post_meta($id,$key);} }else{ if(get_post_status($id)!=='draft'){return $this->err('conflict','Only this session’s unpublished draft can be discarded.',409);} // Always move to Trash, including sites configured for immediate permanent deletion. update_post_meta($id,'_wp_trash_meta_status','draft');update_post_meta($id,'_wp_trash_meta_time',time()); $result=wp_update_post(['ID'=>$id,'post_status'=>'trash'],true);if(is_wp_error($result)){return $result;} } return $this->response(['discarded'=>true,'id'=>$id]); }); } private function payload(int $id): array { $p=get_post($id);$obj=get_post_type_object($p->post_type);$env=$this->envelope($id); return ['id'=>$id,'doc'=>$env['doc'],'sourceRevision'=>$this->source_revision($id),'revision'=>$env['revision'],'status'=>$p->post_status,'type'=>$p->post_type,'url'=>$this->view_url($id),'builderUrl'=>$this->builder_url($id),'frameUrl'=>$this->frame_url($id),'editUrl'=>get_edit_post_link($id,'raw'),'canPublish'=>current_user_can($obj->cap->publish_posts),'savedAt'=>get_post_meta($id,'_digibl_saved_at',true),'publishedRevision'=>get_post_meta($id,'_digibl_published_revision',true)]; } public function get_document($r) { $payload=$this->payload($this->request_id($r));$doc=Document::clean($payload['doc']);if(is_wp_error($doc)){return $doc;}$payload['doc']=json_decode(wp_json_encode($doc),true);return $this->response($payload); } private function response($data) { $res=new \WP_REST_Response($data);$res->header('Cache-Control','no-store, private');return $res; } /** Database-backed lock; the token prevents one request from releasing another's lock. */ private function locked(int $id, callable $run) { global $wpdb;$name='digibl_write_lock_'.$id;$token=wp_generate_uuid4();$value=wp_json_encode(['token'=>$token,'time'=>time()]); if(!add_option($name,$value,'','no')) { $old=get_option($name);$decoded=json_decode((string)$old,true); if(is_array($decoded) && ($decoded['time']??time())delete($wpdb->options,['option_name'=>$name,'option_value'=>$old]);wp_cache_delete($name,'options'); } if(!add_option($name,$value,'','no')) {return $this->err('busy','Another save is running. Please retry in a moment.',409);} } // Permissions can prime the meta cache before another request finishes a save. // Read the current revision from storage only after this request owns the lock. wp_cache_delete($id,'post_meta'); try { return $run(); } finally { $wpdb->delete($wpdb->options,['option_name'=>$name,'option_value'=>$value]);wp_cache_delete($name,'options'); } } public function save_document($r) { $id=$this->request_id($r);$doc=Document::clean($r->get_param('doc'));if(is_wp_error($doc)){return $doc;} return $this->locked($id,function()use($id,$r,$doc){ $old=$this->envelope($id); if(!hash_equals($old['revision'],(string)$r->get_param('revision'))){return $this->err('conflict','This document changed in another editor. Export your work, then reload before saving.',409);} $canonicalOld=Document::clean($old['doc']??[]); if(!is_wp_error($canonicalOld)&&wp_json_encode($doc)===wp_json_encode($canonicalOld)&&wp_json_encode($old['doc'])===wp_json_encode($canonicalOld)){return $this->response($this->payload($id));} if(!$this->remember($id,$old)){return $this->err('history_storage','The previous version could not be stored safely. The draft was not changed. Check database storage and retry.',500);} $new=['revision'=>wp_generate_uuid4(),'doc'=>$doc]; if(!update_post_meta($id,'_digibl_draft',wp_slash(wp_json_encode($new)))){return $this->err('storage','The draft could not be saved. Your changes remain in the editor.',500);} update_post_meta($id,'_digibl_saved_at',gmdate('Y-m-d H:i:s')); update_post_meta($id,'_digibl_saved_by',get_current_user_id()); return $this->response($this->payload($id)); }); } /** History is independent of WP revisions, and never includes transient local edits. */ private function remember(int $id,array $env): bool { $raw=get_post_meta($id,'_digibl_history',true); $history=$raw!==''?json_decode((string)$raw,true):[]; if(!is_array($history)){return false;} $history=array_values(array_filter($history,static fn($v)=>is_array($v)&&($v['revision']??'')!==($env['revision']??''))); $date=(string)get_post_meta($id,'_digibl_saved_at',true); $author=(int)get_post_meta($id,'_digibl_saved_by',true); array_unshift($history,['date'=>$date?str_replace(' ','T',$date).'Z':gmdate('c'),'user'=>$author?: (int)get_post_field('post_author',$id),'revision'=>$env['revision'],'doc'=>$env['doc'],'kind'=>'history']); $history=array_slice($history,0,20); while(strlen((string)wp_json_encode($history))>3000000&&count($history)>1){array_pop($history);} $json=wp_json_encode($history); return is_string($json)&&($json===$raw||update_post_meta($id,'_digibl_history',wp_slash($json))!==false); } public function history($r) { $id=$this->request_id($r);$raw=get_post_meta($id,'_digibl_history',true); $history=$raw!==''?json_decode((string)$raw,true):[]; if(!is_array($history)){return $this->err('history_data','Saved history is unreadable. Existing data has been preserved; contact the site administrator.',500);} $env=$this->envelope($id);$list=[]; if(isset($env['doc'],$env['revision'])){ $date=(string)get_post_meta($id,'_digibl_saved_at',true); $list[]=['date'=>$date?str_replace(' ','T',$date).'Z':'','user'=>(int)get_post_meta($id,'_digibl_saved_by',true),'revision'=>$env['revision'],'doc'=>$env['doc'],'kind'=>'current','current'=>true]; } $published=json_decode((string)get_post_meta($id,'_digibl_published',true),true); if(is_array($published)){ $list[]=['date'=>(string)get_post_meta($id,'_digibl_published_at',true),'user'=>(int)get_post_meta($id,'_digibl_published_by',true),'revision'=>(string)get_post_meta($id,'_digibl_published_revision',true),'doc'=>$published,'kind'=>'published','current'=>false]; } foreach(array_slice($history,0,20) as $v){if(is_array($v)&&isset($v['doc'],$v['revision'])){$v['kind']='history';$v['current']=false;$list[]=$v;}} $out=[]; foreach($list as $entry){ $clean=Document::clean($entry['doc']);if(is_wp_error($clean)){continue;} $user=!empty($entry['user'])?get_userdata((int)$entry['user']):false; $out[]=['date'=>sanitize_text_field($entry['date']??''),'user'=>(int)($entry['user']??0),'author'=>$user?sanitize_text_field($user->display_name):'', 'revision'=>sanitize_text_field($entry['revision']),'doc'=>$clean,'kind'=>$entry['kind'],'current'=>!empty($entry['current'])]; } return $this->response($out); } public function publish_document($r) { $id=$this->request_id($r);$p=get_post($id);$obj=get_post_type_object($p->post_type); if(!current_user_can($obj->cap->publish_posts)){return $this->err('publish','You do not have permission to publish this content.',403);} return $this->locked($id,function()use($id,$r,$p){ $p=get_post($id);$env=$this->envelope($id); if(!is_string($r->get_param('sourceRevision'))||!hash_equals($this->source_revision($id),(string)$r->get_param('sourceRevision'))){return $this->err('conflict','The original page changed outside this editor. Reopen it before publishing.',409);} if(!hash_equals($env['revision'],(string)$r->get_param('revision'))){return $this->err('conflict','The saved draft changed. Reload before publishing.',409);} $doc=Document::clean($env['doc']);if(is_wp_error($doc)){return $doc;} // 1.2.9: template slot markers are editorial recommendations only. // WordPress may publish an untitled or intentionally sparse page; do not block it here. $publish=function()use($id,$p,$env,$doc){ $previous=get_post_meta($id,'_digibl_published',true); // Set revisioned meta before wp_update_post, so WordPress revisions capture it together. $json=wp_json_encode($doc);if($previous!==$json && !update_post_meta($id,'_digibl_published',wp_slash($json))){return $this->err('storage','The publication could not be saved.',500);} $status='private'===$p->post_status?'private':'publish'; $saved=wp_update_post(wp_slash(['ID'=>$id,'post_status'=>$status,'post_title'=>$doc['title'],'post_excerpt'=>$doc['excerpt'],'post_content'=>(new Renderer(false,$id,true))->render($doc)]),true); if(is_wp_error($saved)){if($previous){update_post_meta($id,'_digibl_published',wp_slash($previous));}else{delete_post_meta($id,'_digibl_published');}return $saved;} $view_area=get_post_type($id)==='digibl_template'?(string)get_post_meta($id,'_digibl_pending_view_area',true):''; if($view_area){$assigned=ThemeBuilder::assign_view_template($view_area,$id);if(is_wp_error($assigned)){$restored=wp_update_post(wp_slash(['ID'=>$id,'post_status'=>$p->post_status,'post_title'=>$p->post_title,'post_excerpt'=>$p->post_excerpt,'post_content'=>$p->post_content]),true);if($previous){update_post_meta($id,'_digibl_published',wp_slash($previous));}else{delete_post_meta($id,'_digibl_published');}if(is_wp_error($restored)||get_post_meta($id,'_digibl_published',true)!==$previous){return $this->err('rollback','Publication failed and restoration could not be completed. Contact support before retrying.',500);}return $assigned;}} update_post_meta($id,'_digibl_published_revision',$env['revision']); update_post_meta($id,'_digibl_published_at',gmdate('c')); update_post_meta($id,'_digibl_published_by',get_current_user_id()); foreach(['_digibl_creation_session','_digibl_adoption_session','_digibl_adoption_source_revision'] as $key){delete_post_meta($id,$key);} clean_post_cache($id); do_action('digibl_document_published',$id,$doc); do_action('digiter_builder_content_changed',$id,'digiter-builder-lite'); return $this->response($this->payload($id)); }; return $p->post_type==='digibl_template'&&get_post_meta($id,'_digibl_pending_view_area',true)?ThemeBuilder::with_settings_lock($publish):$publish(); }); } public function preview($r) { $context=$r->get_param('contextId')??0; if(!is_int($context)||$context<0){return $this->err('preview_context','Invalid preview context.');} if($context&&(!get_post($context)||!current_user_can('edit_post',$context))){return $this->err('preview_context','You cannot preview this content.',403);} $doc=Document::clean($r->get_param('doc'));if(is_wp_error($doc)){return $doc;} $payload=['html'=>(new Renderer(true,$context))->render($doc)]; if($r->get_param('validateDocument')===true){$payload['doc']=$doc;} // Explicit card commits reuse the same allowlist as page save. No new permissions. if($r->get_param('validateCard')===true&&count($doc['nodes'])===1){$payload['node']=$doc['nodes'][0];} return $this->response($payload); } public function saved_templates() {return $this->response(get_user_meta(get_current_user_id(),'_digibl_templates',true)?:[]);} public function analyze_design($r) { $source=$r->get_param('html'); $candidate=null!==$source?(new DesignImporter())->analyze($source):DesignImporter::from_url($r->get_param('url')); if(is_wp_error($candidate)){return $candidate;} $candidate['targetId']=$this->request_id($r); $candidate['html']=(new Renderer(true))->render($candidate['doc']); return $this->response($candidate); } public function save_template($r) { $doc=Document::clean($r->get_param('doc'));if(is_wp_error($doc)){return $doc;} $name=Document::text($r->get_param('name'),100);if(!$name){return $this->err('name','Give this template a name.');} $templates=get_user_meta(get_current_user_id(),'_digibl_templates',true)?:[]; array_unshift($templates,['id'=>wp_generate_uuid4(),'name'=>$name,'doc'=>$doc,'date'=>gmdate('c')]);$templates=array_slice($templates,0,20); update_user_meta(get_current_user_id(),'_digibl_templates',wp_slash($templates));return $this->response($templates); } public function conditional_cache(): void { if(!is_singular(PostTypes::all())){return;} $doc=json_decode(get_post_meta(get_queried_object_id(),'_digibl_published',true),true);if(!is_array($doc)){return;} $sensitive=is_user_logged_in();$scan=function($nodes)use(&$scan,&$sensitive){foreach($nodes as $n){$s=$n['style']??[];if(($s['audience']??'all')!=='all'||!empty($s['start'])||!empty($s['end'])||!empty($s['visibility'])||in_array($n['type'],['component','reusable','woo-pages','loop','mega-menu','data-repeater','builder-template','integration'],true)){$sensitive=true;}$scan($n['children']??[]);}};$scan($doc['nodes']??[]); if($sensitive){if(!defined('DONOTCACHEPAGE')){define('DONOTCACHEPAGE',true);}nocache_headers();} } public function front_assets(): void { if(!is_singular(PostTypes::all()) || (!$this->frame_id && !$this->private_preview() && !get_post_meta(get_queried_object_id(),'_digibl_published',true))){return;} Assets::styles((bool)($this->frame_id || $this->private_preview())); // An editor frame may receive any module after the initial page load. if($this->frame_id || $this->private_preview()){Assets::full();Modules::preview_assets();} } public function content($content) { if($this->frame_id && get_the_ID()===$this->frame_id && in_the_loop() && is_main_query()){ $doc=Document::clean($this->envelope($this->frame_id)['doc']??$this->source_document($this->frame_id)); return is_wp_error($doc)?$content:'
'.(new Renderer(true))->render($doc).'
'; } if(is_admin() || !is_singular(PostTypes::all()) || !in_the_loop() || !is_main_query() || post_password_required()){return $content;} if($this->private_preview()){ $doc=Document::clean($this->envelope(get_the_ID())['doc']??[]); return is_wp_error($doc)?$content:(new Renderer())->render($doc); } $json=get_post_meta(get_the_ID(),'_digibl_published',true);if(!$json){return $content;} $doc=Document::clean(json_decode($json,true));if(is_wp_error($doc)){Assets::enqueue_html($content);return PageCSS::stylesheet($content,(int)get_the_ID()).$content;}return (new Renderer())->render($doc); } public function template($template) { if(!is_singular(PostTypes::all())){return $template;} $doc=($this->frame_id||$this->private_preview())?($this->envelope(get_queried_object_id())['doc']??[]):json_decode(get_post_meta(get_queried_object_id(),'_digibl_published',true),true); if($this->frame_id && isset($_GET['digibl_canvas'])){$doc['settings']['canvas']=is_string($_GET['digibl_canvas']) && $_GET['digibl_canvas']==='1';} return !empty($doc['settings']['canvas'])?DIGIBL_PATH.'templates/canvas.php':$template; } private function private_preview(): bool { return is_preview() && $this->owned(get_queried_object_id()) && current_user_can('edit_post',get_queried_object_id()); } public function row_actions($actions,$post) { if(($this->owned($post->ID)||$this->can_prepare($post->ID)) && current_user_can('edit_post',$post->ID)){$actions['dbs']='Digiter Builder';} return $actions; } public function meta_box(): void { foreach(PostTypes::all() as $type){add_meta_box('dbs-open','Digiter Builder',function($post){ if($this->owned($post->ID)||$this->can_prepare($post->ID)){echo '

'.esc_html__('Open this page with the front-end editor.','digiter-builder-lite').'

Open Digiter Builder';} else{echo '

'.esc_html__('This content is controlled by another builder and is not converted automatically.','digiter-builder-lite').'

Open Studio';} },$type,'side');} } public function admin_bar($bar): void { if(!PostTypes::can_edit()){return;} $id=get_queried_object_id();$singular=!is_admin() && is_singular(PostTypes::all()) && current_user_can('edit_post',$id); $bar->add_node(['id'=>'dbs-edit','title'=>'Digiter Builder','href'=>$singular?$this->builder_url($id):$this->studio_url()]); // Template selection belongs to the Studio sidebar, not an automatic landing screen. } }