* Represents one virtual file in storage. Virtual file has up to two parts: * * @see GreenBox * @see MetaData * @see RawMediaData */ class StoredFile{ /* ========================================================== constructor */ /** * Constructor, but shouldn't be externally called * * @param gb reference to GreenBox object * @param gunid string, optional, globally unique id of file * @return this */ function StoredFile(&$gb, $gunid=NULL) { $this->gb =& $gb; $this->dbc =& $gb->dbc; $this->filesTable = $gb->filesTable; $this->accessTable= $gb->accessTable; $this->gunid = $gunid; if(is_null($this->gunid)) $this->gunid = $this->_createGunid(); $this->resDir = $this->_getResDir($this->gunid); $this->accessDir = $this->gb->accessDir; $this->rmd =& new RawMediaData($this->gunid, $this->resDir); $this->md =& new MetaData(&$gb, $this->gunid); return $this->gunid; } /* ========= 'factory' methods - should be called to construct StoredFile */ /** * Create instace of StoreFile object and insert new file * * @param gb reference to GreenBox object * @param oid int, local object id in the tree * @param name string, name of new file * @param mediaFileLP string, local path to media file * @param mdataFileLP string, local path to metadata XML file * @param gunid global unique id (optional) - for insert file with gunid * @return instace of StoredFile object */ function insert(&$gb, $oid, $name, $mediaFileLP='', $mdataFileLP='', $gunid=NULL) { $ac =& new StoredFile(&$gb, ($gunid ? $gunid : NULL)); $ac->name = $name; $ac->id = $oid; $ac->type = "unKnown"; if($ac->name=='') $ac->name=$ac->gunid; $this->dbc->query("BEGIN"); $res = $ac->dbc->query("INSERT INTO {$ac->filesTable} (id, name, gunid, type) VALUES ('$oid', '{$ac->name}', '{$ac->gunid}', '{$ac->type}')" ); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } if($mdataFileLP != ''){ $res = $ac->md->insert($mdataFileLP); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } } if($mediaFileLP != ''){ $res = $ac->rmd->insert($mediaFileLP); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } $mime = $ac->rmd->getMime(); // return PEAR::raiseError("X1"); // $gb->debugLog("gunid={$ac->gunid}, mime=$mime"); if($mime !== FALSE){ $res = $ac->dbc->query("UPDATE {$ac->filesTable} SET type='$mime' WHERE id='$oid'"); if(PEAR::isError($res)){ $ac->dbc->query("ROLLBACK"); return $res; } } } $res = $this->dbc->query("COMMIT"); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } return $ac; } /** * Create instace of StoreFile object and recall existing file.
* Should be supplied oid XOR gunid - not both ;) * * @param gb reference to GreenBox object * @param oid int, optional, local object id in the tree * @param gunid string, optional, global unique id of file * @return instace of StoredFile object */ function recall(&$gb, $oid='', $gunid='') { $cond = ($gunid=='' ? "id='$oid'" : "gunid='$gunid'" ); $row = $gb->dbc->getRow("SELECT id, gunid, type, name FROM {$gb->filesTable} WHERE $cond"); if(PEAR::isError($row)) return $row; if(is_null($row)){ return PEAR::raiseError( "StoredFile::recall: fileobj not exist ($oid/$gunid)", GBERR_FOBJNEX ); } $ac =& new StoredFile(&$gb, $row['gunid']); $ac->type = $row['type']; $ac->name = $row['name']; $ac->id = $row['id']; return $ac; } /** * Create instace of StoreFile object and recall existing file from tmpLink * * @param gb reference to GreenBox object * @param tmpLink string * @param sessid string */ function recallFromLink(&$gb, $tmpLink, $sessid) { $gunid = $gb->dbc->getOne("SELECT gunid FROM {$gb->accessTable} WHERE tmpLink='$tmpLink' AND sessid='$sessid'"); if(PEAR::isError($gunid)) return $gunid; if(is_null($gunid)) return PEAR::raiseError( "StoredFile::recallFromLink: accessobj not exist ($tmpLink)", GBERR_AOBJNEX); return StoredFile::recall(&$gb, '', $gunid); } /** * Create instace of StoreFile object and make copy of existing file * * @param src reference to source object * @param nid int, new local id */ function copyOf(&$src, $nid) { $ac =& StoredFile::insert(&$src->gb, $nid, $src->name, $src->_getRealRADFname(), ''); if(PEAR::isError($ac)) return $ac; $ac->md->replace($src->md->getMetaData(), 'xml'); return $ac; } /* ======================================================= public methods */ /** * Replace existing file with new data * * @param oid int, local id * @param name string, name of file * @param mediaFileLP string, local path to media file * @param mdataFileLP string, local path to metadata XML file or XML string * @param mdataLoc string 'file'|'string' */ function replace($oid, $name, $mediaFileLP='', $mdataFileLP='', $mdataLoc='file') { $this->dbc->query("BEGIN"); $res = $this->dbc->query("UPDATE {$this->filesTable} SET name='$name', type='{$this->type}' WHERE id='$oid'"); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } if($mediaFileLP != ''){ $res = $this->rmd->replace($mediaFileLP, $this->type); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } $mime = $this->rmd->getMime(); if($mime !== FALSE){ $res = $this->dbc->query("UPDATE {$this->filesTable} SET type='$mime' WHERE id='$oid'"); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } } } if($mdataFileLP != ''){ $res = $this->md->replace($mdataFileLP, $mdataLoc); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } } $res = $this->dbc->query("COMMIT"); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } return TRUE; } /** * Increase access counter, create access record, * call access method of RawMediaData * * @param sessid string */ function accessRawMediaData($sessid) { $this->dbc->query("BEGIN"); $res = $this->dbc->query("UPDATE {$this->filesTable} SET currentlyAccessing=currentlyAccessing+1 WHERE gunid='{$this->gunid}'"); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } $accLinkName = $this->_getAccessFname($sessid, $this->_getExt()); $res = $this->dbc->query("INSERT INTO {$this->accessTable} (gunid, sessid, tmplink, ts) VALUES ('{$this->gunid}', '$sessid', '$accLinkName', now())"); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } $acc = $this->rmd->access($accLinkName); if(PEAR::isError($acc)){ $this->dbc->query("ROLLBACK"); return $acc; } if($acc === FALSE){ $this->dbc->query("ROLLBACK"); return PEAR::raiseError( 'StoredFile::accessRawMediaData: not exists' ); } $res = $this->dbc->query("COMMIT"); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } return $acc; } /** * Decrease access couter, delete access record, * call release method of RawMediaData * * @param sessid string */ function releaseRawMediaData($sessid) { $this->dbc->query("BEGIN"); $res = $this->dbc->query("UPDATE {$this->filesTable} SET currentlyAccessing=currentlyAccessing-1 WHERE gunid='{$this->gunid}' AND currentlyAccessing>0" ); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } $ca = $this->dbc->getOne("SELECT currentlyAccessing FROM {$this->filesTable} WHERE gunid='{$this->gunid}'" ); if(PEAR::isError($ca)){ $this->dbc->query("ROLLBACK"); return $ca; } $accLinkName = $this->_getAccessFname($sessid, $this->_getExt()); $res = $this->dbc->query("DELETE FROM {$this->accessTable} WHERE gunid='{$this->gunid}' AND sessid='$sessid' AND tmplink='$accLinkName'"); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } $res = $this->dbc->query("COMMIT"); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } if(intval($ca)==0) return $this->rmd->release($accLinkName); return TRUE; } /** * Replace metadata with new XML file * * @param mdataFileLP string, local path to metadata XML file or XML string * @param mdataLoc string 'file'|'string' */ function replaceMetaData($mdataFileLP, $mdataLoc='file') { $this->dbc->query("BEGIN"); $res = $this->md->replace($mdataFileLP, $mdataLoc); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } $res = $this->dbc->query("COMMIT"); if(PEAR::isError($res)) return $res; return TRUE; } /** * Update metadata with new XML file * * @param mdataFileLP string, local path to metadata XML file or XML string * @param mdataLoc string 'file'|'string' * @return boolean or PEAR::error */ function updateMetaData($mdataFileLP, $mdataLoc='file') { $this->dbc->query("BEGIN"); $res = $this->md->update($mdataFileLP, $mdataLoc); if(PEAR::isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } $res = $this->dbc->query("COMMIT"); if(PEAR::isError($res)) return $res; return TRUE; } /** * Update object namespace and value of one metadata record * * @param mdid int, metadata record id * @param object string, object value, e.g. title string * @param objns string, object namespace prefix, have to be defined * in file's metadata (or reserved prefix) * @see MetaData * @return boolean or PEAR::error */ function updateMetaDataRecord($mdid, $object, $objns='_L') { return $this->md->updateRecord($mdid, $object, $objns='_L'); } /** * Get metadata as XML string * * @return XML string * @see MetaData */ function getMetaData() { return $this->md->getMetaData(); } /** * Analyze file with getid3 module.
* Obtain some metadata stored in media file.
* This method should be used for prefilling metadata input form. * * @return array * @see MetaData */ function analyzeMediaFile() { $ia = $this->rmd->analyze(); return $ia; } /** * Rename stored virtual file * * @param newname string * @return true or PEAR::error */ function rename($newname) { $res = $this->dbc->query("UPDATE {$this->filesTable} SET name='$newname' WHERE gunid='{$this->gunid}'"); if(PEAR::isError($res)) return $res; return TRUE; } /** * Delete stored virtual file * * @see RawMediaData * @see MetaData */ function delete() { $res = $this->rmd->delete(); if(PEAR::isError($res)) return $res; $res = $this->md->delete(); if(PEAR::isError($res)) return $res; $links = $this->dbc->getAll("SELECT tmplink FROM {$this->accessTable} WHERE gunid='{$this->gunid}'"); if(is_array($links)) foreach($links as $i=>$item){ @unlink($item['tmplink']); } $res = $this->dbc->query("DELETE FROM {$this->accessTable} WHERE gunid='{$this->gunid}'"); if(PEAR::isError($res)) return $res; $res = $this->dbc->query("DELETE FROM {$this->filesTable} WHERE gunid='{$this->gunid}'"); if(PEAR::isError($res)) return $res; return TRUE; } /** * Returns true if virtual file is accessed.
* Static or dynamic call is possible. * * @param gunid string, optional (for static call), global unique id */ function isAccessed($gunid=NULL) { if(is_null($gunid)) $gunid = $this->gunid; return (0 < $this->dbc->getOne(" SELECT currentlyAccessing FROM {$this->filesTable} WHERE gunid='$gunid' ")); } /** * Returns local id of virtual file * */ function getId() { return $this->id; } /** * Returns true if raw media file exists * */ function exists() { $indb = $this->dbc->getRow("SELECT gunid FROM {$this->filesTable} WHERE gunid='{$this->gunid}'"); return (!is_null($indb) && $this->rmd->exists()); } /* ==================================================== "private" methods */ /** * Create new global unique id * */ function _createGunid() { return md5(microtime().$_SERVER['SERVER_ADDR'].rand(). "org.mdlf.livesupport"); } /** * Get local id from global id. * Static or dynamic call is possible. * * @param gunid string, optional (for static call), * global unique id of file */ function _idFromGunid($gunid=NULL) { if(is_null($gunid)) $gunid = $this->$gunid; $id = $this->dbc->getOne("SELECT id FROM {$this->filesTable} WHERE gunid='$gunid'"); if(is_null($id)) return PEAR::raiseError( "StoredFile::_idFromGunid: no such global unique id ($gunid)" ); return $id; } /** * Return suitable extension.
* TODO: make it general - is any tool for it? * * @return string file extension without a dot */ function _getExt() { switch($this->type){ case"audio/mpeg": $ext="mp3"; break; case"audio/x-wave": $ext="wav"; break; case"application/x-ogg": $ext="ogg"; break; default: $ext="bin"; break; } return $ext; } /** * Get filetype from global id * * @param gunid string, optional, global unique id of file */ function _getType($gunid) { return $this->dbc->getOne("SELECT type FROM {$this->filesTable} WHERE gunid='$gunid'"); } /** * Get and optionaly create subdirectory in real filesystem for storing * raw media data * */ function _getResDir() { $resDir="{$this->gb->storageDir}/".substr($this->gunid, 0, 3); // see Transport::_getResDir too for resDir name create code if(!file_exists($resDir)){ mkdir($resDir, 02775); chmod($resDir, 02775); } return $resDir; } /** * Get real filename of raw media data * * @see RawMediaData */ function _getRealRADFname() { return $this->rmd->getFname(); } /** * Create and return name for temporary symlink.
* TODO: Should be more unique * */ function _getAccessFname($sessid, $ext='EXT') { $spart = md5("{$sessid}_{$this->gunid}"); return "{$this->accessDir}/$spart.$ext"; } } ?>