PluginProbe
Print, PDF & Email by PrintFriendly / 5.5.9
Print, PDF & Email by PrintFriendly v5.5.9
5.5.12 5.5.11 trunk 0.2 0.9 1.0 2.0 2.1.8 3.0 3.0.9 3.1.6 3.10.0 3.11.0 3.11.1 3.11.2 3.12.0 3.12.1 3.12.2 3.12.3 3.12.4 3.12.5 3.13.0 3.14.0 3.14.1 3.14.2 All 94 releases
printfriendly / pf.php

pf.php in Print, PDF & Email by PrintFriendly 5.5.9, at pf.php

1,838 lines 69.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore PSR1.Files.SideEffects.FoundWithSymbols
2
3 /*
4 Plugin Name: Print, PDF & Email by PrintFriendly
5 Plugin URI: https://www.printfriendly.com
6 Description: PrintFriendly & PDF button for your website. Optimizes your pages and brand for print, pdf, and email.
7 Name and URL are included to ensure repeat visitors and new visitors when printed versions are shared.
8 Version: 5.5.9
9 Author: Print, PDF, & Email by PrintFriendly
10 Author URI: https://www.printfriendly.com
11 License: GPLv2 or later
12 License URI: https://www.gnu.org/licenses/gpl-2.0.html
13 Domain Path: /languages
14 Text Domain: printfriendly
15 */
16
17 // phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
18 // phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
19 // phpcs:disable PSR2.Classes.PropertyDeclaration.VarUsed
20 // phpcs:disable PSR2.Classes.PropertyDeclaration.ScopeMissing
21 // phpcs:disable Squiz.Scope.MethodScope.Missing
22 // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
23
24 /**
25 * PrintFriendly WordPress plugin. Allows easy embedding of printfriendly.com buttons.
26 *
27 * @package PrintFriendly_WordPress
28 * @author PrintFriendly <support@printfriendly.com>
29 * @copyright Copyright (C) 2023, PrintFriendly
30 */
31
32 if (! class_exists('PrintFriendly_WordPress')) {
33 /**
34 * Class containing all the plugins functionality.
35 *
36 * @package PrintFriendly_WordPress
37 */
38 class PrintFriendly_WordPress
39 {
40 /**
41 * Current plugin version.
42 *
43 * @var string
44 */
45 var $plugin_version = '5.5.9';
46 /**
47 * The hook, used for text domain as well as hooks on pages and in get requests for admin.
48 *
49 * @var string
50 */
51 var $hook = 'printfriendly';
52 /**
53 * The option name, used throughout to refer to the plugins option and option group.
54 *
55 * @var string
56 */
57 var $option_name = 'printfriendly_option';
58 /**
59 * The plugins options, loaded on init containing all the plugins settings.
60 *
61 * @var array
62 */
63 var $options = array();
64 /**
65 * Database version, used to allow for easy upgrades to / additions in plugin options between plugin versions.
66 *
67 * @var int
68 */
69 var $db_version = 21;
70 /**
71 * Settings page, used within the plugin to reliably load the plugins admin JS and CSS files only on the admin page.
72 *
73 * @var string
74 */
75 var $settings_page = '';
76 /**
77 * List of all buttons with their attributes.
78 *
79 * @var array
80 */
81 private static $_buttons = array( // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
82 'buttons/printfriendly-pdf-email-button.png' => array( 'width' => 170, 'height' => 24 ),
83 'buttons/printfriendly-pdf-email-button-md.png' => array( 'width' => 194, 'height' => 30 ),
84 'buttons/printfriendly-pdf-email-button-notext.png' => array( 'width' => 110, 'height' => 30 ),
85 'buttons/printfriendly-pdf-button.png' => array( 'width' => 112, 'height' => 24 ),
86 'buttons/printfriendly-pdf-button-nobg.png' => array( 'width' => 112, 'height' => 24 ),
87 'buttons/printfriendly-pdf-button-nobg-md.png' => array( 'width' => 124, 'height' => 30 ),
88 'buttons/printfriendly-button.png' => array( 'width' => 112, 'height' => 24 ),
89 'buttons/printfriendly-button-nobg.png' => array( 'width' => 112, 'height' => 24 ),
90 'buttons/printfriendly-button-md.png' => array( 'width' => 124, 'height' => 30 ),
91 'buttons/printfriendly-button-lg.png' => array( 'width' => 154, 'height' => 28 ),
92 'buttons/print-button.png' => array( 'width' => 66, 'height' => 24 ),
93 'buttons/print-button-nobg.png' => array( 'width' => 66, 'height' => 24 ),
94 'buttons/print-button-gray.png' => array( 'width' => 66, 'height' => 24 ),
95 'icons/printfriendly-icon-sm.png' => array( 'width' => 17, 'height' => 17 ),
96 'icons/printfriendly-icon-md.png' => array( 'width' => 16, 'height' => 16 ),
97 'icons/printfriendly-icon-lg.png' => array( 'width' => 25, 'height' => 25 ),
98 );
99
100 /**
101 * Constructor
102 *
103 * @since 3.0
104 */
105 function __construct()
106 {
107 define('PRINTFRIENDLY_BASEPATH', dirname(__FILE__));
108 define('PRINTFRIENDLY_BASEURL', plugins_url('/', __FILE__));
109 // Retrieve the plugin options
110 $this->options = get_option($this->option_name);
111 // If the options array is empty, set defaults
112 if (! is_array($this->options)) {
113 $this->set_defaults();
114 }
115
116 /**
117 * Set page content selection option "WordPress Standard/Strict" to "WP Template"
118 */
119 if (isset($this->options['pf_algo']) && $this->options['pf_algo'] === 'ws') {
120 $this->options['pf_algo'] = 'wp';
121 update_option($this->option_name, $this->options);
122 }
123
124 // If the version number doesn't match, upgrade
125 if ($this->db_version > $this->options['db_version']) {
126 $this->upgrade();
127 }
128
129 $this->public_hooks();
130 if (is_admin()) {
131 $this->admin_hooks();
132 }
133 }
134
135 /**
136 * All public hooks.
137 */
138 function public_hooks()
139 {
140 add_action('wp_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ));
141 add_action('wp_head', array(&$this, 'front_head'));
142
143 // automaticaly add the link
144 add_filter('the_content', array(&$this, 'show_link_on_content'));
145 add_filter('the_excerpt', array(&$this, 'show_link_on_excerpt'));
146
147 add_action('the_content', array(&$this, 'add_pf_content_class_around_content_hook'));
148 }
149
150 /**
151 * Load front end dependencies.
152 */
153 function wp_enqueue_scripts()
154 {
155 wp_enqueue_script('jquery');
156 }
157
158 /**
159 * All admin hooks.
160 */
161 function admin_hooks()
162 {
163 // Hook into init for registration of the option and the language files
164 add_action('admin_init', array(&$this, 'init'));
165 // Register the settings page
166 add_action('admin_menu', array(&$this, 'add_config_page'));
167 // Enqueue the needed scripts and styles
168 add_action('admin_enqueue_scripts', array(&$this, 'admin_enqueue_scripts'));
169 // Register a link to the settings page on the plugins overview page
170 add_filter('plugin_action_links', array(&$this, 'filter_plugin_actions'), 10, 2);
171 add_filter('plugin_row_meta', array(&$this, 'additional_links'), 10, 2);
172 add_filter('wp_dropdown_cats', array( &$this, 'wp_dropdown_cats_multiple' ), 10, 2);
173 }
174
175 /**
176 * Adds multiple select support for category dropdown.
177 *
178 * @since 4.2
179 **/
180 function wp_dropdown_cats_multiple($output, $attributes)
181 {
182 if (isset($attributes['multiple']) && $attributes['multiple']) {
183 $output = preg_replace('/^<select/i', '<select multiple', $output);
184 $output = str_replace("name='{$attributes['name']}'", "name='{$attributes['name']}[]'", $output);
185 foreach (array_map('trim', explode(',', $attributes['selected'])) as $value) {
186 $output = str_replace("value=\"{$value}\"", "value=\"{$value}\" selected", $output);
187 }
188 }
189 return $output;
190 }
191
192 /**
193 * Adds wraps content in pf-content class to help Printfriendly algo determine the content
194 *
195 * @since 3.2.8
196 **/
197 function add_pf_content_class_around_content_hook($content = false)
198 {
199 if ($this->is_enabled() && $this->is_wp_algo_on($content)) {
200 add_action('wp_footer', array(&$this, 'print_script_footer'));
201 return '<div class="pf-content">' . $content . '</div>';
202 } else {
203 return $content;
204 }
205 }
206
207 /**
208 * Override to check if print-only command is being used
209 *
210 * @since 3.3.0
211 **/
212 function print_only_override($content)
213 {
214 $pattern = '/class\s*?=\s*?(["\']|["\']([^"\']*?)\s)print-only(["\']|\s([^"\']*?)["\'])/';
215 $pf_pattern = '/class\s*?=\s*?(["\']|["\']([^"\']*?)\s)pf-content(["\']|\s([^"\']*?)["\'])/';
216 return ( preg_match($pattern, $content) || preg_match($pf_pattern, $content) );
217 }
218
219 /**
220 * Check if WP Algorithm is selected and content doesn't use print-only
221 *
222 * @since 3.5.4
223 **/
224 function is_wp_algo_on($content)
225 {
226 return ! class_exists('WooCommerce') && $content && $this->getVal('pf_algo') === 'wp' && ! $this->print_only_override($content);
227 }
228
229 /**
230 * PHP 4 Compatible Constructor
231 *
232 * @since 3.0
233 */
234 function PrintFriendly_WordPress()
235 {
236 $this->__construct();
237 }
238
239 /**
240 * Check if button should be visible on particular page
241 */
242 function is_enabled()
243 {
244 if (
245 ( is_page() && 'on' === $this->getVal('show_on_pages') )
246 || ( is_home() && 'on' === $this->getVal('show_on_homepage') )
247 || ( is_tax() && 'on' === $this->getVal('show_on_taxonomies') && $this->category_included() )
248 || ( is_category() && 'on' === $this->getVal('show_on_categories') && $this->category_included() )
249 || ( is_single() && 'on' === $this->getVal('show_on_posts') && $this->category_included() )
250 ) {
251 return true;
252 }
253 return false;
254 }
255
256 /**
257 * Determines the CSS rules to return for the `printfriendly` class.
258 *
259 * @since 5.2.5
260 */
261 function get_css_rules_for_button()
262 {
263 $css = array( 'z-index: 1000' );
264
265 if ($this->getVal('button_alignment_method') === 'default') {
266 $css[] = 'display: flex';
267 $css[] = sprintf(
268 'margin: %dpx %dpx %dpx %dpx',
269 $this->getVal('margin_top', 0),
270 $this->getVal('margin_right', 0),
271 $this->getVal('margin_bottom', 0),
272 $this->getVal('margin_left', 0)
273 );
274 } else {
275 $css[] = 'position: relative';
276 }
277
278 return implode('; ', $css);
279 }
280
281 /**
282 * Determines the CSS rules to return for the front end.
283 *
284 * @since 5.2.5
285 */
286 function get_main_css()
287 {
288 if ($this->getVal('button_alignment_method') === 'css') {
289 return '
290 @media print {
291 .printfriendly {
292 display: none;
293 }
294 }
295 ';
296 }
297
298 return sprintf(
299 '
300 @media screen {
301 .printfriendly {
302 %s
303 }
304 .printfriendly a, .printfriendly a:link, .printfriendly a:visited, .printfriendly a:hover, .printfriendly a:active {
305 font-weight: 600;
306 cursor: pointer;
307 text-decoration: none;
308 border: none;
309 -webkit-box-shadow: none;
310 -moz-box-shadow: none;
311 box-shadow: none;
312 outline:none;
313 font-size: %dpx !important;
314 color: %s !important;
315 }
316 .printfriendly.pf-alignleft {
317 %s
318 }
319 .printfriendly.pf-alignright {
320 %s
321 }
322 .printfriendly.pf-aligncenter {
323 justify-content: center;
324 %s
325 }
326 }
327
328 .pf-button-img {
329 border: none;
330 -webkit-box-shadow: none;
331 -moz-box-shadow: none;
332 box-shadow: none;
333 padding: 0;
334 margin: 0;
335 display: inline;
336 vertical-align: middle;
337 }
338
339 img.pf-button-img + .pf-button-text {
340 margin-left: 6px;
341 }
342
343 @media print {
344 .printfriendly {
345 display: none;
346 }
347 }
348 ',
349 $this->get_css_rules_for_button(),
350 $this->getVal('text_size'),
351 $this->getVal('text_color'),
352 $this->getVal('button_alignment_method') === 'default' ? 'justify-content: start;' : 'float: left;',
353 $this->getVal('button_alignment_method') === 'default' ? 'justify-content: end;' : 'float: right;',
354 $this->getVal('button_alignment_method') === 'default' ? '' : 'display: flex; align-items: center;'
355 );
356 }
357
358 /**
359 * Returns the custom CSS (including the style tag) for content positioning.
360 *
361 * @since 5.2.5
362 */
363 function get_content_position_css_tag()
364 {
365 $css = $this->getVal('content_position_css');
366 if (! empty($css)) {
367 return sprintf('<style type="text/css" id="pf-button-css">%s</style>', $css);
368 }
369 return null;
370 }
371
372 /**
373 * Prints the PrintFriendly button CSS, in the header.
374 *
375 * @since 3.0
376 */
377 function front_head()
378 {
379 if (! $this->is_enabled()) {
380 return;
381 }
382
383 if ($this->getVal('enable_css') !== 'on') {
384 return;
385 }
386 ?>
387 <style type="text/css" id="pf-main-css">
388 <?php echo $this->get_main_css(); ?>
389 </style>
390
391 <?php echo $this->get_content_position_css_tag(); ?>
392
393 <style type="text/css" id="pf-excerpt-styles">
394 .pf-button.pf-button-excerpt {
395 display: none;
396 }
397 </style>
398
399 <?php
400 }
401
402 /**
403 * Prints the PrintFriendly JavaScript, in the footer, and loads it asynchronously.
404 *
405 * @since 3.0
406 */
407 function print_script_footer()
408 {
409 $tagline = $this->getVal('tagline');
410 $image_url = $this->getVal('image_url');
411 if ($this->getVal('logo') === 'favicon') {
412 $tagline = '';
413 $image_url = '';
414 }
415
416 echo $this->getSelectorsFromCustomCSS();
417
418 // Currently we use v3 for both: normal and password protected sites
419 ?>
420 <script type="text/javascript" id="pf_script">
421 <?php echo $this->get_analytics_code(); ?>
422 var pfHeaderImgUrl = '<?php echo esc_js(esc_url($image_url)); ?>';
423 var pfHeaderTagline = '<?php echo esc_js($tagline); ?>';
424 var pfdisableClickToDel = '<?php echo esc_js($this->getVal('click_to_delete')); ?>';
425 var pfImagesSize = '<?php echo esc_js($this->getVal('images-size')); ?>';
426 var pfImageDisplayStyle = '<?php echo esc_js($this->getVal('image-style')); ?>';
427 var pfEncodeImages = '<?php echo esc_js($this->getVal('password_protected') === 'yes' ? 1 : 0); ?>';
428 var pfShowHiddenContent = '<?php echo esc_js($this->getVal('show_hidden_content') === 'yes' ? 1 : 0); ?>';
429 var pfDisableEmail = '<?php echo esc_js($this->getVal('email')); ?>';
430 var pfDisablePDF = '<?php echo esc_js($this->getVal('pdf')); ?>';
431 var pfDisablePrint = '<?php echo esc_js($this->getVal('print')); ?>';
432
433 <?php
434 echo $this->get_custom_css_js_var();
435 ?>
436
437 var pfPlatform = 'WordPress';
438
439 (function($){
440 $(document).ready(function(){
441 if($('.pf-button-content').length === 0){
442 $('style#pf-excerpt-styles').remove();
443 }
444 });
445 })(jQuery);
446 </script>
447 <script defer src='https://cdn.printfriendly.com/printfriendly.js'></script>
448 <?php
449 echo $this->get_custom_css_tag();
450 ?>
451
452 <?php
453 }
454
455 /**
456 * Used as a filter for the_content.
457 *
458 * @since ?
459 *
460 * @param string $content The content of the post, when the function is used as a filter.
461 *
462 * @return string The content with the button added to the content.
463 */
464 function show_link_on_content($content)
465 {
466 $content = $this->show_link($content, true);
467 return $content;
468 }
469
470 /**
471 * Used as a filter for the_excerpt.
472 *
473 * @since ?
474 *
475 * @param string $content The content of the post, when the function is used as a filter.
476 *
477 * @return string The content with the button added to the content.
478 */
479 function show_link_on_excerpt($content)
480 {
481 return $this->show_link($content, false);
482 }
483
484 /**
485 * Used to generate the the final content as per the required placement.
486 *
487 * @since 3.0
488 *
489 * @param string $content The content of the post, when the function is used as a filter.
490 * @param bool $on_content True if showing on the_content and False if showing on the_excerpt.
491 *
492 * @return string $button or $content with the button added to the content when appropriate, just the content when button shouldn't be added or just button when called manually.
493 */
494 private function show_link($content = false, $on_content = true)
495 {
496 if ($this->is_enabled() && $content) {
497 $button = $this->getButton(false, $on_content ? 'pf-button-content' : 'pf-button-excerpt');
498 // Hook the script call now, so it only get's loaded when needed, and need is determined by the user calling pf_button
499 add_action('wp_footer', array(&$this, 'print_script_footer'));
500 if ($this->getVal('content_placement') === 'before') {
501 return $button . $content;
502 } else {
503 return $content . $button;
504 }
505 }
506 return $content;
507 }
508
509 /**
510 * Returns the analytics function to be included in the front end.
511 *
512 * @since 5.1
513 *
514 * @return string
515 */
516 private function get_analytics_code()
517 {
518 $return = null;
519 if ($this->google_analytics_enabled()) {
520 $code = apply_filters(
521 'printfriendly_analytics_code',
522 "
523 if(typeof(_gaq) === 'function') {
524 _gaq.push(['_trackEvent','PRINTFRIENDLY', 'print', title]);
525 }else if(typeof(ga) === 'function') {
526 ga('send', 'event','PRINTFRIENDLY', 'print', title);
527 }else if(typeof(gtag) === 'function') {
528 gtag('event', 'printfriendly_button_click', {'event_category': 'printfriendly', 'event_label': title})
529 }else if(typeof(dataLayer) === 'object') {
530 dataLayer.push({
531 'event': 'printfriendly_button_click',
532 'pageTitle': title
533 })
534 }
535 "
536 );
537 $return = "
538 function pfTrackEvent(title) {
539 $code
540 }
541 ";
542 }
543 return $return;
544 }
545
546 /**
547 * Returns the HTML of the button.
548 *
549 * This can be called publicly from `pf_show_link()`.
550 *
551 * @since 3.3.8
552 *
553 * @param bool $add_footer_script Whether to add the script in the footer.
554 * @param string $add_class Additional class for the HTML button div.
555 * @param bool $print_whole_page Whether to print the whole page or follow default behaviour.
556 *
557 * @return Printfriendly Button HTML
558 */
559 public function getButton($add_footer_script = false, $add_class = '', $print_whole_page = false)
560 {
561 if ($add_footer_script) {
562 add_action('wp_footer', array(&$this, 'print_script_footer'));
563 }
564
565 $onclick = array();
566 $href = '#';
567
568 if ($print_whole_page) {
569 $onclick[] = 'window.print()';
570 } else {
571 if (is_singular()) {
572 $onclick[] = 'window.print()';
573 } else {
574 $href = add_query_arg('pfstyle', 'wp', get_permalink());
575 }
576 }
577
578 if ($this->google_analytics_enabled()) {
579 $onclick[] = "pfTrackEvent('" . esc_attr(get_the_title()) . "')";
580 }
581
582 $align_class = '';
583 $position = $this->getVal('content_position');
584
585 // none option has been removed and translates to left
586 if ('none' === $position) {
587 $position = 'left';
588 }
589 $align_class = 'pf-align' . $position;
590 $button = sprintf(
591 '<div class="printfriendly pf-button %s %s">
592 <a href="%s" rel="nofollow" onclick="%s" title="Printer Friendly, PDF & Email">
593 %s
594 </a>
595 </div>',
596 sanitize_html_class($add_class),
597 sanitize_html_class($align_class),
598 esc_attr(esc_url($href)),
599 ! empty($onclick) ? esc_attr(implode('; ', $onclick)) . '; return false;' : '',
600 $this->button()
601 );
602 $button = apply_filters('printfriendly_button', $button);
603 return $button;
604 }
605
606 /**
607 * Returns the custom selector CSS to be added.
608 *
609 * @since 5.0
610 * @returns string
611 */
612 private function getSelectorsFromCustomCSS()
613 {
614 $css = '';
615 if ($this->getVal('pf_algo') === 'css') {
616 $attributes = array();
617 $names = array( 'author', 'date', 'content', 'title', 'primaryImage' );
618 $data = '';
619 foreach ($names as $name) {
620 $selector = $this->getVal("css-{$name}");
621 $suffix = $name === 'content' ? 'Selectors' : 'Selector';
622 if (! empty($selector)) {
623 $data .= "{$name}{$suffix}=$selector;";
624 }
625 }
626 $fallback = $this->getVal('pf_algo_css_content');
627 if ($data) {
628 $css = sprintf('<printfriendly-options data-selectors="%s" data-fallback-strategy="%s"></printfriendly-options>', esc_attr($data), esc_attr($fallback));
629 }
630 }
631 return $css;
632 }
633
634
635 /**
636 * Checks if GA is enabled.
637 *
638 * @since 3.2.9
639 * @returns if google analytics enabled
640 */
641 function google_analytics_enabled()
642 {
643 return $this->getVal('enable_google_analytics') === 'yes';
644 }
645
646 /**
647 * Filter posts by category.
648 *
649 * @since 3.2.2
650 * @return boolean true if post belongs to category selected for button display
651 */
652 function category_included()
653 {
654 return !empty($this->getVal('show_on_cat')) ? in_category(explode(',', $this->getVal('show_on_cat'))) : true;
655 }
656
657 /**
658 * Register the textdomain and the options array along with the validation function
659 *
660 * @since 3.0
661 */
662 function init()
663 {
664 // Allow for localization
665 load_plugin_textdomain('printfriendly', false, basename(dirname(__FILE__)) . '/languages');
666 // Register our option array
667 register_setting($this->option_name, $this->option_name, array( 'sanitize_callback' => array(&$this, 'options_validate') ));
668 }
669
670 /**
671 * Validate the saved options.
672 *
673 * @since 3.0
674 * @param array $input with unvalidated options.
675 * @return array $valid_input with validated options.
676 */
677 function options_validate($input)
678 {
679 // Prevent CSRF attack
680 check_admin_referer('pf-options', 'pf-nonce');
681 $valid_input = $input;
682
683 // Section 1 options
684 if (
685 ! isset($input['button_type']) || ! in_array(
686 $input['button_type'],
687 array(
688 'buttons/printfriendly-pdf-email-button.png',
689 'buttons/printfriendly-pdf-email-button-md.png',
690 'buttons/printfriendly-pdf-email-button-notext.png', // buttongroup1
691 'buttons/printfriendly-pdf-button.png',
692 'buttons/printfriendly-pdf-button-nobg.png',
693 'buttons/printfriendly-pdf-button-nobg-md.png', // buttongroup2
694 'buttons/printfriendly-button.png',
695 'buttons/printfriendly-button-nobg.png',
696 'buttons/printfriendly-button-md.png',
697 'buttons/printfriendly-button-lg.png', // buttongroup3
698 'buttons/print-button.png',
699 'buttons/print-button-nobg.png',
700 'buttons/print-button-gray.png', // buttongroup3
701 'custom-button', // custom
702 ),
703 true
704 )
705 ) {
706 $valid_input['button_type'] = 'printfriendly-pdf-button.png';
707 }
708
709 if (
710 ! isset($input['custom_button_icon']) || ! in_array(
711 $input['custom_button_icon'],
712 array(
713 'https://cdn.printfriendly.com/icons/printfriendly-icon-sm.png',
714 'https://cdn.printfriendly.com/icons/printfriendly-icon-md.png',
715 'https://cdn.printfriendly.com/icons/printfriendly-icon-lg.png',
716 'custom-image',
717 'no-image',
718 ),
719 true
720 )
721 ) {
722 $valid_input['custom_button_icon'] = 'https://cdn.printfriendly.com/icons/printfriendly-icon-md.png';
723 }
724
725 // if a custom image is not being chosen, reset it in case it existed in the past.
726 if (isset($valid_input['custom_button_icon']) && $valid_input['custom_button_icon'] !== 'custom-image') {
727 $valid_input['custom_image'] = '';
728 }
729
730 // @todo custom image url validation
731 if (! isset($input['custom_image']) || empty($input['custom_image'])) {
732 $valid_input['custom_image'] = '';
733 }
734
735 if (! isset($input['custom_button_text'])) {
736 $valid_input['custom_button_text'] = 'custom-text';
737 }
738
739 // @todo validate optional custom text
740 if (! isset($input['custom_text'])) {
741 $valid_input['custom_text'] = 'Print Friendly';
742 } else {
743 $valid_input['custom_text'] = $this->esc_html_if_needed($valid_input['custom_text']);
744 }
745
746 // Custom button selected, but no url nor text given, reset button type to default
747 if ('custom-button' === $valid_input['button_type'] && ( '' === $valid_input['custom_image'] && '' === $input['custom_text'] )) {
748 $valid_input['button_type'] = 'buttons/printfriendly-pdf-button.png';
749 add_settings_error($this->option_name, 'invalid_custom_image', __('No valid custom image url received, please enter a valid url to use a custom image.', 'printfriendly'));
750 }
751
752 $valid_input['text_size'] = (int) $input['text_size'];
753 if (! isset($valid_input['text_size']) || 0 === $valid_input['text_size']) {
754 $valid_input['text_size'] = 14;
755 } elseif (25 < $valid_input['text_size'] || 9 > $valid_input['text_size']) {
756 $valid_input['text_size'] = 14;
757 add_settings_error($this->option_name, 'invalid_text_size', __('The text size you entered is invalid, please stay between 9px and 25px', 'printfriendly'));
758 }
759
760 if (! isset($input['text_color'])) {
761 $valid_input['text_color'] = $this->getVal('text_color');
762 } elseif (! preg_match('/^#[a-f0-9]{3,6}$/i', $input['text_color'])) {
763 // Revert to previous setting and throw error.
764 $valid_input['text_color'] = $this->getVal('text_color');
765 add_settings_error($this->option_name, 'invalid_color', __('The color you entered is not valid, it must be a valid hexadecimal RGB font color.', 'printfriendly'));
766 }
767
768 /* Section 2 options */
769 if (! isset($input['enable_css']) || 'off' !== $input['enable_css']) {
770 $valid_input['enable_css'] = 'on';
771 }
772
773 if (! isset($input['content_position']) || ! in_array($input['content_position'], array('left', 'center', 'right'), true)) {
774 $valid_input['content_position'] = 'left';
775 }
776
777 if (! isset($input['button_alignment_method']) || ! in_array($input['button_alignment_method'], array('default', 'css'), true)) {
778 $valid_input['button_alignment_method'] = 'default';
779 }
780
781 if (! isset($input['button_alignment_method']) || 'css' !== $input['button_alignment_method']) {
782 $valid_input['content_position_css'] = '';
783 }
784
785 if (! isset($input['content_placement']) || ! in_array($input['content_placement'], array('before', 'after'), true)) {
786 $valid_input['content_placement'] = 'after';
787 }
788
789 foreach (array('margin_top', 'margin_right', 'margin_bottom', 'margin_left') as $opt) {
790 // if margin is not defined, don't throw a PHP notice
791 if (isset($input[ $opt ])) {
792 $valid_input[ $opt ] = (int) $input[ $opt ];
793 }
794 }
795
796 unset($opt);
797
798 /* Section 3 options */
799 foreach (array('show_on_posts', 'show_on_pages', 'show_on_homepage', 'show_on_categories', 'show_on_taxonomies') as $opt) {
800 if (! isset($input[ $opt ]) || 'on' !== $input[ $opt ]) {
801 unset($valid_input[ $opt ]);
802 }
803 }
804 unset($opt);
805
806 if (isset($input['category_ids'])) {
807 unset($valid_input['category_ids']);
808 }
809
810 /* Section 4 options */
811 if (! isset($input['logo']) || ! in_array($input['logo'], array('favicon', 'upload-an-image'), true)) {
812 $valid_input['logo'] = 'favicon';
813 }
814
815 // @todo custom logo url validation
816 if (! isset($input['image_url']) || empty($input['image_url'])) {
817 $valid_input['image_url'] = '';
818 }
819
820 // @todo validate optional tagline text
821 if (! isset($input['tagline'])) {
822 $valid_input['tagline'] = '';
823 }
824
825 // Custom logo selected, but no valid url given, reset logo to default
826 if ('upload-an-image' === $valid_input['logo'] && '' === $valid_input['image_url']) {
827 $valid_input['logo'] = 'favicon';
828 add_settings_error($this->option_name, 'invalid_custom_logo', __('No valid custom logo url received, please enter a valid url to use a custom logo.', 'printfriendly'));
829 }
830
831 if (! isset($input['image-style']) || ! in_array($input['image-style'], array('right', 'left', 'none', 'block'), true)) {
832 $valid_input['image-style'] = 'block';
833 }
834
835 foreach (array('click_to_delete', 'email', 'pdf', 'print') as $opt) {
836 // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
837 if (! isset($input[ $opt ]) || ! in_array($input[ $opt ], array('0', '1'))) {
838 $valid_input[ $opt ] = '0';
839 }
840 }
841 unset($opt);
842
843 if (! isset($input['images-size']) || ! in_array($input['images-size'], array('full-size', 'remove-images', 'large', 'medium', 'small'), true)) {
844 $valid_input['images-size'] = 'full-size';
845 }
846
847 if (! isset($input['css_include_via']) || ! in_array($input['css_include_via'], array('file', 'inline_tag'), true)) {
848 $valid_input['css_include_via'] = 'inline_tag';
849 }
850
851 $css = $input['custom_css'];
852
853 // Strip all HTML/script tags before further sanitization.
854 $css = wp_strip_all_tags($css);
855 $valid_input['custom_css'] = sanitize_textarea_field($css);
856
857
858 // if empty CSS is provided in the box
859 // check if this version is upgraded from the version that was using url instead of textbox
860 // and reuse it
861 if (empty($valid_input['custom_css']) && isset($this->options['custom_css_url'])) {
862 if (empty($this->getVal('custom_css_url'))) {
863 unset($valid_input['custom_css_url']);
864 } else {
865 $valid_input['custom_css_url'] = $this->getVal('custom_css_url');
866 }
867 } else {
868 if (empty($valid_input['custom_css'])) {
869 unset($valid_input['custom_css_url_pro']);
870 } else {
871 if ($valid_input['css_include_via'] === 'file') {
872 $file = $this->generate_custom_css_file($valid_input['custom_css']);
873 $valid_input['custom_css_url_pro'] = $file;
874 }
875 }
876 }
877
878 /* Section 5 options */
879 if (! isset($input['password_protected']) || ! in_array($input['password_protected'], array('no', 'yes'), true)) {
880 $valid_input['password_protected'] = 'no';
881 }
882 if (! isset($input['show_hidden_content']) || ! in_array($input['show_hidden_content'], array('no', 'yes'), true)) {
883 $valid_input['show_hidden_content'] = 'no';
884 }
885
886 /*Analytics Options */
887 if (! isset($input['enable_google_analytics']) || ! in_array($input['enable_google_analytics'], array('no', 'yes'), true)) {
888 $valid_input['enable_google_analytics'] = 'no';
889 }
890
891 if (! isset($input['pf_algo']) || ! in_array($input['pf_algo'], array('wp', 'pf', 'css'), true)) {
892 $valid_input['pf_algo'] = 'wp';
893 }
894
895 if ($valid_input['pf_algo'] === 'css' && empty($valid_input['pf_algo_css_content']) && ! empty($valid_input['css-content'])) {
896 $valid_input['pf_algo_css_content'] = 'original';
897 }
898
899 if (! isset($valid_input['custom_image_width'])) {
900 $valid_input['custom_image_width'] = 0;
901 }
902
903 if (! isset($valid_input['custom_image_height'])) {
904 $valid_input['custom_image_height'] = 0;
905 }
906
907 if (empty($valid_input['custom_image'])) {
908 unset($valid_input['custom_image_width']);
909 unset($valid_input['custom_image_height']);
910 }
911
912 /* Database version */
913 $valid_input['db_version'] = $this->db_version;
914
915 // set the current tab from where the settings were saved from
916 if (isset($_POST['tab'])) {
917 set_transient('pf-tab', $_POST['tab'], 5);
918 }
919
920 // save the categories as comma-separated
921 if (isset($valid_input['show_on_cat']) && ! empty($valid_input['show_on_cat'])) {
922 $valid_input['show_on_cat'] = implode(',', $valid_input['show_on_cat']);
923 }
924
925 return $valid_input;
926 }
927
928 /**
929 * Register the config page for all users that have the manage_options capability
930 *
931 * @since 3.0
932 */
933 function add_config_page()
934 {
935 $this->settings_page = add_options_page(__('PrintFriendly Options', 'printfriendly'), __('Print Friendly & PDF', 'printfriendly'), 'manage_options', $this->hook, array(&$this, 'config_page'));
936
937 // register callback gets call prior your own page gets rendered
938 add_action('load-' . $this->settings_page, array(&$this, 'on_load_printfriendly'));
939 }
940
941 /**
942 * Enqueue the scripts for the admin settings page
943 *
944 * @since 3.0
945 * @param string $screen_id check whether the current page is the PrintFriendly settings page.
946 */
947 function admin_enqueue_scripts($screen_id)
948 {
949 if ($this->settings_page === $screen_id) {
950 if (! did_action('wp_enqueue_media')) {
951 wp_enqueue_media();
952 }
953
954 if (! wp_script_is('clipboard')) {
955 wp_enqueue_script('clipboard', plugins_url('assets/js/lib/clipboard.min.js', __FILE__));
956 }
957
958 if (! wp_script_is('select2')) {
959 wp_enqueue_script('select2', plugins_url('assets/js/lib/select2.min.js', __FILE__));
960 }
961
962 wp_register_script('pf-admin', plugins_url('assets/js/admin.js', __FILE__), array( 'jquery', 'jquery-ui-tabs', 'jquery-ui-accordion', 'media-upload', 'wp-color-picker', 'clipboard', 'select2' ), $this->plugin_version);
963 wp_localize_script(
964 'pf-admin',
965 'pf_config',
966 array(
967 'i10n' => array(
968 'upload_window_title' => __('Custom Image', 'printfriendly'),
969 'upload_window_button_title' => __('Use Image', 'printfriendly'),
970 'invalid_image_url' => __('Invalid Image URL', 'printfriendly'),
971 ),
972 )
973 );
974
975 wp_register_script('pf-admin-pro', plugins_url('assets/js/admin_pro.js', __FILE__), array( 'pf-admin' ), $this->plugin_version);
976
977 wp_localize_script(
978 'pf-admin-pro',
979 'pf_config',
980 array(
981 'nonce' => wp_create_nonce($this->hook . $this->plugin_version),
982 'action' => $this->hook,
983 'i10n' => array(
984 'activation' => __('Activation', 'printfriendly'),
985 'check_status' => __('Checking status', 'printfriendly'),
986 'activate' => __('Activate', 'printfriendly'),
987 'active_trial' => __('Active Trial', 'printfriendly'),
988 'active' => __('Active', 'printfriendly'),
989 'expired' => __('Expired', 'printfriendly'),
990 'connection' => __('Please check Internet connection.', 'printfriendly'),
991 ),
992 )
993 );
994 wp_enqueue_script('pf-admin-pro');
995
996 wp_enqueue_style('pf-bulma', plugins_url('assets/css/lib/bulma.prefixed.min.css', __FILE__), array( 'wp-color-picker' ), $this->plugin_version);
997 wp_enqueue_style('pf-select2', plugins_url('assets/css/lib/select2.min.css', __FILE__), array(), $this->plugin_version);
998 wp_enqueue_style('pf-admin', plugins_url('assets/css/admin.css', __FILE__), array( 'pf-select2', 'pf-bulma' ), $this->plugin_version);
999 }
1000 }
1001
1002 /**
1003 * Register the settings link for the plugins page
1004 *
1005 * @since 3.0
1006 * @param array $links the links for the plugins.
1007 * @param string $file filename to check against plugins filename.
1008 * @return array $links the links with the settings link added to it if appropriate.
1009 */
1010 function filter_plugin_actions($links, $file)
1011 {
1012 // Static so we don't call plugin_basename on every plugin row.
1013 static $this_plugin;
1014 if (! $this_plugin) {
1015 $this_plugin = plugin_basename(__FILE__);
1016 }
1017
1018 if ($file === $this_plugin) {
1019 $settings_link = '<a href="options-general.php?page=' . $this->hook . '">' . __('Settings', 'printfriendly') . '</a>';
1020 array_unshift($links, $settings_link); // before other links
1021 }
1022
1023 return $links;
1024 }
1025
1026 /**
1027 * Register the additional link for the plugins page in the plugin description column.
1028 *
1029 * @since ?
1030 * @param array $links the links for the plugins.
1031 * @param string $file filename to check against plugins filename.
1032 * @return array $links the links with the links added to it if appropriate.
1033 */
1034 function additional_links($links, $file)
1035 {
1036 // Static so we don't call plugin_basename on every plugin row.
1037 static $this_plugin;
1038 if (! $this_plugin) {
1039 $this_plugin = plugin_basename(__FILE__);
1040 }
1041
1042 if ($file === $this_plugin) {
1043 $new_links = array( '<a href="https://support.printfriendly.com/wordpress/customize-content-selection/" target="_new">' . __('Documentation', 'printfriendly') . '</a>' );
1044 $links = array_merge($links, $new_links);
1045 }
1046 return $links;
1047 }
1048
1049 /**
1050 * Set default values for the plugin. If old, as in pre 1.0, settings are there, use them and then delete them.
1051 *
1052 * @since 3.0
1053 */
1054 function set_defaults()
1055 {
1056 // Set some defaults
1057 $this->options = array(
1058 'button_type' => 'buttons/printfriendly-pdf-button.png',
1059 'content_position' => 'left',
1060 'button_alignment_method' => 'default',
1061 'content_placement' => 'after',
1062 'custom_button_icon' => 'https://cdn.printfriendly.com/icons/printfriendly-icon-md.png',
1063 'custom_button_text' => 'custom-text',
1064 'custom_image' => '',
1065 'custom_text' => 'PrintFriendly',
1066 'enable_css' => 'on',
1067 'margin_top' => '12',
1068 'margin_right' => '12',
1069 'margin_bottom' => '12',
1070 'margin_left' => '12',
1071 'show_on_posts' => 'on',
1072 'show_on_pages' => 'on',
1073 'text_color' => '#3AAA11',
1074 'text_size' => 14,
1075 'logo' => 'favicon',
1076 'image_url' => '',
1077 'tagline' => '',
1078 'click_to_delete' => '0', // 0 - allow, 1 - do not allow
1079 'hide-images' => '0', // 0 - show images, 1 - hide images
1080 'image-style' => 'block', // 'right', 'left', 'none', 'block'
1081 'email' => '0', // 0 - allow, 1 - do not allow
1082 'pdf' => '0', // 0 - allow, 1 - do not allow
1083 'print' => '0', // 0 - allow, 1 - do not allow
1084 'password_protected' => 'no',
1085 'enable_google_analytics' => 'no',
1086 'enable_error_reporting' => 'yes',
1087 'pf_algo' => 'wp',
1088 'images-size' => 'full-size',
1089 'show_hidden_content' => 'no',
1090 'css_include_via' => 'inline_tag',
1091 );
1092
1093 // Check whether the old badly named singular options are there, if so, use the data and delete them.
1094 foreach (array_keys($this->options) as $opt) {
1095 $old_opt = get_option('pf_' . $opt);
1096 if ($old_opt !== false) {
1097 $this->options[ $opt ] = $old_opt;
1098 delete_option('pf_' . $opt);
1099 }
1100 }
1101
1102 // This should always be set to the latest immediately when defaults are pushed in.
1103 $this->options['db_version'] = $this->db_version;
1104
1105 update_option($this->option_name, $this->options);
1106 }
1107
1108 /**
1109 * Upgrades the stored options, used to add new defaults if needed etc.
1110 *
1111 * @since 3.0
1112 */
1113 function upgrade()
1114 {
1115 // update options to version 2
1116 if ($this->options['db_version'] < 2) {
1117 $additional_options = array(
1118 'enable_css' => 'on',
1119 'logo' => 'favicon',
1120 'image_url' => '',
1121 'tagline' => '',
1122 'click_to_delete' => '0',
1123 'password_protected' => 'no',
1124 );
1125
1126 // correcting badly named option
1127 if (isset($this->options['disable_css'])) {
1128 $additional_options['enable_css'] = $this->options['disable_css'];
1129 unset($this->options['disable_css']);
1130 }
1131
1132 // check whether image we do not list any more was used
1133 if (in_array($this->options['button_type'], array('button-print-whgn20.png', 'pf_button_sq_qry_m.png', 'pf_button_sq_qry_l.png', 'pf_button_sq_grn_m.png', 'pf_button_sq_grn_l.png'), true)) {
1134 // previous version had a bug with button name
1135 if (in_array($this->options['button_type'], array('pf_button_sq_qry_m.png', 'pf_button_sq_qry_l.png'), true)) {
1136 $this->options['button_type'] = str_replace('qry', 'gry', $this->options['button_type']);
1137 }
1138
1139 $image_address = 'https://cdn.printfriendly.com/' . $this->options['button_type'];
1140 $this->options['button_type'] = 'custom-image';
1141 $this->options['custom_text'] = '';
1142 $this->options['custom_image'] = $image_address;
1143 }
1144
1145 $this->options = array_merge($this->options, $additional_options);
1146 }
1147
1148 // update options to version 3
1149 if ($this->options['db_version'] < 3) {
1150 $old_show_on = $this->options['show_list'];
1151 // 'manual' setting
1152 $additional_options = array('custom_css_url' => '');
1153
1154 if ($old_show_on === 'all') {
1155 $additional_options = array(
1156 'show_on_pages' => 'on',
1157 'show_on_posts' => 'on',
1158 'show_on_homepage' => 'on',
1159 'show_on_categories' => 'on',
1160 'show_on_taxonomies' => 'on',
1161 );
1162 }
1163
1164 if ($old_show_on === 'single') {
1165 $additional_options = array(
1166 'show_on_pages' => 'on',
1167 'show_on_posts' => 'on',
1168 );
1169 }
1170
1171 if ($old_show_on === 'posts') {
1172 $additional_options = array(
1173 'show_on_posts' => 'on',
1174 );
1175 }
1176
1177 unset($this->options['show_list']);
1178
1179 $this->options = array_merge($this->options, $additional_options);
1180 }
1181
1182 // update options to version 4
1183 if ($this->options['db_version'] < 4) {
1184 $additional_options = array(
1185 'email' => '0',
1186 'pdf' => '0',
1187 'print' => '0',
1188 );
1189
1190 $this->options = array_merge($this->options, $additional_options);
1191 }
1192
1193 // update options to version 6
1194 // Replacement for db version 5 - should also be run for those already upgraded
1195 if ($this->options['db_version'] < 6) {
1196 unset($this->options['category_ids']);
1197 }
1198
1199 if ($this->options['db_version'] < 7) {
1200 $additional_options = array(
1201 'hide-images' => '0',
1202 'image-style' => 'right',
1203 );
1204
1205 $this->options = array_merge($this->options, $additional_options);
1206 }
1207
1208 if ($this->options['db_version'] < 8) {
1209 $this->options['enable_google_analytics'] = 'no';
1210 }
1211
1212 if ($this->options['db_version'] < 9) {
1213 $this->options['pf_algo'] = 'wp';
1214 }
1215
1216 if ($this->options['db_version'] < 10) {
1217 $this->options['enable_error_reporting'] = 'yes';
1218 }
1219
1220 if ($this->options['db_version'] < 11) {
1221 if (! isset($this->options['custom_css_url'])) {
1222 $this->options['custom_css_url'] = '';
1223 }
1224 }
1225
1226 if ($this->options['db_version'] < 12) {
1227 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1228 $this->options['images-size'] = $this->options['hide-images'] == '1' ? 'remove-images' : 'full-size';
1229 }
1230
1231 if ($this->options['db_version'] < 13) {
1232 switch ($this->options['button_type']) {
1233 case 'pf-button.gif':
1234 $this->options['button_type'] = 'buttons/printfriendly-button.png';
1235 break;
1236 case 'pf-button-both.gif':
1237 $this->options['button_type'] = 'buttons/printfriendly-pdf-button.png';
1238 break;
1239 case 'pf-button-big.gif':
1240 $this->options['button_type'] = 'buttons/printfriendly-button-lg.png';
1241 break;
1242 case 'pf-button-print-pdf-mail.png':
1243 $this->options['button_type'] = 'buttons/printfriendly-pdf-email-button-notext.png';
1244 break;
1245 case 'button-print-grnw20.png':
1246 $this->options['button_type'] = 'buttons/print-button.png';
1247 break;
1248 case 'button-print-blu20.png':
1249 $this->options['button_type'] = 'buttons/print-button-nobg.png';
1250 break;
1251 case 'button-print-gry20.png':
1252 $this->options['button_type'] = 'buttons/print-button-gray.png';
1253 break;
1254 case 'pf-icon-small.gif':
1255 case 'pf-icon.gif':
1256 $this->options['button_type'] = 'custom-button';
1257 $this->options['custom_button_icon'] = 'icons/printfriendly-icon-md.png';
1258 $this->options['custom_button_text'] = 'custom-text';
1259 $this->options['custom_image'] = 'icons/printfriendly-icon-md.png';
1260 break;
1261 case 'text-only':
1262 $this->options['button_type'] = 'custom-button';
1263 $this->options['custom_button_icon'] = 'no-image';
1264 $this->options['custom_button_text'] = 'custom-text';
1265 break;
1266 }
1267 }
1268
1269 if ($this->options['db_version'] < 14) {
1270 if ($this->options['button_type'] === 'pf-icon-both.gif') {
1271 $this->options['button_type'] = 'buttons/printfriendly-pdf-button-nobg.png';
1272 }
1273 }
1274
1275 if ($this->options['db_version'] < 15) {
1276 if ($this->options['button_type'] === 'custom-image') {
1277 $this->options['button_type'] = 'custom-button';
1278
1279 switch ($this->options['custom_image']) {
1280 case 'icons/printfriendly-icon-sm.png':
1281 case 'icons/printfriendly-icon-md.png':
1282 case 'icons/printfriendly-icon-lg.png':
1283 $this->options['custom_button_icon'] = $this->options['custom_image'];
1284 break;
1285 case '':
1286 $this->options['custom_button_icon'] = 'no-image';
1287 break;
1288 default:
1289 $this->options['custom_button_icon'] = 'custom-image';
1290 }
1291
1292 if ($this->options['custom_text'] === '') {
1293 $this->options['custom_button_text'] = 'no-text';
1294 } else {
1295 $this->options['custom_button_text'] = 'custom-text';
1296 }
1297 }
1298 }
1299
1300 if ($this->options['db_version'] < 16) {
1301 if ($this->options['custom_button_icon'] === 'icons/printfriendly-icon-md.png') {
1302 $this->options['custom_button_icon'] = 'https://cdn.printfriendly.com/icons/printfriendly-icon-md.png';
1303 }
1304 }
1305
1306 if ($this->options['db_version'] < 17) {
1307 $this->options['pro_email'] = get_bloginfo('admin_email');
1308
1309 $url = get_bloginfo('url');
1310 $parsed_url = parse_url($url);
1311 $this->options['pro_domain'] = $parsed_url['host'];
1312 }
1313
1314 if ($this->options['db_version'] < 21) {
1315 if (empty($this->getVal('custom_css'))) {
1316 $this->options['css_include_via'] = 'inline_tag';
1317 } else {
1318 $this->options['css_include_via'] = 'file';
1319 }
1320 }
1321
1322 $this->options['db_version'] = $this->db_version;
1323
1324 update_option($this->option_name, $this->options);
1325 }
1326
1327 /**
1328 * Displays radio button in the admin area
1329 *
1330 * @since 3.0
1331 * @param string $name the name of the radio button to generate.
1332 * @param boolean $br whether or not to add an HTML <br> tag, defaults to true.
1333 * @param boolean $value if this is null, will have the same value as $name.
1334 */
1335 function radio($name, $br = false, $value = null)
1336 {
1337 if (is_null($value)) {
1338 $value = $name;
1339 }
1340
1341 $var = '<input id="' . $name . '" class="radio" name="' . $this->option_name . '[button_type]" type="radio" value="' . $value . '" ' . $this->checked('button_type', $value, false) . '/>';
1342 $button = $this->button($name);
1343 if (! empty($button)) {
1344 echo '<label for="' . $name . '">' . $var . $button . '</label>';
1345 } else {
1346 echo $var;
1347 }
1348
1349 if ($br) {
1350 echo '<br>';
1351 }
1352 }
1353
1354 /**
1355 * Displays radio button in the admin area
1356 *
1357 * @since 3.12.0
1358 * @param string $value the value of the radio button to generate.
1359 */
1360 function radio_custom_image($value)
1361 {
1362 $button = $this->button($value);
1363 $value = "https://cdn.printfriendly.com/{$value}";
1364 ?>
1365 <label for="<?php echo esc_attr($value); ?>">
1366 <input type="radio" id="<?php echo esc_attr($value); ?>" name="<?php echo $this->option_name; ?>[custom_button_icon]" value="<?php echo $value; ?>" <?php $this->checked('custom_button_icon', $value); ?>>
1367 <?php echo $button; ?>
1368 </label>
1369 <?php
1370 }
1371
1372 /**
1373 * Displays button image in the admin area
1374 *
1375 * @since 3.0
1376 * @param string $name the name of the button to generate.
1377 */
1378 function button($name = false)
1379 {
1380 if (! $name) {
1381 $name = $this->getVal('button_type');
1382 }
1383
1384 $img_path = 'https://cdn.printfriendly.com/';
1385
1386 $return = '';
1387 $imgStyle = '';
1388
1389 if ($name === 'custom-button') {
1390 if ($this->getVal('custom_button_icon') === 'custom-image' && '' !== trim($this->getVal('custom_image', ''))) {
1391 $width = $this->getVal('custom_image_width');
1392 $height = $this->getVal('custom_image_height');
1393 if (! empty($width)) {
1394 $imgStyle .= sprintf('width: %dpx;', $width);
1395 }
1396 if (! empty($height)) {
1397 $imgStyle .= sprintf('height: %dpx;', $height);
1398 }
1399
1400 $return = '<img src="' . esc_url($this->getVal('custom_image')) . '" alt="Print Friendly, PDF & Email" class="pf-button-img" style="' . $imgStyle . '" />';
1401 } elseif ($this->getVal('custom_button_icon') !== 'no-image') {
1402 $icon = str_replace($img_path, '', $this->getVal('custom_button_icon'));
1403 $attributes = self::$_buttons[ $icon ];
1404 $atts = '';
1405 $width = $attributes['width'];
1406 $height = $attributes['height'];
1407 if (! empty($width)) {
1408 $imgStyle .= sprintf('width: %dpx;', $width);
1409 }
1410 if (! empty($height)) {
1411 $imgStyle .= sprintf('height: %dpx;', $height);
1412 }
1413
1414 $return = '<img src="' . esc_url($this->getVal('custom_button_icon')) . '" alt="Print Friendly, PDF & Email" class="pf-button-img" style="' . $imgStyle . '" />';
1415 }
1416
1417 if ($this->getVal('custom_button_text') === 'custom-text') {
1418 $return .= sprintf('<span id="printfriendly-text2" class="pf-button-text">%s</span>', $this->esc_html_if_needed($this->getVal('custom_text')));
1419 }
1420
1421 return $return;
1422 } elseif ($name === 'custom-btn') {
1423 return __('Custom Button', 'printfriendly');
1424 } else {
1425 $attributes = self::$_buttons[ $name ];
1426 $atts = '';
1427 $width = $attributes['width'];
1428 $height = $attributes['height'];
1429 if (! empty($width)) {
1430 $imgStyle .= sprintf('width: %dpx;', $width);
1431 }
1432 if (! empty($height)) {
1433 $imgStyle .= sprintf('height: %dpx;', $height);
1434 }
1435
1436 return '<img class="pf-button-img" src="' . $img_path . $name . '" alt="Print Friendly, PDF & Email" style="' . $imgStyle . '" />';
1437 }
1438 }
1439
1440 /**
1441 * Convenience function to output a value custom button preview elements
1442 *
1443 * @since 3.0.9
1444 */
1445 function custom_button_preview()
1446 {
1447 $img = $url = $button_text = $style = $width = $height = '';
1448 switch ($this->getVal('custom_button_icon')) {
1449 case 'no-image':
1450 break;
1451 case 'custom-image':
1452 $url = $this->getVal('custom_image');
1453 $width = $this->getVal('custom_image_width');
1454 $height = $this->getVal('custom_image_height');
1455 break;
1456 default:
1457 $url = $this->getVal('custom_button_icon');
1458 break;
1459 }
1460
1461 if (! empty($url)) {
1462 $imgStyle = '';
1463 if (! empty($width)) {
1464 $imgStyle .= sprintf('width: %dpx;', $width);
1465 }
1466 if (! empty($height)) {
1467 $imgStyle .= sprintf('height: %dpx;', $height);
1468 }
1469 $img = sprintf('<img src="%s" style="%s" alt="Print Friendly, PDF & Email">', esc_url($url), $imgStyle);
1470 }
1471
1472 if ($this->getVal('custom_button_text') !== 'no-text') {
1473 $button_text = $this->getVal('custom_text');
1474 }
1475
1476 if ('' !== $this->getVal('text_color', '')) {
1477 $style .= 'color: ' . $this->getVal('text_color') . ';';
1478 }
1479
1480 $button_preview = sprintf('<span><span id="pf-custom-button-preview" class="pf-button-img">%s</span><span id="printfriendly-text2" class="pf-button-text" style="%s">%s</span></span>', $img, $style, $this->esc_html_if_needed($button_text));
1481
1482 echo $button_preview;
1483 }
1484
1485 function echoVal($option, $default = '')
1486 {
1487 echo $this->getVal($option, $default);
1488 }
1489
1490 function getVal($option, $default = null)
1491 {
1492 $value = $default;
1493 if (isset($this->options[ $option ])) {
1494 $value = $this->options[ $option ];
1495 }
1496
1497 return $value;
1498 }
1499
1500
1501 /**
1502 * Like the WordPress checked() function but it doesn't throw notices when the array key isn't set and uses the plugins options array.
1503 *
1504 * @since 3.0
1505 * @param mixed $val value to check.
1506 * @param mixed $check_against value to check against.
1507 * @param boolean $echo whether or not to echo the output.
1508 * @return string checked, when true, empty, when false.
1509 */
1510 function checked($val, $check_against = true, $echo = true)
1511 {
1512 $result = '';
1513 if (! isset($this->options[ $val ])) {
1514 return $result;
1515 }
1516
1517 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1518 if ($this->options[ $val ] == $check_against) {
1519 $result = ' checked="checked" ';
1520 if ($echo) {
1521 echo $result;
1522 }
1523 }
1524 return $result;
1525 }
1526
1527 /**
1528 * Helper for creating checkboxes.
1529 *
1530 * @since 3.1.5
1531 * @param string $name string used for various parts of checkbox.
1532 */
1533 function create_checkbox($name, $label = '', $labelid = '')
1534 {
1535 // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText
1536 $label = ( ! empty($label) ? $label : __(ucfirst($name), 'printfriendly') );
1537 echo '<label' . ( ! empty($labelid) ? ' id=' . $labelid : '' ) . '><input type="checkbox" class="show_list" name="' . $this->option_name . '[show_on_' . $name . ']" value="on" ';
1538 $this->checked('show_on_' . $name, 'on');
1539 echo ' />' . $label . "</label>\r\n";
1540 }
1541
1542
1543 /**
1544 * Like the WordPress selected() function but it doesn't throw notices when the array key isn't set and uses the plugins options array.
1545 *
1546 * @since 3.0.9
1547 * @param mixed $val value to check.
1548 * @param mixed $check_against value to check against.
1549 * @return string checked, when true, empty, when false.
1550 */
1551 function selected($val, $check_against = true)
1552 {
1553 if (! isset($this->options[ $val ])) {
1554 return;
1555 }
1556
1557 return selected($this->options[ $val ], $check_against);
1558 }
1559
1560 /**
1561 * For use with page metabox.
1562 *
1563 * @since 3.2.2
1564 */
1565 function get_page_post_type()
1566 {
1567 $post_types = get_post_types(array('name' => 'page'), 'object');
1568 // echo '<pre>'.print_r($post_types,1).'</pre>';
1569 // die;
1570
1571 return $post_types['page'];
1572 }
1573
1574
1575 /**
1576 * Helper that checks if wp versions is above 3.0.
1577 *
1578 * @since 3.2.2
1579 * @return boolean true wp version is above 3.0
1580 */
1581 function wp_version_gt30()
1582 {
1583 global $wp_version;
1584 return version_compare($wp_version, '3.0', '>=');
1585 }
1586
1587
1588 /**
1589 * Create box for picking individual categories.
1590 *
1591 * @since 3.2.2
1592 */
1593 function create_category_metabox()
1594 {
1595 $obj = new stdClass();
1596 $obj->ID = 0;
1597 do_meta_boxes('settings_page_' . $this->hook, 'normal', $obj);
1598 }
1599
1600
1601 /**
1602 * Load metaboxes advanced button display settings.
1603 *
1604 * @since 3.2.2
1605 */
1606 function on_load_printfriendly()
1607 {
1608 global $wp_version;
1609 if ($this->wp_version_gt30()) {
1610 // require_once(dirname(__FILE__).'/includes/meta-boxes.php');
1611 // require_once(dirname(__FILE__).''includes/nav-menu.php');
1612 wp_enqueue_script('post');
1613
1614 add_meta_box('categorydiv', __('Only display when post is in:', 'printfriendly'), 'post_categories_meta_box', 'settings_page_' . $this->hook, 'normal', 'core');
1615 }
1616 }
1617
1618 /**
1619 * Previously, users with capability unfiltered_html were allowed to use raw HTML.
1620 * But now, the input for all users is sanitized.
1621 */
1622 function esc_html_if_needed($input)
1623 {
1624 $final = esc_html(strip_tags($input));
1625 return $final;
1626 }
1627
1628 /**
1629 * Returns if the user is a pro user.
1630 */
1631 function is_pro($feature = null)
1632 {
1633 $licensed = true;//$this->getVal('license_status') === 'pro';
1634
1635 switch ($feature) {
1636 case 'custom-css':
1637 // custom css needs to be available for all irrespective of the license.
1638 return true;
1639 }
1640
1641 return $licensed;
1642 }
1643
1644 function get_custom_css_tag()
1645 {
1646 $custom_css = $this->getVal('custom_css');
1647 if (!empty($custom_css) && 'inline_tag' === $this->getVal('css_include_via')) {
1648 return sprintf('<printfriendly-css style="display: none;">%s</printfriendly-css>', esc_html($custom_css));
1649 }
1650 }
1651
1652 function get_custom_css_js_var()
1653 {
1654 $css_url = $this->get_custom_css_url();
1655 if (!empty($css_url)) {
1656 return sprintf('var pfCustomCSS = "%s";', esc_js(esc_url($css_url)));
1657 }
1658 }
1659
1660 function get_custom_css_url()
1661 {
1662 $css_url = $this->getVal('custom_css_url', '');
1663
1664 if (empty($css_url) && 'file' === $this->getVal('css_include_via')) {
1665 // It could be unset because custom css is empty
1666 $css_url = $this->getVal('custom_css_url_pro');
1667 if (!empty($css_url)) {
1668 $dirs = wp_get_upload_dir();
1669 $css_url = $dirs['baseurl'] . '/' . $css_url;
1670 }
1671 }
1672 return $css_url;
1673 }
1674
1675
1676 /**
1677 * Generates the custom css file from the CSS block.
1678 */
1679 function generate_custom_css_file($css)
1680 {
1681 $file = $this->getVal('custom_css_url_pro');
1682
1683 $custom_css_old = $this->getVal('custom_css');
1684
1685 // return the old file if the CSS has not changed.
1686 if ($custom_css_old === $css && ! empty($file)) {
1687 return $file;
1688 }
1689
1690 $dirs = wp_get_upload_dir();
1691
1692 // delete old file, if it exists
1693 if (! empty($file)) {
1694 wp_delete_file($dirs['basedir'] . '/' . $file);
1695 }
1696
1697 // add a comment so that users know whence this file came.
1698 $date = date_i18n(get_option('date_format') . ' ' . get_option('time_format'));
1699 $comment = sprintf('/* DO NOT EDIT - FILE AUTO-GENERATED BY PRINTFRIENDLY v%s ON %s */', $this->plugin_version, $date);
1700 $css = $comment . PHP_EOL . PHP_EOL . $css;
1701
1702 // create new file, suffixed with the current time.
1703 $file = sprintf('%s_%s.css', $this->hook, time());
1704 include_once ABSPATH . 'wp-admin/includes/file.php';
1705 WP_Filesystem();
1706 global $wp_filesystem;
1707 $wp_filesystem->put_contents(
1708 $dirs['basedir'] . '/' . $file,
1709 $css,
1710 FS_CHMOD_FILE
1711 );
1712
1713 // return the new file name
1714 return $file;
1715 }
1716
1717 /**
1718 * If upgrading from a previous version that was using urls instead of the textarea
1719 * it will return an appropriate message for the user.
1720 */
1721 function get_custom_css_upgrade_message()
1722 {
1723 // upgrading from a version that was using urls instead of the textarea?
1724 $css_url = $this->getVal('custom_css_url');
1725 if (! empty($css_url)) {
1726 return sprintf(__('You are currently using %1$s%2$s%3$s. You can copy its contents into the textbox if you want to update the styles.', 'printfriendly'), '<a href="' . $css_url . '" target="_blank">', $css_url, '</a>');
1727 }
1728
1729 return null;
1730 }
1731
1732 /**
1733 * Output the config page
1734 *
1735 * @since 3.0
1736 */
1737 function config_page()
1738 {
1739
1740 // Since WP 3.2 outputs these errors by default, only display them when we're on versions older than 3.2 that do support the settings errors.
1741 global $wp_version;
1742 if (version_compare($wp_version, '3.2', '<') && $this->wp_version_gt30()) {
1743 settings_errors();
1744 }
1745
1746 $customCssOptionCode = $this->getSelectorsFromCustomCSS();
1747 include_once PRINTFRIENDLY_BASEPATH . '/views/settings.php';
1748 }
1749
1750 /**
1751 * Returns the current tab to activate.
1752 *
1753 * @since 4.0.1
1754 */
1755 function is_tab($tab_id)
1756 {
1757 $tab = get_transient('pf-tab');
1758 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1759 return empty($tab) && $tab_id == 0 ? true : $tab == $tab_id;
1760 }
1761 }
1762 $printfriendly = new PrintFriendly_WordPress();
1763 }
1764
1765 // Add shortcode for printfriendly button
1766 add_shortcode(
1767 'printfriendly',
1768 /**
1769 * Show the printfriendly button.
1770 *
1771 * @param array $atts The attributes of the shortcode.
1772 * If current="yes", the whole page will be printed (pf_current_page_button is called).
1773 * The default behaviour is to call pf_default_button.
1774 *
1775 * @since 3.0
1776 *
1777 * @return string The button.
1778 */
1779 function ($atts) {
1780 $atts = shortcode_atts(
1781 array(
1782 'current' => '', // yes => print the whole page
1783 ),
1784 $atts,
1785 'printfriendly'
1786 );
1787
1788 if (empty($atts['current'])) {
1789 return pf_default_button();
1790 } else {
1791 return pf_current_page_button();
1792 }
1793
1794 return null;
1795 }
1796 );
1797
1798
1799 /**
1800 * Convenience function for use in templates.
1801 *
1802 * @deprecated Use pf_default_button instead.
1803 *
1804 * @since 3.0
1805 *
1806 * @return string returns a button to be printed.
1807 */
1808 function pf_show_link()
1809 {
1810 return pf_default_button();
1811 }
1812
1813 /**
1814 * Convenience function for use in templates.
1815 *
1816 * @since 5.1
1817 *
1818 * @return string returns a button to be printed.
1819 */
1820 function pf_default_button()
1821 {
1822 global $printfriendly;
1823 return $printfriendly->getButton(true);
1824 }
1825
1826 /**
1827 * Convenience function for use in templates.
1828 *
1829 * @since 5.1
1830 *
1831 * @return string returns a button that prints the entire page it is on.
1832 */
1833 function pf_current_page_button()
1834 {
1835 global $printfriendly;
1836 return $printfriendly->getButton(true, '', true);
1837 }
1838