media_browser_plus.module in Media Browser Plus 7.2
Same filename and directory in other branches
Adds fields to the media browser forms for better UX
File
media_browser_plus.moduleView source
<?php
/**
* @file
* Adds fields to the media browser forms for better UX
*/
/** ***************************************** */
/** INCLUDES */
/** ***************************************** */
// A registry of variable_get defaults.
require_once dirname(__FILE__) . '/includes/media_browser_plus.variables.inc';
require_once dirname(__FILE__) . '/includes/file_entity.admin.inc';
/**
* Puts all selected media items into a zip archive and sends it as download.
*
* @TODO: check for internet sources etc. Only local files should be parsed.
*
* @param array $form
* The form structure.
* @param array $form_state
* The form state.
*/
function media_browser_plus_download_images_submit($form, &$form_state) {
if (isset($form_state['input']['selected_media']) && media_browser_plus_access('download media')) {
$ids = array_keys($form_state['input']['selected_media']);
// Only load those.
$conditions[] = array(
'property' => array(
'fid',
array(
$ids,
),
'IN',
),
);
$media_entities = media_browser_plus_load_multiple(array(
'conditions' => $conditions,
'apply_filter' => FALSE,
'paging' => FALSE,
));
// If possible avoid duplicate compression.
if (function_exists('apache_setenv')) {
@apache_setenv('no-gzip', '1');
}
// Create archive.
$name = 'media-download-' . md5(microtime() . uniqid());
$zip_file = '/tmp/' . $name . '.zip';
$zip = new ZipArchive();
$res = $zip
->open($zip_file, ZipArchive::CREATE);
if ($res === TRUE && count($media_entities->results)) {
foreach ($media_entities->results as $media) {
$zip
->addFile(drupal_realpath($media->uri), $media->filename);
}
$zip
->close();
header('Cache-Control: public');
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: public');
// header('Content-Description: File Transfer');
header('Content-type: application/zip');
header('Content-Disposition: attachment; filename=' . $zip_file);
// header('Content-Transfer-Encoding: binary');
header('Content-length: ' . filesize($zip_file));
readfile($zip_file);
unlink($zip_file);
die;
}
else {
drupal_set_message(t('Failed to create download archive'), 'error');
}
}
}
/**
* Called by the JS fronted (ajax) to change the folder of a media object.
*/
function media_browser_plus_change_folder($form, &$form_state) {
if (!isset($form_state['input']['media'])) {
// @fixme Decide: die() or return.
die('');
return $form;
}
// Parse values.
$folder = (int) str_replace('folder_load_', '', $form_state['input']['folder']);
$media_id = (int) str_replace('edit-files-', '', $form_state['input']['media']);
if (!$media_id) {
// @fixme Decide: die() or return.
die('');
return $form;
}
$media = file_load($media_id);
// Apply new folder.
if (isset($media->field_folder[LANGUAGE_NONE][0]['tid'])) {
$media->field_folder[LANGUAGE_NONE][0]['tid'] = $folder;
}
else {
$media->field_folder = array(
LANGUAGE_NONE => array(
array(
'tid' => $folder,
),
),
);
}
// Save changes.
media_browser_plus_move_file($folder, $media);
// @fixme Decide: die() or return.
die('');
return $form;
}
/**
* Called by the JS fronted (ajax) to get the media list for a given folder.
*/
function media_browser_plus_thumbnailsJSON() {
if (isset($_GET['folder'])) {
$folder = (int) str_replace('folder_load_', '', $_GET['folder']);
// Create conditions.
$conditions = array();
// Check for filter set by library.
if (isset($_GET['filter'])) {
$filter = drupal_json_decode($_GET['filter']);
// Bugfix - $conditions = $filter;
foreach ($filter as $key => $value) {
// Checking each filter.
$valid = TRUE;
foreach ($value as $type => $params) {
foreach ($params as $param) {
if (is_array($param)) {
$valid = !empty($param);
foreach ($param as $media_type) {
if (strlen(trim($media_type)) == 0) {
$valid = FALSE;
break;
}
}
}
else {
if (strlen(trim($param)) == 0) {
$valid = FALSE;
break;
}
}
}
}
if ($valid) {
$conditions[] = $value;
}
}
}
// More conditions.
if ($folder) {
$conditions[] = array(
'field' => array(
'field_folder',
'tid',
array(
$folder,
),
'IN',
),
);
}
elseif ($folder === 0) {
// Get unsorted files. We can't use EFQ conditions because the field
// tables are joined using inner join, what excludes unsorted files from
// the whole query at all. Thus fetch the fids of the unsorted fields and
// use this as condition.
$query = db_select('file_managed')
->fields('file_managed', array(
'fid',
'fid',
))
->condition('uri', db_like('temporary://') . '%', 'NOT LIKE')
->isNull('entity_id');
$query
->leftJoin('field_data_field_folder', 'folder', "entity_type = 'file' AND entity_id = file_managed.fid");
$result = $query
->execute()
->fetchAll(PDO::FETCH_KEY_PAIR);
$conditions[] = array(
'property' => array(
'fid',
$result,
'IN',
),
);
}
$order = array(
array(
'property' => array(
'fid',
'DESC',
),
),
);
$media_entities = media_browser_plus_load_multiple(array(
'conditions' => $conditions,
'order' => $order,
));
module_load_include('inc', 'media', 'includes/media.browser');
foreach ($media_entities->results as $media) {
media_browser_build_media_item($media);
}
$output = array(
'media' => array_values($media_entities->results),
'folder_loaded' => 'folder_load_' . $folder,
'overall_count' => $media_entities->overall_count,
);
drupal_json_output($output);
die;
}
}
/**
* Implements hook_form_FORM_ID_alter() for media_add_upload_multiple().
*
* @deprecated Remove once >= Media7.x-2.0-unstable5 is wide spread.
*/
function media_browser_plus_form_media_add_upload_multiple_alter(&$form, &$form_state) {
media_browser_plus_form_file_entity_add_upload_multiple_alter($form, $form_state);
}
/**
* Implements hook_form_FORM_ID_alter() for file_entity_add_upload_multiple().
*/
function media_browser_plus_form_file_entity_add_upload_multiple_alter(&$form, &$form_state) {
// This builds the tags textfield and adds the autocomplete handlers to it.
// The #element_validate may not be necessary because this never triggers
// hook_field_update() or hook_field_insert()
$form['field_tags'] = _media_browser_plus_tag_form();
$form['field_folder'] = _media_browser_plus_folder_form();
// Maintain the order of the other form items.
$form['upload']['#weight'] = 0;
$form['submit']['#weight'] = 2;
// Add an additional form submission callback that fires after the default.
$form['#submit'][] = 'media_browser_plus_submit';
}
/**
* Implements hook_form_FORM_ID_alter() for media_import().
*/
function media_browser_plus_form_media_import_alter(&$form, &$form_state) {
$form['field_tags'] = _media_browser_plus_tag_form();
$form['field_folder'] = _media_browser_plus_folder_form();
// Change submit.
$form['submit']['#weight'] = 2;
$form['#submit'] = array(
'media_browser_plus_media_import_submit',
);
}
/**
* Changing the media import standard submit to use our own batch process.
*/
function media_browser_plus_media_import_submit($form, &$form_state) {
if ($form_state['values']['op'] == t('Confirm')) {
$files = $form_state['storage']['files'];
$batch = array(
'title' => t('Importing Media'),
'operations' => array(
array(
'media_browser_plus_media_import_batch_import_files',
array(
$files,
$form_state['values'],
),
),
),
'finished' => 'media_browser_plus_media_import_batch_complete',
);
if (!empty($form_state['values']['field_tags'])) {
// Create any new taxonomy terms.
foreach ($form_state['values']['field_tags'] as $i => &$item) {
if ($item['tid'] == 'autocreate') {
$term = (object) $item;
unset($term->tid);
taxonomy_term_save($term);
$item['tid'] = $term->tid;
}
}
}
batch_set($batch);
return;
}
$form_state['rebuild'] = TRUE;
}
/**
* Batch process with the ability to apply the field values to the items.
*/
function media_browser_plus_media_import_batch_import_files($files, $form_values, &$context) {
// Split up attributes.
// list($files, $form_values) = $attributes;
// Need to repeat a lot of code here just to add the fields :-(
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 = media_variable_get('import_batch_size', 10);
if ($length > count($files)) {
$length = 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;
// Adding fields here.
$media = file_load($file_obj->fid);
$media->field_folder[LANGUAGE_NONE][0] = array(
'tid' => $form_values['field_folder'],
);
$media->field_tags[LANGUAGE_NONE] = $form_values['field_tags'];
media_browser_plus_move_file($form_values['field_folder'], $media);
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.
$media = file_load($file_obj->fid);
$image_in_message = file_view_file($media, 'media_preview');
}
} catch (Exception $e) {
$context['results']['errors'][] = $file . ' Reason: ' . $e
->getMessage();
}
}
$context['message'] = t('Importing') . theme('item_list', array(
'items' => $to_process,
));
// Just for kicks, show an image we are importing.
$context['message'] .= drupal_render($image_in_message);
$context['finished'] = ($context['sandbox']['max'] - count($files)) / $context['sandbox']['max'];
}
/**
* Same completed batch method as in media.
*/
function media_browser_plus_media_import_batch_complete($success, $results, $operations) {
if ($results['errors']) {
drupal_set_message(filter_xss(theme('item_list', array(
'items' => $results['errors'],
))), 'error');
}
if ($results['success']) {
drupal_set_message(filter_xss(theme('item_list', array(
'items' => $results['success'],
))));
}
}
/**
* Implements hook_form_FORM_ID_alter() for media_internet().
*/
function media_browser_plus_form_media_internet_add_alter(&$form, &$form_state) {
// Add an additional form submission callback that fires after the default.
media_browser_plus_form_file_entity_add_upload_alter($form, $form_state);
}
/**
* Form element validate handler for taxonomy term autocomplete element.
*
* Because media_browser_plus_form_file_entity_add_upload_multiple_alter() adds
* a tags widget to a form that is not an entity editing form, but it is desired
* for taxonomy_autocomplete_validate() to have access to the settings of the
* field for which this widget is being added, this handler runs before
* taxonomy_autocomplete_validate(), and adds the expected information to
* $form_state, as expected by taxonomy_autocomplete_validate().
*/
function media_browser_plus_prepare_taxonomy_autocomplete_validate(&$element, &$form_state) {
// Causes: Notice: Undefined index: #language in
// media_browser_plus_prepare_taxonomy_autocomplete_validate()
// (line 436 of ...sites/all/modules/media_browser_plus-HEAD/media_browser_plus.module).
// @TODO: Fix.
$form_state['field'][$element['#field_name']][$element['#language']]['field'] = field_info_field($element['#field_name']);
// Fixes: undefined index #field_parents in field_widget_field().
if (!isset($element['#field_parents'])) {
$element['#field_parents'] = array();
}
}
/**
* Submit handler for the media browser forms that create new media entities.
*
* Enhances the media creation process by populating field content for the newly
* created entities from user-submitted data and/or data available from a
* remote provider.
*
* @see media_browser_plus_form_file_entity_add_upload_multiple_alter()
* @see media_browser_plus_form_media_internet_add_alter()
*/
function media_browser_plus_submit($form, &$form_state) {
// Grab the fids of the newly created media entities from the redirect query
// string that was created by the form's primary submit handler, and load the
// corresponding entities.
$fids = NULL;
if (isset($form_state['files'])) {
foreach ($form_state['files'] as $i => $fid) {
$fids[$i] = $fid->fid;
$i++;
}
}
elseif (isset($form_state['file'])) {
$fids = $form_state['file']->fid;
}
elseif (isset($form_state['redirect'][1]['query']['fid'])) {
// Compatibility mode.
// @TODO: Remove once the latest versions of media and file_entity are wide
// spread.
$fids = $form_state['redirect'][1]['query']['fid'];
}
elseif (isset($form_state['storage']['upload'])) {
$fids = $form_state['storage']['upload'];
}
if (!is_array($fids)) {
$fids = array(
$fids,
);
}
$media_entities = file_load_multiple($fids);
// If tags have been entered, apply them to each new entity.
if (!empty($form_state['values']['field_tags'])) {
// Create any new taxonomy terms.
foreach ($form_state['values']['field_tags'] as $i => &$item) {
if ($item['tid'] == 'autocreate') {
$term = (object) $item;
unset($term->tid);
taxonomy_term_save($term);
$item['tid'] = $term->tid;
}
unset($item);
}
foreach ($media_entities as $media) {
$media->field_tags[LANGUAGE_NONE] = $form_state['values']['field_tags'];
}
}
// Apply folder.
foreach ($media_entities as $media) {
$media->field_folder[LANGUAGE_NONE] = array(
array(
'tid' => $form_state['values']['field_folder'],
),
);
}
// If the new media is from a 3rd party provider, and that provider also
// provides MRSS data about the media, then populate the title and description
// fields from that data.
if (!empty($form_state['values']['embed_code'])) {
$provider = media_internet_get_provider($form_state['values']['embed_code']);
if ($data = _media_browser_plus_metadata($provider)) {
foreach ($data as $field_name => $value) {
$field = field_info_field($field_name);
// Limiting value population only if the field is of type 'text' or
// 'text_long' isn't as extensible as would be ideal, but we need some
// protection against populating a field with incompatible content.
if (isset($field) && in_array($field['type'], array(
'text',
'text_long',
)) && isset($field['bundles']['media'])) {
foreach ($media_entities as $media) {
if (in_array($media->type, $field['bundles']['media']) && !isset($media->{$field_name}[LANGUAGE_NONE][0]['value'])) {
$media->{$field_name}[LANGUAGE_NONE][0]['value'] = $value;
}
}
}
}
}
}
foreach ($media_entities as $media) {
media_browser_plus_move_file($form_state['values']['field_folder'], $media);
}
if (!empty($form_state['redirect']) && is_string($form_state['redirect']) && mb_strpos($form_state['redirect'], 'file/', 0, 'UTF-8') === 0) {
// Path was set to file/%file, but we need to thoroughly check if we have
// access to "admin/content/file", using a failsafe approach.
$item = menu_get_item('admin/content/file');
if ($item['access']) {
$form_state['redirect'] = 'admin/content/file';
}
}
}
/**
* Form submit handler for the media browser forms that edit media entities.
*
* Changes file's filesystem physical folder
*
* @see media_browser_plus_form_media_edit_alter()
*/
function media_browser_plus_edit_file_submit($form, &$form_state) {
$media = $form_state['file'];
$folder_id = $media->field_folder[LANGUAGE_NONE][0]['tid'];
// Only save it if the folder id is changed.
if ($form['field_folder'][LANGUAGE_NONE]['#default_value'][0] != $folder_id) {
media_browser_plus_move_file($folder_id, $media);
}
}
/**
* Helper function to return metadata from a 3rd party media provider.
*
* Support for 3rd party metadata such as YouTube.
*
* @param object $provider
* A provider object as returned by media_internet_get_provider().
*
* @return array
* An array of media metadata available from the provider, keyed on field
* name.
*
* @see http://video.search.yahoo.com/mrss
* @see media_internet_get_provider()
* @see MediaInternetYouTubeHandler::getMRSS()
*/
function _media_browser_plus_metadata($provider) {
// @todo This is early, experimental code, still subject to much change. For
// now, we assume $provider->getMRSS() returns a SimpleXML element. We'll
// want to change this assumption and have it return an array instead, but
// that requires fixing media_retrieve_xml() to handle XML namespaces
// properly.
$data = array();
if (is_callable(array(
$provider,
'getMRSS',
)) && ($rss = $provider
->getMRSS())) {
// MRSS is an extension of RSS, so the title field is available in the
// default (ATOM) namespace.
$data['media_title'] = (string) $rss->title;
// The MRSS extensions are in their own namespace.
$mrss = $rss
->children('http://search.yahoo.com/mrss/');
$data['media_description'] = (string) $mrss->group->description;
}
$data = array_filter($data, 'strlen');
return $data;
}
/**
* Implements hook_preprocess_media_link().
*/
function media_browser_plus_preprocess_media_link(&$variables) {
// Use the value of the title field, when there is one, as the link text for
// all links that would otherwise default to the filename.
// @todo Solve generically using the 'label' key of hook_entity_indo(). See
// http://drupal.org/node/910396.
$media = file_load($variables['file']->fid);
if (empty($variables['file']->description) && isset($media->media_title[LANGUAGE_NONE][0]['value'])) {
$variables['file']->description = $media->media_title[LANGUAGE_NONE][0]['value'];
}
}
/**
* Implements hook_preprocess_media_thubmnail().
*/
function media_browser_plus_preprocess_media_thumbnail(&$variables) {
// See media_browser_plus_preprocess_media_link(). Same thing here, but for
// the links that appear underneath thumbnail previews.
$media = $variables['element']['#file'];
if (isset($media->media_title[LANGUAGE_NONE][0]['value'])) {
$variables['element']['#name'] = $media->media_title[LANGUAGE_NONE][0]['value'];
}
}
/**
* Implements hook_form_FORM_ID_alter() for media_add_upload().
*
* Altering the add file upload form the include folder and tag options
*
* @deprecated Remove once >= Media7.x-2.0-unstable5 is wide spread.
*/
function media_browser_plus_form_media_add_upload_alter(&$form, &$form_state) {
media_browser_plus_form_file_entity_add_upload_alter($form, $form_state);
}
/**
* Implements hook_form_FORM_ID_alter() for file_entity_add_upload().
*
* Altering the add file upload form the include folder and tag options
*/
function media_browser_plus_form_file_entity_add_upload_alter(&$form, &$form_state) {
// Alter weight to display new forms in correct order.
$form['file']['#weight'] = -5;
$form['submit']['#weight'] = 5;
$form['field_tags'] = _media_browser_plus_tag_form();
$form['field_folder'] = _media_browser_plus_folder_form();
$form['#submit'][] = 'media_browser_plus_submit';
}
/**
* Implements hook_form_FORM_ID_alter() for file_entity_edit().
*
* Ensure the tagging form is below the folder form part.
*/
function media_browser_plus_form_file_entity_edit_alter(&$form, &$form_state) {
// Setting the weight accordingly.
if (isset($form['field_tags']['#weight'])) {
$form['field_folder']['#weight'] = $form['field_tags']['#weight'] - 1;
}
$form['actions']['cancel'] = array(
'#type' => 'submit',
'#value' => t('Cancel'),
'#weight' => 20,
'#submit' => array(
'media_browser_plus_edit_cancel',
),
);
$form['actions']['submit']['#submit'][] = 'media_browser_plus_edit_file_submit';
}
/**
* Handles a cancelation of the file_entity_edit() form.
*
* Redirects the user to the appropriate location.
*/
function media_browser_plus_edit_cancel($form, &$form_state) {
$destination = array();
if (isset($_GET['destination'])) {
$destination = drupal_get_destination();
unset($_GET['destination']);
}
if (isset($destination['destination'])) {
$parsed_url = parse_url($destination['destination']);
parse_str($parsed_url['query'], $query);
$form_state['redirect'] = array(
$parsed_url['path'],
array(
'query' => $query,
),
);
}
}
/**
* Returns the tag form element.
*/
function _media_browser_plus_tag_form() {
return array(
'#weight' => 1,
'#language' => LANGUAGE_NONE,
'#field_name' => 'field_tags',
'#columns' => array(
'tid',
),
'#title' => t('Tags'),
'#description' => t('Enter a comma-separated list of words to describe your media.'),
'#required' => FALSE,
'#delta' => 0,
'#type' => 'textfield',
'#default_value' => '',
'#autocomplete_path' => 'taxonomy/autocomplete/field_tags',
'#element_validate' => array(
'media_browser_plus_prepare_taxonomy_autocomplete_validate',
'taxonomy_autocomplete_validate',
),
'#size' => 60,
);
}
/**
* Returns a select form item with a selectable media folders.
*/
function _media_browser_plus_folder_form() {
$list = _media_browser_plus_folder_list();
return array(
'#type' => 'select',
'#language' => LANGUAGE_NONE,
'#title' => t('Media Folder'),
'#field_name' => 'field_tags',
'#weigth' => -2,
'#options' => $list,
'#description' => t('Select a folder for the media to be put in'),
);
}
/**
* Implements hook_menu().
*/
function media_browser_plus_menu() {
$items['admin/content/file/change_folder'] = array(
'title' => 'Change Folder',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'media_browser_plus_change_folder',
),
'type' => MENU_CALLBACK,
'access callback' => 'media_browser_plus_access',
'access arguments' => array(
'edit media files',
),
'file' => 'media_browser_plus.module',
);
$items['admin/content/file/filter'] = array(
'title' => 'Media Filter',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'media_browser_plus_media_filter',
),
'file' => 'includes/media_browser_plus.pages.inc',
'access callback' => 'media_browser_plus_access',
'access arguments' => array(
'filter media',
),
'type' => MENU_LOCAL_ACTION,
'context' => MENU_CONTEXT_INLINE,
);
$items['admin/content/file/thumbnailsJSON'] = array(
'title' => 'Load Media Entities',
'page callback' => 'media_browser_plus_thumbnailsJSON',
'page arguments' => array(),
'access callback' => 'media_browser_plus_access',
'access arguments' => array(
'view files',
),
'type' => MENU_CALLBACK,
'file' => 'media_browser_plus.module',
);
$items['admin/config/media/media_browser_plus_settings'] = array(
'title' => 'Media Browser Plus Settings',
'description' => 'Change the behaviour and layout of the media browser plus UI',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'media_browser_plus_media_settings',
),
'access arguments' => array(
'administer files',
),
);
$items['admin/content/file/edit_multiple/%'] = array(
'title' => 'Edit Media Items',
'page callback' => 'media_browser_plus_edit_multiple_form',
'page arguments' => array(
4,
),
'file' => 'includes/media_browser_plus.pages.inc',
'access callback' => 'media_browser_plus_access',
'access arguments' => array(
'edit media files',
),
);
$items['admin/content/file/delete_multiple/%'] = array(
'title' => 'Delete Media Items',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'media_browser_plus_delete_multiple_form',
4,
),
'file' => 'includes/media_browser_plus.pages.inc',
'access callback' => 'media_browser_plus_access',
'access arguments' => array(
'edit media files',
),
);
// Folder management disabled until menu bug has been fixed.
$items['admin/content/file/folder_list'] = array(
'title' => 'Administer folders',
'description' => 'Manage your media folders',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'media_browser_plus_folder_list',
),
'file' => 'includes/media_browser_plus.folders.inc',
'access callback' => 'media_browser_plus_access',
'access arguments' => array(
'administer media folders',
),
);
$items['admin/content/file/add_folder'] = array(
'title' => 'Add Folder',
'description' => 'Add a new media folder',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'media_browser_plus_folder_add',
),
'access callback' => 'media_browser_plus_access',
'access arguments' => array(
'administer media folders',
),
'file' => 'includes/media_browser_plus.folders.inc',
);
$items['admin/content/file/folder/%media_browser_plus_folder/edit'] = array(
'title' => 'Edit Folder',
'description' => 'Edit media folder',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'media_browser_plus_folder_edit',
4,
),
'access callback' => 'media_browser_plus_access',
'access arguments' => array(
'administer media folders',
),
'file' => 'includes/media_browser_plus.folders.inc',
);
$items['admin/content/file/folder/%media_browser_plus_folder/delete'] = array(
'title' => 'Delete Folder',
'description' => 'Delete media folder',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'media_browser_plus_folder_delete',
4,
),
'access callback' => 'media_browser_plus_access',
'access arguments' => array(
'administer media folders',
),
'file' => 'includes/media_browser_plus.folders.inc',
);
$items['admin/content/file/%file/preview'] = array(
'title' => 'Preview Media',
'description' => 'Preview Media Item',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'media_browser_plus_media_preview',
3,
),
'access callback' => 'media_browser_plus_access',
'access arguments' => array(
'preview media',
),
'file' => 'includes/media_browser_plus.pages.inc',
);
return $items;
}
/**
* Implements hook_menu_alter().
*
* @param array $items
* List of existing menu items.
*/
function media_browser_plus_menu_alter(&$items) {
$items['admin/content/file']['access callback'] = 'media_browser_plus_access';
$items['admin/content/file']['access arguments'] = array(
'access media backend',
);
$items['admin/content/file/thumbnails']['access callback'] = array(
'media_browser_plus_access',
);
$items['admin/content/file/thumbnails']['access arguments'] = array(
'media grid view',
);
if (variable_get('media_browser_plus_thumbnails_as_default_browser', TRUE)) {
$items['admin/content/file/list']['access callback'] = 'media_browser_plus_access';
$items['admin/content/file/list']['access arguments'] = array(
'media list view',
);
$items['admin/content/file/list'] += $items['admin/content/file/thumbnails'];
$items['admin/content/file/list']['type'] = MENU_LOCAL_TASK | MENU_NORMAL_ITEM;
$items['admin/content/file/thumbnails']['type'] = MENU_DEFAULT_LOCAL_TASK;
}
}
/**
* Implements hook_permission().
*/
function media_browser_plus_permission() {
return array(
'media grid view' => array(
'title' => t('Grid View'),
'description' => t('Allow users to use the grid view.'),
),
'media list view' => array(
'title' => t('List View'),
'description' => t('Allow users to use the list view.'),
),
'access media backend' => array(
'title' => t('Access media backend'),
'description' => t('Allow user to access the media backend according to their privileges.'),
),
'upload media' => array(
'title' => t('Upload media'),
'description' => t('Allow user to add media.'),
),
'download media files' => array(
'title' => t('Download Files'),
'description' => t('Allows the user to download the original file items.'),
),
'filter media' => array(
'title' => t('Filter Media'),
'description' => t('Allows the user to filter the displayed media in the backend.'),
),
'administer media folders' => array(
'title' => t('Administer Media Folders'),
// @TODO better description
'description' => t('Allows the user to add, edite, delete and resort media folders.'),
),
'preview media' => array(
'title' => t('Preview media'),
// @TODO better description
'description' => t('Allows the user to view a configurable preview of the original media item.'),
),
'edit media files' => array(
'title' => t('Edit media files'),
// @TODO better description
'description' => t('Allows the user to edit the original media item.'),
),
);
}
/**
* Implements hook_library().
*/
function media_browser_plus_library() {
$colorbox_path = variable_get('media_browser_plus_library_path', FALSE);
if ($colorbox_path === FALSE) {
$colorbox_path = module_exists('libraries') ? libraries_get_path('colorbox') : 'sites/all/libraries/colorbox';
if (is_dir($colorbox_path . '/colorbox')) {
$colorbox_path .= '/colorbox';
}
}
else {
$colorbox_path .= '/colorbox';
}
$stylesheet = variable_get('media_browser_plus_stylesheet', 'example1');
$libraries['colorbox'] = array(
'title' => 'Colorbox',
'website' => 'http://colorpowered.com/colorbox/',
'version' => '1.3.9',
'js' => array(
$colorbox_path . '/jquery.colorbox-min.js' => array(),
),
'css' => array(
$colorbox_path . '/' . $stylesheet . '/colorbox.css' => array(
'type' => 'file',
'media' => 'screen',
),
),
);
$path = drupal_get_path('module', 'media_browser_plus');
$libraries['media_browser_plus'] = array(
'title' => 'Media Browser Plus',
'version' => '1',
'js' => array(
$path . '/js/media_browser_plus.admin.menu.js' => array(),
),
'css' => array(
$path . '/css/media_browser_plus.admin.css' => array(
'type' => 'file',
'media' => 'screen',
),
$path . '/css/colorbox.css' => array(
'type' => 'file',
'media' => 'screen',
),
),
);
return $libraries;
}
/**
* Checks if the user has the permission to download a file.
*
* @todo: this function may be obsolete in 2.x
*
* @param $field
* @param string $entity_type
* The entity type.
* @param string $entity
* Then entity.
*/
function media_browser_plus_file_download_access($field, $entity_type, $entity) {
// Only check against media entities.
if ($entity_type == 'file') {
// Check for media admin AND return true if found.
return file_acces('view', $entity);
// return media_browser_plus_media_access($entity);
}
}
/**
* Checks access to a given media entity.
*
* @todo: this function may be obsolete in 2.x
*
* @param object $media_entity
*/
function media_browser_plus_media_access($media_entity) {
if (media_browser_plus_access('administer files')) {
return TRUE;
}
// Start with ACCESS_ALLOW - by default media items are fully accessible.
$access = MEDIA_ENTITY_ACCESS_ALLOW;
// Collect all modules implementing hook_media_entity_access.
foreach (module_implements('media_entity_access') as $module) {
// And invoke the hook.
$return = module_invoke($module, 'media_entity_access', $media_entity);
// If no ALLOW or DENY was returned we assume IGNORE and check the next.
if ($return != MEDIA_ENTITY_ACCESS_ALLOW && $return != MEDIA_ENTITY_ACCESS_DENY) {
continue;
}
// If we have a DENY we can return a complete false here.
if ($return === MEDIA_ENTITY_ACCESS_DENY) {
return FALSE;
}
// Otherwise it is an ALLOW and we save it.
$access = MEDIA_ENTITY_ACCESS_ALLOW;
}
// Check if we had one allow.
return $access === MEDIA_ENTITY_ACCESS_ALLOW;
}
/**
* Revokes the general 'view media' == 'download media' access rule.
* @todo: this function may be obsolete in 2.x
*/
function media_browser_plus_file_download_access_alter(&$grants, $field, $entity_type, $entity = NULL) {
if ($entity_type == 'file') {
unset($grants['file']);
}
}
/**
* Implements hook_media_operations().
*/
function media_browser_plus_media_operations() {
return array(
'edit_multiple' => array(
'label' => t('Edit'),
'callback' => NULL,
'redirect' => 'admin/content/file/edit_multiple/%fids',
),
);
}
/**
* Manages access for media browser plus actions.
*
* @param string $op
* The permission, such as "administer nodes", being checked for.
*
* @return boolean
* TRUE if the user has the permission.
*/
function media_browser_plus_access($op) {
return user_access('administer files') || user_access($op);
}
/**
* Implements of hook_load().
*
* Used to load media folders.
*/
function media_browser_plus_folder_load($id) {
return taxonomy_term_load($id);
}
/**
* Creates the folder media tree.
*/
function _media_browser_plus_folder_hierarchy_list($folders) {
// Only do all the work if there's something to process.
if (count($folders) == 0) {
return t('No folders created yet');
}
// Create hierarchical structure.
$path = base_path() . drupal_get_path('module', 'media_browser_plus');
$pixel_img = '<img src="' . $path . '/images/pixel.gif" border="0" alt="">';
// Check if there's a folder requested.
$folder_id = isset($_REQUEST['folder']) ? (int) $_REQUEST['folder'] : NULL;
// List items.
$list_items = array();
// Keeps an array with all the folders referenced into the list.
$all_items = array();
foreach ($folders as $folder) {
$has_media = _media_browser_plus_folder_empty($folder->tid) || $folder->tid == 0;
$all_items[$folder->tid] = array(
'data' => '<div class="icon">' . $pixel_img . '</div><div class="drop">' . $folder->name . '</div>',
'id' => 'folder_load_' . $folder->tid,
'class' => array(
'folder',
),
);
if (!$has_media) {
$all_items[$folder->tid]['class'][] = 'empty';
}
if ($folder_id == $folder->tid) {
$all_items[$folder->tid]['class'][] = 'selected';
}
// Check if this element has to be registered as a child in a parent item.
if (!empty($folder->parents) && ($parent_tid = reset($folder->parents)) > 0 && isset($all_items[$parent_tid])) {
// Set the children data for the parent. Since the parent is referenced
// from the list into all_items the changes will be transfered.
$all_items[$parent_tid]['children'][$folder->tid] =& $all_items[$folder->tid];
$all_items[$parent_tid]['class']['parent'] = 'parent';
}
else {
// Add referenced data into item list. Later extensions of potential
// child elements will affect the whole structure.
$list_items[$folder->tid] =& $all_items[$folder->tid];
}
}
// Add folder for unsorted media if necessary.
if (_media_browser_plus_has_unsorted_media()) {
array_unshift($list_items, array(
'data' => '<div class="icon">' . $pixel_img . '</div>' . t('Unsorted'),
'id' => 'folder_load_0',
'class' => array(
'folder',
),
));
}
// If none of the folders was selected, just select the first one.
if (empty($folder_id)) {
reset($list_items);
$list_items[key($list_items)]['class'][] = 'selected';
}
return theme('item_list', array(
'items' => $list_items,
'attributes' => array(
'class' => array(
'media-folder-list',
),
),
));
}
/**
* Checks for unsorted (i.e. media not in folders) media.
*/
function _media_browser_plus_has_unsorted_media() {
// EFQ doesn't work because the field table is used as base table and an inner
// join is used?!
// $query = new EntityFieldQuery('file');
// $result = $query
// ->entityCondition('entity_type', 'file')
// ->propertyCondition('status', 1)
// ->fieldCondition('field_folder', 'tid', '', 'IS NULL')
// ->range(0, 1)
// ->execute();
//
// In favor of not unnecessarily loading file entities we use a raw query.
// *eeeewwh*!
$query = db_select('file_managed')
->fields('file_managed', array(
'fid',
))
->condition('uri', db_like('temporary://') . '%', 'NOT LIKE')
->isNull('entity_id')
->range(0, 1);
$query
->leftJoin('field_data_field_folder', 'folder', "entity_type = 'file' AND entity_id = file_managed.fid");
$result = $query
->execute()
->fetchCol();
return !empty($result);
}
/**
* @todo Document what this function does.
*
* @param string $prefix_padding
*/
function _media_browser_plus_folder_list($prefix_padding = '-') {
$vocabulary = taxonomy_vocabulary_machine_name_load('media_folders');
$folders = taxonomy_get_tree($vocabulary->vid);
$folders = _media_browser_plus_filter_folder_access($folders);
// Fill list with padded folders.
$list = array();
foreach ($folders as $folder) {
$pad = $folder->depth * strlen($prefix_padding) + strlen($folder->name);
$list[$folder->tid] = str_pad($folder->name, $pad, $prefix_padding, STR_PAD_LEFT);
}
return $list;
}
/**
* Loads media entities and allows filtering, sorting and paging.
*
* @param array $variables
* Holds an array with the following optional parameters
* $ids
* one dimensional array of entity_ids
* $conditions
* multidemsional array build like this:
* array(
* array('entity' => array('comlumn', 'value', 'condition')),
* array('property' => array('comlumn', 'value', 'condition')),
* array('field' => array('field_name', 'comlumn', 'value', 'condition')),
* )
* $order
* multidemsional array build like this:
* array(array('entity' => array('comlumn', 'direction')),
* array('property' => array('comlumn', 'direction')),
* array('field' => array('comlumn', 'direction')))
* $header
* table header used for sorting
* $per_page
* items per page
*/
function media_browser_plus_load_multiple($variables) {
// Set up default parameter.
$params = array(
'ids' => array(),
'conditions' => array(),
'order' => array(),
'header' => array(),
'page' => -1,
'per_page' => variable_get('media_media_per_page', 30),
'paging' => TRUE,
'ids_only' => FALSE,
'count_only' => FALSE,
'apply_filter' => TRUE,
);
// Override defaults.
// @todo Is this loop really necessary. Something like
// $params = $variables + $params; should work too, right?
foreach ($variables as $key => $value) {
if (isset($params[$key])) {
$params[$key] =& $variables[$key];
}
}
if ($params['apply_filter'] && isset($_SESSION['media-filter'])) {
if (strlen($_SESSION['media-filter']['filename'])) {
$params['conditions'][] = array(
'property' => array(
'filename',
'%' . $_SESSION['media-filter']['filename'] . '%',
'LIKE',
),
);
}
if (count($_SESSION['media-filter']['type'])) {
$params['conditions'][] = array(
'property' => array(
'type',
explode(',', $_SESSION['media-filter']['type']),
'IN',
),
);
}
if (count($_SESSION['media-filter']['field_folder'])) {
$params['conditions'][] = array(
'field' => array(
'field_folder',
'tid',
$_SESSION['media-filter']['field_folder'],
'IN',
),
);
}
if (count($_SESSION['media-filter']['field_tags'])) {
foreach ($_SESSION['media-filter']['field_tags'] as $tag_id) {
$params['conditions'][] = array(
'field' => array(
'field_tags',
'tid',
$tag_id,
'=',
),
);
}
}
}
// Another backward compatibility check.
$hidden_stream_wrapper_function = 'media_get_hidden_stream_wrappers';
if (function_exists('file_entity_get_hidden_stream_wrappers')) {
$hidden_stream_wrapper_function = 'file_entity_get_hidden_stream_wrappers';
}
// Do not list temporary files.
foreach (array_keys(call_user_func($hidden_stream_wrapper_function)) as $name) {
$params['conditions'][] = array(
'property' => array(
'uri',
$name . '%',
'NOT LIKE',
),
);
}
// Allow other modules to add/alter conditions.
foreach (module_implements('media_access_conditions') as $module) {
$params['conditions'] = array_merge(module_invoke($module, 'media_access_conditions'), $params['conditions']);
}
$query = new EntityFieldQuery();
// Set entity type to media.
$query
->entityCondition('entity_type', 'file');
// EntityFieldQuery shouldn't check access against node_access.
$query
->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');
// Parse ids if any have been passed.
if (count($params['ids'])) {
$query
->entityCondition('entity_id', $params['ids'], 'IN');
}
// Check for table header.
if (count($params['header'])) {
$query
->tableSort($params['header']);
}
// Parse conditions.
foreach ($params['conditions'] as $condition) {
// Look what type we have.
$condition_keys = array_keys($condition);
switch (array_pop($condition_keys)) {
case 'entity':
$query
->entityCondition($condition['entity'][0], $condition['entity'][1], $condition['entity'][2]);
break;
case 'property':
$query
->propertyCondition($condition['property'][0], $condition['property'][1], $condition['property'][2]);
break;
case 'field':
$query
->fieldCondition($condition['field'][0], $condition['field'][1], $condition['field'][2], $condition['field'][3]);
break;
}
}
// Parse order array.
foreach ($params['order'] as $condition) {
// Look what type we have.
$condition_keys = array_keys($condition);
switch (array_pop($condition_keys)) {
case 'entity':
$query
->entityOrderBy($condition['entity'][0], $condition['entity'][1]);
break;
case 'property':
$query
->propertyOrderBy($condition['property'][0], $condition['property'][1]);
break;
case 'field':
$query
->fieldOrderBy($condition['field'][0], $condition['field'][1], $condition['field'][2]);
break;
}
}
// Prepare result object.
$res_object = new stdClass();
if ($params['paging'] && !$params['count_only']) {
// Doing a separate count query here, because including the internal pager
// doesn't work but seems the only way to get a total result count.
$count = clone $query;
$count
->pager();
$count
->execute();
$res_object->overall_count = $count->pager['total'];
// Using range here, because as said above the internal pager seems to be
// bugged.
$page = $params['page'];
if ($page == -1) {
$page = isset($_REQUEST['page']) ? $_REQUEST['page'] : 0;
}
$query
->range($page * $params['per_page'], $params['per_page']);
// Adding the addition result fields.
$res_object->page = $page;
$res_object->pages = ceil($res_object->overall_count / variable_get('media_media_per_page', 30));
$res_object->per_page = $params['per_page'];
}
// Count only query.
if ($params['count_only']) {
$query
->pager();
$query
->execute();
return $query->pager['total'];
}
$res_object->results = array();
// Execute query and load results.
$result = $query
->execute();
if (!empty($result['file'])) {
$res_object->results = $params['ids_only'] ? array_keys($result['file']) : entity_load('file', array_keys($result['file']));
}
return $res_object;
}
/**
* Looks if a filter is active and filters the folders accordingly.
*
* @param array $folders
* The folders to filter.
*
* @return array
* The list with the filtered folders.
*/
function _media_browser_plus_filter_folders($folders) {
// No filter active, return unchanged $folders array.
$filtered_folders = $folders;
if (isset($_SESSION['media-filter'])) {
$folder_filtered = array();
// Look through the tree and add elements that match filter.
foreach ($folders as $item) {
if (in_array($item->tid, $_SESSION['media-filter']['field_folder'])) {
$folder_filtered[] = $item;
}
}
$filtered_folders = $folder_filtered;
}
$filtered_folders = _media_browser_plus_filter_folder_access($filtered_folders);
return $filtered_folders;
}
/**
* @todo Document what this function does.
*
* @param array $folders
* The folders to scan.
*
* @return array
* List with the accessible folders.
*/
function _media_browser_plus_filter_folder_access($folders) {
$temp_folders = $folders;
$filtered_folders = array();
foreach ($temp_folders as $item) {
$access = TRUE;
foreach (module_implements('media_folder_access') as $module) {
$access = module_invoke($module, 'media_folder_access', $item);
if ($access === FALSE) {
break;
}
else {
$access = TRUE;
}
}
if ($access) {
$filtered_folders[] = $item;
}
}
return $filtered_folders;
}
/**
* @todo Document what this function does.
*
* @param integer $page
* Current page.
* @param integer $pages
* Total number of pages.
*/
function _media_browser_plus_pager($page, $pages) {
// Get url for paging link.
$destination = drupal_get_destination();
$link = base_path() . '?q=admin/content/file/list';
$sort = isset($_GET['sort']) ? '&sort=' . check_plain($_GET['sort']) : '';
$order = isset($_GET['order']) ? '&order=' . check_plain($_GET['order']) : '';
$url = $link . $sort . $order;
// Create paging div inside buffer.
$output = '<div id="media_paging_table" align="center">';
// Calculate paging.
$start = max(0, $page - ceil(variable_get('media_page_items_per_page') / 2));
$end = min($pages, $start + variable_get('media_page_items_per_page'));
if ($start > 0) {
$output .= _media_browser_plus_pager_add_page_item($url, $start - 1, $page, '...');
}
// Create numbers.
for ($i = $start; $i < $end; $i++) {
$output .= _media_browser_plus_pager_add_page_item($url, $i, $page, $i + 1);
}
if ($pages > $i) {
$output .= _media_browser_plus_pager_add_page_item($url, $i, $page, '...');
}
return $output . '</div>';
}
/**
* @todo Document what this function does.
*
* @param string $url
* The base url.
* @param integer $page
* The page number.
* @param integer $current_page
* The current page number.
* @param string $title
* The title of the link.
*/
function _media_browser_plus_pager_add_page_item($url, $page, $current_page, $title) {
$page_item = '<a class="media_paging_page';
if ($page == $current_page) {
$page_item .= ' active_page';
}
$page_item .= '" href="' . $url . '&page=' . (int) $page . '">' . $title . '</a>';
return $page_item;
}
/**
* Creates the media browser plus settings form.
*/
function media_browser_plus_media_settings($form, &$form_state = array()) {
$form = array(
'media_per_page' => array(
'#type' => 'textfield',
'#title' => t('Media Items per page'),
'#description' => t('Insert a number higher than one for the amount of media items displayed per page.'),
'#default_value' => variable_get('media_media_per_page', 30),
'#maxlength' => 4,
'#required' => TRUE,
),
'grid_window_height' => array(
'#type' => 'textfield',
'#title' => t('Grind Window Height'),
'#default_value' => variable_get('media_grid_window_height', 400),
'#description' => t('Set a maximum height of pixels for the media grid view.'),
'#maxlength' => 4,
'#required' => TRUE,
),
'page_items_per_page' => array(
'#type' => 'textfield',
'#title' => t('Page Items per page'),
'#default_value' => variable_get('media_page_items_per_page'),
'#description' => t('Set how many page items you want in the paging navigation of each page.'),
'#maxlength' => 4,
'#required' => TRUE,
),
'max_filesize' => array(
'#type' => 'textfield',
'#title' => t('Upload Maximum Filesize'),
'#default_value' => media_variable_get('max_filesize'),
'#description' => t('Standard unit is bytes and therefore can be left out. Otherwise use "NUMBER UNIT"'),
'#maxlength' => 20,
'#required' => TRUE,
),
'root_folder' => array(
'#type' => 'textfield',
'#title' => t('Media Root folder'),
'#default_value' => variable_get('media_root_folder'),
'#description' => t("The root folder for files handled by the media module. <strong>Attention: Changing this will move all existing files in the file system too!</strong>"),
),
'media_browser_plus_thumbnails_as_default_browser' => array(
'#type' => 'checkbox',
'#title' => t('Use thumbnails view as default.'),
'#description' => t('If enabled the thumbnails view will displayed when accessing "admin/content/file".'),
'#default_value' => variable_get('media_browser_plus_thumbnails_as_default_browser', TRUE),
),
);
$form['actions'] = array(
'#type' => 'actions',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save Changes'),
);
return $form;
}
/**
* Saves the entered settings.
*/
function media_browser_plus_media_settings_submit($form, &$form_state) {
// Change settings.
variable_set('media_media_per_page', (int) $form_state['values']['media_per_page']);
variable_set('media_grid_window_height', (int) $form_state['values']['grid_window_height']);
variable_set('media_page_items_per_page', (int) $form_state['values']['page_items_per_page']);
media_variable_set('max_filesize', trim($form_state['values']['max_filesize']));
// Check if the root folder was changed.
$old_root = variable_get('media_root_folder');
$new_root = trim($form_state['values']['root_folder'], '/');
// Move the files to the new root folder if necessary.
if ($old_root != $new_root) {
// Load folder handling functions.
module_load_include('inc', 'media_browser_plus', 'includes/media_browser_plus.folders');
media_browser_plus_move_root_folder($old_root, $new_root);
}
// Notify user.
drupal_set_message(t('Media Browser Plus Settings changed successfully'));
}
/**
* Validates the settings.
*/
function media_browser_plus_media_settings_validate($form, &$form_state) {
// Validate settings.
$value = (int) $form_state['values']['media_per_page'];
if ($value <= 0) {
form_set_error('Media Settings', t('Illegal value for "Media Items per page"'));
}
$value = (int) $form_state['values']['grid_window_height'];
if ($value <= 100) {
form_set_error('Media Settings', t('Illegal value for "Grind Window Height"'));
}
$value = (int) $form_state['values']['page_items_per_page'];
if ($value <= 2) {
form_set_error('Media Settings', t('Illegal value for "Page Items per page"'));
}
$value = parse_size(trim($form_state['values']['max_filesize']));
if ($value === 0) {
form_set_error('Media Settings', t('Illegal value for "Upload Maximum Filesize"'));
}
}
/**
* Implements hook_field_attach_presave().
*
* Makes sure all media are in a folder.
*/
function media_browser_plus_field_attach_presave($entity_type, $entity) {
if ($entity_type != 'file') {
return;
}
// Look for folder and set default if none found
// $media->field_folder[LANGUAGE_NONE] = array(array('tid' => $form_state['values']['field_folder']));
if (!isset($entity->field_folder) || !isset($entity->field_folder[LANGUAGE_NONE]) || !count($entity->field_folder[LANGUAGE_NONE]) || !isset($entity->field_folder[LANGUAGE_NONE][0]['tid']) || (int) $entity->field_folder[LANGUAGE_NONE][0]['tid'] == 0) {
// sSt default folder is no folder or incorrect value.
$root = media_browser_plus_get_media_root_folder();
$entity->field_folder = array();
$entity->field_folder[LANGUAGE_NONE] = array(
array(
'tid' => $root->tid,
),
);
}
}
/**
* Check if a given folder has media in it (does include current filter).
*
* @param integer $folder_id
* Id of the folder to check.
*/
function _media_browser_plus_folder_empty($folder_id) {
// Save paging values.
$gp = isset($_GET['page']) ? $_GET['page'] : NULL;
$pp = isset($_POST['page']) ? $_POST['page'] : NULL;
// Set media condition.
$condition = array(
array(
'field' => array(
'field_folder',
'tid',
array(
$folder_id,
),
'IN',
),
),
);
$variables = array(
'conditions' => $condition,
'per_page' => 1,
'ids_only' => TRUE,
);
$media = media_browser_plus_load_multiple($variables);
// Restore paging values if necessary.
if ($gp) {
$_GET['page'] = $gp;
}
if ($pp) {
$_POST['page'] = $pp;
}
// Check if media has been found in the folder.
return $media->overall_count > 0;
}
/**
* @todo Document what this function does.
*
* @param string $tags
* Comma separated string of the tags.
* @param bool $tids_only
* Return the term ids instead term entities.
* @param bool $auto_create
* Create missing tags on the fly.
*
* @return array
* List of term entities or term ids.
*/
function media_browser_plus_load_tag_terms($tags, $tids_only = TRUE, $auto_create = FALSE) {
$vocabulary = taxonomy_vocabulary_machine_name_load('tags');
$select = $tids_only ? 'tid, name' : '*';
$all_tags = array();
$found_tags = array();
$found_terms = array();
$terms = explode(',', $tags);
foreach ($terms as $tag) {
// Check if tag exists.
// @todo Would an EntityFieldQuery be appropriate?
// http://drupal.org/node/916776
$results = db_query('SELECT ' . $select . ' FROM {taxonomy_term_data} ttd WHERE ttd.name = :name AND ttd.vid = :vocabulary', array(
'name' => check_plain(trim($tag)),
'vocabulary' => $vocabulary->vid,
));
foreach ($results as $result) {
$term = taxonomy_term_load($result->tid);
if ($tids_only) {
$found_terms[] = array(
'tid' => $term->tid,
);
}
else {
$found_terms[] = get_object_vars($term);
}
$found_tags[] = trim($tag);
}
$all_tags[] = trim($tag);
}
if ($auto_create) {
foreach (array_diff($all_tags, $found_tags) as $id => $tag) {
$term = new stdClass();
$term->name = $tag;
$term->vid = $vocabulary->vid;
if (strlen(trim($term->name))) {
taxonomy_term_save($term);
if ($tids_only) {
$found_terms[] = array(
'tid' => $term->tid,
);
}
else {
$found_terms[] = get_object_vars($term);
}
}
}
}
return $found_terms;
}
/**
* Loads and (if $autocreate is set) creates the default media folder object.
*
* @return object|FALSE
* The folder term or FALSE if not found.
*/
function media_browser_plus_get_media_root_folder($autocreate = FALSE) {
$root_folder = FALSE;
$vocabulary = taxonomy_vocabulary_machine_name_load('media_folders');
if ($vocabulary) {
// Fetch the media root term.
$results = taxonomy_get_term_by_name('Media Root', 'media_folders');
if (!empty($results)) {
$root_folder = reset($results);
}
elseif ($autocreate) {
$root_folder = new stdClass();
$root_folder->name = 'Media Root';
$root_folder->description = 'default media folder';
$root_folder->vid = $vocabulary->vid;
$root_folder->weight = '-10';
// Save (default folder) term.
taxonomy_term_save($root_folder);
}
}
return $root_folder;
}
/**
* Implements hook_media_browser_plugin_info().
*/
function media_browser_plus_media_browser_plugin_info() {
$plugins = array();
$plugins['library_plus'] = array(
'title' => t('Library Plus'),
'weight' => 10,
'class' => 'MediaBrowserPlusLibrary',
'handler' => array(
'path' => drupal_get_path('module', 'media_browser_plus') . '/includes',
'file' => 'MediaBrowserPlusLibrary.inc',
'class' => 'MediaBrowserPlusLibrary',
),
'access callback' => 'media_browser_plus_access',
'access arguments' => array(
'view files',
),
);
return $plugins;
}
/**
* Creates the Extended Library for the media browser popup.
*
* @param array $form
* The form structure.
* @param array $form_state
* The form state.
* @param bool $multiselect
* Allow multiple selection.
* @param array $types
* The allowed file types.
*
* @return array
* The form extended with the necessary library stuff.
*/
function media_browser_plus_library_browser($form, &$form_state, $multiselect, $types) {
$path = drupal_get_path('module', 'media_browser_plus');
// Assemble library from grid view.
$form['#attached']['library'][] = array(
'media_browser_plus',
'media_browser_plus',
);
$form['#attached']['library'][] = array(
'media_browser_plus',
'colorbox',
);
// Set base href for javascript requests.
$settings = media_browser_plus_main_view_javascript_settings();
$settings['media_browser_plus']['multiselect'] = $multiselect;
$settings['media_browser_plus']['folder_dnd_enabled'] = FALSE;
// Setting filter condition.
$filter = array(
array(
'property' => array(
'type',
$types,
'IN',
),
),
);
$settings['media_browser_plus']['filter'] = drupal_json_encode($filter);
// Attach settings.
$form['#attached']['js'][] = array(
'type' => 'setting',
'data' => $settings,
);
// Check access rights.
if (!media_browser_plus_access('media grid view')) {
drupal_access_denied();
return array();
}
// Adding grid view js and css.
$form['#attached']['js'][] = $path . '/js/media_browser_plus.admin.js';
$form['#attached']['js'][] = $path . '/js/media_browser_plus.library.js';
drupal_add_library('system', 'ui.draggable');
drupal_add_library('system', 'ui.droppable');
// Removing options form part.
unset($form['options']);
if (!media_browser_plus_access('media list view') || !media_browser_plus_access('media grid view')) {
unset($form['switch']);
}
// Add main grid view window.
$form['admin'] = media_browser_plus_grid_view_form(TRUE, $multiselect, $form_state);
// Append media basket only for multi select.
if ($multiselect) {
$form['media-basket'] = media_browser_plus_media_basket_form(TRUE, $form_state);
}
return $form;
}
/**
* Deleting all field data attached to the media entity.
*
* @todo: This should actually be done in media module
*
* @param object $file
* The file object to delete.
*/
function media_browser_plus_file_delete($file) {
$media = file_load($file->fid);
module_invoke_all('entity_delete', $media, 'file');
field_attach_delete('file', $media);
}
/**
* Appends main grid view form.
*/
function media_browser_plus_grid_view_form($library_mode = FALSE, $multiselect = FALSE, &$form_state = array()) {
// Load the media folders.
$vocabulary = taxonomy_vocabulary_machine_name_load('media_folders');
$folders = taxonomy_get_tree($vocabulary->vid);
$folders = _media_browser_plus_filter_folders($folders);
$folders = _media_browser_plus_folder_hierarchy_list($folders);
form_load_include($form_state, 'inc', 'media_browser_plus', 'includes/file_entity.admin');
// Build the folder form.
$form['admin']['folder'] = array(
'#type' => 'markup',
'#markup' => '<div id="folder" style="height:' . variable_get('media_grid_window_height', 400) . 'px;">' . $folders . ' </div>',
);
$form['admin']['files']['#prefix'] = '<div class="media-display-thumbnails media-clear clearfix">' . '<ul class="media-list-thumbnails" style="height:' . variable_get('media_grid_window_height', 400) . 'px;">';
// Setting up the header.
$header = array(
array(
'data' => t('Folders'),
'width' => '200',
),
array(
'data' => t('Media Files'),
'width' => '*',
),
);
// And the data.
$form['buttons'] = array();
$form['buttons']['selection_assets'] = array(
'#type' => 'markup',
'#markup' => '<a href="#media_folder_table" id="media_main_view_select_all" >' . t('Select All') . '</a>' . '<a href="#media_folder_table" id="media_main_view_deselect_all" >' . t('Deselect All') . '</a>',
);
if (!$library_mode) {
if (media_browser_plus_access('view files')) {
$form['buttons']['view_media'] = array(
'#type' => 'button',
'#attributes' => array(
'id' => 'media_buttons_view',
),
'#value' => t('View'),
);
}
if (media_browser_plus_access('preview media')) {
$form['buttons']['preview_media'] = array(
'#type' => 'button',
'#attributes' => array(
'id' => 'media_buttons_preview',
),
'#value' => t('Preview'),
);
}
if (media_browser_plus_access('edit media files')) {
$form['buttons']['edit_media'] = array(
'#type' => 'submit',
'#attributes' => array(
'id' => 'media_buttons_edit',
),
'#submit' => array(
'media_browser_plus_edit_multiple_redirect',
),
'#validate' => array(
'media_browser_plus_file_entity_admin_files_validate',
),
'#value' => t('Edit'),
);
}
if (media_browser_plus_access('edit media files')) {
// edit-submit
$form['buttons']['delete_media'] = array(
'#type' => 'submit',
'#submit' => array(
'media_browser_plus_delete_multiple_redirect',
),
'#validate' => array(
'media_browser_plus_file_entity_admin_files_validate',
),
'#value' => t('Delete'),
);
}
}
$form['buttons']['select_media'] = array(
'#type' => 'button',
'#attributes' => array(
'id' => 'media_buttons_select',
),
'#value' => t('Add to Media Basket'),
);
if ($library_mode && !$multiselect) {
// Remove select/deselect all.
unset($form['buttons']['selection_assets']);
// Change "Add to media basket" to process with selection.
$form['buttons']['select_media'] = _media_browser_plus_media_basket_select_button();
}
$options = array(
array(
'categories' => array(
'data' => drupal_render($form['admin']['folder']),
),
'media' => array(
'data' => '<div class="media-display-thumbnails media-clear clearfix">' . '<ul class="media-list-thumbnails" id="media-thumb-list" style="height: ' . variable_get('media_grid_window_height', 400) . 'px;">' . '</ul></div>',
),
),
array(
'categories' => array(
'data' => '',
),
'media' => array(
'data' => '',
'id' => array(
'media_browser_plus_pages',
),
),
),
);
// If no library mode with single selection add media basket buttons.
if (!$library_mode || $multiselect) {
$options[] = array(
'categories' => array(
'data' => '',
),
'media' => array(
'data' => drupal_render($form['buttons']),
'id' => array(
'media_browser_plus_selection_panel',
),
),
);
}
// Table setup.
$table = array(
'header' => $header,
'rows' => $options,
'attributes' => array(
'id' => 'media_folder_table',
'class' => array(
'mbp-item-list',
),
),
'empty' => t('No media added yet.'),
);
// Return themed table.
$output = array(
'#type' => 'markup',
'#markup' => theme('table', $table),
'buttons' => $form['buttons'],
);
if ($library_mode && !$multiselect) {
$output['#markup'] .= drupal_render($form['buttons']);
}
return $output;
}
/**
* Returns default javascript settings.
*/
function media_browser_plus_main_view_javascript_settings() {
global $base_url;
// Gather environment data.
$path = drupal_get_path('module', 'media_browser_plus');
$url = rtrim(url('', array(
'absolute' => TRUE,
)), '/') . '/';
// Create default settings.
$settings = array(
'media_browser_plus' => array(
'filter_active' => isset($_SESSION['media-filter']),
'url' => $url,
'images_url' => $base_url . '/' . $path . '/images/',
'page' => isset($_GET['page']) ? $_GET['page'] : 0,
'per_page' => variable_get('media_media_per_page', 30),
'page_items_per_page' => variable_get('media_page_items_per_page'),
'folder_management_url' => url('admin/content/file/folder_list'),
'multiselect' => TRUE,
// Texts.
'messages' => array(
'only_one_selection_allowed' => t('Only one media item may be selected'),
),
// Access settings.
'folder_dnd_enabled' => media_browser_plus_access('edit media files'),
'manage_folders' => media_browser_plus_access('administer media folders'),
'filter_allowed' => media_browser_plus_access('filter media'),
'add_files' => media_browser_plus_access('upload media'),
),
);
return $settings;
}
/**
* Appends the media basket.
*/
function media_browser_plus_media_basket_form($library_mode = FALSE, &$form_state = array()) {
$header = array(
array(
'data' => t('Media Basket'),
),
);
$form['basket_actions'] = array();
$form['basket_actions']['selection_assets'] = array(
'#type' => 'markup',
'#markup' => '<a href="#media_basket_table" id="media_basket_remove_all" >' . t('Remove All') . '</a>',
);
form_load_include($form_state, 'inc', 'media_browser_plus', 'includes/file_entity.admin');
if ($library_mode) {
$form['basket_actions']['select'] = _media_browser_plus_media_basket_select_button();
}
else {
if (media_browser_plus_access('download media files')) {
$form['basket_actions']['download'] = array(
'#type' => 'submit',
'#value' => t('Download'),
'#attributes' => array(
'id' => 'perform_download',
),
'#limit_validation_errors' => array(),
'#validate' => array(
'media_browser_plus_file_entity_admin_files_validate',
),
'#submit' => array(
'media_browser_plus_download_images_submit',
),
);
}
}
$options = array(
array(
'media' => array(
'data' => '<ul id="media-basket-list" class="media-list-thumbnails"></ul>',
),
),
array(
'media' => array(
'data' => drupal_render($form['basket_actions']),
'id' => array(
'media_browser_plus_basket_panel',
),
),
),
);
$table = array(
'header' => $header,
'rows' => $options,
'attributes' => array(
'id' => 'media_basket_table',
'class' => array(
'mbp-item-list',
),
),
'empty' => t('No media added yet.'),
);
return array(
'#type' => 'markup',
'#markup' => theme('table', $table),
'basket_actions' => $form['basket_actions'],
);
}
/**
* Appends the hidden preview form.
*/
function media_browser_plus_media_preview_form() {
$header = array(
array(
'data' => t('Media Preview'),
'id' => array(
'media-preview-label',
),
),
);
$form['preview_actions'] = array(
'previous' => array(
'#type' => 'submit',
'#value' => t('previous'),
'#attributes' => array(
'id' => 'previous_preview_item',
),
),
'select' => array(
'#type' => 'submit',
'#value' => t('Select'),
'#attributes' => array(
'id' => 'select_preview_item',
),
),
'next' => array(
'#type' => 'submit',
'#value' => t('next'),
'#attributes' => array(
'id' => 'next_preview_item',
),
),
);
$options = array(
array(
'media' => array(
'data' => '',
'id' => array(
'media_browser_plus_preview_content',
),
),
),
array(
'media' => array(
'data' => drupal_render($form['preview_actions']),
'id' => array(
'media_browser_plus_preview_panel',
),
),
),
);
$table = array(
'header' => $header,
'rows' => $options,
'attributes' => array(
'id' => 'media-preview-table',
),
'empty' => t('No media added yet.'),
);
return array(
'#type' => 'markup',
'#markup' => '<div id="media-preview-table-container" style="display: none;">' . theme('table', $table) . '</div>',
'preview_actions' => $form['preview_actions'],
);
}
/**
* @todo Document what this is for!
*
* @return array
* Form element.
*/
function _media_browser_plus_media_basket_select_button() {
return array(
'#type' => 'submit',
'#value' => t('Continue with Selection'),
'#attributes' => array(
'id' => 'proceed_with_select',
),
'#limit_validation_errors' => array(),
'#validate' => array(
'media_browser_plus_file_entity_admin_files_validate',
),
);
}
/**
* Construct the path of a media_folder term.
*
* @param object|NULL $term
* Containing term id and term name. If left empty the root folder will be
* returned.
*
* @return string
* The path to the requested folder. Doesn't have a trailing slash.
*/
function media_browser_plus_construct_dir_path($term = NULL) {
$path = variable_get('file_default_scheme', 'public') . '://';
if ($root_folder = variable_get('media_root_folder')) {
$path .= $root_folder . '/';
}
$root_folder_term = media_browser_plus_get_media_root_folder();
if ($term) {
$parents = array_reverse(taxonomy_get_parents_all($term->tid));
if (is_array($parents) && !empty($parents)) {
foreach ($parents as $parent) {
// @TODO Shouldn't we sanitize this?!? There can be special chars!
$folder_name = $parent->name;
if ($parent->tid != $root_folder_term->tid) {
$path .= $folder_name . '/';
}
}
}
}
return rtrim($path, '/');
}
/**
* Cut-paste a directory with its children into a new filesystem location.
*
* @param string $source
* The current folder path.
* @param string $destination
* The path we want the folder moved to.
*/
function media_browser_plus_move_physical_folder($source, $destination) {
$destination = drupal_realpath($destination);
$source = drupal_realpath($source);
$jail = drupal_realpath(variable_get('file_default_scheme', 'public') . '://');
// @todo Please avoid an error by checking the preconditions instead.
$files = @scandir($source);
if ($files && count($files) > 2) {
$transfer = new FileTransferLocal($jail);
$transfer
->copyDirectory($source, $destination);
$transfer
->removeDirectory($source);
}
else {
// The folder is empty so just delete and create the new one.
drupal_rmdir($source);
file_prepare_directory($destination, FILE_CREATE_DIRECTORY);
}
return TRUE;
}
/**
* Moves and saves permanently a media file.
*
* Every media file that is saved or updated,should pass through this to make
* sure the filesystem location is the same with the folder term.
*
* @param int $tid
* The folder's term id.
* @param object $media
* The media object.
* @param int $replace
* Replace behavior when the destination file already exists.
*/
function media_browser_plus_move_file($tid, $media, $replace = FILE_EXISTS_RENAME) {
// Don't change the uri for media files with external source.
if (strpos($media->uri, 'public') === FALSE && strpos($media->uri, 'private') === FALSE) {
file_save($media);
}
else {
// Media translation module does need this since it allows the creation of
// file references which shouldn't move the referenced file itself when
// moved. See http://drupal.org/node/1331818 for details.
if (module_exists('media_translation') && media_translation_is_virtual_file($media->fid)) {
file_save($media);
return;
}
$folder = media_browser_plus_folder_load($tid);
$path = media_browser_plus_construct_dir_path($folder);
if (dirname($media->uri) !== $path) {
file_prepare_directory($path, FILE_CREATE_DIRECTORY);
file_move($media, $path, $replace);
}
}
}
/**
* Implements hook_taxonomy_term_presave().
*
* @see media_browser_plus_taxonomy_term_update()
*/
function media_browser_plus_taxonomy_term_presave($term) {
// Figure out if this is a folder term and if so store the current file path
// for further processing in media_browser_plus_taxonomy_term_update().
$vocabulary = taxonomy_vocabulary_machine_name_load('media_folders');
if (!empty($term->tid) && !empty($vocabulary) && $term->vid == $vocabulary->vid) {
$term->media_browser_plus_original_path = media_browser_plus_construct_dir_path($term);
}
}
/**
* Implements hook_taxonomy_term_insert().
*/
function media_browser_plus_taxonomy_term_insert($term) {
if ($term->vocabulary_machine_name == 'media_folders') {
$dir = media_browser_plus_construct_dir_path($term);
if (file_prepare_directory($dir, FILE_CREATE_DIRECTORY)) {
drupal_set_message(t('Folder %term_name created successfully', array(
'%term_name' => $term->name,
)));
}
else {
drupal_set_message(t('Folder %term_name created successfully as term but failed to create as physical folder.Please do it manually', array(
'%term_name' => $term->name,
)), 'warning');
}
}
}
/**
* Implements hook_taxonomy_term_update().
*
* @see media_browser_plus_taxonomy_term_presave()
*/
function media_browser_plus_taxonomy_term_update($term) {
// Check if this is a folder term and the source path is set.
if (!empty($term->media_browser_plus_original_path)) {
module_load_include('inc', 'media_browser_plus', '/includes/media_browser_plus.folders');
// Update physical folder.
$destination = media_browser_plus_construct_dir_path($term);
media_browser_plus_move_subfolder($term, $term->media_browser_plus_original_path, $destination);
}
}
/**
* Implements hook_taxonomy_term_delete().
*/
function media_browser_plus_taxonomy_term_delete($term) {
// Figure out if this is a folder term and if so handle the related files.
$vocabulary = taxonomy_vocabulary_machine_name_load('media_folders');
if (!empty($vocabulary) && $term->vid == $vocabulary->vid) {
// Create an array of all the folders to handle.
$folders = array(
'0:' . $term->tid => $term,
);
// Fetch all sub-folders.
$tree = taxonomy_get_tree($term->vid, $term->tid);
foreach ($tree as $subterm) {
$folders[$subterm->depth + 1 . ':' . $subterm->tid] = $subterm;
}
// Ensure the order for processing is right.
krsort($folders);
foreach ($folders as $folder) {
// Fetch all files from the folder.
$conditions = array();
$conditions[] = array(
'field' => array(
'field_folder',
'tid',
$folder->tid,
'=',
),
);
$options = array(
'apply_filter' => FALSE,
'count_only' => FALSE,
'paging' => FALSE,
'conditions' => $conditions,
);
$folder_path = media_browser_plus_construct_dir_path($folder);
if ($files = media_browser_plus_load_multiple($options)) {
$all_files_deleted = TRUE;
if (!empty($files->results)) {
foreach ($files->results as $file) {
if (!file_delete($file)) {
$all_files_deleted = FALSE;
}
}
}
// Also delete the folder when it's empty.
if ($all_files_deleted) {
if (!@drupal_rmdir($folder_path)) {
drupal_set_message(t('Unable to delete the folder (!path) on the disk', array(
'!path' => $folder_path,
)), 'error');
}
}
}
}
}
}