*/ if (!class_exists('MasterAddons\Inc\Classes\Pro_Upgrade')) { class Pro_Upgrade { use Date; public $slug; protected $data = array(); /** * Construct method */ public function __construct() { $this->slug = Helper::jltma_slug_cleanup(); $this->maybe_sync_remote_data(); $this->register_sync_hook(); $this->set_data(); add_action('admin_footer', array($this, 'display_popup')); // Add popup to Elementor editor (runs in iframe, needs separate hook) add_action('elementor/editor/footer', array($this, 'display_popup')); add_action('wp_dashboard_setup', array($this, 'dashboard_widget'), 999); } /** * Register Dashboard widget * * @return void * @author Jewel Theme */ public function dashboard_widget() { wp_add_dashboard_widget( 'jltma_dashboard_widget', esc_html__('Master Addons - News & Updates', 'master-addons'), array($this, 'dashboard_widget_render') ); // Force widget to very first position on the dashboard global $wp_meta_boxes; // Get the regular dashboard widgets array // (which already has our new widget but appended at the end). $default_dashboard = $wp_meta_boxes['dashboard']['normal']['core']; // Backup and delete our new dashboard widget from the end of the array. $default_widget_backup = array( 'jltma_dashboard_widget' => $default_dashboard['jltma_dashboard_widget'] ); unset( $default_dashboard['jltma_dashboard_widget'] ); // Merge the two arrays together so our widget is at the beginning. $sorted_dashboard = array_merge( $default_widget_backup, $default_dashboard ); // Save the sorted array back into the original metaboxes . $wp_meta_boxes['dashboard']['normal']['core'] = $sorted_dashboard; } /** * Render dashboard widget * * @author Jewel Theme */ public function dashboard_widget_render() { // Fetch feed with descriptions (master-addons.com/feed has content, jeweltheme doesn't) $feed_items = $this->get_dashboard_feed_items(5); echo '
'; if (wp_validate_boolean($this->get_content('is_campaign'))) { ?> '; } /** * Fetch RSS feed items for dashboard widget * Handles master-addons.com/feed/ which has a script tag before XML declaration * * @param int $count Number of items to fetch * @return array Array of items with title, link, excerpt keys */ public function get_dashboard_feed_items($count = 5) { $transient_key = 'jltma_dashboard_feed_items'; $cached = get_transient($transient_key); if (false !== $cached) { return array_slice($cached, 0, $count); } $items = array(); // Try master-addons.com/feed/ first (has descriptions) $response = wp_remote_get('https://master-addons.com/feed/', array('timeout' => 10)); if (!is_wp_error($response)) { $body = wp_remote_retrieve_body($response); // Strip script tag injected before XML declaration $body = preg_replace('/^.*?(<\?xml)/s', '$1', $body); // Parse with SimpleXML libxml_use_internal_errors(true); $xml = simplexml_load_string($body); libxml_clear_errors(); if ($xml && isset($xml->channel->item)) { foreach ($xml->channel->item as $xml_item) { $title = (string) $xml_item->title; $link = (string) $xml_item->link; $desc = (string) $xml_item->description; $desc = wp_strip_all_tags($desc); $excerpt = mb_strlen($desc) > 200 ? mb_substr($desc, 0, 200) . '…' : $desc; $items[] = array( 'title' => $title, 'link' => $link, 'excerpt' => $excerpt, ); } } } // Fallback to jeweltheme feed (no descriptions) if (empty($items)) { include_once ABSPATH . WPINC . '/feed.php'; $rss = fetch_feed('https://master-addons.com/feed/'); if (!is_wp_error($rss)) { $rss_items = $rss->get_items(0, 10); foreach ($rss_items as $rss_item) { $items[] = array( 'title' => $rss_item->get_title(), 'link' => $rss_item->get_permalink(), 'excerpt' => '', ); } } } if (!empty($items)) { set_transient($transient_key, $items, 6 * HOUR_IN_SECONDS); } return array_slice($items, 0, $count); } /** * Set merged data * * @return void * @author Jewel Theme */ public function set_data() { $this->data = Helper::get_merged_data(self::get_data()); } /** * Get Sheet data * * @author Jewel Theme */ public static function get_data() { return get_option('jltma_sheet_promo_data'); } /** * Get Contents * * @param [type] $key . * * @author Jewel Theme */ public function get_content($key) { if( empty( $this->data ) ) return; return $this->data[$key] ?? null; } /** * Get Option has data * * @author Jewel Theme */ public function get_data_hash() { return get_option('jltma_sheet_promo_data_hash'); } /** * Sync to remote data * * @author Jewel Theme */ public function maybe_sync_remote_data() { $data = self::get_data(); if (empty($data)) { $this->sheet_data_remote_sync(); } } /** * Register Sync hook * * @return void * @author Jewel Theme */ public function register_sync_hook() { $hook_action = 'jltma_sheet_promo_data_remote_sync'; add_action($hook_action, array($this, 'sheet_data_remote_sync')); if (!wp_next_scheduled($hook_action)) { wp_schedule_event(time(), 'daily', $hook_action); } register_deactivation_hook(JLTMA_FILE, array($this, 'clear_register_sync_hook')); } /** * Clear register sync hook * * @return void * @author Jewel Theme */ public function clear_register_sync_hook() { wp_clear_scheduled_hook('jltma_sheet_promo_data_remote_sync'); } /** * Data sync with remote * * @return void * @author Jewel Theme */ public function sheet_data_remote_sync() { $data = self::get_data(); $force = empty( $data ); $remote_data = $this->get_update_remote_data(); // Don't clobber stored data when the API is unreachable / returned nothing. if ( false === $remote_data ) { return; } $stored_hash = $this->get_data_hash(); $remote_hash = base64_encode( json_encode( $remote_data ) ); if ( $force || $stored_hash !== $remote_hash ) { update_option('jltma_sheet_promo_data', $remote_data); update_option('jltma_sheet_promo_data_hash', $remote_hash); do_action('jltma_sheet_promo_data_reset'); } } /** * Update endpoint URL for this plugin * * @author Jewel Theme */ public function get_update_url() { return esc_url( Helper::api_endpoint() . 'api/plugin/update/' . Helper::jltma_slug_cleanup() ); } /** * Promotional remote data (REST API) * * Expects a single JSON object describing the update, e.g. * { product_name, product_slug, image_url, start_date, end_date, * button_text, button_url, btn_color, notice, show_for_premium, is_live } * * @return array|false * @author Jewel Theme */ public function get_update_remote_data() { $response = wp_remote_get( $this->get_update_url(), array( 'timeout' => 15, 'headers' => array( 'Accept' => 'application/json' ), ) ); if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) { return false; } $body = wp_remote_retrieve_body( $response ); if ( ! $body ) { return false; } $data = json_decode( $body, true ); if ( ! is_array( $data ) || JSON_ERROR_NONE !== json_last_error() ) { return false; } // API may return the update directly, or wrapped (e.g. { data: {...} }) / as a list. if ( isset( $data['data'] ) && is_array( $data['data'] ) ) { $data = $data['data']; } if ( isset( $data[0] ) && is_array( $data[0] ) ) { $data = wp_list_filter( $data, array( 'product_slug' => Helper::jltma_slug_cleanup() ) ); $data = $data ? array_values( $data )[0] : array(); } if ( empty( $data ) || empty( $data['product_slug'] ) ) { return false; } // Only accept a update that targets this plugin. if ( Helper::jltma_slug_cleanup() !== $data['product_slug'] ) { return false; } // API sends dates as "d/m/Y h:i a" — normalise so PHP/JS date parsers don't choke. foreach ( array( 'start_date', 'end_date' ) as $key ) { if ( ! empty( $data[ $key ] ) ) { $data[ $key ] = $this->normalize_date( $data[ $key ] ); } } return $data; } /** * Display popup contents * * @author Jewel Theme */ public function display_popup() { if (Helper::jltma_premium()) { if (!$this->get_content('show_for_premium')) { return; } } if (empty( $this->get_content('is_live') )) { $image_url = JLTMA_IMAGE_DIR . 'ma-fallback.png'; $notice = 'Use "SPECIAL40" to Get Flat 40% OFF'; $btn_url = 'https://master-addons.com/pricing/'; $btn_text = 'GET THE DEAL'; } else { $image_url = $this->get_content('image_url'); $notice = $this->get_content('notice'); $btn_url = $this->get_content('button_url'); $btn_text = $this->get_content('button_text'); } ?> */ public function counter_date() { $endDate = $this->get_content('end_date'); $is_active = $this->date_is_current_or_next($endDate); if ($is_active) { return $endDate; } return $this->date_increment($this->current_time(), 3); } /** * Normalise a update date string to 'Y-m-d H:i:s' * * Accepts d/m/Y (optionally with 12h time) and a few common fallbacks. * Returns the original value untouched if it cannot be parsed. * * @param string $value . * @author Jewel Theme */ public function normalize_date( $value ) { $value = trim( (string) $value ); $formats = array( 'd/m/Y h:i a', 'd/m/Y H:i', 'd/m/Y', 'd-m-Y H:i:s', 'd-m-Y' ); foreach ( $formats as $format ) { $dt = \DateTime::createFromFormat( $format, $value ); if ( $dt instanceof \DateTime ) { return $dt->format( 'Y-m-d H:i:s' ); } } $ts = strtotime( $value ); return $ts ? gmdate( 'Y-m-d H:i:s', $ts ) : $value; } } // End class Pro_Upgrade } // End class_exists check