File "SWP_Buttons_Panel.php"

Full Path: /home/bfxleof/www/wp-content/plugins/social-warfare/lib/buttons-panel/SWP_Buttons_Panel.php
File size: 20.01 KB
MIME-type: text/x-php
Charset: utf-8

<?php

/**
 * The Buttons_Panel object class.
 *
 * Creates the Panel of share buttons based on options and settings. The class
 * is comprised of four main sections:
 *
 *
 * TABLE OF CONTENTS:
 *
 * SECTION #1: Insantiate a post object & compile all necessary data (protected).
 *     __construct();
 *     establish_post_data();
 *     establish_local_options();
 *     establish_share_data();
 *     establish_location();
 *     establish_permalink();
 *     establish_active_buttons();
 *
 * SECTION #2: Allow developers to manipulate the object using setters (public).
 *     set_option();
 *     set_options();
 *
 * SECTION #3: Use the data to render out the HTML to display a panel of buttons (public).
 *     render_html();
 *
 * SECTION #4: Utility methods used throughout the class (protected).
 *
 * 	   NOTE: These are inherited from SWP_Buttons_Panel_Trait.
 *
 *     generate_panel_html();
 *     generate_individual_buttons_html();
 *     generate_total_shares_html();
 *
 *     should_panel_display();
 *     should_total_shares_display();
 *
 *     get_alignment();
 *     get_colors();
 *     get_shape();
 *     get_scale();
 *     get_min_width();
 *     get_float_background();
 *     get_option();
 *     get_mobile_float_location();
 *     get_order_of_icons();
 *     get_ordered_network_objects();
 *     get_key_from_name();
 *
 *
 *
 * @package   SocialWarfare\Functions
 * @copyright Copyright (c) 2018, Warfare Plugins, LLC
 * @license   GPL-3.0+
 * @since     3.0.0 | 01 MAR 2018 | Created
 * @since     3.4.0 | 20 SEP 2018 | Moved the floating buttons panel out of this
 *                                  class and into a child class of this class.
 *
 */
class SWP_Buttons_Panel {


	use SWP_Buttons_Panel_Trait;
	use SWP_Debug_Trait;


	/**
	 * This is the type of panel being generated by this class. The child class
	 * for the side floating buttons will set this to floating_side.
	 *
	 */
	public $panel_type = 'static_horizontal';


	/**
	 * Options
	 *
	 * We're using a local property to clone in the global $swp_user_options
	 * array. As a local options we and other developers accessing this object
	 * can use getters and setters to change any options possibly imaginable
	 * without affecting the global options.
	 *
	 * @var array
	 *
	 */
	public $options = array();


	/**
	 * The Post ID
	 *
	 * @var int
	 *
	 */
	public $post_id;


	/**
	 * The location of the buttons in relation to the content.
	 *
	 * @var string above | below | both | none
	 *
	 */
	public $location = 'above';


	/**
	 * Arguments
	 *
	 * A temporary property used to store and access any arguments passed into
	 * the constructor. These will then be processed into the other properties
	 * as possible.
	 *
	 * @var array $args;
	 *
	 */
	public $args = array();


	/**
	 * The Content
	 *
	 * The WordPress content to which we are going to append the HTML of these buttons.
	 *
	 * @var string $content;
	 *
	 */
	public $content = '';


	/**
	 * The fully qualified HTML for the Buttons Panel.
	 *
	 * @var string $html;
	 *
	 */
	public $html = '';


	/**
	 * The array of active buttons for $this Social Panel.
	 *
	 * @var array $active_buttons;
	 *
	 */
	public $active_buttons = array();


	/**
	 * The sum of share counts across active networks.
	 *
	 * @var integer $total_shares;
	 *
	 */
	public $total_shares = 0;


	/**
	 * The maximum number of buttons to display in the panel. If set to zero,
	 * the maximum number of buttons will be unlimited.
	 *
	 * @var integer $max_buttons
	 *
	 */
	public $max_buttons = 0;



	/***************************************************************************
	 *
	 * SECTION #1: INSTANTIATE THE OBJECT
	 *
	 * This is the first of three sections. In this section, we will set up all
	 * the necessary information in order to render out the buttons panel later.
	 *
	 *
	 */


	/**
	 * The Construct Method
	 *
	 * This method creates the Buttons_Panel object. It gathers all of the
	 * necessary data, the user options, the share counts, and stores it all in
	 * local properties. Later we will call the public method render_html()
	 * (e.g. $Buttons_Panel->render_html(); ) to actually render out the panel to
	 * the screen.
	  *
	 * @since  3.0.0 | 01 MAR 2018 | Created
	 * @since  3.1.0 | 05 JUL 2018 | Created debug() & establish_post_data() methods.
	 * @since  3.4.0 | 20 SEP 2018 | Moved establish_post_id() into a conditional.
	 * @param  array optional $args The arguments passed in via shortcode.
	 * @param  boolean optional $shortcode If a shortcode/direct function
	 *                          is calling this class.
	 * @return void
	 *
	 */
	public function __construct( $args = array(), $shortcode = false ) {
		global $swp_social_networks;


		/**
		 * Pull in necessary data so that the methods below can use it to setup
		 * the Buttons_panel object properly.
		 *
		 */
		$this->networks     = $swp_social_networks;
		$this->args         = $args;
		$this->content      = isset( $args['content'] ) ? $args['content'] : '';
		$this->is_shortcode = $shortcode;


		/**
		 * The establish_post_id() runs several checks including fallback
		 * methods to ensure that there is an available post_id for us to use.
		 * However, if it fails to find a valid post_id, it will return false,
		 * and if that is the case, we bail. We can't build a set of buttons
		 * without one present.
		 *
		 */
		if ( false === $this->establish_post_id() ) {
			return;
		}


		/**
		 * Step by step, these methods walk through the process of compiling
		 * everything we'll need in order to render out a panel of buttons
		 * according to the user options, the per post options, share counts, etc.
		 *
		 */
		$this->establish_post_data();
		$this->establish_local_options();
		$this->establish_share_data();
		$this->establish_location();
		$this->establish_permalink();
		$this->establish_active_buttons();
		add_action( 'wp_footer', array( $this, 'debug' ) );
	}


	/**
	 * Set the post ID for this buttons panel.
	 *
	 * We want to use the global post ID for whichever post is being looped
	 * through unless the post ID has been passed in as an argument.
	 *
	 * @since  3.0.0 | 09 APR 2018 | Created
	 * @since  3.4.0 | 20 SEP 2018 | Refactored to refine logic.
	 * @param  array $args The array of args passed in.
	 * @return boolean     True on success; False on failure.
	 *
	 */
	 public function establish_post_id() {


		/**
		 * Cycle through the available post_id labels, find which one was passed
		 * in and use it as the post_id for this panel of buttons.
		 *
		 */
		 $id_labels = array( 'id', 'post_id', 'postid' );
		 foreach( $id_labels as $label ) {
			 if ( isset( $this->args[$label] ) && is_numeric( $this->args[$label] ) ) {
				 $this->post_id = $this->args[$label];
				 return true;
			 }
		 }


		/**
		 * If the user provided a URL instead of an ID, let's see if we can
		 * convert it into a valid WordPress ID for a post.
		 *
		 */
		if ( isset( $this->args['url'] ) && $post_id_from_url = url_to_postid( $this->args['url'] ) ) {
			$this->post_id = $post_id_from_url;
			return true;
		}


		/**
		 * If the user didn't pass in any arguments related to an ID or URL then
		 * we will just use the post id of the current post.
		 *
		 */
		global $post;
		if ( is_object( $post ) ) :
			$this->post_id = $post->ID;
			return true;
		endif;


		/**
		 * If we were completely unable to establish a post_id, then return
		 * false.
		 *
		 */
		return false;

	   }


	/**
	 * Set the post data for this buttons panel.
	 *
	 * @since  3.1.0 | 05 JUL 2018 | Created
	 * @param  void
	 * @return void
	 *
	 */
	public function establish_post_data() {

		// Fetch the post object.
		$post = get_post( $this->post_id );

		// Bail if the post object failed.
		if ( !is_object( $post ) ) {
			return;
		}

		// Set up the post data.
		$this->post_data = array(
			'ID'           => $post->ID,
			'post_type'    => $post->post_type,
			'permalink'    => get_the_permalink( $post->ID ),
			'post_title'   => $post->post_title,
			'post_status'  => $post->post_status,
			'post_content' => $post->post_content
		);
	}


	/**
	 * Localize the global options
	 *
	 * The goal here is to move the global $swp_options array into a local
	 * property so that the options for this specific instantiation of the
	 * buttons panel can have the options manipulated prior to rendering the
	 * HTML for the panel. We can do this by using getters and setters or by
	 * passing in arguments.
	 *
	 * @since  3.0.0 | 09 APR 2018 | Created
	 * @param  void
	 * @return void
	 *
	 */
	protected function establish_local_options() {
		global $swp_user_options;
		$this->options = array_merge( $swp_user_options, $this->args);
	}


	/**
	 * Establishes the share data from a given post ID.
	 *
	 * @since 3.1.0 | 25 JUN 2018 | Created the method.
	 * @param  void
	 * @return $this Allows for method chaining.
	 *
	 */
	public function establish_share_data() {
		global $SWP_Post_Caches;
		$this->shares = $SWP_Post_Caches->get_post_cache( $this->post_id )->get_shares();

		return $this;
	}


	/**
	 * Establish the Location of the Buttons Panel
	 *
	 * A method to handle figuring out where in the content these buttons are
	 * supposed to appear. It has to check the global options, the options set
	 * on the post, and be able to tell if this is being called without any
	 * content to which to append.
	 *
	 * @since  3.0.0 | 10 APR 2018 | Created
	 * @since  3.0.7 | 15 MAY 2018 | Added conditionals to ensure $post_setting isn't an array.
	 * @since  3.4.2 | 11 DEC 2018 | Added check for $this->is_shortcode.
	 * @param  void
	 * @return void All values are stored in local properties.
	 *
	 */
	public function establish_location() {


		/**
		 * Establish the default as none. If nothing gets caught in the
		 * conditionals below, the buttons should not be displayed so we'll
		 * return with the location set to 'none'.
		 *
		 */
		$this->location = 'none';


		/**
		 * In previous versions, we had reports of buttons showing up on media
		 * attachment pages. This prevents that from happening by returning with
		 * $this->location still set to 'none'.
		 *
		 */
		if( is_attachment() ) {
			return;
		}


		/**
		 * If this class is instantiated via the [social_warfare] shortcode or
		 * the social_warfare() function, then it will pass in during
		 * instantiation a paramter which will toggle our local property of
		 * $this->is_shortcode from falst to true.
		 *
		 * If it is being called directly, the content will be blank, but we will
		 * want to append our buttons at least once in order to actually return
		 * some html to be printed on the screen. As such, any option that results
		 * in the buttons being appended once (in this case we'll use above) is
		 * what we need.
		 *
		 */
		if ( empty( $this->content ) && true == $this->is_shortcode ) {
			$this->location = 'above';
			return;
		}


		/**
		 * Location from the Post Options
		 *
		 * If the location was specified on the post options, we'll make sure
		 * to use this instead of the global options.
		 *
		 */
		$post_setting = get_post_meta( $this->post_id, 'swp_post_location', true );


		/**
		 * Since we passed true in as the third parameter of get_post_meta(), it
		 * should never return an array of items, but in a few rare instances,
		 * we saw it doing so anyways. If this does happen, we'll just pull the
		 * first item from the array and use that instead.
		 *
		 */
		if( is_array($post_setting) ) {
			 $post_setting = $post_setting[0];
		}


		/**
		 * This checks to see if the user has explicitly set the buttons location
		 * at the post level in the post editor. If so, we use this instead of
		 * using any of the global or post type settings for the location.
		 *
		 */
		if ( is_singular() && !empty( $post_setting ) && 'default' != $post_setting ) {
			$this->location = $post_setting;
			return;
		}


		/**
		 * Global Location Settings
		 *
		 * Decide which post type we're on and pull the location setting
		 * for that post type from the global options.
		 *
		 */


		/**
		 * If we are on the home page of the site then we'll use the location_home
		 * option from the settings page. This conditional needs to come prior
		 * to the singular() check because some home pages use WordPress pages
		 * which would make that conditional evaluate as true. But we want this
		 * one to trump that one so this comes first.
		 *
		 */
		$home_location = $this->get_option( 'location_home' );
		if( is_front_page() && !empty( $home_location ) ) {
			$this->location = $home_location;
			return;
		}


		/**
		 * If we are on a singular() post/page and we have a valid location
		 * setting for this particular post type, then we'll use that setting.
		 *
		 * If this is set to FALSE then it means that this option was not
		 * available for some reason as all valid values for this option are
		 * strings. As such, we'll leave the location setting to 'none'.
		 *
		 */
		$post_type_location = $this->get_option( 'location_' . $this->post_data['post_type'] );
		if ( is_singular() && !empty( $post_type_location ) ) {
			$this->location = $post_type_location;
			return;
		}

		// If we are on the blogroll or an archive listing.
		if ( is_archive() || is_home() ) {
			$this->location = $this->get_option( 'location_archive_categories' );
			return;
		}
	}


	/**
	 * A method for fetching the permalink.
	 *
	 * @since  3.0.0 | 01 MAR 2018 | Created
	 * @param  void
	 * @return void Values are stored in $this->permalink.
	 *
	 */
	protected function establish_permalink() {
		$this->permalink = get_permalink( $this->post_id );
	}


	/**
	 * A method to establish the active buttons for this panel.
	 *
	 * First it will check to see if user arguments have been passed in. If not,
	 * it will check to see if they are set to manual or dynamic sorting. If
	 * manual, we will use the buttons in the order they were stored in the
	 * options array (they were set in this order on the options page.) If
	 * dynamic, we will look at the share counts and order them with the largest
	 * share counts appearing first.
	 *
	 * The results will be stored as an ordered array of network objects in the
	 * $this->networks property.
	 *
	 * @since  3.0.0 | 04 MAY 2018 | Created
	 * @param  void
	 * @return object $this Allows for method chaining.
	 *
	 */
	public function establish_active_buttons() {
		$network_objects = array();


		/**
		 * If the user passed in an array of buttons either via the social_warfare()
		 * function of the [social_warfare buttons="buttons"] shortcode, these
		 * will take precedence over the buttons that are selected on the
		 * Social Warfare options page.
		 *
		 */
		if ( isset( $this->args['buttons'] ) ) {
			$this->args['buttons'] = explode( ',', $this->args['buttons'] );


			/**
			 * Trim out white space. We need to trim any whitespace in case
			 * folks put a space before or after any of the commas separating
			 * the button names that were passed in.
			 *
			 * e.g. [social_warfare buttons="twitter, google_plus"]
			 *
			 */
			foreach( $this->args['buttons'] as $index => $button ) {
				$this->args['buttons'][$index] = trim( $button );
			}


			/**
			 * Loop through the passed-in array of buttons, find the global
			 * Social_Network object associated to each network, and store them
			 * in the $network_objects array.
			 *
			 */
			foreach ( $this->args['buttons'] as $network_name ) {
				$network_key = $this->get_key_from_name( $network_name );
				foreach( $this->networks as $key => $network ):
					if( $network_key === $key ):
						$network_objects[] = $network;
					endif;
				endforeach;
			}

			// Store it in the $networks property and terminate the method.
			$this->networks = $network_objects;
			return $this;

		}

		$order           = $this->get_order_of_icons();
		$network_objects = $this->get_ordered_network_objects( $order );
		$this->networks  = $network_objects;
		return $this;
	}




	/***************************************************************************
	 *
	 * SECTION #2: ALLOW DEVELOPERS ACCESS TO SETTERS
	 *
	 * This section allows developers to manipulate the Buttons_Panel object
	 * prior to the final rendering of the html at the end.
	 *
	 *
	 */


	/**
	 * Set an option
	 *
	 * This method allows you to change one of the options for the buttons panel.
	 *
	 * @since  3.0.0 | 09 APR 2018 | Created
	 * @param  string $option The key of the option to be set.
	 * @param  mixed  $value  The value to which we will set that option.
	 * @return object $this   Allows for method chaining.
	 * @access public
	 *
	 */
	public function set_option( $option = '', $value = null ) {
		if ( empty( $option ) ) :
			$message = "Hey developer, " . __CLASS__ . __METHOD__ . "  a first paramter $option (string) and \$value (mixed). You provided " . gettype($value) . ".";
			throw new Exception($message);
		elseif ( null == $value ) :
			$message = "Hey developer, " . __CLASS__ . __METHOD__ . " a second paramter: \$value (mixed type). You provided " . gettype($value) . ".";
			throw new Exception($message);
		endif;

		$this->options[$this->options] = $value;
		return $this;
	}


	/**
	 * Set multiple options
	 *
	 * This method allows you to change multiple options for the buttons panel.
	 *
	 * @since  3.0.0 | 09 APR 2018 | Created
	 * @param  array  $this->options An array of options to be merged into the
	 *                               existing options.
	 * @return object $this          Allows for method chaining.
	 *
	 */
	public function set_options( $options = array() ) {
		if ( !is_array( $options) ) :
			$message = "Hey developer, " . __CLASS__ . __METHOD__ . " requires an arry of options. You provided " . gettype($options) . ".";
			throw new Exception($message);
		endif;

		array_merge( $this->options , $options );
		return $this;
	}




	/***************************************************************************
	 *                                                                         *
	 * SECTION #3: RENDER THE FULLY QUALIFIED HTML FOR THE PANEL               *
	 *                                                                         *
	 * This section will use all of the data created by the object and process *
	 * it all into properly formatted html for display on the screen.          *
	 *                                                                         *
	 ***************************************************************************/


	 /**
	  * Runs checks before ordering a set of buttons.
	  *
	  * @since  3.0.6 | 14 MAY 2018 | Removed the swp-content-locator div.
	  * @since  3.3.3 | 18 SEP 2018 | Added return value for
	  *                               should_panel_display() condition.
	  * @since  3.4.0 | 21 SEP 2018 | Removed $content parameter.
	  * @since  3.4.0 | 24 OCT 2018 | Simplified, combined with method from the
	  *                               side panel child class.
	  * @param  string $content The WordPress content, if passed in.
	  *
	  */
	public function render_html() {


		 /**
		  * We have a standalone method designed to let us know if all the proper
		  * desired conditions are met in order to allow us to print the buttons.
		  *
		  */
		if ( false == $this->should_panel_display() ) {
			return $this->content;
		}


		/**
		 * Compile the entire buttons panel. Generate the CSS classes, generate
		 * the html attributes, generate the html for the buttons and total
		 * shares. Then attach it all to the content and return it to the caller.
		 *
		 */
		$this->generate_css_classes();
		$this->generate_attributes();
		$this->generate_buttons_and_totals_html();
		$this->combine_html_assets();
		 $this->append_panel_to_content();

		return $this->content;
	}
}