• Resolved Vincenzo Casu

    (@vincent06)


    Hello, I am here to write again to find out how to extend the function that displays all content. In particular I would like to insert a personalized link after the edit link for each line of content. I need to do this because I would to associate my h5p content to pdf files in my site. With that link I open a popup that lets me choose which pdf to associate with the selected content. I would like to do that not lose any changes after h5p plugin updates. There is a filter or action that I can use?

    Any idea on how to do this task?

    Thank you so much!

    https://www.ads-software.com/plugins/h5p/

Viewing 10 replies - 1 through 10 (of 10 total)
  • Plugin Author icc0rz

    (@icc0rz)

    Hi, this sounds exciting! Unfortunately, I don’t think there’s a simple way to do this. The table is created using JavaScript and uses AJAX to load data. You’ll want to add an SQL join to get the data you relate to the H5P content. To do this, you need to create your own class which extends the H5PContentQuery class. You’ll want to add the field to $this->fields and the table to $this->join. To use your new class you’ll have to add your own ajax callback to WP, i.e. your own version of H5PContentAdmin::ajax_contents. With the AJAX working and returning data you’ll simply have to change the URL and headers used by the JavaScript creating the table. This can be done my overriding the data in the H5PIntegration.dataViews array.

    And if you want even more control over the table you can always wp_dequeue_script the h5p/admin/scripts/h5p-data-views.js script and create your own version. Or override some of the functions in h5p/h5p-php-library/js/h5p-data-view.js.

    Thread Starter Vincenzo Casu

    (@vincent06)

    I did not understand how to do this. I create a class MyCustomH5PContentQuery that extends H5PContentQuery.
    How the plugin would use the extended class instead of H5PContentQuery? Can you show me a code example?

    /**
     * H5P Plugin.
     *
     * @package   H5P
     * @author    Joubel <[email protected]>
     * @license   MIT
     * @link      https://joubel.com
     * @copyright 2015 Joubel
     */
    
    /**
     * CUSTOM H5P Content Query class
     *
     * @package H5P_Plugin_Admin
     * @author Joubel <[email protected]>
     */
    class MyCustomH5PContentQuery extends H5PContentQuery{
    
      private $base_table;
      private $valid_joins;
    
      // Valid filter operators
      private $valid_operators = array(
        '=' => " = '%s'",
        'LIKE' => " LIKE '%%%s%%'"
      );
    
      // Valid fields and their true database names
      private $valid_fields = array(
        'id' => array('hc', 'id'),
        'title' => array('hc', 'title', TRUE),
        'content_type' => array('hl', 'title', TRUE),
        'created_at' => array('hc', 'created_at'),
        'updated_at' => array('hc', 'updated_at'),
        'user_id' => array('u', 'ID'),
        'user_name' => array('u', 'display_name', TRUE),
        // MY CUSTOM FIELD
        'a' => array('a', 'id_file')
      );
    
      private $fields, $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",
          'u' => " LEFT JOIN {$wpdb->base_prefix}users u ON hc.user_id = u.ID",
          // MY CUSTOM JOIN
          'a' => " LEFT JOIN {$wpdb->prefix}pdf_activities a ON hc.id = a.id_h5p_content"
        );
    
        $this->join = array();
    
        // Start adding fields
        $this->fields = '';
        foreach ($fields as $field) {
          if (!isset($this->valid_fields[$field])) {
            throw new Exception('Invalid field: ' . $field);
          }
    
          $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 .= ', ';
          }
          $this->fields .= $table . '.' . $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] . '.' . $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) {
          $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->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}
          {$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);
        }
        return $wpdb->get_results($query);
      }
    
      /**
       * Total number of matches. Useful for pagination.
       *
       * @since 1.5.3
       * @return int
       */
      public function get_total() {
        global $wpdb;
    
        $query = "SELECT COUNT(hc.id)
          FROM {$this->base_table}
          {$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);
      }
    }

    Thanks a lot!

    Plugin Author icc0rz

    (@icc0rz)

    I’m sorry I think I misunderstood what you were trying to achieve. If you only want to insert a link that triggers your JavaScript, you won’t have to override the query class since you’ll only be modifying the UI, i.e. you won’t be adding any data.

    Unforently there aren’t many events/hooks into the data view table, so you’ll need to do something like this in a custom JavaScript:

    (function ($) {
      var pdfHandler = function (h5pContentId) {
        // Replace with code that opens your dialog etc.
        console.log('Opening dialog for H5P ' + h5pContentId);
      };
      var addedHeader = false;
    
      $(document).ajaxComplete(function(event, xhr, settings) {
        if (settings.url.indexOf('wp-admin/admin-ajax.php?action=h5p_contents') !== -1) {
          // Use setTimeout to run after AJAX
          setTimeout(function () {
            if (!addedHeader) {
              // Update headers
              $('#h5p-contents thead tr').append('<th class="h5p-edit-link"></th>');
              var $footTd = $('#h5p-contents tfoot td');
              $footTd.attr('colspan', parseInt($footTd.attr('colspan')) + 1);
              addedHeader = true;
            }
    
            // Add pdf button to each row
            $('#h5p-contents tbody tr').each(function () {
              var id = $(this).find('a:first').attr('href').match(/&id=(\d+)/)[1];
              $('<td/>', {
                'class': 'my-custom-pdf-button',
                tabIndex: 0,
                text: 'PDF',
                appendTo: this,
                on: {
                  click: function ()?{
                    pdfHandler(id);
                  },
                  keydown: function (event)?{
                    if (event.which === 32) {
                      pdfHandler(id);
                    }
                  }
                }
              });
            });
          }, 0);
        }
      });
    })(H5P.jQuery);

    You can also add the link by creating your own version of H5PContentAdmin::ajax_contents, but you’ll still need JS to activate it so it’ll be some more work.

    I hope this will be of some use to you or others looking to do the same.

    Thread Starter Vincenzo Casu

    (@vincent06)

    Thanks for your help. This sounds a lot better. I do tests and let you know. see you soon!

    Thread Starter Vincenzo Casu

    (@vincent06)

    I’m trying to load the script, but console appears this error:

    Uncaught ReferenceError: H5P is not defined(anonymous function) @ integration.js?ver=3.9.3:2

    I have enqueued my custom script with this code:

    function integration_h5p_enqueue($hook) {
        wp_enqueue_script( 'integration_h5p_script', plugin_dir_url( __FILE__ ) . '/js/integration.js', true );
    }
    add_action( 'admin_enqueue_scripts', 'integration_h5p_enqueue', 20 );

    I think it’s due to the fact that the script is loaded before the h5p js core.

    Plugin Author icc0rz

    (@icc0rz)

    Yes, you must make sure script is loaded after the H5P scripts.

    Thread Starter Vincenzo Casu

    (@vincent06)

    Now it is in the footer, but not under h5p scripts. I’m going crazy, I can not even set a dependency for my custom js, because your scripts are not registered, but only queued.

    How can I do?

    Plugin Author icc0rz

    (@icc0rz)

    You must specify $deps. I don’t think the script needs to be registered. This should work:

    wp_enqueue_script('yourpluginslug-h5p-integration', plugins_url('yourplugin/scripts/h5p-integration.js'), array('h5p-jquery'), Your_Plugin::VERSION);

    Thread Starter Vincenzo Casu

    (@vincent06)

    That’s work very well! You’re great!

    Best regards!

    Plugin Author icc0rz

    (@icc0rz)

    Very good to hear! It’s fun to see people using and adapting the plugin to fit their needs ??

Viewing 10 replies - 1 through 10 (of 10 total)
  • The topic ‘Extending plugin’ is closed to new replies.