PluginProbe
Parse.ly / 2.0
Parse.ly v2.0
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 2.0, at wp-parsely.php

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