PluginProbe
WpStream – Live Streaming, Video on Demand, Pay Per View / 4.14.1
WpStream – Live Streaming, Video on Demand, Pay Per View v4.14.1
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
wpstream / hello-wpstream / framework / dashboard-functions.php

dashboard-functions.php in WpStream – Live Streaming, Video on Demand, Pay Per View 4.14.1, at hello-wpstream/framework/dashboard-functions.php

706 lines 33.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Dashboard functions
5 *
6 * Helpers that build the front-end broadcaster/dashboard UI for the
7 * hello-wpstream theme: a select of the user's own channels, the plupload
8 * localization for image/video uploaders, and the HTML markup for the image,
9 * profile-image, trailer and preview upload widgets plus the gallery grid.
10 *
11 * @package wpstream-plugin
12 */
13
14
15 // Exit if accessed directly.
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit;
18 }
19
20 if ( ! function_exists( 'wpstream_theme_return_user_channel_list' ) ) {
21 /**
22 * Return user channel list.
23 *
24 * This function generates and returns HTML markup for a select field containing user channels.
25 *
26 * @param int $already_selected The ID of the already selected channel.
27 * @param string $return_type The type of return. Default is empty.
28 * @return string|int The HTML markup for the select field or the count of found posts.
29 */
30 function wpstream_theme_return_user_channel_list( $already_selected, $return_type = '' ) {
31 // Current user whose channels we are listing.
32 $current_user = wp_get_current_user();
33 // Cap the query at 100 channels.
34 $limit = 100;
35 $return_string = '';
36
37 // When WooCommerce is active, channels can be either WC products of
38 // type live_stream or plain wpstream_product posts.
39 if ( class_exists( 'WooCommerce' ) ) {
40 $post_type = array( 'product', 'wpstream_product' );
41 // Match live_stream products OR posts with no product_type term.
42 $taxonomy_array = array(
43 'relation' => 'OR',
44 array(
45 'taxonomy' => 'product_type',
46 'field' => 'slug',
47 'terms' => array( 'live_stream' ),
48 ),
49 array(
50 'taxonomy' => 'product_type',
51 'operator' => 'NOT EXISTS',
52 ),
53 );
54 } else {
55 // Without WooCommerce, only the custom post type applies.
56 $post_type = 'wpstream_product';
57 $taxonomy_array = array();
58 }
59
60 // Query the current user's channels.
61 $args = array(
62 'post_type' => $post_type,
63 'posts_per_page' => $limit,
64 'tax_query' => $taxonomy_array, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
65 'author' => $current_user->ID,
66 );
67
68 $query_list = new WP_Query( $args );
69
70 // Caller only wants the count of channels found: return it early.
71 if ( 'found_posts' === $return_type ) {
72 return intval( $query_list->found_posts );
73 }
74
75 // Open the select element with a placeholder first option.
76 $return_string .= '<select id="wpstream-user-channel-selection" name="wpstream-user-channel-selection" '
77 . 'class="wpstream-user-channel-selection">'
78 . '<option value="">' . esc_html__( 'select the channel', 'hello-wpstream' ) . '</option>';
79
80 if ( $query_list->have_posts() ) {
81 // Emit one <option> per channel in the loop.
82 while ( $query_list->have_posts() ) :
83 $query_list->the_post();
84 // Gather the channel's ID, title and post type.
85 $the_id = get_the_ID();
86 $the_title = get_the_title( $the_id );
87 $post_type = get_post_type( $the_id );
88 $product_type = '';
89
90 // For WC products, read the stored product type meta.
91 if ( get_post_type( $the_id ) === 'product' ) {
92 $product_type = get_post_meta( $the_id, '_product_type', true );
93 }
94
95 // Build the option, marking it selected when it matches.
96 $image_url = '';
97 $return_string .= '<option value="' . intval( $the_id ) . '"';
98 if ( intval( $already_selected ) === $the_id ) {
99 $return_string .= ' selected';
100 }
101 // Attach the (currently empty) thumbnail data attribute and label.
102 $return_string .= ' data-thumbnail="' . esc_url( $image_url ) . '">';
103 $return_string .= esc_html( $the_title ) . '</option>';
104
105 endwhile;
106 }
107
108 // Close the select element.
109 $return_string .= '</select>';
110
111 // Restore the global post after the custom query loop.
112 wp_reset_postdata();
113
114 // Return the assembled <select> markup.
115 return $return_string;
116 }
117 }
118
119 if ( ! function_exists( 'wpstream_max_gallery_images' ) ) {
120 /**
121 * Maximum number of images a channel or video gallery may hold.
122 *
123 * Read by the uploader config (client-side warning) and enforced by the
124 * dashboard save handlers, so the two can never disagree.
125 *
126 * @return int Limit in [1, 100].
127 */
128 function wpstream_max_gallery_images() {
129 /**
130 * Filter the maximum number of gallery images. Clamped to [1, 100].
131 *
132 * @since 4.14.0
133 *
134 * @param int $max Limit; default 20.
135 */
136 return wpstream_tunable( 'wpstream_max_gallery_images', 20, 100 );
137 }
138 }
139
140 if ( ! function_exists( 'wpstream_limit_gallery_images' ) ) {
141 /**
142 * Trim a submitted comma list of attachment ids to the gallery limit.
143 *
144 * @param string $images Comma-separated attachment ids as submitted.
145 * @return string The first N ids, N = wpstream_max_gallery_images().
146 */
147 function wpstream_limit_gallery_images( $images ) {
148 // Keep positive attachment ids only, in submitted order, then the first N.
149 $ids = array_values( array_filter( array_map( 'absint', explode( ',', (string) $images ) ) ) );
150 return implode( ',', array_slice( $ids, 0, wpstream_max_gallery_images() ) );
151 }
152 }
153
154 if ( ! function_exists( 'wpstream_theme_localize_upload_script_images' ) ) :
155 /**
156 * Localize the upload script for images.
157 *
158 * @param string $button_upload The ID of the upload button.
159 */
160 function wpstream_theme_localize_upload_script_images( $button_upload ) {
161 // Build the admin-ajax upload endpoint URL, embedding the upload nonce.
162 $plup_url = add_query_arg(
163 array(
164 'action' => 'wpstream_me_upload',
165 'nonce' => wp_create_nonce( 'aaiu_allow' ),
166 ),
167 esc_url( admin_url( 'admin-ajax.php' ) )
168 );
169
170 // Maximum accepted upload size: the same cap the server enforces.
171 $max_file_size = wpstream_max_upload_bytes( $button_upload );
172
173 // Core plupload configuration passed to the browser uploader.
174 $plupload_values = array(
175 'runtimes' => 'html5,flash,html4',
176 'max_file_size' => $max_file_size . 'b',
177 'url' => $plup_url,
178 'file_data_name' => 'aaiu_upload_file',
179 'flash_swf_url' => includes_url( 'js/plupload/plupload.flash.swf' ),
180 // Client-side allowed file extensions (not a server-side guarantee).
181 'filters' => array(
182 array(
183 'title' => esc_html__( 'Allowed Files', 'hello-wpstream' ),
184 'extensions' => 'jpeg,jpg,gif,png,pdf,webp,mp4,mov',
185 ),
186 ),
187 'multipart' => true,
188 'urlstream_upload' => true,
189 // Pass the originating button id so the server knows which uploader fired.
190 'multipart_params' => array( 'button_id' => $button_upload ),
191 );
192
193 // UI wiring: which element opens the file picker and which is the container.
194 $tmp_plupload_values = array(
195 'browse_button' => $button_upload,
196 'container' => 'aaiu-upload-container',
197 );
198
199 // Merge the UI wiring into the config and set the drag-and-drop target.
200 $plupload_values = wp_parse_args( $plupload_values, $tmp_plupload_values );
201 $plupload_values['drop_element'] = 'drag-and-drop';
202 // Upper bound on the number of images allowed in the gallery uploader.
203 $max_images = wpstream_max_gallery_images();
204
205 // Expose all upload settings/nonces/strings to the ajax-upload JS handle.
206 wp_localize_script(
207 'ajax-upload',
208 'ajax_vars',
209 array(
210 'ajaxurl' => esc_url( admin_url( 'admin-ajax.php' ) ),
211 'nonce' => wp_create_nonce( 'aaiu_upload' ),
212 'remove' => wp_create_nonce( 'aaiu_remove' ),
213 'number' => 1,
214 'upload_enabled' => true,
215 'warning' => __( 'Image needs to be at least 500px height x 500px wide!', 'hello-wpstream' ),
216 'max_images' => $max_images,
217 'warning_max' => __( 'You cannot upload more than', 'hello-wpstream' ) . ' ' . $max_images . ' ' . __( 'images', 'hello-wpstream' ),
218 'path' => trailingslashit( WPSTREAM_PLUGIN_PATH ),
219 'confirmMsg' => esc_html__( 'Are you sure you want to delete this?', 'hello-wpstream' ),
220 'plupload' => $plupload_values,
221 )
222 );
223 }
224 endif;
225
226 if ( ! function_exists( 'wpstream_theme_return_image_upload_markup' ) ) {
227 /**
228 * Return the markup for image upload.
229 *
230 * This function generates and returns the markup for image upload based on the provided post ID.
231 *
232 * @param int $user_id The ID of the user.
233 * @param int $post_id The ID of the post.
234 * @return string The HTML markup for image upload.
235 */
236 function wpstream_theme_return_image_upload_markup( $user_id, $post_id ) {
237 // Determine the post type to know where the gallery data lives.
238 $post_type = get_post_type( $post_id );
239 $gallery_images = '';
240
241 if ( 'product' === $post_type ) {
242 // WooCommerce products store gallery IDs as a comma list in meta.
243 $gallery_images_source = get_post_meta( $post_id, '_product_image_gallery', true );
244 $gallery_images = explode( ',', $gallery_images_source );
245 } else {
246 // Other post types use the Meta Box (rwmb) gallery field, if present.
247 if(function_exists('rwmb_meta')){
248 $gallery_images = rwmb_meta( 'wpstream_theme_gallery', array(), $post_id );
249
250 // rwmb returns id => data; reduce to the list of IDs.
251 if ( is_array( $gallery_images ) ) {
252 $gallery_images = array_keys( $gallery_images );
253 }
254 }
255 }
256
257 // Canonical gallery source: overrides whatever was gathered above.
258 $gallery_images = wpstream_theme_return_image_gallery( $post_id );
259
260 // Featured image ID and accumulator for the rendered thumbnails.
261 $thumbid = get_post_thumbnail_id( $post_id );
262 $images = '';
263
264 if ( is_array( $gallery_images ) ) {
265 // Build a thumbnail tile (with delete icon) for each gallery image.
266 foreach ( $gallery_images as $attachment_id ) {
267
268 // Resolve the card-sized preview for this attachment.
269 $preview = wp_get_attachment_image_src( $attachment_id, 'wpstream_featured_unit_cards' );
270
271 // Only render tiles that have a usable preview URL.
272 if ( $preview && $preview[0] != '' ) {
273 $images .= '<div class="wpstream_uploaded_images" data-imageid="' . esc_attr( $attachment_id ) . '">'
274 . '<img src="' . esc_url( $preview[0] ) . '" alt="' . esc_html__( 'thumb', 'hello-wpstream' ) . '" />'
275 . '<i class="far fa-trash-alt wpstream_delete_image"></i>';
276
277 $images .= '</div>';
278 }
279 }
280 }
281
282 // Flatten the gallery IDs into a comma string for the hidden field.
283 $gallery_images_string = '';
284 if ( is_array( $gallery_images ) ) {
285 $gallery_images_string = implode( ',', $gallery_images );
286 }
287
288 // Capture the template output into a string via output buffering.
289 $return_string = '';
290 ob_start();
291 ?>
292
293 <!-- Multi-image uploader (gallery) container -->
294 <div id="upload-container" class="upload-container">
295 <div id="aaiu-upload-container upload-container__body">
296 <h3><?php echo esc_html__( 'Image list', 'hello-wpstream' ); ?></h3>
297 <!-- Live list plupload appends queued items into -->
298 <div id="aaiu-upload-imagelist">
299 <ul id="aaiu-ul-list" class="aaiu-upload-list"></ul>
300 </div>
301
302 <!-- Existing uploaded images plus the upload nonce field -->
303 <div id="wpstream_imagelist">
304 <?php
305 // Nonce consumed by the delete-image AJAX handler.
306 $ajax_nonce = wp_create_nonce( 'wpstream_image_upload' );
307 print '<input type="hidden" id="wpstream_image_upload" value="' . esc_html( $ajax_nonce ) . '" /> ';
308 // Print the pre-rendered thumbnail tiles, if any.
309 if ( '' !== $images ) {
310 print trim( $images ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
311 }
312 ?>
313 </div>
314
315 <!-- Actions row: drag-and-drop trigger and hidden state fields -->
316 <div class="upload-container__actions-wrap upload-container__actions-wrap--row">
317 <div id="drag-and-drop" class="rh_drag_and_drop_wrapper ">
318 <!-- "Upload More" button plupload binds to -->
319 <div id="aaiu-uploader" class="wpstream_theme_button_dashboard">
320 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
321 <path fill-rule="evenodd" clip-rule="evenodd" d="M10.7379 16.6273C9.96427 16.6273 9.31895 16.036 9.2514 15.2654C9.11015 13.6541 9.07441 12.0356 9.14427 10.4203C9.05994 10.4147 8.97563 10.4088 8.89133 10.4026L7.40178 10.2941C6.44973 10.2247 5.91752 9.16309 6.43151 8.35871C7.5277 6.6432 9.53693 4.72314 11.1904 3.53541C11.6742 3.18786 12.3258 3.18786 12.8097 3.53541C14.4631 4.72314 16.4723 6.64319 17.5685 8.35871C18.0825 9.16309 17.5503 10.2247 16.5983 10.2941L15.1087 10.4026C15.0244 10.4088 14.9401 10.4147 14.8558 10.4203C14.9256 12.0356 14.8899 13.6541 14.7486 15.2654C14.6811 16.036 14.0358 16.6273 13.2622 16.6273H10.7379ZM10.6815 9.76253C10.5678 11.5498 10.589 13.3431 10.745 15.1273H13.255C13.411 13.3431 13.4323 11.5498 13.3186 9.76253C13.3058 9.56216 13.3739 9.36505 13.5077 9.21531C13.6414 9.06556 13.8296 8.9757 14.0301 8.96582C14.3535 8.94989 14.6767 8.93015 14.9997 8.90661L16.0815 8.82775C15.1219 7.41445 13.9204 6.1802 12.5313 5.18235L12 4.80071L11.4687 5.18235C10.0796 6.1802 8.87813 7.41446 7.91858 8.82775L9.00038 8.90661C9.32337 8.93015 9.64656 8.94989 9.9699 8.96582C10.1704 8.9757 10.3586 9.06556 10.4924 9.21531C10.6261 9.36505 10.6942 9.56216 10.6815 9.76253Z" fill="#0F0F0F"/>
322 <path d="M5.75 17C5.75 16.5858 5.41421 16.25 5 16.25C4.58579 16.25 4.25 16.5858 4.25 17V19C4.25 19.9665 5.0335 20.75 6 20.75H18C18.9665 20.75 19.75 19.9665 19.75 19V17C19.75 16.5858 19.4142 16.25 19 16.25C18.5858 16.25 18.25 16.5858 18.25 17V19C18.25 19.1381 18.1381 19.25 18 19.25H6C5.86193 19.25 5.75 19.1381 5.75 19V17Z" fill="#0F0F0F"/>
323 </svg>
324
325 <?php esc_html_e( 'Upload More', 'hello-wpstream' ); ?>
326 </div>
327 </div>
328
329 <!-- Hidden fields carrying the current gallery IDs and thumbnail ID -->
330 <input type="hidden" name="attachid" id="attachid" value="<?php print esc_html( $gallery_images_string ); ?>">
331 <input type="hidden" name="attachthumb" id="attachthumb" value="<?php print esc_html( $thumbid ); ?>">
332 <p class="full_form full_form_image">
333 <?php
334 // Guidance text on recommended image dimensions/format.
335 esc_html_e(
336 "It's recommended to use a picture that's at least 1280 x 720 pixels and 4MB or less.
337 Use a PNG or GIF (no animations) file.",
338 'hello-wpstream'
339 );
340 ?>
341 </p>
342 </div>
343 </div>
344 </div>
345 <?php
346 // Flush the buffered template into the return string and clean up.
347 $return_string .= ob_get_contents();
348 ob_end_clean();
349
350 // Return the complete uploader markup.
351 return $return_string;
352 }
353 }
354
355 if ( ! function_exists( 'wpstream_theme_return_image_upload_markup_single' ) ) {
356 /**
357 * Return the markup for single image upload.
358 *
359 * This function generates and returns the markup for single image upload based on the provided post ID.
360 *
361 * @param int $user_id The ID of the user.
362 * @param int $post_id The ID of the post.
363 * @return string The HTML markup for single image upload.
364 */
365 function wpstream_theme_return_image_upload_markup_single( $post_id ) {
366 // Current featured image (thumbnail) ID for this post.
367 $thumbid = get_post_thumbnail_id( $post_id );
368 $images = '';
369 // Card-sized preview of the existing thumbnail, if any.
370 $preview = wp_get_attachment_image_src( $thumbid, 'wpstream_featured_unit_cards' );
371
372 // Render the existing thumbnail tile when a valid preview URL exists.
373 if ( isset( $preview[0] ) && '' !== $preview[0] ) {
374 $images .= '<div class="wpstream_uploaded_images" id="wpstream_uploaded_profile_image" data-imageid="' . esc_attr( $thumbid ) . '">'
375 . '<img src="' . esc_url( $preview[0] ) . '" alt="' . esc_html__( 'thumb', 'hello-wpstream' ) . '" /></div>';
376 }
377
378 // Buffer the template markup into a return string.
379 $return_string = '';
380 ob_start();
381 ?>
382
383 <!-- Single-image (channel thumbnail) uploader container -->
384 <div id="upload-container">
385 <div id="aaiu-upload-container" class="upload-container">
386 <p class="upload-container__title"><?php echo esc_html__( 'Channel Thumbnail', 'hello-wpstream' ); ?></p>
387 <div class="upload-container__row">
388 <div class="upload-container__image-wrap">
389 <!-- Live list plupload appends queued items into -->
390 <div id="aaiu-upload-imagelist_single">
391 <ul id="aaiu-ul-list" class="aaiu-upload-list"></ul>
392 </div>
393
394 <!-- Existing thumbnail plus the upload nonce field -->
395 <div id="wpstream_imagelist_single">
396 <?php
397 // Nonce for the image delete/upload AJAX handler.
398 $ajax_nonce = wp_create_nonce( 'wpstream_image_upload' );
399 print '<input type="hidden" id="wpstream_image_upload" value="' . esc_html( $ajax_nonce ) . '" /> ';
400 // Print the current thumbnail tile, if present.
401 if ( '' !== $images ) {
402 print trim( $images ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
403 }
404 ?>
405 </div>
406
407 <!-- Hidden field carrying the current thumbnail ID -->
408 <input type="hidden" name="attachthumb" id="attachthumb" value="<?php print esc_html( $thumbid ); ?>">
409 </div>
410
411 <div class="upload-container__actions-wrap upload-container__actions-wrap--column">
412 <p class="full_form full_form_image">
413 <?php
414 esc_html_e(
415 "It's recommended to use a picture that's at least 1280 x 720 pixels and 4MB or less.
416 Use a PNG or GIF (no animations) file.",
417 'hello-wpstream'
418 );
419 ?>
420 </p>
421
422 <!-- Drag-and-drop target / "Change" button for the thumbnail -->
423 <div id="drag-and-drop" class="rh_drag_and_drop_wrapper">
424 <div id="aaiu-uploader-single" class="wpstream_theme_button_dashboard">
425 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
426 <path fill-rule="evenodd" clip-rule="evenodd" d="M10.7379 16.6273C9.96427 16.6273 9.31895 16.036 9.2514 15.2654C9.11015 13.6541 9.07441 12.0356 9.14427 10.4203C9.05994 10.4147 8.97563 10.4088 8.89133 10.4026L7.40178 10.2941C6.44973 10.2247 5.91752 9.16309 6.43151 8.35871C7.5277 6.6432 9.53693 4.72314 11.1904 3.53541C11.6742 3.18786 12.3258 3.18786 12.8097 3.53541C14.4631 4.72314 16.4723 6.64319 17.5685 8.35871C18.0825 9.16309 17.5503 10.2247 16.5983 10.2941L15.1087 10.4026C15.0244 10.4088 14.9401 10.4147 14.8558 10.4203C14.9256 12.0356 14.8899 13.6541 14.7486 15.2654C14.6811 16.036 14.0358 16.6273 13.2622 16.6273H10.7379ZM10.6815 9.76253C10.5678 11.5498 10.589 13.3431 10.745 15.1273H13.255C13.411 13.3431 13.4323 11.5498 13.3186 9.76253C13.3058 9.56216 13.3739 9.36505 13.5077 9.21531C13.6414 9.06556 13.8296 8.9757 14.0301 8.96582C14.3535 8.94989 14.6767 8.93015 14.9997 8.90661L16.0815 8.82775C15.1219 7.41445 13.9204 6.1802 12.5313 5.18235L12 4.80071L11.4687 5.18235C10.0796 6.1802 8.87813 7.41446 7.91858 8.82775L9.00038 8.90661C9.32337 8.93015 9.64656 8.94989 9.9699 8.96582C10.1704 8.9757 10.3586 9.06556 10.4924 9.21531C10.6261 9.36505 10.6942 9.56216 10.6815 9.76253Z" fill="#0F0F0F"/>
427 <path d="M5.75 17C5.75 16.5858 5.41421 16.25 5 16.25C4.58579 16.25 4.25 16.5858 4.25 17V19C4.25 19.9665 5.0335 20.75 6 20.75H18C18.9665 20.75 19.75 19.9665 19.75 19V17C19.75 16.5858 19.4142 16.25 19 16.25C18.5858 16.25 18.25 16.5858 18.25 17V19C18.25 19.1381 18.1381 19.25 18 19.25H6C5.86193 19.25 5.75 19.1381 5.75 19V17Z" fill="#0F0F0F"/>
428 </svg>
429
430 <?php esc_html_e( 'Change', 'hello-wpstream' ); ?>
431 </div>
432 </div>
433 </div>
434 </div>
435 </div>
436 </div>
437 <?php
438 // Capture the buffered markup and return it.
439 $return_string .= ob_get_contents();
440 ob_end_clean();
441
442 return $return_string;
443 }
444 }
445
446 if ( ! function_exists( 'wpstream_theme_return_trailer_upload_markup' ) ) {
447 /**
448 * Return the markup for trailer video upload.
449 *
450 * This function generates and returns the markup for trailer video upload based on the provided post ID.
451 *
452 * @param int $post_id The ID of the post.
453 * @return string The HTML markup for trailer video upload.
454 */
455 function wpstream_theme_return_trailer_upload_markup( $post_id ) {
456 // Attachment ID of the stored trailer video (from post meta).
457 $trailer_id = get_post_meta( $post_id, 'video_trailer', true );
458 // Public URL of that trailer attachment, if it exists.
459 $attachment_url = wp_get_attachment_url( $trailer_id );
460 $video_html = '';
461
462 // Pre-render an inline <video> preview when a trailer is present.
463 if ( $trailer_id && $attachment_url ) {
464 $video_html = '<div class="wpstream_uplod_video" id="wpstream_uploaded_trailer" data-videoid="' . esc_attr( $trailer_id ) . '">'
465 .'<video height="240" controls><source src="' . esc_url( $attachment_url ) . '" type="video/mp4"></video></div>';
466 }
467
468 // Buffer the template markup into a return string.
469 $return_string = '';
470 ob_start();
471 ?>
472
473 <!-- Video trailer uploader container -->
474 <div id="trailer-upload-container">
475 <div id="aaiu-trailer-container" class="upload-container">
476 <p class="upload-container__title"><?php echo esc_html__( 'Video Trailer', 'hello-wpstream' ); ?></p>
477 <div class="upload-container__row">
478 <div class="upload-container__video-wrap">
479 <!-- Live list plupload appends queued items into -->
480 <div id="aaiu-upload-trailer">
481 <ul id="aaiu-ul-list" class="aaiu-upload-list"></ul>
482 </div>
483
484 <!-- Existing trailer preview plus the upload nonce field -->
485 <div id="wpstream_trailerlist">
486 <?php
487 // Nonce for the trailer upload AJAX handler.
488 $ajax_nonce = wp_create_nonce( 'wpstream_trailer_upload' );
489 print '<input type="hidden" id="wpstream_trailer_upload" value="' . esc_html( $ajax_nonce ) . '" /> ';
490 // Print the pre-rendered trailer <video> preview, if any.
491 if ( '' !== $video_html ) {
492 print trim( $video_html ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
493 }
494 ?>
495 </div>
496
497 <input type="hidden" id="upload_trailer" class="wpstream_upload_file" />
498 </div>
499
500 <div class="upload-container__actions-wrap upload-container__actions-wrap--column">
501 <p class="full_form full_form_trailer">
502 <?php
503 esc_html_e( "Upload a video trailer for your content. Supported formats: MP4, MOV", 'hello-wpstream' );
504 ?>
505 </p>
506
507 <!-- Drag-and-drop target / "Change" button for the trailer -->
508 <div id="drag-and-drop" class="rh_drag_and_drop_wrapper">
509 <div id="aaiu-uploader-trailer" class="wpstream_theme_button_dashboard">
510 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
511 <path fill-rule="evenodd" clip-rule="evenodd" d="M10.7379 16.6273C9.96427 16.6273 9.31895 16.036 9.2514 15.2654C9.11015 13.6541 9.07441 12.0356 9.14427 10.4203C9.05994 10.4147 8.97563 10.4088 8.89133 10.4026L7.40178 10.2941C6.44973 10.2247 5.91752 9.16309 6.43151 8.35871C7.5277 6.6432 9.53693 4.72314 11.1904 3.53541C11.6742 3.18786 12.3258 3.18786 12.8097 3.53541C14.4631 4.72314 16.4723 6.64319 17.5685 8.35871C18.0825 9.16309 17.5503 10.2247 16.5983 10.2941L15.1087 10.4026C15.0244 10.4088 14.9401 10.4147 14.8558 10.4203C14.9256 12.0356 14.8899 13.6541 14.7486 15.2654C14.6811 16.036 14.0358 16.6273 13.2622 16.6273H10.7379ZM10.6815 9.76253C10.5678 11.5498 10.589 13.3431 10.745 15.1273H13.255C13.411 13.3431 13.4323 11.5498 13.3186 9.76253C13.3058 9.56216 13.3739 9.36505 13.5077 9.21531C13.6414 9.06556 13.8296 8.9757 14.0301 8.96582C14.3535 8.94989 14.6767 8.93015 14.9997 8.90661L16.0815 8.82775C15.1219 7.41445 13.9204 6.1802 12.5313 5.18235L12 4.80071L11.4687 5.18235C10.0796 6.1802 8.87813 7.41446 7.91858 8.82775L9.00038 8.90661C9.32337 8.93015 9.64656 8.94989 9.9699 8.96582C10.1704 8.9757 10.3586 9.06556 10.4924 9.21531C10.6261 9.36505 10.6942 9.56216 10.6815 9.76253Z" fill="#0F0F0F"/>
512 <path d="M5.75 17C5.75 16.5858 5.41421 16.25 5 16.25C4.58579 16.25 4.25 16.5858 4.25 17V19C4.25 19.9665 5.0335 20.75 6 20.75H18C18.9665 20.75 19.75 19.9665 19.75 19V17C19.75 16.5858 19.4142 16.25 19 16.25C18.5858 16.25 18.25 16.5858 18.25 17V19C18.25 19.1381 18.1381 19.25 18 19.25H6C5.86193 19.25 5.75 19.1381 5.75 19V17Z" fill="#0F0F0F"/>
513 </svg>
514
515 <?php esc_html_e( 'Change', 'hello-wpstream' ); ?>
516 </div>
517 </div>
518 </div>
519 </div>
520 </div>
521 </div>
522 <?php
523 // Capture the buffered markup and return it.
524 $return_string .= ob_get_contents();
525 ob_end_clean();
526
527 return $return_string;
528 }
529 }
530
531 if ( ! function_exists( 'wpstream_theme_return_preview_upload_markup' ) ) {
532 /**
533 * Return the markup for preview video upload.
534 *
535 * This function generates and returns the markup for preview video upload based on the provided post ID.
536 *
537 * @param int $post_id The ID of the post.
538 * @return string The HTML markup for preview video upload.
539 */
540 function wpstream_theme_return_preview_upload_markup( $post_id ) {
541 // Attachment ID of the stored preview video (from post meta).
542 $preview_id = get_post_meta( $post_id, 'video_preview', true );
543 // Public URL of that preview attachment, if it exists.
544 $attachment_url = wp_get_attachment_url( $preview_id );
545 $video_html = '';
546
547 // Pre-render an inline <video> preview when a preview clip is present.
548 if ( $preview_id && $attachment_url ) {
549 $video_html = '<div class="wpstream_uplod_video" id="wpstream_uploaded_preview" data-videoid="' . esc_attr( $preview_id ) . '">'
550 .'<video height="240" controls><source src="' . esc_url( $attachment_url ) . '" type="video/mp4"></video></div>';
551 }
552
553 // Buffer the template markup into a return string.
554 $return_string = '';
555 ob_start();
556 ?>
557
558 <!-- Video preview uploader container -->
559 <div id="preview-upload-container">
560 <div id="aaiu-preview-container" class="upload-container">
561 <p class="upload-container__title"><?php echo esc_html__( 'Video Preview', 'hello-wpstream' ); ?></p>
562 <div class="upload-container__row">
563 <div class="upload-container__video-wrap">
564 <!-- Live list plupload appends queued items into -->
565 <div id="aaiu-upload-preview">
566 <ul id="aaiu-ul-list" class="aaiu-upload-list"></ul>
567 </div>
568
569 <!-- Existing preview clip plus the upload nonce field -->
570 <div id="wpstream_previewlist">
571 <?php
572 // Nonce for the preview upload AJAX handler.
573 $ajax_nonce = wp_create_nonce( 'wpstream_preview_upload' );
574 print '<input type="hidden" id="wpstream_preview_upload" value="' . esc_html( $ajax_nonce ) . '" /> ';
575 // Print the pre-rendered preview <video>, if any.
576 if ( '' !== $video_html ) {
577 print trim( $video_html ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
578 }
579 ?>
580 </div>
581
582 <input type="hidden" id="upload_preview" class="wpstream_upload_file" />
583 </div>
584
585 <div class="upload-container__actions-wrap upload-container__actions-wrap--column">
586 <p class="full_form full_form_trailer">
587 <?php
588 esc_html_e( "Upload a video preview for your content. Supported formats: MP4, MOV", 'hello-wpstream' );
589 ?>
590 </p>
591
592 <!-- Drag-and-drop target / "Change" button for the preview -->
593 <div id="drag-and-drop" class="rh_drag_and_drop_wrapper">
594 <div id="aaiu-uploader-preview" class="wpstream_theme_button_dashboard">
595 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
596 <path fill-rule="evenodd" clip-rule="evenodd" d="M10.7379 16.6273C9.96427 16.6273 9.31895 16.036 9.2514 15.2654C9.11015 13.6541 9.07441 12.0356 9.14427 10.4203C9.05994 10.4147 8.97563 10.4088 8.89133 10.4026L7.40178 10.2941C6.44973 10.2247 5.91752 9.16309 6.43151 8.35871C7.5277 6.6432 9.53693 4.72314 11.1904 3.53541C11.6742 3.18786 12.3258 3.18786 12.8097 3.53541C14.4631 4.72314 16.4723 6.64319 17.5685 8.35871C18.0825 9.16309 17.5503 10.2247 16.5983 10.2941L15.1087 10.4026C15.0244 10.4088 14.9401 10.4147 14.8558 10.4203C14.9256 12.0356 14.8899 13.6541 14.7486 15.2654C14.6811 16.036 14.0358 16.6273 13.2622 16.6273H10.7379ZM10.6815 9.76253C10.5678 11.5498 10.589 13.3431 10.745 15.1273H13.255C13.411 13.3431 13.4323 11.5498 13.3186 9.76253C13.3058 9.56216 13.3739 9.36505 13.5077 9.21531C13.6414 9.06556 13.8296 8.9757 14.0301 8.96582C14.3535 8.94989 14.6767 8.93015 14.9997 8.90661L16.0815 8.82775C15.1219 7.41445 13.9204 6.1802 12.5313 5.18235L12 4.80071L11.4687 5.18235C10.0796 6.1802 8.87813 7.41446 7.91858 8.82775L9.00038 8.90661C9.32337 8.93015 9.64656 8.94989 9.9699 8.96582C10.1704 8.9757 10.3586 9.06556 10.4924 9.21531C10.6261 9.36505 10.6942 9.56216 10.6815 9.76253Z" fill="#0F0F0F"/>
597 <path d="M5.75 17C5.75 16.5858 5.41421 16.25 5 16.25C4.58579 16.25 4.25 16.5858 4.25 17V19C4.25 19.9665 5.0335 20.75 6 20.75H18C18.9665 20.75 19.75 19.9665 19.75 19V17C19.75 16.5858 19.4142 16.25 19 16.25C18.5858 16.25 18.25 16.5858 18.25 17V19C18.25 19.1381 18.1381 19.25 18 19.25H6C5.86193 19.25 5.75 19.1381 5.75 19V17Z" fill="#0F0F0F"/>
598 </svg>
599
600 <?php esc_html_e( 'Change', 'hello-wpstream' ); ?>
601 </div>
602 </div>
603 </div>
604 </div>
605 </div>
606 </div>
607 <?php
608 // Capture the buffered markup and return it.
609 $return_string .= ob_get_contents();
610 ob_end_clean();
611
612 return $return_string;
613 }
614 }
615
616 if ( ! function_exists( 'wpstream_theme_build_html_gallery_dashboard' ) ) {
617 /**
618 * Build HTML for the gallery in the dashboard.
619 *
620 * This function builds HTML for the gallery in the dashboard based on the provided array of image IDs.
621 *
622 * @param array $gallery_images An array of image IDs.
623 * @return string The HTML string for the gallery.
624 */
625 function wpstream_theme_build_html_gallery_dashboard( $gallery_images ) {
626 // Accumulator for the gallery tiles markup.
627 $return_string = '';
628
629 if ( is_array( $gallery_images ) ) {
630 // Render one tile per image ID in the list.
631 foreach ( $gallery_images as $attachment_id ) {
632 // Resolve the card-sized preview for this attachment.
633 $preview = wp_get_attachment_image_src( $attachment_id, 'wpstream_featured_unit_cards' );
634
635 // Only output tiles that have a usable preview URL.
636 if ( $preview && '' !== $preview[0] ) {
637 $return_string .= '<div class="wpstream_uploaded_images" data-imageid="' . esc_attr( $attachment_id ) . '">';
638 $return_string .= '<img src="' . esc_url( $preview[0] ) . '" alt="' . esc_html__( 'thumb', 'hello-wpstream' ) . '" /></div>';
639 }
640 }
641 }
642
643 // Return the concatenated gallery markup.
644 return $return_string;
645 }
646 }
647 if ( ! function_exists( 'wpstream_return_user_can_create_paid' ) ) {
648 /**
649 * Whether non-admin users may create paid (WooCommerce) channels.
650 *
651 * Canonical default for a capability that used to exist only in the
652 * companion theme, which silently blocked paid auto-creation on every
653 * other theme. Overridable by defining the function in a (child) theme.
654 *
655 * @return bool True when paid channel creation is allowed for the user.
656 */
657 function wpstream_return_user_can_create_paid() {
658 // The site is configured to auto-create paid channels on the go-live
659 // surface: paid creation is explicit site policy.
660 if ( 'paid' === get_option( 'wpstream_user_streaming_channel_type' ) ) {
661 return true;
662 }
663
664 // Otherwise the companion theme's customizer toggle decides (its
665 // setting is stored as a theme mod of the active theme).
666 return (bool) get_theme_mod( 'wpstream_user_can_create_paid', false );
667 }
668 }
669
670 if ( ! function_exists( 'wpstream_return_max_channels_per_user' ) ) {
671 /**
672 * Maximum number of channels a single non-admin user may own.
673 *
674 * Canonical default for the companion theme's customizer setting, so the
675 * dashboard create-channel CTA works on any theme. An unset/zero setting
676 * means effectively unlimited (999, the companion theme's convention).
677 * The one accessor for the cap: the Streaming Content Creation module and
678 * the dashboard both read it, so the filter below governs both. The plugin
679 * loads this framework on after_setup_theme before the companion theme's
680 * copy, which is what keeps this filtered definition the one in effect.
681 *
682 * @param int $owner_id User whose cap is asked for; 0 means the current user.
683 * @return int The per-user channel cap.
684 */
685 function wpstream_return_max_channels_per_user( $owner_id = 0 ) {
686 $value = intval( get_theme_mod( 'wpstream_return_max_channels_per_user' ) );
687
688 // Zero/unset means effectively unlimited.
689 if ( 0 === $value ) {
690 $value = 999;
691 }
692
693 $owner_id = intval( $owner_id ) > 0 ? intval( $owner_id ) : get_current_user_id();
694
695 /**
696 * Filters how many live channels one non-admin user may own.
697 *
698 * @since 4.14.0
699 *
700 * @param int $max The configured cap (customizer setting, 999 when unset).
701 * @param int $owner_id User the cap applies to.
702 */
703 return max( 0, intval( apply_filters( 'wpstream_max_channels_per_user', $value, $owner_id ) ) );
704 }
705 }
706