PluginProbe
Parse.ly / 3.23.1
Parse.ly v3.23.1
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 3.20.3 All 105 releases
wp-parsely / wp-parsely.php

wp-parsely.php in Parse.ly 3.23.1, at wp-parsely.php

371 lines 11.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Parse.ly
4 *
5 * @package Parsely
6 * @author Parse.ly
7 * @copyright 2012 Parse.ly
8 * @license GPL-2.0-or-later
9 *
10 * @wordpress-plugin
11 * Plugin Name: Parse.ly
12 * Plugin URI: https://docs.parse.ly/wordpress
13 * Description: This plugin makes it a snap to add Parse.ly tracking code and metadata to your WordPress blog.
14 * Version: 3.23.1
15 * Author: Parse.ly
16 * Author URI: https://www.parse.ly
17 * Text Domain: wp-parsely
18 * License: GPL-2.0-or-later
19 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
20 * GitHub Plugin URI: https://github.com/Parsely/wp-parsely
21 * Requires PHP: 7.4
22 * Requires WP: 6.0.0
23 */
24
25 declare(strict_types=1);
26
27 namespace Parsely;
28
29 use Parsely\Content_Helper\Dashboard_Widget;
30 use Parsely\Content_Helper\Editor_Sidebar;
31 use Parsely\Content_Helper\Post_List_Stats;
32 use Parsely\Endpoints\GraphQL_Metadata;
33 use Parsely\Endpoints\Rest_Metadata;
34 use Parsely\Headline_Testing;
35 use Parsely\Integrations\Amp;
36 use Parsely\Integrations\Google_Web_Stories;
37 use Parsely\Integrations\Integrations;
38 use Parsely\UI\Admin_Bar;
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;
43 use Parsely\UI\Plugins_Actions;
44 use Parsely\UI\Recommended_Widget;
45 use Parsely\UI\Row_Actions;
46 use Parsely\UI\Settings_Page;
47 use Parsely\UI\Site_Health;
48
49 if ( class_exists( Parsely::class ) ) {
50 return;
51 }
52
53 const PARSELY_VERSION = '3.23.1';
54 const PARSELY_FILE = __FILE__;
55 const PARSELY_DATA_SCHEMA_VERSION = '1';
56 const PARSELY_CACHE_GROUP = 'wp-parsely';
57
58 if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
59 require_once __DIR__ . '/vendor/autoload.php';
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
80 add_action( 'plugins_loaded', __NAMESPACE__ . '\\parsely_initialize_plugin' );
81 /**
82 * Registers the basic classes to initialize the plugin.
83 */
84 function parsely_initialize_plugin(): void {
85 $parsely = get_parsely();
86 $parsely->run();
87
88 if ( class_exists( 'WPGraphQL' ) ) {
89 $graphql = new GraphQL_Metadata( $parsely );
90 $graphql->run();
91 }
92
93 $scripts = new Scripts( $parsely );
94 $scripts->run();
95
96 $admin_bar = new Admin_Bar( $parsely );
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();
105 }
106
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();
113
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( '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
170 add_action( 'init', __NAMESPACE__ . '\\parsely_wp_admin_early_register' );
171 /**
172 * Registers the additions the Parse.ly wp-admin settings page and Multisite
173 * Network Admin Sites List table.
174 */
175 function parsely_wp_admin_early_register(): void {
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 );
184 $GLOBALS['parsely_settings_page']->run();
185
186 $network_admin_sites_list = new Network_Admin_Sites_List( $parsely );
187 $network_admin_sites_list->run();
188
189 // Initialize the REST API Controller.
190 $rest_api_controller = $parsely->get_rest_api_controller();
191 $rest_api_controller->init();
192 }
193
194 add_action( 'rest_api_init', __NAMESPACE__ . '\\parsely_rest_api_init' );
195 /**
196 * Registers REST Endpoints that act as a proxy to the Parse.ly API.
197 * This is needed to get around CORS issues with Firefox.
198 *
199 * @since 3.2.0
200 */
201 function parsely_rest_api_init(): void {
202 $parsely = get_parsely();
203
204 $rest = new Rest_Metadata( $parsely );
205 $rest->run();
206 }
207
208 add_action( 'init', __NAMESPACE__ . '\\init_recommendations_block' );
209 /**
210 * Registers the Recommendations Block.
211 */
212 function init_recommendations_block(): void {
213 $recommendations_block = new Recommendations_Block();
214 $recommendations_block->run();
215 }
216
217 add_action( 'enqueue_block_editor_assets', __NAMESPACE__ . '\\init_content_helper_editor_sidebar' );
218 /**
219 * Inserts the PCH Editor Sidebar.
220 *
221 * @since 3.5.0 Moved from Parsely\Scripts\enqueue_block_editor_assets().
222 * @since 3.9.0 Renamed from init_content_helper().
223 */
224 function init_content_helper_editor_sidebar(): void {
225 $GLOBALS['parsely_editor_sidebar']->run();
226 }
227
228 add_action( 'admin_init', __NAMESPACE__ . '\\parsely_content_helper_editor_sidebar_features' );
229 add_action( 'rest_api_init', __NAMESPACE__ . '\\parsely_content_helper_editor_sidebar_features' );
230 /**
231 * Initializes the PCH Editor Sidebar features.
232 *
233 * @since 3.16.0
234 */
235 function parsely_content_helper_editor_sidebar_features(): void {
236 if ( ! isset( $GLOBALS['parsely_editor_sidebar'] ) ) {
237 /**
238 * The Editor Sidebar instance.
239 *
240 * @since 3.16.0
241 * @var Editor_Sidebar $GLOBALS['parsely_editor_sidebar']
242 */
243 $parsely = get_parsely();
244
245 $GLOBALS['parsely_editor_sidebar'] = new Editor_Sidebar( $parsely );
246 $GLOBALS['parsely_editor_sidebar']->init_features();
247 }
248 }
249
250 add_action( 'widgets_init', __NAMESPACE__ . '\\parsely_recommended_widget_register' );
251 /**
252 * Registers the Parse.ly Recommended widget.
253 */
254 function parsely_recommended_widget_register(): void {
255 $parsely = get_parsely();
256
257 register_widget( new Recommended_Widget( $parsely ) );
258 }
259
260 add_action( 'init', __NAMESPACE__ . '\\parsely_integrations' ); // @phpstan-ignore-line
261 /**
262 * Instantiates Integrations collection and registers built-in integrations.
263 *
264 * @since 2.6.0
265 *
266 * @param Parsely|string|null $parsely The Parsely object to pass to the integrations.
267 * @return Integrations
268 */
269 function parsely_integrations( $parsely = null ): Integrations {
270 // If $parsely value is "", then this function is being called by the init
271 // hook and we can get the value from $GLOBALS. If $parsely is an instance
272 // of the Parsely object, then this function is being called by a test.
273 if ( ! is_object( $parsely ) || get_class( $parsely ) !== Parsely::class ) {
274 $parsely = get_parsely();
275 }
276
277 $parsely_integrations = new Integrations( $parsely );
278 $parsely_integrations->register( 'amp', Amp::class );
279 $parsely_integrations->register( 'webstories', Google_Web_Stories::class );
280 $parsely_integrations = apply_filters( 'wp_parsely_add_integration', $parsely_integrations );
281 $parsely_integrations->integrate();
282
283 return $parsely_integrations;
284 }
285
286 add_action( 'admin_init', __NAMESPACE__ . '\\parsely_check_data_schema_updates', 999 );
287 /**
288 * Checks and performs any data schema updates.
289 *
290 * @since 3.19.0 Handles the update from schema version 0 to 1.
291 */
292 function parsely_check_data_schema_updates(): void {
293 $current_data_schema_version = get_option( 'parsely_data_schema_version' );
294
295 if ( false === $current_data_schema_version ) {
296 $current_data_schema_version = 0;
297 }
298
299 if ( PARSELY_DATA_SCHEMA_VERSION <= $current_data_schema_version ) {
300 return;
301 }
302
303 /**
304 * Updates the smart links to have the Smart Link Status terms,
305 * and checks the _smart_link_applied meta, if it exists.
306 *
307 * Schema version 1.
308 *
309 * @since 3.19.0
310 */
311 if ( 0 === $current_data_schema_version ) {
312 // Get all the smart links that do not have any Smart Link Status terms.
313 // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.get_posts_get_posts
314 $smart_links_without_status = get_posts(
315 array(
316 'post_type' => 'parsely_smart_link',
317 'posts_per_page' => -1,
318 'fields' => 'ids',
319 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
320 'tax_query' => array(
321 array(
322 'taxonomy' => 'smart_link_status',
323 'field' => 'name',
324 'terms' => \Parsely\Models\Smart_Link_Status::get_all_statuses(),
325 'operator' => 'NOT IN',
326 ),
327 ),
328 )
329 );
330
331 if ( count( $smart_links_without_status ) === 0 ) {
332 update_option( 'parsely_data_schema_version', PARSELY_DATA_SCHEMA_VERSION );
333 return;
334 }
335
336 // Loop through the smart links and update them to have the Smart Link Status terms.
337 foreach ( $smart_links_without_status as $post_id ) {
338 $smart_link = \Parsely\Models\Smart_Link::get_smart_link_by_id( intval( $post_id ) );
339
340 if ( false === $smart_link ) {
341 continue;
342 }
343
344 $meta_exists = metadata_exists( 'post', $post_id, '_smart_link_applied' );
345
346 // If there is no meta, it means that the smart link is considered applied,
347 // for backwards compatibility with Parse.ly < 3.18.0.
348 if ( ! $meta_exists ) {
349 $smart_link->set_status( \Parsely\Models\Smart_Link_Status::APPLIED, true );
350 continue;
351 }
352
353 // Get the value of the _smart_link_applied meta.
354 $meta_value = get_post_meta( $post_id, '_smart_link_applied', true );
355
356 // If the meta value is true, then the smart link is considered applied.
357 if ( 'true' === $meta_value || true === $meta_value ) {
358 $smart_link->set_status( \Parsely\Models\Smart_Link_Status::APPLIED, true );
359 } else {
360 // If the meta value is not true, then the smart link is considered pending.
361 $smart_link->set_status( \Parsely\Models\Smart_Link_Status::PENDING, true );
362 }
363
364 // Flush the cache for the smart link.
365 $smart_link->flush_all_cache();
366 }
367
368 update_option( 'parsely_data_schema_version', PARSELY_DATA_SCHEMA_VERSION );
369 }
370 }
371