Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
File Manager
/
06
/
wp-content
/
plugins
/
wordpress-popup
/
inc
:
hustle-sshare-model.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?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(); } }