Title: Extending plugin
Last modified: August 30, 2016

---

# Extending plugin

 *  Resolved [Vincenzo Casu](https://wordpress.org/support/users/vincent06/)
 * (@vincent06)
 * [10 years, 8 months ago](https://wordpress.org/support/topic/extending-plugin-2/)
 * 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://wordpress.org/plugins/h5p/](https://wordpress.org/plugins/h5p/)

Viewing 10 replies - 1 through 10 (of 10 total)

 *  Plugin Author [icc0rz](https://wordpress.org/support/users/icc0rz/)
 * (@icc0rz)
 * [10 years, 8 months ago](https://wordpress.org/support/topic/extending-plugin-2/#post-6586007)
 * 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](https://wordpress.org/support/users/vincent06/)
 * (@vincent06)
 * [10 years, 8 months ago](https://wordpress.org/support/topic/extending-plugin-2/#post-6586012)
 * 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 <contact@joubel.com>
        * @license   MIT
        * @link      http://joubel.com
        * @copyright 2015 Joubel
        */
   
       /**
        * CUSTOM H5P Content Query class
        *
        * @package H5P_Plugin_Admin
        * @author Joubel <contact@joubel.com>
        */
       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](https://wordpress.org/support/users/icc0rz/)
 * (@icc0rz)
 * [10 years, 8 months ago](https://wordpress.org/support/topic/extending-plugin-2/#post-6586061)
 * 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](https://wordpress.org/support/users/vincent06/)
 * (@vincent06)
 * [10 years, 8 months ago](https://wordpress.org/support/topic/extending-plugin-2/#post-6586063)
 * Thanks for your help. This sounds a lot better. I do tests and let you know. 
   see you soon!
 *  Thread Starter [Vincenzo Casu](https://wordpress.org/support/users/vincent06/)
 * (@vincent06)
 * [10 years, 8 months ago](https://wordpress.org/support/topic/extending-plugin-2/#post-6586067)
 * 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](https://wordpress.org/support/users/icc0rz/)
 * (@icc0rz)
 * [10 years, 8 months ago](https://wordpress.org/support/topic/extending-plugin-2/#post-6586068)
 * Yes, you must make sure script is loaded after the H5P scripts.
 *  Thread Starter [Vincenzo Casu](https://wordpress.org/support/users/vincent06/)
 * (@vincent06)
 * [10 years, 8 months ago](https://wordpress.org/support/topic/extending-plugin-2/#post-6586071)
 * 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](https://wordpress.org/support/users/icc0rz/)
 * (@icc0rz)
 * [10 years, 8 months ago](https://wordpress.org/support/topic/extending-plugin-2/#post-6586073)
 * 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](https://wordpress.org/support/users/vincent06/)
 * (@vincent06)
 * [10 years, 8 months ago](https://wordpress.org/support/topic/extending-plugin-2/#post-6586093)
 * That’s work very well! You’re great!
 * Best regards!
 *  Plugin Author [icc0rz](https://wordpress.org/support/users/icc0rz/)
 * (@icc0rz)
 * [10 years, 8 months ago](https://wordpress.org/support/topic/extending-plugin-2/#post-6586094)
 * 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.

 * ![](https://ps.w.org/h5p/assets/icon.svg?rev=986118)
 * [Interactive Content – H5P](https://wordpress.org/plugins/h5p/)
 * [Frequently Asked Questions](https://wordpress.org/plugins/h5p/#faq)
 * [Support Threads](https://wordpress.org/support/plugin/h5p/)
 * [Active Topics](https://wordpress.org/support/plugin/h5p/active/)
 * [Unresolved Topics](https://wordpress.org/support/plugin/h5p/unresolved/)
 * [Reviews](https://wordpress.org/support/plugin/h5p/reviews/)

 * 10 replies
 * 2 participants
 * Last reply from: [icc0rz](https://wordpress.org/support/users/icc0rz/)
 * Last activity: [10 years, 8 months ago](https://wordpress.org/support/topic/extending-plugin-2/#post-6586094)
 * Status: resolved