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 +205 -21 3.18.1 → 3.24.3 View file →
@@ -10,9 +10,9 @@
10 10 * @wordpress-plugin
11 11 * Plugin Name: Parse.ly
12 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.18.1
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.2
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
@@ -27,18 +27,18 @@
27 27 namespace Parsely;
28 28
29 29 use Parsely\Content_Helper\Dashboard_Widget;
30 30 use Parsely\Content_Helper\Editor_Sidebar;
31 -use Parsely\Content_Helper\Excerpt_Suggestions;
32 31 use Parsely\Content_Helper\Post_List_Stats;
33 32 use Parsely\Endpoints\GraphQL_Metadata;
34 33 use Parsely\Endpoints\Rest_Metadata;
34 +use Parsely\Headline_Testing;
35 35 use Parsely\Integrations\Amp;
36 36 use Parsely\Integrations\Google_Web_Stories;
37 37 use Parsely\Integrations\Integrations;
38 -use Parsely\REST_API\REST_API_Controller;
39 38 use Parsely\UI\Admin_Bar;
40 39 use Parsely\UI\Admin_Warning;
40 +use Parsely\UI\Dashboard_Page;
41 41 use Parsely\UI\Metadata_Renderer;
42 42 use Parsely\UI\Network_Admin_Sites_List;
43 43 use Parsely\UI\Plugins_Actions;
44 44 use Parsely\UI\Recommended_Widget;
@@ -49,10 +49,12 @@
49 49 if ( class_exists( Parsely::class ) ) {
50 50 return;
51 51 }
52 52
53 -const PARSELY_VERSION = '3.18.1';
54 -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';
55 57
56 58 if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
57 59 require_once __DIR__ . '/vendor/autoload.php';
58 60 }
@@ -59,29 +61,48 @@
59 61
60 62 // Load Telemetry classes.
61 63 require_once __DIR__ . '/src/Telemetry/telemetry-init.php';
62 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 +
63 80 add_action( 'plugins_loaded', __NAMESPACE__ . '\\parsely_initialize_plugin' );
64 81 /**
65 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();
81 98
82 - $metadata_renderer = new Metadata_Renderer( $GLOBALS['parsely'] );
99 + $metadata_renderer = new Metadata_Renderer( $parsely );
83 100 $metadata_renderer->run();
101 +
102 + // Initialize Headline Testing feature.
103 + $headline_testing = new Headline_Testing( $parsely );
104 + $headline_testing->run();
84 105 }
85 106
86 107 add_action( 'admin_init', __NAMESPACE__ . '\\parsely_admin_init_register' );
87 108 /**
@@ -87,9 +108,9 @@
87 108 /**
88 109 * Registers the Parse.ly wp-admin warnings, plugin actions and row actions.
89 110 */
90 111 function parsely_admin_init_register(): void {
91 - $parsely = $GLOBALS['parsely'];
112 + $parsely = get_parsely();
92 113
93 114 ( new Admin_Warning( $parsely ) )->run();
94 115 ( new Plugins_Actions() )->run();
95 116 ( new Row_Actions( $parsely ) )->run();
@@ -97,8 +118,58 @@
97 118 ( new Site_Health( $parsely ) )->run();
98 119 ( new Dashboard_Widget( $parsely ) )->run();
99 120 }
100 121
122 +add_action( 'load-toplevel_page_parsely-dashboard-page', __NAMESPACE__ . '\\create_engagement_boost_changeset_post' );
123 +/**
124 + * Creates a predefined changeset post in order to make the Engagement Boost
125 + * preview work for non-administrator user roles.
126 + *
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.
141 + */
142 +function create_engagement_boost_changeset_post(): void {
143 + if ( ! function_exists( 'post_exists' ) ) {
144 + return;
145 + }
146 +
147 + $parsely = get_parsely();
148 +
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 + }
170 +}
171 +
101 172 add_action( 'init', __NAMESPACE__ . '\\parsely_wp_admin_early_register' );
102 173 /**
103 174 * Registers the additions the Parse.ly wp-admin settings page and Multisite
104 175 * Network Admin Sites List table.
@@ -103,16 +174,23 @@
103 174 * Registers the additions the Parse.ly wp-admin settings page and Multisite
104 175 * Network Admin Sites List table.
105 176 */
106 177 function parsely_wp_admin_early_register(): void {
107 - $GLOBALS['parsely_settings_page'] = new Settings_Page( $GLOBALS['parsely'] );
178 + $parsely = get_parsely();
179 +
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 );
108 186 $GLOBALS['parsely_settings_page']->run();
109 187
110 - $network_admin_sites_list = new Network_Admin_Sites_List( $GLOBALS['parsely'] );
188 + $network_admin_sites_list = new Network_Admin_Sites_List( $parsely );
111 189 $network_admin_sites_list->run();
112 190
113 191 // Initialize the REST API Controller.
114 - $rest_api_controller = $GLOBALS['parsely']->get_rest_api_controller();
192 + $rest_api_controller = $parsely->get_rest_api_controller();
115 193 $rest_api_controller->init();
116 194 }
117 195
118 196 add_action( 'rest_api_init', __NAMESPACE__ . '\\parsely_rest_api_init' );
@@ -122,9 +200,11 @@
122 200 *
123 201 * @since 3.2.0
124 202 */
125 203 function parsely_rest_api_init(): void {
126 - $rest = new Rest_Metadata( $GLOBALS['parsely'] );
204 + $parsely = get_parsely();
205 +
206 + $rest = new Rest_Metadata( $parsely );
127 207 $rest->run();
128 208 }
129 209
130 210 add_action( 'init', __NAMESPACE__ . '\\init_recommendations_block' );
@@ -146,8 +226,22 @@
146 226 function init_content_helper_editor_sidebar(): void {
147 227 $GLOBALS['parsely_editor_sidebar']->run();
148 228 }
149 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 +
150 244 add_action( 'admin_init', __NAMESPACE__ . '\\parsely_content_helper_editor_sidebar_features' );
151 245 add_action( 'rest_api_init', __NAMESPACE__ . '\\parsely_content_helper_editor_sidebar_features' );
152 246 /**
153 247 * Initializes the PCH Editor Sidebar features.
@@ -161,9 +255,11 @@
161 255 *
162 256 * @since 3.16.0
163 257 * @var Editor_Sidebar $GLOBALS['parsely_editor_sidebar']
164 258 */
165 - $GLOBALS['parsely_editor_sidebar'] = new Editor_Sidebar( $GLOBALS['parsely'] );
259 + $parsely = get_parsely();
260 +
261 + $GLOBALS['parsely_editor_sidebar'] = new Editor_Sidebar( $parsely );
166 262 $GLOBALS['parsely_editor_sidebar']->init_features();
167 263 }
168 264 }
169 265
@@ -171,9 +267,11 @@
171 267 /**
172 268 * Registers the Parse.ly Recommended widget.
173 269 */
174 270 function parsely_recommended_widget_register(): void {
175 - register_widget( new Recommended_Widget( $GLOBALS['parsely'] ) );
271 + $parsely = get_parsely();
272 +
273 + register_widget( new Recommended_Widget( $parsely ) );
176 274 }
177 275
178 276 add_action( 'init', __NAMESPACE__ . '\\parsely_integrations' ); // @phpstan-ignore-line
179 277 /**
@@ -188,9 +286,9 @@
188 286 // If $parsely value is "", then this function is being called by the init
189 287 // hook and we can get the value from $GLOBALS. If $parsely is an instance
190 288 // of the Parsely object, then this function is being called by a test.
191 289 if ( ! is_object( $parsely ) || get_class( $parsely ) !== Parsely::class ) {
192 - $parsely = $GLOBALS['parsely'];
290 + $parsely = get_parsely();
193 291 }
194 292
195 293 $parsely_integrations = new Integrations( $parsely );
196 294 $parsely_integrations->register( 'amp', Amp::class );
@@ -198,5 +296,91 @@
198 296 $parsely_integrations = apply_filters( 'wp_parsely_add_integration', $parsely_integrations );
199 297 $parsely_integrations->integrate();
200 298
201 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 + }
202 386 }