420 lines
10 KiB
PHP
420 lines
10 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* Add custom meta data to posts.
|
||
|
*
|
||
|
* @package visual-portfolio/admin
|
||
|
*/
|
||
|
|
||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||
|
exit;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Class Visual_Portfolio_Custom_Post_Meta
|
||
|
*/
|
||
|
class Visual_Portfolio_Custom_Post_Meta {
|
||
|
/**
|
||
|
* Visual_Portfolio_Custom_Post_Meta constructor.
|
||
|
*/
|
||
|
public static function init() {
|
||
|
// add post formats.
|
||
|
add_action( 'after_setup_theme', array( __CLASS__, 'add_extra_post_format' ), 99 );
|
||
|
add_action( 'init', array( __CLASS__, 'register_post_meta' ) );
|
||
|
add_action( 'add_meta_boxes', array( __CLASS__, 'add_post_format_metaboxes' ), 1 );
|
||
|
add_action( 'save_post', array( __CLASS__, 'save_post_format_metaboxes' ) );
|
||
|
add_action( 'save_post', array( __CLASS__, 'update_words_count' ) );
|
||
|
add_action( 'wp_head', array( __CLASS__, 'update_views_count' ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if current page is gutenberg.
|
||
|
*
|
||
|
* @return boolean
|
||
|
*/
|
||
|
public static function is_gutenberg() {
|
||
|
$current_screen = get_current_screen();
|
||
|
if ( method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor() ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add video post format.
|
||
|
*/
|
||
|
public static function add_extra_post_format() {
|
||
|
global $_wp_theme_features;
|
||
|
|
||
|
$formats = array( 'image', 'video' );
|
||
|
|
||
|
// Add existing formats.
|
||
|
if ( isset( $_wp_theme_features['post-formats'] ) && isset( $_wp_theme_features['post-formats'][0] ) ) {
|
||
|
$formats = array_merge( (array) $_wp_theme_features['post-formats'][0], $formats );
|
||
|
}
|
||
|
$formats = array_unique( $formats );
|
||
|
|
||
|
add_theme_support( 'post-formats', $formats );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Register post meta.
|
||
|
*/
|
||
|
public static function register_post_meta() {
|
||
|
$post_type_names = array_keys( get_post_types() );
|
||
|
|
||
|
foreach ( $post_type_names as $post_type ) {
|
||
|
if ( ! is_post_type_viewable( $post_type ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Register meta for all post types.
|
||
|
register_meta(
|
||
|
'post',
|
||
|
'_vp_format_video_url',
|
||
|
array(
|
||
|
'object_subtype' => $post_type,
|
||
|
'type' => 'string',
|
||
|
'single' => true,
|
||
|
'show_in_rest' => true,
|
||
|
'auth_callback' => array( __CLASS__, 'rest_auth' ),
|
||
|
)
|
||
|
);
|
||
|
register_meta(
|
||
|
'post',
|
||
|
'_vp_image_focal_point',
|
||
|
array(
|
||
|
'object_subtype' => $post_type,
|
||
|
'type' => 'object',
|
||
|
'single' => true,
|
||
|
'show_in_rest' => array(
|
||
|
'schema' => array(
|
||
|
'type' => 'object',
|
||
|
'properties' => array(
|
||
|
'x' => array(
|
||
|
'type' => 'number',
|
||
|
),
|
||
|
'y' => array(
|
||
|
'type' => 'number',
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
'auth_callback' => array( __CLASS__, 'rest_auth' ),
|
||
|
)
|
||
|
);
|
||
|
|
||
|
// Add support for 'custom-fields' to work in Gutenberg.
|
||
|
add_post_type_support( $post_type, 'custom-fields' );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Determines REST API authentication.
|
||
|
*
|
||
|
* @param bool $allowed Whether it is allowed.
|
||
|
* @param string $meta_key The meta key being checked.
|
||
|
* @param int $post_id The post ID being checked.
|
||
|
* @param int $user_id The user ID being checked.
|
||
|
* @param string $cap The current capability.
|
||
|
* @param array $caps All capabilities.
|
||
|
* @return bool Whether the user can do it.
|
||
|
*/
|
||
|
// phpcs:ignore
|
||
|
public static function rest_auth( $allowed, $meta_key, $post_id, $user_id, $cap, $caps ) {
|
||
|
return user_can( $user_id, 'edit_post', $post_id );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add post format metaboxes.
|
||
|
*
|
||
|
* @param string $post_type post type.
|
||
|
*/
|
||
|
public static function add_post_format_metaboxes( $post_type ) {
|
||
|
// Prevent if Gutenberg enabled.
|
||
|
if ( self::is_gutenberg() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Prevent if no Video post format supported.
|
||
|
if ( ! post_type_supports( $post_type, 'post-formats' ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
add_meta_box(
|
||
|
'vp_format_video',
|
||
|
esc_html__( 'Video', 'visual-portfolio' ),
|
||
|
array( __CLASS__, 'add_video_format_metabox' ),
|
||
|
null,
|
||
|
'side',
|
||
|
'default'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add Video Format metabox
|
||
|
*
|
||
|
* @param object $post The post object.
|
||
|
*/
|
||
|
public static function add_video_format_metabox( $post ) {
|
||
|
wp_nonce_field( basename( __FILE__ ), 'vp_format_video_nonce' );
|
||
|
|
||
|
$video_url = self::get_video_format_url( $post->ID );
|
||
|
$oembed_html = false;
|
||
|
|
||
|
$wpkses_iframe = array(
|
||
|
'iframe' => array(
|
||
|
'src' => array(),
|
||
|
'height' => array(),
|
||
|
'width' => array(),
|
||
|
'frameborder' => array(),
|
||
|
'allowfullscreen' => array(),
|
||
|
),
|
||
|
);
|
||
|
|
||
|
if ( $video_url ) {
|
||
|
$oembed = visual_portfolio()->get_oembed_data( $video_url );
|
||
|
|
||
|
if ( $oembed && isset( $oembed['html'] ) ) {
|
||
|
$oembed_html = $oembed['html'];
|
||
|
}
|
||
|
}
|
||
|
?>
|
||
|
|
||
|
<p></p>
|
||
|
<input class="vp-input" name="_vp_format_video_url" type="url" id="_vp_format_video_url" value="<?php echo esc_attr( $video_url ); ?>" placeholder="<?php echo esc_attr__( 'https://', 'visual-portfolio' ); ?>">
|
||
|
<div class="vp-oembed-preview">
|
||
|
<?php
|
||
|
if ( $oembed_html ) {
|
||
|
echo wp_kses( $oembed_html, $wpkses_iframe );
|
||
|
}
|
||
|
?>
|
||
|
</div>
|
||
|
<style>
|
||
|
#vp_format_video {
|
||
|
display: <?php echo has_post_format( 'video' ) ? 'block' : 'none'; ?>;
|
||
|
}
|
||
|
</style>
|
||
|
<?php
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Save Format metabox
|
||
|
*
|
||
|
* @param int $post_id The post ID.
|
||
|
*/
|
||
|
public static function save_post_format_metaboxes( $post_id ) {
|
||
|
if ( ! isset( $_POST['vp_format_video_nonce'] ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( ! wp_verify_nonce( sanitize_key( $_POST['vp_format_video_nonce'] ), basename( __FILE__ ) ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$meta = array(
|
||
|
'_vp_format_video_url',
|
||
|
);
|
||
|
|
||
|
foreach ( $meta as $item ) {
|
||
|
if ( isset( $_POST[ $item ] ) ) {
|
||
|
if ( is_array( $_POST[ $item ] ) ) {
|
||
|
$result = array_map( 'sanitize_text_field', wp_unslash( $_POST[ $item ] ) );
|
||
|
} else {
|
||
|
$result = sanitize_text_field( wp_unslash( $_POST[ $item ] ) );
|
||
|
}
|
||
|
|
||
|
update_post_meta( $post_id, $item, $result );
|
||
|
|
||
|
// remove old video meta.
|
||
|
if ( '_vp_format_video_url' === $item && get_post_meta( $post_id, 'video_url', true ) ) {
|
||
|
delete_post_meta( $post_id, 'video_url' );
|
||
|
}
|
||
|
} else {
|
||
|
update_post_meta( $post_id, $item, false );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get video format URL.
|
||
|
*
|
||
|
* @param int $post_id The post ID.
|
||
|
*/
|
||
|
public static function get_video_format_url( $post_id ) {
|
||
|
if ( ! $post_id ) {
|
||
|
$post_id = get_the_ID();
|
||
|
}
|
||
|
|
||
|
$video_url = get_post_meta( $post_id, '_vp_format_video_url', true );
|
||
|
|
||
|
// fallback.
|
||
|
if ( ! $video_url ) {
|
||
|
$video_url = get_post_meta( $post_id, 'video_url', true );
|
||
|
}
|
||
|
|
||
|
return $video_url;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get featured image focal point.
|
||
|
*
|
||
|
* @param int $post_id The post ID.
|
||
|
*/
|
||
|
public static function get_featured_image_focal_point( $post_id ) {
|
||
|
if ( ! $post_id ) {
|
||
|
$post_id = get_the_ID();
|
||
|
}
|
||
|
|
||
|
$focal_point = get_post_meta( $post_id, '_vp_image_focal_point', true );
|
||
|
|
||
|
if (
|
||
|
! isset( $focal_point ) ||
|
||
|
empty( $focal_point ) ||
|
||
|
! isset( $focal_point['x'] ) || ! isset( $focal_point['y'] ) ||
|
||
|
( '0.5' === $focal_point['x'] && '0.5' === $focal_point['y'] )
|
||
|
) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return $focal_point;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update views count.
|
||
|
*
|
||
|
* @param int $post_id The post ID.
|
||
|
*/
|
||
|
public static function update_views_count( $post_id ) {
|
||
|
if ( ! is_single() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( ! $post_id ) {
|
||
|
$post_id = get_the_ID();
|
||
|
}
|
||
|
|
||
|
$current_views = self::get_views_count( $post_id );
|
||
|
|
||
|
update_post_meta( $post_id, '_vp_views_count', $current_views + 1 );
|
||
|
|
||
|
// Support for https://wordpress.org/plugins/post-views-counter/ .
|
||
|
if ( function_exists( 'pvc_view_post' ) ) {
|
||
|
pvc_view_post( $post_id );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get views count.
|
||
|
*
|
||
|
* @param int $post_id The post ID.
|
||
|
*/
|
||
|
public static function get_views_count( $post_id ) {
|
||
|
if ( ! $post_id ) {
|
||
|
$post_id = get_the_ID();
|
||
|
}
|
||
|
|
||
|
// Support for https://wordpress.org/plugins/post-views-counter/ .
|
||
|
if ( function_exists( 'pvc_get_post_views' ) ) {
|
||
|
return pvc_get_post_views( $post_id );
|
||
|
}
|
||
|
|
||
|
$current_views = get_post_meta( $post_id, '_vp_views_count', true );
|
||
|
|
||
|
if ( ! $current_views ) {
|
||
|
$current_views = 0;
|
||
|
}
|
||
|
|
||
|
return $current_views;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update reading time.
|
||
|
*
|
||
|
* @param int $post_id The post ID.
|
||
|
*/
|
||
|
public static function update_words_count( $post_id ) {
|
||
|
if ( ! $post_id ) {
|
||
|
$post_id = get_the_ID();
|
||
|
}
|
||
|
|
||
|
$current_reading_time = self::calculate_words_count( $post_id );
|
||
|
|
||
|
update_post_meta( $post_id, '_vp_words_count', $current_reading_time );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get reading time.
|
||
|
*
|
||
|
* Read time is based on the average reading speed of an adult (roughly 265 WPM).
|
||
|
* We take the total word count of a post and translate it into minutes.
|
||
|
* For posts in Chinese, Japanese and Korean, it's a function of
|
||
|
* number of characters (500 characters/min).
|
||
|
*
|
||
|
* @thanks https://help.medium.com/hc/en-us/articles/214991667-Read-time
|
||
|
*
|
||
|
* @param int $post_id The post ID.
|
||
|
*/
|
||
|
public static function get_reading_time( $post_id ) {
|
||
|
if ( ! $post_id ) {
|
||
|
$post_id = get_the_ID();
|
||
|
}
|
||
|
|
||
|
$post_words_count = get_post_meta( $post_id, '_vp_words_count', true );
|
||
|
|
||
|
if ( ! $post_words_count ) {
|
||
|
$post_words_count = self::calculate_words_count( $post_id );
|
||
|
}
|
||
|
|
||
|
$locale = get_locale();
|
||
|
|
||
|
switch ( $locale ) {
|
||
|
// zh_CN - Chinese (China)
|
||
|
// zh_HK - Chinese (Hong Kong SAR China)
|
||
|
// zh_SG - Chinese (Singapore)
|
||
|
// zh_TW - Chinese (Taiwan)
|
||
|
// ja_JP - Japanese (Japan)
|
||
|
// ko_KR - Korean (South Korea).
|
||
|
case 'zh_CN':
|
||
|
case 'zh_HK':
|
||
|
case 'zh_SG':
|
||
|
case 'zh_TW':
|
||
|
case 'ja_JP':
|
||
|
case 'ko_KR':
|
||
|
$reading_time = $post_words_count / 500;
|
||
|
break;
|
||
|
default:
|
||
|
$reading_time = $post_words_count / 265;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// When reading time is 0, return it as `< 1` instead of `0`.
|
||
|
if ( 1 > $reading_time ) {
|
||
|
$reading_time = esc_html__( '< 1', 'visual-portfolio' );
|
||
|
} else {
|
||
|
$reading_time = ceil( $reading_time );
|
||
|
}
|
||
|
|
||
|
return $reading_time;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Calculate words count.
|
||
|
*
|
||
|
* @param int $post_id The post ID.
|
||
|
*/
|
||
|
public static function calculate_words_count( $post_id ) {
|
||
|
if ( ! $post_id ) {
|
||
|
$post_id = get_the_ID();
|
||
|
}
|
||
|
|
||
|
$content = get_the_content( null, false, $post_id );
|
||
|
$content = wp_strip_all_tags( $content );
|
||
|
$words_count = count( preg_split( '/\s+/', $content ) );
|
||
|
|
||
|
return $words_count;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Visual_Portfolio_Custom_Post_Meta::init();
|