<?php /** * @file * A light-weight, customizable image gallery plugin for Drupal based on jQuery */ /** * Implements hook_menu(). */ function galleria_menu() { $items = array(); $items['admin/config/media/galleria'] = array( 'title' => 'Galleria', 'description' => 'Configure Galleria image galleries.', 'page callback' => 'galleria_page_optionset_list', 'access arguments' => array('administer site configuration'), 'file' => 'includes/galleria.admin.inc', ); $items['admin/config/media/galleria/list'] = array( 'title' => 'Option sets', 'description' => 'List the current Galleria option sets.', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 1, ); $items['admin/config/media/galleria/add'] = array( 'title' => 'Add option set', 'description' => 'Add a new Galleria option set.', 'page callback' => 'drupal_get_form', 'page arguments' => array('galleria_form_optionset_add'), 'access arguments' => array('administer site configuration'), 'type' => MENU_LOCAL_ACTION, 'weight' => 1, 'file' => 'includes/galleria.admin.inc', ); $items['admin/config/media/galleria/edit/%galleria_optionset'] = array( 'title' => 'Edit option set', 'description' => 'Configure an option set.', 'page callback' => 'drupal_get_form', 'page arguments' => array('galleria_form_optionset_edit', 5), 'access arguments' => array('administer site configuration'), 'file' => 'includes/galleria.admin.inc', ); $items['admin/config/media/galleria/delete/%galleria_optionset'] = array( 'title' => 'Delete option set', 'description' => 'Delete an option set.', 'page callback' => 'drupal_get_form', 'page arguments' => array('galleria_optionset_form_delete', 5), 'access arguments' => array('administer site configuration'), 'file' => 'includes/galleria.admin.inc', ); $items['admin/config/media/galleria/advanced'] = array( 'title' => 'Advanced settings', 'description' => 'Configure the advanced Galleria module settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('galleria_form_settings'), 'access arguments' => array('administer site configuration'), 'type' => MENU_LOCAL_TASK, 'weight' => 2, 'file' => 'includes/galleria.admin.inc', ); return $items; } /** * Implements hook_help(). */ function galleria_help($path, $arg) { switch ($path) { case 'admin/config/media/galleria/edit/%': return '<p>' . t('An <em>option set</em> defines exactly how a Galleria image gallery looks like on your site. ' . 'It is s a combination of <a href="@styles">image styles</a> for the various image sizes, Galleria theme and options.', array('@styles' => url('admin/config/media/image-styles'))). '<br />' . t('For a full documentation of all options, refer to the official <a href="@docs">Galleria documentation</a>.', array('@docs' => url('http://galleria.aino.se/docs/1.2/options/'))) . '</p>'; case 'admin/config/media/galleria/advanced': return '<p>' . t('This page lists some automatically detected files and folders. To improve performance, they are cached until the files get deleted.<br />' . 'If the module behaves strangely, try to clear the cached values below.') . '</p>'; } } /** * Implements hook_theme(). */ function galleria_theme() { return array( 'galleria_container' => array( 'variables' => array('items' => array(), 'settings' => array()), 'template' => 'theme/galleria-container', 'file' => 'theme/theme.inc', ), 'galleria_form_table' => array( 'render element' => 'form', 'file' => 'includes/galleria.admin.inc', ), ); } /** * Implements hook_image_default_styles(). * * Provides default image style presets that can be overridden by site administrators. */ function galleria_image_default_styles() { $styles = array(); // image preset for the big image $styles['galleria_zoom'] = array( 'effects' => array( array( 'name' => 'image_scale', 'data' => array('width' => 450, 'height' => 300, 'upscale' => 1), 'weight' => 0, ), ) ); // image preset for thumbnails $styles['galleria_thumb'] = array( 'effects' => array( array( 'name' => 'image_scale', 'data' => array('width' => 50, 'height' => 40, 'upscale' => 1), 'weight' => 0, ), ) ); return $styles; } /** * Implements hook_views_api(). * * This tells drupal that there is Views integration file named * galleria.views.inc */ function galleria_views_api() { return array( 'api' => 3, ); } /** * Fetches all option sets from the database and returns them as an associative array. */ function galleria_optionsets() { $optionsets = db_query("SELECT * FROM {galleria_optionset}")->fetchAllAssoc('name', PDO::FETCH_ASSOC); foreach ($optionsets as &$optionset) { $optionset['options'] = empty($optionset['options']) ? array() : unserialize($optionset['options']); $optionset['plugins'] = empty($optionset['plugins']) ? array() : unserialize($optionset['plugins']); } return $optionsets; } /** * Fetches the given option set and returns it as an associative array or FALSE, if no set could be found. */ function galleria_optionset_load($optionset_name) { $optionset = db_query("SELECT * FROM {galleria_optionset} WHERE name = :name", array(':name' => $optionset_name))->fetchAssoc(); if ($optionset !== FALSE) { $optionset['options'] = empty($optionset['options']) ? array() : unserialize($optionset['options']); $optionset['plugins'] = empty($optionset['plugins']) ? array() : unserialize($optionset['plugins']); } return $optionset; } /** * Checks whether an option set with the given name already exists. */ function galleria_optionset_exists($optionset_name) { return (galleria_optionset_load($optionset_name) !== FALSE); } /** * Saves the given option set to the database. * Set the $new flag if this set has not been written before. */ function galleria_optionset_save($optionset, $new = FALSE) { $optionset += array( 'title' => $optionset['name'], 'options' => array(), ); if ($new) { drupal_write_record('galleria_optionset', $optionset); } else { drupal_write_record('galleria_optionset', $optionset, 'name'); } return $optionset; } /** * Deletes the given option set from the database. */ function galleria_optionset_delete($optionset) { db_delete('galleria_optionset')->condition('name', $optionset['name'])->execute(); // TODO: update formatters? return TRUE; } /* * Returns the JavaScript file of the Galleria core. * Uses a cached filename until this file gets deleted or the cache gets cleared. */ function galleria_get_library_file() { $cache = cache_get('galleria_lib_file'); if (($cache !== FALSE) && file_exists($cache->data)) return $cache->data; // Search for library file $libpath = libraries_get_path('galleria'); // Seach for minimized files first. // Sort the found files to use the newest version if there's more than one. $js = glob($libpath . '/galleria-*.min.js'); if (count($js) == 0) $js = glob($libpath . '/galleria-*.js'); if (count($js) > 0) { rsort($js); cache_set('galleria_lib_file', $js[0]); return $js[0]; } else { // Could not find JavaScript library return FALSE; } } /** * Searches for available themes inside the Galleria folder. * The list of themes is cached for performance reasons. */ function galleria_get_themes($nocache = FALSE) { if (!$nocache && (($themes = cache_get('galleria_themes')) !== FALSE)) return $themes->data; $themes = array(); // Search for theme folders $path = libraries_get_path('galleria') . '/themes/'; if (is_dir($path) && (($path_handle = opendir($path)) !== FALSE)) { while (($theme = readdir($path_handle)) !== FALSE) { if (!is_dir($path . $theme) || $theme[0] == '.') continue; // Search for the theme JavaScript file, minified version first $js = glob($path . $theme . '/*.min.js'); if (count($js) == 0) $js = glob($path . $theme . '/*.js'); // Sort by filename to (hopefully) get the newest version. if (count($js) > 0) { rsort($js); $themes[$theme] = $js[0]; } } closedir($path_handle); cache_set('galleria_themes', $themes); } return $themes; } /** * Returns the JavaScript file of the given theme, or FALSE if it could not be found. */ function galleria_get_theme_file($theme) { $themes = galleria_get_themes(); return isset($themes[$theme]) ? $themes[$theme] : FALSE; } /** * Searches for available plugins inside the Galleria folder. * The list of plugins is cached for performance reasons. */ function galleria_get_plugins($nocache = FALSE) { if (!$nocache && (($plugins = cache_get('galleria_plugins')) !== FALSE)) return $plugins->data; $plugins = array(); // Search for theme folders $path = libraries_get_path('galleria') . '/plugins/'; if (is_dir($path) && (($path_handle = opendir($path)) !== FALSE)) { while (($plugin = readdir($path_handle)) !== FALSE) { if (!is_dir($path . $plugin) || $plugin[0] == '.') continue; // Search for the theme JavaScript file, minified version first $js = glob($path . $plugin . '/*.min.js'); if (count($js) == 0) $js = glob($path . $plugin . '/*.js'); // Sort by filename to (hopefully) get the newest version. if (count($js) > 0) { rsort($js); $plugins[$plugin] = $js[0]; } } closedir($path_handle); cache_set('galleria_plugins', $plugins); } return $plugins; } /** * Returns the JavaScript file of the given plugin, or FALSE if it could not be found. */ function galleria_get_plugin_file($plugin) { $plugins = galleria_get_plugins(); return isset($plugins[$plugin]) ? $plugins[$plugin] : FALSE; } /** * Implements hook_field_formatter_info(). * * Adds the Galleria format option within the manage display form of * of an image field. */ function galleria_field_formatter_info() { return array( 'galleria' => array( 'label' => t('Galleria'), 'field types' => array('image', 'media', 'node_reference', 'file'), 'settings' => array( 'optionset' => 'default', 'title_field' => '', 'alt_field' => '', 'referenced_fields' => array(), ), ), ); } /** * Implements hook_field_formatter_settings_form(). * * Provides display settings form within the manage display page of * an image field with formatter galleria. */ function galleria_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { $display = $instance['display'][$view_mode]; $settings = $display['settings']; $form = array( '#tree' => TRUE, ); // Show select box for the option set $optionsets = array(); foreach (galleria_optionsets() as $name => $optionset) { $optionsets[$name] = check_plain($optionset['title']); } $form['optionset'] = array( '#title' => t('Option set'), '#type' => 'select', '#options' => $optionsets, '#default_value' => $settings['optionset'], ); if ($field['type'] == 'file') { // Provide alternate settings to allow selection of caption and alt tags. $bundles = field_info_instances('file'); // Determine if there are additional fields on the image instance. if (isset($bundles['image']) && !empty($bundles['image'])) { $options = array('' => t('None')); foreach ($bundles['image'] as $field_name => $field_details) { $options[$field_name] = $field_details['label']; } $form['alt_field'] = array( '#title' => t('Alt field'), '#description' => t('Select an optional field to draw alt tags from.'), '#type' => 'select', '#options' => $options, '#default_value' => isset($settings['alt_field']) ? $settings['alt_field'] : '' ); $form['title_field'] = array( '#description' => t('Select an optional field to draw images titles from.'), '#title' => t('Title field'), '#type' => 'select', '#options' => $options, '#default_value' => isset($settings['title_field']) ? $settings['title_field'] : '' ); } } // Show select box for image fields if we're formatting a node_reference field if ($field['type'] == 'node_reference') { // Find all image fields $referenceable_types = array_filter($field['settings']['referenceable_types']); foreach ($referenceable_types as $referenceable_type) { $image_fields = array(); foreach (field_info_instances('node', $referenceable_type) as $field_name => $field_instance) { $field = field_info_field($field_name); if (($field['type'] == 'image') || ($field['type'] == 'media')) { $image_fields[$field_name] = $field_instance['label']; } } if (!empty($image_fields)) { $form['referenced_fields'][$referenceable_type] = array( '#type' => 'checkboxes', '#title' => node_type_get_name($referenceable_type), '#options' => $image_fields, '#default_value' => isset($settings['referenced_fields'][$referenceable_type]) ? $settings['referenced_fields'][$referenceable_type] : array(), ); } } if (count($form['referenced_fields']) == 0) { drupal_set_message(t('The referenced node type(s) does not contain any valid image fields.'), 'error'); } else { $form['referenced_fields'] += array( '#type' => 'fieldset', '#title' => t('Image source fields'), '#description' => t('Select the image fields of the referenced content types to use as the source for Galleria.'), ); } } return $form; } /** * Implements hook_field_formatter_settings_summary(). * * Displays the summary of the set options of a Galleria formatted image field */ function galleria_field_formatter_settings_summary($field, $instance, $view_mode) { $display = $instance['display'][$view_mode]; $settings = $display['settings']; $summary = array(); // Optionset $optionset = t('Default settings'); if (!empty($settings['optionset'])) { $o = galleria_optionset_load($settings['optionset']); if ($o !== FALSE) { $optionset = $o['title']; } } $summary[] = t('Option set: %optionset', array('%optionset' => $optionset)); // Summary for file fields. if ($field['type'] == 'file') { // Prepare options list. if (!empty($settings['alt_field']) || !empty($settings['title_field'])) { $bundles = field_info_instances('file'); // Determine if there are additional fields on the image instance. if (isset($bundles['image']) && !empty($bundles['image'])) { $options = array('' => t('None')); foreach ($bundles['image'] as $field_name => $field_details) { $options[$field_name] = $field_details['label']; } } } if (!empty($settings['alt_field'])) { $summary[] = t('Alt field: @title', array('@title' => $options[$settings['alt_field']])); } if (!empty($settings['title_field'])) { $summary[] = t('Title field: @title', array('@title' => $options[$settings['title_field']])); } } // For node_reference fields: Referenced image fields if ($field['type'] == 'node_reference') { $referenced_fields = array(); foreach ($settings['referenced_fields'] as $node_type => $fields) { $fields = array_keys(array_filter($fields)); if (!empty($fields)) { foreach ($fields as &$field) { $field_info = field_info_instance('node', $field, $node_type); $field = $field_info['label']; } $referenced_fields[] = node_type_get_name($node_type) . ': ' . implode(', ', $fields); } } $referenced_fields = empty($referenced_fields) ? t('All') : implode('; ', $referenced_fields); $summary[] = t('Referenced image fields: %fields', array('%fields' => $referenced_fields)); } return '<p>' . implode("</p>\n<p>", $summary) . '</p>'; } /** * Implements hook_field_formatter_prepare_view(). * * Loads the attached nodes for node_reference fields so we can later access the image fields. */ function galleria_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) { if ($field['type'] == 'node_reference') { // Collect ids to load. $ids = array(); foreach ($displays as $id => $display) { foreach ($items[$id] as $item) { if ($item['access']) { $ids[$item['nid']] = $item['nid']; } } } // Load the nodes. $nodes = node_load_multiple($ids); field_attach_prepare_view('node', $nodes, 'default', $langcode); entity_prepare_view('node', $nodes, $langcode); // Add the loaded nodes to the items. foreach ($displays as $id => $display) { foreach ($items[$id] as &$item) { if ($item['access']) { $item['node'] = $nodes[$item['nid']]; } } } } } /** * Implements hook_field_formatter_view(). * * Prepares a renderable array of images and adds the neccessary JS and CSS */ function galleria_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { if ($field['type'] == 'media') { $items = galleria_prepare_media_images($items); } elseif ($field['type'] == 'file') { $items = galleria_prepare_file_images($items, $display['settings'], $langcode); } elseif ($field['type'] == 'node_reference') { // Cleanup referenced_fields array $referenced_fields = &$display['settings']['referenced_fields']; foreach ($referenced_fields as &$field) { $field = array_filter($field); } $referenced_fields = array_filter($referenced_fields); // Collect items $images = array(); foreach ($items as $item) { $node = $item['node']; foreach (field_info_instances('node', $node->type) as $field_name => $field_instance) { $field_info = field_info_field($field_name); if ($field_info['type'] == 'image' || $field_info['type'] == 'media') { if (empty($referenced_fields) || !empty($referenced_fields[$node->type][$field_name])) { $lang = field_language('node', $node, $field_name, $langcode); $node_items = $node->{$field_name}[$lang]; if ($field_info['type'] == 'media') { $node_items = galleria_prepare_media_images($node_items); } $images = array_merge($images, $node_items); } } } } $items = $images; } $element = array(); if (count($items) > 0) { $element[] = array( '#theme' => 'galleria_container', '#items' => $items, '#settings' => $display['settings'], ); } return $element; } /** * Util function to parse media $items and return images in format expected by * galleria formatter. * * @param array $items * Array of field items. */ function galleria_prepare_media_images($items) { $image_items = array(); foreach ($items as $item) { if ($item['file']->type == 'image') { $file = (array) $item['file']; $file += array('alt' => '', 'title' => ''); $image_items[] = $file; } } return $image_items; } /** * Util function to parse file $items and return images in format expected by * galleria formatter. * * @param array $items * Array of field items. * @param array $settings * Array of settings * @param string $langcode * Language code of the field */ function galleria_prepare_file_images($items, $settings, $langcode) { $image_fids = array(); $alt_field = empty($settings['alt_field']) ? FALSE : $settings['alt_field']; $title_field = empty($settings['title_field']) ? FALSE : $settings['title_field']; // First pass, fetch the fids. We pass twice so we can minimize calls to // entity_load which is expensive to call multiple times. foreach ($items as $delta => $item) { if ($item['type'] == 'image') { $image_fids[] = $item['fid']; // Set a sensible default. $items[$delta]['alt'] = $items[$delta]['title'] = ''; } else { // Galleria only handles images. unset($items['key']); } } $file_entities = entity_load('file', $image_fids); // Second pass, set the alt/title tag. foreach ($items as $delta => $item) { if ($item['type'] == 'image') { $file_entity = $file_entities[$item['fid']]; if ($alt_field) { $alt_field_items = field_get_items('file', $file_entity, $alt_field, $langcode); if (!empty($alt_field_items[0]['safe_value'])) { $items[$delta]['alt'] = $alt_field_items[0]['safe_value']; } } if ($title_field) { $title_field_items = field_get_items('file', $file_entity, $title_field, $langcode); if (!empty($title_field_items[0]['safe_value'])) { $items[$delta]['title'] = $title_field_items[0]['safe_value']; } } } } return $items; } /** * This function loads the required JavaScripts and settings for a Galleria instance. */ function galleria_add_js($id, $optionset) { // Static array to remember which scripts are already attached. $cache = &drupal_static(__FUNCTION__, array()); // Library JavaScript // Cache this filename to prevent multiple file_exists() calls. if (!isset($cache['lib'])) { $lib = galleria_get_library_file(); if ($lib === FALSE) { drupal_set_message(t('The Galleria JavaScript file was not found in its path. Please refer to README.txt for installation instructions.'), 'error'); return; } $cache['lib'] = $lib; } drupal_add_js($cache['lib'], array('group' => JS_LIBRARY)); // Theme // As it's only possible to use one theme per page, use the first one we get for everything. if (empty($cache['theme'])) { $theme = (!empty($optionset['theme'])) ? $optionset['theme'] : 'classic'; $theme_file = galleria_get_theme_file($theme); if (($theme_file === FALSE) && ($theme !== 'classic')) { // Try to fall back to classic theme, the other one might have been deleted. $theme_file = galleria_get_theme_file('classic'); } $cache['theme'] = $theme_file; } // Plugins $plugins = empty($optionset['plugins']) ? array() : $optionset['plugins']; foreach ($plugins as $plugin) { if (empty($cache['plugins'][$plugin])) { $plugin_file = galleria_get_plugin_file($plugin); if ($plugin_file !== FALSE) { $cache['plugins'][$plugin] = $plugin_file; drupal_add_js($plugin_file); } } } // JavaScript settings $js_settings = array( 'themepath' => file_create_url($cache['theme']), 'optionsets' => array( $optionset['name'] => $optionset['options'], ), 'instances' => array( 'galleria-' . $id => $optionset['name'], ), ); drupal_add_js(array('galleria' => $js_settings), 'setting'); // Loader JavaScript drupal_add_js(drupal_get_path('module', 'galleria') . '/js/galleria.load.js'); }