Newer
Older
Jacob Singh
committed
<?php
/**
* @file
* Lets users create galleries made up of media items.
*/
require_once(dirname(__FILE__) . '/media_gallery.fields.inc');
require_once(dirname(__FILE__) . '/fields_rsi_prevention.inc');
/**
* The pager element to use for paging through the media items in a gallery.
*
* We avoid using the default pager for now because there is too much risk of
* it colliding with other pagers initialized for the same page (for example,
* by the comment module, if the gallery node manages to get comment loading or
* display functions called on it).
*/
define('MEDIA_GALLERY_PAGER_ELEMENT', 1);
Alex Bronstein
committed
/**
* Helper function to return the view modes used by this module for displaying files in gallery context.
*/
function media_gallery_file_view_modes() {
return array(
'media_gallery_thumbnail' => t('Gallery thumbnail'),
'media_gallery_lightbox' => t('Gallery lightbox'),
'media_gallery_detail' => t('Gallery detail'),
'media_gallery_block_thumbnail' => t('Gallery block thumbnail'),
'media_gallery_collection_thumbnail' => t('Gallery collection thumbnail'),
);
}
Jacob Singh
committed
/**
* Implements hook_menu().
*/
function media_gallery_menu() {
$items['admin/config/media/galleries'] = array(
'title' => 'Gallery settings',
'description' => 'Configure settings for the "All galleries" page.',
'access arguments' => array('administer media galleries'),
'page callback' => 'media_gallery_admin_settings',
'file' => 'media_gallery.admin.inc',
);
$items['media-gallery/sort/collection/%taxonomy_term/%'] = array(
'title' => 'Gallery sort callback',
'access callback' => 'media_gallery_edit_access_ajax',
'access arguments' => array('collection', 3, 4),
'page callback' => 'media_gallery_ajax_sort',
'page arguments' => array('collection', 3),
'file' => 'media_gallery.pages.inc',
);
$items['media-gallery/sort/gallery/%node/%'] = array(
'title' => 'Gallery sort callback',
'access callback' => 'media_gallery_edit_access_ajax',
'access arguments' => array('gallery', 3, 4),
'page callback' => 'media_gallery_ajax_sort',
'page arguments' => array('gallery', 3),
'file' => 'media_gallery.pages.inc',
);
Alex Bronstein
committed
$items['media-gallery/detail/%node/%file'] = array(
Jacob Singh
committed
'page callback' => 'media_gallery_detail_page',
'page arguments' => array(2, 3),
'access callback' => 'node_access',
'access arguments' => array('view', 2),
'file' => 'media_gallery.pages.inc',
);
Alex Bronstein
committed
$items['media-gallery/detail/%node/%file/view'] = array(
Jacob Singh
committed
'title' => 'View',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
Alex Bronstein
committed
// An in-gallery-context version of media/%file/edit.
$items['media-gallery/detail/%node/%file/edit'] = array(
Jacob Singh
committed
'title' => 'Edit',
'page callback' => 'media_gallery_media_page_edit',
'page arguments' => array(2, 3),
'access callback' => 'media_access',
'access arguments' => array('edit'),
'weight' => 0,
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
'file' => 'media_gallery.pages.inc',
);
Alex Bronstein
committed
$items['media-gallery/detail/%node/%file/remove'] = array(
Jacob Singh
committed
'title' => 'Remove',
'page callback' => 'drupal_get_form',
'page arguments' => array('media_gallery_remove_item_form', 2, 3),
'access callback' => 'media_gallery_remove_item_access',
'access arguments' => array(2, 3),
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
'file' => 'media_gallery.pages.inc',
);
Alex Bronstein
committed
$items['media-gallery/lightbox/%node/%file'] = array(
Jacob Singh
committed
'page callback' => 'media_gallery_lightbox_page',
'page arguments' => array(2, 3),
'access callback' => 'node_access',
'access arguments' => array('view', 2),
'file' => 'media_gallery.pages.inc',
'delivery callback' => 'media_gallery_lightbox_page_deliver',
);
$items['media-gallery/add-images/%node/%'] = array(
'access callback' => 'media_gallery_edit_access_ajax',
'access arguments' => array('gallery', 2, 3),
'page callback' => 'media_gallery_add_images',
'page arguments' => array(2),
'file' => 'media_gallery.pages.inc',
);
// An in-gallery-context version of media/%media_multi/edit.
$items['node/%node/multiedit'] = array(
Jacob Singh
committed
'page callback' => 'media_gallery_media_page_multiedit',
'page arguments' => array(1),
'access callback' => 'media_gallery_multiedit_access',
'access arguments' => array(1),
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
'type' => MENU_LOCAL_TASK,
'file' => 'media_gallery.pages.inc',
);
// @todo Move to Media module once it is ready.
Alex Bronstein
committed
$items['media/%file/download'] = array(
Jacob Singh
committed
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
'title' => 'Download',
'page callback' => 'media_download',
'page arguments' => array(1),
'access callback' => 'media_access',
'access arguments' => array('view'),
'type' => MENU_CALLBACK,
'file' => 'media.pages.inc',
);
return $items;
}
/**
* Implements hook_menu_alter().
*/
function media_gallery_menu_alter(&$items) {
// Take over taxonomy term list pages by substituting our own callback.
// TODO: Use hook_entity_info_alter() to change the entity uri callback for
// gallery collections only.
$items['taxonomy/term/%taxonomy_term']['page callback'] = 'media_gallery_list_galleries';
$items['taxonomy/term/%taxonomy_term']['file'] = 'media_gallery.pages.inc';
$items['taxonomy/term/%taxonomy_term']['module'] = 'media_gallery';
// If you're viewing a media item in context somewhere (which we do inside
// media gallery nodes), that means it's being used on your site, which means
// you won't be allowed to delete it anyway. Therefore, do not show
// contextual links there.
// @todo Perhaps this should be changed in the media module itself?
Alex Bronstein
committed
$items['media/%file/delete']['context'] = MENU_CONTEXT_PAGE;
Jacob Singh
committed
}
/**
* Implements hook_admin_paths().
*/
function media_gallery_admin_paths() {
$paths = array(
'media-gallery/detail/*/*/edit' => TRUE,
'media-gallery/detail/*/*/remove' => TRUE,
'node/*/multiedit' => TRUE,
);
return $paths;
}
/**
* Implements hook_menu_local_tasks_alter().
*/
function media_gallery_menu_local_tasks_alter(&$data, $router_item, $root_path) {
// Rename the "Edit" tab on gallery nodes to "Edit gallery".
if (($node = menu_get_object()) && isset($node->type) && $node->type == 'media_gallery' && !empty($data['tabs'])) {
Jacob Singh
committed
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
$tabs = &$data['tabs'][0]['output'];
foreach ($tabs as &$tab) {
if (isset($tab['#link']['path']) && $tab['#link']['path'] == 'node/%/edit') {
$tab['#link']['title'] = t('Edit gallery');
}
}
}
// Rename the "Edit" tab on the "All Galleries" taxonomy term to "Edit all
// galleries" and point it to our configuration page.
// @todo: Once we have additional gallery-related taxonomy terms and
// http://drupal.org/node/678592 is committed to core (so the term edit
// pages show the correct admin theme) we'll do something different here,
// perhaps not even alter anything at all.
if (($term = menu_get_object('taxonomy_term', 2)) && isset($term->vid) && $term->vid == variable_get('media_gallery_collection_vid')) {
$tabs = &$data['tabs'][0]['output'];
foreach ($tabs as &$tab) {
if (isset($tab['#link']['path']) && $tab['#link']['path'] == 'taxonomy/term/%/edit') {
$tab['#link']['href'] = 'admin/config/media/galleries';
$tab['#link']['title'] = t('Edit all galleries');
}
}
}
}
/**
* Implements hook_node_load().
*/
function media_gallery_node_load($nodes, $types) {
// Store a copy of the media_gallery_media field before mucking with it in
// media_gallery_view(). We use hook_node_load() instead of hook_load(),
// because the latter runs before fields are loaded.
if (in_array('media_gallery', $types)) {
foreach ($nodes as $node) {
if ($node->type == 'media_gallery') {
$node->media_gallery_media_original = $node->media_gallery_media;
}
}
}
}
/**
* Implements hook_view().
*/
function media_gallery_view($node, $view_mode) {
// Add display elements and resources for users who can edit the gallery.
if (node_access('update', $node)) {
// Add the "Add images" link, themed as a local action. Note that this
// element is listed in hook_field_extra_fields(), so whether or not it
// will *actually* be displayed in the current view mode depends on the
// site's configuration for the corresponding pseudo-field.
Jacob Singh
committed
'#theme' => 'menu_local_action',
'#link' => array(
Jacob Singh
committed
'href' => 'media/browser',
'localized_options' => array(
'query' => array('render' => 'media-popup'),
'attributes' => array(
'class' => 'media-gallery-add launcher',
),
),
),
// @todo Drupal could really use a theme_menu_local_actions() function...
'#prefix' => '<ul class="field action-links">',
'#suffix' => '</ul>',
);
// Enable the "Add media" link to launch the media browser.
module_load_include('inc', 'media', 'includes/media.browser');
media_attach_browser_js($node->content['add_media_link']);
$node->content['add_media_link']['#attached']['js'][] = drupal_get_path('module', 'media_gallery') . '/media_gallery.addimage.js';
// These JS settings are used by the "add media" link but some are also
// shared by the drag-and-drop code below.
$instance = field_info_instance('node', 'media_gallery_media', $node->type);
David Rothstein
committed
$token = drupal_get_token('media_gallery');
Jacob Singh
committed
$gallery_js_settings = array(
David Rothstein
committed
'mediaGalleryAddImagesUrl' => url('media-gallery/add-images/' . $node->nid . '/' . $token),
'mediaGallerySortGalleryUrl' => url('media-gallery/sort/gallery/' . $node->nid . '/' . $token),
'mediaGalleryAllowedMediaTypes' => array_filter($instance['widget']['settings']['allowed_types']),
Jacob Singh
committed
);
// When viewing the full node, add front-end resources for drag-and-drop
// sorting.
if ($view_mode == 'full') {
drupal_add_css(drupal_get_path('module', 'media_gallery') . '/media_gallery.dragdrop.css');
drupal_add_library('system', 'ui.sortable');
Alex Bronstein
committed
drupal_add_library('system', 'jquery.bbq');
Jacob Singh
committed
drupal_add_js(drupal_get_path('module', 'media_gallery') . '/media_gallery.dragdrop.js');
drupal_add_js($gallery_js_settings, array('type' => 'setting'));
}
else {
// Otherwise, attach the setting to the "add media" link, as per above.
$node->content['add_media_link']['#attached']['js'][] = array(
Jacob Singh
committed
'type' => 'setting',
'data' => $gallery_js_settings,
);
}
}
// For the teaser, only the first thumbnail needs to be displayed, so remove the
Jacob Singh
committed
// rest from the node's field data, so that the field formatter doesn't waste
// time building the render structure for items that won't be shown.
if ($view_mode == 'teaser') {
if (!empty($node->media_gallery_media[LANGUAGE_NONE])) {
$first_item = array_shift($node->media_gallery_media[LANGUAGE_NONE]);
$node->media_gallery_media[LANGUAGE_NONE] = array($first_item);
}
}
// For the full display, implement pagination.
elseif ($view_mode == 'full' || $view_mode == 'media_gallery_block') {
$full = $view_mode == 'full' ? TRUE : FALSE;
if (!empty($node->media_gallery_media)) {
$media = $node->media_gallery_media[LANGUAGE_NONE];
}
else {
$media = array();
}
Jacob Singh
committed
// Only display the requested number of media items per page.
$columns = $full ? $node->media_gallery_columns[LANGUAGE_NONE][0]['value'] : $node->media_gallery_block_columns[LANGUAGE_NONE][0]['value'];
$rows = $full ? $node->media_gallery_rows[LANGUAGE_NONE][0]['value'] : $node->media_gallery_block_rows[LANGUAGE_NONE][0]['value'];
Jacob Singh
committed
$number_per_page = $columns * $rows;
Jacob Singh
committed
$total = count($deltas);
// Initialize the pager and find out what page we are viewing.
$page = $full ? pager_default_initialize($total, $number_per_page, MEDIA_GALLERY_PAGER_ELEMENT) : 0;
Jacob Singh
committed
// Deny access to all media items that aren't on this page.
$min_on_page = $number_per_page * $page;
$max_on_page = $number_per_page * ($page + 1) - 1;
Jacob Singh
committed
foreach ($deltas as $key => $delta) {
Jacob Singh
committed
$fid = _media_gallery_get_media_fid($item);
if ($key < $min_on_page) {
$pre_links[$key] = array(
Jacob Singh
committed
'href' => 'media-gallery/detail/' . $node->nid . '/' . $fid,
'attributes' => array('class' => array('colorbox-supplemental-link pre')),
Jacob Singh
committed
);
Jacob Singh
committed
}
elseif ($key > $max_on_page) {
$post_links[$key] = array(
Jacob Singh
committed
'href' => 'media-gallery/detail/' . $node->nid . '/' . $fid,
'attributes' => array('class' => array('colorbox-supplemental-link post')),
Jacob Singh
committed
);
Jacob Singh
committed
}
}
// Field rendering requires sequential deltas, so re-key.
// @todo Open a Drupal core issue about this.
if ($media) {
$node->media_gallery_media[LANGUAGE_NONE] = array_values($media);
}
else {
$node->media_gallery_media[LANGUAGE_NONE] = array();
}
Jacob Singh
committed
// Create a set of dummy links to media items that don't appear on this
// page, so colorbox can link to them in the slideshow.
// @todo If the gallery contains 1000 media, then rendering each link takes
Jacob Singh
committed
// extra server-side time, extra network time to transfer the markup, and
// extra client-side time to initialize the DOM. Performance can likely be
// significantly improved if we only send the fids to Drupal.settings, and
// add JavaScript code to make that information usable by Colorbox.
$node->content['colorbox_links_pre'] = array(
'#theme' => 'links',
'#links' => $pre_links,
'#weight' => -20,
Alex Bronstein
committed
'#attributes' => array('class' => array('colorbox-supplemental-links')),
Jacob Singh
committed
);
$node->content['colorbox_links_post'] = array(
'#theme' => 'links',
'#links' => $post_links,
'#weight' => 20,
Alex Bronstein
committed
'#attributes' => array('class' => array('colorbox-supplemental-links')),
Jacob Singh
committed
);
// Finally, display the pager, with a high weight so it appears at the
// bottom.
if ($full) {
$node->content['media_gallery_pager'] = array(
'#theme' => 'pager',
'#element' => MEDIA_GALLERY_PAGER_ELEMENT,
'#weight' => 2000,
);
}
Jacob Singh
committed
}
return $node;
}
/**
* Implements hook_field_extra_fields().
*/
function media_gallery_field_extra_fields() {
// Allow the "Add media" link to be sorted with respect to the actual media
Jacob Singh
committed
// gallery fields.
$extra['node']['media_gallery'] = array(
'display' => array(
'add_media_link' => array(
'label' => t('"Add media" link'),
Jacob Singh
committed
'weight' => 1,
),
),
);
return $extra;
}
/**
* Access callback for AJAX-based gallery editing operations.
Jacob Singh
committed
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
* @param string $type
* The type of item being edited: 'gallery' for an individual gallery or
* 'collection' for a gallery collection.
* @param mixed $item
* For a media gallery, the $node object for that gallery; for gallery
* collections, the taxonomy term corresponding to the collection.
* @param string $token
* A token from drupal_get_token.
*/
function media_gallery_edit_access_ajax($type, $item, $token) {
if (!drupal_valid_token($token, 'media_gallery')) {
return FALSE;
}
switch ($type) {
case 'gallery':
return node_access('update', $item);
break;
case 'collection':
return user_access('administer media galleries');
break;
default:
return FALSE;
}
}
/**
* Implements hook_theme().
*/
function media_gallery_theme() {
return array(
'media_gallery_collection' => array(
'render element' => 'element',
'file' => 'media_gallery.theme.inc',
),
'media_gallery_teaser' => array(
'render element' => 'element',
'file' => 'media_gallery.theme.inc',
),
'media_gallery_media_item_thumbnail' => array(
'render element' => 'element',
'file' => 'media_gallery.theme.inc',
'template' => 'media-gallery-media-item-thumbnail',
),
'media_gallery_media_item_lightbox' => array(
'render element' => 'element',
'file' => 'media_gallery.theme.inc',
),
'media_gallery_media_item_detail' => array(
'render element' => 'element',
'file' => 'media_gallery.theme.inc',
),
'media_gallery_block_thumbnail' => array(
'render element' => 'element',
'file' => 'media_gallery.theme.inc',
),
'media_gallery_collection_thumbnail' => array(
'render element' => 'element',
'file' => 'media_gallery.theme.inc',
),
'media_gallery_download_link' => array(
'variables' => array('file' => NULL, 'text' => NULL, 'options' => array()),
'file' => 'media_gallery.theme.inc',
),
'media_gallery_meta' => array(
'variables' => array('display' => NULL, 'location' => NULL, 'title' => NULL, 'license' => NULL, 'description' => NULL),
Jacob Singh
committed
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
'file' => 'media_gallery.theme.inc',
),
'media_gallery_item' => array(
'variables' => array('image' => NULL, 'link_path' => NULL, 'classes' => NULL),
'file' => 'media_gallery.theme.inc',
),
'media_gallery_license' => array(
'variables' => array('element' => NULL, 'color' => 'dark'),
'file' => 'media_gallery.theme.inc',
),
'media_gallery_file_field_inline' => array(
'render element' => 'element',
'file' => 'media_gallery.theme.inc',
),
);
}
/**
* Implements hook_permission().
*/
function media_gallery_permission() {
return array(
'administer media galleries' => array(
'title' => t('Administer media galleries'),
),
);
}
/**
* Implements hook_node_info().
*/
function media_gallery_node_info() {
return array(
'media_gallery' => array(
'name' => t('Gallery'),
'base' => 'media_gallery',
'description' => t('A flexible gallery of media.'),
'help' => t('Create a gallery of thumbnails including custom display settings. Once your gallery is saved, your media can be added.'),
Jacob Singh
committed
),
);
}
/**
* Implements hook_update().
*/
function media_gallery_update($node) {
// If the media gallery node is being saved and is configured to not provide
// a block, remove all blocks associated with it from the database. The block
// module might not be installed, so we need to check that the database table
// exists before querying it.
if (empty($node->media_gallery_expose_block[LANGUAGE_NONE][0]['value']) && db_table_exists('block')) {
Jacob Singh
committed
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
db_delete('block')
->condition('module', 'media_gallery')
->condition('delta', $node->nid)
->execute();
}
}
/**
* Implements hook_block_info().
*/
function media_gallery_block_info() {
$blocks = array();
// Define a block for each media gallery node that is configured to expose
// one.
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'node');
$query->entityCondition('bundle', 'media_gallery');
$query->fieldCondition('media_gallery_expose_block', 'value', 1);
$result = $query->execute();
if (!empty($result['node'])) {
// There is no reason to waste going through a full node_load_multiple()
// when we only need the titles.
$nids = array_keys($result['node']);
$node_titles = db_query("SELECT nid, title FROM {node} WHERE nid IN (:nids)", array(':nids' => $nids))->fetchAllKeyed();
foreach ($node_titles as $nid => $title) {
// The 'info' element is escaped on display, so we pass it through
// unfiltered here.
$blocks[$nid]['info'] = t('Recent gallery items: !title', array('!title' => $title));
$blocks[$nid]['visibility'] = 0;
$blocks[$nid]['pages'] = 'node/' . $nid . "\ngalleries";
}
}
Jacob Singh
committed
return $blocks;
}
Jacob Singh
committed
/**
* Implements hook_block_view().
*/
function media_gallery_block_view($delta = '') {
$node = node_load($delta);
if (empty($node->media_gallery_expose_block[LANGUAGE_NONE][0]['value'])) {
// Bail out now if the node doesn't exist or if it is configured not to
// display a block.
$block['subject'] = NULL;
$block['content'] = '';
}
elseif (empty($node->media_gallery_media_original)) {
Jacob Singh
committed
// Bail out now if there won't be any media items to show.
$block['subject'] = check_plain($node->title);
Jacob Singh
committed
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
$block['content'] = t('No content available.');
}
else {
// Collect an array of file IDs associated with this gallery. For
// simplicity we will assume (here and below) that this is not a
// multilingual field. Also note that the node may have been loaded and
// viewed elsewhere on the page, in which case the 'media_gallery_media'
// field was modified and does not contain what we want, so we have to go
// back to the original field value set in hook_node_load() instead, and
// also clone the node before changing it so our modifications do not
// affect other places where it might be being viewed.
$node = clone $node;
$node->media_gallery_media = $node->media_gallery_media_original;
$files = &$node->media_gallery_media[LANGUAGE_NONE];
$gallery_fids = array();
foreach ($files as $file) {
$gallery_fids[] = _media_gallery_get_media_fid($file);
}
// Construct a list of file IDs that is limited to the specified number of
// items and ordered by most recent; these are the files that will be
// displayed in the block.
$columns = !empty($node->media_gallery_block_columns[LANGUAGE_NONE][0]['value']) ? $node->media_gallery_block_columns[LANGUAGE_NONE][0]['value'] : 1;
$rows = !empty($node->media_gallery_block_rows[LANGUAGE_NONE][0]['value']) ? $node->media_gallery_block_rows[LANGUAGE_NONE][0]['value'] : 1;
$number_to_show = $columns * $rows;
$block_fids = db_select('file_managed', 'f')
->fields('f', array('fid'))
->condition('fid', $gallery_fids, 'IN')
->orderBy('timestamp', 'DESC')
->range(0, $number_to_show)
->execute()
->fetchCol();
// Before sorting, remove any items that will not display in the block.
$fid_order = array_flip($block_fids);
if ($number_to_show < count($files)) {
foreach ($files as $key => $file) {
if (!isset($fid_order[_media_gallery_get_media_fid($file)])) {
unset($files[$key]);
}
}
}
// Prepare the sorting function with the list of file ID orders.
_media_gallery_sort_by_recent(NULL, NULL, $fid_order);
// Perform the sort.
usort($files, '_media_gallery_sort_by_recent');
// Display the block.
$block['subject'] = check_plain($node->title);
David Rothstein
committed
$block['content']['gallery'] = node_view($node, 'media_gallery_block');
// Move the node's contextual links so that they display on the block
// rather than the node (i.e., in the same dropdown as the "Configure
// block" link). This is also required in order to properly integrate with
// the code in media_gallery_contextual_links_view_alter().
if (isset($block['content']['gallery']['#contextual_links'])) {
$block['content']['#contextual_links'] = $block['content']['gallery']['#contextual_links'];
unset($block['content']['gallery']['#contextual_links']);
}
Jacob Singh
committed
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
$block['content']['more_link'] = array(
'#theme' => 'more_link',
'#url' => 'node/' . $node->nid,
'#title' => t('Show the complete gallery'),
'#weight' => 1000,
);
}
return $block;
}
/**
* Implements hook_block_configure().
*/
function media_gallery_block_configure($delta = '') {
// Duplicate the form for configuring media gallery node fields, including
// our module's custom alterations. We can't use drupal_get_form() here
// because that will mess up the form submission, so for now we have to live
// without getting any other module's alterations to this part of the node
// form.
$node = node_load($delta);
$form = array();
$form_state = array();
field_attach_form('node', $node, $form, $form_state, $node->language);
media_gallery_form_media_gallery_node_form_alter($form, $form_state);
// Pull out only the parts of the node form that allow configuration of the
// block settings; these are the ones we want to display.
Jacob Singh
committed
// Store a record of all node fields that we will need to save when
// hook_block_save() is called.
$block_settings['block']['media_gallery_block_fields'] = array(
Jacob Singh
committed
'#type' => 'value',
'#value' => element_children($block_settings['block']),
Jacob Singh
committed
);
// Don't allow people to destroy the block itself from the block
// configuration page.
$block_settings['block']['media_gallery_expose_block']['#access'] = FALSE;
Jacob Singh
committed
// Customize the fieldset display.
$block_settings['block']['#collapsible'] = FALSE;
$block_settings['block']['#title'] = t('Block settings');
unset($block_settings['block']['#weight']);
Jacob Singh
committed
// Add the necessary attached JS and CSS.
_media_gallery_attach_form_resources($block_settings['block']);
Jacob Singh
committed
return $block_settings;
}
/**
* Implements hook_block_save().
*/
function media_gallery_block_save($delta = '', $edit = array()) {
// Save the block-related media gallery fields on the node.
$node = node_load($delta);
foreach ($edit['media_gallery_block_fields'] as $field) {
$node->{$field} = $edit[$field];
}
node_save($node);
}
/**
* Implements hook_library().
*/
function media_gallery_library() {
$colorbox_path = variable_get('media_gallery_library_path', FALSE);
if ($colorbox_path === FALSE ) {
$colorbox_path = module_exists('libraries') ? libraries_get_path('colorbox') : 'sites/all/libraries/colorbox';
}
else {
$colorbox_path .= '/colorbox';
}
Jacob Singh
committed
$stylesheet = variable_get('media_gallery_colorbox_stylesheet', 'example1');
$libraries['colorbox'] = array(
'title' => 'Colorbox',
'website' => 'http://colorpowered.com/colorbox/',
'version' => '1.3.9',
'js' => array(
$colorbox_path . '/colorbox/jquery.colorbox-min.js' => array(),
Jacob Singh
committed
),
'css' => array(
$colorbox_path . '/' . $stylesheet . '/colorbox.css' => array(
Jacob Singh
committed
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
'type' => 'file',
'media' => 'screen',
),
),
);
return $libraries;
}
/**
* Helper function to sort media gallery items by an ordered list of file IDs.
*
* Call once with $set_fid_order set to an array of file orders, keyed by the
* file ID, before performing the actual sort.
*/
function _media_gallery_sort_by_recent($a, $b, $set_fid_order = NULL) {
$fid_order = &drupal_static(__FUNCTION__, array());
// Stored the ordered list if this is a preparatory call.
if (isset($set_fid_order)) {
$fid_order = $set_fid_order;
return;
}
// Otherwise, perform the sort.
$a_order = $fid_order[_media_gallery_get_media_fid($a)];
$b_order = $fid_order[_media_gallery_get_media_fid($b)];
if ($a_order < $b_order) {
return -1;
}
elseif ($b_order < $a_order) {
return 1;
}
else {
return 0;
}
}
/**
* Returns the number of files of each type attached to a media gallery node.
*/
function media_gallery_get_media_type_count($node, $media_field = 'media_gallery_media') {
$fids = media_gallery_get_file_ids($node, $media_field);
if (empty($fids)) {
return array();
}
$query = db_select('file_managed', 'f');
$type = $query->addField('f', 'type');
$query->addExpression('COUNT(*)');
$type_count = $query->condition('f.fid', $fids, 'IN')
->groupBy($type)
->execute()
->fetchAllKeyed();
return $type_count;
}
Jacob Singh
committed
/**
* Returns all file IDs attached to a media gallery node.
*/
function media_gallery_get_file_ids($node, $media_field = 'media_gallery_media') {
Jacob Singh
committed
$fids = array();
lsolesen
committed
$media_items = field_get_items('node', $node, $media_field);
if( $media_items !== FALSE ) {
foreach ($media_items as $item) {
$fids[] = _media_gallery_get_media_fid($item);
}
Jacob Singh
committed
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
}
return drupal_map_assoc($fids);
}
/**
* Determines the file ID from a media file array or object.
*
* This is ugly, but necessary since a media field attached to a node may
* be represented either as an array or a full object, depending on whether the
* node has been processed for viewing yet or not.
*
* @param $file
* Either a media file object or media file array.
*
* @return
* The value of the 'fid' object property or array key.
*/
function _media_gallery_get_media_fid($file) {
return is_object($file) ? $file->fid : $file['fid'];
}
/**
* Removes a media item from a gallery.
*
* @param $node
* The gallery node object.
Alex Bronstein
committed
* @param $file
* The file to remove from the gallery.
Jacob Singh
committed
*
* @return
* The updated gallery node object.
*/
Alex Bronstein
committed
function media_gallery_remove_item_from_gallery($node, $file) {
Jacob Singh
committed
$items = &$node->media_gallery_media[LANGUAGE_NONE];
foreach ($items as $key => $item) {
Alex Bronstein
committed
if ($file->fid == _media_gallery_get_media_fid($item)) {
Jacob Singh
committed
unset($items[$key]);
}
}
node_save($node);
return $node;
}
/**
* Implements hook_entity_info_alter().
*/
Alex Bronstein
committed
function media_gallery_entity_info_alter(&$entity_info) {
// Add view modes for displaying files in gallery contexts.
foreach (media_gallery_file_view_modes() as $view_mode => $label) {
$entity_info['file']['view modes'][$view_mode] = array('label' => $label, 'custom settings' => FALSE);
Jacob Singh
committed
}
Alex Bronstein
committed
// Add a view mode for media_gallery_block_view() to use for displaying a
// media gallery node in a block. Drupal does not support restricting view
// modes to specific bundles.
$entity_info['node']['view modes']['media_gallery_block'] = array('label' => t('Media gallery block'), 'custom settings' => FALSE);
Jacob Singh
committed
}
/**
* Implements hook_form().
*/
function media_gallery_form($node, $form_state) {
$form = node_content_form($node, $form_state);
return $form;
}
/**
* Implements hook_form_alter().
*/
function media_gallery_form_alter(&$form, &$form_state, $form_id) {
if (strpos($form_id, 'media_edit') === 0) {
// Act on both the regular and multiform versions of the edit form.
if ($form_id === 'media_edit' || preg_match('/^media_edit_[0-9]+$/', $form_id)) {
// Prepopulate the media_edit form with our best guess at the image title.
Jacob Singh
committed
if (!empty($form['media_title']) && empty($form['media_title'][LANGUAGE_NONE][0]['value']['#default_value'])) {
$fid = $form['fid']['#value'];
Alex Bronstein
committed
$file = file_load($fid);
if ($file->type === 'image') {
$form['media_title'][LANGUAGE_NONE][0]['value']['#default_value'] = _media_gallery_get_media_title($file);
Jacob Singh
committed
}
}
// Prepopulate the license field with the correct default.
if ($form['field_license'][LANGUAGE_NONE]['#default_value'] == '_none') {
$form['field_license'][LANGUAGE_NONE]['#default_value'] = 'none';
}
unset($form['field_license'][LANGUAGE_NONE]['#options']['_none']);
Jacob Singh
committed
}
David Rothstein
committed
// Attach JavaScript and CSS needed to alter elements in the form.
_media_gallery_attach_edit_resources($form);
Jacob Singh
committed
}
}
/*
* Implements hook_form_FORM_ID_alter().
*/
function media_gallery_form_media_gallery_node_form_alter(&$form, &$form_state) {
_media_gallery_attach_form_resources($form);
// The UI for the multi value media field and the node weight is elsewhere.
$form['media_gallery_media']['#access'] = FALSE;
$form['media_gallery_weight']['#access'] = FALSE;
Jacob Singh
committed
// Hiding this field because we only support a single collection at the moment.
$form['media_gallery_collection']['#access'] = FALSE;
// Wrap a fieldset around the gallery settings.
$form['settings_wrapper'] = array(
'#type' => 'fieldset',
'#title' => t('Gallery settings'),
'#weight' => 10,
);
unset($form['media_gallery_lightbox_extras'][LANGUAGE_NONE]['#description']);
$form['media_gallery_lightbox_extras'][LANGUAGE_NONE]['#label'] = 'Show title and description';
Jacob Singh
committed
// These are the items that need to be added to the fieldset. The array
// values represent a subgroup within the fieldset array that the items are
// further grouped by.
$fieldset = array(
'media_gallery_columns' => 'gallery',
'media_gallery_rows' => 'gallery',
'media_gallery_image_info_where' => 'gallery',
'media_gallery_allow_download' => 'presentation',
'media_gallery_format' => 'presentation',
'media_gallery_lightbox_extras' => 'presentation',
);
// Move the items to the fieldset.
foreach($fieldset as $id => $subgroup) {
$form['settings_wrapper'][$subgroup][$id] = $form[$id];
Alex Bronstein
committed
// locale_field_node_form_submit() looks for field language information in
// a hard-coded part of $form.
// @todo Other modules may as well, so would be best to move form elements
// within #pre_render rather than in hook_form_alter().
$form[$id] = array('#language' => $form[$id]['#language']);
}
// Add a vertical tab menu for blocks
$form['block'] = array(
'#type' => 'fieldset',
'#title' => 'Blocks',
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#group' => 'additional_settings',
'#attached' => array(),
'#weight' => -100,
);
$fieldset = array(
Jacob Singh
committed
'media_gallery_expose_block' => 'block',
'media_gallery_block_columns' => 'block',
'media_gallery_block_rows' => 'block',
);
// Move the items to the fieldset.
foreach($fieldset as $id => $subgroup) {
Alex Bronstein
committed
// locale_field_node_form_submit() looks for field language information in
// a hard-coded part of $form.
// @todo Other modules may as well, so would be best to move form elements
// within #pre_render rather than in hook_form_alter().
$form[$id] = array('#language' => $form[$id]['#language']);
Jacob Singh
committed
}
// Add a class to the fieldset to target it in the js
$form['block']['#attributes']['class'] = array('block-form');
Jacob Singh
committed
// Add classes where necessary for JS enhancement.
$form['settings_wrapper']['gallery']['media_gallery_image_info_where']['#attributes']['class'][] = 'form-inline label';
$form['settings_wrapper']['gallery']['media_gallery_image_info']['#attributes']['class'][] = 'form-inline';
// Use #prefix and #suffix to group the fields.
$form['settings_wrapper']['presentation']['media_gallery_format']['#attributes']['class'][] = 'media-gallery-show no-group-label';
$form['settings_wrapper']['gallery']['#prefix'] = '<div class="gallery-settings settings-group hidden clearfix"><div class="setting-icon"></div><div class="no-overflow">';
Jacob Singh
committed
$form['settings_wrapper']['gallery']['#suffix'] = '</div></div>';
$form['settings_wrapper']['presentation']['#prefix'] = '<div class="presentation-settings settings-group hidden clearfix"><div class="group-label">' . t('Presentation settings') . '</div><div class="setting-icon"></div><div class="no-overflow">';
$form['settings_wrapper']['presentation']['#suffix'] = '</div></div>';
// Enhance the "number of rows" textfields by adding a dropdown element.
$form['settings_wrapper']['gallery']['media_gallery_rows']['#process'][] = 'media_gallery_process_dropdown';
$form['settings_wrapper']['gallery']['media_gallery_rows']['#media_gallery_dropdown_options'] = array('1', '3', '5', '10', 'other');
$form['block']['media_gallery_block_rows']['#process'][] = 'media_gallery_process_dropdown';
$form['block']['media_gallery_block_rows']['#media_gallery_dropdown_options'] = array('1', '2', '3', '4', 'other');
// Adjust the weight of the fields in the presentation wrapper
$form['settings_wrapper']['presentation']['media_gallery_allow_download']['#weight'] = 0;
Jacob Singh
committed
// @todo At some point, it would be nice to have a functional preview display
// of gallery nodes, but until happens, remove the Preview button.
$form['actions']['preview']['#access'] = FALSE;
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function media_gallery_form_media_edit_alter(&$form, &$form_state) {
David Rothstein
committed
// Adjust the media edit form when it is shown within a gallery context.
Jacob Singh
committed
if (isset($form_state['media_gallery']['gallery'])) {
// Remove the Delete button, since media entities can't be deleted when they
David Rothstein
committed
// are in use.
Jacob Singh
committed
$form['actions']['delete']['#access'] = FALSE;
David Rothstein
committed
// Instead, provide a "Remove" checkbox to let users remove the item from
// the gallery.
_media_gallery_add_remove_checkbox($form, $form_state, $form_state['media_gallery']['gallery']);
Jacob Singh
committed
// Add a submit handler to alter $form_state['redirect'] to the
// in-gallery-context View page. It's annoying to have to add a submit
// handler for this, but see http://drupal.org/node/579366#comment-2099836.
// Make sure to add this for the form-level submit handlers and also for the
// button-level submit handlers of the "Save" button, in case those are
// being used.
$form['#submit'][] = 'media_gallery_media_edit_submit';
if (isset($form['actions']['submit']['#submit'])) {
$form['actions']['submit']['#submit'][] = 'media_gallery_media_edit_submit';
}
}
David Rothstein
committed
// On the media gallery multiedit page, add a "Remove" checkbox to each item.
elseif (($node = menu_get_object()) && arg(2) === 'multiedit' && $node->type === 'media_gallery') {
_media_gallery_add_remove_checkbox($form, $form_state, $node);
}
}
/**
* Add a "remove" checkbox to the media edit form.
*/
function _media_gallery_add_remove_checkbox(&$form, &$form_state, $node) {
// Keep a reference to the gallery this media item belongs to, so we know
// what to remove it from.
$form['#gallery'] = $node;
// Put the remove checkbox inside the "preview" section, so it shows up
// underneath the thumbnail.
// @todo: Move into $form['preview']['remove'] when issue
// http://drupal.org/node/1055986 get committed.
$form['remove'] = array(
David Rothstein
committed
'#type' => 'checkbox',
'#title' => t('Remove from gallery'),
'#description' => t('The original file remains in your <a href="@library">media library</a>.', array('@library' => url('admin/content/media'))),