HEX
Server: nginx/1.18.0
System: Linux iZj6c1ieg2jrpk1z5tzi19Z 6.3.9-1.el7.elrepo.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Jun 21 22:18:40 EDT 2023 x86_64
User: www (1001)
PHP: 8.2.4
Disabled: passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
Upload Files
File: /www/wwwroot/www.cytocare.cn/wp-content/plugins/wpjam-basic/includes/class-wpjam-post.php
<?php
class WPJAM_Post{
	use WPJAM_Instance_Trait;

	protected $id;

	protected function __construct($id){
		$this->id	= (int)$id;
	}

	public function __get($key){
		if(in_array($key, ['id', 'post_id'])){
			return $this->id;
		}elseif($key == 'views'){
			return (int)get_post_meta($this->id, 'views', true);
		}elseif($key == 'permalink'){
			return get_permalink($this->id);
		}elseif($key == 'ancestors'){
			return get_post_ancestors($this->id);
		}elseif($key == 'children'){
			return get_children($this->id);
		}elseif($key == 'viewable'){
			return is_post_publicly_viewable($this->id);
		}elseif($key == 'format'){
			return get_post_format($this->id) ?: '';
		}elseif($key == 'taxonomies'){
			return get_object_taxonomies($this->post);
		}elseif($key == 'type_object'){
			return wpjam_get_post_type_object($this->post_type);
		}elseif($key == 'icon'){
			return (string)$this->get_type_setting('icon');
		}elseif($key == 'thumbnail'){
			return $this->supports('thumbnail') ? get_the_post_thumbnail_url($this->id, 'full') : '';
		}elseif($key == 'images'){
			return $this->supports('images') ? array_values(get_post_meta($this->id, 'images', true) ?: []) : [];
		}elseif($key == 'post'){
			return get_post($this->id);
		}else{
			$_post	= get_post($this->id);

			if($key == 'data'){
				return $_post->to_array();
			}elseif(!str_starts_with($key, 'post_') && isset($_post->{'post_'.$key})){
				return $_post->{'post_'.$key};
			}else{
				return $_post->$key ?? $this->meta_get($key);
			}
		}
	}

	public function __isset($key){
		return $this->$key !== null;
	}

	public function __call($method, $args){
		if(in_array($method, ['get_content', 'get_excerpt', 'get_first_image_url'])){
			$function	= 'wpjam_get_post_'.substr($method, 4);

			return $function($this->post, ...$args);
		}elseif(in_array($method, ['get_thumbnail_url', 'get_images'])){
			$method	= str_replace('get_', 'parse_', $method);

			return $this->$method(...$args);
		}

		return $this->call_dynamic_method($method, ...$args);
	}

	public function get_type_setting($key){
		return $this->type_object ? $this->type_object->$key : null;
	}

	public function supports($feature){
		return post_type_supports($this->post_type, $feature);
	}

	public function save($data){
		if(array_any(['post_status', 'status'], fn($k)=> wpjam_get($data, $k) == 'publish')){
			$result	= $this->is_publishable();

			if(is_wp_error($result) || !$result){
				return $result ?: new WP_Error('unpublishable', '不可发布');
			}
		}

		return self::update($this->id, $data, false);
	}

	public function set_status($status){
		return $this->save(['post_status'=>$status]);
	}

	public function publish(){
		return $this->set_status('publish');
	}

	public function unpublish(){
		return $this->set_status('draft');
	}

	public function is_publishable(){
		return true;
	}

	public function get_unserialized(){
		return wpjam_unserialize($this->content, fn($fixed)=> $this->save(['content'=>$fixed])) ?: [];
	}

	public function get_terms($taxonomy='post_tag'){
		return get_the_terms($this->id, $taxonomy) ?: [];
	}

	public function set_terms($terms='', $taxonomy='post_tag', $append=false){
		return wp_set_post_terms($this->id, $terms, $taxonomy, $append);
	}

	public function in_term($taxonomy, $terms=null){
		return is_object_in_term($this->id, $taxonomy, $terms);
	}

	public function in_taxonomy($taxonomy){
		return is_object_in_taxonomy($this->post, $taxonomy);
	}

	public function parse_thumbnail_url($size='thumbnail', $crop=1){
		$thumbnail	= $this->thumbnail ?: ($this->images ? $this->images[0] : apply_filters('wpjam_post_thumbnail_url', '', $this->post));

		return $thumbnail ? wpjam_get_thumbnail($thumbnail, ($size ?: ($this->get_type_setting('thumbnail_size') ?: 'thumbnail')), $crop) : '';
	}

	public function parse_images($large_size='', $thumbnail_size='', $full_size=true){
		if(!$this->images){
			return [];
		}

		$get_size	= function($key){
			$setting	= $this->get_type_setting('images_sizes');

			if($setting){
				if($key == 'large'){
					return $setting[0];
				}

				if(count($this->images) == 1){
					$query	= wpjam_parse_image_query($this->images[0]);

					if(!$query){
						$query	= wpjam_get_image_size($this->images[0], 'url') ?: ['width'=>0, 'height'=>0];

						update_post_meta($this->id, 'images', [add_query_arg($query, $this->images[0])]);
					}

					if(!empty($query['orientation'])){
						$i	= ['landscape'=>2, 'portrait'=>3][$query['orientation']] ?? 1;

						return $setting[$i] ?? $setting[1];
					}
				}

				return $setting[1];
			}

			return $this->get_type_setting($key.'_size') ?: $key;
		};

		$sizes	= wpjam_array(['large'=>$large_size, 'thumbnail'=>$thumbnail_size], fn($k, $v)=> $v === false ? null : [$k, $v ?: $get_size($k)]);

		foreach($this->images as $image){
			if(!$sizes || $full_size){
				$sizes['full']	= 'full';
			}

			$parsed	= array_map(fn($s)=> wpjam_get_thumbnail($image, $s), $sizes);
			$query	= wpjam_parse_image_query($image);

			if($query){
				$parsed	= array_merge($parsed, ['width'=>0, 'height'=>0], wpjam_slice($query, ['orientation', 'width', 'height']));
			}

			if(isset($sizes['thumbnail'])){
				$size	= wpjam_parse_size($sizes['thumbnail']);
				$parsed	= array_merge($parsed, wpjam_array(['width', 'height'], fn($k, $v)=> ['thumbnail_'.$v, $size[$v] ?? 0]));
			}

			$images[]	= count($sizes) == 1 ? reset($parsed) : $parsed;
		}

		return $images;
	}

	public function parse_for_json($args=[]){
		$args	+= [
			'list_query'		=> false,
			'content_required'	=> false,
			'raw_content'		=> false,
			'suppress_filter'	=> false,
		];

		$json	= wpjam_pick($this, ['id', 'type', 'post_type', 'status', 'views', 'icon']);
		$json	+= wpjam_fill(['title', 'excerpt'],	fn($field)=> $this->supports($field) ? html_entity_decode(call_user_func('get_the_'.$field, $this->id)) : '');
		$fields	= ['title', 'excerpt', 'thumbnail', 'images', 'author', 'date', 'modified', 'password', 'name', 'menu_order', 'format'];
		$json	= array_reduce($fields, fn($carry, $field)=> array_merge($carry, $this->parse_field($field, $args)), $json);

		if($args['list_query']){
			return $json;
		}

		$json	= array_reduce(wpjam_get_post_options($this->type), fn($carry, $option)=> array_merge($carry, $option->prepare($this->id)), $json);
		$json	+= wpjam_fill(array_filter($this->taxonomies, fn($tax)=> $tax != 'post_format' && is_taxonomy_viewable($tax)), fn($tax)=> array_map('wpjam_get_term', $this->get_terms($tax)));
		$json	+= $this->parse_field('content', $args);

		return $args['suppress_filter'] ? $json : apply_filters('wpjam_post_json', $json, $this->id, $args);
	}

	protected function parse_field($field, $args=[]){
		if($field == 'name'){
			return $this->viewable ? [
				'name'		=> urldecode($this->name),
				'post_url'	=> str_replace(home_url(), '', $this->permalink)
			] : [];
		}elseif($field == 'thumbnail'){
			$size	= $args['thumbnail_size'] ?? ($args['size'] ?? null);

			return [$field=> $this->get_thumbnail_url($size)];
		}elseif($field == 'images'){
			return $this->supports($field) ? [$field=> $this->parse_images()] : [];
		}elseif($field == 'author'){
			$parsed	= ['user_id'=> (int)$this->author];
			$parsed	+= $this->supports($field) ? ['author' => wpjam_get_user($this->author)] : [];
		}elseif($field == 'content'){
			if((is_single($this->id) || is_page($this->id) || $args['content_required'])){
				if($this->supports('editor')){
					if($args['raw_content']){
						$parsed['raw_content']	= $this->content;
					}

					$parsed['content']		= wpjam_get_post_content($this->post);
					$parsed['multipage']	= (bool)$GLOBALS['multipage'];

					if($parsed['multipage']){
						$parsed['numpages']	= $GLOBALS['numpages'];
						$parsed['page']		= $GLOBALS['page'];
					}
				}else{
					if(is_serialized($this->content)){
						$parsed['content']	= $this->get_unserialized();
					}
				}
			}
		}elseif($field == 'password'){
			return $this->password ? [
				'password_protected'	=> true,
				'password_required'		=> post_password_required($this->id),
			] : [];
		}elseif(in_array($field, ['date', 'modified'])){
			$timestamp	= get_post_timestamp($this->id, $field);
			$prefix		= $field == 'modified' ? 'modified_' : '';
			$parsed		= [
				$prefix.'timestamp'	=> $timestamp,
				$prefix.'time'		=> wpjam_human_time_diff($timestamp),
				$prefix.'date'		=> wpjam_date('Y-m-d', $timestamp),
			];

			if($field == 'date' && !$args['list_query'] && is_main_query()){
				$current_posts	= $GLOBALS['wp_query']->posts;

				if($current_posts && in_array($this->id, array_column($current_posts, 'ID'))){
					if(is_new_day()){
						$GLOBALS['previousday']	= $GLOBALS['currentday'];

						$parsed['day']	= wpjam_human_date_diff($parsed['date']);
					}else{
						$parsed['day']	= '';
					}
				}
			}
		}elseif($field == 'menu_order'){
			if($this->supports('page-attributes')){
				return [$field=> (int)$this->menu_order];
			}
		}elseif($field == 'format'){
			if($this->supports('post-formats')){
				return [$field=> $this->format];
			}
		}

		return $parsed ?? [];
	}

	public function meta_get($key){
		return wpjam_get_metadata('post', $this->id, $key, null);
	}

	public function meta_exists($key){
		return metadata_exists('post', $this->id, $key);
	}

	public function meta_input(...$args){
		if($args){
			return wpjam_update_metadata('post', $this->id, ...$args);
		}
	}

	public function value_callback($field){
		if($field == 'tax_input'){
			return wpjam_fill($this->taxonomies, fn($tax)=> array_column($this->get_terms($tax), 'term_id'));
		}

		return $this->post->$field ?? $this->meta_get($field);
	}

	// update/insert 方法同时支持 title 和 post_xxx 字段写入 post 中,meta 字段只支持 meta_input
	// update_callback 方法只支持 post_xxx 字段写入 post 中,其他字段都写入 meta_input
	public function update_callback($data, $defaults){
		$current	= $this->data;
		$post_data	= wpjam_pull($data, [...array_keys($current), 'tax_input']);
		$result		= $post_data ? $this->save($post_data) : true;

		return (!is_wp_error($result) && $data) ? $this->meta_input($data, $defaults) : $result;
	}

	public static function get_instance($post=null, $post_type=null, $wp_error=false){
		$post	= $post ?: get_post();
		$post	= static::validate($post, $post_type);

		if(is_wp_error($post)){
			return $wp_error ? $post : null;
		}

		$model	= wpjam_get_post_type_setting(get_post_type($post), 'model') ?: 'WPJAM_Post';

		return self::instance($post->ID, fn($id)=> $model::create_instance($id));
	}

	public static function validate($post_id, $post_type=null){
		$post	= $post_id ? self::get_post($post_id) : null;

		if(!$post || !($post instanceof WP_Post)){
			return new WP_Error('invalid_id');
		}

		if(!post_type_exists($post->post_type)){
			return new WP_Error('invalid_post_type');
		}

		$post_type	??= static::get_current_post_type();

		if($post_type && $post_type !== 'any' && !in_array($post->post_type, (array)$post_type)){
			return new WP_Error('invalid_post_type');
		}

		return $post;
	}

	public static function validate_by_field($value, $field){
		$post_type 	= is_numeric($value) ? get_post_type($value) : null;

		if($post_type && (!$field->post_type || in_array($post_type, (array)$field->post_type))){
			return (int)$value;
		}

		return new WP_Error('invalid_post_id', [$field->_title]);
	}

	public static function get($post){
		$data	= self::get_post($post, ARRAY_A);

		if($data){
			$data['id']				= $data['ID'];
			$data['post_content']	= is_serialized($data['post_content']) ? unserialize($data['post_content']) : $data['post_content'];

			$data	+= wpjam_array($data, fn($k, $v)=> str_starts_with($k, 'post_') ? [substr($k, 5), $v] : null);
		}

		return $data;
	}

	public static function insert($data){
		try{
			$data	= static::prepare_data($data);
			$meta	= wpjam_pull($data, 'meta_input');

			if(isset($data['post_type'])){
				if(!post_type_exists($data['post_type'])){
					wpjam_throw('invalid_post_type');
				}
			}else{
				$data['post_type']	= static::get_current_post_type() ?: 'post';
			}

			if(empty($data['post_status'])){
				$cap	= get_post_type_object($data['post_type'])->cap->publish_posts;

				$data['post_status']	= current_user_can($cap) ? 'publish' : 'draft';
			}

			$data		+= ['post_author'=>get_current_user_id(), 'post_date'=> wpjam_date('Y-m-d H:i:s')];
			$post_id	= wp_insert_post(wp_slash($data), true, true);

			if($meta && !is_wp_error($post_id)){
				wpjam_update_metadata('post', $post_id, $meta);
			}

			return $post_id;
		}catch(Exception $e){
			return wpjam_catch($e);
		}
	}

	public static function update($post_id, $data, $validate=true){
		try{
			if($validate){
				wpjam_throw_if_error(self::validate($post_id));
			}

			$data	= static::prepare_data($data, $post_id);
			$data	= array_merge($data, ['ID'=>$post_id]);
			$meta	= wpjam_pull($data, 'meta_input');
			$result	= wp_update_post(wp_slash($data), true, true);

			if($meta && !is_wp_error($result)){
				wpjam_update_metadata('post', $post_id, $meta);
			}

			return $result;
		}catch(Exception $e){
			return wpjam_catch($e);
		}
	}

	public static function delete($post_id, $force=true){
		try{
			static::before_delete($post_id);

			return wp_delete_post($post_id, $force) ?: new WP_Error('delete_error', '删除失败');
		}catch(Exception $e){
			return wpjam_catch($e);
		}
	}

	protected static function sanitize_data($data, $post_id=0){
		$data	+= wpjam_array(wpjam_slice($data, ['title', 'content', 'excerpt', 'name', 'status', 'author', 'parent', 'password', 'date', 'date_gmt', 'modified', 'modified_gmt']), fn($k)=> 'post_'.$k);

		if(isset($data['post_content']) && is_array($data['post_content'])){
			$data['post_content']	= serialize($data['post_content']);
		}

		if($post_id){
			$data['ID'] = $post_id;

			if(isset($data['post_date']) && !isset($data['post_date_gmt'])){
				$current_date_gmt	= get_post($post_id)->post_date_gmt;

				if($current_date_gmt && $current_date_gmt != '0000-00-00 00:00:00'){
					$data['post_date_gmt']	= get_gmt_from_date($data['post_date']);
				}
			}
		}

		return $data;
	}

	public static function get_by_ids($post_ids){
		return array_map('get_post', self::update_caches($post_ids));
	}

	public static function update_caches($post_ids, $update_term_cache=false, $update_meta_cache=false){
		$post_ids	= array_filter(wp_parse_id_list($post_ids));

		if(!$post_ids){
			return [];
		}

		_prime_post_caches($post_ids, $update_term_cache, $update_meta_cache);

		$posts	= array_filter(wp_cache_get_multiple($post_ids, 'posts'));

		if(!$update_meta_cache){
			wpjam_lazyload('post_meta', array_keys($posts));
		}

		do_action('wpjam_update_post_caches', $posts);

		return $posts;
	}

	public static function get_post($post, $output=OBJECT, $filter='raw'){
		if($post && is_numeric($post)){	// 不存在情况下的缓存优化
			$found	= false;
			$cache	= wp_cache_get($post, 'posts', false, $found);

			if($found){
				if(is_wp_error($cache)){
					return $cache;
				}elseif(!$cache){
					return null;
				}
			}else{
				$_post	= WP_Post::get_instance($post);

				if(!$_post){	// 防止重复 SQL 查询。
					wp_cache_add($post, false, 'posts', 10);
					return null;
				}
			}
		}

		return get_post($post, $output, $filter);
	}

	public static function get_current_post_type(){
		$object	= WPJAM_Post_Type::get(get_called_class(), 'model', 'WPJAM_Post');

		return $object ? $object->name : null;
	}

	public static function get_path($args, $item=[]){
		$id	= is_array($args) ? (int)array_get($args, $item['post_type'].'_id') : $args;

		if(!$id){
			return new WP_Error('invalid_id', [wpjam_get_post_type_setting($item['post_type'], 'title')]);
		}

		return $item['platform'] == 'template' ? get_permalink($id) : str_replace('%post_id%', $id, $item['path']);
	}

	public static function get_path_fields($args){
		$post_type	= $args['post_type'];
		$object		= get_post_type_object($post_type);

		return $object ? [$post_type.'_id' => self::get_field(['post_type'=>$post_type, 'required'=>true])] : [];
	}

	public static function get_field($args){
		$post_type	= wpjam_pull($args, 'post_type');
		$title		= wpjam_pull($args, 'title');

		if(is_null($title) && $post_type && is_string($post_type)){
			$title	= wpjam_get_post_type_setting($post_type, 'title');
		}

		return $args+[
			'title'			=> $title,
			'type'			=> 'text',
			'class'			=> 'all-options',
			'data_type'		=> 'post_type',
			'post_type'		=> $post_type,
			'placeholder'	=> '请输入'.$title.'ID或者输入关键字筛选',
			'show_in_rest'	=> ['type'=>'integer']
		];
	}

	public static function query_items($args){
		if(wpjam_pull($args, 'data_type')){
			return wpjam_get_posts($args+[
				's'					=> $args['search'] ?? null,
				'posts_per_page'	=> $args['number'] ?? 10,
				'suppress_filters'	=> false,
			]);
		}

		$args['post_status']	= wpjam_pull($args, 'status') ?: 'any';
		$args['post_type']		??= static::get_current_post_type();
		$args['posts_per_page']	??= $args['number'] ?? 10;

		$wp_query	= $GLOBALS['wp_query'];
		$wp_query->query($args);

		return [
			'items'	=> $wp_query->posts,
			'total'	=> $wp_query->found_posts
		];
	}

	public static function query_calendar($args){
		$args['post_status']	= wpjam_pull($args, 'status') ?: 'any';
		$args['post_type']		??= static::get_current_post_type();
		$args['monthnum']		= $args['month'];
		$args['posts_per_page']	= -1;

		$wp_query	= $GLOBALS['wp_query'];
		$wp_query->query($args);

		return array_reduce($wp_query->posts, function($carry, $post){
			$carry[explode(' ', $post->post_date)[0]][]	= $post;

			return $carry;
		}, []);
	}

	public static function get_views(){
		if(get_current_screen()->base != 'edit'){
			$post_type	= static::get_current_post_type();
			$counts		= $post_type ? array_filter((array)wp_count_posts($post_type)) : [];

			if($counts){
				$views		= ['all'=>['filter'=>['status'=>null, 'show_sticky'=>null], 'label'=>'全部', 'count'=>array_sum($counts)]];
				$statuses	= wpjam_slice(get_post_stati(['show_in_admin_status_list'=>true], 'objects'), array_keys($counts));

				return $views+wpjam_map($statuses, fn($object, $status)=> ['filter'=>['status'=>$status], 'label'=>$object->label, 'count'=>$counts[$status]]);
			}
		}
	}

	public static function filter_fields($fields, $id){
		if($id && !is_array($id) && !isset($fields['title']) && !isset($fields['post_title'])){
			return ['title'=>['title'=>wpjam_get_post_type_setting($id, 'title').'标题', 'type'=>'view', 'value'=>get_the_title($id)]]+$fields;
		}

		return $fields;
	}

	public static function get_meta($post_id, ...$args){
		// _deprecated_function(__METHOD__, 'WPJAM Basic 6.0', 'wpjam_get_metadata');
		return wpjam_get_metadata('post', $post_id, ...$args);
	}

	public static function update_meta($post_id, ...$args){
		// _deprecated_function(__METHOD__, 'WPJAM Basic 6.0', 'wpjam_update_metadata');
		return wpjam_update_metadata('post', $post_id, ...$args);
	}

	public static function update_metas($post_id, $data, $meta_keys=[]){
		return static::update_meta($post_id, $data, $meta_keys);
	}
}

/**
* @config menu_page, admin_load, register_json, init
**/
#[config('menu_page', 'admin_load', 'register_json', 'init')]
class WPJAM_Post_Type extends WPJAM_Register{
	public function __get($key){
		if($key == 'title'){
			return $this->labels ? $this->labels->singular_name : $this->label;
		}elseif($key != 'name' && property_exists('WP_Post_Type', $key)){
			$object	= get_post_type_object($this->name);

			if($object){
				return $object->$key;
			}
		}

		$value	= parent::__get($key);

		if($key == 'model'){
			if(!$value || !class_exists($value) || !is_subclass_of($value, 'WPJAM_Post')){
				return 'WPJAM_Post';
			}
		}elseif($key == 'permastruct'){
			$value	??= $this->call_model('get_'.$key);
			$value	= $value ? trim($value, '/') : $value;
		}

		return $value;
	}

	public function __set($key, $value){
		if($key != 'name' && property_exists('WP_Post_Type', $key)){
			$object	= get_post_type_object($this->name);

			if($object){
				$object->$key = $value;
			}
		}

		parent::__set($key, $value);
	}

	protected function preprocess_args($args){
		$args	= parent::preprocess_args($args);
		$args	+= ['plural'=>$this->name.'s', '_jam'=>true];

		if($args['_jam']){
			if(!is_array(array_get($args, 'taxonomies'))){
				unset($args['taxonomies']);
			}

			return $args+[
				'public'		=> true,
				'show_ui'		=> true,
				'hierarchical'	=> false,
				'rewrite'		=> true,
				'permastruct'	=> false,
				'supports'		=> ['title'],
			];
		}

		return $args;
	}

	public function get_menu_slug(){
		if(is_null($this->menu_slug)){
			$menu_page	= $this->get_arg('menu_page');

			if($menu_page && is_array($menu_page)){
				$this->menu_slug	= wp_is_numeric_array($menu_page) ? rest($menu_page)['menu_slug'] : $menu_page['menu_slug'];
			}
		}

		return $this->menu_slug;
	}

	public function to_array(){
		$this->filter_args();

		if(doing_filter('register_post_type_args')){
			if(!$this->_builtin && $this->permastruct){
				$this->permastruct	= str_replace(
					['%post_id%', '%postname%'],
					['%'.$this->name.'_id%', '%'.$this->name.'%'],
					$this->permastruct
				);

				if(strpos($this->permastruct, '%'.$this->name.'_id%')){
					if($this->hierarchical){
						$this->permastruct	= false;
					}else{
						$this->query_var	??= false;
					}
				}

				if($this->permastruct){
					$this->rewrite	= $this->rewrite ?: true;
				}
			}

			if($this->_jam){
				if($this->hierarchical){
					$this->supports		= array_merge($this->supports, ['page-attributes']);
				}

				if($this->rewrite){
					$this->rewrite	= is_array($this->rewrite) ? $this->rewrite : [];
					$this->rewrite	= $this->rewrite+['with_front'=>false, 'feeds'=>false];
				}
			}

			if($this->menu_icon){
				$this->menu_icon	= (str_starts_with($this->menu_icon, 'dashicons-') ? '' : 'dashicons-').$this->menu_icon;
			}
		}

		return $this->args;
	}

	public function get_fields($id=0, $action_key=''){
		if(in_array($action_key, ['add', 'set'])){
			if($action_key == 'add'){
				$fields['post_type']	= ['type'=>'hidden',	'value'=>$this->name];
				$fields['post_status']	= ['type'=>'hidden',	'value'=>'draft'];
			}

			$fields['post_title']	= ['title'=>'标题',	'type'=>'text',	'required'];

			if($this->supports('excerpt')){
				$fields['post_excerpt']	= ['title'=>'摘要',	'type'=>'textarea'];
			}

			if($this->supports('thumbnail')){
				$fields['_thumbnail_id']	= ['title'=>'头图', 'type'=>'img', 'size'=>'600x0',	'name'=>'meta_input[_thumbnail_id]'];
			}
		}

		if($this->supports('images')){
			$size	= $this->images_sizes ? $this->images_sizes[0] : '';

			$fields['images']	= [
				'title'			=> '头图',
				'name'			=> 'meta_input[images]',
				'type'			=> 'mu-img',
				'item_type'		=> 'url',
				'show_in_rest'	=> false,
				'size'			=> $size,
				'max_items'		=> $this->images_max_items
			];
		}

		if($this->supports('video')){
			$fields['video']	= ['title'=>'视频',	'type'=>'url',	'name'=>'meta_input[video]'];
		}

		foreach($this->get_items('_fields') as $key => $field){
			if(in_array($action_key, ['add', 'set']) && empty($field['name']) && !property_exists('WP_Post', $key)){
				$field['name']	= 'meta_input['.$key.']';
			}

			$fields[$key]	= $field;
		}

		return $fields ?? [];
	}

	public function register_option($list_table=false){
		if(!wpjam_get_post_option($this->name.'_base')){
			$fields	= $list_table ? [$this, 'get_fields'] : $this->get_fields();

			if($fields){
				wpjam_register_post_option($this->name.'_base', [
					'post_type'		=> $this->name,
					'title'			=> '基础信息',
					'page_title'	=> '设置'.$this->title,
					'fields'		=> $fields,
					'list_table'	=> $this->show_ui,
					'action_name'	=> 'set',
					'row_action'	=> false,
					'order'			=> 99,
				]);
			}
		}
	}

	public function get_support($feature){
		$support	= get_all_post_type_supports($this->name)[$feature] ?? false;

		if($support && is_array($support) && wp_is_numeric_array($support) && count($support) == 1){
			return reset($support);
		}

		return $support;
	}

	public function supports($feature){
		return post_type_supports($this->name, $feature);
	}

	public function get_size($type='thumbnail'){
		return $this->{$type.'_size'} ?: $type;
	}

	public function get_taxonomies(...$args){
		$taxonomies	= get_object_taxonomies($this->name);
		$output		= 'objects';
		$filters	= [];

		if($args){
			if(is_array($args[0])){
				$output		= $args[1] ?? 'objects';
				$filters	= $args[0];
			}else{
				$output		= $args[0];
			}
		}

		if($filters || $output == 'objects'){
			$objects	= array_filter(wpjam_fill($taxonomies, 'wpjam_get_taxonomy_object'));

			if($filters){
				$objects	= wp_filter_object_list($objects, $filters);
			}

			return $output == 'objects' ? $objects : array_keys($objects);
		}

		return $taxonomies;
	}

	public function in_taxonomy($taxonomy){
		return is_object_in_taxonomy($this->name, $taxonomy);
	}

	public function is_viewable(){
		return is_post_type_viewable($this->name);
	}

	public function reset_invalid_parent($value=0){
		$wpdb		= $GLOBALS['wpdb'];
		$post_ids	= $wpdb->get_col($wpdb->prepare("SELECT p1.ID FROM {$wpdb->posts} p1 LEFT JOIN  {$wpdb->posts} p2 ON p1.post_parent = p2.ID WHERE p1.post_type=%s AND p1.post_parent > 0 AND p2.ID is null", $this->name)) ?: [];

		array_walk($post_ids, fn($id)=> wp_update_post(['ID'=>$id, 'post_parent'=>$value]));

		return count($post_ids);
	}

	public function filter_labels($labels){
		$labels		= (array)$labels;
		$name		= $labels['name'];
		$search		= $this->hierarchical ? ['撰写新', '写文章', '页面', 'page', 'Page'] : ['撰写新', '写文章', '文章', 'post', 'Post'];
		$replace	= ['添加', '添加'.$name, $name, $name, ucfirst($name)];
		$labels		= wpjam_map($labels, fn($v, $k)=> ['all_items'=>'所有'.$name, 'archives'=>$name.'归档'][$k] ?? (($v && $v != $name) ? str_replace($search, $replace, $v) : $v));

		return array_merge($labels, (array)($this->labels ?? []));
	}

	public function filter_link($link, $post){
		return get_post_type($post) == $this->name ? str_replace('%'.$this->name.'_id%', $post->ID, $link) : $link;
	}

	public function registered_callback($name, $object){
		if($this->name == $name){
			if($this->permastruct){
				if(strpos($this->permastruct, '%'.$name.'_id%')){
					add_rewrite_tag('%'.$name.'_id%', '([0-9]+)', 'post_type='.$name.'&p=');

					remove_rewrite_tag('%'.$name.'%');

					add_filter('post_type_link',	[$this, 'filter_link'], 1, 2);
				}

				add_permastruct($name, $this->permastruct, array_merge($object->rewrite, ['feed'=>$this->rewrite['feeds']]));
			}

			$callback	= $this->registered_callback;

			if($callback && is_callable($callback)){
				$callback($name, $object);
			}
		}
	}

	public function registered(){
		if($this->_jam){
			if(is_admin() && $this->show_ui){
				add_filter('post_type_labels_'.$this->name,	[$this, 'filter_labels']);
			}

			add_action('registered_post_type_'.$this->name,	[$this, 'registered_callback'], 10, 2);

			wpjam_init(fn()=> register_post_type($this->name, $this->to_array()));

			if($this->options){
				wpjam_map($this->options, fn($option, $name)=> wpjam_register_post_option($name, $option+['post_type'=>$this->name]));
			}
		}
	}

	public static function filter_register_args($args, $name){
		if(did_action('init') || empty($args['_builtin'])){
			$object	= self::get($name);
			$object	= $object ? $object->update_args($args) : self::register($name, array_merge($args, ['_jam'=>false]));

			return $object->to_array();
		}

		return $args;
	}
}

class WPJAM_Posts{
	public static function query($query, &$args=[]){
		return new WP_Query(self::parse_query_vars($query, $args)+['no_found_rows'=>true, 'ignore_sticky_posts'=>true]);
	}

	public static function parse($query, $args=[], $format=''){
		$filter	= wpjam_pull($args, 'filter');
		$query	= is_object($query) ? $query : self::query($query, $args);

		if(!$filter && $query->get('related_query')){
			$filter	= 'wpjam_related_post_json';
		}

		if($format == 'date'){
			$day	= wpjam_pull($query->query_vars, 'day');
		}

		while($query->have_posts()){
			$query->the_post();

			$id		= get_the_ID();
			$json	= wpjam_get_post($id, $args);

			if($json){
				$json	= $filter ? apply_filters($filter, $json, $id, $args) : $json;

				if($format == 'date'){
					$date	= explode(' ', $json['date'])[0];
					$number	= (int)(explode('-', $date)[2]);

					if($day && $number != $day){
						continue;
					}

					$parsed[$date]		??= [];
					$parsed[$date][]	= $json;
				}else{
					$parsed[]	= $json;
				}
			}
		}

		if(!empty($args['list_query'])){
			wp_reset_postdata();
		}

		return $parsed ?? [];
	}

	public static function render($query, $args=[]){
		$output	= '';
		$query	= is_object($query) ? $query : self::query($query, $args);
		$get_cb	= fn($name, &$args)=> (($value = wpjam_pull($args, $name)) && is_callable($value)) ? $value : [self::class, $name];

		$item_callback	= $get_cb('item_callback', $args);
		$wrap_callback	= $get_cb('wrap_callback', $args);
		$title_number	= wpjam_pull($args, 'title_number');
		$threshold		= strlen(count($query->posts));

		while($query->have_posts()){
			$query->the_post();

			$output .= $item_callback(get_the_ID(), array_merge($args, $title_number ? ['title_number'=>zeroise($query->current_post+1, $threshold)] : []));
		}

		wp_reset_postdata();

		return $wrap_callback($output, $args);
	}

	public static function item_callback($post_id, $args){
		$args	+= [
			'title_number'	=> 0,
			'excerpt'		=> false,
			'thumb'			=> true,
			'size'			=> 'thumbnail',
			'thumb_class'	=> 'wp-post-image',
			'wrap_tag'		=> 'li'
		];

		$title	= get_the_title($post_id);
		$item	= wpjam_wrap($title);

		if($args['title_number']){
			$item->before('span', ['title-number'], $args['title_number'].'. ');
		}

		if($args['thumb'] || $args['excerpt']){
			$item->wrap('h4');

			if($args['thumb']){
				$item->before(get_the_post_thumbnail($post_id, $args['size'], ['class'=>$args['thumb_class']]));
			}

			if($args['excerpt']){
				$item->after(wpautop(get_the_excerpt($post_id)));
			}
		}

		$item->wrap('a', ['href'=>get_permalink($post_id), 'title'=>strip_tags($title)]);

		if($args['wrap_tag']){
			$item->wrap($args['wrap_tag']);
		}

		return $item->render();
	}

	public static function wrap_callback($output, $args){
		if(!$output){
			return '';
		}

		$args	+= [
			'title'		=> '',
			'div_id'	=> '',
			'class'		=> [],
			'thumb'		=> true,
			'wrap_tag'	=> 'ul'
		];

		$output	= wpjam_wrap($output);

		if($args['wrap_tag']){
			$output->wrap($args['wrap_tag'])->add_class($args['class']);

			if($args['thumb']){
				$output->add_class('has-thumb');
			}
		}

		if($args['title']){
			$output->before($args['title'], 'h3');
		}

		if($args['div_id']){
			$output->wrap('div', ['id'=>$args['div_id']]);
		}

		return $output->render();
	}

	public static function parse_query_vars($vars, &$args=[]){
		$query_keys	= wpjam_fill([...array_values(get_taxonomies(['_builtin'=>false])), 'category', 'post_tag'], 'wpjam_get_taxonomy_query_key');
		$term_ids	= array_filter(wpjam_map($query_keys, fn($v)=> wpjam_get($vars, $v)));
		$vars 		= wpjam_except($vars, array_values($query_keys));

		if(!empty($vars['taxonomy']) && empty($vars['term'])){
			$term_id	= wpjam_pull($vars, 'term_id');

			if($term_id){
				if(is_numeric($term_id)){
					$term_ids[wpjam_pull($vars, 'taxonomy')]	= $term_id;
				}else{
					$vars['term']	= $term_id;
				}
			}
		}

		foreach($term_ids as $tax => $term_id){
			if($tax == 'category' && $term_id != 'none'){
				$vars['cat']	= $term_id;
			}else{
				$vars['tax_query'][]	= ['taxonomy'=>$tax, 'field'=>'term_id']+($term_id == 'none' ? ['operator'=>'NOT EXISTS'] : ['terms'=>[$term_id]]);
			}
		}

		foreach(['cursor'=>'before', 'since'=>'after'] as $key => $var){
			$value	= wpjam_pull($vars, $key);

			if($value){
				$vars['date_query'][]	= [$var=> wpjam_date('Y-m-d H:i:s', $value)];
			}
		}

		if($args){
			$vars	= array_merge($vars, wpjam_pull($args, ['post_type', 'orderby', 'posts_per_page']));
			$number	= wpjam_pull($args, 'number');
			$vars	= array_merge($vars, ($number ? ['posts_per_page'=>$number] : []));
			$days	= wpjam_pull($args, 'days');

			if($days){
				$after	= wpjam_date('Y-m-d', time() - DAY_IN_SECONDS * $days).' 00:00:00';
				$column	= wpjam_pull($args, 'column') ?: 'post_date_gmt';

				$vars['date_query'][]	= ['column'=>$column, 'after'=>$after];
			}
		}

		return $vars;
	}

	public static function get_related_query($post, $args=[]){
		$post	= get_post($post);

		if($post){
			$object		= wpjam_post($post);
			$post_type	= [get_post_type($post)];
			$tt_ids		= [];

			foreach(array_diff($object->taxonomies, ['post_format']) as $tax){
				$terms	= $object->get_terms($tax);

				if($terms){
					$post_type	= array_merge($post_type, get_taxonomy($tax)->object_type);
					$tt_ids		= array_merge($tt_ids, array_column($terms, 'term_taxonomy_id'));
				}
			}

			if($tt_ids){
				return self::query([
					'related_query'		=> true,
					'post_status'		=> 'publish',
					'post__not_in'		=> [$post->ID],
					'post_type'			=> array_unique($post_type),
					'term_taxonomy_ids'	=> array_unique(array_filter($tt_ids)),
				], $args);
			}
		}

		return false;
	}

	public static function get_related_object_ids($tt_ids, $number, $page=1){
		$tt_ids	= implode(',', array_map('intval', $tt_ids));
		$key	= 'related_object_ids:'.$tt_ids.':'.$page.':'.$number;
		$sql	= 'SELECT object_id, count(object_id) as cnt FROM '.$GLOBALS['wpdb']->term_relationships.' WHERE term_taxonomy_id IN ('.$tt_ids.') GROUP BY object_id ORDER BY cnt DESC, object_id DESC LIMIT '.(($page-1)*$number).', '.$number;

		return wpjam_transient($key, fn()=> $GLOBALS['wpdb']->get_col($sql), DAY_IN_SECONDS);
	}

	public static function parse_json_module($args){
		$action	= wpjam_pull($args, 'action');

		if(!$action){
			return;
		}

		$wp	= $GLOBALS['wp'];

		if(isset($wp->raw_query_vars)){
			$wp->query_vars		= $wp->raw_query_vars;
		}else{
			$wp->raw_query_vars	= $wp->query_vars;
		}

		if($action == 'list'){
			return self::parse_list_json_module($args);
		}elseif($action == 'calendar'){
			return self::parse_calendar_json_module($args);
		}elseif($action == 'get'){
			return self::parse_get_json_module($args);
		}elseif($action == 'upload'){
			return self::parse_media_json_module($args, 'post_type');
		}
	}

	protected static function parse_json_query_vars($vars){
		$post_type	= $vars['post_type'] ?? '';

		if(is_string($post_type) && str_contains($post_type, ',')){
			$post_type = $vars['post_type']	= wp_parse_list($post_type);
		}

		$taxonomies	= ($post_type && is_string($post_type)) ? get_object_taxonomies($post_type) : get_taxonomies(['public'=>true]);

		foreach(array_diff($taxonomies, ['post_format']) as $tax){	// taxonomy 参数处理,同时支持 $_GET 和 $query_vars 参数
			if($tax == 'category'){
				$vars['cat']	??= wpjam_found(['category_id', 'cat_id'], fn($k)=> (int)wpjam_get_parameter($k));
	
				if(!$vars['cat']){
					unset($vars['cat']);	
				}
			}else{
				$query_key	= wpjam_get_taxonomy_query_key($tax);
				$term_id	= (int)wpjam_get_parameter($query_key);

				if($term_id){
					$vars[$query_key]	= $term_id;
				}
			}
		}

		$term_id	= (int)wpjam_get_parameter('term_id');
		$taxonomy	= wpjam_get_parameter('taxonomy');

		if($term_id && $taxonomy){
			$vars['term_id']	= $term_id;
			$vars['taxonomy']	= $taxonomy;
		}

		return self::parse_query_vars($vars);
	}

	protected static function parse_json_output($vars){
		$name	= $vars['post_type'] ?? '';

		return ($name && is_string($name)) ? wpjam_get_post_type_setting($name, 'plural') ?: $name.'s' : 'posts';
	}

	/* 规则:
	** 1. 分成主的查询和子查询($query_args['sub']=1)
	** 2. 主查询支持 $_GET 参数 和 $_GET 参数 mapping
	** 3. 子查询(sub)只支持 $query_args 参数
	** 4. 主查询返回 next_cursor 和 total_pages,current_page,子查询(sub)没有
	** 5. $_GET 参数只适用于 post.list
	** 6. term.list 只能用 $_GET 参数 mapping 来传递参数
	*/
	public static function parse_list_json_module($args){
		$output	= wpjam_pull($args, 'output');
		$sub	= wpjam_pull($args, 'sub');

		$is_main_query	= !$sub;	// 子查询不支持 $_GET 参数,置空之前要把原始的查询参数存起来

		if($is_main_query){
			$wp			= $GLOBALS['wp'];
			$wp_query	= $GLOBALS['wp_query'];
			$query_vars	= array_merge($wp->query_vars, $args);

			$number		= wpjam_found(['number', 'posts_per_page'], fn($k)=> (int)wpjam_get_parameter($k));
			$offset		= wpjam_get_parameter('offset');
			$query_vars	+= ($number && $number != -1) ? ['posts_per_page'=> $number > 100 ? 100 : $number] : [];
			$query_vars	+= $offset ? compact('offset') : [];

			if($post__in = wpjam_get_parameter('post__in')){
				$query_vars['post__in']			= wp_parse_id_list($post__in?: []);
				$query_vars['orderby']			??= 'post__in';
				$query_vars['posts_per_page']	??= -1;	
			}

			$orderby	= $query_vars['orderby'] ?? 'date';
			$use_cursor	= empty($query_vars['paged']) && empty($query_vars['s']) && !is_array($orderby) && in_array($orderby, ['date', 'post_date']);

			if($use_cursor){
				foreach(['cursor', 'since'] as $key){
					$query_vars[$key]	= (int)wpjam_get_parameter($key);

					if($query_vars[$key]){
						$query_vars['ignore_sticky_posts']	= true;
					}
				}
			}

			$query_vars	= $wp->query_vars = self::parse_json_query_vars($query_vars);

			$wp->query_posts();
		}else{
			$wp_query	= self::query($args);
		}

		$parsed	= self::parse($wp_query, $args);

		if($is_main_query){
			$posts_json['total']	= $total = $wp_query->found_posts;

			if($wp_query->get('nopaging')){
				$posts_json['total_pages']	= $total ? 1 : 0;
				$posts_json['current_page']	= 1;
			}else{
				$posts_json['total_pages']	= $wp_query->max_num_pages;
				$posts_json['current_page']	= $wp_query->get('paged') ?: 1;
			}

			if($use_cursor){
				$posts_json['next_cursor']	= ($parsed && $wp_query->max_num_pages > 1) ? end($parsed)['timestamp'] : 0;
			}

			if(is_front_page()){
				$is	= 'home';
			}elseif(is_author()){
				$is	= 'author';

				$posts_json	+= ['current_author'=>wpjam_get_user($wp_query->get('author'))];
			}elseif(is_category() || is_tag() || is_tax()){
				$is		= is_category() ? 'category' : (is_tag() ? 'tag' : 'tax');
				$term	= $wp_query->get_queried_object();

				$posts_json	+= ['current_taxonomy'=>($term ? $term->taxonomy : null)];
				$posts_json	+= $term ? ['current_term'=>wpjam_get_term($term, $term->taxonomy)] : [];
			}elseif(is_post_type_archive()){
				$is		= 'post_type_archive';
				$object	= $wp_query->get_queried_object();
				
				$posts_json	+= ['current_post_type'=>($object ? $object->name : null)];
			}elseif(is_search()){
				$is	= 'search';
			}elseif(is_archive()){
				$is	= 'archive';
			}

			if(isset($is)){
				$posts_json['is']	= $is;
			}

			$output	= $output ?: self::parse_json_output($query_vars);
		}else{
			$output	= $output ?: 'posts';
		}

		$posts_json[$output]	= $parsed;

		return apply_filters('wpjam_posts_json', $posts_json, $wp_query, $output);
	}

	public static function parse_calendar_json_module($args){
		$output	= wpjam_pull($args, 'output');
		$wp		= $GLOBALS['wp'];
		$vars	= array_merge($wp->query_vars, $args, [
			'year'		=> (int)wpjam_get_parameter('year') ?: wpjam_date('Y'),
			'monthnum'	=> (int)wpjam_get_parameter('month') ?: wpjam_date('m'),
			'day'		=> (int)wpjam_get_parameter('day')
		]);

		$wp->query_vars	= $vars = self::parse_json_query_vars(wpjam_except($vars, 'day'));

		$wp->query_posts();

		$parsed	= self::parse($GLOBALS['wp_query'], $args, 'date');
		$output	= $output ?: self::parse_json_output($vars);

		return [$output=>$parsed];
	}

	public static function parse_get_json_module($args){
		$wp			= $GLOBALS['wp'];
		$wp_query	= $GLOBALS['wp_query'];

		$wp->set_query_var('cache_results', true);

		if(!empty($args['post_status'])){
			$wp->set_query_var('post_status', $args['post_status']);
		}

		$query_vars	= $wp->query_vars;
		$post_type	= array_get($args, 'post_type');
		$post_type	= $post_type == 'any' ? '' : $post_type;

		if(!$post_type){
			$post_types	= get_post_types(['_builtin'=>false, 'query_var'=>true], 'objects');
			$query_keys	= wp_list_pluck($post_types, 'query_var')+['post'=>'name', 'page'=>'pagename'];
			$post_type	= array_find_key($query_keys, fn($key, $post_type)=> !empty($query_vars[$key]));

			if($post_type){
				$key	= $query_keys[$post_type];
				$name	= $query_vars[$key];
			}
		}

		if(empty($name)){
			if($post_type){
				$key		= is_post_type_hierarchical($post_type) ? 'pagename' : 'name';
				$required	= empty($query_vars[$key]);
			}else{
				$required	= true;
			}

			$post_id	= array_get($args, 'id') ?: (int)wpjam_get_parameter('id', ['required'=>$required]);
			$post_type	= $post_type ?: get_post_type($post_id);

			if(!post_type_exists($post_type)){
				wp_die('invalid_post_type');
			}

			if(!$post_type || ($post_id && get_post_type($post_id) != $post_type)){
				wp_die('无效的参数:id', 'invalid_parameter');
			}

			$wp->set_query_var('post_type', $post_type);

			if($post_id){
				$wp->set_query_var('p', $post_id);
			}
		}

		$wp->query_posts();

		if(empty($post_id) && empty($args['post_status']) && !$wp_query->have_posts()){
			$post_id	= apply_filters('old_slug_redirect_post_id', null);

			if(!$post_id){
				wp_die('无效的文章 ID', 'invalid_post');
			}

			$wp->set_query_var('post_type', 'any');
			$wp->set_query_var('p', $post_id);
			$wp->set_query_var('name', '');
			$wp->set_query_var('pagename', '');
			$wp->query_posts();
		}

		$parsed	= $wp_query->have_posts() ? self::parse($wp_query, $args) : [];

		if(!$parsed){
			wp_die('参数错误', 'invalid_parameter');
		}

		$parsed		= current($parsed);
		$output		= array_get($args, 'output') ?: $parsed['post_type'];
		$response	= wpjam_pull($parsed, ['share_title', 'share_image', 'share_data']);

		if(is_single($parsed['id']) || is_page($parsed['id'])){
			wpjam_update_post_views($parsed['id']);
		}

		return array_merge($response, [$output => $parsed]);
	}

	public static function parse_media_json_module($args, $type=''){
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/media.php';
		require_once ABSPATH . 'wp-admin/includes/image.php';

		$media	= array_get($args, 'media') ?: 'media';
		$output	= array_get($args, 'output') ?: 'url';

		if(!isset($_FILES[$media])){
			wp_die('无效的参数:media', 'invalid_parameter');
		}

		if($type == 'post_type'){
			$pid	= (int)wpjam_get_post_parameter('post_id',	['default'=>0]);
			$id		= wpjam_try('media_handle_upload', $media, $pid);
			$url	= wp_get_attachment_url($id);
			$query	= wpjam_get_image_size($id);
		}else{
			$upload	= wpjam_try('wpjam_upload', $media);
			$url	= $upload['url'];
			$query	= wpjam_get_image_size($upload['file'], 'file');
		}

		if($query){
			$url	= add_query_arg($query, $url);
		}

		return $output ? [$output => $url] : $url;
	}

	public static function json_modules_callback($type='list', $args=[]){
		if(strpos($type, '.')){
			$parts	= explode('.', $type);
			$type	= end($parts);
		}

		$post_type	= wpjam_get_parameter('post_type');
		$args		+= ['post_type'=>$post_type, 'action'=>$type, 'output'=>($type == 'get' ? 'post' : 'posts')];
		$modules[]	= ['type'=>'post_type',	'args'=>array_filter($args)];

		if($type == 'list' && $post_type && is_string($post_type) && !str_contains($post_type, ',')){
			foreach(get_object_taxonomies($post_type, 'objects') as $tax => $tax_object){
				if($tax_object->hierarchical && $tax_object->public){
					$modules[]	= ['type'=>'taxonomy',	'args'=>['taxonomy'=>$tax, 'hide_empty'=>0]];
				}
			}
		}

		return $modules;
	}

	public static function filter_clauses($clauses, $wp_query){
		global $wpdb;

		if($wp_query->get('related_query')){
			$tt_ids	= $wp_query->get('term_taxonomy_ids');

			if($tt_ids){
				$clauses['join']	.= "INNER JOIN {$wpdb->term_relationships} AS tr ON {$wpdb->posts}.ID = tr.object_id";
				$clauses['where']	.= " AND tr.term_taxonomy_id IN (".implode(",",$tt_ids).")";
				$clauses['groupby']	.= " tr.object_id";
				$clauses['orderby']	= " count(tr.object_id) DESC, {$wpdb->posts}.ID DESC";
			}
		}else{
			$orderby	= $wp_query->get('orderby');
			$order		= $wp_query->get('order') ?: 'DESC';

			if($orderby == 'comment_date'){
				$comment_type	= $wp_query->get('comment_type') ?: 'comment';
				$type_str		= $comment_type	== 'comment' ? "'comment', ''" : "'".esc_sql($comment_type)."'";
				$ct_where		= "ct.comment_type IN ({$type_str}) AND ct.comment_parent=0 AND ct.comment_approved NOT IN ('spam', 'trash', 'post-trashed')";

				$clauses['join']	= "INNER JOIN {$wpdb->comments} AS ct ON {$wpdb->posts}.ID = ct.comment_post_ID AND {$ct_where}";
				$clauses['groupby']	= "ct.comment_post_ID";
				$clauses['orderby']	= "MAX(ct.comment_ID) {$order}";
			}elseif($orderby == 'views' || $orderby == 'comment_type'){
				$meta_key			= $orderby == 'comment_type' ? $wp_query->get('comment_count') : 'views';
				$clauses['join']	.= "LEFT JOIN {$wpdb->postmeta} jam_pm ON {$wpdb->posts}.ID = jam_pm.post_id AND jam_pm.meta_key = '{$meta_key}' ";
				$clauses['orderby']	= "(COALESCE(jam_pm.meta_value, 0)+0) {$order}, " . $clauses['orderby'];
			}elseif(in_array($orderby, ['', 'date', 'post_date'])){
				$clauses['orderby']	.= ", {$wpdb->posts}.ID {$order}";
			}
		}

		return $clauses;
	}

	public static function filter_content_save_pre($content){
		if($content && is_serialized($content)){
			$name		= current_filter();
			$callback	= 'wp_filter_post_kses';

			if(has_filter($name, $callback)){
				remove_filter($name, $callback);
				add_filter($name, fn($content)=> $content && is_serialized($content) ? $content : $callback($content));
			}
		}

		return $content;
	}
}