PluginProbe
FV Player 8 / 8.1
FV Player 8 v8.1
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 / models / db-player.php

db-player.php in FV Player 8 8.1, at models/db-player.php

1,201 lines 35.4 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 // player instance with options that's stored in a DB
20 class FV_Player_Db_Player {
21
22 private
23 $id, // automatic ID for the player
24 $is_valid = true, // used when loading the player from DB to determine whether we've found it
25 $author, // user ID that created this player
26 $changed_by, // user ID that last updated this player
27 $date_created, // date this playlist was created on
28 $date_modified, // date this playlist was modified on
29 $ab, // whether to show AB loop
30 $align, // alignment position, DEPRECATED
31 $autoplay, // whether to autoplay videos on page load
32 $controlbar, // whether to show the control bar for this player
33 $copy_text, // whether to show DRM text on the player
34 $embed, // whether to show embed links for this player
35 $end_actions, // what do to when the playlist in this player ends
36 $end_action_value, // the actual shortcode value for end_actions field
37 $height, // height of this player on page
38 $hflip, // whether to horizontally flip the player
39 $lightbox, // whether to enable displaying this player in a lightbox
40 $lightbox_caption, // title for the lightbox popup
41 $lightbox_height, // height for the lightbox popup
42 $lightbox_width, // width for the lightbox popup
43 $overlay, // any HTML overlay text
44 $overlay_height, // height of overlay for this player
45 $overlay_width, // width of overlay for this player
46 $overlay_skip, // whether or not to use global overlay for this player
47 $player_name, // custom name for the player
48 $player_slug, // short slug to be used as a unique identifier for this player that can be used instead of an ID
49 $playlist,
50 $playlist_advance, // whether to auto-advance the playlist in this player (On / Off / Default)
51 $qsel,
52 $share, // whether to display sharing buttons (On / Off / Default)
53 $share_title, // title for sharing buttons
54 $share_url,
55 $speed,
56 $sticky, // whether or not to enable sticky functionality for this player
57 $video_ads,
58 $video_ads_post,
59 $width, // with of the player on page
60 $status, // draft of published
61 $videos, // comma-delimited IDs of videos for this player
62 $toggle_end_action,
63 $toggle_overlay,
64 $video_objects = null,
65 $numeric_properties = array('id', 'author', 'changed_by'),
66 $meta_data = null,
67 $subtitles_count,
68 $chapters_count,
69 $transcript_count,
70 $cues_count,
71 $ignored_input_fields = array();
72
73 private static
74 $db_table_name,
75 $DB_Instance = null;
76
77 /**
78 * @param mixed $videos
79 */
80 public function setVideos( $videos ) {
81 $this->videos = $videos;
82 }
83
84 /**
85 * @return int
86 */
87 public function getAuthor() {
88 return intval($this->author);
89 }
90
91 /**
92 * @return int
93 */
94 public function getChangedBy() {
95 return $this->changed_by;
96 }
97
98 /**
99 * @return int
100 */
101 public function getDateCreated() {
102 return $this->date_created;
103 }
104
105 /**
106 * @return int
107 */
108 public function getDateModified() {
109 return $this->date_modified;
110 } // object of this player's meta data
111
112 /**
113 * @return string
114 */
115 public function getPlayerName() {
116 return $this->player_name;
117 }
118
119 /**
120 * @return string
121 */
122 public function getPlayerSlug() {
123 return $this->player_slug;
124 }
125
126 /**
127 * @return string
128 */
129 public function getEndActionValue() {
130 return $this->end_action_value;
131 }
132
133 /**
134 * @return string
135 */
136 public function getAd() {
137 _deprecated_function( __METHOD__, '8.0', __CLASS__ . '::getOverlay' );
138
139 return $this->getOverlay();
140 }
141
142 /**
143 * @return int
144 */
145 public function getId() {
146 return $this->id;
147 }
148
149 /**
150 * @return string
151 */
152 public function getAb() {
153 return $this->ab;
154 }
155
156 /**
157 * @return int
158 */
159 public function getAdHeight() {
160 _deprecated_function( __METHOD__, '8.0', __CLASS__ . '::getOverlayHeight' );
161
162 return $this->getOverlayHeight();
163 }
164
165 /**
166 * @return int
167 */
168 public function getAdWidth() {
169 _deprecated_function( __METHOD__, '8.0', __CLASS__ . '::getOverlayWidth' );
170
171 return $this->getOverlayWidth();
172 }
173
174 /**
175 * @return string
176 */
177 public function getAdSkip() {
178 _deprecated_function( __METHOD__, '8.0', __CLASS__ . '::getOverlaySkip' );
179
180 return $this->getOverlaySkip();
181 }
182
183 /**
184 * @return string
185 */
186 public function getAlign() { // DEPRECATED
187 return $this->align;
188 }
189
190 /**
191 * @return string
192 */
193 public function getAutoplay() {
194 return $this->autoplay;
195 }
196
197 /**
198 * @return string
199 */
200 public function getControlbar() {
201 return $this->controlbar;
202 }
203
204 /**
205 * @return string
206 */
207 public function getCopyText() {
208 return $this->copy_text;
209 }
210
211 public function getCount($video_meta) {
212 if( $video_meta == 'subtitles' && isset($this->subtitles_count) ) return $this->subtitles_count;
213 if( $video_meta == 'cues' && isset($this->cues_count) ) return $this->cues_count;
214 if( $video_meta == 'chapters' && isset($this->chapters_count) ) return $this->chapters_count;
215 if( $video_meta == 'transcript' && isset($this->transcript_count) ) return $this->transcript_count;
216 return 0;
217 }
218
219 /**
220 * @return string
221 */
222 public function getEmbed() {
223 return $this->embed;
224 }
225
226 /**
227 * @return string
228 */
229 public function getEndActions() {
230 return $this->end_actions;
231 }
232
233 /**
234 * @return int
235 */
236 public function getHeight() {
237 return $this->height;
238 }
239
240 /**
241 * @return string
242 */
243 public function getHflip() {
244 return $this->hflip;
245 }
246
247 /**
248 * @return string
249 */
250 public function getLightbox() {
251 return $this->lightbox;
252 }
253
254 /**
255 * @return string
256 */
257 public function getLightboxCaption() {
258 return $this->lightbox_caption;
259 }
260
261 /**
262 * @return int
263 */
264 public function getLightboxHeight() {
265 return $this->lightbox_height;
266 }
267
268 /**
269 * @return int
270 */
271 public function getLightboxWidth() {
272 return $this->lightbox_width;
273 }
274
275 /**
276 * @return string
277 */
278 public function getOverlay() {
279 return $this->overlay;
280 }
281
282 /**
283 * @return int
284 */
285 public function getOverlayHeight() {
286 return $this->overlay_height;
287 }
288
289 /**
290 * @return int
291 */
292 public function getOverlayWidth() {
293 return $this->overlay_width;
294 }
295
296 /**
297 * @return string
298 */
299 public function getOverlaySkip() {
300 return $this->overlay_skip;
301 }
302
303 /**
304 * @return string
305 */
306 public function getPlaylist() {
307 return $this->playlist;
308 }
309
310 /**
311 * @return string
312 */
313 public function getPlaylistAdvance() {
314 return $this->playlist_advance;
315 }
316
317 /**
318 * @return string
319 */
320 public function getPlaylistSplash() {
321 return $this->playlist_splash;
322 }
323
324
325 /**
326 * @return int
327 */
328 public function getPlaylistAttachmentId() {
329 return $this->playlist_splash_attachment_id;
330 }
331
332 /**
333 * @return string
334 */
335 public function getQsel() {
336 return $this->qsel;
337 }
338
339 /**
340 * @return string
341 */
342 public function getShare() {
343 return $this->share;
344 }
345
346 /**
347 * @return string
348 */
349 public function getShareTitle() {
350 return $this->share_title;
351 }
352
353 /**
354 * @return string
355 */
356 public function getShareUrl() {
357 return $this->share_url;
358 }
359
360 /**
361 * @return string
362 */
363 public function getSpeed() {
364 return $this->speed;
365 }
366
367 /**
368 * @return string
369 */
370 public function getSticky() {
371 return $this->sticky;
372 }
373
374 /**
375 * @return string
376 */
377 public function getVideoAds() {
378 return $this->video_ads;
379 }
380
381 /**
382 * @return string
383 */
384 public function getVideoAdsPost() {
385 return $this->video_ads_post;
386 }
387
388 /**
389 * @return int
390 */
391 public function getWidth() {
392 return $this->width;
393 }
394
395 /**
396 * @return string
397 */
398 public function getStatus() {
399 return $this->status;
400 }
401
402 /**
403 * @param $status
404 */
405 public function setStatus( $status ) {
406 $this->status = $status;
407 }
408
409 /**
410 * @return string
411 */
412 public function getVideoIds() {
413 return $this->videos;
414 } // comma-separated list of video IDs for this player
415
416 /**
417 * @return bool
418 */
419 public function getIsValid() {
420 return boolval($this->is_valid);
421 }
422
423 /**
424 * @return bool
425 */
426 public function getToggleEndAction() {
427 return boolval($this->toggle_end_action);
428 }
429
430 /**
431 * @return bool
432 */
433 public function getToggleAdCustom() {
434 _deprecated_function( __METHOD__, '8.0', __CLASS__ . '::getToggleOverlay' );
435
436 return $this->getToggleOverlay();
437 }
438
439 /**
440 * @return bool
441 */
442 public function getToggleOverlay() {
443 return boolval($this->toggle_overlay);
444 }
445
446 /**
447 * Initializes database name, including WP prefix
448 * once WPDB class is initialized.
449 *
450 * @return string Returns the actual table name for this ORM class.
451 */
452 public static function init_db_name() {
453 global $wpdb;
454
455 self::$db_table_name = $wpdb->prefix.'fv_player_players';
456 return self::$db_table_name;
457 }
458
459 /**
460 * Returns name of the video DB table.
461 *
462 * @return mixed The name of the video DB table.
463 */
464 public static function get_db_table_name() {
465 if ( !self::$db_table_name ) {
466 self::init_db_name();
467 }
468
469 return self::$db_table_name;
470 }
471
472 /**
473 * Checks for DB tables existence and creates it as necessary.
474 *
475 * @param $force Forces to run dbDelta.
476 */
477 public static function initDB($force = false) {
478 global $wpdb, $fv_fp, $fv_wp_flowplayer_ver;
479
480 self::init_db_name();
481
482 $res = false;
483
484 if( defined('PHPUnitTestMode') || !$fv_fp->_get_option('player_model_db_checked') || $fv_fp->_get_option('player_model_db_checked') != $fv_wp_flowplayer_ver || $force ) {
485 $sql = "
486 CREATE TABLE " . self::$db_table_name . " (
487 id bigint(20) unsigned NOT NULL auto_increment,
488 player_name varchar(255) NOT NULL,
489 player_slug varchar(255) NOT NULL,
490 videos text NOT NULL,
491 ab varchar(7) NOT NULL,
492 align varchar(7) NOT NULL,
493 author bigint(20) unsigned NOT NULL default '0',
494 autoplay varchar(7) NOT NULL,
495 controlbar varchar(7) NOT NULL,
496 copy_text varchar(120) NOT NULL,
497 changed_by bigint(20) unsigned NOT NULL default '0',
498 date_created datetime NOT NULL default CURRENT_TIMESTAMP,
499 date_modified datetime NOT NULL default CURRENT_TIMESTAMP,
500 embed varchar(12) NOT NULL,
501 end_actions varchar(10) NOT NULL,
502 end_action_value varchar(255) NOT NULL,
503 height varchar(7) NOT NULL,
504 hflip varchar(7) NOT NULL,
505 lightbox varchar(7) NOT NULL,
506 lightbox_caption varchar(120) NOT NULL,
507 lightbox_height varchar(7) NOT NULL,
508 lightbox_width varchar(7) NOT NULL,
509 overlay text NOT NULL,
510 overlay_height varchar(7) NOT NULL,
511 overlay_width varchar(7) NOT NULL,
512 overlay_skip varchar(7) NOT NULL,
513 playlist varchar(12) NOT NULL,
514 playlist_advance varchar(7) NOT NULL,
515 qsel varchar(25) NOT NULL,
516 share varchar(7) NOT NULL,
517 share_title varchar(120) NOT NULL,
518 share_url varchar(255) NOT NULL,
519 speed varchar(255) NOT NULL,
520 sticky varchar(7) NOT NULL,
521 video_ads varchar(10) NOT NULL,
522 video_ads_post varchar(10) NOT NULL,
523 width varchar(7) NOT NULL,
524 status varchar(9) NOT NULL default 'published',
525 toggle_end_action varchar(7) NOT NULL,
526 toggle_overlay varchar(7) NOT NULL,
527 PRIMARY KEY (id)
528 )" . $wpdb->get_charset_collate() . ";";
529 require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
530 $res = dbDelta( $sql );
531
532 if( $fv_fp->_get_option('player_model_db_checked') != $fv_wp_flowplayer_ver ) {
533 self::updateTableConversion();
534 }
535
536 $fv_fp->_set_option('player_model_db_checked', $fv_wp_flowplayer_ver);
537 }
538
539 return $res;
540 }
541
542 /**
543 * Run conversion of old data to new data structure.
544 *
545 * @return void
546 */
547 private static function updateTableConversion() {
548 global $fv_fp, $wpdb;
549
550 $table = self::$db_table_name;
551
552 if( $fv_fp->_get_option('player_model_db_updated') != '7.9.3' ) {
553 // enable toggle end action
554 $wpdb->query("UPDATE `{$wpdb->prefix}fv_player_players` SET toggle_end_action = 'true' WHERE end_actions != '' AND end_action_value != ''");
555
556 // enable toggle ad custom
557 if ( $wpdb->get_results( $wpdb->prepare( "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = %s AND column_name = 'ad'", $table ) ) ) {
558 $wpdb->query("UPDATE `{$wpdb->prefix}fv_player_players` SET toggle_overlay = 'true' WHERE ad != ''");
559 }
560
561 // ad => overlay
562 if ( !FV_Player_Db::has_table_column( self::$db_table_name , 'overlay' ) ) {
563 if ( $wpdb->get_results( $wpdb->prepare( "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = %s AND column_name = %s", $table, 'ad' ) ) ) {
564 $wpdb->query( "UPDATE `{$wpdb->prefix}fv_player_players` SET `overlay` = `ad` WHERE `overlay` = '' AND `ad` != ''" );
565 }
566 }
567
568 // ad_height => overlay_height
569 if ( !FV_Player_Db::has_table_column( self::$db_table_name , 'overlay_height' ) ) {
570 if ( $wpdb->get_results( $wpdb->prepare( "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = %s AND column_name = %s", $table, 'ad_height' ) ) ) {
571 $wpdb->query( "UPDATE `{$wpdb->prefix}fv_player_players` SET `overlay_height` = `ad_height` WHERE `overlay_height` = '' AND `ad_height` != ''" );
572 }
573 }
574
575 // ad_skip => overlay_skip
576 if ( !FV_Player_Db::has_table_column( self::$db_table_name , 'overlay_skip' ) ) {
577 if ( $wpdb->get_results( $wpdb->prepare( "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = %s AND column_name = %s", $table, 'ad_skip' ) ) ) {
578 $wpdb->query( "UPDATE `{$wpdb->prefix}fv_player_players` SET `overlay_skip` = `ad_skip` WHERE `overlay_skip` = '' AND `ad_skip` != ''" );
579 }
580 }
581
582 // ad_width => overlay_width
583 if ( !FV_Player_Db::has_table_column( self::$db_table_name , 'overlay_width' ) ) {
584 if ( $wpdb->get_results( $wpdb->prepare( "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = %s AND column_name = %s", $table, 'ad_width' ) ) ) {
585 $wpdb->query( "UPDATE `{$wpdb->prefix}fv_player_players` SET `overlay_width` = `ad_width` WHERE `overlay_width` = '' AND `ad_width` != ''" );
586 }
587 }
588
589 // toggle_ad_custom => toggle_overlay
590 if ( !FV_Player_Db::has_table_column( self::$db_table_name , 'toggle_overlay' ) ) {
591 if ( $wpdb->get_results( $wpdb->prepare( "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = %s AND column_name = %s", $table, 'toggle_ad_custom' ) ) ) {
592 $wpdb->query( "UPDATE `{$wpdb->prefix}fv_player_players` SET `toggle_overlay` = `toggle_ad_custom` WHERE `toggle_overlay` = '' AND `toggle_ad_custom` != ''" );
593 }
594 }
595
596 $fv_fp->_set_option('player_model_db_updated', '7.9.3');
597 }
598 }
599
600 /**
601 * Fills non-orm variables that are not directly linked
602 * from received POST data to the DB.
603 */
604 private function fill_properties( $options, $DB_Cache = false ) {
605 // fill-in our internal variables, as they have the same name as DB fields (ORM baby!)
606 foreach ($options as $key => $value) {
607 if (!in_array($key, $this->ignored_input_fields)) {
608 if (property_exists($this, $key)) {
609
610 $value = FV_Player_Db::strip_tags( $value, $key );
611
612 $this->$key = FV_Player_Db::sanitize( $value );
613
614 } else if ( in_array($key, array('subtitles_count', 'chapters_count', 'transcript_count', 'cues_count'))) {
615 $this->$key = FV_Player_Db::sanitize( $value );
616
617 }
618 }
619 }
620 }
621
622 /**
623 * FV_Player_Db_Player constructor.
624 *
625 * @param int $id ID of player to load data from the DB for.
626 * @param array $options Options for a newly created player that will be stored in a DB.
627 * @param FV_Player_Db $DB_Cache Instance of the DB shortcode global object that handles caching
628 * of videos, players and their meta data.
629 */
630 function __construct($id, $options = array(), $DB_Cache = null) {
631
632 global $wpdb;
633
634 // add any extra fields from extending plugins that should be ORM-ignored
635 $this->ignored_input_fields = apply_filters('fv_flowplayer_add_ignored_input_names', $this->ignored_input_fields);
636
637 if ($DB_Cache) {
638 self::$DB_Instance = $DB_Cache;
639 } else {
640 global $FV_Player_Db;
641 self::$DB_Instance = $DB_Cache = $FV_Player_Db;
642 }
643
644 self::initDB();
645
646 // For a while https://foliovision.com/player/advanced/player-database used to say the $player_id should be passed as array
647 if( is_array($id) ) {
648 $id = array_pop($id);
649 }
650
651 // don't load anything, if we've only created this instance
652 // to initialize the database (this comes from list-table.php and unit tests)
653 if ($id === -1) {
654 return;
655 }
656
657 if ( is_numeric($id) && $id >= -1 ) {
658 /* @var $cache FV_Player_Db_Player[] */
659 $cache = ($DB_Cache ? $DB_Cache->getPlayersCache() : array());
660 $all_cached = false;
661
662 if ($DB_Cache && !$DB_Cache->isPlayerCached($id)) {
663 // load a single player
664 $player_data = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fv_player_players WHERE id = %d", $id ), ARRAY_A );
665
666 } else if ($DB_Cache && $DB_Cache->isPlayerCached($id)) {
667 $all_cached = true;
668 } else {
669 $player_data = -1;
670 $this->is_valid = false;
671 }
672
673 if (isset($player_data) && $player_data !== -1 && is_array($player_data) && count($player_data)) {
674
675 $this->fill_properties($player_data,$DB_Cache);
676
677 // cache this player in DB object
678 if ($DB_Cache) {
679 $cache[$this->id] = $this;
680 }
681
682 } else if ($all_cached) {
683 // fill the data for this class with data of the cached class
684 $cached_player = $cache[$id];
685
686 foreach ($cached_player->getAllDataValues() as $key => $value) {
687 if( is_array($value) ) continue; // problem with video_objects when exporting
688
689 $this->$key = FV_Player_Db::sanitize( $value );
690 }
691
692 // add meta data
693 $this->meta_data = $cached_player->getMetaData();
694
695 // make this class a valid player
696 $this->is_valid = true;
697 } else if ($player_data !== -1) {
698 // no players found in DB
699 $this->is_valid = false;
700 }
701
702 // if we've got options, fill them in instead of querying the DB,
703 // since we're storing new player into the DB in such case
704 } else if (is_array($options)) {
705
706 if( !empty($options['id']) ) {
707 // ID cannot be set, as it's automatically assigned to all new players
708 trigger_error('ID of a newly created DB player was provided but will be generated automatically.');
709 unset($options['id']);
710 }
711
712 $this->fill_properties($options);
713
714 // add dates for newly created players
715 if( empty($this->date_created) ) $this->date_created = date_format( date_create(), "Y-m-d H:i:s" );
716 if( empty($this->date_modified) ) $this->date_modified = date_format( date_create(), "Y-m-d H:i:s" );
717
718 // add author, if we're creating new player and not loading a player from DB for caching purposes
719 if ( empty($options['author']) ) {
720 $this->author = get_current_user_id();
721 }
722
723 if ( empty($options['changed_by']) ) {
724 if ( !empty($options['author']) ) {
725 $this->changed_by = $options['author'];
726 } else {
727 $this->changed_by = get_current_user_id();
728 }
729 }
730 } else {
731 if ( defined('WP_DEBUG') && WP_DEBUG ) {
732 trigger_error( 'No options nor a valid ID was provided for DB player instance.' );
733 }
734
735 return;
736 }
737
738 // update cache, if changed
739 if (isset($cache) && (!isset($all_cached) || !$all_cached)) {
740 self::$DB_Instance->setPlayersCache($cache);
741 }
742 }
743
744 /**
745 * Makes this player linked to a record in database.
746 * This is used when we want to update a player in the DB
747 * instead of inserting a new record for it.
748 *
749 * @param int $id The DB ID to which we'll link this player.
750 * @param bool $load_meta If true, the meta data will be loaded for the video from database.
751 * Used when loading multiple videos at once with the array $id constructor parameter.
752 *
753 * @throws Exception When the underlying Meta object throws.
754 */
755 public function link2db($id, $load_meta = false) {
756 $this->id = (int) $id;
757
758 if ($load_meta) {
759 $this->meta_data = new FV_Player_Db_Player_Meta(null, array('id_player' => array($id)), self::$DB_Instance);
760 }
761 }
762
763 /**
764 * This method will manually link meta data to the player.
765 * Used when not using save() method to link meta data to player while saving it
766 * into the database (i.e. while previewing etc.)
767 *
768 * @param FV_Player_Db_Player_Meta $meta_data The meta data object to link to this player.
769 *
770 * @throws Exception When an underlying meta data object throws an exception.
771 */
772 public function link2meta($meta_data) {
773 if (is_array($meta_data) && count($meta_data)) {
774 // we have meta, let's insert that
775 $first_done = false;
776 foreach ($meta_data as $meta_record) {
777 // create new record in DB
778 $meta_object = new FV_Player_Db_Player_Meta(null, $meta_record, self::$DB_Instance);
779
780 // link to DB, if the meta record has an ID
781 if (!empty($meta_record['id'])) {
782 $meta_object->link2db($meta_record['id']);
783 }
784
785 if (!$first_done) {
786 $this->meta_data = array($meta_object);
787 $first_done = true;
788 } else {
789 $this->meta_data[] = $meta_object;
790 }
791 }
792 }
793 }
794
795 /**
796 * Returns player name, if its empty then its assembled from video captions and /or sources
797 *
798 * @return string player name from getter or assembled name
799 */
800 public function getPlayerNameWithFallback() {
801 $player_name = $this->getPlayerName();
802
803 // player name is present - return it
804 if( empty( $player_name ) ) {
805 $player_name = join(', ', $this->getPlayerVideoNames() );
806 }
807
808 // add "Draft" at the end of player, if in draft status
809 if ( $this->getStatus() == 'draft' ) {
810 $player_name .= ' (' . esc_attr__( 'Draft', 'fv-player' ) . ')';
811 }
812
813 return $player_name;
814 }
815
816 public function getPlayerVideoNames() {
817 $video_names = array();
818
819 foreach( $this->getVideos() as $video ) {
820 $title = $video->getTitle();
821 if( !$title ) {
822 $title = $video->getTitleFromSrc();
823 }
824
825 $video_names[] = $title;
826 }
827
828 return $video_names;
829 }
830
831 /**
832 * Returns all global options data for this player.
833 *
834 * @return array Returns all global options data for this player.
835 */
836 public function getAllDataValues() {
837 $data = array();
838 foreach (get_object_vars($this) as $property => $value) {
839 if (!in_array($property, array('numeric_properties', 'is_valid', 'DB_Instance', 'db_table_name', 'meta_data', 'ignored_input_fields', 'subtitles_count', 'chapters_count', 'transcript_count', 'cues_count' ))) {
840 $data[$property] = $value;
841 }
842 }
843
844 return $data;
845 }
846
847 /**
848 * Returns meta data for this player.
849 *
850 * @return FV_Player_Db_Player_Meta[] Returns all meta data for this player.
851 * @throws Exception When an underlying meta data object throws an exception.
852 */
853 public function getMetaData() {
854 // meta data already loaded and present, return them
855 if ($this->meta_data && $this->meta_data !== -1) {
856 if (is_array($this->meta_data)) {
857 return $this->meta_data;
858 } else if ( self::$DB_Instance && self::$DB_Instance->isPlayerMetaCached($this->id) ) {
859 $cache = self::$DB_Instance->getPlayerMetaCache();
860 return $cache[$this->id];
861 } else {
862 if ($this->meta_data && $this->meta_data->getIsValid()) {
863 return array( $this->meta_data );
864 } else {
865 return array();
866 }
867 }
868 } else if ($this->meta_data === null) {
869 // meta data not loaded yet - load them now
870 $this->meta_data = new FV_Player_Db_Player_Meta(null, array('id_player' => array($this->id)), self::$DB_Instance);
871
872 // set meta data to -1, so we know we didn't get any meta data for this player
873 if (!$this->meta_data->getIsValid()) {
874 $this->meta_data = -1;
875 return array();
876 } else {
877 if ($this->meta_data && $this->meta_data->getIsValid()) {
878 // we want to return all meta data for this player
879 if ( self::$DB_Instance && self::$DB_Instance->isPlayerMetaCached($this->id) ) {
880 $cache = self::$DB_Instance->getPlayerMetaCache();
881 return $cache[$this->id];
882 } else {
883 if ($this->meta_data && $this->meta_data->getIsValid()) {
884 return array( $this->meta_data );
885 } else {
886 return array();
887 }
888 }
889 } else {
890 return array();
891 }
892 }
893 } else {
894 return array();
895 }
896 }
897
898 /**
899 * Returns actual meta data for a key for this player.
900 */
901 public function getMetaValue( $key, $single = false ) {
902 $output = array();
903 $data = $this->getMetaData();
904 if (count($data)) {
905 foreach ($data as $meta_object) {
906 if ($meta_object->getMetaKey() == $key) {
907 if( $single ) return $meta_object->getMetaValue();
908 $output[] = $meta_object->getMetaValue();
909 }
910 }
911 }
912 if( $single ) return false;
913 return $output;
914 }
915
916 /**
917 * Returns all video objects for this player.
918 *
919 * @return FV_Player_Db_Video[] Returns all video objects for this player.
920 * @throws Exception When an underlying video object throws an exception.
921 */
922 public function getVideos() {
923 // video data already loaded and present, return them
924 if ($this->video_objects && $this->video_objects !== -1) {
925 return $this->video_objects;
926 } else if ($this->video_objects === null && ! empty( $this->videos ) ) {
927 // video objects not loaded yet - load them now
928 $videos_in_order = explode(',', trim($this->videos, ','));
929 $videos = new FV_Player_Db_Video($videos_in_order, array(), self::$DB_Instance);
930
931 // set meta data to -1, so we know we didn't get any meta data for this video
932 if (!$videos->getIsValid()) {
933 $this->video_objects = -1;
934 return array();
935 } else {
936 $this->video_objects = self::$DB_Instance->getVideosCache();
937
938 // load meta data for all videos at once, then link them to those videos,
939 // as we will always load meta data for those, so it's no use to lazy-load
940 // those only when needed (which creates additional DB requests per each video)
941 $ids = array();
942 foreach ($this->video_objects as $video) {
943 $ids[] = $video->getId();
944 }
945
946 new FV_Player_Db_Video_Meta(null, array('id_video' => $ids), self::$DB_Instance);
947
948 // assign all meta data to their respective videos
949 foreach ( $this->video_objects as $video ) {
950 if ( self::$DB_Instance->isVideoMetaCached($video->getId()) ) {
951 // prepare meta data
952 $meta_2_video = array();
953 $cache = self::$DB_Instance->getVideoMetaCache();
954 foreach ($cache[$video->getId()] as $meta_object) {
955 $meta_2_video[] = $meta_object->getAllDataValues();
956 }
957
958 $video->link2meta( $meta_2_video );
959 } else {
960 $video->link2meta(-1);
961 }
962 }
963
964 // fill video objects with videos sorted according to playlist order
965 $ordered_video_objects = array();
966 foreach ($videos_in_order as $video_id) {
967 // find the correct video
968 foreach ( $this->video_objects as $video ) {
969 if ($video->getId() == $video_id) {
970 $ordered_video_objects[] = $video;
971 break;
972 }
973 }
974 }
975 $this->video_objects = $ordered_video_objects;
976
977 return $this->video_objects;
978 }
979 } else {
980 return array();
981 }
982 }
983
984 /**
985 * Stores new player instance or updates and existing one
986 * in the database.
987 *
988 * @param array $meta_data An optional array of key-value objects
989 * with possible meta data for this player.
990 * @param bool $is_import Used when importing to respect the desired player status flag
991 *
992 * @return bool|int Returns record ID if successful, false otherwise.
993 * @throws Exception When the underlying metadata object throws.
994 */
995 public function save($meta_data = array(), $is_import = false ) {
996 global $wpdb;
997
998 // prepare SQL
999 $is_update = ($this->id ? true : false);
1000 $data_values = array();
1001
1002 // fill gmdate(s)
1003 $this->date_modified = date_format( date_create(), "Y-m-d H:i:s" );
1004
1005 if (!$is_update && empty($this->date_created) ) {
1006 $this->date_created = $this->date_modified;
1007 }
1008
1009 // fill author(s)
1010 $this->changed_by = get_current_user_id();
1011
1012 if (!$is_update) {
1013 $this->author = $this->changed_by;
1014 }
1015
1016 foreach (get_object_vars($this) as $property => $value) {
1017 if (!in_array($property, array('id', 'numeric_properties', 'is_valid', 'DB_Instance', 'db_table_name', 'video_objects', 'meta_data', 'ignored_input_fields', 'subtitles_count', 'chapters_count', 'transcript_count', 'cues_count' ))) {
1018 // don't update author or date created if we're updating
1019 if ($is_update && ($property == 'date_created' || $property == 'author')) {
1020 continue;
1021 }
1022
1023 // make sure status is set to "draft" for a new player
1024 if ( $property == 'status' ) {
1025 if ( !$is_update && !$is_import ) {
1026 $value = 'draft';
1027 } else if ( !$value ) {
1028 // for existing player, only update if we need to change player status
1029 // from "draft" to "published", otherwise leave the status alone
1030 continue;
1031 }
1032 }
1033
1034 $value = FV_Player_Db::strip_tags( $value, $property );
1035
1036 if ( in_array( $property, array( 'author', 'changed_by' ) ) ) {
1037 $format[ $property ] = '%d';
1038
1039 if ( ! $value ) {
1040 $value = 0;
1041 }
1042
1043 } else {
1044 $format[ $property ] = '%s';
1045
1046 if ( ! $value ) {
1047 $value = '';
1048 }
1049 }
1050
1051 $data_values[ $property ] = $value;
1052 }
1053 }
1054
1055 if ($is_update) {
1056 $wpdb->update( self::$db_table_name, $data_values, array( 'id' => $this->id ), $format );
1057
1058 } else {
1059 $wpdb->insert( self::$db_table_name, $data_values, $format );
1060 }
1061
1062 if (!$is_update) {
1063 $this->id = $wpdb->insert_id;
1064 }
1065
1066 if (!$wpdb->last_error) {
1067 // check for any meta data
1068 if (is_array($meta_data) && count($meta_data)) {
1069 // we check which meta values are no longer set and remove these
1070 $existing_meta = $is_update ? $this->getMetaData() : array();
1071 $existing_meta_ids = array();
1072 foreach( $existing_meta AS $existing ) {
1073 $found = false;
1074 foreach ($meta_data as $meta_record) {
1075 if( !empty($meta_record['meta_value']) && $meta_record['meta_key'] == $existing->getMetaKey() ) {
1076 $found = true;
1077 break;
1078 }
1079 }
1080 if( !$found ) {
1081 $existing->delete();
1082 } else {
1083 $existing_meta_ids[$existing->getId()] = true;
1084 }
1085 }
1086
1087 // we have meta, let's insert that
1088 foreach ($meta_data as $meta_record) {
1089 // it's possible that we switched the checkbox off and then on, by that time its id won't exist anymore! Todo: remove data-id instead?
1090 if( !empty($meta_record['id']) && empty($existing_meta_ids[$meta_record['id']]) ) {
1091 unset($meta_record['id']);
1092 }
1093
1094 // if the meta value has no ID associated, we replace the first one which exists, effectively preventing multiple values under the same meta key, which is something to improve, perhaps
1095 if( empty($meta_record['id']) ) {
1096 foreach( $existing_meta AS $existing ) {
1097 if( $meta_record['meta_key'] == $existing->getMetaKey() ) {
1098 $meta_record['id'] = $existing->getId();
1099 break;
1100 }
1101 }
1102 }
1103
1104 // add our player ID
1105 $meta_record['id_player'] = $this->id;
1106
1107 // create new record in DB
1108 $meta_object = new FV_Player_Db_Player_Meta(null, $meta_record, self::$DB_Instance);
1109
1110 // add meta data ID
1111 if( !empty($meta_record['id']) ) {
1112 $meta_object->link2db($meta_record['id']);
1113 } else if( empty($meta_record['meta_value']) ) {
1114 continue;
1115 }
1116
1117 $meta_object->save();
1118 $this->meta_data = $meta_object;
1119 }
1120 }
1121
1122 // cache this instance
1123 $cache = self::$DB_Instance->getPlayersCache();
1124 $cache[$this->id] = $this;
1125 self::$DB_Instance->setPlayersCache($cache);
1126
1127 return $this->id;
1128 } else {
1129 /*var_export($wpdb->last_error);
1130 var_export($wpdb->last_query);*/
1131 return false;
1132 }
1133 }
1134
1135 /**
1136 * Prepares this class' properties for export
1137 * and returns them in an associative array.
1138 *
1139 * @return array Returns an associative array of this class' properties and their values.
1140 */
1141 public function export() {
1142 $export_data = array();
1143 foreach (get_object_vars($this) as $property => $value) {
1144 if (!in_array($property, array('id', 'id_player', 'numeric_properties', 'is_valid', 'DB_Instance', 'db_table_name', 'videos', 'video_objects', 'meta_data', 'author', 'changed_by', 'date_created', 'date_modified', 'ignored_input_fields', 'subtitles_count', 'chapters_count', 'transcript_count', 'cues_count' ))) {
1145 $export_data[$property] = $value;
1146 }
1147 }
1148
1149 return $export_data;
1150 }
1151
1152 /**
1153 * Removes the player and all of its videos, meta and video meta data from database.
1154 *
1155 * @return false|int Returns number of rows updated or false on failure.
1156 * @throws Exception When any of the underlying classes throws an exception.
1157 */
1158 public function delete() {
1159 global $wpdb;
1160
1161 // load player meta data
1162 $meta = $this->getMetaData();
1163 if ($meta && count($meta)) {
1164 $export_data['meta'] = array();
1165
1166 foreach ($meta as $meta_data) {
1167 // don't include edit locks
1168 $meta_data->delete();
1169 }
1170 }
1171
1172 // load videos and meta for this player
1173 $videos = $this->getVideos();
1174
1175 // this line will load and cache meta for all videos at once
1176 new FV_Player_Db_Video_Meta(null, array('id_video' => explode(',', $this->getVideoIds())), self::$DB_Instance);
1177
1178 if ($videos && count($videos)) {
1179 foreach ($videos as $video) {
1180 // only delete videos which are used for this particular player and no other player
1181 if( $wpdb->get_var( $wpdb->prepare( "select count(*) from `{$wpdb->prefix}fv_player_players` where FIND_IN_SET( %d, videos )", $video->getId() ) ) > 1 ) {
1182 continue;
1183 }
1184
1185 $video->delete();
1186
1187 // load all meta data for this video
1188 if (self::$DB_Instance->isVideoMetaCached($video->getId())) {
1189 $cache = self::$DB_Instance->getVideoMetaCache();
1190 foreach ($cache[$video->getId()] as $meta) {
1191 $meta->delete();
1192 }
1193 }
1194 }
1195 }
1196
1197 return $wpdb->delete(self::$db_table_name, array('id' => $this->id));
1198 }
1199
1200 }
1201