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