/** * Plugin Name: Elementor * Description: The Elementor Website Builder has it all: drag and drop page builder, pixel perfect design, mobile responsive editing, and more. Get started now! * Plugin URI: https://elementor.com/?utm_source=wp-plugins&utm_campaign=plugin-uri&utm_medium=wp-dash * Author: Elementor.com * Version: 3.20.1 * Author URI: https://elementor.com/?utm_source=wp-plugins&utm_campaign=author-uri&utm_medium=wp-dash * * Text Domain: elementor * * @package Elementor * @category Core * * Elementor is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * Elementor is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } define( 'ELEMENTOR_VERSION', '3.20.1' ); define( 'ELEMENTOR__FILE__', __FILE__ ); define( 'ELEMENTOR_PLUGIN_BASE', plugin_basename( ELEMENTOR__FILE__ ) ); define( 'ELEMENTOR_PATH', plugin_dir_path( ELEMENTOR__FILE__ ) ); if ( defined( 'ELEMENTOR_TESTS' ) && ELEMENTOR_TESTS ) { define( 'ELEMENTOR_URL', 'file://' . ELEMENTOR_PATH ); } else { define( 'ELEMENTOR_URL', plugins_url( '/', ELEMENTOR__FILE__ ) ); } define( 'ELEMENTOR_MODULES_PATH', plugin_dir_path( ELEMENTOR__FILE__ ) . '/modules' ); define( 'ELEMENTOR_ASSETS_PATH', ELEMENTOR_PATH . 'assets/' ); define( 'ELEMENTOR_ASSETS_URL', ELEMENTOR_URL . 'assets/' ); add_action( 'plugins_loaded', 'elementor_load_plugin_textdomain' ); if ( ! version_compare( PHP_VERSION, '7.4', '>=' ) ) { add_action( 'admin_notices', 'elementor_fail_php_version' ); } elseif ( ! version_compare( get_bloginfo( 'version' ), '6.0', '>=' ) ) { add_action( 'admin_notices', 'elementor_fail_wp_version' ); } else { require ELEMENTOR_PATH . 'includes/plugin.php'; } /** * Load Elementor textdomain. * * Load gettext translate for Elementor text domain. * * @since 1.0.0 * * @return void */ function elementor_load_plugin_textdomain() { load_plugin_textdomain( 'elementor' ); } /** * Elementor admin notice for minimum PHP version. * * Warning when the site doesn't have the minimum required PHP version. * * @since 1.0.0 * * @return void */ function elementor_fail_php_version() { $message = sprintf( /* translators: 1: `

` opening tag, 2: `

` closing tag, 3: PHP version. 4: Link opening tag, 5: Link closing tag. */ esc_html__( '%1$sElementor isn’t running because PHP is outdated.%2$s Update to PHP version %3$s and get back to creating! %4$sShow me how%5$s', 'elementor' ), '

', '

', '7.4', '', '' ); $html_message = sprintf( '
%s
', wpautop( $message ) ); echo wp_kses_post( $html_message ); } /** * Elementor admin notice for minimum WordPress version. * * Warning when the site doesn't have the minimum required WordPress version. * * @since 1.5.0 * * @return void */ function elementor_fail_wp_version() { $message = sprintf( /* translators: 1: `

` opening tag, 2: `

` closing tag, 3: WP version. 4: Link opening tag, 5: Link closing tag. */ esc_html__( '%1$sElementor isn’t running because WordPress is outdated.%2$s Update to version %3$s and get back to creating! %4$sShow me how%5$s', 'elementor' ), '

', '

', '6.0', '', '' ); $html_message = sprintf( '
%s
', wpautop( $message ) ); echo wp_kses_post( $html_message ); }/** * Exit if accessed directly. */ if ( ! defined( 'ABSPATH' ) ) { exit; } #[AllowDynamicProperties] class Wpzoom_Instagram_Widget_API { /** * @var Wpzoom_Instagram_Widget_API The reference to *Singleton* instance of this class */ private static $instance; /** * Request headers. * * @var array */ public $headers = array(); /** * Errors collector. * * @var array|WP_Error */ public $errors = array(); /** * Instagram Settings * * @var array */ public $settings; /** * Instagram Access Token * * @var string */ protected $access_token; /** * Feed ID * * @var string */ protected $feed_id; /** * Class constructor */ protected function __construct() { $this->is_forced_timeout = (bool) WPZOOM_Instagram_Widget_Settings::get_feed_setting_value( get_the_ID(), 'enable-request-timeout' ); $this->request_timeout_value = 15; if ( $this->is_forced_timeout && ! empty( $this->request_timeout_value ) ) { $this->headers['timeout'] = $this->request_timeout_value; } $this->image_uploader = WPZOOM_Instagram_Image_Uploader::getInstance(); $this->errors = new WP_Error(); } public function init() { add_action( 'init', array( $this, 'set_schedule' ) ); add_action( 'wpzoom_instagram_widget_cron_hook', array( $this, 'execute_cron' ) ); add_filter( 'cron_schedules', array( $this, 'add_cron_interval' ) ); } /** * Returns the *Singleton* instance of this class. * * @return Wpzoom_Instagram_Widget_API The *Singleton* instance. */ public static function getInstance() { if ( null === self::$instance ) { self::$instance = new self(); self::$instance->init(); } return self::$instance; } /** * Manually set the access token. * * @since 2.0.0 * * @param string $token The access token to set. * @return void */ public function set_access_token( $token ) { $this->access_token = $token; } /** * Manually set the access token. * * @since 2.0.0 * * @param string $token The access token to set. * @return void */ public function set_feed_id( $id ) { $this->feed_id = $id; } /** * Fetches a remote URL either safely or not, depending on a setting. * * @since 2.0.6 * * @param string $url URL to retrieve. * @param array $args Optional. Request arguments. Default empty array. * @return array|WP_Error The response or WP_Error on failure. */ public static function remote_get( $url, $args = array() ) { $settings = get_option( 'wpzoom-instagram-general-settings' ); $enable_unsafe_requests = ! empty( $settings['enable-unsafe-requests'] ) ? wp_validate_boolean( $settings['enable-unsafe-requests'] ) : false; return $enable_unsafe_requests ? wp_remote_get( $url, $args ) : wp_safe_remote_get( $url, $args ); } /** * Register custom cron intervals * * @since 1.8.0 * * @param array $schedules Registered schedules array. * @return array */ public function add_cron_interval( $schedules ) { $schedules['before_access_token_expires'] = array( 'interval' => 5097600, // 59 days. 'display' => esc_attr__( 'Before Access Token Expires', 'instagram-widget-by-wpzoom' ), ); return $schedules; } /** * Register schedule event * * @return void */ public function set_schedule() { if ( ! wp_next_scheduled( 'wpzoom_instagram_widget_cron_hook' ) ) { wp_schedule_event( time(), 'before_access_token_expires', 'wpzoom_instagram_widget_cron_hook' ); } } /** * Execute cron event * * @return boolean */ public function execute_cron() { $all_users = get_posts( array( 'numberposts' => -1, 'post_type' => 'wpz-insta_user', ) ); if ( ! empty( $all_users ) && is_array( $all_users ) ) { foreach ( $all_users as $user ) { if ( $user instanceof WP_Post ) { $user_name = get_the_title( $user ); $user_display = sprintf( '@%s', $user_name ); $token = get_post_meta( $user->ID, '_wpz-insta_token', true ); if ( false !== $token && ! empty( $token ) ) { $request_url = add_query_arg( array( 'grant_type' => 'ig_refresh_token', 'access_token' => $token, ), 'https://graph.instagram.com/refresh_access_token' ); $response = self::remote_get( $request_url, $this->headers ); $response_code = wp_remote_retrieve_response_code( $response ); if ( ! is_wp_error( $response ) ) { $body = wp_remote_retrieve_body( $response ); $data = json_decode( $body ); } if ( 200 === $response_code ) { $date_format = get_option( 'date_format' ); $time_format = get_option( 'time_format' ); $notice_status = 'success'; $notice_message = sprintf( __( 'WPZOOM Instagram Widget: The Instagram Access Token was refreshed automatically on %1$s at %2$s for the account %3$s.', 'instagram-widget-by-wpzoom' ), date( $date_format ), date( $time_format ), esc_html( $user_display ) ); update_post_meta( $user->ID, '_wpz-insta_token', $data->access_token ); update_post_meta( $user->ID, '_wpz-insta_token_expire', strtotime( '+60 days' ) ); } else { if ( ! isset( $data->error ) ) { error_log( __( 'Something wrong! Doesn\'t isset $data->error.', 'instagram-widget-by-wpzoom' ) ); return false; } else { error_log( $data->error->error_user_msg ); } $notice_status = 'error'; $notice_message = ''; $settings_url = admin_url( 'edit.php?post_type=wpz-insta_user' ); if ( 190 === $data->error->code ) { // Error validating access token: Session has expired. $notice_message = wp_kses_post( __( 'WPZOOM Instagram Widget: ', 'instagram-widget-by-wpzoom' ) ) . $data->error->message; } elseif ( 10 === $data->error->code && ! self::is_access_token_valid( $token ) ) { // Application does not have permission for this action. // User need to generate new Access Token manually. $notice_message = sprintf( __( 'WPZOOM Instagram Widget: The Access Token for the account %1$s has expired!
', 'instagram-widget-by-wpzoom' ), $user_display ); $notice_message .= sprintf( __( 'We cannot update access tokens automatically for Instagram private accounts. You need to manually generate a new access token, reauthorize here: %1$s', 'instagram-widget-by-wpzoom' ), '' . __( 'Instagram Widget Settings', 'instagram-widget-by-wpzoom' ) . '' ); } } update_option( '_wpz-insta_cron-result', array( $user->ID => array( 'status' => $notice_status, 'message' => $notice_message ) ) + (array) get_option( '_wpz-insta_cron-result', array() ) ); } } } } } public static function reset_cache( $sanitized_data ) { delete_transient( 'zoom_instagram_is_configured' ); delete_transient( 'zoom_instagram_user_info' ); // Remove schedule hook `wpzoom_instagram_widget_cron_hook`. if ( empty( $sanitized_data['basic-access-token'] ) ) { wp_clear_scheduled_hook( 'wpzoom_instagram_widget_cron_hook' ); } } /** * @param $screen_name string Instagram username * @param $image_limit int Number of images to retrieve * @param $image_width int Desired image width to retrieve * * @return array|bool Array of tweets or false if method fails */ public function get_items( $instance ) { $sliced = wp_array_slice_assoc( $instance, array( 'image-limit', 'image-width', 'image-resolution', 'username', 'disable-video-thumbs', 'include-pagination', 'bypass-transient', ) ); $image_limit = $sliced['image-limit']; $image_width = $sliced['image-width']; $image_resolution = ! empty( $sliced['image-resolution'] ) ? $sliced['image-resolution'] : 'low_resolution'; $injected_username = ! empty( $sliced['username'] ) ? $sliced['username'] : ''; $disable_video_thumbs = ! empty( $sliced['disable-video-thumbs'] ); $include_pagination = ! empty( $sliced['include-pagination'] ); $bypass_transient = ! empty( $sliced['bypass-transient'] ); if( isset( $instance['widget-id'] ) ) { $transient = 'zoom_instagram_is_configured_' . $instance['widget-id']; } else { $transient = 'zoom_instagram_is_configured'; } if ( ! empty( $this->access_token ) ) { $transient = $transient . '_' . substr( $this->access_token, 0, 20 ); } $injected_username = trim( $injected_username ); if ( ! $bypass_transient ) { $data = json_decode( get_transient( $transient ) ); if ( false !== $data && is_object( $data ) && ! empty( $data->data ) ) { return self::processing_response_data( $data, $image_width, $image_resolution, $image_limit, $disable_video_thumbs, $include_pagination ); } } if ( ! empty( $this->access_token ) ) { $request_url = add_query_arg( array( 'fields' => 'media_url,media_type,caption,username,permalink,thumbnail_url,timestamp,children{media_url,media_type,thumbnail_url}', 'access_token' => $this->access_token, 'limit' => $image_limit, ), 'https://graph.instagram.com/me/media' ); $response = self::remote_get( $request_url, $this->headers ); if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { if ( ! $bypass_transient ) { set_transient( $transient, wp_json_encode( false ), MINUTE_IN_SECONDS ); } $error_data = $this->get_error( 'items-with-token-invalid-response' ); $this->errors->add( $error_data['code'], $error_data['message'] ); return false; } $raw_data = json_decode( wp_remote_retrieve_body( $response ) ); $data = self::convert_items_to_old_structure( $raw_data, $bypass_transient ); if ( $include_pagination && property_exists( $raw_data, 'paging' ) ) { $data->paging = $raw_data->paging; } } if ( ! empty( $data->data ) ) { if ( ! $bypass_transient ) { set_transient( $transient, wp_json_encode( $data ), $this->get_transient_lifetime( $this->feed_id ) ); } } else { if ( ! $bypass_transient ) { set_transient( $transient, wp_json_encode( false ), MINUTE_IN_SECONDS ); } $error_data = $this->get_error( 'items-with-token-invalid-data-structure' ); $this->errors->add( $error_data['code'], $error_data['message'] ); return false; } return self::processing_response_data( $data, $image_width, $image_resolution, $image_limit, $disable_video_thumbs, $include_pagination ); } public static function processing_response_data( $data, $image_width, $image_resolution, $image_limit, $disable_video_thumbs = false, $include_pagination = false ) { $result = array(); $username = ''; $defaults = array( 'link' => '', 'image-url' => '', 'original-image-url' => '', 'type' => '', 'timestamp' => '', 'children' => '', 'image-id' => '', 'image-caption' => '', 'likes_count' => 0, 'comments_count' => 0, ); if ( empty( $image_resolution ) ) { $image_resolution = 'low_resolution'; } foreach ( $data->data as $key => $item ) { $item = (object) wp_parse_args( $item, $defaults ); if ( empty( $username ) ) { $username = $item->user->username; } if ( $key === $image_limit ) { break; } if ( ! empty( $disable_video_thumbs ) && isset( $item->type ) && 'VIDEO' == $item->type ) { $image_limit ++; continue; } $best_size = self::get_best_size( $image_width, $image_resolution ); $image_url = $item->images->{$best_size}->url; $regexPattern = '/-\d+[Xx]\d+\./'; $subst = '.'; $local_image_url = preg_replace( $regexPattern, $subst, $image_url, 1 ); $result[] = array( 'link' => $item->link, 'image-url' => $image_url, 'local-image-url' => $local_image_url, 'original-image-url' => property_exists( $item, 'media_url' ) && ! empty( $item->media_url ) ? $item->media_url : '', 'type' => $item->type, 'timestamp' => property_exists( $item, 'timestamp' ) && ! empty( $item->timestamp ) ? $item->timestamp : '', 'children' => property_exists( $item, 'children' ) && ! empty( $item->children ) ? $item->children : '', 'image-id' => ! empty( $item->id ) ? esc_attr( $item->id ) : '', 'image-caption' => ! empty( $item->caption->text ) ? esc_attr( $item->caption->text ) : '', 'likes_count' => ! empty( $item->likes->count ) ? esc_attr( $item->likes->count ) : 0, 'comments_count' => ! empty( $item->comments->count ) ? esc_attr( $item->comments->count ) : 0, ); } $result = array( 'items' => $result, 'username' => $username, ); if ( $include_pagination && property_exists( $data, 'paging' ) ) { $result['paging'] = $data->paging; } return $result; } /** * @param $desired_width int Desired image width in pixels * * @return string Image size for Instagram API */ public static function get_best_size( $desired_width, $image_resolution = 'low_resolution' ) { $size = 'thumbnail'; $sizes = array( 'thumbnail' => 150, 'low_resolution' => 306, 'standard_resolution' => 640, 'full_resolution' => 9999, ); $diff = PHP_INT_MAX; if ( array_key_exists( $image_resolution, $sizes ) ) { return $image_resolution; } foreach ( $sizes as $key => $value ) { if ( abs( $desired_width - $value ) < $diff ) { $size = $key; $diff = abs( $desired_width - $value ); } } return $size; } /** * Retrieve error message by key. * * @param $key * * @return bool|mixed */ public function get_error( $key ) { $errors = $this->get_errors(); return array_key_exists( $key, $errors ) ? $errors[ $key ] : false; } /** * Get error messages collection. * * @return array */ public function get_errors() { return array( 'user-info-without-token' => array( 'code' => 'user-info-without-token', 'message' => esc_html__( 'Empty json user info from Public Feed.', 'instagram-widget-by-wpzoom' ), ), 'response-data-without-token-from-json-invalid-response' => array( 'code' => 'response-data-without-token-from-json-invalid-response', 'message' => esc_html__( 'The request from the Public Feed failed. Invalid server response from Public JSON API url.', 'instagram-widget-by-wpzoom' ), ), 'response-data-without-token-from-json-invalid-json-format' => array( 'code' => 'response-data-without-token-from-json-invalid-json-format', 'message' => esc_html__( 'The request from the Public Feed failed. Invalid JSON format from Public JSON API url.', 'instagram-widget-by-wpzoom' ), ), 'response-data-without-token-from-html-invalid-response' => array( 'code' => 'response-data-without-token-from-html-invalid-response', 'message' => esc_html__( 'The request from the Public Feed failed. Check username.', 'instagram-widget-by-wpzoom' ), ), 'response-data-without-token-from-html-invalid-json-format' => array( 'code' => 'response-data-without-token-from-html-invalid-json-format', 'message' => esc_html__( 'The request from the Public Feed failed. Invalid JSON format from parsed html body.', 'instagram-widget-by-wpzoom' ), ), 'items-without-token-invalid-response' => array( 'code' => 'items-without-token-invalid-response', 'message' => esc_html__( 'Get items from the Public Feed failed. Invalid response.', 'instagram-widget-by-wpzoom' ), ), 'items-without-token-invalid-json-structure' => array( 'code' => 'items-without-token-invalid-json-structure', 'message' => esc_html__( 'Get items from the Public Feed failed. Malformed data structure.', 'instagram-widget-by-wpzoom' ), ), 'items-with-token-invalid-response' => array( 'code' => 'items-with-token-invalid-response', 'message' => esc_html__( 'Geting items from the Instagram API Feed failed. Invalid response.', 'instagram-widget-by-wpzoom' ), ), 'items-with-token-invalid-data-structure' => array( 'code' => 'items-with-token-invalid-data-structure', 'message' => esc_html__( 'Get items from the Instagram API Feed failed. Malformed data structure.', 'instagram-widget-by-wpzoom' ), ), 'user-with-token-invalid-response' => array( 'code' => 'user-with-token-invalid-response', 'message' => esc_html__( 'Get user data from the Instagram API Feed failed. Invalid response.', 'instagram-widget-by-wpzoom' ), ), 'user-with-token-invalid-data-structure' => array( 'code' => 'user-with-token-invalid-data-structure', 'message' => esc_html__( 'Get user data from the Instagram API Feed failed. Malformed data structure.', 'instagram-widget-by-wpzoom' ), ), ); } public static function convert_items_to_old_structure( $data, $preview = false ) { $converted = new stdClass(); $converted->data = array(); $image_uploader = WPZOOM_Instagram_Image_Uploader::getInstance(); foreach ( $data->data as $key => $item ) { $is_video = property_exists( $item, 'media_type' ) && 'VIDEO' === $item->media_type; $media_url = $is_video && property_exists( $item, 'thumbnail_url' ) && ! empty( $item->thumbnail_url ) ? $item->thumbnail_url : $item->media_url; $converted->data[] = (object) array( 'id' => $item->id, 'media_url' => ( $is_video ? $item->media_url : $media_url ), 'user' => (object) array( 'id' => null, 'fullname' => null, 'profile_picture' => null, 'username' => $item->username, ), 'images' => (object) array( 'thumbnail' => (object) array( 'url' => $preview ? $media_url : $image_uploader->get_image( 'thumbnail', $media_url, $item->id ), 'width' => 150, 'height' => 150, ), 'low_resolution' => (object) array( 'url' => $preview ? $media_url : $image_uploader->get_image( 'low_resolution', $media_url, $item->id ), 'width' => 320, 'height' => 320, ), 'standard_resolution' => (object) array( 'url' => $preview ? $media_url : $image_uploader->get_image( 'standard_resolution', $media_url, $item->id ), 'width' => 640, 'height' => 640, ), 'full_resolution' => (object) array( 'url' => $preview ? $media_url : $image_uploader->get_image( 'full_resolution', $media_url, $item->id ), 'width' => 9999, 'height' => 9999, ), ), 'type' => $item->media_type, 'likes' => null, 'comments' => null, 'created_time' => null, 'timestamp' => $item->timestamp, 'children' => ( isset( $item->children ) ? $item->children : null ), 'link' => $item->permalink, 'caption' => (object) array( 'text' => isset( $item->caption ) ? $item->caption : '', ), ); } return $converted; } function get_transient_lifetime( $id ) { $feed_id = isset( $id ) ? $id : 0; $interval = (int) WPZOOM_Instagram_Widget_Settings::get_feed_setting_value( $feed_id, 'check-new-posts-interval-number' ); $interval_suffix = (int) WPZOOM_Instagram_Widget_Settings::get_feed_setting_value( $feed_id, 'check-new-posts-interval-suffix' ); $values = array( MINUTE_IN_SECONDS, HOUR_IN_SECONDS, DAY_IN_SECONDS, WEEK_IN_SECONDS, MONTH_IN_SECONDS, ); $keys = array_keys( $values ); $type = in_array( $interval_suffix, $keys ) ? $values[ $interval_suffix ] : $values[2]; return intval( $type * $interval ) ; } public function get_user_info( $injected_username = '' ) { $transient = 'zoom_instagram_user_info'; $injected_username = rtrim( $injected_username ); if ( false !== ( $data = json_decode( get_transient( $transient ) ) ) && is_object( $data ) && ! empty( $data->data ) ) { return $data; } if ( ! empty( $this->access_token ) ) { $request_url = add_query_arg( array( 'access_token' => $this->access_token, 'fields' => 'account_type,id,media_count,username', ), 'https://graph.instagram.com/me' ); $response = self::remote_get( $request_url, $this->headers ); if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) { set_transient( $transient, wp_json_encode( false ), MINUTE_IN_SECONDS ); $error_data = $this->get_error( 'user-with-token-invalid-response' ); $this->errors->add( $error_data['code'], $error_data['message'] ); return false; } $data = json_decode( wp_remote_retrieve_body( $response ) ); $data = $this->convert_user_info_to_old_structure( $data ); } if ( ! empty( $data->data ) ) { set_transient( $transient, wp_json_encode( $data ), $this->get_transient_lifetime( $this->feed_id ) ); } else { set_transient( $transient, wp_json_encode( false ), MINUTE_IN_SECONDS ); $error_data = $this->get_error( 'user-with-token-invalid-data-structure' ); $this->errors->add( $error_data['code'], $error_data['message'] ); return false; } return $data; } public static function get_basic_user_info_from_token( $access_token ) { $output = false; if ( ! empty( $access_token ) ) { $request_url = add_query_arg( array( 'access_token' => $access_token, 'fields' => 'account_type,username', ), 'https://graph.instagram.com/me' ); $response = self::remote_get( $request_url ); if ( ! is_wp_error( $response ) && 200 == wp_remote_retrieve_response_code( $response ) ) { $output = json_decode( wp_remote_retrieve_body( $response ) ); } } return $output; } function convert_user_info_to_old_structure( $user_info ) { $converted = new stdClass(); $user_info_from_settings = WPZOOM_Instagram_Widget_Settings::get_instance()->get_settings(); $avatar = property_exists( $user_info, 'profile_picture' ) ? $user_info->profile_picture : null; if ( ! empty( $user_info_from_settings['user-info-avatar'] ) ) { $img_src = wp_get_attachment_image_src( $user_info_from_settings['user-info-avatar'] ); if ( ! empty( $img_src ) && is_array( $img_src ) ) { $avatar = $img_src[0]; } } $fullname = ! empty( $user_info->username ) ? $user_info->username : null; if ( ! empty( $user_info_from_settings['user-info-fullname'] ) ) { $fullname = $user_info_from_settings['user-info-fullname']; } $converted->data = (object) array( 'bio' => ! empty( $user_info_from_settings['user-info-biography'] ) ? $user_info_from_settings['user-info-biography'] : null, 'counts' => (object) array( 'followed_by' => null, 'follows' => null, 'media' => null, ), 'full_name' => $fullname, 'id' => ! empty( $user_info->id ) ? $user_info->id : '', 'is_business' => null, 'profile_picture' => $avatar, 'username' => ! empty( $user_info->username ) ? $user_info->username : '', 'website' => null, ); return $converted; } public function is_configured() { $transient = 'zoom_instagram_is_configured'; if ( false !== ( $result = json_decode( get_transient( $transient ) ) ) ) { if ( 'yes' === $result ) { return true; } if ( 'no' === $result ) { return false; } if ( ! empty( $result ) ) { return true; } } $condition = $this->is_access_token_valid( $this->access_token ); if ( true === $condition ) { set_transient( $transient, wp_json_encode( 'yes' ), DAY_IN_SECONDS ); return true; } set_transient( $transient, wp_json_encode( 'no' ), DAY_IN_SECONDS ); return false; } /** * Check if given access token is valid for Instagram Api. */ public static function is_access_token_valid( $access_token ) { if ( empty( $access_token ) ) { return false; } $request_url = add_query_arg( array( 'fields' => 'username', 'access_token' => $access_token, ), 'https://graph.instagram.com/me' ); $response = self::remote_get( $request_url ); if ( is_wp_error( $response ) ) { return $response; } if ( 200 != wp_remote_retrieve_response_code( $response ) ) { return false; } return true; } } Wpzoom_Instagram_Widget_API::getInstance(); /** * * Enqueue CSS/JS of the plugin. * * @since 2.0.2 * @package WPZOOM_Instagram_Widget */ // Exit if accessed directly. if ( ! defined( 'ABSPATH' ) ) { exit; } if ( ! class_exists( 'WPZOOM_Instagram_Widget_Assets ' ) ) { /** * Main WPZOOM_Instagram_Widget_Assets Class. * * @since 2.0.2 */ class WPZOOM_Instagram_Widget_Assets { /** * This plugin's instance. * * @var WPZOOM_Instagram_Widget_Assets * @since 2.0.2 */ private static $instance; /** * Provides singleton instance. * * @since 2.0.2 * @return self instance */ public static function instance() { if ( null === self::$instance ) { self::$instance = new WPZOOM_Instagram_Widget_Assets(); } return self::$instance; } /** * The base directory path. * * @var string $_dir */ private $_dir; /** * The base URL path. * * @var string $_url */ private $_url; /** * The Constructor. */ public function __construct() { add_action( 'enqueue_block_assets', array( $this, 'frontend_register_scripts' ), 5 ); add_action( 'enqueue_block_assets', array( $this, 'widget_styles' ), 5 ); add_action( 'enqueue_block_editor_assets', array( $this, 'register_block_assets' ) ); add_action( 'enqueue_block_editor_assets', array( $this, 'widget_styles' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'widget_styles' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'register_widget_scripts' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_widget_scripts' ) ); /** * Enqueue styles and scripts for SiteOrigin Page Builder. */ add_action( 'siteorigin_panel_enqueue_admin_scripts', array( $this, 'widget_styles' ) ); add_action( 'siteorigin_panel_enqueue_admin_scripts', array( $this, 'register_widget_scripts' ) ); add_action( 'siteorigin_panel_enqueue_admin_scripts', array( $this, 'enqueue_widget_scripts' ) ); } public function frontend_register_scripts() { global $post; $general_options = get_option( 'wpzoom-instagram-general-settings' ); $should_enqueue = has_block( 'wpzoom/instagram-block' ); $has_reusable_block = self::has_reusable_block( 'wpzoom/instagram-block' ); $is_active_widget = is_active_widget( false, false, 'wpzoom_instagram_widget', false ); $has_shortcode = ( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'instagram' ) ); $has_widget_block = self::is_active_block_widget( 'wpzoom/instagram-block' ); $load_css_js = isset( $general_options['load-css-js'] ) ? true : false; $script_asset_file = include( plugin_dir_path( __FILE__ ) . 'dist/scripts/backend/block.asset.php' ); $style_asset_file = include( plugin_dir_path( __FILE__ ) . 'dist/styles/frontend/index.asset.php' ); if( is_admin() || $load_css_js || $should_enqueue || $has_reusable_block || $is_active_widget || $has_shortcode || $has_widget_block || isset( $_GET['wpz-insta-widget-preview'] ) ) { wp_register_script( 'magnific-popup', plugins_url( 'dist/scripts/library/magnific-popup.js', __FILE__ ), array( 'jquery', 'underscore', 'wp-util' ), filemtime( plugin_dir_path( __FILE__ ) . 'dist/scripts/library/magnific-popup.js' ), true ); wp_register_script( 'swiper-js', plugins_url( 'dist/scripts/library/swiper.js', __FILE__ ), array(), '7.4.1' ); wp_register_script( 'wpz-insta_block-frontend-script', plugins_url( 'dist/scripts/frontend/block.js', __FILE__ ), array( 'jquery', 'underscore', 'magnific-popup', 'swiper-js' ), $script_asset_file['version'] ); wp_register_style( 'magnific-popup', plugins_url( 'dist/styles/library/magnific-popup.css', __FILE__ ), array( 'dashicons' ), WPZOOM_INSTAGRAM_VERSION ); wp_register_style( 'wpz-insta_block-frontend-style', plugins_url( 'dist/styles/frontend/index.css', __FILE__ ), array( 'magnific-popup', 'swiper-css' ), $style_asset_file['version'] ); } } public function register_block_assets() { $script_asset_file = include( plugin_dir_path( __FILE__ ) . 'dist/scripts/backend/block.asset.php' ); $style_asset_file = include( plugin_dir_path( __FILE__ ) . 'dist/styles/frontend/index.asset.php' ); wp_register_script( 'wpz-insta_block-backend-script', plugins_url( 'dist/scripts/backend/block.js', __FILE__ ), $script_asset_file['dependencies'], $script_asset_file['version'] ); } /** * Load widget specific styles. */ public function widget_styles() { global $post; $general_options = get_option( 'wpzoom-instagram-general-settings' ); $should_enqueue = has_block( 'wpzoom/instagram-block' ); $has_reusable_block = self::has_reusable_block( 'wpzoom/instagram-block' ); $is_active_widget = is_active_widget( false, false, 'wpzoom_instagram_widget', false ); $has_shortcode = ( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'instagram' ) ); $has_widget_block = self::is_active_block_widget( 'wpzoom/instagram-block' ); $load_css_js = isset( $general_options['load-css-js'] ) ? true : false; if( is_admin() || $load_css_js || $should_enqueue || $has_reusable_block || $is_active_widget || $has_shortcode || $has_widget_block || isset( $_GET['wpz-insta-widget-preview'] ) ) { wp_enqueue_style( 'swiper-css', plugin_dir_url( __FILE__ ) . 'dist/styles/library/swiper.css', array(), '7.4.1' ); wp_enqueue_style( 'wpz-insta_block-frontend-style', plugin_dir_url( __FILE__ ) . 'dist/styles/frontend/index.css', array( 'dashicons' ), WPZOOM_INSTAGRAM_VERSION ); wp_enqueue_style( 'magnific-popup', plugin_dir_url( __FILE__ ) . 'dist/styles/library/magnific-popup.css', array( 'dashicons' ), WPZOOM_INSTAGRAM_VERSION ); } } /** * Register widget specific scripts. */ public function register_widget_scripts() { wp_register_script( 'zoom-instagram-widget-lazy-load', plugin_dir_url( __FILE__ ) . 'dist/scripts/library/lazy.js', array( 'jquery' ), filemtime( plugin_dir_path( __FILE__ ) . 'dist/scripts/library/lazy.js' ), true ); wp_register_script( 'magnific-popup', plugin_dir_url( __FILE__ ) . 'dist/scripts/library/magnific-popup.js', array( 'jquery', 'underscore', 'wp-util' ), filemtime( plugin_dir_path( __FILE__ ) . 'dist/scripts/library/magnific-popup.js' ), true ); wp_register_script( 'swiper-js', plugin_dir_url( __FILE__ ) . 'dist/scripts/library/swiper.js', array(), '7.0.0-alpha.21', true ); wp_register_script( 'zoom-instagram-widget', plugin_dir_url( __FILE__ ) . 'dist/scripts/frontend/index.js', array( 'jquery', 'underscore', 'wp-util', 'magnific-popup', 'swiper-js' ), WPZOOM_INSTAGRAM_VERSION, true ); } /** * Load widget specific scripts. */ public function enqueue_widget_scripts() { global $post; $general_options = get_option( 'wpzoom-instagram-general-settings' ); $should_enqueue = has_block( 'wpzoom/instagram-block' ); $has_reusable_block = self::has_reusable_block( 'wpzoom/instagram-block' ); $is_active_widget = is_active_widget( false, false, 'wpzoom_instagram_widget', false ); $has_shortcode = ( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'instagram' ) ); $has_widget_block = self::is_active_block_widget( 'wpzoom/instagram-block' ); $load_css_js = isset( $general_options['load-css-js'] ) ? true : false; if( is_admin() || $load_css_js || $should_enqueue || $has_reusable_block || $is_active_widget || $has_shortcode || $has_widget_block || isset( $_GET['wpz-insta-widget-preview'] ) ) { wp_enqueue_script( 'zoom-instagram-widget-lazy-load' ); wp_enqueue_script( 'magnific-popup' ); wp_enqueue_script( 'swiper-js' ); wp_enqueue_script( 'zoom-instagram-widget' ); wp_enqueue_script( 'wpz-insta_block-frontend-script' ); } } /** * Check the widget block based area has the block * * @since 2.0.2 * @param string $block_name The block name. * @return boolean Return true if post content has provided block name as reusable block, else return false. */ public static function is_active_block_widget( $blockname ) { $allwidgets = []; $widget_blocks = get_option( 'widget_block' ); $sidebars_widgets = get_option('sidebars_widgets'); if( is_array( $sidebars_widgets ) ) { foreach ( $sidebars_widgets as $key => $value ) { if( is_array( $value ) ) { foreach ($value as $widget_id) { $pieces = explode( '-', $widget_id ); $multi_number = array_pop( $pieces ); $id_base = implode( '-', $pieces ); $widget_data = get_option( 'widget_' . $id_base ); // Remove inactive widgets if( $key != 'wp_inactive_widgets' ) { unset( $widget_data['_multiwidget'] ); $allwidgets[ $key ] = $widget_data; } } } } } foreach( (array) $allwidgets as $widget ) { foreach( (array) $widget as $widget_element ) { foreach( (array)$widget_element as $value ) { if( is_string( $value ) && has_shortcode( $value, 'instagram' ) ) { return true; } } } } foreach( (array) $widget_blocks as $widget_block ) { if ( ! empty( $widget_block['content'] ) && ( has_block( $blockname, $widget_block['content'] ) || has_shortcode( $widget_block['content'], 'instagram' ) ) ) { return true; } } return false; } /** * Check the post content has reusable block * * @since 2.0.2 * @param string $block_name The block name. * @param int $post_id The post ID. * @param int $reusable_block_id The reusable block post ID. * @param boolean|int $content The post content. * @return boolean Return true if post content has provided block name as reusable block, else return false. */ public static function has_reusable_block( $block_name, $post_id = 0, $reusable_block_id = 0, $content = '' ) { $has_reusable_block = false; $post_id = $post_id > 0 ? $post_id : get_the_ID(); /** * Loop reusable blocks to get needed block * * @since 2.0.2 */ if ( ! empty( self::get_reusable_block( absint( $reusable_block_id ) ) ) ) { $args = array( 'post_type' => 'wp_block', 'posts_per_page' => -1, 'post_status' => 'publish', ); $query = new WP_Query( $args ); while ( $query->have_posts() ) { $query->the_post(); if ( absint( $reusable_block_id ) === get_the_ID() ) { $content = get_post_field( 'post_content', get_the_ID() ); if ( has_block( $block_name, $content ) ) { $has_reusable_block = true; return $has_reusable_block; } } } // Reset global post variable. After this point, we are back to the Main Query object. wp_reset_postdata(); } // Early return if $has_reusable_block is true. if ( true === $has_reusable_block ) { return; } if ( empty( $content ) ) { $content = get_post_field( 'post_content', $post_id ); } if ( $content ) { if ( has_block( 'block', $content ) ) { // Check reusable blocks. $blocks = parse_blocks( $content ); if ( ! is_array( $blocks ) || empty( $blocks ) ) { return false; } foreach ( $blocks as $block ) { if ( $block['blockName'] === 'core/block' && ! empty( $block['attrs']['ref'] ) ) { $reusable_block_id = absint( $block['attrs']['ref'] ); if ( has_block( $block_name, $reusable_block_id ) ) { return true; } elseif ( ! empty( self::get_reusable_block( $reusable_block_id ) ) ) { return true; } } } } elseif ( has_block( $block_name, $content ) ) { return true; } elseif ( has_shortcode( $content, 'reblex' ) ) { return true; } else { return false; } } return false; } /** * Get reusable block. * * @since 2.0.2 * @param int $id Reusable block id. * @return string Reusable block post content. */ public static function get_reusable_block( $id ) { $post = ''; if ( ! is_string( $id ) && $id > 0 ) { $wp_post = get_post( $id ); if ( $wp_post instanceof WP_Post ) { $post = $wp_post->post_content; } } return $post; } } } WPZOOM_Instagram_Widget_Assets::instance(); /** * WPZOOM Portfolio * * @package WPZOOM_Portfolio * @author WPZOOM * @copyright 2022 WPZOOM * @license GPL-2.0-or-later * * @wordpress-plugin * Plugin Name: WPZOOM Portfolio * Plugin URI: https://www.wpzoom.com/plugins/wpzoom-portfolio/ * Description: The ultimate solution for creatives, designers, photographers, and businesses looking to showcase their work in an elegant, professional, and fully customizable way. * Author: WPZOOM * Author URI: https://www.wpzoom.com * Text Domain: wpzoom-portfolio * Version: 1.4.2 * License: GPL2+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt */ // Exit if accessed directly defined( 'ABSPATH' ) || exit; if ( ! defined( 'WPZOOM_PORTFOLIO_VERSION' ) ) { define( 'WPZOOM_PORTFOLIO_VERSION', get_file_data( __FILE__, [ 'Version' ] )[0] ); // phpcs:ignore } // settings page url attribute define( 'WPZOOM_PORTFOLIO_SETTINGS_PAGE', 'wpzoom-portfolio-settings' ); define( 'WPZOOM_PORTFOLIO__FILE__', __FILE__ ); define( 'WPZOOM_PORTFOLIO_PLUGIN_BASE', plugin_basename( WPZOOM_PORTFOLIO__FILE__ ) ); define( 'WPZOOM_PORTFOLIO_PLUGIN_DIR', dirname( WPZOOM_PORTFOLIO_PLUGIN_BASE ) ); define( 'WPZOOM_PORTFOLIO_PATH', plugin_dir_path( WPZOOM_PORTFOLIO__FILE__ ) ); define( 'WPZOOM_PORTFOLIO_URL', plugin_dir_url( WPZOOM_PORTFOLIO__FILE__ ) ); // Instance the plugin $wpzoom_blocks = new WPZOOM_Blocks(); // Register plugin activation hook register_activation_hook( __FILE__, array( $wpzoom_blocks, 'activate' ) ); // Hook the plugin into WordPress add_action( 'init', array( $wpzoom_blocks, 'init' ) ); /** * Class WPZOOM_Blocks * * Main container class of the WPZOOM Blocks WordPress plugin. * * @since 1.0.0 */ class WPZOOM_Blocks { /** * Whether the plugin has been initialized. * * @var boolean * @access public * @since 1.0.0 */ public $initialized = false; /** * The path to this plugin's root directory. * * @var string * @access public * @since 1.0.0 */ public $plugin_dir_path; /** * The URL to this plugin's root directory. * * @var string * @access public * @since 1.0.0 */ public $plugin_dir_url; /** * The path to this plugin's "main" directory. * * @var string * @access public * @since 1.0.0 */ public $main_dir_path; /** * The URL to this plugin's "main" directory. * * @var string * @access public * @since 1.0.0 */ public $main_dir_url; /** * The path to this plugin's "blocks" directory. * * @var string * @access public * @since 1.0.0 */ public $blocks_dir_path; /** * The URL to this plugin's "blocks" directory. * * @var string * @access public * @since 1.0.0 */ public $blocks_dir_url; /** * Initializes the plugin and sets up needed hooks and features. * * @access public * @return void * @since 1.0.0 * @see WPZOOM_Blocks::load_assets() */ public function init() { // If the plugin has not already been initialized... if ( false === $this->initialized ) { // Assign the values for the plugins 'root' dir/url $this->plugin_dir_path = plugin_dir_path( __FILE__ ); $this->plugin_dir_url = plugin_dir_url( __FILE__ ); // Assign the values for the plugins 'main' dir/url $this->main_dir_path = trailingslashit( $this->plugin_dir_path . 'build' ); $this->main_dir_url = trailingslashit( $this->plugin_dir_url . 'build' ); // Assign the values for the plugins 'blocks' dir/url $this->blocks_dir_path = trailingslashit( $this->main_dir_path . 'blocks' ); $this->blocks_dir_url = trailingslashit( $this->main_dir_url . 'blocks' ); // Load the correct translation files for the plugin load_plugin_textdomain( 'wpzoom-portfolio', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); // Filter the Gutenberg block categories to add our custom 'WPZOOM Blocks' category if needed add_filter( 'block_categories_all', array( $this, 'filter_block_categories' ), 10, 2 ); // Load in all needed assets for the plugin $this->load_assets(); // Enqueue the main/root scripts and styles in the Gutenberg editor add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_portfolio_block_editor_assets' ) ); add_action( 'enqueue_block_assets', array( $this, 'enqueue_portfolio_block_assets' ) ); // Hook into the REST API in order to add some custom things add_action( 'rest_api_init', array( $this, 'rest_api_routes' ) ); // Add some extra needed styles on the frontend add_action( 'wp_enqueue_scripts', function() { wp_enqueue_script( 'jquery' ); wp_enqueue_style( 'dashicons' ); } ); // Mark the plugin as initialized $this->initialized = true; } } /** * Runs once during the activation of the plugin to run some one-time setup functions. * * @access public * @return void * @since 1.0.0 */ public function enqueue_portfolio_block_editor_assets() { wp_enqueue_script( 'masonry' ); $options = get_option( 'wpzoom-portfolio-settings' ); wp_enqueue_script( 'wpzoom-blocks-js-index-main' ); wp_localize_script( 'wpzoom-blocks-js-index-main', 'wpzoomPortfolioBlock', array( 'setting_options' => ( !empty( $options ) ? $options : array() ) ) ); wp_enqueue_style( 'wpzoom-blocks-css-editor-main' ); } /** * Runs once during the activation of the plugin to run some one-time setup functions. * * @access public * @return void * @since 1.0.0 */ public function enqueue_portfolio_block_assets() { $should_enqueue = has_block( 'wpzoom-blocks/portfolio' ) || has_block( 'wpzoom-blocks/portfolio-layouts' ) || WPZOOM_Portfolio_Assets_Manager::has_wpzoom_portfolio_shortcode(); if( ! $should_enqueue ) { return; } wp_enqueue_script( 'masonry' ); wp_enqueue_script( 'wpzoom-blocks-js-script-main' ); wp_enqueue_style( 'wpzoom-blocks-css-style-main' ); } /** * Runs once during the activation of the plugin to run some one-time setup functions. * * @access public * @return void * @since 1.0.0 * @see WPZOOM_Blocks::init() */ public function activate() { // Make sure the plugin is initialized $this->init(); // Flush the rewrite rules so any custom post types work correctly flush_rewrite_rules(); } /** * Loads in all the needed assets for the plugin. * * @access public * @return void * @since 1.0.0 * @see register_block_type() */ public function load_assets() { // Set a fallback for files with no version/dependency info $no_asset = array( 'dependencies' => array( 'wp-blocks', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill' ), 'version' => '-1' ); // Go through the main directory and each sub-directory in the blocks directory... foreach ( array_merge( array( $this->main_dir_path ), glob( $this->blocks_dir_path . '*', GLOB_ONLYDIR | GLOB_NOSORT ) ) as $path ) { // Get the slug for the directory in the current iteration $slug = 0 === substr_compare( $path, 'build/', -strlen( 'build/' ) ) ? 'main' : str_replace( $this->blocks_dir_path, '', $path ); // Get a version of the slug with dashes replaced by underscores $slug_ = str_replace( '-', '_', $slug ); // Consistent slashing $path = trailingslashit( $path ); // Go through every possible script/style there could be in the directory from the current iteration... foreach ( array( 'index' => 'js', 'script' => 'js', 'editor' => 'css', 'style' => 'css' ) as $name => $ext ) { // If a script/style with the given name exists in the directory from the current iteration... if ( file_exists( "$path$name.$ext" ) ) { // Get the version/dependency info $asset_file = "$path$name.asset.php"; $asset = file_exists( $asset_file ) ? require_once( $asset_file ) : $no_asset; // Register the script/style so it can be enqueued later $func = 'js' == $ext ? 'wp_register_script' : 'wp_register_style'; $url = trailingslashit( 'main' == $slug_ ? $this->main_dir_url : $this->blocks_dir_url . $slug ) . "$name.$ext"; $depends = 'js' == $ext ? $asset[ 'dependencies' ] : array(); $func( "wpzoom-blocks-$ext-$name-$slug_", $url, $depends, $asset[ 'version' ], ( 'main' != $slug_ && 'js' == $ext ) ); // If the file in the current iteration is a script... if ( 'js' == $ext && function_exists( 'wp_set_script_translations' ) ) { // Setup the translations for it wp_set_script_translations( "wpzoom-blocks-js-$name-$slug_", 'wpzoom-portfolio', plugin_dir_path( __FILE__ ) . 'languages' ); } } } // If the file in the current iteration is in a block... if ( 'main' != $slug_ ) { // Include the index.php file if the block has one if ( file_exists( $path . 'index.php' ) ) { require_once( $path . 'index.php' ); } // Construct the arguments array $args = array( 'editor_script' => "wpzoom-blocks-js-index-$slug_", 'editor_style' => "wpzoom-blocks-css-editor-$slug_", 'script' => "wpzoom-blocks-js-script-$slug_", 'style' => "wpzoom-blocks-css-style-$slug_" ); // Construct the class name to use below $class_name = 'WPZOOM_Blocks_' . ucwords( $slug_, '_' ); // If a class with the given name exists... if ( class_exists( $class_name ) ) { // Instantiate the class $class = new $class_name(); // Add attributes if they have been declared in the class if ( property_exists( $class, 'attributes' ) ) { $args[ 'attributes' ] = $class->attributes; } // Add a render callback if one is specified in the class if ( method_exists( $class, 'render' ) ) { $args[ 'render_callback' ] = array( $class, 'render' ); } } // Register the block with Gutenberg using the given arguments register_block_type( "wpzoom-blocks/$slug", $args ); } } } /** * Adds the WPZOOM category to the Gutenberg block categories, if not already present. * * @access public * @param array $categories Array containing all registered Gutenberg block categories. * @param WP_Post $post A WP_Post object representing the post being loaded. * @return array * @since 1.0.0 */ public function filter_block_categories( $categories, $post ) { // Get a list of all the block category slugs $category_slugs = wp_list_pluck( $categories, 'slug' ); // Return the list of categories with our custom category included return in_array( 'wpzoom-blocks', $category_slugs, true ) ? $categories : array_merge( $categories, array( array( 'slug' => 'wpzoom-blocks', 'title' => esc_html__( 'WPZOOM - Blocks', 'wpzoom-portfolio' ) ) ) ); } /** * Adds extra needed routes in the WordPress REST API. * * @access public * @return void * @since 1.0.0 * @see register_rest_route() * @see register_rest_field() * @see WPZOOM_Blocks::get_rest_image_sizes() * @see WPZOOM_Blocks::get_featured_media_urls() */ public function rest_api_routes() { // Register the 'image-sizes' REST API route register_rest_route( 'wpzoom-blocks/v1', '/image-sizes', array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_rest_image_sizes' ), 'permission_callback' => function() { return current_user_can( 'edit_posts' ); } ) ); // Register the 'featured_media_urls' REST API field on all post types register_rest_field( get_post_types(), 'featured_media_urls', array( 'get_callback' => array( $this, 'get_featured_media_urls' ), 'update_callback' => null, 'schema' => array( 'description' => esc_html__( 'Different sized featured images', 'wpzoom-portfolio' ), 'type' => 'array' ) ) ); } /** * Returns a REST response containing all available media library image sizes. * * @access public * @return array * @since 1.0.0 * @see get_intermediate_image_sizes() */ public function get_rest_image_sizes() { // Call the built-in get_intermediate_image_sizes() WordPress function to get an array of sizes $raw_sizes = get_intermediate_image_sizes(); // Build an array with sizes and their labels $sizes = array(); foreach ( $raw_sizes as $raw_size ) { $sizes[] = array( 'label' => ucwords( preg_replace( '/[_-]/', ' ', $raw_size ) ), 'value' => $raw_size ); } // Return the sizes array properly formatted for a rest response return rest_ensure_response( $sizes ); } /** * Returns an array of all the available image size URLs for the featured media from the given post object. * * @access public * @param WP_Post|Object $object The object that is the context to get the featured media ID from. * @return array * @since 1.0.0 * @see get_intermediate_image_sizes() * @see wp_get_attachment_image_src() */ function get_featured_media_urls( $object ) { // Initialize the array that will be returned $featured_media_urls = array(); // If the given object has attached featured media... if ( isset( $object[ 'featured_media' ] ) ) { // Keep track of the featured media ID $featured_media_id = $object[ 'featured_media' ]; // Call wp_get_attachment_image_src() with the default options for the best chance to get a fallback $thumb = wp_get_attachment_image_src( $featured_media_id ); // If the size above was found... if ( is_array( $thumb ) ) { // Set it so it will be present as a fallback if no other sizes can be found $featured_media_urls[ 'thumbnail' ] = $thumb; } // Go through every available image size... foreach ( get_intermediate_image_sizes() as $size ) { // Get the featured media source attached to the given object in the size from the current iteration $src = wp_get_attachment_image_src( $featured_media_id, $size, false ); // If the size was found... if ( is_array( $src ) ) { // Add it to the array of size URLs $featured_media_urls[ $size ] = $src; } } } // Return the array return $featured_media_urls; } } function wpzoom_theme_has_portfolio() { $wpzoom_themes = array( 'angle', 'inspiro', 'wpzoom-inspiro-pro', 'wpzoom-reel', 'wpzoom-rezzo' ); $current_theme = get_option( 'stylesheet' ); if( ! in_array( $current_theme, $wpzoom_themes ) ) { return false; } else { if( 'inspiro' == $current_theme ) { $theme = wp_get_theme(); if( 'https://www.wpzoom.com/free-wordpress-themes/inspiro-lite/' == $theme->get( 'ThemeURI' ) ) { return false; } } } return true; } if( ! function_exists( 'wpzoom_portfolio_load_files' ) ) { function wpzoom_portfolio_load_files() { //Add Portfolio Shortcode require_once 'classes/class-wpzoom-portfolio-shortcode.php'; require_once 'classes/class-wpzoom-portfolio-admin-menu.php'; require_once 'classes/class-wpzoom-portfolio-custom-posts.php'; require_once 'classes/class-wpzoom-portfolio-assets-manager.php'; require_once 'classes/class-wpzoom-wptt-webfont-loader.php'; //Load Settings Panel require_once 'classes/class-wpzoom-settings-fields.php'; require_once 'classes/class-wpzoom-portfolio-settings-page.php'; if( ! wpzoom_theme_has_portfolio() ) { //Load Archive template require_once 'classes/class-wpzoom-portfolio-template.php'; } if( ! wpzoom_theme_has_portfolio() ) { //Load Archive template require_once 'classes/class-wpzoom-portfolio-template.php'; } if( ! class_exists( 'WPZOOM_Portfolio_Pro' ) && ! wpzoom_theme_has_portfolio() ) { require_once 'classes/class-wpzoom-portfolio-metaboxes-upsell.php'; } } add_action( 'plugin_loaded', 'wpzoom_portfolio_load_files' ); } function load_reorder_portfolio_items() { if( ! current_user_can( 'edit_posts' ) || current_theme_supports( 'zoom-portfolio' ) ) { return; } //Load Re-Order feature require_once 'classes/featured-posts/class-wpzoom-portfolio-featured-posts.php'; $wpzoom_portfrolio_reorder_settings = array( //Unique Id that is used to add the new column in posts list table. 'id' => 'wpzoom_is_featured_id', //Label that appears in the submenu of post types 'menu_title' => __( 'Re-order', 'wpzoom-portfolio' ), //Post type in which this feature will be added. 'post_type' => 'portfolio_item', ); $featured_posts_plugin_uri = WPZOOM_PORTFOLIO_URL . '/classes/featured-posts/'; $list_table_checkbox_directory_uri = WPZOOM_PORTFOLIO_URL . '/classes/featured-posts/list-table-checkbox'; new WPZOOM_Featured_Posts( $wpzoom_portfrolio_reorder_settings, $featured_posts_plugin_uri ); } add_action( 'init', 'load_reorder_portfolio_items' ); add_action( 'init', 'WPZOOM_Blocks_Portfolio_Shortcode::instance' );/** * Plugin Name: Video Popup Block by WPZOOM * Plugin URI: https://wordpress.org/plugins/wpzoom-video-popup-block/ * Description: Quickly add a button displaying a YouTube, Vimeo or Self-Hosted (MP4) video in a popup when clicked. * Version: 1.1.1 * Author: WPZOOM * Author URI: https://www.wpzoom.com/ * Text Domain: wpzoom-video-popup-block * Domain Path: /languages * License: GPLv2 or later * License URI: https://www.gnu.org/licenses/gpl-2.0.html * Requires at least: 6.0 * Requires PHP: 7.2 * Tested up to: 6.4 * * @package Wpzoom_Video_Popup_Block */ namespace WPZOOM\Video_Popup_Block; // Exit if accessed directly. defined( 'ABSPATH' ) || exit; // Intitalize the plugin. new Plugin(); /** * Main WPZOOM Video Popup Block class. * * The entry point into WordPress for this plugin. * * @since 1.0.0 */ class Plugin { /** * The version of this plugin. * * @since 1.0.0 * @var int */ public const VERSION = '1.1.1'; /** * Path to the plugin directory. * * @since 1.0.0 * @var string */ public $plugin_path; /** * URL to the plugin directory. * * @since 1.0.0 * @var string */ public $plugin_url; /** * Main directory name of the plugin. * * @since 1.0.0 * @var string */ public $plugin_dirname; /** * Main file name of the plugin. * * @since 1.0.0 * @var string */ public $plugin_filename; /** * The name of the block this plugin adds. * * @since 1.0.0 * @var string */ public $block_name; /** * Plugin class constructor. * * @since 1.0.0 * @return void */ public function __construct() { $this->plugin_path = plugin_dir_path( __FILE__ ); $this->plugin_url = plugin_dir_url( __FILE__ ); $this->plugin_dirname = trailingslashit( wp_basename( __DIR__ ) ); $this->plugin_filename = wp_basename( __FILE__ ); $this->block_name = 'wpzoom-video-popup-block/block'; // Do some initial setup on the WordPress `init` hook. add_action( 'init', array( $this, 'init' ) ); // Add the WPZOOM block category, if needed. add_filter( 'block_categories_all', array( $this, 'block_categories' ), 10, 2 ); // Add some useful CSS classes. add_filter( 'body_class', array( $this, 'body_class' ) ); add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) ); } /** * Initializes the plugin and hooks into WordPress. * * @since 1.0.0 * @return void */ public function init() { // Load the translations for the plugin. load_plugin_textdomain( 'wpzoom-video-popup-block', false, $this->plugin_dirname . 'languages/' ); // Register the main block in Gutenberg. register_block_type( $this->plugin_path . 'block.json' ); // Setup translations for the main block. wp_set_script_translations( 'wpzoom-video-popup-block-block-editor-script-js', 'wpzoom-video-popup-block', $this->plugin_path . 'languages/' ); } /** * Adds the WPZOOM block category if needed. * * @since 1.0.0 * @param array $categories The list of existing block categories. * @return array The modified list of block categories. */ public function block_categories( $categories ) { if ( empty( $categories ) || ( ! empty( $categories ) && is_array( $categories ) && ! in_array( 'wpzoom-blocks', wp_list_pluck( $categories, 'slug' ), true ) ) ) { $categories = array_merge( $categories, array( array( 'slug' => 'wpzoom-blocks', 'title' => esc_html__( 'WPZOOM - Blocks', 'wpzoom-video-popup-block' ), ), ) ); } return $categories; } /** * Returns whether the plugin is in "PRO" mode. * * @since 1.0.1 * @return bool Boolean indicating whether the plugin is in "PRO" mode. */ public function is_pro() { return boolval( apply_filters( 'wpzoom_video_popup_block_is_pro', false ) ); } /** * Adds some classes for the plugin to the `` tag of the page. * * @since 1.0.1 * @param array $classes Array of existing classes. * @return array The modified classes array. */ public function body_class( $classes ) { if ( has_block( 'wpzoom-video-popup-block/block' ) ) { $classes[] = 'wpzoom-video-popup_enabled'; if ( is_admin() ) { $classes[] = 'wpzoom-video-popup_admin'; } if ( $this->is_pro() ) { $classes[] = 'wpzoom-video-popup_is-pro'; } } return $classes; } /** * Adds some classes for the plugin to the `` tag of the WordPress admin. * * @since 1.0.1 * @param string $classes Space-separated string of existing classes. * @return string The modified classes string. */ public function admin_body_class( $classes ) { if ( has_block( 'wpzoom-video-popup-block/block' ) ) { $classes .= ' wpzoom-video-popup_enabled '; if ( is_admin() ) { $classes .= ' wpzoom-video-popup_admin '; } if ( $this->is_pro() ) { $classes .= ' wpzoom-video-popup_is-pro '; } } return $classes; } } /** * Inspiro functions and definitions * * @link https://developer.wordpress.org/themes/basics/theme-functions/ * * @package Inspiro * @since Inspiro 1.0.0 */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Define Constants */ define( 'INSPIRO_THEME_VERSION', '2.1.3' ); define( 'INSPIRO_THEME_DIR', trailingslashit( get_template_directory() ) ); define( 'INSPIRO_THEME_URI', trailingslashit( esc_url( get_template_directory_uri() ) ) ); define( 'INSPIRO_THEME_ASSETS_URI', INSPIRO_THEME_URI . 'dist' ); // Marketing define( 'INSPIRO_MARKETING_UTM_CODE_STARTER_SITE', '?utm_source=wpadmin&utm_medium=starter-sites&utm_campaign=upgrade-premium' ); define( 'INSPIRO_MARKETING_UTM_CODE_FOOTER_MENU', '?utm_source=wpadmin&utm_medium=footer-menu&utm_campaign=upgrade-premium' ); // This theme requires WordPress 5.3 or later. if ( version_compare( $GLOBALS['wp_version'], '5.3', '<' ) ) { require INSPIRO_THEME_DIR . 'inc/back-compat.php'; } /** * Recommended Plugins */ require INSPIRO_THEME_DIR . 'inc/classes/class-tgm-plugin-activation.php'; /** * Setup helper functions. */ require INSPIRO_THEME_DIR . 'inc/common-functions.php'; /** * Setup theme media. */ require INSPIRO_THEME_DIR . 'inc/theme-media.php'; /** * Enqueues scripts and styles */ require INSPIRO_THEME_DIR . 'inc/classes/class-inspiro-enqueue-scripts.php'; /** * Starter Content Notice */ require INSPIRO_THEME_DIR . 'inc/classes/class-inspiro-starter-content-notice.php'; /** * Setup custom wp-admin options pages */ require INSPIRO_THEME_DIR . 'inc/classes/class-inspiro-custom-wp-admin-menu.php'; /** * Additional features to include custom WP pointer function */ require INSPIRO_THEME_DIR . 'inc/classes/class-inspiro-wp-admin-menu-pointer.php'; /** * Functions and definitions. */ require INSPIRO_THEME_DIR . 'inc/classes/class-inspiro-after-setup-theme.php'; /** * Handle SVG icons. */ require INSPIRO_THEME_DIR . 'inc/classes/class-inspiro-svg-icons.php'; /** * Implement the Custom Header feature. */ require INSPIRO_THEME_DIR . 'inc/custom-header.php'; /** * Custom template tags for this theme. */ require INSPIRO_THEME_DIR . 'inc/template-tags.php'; /** * Additional features to allow styling of the templates. */ require INSPIRO_THEME_DIR . 'inc/template-functions.php'; /** * Custom Template WC functions */ require INSPIRO_THEME_DIR . 'inc/wc-custom-functions.php'; /** * Editor Fonts */ require INSPIRO_THEME_DIR . 'inc/editor-fonts.php'; /** * Custom template shortcode tags for this theme */ // require INSPIRO_THEME_DIR . 'inc/shortcodes.php'; /** * Customizer additions. */ require INSPIRO_THEME_DIR . 'inc/classes/class-inspiro-font-family-manager.php'; require INSPIRO_THEME_DIR . 'inc/classes/class-inspiro-fonts-manager.php'; // Include Customizer Guided Tour if ( is_admin() ) { // && is_customize_preview(), AJAX don't work with is_customize_preview() included require INSPIRO_THEME_DIR . 'inc/classes/inspiro-customizer-guided-tour.php'; } require INSPIRO_THEME_DIR . 'inc/customizer-functions.php'; require INSPIRO_THEME_DIR . 'inc/customizer/class-inspiro-customizer-control-base.php'; require INSPIRO_THEME_DIR . 'inc/customizer/class-inspiro-customizer.php'; /** * SVG icons functions and filters. */ require INSPIRO_THEME_DIR . 'inc/icon-functions.php'; /** * Theme admin notices and info page */ if ( is_admin() ) { require INSPIRO_THEME_DIR . 'inc/admin-notice.php'; require INSPIRO_THEME_DIR . 'inc/admin/admin-api.php'; // temporary marketing black friday functionality require INSPIRO_THEME_DIR . 'inc/marketing-functions.php'; if ( current_user_can( 'manage_options' ) ) { require INSPIRO_THEME_DIR . 'inc/classes/class-inspiro-notices.php'; require INSPIRO_THEME_DIR . 'inc/classes/class-inspiro-notice-review.php'; require INSPIRO_THEME_DIR . 'inc/classes/class-inspiro-theme-deactivation.php'; } } /** * Theme Upgrader */ require INSPIRO_THEME_DIR . 'inc/classes/class-inspiro-theme-upgrader.php'; /** * Inline theme css generated dynamically */ require INSPIRO_THEME_DIR . 'inc/dynamic-css/body.php'; require INSPIRO_THEME_DIR . 'inc/dynamic-css/logo.php'; require INSPIRO_THEME_DIR . 'inc/dynamic-css/headings.php'; require INSPIRO_THEME_DIR . 'inc/dynamic-css/h1.php'; require INSPIRO_THEME_DIR . 'inc/dynamic-css/page-title.php'; require INSPIRO_THEME_DIR . 'inc/dynamic-css/h1-content.php'; require INSPIRO_THEME_DIR . 'inc/dynamic-css/content-headings.php'; require INSPIRO_THEME_DIR . 'inc/dynamic-css/hero-header-title.php'; require INSPIRO_THEME_DIR . 'inc/dynamic-css/hero-header-desc.php'; require INSPIRO_THEME_DIR . 'inc/dynamic-css/hero-header-button.php'; require INSPIRO_THEME_DIR . 'inc/dynamic-css/main-menu.php'; require INSPIRO_THEME_DIR . 'inc/dynamic-css/mobile-menu.php'; /** * Container Width Functions */ /** * Filter theme.json to make contentSize dynamic based on customizer container width */ if ( ! function_exists( 'inspiro_filter_theme_json_data' ) ) : function inspiro_filter_theme_json_data( $theme_json_data ) { $container_width = get_theme_mod( 'container_width', 1200 ); $container_width_narrow = get_theme_mod( 'container_width_narrow', 950 ); // Get the data array from the WP_Theme_JSON_Data object $theme_json = $theme_json_data->get_data(); // Determine which width to use based on context // Pages use default container width, single posts use narrow width $content_size = $container_width; // Default to full width for pages if ( is_single() || is_home() || is_archive() || is_category() || is_tag() || is_author() || is_date() ) { $content_size = $container_width_narrow; // Use narrow width for blog contexts } // Update the contentSize in theme.json if ( isset( $theme_json['settings']['layout']['contentSize'] ) ) { $theme_json['settings']['layout']['contentSize'] = $content_size . 'px'; } // Set wideSize to be content width + 250px to match CSS .alignwide styles if ( isset( $theme_json['settings']['layout']['wideSize'] ) ) { $wide_size = $content_size + 250; $theme_json['settings']['layout']['wideSize'] = $wide_size . 'px'; } // Update the data in the object and return it $theme_json_data->update_with( $theme_json ); return $theme_json_data; } endif; add_filter( 'wp_theme_json_data_user', 'inspiro_filter_theme_json_data' ); /** * Also apply the container width to block editor */ if ( ! function_exists( 'inspiro_filter_theme_json_theme' ) ) : function inspiro_filter_theme_json_theme( $theme_json_data ) { return inspiro_filter_theme_json_data( $theme_json_data ); } endif; add_filter( 'wp_theme_json_data_theme', 'inspiro_filter_theme_json_theme' ); /** * Update editor styles to reflect container width changes */ if ( ! function_exists( 'inspiro_add_editor_container_width_styles' ) ) : function inspiro_add_editor_container_width_styles() { $container_width = get_theme_mod( 'container_width', 1200 ); $container_width_narrow = get_theme_mod( 'container_width_narrow', 950 ); // Determine which width to use based on context // Pages use default container width, single posts use narrow width $content_size = $container_width; // Default to full width for pages if ( is_single() || is_home() || is_archive() || is_category() || is_tag() || is_author() || is_date() ) { $content_size = $container_width_narrow; // Use narrow width for blog contexts } $wide_size = $content_size + 250; $editor_styles = " .editor-styles-wrapper .wp-block { max-width: {$content_size}px; } .editor-styles-wrapper .wp-block[data-align='wide'] { max-width: {$wide_size}px; } "; wp_add_inline_style( 'wp-edit-blocks', $editor_styles ); } endif; add_action( 'enqueue_block_editor_assets', 'inspiro_add_editor_container_width_styles' ); /** * Add dynamic CSS variables for container widths */ if ( ! function_exists( 'inspiro_add_container_width_css_variables' ) ) : function inspiro_add_container_width_css_variables() { $container_width = get_theme_mod( 'container_width', 1200 ); $container_width_narrow = get_theme_mod( 'container_width_narrow', 950 ); $container_width_elementor = get_theme_mod( 'container_width_elementor', false ); // Calculate responsive padding breakpoints $container_padding = 30; // 30px padding $container_width_breakpoint = $container_width + 60; // container width + 60px buffer $container_width_narrow_breakpoint = $container_width_narrow + 60; // narrow container width + 60px buffer $css = " :root { --container-width: {$container_width}px; --container-width-narrow: {$container_width_narrow}px; --container-padding: {$container_padding}px; } /* Dynamic responsive padding media queries */ @media (max-width: {$container_width_breakpoint}px) { .wrap, .inner-wrap, .page .entry-content, .page:not(.inspiro-front-page) .entry-footer, .single .entry-wrapper, .single.has-sidebar.page-layout-sidebar-right .entry-header .inner-wrap, .wp-block-group > .wp-block-group__inner-container { padding-left: {$container_padding}px; padding-right: {$container_padding}px; } } @media (max-width: {$container_width_narrow_breakpoint}px) { .single .entry-header .inner-wrap, .single .entry-content, .single .entry-footer, #comments { padding-left: {$container_padding}px; padding-right: {$container_padding}px; } } "; // Add Elementor container width override if enabled if ( $container_width_elementor ) { $css .= " .elementor-container { max-width: {$container_width}px !important; } "; } wp_add_inline_style( 'inspiro-style', $css ); } endif; add_action( 'wp_enqueue_scripts', 'inspiro_add_container_width_css_variables' );{"id":20979,"date":"2026-03-26T20:42:43","date_gmt":"2026-03-26T20:42:43","guid":{"rendered":"https:\/\/bragitrade.com\/?p=20979"},"modified":"2026-03-26T20:49:14","modified_gmt":"2026-03-26T20:49:14","slug":"bonuskong-casino-lessor-du-jeu-en-ligne-en-2025-et-ses-nouvelles-tendances","status":"publish","type":"post","link":"https:\/\/bragitrade.com\/index.php\/2026\/03\/26\/bonuskong-casino-lessor-du-jeu-en-ligne-en-2025-et-ses-nouvelles-tendances\/","title":{"rendered":"Bonuskong Casino : L’essor du jeu en ligne en 2025 et ses nouvelles tendances"},"content":{"rendered":"
\n

L’\u00c9volution du Jeu en Ligne en 2025 : Ce Que Vous Devez Savoir<\/h2>\n

\n L’univers du jeu en ligne, aussi connu sous le nom d’iGaming<\/strong>, conna\u00eet une croissance impressionnante. En 2025, cette industrie continue de se r\u00e9inventer, offrant des exp\u00e9riences toujours plus riches et accessibles. Pour vous, nouveaux venus ou joueurs occasionnels, comprendre ces \u00e9volutions est essentiel pour profiter pleinement de ce monde fascinant. Pensez-y comme \u00e0 une salle de jeux moderne, mais directement sur votre appareil pr\u00e9f\u00e9r\u00e9. Vous pouvez trouver des informations d\u00e9taill\u00e9es sur ce type de plateforme sur ce site<\/a>, qui illustre bien la diversit\u00e9 des offres disponibles. L’accessibilit\u00e9 accrue, gr\u00e2ce aux progr\u00e8s technologiques, signifie que vous pouvez jouer o\u00f9 vous voulez, quand vous voulez. C’est un changement majeur par rapport aux casinos physiques d’autrefois, n\u00e9cessitant d\u00e9placements et horaires fixes.\n <\/p>\n

\n Pourquoi est-ce important pour vous ? Parce que le march\u00e9 \u00e9volue rapidement. De nouvelles machines \u00e0 sous apparaissent chaque mois, les bonus<\/strong> se font plus attrayants et les options de jeu se diversifient. Savoir o\u00f9 chercher et comment fonctionnent ces \u00e9l\u00e9ments vous permet de faire de meilleurs choix et d’optimiser votre exp\u00e9rience. Imaginez devoir choisir une nouvelle voiture sans conna\u00eetre les options disponibles ; c’est un peu la m\u00eame id\u00e9e ici. On parle souvent de RTP<\/strong> (taux de retour au joueur), un indicateur cl\u00e9 que nous allons expliquer plus loin. Il s’agit d’un pourcentage qui vous donne une id\u00e9e de la rentabilit\u00e9 d’une machine \u00e0 sous sur le long terme. Un RTP \u00e9lev\u00e9 signifie, en th\u00e9orie, que la machine redistribue une plus grande partie des sommes mis\u00e9es.\n <\/p>\n

\n Je pense que la clart\u00e9 est primordiale. Trop de sites utilisent un jargon complexe qui peut intimider. Mon but ici est de d\u00e9mystifier tout cela pour vous. Nous allons aborder les aspects essentiels, des termes techniques aux nouvelles tendances, d’une mani\u00e8re simple et directe. J’ai vu beaucoup de changements dans ce secteur au fil des ans, et je sais que comprendre les bases fait toute la diff\u00e9rence. Vous cherchez peut-\u00eatre \u00e0 essayer une nouvelle machine \u00e0 sous ou \u00e0 comprendre comment fonctionnent les tours gratuits. Tout cela est \u00e0 votre port\u00e9e avec un peu de connaissance.\n <\/p>\n

Bonuskong Casino ou la strat\u00e9gie gagnante pour les joueurs exigeants<\/a><\/p>\n

Les Bases du Jeu en Ligne D\u00e9mystifi\u00e9es<\/h3>\n

\n Avant de plonger dans les tendances de 2025, comprenons ce qu’est un casino en ligne. C’est une plateforme num\u00e9rique o\u00f9 vous pouvez jouer \u00e0 divers jeux de hasard, comme les machines \u00e0 sous, le blackjack, la roulette, et bien d’autres, en utilisant de l’argent r\u00e9el. L’inscription est g\u00e9n\u00e9ralement simple et rapide. Une fois votre compte cr\u00e9\u00e9 et approvisionn\u00e9, vous acc\u00e9dez \u00e0 une ludoth\u00e8que virtuelle. La diff\u00e9rence principale avec un casino terrestre, c’est la commodit\u00e9. Vous pouvez jouer depuis chez vous, dans les transports, ou lors d’une pause. C’est une democratisation de l’acc\u00e8s aux jeux de casino.\n <\/p>\n

\n Le terme iGaming<\/strong> englobe toutes ces activit\u00e9s. Il est devenu un terme courant dans l’industrie. Vous entendrez aussi parler de jeux de hasard<\/strong>, qui sont les jeux o\u00f9 le r\u00e9sultat d\u00e9pend principalement de la chance. Pensez \u00e0 la roulette ; vous ne contr\u00f4lez pas o\u00f9 la bille va s’arr\u00eater. Les machines \u00e0 sous fonctionnent sur un principe similaire, avec des algorithmes al\u00e9atoires d\u00e9terminant les combinaisons gagnantes. C’est cette part d’al\u00e9atoire qui rend le jeu excitant pour beaucoup.\n <\/p>\n

\n Un aspect important pour tout joueur est la gestion de son budget. Le jeu doit rester un divertissement. Les casinos en ligne proposent souvent des outils pour vous aider, comme des limites de d\u00e9p\u00f4t, de mise ou de temps de session. C’est une forme de jeu responsable<\/strong>, une pratique que je recommande vivement. Savoir s’arr\u00eater est aussi important que savoir jouer. Avez-vous d\u00e9j\u00e0 ressenti l’envie de jouer plus que pr\u00e9vu ? C’est l\u00e0 que ces outils prennent tout leur sens. Les casinos s\u00e9rieux mettent un point d’honneur \u00e0 proposer ces dispositifs.\n <\/p>\n

6 Redenen Waarom Bonuskong Casino Jouw Volgende Speelplek Moet Zijn<\/a><\/p>\n

Les Nouvelles Tendances en 2025 : Que Voir Arriver ?<\/h2>\n

\n L’ann\u00e9e 2025 est marqu\u00e9e par une acc\u00e9l\u00e9ration de plusieurs tendances qui remod\u00e8lent le paysage de l’iGaming. L’une des plus visibles est l’essor des machines \u00e0 sous vid\u00e9o<\/strong> toujours plus sophistiqu\u00e9es. Ces jeux ne se contentent plus de faire tourner des rouleaux ; ils int\u00e8grent des sc\u00e9narios, des animations dignes des plus grands films et des fonctionnalit\u00e9s bonus complexes. La volatilit\u00e9<\/strong> (la fr\u00e9quence et le montant des gains potentiels) est un facteur cl\u00e9 que les d\u00e9veloppeurs ajustent pour plaire \u00e0 diff\u00e9rents types de joueurs. Une machine \u00e0 faible volatilit\u00e9 paie plus souvent, mais avec des montants plus petits, tandis qu’une machine \u00e0 forte volatilit\u00e9 offre des gains moins fr\u00e9quents, mais potentiellement beaucoup plus importants.\n <\/p>\n

\n Les joueurs<\/strong> recherchent de plus en plus des exp\u00e9riences immersives. C’est l\u00e0 que les jeux avec dealer en direct<\/strong> (ou live dealer<\/em>) prennent une place de choix. Imaginez jouer au blackjack ou \u00e0 la roulette face \u00e0 un vrai croupier, en temps r\u00e9el, depuis votre salon. Les flux vid\u00e9o de haute qualit\u00e9 et les tables interactives cr\u00e9ent une atmosph\u00e8re proche de celle d’un casino physique. Les d\u00e9veloppeurs travaillent sur l’int\u00e9gration de l’intelligence artificielle pour rendre ces interactions encore plus naturelles, par exemple en permettant au croupier de r\u00e9agir de mani\u00e8re plus personnalis\u00e9e aux actions des joueurs. C’est un domaine qui va continuer de cro\u00eetre.\n <\/p>\n

\n La personnalisation est une autre tendance forte. Les plateformes apprennent \u00e0 conna\u00eetre vos pr\u00e9f\u00e9rences pour vous proposer des jeux et des offres qui vous correspondent. Si vous aimez les machines \u00e0 sous \u00e0 th\u00e8me m\u00e9di\u00e9val, vous verrez appara\u00eetre davantage de recommandations de ce type. Les bonus<\/strong>, qui sont des avantages offerts aux joueurs, se d\u00e9clinent eux aussi de mani\u00e8re plus cibl\u00e9e. Au lieu d’un bonus g\u00e9n\u00e9rique, vous pourriez recevoir une offre sp\u00e9ciale sur votre type de jeu pr\u00e9f\u00e9r\u00e9. Je pense que cette approche personnalis\u00e9e est une excellente chose pour l’exp\u00e9rience utilisateur. Cela montre que les op\u00e9rateurs se soucient de votre satisfaction.\n <\/p>\n

\n Et n’oublions pas les jeux mobiles. La majorit\u00e9 des joueurs acc\u00e8dent d\u00e9sormais aux casinos en ligne via leur smartphone ou leur tablette. Les interfaces sont optimis\u00e9es pour les petits \u00e9crans, et l’exp\u00e9rience est fluide. Les d\u00e9veloppeurs cr\u00e9ent des jeux qui fonctionnent parfaitement sur tous les appareils, sans perte de qualit\u00e9. C’est un aspect indispensable aujourd’hui.\n <\/p>\n

Les Offres et Bonus : Un Point Cl\u00e9 pour les Joueurs<\/h3>\n

\n Les bonus<\/strong> sont souvent le premier point de contact entre un nouveau joueur et un casino en ligne. Ils sont con\u00e7us pour attirer et fid\u00e9liser. Le plus courant est le bonus de bienvenue<\/strong>, souvent un pourcentage de votre premier d\u00e9p\u00f4t, ou des tours gratuits<\/strong> (free spins<\/em>) sur une machine \u00e0 sous sp\u00e9cifique. Par exemple, un bonus de 100% jusqu’\u00e0 200\u20ac signifie que si vous d\u00e9posez 200\u20ac, le casino vous en ajoute 200\u20ac pour jouer. C’est comme recevoir un cadeau pour commencer.\n <\/p>\n

\n Cependant, il est essentiel<\/strong> de comprendre les conditions associ\u00e9es \u00e0 ces bonus. Le terme le plus important ici est le requisito de mise<\/strong> (ou wagering requirement<\/em>). Il s’agit du nombre de fois que vous devez miser le montant du bonus (et parfois du d\u00e9p\u00f4t) avant de pouvoir retirer vos gains. Si vous recevez un bonus de 100\u20ac avec un requisito de mise de 30x, cela signifie que vous devez miser un total de 3000\u20ac avant de pouvoir retirer l’argent gagn\u00e9 gr\u00e2ce \u00e0 ce bonus. C’est un peu comme si vous deviez prouver que vous avez jou\u00e9 s\u00e9rieusement avant de pouvoir garder vos gains.\n <\/p>\n

\n Je conseille toujours de lire attentivement les termes et conditions. Ne vous laissez pas aveugler par le montant du bonus sans comprendre les contraintes. Les tours gratuits<\/strong> sont une autre forme de bonus populaire. Ils vous permettent de jouer un certain nombre de fois sur une machine \u00e0 sous sans utiliser votre propre argent. Les gains obtenus avec ces tours sont souvent soumis aux m\u00eames requisitos de mise que les bonus en esp\u00e8ces. C’est une excellente mani\u00e8re de d\u00e9couvrir de nouvelles machines sans risque.\n <\/p>\n

    \n
  • Bonus de bienvenue :<\/strong> Offert lors de votre premi\u00e8re inscription.<\/li>\n
  • Bonus sans d\u00e9p\u00f4t :<\/strong> Rare, mais tr\u00e8s appr\u00e9ci\u00e9, il vous donne du cr\u00e9dit ou des tours gratuits sans que vous ayez \u00e0 d\u00e9poser d’argent.<\/li>\n
  • Bonus de recharge :<\/strong> Pour les d\u00e9p\u00f4ts suivants, souvent propos\u00e9s aux joueurs fid\u00e8les.<\/li>\n
  • Tours gratuits :<\/strong> Permettent de jouer gratuitement sur des machines \u00e0 sous.<\/li>\n
  • Cashback :<\/strong> Un pourcentage de vos pertes rembours\u00e9.<\/li>\n<\/ul>\n

    Les Jeux les Plus Populaires et Leurs Sp\u00e9cificit\u00e9s<\/h2>\n

    \n Les machines \u00e0 sous restent le c\u0153ur de l’offre de la plupart des casinos en ligne. En 2025, elles continuent d’innover avec des m\u00e9caniques de jeu toujours plus complexes. Pensez aux m\u00e9gaways<\/strong>, o\u00f9 le nombre de fa\u00e7ons de gagner change \u00e0 chaque tour, offrant des milliers, voire des centaines de milliers de combinaisons possibles. C’est une \u00e9volution majeure par rapport aux machines \u00e0 sous classiques \u00e0 3 rouleaux. La volatilit\u00e9<\/strong> de ces jeux peut varier \u00e9norm\u00e9ment, allant de machines douces qui vous offrent de petits gains fr\u00e9quents \u00e0 des monstres \u00e0 forte volatilit\u00e9 qui promettent des jackpots gigantesques, mais avec beaucoup moins de r\u00e9gularit\u00e9.\n <\/p>\n

    \n Les jeux de table traditionnels comme le blackjack, la roulette et le baccarat continuent de s\u00e9duire. Les versions avec dealer en direct<\/strong> transcendent l’exp\u00e9rience. Le blackjack en direct, par exemple, vous permet de vous asseoir \u00e0 une table virtuelle et de jouer avec un vrai croupier qui distribue les cartes. La communication se fait via un chat, et vous pouvez voir l’action se d\u00e9rouler en temps r\u00e9el. C’est l’interaction humaine qui fait la diff\u00e9rence, rendant le jeu plus engageant. J’aime particuli\u00e8rement l’atmosph\u00e8re qui se d\u00e9gage de ces tables.\n <\/p>\n

    \n La roulette en direct offre plusieurs variantes : europ\u00e9enne, am\u00e9ricaine, fran\u00e7aise, et m\u00eame des versions avec plusieurs balles ou des jokers. Le RTP<\/strong> sur les jeux de table est g\u00e9n\u00e9ralement plus \u00e9lev\u00e9 que sur les machines \u00e0 sous. Par exemple, le blackjack europ\u00e9en a un RTP qui peut d\u00e9passer 99% si jou\u00e9 avec une strat\u00e9gie optimale. La roulette europ\u00e9enne, avec son unique z\u00e9ro, a un RTP d’environ 97.3%. Ces chiffres vous donnent une perspective sur la “maison” (l’avantage du casino) qui est plus faible sur ces jeux, surtout si vous jouez intelligemment.\n <\/p>\n

    \n Les jackpots progressifs continuent d’attirer ceux qui r\u00eavent de devenir millionnaires du jour au lendemain. Ces jackpots, souvent li\u00e9s \u00e0 un r\u00e9seau de machines \u00e0 sous sur plusieurs casinos, peuvent atteindre des sommes astronomiques avant d’\u00eatre d\u00e9croch\u00e9s. La probabilit\u00e9 de gagner un jackpot progressif est faible, mais l’attrait du gain immense est puissant. Il est important de noter que pour \u00eatre \u00e9ligible au jackpot, vous devez souvent miser le montant maximum autoris\u00e9.\n <\/p>\n

    \n Et pour ceux qui aiment les paris plus rapides, les jeux de type loterie ou Keno<\/strong> offrent des tirages fr\u00e9quents et des gains potentiels importants. Ces jeux sont simples \u00e0 comprendre : vous choisissez des num\u00e9ros, et si un certain nombre de vos num\u00e9ros sont tir\u00e9s, vous gagnez.\n <\/p>\n

    G\u00e9rer Votre Argent et Jouer de Mani\u00e8re Responsable<\/h2>\n

    \n Le jeu en ligne peut \u00eatre une source de divertissement formidable, \u00e0 condition d’aborder la gestion de votre argent avec discipline. C’est l\u00e0 que le jeu responsable<\/strong> entre en jeu. Avant de commencer \u00e0 jouer, d\u00e9terminez un budget clair. Combien pouvez-vous vous permettre de d\u00e9penser sans que cela n’affecte vos finances personnelles ? Ce budget doit \u00eatre consid\u00e9r\u00e9 comme le co\u00fbt de votre divertissement, un peu comme aller au cin\u00e9ma ou au restaurant. Une fois ce budget d\u00e9fini, tenez-vous-y.\n <\/p>\n

    \n Les casinos en ligne proposent des outils pour vous aider \u00e0 respecter votre budget. Vous pouvez d\u00e9finir des limites de d\u00e9p\u00f4t quotidiennes, hebdomadaires ou mensuelles. Vous pouvez aussi fixer des limites de perte, c’est-\u00e0-dire le montant maximum que vous \u00eates pr\u00eat \u00e0 perdre avant que votre compte ne soit temporairement bloqu\u00e9. D’autres outils incluent des limites de temps de session, qui vous rappellent de faire une pause apr\u00e8s une certaine dur\u00e9e de jeu. L’utilisation de ces outils est une marque de maturit\u00e9 et de conscience.\n <\/p>\n

    \n Je pense que la connaissance du RTP<\/strong> et de la volatilit\u00e9<\/strong> est \u00e9galement une forme de gestion responsable. Savoir qu’une machine \u00e0 sous a une forte volatilit\u00e9 et donc des gains moins fr\u00e9quents vous aide \u00e0 ajuster vos attentes et \u00e0 ne pas vous d\u00e9courager trop vite, ou au contraire, \u00e0 ne pas vous attendre \u00e0 des gains constants. Comprendre ces concepts vous permet de choisir des jeux qui correspondent \u00e0 votre style et \u00e0 votre tol\u00e9rance au risque.\n <\/p>\n

    \n Si jamais vous sentez que le jeu devient une source de stress ou d’endettement, il est imp\u00e9ratif de demander de l’aide. De nombreuses organisations proposent un soutien aux personnes ayant des probl\u00e8mes de jeu. Les casinos s\u00e9rieux affichent des liens vers ces ressources. Ne restez pas seul face \u00e0 un probl\u00e8me.\n <\/p>\n

    Termes Cl\u00e9s \u00e0 Retenir<\/h3>\n
      \n
    • iGaming :<\/strong> L’industrie du jeu en ligne.<\/li>\n
    • RTP (Return To Player) :<\/strong> Taux de retour au joueur, pourcentage th\u00e9orique redistribu\u00e9 aux joueurs.<\/li>\n
    • Volatilit\u00e9 :<\/strong> Fr\u00e9quence et ampleur des gains sur une machine \u00e0 sous.<\/li>\n
    • Bonus :<\/strong> Avantage offert par le casino (argent suppl\u00e9mentaire, tours gratuits).<\/li>\n
    • Requis de mise (Wagering Requirement) :<\/strong> Montant \u00e0 miser avant de pouvoir retirer les gains issus d’un bonus.<\/li>\n
    • Tours gratuits (Free Spins) :<\/strong> Jeux offerts sur des machines \u00e0 sous.<\/li>\n
    • Dealer en direct (Live Dealer) :<\/strong> Un croupier r\u00e9el qui anime un jeu via vid\u00e9o.<\/li>\n
    • Jeu responsable :<\/strong> Pratiques de jeu qui visent \u00e0 pr\u00e9venir les probl\u00e8mes li\u00e9s au jeu.<\/li>\n<\/ul>\n

      L’Avenir du Jeu en Ligne : Ce Qui Nous Attend<\/h2>\n

      \n L’\u00e9volution constante du secteur de l’iGaming en 2025 promet des exp\u00e9riences encore plus captivantes. Nous allons probablement voir une int\u00e9gration plus pouss\u00e9e de la r\u00e9alit\u00e9 virtuelle<\/strong> (VR) et de la r\u00e9alit\u00e9 augment\u00e9e<\/strong> (AR) dans les jeux de casino. Imaginez vous promener dans un casino virtuel en VR, interagir avec d’autres joueurs et choisir votre table favorite. Cela pourrait sembler lointain, mais les premiers pas sont d\u00e9j\u00e0 faits. Les technologies se d\u00e9veloppent rapidement, rendant ces exp\u00e9riences de plus en plus accessibles.\n <\/p>\n

      \n L’intelligence artificielle continuera de jouer un r\u00f4le croissant. Elle sera utilis\u00e9e pour am\u00e9liorer la personnalisation des offres, pour cr\u00e9er des exp\u00e9riences de jeu plus adapt\u00e9es \u00e0 chaque utilisateur, et m\u00eame pour d\u00e9velopper de nouveaux types de jeux plus interactifs. L’IA peut aider \u00e0 analyser le comportement des joueurs pour anticiper leurs besoins et proposer des solutions sur mesure. C’est un peu comme avoir un assistant personnel d\u00e9di\u00e9 \u00e0 votre plaisir de jeu.\n <\/p>\n

      \n La s\u00e9curit\u00e9 des transactions et la protection des donn\u00e9es des joueurs restent des priorit\u00e9s absolues. Les casinos en ligne investissent massivement dans des technologies de cryptage avanc\u00e9es pour garantir que vos informations et votre argent sont en s\u00e9curit\u00e9. La blockchain<\/strong> pourrait \u00e9galement jouer un r\u00f4le croissant dans l’am\u00e9lioration de la transparence et de la s\u00e9curit\u00e9 des transactions. Je suis convaincu que la confiance est la base de toute relation r\u00e9ussie entre un joueur et un casino.\n <\/p>\n

      \n La r\u00e9gulation des march\u00e9s \u00e9volue aussi. Les gouvernements cherchent \u00e0 encadrer davantage l’industrie pour prot\u00e9ger les consommateurs et assurer une concurrence loyale. Cela signifie que vous pourriez voir des licences plus strictes et des v\u00e9rifications plus pouss\u00e9es, ce qui est une bonne chose pour la cr\u00e9dibilit\u00e9 du secteur.\n <\/p>\n

      \n Alors, qu’est-ce que tout cela signifie pour vous ? Cela signifie un avenir plein de promesses pour le jeu en ligne. Une \u00e8re o\u00f9 les jeux seront plus immersifs, plus personnalis\u00e9s et plus s\u00e9curis\u00e9s que jamais. Le plus important est de rester inform\u00e9 et de choisir des plateformes fiables qui mettent l’accent sur le jeu responsable. Comment comptez-vous explorer ces nouvelles possibilit\u00e9s dans votre pratique du jeu en ligne ?\n <\/p>\n<\/article>\n","protected":false},"excerpt":{"rendered":"

      L’\u00c9volution du Jeu en Ligne en 2025 : Ce Que Vous Devez Savoir L’univers du jeu en ligne, aussi connu sous le nom d’iGaming, conna\u00eet une croissance impressionnante. En 2025, cette industrie continue de se r\u00e9inventer, offrant des exp\u00e9riences toujours plus riches et accessibles. Pour vous, nouveaux venus ou joueurs occasionnels, comprendre ces \u00e9volutions est […]<\/p>\n","protected":false},"author":15,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[797],"tags":[],"class_list":["post-20979","post","type-post","status-publish","format-standard","hentry","category-bonuskong-casino"],"_links":{"self":[{"href":"https:\/\/bragitrade.com\/index.php\/wp-json\/wp\/v2\/posts\/20979"}],"collection":[{"href":"https:\/\/bragitrade.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bragitrade.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bragitrade.com\/index.php\/wp-json\/wp\/v2\/users\/15"}],"replies":[{"embeddable":true,"href":"https:\/\/bragitrade.com\/index.php\/wp-json\/wp\/v2\/comments?post=20979"}],"version-history":[{"count":2,"href":"https:\/\/bragitrade.com\/index.php\/wp-json\/wp\/v2\/posts\/20979\/revisions"}],"predecessor-version":[{"id":20981,"href":"https:\/\/bragitrade.com\/index.php\/wp-json\/wp\/v2\/posts\/20979\/revisions\/20981"}],"wp:attachment":[{"href":"https:\/\/bragitrade.com\/index.php\/wp-json\/wp\/v2\/media?parent=20979"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bragitrade.com\/index.php\/wp-json\/wp\/v2\/categories?post=20979"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bragitrade.com\/index.php\/wp-json\/wp\/v2\/tags?post=20979"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}