| 1 |
<?php |
| 2 |
|
| 3 |
/** |
| 4 |
* The admin-specific functionality of the plugin. |
| 5 |
* |
| 6 |
* @link http://wpstream.net |
| 7 |
* @since 3.0.1 |
| 8 |
* |
| 9 |
* @package Wpstream |
| 10 |
* @subpackage Wpstream/admin |
| 11 |
*/ |
| 12 |
|
| 13 |
/** |
| 14 |
* The admin-specific functionality of the plugin. |
| 15 |
* |
| 16 |
* This is the admin "god class": it wires up the whole wp-admin surface of the |
| 17 |
* plugin. Responsibilities include registering admin CSS/JS, building the |
| 18 |
* top-level WpStream menu and its sub-pages (Credentials, Channels, Recordings, |
| 19 |
* Settings, Quick Start), rendering channel/settings/onboarding screens and |
| 20 |
* their modal dialogs, defining the per-event global streaming options, hooking |
| 21 |
* into WooCommerce to register the custom stream/VOD product types and their |
| 22 |
* pricing/metabox behaviour, rendering post metaboxes, driving the multipart S3 |
| 23 |
* upload AJAX endpoints, and emitting the various admin notices (plugin update, |
| 24 |
* cache flush, theme, etc.). Many methods echo HTML directly. |
| 25 |
* |
| 26 |
* @package Wpstream |
| 27 |
* @subpackage Wpstream/admin |
| 28 |
* @author wpstream <office@wpstream.net> |
| 29 |
*/ |
| 30 |
class Wpstream_Admin { |
| 31 |
|
| 32 |
|
| 33 |
/** |
| 34 |
* Store plugin main class to allow public access. |
| 35 |
* |
| 36 |
* @since 20180622 |
| 37 |
* @var object The main class. |
| 38 |
*/ |
| 39 |
public $main; |
| 40 |
|
| 41 |
|
| 42 |
/** |
| 43 |
* The ID of this plugin. |
| 44 |
* |
| 45 |
* @since 3.0.1 |
| 46 |
* @access private |
| 47 |
* @var string $plugin_name The ID of this plugin. |
| 48 |
*/ |
| 49 |
private $plugin_name; |
| 50 |
|
| 51 |
/** |
| 52 |
* The version of this plugin. |
| 53 |
* |
| 54 |
* @since 3.0.1 |
| 55 |
* @access private |
| 56 |
* @var string $version The current version of this plugin. |
| 57 |
*/ |
| 58 |
private $version; |
| 59 |
|
| 60 |
/** |
| 61 |
* @var array Definitions of the per-event "global" streaming options (record, |
| 62 |
* view count, autoplay, encryption, etc.). Each entry holds a |
| 63 |
* translated label, a help "details" string, and a default value. |
| 64 |
* Populated lazily on `init` by load_global_event_options(). |
| 65 |
*/ |
| 66 |
public $global_event_options ; |
| 67 |
|
| 68 |
|
| 69 |
|
| 70 |
|
| 71 |
|
| 72 |
/** |
| 73 |
* Initialize the class and set its properties. |
| 74 |
* |
| 75 |
* @since 3.0.1 |
| 76 |
* @param string $plugin_name The name (ID) of this plugin. |
| 77 |
* @param string $version The version of this plugin. |
| 78 |
* @param object $plugin_main The main plugin class instance, kept for public access. |
| 79 |
* @return void |
| 80 |
*/ |
| 81 |
public function __construct( $plugin_name, $version,$plugin_main ) { |
| 82 |
|
| 83 |
// Remember the plugin identity and the main class for later use. |
| 84 |
$this->plugin_name = $plugin_name; |
| 85 |
$this->version = $version; |
| 86 |
$this->main = $plugin_main; |
| 87 |
|
| 88 |
// Build the translatable global event options once WP (and its i18n) is ready. |
| 89 |
add_action('init', array($this, 'load_global_event_options')); |
| 90 |
} |
| 91 |
|
| 92 |
/** |
| 93 |
* Populate $global_event_options with the translatable per-event settings. |
| 94 |
* |
| 95 |
* Deferred to the `init` hook so the esc_html__() translations resolve |
| 96 |
* against a loaded text domain. |
| 97 |
* |
| 98 |
* @return void |
| 99 |
*/ |
| 100 |
public function load_global_event_options() { |
| 101 |
// Each key maps to a label/details/default triple used when rendering the event option toggles. |
| 102 |
$this->global_event_options = array( |
| 103 |
'record' => array( |
| 104 |
'name' => esc_html__('Record Live Stream','wpstream'), |
| 105 |
'details' => esc_html__('If enabled, live streams will be recorded and saved to your library.','wpstream'), |
| 106 |
'defaults' => 'no', |
| 107 |
), |
| 108 |
'view_count' => array( |
| 109 |
'name' => esc_html__('Display Viewer Count','wpstream'), |
| 110 |
'details' => esc_html__('If enabled, the live viewer count will show up in the player.','wpstream'), |
| 111 |
'defaults' => 'yes', |
| 112 |
), |
| 113 |
'domain_lock' => array( |
| 114 |
'name' => esc_html__('Lock To Website','wpstream'), |
| 115 |
'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') ), |
| 116 |
'defaults' => 'no', |
| 117 |
), |
| 118 |
'autoplay' => array( |
| 119 |
'name' => esc_html__('Autoplay','wpstream'), |
| 120 |
'details' => esc_html__('If enabled, live video will attempt to start playing automatically. This is only achievable in some browsers.','wpstream'), |
| 121 |
'defaults' => 'yes', |
| 122 |
), |
| 123 |
'mute' => array( |
| 124 |
'name' => esc_html__('Start Muted','wpstream'), |
| 125 |
'details' => esc_html__('If enabled, live video will start muted. This may increase the rate of autoplay in some browsers. ','wpstream'), |
| 126 |
'defaults' => 'no', |
| 127 |
), |
| 128 |
'low_latency' => array( |
| 129 |
'name' => esc_html__('Low Latency (beta)','wpstream'), |
| 130 |
'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'), |
| 131 |
'defaults' => 'no', |
| 132 |
), |
| 133 |
'adaptive_bitrate' => array( |
| 134 |
'name' => esc_html__('Adaptive Bitrate (beta)','wpstream'), |
| 135 |
'details' => esc_html__('Ensures a smooth and uninterrupted viewing experience by adjusting video quality for viewers with reduced network speed or device capabilities.','wpstream'), |
| 136 |
'defaults' => 'no', |
| 137 |
), |
| 138 |
'encrypt' =>array( |
| 139 |
'name' => esc_html__('Encrypt Live Stream','wpstream'), |
| 140 |
'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'), |
| 141 |
'defaults' => 'no', |
| 142 |
), |
| 143 |
'ses_encrypt'=>array( |
| 144 |
'name' => esc_html__('Use Sessions with Encryption','wpstream'), |
| 145 |
'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'), |
| 146 |
'defaults' => 'no', |
| 147 |
), |
| 148 |
// 'autostart' =>array( |
| 149 |
// 'name' => esc_html__('Auto TURN ON','wpstream'), |
| 150 |
// 'details' => esc_html__('If enabled, channel will TURN ON automatically when broadcasting with an External Streaming App (RTMP Encoder/Broadcaster)','wpstream'), |
| 151 |
// 'defaults' => 'no', |
| 152 |
// ), |
| 153 |
); |
| 154 |
} |
| 155 |
|
| 156 |
/** |
| 157 |
* Register and enqueue the plugin's admin-area stylesheets. |
| 158 |
* |
| 159 |
* Hooked (via Wpstream_Loader) to admin_enqueue_scripts. |
| 160 |
* |
| 161 |
* @since 3.0.1 |
| 162 |
* @return void |
| 163 |
*/ |
| 164 |
public function enqueue_styles() { |
| 165 |
|
| 166 |
/** |
| 167 |
* This function is provided for demonstration purposes only. |
| 168 |
* |
| 169 |
* An instance of this class should be passed to the run() function |
| 170 |
* defined in Wpstream_Loader as all of the hooks are defined |
| 171 |
* in that particular class. |
| 172 |
* |
| 173 |
* The Wpstream_Loader will then create the relationship |
| 174 |
* between the defined hooks and the functions defined in this |
| 175 |
* class. |
| 176 |
*/ |
| 177 |
// Google "Roboto" webfont used across the admin UI. |
| 178 |
wp_enqueue_style( 'wpstream-roboto', "https://fonts.googleapis.com/css?family=Roboto:300,400,500,600,700,900&display=swap&subset=latin-ext" ); |
| 179 |
// Main admin stylesheet; filemtime() is used as the cache-busting version. |
| 180 |
wp_enqueue_style( |
| 181 |
$this->plugin_name, |
| 182 |
plugin_dir_url( __FILE__ ) . 'css/wpstream-admin.css', |
| 183 |
array(), |
| 184 |
filemtime(plugin_dir_path(__FILE__) . 'css/wpstream-admin.css' ), |
| 185 |
'all' |
| 186 |
); |
| 187 |
|
| 188 |
// Onboarding-specific styles, again cache-busted by file modification time. |
| 189 |
wp_enqueue_style( |
| 190 |
'wpstream-on-boarding-css', |
| 191 |
plugin_dir_url( __FILE__ ) . 'css/wpstream-admin-onboarding.css', |
| 192 |
array(), |
| 193 |
filemtime(plugin_dir_path(__FILE__) . 'css/wpstream-admin-onboarding.css'), |
| 194 |
); |
| 195 |
|
| 196 |
// Ensure the WP media library scripts/styles are available (for logo/image pickers) if not already loaded. |
| 197 |
if (!did_action('wp_enqueue_media')) { |
| 198 |
wp_enqueue_media(); |
| 199 |
} |
| 200 |
} |
| 201 |
|
| 202 |
/** |
| 203 |
* Register, enqueue and localize the plugin's admin-area JavaScript. |
| 204 |
* |
| 205 |
* Enqueues jQuery UI helpers, the fileupload lib, the admin control/upload |
| 206 |
* scripts, the start-streaming and settings scripts, and the onboarding |
| 207 |
* bundles. Each script is fed a localized vars array (translated strings, |
| 208 |
* URLs, nonces, feature flags). Some bundles are only loaded on specific |
| 209 |
* admin screens. Hooked (via Wpstream_Loader) to admin_enqueue_scripts. |
| 210 |
* |
| 211 |
* @since 3.0.1 |
| 212 |
* @return void |
| 213 |
*/ |
| 214 |
public function enqueue_scripts() { |
| 215 |
|
| 216 |
// jQuery UI widgets used by the settings/date pickers and sliders. |
| 217 |
wp_enqueue_script("jquery-ui-slider"); |
| 218 |
wp_enqueue_script("jquery-ui-datepicker"); |
| 219 |
// Blueimp-style file upload library backing the recordings/VOD uploader. |
| 220 |
wp_enqueue_script('jquery.fileupload', plugin_dir_url( __FILE__ ) .'js/jquery.fileupload.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true); |
| 221 |
|
| 222 |
// Shared admin utility helpers, then the main admin control script that drives uploads and channel actions. |
| 223 |
wp_enqueue_script( 'wpstream-admin-utils', plugin_dir_url( __FILE__ ) .'js/utils/admin_utils.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true); |
| 224 |
wp_enqueue_script('wpstream-admin-control', plugin_dir_url( __FILE__ ) .'js/admin_control.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true); |
| 225 |
// Hand the control script all of its translated status strings, the admin URL, and the multipart upload nonce. |
| 226 |
wp_localize_script('wpstream-admin-control', 'wpstream_admin_control_vars', |
| 227 |
array( |
| 228 |
'admin_url' => get_admin_url(), |
| 229 |
'multipart_upload_nonce' => wp_create_nonce( 'wpstream_multipart_upload_nonce' ), |
| 230 |
'loading_url' => WPSTREAM_PLUGIN_DIR_URL.'/img/loading.gif', |
| 231 |
'download_mess' => esc_html__('Click to download!','wpstream'), |
| 232 |
'uploading' => esc_html__('We are uploading your file. Do not close this window!','wpstream'), |
| 233 |
'upload_complete2' => esc_html__('Upload Complete! You can upload another file!','wpstream'), |
| 234 |
'not_accepted' => esc_html__('The file is not an accepted video format','wpstream'), |
| 235 |
'upload_complete' => esc_html__('Upload Complete!','wpstream'), |
| 236 |
'no_band' => esc_html__('Not enough streaming data.','wpsteam'), |
| 237 |
'no_band_no_store' => esc_html__('Not enough streaming data or storage.','wpsteam'), |
| 238 |
'no_streaming_hours' => esc_html__('Not enough streaming hours.','wpstream'), |
| 239 |
'exceeding_limit' => esc_html__('File size exceeds 5GB. Initiating multipart upload...','wpsteam'), |
| 240 |
'upload_failed' => esc_html__('Upload Failed!','wpstream'), |
| 241 |
'upload_failed2' => esc_html__('Upload Failed! Please Try again!','wpstream'), |
| 242 |
'choose_a_file' => esc_html__('Choose a file…','wpstream'), |
| 243 |
'preparing_multipart' => esc_html__('Preparing multipart upload...','wpstream'), |
| 244 |
'uploading_part' => esc_html__('Uploading part {part} of {total}...','wpstream'), |
| 245 |
'upload_failed_part' => esc_html__('Failed to upload part {part}. Please try again.','wpstream'), |
| 246 |
'completing_upload' => esc_html__('Completing upload. Please wait...','wpstream'), |
| 247 |
'upload_failed_part_retry' => esc_html__('Failed to upload part {part}. Retrying...','wpstream'), |
| 248 |
'choose_recording' => esc_html__( 'Choose Recording', 'wpstream' ), |
| 249 |
'select_recording' => esc_html__( 'Please select a recording from the list', 'wpstream' ), |
| 250 |
'invalid_response' => esc_html__('Invalid response from server. Missing required upload data.', 'wpstream'), |
| 251 |
'video_processing' => esc_html__( 'The video is still processing', 'wpstream' ), |
| 252 |
'file_name_text' => esc_html__('File Name:','wpstream'), |
| 253 |
'channel_create_error' => esc_html__('Something did not work. Please try again.', 'wpstream'), |
| 254 |
'select_caption_file' => esc_html__('Select .vtt Captions File', 'wpstream'), |
| 255 |
'select_button' => esc_html__('Select', 'wpstream'), |
| 256 |
'remove_button' => esc_html__('Remove', 'wpstream'), |
| 257 |
'use_streaming_hours' => $this->wpstream_get_start_streaming_localization_flags()['use_streaming_hours'], |
| 258 |
)); |
| 259 |
|
| 260 |
// Recordings list script + its localized labels and the "create VOD from recording" links (free vs paid depending on WooCommerce). |
| 261 |
wp_enqueue_script('wpstream-recordings-videos-list', plugin_dir_url( __FILE__ ) .'js/recordings_videos_list.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true); |
| 262 |
wp_localize_script( 'wpstream-recordings-videos-list', 'wpstream_recordings_videos_list_vars', |
| 263 |
array( |
| 264 |
'delete_file' => esc_html__('Delete file', 'wpstream'), |
| 265 |
'download' => esc_html__( 'Download', 'wpstream'), |
| 266 |
'download_available' => esc_html__( 'Click to download! The url will work for the next 20 minutes!', 'wpstream'), |
| 267 |
'add_free_video_url' => esc_url( admin_url( 'post-new.php?post_type=wpstream_product_vod') . '&new_video_name=' ), |
| 268 |
'create_ftv_vod' => esc_html__( 'Create new Free-To-View VOD from this recording' , 'wpstream' ), |
| 269 |
'woocommerce_exists' => class_exists( 'WooCommerce' ), |
| 270 |
'add_paid_video_url' => esc_url( admin_url( 'post-new.php?post_type=product').'&new_video_name=' ), |
| 271 |
'create_ptv_vod' => esc_html__( 'Create new Pay-Per-View VOD from this recording' , 'wpstream' ), |
| 272 |
) |
| 273 |
); |
| 274 |
|
| 275 |
// Bundled QR generator (SEC-05): the Larix QR is built locally so the RTMP URL + |
| 276 |
// secret stream key never reach a third-party image service. Registered here too |
| 277 |
// because the admin loads start_streaming.js independently of the public enqueue. |
| 278 |
wp_register_script('wpstream-qrcode-generator', plugin_dir_url( __DIR__ ) . 'public/js/vendor/qrcode-generator.js', array(), '1.4.4', true); |
| 279 |
wp_register_script('wpstream-qr', plugin_dir_url( __DIR__ ) . 'public/js/wpstream-qr.js', array('wpstream-qrcode-generator'), WPSTREAM_PLUGIN_VERSION, true); |
| 280 |
|
| 281 |
// Shared public start/stop-streaming script (reused in admin), cache-busted by its file mtime. |
| 282 |
// Depends on wpstream-qr so wpstreamRenderQr() is defined before this runs. |
| 283 |
$modified_start_streaming_file_time = gmdate( 'YmdHi', filemtime( WPSTREAM_PLUGIN_PATH . 'public/js/start_streaming.js' ) ); |
| 284 |
wp_enqueue_script('wpstream-start-streaming_admin', plugin_dir_url( __DIR__ ) .'public/js/start_streaming.js', array('wpstream-qr'), $modified_start_streaming_file_time, true); |
| 285 |
// Feature flags (basic-streaming / streaming-hours mode) that tailor the confirm dialogs and warnings. |
| 286 |
$streaming_localization_flags = $this->wpstream_get_start_streaming_localization_flags(); |
| 287 |
wp_localize_script('wpstream-start-streaming_admin', 'wpstream_start_streaming_vars', |
| 288 |
array( |
| 289 |
'admin_url' => get_admin_url(), |
| 290 |
'loading_url' => WPSTREAM_PLUGIN_DIR_URL.'/img/loading.gif', |
| 291 |
'download_mess' => esc_html__('Click to download!','wpstream'), |
| 292 |
'uploading' => esc_html('We are uploading your file.Do not close this window!','wpstream'), |
| 293 |
'upload_complete2' => esc_html('Upload Complete! You can upload another file!','wpstream'), |
| 294 |
'not_accepted' => esc_html('The file is not an accepted video format','wpstream'), |
| 295 |
'upload_complete' => esc_html('Upload Complete!','wpstream'), |
| 296 |
'no_band' => esc_html('Not enough streaming data.','wpsteam'), |
| 297 |
'no_band_no_store' => esc_html('Not enough streaming data or storage.','wpsteam'), |
| 298 |
|
| 299 |
'start_streaming_action'=> esc_html__('TURNING ON','wpstream'), |
| 300 |
'stop_streaming_action' => esc_html__('TURNING OFF','wpstream'), |
| 301 |
'start_streaming' => esc_html__('TURN ON','wpstream'), |
| 302 |
'stop_streaming' => esc_html__('TURN OFF','wpstream'), |
| 303 |
'failed_fetching' => esc_html__('Failed to get channel info. Please try again.','wpstream'), |
| 304 |
'turned_on_tooltip' => esc_html__('Channel is now OFF. Click to turn ON.','wpstream'), |
| 305 |
'turned_off_tooltip' => esc_html__('Click to turn channel off. This will interrupt any ongoing broadcast.','wpstream'), |
| 306 |
'turning_on_tooltip' => esc_html__('Turning a channel on may take 1-2 minutes or more. Please be patient.','wpstream'), |
| 307 |
'turning_off_tooltip' => esc_html__('This may take a few minutes.','wpstream'), |
| 308 |
'error1' => esc_html__('You don\'t have enough data to start a new event!','wpstream'), |
| 309 |
'failed_event_creation' => esc_html__('Failed to start the channel. Please try again in a few minutes.','wpstream'), |
| 310 |
'channel_turning_on' => esc_html__('Channel is turning on','wpstream'), |
| 311 |
'channel_turning_off' => esc_html__('Channel is turning off','wpstream'), |
| 312 |
'channel_on' => esc_html__('Channel is ON','wpstream'), |
| 313 |
'channel_off' => esc_html__('Channel is OFF','wpstream'), |
| 314 |
'turn_off_confirm' => esc_html__( |
| 315 |
'ARE YOU SURE you\'d like to TURN OFF the channel now? ' . PHP_EOL . PHP_EOL . |
| 316 |
'- Channels TURN OFF automatically after 1 hour of inactivity (no active broadcast).' . PHP_EOL . |
| 317 |
'- Manual TURN OFF is only useful if you require to change the channel settings immediately.' . PHP_EOL . |
| 318 |
'- If your channel is configured with Auto TURN ON, it will turn back on as soon as there is a broadcast.', |
| 319 |
'wpstream' |
| 320 |
), |
| 321 |
'is_basic_streaming' => $streaming_localization_flags['is_basic_streaming'], |
| 322 |
'use_streaming_hours' => $streaming_localization_flags['use_streaming_hours'], |
| 323 |
'basic_streaming_warning' => esc_html__( |
| 324 |
'You’ve used all available broadcast or viewer hours.' . PHP_EOL . PHP_EOL . |
| 325 |
'Some live channel features will be limited, including recording, viewer count, browser broadcasting, and content protection.' . PHP_EOL . PHP_EOL . |
| 326 |
'Top up your resources or upgrade your plan to use all features, or choose OK to start anyway.', |
| 327 |
'wpstream' |
| 328 |
), |
| 329 |
'basic_streaming_warning_traffic' => esc_html__( |
| 330 |
'Your account is now in BASIC STREAMING mode.' . PHP_EOL . PHP_EOL . |
| 331 |
'Instead of offloading to the WpStream Cloud, this mode relies on WordPress and hosting resources to process and deliver video. In some WP environments, streaming may be unreliable.' . PHP_EOL . |
| 332 |
'Certain features, such as recording, viewer count, browser broadcasting, and content protection are unavailable.' . PHP_EOL . PHP_EOL . |
| 333 |
'- To take advantage of all features, please choose Cancel and upgrade your plan.' . PHP_EOL . |
| 334 |
'- Otherwise, choose OK to start your channel with these limitations.' . PHP_EOL . PHP_EOL . |
| 335 |
'ARE YOU SURE you want to continue with Basic Streaming?', |
| 336 |
'wpstream' |
| 337 |
), |
| 338 |
'broadcaster_url' => esc_url( esc_url(home_url('/broadcaster-page/') ) ), |
| 339 |
'is_onboarding' => isset($_GET['onboard']) && $_GET['onboard'] === 'yes' ? 'yes' : 'no', |
| 340 |
)); |
| 341 |
|
| 342 |
// Settings-page script with its save/logo-picker strings and the broadcaster page URL. |
| 343 |
wp_enqueue_script('wpstream-settings', plugin_dir_url( __DIR__ ) .'/admin/js/wpstream_settings.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true); |
| 344 |
wp_localize_script('wpstream-settings', 'wpstream_settings_vars', array( |
| 345 |
'error_message' => esc_html__( 'Failed to save settings. Please try again.', 'wpstream'), |
| 346 |
'choose_image_text' => esc_html__( 'Choose Logo Image', 'wpstream'), |
| 347 |
'select_image_text' => esc_html__( 'Select Image', 'wpstream'), |
| 348 |
'update_successful' => esc_html__( 'Update Successful.', 'wpstream'), |
| 349 |
'update_failed' => esc_html__( 'Something went wrong. Try again.', 'wpstream'), |
| 350 |
'broadcaster_url' => esc_url( esc_url(home_url('/broadcaster-page/') ) ), |
| 351 |
)); |
| 352 |
|
| 353 |
|
| 354 |
// Optional ?branch= override (e.g. beta onboarding flow), sanitized from the query string. |
| 355 |
$branch = isset($_GET['branch']) ? sanitize_text_field( wp_unslash( $_GET['branch'] ) ) : ''; |
| 356 |
// Core onboarding wizard script + its navigation URLs. |
| 357 |
wp_enqueue_script('wpstream-on-boarding-js',plugin_dir_url( __DIR__ ) .'/admin/js/wpstream-onboarding2.js',array(), WPSTREAM_PLUGIN_VERSION, true); |
| 358 |
wp_localize_script('wpstream-on-boarding-js', 'wpstreamonboarding_js_vars', |
| 359 |
array( |
| 360 |
'admin_url' => get_admin_url(), |
| 361 |
'plugin_url' => get_dashboard_url().'/plugins.php', |
| 362 |
'upload_url' => get_dashboard_url().'admin.php?page=wpstream_recordings', |
| 363 |
'branch' => $branch |
| 364 |
)); |
| 365 |
|
| 366 |
// Identify the current admin screen so screen-specific bundles load only where needed. |
| 367 |
$current_screen=get_current_screen(); |
| 368 |
// enqueue the file only on the on-boarding page and wpstream_product post type |
| 369 |
// True on the dedicated Quick Start / onboarding admin page. |
| 370 |
$is_wpstream_onboarding_page = $current_screen->base ==='wpstream_page_wpstream_onboard'; |
| 371 |
// True on a product edit screen reached from the onboarding flow (?onboard=yes). |
| 372 |
$is_wpstream_onboarding_post_type_page = isset($_GET['onboard']) && $_GET['onboard'] === 'yes'; |
| 373 |
// Load the onboarding-page bundle in either onboarding context. |
| 374 |
$onboarding_visible = $is_wpstream_onboarding_page || $is_wpstream_onboarding_post_type_page; |
| 375 |
if( $onboarding_visible) { |
| 376 |
// Onboarding telemetry/page script; current_page distinguishes wizard vs post-edit context. |
| 377 |
wp_enqueue_script('wpstream-on-boarding-page-js', plugin_dir_url( __DIR__ ) .'admin/js/wpstream-onboarding-page.js',array(), WPSTREAM_PLUGIN_VERSION, true); |
| 378 |
wp_localize_script( 'wpstream-on-boarding-page-js', 'wpstream_onboarding_page_vars', |
| 379 |
array( |
| 380 |
'admin_url' => get_admin_url(), |
| 381 |
'request_url' => WPSTREAM_CLICK, |
| 382 |
'wps_user' => get_option('wpstream_api_username_from_token'), |
| 383 |
'current_page' => $is_wpstream_onboarding_post_type_page ? 'post_edit' : 'onboarding', |
| 384 |
'plugin_version' => WPSTREAM_PLUGIN_VERSION, |
| 385 |
'branch' => $branch, |
| 386 |
) |
| 387 |
); |
| 388 |
} |
| 389 |
|
| 390 |
// On the credentials/channels/recordings/onboard screens, load the quota widget updater. |
| 391 |
if ( in_array( $current_screen->base, ['toplevel_page_wpstream_credentials', 'wpstream_page_wpstream_live_channels', 'wpstream_page_wpstream_recordings', 'wpstream_page_wpstream_onboard'] ) ) { |
| 392 |
wp_enqueue_script( 'wpstream-user-quota-update', plugin_dir_url( __DIR__ ) . 'admin/js/wpstream-user-quota.js', array(), WPSTREAM_PLUGIN_VERSION, true ); |
| 393 |
wp_localize_script( 'wpstream-user-quota-update', 'wpstream_user_quota_vars', array( |
| 394 |
'admin_url' => get_admin_url() |
| 395 |
)); |
| 396 |
} |
| 397 |
|
| 398 |
// Add localized variables for broadcaster |
| 399 |
// Provide the broadcaster script (registered elsewhere) with its AJAX URL and nonce. |
| 400 |
wp_localize_script('wpstream-broadcaster', 'wpstream_broadcaster_vars', array( |
| 401 |
'ajax_url' => admin_url('admin-ajax.php'), |
| 402 |
'nonce' => wp_create_nonce('wpstream_broadcaster_nonce'), |
| 403 |
'plugin_url' => plugin_dir_url(__FILE__), |
| 404 |
|
| 405 |
)); |
| 406 |
|
| 407 |
} |
| 408 |
|
| 409 |
|
| 410 |
/** |
| 411 |
* Add Plugin Administation menu |
| 412 |
* |
| 413 |
* Registers the top-level "WpStream" menu and its sub-pages (Credentials, |
| 414 |
* All Channels, Recordings, Settings, Quick Start), each mapped to a render |
| 415 |
* callback on this class and gated to the `administrator` capability. |
| 416 |
* |
| 417 |
* @since 3.0.1 |
| 418 |
* @return void |
| 419 |
*/ |
| 420 |
public function wpstream_manage_admin_menu() { |
| 421 |
|
| 422 |
// Top-level menu; its page defaults to the Credentials screen and uses the WpStream icon at position 20. |
| 423 |
add_menu_page( __('WpStream','wpestream'), __('WpStream ','wpstream'), 'administrator', 'wpstream_credentials', array($this,'wpstream_set_wpstream_credentials'), WPSTREAM_PLUGIN_DIR_URL.'img/wpstream-icon-menu_2.png',20 ); |
| 424 |
// Credentials sub-page (same slug as the parent so it is the default view). |
| 425 |
add_submenu_page( 'wpstream_credentials', __('WpStream Credentials','wpestream'), __('Credentials','wpestream'), 'administrator', 'wpstream_credentials', array($this,'wpstream_set_wpstream_credentials') ); |
| 426 |
// All Channels listing. |
| 427 |
add_submenu_page( 'wpstream_credentials', __('WpStream Live Channels','wpestream'), __('All Channels','wpestream'), 'administrator', 'wpstream_live_channels', array( $this,'wpstream_new_general_set')); |
| 428 |
// Recordings / media management. |
| 429 |
add_submenu_page( 'wpstream_credentials', __('WpStream Recordings','wpestream'), __('Recordings','wpestream'), 'administrator', 'wpstream_recordings', array($this,'wpstream_media_management')); |
| 430 |
// Global plugin settings. |
| 431 |
add_submenu_page( 'wpstream_credentials', __('WpStream Settings','wpestream'), __('Settings','wpestream'), 'administrator', 'wpstream_settings', array($this,'wpstream_settings')); |
| 432 |
|
| 433 |
// Quick Start / onboarding entry point. |
| 434 |
add_submenu_page( 'wpstream_credentials', __('WpStream Quick Start','wpestream'), __('WpStream Quick Start','wpestream'), 'administrator', 'wpstream_onboard', array($this,'wpstream_pre_onboard_display')); |
| 435 |
|
| 436 |
|
| 437 |
|
| 438 |
|
| 439 |
} |
| 440 |
|
| 441 |
|
| 442 |
|
| 443 |
|
| 444 |
/** |
| 445 |
* Shows events wpstream |
| 446 |
* |
| 447 |
* Renders the "All Channels" admin screen: the Pay-Per-View (WooCommerce |
| 448 |
* `product`) channel list, the Free-To-View (`wpstream_product`) list, the |
| 449 |
* quota/pack summary, and the "no channels" call-to-action. Each channel is |
| 450 |
* drawn via wpstream_live_stream_unit(). Echoes HTML directly. |
| 451 |
* |
| 452 |
* @since 3.0.1 |
| 453 |
* @return void |
| 454 |
*/ |
| 455 |
public function wpstream_new_general_set() { |
| 456 |
|
| 457 |
// Assume no channels until a query proves otherwise. |
| 458 |
$no_channel=1; |
| 459 |
|
| 460 |
// Presence check for WooCommerce Subscriptions (branch intentionally left empty here). |
| 461 |
if(class_exists ('WC_Subscription')){ |
| 462 |
|
| 463 |
} |
| 464 |
|
| 465 |
//event_passed |
| 466 |
// Query published PPV products that have not yet "passed" (event_passed != 1) and are of type live_stream/subscription. |
| 467 |
$args = array( |
| 468 |
'posts_per_page' => -1, |
| 469 |
'post_type' => 'product', |
| 470 |
'post_status' => 'publish', |
| 471 |
'meta_query' => array( |
| 472 |
array( |
| 473 |
'key' => 'event_passed', |
| 474 |
'value' => 1, |
| 475 |
'compare' => '!=', |
| 476 |
) |
| 477 |
), |
| 478 |
|
| 479 |
'tax_query' => array( |
| 480 |
'relation' => 'AND', |
| 481 |
array( |
| 482 |
'taxonomy' => 'product_type', |
| 483 |
'field' => 'slug', |
| 484 |
'terms' => array('live_stream','subscription') |
| 485 |
) |
| 486 |
), |
| 487 |
); |
| 488 |
|
| 489 |
|
| 490 |
|
| 491 |
// Run the PPV channel query and load the current live-event map + quota/pack data for the user. |
| 492 |
$event_list = new WP_Query($args); |
| 493 |
global $live_event_for_user; |
| 494 |
$live_event_for_user = $this->main->wpstream_live_connection->wpstream_get_live_event_for_user(); |
| 495 |
$pack_details = $this->main->quota_manager->get_live_quota_data( 'wpstream_new_general_set' ); |
| 496 |
|
| 497 |
// Print the quota/pack summary header for the user. |
| 498 |
$this->main->show_user_data($pack_details); |
| 499 |
if( $event_list->have_posts()){ |
| 500 |
|
| 501 |
|
| 502 |
// Section header + "create new PPV channel" link. |
| 503 |
print '<div class="pack_details_wrapper_transparent"> |
| 504 |
<h3>'.__('Your Pay-Per-View Channel List','wpstream').'</h3>'; |
| 505 |
|
| 506 |
|
| 507 |
$link_new = admin_url('post-new.php?post_type=product').'&new_stream='. rawurlencode('new'); |
| 508 |
|
| 509 |
print '<a href="'.esc_url($link_new).'" class="wpstream_create_new_product_link">'.esc_html__('Create new Pay-Per-View channel.','wpstream').'</a>'; |
| 510 |
print '</div>'; |
| 511 |
|
| 512 |
// Open the grid that will hold each channel card. |
| 513 |
print '<div style="clear: both;"></div><div class="event_list_wrapper">'; |
| 514 |
|
| 515 |
// Loop every matched PPV product. |
| 516 |
while ($event_list->have_posts()): $event_list->the_post(); |
| 517 |
|
| 518 |
// Gather the product id, whether a subscription is flagged as a live event, and its product type. |
| 519 |
$the_id = get_the_ID(); |
| 520 |
$is_subscription_live_event = esc_html(get_post_meta($the_id,'_subscript_live_event',true)); |
| 521 |
$term_list = wp_get_post_terms($the_id, 'product_type'); |
| 522 |
|
| 523 |
// Skip plain subscriptions that are not marked as live events. |
| 524 |
if( $term_list[0]->name=='subscription' && $is_subscription_live_event=='no'){ |
| 525 |
continue; |
| 526 |
} |
| 527 |
|
| 528 |
// Render the channel card for this product. |
| 529 |
$this->wpstream_live_stream_unit($the_id); |
| 530 |
|
| 531 |
endwhile; |
| 532 |
|
| 533 |
// Close the grid; at least one PPV channel exists. |
| 534 |
print'</div>'; |
| 535 |
$no_channel=1; |
| 536 |
}else{ |
| 537 |
// No PPV channels found. |
| 538 |
$no_channel=0; |
| 539 |
} |
| 540 |
|
| 541 |
|
| 542 |
// Nonce hidden input used by the JS "start event" action. |
| 543 |
$ajax_nonce = wp_create_nonce( "wpstream_start_event_nonce" ); |
| 544 |
print '<input type="hidden" id="wpstream_start_event_nonce" value="'.$ajax_nonce.'">'; |
| 545 |
|
| 546 |
// Current user context (user's own live_shows meta collected but not directly used below). |
| 547 |
$current_user = wp_get_current_user(); |
| 548 |
$allowded_html = array(); |
| 549 |
$userID = $current_user->ID; |
| 550 |
$user_live_streams = get_user_meta($userID,'live_shows'); |
| 551 |
|
| 552 |
|
| 553 |
// Restore the main query after the custom WP_Query loop. |
| 554 |
wp_reset_postdata(); |
| 555 |
|
| 556 |
|
| 557 |
|
| 558 |
|
| 559 |
// free |
| 560 |
|
| 561 |
// Query all published Free-To-View channels (custom `wpstream_product` post type). |
| 562 |
$args_free = array( |
| 563 |
'posts_per_page' => -1, |
| 564 |
'post_type' => 'wpstream_product', |
| 565 |
'post_status' => 'publish', |
| 566 |
|
| 567 |
|
| 568 |
); |
| 569 |
$event_list_free = new WP_Query($args_free); |
| 570 |
|
| 571 |
|
| 572 |
if( $event_list_free->have_posts()){ |
| 573 |
// Section header + "create new free channel" link. |
| 574 |
print '<div class="pack_details_wrapper_transparent"> |
| 575 |
<h3>'.__('Free-To-View Channels','wpstream').'</h3>'; |
| 576 |
|
| 577 |
$link_new = admin_url('post-new.php?post_type=wpstream_product'); |
| 578 |
print '<a href="'.esc_url($link_new).'" class="wpstream_create_new_product_link">'.esc_html__('Create new Free-To-View channel.','wpstream').'</a>'; |
| 579 |
print '</div>'; |
| 580 |
// Open the free-channel grid. |
| 581 |
print '<div style="clear: both;"></div><div class="event_list_wrapper">'; |
| 582 |
|
| 583 |
// Loop each free channel. |
| 584 |
while ($event_list_free->have_posts()): $event_list_free->the_post(); |
| 585 |
|
| 586 |
|
| 587 |
$the_id = get_the_ID(); |
| 588 |
|
| 589 |
// Render only channels whose event has not passed. |
| 590 |
if( get_post_meta ($the_id,'event_passed',true)!=1){ |
| 591 |
$this->wpstream_live_stream_unit($the_id); |
| 592 |
} |
| 593 |
|
| 594 |
endwhile; |
| 595 |
|
| 596 |
// Close the grid and emit the shared modal background + error modal markup. |
| 597 |
print'</div><div class="wpstream_modal_background"></div>'; |
| 598 |
print '<div class="wpstream_error_modal_notification"><div class="wpstream_error_content">er2</div> |
| 599 |
<div class="wpstream_error_ok wpstream_button" type="button">'.esc_html__('Close','wpstream').'</div> |
| 600 |
</div>'; |
| 601 |
$no_channel=1; |
| 602 |
}else{ |
| 603 |
// No free channels found. |
| 604 |
$no_channel=0; |
| 605 |
} |
| 606 |
|
| 607 |
|
| 608 |
|
| 609 |
|
| 610 |
// "No channels" call-to-action: build the two create links. |
| 611 |
$link_new_paid = admin_url('post-new.php?post_type=product').'&new_stream='. rawurlencode('new'); |
| 612 |
$link_new_free = admin_url('post-new.php?post_type=wpstream_product'); |
| 613 |
print '<div class="no_events_warning"> '; |
| 614 |
// Warn when the user has zero PPV channels. |
| 615 |
if($event_list->found_posts==0){ |
| 616 |
print '<div class="no_events_warning_mes">'.__('* You do not have any Pay-Per-View channels!','wpstream').'</div>'; |
| 617 |
} |
| 618 |
// Warn when the user has zero free channels. |
| 619 |
if($event_list_free->found_posts==0){ |
| 620 |
print '<div class="no_events_warning_mes">'. __('* You do not have any free channels!','wpstream').'</div>'; |
| 621 |
} |
| 622 |
|
| 623 |
// Offer buttons to create a free or paid channel. |
| 624 |
print '<a href="'.esc_url($link_new_free).'" class="wpstream_no_chanel_add_channel">'.esc_html__('Add new Free-To-View channel ','wpstream').'</a>'; |
| 625 |
print '<a href="'.esc_url($link_new_paid).'" class="wpstream_no_chanel_add_channel">'.esc_html__('Add new Pay-Per-View channel ','wpstream').'</a>'; |
| 626 |
|
| 627 |
print '</div>'; |
| 628 |
|
| 629 |
|
| 630 |
|
| 631 |
|
| 632 |
} |
| 633 |
// end wpstream_new_general_set |
| 634 |
|
| 635 |
|
| 636 |
/** |
| 637 |
* Social share |
| 638 |
* |
| 639 |
* Outputs the social-sharing icon row (Facebook, Twitter/X, Pinterest, |
| 640 |
* WhatsApp, LinkedIn, Reddit, email) for a given channel/post. Echoes HTML. |
| 641 |
* |
| 642 |
* @since 3.0.1 |
| 643 |
* @param int $the_id Post/product ID whose permalink and title are shared. |
| 644 |
* @return void |
| 645 |
*/ |
| 646 |
public function wpstream_social_share($the_id){ |
| 647 |
// Match the current request scheme so share URLs are not mixed-content. |
| 648 |
$protocol = is_ssl() ? 'https' : 'http'; |
| 649 |
// Featured image (used as the Pinterest "media") and the canonical permalink/title. |
| 650 |
$pinterest = wp_get_attachment_image_src(get_post_thumbnail_id($the_id), 'full'); |
| 651 |
$link = esc_url ( get_permalink($the_id) ); |
| 652 |
$title = get_the_title($the_id); |
| 653 |
// Pre-encode the Twitter status text and the mailto subject/body query. |
| 654 |
$twiter_status = urlencode( $title.' '.$link); |
| 655 |
$email_link = 'subject='.urlencode ( $title ) .'&body='. urlencode( esc_url($link)); |
| 656 |
|
| 657 |
// Pre-build the Facebook sharer URL. |
| 658 |
$facebook_link = esc_html($protocol).'://www.facebook.com/sharer.php?u='. esc_url($link) .'&t='. urlencode(get_the_title()); |
| 659 |
|
| 660 |
// Below: HTML markup for the share bar; each <a> targets one social network's share endpoint. |
| 661 |
?> |
| 662 |
<div class="wpstream_social_share_wrapper"> |
| 663 |
|
| 664 |
<a href="<?php print esc_url( $facebook_link); ?>" target="_blank" class="social_facebook wpstream_sharing_social"> |
| 665 |
<span class="dashicons dashicons-facebook-alt"></span> |
| 666 |
</a> |
| 667 |
|
| 668 |
<a href="<?php print esc_html($protocol);?>://twitter.com/intent/tweet?text=<?php echo esc_html($twiter_status); ?>" class="social_tweet wpstream_sharing_social" target="_blank"> |
| 669 |
<span class="dashicons dashicons-twitter"></span> |
| 670 |
</a> |
| 671 |
|
| 672 |
<a href="<?php print esc_html($protocol);?>://pinterest.com/pin/create/button/?url=<?php echo esc_url($link); ?>&media=<?php if (isset( $pinterest[0])){ echo esc_url($pinterest[0]); }?>&description=<?php echo urlencode(get_the_title()); ?>" target="_blank" class="social_pinterest wpstream_sharing_social"> |
| 673 |
<span class="dashicons dashicons-pinterest"></span> |
| 674 |
</a> |
| 675 |
|
| 676 |
<a href="<?php print esc_html($protocol);?>://api.whatsapp.com/send?text=<?php echo urlencode( get_the_title().' '. esc_url( $link )); ?>" class="social_whatsup wpstream_sharing_social" target="_blank"> |
| 677 |
<span class="dashicons dashicons-whatsapp"></span> |
| 678 |
</a> |
| 679 |
|
| 680 |
<a href="<?php print esc_html($protocol);?>://www.linkedin.com/sharing/share-offsite/?url=<?php echo urlencode(esc_url($link)); ?>" class="social_linkedin wpstream_sharing_social" target="_blank"> |
| 681 |
<span class="dashicons dashicons-linkedin"></span> |
| 682 |
</a> |
| 683 |
|
| 684 |
<a href="<?php print esc_html($protocol);?>:///www.reddit.com/submit?url==<?php echo urlencode(esc_url($link)); ?>" class="social_linkedin wpstream_sharing_social" target="_blank"> |
| 685 |
<span class="dashicons dashicons-reddit"></span> |
| 686 |
</a> |
| 687 |
|
| 688 |
<a href="mailto:email@email.com?<?php echo trim(esc_html($email_link));?>" data-action="share email" class="social_email wpstream_sharing_social"> |
| 689 |
<span class="dashicons dashicons-email-alt"></span> |
| 690 |
</a> |
| 691 |
|
| 692 |
<div class="wpstream_modal_explanations"> <?php print esc_html__('Spread the word! To let people know about your channel, click on the corresponding icon and share on the social platforms of your choice. The more the merrier!','wpstream'); ?> </div> |
| 693 |
<?php |
| 694 |
print '</div>'; |
| 695 |
} |
| 696 |
|
| 697 |
|
| 698 |
|
| 699 |
|
| 700 |
/** |
| 701 |
* Shows event unit card in admin |
| 702 |
* |
| 703 |
* Renders one channel "card" (thumbnail, title, ON/OFF button, and the |
| 704 |
* webcam / external-app / stats / settings / edit / view / share icon |
| 705 |
* controls) plus its settings/share/broadcast modals. Bails with a notice |
| 706 |
* if the user lacks the administrator capability or streaming permission. |
| 707 |
* Echoes HTML. |
| 708 |
* |
| 709 |
* @since 3.0.1 |
| 710 |
* @param int $the_id Channel product/post ID. |
| 711 |
* @param string $is_front Non-empty (e.g. 'front') when rendered on the front end. |
| 712 |
* @return void |
| 713 |
*/ |
| 714 |
public function wpstream_live_stream_unit($the_id,$is_front=''){ |
| 715 |
global $live_event_for_user; |
| 716 |
global $wpstream_plugin; |
| 717 |
$current_user = wp_get_current_user(); |
| 718 |
|
| 719 |
// Non-admins get a "not allowed" notice in the admin (back-end) context. |
| 720 |
if( !current_user_can('administrator')){ |
| 721 |
if($is_front=='' ){ |
| 722 |
print '<div class="event_list_unit">'; |
| 723 |
esc_html_e('You are not allowed to broadcast.','wpstream'); |
| 724 |
print '</div>'; |
| 725 |
return; |
| 726 |
} |
| 727 |
|
| 728 |
} |
| 729 |
|
| 730 |
// Enforce the plugin's own streaming-permission gate as well. |
| 731 |
if( !$this->main->wpstream_check_user_can_stream() ){ |
| 732 |
print '<div class="event_list_unit">'; |
| 733 |
esc_html_e('You are not allowed to broadcast','wpstream'); |
| 734 |
print '</div>'; |
| 735 |
return; |
| 736 |
} |
| 737 |
|
| 738 |
|
| 739 |
// Flag this card as "live" if the channel currently has an active event. |
| 740 |
$live_class=''; |
| 741 |
if(isset($live_event_for_user[$the_id])) { |
| 742 |
$live_class=" wpstream_show_started"; |
| 743 |
} |
| 744 |
|
| 745 |
|
| 746 |
// Thumbnail: the post's featured image, or the plugin logo as a fallback. |
| 747 |
if(has_post_thumbnail($the_id)){ |
| 748 |
$thumb = get_the_post_thumbnail_url($the_id,'thumbnail'); |
| 749 |
}else{ |
| 750 |
$thumb= plugin_dir_url( dirname( __FILE__ ) ). 'img/plugin-logo.png'; |
| 751 |
} |
| 752 |
|
| 753 |
// Default streaming display state (hidden data, empty OBS credentials/stats URL). |
| 754 |
$pending_streaming_class = 'hide_stream_data'; |
| 755 |
$external_software_streaming_class = ''; |
| 756 |
$obs_uri = ''; |
| 757 |
$obs_stream = ''; |
| 758 |
$live_data_url = ''; |
| 759 |
|
| 760 |
// On the front end, lazily fetch the live-event map if it was not preloaded. |
| 761 |
if( $live_event_for_user=='' && $is_front=='front' ){ |
| 762 |
$live_event_for_user = $this->main->wpstream_live_connection->wpstream_get_live_event_for_user(); |
| 763 |
} |
| 764 |
// Default the status/button to spinners; resolved below based on live state. |
| 765 |
$channel_status = '<div class="spinner" style="visibility: visible"></div>'; |
| 766 |
$button_status = '<div class="spinner" style="visibility: visible"></div>'; |
| 767 |
if(is_array($live_event_for_user) && isset($live_event_for_user[$the_id])) { |
| 768 |
// Channel is live: reveal stream data and grab the QoS/stats URL. |
| 769 |
$pending_streaming_class = 'pending_trigger'; |
| 770 |
$live_data_url = get_post_meta($the_id,'qos_url',true); |
| 771 |
// $channel_status = esc_html__('Channel is on','wpstream'); |
| 772 |
} else { |
| 773 |
// Channel is off: show OFF status and a TURN ON button. |
| 774 |
$channel_status = esc_html__('Channel is OFF','wpstream'); |
| 775 |
$button_status = esc_html__('TURN ON','wpstream'); |
| 776 |
} |
| 777 |
|
| 778 |
// Load the channel's server id and broadcasting credentials/URLs. |
| 779 |
$server_id = get_post_meta($the_id,'server_id',true); |
| 780 |
$obs_uri = get_post_meta($the_id,'obs_uri',true); |
| 781 |
$obs_stream = get_post_meta($the_id,'obs_stream',true); |
| 782 |
$webcaster_url = get_post_meta($the_id,'webcaster_url',true); |
| 783 |
$rtmp_ip_uri = ''; |
| 784 |
|
| 785 |
// Emit the start-event nonce, then open the card wrapper with all its data-* attributes. |
| 786 |
$ajax_nonce = wp_create_nonce( 'wpstream_start_event_nonce' ); |
| 787 |
print '<input type="hidden" id="wpstream_start_event_nonce" value="'.$ajax_nonce.'">'; |
| 788 |
print '<div class="event_list_unit '.$live_class.' '.$pending_streaming_class.' event_unit_style_'.esc_attr($is_front).'" data-show-id="'.intval($the_id).'" data-server-id="'.$server_id.'" data-server-url="'.$rtmp_ip_uri.'"">'; |
| 789 |
|
| 790 |
// Status line, notification slot, thumbnail and (trimmed) title with the channel ID. |
| 791 |
print '<div class="wpstream_channel_status">'.$channel_status.'</div>'; |
| 792 |
|
| 793 |
print '<div class="server_notification"></div>'; |
| 794 |
|
| 795 |
print '<div class="event_thumb_wrapper" style="background-image:url('.$thumb.')"></div>'; |
| 796 |
|
| 797 |
print '<div class="event_title" data-prodid="'.$the_id.'">'.wp_trim_words(get_the_title($the_id),10); |
| 798 |
|
| 799 |
|
| 800 |
/* print '***</br>'.get_post_meta($the_id,'obs_uri',true) .'***</br>'. |
| 801 |
get_post_meta($the_id,'obs_stream',true).'***</br>'. |
| 802 |
get_post_meta($the_id,'broadcast_url',true); |
| 803 |
*/ |
| 804 |
print '<div class="wpstream_channel_item_id">'.esc_html( '#ID' ).' '.$the_id.'</div>'; |
| 805 |
print '</div>'; |
| 806 |
|
| 807 |
|
| 808 |
// Primary ON/OFF toggle button (carries the start-event nonce). |
| 809 |
print '<div class="start_event wpstream_button wpstream_tooltip_wrapper" data-show-id="'.$the_id.'" data-nonce="' . esc_attr( $ajax_nonce ) . '" > ' . $button_status; |
| 810 |
print '<div class="wpstream_tooltip">'.esc_html__('Channel is now OFF. Click to turn ON.','wpestream').'</div>'; |
| 811 |
print '</div>'; |
| 812 |
|
| 813 |
// Column 1: broadcasting entry points (webcam, external app, live stats). |
| 814 |
print '<div class="wpstream_options_col1 wpstream_stream_browser_wrapper">'; |
| 815 |
|
| 816 |
// Webcam ("go live in browser") icon button. |
| 817 |
print '<div class="wpstream_inactive_icon start_webcaster wpstream_stream_browser wpstream-button-icon wpstream_tooltip_wrapper" data-webcaster-url="'.$webcaster_url.'" data-show-id="'.$the_id.'"">'; |
| 818 |
|
| 819 |
print '<div class="wpstream_tooltip_disabled">'.esc_html__('Turn ON the channel to go live.','wpestream').'</div>'; |
| 820 |
print '<div class="wpstream_tooltip">'.esc_html__('Go live with your webcam','wpestream').'</div>'; |
| 821 |
|
| 822 |
print '<svg width="41" height="51" viewBox="0 0 41 51" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 823 |
<path d="M40.7646 44.2989C39.8782 41.4216 38.3361 38.7814 36.181 36.4522C35.7274 35.9619 35.1629 35.3518 34.4858 34.8356C34.0448 34.4996 33.4262 34.5281 33.0182 34.9035C29.358 38.2714 25.2628 39.9088 20.4979 39.9088H20.4853C15.7103 39.9059 11.6117 38.264 7.95648 34.889C7.52404 34.4896 6.85874 34.484 6.41953 34.8764C3.30565 37.6569 1.20501 40.9206 0.175434 44.5778C-0.276291 46.1833 0.15652 47.8497 1.33328 49.0353C2.22709 49.9362 3.39869 50.4173 4.61958 50.4173C5.0357 50.4173 5.45813 50.3613 5.87645 50.247C10.147 49.081 14.0772 48.4108 17.8916 48.1983C21.1954 48.014 24.4465 48.1323 27.5541 48.5495C29.9559 48.8718 32.4237 49.3888 35.0996 50.1299C36.823 50.6076 38.459 50.1562 39.7059 48.86C40.9268 47.5901 41.2933 46.0132 40.7651 44.2994L40.7646 44.2989ZM38.0572 47.274C37.3825 47.9754 36.6367 48.1823 35.7092 47.9257C32.9321 47.1565 30.3638 46.6187 27.8578 46.2827C24.6079 45.8465 21.2117 45.723 17.7643 45.9151C13.7874 46.1365 9.70183 46.8319 5.27439 48.041C4.43435 48.2706 3.5673 48.0403 2.9568 47.425C2.35932 46.8227 2.14793 46.0108 2.37712 45.1975C3.21567 42.2171 4.7982 39.6162 7.2018 37.2734C11.0641 40.5378 15.5276 42.193 20.4845 42.196H20.4978C25.4557 42.196 29.9251 40.5386 33.7967 37.2667C34.024 37.4904 34.254 37.7359 34.5032 38.0059C36.4236 40.0816 37.7951 42.4256 38.5799 44.9728C38.8603 45.8811 38.6941 46.6125 38.0581 47.274L38.0572 47.274ZM20.4954 30.7538C13.8088 30.7523 8.54664 25.4128 8.54379 18.6256C8.5412 12.2624 13.9961 6.83353 20.3776 6.84872C27.1094 6.86467 32.4526 12.1652 32.4488 18.8231C32.4447 25.4243 27.1038 30.7547 20.4954 30.7538V30.7538ZM29.4401 18.8383C29.5559 13.9512 25.4358 9.8445 20.5347 9.83671C15.5301 9.82892 11.6488 13.9279 11.5328 18.5667C11.4086 23.5202 15.344 27.7103 20.3381 27.7926C25.5542 27.8783 29.554 23.5758 29.4401 18.8383V18.8383ZM20.4917 25.1342C17.2131 25.2436 14.1541 22.4349 14.1567 18.8924C14.1593 15.2627 16.9642 12.4191 20.5858 12.4662C24.0109 12.5111 26.8269 15.2466 26.8362 18.8471C26.8451 22.4293 23.782 25.2436 20.4917 25.1342V25.1342ZM18.5119 13.9982C17.3006 13.9908 15.6202 15.656 15.6295 16.8543C15.6328 17.3209 15.961 17.661 16.3983 17.6513C17.5973 17.6246 19.2281 16.0169 19.2696 14.8205C19.2881 14.2742 19.0371 14.0016 18.5119 13.9982L18.5119 13.9982ZM20.4964 0.568359C10.4428 0.568359 2.26333 8.74761 2.26333 18.8014C2.26333 28.8551 10.4426 37.0345 20.4964 37.0345C30.5502 37.0345 38.7295 28.8553 38.7295 18.8014C38.7291 8.7478 30.5502 0.568359 20.4964 0.568359V0.568359ZM20.4964 33.3886C12.4528 33.3886 5.90919 26.8449 5.90919 18.8014C5.90919 11.3105 11.585 5.12093 18.8624 4.30631C18.8561 4.36009 18.8528 4.41423 18.8528 4.46912C18.8468 5.37368 19.6112 6.16064 20.4927 6.15846C21.3636 6.15586 22.1283 5.39706 22.142 4.52143C22.1431 4.44874 22.1372 4.3768 22.1279 4.30596C29.4067 5.11929 35.0849 11.3092 35.0849 18.802C35.0842 26.8456 28.5404 33.3892 20.4968 33.3892L20.4964 33.3886Z" fill="black"/> |
| 824 |
</svg>'; |
| 825 |
print '</div>'; |
| 826 |
|
| 827 |
|
| 828 |
print '<div class="wpstream_inactive_icon wpstream_stream_pro wpstream-button-icon wpstream-trigger-modal wpstream_tooltip_wrapper" data-modal="wpestate_broadcast_modal" data-show-id="'.$the_id.'"">'; |
| 829 |
|
| 830 |
print '<div class="wpstream_tooltip_disabled">'.esc_html__('Turn ON the channel to go live.','wpestream').'</div>'; |
| 831 |
print '<div class="wpstream_tooltip">'.esc_html__('Go Live with external streaming app','wpestream').'</div>'; |
| 832 |
|
| 833 |
print '<svg width="51" height="38" viewBox="0 0 51 38" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 834 |
<path d="M48.1266 13.9634L41.891 17.5343V13.6438C41.891 10.8723 39.6525 8.63384 36.8811 8.63384H33.1504L30.1124 3.62393C28.7268 1.3324 26.2751 0 23.6103 0H10.2863C8.84722 0 7.67471 1.17251 7.67471 2.6116C7.67471 4.05068 8.84722 5.22319 10.2863 5.22319H23.6103C24.4631 5.22319 25.2093 5.64961 25.6888 6.3957L27.0744 8.63413L5.00991 8.63376C2.23843 8.63376 0 10.8722 0 13.6437V32.9901C0 35.7616 2.23843 38 5.00991 38H36.9342C39.7057 38 41.9441 35.7616 41.9441 32.9901V29.0461L48.1797 32.6169C49.2991 33.2564 50.6846 32.4571 50.6846 31.1778L50.6842 15.4022C50.6311 14.123 49.2455 13.3237 48.1261 13.9632L48.1266 13.9634ZM38.1603 32.9368C38.1603 33.6297 37.574 34.1625 36.9345 34.1625L5.01029 34.1629C4.31733 34.1629 3.78458 33.5766 3.78458 32.9372V13.5907C3.78458 12.8978 4.37086 12.365 5.01029 12.365H36.9345C37.6275 12.365 38.1603 12.9513 38.1603 13.5907V32.9368Z" fill="black"/> |
| 835 |
<path d="M22.4917 21.585H9.70066C8.84784 21.585 8.15527 22.2779 8.15527 23.1304V28.247C8.15527 29.0998 8.84823 29.7923 9.70066 29.7923H22.4917C23.3445 29.7923 24.0371 29.0994 24.0371 28.247V23.1304C24.0371 22.2775 23.3445 21.585 22.4917 21.585Z" fill="black"/> |
| 836 |
</svg>'; |
| 837 |
print '</div>'; |
| 838 |
|
| 839 |
|
| 840 |
|
| 841 |
|
| 842 |
print '<a href="'.esc_url($live_data_url).'" target="_blank" class="wpstream_inactive_icon wpstream_live_data wpstream_statistics_channel wpstream-button-icon wpstream_tooltip_wrapper" data-show-id="'.$the_id.'" >'; |
| 843 |
|
| 844 |
print '<div class="wpstream_tooltip_disabled">'.esc_html__('Turn ON the channel to see live stats.','wpestream').'</div>'; |
| 845 |
print '<div class="wpstream_tooltip">'.esc_html__('Live Statistics','wpestream').'</div>'; |
| 846 |
|
| 847 |
print'<svg width="50" height="42" viewBox="0 0 50 42" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 848 |
<path fill-rule="evenodd" clip-rule="evenodd" d="M48.9001 9.02344H40.3342C39.7288 9.02344 39.2344 9.51791 39.2344 10.1233V40.4275C39.2344 41.033 39.7288 41.5274 40.3342 41.5274H48.9001C49.5055 41.5274 50 41.0329 50 40.4275V10.1233C50 9.51791 49.5055 9.02344 48.9001 9.02344V9.02344Z" fill="black"/> |
| 849 |
<path fill-rule="evenodd" clip-rule="evenodd" d="M35.821 22.4067H27.2551C26.6497 22.4067 26.1553 22.9012 26.1553 23.5066V40.4277C26.1553 41.0332 26.6497 41.5276 27.2551 41.5276H35.821C36.4264 41.5276 36.9209 41.0332 36.9209 40.4277V23.5066C36.9209 22.9012 36.4264 22.4067 35.821 22.4067V22.4067Z" fill="black"/> |
| 850 |
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.7439 0H14.178C13.5726 0 13.0781 0.494478 13.0781 1.09989V40.4275C13.0781 41.0329 13.5726 41.5274 14.178 41.5274H22.7439C23.3493 41.5274 23.8438 41.0329 23.8438 40.4275V1.09989C23.8438 0.49447 23.3493 0 22.7439 0V0Z" fill="black"/> |
| 851 |
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.66573 15.2559H1.09987C0.49445 15.2559 -2.24584e-05 15.7512 -2.24584e-05 16.3558V40.4285C-2.24584e-05 41.0331 0.495325 41.5284 1.09987 41.5284H9.66573C10.2703 41.5284 10.7656 41.034 10.7656 40.4285V16.3558C10.7656 15.7503 10.2711 15.2559 9.66573 15.2559V15.2559Z" fill="black"/> |
| 852 |
</svg>'; |
| 853 |
print '</a>'; |
| 854 |
print '</div>'; |
| 855 |
|
| 856 |
// Column 2: management controls (settings + edit only for back-end admins, then view/share). |
| 857 |
print '<div class="wpstream_options_col2 wpstream_show_settings_wrapper">'; |
| 858 |
if($is_front==''){ |
| 859 |
// Settings modal trigger (disabled while the channel is ON). |
| 860 |
print '<div class="wpstream_show_settings wpstream-button-icon wpstream-trigger-modal wpstream_tooltip_wrapper" data-modal="wpestate_settings_modal" data-show-id="'.$the_id.'" value="'.esc_html__('Settings','wpstream').'">'; |
| 861 |
|
| 862 |
print '<div class="wpstream_tooltip_disabled">'.esc_html__(' |
| 863 |
Turn OFF the channel to change its settings.','wpestream').'</div>'; |
| 864 |
print '<div class="wpstream_tooltip">'.esc_html__('Channel Settings','wpestream').'</div>'; |
| 865 |
|
| 866 |
print '<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 867 |
<path d="M49.22 21.8648C48.82 21.6099 45.665 19.7247 44.415 19.15L42.8651 15.4C43.3251 14.1601 44.2099 10.6849 44.3652 10.0798H44.3649C44.4911 9.52035 44.3213 8.93514 43.9149 8.52987L41.47 6.09995C41.0658 5.69189 40.4795 5.52168 39.9201 5.65003C39.4601 5.75501 35.92 6.64994 34.5999 7.15012L30.8499 5.60021C30.3048 4.40012 28.4699 1.32529 28.1351 0.795199V0.79485C27.8337 0.301688 27.298 0.00069643 26.7201 0H23.2798C22.7057 0.000348769 22.1718 0.294715 21.8648 0.779859C21.6098 1.1799 19.7247 4.33487 19.15 5.58487L15.4 7.13478C14.1601 6.67476 10.6849 5.78996 10.0798 5.63469V5.63504C9.52035 5.50878 8.93513 5.67864 8.52987 6.08496L6.09995 8.52988C5.69189 8.9341 5.52168 9.52042 5.65003 10.0798C5.75501 10.5398 6.64994 14.0799 7.15012 15.4L5.60021 19.15C4.40012 19.6951 1.32529 21.53 0.795198 21.8648H0.79485C0.301688 22.1662 0.00069643 22.7019 0 23.2798V26.7149C0.000348751 27.2893 0.294715 27.8233 0.779858 28.1299C1.1799 28.3849 4.33486 30.27 5.58487 30.8448L7.13478 34.5948C6.67475 35.8347 5.78996 39.3099 5.63469 39.915H5.63504C5.50878 40.4747 5.67863 41.06 6.08496 41.4649L8.51487 43.8948V43.8951C8.9191 44.3032 9.50541 44.4731 10.0648 44.3451C10.5248 44.2401 14.0649 43.3451 15.385 42.845L19.135 44.3949C19.6801 45.595 21.515 48.6698 21.8498 49.1999C22.1525 49.6997 22.6956 50.0035 23.2798 50H26.7149C27.2893 49.9996 27.8233 49.7053 28.1299 49.2201C28.3849 48.8201 30.27 45.6651 30.8447 44.4151L34.5947 42.8652C35.8347 43.3252 39.3098 44.21 39.9149 44.3653V44.3649C40.4747 44.4912 41.0599 44.3213 41.4649 43.915L43.8948 41.4851H43.8951C44.3032 41.0809 44.473 40.4945 44.345 39.9352C44.2401 39.4751 43.3451 35.9351 42.8449 34.615L44.3949 30.865C45.5949 30.3198 48.6698 28.485 49.1999 28.1501C49.6997 27.8474 50.0034 27.3044 49.9999 26.7201V23.2799C50.0045 22.7047 49.7087 22.1683 49.2201 21.8649L49.22 21.8648ZM24.9995 35.8845C22.1099 35.882 19.3399 34.7314 17.2985 32.6866C15.2572 30.6414 14.1118 27.8694 14.1146 24.9798C14.1171 22.0903 15.2676 19.3199 17.3125 17.2785C19.3577 15.2372 22.1297 14.0921 25.0193 14.0946C27.9088 14.0974 30.6792 15.2477 32.7205 17.2928C34.7619 19.338 35.907 22.1101 35.9045 24.9996C35.8978 27.8879 34.7462 30.6557 32.7021 32.6964C30.6576 34.7367 27.8876 35.8834 24.9994 35.8845H24.9995Z" fill="black"/> |
| 868 |
</svg> |
| 869 |
</div>'; |
| 870 |
|
| 871 |
|
| 872 |
// Edit-post link for the channel (admin only). |
| 873 |
print '<a href="'.get_edit_post_link($the_id).'" class="wpstream_edit_channel wpstream-button-icon wpstream_tooltip_wrapper" target="_blank" data-show-id="'.$the_id.'"">'; |
| 874 |
print '<div class="wpstream_tooltip">'.esc_html__('Edit Channel','wpestream').'</div>'; |
| 875 |
print '<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 876 |
<path d="M44.8203 1.79549C42.4263 -0.598498 38.5591 -0.598498 36.1651 1.79549L4.67494 33.2246C4.55203 33.3475 4.49079 33.4088 4.42954 33.5317C4.42954 33.5317 4.42954 33.5929 4.36829 33.5929C4.30705 33.7159 4.24539 33.7771 4.24539 33.9V33.9613L0.0711091 47.5886C-0.113044 48.264 0.0711092 48.9389 0.500659 49.4301C0.868966 49.7984 1.29852 49.9826 1.78973 49.9826C1.97388 49.9826 2.15804 49.9826 2.34219 49.9213L15.9085 45.747H15.9697C16.0926 45.6858 16.2151 45.6858 16.2768 45.6241C16.2768 45.6241 16.338 45.6241 16.338 45.5629C16.4609 45.5016 16.5834 45.44 16.6451 45.3175L48.0742 13.8884C50.4682 11.4944 50.4682 7.62715 48.0742 5.23316L44.8203 1.79549Z" fill="black"/> |
| 877 |
</svg>'; |
| 878 |
print '</a>'; |
| 879 |
} |
| 880 |
|
| 881 |
// Public "view channel" link (always shown). |
| 882 |
print '<a href="'.get_permalink($the_id).'" target="_blank" class="wpstream_view_channel wpstream-button-icon wpstream_tooltip_wrapper" data-show-id="'.$the_id.'"">'; |
| 883 |
print '<div class="wpstream_tooltip">'.esc_html__('View Channel','wpestream').'</div>'; |
| 884 |
print '<svg width="50" height="32" viewBox="0 0 50 32" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 885 |
<path d="M18.6201 15.2828H21.8752C21.8752 14.069 22.8681 13.0207 24.1373 13.0207V9.76562C21.1032 9.76562 18.6201 12.2487 18.6201 15.2828Z" fill="black"/> |
| 886 |
<path d="M24.855 0C12.9378 0 3.28272 10.9792 0.579292 14.3447C-0.193097 15.2826 -0.193097 16.662 0.579292 17.6553C3.28282 21.0208 12.9378 32 24.855 32C36.7722 32 46.4273 21.0208 49.1307 17.6553C49.9031 16.7174 49.9031 15.338 49.1307 14.3447C46.4275 10.9792 36.7722 0 24.855 0V0ZM24.855 25.8205C19.4482 25.8205 15.0344 21.4067 15.0344 15.9999C15.0344 10.5931 19.4482 6.17927 24.855 6.17927C30.2618 6.17927 34.6756 10.5931 34.6756 15.9999C34.6756 21.4067 30.2618 25.8205 24.855 25.8205Z" fill="black"/> |
| 887 |
</svg>'; |
| 888 |
print '</a>'; |
| 889 |
|
| 890 |
|
| 891 |
// Share modal trigger. |
| 892 |
print '<div class="wpstream_share_channel wpstream-button-icon wpstream-trigger-modal wpstream_tooltip_wrapper" data-modal="wpestate_share_modal" data-show-id="'.$the_id.'"">'; |
| 893 |
print '<div class="wpstream_tooltip">'.esc_html__('Share Channel','wpestream').'</div>'; |
| 894 |
print '<svg width="44" height="50" viewBox="0 0 44 50" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 895 |
<path d="M20.0777 20.5087L24.3481 17.9427C26.6432 16.5645 29.4554 16.2443 31.96 17.198C33.443 17.7642 35.1166 17.9363 36.852 17.6105C40.3163 16.9521 43.1162 14.1831 43.8115 10.7248C45.0606 4.51597 39.7751 -0.910703 33.5907 0.12835C30.2924 0.682061 27.5604 3.16211 26.5942 6.36168C26.4405 6.86646 26.3418 7.35835 26.2804 7.84466C25.9482 10.5028 24.2498 12.8044 21.9547 14.1827L19.3458 15.7456C17.1061 17.0869 14.3986 17.1792 11.9494 16.2748C10.9956 15.9241 9.96159 15.7271 8.88507 15.7271C3.39004 15.7271 -0.960663 20.7607 0.184097 26.4527C0.879447 29.911 3.67936 32.68 7.14364 33.3384C8.87902 33.6706 10.5525 33.492 12.0356 32.9259C14.5402 31.9721 17.3461 32.2919 19.6475 33.6706L23.906 36.2241C25.346 37.0855 26.2195 38.6424 26.2195 40.3159V40.3403C26.2195 45.8229 31.2287 50.1611 36.9018 49.0472C40.3602 48.3703 43.1415 45.583 43.8244 42.1246C44.9383 36.451 40.6 31.4423 35.1175 31.4423C34.0406 31.4423 33.007 31.6394 32.0532 31.9901C29.604 32.8944 26.9029 32.8022 24.6627 31.4608L20.0721 28.7038C18.6445 27.8484 17.7706 26.304 17.7706 24.6364V24.5875C17.7642 22.9208 18.6441 21.37 20.0777 20.5085L20.0777 20.5087Z" fill="black"/> |
| 896 |
</svg>'; |
| 897 |
print '</div>'; |
| 898 |
print '</div>'; |
| 899 |
|
| 900 |
// Emit the hidden per-channel modals (settings, share, broadcast) referenced by the triggers above. |
| 901 |
$this->wpstream_display_modal_seetings($the_id); |
| 902 |
$this->wpstream_display_modal_share($the_id); |
| 903 |
$this->wpstream_display_modal_broadcast($the_id,$external_software_streaming_class,$obs_uri,$obs_stream); |
| 904 |
|
| 905 |
// Close the card wrapper. |
| 906 |
print '</div>'; |
| 907 |
|
| 908 |
} |
| 909 |
|
| 910 |
|
| 911 |
|
| 912 |
|
| 913 |
/** |
| 914 |
* Shows event unit card in admin - theme version |
| 915 |
* |
| 916 |
* Front-end/theme variant of wpstream_live_stream_unit(): same controls |
| 917 |
* (webcam, external app, stats, settings, edit, view, share, ON/OFF) but |
| 918 |
* wrapped in theme-specific markup and with visible text labels under each |
| 919 |
* icon. Echoes HTML. |
| 920 |
* |
| 921 |
* @since 3.0.1 |
| 922 |
* @param int $the_id Channel product/post ID. |
| 923 |
* @param string $is_front Non-empty (e.g. 'front') when rendered on the front end. |
| 924 |
* @return void |
| 925 |
*/ |
| 926 |
public function wpstream_live_stream_unit_for_theme($the_id,$is_front=''){ |
| 927 |
global $live_event_for_user; |
| 928 |
global $wpstream_plugin; |
| 929 |
$current_user = wp_get_current_user(); |
| 930 |
|
| 931 |
// Non-admins get a "not allowed" notice in the back-end context. |
| 932 |
if( !current_user_can('administrator')){ |
| 933 |
if($is_front=='' ){ |
| 934 |
print '<div class="event_list_unit">'; |
| 935 |
esc_html_e('You are not allowed to broadcast.','wpstream'); |
| 936 |
print '</div>'; |
| 937 |
return; |
| 938 |
} |
| 939 |
|
| 940 |
} |
| 941 |
|
| 942 |
// Enforce the plugin's streaming-permission gate. |
| 943 |
if( !$this->main->wpstream_check_user_can_stream() ){ |
| 944 |
print '<div class="event_list_unit">'; |
| 945 |
esc_html_e('You are not allowed to broadcast','wpstream'); |
| 946 |
print '</div>'; |
| 947 |
return; |
| 948 |
} |
| 949 |
|
| 950 |
|
| 951 |
// Mark the card "live" when the channel currently has an active event. |
| 952 |
$live_class=''; |
| 953 |
if(isset($live_event_for_user[$the_id])) { |
| 954 |
$live_class=" wpstream_show_started"; |
| 955 |
} |
| 956 |
|
| 957 |
|
| 958 |
// Thumbnail: featured image or plugin-logo fallback. |
| 959 |
if(has_post_thumbnail($the_id)){ |
| 960 |
$thumb = get_the_post_thumbnail_url($the_id,'thumbnail'); |
| 961 |
}else{ |
| 962 |
$thumb= plugin_dir_url( dirname( __FILE__ ) ). 'img/plugin-logo.png'; |
| 963 |
} |
| 964 |
|
| 965 |
// Default streaming state and empty broadcasting credentials/URLs. |
| 966 |
$pending_streaming_class = 'hide_stream_data'; |
| 967 |
$external_software_streaming_class = ''; |
| 968 |
$obs_uri = ''; |
| 969 |
$obs_stream = ''; |
| 970 |
$live_data_url = ''; |
| 971 |
|
| 972 |
// Lazily load the live-event map on the front end if not preloaded. |
| 973 |
if( $live_event_for_user=='' && $is_front=='front' ){ |
| 974 |
$live_event_for_user = $this->main->wpstream_live_connection->wpstream_get_live_event_for_user(); |
| 975 |
} |
| 976 |
|
| 977 |
// Default the button to a spinner (WP admin spinner gif) and no status text. |
| 978 |
$spinner_url = admin_url('images/spinner-2x.gif'); |
| 979 |
$button_status = '<div class="spinner" style="background-image: url(' . $spinner_url . ')"></div>'; |
| 980 |
$channel_status = ''; |
| 981 |
if(is_array($live_event_for_user) && isset($live_event_for_user[$the_id])) { |
| 982 |
// Channel live: reveal data and grab the stats URL. |
| 983 |
$pending_streaming_class = 'pending_trigger'; |
| 984 |
$live_data_url = get_post_meta($the_id,'qos_url',true); |
| 985 |
// $channel_status = esc_html__('Channel is on','wpstream'); |
| 986 |
} else { |
| 987 |
// Channel off: show OFF status and a TURN ON button. |
| 988 |
$channel_status = esc_html__('Channel is OFF','wpstream'); |
| 989 |
$button_status = esc_html__('TURN ON', 'wpstream'); |
| 990 |
} |
| 991 |
|
| 992 |
// Load the channel's server id and broadcasting credentials/URLs. |
| 993 |
$server_id = get_post_meta($the_id,'server_id',true); |
| 994 |
$obs_uri = get_post_meta($the_id,'obs_uri',true); |
| 995 |
$obs_stream = get_post_meta($the_id,'obs_stream',true); |
| 996 |
$webcaster_url = get_post_meta($the_id,'webcaster_url',true); |
| 997 |
$rtmp_ip_uri = ''; |
| 998 |
|
| 999 |
// Open the theme card wrapper with its data-* attributes. |
| 1000 |
print '<div class="wpstream_theme_event_list_unit event_list_unit '.$live_class.' '.$pending_streaming_class.' event_unit_style_'.esc_attr($is_front).'" data-show-id="'.intval($the_id).'" data-server-id="'.$server_id.'" data-server-url="'.$rtmp_ip_uri.'"">'; |
| 1001 |
|
| 1002 |
// Status line (hidden when empty), notification slot, thumbnail and title with the channel ID. |
| 1003 |
print '<div class="wpstream_channel_status" style="' . ($channel_status ? '' : 'display: none') . '">'.$channel_status.'</div>'; |
| 1004 |
|
| 1005 |
print '<div class="server_notification"></div>'; |
| 1006 |
|
| 1007 |
print '<div class="wpstream_theme_event_thumb_wrapper">'; |
| 1008 |
print '<div class="event_thumb_wrapper" style="background-image:url('.$thumb.')"></div>'; |
| 1009 |
print '<div class="event_title" data-prodid="'.$the_id.'">'.wp_trim_words(get_the_title($the_id),10); |
| 1010 |
print '<div class="wpstream_channel_item_id">'.esc_html('#ID','wpstream').' '.$the_id.'</div>'; |
| 1011 |
print '</div>'; |
| 1012 |
print '</div>'; |
| 1013 |
|
| 1014 |
// Control bar: column 1 = broadcasting entry points (webcam, external app, live stats). |
| 1015 |
print '<div class="wpstream_theme_control_bar_wrapper">'; |
| 1016 |
|
| 1017 |
|
| 1018 |
print '<div class="wpstream_options_col1 wpstream_stream_browser_wrapper">'; |
| 1019 |
|
| 1020 |
print '<div class="wpstream_inactive_icon start_webcaster wpstream_stream_browser wpstream-button-icon wpstream_tooltip_wrapper" data-webcaster-url="'.$webcaster_url.'" data-show-id="'.$the_id.'"">'; |
| 1021 |
|
| 1022 |
print '<div class="wpstream_tooltip_disabled">'.esc_html__('Turn ON the channel to go live.','wpestream').'</div>'; |
| 1023 |
print '<div class="wpstream_tooltip">'.esc_html__('Go live with your webcam','wpestream').'</div>'; |
| 1024 |
|
| 1025 |
print '<svg width="41" height="51" viewBox="0 0 41 51" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 1026 |
<path d="M40.7646 44.2989C39.8782 41.4216 38.3361 38.7814 36.181 36.4522C35.7274 35.9619 35.1629 35.3518 34.4858 34.8356C34.0448 34.4996 33.4262 34.5281 33.0182 34.9035C29.358 38.2714 25.2628 39.9088 20.4979 39.9088H20.4853C15.7103 39.9059 11.6117 38.264 7.95648 34.889C7.52404 34.4896 6.85874 34.484 6.41953 34.8764C3.30565 37.6569 1.20501 40.9206 0.175434 44.5778C-0.276291 46.1833 0.15652 47.8497 1.33328 49.0353C2.22709 49.9362 3.39869 50.4173 4.61958 50.4173C5.0357 50.4173 5.45813 50.3613 5.87645 50.247C10.147 49.081 14.0772 48.4108 17.8916 48.1983C21.1954 48.014 24.4465 48.1323 27.5541 48.5495C29.9559 48.8718 32.4237 49.3888 35.0996 50.1299C36.823 50.6076 38.459 50.1562 39.7059 48.86C40.9268 47.5901 41.2933 46.0132 40.7651 44.2994L40.7646 44.2989ZM38.0572 47.274C37.3825 47.9754 36.6367 48.1823 35.7092 47.9257C32.9321 47.1565 30.3638 46.6187 27.8578 46.2827C24.6079 45.8465 21.2117 45.723 17.7643 45.9151C13.7874 46.1365 9.70183 46.8319 5.27439 48.041C4.43435 48.2706 3.5673 48.0403 2.9568 47.425C2.35932 46.8227 2.14793 46.0108 2.37712 45.1975C3.21567 42.2171 4.7982 39.6162 7.2018 37.2734C11.0641 40.5378 15.5276 42.193 20.4845 42.196H20.4978C25.4557 42.196 29.9251 40.5386 33.7967 37.2667C34.024 37.4904 34.254 37.7359 34.5032 38.0059C36.4236 40.0816 37.7951 42.4256 38.5799 44.9728C38.8603 45.8811 38.6941 46.6125 38.0581 47.274L38.0572 47.274ZM20.4954 30.7538C13.8088 30.7523 8.54664 25.4128 8.54379 18.6256C8.5412 12.2624 13.9961 6.83353 20.3776 6.84872C27.1094 6.86467 32.4526 12.1652 32.4488 18.8231C32.4447 25.4243 27.1038 30.7547 20.4954 30.7538V30.7538ZM29.4401 18.8383C29.5559 13.9512 25.4358 9.8445 20.5347 9.83671C15.5301 9.82892 11.6488 13.9279 11.5328 18.5667C11.4086 23.5202 15.344 27.7103 20.3381 27.7926C25.5542 27.8783 29.554 23.5758 29.4401 18.8383V18.8383ZM20.4917 25.1342C17.2131 25.2436 14.1541 22.4349 14.1567 18.8924C14.1593 15.2627 16.9642 12.4191 20.5858 12.4662C24.0109 12.5111 26.8269 15.2466 26.8362 18.8471C26.8451 22.4293 23.782 25.2436 20.4917 25.1342V25.1342ZM18.5119 13.9982C17.3006 13.9908 15.6202 15.656 15.6295 16.8543C15.6328 17.3209 15.961 17.661 16.3983 17.6513C17.5973 17.6246 19.2281 16.0169 19.2696 14.8205C19.2881 14.2742 19.0371 14.0016 18.5119 13.9982L18.5119 13.9982ZM20.4964 0.568359C10.4428 0.568359 2.26333 8.74761 2.26333 18.8014C2.26333 28.8551 10.4426 37.0345 20.4964 37.0345C30.5502 37.0345 38.7295 28.8553 38.7295 18.8014C38.7291 8.7478 30.5502 0.568359 20.4964 0.568359V0.568359ZM20.4964 33.3886C12.4528 33.3886 5.90919 26.8449 5.90919 18.8014C5.90919 11.3105 11.585 5.12093 18.8624 4.30631C18.8561 4.36009 18.8528 4.41423 18.8528 4.46912C18.8468 5.37368 19.6112 6.16064 20.4927 6.15846C21.3636 6.15586 22.1283 5.39706 22.142 4.52143C22.1431 4.44874 22.1372 4.3768 22.1279 4.30596C29.4067 5.11929 35.0849 11.3092 35.0849 18.802C35.0842 26.8456 28.5404 33.3892 20.4968 33.3892L20.4964 33.3886Z" fill="black"/> |
| 1027 |
</svg>'; |
| 1028 |
esc_html_e('Webcam','wpstream'); |
| 1029 |
print '</div>'; |
| 1030 |
|
| 1031 |
|
| 1032 |
print '<div class="wpstream_inactive_icon wpstream_stream_pro wpstream-button-icon wpstream-trigger-modal wpstream_tooltip_wrapper" data-modal="wpestate_broadcast_modal" data-show-id="'.$the_id.'"">'; |
| 1033 |
|
| 1034 |
print '<div class="wpstream_tooltip_disabled">'.esc_html__('Turn ON the channel to go live.','wpestream').'</div>'; |
| 1035 |
print '<div class="wpstream_tooltip">'.esc_html__('Go Live with external streaming app','wpestream').'</div>'; |
| 1036 |
|
| 1037 |
print '<svg width="51" height="38" viewBox="0 0 51 38" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 1038 |
<path d="M48.1266 13.9634L41.891 17.5343V13.6438C41.891 10.8723 39.6525 8.63384 36.8811 8.63384H33.1504L30.1124 3.62393C28.7268 1.3324 26.2751 0 23.6103 0H10.2863C8.84722 0 7.67471 1.17251 7.67471 2.6116C7.67471 4.05068 8.84722 5.22319 10.2863 5.22319H23.6103C24.4631 5.22319 25.2093 5.64961 25.6888 6.3957L27.0744 8.63413L5.00991 8.63376C2.23843 8.63376 0 10.8722 0 13.6437V32.9901C0 35.7616 2.23843 38 5.00991 38H36.9342C39.7057 38 41.9441 35.7616 41.9441 32.9901V29.0461L48.1797 32.6169C49.2991 33.2564 50.6846 32.4571 50.6846 31.1778L50.6842 15.4022C50.6311 14.123 49.2455 13.3237 48.1261 13.9632L48.1266 13.9634ZM38.1603 32.9368C38.1603 33.6297 37.574 34.1625 36.9345 34.1625L5.01029 34.1629C4.31733 34.1629 3.78458 33.5766 3.78458 32.9372V13.5907C3.78458 12.8978 4.37086 12.365 5.01029 12.365H36.9345C37.6275 12.365 38.1603 12.9513 38.1603 13.5907V32.9368Z" fill="black"/> |
| 1039 |
<path d="M22.4917 21.585H9.70066C8.84784 21.585 8.15527 22.2779 8.15527 23.1304V28.247C8.15527 29.0998 8.84823 29.7923 9.70066 29.7923H22.4917C23.3445 29.7923 24.0371 29.0994 24.0371 28.247V23.1304C24.0371 22.2775 23.3445 21.585 22.4917 21.585Z" fill="black"/> |
| 1040 |
</svg>'; |
| 1041 |
esc_html_e('External App','wpstream'); |
| 1042 |
print '</div>'; |
| 1043 |
|
| 1044 |
|
| 1045 |
|
| 1046 |
|
| 1047 |
print '<a href="'.esc_url($live_data_url).'" target="_blank" class="wpstream_inactive_icon wpstream_live_data wpstream_statistics_channel wpstream-button-icon wpstream_tooltip_wrapper" data-show-id="'.$the_id.'" >'; |
| 1048 |
|
| 1049 |
print '<div class="wpstream_tooltip_disabled">'.esc_html__('Turn ON the channel to see live stats.','wpestream').'</div>'; |
| 1050 |
print '<div class="wpstream_tooltip">'.esc_html__('Live Statistics','wpestream').'</div>'; |
| 1051 |
|
| 1052 |
print'<svg width="50" height="42" viewBox="0 0 50 42" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 1053 |
<path fill-rule="evenodd" clip-rule="evenodd" d="M48.9001 9.02344H40.3342C39.7288 9.02344 39.2344 9.51791 39.2344 10.1233V40.4275C39.2344 41.033 39.7288 41.5274 40.3342 41.5274H48.9001C49.5055 41.5274 50 41.0329 50 40.4275V10.1233C50 9.51791 49.5055 9.02344 48.9001 9.02344V9.02344Z" fill="black"/> |
| 1054 |
<path fill-rule="evenodd" clip-rule="evenodd" d="M35.821 22.4067H27.2551C26.6497 22.4067 26.1553 22.9012 26.1553 23.5066V40.4277C26.1553 41.0332 26.6497 41.5276 27.2551 41.5276H35.821C36.4264 41.5276 36.9209 41.0332 36.9209 40.4277V23.5066C36.9209 22.9012 36.4264 22.4067 35.821 22.4067V22.4067Z" fill="black"/> |
| 1055 |
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.7439 0H14.178C13.5726 0 13.0781 0.494478 13.0781 1.09989V40.4275C13.0781 41.0329 13.5726 41.5274 14.178 41.5274H22.7439C23.3493 41.5274 23.8438 41.0329 23.8438 40.4275V1.09989C23.8438 0.49447 23.3493 0 22.7439 0V0Z" fill="black"/> |
| 1056 |
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.66573 15.2559H1.09987C0.49445 15.2559 -2.24584e-05 15.7512 -2.24584e-05 16.3558V40.4285C-2.24584e-05 41.0331 0.495325 41.5284 1.09987 41.5284H9.66573C10.2703 41.5284 10.7656 41.034 10.7656 40.4285V16.3558C10.7656 15.7503 10.2711 15.2559 9.66573 15.2559V15.2559Z" fill="black"/> |
| 1057 |
</svg>'; |
| 1058 |
esc_html_e('Statistics','wpstream'); |
| 1059 |
print '</a>'; |
| 1060 |
print '</div>'; |
| 1061 |
|
| 1062 |
// Column 2: management controls (settings + edit only for back-end admins, then view/share). |
| 1063 |
print '<div class="wpstream_options_col2 wpstream_show_settings_wrapper">'; |
| 1064 |
if($is_front==''){ |
| 1065 |
// Settings modal trigger (disabled while the channel is ON). |
| 1066 |
print '<div class="wpstream_show_settings wpstream-button-icon wpstream-trigger-modal wpstream_tooltip_wrapper" data-modal="wpestate_settings_modal" data-show-id="'.$the_id.'" value="'.esc_html__('Settings','wpstream').'">'; |
| 1067 |
|
| 1068 |
print '<div class="wpstream_tooltip_disabled">'.esc_html__('Turn OFF the channel to change its settings.','wpestream').'</div>'; |
| 1069 |
print '<div class="wpstream_tooltip">'.esc_html__('Channel Settings','wpestream').'</div>'; |
| 1070 |
|
| 1071 |
print '<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 1072 |
<path d="M49.22 21.8648C48.82 21.6099 45.665 19.7247 44.415 19.15L42.8651 15.4C43.3251 14.1601 44.2099 10.6849 44.3652 10.0798H44.3649C44.4911 9.52035 44.3213 8.93514 43.9149 8.52987L41.47 6.09995C41.0658 5.69189 40.4795 5.52168 39.9201 5.65003C39.4601 5.75501 35.92 6.64994 34.5999 7.15012L30.8499 5.60021C30.3048 4.40012 28.4699 1.32529 28.1351 0.795199V0.79485C27.8337 0.301688 27.298 0.00069643 26.7201 0H23.2798C22.7057 0.000348769 22.1718 0.294715 21.8648 0.779859C21.6098 1.1799 19.7247 4.33487 19.15 5.58487L15.4 7.13478C14.1601 6.67476 10.6849 5.78996 10.0798 5.63469V5.63504C9.52035 5.50878 8.93513 5.67864 8.52987 6.08496L6.09995 8.52988C5.69189 8.9341 5.52168 9.52042 5.65003 10.0798C5.75501 10.5398 6.64994 14.0799 7.15012 15.4L5.60021 19.15C4.40012 19.6951 1.32529 21.53 0.795198 21.8648H0.79485C0.301688 22.1662 0.00069643 22.7019 0 23.2798V26.7149C0.000348751 27.2893 0.294715 27.8233 0.779858 28.1299C1.1799 28.3849 4.33486 30.27 5.58487 30.8448L7.13478 34.5948C6.67475 35.8347 5.78996 39.3099 5.63469 39.915H5.63504C5.50878 40.4747 5.67863 41.06 6.08496 41.4649L8.51487 43.8948V43.8951C8.9191 44.3032 9.50541 44.4731 10.0648 44.3451C10.5248 44.2401 14.0649 43.3451 15.385 42.845L19.135 44.3949C19.6801 45.595 21.515 48.6698 21.8498 49.1999C22.1525 49.6997 22.6956 50.0035 23.2798 50H26.7149C27.2893 49.9996 27.8233 49.7053 28.1299 49.2201C28.3849 48.8201 30.27 45.6651 30.8447 44.4151L34.5947 42.8652C35.8347 43.3252 39.3098 44.21 39.9149 44.3653V44.3649C40.4747 44.4912 41.0599 44.3213 41.4649 43.915L43.8948 41.4851H43.8951C44.3032 41.0809 44.473 40.4945 44.345 39.9352C44.2401 39.4751 43.3451 35.9351 42.8449 34.615L44.3949 30.865C45.5949 30.3198 48.6698 28.485 49.1999 28.1501C49.6997 27.8474 50.0034 27.3044 49.9999 26.7201V23.2799C50.0045 22.7047 49.7087 22.1683 49.2201 21.8649L49.22 21.8648ZM24.9995 35.8845C22.1099 35.882 19.3399 34.7314 17.2985 32.6866C15.2572 30.6414 14.1118 27.8694 14.1146 24.9798C14.1171 22.0903 15.2676 19.3199 17.3125 17.2785C19.3577 15.2372 22.1297 14.0921 25.0193 14.0946C27.9088 14.0974 30.6792 15.2477 32.7205 17.2928C34.7619 19.338 35.907 22.1101 35.9045 24.9996C35.8978 27.8879 34.7462 30.6557 32.7021 32.6964C30.6576 34.7367 27.8876 35.8834 24.9994 35.8845H24.9995Z" fill="black"/> |
| 1073 |
</svg> |
| 1074 |
</div>'; |
| 1075 |
|
| 1076 |
|
| 1077 |
print '<a href="'.get_edit_post_link($the_id).'" class="wpstream_edit_channel wpstream-button-icon wpstream_tooltip_wrapper" target="_blank" data-show-id="'.$the_id.'"">'; |
| 1078 |
print '<div class="wpstream_tooltip">'.esc_html__('Edit Channel','wpestream').'</div>'; |
| 1079 |
print '<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 1080 |
<path d="M44.8203 1.79549C42.4263 -0.598498 38.5591 -0.598498 36.1651 1.79549L4.67494 33.2246C4.55203 33.3475 4.49079 33.4088 4.42954 33.5317C4.42954 33.5317 4.42954 33.5929 4.36829 33.5929C4.30705 33.7159 4.24539 33.7771 4.24539 33.9V33.9613L0.0711091 47.5886C-0.113044 48.264 0.0711092 48.9389 0.500659 49.4301C0.868966 49.7984 1.29852 49.9826 1.78973 49.9826C1.97388 49.9826 2.15804 49.9826 2.34219 49.9213L15.9085 45.747H15.9697C16.0926 45.6858 16.2151 45.6858 16.2768 45.6241C16.2768 45.6241 16.338 45.6241 16.338 45.5629C16.4609 45.5016 16.5834 45.44 16.6451 45.3175L48.0742 13.8884C50.4682 11.4944 50.4682 7.62715 48.0742 5.23316L44.8203 1.79549Z" fill="black"/> |
| 1081 |
</svg>'; |
| 1082 |
print '</a>'; |
| 1083 |
} |
| 1084 |
|
| 1085 |
print '<a href="'.get_permalink($the_id).'" target="_blank" class="wpstream_view_channel wpstream-button-icon wpstream_tooltip_wrapper" data-show-id="'.$the_id.'"">'; |
| 1086 |
print '<div class="wpstream_tooltip">'.esc_html__('View Channel','wpestream').'</div>'; |
| 1087 |
print '<svg width="50" height="32" viewBox="0 0 50 32" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 1088 |
<path d="M18.6201 15.2828H21.8752C21.8752 14.069 22.8681 13.0207 24.1373 13.0207V9.76562C21.1032 9.76562 18.6201 12.2487 18.6201 15.2828Z" fill="black"/> |
| 1089 |
<path d="M24.855 0C12.9378 0 3.28272 10.9792 0.579292 14.3447C-0.193097 15.2826 -0.193097 16.662 0.579292 17.6553C3.28282 21.0208 12.9378 32 24.855 32C36.7722 32 46.4273 21.0208 49.1307 17.6553C49.9031 16.7174 49.9031 15.338 49.1307 14.3447C46.4275 10.9792 36.7722 0 24.855 0V0ZM24.855 25.8205C19.4482 25.8205 15.0344 21.4067 15.0344 15.9999C15.0344 10.5931 19.4482 6.17927 24.855 6.17927C30.2618 6.17927 34.6756 10.5931 34.6756 15.9999C34.6756 21.4067 30.2618 25.8205 24.855 25.8205Z" fill="black"/> |
| 1090 |
</svg>'; |
| 1091 |
esc_html_e('View','wpstream'); |
| 1092 |
print '</a>'; |
| 1093 |
|
| 1094 |
|
| 1095 |
print '<div class="wpstream_share_channel wpstream-button-icon wpstream-trigger-modal wpstream_tooltip_wrapper" data-modal="wpestate_share_modal" data-show-id="'.$the_id.'"">'; |
| 1096 |
print '<div class="wpstream_tooltip">'.esc_html__('Share Channel','wpestream').'</div>'; |
| 1097 |
print '<svg width="44" height="50" viewBox="0 0 44 50" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 1098 |
<path d="M20.0777 20.5087L24.3481 17.9427C26.6432 16.5645 29.4554 16.2443 31.96 17.198C33.443 17.7642 35.1166 17.9363 36.852 17.6105C40.3163 16.9521 43.1162 14.1831 43.8115 10.7248C45.0606 4.51597 39.7751 -0.910703 33.5907 0.12835C30.2924 0.682061 27.5604 3.16211 26.5942 6.36168C26.4405 6.86646 26.3418 7.35835 26.2804 7.84466C25.9482 10.5028 24.2498 12.8044 21.9547 14.1827L19.3458 15.7456C17.1061 17.0869 14.3986 17.1792 11.9494 16.2748C10.9956 15.9241 9.96159 15.7271 8.88507 15.7271C3.39004 15.7271 -0.960663 20.7607 0.184097 26.4527C0.879447 29.911 3.67936 32.68 7.14364 33.3384C8.87902 33.6706 10.5525 33.492 12.0356 32.9259C14.5402 31.9721 17.3461 32.2919 19.6475 33.6706L23.906 36.2241C25.346 37.0855 26.2195 38.6424 26.2195 40.3159V40.3403C26.2195 45.8229 31.2287 50.1611 36.9018 49.0472C40.3602 48.3703 43.1415 45.583 43.8244 42.1246C44.9383 36.451 40.6 31.4423 35.1175 31.4423C34.0406 31.4423 33.007 31.6394 32.0532 31.9901C29.604 32.8944 26.9029 32.8022 24.6627 31.4608L20.0721 28.7038C18.6445 27.8484 17.7706 26.304 17.7706 24.6364V24.5875C17.7642 22.9208 18.6441 21.37 20.0777 20.5085L20.0777 20.5087Z" fill="black"/> |
| 1099 |
</svg>'; |
| 1100 |
esc_html_e('Share','wpstream'); |
| 1101 |
print '</div>'; |
| 1102 |
print '</div>'; |
| 1103 |
|
| 1104 |
|
| 1105 |
// ON/OFF toggle button placed after the control columns in the theme layout. |
| 1106 |
print '<div class="start_event wpstream_button wpstream_tooltip_wrapper" data-show-id="'.$the_id.'" > '. $button_status; |
| 1107 |
print '<div class="wpstream_tooltip">'.esc_html__('Channel is now OFF. Click to turn ON.','wpestream').'</div>'; |
| 1108 |
print '</div>'; |
| 1109 |
|
| 1110 |
// Close the control bar wrapper. |
| 1111 |
print '</div>'; |
| 1112 |
|
| 1113 |
|
| 1114 |
|
| 1115 |
|
| 1116 |
// Emit the hidden per-channel modals (settings, share, broadcast). |
| 1117 |
$this->wpstream_display_modal_seetings($the_id); |
| 1118 |
$this->wpstream_display_modal_share($the_id); |
| 1119 |
$this->wpstream_display_modal_broadcast($the_id,$external_software_streaming_class,$obs_uri,$obs_stream); |
| 1120 |
|
| 1121 |
// Close the theme card wrapper. |
| 1122 |
print '</div>'; |
| 1123 |
|
| 1124 |
} |
| 1125 |
|
| 1126 |
|
| 1127 |
/* |
| 1128 |
* |
| 1129 |
* Close modal button |
| 1130 |
* |
| 1131 |
*/ |
| 1132 |
// Outputs the small "X" element used to dismiss the modal dialogs. |
| 1133 |
public function wpstream_close_modal_button(){ |
| 1134 |
print '<div class="wpstream_close_modal"></div>'; |
| 1135 |
} |
| 1136 |
|
| 1137 |
/** |
| 1138 |
* Render the "edit settings for this channel" toggle plus the per-channel |
| 1139 |
* event-options block on the product edit screen. |
| 1140 |
* |
| 1141 |
* When the toggle is off, the site-wide Default Channel Settings apply; |
| 1142 |
* when on, the local per-channel options are used. In basic-stream mode a |
| 1143 |
* warning is shown and the toggle is disabled. Echoes HTML. |
| 1144 |
* |
| 1145 |
* @param bool $is_basic_stream_mode True to render the basic-streaming warning and disable the toggle. |
| 1146 |
* @return void |
| 1147 |
*/ |
| 1148 |
public function wpstream_local_event_options_toggle( $is_basic_stream_mode = false ) { |
| 1149 |
// Read whether this post uses the global options and any stored per-channel overrides. |
| 1150 |
$use_global_event_options = get_post_meta(get_the_ID(), 'use_global_event_options', true ); |
| 1151 |
$local_event_options = get_post_meta( get_the_ID(), 'local_event_options', true ); |
| 1152 |
// The local-options toggle is "on" when local options exist and global isn't forced, or global is explicitly 0. |
| 1153 |
$use_local_event_options_enabled = ( is_array( $local_event_options ) && empty( $use_global_event_options ) ) || |
| 1154 |
( !empty($use_global_event_options) && intval( $use_global_event_options ) === 0 ); |
| 1155 |
|
| 1156 |
// In basic-streaming mode, prepend the limitation notice. |
| 1157 |
if ( $is_basic_stream_mode ) { |
| 1158 |
$this->wpstream_basic_stream_mode_message(); |
| 1159 |
} |
| 1160 |
|
| 1161 |
// Toggle row: label/description on the left, a switch checkbox on the right. |
| 1162 |
print '<div class="wpstream_local_event_options_toggle_wrapper">'; |
| 1163 |
print '<div class="wpstream_local_event_options_toggle_info">'; |
| 1164 |
print '<label for="local_event_options_enabled" class="wpstream_local_event_options_label">'.esc_html__('Edit settings for this channel','wpstream').'</label>'; |
| 1165 |
print '<span>'.sprintf(esc_html__('When is OFF, the settings from %s will be applied','wpstream'), '<a href="' . admin_url('admin.php?page=wpstream_settings&tab=default_options') . '" target="_blank">'.esc_html__('Default Channel Settings','wpstream').'</a>').'</span>'; |
| 1166 |
print '</div>'; |
| 1167 |
// The switch checkbox reflects the current toggle state and is disabled in basic-stream mode. |
| 1168 |
print '<label class="wpstream_switch">'; |
| 1169 |
print '<input id="local_event_options_enabled" type="checkbox" class="wpstream_local_event_options_toggle" ' . ($use_local_event_options_enabled === true ? 'checked' : '') . ' ' . ( $is_basic_stream_mode ? 'disabled' : '' ) . '>'; |
| 1170 |
print '<span class="wpstream_slider round"></span>'; |
| 1171 |
print '</label>'; |
| 1172 |
print '</div>'; |
| 1173 |
} |
| 1174 |
|
| 1175 |
/** |
| 1176 |
* Print the basic-streaming-mode notice shown above the channel settings. |
| 1177 |
* |
| 1178 |
* Wording differs depending on whether the account is out of streaming |
| 1179 |
* hours vs. simply on a basic plan; both variants link to pricing. Echoes HTML. |
| 1180 |
* |
| 1181 |
* @return void |
| 1182 |
*/ |
| 1183 |
public function wpstream_basic_stream_mode_message() { |
| 1184 |
print '<div class="basic-mode-notice">'; |
| 1185 |
// Out-of-hours variant: offer to top up or upgrade. |
| 1186 |
if ( $this->wpstream_is_use_streaming_hours() ) { |
| 1187 |
print sprintf( |
| 1188 |
wp_kses( |
| 1189 |
__( |
| 1190 |
'You’ve used all available broadcast or viewer hours. Default channel settings will be used for now. To edit channel settings, <a href="%s" target="_blank">top up</a> your resources or <a href="%s" target="_blank">upgrade</a> your plan.', |
| 1191 |
'wpstream' |
| 1192 |
), |
| 1193 |
array( 'a' => array( |
| 1194 |
'href' => array(), |
| 1195 |
'target' => array() |
| 1196 |
) |
| 1197 |
) |
| 1198 |
), |
| 1199 |
esc_url( 'https://wpstream.net/pricing/' ), |
| 1200 |
esc_url( 'https://wpstream.net/pricing/' ) |
| 1201 |
); |
| 1202 |
} else { |
| 1203 |
// Plain basic-mode variant: offer to upgrade. |
| 1204 |
print sprintf( |
| 1205 |
wp_kses( |
| 1206 |
__( |
| 1207 |
'You are currently in Basic Streaming Mode. The default channel settings will be used instead. Please <a href="%s" target="_blank">upgrade</a> your plan to edit the channel settings.', |
| 1208 |
'wpstream' |
| 1209 |
), |
| 1210 |
array( |
| 1211 |
'a' => array( |
| 1212 |
'href' => array() |
| 1213 |
) |
| 1214 |
) |
| 1215 |
), |
| 1216 |
esc_url( 'https://wpstream.net/pricing/' ) |
| 1217 |
); |
| 1218 |
} |
| 1219 |
print '</div>'; |
| 1220 |
} |
| 1221 |
|
| 1222 |
/* |
| 1223 |
* |
| 1224 |
* Display modal settings |
| 1225 |
* |
| 1226 |
*/ |
| 1227 |
/** |
| 1228 |
* Render the per-channel "Channel Settings" modal (settings toggle plus |
| 1229 |
* the streaming option controls). Echoes HTML. |
| 1230 |
* |
| 1231 |
* @param int $the_id Channel post/product ID. |
| 1232 |
* @return void |
| 1233 |
*/ |
| 1234 |
public function wpstream_display_modal_seetings($the_id){ |
| 1235 |
// Whether the account is in basic-streaming mode (disables/locks some options). |
| 1236 |
$is_basic_stream_mode = $this->wpstream_is_basic_streaming_mode(); |
| 1237 |
|
| 1238 |
// Modal shell with close button and title carrying the channel ID. |
| 1239 |
print '<div class="wpstream_modal_form wpestate_settings_modal">'; |
| 1240 |
$this->wpstream_close_modal_button(); |
| 1241 |
print '<h3>'; |
| 1242 |
printf( esc_html__('Channel Settings (#ID %s)','wpstream'),$the_id); |
| 1243 |
print '</h3>'; |
| 1244 |
|
| 1245 |
// The "edit settings for this channel" toggle. |
| 1246 |
$this->wpstream_local_event_options_toggle( $is_basic_stream_mode ); |
| 1247 |
|
| 1248 |
// Resolve whether local per-channel options are in effect (same rule as the toggle). |
| 1249 |
$local_event_options = get_post_meta($the_id,'local_event_options',true); |
| 1250 |
$use_global_event_options = get_post_meta($the_id, 'use_global_event_options',true); |
| 1251 |
$is_local_event_options_enabled = ( is_array( $local_event_options ) && empty( $use_global_event_options ) ) || |
| 1252 |
( !empty($use_global_event_options) && intval( $use_global_event_options ) === 0 ); |
| 1253 |
|
| 1254 |
// Options that do not apply to the per-channel (local) context. |
| 1255 |
$local_array_exclude=array('ses_encrypt','vod_domain_lock','vod_encrypt'); |
| 1256 |
|
| 1257 |
// Render the option controls; the 4th arg disables them when local options are off. |
| 1258 |
print '<div class="wpstream_event_streaming_local">'; |
| 1259 |
$this->user_streaming_global_channel_options( |
| 1260 |
'', |
| 1261 |
$local_event_options, |
| 1262 |
$local_array_exclude, |
| 1263 |
!$is_local_event_options_enabled, |
| 1264 |
$is_basic_stream_mode |
| 1265 |
); |
| 1266 |
print '</div>'; |
| 1267 |
print '</div>'; |
| 1268 |
} |
| 1269 |
|
| 1270 |
|
| 1271 |
/* |
| 1272 |
* |
| 1273 |
* Display share settings |
| 1274 |
* |
| 1275 |
*/ |
| 1276 |
/** |
| 1277 |
* Render the per-channel "Share your Channel" modal (social-share bar). Echoes HTML. |
| 1278 |
* |
| 1279 |
* @param int $the_id Channel post/product ID. |
| 1280 |
* @return void |
| 1281 |
*/ |
| 1282 |
public function wpstream_display_modal_share($the_id){ |
| 1283 |
// Modal shell with close button, title and the social-share icon row. |
| 1284 |
print '<div class="wpstream_modal_form wpestate_share_modal">'; |
| 1285 |
$this->wpstream_close_modal_button(); |
| 1286 |
print '<h3>'.esc_html__('Share your Channel','wpstream').'</h3>'; |
| 1287 |
$this->wpstream_social_share($the_id); |
| 1288 |
print '</div>'; |
| 1289 |
} |
| 1290 |
|
| 1291 |
|
| 1292 |
/* |
| 1293 |
* |
| 1294 |
* Display broadcast modal |
| 1295 |
* |
| 1296 |
*/ |
| 1297 |
|
| 1298 |
/** |
| 1299 |
* Render the "Go Live with External Streaming App" modal: an encoder |
| 1300 |
* picker (OBS, StreamYard, Restream, vMix, Wirecast, XSplit, Larix) plus |
| 1301 |
* one settings panel per encoder showing the RTMP server URI and stream |
| 1302 |
* key. Echoes HTML. |
| 1303 |
* |
| 1304 |
* @param int $the_id Channel post/product ID. |
| 1305 |
* @param string $external_software_streaming_class Extra CSS class flag (unused in body). |
| 1306 |
* @param string $obs_uri RTMP server/ingest URI for the channel. |
| 1307 |
* @param string $obs_stream RTMP stream key for the channel. |
| 1308 |
* @return void |
| 1309 |
*/ |
| 1310 |
public function wpstream_display_modal_broadcast($the_id,$external_software_streaming_class,$obs_uri,$obs_stream){ |
| 1311 |
// Modal shell with close button, title and instructions. |
| 1312 |
print '<div class="wpstream_modal_form wpestate_broadcast_modal">'; |
| 1313 |
$this->wpstream_close_modal_button(); |
| 1314 |
print '<h3>'.esc_html__('Go Live with External Streaming App','wpstream').'</h3>'; |
| 1315 |
|
| 1316 |
print '<div class="wpstream_modal_explanations">'.esc_html__('Please choose your RTMP encoder/broadcaster','wpstream').'</div>'; |
| 1317 |
|
| 1318 |
// Dropdown selecting which encoder's settings panel to show (driven by JS). |
| 1319 |
print '<select class="wpstream_external_broadcast_options">'; |
| 1320 |
print '<option value="wpstream_obs_settings">'.esc_html('OBS','wpstream').'</option>'; |
| 1321 |
print '<option value="wpstream_streamyard_settings">'.esc_html('StreamYard','wpstream').'</option>'; |
| 1322 |
print '<option value="wpstream_restream_settings">'.esc_html('Restream','wpstream').'</option>'; |
| 1323 |
print '<option value="wpstream_wimx_settings">'.esc_html('vMix','wpstream').'</option>'; |
| 1324 |
print '<option value="wpstream_wirecast_settings">'.esc_html('Wirecast','wpstream').'</option>'; |
| 1325 |
print '<option value="wpstream_xplit_settings">'.esc_html('XSplit','wpstream').'</option>'; |
| 1326 |
print '<option value="wpstream_larix_settings">'.esc_html('Larix Broadcaster','wpstream').'</option>'; |
| 1327 |
|
| 1328 |
print '</select>'; |
| 1329 |
|
| 1330 |
// Emit every encoder's settings panel; JS toggles the visible one. |
| 1331 |
$this->wpstream_obs_settings($obs_uri,$obs_stream); |
| 1332 |
$this->wpstream_streamyard_settings($obs_uri,$obs_stream); |
| 1333 |
$this->wpstream_restream_settings($obs_uri,$obs_stream); |
| 1334 |
$this->wpstream_wmix_settings($obs_uri,$obs_stream); |
| 1335 |
$this->wpstream_wirecast_settings($obs_uri,$obs_stream); |
| 1336 |
$this->wpstream_xplit_settings($obs_uri,$obs_stream); |
| 1337 |
$this->wpstream_larix_settings($obs_uri,$obs_stream); |
| 1338 |
|
| 1339 |
print '</div>'; |
| 1340 |
} |
| 1341 |
|
| 1342 |
|
| 1343 |
|
| 1344 |
|
| 1345 |
/* |
| 1346 |
* |
| 1347 |
* Display OBS settings |
| 1348 |
* |
| 1349 |
*/ |
| 1350 |
|
| 1351 |
/** |
| 1352 |
* Render the OBS encoder settings panel (server URI + stream key + steps). |
| 1353 |
* Shown by default (display:block). Echoes HTML. |
| 1354 |
* |
| 1355 |
* @param string $obs_uri RTMP server/ingest URI. |
| 1356 |
* @param string $obs_stream RTMP stream key. |
| 1357 |
* @return void |
| 1358 |
*/ |
| 1359 |
public function wpstream_obs_settings($obs_uri,$obs_stream){ |
| 1360 |
|
| 1361 |
print '<div class="external_software_streaming wpstream_obs_settings" style="display:block;">'; |
| 1362 |
|
| 1363 |
// Server URI and stream key, each with a "copy" affordance. |
| 1364 |
print '<div class="external_software_streaming_details">'; |
| 1365 |
print '<div class="event_list_unit_notificationx"><strong>'.esc_html__('Server:').' </strong>'; |
| 1366 |
print '<div class="wpstream_live_uri_text">' . $obs_uri.'</div> |
| 1367 |
<div class="copy_live_uri">'.__('copy','wpstream').'</div>'; |
| 1368 |
|
| 1369 |
print '<div class="event_list_stream_key_wrap"><strong>'.__('Stream Key:').' </strong> |
| 1370 |
<div class="wpstream_live_key_text">'. $obs_stream.'</div><div class="copy_live_key">'.__('copy','wpstream').'</div></div>'; |
| 1371 |
print '</div>'; |
| 1372 |
|
| 1373 |
print'</div>'; |
| 1374 |
|
| 1375 |
print ' <div class="wpstream_modal_explanations">'; |
| 1376 |
print '<ul> |
| 1377 |
<li>1. Click Settings in the OBS Window and then Select Stream.</li> |
| 1378 |
<li>2. Choose Custom Streaming Server in the Stream Type dropdown menu.</li> |
| 1379 |
<li>3. In the URL box, type/paste your Server.</li> |
| 1380 |
<li>4. In the Stream key, type/paste your Stream key.</li> |
| 1381 |
<li>5. Save changes.Close the Settings window and click on the "Start Streaming" button in the main window of OBS.</li></ul>'; |
| 1382 |
|
| 1383 |
print '</div>'; |
| 1384 |
|
| 1385 |
|
| 1386 |
print'</div>'; |
| 1387 |
} |
| 1388 |
|
| 1389 |
/* |
| 1390 |
* |
| 1391 |
* Display StreamYard settings |
| 1392 |
* |
| 1393 |
*/ |
| 1394 |
|
| 1395 |
/** |
| 1396 |
* Render the StreamYard encoder settings panel (RTMP URL + stream key + steps). Echoes HTML. |
| 1397 |
* |
| 1398 |
* @param string $obs_uri RTMP server/ingest URI. |
| 1399 |
* @param string $obs_stream RTMP stream key. |
| 1400 |
* @return void |
| 1401 |
*/ |
| 1402 |
public function wpstream_streamyard_settings($obs_uri,$obs_stream){ |
| 1403 |
|
| 1404 |
print '<div class="external_software_streaming wpstream_streamyard_settings">'; |
| 1405 |
|
| 1406 |
// Server URI and stream key, each with a "copy" affordance. |
| 1407 |
print '<div class="external_software_streaming_details">'; |
| 1408 |
print '<div class="event_list_unit_notificationx"><strong>'.esc_html__('RTMP server URL:').' </strong>'; |
| 1409 |
print '<div class="wpstream_live_uri_text">' . $obs_uri.'</div> |
| 1410 |
<div class="copy_live_uri">'.__('copy','wpstream').'</div>'; |
| 1411 |
|
| 1412 |
print '<div class="event_list_stream_key_wrap"><strong>'.__('Stream key:').' </strong> |
| 1413 |
<div class="wpstream_live_key_text">'. $obs_stream.'</div><div class="copy_live_key">'.__('copy','wpstream').'</div></div>'; |
| 1414 |
print '</div>'; |
| 1415 |
|
| 1416 |
print'</div>'; |
| 1417 |
|
| 1418 |
print ' <div class="wpstream_modal_explanations">'; |
| 1419 |
print '<ul> |
| 1420 |
<li>1. Set up your destination by going to your StreamYard account.</li> |
| 1421 |
<li>2. Choose "Custom RTMP" and add the RTMP server URL and Stream key from WpStream.</li> |
| 1422 |
<li>3. Go to "Broadcasts" then "create a broadcast".</li> |
| 1423 |
<li>4. Enter Live Studio.</li> |
| 1424 |
<li>5. Adjust all your preferred settings and Click on "Go Live".</li></ul>'; |
| 1425 |
|
| 1426 |
print '</div>'; |
| 1427 |
|
| 1428 |
|
| 1429 |
print'</div>'; |
| 1430 |
} |
| 1431 |
|
| 1432 |
|
| 1433 |
|
| 1434 |
/* |
| 1435 |
* |
| 1436 |
* Display StreamYard settings |
| 1437 |
* |
| 1438 |
*/ |
| 1439 |
|
| 1440 |
/** |
| 1441 |
* Render the Restream encoder settings panel (RTMP URL + stream key + steps). Echoes HTML. |
| 1442 |
* |
| 1443 |
* @param string $obs_uri RTMP server/ingest URI. |
| 1444 |
* @param string $obs_stream RTMP stream key. |
| 1445 |
* @return void |
| 1446 |
*/ |
| 1447 |
public function wpstream_restream_settings($obs_uri,$obs_stream){ |
| 1448 |
|
| 1449 |
print '<div class="external_software_streaming wpstream_restream_settings">'; |
| 1450 |
|
| 1451 |
// Server URI and stream key, each with a "copy" affordance. |
| 1452 |
print '<div class="external_software_streaming_details">'; |
| 1453 |
print '<div class="event_list_unit_notificationx"><strong>'.esc_html__('RTMP URL:').' </strong>'; |
| 1454 |
print '<div class="wpstream_live_uri_text">' . $obs_uri.'</div> |
| 1455 |
<div class="copy_live_uri">'.__('copy','wpstream').'</div>'; |
| 1456 |
|
| 1457 |
print '<div class="event_list_stream_key_wrap"><strong>'.__('Stream key:').' </strong> |
| 1458 |
<div class="wpstream_live_key_text">'. $obs_stream.'</div><div class="copy_live_key">'.__('copy','wpstream').'</div></div>'; |
| 1459 |
print '</div>'; |
| 1460 |
|
| 1461 |
print'</div>'; |
| 1462 |
|
| 1463 |
print ' <div class="wpstream_modal_explanations">'; |
| 1464 |
print '<ul> |
| 1465 |
<li>1. Go to your Restream account and set up a destination or channel.</li> |
| 1466 |
<li>2. Choose "Custom RTMP" and add the RTMP URL and Stream Key from WpStream.</li> |
| 1467 |
<li>3. Click on "Add Channel".</li> |
| 1468 |
<li>4. Enter Live Studio.</li> |
| 1469 |
<li>5. Adjust your preferred settings and Go Live.</li></ul>'; |
| 1470 |
|
| 1471 |
print '</div>'; |
| 1472 |
|
| 1473 |
|
| 1474 |
print'</div>'; |
| 1475 |
} |
| 1476 |
|
| 1477 |
|
| 1478 |
|
| 1479 |
/* |
| 1480 |
* |
| 1481 |
* Display Xplit settings |
| 1482 |
* |
| 1483 |
*/ |
| 1484 |
|
| 1485 |
|
| 1486 |
/** |
| 1487 |
* Render the XSplit encoder settings panel (RTMP URL + stream key + steps). |
| 1488 |
* Hidden by default (display:none). Echoes HTML. |
| 1489 |
* |
| 1490 |
* @param string $obs_uri RTMP server/ingest URI. |
| 1491 |
* @param string $obs_stream RTMP stream key. |
| 1492 |
* @return void |
| 1493 |
*/ |
| 1494 |
public function wpstream_xplit_settings($obs_uri,$obs_stream){ |
| 1495 |
print '<div class="external_software_streaming wpstream_xplit_settings" style="display:none;">'; |
| 1496 |
// Server URI and stream key, each with a "copy" affordance. |
| 1497 |
print '<div class="external_software_streaming_details">'; |
| 1498 |
print '<div class="event_list_unit_notificationx"><strong>'.esc_html__('RTMP Url:').' </strong>'; |
| 1499 |
print '<div class="wpstream_live_uri_text">' . $obs_uri.'</div> |
| 1500 |
<div class="copy_live_uri">'.__('copy','wpstream').'</div>'; |
| 1501 |
|
| 1502 |
print '<div class="event_list_stream_key_wrap"><strong>'.__('Stream Key:').' </strong> |
| 1503 |
<div class="wpstream_live_key_text">'. $obs_stream.'</div><div class="copy_live_key">'.__('copy','wpstream').'</div></div>'; |
| 1504 |
print '</div>'; |
| 1505 |
|
| 1506 |
print'</div>'; |
| 1507 |
|
| 1508 |
print ' <div class="wpstream_modal_explanations">'; |
| 1509 |
print '<ul> |
| 1510 |
<li>1. Click Broadcast in the XSplit Window and then click "Set up a new output".</li> |
| 1511 |
<li>2. Choose Custom RTMP in the Set up a new output dropdown menu.</li> |
| 1512 |
<li>3. In the URL box, type/paste your RTMP Url.</li> |
| 1513 |
<li>4. In the Stream key, type/paste your Stream key.</li> |
| 1514 |
<li>5. Save changes and click on the "Stream" button in the main window.</li></ul>'; |
| 1515 |
print '</div>'; |
| 1516 |
|
| 1517 |
print '</div>'; |
| 1518 |
} |
| 1519 |
|
| 1520 |
|
| 1521 |
|
| 1522 |
/* |
| 1523 |
* |
| 1524 |
* Display WireCast settings |
| 1525 |
* |
| 1526 |
*/ |
| 1527 |
|
| 1528 |
|
| 1529 |
/** |
| 1530 |
* Render the Wirecast encoder settings panel (address + stream + steps). |
| 1531 |
* Hidden by default (display:none). Echoes HTML. |
| 1532 |
* |
| 1533 |
* @param string $obs_uri RTMP server/ingest URI (shown as "Address"). |
| 1534 |
* @param string $obs_stream RTMP stream key (shown as "Stream"). |
| 1535 |
* @return void |
| 1536 |
*/ |
| 1537 |
public function wpstream_wirecast_settings($obs_uri,$obs_stream){ |
| 1538 |
print '<div class="external_software_streaming wpstream_wirecast_settings" style="display:none;">'; |
| 1539 |
|
| 1540 |
// Server address and stream, each with a "copy" affordance. |
| 1541 |
print '<div class="external_software_streaming_details">'; |
| 1542 |
print '<div class="event_list_unit_notificationx"><strong>'.esc_html__('Address:').' </strong>'; |
| 1543 |
print '<div class="wpstream_live_uri_text">' . $obs_uri.'</div> |
| 1544 |
<div class="copy_live_uri">'.__('copy','wpstream').'</div>'; |
| 1545 |
|
| 1546 |
print '<div class="event_list_stream_key_wrap"><strong>'.__('Stream:').' </strong> |
| 1547 |
<div class="wpstream_live_key_text">'. $obs_stream.'</div><div class="copy_live_key">'.__('copy','wpstream').'</div></div>'; |
| 1548 |
print '</div>'; |
| 1549 |
|
| 1550 |
print'</div>'; |
| 1551 |
|
| 1552 |
print ' <div class="wpstream_modal_explanations">'; |
| 1553 |
print '<ul> |
| 1554 |
<li>1. Click Output on the top of the screen and then Select Output Settings.</li> |
| 1555 |
<li>2. In the destination, choose RTMP Server and click OK.</li> |
| 1556 |
<li>3. In the Address box, type/paste your Address.</li> |
| 1557 |
<li>4. In the Stream box, type/paste your Stream key.</li> |
| 1558 |
<li>5. Click on OK to save changes just click the Output on the top of the screen and then Start/Stop Broadcasting.</li></ul>'; |
| 1559 |
print '</div>'; |
| 1560 |
|
| 1561 |
print '</div>'; |
| 1562 |
} |
| 1563 |
|
| 1564 |
|
| 1565 |
|
| 1566 |
/* |
| 1567 |
* |
| 1568 |
* Display Larix settings |
| 1569 |
* |
| 1570 |
*/ |
| 1571 |
|
| 1572 |
|
| 1573 |
/** |
| 1574 |
* Render the Larix Broadcaster (mobile) settings panel: a combined RTMP |
| 1575 |
* string, a QR code image (populated by JS), and, on mobile, a deep-link |
| 1576 |
* button to launch Larix. Hidden by default. Echoes HTML. |
| 1577 |
* |
| 1578 |
* @param string $obs_uri RTMP server/ingest URI. |
| 1579 |
* @param string $obs_stream RTMP stream key. |
| 1580 |
* @return void |
| 1581 |
*/ |
| 1582 |
public function wpstream_larix_settings($obs_uri,$obs_stream){ |
| 1583 |
print '<div class="external_software_streaming wpstream_larix_settings" style="display:none;">'; |
| 1584 |
// Larix takes a single combined RTMP URL (server + key). |
| 1585 |
$larix_rtmp=$obs_uri.$obs_stream; |
| 1586 |
// RTMP value (filled in by JS) with a "copy" affordance. |
| 1587 |
print '<div class="external_software_streaming_details">'; |
| 1588 |
print '<div class="event_list_unit_notificationx"><strong>'.esc_html__('RTMP:').'</strong>'; |
| 1589 |
print '<div class="wpstream_live_uri_text wpstream_larix_rtmp"></div> |
| 1590 |
<div class="copy_live_uri">'.__('copy','wpstream').'</div>'; |
| 1591 |
|
| 1592 |
print '</div>'; |
| 1593 |
|
| 1594 |
print'</div>'; |
| 1595 |
|
| 1596 |
// Instructions: scan QR, configure manually, or (mobile only) tap the launch button. |
| 1597 |
print ' <div class="wpstream_modal_explanations">'; |
| 1598 |
print '<ul> |
| 1599 |
<li>A. Scan the QR code.</li> |
| 1600 |
<li>or</li> |
| 1601 |
<li>B. Manually configure Larix with the above RTMP</li>'; |
| 1602 |
if(wp_is_mobile() ){ |
| 1603 |
print ' |
| 1604 |
<li>or</li> |
| 1605 |
<li>C. Click on the button below </li></ul>'; |
| 1606 |
} |
| 1607 |
|
| 1608 |
|
| 1609 |
print '</div>'; |
| 1610 |
|
| 1611 |
|
| 1612 |
// QR image (src set by JS) and, on mobile, the Larix deep-link button. |
| 1613 |
print '<img class="print_qrcode" src="" />'; |
| 1614 |
if(wp_is_mobile() ){ |
| 1615 |
print '<a href="" class="wpstream_start_with_larix_mobile" >Start Streaming with Larix</a>'; |
| 1616 |
} |
| 1617 |
|
| 1618 |
|
| 1619 |
|
| 1620 |
print '</div>'; |
| 1621 |
} |
| 1622 |
|
| 1623 |
|
| 1624 |
|
| 1625 |
|
| 1626 |
/* |
| 1627 |
* |
| 1628 |
* Display Wmix settings |
| 1629 |
* |
| 1630 |
*/ |
| 1631 |
|
| 1632 |
|
| 1633 |
/** |
| 1634 |
* Render the vMix encoder settings panel (URL + stream key + steps). |
| 1635 |
* Hidden by default (display:none). Echoes HTML. |
| 1636 |
* |
| 1637 |
* @param string $obs_uri RTMP server/ingest URI. |
| 1638 |
* @param string $obs_stream RTMP stream key. |
| 1639 |
* @return void |
| 1640 |
*/ |
| 1641 |
public function wpstream_wmix_settings($obs_uri,$obs_stream){ |
| 1642 |
print '<div class="external_software_streaming wpstream_wimx_settings" style="display:none;">'; |
| 1643 |
|
| 1644 |
// Server URL and stream key, each with a "copy" affordance. |
| 1645 |
print '<div class="external_software_streaming_details">'; |
| 1646 |
print '<div class="event_list_unit_notificationx"><strong>'.esc_html__('URL:').' </strong>'; |
| 1647 |
print '<div class="wpstream_live_uri_text">' . $obs_uri.'</div> |
| 1648 |
<div class="copy_live_uri">'.__('copy','wpstream').'</div>'; |
| 1649 |
|
| 1650 |
print '<div class="event_list_stream_key_wrap"><strong>'.__('Stream Key:').' </strong> |
| 1651 |
<div class="wpstream_live_key_text">'. $obs_stream.'</div><div class="copy_live_key">'.__('copy','wpstream').'</div></div>'; |
| 1652 |
print '</div>'; |
| 1653 |
|
| 1654 |
print'</div>'; |
| 1655 |
|
| 1656 |
print ' <div class="wpstream_modal_explanations">'; |
| 1657 |
print '<ul> |
| 1658 |
<li>1. Click on the gear icon near the stream button on the bottom.</li> |
| 1659 |
<li>2. Choose a custom RTMP Server in destination.</li> |
| 1660 |
<li>3. In the URL box, type/paste your URL.</li> |
| 1661 |
<li>4. In the Stream key, type/paste your Stream key.</li> |
| 1662 |
<li>5. Save changes. You can start streaming by clicking on the stream button at the bottom of the dashboard.</li></ul>'; |
| 1663 |
print '</div>'; |
| 1664 |
|
| 1665 |
print '</div>'; |
| 1666 |
} |
| 1667 |
|
| 1668 |
|
| 1669 |
|
| 1670 |
/* |
| 1671 |
* Set Settings |
| 1672 |
* |
| 1673 |
* |
| 1674 |
*/ |
| 1675 |
/** |
| 1676 |
* Render the WpStream Settings admin screen and persist submitted values. |
| 1677 |
* |
| 1678 |
* On POST it verifies the nonce, then stores each field as a `wpstream_*` |
| 1679 |
* option (with special handling for the streamer role and the per-tab |
| 1680 |
* global event options), and flushes rewrite rules. It then builds the |
| 1681 |
* tabbed settings definition array and renders the form. Echoes HTML. |
| 1682 |
* |
| 1683 |
* @return void |
| 1684 |
*/ |
| 1685 |
public function wpstream_settings(){ |
| 1686 |
|
| 1687 |
// Admin-only screen. |
| 1688 |
if( !current_user_can('administrator') ){ |
| 1689 |
die('Only for administrators'); |
| 1690 |
} |
| 1691 |
|
| 1692 |
|
| 1693 |
// Handle a settings submission. |
| 1694 |
if($_SERVER['REQUEST_METHOD'] === 'POST'){ |
| 1695 |
// CSRF protection. |
| 1696 |
if( !wp_verify_nonce($_POST['wpstream-settings-nonce'],'wpstream-settings-nonce') ){ |
| 1697 |
die('Security check'); |
| 1698 |
} |
| 1699 |
|
| 1700 |
$allowed_html = array(); |
| 1701 |
$exclude_array = array(); |
| 1702 |
$allowed_html = array(); |
| 1703 |
|
| 1704 |
|
| 1705 |
|
| 1706 |
// If the "channel type" section was shown but no streamer role was chosen, clear the stored role. |
| 1707 |
if( isset($_POST['user_streaming_channel_type_hidden']) && intval($_POST['user_streaming_channel_type_hidden'])==1 && !isset($_POST['stream_role']) ){ |
| 1708 |
update_option( sanitize_key('wpstream_stream_role'), '' ); |
| 1709 |
} |
| 1710 |
|
| 1711 |
// Persist every posted field (except the submit button) as a sanitized wpstream_* option. |
| 1712 |
foreach($_POST as $variable=>$value){ |
| 1713 |
if ($variable!='submit'){ |
| 1714 |
if (!in_array($variable, $exclude_array) ){ |
| 1715 |
update_option( sanitize_key('wpstream_'.$variable), sanitize_text_field ($value) ); |
| 1716 |
} |
| 1717 |
|
| 1718 |
// Streamer role is stored raw (may be an array of roles) rather than sanitized as text. |
| 1719 |
if($variable=='stream_role'){ |
| 1720 |
update_option( sanitize_key('wpstream_stream_role'), $value ); |
| 1721 |
} |
| 1722 |
|
| 1723 |
} |
| 1724 |
} |
| 1725 |
|
| 1726 |
|
| 1727 |
// On the "default options" tab, fold the per-option checkboxes into a single 1/0 map. |
| 1728 |
if( isset($_GET['tab']) && $_GET['tab']=='default_options' ){ |
| 1729 |
$event_settings=array(); |
| 1730 |
foreach($this->global_event_options as $key=>$option){ |
| 1731 |
$event_settings[$key]=''; |
| 1732 |
if(isset($_POST['wpstream_event_set_'.$key]) && $_POST['wpstream_event_set_'.$key]=='on'){ |
| 1733 |
$event_settings[$key]=1; |
| 1734 |
}else{ |
| 1735 |
$event_settings[$key]=0; |
| 1736 |
} |
| 1737 |
} |
| 1738 |
update_option('wpstream_user_streaming_global_channel_options',$event_settings); |
| 1739 |
} |
| 1740 |
|
| 1741 |
|
| 1742 |
|
| 1743 |
|
| 1744 |
// reset permalinkgs |
| 1745 |
// Slug settings can change permalinks, so drop and rebuild the rewrite rules. |
| 1746 |
global $wp_rewrite; |
| 1747 |
|
| 1748 |
update_option( "rewrite_rules", FALSE ); |
| 1749 |
$wp_rewrite->flush_rules( true ); |
| 1750 |
|
| 1751 |
} |
| 1752 |
|
| 1753 |
// Definition of every settings field, keyed and grouped by 'tab' (general, subscription, messages, default options, VOD defaults, support). |
| 1754 |
$wpstream_settings_array =array( |
| 1755 |
1 => array( |
| 1756 |
'tab' => 'general_options', |
| 1757 |
'label' => esc_html__('Slug for free video/channel pages ','wpstream'), |
| 1758 |
'name' => 'free_media_slug', |
| 1759 |
'type' => 'text', |
| 1760 |
'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'), |
| 1761 |
), |
| 1762 |
|
| 1763 |
'free_vod_slug' => array( |
| 1764 |
'tab' => 'general_options', |
| 1765 |
'label' => esc_html__('Slug for free VOD pages ','wpstream'), |
| 1766 |
'name' => 'free_media_slug_vod', |
| 1767 |
'type' => 'text', |
| 1768 |
'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'), |
| 1769 |
), |
| 1770 |
|
| 1771 |
2 => array( |
| 1772 |
'tab' => 'general_options', |
| 1773 |
'label' => esc_html__('Non-Admin User Roles Allowed to Broadcast','wpstream'), |
| 1774 |
'name' => 'stream_role', |
| 1775 |
'type' => 'user_roles', |
| 1776 |
'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'), |
| 1777 |
|
| 1778 |
), |
| 1779 |
3 => array( |
| 1780 |
'tab' => 'general_options', |
| 1781 |
'label' => esc_html__('Non Admin Streamers Channel Type.','wpstream'), |
| 1782 |
'name' => 'user_streaming_channel_type', |
| 1783 |
'type' => 'select', |
| 1784 |
'select_values'=>array( |
| 1785 |
'free' => esc_html__('Free Live Channel','wpstream'), |
| 1786 |
'paid' => esc_html__('Pay-Per-View','wpstream') |
| 1787 |
), |
| 1788 |
'details' => esc_html__('Choose whether the channels assigned to non-admins are free-for-all or pay-per-view (WooCommerce product).','wpstream'), |
| 1789 |
), |
| 1790 |
|
| 1791 |
4 => array( |
| 1792 |
'tab' => 'general_options', |
| 1793 |
'label' => esc_html__('Default Pay-Per-View Price','wpstream'), |
| 1794 |
'name' => 'user_streaming_default_price', |
| 1795 |
'type' => 'text', |
| 1796 |
'details' => esc_html__('Default price of pay-per-view channels assigned to non-admins.','wpstream'), |
| 1797 |
), |
| 1798 |
|
| 1799 |
|
| 1800 |
|
| 1801 |
6 => array( |
| 1802 |
'tab' => 'subscription_options', |
| 1803 |
'label' => esc_html__('Use Global Subscription Mode','wpstream'), |
| 1804 |
'name' => 'global_sub', |
| 1805 |
'type' => 'slidertoogle', |
| 1806 |
'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'), |
| 1807 |
), |
| 1808 |
|
| 1809 |
7 => array( |
| 1810 |
'tab' => 'subscription_options', |
| 1811 |
'label' => esc_html__('Subscription ID for Global Subscription Mode','wpstream'), |
| 1812 |
'name' => 'global_sub_id', |
| 1813 |
'type' => 'text', |
| 1814 |
'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'), |
| 1815 |
), |
| 1816 |
8 => array( |
| 1817 |
'tab' => 'messages_options', |
| 1818 |
'label' => esc_html__('PPV not logged in message','wpstream'), |
| 1819 |
'name' => 'product_not_login', |
| 1820 |
'type' => 'text', |
| 1821 |
'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'), |
| 1822 |
'default' => esc_html__('You must be logged in to watch this video.','wpstream'), |
| 1823 |
), |
| 1824 |
9 => array( |
| 1825 |
'tab' => 'messages_options', |
| 1826 |
'label' => esc_html__('PPV not purchased message','wpstream'), |
| 1827 |
'name' => 'product_not_bought', |
| 1828 |
'type' => 'text', |
| 1829 |
'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'), |
| 1830 |
'default' => esc_html__('You did not yet purchase this item.','wpstream'), |
| 1831 |
), |
| 1832 |
10 => array( |
| 1833 |
'tab' => 'messages_options', |
| 1834 |
'label' => esc_html__('Subscription not purchased message','wpstream'), |
| 1835 |
'name' => 'product_not_subscribe', |
| 1836 |
'type' => 'text', |
| 1837 |
'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'), |
| 1838 |
'default' => esc_html__(' You did not yet subscribe to this item.','wpstream'), |
| 1839 |
), |
| 1840 |
11 => array( |
| 1841 |
'tab' => 'messages_options', |
| 1842 |
'label' => esc_html__('Thank you message','wpstream'), |
| 1843 |
'name' => 'product_thankyou', |
| 1844 |
'type' => 'text', |
| 1845 |
'details' => esc_html__('This message will be displayed on the thank you page (after purchase) and the confirmation email.','wpstream'), |
| 1846 |
'default' => esc_html__('Thanks for your purchase. You can access your item at any time by visiting the following page: {item_link}','wpstream'), |
| 1847 |
), |
| 1848 |
|
| 1849 |
12 => array( |
| 1850 |
'tab' => 'messages_options', |
| 1851 |
'label' => esc_html__('Subscription Active message','wpstream'), |
| 1852 |
'name' => 'subscription_active', |
| 1853 |
'type' => 'text', |
| 1854 |
'details' => esc_html__('This message will be displayed on subscription product page.','wpstream'), |
| 1855 |
'default' => esc_html__('Your Subscription is Active','wpstream'), |
| 1856 |
), |
| 1857 |
13 => array( |
| 1858 |
'tab' => 'messages_options', |
| 1859 |
'label' => esc_html__('You are not live message','wpstream'), |
| 1860 |
'name' => 'you_are_not_live', |
| 1861 |
'type' => 'text', |
| 1862 |
'details' => esc_html__('This message will be displayed in player.','wpstream'), |
| 1863 |
'default' => esc_html__('We are not live at this moment','wpstream'), |
| 1864 |
), |
| 1865 |
|
| 1866 |
14 => array( |
| 1867 |
'tab' => 'general_options', |
| 1868 |
'label' => esc_html__('Video player theme','wpstream'), |
| 1869 |
'name' => 'video_player_theme', |
| 1870 |
'type' => 'select', |
| 1871 |
'select_values'=>array( |
| 1872 |
'default' => esc_html__('Default','wpstream'), |
| 1873 |
'city' => esc_html__('City','wpstream'), |
| 1874 |
'forest' => esc_html__('Forest','wpstream'), |
| 1875 |
'fantasy' => esc_html__('Fantasy','wpstream'), |
| 1876 |
'sea' => esc_html__('Sea','wpstream'), |
| 1877 |
), |
| 1878 |
'details' => esc_html__('Choose the video player theme to have a different look for the player.','wpstream'), |
| 1879 |
), |
| 1880 |
'wpstream_player_logo' => array( |
| 1881 |
'tab' => 'general_options', |
| 1882 |
'name' => 'player_logo', |
| 1883 |
'label' => esc_html__('Logo for the video player','wpstream'), |
| 1884 |
'type' => 'image', |
| 1885 |
'details' => esc_html__('This logo will be displayed on the the video player.','wpstream'), |
| 1886 |
'default' => '', |
| 1887 |
'image_size' => 'thumbnail', |
| 1888 |
), |
| 1889 |
// hide the video player logo opacity for now |
| 1890 |
// 'wpstream_player_logo_opacity' => array( |
| 1891 |
// 'tab' => 'general_options', |
| 1892 |
// 'name' => 'player_logo_opacity', |
| 1893 |
// 'label' => esc_html__('Opacity of the video player logo','wpstream'), |
| 1894 |
// 'type' => 'range', |
| 1895 |
// 'details' => esc_html__('Set the opacity of the logo','wpstream'), |
| 1896 |
// 'default' => '', |
| 1897 |
// 'image_size' => 'thumbnail', |
| 1898 |
// ), |
| 1899 |
'wpsteram_player_logo_position' => array( |
| 1900 |
'tab' => 'general_options', |
| 1901 |
'name' => 'player_logo_position', |
| 1902 |
'label' => esc_html__('Position of the video player logo','wpstream'), |
| 1903 |
'type' => 'select', |
| 1904 |
'select_values'=>array( |
| 1905 |
'top-left' => esc_html__('Top Left','wpstream'), |
| 1906 |
'top-right' => esc_html__('Top Right','wpstream'), |
| 1907 |
'bottom-left' => esc_html__('Bottom Left','wpstream'), |
| 1908 |
'bottom-right' => esc_html__('Bottom Right','wpstream'), |
| 1909 |
), |
| 1910 |
'details' => esc_html__('Choose the position of the logo on the video player.','wpstream'), |
| 1911 |
'default' => '', |
| 1912 |
), |
| 1913 |
|
| 1914 |
|
| 1915 |
|
| 1916 |
99 => array( |
| 1917 |
'tab' => 'default_options', |
| 1918 |
'label' => esc_html__('Events Options ','wpstream'), |
| 1919 |
'name' => 'user_streaming_global_channel_options', |
| 1920 |
'type' => 'user_streaming_global_channel_options', |
| 1921 |
'details' => esc_html__('Global Options for live events.','wpstream'), |
| 1922 |
), |
| 1923 |
|
| 1924 |
100 => array( |
| 1925 |
'tab' => 'default_options_vod', |
| 1926 |
'label' => esc_html__('Autoplay','wpstream'), |
| 1927 |
'name' => 'vod_autoplay', |
| 1928 |
'type' => 'slidertoogle', |
| 1929 |
'details' => esc_html__('If enabled, video will attempt to start playing automatically. This is only achievable in some browsers.','wpstream'), |
| 1930 |
), |
| 1931 |
|
| 1932 |
|
| 1933 |
101 => array( |
| 1934 |
'tab' => 'default_options_vod', |
| 1935 |
'label' => esc_html__('Start Muted','wpstream'), |
| 1936 |
'name' => 'vod_start_muted', |
| 1937 |
'type' => 'slidertoogle', |
| 1938 |
'details' => esc_html__('If enabled, video will start muted. This may increase the rate of autoplay in some browsers.','wpstream'), |
| 1939 |
), |
| 1940 |
|
| 1941 |
102 => array( |
| 1942 |
'tab' => 'default_options_vod', |
| 1943 |
'label' => esc_html__('Lock To Website','wpstream'), |
| 1944 |
'name' => 'vod_domain_lock', |
| 1945 |
'type' => 'slidertoogle', |
| 1946 |
'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') ), |
| 1947 |
), |
| 1948 |
|
| 1949 |
103 => array( |
| 1950 |
'tab' => 'default_options_vod', |
| 1951 |
'label' => esc_html__('Encrypt Video','wpstream'), |
| 1952 |
'name' => 'vod_encrypt', |
| 1953 |
'type' => 'slidertoogle', |
| 1954 |
'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'), |
| 1955 |
), |
| 1956 |
|
| 1957 |
/* 'vod_domain_lock' =>array( |
| 1958 |
'name' => esc_html__('Video On Demand - Lock To Website','wpstream'), |
| 1959 |
'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') ), |
| 1960 |
'defaults' => 'no', |
| 1961 |
), |
| 1962 |
'vod_encrypt' =>array( |
| 1963 |
'name' => esc_html__('Encrypt Video on Demand','wpstream'), |
| 1964 |
'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'), |
| 1965 |
'defaults' => 'no', |
| 1966 |
),*/ |
| 1967 |
|
| 1968 |
104 => array( |
| 1969 |
'tab' => 'support_tab', |
| 1970 |
'label' => esc_html__('Logs','wpstream'), |
| 1971 |
'name' => 'logs', |
| 1972 |
'type' => 'logs_table', |
| 1973 |
'details' => esc_html__('This is the error log of the plugin.','wpstream'), |
| 1974 |
) |
| 1975 |
|
| 1976 |
); |
| 1977 |
|
| 1978 |
// Determine the active tab (defaults to General Options). |
| 1979 |
$active_tab = 'general_options'; |
| 1980 |
if( isset( $_GET[ 'tab' ] ) ) { |
| 1981 |
$active_tab = $_GET[ 'tab' ]; |
| 1982 |
} |
| 1983 |
|
| 1984 |
|
| 1985 |
// Open the settings panel and the form. |
| 1986 |
print '<div class="theme_options_tab_wpstream" style="display:block;" > |
| 1987 |
<h1>'.__('WpStream Settings','wpstream').'</h1> |
| 1988 |
<form method="post" action="" >'; |
| 1989 |
|
| 1990 |
// Tab navigation bar; the current tab gets the nav-tab-active class. |
| 1991 |
print '<h2 class="nav-tab-wrapper"> |
| 1992 |
<a href="?page=wpstream_settings&tab=general_options" class="nav-tab '; echo $active_tab == 'general_options' ? 'nav-tab-active' : ''; echo '">'.esc_html__('General Options','wpstream').'</a> |
| 1993 |
<a href="?page=wpstream_settings&tab=default_options" class="nav-tab '; echo $active_tab == 'default_options' ? 'nav-tab-active' : ''; echo '">'.esc_html__('Default Channel Settings','wpstream').'</a> |
| 1994 |
<a href="?page=wpstream_settings&tab=default_options_vod" class="nav-tab '; echo $active_tab == 'default_options_vod' ? 'nav-tab-active' : ''; echo '">'.esc_html__('VOD Settings','wpstream').'</a> |
| 1995 |
<a href="?page=wpstream_settings&tab=subscription_options" class="nav-tab '; echo $active_tab == 'subscription_options' ? 'nav-tab-active' : '';echo '">'.esc_html__('Subscription Options','wpstream').'</a> |
| 1996 |
<a href="?page=wpstream_settings&tab=messages_options" class="nav-tab '; echo $active_tab == 'messages_options' ? 'nav-tab-active' : ''; echo '">'.esc_html__('Customize Messages','wpstream').'</a> |
| 1997 |
<a href="?page=wpstream_settings&tab=support_tab" class="nav-tab '; echo $active_tab == 'support_tab' ? 'nav-tab-active' : ''; echo '">'.esc_html__('Support','wpstream').'</a> |
| 1998 |
</h2>'; |
| 1999 |
$help_link=''; |
| 2000 |
|
| 2001 |
// Basic-stream mode disables/annotates some default-channel controls. |
| 2002 |
$is_basic_stream_mode = $this->wpstream_is_basic_streaming_mode(); |
| 2003 |
print '<div class="wpstream_option_wrapper">'; |
| 2004 |
|
| 2005 |
// Pick the contextual "Video Help" docs link for the active tab. |
| 2006 |
switch ($active_tab) { |
| 2007 |
case 'general_options': |
| 2008 |
$help_link='https://docs.wpstream.net/docs/general-settings/'; |
| 2009 |
break; |
| 2010 |
case 'default_options': |
| 2011 |
$help_link='https://docs.wpstream.net/docs/default-channel-settings/'; |
| 2012 |
break; |
| 2013 |
case 'default_options_vod': |
| 2014 |
$help_link='https://docs.wpstream.net/docs/vod-settings/'; |
| 2015 |
break; |
| 2016 |
case 'subscription_options': |
| 2017 |
$help_link='https://docs.wpstream.net/docs/subscription-options/'; |
| 2018 |
break; |
| 2019 |
case 'messages_options': |
| 2020 |
$help_link='https://docs.wpstream.net/docs/customize-messages/'; |
| 2021 |
break; |
| 2022 |
} |
| 2023 |
|
| 2024 |
print '<div class="options_wrapper">'; |
| 2025 |
// Render each field that belongs to the active tab. |
| 2026 |
foreach ($wpstream_settings_array as $key=>$option){ |
| 2027 |
// Skip fields from other tabs. |
| 2028 |
if($option['tab']!=$active_tab){ |
| 2029 |
continue; |
| 2030 |
} |
| 2031 |
|
| 2032 |
// Intro blurb (and basic-mode notice) shown above the global channel options control. |
| 2033 |
if ( $option['type']=='user_streaming_global_channel_options' ) { |
| 2034 |
print '<div class="default-channel-settings-info">'; |
| 2035 |
print esc_html__( 'These settings will apply to newly created channels; existing channels will not change settings if you change them here', 'wpstream'); |
| 2036 |
if ( $is_basic_stream_mode ) { |
| 2037 |
$this->wpstream_basic_stream_mode_message(); |
| 2038 |
} |
| 2039 |
print '</div>'; |
| 2040 |
|
| 2041 |
} |
| 2042 |
print '<div class="wpstream_option">'; |
| 2043 |
// Load this option's stored value. |
| 2044 |
$options_value = get_option('wpstream_'.$option['name']) ; |
| 2045 |
|
| 2046 |
|
| 2047 |
// Render the appropriate control for this field's type. |
| 2048 |
switch( $option['type'] ) { |
| 2049 |
// Multi-select of WordPress user roles allowed to broadcast. |
| 2050 |
case 'user_roles': |
| 2051 |
print '<label for="'.$option['name'].'">'.$option['label'].'</label>'; |
| 2052 |
print $this->wpstream_select_user_roles($option['name'],$options_value); |
| 2053 |
print '<div class="settings_details">'.$option['details'].'</div>'; |
| 2054 |
break; |
| 2055 |
// The grid of default per-event streaming options. |
| 2056 |
case 'user_streaming_global_channel_options': |
| 2057 |
$exclude_array=array(); |
| 2058 |
$this->user_streaming_global_channel_options( |
| 2059 |
$option['name'], |
| 2060 |
$options_value, |
| 2061 |
$exclude_array, |
| 2062 |
$is_basic_stream_mode |
| 2063 |
); |
| 2064 |
break; |
| 2065 |
// Plain text input; falls back to the field's default when unset. |
| 2066 |
case 'text': |
| 2067 |
if($options_value==''){ |
| 2068 |
$options_value=''; |
| 2069 |
if(isset($option['default'])){ |
| 2070 |
$options_value=$option['default']; |
| 2071 |
} |
| 2072 |
} |
| 2073 |
|
| 2074 |
print '<label for="'.$option['name'].'">'.$option['label'].'</label>'; |
| 2075 |
print '<input class="wpstream-text-input-setting" id="'.$option['name'].'" type="'.$option['type'].'" size="36" name="'.$option['name'].'" value="'.esc_attr($options_value).'" />'; |
| 2076 |
print '<div class="settings_details">'.$option['details'].'</div>'; |
| 2077 |
break; |
| 2078 |
// Dropdown built from the field's select_values map; a hidden mirror marks the field as present. |
| 2079 |
case 'select': |
| 2080 |
print '<label for="'.$option['name'].'">'.$option['label'].'</label>'; |
| 2081 |
print '<select id="'.$option['name'].'" name="'.$option['name'].'" >'; |
| 2082 |
foreach($option['select_values'] as $key=>$value){ |
| 2083 |
print '<option value="'.$key.'" '; |
| 2084 |
// Pre-select the currently stored value. |
| 2085 |
if( $key == esc_html($options_value) ){ |
| 2086 |
print ' selected '; |
| 2087 |
} |
| 2088 |
print '>'.$value.'</option>'; |
| 2089 |
} |
| 2090 |
print '</select>'; |
| 2091 |
print '<input type="hidden" name="'.$option['name'].'_hidden" value="1" >'; |
| 2092 |
print '<div class="settings_details">'.$option['details'].'</div>'; |
| 2093 |
break; |
| 2094 |
// On/off switch; a hidden 0-valued twin ensures "off" posts a value. |
| 2095 |
case 'slidertoogle': |
| 2096 |
print '<label for="'.$option['name'].'">'.$option['label'].'</label>'; |
| 2097 |
print '<div style="display: flex; gap: 25px; justify-content: space-between;">'; |
| 2098 |
print '<div class="settings_details">'.$option['details'].'</div>'; |
| 2099 |
print '<label class="wpstream_switch"> |
| 2100 |
<input type="hidden" class="wpstream_event_option_itemc" value="0" name="'.$option['name'].'" > |
| 2101 |
<input type="checkbox" class="wpstream_event_option_itemc" value="1" name="'.$option['name'].'" '; |
| 2102 |
if( intval($options_value) !==0 ){ |
| 2103 |
print ' checked '; |
| 2104 |
} |
| 2105 |
print '> <span class="wpstream_slider round"></span>'; |
| 2106 |
print '</label>'; |
| 2107 |
print '</div>'; |
| 2108 |
break; |
| 2109 |
// Media-library image picker with preview + upload/remove buttons. |
| 2110 |
case 'image': |
| 2111 |
$image_url = $options_value ? esc_url($options_value) : ''; |
| 2112 |
$has_image = !empty($image_url); |
| 2113 |
|
| 2114 |
print '<label for="' . $option['name'] . '">' . $option['label'] . '</label>'; |
| 2115 |
print '<div class="wpstream-image-upload-wrapper">'; |
| 2116 |
print '<input type="hidden" id="' . $option['name'] . '" name="' . $option['name'] . '" value="' . $image_url . '" />'; |
| 2117 |
|
| 2118 |
// Preview area |
| 2119 |
print '<div class="wpstream-image-preview" style="' . (!$has_image ? 'display:none;' : '') . '">'; |
| 2120 |
print '<img src="' . $image_url . '" alt="Preview" />'; |
| 2121 |
print '</div>'; |
| 2122 |
|
| 2123 |
// Upload/remove buttons |
| 2124 |
print '<div class="wpstream-image-upload-buttons">'; |
| 2125 |
print '<button type="button" class="wpstream-upload-image button">' . esc_html__('Upload Image', 'wpstream') . '</button>'; |
| 2126 |
print '<button type="button" class="wpstream-remove-image button" style="' . (!$has_image ? 'display:none;' : '') . '">' . esc_html__('Remove Image', 'wpstream') . '</button>'; |
| 2127 |
print '</div>'; |
| 2128 |
|
| 2129 |
print '</div>'; |
| 2130 |
print '<div class="settings_details">' . $option['details'] . '</div>'; |
| 2131 |
break; |
| 2132 |
// 0-100 range slider. |
| 2133 |
case 'range': |
| 2134 |
print '<label for="'.$option['name'].'">'.$option['label'].'</label>'; |
| 2135 |
print '<input class="wpstream-range-input" type="range" id="'.$option['name'].'" name="'.$option['name'].'" min="0" max="100" step="10" value="'.esc_attr($options_value).'" />'; |
| 2136 |
print '<div class="settings_details">'.$option['details'].'</div>'; |
| 2137 |
break; |
| 2138 |
// Delegates to the Support tab renderer (plugin error log table). |
| 2139 |
case 'logs_table': |
| 2140 |
$this->wpstream_support_tab(); |
| 2141 |
break; |
| 2142 |
} |
| 2143 |
print '</div>'; |
| 2144 |
} |
| 2145 |
print '</div>'; // options wrapper |
| 2146 |
// Contextual help link for tabs that have one. |
| 2147 |
if($help_link!==''){ |
| 2148 |
print '<div class="wpstream_options_help"><a href="'.esc_url($help_link).'" target="_blank" >'.esc_html__('Video Help','wpstream').'</a></div>'; |
| 2149 |
} |
| 2150 |
print '</div>'; |
| 2151 |
|
| 2152 |
|
| 2153 |
// Save button (hidden on the read-only Support tab). |
| 2154 |
if ( $active_tab != 'support_tab') { |
| 2155 |
print '<div class="wpstream-save-settings">'; |
| 2156 |
print '<input type="submit" name="submit" class="wpstream_button wpstream_button_action" value="'.__('Save Changes','wpstream').'" />'; |
| 2157 |
print '<div class="spinner"></div>'; |
| 2158 |
print '</div>'; |
| 2159 |
} |
| 2160 |
|
| 2161 |
// CSRF nonce for the submission handled at the top of this method. |
| 2162 |
print '<input id="wpstream-settings-nonce" name="wpstream-settings-nonce" type="hidden" value="'.wp_create_nonce('wpstream-settings-nonce').'" /> '; |
| 2163 |
print '</form>'; |
| 2164 |
print '</div>'; |
| 2165 |
|
| 2166 |
} |
| 2167 |
|
| 2168 |
/** |
| 2169 |
* Get system information for support tab |
| 2170 |
* |
| 2171 |
* @return array System information |
| 2172 |
*/ |
| 2173 |
private function get_system_info() { |
| 2174 |
global $wp_version; |
| 2175 |
|
| 2176 |
// Environment facts: PHP/WP version, debug flag, memory limit and the plugin version. |
| 2177 |
$php_version = phpversion(); |
| 2178 |
$wp_version_info = $wp_version; |
| 2179 |
$site_debug_mode = (defined('WP_DEBUG') && WP_DEBUG); |
| 2180 |
$wp_memory_limit = WP_MEMORY_LIMIT; |
| 2181 |
|
| 2182 |
$wpstream_version = WPSTREAM_PLUGIN_VERSION; |
| 2183 |
$wpstream_plugin_outdated = false; |
| 2184 |
|
| 2185 |
// Check if plugin is outdated |
| 2186 |
// Mark the plugin outdated if WP's update transient lists an available update for it. |
| 2187 |
$update_plugins = get_site_transient('update_plugins'); |
| 2188 |
if (isset($update_plugins->response['plugin/wpstream.php'])) { |
| 2189 |
$wpstream_plugin_outdated = true; |
| 2190 |
} |
| 2191 |
|
| 2192 |
// Check API status |
| 2193 |
// API is "connected" when a WpStream cloud token is present. |
| 2194 |
$api_status = false; |
| 2195 |
if (method_exists($this->main->wpstream_live_connection, 'wpstream_get_token')) { |
| 2196 |
$token = $this->main->wpstream_live_connection->wpstream_get_token(); |
| 2197 |
$api_status = !empty($token); |
| 2198 |
} |
| 2199 |
|
| 2200 |
// Return the collected diagnostics for the Support tab to render. |
| 2201 |
return array( |
| 2202 |
'php_version' => $php_version, |
| 2203 |
'wp_version' => $wp_version_info, |
| 2204 |
'site_debug_mode' => $site_debug_mode, |
| 2205 |
'wp_memory_limit' => $wp_memory_limit, |
| 2206 |
'wpstream_version' => $wpstream_version, |
| 2207 |
'wpstream_plugin_outdated' => $wpstream_plugin_outdated, |
| 2208 |
'api_status' => $api_status |
| 2209 |
); |
| 2210 |
} |
| 2211 |
|
| 2212 |
/** |
| 2213 |
* Render system information HTML |
| 2214 |
* |
| 2215 |
* Prints the Support-tab diagnostics table (PHP/WP versions, debug mode, |
| 2216 |
* memory limit, plugin version + update button, API connection) with a |
| 2217 |
* warning/OK dashicon per row. Echoes HTML. |
| 2218 |
* |
| 2219 |
* @return void |
| 2220 |
*/ |
| 2221 |
private function render_system_info() { |
| 2222 |
// Pull the current environment diagnostics. |
| 2223 |
$system_info = $this->get_system_info(); |
| 2224 |
// Below: diagnostics table; each row shows a value and a warning/OK indicator based on recommended thresholds. |
| 2225 |
?> |
| 2226 |
|
| 2227 |
<div class="wpstream-system-info"> |
| 2228 |
<h3><?php esc_html_e('System Information', 'wpstream'); ?></h3> |
| 2229 |
<table class="widefat"> |
| 2230 |
<tbody> |
| 2231 |
<tr> |
| 2232 |
<td><strong><?php esc_html_e('PHP Version', 'wpstream'); ?></strong></td> |
| 2233 |
<td><?php echo esc_html($system_info['php_version']); ?></td> |
| 2234 |
<td> |
| 2235 |
<?php if (version_compare($system_info['php_version'], '7.4', '<')): ?> |
| 2236 |
<span class="dashicons dashicons-warning" style="color: #ffb900;"></span> |
| 2237 |
<?php esc_html_e('We recommend PHP 7.4 or higher', 'wpstream'); ?> |
| 2238 |
<?php else: ?> |
| 2239 |
<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span> |
| 2240 |
<?php endif; ?> |
| 2241 |
</td> |
| 2242 |
</tr> |
| 2243 |
<tr> |
| 2244 |
<td><strong><?php esc_html_e('WordPress Version', 'wpstream'); ?></strong></td> |
| 2245 |
<td><?php echo esc_html($system_info['wp_version']); ?></td> |
| 2246 |
<td> |
| 2247 |
<?php if (version_compare($system_info['wp_version'], '5.6', '<')): ?> |
| 2248 |
<span class="dashicons dashicons-warning" style="color: #ffb900;"></span> |
| 2249 |
<?php esc_html_e('We recommend WordPress 5.6 or higher', 'wpstream'); ?> |
| 2250 |
<?php else: ?> |
| 2251 |
<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span> |
| 2252 |
<?php endif; ?> |
| 2253 |
</td> |
| 2254 |
</tr> |
| 2255 |
<tr> |
| 2256 |
<td><strong><?php esc_html_e('WP Debug Mode', 'wpstream'); ?></strong></td> |
| 2257 |
<td><?php echo $system_info['site_debug_mode'] ? esc_html__('Enabled', 'wpstream') : esc_html__('Disabled', 'wpstream'); ?></td> |
| 2258 |
<td> |
| 2259 |
<?php if ($system_info['site_debug_mode']): ?> |
| 2260 |
<span class="dashicons dashicons-info" style="color: #00a0d2;"></span> |
| 2261 |
<?php esc_html_e('Debug mode should be disabled on production sites', 'wpstream'); ?> |
| 2262 |
<?php else: ?> |
| 2263 |
<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span> |
| 2264 |
<?php endif; ?> |
| 2265 |
</td> |
| 2266 |
</tr> |
| 2267 |
<tr> |
| 2268 |
<td><strong><?php esc_html_e('WP Memory Limit', 'wpstream'); ?></strong></td> |
| 2269 |
<td><?php echo esc_html($system_info['wp_memory_limit']); ?></td> |
| 2270 |
<td> |
| 2271 |
<?php |
| 2272 |
$memory_limit = wp_convert_hr_to_bytes($system_info['wp_memory_limit']); |
| 2273 |
if ($memory_limit < 64 * 1024 * 1024): // 64MB |
| 2274 |
?> |
| 2275 |
<span class="dashicons dashicons-warning" style="color: #ffb900;"></span> |
| 2276 |
<?php esc_html_e('We recommend at least 64MB', 'wpstream'); ?> |
| 2277 |
<?php else: ?> |
| 2278 |
<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span> |
| 2279 |
<?php endif; ?> |
| 2280 |
</td> |
| 2281 |
</tr> |
| 2282 |
<tr> |
| 2283 |
<td><strong><?php esc_html_e('WpStream Version', 'wpstream'); ?></strong></td> |
| 2284 |
<td><?php echo esc_html($system_info['wpstream_version']); ?></td> |
| 2285 |
<td style="display: flex; align-items: center; gap: 5px;"> |
| 2286 |
<?php if ($system_info['wpstream_plugin_outdated']): ?> |
| 2287 |
<span class="dashicons dashicons-warning" style="color: #ffb900;"></span> |
| 2288 |
<?php esc_html_e('Update available', 'wpstream'); ?> |
| 2289 |
<div class="update-button-wrapper"> |
| 2290 |
<button class="wpstream-update-plugin-button button button-primary" data-plugin="wpstream/wpstream.php"> |
| 2291 |
<?php esc_html_e('Update Now', 'wpstream'); ?> |
| 2292 |
</button> |
| 2293 |
</div> |
| 2294 |
<?php else: ?> |
| 2295 |
<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span> |
| 2296 |
<?php endif; ?> |
| 2297 |
</td> |
| 2298 |
</tr> |
| 2299 |
<tr> |
| 2300 |
<td><strong><?php esc_html_e('API Connection', 'wpstream'); ?></strong></td> |
| 2301 |
<td><?php echo $system_info['api_status'] ? esc_html__('Connected', 'wpstream') : esc_html__('Disconnected', 'wpstream'); ?></td> |
| 2302 |
<td> |
| 2303 |
<?php if (!$system_info['api_status']): ?> |
| 2304 |
<span class="dashicons dashicons-warning" style="color: #ffb900;"></span> |
| 2305 |
<?php esc_html_e('API connection issue', 'wpstream'); ?> |
| 2306 |
<?php else: ?> |
| 2307 |
<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span> |
| 2308 |
<?php endif; ?> |
| 2309 |
</td> |
| 2310 |
</tr> |
| 2311 |
</tbody> |
| 2312 |
</table> |
| 2313 |
</div> |
| 2314 |
<?php |
| 2315 |
} |
| 2316 |
|
| 2317 |
/** |
| 2318 |
* Render support tab content |
| 2319 |
* |
| 2320 |
* Prints the Support tab: the system-info table, a table of relevant active |
| 2321 |
* plugins with update tooltips, and a table of the plugin's recent logs. |
| 2322 |
* Echoes HTML. |
| 2323 |
* |
| 2324 |
* @return void |
| 2325 |
*/ |
| 2326 |
public function wpstream_support_tab() { |
| 2327 |
// Below: Support tab markup (system info, active plugins table, recent logs table). |
| 2328 |
?> |
| 2329 |
<div class="wrap"> |
| 2330 |
<div class="wpstream-support-tab-root"> |
| 2331 |
<?php $this->render_system_info(); ?> |
| 2332 |
|
| 2333 |
<div class="wpstream-plugins-table-container"> |
| 2334 |
<h3><?php esc_html_e('Active Plugins', 'wpstream'); ?></h3> |
| 2335 |
<table class="widefat wpstream-plugins-table"> |
| 2336 |
<thead> |
| 2337 |
<tr> |
| 2338 |
<th><?php esc_html_e('Plugin', 'wpstream'); ?></th> |
| 2339 |
<th><?php esc_html_e('Version', 'wpstream'); ?></th> |
| 2340 |
</tr> |
| 2341 |
</thead> |
| 2342 |
<tbody> |
| 2343 |
<?php |
| 2344 |
// Build the relevant-plugins list; show a placeholder row when none are found. |
| 2345 |
$plugins_data = $this->wpstream_get_plugins_data(); |
| 2346 |
if (empty($plugins_data)) { |
| 2347 |
echo '<tr><td colspan="3">' . esc_html__('No WPStream plugins found.', 'wpstream') . '</td></tr>'; |
| 2348 |
} else { |
| 2349 |
// One row per plugin; append an "update available" tooltip when a newer version exists. |
| 2350 |
foreach ($plugins_data as $plugin) { |
| 2351 |
echo '<tr>'; |
| 2352 |
echo '<td>' . esc_html($plugin['name']) . '</td>'; |
| 2353 |
echo '<td>'; |
| 2354 |
echo esc_html($plugin['version']); |
| 2355 |
if ( isset($plugin['new_version']) ) { |
| 2356 |
echo '<div class="wpstream-tooltip-container">'; |
| 2357 |
echo '<span class="dashicons dashicons-info wpstream-tooltip" title="' . esc_attr($plugin['new_version']) . '">'; |
| 2358 |
echo '</span>'; |
| 2359 |
echo '<div class="wpstream-custom-tooltip">' . sprintf( |
| 2360 |
esc_html__('A new version is available: %s', 'wpstream'), |
| 2361 |
esc_html($plugin['new_version']) |
| 2362 |
) . '</div>'; |
| 2363 |
echo '</div>'; |
| 2364 |
} |
| 2365 |
echo '</td>'; |
| 2366 |
echo '</tr>'; |
| 2367 |
} |
| 2368 |
} |
| 2369 |
?> |
| 2370 |
</tbody> |
| 2371 |
</table> |
| 2372 |
</div> |
| 2373 |
|
| 2374 |
<div class="wpstream-logs-table-container"> |
| 2375 |
<h3><?php esc_html_e('Recent Logs', 'wpstream'); ?></h3> |
| 2376 |
<table class="widefat wpstream-logs-table"> |
| 2377 |
<thead> |
| 2378 |
<tr> |
| 2379 |
<th><?php esc_html_e('Time', 'wpstream'); ?></th> |
| 2380 |
<th><?php esc_html_e('Type', 'wpstream'); ?></th> |
| 2381 |
<th><?php esc_html_e('Description', 'wpstream'); ?></th> |
| 2382 |
</tr> |
| 2383 |
</thead> |
| 2384 |
<tbody> |
| 2385 |
<?php |
| 2386 |
// Load the stored plugin logs; show a placeholder row when empty. |
| 2387 |
$logs = get_option('wpstream_logs'); |
| 2388 |
if ( !is_array($logs) || empty($logs) ) { |
| 2389 |
echo '<tr><td colspan="3">' . esc_html__('No logs found.', 'wpstream') . '</td></tr>'; |
| 2390 |
} else { |
| 2391 |
// One row per log entry (time, type, description). |
| 2392 |
foreach ($logs as $log) { |
| 2393 |
echo '<tr>'; |
| 2394 |
echo '<td>' . esc_html(date('Y-m-d H:i:s', $log['timestamp'] ) ) . '</td>'; |
| 2395 |
echo '<td>' . esc_html($log['type']) . '</td>'; |
| 2396 |
echo '<td>' . esc_html($log['description']) . '</td>'; |
| 2397 |
echo '</tr>'; |
| 2398 |
} |
| 2399 |
} |
| 2400 |
?> |
| 2401 |
</tbody> |
| 2402 |
</table> |
| 2403 |
</div> |
| 2404 |
</div> |
| 2405 |
</div> |
| 2406 |
<?php |
| 2407 |
} |
| 2408 |
|
| 2409 |
/** |
| 2410 |
* Get plugins data. |
| 2411 |
* |
| 2412 |
* Collects name/version/active/update info for a fixed allow-list of plugins |
| 2413 |
* relevant to WpStream (WpStream, WooCommerce, Meta Box, One Click Demo |
| 2414 |
* Import, Better Messages), used by the Support tab. |
| 2415 |
* |
| 2416 |
* @return array List of plugin info arrays (name, version, path, active, needs_update, [new_version]). |
| 2417 |
*/ |
| 2418 |
public function wpstream_get_plugins_data() { |
| 2419 |
// Ensure get_plugins() is available in non-admin contexts. |
| 2420 |
if (!function_exists('get_plugins')) { |
| 2421 |
require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 2422 |
} |
| 2423 |
|
| 2424 |
// Get all installed plugins |
| 2425 |
$all_plugins = get_plugins(); |
| 2426 |
$update_data = get_site_transient('update_plugins'); |
| 2427 |
$all_plugins_info = []; |
| 2428 |
|
| 2429 |
// We want to get data only for the WpStream plugins |
| 2430 |
$wpstream_plugins = array( |
| 2431 |
'WpStream' => array( |
| 2432 |
'path' => 'wpstream/wpstream.php', |
| 2433 |
), |
| 2434 |
'WooCommerce' => array( |
| 2435 |
'path' => 'woocommerce/woocommerce.php', |
| 2436 |
), |
| 2437 |
'Meta Box' => array( |
| 2438 |
'path' => 'meta-box/meta-box.php', |
| 2439 |
), |
| 2440 |
'One Click Demo Import' => array( |
| 2441 |
'path' => 'one-click-demo-import/one-click-demo-import.php', |
| 2442 |
), |
| 2443 |
'Better Messages' => array( |
| 2444 |
'path' => 'bp-better-messages/bp-better-messages.php', |
| 2445 |
), |
| 2446 |
|
| 2447 |
); |
| 2448 |
|
| 2449 |
// Build an info array for every installed plugin (filtered down afterwards). |
| 2450 |
foreach ($all_plugins as $plugin_path => $plugin_data) { |
| 2451 |
$is_active = is_plugin_active( $plugin_path ); |
| 2452 |
$has_update = isset( $update_data->response[$plugin_path] ); |
| 2453 |
|
| 2454 |
$plugin_info = [ |
| 2455 |
'name' => $plugin_data['Name'], |
| 2456 |
'version' => $plugin_data['Version'], |
| 2457 |
'path' => $plugin_path, |
| 2458 |
'active' => $is_active ? 'Yes' : 'No', |
| 2459 |
'needs_update' => $has_update ? 'Yes' : 'No' |
| 2460 |
]; |
| 2461 |
|
| 2462 |
// Record the offered version when an update is pending. |
| 2463 |
if ($has_update) { |
| 2464 |
$plugin_info['new_version'] = $update_data->response[$plugin_path]->new_version; |
| 2465 |
} |
| 2466 |
|
| 2467 |
$all_plugins_info[] = $plugin_info; |
| 2468 |
} |
| 2469 |
|
| 2470 |
// Filter out the elements from $all_plugins_info that are not in $wpstream_plugins |
| 2471 |
foreach ( $all_plugins_info as $key => $plugin_info ) { |
| 2472 |
// compare $plugin_info['path'] against the path property on each $wpstream_plugins element item |
| 2473 |
// if the path is not in $wpstream_plugins, unset the element |
| 2474 |
if ( !in_array( $plugin_info['path'], array_column( $wpstream_plugins, 'path' ) ) ) { |
| 2475 |
unset( $all_plugins_info[$key] ); |
| 2476 |
} |
| 2477 |
} |
| 2478 |
|
| 2479 |
return $all_plugins_info; |
| 2480 |
} |
| 2481 |
|
| 2482 |
/** |
| 2483 |
* Print a dismissible admin notice when a WpStream plugin update is pending. |
| 2484 |
* |
| 2485 |
* @return void |
| 2486 |
*/ |
| 2487 |
public function wpstream_render_outdated_plugin_notice() { |
| 2488 |
// Only render when WP's update transient lists an update for the plugin. |
| 2489 |
$has_update = get_site_transient('update_plugins'); |
| 2490 |
if (isset($has_update->response['plugin/wpstream.php'])) { |
| 2491 |
// Below: warning notice linking to the WP updates page. |
| 2492 |
?> |
| 2493 |
<div class="notice notice-warning is-dismissible"> |
| 2494 |
<p> |
| 2495 |
<?php |
| 2496 |
printf( |
| 2497 |
/* translators: 1: Link to update page */ |
| 2498 |
esc_html__('A new version of WpStream is available. Please update to the latest version for the best experience. Go to the %1$s to update now.', 'wpstream'), |
| 2499 |
'<a href="' . esc_url(admin_url('update-core.php')) . '">' . esc_html__('updates page', 'wpstream') . '</a>' |
| 2500 |
); |
| 2501 |
?> |
| 2502 |
</p> |
| 2503 |
</div> |
| 2504 |
<?php |
| 2505 |
} |
| 2506 |
} |
| 2507 |
|
| 2508 |
|
| 2509 |
/** |
| 2510 |
* Set user roles |
| 2511 |
* |
| 2512 |
* Render the grid of per-event streaming option switches (from |
| 2513 |
* $global_event_options). Reused for both the site-wide default settings |
| 2514 |
* and the per-channel settings modal. Echoes HTML. |
| 2515 |
* |
| 2516 |
* @param string $name Field name prefix/context (unused in body; kept for callers). |
| 2517 |
* @param array|string $value Stored 1/0 map of option states (empty falls back to defaults). |
| 2518 |
* @param array|string $local_array Option keys to EXCLUDE from this render (per-channel context). |
| 2519 |
* @param bool $disabled True to render the switches disabled. |
| 2520 |
* @param bool $is_basic_stream_mode True to disable switches for basic-streaming accounts. |
| 2521 |
* @return void |
| 2522 |
*/ |
| 2523 |
public function user_streaming_global_channel_options( |
| 2524 |
$name, |
| 2525 |
$value, |
| 2526 |
$local_array='', |
| 2527 |
$disabled = false, |
| 2528 |
$is_basic_stream_mode = false |
| 2529 |
) { |
| 2530 |
|
| 2531 |
// Render one switch per defined global event option. |
| 2532 |
foreach($this->global_event_options as $key=>$option){ |
| 2533 |
|
| 2534 |
// Skip options listed in the exclude array (used by the per-channel modal). |
| 2535 |
if( is_array($local_array) && !in_array($key,$local_array)){ |
| 2536 |
print '<div class="wpstream_setting_event_unit_wrapper wpstream-setting-'.esc_attr($key).' ">'; |
| 2537 |
|
| 2538 |
print '<label for="'.$option['name'].'">'.$option['name'].'</label>'; |
| 2539 |
|
| 2540 |
print '<div style="display: flex; gap: 25px; justify-content: space-between;">'; |
| 2541 |
print '<div class="settings_details">'.$option['details'].'</div>'; |
| 2542 |
print ' |
| 2543 |
<label class="wpstream_switch"> |
| 2544 |
<input type="checkbox" class="wpstream_event_option_item" data-attr-ajaxname="'.esc_attr($key).'" name="wpstream_event_set_'.esc_attr($key).'" '; |
| 2545 |
// Check state: use the stored value if present, otherwise the option's default. |
| 2546 |
if( isset($value[$key]) ){ |
| 2547 |
if( intval($value[$key]) !==0 ){ |
| 2548 |
print ' checked '; |
| 2549 |
} |
| 2550 |
}else{ |
| 2551 |
if($option['defaults']=='yes') { |
| 2552 |
print ' checked '; |
| 2553 |
} |
| 2554 |
} |
| 2555 |
// Disable the switch when requested or in basic-streaming mode. |
| 2556 |
if ( $disabled || $is_basic_stream_mode ) { |
| 2557 |
print ' disabled '; |
| 2558 |
} |
| 2559 |
|
| 2560 |
|
| 2561 |
print '> <span class="wpstream_slider round"></span>'; |
| 2562 |
print '</label>'; |
| 2563 |
print '</div>'; |
| 2564 |
|
| 2565 |
|
| 2566 |
print '</div>'; |
| 2567 |
} |
| 2568 |
} |
| 2569 |
|
| 2570 |
|
| 2571 |
} |
| 2572 |
|
| 2573 |
|
| 2574 |
|
| 2575 |
|
| 2576 |
|
| 2577 |
|
| 2578 |
|
| 2579 |
|
| 2580 |
|
| 2581 |
/* |
| 2582 |
* Set user roles |
| 2583 |
* |
| 2584 |
* @since 3.0.1 |
| 2585 |
*/ |
| 2586 |
/** |
| 2587 |
* Build a multi-select of editable user roles (administrator excluded) |
| 2588 |
* for the "roles allowed to broadcast" setting. |
| 2589 |
* |
| 2590 |
* @param string $name Field name (rendered as name="{name}[]"). |
| 2591 |
* @param array|string $value Currently selected role keys ('' becomes an empty array). |
| 2592 |
* @return string HTML <select> markup. |
| 2593 |
*/ |
| 2594 |
public function wpstream_select_user_roles($name,$value){ |
| 2595 |
// Normalize an empty stored value to an array for in_array() below. |
| 2596 |
if($value==''){ |
| 2597 |
$value=array(); |
| 2598 |
} |
| 2599 |
|
| 2600 |
// All editable roles minus administrator (admins always can broadcast). |
| 2601 |
$roles = get_editable_roles(); |
| 2602 |
$return = '<select id="wpstream_user_roles" name="'.esc_html($name).'[]" multiple>'; |
| 2603 |
unset( $roles['administrator'] ); |
| 2604 |
|
| 2605 |
// One option per role, pre-selecting those already chosen. |
| 2606 |
foreach ($roles as $key=>$role){ |
| 2607 |
$return .= '<option value="'.$key.'" '; |
| 2608 |
if( in_array($key, $value) ){ |
| 2609 |
$return .= ' selected '; |
| 2610 |
} |
| 2611 |
$return .= '>'.$role['name'].'</option>'; |
| 2612 |
} |
| 2613 |
$return .= '</select>'; |
| 2614 |
|
| 2615 |
|
| 2616 |
return $return; |
| 2617 |
} |
| 2618 |
|
| 2619 |
|
| 2620 |
/* |
| 2621 |
* Set credential admin function |
| 2622 |
* |
| 2623 |
* @since 3.0.1 |
| 2624 |
*/ |
| 2625 |
/** |
| 2626 |
* Render the Credentials admin screen and save submitted API credentials. |
| 2627 |
* |
| 2628 |
* On POST it stores the WpStream username/password options and clears the |
| 2629 |
* cached token/quota transients so the next request re-authenticates. It |
| 2630 |
* then shows the connection status and the credentials form. Echoes HTML. |
| 2631 |
* |
| 2632 |
* @return void |
| 2633 |
*/ |
| 2634 |
public function wpstream_set_wpstream_credentials(){ |
| 2635 |
|
| 2636 |
// Handle a credentials submission. |
| 2637 |
if($_SERVER['REQUEST_METHOD'] === 'POST'){ |
| 2638 |
$allowed_html = array(); |
| 2639 |
$exclude_array = array(); |
| 2640 |
$allowed_html = array(); |
| 2641 |
|
| 2642 |
// Persist only the recognised credential fields. |
| 2643 |
foreach($_POST as $variable=>$value){ |
| 2644 |
if ($variable!='submit'){ |
| 2645 |
if (!in_array($variable, $exclude_array) ){ |
| 2646 |
switch ( $variable ) { |
| 2647 |
case 'api_username': |
| 2648 |
// Username is sanitized as text. |
| 2649 |
update_option( sanitize_key('wpstream_api_username'), sanitize_text_field($value) ); |
| 2650 |
break; |
| 2651 |
case 'api_password': |
| 2652 |
// Password stored raw (may contain characters sanitization would strip). |
| 2653 |
update_option( sanitize_key('wpstream_api_password'), $value ); |
| 2654 |
break; |
| 2655 |
} |
| 2656 |
} |
| 2657 |
} |
| 2658 |
} |
| 2659 |
|
| 2660 |
|
| 2661 |
|
| 2662 |
// Invalidate any cached token/quota so the new credentials are used immediately. |
| 2663 |
update_option('wp_estate_token_expire',0); |
| 2664 |
update_option('wp_estate_curent_token',' '); |
| 2665 |
delete_transient( 'wpstream_token_api'); |
| 2666 |
delete_transient('wpstream_token_request_30'); |
| 2667 |
delete_transient('wpstream_request_pack_data_per_user_transient'); |
| 2668 |
} |
| 2669 |
|
| 2670 |
|
| 2671 |
$allowed_html = array(); |
| 2672 |
|
| 2673 |
|
| 2674 |
// Field definitions for the credentials form (username + password). |
| 2675 |
$wpstream_options_array =array( |
| 2676 |
2 => array( |
| 2677 |
'label' => 'WpStream.net Username or Email', |
| 2678 |
'name' => 'api_username', |
| 2679 |
'type' => 'text', |
| 2680 |
), |
| 2681 |
3 => array( |
| 2682 |
'label' => 'WpStream.net Password', |
| 2683 |
'name' => 'api_password', |
| 2684 |
'type' => 'password', |
| 2685 |
), |
| 2686 |
|
| 2687 |
); |
| 2688 |
|
| 2689 |
|
| 2690 |
// Current auth token and quota data drive the connection-status banner below. |
| 2691 |
$token = $this->main->wpstream_live_connection->wpstream_get_token(); |
| 2692 |
$pack_details = $this->main->quota_manager->get_live_quota_data( 'wpstream_set_wpstream_credentials' ); |
| 2693 |
|
| 2694 |
$this->main->show_user_data($pack_details); |
| 2695 |
|
| 2696 |
print '<form method="post" action="" >'; |
| 2697 |
print '<div class="theme_options_tab_wpstream" style="display:block;" > |
| 2698 |
<h1>'.__('WpStream Credentials','wpstream').'</h1>'; |
| 2699 |
|
| 2700 |
// Connection status banner: no credentials, bad credentials, connected, or CURL failure. |
| 2701 |
if( get_option('wpstream_api_username')=='' || get_option('wpstream_api_password')== '' ){ |
| 2702 |
echo '<div class="api_not_conected wpstream_orange">'; |
| 2703 |
$admin_url_onboard=get_admin_url().'admin.php?page=wpstream_onboard'; |
| 2704 |
printf ( __('To connect your plugin, enter your WpStream credentials below or go <a href="%s" target="_blank">here</a> to create an account.','wpstream'),$admin_url_onboard); |
| 2705 |
echo '</div>'; |
| 2706 |
|
| 2707 |
}else if($token==''){ |
| 2708 |
$text = get_option('wpstream_curl_failed') === "0" ? |
| 2709 |
' Incorrect username or password. Please check your credentials or go <a href="https://wpstream.net/my-account/edit-account/" target="_blank">here</a> to reset your password.' : |
| 2710 |
'Not connected to WpStream. Please note the errors above and contact support.'; |
| 2711 |
echo '<div class="api_not_conected">'.__($text,'wpstream').'</div>'; |
| 2712 |
}else if( $this->main->wpstream_live_connection->wpstream_client_check_api_status() ){ |
| 2713 |
echo '<div class="api_conected">'.__('Connected to WpStream.net!','wpstream').'</div>'; |
| 2714 |
}else{ |
| 2715 |
echo '<div class="api_not_conected wpstream_brown">'.__('Failed to connect to WpStream.net. Please address CURL connectivity with your hosting provider.','wpstream').'</div>'; |
| 2716 |
} |
| 2717 |
// Render each credential input pre-filled with its stored value. |
| 2718 |
print '<div class="wpstream_option_wrapper">'; |
| 2719 |
foreach ($wpstream_options_array as $key=>$option){ |
| 2720 |
print '<div class="wpstream_option">'; |
| 2721 |
|
| 2722 |
$options_value = esc_html( get_option('wpstream_'.$option['name'],'') ); |
| 2723 |
print '<label for="'.$option['name'].'">'.$option['label'].'</label>'; |
| 2724 |
print '<input id="'.$option['name'].'" type="'.$option['type'].'" size="36" name="'.$option['name'].'" value="'.esc_html($options_value).'" />'; |
| 2725 |
|
| 2726 |
print '</div>'; |
| 2727 |
} |
| 2728 |
print '</div>'; |
| 2729 |
|
| 2730 |
|
| 2731 |
print '<input type="submit" name="submit" class="wpstream_button wpstream_button_action" value="'.__('Save Changes','wpstream').'" />'; |
| 2732 |
|
| 2733 |
print '<h3>Video Tutorials</h3>'; |
| 2734 |
|
| 2735 |
print '<a class="how_to_videos" target="_blank" href="https://youtu.be/9DQrxsKcpmQ">How to Live Stream to WordPress with OBS</a>'; |
| 2736 |
print '<a class="how_to_videos" target="_blank" href="https://youtu.be/qMSjJCskAfM">How to Live Stream to WordPress in less than 3 Minutes</a>'; |
| 2737 |
print '<a class="how_to_videos" target="_blank" href="https://youtu.be/h6myD_vhKcg">How to Live-Stream to WordPress using your iPhone</a>'; |
| 2738 |
|
| 2739 |
print '<a style="margin-top:10px;" href="https://www.youtube.com/channel/UCIjItiJc4Z7aJApj3W6ArJA" target="_blank" class="how_to_videos">More Tutorials On Our YouTube Channel</a>'; |
| 2740 |
|
| 2741 |
|
| 2742 |
print '</div>'; |
| 2743 |
print '</form>'; |
| 2744 |
|
| 2745 |
// Quick-action links: create free / paid channel, or jump to the channels list. |
| 2746 |
print '<div class="theme_options_tab_wpstream" style="display:block;" >'; |
| 2747 |
$link_new = admin_url('admin.php?page=wpstream_live_channels'); |
| 2748 |
$link_new_paid = admin_url('post-new.php?post_type=product').'&new_stream='. rawurlencode('new'); |
| 2749 |
$link_new_free = admin_url('post-new.php?post_type=wpstream_product'); |
| 2750 |
|
| 2751 |
|
| 2752 |
print '<a href="'.esc_url($link_new_free).'" class="wpstream_no_chanel_add_channel">'.esc_html__('Create new Free-To-View channel','wpstream').'</a>'; |
| 2753 |
print '<a href="'.esc_url($link_new_paid).'" class="wpstream_no_chanel_add_channel">'.esc_html__('Create Pay-Per-View channel','wpstream').'</a>'; |
| 2754 |
print '<a href="'.esc_url($link_new).'" class="wpstream_no_chanel_add_channel">'.esc_html('My Channels','wpstream').'</a>'; |
| 2755 |
print '</div>'; |
| 2756 |
|
| 2757 |
|
| 2758 |
} |
| 2759 |
|
| 2760 |
|
| 2761 |
|
| 2762 |
/** |
| 2763 |
* Media Management |
| 2764 |
* |
| 2765 |
* Render the Recordings admin screen: the quota summary, the upload widget, |
| 2766 |
* and the list of existing recordings. Echoes HTML. |
| 2767 |
* |
| 2768 |
* @since 3.0.1 |
| 2769 |
* @return void |
| 2770 |
*/ |
| 2771 |
public function wpstream_media_management(){ |
| 2772 |
// Storage/streaming quota data for the summary header. |
| 2773 |
$pack_details = $this->main->quota_manager->get_live_quota_data( 'wpstream_media_management' ); |
| 2774 |
|
| 2775 |
$this->main->show_user_data($pack_details); |
| 2776 |
|
| 2777 |
|
| 2778 |
// Upload widget section. |
| 2779 |
print '<div id="wpstream_media_upload"><h3>'.__('Upload New Recording','wpstream').'</h3>'.$this->wpstream_present_media_upload().'</div>'; |
| 2780 |
|
| 2781 |
// Existing recordings list section. |
| 2782 |
print '<div id="wpstream_file_management"><h3 id="video_management_title">'.__('Your Recordings','wpstream').'</h3>'.$this->wpstream_present_file_management().'</div>'; |
| 2783 |
|
| 2784 |
|
| 2785 |
} |
| 2786 |
|
| 2787 |
|
| 2788 |
|
| 2789 |
|
| 2790 |
/** |
| 2791 |
* |
| 2792 |
* |
| 2793 |
* WpStream Pagination |
| 2794 |
* |
| 2795 |
* @since 3.0.1 |
| 2796 |
* |
| 2797 |
* |
| 2798 |
*/ |
| 2799 |
|
| 2800 |
/** |
| 2801 |
* Build a numeric pager (first/prev/window/next/last) for list screens. |
| 2802 |
* |
| 2803 |
* @param int $pages Total number of pages. |
| 2804 |
* @param int $range How many page links to show on each side of the current page. |
| 2805 |
* @return string Pager HTML, or '' when there is a single page / no pages. |
| 2806 |
*/ |
| 2807 |
public function wpstream_pagination($pages , $range = 2) { |
| 2808 |
$return=''; |
| 2809 |
// Total visible window width around the current page. |
| 2810 |
$showitems = ($range * 2) + 1; |
| 2811 |
// Current page from the query string (defaults to 1). |
| 2812 |
$paged = ( isset( $_GET['paged'] ) ) ? intval($_GET['paged']) : 1; |
| 2813 |
|
| 2814 |
|
| 2815 |
// Only render a pager when there is more than one page. |
| 2816 |
if (1 != $pages && $pages != 0) { |
| 2817 |
$return.= '<ul class="pagination wpstream_pagination">'; |
| 2818 |
// "Previous" arrow. |
| 2819 |
$return.= "<li class=\"roundleft\"><a href='" . get_pagenum_link($paged - 1) . "'><</a></li>"; |
| 2820 |
|
| 2821 |
$last_page = get_pagenum_link($pages); |
| 2822 |
// Emit page-number links, but only those within the visible window (or all if they fit). |
| 2823 |
for ($i = 1; $i <= $pages; $i++) { |
| 2824 |
if (1 != $pages && (!($i >= $paged + $range + 1 || $i <= $paged - $range - 1) || $pages <= $showitems )) { |
| 2825 |
if ($paged == $i) { |
| 2826 |
// Current page marker. |
| 2827 |
$return.= '<li class="active"><a href="' . esc_url(get_pagenum_link($i)) . '" >' . $i . '</a><li>'; |
| 2828 |
} else { |
| 2829 |
$return.= '<li><a href="' . esc_url(get_pagenum_link($i)) . '" >' . $i . '</a><li>'; |
| 2830 |
} |
| 2831 |
} |
| 2832 |
} |
| 2833 |
|
| 2834 |
// "Next" target, clamped to the last page. |
| 2835 |
$prev_page = get_pagenum_link($paged + 1); |
| 2836 |
if (($paged + 1) > $pages) { |
| 2837 |
$prev_page = get_pagenum_link($paged); |
| 2838 |
} else { |
| 2839 |
$prev_page = get_pagenum_link($paged + 1); |
| 2840 |
} |
| 2841 |
|
| 2842 |
|
| 2843 |
// "Next" and "Last" arrows, then close the list. |
| 2844 |
$return.= "<li class=\"roundright\"><a href='" . $prev_page . "'>></a><li>"; |
| 2845 |
$return.= "<li class=\"roundright\"><a href='" . $last_page . "'>>><li>"; |
| 2846 |
$return.= "</ul>"; |
| 2847 |
} |
| 2848 |
return $return; |
| 2849 |
} |
| 2850 |
|
| 2851 |
|
| 2852 |
|
| 2853 |
|
| 2854 |
|
| 2855 |
|
| 2856 |
/** |
| 2857 |
* Media upload |
| 2858 |
* |
| 2859 |
* Build the S3 direct-upload widget for new recordings: checks storage |
| 2860 |
* quota and API connectivity, then renders a multipart form pre-populated |
| 2861 |
* with the signed S3 upload fields. |
| 2862 |
* |
| 2863 |
* @since 3.0.1 |
| 2864 |
* @return string Upload widget HTML, or an alert/notice when unavailable. |
| 2865 |
*/ |
| 2866 |
public function wpstream_present_media_upload(){ |
| 2867 |
$to_return=''; |
| 2868 |
|
| 2869 |
// Refuse when the account is out of storage/data quota. |
| 2870 |
if ( ! $this->main->quota_manager->has_storage_quota( null, 'recordings_screen' ) ) { |
| 2871 |
return '<div class="wpstream_upload_alert">'.esc_html__('You don\'t have enough cloud storage or data to upload a new item. Please delete some videos or upgrade your plan.','wpstream').'</div>'; |
| 2872 |
} |
| 2873 |
|
| 2874 |
// Request the signed S3 POST fields from the cloud API. |
| 2875 |
$formInputs=$this->main->wpstream_live_connection->wpstream_get_signed_form_upload_data(); |
| 2876 |
|
| 2877 |
// On failure, show either a "not connected" or an "out of quota" message. |
| 2878 |
if( !$formInputs['success'] ){ |
| 2879 |
if ($formInputs['error'] == 'not_connected'){ |
| 2880 |
$to_return.='<div class="wpstream_upload_container">'.esc_html__('Not connected. Please connect to WpStream to upload videos.','wpstream').'</div>'; |
| 2881 |
} |
| 2882 |
else { |
| 2883 |
$to_return.='<div class="wpstream_upload_alert">'.esc_html__('You don\'t have enough cloud storage and data to upload a new item. Please delete some videos or upgrade your plan.','wpstream').'</div>'; |
| 2884 |
} |
| 2885 |
return $to_return; |
| 2886 |
} |
| 2887 |
|
| 2888 |
// Signed data obtained: render the direct-to-S3 upload form. |
| 2889 |
if($formInputs['success'] ===true){ |
| 2890 |
|
| 2891 |
|
| 2892 |
|
| 2893 |
$to_return.='<div class="wpstream_upload_container">'; |
| 2894 |
$to_return.='<div id="wpstream_uploaded_mes">'.esc_html__('Please select or drop a video file. Do not close this window during the upload!','wpstream').'</div>'; |
| 2895 |
$to_return.='<form action="https://wpstream-video.s3.amazonaws.com/" |
| 2896 |
method="POST" |
| 2897 |
enctype="multipart/form-data" |
| 2898 |
data-singleFileUploads="true" |
| 2899 |
data-limitMultiFileUploads="1" |
| 2900 |
data-limitConcurrentUploads="1" |
| 2901 |
class="direct-upload">'; |
| 2902 |
|
| 2903 |
$to_return.='<input id="wpstream_upload" type="file" class="inputfile inputfile-1" value="Pick a video file" name="file" multiple>'; |
| 2904 |
$to_return.='<label for="wpstream_upload"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" viewBox="0 0 20 17"><path d="M10 0l-5.2 4.9h3.3v5.1h3.8v-5.1h3.3l-5.2-4.9zm9.3 11.5l-3.2-2.1h-2l3.4 2.6h-3.5c-.1 0-.2.1-.2.1l-.8 2.3h-6l-.8-2.2c-.1-.1-.1-.2-.2-.2h-3.6l3.4-2.6h-2l-3.2 2.1c-.4.3-.7 1-.6 1.5l.6 3.1c.1.5.7.9 1.2.9h16.3c.6 0 1.1-.4 1.3-.9l.6-3.1c.1-.5-.2-1.2-.7-1.5z"/></svg> <span id="wpstream_label_action">' . esc_html__('Choose a file…','wpstream') . '</span></label>'; |
| 2905 |
|
| 2906 |
|
| 2907 |
$to_return.='<div class="wpstream_file_drop_color">'; |
| 2908 |
$to_return.='<div class="wpstream_form_ex">'.esc_html__('Drop a video file here!','wpstream').'</div>'; |
| 2909 |
$to_return.='<div class="wpstream_form_ex_details">'.__('The Video File must be encoded with the following settings:<br> |
| 2910 |
|
| 2911 |
Container: <strong>MP4</strong>,<br> |
| 2912 |
Video codec: <strong>H264</strong>,<br> |
| 2913 |
Audio codec: <strong>AAC</strong>.<br> |
| 2914 |
Media will fail to play if it does not follow the above settings. |
| 2915 |
You may use a tool like MediaInfo to verify your file. Also you may convert it with specialized software like HandBrake.','wpstream').'<strong> '.esc_html__('Accepted file extensions: .mp4, .mov','wpstream').'</strong></div>'; |
| 2916 |
// Inject each signed S3 field as a hidden input so the browser POST is authorized. |
| 2917 |
if(is_array($formInputs)){ |
| 2918 |
foreach ($formInputs['ref'] as $name => $value) { |
| 2919 |
$to_return.='<input type="hidden" name="'. $name.'" value="'.$value.'">'; |
| 2920 |
} |
| 2921 |
} |
| 2922 |
|
| 2923 |
$to_return.=' |
| 2924 |
<div class="progress-bar-area"></div></div> |
| 2925 |
</form>'; |
| 2926 |
|
| 2927 |
$to_return.='</div>'; |
| 2928 |
} |
| 2929 |
|
| 2930 |
return $to_return; |
| 2931 |
|
| 2932 |
} |
| 2933 |
|
| 2934 |
|
| 2935 |
|
| 2936 |
|
| 2937 |
|
| 2938 |
/** |
| 2939 |
* Display movie list |
| 2940 |
* |
| 2941 |
* Fetch the account's recordings from the cloud API and render them: |
| 2942 |
* still-processing ("pending") items first, then completed items with |
| 2943 |
* delete/download links and "create VOD from this recording" actions. |
| 2944 |
* |
| 2945 |
* @since 3.0.1 |
| 2946 |
* @return string Recordings list HTML, or a notice when not connected / empty. |
| 2947 |
*/ |
| 2948 |
public function wpstream_present_file_management(){ |
| 2949 |
// Pull the raw recordings payload from the API. |
| 2950 |
$video_list_raw = $this->main->wpstream_live_connection->wpstream_get_videos_from_api(); |
| 2951 |
|
| 2952 |
// false means the API call failed / not connected. |
| 2953 |
if ( $video_list_raw === false ) { |
| 2954 |
return '<div class="wpstream_upload_container">'.esc_html__('Not connected. Please connect to WpStream to upload videos.','wpstream').'</div>'; |
| 2955 |
} |
| 2956 |
|
| 2957 |
// Normalise the completed-items list. |
| 2958 |
$video_list_raw_array = []; |
| 2959 |
if( isset( $video_list_raw['items'] ) ){ |
| 2960 |
$video_list_raw_array = $video_list_raw['items']; |
| 2961 |
} |
| 2962 |
|
| 2963 |
// Sort completed items newest-first by their 'time' field. |
| 2964 |
$keys = array_column( $video_list_raw_array, 'time' ); |
| 2965 |
array_multisort($keys, SORT_DESC , $video_list_raw_array); |
| 2966 |
|
| 2967 |
$to_return=''; |
| 2968 |
|
| 2969 |
// show pending items |
| 2970 |
// Render still-processing uploads with a "processing" badge. |
| 2971 |
if ( key_exists( 'pending', $video_list_raw ) && is_array( $video_list_raw['pending'] ) ) { |
| 2972 |
foreach ( $video_list_raw['pending'] as $key => $video ) { |
| 2973 |
$video_size = intval($video['size']/1048576); |
| 2974 |
$video_name = esc_html($video['name']); |
| 2975 |
if($video_name!=''): |
| 2976 |
$to_return.='<div class="wpstream_video_wrapper">'; |
| 2977 |
$to_return.='<div class="wpstream_video_title">'; |
| 2978 |
$to_return.='<div class="wpstream_video_notice"></div></div>'; |
| 2979 |
$to_return.='<div class="wpstream_video_title"><strong class="storage_file_name">'.esc_html__('File Name :','wpstream').'</strong>'.'<span class="storage_file_name_real">'.$video_name.'</span><span class="storage_file_size">'.$video_size.' MB </span></div>'; |
| 2980 |
$to_return.='<div class="wpstream_video_pending">' . esc_html__( 'The video is still processing', 'wpstream') . '</div>'; |
| 2981 |
$to_return.='</div>'; |
| 2982 |
endif; |
| 2983 |
|
| 2984 |
} |
| 2985 |
} |
| 2986 |
|
| 2987 |
// show uploaded items |
| 2988 |
// Render each completed recording with its size, delete/download controls and VOD-creation links. |
| 2989 |
if( is_array($video_list_raw['items'] ) ) { |
| 2990 |
foreach ($video_list_raw_array as $key =>$video){ |
| 2991 |
// Size in MB and the (escaped) file name. |
| 2992 |
$video_size = intval($video['size']/1048576); |
| 2993 |
$video_name = esc_html($video['name']); |
| 2994 |
if($video_name!=''): |
| 2995 |
$to_return.='<div class="wpstream_video_wrapper">'; |
| 2996 |
|
| 2997 |
$to_return.='<div class="wpstream_video_title">'; |
| 2998 |
$to_return.='<div class="wpstream_video_notice"></div></div>'; |
| 2999 |
$to_return.='<div class="wpstream_video_title"><strong class="storage_file_name">'.esc_html__('File Name:','wpstream').'</strong>'.'<span class="storage_file_name_real">'.$video_name.'</span><span class="storage_file_size">'.$video_size.' MB </span></div>'; |
| 3000 |
// Delete control with a JS confirm prompt. |
| 3001 |
$to_return.=' <div class="wpstream_delete_media" '; |
| 3002 |
$to_return.=' onclick="return confirm(\' Are you sure you wish to delete '.$video_name.'?\')" data-filename="'.$video_name.'">'.esc_html__('delete file','wpstream').'</div>'; |
| 3003 |
// Download trigger (JS fetches a time-limited signed URL) and the resulting link. |
| 3004 |
$to_return.='<div class="wpstream_get_download_link" data-filename="'.$video_name.'">'.esc_html__('download','wpstream').'</div>'; |
| 3005 |
$to_return.='<a href="" class="wpstream_download_link">'.esc_html__('Click to download! The url will work for the next 20 minutes!','wpstream').'</a>'; |
| 3006 |
|
| 3007 |
// Pre-fill new-VOD links (free and, if WooCommerce present, paid) with this recording's name. |
| 3008 |
$add_free_video_url=admin_url('post-new.php?post_type=wpstream_product_vod').'&new_video_name='. rawurlencode($video_name); |
| 3009 |
$add_paid_video_url=admin_url('post-new.php?post_type=product').'&new_video_name='. rawurlencode($video_name); |
| 3010 |
|
| 3011 |
|
| 3012 |
|
| 3013 |
$to_return .='<a class="create_new_free_video" href="'.esc_url($add_free_video_url).'">'.esc_html__('Create new Free-To-View VOD from this recording').'</a>'; |
| 3014 |
if (class_exists('WooCommerce')) { |
| 3015 |
$to_return .='<a class="create_new_ppv_video" href="'.esc_url($add_paid_video_url).'">'.esc_html__('Create new Pay-Per-View VOD from this recording').'</a>'; |
| 3016 |
} |
| 3017 |
|
| 3018 |
$to_return.='</div>'; |
| 3019 |
endif; |
| 3020 |
|
| 3021 |
} |
| 3022 |
$current_page= get_current_screen(); |
| 3023 |
} |
| 3024 |
|
| 3025 |
// no items to show |
| 3026 |
// Empty-state message when there are neither completed nor pending items. |
| 3027 |
if ( !is_array( $video_list_raw['items'] ) || ( key_exists( 'pending', $video_list_raw ) && !is_array( $video_list_raw['pending'] ) ) ) { |
| 3028 |
$to_return.= '<div class="wpstream_video_wrapper">'.esc_html__('You don\'t have any videos.','wpstream').'</div>'; |
| 3029 |
} |
| 3030 |
return $to_return; |
| 3031 |
} |
| 3032 |
|
| 3033 |
|
| 3034 |
/** |
| 3035 |
* Set defualt channels values |
| 3036 |
* |
| 3037 |
* On publish of a free channel, seed its per-channel `local_event_options` |
| 3038 |
* from the site-wide default options if it has none yet. Hooked to the |
| 3039 |
* post publish/save action. |
| 3040 |
* |
| 3041 |
* @since 3.0.1 |
| 3042 |
* @param int $post_id Post ID being published. |
| 3043 |
* @param WP_Post $post Post object being published. |
| 3044 |
* @return void |
| 3045 |
*/ |
| 3046 |
public function wpstream_publish_wpstream_product($post_id,$post){ |
| 3047 |
// Only applies to free channel posts. |
| 3048 |
if( $post->post_type == 'wpstream_product' ){ |
| 3049 |
// Marker meta + flag that global options are the source of truth. |
| 3050 |
update_post_meta ($post_id,'local_event_options_test','working_on_'.$post_id); |
| 3051 |
update_post_meta ($post_id, 'use_global_event_options', true); |
| 3052 |
$to_save_option=array(); |
| 3053 |
|
| 3054 |
// Site-wide default event options and any existing per-channel overrides. |
| 3055 |
$global_options= get_option('wpstream_user_streaming_global_channel_options'); |
| 3056 |
|
| 3057 |
$local_events = get_post_meta ($post_id ,'local_event_options',true) ; |
| 3058 |
|
| 3059 |
// Only seed when this channel has no local options yet. |
| 3060 |
if( $local_events =='' ){ |
| 3061 |
if( is_array($global_options) ){ |
| 3062 |
// Copy each global option (sanitized) into the per-channel meta. |
| 3063 |
foreach($global_options as $key=>$value){ |
| 3064 |
$to_save_option[sanitize_key($key)]=sanitize_text_field($value); |
| 3065 |
} |
| 3066 |
|
| 3067 |
update_post_meta ($post_id,'local_event_options',$to_save_option); |
| 3068 |
|
| 3069 |
} |
| 3070 |
} |
| 3071 |
|
| 3072 |
|
| 3073 |
} |
| 3074 |
} |
| 3075 |
|
| 3076 |
/** |
| 3077 |
* Create the remote WpStream cloud channel when a channel post is published. |
| 3078 |
* |
| 3079 |
* Runs for free (`wpstream_product`) and paid live-stream (`product` with |
| 3080 |
* the live_stream type) posts, skipping autosaves/revisions and posts that |
| 3081 |
* already have a remote channel. On success it stores the channelId and |
| 3082 |
* embedUrl meta. Hooked to the post publish/save action. |
| 3083 |
* |
| 3084 |
* @param int $post_id Post ID being published. |
| 3085 |
* @param WP_Post $post Post object being published. |
| 3086 |
* @return void |
| 3087 |
*/ |
| 3088 |
public function wpstream_create_remote_channel_on_publish( $post_id, $post ) { |
| 3089 |
// Determine whether this is a channel post that needs a cloud channel. |
| 3090 |
$is_free_channel = $post->post_type == 'wpstream_product'; |
| 3091 |
$is_paid_channel = $post->post_type == 'product' && has_term( 'live_stream', 'product_type', $post_id ); |
| 3092 |
|
| 3093 |
// Bail for non-channel post types. |
| 3094 |
if ( !$is_free_channel && !$is_paid_channel ) { |
| 3095 |
return; |
| 3096 |
} |
| 3097 |
|
| 3098 |
// Skip autosaves and revisions (they are not real publishes). |
| 3099 |
if ( defined( 'DOING_AUTOSAVE') && DOING_AUTOSAVE ) { |
| 3100 |
return; |
| 3101 |
} |
| 3102 |
if ( wp_is_post_revision( $post_id ) ) { |
| 3103 |
return; |
| 3104 |
} |
| 3105 |
// Idempotency: skip if a remote channel was already created. |
| 3106 |
if ( get_post_meta( $post_id, '_wpstream_remote_channel_created', true ) ) { |
| 3107 |
return; |
| 3108 |
} |
| 3109 |
|
| 3110 |
// Ask the cloud API to create the channel and log the raw response. |
| 3111 |
$response = $this->main->wpstream_live_connection->wpstream_create_channel( $post_id ); |
| 3112 |
error_log('channel/create response for post ' . $post_id . ' is ' . print_r($response, true)); |
| 3113 |
|
| 3114 |
// On success, persist the returned channel id + player embed URL and mark it created. |
| 3115 |
if ( $response && ! empty( $response['success'] ) ) { |
| 3116 |
if ( isset($response['channel_id']) && $response['channel_id'] !== '' ) { |
| 3117 |
update_post_meta( $post_id, 'channelId', $response['channel_id'] ); |
| 3118 |
update_post_meta( $post_id, 'embedUrl', LIVE_PLAYER_URL_PREFIX . '?' . http_build_query( array( 'channelId' => $response['channel_id'] ) ) ); |
| 3119 |
update_post_meta( $post_id, '_wpstream_remote_channel_created', 1 ); |
| 3120 |
} |
| 3121 |
} |
| 3122 |
} |
| 3123 |
|
| 3124 |
/** |
| 3125 |
* save meta options |
| 3126 |
* |
| 3127 |
* Persist the allow-listed free channel / VOD meta fields on save for the |
| 3128 |
* `wpstream_product` and `wpstream_product_vod` post types. Hooked to the |
| 3129 |
* post save action. |
| 3130 |
* |
| 3131 |
* @since 3.0.1 |
| 3132 |
* @param int $post_id Post ID being saved. |
| 3133 |
* @param WP_Post $post Post object being saved. |
| 3134 |
* @return void |
| 3135 |
*/ |
| 3136 |
public function wpstream_free_product_update_post($post_id,$post){ |
| 3137 |
|
| 3138 |
// Guard against non-post callers. |
| 3139 |
if(!is_object($post) || !isset($post->post_type)) { |
| 3140 |
return; |
| 3141 |
} |
| 3142 |
|
| 3143 |
|
| 3144 |
|
| 3145 |
|
| 3146 |
|
| 3147 |
// Only handle the free channel and VOD post types. |
| 3148 |
if( $post->post_type == 'wpstream_product' || |
| 3149 |
$post->post_type == 'wpstream_product_vod' ): |
| 3150 |
|
| 3151 |
// Meta keys this handler is allowed to write. |
| 3152 |
$allowed_keys=array( |
| 3153 |
'wpstream_product_type', |
| 3154 |
'wpstream_free_video', |
| 3155 |
'wpstream_free_video_external', |
| 3156 |
'wpstream_closed_captions_file' |
| 3157 |
); |
| 3158 |
|
| 3159 |
|
| 3160 |
// Save each allow-listed, scalar POST field as sanitized post meta. |
| 3161 |
foreach ($_POST as $key => $value) { |
| 3162 |
if( !is_array ($value) ){ |
| 3163 |
if (in_array ($key, $allowed_keys)) { |
| 3164 |
$postmeta = sanitize_text_field ( $value ); |
| 3165 |
update_post_meta($post_id, sanitize_key($key), $postmeta ); |
| 3166 |
} |
| 3167 |
} |
| 3168 |
} |
| 3169 |
|
| 3170 |
endif; |
| 3171 |
|
| 3172 |
} |
| 3173 |
|
| 3174 |
|
| 3175 |
/** |
| 3176 |
* save meta options |
| 3177 |
* |
| 3178 |
* Register the plugin's post metaboxes: VOD settings on `wpstream_product_vod`, |
| 3179 |
* the video collection box on `wpstream_bundles`, and (for WooCommerce bundle |
| 3180 |
* products) the video-collection options box on `product`. Hooked to add_meta_boxes. |
| 3181 |
* |
| 3182 |
* @since 3.0.1 |
| 3183 |
* @return void |
| 3184 |
*/ |
| 3185 |
public function add_wpstream_product_metaboxes() { |
| 3186 |
global $post; |
| 3187 |
$post_id = $post->ID; |
| 3188 |
|
| 3189 |
|
| 3190 |
// VOD settings metabox and the bundle video-collection metabox. |
| 3191 |
add_meta_box( 'add_wpstream_product_metaboxes-sectionid', esc_html__( 'Video On Demand Settings', 'wpstream' ),array($this,'display_meta_options'),'wpstream_product_vod' ,'normal','default'); |
| 3192 |
add_meta_box( 'custom_metabox_video_collection', esc_html__( 'Video Collection', 'wpstream' ), 'wpstream_bundle_custom_metabox_callback', 'wpstream_bundles', 'normal', 'high' ); |
| 3193 |
|
| 3194 |
// For WooCommerce bundle products, add the video-collection options box on the product screen. |
| 3195 |
if(function_exists('wc_get_product')): |
| 3196 |
$product = wc_get_product( $post_id ); |
| 3197 |
if ( $product ) { |
| 3198 |
|
| 3199 |
if ( $product->get_type() === 'wpstream_bundle' ) { |
| 3200 |
add_meta_box( |
| 3201 |
'wpstream_woo_custom_metabox', |
| 3202 |
esc_html__( 'Video Collection Options', 'wpstream' ), |
| 3203 |
'wpstream_bundle_custom_metabox_callback', |
| 3204 |
'product', |
| 3205 |
'normal', |
| 3206 |
'default' |
| 3207 |
); |
| 3208 |
} |
| 3209 |
} |
| 3210 |
endif; |
| 3211 |
|
| 3212 |
} |
| 3213 |
|
| 3214 |
|
| 3215 |
/** |
| 3216 |
* make woocomerce virtual products |
| 3217 |
* |
| 3218 |
* Force WpStream WooCommerce product types (live_stream, video_on_demand, |
| 3219 |
* wpstream_bundle) to be virtual so they need no shipping. Hooked to save. |
| 3220 |
* |
| 3221 |
* @since 3.0.1 |
| 3222 |
* @param int $post_id Post ID being saved (unused; uses global $post). |
| 3223 |
* @param WP_Post $post Post object (overwritten by global $post). |
| 3224 |
* @return void |
| 3225 |
*/ |
| 3226 |
public function wpstream_make_product_virtual($post_id,$post){ |
| 3227 |
global $post; |
| 3228 |
if(isset($post->ID)){ |
| 3229 |
// Only WooCommerce products are relevant. |
| 3230 |
if ( $post->post_type !== 'product' ) return; |
| 3231 |
// Mark WpStream product types as virtual. |
| 3232 |
$term_list = wp_get_post_terms($post->ID, 'product_type'); |
| 3233 |
if( !empty($term_list) && |
| 3234 |
isset($term_list[0]->name) && |
| 3235 |
in_array($term_list[0]->name, ['live_stream', 'video_on_demand', 'wpstream_bundle']) |
| 3236 |
){ |
| 3237 |
update_post_meta( $post->ID, '_virtual', 'yes' ); |
| 3238 |
} |
| 3239 |
} |
| 3240 |
} |
| 3241 |
|
| 3242 |
|
| 3243 |
|
| 3244 |
/** |
| 3245 |
* render meta options |
| 3246 |
* |
| 3247 |
* Render the "Video On Demand Settings" metabox: media-type selector, |
| 3248 |
* recording chooser, optional captions (.vtt) picker, and the self-hosted/ |
| 3249 |
* external video URL field. Echoes HTML. |
| 3250 |
* |
| 3251 |
* @since 3.0.1 |
| 3252 |
* @param WP_Post $post Post being edited (overwritten by global $post). |
| 3253 |
* @return void |
| 3254 |
*/ |
| 3255 |
public function display_meta_options( $post ) { |
| 3256 |
// Nonce for the metabox save + use the global post object. |
| 3257 |
wp_nonce_field( plugin_basename( __FILE__ ), 'estate_agent_noncename' ); |
| 3258 |
global $post; |
| 3259 |
|
| 3260 |
// Determine the pre-selected media type / video. |
| 3261 |
$is_live = ''; |
| 3262 |
$is_video = ''; |
| 3263 |
$is_video_external = ''; |
| 3264 |
// When arriving from "create VOD from recording", preselect the recording. |
| 3265 |
if( isset( $_GET['new_video_name']) && $_GET['new_video_name']!='' ){ |
| 3266 |
$is_video = ' selected '; |
| 3267 |
$wpstream_free_video = esc_html( $_GET['new_video_name']); |
| 3268 |
}else{ |
| 3269 |
// Otherwise read the saved type/video from post meta and select accordingly. |
| 3270 |
$wpstream_product_type = esc_html(get_post_meta($post->ID, 'wpstream_product_type', true)); |
| 3271 |
$wpstream_free_video = esc_html(get_post_meta($post->ID, 'wpstream_free_video', true)); |
| 3272 |
|
| 3273 |
if($wpstream_product_type==1){ |
| 3274 |
$is_live = ' selected '; |
| 3275 |
} |
| 3276 |
|
| 3277 |
if($wpstream_product_type==2){ |
| 3278 |
$is_video = ' selected '; |
| 3279 |
} |
| 3280 |
|
| 3281 |
if($wpstream_product_type==3){ |
| 3282 |
$is_video_external = ' selected '; |
| 3283 |
} |
| 3284 |
} |
| 3285 |
|
| 3286 |
// Media-type dropdown (recording vs self-hosted/external). |
| 3287 |
print' |
| 3288 |
<p class="meta-options"> |
| 3289 |
<label for="wpstream_product_type">'.__('Media Type:','wpstream').' </label><br /> |
| 3290 |
<select id="wpstream_product_type" name="wpstream_product_type"> |
| 3291 |
<option value="2" '.$is_video.'>'.__('Recording','wpstream').'</option> |
| 3292 |
<option value="3" '.$is_video_external.'>'.__('Self Hosted or External Video','wpstream').'</option> |
| 3293 |
</select> |
| 3294 |
</p> |
| 3295 |
'; |
| 3296 |
|
| 3297 |
|
| 3298 |
// Fetch the account's recordings to populate the chooser. |
| 3299 |
$video_list = $this->main->wpstream_live_connection->wpstream_get_videos(); |
| 3300 |
|
| 3301 |
|
| 3302 |
// Recording chooser dropdown, pre-selecting the saved recording. |
| 3303 |
print '<div class="meta-options video_free">'; |
| 3304 |
print '<p class="meta-option wpstream_free_video">'; |
| 3305 |
print '<label for="wpstream_free_video">'.__('Choose video:','wpstream').' </label><br /> |
| 3306 |
<select id="wpstream_free_video" name="wpstream_free_video">'; |
| 3307 |
|
| 3308 |
if( is_array( $video_list ) ) { |
| 3309 |
foreach ($video_list as $key=>$value){ |
| 3310 |
print '<option value="'.$key.'"'; |
| 3311 |
if($wpstream_free_video === $key){ |
| 3312 |
print ' selected '; |
| 3313 |
} |
| 3314 |
print '>'.$value.'</option>'; |
| 3315 |
} |
| 3316 |
} |
| 3317 |
print'</select>'; |
| 3318 |
print '</p> '; |
| 3319 |
|
| 3320 |
// Optional captions (.vtt) picker; hide the select button when a file is already set. |
| 3321 |
$wpstream_closed_captions_file = get_post_meta($post->ID, 'wpstream_closed_captions_file', true); |
| 3322 |
|
| 3323 |
$button_style = $wpstream_closed_captions_file ? 'style="display:none;"' : ''; |
| 3324 |
|
| 3325 |
print '<p class="meta-option wpstream_vod_captions_url">'; |
| 3326 |
print '<label for="wpstream_vod_captions_url_button">'.__('Captions file (optional):','wpstream').' </label><br /> |
| 3327 |
<input type="hidden" id="wpstream_closed_captions_file" name="wpstream_closed_captions_file" value="'.esc_attr($wpstream_closed_captions_file).'" /> |
| 3328 |
<input id="wpstream_vod_captions_url_button" type="button" class="upload_button button" value="'.esc_html__('Select .vtt Captions File','wpstream').'" '.$button_style.' /> |
| 3329 |
<span class="wpstream_caption_file_display">'.( $wpstream_closed_captions_file ? esc_html( basename( $wpstream_closed_captions_file ) ) : '' ).'</span>'; |
| 3330 |
if ( $wpstream_closed_captions_file ) { |
| 3331 |
print '<input type="button" class="button wpstream_remove_caption" value="'.esc_html__('Remove','wpstream').'" style="margin-left: 5px;" />'; |
| 3332 |
} |
| 3333 |
print '</p> '; |
| 3334 |
print '</div>'; |
| 3335 |
|
| 3336 |
// Self-hosted / external video URL field with a media-library select button. |
| 3337 |
$wpstream_free_video_external= esc_html(get_post_meta($post->ID, 'wpstream_free_video_external', true)); |
| 3338 |
print '<div class="meta-options1 video_free_external"> |
| 3339 |
<label for="wpstream_free_video_external">'.__('Video:','wpstream').' </label><br /> |
| 3340 |
|
| 3341 |
<input id="wpstream_free_video_external" type="text" size="36" name="wpstream_free_video_external" value="'.$wpstream_free_video_external.'" /> |
| 3342 |
<input id="wpstream_free_video_external_button" type="button" size="40" class="upload_button button" value="'.esc_html__('Select Video','wpstream').'" />'; |
| 3343 |
// Show the recording hint vs the external hint depending on the saved media type. |
| 3344 |
if($wpstream_product_type==2){ |
| 3345 |
$show_recording=''; |
| 3346 |
$show_external='style="display:none"'; |
| 3347 |
}else{ |
| 3348 |
$show_recording='style="display:none"'; |
| 3349 |
$show_external=''; |
| 3350 |
} |
| 3351 |
print '<p '.$show_recording.' class="wpstream_option_vod_source wpstream_show_recording">'.esc_html__('Choose one of your existing recordings.','wpstream').'</p>'; |
| 3352 |
print '<p '. $show_external.' class="wpstream_option_vod_source wpstream_show_external">'.esc_html__('Upload a video from your computer or paste the URL of a YouTube/external video.','wpstream').'</p>'; |
| 3353 |
|
| 3354 |
print '</div> '; |
| 3355 |
} |
| 3356 |
|
| 3357 |
|
| 3358 |
|
| 3359 |
|
| 3360 |
|
| 3361 |
|
| 3362 |
|
| 3363 |
/** |
| 3364 |
* Add new product types to Woocommerce select product type |
| 3365 |
* |
| 3366 |
* Register the "Live Channel" and "Video On Demand" WooCommerce product |
| 3367 |
* types in the product-type dropdown. Filter callback. |
| 3368 |
* |
| 3369 |
* @since 3.0.1 |
| 3370 |
* @param array $types Existing product-type => label map. |
| 3371 |
* @return array The map with the WpStream types added. |
| 3372 |
*/ |
| 3373 |
public function wpstream_add_products( $types ){ |
| 3374 |
$types[ 'live_stream' ] = __( 'Live Channel','wpestream' ); |
| 3375 |
$types[ 'video_on_demand' ] = __( 'Video On Demand','wpestream' ); |
| 3376 |
|
| 3377 |
return $types; |
| 3378 |
} |
| 3379 |
|
| 3380 |
/** |
| 3381 |
* Map the WpStream product-type slugs to their WC_Product subclasses. |
| 3382 |
* Filter callback for woocommerce_product_class. |
| 3383 |
* |
| 3384 |
* @param string $classname Default product class name. |
| 3385 |
* @param string $product_type The product type slug being instantiated. |
| 3386 |
* @return string The resolved product class name. |
| 3387 |
*/ |
| 3388 |
public function wpstream_add_products_class( $classname, $product_type ) { |
| 3389 |
if ( 'live_stream' === $product_type ) { |
| 3390 |
$classname = 'WC_Product_Live_Stream'; |
| 3391 |
} |
| 3392 |
if ( 'video_on_demand' === $product_type ) { |
| 3393 |
$classname = 'WC_Product_Video_On_Demand'; |
| 3394 |
} |
| 3395 |
return $classname; |
| 3396 |
} |
| 3397 |
|
| 3398 |
/** |
| 3399 |
* Load the custom WC_Product subclass files when WooCommerce is active. |
| 3400 |
* |
| 3401 |
* @return void |
| 3402 |
*/ |
| 3403 |
public function wpstream_add_custom_wc_products() { |
| 3404 |
// Require the live-stream and VOD product classes (WooCommerce only). |
| 3405 |
if( class_exists( 'WooCommerce' ) ){ |
| 3406 |
require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-wc_product_live_stream.php'; |
| 3407 |
require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-wc_product_video_on_demand.php'; |
| 3408 |
} |
| 3409 |
} |
| 3410 |
|
| 3411 |
/** |
| 3412 |
* Js action to do when user pick live stream or video on demand |
| 3413 |
* |
| 3414 |
* @since 3.0.1 |
| 3415 |
*/ |
| 3416 |
|
| 3417 |
/** |
| 3418 |
* Whether WpStream WooCommerce product UI should load for this product. |
| 3419 |
* |
| 3420 |
* Skips simple, course, and other non-WpStream types so LearnDash and |
| 3421 |
* other integrations can use the General product data tab without conflict. |
| 3422 |
* |
| 3423 |
* @param int $post_id Product post ID. Uses global $post when 0. |
| 3424 |
* @return bool |
| 3425 |
*/ |
| 3426 |
public function wpstream_is_wpstream_wc_product_context( $post_id = 0 ) { |
| 3427 |
// Creating a new stream/VOD from a query-string action always counts. |
| 3428 |
if ( isset( $_GET['new_stream'] ) || isset( $_GET['new_video_name'] ) ) { |
| 3429 |
return true; |
| 3430 |
} |
| 3431 |
|
| 3432 |
// Fall back to the current global post when no ID was passed. |
| 3433 |
if ( ! $post_id ) { |
| 3434 |
global $post; |
| 3435 |
$post_id = ( $post && isset( $post->ID ) ) ? (int) $post->ID : 0; |
| 3436 |
} |
| 3437 |
|
| 3438 |
// Need a product ID and WooCommerce to resolve the type. |
| 3439 |
if ( ! $post_id || ! function_exists( 'wc_get_product' ) ) { |
| 3440 |
return false; |
| 3441 |
} |
| 3442 |
|
| 3443 |
$product = wc_get_product( $post_id ); |
| 3444 |
if ( ! $product ) { |
| 3445 |
return false; |
| 3446 |
} |
| 3447 |
|
| 3448 |
// True only for WpStream (and subscription) product types. |
| 3449 |
return in_array( |
| 3450 |
$product->get_type(), |
| 3451 |
array( 'live_stream', 'video_on_demand', 'wpstream_bundle', 'subscription' ), |
| 3452 |
true |
| 3453 |
); |
| 3454 |
} |
| 3455 |
|
| 3456 |
/** |
| 3457 |
* WooCommerce product types that use the core Regular price field. |
| 3458 |
* |
| 3459 |
* @return string[] |
| 3460 |
*/ |
| 3461 |
public function wpstream_get_wc_product_types_with_pricing() { |
| 3462 |
return array( 'live_stream', 'video_on_demand', 'wpstream_bundle' ); |
| 3463 |
} |
| 3464 |
|
| 3465 |
/** |
| 3466 |
* Add show_if_* classes to the pricing panel for WpStream product types. |
| 3467 |
* |
| 3468 |
* WooCommerce renders .options_group.pricing with show_if_simple only. Custom |
| 3469 |
* types rely on show_if_{type} classes so meta-boxes-product.js can toggle them |
| 3470 |
* when the product type dropdown changes, including on unsaved new products. |
| 3471 |
* |
| 3472 |
* @param string $hook Admin screen hook suffix. |
| 3473 |
*/ |
| 3474 |
public function wpstream_enqueue_wc_product_pricing_visibility( $hook ) { |
| 3475 |
// Only on the add/edit-post admin screens. |
| 3476 |
if ( ! in_array( $hook, array( 'post.php', 'post-new.php' ), true ) ) { |
| 3477 |
return; |
| 3478 |
} |
| 3479 |
|
| 3480 |
// Only on the WooCommerce product post type. |
| 3481 |
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; |
| 3482 |
if ( ! $screen || 'product' !== $screen->post_type ) { |
| 3483 |
return; |
| 3484 |
} |
| 3485 |
|
| 3486 |
// Need WooCommerce's product meta-boxes script to attach to. |
| 3487 |
if ( ! wp_script_is( 'wc-admin-product-meta-boxes', 'registered' ) ) { |
| 3488 |
return; |
| 3489 |
} |
| 3490 |
|
| 3491 |
// Pass the WpStream pricing types to JS and add show_if_{type} classes so the pricing panel toggles correctly. |
| 3492 |
$types_json = wp_json_encode( array_values( $this->wpstream_get_wc_product_types_with_pricing() ) ); |
| 3493 |
|
| 3494 |
wp_add_inline_script( |
| 3495 |
'wc-admin-product-meta-boxes', |
| 3496 |
"jQuery( function( $ ) { |
| 3497 |
var wpstreamPricingProductTypes = {$types_json}; |
| 3498 |
var \$pricing = $( '.options_group.pricing' ); |
| 3499 |
var \$soldIndividually = $( '._sold_individually_field' ).parent(); |
| 3500 |
|
| 3501 |
wpstreamPricingProductTypes.forEach( function( type ) { |
| 3502 |
\$pricing.addClass( 'show_if_' + type ); |
| 3503 |
\$soldIndividually.addClass( 'show_if_' + type ); |
| 3504 |
} ); |
| 3505 |
|
| 3506 |
if ( wpstreamPricingProductTypes.indexOf( $( '#product-type' ).val() ) !== -1 ) { |
| 3507 |
\$pricing.show(); |
| 3508 |
\$soldIndividually.show(); |
| 3509 |
} |
| 3510 |
} );" |
| 3511 |
); |
| 3512 |
} |
| 3513 |
|
| 3514 |
|
| 3515 |
/** |
| 3516 |
* Add custom classes to the product types |
| 3517 |
* |
| 3518 |
* Adjust the WooCommerce product-data tab visibility classes so WpStream |
| 3519 |
* types hide the Shipping tab and show the Inventory tab. Filter callback. |
| 3520 |
* |
| 3521 |
* @since 3.0.1 |
| 3522 |
* @param array $tabs WooCommerce product data tabs config. |
| 3523 |
* @return array The modified tabs config. |
| 3524 |
*/ |
| 3525 |
public function wpstream_hide_attributes_data_panel( $tabs) { |
| 3526 |
|
| 3527 |
// Hide Shipping and show Inventory for WpStream product types. |
| 3528 |
$tabs['shipping']['class'][] = 'hide_if_live_stream hide_if_video_on_demand hide_if_wpstream_bundle'; |
| 3529 |
$tabs['inventory']['class'][] = 'show_if_live_stream show_if_video_on_demand show_if_wpstream_bundle'; |
| 3530 |
|
| 3531 |
return $tabs; |
| 3532 |
} |
| 3533 |
|
| 3534 |
|
| 3535 |
|
| 3536 |
/** |
| 3537 |
* Hide buy now on products if Netflix mode |
| 3538 |
* |
| 3539 |
* When global subscription ("Netflix") mode is on, mark WpStream media |
| 3540 |
* products as not individually purchasable (access comes via the |
| 3541 |
* subscription instead). Filter callback for is_purchasable. |
| 3542 |
* |
| 3543 |
* @since 3.12 |
| 3544 |
* @param bool $purchaseable_product_wpblog Current purchasable flag. |
| 3545 |
* @param WC_Product $product The product being checked. |
| 3546 |
* @return bool False for WpStream media in global-sub mode, else the original flag. |
| 3547 |
*/ |
| 3548 |
public function wpstream_hide_buy_now_subscription_mode( $purchaseable_product_wpblog,$product){ |
| 3549 |
$product_id=$product->get_id(); |
| 3550 |
|
| 3551 |
$term_list = wp_get_post_terms($product_id, 'product_type'); |
| 3552 |
|
| 3553 |
// Global subscription ("Netflix") mode flag. |
| 3554 |
$subscription_model = intval( get_option('wpstream_global_sub','')) ; |
| 3555 |
|
| 3556 |
if($subscription_model==1){ // if we have Neflix mode |
| 3557 |
// WpStream media types are not directly purchasable in this mode. |
| 3558 |
if( $term_list[0]->name=='live_stream' || $term_list[0]->name=='video_on_demand' || $term_list[0]->name=='wpstream_bundle' ){ |
| 3559 |
return false; |
| 3560 |
} |
| 3561 |
} |
| 3562 |
|
| 3563 |
return $purchaseable_product_wpblog; |
| 3564 |
} |
| 3565 |
|
| 3566 |
|
| 3567 |
|
| 3568 |
|
| 3569 |
|
| 3570 |
|
| 3571 |
/** |
| 3572 |
* Add custom fields to custom product types |
| 3573 |
* |
| 3574 |
* Render the extra WooCommerce "General" product fields for WpStream types: |
| 3575 |
* the subscription-live flag, the recording/video chooser, and the |
| 3576 |
* "attach to subscription" multi-select. Echoes HTML. |
| 3577 |
* |
| 3578 |
* @since 3.0.1 |
| 3579 |
* @return void |
| 3580 |
*/ |
| 3581 |
public function wpstream_add_custom_general_fields() { |
| 3582 |
global $woocommerce, $post; |
| 3583 |
|
| 3584 |
// Only render for WpStream product contexts (avoids clashing with other product types). |
| 3585 |
if ( ! $this->wpstream_is_wpstream_wc_product_context( isset( $post->ID ) ? (int) $post->ID : 0 ) ) { |
| 3586 |
return; |
| 3587 |
} |
| 3588 |
// Subscription-based live channel toggle (only when WC Subscriptions is active). |
| 3589 |
if(function_exists('wcs_user_has_subscription')){ |
| 3590 |
echo '<div class="options_group show_if_subscription">'; |
| 3591 |
woocommerce_wp_select( |
| 3592 |
array( |
| 3593 |
'id' => '_subscript_live_event', |
| 3594 |
'label' => __( 'Is a subscription based live channel ?', 'woocommerce' ), |
| 3595 |
'options' => array("yes"=>"yes","no"=>"no", "none" => "none") |
| 3596 |
) |
| 3597 |
); |
| 3598 |
echo '</div>'; |
| 3599 |
} |
| 3600 |
|
| 3601 |
echo '<div class="options_group show_if_live_stream" style="border:none;"></div>'; |
| 3602 |
// VOD video chooser section. |
| 3603 |
echo '<div class="options_group show_if_video_on_demand">'; |
| 3604 |
|
| 3605 |
|
| 3606 |
// Pre-selected video: from the new-video query arg, else the saved _movie_url meta. |
| 3607 |
$selected=''; |
| 3608 |
if( isset( $_GET['new_video_name']) && $_GET['new_video_name']!='' ){ |
| 3609 |
$selected=esc_html($_GET['new_video_name']); |
| 3610 |
} |
| 3611 |
if($selected==''){ |
| 3612 |
$selected= get_post_meta($post->ID,'_movie_url',true); |
| 3613 |
} |
| 3614 |
// Only administrators may assign videos. |
| 3615 |
if( !current_user_can('administrator') ){ |
| 3616 |
print '<div style="margin:10px;">'.esc_html('You need to be an administrator in order to assign videos','wpstream').'</div>'; |
| 3617 |
}else{ |
| 3618 |
woocommerce_wp_select( |
| 3619 |
array( |
| 3620 |
'id' => '_movie_url', |
| 3621 |
'label' => __( 'Choose video', 'woocommerce' ), |
| 3622 |
'options' => $this->main->wpstream_live_connection->wpstream_get_videos(), |
| 3623 |
'selected'=> true, |
| 3624 |
'value' => $selected |
| 3625 |
) |
| 3626 |
); |
| 3627 |
} |
| 3628 |
|
| 3629 |
|
| 3630 |
|
| 3631 |
echo '</div>'; |
| 3632 |
|
| 3633 |
// "Attach to subscription" multi-select (only when WC Subscriptions is active). |
| 3634 |
if(function_exists('wcs_user_has_subscription')){ |
| 3635 |
$selected_sub=''; |
| 3636 |
echo '<div class="options_group show_if_video_on_demand show_if_live_stream">'; |
| 3637 |
if( isset( $_GET['wpstream_parent_sub']) && $_GET['wpstream_parent_sub']!='' ){ |
| 3638 |
$selected_sub=esc_html($_GET['wpstream_parent_sub']); |
| 3639 |
} |
| 3640 |
if($selected_sub==''){ |
| 3641 |
$selected_sub= get_post_meta($post->ID,'_wpstream_parent_sub',true); |
| 3642 |
} |
| 3643 |
woocommerce_wp_select( |
| 3644 |
array( |
| 3645 |
'id' => '_wpstream_parent_sub', |
| 3646 |
'name' => '_wpstream_parent_sub[]', |
| 3647 |
'label' => __( 'Attach to subscription', 'woocommerce' ), |
| 3648 |
'options' => $this->wpstream_return_subscriptions_created(), |
| 3649 |
'selected'=> true, |
| 3650 |
'value' => $selected_sub, |
| 3651 |
'custom_attributes' => array('multiple' => 'multiple') |
| 3652 |
) |
| 3653 |
); |
| 3654 |
|
| 3655 |
echo '</div>'; |
| 3656 |
|
| 3657 |
} |
| 3658 |
} |
| 3659 |
|
| 3660 |
|
| 3661 |
/** |
| 3662 |
* Build an id => title map of all WooCommerce subscription products, |
| 3663 |
* for the "attach to subscription" selector. Includes a "none" option. |
| 3664 |
* |
| 3665 |
* @return array Map of subscription product ID => title (0 => 'none'). |
| 3666 |
*/ |
| 3667 |
public function wpstream_return_subscriptions_created(){ |
| 3668 |
// Seed with the "none" choice. |
| 3669 |
$return=array('0'=>'none'); |
| 3670 |
|
| 3671 |
// Query all subscription-type products, title-ordered. |
| 3672 |
$args = array( |
| 3673 |
'post_type' => 'product', |
| 3674 |
'posts_per_page' => -1, |
| 3675 |
'orderby' => 'title', |
| 3676 |
'order' => 'ASC', |
| 3677 |
'tax_query' => array( |
| 3678 |
'relation' => 'AND', |
| 3679 |
array( |
| 3680 |
'taxonomy' => 'product_type', |
| 3681 |
'field' => 'slug', |
| 3682 |
'terms' => array( 'subscription'), |
| 3683 |
) |
| 3684 |
) |
| 3685 |
); |
| 3686 |
|
| 3687 |
// Collect each subscription's id => title. |
| 3688 |
$subscriptions = new WP_Query($args); |
| 3689 |
if($subscriptions->have_posts()): |
| 3690 |
while ($subscriptions->have_posts()): $subscriptions->the_post(); |
| 3691 |
$return[ get_the_ID() ] = get_the_title(); |
| 3692 |
endwhile; |
| 3693 |
endif; |
| 3694 |
|
| 3695 |
// Restore the main query and return the map. |
| 3696 |
wp_reset_postdata(); |
| 3697 |
return $return; |
| 3698 |
|
| 3699 |
} |
| 3700 |
|
| 3701 |
|
| 3702 |
|
| 3703 |
|
| 3704 |
|
| 3705 |
|
| 3706 |
/** |
| 3707 |
* Save custom fields |
| 3708 |
* |
| 3709 |
* Persist the WpStream WooCommerce product fields (_movie_url, |
| 3710 |
* _subscript_live_event, _wpstream_parent_sub) on save and reset the |
| 3711 |
* event_passed flag. Hooked to the product save action. |
| 3712 |
* |
| 3713 |
* @since 3.0.1 |
| 3714 |
* @param int $post_id Product ID being saved. |
| 3715 |
* @return void |
| 3716 |
*/ |
| 3717 |
public function wpstream_add_custom_general_fields_save( $post_id ){ |
| 3718 |
// Only handle WpStream product contexts. |
| 3719 |
if ( ! $this->wpstream_is_wpstream_wc_product_context( (int) $post_id ) ) { |
| 3720 |
return; |
| 3721 |
} |
| 3722 |
|
| 3723 |
// Meta keys this handler may write. |
| 3724 |
$permited_values = array( |
| 3725 |
'_movie_url', |
| 3726 |
'_subscript_live_event', |
| 3727 |
'_wpstream_parent_sub', |
| 3728 |
|
| 3729 |
); |
| 3730 |
|
| 3731 |
|
| 3732 |
|
| 3733 |
// Reset event_passed and save each allow-listed field. |
| 3734 |
foreach($_POST as $key=>$value){ |
| 3735 |
update_post_meta( $post_id, 'event_passed', 0 ); |
| 3736 |
if( in_array($key, $permited_values) ){ |
| 3737 |
if( !empty( $_POST[$key] ) ){ |
| 3738 |
$key = sanitize_key($key); |
| 3739 |
$value = sanitize_text_field($_POST[$key]); |
| 3740 |
|
| 3741 |
// The parent-subscription field is an array; sanitize each element. |
| 3742 |
if($key=='_wpstream_parent_sub'){ |
| 3743 |
$value= $_POST[$key]; |
| 3744 |
$value = array_map("sanitize_text_field", $value); |
| 3745 |
|
| 3746 |
} |
| 3747 |
update_post_meta( $post_id, $key, $value ); |
| 3748 |
} |
| 3749 |
} |
| 3750 |
} |
| 3751 |
//die(); |
| 3752 |
|
| 3753 |
} |
| 3754 |
|
| 3755 |
/** |
| 3756 |
* Add to cart redirect |
| 3757 |
* |
| 3758 |
* Render the simple add-to-cart template for WpStream product types. |
| 3759 |
* |
| 3760 |
* @since 3.0.1 |
| 3761 |
* @return void |
| 3762 |
*/ |
| 3763 |
public function wpstream_add_to_cart() { |
| 3764 |
wc_get_template( 'single-product/add-to-cart/simple.php' ); |
| 3765 |
} |
| 3766 |
|
| 3767 |
|
| 3768 |
/** |
| 3769 |
* Replace add to cart button |
| 3770 |
* |
| 3771 |
* For live_stream / video_on_demand products, swap the loop add-to-cart |
| 3772 |
* button for a direct shop add-to-cart link. Filter callback. |
| 3773 |
* |
| 3774 |
* @since 3.0.1 |
| 3775 |
* @param string $button Original button HTML. |
| 3776 |
* @param WC_Product $product The product (overwritten by global $product). |
| 3777 |
* @return string The (possibly replaced) button HTML. |
| 3778 |
*/ |
| 3779 |
public function replacing_add_to_cart_button( $button, $product ) { |
| 3780 |
global $product; |
| 3781 |
$product_type = $product->get_type(); |
| 3782 |
|
| 3783 |
// Only WpStream media types get the custom link; others keep the default button. |
| 3784 |
if($product_type==='live_stream' || $product_type=='video_on_demand'){ |
| 3785 |
return $button = '<a class="button" href="'.get_site_url().'/shop/?add-to-cart=' .$product->get_id(). '&quantity=1">' . __( 'Add to Cart', 'woocommerce' ) . '</a>'; |
| 3786 |
}else{ |
| 3787 |
return $button; |
| 3788 |
} |
| 3789 |
} |
| 3790 |
|
| 3791 |
|
| 3792 |
/** |
| 3793 |
* Admin notices |
| 3794 |
* |
| 3795 |
* Print global admin notices: (commented-out) WooCommerce-missing notice |
| 3796 |
* and an error if the PHP cURL extension is unavailable, plus the dismiss |
| 3797 |
* nonce. Echoes HTML. |
| 3798 |
* |
| 3799 |
* @since 3.0.1 |
| 3800 |
* @return void |
| 3801 |
*/ |
| 3802 |
public function wpstream_admin_notice() { |
| 3803 |
global $pagenow; |
| 3804 |
global $typenow; |
| 3805 |
|
| 3806 |
// Stored dismissed-notice flags. |
| 3807 |
$wpstream_notices = get_option('wpstream_notices'); |
| 3808 |
|
| 3809 |
/* |
| 3810 |
if ( !in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) { |
| 3811 |
if( !is_array($wpstream_notices) || |
| 3812 |
!isset($wpstream_notices['wpstream_woo_notice']) || |
| 3813 |
( isset($wpestate_notices['wpstream_woo_notice']) && $wpestate_notices['wpstream_woo_notice']!='yes') ){ |
| 3814 |
|
| 3815 |
|
| 3816 |
print '<div class="notice wpstream_notices notice-error is-dismissible" data-notice-type="wpstream_woo_notice" > |
| 3817 |
<p>'.__( 'WpStream Pay-Per-View Live Streaming and VOD only works with WooCommerce - Please enable and activate the WooCommerce plugin if you want to monetize your Live Events or Recorded Videos', 'wpstream' ).'</p> |
| 3818 |
</div>'; |
| 3819 |
} |
| 3820 |
} |
| 3821 |
*/ |
| 3822 |
|
| 3823 |
// Hard requirement: warn when the PHP cURL extension is missing. |
| 3824 |
if( !in_array ('curl', get_loaded_extensions())) { |
| 3825 |
print '<div class="notice notice-error is-dismissible"> |
| 3826 |
<p>'.__( 'The php CURL library is not enabled on your server. WpStream plugin needs this library in order to work. Please address this issue with your hosting provider.', 'wpstream' ).'</p> |
| 3827 |
</div>'; |
| 3828 |
} |
| 3829 |
|
| 3830 |
|
| 3831 |
// Nonce used by the JS that records notice dismissals. |
| 3832 |
$ajax_nonce = wp_create_nonce( "wpstream_notice_nonce" ); |
| 3833 |
print '<input type="hidden" id="wpstream_notice_nonce" value="'.esc_html($ajax_nonce).'"/>'; |
| 3834 |
|
| 3835 |
} |
| 3836 |
|
| 3837 |
/** |
| 3838 |
* Get plugin latest update release date from WordPress.org |
| 3839 |
* @param $plugin_slug |
| 3840 |
* @param $version |
| 3841 |
* |
| 3842 |
* @return bool |
| 3843 |
*/ |
| 3844 |
public function get_plugin_release_date( $plugin_slug, $version = null ) { |
| 3845 |
// Query the WordPress.org plugins info API. |
| 3846 |
$api_url = 'https://api.wordpress.org/plugins/info/1.0/' . $plugin_slug . '.json'; |
| 3847 |
$response = wp_remote_get( $api_url ); |
| 3848 |
|
| 3849 |
// Bail on transport error. |
| 3850 |
if ( is_wp_error( $response ) ) { |
| 3851 |
return false; |
| 3852 |
} |
| 3853 |
|
| 3854 |
// Decode the JSON body; bail if there is no versions list. |
| 3855 |
$body = wp_remote_retrieve_body( $response ); |
| 3856 |
$data = json_decode( $body, true ); |
| 3857 |
|
| 3858 |
if ( !$data || !isset( $data['versions'] ) ) { |
| 3859 |
return false; |
| 3860 |
} |
| 3861 |
|
| 3862 |
// no version provided, get the latest version |
| 3863 |
if ( !$version ) { |
| 3864 |
$version = $data['version']; |
| 3865 |
} |
| 3866 |
|
| 3867 |
// check if the version exists in the versions array |
| 3868 |
if ( !isset( $data['versions'][ $version ] ) ) { |
| 3869 |
return false; |
| 3870 |
} |
| 3871 |
|
| 3872 |
// Return the plugin's last-updated date (note: this is the plugin's overall last_updated, not per-version). |
| 3873 |
if ( isset( $data['last_updated'] ) ) { |
| 3874 |
return date('Y-m-d', strtotime( $data['last_updated'] ) ); |
| 3875 |
} |
| 3876 |
|
| 3877 |
return false; |
| 3878 |
} |
| 3879 |
|
| 3880 |
/** |
| 3881 |
* Adds notice for the WpStream update availability |
| 3882 |
* when the update is not older than 30 days |
| 3883 |
*/ |
| 3884 |
public function wpstream_plugin_update_available_notice() { |
| 3885 |
// Only show to users who can update plugins. |
| 3886 |
if (!current_user_can('update_plugins')) { |
| 3887 |
return; |
| 3888 |
} |
| 3889 |
|
| 3890 |
// Look up a pending update for this plugin in WP's transient. |
| 3891 |
$plugin_slug = 'wpstream/wpstream.php'; |
| 3892 |
$update_data = get_site_transient('update_plugins'); |
| 3893 |
|
| 3894 |
if ( is_object( $update_data ) && |
| 3895 |
property_exists( $update_data, 'response' ) && |
| 3896 |
is_array($update_data->response) && |
| 3897 |
key_exists($plugin_slug, $update_data->response) |
| 3898 |
) { |
| 3899 |
$new_version = $update_data->response[$plugin_slug]->new_version; |
| 3900 |
|
| 3901 |
// Suppress the notice for very fresh releases (grace period). |
| 3902 |
$release_date = $this->get_plugin_release_date( 'wpstream', $new_version ); |
| 3903 |
|
| 3904 |
if ( $release_date ) { |
| 3905 |
$days_since_release = ( time() - strtotime( $release_date ) ) / DAY_IN_SECONDS; |
| 3906 |
|
| 3907 |
// if there's an update newer than 7 days, do not show the notice |
| 3908 |
if ( $days_since_release < 7 ) { |
| 3909 |
return; |
| 3910 |
} |
| 3911 |
} |
| 3912 |
// Build the one-click update URL (nonce-protected) and print the notice. |
| 3913 |
$update_url = wp_nonce_url( |
| 3914 |
self_admin_url('update.php?action=upgrade-plugin&plugin=' . urlencode($plugin_slug)), |
| 3915 |
'upgrade-plugin_' . $plugin_slug |
| 3916 |
); |
| 3917 |
|
| 3918 |
echo '<div class="notice notice-warning is-dismissible">'; |
| 3919 |
echo '<p><strong>' . __('WpStream Plugin Update Available', 'wpstream') . '</strong></p>'; |
| 3920 |
echo '<p>' . sprintf( |
| 3921 |
__('Version %s is available. Please update to the latest version for new features and security improvements.', 'wpstream'), |
| 3922 |
'<strong>' . esc_html($new_version) . '</strong>' |
| 3923 |
) . '</p>'; |
| 3924 |
echo '<p><a href="' . esc_url($update_url) . '" class="button button-primary">' . |
| 3925 |
__('Update Now', 'wpstream') . '</a></p>'; |
| 3926 |
echo '</div>'; |
| 3927 |
} |
| 3928 |
} |
| 3929 |
|
| 3930 |
/** |
| 3931 |
* Admin notices |
| 3932 |
* |
| 3933 |
* AJAX handler that records a dismissed notice: marks the posted notice |
| 3934 |
* type as 'yes' in the wpstream_notices option so it stops showing. |
| 3935 |
* |
| 3936 |
* @since 3.0.1 |
| 3937 |
* @return void Ends the request with die(). |
| 3938 |
*/ |
| 3939 |
public function wpstream_update_cache_notice(){ |
| 3940 |
|
| 3941 |
//check_ajax_referer( 'wpstream_notice_nonce', 'security' ); |
| 3942 |
|
| 3943 |
// Which notice was dismissed. |
| 3944 |
$notice_type = esc_html($_POST['notice_type']); |
| 3945 |
$notices = get_option('wp_stream_notices'); |
| 3946 |
|
| 3947 |
// Normalise to an array. |
| 3948 |
if(! is_array($notices) ){ |
| 3949 |
$notices=array(); |
| 3950 |
} |
| 3951 |
|
| 3952 |
// Flag this notice as dismissed and persist. |
| 3953 |
$notices[$notice_type]='yes'; |
| 3954 |
|
| 3955 |
update_option('wpstream_notices',$notices); |
| 3956 |
die(); |
| 3957 |
} |
| 3958 |
|
| 3959 |
|
| 3960 |
|
| 3961 |
|
| 3962 |
/** |
| 3963 |
* Activate metaboxes for Streaming controls on sidebar |
| 3964 |
* |
| 3965 |
* Register the "Live Streaming" sidebar metabox on free channel posts, and |
| 3966 |
* also on WooCommerce products that are live_stream (or a subscription |
| 3967 |
* flagged as a live event). Hooked to add_meta_boxes. |
| 3968 |
* |
| 3969 |
* @since 3.0.1 |
| 3970 |
* @return void |
| 3971 |
*/ |
| 3972 |
public function wpstream_startstreaming_sidebar_meta() { |
| 3973 |
global $post; |
| 3974 |
$term_list = wp_get_post_terms($post->ID, 'product_type'); |
| 3975 |
|
| 3976 |
// Always add the sidebar controls to the free channel post type. |
| 3977 |
add_meta_box( |
| 3978 |
'wpstream-sidebar-meta', |
| 3979 |
esc_html__('Live Streaming', 'wpstream'), |
| 3980 |
array($this,'wpstream_start_stream_meta'), |
| 3981 |
'wpstream_product', |
| 3982 |
'side', |
| 3983 |
'high' |
| 3984 |
); |
| 3985 |
|
| 3986 |
// For WooCommerce products, add it only for live channels / subscription-live events. |
| 3987 |
$is_subscription_live_event = esc_html(get_post_meta($post->ID,'_subscript_live_event',true)); |
| 3988 |
if(!is_wp_error( $term_list )){ |
| 3989 |
if( isset($term_list[0]->name) ){ |
| 3990 |
if( $term_list[0]->name=='live_stream' || ($term_list[0]->name=='subscription' && $is_subscription_live_event=='yes' ) ){ |
| 3991 |
add_meta_box('wpstream-sidebar-meta', esc_html__('Live Streaming', 'wpstream'), array($this,'wpstream_start_stream_meta'), 'product', 'side', 'high'); |
| 3992 |
} |
| 3993 |
} |
| 3994 |
} |
| 3995 |
|
| 3996 |
} |
| 3997 |
|
| 3998 |
/** |
| 3999 |
* edited 4.0 |
| 4000 |
* |
| 4001 |
* Show Streaming controls on sidebar |
| 4002 |
* |
| 4003 |
* Render the sidebar metabox contents: for published channels, the live |
| 4004 |
* stream unit card (plus a basic-streaming flag when out of data) and the |
| 4005 |
* error modal; otherwise a "publish first" prompt. Echoes HTML. |
| 4006 |
* |
| 4007 |
* @since 3.0.1 |
| 4008 |
* @return void |
| 4009 |
*/ |
| 4010 |
public function wpstream_start_stream_meta(){ |
| 4011 |
// Preload the current live-event map for the card renderer. |
| 4012 |
global $live_event_for_user; |
| 4013 |
$live_event_for_user = $this->main->wpstream_live_connection->wpstream_get_live_event_for_user(); |
| 4014 |
|
| 4015 |
global $post; |
| 4016 |
$local_event_options = get_post_meta ($post->ID,'local_event_options',''); |
| 4017 |
|
| 4018 |
|
| 4019 |
|
| 4020 |
// Streaming controls only make sense once the channel is published. |
| 4021 |
if( get_post_status( $post->ID ) === 'publish' ) { |
| 4022 |
// Start-event nonce for the card's ON/OFF button. |
| 4023 |
$ajax_nonce = wp_create_nonce( "wpstream_start_event_nonce" ); |
| 4024 |
print '<input type="hidden" id="wpstream_start_event_nonce" value="'.$ajax_nonce.'">'; |
| 4025 |
|
| 4026 |
// Flag basic-streaming mode when the account has no data left. |
| 4027 |
$pack_details = $this->main->quota_manager->get_live_quota_data( 'wpstream_new_general_set' ); |
| 4028 |
if( isset($pack_details['available_data_mb'])){ |
| 4029 |
if ($pack_details['available_data_mb'] <= 0){ |
| 4030 |
print '<input type="hidden" id="wpstream_basic_streaming" value="true">'; |
| 4031 |
} |
| 4032 |
} |
| 4033 |
|
| 4034 |
// Render the channel card plus the shared modal background and error modal. |
| 4035 |
$this->wpstream_live_stream_unit($post->ID); |
| 4036 |
print '<div class="wpstream_modal_background"></div>'; |
| 4037 |
print '<div class="wpstream_error_modal_notification"><div class="wpstream_error_content">er1</div> |
| 4038 |
<div class="wpstream_error_ok wpstream_button" type="button">'.esc_html__('Close','wpstream').'</div> |
| 4039 |
</div>'; |
| 4040 |
} else { |
| 4041 |
// Not published yet. |
| 4042 |
esc_html_e('To Go Live, please publish your channel first !','wpstream'); |
| 4043 |
} |
| 4044 |
} |
| 4045 |
|
| 4046 |
|
| 4047 |
/** |
| 4048 |
* Whether the account is currently limited to "basic streaming" mode. |
| 4049 |
* |
| 4050 |
* @return bool |
| 4051 |
*/ |
| 4052 |
public function wpstream_is_basic_streaming_mode(){ |
| 4053 |
return $this->main->quota_manager->is_basic_streaming_mode( null, 'wpstream_is_basic_streaming_mode' ); |
| 4054 |
} |
| 4055 |
|
| 4056 |
/** |
| 4057 |
* Whether the account's plan meters streaming by hours (vs. data). |
| 4058 |
* |
| 4059 |
* @return bool |
| 4060 |
*/ |
| 4061 |
public function wpstream_is_use_streaming_hours() { |
| 4062 |
$pack_details = $this->main->quota_manager->get_live_quota_data( 'wpstream_start_channel' ); |
| 4063 |
return $this->main->quota_manager->uses_streaming_hours( $pack_details ); |
| 4064 |
} |
| 4065 |
|
| 4066 |
/** |
| 4067 |
* Cached-only flags for start_streaming.js localization (no API on cold cache). |
| 4068 |
* |
| 4069 |
* @return array{is_basic_streaming: bool, use_streaming_hours: bool} |
| 4070 |
*/ |
| 4071 |
public function wpstream_get_start_streaming_localization_flags() { |
| 4072 |
return $this->main->quota_manager->get_streaming_ui_flags_from_cache(); |
| 4073 |
} |
| 4074 |
|
| 4075 |
|
| 4076 |
|
| 4077 |
/** |
| 4078 |
* Register a hidden dashboard page used as the onboarding wizard endpoint. |
| 4079 |
* |
| 4080 |
* @return void |
| 4081 |
*/ |
| 4082 |
public function add_dashboard_page() { |
| 4083 |
add_dashboard_page( '', '', 'administrator', 'wpstream-onboarding', '' ); |
| 4084 |
} |
| 4085 |
|
| 4086 |
|
| 4087 |
|
| 4088 |
|
| 4089 |
/** |
| 4090 |
* Bootstrap the full-screen onboarding wizard screen (non-AJAX requests). |
| 4091 |
* |
| 4092 |
* @return void |
| 4093 |
*/ |
| 4094 |
public function wpstream_load_onboarding_wizard() { |
| 4095 |
|
| 4096 |
// Check for wizard-specific parameter |
| 4097 |
// Allow plugins to disable the onboarding wizard |
| 4098 |
// Check if current user is allowed to save settings. |
| 4099 |
|
| 4100 |
|
| 4101 |
// Don't load the interface if doing an ajax call. |
| 4102 |
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { |
| 4103 |
return; |
| 4104 |
} |
| 4105 |
|
| 4106 |
// Ensure a current screen is set for the standalone wizard context. |
| 4107 |
set_current_screen(); |
| 4108 |
|
| 4109 |
// Remove an action in the Gutenberg plugin ( not core Gutenberg ) which throws an error. |
| 4110 |
remove_action( 'admin_print_styles', 'gutenberg_block_editor_admin_print_styles' ); |
| 4111 |
|
| 4112 |
// Hand off to the actual wizard renderer. |
| 4113 |
$this->actual_load_onboarding_wizard(); |
| 4114 |
|
| 4115 |
} |
| 4116 |
|
| 4117 |
|
| 4118 |
/* |
| 4119 |
* Add on boarding to footer |
| 4120 |
*/ |
| 4121 |
/** |
| 4122 |
* On the onboarding admin page, print the onboarding modal into the footer. |
| 4123 |
* |
| 4124 |
* @return void |
| 4125 |
*/ |
| 4126 |
public function wpstream_admin_footer_onboarding(){ |
| 4127 |
if( isset($_GET['page']) && $_GET['page']==='wpstream_onboard') { |
| 4128 |
$this->wpstream_onboard_display(); |
| 4129 |
} |
| 4130 |
} |
| 4131 |
|
| 4132 |
|
| 4133 |
/* |
| 4134 |
* On Board Display |
| 4135 |
* |
| 4136 |
*/ |
| 4137 |
/** |
| 4138 |
* Render the Quick Start landing screen (logo, intro, "Start the Guide" |
| 4139 |
* button and the script that opens the onboarding wizard). Echoes HTML. |
| 4140 |
* |
| 4141 |
* @return void |
| 4142 |
*/ |
| 4143 |
public function wpstream_pre_onboard_display(){ |
| 4144 |
// Quota summary header for the Quick Start page. |
| 4145 |
$pack_details = $this->main->quota_manager->get_live_quota_data( 'wpstream_pre_onboard_display' ); |
| 4146 |
$this->main->show_user_data($pack_details); |
| 4147 |
|
| 4148 |
// Onboarding logo used in the intro card. |
| 4149 |
$thumb= plugin_dir_url( dirname( __FILE__ ) ). 'img/logo_onboarding.svg'; |
| 4150 |
// Below: Quick Start intro markup plus a script that reveals the onboarding wizard modal. |
| 4151 |
?> |
| 4152 |
<div id="wpstream-onboarding-root"></div> |
| 4153 |
<div class="wpstream_quick_start_wrapper"> |
| 4154 |
<img class="wpstream_onboarding_logo" src="<?php echo esc_url($thumb); ?>" /> |
| 4155 |
|
| 4156 |
<h1><?php print esc_html__('WpStream','wpstream').' <span class="header_special">'.esc_html__('Quick Start','wpstream').'</span>';?></h1> |
| 4157 |
|
| 4158 |
|
| 4159 |
<p> |
| 4160 |
<?php esc_html_e('The quick start guide will help you set up Live Streaming, Video On Demand, and Monetization in a fun and interactive way. Give it a shot! ','wpstream');?> |
| 4161 |
</p> |
| 4162 |
|
| 4163 |
<div id="wpstream_trigger_quick_start" class="wpstream_button wpstream_button_action"><?php esc_html_e('Start the Guide','wpstream');?></div> |
| 4164 |
|
| 4165 |
|
| 4166 |
|
| 4167 |
</div> |
| 4168 |
|
| 4169 |
|
| 4170 |
|
| 4171 |
<script type="text/javascript"> |
| 4172 |
//<![CDATA[ |
| 4173 |
jQuery(document).ready(function(){ |
| 4174 |
jQuery(".wpstream_on_boarding_wrapper").show(); |
| 4175 |
jQuery(".wpstream_modal_background_onboard").show(); |
| 4176 |
}); |
| 4177 |
|
| 4178 |
//]]> |
| 4179 |
</script> |
| 4180 |
|
| 4181 |
<?php |
| 4182 |
|
| 4183 |
} |
| 4184 |
|
| 4185 |
|
| 4186 |
|
| 4187 |
|
| 4188 |
|
| 4189 |
|
| 4190 |
|
| 4191 |
|
| 4192 |
/* |
| 4193 |
* |
| 4194 |
* On Boarding Content |
| 4195 |
* |
| 4196 |
*/ |
| 4197 |
/** |
| 4198 |
* Emit the full onboarding wizard markup by rendering each step in order |
| 4199 |
* (header, account, path choice, live/VOD branches, footer). Echoes HTML. |
| 4200 |
* |
| 4201 |
* @return void |
| 4202 |
*/ |
| 4203 |
public function wpstream_onboard_display() { |
| 4204 |
// Render each wizard step sequentially into the modal. |
| 4205 |
$this->onboarding_wizard_header(); |
| 4206 |
$this->wpstream_onboarding_step1(); |
| 4207 |
$this->wpstream_onboarding_step2(); |
| 4208 |
$this->wpstream_onboarding_step3_live_streaming(); |
| 4209 |
$this->wpstream_onboarding_step_3_A_live_streaming_free_view(); |
| 4210 |
$this->wpstream_onboarding_step_3_B_live_streaming_pay_per_view(); |
| 4211 |
$this->wpstream_onboarding_step4_vod(); |
| 4212 |
$this->wpstream_onboarding_step_4_free_vod(); |
| 4213 |
$this->wpstream_onboarding_step_4_ppv_vod(); |
| 4214 |
$this->onboarding_wizard_footer(); |
| 4215 |
} |
| 4216 |
|
| 4217 |
|
| 4218 |
/* |
| 4219 |
* |
| 4220 |
* On Boarding Step 1 - the login/register |
| 4221 |
* |
| 4222 |
*/ |
| 4223 |
/** |
| 4224 |
* Onboarding step 1: WpStream account login/registration UI, including the |
| 4225 |
* ALTCHA captcha widget (on HTTPS) and a hidden generated registration |
| 4226 |
* password. Emits a marker div when a token already exists. Echoes HTML. |
| 4227 |
* |
| 4228 |
* @return void |
| 4229 |
*/ |
| 4230 |
public function wpstream_onboarding_step1(){ |
| 4231 |
|
| 4232 |
// Login form field definitions (username + password). |
| 4233 |
$wpstream_options_array =array( |
| 4234 |
2 => array( |
| 4235 |
'label' => 'WpStream.net Username or Email', |
| 4236 |
'name' => 'api_username', |
| 4237 |
'type' => 'text', |
| 4238 |
), |
| 4239 |
3 => array( |
| 4240 |
'label' => 'WpStream.net Password', |
| 4241 |
'name' => 'api_password', |
| 4242 |
'type' => 'password', |
| 4243 |
), |
| 4244 |
|
| 4245 |
); |
| 4246 |
// Existing auth token (if already connected) checked at the end. |
| 4247 |
$token = $this->main->wpstream_live_connection->wpstream_get_token(); |
| 4248 |
|
| 4249 |
// Below: the account step markup (login form, register form, captcha, nonce). |
| 4250 |
?> |
| 4251 |
<div class="wpstream_step_wrapper wpstream_step_1" id="wpstream_step_1"> |
| 4252 |
<div class="wpstream_has_credential"> |
| 4253 |
<h1><?php esc_html_e('WpStream Account','wpstream');?></h1> |
| 4254 |
|
| 4255 |
<div class="wpstream_on_board_login_wrapper_explanations"> |
| 4256 |
<?php esc_html_e('A WpStream account is required to make use of the plugin.','wpstream');?> |
| 4257 |
</div> |
| 4258 |
|
| 4259 |
|
| 4260 |
<div class="wpstream_check_account_status"> |
| 4261 |
<?php esc_html_e( 'Checking if you are already logged.....', 'wpstream' ); ?> |
| 4262 |
</div> |
| 4263 |
|
| 4264 |
<div class="wpstream_onboarding_notification"></div> |
| 4265 |
|
| 4266 |
<div class="wpstream_option_wrapper wpstream_on_board_login_wrapper"> |
| 4267 |
<h2><?php esc_html_e('Login with your WpStream Account','wpstream');?></h2> |
| 4268 |
<?php |
| 4269 |
// Render each login field pre-filled with its stored value. |
| 4270 |
foreach ($wpstream_options_array as $key=>$option){ |
| 4271 |
print '<div class="wpstream_option">'; |
| 4272 |
|
| 4273 |
$options_value = esc_html( get_option('wpstream_'.$option['name'],'') ); |
| 4274 |
print '<label for="'.$option['name'].'">'.$option['label'].'</label>'; |
| 4275 |
print '<input id="'.$option['name'].'" type="'.$option['type'].'" size="36" name="'.$option['name'].'" value="'.esc_html($options_value).'" />'; |
| 4276 |
|
| 4277 |
print '</div>'; |
| 4278 |
} |
| 4279 |
?> |
| 4280 |
<input type="submit" name="submit" class="wpstream_button wpstream_button_action wpstream_onboard_login" value="<?php esc_html_e('Login','wpstream');?>" /> |
| 4281 |
</div> |
| 4282 |
|
| 4283 |
|
| 4284 |
|
| 4285 |
|
| 4286 |
<div class="wpstream_on_board_register_wrapper"> |
| 4287 |
<h2><?php esc_html_e('Register for a WpStream Account','wpstream');?></h2> |
| 4288 |
<div class="wpstream_option"> |
| 4289 |
<label for="wpstream_register_email"><?php esc_html_e('Your Email','wpstream');?></label> |
| 4290 |
<input id="wpstream_register_email" type="text" size="36" name="wpstream_register_email" value="<?php echo get_option('admin_email'); ?>" /> |
| 4291 |
|
| 4292 |
</div> |
| 4293 |
|
| 4294 |
<div class="wpstream_option"> |
| 4295 |
<!-- <label for="wpstream_register_password">--><?php //esc_html_e('Your Password','wpstream');?><!--</label>--> |
| 4296 |
<input id="wpstream_register_password" hidden type="text" size="36" name="wpstream_register_password" value="<?php echo $this->randomPassword();?>" /> |
| 4297 |
<span class="" ><?php esc_html_e('We\'ll send the password to the email you attached. ', 'wpstream') ?></span> |
| 4298 |
</div> |
| 4299 |
|
| 4300 |
|
| 4301 |
<!-- Altcha Widget (requires HTTPS/secure context) --> |
| 4302 |
<?php if ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ) : ?> |
| 4303 |
<script async defer src="https://cdn.jsdelivr.net/gh/altcha-org/altcha/dist/altcha.min.js" type="module"></script> |
| 4304 |
<div class="wpstream_option" style="display:none;"> |
| 4305 |
<altcha-widget |
| 4306 |
challengeurl="<?php echo esc_url( WPSTREAM_API . '/v2/user/getcaptcha' ); ?>" |
| 4307 |
name="altcha" |
| 4308 |
auto="onload" |
| 4309 |
hidefooter |
| 4310 |
hidelogo |
| 4311 |
strings='{"label": "<?php esc_html_e('I am not a robot', 'wpstream'); ?>", "error": "<?php esc_html_e('Verification failed', 'wpstream'); ?>", "wait": "<?php esc_html_e('Verifying...', 'wpstream'); ?>"}' |
| 4312 |
></altcha-widget> |
| 4313 |
</div> |
| 4314 |
<?php endif; ?> |
| 4315 |
|
| 4316 |
<div class="wpstream_option wpstream_terms_agreement"> |
| 4317 |
<!-- Add "by registering you agree to the privacy terms" checkbox--> |
| 4318 |
<input id="wpstream_register_privacy" type="checkbox" name="wpstream_register_privacy" /> |
| 4319 |
<label for="wpstream_register_privacy"> |
| 4320 |
<?php printf( |
| 4321 |
esc_html__('By registering you agree to the %sPrivacy Policy%s','wpstream'), |
| 4322 |
'<a href="https://wpstream.net/privacy-policy/" target="_blank">', |
| 4323 |
'</a>' |
| 4324 |
);?> |
| 4325 |
</label> |
| 4326 |
</div> |
| 4327 |
<input type="submit" name="submit" class="wpstream_button wpstream_button_action wpstream_onboard_register" value="<?php esc_html_e('register','wpstream');?>" /> |
| 4328 |
</div> |
| 4329 |
|
| 4330 |
|
| 4331 |
<div id="wpstream_onboarding_action_login"><?php esc_html_e('I already have a WpStream Account','wpstream');?></div> |
| 4332 |
|
| 4333 |
<div id="wpstream_onboarding_action_register"><?php esc_html_e('Back to Registration','wpstream');?></div> |
| 4334 |
|
| 4335 |
</div> |
| 4336 |
|
| 4337 |
|
| 4338 |
<?php |
| 4339 |
// Nonce protecting the onboarding AJAX actions. |
| 4340 |
$ajax_nonce = wp_create_nonce( "wpstream_onboarding_nonce"); |
| 4341 |
print'<input type="hidden" id="wpstream_onboarding_nonce" value="'.esc_html($ajax_nonce).'" /> '; |
| 4342 |
|
| 4343 |
?> |
| 4344 |
|
| 4345 |
|
| 4346 |
<div id="wpstream_on_board" class="wpstream_action_next_step" data-nextthing="wpstream_step_2" style="display:none;" >Move to step 2</div> |
| 4347 |
</div> |
| 4348 |
<?php |
| 4349 |
|
| 4350 |
// If already authenticated, emit a marker the wizard JS uses to skip login. |
| 4351 |
if( !is_null( $token ) && trim( $token ) !== '' ) { |
| 4352 |
print '<div id="wpstream_have_token"></div>'; |
| 4353 |
} |
| 4354 |
} |
| 4355 |
|
| 4356 |
|
| 4357 |
|
| 4358 |
|
| 4359 |
|
| 4360 |
|
| 4361 |
|
| 4362 |
|
| 4363 |
|
| 4364 |
|
| 4365 |
/* |
| 4366 |
* |
| 4367 |
* Generate random pass |
| 4368 |
* |
| 4369 |
*/ |
| 4370 |
/** |
| 4371 |
* Generate a random 16-character alphanumeric password (used to pre-fill |
| 4372 |
* the onboarding registration form). |
| 4373 |
* |
| 4374 |
* @return string The generated password. |
| 4375 |
*/ |
| 4376 |
public function randomPassword() { |
| 4377 |
// Character pool for the password. |
| 4378 |
$alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; |
| 4379 |
$pass = array(); //remember to declare $pass as an array |
| 4380 |
$alphaLength = strlen($alphabet) - 1; //put the length -1 in cache |
| 4381 |
// Pick 16 cryptographically-random characters. |
| 4382 |
for ($i = 0; $i < 16; $i++) { |
| 4383 |
$n = random_int(0, $alphaLength); |
| 4384 |
$pass[] = $alphabet[$n]; |
| 4385 |
} |
| 4386 |
return implode($pass); //turn the array into a string |
| 4387 |
} |
| 4388 |
|
| 4389 |
|
| 4390 |
/* |
| 4391 |
* |
| 4392 |
* |
| 4393 |
* |
| 4394 |
*/ |
| 4395 |
/** |
| 4396 |
* Onboarding step 2: choose a starting path (Go Live vs create a VOD). Echoes HTML. |
| 4397 |
* |
| 4398 |
* @return void |
| 4399 |
*/ |
| 4400 |
public function wpstream_onboarding_step2(){ |
| 4401 |
// Below: the path-choice step markup. |
| 4402 |
?> |
| 4403 |
|
| 4404 |
<div class="wpstream_step_wrapper wpstream_step_2" id="wpstream_step_2"> |
| 4405 |
<h1>Welcome to <span class="header_special">WpStream</span>! How would you like to start?</h1> |
| 4406 |
|
| 4407 |
<div class="wpstream_accordion_header wpstream_action_next_step" data-nextthing="wpstream_step_3a" ><?php esc_html_e('Go LIVE!','wpstream');?></div> |
| 4408 |
<div class="wpstram_or">or</div> |
| 4409 |
<div class="wpstream_accordion_header wpstream_action_next_step wpstream_step_2_create_vod" data-nextthing="wpstream_step_4a" ><?php esc_html_e('Create a Video-On-Demand (VOD)','wpstream');?></div> |
| 4410 |
|
| 4411 |
</div> |
| 4412 |
|
| 4413 |
|
| 4414 |
<?php |
| 4415 |
} |
| 4416 |
|
| 4417 |
/* |
| 4418 |
* |
| 4419 |
* |
| 4420 |
* |
| 4421 |
*/ |
| 4422 |
/** |
| 4423 |
* Onboarding step 3 (live): choose Free-To-View vs Pay-Per-View. Echoes HTML. |
| 4424 |
* |
| 4425 |
* @return void |
| 4426 |
*/ |
| 4427 |
public function wpstream_onboarding_step3_live_streaming(){ |
| 4428 |
// Below: the FTV/PPV choice step for live streaming. |
| 4429 |
?> |
| 4430 |
|
| 4431 |
<div class="wpstream_step_wrapper wpstream_step_3 wpstream_onboarding_live" id="wpstream_step_3"> |
| 4432 |
<h1><?php esc_html_e('Do you want to charge a fee for watching?','wpstream'); ?></h1> |
| 4433 |
|
| 4434 |
<div class="wpstream_accordion_header wpstream_action_next_step wpstream_step_3a" data-nextthing="wpstream_step_3a"><?php esc_html_e('No - Free-To-View (FTV)','wpstream');?></div> |
| 4435 |
<div class="wpstram_or">or</div> |
| 4436 |
<div class="wpstream_accordion_header wpstream_action_next_step" data-nextthing="wpstream_step_3b" ><?php esc_html_e('Yes - Pay-Per-View (PPV)','wpstream');?></div> |
| 4437 |
|
| 4438 |
<div class="wpstream_initial_onboarding_controls_wrapper"> |
| 4439 |
<span class="wpstream_onboard_initial_bubble_prev" data-step="wpstream_step_2"><?php esc_html_e('Prev','wpstream');?></span> |
| 4440 |
</div> |
| 4441 |
|
| 4442 |
</div> |
| 4443 |
|
| 4444 |
|
| 4445 |
<?php |
| 4446 |
} |
| 4447 |
|
| 4448 |
|
| 4449 |
/* |
| 4450 |
* |
| 4451 |
* |
| 4452 |
* |
| 4453 |
*/ |
| 4454 |
/** |
| 4455 |
* Onboarding step 3A: create the first Free-To-View live channel (name + |
| 4456 |
* create button). Echoes HTML. |
| 4457 |
* |
| 4458 |
* @return void |
| 4459 |
*/ |
| 4460 |
public function wpstream_onboarding_step_3_A_live_streaming_free_view(){ |
| 4461 |
// Below: the create-FTV-channel step markup. |
| 4462 |
?> |
| 4463 |
|
| 4464 |
<div class="wpstream_step_wrapper wpstream_step_3a wpstream_onboarding_live" id="wpstream_step_3a"> |
| 4465 |
<h1><?php esc_html_e('Let’s create your first FTV live channel','wpstream');?></h1> |
| 4466 |
|
| 4467 |
<div id="wpstream_onboard_live_notice" class="wpstream_onboarding_notification"></div> |
| 4468 |
<div id="wpstream_onboard_live" class="wpstream_accordion_container"> |
| 4469 |
<label>Name your first Free-To-View channel</label> |
| 4470 |
<input type="text" name="channel_name" id="wpstream_onboarding_channel_name" class="wpstream_onboarding_channel_name" value="<?php esc_html_e('My first FTV channel','wpstream');?>" > |
| 4471 |
<input type="submit" name="submit" id="wpstream_on_board_create_channel" class="wpstream_button wpstream_button_action wpstream_onboard_live_action" value="<?php esc_html_e('Create Channel','wpstream');?>" /> |
| 4472 |
|
| 4473 |
</div> |
| 4474 |
|
| 4475 |
<div class="wpstream_initial_onboarding_controls_wrapper"> |
| 4476 |
<span class="wpstream_onboard_initial_bubble_prev" data-step="wpstream_step_2"><?php esc_html_e('Prev','wpstream');?></span> |
| 4477 |
</div> |
| 4478 |
|
| 4479 |
</div> |
| 4480 |
|
| 4481 |
|
| 4482 |
<?php |
| 4483 |
} |
| 4484 |
|
| 4485 |
|
| 4486 |
/* |
| 4487 |
* |
| 4488 |
* |
| 4489 |
* |
| 4490 |
*/ |
| 4491 |
|
| 4492 |
/** |
| 4493 |
* Onboarding step 3B: create the first Pay-Per-View live channel (name + |
| 4494 |
* price). Requires WooCommerce; otherwise shows the Woo warning. Echoes HTML. |
| 4495 |
* |
| 4496 |
* @return void |
| 4497 |
*/ |
| 4498 |
public function wpstream_onboarding_step_3_B_live_streaming_pay_per_view(){ |
| 4499 |
// Below: the create-PPV-channel step markup. |
| 4500 |
?> |
| 4501 |
|
| 4502 |
<div class="wpstream_step_wrapper wpstream_step_3b wpstream_onboarding_live" id="wpstream_step_3b"> |
| 4503 |
<h1><?php esc_html_e('Make your live stream Pay Per View','wpstream');?></h1> |
| 4504 |
<?php |
| 4505 |
// PPV needs WooCommerce for the product; show a warning when missing. |
| 4506 |
if ( class_exists( 'WooCommerce' ) ) { |
| 4507 |
?> |
| 4508 |
<div id="wpstream_onboard_live_ppv_notice" class="wpstream_onboarding_notification"></div> |
| 4509 |
<div id="wpstream_onboard_live_ppv" class="wpstream_accordion_container"> |
| 4510 |
<label><?php esc_html_e('Choose a name for your channel','wpstream');?></label> |
| 4511 |
<input type="text" name="channel_name" id="wpstream_onboarding_channel_name_ppv" class="wpstream_onboarding_channel_name" value="<?php esc_html_e('My First PPV Channel','wpstream');?>"> |
| 4512 |
<label><?php esc_html_e('Pay-Per-View Price ($)','wpstream');?></label> |
| 4513 |
<input type="text" name="channel_name" id="wpstream_onboarding_event_price_ppv" class="wpstream_onboarding_event_price" value="10"> |
| 4514 |
<input type="submit" name="submit" id="wpstream_onboard_live_ppv_action" class="wpstream_button wpstream_button_action wpstream_onboard_live_ppv_action" value="<?php esc_html_e('Create Channel','wpstream');?>" /> |
| 4515 |
|
| 4516 |
</div> |
| 4517 |
<?php |
| 4518 |
} else { |
| 4519 |
$this->wpstream_onboarding_woo_warning(); |
| 4520 |
} ?> |
| 4521 |
|
| 4522 |
|
| 4523 |
<div class="wpstream_initial_onboarding_controls_wrapper"> |
| 4524 |
<span class="wpstream_onboard_initial_bubble_prev" data-step="wpstream_step_2"><?php esc_html_e('Prev','wpstream');?></span> |
| 4525 |
</div> |
| 4526 |
</div> |
| 4527 |
<?php |
| 4528 |
} |
| 4529 |
|
| 4530 |
|
| 4531 |
|
| 4532 |
|
| 4533 |
|
| 4534 |
|
| 4535 |
/* |
| 4536 |
* WooCommerce not installed Warning |
| 4537 |
* |
| 4538 |
* |
| 4539 |
*/ |
| 4540 |
/** |
| 4541 |
* Print the "PPV needs WooCommerce" warning with install / try-again actions. |
| 4542 |
* |
| 4543 |
* @return void |
| 4544 |
*/ |
| 4545 |
public function wpstream_onboarding_woo_warning(){ |
| 4546 |
print'<div class="wpstream_warning_onboarding"> |
| 4547 |
'.esc_html__('Pay-Per-View streaming requires WooCommerce. Please install the WooCommerce plugin and try again.','wpstream').' |
| 4548 |
</br> |
| 4549 |
<div class="wpstream_install_plugin">'. esc_html__('Install WooCommerce','wpstream').'</div> |
| 4550 |
|
| 4551 |
<div class="wpstream_onboarding_tryagain">'.esc_html__('Try Again','wpstream').'</div> |
| 4552 |
</div>'; |
| 4553 |
} |
| 4554 |
|
| 4555 |
|
| 4556 |
/* |
| 4557 |
* |
| 4558 |
* |
| 4559 |
* |
| 4560 |
*/ |
| 4561 |
|
| 4562 |
/** |
| 4563 |
* Onboarding step 4 (VOD): choose Free-To-View vs Pay-Per-View. Echoes HTML. |
| 4564 |
* |
| 4565 |
* @return void |
| 4566 |
*/ |
| 4567 |
public function wpstream_onboarding_step4_vod(){ |
| 4568 |
// Below: the FTV/PPV choice step for VOD. |
| 4569 |
?> |
| 4570 |
<div class="wpstream_step_wrapper wpstream_step_4 wpstream_onboarding_vod" id="wpstream_step_4"> |
| 4571 |
<h1><?php esc_html_e('Do you want to charge a fee for watching?','wpstream');?></h1> |
| 4572 |
|
| 4573 |
<div class="wpstream_accordion_header wpstream_action_next_step wpstream_step_4a" data-control="wpstream_onboard_vod_free" data-nextthing="wpstream_step_4a" ><?php esc_html_e('No - Free-To-View (FTV)','wpstream'); ?></div> |
| 4574 |
<div class="wpstram_or">or</div> |
| 4575 |
<input type="hidden" id="wpstream_onboarding_video_list_nonce" value="<?php echo wp_create_nonce( "wpstream_onboarding_video_list_nonce" ); ?>"> |
| 4576 |
<div class="wpstream_accordion_header wpstream_action_next_step wpstream_step_4b" data-control="wpstream_onboard_vod_ppv" data-nextthing="wpstream_step_4b" ><?php esc_html_e('Yes - Pay-Per-View (PPV)','wpstream'); ?></div> |
| 4577 |
<div class="wpstream_initial_onboarding_controls_wrapper"> |
| 4578 |
<span class="wpstream_onboard_initial_bubble_prev" data-step="wpstream_step_2"><?php esc_html_e('Prev','wpstream');?></span> |
| 4579 |
</div> |
| 4580 |
|
| 4581 |
</div> |
| 4582 |
<?php |
| 4583 |
} |
| 4584 |
|
| 4585 |
|
| 4586 |
/* |
| 4587 |
* |
| 4588 |
* |
| 4589 |
* |
| 4590 |
*/ |
| 4591 |
/** |
| 4592 |
* Onboarding step 4A: create the first Free-To-View VOD (name + recording |
| 4593 |
* chooser). Only rendered on the dedicated onboarding page. Echoes HTML. |
| 4594 |
* |
| 4595 |
* @return void |
| 4596 |
*/ |
| 4597 |
public function wpstream_onboarding_step_4_free_vod(){ |
| 4598 |
|
| 4599 |
// Only render on the standalone onboarding admin page. |
| 4600 |
$current_screen=get_current_screen(); |
| 4601 |
if($current_screen->base !=='wpstream_page_wpstream_onboard'){ |
| 4602 |
return; |
| 4603 |
} |
| 4604 |
// Below: the create-FTV-VOD step markup. |
| 4605 |
?> |
| 4606 |
|
| 4607 |
<div class="wpstream_step_wrapper wpstream_step_4a wpstream_onboarding_live" id="wpstream_step_4a"> |
| 4608 |
<h1><?php esc_html_e('Let’s create your first Free-To-View VOD','wpstream');?></h1> |
| 4609 |
<div id="wpstream_onboard_vod_free_notice" class="wpstream_onboarding_notification"></div> |
| 4610 |
<div id="wpstream_onboard_vod_free" class="wpstream_accordion_container"> |
| 4611 |
<div class="spinner"></div> |
| 4612 |
<div class="wpstream-step-container" style="display: none"> |
| 4613 |
<label><?php esc_html_e('Name your FTV Video-On-Demand','wpstream')?></label> |
| 4614 |
<input type="text" name="channel_name" class="wpstream_onboarding_vod_name" id="wpstream_onboarding_vod_name" value="<?php esc_html_e('My First FTV VOD','wpstream');?>" > |
| 4615 |
<div id="wpstream_free_vod_dropdown_videos_list"></div> |
| 4616 |
<input type="submit" name="submit" class="wpstream_button wpstream_button_action wpstream_onboard_vod_free_action" id="wpstream_onboard_vod_free_action" value="Create FTV VOD"> |
| 4617 |
</div> |
| 4618 |
</div> |
| 4619 |
<?php $this->wpstream_obboarding_file_warning(); ?> |
| 4620 |
|
| 4621 |
<div class="wpstream_initial_onboarding_controls_wrapper"> |
| 4622 |
<span class="wpstream_onboard_initial_bubble_prev" data-step="wpstream_step_4"><?php esc_html_e('Prev','wpstream');?></span> |
| 4623 |
</div> |
| 4624 |
|
| 4625 |
</div> |
| 4626 |
|
| 4627 |
|
| 4628 |
<?php |
| 4629 |
} |
| 4630 |
|
| 4631 |
/* |
| 4632 |
* |
| 4633 |
* |
| 4634 |
* |
| 4635 |
*/ |
| 4636 |
/** |
| 4637 |
* Print the "no recordings available for a VOD" warning with upload / |
| 4638 |
* try-again actions (hidden until the JS detects no recordings). |
| 4639 |
* |
| 4640 |
* @return void |
| 4641 |
*/ |
| 4642 |
public function wpstream_obboarding_file_warning(){ |
| 4643 |
print '<div class="wpstream_warning_onboarding" style="display: none"> |
| 4644 |
'.esc_html__('A recording is needed to create a VOD from. There are no recordings under your account. You can create new recordings by recording a live channel or uploading video files directly.','wpstream').' |
| 4645 |
</br> |
| 4646 |
<div class="wpstream_upload_video">'.esc_html__('Upload Video','wpstream').'</div> |
| 4647 |
|
| 4648 |
<div class="wpstream_onboarding_tryagain">'.esc_html__('Try Again','wpstream').'</div> |
| 4649 |
</div>'; |
| 4650 |
} |
| 4651 |
|
| 4652 |
/* |
| 4653 |
* |
| 4654 |
* |
| 4655 |
* |
| 4656 |
*/ |
| 4657 |
/** |
| 4658 |
* Onboarding step 4B: create the first Pay-Per-View VOD (name + recording |
| 4659 |
* chooser + price). Requires WooCommerce and the onboarding page. Echoes HTML. |
| 4660 |
* |
| 4661 |
* @return void |
| 4662 |
*/ |
| 4663 |
public function wpstream_onboarding_step_4_ppv_vod(){ |
| 4664 |
// Only render on the standalone onboarding admin page. |
| 4665 |
$current_screen=get_current_screen(); |
| 4666 |
|
| 4667 |
if($current_screen->base !=='wpstream_page_wpstream_onboard'){ |
| 4668 |
return; |
| 4669 |
} |
| 4670 |
// Below: the create-PPV-VOD step markup. |
| 4671 |
?> |
| 4672 |
|
| 4673 |
<div class="wpstream_step_wrapper wpstream_step_4b wpstream_onboarding_live" id="wpstream_step_4b"> |
| 4674 |
<h1><?php esc_html_e('Let\'s create your first Pay-Per-View VOD','wpstream');?></h1> |
| 4675 |
|
| 4676 |
<div id="wpstream_onboard_vod_ppv_notice" class="wpstream_onboarding_notification"></div> |
| 4677 |
<?php |
| 4678 |
// PPV VOD needs WooCommerce; otherwise show the Woo warning. |
| 4679 |
if ( class_exists( 'WooCommerce' ) ) { ?> |
| 4680 |
<div id="wpstream_onboard_vod_ppv" class="wpstream_accordion_container"> |
| 4681 |
<div class="spinner"></div> |
| 4682 |
<div class="wpstream-step-container" style="display: none"> |
| 4683 |
<label><?php esc_html_e('Name your PPV Video-On-Demand','wpstream'); ?></label> |
| 4684 |
<input type="text" name="channel_name" class="wpstream_onboarding_ppv_vod_name" id="wpstream_onboarding_ppv_vod_name" value="<?php esc_html_e('My First PPV VOD','wpstream');?>"> |
| 4685 |
<div id="wpstream_ppv_vod_dropdown_videos_list"></div> |
| 4686 |
<label><?php esc_html_e('Pay-Per-View Price','wpstream');?></label> |
| 4687 |
<input type="text" name="channel_name" class="wpstream_onboarding_vod_price" id="wpstream_onboarding_vod_price" value="10"> |
| 4688 |
<input type="submit" name="submit" class="wpstream_button wpstream_button_action wpstream_onboard_vod_ppv_action" id="wpstream_onboard_vod_ppv_action" value="<?php esc_html_e('Create PPV VOD','wpstream');?>" /> |
| 4689 |
</div> |
| 4690 |
</div> |
| 4691 |
<?php $this->wpstream_obboarding_file_warning(); ?> |
| 4692 |
<?php } else { |
| 4693 |
$this->wpstream_onboarding_woo_warning(); |
| 4694 |
} ?> |
| 4695 |
<div class="wpstream_initial_onboarding_controls_wrapper"> |
| 4696 |
<span class="wpstream_onboard_initial_bubble_prev" data-step="wpstream_step_4"><?php esc_html_e('Prev','wpstream');?></span> |
| 4697 |
</div> |
| 4698 |
</div> |
| 4699 |
<?php |
| 4700 |
} |
| 4701 |
|
| 4702 |
|
| 4703 |
|
| 4704 |
|
| 4705 |
/* |
| 4706 |
* |
| 4707 |
* On boarding Header |
| 4708 |
* |
| 4709 |
*/ |
| 4710 |
/** |
| 4711 |
* Open the onboarding wizard wrapper (logo + close controls). Echoes HTML. |
| 4712 |
* |
| 4713 |
* @return void |
| 4714 |
*/ |
| 4715 |
public function onboarding_wizard_header() { |
| 4716 |
$thumb= plugin_dir_url( dirname( __FILE__ ) ). 'img/logo_onboarding.svg'; |
| 4717 |
// Below: the wizard wrapper opening markup (closed by onboarding_wizard_footer()). |
| 4718 |
?> |
| 4719 |
|
| 4720 |
<div class="wpstream_on_boarding_wrapper"> |
| 4721 |
<div class="wpstream_close_onboarding wpstream_close_initial_onboarding"></div> |
| 4722 |
<img class="wpstream_onboarding_logo" src="<?php echo esc_url($thumb); ?>" /> |
| 4723 |
<div class="wpstream_close_onboarding_warning"></div> |
| 4724 |
|
| 4725 |
<?php |
| 4726 |
} |
| 4727 |
|
| 4728 |
|
| 4729 |
|
| 4730 |
|
| 4731 |
|
| 4732 |
/* |
| 4733 |
* |
| 4734 |
* On Boarding Footer |
| 4735 |
* |
| 4736 |
*/ |
| 4737 |
/** |
| 4738 |
* Close the onboarding wizard wrapper and print its modal background. Echoes HTML. |
| 4739 |
* |
| 4740 |
* @return void |
| 4741 |
*/ |
| 4742 |
public function onboarding_wizard_footer() { |
| 4743 |
// Below: closes the wrapper opened by onboarding_wizard_header() and adds the modal backdrop. |
| 4744 |
?> |
| 4745 |
</div> |
| 4746 |
<div class="wpstream_modal_background_onboard"></div> |
| 4747 |
<?php |
| 4748 |
} |
| 4749 |
|
| 4750 |
|
| 4751 |
|
| 4752 |
/* |
| 4753 |
* |
| 4754 |
* On Boarding create PPV channel |
| 4755 |
* |
| 4756 |
*/ |
| 4757 |
|
| 4758 |
|
| 4759 |
/** |
| 4760 |
* AJAX: create a Pay-Per-View live channel during onboarding. |
| 4761 |
* |
| 4762 |
* Inserts a WooCommerce `product`, sets its price, marks it live_stream, |
| 4763 |
* and returns the edit link (with onboarding query args) as JSON. |
| 4764 |
* Nonce- and administrator-gated. |
| 4765 |
* |
| 4766 |
* @return void Ends with die(). |
| 4767 |
*/ |
| 4768 |
public function wpstream_on_board_create_channel_ppv(){ |
| 4769 |
check_ajax_referer( 'wpstream_onboarding_nonce', 'security' ); |
| 4770 |
$current_user = wp_get_current_user(); |
| 4771 |
|
| 4772 |
if(current_user_can('administrator')){ |
| 4773 |
// Sanitize the submitted channel name and price. |
| 4774 |
$channel_name = sanitize_text_field($_POST['channel_name']); |
| 4775 |
$channel_price = floatval($_POST['channel_price']); |
| 4776 |
$my_post = array( |
| 4777 |
'post_title' => $channel_name, |
| 4778 |
'post_content' => '', |
| 4779 |
'post_status' => 'publish', |
| 4780 |
'post_type' => 'product', |
| 4781 |
'post_author' => $current_user->ID |
| 4782 |
); |
| 4783 |
|
| 4784 |
// Insert the post into the database |
| 4785 |
$post_id = wp_insert_post( $my_post ); |
| 4786 |
|
| 4787 |
if(is_wp_error($post_id)){ |
| 4788 |
// Log the failure and report it to the client. |
| 4789 |
$logger = new WPStream_Logger(); |
| 4790 |
$log_entry = new WpStream_Log_Entry([ |
| 4791 |
'type' => 'error', |
| 4792 |
'description' => 'Couldn\'t create channel during onboarding because of error: ' . $post_id->get_error_message(), |
| 4793 |
]); |
| 4794 |
$logger->add( $log_entry ); |
| 4795 |
echo json_encode( array('succes'=>false) ); |
| 4796 |
}else{ |
| 4797 |
|
| 4798 |
// Apply the price to the new product. |
| 4799 |
$product = wc_get_product($post_id); |
| 4800 |
$price = wc_format_decimal($channel_price); |
| 4801 |
|
| 4802 |
$product = wc_get_product( $post_id ); |
| 4803 |
$product->set_price( $price ); |
| 4804 |
$product->set_regular_price( $price ); // To be sure |
| 4805 |
$product->save(); |
| 4806 |
// Reset event flag and mark it as a live stream product. |
| 4807 |
update_post_meta ($post_id,'event_passed',0); |
| 4808 |
wp_set_object_terms( $post_id, 'live_stream', 'product_type' ); |
| 4809 |
|
| 4810 |
|
| 4811 |
|
| 4812 |
|
| 4813 |
// Return the edit link carrying the onboarding continuation args. |
| 4814 |
$permalink = get_edit_post_link($post_id); |
| 4815 |
|
| 4816 |
$permalink= add_query_arg( 'onboard', 'yes', $permalink ); |
| 4817 |
$permalink= add_query_arg( 'branch', '2', $permalink ); |
| 4818 |
|
| 4819 |
|
| 4820 |
echo json_encode( array( |
| 4821 |
'success'=> true, |
| 4822 |
'link' => ($permalink) |
| 4823 |
)); |
| 4824 |
} |
| 4825 |
|
| 4826 |
} |
| 4827 |
die(); |
| 4828 |
} |
| 4829 |
|
| 4830 |
/* |
| 4831 |
* |
| 4832 |
* On Boarding create channel |
| 4833 |
* |
| 4834 |
*/ |
| 4835 |
|
| 4836 |
|
| 4837 |
/** |
| 4838 |
* AJAX: create a Free-To-View live channel during onboarding. |
| 4839 |
* |
| 4840 |
* Inserts a `wpstream_product` post and returns its edit link (with |
| 4841 |
* onboarding query args) as JSON. Nonce- and administrator-gated. |
| 4842 |
* |
| 4843 |
* @return void Ends with die(). |
| 4844 |
*/ |
| 4845 |
public function wpstream_on_board_create_channel(){ |
| 4846 |
check_ajax_referer( 'wpstream_onboarding_nonce', 'security' ); |
| 4847 |
$current_user = wp_get_current_user(); |
| 4848 |
|
| 4849 |
if(current_user_can('administrator')){ |
| 4850 |
// Sanitize the submitted channel name. |
| 4851 |
$channel_name=sanitize_text_field($_POST['channel_name']); |
| 4852 |
$my_post = array( |
| 4853 |
'post_title' => $channel_name, |
| 4854 |
'post_content' => '', |
| 4855 |
'post_status' => 'publish', |
| 4856 |
'post_type' => 'wpstream_product', |
| 4857 |
'post_author' => $current_user->ID |
| 4858 |
); |
| 4859 |
|
| 4860 |
// Insert the post into the database |
| 4861 |
$post_id = wp_insert_post( $my_post ); |
| 4862 |
|
| 4863 |
if( is_wp_error( $post_id ) ) { |
| 4864 |
// Log the failure and report it to the client. |
| 4865 |
$logger = new WPStream_Logger(); |
| 4866 |
$log_entry = new WpStream_Log_Entry([ |
| 4867 |
'type' => 'error', |
| 4868 |
'description' => 'Couldn\'t create channel during onboarding because of error: ' . $post_id->get_error_message(), |
| 4869 |
]); |
| 4870 |
$logger->add( $log_entry ); |
| 4871 |
echo json_encode( array('succes'=>false) ); |
| 4872 |
} else { |
| 4873 |
// Return the edit link carrying the onboarding continuation args. |
| 4874 |
$permalink = get_edit_post_link($post_id); |
| 4875 |
|
| 4876 |
$permalink= add_query_arg( 'onboard', 'yes', $permalink ); |
| 4877 |
$permalink= add_query_arg( 'branch', '1', $permalink ); |
| 4878 |
|
| 4879 |
echo json_encode( array( |
| 4880 |
'success'=> true, |
| 4881 |
'link' => ($permalink) |
| 4882 |
)); |
| 4883 |
} |
| 4884 |
} |
| 4885 |
die(); |
| 4886 |
} |
| 4887 |
|
| 4888 |
/* |
| 4889 |
* |
| 4890 |
* On Boarding create free vod |
| 4891 |
* |
| 4892 |
*/ |
| 4893 |
/** |
| 4894 |
* AJAX: create a Free-To-View VOD during onboarding. |
| 4895 |
* |
| 4896 |
* Inserts a `wpstream_product_vod` post, links the chosen recording, and |
| 4897 |
* returns the edit link (with onboarding query args). Nonce/admin gated. |
| 4898 |
* |
| 4899 |
* @return void Ends with die(). |
| 4900 |
*/ |
| 4901 |
public function wpstream_on_board_create_free_vod(){ |
| 4902 |
$current_user = wp_get_current_user(); |
| 4903 |
check_ajax_referer( 'wpstream_onboarding_nonce', 'security' ); |
| 4904 |
if(current_user_can('administrator')){ |
| 4905 |
// Sanitize the VOD name and the source recording file name. |
| 4906 |
$channel_name =sanitize_text_field($_POST['channel_name']); |
| 4907 |
$file_name =sanitize_text_field($_POST['file_name']); |
| 4908 |
$my_post = array( |
| 4909 |
'post_title' => $channel_name, |
| 4910 |
'post_content' => '', |
| 4911 |
'post_status' => 'publish', |
| 4912 |
'post_type' => 'wpstream_product_vod', |
| 4913 |
'post_author' => $current_user->ID |
| 4914 |
); |
| 4915 |
|
| 4916 |
// Insert the post into the database |
| 4917 |
$post_id = wp_insert_post( $my_post ); |
| 4918 |
|
| 4919 |
if(is_wp_error($post_id)){ |
| 4920 |
$logger = new WPStream_Logger(); |
| 4921 |
$log_entry = new WpStream_Log_Entry([ |
| 4922 |
'type' => 'error', |
| 4923 |
'description' => 'Couldn\'t create free VOD during onboarding because of error: ' . $post_id->get_error_message(), |
| 4924 |
]); |
| 4925 |
$logger->add( $log_entry ); |
| 4926 |
echo json_encode( array('succes'=>false) ); |
| 4927 |
}else{ |
| 4928 |
// Mark as a recording-type VOD and attach the chosen recording. |
| 4929 |
update_post_meta($post_id, 'wpstream_product_type', 2); |
| 4930 |
update_post_meta($post_id, 'wpstream_free_video', $file_name); |
| 4931 |
|
| 4932 |
|
| 4933 |
// Return the edit link carrying the onboarding continuation args. |
| 4934 |
$permalink = get_edit_post_link($post_id); |
| 4935 |
|
| 4936 |
$permalink= add_query_arg( 'onboard', 'yes', $permalink ); |
| 4937 |
$permalink= add_query_arg( 'branch', '3', $permalink ); |
| 4938 |
|
| 4939 |
|
| 4940 |
echo json_encode( array( |
| 4941 |
'success'=> true, |
| 4942 |
'link' => ($permalink) |
| 4943 |
)); |
| 4944 |
} |
| 4945 |
|
| 4946 |
} |
| 4947 |
die(); |
| 4948 |
} |
| 4949 |
|
| 4950 |
|
| 4951 |
/* |
| 4952 |
* |
| 4953 |
* On Boarding create ppv vod |
| 4954 |
* |
| 4955 |
*/ |
| 4956 |
|
| 4957 |
|
| 4958 |
/** |
| 4959 |
* AJAX: create a Pay-Per-View VOD during onboarding. |
| 4960 |
* |
| 4961 |
* Inserts a WooCommerce `product`, sets its price, links the recording, |
| 4962 |
* marks it video_on_demand, and returns the edit link (with onboarding |
| 4963 |
* query args). Nonce- and administrator-gated. |
| 4964 |
* |
| 4965 |
* @return void Ends with die(). |
| 4966 |
*/ |
| 4967 |
public function wpstream_on_board_create_ppv_vod(){ |
| 4968 |
$current_user = wp_get_current_user(); |
| 4969 |
check_ajax_referer( 'wpstream_onboarding_nonce', 'security' ); |
| 4970 |
|
| 4971 |
if(current_user_can('administrator')){ |
| 4972 |
// Sanitize the VOD name, price and source recording file name. |
| 4973 |
$channel_name = sanitize_text_field($_POST['channel_name']); |
| 4974 |
$vod_price = floatval($_POST['vod_price']); |
| 4975 |
$file_name = sanitize_text_field($_POST['file_name']); |
| 4976 |
|
| 4977 |
$my_post = array( |
| 4978 |
'post_title' => $channel_name, |
| 4979 |
'post_content' => '', |
| 4980 |
'post_status' => 'publish', |
| 4981 |
'post_type' => 'product', |
| 4982 |
'post_author' => $current_user->ID |
| 4983 |
); |
| 4984 |
|
| 4985 |
// Insert the post into the database |
| 4986 |
$post_id = wp_insert_post( $my_post ); |
| 4987 |
|
| 4988 |
if( is_wp_error( $post_id ) ) { |
| 4989 |
$logger = new WPStream_Logger(); |
| 4990 |
$log_entry = new WpStream_Log_Entry([ |
| 4991 |
'type' => 'error', |
| 4992 |
'description' => 'Couldn\'t create PPV VOD during onboarding because of error: ' . $post_id->get_error_message(), |
| 4993 |
]); |
| 4994 |
$logger->add( $log_entry ); |
| 4995 |
echo json_encode( array('succes'=>false) ); |
| 4996 |
} else { |
| 4997 |
|
| 4998 |
// Apply the price to the new product. |
| 4999 |
$product = wc_get_product($post_id); |
| 5000 |
$price = wc_format_decimal($vod_price); |
| 5001 |
|
| 5002 |
$product = wc_get_product( $post_id ); |
| 5003 |
$product->set_price( $price ); |
| 5004 |
$product->set_regular_price( $price ); // To be sure |
| 5005 |
$product->save(); |
| 5006 |
// Reset event flag, attach the recording, and mark it VOD. |
| 5007 |
update_post_meta ($post_id,'event_passed',0); |
| 5008 |
update_post_meta ($post_id,'_movie_url', $file_name); |
| 5009 |
wp_set_object_terms( $post_id, 'video_on_demand', 'product_type' ); |
| 5010 |
|
| 5011 |
|
| 5012 |
|
| 5013 |
|
| 5014 |
// Return the edit link carrying the onboarding continuation args. |
| 5015 |
$permalink = get_edit_post_link($post_id); |
| 5016 |
|
| 5017 |
$permalink= add_query_arg( 'onboard', 'yes', $permalink ); |
| 5018 |
$permalink= add_query_arg( 'branch', '4', $permalink ); |
| 5019 |
|
| 5020 |
|
| 5021 |
echo json_encode( array( |
| 5022 |
'success'=> true, |
| 5023 |
'link' => ($permalink) , |
| 5024 |
' $file_name'=> $file_name, |
| 5025 |
)); |
| 5026 |
} |
| 5027 |
|
| 5028 |
} |
| 5029 |
die(); |
| 5030 |
} |
| 5031 |
|
| 5032 |
|
| 5033 |
/* |
| 5034 |
* |
| 5035 |
* On Boarding login |
| 5036 |
* |
| 5037 |
*/ |
| 5038 |
/** |
| 5039 |
* AJAX: log in to WpStream during onboarding. |
| 5040 |
* |
| 5041 |
* Stores the submitted credentials, clears the token cache, then attempts |
| 5042 |
* to fetch a token; returns success/failure JSON without ever exposing the |
| 5043 |
* token to the client. Nonce- and administrator-gated. |
| 5044 |
* |
| 5045 |
* @return void Ends with die(). |
| 5046 |
*/ |
| 5047 |
public function wpstream_on_board_login(){ |
| 5048 |
check_ajax_referer( 'wpstream_onboarding_nonce', 'security' ); |
| 5049 |
|
| 5050 |
if(current_user_can('administrator')){ |
| 5051 |
// Persist the submitted credentials (password stored raw). |
| 5052 |
$username = sanitize_text_field($_POST['api_username']); |
| 5053 |
$password = $_POST['api_password']; |
| 5054 |
update_option('wpstream_api_username',$username); |
| 5055 |
update_option('wpstream_api_password',$password); |
| 5056 |
|
| 5057 |
// Force a fresh token fetch. |
| 5058 |
delete_transient( 'wpstream_token_api' ); |
| 5059 |
|
| 5060 |
$token = $this->main->wpstream_live_connection->wpstream_get_token(); |
| 5061 |
$videos_list = $this->main->wpstream_live_connection->wpstream_get_videos(); |
| 5062 |
// cleanup any previous echo before sending json |
| 5063 |
ob_end_clean(); |
| 5064 |
// !DO NOT SEND TOKEN TO THE CLIENT! |
| 5065 |
if ($token){ |
| 5066 |
// Authentication succeeded. |
| 5067 |
echo json_encode( array( |
| 5068 |
'success'=> true |
| 5069 |
)); |
| 5070 |
} |
| 5071 |
else { |
| 5072 |
// Distinguish a hard CURL failure from bad credentials. |
| 5073 |
$text = get_option('wpstream_curl_failed') ? |
| 5074 |
'Login failed with critical error: ' . get_option('wpstream_curl_failed') : |
| 5075 |
'Wrong username or password!'; |
| 5076 |
echo json_encode( array( |
| 5077 |
'success'=> false, |
| 5078 |
'error' => $text, |
| 5079 |
)); |
| 5080 |
} |
| 5081 |
|
| 5082 |
}else{ |
| 5083 |
// Non-admins may not connect the account. |
| 5084 |
echo json_encode( array( |
| 5085 |
'success'=> false, |
| 5086 |
'token' => esc_html('You are not an administrator','wpstream') |
| 5087 |
)); |
| 5088 |
} |
| 5089 |
die(); |
| 5090 |
} |
| 5091 |
|
| 5092 |
/** |
| 5093 |
* Placeholder AJAX callback to refresh the captcha (currently a no-op). |
| 5094 |
* |
| 5095 |
* @return void |
| 5096 |
*/ |
| 5097 |
public function wpstream_register_refresh_capthca(){ |
| 5098 |
|
| 5099 |
if(current_user_can('administrator')){ |
| 5100 |
|
| 5101 |
} |
| 5102 |
} |
| 5103 |
|
| 5104 |
/** |
| 5105 |
* AJAX proxy: fetch a captcha challenge from the baker API and return it. |
| 5106 |
* Used on HTTP sites where the Altcha widget cannot run (requires Web Crypto / HTTPS). |
| 5107 |
* The JS side solves the PoW locally and sends back the full base64 Altcha payload. |
| 5108 |
*/ |
| 5109 |
public function wpstream_get_captcha_challenge() { |
| 5110 |
// Fetch a challenge from the captcha ("baker") API. |
| 5111 |
$api_url = WPSTREAM_API . '/v2/user/getcaptcha'; |
| 5112 |
$response = wp_remote_get( $api_url, array( 'timeout' => 10 ) ); |
| 5113 |
|
| 5114 |
// On transport failure, return an error JSON. |
| 5115 |
if ( is_wp_error( $response ) ) { |
| 5116 |
echo json_encode( array( 'success' => false, 'error' => 'Could not reach captcha service.' ) ); |
| 5117 |
die(); |
| 5118 |
} |
| 5119 |
|
| 5120 |
$body = wp_remote_retrieve_body( $response ); |
| 5121 |
// Forward the challenge JSON directly to the browser |
| 5122 |
header( 'Content-Type: application/json' ); |
| 5123 |
echo $body; |
| 5124 |
die(); |
| 5125 |
} |
| 5126 |
|
| 5127 |
/** |
| 5128 |
* Solve a simple proof-of-work: find a nonce whose sha256(challenge.nonce) |
| 5129 |
* hash begins with $difficulty leading zeros. (Server-side helper.) |
| 5130 |
* |
| 5131 |
* @param string $challenge Challenge string to hash against. |
| 5132 |
* @param int $difficulty Number of required leading "0" hex chars. |
| 5133 |
* @return int|null The solving nonce, or null if none found within the cap. |
| 5134 |
*/ |
| 5135 |
private function solve_pow( $challenge, $difficulty ) { |
| 5136 |
// Start at nonce 0 and require this many leading zeros. |
| 5137 |
$nonce = 0; |
| 5138 |
$target = str_repeat( "0", $difficulty ); |
| 5139 |
|
| 5140 |
// Brute-force up to a hard cap of attempts. |
| 5141 |
while ( $nonce < 5000000 ) { |
| 5142 |
$hash = hash( 'sha256', $challenge . $nonce ); |
| 5143 |
// Accept the first nonce whose hash starts with the target zeros. |
| 5144 |
if ( strpos( $hash, $target ) === 0 ) { |
| 5145 |
return $nonce; |
| 5146 |
} |
| 5147 |
$nonce++; |
| 5148 |
} |
| 5149 |
return null; // Return null if no solution is found within the max attempts |
| 5150 |
} |
| 5151 |
|
| 5152 |
/* |
| 5153 |
* |
| 5154 |
* On Boarding login |
| 5155 |
* |
| 5156 |
*/ |
| 5157 |
/** |
| 5158 |
* AJAX: register a new WpStream account during onboarding. |
| 5159 |
* |
| 5160 |
* Validates the email/password, requires an ALTCHA solution, calls the |
| 5161 |
* account-create API, and on success stores the credentials and fetches a |
| 5162 |
* token. Returns JSON. Nonce- and administrator-gated. |
| 5163 |
* |
| 5164 |
* @return void Ends with die(). |
| 5165 |
*/ |
| 5166 |
public function wpstream_on_board_register(){ |
| 5167 |
check_ajax_referer( 'wpstream_onboarding_nonce', 'security' ); |
| 5168 |
if(current_user_can('administrator')){ |
| 5169 |
// Read submitted email/password (password kept raw). |
| 5170 |
$wpstream_register_email = sanitize_text_field($_POST['wpstream_register_email']); |
| 5171 |
$wpstream_register_password = $_POST['wpstream_register_password']; |
| 5172 |
|
| 5173 |
// Field-level validation; bail with the error payload if invalid. |
| 5174 |
$validate = $this->wpstream_validate_onboard_register($wpstream_register_email,$wpstream_register_password); |
| 5175 |
if(!$validate['success']){ |
| 5176 |
// cleanup any previous echo before sending json |
| 5177 |
ob_end_clean(); |
| 5178 |
echo json_encode($validate); |
| 5179 |
die(); |
| 5180 |
} |
| 5181 |
|
| 5182 |
// Require a captcha solution; message differs on HTTPS vs HTTP (widget readiness). |
| 5183 |
$wpstream_altcha = isset($_POST['wpstream_altcha']) ? trim( $_POST['wpstream_altcha'] ) : ''; |
| 5184 |
$is_https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'; |
| 5185 |
|
| 5186 |
if ( empty($wpstream_altcha) ) { |
| 5187 |
if ( $is_https ) { |
| 5188 |
$message = esc_html__('Captcha verification failed. Please try again.', 'wpstream'); |
| 5189 |
} else { |
| 5190 |
$message = esc_html__('Security check not ready yet. Please wait a moment and try again.', 'wpstream'); |
| 5191 |
} |
| 5192 |
echo json_encode( array( 'success' => false, 'message' => $message ) ); |
| 5193 |
die(); |
| 5194 |
} |
| 5195 |
|
| 5196 |
// Call the account-create API with the captcha solution. |
| 5197 |
$url='v2/user/create'; |
| 5198 |
$curl_post_fields=array( |
| 5199 |
'email' => $wpstream_register_email, |
| 5200 |
'password' => $wpstream_register_password, |
| 5201 |
'solution' => $wpstream_altcha, |
| 5202 |
'captcha_id' => '', |
| 5203 |
); |
| 5204 |
|
| 5205 |
$curl_response = $this->main->wpstream_live_connection->wpstream_baker_do_curl_base($url,$curl_post_fields,true); |
| 5206 |
$curl_response_decoded = json_decode($curl_response,JSON_OBJECT_AS_ARRAY); |
| 5207 |
|
| 5208 |
if($curl_response_decoded['success']){ |
| 5209 |
|
| 5210 |
if($curl_response_decoded['request']['registred']){ |
| 5211 |
// we are registerd |
| 5212 |
|
| 5213 |
// Store the new credentials and obtain a token. |
| 5214 |
update_option('wpstream_api_username',$wpstream_register_email); |
| 5215 |
update_option('wpstream_api_password',$wpstream_register_password); |
| 5216 |
|
| 5217 |
$token = $this->main->wpstream_live_connection->wpstream_get_token(); |
| 5218 |
|
| 5219 |
// cleanup any previous echo before sending json |
| 5220 |
ob_end_clean(); |
| 5221 |
echo json_encode( array( |
| 5222 |
'success' => true, |
| 5223 |
'token' => $token , |
| 5224 |
'message' => esc_html__('Your Account was created. Please stand by...','wpstream'), |
| 5225 |
|
| 5226 |
)); |
| 5227 |
die(); |
| 5228 |
}else{ |
| 5229 |
// API reachable but registration was rejected (e.g. email taken). |
| 5230 |
|
| 5231 |
// cleanup any previous echo before sending json |
| 5232 |
ob_end_clean(); |
| 5233 |
echo json_encode( array( |
| 5234 |
'success'=> false, |
| 5235 |
'message'=> $curl_response_decoded['request']['message'], |
| 5236 |
'curl'=>$curl_response_decoded, |
| 5237 |
|
| 5238 |
)); |
| 5239 |
die(); |
| 5240 |
} |
| 5241 |
|
| 5242 |
|
| 5243 |
|
| 5244 |
|
| 5245 |
}else{ |
| 5246 |
// API call itself failed. |
| 5247 |
// cleanup any previous echo before sending json |
| 5248 |
ob_end_clean(); |
| 5249 |
echo json_encode( array( |
| 5250 |
'success'=> false, |
| 5251 |
'message'=> esc_html__('Registration could not be completed. Please try again or register on wpstream.net','wpstream'), |
| 5252 |
|
| 5253 |
)); |
| 5254 |
die(); |
| 5255 |
} |
| 5256 |
|
| 5257 |
|
| 5258 |
|
| 5259 |
|
| 5260 |
|
| 5261 |
|
| 5262 |
|
| 5263 |
}else{ |
| 5264 |
// Non-admins may not register the account. |
| 5265 |
// cleanup any previous echo before sending json |
| 5266 |
ob_end_clean(); |
| 5267 |
echo json_encode( array( |
| 5268 |
'success'=> false, |
| 5269 |
'message' => esc_html('You are not an administrator','wpstream') |
| 5270 |
)); |
| 5271 |
die(); |
| 5272 |
} |
| 5273 |
|
| 5274 |
die(); |
| 5275 |
} |
| 5276 |
|
| 5277 |
|
| 5278 |
/* |
| 5279 |
* |
| 5280 |
* Validate for register |
| 5281 |
* |
| 5282 |
*/ |
| 5283 |
/** |
| 5284 |
* Validate the onboarding registration email + password. |
| 5285 |
* |
| 5286 |
* Checks for a non-empty, well-formed email whose domain has DNS records, |
| 5287 |
* and a password of at least 5 characters. |
| 5288 |
* |
| 5289 |
* @param string $wpstream_register_email Submitted email. |
| 5290 |
* @param string $wpstream_register_password Submitted password. |
| 5291 |
* @return array{success: bool, message?: string} Validation result. |
| 5292 |
*/ |
| 5293 |
public function wpstream_validate_onboard_register($wpstream_register_email,$wpstream_register_password){ |
| 5294 |
|
| 5295 |
// Default to valid; each check below can override with an error. |
| 5296 |
$return= array( |
| 5297 |
'success'=>true |
| 5298 |
); |
| 5299 |
|
| 5300 |
// Email must not be empty. |
| 5301 |
if ($wpstream_register_email=='' ){ |
| 5302 |
$return= array( |
| 5303 |
'success'=> false, |
| 5304 |
'message' => esc_html('The email Field is Empty','wpstream') |
| 5305 |
); |
| 5306 |
return $return;die(); |
| 5307 |
} |
| 5308 |
|
| 5309 |
// Email must be syntactically valid. |
| 5310 |
if(filter_var($wpstream_register_email,FILTER_VALIDATE_EMAIL) === false) { |
| 5311 |
$return= array( |
| 5312 |
'success'=> false, |
| 5313 |
'message' => esc_html__('The email doesn\'t look right !','wpstream') |
| 5314 |
); |
| 5315 |
return $return;die(); |
| 5316 |
} |
| 5317 |
|
| 5318 |
|
| 5319 |
// The email domain must resolve in DNS. |
| 5320 |
$domain = mb_substr(strrchr($wpstream_register_email, "@"), 1); |
| 5321 |
if( $domain!='' && !checkdnsrr ($domain) ){ |
| 5322 |
$return= array( |
| 5323 |
'success'=> false, |
| 5324 |
'message' => esc_html__('The email doesn\'t look right !','wpstream') |
| 5325 |
); |
| 5326 |
return $return;die(); |
| 5327 |
} |
| 5328 |
|
| 5329 |
|
| 5330 |
|
| 5331 |
// Enforce a minimum password length. |
| 5332 |
if(strlen($wpstream_register_password)<5){ |
| 5333 |
$return= array( |
| 5334 |
'success'=> false, |
| 5335 |
'message' => esc_html('The password is too short. Please use at least 5 characters.','wpstream') |
| 5336 |
); |
| 5337 |
return $return;die(); |
| 5338 |
} |
| 5339 |
|
| 5340 |
return $return;die(); |
| 5341 |
} |
| 5342 |
|
| 5343 |
/** |
| 5344 |
* Handle the AJAX request to initiate a multipart upload |
| 5345 |
* |
| 5346 |
* Validates the request, builds a clean filename, and asks the cloud API to |
| 5347 |
* open a multipart S3 upload; returns the upload id and per-part pre-signed |
| 5348 |
* URLs as JSON. Nonce- and administrator-gated. |
| 5349 |
* |
| 5350 |
* @since 3.0.1 |
| 5351 |
* @return void Responds via wp_send_json_*. |
| 5352 |
*/ |
| 5353 |
public function handle_initiate_multipart_upload() { |
| 5354 |
check_ajax_referer( 'wpstream_multipart_upload_nonce', 'security' ); |
| 5355 |
|
| 5356 |
// Security check - only admins can do this |
| 5357 |
if (!current_user_can('administrator')) { |
| 5358 |
wp_send_json_error('Unauthorized access'); |
| 5359 |
return; |
| 5360 |
} |
| 5361 |
|
| 5362 |
// Get file details from request |
| 5363 |
$file_name = sanitize_text_field($_POST['file_name']); |
| 5364 |
$file_size = intval($_POST['file_size']); |
| 5365 |
$content_type = sanitize_text_field($_POST['content_type']); |
| 5366 |
$num_parts = intval($_POST['parts']); |
| 5367 |
|
| 5368 |
// Reject incomplete/invalid file metadata. |
| 5369 |
if (empty($file_name) || $file_size <= 0 || $num_parts <= 0) { |
| 5370 |
wp_send_json_error('Invalid file information'); |
| 5371 |
return; |
| 5372 |
} |
| 5373 |
|
| 5374 |
// Prepare a clean filename (similar to the standard upload process) |
| 5375 |
// Keep the extension, slugify the base name (spaces -> _, strip non-word chars). |
| 5376 |
$file_name_array = explode(".", $file_name); |
| 5377 |
$file_extension = $file_name_array[count($file_name_array) - 1]; |
| 5378 |
$temp_file_name = $file_name_array[0]; |
| 5379 |
$temp_file_name = str_replace(' ', '_', $temp_file_name); |
| 5380 |
$temp_file_name = preg_replace('/\W/', '', $temp_file_name); |
| 5381 |
$clean_file_name = $temp_file_name . '.' . $file_extension; |
| 5382 |
|
| 5383 |
// Make API call to initiate multipart upload |
| 5384 |
$url = 'video/upload'; |
| 5385 |
$access_token = $this->main->wpstream_live_connection->wpstream_get_token(); |
| 5386 |
|
| 5387 |
// Need an authenticated token to talk to the cloud. |
| 5388 |
if (!$access_token) { |
| 5389 |
wp_send_json_error('Not connected to WPStream service'); |
| 5390 |
return; |
| 5391 |
} |
| 5392 |
|
| 5393 |
// Request the multipart upload initiation. |
| 5394 |
$api_params = array( |
| 5395 |
'access_token' => $access_token, |
| 5396 |
'size' => $file_size, |
| 5397 |
'name' => $clean_file_name, |
| 5398 |
'content_type' => $content_type, |
| 5399 |
'parts' => $num_parts |
| 5400 |
); |
| 5401 |
|
| 5402 |
$response = $this->main->wpstream_live_connection->wpstream_baker_do_curl_base($url, $api_params, true); |
| 5403 |
$response_data = json_decode($response, true); |
| 5404 |
|
| 5405 |
// Bubble up any API error. |
| 5406 |
if (!isset($response_data['success']) || $response_data['success'] !== true) { |
| 5407 |
$error_message = isset($response_data['error']) ? $response_data['error'] : 'Failed to initiate multipart upload'; |
| 5408 |
wp_send_json_error($error_message); |
| 5409 |
return; |
| 5410 |
} |
| 5411 |
|
| 5412 |
// Return the upload ID and pre-signed URLs for each part |
| 5413 |
wp_send_json_success($response_data); |
| 5414 |
} |
| 5415 |
|
| 5416 |
/** |
| 5417 |
* Handle the AJAX request to complete a multipart upload |
| 5418 |
* |
| 5419 |
* Validates the request and tells the cloud API to finalize the multipart S3 |
| 5420 |
* upload for the given parts/handle. Nonce- and administrator-gated. |
| 5421 |
* |
| 5422 |
* @since 3.0.1 |
| 5423 |
* @return void Responds via wp_send_json_*. |
| 5424 |
*/ |
| 5425 |
public function handle_complete_multipart_upload() { |
| 5426 |
check_ajax_referer( 'wpstream_multipart_upload_nonce', 'security' ); |
| 5427 |
|
| 5428 |
// Security check - only admins can do this |
| 5429 |
if (!current_user_can('administrator')) { |
| 5430 |
wp_send_json_error('Unauthorized access'); |
| 5431 |
return; |
| 5432 |
} |
| 5433 |
|
| 5434 |
// Get completion details |
| 5435 |
$parts = json_decode(stripslashes($_POST['parts']), true); |
| 5436 |
$file_name = sanitize_text_field($_POST['file_name']); |
| 5437 |
$handle = sanitize_text_field($_POST['handle']); |
| 5438 |
|
| 5439 |
// Reject incomplete completion metadata. (Note: is_numeric($parts) checks the decoded parts array — see report.) |
| 5440 |
if (empty($parts) || !is_numeric($parts) || empty($file_name)) { |
| 5441 |
wp_send_json_error('Invalid completion information'); |
| 5442 |
return; |
| 5443 |
} |
| 5444 |
|
| 5445 |
// Make API call to complete the multipart upload |
| 5446 |
$url = 'video/upload'; |
| 5447 |
$access_token = $this->main->wpstream_live_connection->wpstream_get_token(); |
| 5448 |
|
| 5449 |
// Need an authenticated token to talk to the cloud. |
| 5450 |
if (!$access_token) { |
| 5451 |
wp_send_json_error('Not connected to WPStream service'); |
| 5452 |
return; |
| 5453 |
} |
| 5454 |
|
| 5455 |
// Send the finalize ("complete") request with the uploaded part list. |
| 5456 |
$api_params = array( |
| 5457 |
'access_token' => $access_token, |
| 5458 |
'parts' => $parts, |
| 5459 |
'name' => $file_name, |
| 5460 |
'handle' => $handle, |
| 5461 |
'action' => 'complete' |
| 5462 |
); |
| 5463 |
|
| 5464 |
$response = $this->main->wpstream_live_connection->wpstream_baker_do_curl_base($url, $api_params, true); |
| 5465 |
$response_data = json_decode($response, true); |
| 5466 |
|
| 5467 |
// Bubble up any API error. |
| 5468 |
if (!isset($response_data['success']) || $response_data['success'] !== true) { |
| 5469 |
$error_message = isset($response_data['error']) ? $response_data['error'] : 'Failed to complete multipart upload'; |
| 5470 |
wp_send_json_error($error_message); |
| 5471 |
return; |
| 5472 |
} |
| 5473 |
|
| 5474 |
// Return success |
| 5475 |
wp_send_json_success(); |
| 5476 |
} |
| 5477 |
|
| 5478 |
/** |
| 5479 |
* AJAX: update the WpStream plugin in place from the Settings/Support tab. |
| 5480 |
* |
| 5481 |
* Loads the upgrader APIs, initializes WP_Filesystem, runs the plugin |
| 5482 |
* upgrade, re-activates the plugin, and returns success/error JSON. |
| 5483 |
* Requires the update_plugins capability. |
| 5484 |
* |
| 5485 |
* @return void Responds via wp_send_json_*. |
| 5486 |
*/ |
| 5487 |
public function wpstream_settings_tab_update_plugin() { |
| 5488 |
// Capability gate. |
| 5489 |
if ( !current_user_can( 'update_plugins' ) ) { |
| 5490 |
wp_send_json_error( __( 'Not enough permissions to make this change', 'wpstream' ) ); |
| 5491 |
} |
| 5492 |
|
| 5493 |
// Load the core upgrade/filesystem APIs. |
| 5494 |
include_once ABSPATH . 'wp-admin/includes/plugin-install.php'; |
| 5495 |
include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; |
| 5496 |
include_once ABSPATH . 'wp-admin/includes/file.php'; |
| 5497 |
|
| 5498 |
// Initialize the filesystem abstraction (needed to write plugin files). |
| 5499 |
$credentials = request_filesystem_credentials(''); |
| 5500 |
if ( !WP_Filesystem( $credentials ) ) { |
| 5501 |
wp_send_json_error( __( 'Failed to connect to the filesystem', 'wpstream' ) ); |
| 5502 |
} |
| 5503 |
|
| 5504 |
// Run the in-place upgrade for this plugin, then re-activate it. |
| 5505 |
$upgrader = new Plugin_Upgrader(new Automatic_Upgrader_Skin()); |
| 5506 |
$plugin_path = plugin_basename( WPSTREAM_PLUGIN_PATH . 'wpstream.php' ); |
| 5507 |
$result = $upgrader->upgrade( $plugin_path ); |
| 5508 |
activate_plugin( $plugin_path ); |
| 5509 |
|
| 5510 |
if ( is_wp_error( $result ) ) { |
| 5511 |
wp_send_json_error( __( 'Update failed due to', 'wpstream' ) . $result->get_error_message() ); |
| 5512 |
} |
| 5513 |
|
| 5514 |
wp_send_json_success(); |
| 5515 |
} |
| 5516 |
} |
| 5517 |
|