PluginProbe
Parse.ly / 1.12.3
Parse.ly v1.12.3
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 1.12.3, at wp-parsely.php

1,362 lines 45.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Parse.ly
4 Plugin URI: http://www.parsely.com/
5 Description: This plugin makes it a snap to add Parse.ly tracking code to your WordPress blog.
6 Author: Mike Sukmanowsky ( mike@parsely.com )
7 Version: 1.12.3
8 Requires at least: 4.0.0
9 Author URI: http://www.parsely.com/
10 License: GPL2
11
12 Copyright 2012 Parsely Incorporated
13
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License, version 2, as
16 published by the Free Software Foundation.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26
27 Authors: Mike Sukmanowsky ( mike@parsely.com), Xand Lourenco ( xand@parsely.com ), James O'Toole (james.otoole@parsely.com )
28 */
29
30 /*
31 TODO List:
32 * WordPress Network support - going to hold off on any specific support here as content id prefix should work ok for now
33 * Allow the user to map get_post_types() to Parse.ly post types
34 * Support: is_search(), is_404()
35 */
36
37 /**
38 * This is the main class for Parsely
39 *
40 * @category Class
41 * @package Parsely
42 */
43 class Parsely {
44 /**
45 * Declare our constants
46 *
47 * @codeCoverageIgnoreStart
48 */
49 const VERSION = '1.12.3';
50 const MENU_SLUG = 'parsely'; // Defines the page param passed to options-general.php
51 const MENU_TITLE = 'Parse.ly'; // Text to be used for the menu as seen in Settings sub-menu
52 const MENU_PAGE_TITLE = 'Parse.ly > Settings'; // Text shown in <title></title> when the settings screen is viewed
53 const OPTIONS_KEY = 'parsely'; // Defines the key used to store options in the WP database
54 const CAPABILITY = 'manage_options'; // The capability required for the user to administer settings
55
56 /**
57 * Declare some class propeties
58 *
59 * @var array $option_defaults The defaults we need for the class.
60 */
61 private $option_defaults = array(
62 'apikey' => '',
63 'content_id_prefix' => '',
64 'api_secret' => '',
65 'use_top_level_cats' => false,
66 'custom_taxonomy_section' => 'category',
67 'cats_as_tags' => false,
68 'track_authenticated_users' => true,
69 'lowercase_tags' => true,
70 'force_https_canonicals' => false,
71 'track_post_types' => array( 'post' ),
72 'track_page_types' => array( 'page' ),
73 'disable_javascript' => false,
74 'meta_type' => 'json_ld',
75 );
76
77 /**
78 * Declare some class propeties
79 *
80 * @var array $implementation_opts The implementation options for the class.
81 */
82 private $implementation_opts = array(
83 'standard' => 'Standard',
84 'dom_free' => 'DOM-Free',
85 );
86
87 /**
88 * The constructor
89 *
90 * @category Function
91 * @package Parsely
92 */
93 public function __construct() {
94 // Run upgrade options if they exist for the version currently defined.
95 $options = $this->get_options();
96 if ( empty( $options['plugin_version'] ) || Parsely::VERSION !== $options['plugin_version'] ) {
97 $method = 'upgrade_plugin_to_version_' . str_replace( '.', '_', Parsely::VERSION );
98 if ( method_exists( $this, $method ) ) {
99 call_user_func_array( array( $this, $method ), array( $options ) );
100 }
101 // Update our version info.
102 $options['plugin_version'] = Parsely::VERSION;
103 update_option( Parsely::OPTIONS_KEY, $options );
104 }
105
106 // admin_menu and a settings link.
107 add_action( 'admin_head', array( $this, 'add_admin_header' ) );
108 add_action( 'admin_menu', array( $this, 'add_settings_sub_menu' ) );
109 add_action( 'admin_init', array( $this, 'initialize_settings' ) );
110 // display warning when plugin hasn't been configured.
111 add_action( 'admin_footer', array( $this, 'display_admin_warning' ) );
112
113 $basename = plugin_basename( __FILE__ );
114 add_filter(
115 'plugin_action_links_' . $basename,
116 array( $this, 'add_plugin_meta_links' )
117 );
118
119 // inserting parsely code.
120 add_action( 'wp_head', array( $this, 'insert_parsely_page' ) );
121 add_action( 'wp_footer', array( $this, 'insert_parsely_javascript' ) );
122 add_action( 'instant_articles_compat_registry_analytics', array( $this, 'insert_parsely_tracking_fbia' ) );
123 add_action( 'pre_amp_render_post', array( $this, 'parsely_add_amp_actions' ) );
124 if ( ! defined( 'WP_PARSELY_TESTING' ) ) {
125 /**
126 * Initialize parsely WordPress style
127 */
128 function wp_parsely_style_init() {
129 wp_enqueue_style( 'wp-parsely-style', plugins_url( 'wp-parsely.css', __FILE__ ), array(), filemtime( get_stylesheet_directory() ) );
130 }
131
132 /**
133 * Make sure that jquery exists
134 */
135 function ensure_jquery_exists() {
136 wp_enqueue_script( 'jquery' );
137 }
138 add_action( 'wp_enqueue_scripts', 'wp_parsely_style_init' );
139 add_action( 'wp_enqueue_scripts', 'ensure_jquery_exists' );
140 }
141
142 }
143
144 /**
145 * Include the parsely admin header
146 *
147 * @category Function
148 * @package Parsely
149 */
150 public function add_admin_header() {
151 include 'parsely-admin-header.php';
152 }
153
154 /**
155 * Parsely settings page in WordPress settings menu.
156 *
157 * @category Function
158 * @package Parsely
159 */
160 public function add_settings_sub_menu() {
161 add_options_page(
162 Parsely::MENU_PAGE_TITLE,
163 Parsely::MENU_TITLE,
164 Parsely::CAPABILITY,
165 Parsely::MENU_SLUG,
166 array( $this, 'display_settings' )
167 );
168 }
169
170 /**
171 * Parse.ly settings screen ( options-general.php?page=[MENU_SLUG] )
172 *
173 * @category Function
174 * @package Parsely
175 */
176 public function display_settings() {
177 if ( ! current_user_can( Parsely::CAPABILITY ) ) {
178 wp_die( esc_attr( 'You do not have sufficient permissions to access this page.' ) );
179 }
180
181 include 'parsely-settings.php';
182 }
183
184 /**
185 * Initialize the settings for Parsely
186 *
187 * @category Function
188 * @package Parsely
189 */
190 public function initialize_settings() {
191 // All our options are actually stored in one single array to reduce
192 // DB queries.
193 register_setting(
194 Parsely::OPTIONS_KEY, Parsely::OPTIONS_KEY,
195 array( $this, 'validate_options' )
196 );
197
198 // These are the Required Settings.
199 add_settings_section(
200 'required_settings', 'Required Settings',
201 array( $this, 'print_required_settings' ),
202 Parsely::MENU_SLUG
203 );
204
205 // Get the API Key.
206 $h = 'Your Site ID is your own site domain ( e.g. `mydomain.com` )';
207
208 $field_args = array(
209 'option_key' => 'apikey',
210 'help_text' => $h,
211 );
212 add_settings_field(
213 'apikey',
214 'Parse.ly Site ID <div class="help-icons"></div>',
215 array( $this, 'print_text_tag' ),
216 Parsely::MENU_SLUG, 'required_settings',
217 $field_args
218 );
219
220 // These are the Optional Settings.
221 add_settings_section(
222 'optional_settings', 'Optional Settings',
223 array( $this, 'print_optional_settings' ),
224 Parsely::MENU_SLUG
225 );
226
227 $h = 'Your API secret is your secret code to %s%s%saccess our API.%s
228 It can be found at dash.parsely.com/yoursitedomain/settings/api
229 ( replace yoursitedown with your domain name, e.g. `mydomain.com` ) If you haven\'t purchased access to the API, and would
230 like to do so, email your account manager or support@parsely.com!';
231 $h_link = 'https://www.parse.ly/help/api/analytics/';
232
233 $field_args = array(
234 'option_key' => 'api_secret',
235 'help_text' => $h,
236 'help_link' => $h_link,
237 );
238 add_settings_field(
239 'api_secret',
240 'Parse.ly API Secret <div class="help-icons"></div>',
241 array( $this, 'print_text_tag' ),
242 Parsely::MENU_SLUG, 'optional_settings',
243 $field_args
244 );
245
246 $h = 'Choose the metadata format for our crawlers to access. ' .
247 'Most publishers are fine with JSON-LD ( %s%s%shttps://www.parse.ly/help/integration/jsonld/%s ), ' .
248 'but if you prefer to use our proprietary metadata format then you can do so here.';
249 $h_link = 'https://www.parse.ly/help/integration/jsonld/';
250
251 add_settings_field(
252 'meta_type',
253 'Metadata Format <div class="help-icons"></div>',
254 array( $this, 'print_select_tag' ),
255 Parsely::MENU_SLUG, 'optional_settings',
256 array(
257 'option_key' => 'meta_type',
258 'help_text' => $h,
259 'help_link' => $h_link,
260 // filter WordPress taxonomies under the hood that should not appear in dropdown.
261 'select_options' => array(
262 'json_ld' => 'json_ld',
263 'repeated_metas' => 'repeated_metas',
264 ),
265 'requires_recrawl' => true,
266 'multiple' => false,
267 )
268 );
269
270 // Content ID Prefix.
271 $h = 'If you use more than one content management system (e.g. ' .
272 'WordPress and Drupal), you may end up with duplicate content ' .
273 'IDs. Adding a Content ID Prefix will ensure the content IDs ' .
274 'from WordPress will not conflict with other content management ' .
275 'systems. We recommend using "WP-" for your prefix.';
276
277 $field_args = array(
278 'option_key' => 'content_id_prefix',
279 'optional_args' => array(
280 'placeholder' => 'WP-',
281 ),
282 'help_text' => $h,
283 'requires_recrawl' => true,
284 );
285 add_settings_field(
286 'content_id_prefix',
287 'Content ID Prefix <div class="help-icons"></div>',
288 array( $this, 'print_text_tag' ),
289 Parsely::MENU_SLUG, 'optional_settings',
290 $field_args
291 );
292
293 // Disable javascript.
294 $h = 'If you use a separate system for Javascript tracking ( Tealium / Segment / other tag manager solution ) ' .
295 'you may want to use that instead of having the plugin load the tracker. WARNING: disabling this option ' .
296 'will also disable the "Personalize Results" section of the recommended widget! We highly recommend leaving ' .
297 'this option set to "No"!';
298 add_settings_field(
299 'disable_javascript',
300 'Disable Javascript <div class="help-icons"></div>',
301 array( $this, 'print_binary_radio_tag' ),
302 Parsely::MENU_SLUG, 'optional_settings',
303 array(
304 'option_key' => 'disable_javascript',
305 'help_text' => $h,
306 'requires_recrawl' => false,
307 )
308 );
309
310 // Use top-level categories.
311 $h = 'wp-parsely will use the first category assigned to a post. ' .
312 'With this option selected, if you post a story to News > ' .
313 'National > Florida, wp-parsely will use the "News" for the ' .
314 'section name in your dashboard instead of "Florida".';
315 add_settings_field(
316 'use_top_level_cats',
317 'Use Top-Level Categories for Section <div class="help-icons"></div>',
318 array( $this, 'print_binary_radio_tag' ),
319 Parsely::MENU_SLUG, 'optional_settings',
320 array(
321 'option_key' => 'use_top_level_cats',
322 'help_text' => $h,
323 'requires_recrawl' => true,
324 )
325 );
326
327 // Allow use of custom taxonomy to populate articleSection in parselyPage; defaults to category.
328 $h = 'By default, the section value in your Parse.ly dashboard maps to a post\'s category. ' .
329 'You can optionally choose a custom taxonomy, if you\'ve created one, to ' .
330 'populate the section value instead. ';
331 add_settings_field(
332 'custom_taxonomy_section',
333 'Use Custom Taxonomy for Section <div class="help-icons"></div>',
334 array( $this, 'print_select_tag' ),
335 Parsely::MENU_SLUG, 'optional_settings',
336 array(
337 'option_key' => 'custom_taxonomy_section',
338 'help_text' => $h,
339 // filter WordPress taxonomies under the hood that should not appear in dropdown.
340 'select_options' => array_diff( get_taxonomies(), array( 'post_tag', 'nav_menu', 'author', 'link_category', 'post_format' ) ),
341 'requires_recrawl' => true,
342 )
343 );
344
345 // Use categories and custom taxonomies as tags.
346 $h = 'You can use this option to add all assigned categories and taxonomies to ' .
347 'your tags. For example, if you had a post assigned to ' .
348 'the categories: "Business/Tech", "Business/Social", your tags would include ' .
349 '"Business/Tech" and "Business/Social" in addition to your other tags.';
350 add_settings_field(
351 'cats_as_tags',
352 'Add Categories to Tags <div class="help-icons"></div>',
353 array( $this, 'print_binary_radio_tag' ),
354 Parsely::MENU_SLUG, 'optional_settings',
355 array(
356 'option_key' => 'cats_as_tags',
357 'help_text' => $h,
358 'requires_recrawl' => true,
359 )
360 );
361
362 // Track logged-in users.
363 $h = 'By default, wp-parsely will track the activity of users that ' .
364 'are logged into this site. You can change this setting to only ' .
365 'track the activity of anonymous visitors. Note: You will no ' .
366 'longer see the Parse.ly tracking code on your site if you ' .
367 'browse while logged in.';
368 add_settings_field(
369 'track_authenticated_users',
370 'Track Logged-in Users <div class="help-icons"></div>',
371 array( $this, 'print_binary_radio_tag' ),
372 Parsely::MENU_SLUG, 'optional_settings',
373 array(
374 'option_key' => 'track_authenticated_users',
375 'help_text' => $h,
376 'requires_recrawl' => true,
377 )
378 );
379
380 // Lowercase all tags.
381 $h = 'By default, wp-parsely will use lowercase versions of your ' .
382 'tags to correct for potential misspellings. You can change this ' .
383 'setting to ensure that tag names are used verbatim.';
384 add_settings_field(
385 'lowercase_tags',
386 'Lowercase All Tags <div class="help-icons"></div>',
387 array( $this, 'print_binary_radio_tag' ),
388 Parsely::MENU_SLUG, 'optional_settings',
389 array(
390 'option_key' => 'lowercase_tags',
391 'help_text' => $h,
392 'requires_recrawl' => true,
393 )
394 );
395
396 $h = 'wp-parsely uses http canonical URLs by default. If this needs to be forced to use https, set this option ' .
397 ' to true. Note: the default is fine for almost all publishers, it\'s unlikely you\'ll have to change this unless' .
398 ' directed to do so by a Parsely support rep.';
399 add_settings_field(
400 'force_https_canonicals',
401 'Force HTTPS canonicals <div class="help-icons"></div>',
402 array( $this, 'print_binary_radio_tag' ),
403 Parsely::MENU_SLUG, 'optional_settings',
404 array(
405 'option_key' => 'force_https_canonicals',
406 'help_text' => $h,
407 'requires_recrawl' => true,
408 )
409 );
410
411 // Allow use of custom taxonomy to populate articleSection in parselyPage; defaults to category.
412 $h = 'By default, Parsely only tracks the default post type as a post page. ' .
413 'If you want to track custom post types, select them here!';
414 add_settings_field(
415 'track_post_types',
416 'Post Types To Track <div class="help-icons"></div>',
417 array( $this, 'print_select_tag' ),
418 Parsely::MENU_SLUG, 'optional_settings',
419 array(
420 'option_key' => 'track_post_types',
421 'help_text' => $h,
422 // filter WordPress taxonomies under the hood that should not appear in dropdown.
423 'select_options' => get_post_types(),
424 'requires_recrawl' => true,
425 'multiple' => true,
426 )
427 );
428
429 // Allow use of custom taxonomy to populate articleSection in parselyPage; defaults to category.
430 $h = 'By default, Parsely only tracks the default page type as a non-post page. ' .
431 'If you want to track custom post types as non-post pages, select them here!';
432 add_settings_field(
433 'track_page_types',
434 'Page Types To Track <div class="help-icons"></div>',
435 array( $this, 'print_select_tag' ),
436 Parsely::MENU_SLUG, 'optional_settings',
437 array(
438 'option_key' => 'track_page_types',
439 'help_text' => $h,
440 // filter WordPress taxonomies under the hood that should not appear in dropdown.
441 'select_options' => get_post_types(),
442 'requires_recrawl' => true,
443 'multiple' => true,
444 )
445 );
446
447 // Dynamic tracking note.
448 add_settings_field(
449 'dynamic_tracking_note', 'Note: ',
450 array( $this, 'print_dynamic_tracking_note' ),
451 Parsely::MENU_SLUG, 'optional_settings'
452 );
453
454 }
455
456 /**
457 * Validate options from an array
458 *
459 * @category Function
460 * @package Parsely
461 * @param array $array Array of options to be sanitized.
462 * @param string $name Unused?.
463 */
464 public function validate_option_array( $array, $name ) {
465 $new_array = $array;
466 foreach ( $array as $key => $val ) {
467 $new_array[ $key ] = sanitize_text_field( $val );
468 }
469 return $new_array;
470 }
471
472 /**
473 * Validate the options provided by the user
474 *
475 * @category Function
476 * @package Parsely
477 * @param array $input Options from the settings page.
478 */
479 public function validate_options( $input ) {
480 if ( empty( $input['apikey'] ) ) {
481 add_settings_error(
482 Parsely::OPTIONS_KEY, 'apikey',
483 'Please specify the Site ID'
484 );
485 } else {
486 $input['apikey'] = strtolower( $input['apikey'] );
487 $input['apikey'] = sanitize_text_field( $input['apikey'] );
488 if ( strpos( $input['apikey'], '.' ) === false || strpos( $input['apikey'], ' ' ) !== false ) {
489 add_settings_error(
490 Parsely::OPTIONS_KEY, 'apikey',
491 'Your Parse.ly Site ID looks incorrect, it should look like "example.com".'
492 );
493 }
494 }
495 // these can't be null, if somebody accidentally deselected them just reset to default.
496 if ( ! isset( $input['track_post_types'] ) ) {
497 $input['track_post_types'] = array( 'post' );
498
499 }
500 if ( ! isset( $input['track_page_types'] ) ) {
501 $input['track_page_types'] = array( 'page' );
502 }
503 $input['track_post_types'] = $this->validate_option_array( $input['track_post_types'], 'track_post_types' );
504 $input['track_page_types'] = $this->validate_option_array( $input['track_page_types'], 'track_page_types' );
505
506 $input['api_secret'] = sanitize_text_field( $input['api_secret'] );
507 // Content ID prefix.
508 $input['content_id_prefix'] = sanitize_text_field( $input['content_id_prefix'] );
509 $input['custom_taxonomy_section'] = sanitize_text_field( $input['custom_taxonomy_section'] );
510
511 // Custom taxonomy as section.
512 // Top-level categories.
513 if ( 'true' !== $input['use_top_level_cats'] && 'false' !== $input['use_top_level_cats'] ) {
514 add_settings_error(
515 Parsely::OPTIONS_KEY, 'use_top_level_cats',
516 'Value passed for use_top_level_cats must be either "true" or "false".'
517 );
518 } else {
519 $input['use_top_level_cats'] = 'true' === $input['use_top_level_cats'] ? true : false;
520 }
521
522 // Child categories as tags.
523 if ( 'true' !== $input['cats_as_tags'] && 'false' !== $input['cats_as_tags'] ) {
524 add_settings_error(
525 Parsely::OPTIONS_KEY, 'cats_as_tags',
526 'Value passed for cats_as_tags must be either "true" or "false".'
527 );
528 } else {
529 $input['cats_as_tags'] = 'true' === $input['cats_as_tags'] ? true : false;
530 }
531
532 // Track authenticated users.
533 if ( 'true' !== $input['track_authenticated_users'] && 'false' !== $input['track_authenticated_users'] ) {
534 add_settings_error(
535 Parsely::OPTIONS_KEY, 'track_authenticated_users',
536 'Value passed for track_authenticated_users must be either "true" or "false".'
537 );
538 } else {
539 $input['track_authenticated_users'] = 'true' === $input['track_authenticated_users'] ? true : false;
540 }
541
542 // Lowercase tags.
543 if ( 'true' !== $input['lowercase_tags'] && 'false' !== $input['lowercase_tags'] ) {
544 add_settings_error(
545 Parsely::OPTIONS_KEY, 'lowercase_tags',
546 'Value passed for lowercase_tags must be either "true" or "false".'
547 );
548 } else {
549 $input['lowercase_tags'] = 'true' === $input['lowercase_tags'] ? true : false;
550 }
551
552 if ( 'true' !== $input['force_https_canonicals'] && 'false' !== $input['force_https_canonicals'] ) {
553 add_settings_error(
554 Parsely::OPTIONS_KEY, 'force_https_canonicals',
555 'Value passed for force_https_canonicals must be either "true" or "false".'
556 );
557 } else {
558 $input['force_https_canonicals'] = 'true' === $input['force_https_canonicals'] ? true : false;
559 }
560
561 if ( 'true' !== $input['disable_javascript'] && 'false' !== $input['disable_javascript'] ) {
562 add_settings_error(
563 Parsely::OPTIONS_KEY, 'disable_javascript',
564 'Value passed for disable_javascript must be either "true" or "false".'
565 );
566 } else {
567 $input['disable_javascript'] = 'true' === $input['disable_javascript'] ? true : false;
568 }
569
570 return $input;
571 }
572
573 /**
574 * Not doing anything here
575 *
576 * @category Function
577 * @package Parsely
578 */
579 public function print_required_settings() {
580 // We can optionally print some text here in the future, but we don't
581 // need to now.
582 }
583
584 /**
585 * Not doing anything here
586 *
587 * @category Function
588 * @package Parsely
589 */
590 public function print_optional_settings() {
591 // We can optionally print some text here in the future, but we don't
592 // need to now.
593 }
594
595 /**
596 * Adds a 'Settings' link to the Plugins screen in WP admin
597 *
598 * @category Function
599 * @package Parsely
600 * @param array $links The links to add.
601 */
602 public function add_plugin_meta_links( $links ) {
603 array_unshift( $links, '<a href="' . $this->get_settings_url() . '">' . __( 'Settings' ) . '</a>' );
604 return $links;
605 }
606
607 /**
608 * Display the admin warning if needed
609 *
610 * @category Function
611 * @package Parsely
612 */
613 public function display_admin_warning() {
614 $options = $this->get_options();
615 if ( ! isset( $options['apikey'] ) || empty( $options['apikey'] ) ) {
616 ?>
617 <div id='message' class='error'>
618 <p>
619 <strong>Parse.ly - Dash plugin is not active.</strong>
620 You need to
621 <a href='<?php echo esc_html( $this->get_settings_url() ); ?>'>
622 provide your Parse.ly Dash Site ID
623 </a>
624 before things get cooking.
625 </p>
626 </div>
627 <?php
628 }
629 }
630
631 /**
632 * Show our note about dynamic tracking
633 *
634 * @category Function
635 * @package Parsely
636 */
637 public function print_dynamic_tracking_note() {
638 printf(
639 'This plugin does not currently support dynamic tracking ( the tracking of multiple pageviews on a single page). Some common use-cases for dynamic tracking are slideshows or articles loaded via AJAX calls in single-page applications -- situations in which new content is loaded without a full page refresh. Tracking these events requires manually implementing additional JavaScript above <a href="%s">the standard Parse.ly include</a> that the plugin injects into your page source. Please consult <a href="%s">the Parse.ly documentation on dynamic tracking</a> for instructions on implementing dynamic tracking, or contact Parse.ly support (<a href="%s">support@parsely.com</a> ) for additional assistance.',
640 esc_url( 'http://www.parsely.com/help/integration/basic/' ),
641 esc_url( 'https://www.parsely.com/help/integration/dynamic/' ),
642 esc_url( 'mailto:support@parsely.com' )
643 );
644 }
645
646 /**
647 * End the code coverage ignore
648 *
649 * @codeCoverageIgnoreEnd
650 */
651
652 /**
653 * Actually inserts the code for the <meta name='parsely-page'> parameter within the <head></head> tag.
654 */
655 public function insert_parsely_page() {
656 $parsely_options = $this->get_options();
657
658 // If we don't have an API key or if we aren't supposed to show to logged in users, there's no need to proceed.
659 if ( empty( $parsely_options['apikey'] ) || ( ! $parsely_options['track_authenticated_users'] && $this->parsely_is_user_logged_in() ) ) {
660 return '';
661 }
662
663 global $wp_query;
664 global $post;
665 // Assign default values for LD+JSON.
666 // TODO: Maping of an install's post types to Parse.ly post types, namely page/post.
667 $parsely_page = array(
668 '@context' => 'http://schema.org',
669 '@type' => 'WebPage',
670 );
671
672 $current_url = $this->get_current_url();
673
674 if ( in_array( get_post_type(), $parsely_options['track_post_types'], true ) && 'publish' === $post->post_status ) {
675 $authors = $this->get_author_names( $post );
676 $category = $this->get_category_name( $post, $parsely_options );
677 $post_id = $parsely_options['content_id_prefix'] . (string) get_the_ID();
678
679 if ( has_post_thumbnail() ) {
680 $image_id = get_post_thumbnail_id();
681 $image_url = wp_get_attachment_image_src( $image_id );
682 $image_url = $image_url[0];
683 } else {
684 $image_url = $this->get_first_image( $post );
685 }
686
687 $tags = $this->get_tags( $post->ID );
688 if ( $parsely_options['cats_as_tags'] ) {
689 $tags = array_merge( $tags, $this->get_categories( $post->ID ) );
690 // Add custom taxonomy values.
691 $tags = array_merge( $tags, $this->get_custom_taxonomy_values( $post, $parsely_options ) );
692 }
693 // The function 'mb_strtolower' is not enabled by default in php, so this check
694 // falls back to the native php function 'strtolower' if necessary.
695 if ( function_exists( 'mb_strtolower' ) ) {
696 $lowercase_callback = 'mb_strtolower';
697 } else {
698 $lowercase_callback = 'strtolower';
699 }
700 if ( $parsely_options['lowercase_tags'] ) {
701 $tags = array_map( $lowercase_callback, $tags );
702 }
703 $tags = apply_filters( 'wp_parsely_post_tags', $tags, $post->ID );
704 $tags = array_map( array( $this, 'get_clean_parsely_page_value' ), $tags );
705 $tags = array_values( array_unique( $tags ) );
706
707 $parsely_page['@type'] = 'NewsArticle';
708 $parsely_page['mainEntityOfPage'] = array(
709 '@type' => 'WebPage',
710 '@id' => $this->get_current_url( 'post' ),
711 );
712 $parsely_page['headline'] = $this->get_clean_parsely_page_value( get_the_title() );
713 $parsely_page['url'] = $this->get_current_url( 'post' );
714 if ( ! empty( $image_url ) ) {
715 $parsely_page['thumbnailUrl'] = $image_url;
716 $parsely_page['image'] = array(
717 '@type' => 'ImageObject',
718 'url' => $image_url,
719 );
720 }
721 $parsely_page['dateCreated'] = gmdate( 'Y-m-d\TH:i:s\Z', get_post_time( 'U', true ) );
722 $parsely_page['datePublished'] = gmdate( 'Y-m-d\TH:i:s\Z', get_post_time( 'U', true ) );
723
724 if ( get_the_modified_date( 'U', true ) >= get_post_time( 'U', true ) ) {
725 $parsely_page['dateModified'] = gmdate( 'Y-m-d\TH:i:s\Z', get_the_modified_date( 'U', true ) );
726 } else {
727 // Use the post time as the earliest possible modification date.
728 $parsely_page['dateModified'] = gmdate( 'Y-m-d\TH:i:s\Z', get_post_time( 'U', true ) );
729 }
730
731 $author_objects = array();
732 foreach ( $authors as $author ) {
733 $author_tag = array(
734 '@type' => 'Person',
735 'name' => $author,
736 );
737 array_push( $author_objects, $author_tag );
738 }
739
740 $parsely_page['articleSection'] = $category;
741 $parsely_page['author'] = $author_objects;
742 $parsely_page['creator'] = $authors;
743 $parsely_page['keywords'] = $tags;
744
745 $parsely_page['publisher'] = array(
746 '@type' => 'Organization',
747 'name' => get_bloginfo( 'name' ),
748 );
749
750 } elseif ( in_array( get_post_type(), $parsely_options['track_page_types'], true ) && 'publish' === $post->post_status ) {
751 $parsely_page['headline'] = $this->get_clean_parsely_page_value( get_the_title() );
752 $parsely_page['url'] = $this->get_current_url( 'post' );
753 }
754 if ( is_front_page() ) {
755 $parsely_page['headline'] = $this->get_clean_parsely_page_value( get_bloginfo( 'name', 'raw' ) );
756 $parsely_page['url'] = get_home_url();
757 $parsely_page['@type'] = 'WebPage';
758 }
759 if ( is_archive() ) {
760 $parsely_page['@type'] = 'WebPage';
761 $parsely_page['url'] = $this->get_current_url();
762 if ( is_author() ) {
763 $author = get_user_by( 'slug', get_query_var( 'author_name' ) );
764 $parsely_page['headline'] = $this->get_clean_parsely_page_value( 'Author - ' . $author->display_name );
765 } else {
766 $parsely_page['headline'] = get_the_archive_title();
767 }
768 }
769
770 $parsely_page = apply_filters( 'after_set_parsely_page', $parsely_page, $post, $parsely_options );
771 include 'parsely-parsely-page.php';
772 return $parsely_page;
773 }
774
775 /**
776 * Inserts the JavaScript code required to send off beacon requests
777 */
778 public function insert_parsely_javascript() {
779 $parsely_options = $this->get_options();
780 // If we don't have an API key, there's no need to proceed.
781 if ( empty( $parsely_options['apikey'] ) || $parsely_options['disable_javascript'] ) {
782 return '';
783 }
784
785 global $post;
786 $display = true;
787 if ( in_array( get_post_type(), $parsely_options['track_post_types'], true ) && 'publish' !== $post->post_status ) {
788 $display = false;
789 }
790 if ( ! $parsely_options['track_authenticated_users'] && $this->parsely_is_user_logged_in() ) {
791 $display = false;
792 }
793 if ( ! in_array( get_post_type(), $parsely_options['track_post_types'], true ) && ! in_array( get_post_type(), $parsely_options['track_page_types'], true ) ) {
794 $display = false;
795 }
796 if ( $display ) {
797 include 'parsely-javascript.php';
798 }
799 }
800
801 /**
802 * Print out the select tags
803 *
804 * @param array $args The arguments for the select drop downs.
805 */
806 public function print_select_tag( $args ) {
807 $options = $this->get_options();
808 $name = $args['option_key'];
809 $select_options = $args['select_options'];
810 if ( isset( $args['multiple'] ) ) {
811 $multiple = $args['multiple'];
812 } else {
813 $multiple = false;
814 }
815 $selected = isset( $options[ $name ] ) ? $options[ $name ] : null;
816 $optional_args = isset( $args['optional_args'] ) ? $args['optional_args'] : array();
817 $id = esc_attr( $name );
818 $name = Parsely::OPTIONS_KEY . "[$id]";
819
820 if ( isset( $args['help_text'] ) ) {
821 echo '<div class="parsely-form-controls" data-has-help-text="true">';
822 }
823 if ( isset( $args['requires_recrawl'] ) ) {
824 echo '<div class="parsely-form-controls" data-requires-recrawl="true">';
825 }
826
827 if ( $multiple ) {
828 echo sprintf( "<select multiple='multiple' name='%s[]'id='%s'", esc_attr( $name ), esc_attr( $name ) );
829 } else {
830 echo sprintf( "<select name='%s' id='%s'", esc_attr( $name ), esc_attr( $name ) );
831 }
832
833 foreach ( $optional_args as $key => $val ) {
834 echo ' ' . esc_attr( $key ) . '="' . esc_attr( $val ) . '"';
835 }
836 echo '>';
837
838 foreach ( $select_options as $key => $val ) {
839 echo '<option value="' . esc_attr( $key ) . '" ';
840
841 if ( $multiple ) {
842 $selected = in_array( $val, $options[ $args['option_key'] ], true );
843 echo selected( $selected, true, false ) . '>';
844 } else {
845 echo selected( $selected, $key, false ) . '>';
846 }
847 echo esc_html( $val );
848 echo '</option>';
849 }
850 echo '</select>';
851
852 if ( isset( $args['help_text'] ) ) {
853 if ( isset( $args['help_link'] ) ) {
854 echo '<div class="help-text"> <p class="description">' .
855 sprintf( esc_html( $args['help_text'] ), '<a href="', esc_url( $args['help_link'] ), '">', '</a>' ) .
856 '</p></div>';
857 } else {
858 echo '<div class="help-text"> <p class="description">' . esc_html( $args['help_text'] ) . '</p></div>';
859 }
860 }
861 echo '</div>';
862 }
863
864 /**
865 * Print out the radio buttons
866 *
867 * @param array $args The arguments for the radio buttons.
868 */
869 public function print_binary_radio_tag( $args ) {
870 $options = $this->get_options();
871 $name = $args['option_key'];
872 $value = $options[ $name ];
873 $id = esc_attr( $name );
874 $name = Parsely::OPTIONS_KEY . "[$id]";
875
876 if ( isset( $args['help_text'] ) ) {
877 echo '<div class="parsely-form-controls" data-has-help-text="true">';
878 }
879 if ( isset( $args['requires_recrawl'] ) ) {
880 echo '<div class="parsely-form-controls" data-requires-recrawl="true">';
881 }
882
883 echo sprintf( "<input type='radio' name='%s' id='%s_true' value='true' ", esc_attr( $name ), esc_attr( $id ) );
884 echo checked( true === $value, true, false );
885 echo sprintf( " /> <label for='%s_true'>Yes</label> <input type='radio' name='%s' id='%s_false' value='false' ", esc_attr( $id ), esc_attr( $name ), esc_attr( $id ) );
886 echo checked( true !== $value, true, false );
887 echo sprintf( " /> <label for='%s_false'>No</label>", esc_attr( $id ) );
888
889 if ( isset( $args['help_text'] ) ) {
890 echo '<div class="help-text"><p class="description">' . esc_html( $args['help_text'] ) . '</p></div>';
891 }
892 echo '</div>';
893
894 }
895
896 /**
897 * Print out the radio buttons
898 *
899 * @param array $args The arguments for text tags.
900 */
901 public function print_text_tag( $args ) {
902 $options = $this->get_options();
903 $name = $args['option_key'];
904 $value = isset( $options[ $name ] ) ? $options[ $name ] : '';
905 $optional_args = isset( $args['optional_args'] ) ? $args['optional_args'] : array();
906 $id = esc_attr( $name );
907 $name = Parsely::OPTIONS_KEY . "[$id]";
908 $value = esc_attr( $value );
909
910 if ( isset( $args['help_text'] ) ) {
911 echo '<div class="parsely-form-controls" data-has-help-text="true">';
912 }
913 if ( isset( $args['requires_recrawl'] ) ) {
914 echo '<div class="parsely-form-controls" data-requires-recrawl="true">';
915 }
916
917 echo sprintf( "<input type='text' name='%s' id='%s' value='%s'", esc_attr( $name ), esc_attr( $id ), esc_attr( $value ) );
918 foreach ( $optional_args as $key => $val ) {
919 echo ' ' . esc_attr( $key ) . '="' . esc_attr( $val ) . '"';
920 }
921 if ( isset( $args['requires_recrawl'] ) ) {
922 echo ' data-requires-recrawl="true"';
923 }
924 echo ' />';
925
926 if ( isset( $args['help_text'] ) ) {
927 if ( isset( $args['help_link'] ) ) {
928 echo ' <div class="help-text" id="' .
929 esc_attr( $args['option_key'] ) .
930 '_help_text"><p class="description">' .
931 sprintf( esc_html( $args['help_text'] ), '<a href="', esc_url( $args['help_link'] ), '">', '</a>' ) .
932 '</p>' .
933 '</div>';
934 } else {
935 echo ' <div class="help-text" id="' .
936 esc_attr( $args['option_key'] ) .
937 '_help_text"><p class="description">' .
938 esc_html( $args['help_text'] ) . '</p>' .
939 '</div>';
940 }
941 }
942 }
943
944 /**
945 * Extracts a host ( not TLD ) from a URL
946 *
947 * @param string $url The url of the host.
948 */
949 private function get_host_from_url( $url ) {
950 if ( preg_match( '/^https?:\/\/( [^\/]+ )\/.*$/', $url, $matches ) ) {
951 return $matches[1];
952 } else {
953 return $url;
954 }
955 }
956
957 /**
958 * Returns the tags associated with this page or post
959 *
960 * @param string $post_id The id of the post you're trying to get tags for.
961 */
962 private function get_tags( $post_id ) {
963 $tags = array();
964 $wp_tags = wp_get_post_tags( $post_id );
965 foreach ( $wp_tags as $wp_tag ) {
966 array_push( $tags, $wp_tag->name );
967 }
968
969 return $tags;
970 }
971
972 /**
973 * Returns an array of all the child categories for the current post
974 *
975 * @param string $post_id The id of the post you're trying to get categories for.
976 * @param string $delimiter What character will delimit the categories.
977 */
978 private function get_categories( $post_id, $delimiter = '/' ) {
979 $tags = array();
980 $categories = get_the_category( $post_id );
981 foreach ( $categories as $category ) {
982 $hierarchy = get_category_parents( $category, false, $delimiter );
983 $hierarchy = rtrim( $hierarchy, '/' );
984 array_push( $tags, $hierarchy );
985 }
986 // take last element in the hierarchy, a string representing the full parent->child tree,
987 // and split it into individual category names.
988 $tags = explode( '/', end( $tags ) );
989 // remove uncategorized value from tags.
990 $tags = array_diff( $tags, array( 'Uncategorized' ) );
991 return $tags;
992 }
993
994 /**
995 * Safely returns options for the plugin by assigning defaults contained in optionDefaults. As soon as actual
996 * options are saved, they override the defaults. This prevents us from having to do a lot of isset() checking
997 * on variables.
998 */
999 private function get_options() {
1000 $options = get_option( Parsely::OPTIONS_KEY );
1001 if ( false === $options ) {
1002 $options = $this->option_defaults;
1003 } else {
1004 $options = array_merge( $this->option_defaults, $options );
1005 }
1006 return $options;
1007 }
1008
1009 /**
1010 * Returns a properly cleaned category/taxonomy value and will optionally use the top-level category/taxonomy value
1011 * if so instructed via the `use_top_level_cats` option.
1012 *
1013 * @param Parsely $post_obj The object for the post.
1014 * @param array $parsely_options The parsely options.
1015 */
1016 private function get_category_name( $post_obj, $parsely_options ) {
1017 $taxonomy_dropdown_choice = get_the_terms( $post_obj->ID, $parsely_options['custom_taxonomy_section'] );
1018 // Get top-level taxonomy name for chosen taxonomy and assign to $parent_name; it will be used
1019 // as the category value if 'use_top_level_cats' option is checked.
1020 // Assign as "Uncategorized" if no value is checked for the chosen taxonomy.
1021 if ( ! empty( $taxonomy_dropdown_choice ) ) {
1022 $first_term = array_shift( $taxonomy_dropdown_choice );
1023 $parent_name = $this->get_top_level_term( $first_term->term_id, $first_term->taxonomy );
1024 $child_name = $this->get_bottom_level_term( $post_obj->ID, $parsely_options['custom_taxonomy_section'] );
1025 $category = $parsely_options['use_top_level_cats'] ? $parent_name : $child_name;
1026 } else {
1027 $category = 'Uncategorized';
1028 }
1029 $category = apply_filters( 'wp_parsely_post_category', $category, $post_obj, $parsely_options );
1030 $category = $this->get_clean_parsely_page_value( $category );
1031 return $category;
1032 }
1033
1034 /**
1035 * Return the top-most category/taxonomy value in a hierarcy given a taxonomy value's ID
1036 * ( WordPress calls taxonomy values 'terms' ).
1037 *
1038 * @param string $term_id The id of the top level term.
1039 * @param string $taxonomy_name The name of the taxonomy.
1040 */
1041 private function get_top_level_term( $term_id, $taxonomy_name ) {
1042 $parent = get_term_by( 'id', $term_id, $taxonomy_name );
1043 while ( 0 !== $parent->parent ) {
1044 $parent = get_term_by( 'id', $parent->parent, $taxonomy_name );
1045 }
1046 return $parent->name;
1047 }
1048
1049 /**
1050 * Return the bottom-most category/taxonomy value in a hierarcy given a post ID
1051 * ( WordPress calls taxonomy values 'terms' ).
1052 *
1053 * @param string $post_id The post id you're interested in.
1054 * @param string $taxonomy_name The name of the taxonomy.
1055 */
1056 private function get_bottom_level_term( $post_id, $taxonomy_name ) {
1057 $terms = get_the_terms( $post_id, $taxonomy_name );
1058 $term_ids = wp_list_pluck( $terms, 'term_id' );
1059 $parents = array_filter( wp_list_pluck( $terms, 'parent' ) );
1060
1061 // Get array of IDs of terms which are not parents.
1062 $term_ids_not_parents = array_diff( $term_ids, $parents );
1063 // Get corresponding term objects, which are mapped to array index keys.
1064 $terms_not_parents = array_intersect_key( $terms, $term_ids_not_parents );
1065 // remove array index keys.
1066 $terms_not_parents_cleaned = array();
1067 foreach ( $terms_not_parents as $index => $value ) {
1068 array_push( $terms_not_parents_cleaned, $value );
1069 }
1070 // if you assign multiple child terms in a custom taxonomy, will only return the first.
1071 return $terms_not_parents_cleaned[0]->name;
1072 }
1073
1074 /**
1075 * Get all term values from custom taxonomies.
1076 *
1077 * @param Parsely $post_obj The post object.
1078 * @param Parsely $parsely_options The pparsely options.
1079 */
1080 private function get_custom_taxonomy_values( $post_obj, $parsely_options ) {
1081 // filter out default WordPress taxonomies.
1082 $all_taxonomies = array_diff( get_taxonomies(), array( 'post_tag', 'nav_menu', 'author', 'link_category', 'post_format' ) );
1083 $all_values = array();
1084
1085 if ( is_array( $all_taxonomies ) ) {
1086 foreach ( $all_taxonomies as $taxonomy ) {
1087 $custom_taxonomy_objects = get_the_terms( $post_obj->ID, $taxonomy );
1088 if ( is_array( $custom_taxonomy_objects ) ) {
1089 foreach ( $custom_taxonomy_objects as $custom_taxonomy_object ) {
1090 array_push( $all_values, $custom_taxonomy_object->name );
1091 }
1092 }
1093 }
1094 }
1095 return $all_values;
1096 }
1097
1098 /**
1099 * Returns a list of coauthors for a post assuming the coauthors plugin is
1100 * installed. Borrowed from
1101 * https://github.com/Automattic/Co-Authors-Plus/blob/master/template-tags.php#L3-35
1102 *
1103 * @param string $post_id The id of the post.
1104 */
1105 private function get_coauthor_names( $post_id ) {
1106 $coauthors = array();
1107 if ( class_exists( 'coauthors_plus' ) ) {
1108 global $post, $post_ID, $coauthors_plus, $wpdb;
1109
1110 $post_id = (int) $post_id;
1111 if ( ! $post_id && $post_ID ) {
1112 $post_id = $post_ID;
1113 }
1114
1115 if ( ! $post_id && $post ) {
1116 $post_id = $post->ID;
1117 }
1118
1119 if ( $post_id ) {
1120 $coauthor_terms = get_the_terms( $post_id, $coauthors_plus->coauthor_taxonomy );
1121
1122 if ( is_array( $coauthor_terms ) && ! empty( $coauthor_terms ) ) {
1123 foreach ( $coauthor_terms as $coauthor ) {
1124 $coauthor_slug = preg_replace( '#^cap\-#', '', $coauthor->slug );
1125 $post_author = $coauthors_plus->get_coauthor_by( 'user_nicename', $coauthor_slug );
1126 // In case the user has been deleted while plugin was deactivated.
1127 if ( ! empty( $post_author ) ) {
1128 $coauthors[] = $post_author;
1129 }
1130 }
1131 } elseif ( ! $coauthors_plus->force_guest_authors ) {
1132 if ( $post && $post_id === $post->ID ) {
1133 $post_author = get_userdata( $post->post_author );
1134 }
1135 if ( ! empty( $post_author ) ) {
1136 $coauthors[] = $post_author;
1137 }
1138 } // the empty else case is because if we force guest authors, we don't ever care what value wp_posts.post_author has.
1139 }
1140 }
1141 return $coauthors;
1142 }
1143
1144 /**
1145 * Determine author name from display name, falling back to firstname
1146 * lastname, then nickname and finally the nicename.
1147 *
1148 * @param string $author The author of the post.
1149 */
1150 private function get_author_name( $author ) {
1151 $author_name = $author->display_name;
1152 if ( ! empty( $author_name ) ) {
1153 return $author_name;
1154 }
1155
1156 $author_name = $author->user_firstname . ' ' . $author->user_lastname;
1157 if ( ' ' !== $author_name ) {
1158 return $author_name;
1159 }
1160
1161 $author_name = $author->nickname;
1162 if ( ! empty( $author_name ) ) {
1163 return $author_name;
1164 }
1165
1166 return $author->user_nicename;
1167 }
1168
1169 /**
1170 * Retrieve all the authors for a post as an array. Can include multiple
1171 * authors if coauthors plugin is in use.
1172 *
1173 * @param Parsely $post The post object.
1174 */
1175 private function get_author_names( $post ) {
1176 $authors = $this->get_coauthor_names( $post->ID );
1177 if ( empty( $authors ) ) {
1178 $authors = array( get_user_by( 'id', $post->post_author ) );
1179 }
1180 $authors = array_map( array( $this, 'get_author_name' ), $authors );
1181 $authors = apply_filters( 'wp_parsely_post_authors', $authors, $post );
1182 $authors = array_map( array( $this, 'get_clean_parsely_page_value' ), $authors );
1183 return $authors;
1184 }
1185
1186 /**
1187 * Sanitize content
1188 *
1189 * @param string $val The content you'd like sanitized.
1190 */
1191 private function get_clean_parsely_page_value( $val ) {
1192 if ( is_string( $val ) ) {
1193 $val = str_replace( "\n", '', $val );
1194 $val = str_replace( "\r", '', $val );
1195 $val = strip_tags( $val );
1196 $val = trim( $val );
1197 return $val;
1198 } else {
1199 return $val;
1200 }
1201 }
1202
1203
1204 /**
1205 * Get the URL of the plugin settings page
1206 */
1207 private function get_settings_url() {
1208 return admin_url( 'options-general.php?page=' . Parsely::MENU_SLUG );
1209 }
1210
1211
1212 /**
1213 * Get the URL of the current PHP script.
1214 * A fall-back implementation to determine permalink
1215 *
1216 * @param string $post The post object you're interested in.
1217 */
1218 private function get_current_url( $post = 'nonpost' ) {
1219 $options = $this->get_options();
1220 $scheme = ( $options['force_https_canonicals'] ? 'https://' : 'http://' );
1221
1222 if ( 'post' === $post ) {
1223 $permalink = get_permalink();
1224 $parsed_canonical = wp_parse_url( $permalink );
1225 $canonical = $scheme . $parsed_canonical['host'] . $parsed_canonical['path'];
1226 return $canonical;
1227 }
1228 $page_url = site_url( null, $scheme );
1229
1230 if ( isset( $_SERVER['SERVER_PORT'] ) ) { // Input var okay.
1231 $port_number = intval( $_SERVER['SERVER_PORT'] ); // Input var okay.
1232 }
1233 if ( 80 !== $port_number && 443 !== $port_number ) {
1234 $page_url .= ':' . $port_number;
1235 }
1236 if ( isset( $_SERVER['REQUEST_URI'] ) ) { // Input var okay.
1237 $page_url .= sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ); // Input var okay.
1238 }
1239 return $page_url;
1240 }
1241
1242 /**
1243 * Get the first image from a post
1244 * https://css-tricks.com/snippets/wordpress/get-the-first-image-from-a-post/
1245 *
1246 * @param Parsely $post The post object you're interested in.
1247 */
1248 public function get_first_image( $post ) {
1249 ob_start();
1250 ob_end_clean();
1251 if ( preg_match_all( '/<img.+src=[\'"]( [^\'"]+ )[\'"].*>/i', $post->post_content, $matches ) ) {
1252 $first_img = $matches[1][0];
1253 return $first_img;
1254 }
1255 return '';
1256 }
1257
1258 /**
1259 * Add parsely tracking to facebook instant articles
1260 *
1261 * @param type $registry The registry info for fbia.
1262 */
1263 public function insert_parsely_tracking_fbia( &$registry ) {
1264 $options = $this->get_options();
1265 $display_name = 'Parsely Analytics';
1266 $identifier = 'parsely-analytics-for-wordpress';
1267
1268 $embed_code = '<script>
1269 PARSELY = {
1270 autotrack: false,
1271 onload: function() {
1272 PARSELY.beacon.trackPageView({
1273 urlref: \'http://facebook.com/instantarticles\'
1274 });
1275 return true;
1276 }
1277 }
1278 </script>
1279 <div id="parsely-root" style="display: none">
1280 <span id="parsely-cfg" data-parsely-site="' . esc_attr( $options['apikey'] ) . '"></span>
1281 </div>
1282 <script>
1283 ( function(s, p, d ) {
1284 var h=d.location.protocol, i=p+"-"+s,
1285 e=d.getElementById( i), r=d.getElementById(p+"-root" ),
1286 u=h==="https:"?"d1z2jf7jlzjs58.cloudfront.net"
1287 :"static."+p+".com";
1288 if ( e ) return;
1289 e = d.createElement( s ); e.id = i; e.async = true;
1290 e.src = h+"//"+u+"/p.js"; r.appendChild( e );
1291 })( "script", "parsely", document );
1292 </script>
1293 <!-- END Parse.ly Include: Standard -->';
1294
1295 $registry[ $identifier ] = array(
1296 'name' => $display_name,
1297 'payload' => $embed_code,
1298 );
1299
1300 return $embed_code;
1301 }
1302
1303 /**
1304 * Add amp actions.
1305 */
1306 public function parsely_add_amp_actions() {
1307 add_filter( 'amp_post_template_analytics', array( $this, 'parsely_add_amp_analytics' ) );
1308 }
1309
1310 /**
1311 * Add amp analytics.
1312 *
1313 * @param type $analytics The analytics object you want to add.
1314 */
1315 public function parsely_add_amp_analytics( $analytics ) {
1316 $options = $this->get_options();
1317
1318 if ( empty( $options['apikey'] ) ) {
1319 return $analytics;
1320 }
1321
1322 $analytics['parsely'] = array(
1323 'type' => 'parsely',
1324 'attributes' => array(),
1325 'config_data' => array(
1326 'vars' => array(
1327 'apikey' => $options['apikey'],
1328 ),
1329 ),
1330 );
1331
1332 return $analytics;
1333 }
1334
1335 /**
1336 * Check to see if parsely user is logged in
1337 */
1338 public function parsely_is_user_logged_in() {
1339 // can't use $blog_id here because it futzes with the global $blog_id.
1340 $current_blog_id = get_current_blog_id();
1341 $current_user_id = get_current_user_id();
1342 return is_user_member_of_blog( $current_user_id, $current_blog_id );
1343 }
1344
1345 /**
1346 * Why is this here?
1347 */
1348 public function return_personalized_json() {
1349
1350 }
1351 }
1352
1353
1354
1355
1356 if ( class_exists( 'Parsely' ) ) {
1357 define( 'PARSELY_VERSION', Parsely::VERSION );
1358 $parsely = new Parsely();
1359 }
1360
1361 require 'class-parsely-recommended-widget.php';
1362