Plugin Directory

Changeset 3366689


Ignore:
Timestamp:
09/23/2025 04:51:48 PM (5 months ago)
Author:
bobteng
Message:

Update to version v1.2.15 from GitHub

Location:
plugnmeet
Files:
8 edited
1 copied

Legend:

Unmodified
Added
Removed
  • plugnmeet/tags/v1.2.15/README.txt

    r3363798 r3366689  
    55Requires at least: 5.9
    66Tested up to: 6.8.2
    7 Stable tag: 1.2.14
     7Stable tag: 1.2.15
    88Requires PHP: 7.4
    99License: GPLv2 or later
     
    24241.  **Self-Host:** Create your own server for maximum control and privacy by following the [official installation instructions](https://www.plugnmeet.org/docs/installation).
    25252.  **Use the Cloud:** Get started in minutes with a ready-to-use [plugNmeet cloud subscription](https://www.plugnmeet.cloud).
     26
     27**Note:** The plugin includes pre-configured demo credentials to help you test its features immediately. This demo server is a shared resource and is **not intended for production use** as it can be unreliable. For any important meetings, we strongly recommend using one of the options above to ensure a stable and professional experience for you and your users.
    2628
    2729---
  • plugnmeet/tags/v1.2.15/plugnmeet.php

    r3363798 r3366689  
    1717 * Description:       Plug-N-Meet web conference integration with WordPress
    1818 * x-release-please-start-version
    19  * Version:           1.2.14
     19 * Version:           1.2.15
    2020 * x-release-please-end
    2121 * Author:            Jibon L. Costa <[email protected]>
     
    4141 */
    4242// x-release-please-start-version
    43 define( 'PLUGNMEET_VERSION', '1.2.14' );
     43define( 'PLUGNMEET_VERSION', '1.2.15' );
    4444// x-release-please-end
    4545/**
  • plugnmeet/tags/v1.2.15/public/class-plugnmeet-public.php

    r3080964 r3366689  
    9292
    9393    public function start_session() {
     94        // Don't start a session for WP-CLI or REST API requests.
     95        if ( ( defined( 'WP_CLI' ) && WP_CLI ) || defined( 'REST_REQUEST' ) ) {
     96            return;
     97        }
     98
     99        // Don't start a session on "true" admin pages, but DO allow it for AJAX requests.
     100        if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) {
     101            return;
     102        }
     103
    94104        if ( ! session_id() ) {
    95105            session_start();
     
    232242    public function plugnmeet_shortcode_room_view( $atts, $content = null, $tag = "" ) {
    233243
    234         /**
    235          * Combine user attributes with known attributes.
    236          *
    237          * @see https://developer.wordpress.org/reference/functions/shortcode_atts/
    238          *
    239          * Pass third paramter $shortcode to enable ShortCode Attribute Filtering.
    240          * @see https://developer.wordpress.org/reference/hooks/shortcode_atts_shortcode/
    241          */
    242 
    243244        $atts = shortcode_atts(
    244245            array(
    245                 'id' => 1,
     246                'id' => 0, // Default to 0 to better handle cases where no ID is provided.
    246247            ),
    247248            $atts,
     
    249250        );
    250251
     252        $id = intval( $atts['id'] );
     253
    251254        /**
    252          * Build our ShortCode output.
    253          * Remember to sanitize all user input.
    254          * In this case, we expect a integer value to be passed to the ShortCode attribute.
    255          *
    256          * @see https://developer.wordpress.org/themes/theme-security/data-sanitization-escaping/
    257          */
    258         $id = intval( $atts['id'] );
    259 
    260         /**
    261          * If the shortcode is enclosing, we may want to do something with $content
     255         * If the shortcode is enclosing, the content will be used as the ID,
     256         * overriding the 'id' attribute.
     257         * e.g. [plugnmeet_room_view]123[/plugnmeet_room_view]
    262258         */
    263259        if ( ! empty( $content ) ) {
    264             $id = do_shortcode( $content );// We can parse shortcodes inside $content.
    265             $id = intval( $atts['id'] ) . ' ' . sanitize_text_field( $id );// Remember to sanitize your user input.
     260            $id = intval( do_shortcode( $content ) );
    266261        }
    267262
    268263        if ( ! $id ) {
    269             return null;
     264            // If no ID is provided via attribute or content, there's nothing to show.
     265            return __( 'Room ID not provided for the shortcode.', 'plugnmeet' );
    270266        }
    271267
  • plugnmeet/tags/v1.2.15/public/partials/plugnmeet-public-display-client.php

    r3363798 r3366689  
    11<?php
    22/**
     3 * This template provides a "blank slate" environment for the external React application.
     4 * It aggressively removes all WordPress theme/plugin assets to prevent conflicts.
    35 *
    46 * @link       https://www.mynaparrot.com
     
    1012
    1113if ( ! defined( 'PLUGNMEET_BASE_NAME' ) ) {
    12     die;
     14    die;
    1315}
    1416
     17// Remove unnecessary meta tags and actions from wp_head for a cleaner output.
    1518remove_action( 'wp_head', 'rsd_link' );
    1619remove_action( 'wp_head', 'wp_generator' );
     
    2831remove_action( 'wp_head', 'index_rel_link' );
    2932
    30 function insert_plnm_js_css() {
    31     global $wp_styles, $wp_scripts;
     33// This global will hold the handle of the main JS module to identify it in the filter.
     34$pnm_main_module_handle = '';
    3235
    33     $setting_params = (object) get_option( "plugnmeet_settings" );
    34     if ( ! isset( $setting_params->client_load ) || $setting_params->client_load === "remote" ) {
    35         if ( ! class_exists( "plugNmeetConnect" ) ) {
    36             require PLUGNMEET_ROOT_PATH . '/helpers/plugNmeetConnect.php';
    37         }
    38         $connect  = new plugNmeetConnect( $setting_params );
    39         $files    = $connect->getClientFiles();
    40         $jsFiles  = $files->getJSFiles() ?? [];
    41         $cssFiles = $files->getCSSFiles() ?? [];
    42         $path     = $setting_params->plugnmeet_server_url . "/assets";
    43     } else {
    44         $clientPath = PLUGNMEET_ROOT_PATH . "/public/client/dist/assets";
    45         $jsFiles    = preg_grep( '~\.(js)$~', scandir( $clientPath . "/js", SCANDIR_SORT_DESCENDING ) );
    46         $cssFiles   = preg_grep( '~\.(css)$~', scandir( $clientPath . "/css", SCANDIR_SORT_DESCENDING ) );
    47         $path       = plugins_url( 'public/client/dist/assets', PLUGNMEET_BASE_NAME );
    48     }
     36/**
     37 * This is the core function for creating the "blank slate".
     38 * It runs late, enqueues the app's assets, and then overwrites the global script/style queues
     39 * to ensure nothing else is loaded.
     40 */
     41function pnm_final_asset_loader() {
     42    global $wp_styles, $wp_scripts, $pnm_main_module_handle;
    4943
    50     foreach ( $jsFiles as $file ) {
    51         wp_enqueue_script( $file, $path . '/js/' . $file, array(), null );
    52     }
    53     foreach ( $cssFiles as $file ) {
    54         wp_enqueue_style( $file, $path . '/css/' . $file, array(), null );
    55     }
     44    // 1. Define the assets your app needs.
     45    $setting_params = (object) get_option( "plugnmeet_settings" );
     46    if ( ! isset( $setting_params->client_load ) || $setting_params->client_load === "remote" ) {
     47        if ( ! class_exists( "plugNmeetConnect" ) ) {
     48            require PLUGNMEET_ROOT_PATH . '/helpers/plugNmeetConnect.php';
     49        }
     50        $connect  = new plugNmeetConnect( $setting_params );
     51        $files    = $connect->getClientFiles();
     52        $jsFiles  = $files->getJSFiles() ?? [];
     53        $cssFiles = $files->getCSSFiles() ?? [];
     54        $path     = $setting_params->plugnmeet_server_url . "/assets";
     55    } else {
     56        $clientPath = PLUGNMEET_ROOT_PATH . "/public/client/dist/assets";
     57        $jsFiles    = array_values( preg_grep( '~\.(js)$~', scandir( $clientPath . "/js", SCANDIR_SORT_DESCENDING ) ) );
     58        $cssFiles   = array_values( preg_grep( '~\.(css)$~', scandir( $clientPath . "/css", SCANDIR_SORT_DESCENDING ) ) );
     59        $path       = plugins_url( 'public/client/dist/assets', PLUGNMEET_BASE_NAME );
     60    }
    5661
    57     foreach ( $wp_styles->queue as $style ) {
    58         if ( in_array( $style, $cssFiles ) ) {
    59             continue;
    60         }
    61         $wp_styles->remove( $style );
    62     }
    63     foreach ( $wp_scripts->queue as $script ) {
    64         if ( in_array( $script, $jsFiles ) ) {
    65             continue;
    66         }
    67         $wp_scripts->remove( $script );
    68     }
     62    $allowed_js_handles = [];
     63    foreach ( $jsFiles as $file ) {
     64        $handle = $file; // Use the filename as the handle, matching original logic.
     65        if ( str_starts_with( $handle, 'main-module.' ) ) {
     66            $pnm_main_module_handle = $handle;
     67        }
     68        $allowed_js_handles[] = $handle;
     69        // Enqueue the script to register it. We'll force it into the queue later.
     70        wp_enqueue_script( $handle, $path . '/js/' . $file, [], null, false );
     71    }
     72
     73    $allowed_css_handles = [];
     74    foreach ( $cssFiles as $file ) {
     75        $handle                = $file;
     76        $allowed_css_handles[] = $handle;
     77        wp_enqueue_style( $handle, $path . '/css/' . $file, [], null );
     78    }
     79
     80    // 2. Aggressively overwrite the queues.
     81    // This ensures ONLY your assets are listed for printing.
     82    $wp_scripts->queue = $allowed_js_handles;
     83    $wp_styles->queue  = $allowed_css_handles;
    6984}
    7085
    71 add_action( 'wp_print_styles', 'insert_plnm_js_css', 100 );
     86add_action( 'wp_print_styles', 'pnm_final_asset_loader', 9999 );
    7287
    73 function add_custom_attr( $tag ) {
    74     if ( str_contains( $tag, "main-module." ) ) {
    75         return str_replace( 'src=', 'type="module" src=', $tag );
    76     }
    7788
    78     return str_replace( 'src=', 'defer="defer" src=', $tag );
     89/**
     90 * Filters the script tag to add `type="module"` or `defer` attributes.
     91 *
     92 * @param string $tag The <script> tag for the enqueued script.
     93 * @param string $handle The script's handle.
     94 *
     95 * @return string The modified <script> tag.
     96 */
     97function pnm_script_loader_tag_filter( $tag, $handle ) {
     98    global $pnm_main_module_handle;
     99
     100    // Check if the current script is our main module and add type="module".
     101    if ( $pnm_main_module_handle === $handle ) {
     102        return str_replace( ' src=', ' type="module" src=', $tag );
     103    }
     104
     105    // For all other scripts on this page, add 'defer'.
     106    return str_replace( ' src=', ' defer="defer" src=', $tag );
    79107}
    80108
    81 add_filter( 'script_loader_tag', 'add_custom_attr', 10, 3 );
     109add_filter( 'script_loader_tag', 'pnm_script_loader_tag_filter', 10, 2 );
     110
    82111?>
    83112<!DOCTYPE html>
    84 <html lang="en">
     113<html <?php language_attributes(); ?>>
    85114<head>
    86     <meta charset="UTF-8"/>
     115    <meta charset="<?php bloginfo( 'charset' ); ?>"/>
    87116    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    88117    <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    89     <?php wp_head(); ?>
    90     <?php wp_print_inline_script_tag( $jsOptions ); ?>
     118    <title>plugNmeet</title>
     119    <?php wp_head(); ?>
     120    <?php wp_print_inline_script_tag( $jsOptions ); ?>
    91121</head>
    92122<body>
    93123<div id="plugNmeet-app"></div>
     124<?php // Do not include wp_footer() to ensure a clean environment. ?>
    94125</body>
    95126</html>
  • plugnmeet/trunk/README.txt

    r3363798 r3366689  
    55Requires at least: 5.9
    66Tested up to: 6.8.2
    7 Stable tag: 1.2.14
     7Stable tag: 1.2.15
    88Requires PHP: 7.4
    99License: GPLv2 or later
     
    24241.  **Self-Host:** Create your own server for maximum control and privacy by following the [official installation instructions](https://www.plugnmeet.org/docs/installation).
    25252.  **Use the Cloud:** Get started in minutes with a ready-to-use [plugNmeet cloud subscription](https://www.plugnmeet.cloud).
     26
     27**Note:** The plugin includes pre-configured demo credentials to help you test its features immediately. This demo server is a shared resource and is **not intended for production use** as it can be unreliable. For any important meetings, we strongly recommend using one of the options above to ensure a stable and professional experience for you and your users.
    2628
    2729---
  • plugnmeet/trunk/plugnmeet.php

    r3363798 r3366689  
    1717 * Description:       Plug-N-Meet web conference integration with WordPress
    1818 * x-release-please-start-version
    19  * Version:           1.2.14
     19 * Version:           1.2.15
    2020 * x-release-please-end
    2121 * Author:            Jibon L. Costa <[email protected]>
     
    4141 */
    4242// x-release-please-start-version
    43 define( 'PLUGNMEET_VERSION', '1.2.14' );
     43define( 'PLUGNMEET_VERSION', '1.2.15' );
    4444// x-release-please-end
    4545/**
  • plugnmeet/trunk/public/class-plugnmeet-public.php

    r3080964 r3366689  
    9292
    9393    public function start_session() {
     94        // Don't start a session for WP-CLI or REST API requests.
     95        if ( ( defined( 'WP_CLI' ) && WP_CLI ) || defined( 'REST_REQUEST' ) ) {
     96            return;
     97        }
     98
     99        // Don't start a session on "true" admin pages, but DO allow it for AJAX requests.
     100        if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) {
     101            return;
     102        }
     103
    94104        if ( ! session_id() ) {
    95105            session_start();
     
    232242    public function plugnmeet_shortcode_room_view( $atts, $content = null, $tag = "" ) {
    233243
    234         /**
    235          * Combine user attributes with known attributes.
    236          *
    237          * @see https://developer.wordpress.org/reference/functions/shortcode_atts/
    238          *
    239          * Pass third paramter $shortcode to enable ShortCode Attribute Filtering.
    240          * @see https://developer.wordpress.org/reference/hooks/shortcode_atts_shortcode/
    241          */
    242 
    243244        $atts = shortcode_atts(
    244245            array(
    245                 'id' => 1,
     246                'id' => 0, // Default to 0 to better handle cases where no ID is provided.
    246247            ),
    247248            $atts,
     
    249250        );
    250251
     252        $id = intval( $atts['id'] );
     253
    251254        /**
    252          * Build our ShortCode output.
    253          * Remember to sanitize all user input.
    254          * In this case, we expect a integer value to be passed to the ShortCode attribute.
    255          *
    256          * @see https://developer.wordpress.org/themes/theme-security/data-sanitization-escaping/
    257          */
    258         $id = intval( $atts['id'] );
    259 
    260         /**
    261          * If the shortcode is enclosing, we may want to do something with $content
     255         * If the shortcode is enclosing, the content will be used as the ID,
     256         * overriding the 'id' attribute.
     257         * e.g. [plugnmeet_room_view]123[/plugnmeet_room_view]
    262258         */
    263259        if ( ! empty( $content ) ) {
    264             $id = do_shortcode( $content );// We can parse shortcodes inside $content.
    265             $id = intval( $atts['id'] ) . ' ' . sanitize_text_field( $id );// Remember to sanitize your user input.
     260            $id = intval( do_shortcode( $content ) );
    266261        }
    267262
    268263        if ( ! $id ) {
    269             return null;
     264            // If no ID is provided via attribute or content, there's nothing to show.
     265            return __( 'Room ID not provided for the shortcode.', 'plugnmeet' );
    270266        }
    271267
  • plugnmeet/trunk/public/partials/plugnmeet-public-display-client.php

    r3363798 r3366689  
    11<?php
    22/**
     3 * This template provides a "blank slate" environment for the external React application.
     4 * It aggressively removes all WordPress theme/plugin assets to prevent conflicts.
    35 *
    46 * @link       https://www.mynaparrot.com
     
    1012
    1113if ( ! defined( 'PLUGNMEET_BASE_NAME' ) ) {
    12     die;
     14    die;
    1315}
    1416
     17// Remove unnecessary meta tags and actions from wp_head for a cleaner output.
    1518remove_action( 'wp_head', 'rsd_link' );
    1619remove_action( 'wp_head', 'wp_generator' );
     
    2831remove_action( 'wp_head', 'index_rel_link' );
    2932
    30 function insert_plnm_js_css() {
    31     global $wp_styles, $wp_scripts;
     33// This global will hold the handle of the main JS module to identify it in the filter.
     34$pnm_main_module_handle = '';
    3235
    33     $setting_params = (object) get_option( "plugnmeet_settings" );
    34     if ( ! isset( $setting_params->client_load ) || $setting_params->client_load === "remote" ) {
    35         if ( ! class_exists( "plugNmeetConnect" ) ) {
    36             require PLUGNMEET_ROOT_PATH . '/helpers/plugNmeetConnect.php';
    37         }
    38         $connect  = new plugNmeetConnect( $setting_params );
    39         $files    = $connect->getClientFiles();
    40         $jsFiles  = $files->getJSFiles() ?? [];
    41         $cssFiles = $files->getCSSFiles() ?? [];
    42         $path     = $setting_params->plugnmeet_server_url . "/assets";
    43     } else {
    44         $clientPath = PLUGNMEET_ROOT_PATH . "/public/client/dist/assets";
    45         $jsFiles    = preg_grep( '~\.(js)$~', scandir( $clientPath . "/js", SCANDIR_SORT_DESCENDING ) );
    46         $cssFiles   = preg_grep( '~\.(css)$~', scandir( $clientPath . "/css", SCANDIR_SORT_DESCENDING ) );
    47         $path       = plugins_url( 'public/client/dist/assets', PLUGNMEET_BASE_NAME );
    48     }
     36/**
     37 * This is the core function for creating the "blank slate".
     38 * It runs late, enqueues the app's assets, and then overwrites the global script/style queues
     39 * to ensure nothing else is loaded.
     40 */
     41function pnm_final_asset_loader() {
     42    global $wp_styles, $wp_scripts, $pnm_main_module_handle;
    4943
    50     foreach ( $jsFiles as $file ) {
    51         wp_enqueue_script( $file, $path . '/js/' . $file, array(), null );
    52     }
    53     foreach ( $cssFiles as $file ) {
    54         wp_enqueue_style( $file, $path . '/css/' . $file, array(), null );
    55     }
     44    // 1. Define the assets your app needs.
     45    $setting_params = (object) get_option( "plugnmeet_settings" );
     46    if ( ! isset( $setting_params->client_load ) || $setting_params->client_load === "remote" ) {
     47        if ( ! class_exists( "plugNmeetConnect" ) ) {
     48            require PLUGNMEET_ROOT_PATH . '/helpers/plugNmeetConnect.php';
     49        }
     50        $connect  = new plugNmeetConnect( $setting_params );
     51        $files    = $connect->getClientFiles();
     52        $jsFiles  = $files->getJSFiles() ?? [];
     53        $cssFiles = $files->getCSSFiles() ?? [];
     54        $path     = $setting_params->plugnmeet_server_url . "/assets";
     55    } else {
     56        $clientPath = PLUGNMEET_ROOT_PATH . "/public/client/dist/assets";
     57        $jsFiles    = array_values( preg_grep( '~\.(js)$~', scandir( $clientPath . "/js", SCANDIR_SORT_DESCENDING ) ) );
     58        $cssFiles   = array_values( preg_grep( '~\.(css)$~', scandir( $clientPath . "/css", SCANDIR_SORT_DESCENDING ) ) );
     59        $path       = plugins_url( 'public/client/dist/assets', PLUGNMEET_BASE_NAME );
     60    }
    5661
    57     foreach ( $wp_styles->queue as $style ) {
    58         if ( in_array( $style, $cssFiles ) ) {
    59             continue;
    60         }
    61         $wp_styles->remove( $style );
    62     }
    63     foreach ( $wp_scripts->queue as $script ) {
    64         if ( in_array( $script, $jsFiles ) ) {
    65             continue;
    66         }
    67         $wp_scripts->remove( $script );
    68     }
     62    $allowed_js_handles = [];
     63    foreach ( $jsFiles as $file ) {
     64        $handle = $file; // Use the filename as the handle, matching original logic.
     65        if ( str_starts_with( $handle, 'main-module.' ) ) {
     66            $pnm_main_module_handle = $handle;
     67        }
     68        $allowed_js_handles[] = $handle;
     69        // Enqueue the script to register it. We'll force it into the queue later.
     70        wp_enqueue_script( $handle, $path . '/js/' . $file, [], null, false );
     71    }
     72
     73    $allowed_css_handles = [];
     74    foreach ( $cssFiles as $file ) {
     75        $handle                = $file;
     76        $allowed_css_handles[] = $handle;
     77        wp_enqueue_style( $handle, $path . '/css/' . $file, [], null );
     78    }
     79
     80    // 2. Aggressively overwrite the queues.
     81    // This ensures ONLY your assets are listed for printing.
     82    $wp_scripts->queue = $allowed_js_handles;
     83    $wp_styles->queue  = $allowed_css_handles;
    6984}
    7085
    71 add_action( 'wp_print_styles', 'insert_plnm_js_css', 100 );
     86add_action( 'wp_print_styles', 'pnm_final_asset_loader', 9999 );
    7287
    73 function add_custom_attr( $tag ) {
    74     if ( str_contains( $tag, "main-module." ) ) {
    75         return str_replace( 'src=', 'type="module" src=', $tag );
    76     }
    7788
    78     return str_replace( 'src=', 'defer="defer" src=', $tag );
     89/**
     90 * Filters the script tag to add `type="module"` or `defer` attributes.
     91 *
     92 * @param string $tag The <script> tag for the enqueued script.
     93 * @param string $handle The script's handle.
     94 *
     95 * @return string The modified <script> tag.
     96 */
     97function pnm_script_loader_tag_filter( $tag, $handle ) {
     98    global $pnm_main_module_handle;
     99
     100    // Check if the current script is our main module and add type="module".
     101    if ( $pnm_main_module_handle === $handle ) {
     102        return str_replace( ' src=', ' type="module" src=', $tag );
     103    }
     104
     105    // For all other scripts on this page, add 'defer'.
     106    return str_replace( ' src=', ' defer="defer" src=', $tag );
    79107}
    80108
    81 add_filter( 'script_loader_tag', 'add_custom_attr', 10, 3 );
     109add_filter( 'script_loader_tag', 'pnm_script_loader_tag_filter', 10, 2 );
     110
    82111?>
    83112<!DOCTYPE html>
    84 <html lang="en">
     113<html <?php language_attributes(); ?>>
    85114<head>
    86     <meta charset="UTF-8"/>
     115    <meta charset="<?php bloginfo( 'charset' ); ?>"/>
    87116    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    88117    <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    89     <?php wp_head(); ?>
    90     <?php wp_print_inline_script_tag( $jsOptions ); ?>
     118    <title>plugNmeet</title>
     119    <?php wp_head(); ?>
     120    <?php wp_print_inline_script_tag( $jsOptions ); ?>
    91121</head>
    92122<body>
    93123<div id="plugNmeet-app"></div>
     124<?php // Do not include wp_footer() to ensure a clean environment. ?>
    94125</body>
    95126</html>
Note: See TracChangeset for help on using the changeset viewer.