PluginProbe
WpStream – Live Streaming, Video on Demand, Pay Per View / 3.7
WpStream – Live Streaming, Video on Demand, Pay Per View v3.7
4.14.1 4.14.0 4.13.2 4.13.1 4.13 4.12.5 4.12.4 4.12.3 4.12.2 4.12.1 4.12 4.4.4 4.4.5 4.4.6 4.4.7 4.4.8 4.4.9 4.5 4.5.1 4.5.11 4.5.11.1 4.5.11.2 4.5.11.4 4.5.11.5 4.5.11.6 All 181 releases
← All changes | admin/class-wpstream-admin.php +788 -3032 4.14.13.7 View file →
@@ -9,26 +9,13 @@
9 9 * @package Wpstream
10 10 * @subpackage Wpstream/admin
11 11 */
12 12
13 -
14 -// Exit if accessed directly.
15 -if ( ! defined( 'ABSPATH' ) ) {
16 - exit;
17 -}
18 -
19 13 /**
20 14 * The admin-specific functionality of the plugin.
21 15 *
22 - * This is the admin "god class": it wires up the whole wp-admin surface of the
23 - * plugin. Responsibilities include registering admin CSS/JS, building the
24 - * top-level WpStream menu and its sub-pages (Credentials, Channels, Recordings,
25 - * Settings, Quick Start), rendering channel/settings/onboarding screens and
26 - * their modal dialogs, defining the per-event global streaming options, hooking
27 - * into WooCommerce to register the custom stream/VOD product types and their
28 - * pricing/metabox behaviour, rendering post metaboxes, driving the multipart S3
29 - * upload AJAX endpoints, and emitting the various admin notices (plugin update,
30 - * cache flush, theme, etc.). Many methods echo HTML directly.
16 + * Defines the plugin name, version, and two examples hooks for how to
17 + * enqueue the admin-specific stylesheet and JavaScript.
31 18 *
32 19 * @package Wpstream
33 20 * @subpackage Wpstream/admin
34 21 * @author wpstream <office@wpstream.net>
@@ -33,20 +20,11 @@
33 20 * @subpackage Wpstream/admin
34 21 * @author wpstream <office@wpstream.net>
35 22 */
36 23 class Wpstream_Admin {
37 -
24 +
25 +
38 26 /**
39 - * Most parts a multipart VOD upload may be split into.
40 - *
41 - * The storage backend assembles at most this many parts, so a larger
42 - * split uploads every byte and still can never complete.
43 - *
44 - * @var int
45 - */
46 - const MULTIPART_MAX_PARTS = 1000;
47 -
48 - /**
49 27 * Store plugin main class to allow public access.
50 28 *
51 29 * @since 20180622
52 30 * @var object The main class.
@@ -53,1391 +31,646 @@
53 31 */
54 32 public $main;
55 33
56 34
57 - /**
58 - * The ID of this plugin.
59 - *
60 - * @since 3.0.1
61 - * @access private
62 - * @var string $plugin_name The ID of this plugin.
63 - */
64 - private $plugin_name;
35 + /**
36 + * The ID of this plugin.
37 + *
38 + * @since 3.0.1
39 + * @access private
40 + * @var string $plugin_name The ID of this plugin.
41 + */
42 + private $plugin_name;
65 43
66 - /**
67 - * The version of this plugin.
68 - *
69 - * @since 3.0.1
70 - * @access private
71 - * @var string $version The current version of this plugin.
72 - */
73 - private $version;
44 + /**
45 + * The version of this plugin.
46 + *
47 + * @since 3.0.1
48 + * @access private
49 + * @var string $version The current version of this plugin.
50 + */
51 + private $version;
74 52
75 - /**
76 - * @var array Definitions of the per-event "global" streaming options (record,
77 - * view count, autoplay, encryption, etc.). Each entry holds a
78 - * translated label, a help "details" string, and a default value.
79 - * Populated lazily on `init` by load_global_event_options().
80 - */
81 - public $global_event_options ;
53 + /**
54 + * Initialize the class and set its properties.
55 + *
56 + * @since 3.0.1
57 + * @param string $plugin_name The name of this plugin.
58 + * @param string $version The version of this plugin.
59 + */
60 + public function __construct( $plugin_name, $version,$plugin_main ) {
82 61
62 + $this->plugin_name = $plugin_name;
63 + $this->version = $version;
64 + $this->main = $plugin_main;
83 65
66 + }
84 67
68 + /**
69 + * Register the stylesheets for the admin area.
70 + *
71 + * @since 3.0.1
72 + */
73 + public function enqueue_styles() {
85 74
75 + /**
76 + * This function is provided for demonstration purposes only.
77 + *
78 + * An instance of this class should be passed to the run() function
79 + * defined in Wpstream_Loader as all of the hooks are defined
80 + * in that particular class.
81 + *
82 + * The Wpstream_Loader will then create the relationship
83 + * between the defined hooks and the functions defined in this
84 + * class.
85 + */
86 + wp_enqueue_style( 'wpestate-roboto', "https://fonts.googleapis.com/css?family=Roboto:300,400,500,600,700,900&display=swap&subset=latin-ext" );
87 + wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/wpstream-admin.css', array(), WPSTREAM_PLUGIN_VERSION, 'all' );
88 +
89 + }
86 90
87 - /**
88 - * Initialize the class and set its properties.
89 - *
90 - * @since 3.0.1
91 - * @param string $plugin_name The name (ID) of this plugin.
92 - * @param string $version The version of this plugin.
93 - * @param object $plugin_main The main plugin class instance, kept for public access.
94 - * @return void
95 - */
96 - public function __construct( $plugin_name, $version,$plugin_main ) {
91 + /**
92 + * Register the JavaScript for the admin area.
93 + *
94 + * @since 3.0.1
95 + */
96 + public function enqueue_scripts() {
97 97
98 - // Remember the plugin identity and the main class for later use.
99 - $this->plugin_name = $plugin_name;
100 - $this->version = $version;
101 - $this->main = $plugin_main;
102 -
103 - // Build the translatable global event options once WP (and its i18n) is ready.
104 - add_action('init', array($this, 'load_global_event_options'));
105 - }
106 -
107 - /**
108 - * Populate $global_event_options with the translatable per-event settings.
109 - *
110 - * Deferred to the `init` hook so the esc_html__() translations resolve
111 - * against a loaded text domain.
112 - *
113 - * @return void
114 - */
115 - public function load_global_event_options() {
116 - // Each key maps to a label/details/default triple used when rendering the event option toggles.
117 - $this->global_event_options = array(
118 - 'record' => array(
119 - 'name' => esc_html__('Record Live Stream','wpstream'),
120 - 'details' => esc_html__('If enabled, live streams will be recorded and saved to your library.','wpstream'),
121 - 'defaults' => 'no',
122 - ),
123 - 'view_count' => array(
124 - 'name' => esc_html__('Display Viewer Count','wpstream'),
125 - 'details' => esc_html__('If enabled, the live viewer count will show up in the player.','wpstream'),
126 - 'defaults' => 'yes',
127 - ),
128 - 'domain_lock' => array(
129 - 'name' => esc_html__('Lock To Website','wpstream'),
130 - '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') ),
131 - 'defaults' => 'no',
132 - ),
133 - 'autoplay' => array(
134 - 'name' => esc_html__('Autoplay','wpstream'),
135 - 'details' => esc_html__('If enabled, live video will attempt to start playing automatically. This is only achievable in some browsers.','wpstream'),
136 - 'defaults' => 'yes',
137 - ),
138 - 'mute' => array(
139 - 'name' => esc_html__('Start Muted','wpstream'),
140 - 'details' => esc_html__('If enabled, live video will start muted. This may increase the rate of autoplay in some browsers. ','wpstream'),
141 - 'defaults' => 'no',
142 - ),
143 - 'low_latency' => array(
144 - 'name' => esc_html__('Low Latency (beta)','wpstream'),
145 - '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'),
146 - 'defaults' => 'no',
147 - ),
148 - 'adaptive_bitrate' => array(
149 - 'name' => esc_html__('Adaptive Bitrate (beta)','wpstream'),
150 - 'details' => esc_html__('Ensures a smooth and uninterrupted viewing experience by adjusting video quality for viewers with reduced network speed or device capabilities.','wpstream'),
151 - 'defaults' => 'no',
152 - ),
153 - 'encrypt' =>array(
154 - 'name' => esc_html__('Encrypt Live Stream','wpstream'),
155 - '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'),
156 - 'defaults' => 'no',
157 - ),
158 - 'ses_encrypt'=>array(
159 - 'name' => esc_html__('Use Sessions with Encryption','wpstream'),
160 - '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'),
161 - 'defaults' => 'no',
162 - ),
163 - );
164 -
165 - /**
166 - * Filter the catalog of per-channel options shown as toggles.
167 - *
168 - * Each entry is `key => array( name, details, defaults )`. An added key
169 - * renders a toggle on the settings and channel screens; it is stored
170 - * only if the Channel Settings Module knows the key.
171 - *
172 - * @since 4.14.0
173 - *
174 - * @param array $options Option catalog keyed by setting.
175 - */
176 - $this->global_event_options = (array) apply_filters( 'wpstream_default_event_options', $this->global_event_options );
177 - }
178 -
179 - /**
180 - * Register and enqueue the plugin's admin-area stylesheets.
181 - *
182 - * Hooked (via Wpstream_Loader) to admin_enqueue_scripts.
183 - *
184 - * @since 3.0.1
185 - * @return void
186 - */
187 - public function enqueue_styles() {
188 -
189 - /**
190 - * This function is provided for demonstration purposes only.
191 - *
192 - * An instance of this class should be passed to the run() function
193 - * defined in Wpstream_Loader as all of the hooks are defined
194 - * in that particular class.
195 - *
196 - * The Wpstream_Loader will then create the relationship
197 - * between the defined hooks and the functions defined in this
198 - * class.
199 - */
200 - // No external webfont: the admin UI falls back to the system font stack
201 - // declared in the stylesheets, so no request leaves the site for fonts.
202 - // Main admin stylesheet; filemtime() is used as the cache-busting version.
203 - wp_enqueue_style(
204 - $this->plugin_name,
205 - plugin_dir_url( __FILE__ ) . 'css/wpstream-admin.css',
206 - array(),
207 - filemtime(plugin_dir_path(__FILE__) . 'css/wpstream-admin.css' ),
208 - 'all'
209 - );
210 -
211 - // Ensure the WP media library scripts/styles are available (for logo/image pickers) if not already loaded.
212 - if (!did_action('wp_enqueue_media')) {
213 - wp_enqueue_media();
214 - }
215 - }
216 -
217 - /**
218 - * Register, enqueue and localize the plugin's admin-area JavaScript.
219 - *
220 - * Enqueues jQuery UI helpers, the fileupload lib, the admin control/upload
221 - * scripts, the start-streaming and settings scripts, and the onboarding
222 - * bundles. Each script is fed a localized vars array (translated strings,
223 - * URLs, nonces, feature flags). Some bundles are only loaded on specific
224 - * admin screens. Hooked (via Wpstream_Loader) to admin_enqueue_scripts.
225 - *
226 - * @since 3.0.1
227 - * @return void
228 - */
229 - public function enqueue_scripts() {
230 -
231 - // jQuery UI widgets used by the settings/date pickers and sliders.
232 98 wp_enqueue_script("jquery-ui-slider");
233 - wp_enqueue_script("jquery-ui-datepicker");
234 - // Blueimp-style file upload library backing the recordings/VOD uploader.
235 - wp_enqueue_script('jquery.fileupload', plugin_dir_url( __FILE__ ) .'js/jquery.fileupload.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true);
99 + wp_enqueue_script("jquery-ui-datepicker");
100 + wp_enqueue_script('jquery.fileupload', plugin_dir_url( __FILE__ ) .'js/jquery.fileupload.js',array(), WPSTREAM_PLUGIN_VERSION, true);
101 + wp_enqueue_script('wpstream-admin-control', plugin_dir_url( __FILE__ ) .'js/admin_control.js',array(), WPSTREAM_PLUGIN_VERSION, true);
102 + wp_localize_script('wpstream-admin-control', 'wpstream_admin_control_vars',
103 + array(
104 + 'admin_url' => get_admin_url(),
105 + 'loading_url' => WPSTREAM_PLUGIN_DIR_URL.'/img/loading.gif',
106 + 'download_mess' => esc_html__('Click to download!','wpstream'),
107 + 'uploading' => esc_html('We are uploading your file.Do not close this window!','wpstream'),
108 + 'upload_complete2' => esc_html('Upload Complete! You can upload another file!','wpstream'),
109 + 'not_accepted' => esc_html('The file is not an accepted video format','wpstream'),
110 + 'upload_complete' => esc_html('Upload Complete!','wpstream'),
111 + 'no_band' => esc_html('Not enough bandwidth.','wpsteam'),
112 + 'no_band_no_store' => esc_html('Not enough bandwidth or storage.','wpsteam')
236 113
237 - // Shared admin utility helpers, then the main admin control script that drives uploads and channel actions.
238 - wp_enqueue_script( 'wpstream-admin-utils', plugin_dir_url( __FILE__ ) .'js/utils/admin_utils.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true);
239 - wp_enqueue_script('wpstream-admin-control', plugin_dir_url( __FILE__ ) .'js/admin_control.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true);
240 - // Hand the control script all of its translated status strings, the admin URL, and the multipart upload nonce.
241 - wp_localize_script('wpstream-admin-control', 'wpstream_admin_control_vars',
114 + ));
115 +
116 +
117 +
118 + wp_enqueue_script('wpstream-start-streaming_admin', plugin_dir_url( __DIR__ ) .'/public/js/start_streaming.js',array(), WPSTREAM_PLUGIN_VERSION, true);
119 + wp_localize_script('wpstream-start-streaming_admin', 'wpstream_start_streaming_vars',
242 120 array(
243 - 'admin_url' => get_admin_url(),
244 - 'multipart_upload_nonce' => wp_create_nonce( 'wpstream_multipart_upload_nonce' ),
245 - 'recordings_nonce' => wp_create_nonce( 'wpstream_recordings_nonce' ),
246 - 'loading_url' => WPSTREAM_PLUGIN_DIR_URL.'/img/loading.gif',
247 - 'download_mess' => esc_html__('Click to download!','wpstream'),
248 - 'uploading' => esc_html__('We are uploading your file. Do not close this window!','wpstream'),
249 - 'upload_complete2' => esc_html__('Upload Complete! You can upload another file!','wpstream'),
250 - 'not_accepted' => esc_html__('The file is not an accepted video format','wpstream'),
251 - 'upload_complete' => esc_html__('Upload Complete!','wpstream'),
252 - 'no_band' => esc_html__('Not enough streaming data.','wpstream'),
253 - 'no_band_no_store' => esc_html__('Not enough streaming data or storage.','wpstream'),
254 - 'no_streaming_hours' => esc_html__('Not enough streaming hours.','wpstream'),
255 - 'exceeding_limit' => esc_html__('File size exceeds 5GB. Initiating multipart upload...','wpstream'),
256 - 'upload_failed' => esc_html__('Upload Failed!','wpstream'),
257 - 'upload_failed2' => esc_html__('Upload Failed! Please Try again!','wpstream'),
258 - 'choose_a_file' => esc_html__('Choose a file&hellip;','wpstream'),
259 - 'preparing_multipart' => esc_html__('Preparing multipart upload...','wpstream'),
260 - 'uploading_part' => esc_html__('Uploading part {part} of {total}...','wpstream'),
261 - 'upload_failed_part' => esc_html__('Failed to upload part {part}. Please try again.','wpstream'),
262 - 'completing_upload' => esc_html__('Completing upload. Please wait...','wpstream'),
263 - 'upload_failed_part_retry' => esc_html__('Failed to upload part {part}. Retrying...','wpstream'),
264 - 'choose_recording' => esc_html__( 'Choose Recording', 'wpstream' ),
265 - 'select_recording' => esc_html__( 'Please select a recording from the list', 'wpstream' ),
266 - 'invalid_response' => esc_html__('Invalid response from server. Missing required upload data.', 'wpstream'),
267 - 'video_processing' => esc_html__( 'The video is still processing', 'wpstream' ),
268 - 'file_name_text' => esc_html__('File Name:','wpstream'),
269 - 'channel_create_error' => esc_html__('Something did not work. Please try again.', 'wpstream'),
270 - 'select_caption_file' => esc_html__('Select .vtt Captions File', 'wpstream'),
271 - 'select_button' => esc_html__('Select', 'wpstream'),
272 - 'remove_button' => esc_html__('Remove', 'wpstream'),
273 - 'use_streaming_hours' => $this->main->quota_manager->get_streaming_ui_flags_from_cache()['use_streaming_hours'],
121 + 'admin_url' => get_admin_url(),
122 + 'loading_url' => WPSTREAM_PLUGIN_DIR_URL.'/img/loading.gif',
123 + 'download_mess' => esc_html__('Click to download!','wpstream'),
124 + 'uploading' => esc_html('We are uploading your file.Do not close this window!','wpstream'),
125 + 'upload_complete2' => esc_html('Upload Complete! You can upload another file!','wpstream'),
126 + 'not_accepted' => esc_html('The file is not an accepted video format','wpstream'),
127 + 'upload_complete' => esc_html('Upload Complete!','wpstream'),
128 + 'no_band' => esc_html('Not enough bandwidth.','wpsteam'),
129 + 'no_band_no_store' => esc_html('Not enough bandwidth or storage.','wpsteam')
130 +
274 131 ));
275 132
276 - // Recordings list script + its localized labels and the "create VOD from recording" links (free vs paid depending on WooCommerce).
277 - wp_enqueue_script('wpstream-recordings-videos-list', plugin_dir_url( __FILE__ ) .'js/recordings_videos_list.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true);
278 - wp_localize_script( 'wpstream-recordings-videos-list', 'wpstream_recordings_videos_list_vars',
279 - array(
280 - 'delete_file' => esc_html__('Delete file', 'wpstream'),
281 - 'download' => esc_html__( 'Download', 'wpstream'),
282 - 'download_available' => esc_html__( 'Click to download! The url will work for the next 20 minutes!', 'wpstream'),
283 - 'add_free_video_url' => esc_url( admin_url( 'post-new.php?post_type=wpstream_product_vod') . '&new_video_name=' ),
284 - 'create_ftv_vod' => esc_html__( 'Create new Free-To-View VOD from this recording' , 'wpstream' ),
285 - 'woocommerce_exists' => class_exists( 'WooCommerce' ),
286 - 'add_paid_video_url' => esc_url( admin_url( 'post-new.php?post_type=product').'&new_video_name=' ),
287 - 'create_ptv_vod' => esc_html__( 'Create new Pay-Per-View VOD from this recording' , 'wpstream' ),
288 - )
289 - );
290 -
291 -
292 - // Settings-page script with its save/logo-picker strings and the broadcaster page URL.
293 - wp_enqueue_script('wpstream-settings', plugin_dir_url( __DIR__ ) .'/admin/js/wpstream_settings.js?v='.time(),array(), WPSTREAM_PLUGIN_VERSION, true);
294 - wp_localize_script('wpstream-settings', 'wpstream_settings_vars', array(
295 - 'error_message' => esc_html__( 'Failed to save settings. Please try again.', 'wpstream'),
296 - 'choose_image_text' => esc_html__( 'Choose Logo Image', 'wpstream'),
297 - 'select_image_text' => esc_html__( 'Select Image', 'wpstream'),
298 - 'update_successful' => esc_html__( 'Update Successful.', 'wpstream'),
299 - 'update_failed' => esc_html__( 'Something went wrong. Try again.', 'wpstream'),
300 - 'broadcaster_url' => esc_url( esc_url(home_url('/broadcaster-page/') ) ),
301 - ));
302 -
303 -
304 - // Identify the current admin screen so screen-specific bundles load only where needed.
305 - $current_screen=get_current_screen();
306 -
307 - // On the credentials/channels/recordings/onboard screens, load the quota widget updater.
308 - if ( in_array( $current_screen->base, ['toplevel_page_wpstream_credentials', 'wpstream_page_wpstream_live_channels', 'wpstream_page_wpstream_recordings', 'wpstream_page_wpstream_onboard'] ) ) {
309 - wp_enqueue_script( 'wpstream-user-quota-update', plugin_dir_url( __DIR__ ) . 'admin/js/wpstream-user-quota.js', array(), WPSTREAM_PLUGIN_VERSION, true );
310 - wp_localize_script( 'wpstream-user-quota-update', 'wpstream_user_quota_vars', array(
311 - 'admin_url' => get_admin_url()
312 - ));
313 - }
314 -
315 - // Add localized variables for broadcaster
316 - // Provide the broadcaster script (registered elsewhere) with its AJAX URL and nonce.
317 - wp_localize_script('wpstream-broadcaster', 'wpstream_broadcaster_vars', array(
318 - 'ajax_url' => admin_url('admin-ajax.php'),
319 - 'nonce' => wp_create_nonce('wpstream_broadcaster_nonce'),
320 - 'plugin_url' => plugin_dir_url(__FILE__),
321 - // Gates the broadcaster's diagnostic console logging (see broadcaster.js).
322 - 'debug' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG,
323 - ));
324 -
325 133 }
326 134
327 135
328 136 /**
329 - * Add Plugin Administation menu
330 - *
331 - * Registers the top-level "WpStream" menu and its sub-pages (Credentials,
332 - * All Channels, Recordings, Settings, Quick Start), each mapped to a render
333 - * callback on this class and gated to the `administrator` capability.
334 - *
335 - * @since 3.0.1
336 - * @return void
337 - */
137 + * Add Plugin Administation menu
138 + *
139 + * @since 3.0.1
140 + */
141 +
338 142 public function wpstream_manage_admin_menu() {
339 -
340 - // Top-level menu; its page defaults to the Credentials screen and uses the WpStream icon at position 20.
341 - add_menu_page( __('WpStream','wpstream'), __('WpStream ','wpstream'), 'administrator', 'wpstream_credentials', array($this,'wpstream_set_wpstream_credentials'), WPSTREAM_PLUGIN_DIR_URL.'img/wpstream-icon-menu_2.png',20 );
342 - // Credentials sub-page (same slug as the parent so it is the default view).
343 - add_submenu_page( 'wpstream_credentials', __('WpStream Credentials','wpstream'), __('Credentials','wpstream'), 'administrator', 'wpstream_credentials', array($this,'wpstream_set_wpstream_credentials') );
344 - // All Channels listing.
345 - add_submenu_page( 'wpstream_credentials', __('WpStream Live Channels','wpstream'), __('All Channels','wpstream'), 'administrator', 'wpstream_live_channels', array( $this,'wpstream_new_general_set'));
346 - // Recordings / media management.
347 - add_submenu_page( 'wpstream_credentials', __('WpStream Recordings','wpstream'), __('Recordings','wpstream'), 'administrator', 'wpstream_recordings', array($this,'wpstream_media_management'));
348 - // Global plugin settings.
349 - add_submenu_page( 'wpstream_credentials', __('WpStream Settings','wpstream'), __('Settings','wpstream'), 'administrator', 'wpstream_settings', array($this,'wpstream_settings'));
350 -
351 - // Quick Start / onboarding entry point.
352 - add_submenu_page( 'wpstream_credentials', __('WpStream Quick Start','wpstream'), __('WpStream Quick Start','wpstream'), 'administrator', 'wpstream_onboard', array($this,'wpstream_pre_onboard_display'));
353 -
354 -
355 -
356 -
143 +
144 + add_menu_page( __('WpStream','wpestream'), __('WpStream ','wpstream'), 'administrator', 'wpstream_plugin_options', array($this,'wpstream_set_wpstream_credentials') );
145 + add_submenu_page( 'wpstream_plugin_options', __('WpStream Credentials','wpestream'), __('WpStream Credentials','wpestream'), 'administrator', 'wpstream_plugin_options', array($this,'wpstream_set_wpstream_credentials') );
146 + add_submenu_page( 'wpstream_plugin_options', __('WpStream Channels','wpestream'), __('WpStream Channels','wpestream'), 'administrator', 'wpstream_new_general_set', array( $this,'wpstream_new_general_set'));
147 + add_submenu_page( 'wpstream_plugin_options', __('WpStream Media Management','wpestream'), __('WpStream Media Management','wpestream'), 'administrator', 'wpstream_media_management', array($this,'wpstream_media_management'));
148 + add_submenu_page( 'wpstream_plugin_options', __('WpStream Settings','wpestream'), __('WpStream Settings','wpestream'), 'administrator', 'wpstream_settings', array($this,'wpstream_settings'));
149 +
357 150 }
358 151
359 -
360 -
361 152
153 +
362 154 /**
363 155 * Shows events wpstream
364 156 *
365 - * Renders the "All Channels" admin screen: the Pay-Per-View (WooCommerce
366 - * `product`) channel list, the Free-To-View (`wpstream_product`) list, the
367 - * quota/pack summary, and the "no channels" call-to-action. Each channel is
368 - * drawn via wpstream_live_stream_unit(). Echoes HTML directly.
369 - *
370 - * @since 3.0.1
371 - * @return void
157 + * @since 3.0.1
372 158 */
373 - public function wpstream_new_general_set() {
374 - return $this->main->get_live_channel_presentation()->render_channels_page();
375 - }
376 - public function wpstream_social_share($the_id){
377 - return $this->main->get_live_channel_presentation()->render_part( 'social_share', array( $the_id ) );
378 - }
379 - public function wpstream_live_stream_unit($the_id,$is_front=''){
380 - $context = '' === $is_front ? 'admin' : $is_front;
381 - return $this->main->get_live_channel_presentation()->render_channel( $the_id, $context );
382 - }
383 - public function wpstream_live_stream_unit_for_theme($the_id,$is_front=''){
384 - return $this->main->get_live_channel_presentation()->render_part( 'theme_channel', array( $the_id, $is_front ) );
385 - }
386 - public function wpstream_close_modal_button(){
387 - return $this->main->get_live_channel_presentation()->render_part( 'close_modal_button' );
388 - }
389 - public function wpstream_local_event_options_toggle( $is_basic_stream_mode = false ) {
390 - return $this->main->get_live_channel_presentation()->render_part( 'local_event_options_toggle', array( $is_basic_stream_mode ) );
391 - }
392 - public function wpstream_basic_stream_mode_message() {
393 - return $this->main->get_live_channel_presentation()->render_part( 'basic_stream_mode_message' );
394 - }
395 - public function wpstream_display_modal_seetings($the_id){
396 - return $this->main->get_live_channel_presentation()->render_part( 'settings_modal', array( $the_id ) );
397 - }
398 - public function wpstream_display_modal_share($the_id){
399 - return $this->main->get_live_channel_presentation()->render_part( 'share_modal', array( $the_id ) );
400 - }
401 - public function wpstream_display_modal_broadcast($the_id,$external_software_streaming_class,$obs_uri,$obs_stream){
402 - return $this->main->get_live_channel_presentation()->render_part(
403 - 'broadcast_modal',
404 - array( $the_id, $external_software_streaming_class, $obs_uri, $obs_stream )
159 +
160 + public function wpstream_new_general_set() {
161 +
162 + $no_channel=1;
163 +
164 + if(class_exists ('WC_Subscription')){
165 +
166 + }
167 +
168 + //event_passed
169 + $args = array(
170 + 'posts_per_page' => -1,
171 + 'post_type' => 'product',
172 + 'post_status' => 'publish',
173 + 'meta_query' => array(
174 + array(
175 + 'key' => 'event_passed',
176 + 'value' => 1,
177 + 'compare' => '!=',
178 + )
179 + ),
180 +
181 + 'tax_query' => array(
182 + 'relation' => 'AND',
183 + array(
184 + 'taxonomy' => 'product_type',
185 + 'field' => 'slug',
186 + 'terms' => array('live_stream','subscription')
187 + )
188 + ),
405 189 );
406 - }
407 - public function wpstream_encoder_table(){
408 - return $this->main->get_live_channel_presentation()->render_part( 'encoder_table' );
409 - }
410 - public function wpstream_encoder_panel( $encoder, $obs_uri, $obs_stream, $visible = false ){
411 - return $this->main->get_live_channel_presentation()->render_part(
412 - 'encoder_panel',
413 - array( $encoder, $obs_uri, $obs_stream, $visible )
414 - );
415 - }
416 - private function wpstream_settings_fields(){
417 - // Definition of every settings field, keyed and grouped by 'tab' (general, subscription, messages, default options, VOD defaults, support).
418 - return array(
419 - 1 => array(
420 - 'tab' => 'general_options',
421 - 'label' => esc_html__('Slug for free video/channel pages ','wpstream'),
422 - 'name' => 'free_media_slug',
423 - 'type' => 'text',
424 - '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'),
425 - ),
426 190
427 - 'free_vod_slug' => array(
428 - 'tab' => 'general_options',
429 - 'label' => esc_html__('Slug for free VOD pages ','wpstream'),
430 - 'name' => 'free_media_slug_vod',
431 - 'type' => 'text',
432 - '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'),
433 - ),
434 -
435 - 2 => array(
436 - 'tab' => 'general_options',
437 - 'label' => esc_html__('Non-Admin User Roles Allowed to Broadcast','wpstream'),
438 - 'name' => 'stream_role',
439 - 'type' => 'user_roles',
440 - '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'),
441 -
442 - ),
443 - 3 => array(
444 - 'tab' => 'general_options',
445 - 'label' => esc_html__('Non Admin Streamers Channel Type.','wpstream'),
446 - 'name' => 'user_streaming_channel_type',
447 - 'type' => 'select',
448 - 'select_values'=>array(
449 - 'free' => esc_html__('Free Live Channel','wpstream'),
450 - 'paid' => esc_html__('Pay-Per-View','wpstream')
451 - ),
452 - 'details' => esc_html__('Choose whether the channels assigned to non-admins are free-for-all or pay-per-view (WooCommerce product).','wpstream'),
453 - ),
454 -
455 - 4 => array(
456 - 'tab' => 'general_options',
457 - 'label' => esc_html__('Default Pay-Per-View Price','wpstream'),
458 - 'name' => 'user_streaming_default_price',
459 - 'type' => 'text',
460 - 'details' => esc_html__('Default price of pay-per-view channels assigned to non-admins.','wpstream'),
461 - ),
462 -
463 -
464 -
465 - 6 => array(
466 - 'tab' => 'subscription_options',
467 - 'label' => esc_html__('Use Global Subscription Mode','wpstream'),
468 - 'name' => 'global_sub',
469 - 'type' => 'slidertoogle',
470 - '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'),
471 - ),
472 -
473 - 7 => array(
474 - 'tab' => 'subscription_options',
475 - 'label' => esc_html__('Subscription ID for Global Subscription Mode','wpstream'),
476 - 'name' => 'global_sub_id',
477 - 'type' => 'text',
478 - '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'),
479 - ),
480 - 8 => array(
481 - 'tab' => 'messages_options',
482 - 'label' => esc_html__('PPV not logged in message','wpstream'),
483 - 'name' => 'product_not_login',
484 - 'type' => 'text',
485 - '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'),
486 - 'default' => esc_html__('You must be logged in to watch this video.','wpstream'),
487 - ),
488 - 9 => array(
489 - 'tab' => 'messages_options',
490 - 'label' => esc_html__('PPV not purchased message','wpstream'),
491 - 'name' => 'product_not_bought',
492 - 'type' => 'text',
493 - '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'),
494 - 'default' => esc_html__('You did not yet purchase this item.','wpstream'),
495 - ),
496 - 10 => array(
497 - 'tab' => 'messages_options',
498 - 'label' => esc_html__('Subscription not purchased message','wpstream'),
499 - 'name' => 'product_not_subscribe',
500 - 'type' => 'text',
501 - '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'),
502 - 'default' => esc_html__(' You did not yet subscribe to this item.','wpstream'),
503 - ),
504 - 11 => array(
505 - 'tab' => 'messages_options',
506 - 'label' => esc_html__('Thank you message','wpstream'),
507 - 'name' => 'product_thankyou',
508 - 'type' => 'text',
509 - 'details' => esc_html__('This message will be displayed on the thank you page (after purchase) and the confirmation email.','wpstream'),
510 - 'default' => esc_html__('Thanks for your purchase. You can access your item at any time by visiting the following page: {item_link}','wpstream'),
511 - ),
512 -
513 - 12 => array(
514 - 'tab' => 'messages_options',
515 - 'label' => esc_html__('Subscription Active message','wpstream'),
516 - 'name' => 'subscription_active',
517 - 'type' => 'text',
518 - 'details' => esc_html__('This message will be displayed on subscription product page.','wpstream'),
519 - 'default' => esc_html__('Your Subscription is Active','wpstream'),
520 - ),
521 - 13 => array(
522 - 'tab' => 'messages_options',
523 - 'label' => esc_html__('You are not live message','wpstream'),
524 - 'name' => 'you_are_not_live',
525 - 'type' => 'text',
526 - 'details' => esc_html__('This message will be displayed in player.','wpstream'),
527 - 'default' => esc_html__('We are not live at this moment','wpstream'),
528 - ),
529 191
530 - 14 => array(
531 - 'tab' => 'general_options',
532 - 'label' => esc_html__('Video player theme','wpstream'),
533 - 'name' => 'video_player_theme',
534 - 'type' => 'select',
535 - 'select_values'=>array(
536 - 'default' => esc_html__('Default','wpstream'),
537 - 'city' => esc_html__('City','wpstream'),
538 - 'forest' => esc_html__('Forest','wpstream'),
539 - 'fantasy' => esc_html__('Fantasy','wpstream'),
540 - 'sea' => esc_html__('Sea','wpstream'),
541 - ),
542 - 'details' => esc_html__('Choose the video player theme to have a different look for the player.','wpstream'),
543 - ),
544 - 'wpstream_player_logo' => array(
545 - 'tab' => 'general_options',
546 - 'name' => 'player_logo',
547 - 'label' => esc_html__('Logo for the video player','wpstream'),
548 - 'type' => 'image',
549 - 'details' => esc_html__('This logo will be displayed on the the video player.','wpstream'),
550 - 'default' => '',
551 - 'image_size' => 'thumbnail',
552 - ),
553 - // hide the video player logo opacity for now
554 -// 'wpstream_player_logo_opacity' => array(
555 -// 'tab' => 'general_options',
556 -// 'name' => 'player_logo_opacity',
557 -// 'label' => esc_html__('Opacity of the video player logo','wpstream'),
558 -// 'type' => 'range',
559 -// 'details' => esc_html__('Set the opacity of the logo','wpstream'),
560 -// 'default' => '',
561 -// 'image_size' => 'thumbnail',
562 -// ),
563 - 'wpsteram_player_logo_position' => array(
564 - 'tab' => 'general_options',
565 - 'name' => 'player_logo_position',
566 - 'label' => esc_html__('Position of the video player logo','wpstream'),
567 - 'type' => 'select',
568 - 'select_values'=>array(
569 - 'top-left' => esc_html__('Top Left','wpstream'),
570 - 'top-right' => esc_html__('Top Right','wpstream'),
571 - 'bottom-left' => esc_html__('Bottom Left','wpstream'),
572 - 'bottom-right' => esc_html__('Bottom Right','wpstream'),
573 - ),
574 - 'details' => esc_html__('Choose the position of the logo on the video player.','wpstream'),
575 - 'default' => '',
576 - ),
577 192
193 + $event_list = new WP_Query($args);
194 + global $live_event_for_user;
195 + $live_event_for_user = $this->main->wpstream_live_connection->wpstream_get_live_event_for_user();
196 + $pack_details = $this->main->wpstream_live_connection->wpstream_request_pack_data_per_user();
578 197
579 -
580 - 99 => array(
581 - 'tab' => 'default_options',
582 - 'label' => esc_html__('Events Options ','wpstream'),
583 - 'name' => 'user_streaming_global_channel_options',
584 - 'type' => 'user_streaming_global_channel_options',
585 - 'details' => esc_html__('Global Options for live events.','wpstream'),
586 - ),
198 + $this->main->show_user_data($pack_details);
587 199
588 - 100 => array(
589 - 'tab' => 'default_options_vod',
590 - 'label' => esc_html__('Autoplay','wpstream'),
591 - 'name' => 'vod_autoplay',
592 - 'type' => 'slidertoogle',
593 - 'details' => esc_html__('If enabled, video will attempt to start playing automatically. This is only achievable in some browsers.','wpstream'),
594 - ),
595 200
201 +
202 +
203 + if( $event_list->have_posts()){
596 204
597 - 101 => array(
598 - 'tab' => 'default_options_vod',
599 - 'label' => esc_html__('Start Muted','wpstream'),
600 - 'name' => 'vod_start_muted',
601 - 'type' => 'slidertoogle',
602 - 'details' => esc_html__('If enabled, video will start muted. This may increase the rate of autoplay in some browsers.','wpstream'),
603 - ),
205 +
206 + print '<div class="pack_details_wrapper_transparent">
207 + <h3>'.__('Your Pay-Per-View Channel List','wpstream').'</h3>';
208 +
209 +
210 + $link_new = admin_url('post-new.php?post_type=product').'&new_stream='. rawurlencode('new');
604 211
605 - 102 => array(
606 - 'tab' => 'default_options_vod',
607 - 'label' => esc_html__('Lock To Website','wpstream'),
608 - 'name' => 'vod_domain_lock',
609 - 'type' => 'slidertoogle',
610 - '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') ),
611 - ),
612 -
613 - 103 => array(
614 - 'tab' => 'default_options_vod',
615 - 'label' => esc_html__('Encrypt Video','wpstream'),
616 - 'name' => 'vod_encrypt',
617 - 'type' => 'slidertoogle',
618 - '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'),
619 - ),
620 -
621 - /* 'vod_domain_lock' =>array(
622 - 'name' => esc_html__('Video On Demand - Lock To Website','wpstream'),
623 - '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') ),
624 - 'defaults' => 'no',
625 - ),
626 - 'vod_encrypt' =>array(
627 - 'name' => esc_html__('Encrypt Video on Demand','wpstream'),
628 - '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'),
629 - 'defaults' => 'no',
630 - ),*/
212 + print '<a href="'.esc_url($link_new).'" class="wpstream_create_new_product_link">'.esc_html__('Create new pay-per-view channel.','wpstream').'</a>';
213 + print '</div>';
631 214
632 - 104 => array(
633 - 'tab' => 'support_tab',
634 - 'label' => esc_html__('Logs','wpstream'),
635 - 'name' => 'logs',
636 - 'type' => 'logs_table',
637 - 'details' => esc_html__('This is the error log of the plugin.','wpstream'),
638 - )
639 -
640 - );
641 - }
215 + print '<div style="clear: both;"></div><div class="event_list_wrapper">';
642 216
643 - /**
644 - * Stop the request unless the current user may manage plugin settings.
645 - *
646 - * Shared by the settings and credentials screens so both enforce the
647 - * same capability (`manage_options`).
648 - *
649 - * @return void
650 - */
651 - private function wpstream_require_settings_capability(){
652 - if ( ! current_user_can( 'manage_options' ) ) {
653 - wp_die( esc_html__( 'Not enough permissions to make this change', 'wpstream' ) );
654 - }
655 - }
217 + while ($event_list->have_posts()): $event_list->the_post();
656 218
657 - /**
658 - * Render the WpStream Settings admin screen and persist submitted values.
659 - *
660 - * On POST it verifies the nonce, then stores only the fields declared for
661 - * the active tab as `wpstream_*` options (the streamer role is checked
662 - * against the site's roles, the "default options" tab folds its checkboxes
663 - * into one map), and flushes rewrite rules. It then renders the tabbed
664 - * form from the same field definitions. Echoes HTML.
665 - *
666 - * @return void
667 - */
668 - public function wpstream_settings(){
219 + $the_id = get_the_ID();
220 + $is_subscription_live_event = esc_html(get_post_meta($the_id,'_subscript_live_event',true));
221 + $term_list = wp_get_post_terms($the_id, 'product_type');
669 222
670 - // Admin-only screen.
671 - $this->wpstream_require_settings_capability();
223 + if( $term_list[0]->name=='subscription' && $is_subscription_live_event=='no'){
224 + continue;
225 + }
672 226
227 + $this->wpstream_live_stream_unit($the_id);
673 228
674 - $active_tab = isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'general_options';
229 + endwhile;
675 230
676 - /**
677 - * Filter the settings field definitions.
678 - *
679 - * Field definitions drive both the save allow-list and the rendered
680 - * form, so an added field (`tab`, `label`, `name`, `type`, `details`)
681 - * is rendered on its tab and saved as the `wpstream_{name}` option
682 - * through sanitize_text_field().
683 - *
684 - * @since 4.14.0
685 - *
686 - * @param array $fields Field definitions.
687 - * @param string $active_tab Tab being rendered or saved.
688 - */
689 - $wpstream_settings_array = (array) apply_filters( 'wpstream_settings_fields', $this->wpstream_settings_fields(), $active_tab );
231 + print'</div>';
232 + $no_channel=1;
233 + }else{
234 + $no_channel=0;
235 + }
690 236
691 - /**
692 - * Filter the settings tabs.
693 - *
694 - * Added tabs render their fields from `wpstream_settings_fields`
695 - * and fire `wpstream_settings_tab_{slug}` for a custom body.
696 - *
697 - * @since 4.14.0
698 - *
699 - * @param array $tabs Tab slug => label.
700 - * @param string $active_tab Tab being rendered or saved.
701 - */
702 - $tabs = (array) apply_filters(
703 - 'wpstream_settings_tabs',
704 - array(
705 - 'general_options' => esc_html__( 'General Options', 'wpstream' ),
706 - 'default_options' => esc_html__( 'Default Channel Settings', 'wpstream' ),
707 - 'default_options_vod' => esc_html__( 'VOD Settings', 'wpstream' ),
708 - 'subscription_options' => esc_html__( 'Subscription Options', 'wpstream' ),
709 - 'messages_options' => esc_html__( 'Customize Messages', 'wpstream' ),
710 - 'support_tab' => esc_html__( 'Support', 'wpstream' ),
711 - ),
712 - $active_tab
713 - );
714 237
715 - // Handle a settings submission.
716 - if($_SERVER['REQUEST_METHOD'] === 'POST'){
717 - // CSRF protection.
718 - check_admin_referer( 'wpstream-settings-nonce', 'wpstream-settings-nonce' );
238 + $ajax_nonce = wp_create_nonce( "wpstream_start_event_nonce" );
239 + print '<input type="hidden" id="wpstream_start_event_nonce" value="'.$ajax_nonce.'">';
240 + $current_user = wp_get_current_user();
241 + $allowded_html = array();
242 + $userID = $current_user->ID;
243 + $user_live_streams = get_user_meta($userID,'live_shows');
719 244
720 - // Only the fields declared for the active tab may be persisted.
721 - $allowed = array();
722 - foreach ( $wpstream_settings_array as $field ) {
723 - if ( isset( $field['tab'], $field['name'] ) && $field['tab'] === $active_tab ) {
724 - $allowed[] = $field['name'];
725 - }
726 - }
727 245
728 - // Option values as they were before this submission, and as written by it
729 - // (both keyed by option name) — announced together once the save is complete.
730 - $previous_options = array();
731 - $saved_options = array();
246 + wp_reset_postdata();
732 247
733 - // If the "channel type" section was shown but no streamer role was chosen, clear the stored role.
734 - if( isset($_POST['user_streaming_channel_type_hidden']) && intval($_POST['user_streaming_channel_type_hidden'])==1 && !isset($_POST['stream_role']) ){
735 - $previous_options['wpstream_stream_role'] = get_option( 'wpstream_stream_role' );
736 - $saved_options['wpstream_stream_role'] = '';
737 - update_option( sanitize_key('wpstream_stream_role'), '' );
738 - }
739 248
740 - // Persist each declared field as a sanitized wpstream_* option; anything else is ignored.
741 - foreach ( $allowed as $variable ) {
742 - if ( ! isset( $_POST[ $variable ] ) ) {
743 - continue;
744 - }
745 - $value = $_POST[ $variable ];
746 249
747 - // Streamer role is a list of role slugs; keep only the roles the form offers
748 - // (editable roles minus administrator, who can always broadcast).
749 - if ( 'stream_role' === $variable ) {
750 - $known = array_diff( array_keys( get_editable_roles() ), array( 'administrator' ) );
751 - $roles = array_values( array_intersect( (array) $value, $known ) );
752 - $previous_options['wpstream_stream_role'] = get_option( 'wpstream_stream_role' );
753 - $saved_options['wpstream_stream_role'] = $roles;
754 - update_option( 'wpstream_stream_role', $roles );
755 - continue;
756 - }
757 250
758 - $option_name = sanitize_key( 'wpstream_' . $variable );
759 - $previous_options[ $option_name ] = get_option( $option_name );
760 - $saved_options[ $option_name ] = sanitize_text_field( $value );
761 - update_option( $option_name, $saved_options[ $option_name ] );
762 - }
251 + // free
763 252
253 + $args_free = array(
254 + 'posts_per_page' => -1,
255 + 'post_type' => 'wpstream_product',
256 + 'post_status' => 'publish',
257 + 'meta_query' => array(
258 + 'relation' => 'AND',
259 + array(
260 + 'key' => 'wpstream_product_type',
261 + 'value' => 1,
262 + 'compare' => '==',
263 + ),
764 264
765 - // On the "default options" tab, fold the per-option checkboxes into a single 1/0 map.
766 - if ( 'default_options' === $active_tab ) {
767 - $event_settings=array();
768 - foreach($this->global_event_options as $key=>$option){
769 - $event_settings[$key]='';
770 - if(isset($_POST['wpstream_event_set_'.$key]) && $_POST['wpstream_event_set_'.$key]=='on'){
771 - $event_settings[$key]=1;
772 - }else{
773 - $event_settings[$key]=0;
774 - }
775 - }
776 - $this->main->channel_settings->apply(
777 - array(
778 - 'type' => 'save_defaults',
779 - 'options' => $event_settings,
780 - )
781 - );
782 - }
265 + ),
783 266
784 267
268 + );
269 + $event_list_free = new WP_Query($args_free);
785 270
786 271
787 - // reset permalinkgs
788 - // Slug settings can change permalinks, so drop and rebuild the rewrite rules.
789 - global $wp_rewrite;
272 + if( $event_list_free->have_posts()){
273 + print '<div class="pack_details_wrapper_transparent">
274 + <h3>'.__('Your Free Channel List','wpestate').'</h3>';
790 275
791 - update_option( "rewrite_rules", FALSE );
792 - $wp_rewrite->flush_rules( true );
276 + $link_new = admin_url('post-new.php?post_type=wpstream_product');
277 + print '<a href="'.esc_url($link_new).'" class="wpstream_create_new_product_link">'.esc_html__('Create new free channel.','wpstream').'</a>';
278 + print '</div>';
279 + print '<div style="clear: both;"></div><div class="event_list_wrapper">';
793 280
794 - /**
795 - * Fires after a WpStream Settings tab was saved and the rewrite
796 - * rules rebuilt. Default Channel Settings saved from the
797 - * "default options" tab are announced separately by
798 - * wpstream_default_channel_settings_saved.
799 - *
800 - * @since 4.14.0
801 - *
802 - * @param string $active_tab Settings tab that was submitted.
803 - * @param array $saved Option values written, keyed by option name.
804 - * @param array $previous The values those options held before.
805 - */
806 - do_action( 'wpstream_settings_saved', $active_tab, $saved_options, $previous_options );
281 + while ($event_list_free->have_posts()): $event_list_free->the_post();
807 282
808 - }
809 283
284 + $the_id = get_the_ID();
810 285
286 + if( get_post_meta ($the_id,'event_passed',true)!=1){
287 + $this->wpstream_live_stream_unit($the_id);
288 + }
811 289
290 + endwhile;
812 291
813 - // Open the settings panel and the form.
814 - print '<div class="theme_options_tab_wpstream" style="display:block;" >
815 - <h1>'.__('WpStream Settings','wpstream').'</h1>
816 - <form method="post" action="" >';
292 + print'</div>';
293 + $no_channel=1;
294 + }else{
295 + $no_channel=0;
296 + }
817 297
818 - // Tab navigation bar; the current tab gets the nav-tab-active class.
819 - print '<h2 class="nav-tab-wrapper">';
820 - foreach ( $tabs as $tab_slug => $tab_label ) {
821 - $tab_slug = sanitize_key( $tab_slug );
822 - print '<a href="' . esc_url( add_query_arg( array( 'page' => 'wpstream_settings', 'tab' => $tab_slug ), admin_url( 'admin.php' ) ) ) . '" class="nav-tab ' . ( $active_tab === $tab_slug ? 'nav-tab-active' : '' ) . '">' . esc_html( $tab_label ) . '</a>';
823 - }
824 - print '</h2>';
825 - $help_link='';
826 298
827 - // Basic-stream mode disables/annotates some default-channel controls.
828 - $is_basic_stream_mode = $this->wpstream_is_basic_streaming_mode();
829 - print '<div class="wpstream_option_wrapper">';
830 299
831 - // Pick the contextual "Video Help" docs link for the active tab.
832 - switch ($active_tab) {
833 - case 'general_options':
834 - $help_link='https://docs.wpstream.net/docs/general-settings/';
835 - break;
836 - case 'default_options':
837 - $help_link='https://docs.wpstream.net/docs/default-channel-settings/';
838 - break;
839 - case 'default_options_vod':
840 - $help_link='https://docs.wpstream.net/docs/vod-settings/';
841 - break;
842 - case 'subscription_options':
843 - $help_link='https://docs.wpstream.net/docs/subscription-options/';
844 - break;
845 - case 'messages_options':
846 - $help_link='https://docs.wpstream.net/docs/customize-messages/';
847 - break;
848 - }
300 + if($no_channel==0){
301 + $link_new_paid = admin_url('post-new.php?post_type=product').'&new_stream='. rawurlencode('new');
302 + $link_new_free = admin_url('post-new.php?post_type=wpstream_product');
303 + print '<div class="no_events_warning"> '.__('* You don\'t have any live channels!','wpstream');
849 304
850 - print '<div class="options_wrapper">';
851 - // Render each field that belongs to the active tab.
852 - foreach ($wpstream_settings_array as $key=>$option){
853 - // Skip fields from other tabs.
854 - if($option['tab']!=$active_tab){
855 - continue;
856 - }
305 + print '<a href="'.esc_url($link_new_free).'" class="wpstream_no_chanel_add_channel">'.esc_html__('Add new free channel ','wpstream').'</a>';
306 + print '<a href="'.esc_url($link_new_paid).'" class="wpstream_no_chanel_add_channel">'.esc_html__('Add new pay-per-view channel ','wpstream').'</a>';
857 307
858 - // Intro blurb (and basic-mode notice) shown above the global channel options control.
859 - if ( $option['type']=='user_streaming_global_channel_options' ) {
860 - print '<div class="default-channel-settings-info">';
861 - print esc_html__( 'These settings will apply to newly created channels; existing channels will not change settings if you change them here', 'wpstream');
862 - if ( $is_basic_stream_mode ) {
863 - $this->wpstream_basic_stream_mode_message();
864 - }
865 - print '</div>';
308 + print '</div>';
309 + }
866 310
867 - }
868 - print '<div class="wpstream_option">';
869 - // Load this option's stored value.
870 - $options_value = get_option('wpstream_'.$option['name']) ;
871 311
872 312
873 - // Render the appropriate control for this field's type.
874 - switch( $option['type'] ) {
875 - // Multi-select of WordPress user roles allowed to broadcast.
876 - case 'user_roles':
877 - print '<label for="'.esc_attr( $option['name'] ).'">'.wp_kses_post( $option['label'] ).'</label>';
878 - print $this->wpstream_select_user_roles(esc_attr( $option['name'] ),$options_value);
879 - print '<div class="settings_details">'.wp_kses_post( $option['details'] ).'</div>';
880 - break;
881 - // The grid of default per-event streaming options.
882 - case 'user_streaming_global_channel_options':
883 - $exclude_array=array();
884 - $this->user_streaming_global_channel_options(
885 - $option['name'],
886 - $options_value,
887 - $exclude_array,
888 - $is_basic_stream_mode
889 - );
890 - break;
891 - // Plain text input; falls back to the field's default when unset.
892 - case 'text':
893 - if($options_value==''){
894 - $options_value='';
895 - if(isset($option['default'])){
896 - $options_value=$option['default'];
897 - }
898 - }
899 313
900 - print '<label for="'.esc_attr( $option['name'] ).'">'.wp_kses_post( $option['label'] ).'</label>';
901 - print '<input class="wpstream-text-input-setting" id="'.esc_attr( $option['name'] ).'" type="'.$option['type'].'" size="36" name="'.esc_attr( $option['name'] ).'" value="'.esc_attr($options_value).'" />';
902 - print '<div class="settings_details">'.wp_kses_post( $option['details'] ).'</div>';
903 - break;
904 - // Dropdown built from the field's select_values map; a hidden mirror marks the field as present.
905 - case 'select':
906 - print '<label for="'.esc_attr( $option['name'] ).'">'.wp_kses_post( $option['label'] ).'</label>';
907 - print '<select id="'.esc_attr( $option['name'] ).'" name="'.esc_attr( $option['name'] ).'" >';
908 - foreach($option['select_values'] as $key=>$value){
909 - print '<option value="'.$key.'" ';
910 - // Pre-select the currently stored value.
911 - if( $key == esc_html($options_value) ){
912 - print ' selected ';
913 - }
914 - print '>'.$value.'</option>';
915 - }
916 - print '</select>';
917 - print '<input type="hidden" name="'.esc_attr( $option['name'] ).'_hidden" value="1" >';
918 - print '<div class="settings_details">'.wp_kses_post( $option['details'] ).'</div>';
919 - break;
920 - // On/off switch; a hidden 0-valued twin ensures "off" posts a value.
921 - case 'slidertoogle':
922 - print '<label for="'.esc_attr( $option['name'] ).'">'.wp_kses_post( $option['label'] ).'</label>';
923 - print '<div style="display: flex; gap: 25px; justify-content: space-between;">';
924 - print '<div class="settings_details">'.wp_kses_post( $option['details'] ).'</div>';
925 - print '<label class="wpstream_switch">
926 - <input type="hidden" class="wpstream_event_option_itemc" value="0" name="'.esc_attr( $option['name'] ).'" >
927 - <input type="checkbox" class="wpstream_event_option_itemc" value="1" name="'.esc_attr( $option['name'] ).'" ';
928 - if( intval($options_value) !==0 ){
929 - print ' checked ';
930 - }
931 - print '> <span class="wpstream_slider round"></span>';
932 - print '</label>';
933 - print '</div>';
934 - break;
935 - // Media-library image picker with preview + upload/remove buttons.
936 - case 'image':
937 - $image_url = $options_value ? esc_url($options_value) : '';
938 - $has_image = !empty($image_url);
314 + }
315 + // end wpstream_new_general_set
939 316
940 - print '<label for="' . esc_attr( $option['name'] ) . '">' . wp_kses_post( $option['label'] ) . '</label>';
941 - print '<div class="wpstream-image-upload-wrapper">';
942 - print '<input type="hidden" id="' . esc_attr( $option['name'] ) . '" name="' . esc_attr( $option['name'] ) . '" value="' . $image_url . '" />';
943 317
944 - // Preview area
945 - print '<div class="wpstream-image-preview" style="' . (!$has_image ? 'display:none;' : '') . '">';
946 - print '<img src="' . $image_url . '" alt="Preview" />';
947 - print '</div>';
318 +
319 +
320 +
321 +
322 + /**
323 + * Shows event unit card in admin
324 + *
325 + * @since 3.0.1
326 + */
327 + public function wpstream_live_stream_unit($the_id,$is_front=''){
328 + global $live_event_for_user;
329 + $current_user = wp_get_current_user();
330 +
331 + if(!current_user_can('administrator')){
332 + if($is_front=='' ){
333 + print '<div class="event_list_unit">';
334 + esc_html_e('You are not allowed to broadcast.','wpstream');
335 + print '</div>';
336 + return;
337 + }
338 +
339 + }
340 +
341 + if( !$this->main->wpstream_check_user_can_stream() ){
342 + print '<div class="event_list_unit">';
343 + esc_html_e('You are not allowed to broadcast','wpstream');
344 + print '</div>';
345 + return;
346 +
347 + }
348 +
349 +
350 + $live_class='';
351 + if(isset($live_event_for_user[$the_id])) {
352 + $live_class=" wpstream_show_started";
353 + }
948 354
949 - // Upload/remove buttons
950 - print '<div class="wpstream-image-upload-buttons">';
951 - print '<button type="button" class="wpstream-upload-image button">' . esc_html__('Upload Image', 'wpstream') . '</button>';
952 - print '<button type="button" class="wpstream-remove-image button" style="' . (!$has_image ? 'display:none;' : '') . '">' . esc_html__('Remove Image', 'wpstream') . '</button>';
953 - print '</div>';
954 355
955 - print '</div>';
956 - print '<div class="settings_details">' . wp_kses_post( $option['details'] ) . '</div>';
957 - break;
958 - // 0-100 range slider.
959 - case 'range':
960 - print '<label for="'.esc_attr( $option['name'] ).'">'.wp_kses_post( $option['label'] ).'</label>';
961 - print '<input class="wpstream-range-input" type="range" id="'.esc_attr( $option['name'] ).'" name="'.esc_attr( $option['name'] ).'" min="0" max="100" step="10" value="'.esc_attr($options_value).'" />';
962 - print '<div class="settings_details">'.wp_kses_post( $option['details'] ).'</div>';
963 - break;
964 - // Delegates to the Support tab renderer (plugin error log table).
965 - case 'logs_table':
966 - $this->wpstream_support_tab();
967 - break;
968 - }
969 - print '</div>';
970 - }
356 + print '<div class="event_list_unit '.$live_class.' event_unit_style_'.esc_attr($is_front).'">';
357 + if(has_post_thumbnail($the_id)){
358 + $thumb = get_the_post_thumbnail_url($the_id,'thumbnail');
359 + }else{
360 + $thumb= plugin_dir_url( dirname( __FILE__ ) ). 'img/default_150.jpg';
361 + }
971 362
972 - /**
973 - * Fires inside the options wrapper of a settings tab, after its fields.
974 - *
975 - * The dynamic part is the tab slug. Callbacks echo their own markup
976 - * and own its escaping; fields they add to the form are saved only
977 - * when declared through `wpstream_settings_fields`.
978 - *
979 - * @since 4.14.0
980 - *
981 - * @param string $active_tab Tab being rendered.
982 - */
983 - do_action( 'wpstream_settings_tab_' . $active_tab, $active_tab );
984 - print '</div>'; // options wrapper
985 - // Contextual help link for tabs that have one.
986 - if($help_link!==''){
987 - print '<div class="wpstream_options_help"><a href="'.esc_url($help_link).'" target="_blank" >'.esc_html__('Video Help','wpstream').'</a></div>';
988 - }
989 - print '</div>';
363 + global $wpstream_plugin;
364 +
365 +
366 + print '<div class="event_thumb_wrapper"><img class="event_thumb" src="'.$thumb.'" alt="show_imagge"></div>';
367 + print '<h3 data-prodid="'.$the_id.'">'.get_the_title($the_id).' - '.esc_html__('Id','wpstream').': '.$the_id.'<a class="view_channel" href="'.get_permalink($the_id).'" target="_blank">'.esc_html__('View Channel','wprentals').'</a></h3> ';
990 368
369 +
370 +
371 + $live_event_stream_name = get_post_meta($the_id,'live_event_stream_name',true);
372 + $live_event_array = get_post_meta($the_id,'live_event_uri',true);
373 + $live_event_uri = '';
991 374
992 - // Save button (hidden on the read-only Support tab).
993 - if ( $active_tab != 'support_tab') {
994 - print '<div class="wpstream-save-settings">';
995 - print '<input type="submit" name="submit" class="wpstream_button wpstream_button_action" value="'.__('Save Changes','wpstream').'" />';
996 - print '<div class="spinner"></div>';
997 - print '</div>';
998 - }
999 375
1000 - // CSRF nonce for the submission handled at the top of this method.
1001 - print '<input id="wpstream-settings-nonce" name="wpstream-settings-nonce" type="hidden" value="'.wp_create_nonce('wpstream-settings-nonce').'" /> ';
1002 - print '</form>';
1003 - print '</div>';
376 + $pending_streaming_class = '';
377 + $wpstream_ready_to_stream_class = '';
378 + $external_software_streaming_class = '';
379 + $wpstream_no_stream_class = '';
380 + $carnat_key1 = '';
381 + $carnat_key2 = '';
382 + $uri = '';
383 + $webcaster_url = '';
384 + $rtmp_ip_uri = '';
385 + $uri_ip = '';
386 + $server_id = '';
387 + $obs_uri = '';
388 + $obs_stream = '';
389 + $webCasterUrl = '';
390 +
391 + if( $live_event_for_user=='' && $is_front=='front' ){
392 + $live_event_for_user = $this->main->wpstream_live_connection->wpstream_get_live_event_for_user();
393 + }
394 +
395 +
396 +
397 + if(is_array($live_event_for_user) && isset($live_event_for_user[$the_id])) {
1004 398
1005 - }
399 + $pending_streaming_class = 'show_stream_data pending_trigger';
400 + $wpstream_ready_to_stream_class = 'hide_stream_data';
401 + $external_software_streaming_class = 'show_stream_data';
402 + $wpstream_no_stream_class = 'hide_stream_data';
403 + if(isset($live_event_for_user[$the_id]['live_uri'])){
404 + $explode = explode('wpstream.net/', $live_event_for_user[$the_id]['live_uri']);
405 + $uri = $explode[0].'wpstream.net/wpstream/';
406 + $stream_name = get_post_meta($the_id,'live_event_stream_name',true);
407 + $carnat_key1 = $live_event_for_user[$the_id]['carnat1'];
408 + $carnat_key2 = $live_event_for_user[$the_id]['carnat2'];
409 + $webcaster_url = 'https://'.$live_event_for_user[$the_id]['subdomain_key'].'.live.streamer.wpstream.net:8443/'.$live_event_for_user[$the_id]['carnat2'];
1006 410
1007 - /**
1008 - * Get system information for support tab
1009 - *
1010 - * @return array System information
1011 - */
1012 - private function get_system_info() {
1013 - global $wp_version;
411 + $rtmp_ip_uri = 'http://'.$live_event_for_user[$the_id]['ip'].':8444';
412 + $uri_ip= 'rtmp://'.$live_event_for_user[$the_id]['ip'].'/wpstream/';
413 +
414 + // starting from 2.0
415 + $server_id = get_post_meta($the_id,'server_id',true);
416 + $obs_uri = get_post_meta($the_id,'obs_uri',true);
417 + $obs_stream = get_post_meta($the_id,'obs_stream',true);
418 + $webCasterUrl= get_post_meta($the_id,'webCasterUrl',true);
419 +
420 + // if local server id diffrens than wpstream server id it means call was made via api and we use wpstream ip
421 + if( $server_id != $live_event_for_user[$the_id]['api20_event_id'] && $live_event_for_user[$the_id]['api20_event_id']!='' ){
422 +
423 + $server_id=esc_html($live_event_for_user[$the_id]['api20_event_id']);
424 + }
425 +
426 + }
1014 427
1015 - // Environment facts: PHP/WP version, debug flag, memory limit and the plugin version.
1016 - $php_version = phpversion();
1017 - $wp_version_info = $wp_version;
1018 - $site_debug_mode = (defined('WP_DEBUG') && WP_DEBUG);
1019 - $wp_memory_limit = WP_MEMORY_LIMIT;
428 + }else{
1020 429
1021 - $wpstream_version = WPSTREAM_PLUGIN_VERSION;
1022 - $wpstream_plugin_outdated = false;
430 + $pending_streaming_class = 'hide_stream_data';
431 + $wpstream_ready_to_stream_class = 'hide_stream_data';
432 + $external_software_streaming_class = 'hide_stream_data';
433 + $wpstream_no_stream_class = 'show_stream_data';
434 + }
1023 435
1024 - // Check if plugin is outdated
1025 - // Mark the plugin outdated if WP's update transient lists an available update for it.
1026 - $update_plugins = get_site_transient('update_plugins');
1027 - if (isset($update_plugins->response['plugin/wpstream.php'])) {
1028 - $wpstream_plugin_outdated = true;
1029 - }
1030 436
1031 - // Check API status
1032 - // API is "connected" when a WpStream cloud token can be obtained.
1033 - $api_status = false;
1034 - if (method_exists($this->main->wpstream_live_connection, 'is_connected')) {
1035 - $api_status = $this->main->wpstream_live_connection->is_connected();
1036 - }
1037 437
1038 - // Return the collected diagnostics for the Support tab to render.
1039 - return array(
1040 - 'php_version' => $php_version,
1041 - 'wp_version' => $wp_version_info,
1042 - 'site_debug_mode' => $site_debug_mode,
1043 - 'wp_memory_limit' => $wp_memory_limit,
1044 - 'wpstream_version' => $wpstream_version,
1045 - 'wpstream_plugin_outdated' => $wpstream_plugin_outdated,
1046 - 'api_status' => $api_status
1047 - );
1048 - }
1049 438
1050 - /**
1051 - * Render system information HTML
1052 - *
1053 - * Prints the Support-tab diagnostics table (PHP/WP versions, debug mode,
1054 - * memory limit, plugin version + update button, API connection) with a
1055 - * warning/OK dashicon per row. Echoes HTML.
1056 - *
1057 - * @return void
1058 - */
1059 - private function render_system_info() {
1060 - // Pull the current environment diagnostics.
1061 - $system_info = $this->get_system_info();
1062 - // Below: diagnostics table; each row shows a value and a warning/OK indicator based on recommended thresholds.
1063 - ?>
439 + print' <div class="pending_streaming '.$pending_streaming_class.' " data-show-id="'.$the_id.'" data-server-id="'.$server_id.'" data-server-url="'.$rtmp_ip_uri.'">';
440 + print'<div class="wpstream_channel_status not_ready_to_stream"><span class="dashicons dashicons-dismiss"></span>'.esc_html__('Your Event is starting. Please wait...','wprentals').' ';
441 + print '<img class="server_loading_new" src="'.plugin_dir_url( dirname( __FILE__ ) ).'img/loading.gif" alt="loading" /></div>';
442 + print '<div class="multiple_warning_events"> '.esc_html__('To go Live on a Channel, you need to start an Event. It will stop automatically after an hour of inactivity.','wpstream').'</div>';
443 + print '</div>';
1064 444
1065 - <div class="wpstream-system-info">
1066 - <h3><?php esc_html_e('System Information', 'wpstream'); ?></h3>
1067 - <table class="widefat">
1068 - <tbody>
1069 - <tr>
1070 - <td><strong><?php esc_html_e('PHP Version', 'wpstream'); ?></strong></td>
1071 - <td><?php echo esc_html($system_info['php_version']); ?></td>
1072 - <td>
1073 - <?php if (version_compare($system_info['php_version'], '7.4', '<')): ?>
1074 - <span class="dashicons dashicons-warning" style="color: #ffb900;"></span>
1075 - <?php esc_html_e('We recommend PHP 7.4 or higher', 'wpstream'); ?>
1076 - <?php else: ?>
1077 - <span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span>
1078 - <?php endif; ?>
1079 - </td>
1080 - </tr>
1081 - <tr>
1082 - <td><strong><?php esc_html_e('WordPress Version', 'wpstream'); ?></strong></td>
1083 - <td><?php echo esc_html($system_info['wp_version']); ?></td>
1084 - <td>
1085 - <?php if (version_compare($system_info['wp_version'], '5.6', '<')): ?>
1086 - <span class="dashicons dashicons-warning" style="color: #ffb900;"></span>
1087 - <?php esc_html_e('We recommend WordPress 5.6 or higher', 'wpstream'); ?>
1088 - <?php else: ?>
1089 - <span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span>
1090 - <?php endif; ?>
1091 - </td>
1092 - </tr>
1093 - <tr>
1094 - <td><strong><?php esc_html_e('WP Debug Mode', 'wpstream'); ?></strong></td>
1095 - <td><?php echo $system_info['site_debug_mode'] ? esc_html__('Enabled', 'wpstream') : esc_html__('Disabled', 'wpstream'); ?></td>
1096 - <td>
1097 - <?php if ($system_info['site_debug_mode']): ?>
1098 - <span class="dashicons dashicons-info" style="color: #00a0d2;"></span>
1099 - <?php esc_html_e('Debug mode should be disabled on production sites', 'wpstream'); ?>
1100 - <?php else: ?>
1101 - <span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span>
1102 - <?php endif; ?>
1103 - </td>
1104 - </tr>
1105 - <tr>
1106 - <td><strong><?php esc_html_e('WP Memory Limit', 'wpstream'); ?></strong></td>
1107 - <td><?php echo esc_html($system_info['wp_memory_limit']); ?></td>
1108 - <td>
1109 - <?php
1110 - $memory_limit = wp_convert_hr_to_bytes($system_info['wp_memory_limit']);
1111 - if ($memory_limit < 64 * 1024 * 1024): // 64MB
1112 - ?>
1113 - <span class="dashicons dashicons-warning" style="color: #ffb900;"></span>
1114 - <?php esc_html_e('We recommend at least 64MB', 'wpstream'); ?>
1115 - <?php else: ?>
1116 - <span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span>
1117 - <?php endif; ?>
1118 - </td>
1119 - </tr>
1120 - <tr>
1121 - <td><strong><?php esc_html_e('WpStream Version', 'wpstream'); ?></strong></td>
1122 - <td><?php echo esc_html($system_info['wpstream_version']); ?></td>
1123 - <td style="display: flex; align-items: center; gap: 5px;">
1124 - <?php if ($system_info['wpstream_plugin_outdated']): ?>
1125 - <span class="dashicons dashicons-warning" style="color: #ffb900;"></span>
1126 - <?php esc_html_e('Update available', 'wpstream'); ?>
1127 - <div class="update-button-wrapper">
1128 - <button class="wpstream-update-plugin-button button button-primary" data-plugin="wpstream/wpstream.php">
1129 - <?php esc_html_e('Update Now', 'wpstream'); ?>
1130 - </button>
1131 - </div>
1132 - <?php else: ?>
1133 - <span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span>
1134 - <?php endif; ?>
1135 - </td>
1136 - </tr>
1137 - <tr>
1138 - <td><strong><?php esc_html_e('API Connection', 'wpstream'); ?></strong></td>
1139 - <td><?php echo $system_info['api_status'] ? esc_html__('Connected', 'wpstream') : esc_html__('Disconnected', 'wpstream'); ?></td>
1140 - <td>
1141 - <?php if (!$system_info['api_status']): ?>
1142 - <span class="dashicons dashicons-warning" style="color: #ffb900;"></span>
1143 - <?php esc_html_e('API connection issue', 'wpstream'); ?>
1144 - <?php else: ?>
1145 - <span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span>
1146 - <?php endif; ?>
1147 - </td>
1148 - </tr>
1149 - </tbody>
1150 - </table>
1151 - </div>
1152 - <?php
1153 - }
1154 445
1155 - /**
1156 - * Render support tab content
1157 - *
1158 - * Prints the Support tab: the system-info table, a table of relevant active
1159 - * plugins with update tooltips, and a table of the plugin's recent logs.
1160 - * Echoes HTML.
1161 - *
1162 - * @return void
1163 - */
1164 - public function wpstream_support_tab() {
1165 - // Below: Support tab markup (system info, active plugins table, recent logs table).
1166 - ?>
1167 - <div class="wrap">
1168 - <div class="wpstream-support-tab-root">
1169 - <?php $this->render_system_info(); ?>
446 + print '<div class="wpstream_ready_to_stream '.$wpstream_ready_to_stream_class.'">';
447 + print '<div class="wpstream_channel_status"><span class="dashicons dashicons-yes"></span>'.esc_html__('Ready to Go Live !','wpstream').'</div>';
448 + print '<div class="start_webcaster wpstream_button" data-webcaster-url="'.$webCasterUrl.'" >'.esc_html__('Go Live from Browser','wpstream').'</div>';
449 + print '<div class="start_external wpstream_button" >'.esc_html__('Go Live with External Broadcaster','wpstream').'</div>';
450 + print '<div class="stop_server wpstream_button" data-show-id="'.$the_id.'">'.esc_html__('Turn Off Channel','wpstream').'</div>';
451 + $ajax_nonce_stop = wp_create_nonce( "wpstream_stop_event_nonce" );
452 + print '<input type="hidden" id="wpstream_stop_event_nonce" value="'.$ajax_nonce_stop.'">';
453 + print '</div>';
1170 454
1171 - <div class="wpstream-plugins-table-container">
1172 - <h3><?php esc_html_e('Active Plugins', 'wpstream'); ?></h3>
1173 - <table class="widefat wpstream-plugins-table">
1174 - <thead>
1175 - <tr>
1176 - <th><?php esc_html_e('Plugin', 'wpstream'); ?></th>
1177 - <th><?php esc_html_e('Version', 'wpstream'); ?></th>
1178 - </tr>
1179 - </thead>
1180 - <tbody>
1181 - <?php
1182 - // Build the relevant-plugins list; show a placeholder row when none are found.
1183 - $plugins_data = $this->wpstream_get_plugins_data();
1184 - if (empty($plugins_data)) {
1185 - echo '<tr><td colspan="3">' . esc_html__('No WPStream plugins found.', 'wpstream') . '</td></tr>';
1186 - } else {
1187 - // One row per plugin; append an "update available" tooltip when a newer version exists.
1188 - foreach ($plugins_data as $plugin) {
1189 - echo '<tr>';
1190 - echo '<td>' . esc_html($plugin['name']) . '</td>';
1191 - echo '<td>';
1192 - echo esc_html($plugin['version']);
1193 - if ( isset($plugin['new_version']) ) {
1194 - echo '<div class="wpstream-tooltip-container">';
1195 - echo '<span class="dashicons dashicons-info wpstream-tooltip" title="' . esc_attr($plugin['new_version']) . '">';
1196 - echo '</span>';
1197 - echo '<div class="wpstream-custom-tooltip">' . sprintf(
1198 - esc_html__('A new version is available: %s', 'wpstream'),
1199 - esc_html($plugin['new_version'])
1200 - ) . '</div>';
1201 - echo '</div>';
1202 - }
1203 - echo '</td>';
1204 - echo '</tr>';
1205 - }
1206 - }
1207 - ?>
1208 - </tbody>
1209 - </table>
1210 - </div>
1211 455
1212 - <div class="wpstream-logs-table-container">
1213 - <h3><?php esc_html_e('Recent Logs', 'wpstream'); ?></h3>
1214 - <table class="widefat wpstream-logs-table">
1215 - <thead>
1216 - <tr>
1217 - <th><?php esc_html_e('Time', 'wpstream'); ?></th>
1218 - <th><?php esc_html_e('Type', 'wpstream'); ?></th>
1219 - <th><?php esc_html_e('Description', 'wpstream'); ?></th>
1220 - </tr>
1221 - </thead>
1222 - <tbody>
1223 - <?php
1224 - // Load the stored plugin logs; show a placeholder row when empty.
1225 - $logs = get_option('wpstream_logs');
1226 - if ( !is_array($logs) || empty($logs) ) {
1227 - echo '<tr><td colspan="3">' . esc_html__('No logs found.', 'wpstream') . '</td></tr>';
1228 - } else {
1229 - // One row per log entry (time, type, description).
1230 - foreach ($logs as $log) {
1231 - echo '<tr>';
1232 - echo '<td>' . esc_html(date('Y-m-d H:i:s', $log['timestamp'] ) ) . '</td>';
1233 - echo '<td>' . esc_html($log['type']) . '</td>';
1234 - echo '<td>' . esc_html($log['description']) . '</td>';
1235 - echo '</tr>';
1236 - }
1237 - }
1238 - ?>
1239 - </tbody>
1240 - </table>
1241 - </div>
1242 - </div>
1243 - </div>
1244 - <?php
1245 - }
1246 456
1247 - /**
1248 - * Get plugins data.
1249 - *
1250 - * Collects name/version/active/update info for a fixed allow-list of plugins
1251 - * relevant to WpStream (WpStream, WooCommerce, Meta Box, One Click Demo
1252 - * Import, Better Messages), used by the Support tab.
1253 - *
1254 - * @return array List of plugin info arrays (name, version, path, active, needs_update, [new_version]).
1255 - */
1256 - public function wpstream_get_plugins_data() {
1257 - // Ensure get_plugins() is available in non-admin contexts.
1258 - if (!function_exists('get_plugins')) {
1259 - require_once ABSPATH . 'wp-admin/includes/plugin.php';
1260 - }
457 + print '<div class="external_software_streaming '. $external_software_streaming_class.' ">';
458 + print '<div class="event_list_unit_notificationx"><strong>'.__('Server:').' </strong> <div class="wpstream_live_uri_text">' . $obs_uri.'</div><div class="copy_live_uri">'.__('copy to clipboard','wpstream').'</div>';
459 + print '<div class="event_list_stream_key_wrap"><strong>'.__('Stream Key:').' </strong><div class="wpstream_live_key_text">'. $obs_stream.'</div><div class="copy_live_key">'.__('copy to clipboard','wpstream').'</div></div>';
460 + print '<div class="warning_stream">'.
461 + sprintf(esc_html__('Use %s on your Windows or Mac computer, or the Larix Broadcaster app on your %s or %s smartphone/tablet. Various other Apps and devices are compatible. See %s for more details.','wpstream'),'<a href="https://obsproject.com/download" target="_blank">OBS Studio</a>','<a href="https://apps.apple.com/us/app/larix-broadcaster/id1042474385" target="_blank"> iOS</a>','<a href="https://play.google.com/store/apps/details?id=com.wmspanel.larix_broadcaster&hl=en" target="_blank">Android</a>','<a href="https://wpstream.net/external-broadcasters/" target="_blank">this page</a>').'</div>';
462 + print ' <img class="how_to" src="'.plugin_dir_url( dirname( __FILE__ ) ).'img/how_to_obs.jpg" alt="show_imagge">';
463 + print'</div>';
464 + print'</div>';
1261 465
1262 - // Get all installed plugins
1263 - $all_plugins = get_plugins();
1264 - $update_data = get_site_transient('update_plugins');
1265 - $all_plugins_info = [];
1266 466
1267 - // We want to get data only for the WpStream plugins
1268 - $wpstream_plugins = array(
1269 - 'WpStream' => array(
1270 - 'path' => 'wpstream/wpstream.php',
1271 - ),
1272 - 'WooCommerce' => array(
1273 - 'path' => 'woocommerce/woocommerce.php',
1274 - ),
1275 - 'Meta Box' => array(
1276 - 'path' => 'meta-box/meta-box.php',
1277 - ),
1278 - 'One Click Demo Import' => array(
1279 - 'path' => 'one-click-demo-import/one-click-demo-import.php',
1280 - ),
1281 - 'Better Messages' => array(
1282 - 'path' => 'bp-better-messages/bp-better-messages.php',
1283 - ),
1284 467
1285 - );
1286 468
1287 - // Build an info array for every installed plugin (filtered down afterwards).
1288 - foreach ($all_plugins as $plugin_path => $plugin_data) {
1289 - $is_active = is_plugin_active( $plugin_path );
1290 - $has_update = isset( $update_data->response[$plugin_path] );
469 + print '<div class="wpstream_no_stream '.$wpstream_no_stream_class.' ">';
470 + print '<div class="event_list_unit_notificationx"><span class="server_notification">'.__('Broadcast Offline.','wpestream').' <img class="server_loading" src="'.plugins_url().'/wpstream/img/loading.gif" alt="loading" /></span></div>';
471 + print '<div class="record_wrapper">Record ? <input type="checkbox" name="record_event" class="record_event"/>Yes</div>';
472 + print '<div class="encrypt_wrapper">Encrypt ? <input type="checkbox" name="encrypt_event" class="encrypt_event"/>Yes</div>';
473 +
474 + print '<input class="start_event wpstream_button" type="button" data-show-id="'.$the_id.'" value="'.esc_html__('Start Live Event','wpstream').'">';
475 +
476 + if($is_front==''){
477 + print '<input class="close_event wpstream_button" type="button" data-show-id="'.$the_id.'" value="'.esc_html__('Delete Channel','wpstream').'">';
478 + }
479 +
480 + print '<div class="multiple_warning_events"> '.esc_html__('To go Live on a Channel, you need to start an Event. It will stop automatically after an hour of inactivity.','wpstream').'</div>';
481 +
482 +
483 + print '<div class="wpstream_social_media_broadcast">';
484 +
485 + print '<div class="wpstream_social_media_unit"><input type="checkbox" class="wpstream_on_facebook" id="wpstream_on_facebook'.$the_id.'"><label for="wpstream_on_facebook'.$the_id.'">'.esc_html__('Stream on Facebook','wpstream').'</label>';
486 + print '</div>';
1291 487
1292 - $plugin_info = [
1293 - 'name' => $plugin_data['Name'],
1294 - 'version' => $plugin_data['Version'],
1295 - 'path' => $plugin_path,
1296 - 'active' => $is_active ? 'Yes' : 'No',
1297 - 'needs_update' => $has_update ? 'Yes' : 'No'
1298 - ];
488 + print '<div class="wpstream_social_media_unit"><input type="checkbox" class="wpstream_on_youtube" id="wpstream_on_youtube'.$the_id.'"><label for="wpstream_on_youtube'.$the_id.'">'.esc_html__('Stream on Youtube','wpstream').'</label>';
489 + print '</div>';
1299 490
1300 - // Record the offered version when an update is pending.
1301 - if ($has_update) {
1302 - $plugin_info['new_version'] = $update_data->response[$plugin_path]->new_version;
1303 - }
1304 491
1305 - $all_plugins_info[] = $plugin_info;
1306 - }
492 + print '<div class="wpstream_social_media_unit"><input type="checkbox" class="wpstream_on_twich" id="wpstream_on_twich'.$the_id.'"><label for="wpstream_on_twich'.$the_id.'">'.esc_html__('Stream on Twich','wpstream').'</label>';
493 + print '</div>';
494 +
495 + print '<div class="wpstream_on_facebook_container wpstream_social_stream_container">';
496 + print 'Some Facebook Settings for event '.$the_id;
497 + print '</div>';
498 +
499 + print '<div class="wpstream_on_youtube_container wpstream_social_stream_container">';
500 + print '<label for="wpstream_youtube_rtmp'.$the_id.'">'.esc_html__('Youtube RTMP','wpstream').'</label>';
501 + print '<input type="text" class="wpstream_youtube_rtmp" id="wpstream_youtube_rtmp'.$the_id.'" value="'.esc_html(get_post_meta($the_id,'wpstream_youtube_rtmp',true )).'">';
502 + print '</div>';
503 +
504 + print '<div class="wpstream_on_twich_container wpstream_social_stream_container">';
505 + print '<label for="wpstream_twich_rtmp'.$the_id.'">'.esc_html__('Twich RTMP','wpstream').'</label>';
506 + print '<input type="text" class="wpstream_twich_rtmp" id="wpstream_twich_rtmp'.$the_id.'" value="'.esc_html(get_post_meta($the_id,'wpstream_twich_rtmp',true )).'">';
507 + print '</div>';
1307 508
1308 - // Filter out the elements from $all_plugins_info that are not in $wpstream_plugins
1309 - foreach ( $all_plugins_info as $key => $plugin_info ) {
1310 - // compare $plugin_info['path'] against the path property on each $wpstream_plugins element item
1311 - // if the path is not in $wpstream_plugins, unset the element
1312 - if ( !in_array( $plugin_info['path'], array_column( $wpstream_plugins, 'path' ) ) ) {
1313 - unset( $all_plugins_info[$key] );
1314 - }
1315 - }
509 +
510 + print '</div>';
511 + print '</div>';
1316 512
1317 - return $all_plugins_info;
1318 - }
1319 513
1320 - /**
1321 - * Print a dismissible admin notice when a WpStream plugin update is pending.
1322 - *
1323 - * @return void
1324 - */
1325 - public function wpstream_render_outdated_plugin_notice() {
1326 - // Only render when WP's update transient lists an update for the plugin.
1327 - $has_update = get_site_transient('update_plugins');
1328 - if (isset($has_update->response['plugin/wpstream.php'])) {
1329 - // Below: warning notice linking to the WP updates page.
1330 - ?>
1331 - <div class="notice notice-warning is-dismissible">
1332 - <p>
1333 - <?php
1334 - printf(
1335 - /* translators: 1: Link to update page */
1336 - 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'),
1337 - '<a href="' . esc_url(admin_url('update-core.php')) . '">' . esc_html__('updates page', 'wpstream') . '</a>'
1338 - );
1339 - ?>
1340 - </p>
1341 - </div>
1342 - <?php
514 + print '</div>';
1343 515 }
1344 - }
1345 516
1346 517
518 +
519 +
520 +
1347 521 /**
1348 - * Set user roles
1349 - *
1350 - * Render the grid of per-event streaming option switches (from
1351 - * $global_event_options). Reused for both the site-wide default settings
1352 - * and the per-channel settings modal. Echoes HTML.
1353 - *
1354 - * @param string $name Field name prefix/context (unused in body; kept for callers).
1355 - * @param array|string $value Stored 1/0 map of option states (empty falls back to defaults).
1356 - * @param array|string $local_array Option keys to EXCLUDE from this render (per-channel context).
1357 - * @param bool $disabled True to render the switches disabled.
1358 - * @param bool $is_basic_stream_mode True to disable switches for basic-streaming accounts.
1359 - * @return void
1360 - */
1361 - public function user_streaming_global_channel_options(
1362 - $name,
1363 - $value,
1364 - $local_array='',
1365 - $disabled = false,
1366 - $is_basic_stream_mode = false
1367 - ) {
522 + * Set Settings
523 + *
524 + *
525 + */
1368 526
1369 - // Render one switch per defined global event option.
1370 - foreach($this->global_event_options as $key=>$option){
527 + public function wpstream_settings(){
528 +
529 + if($_SERVER['REQUEST_METHOD'] === 'POST'){
530 + $allowed_html = array();
531 + $exclude_array = array();
532 + $allowed_html = array();
1371 533
1372 - // Skip options listed in the exclude array (used by the per-channel modal).
1373 - if( is_array($local_array) && !in_array($key,$local_array)){
1374 - print '<div class="wpstream_setting_event_unit_wrapper wpstream-setting-'.esc_attr($key).' ">';
1375 -
1376 - print '<label for="'.$option['name'].'">'.$option['name'].'</label>';
1377 -
1378 - print '<div style="display: flex; gap: 25px; justify-content: space-between;">';
1379 - print '<div class="settings_details">'.$option['details'].'</div>';
1380 - print '
1381 - <label class="wpstream_switch">
1382 - <input type="checkbox" class="wpstream_event_option_item" data-attr-ajaxname="'.esc_attr($key).'" name="wpstream_event_set_'.esc_attr($key).'" ';
1383 - // Check state: only a stored value of 1 renders the switch on.
1384 - // An absent key resolves to off everywhere the settings are
1385 - // consumed, so the screen must not promise otherwise.
1386 - if( isset($value[$key]) && intval($value[$key]) !== 0 ){
1387 - print ' checked ';
534 +
535 + if( isset($_POST['user_streaming_channel_type_hidden']) && intval($_POST['user_streaming_channel_type_hidden'])==1 && !isset($_POST['stream_role']) ){
536 + update_option( sanitize_key('wpstream_stream_role'), '' );
537 + }
538 +
539 +
540 + foreach($_POST as $variable=>$value){
541 + if ($variable!='submit'){
542 + if (!in_array($variable, $exclude_array) ){
543 + update_option( sanitize_key('wpstream_'.$variable), sanitize_text_field ($value) );
1388 544 }
1389 - // Disable the switch when requested or in basic-streaming mode.
1390 - if ( $disabled || $is_basic_stream_mode ) {
1391 - print ' disabled ';
545 +
546 + if($variable=='stream_role'){
547 + update_option( sanitize_key('wpstream_stream_role'), $value );
1392 548 }
549 +
550 + }
551 + }
552 +
553 + // reset permalinkgs
554 + global $wp_rewrite;
555 +
556 + update_option( "rewrite_rules", FALSE );
557 + $wp_rewrite->flush_rules( true );
558 +
559 + }
560 +
561 + $wpstream_settings_array =array(
562 + 1 => array(
563 + 'label' => esc_html__('Slug for wpstream free videos/channels','wpstream'),
564 + 'name' => 'free_media_slug',
565 + 'type' => 'text',
566 + 'details' => esc_html__('It will replace wpstream in the free videos/channels url with your new slug. You cannot use special characters like "&". After changing the url go to Settings - Permalinks and Save again the "Permalinks Settings" - option "Post name" ','wpstream'),
567 + ),
568 +
569 + 2 => array(
570 + 'label' => esc_html__('Extra roles who can stream via wpstream shortcodes / blocks','wpstream'),
571 + 'name' => 'stream_role',
572 + 'type' => 'user_roles',
573 + 'details' => esc_html__('A non admin user will be able to stream only on one channel only that will be automaticaly created.','wpstream'),
574 +
575 + ),
576 + 3 => array(
577 + 'label' => esc_html__('Type channel on wich non admin users can stream ','wpstream'),
578 + 'name' => 'user_streaming_channel_type',
579 + 'type' => 'select',
580 + 'select_values'=>array(
581 + 'free' => esc_html__('Free Live Channels','wpstream'),
582 + 'paid' => esc_html__('Pay per view (WooCommerce product)','wpstream')
583 + ),
584 + 'details' => esc_html__('The product type channel is a WooCommerce product (pay per view) while Free Channels is visible to all types of visitors ','wpstream'),
585 + ),
586 +
587 + 4 => array(
588 + 'label' => esc_html__('Default Price ','wpstream'),
589 + 'name' => 'user_streaming_default_price',
590 + 'type' => 'text',
591 + 'details' => esc_html__('If non admins can stream on a product channel then you can set here the default price here.','wpstream'),
592 + ),
593 + );
594 +
595 +
596 +
597 + print '<form method="post" action="" >';
598 + print '<div class="theme_options_tab_wpstream" style="display:block;" >
599 + <h1>'.__('WpStream Settings','wpstream').'</h1>';
1393 600
1394 601
1395 - print '> <span class="wpstream_slider round"></span>';
1396 - print '</label>';
1397 - print '</div>';
1398 602
603 + print '<div class="wpstream_option_wrapper">';
604 + foreach ($wpstream_settings_array as $key=>$option){
605 + print '<div class="wpstream_option">';
606 + $options_value = get_option('wpstream_'.$option['name']) ;
607 +
608 +
609 +
610 + if($option['type']=='user_roles'){
611 +
612 + print '<label for="'.$option['name'].'">'.$option['label'].'</label>';
613 + print $this->wpstream_select_user_roles($option['name'],$options_value);
614 + print '<div class="settings_details">'.$option['details'].'</div>';
1399 615
1400 - print '</div>';
1401 - }
1402 - }
1403 616
617 + }else if($option['type']=='text'){
1404 618
1405 - }
1406 -
619 + print '<label for="'.$option['name'].'">'.$option['label'].'</label>';
620 + print '<input id="'.$option['name'].'" type="'.$option['type'].'" size="36" name="'.$option['name'].'" value="'.esc_attr($options_value).'" />';
621 + print '<div class="settings_details">'.$option['details'].'</div>';
622 +
623 + } else if($option['type']=='select'){
624 +
625 + print '<label for="'.$option['name'].'">'.$option['label'].'</label>';
626 + print '<select id="'.$option['name'].'" name="'.$option['name'].'" >';
627 + foreach($option['select_values'] as $key=>$value){
628 + print '<option value="'.$key.'" ';
629 + if( $key == esc_html($options_value) ){
630 + print ' selected ';
631 + }
632 + print '>'.$value.'</option>';
633 + }
634 + print '</select>';
635 + print '<input type="hidden" name="'.$option['name'].'_hidden" value="1" >';
636 + print '<div class="settings_details">'.$option['details'].'</div>';
637 +
638 + }
639 +
640 + print '</div>';
641 + }
642 + print '</div>';
1407 643
1408 -
1409 -
1410 644
645 + print '<input type="submit" name="submit" class="wpstream_button" value="'.__('Save Changes','wpstream').'" />';
1411 646
1412 647
648 +
649 + print '</div>';
650 + print '</form>';
651 + }
652 +
1413 653
1414 654
1415 - /*
1416 - * Set user roles
1417 - *
1418 - * @since 3.0.1
1419 - */
655 +
656 +
657 +
658 +
1420 659 /**
1421 - * Build a multi-select of editable user roles (administrator excluded)
1422 - * for the "roles allowed to broadcast" setting.
1423 - *
1424 - * @param string $name Field name (rendered as name="{name}[]").
1425 - * @param array|string $value Currently selected role keys ('' becomes an empty array).
1426 - * @return string HTML <select> markup.
1427 - */
660 + * Set user roles
661 + *
662 + * @since 3.0.1
663 + */
1428 664 public function wpstream_select_user_roles($name,$value){
1429 - // Normalize an empty stored value to an array for in_array() below.
1430 665 if($value==''){
1431 666 $value=array();
1432 667 }
1433 -
1434 - // All editable roles minus administrator (admins always can broadcast).
668 +
1435 669 $roles = get_editable_roles();
1436 670 $return = '<select id="wpstream_user_roles" name="'.esc_html($name).'[]" multiple>';
1437 671 unset( $roles['administrator'] );
1438 -
1439 - // One option per role, pre-selecting those already chosen.
672 +
1440 673 foreach ($roles as $key=>$role){
1441 674 $return .= '<option value="'.$key.'" ';
1442 675 if( in_array($key, $value) ){
1443 676 $return .= ' selected ';
@@ -1444,129 +677,48 @@
1444 677 }
1445 678 $return .= '>'.$role['name'].'</option>';
1446 679 }
1447 680 $return .= '</select>';
1448 -
1449 -
681 +
682 +
1450 683 return $return;
1451 684 }
1452 685
1453 -
1454 - /*
1455 - * Set credential admin function
1456 - *
1457 - * @since 3.0.1
1458 - */
686 +
1459 687 /**
1460 - * Render the Credentials admin screen and save submitted API credentials.
1461 - *
1462 - * On POST it stores the WpStream username/password options and clears the
1463 - * cached token/quota transients so the next request re-authenticates. It
1464 - * then shows the connection status and the credentials form. Echoes HTML.
1465 - *
1466 - * @return void
1467 - */
688 + * Set credential admin function
689 + *
690 + * @since 3.0.1
691 + */
1468 692 public function wpstream_set_wpstream_credentials(){
1469 - $credentials_from_constants = wpstream_api_credentials_from_constants();
1470 -
1471 - // Handle a credentials submission.
1472 - if($_SERVER['REQUEST_METHOD'] === 'POST'){
1473 - // CSRF/permission gate: only an admin submitting the credentials
1474 - // form (with its nonce) may overwrite the stored API credentials.
1475 - $this->wpstream_require_settings_capability();
1476 - check_admin_referer( 'wpstream_save_credentials', 'wpstream_credentials_nonce' );
1477 -
1478 - // wp-config owns the complete pair. A forged/manual POST must be the
1479 - // same no-op as the disabled form presented to the administrator.
1480 - if ( ! $credentials_from_constants ) {
1481 -
693 +
694 + if($_SERVER['REQUEST_METHOD'] === 'POST'){
1482 695 $allowed_html = array();
1483 696 $exclude_array = array();
1484 697 $allowed_html = array();
1485 698
1486 - // Username before this submission: tells a cleared form apart from a new account.
1487 - $previous_username = (string) get_option( 'wpstream_api_username', '' );
1488 -
1489 - // Persist only the recognised credential fields.
1490 - foreach($_POST as $variable=>$value){
699 + foreach($_POST as $variable=>$value){
1491 700 if ($variable!='submit'){
1492 701 if (!in_array($variable, $exclude_array) ){
1493 - switch ( $variable ) {
1494 - case 'api_username':
1495 - // Unslashed first: WordPress slashes every $_POST value,
1496 - // so a username containing a quote would otherwise be
1497 - // stored with a stray backslash and never authenticate.
1498 - // Then sanitized as text.
1499 - update_option( sanitize_key('wpstream_api_username'), sanitize_text_field( wp_unslash( $value ) ) );
1500 - break;
1501 - case 'api_password':
1502 - // Only unslashed, never sanitized: the password is a secret
1503 - // sent verbatim to the API, and sanitizing would silently
1504 - // change it. Unslashing undoes the escaping WordPress adds
1505 - // to every $_POST value, without which a password holding a
1506 - // quote or a backslash is stored — and sent — wrong, so the
1507 - // customer is told their correct password is incorrect.
1508 - // Not autoloaded: it is only needed when authenticating.
1509 - // An empty submission keeps the current password, since the
1510 - // form never pre-fills the field with the stored secret.
1511 - $password = wp_unslash( $value );
1512 - if ( $password !== '' ) {
1513 - update_option( 'wpstream_api_password', $password, false );
1514 - } elseif (
1515 - array_key_exists( 'api_username', $_POST )
1516 - && '' === sanitize_text_field( wp_unslash( $_POST['api_username'] ) )
1517 - ) {
1518 - // Clearing the account is an explicit disconnect, not a
1519 - // password-only edit. Do not retain an orphaned secret.
1520 - delete_option( 'wpstream_api_password' );
1521 - }
1522 - break;
1523 - }
1524 - }
1525 - }
702 + update_option( sanitize_key('wpstream_'.$variable), wp_kses ($value,$allowed_html) );
703 + }
704 + }
1526 705 }
706 +
707 +
1527 708
1528 709
710 + update_option('wp_estate_token_expire',0);
711 + update_option('wp_estate_curent_token',' ');
712 + delete_transient( 'wpstream_token_request');
1529 713
1530 - // Invalidate any cached token/quota so the new credentials are used immediately.
1531 - update_option('wpstream_token_expire',0);
1532 - update_option('wpstream_current_token',' ');
1533 - delete_transient( 'wpstream_token_api');
1534 - delete_transient('wpstream_token_request_30');
1535 - $this->main->quota_manager->invalidate();
1536 -
1537 - // Announce the account change. Only the username travels: the
1538 - // password and any token stay server-side.
1539 - $username = (string) get_option( 'wpstream_api_username', '' );
1540 - if ( '' !== $username ) {
1541 - /**
1542 - * Fires after WpStream account credentials were saved and the
1543 - * cached token/quota dropped; the connection itself is verified
1544 - * on the next API call.
1545 - *
1546 - * @since 4.14.0
1547 - *
1548 - * @param string $username WpStream.net username or e-mail.
1549 - */
1550 - do_action( 'wpstream_account_connected', $username );
1551 - } elseif ( '' !== $previous_username ) {
1552 - /**
1553 - * Fires after the WpStream account username was cleared.
1554 - *
1555 - * @since 4.14.0
1556 - *
1557 - * @param string $username The username that was removed.
1558 - */
1559 - do_action( 'wpstream_account_disconnected', $previous_username );
1560 - }
1561 - }
1562 714 }
1563 715
1564 716
1565 717 $allowed_html = array();
718 + $api_key = esc_html( get_option('wpstream_api_key','') );
1566 719
1567 720
1568 - // Field definitions for the credentials form (username + password).
1569 721 $wpstream_options_array =array(
1570 722 2 => array(
1571 723 'label' => 'WpStream.net Username or Email',
1572 724 'name' => 'api_username',
@@ -1580,67 +732,34 @@
1580 732
1581 733 );
1582 734
1583 735
1584 - // Existing installs saved the password as an autoloaded option; flip it
1585 - // to non-autoloaded so the secret is not loaded on every request.
1586 - if ( function_exists( 'wp_set_option_autoload' ) ) {
1587 - wp_set_option_autoload( 'wpstream_api_password', false );
1588 - }
1589 -
1590 - // Connectivity and quota data drive the connection-status banner below.
1591 - $connected = $this->main->wpstream_live_connection->is_connected();
1592 - $pack_details = $this->main->quota_manager->get_live_quota_data( 'wpstream_set_wpstream_credentials' );
1593 -
736 + $token = $this->main->wpstream_live_connection->wpstream_get_token();
737 + $pack_details = $this->main->wpstream_live_connection->wpstream_request_pack_data_per_user();
738 +
1594 739 $this->main->show_user_data($pack_details);
1595 740
1596 741 print '<form method="post" action="" >';
1597 - // CSRF nonce for the credentials save handled at the top of this method.
1598 - wp_nonce_field( 'wpstream_save_credentials', 'wpstream_credentials_nonce' );
1599 742 print '<div class="theme_options_tab_wpstream" style="display:block;" >
1600 743 <h1>'.__('WpStream Credentials','wpstream').'</h1>';
1601 744
1602 - // Credentials supplied via wp-config.php constants take
1603 - // precedence over anything saved here — tell the admin.
1604 - if( $credentials_from_constants ){
1605 - echo '<div class="api_conected">'.esc_html__('Your WpStream credentials are defined in wp-config.php; values saved on this screen are ignored.','wpstream').'</div>';
1606 - }
1607 -
1608 - // Connection status banner: no credentials, bad credentials, connected, or CURL failure.
1609 - if( wpstream_get_api_username()=='' || wpstream_get_api_password()== '' ){
1610 - echo '<div class="api_not_conected wpstream_orange">';
1611 - $admin_url_onboard=get_admin_url().'admin.php?page=wpstream_onboard';
1612 - 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);
1613 - echo '</div>';
1614 -
1615 - }else if(!$connected){
1616 - // Translate the literals at definition — gettext cannot extract variable strings.
1617 - $text = get_option('wpstream_curl_failed') === "0" ?
1618 - __( ' 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.', 'wpstream' ) :
1619 - __( 'Not connected to WpStream. Please note the errors above and contact support.', 'wpstream' );
1620 - echo '<div class="api_not_conected">'.wp_kses_post($text).'</div>';
745 +
746 + if( get_option('wpstream_api_username')=='' || get_option('wpstream_api_password')== ' '){
747 + echo '<div class="api_not_conected wpstream_orange">'.__('To connect your plugin, enter your WpStream credentials below or go <a href="https://wpstream.net/my-account/" target="_blank">here</a> to create an account.','wpstream').'</div>';
748 + }else if($token==''){
749 + echo '<div class="api_not_conected">'.__(' 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.','wpstream').'</div>';
750 + }else if( $this->main->wpstream_live_connection->wpstream_client_check_api_status() ){
751 + echo '<div class="api_conected">'.__('Conected to WpStream.net!','wpstream').'</div>';
1621 752 }else{
1622 - // Credentials present and a token was obtained: connected.
1623 - echo '<div class="api_conected">'.__('Connected to WpStream.net!','wpstream').'</div>';
753 + echo '<div class="api_not_conected wpstream_brown">'.__('Failed to connect to WpStream.net. Please address CURL connectivity with your hosting provider.','wpstream').'</div>';
1624 754 }
1625 - // Render the credential inputs. The username is pre-filled;
1626 - // the password field is always left blank so the stored
1627 - // secret is never echoed into the page source (an empty
1628 - // submission keeps the saved password). Inputs are disabled
1629 - // when the wp-config constants are in charge.
1630 - $inputs_disabled = $credentials_from_constants ? ' disabled' : '';
1631 755 print '<div class="wpstream_option_wrapper">';
1632 756 foreach ($wpstream_options_array as $key=>$option){
1633 757 print '<div class="wpstream_option">';
1634 758
759 + $options_value = esc_html( get_option('wpstream_'.$option['name'],'') );
1635 760 print '<label for="'.$option['name'].'">'.$option['label'].'</label>';
1636 - if( $option['type']==='password' ){
1637 - $placeholder = get_option('wpstream_'.$option['name'],'') !== '' ? '*****' : '';
1638 - print '<input id="'.$option['name'].'" type="password" size="36" name="'.$option['name'].'" value="" autocomplete="new-password" placeholder="'.$placeholder.'"'.$inputs_disabled.' />';
1639 - }else{
1640 - $options_value = esc_html( get_option('wpstream_'.$option['name'],'') );
1641 - print '<input id="'.$option['name'].'" type="'.$option['type'].'" size="36" name="'.$option['name'].'" value="'.esc_html($options_value).'"'.$inputs_disabled.' />';
1642 - }
761 + print '<input id="'.$option['name'].'" type="'.$option['type'].'" size="36" name="'.$option['name'].'" value="'.esc_html($options_value).'" />';
1643 762
1644 763 print '</div>';
1645 764 }
1646 765 print '</div>';
@@ -1645,32 +764,24 @@
1645 764 }
1646 765 print '</div>';
1647 766
1648 767
1649 - print '<input type="submit"'.$inputs_disabled.' name="submit" class="wpstream_button wpstream_button_action" value="'.__('Save Changes','wpstream').'" />';
768 + print '<input type="submit" name="submit" class="wpstream_button" value="'.__('Save Changes','wpstream').'" />';
1650 769
1651 - print '<h3>Video Tutorials</h3>';
1652 -
1653 - print '<a class="how_to_videos" target="_blank" href="https://youtu.be/9DQrxsKcpmQ">How to Live Stream to WordPress with OBS</a>';
1654 - 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>';
1655 - print '<a class="how_to_videos" target="_blank" href="https://youtu.be/h6myD_vhKcg">How to Live-Stream to WordPress using your iPhone</a>';
1656 -
1657 - 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>';
1658 -
1659 770
771 +
1660 772 print '</div>';
1661 773 print '</form>';
1662 774
1663 - // Quick-action links: create free / paid channel, or jump to the channels list.
1664 775 print '<div class="theme_options_tab_wpstream" style="display:block;" >';
1665 - $link_new = admin_url('admin.php?page=wpstream_live_channels');
776 + $link_new = admin_url('admin.php?page=wpstream_new_general_set');
1666 777 $link_new_paid = admin_url('post-new.php?post_type=product').'&new_stream='. rawurlencode('new');
1667 778 $link_new_free = admin_url('post-new.php?post_type=wpstream_product');
779 +
1668 780
1669 -
1670 - print '<a href="'.esc_url($link_new_free).'" class="wpstream_no_chanel_add_channel">'.esc_html__('Create new Free-To-View channel','wpstream').'</a>';
1671 - print '<a href="'.esc_url($link_new_paid).'" class="wpstream_no_chanel_add_channel">'.esc_html__('Create Pay-Per-View channel','wpstream').'</a>';
1672 - print '<a href="'.esc_url($link_new).'" class="wpstream_no_chanel_add_channel">'.esc_html__('My Channels','wpstream').'</a>';
781 + print '<a href="'.esc_url($link_new_free).'" class="wpstream_no_chanel_add_channel">'.esc_html__('Create new free channel','wpstream').'</a>';
782 + print '<a href="'.esc_url($link_new_paid).'" class="wpstream_no_chanel_add_channel">'.esc_html__('Create pay-per-view channel','wpstream').'</a>';
783 + print '<a href="'.esc_url($link_new).'" class="wpstream_no_chanel_add_channel">'.esc_html('My Channels','wpstream').'</a>';
1673 784 print '</div>';
1674 785
1675 786
1676 787 }
@@ -1677,559 +788,177 @@
1677 788
1678 789
1679 790
1680 791 /**
1681 - * Media Management
1682 - *
1683 - * Render the Recordings admin screen: the quota summary, the upload widget,
1684 - * and the list of existing recordings. Echoes HTML.
1685 - *
1686 - * @since 3.0.1
1687 - * @return void
1688 - */
792 + * Media Management
793 + *
794 + * @since 3.0.1
795 + */
1689 796 public function wpstream_media_management(){
1690 - // Storage/streaming quota data for the summary header.
1691 - $pack_details = $this->main->quota_manager->get_live_quota_data( 'wpstream_media_management' );
797 + $pack_details = $this->main->wpstream_live_connection->wpstream_request_pack_data_per_user();
1692 798
1693 799 $this->main->show_user_data($pack_details);
1694 800
1695 801
1696 - // Upload widget section.
1697 - print '<div id="wpstream_media_upload"><h3>'.__('Upload New Recording','wpstream').'</h3>'.$this->wpstream_present_media_upload().'</div>';
802 + print '<div id="wpstream_media_upload"><h3>'.__('Media Upload','wpstream').'</h3>'.$this->wpstream_present_media_upload().'</div>';
1698 803
1699 - // Existing recordings list section.
1700 - print '<div id="wpstream_file_management"><h3 id="video_management_title">'.__('Your Recordings','wpstream').'</h3>'.$this->wpstream_present_file_management().'</div>';
804 + print '<div id="wpstream_file_management"><h3 id="video_management_title">'.__('Video Management','wpstream').'</h3>'.$this->wpstream_present_file_management().'</div>';
1701 805
1702 -
1703 806 }
1704 807
1705 808
1706 -
1707 -
1708 - /**
1709 - *
1710 - *
1711 - * WpStream Pagination
1712 - *
1713 - * @since 3.0.1
1714 - *
1715 - *
1716 - */
1717 -
1718 - /**
1719 - * Build a numeric pager (first/prev/window/next/last) for list screens.
1720 - *
1721 - * @param int $pages Total number of pages.
1722 - * @param int $range How many page links to show on each side of the current page.
1723 - * @return string Pager HTML, or '' when there is a single page / no pages.
1724 - */
1725 - public function wpstream_pagination($pages , $range = 2) {
1726 - $return='';
1727 - // Total visible window width around the current page.
1728 - $showitems = ($range * 2) + 1;
1729 - // Current page from the query string (defaults to 1).
1730 - $paged = ( isset( $_GET['paged'] ) ) ? intval($_GET['paged']) : 1;
1731 -
1732 -
1733 - // Only render a pager when there is more than one page.
1734 - if (1 != $pages && $pages != 0) {
1735 - $return.= '<ul class="pagination wpstream_pagination">';
1736 - // "Previous" arrow.
1737 - $return.= "<li class=\"roundleft\"><a href='" . get_pagenum_link($paged - 1) . "'><</a></li>";
1738 -
1739 - $last_page = get_pagenum_link($pages);
1740 - // Emit page-number links, but only those within the visible window (or all if they fit).
1741 - for ($i = 1; $i <= $pages; $i++) {
1742 - if (1 != $pages && (!($i >= $paged + $range + 1 || $i <= $paged - $range - 1) || $pages <= $showitems )) {
1743 - if ($paged == $i) {
1744 - // Current page marker.
1745 - $return.= '<li class="active"><a href="' . esc_url(get_pagenum_link($i)) . '" >' . $i . '</a><li>';
1746 - } else {
1747 - $return.= '<li><a href="' . esc_url(get_pagenum_link($i)) . '" >' . $i . '</a><li>';
1748 - }
1749 - }
1750 - }
1751 -
1752 - // "Next" target, clamped to the last page.
1753 - $prev_page = get_pagenum_link($paged + 1);
1754 - if (($paged + 1) > $pages) {
1755 - $prev_page = get_pagenum_link($paged);
1756 - } else {
1757 - $prev_page = get_pagenum_link($paged + 1);
1758 - }
1759 -
1760 -
1761 - // "Next" and "Last" arrows, then close the list.
1762 - $return.= "<li class=\"roundright\"><a href='" . $prev_page . "'>></a><li>";
1763 - $return.= "<li class=\"roundright\"><a href='" . $last_page . "'>>><li>";
1764 - $return.= "</ul>";
1765 - }
1766 - return $return;
1767 - }
1768 -
1769 -
1770 -
1771 -
1772 -
1773 809
1774 810 /**
1775 - * Media upload
1776 - *
1777 - * Build the S3 direct-upload widget for new recordings: checks storage
1778 - * quota and API connectivity, then renders a multipart form pre-populated
1779 - * with the signed S3 upload fields.
1780 - *
1781 - * @since 3.0.1
1782 - * @return string Upload widget HTML, or an alert/notice when unavailable.
1783 - */
811 + * Media upload
812 + *
813 + * @since 3.0.1
814 + */
1784 815 public function wpstream_present_media_upload(){
1785 816 $to_return='';
1786 -
1787 - // Refuse when the account is out of storage/data quota.
1788 - if ( ! $this->main->quota_manager->has_storage_quota( null, 'recordings_screen' ) ) {
1789 - 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>';
1790 - }
1791 -
1792 - // Request the signed S3 POST fields from the cloud API.
1793 817 $formInputs=$this->main->wpstream_live_connection->wpstream_get_signed_form_upload_data();
1794 818
1795 - // On failure, show either a "not connected" or an "out of quota" message.
1796 - if( !$formInputs['success'] ){
1797 - if ($formInputs['error'] == 'not_connected'){
1798 - $to_return.='<div class="wpstream_upload_container">'.esc_html__('Not connected. Please connect to WpStream to upload videos.','wpstream').'</div>';
1799 - }
1800 - else {
1801 - $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>';
1802 - }
819 + if($formInputs === 'notenough'){
820 + $to_return.='<div class="wpstream_upload_alert">'.esc_html__('You do not have enough bandwidth or storage to upload a video!','wpstream').'</div>';
1803 821 return $to_return;
1804 822 }
1805 823
1806 - // Signed data obtained: render the direct-to-S3 upload form.
1807 - if($formInputs['success'] ===true){
1808 824
1809 -
825 + $to_return.='<div class="wpstream_upload_container">';
826 + $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>';
827 + $to_return.='<form action="https://wpstream-videos.s3.amazonaws.com/"
828 + method="POST"
829 + enctype="multipart/form-data"
830 + class="direct-upload">';
1810 831
1811 - $to_return.='<div class="wpstream_upload_container">';
1812 - $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>';
1813 - $to_return.='<form action="https://wpstream-video.s3.amazonaws.com/"
1814 - method="POST"
1815 - enctype="multipart/form-data"
1816 - data-singleFileUploads="true"
1817 - data-limitMultiFileUploads="1"
1818 - data-limitConcurrentUploads="1"
1819 - class="direct-upload">';
832 + $to_return.='<input id="wpstream_upload" type="file" class="inputfile inputfile-1" value="Pick a video file" name="file" multiple>';
833 + $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">Choose a file&hellip;</span></label>';
1820 834
1821 - $to_return.='<input id="wpstream_upload" type="file" class="inputfile inputfile-1" value="Pick a video file" name="file" multiple>';
1822 - $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&hellip;','wpstream') . '</span></label>';
1823 835
836 + $to_return.='<div class="wpstream_file_drop_color">';
837 + $to_return.='<div class="wpstream_form_ex">'.esc_html__('Drop a video file here!','wpstream').'</div>';
838 + $to_return.='<div class="wpstream_form_ex_details">'.esc_html__('The Video File must be encoded with the following settings:
1824 839
1825 - $to_return.='<div class="wpstream_file_drop_color">';
1826 - $to_return.='<div class="wpstream_form_ex">'.esc_html__('Drop a video file here!','wpstream').'</div>';
1827 - $to_return.='<div class="wpstream_form_ex_details">'.__('The Video File must be encoded with the following settings:<br>
840 + Container: MP4,
841 + Video codec: H264,
842 + Audio codec: AAC. Media will fail to play back if it does not follow the above settings.
843 + 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>';
844 + if(is_array($formInputs)){
845 + foreach ($formInputs as $name => $value) {
846 + $to_return.='<input type="hidden" name="'. $name.'" value="'.$value.'">';
847 + }
848 + }
1828 849
1829 - Container: <strong>MP4</strong>,<br>
1830 - Video codec: <strong>H264</strong>,<br>
1831 - Audio codec: <strong>AAC</strong>.<br>
1832 - Media will fail to play if it does not follow the above settings.
1833 - 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>';
1834 - // Inject each signed S3 field as a hidden input so the browser POST is authorized.
1835 - if(is_array($formInputs)){
1836 - foreach ($formInputs['ref'] as $name => $value) {
1837 - $to_return.='<input type="hidden" name="'. $name.'" value="'.$value.'">';
1838 - }
1839 - }
850 + $to_return.='
851 + <div class="progress-bar-area"></div></div>
852 + </form>';
1840 853
1841 - $to_return.='
1842 - <div class="progress-bar-area"></div></div>
1843 - </form>';
1844 -
1845 - $to_return.='</div>';
1846 - }
1847 -
854 + $to_return.='</div>';
1848 855 return $to_return;
1849 856
1850 857 }
1851 858
1852 -
1853 859
1854 860
1855 -
1856 861 /**
1857 - * Display movie list
1858 - *
1859 - * Fetch the account's recordings from the cloud API and render them:
1860 - * still-processing ("pending") items first, then completed items with
1861 - * delete/download links and "create VOD from this recording" actions.
1862 - *
1863 - * @since 3.0.1
1864 - * @return string Recordings list HTML, or a notice when not connected / empty.
1865 - */
862 + * Display movie list
863 + *
864 + * @since 3.0.1
865 + */
1866 866 public function wpstream_present_file_management(){
1867 - // Pull the raw recordings payload from the API.
1868 - $video_list_raw = $this->main->wpstream_live_connection->wpstream_get_videos_from_api();
867 + $video_list_raw = $this->main->wpstream_live_connection->wpstream_get_videos_from_storage_raw_data();
1869 868
1870 - // false means the API call failed / not connected.
1871 - if ( $video_list_raw === false ) {
1872 - return '<div class="wpstream_upload_container">'.esc_html__('Not connected. Please connect to WpStream to upload videos.','wpstream').'</div>';
1873 - }
1874 -
1875 - // Normalise the completed-items list.
1876 - $video_list_raw_array = [];
1877 - if( isset( $video_list_raw['items'] ) ){
1878 - $video_list_raw_array = $video_list_raw['items'];
1879 - }
1880 -
1881 - // Sort completed items newest-first by their 'time' field.
1882 - $keys = array_column( $video_list_raw_array, 'time' );
1883 - array_multisort($keys, SORT_DESC , $video_list_raw_array);
1884 -
1885 869 $to_return='';
870 + if(is_array($video_list_raw)){
871 + foreach ($video_list_raw as $key =>$video){
872 + $to_return.='<div class="wpstream_video_wrapper">';
1886 873
1887 - // show pending items
1888 - // Render still-processing uploads with a "processing" badge.
1889 - if ( key_exists( 'pending', $video_list_raw ) && is_array( $video_list_raw['pending'] ) ) {
1890 - foreach ( $video_list_raw['pending'] as $key => $video ) {
1891 - $video_size = intval($video['size']/1048576);
1892 - $video_name = esc_html($video['name']);
1893 - if($video_name!=''):
1894 - $to_return.='<div class="wpstream_video_wrapper">';
1895 - $to_return.='<div class="wpstream_video_title">';
1896 - $to_return.='<div class="wpstream_video_notice"></div></div>';
1897 - $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>';
1898 - $to_return.='<div class="wpstream_video_pending">' . esc_html__( 'The video is still processing', 'wpstream') . '</div>';
1899 - $to_return.='</div>';
1900 - endif;
874 + $to_return.='<div class="wpstream_video_title">';
875 + $to_return.='<div class="wpstream_video_notice"></div></div>';
876 + $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['video_name_storage'].'</span></div>';
877 + $to_return.=' <div class="wpstream_delete_media" ';
878 + $to_return.=' onclick="return confirm(\' Are you sure you wish to delete '.$video['video_name_storage'].'?\')" data-filename="'.$video['video_name_storage'].'">'.esc_html__('delete file','wpstream').'</div>';
879 + $to_return.='<div class="wpstream_get_download_link" data-filename="'.$video['video_name_storage'].'">'.esc_html__('get download link','wpstream').'</div>';
880 + $to_return.='<a href="" class="wpstream_download_link">'.esc_html__('Click to download! The url will work for the next 20 minutes!','wpstream').'</a>';
1901 881
1902 - }
1903 - }
882 + $add_free_video_url=admin_url('post-new.php?post_type=wpstream_product').'&new_video_name='. rawurlencode($video['video_name_storage']);
883 + $add_paid_video_url=admin_url('post-new.php?post_type=product').'&new_video_name='. rawurlencode($video['video_name_storage']);
1904 884
1905 - // show uploaded items
1906 - // Render each completed recording with its size, delete/download controls and VOD-creation links.
1907 - if( is_array($video_list_raw['items'] ) ) {
1908 - foreach ($video_list_raw_array as $key =>$video){
1909 - // Keep the service-provided Source Recording identity
1910 - // separate from its context-specific HTML rendering.
1911 - $video_size = intval($video['size']/1048576);
1912 - $video_source_name = (string) $video['name'];
1913 - $video_name = esc_html($video_source_name);
1914 - if($video_name!=''):
1915 - $to_return.='<div class="wpstream_video_wrapper">';
1916 885
1917 - $to_return.='<div class="wpstream_video_title">';
1918 - $to_return.='<div class="wpstream_video_notice"></div></div>';
1919 - $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>';
1920 - // Delete control. The shared JS utility owns the
1921 - // confirmation; the renderer supplies inert data
1922 - // only so any valid recording name remains safe.
1923 - $to_return.='<div class="wpstream_delete_media" data-filename="'.esc_attr($video_source_name).'">'.esc_html__('delete file','wpstream').'</div>';
1924 - // Download trigger (JS fetches a time-limited signed URL) and the resulting link.
1925 - $to_return.='<div class="wpstream_get_download_link" data-filename="'.$video_name.'">'.esc_html__('download','wpstream').'</div>';
1926 - $to_return.='<a href="" class="wpstream_download_link">'.esc_html__('Click to download! The url will work for the next 20 minutes!','wpstream').'</a>';
1927 886
1928 - // Pre-fill new-VOD links (free and, if WooCommerce present, paid) with this recording's name.
1929 - $add_free_video_url=admin_url('post-new.php?post_type=wpstream_product_vod').'&new_video_name='. rawurlencode($video_source_name);
1930 - $add_paid_video_url=admin_url('post-new.php?post_type=product').'&new_video_name='. rawurlencode($video_source_name);
887 + $to_return .='<a class="create_new_free_video" href="'.esc_url($add_free_video_url).'">'.esc_html__('Create new free VOD from this video').'</a>';
888 + $to_return .='<a class="create_new_ppv_video" href="'.esc_url($add_paid_video_url).'">'.esc_html__('Create pay-per-view VOD from this video').'</a>';
1931 889
890 + $to_return.='</div>';
1932 891
1933 -
1934 - $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>';
1935 - if (class_exists('WooCommerce')) {
1936 - $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>';
1937 - }
1938 -
1939 - $to_return.='</div>';
1940 - endif;
1941 -
1942 892 }
1943 - $current_page= get_current_screen();
1944 - }
1945 -
1946 - // no items to show
1947 - // Empty-state message when there are neither completed nor pending items.
1948 - if ( !is_array( $video_list_raw['items'] ) || ( key_exists( 'pending', $video_list_raw ) && !is_array( $video_list_raw['pending'] ) ) ) {
1949 - $to_return.= '<div class="wpstream_video_wrapper">'.esc_html__('You don\'t have any videos.','wpstream').'</div>';
893 + } else {
894 + $to_return.= '<div class="wpstream_video_wrapper">'.esc_html__('You don\'t have any videos.','wpstream').'</div>';
1950 895 }
1951 896 return $to_return;
1952 897 }
1953 898
1954 899
1955 - /**
1956 - * Preserve the legacy free-Channel publish marker.
1957 - *
1958 - * Channel Settings initialization is coordinated by the Streaming Content
1959 - * Creation Module; this compatibility hook only maintains historical meta.
1960 - *
1961 - * @since 3.0.1
1962 - * @param int $post_id Post ID being published.
1963 - * @param WP_Post $post Post object being published.
1964 - * @return void
1965 - */
1966 - public function wpstream_publish_wpstream_product($post_id,$post){
1967 - // Only applies to free channel posts.
1968 - if( $post->post_type == 'wpstream_product' ){
1969 - // Preserve the legacy marker used by existing installations.
1970 - update_post_meta ($post_id,'local_event_options_test','working_on_'.$post_id);
1971 - }
1972 - }
1973 -
900 +
901 +
902 +
1974 903 /**
1975 - * Initialize existing Streaming Content when it is published or saved.
1976 - *
1977 - * The shared Module performs safe legacy repair and retries cloud provisioning
1978 - * for Live Channels. VODs receive only their applicable local initialization.
1979 - *
1980 - * @param int $post_id Post ID being published.
1981 - * @param WP_Post $post Post object being published.
1982 - * @return void
1983 - */
1984 - public function wpstream_create_remote_channel_on_publish( $post_id, $post ) {
1985 - // Skip autosaves and revisions (they are not real publishes).
1986 - if ( defined( 'DOING_AUTOSAVE') && DOING_AUTOSAVE ) {
1987 - return;
1988 - }
1989 - if ( wp_is_post_revision( $post_id ) ) {
1990 - return;
1991 - }
1992 - $this->main->streaming_content_creation->initialize_existing( $post_id );
1993 - }
904 + * save meta options
905 + *
906 + * @since 3.0.1
907 + */
908 + public function wpstream_free_product_update_post($post_id,$post){
1994 909
1995 - /**
1996 - * Initialize a manually saved paid streaming product after WooCommerce
1997 - * has persisted its product-type taxonomy. The earlier publish hook may
1998 - * run while a new product still looks like `simple`; this later adapter
1999 - * enters the same idempotent creation policy once `live_stream` or
2000 - * `video_on_demand` is observable.
2001 - *
2002 - * @param int $post_id WooCommerce product ID.
2003 - * @return void
2004 - */
2005 - public function wpstream_initialize_paid_streaming_product( $post_id ) {
2006 - if ( 'product' !== get_post_type( $post_id ) ) {
910 + if(!is_object($post) || !isset($post->post_type)) {
2007 911 return;
2008 912 }
2009 - if ( ! has_term( array( 'live_stream', 'video_on_demand' ), 'product_type', $post_id ) ) {
2010 - return;
2011 - }
2012 - $this->main->streaming_content_creation->initialize_existing( $post_id );
2013 - }
2014 913
2015 - /**
2016 - * Delete the remote channel when its Live Channel post is deleted for good.
2017 - *
2018 - * Hooked to before_delete_post: fires only on permanent deletion, never on
2019 - * trash, so trashing stays reversible and a restored channel keeps its
2020 - * cloud identity. Only a Live Channel that was actually Provisioned
2021 - * (`channelId` meta present) has anything at the service to remove.
2022 - *
2023 - * The local deletion proceeds whatever the service answers — this hook
2024 - * cannot cancel it, and a channel the customer removed must leave the
2025 - * site either way.
2026 - *
2027 - * @param int $post_id Post ID being permanently deleted.
2028 - * @param WP_Post $post Post object being permanently deleted.
2029 - * @return void
2030 - */
2031 - public function wpstream_delete_remote_channel_on_delete( $post_id, $post ) {
2032 - // Only Live Channel posts: the free post type, or a WooCommerce
2033 - // product of the live_stream type (paid channels).
2034 - $is_live_channel = $post instanceof WP_Post
2035 - && ( 'wpstream_product' === $post->post_type
2036 - || ( 'product' === $post->post_type && has_term( 'live_stream', 'product_type', $post_id ) ) );
2037 - if ( ! $is_live_channel ) {
2038 - return;
914 + if($post->post_type!='wpstream_product'){
915 + return;
2039 916 }
2040 917
2041 - // A channel that never obtained a cloud identity has nothing to delete.
2042 - if ( '' === (string) get_post_meta( $post_id, 'channelId', true ) ) {
2043 - return;
2044 - }
2045 918
2046 - $this->main->wpstream_live_connection->wpstream_delete_channel( $post_id );
2047 - }
919 + $allowed_keys=array(
920 + 'wpstream_product_type',
921 + 'wpstream_free_video',
922 + 'wpstream_free_video_external'
923 + );
2048 924
2049 - /**
2050 - * Restore a paid Live Channel as a draft instead of immediately putting
2051 - * it back on sale. WooCommerce restores ordinary products to their
2052 - * previous status; a channel requires the seller to republish it.
2053 - *
2054 - * @param string $new_status Status selected by earlier filters.
2055 - * @param int $post_id Restored post ID.
2056 - * @param string $previous_status Status before trashing.
2057 - * @return string
2058 - */
2059 - public function wpstream_paid_live_untrash_status( $new_status, $post_id, $previous_status ) {
2060 - if ( 'product' === get_post_type( $post_id ) && has_term( 'live_stream', 'product_type', $post_id ) ) {
2061 - return 'draft';
2062 - }
2063 - return $new_status;
2064 - }
925 + $allowed_html=array();
2065 926
2066 - /** Invalidate VOD DRM lookup state whenever publication visibility changes. */
2067 - public function wpstream_invalidate_vod_drm_on_status_change( $new_status, $old_status, $post ) {
2068 - if ( $post instanceof WP_Post && 'wpstream_product_vod' === $post->post_type && $new_status !== $old_status ) {
2069 - $this->main->drm_key_delivery->invalidate_vod_lookup( $post->ID );
2070 - }
2071 - }
2072 -
2073 - /** Invalidate VOD DRM lookup state before its key-index metadata is deleted. */
2074 - public function wpstream_invalidate_vod_drm_on_delete( $post_id, $post ) {
2075 - if ( $post instanceof WP_Post && 'wpstream_product_vod' === $post->post_type ) {
2076 - $this->main->drm_key_delivery->invalidate_vod_lookup( $post_id );
2077 - }
2078 - }
2079 -
2080 - /**
2081 - * save meta options
2082 - *
2083 - * Persist the allow-listed free channel / VOD meta fields on save for the
2084 - * `wpstream_product` and `wpstream_product_vod` post types. Hooked to the
2085 - * post save action.
2086 - *
2087 - * @since 3.0.1
2088 - * @param int $post_id Post ID being saved.
2089 - * @param WP_Post $post Post object being saved.
2090 - * @return void
2091 - */
2092 - public function wpstream_free_product_update_post($post_id,$post){
2093 -
2094 - // Guard against non-post callers.
2095 - if(!is_object($post) || !isset($post->post_type)) {
2096 - return;
927 + foreach ($_POST as $key => $value) {
928 + if( !is_array ($value) ){
929 + if (in_array ($key, $allowed_keys)) {
930 + $postmeta = wp_kses ( $value,$allowed_html );
931 + update_post_meta($post_id, sanitize_key($key), $postmeta );
932 + }
933 + }
2097 934 }
2098 -
2099 -
2100 -
2101 -
2102 -
2103 - // Only handle the free channel and VOD post types.
2104 - if( $post->post_type == 'wpstream_product' ||
2105 - $post->post_type == 'wpstream_product_vod' ):
2106 - $previous_vod_type = 'wpstream_product_vod' === $post->post_type
2107 - ? intval( get_post_meta( $post_id, 'wpstream_product_type', true ) )
2108 - : 0;
2109 - $previous_recording = 'wpstream_product_vod' === $post->post_type
2110 - ? (string) get_post_meta( $post_id, 'wpstream_free_video', true )
2111 - : '';
2112 -
2113 - // Meta keys this handler is allowed to write.
2114 - $allowed_keys=array(
2115 - 'wpstream_product_type',
2116 - 'wpstream_free_video',
2117 - 'wpstream_free_video_external',
2118 - 'wpstream_closed_captions_file'
2119 - );
2120 -
2121 -
2122 - // Save each allow-listed, scalar POST field as sanitized post meta.
2123 - foreach ($_POST as $key => $value) {
2124 - if( !is_array ($value) ){
2125 - if (in_array ($key, $allowed_keys)) {
2126 - $postmeta = sanitize_text_field ( $value );
2127 - update_post_meta($post_id, sanitize_key($key), $postmeta );
2128 - }
2129 - }
2130 - }
2131 -
2132 - if ( 'wpstream_product_vod' === $post->post_type && isset( $_POST['wpstream_product_type'] ) ) {
2133 - $new_vod_type = intval( $_POST['wpstream_product_type'] );
2134 - if ( 3 === $new_vod_type ) {
2135 - $this->wpstream_retire_vod_recording_identity( $post_id );
2136 - delete_post_meta( $post_id, 'wpstream_free_video' );
2137 - } elseif ( 2 === $new_vod_type ) {
2138 - $new_recording = isset( $_POST['wpstream_free_video'] ) && is_scalar( $_POST['wpstream_free_video'] )
2139 - ? sanitize_text_field( wp_unslash( $_POST['wpstream_free_video'] ) )
2140 - : $previous_recording;
2141 - if ( 2 !== $previous_vod_type || $new_recording !== $previous_recording ) {
2142 - $this->wpstream_retire_vod_recording_identity( $post_id );
2143 - }
2144 - delete_post_meta( $post_id, 'wpstream_free_video_external' );
2145 - }
2146 - }
2147 -
2148 - endif;
2149 -
2150 935 }
2151 -
2152 - /** Retire key and hosted-player metadata owned by a VOD's old recording. */
2153 - private function wpstream_retire_vod_recording_identity( $post_id ) {
2154 - $this->main->drm_key_delivery->invalidate_vod_lookup( $post_id );
2155 - foreach ( array( 'hlsDecryptionKey', 'hlsDecryptionKeyIndex', 'wpstream_vod_video_data', 'wpstream_vod_embed_key', 'wpstream_vod_embed_url' ) as $meta_key ) {
2156 - delete_post_meta( $post_id, $meta_key );
2157 - }
2158 - }
2159 936
2160 937
2161 938 /**
2162 - * save meta options
2163 - *
2164 - * Register the plugin's post metaboxes: VOD settings on `wpstream_product_vod`,
2165 - * the video collection box on `wpstream_bundles`, and (for WooCommerce bundle
2166 - * products) the video-collection options box on `product`. Hooked to add_meta_boxes.
2167 - *
2168 - * @since 3.0.1
2169 - * @return void
2170 - */
2171 - public function add_wpstream_product_metaboxes() {
2172 - global $post;
2173 - $post_id = $post->ID;
2174 -
2175 -
2176 - // VOD settings metabox and the bundle video-collection metabox.
2177 - 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');
2178 - add_meta_box( 'custom_metabox_video_collection', esc_html__( 'Video Collection', 'wpstream' ), 'wpstream_bundle_custom_metabox_callback', 'wpstream_bundles', 'normal', 'high' );
2179 -
2180 - // For WooCommerce bundle products, add the video-collection options box on the product screen.
2181 - if(function_exists('wc_get_product')):
2182 - $product = wc_get_product( $post_id );
2183 - if ( $product ) {
2184 -
2185 - if ( $product->get_type() === 'wpstream_bundle' ) {
2186 - add_meta_box(
2187 - 'wpstream_woo_custom_metabox',
2188 - esc_html__( 'Video Collection Options', 'wpstream' ),
2189 - 'wpstream_bundle_custom_metabox_callback',
2190 - 'product',
2191 - 'normal',
2192 - 'default'
2193 - );
2194 - }
2195 - }
2196 - endif;
2197 -
2198 - /**
2199 - * Fires after the plugin registered its metaboxes for the edit screen.
2200 - *
2201 - * @since 4.14.0
2202 - *
2203 - * @param int $post_id Post being edited.
2204 - * @param WP_Post $post The post.
2205 - */
2206 - do_action( 'wpstream_registered_metaboxes', intval( $post_id ), $post );
939 + * save meta options
940 + *
941 + * @since 3.0.1
942 + */
943 + public function add_wpstream_product_metaboxes() {
944 + add_meta_box( 'add_wpstream_product_metaboxes-sectionid', __( 'Live Channel/Video Settings', 'wpestate' ),array($this,'display_meta_options'),'wpstream_product' ,'normal','default');
2207 945 }
2208 946
2209 947
2210 948 /**
2211 - * make woocomerce virtual products
2212 - *
2213 - * Force WpStream WooCommerce product types (live_stream, video_on_demand,
2214 - * wpstream_bundle) to be virtual so they need no shipping. Hooked to save.
2215 - *
2216 - * @since 3.0.1
2217 - * @param int $post_id Post ID being saved (unused; uses global $post).
2218 - * @param WP_Post $post Post object (overwritten by global $post).
2219 - * @return void
2220 - */
949 + * make woocomerce virtual products
950 + *
951 + * @since 3.0.1
952 + */
953 +
954 +
2221 955 public function wpstream_make_product_virtual($post_id,$post){
2222 956 global $post;
2223 957 if(isset($post->ID)){
2224 - // Only WooCommerce products are relevant.
2225 958 if ( $post->post_type !== 'product' ) return;
2226 - // Mark WpStream product types as virtual.
2227 959 $term_list = wp_get_post_terms($post->ID, 'product_type');
2228 - if( !empty($term_list) &&
2229 - isset($term_list[0]->slug) &&
2230 - in_array($term_list[0]->slug, ['live_stream', 'video_on_demand', 'wpstream_bundle'])
2231 - ){
960 + if( $term_list[0]->name=='video_on_demand' || $term_list[0]->name=='live_stream'){
2232 961 update_post_meta( $post->ID, '_virtual', 'yes' );
2233 962 }
2234 963 }
2235 964 }
@@ -2235,41 +964,31 @@
2235 964 }
2236 965
2237 966
2238 967
2239 - /**
2240 - * render meta options
2241 - *
2242 - * Render the "Video On Demand Settings" metabox: media-type selector,
2243 - * recording chooser, optional captions (.vtt) picker, and the self-hosted/
2244 - * external video URL field. Echoes HTML.
2245 - *
2246 - * @since 3.0.1
2247 - * @param WP_Post $post Post being edited (overwritten by global $post).
2248 - * @return void
2249 - */
968 + /**
969 + * render meta options
970 + *
971 + * @since 3.0.1
972 + */
2250 973 public function display_meta_options( $post ) {
2251 - // Nonce for the metabox save + use the global post object.
2252 974 wp_nonce_field( plugin_basename( __FILE__ ), 'estate_agent_noncename' );
2253 975 global $post;
2254 976
2255 - // Determine the pre-selected media type / video.
2256 977 $is_live = '';
2257 978 $is_video = '';
2258 979 $is_video_external = '';
2259 - // When arriving from "create VOD from recording", preselect the recording.
2260 980 if( isset( $_GET['new_video_name']) && $_GET['new_video_name']!='' ){
2261 981 $is_video = ' selected ';
2262 982 $wpstream_free_video = esc_html( $_GET['new_video_name']);
2263 983 }else{
2264 - // Otherwise read the saved type/video from post meta and select accordingly.
2265 984 $wpstream_product_type = esc_html(get_post_meta($post->ID, 'wpstream_product_type', true));
2266 985 $wpstream_free_video = esc_html(get_post_meta($post->ID, 'wpstream_free_video', true));
2267 -
986 +
2268 987 if($wpstream_product_type==1){
2269 988 $is_live = ' selected ';
2270 989 }
2271 -
990 +
2272 991 if($wpstream_product_type==2){
2273 992 $is_video = ' selected ';
2274 993 }
2275 994
@@ -2277,90 +996,51 @@
2277 996 $is_video_external = ' selected ';
2278 997 }
2279 998 }
2280 999
2281 - // Media-type dropdown (recording vs self-hosted/external).
2282 1000 print'
2283 1001 <p class="meta-options">
2284 1002 <label for="wpstream_product_type">'.__('Media Type:','wpstream').' </label><br />
2285 1003 <select id="wpstream_product_type" name="wpstream_product_type">
2286 - <option value="2" '.$is_video.'>'.__('Recording','wpstream').'</option>
2287 - <option value="3" '.$is_video_external.'>'.__('Self Hosted or External Video','wpstream').'</option>
1004 + <option value="1" '.$is_live.'>'.__('Free Live Channel - encrypted streaming & copy protection','wpstream').'</option>
1005 + <option value="2" '.$is_video.'>'.__('Free Video - encrypted streaming & copy protection','wpstream').'</option>
1006 + <option value="3" '.$is_video_external.'>'.__('Free Video - unprotected','wpstream').'</option>
2288 1007 </select>
2289 1008 </p>
2290 1009 ';
2291 1010
2292 1011
2293 - // Fetch the account's recordings to populate the chooser.
2294 - $video_list = $this->main->wpstream_live_connection->wpstream_get_videos();
2295 1012
2296 -
2297 - // Recording chooser dropdown, pre-selecting the saved recording.
2298 - print '<div class="meta-options video_free">';
2299 - print '<p class="meta-option wpstream_free_video">';
2300 - print '<label for="wpstream_free_video">'.__('Choose video:','wpstream').' </label><br />
1013 + print '
1014 + <p class="meta-options video_free">
1015 + <label for="wpstream_free_video">'.__('Choose video:','wpstream').' </label><br />
2301 1016 <select id="wpstream_free_video" name="wpstream_free_video">';
1017 + $video_list = $this->main->wpstream_live_connection->wpstream_get_videos();
2302 1018
2303 - if( is_array( $video_list ) ) {
2304 - foreach ($video_list as $key=>$value){
2305 - print '<option value="'.$key.'"';
2306 - if($wpstream_free_video === $key){
2307 - print ' selected ';
1019 + if(is_array($video_list)){
1020 + foreach ($video_list as $key=>$value){
1021 + print '<option value="'.$key.'"';
1022 + if($wpstream_free_video === $key){
1023 + print ' selected ';
1024 + }
1025 + print '>'.$value.'</option>';
1026 + }
2308 1027 }
2309 - print '>'.$value.'</option>';
2310 - }
2311 - }
2312 - print'</select>';
2313 - print '</p> ';
2314 - print '</div>';
1028 + print'
1029 + </select>
1030 + </p>
1031 + ';
2315 1032
2316 - // Optional captions (.vtt) picker; hide the select button when a file is already set.
2317 - $wpstream_closed_captions_file = get_post_meta($post->ID, 'wpstream_closed_captions_file', true);
2318 -
2319 - $button_style = $wpstream_closed_captions_file ? 'style="display:none;"' : '';
2320 -
2321 - print '<p class="meta-option wpstream_vod_captions_url">';
2322 - print '<label for="wpstream_vod_captions_url_button">'.__('Captions file (optional):','wpstream').' </label><br />
2323 - <input type="hidden" id="wpstream_closed_captions_file" name="wpstream_closed_captions_file" value="'.esc_attr($wpstream_closed_captions_file).'" />
2324 - <input id="wpstream_vod_captions_url_button" type="button" class="upload_button button" value="'.esc_html__('Select .vtt Captions File','wpstream').'" '.$button_style.' />
2325 - <span class="wpstream_caption_file_display">'.( $wpstream_closed_captions_file ? esc_html( basename( $wpstream_closed_captions_file ) ) : '' ).'</span>';
2326 - if ( $wpstream_closed_captions_file ) {
2327 - print '<input type="button" class="button wpstream_remove_caption" value="'.esc_html__('Remove','wpstream').'" style="margin-left: 5px;" />';
2328 - }
2329 - print '</p> ';
2330 -
2331 - // Self-hosted / external video URL field with a media-library select button.
2332 1033 $wpstream_free_video_external= esc_html(get_post_meta($post->ID, 'wpstream_free_video_external', true));
2333 - print '<div class="meta-options1 video_free_external">
2334 - <label for="wpstream_free_video_external">'.__('Video:','wpstream').' </label><br />
1034 + print '<p class="meta-options1 video_free_external">
1035 + <label for="wpstream_free_video_external">'.__('Choose video:','wpstream').' </label><br />
2335 1036
2336 1037 <input id="wpstream_free_video_external" type="text" size="36" name="wpstream_free_video_external" value="'.$wpstream_free_video_external.'" />
2337 - <input id="wpstream_free_video_external_button" type="button" size="40" class="upload_button button" value="'.esc_html__('Select Video','wpstream').'" />';
2338 - // Show the recording hint vs the external hint depending on the saved media type.
2339 - if($wpstream_product_type==2){
2340 - $show_recording='';
2341 - $show_external='style="display:none"';
2342 - }else{
2343 - $show_recording='style="display:none"';
2344 - $show_external='';
2345 - }
2346 - print '<p '.$show_recording.' class="wpstream_option_vod_source wpstream_show_recording">'.esc_html__('Choose one of your existing recordings.','wpstream').'</p>';
2347 - 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>';
2348 -
2349 - print '</div> ';
1038 + <input id="wpstream_free_video_external_button" type="button" size="40" class="upload_button button" value="'.esc_html__('Select Video','wpstream').'" />
2350 1039
2351 - /**
2352 - * Fires at the end of the Video On Demand Settings metabox.
2353 - *
2354 - * Callbacks echo their own fields and own their escaping; saving
2355 - * them is the callback's job (hook `save_post`).
2356 - *
2357 - * @since 4.14.0
2358 - *
2359 - * @param int $post_id VOD post ID.
2360 - * @param WP_Post $post The post.
2361 - */
2362 - do_action( 'wpstream_vod_meta_options_after', intval( $post->ID ), $post );
1040 + <p>'.esc_html__('You can choose a video from your computer or use the url from external source or use the url of a YouTube Video or use the url from a Vimeo video.','wpstream').'</p>
1041 +
1042 + </p> ';
2363 1043 }
2364 1044
2365 1045
2366 1046
@@ -2370,179 +1050,44 @@
2370 1050
2371 1051 /**
2372 1052 * Add new product types to Woocommerce select product type
2373 1053 *
2374 - * Register the "Live Channel" and "Video On Demand" WooCommerce product
2375 - * types in the product-type dropdown. Filter callback.
2376 - *
2377 - * @since 3.0.1
2378 - * @param array $types Existing product-type => label map.
2379 - * @return array The map with the WpStream types added.
2380 - */
1054 + * @since 3.0.1
1055 + */
2381 1056 public function wpstream_add_products( $types ){
2382 - $types[ 'live_stream' ] = __( 'Live Channel','wpstream' );
2383 - $types[ 'video_on_demand' ] = __( 'Video On Demand','wpstream' );
2384 -
1057 + $types[ 'live_stream' ] = __( 'Live Channel','wpestream' );
1058 + $types[ 'video_on_demand' ] = __( 'Video On Demand','wpestream' );
1059 +
2385 1060 return $types;
2386 1061 }
2387 -
2388 - /**
2389 - * Map the WpStream product-type slugs to their WC_Product subclasses.
2390 - * Filter callback for woocommerce_product_class.
2391 - *
2392 - * @param string $classname Default product class name.
2393 - * @param string $product_type The product type slug being instantiated.
2394 - * @return string The resolved product class name.
2395 - */
2396 - public function wpstream_add_products_class( $classname, $product_type ) {
2397 - /**
2398 - * Filter the product-type slug => WC_Product subclass map.
2399 - *
2400 - * A mapped class must exist; unknown classes leave WooCommerce's own
2401 - * resolution in place.
2402 - *
2403 - * @since 4.14.0
2404 - *
2405 - * @param array $map Slug => class name.
2406 - */
2407 - $map = (array) apply_filters(
2408 - 'wpstream_product_class_map',
2409 - array(
2410 - 'live_stream' => 'WC_Product_Live_Stream',
2411 - 'video_on_demand' => 'WC_Product_Video_On_Demand',
2412 - )
2413 - );
2414 - if ( isset( $map[ $product_type ] ) && class_exists( $map[ $product_type ] ) ) {
2415 - $classname = $map[ $product_type ];
2416 - }
2417 - return $classname;
2418 - }
2419 -
2420 - /**
2421 - * Load the custom WC_Product subclass files when WooCommerce is active.
2422 - *
2423 - * @return void
2424 - */
2425 - public function wpstream_add_custom_wc_products() {
2426 - // Require the live-stream and VOD product classes (WooCommerce only).
2427 - if( class_exists( 'WooCommerce' ) ){
2428 - require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-wc-product-live-stream.php';
2429 - require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-wc-product-video-on-demand.php';
2430 - }
2431 - }
2432 -
1062 +
1063 +
1064 +
2433 1065 /**
2434 1066 * Js action to do when user pick live stream or video on demand
2435 1067 *
2436 1068 * @since 3.0.1
2437 - */
2438 -
2439 - /**
2440 - * Whether WpStream WooCommerce product UI should load for this product.
2441 - *
2442 - * Skips simple, course, and other non-WpStream types so LearnDash and
2443 - * other integrations can use the General product data tab without conflict.
2444 - *
2445 - * @param int $post_id Product post ID. Uses global $post when 0.
2446 - * @return bool
2447 - */
2448 - public function wpstream_is_wpstream_wc_product_context( $post_id = 0 ) {
2449 - // Creating a new stream/VOD from a query-string action always counts.
2450 - if ( isset( $_GET['new_stream'] ) || isset( $_GET['new_video_name'] ) ) {
2451 - return true;
2452 - }
2453 -
2454 - // On the first product save WooCommerce has not necessarily made
2455 - // the selected taxonomy term observable through wc_get_product()
2456 - // yet. Trust only its allow-listed product-type form field here;
2457 - // the save handler remains scoped to WooCommerce product metadata.
2458 - if ( isset( $_POST['product-type'] ) && is_scalar( $_POST['product-type'] ) ) {
2459 - $submitted_type = sanitize_key( wp_unslash( $_POST['product-type'] ) );
2460 - if ( in_array( $submitted_type, array( 'live_stream', 'video_on_demand', 'wpstream_bundle', 'subscription' ), true ) ) {
2461 - return true;
2462 - }
2463 - }
2464 -
2465 - // Fall back to the current global post when no ID was passed.
2466 - if ( ! $post_id ) {
2467 - global $post;
2468 - $post_id = ( $post && isset( $post->ID ) ) ? (int) $post->ID : 0;
2469 - }
2470 -
2471 - // Need a product ID and WooCommerce to resolve the type.
2472 - if ( ! $post_id || ! function_exists( 'wc_get_product' ) ) {
2473 - return false;
2474 - }
2475 -
2476 - $product = wc_get_product( $post_id );
2477 - if ( ! $product ) {
2478 - return false;
2479 - }
2480 -
2481 - // True only for WpStream (and subscription) product types.
2482 - return in_array(
2483 - $product->get_type(),
2484 - array( 'live_stream', 'video_on_demand', 'wpstream_bundle', 'subscription' ),
2485 - true
2486 - );
2487 - }
2488 -
2489 - /**
2490 - * WooCommerce product types that use the core Regular price field.
2491 - *
2492 - * @return string[]
2493 - */
2494 - public function wpstream_get_wc_product_types_with_pricing() {
2495 - return array( 'live_stream', 'video_on_demand', 'wpstream_bundle' );
2496 - }
2497 -
2498 - /**
2499 - * Add show_if_* classes to the pricing panel for WpStream product types.
2500 - *
2501 - * WooCommerce renders .options_group.pricing with show_if_simple only. Custom
2502 - * types rely on show_if_{type} classes so meta-boxes-product.js can toggle them
2503 - * when the product type dropdown changes, including on unsaved new products.
2504 - *
2505 - * @param string $hook Admin screen hook suffix.
2506 - */
2507 - public function wpstream_enqueue_wc_product_pricing_visibility( $hook ) {
2508 - // Only on the add/edit-post admin screens.
2509 - if ( ! in_array( $hook, array( 'post.php', 'post-new.php' ), true ) ) {
1069 + */
1070 +
1071 + public function wpstream_products_custom_js() {
1072 + if ( 'product' != get_post_type() ) :
2510 1073 return;
2511 - }
1074 + endif;
2512 1075
2513 - // Only on the WooCommerce product post type.
2514 - $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
2515 - if ( ! $screen || 'product' !== $screen->post_type ) {
2516 - return;
2517 - }
1076 + ?>
1077 + <script type='text/javascript'>
1078 + jQuery( document ).ready( function() {
1079 + jQuery('.options_group.pricing' ).addClass ( 'show_if_live_stream' ).show();
1080 + jQuery('.options_group.pricing' ).addClass ( 'show_if_video_on_demand' ).show();
1081 + jQuery('._sold_individually_field').parent().addClass('show_if_live_stream').show();
1082 + jQuery('._sold_individually_field').parent().addClass('show_if_video_on_demand').show();
1083 + jQuery('._sold_individually_field').show();
1084 +
1085 + var selected = jQuery('#product-type').val();
1086 + });
1087 + </script>
1088 + <?php
2518 1089
2519 - // Need WooCommerce's product meta-boxes script to attach to.
2520 - if ( ! wp_script_is( 'wc-admin-product-meta-boxes', 'registered' ) ) {
2521 - return;
2522 - }
2523 -
2524 - // Pass the WpStream pricing types to JS and add show_if_{type} classes so the pricing panel toggles correctly.
2525 - $types_json = wp_json_encode( array_values( $this->wpstream_get_wc_product_types_with_pricing() ) );
2526 -
2527 - wp_add_inline_script(
2528 - 'wc-admin-product-meta-boxes',
2529 - "jQuery( function( $ ) {
2530 - var wpstreamPricingProductTypes = {$types_json};
2531 - var \$pricing = $( '.options_group.pricing' );
2532 - var \$soldIndividually = $( '._sold_individually_field' ).parent();
2533 -
2534 - wpstreamPricingProductTypes.forEach( function( type ) {
2535 - \$pricing.addClass( 'show_if_' + type );
2536 - \$soldIndividually.addClass( 'show_if_' + type );
2537 - } );
2538 -
2539 - if ( wpstreamPricingProductTypes.indexOf( $( '#product-type' ).val() ) !== -1 ) {
2540 - \$pricing.show();
2541 - \$soldIndividually.show();
2542 - }
2543 - } );"
2544 - );
2545 1090 }
2546 1091
2547 1092
2548 1093 /**
@@ -2547,103 +1092,44 @@
2547 1092
2548 1093 /**
2549 1094 * Add custom classes to the product types
2550 1095 *
2551 - * Adjust the WooCommerce product-data tab visibility classes so WpStream
2552 - * types hide the Shipping tab and show the Inventory tab. Filter callback.
2553 - *
2554 - * @since 3.0.1
2555 - * @param array $tabs WooCommerce product data tabs config.
2556 - * @return array The modified tabs config.
2557 - */
1096 + * @since 3.0.1
1097 + */
1098 +
2558 1099 public function wpstream_hide_attributes_data_panel( $tabs) {
1100 +
1101 + $tabs['shipping']['class'][] = 'hide_if_live_stream hide_if_video_on_demand';
1102 + $tabs['inventory']['class'][] = 'show_if_live_stream show_if_video_on_demand';
1103 + // $tabs['general']['class'][] = 'show_if_live_stream show_if_video_on_demand';
2559 1104
2560 - // Hide Shipping and show Inventory for WpStream product types.
2561 - $tabs['shipping']['class'][] = 'hide_if_live_stream hide_if_video_on_demand hide_if_wpstream_bundle';
2562 - $tabs['inventory']['class'][] = 'show_if_live_stream show_if_video_on_demand show_if_wpstream_bundle';
2563 -
2564 1105 return $tabs;
2565 1106 }
2566 1107
2567 1108
2568 -
2569 - /**
2570 - * Hide buy now on products if Netflix mode
2571 - *
2572 - * When global subscription ("Netflix") mode is on, mark WpStream media
2573 - * products as not individually purchasable (access comes via the
2574 - * subscription instead). Filter callback for is_purchasable.
2575 - *
2576 - * @since 3.12
2577 - * @param bool $purchaseable_product_wpblog Current purchasable flag.
2578 - * @param WC_Product $product The product being checked.
2579 - * @return bool False for WpStream media in global-sub mode, else the original flag.
2580 - */
2581 - public function wpstream_hide_buy_now_subscription_mode( $purchaseable_product_wpblog,$product){
2582 - $product_id=$product->get_id();
2583 -
2584 - // Stable type slug is '' when the product has no term (never a crash).
2585 - $product_type_slug = wpstream_get_product_type_slug($product_id);
2586 -
2587 - // Global subscription ("Netflix") mode flag.
2588 - $subscription_model = intval( get_option('wpstream_global_sub','')) ;
2589 -
2590 - if($subscription_model==1){ // if we have Neflix mode
2591 - // WpStream media types are not directly purchasable in this mode.
2592 - if( $product_type_slug=='live_stream' || $product_type_slug=='video_on_demand' || $product_type_slug=='wpstream_bundle' ){
2593 - return false;
2594 - }
2595 - }
2596 -
2597 - return $purchaseable_product_wpblog;
2598 - }
2599 -
2600 -
2601 -
2602 -
2603 -
2604 -
2605 1109 /**
2606 1110 * Add custom fields to custom product types
2607 1111 *
2608 - * Render the extra WooCommerce "General" product fields for WpStream types:
2609 - * the subscription-live flag, the recording/video chooser, and the
2610 - * "attach to subscription" multi-select. Echoes HTML.
2611 - *
2612 - * @since 3.0.1
2613 - * @return void
2614 - */
1112 + * @since 3.0.1
1113 + */
1114 +
2615 1115 public function wpstream_add_custom_general_fields() {
1116 +
2616 1117 global $woocommerce, $post;
2617 -
2618 - // A new WooCommerce product is still an auto-draft `simple` product
2619 - // when this hook renders. Emit the type-scoped controls there so
2620 - // WooCommerce can reveal them after the administrator selects a
2621 - // WpStream type; the show_if_* classes keep them hidden otherwise.
2622 - $post_id = isset( $post->ID ) ? (int) $post->ID : 0;
2623 - $is_new_product = $post_id && 'auto-draft' === get_post_status( $post_id );
2624 - if ( ! $is_new_product && ! $this->wpstream_is_wpstream_wc_product_context( $post_id ) ) {
2625 - return;
2626 - }
2627 - // Subscription-based live channel toggle (only when WC Subscriptions is active).
2628 1118 if(function_exists('wcs_user_has_subscription')){
2629 - echo '<div class="options_group show_if_subscription">';
1119 + echo '<div class="options_group show_if_subscription">';
2630 1120 woocommerce_wp_select(
2631 1121 array(
2632 1122 'id' => '_subscript_live_event',
2633 1123 'label' => __( 'Is a subscription based live channel ?', 'woocommerce' ),
2634 - 'options' => array("yes"=>"yes","no"=>"no", "none" => "none")
1124 + 'options' => array("yes"=>"yes","no"=>"no")
2635 1125 )
2636 1126 );
2637 1127 echo '</div>';
2638 1128 }
2639 1129
2640 - echo '<div class="options_group show_if_live_stream" style="border:none;"></div>';
2641 - // VOD video chooser section.
2642 - echo '<div class="options_group show_if_video_on_demand">';
2643 -
2644 -
2645 - // Pre-selected video: from the new-video query arg, else the saved _movie_url meta.
1130 + echo '<div class="options_group show_if_live_stream" style="border:none;"></div>';
1131 + echo '<div class="options_group show_if_video_on_demand">';
2646 1132 $selected='';
2647 1133 if( isset( $_GET['new_video_name']) && $_GET['new_video_name']!='' ){
2648 1134 $selected=esc_html($_GET['new_video_name']);
2649 1135 }
@@ -2649,161 +1135,43 @@
2649 1135 }
2650 1136 if($selected==''){
2651 1137 $selected= get_post_meta($post->ID,'_movie_url',true);
2652 1138 }
2653 - // Only administrators may assign videos.
2654 - if( !current_user_can('administrator') ){
2655 - print '<div style="margin:10px;">'.esc_html__('You need to be an administrator in order to assign videos','wpstream').'</div>';
2656 - }else{
2657 - woocommerce_wp_select(
2658 - array(
2659 - 'id' => '_movie_url',
2660 - 'label' => __( 'Choose video', 'woocommerce' ),
2661 - 'options' => $this->main->wpstream_live_connection->wpstream_get_videos(),
2662 - 'selected'=> true,
2663 - 'value' => $selected
2664 - )
2665 - );
2666 - }
2667 -
2668 -
2669 1139
2670 - echo '</div>';
2671 -
2672 - // "Attach to subscription" multi-select (only when WC Subscriptions is active).
2673 - if(function_exists('wcs_user_has_subscription')){
2674 - $selected_sub='';
2675 - echo '<div class="options_group show_if_video_on_demand show_if_live_stream">';
2676 - if( isset( $_GET['wpstream_parent_sub']) && $_GET['wpstream_parent_sub']!='' ){
2677 - $selected_sub=esc_html($_GET['wpstream_parent_sub']);
2678 - }
2679 - if($selected_sub==''){
2680 - $selected_sub= get_post_meta($post->ID,'_wpstream_parent_sub',true);
2681 - }
2682 - woocommerce_wp_select(
1140 + woocommerce_wp_select(
2683 1141 array(
2684 - 'id' => '_wpstream_parent_sub',
2685 - 'name' => '_wpstream_parent_sub[]',
2686 - 'label' => __( 'Attach to subscription', 'woocommerce' ),
2687 - 'options' => $this->wpstream_return_subscriptions_created(),
1142 + 'id' => '_movie_url',
1143 + 'label' => __( 'Select the video file.', 'woocommerce' ),
1144 + 'options' => $this->main->wpstream_live_connection->wpstream_get_videos(),
2688 1145 'selected'=> true,
2689 - 'value' => $selected_sub,
2690 - 'custom_attributes' => array('multiple' => 'multiple')
1146 + 'value' => $selected
2691 1147 )
2692 1148 );
2693 -
2694 - echo '</div>';
2695 -
2696 - }
2697 - }
2698 -
2699 -
2700 - /**
2701 - * Build an id => title map of all WooCommerce subscription products,
2702 - * for the "attach to subscription" selector. Includes a "none" option.
2703 - *
2704 - * @return array Map of subscription product ID => title (0 => 'none').
2705 - */
2706 - public function wpstream_return_subscriptions_created(){
2707 - // Seed with the "none" choice.
2708 - $return=array('0'=>'none');
2709 1149
2710 - // Query all subscription-type products, title-ordered.
2711 - $args = array(
2712 - 'post_type' => 'product',
2713 - 'posts_per_page' => -1,
2714 - 'orderby' => 'title',
2715 - 'order' => 'ASC',
2716 - 'tax_query' => array(
2717 - 'relation' => 'AND',
2718 - array(
2719 - 'taxonomy' => 'product_type',
2720 - 'field' => 'slug',
2721 - 'terms' => array( 'subscription'),
2722 - )
2723 - )
2724 - );
2725 -
2726 - // Collect each subscription's id => title.
2727 - $subscriptions = new WP_Query($args);
2728 - if($subscriptions->have_posts()):
2729 - while ($subscriptions->have_posts()): $subscriptions->the_post();
2730 - $return[ get_the_ID() ] = get_the_title();
2731 - endwhile;
2732 - endif;
2733 -
2734 - // Restore the main query and return the map.
2735 - wp_reset_postdata();
2736 - return $return;
2737 -
1150 + echo '</div>';
2738 1151 }
2739 -
2740 -
2741 -
2742 -
2743 -
2744 1152
2745 1153 /**
2746 1154 * Save custom fields
2747 1155 *
2748 - * Persist the WpStream WooCommerce product fields (_movie_url,
2749 - * _subscript_live_event, _wpstream_parent_sub) on save and reset the
2750 - * event_passed flag. Hooked to the product save action.
2751 - *
2752 - * @since 3.0.1
2753 - * @param int $post_id Product ID being saved.
2754 - * @return void
2755 - */
1156 + * @since 3.0.1
1157 + */
1158 +
2756 1159 public function wpstream_add_custom_general_fields_save( $post_id ){
2757 - // Only handle WpStream product contexts.
2758 - if ( ! $this->wpstream_is_wpstream_wc_product_context( (int) $post_id ) ) {
2759 - return;
2760 - }
2761 1160
2762 - // A Source Recording owns the cached DRM and hosted-player identity.
2763 - // Retire that identity before replacing the source so its old key index
2764 - // is still available to invalidate the lookup caches.
2765 - if (
2766 - has_term( 'video_on_demand', 'product_type', $post_id )
2767 - && ! empty( $_POST['_movie_url'] )
2768 - && is_scalar( $_POST['_movie_url'] )
2769 - ) {
2770 - $previous_recording = (string) get_post_meta( $post_id, '_movie_url', true );
2771 - $new_recording = sanitize_text_field( wp_unslash( $_POST['_movie_url'] ) );
2772 - if ( $new_recording !== $previous_recording ) {
2773 - $this->wpstream_retire_vod_recording_identity( $post_id );
2774 - }
2775 - }
2776 -
2777 - // Meta keys this handler may write.
2778 1161 $permited_values = array(
2779 1162 '_movie_url',
2780 - '_subscript_live_event',
2781 - '_wpstream_parent_sub',
2782 -
1163 + '_subscript_live_event'
2783 1164 );
2784 -
2785 -
2786 -
2787 - // Reset event_passed and save each allow-listed field.
1165 + $allowed_html=array();
2788 1166 foreach($_POST as $key=>$value){
2789 1167 update_post_meta( $post_id, 'event_passed', 0 );
2790 1168 if( in_array($key, $permited_values) ){
2791 1169 if( !empty( $_POST[$key] ) ){
2792 - $key = sanitize_key($key);
2793 - $value = sanitize_text_field($_POST[$key]);
2794 -
2795 - // The parent-subscription field is an array; sanitize each element.
2796 - if($key=='_wpstream_parent_sub'){
2797 - $value= $_POST[$key];
2798 - $value = array_map("sanitize_text_field", $value);
2799 -
2800 - }
2801 - update_post_meta( $post_id, $key, $value );
1170 + update_post_meta( $post_id, wp_kses ($key,$allowed_html), wp_kses ( $_POST[$key],$allowed_html) );
2802 1171 }
2803 1172 }
2804 1173 }
2805 - //die();
2806 1174
2807 1175 }
2808 1176
2809 1177 /**
@@ -2808,34 +1176,25 @@
2808 1176
2809 1177 /**
2810 1178 * Add to cart redirect
2811 1179 *
2812 - * Render the simple add-to-cart template for WpStream product types.
2813 - *
2814 - * @since 3.0.1
2815 - * @return void
2816 - */
1180 + * @since 3.0.1
1181 + */
1182 +
2817 1183 public function wpstream_add_to_cart() {
2818 1184 wc_get_template( 'single-product/add-to-cart/simple.php' );
2819 1185 }
2820 1186
2821 -
1187 +
2822 1188 /**
2823 1189 * Replace add to cart button
2824 1190 *
2825 - * For live_stream / video_on_demand products, swap the loop add-to-cart
2826 - * button for a direct shop add-to-cart link. Filter callback.
2827 - *
2828 - * @since 3.0.1
2829 - * @param string $button Original button HTML.
2830 - * @param WC_Product $product The product (overwritten by global $product).
2831 - * @return string The (possibly replaced) button HTML.
2832 - */
1191 + * @since 3.0.1
1192 + */
2833 1193 public function replacing_add_to_cart_button( $button, $product ) {
2834 1194 global $product;
2835 1195 $product_type = $product->get_type();
2836 1196
2837 - // Only WpStream media types get the custom link; others keep the default button.
2838 1197 if($product_type==='live_stream' || $product_type=='video_on_demand'){
2839 1198 return $button = '<a class="button" href="'.get_site_url().'/shop/?add-to-cart=' .$product->get_id(). '&quantity=1">' . __( 'Add to Cart', 'woocommerce' ) . '</a>';
2840 1199 }else{
2841 1200 return $button;
@@ -2845,711 +1204,108 @@
2845 1204
2846 1205 /**
2847 1206 * Admin notices
2848 1207 *
2849 - * Print global admin notices: (commented-out) WooCommerce-missing notice
2850 - * and an error if the PHP cURL extension is unavailable, plus the dismiss
2851 - * nonce. Echoes HTML.
2852 - *
2853 - * @since 3.0.1
2854 - * @return void
2855 - */
1208 + * @since 3.0.1
1209 + */
2856 1210 public function wpstream_admin_notice() {
2857 1211 global $pagenow;
2858 1212 global $typenow;
2859 1213
2860 - // Stored dismissed-notice flags.
2861 - $wpstream_notices = get_option('wpstream_notices');
2862 -
2863 - /*
1214 +
2864 1215 if ( !in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
2865 - if( !is_array($wpstream_notices) ||
2866 - !isset($wpstream_notices['wpstream_woo_notice']) ||
2867 - ( isset($wpstream_notices['wpstream_woo_notice']) && $wpstream_notices['wpstream_woo_notice']!='yes') ){
2868 -
2869 -
2870 - print '<div class="notice wpstream_notices notice-error is-dismissible" data-notice-type="wpstream_woo_notice" >
2871 - <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>
1216 + print '<div class="notice notice-error is-dismissible">
1217 + <p>'.__( 'WpStream Pay Per View / Subscription mode works only with WooCommerce - If you want to charge money for your live events or videos you need to activate WooCommerce plugin', 'wpestate' ).'</p>
2872 1218 </div>';
2873 - }
2874 1219 }
2875 - */
2876 -
2877 - // Hard requirement: warn when the PHP cURL extension is missing.
2878 1220 if( !in_array ('curl', get_loaded_extensions())) {
2879 1221 print '<div class="notice notice-error is-dismissible">
2880 - <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>
1222 + <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.', 'wpestate' ).'</p>
2881 1223 </div>';
2882 1224 }
2883 1225
2884 1226
2885 - // Nonce used by the JS that records notice dismissals.
2886 - $ajax_nonce = wp_create_nonce( "wpstream_notice_nonce" );
2887 - print '<input type="hidden" id="wpstream_notice_nonce" value="'.esc_html($ajax_nonce).'"/>';
1227 + $wpestate_notices = get_option('wp_stream_notices');
2888 1228
2889 - }
1229 + if( !is_array($wpestate_notices) ||
1230 + !isset($wpestate_notices['wpstream_update_1021']) ||
1231 + ( isset($wpestate_notices['wpstream_update_1021']) && $wpestate_notices['wpstream_update_1021']!='yes') ){
2890 1232
2891 - /**
2892 - * Get plugin latest update release date from WordPress.org
2893 - * @param $plugin_slug
2894 - * @param $version
2895 - *
2896 - * @return bool
2897 - */
2898 - public function get_plugin_release_date( $plugin_slug, $version = null ) {
2899 - // This runs on every admin page while an update is pending, so the
2900 - // answer is cached for 12h — including a negative one, otherwise a
2901 - // failed lookup would retry per page load. Keyed by version so a
2902 - // newer release triggers a fresh lookup.
2903 - $cache_key = 'wpstream_release_date_' . md5( $plugin_slug . '|' . (string) $version );
2904 - $cached = get_transient( $cache_key );
2905 - if ( false !== $cached ) {
2906 - // 'none' is the stored form of a negative result.
2907 - return 'none' === $cached ? false : $cached;
1233 + print '<div id ="setting-error-wprentals-cache" data-notice-type="wpstream_update_1021" data-dismissible="disable-done-notice-forever" class="wpestate_notices updated settings-error notice is-dismissible">
1234 + <p>'.esc_html__( 'New! We just released WpStream WordPress Theme - a turn key solution for video delivery & live streaming. This theme is built around WpStream plugin and is the perfect solution for video streaming or rentals platform. ','wpstream').'<a href="https://wpstream.net/wpstream-theme-a-live-streaming-wordpress-theme" target="_blank">'.esc_html__('Download Theme','wpstream').'</a></p>
1235 + </div>';
2908 1236 }
2909 1237
2910 - // Query the WordPress.org plugins info API (short timeout: this is
2911 - // cosmetic notice logic and must never stall the admin).
2912 - $api_url = 'https://api.wordpress.org/plugins/info/1.0/' . $plugin_slug . '.json';
2913 - $response = wp_remote_get( $api_url, array( 'timeout' => 3 ) );
2914 1238
2915 - // Resolve the answer (false = no usable date), then cache it once.
2916 - $release_date = false;
2917 1239
2918 - if ( ! is_wp_error( $response ) ) {
2919 - // Decode the JSON body; a date needs a versions list to validate against.
2920 - $data = json_decode( wp_remote_retrieve_body( $response ), true );
2921 1240
2922 - if ( $data && isset( $data['versions'] ) ) {
2923 - // no version provided, get the latest version
2924 - if ( ! $version ) {
2925 - $version = $data['version'];
2926 - }
2927 1241
2928 - // The version must exist in the versions array.
2929 - // (note: last_updated is the plugin's overall date, not per-version)
2930 - if ( isset( $data['versions'][ $version ] ) && isset( $data['last_updated'] ) ) {
2931 - $release_date = date( 'Y-m-d', strtotime( $data['last_updated'] ) );
2932 - }
2933 - }
2934 - }
1242 + $ajax_nonce = wp_create_nonce( "wpstream_notice_nonce" );
1243 + print '<input type="hidden" id="wpstream_notice_nonce" value="'.esc_html($ajax_nonce).'"/>';
2935 1244
2936 - set_transient( $cache_key, false === $release_date ? 'none' : $release_date, 12 * HOUR_IN_SECONDS );
2937 - return $release_date;
2938 1245 }
2939 -
2940 - /**
2941 - * Adds notice for the WpStream update availability
2942 - * when the update is not older than 30 days
2943 - */
2944 - public function wpstream_plugin_update_available_notice() {
2945 - // Only show to users who can update plugins.
2946 - if (!current_user_can('update_plugins')) {
2947 - return;
2948 - }
2949 -
2950 - // Look up a pending update for this plugin in WP's transient.
2951 - $plugin_slug = 'wpstream/wpstream.php';
2952 - $update_data = get_site_transient('update_plugins');
2953 -
2954 - if ( is_object( $update_data ) &&
2955 - property_exists( $update_data, 'response' ) &&
2956 - is_array($update_data->response) &&
2957 - key_exists($plugin_slug, $update_data->response)
2958 - ) {
2959 - $new_version = $update_data->response[$plugin_slug]->new_version;
2960 -
2961 - // Suppress the notice for very fresh releases (grace period).
2962 - $release_date = $this->get_plugin_release_date( 'wpstream', $new_version );
2963 -
2964 - if ( $release_date ) {
2965 - $days_since_release = ( time() - strtotime( $release_date ) ) / DAY_IN_SECONDS;
2966 -
2967 - // if there's an update newer than 7 days, do not show the notice
2968 - if ( $days_since_release < 7 ) {
2969 - return;
2970 - }
2971 - }
2972 - // Build the one-click update URL (nonce-protected) and print the notice.
2973 - $update_url = wp_nonce_url(
2974 - self_admin_url('update.php?action=upgrade-plugin&plugin=' . urlencode($plugin_slug)),
2975 - 'upgrade-plugin_' . $plugin_slug
2976 - );
2977 -
2978 - echo '<div class="notice notice-warning is-dismissible">';
2979 - echo '<p><strong>' . __('WpStream Plugin Update Available', 'wpstream') . '</strong></p>';
2980 - echo '<p>' . sprintf(
2981 - __('Version %s is available. Please update to the latest version for new features and security improvements.', 'wpstream'),
2982 - '<strong>' . esc_html($new_version) . '</strong>'
2983 - ) . '</p>';
2984 - echo '<p><a href="' . esc_url($update_url) . '" class="button button-primary">' .
2985 - __('Update Now', 'wpstream') . '</a></p>';
2986 - echo '</div>';
2987 - }
2988 - }
2989 -
1246 +
2990 1247 /**
2991 1248 * Admin notices
2992 1249 *
2993 - * AJAX handler that records a dismissed notice: marks the posted notice
2994 - * type as 'yes' in the wpstream_notices option so it stops showing.
2995 - *
2996 - * @since 3.0.1
2997 - * @return void Ends the request with die().
2998 - */
1250 + * @since 3.0.1
1251 + */
2999 1252 public function wpstream_update_cache_notice(){
3000 1253
3001 - // CSRF gate: the dismiss JS sends the nonce printed next to the notice.
3002 - check_ajax_referer( 'wpstream_notice_nonce', 'security' );
3003 - // Dismissals are stored in a site-wide option, so require admin rights.
3004 - if ( ! current_user_can( 'manage_options' ) ) {
3005 - die();
3006 - }
1254 + //check_ajax_referer( 'wpstream_notice_nonce', 'security' );
3007 1255
3008 - // Which notice was dismissed.
3009 1256 $notice_type = esc_html($_POST['notice_type']);
3010 1257 $notices = get_option('wp_stream_notices');
3011 1258
3012 - // Normalise to an array.
3013 1259 if(! is_array($notices) ){
3014 1260 $notices=array();
3015 1261 }
3016 1262
3017 - // Flag this notice as dismissed and persist.
3018 1263 $notices[$notice_type]='yes';
3019 1264
3020 - update_option('wpstream_notices',$notices);
1265 + update_option('wp_stream_notices',$notices);
3021 1266 die();
3022 1267 }
3023 1268
3024 -
3025 1269
3026 1270
3027 1271 /**
3028 1272 * Activate metaboxes for Streaming controls on sidebar
3029 1273 *
3030 - * Register the "Live Streaming" sidebar metabox on free channel posts, and
3031 - * also on WooCommerce products that are live_stream (or a subscription
3032 - * flagged as a live event). Hooked to add_meta_boxes.
3033 - *
3034 - * @since 3.0.1
3035 - * @return void
3036 - */
1274 + * @since 3.0.1
1275 + */
3037 1276 public function wpstream_startstreaming_sidebar_meta() {
3038 - return $this->main->get_live_channel_presentation()->register_metaboxes();
3039 - }
3040 - public function wpstream_start_stream_meta(){
3041 - return $this->main->get_live_channel_presentation()->render_sidebar();
3042 - }
3043 - public function wpstream_is_basic_streaming_mode(){
3044 - return $this->main->quota_manager->is_basic_streaming_mode( null, 'wpstream_is_basic_streaming_mode' );
3045 - }
1277 + global $post;
1278 + $term_list = wp_get_post_terms($post->ID, 'product_type');
1279 + if( get_post_meta($post->ID, 'wpstream_product_type', true)==1 ){
1280 + add_meta_box('wpstream-sidebar-meta', esc_html__('Live Streaming', 'wpstream'), array($this,'wpstream_start_stream_meta'), 'wpstream_product', 'side', 'high');
1281 + }
1282 +
1283 + $is_subscription_live_event = esc_html(get_post_meta($post->ID,'_subscript_live_event',true));
1284 +
3046 1285
3047 - /**
3048 - * Whether the account's plan meters streaming by hours (vs. data).
3049 - *
3050 - * @return bool
3051 - */
3052 - public function wpstream_is_use_streaming_hours() {
3053 - $pack_details = $this->main->quota_manager->get_live_quota_data( 'wpstream_start_channel' );
3054 - return $this->main->quota_manager->uses_streaming_hours( $pack_details );
3055 - }
1286 + if(!is_wp_error( $term_list )){
1287 + if( isset($term_list[0]->name) ){
1288 + if( $term_list[0]->name=='live_stream' || ($term_list[0]->name=='subscription' && $is_subscription_live_event=='yes' ) ){
1289 + add_meta_box('wpstream-sidebar-meta', esc_html__('Live Streaming', 'wpstream'), array($this,'wpstream_start_stream_meta'), 'product', 'side', 'high');
1290 + }
1291 + }
1292 + }
3056 1293
3057 - /**
3058 - * Cached-only flags for start_streaming.js localization (no API on cold cache).
3059 - *
3060 - * @return array{is_basic_streaming: bool, use_streaming_hours: bool}
3061 - */
3062 - public function wpstream_get_start_streaming_localization_flags() {
3063 - return $this->main->get_live_channel_presentation()->get_localization_flags();
3064 - }
3065 -
3066 -
3067 -
3068 - /**
3069 - * Register a hidden dashboard page used as the onboarding wizard endpoint.
3070 - *
3071 - * @return void
3072 - */
3073 - public function add_dashboard_page() {
3074 - $this->main->get_onboarding()->render_part( 'dashboard_page' );
3075 1294 }
3076 -
3077 -
3078 -
3079 -
3080 - /**
3081 - * Bootstrap the full-screen onboarding wizard screen (non-AJAX requests).
3082 - *
3083 - * @return void
3084 - */
3085 - public function wpstream_load_onboarding_wizard() {
3086 - $this->main->get_onboarding()->render_part( 'standalone' );
3087 - }
3088 -
3089 -
3090 - /*
3091 - * Add on boarding to footer
3092 - */
3093 - /**
3094 - * On the onboarding admin page, print the onboarding modal into the footer.
3095 - *
3096 - * @return void
3097 - */
3098 - public function wpstream_admin_footer_onboarding(){
3099 - $this->main->get_onboarding()->render_part( 'page_footer' );
3100 - }
3101 1295
3102 -
3103 - /*
3104 - * On Board Display
1296 + /**
1297 + * Show Streaming controls on sidebar
3105 1298 *
3106 - */
3107 - /**
3108 - * Render the Quick Start landing screen (logo, intro, "Start the Guide"
3109 - * button and the script that opens the onboarding wizard). Echoes HTML.
3110 - *
3111 - * @return void
3112 - */
3113 - public function wpstream_pre_onboard_display(){
3114 - echo $this->main->get_onboarding()->render();
3115 - }
1299 + * @since 3.0.1
1300 + */
1301 + public function wpstream_start_stream_meta(){
1302 + global $live_event_for_user;
1303 + global $post;
1304 +
1305 + $ajax_nonce = wp_create_nonce( "wpstream_start_event_nonce" );
1306 + print '<input type="hidden" id="wpstream_start_event_nonce" value="'.$ajax_nonce.'">';
1307 + $live_event_for_user = $this->main->wpstream_live_connection->api20_wpstream_request_live_stream_for_user();
1308 + $this->wpstream_live_stream_unit($post->ID);
3116 1309
3117 -
3118 -
3119 -
3120 -
3121 -
3122 -
3123 -
3124 - /*
3125 - *
3126 - * On Boarding Content
3127 - *
3128 - */
3129 - /**
3130 - * Emit the full onboarding wizard markup by rendering each step in order
3131 - * (header, account, path choice, live/VOD branches, footer). Echoes HTML.
3132 - *
3133 - * @return void
3134 - */
3135 - public function wpstream_onboard_display() {
3136 - $this->main->get_onboarding()->render_part( 'wizard' );
3137 1310 }
3138 -
3139 -
3140 - /*
3141 - *
3142 - * On Boarding Step 1 - the login/register
3143 - *
3144 - */
3145 - /**
3146 - * Onboarding step 1: WpStream account login/registration UI, including the
3147 - * ALTCHA captcha widget (on HTTPS) and a hidden generated registration
3148 - * password. Emits a marker div when a token already exists. Echoes HTML.
3149 - *
3150 - * @return void
3151 - */
3152 - public function wpstream_onboarding_step1(){
3153 - $this->main->get_onboarding()->render_part( 'account' );
3154 - }
3155 -
3156 - public function randomPassword() {
3157 - return $this->main->get_onboarding()->render_part( 'password' );
3158 - }
3159 -
3160 - public function wpstream_onboarding_step2(){
3161 - $this->main->get_onboarding()->render_part( 'path' );
3162 - }
3163 -
3164 - public function wpstream_onboarding_step3_live_streaming(){
3165 - $this->main->get_onboarding()->render_part( 'live' );
3166 - }
3167 -
3168 - public function wpstream_onboarding_step_3_A_live_streaming_free_view(){
3169 - $this->main->get_onboarding()->render_part( 'live_free' );
3170 - }
3171 -
3172 - public function wpstream_onboarding_step_3_B_live_streaming_pay_per_view(){
3173 - $this->main->get_onboarding()->render_part( 'live_ppv' );
3174 - }
3175 -
3176 - public function wpstream_onboarding_woo_warning(){
3177 - $this->main->get_onboarding()->render_part( 'woo_warning' );
3178 - }
3179 -
3180 - public function wpstream_onboarding_step4_vod(){
3181 - $this->main->get_onboarding()->render_part( 'vod' );
3182 - }
3183 -
3184 - public function wpstream_onboarding_step_4_free_vod(){
3185 - $this->main->get_onboarding()->render_part( 'vod_free' );
3186 - }
3187 -
3188 - public function wpstream_obboarding_file_warning(){
3189 - $this->main->get_onboarding()->render_part( 'file_warning' );
3190 - }
3191 -
3192 - public function wpstream_onboarding_step_4_ppv_vod(){
3193 - $this->main->get_onboarding()->render_part( 'vod_ppv' );
3194 - }
3195 -
3196 - public function onboarding_wizard_header() {
3197 - $this->main->get_onboarding()->render_part( 'wizard_header' );
3198 - }
3199 -
3200 -
3201 -
3202 -
3203 -
3204 - /*
3205 - *
3206 - * On Boarding Footer
3207 - *
3208 - */
3209 - /**
3210 - * Close the onboarding wizard wrapper and print its modal background. Echoes HTML.
3211 - *
3212 - * @return void
3213 - */
3214 - public function onboarding_wizard_footer() {
3215 - $this->main->get_onboarding()->render_part( 'wizard_footer' );
3216 - }
3217 -
3218 -
3219 -
3220 - /*
3221 - *
3222 - * On Boarding create PPV channel
3223 - *
3224 - */
3225 -
3226 -
3227 - /**
3228 - * AJAX: create a Pay-Per-View live channel during onboarding.
3229 - *
3230 - * Inserts a WooCommerce `product`, sets its price, marks it live_stream,
3231 - * and returns the edit link (with onboarding query args) as JSON.
3232 - * Nonce- and administrator-gated.
3233 - *
3234 - * @return void Ends with die().
3235 - */
3236 - public function wpstream_on_board_create_channel_ppv(){
3237 - return $this->main->get_onboarding()->handle_ajax( 'wpstream_on_board_create_channel_ppv' );
3238 - }
3239 -
3240 - /*
3241 - *
3242 - * On Boarding create channel
3243 - *
3244 - */
3245 -
3246 -
3247 - /**
3248 - * AJAX: create a Free-To-View live channel during onboarding.
3249 - *
3250 - * Inserts a `wpstream_product` post and returns its edit link (with
3251 - * onboarding query args) as JSON. Nonce- and administrator-gated.
3252 - *
3253 - * @return void Ends with die().
3254 - */
3255 - public function wpstream_on_board_create_channel(){
3256 - return $this->main->get_onboarding()->handle_ajax( 'wpstream_on_board_create_channel' );
3257 - }
3258 -
3259 - /*
3260 - *
3261 - * On Boarding create free vod
3262 - *
3263 - */
3264 - /**
3265 - * AJAX: create a Free-To-View VOD during onboarding.
3266 - *
3267 - * Inserts a `wpstream_product_vod` post, links the chosen recording, and
3268 - * returns the edit link (with onboarding query args). Nonce/admin gated.
3269 - *
3270 - * @return void Ends with die().
3271 - */
3272 - public function wpstream_on_board_create_free_vod(){
3273 - return $this->main->get_onboarding()->handle_ajax( 'wpstream_on_board_create_free_vod' );
3274 - }
3275 -
3276 -
3277 - /*
3278 - *
3279 - * On Boarding create ppv vod
3280 - *
3281 - */
3282 -
3283 -
3284 - /**
3285 - * AJAX: create a Pay-Per-View VOD during onboarding.
3286 - *
3287 - * Inserts a WooCommerce `product`, sets its price, links the recording,
3288 - * marks it video_on_demand, and returns the edit link (with onboarding
3289 - * query args). Nonce- and administrator-gated.
3290 - *
3291 - * @return void Ends with die().
3292 - */
3293 - public function wpstream_on_board_create_ppv_vod(){
3294 - return $this->main->get_onboarding()->handle_ajax( 'wpstream_on_board_create_ppv_vod' );
3295 - }
3296 -
3297 -
3298 - /*
3299 - *
3300 - * On Boarding login
3301 - *
3302 - */
3303 - /**
3304 - * AJAX: log in to WpStream during onboarding.
3305 - *
3306 - * Stores the submitted credentials, clears the token cache, then attempts
3307 - * to fetch a token; returns success/failure JSON without ever exposing the
3308 - * token to the client. Nonce- and administrator-gated.
3309 - *
3310 - * @return void Ends with die().
3311 - */
3312 - public function wpstream_on_board_login(){
3313 - return $this->main->get_onboarding()->handle_ajax( 'wpstream_on_board_login' );
3314 - }
3315 -
3316 - /**
3317 - * AJAX proxy: fetch a captcha challenge from the baker API and return it.
3318 - * Used on HTTP sites where the Altcha widget cannot run (requires Web Crypto / HTTPS).
3319 - * The JS side solves the PoW locally and sends back the full base64 Altcha payload.
3320 - * Logged-in administrators only: every call proxies an outbound request, so the
3321 - * endpoint is gated like the rest of the onboarding wizard.
3322 - */
3323 - public function wpstream_get_captcha_challenge() {
3324 - return $this->main->get_onboarding()->handle_ajax( 'wpstream_get_captcha_challenge' );
3325 - }
3326 -
3327 - /*
3328 - *
3329 - * On Boarding login
3330 - *
3331 - */
3332 - /**
3333 - * AJAX: register a new WpStream account during onboarding.
3334 - *
3335 - * Validates the email/password, requires an ALTCHA solution, calls the
3336 - * account-create API, and on success stores the credentials and fetches a
3337 - * token. Returns JSON. Nonce- and administrator-gated.
3338 - *
3339 - * @return void Ends with die().
3340 - */
3341 - public function wpstream_on_board_register(){
3342 - return $this->main->get_onboarding()->handle_ajax( 'wpstream_on_board_register' );
3343 - }
3344 -
3345 -
3346 - /*
3347 - *
3348 - * Validate for register
3349 - *
3350 - */
3351 - /**
3352 - * Validate the onboarding registration email + password.
3353 - *
3354 - * Checks for a non-empty, well-formed email whose domain has DNS records,
3355 - * and a password of at least 5 characters.
3356 - *
3357 - * @param string $wpstream_register_email Submitted email.
3358 - * @param string $wpstream_register_password Submitted password.
3359 - * @return array{success: bool, message?: string} Validation result.
3360 - */
3361 - public function wpstream_validate_onboard_register($wpstream_register_email,$wpstream_register_password){
3362 - return $this->main->get_onboarding()->validate_registration( $wpstream_register_email, $wpstream_register_password );
3363 - }
3364 -
3365 - /**
3366 - * Handle the AJAX request to initiate a multipart upload
3367 - *
3368 - * Validates the request, builds a clean filename, and asks the cloud API to
3369 - * open a multipart S3 upload; returns the upload id and per-part pre-signed
3370 - * URLs as JSON. Nonce- and administrator-gated.
3371 - *
3372 - * @since 3.0.1
3373 - * @return void Responds via wp_send_json_*.
3374 - */
3375 - public function handle_initiate_multipart_upload() {
3376 - check_ajax_referer( 'wpstream_multipart_upload_nonce', 'security' );
3377 -
3378 - // Security check - only admins can do this
3379 - if (!current_user_can('administrator')) {
3380 - wp_send_json_error('Unauthorized access');
3381 - return;
3382 - }
3383 -
3384 - // Get file details from request
3385 - $file_name = sanitize_text_field($_POST['file_name']);
3386 - $file_size = intval($_POST['file_size']);
3387 - $content_type = sanitize_text_field($_POST['content_type']);
3388 - $num_parts = intval($_POST['parts']);
3389 -
3390 - // Reject incomplete/invalid file metadata.
3391 - if (empty($file_name) || $file_size <= 0 || $num_parts <= 0) {
3392 - wp_send_json_error('Invalid file information');
3393 - return;
3394 - }
3395 -
3396 - // The part count is caller-supplied: refuse a split the backend can
3397 - // never assemble before spending a cloud call on it.
3398 - if ($num_parts > self::MULTIPART_MAX_PARTS) {
3399 - wp_send_json_error(
3400 - sprintf(
3401 - /* translators: 1: requested part count, 2: maximum part count */
3402 - __('The file would be split into %1$d parts but at most %2$d are allowed. Reload the page to get the current uploader and try again.', 'wpstream'),
3403 - $num_parts,
3404 - self::MULTIPART_MAX_PARTS
3405 - )
3406 - );
3407 - return;
3408 - }
3409 -
3410 - // Prepare a clean filename (similar to the standard upload process)
3411 - // Keep the extension, slugify the base name (spaces -> _, strip non-word chars).
3412 - $file_name_array = explode(".", $file_name);
3413 - $file_extension = $file_name_array[count($file_name_array) - 1];
3414 - $temp_file_name = $file_name_array[0];
3415 - $temp_file_name = str_replace(' ', '_', $temp_file_name);
3416 - $temp_file_name = preg_replace('/\W/', '', $temp_file_name);
3417 - $clean_file_name = $temp_file_name . '.' . $file_extension;
3418 -
3419 - // Make API call to initiate multipart upload; the auth token is
3420 - // injected by the transport (not-connected surfaces as a WP_Error
3421 - // whose message is the legacy "Not connected to WPStream service").
3422 - $api_params = array(
3423 - 'size' => $file_size,
3424 - 'name' => $clean_file_name,
3425 - 'content_type' => $content_type,
3426 - 'parts' => $num_parts
3427 - );
3428 -
3429 - $response_data = $this->main->wpstream_live_connection->authorized_request('video/upload', $api_params);
3430 -
3431 - // Bubble up any API error.
3432 - if (is_wp_error($response_data)) {
3433 - wp_send_json_error($response_data->get_error_message());
3434 - return;
3435 - }
3436 - if (!isset($response_data['success']) || $response_data['success'] !== true) {
3437 - $error_message = isset($response_data['error']) ? $response_data['error'] : 'Failed to initiate multipart upload';
3438 - wp_send_json_error($error_message);
3439 - return;
3440 - }
3441 -
3442 - // Return the upload ID and pre-signed URLs for each part
3443 - wp_send_json_success($response_data);
3444 - }
3445 -
3446 - /**
3447 - * Handle the AJAX request to complete a multipart upload
3448 - *
3449 - * Validates the request and tells the cloud API to finalize the multipart S3
3450 - * upload for the given parts/handle. Nonce- and administrator-gated.
3451 - *
3452 - * @since 3.0.1
3453 - * @return void Responds via wp_send_json_*.
3454 - */
3455 - public function handle_complete_multipart_upload() {
3456 - check_ajax_referer( 'wpstream_multipart_upload_nonce', 'security' );
3457 -
3458 - // Security check - only admins can do this
3459 - if (!current_user_can('administrator')) {
3460 - wp_send_json_error('Unauthorized access');
3461 - return;
3462 - }
3463 -
3464 - // Get completion details. The client sends 'parts' as the plain part
3465 - // COUNT (not a part list), so parse it as a positive integer.
3466 - $parts = isset($_POST['parts']) ? absint(wp_unslash($_POST['parts'])) : 0;
3467 - $file_name = sanitize_text_field($_POST['file_name']);
3468 - $handle = sanitize_text_field($_POST['handle']);
3469 -
3470 - // Reject incomplete completion metadata: no parts uploaded or no file name.
3471 - if ($parts < 1 || empty($file_name)) {
3472 - wp_send_json_error('Invalid completion information');
3473 - return;
3474 - }
3475 -
3476 - // Send the finalize ("complete") request with the uploaded part list;
3477 - // the auth token is injected by the transport (not-connected surfaces
3478 - // as a WP_Error whose message is the legacy "Not connected to WPStream
3479 - // service").
3480 - $api_params = array(
3481 - 'parts' => $parts,
3482 - 'name' => $file_name,
3483 - 'handle' => $handle,
3484 - 'action' => 'complete'
3485 - );
3486 -
3487 - $response_data = $this->main->wpstream_live_connection->authorized_request('video/upload', $api_params);
3488 -
3489 - // Bubble up any API error.
3490 - if (is_wp_error($response_data)) {
3491 - wp_send_json_error($response_data->get_error_message());
3492 - return;
3493 - }
3494 - if (!isset($response_data['success']) || $response_data['success'] !== true) {
3495 - $error_message = isset($response_data['error']) ? $response_data['error'] : 'Failed to complete multipart upload';
3496 - wp_send_json_error($error_message);
3497 - return;
3498 - }
3499 -
3500 - /**
3501 - * Fires after the cloud accepted a completed multipart VOD upload.
3502 - *
3503 - * @since 4.14.0
3504 - *
3505 - * @param string $file_name Uploaded file name.
3506 - * @param string $handle Upload handle issued at initiation.
3507 - * @param int $parts Number of parts uploaded.
3508 - */
3509 - do_action( 'wpstream_multipart_upload_completed', $file_name, $handle, $parts );
3510 -
3511 - // Return success
3512 - wp_send_json_success();
3513 - }
3514 -
3515 - /**
3516 - * AJAX: update the WpStream plugin in place from the Settings/Support tab.
3517 - *
3518 - * Loads the upgrader APIs, initializes WP_Filesystem, runs the plugin
3519 - * upgrade, re-activates the plugin, and returns success/error JSON.
3520 - * Requires the update_plugins capability.
3521 - *
3522 - * @return void Responds via wp_send_json_*.
3523 - */
3524 - public function wpstream_settings_tab_update_plugin() {
3525 - // CSRF gate: the update button JS sends the settings-page nonce as 'security'.
3526 - check_ajax_referer( 'wpstream-settings-nonce', 'security' );
3527 - // Capability gate.
3528 - if ( !current_user_can( 'update_plugins' ) ) {
3529 - wp_send_json_error( __( 'Not enough permissions to make this change', 'wpstream' ) );
3530 - }
3531 -
3532 - // Load the core upgrade/filesystem APIs.
3533 - include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
3534 - include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
3535 - include_once ABSPATH . 'wp-admin/includes/file.php';
3536 -
3537 - // Initialize the filesystem abstraction (needed to write plugin files).
3538 - $credentials = request_filesystem_credentials('');
3539 - if ( !WP_Filesystem( $credentials ) ) {
3540 - wp_send_json_error( __( 'Failed to connect to the filesystem', 'wpstream' ) );
3541 - }
3542 -
3543 - // Run the in-place upgrade for this plugin, then re-activate it.
3544 - $upgrader = new Plugin_Upgrader(new Automatic_Upgrader_Skin());
3545 - $plugin_path = plugin_basename( WPSTREAM_PLUGIN_PATH . 'wpstream.php' );
3546 - $result = $upgrader->upgrade( $plugin_path );
3547 - activate_plugin( $plugin_path );
3548 -
3549 - if ( is_wp_error( $result ) ) {
3550 - wp_send_json_error( __( 'Update failed due to', 'wpstream' ) . $result->get_error_message() );
3551 - }
3552 -
3553 - wp_send_json_success();
3554 - }
3555 -}
1311 +}