PluginProbe
FV Player 8 / trunk
FV Player 8 vtrunk
trunk 8.0.18 8.0.19 8.0.20 8.0.21 8.0.25 8.0.27 8.1 8.1.3
fv-player / controller / shortcodes.php

shortcodes.php in FV Player 8 trunk, at controller/shortcodes.php

650 lines 21.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /* FV Player - HTML5 video player
3 Copyright (C) 2013 Foliovision
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 if ( ! defined( 'ABSPATH' ) ) {
20 exit;
21 }
22
23 require_once dirname( __FILE__ ) . '/../models/fv-player.php';
24 if (!class_exists('flowplayer_frontend'))
25 require_once dirname( __FILE__ ) . '/../models/fv-player-frontend.php';
26
27 add_shortcode('flowplayer','flowplayer_content_handle');
28
29 add_shortcode('fvplayer','flowplayer_content_handle');
30
31 add_shortcode('fv_time','fv_player_time');
32
33 function flowplayer_content_handle( $atts, $content = null, $tag = false ) {
34 global $fv_fp;
35 if( !$fv_fp ) return false;
36
37 if( $fv_fp->_get_option('parse_commas') && strcmp($tag,'flowplayer') == 0 ) {
38
39 if( !isset( $atts['src'] ) ) {
40 foreach( $atts AS $key => $att ) {
41 if( stripos( $att, 'src=' ) !== FALSE ) {
42 if( stripos( $att, ',' ) === FALSE ) { // if the broken attribute is not using ','
43 $atts['src'] = preg_replace( '/^\s*?src=[\'"](.*)[\'"].*?$/', '$1', $att );
44 } else {
45 $atts['src'] = preg_replace( '/^\s*?src=[\'"](.*)[\'"],\s*?$/', '$1', $att );
46 }
47 $i = $key+1;
48 unset( $atts[$key] ); // = ''; // let's remove it, so it won't confuse the rest of workaaround
49 }
50 }
51 }
52
53 if( !isset( $atts['splash'] ) ) {
54 foreach( $atts AS $key => $att ) {
55 if( stripos( $att, 'splash=' ) !== FALSE ) {
56 $atts['splash'] = preg_replace( '/^\s*?splash=[\'"](.*)[\'"],\s*?$/', '$1', $att );
57 unset( $atts[$key] ); // = ''; // let's remove it, so it won't confuse the rest of workaround
58 }
59 }
60 }
61
62 // the popup should really be a content of the shortcode, not an attribute
63 // this part will fix the popup if there is any single quote in it.
64 if( !isset( $atts['popup'] ) ) {
65 $popup = array();
66 $is_popup = false;
67 foreach( $atts AS $key => $att ) {
68 if( !is_numeric( $key ) ) continue;
69 if( ( stripos( $att, 'popup=' ) !== FALSE || $is_popup ) && stripos( $att, 'src=' ) === FALSE && stripos( $att, 'splash=' ) === FALSE && stripos( $att, 'ad=' ) === FALSE) {
70 $popup[] = $att;
71 $is_popup = true;
72 unset( $atts[$key] ); // = ''; // let's remove it, so it won't confuse the rest of workaround
73 }
74 }
75 $popup = implode( ' ', $popup );
76 $atts['popup'] = preg_replace( '/^\s*?popup=[\'"](.*)[\'"]\s*?$/mi', '$1', $popup );
77 }
78
79 // same for ad code
80 if( !isset( $atts['ad'] ) ) {
81 $ad = array();
82 $is_ad = false;
83 foreach( $atts AS $key => $att ) {
84 if( !is_numeric( $key ) ) continue;
85 if( ( stripos( $att, 'ad=' ) !== FALSE || $is_ad ) && stripos( $att, 'src=' ) === FALSE && stripos( $att, 'splash=' ) === FALSE && stripos( $att, 'popup=' ) === FALSE) {
86 $ad[] = $att;
87 $is_ad = true;
88 unset( $atts[$key] ); // = ''; // let's remove it, so it won't confuse the rest of workaround
89 }
90 }
91 $ad = implode( ' ', $ad );
92 $atts['ad'] = preg_replace( '/^\s*?ad=[\'"](.*)[\'"]\s*?$/mi', '$1', $ad );
93 }
94
95 }
96
97 $atts = wp_parse_args( $atts, array(
98 'admin_warning' => '',
99 'align' => '',
100 'autoplay' => '',
101 'caption' => '',
102 'caption_html' => '',
103 'controlbar' => '',
104 'embed' => '',
105 'end_popup_preview' => '',
106 'engine' => '',
107 'height' => '',
108 'mobile' => '',
109 'linking' => '',
110 'liststyle' => '',
111 'live' => '',
112 'logo' => '',
113 'loop' => '',
114 'overlay' => '',
115 'overlay_width' => '',
116 'overlay_height' => '',
117 'overlay_skip' => '',
118 'play_button' => '',
119 'playlist' => '',
120 'playlist_advance' => '',
121 'playlist_hide' => '',
122 'popup' => '',
123 'post' => '',
124 'redirect' => '',
125 'rtmp' => '',
126 'rtmp_path' => '',
127 'share' => '',
128 'skin' => '',
129 'speed' => '',
130 'splash' => '',
131 'splash_text' => '',
132 'splashend' => '',
133 'src' => '',
134 'src1' => '',
135 'src2' => '',
136 'sticky' => '',
137 'subtitles' => '',
138 'title' => '',
139 'width' => '',
140 ) );
141
142 if( $fv_fp->_get_option('parse_commas') && strcmp($tag,'flowplayer') == 0 ) {
143 foreach( $atts AS $k => $v ) {
144 if( in_array($k, array('admin_warning','caption','caption_html','playlist','popup','share') ) ) {
145 $arguments[$k] = $v;
146 } else {
147 $arguments[$k] = preg_replace('/\,/', '', $v);
148 }
149 }
150
151 } else {
152 $arguments = $atts;
153 }
154
155 if( ( !isset($arguments['src']) || strlen(trim($arguments['src'])) == 0 ) && isset($arguments['mobile']) && strlen(trim($arguments['mobile'])) ) {
156 $arguments['src'] = $arguments['mobile'];
157 unset($arguments['mobile']);
158 }
159
160 $arguments = apply_filters( 'fv_flowplayer_shortcode', $arguments, $fv_fp, $atts );
161
162 if( $arguments['post'] == 'this' ) {
163 $arguments['post'] = get_the_ID();
164 }
165
166 // accepts:
167 // 1) [fvplayer id="198"] - a DB-based shortcode in the form
168 // 2) [fvplayer src="198" playlist="198;199;200"] - a front-end user's playlist shortcode that is programatically passed to this method
169 if( (!empty($arguments['id']) && intval($arguments['id']) > 0) || (!empty($arguments['src']) && is_numeric($arguments['src']) && intval($arguments['src']) > 0) ) {
170 $new_player = $fv_fp->build_min_player(false, $arguments);
171 if (!empty($new_player['script'])) {
172 $GLOBALS['fv_fp_scripts'] = $new_player['script'];
173 }
174
175 if ( $new_player ) {
176 return $new_player['html'];
177 } else {
178 return '';
179 }
180
181 } else if( intval($arguments['post']) > 0 ) {
182 $objVideoQuery = new WP_Query( array( 'post_type' => 'attachment', 'post_status' => 'inherit', 'post_parent' => intval($post), 'post_mime_type' => 'video' ) );
183 if( $objVideoQuery->have_posts() ) {
184 $sHTML = '';
185 while( $objVideoQuery->have_posts() ) {
186 $objVideoQuery->the_post();
187 $aArgs = $arguments;
188 $aArgs['src'] = wp_get_attachment_url(get_the_ID());
189 if( $aSplash = wp_get_attachment_image_src( get_post_thumbnail_id(get_the_ID()), 'large' ) ) {
190 $aArgs['splash'] = $aSplash[0];
191 }
192 if( strlen($aArgs['lightbox']) ) {
193 $aArgs['lightbox'] .= ';'.html_entity_decode(get_the_title());
194 }
195 if( strlen($aArgs['title']) ) {
196 $aArgs['title'] = apply_filters( 'fv_player_title', $aArgs['title'], false );
197 }
198
199 $new_player = $fv_fp->build_min_player( $aArgs['src'],$aArgs );
200 if ( $new_player ) {
201 $sHTML .= $new_player['html'];
202 }
203 }
204
205 return $sHTML;
206 }
207
208 } else if( $arguments['src'] != '' || ( ( ( strlen($fv_fp->conf['rtmp']) && $fv_fp->conf['rtmp'] != 'false' ) || strlen($arguments['rtmp'])) && strlen($arguments['rtmp_path']) ) ) {
209 // build new player
210 $new_player = $fv_fp->build_min_player($arguments['src'],$arguments);
211 if (!empty($new_player['script'])) {
212 $GLOBALS['fv_fp_scripts'] = $new_player['script'];
213 }
214
215 if ( $new_player ) {
216 return $new_player['html'];
217 } else {
218 return '';
219 }
220
221 }
222 return false;
223 }
224
225
226
227
228 add_filter( 'the_content', 'fv_flowplayer_optimizepress', 1 );
229
230 function fv_flowplayer_optimizepress( $post_content ) {
231
232 if( stripos( $post_content, '[video_player type="url"' ) === false ) {
233 return $post_content;
234 }
235
236 $post_content = preg_replace_callback( '~\[video_player.*?\].*?\[/video_player\]~', 'fv_flowplayer_optimizepress_bridge', $post_content );
237 return $post_content;
238 }
239
240 function fv_flowplayer_optimizepress_bridge( $input ) {
241 $video = $input[0];
242
243 $atts = shortcode_parse_atts($video);
244
245 $default = array(
246 'type' => 'embed', // na
247 'hide_controls' => 'N', // todo
248 'auto_play' => 'N', // ok
249 'auto_buffer' => 'N', // todo
250 'width' => 511, // ok
251 'height' => 288, // ok
252 'margin_top' => 0, // todo
253 'margin_bottom' => 20, // todo
254 'border_size' => 0, // todo
255 'border_color' => '#fff', // todo
256 'placeholder' => '', // ok
257 'align' => 'center', // ok
258 'youtube_url' => '', // na
259 'youtube_auto_play' => 'N', // na
260 'youtube_hide_controls' => 'N', // na
261 'youtube_remove_logo' => 'N', // na
262 'youtube_show_title_bar' => 'N', // na
263 'youtube_force_hd' => '', // na
264 'url1' => '', // ok
265 'url2' => '', // ok
266 );
267 $vars = shortcode_atts($default, $atts);
268
269 $shortcode = '[fvplayer';
270
271 $content = preg_replace( '~\[video_player.*?\](.*?)\[/video_player\]~', '$1', $video );
272 $content = base64_decode($content);
273 if(preg_match('|(https?://[^<"]+)|im',$content,$matches)){
274 $shortcode .= ' src="' . esc_url( $matches[1] ) . '"';
275 }
276 $url1 = base64_decode( $vars['url1'] );
277 if(preg_match('|(https?://[^<"]+)|im',$url1,$matches)){
278 $shortcode .= ' src1="' . esc_url( $matches[1] ) . '"';
279 }
280 $url2 = base64_decode( $vars['url2'] );
281 if(preg_match('|(https?://[^<"]+)|im',$url2,$matches)){
282 $shortcode .= ' src2="' . esc_url( $matches[1] ) . '"';
283 }
284
285 if( $vars['placeholder'] ) {
286 $shortcode .= ' splash="' . esc_url( $vars['placeholder'] ) . '"';
287 }
288
289 if( $vars['auto_play'] == 'Y' ) {
290 $shortcode .= ' autoplay="true"';
291 }
292
293 $shortcode .= ' width="' . sanitize_key( $vars['width'] ) . '"';
294 $shortcode .= ' height="' . sanitize_key( $vars['height'] ) . '"';
295 $shortcode .= ' align="' . sanitize_key( $vars['align'] ) . '"';
296
297 if( current_user_can('manage_options') &&
298 (
299 ( isset($vars['margin-top']) && $vars['margin-top'] > 0 ) ||
300 ( isset($vars['margin-bottom']) && $vars['margin-bottom'] > 0 && $vars['margin-bottom'] != 20 ) ||
301 ( isset($vars['hide_controls']) && $vars['hide_controls'] == 'Y' ) ||
302 ( isset($vars['auto_buffer']) && $vars['auto_buffer'] == 'Y' ) ||
303 ( isset($vars['border_size']) && $vars['border_size'] > 0 ) ||
304 isset($vars['border_color'])
305 )
306 ) {
307 $shortcode .= ' admin_warning="Admin note: Some of the OptimizePress styling parameters are not supported by FV Player. Please visit the <a href=\''.admin_url('admin.php?page=fvplayer').'\'>settings</a> and set your global appearance preferences there."';
308 }
309
310 $shortcode .= ']';
311
312 return $shortcode;
313 }
314
315
316 function fv_player_time( $args = array() ) {
317 global $post, $fv_fp;
318
319 if( !empty($args['id']) ) {
320 $player = new FV_Player_Db_Player($args['id']);
321 if( $player->getIsValid() ) {
322 foreach( $player->getVideos() AS $video ) {
323 if( $duration = $video->getDuration() ) {
324 return flowplayer::format_hms( $duration );
325 }
326 }
327 }
328 }
329
330 if( $post->ID > 0 && isset($fv_fp->aCurArgs['src']) ) {
331 return flowplayer::get_duration( $post->ID, $fv_fp->aCurArgs['src'] );
332 } else {
333 return flowplayer::get_duration_post();
334 }
335 }
336
337
338 function fv_player_handle_vimeo_links( $html, $closing = '<!-- link converted by FV Player -->' ) {
339 $html = preg_replace( '~<iframe[^>]*?vimeo\.com/video/(\d+)[^>]*?(\?h=[a-z0-9]+)?[^>]*?></iframe>~', '[fvplayer src="http://vimeo.com/$1$2"]' . $closing, $html );
340 return $html;
341 }
342
343 function fv_player_handle_youtube_links( $html, $closing = '<!-- link converted by FV Player -->' ) {
344 $html = preg_replace( '~<iframe[^>]*?youtube(?:-nocookie)?\.com/(?:embed|v)/(.*?)[\'"&#\?][^>]*?></iframe>~', '[fvplayer src="http://youtube.com/watch?v=$1"]' . $closing, $html );
345 return $html;
346 }
347
348 function fv_player_handle_video_tags( $html, $closing = '<!-- link converted by FV Player -->' ) {
349 $html = preg_replace( '~<figure class="wp-block-video"><video[^>]*?src\s*=\s*"(.+?)"[^>]*?></video></figure>~', '<figure class="wp-block-video">[fvplayer src="$1"]' . $closing . '</figure>' , $html);
350 return $html;
351 }
352
353 function fv_flowplayer_shortcode_video( $output ) {
354 $aArgs = func_get_args();
355 $atts = $aArgs[1];
356
357 $bridge_atts = array();
358 if( isset($atts['src']) ) {
359 $bridge_atts['src'] = $atts['src'];
360 }
361
362 $count = 0;
363 foreach( array('mp4','webm','ogv','mov','flv','wmv','m4v','mp3') AS $key => $value ) {
364 if( !empty($atts[$value]) ) {
365 $src = 'src'.(( $count > 0 ) ? $count : '');
366 $bridge_atts[$src] = $atts[$value];
367 $count++;
368 }
369 }
370
371 if( isset($atts['poster']) ) {
372 $bridge_atts['splash'] = $atts['poster'];
373 }
374
375 if( isset($atts['loop']) && $atts['loop'] == 'on' ) {
376 $bridge_atts['loop'] = 'true';
377 } else if( isset($atts['loop']) && $atts['loop'] == 'off' ) {
378 $bridge_atts['loop'] = 'false';
379 }
380
381 if( isset($atts['autoplay']) && $atts['autoplay'] == 'on' ) {
382 $bridge_atts['autoplay'] = 'true';
383 } else if( isset($atts['loop']) && $atts['loop'] == 'off' ) {
384 $bridge_atts['autoplay'] = 'false';
385 }
386
387 if( isset($atts['width']) ) {
388 $bridge_atts['width'] = $atts['width'];
389 }
390 if( isset($atts['height']) ) {
391 $bridge_atts['height'] = $atts['height'];
392 }
393
394 if( count($bridge_atts) == 0 ) {
395 return "<!--FV Player video shortcode integration - no attributes recognized-->";
396 }
397 return flowplayer_content_handle( $bridge_atts, false, 'video' );
398 }
399
400 function fv_flowplayer_shortcode_playlist( $output ) {
401 $aArgs = func_get_args();
402 $atts = $aArgs[1];
403
404 // copy from wp-includes/media.php wp_playlist_shortcode()
405 global $post;
406 if ( ! empty( $attr['ids'] ) ) {
407 // 'ids' is explicitly ordered, unless you specify otherwise.
408 if ( empty( $attr['orderby'] ) ) {
409 $attr['orderby'] = 'post__in';
410 }
411 $attr['include'] = $attr['ids'];
412 }
413
414 $atts = shortcode_atts( array(
415 'type' => 'audio',
416 'order' => 'ASC',
417 'orderby' => 'menu_order ID',
418 'id' => $post ? $post->ID : 0,
419 'include' => '',
420 'exclude' => '',
421 'style' => 'light',
422 'tracklist' => true,
423 'tracknumbers' => true,
424 'images' => true,
425 'artists' => true
426 ), $atts, 'playlist' );
427
428 $args = array(
429 'post_status' => 'inherit',
430 'post_type' => 'attachment',
431 'post_mime_type' => $atts['type'],
432 'order' => $atts['order'],
433 'orderby' => $atts['orderby']
434 );
435
436 if( !empty($atts['include']) ) {
437 $args['include'] = $atts['include'];
438 $_attachments = get_posts( $args );
439 if( !count($_attachments) ) {
440 return false;
441 }
442
443 $attachments = array();
444 foreach( $_attachments as $key => $val ) {
445 $attachments[$val->ID] = $_attachments[$key];
446 }
447 } else {
448 return false;
449 }
450
451 $bridge_atts = array();
452 $aPlaylistItems = array();
453 $aPlaylistImages = array();
454 $aPlaylistCaptions = array();
455 $aPlaylistDurations = array();
456 $i = 0;
457 foreach ( $attachments as $attachment ) {
458 $i++;
459
460 $url = wp_get_attachment_url( $attachment->ID );
461 if( $i == 1 ) {
462 $bridge_atts['src'] = $url;
463 } else {
464 $aPlaylistItems[] = $url;
465 }
466
467 $thumb_id = get_post_thumbnail_id( $attachment->ID );
468 $src = false;
469 if( !empty( $thumb_id ) ) {
470 list( $src, $width, $height ) = wp_get_attachment_image_src( $thumb_id, 'thumbnail' );
471 }
472 if( $i == 1 ) {
473 $bridge_atts['splash'] = $src;
474 } else {
475 $aPlaylistImages[] = $src;
476 }
477
478 $aPlaylistCaptions[] = str_replace( array('"',';'), '', flowplayer::esc_caption($attachment->post_title) );
479
480 $meta = wp_get_attachment_metadata( $attachment->ID );
481 if( !empty($meta) ) {
482 if( !empty($meta['length']) ) {
483 $aPlaylistDurations[] = $meta['length'];
484 }
485 }
486
487 }
488
489
490 $bridge_atts['playlist'] = '';
491 foreach( $aPlaylistItems AS $key => $src ) {
492 $bridge_atts['playlist'] .= $src;
493 if( $aPlaylistImages[$key] ) {
494 $bridge_atts['playlist'] .= ','.$aPlaylistImages[$key];
495 }
496 $bridge_atts['playlist'] .= ';';
497 }
498 $bridge_atts['playlist'] = trim($bridge_atts['playlist'],';');
499 if( count($aPlaylistCaptions) > 1 || $atts['tracklist'] ) $bridge_atts['caption'] = implode(';',$aPlaylistCaptions);
500 $bridge_atts['durations'] = implode(';',$aPlaylistDurations);
501
502 if( $atts['tracklist'] ) {
503 $bridge_atts['listshow'] = true;
504 }
505
506 if( isset($atts['width']) ) {
507 $bridge_atts['width'] = $atts['width'];
508 }
509 if( isset($atts['height']) ) {
510 $bridge_atts['height'] = $atts['height'];
511 }
512
513 if( count($bridge_atts) == 0 ) {
514 return "<!--FV Flowplayer video shortcode integration - no attributes recognized-->";
515 }
516
517 if( !empty($atts['type']) && $atts['type'] == 'audio' ) {
518 $bridge_atts['liststyle'] = 'horizontal';
519 }
520
521 return flowplayer_content_handle( $bridge_atts, false, 'video' );
522 }
523
524 global $fv_fp;
525 if( ( empty($_POST['action']) || sanitize_key( $_POST['action'] ) != 'parse-media-shortcode' ) && ( empty($_GET['action']) || sanitize_key( $_GET['action'] ) != 'edit' ) && !empty($fv_fp->conf['integrations']['wp_core_video']) && $fv_fp->conf['integrations']['wp_core_video'] == 'true' ) {
526 add_filter( 'wp_video_shortcode_override', 'fv_flowplayer_shortcode_video', 10, 4 );
527 add_filter( 'wp_audio_shortcode_override', 'fv_flowplayer_shortcode_video', 10, 4 );
528 add_filter( 'post_playlist', 'fv_flowplayer_shortcode_playlist', 10, 2 );
529 add_filter( 'the_content', 'fv_player_handle_youtube_links' );
530 add_filter( 'the_content', 'fv_player_handle_vimeo_links' );
531 add_filter( 'embed_oembed_html', 'fv_player_handle_vimeo_links' );
532 add_filter( 'embed_oembed_html', 'fv_player_handle_youtube_links' );
533 add_filter( 'the_content', 'fv_player_handle_video_tags' );
534
535 /**
536 * Fix excerpts if video links are converted to FV Player.
537 *
538 * If FV Player is inserted using shortcode, then it's removed by core WordPress
539 * strip_shortcodes() before the excerpt is created in wp_trim_excerpt().
540 *
541 * However if we use the "Handle WordPress audio/video" setting
542 * integrations[]'wp_core_video'] the video is just a link and it does not get stripped.
543 *
544 * That way FV Player puts in the HTML code which is then removed, while keeping its text
545 * content and this shows as excerpt:
546 *
547 * "Please enable JavaScriptplay-sharp-fill 01:53 LinkEmbedCopy and paste this HTML code into your webpage to embed."
548 */
549 add_filter( 'wp_trim_words', 'fv_player_fix_wp_trim_words', 100, 4 );
550
551 function fv_player_fix_wp_trim_words( $text, $num_words, $more, $original_text ) {
552
553 if ( stripos( $original_text, '<!-- link converted by FV Player -->' ) !== false ) {
554 remove_filter( 'wp_trim_words', 'fv_player_fix_wp_trim_words', 100, 4 );
555
556 // Since the player HTML code ends with just </div> we look for the HTML comment to be able to detect where it ends.
557 $original_text = preg_replace( '~<div id="wpfp[\s\S]*?<!-- link converted by FV Player -->~', '', $original_text );
558
559 $text = wp_trim_words( $original_text, $num_words, $more );
560
561 add_filter( 'wp_trim_words', 'fv_player_fix_wp_trim_words', 100, 4 );
562 }
563
564 return $text;
565 }
566 }
567
568
569 add_filter( 'fv_flowplayer_shortcode', 'fv_flowplayer_shortcode_fix_fancy_quotes' );
570
571 function fv_flowplayer_shortcode_fix_fancy_quotes( $aArgs ) {
572
573 foreach( $aArgs AS $k => $v ) {
574 $v = preg_replace( "~^(\xe2\x80\x9c|\xe2\x80\x9d|\xe2\x80\xb3)~","", $v);
575 $v = preg_replace( "~(\xe2\x80\x9c|\xe2\x80\x9d|\xe2\x80\xb3)$~","", $v);
576
577 $aArgs[$k] = $v;
578 }
579
580 return $aArgs;
581 }
582
583
584 add_shortcode( 'fvplayer_watched', 'fvplayer_watched' );
585
586 function fvplayer_watched( $args = array() ) {
587 $args = wp_parse_args( $args, array(
588 'count' => 20,
589 'include' => 'all',
590 'post_type' => 'any',
591 'user_id' => get_current_user_id()
592 ) );
593
594 $args['full_details'] = true;
595
596 $videos = fv_player_get_user_watched_video_ids( $args );
597 if( count($videos) == 0 ) {
598 return "<p>No watched videos.</p>";
599 }
600
601 $video_ids = array_filter( array_keys($videos), 'is_numeric' );
602
603 $video_ids = array_map( 'absint', $video_ids );
604 $placeholder = implode( ', ', array_fill( 0, count( $video_ids ), '%d' ) );
605
606 global $wpdb;
607 $videos2durations = $wpdb->get_results(
608 $wpdb->prepare(
609 // $placeholders is a string of %d created above
610 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
611 "SELECT v.id AS video_id, vm.meta_value AS duration FROM {$wpdb->prefix}fv_player_videos AS v JOIN {$wpdb->prefix}fv_player_videometa AS vm ON v.id = vm.id_video WHERE v.id IN ({$placeholder}) AND vm.meta_key = 'duration'",
612 $video_ids
613 )
614 );
615
616 $html = "<ul>\n";
617 foreach( $videos AS $video_id => $data ) {
618
619 global $FV_Player_Db;
620 $objVideo = new FV_Player_Db_Video( $video_id, array(), $FV_Player_Db );
621 if( $objVideo->getTitle() ) {
622 $line = $objVideo->getTitle();
623 } else {
624 $line = $objVideo->getTitleFromSrc();
625 }
626
627 $post = get_post( $data['post_id'] );
628 if( $post ) {
629 $line .= " in <a href='".get_permalink($post)."'>".$post->post_title."</a>";
630 }
631
632 if( !empty($data['time']) ) {
633
634 foreach( $videos2durations AS $details ) {
635 if( $details->video_id == $video_id ) {
636 // In some strange cases the duration might not be set right
637 if( $data['time'] <= intval($details->duration) ) {
638 $line .= ' (<abbr title="'.esc_attr($data['message']).'">'.round( 100 * $data['time'] / intval($details->duration) ).'%</abbr>)';
639 }
640 }
641 }
642 }
643
644 $html .= "<li>".$line."</li>\n";
645 }
646 $html .= "</ul>\n";
647
648 return $html;
649 }
650