Current File : /home/kelaby89/abl.academy/wp-content/plugins/h5p/admin/class-h5p-content-query.php
<?php
/**
 * H5P Plugin.
 *
 * @package   H5P
 * @author    Joubel <[email protected]>
 * @license   MIT
 * @link      http://joubel.com
 * @copyright 2015 Joubel
 */

/**
 * H5P Content Query class
 *
 * @package H5P_Plugin_Admin
 * @author Joubel <[email protected]>
 */
class H5PContentQuery {

  private $base_table;
  private $valid_joins;

  // Valid filter operators
  private $valid_operators = array(
    '=' => " = '%s'",
    'LIKE' => " LIKE '%%%s%%'",
    'IN' => " IN (%s)"
  );

  private $user_fields = [
    'user_name' => 'display_name',
  ];

  // Valid fields and their true database names
  private $valid_fields = array(
    'id' => array('hc', 'id'),
    'title' => array('hc', 'title', TRUE),
    'content_type_id' => array('hl', 'name'),
    'content_type' => array('hl', 'title', TRUE),
    'slug' => array('hc', 'slug', TRUE),
    'created_at' => array('hc', 'created_at'),
    'updated_at' => array('hc', 'updated_at'),
    'user_id' => array('hc', 'user_id'),
    'tags' =>  array('t', 'GROUP_CONCAT(DISTINCT CONCAT(t.id,\',\',t.name) ORDER BY t.id SEPARATOR \';\')')
  );

  private $fields, $fields_raw, $join, $where, $where_args, $order_by, $limit, $limit_args;

  /**
   * Constructor
   *
   * @since 1.5.3
   * @param array $fields List of fields to return.
   *   Valid values are: id, title, content_type, created_at, updated_at, user_id, user_name
   * @param int $offset Skip this many rows.
   * @param int $limit Max number of rows to return.
   * @param string $order_by Field to order content by.
   * @param bool $reverse_order Reverses the ordering.
   * @param array $filters
   *   Must be defined like so: array(array('field', 'Cool Content', 'LIKE'))
   */
  public function __construct($fields, $offset = NULL, $limit = NULL, $order_by = NULL, $reverse_order = NULL, $filters = NULL) {
    global $wpdb;

    $this->base_table = "{$wpdb->prefix}h5p_contents hc";
    $this->valid_joins = array(
      'hl' => " LEFT JOIN {$wpdb->prefix}h5p_libraries hl ON hl.id = hc.library_id",
      't' => " LEFT JOIN {$wpdb->prefix}h5p_contents_tags ct ON ct.content_id = hc.id
               LEFT JOIN {$wpdb->prefix}h5p_tags t ON ct.tag_id = t.id
               LEFT JOIN {$wpdb->prefix}h5p_contents_tags ct2 ON ct2.content_id = hc.id"
    );

    $this->join = array();

    // Start adding fields
    $this->fields_raw = $fields;
    $this->fields = '';
    foreach ($fields as $field) {
      // User fields are handled separately.
      if ( isset( $this->user_fields[ $field ] ) ) {
        continue;
      }

      $valid_field = $this->get_valid_field($field);
      $table = $valid_field[0];

      // Add join
      $this->add_join($table);

      // Add valid fields
      if ($this->fields) {
        $this->fields .= ', ';
      }
      if ($table !== 't') {
        $this->fields .= $table . '.';
      }
      $this->fields .= $valid_field[1] . ' AS ' . $field;
    }
    if (!$this->fields) {
      throw new Exception('No fields specified.');
    }

    // Add filters to data query
    $this->where = '';
    $this->where_args = array();

    if ($filters !== NULL) {
      foreach ($filters as $filter) {
        if (!isset($filter[0]) || !isset($filter[1])) {
          throw new Exception('Missing filter options.');
        }

        $field = $this->get_valid_field($filter[0]);

        // Add join
        $this->add_join($field[0]);

        // Add where
        $this->where .= ($this->where ? ' AND ' : ' WHERE ') . ($field[0] === 't' ? 'ct2.tag_id' : $field[0] . '.' . $field[1]);
        $this->where_args[] = $filter[1];

        // Check if operator is valid, if not use the first valid one.
        $operator = (isset($filter[2]) ? $filter[2] : '=');
        if (!isset($this->valid_operators[$operator])) {
          throw new Exception('Invalid operator: '. $operator);
        }
        $this->where .= $this->valid_operators[$operator];
      }
    }

    // Sort by
    $this->order_by = '';
    if ($order_by !== NULL) {

      // This is merely a workaround for WP user data now being queried
      // separately instead of in a join. Whatever function using order_by will
      // need to handle this. This should probably be refactored completely.
      if ( isset( $this->user_fields[ $order_by ] ) ) {
        $this->order_by_user_field = $order_by;
        $this->reverse_order = $reverse_order;
      }
      else {
        $field = $this->get_valid_field($order_by);

        // Add join
        $this->add_join($field[0]);

        $dir = ($reverse_order ? TRUE : FALSE);
        if (isset($field[2])) {
          $dir = !$dir; // Reverse ordering of text fields
        }
        $this->order_by .= " ORDER BY {$field[0]}.{$field[1]} " . ($dir ? 'ASC' : 'DESC');
      }
    }

    // Add joins
    $this->join = join('', $this->join);

    // Limit
    $this->limit = '';
    $this->limit_args = array();

    if ($limit !== NULL) {
      // This is merely a workaround for WP user data now being queried
      // separately instead of in a join. Whatever function using limit will
      // need to handle this. This should probably be refactored completely.
      if ( isset( $this->user_fields[ $order_by ] ) ) {
        $this->limit_user_field_args = array();

        if ($offset !== NULL) {
          $this->limit_user_field_args[] = $offset;
          $this->limit_user_field_args[] = $limit;
        }
      }
      else {
        $this->limit .= ' LIMIT';

        if ($offset !== NULL) {
          $this->limit .= ' %d,';
          $this->limit_args[] = $offset;
        }

        $this->limit .= ' %d';
        $this->limit_args[] = $limit;
      }
    }
  }

  /**
   * Makes it easier to validate a field while processing fields.
   *
   * @since 1.5.3
   * @param string $field
   * @return array
   */
  private function get_valid_field($field) {
    if (!isset($this->valid_fields[$field])) {
      throw new Exception('Invalid field: ' . $field);
    }

    return $this->valid_fields[$field];
  }

  /**
   * Makes it easier to add valid joins while processing fields.
   *
   * @since 1.5.3
   * @param string $table
   */
  private function add_join($table) {
    if ($table === 'hc' || !is_array($this->join)) {
      return; // Do not join base table.
    }

    if (isset($this->join[$table])) {
      return; // Only add if missing
    }

    // Check if table is valid
    if (!isset($this->valid_joins[$table])) {
      throw new Exception('Invalid table: ' . $table);
    }

    // Add join
    $this->join[$table] = $this->valid_joins[$table];
  }

  /**
   * Get the result of the query.
   *
   * @since 1.5.3
   * @return array
   */
  public function get_rows() {
    global $wpdb;

    $query = "SELECT {$this->fields}
      FROM {$this->base_table}
      {$this->join}
      {$this->where}
      GROUP BY hc.id
      {$this->order_by}
      {$this->limit}";
    $args = array_merge($this->where_args, $this->limit_args);

    if (!empty($args)) {
      // We need to prep if we have args
      $query = $wpdb->prepare($query, $args);
    }

    $results = $wpdb->get_results( $query );
    $results = $this->append_user_data( $results );

    // Manually order results if we're ordering by a user field.
    if ( isset( $this->order_by_user_field ) ) {
      $results = $this->order_results_by(
        $results,
        $this->order_by_user_field,
        $this->reverse_order
      );
    }

    // Manually limit results if we're ordering by a user field.
    if ( isset( $this->limit_user_field_args ) ) {
      $results = $this->limit_results(
        $results,
        $this->limit_user_field_args[0],
        $this->limit_user_field_args[1]
      );
    }

    return $results;
  }

  /**
   * Total number of matches. Useful for pagination.
   *
   * @since 1.5.3
   * @return int
   */
  public function get_total() {
    global $wpdb;

    $query = "SELECT COUNT(DISTINCT hc.id)
      FROM {$this->base_table}
      {$this->join}
      {$this->where}";

    if (!empty($this->where_args)) {
      // We need to prep if we have args
      $query = $wpdb->prepare($query, $this->where_args);
    }
    return (int) $wpdb->get_var($query);
  }

  /**
   * Appends user data to query results by fetching from user table.
   *
   * @since xxx
   * @return array
   */
  protected function append_user_data( $results ) {
    // If no user fields were requested, we can bail.
    if ( ! array_intersect( $this->fields_raw, array_keys( $this->user_fields ) ) ) {
      return $results;
    }

    // Collect all user IDs to process in a single query.
    $user_ids = [];
    foreach ( $results as $result ) {
      if ( ! isset( $result->user_id ) ) {
        continue;
      }

      $user_ids[] = $result->user_id;
    }

    // Fail early, as prompting get_users with empty array will fetch all users
    if ( empty( $user_ids ) ) {
      return $results;
    }

    /*
     * Only used to determine whether there is any WP user for any $user_ids,
     * so only requesting ID to prevent memory issues
     */
    $wp_users = get_users(
      array(
        'include' => array_unique( $user_ids ),
        'fields' => array('ID'),
      )
    );

    // If no users are found, there's nothing to do.
  	if ( ! $wp_users ) {
      return $results;
    }

    // We can fetch items from the now primed cache.
    foreach ( $results as &$result ) {
      if ( ! isset( $result->user_id ) ) {
        continue;
      }

      $userdata = get_userdata( $result->user_id );

      if ( in_array( 'user_name', $this->fields_raw, true ) ) {
        $result->user_name = $userdata->display_name;
      }
    }

    return $results;
  }

  /**
   * Order results by a given property.
   *
   * @param array $results  Array of objects to sort.
   * @param string $order_by  Property to sort by.
   * @param bool $reverse_order Whether to reverse the order.
   * @since xxx
   * @return array
   */
  protected function order_results_by(
    $results, $order_by, $reverse_order = FALSE
  ) {
    if (!isset($order_by)) {
      return $results;
    }

    usort($results, function($a, $b) use ($order_by, $reverse_order) {
      if (!isset($a->$order_by) || !isset($b->$order_by)) {
        return 0; // No sorting possible, because property is missing.
      }

      $a = $a->$order_by;
      $b = $b->$order_by;

      if ($a == $b) {
        return 0;
      }

      if ($reverse_order) {
        return ($a < $b) ? 1 : -1;
      }

      return ($a < $b) ? -1 : 1;
    });

    return $results;
  }

  /**
   * Limit results to a given range.
   *
   * @param array $results  Array of objects to limit.
   * @param int $offset  Offset to start at.
   * @param int $limit  Number of items to return.
   */
  protected function limit_results( $results = array(), $offset = 0, $limit ) {
    return array_slice( $results, $offset, $limit );
  }
}
Page not found – Hello World !