diff --git a/airtime_mvc/application/models/RabbitMq.php b/airtime_mvc/application/models/RabbitMq.php index bbabb6440..f81055460 100644 --- a/airtime_mvc/application/models/RabbitMq.php +++ b/airtime_mvc/application/models/RabbitMq.php @@ -79,10 +79,11 @@ class Application_Model_RabbitMq self::sendMessage($exchange, 'direct', true, $data); } - public static function SendMessageToAnalyzer($tmpFilePath, $importedStorageDirectory, $originalFilename, + public static function SendUploadMessageToAnalyzer($tmpFilePath, $importedStorageDirectory, $originalFilename, $callbackUrl, $apiKey, $messageType) { $exchange = 'airtime-uploads'; + $data['message_type'] = $messageType; $data['tmp_file_path'] = $tmpFilePath; $data['import_directory'] = $importedStorageDirectory; @@ -93,4 +94,17 @@ class Application_Model_RabbitMq $jsonData = json_encode($data); self::sendMessage($exchange, 'topic', false, $jsonData, 'airtime-uploads'); } + + public static function SendDeleteMessageToAnalyzer($callbackUrl, $objectName, $apiKey, $messageType) + { + $exchange = 'airtime-uploads'; + + $data['message_type'] = $messageType; + $data['api_key'] = $apiKey; + $data['object_name'] = $objectName; + $data['callback_url'] = $callbackUrl; + + $jsonData = json_encode($data); + self::sendMessage($exchange, 'topic', false, $jsonData, 'airtime-uploads'); + } } diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index 76415e1da..d5333afee 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -363,9 +363,7 @@ SQL; } /** - * Delete stored virtual file - * - * @param boolean $p_deleteFile + * Deletes the physical file from the local file system or from the cloud * */ public function delete() @@ -383,10 +381,11 @@ SQL; if (!$isAdminOrPM && $this->getFileOwnerId() != $user->getId()) { throw new FileNoPermissionException(); } + $file_id = $this->_file->getDbId(); + Logging::info("User ".$user->getLogin()." is deleting file: ".$this->_file->getDbTrackTitle()." - file id: ".$file_id); $isInCloud = $this->isInCloud(); - $filesize = $this->getFileSize(); if (file_exists($filepath) && !$isInCloud) { try { unlink($filepath); @@ -394,23 +393,46 @@ SQL; Logging::error($e->getMessage()); return; } + + $this->doFileDeletionCleanup($this->getFileSize()); + } elseif ($isInCloud) { - //delete file from cloud + //Dispatch a message to airtime_analyzer through RabbitMQ, + //notifying it that we need to delete a file from the cloud + $CC_CONFIG = Config::getConfig(); + $apiKey = $CC_CONFIG["apiKey"][0]; + + $callbackUrl = 'http://'.$_SERVER['HTTP_HOST'].'/rest/media/'.$file_id.'/delete-success'; + + Application_Model_RabbitMq::SendDeleteMessageToAnalyzer( + $callbackUrl, $this->_file->getDbResourceId(), $apiKey, 'delete'); } + } + /* + * This function handles all the actions required when a file is deleted + */ + public function doFileDeletionCleanup($filesize) + { //Update the user's disk usage Application_Model_Preference::updateDiskUsage(-1 * $filesize); + + //Explicitly update any playlist's and block's length that contains + //the file getting deleted + self::updateBlockAndPlaylistLength($this->_file->getDbId()); + + //delete the file record from cc_files + $this->_file->delete(); + } - Logging::info("User ".$user->getLogin()." is deleting file: ".$this->_file->getDbTrackTitle()." - file id: ".$this->_file->getDbId()); - // set hidden flag to true - //$this->_file->setDbHidden(true); - //$this->_file->setDbFileExists(false); - //$this->_file->save(); - - // need to explicitly update any playlist's and block's length - // that contains the file getting deleted - $fileId = $this->_file->getDbId(); - $plRows = CcPlaylistcontentsQuery::create()->filterByDbFileId()->find(); + /* + * This function is meant to be called when a file is getting + * deleted from the library. It re-calculates the length of + * all blocks and playlists that contained the deleted file. + */ + public static function updateBlockAndPlaylistLength($fileId) + { + $plRows = CcPlaylistcontentsQuery::create()->filterByDbFileId($fileId)->find(); foreach ($plRows as $row) { $pl = CcPlaylistQuery::create()->filterByDbId($row->getDbPlaylistId($fileId))->findOne(); $pl->setDbLength($pl->computeDbLength(Propel::getConnection(CcPlaylistPeer::DATABASE_NAME))); @@ -423,8 +445,6 @@ SQL; $bl->setDbLength($bl->computeDbLength(Propel::getConnection(CcBlockPeer::DATABASE_NAME))); $bl->save(); } - - $this->_file->delete(); } /** diff --git a/airtime_mvc/application/modules/rest/Bootstrap.php b/airtime_mvc/application/modules/rest/Bootstrap.php index 31691ca96..f0e0d773c 100644 --- a/airtime_mvc/application/modules/rest/Bootstrap.php +++ b/airtime_mvc/application/modules/rest/Bootstrap.php @@ -33,5 +33,18 @@ class Rest_Bootstrap extends Zend_Application_Module_Bootstrap ) ); $router->addRoute('clear', $clearLibraryRoute); + + $deleteSuccessRoute = new Zend_Controller_Router_Route( + 'rest/media/:id/delete-success', + array( + 'controller' => 'media', + 'action' => 'delete-success', + 'module' => 'rest' + ), + array( + 'id' => '\d+' + ) + ); + $router->addRoute('delete-success', $deleteSuccessRoute); } } diff --git a/airtime_mvc/application/modules/rest/controllers/MediaController.php b/airtime_mvc/application/modules/rest/controllers/MediaController.php index 16e084e80..9d0dd48fc 100644 --- a/airtime_mvc/application/modules/rest/controllers/MediaController.php +++ b/airtime_mvc/application/modules/rest/controllers/MediaController.php @@ -29,6 +29,9 @@ class Rest_MediaController extends Zend_Rest_Controller public function init() { $this->view->layout()->disableLayout(); + + $ajaxContext = $this->_helper->getHelper('AjaxContext'); + $ajaxContext->addActionContext('delete-success', 'json'); } public function indexAction() @@ -278,7 +281,7 @@ class Rest_MediaController extends Zend_Rest_Controller //get the filesize and update disk_usage $storedFile = Application_Model_StoredFile::RecallById($file->getDbId()); - Application_Model_Preference::updateDiskUsage($storedFile->getFileSize()); + Application_Model_Preference::updateDiskUsage($requestData["filesize"]); $this->getResponse() ->setHttpResponseCode(200) @@ -304,9 +307,8 @@ class Rest_MediaController extends Zend_Rest_Controller if ($file) { $con = Propel::getConnection(); $storedFile = new Application_Model_StoredFile($file, $con); - if ($storedFile->existsOnDisk()) { - $storedFile->delete(); //TODO: This checks your session permissions... Make it work without a session? - } + $storedFile->delete(); //TODO: This checks your session permissions... Make it work without a session? + $this->getResponse() ->setHttpResponseCode(204); } else { @@ -314,6 +316,26 @@ class Rest_MediaController extends Zend_Rest_Controller } } + public function deleteSuccessAction() + { + if (!$this->verifyAuth(true, true)) + { + return; + } + + $id = $this->getId(); + if (!$id) { + return; + } + + $requestData = json_decode($this->getRequest()->getRawBody(), true); + + $con = Propel::getConnection(); + $storedFile = new Application_Model_StoredFile(CcFilesQuery::create()->findPk($id), $con); + + $storedFile->doFileDeletionCleanup($requestData["filesize"]); + } + private function getId() { if (!$id = $this->_getParam('id', false)) { @@ -479,9 +501,9 @@ class Rest_MediaController extends Zend_Rest_Controller //Dispatch a message to airtime_analyzer through RabbitMQ, //notifying it that there's a new upload to process! - Application_Model_RabbitMq::SendMessageToAnalyzer($newTempFilePath, + Application_Model_RabbitMq::SendUploadMessageToAnalyzer($newTempFilePath, $importedStorageDirectory, basename($originalFilename), - $callbackUrl, $apiKey); + $callbackUrl, $apiKey, 'upload'); } private function getOwnerId() diff --git a/airtime_mvc/application/modules/rest/views/scripts/media/delete-success.phtml b/airtime_mvc/application/modules/rest/views/scripts/media/delete-success.phtml new file mode 100644 index 000000000..e69de29bb diff --git a/python_apps/airtime_analyzer/airtime_analyzer/cloud_storage_uploader.py b/python_apps/airtime_analyzer/airtime_analyzer/cloud_storage_uploader.py index 3cd7793df..76e9e6257 100644 --- a/python_apps/airtime_analyzer/airtime_analyzer/cloud_storage_uploader.py +++ b/python_apps/airtime_analyzer/airtime_analyzer/cloud_storage_uploader.py @@ -3,7 +3,7 @@ import logging import uuid from libcloud.storage.providers import get_driver -from libcloud.storage.types import Provider, ContainerDoesNotExistError +from libcloud.storage.types import Provider, ContainerDoesNotExistError, ObjectDoesNotExistError class CloudStorageUploader: def __init__(self, provider, bucket, api_key, api_key_secret): @@ -17,15 +17,16 @@ class CloudStorageUploader: file_name, extension = os.path.splitext(file_base_name) object_name = "%s_%s%s" % (file_name, str(uuid.uuid4()), extension) - cls = get_driver(getattr(Provider, self._provider)) - driver = cls(self._api_key, self._api_key_secret) + #cls = get_driver(getattr(Provider, self._provider)) + driver = self.get_cloud_driver() try: container = driver.get_container(self._bucket) except ContainerDoesNotExistError: container = driver.create_container(self._bucket) - extra = {'meta_data': {'filename': file_base_name}} + extra = {'meta_data': {'filename': file_base_name}, + 'acl': 'public-read-write'} with open(audio_file_path, 'rb') as iterator: obj = driver.upload_object_via_stream(iterator=iterator, @@ -33,14 +34,32 @@ class CloudStorageUploader: object_name=object_name, extra=extra) + + metadata["filesize"] = os.path.getsize(audio_file_path) + '''remove file from organize directory''' try: os.remove(audio_file_path) except OSError: - logging.info("Could not remove %s" % audio_file_path) + logging.info("Could not remove %s from organize directory" % audio_file_path) metadata["s3_object_name"] = object_name return metadata - def delete_obj(self, object_name): - pass \ No newline at end of file + def delete_obj(self, obj_name): + driver = self.get_cloud_driver() + + return_msg = dict() + return_msg["success"] = False + try: + cloud_obj = driver.get_object(container_name=self._bucket, + object_name=obj_name) + return_msg["filesize"] = getattr(cloud_obj, 'size') + return_msg["success"] = driver.delete_object(obj=cloud_obj) + return return_msg + except ObjectDoesNotExistError: + logging.info("Could not find object on %s" % self._provider) + + def get_cloud_driver(self): + cls = get_driver(getattr(Provider, self._provider)) + return cls(self._api_key, self._api_key_secret) diff --git a/python_apps/airtime_analyzer/airtime_analyzer/message_listener.py b/python_apps/airtime_analyzer/airtime_analyzer/message_listener.py index 45db99150..f4df7f360 100644 --- a/python_apps/airtime_analyzer/airtime_analyzer/message_listener.py +++ b/python_apps/airtime_analyzer/airtime_analyzer/message_listener.py @@ -162,19 +162,26 @@ class MessageListener: ''' try: msg_dict = json.loads(body) - audio_file_path = msg_dict["tmp_file_path"] - #final_file_path = msg_dict["final_file_path"] - import_directory = msg_dict["import_directory"] - original_filename = msg_dict["original_filename"] - callback_url = msg_dict["callback_url"] api_key = msg_dict["api_key"] message_type = msg_dict["message_type"] + callback_url = msg_dict["callback_url"] - if event_type == "upload": + if message_type == "upload": + audio_file_path = msg_dict["tmp_file_path"] + import_directory = msg_dict["import_directory"] + original_filename = msg_dict["original_filename"] + audio_metadata = self.spawn_analyzer_process(audio_file_path, import_directory, original_filename) StatusReporter.report_success_to_callback_url(callback_url, api_key, audio_metadata) - elif event_type == "delete": - pass + elif message_type == "delete": + object_name = msg_dict["object_name"] + csu = CloudStorageUploader(self._provider, self._bucket, self._api_key, self._api_key_secret) + response = csu.delete_obj(object_name) + if response["success"]: + audio_metadata = dict() + audio_metadata["delete_success"] = True + audio_metadata["filesize"] = response["filesize"] + StatusReporter.report_success_to_callback_url(callback_url, api_key, audio_metadata) except KeyError as e: # A field in msg_dict that we needed was missing (eg. audio_file_path)