media.types.inc 10.1 KB
Newer Older
1
<?php
aaronwinborn's avatar
aaronwinborn committed
2 3 4 5
// $Id$

/**
 *  @file
6 7 8
 *  Define Media entity types, also provides support for the base types.
 *
 *  See media.install for a list of the base types.
9
 *  @TODO: Make media types have their own table.
aaronwinborn's avatar
aaronwinborn committed
10 11
 */

12 13 14 15 16
/**
 * Implement hook_entity_info().
 *
 */
function media_entity_info() {
JacobSingh's avatar
JacobSingh committed
17
  $view_modes = media_field_view_modes('media');
18 19 20
  $return = array(
    'media' => array(
      'label' => t('Media'),
21 22
  	  'entity class' => 'Media',
      'controller class' => 'MediaEntityController',
23 24
      'base table' => 'file',
      'fieldable' => TRUE,
JacobSingh's avatar
JacobSingh committed
25
      'view modes' => $view_modes,
26 27
      'object keys' => array(
        'id' => 'fid',
aaronwinborn's avatar
aaronwinborn committed
28
        'bundle' => 'type', // @TODO: Why 'type' here...
29 30
      ),
      'bundle keys' => array(
31
        'bundle' => 'name', // @TODO: But 'name' here?
32 33 34 35 36 37 38 39 40 41
      ),
      'bundles' => array(),
    ),
  );

  $media_types = media_type_get_types();

  foreach ($media_types as $type => $bundle_info) {
    $return['media']['bundles'][$type] = (array)$bundle_info;
    $return['media']['bundles'][$type]['admin'] = array(
42
      'label' => $bundle_info->label,
43 44 45
      'path' => 'admin/structure/media/manage/%media_type',
      'real path' => 'admin/structure/media/manage/' . $type,
      'bundle argument' => 4,
aaronwinborn's avatar
aaronwinborn committed
46
      // @TODO: Add an 'administer media types' perm?
47 48 49
      'access arguments' => array('administer site configuration'),
    );
  }
aaronwinborn's avatar
aaronwinborn committed
50 51

  // @TODO: Do we need to define search-related view modes for attached fields?
52 53 54 55
  return $return;
}

/**
aaronwinborn's avatar
aaronwinborn committed
56 57 58 59
 *  Update an existing media type or create a new one.
 *
 *  The default media types are currently 'Audio', 'Image', 'Video', and
 *  'Other', which are defined in media_install().
60
 *
61
 *  @param object &$type
aaronwinborn's avatar
aaronwinborn committed
62
 *    $type is an object with the following fields:
63
 *      ->name => The name of the media asset type, such as 'video';
aaronwinborn's avatar
aaronwinborn committed
64
 *      ->label => The human readable name;
65
 *      ->base => boolean: If the media type cannot be removed.
aaronwinborn's avatar
aaronwinborn committed
66 67
 *      ->type_callback => Function call to filter an instance to its bundle.
 *      ->type_callback_args => An array of args to be passed to type_callback.
aaronwinborn's avatar
aaronwinborn committed
68
 *  @return void;
69
 */
70
function media_type_save(&$type) {
71 72 73 74
  if (empty($type->name)) {
    throw new Exception('Enable to add type, name not provided');
  }

75
  $type = media_type_set_defaults($type);
76 77 78 79 80 81 82 83 84 85
  if (!is_array($type->type_callback_args)) {
    throw new Exception('type_callback_args should be an array');
  }

  $type->type_callback_args = serialize($type->type_callback_args);

  $ret = db_merge('media_type')
    ->key(array('name' => $type->name))
    ->fields((array)$type)
  ->execute();
aaronwinborn's avatar
aaronwinborn committed
86

87
  media_type_configure_fields($type);
88

89 90 91
  // Clear the caches
  drupal_static_reset('media_type_get_types');
  drupal_static_reset('media_type_get_mime_map');
92
  return;
93 94
}

95 96


97 98 99 100 101 102
/**
 * Assigns formatters to view_modes for the file field of a media type.
 *
 * Each media type contains a field called "file" and certain stock view modes.
 * This function will assign a format to each view mode. For instance, images
 * would want to use a thumbnail format for files when in the "preview"
103
 * view_mode.  The called would pass in 'image' for $name and
104 105
 * array('preview' => 'styles_file_square_thumbnail') to $view_modes_to_formatters.
 *
106
 * @param string $name
107 108 109 110
 * @param array $view_modes_to_formatters
 * @throws Exception
 * @return void
 */
111 112
function media_type_configure_formatters($name, $view_modes_to_formatters) {
  $instance = field_info_instance('media', 'file', $name);
113
  if (!$instance) {
114
    throw new Exception('Unable to set formatter preferences for '. $name);
115 116 117 118 119 120 121 122 123
  }
  foreach ($instance['display'] as $view_mode => $display) {
    if (isset($view_modes_to_formatters[$view_mode])) {
      $instance['display'][$view_mode]['type'] = $view_modes_to_formatters[$view_mode];
    }
  }
  field_update_instance($instance);
}

124 125 126
/**
 * Loads a media type based on its machine name.
 *
127
 * @param string $name
128 129
 * @return StdClass
 */
130
function media_type_load($name) {
131
  $types = media_type_get_types();
132 133
  if (isset($types[$name])) {
    return $types[$name];
134 135 136
  }
}

137 138 139 140
/**
 * Sets up the default fields which a media type needs.
 *
 * Currently, this is just a "file" field which contains the reference to the actual file.
aaronwinborn's avatar
aaronwinborn committed
141
 * @TODO Do we also want to make the file 'description/caption' a field?
142 143 144 145 146 147
 *
 * @param StdClass $media_type
 * @return void
 */
function media_type_configure_fields($media_type) {
  $field = field_info_field('file');
148
  $instance = field_info_instance('media', 'file', $media_type->name);
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

  if (empty($field)) {
    $field = array(
      'field_name' => 'file',
      'type' => 'file',
    );
    $field = field_create_field($field);
  }

  // Perhaps the file field exists already, if so skip.
  if (empty($instance)) {
    $weight = -5;
    $instance = array(
      'field_name' => 'file',
      'object_type' => 'media',
164
      'bundle' => $media_type->name,
165 166 167 168 169 170 171 172
      'label' => 'File',
      'widget_type' => 'file_file',
      'widget' => array(
        'weight' => $weight,
      ),
      'required' => TRUE,
      'locked' => TRUE,
      'display' => array(
173
        'media_preview' => array(
174
          'label' => 'hidden',
175
          'type' => 'file_default',
176 177
          'weight' => $weight,
        ),
178
        'media_original' => array(
179
          'label' => 'hidden',
180
          'type' => 'file_default',
181 182 183 184 185 186 187 188
          'weight' => $weight,
        ),
      ),
    );
    field_create_instance($instance);
  }
}

189
/**
aaronwinborn's avatar
aaronwinborn committed
190 191
 *  Loads all media types into an array keyed by machine name and sorted
 *  and weighted lexographically.
192 193 194 195 196 197 198
 *
 * @return array
 *  Media types keyed by machine name.
 */
function media_type_get_types() {
  $types =& drupal_static(__FUNCTION__);
  if (!$types) {
199 200 201 202 203 204 205 206 207
    $types = db_select('media_type', 'mt')
      ->orderBy('weight')
      ->fields('mt')
      ->execute()
      ->fetchAllAssoc('name'); // Will key by the name field.
    foreach ($types as &$type) {
      // I really hate this.
      $type->type_callback_args = unserialize($type->type_callback_args);
    }
208
  }
209

210 211 212
  return $types;
}

aaronwinborn's avatar
aaronwinborn committed
213 214 215
/**
 *  Create the basic class and defaults for a media entity bundle type.
 */
216 217
function media_type_set_defaults($info) {
  $type = new StdClass();
aaronwinborn's avatar
aaronwinborn committed
218 219

  // This is used to filter a file to the proper bundle.
220 221 222 223 224 225 226 227 228 229 230
  $type->type_callback = 'media_is_type';
  $type->type_callback_args = array();
  $type->weight = 0;

  foreach ($info as $k => $v) {
    $type->{$k} = $v;
  }

  return $type;
}

231
/**
232
 * Determines the type of media a passed in $file is.
233
 *
234 235 236 237 238 239 240 241 242 243 244
 * @todo: integrate this properly with other APIs in media when fields is done
 * @param unknown_type $file
 * @return unknown_type
 */
function media_get_type($file) {
  $types = media_type_get_types();
  foreach ($types as $name => $type) {
    if (call_user_func_array($type->type_callback, array($file, $type->type_callback_args))) {
      return $name;
    }
  }
245
  throw new Exception('Unable to determine type of media from ' . var_export($file, 1));
246 247 248
}

/**
249
 * Default callback used to determine if a piece of media is of a given type.
250
 *
251 252
 * @TODO: document 'any' and 'all' matching.
 *
253 254 255
 * @param $media
 *   The media file asset object.
 * @param $args
aaronwinborn's avatar
aaronwinborn committed
256
 *
257
 * @return unknown_type
258
 */
259 260 261 262 263 264 265 266 267 268 269 270 271 272
function media_is_type($media, $args) {
  $match_type = !empty($args['match_type']) ? 'any' : $args['match_type'];
  $match_all = $match_type == 'all';
  if (!empty($args['mimetypes'])) {
    foreach ($args['mimetypes'] as $expression) {
      if (preg_match($expression, $media->filemime)) {
        if (!$match_all) {
          return TRUE;
        }
      }
    }
    // Was not matched, so return
    if ($match_all) {
      return FALSE;
273 274
    }
  }
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289

  if (!empty($args['extensions'])) {
    if (in_array(pathinfo($media->uri, PATHINFO_EXTENSION), $args['extensions'])) {
      if (!$match_all) {
        return TRUE;
      }
    }
    // Was not matched, so return
    if ($match_all) {
      return FALSE;
    }
  }

  if (!empty($args['streams'])) {
    // @TODO: Write this.
aaronwinborn's avatar
aaronwinborn committed
290 291
    // Note that this will ultimately need to be above mimetypes and extensions
    // so that we can allow for things like flickr photos vs. slideshows, etc.
292
  }
aaronwinborn's avatar
aaronwinborn committed
293
}
294 295 296 297 298 299 300 301

/**
 * Implement hook_media_format_form_prepare_alter
 * @return unknown_type
 */
function media_media_format_form_prepare_alter(&$form, &$form_state, $media) {
  switch($media->type) {
    case 'image':
302 303
      // @TODO: Isn't there a $file->description?
      $description = $media->filename;
304 305 306
      $form['options']['alt'] = array(
        '#type' => 'textfield',
        '#title' => t('Description'),
307
        '#default_value' => $description,
308 309 310 311 312 313
      );
      break;
  }
}


314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
class Media extends EntityDb {
  public function __construct($values = array()) {
    parent::__construct($values, 'media');
  }
}

class MediaEntityController extends EntityAPIController {
  
  public $customConditions = array();
  
  /**
   * Wrapper around the DB_API to allow conditions which are not ==
   * 
   * @param $key
   * @param $value
   * @param $operator
   * @return unknown_type
   */
  public function addCondition($key, $value, $operator = NULL) {
    $this->customConditions = array($key, $value, $operator);
  }
  
  /**
   * Overloaded (and this sucks).  $conditions can be an array or key=>value pairs or DBNTG conditions.
   *  
   * @see sites/all/modules/media/includes/EntityAPIController#load($ids, $conditions)
   */
  public function load($ids = array(), $conditions = array()) {
    // Conditions using the = (equals) operator.
    $normal_conditions = array();
    foreach ($conditions as $condition) {
      if (is_array($condition)) {
        $this->customConditions[] = array($condition[0], $condition[1], $condition[2]);
        // We can't use caching in this case
        $this->cache = FALSE;
      }
      else {
        $normal_conditions[] = $condition;  
      }
    }
    // Unfortuantely, the load method won't query unless there is at least one condition
    // This is a bad pattern and undocumented side-effect.
    // @todo: refactor this.
    if (!$normal_conditions && $this->customConditions) {
      $normal_conditions['status'] = TRUE;
    }
    return parent::load($ids, $normal_conditions);
  }
  
  public function buildQuery() {
    parent::buildQuery();
    foreach ($this->customConditions as $condition) {
      $this->query->condition($condition[0], $condition[1], $condition[2]);
    }
  }
}