Fortunately the module gives us some hooks to create our customized list of images. So let’s create a custom module for that.

1. Create a new folder

Create a new folder in the site/all/modules/custom/mediafilter for our module's files.

2. Create the .info file

Add the following code to the newly created mediafilter.info file.

  1. name = Mediafilter
  2. description = Mediafilter module contains a custom media browser plugin to filter the displayed images.
  3. core = 7.x
  4. package = Media
  5. dependencies[] = media (1.x)

3. Create the main file

Create a file called mediafilter.module. Once you have created the mediafilter.module file you can enable the module.

4. Let’s do some coding!

Media module provides two hooks to add custom tabs to the media popup. The hook_media_browser_plugin_info defines the list of created plugins and the hook_media_browser_plugin_view sets the additional settings of the widget.

  1. /**
  2.  * Implements hook_media_browser_plugin_info().
  3.  */
  4. function mediafilter_media_browser_plugin_info() {
  5.   return array(
  6.     'mediafilter' => array(
  7.       '#weight' => 10,
  8.     )
  9.   );
  10. }
  11.  
  12. /**
  13.  * Implements hook_media_browser_plugin_view().
  14.  */
  15. function mediafilter_media_browser_plugin_view($plugin_name, $params) {
  16.   $path = drupal_get_path('module', 'mediafilter');
  17.  
  18.   $params += array(
  19.     'types' => array(),
  20.     'multiselect' => FALSE,
  21.   );
  22.  
  23.   // The multiselect parameter is a string.  So we check to see if it is set and
  24.   // adjust the local variable accordingly.
  25.   if ($params['multiselect'] != 'false' && $params['multiselect'] !== FALSE) {
  26.     $params['multiselect'] = TRUE;
  27.   }
  28.   switch ($plugin_name) {
  29.     case 'mediafilter':
  30.       return array(
  31.         '#title' => t('Library filter'),
  32.         '#attached' => array(
  33.           'js' => array(
  34.             $path . '/js/mediafilter.media.library.js',
  35.           ),
  36.           'css' => array(
  37.             $path . '/css/mediafilter.media.library.css',
  38.           ),
  39.         ),
  40.         '#settings' => array(
  41.           'viewMode' => 'thumbnails',
  42.           'getMediaUrl' => url('mediafilter/media/browser/list'),
  43.         ) + $params,
  44.         'prefix' => array(
  45.           '#markup' => '<div id="container"><div id="scrollbox">', ),
  46.         'filter' => drupal_get_form('mediafilter_browser_filter_form'),
  47.         'content' => array(
  48.           '#markup' => '<ul class="media-list-thumbnails" id="media-browser-custom-library-list"></ul>',
  49.         ),
  50.         'status' => array(
  51.           '#markup' => '<div id="mediafilter-status"></div>',
  52.         ),
  53.         'suffix' => array(
  54.           '#markup' => '</div></div>'
  55.         ),
  56.       );
  57.   }
  58. }

The main part of the code is a customization of the media module default library browser widget. Take a look at the source code in the media module’s includes/media.browser.inc file. The name of our widget is mediafilter. The title of the widget is set to “Library filter”. We attach a javascript and a css file to our widget. The base structure of both comes from the media’s library widget. There the #settings key contains the default settings of the widget. The ‘viewMode’ obviously stores the image style of the listed images and the ‘getMediaurl’ an URL which returns to the HTML of the images list. The #markup contains the HTML of the image browser container. In our case the markup renders the output of the filter form (mediafilter_browser_filter). Additional information about media hooks are found in the media.api.inc file.

5. Add the filter form

In this case we would like to filter the list of images by site users.

  1. /**
  2.  * Creates a form for media filter.
  3.  */
  4. function mediafilter_browser_filter_form($form, &$form_state) {
  5.   $users = entity_load('user');
  6.   $users_option_list = mediafilter_users_option_list($users);
  7.  
  8.   $form['mediafilter'] = array(
  9.     '#type' => 'fieldset',
  10.     '#title' => t('Filter results'),
  11.     '#weight' => 5,
  12.     '#collapsible' => FALSE,
  13.   );
  14.   $form['mediafilter']['users']  = array(
  15.     '#type' => 'checkboxes',
  16.     '#title' => t('Filter by username'),
  17.     '#options' => $users_option_list,
  18.   );
  19.  
  20.   return $form;
  21. }
  22.  
  23. /**
  24.  * Creates an associative array of users.
  25.  * 
  26.  * @param $users
  27.  *   The full list of the site's user objects.
  28.  * 
  29.  * @return array
  30.  *   Returns an associative array of users $user[$user->uid] = $user->username
  31.  */
  32. function mediafilter_users_option_list($users) {
  33.   $return = array();
  34.   foreach ($users as $user) {
  35.     $return[$user->uid] = ($user->uid == 0) ? variable_get('anonymous', t('Anonymous')) : $user->name;
  36.   }
  37. return $return;
  38. }

So, it is time to take a look at our widget.

6. Create a menu callback function.

As you can see we have managed to display our browser widget and the filter form but no images are displayed. So it is time to fetch the images. The heart of the basic Library widget is an ajax callback from media.library.js. We keep the same logic in our module as well, and we continue to create the menu callback function which returns to the list of images. Implementation of the hook_menu function.

  1. /**
  2.  * Implements hook_menu().
  3.  */
  4. function mediafilter_menu() {
  5.   $items = array();
  6.  
  7.   $items['mediafilter/media/browser/list'] = array(
  8.     'title' => 'Custom mediafilter',
  9.     'description' => 'Ajax Callback for getting custom media files',
  10.     'page callback' => 'mediafilter_media_browser_list',
  11.     'access callback' => 'mediafilter_access',
  12.     'access arguments' => array('edit'),
  13.     'type' => MENU_CALLBACK,
  14.     'file' => 'includes/mediafilter.media.browser.inc',
  15.   );
  16.  
  17.   return $items;
  18. }
  19.  
  20. /**
  21.  * Access callback for media assets.
  22.  */
  23. function mediafilter_access($op) {
  24.   return (user_access('access user profiles') || user_access($op . ' media'));
  25. }

We set a custom access control to see if the user has privilege to edit media files and access to user profiles, since we want to filter our files by users. Note that the returned item’s key is the same as the widget's getMediaUrl value. The implementation of the menu callback function is put in the file specified as $items[‘file’]. Create a new file in the included subfolder of our module root with the name mediafilter.media.browser.inc, and copy the whole media_browser_list() function from media module includes/media.browser.inc. Make sure you rename the function, in this case to mediafilter_media_browser_list(). Now that we have created the function we are going to use some code from the media module media.browser.inc file so we need to include this by adding the following line somewhere at the top of the function's body.

  1. module_load_include('inc', 'media', '/includes/media.browser');

We are going to make further modifications to this file later. Next step is to create a javascript file which will handle the ajax loading. The base of our js file comes from media module’s js/plugins/media.library.js file. Copy the js/plugins/media.library.js and js/plugins/media.library.css from media module in our module’s js respective css folder and rename them to mediafilter.media.library.js and mediafilter.media.css. Some additional modifications need to be done on these files, see them in the attachment. If we are finished with the customization, we already have a more or less working image browser. If no images show up, make sure you clear your cache. The new menu item needs to be added to the system.

7. Add an event listener

Ok, now all the images are loading and no filters are working. We need to add our own event listener to act on the checkbox's change. The full code is added to the mediafilter.media.library.js file (see Drupal.behaviors.mediafilterMediaLibraryFilter line 22-48).

  1. Drupal.behaviors.mediafilterMediaLibraryFilter = {
  2.     attach: function (context, settings) {
  3.         $('#mediafilter-browser-filter-form input').bind('change', function (event, ui) {
  4.             var library = new Drupal.media.browser.mediafilter(Drupal.settings.media.browser.mediafilter);
  5.             var selected_checkboxes = $('input:checked');
  6.             var checkboxes = {};
  7.             var panel = $('#media-tab-mediafilter');
  8.  
  9.             selected_checkboxes.each(function( index, value ) {
  10.                 var field_name = $(value).attr('name').split('[');
  11.                 var field = field_name[0];
  12.                 if (!checkboxes[field]) {
  13.                     checkboxes[field] = {};
  14.                 }
  15.                 checkboxes[field][index] = $(value).attr('value');
  16.             });
  17.             var params = {};
  18.             for (var parameter in Drupal.settings.media.browser.mediafilter) {
  19.                 params[parameter] = Drupal.settings.media.browser.mediafilter[parameter];
  20.             }
  21.             params['selection'] = checkboxes;
  22.  
  23.             library.start($(panel), params);
  24.             $('#scrollbox').bind('scroll', library, library.scrollUpdater);
  25.         });
  26.     }
  27. };

8. Add a condition to the callback

Only one thing is left: we need to add our custom condition to our ajax callback function (mediafilter_media_browser_list()) right before the $query object is executed:

  1.  if (isset($params['selection']['users']) && is_array($params['selection']['users'])) {
  2.     $query->condition('f.uid', $params['selection']['users'], 'IN');
  3.   }

This snippet adds an extra condition which can be TRUE if the user ID attached to the file object is in the array of the selected users. The full code of the tutorial can be found in the attachment. Any comments are more than welcome.