juicebox.module in Juicebox HTML5 Responsive Image Galleries 7
Same filename and directory in other branches
Provides Drupal integration with the Juicebox library.
File
juicebox.moduleView source
<?php
/**
* @file
* Provides Drupal integration with the Juicebox library.
*/
/**
* Implements hook_menu().
*/
function juicebox_menu() {
$items = array();
// Add menu item that produces the "config.xml" data that is linked to a
// specific view or entity field.
$items['juicebox/xml/%'] = array(
'title' => 'Juicebox XML from view',
'description' => 'Deliver configuration XML for a Juicebox gallery.',
'page callback' => 'juicebox_page_xml',
'page arguments' => array(
2,
),
// For efficiency we'll check access in parallel to other logic in the
// callback function, so we don't limit any access here.
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_theme().
*/
function juicebox_theme() {
return array(
// Theme hook to generate embed markup for a Juicebox gallery.
'juicebox_embed_markup' => array(
'variables' => array(
'gallery_id' => '',
'gallery_xml_path' => '',
'settings' => array(),
'data' => array(),
),
'path' => drupal_get_path('module', 'juicebox') . '/themes',
'file' => 'juicebox.theme.inc',
),
);
}
/**
* Implements hook_libraries_info().
*/
function juicebox_libraries_info() {
$libraries['juicebox'] = array(
'name' => 'Juicebox',
'vendor url' => 'http://www.juicebox.net/',
'download url' => 'http://www.juicebox.net/download/',
'version arguments' => array(
'file' => 'juicebox.js',
'pattern' => '/Juicebox.([a-zA-Z]+[0-9\\.\\ -]+)/',
'lines' => 5,
),
'files' => array(
// Note that we do not want the Juicebox library javascript to be
// aggregated by Drupal (set preprocess option = FALSE). This is because
// some supporting library CSS files must be at a specific location
// RELATIVE to to the main js file. Aggregation breaks this.
'js' => array(
'juicebox.js' => array(
'preprocess' => FALSE,
),
),
),
'callbacks' => array(
'info' => array(
'_juicebox_library_info',
),
'post-detect' => array(
'_juicebox_library_post_detect',
),
),
);
return $libraries;
}
/**
* Implements hook_views_api().
*/
function juicebox_views_api() {
return array(
'api' => 3.0,
);
}
/**
* Menu callback: generate Juicebox XML.
*
* Note that this callback directly sets page headers and prints the XML result
* (if one can successfully be rendered).
*
* @see juicebox_menu()
*/
function juicebox_page_xml() {
$got_result = FALSE;
// We don't always know exactly how many args are being passed, so we have to
// fetch them programmatically with func_get_args().
$args = func_get_args();
// If this XML request is related to a view, we first have to re-construct the
// view before we can extract the needed XML data.
if ($args[0] == 'view') {
// Set key variables from the path.
$view_name = $args[1];
$view_display = $args[2];
// The view arguments are what remains after the first 3 entries are
// removed from $args.
$view_args = array_slice($args, 3);
// Load the view.
$view = views_get_view($view_name);
if ($view) {
// Check that this user actually has access to the view we are building
// the XML for. Kill callback with "access denied" if not.
if (!$view
->access($view_display)) {
return MENU_ACCESS_DENIED;
}
// Render the view.
$view
->preview($view_display, $view_args);
$data = juicebox_build_gallery_data_from_view($view, current_path());
if (!empty($data)) {
// Render the Juicebox XML.
$xml = juicebox_render_gallery_xml($data);
$got_result = TRUE;
}
}
}
// If this XML request is related to an entity field, we first have to
// re-construct the entity and the field details before we can extract the
// needed XML data.
if ($args[0] == 'entity') {
// Set key variables from the path.
$entity_type = $args[1];
$entity_id = $args[2];
$field_name = $args[3];
$display_name = $args[4];
// Build the entity.
$entities = entity_load($entity_type, array(
$entity_id,
));
if (isset($entities[$entity_id])) {
$entity = $entities[$entity_id];
// Get the bundle details.
$info = entity_get_info($entity_type);
if (empty($info['entity keys']['bundle'])) {
$bundle = $entity_type;
}
else {
$bundle = $entity->{$info['entity keys']['bundle']};
}
// Get the instance and display details.
$instance = field_info_instance($entity_type, $field_name, $bundle);
if (!empty($instance['display'])) {
$settings = $instance['display']['default']['settings'];
if (isset($instance['display'][$display_name]['settings'])) {
$settings = $instance['display'][$display_name]['settings'];
}
// Initialize the "settings" values before working with them. This is
// required for legacy support.
$settings = _juicebox_init_display_settings($settings);
// Build the field. We don't actually need a fully built field (we just
// need the raw items) but building easily triggers all field hooks.
$built_field = field_view_field($entity_type, $entity, $field_name, $instance['display']);
// Check that this user has access. We need to check both field-level
// and entity-level access.
if (empty($built_field['#access']) || !_juicebox_check_entity_view_access($entity_type, $entity)) {
return MENU_ACCESS_DENIED;
}
// If we have items proceed building the Juicebox XML data.
if (!empty($built_field['#items'])) {
// Build the Juicebox gallery data.
$xml_path = current_path();
$data = juicebox_build_gallery_data_from_entity_field($built_field['#items'], $settings, $entity, $xml_path);
if (!empty($data)) {
// Render the Juicebox XML.
$xml = juicebox_render_gallery_xml($data);
$got_result = TRUE;
}
}
}
}
}
// If we did not get any XML result take any special actions needed.
if (!$got_result) {
// Make it clear that we don't have any XML to send.
return MENU_NOT_FOUND;
}
else {
drupal_add_http_header('Content-Type', 'text/xml');
// Bypass all themeing but still return (don't die) so that
// drupal_page_footer() is called.
print $xml;
return NULL;
}
}
/**
* Generate data for a Juicebox gallery from a view object.
*
* @param object $view
* A fully built/executed view object.
* @param string $xml_path
* The path to this gallery's XML (can be used as a unique gallery ID).
* @return array
* An associative array containing all the content and configuration data
* needed to build a Juicebox gallery and XML.
*/
function juicebox_build_gallery_data_from_view($view, $xml_path) {
$images = array();
$settings = $view->style_plugin->options;
// Get rendered field data that's not available in $view->result
$rendered_fields = $view->style_plugin
->render_fields($view->result);
foreach ($view->result as $row_index => $row) {
// First make sure that we have image field data to work with. This prevents
// php errors from coming up if the user has not yet configured their
// view page display or if the view lists items that don't contain an image.
$field_image_name = 'field_' . $settings['image_field'];
$field_thumb_name = 'field_' . $settings['thumb_field'];
if (!empty($row->{$field_image_name}[0]['raw']['uri']) && !empty($row->{$field_thumb_name}[0]['raw']['uri'])) {
$field_image_uri = $row->{$field_image_name}[0]['raw']['uri'];
$unstyled_image_src = file_create_url($field_image_uri);
// Get the main image source.
$image_src = $unstyled_image_src;
if (!empty($settings['image_field_style'])) {
$image_src = image_style_url($settings['image_field_style'], $field_image_uri);
}
// Get the thumbnail source.
$thumb_src = $unstyled_image_src;
$field_thumb_uri = $row->{$field_thumb_name}[0]['raw']['uri'];
if (!empty($settings['thumb_field_style'])) {
$thumb_src = image_style_url($settings['thumb_field_style'], $field_thumb_uri);
}
// Get the image title.
$title = '';
if (!empty($settings['title_field']) && !empty($rendered_fields[$row_index][$settings['title_field']])) {
$title = $rendered_fields[$row_index][$settings['title_field']];
}
// Get the image caption.
$caption = '';
if (!empty($settings['caption_field']) && !empty($rendered_fields[$row_index][$settings['caption_field']])) {
$caption = $rendered_fields[$row_index][$settings['caption_field']];
}
// Filter the image title and caption markup.
if (!empty($settings['apply_markup_filter'])) {
$title = _juicebox_filter_markup($title);
$caption = _juicebox_filter_markup($caption);
}
// Get the linkURL.
$image_link_src = $unstyled_image_src;
if (!empty($settings['linkurl_source'])) {
if ($settings['linkurl_source'] == 'image_styled') {
$image_link_src = $image_src;
}
elseif (!empty($rendered_fields[$row_index][$settings['linkurl_source']])) {
$image_link_src = $rendered_fields[$row_index][$settings['linkurl_source']];
}
}
// Add each image to the data.
$images[$row_index]['image_src'] = $image_src;
$images[$row_index]['image_link_src'] = $image_link_src;
$images[$row_index]['thumb_src'] = $thumb_src;
$images[$row_index]['title'] = $title;
$images[$row_index]['caption'] = $caption;
$images[$row_index]['linkurl_target'] = $settings['linkurl_target'];
}
}
// Get the Juicebox library-specific options.
$jlib_options = array();
if ($settings['show_title']) {
$jlib_options['gallerytitle'] = check_plain($view
->get_title());
}
$jlib_options = array_merge($jlib_options, _juicebox_get_lib_options($settings));
$data = array(
'jlib_options' => $jlib_options,
'images' => $images,
);
// Allow other modules to alter the data we are using to build the gallery.
$source_info = array(
'xml_path' => $xml_path,
'source' => $view,
);
drupal_alter('juicebox_gallery_data', $data, $settings, $source_info);
// Make sure all Juicebox library configuration keys are lowercase to make
// future key lookups easier.
$data['jlib_options'] = array_change_key_case($data['jlib_options']);
return $data;
}
/**
* Generate data for a Juicebox gallery from an entity image field.
*
* @param array $items
* A list of image items from an image field that is part of an entity. This
* will typically be constructed with field_get_items().
* @param array $settings
* A associative array of field formatter settings specific to this gallery
* display.
* @param object $entity
* The full entity object that the field we are rendering as a gallery
* was sourced from.
* @param string $xml_path
* The path to this gallery's XML (can be used as a unique gallery ID).
* @return array
* An associative array containing all the content and configuration data
* needed to build a Juicebox gallery and XML.
*/
function juicebox_build_gallery_data_from_entity_field($items, $settings, $entity, $xml_path) {
// Prepare images
$images = array();
foreach ($items as $id => $item) {
$unstyled_image_src = file_create_url($item['uri']);
// Get the main image source.
$image_src = $unstyled_image_src;
if (!empty($settings['image_style'])) {
$image_src = image_style_url($settings['image_style'], $item['uri']);
}
// Get thumb source.
$thumb_src = $unstyled_image_src;
if (!empty($settings['thumb_style'])) {
$thumb_src = image_style_url($settings['thumb_style'], $item['uri']);
}
// Get the image title.
$title = '';
if (!empty($item[$settings['title_source']])) {
$title = check_markup($item[$settings['title_source']]);
}
// Get the image caption.
$caption = '';
if (!empty($item[$settings['caption_source']])) {
$caption = check_markup($item[$settings['caption_source']]);
}
// Filter the image title and caption markup.
if (!empty($settings['apply_markup_filter'])) {
$title = _juicebox_filter_markup($title);
$caption = _juicebox_filter_markup($caption);
}
// Get the linkURL.
$image_link_src = $unstyled_image_src;
if (!empty($settings['linkurl_source'])) {
$image_link_src = $image_src;
}
// Add each image to the data.
$images[$id]['image_src'] = $image_src;
$images[$id]['image_link_src'] = $image_link_src;
$images[$id]['thumb_src'] = $thumb_src;
$images[$id]['title'] = $title;
$images[$id]['caption'] = $caption;
$images[$id]['linkurl_target'] = $settings['linkurl_target'];
}
// Get the Juicebox library-specific options.
$jlib_options = _juicebox_get_lib_options($settings);
$data = array(
'jlib_options' => $jlib_options,
'images' => $images,
);
// Allow other modules to alter the data we are using to build the gallery.
$source_info = array(
'xml_path' => $xml_path,
'source' => $entity,
);
drupal_alter('juicebox_gallery_data', $data, $settings, $source_info);
// Make sure all Juicebox library configuration keys are lowercase to make
// future key lookups easier.
$data['jlib_options'] = array_change_key_case($data['jlib_options']);
return $data;
}
/**
* Render the final XML for a Juicebox gallery.
*
* This function is a bit like drupal_render() in that it takes an associative
* array as input and returns final markup. Actaully, this function's $data
* input could probably be made to work with drupal_render(), but as we are
* producing structured XML here (and not HTML), using a custom function instead
* seems more prudent and flexible.
*
* @param array $data
* An associative array containing all the content variables that will be
* used in the Juicebox XML.
* @return string
* Fully renderd XML data for a Juicebox gallery.
*/
function juicebox_render_gallery_xml($data) {
// We use DOMDocument instead of a SimpleXMLElement to build the XML as it's
// much more flexible (CDATA is supported, etc.).
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = TRUE;
$juicebox = $dom
->appendChild($dom
->createElement('juicebox'));
foreach ($data['jlib_options'] as $option => $value) {
$juicebox
->setAttribute($option, $value);
}
foreach ($data['images'] as $image) {
$juicebox_image = $juicebox
->appendChild($dom
->createElement('image'));
$juicebox_image
->setAttribute('imageURL', $image['image_src']);
$juicebox_image
->setAttribute('thumbURL', $image['thumb_src']);
$juicebox_image
->setAttribute('linkURL', $image['image_link_src']);
$juicebox_image
->setAttribute('linkTarget', $image['linkurl_target']);
$juicebox_image_title = $juicebox_image
->appendChild($dom
->createElement('title'));
$juicebox_image_title
->appendChild($dom
->createCDATASection($image['title']));
$juicebox_image_caption = $juicebox_image
->appendChild($dom
->createElement('caption'));
$juicebox_image_caption
->appendChild($dom
->createCDATASection($image['caption']));
}
return $dom
->saveXML();
}
/**
* Libraries API Info Callback
*
* Add baseline variables to a Juicebox library array that are not version
* specific but should always be defined. These values are generic to all
* Juicebox libraries and may be referenced even when the local library info
* cannot be loaded or is not used.
*
* @see juicebox_libraries_info().
*/
function _juicebox_library_info(&$library) {
$library['disallowed_conf'] = array();
$library['compatible_mimetypes'] = array(
'image/gif',
'image/jpeg',
'image/png',
);
}
/**
* Libraries API Post-Detect Callback
*
* Add detailed variables to a Juicebox library array after the version info can
* be detected.
*
* @see juicebox_libraries_info().
*/
function _juicebox_library_post_detect(&$library) {
$pro = FALSE;
$disallowed_conf = array();
if (!empty($library['version'])) {
// Check if this is a Pro version.
if (stripos($library['version'], "Pro") !== FALSE) {
$pro = TRUE;
}
// Get numeric part of the version statement.
$version_number = 0;
$matches = array();
preg_match("/[0-9\\.]+[^\\.]\$/u", $library['version'], $matches);
if (!empty($matches[0])) {
$version_number = $matches[0];
}
// Some options are not available as LITE options before v1.3.
if (!$pro && version_compare($version_number, '1.3', '<')) {
$disallowed_conf = array_merge($disallowed_conf, array(
'jlib_textColor',
'jlib_thumbFrameColor',
'jlib_useFullscreenExpand',
'jlib_useThumbDots',
));
}
}
$library['pro'] = $pro;
$library['disallowed_conf'] = $disallowed_conf;
}
/**
* Get/detect the details of a Juicebox javascript library without loading it.
*
* This is essentially a wrapper for libraries_detect(). It allows library info
* to be fetched independently from the currently loaded version if needed
* (e.g. to accomodate XML requests that don't come from this site). This
* function can also utilize extra cache checks for performance.
*
* @param boolean $local
* Whether-or-not to detect the LOCALLY installed Juicebox library details. If
* FALSE Libraries API detection is bypased and generic library information
* is loaded.
* @return array
* An associative array of the library information.
*/
function _juicebox_library_detect($local = TRUE) {
$library = array();
// If this is a local check we can just use standard Libraries API methods.
if ($local) {
$library = libraries_detect('juicebox');
}
else {
_juicebox_library_info($library);
// See if we have been passed version details in the URL.
$query = drupal_get_query_parameters();
if (!empty($query['jb-version'])) {
$version_number = check_plain($query['jb-version']);
if (!empty($query['jb-pro'])) {
$library['pro'] = TRUE;
$version = 'Pro';
}
else {
$version = 'Lite';
}
$library['version'] = $version . ' ' . $version_number;
_juicebox_library_post_detect($library);
}
}
return $library;
}
/**
* Check view access for an arbitrary entity that contains a Juicebox gallery.
*
* @param string $entity_type
* The type of entity being checked (e.g. "node").
* @param object $entity
* The full entity object that the Juicebox gallery field is attached to.
* @return boolean
* Returns TRUE if view access is allowed for the current user or FALSE if
* not. If access cannot be confirmed, returns NULL.
*/
function _juicebox_check_entity_view_access($entity_type, $entity) {
// If the Entity API module is installed we can use entity_access() to check
// access for numerous entity types via their access callbacks. All core
// entities, and many custom ones, can be handled here.
if (module_exists('entity')) {
return entity_access('view', $entity_type, $entity);
}
// If we can't do a check with entity_access() we only maintain checks for a
// couple popular core entity types that provide thier own explicit access
// functions.
switch ($entity_type) {
case 'node':
return node_access('view', $entity);
case 'user':
return user_view_access($entity);
}
// Log a warning and return NULL if we can't do a conclusive check.
watchdog('juicebox', 'Could not verify view access for entity type %type while building Juicebox XML. This may have resulted in a broken gallery display. You may be able to remove this error by installing the Entity API module and ensuring that an access callback exists for entities of type %type.', array(
'%type' => $entity_type,
), WATCHDOG_ERROR);
}
/**
* Helper to get all Juicebox library conf options from the display settings.
*
* This is used in preparation for generating XML output. Some Juicebox XML
* configuration options are set via a GUI and others are set as manual strings.
* This function merges all of these values into one array.
*
* @param array $settings
* An associative array containing all the settings for a Juicebox gallery.
* @return array
* An associative array of Juicebox XML configuration options.
*/
function _juicebox_get_lib_options($settings) {
$custom_options = array();
// Get the string options set via the GUI.
foreach (array(
'jlib_galleryWidth',
'jlib_galleryHeight',
'jlib_backgroundColor',
'jlib_textColor',
'jlib_thumbFrameColor',
) as $name) {
if (!empty($settings[$name])) {
$name_real = str_replace('jlib_', '', $name);
$custom_options[drupal_strtolower($name_real)] = trim(check_plain($settings[$name]));
}
}
// Get the bool options set via the GUI.
foreach (array(
'jlib_showOpenButton',
'jlib_showExpandButton',
'jlib_showThumbsButton',
'jlib_useThumbDots',
'jlib_useFullscreenExpand',
) as $name) {
$name_real = str_replace('jlib_', '', $name);
$custom_options[drupal_strtolower($name_real)] = $settings[$name] ? 'TRUE' : 'FALSE';
}
// Merge-in the manually assigned options making sure they take priority
// over any conflicting GUI options.
$manual_options = explode("\n", $settings['manual_config']);
foreach ($manual_options as $option) {
$option = trim($option);
if (!empty($option)) {
// Each manual option has only been validated (on input) to be in the form
// optionName="optionValue". Now we need split and sanitize the values.
$matches = array();
preg_match('/^([A-Za-z0-9]+?)="([^"]+?)"$/u', $option, $matches);
list($full_match, $name, $value) = $matches;
$name = drupal_strtolower($name);
// See if the manual option is also a GUI option. If so, remove the GUI
// option.
$match = array_search($name, $custom_options);
if ($match) {
unset($custom_options[$match]);
}
$custom_options[$name] = check_plain($value);
}
}
return $custom_options;
}
/**
* Utility function to modify Juicebox settings after they are loaded from
* Drupal but before they are used in any Juicebox module logic.
*
* This is a central place to modify the $settings arrays that describe the
* configuration for each Juicebox gallery (for both field formatters and views
* displays). Note that for views we could implement an override of
* views_plugin_style::init and do these modifications there, but as no such
* option exists for field formatters we just use this ad hoc utilty for both
* instead.
*
* @param array $settings
* An associative array containing all the settings for a Juicebox gallery.
* @return array
* A modified version of the input settings array.
*/
function _juicebox_init_display_settings($settings) {
// Here we check for cases where we may be loading settings stored with an
// older version of the module. In these cases the settings "schema" may be
// different (i.e. the array is nested differently), so we need to adjust
// accordingly. Note that this is done here "realtime" instead of via a
// one-time hook_update_n process because this is the only way to correctly
// deal with views and fields being loaded from code (exported). Once "old"
// settings are re-saved (under the new "schema"), these checks no longer do
// anything. See also: http://drupal.org/node/1965786
if (!empty($settings['width'])) {
$settings['jlib_galleryWidth'] = $settings['width'];
}
if (!empty($settings['height'])) {
$settings['jlib_galleryHeight'] = $settings['height'];
}
if (!empty($settings['advanced']['config'])) {
$settings['manual_config'] = $settings['advanced']['config'];
}
if (!empty($settings['advanced']['custom_parent_classes'])) {
$settings['custom_parent_classes'] = $settings['advanced']['custom_parent_classes'];
}
if (!empty($settings['linkurl_field'])) {
$settings['linkurl_source'] = $settings['linkurl_field'];
}
return $settings;
}
/**
* Helper to add common elements to Juicebox configuration forms.
*
* Both the field formatter and view plugin share some common configuration
* options and structures. These are merged into the appropriate forms via a
* call to this function.
*
* @param array $form
* The Drupal form array that common elements should be added to.
* @param array $settings
* An associative array containing all the settings for a Juicebox gallery
* (used to set default values).
* @return array
* The merged form array.
*/
function _juicebox_common_form_elements($form, $settings) {
// Get locally installed library details.
$library = _juicebox_library_detect();
$disallowed_conf = array();
if (!empty($library)) {
// If we don't have a known version of the Juicebox library, just show a
// generic warning.
if (empty($library['version'])) {
$notification_top = t('<strong>Notice:</strong> Your Juicebox Library version could not be detected. Some options below may not function correctly.');
}
elseif (!empty($library['disallowed_conf'])) {
$disallowed_conf = $library['disallowed_conf'];
$notification_top = t('<strong>Notice:</strong> You are currently using Juicebox library version <strong>@version</strong> which is not compatible with some of the options listed below. These options will appear disabled until you upgrade to the most recent Juicebox library version.', array(
'@version' => $library['version'],
));
$notification_label = t(' (not available in @version)', array(
'@version' => $library['version'],
));
}
}
else {
$notification_top = t('The Juicebox Javascript library does not appear to be installed. Please download and install the most recent version of the Juicebox library.');
drupal_set_message($notification_top, 'error');
}
$form['juicebox_config'] = array(
'#type' => 'fieldset',
'#title' => t('Juicebox Library - Lite Config'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#description' => !empty($notification_top) ? '<p>' . $notification_top . '</p>' : '',
'#weight' => 10,
);
$form['jlib_galleryWidth'] = array(
'#jb_fieldset' => 'juicebox_config',
'#type' => 'textfield',
'#title' => t('Gallery Width'),
'#description' => t('Set the gallery width in a standard numeric format (such as 100% or 300px).'),
'#element_validate' => array(
'_juicebox_element_validate_dimension',
),
);
$form['jlib_galleryHeight'] = array(
'#jb_fieldset' => 'juicebox_config',
'#type' => 'textfield',
'#title' => t('Gallery Height'),
'#description' => t('Set the gallery height in a standard numeric format (such as 100% or 300px).'),
'#element_validate' => array(
'_juicebox_element_validate_dimension',
),
);
$form['jlib_backgroundColor'] = array(
'#jb_fieldset' => 'juicebox_config',
'#type' => 'textfield',
'#title' => t('Background Color'),
'#description' => t('Set the gallery background color as a CSS3 color value (such as rgba(10,50,100,0.7) or #FF00FF).'),
);
$form['jlib_textColor'] = array(
'#jb_fieldset' => 'juicebox_config',
'#type' => 'textfield',
'#title' => t('Text Color'),
'#description' => t('Set the color of all gallery text as a CSS3 color value (such as rgba(255,255,255,1) or #FF00FF).'),
);
$form['jlib_thumbFrameColor'] = array(
'#jb_fieldset' => 'juicebox_config',
'#type' => 'textfield',
'#title' => t('Thumbnail Frame Color'),
'#description' => t('Set the color of the thumbnail frame as a CSS3 color value (such as rgba(255,255,255,.5) or #FF00FF).'),
);
$form['jlib_showOpenButton'] = array(
'#jb_fieldset' => 'juicebox_config',
'#type' => 'checkbox',
'#title' => t('Show Open Image Button'),
'#description' => t('Whether to show the "Open Image" button. This will link to the full size version of the image within a new tab to facilitate downloading.'),
);
$form['jlib_showExpandButton'] = array(
'#jb_fieldset' => 'juicebox_config',
'#type' => 'checkbox',
'#title' => t('Show Expand Button'),
'#description' => t('Whether to show the "Expand" button. Clicking this button expands the gallery to fill the browser window.'),
);
$form['jlib_useFullscreenExpand'] = array(
'#jb_fieldset' => 'juicebox_config',
'#type' => 'checkbox',
'#title' => t('Use Fullscreen Expand'),
'#description' => t('Whether to trigger fullscreen mode when clicking the expand button (for supported browsers).'),
);
$form['jlib_showThumbsButton'] = array(
'#jb_fieldset' => 'juicebox_config',
'#type' => 'checkbox',
'#title' => t('Show Thumbs Button'),
'#description' => t('Whether to show the "Toggle Thumbnails" button.'),
);
$form['jlib_useThumbDots'] = array(
'#jb_fieldset' => 'juicebox_config',
'#type' => 'checkbox',
'#title' => t('Show Thumbs Dots'),
'#description' => t('Whether to replace the thumbnail images with small dots.'),
'#default_value' => $settings['jlib_useThumbDots'],
);
$form['juicebox_manual_config'] = array(
'#type' => 'fieldset',
'#title' => t('Juicebox Library - Pro / Manual Config'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#description' => '<p>' . t('Specify any additional Juicebox library configuration options (such as "Pro" options) here.<br/>Options set here always take precedence over those set in the "Lite" options above if there is a conflict.') . '</p>',
'#weight' => 20,
);
$form['manual_config'] = array(
'#jb_fieldset' => 'juicebox_manual_config',
'#type' => 'textarea',
'#title' => t('Pro / Manual Configuraton Options'),
'#description' => t('Add one option per line in the format <strong>optionName="optionValue"</strong><br/>See also: http://www.juicebox.net/support/config_options'),
'#element_validate' => array(
'_juicebox_element_validate_config',
),
);
$form['advanced'] = array(
'#type' => 'fieldset',
'#title' => t('Juicebox - Advanced Options'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => 30,
);
$form['apply_markup_filter'] = array(
'#jb_fieldset' => 'advanced',
'#type' => 'checkbox',
'#title' => t('Filter title and caption output for compatibility with Juicebox javascript (recommended)'),
'#description' => t('This option helps ensure title/caption output is syntactically compatible with the Juicebox javascript library by removing block-level tags.'),
);
$form['linkurl_source'] = array(
'#jb_fieldset' => 'advanced',
'#type' => 'select',
'#title' => t("LinkURL Source"),
'#description' => t('The linkURL is an image-specific path for accessing each image outside the gallery. This is used by features such as the "Open Image Button".'),
'#options' => array(
'image_styled' => 'Main Image - Styled (use this gallery\'s main image style setting)',
),
'#empty_option' => t('Main Image - Unstyled (original image)'),
);
$form['linkurl_target'] = array(
'#jb_fieldset' => 'advanced',
'#type' => 'select',
'#title' => t('LinkURL Target'),
'#options' => array(
'_blank' => '_blank',
'_self' => '_self',
'_parent' => '_parent',
'_top' => '_top',
),
'#description' => t('Specify a target for any links that make user of the image linkURL.'),
);
$form['custom_parent_classes'] = array(
'#jb_fieldset' => 'advanced',
'#type' => 'textfield',
'#title' => t('Custom Classes for Parent Container'),
'#description' => t('Define any custom classes that should be added to the parent container within the Juicebox embed markup.<br/>This can be handy if you want to apply more advanced styling or dimensioning rules to this gallery via CSS. Enter as space-separated values.'),
);
// Set values that are directly related to each key.
foreach ($form as $conf_key => &$conf_value) {
if (!empty($conf_value['#type']) && $conf_value['#type'] != 'fieldset') {
$conf_value['#default_value'] = $settings[$conf_key];
if (in_array($conf_key, $disallowed_conf)) {
$conf_value['#title'] .= $notification_label;
$conf_value['#disabled'] = TRUE;
}
}
}
$form['#pre_render'] = array(
'_juicebox_form_pre_render_add_fieldset_markup',
);
return $form;
}
/**
* Form pre-render callback: visually render fieldsets without affecting
* tree-based variable storage.
*
* This technique/code is taken almost directly from the Views module in
* views_ui_pre_render_add_fieldset_markup()
*
* @see _juicebox_common_form_elements()
*/
function _juicebox_form_pre_render_add_fieldset_markup($form) {
foreach (element_children($form) as $key) {
$element = $form[$key];
// In our form builder functions, we added an arbitrary #jb_fieldset
// property to any element that belongs in a fieldset. If this form element
// has that property, move it into its fieldset.
if (isset($element['#jb_fieldset']) && isset($form[$element['#jb_fieldset']])) {
$form[$element['#jb_fieldset']][$key] = $element;
// Remove the original element this duplicates.
unset($form[$key]);
}
}
return $form;
}
/**
* Form validation callback: validate width/height inputs.
*
* @see _juicebox_common_form_elements()
*/
function _juicebox_element_validate_dimension($element, &$form_state, $form) {
if (!preg_match('/^[0-9]+?(%|px|em|in|cm|mm|ex|pt|pc)$/u', $element['#value'])) {
form_error($element, t('Please ensure that you width and height values are entered in a standard numeric format (such as <strong>100%</strong> or <strong>300px</strong>).'));
}
}
/**
* Form validation callback: validate Juicebox configuration options.
*
* @see _juicebox_common_form_elements()
*/
function _juicebox_element_validate_config($element, &$form_state, $form) {
// We are looking for input in the format of: optionName="optionValue".
// The check here is not too strict, it is just meant to catch general
// formatting issues.
$custom_options = explode("\n", $element['#value']);
foreach ($custom_options as $key => $option) {
$option = trim($option);
$line_number = $key + 1;
if (!empty($option) && !preg_match('/^[A-Za-z0-9]+?="[^"]+?"$/u', $option)) {
form_error($element, t('One of your manual configuration options appears to be formatted incorrectly. Please check line @line of this field and ensure that you are using the format <strong>optionName="optionValue"</strong> and that all spaces have been removed.', array(
'@line' => $line_number,
)));
}
}
}
/**
* Filter markup for valid display in a Juicebox gallery.
*
* Some markup that validates fine via common Drupal text format filters will
* not be syntactically valid once rendered within Juicebox. This is because
* Juicebox will wrap titles and captions in block-level tags, like <p>, making
* any block-level elements they contain invalid. This filter accommodates for
* this and is meant to be applied AFTER any Drupal text formats.
*
* @param string $markup
* The markup to be filtered after it has been processed by Drupal's text
* format rules.
* @return string
* Valid filtered markup ready for display in a Juicebox gallery.
*/
function _juicebox_filter_markup($markup) {
// Set inline elements that are safe in a Juicebox gallery. References:
// http://www.htmlhelp.com/reference/html40/inline.html
// https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/HTML5_element_list
$valid_elements = "<a><abbr><acronym><b><basefont><bdi><bdo><big><br><cite><code><data><del><dfn><em><font><i><img><ins><kbd><label><mark><q><rp><rt><ruby><s><samp><small><span><strike><strong><sub><sup><time><tt><u><var><wbr>";
$markup = strip_tags($markup, $valid_elements);
// Also remove newlines to keep the output concise.
$markup = str_replace(array(
"\r",
"\n",
), '', $markup);
return $markup;
}
/**
* Implements hook_field_formatter_info().
*
* Add juicebox_formatter formatter.
*/
function juicebox_field_formatter_info() {
$formatters = array(
'juicebox_formatter' => array(
'label' => t('Juicebox Gallery'),
'field types' => array(
'image',
),
'settings' => array(
'image_style' => '',
'thumb_style' => 'thumbnail',
'caption_source' => 'alt',
'title_source' => 'title',
'jlib_galleryWidth' => '100%',
'jlib_galleryHeight' => '100%',
'jlib_backgroundColor' => '#222222',
'jlib_textColor' => 'rgba(255,255,255,1)',
'jlib_thumbFrameColor' => 'rgba(255,255,255,.5)',
'jlib_showOpenButton' => 1,
'jlib_showExpandButton' => 1,
'jlib_showThumbsButton' => 1,
'jlib_useThumbDots' => 0,
'jlib_useFullscreenExpand' => 0,
'manual_config' => '',
'custom_parent_classes' => '',
'apply_markup_filter' => 1,
'linkurl_source' => '',
'linkurl_target' => '_blank',
),
),
);
return $formatters;
}
/**
* Implements hook_field_formatter_settings_form().
*/
function juicebox_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
if (!empty($instance['bundle']) && $instance['bundle'] == 'ctools') {
return array(
'juicebox_formatter_notice' => array(
'#prefix' => '<p>',
'#markup' => t('<strong>NOTICE: The Juicebox field formatter is not fully supported within individual view rows (and other ctools contexts)</strong>. Using this setting is not recommended. For views support please use the Juicebox display style plugin.'),
'#suffix' => '</p>',
),
);
}
$form = array();
// Get available image style presets
$presets = image_style_options(FALSE);
// Initialize the "settings" values before working with them. This is
// required for legacy support.
$settings = _juicebox_init_display_settings($instance['display'][$view_mode]['settings']);
$form = _juicebox_common_form_elements($form, $settings);
// Add the field-formatter-specific elements.
$form['image_style'] = array(
'#type' => 'select',
'#title' => t('Main Image Style'),
'#default_value' => $settings['image_style'],
'#description' => t('The style formatter for the main image.'),
'#options' => $presets,
'#empty_option' => t('None (original image)'),
);
$form['thumb_style'] = array(
'#type' => 'select',
'#title' => t('Thumbnail Style'),
'#default_value' => $settings['thumb_style'],
'#description' => t('The style formatter for the thumbnail.'),
'#options' => $presets,
'#empty_option' => t('None (original image)'),
);
$form['caption_source'] = array(
'#type' => 'select',
'#title' => t('Caption Source'),
'#default_value' => $settings['caption_source'],
'#description' => t('The image field value that should be used for the caption. This value will be processed with your fallback text format.'),
'#options' => array(
'alt' => t('Alternate text'),
'title' => t('Title'),
),
'#empty_option' => t('No caption'),
);
$form['title_source'] = array(
'#type' => 'select',
'#title' => t('Title Source'),
'#default_value' => $settings['title_source'],
'#description' => t('The image field value that should be used for the title. This value will be processed with your fallback text format.'),
'#options' => array(
'alt' => t('Alternate text'),
'title' => t('Title'),
),
'#empty_option' => t('No title'),
);
return $form;
}
/**
* Implements hook_field_formatter_view().
*
* This is where the Juicebox embed code is built for the field formatter.
*/
function juicebox_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
// If there are no images, don't do anything else.
if (empty($items)) {
return;
}
// The gallery shown in preview view will only display field data from the
// previously saved version (that is the only version the XML generation
// methods will have access to). Display a warning because of this.
if (!empty($entity->in_preview)) {
drupal_set_message(t('Juicebox galleries may not display correctly in preview mode. Any edits made to gallery data will only be visible after all changes are saved.'), 'warning', FALSE);
}
$field_name = $instance['field_name'];
$entity_type_info = entity_get_info($entity_type);
$entity_id = $entity->{$entity_type_info['entity keys']['id']};
// Initialize the "settings" values before working with them. This is
// required for legacy support.
$settings = _juicebox_init_display_settings($display['settings']);
// Load the juicebox javascript.
libraries_load('juicebox');
// We need to get the display name to pass as part of our XML path. Though
// we have access to the actaul $display array, it does not look like we
// have access to the actaul display NAME in this scope. We do have access to
// a list of ALL displays in $instanace though, so iterate though them to
// find a match to the settings in $display. This seems SUPER CLUNKY, but
// might be the only way.
$display_name = 'default';
foreach ($instance['display'] as $display_key => $display_data) {
if ($display['settings'] == $display_data['settings']) {
$display_name = $display_key;
}
}
// Generate a unique ID that can be used to identify this gallery and field
// source details.
$xml_id = 'entity/' . $entity_type . '/' . $entity_id . '/' . $field_name . '/' . $display_name;
$xml_path = 'juicebox/xml/' . $xml_id;
// Calculate the data that will ultimately be used to render the gallery XML.
// We won't officially generate the XML until the Juicebox javascript requests
// it, but we will still need the related data within parts of the embed code.
$gallery_data = juicebox_build_gallery_data_from_entity_field($items, $settings, $entity, $xml_path);
// Get a checksum for the gallery data. This can be useful for invalidating
// any old caches of this data. Note json_encode() is faster than serialize().
$gallery_checksum = md5(json_encode($gallery_data));
// Construct the query parameters that should be added to the XML path. Be
// sure to retain any currently active query parameters.
$query = array_merge(array(
'checksum' => $gallery_checksum,
), drupal_get_query_parameters());
// Build the render array for the embed markup.
$element[0] = array(
'#theme' => 'juicebox_embed_markup',
'#gallery_id' => preg_replace('/[^0-9a-zA-Z-]/', '_', str_replace('/', '-', $xml_id)),
'#gallery_xml_path' => url($xml_path, array(
'query' => $query,
)),
'#settings' => $settings,
'#data' => $gallery_data,
);
return $element;
}
/**
* Implements hook_field_formatter_settings_summary().
*/
function juicebox_field_formatter_settings_summary($field, $instance, $view_mode) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$settings_display = array();
// Image style setting.
if (!empty($settings['image_style'])) {
$style = $settings['image_style'];
}
else {
$style = t('Original Image');
}
$settings_display[] = t("Image style: @style", array(
'@style' => $style,
));
// Thumb style setting.
if (!empty($settings['thumb_style'])) {
$style = $settings['thumb_style'];
}
else {
$style = t('Original Image');
}
$settings_display[] = t("Thumbnail style: @style", array(
'@style' => $style,
));
// Define display options for caption and title source.
$options = array(
'alt' => t('Alternate text'),
'title' => t('Title'),
);
// Caption source setting.
if (!empty($settings['caption_source'])) {
$source = $options[$settings['caption_source']];
}
else {
$source = t('None');
}
$settings_display[] = t("Caption source: @source", array(
'@source' => $source,
));
// Title source setting.
if (!empty($settings['title_source'])) {
$source = $options[$settings['title_source']];
}
else {
$source = t('None');
}
$settings_display[] = t("Title source: @source", array(
'@source' => $source,
));
// Add-in a note about the additional fieldsets.
$settings_display[] = t("Additional Juicebox library configuration options may also be set.");
$summary = implode('<br />', $settings_display);
return $summary;
}
Functions
Name | Description |
---|---|
juicebox_build_gallery_data_from_entity_field | Generate data for a Juicebox gallery from an entity image field. |
juicebox_build_gallery_data_from_view | Generate data for a Juicebox gallery from a view object. |
juicebox_field_formatter_info | Implements hook_field_formatter_info(). |
juicebox_field_formatter_settings_form | Implements hook_field_formatter_settings_form(). |
juicebox_field_formatter_settings_summary | Implements hook_field_formatter_settings_summary(). |
juicebox_field_formatter_view | Implements hook_field_formatter_view(). |
juicebox_libraries_info | Implements hook_libraries_info(). |
juicebox_menu | Implements hook_menu(). |
juicebox_page_xml | Menu callback: generate Juicebox XML. |
juicebox_render_gallery_xml | Render the final XML for a Juicebox gallery. |
juicebox_theme | Implements hook_theme(). |
juicebox_views_api | Implements hook_views_api(). |
_juicebox_check_entity_view_access | Check view access for an arbitrary entity that contains a Juicebox gallery. |
_juicebox_common_form_elements | Helper to add common elements to Juicebox configuration forms. |
_juicebox_element_validate_config | Form validation callback: validate Juicebox configuration options. |
_juicebox_element_validate_dimension | Form validation callback: validate width/height inputs. |
_juicebox_filter_markup | Filter markup for valid display in a Juicebox gallery. |
_juicebox_form_pre_render_add_fieldset_markup | Form pre-render callback: visually render fieldsets without affecting tree-based variable storage. |
_juicebox_get_lib_options | Helper to get all Juicebox library conf options from the display settings. |
_juicebox_init_display_settings | Utility function to modify Juicebox settings after they are loaded from Drupal but before they are used in any Juicebox module logic. |
_juicebox_library_detect | Get/detect the details of a Juicebox javascript library without loading it. |
_juicebox_library_info | Libraries API Info Callback |
_juicebox_library_post_detect | Libraries API Post-Detect Callback |