'.esc_html__('Make your content useful.','customfields-com').'
'.esc_html__('Create reusable blocks in Free. The visual directory and listing builder is available in Pro.','customfields-com').'
';
if(isset($_GET['saved'])) { echo '
'.esc_html__('Saved. Your definition is ready in the block editor.','customfields-com').'
'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only routing, filtering or pagination; no mutation is authorized by this query parameter. Object access remains checked by the renderer.
echo '
';
if($d && $tab==='listings'){echo '
'.esc_html__('Live preview','customfields-com').'
'.self::listing($d['slug'],true).'';} // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- listing() builds markup from esc_html/esc_attr/esc_url values and trusted WordPress image/field renderers.
if($d && $tab==='blocks'){echo '
'.esc_html__('In the block editor, search for','customfields-com').' '.esc_html($d['title']).'. '.esc_html__('Its controls appear in the block sidebar.','customfields-com').'
';}
if($d){
echo ''.esc_html__('Remove this definition','customfields-com').'
'.esc_html__('Existing blocks or listing shortcodes using this definition stop displaying. Saved post content remains, but you must recreate the definition to display it again.','customfields-com').'
';
}
echo '
';
}
private static function field_row($index,$field){
$prefix='definition[fields]['.$index.']';
echo '';
}
public static function register(){
wp_register_style('customfields-publishing',CUSTOMFIELDS_URL.'assets/publishing.css',array(),CUSTOMFIELDS_VERSION);
wp_register_script('customfields-authored-blocks',CUSTOMFIELDS_URL.'assets/publishing-blocks.js',array('wp-blocks','wp-element','wp-components','wp-block-editor','wp-i18n','wp-server-side-render'),CUSTOMFIELDS_VERSION,true);
wp_set_script_translations('customfields-authored-blocks','customfields-com',CUSTOMFIELDS_PATH.'languages');
$all=self::all();$configs=array();
foreach($all['blocks'] as $slug=>$raw){
$definition=self::validate('blocks',$raw);if(is_wp_error($definition)){continue;}
$name='customfields/'.$slug;
if(\WP_Block_Type_Registry::get_instance()->is_registered($name)){continue;}
register_block_type($name,array('api_version'=>3,'title'=>$definition['title'],'category'=>'widgets','editor_script'=>'customfields-authored-blocks','style'=>'customfields-publishing','attributes'=>array('values'=>array('type'=>'object','default'=>new \stdClass())),'supports'=>array('html'=>false),'render_callback'=>static function($attributes)use($definition){return Publishing::render_authored($definition,$attributes['values'] ?? array());}));
$configs[]=$definition;
}
register_block_type('customfields/listing',array('api_version'=>3,'title'=>__('CustomFields.com listing','customfields-com'),'category'=>'widgets','editor_script'=>'customfields-authored-blocks','style'=>'customfields-publishing','attributes'=>array('id'=>array('type'=>'string','default'=>'')),'supports'=>array('html'=>false),'render_callback'=>array(__CLASS__,'render_listing')));
wp_localize_script('customfields-authored-blocks','CustomFieldsPublishing',array('blocks'=>$configs,'listings'=>array_values(array_map(static function($v){return array('slug'=>$v['slug'],'title'=>$v['title']);},$all['listings']))));
}
public static function editor(){wp_enqueue_script('customfields-authored-blocks');}
public static function render_authored($definition,$values){
if(!is_array($values) || count($values)>40 || strlen(wp_json_encode($values))>262144){return '';}
$html='';
foreach($definition['fields'] as $field){
$raw=$values[$field['name']] ?? ($field['default'] ?? '');
// Frontend rendering is not an edit operation: verify media visibility, not upload capability.
if($field['type']==='image'){
if(!is_scalar($raw) || !ctype_digit((string)$raw)){continue;}
$attachment=get_post((int)$raw);if(!$attachment || $attachment->post_type!=='attachment' || !wp_attachment_is_image($attachment->ID)){continue;}
if($attachment->post_parent && !Display::accessible($attachment->post_parent)){continue;}
$piece=wp_get_attachment_image((int)$raw,'large');
}else{
$value=Fields::sanitize($raw,$field);if(is_wp_error($value)){continue;}
if($value==='' || $value===false){continue;}
if($field['type']==='wysiwyg'){$piece=wp_kses_post($value);}
elseif($field['type']==='url'){$piece=''.esc_html($field['label']).'';}
elseif($field['type']==='checkbox'){$piece=esc_html($field['label']);}
else{$piece=esc_html($field['type']==='select'?($field['choices'][(string)$value] ?? ''):(string)$value);if($field['type']==='textarea'){$piece=nl2br($piece);}}
}
$html.='
'.$piece.'
';
}
if(!$html){return '';}
wp_enqueue_style('customfields-publishing');
return ''.$html.'';
}
public static function render_listing($attributes){return self::listing(is_string($attributes['id'] ?? null)?$attributes['id']:'');}
public static function shortcode($atts){$atts=shortcode_atts(array('id'=>''),$atts,'customfields_listing');return self::listing($atts['id']);}
public static function listing($slug,$preview=false){
$raw=self::all()['listings'][$slug] ?? null; $d=self::validate('listings',$raw);
if(is_wp_error($d)){return '';}
wp_enqueue_style('customfields-publishing');
$page_key='cfpage_'.$slug;$search_key='cfsearch_'.$slug;
$page = 1;
// Public pagination only selects already-public posts. Links must work for
// anonymous visitors and shared/cached pages; this is not a state change.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only pagination; the query and output enforce published, non-password-protected posts.
$requested_page = $d['pagination'] && isset( $_GET[$page_key] ) && is_string( $_GET[$page_key] ) ? sanitize_text_field( wp_unslash( $_GET[$page_key] ) ) : '';
if ( preg_match( '/^[1-9][0-9]{0,9}$/D', $requested_page ) ) {
$page = min( 10000, (int) $requested_page );
}
$search='';
$args=array('post_type'=>$d['post_type'],'post_status'=>'publish','has_password'=>false,'posts_per_page'=>$d['per_page'],'paged'=>$page,'orderby'=>$d['orderby'],'order'=>$d['order'],'ignore_sticky_posts'=>true,'s'=>$search,'no_found_rows'=>!$d['pagination']);
if(strpos($d['orderby'],'meta_value')===0){$args['meta_key']=$d['order_field'];} // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Explicit user-configured field/taxonomy filtering within the bounded paginated content query; required for the selected listing scope.
if($d['filter_field']){$field=self::fields($d['post_type'])[$d['filter_field']];$args['meta_query']=array(array('key'=>$d['filter_field'],'compare'=>$d['compare'],'value'=>$d['filter_value'],'type'=>$field['type']==='number'?'DECIMAL(20,6)':'CHAR'));} // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Explicit user-configured field/taxonomy filtering within the bounded paginated content query; required for the selected listing scope.
if($d['taxonomy'] && $d['terms']){$args['tax_query']=array(array('taxonomy'=>$d['taxonomy'],'field'=>'term_id','terms'=>array_map('intval',explode(',',$d['terms']))));} // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Explicit user-configured field/taxonomy filtering within the bounded paginated content query; required for the selected listing scope.
$args=apply_filters('customfields_listing_query', $args, $d, $slug, $preview);
$query=new \WP_Query($args);$html='