Source for file mime.flatdefault.php
Documentation is available at mime.flatdefault.php
* Mime handler - flat attachments model default
* As an alternative to storing file attachments in a user based storage tree, this version of mime.default.php
* provides for a flat filing system based on id. A two level tree is currently provided bassed on a Mod 1000 trimming
* of the id number for the first level directories. This limits each branch to a maximum of 1000 sub directories but
* a change the mime_default_branch setting will allow other trimmimg to be implemented.
* define( 'LIBERTY_DEFAULT_MIME_HANDLER', 'mimeflatdefault' );
* in config_inc.php to activate
* @subpackage liberty_mime_handler
* This is the name of the plugin - max char length is 16
* As a naming convention, the liberty mime handler definition should start with:
define( 'PLUGIN_MIME_GUID_FLATDEFAULT', 'mimeflatdefault' );
define( 'FLAT_STORAGE_NAME', 'attachments' );
// Set of functions and what they are called in this paricular plugin
// Use the GUID as your namespace
'verify_function' => 'mime_default_verify',
'store_function' => 'mime_default_store',
'update_function' => 'mime_default_update',
'load_function' => 'mime_default_load',
'branch_function' => 'mime_default_branch',
'download_function' => 'mime_default_download',
'expunge_function' => 'mime_default_expunge',
// Brief description of what the plugin does
'title' => 'Default Flat File Handler',
'description' => 'This mime handler can handle any file type, creates thumbnails when possible and will make the file available as an attachment under a flat file system.',
// Templates to display the files
'upload_tpl' => 'bitpackage:liberty/mime/default/upload.tpl',
'view_tpl' => 'bitpackage:liberty/mime/default/view.tpl',
'inline_tpl' => 'bitpackage:liberty/mime/default/inline.tpl',
'storage_tpl' => 'bitpackage:liberty/mime/default/storage.tpl',
'attachment_tpl' => 'bitpackage:liberty/mime/default/attachment.tpl',
// This should be the same for all mime plugins
// This needs to be specified by plugins that are included by other plugins
'file_name' => 'mime.flatdefault.php',
// Ensure only one mime.default.php version is active and visible
// Set this to TRUE if you want the plugin active right after installation
'auto_activate' => FALSE,
// Help page on bitweaver.org
//'help_page' => 'MimeHelpPage',
// Here you can use a perl regular expression to pick out file extensions you want to handle
// e.g.: Some image types: '#^image/(jpe?g|gif|png)#i'
// This plugin will be picked if nothing matches.
//'mimetypes' => array( '/.*/' ),
* Sanitise and validate data before it's stored
* @param array $pStoreRow Hash of data that needs to be stored
* @param array $pStoreRow['upload'] Hash passed in by $_FILES upload
* @return TRUE on success, FALSE on failure - $pStoreRow['errors'] will contain reason
global $gBitSystem, $gBitUser;
// storage is always owned by the user that uploaded it!
// er... or at least admin if somehow we have a NULL mUserId - anon uploads maybe?
$pStoreRow['user_id'] = @BitBase::verifyId( $gBitUser->mUserId ) ? $gBitUser->mUserId : ROOT_USER_ID;
// Bypass p_liberty_attach_attachments for managed uploads - access controlled by fisheye/treasury or other uploader
// Ignore warning when p_liberty_attach_attachments totally disabled
// if( $this->hasUserPermission( 'p_liberty_manage_attachments' )) {
$pStoreRow['no_perm_check'] = true;
if( !empty( $pStoreRow['upload']['tmp_name'] ) && is_file( $pStoreRow['upload']['tmp_name'] )) {
// attachment_id is only set when we are updating the file
if( @BitBase::verifyId( $pStoreRow['upload']['attachment_id'] )) {
// if a new file has been uploaded, we need to get some information from the database for the file update
$fileInfo = $gBitSystem->mDb->getRow( "
SELECT la.`attachment_id`, lf.`file_id`, lf.`file_name`
INNER JOIN `". BIT_DB_PREFIX. "liberty_files` lf ON ( lf.`file_id` = la.`foreign_id` )
WHERE la.`attachment_id` = ?", array( $pStoreRow['upload']['attachment_id'] ));
// try to generate thumbnails for the upload
//$pStoreRow['upload']['thumbnail'] = !$gBitSystem->isFeatureActive( 'liberty_offline_thumbnailer' );
$pStoreRow['upload']['thumbnail'] = TRUE;
// Little cluge - unattached files create an empty ['upload']['attachment_id'] attached ones do not
// Really need core liberty to be consistent on identifying these - ideally adding flag for linked files
if( isset ($pStoreRow['upload']['attachment_id']) ) {
$pStoreRow['attachment_id'] = $gBitSystem->mDb->GenID( 'liberty_content_id_seq' );
$pStoreRow['attachment_id'] = $pStoreRow['content_id'];
// Generic values needed by the storing mechanism
$pStoreRow['upload']['source_file'] = $pStoreRow['upload']['tmp_name'];
// Store all uploaded files in the flat tree storage area
if( empty( $pStoreRow['upload']['dest_branch'] )) {
// Use group number to protect disk content - still need to set group of directory!
$pStoreRow['errors']['upload'] = tra( 'There was a problem verifying the uploaded file.' );
* @param array $pStoreRow File data needed to store details in the database - sanitised and generated in the verify function
* @return TRUE on success, FALSE on failure - $pStoreRow['errors'] will contain reason
// this will reset the uploaded file
if( BitBase::verifyId( $pStoreRow['attachment_id'] ) && !empty( $pStoreRow['upload'] )) {
if( empty( $pStoreRow['dest_branch'] )) {
if( !empty( $pStoreRow['dest_branch'] ) && !empty( $pStoreRow['file_name'] ) ) {
// First we remove the old file
$file = $path. $pStoreRow['file_name'];
if( !empty( $pStoreRow['unlink_dir'] )) {
// make sure we store the new file in the same place as before
$pStoreRow['upload']['dest_branch'] = $pStoreRow['dest_branch'];
// if we can create new thumbnails for this file, we remove the old ones first
if( !empty( $canThumbFunc ) && $canThumbFunc( $pStoreRow['upload']['type'] )) {
// Now we process the uploaded file
$sql = "UPDATE `". BIT_DB_PREFIX. "liberty_files` SET `file_name` = ?, `mime_type` = ?, `file_size` = ?, `user_id` = ? WHERE `file_id` = ?";
$gBitSystem->mDb->query( $sql, array( $pStoreRow['upload']['name'], $pStoreRow['upload']['type'], $pStoreRow['upload']['size'], $pStoreRow['user_id'], $pStoreRow['file_id'] ));
// ensure we have the correct guid in the db
if( empty( $pStoreRow['attachment_plugin_guid'] )) {
$gBitSystem->mDb->associateUpdate(
array( 'attachment_plugin_guid' => $pStoreRow['attachment_plugin_guid'] ),
array( 'attachment_id' => $pStoreRow['attachment_id'] )
* Store the data in the database
* @param array $pStoreRow File data needed to store details in the database - sanitised and generated in the verify function
* @return TRUE on success, FALSE on failure - $pStoreRow['errors'] will contain reason
global $gBitSystem, $gLibertySystem;
// take care of the uploaded file and insert it into the liberty_files and liberty_attachments tables
if( $storagePath = liberty_process_upload( $pStoreRow['upload'], empty( $pStoreRow['upload']['copy_file'] ))) {
// add row to liberty_files
"file_name" => $pStoreRow['upload']['name'],
"file_id" => $pStoreRow['attachment_id'],
"mime_type" => $pStoreRow['upload']['type'],
"file_size" => $pStoreRow['upload']['size'],
"user_id" => $pStoreRow['user_id'],
if ( !$storeHash['file_size'] ) { $storeHash['file_size'] = 0; }
$gBitSystem->mDb->associateInsert( BIT_DB_PREFIX. "liberty_files", $storeHash );
// add the data into liberty_attachments to make this file available as attachment
"attachment_id" => $pStoreRow['attachment_id'],
"content_id" => $pStoreRow['content_id'],
"attachment_plugin_guid" => !empty( $pStoreRow['attachment_plugin_guid'] ) ? $pStoreRow['attachment_plugin_guid'] : PLUGIN_MIME_GUID_FLATDEFAULT,
"foreign_id" => $pStoreRow['attachment_id'],
"user_id" => $pStoreRow['user_id'],
$gBitSystem->mDb->associateInsert( BIT_DB_PREFIX. "liberty_attachments", $storeHash );
$pStoreRow['errors']['liberty_process'] = "There was a problem processing the file.";
* Load file data from the database
* @param array $pFileHash contains all file information
* @return TRUE on success, FALSE on failure - ['errors'] will contain reason for failure
global $gBitSystem, $gLibertySystem;
if( @BitBase::verifyId( $pFileHash['attachment_id'] )) {
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_files` lf ON( la.`foreign_id` = lf.`file_id` )
WHERE la.`attachment_id`=?";
if( $row = $gBitSystem->mDb->getRow( $query, array( $pFileHash['attachment_id'] ))) {
$storageName = basename( $row['file_name'] );
// compatibility with _FILES hash
$row['name'] = $storageName;
$row['type'] = $row['mime_type'];
// this will fetch the correct thumbnails
if( $canThumbFunc && $canThumbFunc( $row['mime_type'] )) {
$thumbHash['default_image'] = LIBERTY_PKG_URL. 'icons/generating_thumbnails.png';
// indicate that this is a mime thumbnail
if( !empty( $ret['thumbnail_url']['medium'] ) && strpos( $ret['thumbnail_url']['medium'], '/mime/' )) {
$ret['thumbnail_is_mime'] = TRUE;
$ret['display_url'] = LIBERTY_PKG_URL. "view_file.php?attachment_id=". $row['attachment_id'];
// legacy table data was named storage path and included a partial path. strip out any path just in case
$ret['file_name'] = $storageName;
$ret['preferences'] = $pPrefs;
// some stuff is only available if we have a source file
// make sure to check for these when you use them. frequently the original might not be available
// e.g.: video files are large and the original might be deleted after conversion
$ret['source_url'] = STORAGE_PKG_URL. $storageBranch;
$ret['last_modified'] = filemtime( $ret['source_file'] );
$ret['download_url'] = LIBERTY_PKG_URL. "download/file/". $row['attachment_id'];
$ret['download_url'] = LIBERTY_PKG_URL. "download_file.php?attachment_id=". $row['attachment_id'];
// add a description of how to insert this file into a wiki page
if( $gLibertySystem->isPluginActive( 'dataattachment' )) {
$ret['wiki_plugin_link'] = "{image id=". $row['attachment_id']. "}";
// additionally we'll add this to distinguish between old plugins and new ones
// TODO: this should hopefully not be necessary for too long
* Takes care of the entire download process. Make sure it doesn't die at the end.
* in this functioin it would be possible to add download resume possibilites and the like
* @param array $pFileHash Basically the same has as returned by the load function
* @return TRUE on success, FALSE on failure - $pParamHash['errors'] will contain reason for failure
// Check to see if the file actually exists
if( !empty( $pFileHash['source_file'] ) && is_readable( $pFileHash['source_file'] )) {
// if we have PEAR HTTP/Download installed, we make use of it since it allows download resume and download manager access
// read the docs if you want to enable download throttling and the like
if( @include_once( 'HTTP/Download.php' )) {
$dl = new HTTP_Download();
$dl->setLastModified( $pFileHash['last_modified'] );
$dl->setFile( $pFileHash['source_file'] );
//$dl->setContentDisposition( HTTP_DOWNLOAD_INLINE, $pFileHash['file_name'] );
$dl->setContentDisposition( HTTP_DOWNLOAD_ATTACHMENT, $pFileHash['file_name'] );
$dl->setContentType( $pFileHash['mime_type'] );
if( PEAR::isError( $res )) {
$gBitSystem->fatalError( $res->getMessage() );
// make sure we close off obzip compression if it's on
// this will get the browser to open the download dialogue - even when the
// browser could deal with the content type - not perfect, but works
$pFileHash['mime_type'] = "application/force-download";
header( "Accept-Ranges: bytes" );
header( "Content-type: ". $pFileHash['mime_type'] );
header( "Content-Disposition: attachment; filename=". $pFileHash['file_name'] );
header( "Last-Modified: ". gmdate( "D, d M Y H:i:s", $pFileHash['last_modified'] ). " GMT", true, 200 );
header( "Content-Transfer-Encoding: binary" );
header( "Connection: close" );
$pFileHash['errors']['no_file'] = tra( 'No matching file found.' );
* Nuke data in tables when content is removed
* @param integer $pAttachmentId The id of the attachment to delete
* @return TRUE on success, FALSE on failure
global $gBitSystem, $gBitUser;
if( @BitBase::verifyId( $pAttachmentId )) {
if( $fileHash = LibertyMime::loadAttachment( $pAttachmentId )) {
if( $gBitUser->isAdmin() || $gBitUser->mUserId == $fileHash['user_id'] && isset ( $fileHash['source_file'] ) && !empty( $fileHash['source_file'] )) {
// make sure this is a valid storage directory before removing it
$query = "DELETE FROM `". BIT_DB_PREFIX. "liberty_files` WHERE `file_id` = ?";
$gBitSystem->mDb->query( $query, array( $fileHash['foreign_id'] ));
* Generate branch from Id
* @param integer $pAttachmentId The id of the attachment to access
* @return string containing path to storage location for attachment
if( @BitBase::verifyId( $pAttachmentId ) ) {
* liberty_mime_get_storage_branch - get url to store files for the feature site_upload_dir. It creates a calculable hierarchy of directories
* @author Lester Caine<lester@lsces.co.uk>
* @param $pParamHash key=>value pairs to determine path. Possible keys in descending directory depth are: use the 'common' branch if null, 'package' - any desired directory below the StoragePath. this will be created if it doesn't exist, 'sub_dir' - the sub-directory in the package organization directory, this is often a primary id such as attachment_id
* @return string full path on local filsystem to store files.
function liberty_mime_get_storage_branch( $pParamHash ) { // $pSubDir = NULL, $pUserId = NULL, $pPackage = ACTIVE_PACKAGE, $pPermissions = 0755, $pCreateDir = true ) {
// *PRIVATE FUNCTION. GO AWAY! DO NOT CALL DIRECTLY!!!
if( $pAttachmentId = BitBase::getParameter( $pParamHash, 'attachment_id' ) ) {
$pathParts[] = 'attachments';
$pathParts[] = (int) ($pAttachmentId % 1000);
$pathParts[] = $pAttachmentId;
// Added in fisheye override
if( $pPackage = BitBase::getParameter( $pParamHash, 'package' ) ) {
$pathParts[] = $pPackage;
$fullPath = implode( $pathParts, '/' ). '/';
if( BitBase::getParameter( $pParamHash, 'create_dir', TRUE ) ){
if( empty( $pParamHash['package'] ) ) {
if( empty( $pParamHash['sub_dir'] ) ) {
$pParamHash['sub_dir'] = BitBase::getParameter( $pParamHash, 'attachment_id' );
if( empty( $pParamHash['package'] ) ) {
if( empty( $pParamHash['sub_dir'] ) ) {
$pParamHash['sub_dir'] = BitBase::getParameter( $pParamHash, 'attachment_id' );
|