Version Description
- We've made our plugin better and more user-friendly!
- New: Gutenberg block
- New: Create multiple feeds with different configurations
- New: Embed your Instagram feeds using a shortcode anywhere you want
- New: Customize the colors
- New: Full-width layout
- New: Connect multiple Instagram accounts [PRO only]
- New: Masonry Layout [PRO only]
- New: Load More button [PRO only]
Download this release
Release Info
Developer | WPZOOM |
Plugin | ![]() |
Version | 2.0.0 |
Comparing to | |
See all releases |
Code changes from version 1.9.5 to 2.0.0
- .vscode/settings.json +2 -0
- assets/backend/img/user-avatar.jpg +0 -0
- class-wpzoom-instagram-block.php +157 -0
- class-wpzoom-instagram-image-uploader.php +17 -7
- class-wpzoom-instagram-widget-after-setup.php +90 -0
- class-wpzoom-instagram-widget-api.php +170 -339
- class-wpzoom-instagram-widget-display.php +802 -0
- class-wpzoom-instagram-widget-settings.php +2243 -47
- class-wpzoom-instagram-widget.php +745 -744
- css/admin-instagram-widget.css +0 -144
- dist/images/backend/user-avatar.jpg +0 -0
- {images → dist/images/frontend}/wpzoom-instagram-icons.svg +0 -0
- dist/scripts/backend/block.asset.php +1 -0
- dist/scripts/backend/block.js +2 -0
- dist/scripts/backend/index.asset.php +1 -0
- dist/scripts/backend/index.js +1 -0
- dist/scripts/frontend/block.asset.php +1 -0
- dist/scripts/frontend/block.js +1 -0
- dist/scripts/frontend/index.asset.php +1 -0
- dist/scripts/frontend/index.js +1 -0
- dist/scripts/frontend/preview.asset.php +1 -0
- dist/scripts/frontend/preview.js +1 -0
- dist/scripts/library/lazy.js +1 -0
- dist/scripts/library/magnific-popup.js +1 -0
- dist/scripts/library/swiper.js +1 -0
- dist/styles/backend/index-rtl.css +1 -0
- dist/styles/backend/index.asset.php +1 -0
- dist/styles/backend/index.css +1 -0
- dist/styles/frontend/block-rtl.css +1 -0
- dist/styles/frontend/block.asset.php +1 -0
- dist/styles/frontend/block.css +1 -0
- dist/styles/frontend/index-rtl.css +1 -0
- dist/styles/frontend/index.asset.php +1 -0
- dist/styles/frontend/index.css +1 -0
- dist/styles/frontend/preview-rtl.css +1 -0
- dist/styles/frontend/preview.asset.php +1 -0
- dist/styles/frontend/preview.css +1 -0
- {assets/frontend/magnific-popup → dist/styles/library}/magnific-popup.css +0 -0
- {assets/frontend/swiper → dist/styles/library}/swiper.css +0 -0
- images/wpzoom.png +0 -0
- instagram-widget-by-wpzoom.php +13 -8
- js/admin-instagram-widget.js +0 -169
- js/instagram-widget.js +0 -210
- languages/instagram-widget-by-wpzoom.pot +182 -290
- postcss.config.js +67 -0
- readme.txt +43 -14
- src/images/backend/user-avatar.jpg +0 -0
- src/images/frontend/wpzoom-instagram-icons.svg +9 -0
- src/scripts/backend/block.js +165 -0
- src/scripts/backend/custom-server-side-render/custom-server-side-render.js +208 -0
- src/scripts/backend/custom-server-side-render/index.js +50 -0
- src/scripts/backend/index.js +700 -0
- src/scripts/frontend/block.js +79 -0
- src/scripts/frontend/index.js +215 -0
- src/scripts/frontend/preview.js +11 -0
- js/jquery.lazy.min.js → src/scripts/library/lazy.js +0 -0
- assets/frontend/magnific-popup/jquery.magnific-popup.min.js → src/scripts/library/magnific-popup.js +0 -0
- {assets/frontend/swiper → src/scripts/library}/swiper.js +0 -0
- src/styles/backend/index.scss +2549 -0
- src/styles/frontend/block.scss +865 -0
- css/instagram-widget.css → src/styles/frontend/index.scss +372 -50
- src/styles/frontend/preview.scss +107 -0
- src/styles/library/magnific-popup.css +351 -0
- src/styles/library/swiper.css +13 -0
.vscode/settings.json
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
{
|
2 |
+
}
|
assets/backend/img/user-avatar.jpg
DELETED
Binary file
|
class-wpzoom-instagram-block.php
ADDED
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Exit if accessed directly.
|
4 |
+
*/
|
5 |
+
defined( 'ABSPATH' ) or die;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* WPZOOM Instagram Block class
|
9 |
+
*
|
10 |
+
* @package Wpzoom_Instagram_Block
|
11 |
+
*/
|
12 |
+
class Wpzoom_Instagram_Block {
|
13 |
+
/**
|
14 |
+
* @var WPZOOM_Instagram_Widget_Settings The reference to *Singleton* instance of this class
|
15 |
+
*
|
16 |
+
* @since 1.8.4
|
17 |
+
*/
|
18 |
+
private static $instance;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var Wpzoom_Instagram_Widget_Display
|
22 |
+
*/
|
23 |
+
protected $display;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Returns the *Singleton* instance of this class.
|
27 |
+
*
|
28 |
+
* @return WPZOOM_Instagram_Widget_Settings The *Singleton* instance.
|
29 |
+
*/
|
30 |
+
public static function get_instance() {
|
31 |
+
if ( null === self::$instance ) {
|
32 |
+
self::$instance = new self();
|
33 |
+
}
|
34 |
+
|
35 |
+
return self::$instance;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Constructor.
|
40 |
+
*/
|
41 |
+
public function __construct() {
|
42 |
+
$this->display = Wpzoom_Instagram_Widget_Display::getInstance();
|
43 |
+
|
44 |
+
add_action( 'init', array( $this, 'init' ) );
|
45 |
+
add_filter( 'block_categories_all', array( $this, 'block_categories' ), 10, 2 );
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Initialize the block.
|
50 |
+
*/
|
51 |
+
public function init() {
|
52 |
+
$script_asset_file = include( plugin_dir_path( __FILE__ ) . 'dist/scripts/backend/block.asset.php' );
|
53 |
+
$style_asset_file = include( plugin_dir_path( __FILE__ ) . 'dist/styles/frontend/block.asset.php' );
|
54 |
+
|
55 |
+
wp_register_script(
|
56 |
+
'wpz-insta_block-backend-script',
|
57 |
+
plugins_url( 'dist/scripts/backend/block.js', __FILE__ ),
|
58 |
+
$script_asset_file['dependencies'],
|
59 |
+
$script_asset_file['version']
|
60 |
+
);
|
61 |
+
|
62 |
+
wp_register_script(
|
63 |
+
'magnific-popup',
|
64 |
+
plugins_url( 'dist/scripts/library/magnific-popup.js', __FILE__ ),
|
65 |
+
array( 'jquery', 'underscore', 'wp-util' ),
|
66 |
+
filemtime( plugin_dir_path( __FILE__ ) . 'dist/scripts/library/magnific-popup.js' ),
|
67 |
+
true
|
68 |
+
);
|
69 |
+
|
70 |
+
wp_register_script(
|
71 |
+
'swiper-js',
|
72 |
+
plugins_url( 'dist/scripts/library/swiper.js', __FILE__ ),
|
73 |
+
array(),
|
74 |
+
'7.0.0-alpha.21'
|
75 |
+
);
|
76 |
+
|
77 |
+
wp_register_script(
|
78 |
+
'wpz-insta_block-frontend-script',
|
79 |
+
plugins_url( 'dist/scripts/frontend/block.js', __FILE__ ),
|
80 |
+
array( 'jquery', 'underscore', 'magnific-popup', 'swiper-js' ),
|
81 |
+
$script_asset_file['version']
|
82 |
+
);
|
83 |
+
|
84 |
+
wp_register_style(
|
85 |
+
'magnific-popup',
|
86 |
+
plugins_url( 'dist/styles/library/magnific-popup.css', __FILE__ ),
|
87 |
+
array( 'dashicons' ),
|
88 |
+
WPZOOM_INSTAGRAM_VERSION
|
89 |
+
);
|
90 |
+
|
91 |
+
wp_enqueue_style(
|
92 |
+
'swiper-css',
|
93 |
+
plugins_url( 'dist/styles/library/swiper.css', __FILE__ ),
|
94 |
+
array(),
|
95 |
+
'7.0.0-alpha.21'
|
96 |
+
);
|
97 |
+
|
98 |
+
wp_register_style(
|
99 |
+
'wpz-insta_block-frontend-style',
|
100 |
+
plugins_url( 'dist/styles/frontend/block.css', __FILE__ ),
|
101 |
+
array( 'magnific-popup', 'swiper-css' ),
|
102 |
+
$style_asset_file['version']
|
103 |
+
);
|
104 |
+
|
105 |
+
register_block_type(
|
106 |
+
'wpzoom/instagram-block',
|
107 |
+
array(
|
108 |
+
'api_version' => 2,
|
109 |
+
'category' => 'wpzoom-blocks',
|
110 |
+
'editor_script' => 'wpz-insta_block-backend-script',
|
111 |
+
'script' => 'wpz-insta_block-frontend-script',
|
112 |
+
'style' => 'wpz-insta_block-frontend-style',
|
113 |
+
'render_callback' => array( $this, 'render' ),
|
114 |
+
'attributes' => array(
|
115 |
+
'feed' => array(
|
116 |
+
'type' => 'integer',
|
117 |
+
'default' => -1,
|
118 |
+
),
|
119 |
+
),
|
120 |
+
)
|
121 |
+
);
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Add the WPZOOM block category if needed.
|
126 |
+
*/
|
127 |
+
public function block_categories( $categories ) {
|
128 |
+
if ( empty( $categories ) || ( ! empty( $categories ) && is_array( $categories ) && ! in_array( 'wpzoom-blocks', wp_list_pluck( $categories, 'slug' ) ) ) ) {
|
129 |
+
$categories = array_merge(
|
130 |
+
$categories,
|
131 |
+
array(
|
132 |
+
array(
|
133 |
+
'slug' => 'wpzoom-blocks',
|
134 |
+
'title' => esc_html__( 'WPZOOM - Blocks', 'instagram-widget-by-wpzoom' ),
|
135 |
+
),
|
136 |
+
)
|
137 |
+
);
|
138 |
+
}
|
139 |
+
|
140 |
+
return $categories;
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Render the block content.
|
145 |
+
*/
|
146 |
+
public function render( $block_attributes, $content ) {
|
147 |
+
$feed_id = isset( $block_attributes['feed'] ) ? intval( $block_attributes['feed'] ) : -1;
|
148 |
+
|
149 |
+
if ( $feed_id > -1 ) {
|
150 |
+
return $this->display->output_feed( $feed_id, false );
|
151 |
+
} else {
|
152 |
+
return wp_kses_post( __( '<p class="error"><strong>Please select a feed to display...</strong></p>', 'instagram-widget-by-wpzoom' ) );
|
153 |
+
}
|
154 |
+
}
|
155 |
+
}
|
156 |
+
|
157 |
+
Wpzoom_Instagram_Block::get_instance();
|
class-wpzoom-instagram-image-uploader.php
CHANGED
@@ -66,6 +66,13 @@ class WPZOOM_Instagram_Image_Uploader {
|
|
66 |
|
67 |
$image_src = wp_get_attachment_image_src( $attachment_id, self::get_image_size_name( $media_size ) );
|
68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
return ! empty( $image_src ) ? $image_src[0] : $media_url;
|
70 |
}
|
71 |
|
@@ -98,17 +105,20 @@ class WPZOOM_Instagram_Image_Uploader {
|
|
98 |
* @return float|int
|
99 |
*/
|
100 |
function get_transient_lifetime() {
|
101 |
-
$
|
|
|
102 |
|
103 |
$values = array(
|
104 |
-
|
105 |
-
|
106 |
-
|
|
|
|
|
107 |
);
|
108 |
$keys = array_keys( $values );
|
109 |
-
$type = in_array( $
|
110 |
|
111 |
-
return $type * $
|
112 |
}
|
113 |
|
114 |
/**
|
@@ -349,7 +359,7 @@ class WPZOOM_Instagram_Image_Uploader {
|
|
349 |
*
|
350 |
* @return int|string
|
351 |
*/
|
352 |
-
protected function get_best_size( $desired_width, $image_resolution = '
|
353 |
$size = 'thumbnail';
|
354 |
|
355 |
$sizes = array(
|
66 |
|
67 |
$image_src = wp_get_attachment_image_src( $attachment_id, self::get_image_size_name( $media_size ) );
|
68 |
|
69 |
+
return ! empty( $image_src ) ? $image_src[0] : $media_url;
|
70 |
+
} else {
|
71 |
+
$attachment_id = self::upload_image( $media_url, $media_id );
|
72 |
+
self::$instance->set_images_to_transient( $attachment_id, $media_id );
|
73 |
+
|
74 |
+
$image_src = wp_get_attachment_image_src( $attachment_id, self::get_image_size_name( $media_size ) );
|
75 |
+
|
76 |
return ! empty( $image_src ) ? $image_src[0] : $media_url;
|
77 |
}
|
78 |
|
105 |
* @return float|int
|
106 |
*/
|
107 |
function get_transient_lifetime() {
|
108 |
+
$interval = (int) WPZOOM_Instagram_Widget_Settings::get_feed_setting_value( get_the_ID(), 'check-new-posts-interval-number' );
|
109 |
+
$interval_suffix = (int) WPZOOM_Instagram_Widget_Settings::get_feed_setting_value( get_the_ID(), 'check-new-posts-interval-suffix' );
|
110 |
|
111 |
$values = array(
|
112 |
+
MINUTE_IN_SECONDS,
|
113 |
+
HOUR_IN_SECONDS,
|
114 |
+
DAY_IN_SECONDS,
|
115 |
+
WEEK_IN_SECONDS,
|
116 |
+
MONTH_IN_SECONDS,
|
117 |
);
|
118 |
$keys = array_keys( $values );
|
119 |
+
$type = in_array( $interval_suffix, $keys ) ? $values[ $interval_suffix ] : $values[2];
|
120 |
|
121 |
+
return $type * $interval;
|
122 |
}
|
123 |
|
124 |
/**
|
359 |
*
|
360 |
* @return int|string
|
361 |
*/
|
362 |
+
protected function get_best_size( $desired_width, $image_resolution = 'low_resolution' ) {
|
363 |
$size = 'thumbnail';
|
364 |
|
365 |
$sizes = array(
|
class-wpzoom-instagram-widget-after-setup.php
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Exit if accessed directly.
|
4 |
+
*/
|
5 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
6 |
+
exit;
|
7 |
+
}
|
8 |
+
|
9 |
+
class Wpzoom_Instagram_Widget_After_Setup {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var Wpzoom_Instagram_Widget_After_Setup The reference to *Singleton* instance of this class
|
13 |
+
*
|
14 |
+
* @since 1.8.4
|
15 |
+
*/
|
16 |
+
private static $instance;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Returns the *Singleton* instance of this class.
|
20 |
+
*
|
21 |
+
* @return Wpzoom_Instagram_Widget_After_Setup The *Singleton* instance.
|
22 |
+
*/
|
23 |
+
public static function get_instance() {
|
24 |
+
if ( null === self::$instance ) {
|
25 |
+
self::$instance = new self();
|
26 |
+
}
|
27 |
+
|
28 |
+
return self::$instance;
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Construct.
|
33 |
+
*/
|
34 |
+
public function __construct() {
|
35 |
+
|
36 |
+
add_action( 'init', array( $this, 'init' ) );
|
37 |
+
}
|
38 |
+
|
39 |
+
public function init() {
|
40 |
+
|
41 |
+
//Run only once
|
42 |
+
if ( get_option( 'wpzoom_run_only_once_01' ) ) {
|
43 |
+
return;
|
44 |
+
}
|
45 |
+
|
46 |
+
$getOldSettings = get_option( 'wpzoom-instagram-widget-settings' );
|
47 |
+
|
48 |
+
if( is_array( $getOldSettings ) && !empty( $getOldSettings ) ) {
|
49 |
+
|
50 |
+
$token = isset( $getOldSettings['basic-access-token'] ) ? $getOldSettings['basic-access-token'] : '';
|
51 |
+
$user_name = isset( $getOldSettings['user-info-fullname'] ) ? $getOldSettings['user-info-fullname'] : '';
|
52 |
+
$user_bio = isset( $getOldSettings['user-info-biography'] ) ? $getOldSettings['user-info-biography'] : '';
|
53 |
+
$user_image = isset( $getOldSettings['user-info-avatar'] ) ? $getOldSettings['user-info-avatar'] : '';
|
54 |
+
|
55 |
+
if ( ! empty( $token ) ) {
|
56 |
+
$info = Wpzoom_Instagram_Widget_API::get_basic_user_info_from_token( $token );
|
57 |
+
|
58 |
+
if ( false !== $info && is_object( $info ) && property_exists( $info, 'username' ) && property_exists( $info, 'account_type' ) ) {
|
59 |
+
$user = wp_strip_all_tags( $info->username );
|
60 |
+
$insert_post = wp_insert_post( array(
|
61 |
+
'post_title' => $user,
|
62 |
+
'post_type' => 'wpz-insta_user',
|
63 |
+
'post_status' => 'publish',
|
64 |
+
'post_content' => $user_bio
|
65 |
+
), true );
|
66 |
+
|
67 |
+
if ( ! is_wp_error( $insert_post ) ) {
|
68 |
+
update_post_meta( $insert_post, '_wpz-insta_token', $token );
|
69 |
+
update_post_meta( $insert_post, '_wpz-insta_token_expire', strtotime( '+60 days' ) );
|
70 |
+
update_post_meta( $insert_post, '_wpz-insta_account-type', sanitize_text_field( $info->account_type ) );
|
71 |
+
|
72 |
+
update_post_meta( $insert_post, '_wpz-insta_user_name', sanitize_text_field( $user_name ) );
|
73 |
+
update_post_meta( $insert_post, '_thumbnail_id', $user_image );
|
74 |
+
|
75 |
+
if ( property_exists( $info, 'profile_picture' ) && ! empty( $info->profile_picture ) ) {
|
76 |
+
WPZOOM_Instagram_Widget_Settings()->generate_featured_image( $info->profile_picture, $insert_post, $user );
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
}
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
add_option( 'wpzoom_run_only_once_01', true );
|
85 |
+
|
86 |
+
}
|
87 |
+
|
88 |
+
}
|
89 |
+
|
90 |
+
Wpzoom_Instagram_Widget_After_Setup::get_instance();
|
class-wpzoom-instagram-widget-api.php
CHANGED
@@ -44,16 +44,8 @@ class Wpzoom_Instagram_Widget_API {
|
|
44 |
* Class constructor
|
45 |
*/
|
46 |
protected function __construct() {
|
47 |
-
$
|
48 |
-
|
49 |
-
$this->request_type = ! empty( $options['request-type'] ) ? $options['request-type'] : '';
|
50 |
-
$this->access_token = ! empty( $options['basic-access-token'] ) ? $options['basic-access-token'] : '';
|
51 |
-
|
52 |
-
$this->username = ! empty( $options['username'] ) ? $options['username'] : '';
|
53 |
-
$this->transient_lifetime_type = ! empty( $options['transient-lifetime-type'] ) ? $options['transient-lifetime-type'] : 'days';
|
54 |
-
$this->transient_lifetime_value = ! empty( $options['transient-lifetime-value'] ) ? $options['transient-lifetime-value'] : 1;
|
55 |
-
$this->is_forced_timeout = ! empty( $options['is-forced-timeout'] ) ? wp_validate_boolean( $options['is-forced-timeout'] ) : false;
|
56 |
-
$this->request_timeout_value = ! empty( $options['request-timeout-value'] ) ? $options['request-timeout-value'] : 15;
|
57 |
|
58 |
if ( $this->is_forced_timeout && ! empty( $this->request_timeout_value ) ) {
|
59 |
$this->headers['timeout'] = $this->request_timeout_value;
|
@@ -84,6 +76,18 @@ class Wpzoom_Instagram_Widget_API {
|
|
84 |
return self::$instance;
|
85 |
}
|
86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
/**
|
88 |
* Register custom cron intervals
|
89 |
*
|
@@ -96,7 +100,7 @@ class Wpzoom_Instagram_Widget_API {
|
|
96 |
if ( ! empty( $this->access_token ) ) {
|
97 |
$schedules['before_access_token_expires'] = array(
|
98 |
'interval' => 5097600, // 59 days.
|
99 |
-
'display' =>
|
100 |
);
|
101 |
}
|
102 |
return $schedules;
|
@@ -119,68 +123,74 @@ class Wpzoom_Instagram_Widget_API {
|
|
119 |
* @return boolean
|
120 |
*/
|
121 |
public function execute_cron() {
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
}
|
156 |
-
|
157 |
-
$notice_message = '';
|
158 |
-
$user_id = $current_user->ID;
|
159 |
-
$hide_notices_url = wpzoom_instagram_get_notice_dismiss_url();
|
160 |
-
$settings_url = admin_url( 'options-general.php?page=wpzoom-instagram-widget' );
|
161 |
-
|
162 |
-
if ( 190 === $data->error->code ) {
|
163 |
-
// Error validating access token: Session has expired.
|
164 |
-
$notice_message = $data->error->message;
|
165 |
-
$notice_message .= '<a style="text-decoration: none" class="notice-dismiss" href="' . $hide_notices_url . '"></a>';
|
166 |
-
} elseif ( 10 === $data->error->code && ! self::is_access_token_valid( $this->access_token ) ) {
|
167 |
-
// Application does not have permission for this action.
|
168 |
-
// User need to generate new Access Token manually.
|
169 |
-
$notice_message = '<strong>' . __( 'Your Access Token for Instagram Widget has expired!', 'instagram-widget-by-wpzoom' ) . '</strong><br/>';
|
170 |
-
$notice_message .= sprintf( __( 'We cannot update access tokens automatically for Instagram private accounts. You need manually to generate a new access token, reauthorize here: %1$s.', 'instagram-widget-by-wpzoom' ), '<a href="' . esc_url( $settings_url ) . '">' . __( 'Instagram Widget Settings', 'instagram-widget-by-wpzoom' ) . '</a>' ) . ' ';
|
171 |
-
$notice_message .= '<a style="text-decoration: none" class="notice-dismiss" href="' . $hide_notices_url . '"></a>';
|
172 |
-
}
|
173 |
-
|
174 |
-
$stored_data['admin-notice-message'] = $notice_message;
|
175 |
-
|
176 |
-
// Update user meta to display admin notice.
|
177 |
-
update_user_meta( $user_id, 'wpzoom_instagram_admin_notice', false );
|
178 |
}
|
179 |
-
|
180 |
-
return update_option( WPZOOM_Instagram_Widget_Settings::get_instance()->get_option_name(), $stored_data );
|
181 |
}
|
182 |
-
|
183 |
-
return false;
|
184 |
}
|
185 |
|
186 |
public static function reset_cache( $sanitized_data ) {
|
@@ -209,37 +219,40 @@ class Wpzoom_Instagram_Widget_API {
|
|
209 |
'image-resolution',
|
210 |
'username',
|
211 |
'disable-video-thumbs',
|
|
|
|
|
212 |
)
|
213 |
);
|
214 |
|
215 |
$image_limit = $sliced['image-limit'];
|
216 |
$image_width = $sliced['image-width'];
|
217 |
-
$image_resolution = ! empty( $sliced['image-resolution'] ) ? $sliced['image-resolution'] : '
|
218 |
$injected_username = ! empty( $sliced['username'] ) ? $sliced['username'] : '';
|
219 |
$disable_video_thumbs = ! empty( $sliced['disable-video-thumbs'] );
|
|
|
|
|
220 |
|
221 |
$transient = 'zoom_instagram_is_configured';
|
222 |
|
223 |
-
|
224 |
-
|
225 |
-
if ( ! empty( $injected_username ) && 'without-access-token' === $this->request_type ) {
|
226 |
-
$injected_username = str_replace( '@', '', $injected_username );
|
227 |
-
$transient = $transient . '_' . $injected_username;
|
228 |
}
|
229 |
|
230 |
-
$
|
231 |
-
if ( false !== $data && is_object( $data ) && ! empty( $data->data ) ) {
|
232 |
-
return $this->processing_response_data( $data, $image_width, $image_resolution, $image_limit, $disable_video_thumbs );
|
233 |
-
}
|
234 |
|
235 |
-
|
236 |
-
|
|
|
|
|
|
|
|
|
237 |
|
238 |
if ( ! empty( $this->access_token ) ) {
|
239 |
$request_url = add_query_arg(
|
240 |
array(
|
241 |
'fields' => 'media_url,media_type,caption,username,permalink,thumbnail_url,timestamp,children{media_url,media_type,thumbnail_url}',
|
242 |
'access_token' => $this->access_token,
|
|
|
243 |
),
|
244 |
'https://graph.instagram.com/me/media'
|
245 |
);
|
@@ -247,7 +260,9 @@ class Wpzoom_Instagram_Widget_API {
|
|
247 |
$response = wp_safe_remote_get( $request_url, $this->headers );
|
248 |
|
249 |
if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
|
250 |
-
|
|
|
|
|
251 |
|
252 |
$error_data = $this->get_error( 'items-with-token-invalid-response' );
|
253 |
$this->errors->add( $error_data['code'], $error_data['message'] );
|
@@ -255,25 +270,23 @@ class Wpzoom_Instagram_Widget_API {
|
|
255 |
return false;
|
256 |
}
|
257 |
|
258 |
-
$
|
259 |
-
|
260 |
-
$data = $this->convert_items_to_old_structure( $data );
|
261 |
-
}
|
262 |
|
263 |
-
|
264 |
-
$data = $this->get_items_without_token( $external_username );
|
265 |
|
266 |
-
if (
|
267 |
-
|
268 |
-
|
269 |
-
return false;
|
270 |
}
|
271 |
}
|
272 |
|
273 |
if ( ! empty( $data->data ) ) {
|
274 |
-
|
|
|
|
|
275 |
} else {
|
276 |
-
|
|
|
|
|
277 |
|
278 |
$error_data = $this->get_error( 'items-with-token-invalid-data-structure' );
|
279 |
$this->errors->add( $error_data['code'], $error_data['message'] );
|
@@ -281,10 +294,10 @@ class Wpzoom_Instagram_Widget_API {
|
|
281 |
return false;
|
282 |
}
|
283 |
|
284 |
-
return
|
285 |
}
|
286 |
|
287 |
-
public function processing_response_data( $data, $image_width, $image_resolution, $image_limit, $disable_video_thumbs = false ) {
|
288 |
$result = array();
|
289 |
$username = '';
|
290 |
$defaults = array(
|
@@ -301,7 +314,7 @@ class Wpzoom_Instagram_Widget_API {
|
|
301 |
);
|
302 |
|
303 |
if ( empty( $image_resolution ) ) {
|
304 |
-
$image_resolution = '
|
305 |
}
|
306 |
|
307 |
foreach ( $data->data as $key => $item ) {
|
@@ -320,7 +333,7 @@ class Wpzoom_Instagram_Widget_API {
|
|
320 |
continue;
|
321 |
}
|
322 |
|
323 |
-
$best_size =
|
324 |
$image_url = $item->images->{$best_size}->url;
|
325 |
|
326 |
$regexPattern = '/-\d+[Xx]\d+\./';
|
@@ -348,6 +361,10 @@ class Wpzoom_Instagram_Widget_API {
|
|
348 |
'username' => $username,
|
349 |
);
|
350 |
|
|
|
|
|
|
|
|
|
351 |
return $result;
|
352 |
}
|
353 |
|
@@ -356,7 +373,7 @@ class Wpzoom_Instagram_Widget_API {
|
|
356 |
*
|
357 |
* @return string Image size for Instagram API
|
358 |
*/
|
359 |
-
|
360 |
$size = 'thumbnail';
|
361 |
$sizes = array(
|
362 |
'thumbnail' => 150,
|
@@ -402,60 +419,63 @@ class Wpzoom_Instagram_Widget_API {
|
|
402 |
return array(
|
403 |
'user-info-without-token' => array(
|
404 |
'code' => 'user-info-without-token',
|
405 |
-
'message' =>
|
406 |
),
|
407 |
'response-data-without-token-from-json-invalid-response' => array(
|
408 |
'code' => 'response-data-without-token-from-json-invalid-response',
|
409 |
-
'message' =>
|
410 |
),
|
411 |
'response-data-without-token-from-json-invalid-json-format' => array(
|
412 |
'code' => 'response-data-without-token-from-json-invalid-json-format',
|
413 |
-
'message' =>
|
414 |
),
|
415 |
'response-data-without-token-from-html-invalid-response' => array(
|
416 |
'code' => 'response-data-without-token-from-html-invalid-response',
|
417 |
-
'message' =>
|
418 |
),
|
419 |
'response-data-without-token-from-html-invalid-json-format' => array(
|
420 |
'code' => 'response-data-without-token-from-html-invalid-json-format',
|
421 |
-
'message' =>
|
422 |
),
|
423 |
'items-without-token-invalid-response' => array(
|
424 |
'code' => 'items-without-token-invalid-response',
|
425 |
-
'message' =>
|
426 |
),
|
427 |
'items-without-token-invalid-json-structure' => array(
|
428 |
'code' => 'items-without-token-invalid-json-structure',
|
429 |
-
'message' =>
|
430 |
),
|
431 |
'items-with-token-invalid-response' => array(
|
432 |
'code' => 'items-with-token-invalid-response',
|
433 |
-
'message' =>
|
434 |
),
|
435 |
'items-with-token-invalid-data-structure' => array(
|
436 |
'code' => 'items-with-token-invalid-data-structure',
|
437 |
-
'message' =>
|
438 |
),
|
439 |
'user-with-token-invalid-response' => array(
|
440 |
'code' => 'user-with-token-invalid-response',
|
441 |
-
'message' =>
|
442 |
),
|
443 |
'user-with-token-invalid-data-structure' => array(
|
444 |
'code' => 'user-with-token-invalid-data-structure',
|
445 |
-
'message' =>
|
446 |
),
|
447 |
|
448 |
);
|
449 |
}
|
450 |
|
451 |
-
function convert_items_to_old_structure( $data ) {
|
452 |
$converted = new stdClass();
|
453 |
$converted->data = array();
|
|
|
454 |
|
455 |
foreach ( $data->data as $key => $item ) {
|
|
|
|
|
456 |
$converted->data[] = (object) array(
|
457 |
'id' => $item->id,
|
458 |
-
'media_url' =>
|
459 |
'user' => (object) array(
|
460 |
'id' => null,
|
461 |
'fullname' => null,
|
@@ -464,17 +484,17 @@ class Wpzoom_Instagram_Widget_API {
|
|
464 |
),
|
465 |
'images' => (object) array(
|
466 |
'thumbnail' => (object) array(
|
467 |
-
'url' => $
|
468 |
'width' => 150,
|
469 |
'height' => 150,
|
470 |
),
|
471 |
'low_resolution' => (object) array(
|
472 |
-
'url' => $
|
473 |
'width' => 320,
|
474 |
'height' => 320,
|
475 |
),
|
476 |
'standard_resolution' => (object) array(
|
477 |
-
'url' => $
|
478 |
'width' => 640,
|
479 |
'height' => 640,
|
480 |
),
|
@@ -495,168 +515,21 @@ class Wpzoom_Instagram_Widget_API {
|
|
495 |
return $converted;
|
496 |
}
|
497 |
|
498 |
-
function get_items_without_token( $user ) {
|
499 |
-
$result = $this->get_response_without_token( $user );
|
500 |
-
|
501 |
-
if ( is_wp_error( $result ) ) {
|
502 |
-
$error_data = $this->get_error( 'items-without-token-invalid-response' );
|
503 |
-
$this->errors->add( $error_data['code'], $error_data['message'] );
|
504 |
-
|
505 |
-
return new WP_Error( $error_data['code'], $error_data['message'] );
|
506 |
-
}
|
507 |
-
|
508 |
-
if ( isset( $result->entry_data->ProfilePage[0]->graphql->user->edge_owner_to_timeline_media->edges ) ) {
|
509 |
-
$edges = $result->entry_data->ProfilePage[0]->graphql->user->edge_owner_to_timeline_media->edges;
|
510 |
-
} elseif ( isset( $result->graphql->user->edge_owner_to_timeline_media->edges ) ) {
|
511 |
-
$edges = $result->graphql->user->edge_owner_to_timeline_media->edges;
|
512 |
-
} else {
|
513 |
-
$error_data = $this->get_error( 'items-without-token-invalid-json-structure' );
|
514 |
-
$this->errors->add( $error_data['code'], $error_data['message'] );
|
515 |
-
|
516 |
-
return new WP_Error( $error_data['code'], $error_data['message'] );
|
517 |
-
}
|
518 |
-
|
519 |
-
$converted = new stdClass();
|
520 |
-
$converted->data = array();
|
521 |
-
foreach ( $edges as $edge ) {
|
522 |
-
$node = $edge->node;
|
523 |
-
|
524 |
-
$converted->data[] = (object) array(
|
525 |
-
'user' => (object) array(
|
526 |
-
'id' => $node->owner->id,
|
527 |
-
'fullname' => '',
|
528 |
-
'profile_picture' => '',
|
529 |
-
'username' => $node->owner->username,
|
530 |
-
),
|
531 |
-
'images' => (object) array(
|
532 |
-
'thumbnail' => (object) array(
|
533 |
-
'url' => $node->thumbnail_resources[0]->src,
|
534 |
-
'width' => $node->thumbnail_resources[0]->config_width,
|
535 |
-
'height' => $node->thumbnail_resources[0]->config_height,
|
536 |
-
),
|
537 |
-
'low_resolution' => (object) array(
|
538 |
-
'url' => $node->thumbnail_resources[2]->src,
|
539 |
-
'width' => $node->thumbnail_resources[2]->config_width,
|
540 |
-
'height' => $node->thumbnail_resources[2]->config_height,
|
541 |
-
),
|
542 |
-
'standard_resolution' => (object) array(
|
543 |
-
'url' => $node->thumbnail_resources[4]->src,
|
544 |
-
'width' => $node->thumbnail_resources[4]->config_width,
|
545 |
-
'height' => $node->thumbnail_resources[4]->config_height,
|
546 |
-
),
|
547 |
-
),
|
548 |
-
'type' => $this->get_media_type_without_token( $node->__typename ),
|
549 |
-
'likes' => isset( $node->edge_liked_by ) ? $node->edge_liked_by : 0,
|
550 |
-
'comments' => isset( $node->edge_media_to_comment ) ? $node->edge_media_to_comment : 0,
|
551 |
-
'created_time' => $node->taken_at_timestamp,
|
552 |
-
'link' => sprintf( 'https://www.instagram.com/p/%s/', $node->shortcode ),
|
553 |
-
'caption' => (object) array(
|
554 |
-
'text' => isset( $node->edge_media_to_caption->edges[0]->node->text ) ? $node->edge_media_to_caption->edges[0]->node->text : '',
|
555 |
-
),
|
556 |
-
);
|
557 |
-
}
|
558 |
-
|
559 |
-
return $converted;
|
560 |
-
}
|
561 |
-
|
562 |
-
function get_response_without_token( $user ) {
|
563 |
-
$user = trim( $user );
|
564 |
-
$url = 'https://instagram.com/' . str_replace( '@', '', $user );
|
565 |
-
|
566 |
-
$request = wp_safe_remote_get( $url, $this->headers );
|
567 |
-
|
568 |
-
if ( is_wp_error( $request ) || 200 != wp_remote_retrieve_response_code( $request ) ) {
|
569 |
-
$error_data = $this->get_error( 'response-data-without-token-from-html-invalid-response' );
|
570 |
-
$this->errors->add( $error_data['code'], $error_data['message'] );
|
571 |
-
|
572 |
-
$result = $this->get_response_without_token_from_json( $user );
|
573 |
-
|
574 |
-
if ( is_wp_error( $result ) ) {
|
575 |
-
return new WP_Error( 'invalid_response', __( 'Invalid response from Instagram', 'instagram-widget-by-wpzoom' ) );
|
576 |
-
} else {
|
577 |
-
return $result;
|
578 |
-
}
|
579 |
-
}
|
580 |
-
|
581 |
-
$body = wp_remote_retrieve_body( $request );
|
582 |
-
|
583 |
-
$doc = new DOMDocument();
|
584 |
-
|
585 |
-
@$doc->loadHTML( $body );
|
586 |
-
|
587 |
-
$script_tags = $doc->getElementsByTagName( 'script' );
|
588 |
-
|
589 |
-
$json = '';
|
590 |
-
|
591 |
-
foreach ( $script_tags as $script_tag ) {
|
592 |
-
if ( strpos( $script_tag->nodeValue, 'window._sharedData = ' ) !== false ) {
|
593 |
-
$json = $script_tag->nodeValue;
|
594 |
-
break;
|
595 |
-
}
|
596 |
-
}
|
597 |
-
|
598 |
-
$json = str_replace( array( 'window._sharedData = ', '};' ), array( '', '}' ), $json );
|
599 |
-
$result = json_decode( $json );
|
600 |
-
|
601 |
-
if ( empty( $result ) ) {
|
602 |
-
$error_data = $this->get_error( 'response-data-without-token-from-html-invalid-json-format' );
|
603 |
-
$this->errors->add( $error_data['code'], $error_data['message'] );
|
604 |
-
|
605 |
-
$result = $this->get_response_without_token_from_json( $user );
|
606 |
-
|
607 |
-
if ( is_wp_error( $result ) ) {
|
608 |
-
return new WP_Error( 'empty-json', __( 'Empty json decoded data.', 'instagram-widget-by-wpzoom' ) );
|
609 |
-
}
|
610 |
-
}
|
611 |
-
|
612 |
-
return $result;
|
613 |
-
}
|
614 |
-
|
615 |
-
function get_response_without_token_from_json( $user ) {
|
616 |
-
$user = trim( $user );
|
617 |
-
$url = 'https://instagram.com/' . str_replace( '@', '', $user ) . '/?__a=1';
|
618 |
-
|
619 |
-
$request = wp_safe_remote_get( $url, $this->headers );
|
620 |
-
|
621 |
-
if ( is_wp_error( $request ) || 200 != wp_remote_retrieve_response_code( $request ) ) {
|
622 |
-
$error_data = $this->get_error( 'response-data-without-token-from-json-invalid-response' );
|
623 |
-
$this->errors->add( $error_data['code'], $error_data['message'] );
|
624 |
-
|
625 |
-
return new WP_Error( $error_data['code'], $error_data['message'] );
|
626 |
-
}
|
627 |
-
|
628 |
-
$result = json_decode( wp_remote_retrieve_body( $request ) );
|
629 |
-
|
630 |
-
if ( empty( $result ) ) {
|
631 |
-
$error_data = $this->get_error( 'response-data-without-token-from-json-invalid-json-format' );
|
632 |
-
$this->errors->add( $error_data['code'], $error_data['message'] );
|
633 |
-
|
634 |
-
return new WP_Error( $error_data['code'], $error_data['message'] );
|
635 |
-
}
|
636 |
-
|
637 |
-
return $result;
|
638 |
-
}
|
639 |
-
|
640 |
-
function get_media_type_without_token( $media_type ) {
|
641 |
-
$media_types = array(
|
642 |
-
'GraphImage' => 'IMAGE',
|
643 |
-
'GraphSidecar' => 'CAROUSEL_ALBUM',
|
644 |
-
'GraphVideo' => 'VIDEO',
|
645 |
-
);
|
646 |
-
|
647 |
-
return array_key_exists( $media_type, $media_types ) ? $media_types[ $media_type ] : array_shift( $media_types );
|
648 |
-
}
|
649 |
-
|
650 |
function get_transient_lifetime() {
|
|
|
|
|
|
|
651 |
$values = array(
|
652 |
-
|
653 |
-
|
654 |
-
|
|
|
|
|
655 |
);
|
656 |
$keys = array_keys( $values );
|
657 |
-
$type = in_array( $
|
658 |
|
659 |
-
return $type * $
|
660 |
}
|
661 |
|
662 |
public function get_user_info( $injected_username = '' ) {
|
@@ -664,18 +537,10 @@ class Wpzoom_Instagram_Widget_API {
|
|
664 |
|
665 |
$injected_username = rtrim( $injected_username );
|
666 |
|
667 |
-
if ( ! empty( $injected_username ) && 'without-access-token' === $this->request_type ) {
|
668 |
-
$injected_username = str_replace( '@', '', $injected_username );
|
669 |
-
$transient = $transient . '_' . $injected_username;
|
670 |
-
}
|
671 |
-
|
672 |
if ( false !== ( $data = json_decode( get_transient( $transient ) ) ) && is_object( $data ) && ! empty( $data->data ) ) {
|
673 |
return $data;
|
674 |
}
|
675 |
|
676 |
-
$is_external_username = ! empty( $this->username ) || ! empty( $injected_username );
|
677 |
-
$external_username = ! empty( $injected_username ) ? $injected_username : $this->username;
|
678 |
-
|
679 |
if ( ! empty( $this->access_token ) ) {
|
680 |
$request_url = add_query_arg(
|
681 |
array(
|
@@ -700,16 +565,6 @@ class Wpzoom_Instagram_Widget_API {
|
|
700 |
$data = $this->convert_user_info_to_old_structure( $data );
|
701 |
}
|
702 |
|
703 |
-
if ( 'without-access-token' === $this->request_type && ! empty( $is_external_username ) ) {
|
704 |
-
$data = $this->get_user_info_without_token( $external_username );
|
705 |
-
|
706 |
-
if ( is_wp_error( $data ) ) {
|
707 |
-
set_transient( $transient, wp_json_encode( false ), MINUTE_IN_SECONDS );
|
708 |
-
|
709 |
-
return false;
|
710 |
-
}
|
711 |
-
}
|
712 |
-
|
713 |
if ( ! empty( $data->data ) ) {
|
714 |
set_transient( $transient, wp_json_encode( $data ), $this->get_transient_lifetime() );
|
715 |
} else {
|
@@ -724,6 +579,28 @@ class Wpzoom_Instagram_Widget_API {
|
|
724 |
return $data;
|
725 |
}
|
726 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
727 |
function convert_user_info_to_old_structure( $user_info ) {
|
728 |
$converted = new stdClass();
|
729 |
|
@@ -762,48 +639,6 @@ class Wpzoom_Instagram_Widget_API {
|
|
762 |
return $converted;
|
763 |
}
|
764 |
|
765 |
-
|
766 |
-
function get_user_info_without_token( $user ) {
|
767 |
-
$response = $this->get_response_without_token( $user );
|
768 |
-
|
769 |
-
if ( is_wp_error( $response ) ) {
|
770 |
-
$error_data = $this->get_error( 'user-info-without-token' );
|
771 |
-
$this->errors->add( $error_data['code'], $error_data['message'] );
|
772 |
-
|
773 |
-
return new WP_Error( $error_data['code'], $error_data['message'] );
|
774 |
-
}
|
775 |
-
|
776 |
-
if ( isset( $response->entry_data->ProfilePage[0]->graphql->user ) ) {
|
777 |
-
$user_info = $response->entry_data->ProfilePage[0]->graphql->user;
|
778 |
-
} elseif ( isset( $response->graphql->user ) ) {
|
779 |
-
$user_info = $response->graphql->user;
|
780 |
-
} else {
|
781 |
-
$error_data = $this->get_error( 'user-info-without-token' );
|
782 |
-
$this->errors->add( $error_data['code'], $error_data['message'] );
|
783 |
-
|
784 |
-
return new WP_Error( $error_data['code'], $error_data['message'] );
|
785 |
-
}
|
786 |
-
|
787 |
-
$converted = new stdClass();
|
788 |
-
|
789 |
-
$converted->data = (object) array(
|
790 |
-
'bio' => ! empty( $user_info->biography ) ? $user_info->biography : '',
|
791 |
-
'counts' => (object) array(
|
792 |
-
'followed_by' => ! empty( $user_info->edge_followed_by->count ) ? $user_info->edge_followed_by->count : 0,
|
793 |
-
'follows' => ! empty( $user_info->edge_follow->count ) ? $user_info->edge_follow->count : 0,
|
794 |
-
'media' => ! empty( $user_info->edge_owner_to_timeline_media->count ) ? $user_info->edge_owner_to_timeline_media->count : 0,
|
795 |
-
),
|
796 |
-
'full_name' => ! empty( $user_info->full_name ) ? $user_info->full_name : '',
|
797 |
-
'id' => ! empty( $user_info->id ) ? $user_info->id : '',
|
798 |
-
'is_business' => ! empty( $user_info->is_business_account ) ? $user_info->is_business_account : '',
|
799 |
-
'profile_picture' => ! empty( $user_info->profile_pic_url ) ? $user_info->profile_pic_url : '',
|
800 |
-
'username' => ! empty( $user_info->username ) ? $user_info->username : '',
|
801 |
-
'website' => ! empty( $user_info->external_url ) ? $user_info->external_url : '',
|
802 |
-
);
|
803 |
-
|
804 |
-
return $converted;
|
805 |
-
}
|
806 |
-
|
807 |
public function is_configured() {
|
808 |
$transient = 'zoom_instagram_is_configured';
|
809 |
|
@@ -821,11 +656,7 @@ class Wpzoom_Instagram_Widget_API {
|
|
821 |
}
|
822 |
}
|
823 |
|
824 |
-
|
825 |
-
$condition = $this->is_access_token_valid( $this->access_token, $this->request_type );
|
826 |
-
} else {
|
827 |
-
$condition = true;
|
828 |
-
}
|
829 |
|
830 |
if ( true === $condition ) {
|
831 |
set_transient( $transient, wp_json_encode( 'yes' ), DAY_IN_SECONDS );
|
@@ -841,7 +672,7 @@ class Wpzoom_Instagram_Widget_API {
|
|
841 |
/**
|
842 |
* Check if given access token is valid for Instagram Api.
|
843 |
*/
|
844 |
-
public static function is_access_token_valid( $access_token
|
845 |
if ( empty( $access_token ) ) {
|
846 |
return false;
|
847 |
}
|
44 |
* Class constructor
|
45 |
*/
|
46 |
protected function __construct() {
|
47 |
+
$this->is_forced_timeout = (bool) WPZOOM_Instagram_Widget_Settings::get_feed_setting_value( get_the_ID(), 'enable-request-timeout' );
|
48 |
+
$this->request_timeout_value = 15;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
|
50 |
if ( $this->is_forced_timeout && ! empty( $this->request_timeout_value ) ) {
|
51 |
$this->headers['timeout'] = $this->request_timeout_value;
|
76 |
return self::$instance;
|
77 |
}
|
78 |
|
79 |
+
/**
|
80 |
+
* Manually set the access token.
|
81 |
+
*
|
82 |
+
* @since 2.0.0
|
83 |
+
*
|
84 |
+
* @param string $token The access token to set.
|
85 |
+
* @return void
|
86 |
+
*/
|
87 |
+
public function set_access_token( $token ) {
|
88 |
+
$this->access_token = $token;
|
89 |
+
}
|
90 |
+
|
91 |
/**
|
92 |
* Register custom cron intervals
|
93 |
*
|
100 |
if ( ! empty( $this->access_token ) ) {
|
101 |
$schedules['before_access_token_expires'] = array(
|
102 |
'interval' => 5097600, // 59 days.
|
103 |
+
'display' => esc_attr__( 'Before Access Token Expires', 'instagram-widget-by-wpzoom' ),
|
104 |
);
|
105 |
}
|
106 |
return $schedules;
|
123 |
* @return boolean
|
124 |
*/
|
125 |
public function execute_cron() {
|
126 |
+
$all_users = get_posts( array(
|
127 |
+
'numberposts' => -1,
|
128 |
+
'post_type' => 'wpz-insta_user',
|
129 |
+
) );
|
130 |
+
|
131 |
+
if ( ! empty( $all_users ) && is_array( $all_users ) ) {
|
132 |
+
foreach ( $all_users as $user ) {
|
133 |
+
if ( $user instanceof WP_Post ) {
|
134 |
+
$user_name = get_the_title( $user );
|
135 |
+
$user_display = sprintf( '@%s', $user_name );
|
136 |
+
$token = get_post_meta( $user->ID, '_wpz-insta_token', true );
|
137 |
+
|
138 |
+
if ( false !== $token && ! empty( $token ) ) {
|
139 |
+
$request_url = add_query_arg(
|
140 |
+
array(
|
141 |
+
'grant_type' => 'ig_refresh_token',
|
142 |
+
'access_token' => $token,
|
143 |
+
),
|
144 |
+
'https://graph.instagram.com/refresh_access_token'
|
145 |
+
);
|
146 |
+
|
147 |
+
$response = wp_safe_remote_get( $request_url, $this->headers );
|
148 |
+
$response_code = wp_remote_retrieve_response_code( $response );
|
149 |
+
|
150 |
+
if ( ! is_wp_error( $response ) ) {
|
151 |
+
$body = wp_remote_retrieve_body( $response );
|
152 |
+
$data = json_decode( $body );
|
153 |
+
}
|
154 |
+
|
155 |
+
if ( 200 === $response_code ) {
|
156 |
+
$date_format = get_option( 'date_format' );
|
157 |
+
$time_format = get_option( 'time_format' );
|
158 |
+
$notice_status = 'success';
|
159 |
+
$notice_message = sprintf( __( '<strong>WPZOOM Instagram Widget:</strong> The Instagram Access Token was refreshed automatically on %1$s at %2$s for the account <em>%3$s</em>.', 'instagram-widget-by-wpzoom' ), date( $date_format ), date( $time_format ), esc_html( $user_display ) );
|
160 |
+
|
161 |
+
update_post_meta( $user->ID, '_wpz-insta_token', $data->access_token );
|
162 |
+
update_post_meta( $user->ID, '_wpz-insta_token_expire', strtotime( '+60 days' ) );
|
163 |
+
} else {
|
164 |
+
if ( ! isset( $data->error ) ) {
|
165 |
+
error_log( __( 'Something wrong! Doesn\'t isset $data->error.', 'instagram-widget-by-wpzoom' ) );
|
166 |
+
return false;
|
167 |
+
} else {
|
168 |
+
error_log( $data->error->error_user_msg );
|
169 |
+
}
|
170 |
+
|
171 |
+
$notice_status = 'error';
|
172 |
+
$notice_message = '';
|
173 |
+
$settings_url = admin_url( 'edit.php?post_type=wpz-insta_user' );
|
174 |
+
|
175 |
+
if ( 190 === $data->error->code ) {
|
176 |
+
// Error validating access token: Session has expired.
|
177 |
+
$notice_message = wp_kses_post( __( '<strong>WPZOOM Instagram Widget:</strong> ', 'instagram-widget-by-wpzoom' ) ) . $data->error->message;
|
178 |
+
} elseif ( 10 === $data->error->code && ! self::is_access_token_valid( $token ) ) {
|
179 |
+
// Application does not have permission for this action.
|
180 |
+
// User need to generate new Access Token manually.
|
181 |
+
$notice_message = sprintf( __( '<strong>WPZOOM Instagram Widget:</strong> The Access Token for the account <em>%1$s</em> has expired!<br/>', 'instagram-widget-by-wpzoom' ), $user_display );
|
182 |
+
$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' ), '<a href="' . esc_url( $settings_url ) . '">' . __( 'Instagram Widget Settings', 'instagram-widget-by-wpzoom' ) . '</a>' );
|
183 |
+
}
|
184 |
+
}
|
185 |
+
|
186 |
+
update_option(
|
187 |
+
'_wpz-insta_cron-result',
|
188 |
+
array( $user->ID => array( 'status' => $notice_status, 'message' => $notice_message ) ) + (array) get_option( '_wpz-insta_cron-result', array() )
|
189 |
+
);
|
190 |
+
}
|
191 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
}
|
|
|
|
|
193 |
}
|
|
|
|
|
194 |
}
|
195 |
|
196 |
public static function reset_cache( $sanitized_data ) {
|
219 |
'image-resolution',
|
220 |
'username',
|
221 |
'disable-video-thumbs',
|
222 |
+
'include-pagination',
|
223 |
+
'bypass-transient',
|
224 |
)
|
225 |
);
|
226 |
|
227 |
$image_limit = $sliced['image-limit'];
|
228 |
$image_width = $sliced['image-width'];
|
229 |
+
$image_resolution = ! empty( $sliced['image-resolution'] ) ? $sliced['image-resolution'] : 'low_resolution';
|
230 |
$injected_username = ! empty( $sliced['username'] ) ? $sliced['username'] : '';
|
231 |
$disable_video_thumbs = ! empty( $sliced['disable-video-thumbs'] );
|
232 |
+
$include_pagination = ! empty( $sliced['include-pagination'] );
|
233 |
+
$bypass_transient = ! empty( $sliced['bypass-transient'] );
|
234 |
|
235 |
$transient = 'zoom_instagram_is_configured';
|
236 |
|
237 |
+
if ( ! empty( $this->access_token ) ) {
|
238 |
+
$transient = $transient . '_' . $this->access_token;
|
|
|
|
|
|
|
239 |
}
|
240 |
|
241 |
+
$injected_username = trim( $injected_username );
|
|
|
|
|
|
|
242 |
|
243 |
+
if ( ! $bypass_transient ) {
|
244 |
+
$data = json_decode( get_transient( $transient ) );
|
245 |
+
if ( false !== $data && is_object( $data ) && ! empty( $data->data ) ) {
|
246 |
+
return self::processing_response_data( $data, $image_width, $image_resolution, $image_limit, $disable_video_thumbs, $include_pagination );
|
247 |
+
}
|
248 |
+
}
|
249 |
|
250 |
if ( ! empty( $this->access_token ) ) {
|
251 |
$request_url = add_query_arg(
|
252 |
array(
|
253 |
'fields' => 'media_url,media_type,caption,username,permalink,thumbnail_url,timestamp,children{media_url,media_type,thumbnail_url}',
|
254 |
'access_token' => $this->access_token,
|
255 |
+
'limit' => $image_limit,
|
256 |
),
|
257 |
'https://graph.instagram.com/me/media'
|
258 |
);
|
260 |
$response = wp_safe_remote_get( $request_url, $this->headers );
|
261 |
|
262 |
if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
|
263 |
+
if ( ! $bypass_transient ) {
|
264 |
+
set_transient( $transient, wp_json_encode( false ), MINUTE_IN_SECONDS );
|
265 |
+
}
|
266 |
|
267 |
$error_data = $this->get_error( 'items-with-token-invalid-response' );
|
268 |
$this->errors->add( $error_data['code'], $error_data['message'] );
|
270 |
return false;
|
271 |
}
|
272 |
|
273 |
+
$raw_data = json_decode( wp_remote_retrieve_body( $response ) );
|
|
|
|
|
|
|
274 |
|
275 |
+
$data = self::convert_items_to_old_structure( $raw_data, $bypass_transient );
|
|
|
276 |
|
277 |
+
if ( $include_pagination && property_exists( $raw_data, 'paging' ) ) {
|
278 |
+
$data->paging = $raw_data->paging;
|
|
|
|
|
279 |
}
|
280 |
}
|
281 |
|
282 |
if ( ! empty( $data->data ) ) {
|
283 |
+
if ( ! $bypass_transient ) {
|
284 |
+
set_transient( $transient, wp_json_encode( $data ), $this->get_transient_lifetime() );
|
285 |
+
}
|
286 |
} else {
|
287 |
+
if ( ! $bypass_transient ) {
|
288 |
+
set_transient( $transient, wp_json_encode( false ), MINUTE_IN_SECONDS );
|
289 |
+
}
|
290 |
|
291 |
$error_data = $this->get_error( 'items-with-token-invalid-data-structure' );
|
292 |
$this->errors->add( $error_data['code'], $error_data['message'] );
|
294 |
return false;
|
295 |
}
|
296 |
|
297 |
+
return self::processing_response_data( $data, $image_width, $image_resolution, $image_limit, $disable_video_thumbs, $include_pagination );
|
298 |
}
|
299 |
|
300 |
+
public static function processing_response_data( $data, $image_width, $image_resolution, $image_limit, $disable_video_thumbs = false, $include_pagination = false ) {
|
301 |
$result = array();
|
302 |
$username = '';
|
303 |
$defaults = array(
|
314 |
);
|
315 |
|
316 |
if ( empty( $image_resolution ) ) {
|
317 |
+
$image_resolution = 'low_resolution';
|
318 |
}
|
319 |
|
320 |
foreach ( $data->data as $key => $item ) {
|
333 |
continue;
|
334 |
}
|
335 |
|
336 |
+
$best_size = self::get_best_size( $image_width, $image_resolution );
|
337 |
$image_url = $item->images->{$best_size}->url;
|
338 |
|
339 |
$regexPattern = '/-\d+[Xx]\d+\./';
|
361 |
'username' => $username,
|
362 |
);
|
363 |
|
364 |
+
if ( $include_pagination && property_exists( $data, 'paging' ) ) {
|
365 |
+
$result['paging'] = $data->paging;
|
366 |
+
}
|
367 |
+
|
368 |
return $result;
|
369 |
}
|
370 |
|
373 |
*
|
374 |
* @return string Image size for Instagram API
|
375 |
*/
|
376 |
+
public static function get_best_size( $desired_width, $image_resolution = 'low_resolution' ) {
|
377 |
$size = 'thumbnail';
|
378 |
$sizes = array(
|
379 |
'thumbnail' => 150,
|
419 |
return array(
|
420 |
'user-info-without-token' => array(
|
421 |
'code' => 'user-info-without-token',
|
422 |
+
'message' => esc_html__( 'Empty json user info from Public Feed.', 'instagram-widget-by-wpzoom' ),
|
423 |
),
|
424 |
'response-data-without-token-from-json-invalid-response' => array(
|
425 |
'code' => 'response-data-without-token-from-json-invalid-response',
|
426 |
+
'message' => esc_html__( 'The request from the Public Feed failed. Invalid server response from Public JSON API url.', 'instagram-widget-by-wpzoom' ),
|
427 |
),
|
428 |
'response-data-without-token-from-json-invalid-json-format' => array(
|
429 |
'code' => 'response-data-without-token-from-json-invalid-json-format',
|
430 |
+
'message' => esc_html__( 'The request from the Public Feed failed. Invalid JSON format from Public JSON API url.', 'instagram-widget-by-wpzoom' ),
|
431 |
),
|
432 |
'response-data-without-token-from-html-invalid-response' => array(
|
433 |
'code' => 'response-data-without-token-from-html-invalid-response',
|
434 |
+
'message' => esc_html__( 'The request from the Public Feed failed. Check username.', 'instagram-widget-by-wpzoom' ),
|
435 |
),
|
436 |
'response-data-without-token-from-html-invalid-json-format' => array(
|
437 |
'code' => 'response-data-without-token-from-html-invalid-json-format',
|
438 |
+
'message' => esc_html__( 'The request from the Public Feed failed. Invalid JSON format from parsed html body.', 'instagram-widget-by-wpzoom' ),
|
439 |
),
|
440 |
'items-without-token-invalid-response' => array(
|
441 |
'code' => 'items-without-token-invalid-response',
|
442 |
+
'message' => esc_html__( 'Get items from the Public Feed failed. Invalid response.', 'instagram-widget-by-wpzoom' ),
|
443 |
),
|
444 |
'items-without-token-invalid-json-structure' => array(
|
445 |
'code' => 'items-without-token-invalid-json-structure',
|
446 |
+
'message' => esc_html__( 'Get items from the Public Feed failed. Malformed data structure.', 'instagram-widget-by-wpzoom' ),
|
447 |
),
|
448 |
'items-with-token-invalid-response' => array(
|
449 |
'code' => 'items-with-token-invalid-response',
|
450 |
+
'message' => esc_html__( 'Geting items from the Instagram API Feed failed. Invalid response.', 'instagram-widget-by-wpzoom' ),
|
451 |
),
|
452 |
'items-with-token-invalid-data-structure' => array(
|
453 |
'code' => 'items-with-token-invalid-data-structure',
|
454 |
+
'message' => esc_html__( 'Get items from the Instagram API Feed failed. Malformed data structure.', 'instagram-widget-by-wpzoom' ),
|
455 |
),
|
456 |
'user-with-token-invalid-response' => array(
|
457 |
'code' => 'user-with-token-invalid-response',
|
458 |
+
'message' => esc_html__( 'Get user data from the Instagram API Feed failed. Invalid response.', 'instagram-widget-by-wpzoom' ),
|
459 |
),
|
460 |
'user-with-token-invalid-data-structure' => array(
|
461 |
'code' => 'user-with-token-invalid-data-structure',
|
462 |
+
'message' => esc_html__( 'Get user data from the Instagram API Feed failed. Malformed data structure.', 'instagram-widget-by-wpzoom' ),
|
463 |
),
|
464 |
|
465 |
);
|
466 |
}
|
467 |
|
468 |
+
public static function convert_items_to_old_structure( $data, $preview = false ) {
|
469 |
$converted = new stdClass();
|
470 |
$converted->data = array();
|
471 |
+
$image_uploader = WPZOOM_Instagram_Image_Uploader::getInstance();
|
472 |
|
473 |
foreach ( $data->data as $key => $item ) {
|
474 |
+
$media_url = 'VIDEO' === $item->media_type && property_exists( $item, 'thumbnail_url' ) && ! empty( $item->thumbnail_url ) ? $item->thumbnail_url : $item->media_url;
|
475 |
+
|
476 |
$converted->data[] = (object) array(
|
477 |
'id' => $item->id,
|
478 |
+
'media_url' => $media_url,
|
479 |
'user' => (object) array(
|
480 |
'id' => null,
|
481 |
'fullname' => null,
|
484 |
),
|
485 |
'images' => (object) array(
|
486 |
'thumbnail' => (object) array(
|
487 |
+
'url' => $preview ? $media_url : $image_uploader->get_image( 'thumbnail', $media_url, $item->id ),
|
488 |
'width' => 150,
|
489 |
'height' => 150,
|
490 |
),
|
491 |
'low_resolution' => (object) array(
|
492 |
+
'url' => $preview ? $media_url : $image_uploader->get_image( 'low_resolution', $media_url, $item->id ),
|
493 |
'width' => 320,
|
494 |
'height' => 320,
|
495 |
),
|
496 |
'standard_resolution' => (object) array(
|
497 |
+
'url' => $preview ? $media_url : $image_uploader->get_image( 'standard_resolution', $media_url, $item->id ),
|
498 |
'width' => 640,
|
499 |
'height' => 640,
|
500 |
),
|
515 |
return $converted;
|
516 |
}
|
517 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
518 |
function get_transient_lifetime() {
|
519 |
+
$interval = (int) WPZOOM_Instagram_Widget_Settings::get_feed_setting_value( get_the_ID(), 'check-new-posts-interval-number' );
|
520 |
+
$interval_suffix = (int) WPZOOM_Instagram_Widget_Settings::get_feed_setting_value( get_the_ID(), 'check-new-posts-interval-suffix' );
|
521 |
+
|
522 |
$values = array(
|
523 |
+
MINUTE_IN_SECONDS,
|
524 |
+
HOUR_IN_SECONDS,
|
525 |
+
DAY_IN_SECONDS,
|
526 |
+
WEEK_IN_SECONDS,
|
527 |
+
MONTH_IN_SECONDS,
|
528 |
);
|
529 |
$keys = array_keys( $values );
|
530 |
+
$type = in_array( $interval_suffix, $keys ) ? $values[ $interval_suffix ] : $values[2];
|
531 |
|
532 |
+
return $type * $interval;
|
533 |
}
|
534 |
|
535 |
public function get_user_info( $injected_username = '' ) {
|
537 |
|
538 |
$injected_username = rtrim( $injected_username );
|
539 |
|
|
|
|
|
|
|
|
|
|
|
540 |
if ( false !== ( $data = json_decode( get_transient( $transient ) ) ) && is_object( $data ) && ! empty( $data->data ) ) {
|
541 |
return $data;
|
542 |
}
|
543 |
|
|
|
|
|
|
|
544 |
if ( ! empty( $this->access_token ) ) {
|
545 |
$request_url = add_query_arg(
|
546 |
array(
|
565 |
$data = $this->convert_user_info_to_old_structure( $data );
|
566 |
}
|
567 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
568 |
if ( ! empty( $data->data ) ) {
|
569 |
set_transient( $transient, wp_json_encode( $data ), $this->get_transient_lifetime() );
|
570 |
} else {
|
579 |
return $data;
|
580 |
}
|
581 |
|
582 |
+
public static function get_basic_user_info_from_token( $access_token ) {
|
583 |
+
$output = false;
|
584 |
+
|
585 |
+
if ( ! empty( $access_token ) ) {
|
586 |
+
$request_url = add_query_arg(
|
587 |
+
array(
|
588 |
+
'access_token' => $access_token,
|
589 |
+
'fields' => 'account_type,username,profile_picture',
|
590 |
+
),
|
591 |
+
'https://graph.instagram.com/me'
|
592 |
+
);
|
593 |
+
|
594 |
+
$response = wp_safe_remote_get( $request_url );
|
595 |
+
|
596 |
+
if ( ! is_wp_error( $response ) && 200 == wp_remote_retrieve_response_code( $response ) ) {
|
597 |
+
$output = json_decode( wp_remote_retrieve_body( $response ) );
|
598 |
+
}
|
599 |
+
}
|
600 |
+
|
601 |
+
return $output;
|
602 |
+
}
|
603 |
+
|
604 |
function convert_user_info_to_old_structure( $user_info ) {
|
605 |
$converted = new stdClass();
|
606 |
|
639 |
return $converted;
|
640 |
}
|
641 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
642 |
public function is_configured() {
|
643 |
$transient = 'zoom_instagram_is_configured';
|
644 |
|
656 |
}
|
657 |
}
|
658 |
|
659 |
+
$condition = $this->is_access_token_valid( $this->access_token );
|
|
|
|
|
|
|
|
|
660 |
|
661 |
if ( true === $condition ) {
|
662 |
set_transient( $transient, wp_json_encode( 'yes' ), DAY_IN_SECONDS );
|
672 |
/**
|
673 |
* Check if given access token is valid for Instagram Api.
|
674 |
*/
|
675 |
+
public static function is_access_token_valid( $access_token ) {
|
676 |
if ( empty( $access_token ) ) {
|
677 |
return false;
|
678 |
}
|
class-wpzoom-instagram-widget-display.php
ADDED
@@ -0,0 +1,802 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|