| @@ -2,13 +2,13 @@ | ||
| 2 | 2 | /** |
| 3 | 3 | * Plugin Name: Taboola |
| 4 | 4 | * Plugin URI: https://developers.taboola.com/web-integrations/docs/wordpress-plugin |
| 5 | 5 | * Description: Taboola |
| 6 | - * Version: 3.1.0 | |
| 6 | + * Version: 3.0.0 | |
| 7 | 7 | * Author: Taboola |
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | -define( 'TABOOLA_PLUGIN_VERSION', '3.1.0' ); // track every release | |
| 10 | +define( 'TABOOLA_PLUGIN_VERSION', '3.0' ); // track every release | |
| 11 | 11 | define( 'TABOOLA_MIN_VER', '3.0' ); // bump only when DB changes |
| 12 | 12 | define( 'TABOOLA_DEBUG_MODE', false ); |
| 13 | 13 | |
| 14 | 14 | define( 'TABOOLA_OPTION_NAME', 'taboola_plugin_version' ); |
| @@ -21,11 +21,9 @@ | ||
| 21 | 21 | define( 'TABOOLA_CONTENT_FORMAT_HTML', 'html' ); |
| 22 | 22 | |
| 23 | 23 | include_once 'widget.php'; |
| 24 | 24 | require_once 'JavaScriptWrapper.php'; |
| 25 | -if ( ! class_exists( 'simple_html_dom' ) ) { | |
| 26 | - require_once plugin_dir_path( __FILE__ ) . 'simple_html_dom.php'; // ← NEW | |
| 27 | -} | |
| 25 | +require_once plugin_dir_path( __FILE__ ) . 'simple_html_dom.php'; // ← NEW | |
| 28 | 26 | |
| 29 | 27 | if ( ! class_exists( 'TaboolaWP' ) ) { |
| 30 | 28 | class TaboolaWP { |
| 31 | 29 | |
| @@ -50,9 +48,9 @@ | ||
| 50 | 48 | |
| 51 | 49 | /* ─────────────────────────── constructor ─────────────────────────── */ |
| 52 | 50 | public function __construct() { |
| 53 | 51 | global $wpdb; |
| 54 | - //initialize plugin constant | |
| 52 | + | |
| 55 | 53 | define( 'TaboolaWP', true ); |
| 56 | 54 | |
| 57 | 55 | $this->_is_widget_on_page = false; |
| 58 | 56 | $this->_is_head_script_loaded = false; |
| @@ -83,34 +81,11 @@ | ||
| 83 | 81 | if ( is_admin() ) { |
| 84 | 82 | add_action( 'admin_menu', [ $this, 'admin_generate_menu' ] ); |
| 85 | 83 | add_filter( 'plugin_action_links', |
| 86 | 84 | [ $this, 'plugin_action_links' ], 10, 2 ); |
| 87 | - } | |
| 88 | - | |
| 89 | - | |
| 90 | - | |
| 91 | - | |
| 92 | - elseif ( $this->settings ) { | |
| 85 | + } elseif ( $this->settings ) { | |
| 93 | 86 | /* loader & flush */ |
| 94 | 87 | add_action( 'wp_head', [ $this, 'taboola_header_loader_inject' ] ); |
| 95 | - if ( ! empty( $this->settings->publisher_id_push ) ) { | |
| 96 | - add_action( 'wp_head', [ $this, 'taboola_webpush_loader_js' ] ); | |
| 97 | - | |
| 98 | - $sw = 'sw.js'; | |
| 99 | - $sw_path = ABSPATH . $sw; | |
| 100 | - | |
| 101 | - $content = file_exists( $sw_path ) ? file_get_contents( $sw_path ) : ''; | |
| 102 | - | |
| 103 | - if ( strpos( $content, $this->tpl_sw ) === false ) { | |
| 104 | - if ( ! is_writable( ABSPATH ) || ( file_exists( $sw_path ) && ! is_writable( $sw_path ) ) ) { | |
| 105 | - return $this->notice( $this->msg_sw_error ); | |
| 106 | - } | |
| 107 | - $content = $this->tpl_sw . PHP_EOL . $content; | |
| 108 | - if ( file_put_contents( $sw_path, $content ) === false ) { | |
| 109 | - return $this->notice( $this->msg_sw_error ); | |
| 110 | - } | |
| 111 | - } | |
| 112 | - } | |
| 113 | 88 | add_action( 'wp_footer', [ $this, 'taboola_footer_loader_js' ] ); |
| 114 | 89 | |
| 115 | 90 | /* content widgets */ |
| 116 | 91 | add_filter( 'the_content', [ $this, 'load_taboola_content' ] ); |
| @@ -270,21 +245,15 @@ | ||
| 270 | 245 | return $page_type; |
| 271 | 246 | } |
| 272 | 247 | |
| 273 | 248 | // return the head loader script |
| 274 | - function taboola_header_loader_js() { | |
| 249 | + function taboola_header_loader_js() { | |
| 250 | + $head_string = ""; | |
| 251 | + | |
| 252 | + // Only adding the loader if a widget is going to be placed on the page. | |
| 275 | 253 | if ($this->is_widget_on_page()){ |
| 276 | - | |
| 277 | - // New logic to get all mid-article locations | |
| 278 | - $mid_locations_string = ''; | |
| 279 | - if (!empty($this->settings->mid_widgets)) { | |
| 280 | - $mid_widgets_array = json_decode($this->settings->mid_widgets, true); | |
| 281 | - if (is_array($mid_widgets_array)) { | |
| 282 | - $locations = array_column($mid_widgets_array, 'location_string'); | |
| 283 | - $mid_locations_string = implode(', ', $locations); | |
| 284 | - } | |
| 285 | - } | |
| 286 | - | |
| 254 | + // PC - since these params will be inserted in 'loaderInjectionScript.js' via search and replace, | |
| 255 | + // double brackets are used to ensure that each key is a unique string. | |
| 287 | 256 | $stringParams = array( |
| 288 | 257 | '{{PUBLISHER_ID}}' => $this->settings->publisher_id, |
| 289 | 258 | '{{PAGE_TYPE}}' => $this->get_page_type(), |
| 290 | 259 | '{{WORDPRESS_VERSION}}' => get_bloginfo('version'), |
| @@ -289,18 +258,19 @@ | ||
| 289 | 258 | '{{PAGE_TYPE}}' => $this->get_page_type(), |
| 290 | 259 | '{{WORDPRESS_VERSION}}' => get_bloginfo('version'), |
| 291 | 260 | '{{PHP_VERSION}}' => phpversion(), |
| 292 | 261 | '{{PLUGIN_VERSION}}' => TABOOLA_PLUGIN_VERSION, |
| 293 | - '{{LOC_MID}}' => $mid_locations_string, | |
| 294 | - '{{LOC_HOME}}' => $this->settings->home_location_string ?? '' | |
| 262 | + '{{LOC_MID}}' => $this->settings->mid_location_string, | |
| 263 | + '{{LOC_HOME}}' => $this->settings->home_location_string | |
| 295 | 264 | ); |
| 296 | 265 | |
| 297 | 266 | $scriptWrapper = new JavaScriptWrapper("loaderInjectionScript.js",$stringParams); |
| 298 | - return $scriptWrapper->getScriptMarkupString(); | |
| 267 | + $head_string = $scriptWrapper->getScriptMarkupString(); | |
| 299 | 268 | } |
| 300 | - return ""; | |
| 269 | + return $head_string; | |
| 301 | 270 | } |
| 302 | 271 | |
| 272 | + | |
| 303 | 273 | // This function is used for the hook action, injects the header content to the <head> tag. |
| 304 | 274 | function taboola_header_loader_inject(){ |
| 305 | 275 | echo $this->taboola_header_loader_js(); |
| 306 | 276 | } |
| @@ -503,31 +473,8 @@ | ||
| 503 | 473 | implode( "\n", $arr[TABOOLA_CONTENT_FORMAT_SCRIPT] ?? [] ) . |
| 504 | 474 | '</script>', |
| 505 | 475 | ]); |
| 506 | 476 | } |
| 507 | - /* ------------------------------------------------------------------ | |
| 508 | - * wpautop() hardening for anything injected through the_content. | |
| 509 | - * | |
| 510 | - * wpautop() pads block-level tags with blank lines, then splits the | |
| 511 | - * content on blank lines and wraps each chunk in <p>. Its <script> | |
| 512 | - * protection only runs after that split, so two things tear an injected | |
| 513 | - * <script> apart and spill its tail onto the page as reader-visible text: | |
| 514 | - * 1. a blank line anywhere in the script body, and | |
| 515 | - * 2. a block-level tag (e.g. <div>) sitting inside a JS string literal. | |
| 516 | - * Both have to be neutralised; fixing only one still breaks. | |
| 517 | - * ------------------------------------------------------------------*/ | |
| 518 | - | |
| 519 | - // Hide markup from wpautop's block-tag scan. \x3C decodes back to "<" | |
| 520 | - // when the surrounding JS string literal is evaluated. | |
| 521 | - private function js_escape_markup($markup){ | |
| 522 | - return str_replace('<', '\x3C', (string) $markup); | |
| 523 | - } | |
| 524 | - | |
| 525 | - // Collapse blank lines so wpautop has no paragraph boundary to split on. | |
| 526 | - private function wpautop_safe_script($script){ | |
| 527 | - return preg_replace('/(\R[ \t]*){2,}/', "\n", (string) $script); | |
| 528 | - } | |
| 529 | - | |
| 530 | 477 | // Below-article widget |
| 531 | 478 | // Do the actual logic of choosing where to place the taboola content. |
| 532 | 479 | function embed_taboola_content_location($content, $taboola_content){ |
| 533 | 480 | $do_default = true; |
| @@ -535,13 +482,13 @@ | ||
| 535 | 482 | // tag is placed outside of content in order to allow "read more" functionality. |
| 536 | 483 | if ($this->settings->out_of_content_enabled){ |
| 537 | 484 | |
| 538 | 485 | $scriptWrapper = new JavaScriptWrapper("js_inject.min.js",array( |
| 539 | - "{{HTML}}" => $this->js_escape_markup($this->format_taboola_content($taboola_content,TABOOLA_CONTENT_FORMAT_HTML)), | |
| 540 | - "{{SCRIPT}}" => $this->js_escape_markup($this->format_taboola_content($taboola_content,TABOOLA_CONTENT_FORMAT_SCRIPT))) | |
| 486 | + "{{HTML}}" => $this->format_taboola_content($taboola_content,TABOOLA_CONTENT_FORMAT_HTML), | |
| 487 | + "{{SCRIPT}}" => $this->format_taboola_content($taboola_content,TABOOLA_CONTENT_FORMAT_SCRIPT)) | |
| 541 | 488 | ); |
| 542 | 489 | $scriptWrapper->appendScript("injectWidgetByMarker('tbmarker');"); |
| 543 | - $content = $content."<span id='tbmarker'></span><script type='text/javascript'>".$this->wpautop_safe_script($scriptWrapper)."</script>"; | |
| 490 | + $content = $content."<span id='tbmarker'></span><script type='text/javascript'>".$scriptWrapper."</script>"; | |
| 544 | 491 | $do_default = false; |
| 545 | 492 | } |
| 546 | 493 | |
| 547 | 494 | // Default for below-article widget - add to the end of the content |
| @@ -567,13 +514,13 @@ | ||
| 567 | 514 | if ($full_indicator == TABOOLA_JS_INDICATOR){ |
| 568 | 515 | |
| 569 | 516 | $xpath = substr($location,strlen(TABOOLA_JS_INDICATOR)); |
| 570 | 517 | $scriptWrapper = new JavaScriptWrapper("js_inject.min.js",array( |
| 571 | - "{{HTML}}" => $this->js_escape_markup($this->format_taboola_content_mid($taboola_content_mid,TABOOLA_CONTENT_FORMAT_HTML)), | |
| 572 | - "{{SCRIPT}}" => $this->js_escape_markup($this->format_taboola_content_mid($taboola_content_mid,TABOOLA_CONTENT_FORMAT_SCRIPT))) | |
| 518 | + "{{HTML}}" => $this->format_taboola_content_mid($taboola_content_mid,TABOOLA_CONTENT_FORMAT_HTML), | |
| 519 | + "{{SCRIPT}}" => $this->format_taboola_content_mid($taboola_content_mid,TABOOLA_CONTENT_FORMAT_SCRIPT)) | |
| 573 | 520 | ); |
| 574 | 521 | $scriptWrapper->appendScript("injectWidgetByXpath('".$xpath."');"); |
| 575 | - $content = $content."<span id='tbdefault'></span><script type='text/javascript'>".$this->wpautop_safe_script($scriptWrapper)."</script>"; | |
| 522 | + $content = $content."<span id='tbdefault'></span><script type='text/javascript'>".$scriptWrapper."</script>"; | |
| 576 | 523 | |
| 577 | 524 | $do_default = false; |
| 578 | 525 | } |
| 579 | 526 | |
| @@ -579,11 +526,9 @@ | ||
| 579 | 526 | |
| 580 | 527 | // server side selector provided (see simple_html_dom selectors http://simplehtmldom.sourceforge.net/manual.htm) |
| 581 | 528 | // basically it's CSS selectors like in jQuery |
| 582 | 529 | } else{ |
| 583 | - if ( ! class_exists( 'simple_html_dom' ) ) { | |
| 584 | - require_once('simple_html_dom.php'); | |
| 585 | - } | |
| 530 | + require_once('simple_html_dom.php'); | |
| 586 | 531 | |
| 587 | 532 | $html_doc = str_get_html($content); |
| 588 | 533 | $target_location = $html_doc->find($location, ($occurrence) - 1); |
| 589 | 534 | |
| @@ -631,13 +576,13 @@ | ||
| 631 | 576 | if ($full_indicator == TABOOLA_JS_INDICATOR){ |
| 632 | 577 | |
| 633 | 578 | $xpath = substr($location,strlen(TABOOLA_JS_INDICATOR)); |
| 634 | 579 | $scriptWrapper = new JavaScriptWrapper("js_inject.min.js",array( |
| 635 | - "{{HTML}}" => $this->js_escape_markup($this->format_taboola_content_home($taboola_content_home,TABOOLA_CONTENT_FORMAT_HTML)), | |
| 636 | - "{{SCRIPT}}" => $this->js_escape_markup($this->format_taboola_content_home($taboola_content_home,TABOOLA_CONTENT_FORMAT_SCRIPT))) | |
| 580 | + "{{HTML}}" => $this->format_taboola_content_home($taboola_content_home,TABOOLA_CONTENT_FORMAT_HTML), | |
| 581 | + "{{SCRIPT}}" => $this->format_taboola_content_home($taboola_content_home,TABOOLA_CONTENT_FORMAT_SCRIPT)) | |
| 637 | 582 | ); |
| 638 | 583 | $scriptWrapper->appendScript("injectWidgetByXpath('".$xpath."');"); |
| 639 | - $content = $content."<span id='tbdefault'></span><script type='text/javascript'>".$this->wpautop_safe_script($scriptWrapper)."</script>"; | |
| 584 | + $content = $content."<span id='tbdefault'></span><script type='text/javascript'>".$scriptWrapper."</script>"; | |
| 640 | 585 | |
| 641 | 586 | $do_default = false; |
| 642 | 587 | } |
| 643 | 588 | |
| @@ -642,12 +587,10 @@ | ||
| 642 | 587 | } |
| 643 | 588 | |
| 644 | 589 | // server side selector provided (see simple_html_dom selectors http://simplehtmldom.sourceforge.net/manual.htm) |
| 645 | 590 | // basically it's CSS selectors like in jQuery |
| 646 | - } else{ | |
| 647 | - if ( ! class_exists( 'simple_html_dom' ) ) { | |
| 591 | + } else{ | |
| 648 | 592 | require_once('simple_html_dom.php'); |
| 649 | - } | |
| 650 | 593 | |
| 651 | 594 | $html_doc = str_get_html($content); |
| 652 | 595 | $target_location = $html_doc->find($location,($this->settings->home_location_string_occurrence)-1); |
| 653 | 596 | |
| @@ -674,19 +617,12 @@ | ||
| 674 | 617 | global $current_user; |
| 675 | 618 | add_menu_page(__('Taboola','taboola_widget'), __('Taboola','taboola_widget'), 'manage_options', 'taboola_widget', array(&$this, 'admin_taboola_settings'), $this->plugin_url.'img/taboola_icon.png', 110); |
| 676 | 619 | } |
| 677 | 620 | |
| 678 | - // Empty numeric inputs must reach MySQL as NULL, not '', or strict mode | |
| 679 | - // rejects the whole row. | |
| 680 | - private function nullable_int($value){ | |
| 681 | - return (isset($value) && trim((string) $value) !== '') ? (int) $value : null; | |
| 682 | - } | |
| 683 | - | |
| 684 | 621 | function admin_taboola_settings(){ |
| 685 | 622 | global $wpdb; |
| 686 | 623 | $settings = $wpdb->get_row("select * from ".$wpdb->prefix."_taboola_settings limit 1"); |
| 687 | 624 | $taboola_errors = array(); |
| 688 | - $taboola_save_error = ''; | |
| 689 | 625 | if($_SERVER['REQUEST_METHOD'] == 'POST'){ |
| 690 | 626 | |
| 691 | 627 | if(trim(strip_tags($_POST['publisher_id'])) == ''){ |
| 692 | 628 | $taboola_errors[] = "Publisher ID"; |
| @@ -768,38 +704,33 @@ | ||
| 768 | 704 | } |
| 769 | 705 | } |
| 770 | 706 | $mid_widgets_json = json_encode($mid_widgets_data); |
| 771 | 707 | |
| 772 | - /* $wpdb formats every value as %s unless told otherwise, so a PHP | |
| 773 | - false or an empty string reaches MySQL as ''. Under | |
| 774 | - STRICT_TRANS_TABLES (the MySQL 8 default) '' is rejected for the | |
| 775 | - TINYINT/INT columns and the whole write is refused. Send real | |
| 776 | - integers for the flags and NULL for empty numeric fields. */ | |
| 777 | 708 | $data = array( |
| 778 | 709 | "publisher_id" => trim($_POST['publisher_id']), |
| 779 | 710 | |
| 780 | - "web_push_enabled" => isset($_POST['web_push_enabled']) ? 1 : 0, | |
| 781 | - "publisher_id_push" => $this->nullable_int($_POST['publisher_id_push'] ?? null), | |
| 711 | + "web_push_enabled" => isset($_POST['web_push_enabled']) ? true : false, | |
| 712 | + "publisher_id_push" => !empty($_POST['publisher_id_push']) ? trim($_POST['publisher_id_push']) : '', | |
| 782 | 713 | |
| 783 | - "first_bc_enabled" => isset($_POST['first_bc_enabled']) ? 1 : 0, | |
| 714 | + "first_bc_enabled" => isset($_POST['first_bc_enabled']) ? true : false, | |
| 784 | 715 | "first_bc_widget_id" => !empty($_POST['first_bc_widget_id']) ? trim($_POST['first_bc_widget_id']) : '', |
| 785 | 716 | "first_bc_placement" => !empty($_POST['first_bc_placement']) ? trim($_POST['first_bc_placement']) : '', |
| 786 | 717 | |
| 787 | - "out_of_content_enabled" => isset($_POST['out_of_content_enabled']) ? 1 : 0, | |
| 718 | + "out_of_content_enabled" => isset($_POST['out_of_content_enabled']) ? true : false, | |
| 788 | 719 | |
| 789 | - "mid_enabled" => isset($_POST['mid_enabled']) ? 1 : 0, | |
| 720 | + "mid_enabled" => isset($_POST['mid_enabled']) ? true : false, | |
| 790 | 721 | "mid_widgets" => $mid_widgets_json, |
| 791 | 722 | |
| 792 | - "home_enabled" => isset($_POST['home_enabled']) ? 1 : 0, | |
| 723 | + "home_enabled" => isset($_POST['home_enabled']) ? true : false, | |
| 793 | 724 | "home_widget_id" => !empty($_POST['home_widget_id']) ? trim($_POST['home_widget_id']) : '', |
| 794 | 725 | "home_placement" => !empty($_POST['home_placement']) ? trim($_POST['home_placement']) : '', |
| 795 | 726 | |
| 796 | - "home_location_string_occurrence" => $this->nullable_int($_POST['home_location_string_occurrence'] ?? null), | |
| 727 | + "home_location_string_occurrence" => !empty($_POST['home_location_string_occurrence']) ? $_POST['home_location_string_occurrence'] : '', | |
| 797 | 728 | "home_location_string" => !empty($_POST['home_location_string']) ? trim($_POST['home_location_string']) : '', |
| 798 | - "category_enabled" => isset($_POST['category_enabled']) ? 1 : 0, | |
| 729 | + "category_enabled" => isset($_POST['category_enabled']) ? true : false, | |
| 799 | 730 | "category_widget_id" => !empty($_POST['category_widget_id']) ? trim($_POST['category_widget_id']) : '', |
| 800 | 731 | "category_placement" => !empty($_POST['category_placement']) ? trim($_POST['category_placement']) : '', |
| 801 | - "category_location_string_occurrence" => $this->nullable_int($_POST['category_location_string_occurrence'] ?? null), | |
| 732 | + "category_location_string_occurrence" => !empty($_POST['category_location_string_occurrence']) ? $_POST['category_location_string_occurrence'] : '', | |
| 802 | 733 | "category_location_string" => !empty($_POST['category_location_string']) ? trim($_POST['category_location_string']) : '', |
| 803 | 734 | |
| 804 | 735 | ); |
| 805 | 736 | |
| @@ -810,20 +741,12 @@ | ||
| 810 | 741 | } |
| 811 | 742 | |
| 812 | 743 | if ($is_valid_nonce) { |
| 813 | 744 | if($settings == NULL){ |
| 814 | - $saved = $wpdb->insert($this->tbl_taboola_settings, $data); | |
| 745 | + $wpdb->insert($this->tbl_taboola_settings, $data); | |
| 815 | 746 | } else { |
| 816 | - $saved = $wpdb->update($this->tbl_taboola_settings, $data, array('id' => $settings->id)); | |
| 747 | + $wpdb->update($this->tbl_taboola_settings, $data, array('id' => $settings->id)); | |
| 817 | 748 | } |
| 818 | - | |
| 819 | - // update() returns 0 when nothing changed; only false is a failure. | |
| 820 | - if ($saved === false) { | |
| 821 | - $taboola_save_error = "The database rejected the write, so your changes were not saved: " | |
| 822 | - . ($wpdb->last_error !== '' ? $wpdb->last_error : 'unknown database error'); | |
| 823 | - } | |
| 824 | - } else { | |
| 825 | - $taboola_save_error = "Security check failed - the settings page had been open too long. Reload it and apply your changes again."; | |
| 826 | 749 | } |
| 827 | 750 | } |
| 828 | 751 | $settings = $wpdb->get_row("select * from ".$wpdb->prefix."_taboola_settings limit 1"); |
| 829 | 752 | } |