Plugin Directory

Changeset 3156386


Ignore:
Timestamp:
09/23/2024 01:47:57 PM (18 months ago)
Author:
deviodigital
Message:

Releasing BoostBox v2.0

Location:
boostbox
Files:
3 added
20 edited

Legend:

Unmodified
Added
Removed
  • boostbox/assets/blueprints/blueprint.json

    r3061236 r3156386  
    11{
    2     "landingPage": "\/wp-admin\//",
     2    "landingPage": "/wp-admin/post-new.php?post_type=boostbox_popups",
    33    "preferredVersions": {
    44        "php": "8.0",
    5         "wp": "6.3"
     5        "wp": "6.4.3"
    66    },
    77    "phpExtensionBundles": [
     
    1313            "username": "admin",
    1414            "password": "password"
    15         },
    16         {
    17             "step": "installTheme",
    18             "themeZipFile": {
    19                 "resource": "wordpress.org\/themes",
    20                 "slug": "ollie"
    21             }
    2215        },
    2316        {
     
    3225        },
    3326        {
     27            "step": "installTheme",
     28            "themeZipFile": {
     29                "resource": "wordpress.org\/themes",
     30                "slug": "olliewp"
     31            }
     32        },
     33        {
    3434            "step": "importFile",
    3535            "file": {
  • boostbox/trunk/README.md

    r3021397 r3156386  
    1 # BoostBox by Devio Digital
     1# BoostBox Plugin for WordPress
    22
    33:rocket: Increase conversion rates, customer retention and revenue.
    44
    5 Build popups for lead generation, content promotion and more using the core WordPress editor.
     5**BoostBox** is a WordPress plugin designed to create flexible and customizable popups, modals, and content overlays within the core WordPress editor.
    66
    7 Easily style your popups, modals, and content overlays in the editor you are already used to. No more trying to learn *another popup plugin* and their unique design experience.
     7Without requiring additional design interfaces or tools, **BoostBox** integrates seamlessly with the editor you're already familiar with, offering the ability to build effective and targeted popups to improve engagement, conversions, and overall website functionality.
    88
    9 With **BoostBox**, the type of popup you build is limitless.
     9No more trying to learn *another popup plugin* and their unique design experience :sunglasses:
    1010
    11 *   Generate email signups
    12 *   Giveaway free downloads
    13 *   Build a sales funnel
    14 *   Showcase featured products
    15 *   Promote upcoming events
    16 *   ... and more!
     11## Key Features
    1712
    18 ### FSE popup builder for WordPress
     13:white_check_mark: Build popups directly within the WordPress editor using blocks, columns, groups, and patterns. 
     14:white_check_mark: Style and configure each popup with custom animation and targeting options. 
     15:white_check_mark: Display popups to capture leads, promote content, showcase products, drive sales funnels, and more.
    1916
    20 The **BoostBox** plugin was created to give website owners the ability to create popups within the WordPress core editor, and it succeeds flawlessly in this mission.
     17### Custom Popup Types
     18With **BoostBox**, you have the flexibility to create a variety of popup types for different needs:
    2119
    22 Using WordPress blocks, columns, groups and your favorite patterns, the style possibilities for the popups you build with the **BoostBox** plugin are endless.
     20:email: **Email Signups**: Capture and grow your email list with targeted popups. 
     21:camera: **Product Showcases**: Highlight featured products or services to boost conversions. 
     22:calendar: **Event Promotion**: Promote upcoming events or announcements with ease. 
     23:money_with_wings: **Lead Generation**: Build custom forms and popups to attract leads. 
     24:dart: **Custom Campaigns**: Tailor popups to your specific goals with advanced targeting. 
     25
     26### Full-Site Editing (FSE) Integration
     27**BoostBox** takes full advantage of WordPress's Full-Site Editing (FSE) capabilities. By leveraging WordPress blocks and patterns, you can design popups that align with your site's theme and branding.
     28
     29There's no need to use third-party popup builders — **BoostBox** integrates natively with the WordPress editor, ensuring a seamless and familiar experience.
     30
     31## Settings Overview
     32
     33### General Settings
     34- **Display Location**: Choose where your popups appear, such as center on the screen, across the entire top, or a box in the bottom right.
     35- **Max Width**: Set an optional max-width for popups to ensure they fit your design needs.
     36- **Cookie Days**: Configure the cookie expiration to control when users will see the popup again (overrides the global setting).
     37
     38### Animation Settings
     39- **Type**: Choose from several animation options (none, fade in, slide up, slide down, slide left, slide right, pop swirl and anvil).
     40- **Animation Speed**: Set the speed of the animation in milliseconds to match your design preferences.
     41
     42### Targeting Options
     43- **General**: Target popups sitewide, on home pages, search results, 404 pages and blog indexes.
     44- **Custom Post Types**: Display popups across all content for specific custom post types.
     45- **Post/Page**: Target individual posts or pages for granular control over popup placement.
     46
     47### Trigger Options
     48- **Auto Open**: Show the popup immediately on page load.
     49- **On Scroll**: Trigger the popup when the user scrolls a specific percentage down the page.
     50- **Timer**: Delay the popup for a set time after the page loads.
     51
     52### Close Button Settings
     53- **Location**: Control the placement of the close button (inside, outside, or hidden).
     54- **Color**: Customize the color of the close button to match your design.
     55
     56## Getting Started
     57
     58### Installation
     591. Upload the **BoostBox** plugin files to the `/wp-content/plugins/boostbox/` directory, install it directly from the WordPress Plugin Repository or by searching for **BoostBox** in your WordPress dashboard.
     602. Activate the plugin through the 'Plugins' menu in WordPress.
     613. Navigate to `BoosBox -> Add New` to configure your first popup.
  • boostbox/trunk/README.txt

    r3148388 r3156386  
    55Requires at least: 3.0.1
    66Tested up to: 6.6.2
    7 Stable tag: 1.6.2
     7Stable tag: 2.0.0
    88License: GPLv2 or later
    99License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    1313== Description ==
    1414
    15 **BoostBox** helps you increase conversion rates, customer retention and revenue.
    16 
    17 Build popups for lead generation, content promotion and more using the core WordPress editor.
    18 
    19 Easily style your popups, modals, and content overlays in the editor you are already used to. No more trying to learn *another popup plugin* and their unique design experience.
    20 
    21 With **BoostBox**, the type of popup you build is limitless.
    22 
    23 *   Generate email signups
    24 *   Giveaway free downloads
    25 *   Build a sales funnel
     15**BoostBox** is a powerful WordPress plugin designed to increase conversion rates, customer retention, and revenue by offering flexible, customizable popups, modals, and content overlays.
     16
     17With **BoostBox**, you can effortlessly build popups directly within the WordPress editor using blocks, columns, groups, and patterns—no need to learn a new interface or design tool. It's a seamless integration with the editor you're already familiar with, enabling you to create targeted popups that drive engagement and conversions.
     18
     19Whether you're looking to capture leads, promote events, showcase products, or drive sales, **BoostBox** has you covered. The plugin's versatility allows you to:
     20
     21*   Capture email signups
    2622*   Showcase featured products
    2723*   Promote upcoming events
     24*   Build custom sales funnels
     25*   Launch targeted campaigns
    2826*   ... and more!
    2927
    30 ### FSE popup builder
    31 
    32 The **BoostBox** plugin was created to give website owners the ability to create popups within the core editor, and it succeeds flawlessly in this mission.
    33 
    34 Using WordPress blocks, columns, groups and patterns, the style possibilities for the popups you build with the **BoostBox** plugin are endless.
     28### Full-Site Editing (FSE) Integration
     29
     30**BoostBox** leverages WordPress Full-Site Editing (FSE) capabilities, enabling you to design popups that perfectly match your site’s branding and layout. Say goodbye to third-party builders—**BoostBox** works natively within WordPress, making popup creation fast and intuitive.
     31
     32### Key Features
     33
     34*   Create popups, modals, and overlays using WordPress blocks and patterns
     35*   Customize animations, display settings, and targeting options
     36*   Support for custom post types and granular targeting of specific pages or posts
     37*   Multiple trigger options including auto-open, scroll, and timer-based popups
     38*   Full control over popup design and placement without leaving the WordPress editor
     39
     40**BoostBox** is your go-to plugin for building popups that enhance engagement, increase conversions, and improve overall site functionality—all without adding complexity to your workflow.
    3541
    3642== Installation ==
     
    39452. Install and activate the plugin directly in your admin panel
    40463. Pat yourself on the back for a job well done :)
     47
     48== Frequently Asked Questions ==
     49
     50= Can I use BoostBox without any coding knowledge? =
     51
     52Yes, BoostBox is designed to work seamlessly within the WordPress editor, using the familiar block-based interface. No coding skills are needed to create beautiful, functional popups.
     53
     54= What types of popups can I create with BoostBox? =
     55
     56BoostBox allows you to create various types of popups including:
     57
     58* Email signups
     59* Product showcases
     60* Event promotions
     61* Lead generation forms
     62* Basically, the ideas are only limited to your imagination
     63
     64= How can I customize the appearance and behavior of my popups? =
     65
     66BoostBox provides a wide range of customization options beyond the actual popup built in the editor, including:
     67
     68* Setting max width for the popup
     69* Choosing from various animation styles (fade, slide, pop swirl, and more)
     70* Configuring popup triggers (on page load, scroll, or timer)
     71* Targeting specific pages, posts, or custom post types
     72* Customizing the look and placement of the close button
     73
     74= Is BoostBox compatible with other WordPress themes and plugins? =
     75
     76Yes, BoostBox is designed to be compatible with most WordPress themes and plugins. It integrates directly with the core WordPress editor, so it should work smoothly across most environments.
    4177
    4278== Screenshots ==
     
    47834. BoostBox popup display settings
    48845. BoostBox metrics displayed on the Popups screen
     856. Another example popup using a pattern from the Powder theme
    4986
    5087== Changelog ==
     88
     89= 2.0.0 =
     90*   Added 2 new AJAX functions for fetching posts in `admin/class-boostbox-admin.php`
     91*   Added Trigger options to display settings metabox in `admin/metaboxes/boostbox-display-settings.php`
     92*   Added 4 new helper functions in `includes/boostbox-helper-functions.php`
     93*   Updated the `boostbox_allowed_tags` function's svg options in `includes/boostbox-helper-functions.php`
     94*   Updated JS to include new trigger options in `admin/js/boostbox-admin.js`
     95*   Updated localize scripts with code for multiple popups in `public/class-boostbox-public.php`
     96*   Updated CSS to include new Trigger tab in `admin/css/boostbox-admin.css`
     97*   Updated `boostbox_popup_post_check` to include CPTs and general targets in `includes/boostbox-helper-functions.php`
     98*   Updated impression and conversion tracking to work with multiple popups in `public/class-boostbox-public.php`
     99*   Updated the public JS to include functionaliy for multiple popups in `public/js/boostbox-public.js`
     100*   Updated popup metabox with checkbox option to disable popups on a page-by-page basis in `admin/metaboxes/boostbox-popup-settings.php`
     101*   Updated CSS to only enqueue if popups are present on the page in `public/class-boostbox-public.php`
     102*   Updated `boostbox_popup_html` to use `wp_footer` instead of `template_redirect` in `public/boostbox-popups.php`
     103*   Updated admin JS to only load if you are on the edit screen in `admin/class-boostbox-admin.php`
     104*   Optimized the display settings metabox tabbed content in `admin/metaboxes/boostbox-display-settings.php`
     105*   General code cleanup throughout multiple files of the plugin
    51106
    52107= 1.6.2 =
  • boostbox/trunk/admin/boostbox-admin-settings.php

    r3148388 r3156386  
    8282        );
    8383
    84         // Field: Global popup.
    85         $boostbox_obj->add_field(
    86             'boostbox_general',
    87             [
    88                 'id'      => 'boostbox_global_popup',
    89                 'type'    => 'select',
    90                 'name'    => esc_attr__( 'Global popup', 'boostbox' ),
    91                 'desc'    => esc_attr__( 'Select the popup used whenever the global option is set on posts/pages', 'boostbox' ),
    92                 'options' => $options,
    93             ]
    94         );
    9584        // Field: Cookie days.
    9685        $boostbox_obj->add_field(
  • boostbox/trunk/admin/class-boostbox-admin.php

    r3148388 r3156386  
    146146        // Check if you're on the edit screen for the "boostbox_popups" Custom Post Type
    147147        if ( is_admin() && $current_screen && (
    148             in_array( $current_screen->post_type, [ 'boostbox_popups', 'post', 'page' ] ) ||
     148            in_array( $current_screen->post_type, [ 'boostbox_popups' ] ) ||
    149149            ( 'post' === $current_screen->base && 'post-new.php' === $current_screen->id )
    150150        ) ) {
    151151            // General: Select2 JS.
    152152            wp_enqueue_script( $this->plugin_name . '-select2', plugin_dir_url( __FILE__ ) . 'js/select2.min.js', [ 'jquery' ], $this->version, false );
     153            // General: Admin JS.
     154            wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/boostbox-admin.js', [ 'jquery', 'wp-hooks', 'wp-blocks', 'wp-color-picker', 'boostbox-select2' ], $this->version, false );
     155            wp_localize_script( $this->plugin_name, 'script_vars', [
     156                'stylesheet_url'      => get_stylesheet_directory_uri(),
     157                'popup_id'            => json_encode( get_the_ID() ),
     158                'metrics_reset_nonce' => wp_create_nonce( 'boostbox_metrics_reset_nonce' ),
     159                'ajax_url'            => admin_url( 'admin-ajax.php' )
     160            ] );
    153161        }
    154         // General: Admin JS.
    155         wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/boostbox-admin.js', [ 'jquery', 'wp-hooks', 'wp-blocks', 'wp-color-picker', 'boostbox-select2' ], $this->version, false );
    156         wp_localize_script( $this->plugin_name, 'script_vars', [
    157             'stylesheet_url'      => get_stylesheet_directory_uri(),
    158             'popup_id'            => json_encode( get_the_ID() ),
    159             'metrics_reset_nonce' => wp_create_nonce( 'boostbox_metrics_reset_nonce' ),
    160         ] );
    161162    }
    162163
     
    196197}
    197198add_action( 'save_post', 'save_boostbox_popups_block', 10, 2 );
     199
     200/**
     201 * Fetch posts by post type
     202 *
     203 * @since  2.0.0
     204 * @return void
     205 */
     206function fetch_posts_by_post_type() {
     207    // Get the selected post types (can be multiple).
     208    $post_types = isset( $_POST['post_types'] ) ? array_map( 'sanitize_text_field', (array) $_POST['post_types'] ) : [];
     209
     210    if ( empty( $post_types ) ) {
     211        wp_send_json_error( 'No post type selected' );
     212    }
     213
     214    // Get posts from the selected post types.
     215    $args = [
     216        'post_type'      => $post_types,
     217        'posts_per_page' => -1,
     218    ];
     219
     220    $posts = get_posts( $args );
     221
     222    $output = [];
     223
     224    // Loop through the posts.
     225    foreach ( $posts as $post ) {
     226        $output[] = array(
     227            'id'    => $post->ID,
     228            'title' => $post->post_title,
     229        );
     230    }
     231
     232    wp_send_json_success( $output );
     233}
     234add_action( 'wp_ajax_fetch_posts_by_post_type', 'fetch_posts_by_post_type' );
     235add_action( 'wp_ajax_nopriv_fetch_posts_by_post_type', 'fetch_posts_by_post_type' );
     236
     237/**
     238 * Fetch posts by search
     239 *
     240 * @since  2.0.0
     241 * @return void
     242 */
     243function fetch_posts_by_search() {
     244    // Check if the search term exists.
     245    $search_term = isset( $_POST['search_term'] ) ? sanitize_text_field( $_POST['search_term'] ) : '';
     246
     247    // If search term is empty, return no results.
     248    if ( empty( $search_term ) ) {
     249        wp_send_json_error( 'No search term provided.' );
     250    }
     251
     252    // Get all public post types (including custom post types).
     253    $post_types = get_post_types( [ 'public' => true ], 'names' );
     254
     255    // Fetch posts based on the search term across all post types.
     256    $args = [
     257        's'              => $search_term, // Search term for post title
     258        'post_type'      => $post_types,  // Search all public post types
     259        'posts_per_page' => -1,
     260        'post_status'    => 'publish',
     261    ];
     262
     263    $posts = get_posts( $args );
     264
     265    $output = [];
     266
     267    // Loop through the posts and prepare output for Select2.
     268    foreach ( $posts as $post ) {
     269        $output[] = [
     270            'id'    => $post->ID,
     271            'text'  => $post->post_title,
     272        ];
     273    }
     274
     275    // Return the posts as a JSON response.
     276    wp_send_json_success( $output );
     277}
     278add_action( 'wp_ajax_fetch_posts_by_search', 'fetch_posts_by_search' );
     279add_action( 'wp_ajax_nopriv_fetch_posts_by_search', 'fetch_posts_by_search' );
  • boostbox/trunk/admin/css/boostbox-admin.css

    r3027699 r3156386  
    11461146.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio2:focus ~ .tabs #second-tab,
    11471147.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio3:focus ~ .tabs #third-tab,
    1148 .edit-post-meta-boxes-area #boostbox_display_settings .inside #radio4:focus ~ .tabs #fourth-tab  {
     1148.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio4:focus ~ .tabs #fourth-tab,
     1149.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio5:focus ~ .tabs #fifth-tab  {
    11491150}
    11501151
     
    11811182.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio2:checked ~ .tabs #second-tab,
    11821183.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio3:checked ~ .tabs #third-tab,
    1183 .edit-post-meta-boxes-area #boostbox_display_settings .inside #radio4:checked ~ .tabs #fourth-tab{
     1184.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio4:checked ~ .tabs #fourth-tab,
     1185.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio5:checked ~ .tabs #fifth-tab {
    11841186    background-color: #007cba;
    11851187    color: #fff;
     
    12081210.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio2:checked ~ .panels #second-panel,
    12091211.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio3:checked ~ .panels #third-panel,
    1210 .edit-post-meta-boxes-area #boostbox_display_settings .inside #radio4:checked ~ .panels #fourth-panel{
     1212.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio4:checked ~ .panels #fourth-panel,
     1213.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio5:checked ~ .panels #fifth-panel {
    12111214    display: block;
    12121215}
  • boostbox/trunk/admin/css/boostbox-admin.min.css

    r3027699 r3156386  
    1 body.boostbox_popups_page_settings{background:#fff}body.boostbox_popups_page_analytics #wpcontent,body.boostbox_popups_page_settings #wpcontent{padding-left:0}body.boostbox_popups_page_analytics ul#adminmenu>li.current>a.current:after,body.boostbox_popups_page_settings ul#adminmenu a.wp-has-current-submenu:after{display:none}.boostbox-field .select2-container{width:100%!important}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.clearfix{zoom:1}.boostbox *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.toplevel_page_analytics #wpcontent,.toplevel_page_boostbox_settings #wpcontent{padding-left:0}.boostbox{overflow:hidden;margin:0}img.icon{width:20px;height:auto}.boostbox .panel-right .theme-image{background:#64717e;padding:2% 2% 0 2%;display:inline-block;width:100%;width:23%}.boostbox .panels{margin:0}.boostbox .panel{display:inline-block;width:100%;padding:4%;background:#fff;font-size:16px;animation:smoothFade .3s;-moz-animation:smoothFade .3s;-webkit-animation:smoothFade .3s;-o-animation:smoothFade .3s}@keyframes smoothFade{from{opacity:0}to{opacity:1}}@-moz-keyframes smoothFade{from{opacity:0}to{opacity:1}}@-webkit-keyframes smoothFade{from{opacity:0}to{opacity:1}}.boostbox .panel-left.visible{display:inline-block;animation:smoothFade .3s;-moz-animation:smoothFade .3s;-webkit-animation:smoothFade .3s;-o-animation:smoothFade .3s}.boostbox .panel-right p{font-size:16px;line-height:1.6}.boostbox .panel a{text-decoration:none}.boostbox .panel hr{height:1px;margin:2em 0;border:0;border-top:solid 2px #e6eaed}.boostbox .panel-left ol,.boostbox .panel-left ul{margin:0 0 5% 0;background:#f5f4f6;padding:5% 5% 5% 8%;list-style-type:square;font-size:19px;line-height:1.8}.boostbox .panel-left ol li,.boostbox .panel-left ul li{border-bottom:dotted 1px #ccc;margin-bottom:20px;padding-bottom:20px}.boostbox .panel-left ol li:last-child,.boostbox .panel-left ul li:last-child{border:none;margin-bottom:0;padding-bottom:0}.boostbox .panel-left .toc li ul{margin-bottom:0;padding-bottom:0;padding-top:3%}.boostbox .panel-left .toc li ul li{list-style:square}.boostbox .panel-left{display:inline-block;display:none;width:63%}.boostbox .panel-left img{max-width:100%;height:auto}.boostbox .panel-left h3{display:inline-block}.boostbox .panel-left h3,.boostbox .panel-right h3,.boostbox .panel-right p:first-child{margin-top:0}.boostbox .panel-right img{max-width:100%}.boostbox .panel-left ul.anchor-nav{padding:5%}.boostbox .anchor-nav li{list-style:none}.boostbox .back-to-top{text-transform:uppercase;font-size:11px;color:#999;position:absolute;right:0;top:8px}.boostbox .panels h3{width:100%;position:relative;padding-right:90px}.boostbox h3 .back-to-top{float:right}.boostbox .panel-right{width:32%;float:right;vertical-align:top}.boostbox .panel-aside{margin-bottom:40px;background:#f5f4f6}.boostbox .panel-aside:last-child{margin-bottom:0}.boostbox .panel-club{padding:0}.boostbox .panel-club .panel-club-inside{padding:30px 40px}.boostbox .panel-club img{padding-bottom:0;width:100%;max-width:100%;height:auto}.boostbox .panel-plugin img{padding:0}.boostbox .panel-aside .club-button{width:100%;text-align:center;line-height:46px;height:48px;margin-top:10px;font-size:15px}.boostbox .panel-aside h4{margin-top:0;font-size:1.1em;line-height:1.4}.boostbox .panel-aside ul{margin-bottom:25px}.boostbox .panel-aside li{list-style-type:square;margin-left:18px}#wpbody-content .boostbox .error,#wpbody-content .boostbox .updated{margin-top:2%}.boostbox .error+.intro-wrap,.boostbox .updated+.intro-wrap{padding-top:2%}.boostbox .intro-wrap{padding:4% 0 0 0;background:#333 url(../images/background.jpg) no-repeat bottom center;background-size:cover;background-attachment:fixed}.boostbox .intro{display:inline-block;width:100%;vertical-align:top;padding:0 50px;margin-bottom:4.5%}.boostbox .intro h3{font-size:40px;line-height:1.2;font-weight:300;margin:0;color:#fff;display:inline-block}.boostbox .devio-digital-logo{max-width:245px;float:right;margin-top:2px;transition:.3s ease;-webkit-backface-visibility:hidden;transform:translateZ(0)}@media only screen and (max-width:800px){.boostbox .devio-digital-logo{float:none;margin-bottom:6%}}.boostbox .devio-digital-logo:hover{transform:scale(1.03)}.boostbox .devio-digital-logo:active{transform:scale(1)}.boostbox .intro h4{color:#868b96;font-weight:400;font-size:18px;line-height:1.6;margin:0}.boostbox .inline-list{display:inline-block;width:100%;margin:0;background:0 0;padding:0 50px}.boostbox .inline-list li{display:inline-block;margin:0}.boostbox .inline-list li:last-child{margin-right:0;padding-right:0}.boostbox .inline-list li a{font-size:18px;text-decoration:none;padding:25px 30px;display:inline-block;color:#fff}.boostbox .inline-list li a::-moz-focus-inner,.boostbox .inline-list li a:active,.boostbox ul.inline-list a:focus{outline:0;border:0;box-shadow:none}.boostbox ul.toc{padding-left:5%}.boostbox .toc li{list-style-type:none}.boostbox .inline-list li.current a{background:#fff;outline:0;border:none;box-shadow:none;border-top-right-radius:3px;border-top-left-radius:3px;color:#76bd43}.boostbox .inline-list li a i{font-size:16px;margin-right:5px}.boostbox #changelog{display:none}.boostbox #install-video{display:none}.boostbox .arrayvideo{padding:3% 0}.boostbox #updates-panel ul{border-bottom:1px dotted #ddd;padding:1% 0 5% 3%;list-style-position:inside;background:0 0}.boostbox #updates-panel li{border-bottom:0;margin-bottom:0;padding-bottom:7px}.boostbox #updates-panel h4{font-size:1.1em;margin:.5em 0}.boostbox #themes.visible+.panel-right{display:none}.boostbox #themes.visible{width:100%}.boostbox .theme-intro{margin-bottom:4.5%;background:#f5f4f6;padding:2%}@media only screen and (max-width:800px){.boostbox .theme-intro{padding:5%;margin-bottom:5%}}.boostbox .theme-intro-left{max-width:60%;display:inline-block}@media only screen and (max-width:1110px){.boostbox .theme-intro-left{max-width:54%}}@media only screen and (max-width:800px){.boostbox .theme-intro-left,.boostbox .theme-intro-right{max-width:100%;width:100%}.boostbox .theme-intro-right{margin-bottom:5%}}.boostbox .theme-intro-left p{font-size:20px;margin-top:0;margin-bottom:0}.boostbox .theme-intro-right{float:right;margin-top:12px}@media only screen and (max-width:800px){.boostbox .theme-intro-right{margin-bottom:0}}.boostbox .theme-intro-right .button-primary{line-height:56px;height:auto;font-size:20px;padding:10px 20px}@media only screen and (max-width:800px){.boostbox .theme-intro-right .button-primary{width:100%;text-align:center}}.boostbox .array-theme{display:inline-block;width:30%;margin-right:5%;margin-bottom:3%;vertical-align:top}.boostbox .theme-list .array-theme:nth-child(3n+3){margin-right:0}@media only screen and (max-width:1100px){.boostbox .theme-list .array-theme{width:47%}.boostbox .theme-list .array-theme:nth-child(2n+2){margin-right:0}.boostbox .theme-list .array-theme:nth-child(3n+3){margin-right:5%}}@media only screen and (max-width:768px){.boostbox .theme-list .array-theme{width:100%}.boostbox .theme-list .array-theme:nth-child(2n+2){margin-right:0}.boostbox .theme-list .array-theme:nth-child(3n+3){margin-right:0}}.boostbox .array-theme .theme-image{max-height:265px;overflow:hidden;width:100%}.boostbox #themes .array-theme h3{margin-bottom:0;padding-right:0;margin-top:1em}.boostbox h4 .button{float:right;background:#5ac779;color:#fff;border-radius:3px;font-size:14px;padding:3px 6px;vertical-align:middle;margin-top:-5px!important}@media only screen and (max-width:768px){.boostbox .intro,.boostbox .panel-left,.boostbox .panel-right,.boostbox .theme-image{width:100%;float:none}.boostbox .intro{padding:0 20px;margin-top:3%;margin-bottom:8%}.boostbox .intro h2{font-size:34px}.boostbox .intro h3{margin-bottom:0}.boostbox .inline-list{margin-bottom:5%;padding:0 20px}.boostbox .inline-list li{width:100%}.boostbox .inline-list li a{width:100%;display:block}.boostbox .panel-right .theme-image{width:100%;margin-bottom:15px;padding:6% 6% 0 6%}.boostbox .enter-license .submit{width:100%}.boostbox .activate{width:100%;float:none;text-align:left;margin-bottom:20px}}.clear:after,.clear:before{content:'';display:table}.clear:after{clear:both}.boostbox .panel-plugin .panel-club-inside{padding:0}.boostbox .panel-plugin ul{margin:0}.boostbox .panel-plugin .cell{padding:30px 40px;border-bottom:solid 1px #e3e3e4;margin:0;list-style:none}.boostbox .panel-plugin .cell:last-child{border-bottom-width:2px}.quick-start ul i{color:#59bb31}.boostbox .step-complete .button-primary{pointer-events:none}.boostbox .panel-title.cell{background:#3e446d;padding:20px 40px;text-align:center}.boostbox .panel-title h3{margin:0;color:#fff;padding:0}.boostbox .panel-title h3 i{margin-right:6px}.boostbox h2{margin-top:0}.boostbox .form-table th{padding-left:0}.boostbox p.wp-caption-text{font-size:16px;margin-top:.5em;text-align:center}.boostbox div[id^=attachment]{max-width:100%!important}.theme-image{max-height:290px;overflow:hidden;border:solid 1px #ddd;border-radius:5px;display:block}.footer-wrap{margin-top:6%}@media only screen and (max-width:768px){.footer-wrap{padding:5%}}.block-footer{margin-top:5%;margin-bottom:3%}.block-footer-column{width:32.8%;display:inline-block;text-align:center;border-right:solid 3px #eceaee;padding:0 3%}@media (max-width:600px){.block-footer-column{width:100%;padding:0;border:none;margin-bottom:40px}}.block-footer-column:last-child{border-right:none}.block-footer-column i{color:#444;font-size:40px;margin-bottom:10px}.boostbox .block-footer-column h3{padding-right:0;margin-bottom:0;font-weight:700;font-size:22px}.boostbox .block-footer-column p{font-size:17px;margin-bottom:1.5em}.block-footer .button-primary{font-size:18px;border-radius:100px;padding:10px 15px;height:auto;font-size:16px}.block-footer .button-primary:active{vertical-align:initial!important;border-top-width:1px!important;color:#fff;background:#76bd43}.visit-title{text-align:center;display:inline-block;width:100%;margin:0}.footer{text-align:center;margin-top:8%}.footer-links a{margin:0 8px;position:relative}.footer a:hover{border-bottom:dotted 1px;color:#24282d}.footer-links a:after{content:"\22C5";position:absolute;right:-14px}.footer-links a:last-child:after{display:none}.block-feature-wrap{margin:0}@media (max-width:875px){.block-feature-wrap{padding-bottom:5%}}.block-feature-wrap h2{margin-bottom:0!important}.block-feature-wrap p{margin-top:1%;margin-bottom:5%}@media (max-width:875px){.block-feature-wrap p{margin-top:3%}}.block-feature-wrap i{color:#c5c0da;font-size:20px}.block-feature{background:#fff;padding:4% 3% 3% 3%;box-shadow:0 2px rgba(38,30,65,.1);border-radius:5px;width:30.6%;vertical-align:top;float:left;margin-right:4%;margin-bottom:4%;transition:.3s ease;-webkit-backface-visibility:hidden;backface-visibility:hidden}#themes .block-feature{background:0 0;padding:0;box-shadow:none;margin-bottom:2%}@media (max-width:600px){.block-feature{padding:8% 4% 3% 4%}}.block-feature:nth-child(3n+1){clear:both}@media only screen and (min-width:600px){.block-feature:nth-child(3n+3){margin-right:0}.block-feature:nth-child(3n+1):nth-last-child(-n+3),.block-feature:nth-child(3n+1):nth-last-child(-n+3)~.post{margin-bottom:0}}@media (max-width:600px){.block-feature{width:100%;margin-right:0;margin-bottom:8%}.block-feature:last-child{margin-bottom:0}}.block-feature .block-feature-icon,.block-feature .block-feature-text{display:inline-block;width:100%;vertical-align:top}.block-feature .block-feature-text{text-align:center}.block-feature .block-feature-text h3{font-weight:700;margin-bottom:15px;padding-right:0}@media (min-width:1000px){.block-feature .block-feature-text h3{font-size:22px}}.block-feature .block-feature-text p{font-size:16px}.block-feature .block-feature-icon{text-align:center;margin-bottom:6%}.block-feature .block-feature-icon img{max-height:110px;border:none}.block-split-left,.block-split-right{display:inline-block;width:49%;vertical-align:top}@media (max-width:600px){.block-split-left,.block-split-right{width:100%}}.block-split-left{padding-right:4%;padding-top:4%;padding-bottom:5%}.block-split-right{background-size:1400px;padding:0 2%;float:right}.block-theme img{border:none!important;display:block}#boostbox-panel.visible+.panel-left+.panel-left+.panel-right,#boostbox-panel.visible+.panel-left+.panel-right{display:none}#boostbox-analytics-panel,#boostbox-panel{width:100%}.gutenberg-notice{background:#f1f0ff;border-radius:5px;padding:15px 20px 20px 20px;border:solid 1px #dbd9f7;margin-bottom:5%}.panel .gutenberg-notice p{font-size:17px;color:#493c8c;line-height:1.4;display:inline}.panel .gutenberg-notice .button-primary{margin-left:15px}.panel .gutenberg-notice .button-primary:active{vertical-align:baseline;border-top-width:1px}@media (max-width:600px){.panel .gutenberg-notice p{display:block}.panel .gutenberg-notice .button-primary{margin-left:0}}.boostbox-analytics-charts,.boostbox-charts{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;flex-basis:100%;width:100%;position:relative}.viewport-main{display:flex;flex-direction:column;flex-wrap:wrap;justify-content:space-between;flex-basis:100%;margin:24px 0;box-shadow:0 0 10px 3px rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius:3px}.viewport-main.two{flex-basis:48%}.viewport-main.three{flex-basis:31.333%}@media (max-width:768px){.viewport-main.three,.viewport-main.two{flex-basis:100%}}.viewport-top{background-color:#fff;border-radius:4px 4px 0 0;height:30px;position:relative}.viewport-top-dot{border-radius:50%;position:absolute;height:9px;width:9px;top:11px}.viewport-top-dot:first-child{background-color:#ff006d;left:10px}.viewport-top-dot:nth-child(2){background-color:#ffca50;left:26px}.viewport-top-dot:nth-child(3){background-color:#57ffd1;left:42px}.viewport-top span.title{position:absolute;line-height:30px;font-size:13px;font-weight:600;text-align:center;width:100%}.viewport-body{background:linear-gradient(180deg,#f7f7f7 0,#fff 19%,#fff);border:0;padding:15px}.section-title{flex-basis:100%}.section-title h2{margin:1em 0!important}.boostbox-field{padding:12px 0}.edit-post-meta-boxes-area #boostbox_display_settings .inside .boostbox-field.hidden{display:none}.edit-post-meta-boxes-area #boostbox_display_settings .inside .boostbox-field.active{display:block}.boostbox-field select{box-sizing:border-box;display:block;margin:0;width:100%}.edit-post-meta-boxes-area #boostbox_display_settings .inside{display:flex;flex-wrap:wrap;justify-content:space-between;flex-direction:row;padding:0;margin-top:0}.edit-post-meta-boxes-area #boostbox_display_settings .inside .boostbox-field{flex-basis:23%;display:flex;flex-wrap:wrap;flex-direction:column;box-sizing:border-box}.edit-post-meta-boxes-area #boostbox_display_settings .inside .boostbox-field p{font-weight:600}@media screen and (max-width:782px){.edit-post-meta-boxes-area #boostbox_display_settings .inside{flex-direction:column}}.edit-post-meta-boxes-area #boostbox_display_settings .inside .radio-tabs{display:flex;flex-flow:row;justify-content:space-between;width:100%;border-top:1px solid #ddd}.edit-post-meta-boxes-area #boostbox_display_settings .inside .radio-tabs .state{position:absolute;left:-10000px}.edit-post-meta-boxes-area #boostbox_display_settings .inside .tabs{display:flex;flex-flow:column;justify-content:space-around;align-self:flex-start;text-align:left;min-width:200px}@media (max-width:620px){.edit-post-meta-boxes-area #boostbox_display_settings .inside .tabs{min-width:auto}}.edit-post-meta-boxes-area #boostbox_display_settings .inside .radio-tabs .tab{flex:none;display:flex;flex-flow:column;padding:0 12px;background-color:transparent;cursor:hand;cursor:pointer}.edit-post-meta-boxes-area #boostbox_display_settings .inside .radio-tabs .tab:hover{background-color:#f0f0f0}.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio1:checked~.tabs #first-tab,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio2:checked~.tabs #second-tab,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio3:checked~.tabs #third-tab,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio4:checked~.tabs #fourth-tab{background-color:#007cba;color:#fff;cursor:default}.edit-post-meta-boxes-area #boostbox_display_settings .inside .tab-label{font-size:14px;line-height:1.4rem;padding:12px;font-weight:600}.edit-post-meta-boxes-area #boostbox_display_settings .inside .radio-tabs .panels{flex:1;border-left:1px solid #ddd}.edit-post-meta-boxes-area #boostbox_display_settings .inside .radio-tabs .panel{display:none;margin:0 24px;padding:12px 0}.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio1:checked~.panels #first-panel,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio2:checked~.panels #second-panel,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio3:checked~.panels #third-panel,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio4:checked~.panels #fourth-panel{display:block}.edit-post-meta-boxes-area #boostbox_display_settings .inside .left{text-align:left}#boostbox_display_settings.closed .inside{display:none}.boostbox-metrics{display:flex;flex-wrap:wrap;justify-content:space-between;flex-direction:row;padding-bottom:6px;margin-bottom:6px;border-bottom:1px solid #ddd}button#reset-metrics{margin-top:6px}
     1body.boostbox_popups_page_settings{background:#fff}body.boostbox_popups_page_analytics #wpcontent,body.boostbox_popups_page_settings #wpcontent{padding-left:0}body.boostbox_popups_page_analytics ul#adminmenu>li.current>a.current:after,body.boostbox_popups_page_settings ul#adminmenu a.wp-has-current-submenu:after{display:none}.boostbox-field .select2-container{width:100%!important}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.clearfix{zoom:1}.boostbox *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.toplevel_page_analytics #wpcontent,.toplevel_page_boostbox_settings #wpcontent{padding-left:0}.boostbox{overflow:hidden;margin:0}img.icon{width:20px;height:auto}.boostbox .panel-right .theme-image{background:#64717e;padding:2% 2% 0 2%;display:inline-block;width:100%;width:23%}.boostbox .panels{margin:0}.boostbox .panel{display:inline-block;width:100%;padding:4%;background:#fff;font-size:16px;animation:smoothFade .3s;-moz-animation:smoothFade .3s;-webkit-animation:smoothFade .3s;-o-animation:smoothFade .3s}@keyframes smoothFade{from{opacity:0}to{opacity:1}}@-moz-keyframes smoothFade{from{opacity:0}to{opacity:1}}@-webkit-keyframes smoothFade{from{opacity:0}to{opacity:1}}.boostbox .panel-left.visible{display:inline-block;animation:smoothFade .3s;-moz-animation:smoothFade .3s;-webkit-animation:smoothFade .3s;-o-animation:smoothFade .3s}.boostbox .panel-right p{font-size:16px;line-height:1.6}.boostbox .panel a{text-decoration:none}.boostbox .panel hr{height:1px;margin:2em 0;border:0;border-top:solid 2px #e6eaed}.boostbox .panel-left ol,.boostbox .panel-left ul{margin:0 0 5% 0;background:#f5f4f6;padding:5% 5% 5% 8%;list-style-type:square;font-size:19px;line-height:1.8}.boostbox .panel-left ol li,.boostbox .panel-left ul li{border-bottom:dotted 1px #ccc;margin-bottom:20px;padding-bottom:20px}.boostbox .panel-left ol li:last-child,.boostbox .panel-left ul li:last-child{border:none;margin-bottom:0;padding-bottom:0}.boostbox .panel-left .toc li ul{margin-bottom:0;padding-bottom:0;padding-top:3%}.boostbox .panel-left .toc li ul li{list-style:square}.boostbox .panel-left{display:inline-block;display:none;width:63%}.boostbox .panel-left img{max-width:100%;height:auto}.boostbox .panel-left h3{display:inline-block}.boostbox .panel-left h3,.boostbox .panel-right h3,.boostbox .panel-right p:first-child{margin-top:0}.boostbox .panel-right img{max-width:100%}.boostbox .panel-left ul.anchor-nav{padding:5%}.boostbox .anchor-nav li{list-style:none}.boostbox .back-to-top{text-transform:uppercase;font-size:11px;color:#999;position:absolute;right:0;top:8px}.boostbox .panels h3{width:100%;position:relative;padding-right:90px}.boostbox h3 .back-to-top{float:right}.boostbox .panel-right{width:32%;float:right;vertical-align:top}.boostbox .panel-aside{margin-bottom:40px;background:#f5f4f6}.boostbox .panel-aside:last-child{margin-bottom:0}.boostbox .panel-club{padding:0}.boostbox .panel-club .panel-club-inside{padding:30px 40px}.boostbox .panel-club img{padding-bottom:0;width:100%;max-width:100%;height:auto}.boostbox .panel-plugin img{padding:0}.boostbox .panel-aside .club-button{width:100%;text-align:center;line-height:46px;height:48px;margin-top:10px;font-size:15px}.boostbox .panel-aside h4{margin-top:0;font-size:1.1em;line-height:1.4}.boostbox .panel-aside ul{margin-bottom:25px}.boostbox .panel-aside li{list-style-type:square;margin-left:18px}#wpbody-content .boostbox .error,#wpbody-content .boostbox .updated{margin-top:2%}.boostbox .error+.intro-wrap,.boostbox .updated+.intro-wrap{padding-top:2%}.boostbox .intro-wrap{padding:4% 0 0 0;background:#333 url(../images/background.jpg) no-repeat bottom center;background-size:cover;background-attachment:fixed}.boostbox .intro{display:inline-block;width:100%;vertical-align:top;padding:0 50px;margin-bottom:4.5%}.boostbox .intro h3{font-size:40px;line-height:1.2;font-weight:300;margin:0;color:#fff;display:inline-block}.boostbox .devio-digital-logo{max-width:245px;float:right;margin-top:2px;transition:.3s ease;-webkit-backface-visibility:hidden;transform:translateZ(0)}@media only screen and (max-width:800px){.boostbox .devio-digital-logo{float:none;margin-bottom:6%}}.boostbox .devio-digital-logo:hover{transform:scale(1.03)}.boostbox .devio-digital-logo:active{transform:scale(1)}.boostbox .intro h4{color:#868b96;font-weight:400;font-size:18px;line-height:1.6;margin:0}.boostbox .inline-list{display:inline-block;width:100%;margin:0;background:0 0;padding:0 50px}.boostbox .inline-list li{display:inline-block;margin:0}.boostbox .inline-list li:last-child{margin-right:0;padding-right:0}.boostbox .inline-list li a{font-size:18px;text-decoration:none;padding:25px 30px;display:inline-block;color:#fff}.boostbox .inline-list li a::-moz-focus-inner,.boostbox .inline-list li a:active,.boostbox ul.inline-list a:focus{outline:0;border:0;box-shadow:none}.boostbox ul.toc{padding-left:5%}.boostbox .toc li{list-style-type:none}.boostbox .inline-list li.current a{background:#fff;outline:0;border:none;box-shadow:none;border-top-right-radius:3px;border-top-left-radius:3px;color:#76bd43}.boostbox .inline-list li a i{font-size:16px;margin-right:5px}.boostbox #changelog{display:none}.boostbox #install-video{display:none}.boostbox .arrayvideo{padding:3% 0}.boostbox #updates-panel ul{border-bottom:1px dotted #ddd;padding:1% 0 5% 3%;list-style-position:inside;background:0 0}.boostbox #updates-panel li{border-bottom:0;margin-bottom:0;padding-bottom:7px}.boostbox #updates-panel h4{font-size:1.1em;margin:.5em 0}.boostbox #themes.visible+.panel-right{display:none}.boostbox #themes.visible{width:100%}.boostbox .theme-intro{margin-bottom:4.5%;background:#f5f4f6;padding:2%}@media only screen and (max-width:800px){.boostbox .theme-intro{padding:5%;margin-bottom:5%}}.boostbox .theme-intro-left{max-width:60%;display:inline-block}@media only screen and (max-width:1110px){.boostbox .theme-intro-left{max-width:54%}}@media only screen and (max-width:800px){.boostbox .theme-intro-left,.boostbox .theme-intro-right{max-width:100%;width:100%}.boostbox .theme-intro-right{margin-bottom:5%}}.boostbox .theme-intro-left p{font-size:20px;margin-top:0;margin-bottom:0}.boostbox .theme-intro-right{float:right;margin-top:12px}@media only screen and (max-width:800px){.boostbox .theme-intro-right{margin-bottom:0}}.boostbox .theme-intro-right .button-primary{line-height:56px;height:auto;font-size:20px;padding:10px 20px}@media only screen and (max-width:800px){.boostbox .theme-intro-right .button-primary{width:100%;text-align:center}}.boostbox .array-theme{display:inline-block;width:30%;margin-right:5%;margin-bottom:3%;vertical-align:top}.boostbox .theme-list .array-theme:nth-child(3n+3){margin-right:0}@media only screen and (max-width:1100px){.boostbox .theme-list .array-theme{width:47%}.boostbox .theme-list .array-theme:nth-child(2n+2){margin-right:0}.boostbox .theme-list .array-theme:nth-child(3n+3){margin-right:5%}}@media only screen and (max-width:768px){.boostbox .theme-list .array-theme{width:100%}.boostbox .theme-list .array-theme:nth-child(2n+2){margin-right:0}.boostbox .theme-list .array-theme:nth-child(3n+3){margin-right:0}}.boostbox .array-theme .theme-image{max-height:265px;overflow:hidden;width:100%}.boostbox #themes .array-theme h3{margin-bottom:0;padding-right:0;margin-top:1em}.boostbox h4 .button{float:right;background:#5ac779;color:#fff;border-radius:3px;font-size:14px;padding:3px 6px;vertical-align:middle;margin-top:-5px!important}@media only screen and (max-width:768px){.boostbox .intro,.boostbox .panel-left,.boostbox .panel-right,.boostbox .theme-image{width:100%;float:none}.boostbox .intro{padding:0 20px;margin-top:3%;margin-bottom:8%}.boostbox .intro h2{font-size:34px}.boostbox .intro h3{margin-bottom:0}.boostbox .inline-list{margin-bottom:5%;padding:0 20px}.boostbox .inline-list li{width:100%}.boostbox .inline-list li a{width:100%;display:block}.boostbox .panel-right .theme-image{width:100%;margin-bottom:15px;padding:6% 6% 0 6%}.boostbox .enter-license .submit{width:100%}.boostbox .activate{width:100%;float:none;text-align:left;margin-bottom:20px}}.clear:after,.clear:before{content:'';display:table}.clear:after{clear:both}.boostbox .panel-plugin .panel-club-inside{padding:0}.boostbox .panel-plugin ul{margin:0}.boostbox .panel-plugin .cell{padding:30px 40px;border-bottom:solid 1px #e3e3e4;margin:0;list-style:none}.boostbox .panel-plugin .cell:last-child{border-bottom-width:2px}.quick-start ul i{color:#59bb31}.boostbox .step-complete .button-primary{pointer-events:none}.boostbox .panel-title.cell{background:#3e446d;padding:20px 40px;text-align:center}.boostbox .panel-title h3{margin:0;color:#fff;padding:0}.boostbox .panel-title h3 i{margin-right:6px}.boostbox h2{margin-top:0}.boostbox .form-table th{padding-left:0}.boostbox p.wp-caption-text{font-size:16px;margin-top:.5em;text-align:center}.boostbox div[id^=attachment]{max-width:100%!important}.theme-image{max-height:290px;overflow:hidden;border:solid 1px #ddd;border-radius:5px;display:block}.footer-wrap{margin-top:6%}@media only screen and (max-width:768px){.footer-wrap{padding:5%}}.block-footer{margin-top:5%;margin-bottom:3%}.block-footer-column{width:32.8%;display:inline-block;text-align:center;border-right:solid 3px #eceaee;padding:0 3%}@media (max-width:600px){.block-footer-column{width:100%;padding:0;border:none;margin-bottom:40px}}.block-footer-column:last-child{border-right:none}.block-footer-column i{color:#444;font-size:40px;margin-bottom:10px}.boostbox .block-footer-column h3{padding-right:0;margin-bottom:0;font-weight:700;font-size:22px}.boostbox .block-footer-column p{font-size:17px;margin-bottom:1.5em}.block-footer .button-primary{font-size:18px;border-radius:100px;padding:10px 15px;height:auto;font-size:16px}.block-footer .button-primary:active{vertical-align:initial!important;border-top-width:1px!important;color:#fff;background:#76bd43}.visit-title{text-align:center;display:inline-block;width:100%;margin:0}.footer{text-align:center;margin-top:8%}.footer-links a{margin:0 8px;position:relative}.footer a:hover{border-bottom:dotted 1px;color:#24282d}.footer-links a:after{content:"\22C5";position:absolute;right:-14px}.footer-links a:last-child:after{display:none}.block-feature-wrap{margin:0}@media (max-width:875px){.block-feature-wrap{padding-bottom:5%}}.block-feature-wrap h2{margin-bottom:0!important}.block-feature-wrap p{margin-top:1%;margin-bottom:5%}@media (max-width:875px){.block-feature-wrap p{margin-top:3%}}.block-feature-wrap i{color:#c5c0da;font-size:20px}.block-feature{background:#fff;padding:4% 3% 3% 3%;box-shadow:0 2px rgba(38,30,65,.1);border-radius:5px;width:30.6%;vertical-align:top;float:left;margin-right:4%;margin-bottom:4%;transition:.3s ease;-webkit-backface-visibility:hidden;backface-visibility:hidden}#themes .block-feature{background:0 0;padding:0;box-shadow:none;margin-bottom:2%}@media (max-width:600px){.block-feature{padding:8% 4% 3% 4%}}.block-feature:nth-child(3n+1){clear:both}@media only screen and (min-width:600px){.block-feature:nth-child(3n+3){margin-right:0}.block-feature:nth-child(3n+1):nth-last-child(-n+3),.block-feature:nth-child(3n+1):nth-last-child(-n+3)~.post{margin-bottom:0}}@media (max-width:600px){.block-feature{width:100%;margin-right:0;margin-bottom:8%}.block-feature:last-child{margin-bottom:0}}.block-feature .block-feature-icon,.block-feature .block-feature-text{display:inline-block;width:100%;vertical-align:top}.block-feature .block-feature-text{text-align:center}.block-feature .block-feature-text h3{font-weight:700;margin-bottom:15px;padding-right:0}@media (min-width:1000px){.block-feature .block-feature-text h3{font-size:22px}}.block-feature .block-feature-text p{font-size:16px}.block-feature .block-feature-icon{text-align:center;margin-bottom:6%}.block-feature .block-feature-icon img{max-height:110px;border:none}.block-split-left,.block-split-right{display:inline-block;width:49%;vertical-align:top}@media (max-width:600px){.block-split-left,.block-split-right{width:100%}}.block-split-left{padding-right:4%;padding-top:4%;padding-bottom:5%}.block-split-right{background-size:1400px;padding:0 2%;float:right}.block-theme img{border:none!important;display:block}#boostbox-panel.visible+.panel-left+.panel-left+.panel-right,#boostbox-panel.visible+.panel-left+.panel-right{display:none}#boostbox-analytics-panel,#boostbox-panel{width:100%}.gutenberg-notice{background:#f1f0ff;border-radius:5px;padding:15px 20px 20px 20px;border:solid 1px #dbd9f7;margin-bottom:5%}.panel .gutenberg-notice p{font-size:17px;color:#493c8c;line-height:1.4;display:inline}.panel .gutenberg-notice .button-primary{margin-left:15px}.panel .gutenberg-notice .button-primary:active{vertical-align:baseline;border-top-width:1px}@media (max-width:600px){.panel .gutenberg-notice p{display:block}.panel .gutenberg-notice .button-primary{margin-left:0}}.boostbox-analytics-charts,.boostbox-charts{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;flex-basis:100%;width:100%;position:relative}.viewport-main{display:flex;flex-direction:column;flex-wrap:wrap;justify-content:space-between;flex-basis:100%;margin:24px 0;box-shadow:0 0 10px 3px rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);border-radius:3px}.viewport-main.two{flex-basis:48%}.viewport-main.three{flex-basis:31.333%}@media (max-width:768px){.viewport-main.three,.viewport-main.two{flex-basis:100%}}.viewport-top{background-color:#fff;border-radius:4px 4px 0 0;height:30px;position:relative}.viewport-top-dot{border-radius:50%;position:absolute;height:9px;width:9px;top:11px}.viewport-top-dot:first-child{background-color:#ff006d;left:10px}.viewport-top-dot:nth-child(2){background-color:#ffca50;left:26px}.viewport-top-dot:nth-child(3){background-color:#57ffd1;left:42px}.viewport-top span.title{position:absolute;line-height:30px;font-size:13px;font-weight:600;text-align:center;width:100%}.viewport-body{background:linear-gradient(180deg,#f7f7f7 0,#fff 19%,#fff);border:0;padding:15px}.section-title{flex-basis:100%}.section-title h2{margin:1em 0!important}.boostbox-field{padding:12px 0}.edit-post-meta-boxes-area #boostbox_display_settings .inside .boostbox-field.hidden{display:none}.edit-post-meta-boxes-area #boostbox_display_settings .inside .boostbox-field.active{display:block}.boostbox-field select{box-sizing:border-box;display:block;margin:0;width:100%}.edit-post-meta-boxes-area #boostbox_display_settings .inside{display:flex;flex-wrap:wrap;justify-content:space-between;flex-direction:row;padding:0;margin-top:0}.edit-post-meta-boxes-area #boostbox_display_settings .inside .boostbox-field{flex-basis:23%;display:flex;flex-wrap:wrap;flex-direction:column;box-sizing:border-box}.edit-post-meta-boxes-area #boostbox_display_settings .inside .boostbox-field p{font-weight:600}@media screen and (max-width:782px){.edit-post-meta-boxes-area #boostbox_display_settings .inside{flex-direction:column}}.edit-post-meta-boxes-area #boostbox_display_settings .inside .radio-tabs{display:flex;flex-flow:row;justify-content:space-between;width:100%;border-top:1px solid #ddd}.edit-post-meta-boxes-area #boostbox_display_settings .inside .radio-tabs .state{position:absolute;left:-10000px}.edit-post-meta-boxes-area #boostbox_display_settings .inside .tabs{display:flex;flex-flow:column;justify-content:space-around;align-self:flex-start;text-align:left;min-width:200px}@media (max-width:620px){.edit-post-meta-boxes-area #boostbox_display_settings .inside .tabs{min-width:auto}}.edit-post-meta-boxes-area #boostbox_display_settings .inside .radio-tabs .tab{flex:none;display:flex;flex-flow:column;padding:0 12px;background-color:transparent;cursor:hand;cursor:pointer}.edit-post-meta-boxes-area #boostbox_display_settings .inside .radio-tabs .tab:hover{background-color:#f0f0f0}.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio1:checked~.tabs #first-tab,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio2:checked~.tabs #second-tab,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio3:checked~.tabs #third-tab,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio4:checked~.tabs #fourth-tab,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio5:checked~.tabs #fifth-tab{background-color:#007cba;color:#fff;cursor:default}.edit-post-meta-boxes-area #boostbox_display_settings .inside .tab-label{font-size:14px;line-height:1.4rem;padding:12px;font-weight:600}.edit-post-meta-boxes-area #boostbox_display_settings .inside .radio-tabs .panels{flex:1;border-left:1px solid #ddd}.edit-post-meta-boxes-area #boostbox_display_settings .inside .radio-tabs .panel{display:none;margin:0 24px;padding:12px 0}.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio1:checked~.panels #first-panel,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio2:checked~.panels #second-panel,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio3:checked~.panels #third-panel,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio4:checked~.panels #fourth-panel,.edit-post-meta-boxes-area #boostbox_display_settings .inside #radio5:checked~.panels #fifth-panel{display:block}.edit-post-meta-boxes-area #boostbox_display_settings .inside .left{text-align:left}#boostbox_display_settings.closed .inside{display:none}.boostbox-metrics{display:flex;flex-wrap:wrap;justify-content:space-between;flex-direction:row;padding-bottom:6px;margin-bottom:6px;border-bottom:1px solid #ddd}button#reset-metrics{margin-top:6px}
  • boostbox/trunk/admin/js/boostbox-admin.js

    r3027699 r3156386  
    1 jQuery(document).ready(function ($) {
     1jQuery(function ($) {
    22    // Construct the path to the theme.json file.
    33    const themeJsonPath = script_vars.stylesheet_url + '/theme.json';
     
    6666    $( '#boostbox_close_icon_placement' ).select2();
    6767    $( '#boostbox_popup_selected' ).select2();
     68
     69    // Initialize Select2 for Custom Post Types.
     70    $('#custom_post_types').select2({
     71        placeholder: "Select Post Type(s)"
     72    });
     73
     74    // Initialize Select2 for General Settings with multiple selection enabled
     75    $('#general_field').select2({
     76        placeholder: "Select General Settings",
     77        multiple: true // Explicitly enabling multiple
     78    });
     79
     80    // Initialize Select2 for Posts with AJAX search.
     81    $('#posts').select2({
     82        placeholder: "Search and Select Post(s)",
     83        ajax: {
     84            url: script_vars.ajax_url,
     85            method: 'POST',
     86            dataType: 'json',
     87            delay: 250, // Delay for better performance
     88            data: function (params) {
     89                return {
     90                    action: 'fetch_posts_by_search', // AJAX action
     91                    search_term: params.term // Search term entered by user
     92                };
     93            },
     94            processResults: function (response) {
     95                if (response.success) {
     96                    return {
     97                        results: response.data
     98                    };
     99                } else {
     100                    return {
     101                        results: []
     102                    };
     103                }
     104            },
     105            cache: true
     106        },
     107        minimumInputLength: 3 // Minimum characters before search
     108    });
    68109
    69110    // Reset the metrics when button is clicked in metabox.
  • boostbox/trunk/admin/metaboxes/boostbox-display-settings.php

    r3148388 r3156386  
    5656    wp_create_nonce( plugin_basename( __FILE__ ) ) . '" />';
    5757
    58     echo '<div class="radio-tabs">
    59 
    60     <input class="state" type="radio" title="' . esc_attr__( 'General', 'boostbox' ) . '" name="input-state" id="radio1" checked />
    61     <input class="state" type="radio" title="' . esc_attr__( 'Animation', 'boostbox' ) . '" name="input-state" id="radio2" />
    62     <input class="state" type="radio" title="' . esc_attr__( 'Trigger', 'boostbox' ) . '" name="input-state" id="radio3" />
    63     <input class="state" type="radio" title="' . esc_attr__( 'Close Button', 'boostbox' ) . '" name="input-state" id="radio4" />
    64 
    65     <div class="tabs">
    66         <label for="radio1" id="first-tab" class="tab">
    67             <div class="tab-label">' . esc_attr__( 'General', 'boostbox' ) . '</div>
    68         </label>
    69         <label for="radio2" id="second-tab" class="tab">
    70             <div class="tab-label">' . esc_attr__( 'Animation', 'boostbox' ) . '</div>
    71         </label>
    72         <label for="radio3" id="third-tab" class="tab">
    73             <div class="tab-label">' . esc_attr__( 'Trigger', 'boostbox' ) . '</div>
    74         </label>
    75         <label for="radio4" id="fourth-tab" class="tab">
    76             <div class="tab-label">' . esc_attr__( 'Close Button', 'boostbox' ) . '</div>
    77         </label>
    78     </div>
     58    $tabs = [
     59        esc_attr__( 'General', 'boostbox' ),
     60        esc_attr__( 'Animation', 'boostbox' ),
     61        esc_attr__( 'Target', 'boostbox' ),
     62        esc_attr__( 'Trigger', 'boostbox' ),
     63        esc_attr__( 'Close Button', 'boostbox' ),
     64    ];
     65
     66    // Tab label ID names.
     67    $tab_labels = ['first', 'second', 'third', 'fourth', 'fifth'];
     68
     69    echo '<div class="radio-tabs">';
     70
     71    $i = 0;
     72
     73    // Loop through the tabs.
     74    foreach ( $tabs as $tab ) {
     75        $i++;
     76        if ( $i == 1 ) {
     77            $checked = 'checked';
     78        } else {
     79            $checked = '';
     80        }
     81        // Add the tab.
     82        echo '<input class="state" type="radio" title="' . esc_html( $tab ) . '" name="input-state" id="radio' . $i . '" ' . $checked . ' />';
     83    }
     84
     85    echo '<div class="tabs">';
     86
     87    $i = 0;
     88
     89    // Loop through the tabs.
     90    foreach ( $tabs as $tab ) {
     91        $i++;
     92        // Get the current tab's label.
     93        $tab_label = isset( $tab_labels[$i - 1] ) ? $tab_labels[$i - 1] : 'tab' . $i;
     94        // Add the corresponding label with the dynamic ID.
     95        echo '<label for="radio' . $i . '" id="' . $tab_label . '-tab" class="tab">
     96                <div class="tab-label">' . esc_html( $tab ) . '</div>
     97            </label>';
     98    }
     99
     100    echo '</div>
    79101
    80102    <div class="panels">
     
    198220
    199221        <div id="third-panel" class="panel animated slideInRight">', boostbox_allowed_tags() );
     222
     223        echo display_custom_post_type_select();
     224
     225        echo wp_kses( '</div>
     226
     227        <div id="fourth-panel" class="panel animated slideInRight">', boostbox_allowed_tags() );
    200228            // Create an array of triggers.
    201229            $triggers = [
     
    254282        echo wp_kses( '</div>
    255283
    256         <div id="fourth-panel" class="panel animated slideInRight">', boostbox_allowed_tags() );
     284        <div id="fifth-panel" class="panel animated slideInRight">', boostbox_allowed_tags() );
    257285            // Create an array of placements.
    258286            $placements = [
     
    318346function boostbox_display_settings_metabox_save( $post_id, $post ) {
    319347    /**
    320      * Verify this came from the our screen and with proper authorization,
     348     * Verify this came from our screen and with proper authorization,
    321349     * because save_post can be triggered at other times
    322350     */
    323     if ( ! isset( $_POST['boostbox_display_settings_meta_noncename' ] ) || ! wp_verify_nonce( $_POST['boostbox_display_settings_meta_noncename'], plugin_basename( __FILE__ ) ) ) {
     351    if ( ! isset( $_POST['boostbox_display_settings_meta_noncename'] ) || ! wp_verify_nonce( $_POST['boostbox_display_settings_meta_noncename'], 'boostbox_display_settings_meta_nonce_action' ) ) {
    324352        return $post->ID;
    325353    }
    326354
    327355    // Is the user allowed to edit the post or page?
    328     if ( ! current_user_can( 'edit_post', $post->ID ) ) {
     356    if ( ! current_user_can( 'edit_post', $post_id ) ) {
    329357        return $post->ID;
     358    }
     359
     360    // Don't save during autosave.
     361    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
     362        return $post->ID;
     363    }
     364
     365    // Don't save during post revision.
     366    if ( 'revision' === $post->post_type ) {
     367        return;
    330368    }
    331369
     
    342380    $settings_meta['boostbox_scroll_distance']      = filter_input( INPUT_POST, 'boostbox_scroll_distance' );
    343381
     382    // Save custom post types (as an array).
     383    if ( isset( $_POST['custom_post_types'] ) ) {
     384        $custom_post_types = array_map( 'sanitize_text_field', (array) $_POST['custom_post_types'] );
     385        update_post_meta( $post_id, '_custom_post_types', $custom_post_types );
     386    } else {
     387        delete_post_meta( $post_id, '_custom_post_types' );
     388    }
     389
     390    // Save selected posts (as an array).
     391    if ( isset( $_POST['posts'] ) ) {
     392        $selected_posts = array_map( 'sanitize_text_field', (array) $_POST['posts'] );
     393        update_post_meta( $post_id, '_selected_posts', $selected_posts );
     394    } else {
     395        delete_post_meta( $post_id, '_selected_posts' );
     396    }
     397
     398    // Save general field (as an array).
     399    if ( isset( $_POST['general_field'] ) && is_array( $_POST['general_field'] ) ) {
     400        $general_field = array_map( 'sanitize_text_field', $_POST['general_field'] ); // Sanitize each value
     401        update_post_meta( $post_id, '_general_field', $general_field );
     402    } else {
     403        delete_post_meta( $post_id, '_general_field' );
     404    }
     405
    344406    // Save $settings_meta as metadata.
    345407    foreach ( $settings_meta as $key => $value ) {
    346         // Bail on post revisions.
    347         if ( 'revision' === $post->post_type ) {
    348             return;
     408        // Check if the value is an array or a single value.
     409        $value = is_array( $value ) ? $value : sanitize_text_field( $value );
     410
     411        // Check for meta value and either update or add the metadata.
     412        if ( get_post_meta( $post_id, $key, false ) ) {
     413            update_post_meta( $post_id, $key, $value );
     414        } else {
     415            add_post_meta( $post_id, $key, $value );
    349416        }
    350         $value = implode( ',', (array) $value );
    351         // Check for meta value and either update or add the metadata.
    352         if ( get_post_meta( $post->ID, $key, false ) ) {
    353             update_post_meta( $post->ID, $key, $value );
    354         } else {
    355             add_post_meta( $post->ID, $key, $value );
    356         }
    357         // Delete the metavalue if blank.
    358         if ( ! $value ) {
    359             delete_post_meta( $post->ID, $key );
     417
     418        // Delete the meta value if blank.
     419        if ( empty( $value ) ) {
     420            delete_post_meta( $post_id, $key );
    360421        }
    361422    }
  • boostbox/trunk/admin/metaboxes/boostbox-popup-settings.php

    r3148388 r3156386  
    6060    global $post;
    6161
    62     // Noncename needed to verify where the data originated.
     62    // Nonce for verification.
    6363    echo '<input type="hidden" name="boostbox_popup_settings_meta_noncename" id="boostbox_popup_settings_meta_noncename" value="' .
    6464    wp_create_nonce( plugin_basename( __FILE__ ) ) . '" />';
    6565
    66     // Args for popups.
    67     $args = [
    68         'hierarchical' => 1,
    69         'exclude'      => '',
    70         'include'      => '',
    71         'meta_key'     => '',
    72         'meta_value'   => '',
    73         'authors'      => '',
    74         'child_of'     => 0,
    75         'parent'       => -1,
    76         'exclude_tree' => '',
    77         'number'       => '',
    78         'offset'       => 0,
    79         'post_type'    => 'boostbox_popups',
    80         'post_status'  => 'publish',
    81         'orderby'      => 'title',
    82         'order'        => 'ASC'
    83     ];
     66    // Retrieve the current value for disabling the popup.
     67    $popup_disabled = get_post_meta( $post->ID, 'boostbox_popup_disabled', true );
    8468
    85     $args = apply_filters( 'boostbox_popup_settings_args', $args );
    86 
    87     // Get all popups.
    88     $popups = get_posts( $args );
    89 
    90     // Popup selected.
    91     $popup_selected = get_post_meta( $post->ID, 'boostbox_popup_selected', true );
    92 
    93     // Select a Popup: Build the field.
    94     $field = '<div class="boostbox-field">';
    95     $field .= '<p>' . esc_attr__( 'Select popup to display', 'boostbox' ) . '</p>';
    96     $field .= '<select id="boostbox_popup_selected" name="boostbox_popup_selected">';
    97     $field .= '<option value="popup_disabled">' . esc_attr__( 'Disable Popup', 'boostbox' ) . '</option>';
    98     $field .= '<option value="">' . esc_attr__( 'Global Popup', 'boostbox' ) . '</option>';
    99     // Loop through popups.
    100     if ( ! empty( $popups ) ) {
    101         foreach ( $popups as $popup ) {
    102             $selected = '';
    103             if ( $popup->ID == $popup_selected ) {
    104                 $selected = 'selected="selected"';
    105             }
    106             $field .= '<option value="' . esc_attr( $popup->ID ) . '" '. $selected .'>' . esc_html( $popup->post_title ) . '</option>';
    107         }
    108     }
    109     $field .= '</select>';
    110     $field .= '</div>';
    111 
    112     echo wp_kses( $field, boostbox_allowed_tags() );
     69    // Build the checkbox field.
     70    echo '<div class="boostbox-field">';
     71    echo '<label for="boostbox_popup_disabled">';
     72    echo '<input type="checkbox" id="boostbox_popup_disabled" name="boostbox_popup_disabled" value="1" ' . checked( 1, $popup_disabled, false ) . '/>';
     73    echo esc_html__( 'Disable Popups', 'boostbox' );
     74    echo '</label>';
     75    echo '</div>';
    11376}
    11477
     
    12386function boostbox_popup_settings_metabox_save( $post_id, $post ) {
    12487
    125     /**
    126      * Verify this came from the our screen and with proper authorization,
    127      * because save_post can be triggered at other times
    128      */
     88    // Verify this came from our screen and with proper authorization.
    12989    if ( ! isset( $_POST['boostbox_popup_settings_meta_noncename' ] ) || ! wp_verify_nonce( $_POST['boostbox_popup_settings_meta_noncename'], plugin_basename( __FILE__ ) ) ) {
    13090        return $post->ID;
    13191    }
    13292
    133     // Is the user allowed to edit the post or page?
     93    // Check if the user has permission to edit the post/page.
    13494    if ( ! current_user_can( 'edit_post', $post->ID ) ) {
    13595        return $post->ID;
    13696    }
    13797
    138     // Popup settings.
    139     $settings_meta['boostbox_popup_selected'] = filter_input( INPUT_POST, 'boostbox_popup_selected' );
     98    // Checkbox value for disabling the popup.
     99    $popup_disabled = isset( $_POST['boostbox_popup_disabled'] ) ? 1 : 0;
    140100
    141     // Save $settings_meta as metadata.
    142     foreach ( $settings_meta as $key => $value ) {
    143         // Bail on post revisions.
    144         if ( 'revision' === $post->post_type ) {
    145             return;
    146         }
    147         $value = implode( ',', (array) $value );
    148         // Check for meta value and either update or add the metadata.
    149         if ( get_post_meta( $post->ID, $key, false ) ) {
    150             update_post_meta( $post->ID, $key, $value );
    151         } else {
    152             add_post_meta( $post->ID, $key, $value );
    153         }
    154         // Delete the metavalue if blank.
    155         if ( ! $value ) {
    156             delete_post_meta( $post->ID, $key );
    157         }
     101    // Update or delete the meta value based on the checkbox state.
     102    if ( $popup_disabled ) {
     103        update_post_meta( $post->ID, 'boostbox_popup_disabled', $popup_disabled );
     104    } else {
     105        delete_post_meta( $post->ID, 'boostbox_popup_disabled' );
    158106    }
    159107}
  • boostbox/trunk/boostbox.php

    r3148388 r3156386  
    1414 * Plugin URI:        https://deviodigital.com/boostbox-lead-generation-plugin
    1515 * Description:       Build popups for lead generation, content promotion and more using the core editor.
    16  * Version:           1.6.2
     16 * Version:           2.0.0
    1717 * Author:            Devio Digital
    1818 * Author URI:        https://deviodigital.com
     
    2929
    3030// Current plugin version.
    31 define( 'BOOSTBOX_VERSION', '1.6.2' );
     31define( 'BOOSTBOX_VERSION', '2.0.0' );
    3232
    3333// Plugin basename.
  • boostbox/trunk/includes/boostbox-helper-functions.php

    r3148388 r3156386  
    6565    // SVG.
    6666    $my_allowed['svg'] = [
    67         'xmlns'          => [],
    68         'width'          => [],
    69         'height'         => [],
    70         'viewbox'        => [],
    71         'class'          => [],
    72         'aria-hidden'    => [],
    73         'aria-labeledby' => []
     67        'xmlns'           => [],
     68        'width'           => [],
     69        'height'          => [],
     70        'viewBox'         => [],
     71        'stroke-width'    => [],
     72        'stroke'          => [],
     73        'fill'            => [],
     74        'stroke-linecap'  => [],
     75        'stroke-linejoin' => [],
     76        'class'           => [],
    7477    ];
    7578    $my_allowed['path'] = [
    76         'd'    => [],
    77         'fill' => []
    78     ];
     79        'd'      => [],
     80        'stroke' => [],
     81        'fill'   => [],
     82    ];
     83    $my_allowed['line'] = [
     84        'x1' => [],
     85        'y1' => [],
     86        'x2' => [],
     87        'y2' => [],
     88        'stroke' => [],
     89    ];
     90
    7991    return $my_allowed;
    8092}
     
    253265    return apply_filters( 'boostbox_settings_cookie_days', $cookie_days, $popup_id );
    254266}
     267
     268/**
     269 * Display custom post type and general fields
     270 *
     271 * @since  2.0.0
     272 * @return void
     273 */
     274function display_custom_post_type_select() {
     275    global $post;
     276
     277    // Add nonce field for security.
     278    wp_nonce_field( 'boostbox_display_settings_meta_nonce_action', 'boostbox_display_settings_meta_noncename' );
     279
     280    // Retrieve previously saved values.
     281    $saved_custom_post_types = get_post_meta( $post->ID, '_custom_post_types', true );
     282    $saved_selected_posts    = get_post_meta( $post->ID, '_selected_posts', true );
     283    $saved_general_field     = get_post_meta( $post->ID, '_general_field', true ); // New general field value
     284
     285    // Ensure the retrieved values are arrays to avoid issues.
     286    $saved_custom_post_types = !empty($saved_custom_post_types) ? (array) $saved_custom_post_types : [];
     287    $saved_selected_posts    = !empty($saved_selected_posts) ? (array) $saved_selected_posts : [];
     288
     289    // Get all public post types (including built-in ones).
     290    $args = [
     291        'public'   => true,
     292    ];
     293    $custom_post_types = get_post_types( $args, 'objects' );
     294
     295    // General Field (Home Page, Search Results, etc.)
     296    $field  = '<div class="boostbox-field">';
     297    $field .= '<p><label for="general_field">' . esc_html__( 'General Pages', 'boostbox' ) . ':</label></p>';
     298    $field .= '<select id="general_field" name="general_field[]" multiple>'; // Change to array name 'general_field[]'
     299    $general_options = [
     300        'site_wide'      => esc_html__( 'Sitewide', 'boostbox' ),
     301        'home_page'      => esc_html__( 'Home Page', 'boostbox' ),
     302        'search_results' => esc_html__( 'Search Results', 'boostbox' ),
     303        '404_page'       => esc_html__( '404 Page', 'boostbox' ),
     304        'posts_archive'  => esc_html__( 'Posts Archive', 'boostbox' ),
     305    ];
     306
     307    // Filter the general options.
     308    $general_options = apply_filters( 'boostbox_display_metabox_target_general_options', $general_options );
     309
     310    // Populate the general field with the saved value.
     311    foreach ( $general_options as $value => $label ) {
     312        $selected = in_array( $value, (array) $saved_general_field ) ? 'selected' : ''; // Treat saved value as array
     313        $field .= '<option value="' . esc_attr( $value ) . '" ' . $selected . '>' . esc_html( $label ) . '</option>';
     314    }
     315
     316    $field .= '</select>';
     317    $field .= '</div>';
     318
     319    // Custom Post Types Field.
     320    $field .= '<div class="boostbox-field">';
     321    $field .= '<p><label for="custom_post_types">' . esc_html__( 'Custom Post Type(s)', 'boostbox' ) . ':</label></p>';
     322    $field .= '<select id="custom_post_types" class="select2" name="custom_post_types[]" multiple>';
     323    $field .= '<option value="">' . esc_html__( 'Select Post Type(s)', 'boostbox' ) . '</option>';
     324
     325    // Populate select field with custom post types.
     326    foreach ( $custom_post_types as $post_type ) {
     327        $selected = in_array( $post_type->name, $saved_custom_post_types ) ? 'selected' : '';
     328        $field .= '<option value="' . esc_attr( $post_type->name ) . '" ' . $selected . '>' . esc_html( $post_type->label ) . '</option>';
     329    }
     330
     331    $field .= '</select>';
     332    $field .= '</div>';
     333
     334    // Posts Field (dynamically loaded via AJAX).
     335    $field .= '<div class="boostbox-field">';
     336    $field .= '<p><label for="posts">' . esc_html__( 'Singular Content', 'boostbox' ) . ':</label></p>';
     337    $field .= '<select id="posts" class="select2" name="posts[]" multiple>';
     338    $field .= '<option value="">' . esc_html__( 'Search and Select Post(s)', 'boostbox' ) . '</option>';
     339
     340    // Populate the select field for posts with saved values.
     341    foreach ( $saved_selected_posts as $post_id ) {
     342        $post_title = get_the_title( $post_id );
     343        $field .= '<option value="' . esc_attr( $post_id ) . '" selected>' . esc_html( $post_title ) . '</option>';
     344    }
     345
     346    $field .= '</select>';
     347    $field .= '</div>';
     348
     349    // Filter the fields.
     350    $field = apply_filters( 'display_custom_post_type_select_fields', $field );
     351
     352    echo $field;
     353}
     354
     355/**
     356 * Detect the current page context for displaying the popup.
     357 *
     358 * @since  2.0.0
     359 * @return string The detected context for the current page (e.g., 'home_page', 'search_results').
     360 */
     361function boostbox_detect_page_context() {
     362    $context = '';
     363
     364    // Detect the page type using standard WordPress conditionals.
     365    if ( is_front_page() ) {
     366        $context = 'home_page';
     367    } elseif ( is_home() ) {
     368        $context = 'posts_archive'; // Default blog post archive.
     369    } elseif ( is_search() ) {
     370        $context = 'search_results';
     371    } elseif ( is_404() ) {
     372        $context = '404_page';
     373    } else {
     374        // Get all public post types.
     375        $public_post_types = get_post_types( [ 'public' => true ], 'names' );
     376
     377        // Loop through each public post type and check if we are viewing a single post or archive of that type.
     378        foreach ( $public_post_types as $post_type ) {
     379            if ( is_singular( $post_type ) ) {
     380                // Dynamically set the context for single post types.
     381                $context = 'single_' . $post_type;
     382                break; // Exit the loop once a matching post type is found.
     383            } elseif ( is_post_type_archive( $post_type ) ) {
     384                // Dynamically set the context for custom post type archives.
     385                $context = 'archive_' . $post_type;
     386                break; // Exit the loop once a matching archive is found.
     387            }
     388        }
     389    }
     390
     391    // Allow developers to modify or add custom contexts using a filter.
     392    return apply_filters( 'boostbox_detect_page_context', $context );
     393}
     394
     395/**
     396 * Check if a given post ID is found in the _selected_posts metadata,
     397 * matches the general options, or matches custom post types in the boostbox_popups metadata.
     398 *
     399 * @param int $post_id The ID of the post to check.
     400 *
     401 * @since 2.0.0
     402 * @return array|false An array of boostbox_popups post IDs if found, false otherwise.
     403 */
     404function boostbox_popup_post_check( $post_id ) {
     405    // Ensure the $post_id is an integer.
     406    $post_id = (int) $post_id;
     407
     408    $popup_disabled = get_post_meta( $post_id, 'boostbox_popup_disabled', true );
     409    if ( $popup_disabled ) {
     410        // Skip showing the popup for this post.
     411        return false;
     412    }
     413
     414    // Array to store boostbox_popups post IDs where the $post_id is found.
     415    $found_in_popups = [];
     416
     417    // Get the current page context using the new filterable function.
     418    $context = boostbox_detect_page_context();
     419
     420    // Query all published posts of type 'boostbox_popups'.
     421    $query = new WP_Query( [
     422        'post_type'   => 'boostbox_popups',
     423        'post_status' => 'publish',
     424        'fields'      => 'ids', // Only get post IDs to reduce query size.
     425    ] );
     426
     427    // Loop through the found posts.
     428    if ( $query->have_posts() ) {
     429        foreach ( $query->posts as $popup_post_id ) {
     430            // Get the _selected_posts metadata.
     431            $selected_posts = get_post_meta( $popup_post_id, '_selected_posts', true );
     432
     433            // Ensure it's always an array.
     434            if ( ! is_array( $selected_posts ) ) {
     435                $selected_posts = ! empty( $selected_posts ) ? (array) $selected_posts : [];
     436            }
     437
     438            // Convert all post IDs in _selected_posts to integers for reliable comparison.
     439            $selected_posts = array_map( 'intval', $selected_posts );
     440
     441            // Check if the provided $post_id exists in the selected posts array.
     442            if ( in_array( $post_id, $selected_posts, true ) ) {
     443                $found_in_popups[] = $popup_post_id; // Store the boostbox_popups post ID.
     444            }
     445
     446            // Get the general options from the _general_field metadata.
     447            $popup_general_options = get_post_meta( $popup_post_id, '_general_field', true );
     448
     449            // Handle empty or false meta fields to avoid warnings.
     450            if ( $popup_general_options === false ) {
     451                continue;
     452            }
     453
     454            // Check if the popup should be displayed site-wide.
     455            if ( is_array( $popup_general_options ) && in_array( 'site_wide', $popup_general_options, true ) ) {
     456                $found_in_popups[] = $popup_post_id; // Add if site-wide is selected.
     457                continue; // No need for further checks if it's site-wide.
     458            }
     459
     460            // Check specific page contexts.
     461            if ( is_array( $popup_general_options ) && ! empty( $context ) && in_array( $context, $popup_general_options, true ) ) {
     462                $found_in_popups[] = $popup_post_id;
     463            } else {
     464                error_log('Context mismatch: ' . $context . ' not found in ' . print_r($popup_general_options, true));
     465            }
     466
     467            // Check the current post type against the custom post types saved in the metadata.
     468            $custom_post_types = get_post_meta( $popup_post_id, '_custom_post_types', true );
     469
     470            if ( is_array( $custom_post_types ) ) {
     471                $current_post_type = get_post_type();
     472               
     473                // Check if the current post type matches any of the saved custom post types in the metadata.
     474                if ( in_array( $current_post_type, $custom_post_types, true ) ) {
     475                    $found_in_popups[] = $popup_post_id; // Add if the current post type matches.
     476                }
     477            }
     478        }
     479    }
     480
     481    // If found in any boostbox_popups posts, return the array of post IDs, otherwise return false.
     482    return ! empty( $found_in_popups ) ? array_unique( $found_in_popups ) : false;
     483}
     484
     485/**
     486 * Build the popup HTML
     487 *
     488 * @param int $popup_id The ID of the popup to build.
     489 *
     490 * @since  2.0.0
     491 * @return void
     492 */
     493function boostbox_popup_build_html( $popup_id ) {
     494    global $content_width;
     495
     496    // Ensure the $popup_id is an integer.
     497    $popup_id = (int) $popup_id;
     498
     499    // Set popup width from popup meta.
     500    $popup_width = 'max-width: ' . get_post_meta( $popup_id, 'boostbox_display_max_width', true );
     501    if ( ! get_post_meta( $popup_id, 'boostbox_display_max_width', true ) ) {
     502        $popup_width = get_cover_block_styles( $popup_id );
     503    }
     504    $max_width = 'style="' . $popup_width . '"';
     505
     506    // Popup position.
     507    $popup_position = get_post_meta( $popup_id, 'boostbox_display_location', true );
     508
     509    // Popup animation.
     510    $popup_animation = get_post_meta( $popup_id, 'boostbox_animation_type', true );
     511
     512    // Close icon color and placement.
     513    $close_color     = get_post_meta( $popup_id, 'boostbox_close_icon_color', true ) ?: '#FFFFFF';
     514    $close_placement = get_post_meta( $popup_id, 'boostbox_close_icon_placement', true ) ?: 'outside';
     515   
     516    // Close icon (default or custom).
     517    $close_icon = apply_filters( 'boostbox_popup_close_icon', '<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-x" width="32" height="32" viewBox="0 0 24 24" stroke-width="1.5" stroke="' . $close_color . '" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg>', $close_color );
     518
     519    // Popup overlay and content classes.
     520    $popup_overlay_classes = apply_filters( 'boostbox_popup_overlay_classes', 'boostbox-popup-overlay' );
     521    $popup_content_classes = apply_filters( 'boostbox_popup_content_classes', 'boostbox-popup-content' );
     522    ?>
     523    <!-- Popup container with unique data-popup-id -->
     524    <div class="<?php esc_attr_e( $popup_overlay_classes ); ?>" role="dialog" data-popup-id="<?php echo esc_attr( $popup_id ); ?>">
     525        <div class="<?php esc_attr_e( $popup_content_classes ); ?> <?php esc_html_e( $popup_position . ' ' . $popup_animation ); ?>"<?php echo wp_kses_post( $max_width ); ?>>
     526            <?php
     527            // Query args for content.
     528            $args = [
     529                'post_type'           => 'boostbox_popups',
     530                'post_status'         => 'publish',
     531                'p'                   => $popup_id,
     532                'no_found_rows'       => true,
     533                'ignore_sticky_posts' => true
     534            ];
     535
     536            // Build the query.
     537            $query = new WP_Query( $args );
     538
     539            // Output content.
     540            if ( $query->have_posts() ) {
     541                while ( $query->have_posts() ) {
     542                    $query->the_post();
     543                    the_content();
     544                }
     545                wp_reset_postdata();
     546            }
     547            ?>
     548            <!-- Close button if not hidden -->
     549            <?php if ( $close_placement !== 'hidden' ) { ?>
     550            <button class="boostbox-close <?php echo ( $close_placement == 'inside' ) ? 'inside' : ''; ?>">
     551                <?php echo wp_kses( $close_icon, boostbox_allowed_tags() ); ?>
     552            </button>
     553            <?php } ?>
     554        </div>
     555    </div>
     556    <?php
     557}
  • boostbox/trunk/includes/class-boostbox.php

    r3148388 r3156386  
    7878    public function __construct() {
    7979        $this->plugin_name = 'boostbox';
    80         $this->version     = '1.6.2';
     80        $this->version     = '2.0.0';
    8181
    8282        if ( defined( 'BOOSTBOX_VERSION' ) ) {
  • boostbox/trunk/public/boostbox-popups.php

    r3148388 r3156386  
    2626 */
    2727function boostbox_popup_html() {
    28     global $content_width;
    29     // Settings.
    30     $settings = get_option( 'boostbox_general' );
    31     // Get the popup ID.
    32     $popup_id = get_post_meta( get_the_ID(), 'boostbox_popup_selected', true );
    33     // Check for global popup.
    34     if ( '' == $popup_id && '' !== $settings ) {
    35         $popup_id = $settings['boostbox_global_popup'];
     28    // Check popups for post ID's.
     29    $popup_check = boostbox_popup_post_check( get_the_ID() );
     30
     31    // Loop through popup checks.
     32    foreach ( $popup_check as $popup_id ) {
     33        echo boostbox_popup_build_html( $popup_id );
    3634    }
    37     // Bail early?
    38     if ( ! $popup_id || 'popup_disabled' == $popup_id ) { return; }
    39 
    40     // Set popup width from popup meta.
    41     $popup_width = 'max-width: ' . get_post_meta( $popup_id, 'boostbox_display_max_width', true );
    42     // Set width based on the first block, or defaults as a fallback.
    43     if ( ! get_post_meta( $popup_id, 'boostbox_display_max_width', true ) ) {
    44         $popup_width = get_cover_block_styles( $popup_id );
    45     }
    46     // Get the popup max width (if any).
    47     $max_width = 'style="' . $popup_width . '"';
    48     // Popup position.
    49     $popup_position = get_post_meta( $popup_id, 'boostbox_display_location', true );
    50     // Popup animation.
    51     $popup_animation = get_post_meta( $popup_id, 'boostbox_animation_type', true );
    52     // Close icon color.
    53     $close_color = get_post_meta( $popup_id, 'boostbox_close_icon_color', true );
    54     if ( ! $close_color ) {
    55         $close_color = '#FFFFFF';
    56     }
    57     // Close icon placement.
    58     $close_placement = get_post_meta( $popup_id, 'boostbox_close_icon_placement', true );
    59     if ( ! $close_placement ) {
    60         $close_placement = 'outside';
    61     }
    62     // @TODO set custom icon options in the settings for users to choose from.
    63     $close_icon = apply_filters( 'boostbox_popup_close_icon', '<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-x" width="32" height="32" viewBox="0 0 24 24" stroke-width="1.5" stroke="' . $close_color . '" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg>', $close_color );
    64     // Popup overlay classes.
    65     $popup_overlay_classes = apply_filters( 'boostbox_popup_overlay_classes', 'boostbox-popup-overlay' );
    66     // Popup content classes.
    67     $popup_content_classes = apply_filters( 'boostbox_popup_content_classes', 'boostbox-popup-content' );
    68     ?>
    69     <!--Creates the popup body-->
    70     <div class="<?php esc_attr_e( $popup_overlay_classes ); ?>" role="dialog">
    71     <!--Creates the popup content-->
    72     <div class="<?php esc_attr_e( $popup_content_classes ); ?> <?php esc_html_e( $popup_position . ' ' . $popup_animation ); ?>"<?php echo wp_kses_post( $max_width ); ?>>
    73         <?php
    74         // Query args.
    75         $args = [
    76             'post_type'           => 'boostbox_popups',
    77             'post_status'         => 'publish',
    78             'p'                   => $popup_id,
    79             'no_found_rows'       => true,
    80             'ignore_sticky_posts' => true
    81         ];
    82 
    83         // Filter the args.
    84         $args = apply_filters( 'boostbox_popup_html_wp_query_args', $args );
    85 
    86         // Build the query.
    87         $query = new WP_Query( $args );
    88 
    89         // Check if there are any posts.
    90         if ( $query->have_posts() ) {
    91             // Loop through the posts.
    92             while ( $query->have_posts() ) {
    93                 $query->the_post();
    94                     the_content();
    95             }
    96             // Restore original post data.
    97             wp_reset_postdata();
    98         }
    99         ?>
    100         <!--popup's close button-->
    101         <?php if ( $close_placement !== 'hidden' ) { ?>
    102         <button class="boostbox-close <?php if ( $close_placement == 'inside' ) { esc_html_e( 'inside' ); } ?>"><?php print_r( $close_icon ); ?></button>
    103         <?php } ?>
    104     </div>
    105     </div>
    106     <?php
    10735}
    108 add_action( 'template_redirect', 'boostbox_popup_html' );
     36add_action( 'wp_footer', 'boostbox_popup_html' );
  • boostbox/trunk/public/class-boostbox-public.php

    r3148388 r3156386  
    7373     */
    7474    public function enqueue_styles() {
    75         // Publc CSS.
    76         wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/boostbox-public.min.css', [], $this->version, 'all' );
     75        // Check popups for post ID's.
     76        $popup_check = boostbox_popup_post_check( get_the_ID() );   
     77        // Ensure that there are popups to process.
     78        if ( $popup_check && is_array( $popup_check ) ) {
     79            // Publc CSS.
     80            wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/boostbox-public.min.css', [], $this->version, 'all' );
     81        }
    7782    }
    7883
     
    8489     */
    8590    public function enqueue_scripts() {
    86         // Popup ID.
    87         $popup_id = get_post_meta( get_the_ID(), 'boostbox_popup_selected', true );
    88         // Milliseconds.
    89         $milliseconds = get_post_meta( $popup_id, 'boostbox_display_speed', true );
    90         if ( ! $milliseconds ) {
    91             $milliseconds = 0;
     91        // Check popups for post ID's.
     92        $popup_check = boostbox_popup_post_check( get_the_ID() );
     93   
     94        // Initialize an array to hold all popup data.
     95        $all_popups_data = [];
     96   
     97        // Ensure that there are popups to process.
     98        if ( $popup_check && is_array( $popup_check ) ) {
     99            // Loop through popup checks.
     100            foreach ( $popup_check as $popup_id ) {
     101                // Milliseconds.
     102                $milliseconds = get_post_meta( $popup_id, 'boostbox_display_speed', true );
     103                if ( ! $milliseconds ) {
     104                    $milliseconds = 0;
     105                }
     106   
     107                // Scroll distance.
     108                $scroll_distance = '32px';
     109                if ( get_post_meta( $popup_id, 'boostbox_scroll_distance', true ) ) {
     110                    $scroll_distance = get_post_meta( $popup_id, 'boostbox_scroll_distance', true );
     111                }
     112   
     113                // Create localized script args for this popup.
     114                $popup_data = [
     115                    'popup_id'             => $popup_id,
     116                    'milliseconds'         => $milliseconds,
     117                    'cookie_days'          => boostbox_settings_cookie_days( $popup_id ),
     118                    'scroll_distance'      => $scroll_distance,
     119                    'trigger'              => get_post_meta( $popup_id, 'boostbox_trigger_type', true ),
     120                    'close_icon_placement' => get_post_meta( $popup_id, 'boostbox_close_icon_placement', true ),
     121                    'disable_analytics'    => boostbox_settings_disable_analytics()
     122                ];
     123   
     124                // Add the popup data to the array.
     125                $all_popups_data[] = $popup_data;
     126            }
     127   
     128            // Enqueue the script files.
     129            wp_enqueue_script( $this->plugin_name . '-js-cookie', plugin_dir_url( __FILE__ ) . 'js/js.cookie.min.js', [ 'jquery' ], $this->version, false );
     130            wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/boostbox-public.js', [ 'jquery' ], $this->version, false );
     131   
     132            // Localize the script with all popups' data, AJAX URL, and nonce.
     133            wp_localize_script( $this->plugin_name, 'boostbox_settings', [
     134                'ajax_url' => admin_url( 'admin-ajax.php' ), // Localize ajax_url only once
     135                'nonce'    => wp_create_nonce( 'boostbox_nonce' ), // Localize nonce only once
     136                'popups'   => $all_popups_data // Add all popups to the popups array
     137            ]);
    92138        }
    93         // Scroll distance.
    94         $scroll_distance = '32px';
    95         if ( get_post_meta( $popup_id, 'boostbox_scroll_distance', true ) ) {
    96             $scroll_distance = get_post_meta( $popup_id, 'boostbox_scroll_distance', true );
    97         }
    98         // Create localize script args.
    99         $localize_args = [
    100             'popup_id'             => $popup_id,
    101             'milliseconds'         => $milliseconds,
    102             'cookie_days'          => boostbox_settings_cookie_days( $popup_id ),
    103             'scroll_distance'      => $scroll_distance,
    104             'trigger'              => get_post_meta( $popup_id, 'boostbox_trigger_type', true ),
    105             'close_icon_placement' => get_post_meta( $popup_id, 'boostbox_close_icon_placement', true ),
    106             'ajax_url'             => admin_url( 'admin-ajax.php' ),
    107             'nonce'                => wp_create_nonce( 'boostbox_nonce' ), 
    108             'disable_analytics'    => boostbox_settings_disable_analytics()   
    109         ];
    110         // Filter the args.
    111         $localize_args = apply_filters( 'boostbox_localize_scripts_args', $localize_args );
    112         // Public JS.
    113         wp_enqueue_script( $this->plugin_name . '-js-cookie', plugin_dir_url( __FILE__ ) . 'js/js.cookie.min.js', [ 'jquery' ], $this->version, false );
    114         wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/boostbox-public.js', [ 'jquery' ], $this->version, false );
    115         wp_localize_script( $this->plugin_name, 'boostbox_settings', $localize_args );
    116     }
    117 
     139    }   
     140   
    118141}
    119142
     
    127150    $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( $_POST['nonce'] ) : '';
    128151    if ( ! wp_verify_nonce( $nonce, 'boostbox_nonce' ) ) {
    129         wp_die( 'Invalid nonce' );
     152        wp_send_json_error('Invalid nonce');
     153        wp_die();
    130154    }
    131155
    132156    // Get the popup ID from the AJAX request.
    133157    $popup_id = isset( $_POST['popup_id'] ) ? intval( $_POST['popup_id'] ) : 0;
     158    if ( $popup_id === 0 ) {
     159        wp_send_json_error('Invalid popup ID');
     160        wp_die();
     161    }
    134162
    135163    // Increment view count meta field.
    136164    $current_count = get_post_meta( $popup_id, 'boostbox_popup_impressions', true );
    137     $new_count     = (int)$current_count + 1;
    138     update_post_meta( $popup_id, 'boostbox_popup_impressions', $new_count );
    139 
     165    if ( $current_count === '' ) {
     166        $current_count = 0;
     167    }
     168
     169    $new_count = (int) $current_count + 1;
     170    $updated = update_post_meta( $popup_id, 'boostbox_popup_impressions', $new_count );
     171
     172    if ( $updated === false ) {
     173        wp_send_json_error('Failed to update post meta');
     174        wp_die();
     175    }
     176
     177    wp_send_json_success(array('popup_id' => $popup_id, 'new_count' => $new_count));
    140178    wp_die();
    141179}
  • boostbox/trunk/public/js/boostbox-public.js

    r3035929 r3156386  
    11jQuery(document).ready(function ($) {
    2     // Popup ID.
    3     var popupID = boostbox_settings.popup_id;
    4     // Trigger type.
    5     var triggerType = boostbox_settings.trigger;
    6     // Milliseconds.
    7     var milliseconds = boostbox_settings.milliseconds;
    8     // Disable analytics?
    9     var disableAnalytics = boostbox_settings.disable_analytics;
    10     // Window inner height.
    11     var innerHeight = window.innerHeight;
    12     // Check to see if the cookie exists already.
    13     var cookieCheck = Cookies.get( 'boostbox_popup_' + popupID + '' );
     2    if (typeof boostbox_settings !== 'undefined' && Array.isArray(boostbox_settings.popups)) {
     3        // Loop through each popup's settings.
     4        boostbox_settings.popups.forEach(function (popup) {
     5            var popupID = popup.popup_id;
    146
    15     // If there's a cookie saved, bail early.
    16     if ( cookieCheck != null ) { return; }
     7            // Check for existing cookies to see if popup should be skipped.
     8            var cookieCheck = Cookies.get('boostbox_popup_' + popupID);
     9            if (cookieCheck != null) {
     10                console.log('Popup', popupID, 'skipped due to cookie');
     11                return;
     12            }
    1713
    18     // Trigger - auto open.
    19     if ( triggerType === "auto-open" ) {
    20         if (!Cookies.get( 'boostbox_popup_' + popupID + '' )) {
     14            // Trigger handling (auto-open, on-scroll, time).
     15            handlePopupTrigger(popup);
     16
     17            // Close behavior handling (inside/outside/hide).
     18            setupCloseBehavior(popupID, popup.close_icon_placement, popup.cookie_days);
     19
     20            // Track conversion when any button/link within the popup is clicked.
     21            $(".boostbox-popup-overlay[data-popup-id='" + popupID + "']").on("click", ":button:not('.boostbox-close'), button:not('.boostbox-close'), a, input[type='submit'], [role='button']", function () {
     22                trackConversion(popupID);
     23            });
     24        });
     25    }
     26
     27    // Function to handle popup triggers.
     28    function handlePopupTrigger(popup) {
     29        var popupID = popup.popup_id;
     30        var triggerType = popup.trigger;
     31        var popupClosed = false;
     32
     33        let popupViewCountIncremented = false;
     34
     35        if (triggerType === "auto-open" && !Cookies.get('boostbox_popup_' + popupID)) {
     36            window.setTimeout(function () {
     37                $(".boostbox-popup-overlay[data-popup-id='" + popupID + "']").addClass('active');
     38                incrementPopupViewCount(popupID);
     39            }, 0);
     40        }
     41
     42        // Trigger - time.
     43        if ( triggerType === "time" && !Cookies.get( 'boostbox_popup_' + popupID + '' )) {
    2144            // Add class after X seconds.
    2245            window.setTimeout(function(){
    2346                $(".boostbox-popup-overlay").addClass('active');
    2447                incrementPopupViewCount();
    25             }, 0);
     48            }, popup.milliseconds);
    2649        }
    27     }
    2850
    29     // Trigger - time.
    30     if ( triggerType === "time" ) {
    31         if (!Cookies.get( 'boostbox_popup_' + popupID + '' )) {
    32             // Add class after X seconds.
    33             window.setTimeout(function(){
    34                 $(".boostbox-popup-overlay").addClass('active');
    35                 incrementPopupViewCount();
    36             }, milliseconds);
    37         }
    38     }
     51        // Trigger - on scroll.
     52        if (triggerType === "on-scroll" && !Cookies.get('boostbox_popup_' + popupID)) {
     53            // Add class after scrolling X pixels.
     54            $(window).scroll(function () {
     55                if (!popupClosed && !popupViewCountIncremented) {
     56                    if (!popupClosed) {
     57                        var triggerValue = popup.scroll_distance; // Use the popup's specific scroll distance
     58                        var scrolledY = $(window).scrollTop();
    3959
    40     // Variable to track if the popup is closed
    41     var popupClosed = false;
    42     // Set a flag to track whether incrementPopupViewCount has been executed
    43     let popupViewCountIncremented = false;
     60                        if (!triggerValue) {
     61                            console.error('Popup scroll_distance is not defined for popup ' + popupID);
     62                            return;
     63                        }
    4464
    45     // Trigger - on scroll.
    46     if (triggerType === "on-scroll" && !Cookies.get('boostbox_popup_' + popupID)) {
    47         // Add class after scrolling X pixels.
    48         $(window).scroll(function () {
    49             if (!popupClosed && !popupViewCountIncremented) {
    50                 if (!popupClosed) {
    51                     var triggerValue = boostbox_settings.scroll_distance;
    52                     var scrolledY = $(window).scrollTop();
     65                        var isPercentage = triggerValue.includes('%');
     66                        var windowY = isPercentage ? percentageToPixels(triggerValue, innerHeight) : parseInt(triggerValue, 10);
    5367
    54                     var isPercentage = triggerValue.includes('%');
    55                     var windowY = isPercentage ? percentageToPixels(triggerValue, innerHeight) : parseInt(triggerValue, 10);
     68                        if (scrolledY > windowY) {
     69                            $(".boostbox-popup-overlay").addClass('active');
     70                            incrementPopupViewCount(popupID);
     71                            popupViewCountIncremented = true; // Set the flag to true.
     72                        }
     73                    }
    5674
    57                     if (scrolledY > windowY) {
    58                         $(".boostbox-popup-overlay").addClass('active');
    59                         incrementPopupViewCount();
    60                         popupViewCountIncremented = true; // Set the flag to true.
     75                    // Function to convert percentage to pixels
     76                    function percentageToPixels(percentage, containerHeight) {
     77                        var percent = parseInt(percentage, 10);
     78                        return (percent / 100) * containerHeight;
    6179                    }
    62                 }
    63 
    64                 // Function to convert percentage to pixels
    65                 function percentageToPixels(percentage, containerHeight) {
    66                     var percent = parseInt(percentage, 10);
    67                     return (percent / 100) * containerHeight;
    68                 }
    69             }
    70         });
    71     }
    72 
    73     // Set the click class used to close the popup.
    74     if (boostbox_settings.close_icon_placement == 'hidden') {
    75         var closeClickClass = '.boostbox-popup-overlay';
    76     } else {
    77         var closeClickClass = '.boostbox-close';
    78     }
    79 
    80     // Close popup when 'close' button is clicked.
    81     $(closeClickClass).on("click", function (event) {
    82         if (boostbox_settings.close_icon_placement == 'hidden') {
    83             // Check if the clicked element is not within the .boostbox-popup-content class
    84             if (!$(event.target).closest('.boostbox-popup-content').length) {
    85                 $(".boostbox-popup-overlay").removeClass("active");
    86                 popupClosed = true; // Set the variable to true when the popup is closed.
    87                 var expirationDate = new Date();
    88                 var expirationMilliseconds = expirationDate.getTime() + (boostbox_settings.cookie_days * 24 * 60 * 60 * 1000);
    89                 expirationDate.setTime(expirationMilliseconds);
    90                 Cookies.set('boostbox_popup_' + popupID, 'hidden', { expires: expirationDate });
    91             }
    92         } else {
    93             // Check if the clicked element is .boostbox-close
    94             $(".boostbox-popup-overlay").removeClass("active");
    95             popupClosed = true; // Set the variable to true when the popup is closed
    96             var expirationDate = new Date();
    97             var expirationMilliseconds = expirationDate.getTime() + (boostbox_settings.cookie_days * 24 * 60 * 60 * 1000);
    98             expirationDate.setTime(expirationMilliseconds);
    99             Cookies.set('boostbox_popup_' + popupID, 'hidden', { expires: expirationDate });
    100         }
    101     });
    102 
    103     // Track conversion when any button/link within the popup is clicked.
    104     // @TODO figure ways to make the tracking dynamic between buttons, links, form submissions, etc.
    105     $(".boostbox-popup-overlay").on("click", ":button:not('.boostbox-close'), button:not('.boostbox-close'), a, input[type='submit'], [role='button']", function () {
    106         trackConversion();
    107     });
    108 
    109     // Get percentage of number.
    110     function percentage(percent, total) {
    111         return ((percent/ 100) * total)
    112     }
    113 
    114     // Increment popup view count.
    115     function incrementPopupViewCount() {
    116         // Only do this if analytics is not disabled.
    117         if (!disableAnalytics) {
    118             // AJAX request to increment view count.
    119             $.ajax({
    120                 url: boostbox_settings.ajax_url,
    121                 type: 'POST',
    122                 data: {
    123                     action: 'increment_popup_view_count',
    124                     popup_id: popupID,
    125                     nonce: boostbox_settings.nonce,
    126                 },
    127                 success: function (response) {
    128                     // Turned off console log message (for now) @TODO - set up a "debug" option that turns this back on.
    129                     //console.log('[SUCCESS] Impression tracking complete!');
    130                     //console.log(response);
    131                 },
    132                 error: function (error) {
    133                     // Turned off console log message (for now) @TODO - set up a "debug" option that turns this back on.
    134                     //console.log('[ERROR] Impression tracking failed!');
    135                     //console.log(error);
    13680                }
    13781            });
     
    13983    }
    14084
    141     // Increment popup conversion count.
    142     function trackConversion() {
    143         // Only do this if analytics is not disabled.
    144         if (!disableAnalytics) {
    145             // AJAX request to track conversion.
    146             $.ajax({
    147                 url: boostbox_settings.ajax_url,
    148                 type: 'POST',
    149                 data: {
    150                     action: 'track_popup_conversion',
    151                     popup_id: popupID,
    152                     nonce: boostbox_settings.nonce,
    153                 },
    154                 success: function (response) {
    155                     // Turned off console log message (for now) @TODO - set up a "debug" option that turns this back on.
    156                     //console.log('[SUCCESS] Conversion tracking complete!');
    157                     //console.log(response);
    158                 },
    159                 error: function (error) {
    160                     // Turned off console log message (for now) @TODO - set up a "debug" option that turns this back on.
    161                     //console.log('[ERROR] Conversion tracking failed!');
    162                     //console.log(error);
     85    // Function to set up the close behavior.
     86    function setupCloseBehavior(popupID, closeIconPlacement, cookieDays) {
     87        if (closeIconPlacement === 'hidden') {
     88            $(document).on("click", ".boostbox-popup-overlay[data-popup-id='" + popupID + "']", function (event) {
     89                if ($(event.target).hasClass('boostbox-popup-overlay')) {
     90                    closePopup(popupID, cookieDays);
    16391                }
     92            });
     93        } else {
     94            // Close button click handling - specifically target the correct close button.
     95            $(".boostbox-popup-overlay[data-popup-id='" + popupID + "'] .boostbox-close").on("click", function () {
     96                closePopup(popupID, cookieDays);
    16497            });
    16598        }
    16699    }
     100
     101    // Function to close the popup and set a cookie.
     102    function closePopup(popupID, cookieDays) {
     103        $(".boostbox-popup-overlay[data-popup-id='" + popupID + "']").removeClass("active");
     104        var expirationDate = new Date();
     105        expirationDate.setTime(expirationDate.getTime() + (cookieDays * 24 * 60 * 60 * 1000));
     106        Cookies.set('boostbox_popup_' + popupID, 'hidden', { expires: expirationDate });
     107    }
     108
     109    // Function to convert percentage to pixels.
     110    function percentageToPixels(percentage, containerHeight) {
     111        var percent = parseInt(percentage, 10);
     112        return (percent / 100) * containerHeight;
     113    }
     114
     115    // Function to increment popup view count.
     116    function incrementPopupViewCount(popupID) {
     117        $.ajax({
     118            url: boostbox_settings.ajax_url,
     119            type: 'POST',
     120            data: {
     121                action: 'increment_popup_view_count',
     122                popup_id: popupID,
     123                nonce: boostbox_settings.nonce,
     124            },
     125            success: function (response) {
     126                //console.log('[SUCCESS] Impression tracking complete for popup ' + popupID + ' ' + response);
     127            },
     128            error: function (xhr, status, error) {
     129                //console.log('[ERROR] Impression tracking failed for popup ' + popupID);
     130                //console.log('XHR:', xhr);
     131                //console.log('Status:', status);
     132                //console.log('Error:', error);
     133            }
     134        });
     135    }
     136
     137    // Function to track conversions.
     138    function trackConversion(popupID) {
     139        $.ajax({
     140            url: boostbox_settings.ajax_url,
     141            type: 'POST',
     142            data: {
     143                action: 'track_popup_conversion',
     144                popup_id: popupID,
     145                nonce: boostbox_settings.nonce,
     146            },
     147            success: function (response) {
     148                //console.log('[SUCCESS] Conversion tracking complete for popup ' + popupID);
     149            },
     150            error: function (error) {
     151                //console.log('[ERROR] Conversion tracking failed for popup ' + popupID);
     152            }
     153        });
     154    }
    167155});
Note: See TracChangeset for help on using the changeset viewer.