first import

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-08 11:40:19 +02:00
commit 1bc61b12ad
8435 changed files with 1582817 additions and 0 deletions

View File

@@ -0,0 +1,494 @@
<?php
/**
* A base class for Resource Stream Wrappers.
*
* This class provides a complete stream wrapper implementation. It passes
* incoming URL's through an interpolation method then recursively calls
* the invoking PHP filesystem function.
*
* MediaReadOnlyStreamWrapper implementations need to override at least the
* interpolateUrl method to rewrite the URL before is it passed back into the
* calling function.
*/
abstract class MediaReadOnlyStreamWrapper implements DrupalStreamWrapperInterface {
protected $parameters = array();
protected $base_url = NULL;
private $_DEBUG_MODE = NULL;
public function get_parameters() {
return $this->parameters;
}
// As part of the inode protection mode returned by stat(), identifies the
// file as a regular file, as opposed to a directory, symbolic link, or other
// type of "file".
// @see http://linux.die.net/man/2/stat
const S_IFREG = 0100000;
/**
* "Template" for stat calls. All elements must be initialized.
* @var array
*/
protected $_stat = array(
0 => 0, // device number
'dev' => 0,
1 => 0, // inode number
'ino' => 0,
// inode protection mode. file_unmanaged_delete() requires is_file() to
// return TRUE.
2 => self::S_IFREG,
'mode' => self::S_IFREG,
3 => 0, // number of links
'nlink' => 0,
4 => 0, // userid of owner
'uid' => 0,
5 => 0, // groupid of owner
'gid' => 0,
6 => -1, // device type, if inode device *
'rdev' => -1,
7 => 0, // size in bytes
'size' => 0,
8 => 0, // time of last access (Unix timestamp)
'atime' => 0,
9 => 0, // time of last modification (Unix timestamp)
'mtime' => 0,
10 => 0, // time of last inode change (Unix timestamp)
'ctime' => 0,
11 => -1, // blocksize of filesystem IO
'blksize' => -1,
12 => -1, // number of blocks allocated
'blocks' => -1,
);
function interpolateUrl() {
if ($parameters = $this->get_parameters()) {
return $this->base_url . '?' . http_build_query($parameters);
}
}
/**
* Returns a web accessible URL for the resource.
*
* This function should return a URL that can be embedded in a web page
* and accessed from a browser. For example, the external URL of
* "youtube://xIpLd0WQKCY" might be
* "http://www.youtube.com/watch?v=xIpLd0WQKCY".
*
* @return
* Returns a string containing a web accessible URL for the resource.
*/
public function getExternalUrl() {
return $this->interpolateUrl();
}
/**
* Base implementation of getMimeType().
*/
static function getMimeType($uri, $mapping = NULL) {
return 'application/octet-stream';
}
/**
* Base implementation of realpath().
*/
function realpath() {
return $this->getExternalUrl();
}
/**
* Stream context resource.
*
* @var Resource
*/
public $context;
/**
* A generic resource handle.
*
* @var Resource
*/
public $handle = NULL;
/**
* Instance URI (stream).
*
* A stream is referenced as "scheme://target".
*
* @var String
*/
protected $uri;
/**
* Base implementation of setUri().
*/
function setUri($uri) {
$this->uri = $uri;
$this->parameters = $this->_parse_url($uri);
}
/**
* Base implementation of getUri().
*/
function getUri() {
return $this->uri;
}
/**
* Report an error.
* @param $message
* The untranslated string to report.
* @param $options
* An optional array of options to send to t().
* @param $display
* If TRUE, then we display the error to the user.
* @return
* We return FALSE, since we sometimes pass that back from the reporting
* function.
*/
private function _report_error($message, $options = array(), $display = FALSE) {
watchdog('resource', $message, $options, WATCHDOG_ERROR);
if ($display) {
drupal_set_message(t($message, $options), 'error');
}
return FALSE;
}
private function _debug($message, $type = 'status') {
if ($this->_DEBUG_MODE) {
drupal_set_message($message, $type);
}
}
/**
* Returns an array of any parameters stored in the URL's path.
* @param $url
* The URL to parse, such as youtube://v/[video-code]/t/[tags+more-tags].
* @return
* An associative array of all the parameters in the path,
* or FALSE if the $url is ill-formed.
*/
protected function _parse_url($url) {
$path = explode('://', $url);
$parts = explode('/', $path[1]);
$params = array();
$count = 0;
$total = count($parts);
if (!$total || ($total % 2)) {
// If we have no parts, or an odd number of parts, it's malformed.
return FALSE;
}
while ($count < $total) {
// We iterate count for each step of the assignment to keep us honest.
$params[$parts[$count++]] = $parts[$count++];
}
return $params;
}
/**
* Support for fopen(), file_get_contents(), file_put_contents() etc.
*
* @param $path
* A string containing the path to the file to open.
* @param $mode
* The file mode ("r", "wb" etc.).
* @param $options
* A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS.
* @param &$opened_path
* A string containing the path actually opened.
* @return
* TRUE if file was opened successfully.
*/
public function stream_open($url, $mode, $options, &$opened_url) {
$this->_debug(t('Stream open: %url', array('%url' => $url)));
// We only handle Read-Only mode by default.
if ($mode != 'r' && $mode != 'rb') {
return $this->_report_error('Attempted to open %url as mode: %mode.', array('%url' => $url, '%mode' => $mode), ($options & STREAM_REPORT_ERRORS));
}
// We parse a URL as youtube://v/dsyiufo34/t/cats+dogs to store
// the relevant code(s) in our private array of parameters.
$this->parameters = $this->_parse_url($url);
if ($this->parameters === FALSE) {
return $this->_report_error('Attempted to parse an ill-formed url: %url.', array('%url' => $url), ($options & STREAM_REPORT_ERRORS));
}
if ((bool)$this->parameters && ($options & STREAM_USE_PATH)) {
$opened_url = $url;
}
$this->_debug(t('Stream opened: %parameters', array('%parameters' => print_r($this->parameters, TRUE))));
return (bool)$this->parameters;
}
// Undocumented PHP stream wrapper method.
function stream_lock($operation) {
return FALSE;
}
/**
* Support for fread(), file_get_contents() etc.
*
* @param $count
* Maximum number of bytes to be read.
* @return
* The string that was read, or FALSE in case of an error.
*/
public function stream_read($count) {
return FALSE;
}
/**
* Support for fwrite(), file_put_contents() etc.
*
* @param $data
* The string to be written.
* @return
* The number of bytes written.
*/
public function stream_write($data) {
return FALSE;
}
/**
* Support for feof().
*
* @return
* TRUE if end-of-file has been reached.
*/
public function stream_eof() {
return FALSE;
}
/**
* Support for fseek().
*
* @param $offset
* The byte offset to got to.
* @param $whence
* SEEK_SET, SEEK_CUR, or SEEK_END.
* @return
* TRUE on success
*/
public function stream_seek($offset, $whence) {
return FALSE;
}
/**
* Support for fflush().
*
* @return
* TRUE if data was successfully stored (or there was no data to store).
*/
public function stream_flush() {
return FALSE;
}
/**
* Support for ftell().
*
* @return
* The current offset in bytes from the beginning of file.
*/
public function stream_tell() {
return FALSE;
}
/**
* Support for fstat().
*
* @return
* An array with file status, or FALSE in case of an error - see fstat()
* for a description of this array.
*/
public function stream_stat() {
return $this->_stat;
}
/**
* Support for fclose().
*
* @return
* TRUE if stream was successfully closed.
*/
public function stream_close() {
return TRUE;
}
/**
* Support for unlink().
*
* @param $uri
* A string containing the uri to the resource to delete.
* @return
* TRUE if resource was successfully deleted.
* @see http://php.net/manual/en/streamwrapper.unlink.php
*/
// public function unlink($uri) {
// $this->uri = $uri;
// return unlink($this->getLocalPath());
// }
/**
* Support for rename().
*
* @param $from_uri,
* The uri to the file to rename.
* @param $to_uri
* The new uri for file.
* @return
* TRUE if file was successfully renamed.
* @see http://php.net/manual/en/streamwrapper.rename.php
*/
// public function rename($from_uri, $to_uri) {
// return rename($this->getLocalPath($from_uri), $this->getLocalPath($to_uri));
// }
/**
* Support for mkdir().
*
* @param $uri
* A string containing the URI to the directory to create.
* @param $mode
* Permission flags - see mkdir().
* @param $options
* A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
* @return
* TRUE if directory was successfully created.
* @see http://php.net/manual/en/streamwrapper.mkdir.php
*/
// public function mkdir($uri, $mode, $options) {
// $this->uri = $uri;
// $recursive = (bool)($options & STREAM_MKDIR_RECURSIVE);
// if ($recursive) {
// // $this->getLocalPath() fails if $uri has multiple levels of directories
// // that do not yet exist.
// $localpath = $this->getDirectoryPath() . '/' . file_uri_target($uri);
// }
// else {
// $localpath = $this->getLocalPath($uri);
// }
// if ($options & STREAM_REPORT_ERRORS) {
// return mkdir($localpath, $mode, $recursive);
// }
// else {
// return @mkdir($localpath, $mode, $recursive);
// }
// }
/**
* Support for rmdir().
*
* @param $uri
* A string containing the URI to the directory to delete.
* @param $options
* A bit mask of STREAM_REPORT_ERRORS.
* @return
* TRUE if directory was successfully removed.
* @see http://php.net/manual/en/streamwrapper.rmdir.php
*/
// public function rmdir($uri, $options) {
// $this->uri = $uri;
// if ($options & STREAM_REPORT_ERRORS) {
// return rmdir($this->getLocalPath());
// }
// else {
// return @rmdir($this->getLocalPath());
// }
// }
/**
* Support for stat().
*
* @param $url
* A string containing the url to get information about.
* @param $flags
* A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
* @return
* An array with file status, or FALSE in case of an error - see fstat()
* for a description of this array.
*/
public function url_stat($url, $flags) {
return $this->stream_stat();
}
/**
* Support for opendir().
*
* @param $url
* A string containing the url to the directory to open.
* @param $options
* Unknown (parameter is not documented in PHP Manual).
* @return
* TRUE on success.
*/
public function dir_opendir($url, $options) {
return FALSE;
}
/**
* Support for readdir().
*
* @return
* The next filename, or FALSE if there are no more files in the directory.
*/
public function dir_readdir() {
return FALSE;
}
/**
* Support for rewinddir().
*
* @return
* TRUE on success.
*/
public function dir_rewinddir() {
return FALSE;
}
/**
* Support for closedir().
*
* @return
* TRUE on success.
*/
public function dir_closedir() {
return FALSE;
}
public function getDirectoryPath() {
return '';
}
/**
* DrupalStreamWrapperInterface requires that these methods be implemented,
* but none of them apply to a read-only stream wrapper. On failure they
* are expected to return FALSE.
*/
public function unlink($uri) {
// Although the remote file itself can't be deleted, return TRUE so that
// file_delete() can remove the file record from the database.
return TRUE;
}
public function rename($from_uri, $to_uri) {
return FALSE;
}
public function mkdir($uri, $mode, $options) {
return FALSE;
}
public function rmdir($uri, $options) {
return FALSE;
}
public function chmod($mode) {
return FALSE;
}
public function dirname($uri = NULL) {
return FALSE;
}
}

View File

@@ -0,0 +1,660 @@
<?php
/**
* @file
* This file contains the admin functions for the Media module.
*/
/**
* Include media.pages.inc since it has some form definitions we will use.
*/
require_once dirname(__FILE__) . '/media.pages.inc';
/**
* Display the list or thumbnails media admin display.
*/
function media_admin($form, $form_state) {
global $user;
$path = drupal_get_path('module', 'media');
$form['#attached'] = array(
'js' => array($path . '/js/media.admin.js'),
'css' => array($path . '/css/media.css'),
);
if (isset($form_state['values']['operation']) && $form_state['values']['operation'] == 'delete') {
$form['#attributes']['class'][] = "media-list-operation";
return media_multiple_delete_confirm($form, $form_state, array_filter($form_state['values']['files']), 'admin/content/media', 'admin/content/media');
}
require_once dirname(__FILE__) . '/media.browser.inc';
media_attach_browser_js($form);
$types = media_display_types();
if (arg(3)) {
$display = arg(3);
if (!in_array($display, array_keys($types))) {
exit(drupal_not_found());
}
// Save their preference.
db_merge('media_list_type')
->key(array('uid' => $user->uid))
->fields(array(
'type' => $display,
))
->execute();
}
else {
$display = db_query("SELECT type FROM {media_list_type} WHERE uid = :uid", array(':uid' => $user->uid))->fetch();
if (!$display) {
$display = 'list';
}
else {
$display = $display->type;
}
}
// Build the display switch.
$form['switch'] = media_admin_display_switch(array('active display' => $display));
// Build the 'Media operations' form.
$options = array();
foreach (module_invoke_all('media_operations') as $operation => $array) {
$options[$operation] = $array['label'];
}
$form['options'] = array(
'#type' => 'fieldset',
'#title' => t('Operations'),
'#prefix' => '<div class="container-inline">',
'#suffix' => '</div>',
'#access' => !empty($options),
);
$form['options']['operation'] = array(
'#type' => 'select',
'#options' => $options,
'#default_value' => 'delete',
);
$form['options']['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
'#submit' => array('media_admin_submit'),
'#validate' => array('media_admin_validate'),
);
include_once $types[$display]['file'];
$form['admin'] = $types[$display]['callback']($form);
return $form;
}
/**
* Form builder: Builds the media list administration overview.
*/
function media_admin_list(&$parent_form) {
// @todo Change to media_variable_get('admin_pager_limit') for consistency
// with browser_pager_limit?
$limit = variable_get('media_admin_limit', 50);
// Build the sortable table header.
$header = array(
'title' => array('data' => t('Title'), 'field' => 'f.filename'),
'type' => array('data' => t('Type'), 'field' => 'f.filemime'),
'size' => array('data' => t('Size'), 'field' => 'f.filesize'),
'author' => array('data' => t('Author'), 'field' => 'u.name'),
'timestamp' => array('data' => t('Updated'), 'field' => 'f.timestamp', 'sort' => 'desc'),
'operations' => array('data' => t('Operations')),
);
$query = db_select('file_managed', 'f')->extend('PagerDefault')->extend('TableSort');
$query->join('users', 'u', 'f.uid = u.uid');
$query->fields('f', array('fid'));
$query->fields('u', array('uid'));
$query->condition('f.status', FILE_STATUS_PERMANENT);
$query->limit($limit);
$query->orderByHeader($header);
foreach (array_keys(media_get_hidden_stream_wrappers()) as $name) {
$query->condition('f.uri', $name . '%', 'NOT LIKE');
}
// Result array keys are file IDs, values are the file owner's UIDs.
$result = $query->execute()->fetchAllKeyed();
// Hide the operations form if there are no files to operate on.
$parent_form['options']['#access'] &= !empty($result);
// Load all the file entities.
$files = $form['#files'] = file_load_multiple(array_keys($result));
// Load all the file owner user entities to display usernames.
$accounts = $form['#accounts'] = user_load_multiple(array_unique($result));
$destination = drupal_get_destination();
$options = array();
foreach ($files as $file) {
$options[$file->fid] = array(
'title' => theme('media_link', array('file' => $file)),
'type' => check_plain($file->filemime),
'size' => format_size($file->filesize),
'author' => theme('username', array('account' => $accounts[$file->uid])),
'timestamp' => format_date($file->timestamp, 'short'),
);
$options[$file->fid]['operations'] = l(t('Edit'), 'media/' . $file->fid . '/edit', array('query' => $destination));
}
$form['files'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
'#empty' => t('No media available.'),
'#attributes' => array('class' => array('media-display-table', 'media-clear')),
);
$form['pager'] = array('#markup' => theme('pager', array('tags' => NULL)));
return $form;
}
/**
* Form builder: Builds the media thumbnails administration overview.
*/
function media_admin_thumbnails(&$parent_form) {
// @todo Change to media_variable_get('admin_pager_limit') for consistency
// with browser_pager_limit?
$limit = variable_get('media_admin_limit', 50);
$query = db_select('file_managed', 'f')->extend('PagerDefault');
$query->fields('f', array('fid'));
$query->condition('f.status', FILE_STATUS_PERMANENT);
$query->limit($limit);
$query->orderBy('f.timestamp', 'DESC');
foreach (array_keys(media_get_hidden_stream_wrappers()) as $name) {
$query->condition('f.uri', $name . '%', 'NOT LIKE');
}
$fids = $query->execute()->fetchCol();
$files = file_load_multiple($fids);
$files = array();
// Hide the operations form if there are no files to operate on.
$parent_form['options']['#access'] &= !empty($files);
if (empty($files)) {
// Display empty text if there are no files.
$form['files'] = array(
'#markup' => '<p>' . t('No media available.') . '</p>',
);
}
else {
$form['files'] = array(
'#tree' => TRUE,
'#prefix' => '<div class="media-display-thumbnails media-clear clearfix"><ul class="media-list-thumbnails">',
'#suffix' => '</ul></div>',
);
foreach ($files as $file) {
$preview = media_get_thumbnail_preview($file, TRUE);
if (!isset($preview['#file'])) { dpm($file); }
$form['files'][$file->fid] = array(
'#type' => 'checkbox',
'#title' => '',
'#prefix' => '<li>' . drupal_render($preview),
'#suffix' => '</li>',
);
}
$form['pager'] = array('#markup' => theme('pager', array('tags' => NULL)));
}
return $form;
}
/**
* Build the display switch portion of the file listings form.
*/
function media_admin_display_switch($options = array()) {
$options += array(
'form location' => 'admin/content/media',
'active display' => 'list',
);
$display_types = media_display_types();
// Build the item list.
$display_items = array();
foreach ($display_types as $delta => $item) {
$attributes = array('title' => $item['description']);
// Set a seperate icon for the active item.
if ($delta == $options['active display']) {
$icon = $item['icon_active'];
$attributes['class'][] = 'active';
}
else {
$icon = $item['icon'];
}
$display_items[] = array(
'data' => l(theme('image', array('path' => $icon, 'alt' => $item['title'])),
$options['form location'] . '/' . $delta,
array(
'html' => TRUE,
'attributes' => $attributes,
)),
);
}
return array(
'#type' => 'markup',
'#markup' => theme('item_list', array(
'items' => $display_items,
'attributes' => array('class' => 'media-display-switch'),
)
),
);
}
/**
* Validate media_admin_list form submissions.
*
* Check if any files have been selected to perform the chosen
* 'Update option' on.
*/
function media_admin_validate($form, &$form_state) {
$files = array_filter($form_state['values']['files']);
if (count($files) == 0) {
form_set_error('', t('No items selected.'));
}
}
/**
* Process media_admin_list form submissions.
*
* Execute the chosen 'Update option' on the selected files.
*/
function media_admin_submit($form, &$form_state) {
$operations = module_invoke_all('media_operations');
$operation = $operations[$form_state['values']['operation']];
// Filter out unchecked nodes
$files = array_filter($form_state['values']['files']);
if ($function = $operation['callback']) {
// Add in callback arguments if present.
if (isset($operation['callback arguments'])) {
$args = array_merge(array($files), $operation['callback arguments']);
}
else {
$args = array($files);
}
call_user_func_array($function, $args);
cache_clear_all();
}
elseif (!empty($operation['redirect'])) {
$fids = implode(' ', array_keys(array_filter($form_state['values']['files'])));
$form_state['redirect'] = array(str_replace('%fids', $fids, $operation['redirect']), array('query' => array('destination' => 'admin/content/media')));
}
else {
// We need to rebuild the form to go to a second step. For example, to
// show the confirmation form for the deletion of nodes.
$form_state['rebuild'] = TRUE;
}
}
/**
* The administration form for managing media types.
*/
function media_admin_type_manage_form($form, &$form_state, $media_type) {
$form = array();
$form['media_type'] = array(
'#type' => 'value',
'#value' => $media_type->name,
);
// If this Media type is handled by us, then we can put in some default
// options. Otherwise, we leave it to the implementing module to form_alter.
if ($media_type->type_callback == 'media_is_type') {
// Options for match_type.
$options = array(
'all' => t('All'),
'any' => t('Any'),
'other' => t('Other'),
);
if ($media_type->type_callback_args['match_type'] && isset($options[$media_type->type_callback_args['match_type']])) {
$default_value = $media_type->type_callback_args['match_type'];
$other_default_value = '';
}
else {
$default_value = 'other';
$other_default_value = $media_type->type_callback_args['match_type'];
}
$form['match_type'] = array(
'#type' => 'radios',
'#title' => t('Match type'),
'#options' => $options,
'#default_value' => $default_value,
);
$form['match_type_other'] = array(
'#type' => 'textfield',
'#title' => t('Other match type value'),
'#default_value' => $other_default_value,
'#attached' => array(
'js' => array(drupal_get_path('module', 'media') . '/js/media.admin.js'),
),
);
// Options for allowed Streams.
$options = array('public' => t('Public files'), 'private' => t('Private files'));
foreach (file_get_stream_wrappers() as $stream => $wrapper) {
$options[$stream] = $wrapper['name'];
}
unset($options['temporary']);
$default_value = array();
if (isset($media_type->type_callback_args['streams'])) {
foreach ($media_type->type_callback_args['streams'] as $stream) {
$default_value[$stream] = $stream;
}
}
$form['streams'] = array(
'#type' => 'checkboxes',
'#title' => t('Allowed streams'),
'#options' => $options,
'#default_value' => $default_value,
);
// Options for allowed mimetypes & extensions.
$default_value = isset($media_type->type_callback_args['mimetypes']) ? implode(' ', $media_type->type_callback_args['mimetypes']) : '';
$form['mimetypes'] = array(
'#type' => 'textfield',
'#title' => t('Allowed mimetypes'),
'#description' => t('You may optionally enter one or more allowed file mimetypes for this Media type, if appropriate, separating each with a space. You may use a regular expression for matching, such as %image_match (which would match any mimetype beginning with %image) or %any_match, which would match any file mimetype.', array('%image_match' => '/^image/', '%image' => t('image'), '%any_match' => '/.*/')),
'#default_value' => check_plain($default_value),
);
$default_value = isset($media_type->type_callback_args['extensions']) ? implode(' ', $media_type->type_callback_args['extensions']) : '';
$form['extensions'] = array(
'#type' => 'textfield',
'#title' => t('Allowed extensions'),
'#description' => t('You may optionally enter one or more allowed file extensions for this Media type, if appropriate, separating each with a space (and no dots).'),
'#default_value' => check_plain($default_value),
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
'#weight' => 100,
);
return $form;
}
function media_admin_type_manage_form_submit($form, &$form_state) {
$media_type = media_type_load($form_state['values']['media_type']);
// Reset all values to empty.
$media_type->type_callback_args = array();
// What is the logic of the match (AND / OR).
if ($form_state['values']['match_type']) {
$media_type->type_callback_args['match_type'] = $form_state['values']['match_type'];
}
else {
$media_type->type_callback_args['match_type'] = $form_state['values']['match_type_other'];
}
// Which streams are valid for this type.
$media_type->type_callback_args['streams'] = array();
foreach ($form_state['values']['streams'] as $stream) {
if ($stream) {
$media_type->type_callback_args['streams'][] = $stream;
}
}
// Which mimetypes are valid for this type.
if (trim($form_state['values']['mimetypes'])) {
$media_type->type_callback_args['mimetypes'] = explode(' ', $form_state['values']['mimetypes']);
array_walk($media_type->type_callback_args['mimetypes'], 'trim');
array_filter($media_type->type_callback_args['mimetypes']);
}
// Which file extensions are valid for this type.
if (trim($form_state['values']['extensions'])) {
$media_type->type_callback_args['extensions'] = explode(' ', $form_state['values']['extensions']);
array_walk($media_type->type_callback_args['extensions'], 'trim');
array_filter($media_type->type_callback_args['extensions']);
}
media_type_save($media_type);
drupal_set_message(t('The @label media type has been saved.', array('@label' => $media_type->label)));
}
/**
* Form callback for mass import.
*/
function media_import($form, &$form_state) {
if (!isset($form_state['storage']['files'])) {
$form_state['storage']['step'] = 'choose';
$form_state['storage']['next_step'] = 'preview';
$form['directory'] = array(
'#type' => 'textfield',
'#title' => t('Directory'),
'#required' => TRUE,
);
$form['pattern'] = array(
'#type' => 'textfield',
'#title' => t('Pattern'),
'#description' => 'Only files matching this pattern will be imported. For example, to import all jpg and gif files, the pattern would be <em>*.jpg|*.gif</em>.',
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Preview')
);
$form['actions']['cancel'] = array(
'#type' => 'link',
'#title' => t('Cancel'),
'#href' => isset($_GET['destination']) ? $_GET['destination'] : 'admin/content/media',
);
}
else {
$form['preview'] = array(
'#markup' => theme('item_list', array('items' => $form_state['storage']['files'])),
);
$form = confirm_form($form, t('Import these files?'), 'admin/content/media/import');
}
return $form;
}
/**
* Validate handler for media_import().
*/
function media_import_validate($form, &$form_state) {
if ($form_state['values']['op'] != t('Confirm')) {
$directory = $form_state['values']['directory'];
$pattern = $form_state['values']['pattern'];
if (!is_dir($directory)) {
form_set_error('directory', t('The provided directory does not exist.'));
}
$pattern = !empty($pattern) ? $pattern : '*';
$files = glob("$directory/$pattern");
if (empty($files)) {
form_set_error('pattern', t('No files were found in %directory matching %pattern', array('%directory' => $directory, '%pattern' => $pattern)));
}
$form_state['storage']['files'] = $files;
}
}
/**
* Submit handler for media_import().
*/
function media_import_submit($form, &$form_state) {
if ($form_state['values']['op'] == t('Confirm')) {
$files = $form_state['storage']['files'];
$batch = array(
'title' => t('Importing'),
'operations' => array(
array('media_import_batch_import_files', array($files)),
),
'finished' => 'media_import_batch_import_complete',
'file' => drupal_get_path('module', 'media') . '/includes/media.admin.inc',
);
batch_set($batch);
return;
}
$form_state['rebuild'] = TRUE;
}
/**
* BatchAPI callback op for media import.
*/
function media_import_batch_import_files($files, &$context) {
if (!isset($context['sandbox']['files'])) {
// This runs the first time the batch runs.
// This is stupid, but otherwise, I don't think it will work...
$context['results'] = array('success' => array(), 'errors' => array());
$context['sandbox']['max'] = count($files);
$context['sandbox']['files'] = $files;
}
$files =& $context['sandbox']['files'];
// Take a cut of files. Let's do 10 at a time.
$length = (count($files) > media_variable_get('import_batch_size')) ? media_variable_get('import_batch_size') : count($files);
$to_process = array_splice($files, 0, $length);
$image_in_message = '';
foreach ($to_process as $file) {
try {
$file_obj = media_parse_to_file($file);
$context['results']['success'][] = $file;
if (!$image_in_message) {
// @todo Is this load step really necessary? When there's time, test
// this, and either remove it, or comment why it's needed.
$loaded_file = file_load($file_obj->fid);
$image_in_message = file_view_file($loaded_file, 'media_preview');
}
}
catch (Exception $e) {
$context['results']['errors'][] = $file . " Reason: " . $e->getMessage();
}
}
$context['message'] = "Importing " . theme('item_list', array('items' => $to_process));
$context['message'] .= drupal_render($image_in_message); // Just for kicks, show an image we are importing
$context['finished'] = ($context['sandbox']['max'] - count($files)) / $context['sandbox']['max'];
}
/**
* BatchAPI complete callback for media import.
*/
function media_import_batch_import_complete($success, $results, $operations) {
if ($results['errors']) {
drupal_set_message(theme('item_list', array('items' => $results['errors'])), 'error');
}
if ($results['success']) {
drupal_set_message(theme('item_list', array('items' => $results['success'])));
}
}
/**
* Admin configruation form for media browser settings.
*/
function media_admin_config_browser($form, &$form_state) {
$theme_options = array();
$theme_options[NULL] = 'Default administration theme';
foreach (list_themes() as $key => $theme) {
if ($theme->status) {
$theme_options[$key] = $theme->info['name'];
}
}
$form[media_variable_name('dialog_theme')] = array(
'#type' => 'select',
'#title' => t('Media browser theme'),
'#options' => $theme_options,
'#description' => t("This theme will be used for all media related dialogs. It can be different from your site's theme because many site themes do not work well in the small windows which media uses."),
'#default_value' => media_variable_get('dialog_theme'),
);
$form[media_variable_name('file_extensions')] = array(
'#type' => 'textfield',
'#title' => t('Allowed file extensions'),
'#default_value' => media_variable_get('file_extensions'),
'#description' => t('File extensions which are accepted in the media browser. Use spaces to separate (e.g. "jpg gif pdf doc"). <br/><em>Note that this can be overriden on a per-field basis when creating multimedia asset fields and files of certain extensions cannot be embedded at this time.</em>'),
);
$form['#submit'][] = 'media_admin_config_browser_pre_submit';
return system_settings_form($form);
}
function media_admin_config_browser_pre_submit(&$form, &$form_state) {
if (!$form_state['values'][media_variable_name('dialog_theme')]) {
media_variable_del('dialog_theme');
unset($form_state['values'][media_variable_name('dialog_theme')]);
}
}
/**
* Confirmation form for rebuliding the file_managed table to include type
* in rows where there is no type.
*/
function media_admin_rebuild_types_form($form, &$form_state) {
$total = media_type_invalid_files_count();
if ($total == 0) {
media_variable_del('show_file_type_rebuild_nag');
// @TODO: Make this not sound stupid.
drupal_set_message(t('All files in the system have been assigned types. Media installation complete.'));
drupal_goto('admin');
}
$form['total'] = array('#type' => 'value', '#value' => $total);
return confirm_form(
$form,
t('Update types for existing files'),
'admin/config/media',
t('This process is required when installing media on an existing site. Media needs to scan through existing files and identify the file type. <br/><strong>Update types for @file files?</strong>', array('@total' => $total))
);
}
/**
* @see media_admin_rebuild_types_form()
*/
function media_admin_rebuild_types_form_submit(&$form, &$form_state) {
$total = $form_state['values']['total'];
$batch = array(
'title' => t('Rebuilding type information for ' . $total . ' files'),
'operations' => array(
array('media_admin_rebuild_types_batch_op', array($total)),
),
'finished' => 'media_admin_rebuild_types_batch_complete',
'file' => drupal_get_path('module', 'media') . '/includes/media.admin.inc',
);
batch_set($batch);
}
/**
* Batch operation for fixing the file_managed table for media, adding type values
* where no value exists.
*/
function media_admin_rebuild_types_batch_op($total, &$context) {
$per_run = media_variable_get('media_type_batch_update_per_run', 100);
$context['results'] = array_merge($context['results'], media_type_batch_update(FALSE, $per_run));
$context['finished'] = count($context['results']) / $total;
}
/**
* Sets a message informing the user how many file records were updated.
*/
function media_admin_rebuild_types_batch_complete($success, $results, $operations) {
if ($success) {
$message = format_plural(count($results), 'One file identified and given a type.', '@count files identified and given a type.');
media_variable_del('show_file_type_rebuild_nag');
}
drupal_set_message($message);
}

View File

@@ -0,0 +1,367 @@
<?php
/**
* @file
* Media Browser page callback
*/
function media_browser($selected = NULL) {
$output = array();
$output['#attached']['library'][] = array('media', 'media_browser_page');
$params = drupal_get_query_parameters();
array_walk_recursive($params, '_media_recursive_check_plain');
$params = media_set_browser_params($params);
// If one or more files have been selected, the browser interaction is now
// complete. Return empty page content to the dialog which now needs to close,
// but populate Drupal.settings with information about the selected files.
if (isset($params['fid'])) {
$fids = is_array($params['fid']) ? $params['fid'] : array($params['fid']);
if (!is_numeric($fids[0])) {
throw new Exception('Error selecting media, fid param is not an fid or an array of fids');
}
$files = file_load_multiple($fids);
foreach ($files as $file) {
media_browser_build_media_item($file);
}
$setting = array('media' => array('selectedMedia' => array_values($files)));
drupal_add_js($setting, 'setting');
return $output;
}
// Normal browser operation.
foreach (module_implements('media_browser_plugin_info') as $module) {
foreach (module_invoke($module, 'media_browser_plugin_info') as $key => $plugin_data) {
$plugins[$key] = $plugin_data + array(
'#module' => $module,
'#weight' => 0,
);
$plugins[$key]['#weight'] += count($plugins)/1000;
}
}
// Only the plugins in this array are loaded.
if (!empty($params['enabledPlugins'])) {
$plugins = array_intersect_key($plugins, array_fill_keys($params['enabledPlugins'], 1));
}
elseif (!empty($params['disabledPlugins'])) {
$plugins = array_diff_key($plugins, array_fill_keys($params['disabledPlugins'], 1));
}
foreach ($plugins as $key => &$plugin) {
$plugin += module_invoke($plugin['#module'], 'media_browser_plugin_view', $key, $params);
}
// Allow modules to change the tab names or whatever else they want to change
// before we render. Perhaps this should be an alter on the theming function
// that we should write to be making the tabs.
drupal_alter('media_browser_plugins', $plugins);
$tabs = array(); // List of tabs to render.
$settings = array('media' => array('browser' => array()));
$browser_settings =& $settings['media']['browser'];
//@todo: replace with Tabs module if it gets upgraded.
foreach (element_children($plugins, TRUE) as $key) {
$plugin =& $plugins[$key];
//Add any JS settings
$browser_settings[$key] = isset($plugin['#settings']) ? $plugin['#settings'] : array();
// If this is a "ajax" style tab, add the href, otherwise an id.
$href = isset($plugin['#callback']) ? $plugin['#callback'] : "#media-tab-$key";
$tabs[] = "<a href='$href'><span>{$plugin['#title']}</span></a>";
// Create a div for each tab's content.
$plugin['#prefix'] = <<<EOS
<div class="media-browser-tab" id="media-tab-$key">
EOS;
$plugin['#suffix'] = <<<EOS
</div>
<!-- End #media-tab-$key -->
EOS;
}
drupal_add_js($settings, 'setting');
$output['tabset'] = array(
'#prefix' => '<div id="media-browser-tabset">',
'#suffix' => '</div>',
);
$output['tabset']['list'] = array(
'#markup' => '<ul><li>' . implode('</li><li>', $tabs) . '</li></ul>'
);
$output['tabset']['plugins'] = $plugins;
return $output;
}
/**
* Provides a singleton of the params passed to the media browser.
*
* This is useful in situations like form alters because callers can pass
* id="wysiywg_form" or whatever they want, and a form alter could pick this up.
* We may want to change the hook_media_browser_plugin_view() implementations to
* use this function instead of being passed params for consistency.
*
* It also offers a chance for some meddler to meddle with them.
*
* @param array $params
* An array of parameters provided when a media_browser is launched.
*
* @see media_browser()
*/
function media_set_browser_params(array $params = NULL) {
$stored_params = &drupal_static(__FUNCTION__, array());
if (isset($params)) {
$stored_params = $params;
// Allow modules to alter the parameters.
drupal_alter('media_browser_params', $stored_params);
}
return $stored_params;
}
/**
* For sanity in grammar.
*
* @see media_set_browser_params()
*/
function media_get_browser_params() {
return media_set_browser_params();
}
/**
* AJAX Callback function to return a list of media files
*/
function media_browser_list() {
$params = drupal_get_query_parameters();
// How do we validate these? I don't know.
// I think PDO should protect them, but I'm not 100% certain.
array_walk_recursive($params, '_media_recursive_check_plain');
$remote_types = !empty($params['types']) ? $params['types'] : NULL;
$url_include_patterns = !empty($params['url_include_patterns']) ? $params['url_include_patterns'] : NULL;
$url_exclude_patterns = !empty($params['url_exclude_patterns']) ? $params['url_exclude_patterns'] : NULL;
$allowed_schemes = !empty($params['schemes']) ? array_filter($params['schemes']) : array();
$start = isset($params['start']) ? $params['start'] : 0;
$limit = isset($params['limit']) ? $params['limit'] : media_variable_get('browser_pager_limit');
$query = db_select('file_managed', 'f');
$query->fields('f', array('fid'));
$query->range($start, $limit);
$query->orderBy('f.timestamp', 'DESC');
// Add conditions based on remote file type *or* local allowed extensions.
$or_condition = db_or();
// Include local files with the allowed extensions.
if (!empty($params['file_extensions'])) {
$extensions = array_filter(explode(' ', $params['file_extensions']));
$local_wrappers = array_intersect_key(media_get_local_stream_wrappers(), $allowed_schemes);
if (!empty($local_wrappers) && !empty($extensions)) {
$local_condition = db_or();
foreach (array_keys($local_wrappers) as $scheme) {
foreach ($extensions as $extension) {
$local_condition->condition('f.uri', db_like($scheme . '://') . '%' . db_like('.' . $extension), 'LIKE');
}
}
$or_condition->condition($local_condition);
}
}
// Include remote files with the allowed file types.
if (!empty($remote_types)) {
$remote_wrappers = array_intersect_key(media_get_remote_stream_wrappers(), $allowed_schemes);
if (!empty($remote_wrappers)) {
$remote_condition = db_and();
$wrapper_condition = db_or();
foreach (array_keys($remote_wrappers) as $scheme) {
$wrapper_condition->condition('f.uri', db_like($scheme . '://') . '%', 'LIKE');
}
$remote_condition->condition($wrapper_condition);
$remote_condition->condition('f.type', $remote_types, 'IN');
$or_condition->condition($remote_condition);
}
}
if ($or_condition->count()) {
$query->condition($or_condition);
}
if ($url_include_patterns) {
$query->condition('f.uri', '%' . db_like($url_include_patterns) . '%', 'LIKE');
// Insert stream related restrictions here.
}
if ($url_exclude_patterns) {
$query->condition('f.uri', '%' . db_like($url_exclude_patterns) . '%', 'NOT LIKE');
}
// @todo Implement granular editorial access: http://drupal.org/node/696970.
// In the meantime, protect information about private files from being
// discovered by unprivileged users. See also media_view_page().
if (!user_access('administer media')) {
$query->condition('f.uri', db_like('private://') . '%', 'NOT LIKE');
}
$query->condition('f.status', FILE_STATUS_PERMANENT);
foreach (array_keys(media_get_hidden_stream_wrappers()) as $name) {
$query->condition('f.uri', db_like($name . '://') . '%', 'NOT LIKE');
}
$fids = $query->execute()->fetchCol();
$files = file_load_multiple($fids);
foreach ($files as $file) {
media_browser_build_media_item($file);
}
drupal_json_output(array('media' => array_values($files)));
exit();
}
/**
* Silly function to recursively run check_plain on an array.
*
* There is probably something in core I am not aware of that does this.
*
* @param $value
* @param $key
*/
function _media_recursive_check_plain(&$value, $key) {
$value = check_plain($value);
}
/**
* Attaches media browser javascript to an element.
*
* @param $element
* The element array to attach to.
*/
function media_attach_browser_js(&$element) {
$javascript = media_browser_js();
foreach ($javascript as $key => $definitions) {
foreach ($definitions as $definition) {
$element['#attached'][$key][] = $definition;
}
}
}
/**
* Helper function to define browser javascript.
*/
function media_browser_js() {
$settings = array(
'browserUrl' => url('media/browser',
array('query' => array('render' => 'media-popup'))),
'styleSelectorUrl' => url('media/-media_id-/format-form',
array('query' => array('render' => 'media-popup'))),
);
$js = array(
'library' => array(
array('media', 'media_browser'),
),
'js' => array(
array(
'data' => array('media' => $settings),
'type' => 'setting',
),
),
);
return $js;
}
/**
* Menu callback for testing the media browser
*/
function media_browser_testbed($form) {
media_attach_browser_js($form);
$form['test_element'] = array(
'#type' => 'media',
'#media_options' => array(
'global' => array(
'types' => array('video', 'audio'),
),
)
);
$launcher = '<a href="#" id="launcher"> Launch Media Browser</a>';
$form['options'] = array(
'#type' => 'textarea',
'#title' => 'Options (JSON)',
'#rows' => 10,
);
$form['launcher'] = array(
'#markup' => $launcher,
);
$form['result'] = array(
'#type' => 'textarea',
'#title' => 'Result',
);
$js = <<<EOF
Drupal.behaviors.mediaTest = {
attach: function(context, settings) {
var delim = "---";
var recentOptions = [];
var recentOptionsCookie = jQuery.cookie("recentOptions");
if (recentOptionsCookie) {
recentOptions = recentOptionsCookie.split("---");
}
var recentSelectBox = jQuery('<select id="recent_options" style="width:100%"></select>').change(function() { jQuery('#edit-options').val(jQuery(this).val())});
jQuery('.form-item-options').append('<label for="recent_options">Recent</a>');
jQuery('.form-item-options').append(recentSelectBox);
jQuery('.form-item-options').append(jQuery('<a href="#">Reset</a>').click(function() {alert('reset'); jQuery.cookie("recentOptions", null); window.location.reload(); }));
jQuery.each(recentOptions, function (idx, val) {
recentSelectBox.append(jQuery('<option></option>').val(val).html(val));
});
jQuery('#launcher').click(function () {
jQuery('#edit-result').val('');
var options = {};
var optionsTxt = jQuery('#edit-options').val();
if (optionsTxt) {
// Store it in the recent box
recentOptionsCookie += "---" + optionsTxt
jQuery.cookie("recentOptions", recentOptionsCookie, { expires: 7 });
recentSelectBox.append(jQuery('<option></option>').val(optionsTxt).html(optionsTxt));
options = eval('(' + optionsTxt + ')');
}
Drupal.media.popups.mediaBrowser(Drupal.behaviors.mediaTest.mediaSelected, options);
return false;
});
},
mediaSelected: function(selectedMedia) {
var result = JSON.stringify(selectedMedia);
jQuery('#edit-result').val(result);
}
}
EOF;
drupal_add_js($js, array('type' => 'inline'));
return $form;
}
/**
* Adds properties to the passed in file that are needed by the media browser JS code.
*/
function media_browser_build_media_item($file) {
$preview = media_get_thumbnail_preview($file);
$file->preview = drupal_render($preview);
$file->url = file_create_url($file->uri);
}

View File

@@ -0,0 +1,485 @@
<?php
/**
* @file: Provides a "Multimedia asset" field to the fields API
*/
/**
* Implement hook_field_info().
*/
function media_field_info() {
return array(
'media' => array(
'label' => t('Multimedia asset (deprecated)'),
'description' => t('This field stores a reference to a multimedia asset.'),
'settings' => array(),
'instance_settings' => array(
'file_extensions' => media_variable_get('file_extensions'),
),
'default_widget' => 'media_generic',
'default_formatter' => 'media_large',
'property_type' => 'field_item_file',
'property_callbacks' => array('entity_metadata_field_file_callback'),
),
);
}
/**
* Implements hook_field_is_empty().
*/
function media_field_is_empty($item, $field) {
return empty($item['fid']);
}
/**
* Implements hook_field_instance_settings_form().
*/
function media_field_instance_settings_form($field, $instance) {
$settings = $instance['settings'];
// Make the extension list a little more human-friendly by comma-separation.
$extensions = str_replace(' ', ', ', $settings['file_extensions']);
$form['file_extensions'] = array(
'#type' => 'textfield',
'#title' => t('Allowed file extensions for uploaded files'),
'#default_value' => $extensions,
'#description' => t('Separate extensions with a space or comma and do not include the leading dot.'),
'#element_validate' => array('_file_generic_settings_extensions'),
// By making this field required, we prevent a potential security issue
// that would allow files of any type to be uploaded.
'#required' => TRUE,
'#maxlength' => 255,
);
return $form;
}
/**
* Implement hook_field_widget_info().
*/
function media_field_widget_info() {
return array(
'media_generic' => array(
'label' => t('Media file selector'),
'field types' => array('media', 'file', 'image'),
'settings' => array(
'progress_indicator' => 'throbber',
'allowed_types' => array('image'),
'allowed_schemes' => array('public', 'private'),
),
'behaviors' => array(
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
'default value' => FIELD_BEHAVIOR_NONE,
),
),
);
}
/**
* Implements hook_field_formatter_info().
*/
function media_field_formatter_info() {
$formatters = array(
'media_large_icon' => array(
'label' => t('Large filetype icon'),
'field types' => array('file'),
),
// This was originally used when media entities contained file fields. The
// current file entity architecture no longer needs this, but people may
// have used this formatter for other file fields on their website.
// @todo Some day, remove this.
'media' => array(
'label' => t('Media'),
'field types' => array('media'),
'settings' => array('file_view_mode' => 'default'),
),
);
return $formatters;
}
/**
* Implements hook_field_formatter_settings_form().
*/
function media_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$element = array();
if ($display['type'] == 'media') {
$entity_info = entity_get_info('file');
$options = array('default' => t('Default'));
foreach ($entity_info['view modes'] as $file_view_mode => $file_view_mode_info) {
$options[$file_view_mode] = $file_view_mode_info['label'];
}
$element['file_view_mode'] = array(
'#title' => t('File view mode'),
'#type' => 'select',
'#default_value' => $settings['file_view_mode'],
'#options' => $options,
);
}
return $element;
}
/**
* Implements hook_field_formatter_settings_summary().
*/
function media_field_formatter_settings_summary($field, $instance, $view_mode) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$summary = '';
if ($display['type'] == 'media') {
$entity_info = entity_get_info('file');
$file_view_mode_label = isset($entity_info['view modes'][$settings['file_view_mode']]) ? $entity_info['view modes'][$settings['file_view_mode']]['label'] : t('Default');
$summary = t('File view mode: @view_mode', array('@view_mode' => $file_view_mode_label));
}
return $summary;
}
/**
* Implements hook_field_prepare_view().
*/
function media_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) {
// Collect all file IDs that need loading.
$fids = array();
foreach ($entities as $id => $entity) {
// Load the files from the files table.
foreach ($items[$id] as $delta => $item) {
if (!empty($item['fid'])) {
$fids[] = $item['fid'];
}
}
}
// Load the file entities.
$files = file_load_multiple($fids);
// Add the loaded file entities to the field item array.
foreach ($entities as $id => $entity) {
foreach ($items[$id] as $delta => $item) {
// If the file does not exist, mark the entire item as empty.
if (empty($files[$item['fid']])) {
unset($items[$id][$delta]);
}
else {
$items[$id][$delta]['file'] = $files[$item['fid']];
}
}
}
}
/**
* Implement hook_field_formatter_view().
*/
function media_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$element = array();
if ($display['type'] == 'media_large_icon') {
foreach ($items as $delta => $item) {
$element[$delta] = array(
'#theme' => 'media_formatter_large_icon',
'#file' => (object) $item,
);
}
}
// Legacy support for the extra formatter added to file fields. See
// media_field_formatter_info().
if ($display['type'] == 'media') {
$files = array();
foreach ($items as $delta => $item) {
if (!empty($item['file'])) {
$files[$item['fid']] = $item['file'];
}
}
if (!empty($files)) {
$element = file_view_multiple($files, $display['settings']['file_view_mode'], 0, $langcode);
}
}
return $element;
}
/**
* Implement hook_field_widget_settings_form().
*/
function media_field_widget_settings_form($field, $instance) {
$widget = $instance['widget'];
$settings = $widget['settings'];
$form = array();
// Setup type selection form
$types = media_type_get_types();
$options = array();
foreach ($types as $key => $definition) {
$options[$key] = $definition->label;
}
$streams = file_get_stream_wrappers();
$form['allowed_types'] = array(
'#type' => 'checkboxes',
'#title' => t('Allowed remote media types'),
'#options' => $options,
'#default_value' => $settings['allowed_types'],
'#description' => t('Media types which are allowed for this field when using remote streams.'),
'#weight' => 1,
'#access' => count(file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL)) != count($streams),
);
$options = array();
unset($streams['temporary']);
foreach ($streams as $scheme => $data) {
$options[$scheme] = t('@scheme (@name)', array('@scheme' => $scheme . '://', '@name' => $data['name']));
}
$form['allowed_schemes'] = array(
'#type' => 'checkboxes',
'#title' => t('Allowed URI schemes'),
'#options' => $options,
'#default_value' => $settings['allowed_schemes'],
'#description' => t('URI schemes include public:// and private:// which are the Drupal files directories, and may also refer to remote sites.'),
'#weight' => 2,
);
return $form;
}
/**
* Implements hook_field_widget_form().
*/
function media_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
$field_settings = $instance['settings'];
$widget_settings = $instance['widget']['settings'];
// @todo The Field API supports automatic serialization / unserialization, so
// this should no longer be needed. After verifying with a module that uses
// the 'data' column, remove this.
// @see media_field_widget_value()
$current_value = array();
if (isset($items[$delta])) {
$current_value = $items[$delta];
// @todo $items[$delta] is sometimes a loaded media entity (an object)
// rather than an array. This conflicts with Field API expectations (for
// example, it results in fatal errors when previewing a node with a
// multi-valued media field), so should be fixed. In the meantime, don't
// assume that $current_value is an array.
if (is_array($current_value) && isset($current_value['data']) && is_string($current_value['data'])) {
$current_value['data'] = unserialize($current_value['data']);
}
}
$element += array(
'#type' => 'media', // Would like to make this a fieldset, but throws some weird warning about element_children... not sure what it is about yet.
'#collapsed' => TRUE,
'#default_value' => $current_value,
'#required' => $instance['required'],
'#media_options' => array(
'global' => array(
'types' => array_filter($widget_settings['allowed_types']),
'schemes' => $widget_settings['allowed_schemes'],
'file_directory' => isset($field_settings['file_directory']) ? $field_settings['file_directory'] : '',
'file_extensions' => isset($field_settings['file_extensions']) ? $field_settings['file_extensions'] : media_variable_get('file_extensions'),
'max_filesize' => isset($field_settings['max_filesize']) ? $field_settings['max_filesize'] : 0,
'uri_scheme' => !empty($field['settings']['uri_scheme']) ? $field['settings']['uri_scheme'] : file_default_scheme(),
),
),
);
if ($field['type'] == 'file') {
$element['display'] = array(
'#type' => 'value',
'#value' => 1,
);
}
// Add image field specific validators.
if ($field['type'] == 'image') {
if ($field_settings['min_resolution'] || $field_settings['max_resolution']) {
$element['#media_options']['global']['min_resolution'] = $field_settings['min_resolution'];
$element['#media_options']['global']['max_resolution'] = $field_settings['max_resolution'];
}
}
return $element;
}
/**
* Implements hook_field_validate().
*
* Possible error codes:
* - 'media_remote_file_type_not_allowed': The remote file is not an allowed
* file type.
*/
function media_field_validate($obj_type, $object, $field, $instance, $langcode, $items, &$errors) {
$allowed_types = array_keys(array_filter($instance['widget']['settings']['allowed_types']));
// @TODO: merge in stuff from media_uri_value
foreach ($items as $delta => $item) {
if (empty($item['fid'])) {
return TRUE;
//@TODO: make support for submiting with just a URI here?
}
$file = file_load($item['fid']);
// Only validate allowed types if the file is remote and not local.
if (!file_entity_file_is_local($file)) {
if (!in_array($file->type, $allowed_types)) {
$errors[$field['field_name']][$langcode][$delta][] = array(
'error' => 'media_remote_file_type_not_allowed',
'message' => t('%name: Only remote files with the following types are allowed: %types-allowed.', array('%name' => t($instance['label']), '%types-allowed' => !empty($allowed_types) ? implode(', ', $allowed_types) : t('no file types selected'))),
);
}
}
}
}
/**
* Implements_hook_field_widget_error().
*/
function media_field_widget_error($element, $error, $form, &$form_state) {
form_error($element['fid'], $error['message']);
}
/**
* @todo Is this function ever called? If not, remove it. The Field API now
* supports automatic serialization / unserialization, so this should no
* longer be needed. After verifying with a module that uses the 'data'
* column, remove this.
*
* @see media_field_widget_form()
*/
function media_field_widget_value($element, $input, $form_state) {
$return = $input;
if (!is_array($return)) {
$return = array();
}
if (isset($return['data'])) {
$return['data'] = serialize($return['data']);
}
$return += array(
'fid' => 0,
'title' => '',
'data' => NULL,
);
return $return;
}
/**
* @todo The following hook_field_(insert|update|delete|delete_revision)
* implementations are nearly identical to the File module implementations of
* the same field hooks. The only differences are:
* - We pass 'media' rather than 'file' as the module argument to the
* file_usage_(add|delete)() functions.
* - We do not delete the file / media entity when its usage count goes to 0.
* We should submit a core patch to File module to make it flexible with
* respect to the above, so that we can reuse its implementation rather than
* duplicating it.
*/
/**
* Implements hook_field_insert().
*/
function media_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
// Add a new usage of each uploaded file.
foreach ($items as $item) {
$file = (object) $item;
file_usage_add($file, 'media', $entity_type, $id);
}
}
/**
* Implements hook_field_update().
*
* Checks for files that have been removed from the object.
*/
function media_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
// On new revisions, all files are considered to be a new usage and no
// deletion of previous file usages are necessary.
if (!empty($entity->revision)) {
foreach ($items as $item) {
$file = (object) $item;
file_usage_add($file, 'media', $entity_type, $id);
}
return;
}
// Build a display of the current FIDs.
$current_fids = array();
foreach ($items as $item) {
$current_fids[] = $item['fid'];
}
// Compare the original field values with the ones that are being saved.
$original_fids = array();
if (!empty($entity->original->{$field['field_name']}[$langcode])) {
foreach ($entity->original->{$field['field_name']}[$langcode] as $original_item) {
$original_fids[] = $original_item['fid'];
if (isset($original_item['fid']) && !in_array($original_item['fid'], $current_fids)) {
// Decrement the file usage count by 1.
$file = (object) $original_item;
file_usage_delete($file, 'media', $entity_type, $id, 1);
}
}
}
// Add new usage entries for newly added files.
foreach ($items as $item) {
if (!in_array($item['fid'], $original_fids)) {
$file = (object) $item;
file_usage_add($file, 'media', $entity_type, $id);
}
}
}
/**
* Implements hook_field_delete().
*/
function media_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
// Delete all file usages within this entity.
foreach ($items as $delta => $item) {
$file = (object) $item;
file_usage_delete($file, 'media', $entity_type, $id, 0);
}
}
/**
* Implements hook_field_delete_revision().
*/
function media_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) {
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
foreach ($items as $delta => $item) {
// @TODO: Not sure if this is correct
$file = (object)$item;
if (file_usage_delete($file, 'media', $entity_type, $id, 1)) {
$items[$delta] = NULL;
}
}
}
/**
* Implements hook_field_instance_update().
*/
function media_field_update_instance($instance, $prior_instance) {
// Clear the filter cache when updating instance settings for a media entity.
if ($instance['entity_type'] == 'media') {
media_filter_invalidate_caches();
}
}

View File

@@ -0,0 +1,478 @@
<?php
/**
* @file
* Functions related to the WYSIWYG editor and the media input filter.
*
* @TODO: Rename this file?
*/
/**
* Implements hook_wysiwyg_include_directory().
*/
function media_wysiwyg_include_directory($type) {
switch ($type) {
case 'plugins':
return 'wysiwyg_plugins';
break;
}
}
/**
* Filter callback for media markup filter.
*
* @TODO check for security probably pass text through filter_xss
* @return unknown_type
*/
function media_filter($text) {
$text = ' ' . $text . ' ';
$text = preg_replace_callback("/\[\[.*?\]\]/s", 'media_token_to_markup', $text);
return $text;
}
/**
* Filter callback for media url filter.
* @TODO: There are currently problems with this. For instance, if a file is
* to be loaded from a remote location here, it will be recreated multiple
* times, each time this filter is called. If we want to continue supporting
* this feature, we would need to probably create a new stream or other way
* to lookup a remote file w/ its local version. Probably best as a contributed
* module because of this difficulty. ~ aaron.
*/
function media_url_filter($text, $filter) {
$text = ' ' . $text . ' ';
// Need to attach the variables to the callback after the regex.
$callback = _media_url_curry('_media_url_parse_full_links', 1);
// Match absolute URLs.
$text = preg_replace_callback("`(<p>|<li>|<br\s*/?>|[ \n\r\t\(])((http://|https://)([a-zA-Z0-9@:%_+*~#?&=.,/;-]*[a-zA-Z0-9@:%_+*~#&=/;-]))([.,?!]*?)(?=(</p>|</li>|<br\s*/?>|[ \n\r\t\)]))`i", $callback, $text);
return $text;
}
/**
* If one of our allowed providers knows what to do with the url,
* then let it embed the video.
*
* @param int $filter
* The filter id.
* @param array $match
* The matched text from our regex.
*
* @return string
* The replacement text for the url.
*/
function _media_url_parse_full_links($match) {
// Get just the URL.
$match[2] = check_url(decode_entities($match[2]));
try {
$file = media_parse_to_file($match[2]);
}
catch (Exception $e) {
// Ignore errors; pass the original text for other filters to deal with.
return $match[0];
}
if ($file->fid) {
$file = file_load($file->fid);
// Generate a preview of the file
// @TODO: Allow user to change the formatter in the filter settings.
$preview = file_view_file($file, 'media_large');
$preview['#show_names'] = TRUE;
return drupal_render($preview);
}
// Nothing was parsed; return the original text.
return $match[0];
}
function _media_url_curry($func, $arity) {
return create_function('', "
\$args = func_get_args();
if(count(\$args) >= $arity)
return call_user_func_array('$func', \$args);
\$args = var_export(\$args, 1);
return create_function('','
\$a = func_get_args();
\$z = ' . \$args . ';
\$a = array_merge(\$z,\$a);
return call_user_func_array(\'$func\', \$a);
');
");
}
/**
* Parses the contents of a CSS declaration block and returns a keyed array of property names and values.
*
* @param $declarations
* One or more CSS declarations delimited by a semicolon. The same as a CSS
* declaration block (see http://www.w3.org/TR/CSS21/syndata.html#rule-sets),
* but without the opening and closing curly braces. Also the same as the
* value of an inline HTML style attribute.
*
* @return
* A keyed array. The keys are CSS property names, and the values are CSS
* property values.
*/
function media_parse_css_declarations($declarations) {
$properties = array();
foreach (array_map('trim', explode(";", $declarations)) as $declaration) {
if ($declaration != '') {
list($name, $value) = array_map('trim', explode(':', $declaration, 2));
$properties[strtolower($name)] = $value;
}
}
return $properties;
}
/**
* Replace callback to convert a media file tag into HTML markup.
*
* @param string $match
* Takes a match of tag code
* @param boolean $wysiwyg
* Set to TRUE if called from within the WYSIWYG text area editor.
* @return
* The HTML markup representation of the tag, or an empty string on failure.
*
* @see media_get_file_without_label()
* @see hook_media_token_to_markup_alter()
*/
function media_token_to_markup($match, $wysiwyg = FALSE) {
$settings = array();
$match = str_replace("[[", "", $match);
$match = str_replace("]]", "", $match);
$tag = $match[0];
try {
if (!is_string($tag)) {
throw new Exception('Unable to find matching tag');
}
$tag_info = drupal_json_decode($tag);
if (!isset($tag_info['fid'])) {
throw new Exception('No file Id');
}
if (!isset($tag_info['view_mode'])) {
// Should we log or throw an exception here instead?
// Do we need to validate the view mode for fields API?
$tag_info['view_mode'] = media_variable_get('wysiwyg_default_view_mode');
}
$file = file_load($tag_info['fid']);
if (!$file) {
throw new Exception('Could not load media object');
}
$tag_info['file'] = $file;
// Track the fid of this file in the {media_filter_usage} table.
media_filter_track_usage($file->fid);
$attributes = is_array($tag_info['attributes']) ? $tag_info['attributes'] : array();
$attribute_whitelist = media_variable_get('wysiwyg_allowed_attributes');
$settings['attributes'] = array_intersect_key($attributes, array_flip($attribute_whitelist));
// Many media formatters will want to apply width and height independently
// of the style attribute or the corresponding HTML attributes, so pull
// these two out into top-level settings. Different WYSIWYG editors have
// different behavior with respect to whether they store user-specified
// dimensions in the HTML attributes or the style attribute, so check both.
// Per http://www.w3.org/TR/html5/the-map-element.html#attr-dim-width, the
// HTML attributes are merely hints: CSS takes precedence.
if (isset($settings['attributes']['style'])) {
$css_properties = media_parse_css_declarations($settings['attributes']['style']);
foreach (array('width', 'height') as $dimension) {
if (isset($css_properties[$dimension]) && substr($css_properties[$dimension], -2) == 'px') {
$settings[$dimension] = substr($css_properties[$dimension], 0, -2);
}
elseif (isset($settings['attributes'][$dimension])) {
$settings[$dimension] = $settings['attributes'][$dimension];
}
}
}
if ($wysiwyg) {
$settings['wysiwyg'] = $wysiwyg;
}
}
catch (Exception $e) {
watchdog('media', 'Unable to render media from %tag. Error: %error', array('%tag' => $tag, '%error' => $e->getMessage()));
return '';
}
$element = media_get_file_without_label($file, $tag_info['view_mode'], $settings);
drupal_alter('media_token_to_markup', $element, $tag_info, $settings);
return drupal_render($element);
}
/**
* Builds a map of media tags in the element being rendered to their rendered HTML.
*
* The map is stored in JS, so we can transform them when the editor is being displayed.
*
* @param array $element
*/
function media_pre_render_text_format($element) {
// filter_process_format() copies properties to the expanded 'value' child
// element.
if (!isset($element['format'])) {
return $element;
}
$field = &$element['value'];
$settings = array(
'field' => $field['#id'],
);
$tagmap = _media_generate_tagMap($field['#value']);
if (isset($tagmap)) {
drupal_add_js(array('tagmap' => $tagmap), 'setting');
}
return $element;
}
/**
* Generates an array of [inline tags] => <html> to be used in filter
* replacement and to add the mapping to JS.
* @param
* The String containing text and html markup of textarea
* @return
* An associative array with tag code as key and html markup as the value.
*
* @see media_process_form()
* @see media_token_to_markup()
*/
function _media_generate_tagMap($text) {
// Making $tagmap static as this function is called many times and
// adds duplicate markup for each tag code in Drupal.settings JS,
// so in media_process_form it adds something like tagCode:<markup>,
// <markup> and when we replace in attach see two duplicate images
// for one tagCode. Making static would make function remember value
// between function calls. Since media_process_form is multiple times
// with same form, this function is also called multiple times.
static $tagmap = array();
preg_match_all("/\[\[.*?\]\]/s", $text, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
// We see if tagContent is already in $tagMap, if not we add it
// to $tagmap. If we return an empty array, we break embeddings of the same
// media multiple times.
if (empty($tagmap[$match[0]])) {
// @TODO: Total HACK, but better than nothing.
// We should find a better way of cleaning this up.
if ($markup_for_media = media_token_to_markup($match, TRUE)) {
$tagmap[$match[0]] = $markup_for_media;
}
else {
$missing = file_create_url(drupal_get_path('module', 'media') . '/images/icons/default/image-x-generic.png');
$tagmap[$match[0]] = '<div><img src="' . $missing . '" width="100px" height="100px"/></div>';
}
}
}
return $tagmap;
}
/**
* Form callback used when embedding media.
*
* Allows the user to pick a format for their media file.
* Can also have additional params depending on the media type.
*/
function media_format_form($form, $form_state, $file) {
$form = array();
$form['#media'] = $file;
$entity_info = entity_get_info('file');
$view_modes = $entity_info['view modes'];
drupal_alter('media_wysiwyg_allowed_view_modes', $view_modes, $file);
$formats = $options = array();
foreach ($view_modes as $view_mode => $view_mode_info) {
// Don't present the user with an option to choose a view mode in which the
// file is hidden.
$extra_fields = field_extra_fields_get_display('file', $file->type, $view_mode);
if (!$extra_fields['file']['visible']) {
continue;
}
//@TODO: Display more verbose information about which formatter and what it does.
$options[$view_mode] = $view_mode_info['label'];
$element = media_get_file_without_label($file, $view_mode, array('wysiwyg' => TRUE));
// Make a pretty name out of this.
$formats[$view_mode] = drupal_render($element);
}
// Add the previews back into the form array so they can be altered.
$form['#formats'] = &$formats;
if (!count($formats)) {
throw new Exception('Unable to continue, no available formats for displaying media.');
return;
}
$default_view_mode = media_variable_get('wysiwyg_default_view_mode');
if (!isset($formats[$default_view_mode])) {
$default_view_mode = key($formats);
}
// Add the previews by reference so that they can easily be altered by
// changing $form['#formats'].
$settings['media']['formatFormFormats'] = &$formats;
$form['#attached']['js'][] = array('data' => $settings, 'type' => 'setting');
// Add the required libraries, JavaScript and CSS for the form.
$form['#attached']['library'][] = array('media', 'media_base');
$form['#attached']['library'][] = array('system', 'form');
$form['#attached']['js'][] = drupal_get_path('module', 'media') . '/js/media.format_form.js';
$form['#attached']['css'][] = drupal_get_path('module', 'media') . '/css/media-format-form.css';
$form['heading'] = array(
'#type' => 'markup',
'#prefix' => '<h1 class="title">',
'#suffix' => '</h1>',
'#markup' => t('Embedding %filename', array('%filename' => $file->filename)),
);
$preview = media_get_thumbnail_preview($file);
$form['preview'] = array(
'#type' => 'markup',
'#title' => check_plain(basename($file->uri)),
'#markup' => drupal_render($preview),
);
// These will get passed on to WYSIWYG
$form['options'] = array(
'#type' => 'fieldset',
'#title' => t('options'),
);
$form['options']['format'] = array(
'#type' => 'select',
'#title' => t('Current format is'),
'#options' => $options,
'#default_value' => $default_view_mode
);
// Similar to a form_alter, but we want this to run first so that media.types.inc
// can add the fields specific to a given type (like alt tags on media).
// If implemented as an alter, this might not happen, making other alters not
// be able to work on those fields.
// @TODO: We need to pass in existing values for those attributes.
drupal_alter('media_format_form_prepare', $form, $form_state, $file);
if (!element_children($form['options'])) {
$form['options']['#attributes'] = array('style' => 'display:none');
}
return $form;
}
/**
* Returns a drupal_render() array for just the file portion of a file entity.
*
* Optional custom settings can override how the file is displayed.
*/
function media_get_file_without_label($file, $view_mode, $settings = array()) {
$file->override = $settings;
// Legacy support for Styles module plugins that expect overridden HTML
// attributes in $file->override rather than $file->override['attributes'].
if (isset($settings['attributes'])) {
$file->override += $settings['attributes'];
}
$element = file_view_file($file, $view_mode);
// The formatter invoked by file_view_file() can use $file->override to
// customize the returned render array to match the requested settings. To
// support simple formatters that don't do this, set the element attributes to
// what was requested, but not if the formatter applied its own logic for
// element attributes.
if (!isset($element['#attributes']) && isset($settings['attributes'])) {
$element['#attributes'] = $settings['attributes'];
// While this function may be called for any file type, images are a common
// use-case. theme_image() and theme_image_style() require the 'alt'
// attribute to be passed separately from the 'attributes' array (see
// http://drupal.org/node/999338). Until that's fixed, implement this
// special-case logic. Image formatters using other theme functions are
// responsible for their own 'alt' attribute handling. See
// theme_media_formatter_large_icon() for an example.
if (isset($settings['attributes']['alt']) && !isset($element['#alt']) && isset($element['#theme']) && in_array($element['#theme'], array('image', 'image_style'))) {
$element['#alt'] = $settings['attributes']['alt'];
}
}
return $element;
}
/**
* Clears caches that may be affected by the media filter.
*
* The media filter calls file_load(). This means that if a file object
* is updated, the check_markup() and field caches could return stale content.
* There are several possible approaches to deal with this:
* - Disable filter caching in media_filter_info(), this was found to cause a
* 30% performance hit from profiling four node teasers, due to both the
* media filter itself, and other filters that can't be cached.
* - Clear the filter and field caches whenever any media node is updated, this
* would ensure cache coherency but would reduce the effectiveness of those
* caches on high traffic sites with lots of media content updates.
* - The approach taken here: Record the fid of all media objects that are
* referenced by the media filter. Only clear the filter and field caches
* when one of these is updated, as opposed to all media objects.
* - @todo: consider an EntityFieldQuery to limit cache clearing to only those
* entities that use a text format with the media filter, possibly checking
* the contents of those fields to further limit this to fields referencing
* the media object being updated. This would need to be implemented
* carefully to avoid scalability issues with large result sets, and may
* not be worth the effort.
*
* @param $fid
* Optional media fid being updated. If not given, the cache will be cleared
* as long as any file is referenced.
*/
function media_filter_invalidate_caches($fid = FALSE) {
// If fid is passed, confirm that it has previously been referenced by the
// media filter. If not, clear the cache if the {media_filter_usage} has any
// valid records.
if (($fid && db_query('SELECT fid FROM {media_filter_usage} WHERE fid = :fid', array(':fid' => $fid))->fetchField()) || (!$fid && media_filter_usage_has_records())) {
// @todo: support entity cache, either via a hook, or using module_exists().
cache_clear_all('*', 'cache_filter', TRUE);
cache_clear_all('*', 'cache_field', TRUE);
}
}
/**
* Determines if the {media_filter_usage} table has any entries.
*/
function media_filter_usage_has_records() {
return (bool) db_query_range('SELECT 1 FROM {media_filter_usage} WHERE fid > :fid', 0, 1, array(':fid' => 0))->fetchField();
}
/**
* Tracks usage of media fids by the media filter.
*
* @param $fid
* The media fid.
*/
function media_filter_track_usage($fid) {
// This function only tracks when fids are found by the media filter.
// It would be impractical to check when formatted text is edited to remove
// references to fids, however by keeping a timestamp, we can implement
// rudimentary garbage collection in hook_flush_caches().
// However we only need to track that an fid has ever been referenced,
// not every time, so avoid updating this table more than once per month,
// per fid.
$timestamp = db_query('SELECT timestamp FROM {media_filter_usage} WHERE fid = :fid', array(':fid' => $fid))->fetchField();
if (!$timestamp || $timestamp <= REQUEST_TIME - 86400 * 30) {
db_merge('media_filter_usage')->key(array('fid' => $fid))->fields(array('fid' => $fid, 'timestamp' => REQUEST_TIME))->execute();
}
}

View File

@@ -0,0 +1,436 @@
<?php
/**
* @file
* Common pages for the Media module.
*/
/**
* Menu callback; view a single file entity.
*/
function media_view_page($file) {
// @todo Implement granular editorial access: http://drupal.org/node/696970.
// In the meantime, protect information about private files from being
// discovered by unprivileged users. File IDs are autoincrement, so one can
// attempt discovery by trying to access different media/ID paths. See also
// media_browser_list(). This logic potentially belongs within
// media_access(), but that would require extending that function's
// signature to accept a $file paramter, and this is temporary code anyway.
if (!user_access('administer media') && (file_uri_scheme($file->uri) === 'private')) {
return MENU_ACCESS_DENIED;
}
drupal_set_title($file->filename);
return file_view($file, 'media_original');
}
/**
* Menu callback; presents the Media editing form.
*/
function media_page_edit($file) {
drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => ($file->type != FILE_TYPE_NONE ? $file->type : ''), '@title' => $file->filename)), PASS_THROUGH);
return drupal_get_form('media_edit', $file);
}
/**
* Menu callback; presents the Media editing form for multiple file entities.
*/
function media_page_multiedit($files) {
if (!module_exists('multiform')) {
drupal_set_message(t('To edit multiple media items, you must install the multiform module.'));
}
$i = 0;
$forms = array();
foreach ($files as $file) {
// To maintain unique form_ids, increment this counter.
// @see media_forms().
$i++;
$forms[] = array("media_edit_$i", $file);
}
$form = call_user_func_array('multiform_get_form', $forms);
$form['#attributes']['class'][] = 'media-multiedit-form';
unset($form['buttons']['Delete']);
// Would be nice to add this to show a message, but not working.
// Can debug.
//$form['buttons']['Save']['#submit'][] = 'media_page_multiedit_submit';
drupal_set_title(t('Editing multiple media files'));
return $form;
}
/**
* Menu callback; shows delete confirmation form.
*/
function media_page_delete($file) {
drupal_set_title(t('<em>Delete @type</em> @title', array('@type' => ($file->type != FILE_TYPE_NONE ? $file->type : ''), '@title' => $file->filename)), PASS_THROUGH);
// Don't bother showing the form if the item is in use, since we won't allow
// them to delete it anyway.
$references = file_usage_list($file);
if (!empty($references)) {
return t('The file %title is in use and cannot be deleted.', array('%title' => $file->filename));
}
else {
$files = array($file->fid => $file);
return drupal_get_form('media_multiple_delete_confirm', $files, '<front>', 'media/' . $file->fid);
}
}
/**
* Confirm the request to delete files.
*/
function media_multiple_delete_confirm($form, &$form_state, $files, $redirect_on_success = NULL, $redirect_on_cancel = NULL) {
$form['files'] = array('#tree' => TRUE);
$form['file_titles'] = array('#theme' => 'item_list');
foreach ($files as $fid => $value) {
$title = db_query('SELECT filename FROM {file_managed} WHERE fid = :fid', array(':fid' => $fid))->fetchField();
$form['files'][$fid] = array(
'#type' => 'value',
'#value' => $fid,
);
$form['file_titles']['#items'][] = check_plain($title);
}
$form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
if (isset($redirect_on_success)) {
$form['redirect_on_success'] = array(
'#type' => 'value',
'#value' => $redirect_on_success,
);
}
$form['#submit'][] = 'media_multiple_delete_confirm_submit';
$confirm_question = format_plural(count($files),
'Are you sure you want to delete this item?',
'Are you sure you want to delete these items?');
return confirm_form($form,
$confirm_question,
isset($redirect_on_cancel) ? $redirect_on_cancel : current_path(),
t('This action cannot be undone.'),
t('Delete'),
t('Cancel'));
}
/**
* Attempt to delete files and notify the user of the result.
*/
function media_multiple_delete_confirm_submit($form, &$form_state) {
if ($form_state['values']['confirm']) {
$results = array();
$files = array_keys($form_state['values']['files']);
foreach ($files as $fid) {
$file = file_load($fid);
$files[$fid] = $file;
$results[$fid] = file_delete($file);
}
// The result of file_delete can be an array if the file is in use, or TRUE/FALSE.
foreach ($results as $fid => $result) {
if (is_array($result)) {
drupal_set_message(t('The file @title is in use and cannot be deleted.', array('@title' => $files[$fid]->filename)), 'warning');
}
elseif (!$result) {
drupal_set_message(t('The file @title was not deleted due to an error.', array('@title' => $files[$fid]->filename)), 'error');
}
else {
$message = t('File @title was deleted', array('@title' => $files[$fid]->filename));
watchdog('media', $message);
drupal_set_message($message);
}
}
if (isset($form_state['values']['redirect_on_success'])) {
$form_state['redirect'] = $form_state['values']['redirect_on_success'];
}
}
}
/**
* Form callback for adding media via an upload form.
* @todo: should use the AJAX uploader
*/
function media_add_upload($form, &$form_state, $params = array()) {
// Set up file upload validators.
$validators = array();
// Validate file extensions. If there are no file extensions in $params and
// there are no Media defaults, there is no file extension validation.
if (!empty($params['file_extensions'])) {
$validators['file_validate_extensions'] = array($params['file_extensions']);
}
elseif ($tmp = media_variable_get('file_extensions')) {
$validators['file_validate_extensions'] = array($tmp);
}
// Validate file size but do not allow anything higher than file_upload_max_size().
$max_filesize = file_upload_max_size();
if (!empty($params['max_filesize']) && $params['max_filesize'] < $max_filesize) {
$validators['file_validate_size'] = array(parse_size($params['max_filesize']));
}
elseif (($tmp = media_variable_get('max_filesize')) && $tmp < $max_filesize) {
$validators['file_validate_size'] = array(parse_size($tmp));
}
else {
$validators['file_validate_size'] = array($max_filesize);
}
// Add image validators.
$params += array('min_resolution' => 0, 'max_resolution' => 0);
if ($params['min_resolution'] || $params['max_resolution']) {
$validators['file_validate_image_resolution'] = array($params['max_resolution'], $params['min_resolution']);
}
$form['#validators'] = $validators;
$form['upload'] = array(
'#type' => 'file',
'#title' => t('Upload a new file'),
'#description' => theme('file_upload_help', array('description' => '', 'upload_validators' => $validators)),
'#upload_validators' => $validators,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Validate the generic file upload with the global media settings.
*/
function media_add_upload_validate($form, &$form_state) {
// Save the file as a temporary file.
$file = file_save_upload('upload', $form['#validators']);
if ($file === NULL) {
form_set_error('upload', t("No file appears to have been selected."));
}
elseif ($file === FALSE) {
form_set_error('upload', t('File upload error.'));
}
else {
$form_state['values']['upload'] = $file;
}
}
/**
* Upload a file.
*/
function media_add_upload_submit($form, &$form_state) {
$params = $form_state['build_info']['args'][0];
$file = $form_state['values']['upload'];
// The media browser widget does not use the 'display' field.
$file->display = TRUE;
// Change the file status from temporary to permanent.
_media_save_file_permenently($file);
// Determine what URI scheme this file should use.
$scheme = !empty($params['uri_scheme']) && file_stream_wrapper_valid_scheme($params['uri_scheme']) ? $params['uri_scheme'] : file_default_scheme();
$scheme .= '://';
// Prepare the file's subdirectory path.
$directory = '';
if (!empty($params['file_directory'])) {
$directory = token_replace($params['file_directory']) . '/';
// If the directory isn't writable, or doesn't exist and can't be created,
// the upload will fail.
$prepare_directory = file_stream_wrapper_uri_normalize($scheme . $directory);
if (!file_prepare_directory($prepare_directory, FILE_CREATE_DIRECTORY)) {
drupal_set_message(t('The file directory @dir does not exist or is not writable. Please contact an administrator.', array('@dir' => $prepare_directory)), 'error');
return;
}
}
// Compose the file's permanent destination.
$destination = file_stream_wrapper_uri_normalize($scheme . $directory . $file->filename);
// Save the uploaded file to the permanent location.
$file = file_move($file, $destination, FILE_EXISTS_RENAME);
if ($file) {
drupal_set_message(t('The file @name was uploaded', array('@name' => $file->filename)));
}
else {
drupal_set_message(t('An error occurred and no file was uploaded.'), 'error');
return;
}
$form_state['redirect'] = array('media/browser', array('query' => array('render' => 'media-popup', 'fid' => $file->fid)));
}
function media_add_upload_multiple($form, &$form_state, $params = array()) {
$form = media_add_upload($form, $form_state, $params);
unset($form['upload']['#title']);
// The validators will be set from plupload anyway. This isn't pretty, but don't
// it to show up twice.
unset($form['upload']['#description']);
$form['upload']['#type'] = 'plupload';
$form['submit']['#value'] = t('Start upload');
return $form;
}
function media_add_upload_multiple_submit($form, &$form_state) {
$scheme = variable_get('file_default_scheme', 'public') . '://';
$saved_files = array();
// We can't use file_save_upload() because of http://www.jacobsingh.name/content/tight-coupling-no-not
foreach ($form_state['values']['upload'] as $uploaded_file) {
if ($uploaded_file['status'] == 'done') {
$source = $uploaded_file['tmppath'];
$destination = file_stream_wrapper_uri_normalize($scheme . $uploaded_file['name']);
// Rename it to its original name, and put it in its final home.
// Note - not using file_move here because if we call file_get_mime
// (in file_uri_to_object) while it has a .tmp extension, it horks.
$destination = file_unmanaged_move($source, $destination, FILE_EXISTS_RENAME);
$file = file_uri_to_object($destination);
file_save($file);
_media_save_file_permenently($file);
$saved_files[] = $file;
}
else {
// @todo: move this to element validate or something.
form_set_error('pud', t('The specified file %name could not be uploaded.', array('%name' => $uploaded_file['name'])));
}
}
// Get a list of fids to pass back.
$fids = array();
foreach ($saved_files as $file) {
$fids[] = $file->fid;
}
$form_state['redirect'] = array('media/browser', array('query' => array('render' => 'media-popup', 'fid' => $fids)));
}
/**
* Form builder: Builds the edit file form.
*/
function media_edit($form, $form_state, $file) {
$form_state['file'] = $file;
field_attach_form('file', $file, $form, $form_state);
$form['#attached'] = array(
'css' => array(drupal_get_path('module', 'media') . '/css/media.css'),
);
// Not sure about this class name, seems to indicate the style.
$form['#attributes']['class'][] = 'media-image-left';
$form['#attributes']['class'][] = 'media-edit-form';
$form['preview'] = file_view_file($file, 'media_preview');
$form['preview']['#weight'] = -10;
$form['preview']['#suffix'] = '<div class="no-overflow">';
// Add the buttons.
$form['actions'] = array('#type' => 'actions');
$form['actions']['#prefix'] = '</div>';
$form['actions']['delete'] = array(
'#type' => 'submit',
'#value' => t('Delete'),
'#weight' => 15,
'#submit' => array('media_edit_delete_submit'),
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
'#weight' => 5,
'#submit' => array('media_edit_submit'),
);
// Add internal file properties needed by media_edit_validate().
foreach (array('fid', 'type') as $key) {
$form[$key] = array('#type' => 'value', '#value' => $file->$key);
}
return $form;
}
/**
* Form validation handler for the media edit form.
*/
function media_edit_validate($form, &$form_state) {
entity_form_field_validate('file', $form, $form_state);
}
/**
* Form submit handler for the media submit form.
*/
function media_edit_submit($form, &$form_state) {
$file = $form_state['file'];
entity_form_submit_build_entity('file', $file, $form, $form_state);
file_save($file);
$form_state['redirect'] = 'media/' . $file->fid;
}
/**
* Form submit handler for the Delete button on the media edit form.
*/
function media_edit_delete_submit($form, &$form_state) {
$fid = $form_state['values']['fid'];
$destination = array();
if (isset($_GET['destination'])) {
$destination = drupal_get_destination();
unset($_GET['destination']);
}
$form_state['redirect'] = array('media/' . $fid . '/delete', array('query' => $destination));
}
function media_add_remote($form, &$form_state) {
// Totally prototyping code to show designs.
$form['sources'] = array(
'#type' => 'vertical_tabs',
'#title' => 'Sources',
);
$form['sources']['paste'] = array(
'#type' => 'fieldset',
'#title' => 'Paste URL or embed code',
);
$providers = '';
$providers .= '<img style="height:50px; margin:20px" src="http://www.macuser.com/2008/10/09/top_youtube_logo_31_Dec_06.jpg">';
$providers .= '<img style="height:50px; margin:20px" src="http://jasonhilldesign.com/FlikrLogo.jpg">';
$form['sources']['paste']['code'] = array(
'#type' => 'textarea',
'#title' => t('URL or embed code'),
'#description' => t('The following providers are supported: <br/>' . $providers),
);
$form['sources']['youtube'] = array(
'#type' => 'fieldset',
'#title' => 'YouTube',
'#description' => t(''),
'#attributes' => array('style' => 'height: 300px; overflow:auto'),
);
$form['sources']['flikr'] = array(
'#type' => 'fieldset',
'#title' => 'Flikr',
);
$box = '<div style="width:100px; height:100px; border:1px solid blue; padding:10px; float:left; margin:5px;"> Video </div>';
$boxes = '';
for ($i = 0; $i < 10; $i++) {
$boxes .= $box;
}
$form['sources']['youtube']['stupid'] = array(
'#markup' => $boxes,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Import'),
'#attributes' => array('style' => 'float:right'),
'#suffix' => '<br style="clear:both" />',
);
return $form;
}

View File

@@ -0,0 +1,332 @@
<?php
/**
* @file
* Media Theming
*
* Theming functions for the Media module.
*/
/**
* Display the media file browser.
* @TODO this is depreciated I think
* @param array $element
* The form element.
* @return string
*/
function theme_media_file_browser($element) {
// Add the CSS for our display.
$output = '<div class="media browser">' . $element . '</div>';
return $output;
}
/**
* Display a media file list.
* @TODO this is depreciated I think
* @param array $element
* The form element.
* @return string
*/
function theme_media_file_list($element) {
// Add the CSS for our display.
return '<div class="media-file-list">' . theme('form_element', $element, $element['#children']) . '</div>';
}
/**
* Display a browser pane.
* @TODO this is depreciated I think
*
* @param array $form
* The form element.
* @return string
*/
function theme_media_browser_pane($form) {
return;
$output = array();
// render the drawers
$output[] = '<div' . drupal_attributes($form['#attributes']) . '>';
// render the drawer list
$output[] = ' <div class="browser drawers">';
$output[] = drupal_render_form(null, $form['drawers']);
$output[] = ' </div>';
// render the drawer displays
$output[] = drupal_render_form(null, $form);
$output[] = '</div>';
return implode("\n", $output);
}
/**
* Default theming function for creating the browser frame.
* Assumes an array of file objects as $files and an
* array of $parameters
* @param $variables
* array of variables
* @return unknown_type
*/
function theme_media_browser_content_frame($variables) {
// Pull out all the variables into a usable form
extract($variables);
// Did we get any files back?
if (! count($files)) {
// @TODO display no files found
}
$html = array();
// On the first invocation, load javascript and build the browser frame
if ($invoke) {
}
// Render the results limiter
$html[] = theme('media_browser_control_result_limit', array('parameters' => $parameters));
// Render the actual content
$form = drupal_get_form('media_file_listing_form', $files, $parameters);
$html[] = drupal_render($form);
// Make sure to close the wrapping div
if ($invoke) {
$html[] = '</div>';
}
return implode("\n", $html);
}
/**
* Display a item list of files as thumbnails. Implements
* the admin thumbnail theme for now- serves as a wrapper
*
* @param $files
* An array of file objects to display.
* @return
*/
function theme_media_browser_thumbnails($variables) {
$files = $variables['files'];
$style_name = $variables['style_name'];
$thumbnails = array();
foreach ($files as $file) {
$thumbnails[] = theme('media_admin_thumbnail', array('file' => $file, 'style_name' => $style_name));
}
return theme('item_list', array('items' => $thumbnails, 'attributes' => array('class' => 'media_content_navigator results')));
}
/**
* Theme a thumbnail.
* @param $variables
* array items being passed in
*/
function theme_media_admin_thumbnail($variables) {
$path = drupal_get_path('module', 'media');
$file = $variables['file'];
$style_name = $variables['style_name'];
if (isset($file)) {
$file_url = file_create_url($file->uri);
}
else {
return '';
}
$output = '';
if (module_exists('styles')) {
$thumbnail = theme('styles',
array(
'field_type' => 'file',
'style_name' => $style_name,
'uri' => $file->uri,
'description' => t('Thumbnail for !filename.', array('!filename' => $file->filename)),
'object' => $variables['file'],
));
}
else {
// Display a thumbnail for images.
if (strstr($file->filemime, 'image')) {
$thumbnail = theme('image_style',
array(
'style_name' => 'thumbnail',
'path' => $file->uri,
'alt' => t('Thumbnail for !filename.', array('!filename' => $file->filename)),
)
);
}
// Display the 'unknown' icon for other file types.
else {
$thumbnail = theme('image',
array(
'path' => $path . '/images/file-unknown.png',
'alt' => t('Thumbnail for !filename.', array('!filename' => $file->filename)),
'attributes' => array('class' => 'file-unknown'),
));
}
}
$output .= l($thumbnail,
$file_url,
array(
'html' => TRUE,
'attributes' => array('class' => 'media-thumbnail'),
));
return $output;
}
/**
* Theme operations for a thumbnail.
*/
function theme_media_admin_thumbnail_operations($variables) {
$destination = drupal_get_destination();
$file = $variables['file'];
$output = l(t('edit'), 'media/' . $file->fid . '/edit', array('query' => $destination));
return $output;
}
/**
* Add messages to the page.
*/
function template_preprocess_media_dialog_page(&$variables) {
$variables['messages'] = theme('status_messages');
}
/* ******************************************** */
/* Content navigation controls */
/* ******************************************** */
/**
* Theme function to display the results limiting- 10, 30, 50 results
* per page.
*
* @param $variables
* array parameters
* @return unknown
*/
function theme_media_browser_control_result_limit($variables) {
// Pull out all the variables into a usable form
extract($variables);
if (!isset($limits)) {
$limits = array(10, 30, 50);
}
// @NOTE these do not need to be aware of the current
// page because clicking them will reset the
// display to 1 -> $limit
$parameters['page'] = 0;
// save the active limit
$current_limit = $parameters['limit'];
$per_display = array();
foreach ($limits as $limit) {
if ($limit == $current_limit) {
$class = 'active';
}
else {
$class = '';
}
// set the value of this limit parameter to this limit value
$parameters['limit'] = $limit;
$per_display[] = l($limit, $limit, array('query' => $parameters, 'attributes' => array('class' => $class)));
}
return theme('item_list', array('items' => $per_display, 'attributes' => array('class' => 'result_limit')));
}
/**
* Stolen from file.module theme_file_link
*
* @param $variables
* An associative array containing:
* - file: A file object to which the link will be created.
*/
function theme_media_link($variables) {
$file = $variables['file'];
$url = 'media/' . $file->fid;
$icon = theme('file_icon', array('file' => $file));
// Set options as per anchor format described at
// http://microformats.org/wiki/file-format-examples
$options = array(
'attributes' => array(
'type' => $file->filemime . '; length=' . $file->filesize,
),
);
// Use the description as the link text if available.
if (empty($file->description)) {
$link_text = check_plain($file->filename);
}
else {
$link_text = check_plain($file->description);
$options['attributes']['title'] = check_plain($file->filename);
}
return '<span class="file">' . $icon . ' ' . l($link_text, $url, $options) . '</span>';
}
/**
* Adds a wrapper around a preview of a media file.
* @param unknown_type $element
* @return unknown_type
*/
function theme_media_thumbnail($variables) {
$label = '';
$element = $variables['element'];
$destination = drupal_get_destination();
// Wrappers to go around the thumbnail
$prefix = '<div class="media-item"><div class="media-thumbnail">';
$suffix = '</div></div>';
// Arguments for the thumbnail link
$thumb = $element['#children'];
$target = 'media/' . $element['#file']->fid . '/edit';
$options = array('query' => $destination, 'html' => TRUE, 'attributes' => array('title' => t('Click to edit details')));
// Element should be a field renderable array... This is a little wonky - admitted.
if (!empty($element['#show_names']) && $element['#name']) {
$label = '<div class="label-wrapper"><label class="media-filename">' . $element['#name'] . '</label></div>';
}
// How can I attach CSS here?
//$element['#attached']['css'][] = drupal_get_path('module', 'media') . '/css/media.css';
drupal_add_css(drupal_get_path('module', 'media') . '/css/media.css');
$output = $prefix;
if (!empty($element['#add_link'])) {
$output .= l($thumb, $target, $options);
}
else {
$output .= $thumb;
}
$output .= $label . $suffix;
return $output;
}
function template_preprocess_media_thumbnail(&$variables) {
// Set the name for the thumbnail to be the filename. This is done here so
// that other modules can hijack the name displayed if it should not be the
// filename.
$variables['element']['#name'] = isset($variables['element']['#file']->filename) ? check_plain($variables['element']['#file']->filename) : NULL;
}
/**
* Field formatter for displaying a file as a large icon.
*/
function theme_media_formatter_large_icon($variables) {
$file = $variables['file'];
$icon_dir = media_variable_get('icon_base_directory') . '/' . media_variable_get('icon_set');
$icon = file_icon_path($file, $icon_dir);
$variables['path'] = $icon;
// theme_image() requires the 'alt' attribute passed as its own variable.
// @see http://drupal.org/node/999338
if (!isset($variables['alt']) && isset($variables['attributes']['alt'])) {
$variables['alt'] = $variables['attributes']['alt'];
}
return theme('image', $variables);
}

View File

@@ -0,0 +1,321 @@
<?php
/**
* @file
* Helper functions related to media types. CRUD for saving their settings mainly.
*
* Also contains the media entity class definition.
* @see media.install for a list of the base types.
*/
/**
* Implements hook_file_type_info().
*/
function media_file_type_info() {
$types = array();
foreach (media_type_get_types() as $type => $type_object) {
$types[$type] = array(
'label' => $type_object->label,
'weight' => $type_object->weight,
'claim callback' => 'media_file_type_media_claim',
'default view callback' => 'media_file_type_media_default_view',
);
}
return $types;
}
/**
* Implements hook_file_type_TYPE_claim().
*
* Returns TRUE if the passed in file should be assigned the passed in type.
*/
function media_file_type_media_claim($file, $type) {
if (($type_object = media_type_load($type)) && ($function = $type_object->type_callback) && function_exists($function) && $function($file, $type_object->type_callback_args)) {
return TRUE;
}
}
/**
* Implements hook_file_type_TYPE_default_view().
*
* Returns a drupal_render() array for displaying the file when there are no
* administrator-configured formatters, or none of the configured ones return a
* display.
*/
function media_file_type_media_default_view($file, $view_mode, $langcode) {
// During preview, or when custom attribute values are needed on the displayed
// element, use a media icon.
if ($view_mode == 'media_preview' || isset($file->override)) {
return array(
'#theme' => 'media_formatter_large_icon',
'#file' => $file,
);
}
// Finally, fall back to File module's generic file display.
return array(
'#theme' => 'file_link',
'#file' => $file,
);
}
/**
* 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().
*
* @param object &$type
* $type is an object with the following fields:
* ->name => The name of the media asset type, such as 'video';
* ->label => The human readable name;
* ->base => boolean: If the media type cannot be removed.
* ->type_callback => Function call to filter an instance to its bundle.
* ->type_callback_args => An array of args to be passed to type_callback.
* @return void;
*/
function media_type_save(&$type) {
if (empty($type->name)) {
throw new Exception('Enable to add type, name not provided');
}
$type = media_type_set_defaults($type);
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();
// Clear the caches
drupal_static_reset('media_type_get_types');
drupal_static_reset('media_type_get_mime_map');
return;
}
/**
* @todo Remove this function after ensuring that nothing (including update
* functions) call it. It is deprecated with the change from media entity
* containing a file field to just a file entity.
*/
function media_type_configure_formatters($name, $view_modes_to_formatters) {
}
/**
* Loads a media type based on its machine name.
*
* @param string $name
* @return StdClass
*/
function media_type_load($name) {
$types = media_type_get_types();
if (isset($types[$name])) {
return $types[$name];
}
}
/**
* Loads all media types into an array keyed by machine name and sorted
* and weighted lexographically.
*
* @return array
* Media types keyed by machine name.
*/
function media_type_get_types() {
$types =& drupal_static(__FUNCTION__);
if (!$types) {
$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);
}
}
return $types;
}
/**
* Create the basic class and defaults for a media entity bundle type.
*/
function media_type_set_defaults($info) {
$type = new stdClass();
// This is used to filter a file to the proper bundle.
$type->type_callback = 'media_is_type';
$type->type_callback_args = array();
$type->weight = 0;
foreach ($info as $k => $v) {
$type->{$k} = $v;
}
return $type;
}
/**
* Determines the type of media a passed in $file is.
*
* @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;
}
}
throw new Exception('Unable to determine type of media from ' . var_export($file, 1));
}
/**
* Default callback used to determine if a file is of a given type.
*
* @TODO: document 'any' and 'all' matching.
*
* @param $file
* The file object.
* @param $args
*
* @return unknown_type
*/
function media_is_type($file, $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, $file->filemime)) {
if (!$match_all) {
return TRUE;
}
}
}
// Was not matched, so return
if ($match_all) {
return FALSE;
}
}
if (!empty($args['extensions'])) {
if (in_array(pathinfo($file->uri, PATHINFO_EXTENSION), $args['extensions'])) {
if (!$match_all) {
return TRUE;
}
}
// Was not matched, so return
if ($match_all) {
return FALSE;
}
}
if (!empty($args['streams'])) {
}
}
/**
* Implements hook_media_format_form_prepare_alter().
*/
function media_media_format_form_prepare_alter(&$form, &$form_state, $file) {
switch ($file->type) {
case 'image':
$form['options']['alt'] = array(
'#type' => 'textfield',
'#title' => t('Description'),
'#default_value' => $file->filename,
'#description' => t('Alternate text a user will see if the image is not available'),
);
break;
}
}
/**
* Returns the number of files that need to be converted to media.
*/
function media_type_invalid_files_count() {
return db_select('file_managed', 'fm')
->condition('type', FILE_TYPE_NONE)
->countQuery()
->execute()
->fetchField();
}
/**
* Adds a value for the type column in files_managed.
*
* If $update_existing is TRUE, will update the type of files with an existing type value.
*
* @param boolean $update_existing
* @param integer $no_to_update
* @param integer $offset
*
* @return array
* A list of updated file ids
*/
function media_type_batch_update($update_existing = FALSE, $no_to_update = NULL, $offset = 0) {
$results = array();
$query = db_select('file_managed', 'fm')
->fields('fm', array('fid'));
if (!$update_existing) {
$query->condition('type', FILE_TYPE_NONE);
}
if ($no_to_update) {
$query->range($offset, $no_to_update);
}
elseif ($offset) {
$query->range($offset);
}
$fids = $query->execute()->fetchCol();
foreach ($fids as $fid) {
$file = file_load($fid);
if (!$file->fid) {
throw new Exception('Unable to continue, file was not loaded.');
}
file_save($file);
$results[] = $fid;
}
return $results;
}
/**
* Implements hook_file_default_displays().
*
* Provides default display configurations for media types.
*
* @see file_entity_schema()
*/
function media_file_default_displays() {
$default_displays = array();
$default_image_styles = array(
'media_preview' => 'square_thumbnail',
'media_large' => 'large',
'media_original' => ''
);
foreach ($default_image_styles as $view_mode => $image_style) {
$display_name = 'image__' . $view_mode . '__file_image';
$default_displays[$display_name] = (object) array(
'api_version' => 1,
'name' => $display_name,
'status' => 1,
'weight' => 5,
'settings' => array('image_style' => $image_style),
);
}
return $default_displays;
}

View File

@@ -0,0 +1,165 @@
<?php
/**
* @file
* Contains the variables used by media and their defaults.
*/
/* ***************************************** */
/* CONSTANTS */
/* ***************************************** */
/**
* @TODO: iz_cruft?
* @var unknown_type
*/
define('MEDIA_RESOURCE_URI_DEFAULT', 'public://');
define('MEDIA_TYPES_DEFAULT', '*');
/**
* Define constants.
*/
define('MEDIA_VARIABLE_NAMESPACE', 'media__');
/**
* Wrapper for variable_get() that uses the Media variable registry.
*
* @param string $name
* The variable name to retrieve. Note that it will be namespaced by
* pre-pending MEDIA_VARIABLE_NAMESPACE, as to avoid variable collisions with
* other modules.
* @param unknown $default
* An optional default variable to return if the variable hasn't been set
* yet. Note that within this module, all variables should already be set
* in the media_variable_default() function.
* @return unknown
* Returns the stored variable or its default.
*
* @see media_variable_set()
* @see media_variable_del()
* @see media_variable_default()
*/
function media_variable_get($name, $default = NULL) {
// Allow for an override of the default.
// Useful when a variable is required (like $path), but namespacing still desired.
if (!isset($default)) {
$default = media_variable_default($name);
}
// Namespace all variables
$variable_name = MEDIA_VARIABLE_NAMESPACE . $name;
return variable_get($variable_name, $default);
}
/**
* Wrapper for variable_set() that uses the Media variable registry.
*
* @param string $name
* The variable name to set. Note that it will be namespaced by
* pre-pending MEDIA_VARIABLE_NAMESPACE, as to avoid variable collisions with
* other modules.
* @param unknown $value
* The value for which to set the variable.
* @return unknown
* Returns the stored variable after setting.
*
* @see media_variable_get()
* @see media_variable_del()
* @see media_variable_default()
*/
function media_variable_set($name, $value) {
$variable_name = MEDIA_VARIABLE_NAMESPACE . $name;
return variable_set($variable_name, $value);
}
/**
* Wrapper for variable_del() that uses the Media variable registry.
*
* @param string $name
* The variable name to delete. Note that it will be namespaced by
* pre-pending MEDIA_VARIABLE_NAMESPACE, as to avoid variable collisions with
* other modules.
*
* @see media_variable_get()
* @see media_variable_set()
* @see media_variable_default()
*/
function media_variable_del($name) {
$variable_name = MEDIA_VARIABLE_NAMESPACE . $name;
variable_del($variable_name);
}
/**
* Returns the final machine name of a Media namespaced variable.
*/
function media_variable_name($name) {
return MEDIA_VARIABLE_NAMESPACE . $name;
}
/**
* The default variables within the Media namespace.
*
* @param string $name
* Optional variable name to retrieve the default. Note that it has not yet
* been pre-pended with the MEDIA_VARIABLE_NAMESPACE namespace at this time.
* @return unknown
* The default value of this variable, if it's been set, or NULL, unless
* $name is NULL, in which case we return an array of all default values.
*
* @see media_variable_get()
* @see media_variable_set()
* @see media_variable_del()
*/
function media_variable_default($name = NULL) {
static $defaults;
if (!isset($defaults)) {
$defaults = array(
'wysiwyg_title' => 'Media browser',
'wysiwyg_icon_title' => 'Add media',
//@todo: We should do this per type actually. For "other" it should be a link.
'wysiwyg_default_view_mode' => 'media_large',
// Types which can be selected when embedding media vs wysiwyg.
'wysiwyg_allowed_types' => array('audio', 'image', 'video'),
// Attributes which can be modified via the wysiwyg and persist.
'wysiwyg_allowed_attributes' => array('height', 'width', 'hspace', 'vspace', 'border', 'align', 'style', 'alt', 'title', 'class', 'id'),
'field_select_media_text' => 'Select media',
'field_remove_media_text' => 'Remove media',
// Name of the theme to use in media popup dialogs, defaults to admin_theme
'dialog_theme' => '',
// @TODO: Make a configuration form with this.
'file_extensions' => 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp mp3 mov m4v mp4 mpeg avi ogg oga ogv wmv ico',
'max_filesize' => '',
'debug' => FALSE,
'file_list_size' => 10,
// Used in media.xml.inc: how long to cache retrieved remote data.
'xml_cache_expire' => 3600,
// Browser defaults in media.browser.inc.
'browser_viewtype_default' => 'thumbnails',
'browser_pager_limit' => 40,
'browser_library_empty_message' => 'There are currently no media in this library to select.',
'import_batch_size' => 20,
'fromurl_supported_schemes' => array('http', 'https', 'ftp', 'smb', 'ftps'),
'type_icon_directory' => drupal_get_path('module', 'media') . '/images/types',
'icon_base_directory' => drupal_get_path('module', 'media') . '/images/icons',
'icon_set' => 'default',
// This is set in media_enable(). It will show a persistant dsm on every page
// until the user runs the batch operation provided by media_admin_rebuild_types_form().
'show_file_type_rebuild_nag' => FALSE,
);
}
if (!isset($name)) {
return $defaults;
}
if (isset($defaults[$name])) {
return $defaults[$name];
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* @file
* Provide Views data and handlers for media.module
*/
/**
* Implements hook_field_views_data().
*/
function media_field_views_data($field) {
$data = field_views_field_default_views_data($field);
foreach ($data as $table_name => $table_data) {
// Add the relationship only on the fid field.
$data[$table_name][$field['field_name'] . '_fid']['relationship'] = array(
'handler' => 'views_handler_relationship',
'base' => 'file_managed',
'entity type' => 'file',
'base field' => 'fid',
'label' => t('file from !field_name', array('!field_name' => $field['field_name'])),
);
}
return $data;
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* @file
* XML data retrieval and storage API for Media.
*/
/**
* A wrapper around simplexml to retrieve a given XML file.
*
* @param $url
* The URL to the XML to retrieve.
* @param $display_errors
* Optional; if TRUE, then we'll display errors to the end user. They'll be
* logged to the watchdog in any case.
* @param $refresh
* Optional; if TRUE, then we'll force a new load of the XML. Otherwise,
* a cached version will be retrieved if possible.
* @return
* A fully populated object, or FALSE on an error.
*/
function _media_retrieve_xml($url, $display_errors = FALSE, $refresh = FALSE) {
$xmls = &drupal_static(__FUNCTION__, array());
// Return our cached XML if allowed, and it exists.
if (!$refresh && isset($xmls[$url])) {
return $xmls[$url];
}
elseif (!$refresh && $cache = cache_get('media:xml:' . $url, 'cache_media_xml')) {
$xmls[$url] = $cache->data;
return $xmls[$url];
}
// Enable user error handling.
libxml_use_internal_errors(TRUE);
// Load the document
$xml = simplexml_load_file($url);
if (!$xml) {
foreach (libxml_get_errors() as $error) {
$params = array('%url' => $url, '%error' => $error->message);
// Handle errors here.
if ($display_errors) {
drupal_set_message(t('Error retrieving XML from %url: %error', $params), 'error');
}
watchdog('media', 'Error retrieving XML from %url: %error', $params, WATCHDOG_WARNING);
}
// Clear our error cache.
libxml_clear_errors();
// Set the static cache, but not Drupal's cache, so we can attempt to
// retrieve the file another time if possible.
$xmls[$url] = FALSE;
}
else {
$xmls[$url] = _media_unserialize_xml($xml);
cache_set('media:xml:' . $url, $xmls[$url], 'cache_media_xml', media_variable_get('xml_cache_expire', 3600));
}
return $xmls[$url];
}
/**
* Recursively converts a SimpleXMLElement object into an array.
* @param $xml
* The original XML object.
*/
function _media_unserialize_xml($xml) {
if ($xml instanceof SimpleXMLElement) {
$xml = (array) $xml;
}
if (is_array($xml)) {
foreach ($xml as $key => $item) {
$xml[$key] = _media_unserialize_xml($item);
}
}
return $xml;
}