Inventory_Presser_Admin_Customize_Dashboard

Inventory_Presser_Admin_Customize_Dashboard


Description Description

An object that customizes the administrator dashboard to make managing an inventory of vehicles easy.


Top ↑

Source Source

File: includes/admin/class-admin-customize-dashboard.php

class Inventory_Presser_Admin_Customize_Dashboard {

	const NONCE_DELETE_ALL_MEDIA = 'invp_delete_all_media';

	/**
	 * add_columns_to_vehicles_table
	 *
	 * Adds slugs to the post table columns array so the dashboard list of
	 * vehicles is more informative than the vanilla list for posts.
	 *
	 * @param  array $column
	 * @return array
	 */
	function add_columns_to_vehicles_table( $column ) {
		// add our columns
		$column[ apply_filters( 'invp_prefix_meta_key', 'stock_number' ) ] = __( 'Stock #', 'inventory-presser' );
		$column[ apply_filters( 'invp_prefix_meta_key', 'color' ) ]        = __( 'Color', 'inventory-presser' );
		$column[ apply_filters( 'invp_prefix_meta_key', 'odometer' ) ]     = __( 'Odometer', 'inventory-presser' );
		$column[ apply_filters( 'invp_prefix_meta_key', 'price' ) ]        = __( 'Price', 'inventory-presser' );
		$column[ apply_filters( 'invp_prefix_meta_key', 'photo_count' ) ]  = __( 'Photos', 'inventory-presser' );
		$column[ apply_filters( 'invp_prefix_meta_key', 'thumbnail' ) ]    = __( 'Thumbnail', 'inventory-presser' );
		// remove the date and tags columns
		unset( $column['date'] );
		unset( $column['tags'] );
		return $column;
	}

	/**
	 * add_meta_boxes_to_cpt
	 *
	 * Adds meta boxes to the editor when editing vehicles.
	 *
	 * @return void
	 */
	function add_meta_boxes_to_cpt() {
		// Add a meta box to the New/Edit post page
		// add_meta_box('vehicle-meta', 'Attributes', array( $this, 'meta_box_html_vehicle' ), INVP::POST_TYPE, 'normal', 'high' );

		// and another for prices
		// add_meta_box('prices-meta', 'Prices', array( $this, 'meta_box_html_prices' ), INVP::POST_TYPE, 'normal', 'high' );

		// Add another meta box to the New/Edit post page
		add_meta_box( 'options-meta', 'Optional equipment', array( $this, 'meta_box_html_options' ), INVP::POST_TYPE, 'normal', 'high' );

		// Add a meta box to the side column for a featured vehicle checkbox
		add_meta_box( 'featured', 'Featured Vehicle', array( $this, 'meta_box_html_featured' ), INVP::POST_TYPE, 'side', 'low' );
	}

	/**
	 * add_vehicles_to_admin_bar
	 *
	 * Adds a Vehicle button to the Admin Bar
	 *
	 * @return void
	 */
	function add_vehicles_to_admin_bar() {
		// do not do this if we are already looking at the dashboard
		if ( is_admin() ) {
			return;
		}

		global $wp_admin_bar;
		$wp_admin_bar->add_node(
			array(
				'id'     => 'wp-admin-bar-vehicles',
				'title'  => __( 'Vehicles', 'inventory-presser' ),
				'href'   => admin_url( 'edit.php?post_type=' . INVP::POST_TYPE ),
				'parent' => 'site-name',
			)
		);
	}

	/**
	 * annotate_add_media_button
	 *
	 * Adds HTML near the Add Media button on the classic editor.
	 *
	 * @param  mixed $editor_id
	 * @return void
	 */
	function annotate_add_media_button( $editor_id ) {
		if ( 'content' != $editor_id ) {
			return;
		}

		printf(
			'%s<span id="media-annotation" class="annotation">%s</span>',
			$this->create_delete_all_post_attachments_button(),
			$this->create_add_media_button_annotation()
		);
	}

	/**
	 * change_post_updated_messages
	 *
	 * Changes the messages shown to users in the editor when changes are made
	 * to the post object.
	 *
	 * @param  array $msgs
	 * @return array
	 */
	function change_post_updated_messages( $msgs ) {
		global $post;

		$view_link = sprintf(
			'<a href="%1$s">%2$s</a>',
			esc_url( get_permalink( $post->ID ) ),
			__( 'View vehicle', 'inventory-presser' )
		);

		$preview_link = sprintf(
			'<a target="_blank" href="%1$s">%2$s</a>',
			esc_url( get_preview_post_link( $post->ID ) ),
			__( 'Preview vehicle', 'inventory-presser' )
		);

		$scheduled_date = date_i18n( __( 'M j, Y @ H:i', 'inventory-presser' ), strtotime( $post->post_date ) );

		$msgs[ INVP::POST_TYPE ] = array(
			0  => '',
			1  => __( 'Vehicle updated. ', 'inventory-presser' ) . $view_link,
			2  => __( 'Custom field updated.', 'inventory-presser' ),
			3  => __( 'Custom field updated.', 'inventory-presser' ),
			4  => __( 'Vehicle updated.', 'inventory-presser' ),
			5  => isset( $_GET['revision'] ) ? sprintf( __( 'Vehicle restored to revision from %s.', 'inventory-presser' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
			6  => __( 'Vehicle published. ', 'inventory-presser' ) . $view_link,
			7  => __( 'Vehicle saved.', 'inventory-presser' ),
			8  => __( 'Vehicle submitted. ', 'inventory-presser' ) . $preview_link,
			9  => sprintf( __( 'Vehicle scheduled to list on <strong>%s</strong>. ', 'inventory-presser' ), $scheduled_date ) . $preview_link,
			10 => __( 'Vehicle draft updated. ', 'inventory-presser' ) . $preview_link,
		);
		return $msgs;
	}

	/**
	 * change_taxonomy_show_ui_attributes
	 *
	 * When the user flips the "Show All Taxonomies" setting switch, this
	 * method changes the taxonomy registration so they are shown.
	 *
	 * @param  array $taxonomy_data
	 * @return array
	 */
	function change_taxonomy_show_ui_attributes( $taxonomy_data ) {
		for ( $i = 0; $i < sizeof( $taxonomy_data ); $i++ ) {
			if ( ! isset( $taxonomy_data[ $i ]['args']['show_in_menu'] ) ) {
				continue;
			}

			$taxonomy_data[ $i ]['args']['show_in_menu'] = true;
			$taxonomy_data[ $i ]['args']['show_ui']      = true;
		}
		return $taxonomy_data;
	}

	/**
	 * create_add_media_button_annotation
	 *
	 * Computes the number of attachments on vehicles so the number can be
	 * shown in the classic editor near the Add Media button.
	 *
	 * @return string
	 */
	function create_add_media_button_annotation() {
		global $post;
		if ( ! is_object( $post ) && isset( $_POST['post_ID'] ) ) {
			/**
			 * This function is being called via AJAX and the
			 * post_id is incoming, so get the post
			 */
			$post = get_post( intval( $_POST['post_ID'] ) );
		}

		if ( INVP::POST_TYPE != $post->post_type ) {
			return '';
		}

		$attachments = get_children(
			array(
				'post_parent'    => $post->ID,
				'post_type'      => 'attachment',
				'posts_per_page' => -1,
			)
		);
		$counts      = array(
			'image' => 0,
			'video' => 0,
			'text'  => 0,
			'PDF'   => 0,
			'other' => 0,
		);
		foreach ( $attachments as $attachment ) {
			switch ( $attachment->post_mime_type ) {
				case 'image/jpeg':
				case 'image/png':
				case 'image/gif':
					$counts['image']++;
					break;
				case 'video/mpeg':
				case 'video/mp4':
				case 'video/quicktime':
					$counts['video']++;
					break;
				case 'text/csv':
				case 'text/plain':
				case 'text/xml':
					$counts['text']++;
					break;
				case 'application/pdf':
					$counts['PDF']++;
					break;
				default:
					$counts['other']++;
					break;
			}
		}
		if ( 0 < ( $counts['image'] + $counts['video'] + $counts['text'] + $counts['PDF'] + $counts['other'] ) ) {
			$note = '';
			foreach ( $counts as $key => $count ) {
				if ( 0 < $count ) {
					if ( '' != $note ) {
						$note .= ', ';
					}
					$note .= $count . ' ' . $key . ( 1 != $count ? 's' : '' );
				}
			}
			return $note;
		}
		return '0 photos';
	}

	/**
	 * create_delete_all_post_attachments_button
	 *
	 * Creates HTML that renders a button that says "Delete All Media" that will
	 * be placed near the Add Media button.
	 *
	 * @return string
	 */
	function create_delete_all_post_attachments_button() {
		global $post;
		if ( ! is_object( $post ) || ! isset( $post->ID ) ) {
			return '';
		}
		// does this post have attachments?
		$post = get_post( $post->ID );
		if ( INVP::POST_TYPE != $post->post_type ) {
			return '';
		}
		$attachments = get_children(
			array(
				'post_parent'    => $post->ID,
				'post_type'      => 'attachment',
				'posts_per_page' => -1,
			)
		);
		if ( 0 === sizeof( $attachments ) ) {
			return '';
		}
		return sprintf(
			'<button type="button" id="delete-media-button" class="button" onclick="delete_all_post_attachments();">'
			. '<span class="wp-media-buttons-icon"></span> %s</button>',
			__( 'Delete All Media', 'inventory-presser' )
		);
	}

	/**
	 * delete_all_post_attachments
	 *
	 * Deletes all a post's attachments. The callback behind the Delete All
	 * Media button.
	 *
	 * @return void
	 */
	function delete_all_post_attachments() {
		if ( empty( $_POST['_ajax_nonce'] ) || ! wp_verify_nonce( $_POST['_ajax_nonce'], self::NONCE_DELETE_ALL_MEDIA ) ) {
			return;
		}

		$post_id = isset( $_POST['post_ID'] ) ? $_POST['post_ID'] : 0;

		if ( ! isset( $post_id ) ) {
			return; // Will die in case you run a function like this: delete_post_media($post_id); if you will remove this line - ALL ATTACHMENTS WHO HAS A PARENT WILL BE DELETED PERMANENTLY!
		} elseif ( 0 == $post_id ) {
			return; // Will die in case you have 0 set. there's no page id called 0 :)
		} elseif ( is_array( $post_id ) ) {
			return; // Will die in case you place there an array of pages.
		} else {
			INVP::delete_attachments( $post_id );
		}
	}

	/**
	 * enable_order_by_attachment_count
	 *
	 * Handle the ORDER BY on the vehicle list (edit.php) when sorting by photo
	 * count.
	 *
	 * @param  array    $pieces
	 * @param  WP_Query $query
	 * @return array
	 */
	function enable_order_by_attachment_count( $pieces, $query ) {
		if ( ! is_admin() ) {
			return $pieces;
		}

		/**
		 * We only want our code to run in the main WP query
		 * AND if an orderby query variable is designated.
		 */
		if ( $query->is_main_query() && ( $orderby = $query->get( 'orderby' ) ) ) {
			// Get the order query variable - ASC or DESC
			$order = strtoupper( $query->get( 'order' ) );

			// Make sure the order setting qualifies. If not, set default as ASC
			if ( ! in_array( $order, array( 'ASC', 'DESC' ) ) ) {
				$order = 'ASC';
			}

			if ( apply_filters( 'invp_prefix_meta_key', 'photo_count' ) == $orderby
				|| apply_filters( 'invp_prefix_meta_key', 'thumbnail' ) == $orderby
			) {
				global $wpdb;
				$pieces['orderby'] = "( SELECT COUNT( ID ) FROM {$wpdb->posts} forget WHERE post_parent = {$wpdb->posts}.ID ) $order, " . $pieces['orderby'];
			}
		}
		return $pieces;
	}

	/**
	 * hooks
	 *
	 * Adds hooks
	 *
	 * @return void
	 */
	function hooks() {
		add_filter( 'posts_clauses', array( $this, 'enable_order_by_attachment_count' ), 1, 2 );

		// Save custom post meta and term relationships when posts are saved
		add_action( 'save_post_' . INVP::POST_TYPE, array( $this, 'save_vehicle_post_meta' ), 10, 3 );
		add_action( 'save_post_' . INVP::POST_TYPE, array( $this, 'save_vehicle_taxonomy_terms' ), 10, 2 );

		// Add columns to the table that lists all the Vehicles on edit.php
		add_filter( 'manage_' . INVP::POST_TYPE . '_posts_columns', array( $this, 'add_columns_to_vehicles_table' ) );

		// Populate the columns we added to the Vehicles table
		add_action( 'manage_' . INVP::POST_TYPE . '_posts_custom_column', array( $this, 'populate_columns_we_added_to_vehicles_table' ), 10, 2 );

		// Make our added columns to the Vehicles table sortable
		add_filter( 'manage_edit-' . INVP::POST_TYPE . '_sortable_columns', array( $this, 'make_vehicles_table_columns_sortable' ) );

		// Implement the orderby for each of these added columns
		add_filter( 'pre_get_posts', array( $this, 'vehicles_table_columns_orderbys' ) );

		add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes_to_cpt' ) );

		// Move all "advanced" meta boxes above the default editor
		// http://wordpress.stackexchange.com/a/88103
		add_action( 'edit_form_after_title', array( $this, 'move_advanced_meta_boxes' ) );

		// Move the "Tags" metabox below the meta boxes for vehicle custom taxonomies
		add_action( 'add_meta_boxes', array( $this, 'move_tags_meta_box' ), 0 );

		// Load our scripts
		add_action( 'admin_enqueue_scripts', array( $this, 'load_scripts' ) );

		// Add some content next to the "Add Media" button
		add_action( 'media_buttons', array( $this, 'annotate_add_media_button' ) );

		// Define an AJAX handler for the 'Delete All Media' button
		add_filter( 'wp_ajax_delete_all_post_attachments', array( $this, 'delete_all_post_attachments' ) );

		// Make our Add Media button annotation available from an AJAX call
		add_action( 'wp_ajax_output_add_media_button_annotation', array( $this, 'output_add_media_button_annotation' ) );

		// Add a link to the Settings page on the plugin management page
		add_filter( 'plugin_action_links_' . INVP_PLUGIN_BASE, array( $this, 'insert_settings_link' ), 2, 2 );

		// Add a link to the main menu of the Admin bar
		add_action( 'admin_bar_menu', array( $this, 'add_vehicles_to_admin_bar' ), 100 );

		$options = INVP::settings();

		// If the Show All Taxonomies setting is checked, change the way we register taxonomies
		if ( isset( $options['show_all_taxonomies'] ) && $options['show_all_taxonomies'] ) {
			add_filter( 'invp_taxonomy_data', array( $this, 'change_taxonomy_show_ui_attributes' ) );
		}

		// Change some messages in the dashboard the user sees when updating vehicles
		add_filter( 'post_updated_messages', array( $this, 'change_post_updated_messages' ) );
	}

	/**
	 * insert_settings_link
	 *
	 * Adds a link to the settings page near the Activate | Delete links on the
	 * list of plugins on the Plugins page.
	 *
	 * @param  array $links
	 * @return array
	 */
	function insert_settings_link( $links ) {
		$url     = admin_url(
			sprintf(
				'edit.php?post_type=%s&page=dealership-options',
				INVP::POST_TYPE
			)
		);
		$links[] = sprintf(
			'<a href="%s">%s</a>',
			$url,
			__( 'Settings', 'inventory-presser' )
		);
		return $links;
	}

	/**
	 * load_scripts
	 *
	 * Includes JavaScripts and stylesheets that power our changes to the
	 * dashboard.
	 *
	 * @param  string $hook
	 * @return void
	 */
	function load_scripts( $hook ) {
		wp_enqueue_style( 'my-admin-theme', plugins_url( '/css/wp-admin.min.css', INVP_PLUGIN_FILE_PATH ) );
		wp_register_script( 'invp-js', plugins_url( '/js/admin.min.js', INVP_PLUGIN_FILE_PATH ) );
		wp_enqueue_script( 'invp-js' );

		// Provide data to JavaScript for the editor
		wp_add_inline_script(
			'invp-js',
			'const invp = ' . json_encode(
				array(
					'hull_materials'         => apply_filters(
						'invp_default_hull_materials',
						array(
							'Aluminum',
							'Carbon Fiber',
							'Composite',
							'Ferro-Cement',
							'Fiberglass',
							'Hypalon',
							'Other',
							'PVC',
							'Steel',
							'Wood',
						)
					),
					'miles_word'             => apply_filters( 'invp_odometer_word', 'miles' ),
					'meta_prefix'            => INVP::meta_prefix(),
					'payment_frequencies'    => apply_filters(
						'invp_default_payment_frequencies',
						array(
							'Monthly'      => 'monthly',
							'Weekly'       => 'weekly',
							'Bi-weekly'    => 'biweekly',
							'Semi-monthly' => 'semimonthly',
						)
					),
					'title_statuses'         => apply_filters(
						'invp_default_title_statuses',
						array(
							'Unspecified',
							'Clear',
							'Clean',
							'Flood, Water Damage',
							'Lemon and Manufacturers Buyback',
							'Rebuild, Rebuildable, and Reconstructed',
							'Salvage',
							'Other',
						)
					),
					'delete_all_media_nonce' => wp_create_nonce( self::NONCE_DELETE_ALL_MEDIA ),
				)
			),
			'before'
		);
	}

	/**
	 * make_vehicles_table_columns_sortable
	 *
	 * Declares which of our custom columns on the list of posts are sortable.
	 *
	 * @param  array $columns
	 * @return array
	 */
	function make_vehicles_table_columns_sortable( $columns ) {
		$custom = array(
			// meta column id => sortby value used in query
			apply_filters( 'invp_prefix_meta_key', 'color' ) => apply_filters( 'invp_prefix_meta_key', 'color' ),
			apply_filters( 'invp_prefix_meta_key', 'odometer' ) => apply_filters( 'invp_prefix_meta_key', 'odometer' ),
			apply_filters( 'invp_prefix_meta_key', 'price' ) => apply_filters( 'invp_prefix_meta_key', 'price' ),
			apply_filters( 'invp_prefix_meta_key', 'stock_number' ) => apply_filters( 'invp_prefix_meta_key', 'stock_number' ),
			apply_filters( 'invp_prefix_meta_key', 'photo_count' ) => apply_filters( 'invp_prefix_meta_key', 'photo_count' ),
			apply_filters( 'invp_prefix_meta_key', 'thumbnail' ) => apply_filters( 'invp_prefix_meta_key', 'thumbnail' ),
		);
		return wp_parse_args( $custom, $columns );
	}

	/**
	 * meta_box_html_featured
	 *
	 * Creates an editor meta box to help users mark vehicles as featured.
	 *
	 * @param  WP_Post $post
	 * @return void
	 */
	function meta_box_html_featured( $post ) {
		$meta_key = apply_filters( 'invp_prefix_meta_key', 'featured' );
		printf(
			'<input type="checkbox" id="%s" name="%s" value="1"%s><label for="%s">%s</label>',
			$meta_key,
			$meta_key,
			checked( '1', INVP::get_meta( $meta_key, $post->ID ), false ),
			$meta_key,
			__( 'Featured in Slideshows', 'inventory-presser' )
		);
	}

	/**
	 * meta_box_html_options
	 *
	 * Creates a meta box to help the user see and manage vehicle options while
	 * editing a vehicle post.
	 *
	 * @param  WP_Post $post
	 * @return void
	 */
	function meta_box_html_options( $post ) {
		$options = apply_filters(
			'invp_default_options',
			array(
				__( '3rd Row Seats', 'inventory-presser' ),
				__( 'Air Bags', 'inventory-presser' ),
				__( 'Air Conditioning', 'inventory-presser' ),
				__( 'Alloy Wheels', 'inventory-presser' ),
				__( 'Aluminum Wheels', 'inventory-presser' ),
				__( 'AM/FM Stereo', 'inventory-presser' ),
				__( 'Anti-lock Brakes', 'inventory-presser' ),
				__( 'Backup Camera', 'inventory-presser' ),
				__( 'Bed Cap', 'inventory-presser' ),
				__( 'Bluetooth, Hands Free', 'inventory-presser' ),
				__( 'Cassette', 'inventory-presser' ),
				__( 'CD Player', 'inventory-presser' ),
				__( 'Cell or Intergrated Cell Phone', 'inventory-presser' ),
				__( 'Cloth Seats', 'inventory-presser' ),
				__( 'Conversion Package', 'inventory-presser' ),
				__( 'Convertible', 'inventory-presser' ),
				__( 'Cooled Seats', 'inventory-presser' ),
				__( 'Cruise Control', 'inventory-presser' ),
				__( 'Custom Paint', 'inventory-presser' ),
				__( 'Disability Equipped', 'inventory-presser' ),
				__( 'Dual Sliding Doors', 'inventory-presser' ),
				__( 'DVD Player', 'inventory-presser' ),
				__( 'Extended Cab', 'inventory-presser' ),
				__( 'Fog Lights', 'inventory-presser' ),
				__( 'Heated Seats', 'inventory-presser' ),
				__( 'Keyless Entry', 'inventory-presser' ),
				__( 'Leather Seats', 'inventory-presser' ),
				__( 'Lift Kit', 'inventory-presser' ),
				__( 'Long Bed', 'inventory-presser' ),
				__( 'Memory Seat(s)', 'inventory-presser' ),
				__( 'Moon Roof', 'inventory-presser' ),
				__( 'Multi-zone Climate Control', 'inventory-presser' ),
				__( 'Navigation System', 'inventory-presser' ),
				__( 'Oversize Off Road Tires', 'inventory-presser' ),
				__( 'Portable Audio Connection', 'inventory-presser' ),
				__( 'Power Brakes', 'inventory-presser' ),
				__( 'Power Lift Gate', 'inventory-presser' ),
				__( 'Power Locks', 'inventory-presser' ),
				__( 'Power Seats', 'inventory-presser' ),
				__( 'Power Steering', 'inventory-presser' ),
				__( 'Power Windows', 'inventory-presser' ),
				__( 'Premium Audio', 'inventory-presser' ),
				__( 'Premium Wheels', 'inventory-presser' ),
				__( 'Privacy Glass', 'inventory-presser' ),
				__( 'Quad Seating', 'inventory-presser' ),
				__( 'Rear Air Bags', 'inventory-presser' ),
				__( 'Rear Air Conditioning', 'inventory-presser' ),
				__( 'Rear Defroster', 'inventory-presser' ),
				__( 'Rear Heat', 'inventory-presser' ),
				__( 'Refrigerator', 'inventory-presser' ),
				__( 'Roof Rack', 'inventory-presser' ),
				__( 'Running Boards', 'inventory-presser' ),
				__( 'Satellite Radio', 'inventory-presser' ),
				__( 'Security System', 'inventory-presser' ),
				__( 'Short Bed', 'inventory-presser' ),
				__( 'Side Air Bags', 'inventory-presser' ),
				__( 'Skid Plate(s)', 'inventory-presser' ),
				__( 'Snow Plow', 'inventory-presser' ),
				__( 'Spoiler', 'inventory-presser' ),
				__( 'Sport Package', 'inventory-presser' ),
				__( 'Step Side Bed', 'inventory-presser' ),
				__( 'Steering Wheel Controls', 'inventory-presser' ),
				__( 'Styled Steel Wheels', 'inventory-presser' ),
				__( 'Sunroof', 'inventory-presser' ),
				__( 'Supercharger', 'inventory-presser' ),
				__( 'Tilt Steering Wheel', 'inventory-presser' ),
				__( 'Tonneau Cover', 'inventory-presser' ),
				__( 'Topper', 'inventory-presser' ),
				__( 'Tow Package', 'inventory-presser' ),
				__( 'Traction Control', 'inventory-presser' ),
				__( 'Trailer Hitch', 'inventory-presser' ),
				__( 'Turbo', 'inventory-presser' ),
				__( 'Two Tone Paint', 'inventory-presser' ),
				__( 'Wheelchair Access', 'inventory-presser' ),
				__( 'Wide Tires', 'inventory-presser' ),
				__( 'Winch', 'inventory-presser' ),
				__( 'Wire Wheels', 'inventory-presser' ),
				__( 'Xenon Headlights', 'inventory-presser' ),
			)
		);

		// turn the array into an associative array with value false for all
		$options = array_fill_keys( $options, false );

		$options_array = invp_get_the_options( $post->ID );
		if ( is_array( $options_array ) ) {
			foreach ( $options_array as $option ) {
				$options[ $option ] = true;
			}
		}
		// sort the array by key
		ksort( $options );
		// output a bunch of checkboxes
		$HTML = '<div class="list-with-columns"><ul class="optional-equipment">';
		foreach ( $options as $key => $value ) {
			// element IDs cannot contain slashes, spaces or parentheses
			$id    = 'option-' . preg_replace( '/\/\(\)/i', '', str_replace( ' ', '_', $key ) );
			$HTML .= sprintf(
				'<li><input type="checkbox" id="%s" name="%s" value="%s"%s><label for="%s">%s</label></li>',
				$id,
				'inventory_presser_options_array[]',
				$key,
				checked( true, $value, false ),
				$id,
				$key
			);
		}
		echo $HTML . '</ul></div>';
	}

	/**
	 * meta_box_html_prices
	 *
	 * Creates a meta box to help users manage a vehicles prices in the editor.
	 *
	 * @param  WP_Post $post
	 * @param  mixed   $meta_box
	 * @return void
	 */
	function meta_box_html_prices( $post, $meta_box ) {
		$prices = array(
			'price'        => 'Price',
			'msrp'         => 'MSRP',
			'down_payment' => 'Down payment',
			'payment'      => 'Payment',
		);

		echo '<table class="form-table"><tbody>';
		foreach ( $prices as $key => $label ) {
			$meta_key = apply_filters( 'invp_prefix_meta_key', $key );

			printf(
				'<tr><th scope="row"><label for="%s">%s</label></th>'
				. '<td><input type="text" name="%s" value="%s" onkeypress="return is_number(event)"></td></tr>',
				$meta_key,
				$label,
				$meta_key,
				INVP::get_meta( $key, $post->ID )
			);
		}

		// Payment frequency is a drop-down
		printf(
			'<tr><th scope="row"><label for="%s">Payment frequency</label></th>'
			. '<td><select name="%s"><option></option>',
			$meta_key,
			$meta_key
		);

		$frequencies = apply_filters(
			'invp_default_payment_frequencies',
			array(
				'Monthly'      => 'monthly',
				'Weekly'       => 'weekly',
				'Bi-weekly'    => 'biweekly',
				'Semi-monthly' => 'semimonthly',
			)
		);
		foreach ( $frequencies as $key => $value ) {
			printf(
				'<option value="%s"%s>%s</option>',
				$value,
				selected( invp_get_the_payment_frequency( $post->ID ), $value, false ),
				$key
			);
		}
		echo '</select></td></tr>'
		. '</tbody></table>';
	}

	/**
	 * meta_box_html_vehicle
	 *
	 * Creates a meta box to help the user manage the bulk of the meta fields
	 * that define a vehicle.
	 *
	 * @param  WP_Post $post
	 * @param  mixed   $meta_box
	 * @return void
	 */
	function meta_box_html_vehicle( $post, $meta_box ) {
		// HTML output for vehicle data meta box
		$custom = get_post_custom( $post->ID );

		$body_style     = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'body_style' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'body_style' ) ][0] : '' );
		$color          = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'color' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'color' ) ][0] : '' );
		$engine         = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'engine' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'engine' ) ][0] : '' );
		$interior_color = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'interior_color' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'interior_color' ) ][0] : '' );
		$make           = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'make' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'make' ) ][0] : '' );
		$model          = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'model' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'model' ) ][0] : '' );
		$odometer       = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'odometer' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'odometer' ) ][0] : '' );
		$stock_number   = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'stock_number' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'stock_number' ) ][0] : '' );
		$trim           = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'trim' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'trim' ) ][0] : '' );
		$VIN            = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'vin' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'vin' ) ][0] : '' );
		$year           = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'year' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'year' ) ][0] : '' );
		$youtube        = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'youtube' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'youtube' ) ][0] : '' );

		// boat items
		$beam          = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'beam' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'beam' ) ][0] : '' );
		$length        = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'length' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'length' ) ][0] : '' );
		$hull_material = ( isset( $custom[ apply_filters( 'invp_prefix_meta_key', 'hull_material' ) ] ) ? $custom[ apply_filters( 'invp_prefix_meta_key', 'hull_material' ) ][0] : '' );

		printf(
			'<table class="form-table"><tbody>'

			// VIN
			. '<tr><th scope="row"><label for="%s">%s</label></th>'
			. '<td>%s</td>'

			// Stock number
			. '<tr><th scope="row"><label for="%s">%s</label></th>'
			. '<td><input type="text" name="%s" value="%s"></td>'

			// Year
			. '<tr><th scope="row"><label for="%s">%s</label></th>'
			. '<td><select name="%s"><option></option>',
			apply_filters( 'invp_prefix_meta_key', 'vin' ),
			__( 'VIN', 'inventory-presser' ),
			apply_filters(
				'invp_edit_control_vin',
				sprintf(
					'<input type="text" name="%s" maxlength="17" value="%s">',
					apply_filters( 'invp_prefix_meta_key', 'vin' ),
					$VIN
				)
			),
			apply_filters( 'invp_prefix_meta_key', 'stock_number' ),
			__( 'Stock number', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'stock_number' ),
			$stock_number,
			apply_filters( 'invp_prefix_meta_key', 'year' ),
			__( 'Year', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'year' )
		);

		for ( $y = date( 'Y' ) + 2; $y >= 1920; $y-- ) {
			printf(
				'<option%s>%s</option>',
				selected( $y, $year, false ),
				$y
			);
		}

		printf(
			'</select></td></tr>'

			// Make
			. '<tr><th scope="row"><label for="%s">%s</label></th>'
			. '<td><input type="text" name="%s" value="%s"></td></tr>'

			// Model
			. '<tr><th scope="row"><label for="%s">%s</label></th>'
			. '<td><input type="text" name="%s" value="%s"></td></tr>'

			// Trim level
			. '<tr><th scope="row"><label for="%s">%s</label></th>'
			. '<td><input type="text" name="%s" value="%s"></td></tr>'

			// Engine
			. '<tr><th scope="row"><label for="%s">%s</label></th>'
			. '<td><input type="text" name="%s" value="%s"></td></tr>'

			// Body style
			. '<tr><th scope="row"><label for="%s">%s</label></th>'
			. '<td><input type="text" name="%s" id="%s" value="%s">'

			. '<select name="%s_hidden" id="%s_hidden">',
			apply_filters( 'invp_prefix_meta_key', 'make' ),
			__( 'Make', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'make' ),
			$make,
			apply_filters( 'invp_prefix_meta_key', 'model' ),
			__( 'Model', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'model' ),
			$model,
			apply_filters( 'invp_prefix_meta_key', 'trim' ),
			__( 'Trim', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'trim' ),
			$trim,
			apply_filters( 'invp_prefix_meta_key', 'engine' ),
			__( 'Engine', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'engine' ),
			$engine,
			apply_filters( 'invp_prefix_meta_key', 'body_style' ),
			__( 'Body style', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'body_style' ),
			apply_filters( 'invp_prefix_meta_key', 'body_style' ),
			$body_style,
			apply_filters( 'invp_prefix_meta_key', 'body_style' ),
			apply_filters( 'invp_prefix_meta_key', 'body_style' )
		);

		$boat_styles = apply_filters(
			'invp_default_boat_styles',
			array(
				'Bass boat',
				'Bow Rider',
				'Cabin Cruiser',
				'Center Console',
				'Cuddy Cabin',
				'Deck boat',
				'Performance',
				'Pontoon',
			)
		);
		foreach ( $boat_styles as $s ) {
			printf(
				'<option%s>%s</option>',
				selected( $s, $body_style ),
				$s
			);
		}

		printf(
			'</select></td></tr>'

			// Color
			. '<tr><th scope="row"><label for="%s">%s</label></th>'
			. '<td><input type="text" name="%s" value="%s"></td></tr>'

			// Interior color
			. '<tr><th scope="row"><label for="%s">%s</label></th>'
			. '<td><input type="text" name="%s" value="%s"></td></tr>'

			// Odometer
			. '<tr><th scope="row"><label for="%s">%s</label></th>'
			. '<td><input type="text" name="%s" value="%s">'
			. ' <span class="invp_odometer_units">%s</span></td></tr>'

			// YouTube
			. '<tr><th scope="row"><label for="%s">%s</label></th>'
			. '<td><input type="text" name="%s" value="%s"></td></tr>'

			// Beam (boats)
			. '<tr class="boat-postmeta"><th scope="row"><label for="%s">%s</label></th>'
			. '<td><input type="text" name="%s" value="%s"></td></tr>'

			// Length (boats)
			. '<tr class="boat-postmeta"><th scope="row"><label for="%s">%s</label></th>'
			. '<td><input type="text" name="%s" value="%s"></td></tr>'

			// Hull material
			. '<tr class="boat-postmeta"><th scope="row"><label for="%s">%s</label></th>'
			. '<td><select name="%s"><option></option>',
			apply_filters( 'invp_prefix_meta_key', 'color' ),
			__( 'Color', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'color' ),
			$color,
			apply_filters( 'invp_prefix_meta_key', 'interior_color' ),
			__( 'Interior color', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'interior_color' ),
			$interior_color,
			apply_filters( 'invp_prefix_meta_key', 'odometer' ),
			__( 'Odometer', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'odometer' ),
			$odometer,
			apply_filters( 'invp_odometer_word', 'miles' ),
			apply_filters( 'invp_prefix_meta_key', 'youtube' ),
			__( 'YouTube video ID', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'youtube' ),
			$youtube,
			apply_filters( 'invp_prefix_meta_key', 'beam' ),
			__( 'Beam', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'beam' ),
			$beam,
			apply_filters( 'invp_prefix_meta_key', 'length' ),
			__( 'Length', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'length' ),
			$length,
			apply_filters( 'invp_prefix_meta_key', 'hull_material' ),
			__( 'Hull material', 'inventory-presser' ),
			apply_filters( 'invp_prefix_meta_key', 'hull_material' )
		);

		$hull_materials = apply_filters(
			'invp_default_hull_materials',
			array(
				'Aluminum',
				'Carbon Fiber',
				'Composite',
				'Ferro-Cement',
				'Fiberglass',
				'Hypalon',
				'Other',
				'PVC',
				'Steel',
				'Wood',
			)
		);
		foreach ( $hull_materials as $m ) {
			printf(
				'<option%s>%s</option>',
				selected( $m, $hull_material, false ),
				$m
			);
		}
		echo '</select></tbody></table>';
	}

	/**
	 * move_advanced_meta_boxes
	 *
	 * Rearranges meta boxes on the editor when editing vehicles
	 *
	 * @return void
	 */
	function move_advanced_meta_boxes() {
		global $post, $wp_meta_boxes;
		$post_type = get_post_type( $post );

		if ( INVP::POST_TYPE != $post_type ) {
			return;
		}

		do_meta_boxes( get_current_screen(), 'advanced', $post );
		unset( $wp_meta_boxes[ get_post_type( $post ) ]['advanced'] );
	}

	/**
	 * move_tags_meta_box
	 *
	 * Remove and re-add the "Tags" meta box so it ends up at the bottom for our CPT
	 *
	 * @return void
	 */
	function move_tags_meta_box() {
		global $wp_meta_boxes;
		unset( $wp_meta_boxes[ INVP::POST_TYPE ]['side']['core']['tagsdiv-post_tag'] );
		add_meta_box(
			'tagsdiv-post_tag',
			'Tags',
			'post_tags_meta_box',
			INVP::POST_TYPE,
			'side',
			'core',
			array( 'taxonomy' => 'post_tag' )
		);
	}

	/**
	 * output_add_media_button_annotation
	 *
	 * AJAX callback to output the content we put near the Add Media button in
	 * the classic editor.
	 *
	 * @return void
	 */
	function output_add_media_button_annotation() {
		// because AJAX
		echo $this->create_add_media_button_annotation();
		wp_die();
	}

	/**
	 * populate_columns_we_added_to_vehicles_table
	 *
	 * Populates the custom columns we added to the posts table in the
	 * dashboard.
	 *
	 * @param  string $column_name
	 * @param  int    $post_id
	 * @return void
	 */
	function populate_columns_we_added_to_vehicles_table( $column_name, $post_id ) {
		$custom_fields = get_post_custom( $post_id );
		$val           = ( isset( $custom_fields[ $column_name ] ) ? $custom_fields[ $column_name ][0] : '' );
		switch ( true ) {
			case $column_name == apply_filters( 'invp_prefix_meta_key', 'thumbnail' ):
				echo edit_post_link( get_the_post_thumbnail( $post_id, 'thumbnail' ) );
				break;

			case $column_name == apply_filters( 'invp_prefix_meta_key', 'odometer' ):
				echo invp_get_the_odometer( '', $post_id );
				break;

			case $column_name == apply_filters( 'invp_prefix_meta_key', 'photo_count' ):
				echo invp_get_the_photo_count( $post_id );
				break;

			case $column_name == apply_filters( 'invp_prefix_meta_key', 'price' ):
				echo invp_get_the_price( '-', $post_id );
				break;

			default:
				echo $val;
		}
	}

	/**
	 * save_vehicle_post_meta
	 *
	 * Saves vehicle attributes into their corresponding post meta fields when
	 * the Save or Update button is clicked in the editor.
	 *
	 * @param  int     $post_id
	 * @param  WP_Post $post
	 * @param  bool    $is_update
	 * @return void
	 */
	function save_vehicle_post_meta( $post_id, $post, $is_update ) {
		/**
		 * Do not continue if the post is being moved to the trash or if this is
		 * an auto-draft.
		 */
		if ( in_array( $post->post_status, array( 'trash', 'auto-draft' ) ) ) {
			return;
		}

		// Abort if autosave or AJAX/quick edit
		if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
			|| ( defined( 'DOING_AJAX' ) && DOING_AJAX )
		) {
			return;
		}

		// is this a vehicle?
		if ( ! empty( $_POST['post_type'] ) && INVP::POST_TYPE != $_POST['post_type'] ) {
			// no, don't create any meta data for vehicles
			return;
		}

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

		/**
		 * Tick the last modified date of this vehicle since we're saving changes.
		 * It looks like this: Tue, 06 Sep 2016 09:26:12 -0400
		 */
		$offset           = sprintf( '%+03d00', intval( get_option( 'gmt_offset' ) ) );
		$timestamp_format = 'D, d M Y h:i:s';
		// use post_modified to set last_modified
		$post_modified = DateTime::createFromFormat( 'Y-m-d H:i:s', $post->post_modified );
		$timestamp     = $post_modified->format( $timestamp_format ) . ' ' . $offset;
		update_post_meta( $post_id, apply_filters( 'invp_prefix_meta_key', 'last_modified' ), $timestamp );

		/**
		 * If this is not an update or there is no date entered post meta value,
		 * set the date_entered meta value using the post_date
		 */
		if ( ! $is_update || empty( INVP::get_meta( 'date_entered', $post_id ) ) ) {
			// use post_date to set date_entered
			$post_date = DateTime::createFromFormat( 'Y-m-d H:i:s', $post->post_date );
			$timestamp = $post_date->format( $timestamp_format ) . ' ' . $offset;
			update_post_meta( $post_id, apply_filters( 'invp_prefix_meta_key', 'date_entered' ), $timestamp );
		}

		// Clear this value that is defined by a checkbox
		update_post_meta( $post_id, apply_filters( 'invp_prefix_meta_key', 'featured' ), '0' );

		/**
		 * Loop over the post meta keys we manage and save their values
		 * if we find them coming over as part of the post to save.
		 */
		$keys   = INVP::keys();
		$keys[] = 'options_array';

		foreach ( $keys as $key ) {
			$key = apply_filters( 'invp_prefix_meta_key', $key );
			if ( isset( $_POST[ $key ] ) ) {
				if ( is_array( $_POST[ $key ] ) ) {
					// delete all meta, this is essentially for the options
					delete_post_meta( $post->ID, $key );
					$options = array(); // collect the options to maintain a CSV field for backwards compatibility

					foreach ( $this->sanitize_array( $_POST[ $key ] ) as $value ) {
						add_post_meta( $post->ID, $key, $value );
						if ( 'inventory_presser_options_array' == $key ) {
							   $options[] = $value;
						}
					}
				} else {
					// string data
					update_post_meta( $post->ID, $key, sanitize_text_field( $_POST[ $key ] ) );
				}
			}
		}
	}

	/**
	 * save_taxonomy_term
	 *
	 * Create a term relationship between a post and a term. Inserts the term
	 * first if it does not exist.
	 *
	 * @param  int    $post_id
	 * @param  string $taxonomy_name
	 * @param  string $element_name
	 * @return void
	 */
	function save_taxonomy_term( $post_id, $taxonomy_name, $element_name ) {
		if ( ! isset( $_POST[ $element_name ] ) ) {
			return;
		}

		$term_slug = sanitize_text_field( $_POST[ $element_name ] );
		if ( '' == $term_slug ) {
			// the user is setting the vehicle type to empty string
			wp_remove_object_terms( $post_id, Inventory_Presser_Taxonomies::get_term_slug( $taxonomy_name, $post_id ), $taxonomy_name );
			return;
		}
		$term = get_term_by( 'slug', $term_slug, $taxonomy_name );
		if ( empty( $term ) || is_wp_error( $term ) ) {
			// the term does not exist. create it
			$term_arr = array(
				'slug'        => sanitize_title( $term_slug ),
				'description' => $term_slug,
				'name'        => $term_slug,
			);
			$id_arr   = wp_insert_term( $term_slug, $taxonomy_name, $term_arr );
			if ( ! is_wp_error( $id_arr ) ) {
				$term->term_id = $id_arr['term_id'];
			}
		}
		$set = wp_set_object_terms( $post_id, $term->term_id, $taxonomy_name, false );
		if ( is_wp_error( $set ) ) {
			// There was an error setting the term
		}
	}

	/**
	 * save_vehicle_taxonomy_terms
	 *
	 * Saves custom taxonomy terms when vehicles are saved
	 *
	 * @param  int  $post_id
	 * @param  bool $is_update
	 * @return void
	 */
	function save_vehicle_taxonomy_terms( $post_id, $is_update ) {
		foreach ( $this->taxonomies_slugs_array() as $slug ) {
			$taxonomy_name = $slug;
			switch ( $slug ) {
				case 'style':
					$slug = 'body_style';
					break;
				case 'model_year':
					$slug = 'year';
					break;
			}
			$this->save_taxonomy_term( $post_id, $taxonomy_name, apply_filters( 'invp_prefix_meta_key', $slug ) );
		}
	}

	/**
	 * sanitize_array
	 *
	 * Sanitizes every member of an array at every level with
	 * sanitize_text_field()
	 *
	 * @param  array $arr
	 * @return array
	 */
	function sanitize_array( $arr ) {
		foreach ( $arr as $key => $value ) {
			if ( is_array( $value ) ) {
				$arr[ $key ] = $this->sanitize_array( $value );
			} else {
				$arr[ $key ] = sanitize_text_field( $value );
			}
		}
		return $arr;
	}

	/**
	 * taxonomies_slugs_array
	 *
	 * Returns an array of all our taxonomy slugs
	 *
	 * @return array An array of taxonomy slugs
	 */
	protected function taxonomies_slugs_array() {
		$arr = array();
		foreach ( Inventory_Presser_Taxonomies::query_vars_array() as $query_var ) {
			array_push( $arr, str_replace( '-', '_', $query_var ) );
		}
		return $arr;
	}

	/**
	 * vehicles_table_columns_orderbys
	 *
	 * Change the dashboard post query to sort based on a custom column we
	 * added.
	 *
	 * @param  WP_Query $query
	 * @return void
	 */
	function vehicles_table_columns_orderbys( $query ) {
		if ( ! is_admin() || ! $query->is_main_query() ) {
			return;
		}

		$columns = array(
			'color',
			'odometer',
			'price',
			'stock_number',
		);
		$orderby = $query->get( 'orderby' );
		foreach ( $columns as $column ) {
			$meta_key = apply_filters( 'invp_prefix_meta_key', $column );
			if ( $orderby == $meta_key ) {
				$query->set( 'meta_key', $meta_key );
				$query->set( 'orderby', 'meta_value' . ( INVP::meta_value_is_number( $meta_key ) ? '_num' : '' ) );
				return;
			}
		}
	}
}

Top ↑

Methods Methods


Top ↑

Changelog Changelog

Changelog
Version Description
1.3.1 Introduced.