' . t('You are missing some Flash content that should appear here! Perhaps your browser cannot display it, or maybe it did not initialize correctly.') . '
');
define('SWFTOOLS_GRANT_ACCESS_TO_PRIVATE_FILES', FALSE);
define('SWFTOOLS_GRANT_ACCESS_EXTENSIONS', 'swf flv xml mp3 jpg jpeg png');
define('SWFTOOLS_ALWAYS_ADD_JS', TRUE);
define('SWFTOOLS_LIBRARIES', 'sites/all/libraries/');
/**
* Used by swftools_profiles_variable_get() to indicate that the requested item is not defined for the specified profile.
*/
define('SWFTOOLS_PROFILE_UNDEFINED', '__undefined');
/**
* Used by functions request player settings to notify that they only need settings that are non-default.
* For example, _swftools_wijering4_settings().
*/
define('SWFTOOLS_STORED_SETTINGS', 0x0001);
/**
* Used by functions request player settings to notify that they want the full set, including blanks and defaults.
*/
define('SWFTOOLS_FULL_SETTINGS', 0x0002);
/**
* Implementation of hook_init().
*/
function swftools_init() {
// Add JavaScript that allows other scripts to access movies from the DOM
drupal_add_js(drupal_get_path('module', 'swftools') . '/swftools.js');
// Add CSS to enable hiding of accessible controls
drupal_add_css(drupal_get_path('module', 'swftools') . '/swftools.css');
}
/**
* Implementation of hook_menu().
*/
function swftools_menu() {
// Reset methods cache
$methods = swftools_methods_available('', TRUE);
// Initialise array
$items = array();
// Should this be administer swf tools?
$swf_admin = array('administer flash');
$items['admin/settings/swftools'] = array(
'title' => 'SWF Tools',
'description' => 'Settings to control how SWF Tools integrates with Adobe Flash related methods and tools like video players, MP3 players and image viewers.',
'access arguments' => $swf_admin,
'page callback' => 'system_admin_menu_block_page',
'file' => 'system.admin.inc',
'file path' => drupal_get_path('module', 'system'),
);
$items['admin/settings/swftools/handling'] = array(
'title' => 'File handling',
'description' => 'Configure how SWF Tools should handle different types of file.',
'access arguments' => $swf_admin,
'weight' => -2,
'page callback' => 'drupal_get_form',
'page arguments' => array('swftools_admin_handling_form'),
'file' => 'swftools.admin.inc',
);
$items['swftools/playlist/%'] = array(
'title' => 'SWF Tools playlist',
'page callback' => 'swftools_serve_xml_playlist',
'page arguments' => array(2),
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['admin/settings/swftools/embed'] = array(
'title' => 'Embedding settings',
'description' => 'Set the embedding method that SWF Tools should use, and configure embedding defaults.',
'access arguments' => $swf_admin,
'weight' => -2,
'page callback' => 'drupal_get_form',
'page arguments' => array('swftools_admin_embed_form'),
'file' => 'swftools.admin.inc',
);
// If CCK is active then add a link to the CCK formatters
if (module_exists('content')) {
$items['admin/settings/swftools/cck'] = array(
'title' => 'CCK formatters',
'description' => 'Additional settings to control how SWF Tools should interact with CCK.',
'access arguments' => $swf_admin,
'page callback' => 'drupal_get_form',
'page arguments' => array('swftools_admin_cck_form'),
'file' => 'swftools.admin.inc',
);
}
// Add SWF Tools status report
$items['admin/reports/swftools'] = array(
'title' => 'SWF Tools status',
'description' => 'Get an overview of the status of the SWF Tools module and its supporting files.',
'page callback' => 'swftools_status',
'access arguments' => $swf_admin,
'file' => 'swftools.admin.status.inc',
'weight' => 9,
);
// Integrate items from the generic players module
$items = array_merge($items, swftools_genericplayers_menu());
// Return array of menu items
return $items;
}
/**
* Implementation of hook_perm().
*/
function swftools_perm() {
return array(
'administer flash',
);
}
/**
* Take an array of play list data and options, and return a markup string.
*
* @param $playlist_data
* A formatted array of data to be used to create the playlist. An appropriate
* array can be created from an array of filenames by calling swftools_prepare_playlist_data.
* @param $options
* An array of options to pass to the call to swf().
* @return
* A string of markup to produce the playlist in a flash media player.
*/
function swf_list($playlist_data, $options = array()) {
// Populate methods and othervars with playlist data
if (is_array($playlist_data)) {
// If action isn't set then set it
if (empty($options['methods']['action']) && isset($playlist_data['action'])) {
$options['methods']['action'] = $playlist_data['action'];
}
// If playlist_data isn't set then set it
if (empty($options['othervars']['playlist_data'])) {
$options['othervars']['playlist_data'] = $playlist_data;
}
// If playlist filename is set
if (isset($playlist_data['filename'])) {
$playlist = $playlist_data['filename'];
}
else {
$playlist = '';
}
// Produce markup
return swf($playlist, $options);
}
}
/**
* Return output, which might be embed markup, or pre-flash markup
* that includes the appropriate jQuery added to the
*
* @param $file
* The file to be played. If it is a SWF file it will usually be embedded directly.
* Use a full URL, a path relative to webroot, or a path relative to the configured files directory.
* If an array is passed then the array will be converted to a playlist automatically.
* If the file string is a complete url then SWF Tools will pass it along unaltered. If the string
* is a partial path then it will either be resolved to the local file system, or to a remote host,
* depending whether the swftools_media_url variable is set.
* @param $options=>$params
* An associative array of variables to set.eg. array('bgcolor'=>'FF00FF')
* To set height and width: array('width'=>'200', 'height'=>'120'). However,
* as a convenient alternative for the common requirement of just height and width
* you can also pass a text string like '200x100'.
* If you pass nothing, and the file to play is a .swf, swftools will try and
* establish a natural width and height from the actual .swf file that you've
* passed into $file.
* @param $options=>$flashvars
* An associative array of flashvar variables to set. eg. array('playlist'=>'files/my_playlist.xml')
* @param $options=>$othervars
* An associative array of variables that might be required by the $player or $embed technique.
* These values are not output as params or flashvars.
* @param $options=>$methods
* Explicitly declare an action, player or action by passing an array of
* the form: array('action'=>'dosomething','player'=>'withsomething','embed'=>'withthisjavascript').
* These usually default to the administration settings and also you will
* usually use a CONSTANT which will be documented further at a later stage.
*/
function swf($file, $options = array()) {
// $time_start = microtime(true);
// Initialise any $options array elements that weren't passed by the caller
$options += array(
'params' => array(),
'flashvars' => array(),
'othervars' => array(),
'methods' => array(),
);
// TODO: We should put SWF Tools own settings somewhere safe in the othervars array
// If someone passes a variable from the input filter it could collide.
// Maybe put them under #swftools (since the data are properties of swftools?)
// A finished item has player, profile, cid, id, file_url, src_path, src
// Initialise an empty profile is none was passed
$options['othervars'] += array(
'profile' => '',
);
/**
* Try to use SWF Tools cache.
* If the cid is already set then we are on a second pass through swf() because we are
* generating a playlist. The first call to swf() calls swf_list() which in turn calls
* swf() again. In this instance we don't want to hash the second time round. We want
* to cache the output against the cid of the FIRST call so we can pick up the content
* on subsequent calls. So we skip attempts at hashing when cid is present, we let the
* content be processed, and then we cache at the very end. Now on subsequent calls
* we get the "playlist" content immediately.
*/
if (!isset($options['othervars']['cid']) && ($ret = swftools_fetch_from_cache($file, $options))) {
// $time_end = microtime(true);
// $time = $time_end - $time_start;
// dsm('Used cache in ' . $time);
return $ret;
}
// Ensure id is unique, or assign an id if one isn't set
$options = swftools_assign_id($options);
// If swf() was called with an array of files, make a playlist and call swf_list() for processing
if (is_array($file)) {
// Turn the array in to a playlist
$playlist_data = swftools_prepare_playlist_data($file, '', isset($options['methods']['action']) ? FALSE : TRUE);
// Call swf_list to process the playlist and create the markup
$ret = swf_list($playlist_data, $options);
// $time_end = microtime(true);
// dsm('Generated playlist in '. ($time_end - $time_start));
return $ret;
}
// Get all the actions, tools and embedding options available to us
$all_methods = swftools_methods_available();
// ACTION
// Work out what SWF Tools should do with this file
// Was an explicit action set in $options['methods']['action']
$action = (isset($options['methods']['action'])) ? $options['methods']['action'] : FALSE;
// If an explicit action wasn't set then try to determine an appropriate one using the filename
if (!$action) {
$action = swftools_get_action($file);
}
// HTML ALTERNATIVE
// If an explicit value wasn't set in $options['othervars']['html_alt'] use a default
$html_alt = (isset($options['othervars']['html_alt'])) ? $options['othervars']['html_alt'] : variable_get('swftools_html_alt', SWFTOOLS_DEFAULT_HTML_ALT);
// RESOLVE PLAYER AND EMBEDDING
// 'resolved' refers to the fact that these are the methods we now intend to use, not /all/ methods available.
$resolved_methods = new stdClass();
// PLAYER
// Work out what player SWF Tools will need to use for this action
// Was an explicit player set in $options['methods']['player']
$player = (isset($options['methods']['player'])) ? $options['methods']['player'] : FALSE;
// If an explicit player wasn't set then find out what player is configured for the current action
if (!$player) {
// Find out what player is assigned to handle the current action
$player = swftools_get_player($action, $options['othervars']['profile']);
// If still no player assignment then we don't know what to do with this action
if (!$player) {
// Build an array of descriptions for each possible action
$descriptions = array(
SWFTOOLS_IMAGE_DISPLAY_LIST=> 'a series of images',
SWFTOOLS_FLV_DISPLAY=> 'a single flv file',
SWFTOOLS_FLV_DISPLAY_LIST=> 'a series of flv files',
SWFTOOLS_MP3_DISPLAY=> 'a single mp3 file',
SWFTOOLS_MP3_DISPLAY_LIST=> 'a series of mp3 files',
SWFTOOLS_MEDIA_DISPLAY_LIST=> 'a series mixed media files',
);
// If we have a matching description for the specified action, create a meaningful message
if (isset($descriptions[$action])) {
drupal_set_message(t('No player is configured to play ' . $descriptions[$action] . '. Check the SWF Tools file handling settings on the configuration page.'), 'error');
}
// Otherwise we have an action that SWF Tools doesn't understand, so create a fallback message
else {
drupal_set_message(t('No player is configured for the action %action. Check the SWF Tools file handling settings on the configuration page.', array('%action' => $action)), 'error');
}
// We couldn't find a player for this content, so fallback to the alternate markup and return
return $html_alt;
}
}
// Check that the action / player combination is valid (it should appear in the array of all methods)
if (isset($all_methods[$action][$player])) {
// If the combination was found, place player information in to $resolved_methods
$resolved_methods->player = $all_methods[$action][$player];
}
// If the action / player combination doesn't appear then we need to do something else
else {
// If the action is display an swf directly then assume we have a custom player
if ($action == SWFTOOLS_SWF_DISPLAY_DIRECT) {
// Assign SWFTOOLS_CUSTOM data in to $resolved_methods
$resolved_methods->player = $all_methods[$action][SWFTOOLS_CUSTOM];
$resolved_methods->player['library'] = $player;
}
// We couldn't find a player for this content, so fallback to the alternate markup and return
else {
drupal_set_message(t('The combination %player and %action is not valid. Check that the player is available and that it supports the requested action.', array('%player' => $player, '%action' => $action)), 'error', FALSE);
return $html_alt;
}
}
// EMBED
// Work out what embedding method SWF Tools should use for this content
// Was an explicit embedding method set in $options['methods']['embed']
$embed = (isset($options['methods']['embed'])) ? $options['methods']['embed'] : FALSE;
// If an explicit embedding method wasn't set then find get the current default
if (!$embed) {
$embed = variable_get(SWFTOOLS_EMBED_METHOD, SWFTOOLS_NOJAVASCRIPT);
}
// Place the embedding method information in to $resolved_methods
$resolved_methods->embed = $all_methods[SWFTOOLS_EMBED_METHOD][$embed];
// VARIABLES AND PARAMETERS
// Put all the variables on a simple object to make internal function calls simpler
$vars = new stdClass();
// OTHERVARS
// If $options['othervars'] were supplied, add to $vars->othervars
$vars->othervars = (is_array($options['othervars'])) ? $options['othervars'] : array();
// PARAMS
// $options['params'] could be an associative array, or in 'WIDTHxHEIGHT' format.
// If $options were passed to the swf() function then process them
if ($options['params']) {
// If $options['params'] is an array then just add it to $vars
if (is_array($options['params'])) {
$vars->params = $options['params'];
}
// Otherwise see if we can explode the string in to a height and width
else {
$dimensions = explode('x', $options['params']);
if (count($dimensions) == 2) {
$vars->params = array(
'width' => $dimensions[0],
'height' => $dimensions[1],
);
}
}
}
// FLASHVARS
// Flashvars could be passed as an associative array, or as a string in 'a=1&b=2' format
// If the flashvars have been passed as an array simply add to $varsa
if (is_array($options['flashvars'])) {
$vars->flashvars = $options['flashvars'];
}
// Otherwise parse the flashvars string in to an array
else {
// Parse the string as if in 'a=1&b=2' format.
parse_str($options['flashvars'], $vars->flashvars);
}
// BASE
// Determine a reasonable 'base' directory, if a remote url is set, use that
// If file is local, set to the file directory
// Was an explicit base path set in $options['params']['base']
$base = (!empty($vars->params['base'])) ? $vars->params['base'] : '';
// If the base path isn't set, or the path is not valid try to find a reasonable alternative
if (!$base || !valid_url($base)) {
// Retrieve an appropriate base path
$base = swftools_get_base();
}
// Assign the resulting base path in to $vars->params['base']
$vars->params['base'] = $base;
// XML PLAYLIST
// Determine if we trying to generate a playlist that needs xml output, and create as required
// If $options['othervars']['playlist_data'] is set then we are processing a playlist
if (isset($options['othervars']['playlist_data'])) {
// Determine the name of the hook that would be used to generate an xml playlist
$hook = $resolved_methods->player['name'] . '_swftools_playlist';
// Check that the hook exists - if it doesn't the module doesn't need to create xml output
if (module_hook($resolved_methods->player['module'], $hook)) {
// Generate a playlist in the files directory
$file = swftools_generate_playlist($options['othervars']['playlist_data'], '', $resolved_methods, $vars);
// If a file wasn't generated by swftools_generate_playlist then set an error and return alternate markup
if (!$file) {
drupal_set_message(t('Unable to create xml playlist.'), 'error');
return $html_alt;
}
}
}
// CACHING
// To try and prevent the xml files from being cached append the time to the filename to try and force it to reload
if (variable_get('swftools_playlist_caching', 'here') == 'always') {
$nocache = '?nc='. time();
}
else {
$nocache = '';
}
// FILE
// Make sure that the file path in $file is valid, and as necessary try to
// expand it to a relative url on the local file system, or point to the remote media directory
// First we assume that we can just set $file_url to the value in $file
$file_url = $file;
// But if we are not streaming this file then we might need to process it to get a proper path
if (!isset($vars->othervars['stream'])) {
// Process to get a url (in src) and expand the path (in src_path) if necessary
$source = swftools_src($file);
// NOTE : If $file was an empty string then you get src = sites/default/files/site/default/files
// If FALSE was returned then the file doesn't exist so return $html_alt
if (!$source) {
return $html_alt;
}
// $file might have been changed to reflect a path on the local file system
// This happens when the user just supplied a filename and files are being sourced locally
// Put $file = $source['src_path'] in case that happened
$file = $source['src_path'];
// In all cases $file_url is now an absolute, or relative, url to the file that we can use
$file_url = $source['src'];
}
// Attach file_url to othervars so player modules have access if required
$vars->othervars['file_url'] = $file_url;
// We used to attach things to $vars->params, but then we have to unset them later
// So they've been moved to $vars->othervars['src'] and $vars->othervars['src_path']
switch ($player) {
// Embedding an swf directly - no player
case SWFTOOLS_SWF:
$vars->othervars['src_path'] = $file;
$vars->othervars['src'] = $file_url;
break;
// Embedding with a custom player - when would this occur?
// TODO: case SWFTOOLS_CUSTOM: and default: look the same?
case SWFTOOLS_CUSTOM:
$vars->othervars['src_path'] = $resolved_methods->player['library'];
$vars->othervars['src'] = base_path() . $vars->othervars['src_path'];
break;
// Embedding with a standard player
default:
if ($resolved_methods->player['library']) {
$vars->othervars['src_path'] = $resolved_methods->player['library'];
$vars->othervars['src'] = base_path() . $vars->othervars['src_path'];
}
}
// Merge default and user defined parameters, with user defined ones taking precedence
$vars->params = array_merge(_swftools_params(), $vars->params);
// Ask the module implementing the player what flashvars are required, pass
// all existing values by reference to allow optional override at the player.module level.
if (module_hook($resolved_methods->player['module'], 'swftools_flashvars')) {
// Build the name of the function we are going to call
$function = $resolved_methods->player['module'] . '_swftools_flashvars';
// Call the function and return the resulting flashvars to the caller
$player_flashvars = $function($action, $resolved_methods, $vars, $options['othervars']['profile']);
// Merge player flashvars with existing flashvars
if (is_array($player_flashvars)) {
$vars->flashvars = array_merge($vars->flashvars, $player_flashvars);
}
}
// If $resolved_methods->player['file'] is set we should assign $file_url to a flashvar with that name
if (!empty($resolved_methods->player['file'])) {
$vars->flashvars[$resolved_methods->player['file']] = $vars->othervars['file_url'];
}
// If the player requires a specific minimum flash version then assign it to the params
if (isset($resolved_methods->player['version'])) {
$vars->params['version'] = $resolved_methods->player['version'];
}
// Call function to set the size of the content
swftools_set_size($vars, $resolved_methods->player);
// We used to set height and width on $vars->params, but they're not actually params and they were
// being unset in individual embedding functions. So we'll move them to $vars->othervars.
// We will continue to let users pass the data on $vars->othervars so as not to break existing code.
$vars->othervars['height'] = $vars->params['height'];
$vars->othervars['width'] = $vars->params['width'];
unset($vars->params['height'], $vars->params['width']);
// See if anyone wants to alter $vars just before it is output
// Note - because $resolved_methods is an object this is also passed by reference in PHP5
drupal_alter('swftools', $vars, $file, $action, $resolved_methods);
// Call the embedding code to get the HTML and set the JavaScript if necessary
// TODO: Why module_invoke(), why not just make this a theme function too?
$embed_markup = module_invoke($resolved_methods->embed['module'], 'swftools_embed', $action, $resolved_methods, $vars, $html_alt);
// Call theme function to return completed markup, e.g. add containing div
$ret = theme('swftools_embed', $embed_markup, $action, $resolved_methods, $vars, $html_alt);
// Cache the result
cache_set('swf:'.$options['othervars']['cid'], $ret, 'cache_swftools');
// Return the markup
return $ret;
}
/**
* Produce finished markup ready for inserting on the page
*
* @param $embed_markup
* The markup needed to add the swf content to the page
* @param $action
* The action that is being used, in case the themer wants it
* @param $methods
* The player and embedding methods being used, in case the themer wants it
* @param $vars
* The array of othervars, params and flashvars in case the themer wants it
* @param $html_alt
* The alternate HTML content, in case the themer wants it
* @return
* An HTML string that generates the output
*/
function theme_swftools_embed($embed_markup, $action, $methods, $vars, $html_alt) {
// Prepare an array of classes to include in the wrapper div
$classes[] = 'swftools-wrapper';
$classes[] = str_replace('_', '-', $methods->player['name']);
// If the user provided class data already then don't over-rule it
if (!empty($vars->othervars['class'])) {
$classes[] = $vars->othervars['class'];
}
// Allow for handling of #prefix and #suffix supplied via $vars->othervars
$vars->othervars += array(
'#prefix' => '',
'#suffix' => '',
);
// Wrap $embed_markup with prefix and suffix (e.g. to add accessible controls)
$embed_markup = $vars->othervars['#prefix'] . $embed_markup . $vars->othervars['#suffix'];
// Return completed markup
return '
' . $embed_markup . '
';
}
/**
* Collect information from all modules about the players and embedding methods available.
*
* @param $action
* Optional parameter to retrieve data only about a specific method.
* @param $reset
* Optional parameter which if TRUE will reset the method cache and rebuild it.
* @return
* An array of data describing the available methods.
*/
function swftools_methods_available($action = NULL, $reset = FALSE) {
// Cache methods array as it may be called several times
static $methods;
// If user has requested the cache to be reset then reset it
if ($reset || !isset($methods)) {
if (!$reset && ($cached = cache_get('methods', 'cache_swftools'))) {
$methods = $cached->data;
}
else {
$methods = module_invoke_all('swftools_methods');
cache_set('methods', $methods, 'cache_swftools');
}
}
// In case no module is presenting a method for the required action the following line avoids a notice error
if ($action) {
$methods += array(
$action => NULL,
);
}
// Return results - either for the specific action, or the whole array
return $action ? $methods[$action] : $methods;
}
/**
* Converts an array of paramters to JSON and returns them as a string ready for use as a flashvar.
*
* @param $params
* An array of parameters.
* @param $attribute
* The attribute that the JSON string will be attached to.
* @return
* A string in the form [attr]={JSON}
*/
function swftools_json_params(&$params, $attribute = 'swftools') {
return $attribute . "='" . drupal_to_js($params) . "'";
}
/**
* Returns 'true' or 'false' for JavaScript based the supplied value $bool.
*
* @param $bool
* The value that should be cast to true or false.
* @return
* The string 'true' or 'false' depending on the supplied value.
*/
function _swftools_tf($bool) {
// String 'false' is treated as TRUE in PHP logic, so force to FALSE
if (strtolower($bool) == 'false') {
$bool = FALSE;
}
// Return 'true' or 'false' now we are sure of result
return $bool ? 'true' : 'false';
}
/**
* Identify the most likely SWF Tools action for a file, based on its extension.
*
* @param $file
* The name of the file to be processed.
* @return
* A string describing an SWF Tools action.
*/
function swftools_get_action($file) {
// Get the path information for this file
$path_parts = pathinfo($file);
// Select an action based on the file extension
switch (strtolower($path_parts['extension'])) {
case 'flv':
return SWFTOOLS_FLV_DISPLAY;
case 'swf':
return SWFTOOLS_SWF_DISPLAY_DIRECT;
case 'mp3':
return SWFTOOLS_MP3_DISPLAY;
case 'jpg': case 'gif': case 'png': case 'jpeg': case 'img':
return SWFTOOLS_IMAGE_DISPLAY_LIST;
default:
// Assume that the configured mediaplayer will handle this file or playlist
return SWFTOOLS_MEDIA_DISPLAY_LIST;
}
}
/**
* Returns the currently configured player for the specified action and profile.
*
* We use a static array so that if we are generating a complex page we can
* quickly locate the relevant action/profile player after we've done it the
* first time. This saves us from repeat calls to swftools_variable_get().
*
* @param $action
* The SWF Tools action to be performed.
* @param $profile
* The profile being used for this item.
* @return
* The name of the currently configured player for this action.
*/
function swftools_get_player($action, $profile = '') {
// Initialise a static array for this page call
static $players = array();
// We need to give the empty profile a name to place it in the array
$_profile = $profile ? $profile : SWFTOOLS_PROFILE_UNDEFINED;
// Do we already know the players for this profile?
if (!isset($players[$_profile])) {
// Register the players for this profile in the array
$players[$_profile] = _swftools_get_handlers($profile);
}
// Return the result
return isset($players[$_profile][$action]) ? $players[$_profile][$action] : FALSE;
}
/**
* Return an array of default values to use as the swf parameters.
* Parameters are described in the Adobe knowledge base TechNote 12701
* http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_12701
*
* @return
* An array of key/value pairs
*/
function _swftools_params() {
// Cache this
static $params = array();
// If not set then get defaults
if (!$params) {
// Define default parameters for case when settings have been stored
$defaults = array(
'swliveconnect' => 'false',
'play' => 'true',
'loop' => 'true',
'menu' => 'false',
'allowfullscreen' => 'true',
'quality' => 'autohigh',
'scale' => 'showall',
'align' => 'l',
'salign' => 'tl',
'wmode' => 'opaque',
'bgcolor' => '',
'version' => '7',
'allowscriptaccess' => 'sameDomain',
);
// Retrieve settings from the database if available
$params = variable_get('swftools_params', $defaults);
}
// Return the default parameters
return $params;
}
/**
* Returns information about the specified file.
*
* We use this function to try and get the height and width for content that
* we don't have a specific size for.
* The returned value is an array that may include width, height, extension,
* file_size, mime_type. We return FALSE if we didn't get a valid file, or if
* image_get_info() couldn't tell us anything.
*
* @parameter $file
* Path to a local file that we want to interrogate.
* @return
* An array of data, or FALSE if we didn't get anything.
*/
function swftools_get_info($file) {
// Only check the file if it is local
// TODO: Is this function capable of checking a path like /drupal6/some/other/file.swf
if ($file == file_create_path($file) && $info = image_get_info($file)) {
return $info;
}
// Otherwise return FALSE to indicate that we don't know anything about the file
return FALSE;
}
/**
* Generates an xml playlist and places it in {cache_swftools} ready for use.
*
* We used to create an actual file. Now we place the content in {cache_swftools}
* and access it via swftools/playlist/nnn, where nnn is the cid.
*
* @param &$playlist_data
* A formatted array of playlist data that will be converted to an xml file. NEED TO DOCUMENT THE STRUCTURE.
* @param $playlist_name
* An optional name for the playlist. If not specified a filename will be created.
* @param $method
* An array describing the selected player method.
* @param &$vars
* Array of variables. Not used by this function, but will be passed to the playlist generator functions.
* @return
* The path to the virtual xml file (will be in the form swftools/playlist/nnn)
*/
function swftools_generate_playlist(&$playlist_data, $playlist_name, &$method, &$vars) {
// In what cases would we really want to give a playlist name?
// Lets take it out and see if anyone complains!
// We don't need to hash again. We have been given a cid via $vars->othervars['cid']
// which is based on the incoming data, so lets just use that. Then we don't have to
// hash again. We prefix cache entries with xml: when they are a playlist so the cid
// for the playlist will be xml:nnn, and for the movie will be swf:nnn. So we will
// end up with matched pairs in the database which is neat.
// Assign $vars->othervars['cid'] to a variable so things read nicely!
$playlist_name = $vars->othervars['cid'];
if ($ret = cache_get('xml:'.$playlist_name, 'cache_swftools')) {
return url('swftools/playlist/' . $playlist_name);
}
// Determine the name of the relevant hook to output the playlist in the appropriate format
$hook = $method->player['name'] . '_swftools_playlist';
// Build the name of the function we are going to call
$function = $method->player['module'] . '_' . $hook;
// Call the function and pass variables by reference
$playlist = $function($playlist_data, $method, $vars);
// Write data to cache
cache_set('xml:'.$playlist_name, $playlist, 'cache_swftools');
// Return the path to the playlist
return url('swftools/playlist/' . $playlist_name);
}
/**
* Prepares an array of filenames, or file objects, for use in a playlist.
*
* This function processes the supplied array and returns an array of playlist data which has
* two elements, header and playlist. The header array contains the playlist title, which may
* be an empty string if nothing was set. The playlist array contains an array of playlist
* elements. As a minimum each element will contain:
* - filepath : the filepath for the file, if it is a local file, or FALSE if a full url
* - title : the title for the element, set to the filename if nothing given
* - filename : the filename for the file
* - fileurl : the full url to the file
*
* drupal_alter is called just prior to returning the playlist, so a module can implement
* hook_swftools_playlist_alter(&$playlist_data) to modify the playlist prior to return. This
* means other modules can modify, or add, elements. For example, the swftools_getid3 module
* implements this hook to attach ID3 data to each playlist element.
*
* @param $files
* An array of files that will be added to the playlist.
* @param $title
* Optional playlist title
* @param $get_action
* Optional parameter indicating if the function should determine an appropriate action. Default is TRUE.
* @param $type_filter
* Optional parameter - an array of file extensions that are permitted
* @param $stream
* Option parameter to indicate if this is going to be a streamed playlist, in which case checks for the
* existence of files should be skipped
* @return unknown_type
*/
function swftools_prepare_playlist_data($files, $title = '', $get_action = TRUE, $type_filter = array(), $stream = FALSE) {
/**
* Changes coming to this function:
* Internally arrays coming in from filefield, text field and link field are
* all formatted as an array of arrays. The filter mechanism is being re-written
* to do the same. However, not sure if other modules are calling this, so for
* now keep the code to map non-arrays.
*/
// Initialise an array to return the results in
$playlist_data = array();
// Set the playlist title
$playlist_data['header']['title'] = $title;
// Standardise the array of files so subsequent functions are happy
_swftools_playlist_standardise_incoming_array($files);
// Set the $playlist_data['playlist'] to the uniform array of files
$playlist_data['playlist'] = $files;
// Iterate over the array of files to ensure all default keys are set to something meaningful
foreach ($files AS $key => $data) {
// TODO: tidy this up as I'm sure we're repeating in here
$playlist_data['playlist'][$key] += array(
'filename' => '',
'filepath' => '',
'fileurl' => '',
'title' => '',
);
// If the fileurl element isn't set then build it
if (!$data['fileurl']) {
// If the filepath element is a valid url then use it
if (valid_url($data['filepath'], TRUE)) {
// We don't know the filepath, so set to FALSE
$playlist_data['playlist'][$key]['filepath'] = FALSE;
// We know the url so store it
$playlist_data['playlist'][$key]['fileurl'] = $data['filepath'];
}
// If the fid element is set then we have a file element from the upload module
elseif (isset($data['fid'])) {
// We have a filename in this instance so store it
$playlist_data['playlist'][$key]['filename'] = $data['filename'];
// Get the path for the file
$source = swftools_src($playlist_data['playlist'][$key]['filepath']);
// Build the url from the filepath
$playlist_data['playlist'][$key]['fileurl'] = $source['src'];
}
// Otherwise we're not sure where this element came from
else {
// Set the filename element to the file filename
$playlist_data['playlist'][$key]['filename'] = $data['filename'];
// If this playlist isn't a stream
// TODO: We will have stream as a parameter on the playlist elements
if (!$stream) {
// Expand $file as necessary if local or remote
$source = swftools_src($data['filepath']);
// Store results
// TODO: Why are the keys called different things!
$playlist_data['playlist'][$key]['filepath'] = $source['src_path'];
$playlist_data['playlist'][$key]['fileurl'] = $source['src'];
}
else {
// Set both the filepath and fileurl to the filepath
$playlist_data['playlist'][$key]['filepath'] = $data['filepath'];
$playlist_data['playlist'][$key]['fileurl'] = $data['filepath'];
}
}
}
// Ensure all keys are present on the element
$playlist_data['playlist'][$key] = _swftools_playlist_element($playlist_data['playlist'][$key]);
// Allow other modules to modify this playlist element
drupal_alter('swftools_playlist_element', $playlist_data['playlist'][$key]);
// If there is no title for this element create one
if (!$playlist_data['playlist'][$key]['title']) {
$playlist_data['playlist'][$key]['title'] = $playlist_data['playlist'][$key]['filename'];
}
}
// Call drupal_alter to let other modules modify the playlist if they want
drupal_alter('swftools_playlist', $playlist_data);
// If a specific action has already been set then return here
if (!$get_action) {
return $playlist_data;
}
// Try to work out the action from the files passed
$first_valid_file_type = FALSE;
$mixed_media = FALSE;
// Iterate over the array of playlist elements
foreach ($playlist_data['playlist'] AS $key => $data) {
// fileurl is always set, irrespective of what we are passing, so use this to determine extension
$path_parts = pathinfo($data['fileurl']);
// Get the extension for the file
$extension = strtolower($path_parts['extension']);
// Only try to determine actions if there's an extension to work with
if ($extension) {
// Determine if this is an image type
if (strpos('|jpg|jpeg|gif|png|', $extension)) {
// Treat all types of images as the same file type
$extension = 'img';
}
// Only process the file if $type_filter is empty (ie. no filter)
// or if the extension is declared in the $type_filter array.
if (!count($type_filter) || in_array($extension, $type_filter)) {
// $first_valid_file_type stores the file type of the first valid file.
// This will be compared to subsequent files and if two files
// have different types, the action will be defined as SWFTOOLS_MEDIA_DISPLAY_LIST
// in order to utilize a flexible media player.
if (!$first_valid_file_type) {
$first_valid_file_type = $extension;
}
else {
if ($first_valid_file_type != $extension) {
$mixed_media = TRUE;
}
}
}
else {
// This file is not desired so remove it
unset($playlist_data['playlist'][$key]);
}
}
}
// Make a decision based on analysing the file array.
if ($first_valid_file_type == '') {
// No files passed our test.
return FALSE;
}
// Determine the required action.
if ($mixed_media) {
// We have files of multiple types.
$action = SWFTOOLS_MEDIA_DISPLAY_LIST;
}
else {
// We only had one file type, so discover the action for that type by
// calling swftools_get_action() with a dummy filename
$action = swftools_get_action('dummy.' . $first_valid_file_type);
}
// Pluralize the action for multiple files if not already pluralized
if (count($playlist_data['playlist']) > 1 && substr($action, -5, 5) != '_list') {
$action = $action . '_list';
}
// Assign the action to the playlist data ready for return
$playlist_data['action'] = $action;
// Return the resulting playlist data
return $playlist_data;
}
/**
* Implementation of hook_filter_tips().
*/
function swftools_filter_tips($delta, $format, $long = false) {
if ($long) {
return t('
SWF Tools Filter
The basic syntax for embedding a flash file (.swf), flash movie (.flv) or audio file (.mp3) is:
[swf file="filename.swf"]
If you would like to override SWF Tools and flash player default settings,
you can specify additional parameters. For example:
If you would like to output a list of files then the format is:
[swf files="image1.jpg&&image2.jpg&&..."]
SWF Tools Filter will accept following:
params : You can specify values for parameters to be passed to Flash
to control the appearance of the output. Typical values are
bgcolor and wmode. Example: params="wmode=true&&bgcolor="#00FF00"
Alternatively you can supply each parameter individually without using
params. Example wmode="true" bgcolor="#00FF00"
flashvars : You can specify values for output as flashvars, which
become available to the Flash movie that is playing. This is often done
to control a media player. Refer to the documentation of the flash player
you are using to know what flashvar options are available.
Example: flashvars="autostart=true&&volume=80"
methods : Optional information about how to display the file. The most
common usage is to specify a particular media player and
thus override the default specified on the settings page.
Example: methods="player=onepixelout_mp3"
WARNING: with params, flashvars and othervars, pass multiple values
separated by &&.
');
}
else {
return t('You may use !swftools_filter_help to display Flash files inline', array("!swftools_filter_help" => l('[swf file="song.mp3"]', "filter/tips/$format", array('query' => 'swftools_filter'))));
}
}
/**
* Implementation of hook_filter().
*/
function swftools_filter($op, $delta = 0, $format = -1, $text = '') {
switch ($op) {
case 'list':
return array(0 => t('SWF Tools filter'));
case 'no cache':
return FALSE;
case 'description':
return t('Substitutes [swf file="filename.flv"] or [swf files="file1.jpg&&file2.jpg"] with embedding code.');
case 'prepare':
// replace with [swf ] to prevent other filters stripping
return (preg_replace('/\<(swflist|swf)\s*(.*)>/sU', '[\1 \2]', $text));
case 'process':
return _swftools_filter_process_text($text);
}
}
/**
* Processes text obtained from the input filter.
*
* This function processes the filter text that the user has added to the text area.
* If the filter is wrapped in then these are stripped as part of the processing
* This eliminates a validation error in the resulting mark up if SWF Tools filter is
* being used in conjunction with other HTML filters that correct line breaks.
* It won't work in EVERY case, but it will work in MOST cases.
* Filters that are embedded in-line with text will continue to fail validation.
*/
function _swftools_filter_process_text($text) {
$endl = chr(13) ;
if (preg_match_all('@(?:
)?\[(swflist|swf)\s*(.*?)\](?:
)?@s', $text, $match)) {
// $match[0][#] .... fully matched string
// $match[1][#] .... matched tag type ( swf | swflist )
// $match[2][#] .... full params string until the closing '>'
$swftools_parameters = array('file', 'params', 'flashvars', 'othervars', 'methods', 'files');
$match_vars = array();
// Initialise an array to hold playlist arrays
$files = array();
foreach ($match[2] as $key => $passed_parameters) {
//preg_match_all('/(\w*)=\"(.*?)\"/', $passed_parameters, $match_vars[$key]);
preg_match_all('/(\w*)=(?:\"|")(.*?)(?:\"|")/', $passed_parameters, $match_vars[$key]);
// $match_vars[0][#] .... fully matched string
// $match_vars[1][#] .... matched parameter, eg flashvars, params
// $match_vars[2][#] .... value after the '='
// Process the parameters onto the $prepared array.
// Search for standard parameters, parse the values out onto the array.
foreach ($match_vars[$key][1] as $vars_key => $vars_name) {
// Switch to swf or swflist, based on file or files
// Need to tidy up this line, probably use switch/case
if ($vars_name == 'file') {
$match[1][$key] = 'swf';
}
else {
if ($vars_name == 'files') {
$match[1][$key] = 'swflist';
}
}
if ($vars_name == 'file') {
$prepared[$key][$vars_name] = $match_vars[$key][2][$vars_key];
unset ($match_vars[$key][1][$vars_key]);
}
elseif (in_array($vars_name, $swftools_parameters)) {
$prepared[$key][$vars_name] = swftools_url_parse(str_replace(array('&&', '&&'), '&', $match_vars[$key][2][$vars_key]));
unset ($match_vars[$key][1][$vars_key]);
}
else {
$prepared[$key]['othervars'][$vars_name] = $match_vars[$key][2][$vars_key];
}
}
// Search for remaining parameters, map them as elements of the standard parameters.
if (isset($prepared[$key]['methods']['player'])) {
$player = strtolower($prepared[$key]['methods']['player']);
}
else {
$player_key = array_search('player', $match_vars[$key][1]);
if ($player_key!==FALSE) {
$player = strtolower($match_vars[$key][2][$player_key]);
}
else {
$player = FALSE;
}
}
$prepared[$key]['methods']['player'] = $player;
if (count($match_vars[$key][1])) {
// Find out if a player has been set.
foreach ($match_vars[$key][1] as $vars_key => $vars_name) {
if ($parent = swftools_get_filter_alias($vars_name, $player)) {
if ($parent) {
$prepared[$key][$parent][$vars_name] = $match_vars[$key][2][$vars_key];
}
}
}
}
// Just assigning parameters as false if not already set on the $prepared array.
// Really just to avoid declaration warnings when we call swf and swf_list
if (count($prepared[$key])) {
foreach ($swftools_parameters AS $swfparameter) {
if (!isset($prepared[$key][$swfparameter])) {
$prepared[$key][$swfparameter] = array();
}
}
}
// Assemble in to an array of options ready to pass
$options = array();
$options['params'] = $prepared[$key]['params'];
$options['flashvars'] = $prepared[$key]['flashvars'];
$options['othervars'] = $prepared[$key]['othervars'];
$options['methods'] = $prepared[$key]['methods'];
// Set a flag to show if we need to determine an action, or if one was provided
$get_action = TRUE;
if (isset($options['methods']['action'])) {
$get_action = FALSE;
}
switch ($match[1][$key]) {
case 'swf':
$replace = swf($prepared[$key]['file'], $options);
break;
case 'swflist':
// If this filter contains a key called files
if ($prepared[$key]['files']) {
// Iterate over the
foreach ($prepared[$key]['files'] AS $name => $filename) {
if (!$filename) {
$prepared[$key]['files'][$name] = $name;
}
// Put in to proper format for new swftools_prepare_playlist_data()
$files[$key][$name]['filepath'] = $prepared[$key]['files'][$name];
}
// Work out if this is a streamed playlist (in which case we will skip file existence checks)
$stream = FALSE;
if (isset($options['othervars']['stream'])) {
$stream = TRUE;
}
// Get playlist data, but don't determine action if the user specified a player
$playlist_data = swftools_prepare_playlist_data($files[$key], '', $get_action, array(), $stream);
$replace = swf_list($playlist_data, $options);
}
else {
$replace = '';
}
break;
}
$matched[] = $match[0][$key];
$rewrite[] = $replace;
}
return str_replace($matched, $rewrite, $text);
}
return $text;
}
/**
* Implements a hook that extends the parameters that can be passed to the filter
* so that myvar="value" can be mapped to flashvars, etc.
*/
function swftools_get_filter_alias($var, $player = FALSE) {
static $general_mapping = array();
static $player_mapping = array();
if (!count($general_mapping)) {
// Build up the mapping arrays.
$general_mapping = array(
'action' => 'methods',
'embed' => 'methods',
'width' => 'params',
'height' => 'params',
'swliveconnect' => 'params',
'play' => 'params',
'loop' => 'params',
'menu' => 'params',
'quality' => 'params',
'scale' => 'params',
'align' => 'params',
'salign' => 'params',
'wmode' => 'params',
'bgcolor' => 'params',
'base' => 'params',
'version' => 'params',
'allowfullscreen' => 'params',
'allowscriptaccess' => 'params',
);
if (!count($player_mapping)) {
$player_mapping = module_invoke_all('swftools_variable_mapping');
}
$combined = array();
if (count($player_mapping)) {
foreach($player_mapping AS $mapping) {
$combined = array_merge($combined, $mapping);
}
$general_mapping = array_merge($combined, $general_mapping);
}
}
// Return the parent of the variable.
if ($player && isset($player_mapping[$player][$var])) {
return $player_mapping[$player][$var];
}
else {
return (isset($general_mapping[$var])) ? $general_mapping[$var] : FALSE;
}
}
/**
* Parses a string passed to the input filter in to separate key value pairs.
*
* @param $string
* The string to parse.
* @return
* An array of key/value pairs.
*/
function swftools_url_parse($string) {
// TODO: Isn't there a standard function to do this url parsing?
$return = array();
$pairs = split("&", $string);
foreach($pairs as $pair) {
$splitpair = split("=", $pair);
//if(!$splitpair[1] || array_key_exists($splitpair[0], $return)) {
if(!isset($splitpair[1]) || array_key_exists($splitpair[0], $return)) {
$return[] = $splitpair[0];
}
else {
$return[$splitpair[0]] = $splitpair[1];
}
}
return $return;
}
/**
* Implementation of hook_theme().
*/
function swftools_theme() {
return array(
'swftools_embed' => array(
'arguments' => array('embed_code' => NULL, 'action' => NULL, 'methods' => NULL, 'vars' => array(), 'html_alt' => NULL),
),
'swftools_formatter_swftools' => array(
'arguments' => array('element' => NULL, 'profile' => NULL),
'function' => 'theme_swftools_formatter_swftools',
),
'swftools_formatter_swftools_no_file' => array(
'arguments' => array('element' => NULL),
'function' => 'theme_swftools_formatter_swftools',
),
'swftools_formatter_swftools_playlist' => array(
'arguments' => array('element' => NULL, 'profile' => NULL),
'function' => 'theme_swftools_formatter_playlist',
),
'swftools_formatter_swftools_thumbnail' => array(
'arguments' => array('element' => NULL, 'retrieve' => NULL),
'function' => 'theme_swftools_formatter_thumbnail',
),
);
}
/**
* Implementation of hook_file_download().
*
* Allows SWF Tools to work with a private file system that might include files
* upload outside the control of an upload module, e.g. FTP of large video files.
*
* If the file is of a supported type, based on extension, then return a valid header.
* Note: if any other module returns -1 for this file then access will be denied
* even if SWF Tools tries to allow it. See hook_file_download() for details.
*/
function swftools_file_download($file) {
// If SWF Tools is allowed to grant access then check to see if access will be allowed
if (variable_get('swftools_grant_access_to_private_files', SWFTOOLS_GRANT_ACCESS_TO_PRIVATE_FILES)) {
// Get extension of file in question
$extension = pathinfo($file, PATHINFO_EXTENSION);
// Get list of extensions that SWF Tools can grant access to
$extensions = variable_get('swftools_grant_access_extensions', SWFTOOLS_GRANT_ACCESS_EXTENSIONS);
// Need access to the user object
global $user;
// Check if SWF Tools should grant access - skip the check for user #1
if ($user->uid != 1) {
$regex = '/\.('. ereg_replace(' +', '|', preg_quote($extensions)) .')$/i';
if (!preg_match($regex, $file)) {
return;
}
}
// Build an array of types that SWF Tools can react to
$mime_types = array(
'swf' => 'application/x-shockwave-flash',
'flv' => 'application/octet-stream',
'xml' => 'text/xml',
'mp3' => 'audio/mpeg',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/gif',
);
// If file is one of the above types, based on the extension, return headers
if ($mime_types[$extension]) {
return array(
'Content-Type: '. $mime_types[$extension],
'Content-Length: '. filesize(file_create_path($file)),
);
}
}
}
/**
* Implementation of hook_swftools_embed().
*
* Returns the markup for the page, using compliant HTML.
*/
function swftools_swftools_embed($action = '', $methods = FALSE, $vars = FALSE, $html_alt = '') {
// Create parameters markup
$params = '' . "\n";
$params .= '' . "\n";
$params .= '' . "\n";
$params .= '' . "\n";
$params .= '' . "\n";
$params .= '' . "\n";
$params .= '' . "\n";
$params .= '' . "\n";
$params .= '' . "\n";
$params .= '' . "\n";
$params .= '' . "\n";
// Only set background color parameter if it is assigned a value
if ($vars->params['bgcolor']) {
$params .= '' . "\n";
}
// Attach flashvars parameter, if flashvars are present
$params .= $vars->flashvars ? '' . "\n" : '';
// Construct embedding markup
$html = "\n" . '' . "\n";
// Return the result
return $html;
}
/**
* Implementation of hook_swftools_methods().
*/
function swftools_swftools_methods() {
// Initialise array
$methods = array();
// Module implements a default plain HTML embedding method called 'direct'
$methods[SWFTOOLS_EMBED_METHOD][SWFTOOLS_NOJAVASCRIPT] = array(
'name' => SWFTOOLS_NOJAVASCRIPT,
'module' => 'swftools',
'title' => t('Direct embedding - do not use JavaScript replacement'),
);
// Module implements swf embedding
$methods[SWFTOOLS_SWF_DISPLAY_DIRECT][SWFTOOLS_SWF] = array(
'name' => SWFTOOLS_SWF,
'module' => 'swftools',
'title' => t('Use SWF file directly, no streaming through another SWF.'),
);
// Module implements custom embedding of an swf
$methods[SWFTOOLS_SWF_DISPLAY_DIRECT][SWFTOOLS_CUSTOM] = array(
'name' => SWFTOOLS_CUSTOM,
'module' => 'swftools',
'title' => t('Use custom SWF file.'),
);
// Add in the methods from swftools_genericplayers.module
$methods = array_merge($methods, swftools_genericplayers_swftools_methods());
// Return the methods that are native to SWF Tools
return $methods;
}
/**
* Helper function to set the size of the swf content in to $vars->params
*
* @param $vars
* $vars arrays that is being assembled by SWF Tools.
* @param $player
* The array of player data for the player that will be used.
* @return
* Nothing - function directly sets $vars.
*/
function swftools_set_size(&$vars, $player) {
// We use these defaults to filter arrays for their height and width, and assign a fallback value
$defaults = array('height' => '100%', 'width' => '100%');
// If height and width are already set then just return
if (count(array_intersect_key($vars->params, $defaults)) == 2) {
return;
}
// See if we can get height and width from flashvars
$try = array_intersect_key($vars->flashvars, $defaults);
$vars->params += $try;
// See if we can get height and width from player
$try = array_intersect_key($player, $defaults);
$vars->params += $try;
// If we have a height and width now then return
if (count(array_intersect_key($vars->params, $defaults)) == 2) {
return;
}
// Try and get size from the file to be embedded, but preserve height or width if just one was set
$info = swftools_get_info($vars->othervars['src_path']);
// If sizes were retrieved then use them
if ($info) {
$try = array_intersect_key($info, $defaults);
$vars->params += $try;
};
// And if all else fails, assign 100%
$vars->params += $defaults;
}
/**
* Implementation of hook_field_formatter_info().
*/
function swftools_field_formatter_info() {
return array(
'swftools_no_file' => array('label' => t('SWF Tools - no download link'),
'field types' => array('filefield', 'link', 'text'),
'multiple values' => CONTENT_HANDLE_CORE,
),
'swftools_playlist' => array('label' => t('SWF Tools - playlist'),
'field types' => array('filefield', 'link', 'text'),
'multiple values' => CONTENT_HANDLE_MODULE,
),
'swftools' => array('label' => t('SWF Tools - with download link'),
'field types' => array('filefield', 'link'),
'multiple values' => CONTENT_HANDLE_CORE,
),
'swftools_thumbnail' => array('label' => t('SWF Tools - thumbnail'),
'field types' => array('filefield', 'link', 'text'),
'multiple values' => CONTENT_HANDLE_CORE,
),
);
}
/**
* Themes a CCK element in to flash content.
*
* @param $element
* The element to render.
* @return
* A string of markup to produce the flash content, or nothing if the element was empty.
*/
function theme_swftools_formatter_swftools($element, $profile = array()) {
// If the element is empty return
if (empty($element['#item']['fid']) && empty($element['#item']['value']) && empty($element['#item']['url'])) {
return '';
}
// See if a thumbnail image has been stored with this content
$image = theme_swftools_formatter_thumbnail('', TRUE, $element['#item']['#delta']);
// If an image was stored then set the thumbnail
if ($image) {
$options['othervars']['image'] = $image;
}
else {
$options = array();
}
// Assign the profile
$options['othervars']['profile'] = $profile ? $profile['profile'] : '';
// See if we're processing a filefield and get the path and description
if (isset($element['#item']['filepath'])) {
$swf = $element['#item']['filepath'];
$options['othervars']['title'] = $element['#item']['data']['description'];
$source = 'filefield';
}
// See if we're processing a link field and get the path and title
elseif (isset($element['#item']['url'])) {
$swf = $element['#item']['url'];
$options['othervars']['title'] = $element['#item']['title'];
$source = 'link';
// add the query to the base url, if necessary
if (isset($element['#item']['query']) && strlen($element['#item']['query']) > 0) {
$swf .= '?' . ($element['#item']['query']);
}
// add the fragment to the url, if necessary
if (isset($element['#item']['fragment']) && strlen($element['#item']['fragment']) > 0) {
$swf .= '#' . ($element['#item']['fragment']);
}
}
// We're processing a text field so get the path
else {
$swf = $element['#item']['value'];
$options['othervars']['title'] = '';
$source = 'text';
// We need to run $swf through check_url to make sure it is safe, but rtmp isn't in the allowed protocols
// This is configurable (see filter.module line 1182) but not sure if the variable is exposed
// $swf = check_url($swf);
// See if we have been given a stream by trying to explode the string
$stream = explode(' ', $swf);
// If the explode return 2 elements assume we have a stream, element[0] contains the source, element[1] the file
if (count($stream) == 2) {
$options['othervars']['stream'] = $stream[0];
$swf = $stream[1];
}
}
// Pass element to the swf tools processor by attaching it in othervars
$options['othervars']['cck'] = $element;
// Get the markup for the flash content from swf()
$return = swf($swf, $options);
// Add the download link if required (we are either using swftools formatter, or a profile with this option enabled)
if ($element['#formatter'] == 'swftools' || !empty($profile['download_link'])) {
switch ($source) {
case 'filefield':
$return .= "\n" . theme('filefield_formatter_default', $element);
break;
case 'link':
$return .= "\n" . theme('link_formatter_default', $element);
break;
}
}
// Return the resulting markup
return $return;
}
/**
* Themes multiple value CCK content in to a playlist.
*
* @param $element
* The element to render.
* @return
* A string of markup to produce the flash content, or nothing if the element was empty.
*/
function theme_swftools_formatter_playlist($element, $profile = array()) {
// Initialise an array for results
$files = array();
// Get the children
$children = element_children($element);
// If there is only one child then maybe we don't want a playlist
if (count($children) == 1) {
// Pop the first value of the children array
$_children = $children;
$child = array_pop($_children);
// Get the name of the alternate formatter for this content type
$formatter_name = variable_get('swftools_' . $element['#type_name'] . '_' . $element['#field_name'], 'swftools_playlist');
// What happens next depends on the formatter name
switch ($formatter_name) {
case 'hidden':
// If the format is set to hidden then return nothing
if ($formatter_name == 'hidden') {
return;
}
case 'swftools_playlist':
// If swftools_playlist don't do anything different
break;
default:
// Find out what the alternate formatter should be
if ($formatter = _content_get_formatter($formatter_name, 'filefield')) {
// We can work out the theme name from the formatter information
$theme = $formatter['module'] .'_formatter_'. $formatter_name;
// Construct a modified element that mimics a single element
$element['#formatter'] = $formatter_name;
$element['#theme'] = $theme;
$element += $element[$child];
$element['#item']['#delta'] = 0;
unset($element[$child]);
// Theme this new element according to the alternate formatter
return theme($theme, $element);
}
}
}
// Retrieve images
$images = theme_swftools_formatter_thumbnail('', TRUE);
// Cycle through the file elements
foreach ($children as $key) {
// Is this a filefield?
if (isset($element[$key]['#item']['filepath'])) {
$files[$key] = array(
'filepath' => $element[$key]['#item']['filepath'],
'title' => $element[$key]['#item']['data']['description'],
);
}
// Is this a link field?
if (isset($element[$key]['#item']['url'])) {
$files[$key] = array(
'filepath' => $element[$key]['#item']['url'],
'title' => $element[$key]['#item']['title'],
);
}
// Is this a text field?
// TODO: How to handle streams in a playlist
if (isset($element[$key]['#item']['value'])) {
$files[$key] = array(
'filepath' => $element[$key]['#item']['value'],
'title' => '',
);
}
// Is there an image?
if (isset($images[$key]) && $images[$key]) {
// Get the path to the image
$source = swftools_src($images[$key]);
// If $source returned a result then use it
if ($source) {
$files[$key]['image'] = $source['src'];
}
}
}
// If files array is empty then there is nothing to be rendered
if (empty($files)) {
return;
}
// Initialise an empty options array
$options = array();
// Pass element to the swf tools processor by attaching it in othervars
$options['othervars']['cck'] = $element;
// Assign the profile
$options['othervars']['profile'] = $profile ? $profile['profile'] : '';
// But if we got something then we can call swf() now to render it
return swf($files, $options);
}
/**
* Theme function to store and retrieve thumbnail images.
*
* If called as a theme function it stores the path of the image, but if the retrieve flag is set it returns that path.
*
* In effect this function is a local store to hold the most recently "seen" image on behalf of the swf themer.
* In order for this code to work the thumbnail theme function must come before the swf theme function, so it
* is necessary to order the fields appropriately on the content configuration page.
*
* When caching thumbnails then if the element delta is zero the cache is flushed first to start a new
* collection of images.
*
* @param $element
* The element to store.
* @param $retrieve
* When TRUE will return the currently stored path.
* @param $delta
* If set then attempt to retrieve just the image at the specified delta, otherwise return
* the whole array of stored thumbnails.
* @return
* A string, or an array, holding the thumbnail filepaths.
*/
function theme_swftools_formatter_thumbnail($element, $retrieve = FALSE, $delta = NULL) {
// Create a static variable to hold the image path
static $image_path = array();
// It retrieving a previous thumbnail then return its path and reset the stored value
// We may want to retrieve all cached thumbnails ($delta = NULL), or a specific thumbnail
// in which case $delta is the one we want
if ($retrieve) {
if ($delta !== NULL) {
if (isset($image_path[$delta])) {
return $image_path[$delta];
}
else {
return '';
}
}
else {
return $image_path? $image_path : '';
}
}
// If the delta is zero we are starting a new collection so reset in case
// a retrieval call wasn't made by the theme function
if ($element['#item']['#delta'] == 0) {
$image_path = array();
}
// If the element is empty set the stored path to an empty string
if (empty($element['#item']['fid']) && empty($element['#item']['value']) && empty($element['#item']['url'])) {
$image_path[] = '';
return;
}
// Initialise an empty string
$image_url = '';
// See if we're processing a filefield and get the appropriate value
if (isset($element['#item']['filepath'])) {
$image_url = $element['#item']['filepath'];
}
// See if we're processing a link field and get the appropriate value
elseif (isset($element['#item']['url'])) {
$image_url = $element['#item']['url'];
// add the query to the base url, if necessary
if (isset($element['#item']['query']) && strlen($element['#item']['query']) > 0) {
$image_url .= '?' . ($element['#item']['query']);
}
// add the fragment to the url, if necessary
if (isset($element['#item']['fragment']) && strlen($element['#item']['fragment']) > 0) {
$image_url .= '#' . ($element['#item']['fragment']);
}
}
// We're processing a text field so get the appropriate value
else {
$image_url = $element['#item']['value'];
// Check this is a valid url
$image_url = check_url($image_path);
}
// Store the result
$image_path[] = $image_url;
// Return nothing at this point
return;
}
/**
* Assigns an id on the $options['othervars']['id'] if one wasn't set, and verifies that the id is unique.
*
* Typically this is called to get us a unique id. We use time() as the basis to ensure we avoid
* using the same id twice. If the id is not unique (in the current page creation session) then we
* append numbers, using form_clean_id() to do this.
*
* @param $options
* The swftools options array.
* @return
* An options array with a unique id in the othervars sub-array.
*/
function swftools_assign_id($options) {
// If a specific id wasn't set then assign one using swftools suffixed with time
$options['othervars'] += array(
'id' => 'swftools-' . time(),
);
// Check the id is unique
$options['othervars']['id'] = form_clean_id($options['othervars']['id']);
// Return the result
return $options;
}
/**
* Returns the default properties for an SWF Tools method.
*
* @return
* Array of keys with default values
*/
function _swftools_methods() {
return array(
'name' => '',
'module' => '',
'file' => '',
'version' => 7,
'title' => '',
'download' => '',
'width' => '', // This used to be 50 - why?
'height' => '', // This used to be 50 - why?
'library' => '',
);
}
/**
* Ensures the default playlist elements are available on the element.
*
* @param $element
* An array (which may be partial) ready to be populated.
* @return
* An array populated with all default keys.
*/
function _swftools_playlist_element($element) {
/**
* Need to decide what the common keys will be...
* - filepath: The path to the file (url, or filepath)
* - title: Title to use
* - description: Description
* - author: Author
* - image: Thumbnail image
* - stream: Streaming server, or empty string if not a stream
*/
$element += array(
'filepath' => '',
'title' => '',
'description' => '',
'author' => '',
'image' => '',
'stream' => '',
);
return $element;
}
/**
* Standardises an array of files so that it is an array of arrays, with a key
* named filepath.
*
* @param $files
* Array of files to be processed.
* @return
* Standardised array.
*/
function _swftools_playlist_standardise_incoming_array(&$files) {
// Iterate over the array of files that were passed
foreach ($files AS $key => $data) {
// If this $files element is an object convert it to an array
if (is_object($data)) {
$files[$key] = (array)$data;
}
// If this $files element isn't an array then build one
elseif (!is_array($data)) {
// Create an array with key filepath set to the $data string
$files[$key] = array(
'filepath' => $data,
);
}
// Add in standard elements to the array
// TODO: Is this step necessary when the overhaul is complete?
$files[$key] += array(
'filename' => '',
'filepath' => '',
'fileurl' => '',
'title' => '',
);
}
}
/**
* Flushes all caches when new settings are stored.
*
* This function is called by player modules as part of their settings submit
* handlers to ensure all cached content is purged.
*/
function swftools_admin_settings_submit($form, &$form_state) {
// Flush all caches so new players appear
drupal_flush_all_caches();
}
/**
* Creates a relative path url from a file path, using private or public file system.
*
* This is an SWF Tools version of file_create_url(). The only difference is that
* here we do not output absolute paths as we are only using these paths within the
* context of a page and therefore relative is fine.
*
* @param $path
* Path to the file.
* @return
* A string with a complete path, relative to the local file system.
*/
function swftools_create_url($path) {
// Strip file_directory_path from $path. We only include relative paths in urls.
if (strpos($path, file_directory_path() .'/') === 0) {
$path = trim(substr($path, strlen(file_directory_path())), '\\/');
}
// Output a relative url, using public or private file transfer
switch (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC)) {
case FILE_DOWNLOADS_PUBLIC:
return base_path() . file_directory_path() . '/' . str_replace('\\', '/', $path);
case FILE_DOWNLOADS_PRIVATE:
return url('system/files/' . $path);
}
}
/**
* Determines the url for a file, and expands its filepath if necessary.
*
* @param $file
* A string containing a url or a path to a file.
* @return
* An array with two keys
* - src: The url to the file (relative if on the local system)
* - src_path: The path to the file, expanded if necessary
*
* This function is necessary because SWF Tools allows flexibility in how the site interprets
* filenames. If just a filename is provided then this might point to a file on the local system,
* or it might point to an external location if the remote media path is set. However, for things
* like auto-detection of sizes to work when the filename is local it must be expanded to include
* the file path.
*
* If $file is a full and valid absolute url then the function returns $file on both keys.
*
* If $file is full and valid url on the local system then the function returns $file on both keys.
*
* If $file is a path to a local file (sites/default/files/xxx) then src_path will return this,
* and src will return a full relative url.
*
* If $file is just a filename and remote media is active then src and src_path will both be set
* to the url for the external location.
*
* If $file is just a filename and remote media is not active then src_path will return the path
* to the local system (so xxx becomes sites/default/files/xxx) and src will return the full
* relative url.
*/
function swftools_src($file) {
// src will contain a full, or relative, url that is used to render the file
// src_path will contain a partial path (without webroot), or the original file path
$ret = array(
'src' => '',
'src_path' => $file,
);
// If already a valid absolute url simply return it
if (valid_url($file, TRUE)) {
$ret['src'] = $file;
return $ret;
}
// If a valid url, and starts with /, then assume to be a local path relative to web root
// No security check is needed as the result of this is only output to the webpage
if (valid_url($file) && strpos($file, '/') === 0) {
$ret['src'] = $file;
return $ret;
}
// If defintely in the local file system return a relative url to the file
if ($file == file_create_path($file)) {
// If media checking is active check to see if it actually exists
if (variable_get('swftools_check_media', TRUE)) {
// If the file doesn't exist, set an error message and return FALSE to indicate failure
if (!file_exists($file)) {
drupal_set_message(t('SWF Tools could not find %file.', array('%file' => $file)), 'error');
return FALSE;
}
}
// If we got here then return a relative url to the file
$ret['src'] = swftools_create_url($file);
return $ret;
}
// Retrieve the media url setting
$media_url = trim(variable_get('swftools_media_url', ''));
// If a remote path is set build the appropriate url to the file
if ($media_url) {
$ret['src'] = $media_url . '/' . $path;
return $ret;
}
// If we got here then expand to a local file path and call again
return swftools_src(file_create_path($file));
}
/**
* Returns a string defining a base path for flash to use.
*/
function swftools_get_base() {
// Cache the result as we might get multiple calls
static $base = '';
// If $base is not already defined then set it
if (!$base) {
// Retrieve swftools_media_url to see if a remote path has been set
$base = trim(variable_get('swftools_media_url', ''));
// If $base is still empty then use local path to file directory
if (!$base) {
$base = base_path() . file_directory_path() . '/';
}
}
// Return the base path
return $base;
}
/**
* Flatten an array which has sub-arrays in to a single keyed array.
*
* If keys in the sub-array are the same as ones seen previously the early key will be over-written.
*
* @param $array
* Array to be processed.
*/
function swftools_flatten_array(&$array) {
// Only process if we passed an array
if (is_array($array)) {
// Iterate over the array
foreach($array as $key => $value) {
// If the value is in itself an array then flatten that too
if (is_array($value)) {
// Unset this key as this contains an array
unset($array[$key]);
// Flatten the sub-array
swftools_flatten_array($value);
// Merge the flattened sub-array in to the rest of the array
$array = array_merge($array, $value);
}
}
}
}
/**
* Returns a variable, or the equivalent variable from the relevant profile.
*
* This is a custom handler to take care of the SWF Tools profile mechanism.
* If no profile name is given then it simply returns the requested variable,
* or the default if the variable is not set.
*
* If a profile name is given then it will first try to retrive the profiled variable.
* If that fails it will try to return the global setting, and if that fails then it
* will return the default. In this way we cascade through the "most relevant" setting.
*
* @param $name
* The name of the variable to return.
* @param $default
* The default value to use if this variable has never been set.
* @param $profile
* The name of the profile to use.
* @return
* The value of the variable.
*/
function swftools_variable_get($name, $default, $profile = '') {
if ($profile && ($ret = variable_get('swftools_' . $profile . '_' . $name, SWFTOOLS_PROFILE_UNDEFINED)) != SWFTOOLS_PROFILE_UNDEFINED) {
return $ret;
}
return variable_get($name, $default);
}
/**
* Serves an xml playlist from the {cache_swftools} table.
*
* SWF Tools no longer generates a physical file for the playlist. Instead it
* places an entry in its internal cache table and then serves the file from
* there. Pages request the files by accessing swftools/playlist/nnn.xml, where
* nnn is the cid of the content.
*
* Note that we check at swf() whether the relevant cid exists in the table. If
* it doesn't then the playlist file is regenerated.
*
* @param $playlist
* The name of the playlist being requested.
* @return
* Serve the xml file, or issue drupal_not_found() if it doesn't exist.
*/
function swftools_serve_xml_playlist($playlist) {
if (!$output = cache_get('xml:'.$playlist, 'cache_swftools')) {
print drupal_not_found();
}
drupal_set_header('Content-Type: text/xml; charset=utf-8');
print $output->data;
}
/**
* Creates a cid for the call to swf() and returns its data from the cache when available.
*
* To make our cid we create an array from $file and $options. Then we serialize it, and then
* we run it through md5. This generates a unique cid for that piece of content.
*
* Once the cid is created it is attached to $options['othervars']['cid'] so that
* subsequent functions don't have to rehash the data to generate it again.
*
* Within {cache_swftools} we then prefix the cid with swf: or xml: to indicate whether the
* cache entry refers to the markup that produces the content, or the xml: that describes the
* playlist. So for xml playlist content we should have two entries with the same numeric portion
* in their cid.
*
* @param $file
* The $file parameter from the call to swf().
* @param $options
* The $options parameter from the call to swf().
* @return
* The cached content, or FALSE if it is isn't available.
*/
function swftools_fetch_from_cache($file, &$options) {
// Construct a cid for this piece of content
$cid = md5(serialize(array($file, $options)));
// If this is in the cache then return its data
if ($ret = cache_get('swf:'.$cid, 'cache_swftools')) {
return $ret->data;
}
// Otherwise set the cid on othervars ready to store this item when we've rendered it
$options['othervars']['cid'] = $cid;
// Indicate that we don't have cached data
return FALSE;
}
/**
* Implementation of hook_flush_caches().
*/
function swftools_flush_caches() {
return array('cache_swftools');
}
/**
* Return the default players, or customised players, for each action.
*/
function _swftools_get_handlers($profile) {
$defaults = array(
SWFTOOLS_FLV_DISPLAY => SWFTOOLS_GENERIC_FLV,
SWFTOOLS_MP3_DISPLAY => SWFTOOLS_GENERIC_MP3,
SWFTOOLS_SWF_DISPLAY_DIRECT => SWFTOOLS_SWF,
);
// Retrieve settings from the database if available
$settings = swftools_variable_get('swftools_handlers', $defaults, $profile);
// Return result
return $settings;
}
/**
* Merges two multi-dimensional arrays.
*
* This function is used by players that filter their settings to strip out
* blanks or defaults. For the admin page we need a full set of values to prevent
* errors. Since the arrays might be multi-dimensional we cannot use a regular
* array_merge(). The values in the first array will be over-written by values in
* the second, if they exist.
*
* @param $array1
* The first array to merge.
* @param $array2
* The second array to merge.
* @return
* The result of the merge.
*/
function swftools_array_merge($array1, $array2) {
// Iterate over $array 2 (this is normally the smaller of the two)
foreach ($array2 as $key => $value) {
// If this key is present in $array1 then work out what to do
if (isset($array1[$key])) {
// If both keys hold arrays, combine them
if (is_array($value) && is_array($array1[$key])) {
$array1[$key] = swftools_array_merge($array1[$key], $value);
}
else {
// Replace value in $array1 with that from $array2
$array1[$key] = $value;
}
}
else {
// Simply put this value in $array1 if it isn't already in $array1
$array1[$key] = $value;
}
}
// Return the result
return $array1;
}