/home/arranoyd/telegastro/wp-content/plugins/fv-wordpress-flowplayer/models/db-video-meta.php
<?php
/*  FV Wordpress Flowplayer - HTML5 video player with Flash fallback    
    Copyright (C) 2013  Foliovision

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// video meta data instance with options that's stored in a DB
class FV_Player_Db_Video_Meta {

  private
    $id, // automatic ID for the meta data
    $is_valid = true, // used when loading meta data from DB to determine whether we've found it
    $id_video, // DB ID of the video to which this meta data belongs
    $meta_key, // arbitrary meta key
    $meta_value; // arbitrary meta value

  private static
    $db_table_name,
    $DB_Instance = null;

  /**
   * @return int
   */
  public function getId() {
    return $this->id;
  }

  /**
   * @return int
   */
  public function getIdVideo() {
    return $this->id_video;
  }

  /**
   * @return string
   */
  public function getMetaKey() {
    return $this->meta_key;
  }

  /**
   * @return mixed
   */
  public function getMetaValue() {
    return $this->meta_value;
  }

  /**
   * @return bool
   */
  public function getIsValid() {
    return $this->is_valid;
  }

  /**
   * Initializes database name, including WP prefix
   * once WPDB class is initialized.
   *
   * @return string Returns the actual table name for this ORM class.
   */
  public static function init_db_name() {
    global $wpdb;

    self::$db_table_name = $wpdb->prefix.'fv_player_videometa';
    return self::$db_table_name;
  }

  /**
   * Checks for DB tables existence and creates it as necessary.
   *
   * @param $wpdb The global WordPress database object.
   */
  private function initDB($wpdb) {
    global $fv_fp, $fv_wp_flowplayer_ver;

    self::init_db_name();

    if( defined('PHPUnitTestMode') || !$fv_fp->_get_option('video_meta_model_db_checked') || $fv_fp->_get_option('video_meta_model_db_checked') != $fv_wp_flowplayer_ver ) {
      $sql = "
CREATE TABLE " . self::$db_table_name . " (
  id bigint(20) unsigned NOT NULL auto_increment,
  id_video bigint(20) unsigned NOT NULL default '0',
  meta_key varchar(255) NOT NULL,
  meta_value longtext NOT NULL,
  PRIMARY KEY  (id),
  KEY id_video (id_video),
  KEY meta_key (meta_key(191))
)" . $wpdb->get_charset_collate() . ";";
      require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
      dbDelta( $sql );
      $fv_fp->_set_option('video_meta_model_db_checked', $fv_wp_flowplayer_ver);
    }
  }

  /**
   * Makes this meta data object linked to a record in database.
   * This is used when loading multiple meta data records in the constructor,
   * so we can return them as objects from the DB and any saving will
   * not insert their duplicates.
   *
   * @param $id The DB ID to which we'll link this meta data record.
   * @param bool $id_is_video If true, link to an actual video ID will be made.
   */
  public function link2db($id, $id_is_video = false) {
    if (!$id_is_video) {
      $this->id = (int) $id;
    } else {
      $this->id_video = (int) $id;
    }
  }

  /**
   * FV_Player_Db_Video_Meta constructor.
   *
   * @param int $id         ID of video meta data to load data from the DB for.
   * @param array $options  Options for a newly created video meta data that will be stored in a DB.
   * @param FV_Player_Db    $DB_Cache Instance of the DB shortcode global object that handles caching
   *                        of videos, players and their meta data.
   *
   * @throws Exception When no valid ID nor options are provided.
   */
  function __construct($id, $options = array(), $DB_Cache = null) {
    global $wpdb;

    if ($DB_Cache) {
      self::$DB_Instance = $DB_Cache;
    }

    $this->initDB($wpdb);
    $multiID = is_array($id);

    // don't load anything, if we've only created this instance
    // to initialize the database (this comes from list-table.php and unit tests)
    if ($id === -1) {
      return;
    }

    // check whether we're not trying to load data for a single video
    // rather than meta data by its own ID
    $load_for_video = false;
    $force_cache_update = false;

    if (is_array($options) && count($options) && isset($options['id_video']) && is_array($options['id_video'])) {
      $load_for_video = true;
      $multiID = true;
      $id = $options['id_video'];
      // reset this, so we don't try to create a new record below
      $options = array();
    }

    // if we've got options, fill them in instead of querying the DB,
    // since we're storing new video meta data into the DB in such case
    if (is_array($options) && count($options)) {
      foreach ($options as $key => $value) {
        if (property_exists($this, $key)) {
          if ($key !== 'id') {
            $this->$key = stripslashes($value);
          }
        } else {
          // generate warning
          trigger_error('Unknown property for new DB video meta data item: ' . $key);
        }
      }
    } else if ($multiID || (is_numeric($id) && $id > 0)) {
      /* @var $cache FV_Player_Db_Video_Meta[] */
      $cache = ($DB_Cache ? $DB_Cache->getVideoMetaCache() : array());
      $all_cached = false;
      $some_meta_exist = false;

      // no options, load data from DB
      if ($multiID) {
        $query_ids = array();

        // make sure we have numeric IDs and that they're not cached yet
        $is_cached = false;
        foreach ($id as $id_key => $id_value) {
          if ($load_for_video) {
            $is_cached = isset($cache[$id_value]);
            $some_meta_exist = ($is_cached ? count($cache[$id_value]) : false);
          } else {
            // run through all the cached data and check
            // whether our meta data ID was not cached yet
            foreach ($cache as $video_meta) {
              if (isset($video_meta[$id_value])) {
                $is_cached = true;
                $some_meta_exist = (count($video_meta[$id_value]) ? true : false);
              }
            }
          }

          // select from DB if not cached yet
          if (!$is_cached) {
            $query_ids[ $id_key ] = (int) $id_value;
          }

          $id[$id_key] = (int) $id_value;
        }

        if (count($query_ids)) {
          // load multiple video metas via their IDs but a single query and return their values
          $meta_data = $wpdb->get_results( 'SELECT * FROM ' . self::$db_table_name . ' WHERE ' . ( $load_for_video ? 'id_video' : 'id' ) . ' IN(' . implode( ',', $query_ids ) . ')' );

          // run through all of the meta data and
          // fill the ones that were not found with blank arrays
          // for cache-filling purposes
          if ( !is_array( $meta_data ) ) {
            $meta_data = array();
          }

          foreach ( $query_ids as $q_id ) {
            $meta_found = false;
            foreach ( $meta_data as $m_data ) {
              if ( ( $load_for_video && $m_data->id_video == $q_id ) || ( ! $load_for_video && $m_data->id == $q_id ) ) {
                $meta_found = true;
                break;
              }
            }

            // if we have no meta data for the requested ID,
            // fill it with an empty array
            if ( ! $meta_found ) {
              $force_cache_update = true;

              if ( $load_for_video ) {
                // for video, create an empty array with no meta
                $cache[ $q_id ] = array();
              } else {
                // for a single meta, initialize it with null value
                // on a 0-id video (which obviously cannot exist,
                // so we can use it for cache-checking purposes)
                $cache[0][ $q_id ] = null;
              }
            }
          }
        } else {
          $all_cached = true;
        }
      } else {
        $is_cached = false;
        if ($load_for_video) {
          $is_cached = isset($cache[$id]);
          $some_meta_exist = ($is_cached ? count($cache[$id]) : false);
        } else {
          // run through all the cached data and check
          // whether our meta data ID was not cached yet
          foreach ($cache as $video_id => $video_meta) {
            if (isset($video_meta[$id])) {
              $is_cached = true;
              $some_meta_exist = (count($video_meta[$id]) ? true : false);
            }
          }
        }

        if (!$is_cached) {
          // load a single video meta data record
          $meta_data = $wpdb->get_row( $wpdb->query( 'SELECT * FROM ' . self::$db_table_name . ' WHERE id = ' . intval($id) ) );

          // run through all of the meta data and
          // fill the ones that were not found with blank arrays
          // for cache-filling purposes
          if (!is_array($meta_data)) {
            $meta_data = array();
          }

          $meta_found = false;
          foreach ($meta_data as $m_data) {
            if (($load_for_video && $m_data->id_video == $id) || (!$load_for_video && $m_data->id == $id)) {
              $meta_found = true;
              break;
            }
          }

          // if we have no meta data for the requested ID,
          // fill it with an empty array
          if (!$meta_found) {
            $force_cache_update = true;

            if ($load_for_video) {
              // for player, create an empty array with no meta
              $cache[$id] = array();
            } else {
              // for a single meta, initialize it with null value
              // on a 0-id video (which obviously cannot exist,
              // so we can use it for cache-checking purposes)
              $cache[0][$id] = null;
            }
          }
        } else {
          $all_cached = true;
        }
      }

      if (isset($meta_data) && $meta_data && count($meta_data)) {
        // single ID, just populate our own data
        if (!$multiID) {
          // fill-in our internal variables, as they have the same name as DB fields (ORM baby!)
          foreach ( $meta_data as $key => $value ) {
            $this->$key = stripslashes($value);
          }

          // cache this meta in DB object
          if ($DB_Cache) {
            $cache[$this->id_video][$this->id] = $this;
          }
        } else {
          // multiple IDs, create new video meta objects for each of them except the first one,
          // for which we'll use this instance
          $first_done = false;
          foreach ($meta_data as $db_record) {
            if (!$first_done) {
              // fill-in our internal variables
              foreach ( $db_record as $key => $value ) {
                $this->$key = stripslashes($value);
              }

              $first_done = true;

              // cache this meta in DB object
              if ($DB_Cache) {
                $cache[$db_record->id_video][$this->id] = $this;
              }
            } else {
              // create a new video object and populate it with DB values
              $record_id = $db_record->id;
              // if we don't unset this, we'll get warnings
              unset($db_record->id);

              if (!self::$DB_Instance->isVideoMetaCached($db_record->id_video, $record_id)) {
                $video_meta_object = new FV_Player_Db_Video_Meta(null, get_object_vars($db_record), self::$DB_Instance);
                $video_meta_object->link2db($record_id);

                // cache this meta in DB object
                if ($DB_Cache) {
                  $cache[$db_record->id_video][$record_id] = $video_meta_object;
                }
              }
            }
          }
        }
      } else if ($all_cached && $some_meta_exist) {
        // fill the data for this class with data of the cached class
        if ($multiID) {
          $cached_meta = reset($id);
        } else {
          $cached_meta = $id;
        }

        // find the meta in cache and reassign $cached_meta
        foreach ($cache as $video_id => $video) {
          if ($load_for_video && $video_id == $cached_meta ) {
            // load first meta for the requested video
            $cached_meta = reset( $video );
            break;
          } else if (!$load_for_video && isset($video[$cached_meta])) {
            $cached_meta = $video[$cached_meta];
            break;
          }
        }

        // $cached_meta will remain numeric if there are no meta data in the database
        if ($cached_meta instanceof FV_Player_Db_Video_Meta) {
          foreach ( $cached_meta->getAllDataValues() as $key => $value ) {
            $this->$key = stripslashes($value);
          }
        }
      } else {
        $this->is_valid = false;
      }
    } else {
      throw new Exception('No options nor a valid ID was provided for DB video meta data item.');
    }

    // update cache, if changed
    if (isset($cache) && ($force_cache_update || !isset($all_cached) || !$all_cached)) {
      self::$DB_Instance->setVideoMetaCache($cache);
    }
  }

  /**
   * Returns all options data for this video.
   *
   * @return array Returns all options data for this video.
   */
  public function getAllDataValues() {
    $data = array();
    foreach (get_object_vars($this) as $property => $value) {
      if ($property != 'is_valid' && $property != 'db_table_name' && $property != 'DB_Instance') {
        $data[$property] = $value;
      }
    }

    return $data;
  }

  /**
   * Stores new video meta data item or updates and existing one
   * in the database.
   *
   * @return bool|int Returns record ID if successful, false otherwise.
   */
  public function save() {
    global $wpdb;

    // prepare SQL
    $is_update   = ($this->id ? true : false);
    $sql         = ($is_update ? 'UPDATE' : 'INSERT INTO').' '.self::$db_table_name.' SET ';
    $data_keys   = array();
    $data_values = array();

    foreach (get_object_vars($this) as $property => $value) {
      if ($property != 'id' && $property != 'is_valid' && $property != 'db_table_name' && $property != 'DB_Instance') {
        $is_video_id = ($property == 'id_video');
        $data_keys[] = $property . ' = '.($is_video_id ? (int) $value : '%s');

        if (!$is_video_id) {
          $data_values[] = $value;
        }
      }
    }

    $sql .= implode(', ', $data_keys);

    if ($is_update) {
      $sql .= ' WHERE id = ' . $this->id;
    }

    $wpdb->query( $wpdb->prepare( $sql, $data_values ));

    if (!$is_update) {
      $this->id = $wpdb->insert_id;
    }

    if (!$wpdb->last_error) {
      // add this meta into cache
      $cache = self::$DB_Instance->getVideoMetaCache();
      $cache[$this->id_video][$this->id] = $this;
      self::$DB_Instance->setVideoMetaCache($cache);

      return $this->id;
    } else {
      /*var_export($wpdb->last_error);
      var_export($wpdb->last_query);*/
      return false;
    }
  }

  /**
   * Prepares this class' properties for export
   * and returns them in an associative array.
   *
   * @return array Returns an associative array of this class' properties and their values.
   */
  public function export() {
    $export_data = array();
    foreach (get_object_vars($this) as $property => $value) {
      if ($property != 'id' && $property != 'id_video' && $property != 'is_valid' && $property != 'db_table_name' && $property != 'DB_Instance') {
        $export_data[$property] = $value;
      }
    }

    return $export_data;
  }

  /**
   * Removes meta data instance from the database.
   *
   * @return bool Returns true if the delete was successful, false otherwise.
   */
  public function delete() {
    // not a DB meta? no delete
    if (!$this->is_valid) {
      return false;
    }

    global $wpdb;

    $wpdb->delete(self::$db_table_name, array('id' => $this->id));

    if (!$wpdb->last_error) {
      // remove this meta from cache
      $cache = self::$DB_Instance->getVideoMetaCache();
      if (isset($cache[$this->id_video][$this->id])) {
        unset($cache[$this->id_video][$this->id]);
        self::$DB_Instance->setVideoMetaCache($cache);
      }

      return true;
    } else {
      /*var_export($wpdb->last_error);
      var_export($wpdb->last_query);*/
      return false;
    }
  }
}