true]) as $type){if(post_type_supports($type,'digiter-builder')){$types[]=$type;}} $requested=apply_filters('digibl_supported_post_types',$types);$out=['post','page']; if(is_array($requested)){ foreach($requested as $type){ if(!is_string($type)||in_array($type,['attachment','revision','nav_menu_item','digibl_template'],true)){continue;} $object=get_post_type_object($type); if($object&&$object->public&&post_type_supports($type,'editor')){$out[]=$type;} } } $out[]='digibl_template';return array_values(array_unique($out)); } /** Parse a bounded server-verifiable embedding path, also for multipart forms. */ public static function source_chain($raw): ?array { if($raw===null||$raw===''){return [];} if(is_string($raw)){if(strlen($raw)>1200)return null;$raw=json_decode($raw,true);} if(!is_array($raw)||!array_is_list($raw)||count($raw)>3)return null; foreach($raw as $link){ if(!is_array($link)||count($link)!==2||!is_int($link['post']??null)||$link['post']<1||!is_string($link['node']??null)||!preg_match('/^[a-zA-Z0-9_-]{1,80}$/D',$link['node']))return null; } return $raw; } private static function published_source(int $id): bool { $post=get_post($id); return $post&&$post->post_status==='publish'&&$post->post_password===''&&get_post_meta($id,'_digibl_published',true) &&($post->post_type==='digibl_template'||(in_array($post->post_type,self::all(),true)&&is_post_type_viewable($post->post_type))); } /** Validate every published, visible reusable edge from its direct public host. */ public static function public_source(int $id,array $chain=[]): bool { if(!self::published_source($id)||self::source_chain($chain)===null)return false; if(!$chain)return get_post_type($id)!=='digibl_template'||ThemeBuilder::is_assigned_source($id); if(!self::public_source($chain[0]['post']))return false; $seen=[$id]; foreach($chain as $index=>$link){ $source=$link['post'];if(in_array($source,$seen,true)||!self::published_source($source))return false;$seen[]=$source; $doc=Document::clean(json_decode(get_post_meta($source,'_digibl_published',true),true));if(is_wp_error($doc))return false; $target=$chain[$index+1]['post']??$id; $scan=function(array $nodes)use(&$scan,$link,$target,$source): bool { foreach($nodes as $node){ if(!ContentQuery::visible($node['style'],$source))continue; if($node['id']===$link['node'])return $node['type']==='reusable'&&(int)$node['props']['postId']===$target; // Dynamic document boundaries cannot attest a reusable edge in this document. if(in_array($node['type'],['reusable','loop','data-repeater','component','mega-menu'],true))continue; if($scan($node['children']))return true; }return false; }; if(!$scan($doc['nodes']))return false; } return true; } public static function can_edit(): bool { foreach(self::all() as $type){$object=get_post_type_object($type);if($object&¤t_user_can($object->cap->edit_posts)){return true;}} return false; } public static function menu_capability(): string { foreach(self::all() as $type){$object=get_post_type_object($type);if($object&¤t_user_can($object->cap->edit_posts)){return $object->cap->edit_posts;}} return 'edit_posts'; } }