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

1,439 lines 47.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.13.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 = '1.13.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( 'pre_amp_render_post', 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 if ( in_array( get_post_type(), $parsely_options['track_post_types'], true ) && 'publish' === $post->post_status ) {
725 $authors = $this->get_author_names( $post );
726 $category = $this->get_category_name( $post, $parsely_options );
727 $post_id = $parsely_options['content_id_prefix'] . (string) get_the_ID();
728
729 if ( has_post_thumbnail() ) {
730 $image_id = get_post_thumbnail_id();
731 $image_url = wp_get_attachment_image_src( $image_id );
732 $image_url = $image_url[0];
733 } else {
734 $image_url = $this->get_first_image( $post );
735 }
736
737 $tags = $this->get_tags( $post->ID );
738 if ( $parsely_options['cats_as_tags'] ) {
739 $tags = array_merge( $tags, $this->get_categories( $post->ID ) );
740 // Add custom taxonomy values.
741 $tags = array_merge( $tags, $this->get_custom_taxonomy_values( $post, $parsely_options ) );
742 }
743 // The function 'mb_strtolower' is not enabled by default in php, so this check
744 // falls back to the native php function 'strtolower' if necessary.
745 if ( function_exists( 'mb_strtolower' ) ) {
746 $lowercase_callback = 'mb_strtolower';
747 } else {
748 $lowercase_callback = 'strtolower';
749 }
750 if ( $parsely_options['lowercase_tags'] ) {
751 $tags = array_map( $lowercase_callback, $tags );
752 }
753 $tags = apply_filters( 'wp_parsely_post_tags', $tags, $post->ID );
754 $tags = array_map( array( $this, 'get_clean_parsely_page_value' ), $tags );
755 $tags = array_values( array_unique( $tags ) );
756
757 $parsely_page['@type'] = 'NewsArticle';
758 $parsely_page['mainEntityOfPage'] = array(
759 '@type' => 'WebPage',
760 '@id' => $this->get_current_url( 'post' ),
761 );
762 $parsely_page['headline'] = $this->get_clean_parsely_page_value( get_the_title() );
763 $parsely_page['url'] = $this->get_current_url( 'post' );
764 if ( ! empty( $image_url ) ) {
765 $parsely_page['thumbnailUrl'] = $image_url;
766 $parsely_page['image'] = array(
767 '@type' => 'ImageObject',
768 'url' => $image_url,
769 );
770 }
771 $parsely_page['dateCreated'] = gmdate( 'Y-m-d\TH:i:s\Z', get_post_time( 'U', true ) );
772 $parsely_page['datePublished'] = gmdate( 'Y-m-d\TH:i:s\Z', get_post_time( 'U', true ) );
773
774 if ( get_the_modified_date( 'U', true ) >= get_post_time( 'U', true ) ) {
775 $parsely_page['dateModified'] = gmdate( 'Y-m-d\TH:i:s\Z', get_the_modified_date( 'U', true ) );
776 } else {
777 // Use the post time as the earliest possible modification date.
778 $parsely_page['dateModified'] = gmdate( 'Y-m-d\TH:i:s\Z', get_post_time( 'U', true ) );
779 }
780
781 $author_objects = array();
782 foreach ( $authors as $author ) {
783 $author_tag = array(
784 '@type' => 'Person',
785 'name' => $author,
786 );
787 array_push( $author_objects, $author_tag );
788 }
789
790 $parsely_page['articleSection'] = $category;
791 $parsely_page['author'] = $author_objects;
792 $parsely_page['creator'] = $authors;
793 $parsely_page['keywords'] = $tags;
794
795 $parsely_page['publisher'] = array(
796 '@type' => 'Organization',
797 'name' => get_bloginfo( 'name' ),
798 'logo' => array(
799 '@type' => 'ImageObject',
800 'url' => $parsely_options['logo'],
801 ),
802 );
803
804 } elseif ( in_array( get_post_type(), $parsely_options['track_page_types'], true ) && 'publish' === $post->post_status ) {
805 $parsely_page['headline'] = $this->get_clean_parsely_page_value( get_the_title() );
806 $parsely_page['url'] = $this->get_current_url( 'post' );
807 }
808 if ( is_front_page() ) {
809 $parsely_page['headline'] = $this->get_clean_parsely_page_value( get_bloginfo( 'name', 'raw' ) );
810 $parsely_page['url'] = get_home_url();
811 $parsely_page['@type'] = 'WebPage';
812 }
813 if ( is_archive() ) {
814 $parsely_page['@type'] = 'WebPage';
815 $parsely_page['url'] = $this->get_current_url();
816 if ( is_author() ) {
817 $author = get_user_by( 'slug', get_query_var( 'author_name' ) );
818 $parsely_page['headline'] = $this->get_clean_parsely_page_value( 'Author - ' . $author->display_name );
819 } else {
820 $parsely_page['headline'] = get_the_archive_title();
821 }
822 }
823
824 $parsely_page = apply_filters( 'after_set_parsely_page', $parsely_page, $post, $parsely_options );
825 include 'parsely-parsely-page.php';
826 return $parsely_page;
827 }
828
829 /**
830 * Inserts the JavaScript code required to send off beacon requests
831 */
832 public function insert_parsely_javascript() {
833 $parsely_options = $this->get_options();
834 // If we don't have an API key, there's no need to proceed.
835 if ( empty( $parsely_options['apikey'] ) || $parsely_options['disable_javascript'] ) {
836 return '';
837 }
838
839 global $post;
840 $display = true;
841 if ( in_array( get_post_type(), $parsely_options['track_post_types'], true ) && 'publish' !== $post->post_status ) {
842 $display = false;
843 }
844 if ( ! $parsely_options['track_authenticated_users'] && $this->parsely_is_user_logged_in() ) {
845 $display = false;
846 }
847 if ( ! in_array( get_post_type(), $parsely_options['track_post_types'], true ) && ! in_array( get_post_type(), $parsely_options['track_page_types'], true ) ) {
848 $display = false;
849 }
850 if ( $display ) {
851 include 'parsely-javascript.php';
852 }
853 }
854
855 /**
856 * Print out the select tags
857 *
858 * @param array $args The arguments for the select drop downs.
859 */
860 public function print_select_tag( $args ) {
861 $options = $this->get_options();
862 $name = $args['option_key'];
863 $select_options = $args['select_options'];
864 if ( isset( $args['multiple'] ) ) {
865 $multiple = $args['multiple'];
866 } else {
867 $multiple = false;
868 }
869 $selected = isset( $options[ $name ] ) ? $options[ $name ] : null;
870 $id = esc_attr( $name );
871 $name = Parsely::OPTIONS_KEY . "[$id]";
872
873 if ( isset( $args['help_text'] ) ) {
874 echo '<div class="parsely-form-controls" data-has-help-text="true">';
875 }
876 if ( isset( $args['requires_recrawl'] ) ) {
877 echo '<div class="parsely-form-controls" data-requires-recrawl="true">';
878 }
879
880 if ( $multiple ) {
881 echo sprintf( "<select multiple='multiple' name='%s[]'id='%s'", esc_attr( $name ), esc_attr( $name ) );
882 } else {
883 echo sprintf( "<select name='%s' id='%s'", esc_attr( $name ), esc_attr( $name ) );
884 }
885
886 echo '>';
887
888 foreach ( $select_options as $key => $val ) {
889 echo '<option value="' . esc_attr( $key ) . '" ';
890
891 if ( $multiple ) {
892 $selected = in_array( $val, $options[ $args['option_key'] ], true );
893 echo selected( $selected, true, false ) . '>';
894 } else {
895 echo selected( $selected, $key, false ) . '>';
896 }
897 echo esc_html( $val );
898 echo '</option>';
899 }
900 echo '</select>';
901
902 if ( isset( $args['help_text'] ) ) {
903 if ( isset( $args['help_link'] ) ) {
904 echo '<div class="help-text"> <p class="description">' .
905 sprintf( esc_html( $args['help_text'] ), '<a href="', esc_url( $args['help_link'] ), '">', '</a>' ) .
906 '</p></div>';
907 } else {
908 echo '<div class="help-text"> <p class="description">' . esc_html( $args['help_text'] ) . '</p></div>';
909 }
910 }
911 echo '</div>';
912 }
913
914 /**
915 * Print out the radio buttons
916 *
917 * @param array $args The arguments for the radio buttons.
918 */
919 public function print_binary_radio_tag( $args ) {
920 $options = $this->get_options();
921 $name = $args['option_key'];
922 $value = $options[ $name ];
923 $id = esc_attr( $name );
924 $name = Parsely::OPTIONS_KEY . "[$id]";
925
926 if ( isset( $args['help_text'] ) ) {
927 echo '<div class="parsely-form-controls" data-has-help-text="true">';
928 }
929 if ( isset( $args['requires_recrawl'] ) ) {
930 echo '<div class="parsely-form-controls" data-requires-recrawl="true">';
931 }
932
933 echo sprintf( "<input type='radio' name='%s' id='%s_true' value='true' ", esc_attr( $name ), esc_attr( $id ) );
934 echo checked( true === $value, true, false );
935 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 ) );
936 echo checked( true !== $value, true, false );
937 echo sprintf( " /> <label for='%s_false'>No</label>", esc_attr( $id ) );
938
939 if ( isset( $args['help_text'] ) ) {
940 echo '<div class="help-text"><p class="description">' . esc_html( $args['help_text'] ) . '</p></div>';
941 }
942 echo '</div>';
943
944 }
945
946 /**
947 * Print out the radio buttons
948 *
949 * @param array $args The arguments for text tags.
950 */
951 public function print_text_tag( $args ) {
952 $options = $this->get_options();
953 $name = $args['option_key'];
954 $value = isset( $options[ $name ] ) ? $options[ $name ] : '';
955 $optional_args = isset( $args['optional_args'] ) ? $args['optional_args'] : array();
956 $id = esc_attr( $name );
957 $name = Parsely::OPTIONS_KEY . "[$id]";
958 $value = esc_attr( $value );
959 $accepted_args = array( 'placeholder' );
960
961 if ( isset( $args['help_text'] ) ) {
962 echo '<div class="parsely-form-controls" data-has-help-text="true">';
963 }
964 if ( isset( $args['requires_recrawl'] ) ) {
965 echo '<div class="parsely-form-controls" data-requires-recrawl="true">';
966 }
967
968 echo sprintf( "<input type='text' name='%s' id='%s' value='%s'", esc_attr( $name ), esc_attr( $id ), esc_attr( $value ) );
969 foreach ( $optional_args as $key => $val ) {
970 if ( in_array( $key, $accepted_args, true ) ) {
971 echo ' ' . esc_attr( $key ) . '="' . esc_attr( $val ) . '"';
972 }
973 }
974 if ( isset( $args['requires_recrawl'] ) ) {
975 echo ' data-requires-recrawl="true"';
976 }
977 echo ' />';
978
979 if ( isset( $args['help_text'] ) ) {
980 if ( isset( $args['help_link'] ) ) {
981 echo ' <div class="help-text" id="' .
982 esc_attr( $args['option_key'] ) .
983 '_help_text"><p class="description">' .
984 sprintf( esc_html( $args['help_text'] ), '<a href="', esc_url( $args['help_link'] ), '">', '</a>' ) .
985 '</p>' .
986 '</div>';
987 } else {
988 echo ' <div class="help-text" id="' .
989 esc_attr( $args['option_key'] ) .
990 '_help_text"><p class="description">' .
991 esc_html( $args['help_text'] ) . '</p>' .
992 '</div>';
993 }
994 }
995 }
996
997 /**
998 * Returns default logo if one can be found
999 */
1000 private function get_logo_default() {
1001 $custom_logo_id = get_theme_mod( 'custom_logo' );
1002 if ( $custom_logo_id ) {
1003 $logo_attrs = wp_get_attachment_image_src( $custom_logo_id, 'full' );
1004 if ( $logo_attrs ) {
1005 return $logo_attrs[0];
1006 }
1007 }
1008
1009 // get_site_icon_url returns an empty string if one isn't found,
1010 // which is what we want to use as the default anyway.
1011 $site_icon_url = get_site_icon_url();
1012 return $site_icon_url;
1013 }
1014
1015 /**
1016 * Extracts a host ( not TLD ) from a URL
1017 *
1018 * @param string $url The url of the host.
1019 */
1020 private function get_host_from_url( $url ) {
1021 if ( preg_match( '/^https?:\/\/( [^\/]+ )\/.*$/', $url, $matches ) ) {
1022 return $matches[1];
1023 } else {
1024 return $url;
1025 }
1026 }
1027
1028 /**
1029 * Returns the tags associated with this page or post
1030 *
1031 * @param string $post_id The id of the post you're trying to get tags for.
1032 */
1033 private function get_tags( $post_id ) {
1034 $tags = array();
1035 $wp_tags = wp_get_post_tags( $post_id );
1036 foreach ( $wp_tags as $wp_tag ) {
1037 array_push( $tags, $wp_tag->name );
1038 }
1039
1040 return $tags;
1041 }
1042
1043 /**
1044 * Returns an array of all the child categories for the current post
1045 *
1046 * @param string $post_id The id of the post you're trying to get categories for.
1047 * @param string $delimiter What character will delimit the categories.
1048 */
1049 private function get_categories( $post_id, $delimiter = '/' ) {
1050 $tags = array();
1051 $categories = get_the_category( $post_id );
1052 foreach ( $categories as $category ) {
1053 $hierarchy = get_category_parents( $category, false, $delimiter );
1054 $hierarchy = rtrim( $hierarchy, '/' );
1055 array_push( $tags, $hierarchy );
1056 }
1057 // take last element in the hierarchy, a string representing the full parent->child tree,
1058 // and split it into individual category names.
1059 $tags = explode( '/', end( $tags ) );
1060 // remove uncategorized value from tags.
1061 $tags = array_diff( $tags, array( 'Uncategorized' ) );
1062 return $tags;
1063 }
1064
1065 /**
1066 * Safely returns options for the plugin by assigning defaults contained in optionDefaults. As soon as actual
1067 * options are saved, they override the defaults. This prevents us from having to do a lot of isset() checking
1068 * on variables.
1069 */
1070 private function get_options() {
1071 $options = get_option( Parsely::OPTIONS_KEY );
1072 if ( false === $options ) {
1073 $options = $this->option_defaults;
1074 } else {
1075 $options = array_merge( $this->option_defaults, $options );
1076 }
1077 return $options;
1078 }
1079
1080 /**
1081 * Returns a properly cleaned category/taxonomy value and will optionally use the top-level category/taxonomy value
1082 * if so instructed via the `use_top_level_cats` option.
1083 *
1084 * @param Parsely $post_obj The object for the post.
1085 * @param array $parsely_options The parsely options.
1086 */
1087 private function get_category_name( $post_obj, $parsely_options ) {
1088 $taxonomy_dropdown_choice = get_the_terms( $post_obj->ID, $parsely_options['custom_taxonomy_section'] );
1089 // Get top-level taxonomy name for chosen taxonomy and assign to $parent_name; it will be used
1090 // as the category value if 'use_top_level_cats' option is checked.
1091 // Assign as "Uncategorized" if no value is checked for the chosen taxonomy.
1092 if ( ! empty( $taxonomy_dropdown_choice ) ) {
1093 $first_term = array_shift( $taxonomy_dropdown_choice );
1094 $parent_name = $this->get_top_level_term( $first_term->term_id, $first_term->taxonomy );
1095 $child_name = $this->get_bottom_level_term( $post_obj->ID, $parsely_options['custom_taxonomy_section'] );
1096 $category = $parsely_options['use_top_level_cats'] ? $parent_name : $child_name;
1097 } else {
1098 $category = 'Uncategorized';
1099 }
1100 $category = apply_filters( 'wp_parsely_post_category', $category, $post_obj, $parsely_options );
1101 $category = $this->get_clean_parsely_page_value( $category );
1102 return $category;
1103 }
1104
1105 /**
1106 * Return the top-most category/taxonomy value in a hierarcy given a taxonomy value's ID
1107 * ( WordPress calls taxonomy values 'terms' ).
1108 *
1109 * @param string $term_id The id of the top level term.
1110 * @param string $taxonomy_name The name of the taxonomy.
1111 */
1112 private function get_top_level_term( $term_id, $taxonomy_name ) {
1113 $parent = get_term_by( 'id', $term_id, $taxonomy_name );
1114 while ( 0 !== $parent->parent ) {
1115 $parent = get_term_by( 'id', $parent->parent, $taxonomy_name );
1116 }
1117 return $parent->name;
1118 }
1119
1120 /**
1121 * Return the bottom-most category/taxonomy value in a hierarcy given a post ID
1122 * ( WordPress calls taxonomy values 'terms' ).
1123 *
1124 * @param string $post_id The post id you're interested in.
1125 * @param string $taxonomy_name The name of the taxonomy.
1126 */
1127 private function get_bottom_level_term( $post_id, $taxonomy_name ) {
1128 $terms = get_the_terms( $post_id, $taxonomy_name );
1129 $term_ids = wp_list_pluck( $terms, 'term_id' );
1130 $parents = array_filter( wp_list_pluck( $terms, 'parent' ) );
1131
1132 // Get array of IDs of terms which are not parents.
1133 $term_ids_not_parents = array_diff( $term_ids, $parents );
1134 // Get corresponding term objects, which are mapped to array index keys.
1135 $terms_not_parents = array_intersect_key( $terms, $term_ids_not_parents );
1136 // remove array index keys.
1137 $terms_not_parents_cleaned = array();
1138 foreach ( $terms_not_parents as $index => $value ) {
1139 array_push( $terms_not_parents_cleaned, $value );
1140 }
1141 // if you assign multiple child terms in a custom taxonomy, will only return the first.
1142 return $terms_not_parents_cleaned[0]->name;
1143 }
1144
1145 /**
1146 * Get all term values from custom taxonomies.
1147 *
1148 * @param Parsely $post_obj The post object.
1149 * @param Parsely $parsely_options The pparsely options.
1150 */
1151 private function get_custom_taxonomy_values( $post_obj, $parsely_options ) {
1152 // filter out default WordPress taxonomies.
1153 $all_taxonomies = array_diff( get_taxonomies(), array( 'post_tag', 'nav_menu', 'author', 'link_category', 'post_format' ) );
1154 $all_values = array();
1155
1156 if ( is_array( $all_taxonomies ) ) {
1157 foreach ( $all_taxonomies as $taxonomy ) {
1158 $custom_taxonomy_objects = get_the_terms( $post_obj->ID, $taxonomy );
1159 if ( is_array( $custom_taxonomy_objects ) ) {
1160 foreach ( $custom_taxonomy_objects as $custom_taxonomy_object ) {
1161 array_push( $all_values, $custom_taxonomy_object->name );
1162 }
1163 }
1164 }
1165 }
1166 return $all_values;
1167 }
1168
1169 /**
1170 * Returns a list of coauthors for a post assuming the coauthors plugin is
1171 * installed. Borrowed from
1172 * https://github.com/Automattic/Co-Authors-Plus/blob/master/template-tags.php#L3-35
1173 *
1174 * @param string $post_id The id of the post.
1175 */
1176 private function get_coauthor_names( $post_id ) {
1177 $coauthors = array();
1178 if ( class_exists( 'coauthors_plus' ) ) {
1179 global $post, $post_ID, $coauthors_plus, $wpdb;
1180
1181 $post_id = (int) $post_id;
1182 if ( ! $post_id && $post_ID ) {
1183 $post_id = $post_ID;
1184 }
1185
1186 if ( ! $post_id && $post ) {
1187 $post_id = $post->ID;
1188 }
1189
1190 if ( $post_id ) {
1191 $coauthor_terms = get_the_terms( $post_id, $coauthors_plus->coauthor_taxonomy );
1192
1193 if ( is_array( $coauthor_terms ) && ! empty( $coauthor_terms ) ) {
1194 foreach ( $coauthor_terms as $coauthor ) {
1195 $coauthor_slug = preg_replace( '#^cap\-#', '', $coauthor->slug );
1196 $post_author = $coauthors_plus->get_coauthor_by( 'user_nicename', $coauthor_slug );
1197 // In case the user has been deleted while plugin was deactivated.
1198 if ( ! empty( $post_author ) ) {
1199 $coauthors[] = $post_author;
1200 }
1201 }
1202 } elseif ( ! $coauthors_plus->force_guest_authors ) {
1203 if ( $post && $post_id === $post->ID ) {
1204 $post_author = get_userdata( $post->post_author );
1205 }
1206 if ( ! empty( $post_author ) ) {
1207 $coauthors[] = $post_author;
1208 }
1209 } // the empty else case is because if we force guest authors, we don't ever care what value wp_posts.post_author has.
1210 }
1211 }
1212 return $coauthors;
1213 }
1214
1215 /**
1216 * Determine author name from display name, falling back to firstname
1217 * lastname, then nickname and finally the nicename.
1218 *
1219 * @param string $author The author of the post.
1220 */
1221 private function get_author_name( $author ) {
1222 $author_name = $author->display_name;
1223 if ( ! empty( $author_name ) ) {
1224 return $author_name;
1225 }
1226
1227 $author_name = $author->user_firstname . ' ' . $author->user_lastname;
1228 if ( ' ' !== $author_name ) {
1229 return $author_name;
1230 }
1231
1232 $author_name = $author->nickname;
1233 if ( ! empty( $author_name ) ) {
1234 return $author_name;
1235 }
1236
1237 return $author->user_nicename;
1238 }
1239
1240 /**
1241 * Retrieve all the authors for a post as an array. Can include multiple
1242 * authors if coauthors plugin is in use.
1243 *
1244 * @param Parsely $post The post object.
1245 */
1246 private function get_author_names( $post ) {
1247 $authors = $this->get_coauthor_names( $post->ID );
1248 if ( empty( $authors ) ) {
1249 $authors = array( get_user_by( 'id', $post->post_author ) );
1250 }
1251 $authors = array_map( array( $this, 'get_author_name' ), $authors );
1252 $authors = apply_filters( 'wp_parsely_post_authors', $authors, $post );
1253 $authors = array_map( array( $this, 'get_clean_parsely_page_value' ), $authors );
1254 return $authors;
1255 }
1256
1257 /**
1258 * Sanitize content
1259 *
1260 * @param string $val The content you'd like sanitized.
1261 */
1262 private function get_clean_parsely_page_value( $val ) {
1263 if ( is_string( $val ) ) {
1264 $val = str_replace( "\n", '', $val );
1265 $val = str_replace( "\r", '', $val );
1266 $val = strip_tags( $val );
1267 $val = trim( $val );
1268 return $val;
1269 } else {
1270 return $val;
1271 }
1272 }
1273
1274
1275 /**
1276 * Get the URL of the plugin settings page
1277 */
1278 private function get_settings_url() {
1279 return admin_url( 'options-general.php?page=' . Parsely::MENU_SLUG );
1280 }
1281
1282
1283 /**
1284 * Get the URL of the current PHP script.
1285 * A fall-back implementation to determine permalink
1286 *
1287 * @param string $post The post object you're interested in.
1288 */
1289 private function get_current_url( $post = 'nonpost' ) {
1290 $options = $this->get_options();
1291 $scheme = ( $options['force_https_canonicals'] ? 'https://' : 'http://' );
1292
1293 if ( 'post' === $post ) {
1294 $permalink = get_permalink();
1295 $parsed_canonical = wp_parse_url( $permalink );
1296 $canonical = $scheme . $parsed_canonical['host'] . $parsed_canonical['path'];
1297 return $canonical;
1298 }
1299 $page_url = site_url( null, $scheme );
1300
1301 if ( isset( $_SERVER['SERVER_PORT'] ) ) { // Input var okay.
1302 $port_number = intval( $_SERVER['SERVER_PORT'] ); // Input var okay.
1303 }
1304 if ( 80 !== $port_number && 443 !== $port_number ) {
1305 $page_url .= ':' . $port_number;
1306 }
1307 if ( isset( $_SERVER['REQUEST_URI'] ) ) { // Input var okay.
1308 $page_url .= sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ); // Input var okay.
1309 }
1310 return $page_url;
1311 }
1312
1313 /**
1314 * Get the first image from a post
1315 * https://css-tricks.com/snippets/wordpress/get-the-first-image-from-a-post/
1316 *
1317 * @param Parsely $post The post object you're interested in.
1318 */
1319 public function get_first_image( $post ) {
1320 ob_start();
1321 ob_end_clean();
1322 if ( preg_match_all( '/<img.+src=[\'"]( [^\'"]+ )[\'"].*>/i', $post->post_content, $matches ) ) {
1323 $first_img = $matches[1][0];
1324 return $first_img;
1325 }
1326 return '';
1327 }
1328
1329 /**
1330 * Add parsely tracking to facebook instant articles
1331 *
1332 * @param type $registry The registry info for fbia.
1333 */
1334 public function insert_parsely_tracking_fbia( &$registry ) {
1335 $options = $this->get_options();
1336 $display_name = 'Parsely Analytics';
1337 $identifier = 'parsely-analytics-for-wordpress';
1338
1339 $embed_code = '<script>
1340 PARSELY = {
1341 autotrack: false,
1342 onload: function() {
1343 PARSELY.beacon.trackPageView({
1344 urlref: \'http://facebook.com/instantarticles\'
1345 });
1346 return true;
1347 }
1348 }
1349 </script>
1350 <div id="parsely-root" style="display: none">
1351 <span id="parsely-cfg" data-parsely-site="' . esc_attr( $options['apikey'] ) . '"></span>
1352 </div>
1353 <script>
1354 ( function(s, p, d ) {
1355 var h=d.location.protocol, i=p+"-"+s,
1356 e=d.getElementById( i), r=d.getElementById(p+"-root" ),
1357 u=h==="https:"?"d1z2jf7jlzjs58.cloudfront.net"
1358 :"static."+p+".com";
1359 if ( e ) return;
1360 e = d.createElement( s ); e.id = i; e.async = true;
1361 e.src = h+"//"+u+"/p.js"; r.appendChild( e );
1362 })( "script", "parsely", document );
1363 </script>
1364 <!-- END Parse.ly Include: Standard -->';
1365
1366 $registry[ $identifier ] = array(
1367 'name' => $display_name,
1368 'payload' => $embed_code,
1369 );
1370
1371 return $embed_code;
1372 }
1373
1374 /**
1375 * Add amp actions.
1376 */
1377 public function parsely_add_amp_actions() {
1378 $options = $this->get_options();
1379
1380 if ( $options['disable_amp'] ) {
1381 return '';
1382 }
1383
1384 add_filter( 'amp_post_template_analytics', array( $this, 'parsely_add_amp_analytics' ) );
1385 }
1386
1387 /**
1388 * Add amp analytics.
1389 *
1390 * @param type $analytics The analytics object you want to add.
1391 */
1392 public function parsely_add_amp_analytics( $analytics ) {
1393 $options = $this->get_options();
1394
1395 if ( empty( $options['apikey'] ) ) {
1396 return $analytics;
1397 }
1398
1399 $analytics['parsely'] = array(
1400 'type' => 'parsely',
1401 'attributes' => array(),
1402 'config_data' => array(
1403 'vars' => array(
1404 'apikey' => $options['apikey'],
1405 ),
1406 ),
1407 );
1408
1409 return $analytics;
1410 }
1411
1412 /**
1413 * Check to see if parsely user is logged in
1414 */
1415 public function parsely_is_user_logged_in() {
1416 // can't use $blog_id here because it futzes with the global $blog_id.
1417 $current_blog_id = get_current_blog_id();
1418 $current_user_id = get_current_user_id();
1419 return is_user_member_of_blog( $current_user_id, $current_blog_id );
1420 }
1421
1422 /**
1423 * Why is this here?
1424 */
1425 public function return_personalized_json() {
1426
1427 }
1428 }
1429
1430
1431
1432
1433 if ( class_exists( 'Parsely' ) ) {
1434 define( 'PARSELY_VERSION', Parsely::VERSION );
1435 $parsely = new Parsely();
1436 }
1437
1438 require 'class-parsely-recommended-widget.php';
1439