public function update_db( $args, $assoc_args ) {
		global $wpdb, $wp_db_version, $wp_current_db_version;

		$network = Utils\get_flag_value( $assoc_args, 'network' );
		if ( $network && ! is_multisite() ) {
			WP_CLI::error( 'This is not a multisite installation.' );
		}

		$dry_run = Utils\get_flag_value( $assoc_args, 'dry-run' );
		if ( $dry_run ) {
			WP_CLI::log( 'Performing a dry run, with no database modification.' );
		}

		if ( $network ) {
			$iterator_args = [
				'table' => $wpdb->blogs,
				'where' => [
					'spam'     => 0,
					'deleted'  => 0,
					'archived' => 0,
				],
			];
			$it            = new TableIterator( $iterator_args );
			$success       = 0;
			$total         = 0;
			$site_ids      = [];
			foreach ( $it as $blog ) {
				$total++;
				$site_ids[] = $blog->site_id;
				$url        = $blog->domain . $blog->path;
				$cmd        = "--url={$url} core update-db";
				if ( $dry_run ) {
					$cmd .= ' --dry-run';
				}
				$process = WP_CLI::runcommand( $cmd, [ 'return' => 'all' ] );
				if ( 0 === (int) $process->return_code ) {
					// See if we can parse the stdout
					if ( preg_match( '#Success: (.+)#', $process->stdout, $matches ) ) {
						$message = rtrim( $matches[1], '.' );
						$message = "{$message} on {$url}";
					} else {
						$message = "Database upgraded successfully on {$url}";
					}
					WP_CLI::log( $message );
					$success++;
				} else {
					WP_CLI::warning( "Database failed to upgrade on {$url}" );
				}
			}
			if ( ! $dry_run && $total && $success === $total ) {
				foreach ( array_unique( $site_ids ) as $site_id ) {
					update_metadata( 'site', $site_id, 'wpmu_upgrade_site', $wp_db_version );
				}
			}
			WP_CLI::success( "WordPress database upgraded on {$success}/{$total} sites." );
		} else {
			require_once ABSPATH . 'wp-admin/includes/upgrade.php';
			// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Replacing WP Core behavior is the goal here.
			$wp_current_db_version = (int) __get_option( 'db_version' );
			if ( $wp_db_version !== $wp_current_db_version ) {
				if ( $dry_run ) {
					WP_CLI::success( "WordPress database will be upgraded from db version {$wp_current_db_version} to {$wp_db_version}." );
				} else {
					// WP upgrade isn't too fussy about generating MySQL warnings such as "Duplicate key name" during an upgrade so suppress.
					$wpdb->suppress_errors();

					// WP upgrade expects `$_SERVER['HTTP_HOST']` to be set in `wp_guess_url()`, otherwise get PHP notice.
					if ( ! isset( $_SERVER['HTTP_HOST'] ) ) {
						$_SERVER['HTTP_HOST'] = 'http://example.com';
					}

					wp_upgrade();

					WP_CLI::success( "WordPress database upgraded successfully from db version {$wp_current_db_version} to {$wp_db_version}." );
				}
			} else {
				WP_CLI::success( "WordPress database already at latest db version {$wp_db_version}." );
			}
		}
	}

Runs the WordPress database update procedure.

[--network]
: Update databases for all sites on a network

[--dry-run]
: Compare database versions without performing the update.

## EXAMPLES

# Update the WordPress database
$ wp core update-db
Success: WordPress database upgraded successfully from db version 36686 to 35700.

# Update databases for all sites on a network
$ wp core update-db --network
WordPress database upgraded successfully from db version 35700 to 29630 on example.com/
Success: WordPress database upgraded on 123/123 sites

@subcommand update-db

Runs the WordPress database update procedure .


private function get_download_url( $version, $locale = 'en_US', $file_type = 'zip' ) {

		if ( 'nightly' === $version ) {
			if ( 'zip' === $file_type ) {
				return 'https://wordpress.org/nightly-builds/wordpress-latest.zip';
			} else {
				WP_CLI::error( 'Nightly builds are only available in .zip format.' );
			}
		}

		$locale_subdomain = 'en_US' === $locale ? '' : substr( $locale, 0, 2 ) . '.';
		$locale_suffix    = 'en_US' === $locale ? '' : "-{$locale}";

		return "https://{$locale_subdomain}wordpress.org/wordpress-{$version}{$locale_suffix}.{$file_type}";
	}

Gets download url based on version, locale and desired file type.

@param $version
@param string $locale
@param string $file_type
@return string

Gets download url based on version locale and desired file type .


private function get_updates( $assoc_args ) {
		wp_version_check();
		$from_api = get_site_transient( 'update_core' );
		if ( ! $from_api ) {
			return [];
		}

		$compare_version = str_replace( '-src', '', $GLOBALS['wp_version'] );

		$updates = [
			'major' => false,
			'minor' => false,
		];
		foreach ( $from_api->updates as $offer ) {

			$update_type = Utils\get_named_sem_ver( $offer->version, $compare_version );
			if ( ! $update_type ) {
				continue;
			}

			// WordPress follow its own versioning which is roughly equivalent to semver
			if ( 'minor' === $update_type ) {
				$update_type = 'major';
			} elseif ( 'patch' === $update_type ) {
				$update_type = 'minor';
			}

			if ( ! empty( $updates[ $update_type ] ) && ! Comparator::greaterThan( $offer->version, $updates[ $update_type ]['version'] ) ) {
				continue;
			}

			$updates[ $update_type ] = [
				'version'     => $offer->version,
				'update_type' => $update_type,
				'package_url' => ! empty( $offer->packages->partial ) ? $offer->packages->partial : $offer->packages->full,
			];
		}

		foreach ( $updates as $type => $value ) {
			if ( empty( $value ) ) {
				unset( $updates[ $type ] );
			}
		}

		foreach ( [ 'major', 'minor' ] as $type ) {
			if ( true === Utils\get_flag_value( $assoc_args, $type ) ) {
				return ! empty( $updates[ $type ] )
					? [ $updates[ $type ] ]
					: false;
			}
		}
		return array_values( $updates );
	}


public function download_package( $package, $check_signatures = true ) {

		/**
		 * Filter whether to return the package.
		 *
		 * @since 3.7.0
		 *
		 * @param bool    $reply   Whether to bail without returning the package. Default is false.
		 * @param string  $package The package file name.
		 * @param object  $this    The WP_Upgrader instance.
		 */
		$reply = apply_filters(
			// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Override existing hook from Core.
			'upgrader_pre_download',
			false,
			$package,
			$this
		);
		if ( false !== $reply ) {
			return $reply;
		}

		// Check if package is a local or remote file. Bail if it's local.
		if ( ! preg_match( '!^(http|https|ftp)://!i', $package ) && file_exists( $package ) ) {
			return $package;
		}

		if ( empty( $package ) ) {
			return new WP_Error( 'no_package', $this->strings['no_package'] );
		}

		$filename  = pathinfo( $package, PATHINFO_FILENAME );
		$extension = pathinfo( $package, PATHINFO_EXTENSION );

		$temp = Utils\get_temp_dir() . uniqid( 'wp_' ) . ".{$extension}";
		register_shutdown_function(
			function () use ( $temp ) {
				if ( file_exists( $temp ) ) {
					unlink( $temp );
				}
			}
		);

		$cache      = WP_CLI::get_cache();
		$update     = $GLOBALS['wpcli_core_update_obj'];
		$cache_key  = "core/{$filename}-{$update->locale}.{$extension}";
		$cache_file = $cache->has( $cache_key );

		if ( $cache_file && false === stripos( $package, 'https://wordpress.org/nightly-builds/' )
			&& false === stripos( $package, 'http://wordpress.org/nightly-builds/' ) ) {
			WP_CLI::log( "Using cached file '{$cache_file}'..." );
			copy( $cache_file, $temp );
			return $temp;
		}

		/*
		 * Download to a temporary file because piping from cURL to tar is flaky
		 * on MinGW (and probably in other environments too).
		 */
		$headers = [ 'Accept' => 'application/json' ];
		$options = [
			'timeout'       => 600,  // 10 minutes ought to be enough for everybody.
			'filename'      => $temp,
			'halt_on_error' => false,
		];

		$this->skin->feedback( 'downloading_package', $package );

		/** @var Requests_Response|null $req */
		try {
			$response = Utils\http_request( 'GET', $package, null, $headers, $options );
		} catch ( Exception $e ) {
			return new WP_Error( 'download_failed', $e->getMessage() );
		}

		if ( ! is_null( $response ) && 200 !== (int) $response->status_code ) {
			return new WP_Error( 'download_failed', $this->strings['download_failed'] );
		}

		if ( false === stripos( $package, 'https://wordpress.org/nightly-builds/' ) ) {
			$cache->import( $cache_key, $temp );
		}

		return $temp;
	}

Caches the download, and uses cached if available.

@access public

@param string $package          The URI of the package. If this is the full path to an
existing local file, it will be returned untouched.
@param bool   $check_signatures Whether to validate file signatures. Default true.
@return string|WP_Error The full path to the downloaded package file, or a WP_Error object.

Caches the download and uses cached if available .


public function upgrade( $current, $args = [] ) {
		set_error_handler( [ __CLASS__, 'error_handler' ], E_USER_WARNING | E_USER_NOTICE );

		$result = parent::upgrade( $current, $args );

		restore_error_handler();

		return $result;
	}

public function upgrade ( $ current , $ args = [ ] ) { set_error_handler ( [ __CLASS__ , 'error_handler' ] , E_USER_WARNING | E_USER_NOTICE ) ; $ result = parent :: upgrade ( $ current , $ args ) ; restore_error_handler ( ) ; return $ result ; }

Upgrade WordPress core.

@access public

@global WP_Filesystem_Base $wp_filesystem Subclass
@global callable           $_wp_filesystem_direct_method

@param object $current Response object for whether WordPress is current.
@param array  $args {
Optional. Arguments for upgrading WordPress core. Default empty array.

@type bool $pre_check_md5    Whether to check the file checksums before
attempting the upgrade. Default true.
@type bool $attempt_rollback Whether to attempt to rollback the chances if
there is a problem. Default false.
@type bool $do_rollback      Whether to perform this "upgrade" as a rollback.
Default false.
}
@return null|false|WP_Error False or WP_Error on failure, null on success.


protected function build_table( array $arr, $child = false ) {
		$html = '

';
		$allow_html = $this->get_setting( 'allow_html' );

		if ( $child ) {
			$html .= '
';

			foreach ( $arr[0] as $key => $value ) {
				if ( $allow_html ) {
					$key = html_entity_decode( $key );
				}

				$html .= sprintf( '%s	', $key );
			}

			$html .= '
';
		}

		foreach ( $arr as $key => $value ) {
			$html .= '
';

			foreach ( $value as $key2 => $value2 ) {
				if ( is_array( $value2 ) ) {
					$value2 = $this->build_table( $value2, true );
				}

				$value2 = papi_convert_to_string( $value2 );

				if ( $allow_html ) {
					$value2 = html_entity_decode( $value2 );
				}

				$html .= sprintf( '%s	', $value2 );
			}

			$html .= '
';
		}

		return $html . '

';
	}

Build table from array data.

@param  array $arr
@param  bool  $child

@return stringe


public function admin_head() {
		if ( ! $this->box->frame ) {
			echo sprintf(
				'<style type="text/css">label[for="%s-hide"],#%s>h2,#%s>button{display:none !important}</style>',
				esc_attr( $this->box->id ),
				esc_attr( $this->box->id ),
				esc_attr( $this->box->id )
			);
		}
	}

Add custom css for hiding boxes with no frame in screen options.

@return string

Add custom css for hiding boxes with no frame in screen options .


public function move_meta_box_after_title() {
		global $post, $wp_meta_boxes;
		do_meta_boxes( get_current_screen(), $this->box->context, $post );
		unset( $wp_meta_boxes[get_post_type( $post )][$this->box->context] );
	}

public function move_meta_box_after_title ( ) { global $ post , $ wp_meta_boxes ; do_meta_boxes ( get_current_screen ( ) , $ this -> box -> context , $ post ) ; unset ( $ wp_meta_boxes [ get_post_type ( $ post ) ] [ $ this -> box -> context ] ) ; }


protected function get_post_type() {
		if ( papi_get_meta_type() === 'post' ) {
			if ( $post_id = papi_get_post_id() ) {
				return get_post_type( $post_id );
			}

			if ( $post_type = papi_get_post_type() ) {
				return $post_type;
			}
		}

		return $this->box->id;
	}

protected function get_post_type ( ) { if ( papi_get_meta_type ( ) === 'post' ) { if ( $ post_id = papi_get_post_id ( ) ) { return get_post_type ( $ post_id ) ; } if ( $ post_type = papi_get_post_type ( ) ) { return $ post_type ; } } return $ this -> box -> id ; }


protected function get_title() {
		$title = $this->box->title;

		if ( $this->box->get_option( 'required' ) ) {
			$title .= papi_property_required_html(
				$this->box->properties[0],
				true
			);
		}

		return $title;
	}

protected function get_title ( ) { $ title = $ this -> box -> title ; if ( $ this -> box -> get_option ( 'required' ) ) { $ title .= papi_property_required_html ( $ this -> box -> properties [ 0 ] , true ) ; } return $ title ; }


public function render_meta_box( $post, array $args ) {
		if ( ! isset( $args['args'] ) ) {
			return;
		}

		// Do a last check before all properties is rendered.
		$args['args'] = array_filter( $args['args'], 'papi_is_property' );

		// Inherit options from the box.
		foreach ( $args['args'] as $index => $property ) {
			if ( $property->layout === 'horizontal' ) {
				$args['args'][$index]->layout = $this->box->layout;
			}
		}

		// Render the properties.
		papi_render_properties( papi_sort_order( array_reverse( $args['args'] ) ) );
	}

Render the meta box

@param array $post
@param array $args


public function setup_meta_box() {
		$properties = $this->box->properties;

		// Check all properties and remove them that can't be rendered.
		foreach ( $properties as $index => $property ) {
			if ( $property instanceof Papi_Property && ! $property->can_render() ) {
				unset( $properties[$index] );
			}
		}

		// Bail if properties array is empty,
		// no need to render a empty meta box.
		if ( empty( $properties ) ) {
			return;
		}

		add_meta_box(
			$this->box->id,
			$this->get_title(),
			[$this, 'render_meta_box'],
			$this->get_post_type(),
			$this->box->context,
			$this->box->priority,
			$properties
		);
	}


protected function get_meta() {
		$method = 'meta';

		if ( ! method_exists( $this, $method ) ) {
			return [];
		}

		$child_meta = call_user_func( [$this, $method] );
		$child_meta = is_array( $child_meta ) ? $child_meta : [];

		$parent_class  = get_parent_class( $this );
		$parent_exists = method_exists( $parent_class, $method );
		$parent_meta   = [];

		while ( $parent_exists ) {
			$rc = new ReflectionClass( $parent_class );

			// Bail if not instantiable.
			if ( ! $rc->isInstantiable() ) {
				break;
			}

			$parent        = $rc->newInstance();
			$output        = call_user_func( [$parent, $method] );
			$output        = is_array( $output ) ? $output : [];
			$parent_meta   = array_merge( $parent_meta, $output );
			$parent_class  = get_parent_class( $parent_class );
			$parent_exists = method_exists( $parent_class, $method );
		}

		return array_merge( $parent_meta, $child_meta );
	}

protected
function
get_meta
(
)
{
$
method
=
'meta'
;
if
(
!
method_exists
(
$
this
,
$
method
)
)
{
return
[
]
;
}
$
child_meta
=
call_user_func
(
[
$
this
,
$
method
]
)
;
$
child_meta
=
is_array
(
$
child_meta
)
$
child_meta
:
[
]
;
$
parent_class
=
get_parent_class
(
$
this
)
;
$
parent_exists
=
method_exists
(
$
parent_class
,
$
method
)
;
$
parent_meta
=
[
]
;
while
(
$
parent_exists
)
{
$
rc
=
new
ReflectionClass
(
$
parent_class
)
;
// Bail if not instantiable.
if
(
!
$
rc
->
isInstantiable
(
)
)
{
break
;
}
$
parent
=
$
rc
->
newInstance
(
)
;
$
output
=
call_user_func
(
[
$
parent
,
$
method
]
)
;
$
output
=
is_array
(
$
output
)
$
output
:
[
]
;
$
parent_meta
=
array_merge
(
$
parent_meta
,
$
output
)
;
$
parent_class
=
get_parent_class
(
$
parent_class
)
;
$
parent_exists
=
method_exists
(
$
parent_class
,
$
method
)
;
}
return
array_merge
(
$
parent_meta
,
$
child_meta
)
;
}

Get meta data from type class and merge
with the parent meta data.

@return array

Get meta data from type class and merge with the parent meta data .


protected function setup_file( $file_path ) {
		$this->_file_path  = $file_path;
		$this->_class_name = papi_get_class_name( $this->_file_path );
	}

protected function setup_file ( $ file_path ) { $ this -> _file_path = $ file_path ; $ this -> _class_name = papi_get_class_name ( $ this -> _file_path ) ; }

Load the file and setup file path, file name and class name properties.

@param string $file_path

Load the file and setup file path file name and class name properties .


protected function setup_meta_data() {
		foreach ( $this->get_meta() as $key => $value ) {
			if ( substr( $key, 0, 1 ) === '_' ) {
				continue;
			}

			$this->$key = papi_esc_html( $value );
		}

		if ( $this->sort_order === 1000 ) {
			$this->sort_order = papi_filter_settings_sort_order();
		}
	}

protected function setup_meta_data ( ) { foreach ( $ this -> get_meta ( ) as $ key => $ value ) { if ( substr ( $ key , 0 , 1 ) === '_' ) { continue ; } $ this -> $ key = papi_esc_html ( $ value ) ; } if ( $ this -> sort_order === 1000 ) { $ this -> sort_order = papi_filter_settings_sort_order ( ) ; } }


public function disabled() {
		// Return true if site id isn't zero and don't match the current site id.
		if ( $this->site_id !== 0 && $this->site_id !== get_current_blog_id() ) {
			return true;
		}

		// If the post type don't match the current one
		// the property should not be rendered.
		if ( papi_get_meta_type() === 'post' && ! empty( $this->post_type ) && $this->post_type !== papi_get_post_type() ) {
			return true;
		}

		return $this->disabled;
	}

Determine if the property is disabled or not.

@return bool

Determine if the property is disabled or not .


public static function factory() {
		if ( count( func_get_args() ) === 0 ) {
			return new static;
		} else {
			$type = func_get_arg( 0 );
		}

		if ( is_array( $type ) ) {
			$type = (object) $type;
		}

		if ( ! is_string( $type ) && ! is_object( $type ) ) {
			return;
		}

		if ( is_object( $type ) && ! isset( $type->type ) ) {
			$property = new static;
			$property->set_options( $type );

			return $property;
		}

		if ( is_subclass_of( $type, __CLASS__ ) ) {
			return $type;
		}

		$options = null;

		if ( is_object( $type ) ) {
			if ( ! isset( $type->type ) || ! is_string( $type->type ) ) {
				return;
			}

			$options = $type;
			$type    = $type->type;
		}

		// Old types, 'PropertyString' => 'String'.
		$type = preg_replace( '/^Property/', '', $type );

		if ( empty( $type ) ) {
			return;
		}

		$class_name = papi_get_property_class_name( $type );

		if ( ! class_exists( $class_name ) || ! is_subclass_of( $class_name, __CLASS__ ) ) {
			return;
		}

		if ( ! papi()->exists( $class_name ) ) {
			papi()->bind( $class_name, new $class_name() );
		}

		$class = papi()->make( $class_name );

		// @codeCoverageIgnoreStart
		if ( ! is_object( $class ) || $class instanceof Papi_Core_Property === false ) {
			$class = new $class_name();
			papi()->bind( $class_name, $class );
		}
		// @codeCoverageIgnoreEnd

		$property = clone $class;

		if ( is_object( $options ) ) {
			$property->set_options( (array) $options );
		}

		return $property;
	}

Create a new instance of the given type or a
empty core property if no type is given.

@return null|object

Create a new instance of the given type or a empty core property if no type is given .


public function get_child_property( $slug, array $items = [] ) {
		$items = empty( $items ) ? $this->get_child_properties() : $items;

		foreach ( $items as $property ) {
			if ( is_array( $property ) && isset( $property['items'] ) ) {
				$property = $this->get_child_property( $slug, $property['items'] );
			}

			$property = papi_get_property_type( $property );

			if ( papi_is_property( $property ) && $property->match_slug( $slug ) ) {
				return $property;
			}
		}
	}

public function get_child_property ( $ slug , array $ items = [ ] ) { $ items = empty ( $ items ) ? $ this -> get_child_properties ( ) : $ items ; foreach ( $ items as $ property ) { if ( is_array ( $ property ) && isset ( $ property [ 'items' ] ) ) { $ property = $ this -> get_child_property ( $ slug , $ property [ 'items' ] ) ; } $ property = papi_get_property_type ( $ property ) ; if ( papi_is_property ( $ property ) && $ property -> match_slug ( $ slug ) ) { return $ property ; } } }

Get child property.

@param  string $slug
@param  array  $items

@return Papi_Core_Property|null


public function get_option( $key, $default = null ) {
		$value = $default;

		if ( isset( $this->options->$key ) ) {
			$value = $this->options->$key;
		}

		if ( papi_is_empty( $value ) && isset( $this->default_options[$key] ) ) {
			$value = $this->default_options[$key];
		}

		if ( $key === 'settings' && is_array( $value ) ) {
			$value = (object) $value;
		}

		return $value;
	}

public function get_option ( $ key , $ default = null ) { $ value = $ default ; if ( isset ( $ this -> options -> $ key ) ) { $ value = $ this -> options -> $ key ; } if ( papi_is_empty ( $ value ) && isset ( $ this -> default_options [ $ key ] ) ) { $ value = $ this -> default_options [ $ key ] ; } if ( $ key === 'settings' && is_array ( $ value ) ) { $ value = ( object ) $ value ; } return $ value ; }

Get option value.

@param  string $key
@param  mixed  $default

@return mixed


public function get_post_id() {
		if ( ! papi_is_empty( $this->post_id ) ) {
			return $this->post_id;
		}

		if ( $this->store instanceof Papi_Core_Meta_Store ) {
			return $this->store->id;
		}

		return papi_get_post_id();
	}

public function get_post_id ( ) { if ( ! papi_is_empty ( $ this -> post_id ) ) { return $ this -> post_id ; } if ( $ this -> store instanceof Papi_Core_Meta_Store ) { return $ this -> store -> id ; } return papi_get_post_id ( ) ; }


public function html_id( $suffix = '', $row = null ) {
		if ( is_array( $suffix ) || is_object( $suffix ) ) {
			return papi_f( $this->html_name( $suffix, $row ) );
		} else {
			$suffix = empty( $suffix ) || ! is_string( $suffix ) ? '' : '_' . $suffix;
			$suffix = papi_underscorify( papi_slugify( $suffix ) );
		}

		$name = $this->html_name();
		$len  = strlen( $name );

		if ( isset( $name[$len - 1] ) && $name[$len - 1] === ']' ) {
			return papi_f( sprintf( '%s%s]', substr( $name, 0, $len - 1 ), $suffix ) );
		}

		return papi_f( sprintf( '%s%s', $this->html_name(), $suffix ) );
	}

public function html_id ( $ suffix = '' , $ row = null ) { if ( is_array ( $ suffix ) || is_object ( $ suffix ) ) { return papi_f ( $ this -> html_name ( $ suffix , $ row ) ) ; } else { $ suffix = empty ( $ suffix ) || ! is_string ( $ suffix ) ? '' : '_' . $ suffix ; $ suffix = papi_underscorify ( papi_slugify ( $ suffix ) ) ; } $ name = $ this -> html_name ( ) ; $ len = strlen ( $ name ) ; if ( isset ( $ name [ $ len - 1 ] ) && $ name [ $ len - 1 ] === ']' ) { return papi_f ( sprintf ( '%s%s]' , substr ( $ name , 0 , $ len - 1 ) , $ suffix ) ) ; } return papi_f ( sprintf ( '%s%s' , $ this -> html_name ( ) , $ suffix ) ) ; }

Get the html id attribute value.

@param  object|string $suffix
@param  int           $row

@return string


public function html_name( $sub_property = null, $row = null ) {
		$base_slug = $this->slug;

		if ( is_null( $sub_property ) ) {
			return $base_slug;
		}

		if ( is_numeric( $row ) ) {
			$base_slug = sprintf( '%s[%d]', $base_slug, intval( $row ) );
		}

		if ( ! papi_is_property( $sub_property ) ) {
			if ( is_array( $sub_property ) || is_object( $sub_property ) ) {
				$sub_property = self::factory( $sub_property );
			} else {
				return $base_slug;
			}
		}

		return sprintf( '%s[%s]', $base_slug, unpapify( $sub_property->get_slug() ) );
	}

public function html_name ( $ sub_property = null , $ row = null ) { $ base_slug = $ this -> slug ; if ( is_null ( $ sub_property ) ) { return $ base_slug ; } if ( is_numeric ( $ row ) ) { $ base_slug = sprintf ( '%s[%d]' , $ base_slug , intval ( $ row ) ) ; } if ( ! papi_is_property ( $ sub_property ) ) { if ( is_array ( $ sub_property ) || is_object ( $ sub_property ) ) { $ sub_property = self :: factory ( $ sub_property ) ; } else { return $ base_slug ; } } return sprintf ( '%s[%s]' , $ base_slug , unpapify ( $ sub_property -> get_slug ( ) ) ) ; }

Get html name for property with or without sub property and row number.

@param  array|object $sub_property
@param  int          $row

@return string

Get html name for property with or without sub property and row number .


public function match_slug( $slug ) {
		if ( ! is_string( $slug ) ) {
			return false;
		}

		return $this->get_slug( ! preg_match( '/^papi\_/', $slug ) ) === $slug;
	}

public function match_slug ( $ slug ) { if ( ! is_string ( $ slug ) ) { return false ; } return $ this -> get_slug ( ! preg_match ( '/^papi\_/' , $ slug ) ) === $ slug ; }

Match property slug with given slug value.

@param  string $slug

@return bool

Match property slug with given slug value .


protected function prepare_value( $value ) {
		if ( papi_is_empty( $value ) ) {
			return;
		}

		$value = papi_santize_data( $value );

		if ( is_array( $value ) ) {
			$value = array_filter( $value, function ( $val ) {
				return ! papi_is_empty( $val );
			} );

			if ( ! count( array_filter( array_keys( $value ), 'is_string' ) ) ) {
				$value = array_values( $value );
			}
		}

		return $value;
	}

protected function prepare_value ( $ value ) { if ( papi_is_empty ( $ value ) ) { return ; } $ value = papi_santize_data ( $ value ) ; if ( is_array ( $ value ) ) { $ value = array_filter ( $ value , function ( $ val ) { return ! papi_is_empty ( $ val ) ; } ) ; if ( ! count ( array_filter ( array_keys ( $ value ) , 'is_string' ) ) ) { $ value = array_values ( $ value ) ; } } return $ value ; }

Prepare value before database.

@param  mixed $value

@return mixed


public function render_is_allowed_by_rules( array $rules = [] ) {
		if ( empty( $rules ) ) {
			$rules = $this->get_rules();
		}

		return $this->conditional->display( $rules, $this );
	}

public function render_is_allowed_by_rules ( array $ rules = [ ] ) { if ( empty ( $ rules ) ) { $ rules = $ this -> get_rules ( ) ; } return $ this -> conditional -> display ( $ rules , $ this ) ; }

Check if the property is allowed
to render by the conditional rules.

@param  array $rules

@return bool

Check if the property is allowed to render by the conditional rules .


public function set_option( $key, $value ) {
		if ( ! is_object( $this->options ) ) {
			$this->options = (object) $this->default_options;
		}

		$this->options->$key = $value;
	}

public function set_option ( $ key , $ value ) { if ( ! is_object ( $ this -> options ) ) { $ this -> options = ( object ) $ this -> default_options ; } $ this -> options -> $ key = $ value ; }

Set property option value.

@param string $key
@param mixed  $value


public function set_setting( $key, $value ) {
		if ( isset( $this->options->settings, $this->options->settings->$key ) ) {
			$this->options->settings->$key = $value;
		}
	}

public function set_setting ( $ key , $ value ) { if ( isset ( $ this -> options -> settings , $ this -> options -> settings -> $ key ) ) { $ this -> options -> settings -> $ key = $ value ; } }

Set property setting value.

@param string $key
@param mixed  $value


protected function setup_properties() {
		$this->conditional = new Papi_Core_Conditional();

		if ( $this->default_options['sort_order'] === -1 ) {
			$this->default_options['sort_order'] = papi_filter_settings_sort_order();
		}

		if ( papi_is_empty( $this->default_options['post_type'] ) ) {
			$this->default_options['post_type'] = papi_get_post_type();
		}
	}

protected function setup_properties ( ) { $ this -> conditional = new Papi_Core_Conditional ( ) ; if ( $ this -> default_options [ 'sort_order' ] === - 1 ) { $ this -> default_options [ 'sort_order' ] = papi_filter_settings_sort_order ( ) ; } if ( papi_is_empty ( $ this -> default_options [ 'post_type' ] ) ) { $ this -> default_options [ 'post_type' ] = papi_get_post_type ( ) ; } }


protected function setup_options( $options = [] ) {
		// When a object is sent in, just return it.
		if ( is_object( $options ) ) {
			return $options;
		}

		// Only arrays can be handled.
		if ( ! is_array( $options ) ) {
			$options = [];
		}

		// Merge default options with the given options array.
		$options = array_merge( $this->default_options, $options );
		$options = (object) $options;

		// Setup aliases.
		$option_aliases = apply_filters( 'papi/option_aliases', $this->option_aliases );
		$option_aliases = is_array( $option_aliases ) ? $option_aliases : [];
		$option_aliases = array_merge( $this->option_aliases, $option_aliases );

		foreach ( $option_aliases as $alias => $option ) {
			if ( isset( $options->$alias ) && ! papi_is_empty( $options->$alias ) ) {
				$options->$option = $options->$alias;
				unset( $options->$alias );
			}
		}

		// Capabilities should be a array.
		$options->capabilities = papi_to_array( $options->capabilities );

		// Setup property slug.
		$options->slug = strtolower( $this->setup_options_slug( $options ) );

		// Setup property settings.
		$options->settings = $this->setup_options_settings( $options );

		// Type should always be lowercase.
		$options->type = strtolower( $options->type );

		// Escape all options except those that are send it as second argument.
		return papi_esc_html( $options, ['before_html', 'html', 'after_html'] );
	}

Setup property options.

@param  mixed $options

@return mixed


protected function setup_options_slug( $options ) {
		$slug = $options->slug;

		// When `slug` is false or not required a unique slug should be generated.
		if ( $slug === false || ( empty( $slug ) && $this->slug_required === false ) ) {
			return '_' . papi_html_name( md5( uniqid( rand(), true ) ) );
		}

		// If `slug` is empty, check if `title` is not empty
		// and generate a slug from the `title` or if empty
		// use the `type`.
		if ( empty( $slug ) ) {
			if ( empty( $options->title ) ) {
				$slug = papi_slugify( $options->type );
			} else {
				$slug = papi_slugify( $options->title );
			}
		}

		// Create a html friendly name from the `slug`.
		return papi_html_name( $slug );
	}

Setup options slug.

@param  stdClass $options

@return string


protected function setup_options_settings( $options ) {
		$property_class = self::factory( $options->type );

		if ( papi_is_property( $property_class ) ) {
			$options->settings = array_merge( (array) $property_class->get_default_settings(), (array) $options->settings );
		}

		return (object) $options->settings;
	}

protected function setup_options_settings ( $ options ) { $ property_class = self :: factory ( $ options -> type ) ; if ( papi_is_property ( $ property_class ) ) { $ options -> settings = array_merge ( ( array ) $ property_class -> get_default_settings ( ) , ( array ) $ options -> settings ) ; } return ( object ) $ options -> settings ; }

Setup options settings.

@param  stdClass $options

@return stdClass


public function edit_attachment( $form_fields, $post ) {
		foreach ( $this->get_boxes() as $box ) {
			if ( ! empty( $box->title ) ) {
				$form_fields['papi-media-title-' . uniqid()] = [
					'label' => '',
					'input' => 'html',
					'html'  => '
' . $box->title . '
'
				];
			}

			foreach ( $box->properties as $prop ) {
				// Raw output is required.
				$prop->raw = true;

				// Set post id to the property.
				$prop->set_post_id( $post->ID );

				// Add property to form fields.
				$form_fields[$prop->get_slug()] = [
					'label' => $prop->title,
					'input' => 'html',
					'helps' => $prop->description,
					'html'  => papi_maybe_get_callable_value(
						'papi_render_property',
						$prop
					)
				];
			}
		}

		// Add nonce field.
		$form_fields['papi_meta_nonce'] = [
			'label' => '',
			'input' => 'html',
			'html'  => sprintf(
				'<input name="papi_meta_nonce" type="hidden" value="%s" />',
				wp_create_nonce( 'papi_save_data' )
			)
		];

		return $form_fields;
	}

Add attachment fields.

@param  array   $form_fields
@param  WP_Post $post

@return array


public function save_attachment( $post ) {
		update_post_meta( $post['ID'], papi_get_page_type_key(), $this->get_id() );

		$handler = new Papi_Admin_Meta_Handler();
		$handler->save_meta_boxes( $post['ID'], $post );

		return $post;
	}

public function save_attachment ( $ post ) { update_post_meta ( $ post [ 'ID' ] , papi_get_page_type_key ( ) , $ this -> get_id ( ) ) ; $ handler = new Papi_Admin_Meta_Handler ( ) ; $ handler -> save_meta_boxes ( $ post [ 'ID' ] , $ post ) ; return $ post ; }

Save attachment post data.

@param  array $post

@return array


public function singleton() {
		$key = sprintf( 'entry_type_id.post_type.%s', $this->get_post_type() );

		if ( papi()->exists( $key ) ) {
			return true;
		}

		papi()->singleton( $key, $this->get_id() );

		return false;
	}

public function singleton ( ) { $ key = sprintf ( 'entry_type_id.post_type.%s' , $ this -> get_post_type ( ) ) ; if ( papi ( ) -> exists ( $ key ) ) { return true ; } papi ( ) -> singleton ( $ key , $ this -> get_id ( ) ) ; return false ; }

Check if the entry type is a singleton.

@return bool

Check if the entry type is a singleton .


public function get_property_meta_value( $slug ) {
		$slug = strtolower( $slug );

		if ( isset( $this->meta_values[$slug] ) ) {
			return $this->meta_values[$slug];
		}

		return papi_data_get( $this->id, $slug, $this->get_type() );
	}

public function get_property_meta_value ( $ slug ) { $ slug = strtolower ( $ slug ) ; if ( isset ( $ this -> meta_values [ $ slug ] ) ) { return $ this -> meta_values [ $ slug ] ; } return papi_data_get ( $ this -> id , $ slug , $ this -> get_type ( ) ) ; }

Get property meta value.

@param  string $slug

@return mixed


public function format_value( $slug, $value ) {
		$slug     = strtolower( unpapify( $slug ) );
		$property = $this->property( $slug );

		// If no property type is found, just return null.
		if ( ! papi_is_property( $property ) ) {
			return;
		}

		// Format the value from the property class.
		$value = $property->format_value( $value, $slug, $this->id );

		// Only fired when not in admin.
		if ( ! papi_is_admin() ) {
			$value = papi_filter_format_value(
				$property->type,
				$value,
				$slug,
				$this->id,
				papi_get_meta_type()
			);
		}

		// Modify value before it's return to the theme via a defined format callback on the property.
		if ( ! papi_is_admin() && is_callable( $property->format_cb ) ) {
			$value = call_user_func( $property->format_cb, $value );
		}

		// Remove empty values from arrays.
		if ( is_array( $value ) ) {
			foreach ( $value as $index => $val ) {
				if ( papi_is_empty( $val ) ) {
					unset( $value[$index] );
				}
			}
		}

		return $value;
	}

Format property value from the property.

@param  string $slug
@param  mixed  $value

@return mixed

Format property value from the property .


protected function property( $slug = '' ) {
		if ( isset( $this->properties[$slug] ) && papi_is_property( $this->properties[$slug] ) ) {
			$property = $this->properties[$slug];
		} else {
			$property = $this->properties[$slug] = $this->get_property( $slug );
		}

		/**
		 * Modify property.
		 *
		 * @param  Papi_Core_Property $property
		 */
		return apply_filters( 'papi/get_property', $property );
	}

Get current property.

@param  string $slug

@return Papi_Core_Property


public function load_value( $slug ) {
		$slug     = strtolower( unpapify( $slug ) );
		$property = $this->property( $slug );

		// If no property type is found, just return null.
		if ( ! papi_is_property( $property ) ) {
			return;
		}

		// Get raw property meta value.
		$value = $this->get_property_meta_value( $slug );

		// Prepare load value, when you have `overwrite => true`
		// this value will not exist in the database and that's
		// why we need to prepare (change) the value.
		$value = $this->prepare_load_value( $property, $value );

		// Bail if value is empty and option value is empty.
		if ( papi_is_empty( $value ) ) {
			if ( ! papi_is_empty( $property->get_option( 'value' ) ) ) {
				return $property->get_option( 'value' );
			}

			return;
		}

		// A property need to know about the store.
		$this->property( $slug )->set_store( $this );

		// Run load value method right after the value has been loaded from the database.
		$value = $property->load_value( $value, $slug, $this->id );
		$value = papi_filter_load_value(
			$property->type,
			$value,
			$slug,
			$this->id,
			papi_get_meta_type()
		);

		// Remove empty values from arrays.
		if ( is_array( $value ) ) {
			foreach ( $value as $index => $val ) {
				if ( papi_is_empty( $val ) ) {
					unset( $value[$index] );
				}
			}
		}

		return $value;
	}

Load property value from the property.

@param  string $slug

@return mixed


public static function factory( $post_id, $type = 'post' ) {
		$type         = papi_get_meta_type( $type );
		$class_suffix = '_' . ucfirst( $type ) . '_Store';
		$class_name   = 'Papi' . $class_suffix;

		if ( ! class_exists( $class_name ) ) {
			return;
		}

		$post_id = papi_get_post_id( $post_id );
		$page    = new $class_name( $post_id );

		if ( ! $page->valid() ) {
			return;
		}

		return $page;
	}

public static function factory ( $ post_id , $ type = 'post' ) { $ type = papi_get_meta_type ( $ type ) ; $ class_suffix = '_' . ucfirst ( $ type ) . '_Store' ; $ class_name = 'Papi' . $ class_suffix ; if ( ! class_exists ( $ class_name ) ) { return ; } $ post_id = papi_get_post_id ( $ post_id ) ; $ page = new $ class_name ( $ post_id ) ; if ( ! $ page -> valid ( ) ) { return ; } return $ page ; }

Get store from factory.

@param  int    $post_id
@param  string $type

@return mixed


public function get_property_option( $slug, $option, $default = null ) {
		$slug     = unpapify( $slug );
		$property = $this->property( $slug );

		// If no property type is found, return default
		// value since we don't have a property.
		if ( ! papi_is_property( $property ) ) {
			return $default;
		}

		$value = $property->get_option( $option );

		if ( papi_is_empty( $value ) ) {
			return $default;
		}

		return $value;
	}

Get property option or default value.

@param  string $slug
@param  string $option
@param  mixed  $default

@return bool


public function can_render() {
		// Check if current user can view the property.
		if ( ! $this->current_user_can() ) {
			return false;
		}

		// A disabled property should not be rendered.
		if ( $this->disabled() ) {
			return false;
		}

		// Check language option, so we don't render properties on a different language.
		if ( $lang = $this->get_option( 'lang' ) ) {
			return in_array( papi_get_lang(), papi_to_array( $lang ), true );
		}

		// If no valid lang query string exists we have to override the display property.
		return $this->get_option( 'lang' ) === false;
	}

Determine if the property can be rendered or not.

@return bool

Determine if the property can be rendered or not .


public function render() {
		// Bail if we can't render the property.
		if ( ! $this->can_render() ) {
			return;
		}

		// Override display with rules check.
		if ( $this->display() ) {
			$this->display = $this->render_is_allowed_by_rules();
		}

		// Render property.
		$this->render_row_html();
	}


protected function render_hidden_html() {
		$slug = $this->get_option( 'slug' );

		if ( substr( $slug, - 1 ) === ']' ) {
			$slug = substr( $slug, 0, - 1 );
			$slug = papi_get_property_type_key( $slug );
			$slug .= ']';
		} else {
			$slug = papi_get_property_type_key( $slug );
		}

		$slug          = papify( $slug );
		$options       = $this->get_options();
		$property_json = base64_encode( papi_maybe_json_encode( $options ) );

		papi_render_html_tag( 'input', [
			'data-property' => strtolower( $this->get_option( 'type' ) ),
			'name'          => $slug,
			'type'          => 'hidden',
			'value'         => $property_json
		] );
	}

protected function render_hidden_html ( ) { $ slug = $ this -> get_option ( 'slug' ) ; if ( substr ( $ slug , - 1 ) === ']' ) { $ slug = substr ( $ slug , 0 , - 1 ) ; $ slug = papi_get_property_type_key ( $ slug ) ; $ slug .= ']' ; } else { $ slug = papi_get_property_type_key ( $ slug ) ; } $ slug = papify ( $ slug ) ; $ options = $ this -> get_options ( ) ; $ property_json = base64_encode ( papi_maybe_json_encode ( $ options ) ) ; papi_render_html_tag ( 'input' , [ 'data-property' => strtolower ( $ this -> get_option ( 'type' ) ) , 'name' => $ slug , 'type' => 'hidden' , 'value' => $ property_json ] ) ; }

Output hidden input field that cointains which property is used.

Output hidden input field that cointains which property is used .


protected function render_label_html() {
		$title = $this->get_option( 'title' );

		papi_render_html_tag( 'label', [
			'for'   => $this->html_id(),
			'title' => trim( $title . ' ' . papi_property_require_text( $this->get_options() ) ),
			$title,
			papi_property_required_html( $this )
		] );
	}

protected
function
render_label_html
(
)
{
$
title
=
$
this
->
get_option
(
'title'
)
;
papi_render_html_tag
(
'label'
,
[
'for'
=>
$
this
->
html_id
(
)
,
'title'
=>
trim
(
$
title
.
' '
.
papi_property_require_text
(
$
this
->
get_options
(
)
)
)
,
$
title
,
papi_property_required_html
(
$
this
)
]
)
;
}


protected function render_row_html() {
		$display_class = $this->display() ? '' : ' papi-hide';
		$rules_class   = papi_is_empty( $this->get_rules() ) ? '' : ' papi-rules-exists';
		$css_class     = trim( $display_class . $rules_class );
		$css_class    .= sprintf( ' papi-layout-%s', $this->get_option( 'layout' ) );

		if ( $this->get_option( 'raw' ) ) {
			echo sprintf( '
', esc_attr( $css_class ) );
			$this->render_property_html();
			$this->render_hidden_html();
			$this->render_rules_json();
			echo '
';
		} else {
			?>
			<tr class="<?php echo esc_attr( $css_class ); ?>">
				<?php if ( $this->get_option( 'sidebar' ) && $this->get_option( 'layout' ) === 'horizontal' ): ?>

						<?php
							$this->render_label_html();
							$this->render_description_html();
						?>

				<?php endif; ?>
				<td <?php echo $this->get_option( 'sidebar' ) && $this->get_option( 'layout' ) === 'horizontal' ? '' : 'colspan="2"'; ?>>
					<?php
					// Render vertical layout where title and description is above the property.
					if ( $this->get_option( 'layout' ) === 'vertical' && $this->get_option( 'sidebar' ) ) {
						$this->render_label_html();
						$this->render_description_html();
					}

					$this->render_property_html();
					$this->render_hidden_html();
					$this->render_rules_json();
					?>

			<?php
		}
	}

?>
					<td class="papi-table-sidebar">
						<?php

Render the final html that is displayed in the table.

Render the final html that is displayed in the table .


protected function render_rules_json() {
		$rules = $this->get_rules();

		if ( empty( $rules ) ) {
			return;
		}

		$rules = $this->conditional->prepare_rules( $rules, $this );

		papi_render_html_tag( 'script', [
			'data-papi-rule-source-slug' => $this->html_name(),
			'data-papi-rules'            => 'true',
			'type'                       => 'application/json',
			papi_maybe_json_encode( $rules )
		] );
	}

protected function render_rules_json ( ) { $ rules = $ this -> get_rules ( ) ; if ( empty ( $ rules ) ) { return ; } $ rules = $ this -> conditional -> prepare_rules ( $ rules , $ this ) ; papi_render_html_tag ( 'script' , [ 'data-papi-rule-source-slug' => $ this -> html_name ( ) , 'data-papi-rules' => 'true' , 'type' => 'application/json' , papi_maybe_json_encode ( $ rules ) ] ) ; }

Render Conditional rules as script tag with JSON.

Render Conditional rules as script tag with JSON .


public function get_value() {
		$value = $this->format_value(
			parent::get_value(),
			$this->get_slug(),
			papi_get_post_id()
		);

		return $this->get_setting( 'allow_html' ) ? $value : esc_html( $value );
	}

public function get_value ( ) { $ value = $ this -> format_value ( parent :: get_value ( ) , $ this -> get_slug ( ) , papi_get_post_id ( ) ) ; return $ this -> get_setting ( 'allow_html' ) ? $ value : esc_html ( $ value ) ; }

Get value from the database.

@return string


protected function decode_property( $key, $value ) {
		if ( papi_is_property_type_key( $key ) && is_string( $value ) ) {
			$value = base64_decode( $value );
			$value = papi_maybe_json_decode( $value );
		}

		return $value;
	}

protected function decode_property ( $ key , $ value ) { if ( papi_is_property_type_key ( $ key ) && is_string ( $ value ) ) { $ value = base64_decode ( $ value ) ; $ value = papi_maybe_json_decode ( $ value ) ; } return $ value ; }

Decode property.

@param string $key
@param string $value

@return mixed


protected function get_post_data( $pattern = '/^papi\_.*/' ) {
		$data = [];
		$keys = preg_grep( $pattern, array_keys( $_POST ) );

		foreach ( $keys as $key ) {
			// Remove page type keys with suffix. This should not be saved.
			if (
				strpos( $key, papi_get_page_type_key() ) === 0
				&&
				strlen( papi_get_page_type_key() ) !== strlen( $key )
			) {
				continue;
			}

			// Fix for input fields that should be true on `on` value.
			if ( $_POST[$key] === 'on' ) {
				$data[$key] = true;
			} else {
				$value = $this->decode_property( $key, $_POST[$key] );
				$data[$key] = $this->prepare_post_data( $value );
				$data[$key] = $this->santize_data( $data[$key] );
			}
		}

		// Don't wont to save meta nonce field.
		if ( isset( $data['papi_meta_nonce'] ) ) {
			unset( $data['papi_meta_nonce'] );
		}

		return $data;
	}

Get post data.

@param  string $pattern

@return array


protected function get_pre_deep_keys_value( array $arr ) {
		$keys  = [];
		$value = null;

		foreach ( $arr as $key => $v ) {
			if ( is_array( $v ) ) {
				$keys[] = $key;
				list( $ks, $val ) = $this->get_pre_deep_keys_value( $v );
				$keys   = array_merge( $keys, $ks );
				$value  = $val;
			} else {
				$keys[] = $key;
				$value  = $v;
			}
		}

		return [$keys, $value];
	}

protected function get_pre_deep_keys_value ( array $ arr ) { $ keys = [ ] ; $ value = null ; foreach ( $ arr as $ key => $ v ) { if ( is_array ( $ v ) ) { $ keys [ ] = $ key ; list ( $ ks , $ val ) = $ this -> get_pre_deep_keys_value ( $ v ) ; $ keys = array_merge ( $ keys , $ ks ) ; $ value = $ val ; } else { $ keys [ ] = $ key ; $ value = $ v ; } } return [ $ keys , $ value ] ; }

Pre get deep keys and value.

Used for saving pre data when properties are in a flexible or repeater.

@param  array $arr

@return array


protected function prepare_post_data( $data ) {
		if ( ! is_array( $data ) ) {
			return $data;
		}

		foreach ( $data as $key => $value ) {
			if ( is_array( $value ) ) {
				$data[$key] = $this->prepare_post_data( $value );
			} else {
				$data[$key] = $this->decode_property( $key, $value );
			}
		}

		return $data;
	}

protected function prepare_post_data ( $ data ) { if ( ! is_array ( $ data ) ) { return $ data ; } foreach ( $ data as $ key => $ value ) { if ( is_array ( $ value ) ) { $ data [ $ key ] = $ this -> prepare_post_data ( $ value ) ; } else { $ data [ $ key ] = $ this -> decode_property ( $ key , $ value ) ; } } return $ data ; }

Prepare post data.
Will decode property options recursive.

@param  mixed $data

@return mixed

Prepare post data . Will decode property options recursive .


protected function prepare_properties_data( array $data = [], $post_id = 0 ) {
		// Since we are storing witch property it is in the `$data` array
		// we need to remove that and set the property type to the property
		// and make a array of the property type and the value.
		foreach ( $data as $key => $value ) {
			if ( papi_is_property_type_key( $key ) ) {
				continue;
			}

			$property_type_key = papify( papi_get_property_type_key( $key ) );

			// Check if value exists.
			if ( ! isset( $data[$key] ) || ! isset( $data[$property_type_key] ) ) {
				continue;
			}

			// Pair property value with property type object.
			$data[$key] = [
				'type'  => $data[$property_type_key],
				'value' => $value
			];

			// Remove property type object since it's not needed anymore.
			unset( $data[$property_type_key] );
		}

		foreach ( $data as $key => $item ) {
			if ( papi_is_property_type_key( $key ) ) {
				if ( isset( $data[$key] ) ) {
						unset( $data[$key] );
				}
				continue;
			}

			if ( empty( $item['type'] ) ) {
				continue;
			}

			$property = papi_get_property_type( $item['type'] );

			unset( $data[ $key ] );

			if ( papi_is_property( $property ) ) {
				// Run `update_value` method on the property class.
				$data[$key] = $property->update_value(
					$item['value'],
					unpapify( $key ),
					$post_id
				);

				// Apply `update_value` filter so this can be changed from
				// the theme for specified property type.
				$data[$key] = papi_filter_update_value(
					$item['type']->type,
					$data[$key],
					unpapify( $key ),
					$post_id,
					papi_get_meta_type()
				);

				if ( $item['type']->overwrite ) {
					$slug = unpapify( $key );
					$this->overwrite[$slug] = $data[$key];
					unset( $data[$key] );
				}
			}
		}

		return $data;
	}

Prepare properties data for saving.

@param  array $data
@param  int   $post_id

@return array


protected function santize_data( $value ) {
		if ( is_array( $value ) ) {
			foreach ( $value as $k => $v ) {
				if ( is_string( $v ) ) {
					$value[$k] = $this->santize_data( $v );
				}
			}
		} else if ( is_string( $value ) ) {
			$value = wp_unslash( $value );
		}

		return $value;
	}

protected function santize_data ( $ value ) { if ( is_array ( $ value ) ) { foreach ( $ value as $ k => $ v ) { if ( is_string ( $ v ) ) { $ value [ $ k ] = $ this -> santize_data ( $ v ) ; } } } else if ( is_string ( $ value ) ) { $ value = wp_unslash ( $ value ) ; } return $ value ; }

Sanitize data before saving it.

@param  mixed $value

@return mixed


public function delete( $id, $slug ) {
		$fn = $this->get_function( 'delete' );

		// Check so the function is callable before using it.
		if ( ! is_callable( $fn ) ) {
			return false;
		}

		// Delete cached value.
		papi_cache_delete( $slug, $id, $this->type );

		if ( $this->id ) {
			return call_user_func_array( $fn, [$this->type, $id, unpapify( $slug )] );
		}

		return call_user_func_array( $fn, [unpapify( $slug )] );
	}

Delete property value.

@param  int    $id
@param  string $slug

@return bool


public function get_function( $context = 'get' ) {
		switch ( $this->type ) {
			case 'option':
				return sprintf( '%s_option', $context );
			case 'site':
			case 'network':
				return sprintf( '%s_site_option', $context );
			case 'post':
			case 'term':
				return sprintf( '%s_metadata', $context );
			default:
				break;
		}
	}

public function get_function ( $ context = 'get' ) { switch ( $ this -> type ) { case 'option' : return sprintf ( '%s_option' , $ context ) ; case 'site' : case 'network' : return sprintf ( '%s_site_option' , $ context ) ; case 'post' : case 'term' : return sprintf ( '%s_metadata' , $ context ) ; default : break ; } }

Get right meta function for right type and context.

@param  string $context

@return string

Get right meta function for right type and context .


public function get( $id, $slug ) {
		$fn = $this->get_function( 'get' );

		if ( ! is_callable( $fn ) ) {
			return;
		}

		if ( $this->id ) {
			$value = call_user_func_array( $fn, [$this->type, $id, unpapify( $slug ), true] );
		} else {
			$value = call_user_func_array( $fn, [unpapify( $slug ), null] );
		}

		if ( papi_is_empty( $value ) ) {
			return;
		}

		return $value;
	}

public function get ( $ id , $ slug ) { $ fn = $ this -> get_function ( 'get' ) ; if ( ! is_callable ( $ fn ) ) { return ; } if ( $ this -> id ) { $ value = call_user_func_array ( $ fn , [ $ this -> type , $ id , unpapify ( $ slug ) , true ] ) ; } else { $ value = call_user_func_array ( $ fn , [ unpapify ( $ slug ) , null ] ) ; } if ( papi_is_empty ( $ value ) ) { return ; } return $ value ; }

Geta property value for right meta type.

@param  int    $id
@param  string $slug

@return mixed

Geta property value for right meta type .


public function update( $id, $slug, $value ) {
		$save_value = true;

		// Get right update function to use.
		$fn = $this->get_function( 'update' );

		if ( ! is_callable( $fn ) ) {
			return false;
		}

		// Check for string keys in the array if any.
		foreach ( array_keys( papi_to_array( $value ) ) as $key ) {
			if ( is_string( $key ) ) {
				$save_value = false;
				break;
			}
		}

		// If main value shouldn't be saved it should be array.
		if ( ! $save_value && is_array( $value ) ) {
			$value = [$value];
		}

		// Delete saved value if empty.
		if ( papi_is_empty( $value ) ) {
			return $this->delete( $id, $slug );
		}

		$result = true;

		foreach ( papi_to_array( $value ) as $key => $val ) {
			// Delete saved value if value is empty.
			if ( papi_is_empty( $val ) || $val === '[]' || $val === '{}' ) {
				return $this->delete( $id, $slug );
			}

			// Delete main value cache.
			papi_cache_delete( $slug, $id, $this->type );

			// If not a array we can save the value.
			if ( ! is_array( $val ) ) {
				if ( $save_value ) {
					$val = $value;
				}

				if ( $this->id ) {
					$out    = call_user_func_array( $fn, [$this->type, $id, unpapify( $slug ), $val] );
					$result = $out ? $result : $out;
				} else {
					$out    = call_user_func_array( $fn, [unpapify( $slug ), $val] );
					$result = $out ? $result : $out;
				}

				continue;
			}

			// Clear cache for all child values.
			$this->update_clear_cache( $id, $value );

			// Update metadata or option value for all child values.
			foreach ( $val as $child_key => $child_value ) {
				if ( papi_is_empty( $child_value ) ) {
					$this->delete( $id, $child_key );
				} else {
					if ( $this->id ) {
						call_user_func_array( $fn, [$this->type, $id, unpapify( $child_key ), $child_value] );
					} else {
						call_user_func_array( $fn, [unpapify( $child_key ), $child_value] );
					}
				}
			}
		}

		return $result;
	}

Update property meta.

@param  int    $id
@param  string $slug
@param  mixed  $value

@return bool


public function update_clear_cache( $id, $value ) {
		$value = is_array( $value ) ? $value : [];

		foreach ( $value as $child_key => $child_value ) {
			papi_cache_delete( $child_key, $id, $this->type );

			if ( is_array( $child_value ) ) {
				$this->update_clear_cache( $id, $child_value );
			}
		}
	}

public function update_clear_cache ( $ id , $ value ) { $ value = is_array ( $ value ) ? $ value : [ ] ; foreach ( $ value as $ child_key => $ child_value ) { papi_cache_delete ( $ child_key , $ id , $ this -> type ) ; if ( is_array ( $ child_value ) ) { $ this -> update_clear_cache ( $ id , $ child_value ) ; } } }

Clear cache values on update property meta.

@param  int   $id
@param  mixed $value

Clear cache values on update property meta .


public function get_revision_ui_diff( $return, $compare_from, $compare_to ) {
		$meta = get_post_meta( $compare_from->ID );
		$meta = is_array( $meta ) ? $meta : [];

		foreach ( $meta as $key => $value ) {
			if ( $key[0] === '_' && $key !== papi_get_page_type_key() ) {
				continue;
			}

			$content_from = papi_data_get( $compare_from->ID, $key );
			$content_to = papi_data_get( $compare_to->ID, $key );

			$diff = wp_text_diff( $content_from, $content_to, [
				'show_split_view' => true
			] );

			if ( $diff ) {
				$return[] = [
					'id'   => $key,
					'name' => $key,
					'diff' => $diff,
				];
			}
		}

		return $return;
	}

public function get_revision_ui_diff ( $ return , $ compare_from , $ compare_to ) { $ meta = get_post_meta ( $ compare_from -> ID ) ; $ meta = is_array ( $ meta ) ? $ meta : [ ] ; foreach ( $ meta as $ key => $ value ) { if ( $ key [ 0 ] === '_' && $ key !== papi_get_page_type_key ( ) ) { continue ; } $ content_from = papi_data_get ( $ compare_from -> ID , $ key ) ; $ content_to = papi_data_get ( $ compare_to -> ID , $ key ) ; $ diff = wp_text_diff ( $ content_from , $ content_to , [ 'show_split_view' => true ] ) ; if ( $ diff ) { $ return [ ] = [ 'id' => $ key , 'name' => $ key , 'diff' => $ diff , ] ; } } return $ return ; }

Filters the fields displayed in the post revision diff UI.

@param  array   $return
@param  WP_Post $compare_from
@param  WP_Post $compare_to

@return array

Filters the fields displayed in the post revision diff UI .


public function hidden_meta_boxes() {
		global $_wp_post_type_features;

		if ( isset( $_wp_post_type_features[$this->post_type]['editor'] ) ) {
			return;
		}

		add_meta_box( 'papi-hidden-editor', 'Papi hidden editor', [$this, 'hidden_meta_box_editor'], $this->post_type );
	}

public
function
hidden_meta_boxes
(
)
{
global
$
_wp_post_type_features
;
if
(
isset
(
$
_wp_post_type_features
[
$
this
->
post_type
]
[
'editor'
]
)
)
{
return
;
}
add_meta_box
(
'papi-hidden-editor'
,
'Papi hidden editor'
,
[
$
this
,
'hidden_meta_box_editor'
]
,
$
this
->
post_type
)
;
}


public function load_post_new() {
		$request_uri = $_SERVER['REQUEST_URI'];
		$post_types  = papi_get_post_types();

		if ( in_array( $this->post_type, $post_types, true ) && strpos( $request_uri, 'page_type=' ) === false ) {
			$parsed_url = parse_url( $request_uri );

			$only_page_type = papi_filter_settings_only_page_type( $this->post_type );
			$page_types     = papi_get_all_page_types( $this->post_type );
			$show_standard  = false;

			if ( count( $page_types ) === 1 && empty( $only_page_type ) ) {
				$show_standard  = $page_types[0]->standard_type;
				$show_standard  = $show_standard ? papi_filter_settings_show_standard_page_type( $this->post_type ) : $show_standard;
				$only_page_type = $show_standard ? '' : $page_types[0]->get_id();
			}

			// Check if we should show one post type or not and
			// create the right url for that.
			if ( ! empty( $only_page_type ) && ! $show_standard ) {
				$url = papi_get_page_new_url( $only_page_type, false );
			} else {
				$page = 'page=papi-add-new-page,' . $this->post_type;

				if ( $this->post_type !== 'post' ) {
					$page = '&' . $page;
				}

				$url = 'edit.php?' . $parsed_url['query'] . $page;
			}

			wp_safe_redirect( $url );
			papi_is_admin() && exit;
		}
	}

Load post new action
Redirect to right url if no page type is set.

Load post new action Redirect to right url if no page type is set .


public function redirect_post_location( $location ) {
		if ( ! isset( $_SERVER['HTTP_REFERER'] ) ) {
			return $location;
		}

		$referer = $_SERVER['HTTP_REFERER'];
		$referer = strtolower( $referer );

		if ( strpos( $referer, 'papi-iframe-mode' ) === false ) {
			return $location;
		}

		return sprintf( '%s&papi_css[]=papi-iframe-mode', $location );
	}

public function redirect_post_location ( $ location ) { if ( ! isset ( $ _SERVER [ 'HTTP_REFERER' ] ) ) { return $ location ; } $ referer = $ _SERVER [ 'HTTP_REFERER' ] ; $ referer = strtolower ( $ referer ) ; if ( strpos ( $ referer , 'papi-iframe-mode' ) === false ) { return $ location ; } return sprintf ( '%s&papi_css[]=papi-iframe-mode' , $ location ) ; }

Redirect post location when post is in iframe mode.

@param  string $location

@return string

Redirect post location when post is in iframe mode .


public function setup() {
		// Preload all page types.
		foreach ( papi_get_post_types() as $post_type ) {
			papi_get_all_entry_types( [
				'args' => $post_type
			] );
		}

		return ! in_array( $this->post_type, ['revision', 'nav_menu_item'], true );
	}

public
function
setup
(
)
{
// Preload all page types.
foreach
(
papi_get_post_types
(
)
as
$
post_type
)
{
papi_get_all_entry_types
(
[
'args'
=>
$
post_type
]
)
;
}
return
!
in_array
(
$
this
->
post_type
,
[
'revision'
,
'nav_menu_item'
]
,
true
)
;
}


public function rename( $args, $assoc_args ) {
		$type    = $args[0];
		$old_key = $args[1];
		$new_key = $args[2];

		$posts = ( new Papi_Query( [
			'entry_type' => $type,
			'fields'     => 'ids'
		] ) )->get_result();

		if ( empty( $posts ) ) {
			WP_CLI::error( 'No posts found' );
		}

		foreach ( $posts as $post ) {
			$meta = get_post_meta( $post, $old_key, true );

			if ( papi_is_empty( $meta ) ) {
				continue;
			}

			if ( delete_post_meta( $post, $old_key ) === false ) {
				WP_CLI::error( 'Could not delete post meta with key: ' . $old_key );

			}

			if ( update_post_meta( $post, $new_key, $meta ) === false ) {
				WP_CLI::error( 'Could not update post meta with key: ' . $new_key );
			}
		}

		WP_CLI::success( 'Done' );
	}

public
function
rename
(
$
args
,
$
assoc_args
)
{
$
type
=
$
args
[
0
]
;
$
old_key
=
$
args
[
1
]
;
$
new_key
=
$
args
[
2
]
;
$
posts
=
(
new
Papi_Query
(
[
'entry_type'
=>
$
type
,
'fields'
=>
'ids'
]
)
)
->
get_result
(
)
;
if
(
empty
(
$
posts
)
)
{
WP_CLI
::
error
(
'No posts found'
)
;
}
foreach
(
$
posts
as
$
post
)
{
$
meta
=
get_post_meta
(
$
post
,
$
old_key
,
true
)
;
if
(
papi_is_empty
(
$
meta
)
)
{
continue
;
}
if
(
delete_post_meta
(
$
post
,
$
old_key
)
===
false
)
{
WP_CLI
::
error
(
'Could not delete post meta with key: '
.
$
old_key
)
;
}
if
(
update_post_meta
(
$
post
,
$
new_key
,
$
meta
)
===
false
)
{
WP_CLI
::
error
(
'Could not update post meta with key: '
.
$
new_key
)
;
}
}
WP_CLI
::
success
(
'Done'
)
;
}

Rename meta key for page type.

## OPTIONS

<page_type>
: Page type id

<old_key>
: Old meta key

<new_key>
: New meta key

## EXAMPLES

wp papi post rename about-page-type name title

@param  array $args
@param  array $assoc_args


public function get_field_slug() {
		if ( preg_match( '/\[|\]/', $this->slug ) ) {
			$slug = preg_replace( '/\[|\]/', '.', $this->slug );
			$slug = str_replace( '..', '.', $slug );
			return substr( $slug, 0, -1 );
		}

		return $this->slug;
	}

public function get_field_slug ( ) { if ( preg_match ( '/\[|\]/' , $ this -> slug ) ) { $ slug = preg_replace ( '/\[|\]/' , '.' , $ this -> slug ) ; $ slug = str_replace ( '..' , '.' , $ slug ) ; return substr ( $ slug , 0 , - 1 ) ; } return $ this -> slug ; }


public function get_source() {
		if ( is_callable( $this->source ) ) {
			return call_user_func_array( $this->source, [$this->slug] );
		}

		if ( is_string( $this->source ) && strpos( $this->source, '#' ) !== false ) {
			$source = explode( '#', $this->source );

			if ( empty( $source[0] ) || empty( $source[1] ) ) {
				return $this->source;
			}

			$source[0] = new $source[0]();

			if ( method_exists( $source[0], $source[1] ) ) {
				return call_user_func_array( $source, [$this->slug] );
			}

			return;
		}

		return $this->source;
	}

public function get_source ( ) { if ( is_callable ( $ this -> source ) ) { return call_user_func_array ( $ this -> source , [ $ this -> slug ] ) ; } if ( is_string ( $ this -> source ) && strpos ( $ this -> source , '#' ) !== false ) { $ source = explode ( '#' , $ this -> source ) ; if ( empty ( $ source [ 0 ] ) || empty ( $ source [ 1 ] ) ) { return $ this -> source ; } $ source [ 0 ] = new $ source [ 0 ] ( ) ; if ( method_exists ( $ source [ 0 ] , $ source [ 1 ] ) ) { return call_user_func_array ( $ source , [ $ this -> slug ] ) ; } return ; } return $ this -> source ; }


public function setup_source( $value ) {
		if ( is_array( $value ) && count( $value ) === 2 && is_object( $value[0] ) && is_string( $value[1] ) ) {
			return sprintf( '%s#%s', get_class( $value[0] ), $value[1] );
		}

		if ( is_string( $value ) && is_callable( $value ) ) {
			return $value;
		}

		// No support for closure.
		if ( is_object( $value ) && $value instanceof Closure ) {
			return '';
		}

		return $value;
	}

public
function
setup_source
(
$
value
)
{
if
(
is_array
(
$
value
)
&&
count
(
$
value
)
===
2
&&
is_object
(
$
value
[
0
]
)
&&
is_string
(
$
value
[
1
]
)
)
{
return
sprintf
(
'%s#%s'
,
get_class
(
$
value
[
0
]
)
,
$
value
[
1
]
)
;
}
if
(
is_string
(
$
value
)
&&
is_callable
(
$
value
)
)
{
return
$
value
;
}
// No support for closure.
if
(
is_object
(
$
value
)
&&
$
value
instanceof
Closure
)
{
return
''
;
}
return
$
value
;
}

Setup source callable.

@param  string $value

@return string


protected function setup( array $rule ) {
		foreach ( $rule as $key => $value ) {
			if ( $key === 'operator' ) {
				$value = strtoupper( $value );
				$value = html_entity_decode( $value );
			} else if ( $key === 'slug' ) {
				$value = papify( $value );
			} else if ( $key === 'source' ) {
				$value = $this->setup_source( $value );
			}

			$this->$key = $value;
		}
	}

protected function setup ( array $ rule ) { foreach ( $ rule as $ key => $ value ) { if ( $ key === 'operator' ) { $ value = strtoupper ( $ value ) ; $ value = html_entity_decode ( $ value ) ; } else if ( $ key === 'slug' ) { $ value = papify ( $ value ) ; } else if ( $ key === 'source' ) { $ value = $ this -> setup_source ( $ value ) ; } $ this -> $ key = $ value ; } }

Setup the rule and assign properties with values.

@param array  $rule

Setup the rule and assign properties with values .


public function admin_init() {
		$meta_type  = papi_get_meta_type();
		$meta_type  = ucfirst( $meta_type );
		$class_name = 'Papi_Admin_Entry_' . $meta_type;

		// A custom class is not required, e.g
		// options don't have one.
		if ( class_exists( $class_name ) ) {
			$class = call_user_func( [$class_name, 'instance'] );

			if ( ! $class->setup() ) {
				return;
			}
		}

		// Setup entry type.
		if ( $entry_type = $this->get_entry_type() ) {
			$entry_type->setup();
		}
	}

public
function
admin_init
(
)
{
$
meta_type
=
papi_get_meta_type
(
)
;
$
meta_type
=
ucfirst
(
$
meta_type
)
;
$
class_name
=
'Papi_Admin_Entry_'
.
$
meta_type
;
// A custom class is not required, e.g
// options don't have one.
if
(
class_exists
(
$
class_name
)
)
{
$
class
=
call_user_func
(
[
$
class_name
,
'instance'
]
)
;
if
(
!
$
class
->
setup
(
)
)
{
return
;
}
}
// Setup entry type.
if
(
$
entry_type
=
$
this
->
get_entry_type
(
)
)
{
$
entry_type
->
setup
(
)
;
}
}

Preboot all types and setup the current type if any.

Preboot all types and setup the current type if any .


public function admin_body_class( $classes ) {
		if ( $entry_type = $this->get_entry_type() ) {
			$classes .= sprintf( ' papi-body papi-meta-type-%s', papi_get_meta_type() );

			// Add custom css classes from entry type.
			$arr = $entry_type->get_body_classes();
			$arr = is_string( $arr ) ? [ $arr ] : $arr;
			$arr = is_array( $arr ) ? $arr : [];

			$classes .= ' ' . implode( ' ', $arr );
		}

		// Add custom css classes from query string.
		if ( $css = papi_get_qs( 'papi_css' ) ) {
			$css = is_array( $css ) ? $css : [];
			$css = array_map( 'sanitize_text_field', $css );
			$classes .= ' ' . implode( ' ', $css );
		}

		return $classes;
	}

Add custom body class when it's a page type.

@param  string $classes

@return string

Add custom body class when it s a page type .


public function edit_form_after_title() {
		wp_nonce_field( 'papi_save_data', 'papi_meta_nonce' );

		if ( $value = esc_attr( papi_get_entry_type_id() ) ) {
			papi_render_html_tag( 'input', [
				'data-papi-page-type-key' => true,
				'name'                    => esc_attr( papi_get_page_type_key() ),
				'type'                    => 'hidden',
				'value'                   => $value
			] );
		}
	}

public function edit_form_after_title ( ) { wp_nonce_field ( 'papi_save_data' , 'papi_meta_nonce' ) ; if ( $ value = esc_attr ( papi_get_entry_type_id ( ) ) ) { papi_render_html_tag ( 'input' , [ 'data-papi-page-type-key' => true , 'name' => esc_attr ( papi_get_page_type_key ( ) ) , 'type' => 'hidden' , 'value' => $ value ] ) ; } }


protected function get_entry_type() {
		if ( $this->entry_type instanceof Papi_Entry_Type ) {
			return $this->entry_type;
		}

		$entry_type_id = papi_get_entry_type_id();

		// If the entry type id is empty try to load
		// the entry type id from `page` query string.
		//
		// Example:
		//   /wp-admin/options-general.php?page=papi/option/site-option-type
		if ( empty( $entry_type_id ) ) {
			$entry_type_id = preg_replace( '/^papi\/\w+\//', '', papi_get_qs( 'page' ) );
		}

		// Use the default entry type id if empty.
		if ( empty( $entry_type_id ) ) {
			$entry_type_id = papi_get_entry_type_id();
		}

		// If no entry type id exists Papi can't setup a entry type.
		if ( empty( $entry_type_id ) ) {
			return false;
		}

		$entry_type = papi_get_entry_type_by_id( $entry_type_id );

		if ( $entry_type instanceof Papi_Entry_Type === false ) {
			return false;
		}

		$this->entry_type = $entry_type;

		return $entry_type;
	}

Get Entry Type instance.

@return Papi_Entry_Type|false


public function plugin_row_meta( array $links, $file ) {
		if ( $file === PAPI_PLUGIN_BASENAME ) {
			return array_merge( $links, [
				'docs' => '' . __( 'Docs', 'papi' ) . '',
			] );
		}

		return $links;
	}

public
function
plugin_row_meta
(
array
$
links
,
$
file
)
{
if
(
$
file
===
PAPI_PLUGIN_BASENAME
)
{
return
array_merge
(
$
links
,
[
'docs'
=>
''
.
__
(
'Docs'
,
'papi'
)
.
''
,
]
)
;
}
return
$
links
;
}

Add docs links to plugin row meta.

@param  array  $links
@param  string $file

@return array


public function update_front_page( $value, $option ) {
		if ( $option !== 'page_on_front' ) {
			return $value;
		}

		$front_pages = papi_get_all_entry_types( [
			'types' => ['front-page'],
		] );

		if ( count( $front_pages ) === 0 ) {
			return $value;
		}

		$old_page_type_id = papi_get_page_type_id( $value );
		$new_page_type_id = $front_pages[0]->get_id();

		// Delete all old fields from old page type if any.
		if ( strtolower( $old_page_type_id ) !== strtolower( $new_page_type_id ) ) {
			if ( $page_type = papi_get_entry_type_by_id( $old_page_type_id ) ) {
				$slugs = array_map( function( $property ) {
					return $property->get_slug( true );
				}, $page_type->get_properties() );

				foreach ( $slugs as $slug ) {
					papi_delete_field( $value, $slug );
				}
			}
		}

		papi_set_page_type_id( $value, $new_page_type_id );

		return $value;
	}

Update page type to front page type when updating
which page that should be used as front page.

@param  mixed  $value
@param  string $option

@return mixed

Update page type to front page type when updating which page that should be used as front page .


public function wp_link_query( array $results ) {
		foreach ( $results as $index => $value ) {
			$post_type = papi_get_post_type( $value['ID'] );
			$name      = papi_get_page_type_name( $value['ID'] );

			if ( empty( $name ) ) {
				$name = papi_filter_settings_standard_page_type_name( $post_type );
			}

			$results[$index]['info'] = esc_html( $name );
		}

		return $results;
	}

Filter the link query results.

@param  array $results

@return array


protected function get_templates( $id ) {
		if ( empty( $id ) && ! is_numeric( $id ) ) {
			return [];
		}

		if ( $data = papi_get_entry_type_by_meta_id( $id ) ) {
			$templates = papi_to_array( $data->template );

			ksort( $templates );

			return $templates;
		}

		return [];
	}

protected function get_templates ( $ id ) { if ( empty ( $ id ) && ! is_numeric ( $ id ) ) { return [ ] ; } if ( $ data = papi_get_entry_type_by_meta_id ( $ id ) ) { $ templates = papi_to_array ( $ data -> template ) ; ksort ( $ templates ) ; return $ templates ; } return [ ] ; }

Get module templates by post id.

@param  int $id

@return array


protected function prepare_load_value( Papi_Core_Property $property, $value ) {
		if ( $property->overwrite ) {
			// Clear post cache to solve issue with cached post objects
			// when selecting post field.
			clean_post_cache( $this->id );

			$slug    = $property->get_slug( true );
			$context = papi_is_admin() ? 'edit' : 'display';
			$value   = get_post_field( $slug, $this->id, $context );
		}

		return $value;
	}

Prepare load value.

@param  Papi_Core_Property $property
@param  mixed              $value

@return mixed


public function save_properties() {
		if ( $_SERVER ['REQUEST_METHOD'] !== 'POST' || papi_get_meta_type() !== 'option' ) {
			return;
		}

		// Check if our nonce is vailed.
		if ( ! wp_verify_nonce( papi_get_sanitized_post( 'papi_meta_nonce' ), 'papi_save_data' ) ) {
			return;
		}

		// Get properties data.
		$data = $this->get_post_data();

		// Prepare properties data.
		$data = $this->prepare_properties_data( $data, 0 );

		foreach ( $data as $key => $value ) {
			papi_data_update( 0, $key, $value, 'option' );
		}

		/**
		 * Fire `save_properties` action when all is done.
		 *
		 * @param int    $id
		 * @param string $meta_type
		 */
		do_action( 'papi/save_properties', 0, 'option' );
	}

Save properties with a post id of zero .


public function metabox() {
		$post_type     = papi_get_post_type();
		$page_type     = papi_get_entry_type_by_id( papi_get_page_type_id() );
		$page_type_key = papi_get_page_type_key( 'switch' );
		$page_types    = papi_get_all_page_types( $post_type );
		$show_standard = papi_filter_settings_show_standard_page_type( $post_type );

		if ( $show_standard ) {
			$standard_page_type = papi_get_standard_page_type( $post_type );
			$page_types[]       = $standard_page_type;

			if ( empty( $page_type ) ) {
				$page_type = $standard_page_type;
			}
		}

		usort( $page_types, function ( $a, $b ) {
			return strcmp( $a->name, $b->name );
		} );

		$page_types = papi_sort_order( array_reverse( $page_types ) );

		// Don't do anything without any page types.
		if ( empty( $page_type ) || empty( $page_types ) ) {
			return;
		} ?>

			<label for="<?php echo esc_attr( $page_type_key ); ?>"><?php esc_html_e( 'Page Type:', 'papi' ); ?>
			<?php echo esc_html( $page_type->name ); ?>

			<?php if ( papi_current_user_is_allowed( $page_type->capabilities ) && $page_type->switcher ): ?>
				<?php esc_html_e( 'Edit', 'papi' ); ?>

					<select name="<?php echo esc_attr( $page_type_key ); ?>" id="<?php echo esc_attr( $page_type_key ); ?>">
						<?php
						foreach ( $page_types as $pt ) {
							if ( ! papi_current_user_is_allowed( $pt->capabilities ) ) {
								continue;
							}

							papi_render_html_tag( 'option', [
								'selected' => $page_type->match_id( $pt->get_id() ),
								'value'    => esc_attr( $pt->get_id() ),

								esc_html( $pt->name )
							] );
						}
						?>
					</select>
					<?php esc_html_e( 'OK', 'papi' ); ?>
					<?php esc_html_e( 'Cancel', 'papi' ); ?>

			<?php endif; ?>

		<?php
	}

?>

		<div class="misc-pub-section misc-pub-section-last papi-page-type-switcher">
			<label for="<?php

?>
				<a href="#" id="papi-page-type-switcher-edit" class="hide-if-no-js"><?php

?>
					</select>
					<a href="#" id="papi-page-type-switcher-save" class="hide-if-no-js button"><?php


public function save_post( $post_id, $post ) {
		// Check if post id and post object is empty or not.
		if ( empty( $post_id ) || empty( $post ) ) {
			return false;
		}

		// Check if our nonce is vailed.
		if ( ! wp_verify_nonce( papi_get_sanitized_post( 'papi_meta_nonce' ), 'papi_save_data' ) ) {
			return false;
		}

		// Check if so both page type keys exists.
		if ( empty( $_POST[papi_get_page_type_key()] ) || empty( $_POST[papi_get_page_type_key( 'switch' )] ) ) {
			return false;
		}

		// Page type information.
		$page_type_id        = sanitize_text_field( $_POST[papi_get_page_type_key()] );
		$page_type_switch_id = sanitize_text_field( $_POST[papi_get_page_type_key( 'switch' )] );

		// Don't update if the same ids.
		if ( $page_type_id === $page_type_switch_id ) {
			return false;
		}

		// Fetch right page type if standard page type id.
		if ( papi_get_standard_page_type_id( $post->post_type ) === $page_type_id ) {
			$page_type = papi_get_standard_page_type( $post->post_type );
		} else {
			$page_type = papi_get_entry_type_by_id( $page_type_id );
		}

		// Fetch right page type switch if standard page type id.
		if ( papi_get_standard_page_type_id( $post->post_type ) === $page_type_switch_id ) {
			$page_type_switch    = papi_get_standard_page_type( $post->post_type );
			$page_type_switch_id = '';
		} else {
			$page_type_switch = papi_get_entry_type_by_id( $page_type_switch_id );
		}

		$post_type_object = get_post_type_object( $post->post_type );

		// Check if page type and post type is not empty.
		if ( empty( $page_type_switch ) || empty( $post_type_object ) ) {
			return false;
		}

		// Check if autosave.
		if ( wp_is_post_autosave( $post_id ) ) {
			return false;
		}

		// Check if revision.
		if ( wp_is_post_revision( $post_id ) ) {
			return false;
		}

		// Check if revision post type.
		if ( in_array( $post->post_type, ['revision', 'nav_menu_item'], true ) ) {
			return false;
		}

		// Check so page type has the post type.
		if ( ! $page_type->has_post_type( $post->post_type ) || ! $page_type_switch->has_post_type( $post->post_type ) ) {
			return false;
		}

		// Check page type capabilities.
		if ( ! papi_current_user_is_allowed( $page_type_switch->capabilities ) ) {
			return false;
		}

		// Check so user can edit posts and that the user can publish posts on the post type.
		if ( ! current_user_can( 'edit_post', $post_id ) || ! current_user_can( $post_type_object->cap->publish_posts ) ) {
			return false;
		}

		// Get properties.
		$properties        = $page_type->get_properties();
		$properties_switch = $page_type_switch->get_properties();

		// Delete only properties that don't have the same type and slug.
		foreach ( $properties as $property ) {
			$delete = true;

			// Check if the properties are the same or not.
			foreach ( $properties_switch as $property_switch ) {
				if ( $property_switch->type === $property->type && $property_switch->match_slug( $property->get_slug() ) ) {
					$delete = false;
					break;
				}
			}

			if ( ! $delete ) {
				continue;
			}

			// Delete property values.
			$property->delete_value( $property->get_slug( true ), $post_id, papi_get_meta_type() );
		}

		// Delete page type switch id.
		if ( empty( $page_type_switch_id ) ) {
			return delete_post_meta( $post_id, papi_get_page_type_key() );
		}

		// Update page type id.
		return papi_set_page_type_id( $post_id, $page_type_switch_id );
	}

Switch page type if all checks pass.

@param  int     $post_id
@param  WP_post $post

@return bool


protected function overwrite_post_data( $post_id ) {
		global $wpdb;

		if ( empty( $post_id ) || empty( $this->overwrite ) ) {
			return;
		}

		$wpdb->update( $wpdb->posts, $this->overwrite, [
			'ID' => $post_id
		] );

		// Delete cache since it can be cached even if not saved in the database.
		foreach ( array_keys( $this->overwrite ) as $key ) {
			papi_cache_delete( $key, $post_id );
		}

		clean_post_cache( $post_id );
	}

Overwrite post data in `posts` table.

@param int $post_id


protected function pre_save( $id ) {
		if ( empty( $id ) ) {
			return;
		}

		$data = $this->get_pre_data();

		foreach ( $data as $key => $value ) {
			if ( empty( $value ) ) {
				continue;
			}

			if ( is_array( $value ) ) {
				list( $keys, $value ) = $this->get_pre_deep_keys_value( $value );
				$key = sprintf( '%s_%s', $key, implode( '_', $keys ) );
			}

			update_metadata( $this->get_meta_type(), $id, $key, $value );
		}
	}

protected function pre_save ( $ id ) { if ( empty ( $ id ) ) { return ; } $ data = $ this -> get_pre_data ( ) ; foreach ( $ data as $ key => $ value ) { if ( empty ( $ value ) ) { continue ; } if ( is_array ( $ value ) ) { list ( $ keys , $ value ) = $ this -> get_pre_deep_keys_value ( $ value ) ; $ key = sprintf ( '%s_%s' , $ key , implode ( '_' , $ keys ) ) ; } update_metadata ( $ this -> get_meta_type ( ) , $ id , $ key , $ value ) ; } }

Pre save page template and page type.

@param int $id


public function save_meta_boxes( $id, $post = null ) {
		// Check if there was a multisite switch before.
		if ( is_multisite() && ms_is_switched() ) {
			return;
		}

		// Can't proceed without a id.
		if ( empty( $id ) ) {
			return;
		}

		// Check if our nonce is vailed.
		if ( ! wp_verify_nonce( papi_get_sanitized_post( 'papi_meta_nonce' ), 'papi_save_data' ) ) {
			return;
		}

		$meta_type = $this->get_meta_type();
		$post      = is_array( $post ) ? (object) $post : $post;

		if ( $meta_type === 'post' && $post_type = get_post_type_object( $post->post_type ) ) {
			// Check so the id is a post id and not a autosave post.
			if ( ! $this->valid_post_id( $id ) ) {
				return;
			}

			// Check the `edit_posts` capability before we continue.
			if ( ! current_user_can( $post_type->cap->edit_posts ) ) {
				return;
			}

			// Delete all oEmbed caches.
			if ( class_exists( 'WP_Embed' ) ) {
				global $wp_embed;

				if ( $wp_embed instanceof WP_Embed ) {
					$wp_embed->delete_oembed_caches( $id );
				}
			}
		}

		if ( $meta_type === 'term' && $taxonomy = get_taxonomy( papi_get_taxonomy() ) ) {
			// Check the `edit_terms` capability before we continue.
			if ( $taxonomy && ! current_user_can( $taxonomy->cap->edit_terms ) ) {
				return;
			}
		}

		$this->save_properties( $id );
	}

Save meta boxes.

@param int     $id
@param object  $post


public function save_revision( $revision_id ) {
		// Check if our nonce is vailed.
		if ( ! wp_verify_nonce( papi_get_sanitized_post( 'papi_meta_nonce' ), 'papi_save_data' ) ) {
			return;
		}

		// Fetch parent id from revision.
		if ( ! $parent_id = wp_is_post_revision( $revision_id ) ) {
			return;
		}

		// Bail if a entry type don't exists on parent post.
		if ( papi_is_empty( papi_get_entry_type_by_meta_id( $parent_id, 'post' ) ) ) {
			return;
		}

		$meta = get_post_meta( $parent_id );

		foreach ( $meta as $key => $value ) {
			if ( $key[0] === '_' && $key !== papi_get_page_type_key() ) {
				continue;
			}

			papi_data_update( $revision_id, $key, array_shift( $value ) );
		}
	}

Save custom fields for revision post.

@param int $post_id


public function save_properties( $id ) {
		// Pre save page template, page type and some others dynamic values.
		$this->pre_save( $id );

		// Get properties data.
		$data = $this->get_post_data();

		// Prepare properties data.
		$data = $this->prepare_properties_data( $data, $id );

		// Get meta type.
		$meta_type = $this->get_meta_type();

		// Overwrite post data if any.
		if ( $meta_type === 'post' ) {
			$this->overwrite_post_data( $id );
		}

		// Save all properties value
		foreach ( $data as $key => $value ) {
			papi_data_update( $id, $key, $value, $this->get_meta_type() );
		}

		/**
		 * Fire `save_properties` action when all is done.
		 *
		 * @param int    $id
		 * @param string $meta_type
		 */
		do_action( 'papi/save_properties', $id, $meta_type );
	}


public function restore_post_revision( $post_id, $revision_id ) {
		if ( papi_is_empty( papi_get_entry_type_by_meta_id( $post_id, 'post' ) ) ) {
			return;
		}

		$meta = get_post_meta( $revision_id );

		foreach ( $meta as $key => $value ) {
			if ( $key[0] === '_' && $key !== papi_get_page_type_key() ) {
				continue;
			}

			papi_data_update( $post_id, $key, array_shift( $value ) );
		}
	}

public function restore_post_revision ( $ post_id , $ revision_id ) { if ( papi_is_empty ( papi_get_entry_type_by_meta_id ( $ post_id , 'post' ) ) ) { return ; } $ meta = get_post_meta ( $ revision_id ) ; foreach ( $ meta as $ key => $ value ) { if ( $ key [ 0 ] === '_' && $ key !== papi_get_page_type_key ( ) ) { continue ; } papi_data_update ( $ post_id , $ key , array_shift ( $ value ) ) ; } }

Restore to post revision.

@param int $post_id
@param int $revision_id


protected function valid_post_id( $post_id ) {
		$key = papi_get_sanitized_post( 'action' ) === 'save-attachment-compat' ? 'id' : 'post_ID';
		$val = papi_get_sanitized_post( $key );

		// When autosave is in place the post id is located deeper in the post data array, the ids should not match.
		if ( isset( $_POST['data']['wp_autosave'], $_POST['data']['wp_autosave']['post_id'] ) ) {
			// But if it's a auto draft the ids should match.
			if ( isset( $_POST['data']['wp_autosave']['auto_draft'] ) && ! empty( $_POST['data']['wp_autosave']['auto_draft'] ) ) {
				return sanitize_text_field( $_POST['data']['wp_autosave']['post_id'] ) === strval( $post_id );
			}

			return sanitize_text_field( $_POST['data']['wp_autosave']['post_id'] ) !== strval( $post_id );
		}

		// Should not be the same id when `wp-preview` equals `dopreview`.
		if ( isset( $_POST['wp-preview'] ) && strtolower( $_POST['wp-preview'] ) === 'dopreview' ) {
			return $val !== strval( $post_id );
		}

		return $val === strval( $post_id );
	}

Check if post id is valid or not.

@param  int $post_id

@return bool


public function display( array $rules, $property = null ) {
		if ( empty( $rules ) ) {
			return true;
		}

		$rules  = $this->prepare_rules( $rules, $property );

		if ( in_array( $rules['relation'], $this->relations, true ) ) {
			return $this->display_by_relation( $rules );
		}

		return true;
	}

public function display ( array $ rules , $ property = null ) { if ( empty ( $ rules ) ) { return true ; } $ rules = $ this -> prepare_rules ( $ rules , $ property ) ; if ( in_array ( $ rules [ 'relation' ] , $ this -> relations , true ) ) { return $ this -> display_by_relation ( $ rules ) ; } return true ; }

Check if the property should be displayed by the rules.

@param  array $rules
@param  Papi_Core_Property $property

@return bool

Check if the property should be displayed by the rules .


protected function display_by_relation( array $rules ) {
		if ( $rules['relation'] === 'AND' ) {
			$display = true;

			foreach ( $rules as $rule ) {
				if ( ! $display ) {
					break;
				}

				if ( papi_is_rule( $rule ) ) {
					/**
					 * Modify rule allowed.
					 *
					 * @param  bool $result
					 * @param  Papi_Core_Conditional_Rule $rule
					 *
					 * @return bool
					 */
					$display = apply_filters( 'papi/conditional/rule_allowed', papi_filter_conditional_rule_allowed( $rule ), $rule );
				}
			}

			return $display;
		}

		$empty = array_filter( $rules, function ( $rule ) {
			return papi_is_rule( $rule ) ? true : null;
		} );

		if ( empty( $empty ) ) {
			return true;
		}

		$result = [];

		foreach ( $rules as $rule ) {
			if ( papi_is_rule( $rule ) ) {
				/**
				 * Modify rule allowed.
				 *
				 * @param  bool $result
				 * @param  Papi_Core_Conditional_Rule $rule
				 *
				 * @return bool
				 */
				$result[] = apply_filters( 'papi/conditional/rule_allowed', papi_filter_conditional_rule_allowed( $rule ), $rule );
			}
		}

		$result = array_filter( $result, function ( $res ) {
			return $res === true ? true : null;
		} );

		return ! empty( $result );
	}

Get the display by relation.

@param  array $rules

@return bool


protected function get_rule_slug( $rule, $property ) {
		$arr_reg = '/\[\d+\](\[\w+\])$/';
		$slug    = $property->get_slug();

		$page_type = papi_get_entry_type_by_meta_id();

		if ( $page_type instanceof Papi_Page_Type === false ) {
			return $rule->slug;
		}

		if ( preg_match( $arr_reg, $slug, $out ) ) {
			$slug     = str_replace( $out[1], '[' . unpapify( $rule->slug ) . ']', $slug );
			$property = $page_type->get_property( $slug );

			if ( papi_is_property( $property ) ) {
				return $slug;
			}
		}

		return $rule->slug;
	}

protected function get_rule_slug ( $ rule , $ property ) { $ arr_reg = '/\[\d+\](\[\w+\])$/' ; $ slug = $ property -> get_slug ( ) ; $ page_type = papi_get_entry_type_by_meta_id ( ) ; if ( $ page_type instanceof Papi_Page_Type === false ) { return $ rule -> slug ; } if ( preg_match ( $ arr_reg , $ slug , $ out ) ) { $ slug = str_replace ( $ out [ 1 ] , '[' . unpapify ( $ rule -> slug ) . ']' , $ slug ) ; $ property = $ page_type -> get_property ( $ slug ) ; if ( papi_is_property ( $ property ) ) { return $ slug ; } } return $ rule -> slug ; }

Get rule slug.

@param  Papi_Core_Conditional_Rule $rule
@param  Papi_Core_Property $property

@return string


public function prepare_rules( array $rules, $property = null ) {
		if ( ! isset( $rules['relation'] ) ) {
			$rules['relation'] = 'OR';
		} else {
			$rules['relation'] = strtoupper( $rules['relation'] );
		}

		foreach ( $rules as $index => $value ) {
			if ( is_string( $index ) ) {
				continue;
			}

			if ( is_array( $value ) ) {
				$rules[$index] = new Papi_Core_Conditional_Rule( $value );

				if ( strpos( $rules[$index]->slug, '.' ) === false && papi_is_property( $property ) ) {
					$rules[$index]->slug = $this->get_rule_slug(
						$rules[$index],
						$property
					);
				}
			}
		}

		return $rules;
	}

public function prepare_rules ( array $ rules , $ property = null ) { if ( ! isset ( $ rules [ 'relation' ] ) ) { $ rules [ 'relation' ] = 'OR' ; } else { $ rules [ 'relation' ] = strtoupper ( $ rules [ 'relation' ] ) ; } foreach ( $ rules as $ index => $ value ) { if ( is_string ( $ index ) ) { continue ; } if ( is_array ( $ value ) ) { $ rules [ $ index ] = new Papi_Core_Conditional_Rule ( $ value ) ; if ( strpos ( $ rules [ $ index ] -> slug , '.' ) === false && papi_is_property ( $ property ) ) { $ rules [ $ index ] -> slug = $ this -> get_rule_slug ( $ rules [ $ index ] , $ property ) ; } } } return $ rules ; }

Prepare rules.

@param  array $rules
@param  Papi_Core_Property $property

@return array


public function allowed() {
		$args = func_get_args();

		if ( empty( $args ) ) {
			return parent::allowed();
		}

		return papi_current_user_is_allowed( $this->capabilities ) && isset( $args[0] ) && in_array( $args[0], $this->post_type, true );
	}

public function allowed ( ) { $ args = func_get_args ( ) ; if ( empty ( $ args ) ) { return parent :: allowed ( ) ; } return papi_current_user_is_allowed ( $ this -> capabilities ) && isset ( $ args [ 0 ] ) && in_array ( $ args [ 0 ] , $ this -> post_type , true ) ; }

Determine if the page type is allowed by capabilities and post type.

@return bool

Determine if the page type is allowed by capabilities and post type .


public function get_body_classes() {
		$classes = parent::get_body_classes();

		if ( ! $this->show_permalink ) {
			$classes[] = 'papi-hide-edit-slug-box';
		}

		if ( ! $this->show_page_attributes ) {
			$classes[] = 'papi-hide-pageparentdiv';
		}

		return $classes;
	}

public function get_body_classes ( ) { $ classes = parent :: get_body_classes ( ) ; if ( ! $ this -> show_permalink ) { $ classes [ ] = 'papi-hide-edit-slug-box' ; } if ( ! $ this -> show_page_attributes ) { $ classes [ ] = 'papi-hide-pageparentdiv' ; } return $ classes ; }


public function get_child_types() {
		$child_types = [];

		foreach ( papi_to_array( $this->child_types ) as $id ) {
			$child_type = papi_get_entry_type_by_id( $id );

			if ( $child_type instanceof Papi_Page_Type ) {
				$child_types[] = $child_type;
			}
		}

		return $child_types;
	}

public function get_child_types ( ) { $ child_types = [ ] ; foreach ( papi_to_array ( $ this -> child_types ) as $ id ) { $ child_type = papi_get_entry_type_by_id ( $ id ) ; if ( $ child_type instanceof Papi_Page_Type ) { $ child_types [ ] = $ child_type ; } } return $ child_types ; }

Get child page types that lives under the current page type.

@return array

Get child page types that lives under the current page type .


public function get_labels() {
		if ( ! $this->fill_labels ) {
			return $this->labels;
		}

		return array_merge( $this->labels, [
			'add_new_item' => sprintf( '%s %s', __( 'Add New', 'papi' ), $this->name ),
			'edit_item'    => sprintf( '%s %s', __( 'Edit', 'papi' ), $this->name ),
			'view_item'    => sprintf( '%s %s', __( 'View', 'papi' ), $this->name )
		] );
	}

public
function
get_labels
(
)
{
if
(
!
$
this
->
fill_labels
)
{
return
$
this
->
labels
;
}
return
array_merge
(
$
this
->
labels
,
[
'add_new_item'
=>
sprintf
(
'%s %s'
,
__
(
'Add New'
,
'papi'
)
,
$
this
->
name
)
,
'edit_item'
=>
sprintf
(
'%s %s'
,
__
(
'Edit'
,
'papi'
)
,
$
this
->
name
)
,
'view_item'
=>
sprintf
(
'%s %s'
,
__
(
'View'
,
'papi'
)
,
$
this
->
name
)
]
)
;
}

Get labels that should be changed
when using `fill_labels` option.

@return array

Get labels that should be changed when using fill_labels option .


protected function get_post_type_supports() {
		$supports = ['custom-fields'];

		if ( method_exists( $this, 'remove' ) ) {
			$output   = $this->remove();
			$output   = is_string( $output ) ? [$output] : $output;
			$output   = is_array( $output ) ? $output : [];
			$output   = array_filter( $output, 'is_string' );
			$supports = array_merge( $supports, $output );
		}

		$parent_class  = get_parent_class( $this );
		$parent_remove = method_exists( $parent_class, 'remove' );

		while ( $parent_remove ) {
			$parent        = new $parent_class();
			$output        = $parent->remove();
			$output        = is_string( $output ) ? [$output] : $output;
			$output        = is_array( $output ) ? $output : [];
			$output        = array_filter( $output, 'is_string' );
			$supports      = array_merge( $supports, $output );
			$parent_class  = get_parent_class( $parent_class );
			$parent_remove = method_exists( $parent_class, 'remove' );
		}

		return $supports;
	}

protected function get_post_type_supports ( ) { $ supports = [ 'custom-fields' ] ; if ( method_exists ( $ this , 'remove' ) ) { $ output = $ this -> remove ( ) ; $ output = is_string ( $ output ) ? [ $ output ] : $ output ; $ output = is_array ( $ output ) ? $ output : [ ] ; $ output = array_filter ( $ output , 'is_string' ) ; $ supports = array_merge ( $ supports , $ output ) ; } $ parent_class = get_parent_class ( $ this ) ; $ parent_remove = method_exists ( $ parent_class , 'remove' ) ; while ( $ parent_remove ) { $ parent = new $ parent_class ( ) ; $ output = $ parent -> remove ( ) ; $ output = is_string ( $ output ) ? [ $ output ] : $ output ; $ output = is_array ( $ output ) ? $ output : [ ] ; $ output = array_filter ( $ output , 'is_string' ) ; $ supports = array_merge ( $ supports , $ output ) ; $ parent_class = get_parent_class ( $ parent_class ) ; $ parent_remove = method_exists ( $ parent_class , 'remove' ) ; } return $ supports ; }

Get post type supports that will be removed.

@return array

Get post type supports that will be removed .


public function remove_post_type_support() {
		global $_wp_post_type_features;

		$post_type = $this->get_post_type();

		if ( empty( $post_type ) ) {
			return;
		}

		$post_type_supports = $this->get_post_type_supports();

		foreach ( $post_type_supports as $key => $value ) {
			if ( is_numeric( $key ) ) {
				$key = $value;
				$value = '';
			}

			if ( isset( $_wp_post_type_features[$post_type], $_wp_post_type_features[$post_type][$key] ) ) {
				unset( $_wp_post_type_features[$post_type][$key] );
				continue;
			}

			if ( in_array( strtolower( $key ), ['all', 'normal', 'side', 'advanced'], true ) ) {
				$value = strtolower( $key );
			} else if ( empty( $value ) ) {
				$value = 'normal';
			}

			$this->remove_meta_boxes[] = [$key, $value];
		}

		add_action( 'add_meta_boxes', [$this, 'remove_meta_boxes'], 999 );
	}

public function remove_post_type_support ( ) { global $ _wp_post_type_features ; $ post_type = $ this -> get_post_type ( ) ; if ( empty ( $ post_type ) ) { return ; } $ post_type_supports = $ this -> get_post_type_supports ( ) ; foreach ( $ post_type_supports as $ key => $ value ) { if ( is_numeric ( $ key ) ) { $ key = $ value ; $ value = '' ; } if ( isset ( $ _wp_post_type_features [ $ post_type ] , $ _wp_post_type_features [ $ post_type ] [ $ key ] ) ) { unset ( $ _wp_post_type_features [ $ post_type ] [ $ key ] ) ; continue ; } if ( in_array ( strtolower ( $ key ) , [ 'all' , 'normal' , 'side' , 'advanced' ] , true ) ) { $ value = strtolower ( $ key ) ; } else if ( empty ( $ value ) ) { $ value = 'normal' ; } $ this -> remove_meta_boxes [ ] = [ $ key , $ value ] ; } add_action ( 'add_meta_boxes' , [ $ this , 'remove_meta_boxes' ] , 999 ) ; }


public function remove_meta_boxes() {
		global $wp_meta_boxes;

		$post_type = $this->get_post_type();
		$context = [];

		foreach ( $this->remove_meta_boxes as $item ) {
			if ( $item[0] !== $item[1] ) {
				remove_meta_box( $item[0], $post_type, $item[1] );
				continue;
			}

			$context = $item[0];

			// Our special context.
			if ( $context === 'all' ) {
				$context = ['normal', 'side', 'advanced'];
			} else {
				$context = [strtolower( $context )];
			}

			foreach ( $context as $ctx ) {
				if ( ! isset( $wp_meta_boxes[$post_type], $wp_meta_boxes[$post_type][$ctx] ) ) {
					continue;
				}

				$meta_boxes = $wp_meta_boxes[$post_type][$ctx];

				if ( ! is_array( $meta_boxes ) ) {
					continue;
				}

				foreach ( $meta_boxes as $level ) {
					foreach ( $level as $id => $box ) {
						// Don't allow removing of papi boxes.
						if ( strpos( $id, '_papi' ) !== false ) {
							continue;
						}

						// Don't allow removing of submitdiv.
						if ( $id === 'submitdiv' ) {
							continue;
						}

						remove_meta_box( $id, $post_type, $ctx );
					}
				}
		}
		}

		// Special for editor in normal context.
		if ( in_array( 'normal', $context, true ) ) {
			remove_post_type_support( 'page', 'editor' );
		}
	}


public function setup() {
		parent::setup();

		// Remove post type support and meta boxes.
		$this->remove_post_type_support();

		// Add support for displaying information in publish box from a page type.
		if ( method_exists( $this, 'publish_box' ) ) {
			add_action( 'post_submitbox_misc_actions', [$this, 'publish_box'] );
		}

		// Hide page template dropdown if it shouldn't be showed.
		if ( ! $this->show_page_template ) {
			add_filter( 'theme_page_templates', '__return_empty_array' );
		}

		// Main title input placeholder.
		if ( ! empty( $this->labels['title_placeholder'] ) ) {
			add_filter( 'enter_title_here', function () {
				return $this->labels['title_placeholder'];
			} );
		}
	}


protected function setup_post_types() {
		$this->post_type = papi_to_array( $this->post_type );

		// Set a default value to post types array
		// if we don't have a array or a empty array.
		if ( empty( $this->post_type ) ) {
			$this->post_type = ['page'];
		}

		if ( count( $this->post_type ) === 1 && $this->post_type[0] === 'any' ) {
			$this->post_type = get_post_types( '', 'names' );
			$this->post_type = array_values( $this->post_type );
		}
	}


protected function setup_page_templates() {
		if ( ! is_array( $this->template ) || ! $this->has_post_type( papi_get_post_type() ) ) {
			return;
		}

		$this->show_page_template = true;
		foreach ( $this->post_type as $post_type ) {
			// Can't use `theme_templates` filter since it's added in 4.9.6.
			add_filter( "theme_{$post_type}_templates", function( $templates ) {
				foreach ( $this->template as $template ) {
					if ( ! is_array( $template ) || ! isset( $template['template'], $template['label'] ) ) {
						continue;
					}

					$templates[$template['template']] = $template['label'];
				}

				return $templates;
			} );
		}
	}

Setup page templates if templates is a array.

Setup page templates if templates is a array .


protected function add_mce_buttons() {
		for ( $i = 0; $i < 4; $i++ ) {
			$num = $i === 0 ? '' : '_' . ( $i + 1 );
			add_filter( 'mce_buttons' . $num, [$this, 'mce_buttons'] );
		}
	}

protected function add_mce_buttons ( ) { for ( $ i = 0 ; $ i < 4 ; $ i ++ ) { $ num = $ i === 0 ? '' : '_' . ( $ i + 1 ) ; add_filter ( 'mce_buttons' . $ num , [ $ this , 'mce_buttons' ] ) ; } }


protected function reove_mce_buttons() {
		for ( $i = 0; $i < 4; $i++ ) {
			$num = $i === 0 ? '' : '_' . ( $i + 1 );
			remove_filter( 'mce_buttons' . $num, [$this, 'mce_buttons'] );
		}
	}

protected function reove_mce_buttons ( ) { for ( $ i = 0 ; $ i < 4 ; $ i ++ ) { $ num = $ i === 0 ? '' : '_' . ( $ i + 1 ) ; remove_filter ( 'mce_buttons' . $ num , [ $ this , 'mce_buttons' ] ) ; } }

Remove filters that filter TinyMCE buttons.

Remove filters that filter TinyMCE buttons .