array(), 'listings'=>array()), $value) : array('blocks'=>array(), 'listings'=>array()); } public static function hash($value = null) { return hash('sha256', wp_json_encode(null === $value ? self::all() : $value)); } public static function error($message) { return new \WP_Error('publishing_invalid', $message); } public static function public_types() { return get_post_types(array('public'=>true), 'objects'); } public static function fields($post_type = '', $groups = null) { $fields = array(); foreach ($groups === null ? customfields_get_groups() : $groups as $group) { if (($group['target'] ?? 'post') !== 'post' || ($post_type && !in_array($post_type, $group['post_types'], true))) { continue; } foreach ($group['fields'] as $field) { if (in_array($field['type'], array('text','textarea','number','email','url','select','radio','date','time','datetime','checkbox','color','wysiwyg','image','file','gallery','multiselect','checkboxes'), true)) { $fields[$field['name']] = $field; } } } return $fields; } public static function validate($kind, $raw, $context = null) { if (!in_array($kind, array('blocks','listings'), true) || !is_array($raw)) { return self::error(__('Choose a block or listing.', 'customfields-com')); } $slug = $raw['slug'] ?? ''; $title = $raw['title'] ?? ''; if (!is_string($slug) || (!preg_match('/^[a-z][a-z0-9-]{1,47}$/D', $slug) || in_array($slug,array('value','listing'),true)) || !is_string($title) || !trim($title) || strlen($title)>160) { return self::error(__('Use a title and a stable lowercase ID with letters, numbers or dashes.', 'customfields-com')); } $clean = array('slug'=>$slug, 'title'=>sanitize_text_field($title)); if ($kind === 'blocks') { if (!in_array($raw['layout'] ?? '', array('card','stack','banner'), true)) { return self::error(__('Choose a block layout.', 'customfields-com')); } if (!is_array($raw['fields'] ?? null) || !count($raw['fields']) || count($raw['fields'])>20) { return self::error(__('Add between 1 and 20 block controls.', 'customfields-com')); } $clean['layout']=$raw['layout']; $clean['fields']=array(); $names=array(); foreach ($raw['fields'] as $field) { if (!is_array($field) || !in_array($field['type'] ?? '', self::BLOCK_TYPES, true)) { return self::error(__('This control type is unavailable for authored blocks.', 'customfields-com')); } // These simple controls deliberately have no post permissions or nested identity context. $field['key']='field_block_' . ($field['name'] ?? ''); $valid = Schema::validate_field($field); if (is_wp_error($valid)) { return $valid; } if (isset($names[$valid['name']])) { return self::error(__('Block control names must be unique.', 'customfields-com')); } $names[$valid['name']]=true; $clean['fields'][]=$valid; } return $clean; } $portable = is_array($context) && !empty($context['portable']); $types=$context['types'] ?? self::public_types(); $type=$raw['post_type'] ?? ''; if (!is_string($type) || (!$portable && !isset($types[$type])) || !preg_match('/^[a-z0-9_-]{1,20}$/D', $type) || 'attachment' === $type) { return self::error(__('Choose a public post type.', 'customfields-com')); } $fields=self::fields($type, $context['groups'] ?? null); $clean['post_type']=$type; foreach (array('per_page'=>array(1,40), 'columns'=>array(1,4)) as $key=>$bounds) { $v=$raw[$key] ?? ''; if (!is_scalar($v) || !ctype_digit((string)$v) || (int)$v<$bounds[0] || (int)$v>$bounds[1]) { return self::error(__('Listing size is outside the supported range.', 'customfields-com')); } $clean[$key]=(int)$v; } foreach (array('order'=>array('ASC','DESC'), 'orderby'=>array('date','title','modified','meta_value','meta_value_num'), 'layout'=>array('grid','list')) as $key=>$allowed) { if (!in_array($raw[$key] ?? '', $allowed, true)) { return self::error(__('Choose a supported listing order and layout.', 'customfields-com')); } $clean[$key]=$raw[$key]; } foreach (array('order_field','filter_field') as $key) { $v=$raw[$key] ?? ''; if (!is_string($v) || ($v && !preg_match('/^[a-z][a-z0-9_]{0,63}$/D',$v)) || ($v && (!$portable && (!isset($fields[$v]) || !in_array($fields[$v]['type'], array('text','number','email','url','select','radio','date','time','datetime','checkbox','color'), true))))) { return self::error(__('Choose a registered scalar field for sorting or filtering.', 'customfields-com')); } $clean[$key]=$v; } if (strpos($clean['orderby'],'meta_value')===0 && !$clean['order_field']) { return self::error(__('Choose the field used for sorting.', 'customfields-com')); } if ($clean['orderby']==='meta_value_num' && !$portable && ($fields[$clean['order_field']]['type'] ?? '')!=='number') { return self::error(__('Numeric sorting needs a Number field.', 'customfields-com')); } $compare=$raw['compare'] ?? '='; $value=$raw['filter_value'] ?? ''; if (!in_array($compare, array('=','!=','>','>=','<','<=','LIKE'), true) || !is_scalar($value) || strlen((string)$value)>200) { return self::error(__('Use a supported filter and a value of at most 200 characters.', 'customfields-com')); } $clean['compare']=$compare; $clean['filter_value']=sanitize_text_field((string)$value); $tax=$raw['taxonomy'] ?? ''; $terms=$raw['terms'] ?? ''; if (!is_string($tax) || ($tax && !preg_match('/^[a-z0-9_-]{1,32}$/D',$tax)) || ($tax && (!$portable && (!is_object($taxonomy=($context['taxonomies'][$tax] ?? ($context === null ? get_taxonomy($tax) : null))) || !$taxonomy->public || !in_array($type,$taxonomy->object_type,true))))) { return self::error(__('Choose a public taxonomy attached to this post type.', 'customfields-com')); } if (!is_string($terms) || strlen($terms)>400 || ($terms && !preg_match('/^[0-9,\s]+$/D',$terms))) { return self::error(__('Enter comma-separated term IDs.', 'customfields-com')); } $clean['taxonomy']=$tax; $clean['terms']=implode(',',array_unique(array_filter(array_map('absint',preg_split('/[\s,]+/',trim($terms)))))); if ($clean['terms'] && !$tax) { return self::error(__('Choose a taxonomy for these terms.', 'customfields-com')); } if ($clean['terms'] && !$portable) { foreach(explode(',',$clean['terms']) as $id) { if(!term_exists((int)$id,$tax)) { return self::error(__('A selected term no longer exists in this taxonomy.', 'customfields-com')); } } } $selected=$raw['display_fields'] ?? array(); if (!is_array($selected) || count($selected)>12) { return self::error(__('Choose up to 12 display fields.', 'customfields-com')); } $clean['display_fields']=array(); foreach($selected as $name) { if(!is_string($name) || !preg_match('/^[a-z][a-z0-9_]{0,63}$/D',$name) || (!$portable && !isset($fields[$name]))) { return self::error(__('A display field is unavailable.', 'customfields-com')); } $clean['display_fields'][]=$name; } $clean['display_fields']=array_values(array_unique($clean['display_fields'])); foreach(array('show_image','show_excerpt','search','pagination') as $key) { $clean[$key]=!empty($raw[$key]); } return $clean; } public static function save($kind, $raw, $expected) { if ($kind === 'listings') { return is_callable(array('CustomFieldsPro\\ListingBuilder', 'save')) ? \CustomFieldsPro\ListingBuilder::save($raw, $expected) : self::listing_error(); } return self::save_definition($kind, $raw, $expected); } public static function listing_error() { return new \WP_Error('customfields_listing_pro_required', __('The visual directory and listing builder requires activated CustomFields.com Pro. Existing saved listings keep rendering; field creation, repeaters and basic display remain Free.', 'customfields-com'), array('status' => 403)); } public static function listing_notice() { echo '

' . esc_html__('Directory & listing builder · Pro', 'customfields-com') . '

' . esc_html(self::listing_error()->get_error_message()) . '

' . esc_html__('Explore the Pro listing builder', 'customfields-com') . '

' . esc_html__('Saved listings', 'customfields-com') . '

' . esc_html__('Create a reusable block · Free', 'customfields-com') . '

'; } private static function save_definition($kind, $raw, $expected) { if ($kind !== 'blocks') { return self::listing_error(); } global $wpdb; if (!current_user_can('manage_options')) { return new \WP_Error('forbidden',__('You cannot manage publishing definitions.', 'customfields-com')); } $valid=self::validate($kind,$raw); if(is_wp_error($valid)) { return $valid; } $row=$wpdb->get_row($wpdb->prepare("SELECT option_value FROM {$wpdb->options} WHERE option_name=%s",self::OPTION),ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Read authoritative workflow/metadata state for validation and conflict detection; cached values can miss concurrent edits or duplicate rows. if($wpdb->last_error) { return self::error(__('Definitions could not be read.', 'customfields-com')); } $before=$row ? maybe_unserialize($row['option_value']) : array(); if(!is_array($before)) { return self::error(__('Saved definitions need recovery before editing.', 'customfields-com')); } $before=array_merge(array('blocks'=>array(),'listings'=>array()),$before); if(!is_string($expected) || !hash_equals(self::hash($before),$expected)) { return new \WP_Error('publishing_conflict',__('Someone changed these definitions. Reload before saving; your saved definitions were kept.', 'customfields-com')); } if(!isset($before[$kind][$valid['slug']]) && count($before[$kind])>=50) { return self::error(__('This site already has 50 definitions of this kind.', 'customfields-com')); } $after=$before; $after[$kind][$valid['slug']]=$valid; if($row) { $changed=$wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET option_value=%s WHERE option_name=%s AND BINARY option_value=BINARY %s",maybe_serialize($after),self::OPTION,$row['option_value'])); } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Atomic workflow write with explicit formats/prepared values; surrounding transaction performs cache invalidation after commit. else { $changed=add_option(self::OPTION,$after,'',false) ? 1 : false; } if(false===$changed || (0===$changed && $before!==$after)) { return new \WP_Error('publishing_conflict',__('Saving was interrupted by another change. Reload and try again.', 'customfields-com')); } wp_cache_delete(self::OPTION,'options'); wp_cache_delete('alloptions','options'); return $valid; } public static function remove($kind,$slug,$expected) { if ($kind === 'listings') { return is_callable(array('CustomFieldsPro\\ListingBuilder', 'remove')) ? \CustomFieldsPro\ListingBuilder::remove($slug, $expected) : self::listing_error(); } global $wpdb; if(!current_user_can('manage_options') || !in_array($kind,array('blocks','listings'),true) || !is_string($slug) || !is_string($expected)){return self::error(__('Administrator access and a valid definition are required.','customfields-com'));} $row=$wpdb->get_row($wpdb->prepare("SELECT option_value FROM {$wpdb->options} WHERE option_name=%s",self::OPTION),ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Read authoritative workflow/metadata state for validation and conflict detection; cached values can miss concurrent edits or duplicate rows. $before=$row?maybe_unserialize($row['option_value']):null; if(!is_array($before) || !isset($before[$kind][$slug]) || !hash_equals(self::hash($before),$expected)){return self::error(__('The definition changed or was already removed. Reload before trying again.','customfields-com'));} $after=$before;unset($after[$kind][$slug]); $changed=$wpdb->query($wpdb->prepare("UPDATE {$wpdb->options} SET option_value=%s WHERE option_name=%s AND BINARY option_value=BINARY %s",maybe_serialize($after),self::OPTION,$row['option_value'])); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Atomic workflow write with explicit formats/prepared values; surrounding transaction performs cache invalidation after commit. if($changed!==1){return self::error(__('The definitions changed during removal. Reload and review again.','customfields-com'));} wp_cache_delete(self::OPTION,'options');wp_cache_delete('alloptions','options');return true; } public static function handle() { if(!current_user_can('manage_options')) { wp_die(esc_html__('You cannot manage publishing definitions.', 'customfields-com'),'',array('response'=>403)); } if ( ! isset( $_POST['_wpnonce'] ) || ! is_string( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'customfields_publish_save' ) ) { wp_die( esc_html__( 'Your session expired. Reload the publishing definition before continuing.', 'customfields-com' ), '', array( 'response' => 403 ) ); } $request = array(); foreach ( array( 'operation', 'confirm_remove', 'kind', 'slug', 'fingerprint', 'complete' ) as $key ) { $clean = isset( $_POST[$key] ) && is_string( $_POST[$key] ) ? sanitize_text_field( wp_unslash( $_POST[$key] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Equality only: malformed identities/proofs become empty, never repaired into valid values. $request[$key] = isset( $_POST[$key] ) && is_string( $_POST[$key] ) && $clean === wp_unslash( $_POST[$key] ) ? $clean : ''; } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- save() runs validate() and Schema::validate_field() over this typed definition before any database write; preserve structured block defaults for their field-specific sanitizer. $request['definition'] = isset( $_POST['definition'] ) && is_array( $_POST['definition'] ) ? wp_unslash( $_POST['definition'] ) : null; if(($request['operation'] ?? '')==='remove'){ if(($request['confirm_remove'] ?? '')!=='1'){wp_die(esc_html__('Confirm the effect on existing uses before removing a definition.','customfields-com'),'',array('response'=>400,'back_link'=>true));} $result=self::remove($request['kind']??'',$request['slug']??'',$request['fingerprint']??''); if(is_wp_error($result)){wp_die(esc_html($result->get_error_message()),'',array('response'=>409,'back_link'=>true));} wp_safe_redirect(add_query_arg(array('page'=>'customfields-publishing','tab'=>$request['kind']),admin_url('admin.php')));exit; } if(($request['complete'] ?? '')!=='1') { wp_die(esc_html__('The form was incomplete. Nothing was changed.', 'customfields-com'),'',array('response'=>400,'back_link'=>true)); } $kind=$request['kind'] ?? ''; $saved=self::save($kind,$request['definition'] ?? null,$request['fingerprint'] ?? null); if(is_wp_error($saved)) { wp_die(esc_html($saved->get_error_message()),'',array('response'=>409,'back_link'=>true)); } wp_safe_redirect(add_query_arg(array('page'=>'customfields-publishing','tab'=>$kind,'edit'=>$saved['slug'],'saved'=>1),admin_url('admin.php'))); exit; } public static function menu() { add_submenu_page('customfields',__('Blocks & listings','customfields-com'),__('Blocks & listings','customfields-com'),'manage_options','customfields-publishing',array(__CLASS__,'page')); } public static function assets($hook) { if(strpos($hook,'customfields-publishing')===false) { return; } wp_enqueue_style('customfields-publishing-admin',CUSTOMFIELDS_URL.'assets/publishing-admin.css',array(),CUSTOMFIELDS_VERSION); wp_enqueue_script('customfields-publishing-admin',CUSTOMFIELDS_URL.'assets/publishing-admin.js',array('wp-i18n'),CUSTOMFIELDS_VERSION,true); wp_set_script_translations('customfields-publishing-admin','customfields-com',CUSTOMFIELDS_PATH.'languages'); } private static function input($name,$label,$value,$type='text',$extra='') { echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Internal fixed HTML attributes only. } private static function select($name,$label,$value,$choices) { echo ''; } public static function page() { if(!current_user_can('manage_options')) { return; } $all=self::all(); $tab=isset($_GET['tab']) && $_GET['tab']==='listings' ? 'listings' : 'blocks'; // 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. if ($tab === 'listings') { if (is_callable(array('CustomFieldsPro\\ListingBuilder', 'page'))) { \CustomFieldsPro\ListingBuilder::page(); } else { self::listing_notice(); } return; } $edit=isset($_GET['edit']) && is_string($_GET['edit']) ? sanitize_key(wp_unslash($_GET['edit'])) : ''; // 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. $d=$all[$tab][$edit] ?? array(); echo '

'.esc_html__('CUSTOMFIELDS / PUBLISH','customfields-com').'

'.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 '
'; wp_nonce_field('customfields_publish_save'); echo ''; echo '

'.esc_html($d?$d['title']:($tab==='blocks'?__('Create a reusable block','customfields-com'):__('Create a live listing','customfields-com'))).'

'; self::input('title',__('Name','customfields-com'),$d['title'] ?? '', 'text','required maxlength="160" placeholder="'.esc_attr__('Customer stories','customfields-com').'"'); self::input('slug',__('Stable ID','customfields-com'),$d['slug'] ?? '', 'text','required pattern="[a-z][a-z0-9-]{1,47}" maxlength="48" placeholder="customer-stories"'.($d?' readonly':'')); self::select('layout',__('Presentation','customfields-com'),$d['layout'] ?? 'card',array('card'=>__('Card','customfields-com'),'stack'=>__('Simple stack','customfields-com'),'banner'=>__('Feature banner','customfields-com'))); echo '

'.esc_html__('Choose the controls editors will fill in. Each block keeps its own content, so you can reuse this design throughout your site.','customfields-com').'

'; $rows=$d['fields'] ?? array(array('label'=>'Heading','name'=>'heading','type'=>'text'),array('label'=>'Description','name'=>'description','type'=>'textarea')); foreach($rows as $index=>$field) { self::field_row($index,$field); } echo '

'.esc_html__('Up to 20 controls. Removing a control changes its display; existing block content remains in the post.','customfields-com').'

'; echo '
';submit_button(__('Save and make available','customfields-com'),'primary','submit',false);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').'

';wp_nonce_field('customfields_publish_save'); echo '

'; } 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='
'; $html=apply_filters('customfields_listing_controls', $html, $d, $slug, $preview); $html.='
'; foreach($query->posts as $post){ if(!is_post_publicly_viewable($post) || post_password_required($post) || $post->post_status!=='publish'){continue;} $html.='
'; if($d['show_image']){$html.=get_the_post_thumbnail($post->ID,'medium_large');} $html.='

'.esc_html(get_the_title($post)).'

'; if($d['show_excerpt']){$text=$post->post_excerpt?:wp_strip_all_tags(strip_shortcodes($post->post_content));$html.='

'.esc_html(wp_trim_words($text,28)).'

';} foreach($d['display_fields'] as $name){ $field=Display::definition($name,$post->ID);if(!$field){continue;} $fake=new \stdClass();$fake->context=array('postId'=>$post->ID); $value=Display::render(array('name'=>$name,'tag'=>'div'),'',$fake); if($value){$html.='
'.esc_html($field['label']).''.$value.'
';} } $html.='
'; } $html.='
'; if(!$query->post_count){$html.='

'.esc_html__('No matching items yet.','customfields-com').'

';} if($d['pagination'] && $query->max_num_pages>1 && !$preview){$html.='';} return $html.'
'; } }