PluginProbe
Parse.ly / 3.24.3
Parse.ly v3.24.3
3.24.3 3.24.2 3.24.1 3.24.0 3.23.7 3.23.6 3.23.5 3.23.4 3.23.3 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.17.0 3.18.0 3.18.1 3.19.0 3.19.1 3.19.2 3.19.3 3.2.0 3.2.1 3.20.0 3.20.1 All 107 releases
← All changes | wp-parsely.php +281 -92 3.2.0 → 3.24.3 View file →
@@ -1,18 +1,18 @@
1 1 <?php
2 2 /**
3 3 * Parse.ly
4 4 *
5 - * @package Parsely\wp-parsely
6 - * @author Parse.ly
7 - * @copyright 2012 Parse.ly
8 - * @license GPL-2.0-or-later
5 + * @package Parsely
6 + * @author Parse.ly
7 + * @copyright 2012 Parse.ly
8 + * @license GPL-2.0-or-later
9 9 *
10 10 * @wordpress-plugin
11 11 * Plugin Name: Parse.ly
12 - * Plugin URI: https://www.parse.ly/help/integration/wordpress
12 + * Plugin URI: https://docs.parse.ly/wordpress
13 13 * Description: This plugin makes it a snap to add Parse.ly tracking code and metadata to your WordPress blog.
14 - * Version: 3.2.0
14 + * Version: 3.24.3
15 15 * Author: Parse.ly
16 16 * Author URI: https://www.parse.ly
17 17 * Text Domain: wp-parsely
18 18 * License: GPL-2.0-or-later
@@ -17,10 +17,10 @@
17 17 * Text Domain: wp-parsely
18 18 * License: GPL-2.0-or-later
19 19 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
20 20 * GitHub Plugin URI: https://github.com/Parsely/wp-parsely
21 - * Requires PHP: 7.1
22 - * Requires WP: 5.0.0
21 + * Requires PHP: 7.4
22 + * Requires WP: 6.1.0
23 23 */
24 24
25 25 declare(strict_types=1);
26 26
@@ -25,135 +25,192 @@
25 25 declare(strict_types=1);
26 26
27 27 namespace Parsely;
28 28
29 -use Parsely\Endpoints\Related_API_Proxy;
29 +use Parsely\Content_Helper\Dashboard_Widget;
30 +use Parsely\Content_Helper\Editor_Sidebar;
31 +use Parsely\Content_Helper\Post_List_Stats;
30 32 use Parsely\Endpoints\GraphQL_Metadata;
31 33 use Parsely\Endpoints\Rest_Metadata;
34 +use Parsely\Headline_Testing;
32 35 use Parsely\Integrations\Amp;
33 -use Parsely\Integrations\Facebook_Instant_Articles;
34 36 use Parsely\Integrations\Google_Web_Stories;
35 37 use Parsely\Integrations\Integrations;
36 -use Parsely\RemoteAPI\Cached_Proxy;
37 -use Parsely\RemoteAPI\Related_Proxy;
38 -use Parsely\RemoteAPI\WordPress_Cache;
39 38 use Parsely\UI\Admin_Bar;
40 39 use Parsely\UI\Admin_Warning;
40 +use Parsely\UI\Dashboard_Page;
41 +use Parsely\UI\Metadata_Renderer;
42 +use Parsely\UI\Network_Admin_Sites_List;
41 43 use Parsely\UI\Plugins_Actions;
42 -use Parsely\UI\Network_Admin_Sites_List;
43 44 use Parsely\UI\Recommended_Widget;
44 45 use Parsely\UI\Row_Actions;
45 46 use Parsely\UI\Settings_Page;
47 +use Parsely\UI\Site_Health;
46 48
47 49 if ( class_exists( Parsely::class ) ) {
48 50 return;
49 51 }
50 52
51 -const PARSELY_VERSION = '3.2.0';
52 -const PARSELY_FILE = __FILE__;
53 +const PARSELY_VERSION = '3.24.3';
54 +const PARSELY_FILE = __FILE__;
55 +const PARSELY_DATA_SCHEMA_VERSION = '1';
56 +const PARSELY_CACHE_GROUP = 'wp-parsely';
53 57
54 -require __DIR__ . '/src/class-parsely.php';
55 -require __DIR__ . '/src/class-scripts.php';
56 -require __DIR__ . '/src/class-dashboard-link.php';
57 -require __DIR__ . '/src/UI/class-admin-bar.php';
58 -require __DIR__ . '/src/Endpoints/class-metadata-endpoint.php';
59 -require __DIR__ . '/src/Endpoints/class-graphql-metadata.php';
58 +if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
59 + require_once __DIR__ . '/vendor/autoload.php';
60 +}
60 61
62 +// Load Telemetry classes.
63 +require_once __DIR__ . '/src/Telemetry/telemetry-init.php';
64 +
65 +/**
66 + * Gets the Parsely object.
67 + *
68 + * @since 3.19.0
69 + *
70 + * @return Parsely The Parsely object.
71 + */
72 +function get_parsely(): Parsely {
73 + if ( ! isset( $GLOBALS['parsely'] ) ) {
74 + $GLOBALS['parsely'] = new Parsely();
75 + }
76 +
77 + return $GLOBALS['parsely'];
78 +}
79 +
61 80 add_action( 'plugins_loaded', __NAMESPACE__ . '\\parsely_initialize_plugin' );
62 81 /**
63 - * Register the basic classes to initialize the plugin.
64 - *
65 - * @return void
82 + * Registers the basic classes to initialize the plugin.
66 83 */
67 84 function parsely_initialize_plugin(): void {
68 - $GLOBALS['parsely'] = new Parsely();
69 - $GLOBALS['parsely']->run();
85 + $parsely = get_parsely();
86 + $parsely->run();
70 87
71 88 if ( class_exists( 'WPGraphQL' ) ) {
72 - $graphql = new GraphQL_Metadata( $GLOBALS['parsely'] );
89 + $graphql = new GraphQL_Metadata( $parsely );
73 90 $graphql->run();
74 91 }
75 92
76 - $scripts = new Scripts( $GLOBALS['parsely'] );
93 + $scripts = new Scripts( $parsely );
77 94 $scripts->run();
78 95
79 - $admin_bar = new Admin_Bar( $GLOBALS['parsely'] );
96 + $admin_bar = new Admin_Bar( $parsely );
80 97 $admin_bar->run();
98 +
99 + $metadata_renderer = new Metadata_Renderer( $parsely );
100 + $metadata_renderer->run();
101 +
102 + // Initialize Headline Testing feature.
103 + $headline_testing = new Headline_Testing( $parsely );
104 + $headline_testing->run();
81 105 }
82 106
83 -require __DIR__ . '/src/UI/class-admin-warning.php';
84 -require __DIR__ . '/src/UI/class-plugins-actions.php';
85 -require __DIR__ . '/src/UI/class-row-actions.php';
107 +add_action( 'admin_init', __NAMESPACE__ . '\\parsely_admin_init_register' );
108 +/**
109 + * Registers the Parse.ly wp-admin warnings, plugin actions and row actions.
110 + */
111 +function parsely_admin_init_register(): void {
112 + $parsely = get_parsely();
86 113
87 -add_action( 'admin_init', __NAMESPACE__ . '\\parsely_admin_init_register' );
114 + ( new Admin_Warning( $parsely ) )->run();
115 + ( new Plugins_Actions() )->run();
116 + ( new Row_Actions( $parsely ) )->run();
117 + ( new Post_List_Stats( $parsely ) )->run();
118 + ( new Site_Health( $parsely ) )->run();
119 + ( new Dashboard_Widget( $parsely ) )->run();
120 +}
121 +
122 +add_action( 'load-toplevel_page_parsely-dashboard-page', __NAMESPACE__ . '\\create_engagement_boost_changeset_post' );
88 123 /**
89 - * Register the Parse.ly wp-admin warnings, plugin actions and row actions.
124 + * Creates a predefined changeset post in order to make the Engagement Boost
125 + * preview work for non-administrator user roles.
90 126 *
91 - * @return void
127 + * When the Engagement Boost preview iframe is loaded, it calls the WordPress
128 + * Customizer in the `iFrameSrc()` function. The Customizer requires the current
129 + * user to have the `customize` capability (available to Administrators), or the
130 + * passed UUID to have a corresponding changeset post in the database. Otherwise
131 + * it fails with -1 ("Non-existent changeset UUID") around line 561 in
132 + * `class-wp-customize-manager.php` and prevents preview, displaying "-1".
133 + *
134 + * By creating a predefined changeset post with a known UUID that we use in
135 + * `iFrameSrc()`, we guarantee that the preview will work for all authorized
136 + * user roles. The preview only appears on the Engagement Boost page, so the
137 + * post is created when that page loads.
138 + *
139 + * @since 3.20.7
140 + * @since 3.24.3 Runs only when the Engagement Boost page loads.
92 141 */
93 -function parsely_admin_init_register(): void {
94 - $admin_warning = new Admin_Warning( $GLOBALS['parsely'] );
95 - $admin_warning->run();
142 +function create_engagement_boost_changeset_post(): void {
143 + if ( ! function_exists( 'post_exists' ) ) {
144 + return;
145 + }
96 146
97 - $plugins_actions = new Plugins_Actions();
98 - $plugins_actions->run();
147 + $parsely = get_parsely();
99 148
100 - $row_actions = new Row_Actions( $GLOBALS['parsely'] );
101 - $row_actions->run();
149 + if ( ! $parsely->api_secret_is_set() || ! Permissions::current_user_can_use_pch_feature(
150 + 'traffic_boost',
151 + $parsely->get_options()['content_helper']
152 + ) ) {
153 + return;
154 + }
155 +
156 + // Needs to match the UUID in `iFrameSrc()` in `preview-iframe.tsx`.
157 + $uuid = '905b130b-4129-4416-919c-9e31433a6f65';
158 + $post_id = post_exists( "wp-parsely-$uuid", '', '', 'customize_changeset', 'private' );
159 +
160 + if ( 0 === $post_id ) {
161 + wp_insert_post(
162 + array(
163 + 'post_type' => 'customize_changeset',
164 + 'post_name' => $uuid,
165 + 'post_title' => "wp-parsely-$uuid",
166 + 'post_status' => 'private',
167 + )
168 + );
169 + }
102 170 }
103 171
104 -require __DIR__ . '/src/UI/class-settings-page.php';
105 -require __DIR__ . '/src/UI/class-network-admin-sites-list.php';
106 -
107 172 add_action( 'init', __NAMESPACE__ . '\\parsely_wp_admin_early_register' );
108 173 /**
109 - * Register the additions the Parse.ly wp-admin settings page and Multisite Network Admin Sites List table.
110 - *
111 - * @return void
174 + * Registers the additions the Parse.ly wp-admin settings page and Multisite
175 + * Network Admin Sites List table.
112 176 */
113 177 function parsely_wp_admin_early_register(): void {
114 - $settings_page = new Settings_Page( $GLOBALS['parsely'] );
115 - $settings_page->run();
178 + $parsely = get_parsely();
116 179
117 - $network_admin_sites_list = new Network_Admin_Sites_List( $GLOBALS['parsely'] );
180 + // Plugin dashboard page.
181 + $GLOBALS['parsely_dashboard_page'] = new Dashboard_Page( $parsely );
182 + $GLOBALS['parsely_dashboard_page']->run();
183 +
184 + // Plugin settings page.
185 + $GLOBALS['parsely_settings_page'] = new Settings_Page( $parsely );
186 + $GLOBALS['parsely_settings_page']->run();
187 +
188 + $network_admin_sites_list = new Network_Admin_Sites_List( $parsely );
118 189 $network_admin_sites_list->run();
190 +
191 + // Initialize the REST API Controller.
192 + $rest_api_controller = $parsely->get_rest_api_controller();
193 + $rest_api_controller->init();
119 194 }
120 195
121 -require __DIR__ . '/src/RemoteAPI/interface-cache.php';
122 -require __DIR__ . '/src/RemoteAPI/interface-proxy.php';
123 -require __DIR__ . '/src/RemoteAPI/class-base-proxy.php';
124 -require __DIR__ . '/src/RemoteAPI/class-cached-proxy.php';
125 -require __DIR__ . '/src/RemoteAPI/class-related-proxy.php';
126 -require __DIR__ . '/src/RemoteAPI/class-wordpress-cache.php';
127 -require __DIR__ . '/src/Endpoints/class-related-api-proxy.php';
128 -require __DIR__ . '/src/Endpoints/class-rest-metadata.php';
129 -
130 196 add_action( 'rest_api_init', __NAMESPACE__ . '\\parsely_rest_api_init' );
131 197 /**
132 - * Register REST Endpoints that act as a proxy to the Parse.ly API.
133 - * This is needed to get around a CORS issues with Firefox.
198 + * Registers REST Endpoints that act as a proxy to the Parse.ly API.
199 + * This is needed to get around CORS issues with Firefox.
134 200 *
135 201 * @since 3.2.0
136 - *
137 - * @return void
138 202 */
139 203 function parsely_rest_api_init(): void {
140 - $rest = new Rest_Metadata( $GLOBALS['parsely'] );
204 + $parsely = get_parsely();
205 +
206 + $rest = new Rest_Metadata( $parsely );
141 207 $rest->run();
142 -
143 - $proxy = new Related_Proxy( $GLOBALS['parsely'] );
144 - $cached_proxy = new Cached_Proxy( $proxy, new WordPress_Cache( $GLOBALS['wp_object_cache'] ) );
145 - $endpoint = new Related_API_Proxy( $GLOBALS['parsely'], $cached_proxy );
146 - $endpoint->run();
147 208 }
148 209
149 -require __DIR__ . '/src/blocks/recommendations/class-recommendations-block.php';
150 -
151 210 add_action( 'init', __NAMESPACE__ . '\\init_recommendations_block' );
152 211 /**
153 - * Register the Recommendations Block.
154 - *
155 - * @return void
212 + * Registers the Recommendations Block.
156 213 */
157 214 function init_recommendations_block(): void {
158 215 $recommendations_block = new Recommendations_Block();
159 216 $recommendations_block->run();
@@ -158,40 +215,172 @@
158 215 $recommendations_block = new Recommendations_Block();
159 216 $recommendations_block->run();
160 217 }
161 218
162 -require __DIR__ . '/src/UI/class-recommended-widget.php';
219 +add_action( 'enqueue_block_editor_assets', __NAMESPACE__ . '\\init_content_helper_editor_sidebar' );
220 +/**
221 + * Inserts the PCH Editor Sidebar.
222 + *
223 + * @since 3.5.0 Moved from Parsely\Scripts\enqueue_block_editor_assets().
224 + * @since 3.9.0 Renamed from init_content_helper().
225 + */
226 +function init_content_helper_editor_sidebar(): void {
227 + $GLOBALS['parsely_editor_sidebar']->run();
228 +}
163 229
230 +add_action( 'enqueue_block_assets', __NAMESPACE__ . '\\init_content_helper_canvas_styles' );
231 +/**
232 + * Inserts the PCH Editor Sidebar styles that need to reach the Editor canvas.
233 + *
234 + * @since 3.24.0
235 + */
236 +function init_content_helper_canvas_styles(): void {
237 + if ( ! isset( $GLOBALS['parsely_editor_sidebar'] ) ) {
238 + return;
239 + }
240 +
241 + $GLOBALS['parsely_editor_sidebar']->run_canvas_styles();
242 +}
243 +
244 +add_action( 'admin_init', __NAMESPACE__ . '\\parsely_content_helper_editor_sidebar_features' );
245 +add_action( 'rest_api_init', __NAMESPACE__ . '\\parsely_content_helper_editor_sidebar_features' );
246 +/**
247 + * Initializes the PCH Editor Sidebar features.
248 + *
249 + * @since 3.16.0
250 + */
251 +function parsely_content_helper_editor_sidebar_features(): void {
252 + if ( ! isset( $GLOBALS['parsely_editor_sidebar'] ) ) {
253 + /**
254 + * The Editor Sidebar instance.
255 + *
256 + * @since 3.16.0
257 + * @var Editor_Sidebar $GLOBALS['parsely_editor_sidebar']
258 + */
259 + $parsely = get_parsely();
260 +
261 + $GLOBALS['parsely_editor_sidebar'] = new Editor_Sidebar( $parsely );
262 + $GLOBALS['parsely_editor_sidebar']->init_features();
263 + }
264 +}
265 +
164 266 add_action( 'widgets_init', __NAMESPACE__ . '\\parsely_recommended_widget_register' );
165 267 /**
166 - * Register the Parse.ly Recommended widget.
167 - *
168 - * @return void
268 + * Registers the Parse.ly Recommended widget.
169 269 */
170 270 function parsely_recommended_widget_register(): void {
171 - register_widget( Recommended_Widget::class );
271 + $parsely = get_parsely();
272 +
273 + register_widget( new Recommended_Widget( $parsely ) );
172 274 }
173 275
174 -require __DIR__ . '/src/Integrations/class-integration.php';
175 -require __DIR__ . '/src/Integrations/class-integrations.php';
176 -require __DIR__ . '/src/Integrations/class-amp.php';
177 -require __DIR__ . '/src/Integrations/class-facebook-instant-articles.php';
178 -require __DIR__ . '/src/Integrations/class-google-web-stories.php';
179 -
180 -add_action( 'init', __NAMESPACE__ . '\\parsely_integrations' );
276 +add_action( 'init', __NAMESPACE__ . '\\parsely_integrations' ); // @phpstan-ignore-line
181 277 /**
182 - * Instantiate Integrations collection and register built-in integrations.
278 + * Instantiates Integrations collection and registers built-in integrations.
183 279 *
184 280 * @since 2.6.0
185 281 *
282 + * @param Parsely|string|null $parsely The Parsely object to pass to the integrations.
186 283 * @return Integrations
187 284 */
188 -function parsely_integrations(): Integrations {
189 - $parsely_integrations = new Integrations();
285 +function parsely_integrations( $parsely = null ): Integrations {
286 + // If $parsely value is "", then this function is being called by the init
287 + // hook and we can get the value from $GLOBALS. If $parsely is an instance
288 + // of the Parsely object, then this function is being called by a test.
289 + if ( ! is_object( $parsely ) || get_class( $parsely ) !== Parsely::class ) {
290 + $parsely = get_parsely();
291 + }
292 +
293 + $parsely_integrations = new Integrations( $parsely );
190 294 $parsely_integrations->register( 'amp', Amp::class );
191 - $parsely_integrations->register( 'fbia', Facebook_Instant_Articles::class );
192 295 $parsely_integrations->register( 'webstories', Google_Web_Stories::class );
193 296 $parsely_integrations = apply_filters( 'wp_parsely_add_integration', $parsely_integrations );
194 297 $parsely_integrations->integrate();
195 298
196 299 return $parsely_integrations;
300 +}
301 +
302 +add_action( 'admin_init', __NAMESPACE__ . '\\parsely_check_data_schema_updates', 999 );
303 +/**
304 + * Checks and performs any data schema updates.
305 + *
306 + * @since 3.19.0 Handles the update from schema version 0 to 1.
307 + */
308 +function parsely_check_data_schema_updates(): void {
309 + $current_data_schema_version = get_option( 'parsely_data_schema_version' );
310 +
311 + if ( false === $current_data_schema_version ) {
312 + $current_data_schema_version = 0;
313 + }
314 +
315 + if ( PARSELY_DATA_SCHEMA_VERSION <= $current_data_schema_version ) {
316 + return;
317 + }
318 +
319 + /**
320 + * Updates the smart links to have the Smart Link Status terms,
321 + * and checks the _smart_link_applied meta, if it exists.
322 + *
323 + * Schema version 1.
324 + *
325 + * @since 3.19.0
326 + */
327 + if ( 0 === $current_data_schema_version ) {
328 + // Get all the smart links that do not have any Smart Link Status terms.
329 + // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.get_posts_get_posts
330 + $smart_links_without_status = get_posts(
331 + array(
332 + 'post_type' => 'parsely_smart_link',
333 + 'posts_per_page' => -1, // phpcs:ignore WordPressVIPMinimum.Performance.NoPaging.posts_per_page_posts_per_page
334 + 'fields' => 'ids',
335 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
336 + 'tax_query' => array(
337 + array(
338 + 'taxonomy' => 'smart_link_status',
339 + 'field' => 'name',
340 + 'terms' => \Parsely\Models\Smart_Link_Status::get_all_statuses(),
341 + 'operator' => 'NOT IN',
342 + ),
343 + ),
344 + )
345 + );
346 +
347 + if ( count( $smart_links_without_status ) === 0 ) {
348 + update_option( 'parsely_data_schema_version', PARSELY_DATA_SCHEMA_VERSION );
349 + return;
350 + }
351 +
352 + // Loop through the smart links and update them to have the Smart Link Status terms.
353 + foreach ( $smart_links_without_status as $post_id ) {
354 + $smart_link = \Parsely\Models\Smart_Link::get_smart_link_by_id( intval( $post_id ) );
355 +
356 + if ( false === $smart_link ) {
357 + continue;
358 + }
359 +
360 + $meta_exists = metadata_exists( 'post', $post_id, '_smart_link_applied' );
361 +
362 + // If there is no meta, it means that the smart link is considered applied,
363 + // for backwards compatibility with Parse.ly < 3.18.0.
364 + if ( ! $meta_exists ) {
365 + $smart_link->set_status( \Parsely\Models\Smart_Link_Status::APPLIED, true );
366 + continue;
367 + }
368 +
369 + // Get the value of the _smart_link_applied meta.
370 + $meta_value = get_post_meta( $post_id, '_smart_link_applied', true );
371 +
372 + // If the meta value is true, then the smart link is considered applied.
373 + if ( 'true' === $meta_value || true === $meta_value ) {
374 + $smart_link->set_status( \Parsely\Models\Smart_Link_Status::APPLIED, true );
375 + } else {
376 + // If the meta value is not true, then the smart link is considered pending.
377 + $smart_link->set_status( \Parsely\Models\Smart_Link_Status::PENDING, true );
378 + }
379 +
380 + // Flush the cache for the smart link.
381 + $smart_link->flush_all_cache();
382 + }
383 +
384 + update_option( 'parsely_data_schema_version', PARSELY_DATA_SCHEMA_VERSION );
385 + }
197 386 }