Source: classes/controller/admin-snippets.php

<?php

namespace wpbuddy\rich_snippets;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
} // Exit if accessed directly


/**
 * Class Admin_Snippets_Controller.
 *
 * Starts up all the admin things needed to control snippets.
 *
 * @package wpbuddy\rich_snippets
 *
 * @since   2.0.0
 */
class Admin_Snippets_Controller {

	/**
	 * The instance.
	 *
	 * @var Admin_Snippets_Controller
	 *
	 * @since 2.0.0
	 */
	protected static $instance = null;


	/**
	 * If this instance has been initialized already.
	 *
	 * @var bool
	 */
	protected $initialized = false;

	/**
	 * Get the singleton instance.
	 *
	 * Creates a new instance of the class if it does not exists.
	 *
	 * @return   Admin_Snippets_Controller
	 *
	 * @since 2.0.0
	 */
	public static function instance() {

		if ( null === self::$instance ) {
			self::$instance = new self;
		}

		return self::$instance;
	}


	/**
	 * Magic function for cloning.
	 *
	 * Disallow cloning as this is a singleton class.
	 *
	 * @since 2.0.0
	 */
	protected function __clone() {
	}


	/**
	 * Magic method for setting up the class.
	 *
	 * Disallow external instances.
	 *
	 * @since 2.0.0
	 */
	protected function __construct() {
	}


	/**
	 * The fields model.
	 *
	 * @since 2.0.0
	 * @var Fields_Model|null
	 */
	protected $fields = null;


	/**
	 * Init.
	 *
	 * @since 2.0.0
	 */
	public function init() {

		if ( $this->initialized ) {
			return;
		}

		add_action( 'add_meta_boxes', array( self::$instance, 'add_meta_boxes' ), 11, 2 );

		add_action( 'admin_enqueue_scripts', array( self::$instance, 'enqueue_scripts_styles' ), 20 );

		add_filter( 'wpbuddy/rich_snippets/save_snippet/property/sanitize', 'sanitize_text_field' );

		add_action( 'post_submitbox_misc_actions', array( self::$instance, 'submitbox_js' ) );

		add_filter( 'post_row_actions', array( self::$instance, 'filter_row_actions' ), 10, 2 );

		add_filter( 'parent_file', [ self::$instance, 'highlight_menu' ] );

		add_filter( 'manage_wpb-rs-global_posts_columns', [ self::$instance, 'manage_posts_columns' ] );

		add_action( 'manage_wpb-rs-global_posts_custom_column', [ self::$instance, 'print_custom_columns' ], 10, 2 );

		add_action( 'save_post', array( self::$instance, 'save_metaboxes' ), 10, 1 );

		add_action( 'admin_notices', array( self::$instance, 'global_snippets_wizard_notice' ) );

		$this->initialized = true;
	}


	/**
	 *
	 * Creates metaboxes.
	 *
	 * @param string $post_type
	 * @param \WP_Post $post
	 *
	 * @since 2.0.0
	 */
	public function add_meta_boxes( $post_type, $post ) {

		# the main metabox
		add_meta_box(
			'wp-rs-mb-main',
			_x( 'Structured data', 'metabox title', 'rich-snippets-schema' ),
			array( self::$instance, 'render_snippets_meta_box' ),
			'wpb-rs-global',
			'advanced',
			'high'
		);

		add_meta_box(
			'wp-rs-mb-post',
			_x( 'Structured Data', 'metabox title', 'rich-snippets-schema' ),
			array( '\wpbuddy\rich_snippets\View', 'admin_posts_metabox' ),
			(array) get_option( 'wpb_rs/setting/post_types', array( 'post', 'page' ) ),
			'advanced',
			'low'
		);

		add_meta_box(
			'wp-rs-mb-options',
			_x( 'SNIP Options', 'metabox title', 'rich-snippets-schema' ),
			array( '\wpbuddy\rich_snippets\View', 'admin_posts_metabox_options' ),
			get_post_types( [ 'show_ui' => true ] ),
			'side',
			'low'
		);

		add_meta_box(
			'wp-rs-mb-help',
			_x( 'Help', 'metabox title', 'rich-snippets-schema' ),
			array( '\wpbuddy\rich_snippets\View', 'admin_snippets_metabox_help' ),
			'wpb-rs-global',
			'side',
			'low'
		);

		add_meta_box(
			'wp-rs-mb-news',
			_x( 'News', 'metabox title', 'rich-snippets-schema' ),
			array( '\wpbuddy\rich_snippets\View', 'admin_snippets_metabox_news' ),
			'wpb-rs-global',
			'side',
			'low'
		);

		/**
		 * Schema Metabox Action.
		 *
		 * Allows plugins to hook into the Snippets Controller after metaboxes have been set up.
		 *
		 * @hook  wpbuddy/rich_snippets/schemas/metaboxes
		 *
		 * @param {string} $post_type
		 * @param {WP_Post} $post
		 *
		 * @since 2.0.0
		 */
		do_action( 'wpbuddy/rich_snippets/schemas/metaboxes', $post_type, $post );
	}


	/**
	 * Adds scripts and styles.
	 *
	 * @since 2.0.0
	 */
	public function enqueue_scripts_styles() {

		$this->get_scripts_controller()->enqueue_snippets_styles();
		$this->get_scripts_controller()->enqueue_snippets_scripts();

		$post_type = Helper_Model::instance()->get_current_admin_post_type();

		$post_types = (array) get_option( 'wpb_rs/setting/post_types', array( 'post', 'page' ) );

		if ( in_array( $post_type, $post_types ) ) {
			$this->get_scripts_controller()->enqueue_posts_forms_scripts();
		}

		$post_types[] = 'wpb-rs-global';

		if ( in_array( $post_type, $post_types ) ) {
			$this->get_scripts_controller()->enqueue_posts_scripts();
		}

		/**
		 * Schema Scripts Action.
		 *
		 * Allows plugins to hook into the Admin Snippets Controller after scripts and styles have been enqueued.
		 *
		 * @hook  wpbuddy/rich_snippets/schemas/scripts
		 * @since 2.0.0
		 */
		do_action( 'wpbuddy/rich_snippets/schemas/scripts' );
	}


	/**
	 * Renders the meta box.
	 *
	 * @param \WP_Post $post
	 * @param array $metabox
	 *
	 * @since 2.0.0
	 *
	 */
	public function render_snippets_meta_box( $post, $metabox ) {

		$snippet = Snippets_Model::get_first_snippet( (int) $post->ID );

		View::admin_snippets_metabox_snippet( $snippet, $post );

	}


	/**
	 * Saves a schema from the builder to the database.
	 *
	 * @param int $post_id
	 *
	 * @since 2.0.0
	 *
	 */
	public function save_snippets( $post_id ) {

		if ( ! isset( $_POST['snippets'] ) ) {
			return;
		}

		if ( ! is_array( $_POST['snippets'] ) ) {
			return;
		}

		/**
		 * Save snippets
		 */

		$snippets = Snippets_Model::sanitize_and_generate_snippets( $_POST['snippets'] );

		Snippets_Model::update_snippets( $post_id, $snippets );
	}


	/**
	 * Returns the HTML code for the properties to use in the table.
	 *
	 * Uses Rich_Snippet:get_properties() if $prop_ids has no elements.
	 *
	 * @param Rich_Snippet $snippet
	 * @param array $property_ids
	 * @param \WP_Post $post
	 *
	 * @return string[]
	 * @since 2.0.0
	 * @since 2.2.0 Added $post param.
	 *
	 */
	public function get_property_table_elements( $snippet, $property_ids = array(), $post = null ) {

		$this->init_fields();

		$html_elements = array();

		if ( count( $property_ids ) > 0 ) {
			$props = array_map( function ( $val ) {

				return Schemas_Model::get_property_by_id( $val );
			}, $property_ids );
		} else {
			# load the properties from the snippet
			$props = $snippet->get_properties();
		}

		foreach ( $props as $prop ) {
			if ( ! $prop instanceof Schema_Property ) {
				continue;
			}

			ob_start();
			View::admin_snippets_properties_row( $prop, $snippet, $post );
			$html_elements[] = ob_get_clean();
		}

		return $html_elements;
	}


	/**
	 * Builds a property table.
	 *
	 * Uses Rich_Snippet:get_properties() if $prop_ids has no elements.
	 *
	 * @param Rich_Snippet $snippet
	 * @param array $prop_ids
	 * @param \WP_Post $post
	 *
	 * @return string
	 * @since 2.0.0
	 * @since 2.2.0 Added $post parameter
	 *
	 */
	public function get_property_table( $snippet, $prop_ids = array(), $post = null ) {

		$props_rendered = $this->get_property_table_elements( $snippet, $prop_ids, $post );

		ob_start();
		View::admin_snippets_properties_table( $props_rendered, $snippet, $post );

		return ob_get_clean();

	}


	/**
	 * Initializes the fields.
	 *
	 * Prevents double-init.
	 *
	 * @since 2.0.0
	 */
	public function init_fields() {

		if ( ! $this->fields instanceof Fields_Model ) {
			$this->fields = new Fields_Model();
		}
	}

	/**
	 * Returns the current scripts controller.
	 *
	 * @since 2.19.0
	 */
	public function get_scripts_controller() {
		return Admin_Scripts_Controller::instance();
	}

	/**
	 * Adds JS to the submitbox to remove some fields.
	 *
	 * @since 2.0.0
	 */
	public function submitbox_js() {

		if ( ! function_exists( 'get_current_screen' ) ) {
			return;
		}

		$screen = get_current_screen();

		if ( 'wpb-rs-global' !== $screen->post_type ) {
			return;
		}


		?>
        <script type="text/javascript">
            jQuery('#visibility, .misc-pub-curtime').remove();
        </script>
		<?php
	}

	/**
	 * Filters the row actions.
	 *
	 * @param array $actions
	 * @param \WP_Post $post
	 *
	 * @return array
	 * @since 2.0.0
	 *
	 */
	public function filter_row_actions( $actions, $post ) {

		if ( 'wpb-rs-global' !== $post->post_type ) {
			return $actions;
		}

		# Remove the quick edit option.
		# Note that the quickedit option has a weird name ('inline hide-if-no-js')
		if ( isset( $actions['inline hide-if-no-js'] ) ) {
			unset( $actions['inline hide-if-no-js'] );
		}

		return array_merge( [ 'ID: ' . $post->ID ], $actions );
	}


	/**
	 * Make sure the main menu is highlighted if we're on the "Global Snippets"
	 *
	 * @param string $parent_file
	 *
	 * @return string
	 *
	 * @since 2.14.0
	 */
	public function highlight_menu( $parent_file ) {


		$screen = get_current_screen();

		if ( $screen instanceof \WP_Screen && $screen->id === 'wpb-rs-global' ) {
			global $plugin_page;
			$plugin_page = 'edit.php?post_type=wpb-rs-global';
		}

		return $parent_file;
	}


	/**
	 * Manage wpb-rs-global post columns.
	 *
	 * @param array $columns
	 *
	 * @return array
	 * @since 2.8.0
	 *
	 */
	public function manage_posts_columns( $columns ) {

		$columns = Helper_Model::instance()->integrate_into_array(
			$columns,
			2,
			[
				'snippet-ids' => __( 'Snippet IDs', 'rich-snippets-schema' )
			]
		);

		return $columns;
	}


	/**
	 * Print custom columns on wpb-rs-global post overview page.
	 *
	 * @param string $column_name
	 * @param int $post_id
	 *
	 * @since 2.8.0
	 */
	public function print_custom_columns( $column_name, $post_id ) {
		if ( 'snippet-ids' === $column_name || 'predefined' === $column_name ) {
			$snippets = Snippets_Model::get_snippets( $post_id );

			foreach ( $snippets as $snippet ) {
				if ( 'predefined' === $column_name ) {
					if ( false !== stripos( $snippet->id, 'snip-global-' ) ) {
						echo '<span class="dashicons dashicons-yes"></span>';
					} else {
						echo '<span class="dashicons dashicons-no"></span>';
					}
					break;
				} else {
					echo $snippet->id;
				}
			}

		}

		if ( 'sync' === $column_name ) {
			$synced_id = intval( get_post_meta( $post_id, '_wpb_rs_sync_id', true ) );

			if ( $synced_id > 0 ) {
				echo '<span class="dashicons dashicons-yes wpb-rs-global-snippet-sync-yes"></span>';
			} else {
				echo '<span class="dashicons dashicons-no wpb-rs-global-snippet-sync-no"></span>';
			}
		}
	}


	/**
	 * Saves fields from the metaboxes.
	 *
	 * @param int $post_id
	 *
	 * @see   Admin_Snippets_Controller::save_snippets()
	 *
	 * @since 2.22.0
	 *
	 */
	public function save_metaboxes( $post_id ) {

		if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
			return;
		}

		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
			return;
		}

		if ( ! isset( $_POST['snip_hide_schemas'] ) ) {
			delete_post_meta( $post_id, 'snip_hide_schemas' );
		} else {
			update_post_meta( $post_id, 'snip_hide_schemas', Helper_Model::instance()->string_to_bool( $_POST['snip_hide_schemas'] ) );
		}

	}


	/**
	 * Print an admin notice to ask if we should install examples via the wizard.
	 *
	 * @since 2.22.0
	 */
	public function global_snippets_wizard_notice() {

		if ( ! function_exists( 'get_current_screen' ) ) {
			return;
		}

		$screen = get_current_screen();

		if ( ! $screen instanceof \WP_Screen ) {
			return;
		}

		if ( 'edit-wpb-rs-global' !== $screen->id ) {
			return;
		}

		$user = wp_get_current_user();
		?>
        <div class="notice notice-info">
            <p><?php
				printf(
					__( 'Hey <strong>%s!</strong> You can use the Setup Wizard to install some schemas that I\'ve pre-build. Want to check it out?', 'rich-snippets-schema' ),
					$user->display_name
				);

				printf(
					' <a class="button" href="%s">%s</a>',
					admin_url( 'admin.php?page=rich-snippets-setupwizard' ),
					__( 'Awesome! Start the Wizard!', 'rich-snippets-schema' )
				);
				?>
            </p>
        </div>
		<?php
	}
}