HEX
Server: nginx/1.16.1
System: Linux VM-0-14-centos 4.18.0-348.7.1.el8_5.x86_64 #1 SMP Wed Dec 22 13:25:12 UTC 2021 x86_64
User: www (1000)
PHP: 8.3.31
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.sceybwg.com/wp-content/themes/Modular/extends/wpjam-module/public/module.php
<?php
/**
* @config orderby=order
**/
#[config(['orderby'=>'order'])]
class WPJAM_Module_Type extends WPJAM_Register{
	public function __get($key){
		$value	= parent::__get($key);

		if(!is_null($value)){
			return $value;
		}

		if($key == 'label'){
			if($this->image){
				return ['image'=>$this->image, 'label'=>$this->title, 'class'=>'preview'];
			}

			if($this->remixicon){
				$icon	= wpjam_tag('i', ['ri-'.wpjam_remove_prefix($this->remixicon, 'ri-')]);
			}elseif($this->dashicon){
				$icon	= wpjam_tag('span', ['dashicons', 'dashicons-'.$this->dashicon]);
			}

			return wpjam_join(' ', [$icon ?? '', $this->title]);
		}elseif($key == 'post_type'){
			return 'post';
		}elseif($key == 'taxonomy'){
			return 'category';
		}elseif($key == 'title_display'){
			return true;
		}elseif($key == 'title_custom'){
			return $this->title_display;
		}
	}

	public function get_fields($action_key, $id=0){
		if($action_key == 'title'){
			$type	= $this->title_display ? 'checkbox' : 'view';

			return [
				'title'			=> ['type'=>'text',	'required',	'class'=>'all-options',	'label'=>true],
				'title_display'	=> ['type'=>$type,	'before'=>'显示:',	'fields'=>($this->title_custom ? [
					'title_position'	=> ['before'=>'位置:',	'type'=>'radio',	'options'=>['left'=>'左','center'=>'中','right'=>'右']],
					'title_color'		=> ['before'=>'颜色:',	'type'=>'color',	'value'=>'#000']
				] : []), ($type == 'view' ? 'value' : 'label')=>'模块标题在页面上'.($type == 'view' ? '不': '').'显示']
			];
		}elseif($action_key == 'setting'){
			$fields		= [
				'background_color'	=> ['title'=>'背景色',	'type'=>'color',	'value'=>'#FFF',	'alpha'=>true],
				'margin'			=> ['title'=>'下边距',	'type'=>'number',	'class'=>'small-text',	'value'=>0],
				'border_radius'		=> ['title'=>'圆角',		'type'=>'checkbox',	'options'=>['top'=>'上圆角','bottom'=>'下圆角'],	'value'=>['top', 'bottom']]
			];
		}elseif(in_array($action_key, ['create', 'edit'])){
			$fields	= [
				'title'		=> ['title'=>'标题',	'type'=>'fieldset',	'fields'=>wpjam_map($this->get_fields('title', $id), [self::class, 'prepend_settting_name'])],
				'excerpt'	=> ['title'=>'简介',	'type'=>'textarea'],
				'hides'		=> ['title'=>'隐藏',	'type'=>'checkbox',	'name'=>'setting[platform_hides]'],
				'setting'	=> ['title'=>'设置',	'type'=>'fieldset',	'fields'=>wpjam_map($this->get_fields('setting', $id), [self::class, 'prepend_settting_name'])]
			];

			if(!$this->excerpt){
				unset($fields['excerpt']);
			}

			$platforms	= wpjam_get_module_platforms('objects');
			$platforms	= $this->platforms ? wpjam_pick($platforms, $this->platforms) : $platforms;

			if($platforms && count($platforms) > 1){
				$fields['hides']['options']		= wpjam_map($platforms, fn($v)=> $v->title);
				$fields['hides']['max_items']	= count($platforms)-1;
			}else{
				unset($fields['hides']);
			}

			if($this->content_path){
				$fields	= $this->append_path_fields($fields, 'content');
			}
		}

		$args	= ($object	= wpjam_get_post_object($id)) ? $object->setting : [];
		$args	= array_merge($args, ['id'=>$id, 'type_object'=>$this]);
		$fields	= wpjam_merge(($fields ?? []), $this->call_method('get_fields', $action_key, $args) ?: []);
		$fields	= apply_filters('wpjam_module_type_fields', $fields, $action_key, $this->name, $args);

		if(in_array($action_key, ['add_item', 'edit_item']) && $this->item_path !== false){
			return $this->append_path_fields($fields);
		}

		return $fields;
	}

	public function is_over($count){
		$max	= $this->max_modules;

		return $max && $count >= $max ? wpjam_throw('over_max_modules', '最多'.$max.'个'.$this->title.'模块') : false;
	}

	protected static function append_path_fields($fields, $prepend=''){
		$path_fields	= wpjam_filter($fields, fn($field, $k)=> str_starts_with($k, 'page_key_'));
		$fields			= array_diff_key($fields, $path_fields);

		return $fields + wpjam_merge(wpjam_get_module_path_fields($prepend), $path_fields);
	}

	public static function prepend_settting_name($field, $key){
		if($key != 'title' && is_array($field)){
			if(!empty($field['fields'])){
				$field['fields']	= wpjam_map($field['fields'], [self::class, 'prepend_settting_name']);
			}

			if(!isset($field['type']) || !in_array($field['type'], ['fieldset', 'fields'])){
				$field['name']	= 'setting['.$key.']';
			}
		}

		return $field;
	}

	public static function get_options(){
		return wp_list_pluck(self::get_registereds(), 'label');
	}

	public static function get_defaults(){
		return [
			'slider'	=> [
				'title'		=> '幻灯片',
				'model'		=> 'WPJAM_Slider_Module',
				'dashicon'	=> 'images-alt2',
				'max_items'	=> 10,
				'items'		=> true
			],
			'nav'		=> [
				'title'		=> '快捷导航',
				'model'		=> 'WPJAM_Nav_Module',
				'dashicon'	=> 'share',
				'max_items'	=> 15,
				'items'		=> true
			],
			'cube'		=> [
				'title'		=> '图片魔方',
				'model'		=> 'WPJAM_Cube_Module',
				'dashicon'	=> 'layout'
			],
			'text-list'	=> [
				'title'		=> '文本列表',
				'model'		=> 'WPJAM_Text_List_Module',
				'dashicon'	=> 'editor-ul',
				'max_items'	=> 10,
				'items'		=> true
			],
			'text'		=> [
				'title'			=> '简单文本',
				'model'			=> 'WPJAM_Text_Module',
				'dashicon'		=> 'editor-textcolor',
				'content_path'	=> true
			]
		];
	}
}

/**
 * @post_type module
**/
#[post_type('module')]
class WPJAM_Module extends WPJAM_Post{
	use WPJAM_Items_Trait;

	public function __get($key){
		if($key == 'shared'){
			return is_object_in_term($this->id, 'module_page', WPJAM_Module_Page::get_shared());
		}elseif($key == 'items'){
			return $this->get_items();
		}elseif($key == 'setting'){
			$value	= $this->_module_setting ?: [];

			if(!empty($value['background_color']) && strlen($value['background_color']) == 7 && isset($value['background_opacity'])){
				$value['background_color']	.= str_pad(dechex(round(255*$value['background_opacity'], 0)), 2, '0', STR_PAD_LEFT);
			}

			return $value;
		}elseif($key == 'type'){
			return $this->_module_type;
		}elseif($key == '_type_object'){
			return WPJAM_Module_Type::get($this->type);
		}elseif($key == 'model'){
			return $this->_type_object->model;
		}elseif(in_array($key, ['data', 'module'])){
			return [
				'id'		=> $this->id,
				'type'		=> $this->type,
				'model'		=> $this->model,
				'status'	=> $this->status,
				'title'		=> $this->title,
				'excerpt'	=> $this->excerpt,
				'setting'	=> $this->setting,
				'content'	=> $this->items
			];
		}

		return parent::__get($key);
	}

	public function get_type_fields($action_key){
		return $this->_type_object->get_fields($action_key, $this->id);
	}

	public function get_items(){
		return $this->get_unserialized();
	}

	public function update_items($items){
		return $this->save(['content'=>array_values($items)]);
	}

	protected function validate_item($item=null, $key=null, $action=''){
		if($action == 'add' && ($max = $this->_type_object->max_items) && count($this->items) >= $max){
			return new WP_Error('over_quota', '最多'.$max.'个'.$this->_type_object->title);
		}

		return isset($item['page_key']) ? WPJAM_Module_Platform::validate_item($item) : true;
	}

	protected function sanitize_item($item, $id=null){
		return array_filter($item, fn($v)=> !is_null($v));
	}

	protected function after_delete_item($key){
		$this->items || $this->unpublish();
	}

	protected function parse_by_field($value, $field){
		return ($value && $field['type'] == 'img') ? wpjam_get_thumbnail($value, wpjam_get($field, 'size')) : $value;
	}

	protected function parse_by_fields($data, $fields){
		return wpjam_map($data, fn($v, $k)=> isset($fields[$k]) ? $this->parse_by_field($v, $fields[$k]) : $v);
	}

	protected function parse_item($item, $args=[]){
		$item	= apply_filters('wpjam_module_item', $item);
		$parsed	= WPJAM_Module_Platform::parse_item($item);

		return wpjam_reduce($args['fields'] ?? [], fn($c, $v, $k)=> str_starts_with($k, 'page_key_') ? $c : wpjam_set($c, $k, $this->parse_by_field($item[$k] ?? '', $v)), $parsed);
	}

	protected function parse_items(...$args){
		$items	= count($args) > 1 ? array_shift($args) : $this->items;
		$args	= ['fields'=>$this->get_type_fields('edit_item')]+$args[0];

		return array_values(array_map(fn($item)=> $this->parse_item($item, $args), $items));
	}

	protected function render_items(...$args){
		$items	= count($args) > 1 ? array_shift($args) : $this->items;

		return ['items'=>$items, 'args'=>['sortable'=>true, 'max_items'=> $this->_type_object->max_items]+$args[0]];
	}

	public function is_publishable(){
		$this->items || wpjam_throw('empty_'.$this->type, '未设置'.$this->_type_object->title.',不能发布');

		$items	= wp_is_numeric_array($this->items) ? $this->items : [$this->items];
		$fields	= $this->get_type_fields('edit_item') ?: [];
		$key	= array_find_key($fields, fn($field, $key)=> (isset($field['required']) || in_array('required', $field, true)) && array_find($items, fn($item)=> empty($item[$key])));

		return $key ? new WP_Error('empty_'.$key, $fields[$key]['title'].'不能为空') : true;
	}

	public function is_deletable($label='删除'){
		if(self::page('is_shared')){
			return count(wp_get_post_terms($this->id, 'module_page')) > 1 ? new WP_Error('shared_module_in_use', '不可'.$label.'已被页面使用的模块') : true;
		}else{
			return $this->shared ? new WP_Error('shared_module', '不可'.$label.'共用模块') : true;
		}
	}

	public function unpublish(){
		$result	= $this->is_deletable('撤回');

		return is_wp_error($result) ? $result : parent::unpublish();
	}

	public function copy_to($data){
		$object	= !empty($data['to_page']) ? wpjam_get_term_object($data['to_page']) : null;

		return $object ? $object->add_module(array_merge($this->data, ['status'=>'draft'])) : new WP_Error('invalid_page', '无效的模块页面');
	}

	public function show_if($action_key){
		if($action_key == 'delete'){
			return ['title'=> !self::page('is_shared') && $this->shared ? '移除' : '删除'];
		}
	}

	public function parse($args=[]){
		$args	= $args ?: (($platform	= WPJAM_Module_Platform::get_current()) ? $platform->get_arg('module_page') : []);

		if(array_any([
			[$this->_type_object->platforms, false],
			[($this->setting['platform_hides'] ?? []), true]
		], fn($v)=> $v[0] && $v[1] === in_array($args['platform'], $v[0]))){
			return;
		}

		$args['border_radius']	= !empty($args['module_page']) ? (int)($args['module_page']->border_radius) : 0;

		$data	= [
			'id'		=> $this->id,
			'type'		=> $this->type,
			'status'	=> $this->status,
			'title'		=> $this->parse_title($args),
			'excerpt'	=> $this->parse_excerpt($args),
			'setting'	=> $this->parse_setting($args),
			'style'		=> $this->parse_style($args),
			'content'	=> $this->parse_content($args),
		];

		return method_exists($this, 'parse_module') ? $this->parse_module($data, $args) : $data;
	}

	protected function parse_title($args){
		return $args['platform'] == 'template' ? $this->title : html_entity_decode($this->title);
	}

	protected function parse_excerpt($args){
		return $args['platform'] == 'template' ? $this->excerpt : html_entity_decode($this->excerpt);
	}

	protected function parse_content($args){
		return $this->_type_object->items ? $this->parse_items($args) : $this->parse_by_fields($this->get_unserialized(), $this->get_type_fields('edit'));
	}

	protected function parse_setting($args){
		$setting	= wpjam_except($this->setting, ['background_color', 'border_radius', 'margin']);

		if($this->_type_object->title_display){
			$setting['title_display']	= (bool)($setting['title_display'] ?? false);
		}else{
			$setting['title_display']	= false;
		}

		if($this->_type_object->title_custom){
			$setting['title_position']	??= 'left';
			$setting['title_color']		??= '';
		}else{
			$setting['title_position']	= '';
			$setting['title_color']		= '';
		}

		return $this->parse_by_fields($setting, $this->get_type_fields('setting'));
	}

	protected function parse_style($args){
		$setting	= $this->setting;

		if(!empty($setting['margin'])){
			$style[]	= 'margin-bottom:'.$setting['margin'].$args['unit'];
		}

		if(!empty($setting['background_color'])){
			$style[]	= 'background:'.$setting['background_color'];
		}

		if($args['border_radius']){
			$radius_value	= $args['border_radius'];
			$border_radius	= $setting['border_radius'] ?? ['top', 'bottom'];

			if($border_radius){
				$radius_value	.= $args['unit'];
				$radius_style	= array_map(fn($k)=> in_array($k, $border_radius) ? $radius_value.' '.$radius_value : '0 0', ['top', 'bottom']);
				$style[]		= 'border-radius:'.implode(' ', $radius_style);
			}
		}

		return !empty($style) ? implode('; ', $style).';' : '';
	}

	public function render(){
		if(method_exists($this, 'render_module')){
			$data	= $this->render_module($this->data);

			return [
				'title'		=> $this->render_title(),
				'content'	=> is_array($data['content']) ? '' : $data['content'],
				'setting'	=> is_array($data['setting']) ? '' : $data['setting'],
			]+$data;
		}

		return [
			'id'		=> $this->id,
			'type'		=> $this->type,
			'model'		=> $this->model,
			'status'	=> $this->status,
			'title'		=> $this->render_title(),
			'excerpt'	=> $this->excerpt,
			'content'	=> $this->render_content(),
			'setting'	=> $this->render_setting(),
		];
	}

	protected function render_title(){
		if($this->_type_object->title_display && !empty($this->setting['title_display'])){
			if($this->_type_object->title_custom){
				$style		= ['color'=> $this->setting['title_color'] ?? ''];
				$display	= '居'.wpjam_get($this->get_type_fields('title'), 'title_display.fields.title_position.options.'.($this->setting['title_position'] ?: 'left'));
			}
		}else{
			$display	= '不';
		}

		return wpjam_tag('strong', ['style'=>($style ?? [])], wpjam_join(' — ', [
			$this->title,
			$this->status == 'draft' ? wpjam_tag('span', ['post-state'], '待发布') : '',
			!self::page('is_shared') && $this->shared ? wpjam_tag('span', ['shared-module'], '共用模块') : ''
		]))->after('span', ['title-display'], '标题在页面上'.($display ?? '').'显示');
	}

	protected function render_content(){
		return $this->get_unserialized();
	}

	protected function render_setting(){
		$rendered	= [];
		$setting	= $this->setting;

		foreach($this->get_type_fields('setting') as $key => $field){
			if(!in_array($field['type'], ['color', 'fieldset', 'view']) && !empty($field['title']) && !in_array($key, ['border_radius'])){
				$value	= $setting[$key] ?? '';

				if(isset($field['options'])){
					$value	= wpjam_array($value);
					$value	= wpjam_map($value, fn($v)=> $field['options'][$v] ?? $v);

					if(count($value) == 0){
						continue;
					}elseif(count($value) == 1){
						$value	= reset($value);
					}
				}

				$rendered[$key]	= [$field['title'], $value];
			}
		}

		return $rendered;
	}

	public function get_module($args=[]){	// compact
		return $this->parse($args);
	}

	public static function create_instance(...$args){
		$type	= get_post_meta($args[0], '_module_type', true);
		$object	= WPJAM_Module_Type::get($type);
		$model	= $object ? $object->model : null;

		return $model ? new $model(...$args) : null;
	}

	protected static function sanitize_data($data, $id=0){
		$data['meta_input']	= wpjam_array(wpjam_pull($data, ['type', 'setting']), fn($k)=> '_module_'.$k);

		return parent::sanitize_data($data, $id);
	}

	public static function get($id){
		$object	= self::get_instance($id);

		return $object ? $object->data : [];
	}

	public static function delete($id, $force_delete=true){
		$result	= parent::delete($id, $force_delete);

		return is_wp_error($result) && $result->get_error_code() == 'shared_module' ? self::page('remove_module', $id) : $result;
	}

	public static function bulk_delete($ids){
		try{
			wp_defer_term_counting(true);

			wpjam_map($ids, fn($id)=> wpjam_try([self::class, 'delete'], $id));

			return true;
		}catch(Exception $e){
			return wpjam_catch($e); 
		}finally{
			wp_defer_term_counting(false);
		}
	}

	public static function get_actions(){
		$page	= self::page();

		return [
			'add'		=>['title'=>'新建',		'page_title'=>'新建模块',	'next'=>'create'],
			'create'	=>['title'=>'新建',		'page_title'=>'新建模块',	'dismiss'=>true,	'response'=>'add',	'callback'=>[$page, 'add_module']],
		]+(wpjam_get_data_parameter('mode') == 'sort' ? [] : [
			'edit'		=>['title'=>'编辑',		'page_title'=>'编辑模块'],
			'publish'	=>['title'=>'发布',		'direct'=>true,	'confirm'=>true,	'show_if'=>['status', '=', 'draft']],
			'unpublish'	=>['title'=>'撤回',		'direct'=>true,	'confirm'=>true,	'show_if'=>['status', '=', 'publish']],
			'delete'	=>['title'=>'删除',		'direct'=>true,	'confirm'=>true,	'bulk'=>true,	'order'=>9,	'show_if'=>[self::class, 'show_if']],
		]+(self::page('is_shared') ? [] : [
			'copy_to'	=>['title'=>'复制到',		'page_title'=>'复制模块到...',	'show_if'=>['status', '=', 'publish']],
			'empty'		=>['title'=>'清空所有',	'page_title'=>'清空模块',	'direct'=>true,	'confirm'=>true, 'overall'=>true,	'callback'=> fn()=> self::bulk_delete($page->get_module_ids())],
			'add_shared'=>['title'=>'添加共用',	'page_title'=>'添加共用模块',	'class'=>'button-primary',	'response'=>'add',	'dismiss'=>true,	'overall'=>true,	'callback'=> [$page, 'add_shared_module']]
		]))+self::get_item_actions();
	}

	public static function get_fields($action_key='', $id=0){
		$page	= self::page();
		$object	= $id ? self::get_instance($id) : null;

		if($action_key == ''){
			$sort	= wpjam_get_data_parameter('mode') == 'sort';

			return [
				'sort'		=> ['title'=>'排序',	'show_admin_column'=>$sort],
				'title'		=> ['title'=>'标题',	'show_admin_column'=>true,	'required'],
				'type'		=> ['title'=>'类型',	'show_admin_column'=>true,	'filterable'=>true,	'options'=>WPJAM_Module_Type::get_active('title')],
				'status'	=> ['title'=>'状态',	'show_admin_column'=>false,	'filterable'=>true,	'options'=>['publish'=>'已发布', 'draft'=>'待发布']],
				'setting'	=> ['title'=>'设置',	'show_admin_column'=>!$sort],
				'content'	=> ['title'=>'内容',	'show_admin_column'=>!$sort],
			];
		}elseif($action_key == 'add'){
			return [
				'view'	=> '<strong>请选择模块类型:</strong>',
				'type'	=> ['type'=>'radio',	'options'=>WPJAM_Module_Type::get_options(),	'required'],
			];
		}elseif($action_key == 'add_shared'){
			return ['shared'	=> wpjam_get_post_id_field('module', ['title'=>'', 'required',	'query_args'=>[
				'module_page_id'	=> WPJAM_Module_Page::get_shared(), 
				'post_status'		=> 'publish',
				'exclude'			=> $page->get_module_ids()
			]])];
		}elseif($action_key == 'copy_to'){
			$options	= wpjam_except(WPJAM_Module_Page::get_options(), $page->id);
			$options	+= $object->shared ? [] : [(WPJAM_Module_Page::get_shared())=>'共用模块'];

			return ['to_page' => ['title'=>'复制到', 'type'=>'select', 'options'=>$options]];
		}

		$type	= $object ? $object->type : wpjam_get_data_parameter('type');
		$fields	= [WPJAM_Module_Type::get($type), 'get_fields']($action_key, $id);

		$action_key == 'create' && $page->is_modules_over($type);

		if(in_array($action_key, ['create', 'edit'])){
			$fields	= $page->border_radius ? $fields : wpjam_except($fields, 'setting.fields.border_radius');
		}elseif(in_array($action_key, ['add_item', 'edit_item'])){
			$fields	= wpjam_map($fields, fn($v)=> isset($v['name']) ? wpjam_set($v, 'name', preg_replace('/item\[(.*)\]/', '$1', $v['name'])) : $v);
		}

		return ($id && $object->shared ? ['tips'=>'<span class="red">对共用模块的修改将会应用到所有使用该模块的页面中</span>'] : [])+$fields;
	}

	public static function get_views(){
		$ids	= self::page('get_module_ids');
		$posts	= self::get_by_ids($ids);

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

		$counts	= array_count_values(array_column(array_filter($posts), 'post_status'));
		$views	= ['all'=>['filter'=>['status'=>null, 'mode'=>null], 'label'=>'全部', 'count'=>array_sum($counts)]];
		$views	+= wpjam_map(['publish'=>'已发布', 'draft'=>'待发布'], fn($v, $k)=> isset($counts[$k]) ? ['filter'=>['status'=>$k], 'label'=>$v, 'count'=>$counts[$k]] : null);

		$views	+= ['sort'=>['filter'=>['mode'=>'sort'],	'label'=>'快捷排序']];

		return $views;
	}

	public static function query_items($args){
		return self::page('parse_modules', $args, false);
	}

	public static function render_item($item){
		$object	= self::get_instance($item['id']);

		if($object){
			$item		= array_merge($item, $object->render());
			$setting	= $object->setting;

			if(is_array($item['setting'])){
				foreach($item['setting'] as &$_item){
					if(is_array($_item)){
						$title	= $_item[0].':';
						$length	= mb_strlen($title);

						if($length < 5){
							$title	.= str_repeat('&emsp;', 5-$length);
						}

						$value	= $_item[1];
						$value	= is_array($value) ? implode(', ', $value) : $value;

						$_item	= $title.wpjam_tag('strong', [], $value);
					}
				}

				$item['setting']	= implode(wpjam_tag('br'), $item['setting']);
			}else{
				$margin	= $setting['margin'] ?? 0;

				$item['setting']	.= $item['setting'] ? wpjam_tag('br') : '';
				$item['setting']	.= '下边距:&emsp;'.wpjam_tag('strong', [], $margin);
			}

			if($bg_color	= wpjam_pull($setting, 'background_color')){
				$item['content']	= is_array($item['content']) ? $item['content'] : ['text'=>$item['content']];
				$item['content']	+= ['wrap'=>wpjam_tag('div', ['style'=>'width:calc(100% - 20px); float:left; padding:10px; background:'.$bg_color.';'])];
			}

			if(wpjam_get_data_parameter('mode') == 'sort'){
				unset($item['row_actions']['id']);
			}
		}

		return $item;
	}

	public static function get_list_table(){
		wpjam_map(['script', 'style'], fn($t)=> wpjam_map(WPJAM_Module_Type::get_active($t), fn($v)=> wpjam_admin($t, $v)));

		wpjam_admin('style', [
			'td.column-title{min-width:150px;}',
			'td.column-type{min-width:120px;}',
			'td.column-setting{width:240px;}',
			'td.column-sort, th.column-sort{width:6em;}',
			'td.column-sort .row-actions{left:inherit;}',
			'td.column-title span.title-display{font-size:smaller; color:#999;}',
			'#type_options label{width:120px;}'
		]);

		$page	= self::page();

		return [
			'title'		=> self::page('is_shared') ? $page->name : '「'.$page->name.'」页面模块',
			'plural'	=> 'modules',
			'singular'	=> 'module',
			'model'		=> self::class,
			'sortable'	=> ['callback'=>[$page, 'move_module']],
		];
	}

	public static function page($method='', ...$args){
		$object	= wpjam_get_term_object(wpjam_get_data_parameter('page_id'));

		return $method ? $object->$method(...$args) : $object;
	}

	public static function get_path_fields($item_key='item'){
		return wpjam_get_module_path_fields($item_key);	// 兼容
	}
}

class WPJAM_Modules{
	private $page_id;

	public function __construct($page_id){
		$this->page_id	= (int)$page_id;
	}

	public function get_ids($sorted=false){
		$ids	= wpjam_get_posts([
			'post_type'			=> 'module',
			'post_status'		=> 'any',
			'posts_per_page'	=> -1,
			'fields'			=> 'ids',
			'module_page_id'	=> $this->page_id
		]);

		if($sorted){
			$orders	= get_term_meta($this->page_id, 'orders', true) ?: [];
			$orders	= array_intersect(array_unique($orders), $ids);
			$ids	= array_merge(array_diff($ids, $orders), $orders);
		}

		return array_values($ids);
	}

	public function is_over($type=''){
		if($this->page_id != WPJAM_Module_Page::get_shared()){
			$ids	= $this->get_ids();
			$max	= wpjam_get_post_type_setting('module', 'max_modules');
			$max && count($ids) >= $max && wpjam_throw('over_max_modules', '最多'.$max.'个模块');

			if($type && $type != 'shared'){
				$object	= WPJAM_Module_Type::get($type);
				$object && $object->is_over(count(array_filter($ids, fn($id)=> get_post_meta($id, '_module_type', true) == $type)));
			}
		}

		return false;
	}

	public function add($data){
		return $this->is_over($data['type']) ?: wpjam_try(['WPJAM_Module', 'insert'], $data+[
			'status'	=> 'draft',
			'tax_input'	=> ['module_page'=>[$this->page_id]]
		]);
	}

	public function add_shared($shared){
		$shared	= is_array($shared) ? wpjam_get($shared, 'shared') : $shared; 
		$object	= $shared ? wpjam_get_post_object($shared) : null;

		(!$object || !$object->shared) && wpjam_throw('invalid_shared_module', '无效的共用模块');

		$this->is_over($object->type) ?: wpjam_try('wp_set_post_terms', $shared, [$this->page_id], 'module_page', true);

		return $shared;
	}

	public function remove($id){
		$page_ids	= wp_get_post_terms($id, 'module_page', ['fields'=>'ids']);
		$page_ids	= array_diff($page_ids, [$this->page_id]);

		return wp_set_post_terms($id, $page_ids, 'module_page');
	}

	public function move($id, $data){
		$ids	= $this->get_ids(true);

		if(isset($data['module'])){
			array_diff($data['module'], $ids) && wp_die('无效的ID');

			$ids	= $data['module'];
		}else{
			$ids	= wpjam_move($ids, $id, $data);
		}

		return is_wp_error($ids) ? $ids : update_term_meta($this->page_id, 'orders', $ids);
	}

	public function import($data){
		try{
			wp_defer_term_counting(true);

			foreach(array_reverse($data) as $item){
				$id	= $this->add(wp_array_slice_assoc($item, ['type', 'setting', 'title', 'excerpt', 'content', 'status']));

				if(is_wp_error($id)){
					return $id;
				}
			}

			remove_filter('sanitize_title', '__return_empty_string');
		}finally{
			wp_defer_term_counting(false);
		}

		return true;
	}

	public function parse($args=[], $parsed=true){
		if($args === false){
			$args	= [];
			$parsed	= false;
		}

		$args		= wp_parse_args($args, ['type'=>'', 'status'=>($parsed ? 'publish' : '')]);
		$filter		= wpjam_filter($args, fn($v, $k)=> in_array($k, ['type', 'status']) && $v && $v != 'any');
		$modules	= [];

		foreach(WPJAM_Module::update_caches($this->get_ids(true)) as $id => $post){
			$object	= WPJAM_Module::get_instance($id);

			if(!$object || !wpjam_matches($object, $filter)){
				continue;
			}

			if($parsed){
				$module	= $object->parse($args);

				if($module && !empty($module['content'])){
					$modules[]	= $module;
				}
			}else{
				$modules[]	= $object->data;
			}
		}

		return $parsed ? array_reduce(['query', 'parse'], fn($c, $k)=> wpjam_map($c, fn($m)=> ($cb = wpjam_pull($m, $k)) ? $cb($m, $args) : $m), $modules) : $modules;
	}
}

function wpjam_register_module_type($key, $args){
	return WPJAM_Module_Type::register($key, $args);
}

function wpjam_unregister_module_type($key){
	WPJAM_Module_Type::unregister($key);
}

function wpjam_set_max_modules($value=0, $type=''){
	if($type){
		$object	= WPJAM_Module_Type::get($type);

		$object && ($object->max_modules = $value);
	}else{
		wpjam_update_post_type_setting('module', 'max_modules', $value);
	}
}

wpjam_register_post_type('module', [
	'label'		=> '模块',
	'supports'	=> ['title'],
	'public'	=> false,
	'show_ui'	=> false,
	'rewrite'	=> false,
	'query_var'	=> false,
	'model'		=> 'WPJAM_Module',
]);