'emapi_admin_list_page', 'title' => 'Embedded media', 'description' => 'Administration list of embedded media.', 'access arguments' => array('administer embedded media'), 'file' => 'includes/emapi.admin.inc', ); $items['emapi/parse/json'] = array( 'page callback' => 'emapi_parse_json', 'title' => 'Parse media', 'description' => 'Automatically derive a media object from a parsed URL or embed code.', 'access arguments' => array('administer embedded media'), 'file' => 'includes/emapi.parse.inc', 'type' => MENU_CALLBACK, ); return $items; } /** * Implementation of hook_init(). */ function emapi_init() { emapi_get_provider_classes(); // Ensure the proper files are loaded when a new media object is initiated. spl_autoload_register('emapi_autoload'); } /** * Implementation of hook_flush_caches(). */ function emapi_flush_caches() { return array('cache_emapi_xml'); } /** * Builds a registry of Media provider classes. * * Each module supporting a Media provider will need to implement * hook_emapi_register, which will need to return an associated array keyed by * the scheme, with an array containing at least the following key => value * pairs. Note that the scheme portion of the URI this class supports is in the * form of scheme://identifier/id. * 'class_name' => The actual name of the class. * The following key => value pairs are optional, and will otherwise be * automatically derived: * 'name' => The human-readable name of the scheme. * 'description' => A description of the scheme. * 'path' => The path where the class file resides. * 'file' => The file containing the class definition. * 'module' => The module defining the class. * 'url' => The URL to the remote media provider, if applicable. * The following key => value pair will be automatically set to the association * and cannot be overridden: * 'scheme' => The scheme portion of the URI. * * @param string $scheme * (Optional) The scheme of the specific class registration to return. * @param boolean $reset * (Optional) If TRUE, then reset the registration. * @return array * If $scheme is specified, then return only the specified class array, or NULL * if there is no such registered class. Otherwise, return the entiry registry. */ function emapi_get_provider_classes($scheme = NULL, $reset = FALSE) { static $emapi_registered_classes; if ($reset || !isset($emapi_registered_classes)) { $emapi_registered_classes = array(); // Build our media object class registry. foreach (module_implements('emapi_register') as $module) { foreach (module_invoke($module, 'emapi_register') as $scheme_name => $class) { $emapi_registered_classes[$scheme_name] = is_array($class) ? $class : array(); $emapi_registered_classes[$scheme_name]['scheme'] = $scheme_name; if (!isset($emapi_registered_classes[$scheme_name]['name'])) { $emapi_registered_classes[$scheme_name]['name'] = t($scheme_name); } if (!isset($emapi_registered_classes[$scheme_name]['description'])) { $emapi_registered_classes[$scheme_name]['description'] = t('Class definition for @scheme.', array('@scheme' => $scheme_name)); } if (!isset($emapi_registered_classes[$scheme_name]['path'])) { $emapi_registered_classes[$scheme_name]['path'] = drupal_get_path('module', $module); } if (!isset($emapi_registered_classes[$scheme_name]['file'])) { $emapi_registered_classes[$scheme_name]['file'] = $class_name .'.inc'; } if (!isset($emapi_registered_classes[$scheme_name]['module'])) { $emapi_registered_classes[$scheme_name]['module'] = $module; } } } } if (isset($scheme) && isset($emapi_registered_classes[$scheme])) { return $emapi_registered_classes[$scheme]; } else if (!isset($scheme)) { return $emapi_registered_classes; } } /** * Autoload the media object classes when needed. */ function emapi_autoload($class_name) { if ($class_name == 'EmapiMedia') { module_load_include('inc', 'emapi', 'includes/emapi.class.media'); } else if ($class = emapi_get_provider_class_by_class_name($class_name)) { include_once($class['path'] .'/'. $class['file']); } } /** * Return the registered EmAPI class specified by name. */ function emapi_get_provider_class_by_class_name($class_name = NULL, $reset = FALSE) { static $classes; if (!isset($classes) || $reset) { $classes = array(); $provider_classes = emapi_get_provider_classes(); foreach ($provider_classes as $scheme => $class) { $classes[$class['class_name']] = $class; } } if (isset($class_name)) { return $classes[$class_name]; } return $classes; } /** * A wrapper around simplexml to retrieve a given XML file. * * @param $url * The URL to the XML to retrieve. * @param $display_errors * Optional; if TRUE, then we'll display errors to the end user. They'll be * logged to the watchdog in any case. * @param $refresh * Optional; if TRUE, then we'll force a new load of the XML. Otherwise, * a cached version will be retrieved if possible. * @return * A fully populated object, or FALSE on an error. */ function emapi_retrieve_xml($url, $display_errors = FALSE, $refresh = FALSE) { module_load_include('inc', 'emapi', 'includes/emapi.xml'); return _emapi_retrieve_xml($url, $display_errors, $refresh); } /** * Returns the scheme of a URI (e.g. a stream). * * Note that this function is lifted from Drupal 7's file_uri_scheme(). * * @param $uri * A stream, referenced as "scheme://target". * @return * A string containing the name of the scheme, or FALSE if none. For example, * the URI "youtube://v/3gfh6asey" would return "youtube". */ function emapi_uri_scheme($uri) { $data = explode('://', $uri, 2); return count($data) == 2 ? $data[0] : FALSE; } /** * Returns the registered class for a specific provider. * * @param string $uri * A stream, referenced as "scheme://target". * @return string * The registered class, or NULL if it's an unsupported URI. */ function emapi_get_provider_class($uri) { $class = emapi_get_provider_classes(emapi_uri_scheme($uri)); if (is_array($class) && $class['class_name']) { return $class['class_name']; } } /** * Parses a URL or embed code into a media object. * * @param string $url * The URL or embed code to parse. * @return mixed * The fully populated media object, or FALSE. */ function emapi_parse($url) { foreach (emapi_get_provider_classes() as $class) { if (class_exists($class['class_name'])) { $media = new $class['class_name']; if ($media->parse($url)) { $media = emapi_media_from_uri($media->uri); return $media; } } } return FALSE; } /** * Loads a media object based on the given URI. * * @param string $uri * A stream, referenced as "scheme://target". * @return mixed * The fully populated media object, or FALSE. */ function emapi_media_from_uri($uri) { $media = &emapi_static('emapi_media', array()); // First check to see if we've already loaded this media. foreach ($media as $emid => $item) { if ($item->uri == $uri) { return $item; } } // Next see if the item is in the db. $results = db_query("SELECT emid, uri, uid, status, timestamp FROM {emapi_media} WHERE uri = '%s'", $uri); if ($result = db_fetch_object($results)) { $media[$result->emid] = emapi_media_from_db_result($result); return $media[$result->emid]; } // Finally we simply create a new media object. if (($class = emapi_get_provider_class($uri)) && class_exists($class)) { global $user; $item = new $class($uri); $item->set_uid($user->uid); $item->set_status(EMAPI_STATUS_PERMANENT); $item->set_timestamp(time()); drupal_alter('emapi_media', $item); return $item; } // No media matches the given URI. return FALSE; } /** * Create a new media object from a db result. * * @param object $result * The db object returned from the db. * @return mixed * The media object, or NULL if there's no class available for the scheme. */ function emapi_media_from_db_result($result) { if (($class = emapi_get_provider_class($result->uri)) && class_exists($class)) { $media = new $class($result->uri); $media->set_emid($result->emid); $media->set_uid($result->uid); $media->set_status($result->status); $media->set_timestamp($result->timestamp); drupal_alter('emapi_media', $media); } return $media; } /** * Load one or more fully populated media objects. * * @param array $emids * An array of unique integers corresponding to the media ID's. * @return mixed * The fully populated media object, or FALSE. */ function emapi_media_load_multiple($emids) { $media = &emapi_static('emapi_media', array()); if (!empty($emids)) { // If any media were previously loaded, remove from the ids still to load. $emids = array_flip($emids); $ids_to_load = array_keys(array_diff_key($emids, $media)); if (!empty($ids_to_load)) { $results = db_query("SELECT emid, uri, uid, status, timestamp FROM {emapi_media} WHERE emid IN (". db_placeholders($ids_to_load) .")", $ids_to_load); while ($result = db_fetch_object($results)) { $media[$result->emid] = emapi_media_from_db_result($result); module_invoke_all("emapi_media_load", $media[$result->emid]); } } // Ensure that the returned array is ordered the same as the original // $ids array if this was passed in and remove any invalid ids. // Remove any invalid ids from the array. $requested_ids = array_intersect_key($media, $emids); $returned_ids = array(); foreach ($emids as $id => $item) { if ($requested_ids[$id]) { $returned_ids[$id] = $requested_ids[$id]; } } } return $returned_ids; } /** * Load a single media object. * * @param integer $emid * The unique ID for the media. * @return mixed * Either the desired media object, or NULL if not found. */ function emapi_media_load($emid) { $media = emapi_media_load_multiple(array($emid)); return $media[$emid]; } /** * Save a media object to the database. * * @param object &$media * The media object to save. */ function emapi_media_save(&$media) { module_invoke_all('emapi_media_presave', $media); if ($media->emid) { drupal_write_record('emapi_media', $media, 'emid'); module_invoke_all('emapi_media_update', $media); } else if ($media) { drupal_write_record('emapi_media', $media); module_invoke_all('emapi_media_insert', $media); } } /** * Delete a media object from the database. * * @param integer $emid * The unique identifier of the media to delete. */ function emapi_media_delete($emid) { $media = &emapi_static('emapi_media', array()); $item = emapi_media_load($emid); if ($item) { unset($media[$emid]); db_query("DELETE FROM {emapi_media} WHERE emid = %d", $emid); module_invoke_all('emapi_media_delete', $item); $link = l($item->get_uri(), $item->url()); $class = emapi_get_provider_classes(emapi_uri_scheme($item->get_uri())); watchdog('emapi', '@provider: deleted !link.', array('@provider' => $class['name'], '!link' => $link)); drupal_set_message(t('@provider !link has been deleted.', array('@provider' => $class['name'], '!link' => $link))); } } /** * Backport of d7's drupal_static(). */ function &emapi_static($name, $default_value = NULL, $reset = FALSE) { static $data = array(), $default = array(); if (!isset($name)) { // All variables are reset. This needs to be done one at a time so that // references returned by earlier invocations of drupal_static() also get // reset. foreach ($default as $name => $value) { $data[$name] = $value; } // As the function returns a reference, the return should always be a // variable. return $data; } if ($reset) { // The reset means the default is loaded. if (array_key_exists($name, $default)) { $data[$name] = $default[$name]; } else { // Reset was called before a default is set and yet a variable must be // returned. return $data; } } elseif (!array_key_exists($name, $data)) { // Store the default value internally and also copy it to the reference to // be returned. $default[$name] = $data[$name] = $default_value; } return $data[$name]; }