PluginProbe
Print, PDF & Email by PrintFriendly / 5.5.3
Print, PDF & Email by PrintFriendly v5.5.3
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.3, at pf.php

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