site_supports_comics() ) ) { return; } $this->register_post_types(); add_action( 'pre_get_posts', array( $this, 'add_posts_to_loop' ) ); // In order for the Feedbag job to find Comic posts, we need to circumvent any pretty // URLs in the RSS feed given to Feedbag in favor of /?p=123&post_type=jetpack-comic add_filter( 'the_permalink_rss', array( $this, 'custom_permalink_for_feedbag' ) ); // There are some cases (like when Feedbag is fetching posts) that the comics // post type needs to be registered no matter what, but none of the UI needs to be // available. add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) ); if ( function_exists( 'queue_publish_post' ) ) { add_action( 'publish_jetpack-comic', 'queue_publish_post', 10, 2 ); } add_action( 'pre_get_posts', array( $this, 'include_in_feeds' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); add_filter( 'manage_' . self::POST_TYPE . '_posts_columns', array( $this, 'manage_posts_columns' ) ); add_action( 'manage_' . self::POST_TYPE . '_posts_custom_column', array( $this, 'manage_posts_custom_column' ), 10, 2 ); add_image_size( 'jetpack-comic-thumb', 150, 0, false ); // Enable front-end uploading for users special enough. if ( current_user_can( 'upload_files' ) && current_user_can( 'edit_posts' ) ) { add_action( 'wp_ajax_jetpack_comic_upload', array( $this, 'upload' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts' ) ); } /** * Add a "Convert to Comic" and "Convert to Post" option to the bulk * edit dropdowns. */ add_action( 'admin_footer-edit.php', array( $this, 'admin_footer' ) ); add_action( 'load-edit.php', array( $this, 'bulk_edit' ) ); add_action( 'admin_notices', array( $this, 'bulk_edit_notices' ) ); } /** * Enqueue JavaScript in the footer. * * @return void */ public function admin_footer() { $post_type = get_post_type(); ?> current_action(); check_admin_referer( 'bulk-posts' ); if ( 'post2comic' === $action || 'comic2post' === $action ) { if ( ! current_user_can( 'publish_posts' ) ) { wp_die( esc_html__( 'You are not allowed to make this change.', 'jetpack' ) ); } $post_ids = array_map( 'intval', $_REQUEST['post'] ); $modified_count = 0; foreach ( $post_ids as $post_id ) { $destination_post_type = ( $action === 'post2comic' ) ? self::POST_TYPE : 'post'; $origin_post_type = ( $destination_post_type === 'post' ) ? self::POST_TYPE : 'post'; if ( current_user_can( 'edit_post', $post_id ) ) { $post = get_post( $post_id ); // Only convert posts that are post => comic or comic => post. // (e.g., Ignore comic => comic, page => post, etc. ) if ( $post->post_type !== $destination_post_type && $post->post_type === $origin_post_type ) { $post_type_object = get_post_type_object( $destination_post_type ); if ( current_user_can( $post_type_object->cap->publish_posts ) ) { set_post_type( $post_id, $destination_post_type ); ++$modified_count; } } } } $sendback = remove_query_arg( array( 'exported', 'untrashed', 'deleted', 'ids' ), wp_get_referer() ); if ( ! $sendback ) { $sendback = add_query_arg( array( 'post_type', get_post_type() ), admin_url( 'edit.php' ) ); } $pagenum = $wp_list_table->get_pagenum(); $bulk_edit_comics_nonce = wp_create_nonce( 'bulk-edit-comics-nonce' ); $sendback = add_query_arg( array( 'paged' => $pagenum, 'post_type_changed' => $modified_count, 'bulk_edit_comics_nonce' => $bulk_edit_comics_nonce, ), $sendback ); wp_safe_redirect( $sendback ); exit(); } } /** * Show the post conversion success notice. * * @return void */ public function bulk_edit_notices() { global $pagenow; if ( empty( $_GET['bulk_edit_comics_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['bulk_edit_comics_nonce'] ), 'bulk-edit-comics-nonce' ) ) { return; } $number_posts_changed = isset( $_GET['post_type_changed'] ) ? (int) $_GET['post_type_changed'] : 0; if ( 'edit.php' === $pagenow && $number_posts_changed ) { ?>
wp_create_nonce( 'jetpack_comic_upload_nonce' ), 'writeURL' => admin_url( 'admin-ajax.php?action=jetpack_comic_upload' ), 'labels' => array( 'dragging' => __( 'Drop images to upload', 'jetpack' ), 'uploading' => __( 'Uploading...', 'jetpack' ), 'processing' => __( 'Processing...', 'jetpack' ), 'unsupported' => __( "Sorry, your browser isn't supported. Upgrade at browsehappy.com.", 'jetpack' ), 'invalidUpload' => __( 'Only images can be uploaded here.', 'jetpack' ), 'error' => __( "Your upload didn't complete; try again later or cross your fingers and try again right now.", 'jetpack' ), ), ); wp_localize_script( 'jetpack-comics', 'Jetpack_Comics_Options', $options ); } } /** * Enqueue stylesheet in the admin. */ public function admin_enqueue_scripts() { wp_enqueue_style( 'jetpack-comics-admin', get_template_directory_uri() . '/comics/admin.css', array(), JETPACK__VERSION ); } /** * Register the post types if the theme supports them. */ public function maybe_register_post_types() { // Return early if theme does not support Jetpack Comic. if ( ! ( $this->site_supports_comics() ) ) { return; } $this->register_post_types(); } /** * Register our CPT. */ public function register_post_types() { if ( post_type_exists( self::POST_TYPE ) ) { return; } register_post_type( self::POST_TYPE, array( 'description' => __( 'Comics', 'jetpack' ), 'labels' => array( 'name' => esc_html__( 'Comics', 'jetpack' ), 'singular_name' => esc_html__( 'Comic', 'jetpack' ), 'menu_name' => esc_html__( 'Comics', 'jetpack' ), 'all_items' => esc_html__( 'All Comics', 'jetpack' ), 'add_new' => esc_html__( 'Add New', 'jetpack' ), 'add_new_item' => esc_html__( 'Add New Comic', 'jetpack' ), 'edit_item' => esc_html__( 'Edit Comic', 'jetpack' ), 'new_item' => esc_html__( 'New Comic', 'jetpack' ), 'view_item' => esc_html__( 'View Comic', 'jetpack' ), 'search_items' => esc_html__( 'Search Comics', 'jetpack' ), 'not_found' => esc_html__( 'No Comics found', 'jetpack' ), 'not_found_in_trash' => esc_html__( 'No Comics found in Trash', 'jetpack' ), 'filter_items_list' => esc_html__( 'Filter comics list', 'jetpack' ), 'items_list_navigation' => esc_html__( 'Comics list navigation', 'jetpack' ), 'items_list' => esc_html__( 'Comics list', 'jetpack' ), ), 'supports' => array( 'title', 'editor', 'thumbnail', 'comments', 'revisions', 'publicize', // Jetpack 'subscriptions', // wpcom 'shortlinks', // Jetpack ), 'rewrite' => array( 'slug' => 'comic', 'with_front' => false, ), 'taxonomies' => array( 'category', 'post_tag', ), // Only make the type public for sites that support Comics. 'public' => true, 'menu_position' => 5, // below Posts 'map_meta_cap' => true, 'has_archive' => true, 'query_var' => 'comic', 'show_in_rest' => true, ) ); } /** * Add a Preview colunm to the Comic CPT admin view. * * @param array $columns An array of column names. * @return array Updated `$columns`. */ public function manage_posts_columns( $columns ) { $new_columns = array( 'preview-jetpack-comic' => __( 'Preview', 'jetpack' ), ); return array_merge( array_slice( $columns, 0, 2 ), $new_columns, array_slice( $columns, 2 ) ); } /** * Display the post's featured image in column. * * @param string $column_name The name of the column to display. * @param int $post_ID The current post ID. */ public function manage_posts_custom_column( $column_name, $post_ID ) { if ( 'preview-jetpack-comic' === $column_name && has_post_thumbnail( $post_ID ) ) { echo get_the_post_thumbnail( $post_ID, 'jetpack-comic-thumb' ); } } /** * The function url_to_postid() doesn't handle pretty permalinks * for CPTs very well. When we're generating an RSS feed to be consumed * for Feedbag (the Reader's feed storage mechanism), eschew * a pretty URL for one that will get the post into the Reader. * * @see https://core.trac.wordpress.org/ticket/19744 * @param string $permalink The existing (possibly pretty) permalink. * * @return string The permalink to use. */ public function custom_permalink_for_feedbag( $permalink ) { global $post; if ( ! empty( $GLOBALS['is_feedbag_rss_script'] ) && self::POST_TYPE === $post->post_type ) { $permalink = home_url( add_query_arg( array( 'p' => $post->ID, 'post_type' => self::POST_TYPE, ), '?' ) ); } return $permalink; } /** * Update messages for the Comic admin. * * @param array $messages Existing post update messages. * * @return array $messages Amended post update messages. */ public function updated_messages( $messages ) { global $post; $messages['jetpack-comic'] = array( 0 => '', // Unused. Messages start at index 1. 1 => sprintf( /* Translators: link to comic item's page. */ __( 'Comic updated. View comic', 'jetpack' ), esc_url( get_permalink( $post->ID ) ) ), 2 => esc_html__( 'Custom field updated.', 'jetpack' ), 3 => esc_html__( 'Custom field deleted.', 'jetpack' ), 4 => esc_html__( 'Comic updated.', 'jetpack' ), /* translators: %s: date and time of the revision */ 5 => isset( $_GET['revision'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Copying core message handling. ? sprintf( /* Translators: link to comic item's page. */ esc_html__( 'Comic restored to revision from %s', 'jetpack' ), wp_post_revision_title( (int) $_GET['revision'], false ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Copying core message handling. ) : false, 6 => sprintf( /* Translators: link to comic item's page. */ __( 'Comic published. View comic', 'jetpack' ), esc_url( get_permalink( $post->ID ) ) ), 7 => esc_html__( 'Comic saved.', 'jetpack' ), 8 => sprintf( /* Translators: link to portfolio item's page. */ __( 'Comic submitted. Preview comic', 'jetpack' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ), 9 => sprintf( /* Translators: link to comic item's page. */ __( 'Comic scheduled for: %1$s. Preview comic', 'jetpack' ), // translators: Publish box date format, see https://php.net/date date_i18n( __( 'M j, Y @ G:i', 'jetpack' ), strtotime( $post->post_date ) ), esc_url( get_permalink( $post->ID ) ) ), 10 => sprintf( /* Translators: link to comic item's page. */ __( 'Comic draft updated. Preview comic', 'jetpack' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ), ); return $messages; } /** * Should this Custom Post Type be made available? * * @return bool */ public function site_supports_comics() { if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { $php_self = isset( $_SERVER['PHP_SELF'] ) ? sanitize_text_field( wp_unslash( $_SERVER['PHP_SELF'] ) ) : ''; $blog_ids = isset( $_SERVER['argv'] ) ? array_map( 'intval', (array) wp_unslash( $_SERVER['argv'] ) ) : array(); if ( ! empty( $php_self ) && str_ends_with( $php_self, 'blog-rss.php' ) && count( $blog_ids ) > 1 ) { // blog-rss.php isn't run in the context of the target blog when the init action fires, // so check manually whether the target blog supports comics. switch_to_blog( $blog_ids[1] ); // The add_theme_support( 'jetpack-comic' ) won't fire on switch_to_blog, so check for Panel manually. $supports_comics = ( ( function_exists( 'site_vertical' ) && 'comics' === site_vertical() ) || current_theme_supports( self::POST_TYPE ) || get_stylesheet() === 'pub/panel' ); restore_current_blog(); /** This action is documented in modules/custom-post-types/nova.php */ return (bool) apply_filters( 'jetpack_enable_cpt', $supports_comics, self::POST_TYPE ); } } $supports_comics = false; /** * If we're on WordPress.com, and it has the menu site vertical. * * @todo: Extract this out into a wpcom only file. */ if ( function_exists( 'site_vertical' ) && 'comics' === site_vertical() ) { $supports_comics = true; } /** * Else, if the current theme requests it. */ if ( current_theme_supports( self::POST_TYPE ) ) { $supports_comics = true; } /** * Filter it in case something else knows better. */ /** This action is documented in modules/custom-post-types/nova.php */ return (bool) apply_filters( 'jetpack_enable_cpt', $supports_comics, self::POST_TYPE ); } /** * Anywhere that a feed is displaying posts, show comics too. * * @param WP_Query $query The current query. * * @return void */ public function include_in_feeds( $query ) { if ( ! $query->is_feed() ) { return; } // Don't modify the query if the post type isn't public. if ( ! get_post_type_object( 'jetpack-comic' )->public ) { return; } $query_post_types = $query->get( 'post_type' ); if ( empty( $query_post_types ) ) { $query_post_types = 'post'; } if ( ! is_array( $query_post_types ) ) { $query_post_types = array( $query_post_types ); } if ( in_array( 'post', $query_post_types, true ) ) { $query_post_types[] = self::POST_TYPE; $query->set( 'post_type', $query_post_types ); } } /** * API endpoint for front-end image uploading. * * @return never */ public function upload() { global $content_width; header( 'Content-Type: application/json' ); if ( empty( $_REQUEST['nonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['nonce'] ), 'jetpack_comic_upload_nonce' ) ) { die( wp_json_encode( array( 'error' => __( 'Invalid or expired nonce.', 'jetpack' ) ) ) ); } $_POST['action'] = 'wp_handle_upload'; $image_id_arr = array(); $image_error_arr = array(); $i = 0; while ( isset( $_FILES[ 'image_' . $i ] ) ) { // Create attachment for the image. $image_id = media_handle_upload( "image_$i", 0 ); if ( is_wp_error( $image_id ) ) { $error = array( $image_id, $image_id->get_error_message() ); array_push( $image_error_arr, $error ); } else { array_push( $image_id_arr, $image_id ); } ++$i; } if ( $image_id_arr === array() ) { // All image uploads failed. $rv = array( 'error' => '' ); foreach ( $image_error_arr as $error ) { $rv['error'] .= $error[1] . "\n"; } } else { if ( count( $image_id_arr ) === 1 ) { $image_id = $image_id_arr[0]; // Get the image $image_src = get_the_guid( $image_id ); $image_dims = wp_get_attachment_image_src( $image_id, 'full' ); // Take off 10px of width to account for padding and border. @todo make this smarter. if ( $content_width ) { $image_width = $content_width - 10; } else { $image_width = $image_dims[1] - 10; } $image_name = isset( $_FILES['image_0']['name'] ) ? sanitize_file_name( wp_unslash( $_FILES['image_0']['name'] ) ) : ''; $post_content = sprintf( '