PluginProbe
Parse.ly / 3.24.2
Parse.ly v3.24.2
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 3.20.2 All 106 releases
← All changes | wp-parsely.php +203 -21 3.18.0 → 3.24.2 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.0
14 + * Version: 3.24.2
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.0';
54 -const PARSELY_FILE = __FILE__;
53 +const PARSELY_VERSION = '3.24.2';
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,56 @@
97 118 ( new Site_Health( $parsely ) )->run();
98 119 ( new Dashboard_Widget( $parsely ) )->run();
99 120 }
100 121
122 +add_action( 'admin_init', __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.
137 + *
138 + * @since 3.20.7
139 + */
140 +function create_engagement_boost_changeset_post(): void {
141 + if ( ! function_exists( 'post_exists' ) ) {
142 + return;
143 + }
144 +
145 + $parsely = get_parsely();
146 +
147 + if ( ! $parsely->api_secret_is_set() || ! Permissions::current_user_can_use_pch_feature(
148 + 'traffic_boost',
149 + $parsely->get_options()['content_helper']
150 + ) ) {
151 + return;
152 + }
153 +
154 + // Needs to match the UUID in `iFrameSrc()` in `preview-iframe.tsx`.
155 + $uuid = '905b130b-4129-4416-919c-9e31433a6f65';
156 + $post_id = post_exists( "wp-parsely-$uuid", '', '', 'customize_changeset', 'private' );
157 +
158 + if ( 0 === $post_id ) {
159 + wp_insert_post(
160 + array(
161 + 'post_type' => 'customize_changeset',
162 + 'post_name' => $uuid,
163 + 'post_title' => "wp-parsely-$uuid",
164 + 'post_status' => 'private',
165 + )
166 + );
167 + }
168 +}
169 +
101 170 add_action( 'init', __NAMESPACE__ . '\\parsely_wp_admin_early_register' );
102 171 /**
103 172 * Registers the additions the Parse.ly wp-admin settings page and Multisite
104 173 * Network Admin Sites List table.
@@ -103,16 +172,23 @@
103 172 * Registers the additions the Parse.ly wp-admin settings page and Multisite
104 173 * Network Admin Sites List table.
105 174 */
106 175 function parsely_wp_admin_early_register(): void {
107 - $GLOBALS['parsely_settings_page'] = new Settings_Page( $GLOBALS['parsely'] );
176 + $parsely = get_parsely();
177 +
178 + // Plugin dashboard page.
179 + $GLOBALS['parsely_dashboard_page'] = new Dashboard_Page( $parsely );
180 + $GLOBALS['parsely_dashboard_page']->run();
181 +
182 + // Plugin settings page.
183 + $GLOBALS['parsely_settings_page'] = new Settings_Page( $parsely );
108 184 $GLOBALS['parsely_settings_page']->run();
109 185
110 - $network_admin_sites_list = new Network_Admin_Sites_List( $GLOBALS['parsely'] );
186 + $network_admin_sites_list = new Network_Admin_Sites_List( $parsely );
111 187 $network_admin_sites_list->run();
112 188
113 189 // Initialize the REST API Controller.
114 - $rest_api_controller = $GLOBALS['parsely']->get_rest_api_controller();
190 + $rest_api_controller = $parsely->get_rest_api_controller();
115 191 $rest_api_controller->init();
116 192 }
117 193
118 194 add_action( 'rest_api_init', __NAMESPACE__ . '\\parsely_rest_api_init' );
@@ -122,9 +198,11 @@
122 198 *
123 199 * @since 3.2.0
124 200 */
125 201 function parsely_rest_api_init(): void {
126 - $rest = new Rest_Metadata( $GLOBALS['parsely'] );
202 + $parsely = get_parsely();
203 +
204 + $rest = new Rest_Metadata( $parsely );
127 205 $rest->run();
128 206 }
129 207
130 208 add_action( 'init', __NAMESPACE__ . '\\init_recommendations_block' );
@@ -146,8 +224,22 @@
146 224 function init_content_helper_editor_sidebar(): void {
147 225 $GLOBALS['parsely_editor_sidebar']->run();
148 226 }
149 227
228 +add_action( 'enqueue_block_assets', __NAMESPACE__ . '\\init_content_helper_canvas_styles' );
229 +/**
230 + * Inserts the PCH Editor Sidebar styles that need to reach the Editor canvas.
231 + *
232 + * @since 3.24.0
233 + */
234 +function init_content_helper_canvas_styles(): void {
235 + if ( ! isset( $GLOBALS['parsely_editor_sidebar'] ) ) {
236 + return;
237 + }
238 +
239 + $GLOBALS['parsely_editor_sidebar']->run_canvas_styles();
240 +}
241 +
150 242 add_action( 'admin_init', __NAMESPACE__ . '\\parsely_content_helper_editor_sidebar_features' );
151 243 add_action( 'rest_api_init', __NAMESPACE__ . '\\parsely_content_helper_editor_sidebar_features' );
152 244 /**
153 245 * Initializes the PCH Editor Sidebar features.
@@ -161,9 +253,11 @@
161 253 *
162 254 * @since 3.16.0
163 255 * @var Editor_Sidebar $GLOBALS['parsely_editor_sidebar']
164 256 */
165 - $GLOBALS['parsely_editor_sidebar'] = new Editor_Sidebar( $GLOBALS['parsely'] );
257 + $parsely = get_parsely();
258 +
259 + $GLOBALS['parsely_editor_sidebar'] = new Editor_Sidebar( $parsely );
166 260 $GLOBALS['parsely_editor_sidebar']->init_features();
167 261 }
168 262 }
169 263
@@ -171,9 +265,11 @@
171 265 /**
172 266 * Registers the Parse.ly Recommended widget.
173 267 */
174 268 function parsely_recommended_widget_register(): void {
175 - register_widget( new Recommended_Widget( $GLOBALS['parsely'] ) );
269 + $parsely = get_parsely();
270 +
271 + register_widget( new Recommended_Widget( $parsely ) );
176 272 }
177 273
178 274 add_action( 'init', __NAMESPACE__ . '\\parsely_integrations' ); // @phpstan-ignore-line
179 275 /**
@@ -188,9 +284,9 @@
188 284 // If $parsely value is "", then this function is being called by the init
189 285 // hook and we can get the value from $GLOBALS. If $parsely is an instance
190 286 // of the Parsely object, then this function is being called by a test.
191 287 if ( ! is_object( $parsely ) || get_class( $parsely ) !== Parsely::class ) {
192 - $parsely = $GLOBALS['parsely'];
288 + $parsely = get_parsely();
193 289 }
194 290
195 291 $parsely_integrations = new Integrations( $parsely );
196 292 $parsely_integrations->register( 'amp', Amp::class );
@@ -198,5 +294,91 @@
198 294 $parsely_integrations = apply_filters( 'wp_parsely_add_integration', $parsely_integrations );
199 295 $parsely_integrations->integrate();
200 296
201 297 return $parsely_integrations;
298 +}
299 +
300 +add_action( 'admin_init', __NAMESPACE__ . '\\parsely_check_data_schema_updates', 999 );
301 +/**
302 + * Checks and performs any data schema updates.
303 + *
304 + * @since 3.19.0 Handles the update from schema version 0 to 1.
305 + */
306 +function parsely_check_data_schema_updates(): void {
307 + $current_data_schema_version = get_option( 'parsely_data_schema_version' );
308 +
309 + if ( false === $current_data_schema_version ) {
310 + $current_data_schema_version = 0;
311 + }
312 +
313 + if ( PARSELY_DATA_SCHEMA_VERSION <= $current_data_schema_version ) {
314 + return;
315 + }
316 +
317 + /**
318 + * Updates the smart links to have the Smart Link Status terms,
319 + * and checks the _smart_link_applied meta, if it exists.
320 + *
321 + * Schema version 1.
322 + *
323 + * @since 3.19.0
324 + */
325 + if ( 0 === $current_data_schema_version ) {
326 + // Get all the smart links that do not have any Smart Link Status terms.
327 + // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.get_posts_get_posts
328 + $smart_links_without_status = get_posts(
329 + array(
330 + 'post_type' => 'parsely_smart_link',
331 + 'posts_per_page' => -1, // phpcs:ignore WordPressVIPMinimum.Performance.NoPaging.posts_per_page_posts_per_page
332 + 'fields' => 'ids',
333 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
334 + 'tax_query' => array(
335 + array(
336 + 'taxonomy' => 'smart_link_status',
337 + 'field' => 'name',
338 + 'terms' => \Parsely\Models\Smart_Link_Status::get_all_statuses(),
339 + 'operator' => 'NOT IN',
340 + ),
341 + ),
342 + )
343 + );
344 +
345 + if ( count( $smart_links_without_status ) === 0 ) {
346 + update_option( 'parsely_data_schema_version', PARSELY_DATA_SCHEMA_VERSION );
347 + return;
348 + }
349 +
350 + // Loop through the smart links and update them to have the Smart Link Status terms.
351 + foreach ( $smart_links_without_status as $post_id ) {
352 + $smart_link = \Parsely\Models\Smart_Link::get_smart_link_by_id( intval( $post_id ) );
353 +
354 + if ( false === $smart_link ) {
355 + continue;
356 + }
357 +
358 + $meta_exists = metadata_exists( 'post', $post_id, '_smart_link_applied' );
359 +
360 + // If there is no meta, it means that the smart link is considered applied,
361 + // for backwards compatibility with Parse.ly < 3.18.0.
362 + if ( ! $meta_exists ) {
363 + $smart_link->set_status( \Parsely\Models\Smart_Link_Status::APPLIED, true );
364 + continue;
365 + }
366 +
367 + // Get the value of the _smart_link_applied meta.
368 + $meta_value = get_post_meta( $post_id, '_smart_link_applied', true );
369 +
370 + // If the meta value is true, then the smart link is considered applied.
371 + if ( 'true' === $meta_value || true === $meta_value ) {
372 + $smart_link->set_status( \Parsely\Models\Smart_Link_Status::APPLIED, true );
373 + } else {
374 + // If the meta value is not true, then the smart link is considered pending.
375 + $smart_link->set_status( \Parsely\Models\Smart_Link_Status::PENDING, true );
376 + }
377 +
378 + // Flush the cache for the smart link.
379 + $smart_link->flush_all_cache();
380 + }
381 +
382 + update_option( 'parsely_data_schema_version', PARSELY_DATA_SCHEMA_VERSION );
383 + }
202 384 }