resource.module in D7 Media 6
Resource API for Drupal, a replacement for files.
The Resource API superceeds Drupal's file api, providing an interface to manage Resources identified by a URL.
There are two base classes, Resource and ResourceScheme, that constitue the ResourceAPI. The Resource class provides a high level interaction with resources including event hooks, DB synchronization, and support methods including, but not limited, to url generation and mime identification. The ResourceScheme is a user defined stream wrapper for PHP.
In most cases each class extending Resource will have a complimentary ResourceScheme class, and will share configuration options and settings.
File
resource/resource.moduleView source
<?php
/**
* @file
* Resource API for Drupal, a replacement for files.
*
* The Resource API superceeds Drupal's file api, providing an interface to
* manage Resources identified by a URL.
*
* There are two base classes, Resource and ResourceScheme, that constitue the ResourceAPI.
* The Resource class provides a high level interaction with resources including event hooks,
* DB synchronization, and support methods including, but not limited, to url generation and
* mime identification. The ResourceScheme is a user defined stream wrapper for PHP.
*
* In most cases each class extending Resource will have a complimentary ResourceScheme
* class, and will share configuration options and settings.
*
*/
/**
* @todo:
* interface PHP stream wrapper
* interface DrupalStreamWrapperExtends PHP stream wrapper {
* function htmlUrl();
* function mime();
*
*
*/
/**
* Implementation of hook_init().
*
* Registers core stream handlers for public and private files.
*/
function resource_init() {
module_load_include('inc', 'resource', 'ResourceStreamWrapperManager');
module_load_include('inc', 'resource', 'ResourceStreamWrapper');
module_load_include('inc', 'resource', 'ResourcePublicStreamWrapper');
module_load_include('inc', 'resource', 'ResourcePrivateStreamWrapper');
$manager = ResourceStreamWrapperManager::singleton();
$manager
->register('public', 'ResourcePublicStreamWrapper');
$manager
->register('private', 'ResourcePrivateStreamWrapper');
}
/**
* Implements hook_perm().
*/
function resource_perm() {
return array(
'administer resources',
);
}
/**
* Implements hook_menu().
*/
function resource_menu() {
$items = array();
// Menu items that are basically just menu blocks.
$items['admin/settings/resource'] = array(
'title' => 'Resource configuration',
'description' => 'Configure Resource and stream handler settings.',
'page callback' => 'resource_settings_overview',
'access arguments' => array(
'access administration pages',
),
'file' => 'resource.admin.inc',
);
$items['admin/settings/resource/public'] = array(
'title' => 'Public File (public://) settings',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'resource_settings_public_form',
),
'access arguments' => array(
'administer resources',
),
'file' => 'resource.admin.inc',
);
$items['admin/settings/resource/private'] = array(
'title' => 'Private File (private://) settings',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'resource_settings_private_form',
),
'access arguments' => array(
'administer resources',
),
'file' => 'resource.admin.inc',
);
return $items;
}
/**
* Resource API
*/
/**
* Debug Logging function.
*/
function resource_debug($message) {
if (TRUE) {
watchdog('stream', $message, array(), WATCHDOG_DEBUG);
}
}
/**
* Base Resource class.
*
* The drupal_file class represents a physical file stored in Drupal's
* 'File System Path'. It is important to understand that copy, move,
* delete, etc method affect both the the physical file the object
* represents and the database record for the file.
*
* Example Factory Usage:
*
* $factory = new Resource();
* $file = $factory->load_id(87);
* $another = $file->load_path('images/logo.png');
*
* $file = Resource::load_id(24);
*
* $yet_another = $file->load('path', 'uploads/myfile.pdf');
*
*
* if ($yet_another->delete()) unset($yet_another);
*
*/
class Resource {
// @see drupal_file::load()
protected static $files = array();
protected static $streamWrapper;
protected static $streamManager;
protected $rid = 0;
protected $uid = 1;
protected $name = '';
protected $url = '';
protected $mime = 'application/octet-stream';
protected $size = 0;
protected $status = 0;
protected $timestamp = 0;
protected $htmlUrl = '';
/**
* drupal_file constructor.
*
* @param object $file (optional) stdClass object to initialize self with.
*/
protected function __construct($resource) {
$this->streamManager = ResourceStreamWrapperManager::singleton();
$class = $this->streamManager
->class(parse_url($url, PHP_URL_SCHEME));
$this->streamWrapper = new $class();
$this->rid = isset($resource->rid) ? $resource->rid : 0;
$this->url = $resource->url;
$this->mime = $resoruce->mime;
$this->size = $resource->size;
$this->name = $resource->name;
$this->htmlUrl = $this->streamWrapper
->htmlUrl($this->url);
}
/**
* Create a new name for a resource by appending a number if a resource
* exists at $destination.
*/
function _destination($destination, $replace = FILE_EXISTS_RENAME) {
if ($replace & FILE_EXISTS_REPLACE) {
return true;
}
if (!file_exists($destination)) {
return $destination;
}
if ($replace & FILE_EXISTS_ERROR) {
return FALSE;
}
$streamdir = dirname($destination);
$basename = basename($destination);
// Destination file already exists, generate an alternative.
$pos = strrpos($basename, '.');
if ($pos !== FALSE) {
$name = substr($basename, 0, $pos);
$ext = substr($basename, $pos);
}
else {
$name = $basename;
$ext = '';
}
$counter = 0;
do {
$destination = $streamdir . '/' . $name . '_' . $counter++ . $ext;
} while (file_exists($destination));
return $destination;
}
/**
* Create a copy a Resource.
*
* @param string $destination (optional) @see file_copy.
* @param int $replace (optional) @see file_destination
* @return object|bool drupal_file if the copy is successful, or FALSE
*/
function copy($destination, $replace = FILE_EXISTS_RENAME) {
$destination = $this
->_destination($destination, $replace);
if (copy($this->url, $destination)) {
$copy = clone $this;
$copy->rid = NULL;
$copy->url = $destination;
if ($copy
->save()) {
module_invoke_all('resource_copy', $this, $copy);
return ResourceFactory::loadId($copy->rid);
}
}
return FALSE;
}
/**
* Delete a file and its database record.
*
* @param $force
* Boolean indicating that the file should be deleted even if
* hook_file_references() reports that the file is in use.
* @return mixed
* TRUE for success, array for reference count, or FALSE in the event
* of an error.
*
* @see hook_file_references()
*/
function delete($force = FALSE) {
// If any module returns a value from the reference hook, the
// file will not be deleted from Drupal, but file_delete will
// return a populated array that tests as TRUE.
if (!$force && ($references = module_invoke_all('resource', $this))) {
return $references;
}
// Let other modules clean up on delete.
module_invoke_all('resource_delete', $this);
// Make sure the file is deleted before removing its row from the
// database, so UIs can still find the file in the database.
if (unlink($this->url)) {
db_query('DELETE FROM {resource} WHERE rid = %d', $this->rid);
// remove internally used static cache entries.
$this
->reset_cache('rid::' . $this->rid);
$this
->reset_cache('url::' . $this->url);
return TRUE;
}
return FALSE;
}
/**
* Move a Resource.
*
* @param string $destination (optional) @see file_copy.
* @param int $replace (optional) @see file_destination
* @return bool
* @see file_copy()
*/
function move($destination, $replace = FILE_EXISTS_RENAME) {
$destination = $this
->_destination($destination, $rename);
if (copy($this->url, $destination)) {
unlink($this->url);
$orig = clone $this;
$this->url = $destination;
if ($this
->save()) {
module_invoke_all('resource_move', $this, $orig);
return ResourceFactory::loadId($this->rid);
}
}
return FALSE;
}
/**
* Save the current state of a drupal_file in the database.
* If the file->fid is empty a new database record will be added.
*
* @return bool TRUE if save succeeded, FALSE if save failed.
*/
function save() {
$this->timestamp = time();
$manager = DrupalStreamWrapperManager::singleton();
$class = $manager
->streamWrapperClass(parse_url($this->url, PHP_URL_SCHEME));
if ($class != get_class($this
->streamWrapperManager())) {
$this->streamWrapperHandler = new $class();
}
$this->size = $this->streamWrapperHandler
->size($this->url);
$this->mime = $this->streamWrapperHandler
->mime($this->url);
if (empty($this->fid)) {
$result = drupal_write_record('resource', $this);
module_invoke_all('resource_insert', $this);
}
else {
$result = drupal_write_record('resource', $this, 'rid');
module_invoke_all('resource_update', $this);
}
return $result;
}
/**
* Mark a file as permanent.
*
* @return bool
*/
function set_status($bitmask = NULL) {
if (is_null($bitmask)) {
return $file->status;
}
elseif (db_query('UPDATE {files} SET status=%d', $bitmask, $this->fid)) {
$this->status = $status;
module_invoke_all('file_set_status', $this, $status);
return TRUE;
}
return FALSE;
}
function refresh() {
$this->htmlUrl = $this->streamWrapper
->htmlUrl($this->url);
$this->size = $this->streamWrapper
->size($this->url);
$this->mime = $this->streamWrapper
->mime($this->url);
return $this;
}
}
class ResourcePublic extends Resource {
function url() {
}
function mime() {
}
}
class ResourcePrivate extends ResourcePublic {
function url() {
}
}
class ResourceUpload extends Resource {
public $source = '';
public $errors;
function save() {
// validate if not already validated, error is validation didn't pass.
if (!$this
->validate()) {
return FALSE;
}
// rename the file to its original name.
parent::save();
}
function validate($validators = array()) {
if (!isset($this->errors)) {
// Default validation for all uploads.
$validators['file_validate_name_length'] = array();
$this->errors = array();
foreach ($validators as $function => $args) {
array_unshift($args, $file);
$errors = array_merge($errors, call_user_func_array($function, $args));
}
}
return !empty($this->errors);
}
function errors() {
return $this->errors;
}
function move($dest) {
// validate if not already validated, error is validation didn't pass.
if (!$this->fid && !$this
->save()) {
return FALSE;
}
// for upload files we changed the filename in the path to a junk string...
}
/**
* Saves a file upload to a temporary location
*
* The file will be added to the files table as a temporary file. Temporary
* files are periodically cleaned. To make the file permanent file call
* it's set_permanent() method.
*
* @param $source
* A string specifying the name of the upload field to save.
* @param $validators
* An optional, associative array of callback functions used to validate the
* file. The keys are function names and the values arrays of callback
* parameters which will be passed in after the user and file objects. The
* functions should return an array of error messages, an empty array
* indicates that the file passed validation. The functions will be called in
* the order specified.
* @param $destination
* A string containing the directory $source should be copied to. If this is
* not provided or is not writable, the temporary directory will be used.
* @param $replace
* A boolean indicating whether an existing file of the same name in the
* destination directory should overwritten. A FALSE value will generate a
* new, unique filename in the destination directory.
* @return
* An object containing the file information, or FALSE in the event of an
* error.
*/
static function save_upload($source) {
// check and see if there were any errors.
if (drupal_file_upload::upload_error($source)) {
return FALSE;
}
// Begin building file object.
$file = new stdClass();
$file->source = $source;
$file->uid = $user->uid;
$file->filename = basename($_FILES['files']['name'][$source]);
// create a tmp path to use until the file is save to a final location else where.
// we just use a random string to defang the file for processing in tmp.
$file->filepath = file_destination(uniqid(), file_directory_temp(), FALSE);
$file->filemime = $_FILES['files']['type'][$source];
$file->filesize = $_FILES['files']['size'][$source];
// Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary
// directory. This overcomes open_basedir restrictions for future file
// operations.
if (!move_uploaded_file($_FILES['files']['tmp_name'][$source], $file->filepath)) {
form_set_error($source, t('File upload error. Could not move uploaded file.'));
watchdog('file api', 'Upload error. Could not move uploaded file %file to destination %destination.', array(
'%file' => $file->filename,
'%destination' => $file->filepath,
));
return FALSE;
}
return $file;
}
function upload_error($errno) {
// If no file was uploaded there is an error. :)
if (empty($_FILES['files']['tmp_name'][$source]) || !is_uploaded_file($_FILES['files']['tmp_name'][$source])) {
return t('No file uploaded');
}
// @see http://php.net/manual/en/features.file-upload.errors.php
switch ($_FILES['files']['error'][$source]) {
case UPLOAD_ERR_OK:
return FALSE;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
return t('The file %file could not be saved, because it exceeds %maxsize, the maximum allowed size for uploads.', array(
'%file' => $source,
'%maxsize' => format_size(file_upload_max_size()),
));
case UPLOAD_ERR_PARTIAL:
case UPLOAD_ERR_NO_FILE:
return t('The file %file could not be saved, because the upload did not complete.', array(
'%file' => $source,
));
case UPLOAD_ERR_NO_TMP_DIR:
return t('The file %file could not be saved, because the PHP upload_tmp_dir does not exist.', array(
'%file' => $source,
));
case UPLOAD_ERR_CANT_WRITE:
return t('The file %file could not be saved, because the file could not be written to the disk.', array(
'%file' => $source,
));
case UPLOAD_ERR_EXTENSION:
return t('The file %file could not be saved, because the upload was stopped by a php extension.', array(
'%file' => $source,
));
// Unknown error
default:
return t('The file %file could not be saved. An unknown error has occurred.', array(
'%file' => $source,
));
}
}
// Use static load as the entry point to keep the API interface
// consistent.
function load($source) {
if (isset(self::$files[$source])) {
return self::$files[$source];
}
// attempt to save the upload.
if ($file = self::save_upload($source)) {
return new ResourcePublic($file);
}
}
}
class ResourceFactory {
static $resources;
function __construct() {
}
/**
* Return the first matching file in the files table. This is a simple single
* object loader it in combination with the static $files variable allows all
* drupal file objects to also act as factories and share the same static cache.
*
* @param string key (required) database column to use in where condition.
* @param int|string value (required) the value of the column to use in the where condition.
* @return object|bool A Drupal file object or FALSE if a file was not found.
* @see drupal_file::load(), drupal_file::load_path()
*/
public function _load($column, $value) {
// set a cache id based on key and value so we can statically cache
// all simple loads.
$cid = $column . '::' . $value;
if (empty(self::$files[$cid])) {
$resource = db_fetch_object(db_query('SELECT f.* FROM {resource} r WHERE r.%s = %d', $column, $value));
$resource = new Resource($r);
}
module_invoke_all('resource_load', $r);
self::$resources[$cid] = $r;
// Files are not cloned, because there is in fact only one.
return self::$resources[$cid];
}
/**
* Load a file object from the database by id.
*
* @param int $id A file id. (required)
* @return object|bool A Drupal file object or FALSE if a file was not found.
* @see: drupal_file::load()
*/
public function loadId($id) {
return $this
->_load('rid', $id);
}
/**
* Load a file object from the database by path.
*
* @param string $path A path to a file. (required)
* @return object|bool A Drupal file object or FALSE if a file was not found.
* @see: drupal_file::load()
*/
public function loadUrl($url) {
return $this
->_load('url', $url);
}
public function loadUpload($key) {
}
/**
* Reset the shared static cache.
*/
public function resetCache($cid = FALSE) {
// no cache id, reset the entire cache.
if (!$cid) {
self::$resources = array();
}
elseif (isset(self::$resources[$cid])) {
unset(self::$resources[$cid]);
}
}
// table relationship
// relationship_id
// left, (resource_id)
// relationship ( parent_of, member_of, friend_of, ... )
// right, (resource_id)
// table resource_formatter
// relationship_id
// display_context(node_full, node_teaser, comment, block, profile, default)
// media(screen, print),
// formatter
// formatter_arguments
function loadChildren($rid, $context = NULL, $media = NULL) {
// select * from {resource_relationships} where parent = $resource->id;
// foreach child { $children[] = $this->loadId($child->rid)->context($context); }
// return $children;
}
function loadParents($rid, $context = NULL, $media = NULL) {
// select * from {resource_relationships} where child = $resource->id;
// foreach parent { $parent[] = $this->loadId($child->rid)->context($context); }
// return $parents;
}
function loadPeers($rid, $context, $media) {
}
/**
* @param key primary key for where clause
* @param value to limit key to.
think about this context thing later..
*/
function loadRelationship($key, $id, $context = NULL, $media = NULL) {
// select * from relationships where $key = $candidate AND context = $context
// foreach row { $relatives[] = $this->loadId($relative->rid)->context($row) }
}
}
Functions
Name | Description |
---|---|
resource_debug | Debug Logging function. |
resource_init | Implementation of hook_init(). |
resource_menu | Implements hook_menu(). |
resource_perm | Implements hook_perm(). |
Classes
Name | Description |
---|---|
Resource | Base Resource class. |
ResourceFactory | |
ResourcePrivate | |
ResourcePublic | |
ResourceUpload |