*/
class Wpstream_Admin {
/**
* Most parts a multipart VOD upload may be split into.
*
* The storage backend assembles at most this many parts, so a larger
* split uploads every byte and still can never complete.
*
* @var int
*/
const MULTIPART_MAX_PARTS = 1000;
/**
* Store plugin main class to allow public access.
*
* @since 20180622
* @var object The main class.
*/
public $main;
/**
* The ID of this plugin.
*
* @since 3.0.1
* @access private
* @var string $plugin_name The ID of this plugin.
*/
private $plugin_name;
/**
* The version of this plugin.
*
* @since 3.0.1
* @access private
* @var string $version The current version of this plugin.
*/
private $version;
/**
* @var array Definitions of the per-event "global" streaming options (record,
* view count, autoplay, encryption, etc.). Each entry holds a
* translated label, a help "details" string, and a default value.
* Populated lazily on `init` by load_global_event_options().
*/
public $global_event_options ;
/**
* Initialize the class and set its properties.
*
* @since 3.0.1
* @param string $plugin_name The name (ID) of this plugin.
* @param string $version The version of this plugin.
* @param object $plugin_main The main plugin class instance, kept for public access.
* @return void
*/
public function __construct( $plugin_name, $version,$plugin_main ) {
// Remember the plugin identity and the main class for later use.
$this->plugin_name = $plugin_name;
$this->version = $version;
$this->main = $plugin_main;
// Build the translatable global event options once WP (and its i18n) is ready.
add_action('init', array($this, 'load_global_event_options'));
}
/**
* Populate $global_event_options with the translatable per-event settings.
*
* Deferred to the `init` hook so the esc_html__() translations resolve
* against a loaded text domain.
*
* @return void
*/
public function load_global_event_options() {
// Each key maps to a label/details/default triple used when rendering the event option toggles.
$this->global_event_options = array(
'record' => array(
'name' => esc_html__('Record Live Stream','wpstream'),
'details' => esc_html__('If enabled, live streams will be recorded and saved to your library.','wpstream'),
'defaults' => 'no',
),
'view_count' => array(
'name' => esc_html__('Display Viewer Count','wpstream'),
'details' => esc_html__('If enabled, the live viewer count will show up in the player.','wpstream'),
'defaults' => 'yes',
),
'domain_lock' => array(
'name' => esc_html__('Lock To Website','wpstream'),
'details' => sprintf ( esc_html__('If enabled, live video will only display on %1$s, otherwise it can show up on any website.','wpstream'),get_bloginfo('wpurl') ),
'defaults' => 'no',
),
'autoplay' => array(
'name' => esc_html__('Autoplay','wpstream'),
'details' => esc_html__('If enabled, live video will attempt to start playing automatically. This is only achievable in some browsers.','wpstream'),
'defaults' => 'yes',
),
'mute' => array(
'name' => esc_html__('Start Muted','wpstream'),
'details' => esc_html__('If enabled, live video will start muted. This may increase the rate of autoplay in some browsers. ','wpstream'),
'defaults' => 'no',
),
'low_latency' => array(
'name' => esc_html__('Low Latency (beta)','wpstream'),
'details' => esc_html__('Shortens the live delay between streamer and viewers. Useful for interactive applications like gaming, auctions, trading etc. Low latency may worsen the viewer experience on some devices.','wpstream'),
'defaults' => 'no',
),
'adaptive_bitrate' => array(
'name' => esc_html__('Adaptive Bitrate (beta)','wpstream'),
'details' => esc_html__('Ensures a smooth and uninterrupted viewing experience by adjusting video quality for viewers with reduced network speed or device capabilities.','wpstream'),
'defaults' => 'no',
),
'encrypt' =>array(
'name' => esc_html__('Encrypt Live Stream','wpstream'),
'details' => esc_html__('If enabled, video data will be encrypted. Enabling encryption may lead to reduced website performance under certain configurations. Encrypted video may not display in all browsers.','wpstream'),
'defaults' => 'no',
),
'ses_encrypt'=>array(
'name' => esc_html__('Use Sessions with Encryption','wpstream'),
'details' => esc_html__('If enabled, encryption key distribution will be checked against valid user sessions. Setting may malfunction or lead to reduced website performance under certain configurations. ','wpstream'),
'defaults' => 'no',
),
);
/**
* Filter the catalog of per-channel options shown as toggles.
*
* Each entry is `key => array( name, details, defaults )`. An added key
* renders a toggle on the settings and channel screens; it is stored
* only if the Channel Settings Module knows the key.
*
* @since 4.14.0
*
* @param array $options Option catalog keyed by setting.
*/
$this->global_event_options = (array) apply_filters( 'wpstream_default_event_options', $this->global_event_options );
}
/**
* Register and enqueue the plugin's admin-area stylesheets.
*
* Hooked (via Wpstream_Loader) to admin_enqueue_scripts.
*
* @since 3.0.1
* @return void
*/
public function enqueue_styles() {
/**
* This function is provided for demonstration purposes only.
*
* An instance of this class should be passed to the run() function
* defined in Wpstream_Loader as all of the hooks are defined
* in that particular class.
*
* The Wpstream_Loader will then create the relationship
* between the defined hooks and the functions defined in this
* class.
*/
// No external webfont: the admin UI falls back to the system font stack
// declared in the stylesheets, so no request leaves the site for fonts.
// Main admin stylesheet; filemtime() is used as the cache-busting version.
wp_enqueue_style(
$this->plugin_name,
plugin_dir_url( __FILE__ ) . 'css/wpstream-admin.css',
array(),
filemtime(plugin_dir_path(__FILE__) . 'css/wpstream-admin.css' ),
'all'
);
// Ensure the WP media library scripts/styles are available (for logo/image pickers) if not already loaded.
if (!did_action('wp_enqueue_media')) {
wp_enqueue_media();
}
}
/**
* Register, enqueue and localize the plugin's admin-area JavaScript.
*
* Enqueues jQuery UI helpers, the fileupload lib, the admin control/upload
* scripts, the start-streaming and settings scripts, and the onboarding
* bundles. Each script is fed a localized vars array (translated strings,
* URLs, nonces, feature flags). Some bundles are only loaded on specific
* admin screens. Hooked (via Wpstream_Loader) to admin_enqueue_scripts.
*
* @since 3.0.1
* @return void
*/
public function enqueue_scripts() {
// jQuery UI widgets used by the settings/date pickers and sliders.
wp_enqueue_script("jquery-ui-slider");
wp_enqueue_script("jquery-ui-datepicker");
// Blueimp-style file upload library backing the recordings/VOD uploader.
wp_enqueue_script('jquery.fileupload', plugin_dir_url( __FILE__ ) .'js/jquery.fileupload.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true);
// Shared admin utility helpers, then the main admin control script that drives uploads and channel actions.
wp_enqueue_script( 'wpstream-admin-utils', plugin_dir_url( __FILE__ ) .'js/utils/admin_utils.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true);
wp_enqueue_script('wpstream-admin-control', plugin_dir_url( __FILE__ ) .'js/admin_control.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true);
// Hand the control script all of its translated status strings, the admin URL, and the multipart upload nonce.
wp_localize_script('wpstream-admin-control', 'wpstream_admin_control_vars',
array(
'admin_url' => get_admin_url(),
'multipart_upload_nonce' => wp_create_nonce( 'wpstream_multipart_upload_nonce' ),
'recordings_nonce' => wp_create_nonce( 'wpstream_recordings_nonce' ),
'loading_url' => WPSTREAM_PLUGIN_DIR_URL.'/img/loading.gif',
'download_mess' => esc_html__('Click to download!','wpstream'),
'uploading' => esc_html__('We are uploading your file. Do not close this window!','wpstream'),
'upload_complete2' => esc_html__('Upload Complete! You can upload another file!','wpstream'),
'not_accepted' => esc_html__('The file is not an accepted video format','wpstream'),
'upload_complete' => esc_html__('Upload Complete!','wpstream'),
'no_band' => esc_html__('Not enough streaming data.','wpstream'),
'no_band_no_store' => esc_html__('Not enough streaming data or storage.','wpstream'),
'no_streaming_hours' => esc_html__('Not enough streaming hours.','wpstream'),
'exceeding_limit' => esc_html__('File size exceeds 5GB. Initiating multipart upload...','wpstream'),
'upload_failed' => esc_html__('Upload Failed!','wpstream'),
'upload_failed2' => esc_html__('Upload Failed! Please Try again!','wpstream'),
'choose_a_file' => esc_html__('Choose a file…','wpstream'),
'preparing_multipart' => esc_html__('Preparing multipart upload...','wpstream'),
'uploading_part' => esc_html__('Uploading part {part} of {total}...','wpstream'),
'upload_failed_part' => esc_html__('Failed to upload part {part}. Please try again.','wpstream'),
'completing_upload' => esc_html__('Completing upload. Please wait...','wpstream'),
'upload_failed_part_retry' => esc_html__('Failed to upload part {part}. Retrying...','wpstream'),
'choose_recording' => esc_html__( 'Choose Recording', 'wpstream' ),
'select_recording' => esc_html__( 'Please select a recording from the list', 'wpstream' ),
'invalid_response' => esc_html__('Invalid response from server. Missing required upload data.', 'wpstream'),
'video_processing' => esc_html__( 'The video is still processing', 'wpstream' ),
'file_name_text' => esc_html__('File Name:','wpstream'),
'channel_create_error' => esc_html__('Something did not work. Please try again.', 'wpstream'),
'select_caption_file' => esc_html__('Select .vtt Captions File', 'wpstream'),
'select_button' => esc_html__('Select', 'wpstream'),
'remove_button' => esc_html__('Remove', 'wpstream'),
'use_streaming_hours' => $this->main->quota_manager->get_streaming_ui_flags_from_cache()['use_streaming_hours'],
));
// Recordings list script + its localized labels and the "create VOD from recording" links (free vs paid depending on WooCommerce).
wp_enqueue_script('wpstream-recordings-videos-list', plugin_dir_url( __FILE__ ) .'js/recordings_videos_list.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true);
wp_localize_script( 'wpstream-recordings-videos-list', 'wpstream_recordings_videos_list_vars',
array(
'delete_file' => esc_html__('Delete file', 'wpstream'),
'download' => esc_html__( 'Download', 'wpstream'),
'download_available' => esc_html__( 'Click to download! The url will work for the next 20 minutes!', 'wpstream'),
'add_free_video_url' => esc_url( admin_url( 'post-new.php?post_type=wpstream_product_vod') . '&new_video_name=' ),
'create_ftv_vod' => esc_html__( 'Create new Free-To-View VOD from this recording' , 'wpstream' ),
'woocommerce_exists' => class_exists( 'WooCommerce' ),
'add_paid_video_url' => esc_url( admin_url( 'post-new.php?post_type=product').'&new_video_name=' ),
'create_ptv_vod' => esc_html__( 'Create new Pay-Per-View VOD from this recording' , 'wpstream' ),
)
);
// Settings-page script with its save/logo-picker strings and the broadcaster page URL.
wp_enqueue_script('wpstream-settings', plugin_dir_url( __DIR__ ) .'/admin/js/wpstream_settings.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true);
wp_localize_script('wpstream-settings', 'wpstream_settings_vars', array(
'error_message' => esc_html__( 'Failed to save settings. Please try again.', 'wpstream'),
'choose_image_text' => esc_html__( 'Choose Logo Image', 'wpstream'),
'select_image_text' => esc_html__( 'Select Image', 'wpstream'),
'update_successful' => esc_html__( 'Update Successful.', 'wpstream'),
'update_failed' => esc_html__( 'Something went wrong. Try again.', 'wpstream'),
'broadcaster_url' => esc_url( esc_url(home_url('/broadcaster-page/') ) ),
));
// Identify the current admin screen so screen-specific bundles load only where needed.
$current_screen=get_current_screen();
// On the credentials/channels/recordings/onboard screens, load the quota widget updater.
if ( in_array( $current_screen->base, ['toplevel_page_wpstream_credentials', 'wpstream_page_wpstream_live_channels', 'wpstream_page_wpstream_recordings', 'wpstream_page_wpstream_onboard'] ) ) {
wp_enqueue_script( 'wpstream-user-quota-update', plugin_dir_url( __DIR__ ) . 'admin/js/wpstream-user-quota.js', array(), WPSTREAM_PLUGIN_VERSION, true );
wp_localize_script( 'wpstream-user-quota-update', 'wpstream_user_quota_vars', array(
'admin_url' => get_admin_url()
));
}
// Add localized variables for broadcaster
// Provide the broadcaster script (registered elsewhere) with its AJAX URL and nonce.
wp_localize_script('wpstream-broadcaster', 'wpstream_broadcaster_vars', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('wpstream_broadcaster_nonce'),
'plugin_url' => plugin_dir_url(__FILE__),
// Gates the broadcaster's diagnostic console logging (see broadcaster.js).
'debug' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG,
));
}
/**
* Add Plugin Administation menu
*
* Registers the top-level "WpStream" menu and its sub-pages (Credentials,
* All Channels, Recordings, Settings, Quick Start), each mapped to a render
* callback on this class and gated to the `administrator` capability.
*
* @since 3.0.1
* @return void
*/
public function wpstream_manage_admin_menu() {
// Top-level menu; its page defaults to the Credentials screen and uses the WpStream icon at position 20.
add_menu_page( __('WpStream','wpstream'), __('WpStream ','wpstream'), 'administrator', 'wpstream_credentials', array($this,'wpstream_set_wpstream_credentials'), WPSTREAM_PLUGIN_DIR_URL.'img/wpstream-icon-menu_2.png',20 );
// Credentials sub-page (same slug as the parent so it is the default view).
add_submenu_page( 'wpstream_credentials', __('WpStream Credentials','wpstream'), __('Credentials','wpstream'), 'administrator', 'wpstream_credentials', array($this,'wpstream_set_wpstream_credentials') );
// All Channels listing.
add_submenu_page( 'wpstream_credentials', __('WpStream Live Channels','wpstream'), __('All Channels','wpstream'), 'administrator', 'wpstream_live_channels', array( $this,'wpstream_new_general_set'));
// Recordings / media management.
add_submenu_page( 'wpstream_credentials', __('WpStream Recordings','wpstream'), __('Recordings','wpstream'), 'administrator', 'wpstream_recordings', array($this,'wpstream_media_management'));
// Global plugin settings.
add_submenu_page( 'wpstream_credentials', __('WpStream Settings','wpstream'), __('Settings','wpstream'), 'administrator', 'wpstream_settings', array($this,'wpstream_settings'));
// Quick Start / onboarding entry point.
add_submenu_page( 'wpstream_credentials', __('WpStream Quick Start','wpstream'), __('WpStream Quick Start','wpstream'), 'administrator', 'wpstream_onboard', array($this,'wpstream_pre_onboard_display'));
}
/**
* Shows events wpstream
*
* Renders the "All Channels" admin screen: the Pay-Per-View (WooCommerce
* `product`) channel list, the Free-To-View (`wpstream_product`) list, the
* quota/pack summary, and the "no channels" call-to-action. Each channel is
* drawn via wpstream_live_stream_unit(). Echoes HTML directly.
*
* @since 3.0.1
* @return void
*/
public function wpstream_new_general_set() {
return $this->main->get_live_channel_presentation()->render_channels_page();
}
public function wpstream_social_share($the_id){
return $this->main->get_live_channel_presentation()->render_part( 'social_share', array( $the_id ) );
}
public function wpstream_live_stream_unit($the_id,$is_front=''){
$context = '' === $is_front ? 'admin' : $is_front;
return $this->main->get_live_channel_presentation()->render_channel( $the_id, $context );
}
public function wpstream_live_stream_unit_for_theme($the_id,$is_front=''){
return $this->main->get_live_channel_presentation()->render_part( 'theme_channel', array( $the_id, $is_front ) );
}
public function wpstream_close_modal_button(){
return $this->main->get_live_channel_presentation()->render_part( 'close_modal_button' );
}
public function wpstream_local_event_options_toggle( $is_basic_stream_mode = false ) {
return $this->main->get_live_channel_presentation()->render_part( 'local_event_options_toggle', array( $is_basic_stream_mode ) );
}
public function wpstream_basic_stream_mode_message() {
return $this->main->get_live_channel_presentation()->render_part( 'basic_stream_mode_message' );
}
public function wpstream_display_modal_seetings($the_id){
return $this->main->get_live_channel_presentation()->render_part( 'settings_modal', array( $the_id ) );
}
public function wpstream_display_modal_share($the_id){
return $this->main->get_live_channel_presentation()->render_part( 'share_modal', array( $the_id ) );
}
public function wpstream_display_modal_broadcast($the_id,$external_software_streaming_class,$obs_uri,$obs_stream){
return $this->main->get_live_channel_presentation()->render_part(
'broadcast_modal',
array( $the_id, $external_software_streaming_class, $obs_uri, $obs_stream )
);
}
public function wpstream_encoder_table(){
return $this->main->get_live_channel_presentation()->render_part( 'encoder_table' );
}
public function wpstream_encoder_panel( $encoder, $obs_uri, $obs_stream, $visible = false ){
return $this->main->get_live_channel_presentation()->render_part(
'encoder_panel',
array( $encoder, $obs_uri, $obs_stream, $visible )
);
}
private function wpstream_settings_fields(){
// Definition of every settings field, keyed and grouped by 'tab' (general, subscription, messages, default options, VOD defaults, support).
return array(
1 => array(
'tab' => 'general_options',
'label' => esc_html__('Slug for free video/channel pages ','wpstream'),
'name' => 'free_media_slug',
'type' => 'text',
'details' => esc_html__('This will replace the default "wpstream" of all your free video/channel urls. Special characters like "&" are not permitted. To have your new slug show up you need to re-save the "Permalinks Settings" under Settings -> Permalinks, even if not making any changes.','wpstream'),
),
'free_vod_slug' => array(
'tab' => 'general_options',
'label' => esc_html__('Slug for free VOD pages ','wpstream'),
'name' => 'free_media_slug_vod',
'type' => 'text',
'details' => esc_html__('This will replace the default "wpstream_vod" of all your free VOD urls. Special characters like "&" are not permitted. To have your new slug show up you need to re-save the "Permalinks Settings" under Settings -> Permalinks, even if not making any changes.','wpstream'),
),
2 => array(
'tab' => 'general_options',
'label' => esc_html__('Non-Admin User Roles Allowed to Broadcast','wpstream'),
'name' => 'stream_role',
'type' => 'user_roles',
'details' => esc_html__('These types of users can stream via frontend shortcodes / blocks. Single individual channels are automaticlally created for streaming by non-admins.','wpstream'),
),
3 => array(
'tab' => 'general_options',
'label' => esc_html__('Non Admin Streamers Channel Type.','wpstream'),
'name' => 'user_streaming_channel_type',
'type' => 'select',
'select_values'=>array(
'free' => esc_html__('Free Live Channel','wpstream'),
'paid' => esc_html__('Pay-Per-View','wpstream')
),
'details' => esc_html__('Choose whether the channels assigned to non-admins are free-for-all or pay-per-view (WooCommerce product).','wpstream'),
),
4 => array(
'tab' => 'general_options',
'label' => esc_html__('Default Pay-Per-View Price','wpstream'),
'name' => 'user_streaming_default_price',
'type' => 'text',
'details' => esc_html__('Default price of pay-per-view channels assigned to non-admins.','wpstream'),
),
6 => array(
'tab' => 'subscription_options',
'label' => esc_html__('Use Global Subscription Mode','wpstream'),
'name' => 'global_sub',
'type' => 'slidertoogle',
'details' => esc_html__('If enabled, a client can access all the media products (live and VOD) by purchasing a single subscription. The "WooCommerce Subscriptions" plugin is required.','wpstream'),
),
7 => array(
'tab' => 'subscription_options',
'label' => esc_html__('Subscription ID for Global Subscription Mode','wpstream'),
'name' => 'global_sub_id',
'type' => 'text',
'details' => esc_html__('ID of the subscription product to be purchased for global access to media. All non-subscription video products that are not already attached to a subscription will be accessible to users that have purchased it.','wpstream'),
),
8 => array(
'tab' => 'messages_options',
'label' => esc_html__('PPV not logged in message','wpstream'),
'name' => 'product_not_login',
'type' => 'text',
'details' => esc_html__('This message will be displayed on top of the media player for pay-per-view items when user is not logged in.','wpstream'),
'default' => esc_html__('You must be logged in to watch this video.','wpstream'),
),
9 => array(
'tab' => 'messages_options',
'label' => esc_html__('PPV not purchased message','wpstream'),
'name' => 'product_not_bought',
'type' => 'text',
'details' => esc_html__('This message will be displayed on top of the media player for common pay-per-view items that have not been purchased.','wpstream'),
'default' => esc_html__('You did not yet purchase this item.','wpstream'),
),
10 => array(
'tab' => 'messages_options',
'label' => esc_html__('Subscription not purchased message','wpstream'),
'name' => 'product_not_subscribe',
'type' => 'text',
'details' => esc_html__('This message will be displayed on top of the media player for subscription-type pay-per-view items that have not been purchased.','wpstream'),
'default' => esc_html__(' You did not yet subscribe to this item.','wpstream'),
),
11 => array(
'tab' => 'messages_options',
'label' => esc_html__('Thank you message','wpstream'),
'name' => 'product_thankyou',
'type' => 'text',
'details' => esc_html__('This message will be displayed on the thank you page (after purchase) and the confirmation email.','wpstream'),
'default' => esc_html__('Thanks for your purchase. You can access your item at any time by visiting the following page: {item_link}','wpstream'),
),
12 => array(
'tab' => 'messages_options',
'label' => esc_html__('Subscription Active message','wpstream'),
'name' => 'subscription_active',
'type' => 'text',
'details' => esc_html__('This message will be displayed on subscription product page.','wpstream'),
'default' => esc_html__('Your Subscription is Active','wpstream'),
),
13 => array(
'tab' => 'messages_options',
'label' => esc_html__('You are not live message','wpstream'),
'name' => 'you_are_not_live',
'type' => 'text',
'details' => esc_html__('This message will be displayed in player.','wpstream'),
'default' => esc_html__('We are not live at this moment','wpstream'),
),
14 => array(
'tab' => 'general_options',
'label' => esc_html__('Video player theme','wpstream'),
'name' => 'video_player_theme',
'type' => 'select',
'select_values'=>array(
'default' => esc_html__('Default','wpstream'),
'city' => esc_html__('City','wpstream'),
'forest' => esc_html__('Forest','wpstream'),
'fantasy' => esc_html__('Fantasy','wpstream'),
'sea' => esc_html__('Sea','wpstream'),
),
'details' => esc_html__('Choose the video player theme to have a different look for the player.','wpstream'),
),
'wpstream_player_logo' => array(
'tab' => 'general_options',
'name' => 'player_logo',
'label' => esc_html__('Logo for the video player','wpstream'),
'type' => 'image',
'details' => esc_html__('This logo will be displayed on the the video player.','wpstream'),
'default' => '',
'image_size' => 'thumbnail',
),
// hide the video player logo opacity for now
// 'wpstream_player_logo_opacity' => array(
// 'tab' => 'general_options',
// 'name' => 'player_logo_opacity',
// 'label' => esc_html__('Opacity of the video player logo','wpstream'),
// 'type' => 'range',
// 'details' => esc_html__('Set the opacity of the logo','wpstream'),
// 'default' => '',
// 'image_size' => 'thumbnail',
// ),
'wpsteram_player_logo_position' => array(
'tab' => 'general_options',
'name' => 'player_logo_position',
'label' => esc_html__('Position of the video player logo','wpstream'),
'type' => 'select',
'select_values'=>array(
'top-left' => esc_html__('Top Left','wpstream'),
'top-right' => esc_html__('Top Right','wpstream'),
'bottom-left' => esc_html__('Bottom Left','wpstream'),
'bottom-right' => esc_html__('Bottom Right','wpstream'),
),
'details' => esc_html__('Choose the position of the logo on the video player.','wpstream'),
'default' => '',
),
99 => array(
'tab' => 'default_options',
'label' => esc_html__('Events Options ','wpstream'),
'name' => 'user_streaming_global_channel_options',
'type' => 'user_streaming_global_channel_options',
'details' => esc_html__('Global Options for live events.','wpstream'),
),
100 => array(
'tab' => 'default_options_vod',
'label' => esc_html__('Autoplay','wpstream'),
'name' => 'vod_autoplay',
'type' => 'slidertoogle',
'details' => esc_html__('If enabled, video will attempt to start playing automatically. This is only achievable in some browsers.','wpstream'),
),
101 => array(
'tab' => 'default_options_vod',
'label' => esc_html__('Start Muted','wpstream'),
'name' => 'vod_start_muted',
'type' => 'slidertoogle',
'details' => esc_html__('If enabled, video will start muted. This may increase the rate of autoplay in some browsers.','wpstream'),
),
102 => array(
'tab' => 'default_options_vod',
'label' => esc_html__('Lock To Website','wpstream'),
'name' => 'vod_domain_lock',
'type' => 'slidertoogle',
'details' =>sprintf ( esc_html__('If enabled, video will only display on %1$s, otherwise it can show up on any website.','wpstream'),get_bloginfo('wpurl') ),
),
103 => array(
'tab' => 'default_options_vod',
'label' => esc_html__('Encrypt Video','wpstream'),
'name' => 'vod_encrypt',
'type' => 'slidertoogle',
'details' => esc_html__('If enabled, video data will be encrypted. Enabling encryption may lead to reduced website performance under certain configurations. Encrypted video may not display in all browsers.','wpstream'),
),
/* 'vod_domain_lock' =>array(
'name' => esc_html__('Video On Demand - Lock To Website','wpstream'),
'details' => sprintf ( esc_html__('If enabled, VODS will only display on %1$s, otherwise they can show up on any website.','wpstream'),get_bloginfo('wpurl') ),
'defaults' => 'no',
),
'vod_encrypt' =>array(
'name' => esc_html__('Encrypt Video on Demand','wpstream'),
'details' => esc_html__('If enabled, video data will be encrypted. Enabling encryption may lead to reduced website performance under certain configurations. Encrypted video may not display in all browsers.','wpstream'),
'defaults' => 'no',
),*/
104 => array(
'tab' => 'support_tab',
'label' => esc_html__('Logs','wpstream'),
'name' => 'logs',
'type' => 'logs_table',
'details' => esc_html__('This is the error log of the plugin.','wpstream'),
)
);
}
/**
* Stop the request unless the current user may manage plugin settings.
*
* Shared by the settings and credentials screens so both enforce the
* same capability (`manage_options`).
*
* @return void
*/
private function wpstream_require_settings_capability(){
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Not enough permissions to make this change', 'wpstream' ) );
}
}
/**
* Render the WpStream Settings admin screen and persist submitted values.
*
* On POST it verifies the nonce, then stores only the fields declared for
* the active tab as `wpstream_*` options (the streamer role is checked
* against the site's roles, the "default options" tab folds its checkboxes
* into one map), and flushes rewrite rules. It then renders the tabbed
* form from the same field definitions. Echoes HTML.
*
* @return void
*/
public function wpstream_settings(){
// Admin-only screen.
$this->wpstream_require_settings_capability();
$active_tab = isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'general_options';
/**
* Filter the settings field definitions.
*
* Field definitions drive both the save allow-list and the rendered
* form, so an added field (`tab`, `label`, `name`, `type`, `details`)
* is rendered on its tab and saved as the `wpstream_{name}` option
* through sanitize_text_field().
*
* @since 4.14.0
*
* @param array $fields Field definitions.
* @param string $active_tab Tab being rendered or saved.
*/
$wpstream_settings_array = (array) apply_filters( 'wpstream_settings_fields', $this->wpstream_settings_fields(), $active_tab );
/**
* Filter the settings tabs.
*
* Added tabs render their fields from `wpstream_settings_fields`
* and fire `wpstream_settings_tab_{slug}` for a custom body.
*
* @since 4.14.0
*
* @param array $tabs Tab slug => label.
* @param string $active_tab Tab being rendered or saved.
*/
$tabs = (array) apply_filters(
'wpstream_settings_tabs',
array(
'general_options' => esc_html__( 'General Options', 'wpstream' ),
'default_options' => esc_html__( 'Default Channel Settings', 'wpstream' ),
'default_options_vod' => esc_html__( 'VOD Settings', 'wpstream' ),
'subscription_options' => esc_html__( 'Subscription Options', 'wpstream' ),
'messages_options' => esc_html__( 'Customize Messages', 'wpstream' ),
'support_tab' => esc_html__( 'Support', 'wpstream' ),
),
$active_tab
);
// Handle a settings submission.
if($_SERVER['REQUEST_METHOD'] === 'POST'){
// CSRF protection.
check_admin_referer( 'wpstream-settings-nonce', 'wpstream-settings-nonce' );
// Only the fields declared for the active tab may be persisted.
$allowed = array();
foreach ( $wpstream_settings_array as $field ) {
if ( isset( $field['tab'], $field['name'] ) && $field['tab'] === $active_tab ) {
$allowed[] = $field['name'];
}
}
// Option values as they were before this submission, and as written by it
// (both keyed by option name) — announced together once the save is complete.
$previous_options = array();
$saved_options = array();
// If the "channel type" section was shown but no streamer role was chosen, clear the stored role.
if( isset($_POST['user_streaming_channel_type_hidden']) && intval($_POST['user_streaming_channel_type_hidden'])==1 && !isset($_POST['stream_role']) ){
$previous_options['wpstream_stream_role'] = get_option( 'wpstream_stream_role' );
$saved_options['wpstream_stream_role'] = '';
update_option( sanitize_key('wpstream_stream_role'), '' );
}
// Persist each declared field as a sanitized wpstream_* option; anything else is ignored.
foreach ( $allowed as $variable ) {
if ( ! isset( $_POST[ $variable ] ) ) {
continue;
}
$value = $_POST[ $variable ];
// Streamer role is a list of role slugs; keep only the roles the form offers
// (editable roles minus administrator, who can always broadcast).
if ( 'stream_role' === $variable ) {
$known = array_diff( array_keys( get_editable_roles() ), array( 'administrator' ) );
$roles = array_values( array_intersect( (array) $value, $known ) );
$previous_options['wpstream_stream_role'] = get_option( 'wpstream_stream_role' );
$saved_options['wpstream_stream_role'] = $roles;
update_option( 'wpstream_stream_role', $roles );
continue;
}
$option_name = sanitize_key( 'wpstream_' . $variable );
$previous_options[ $option_name ] = get_option( $option_name );
$saved_options[ $option_name ] = sanitize_text_field( $value );
update_option( $option_name, $saved_options[ $option_name ] );
}
// On the "default options" tab, fold the per-option checkboxes into a single 1/0 map.
if ( 'default_options' === $active_tab ) {
$event_settings=array();
foreach($this->global_event_options as $key=>$option){
$event_settings[$key]='';
if(isset($_POST['wpstream_event_set_'.$key]) && $_POST['wpstream_event_set_'.$key]=='on'){
$event_settings[$key]=1;
}else{
$event_settings[$key]=0;
}
}
$this->main->channel_settings->apply(
array(
'type' => 'save_defaults',
'options' => $event_settings,
)
);
}
// reset permalinkgs
// Slug settings can change permalinks, so drop and rebuild the rewrite rules.
global $wp_rewrite;
update_option( "rewrite_rules", FALSE );
$wp_rewrite->flush_rules( true );
/**
* Fires after a WpStream Settings tab was saved and the rewrite
* rules rebuilt. Default Channel Settings saved from the
* "default options" tab are announced separately by
* wpstream_default_channel_settings_saved.
*
* @since 4.14.0
*
* @param string $active_tab Settings tab that was submitted.
* @param array $saved Option values written, keyed by option name.
* @param array $previous The values those options held before.
*/
do_action( 'wpstream_settings_saved', $active_tab, $saved_options, $previous_options );
}
// Open the settings panel and the form.
print '
'.__('WpStream Settings','wpstream').'
';
print '
';
}
/**
* Get system information for support tab
*
* @return array System information
*/
private function get_system_info() {
global $wp_version;
// Environment facts: PHP/WP version, debug flag, memory limit and the plugin version.
$php_version = phpversion();
$wp_version_info = $wp_version;
$site_debug_mode = (defined('WP_DEBUG') && WP_DEBUG);
$wp_memory_limit = WP_MEMORY_LIMIT;
$wpstream_version = WPSTREAM_PLUGIN_VERSION;
$wpstream_plugin_outdated = false;
// Check if plugin is outdated
// Mark the plugin outdated if WP's update transient lists an available update for it.
$update_plugins = get_site_transient('update_plugins');
if (isset($update_plugins->response['plugin/wpstream.php'])) {
$wpstream_plugin_outdated = true;
}
// Check API status
// API is "connected" when a WpStream cloud token can be obtained.
$api_status = false;
if (method_exists($this->main->wpstream_live_connection, 'is_connected')) {
$api_status = $this->main->wpstream_live_connection->is_connected();
}
// Return the collected diagnostics for the Support tab to render.
return array(
'php_version' => $php_version,
'wp_version' => $wp_version_info,
'site_debug_mode' => $site_debug_mode,
'wp_memory_limit' => $wp_memory_limit,
'wpstream_version' => $wpstream_version,
'wpstream_plugin_outdated' => $wpstream_plugin_outdated,
'api_status' => $api_status
);
}
/**
* Render system information HTML
*
* Prints the Support-tab diagnostics table (PHP/WP versions, debug mode,
* memory limit, plugin version + update button, API connection) with a
* warning/OK dashicon per row. Echoes HTML.
*
* @return void
*/
private function render_system_info() {
// Pull the current environment diagnostics.
$system_info = $this->get_system_info();
// Below: diagnostics table; each row shows a value and a warning/OK indicator based on recommended thresholds.
?>
render_system_info(); ?>
wpstream_get_plugins_data();
if (empty($plugins_data)) {
echo '
array(
'path' => 'wpstream/wpstream.php',
),
'WooCommerce' => array(
'path' => 'woocommerce/woocommerce.php',
),
'Meta Box' => array(
'path' => 'meta-box/meta-box.php',
),
'One Click Demo Import' => array(
'path' => 'one-click-demo-import/one-click-demo-import.php',
),
'Better Messages' => array(
'path' => 'bp-better-messages/bp-better-messages.php',
),
);
// Build an info array for every installed plugin (filtered down afterwards).
foreach ($all_plugins as $plugin_path => $plugin_data) {
$is_active = is_plugin_active( $plugin_path );
$has_update = isset( $update_data->response[$plugin_path] );
$plugin_info = [
'name' => $plugin_data['Name'],
'version' => $plugin_data['Version'],
'path' => $plugin_path,
'active' => $is_active ? 'Yes' : 'No',
'needs_update' => $has_update ? 'Yes' : 'No'
];
// Record the offered version when an update is pending.
if ($has_update) {
$plugin_info['new_version'] = $update_data->response[$plugin_path]->new_version;
}
$all_plugins_info[] = $plugin_info;
}
// Filter out the elements from $all_plugins_info that are not in $wpstream_plugins
foreach ( $all_plugins_info as $key => $plugin_info ) {
// compare $plugin_info['path'] against the path property on each $wpstream_plugins element item
// if the path is not in $wpstream_plugins, unset the element
if ( !in_array( $plugin_info['path'], array_column( $wpstream_plugins, 'path' ) ) ) {
unset( $all_plugins_info[$key] );
}
}
return $all_plugins_info;
}
/**
* Print a dismissible admin notice when a WpStream plugin update is pending.
*
* @return void
*/
public function wpstream_render_outdated_plugin_notice() {
// Only render when WP's update transient lists an update for the plugin.
$has_update = get_site_transient('update_plugins');
if (isset($has_update->response['plugin/wpstream.php'])) {
// Below: warning notice linking to the WP updates page.
?>
global_event_options as $key=>$option){
// Skip options listed in the exclude array (used by the per-channel modal).
if( is_array($local_array) && !in_array($key,$local_array)){
print '
';
print '';
print '
';
print '
'.$option['details'].'
';
print '
';
print '
';
print '
';
}
}
}
/*
* Set user roles
*
* @since 3.0.1
*/
/**
* Build a multi-select of editable user roles (administrator excluded)
* for the "roles allowed to broadcast" setting.
*
* @param string $name Field name (rendered as name="{name}[]").
* @param array|string $value Currently selected role keys ('' becomes an empty array).
* @return string HTML