File "hustle-sshare-model.php"
Full Path: /home/londdqdw/public_html/06/wp-content/plugins/wordpress-popup/inc/hustle-sshare-model.php
File size: 21.53 KB
MIME-type: text/x-php
Charset: utf-8
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
/**
* Test CPT conditions
*
* @package Hustle
*/
/**
* Hustle_SShare_Model class
*/
class Hustle_SShare_Model extends Hustle_Model {
const SETTINGS_KEY = 'sshare_counters';
const COUNTER_META_KEY = 'hustle_shares';
const TIMESTAMP_META_KEY = 'hustle_timestamp';
const REFRESH_OPTION_KEY = 'hustle_ss_refresh_counters';
const FLOAT_DESKTOP = 'float_desktop';
const FLOAT_MOBILE = 'float_mobile';
const FLOAT_MODULE = 'floating';
/**
* Display settings
*
* @var array
*/
public $display;
/**
* Get the sub-types for this module.
*
* @since 4.2.0
* @param bool $with_titles With titles.
*
* @return array
*/
public static function get_sshare_types( $with_titles = false ) {
if ( ! $with_titles ) {
return array( self::FLOAT_MODULE, 'inline', 'widget', 'shortcode' );
} else {
return array(
self::FLOAT_MODULE => __( 'Floating', 'hustle' ),
'inline' => __( 'Inline', 'hustle' ),
'widget' => __( 'Widget', 'hustle' ),
'shortcode' => __( 'Shortcode', 'hustle' ),
);
}
}
/**
* Get content
*
* @return \Hustle_SShare_Content
*/
public function get_content() {
return new Hustle_SShare_Content( $this->get_settings_meta( self::KEY_CONTENT ), $this );
}
/**
* Get design
*
* @return \Hustle_SShare_Design
*/
public function get_design() {
return new Hustle_SShare_Design( $this->get_settings_meta( self::KEY_DESIGN ), $this );
}
/**
* Get the stored settings for the "Display" tab.
*
* @since 4.0
*
* @return Hustle_SShare_Display
*/
public function get_display() {
return new Hustle_SShare_Display( $this->get_settings_meta( self::KEY_DISPLAY_OPTIONS ), $this );
}
/**
* Gets the instance of the decorator class for this module type.
*
* @since 4.3.0
*
* @return Hustle_Decorator_Sshare
*/
public function get_decorator_instance() {
return new Hustle_Decorator_Sshare( $this );
}
/**
* Create a new module of the provided mode and type.
*
* @since 4.0
*
* @param array $data Must contain the Module's 'mode', 'name' and 'type.
* @return int|false Module ID if successfully saved. False otherwise.
*/
public function create_new( $data ) {
// Verify it's a valid module type.
if ( self::SOCIAL_SHARING_MODULE !== $data['module_type'] ) {
return false;
}
// Save to modules table.
$this->module_name = sanitize_text_field( $data['module_name'] );
$this->module_type = $data['module_type'];
$this->active = 0;
$this->module_mode = '';
$this->save();
// Save the new module's meta.
$this->store_new_module_meta( $data );
return $this->id;
}
/**
* Store the defaults meta when creating a new module.
*
* @since 4.0.0
*
* @param array $data Data to store.
*/
private function store_new_module_meta( $data ) {
$def_content = apply_filters( 'hustle_module_get_' . self::KEY_CONTENT . '_defaults', $this->get_content()->to_array(), $this, $data );
$content_data = empty( $data['content'] ) ? $def_content : array_merge( $def_content, $data['content'] );
$def_design = apply_filters( 'hustle_module_get_' . self::KEY_DESIGN . '_defaults', $this->get_design()->to_array(), $this, $data );
$design_data = empty( $data['design'] ) ? $def_design : array_merge( $def_design, $data['design'] );
// Visibility settings.
$def_visibility = apply_filters( 'hustle_module_get_' . self::KEY_VISIBILITY . '_defaults', $this->get_visibility()->to_array(), $this, $data );
$visibility_data = empty( $data['visibility'] ) ? $def_visibility : array_merge( $def_visibility, $data['visibility'] );
// Display options.
$def_display = apply_filters( 'hustle_module_get_' . self::KEY_DISPLAY_OPTIONS . '_defaults', $this->get_display()->to_array(), $this, $data );
$display_data = empty( $data['display'] ) ? $def_display : array_merge( $def_display, $data['display'] );
// Save to meta table.
$this->update_meta( self::KEY_CONTENT, $content_data );
$this->update_meta( self::KEY_DESIGN, $design_data );
$this->update_meta( self::KEY_VISIBILITY, $visibility_data );
$this->update_meta( self::KEY_DISPLAY_OPTIONS, $display_data );
$this->enable_type_track_mode( $this->module_type, true );
}
/**
* Sanitize/Replace the module's data.
*
* @param array $data Data to sanitize.
* @return array Sanitized data.
*/
public function sanitize_module( $data ) {
$data = Opt_In_Utils::validate_and_sanitize_fields( $data );
return $data;
}
/**
* Validates the module's data.
*
* @since 4.0.3
*
* @param array $data Data to validate.
* @return array
*/
public function validate_module( $data ) {
$icons = isset( $data['content']['social_icons'] ) ? $data['content']['social_icons'] : array();
$display = $data['display'];
$selector = array(
'desktop' => isset( $display['float_desktop_offset'] ) ? $display['float_desktop_offset'] : '',
'mobile' => isset( $display['float_mobile_offset'] ) ? $display['float_mobile_offset'] : '',
);
$errors = array();
// Name validation.
if ( empty( sanitize_text_field( $data['module']['module_name'] ) ) ) {
$errors['error']['name_error'] = __( 'This field is required', 'hustle' );
}
// Social platform url check.
if ( ! empty( $icons ) ) {
foreach ( $icons as $key => $icon ) {
$icon_with_enpoints = self::get_sharing_endpoints();
if ( ! in_array( $icon['platform'], $icon_with_enpoints, true ) && empty( $icon['link'] ) ) {
$errors['error']['icon_error'][] = $icon['platform'];
}
}
}
// css selector check.
if ( ! empty( $selector ) ) {
if ( 'css_selector' === $selector['desktop'] && empty( $display['float_desktop_css_selector'] )
&& ! empty( $display['float_desktop_enabled'] ) ) {
$errors['error']['selector_error'][] = 'float_desktop';
}
if ( 'css_selector' === $selector['mobile'] && empty( $display['float_mobile_css_selector'] ) && ! empty( $display['float_mobile_enabled'] ) ) {
$errors['error']['selector_error'][] = 'float_mobile';
}
}
// return errors if any.
if ( ! empty( $errors ) ) {
$errors['success'] = false;
return $errors;
}
return true;
}
/**
* Updates the metas specific for Social Sharing modules.
*
* @since 4.3.0
* @param array $data Data to save.
* @return void
*/
protected function update_module_metas( $data ) {
// Meta used in all module types.
if ( isset( $data['content'] ) ) {
$this->update_meta( self::KEY_CONTENT, $data['content'] );
}
// Meta used in all module types.
if ( isset( $data['visibility'] ) ) {
$this->update_meta( self::KEY_VISIBILITY, $data['visibility'] );
}
if ( isset( $data['design'] ) ) {
$this->update_meta( self::KEY_DESIGN, $data['design'] );
}
if ( isset( $data['display'] ) ) {
$this->update_meta( self::KEY_DISPLAY_OPTIONS, $data['display'] );
}
// Force all counters to retrieve the data from the APIs.
self::refresh_all_counters();
}
/**
* Get the module's data. Used to display it.
*
* @since 4.3.0
*
* @return array
*/
public function get_module_data_to_display() {
return $this->get_data();
}
/**
* Get the sub-types for this module.
*
* @since 4.3.1
* @param bool $with_titles With titles.
*
* @return array
*/
public function get_sub_types( $with_titles = false ) {
return self::get_sshare_types( $with_titles );
}
/**
* Return whether or not the requested counter type is enabled
*
* @since 3.0.3
*
* @param string $type Type.
* @return boolean
*/
public function is_click_counter_type_enabled( $type ) {
$content = $this->get_content()->to_array();
if ( isset( $content['click_counter'] ) && $content['click_counter'] === $type ) {
return true;
} else {
return false;
}
}
/**
* Get social patform names
*
* @return array
*/
public static function get_social_platform_names() {
$social_platform_names = array(
'facebook' => esc_html__( 'Facebook', 'hustle' ),
'twitter' => esc_html__( 'Twitter', 'hustle' ),
'pinterest' => esc_html__( 'Pinterest', 'hustle' ),
'reddit' => esc_html__( 'Reddit', 'hustle' ),
'linkedin' => esc_html__( 'LinkedIn', 'hustle' ),
'vkontakte' => esc_html__( 'Vkontakte', 'hustle' ),
'fivehundredpx' => esc_html__( '500px', 'hustle' ),
'houzz' => esc_html__( 'Houzz', 'hustle' ),
'instagram' => esc_html__( 'Instagram', 'hustle' ),
'twitch' => esc_html__( 'Twitch', 'hustle' ),
'youtube' => esc_html__( 'YouTube', 'hustle' ),
'telegram' => esc_html__( 'Telegram', 'hustle' ),
'whatsapp' => esc_html__( 'WhatsApp', 'hustle' ),
'email' => esc_html__( 'Email', 'hustle' ),
);
/**
* Social networks list
*
* @since 4.0.4
*
* @param array $social_platform_names {slug} => {name}
*/
return apply_filters( 'hustle_social_platform_names', $social_platform_names );
}
/**
* Get the stored counter of each network.
*
* @since 3.0.3
*
* @param integer $post_id Post ID.
* @return array
*/
public function get_stored_network_shares( $post_id ) {
if ( 0 !== $post_id ) {
$stored_counters = get_post_meta( $post_id, self::COUNTER_META_KEY, true );
} else {
$sshare_networks_option = Hustle_Settings_Admin::get_hustle_settings( self::SETTINGS_KEY );
$stored_counters = ! empty( $sshare_networks_option[ self::COUNTER_META_KEY ] ) ? $sshare_networks_option[ self::COUNTER_META_KEY ] : array();
}
return $stored_counters;
}
/**
* Check if stored values should be used.
*
* @since 3.0.3
*
* @param integer $post_id Post ID.
* @param bool $check_expiration_time Optional. Check expiration time or not.
* @return bool
*/
public function should_use_stored( $post_id, $check_expiration_time = false ) {
// If we're in a page/post...
if ( 0 !== $post_id ) {
// Don't use stored if we don't have anything stored in this post.
if ( ! get_post_meta( $post_id, self::COUNTER_META_KEY ) ) {
return false;
}
// If we're in somewhere that's not a page/post...
} else {
// Don't use stored if we don't have anything stored in the options.
$sshare_networks_option = Hustle_Settings_Admin::get_hustle_settings( self::SETTINGS_KEY );
if ( empty( $sshare_networks_option ) || empty( $sshare_networks_option[ self::COUNTER_META_KEY ] ) ) {
return false;
}
}
// Do use stored values if traffic is a crawler/bot.
$user_agent = filter_input( INPUT_SERVER, 'HTTP_USER_AGENT', FILTER_SANITIZE_SPECIAL_CHARS );
if ( $user_agent && preg_match( '/bot|crawler|ia_archiver|mediapartners-google|80legs|wget|voyager|baiduspider|curl|yahoo!|slurp/i', $user_agent ) ) {
return true;
}
if ( $check_expiration_time ) {
if ( 0 !== $post_id ) {
$timestamp = intval( get_post_meta( $post_id, self::TIMESTAMP_META_KEY, true ) );
} else {
$timestamp = isset( $sshare_networks_option[ self::TIMESTAMP_META_KEY ] ) ? $sshare_networks_option[ self::TIMESTAMP_META_KEY ] : 'true';
}
// Don't use stored if the expiration time of the counter already passed.
if ( 'true' === $timestamp || time() > ( $timestamp + ( 6 * 60 * 60 ) ) ) {
return false;
}
// Don't use stored if the counter hasn't beeen updated after the last time that all counters were cleared.
$clear_counters_time = get_option( self::REFRESH_OPTION_KEY, false );
if ( $clear_counters_time && $timestamp < intval( $clear_counters_time ) ) {
return false;
}
}
return true;
}
/**
* Set the number of shares of each network in an array. Like $result[facebook] = 44
*
* @since 3.0.3
* @since 4.0
*
* @param array $networks Networks.
* @param integer $post_id Post ID.
* @return array
*/
public function retrieve_networks_shares( $networks, $post_id ) {
// TODO: handle homepage when post_id is not a specific page.
$post_id = apply_filters( 'hustle_network_shares_post_id', $post_id );
$current_link = ( 0 !== $post_id && get_permalink( $post_id ) ) ? get_permalink( $post_id ) : home_url();
// We share URL without trailing slash, So we need to remove slash when we fetch from API.
$current_link = untrailingslashit( $current_link );
$current_link = apply_filters( 'hustle_network_shares_from_url', $current_link );
// If we should use the stored values instead of retrieving them from the API.
$should_use_stored = apply_filters( 'hustle_sshare_should_use_stored_counters', $this->should_use_stored( $post_id, true ), $post_id, $current_link );
if ( $should_use_stored ) {
$networks_info = $this->get_stored_network_shares( $post_id );
// Call the API for the networks we don't have the value stored.
$missing_networks = array_diff( $networks, array_keys( $networks_info ) );
if ( ! empty( $missing_networks ) ) {
$missing_info = $this->get_refreshed_counters( $current_link, $missing_networks, $post_id );
$networks_info = array_merge( $networks_info, $missing_info );
}
} else {
$networks_info = $this->get_refreshed_counters( $current_link, $networks, $post_id );
}
return $networks_info;
}
/**
* Get the counter from the APIs, store it in the post_meta, and store the current time for expiration.
*
* @since 4.0
*
* @param string $current_link Current link.
* @param array $networks Networks.
* @param id $post_id Post ID.
* @return array
*/
public function get_refreshed_counters( $current_link, $networks, $post_id ) {
// Get array with json formatted data for each active network.
$networks_info = $this->get_networks_data_from_api( $current_link, $networks );
$networks_info = $this->format_networks_api_raw_data( $networks_info, false );
// Store the values for "caching".
if ( 0 !== $post_id ) {
// Store the values of all new networks.
$stored_info = get_post_meta( $post_id, self::COUNTER_META_KEY, true );
if ( ! empty( $stored_info ) ) {
$networks_info = array_merge( $stored_info, $networks_info );
}
// Set the counters values for current post.
update_post_meta( $post_id, self::COUNTER_META_KEY, $networks_info );
// Set the counter update time.
update_post_meta( $post_id, self::TIMESTAMP_META_KEY, time() );
} else {
// Store the values of all new networks.
$stored_info = Hustle_Settings_Admin::get_hustle_settings( self::SETTINGS_KEY );
if ( ! empty( $stored_info[ self::COUNTER_META_KEY ] ) ) {
$networks_info = array_merge( $stored_info[ self::COUNTER_META_KEY ], $networks_info );
}
// Set global option for pages that are not a post.
$networks_data = array(
self::COUNTER_META_KEY => $networks_info,
self::TIMESTAMP_META_KEY => time(),
);
Hustle_Settings_Admin::update_hustle_settings( $networks_data, self::SETTINGS_KEY );
}
return $networks_info;
}
/**
* Get the data from each network's API
*
* @since 3.0.3
*
* @param string $current_link Current Link.
* @param array $social_networks Social networks.
* @param array $options Options.
* @return array
*/
private function get_networks_data_from_api( $current_link, $social_networks = array(), $options = array() ) {
$result = array();
$networks_endpoint = self::get_networks_counter_endpoint( false, $current_link );
foreach ( $social_networks as $network ) {
if ( isset( $networks_endpoint[ $network ] ) ) {
$url = $networks_endpoint[ $network ];
} else {
continue;
}
$response = wp_remote_get( $url );
$response_body = wp_remote_retrieve_body( $response );
if ( $response_body ) {
$result[ $network ] = $response_body;
}
}
return $result;
}
/**
* Get the API URL for each network, or the name of the networks with a counter endpoint.
*
* @since 4.0
*
* @param string $networks_only Networks only.
* @param string $current_link Current link.
* @return array
*/
public static function get_networks_counter_endpoint( $networks_only = true, $current_link = '' ) {
if ( $networks_only ) {
return apply_filters(
'hustle_networks_with_counter_endpoint',
array( 'twitter', 'reddit', 'vkontakte' )
);
}
$current_link = rawurlencode( $current_link );
return apply_filters(
'hustle_native_share_counter_enpoints',
array(
// FB stopped working without an app token.
// 'facebook' => 'https://graph.facebook.com/?fields=og_object{engagement{count}}&id=' . $current_link,
// There's no official twitter api for doing this. This alternative requires signing in.
'twitter' => 'https://counts.twitcount.com/counts.php?url=' . $current_link,
'reddit' => 'https://www.reddit.com/api/info.json?url=' . $current_link,
'vkontakte' => 'https://vk.com/share.php?act=count&url=' . untrailingslashit( $current_link ),
)
);
}
/**
* Format the raw response from the networks so it can be displayed in front.
*
* @since 4.0
*
* @param array $networks_data Networks data.
* @param boolean $shorten_count If true, 1000 shares would be formatted to 1K.
* @return array
*/
private function format_networks_api_raw_data( $networks_data, $shorten_count = true ) {
$formatted = array();
foreach ( $networks_data as $network => $response ) {
// Get "count" from each network's response and add the "count" number to $formatted array.
$get_formatted_response = 'format_' . $network . '_api_response';
if ( ! is_callable( array( $this, $get_formatted_response ) ) ) {
continue;
}
$formatted[ $network ] = $this->{$get_formatted_response}( $networks_data[ $network ] );
if ( $shorten_count ) {
$formatted[ $network ] = $this->shorten_count( $formatted[ $network ] );
}
}
return $formatted;
}
/**
* Format a given number to display it nicely. 10K instead of 10093
*
* @since 3.0.3
*
* @param integer $count Count.
* @return string
*/
private function shorten_count( $count ) {
$count = intval( $count );
if ( $count < 1000 ) {
return $count;
} elseif ( $count < 1000000 ) {
return round( $count / 1000, 1, PHP_ROUND_HALF_DOWN ) . __( ' K', 'hustle' );
} else {
return round( $count / 1000000, 1, PHP_ROUND_HALF_DOWN ) . __( ' M', 'hustle' );
}
}
/**
* Set option to trigger the refresh of the counters
*
* @since 3.0.3
*/
public static function refresh_all_counters() {
update_option( self::REFRESH_OPTION_KEY, time() );
}
/**
* Format the response of each API to get the counter
*
* @since 3.0.3
*
* @param string $response Response.
* @return integer
*/
private function format_facebook_api_response( $response ) {
$response = json_decode( $response, true );
$engagement = ! empty( $response['og_object'] ) && ! empty( $response['og_object']['engagement']['count'] ) ? intval( $response['og_object']['engagement']['count'] ) : 0;
return $engagement;
}
/**
* Format twitter api response
*
* @param string $response Response.
* @return int
*/
private function format_twitter_api_response( $response ) {
$response = json_decode( $response, true );
return isset( $response['count'] ) ? intval( $response['count'] ) : 0;
}
/**
* Format pinterest api response
*
* @param string $response Response.
* @return int
*/
private function format_pinterest_api_response( $response ) {
preg_match( '/^receiveCount\((.*)\)$/', $response, $match );
if ( ! isset( $match[1] ) ) {
return 0;
}
$response = json_decode( $match[1], true );
return isset( $response['count'] ) ? intval( $response['count'] ) : 0;
}
/**
* Format reddit api response
*
* @param string $response Response.
* @return int
*/
private function format_reddit_api_response( $response ) {
$response = json_decode( $response, true );
if ( ! isset( $response['data']['children'] ) ) {
return 0;
}
$data = $response['data']['children'];
$counter = 0;
foreach ( $data as $sub ) {
if ( ! isset( $sub['data']['subreddit_subscribers'] ) ) {
continue;
}
$counter = $counter + intval( $sub['data']['subreddit_subscribers'] );
}
return $counter;
}
/**
* Format vkontakte api response
*
* @param string $response Response.
* @return int
*/
private function format_vkontakte_api_response( $response ) {
preg_match( '/^VK\.Share\.count\(.{1,3}(.*)\)/', $response, $match );
if ( ! isset( $match[1] ) ) {
return 0;
}
return intval( $match[1] );
}
/**
* Get the network's sharing endpoints.
*
* @since 4.0
*
* @param bool $networks_only Networks only.
* @return array
*/
public static function get_sharing_endpoints( $networks_only = true ) {
if ( $networks_only ) {
return apply_filters(
'hustle_networks_with_share_enpoints',
array( 'facebook', 'twitter', 'pinterest', 'reddit', 'linkedin', 'vkontakte', 'whatsapp', 'email' )
);
}
global $wp;
$current_url = rawurlencode( home_url( $wp->request ) );
// let users filter the title.
$title = apply_filters( 'hustle_social_share_platform_title', rawurlencode( html_entity_decode( esc_html( get_the_title() ) ) ) );
return apply_filters(
'hustle_native_share_enpoints',
array(
'facebook' => 'https://www.facebook.com/sharer/sharer.php?u=' . $current_url,
'twitter' => 'https://twitter.com/intent/tweet?url=' . $current_url . '&text=' . $title,
'pinterest' => 'https://www.pinterest.com/pin/create/button/?url=' . $current_url,
'reddit' => 'https://www.reddit.com/submit?url=' . $current_url,
'linkedin' => 'https://www.linkedin.com/shareArticle?mini=true&url=' . $current_url,
'vkontakte' => 'https://vk.com/share.php?url=' . $current_url,
'whatsapp' => 'https://api.whatsapp.com/send?text=' . $current_url,
'email' => 'mailto:?subject=' . $title . '&body=' . $current_url,
),
$current_url
);
}
/**
* Get renderer
*
* @return \Hustle_Renderer_Sshare
*/
public function get_renderer() {
return new Hustle_Renderer_Sshare();
}
}