From 826ae17552895a26a1f6d6e0e5f62d400a99cd62 Mon Sep 17 00:00:00 2001 From: Duncan Sommerville Date: Wed, 4 Feb 2015 15:09:27 -0500 Subject: [PATCH] SAAS-580 - Fixed routing issue causing show art to not upload; changed ShowController to be more RESTful --- airtime_mvc/application/Bootstrap.php | 2 +- airtime_mvc/application/configs/ACL.php | 2 + .../controllers/ScheduleController.php | 4 +- .../application/modules/rest/Bootstrap.php | 29 +- .../rest/controllers/ShowController.php | 294 ------------------ .../rest/controllers/ShowImageController.php | 287 +++++++++++++++++ .../application/services/ShowService.php | 6 +- .../public/js/airtime/schedule/add-show.js | 8 +- 8 files changed, 300 insertions(+), 332 deletions(-) delete mode 100644 airtime_mvc/application/modules/rest/controllers/ShowController.php create mode 100644 airtime_mvc/application/modules/rest/controllers/ShowImageController.php diff --git a/airtime_mvc/application/Bootstrap.php b/airtime_mvc/application/Bootstrap.php index 1e1d3f9a9..988e5ee4a 100644 --- a/airtime_mvc/application/Bootstrap.php +++ b/airtime_mvc/application/Bootstrap.php @@ -27,7 +27,7 @@ require_once __DIR__.'/forms/helpers/ValidationTypes.php'; require_once __DIR__.'/forms/helpers/CustomDecorators.php'; require_once __DIR__.'/controllers/plugins/RabbitMqPlugin.php'; require_once __DIR__.'/controllers/plugins/Maintenance.php'; -require_once __DIR__.'/modules/rest/controllers/ShowController.php'; +require_once __DIR__.'/modules/rest/controllers/ShowImageController.php'; require_once __DIR__.'/modules/rest/controllers/MediaController.php'; require_once (APPLICATION_PATH."/logging/Logging.php"); diff --git a/airtime_mvc/application/configs/ACL.php b/airtime_mvc/application/configs/ACL.php index 158736be9..fd38008a6 100644 --- a/airtime_mvc/application/configs/ACL.php +++ b/airtime_mvc/application/configs/ACL.php @@ -34,6 +34,7 @@ $ccAcl->add(new Zend_Acl_Resource('library')) ->add(new Zend_Acl_Resource('upgrade')) ->add(new Zend_Acl_Resource('downgrade')) ->add(new Zend_Acl_Resource('rest:media')) + ->add(new Zend_Acl_Resource('rest:show-image')) ->add(new Zend_Acl_Resource('billing')) ->add(new Zend_Acl_Resource('provisioning')); @@ -54,6 +55,7 @@ $ccAcl->allow('G', 'index') ->allow('G', 'provisioning') ->allow('G', 'downgrade') ->allow('G', 'rest:media') + ->allow('G', 'rest:show-image') ->allow('H', 'preference', 'is-import-in-progress') ->allow('H', 'usersettings') ->allow('H', 'plupload') diff --git a/airtime_mvc/application/controllers/ScheduleController.php b/airtime_mvc/application/controllers/ScheduleController.php index 8071bfe99..452788b23 100644 --- a/airtime_mvc/application/controllers/ScheduleController.php +++ b/airtime_mvc/application/controllers/ScheduleController.php @@ -533,7 +533,7 @@ class ScheduleController extends Zend_Controller_Action if ($service_showForm->validateShowForms($forms, $data, $validateStartDate, $originalShowStartDateTime, true, $data["add_show_instance_id"])) { - // Get the show ID from the show service to pass as a parameter to the RESTful ShowController + // Get the show ID from the show service to pass as a parameter to the RESTful ShowImageController $this->view->showId = $service_show->addUpdateShow($data); $this->view->addNewShow = true; @@ -587,7 +587,7 @@ class ScheduleController extends Zend_Controller_Action $this->view->addNewShow = true; if ($service_showForm->validateShowForms($forms, $data)) { - // Get the show ID from the show service to pass as a parameter to the RESTful ShowController + // Get the show ID from the show service to pass as a parameter to the RESTful ShowImageController $this->view->showId = $service_show->addUpdateShow($data); //send new show forms to the user diff --git a/airtime_mvc/application/modules/rest/Bootstrap.php b/airtime_mvc/application/modules/rest/Bootstrap.php index 93cc1b38c..af4f0ba98 100644 --- a/airtime_mvc/application/modules/rest/Bootstrap.php +++ b/airtime_mvc/application/modules/rest/Bootstrap.php @@ -8,7 +8,7 @@ class Rest_Bootstrap extends Zend_Application_Module_Bootstrap $router = $front->getRouter(); $restRoute = new Zend_Rest_Route($front, array(), array( - 'rest'=> array('media', 'show'))); + 'rest'=> array('media', 'show-image'))); assert($router->addRoute('rest', $restRoute)); /** MediaController Routes **/ @@ -34,32 +34,5 @@ class Rest_Bootstrap extends Zend_Application_Module_Bootstrap ) ); $router->addRoute('clear', $clearLibraryRoute); - - /** ShowController Routes **/ - $uploadImageRoute = new Zend_Controller_Router_Route( - 'rest/show/:id/upload-image', - array( - 'controller' => 'show', - 'action' => 'upload-image', - 'module' => 'rest' - ), - array( - 'id' => '\d+' - ) - ); - $router->addRoute('upload-image', $uploadImageRoute); - - $deleteImageRoute = new Zend_Controller_Router_Route( - 'rest/show/:id/delete-image', - array( - 'controller' => 'show', - 'action' => 'delete-image', - 'module' => 'rest' - ), - array( - 'id' => '\d+' - ) - ); - $router->addRoute('delete-image', $deleteImageRoute); } } diff --git a/airtime_mvc/application/modules/rest/controllers/ShowController.php b/airtime_mvc/application/modules/rest/controllers/ShowController.php deleted file mode 100644 index 5293b07cb..000000000 --- a/airtime_mvc/application/modules/rest/controllers/ShowController.php +++ /dev/null @@ -1,294 +0,0 @@ -view->layout()->disableLayout(); - // Remove reliance on .phtml files to render requests - $this->_helper->viewRenderer->setNoRender(true); - } - - /* - * TODO shift functionality from add-show.js and ScheduleController to here, - * and have a referenceable Show object - */ - - public function indexAction() { - Logging::info("INDEX action received"); - } - - public function getAction() { - Logging::info("GET action received"); - } - - public function putAction() { - Logging::info("PUT action received"); - } - - public function postAction() { - Logging::info("POST action received"); - } - - public function deleteAction() { - Logging::info("DELETE action received"); - } - - public function uploadImageAction() - { - if (!RestAuth::verifyAuth(true, true)) - { - $this->getResponse() - ->setHttpResponseCode(401) - ->appendBody("Authentication failed"); - return; - } - - $showId = $this->getShowId(); - - if (!$showId) { - $this->getResponse() - ->setHttpResponseCode(400) - ->appendBody("No show ID provided"); - return; - } - - try { - $path = $this->processUploadedImage($showId, $_FILES["file"]["tmp_name"], $_FILES["file"]["name"]); - } catch (Exception $e) { - $this->getResponse() - ->setHttpResponseCode(500) - ->appendBody("Error processing image: " . $e->getMessage()); - } - - $show = CcShowQuery::create()->findPk($showId); - - try { - $con = Propel::getConnection(); - $con->beginTransaction(); - - $show->setDbImagePath($path); - $show->save(); - - $con->commit(); - } catch (Exception $e) { - $con->rollBack(); - $this->getResponse() - ->setHttpResponseCode(500) - ->appendBody("Couldn't add show image: " . $e->getMessage()); - } - - $this->getResponse() - ->setHttpResponseCode(201); - } - - public function deleteImageAction() - { - if (!RestAuth::verifyAuth(true, true)) - { - $this->getResponse() - ->setHttpResponseCode(401) - ->appendBody("Authentication failed"); - return; - } - - $showId = $this->getShowId(); - - if (!$showId) { - $this->getResponse() - ->setHttpResponseCode(400) - ->appendBody("No show ID provided"); - return; - } - - try { - Rest_ShowController::deleteShowImagesFromStor($showId); - } catch (Exception $e) { - $this->getResponse() - ->setHttpResponseCode(500) - ->appendBody("Error processing image: " . $e->getMessage()); - } - - $show = CcShowQuery::create()->findPk($showId); - - try { - $con = Propel::getConnection(); - $con->beginTransaction(); - - $show->setDbImagePath(null); - $show->save(); - - $con->commit(); - } catch (Exception $e) { - $con->rollBack(); - $this->getResponse() - ->setHttpResponseCode(500) - ->appendBody("Couldn't remove show image: " . $e->getMessage()); - } - - $this->getResponse() - ->setHttpResponseCode(201); - } - - /** - * Verify and process an uploaded image file, copying it into - * .../stor/imported/:owner-id/show-images/:show-id/ to differentiate between - * individual users and shows - * - * @param unknown $tempFilePath - * - temporary filepath assigned to the upload generally of the form /tmp/:tmp_name - * @param unknown - * - $originalFilename the file name at time of upload - * @throws Exception - * - when a file with an unsupported file extension is uploaded or an - * error occurs in copyFileToStor - */ - private function processUploadedImage($showId, $tempFilePath, $originalFilename) - { - $ownerId = RestAuth::getOwnerId(); - - $CC_CONFIG = Config::getConfig(); - $apiKey = $CC_CONFIG["apiKey"][0]; - - $tempFileName = basename($tempFilePath); - - //Only accept files with a file extension that we support. - $fileExtension = $this->getFileExtension($originalFilename, $tempFilePath); - - if (!in_array(strtolower($fileExtension), explode(",", "jpg,png,gif,jpeg"))) - { - @unlink($tempFilePath); - throw new Exception("Bad file extension."); - } - - $storDir = Application_Model_MusicDir::getStorDir(); - $importedStorageDirectory = $storDir->getDirectory() . "imported/" . $ownerId . "/show-images/" . $showId; - - try { - $importedStorageDirectory = $this->copyFileToStor($tempFilePath, $importedStorageDirectory, $fileExtension); - } catch (Exception $e) { - @unlink($tempFilePath); - throw new Exception("Failed to copy file: " . $e->getMessage()); - } - - return $importedStorageDirectory; - } - - private function getFileExtension($originalFileName, $tempFilePath) - { - // Don't trust the extension - get the MIME-type instead - $fileInfo = finfo_open(); - $mime = finfo_file($fileInfo, $tempFilePath, FILEINFO_MIME_TYPE); - return $this->getExtensionFromMime($mime); - } - - private function getExtensionFromMime($mime) - { - $extensions = array( - 'image/jpeg' => 'jpg', - 'image/png' => 'png', - 'image/gif' => 'gif' - ); - - return $extensions[$mime]; - } - - private function copyFileToStor($tempFilePath, $importedStorageDirectory, $fileExtension) - { - $image_file = $tempFilePath; - - // check if show image dir exists and if not, create one - if (!file_exists($importedStorageDirectory)) { - if (!mkdir($importedStorageDirectory, 0777, true)) { - throw new Exception("Failed to create storage directory."); - } - } - - if (chmod($image_file, 0644) === false) { - Logging::info("Warning: couldn't change permissions of $image_file to 0644"); - } - - $newFileName = substr($tempFilePath, strrpos($tempFilePath, "/")).".".$fileExtension; - - // Did all the checks for real, now trying to copy - $image_stor = Application_Common_OsPath::join($importedStorageDirectory, $newFileName); - Logging::info("Adding image: " . $image_stor); - Logging::info("copyFileToStor: moving file $image_file to $image_stor"); - - if (@rename($image_file, $image_stor) === false) { - //something went wrong likely there wasn't enough space in . - //the audio_stor to move the file too warn the user that . - //the file wasn't uploaded and they should check if there . - //is enough disk space . - unlink($image_file); //remove the file after failed rename - - throw new Exception("The file was not uploaded, this error can occur if the computer " - ."hard drive does not have enough disk space or the stor " - ."directory does not have correct write permissions."); - } - - return $image_stor; - } - - // Should this be an endpoint instead? - public static function deleteShowImagesFromStor($showId) { - $ownerId = RestAuth::getOwnerId(); - - $storDir = Application_Model_MusicDir::getStorDir(); - $importedStorageDirectory = $storDir->getDirectory() . "imported/" . $ownerId . "/show-images/" . $showId; - - Logging::info("Deleting images from " . $importedStorageDirectory); - - // to be safe in case image uploading functionality is extended later - if (!file_exists($importedStorageDirectory)) { - Logging::info("No uploaded images for show with id " . $showId); - return true; - } else { - return Rest_ShowController::delTree($importedStorageDirectory); - } - } - - // from a note @ http://php.net/manual/en/function.rmdir.php - private static function delTree($dir) { - $files = array_diff(scandir($dir), array('.','..')); - foreach ($files as $file) { - (is_dir("$dir/$file")) ? delTree("$dir/$file") : unlink("$dir/$file"); - } - return rmdir($dir); - } - - /** - * Fetch the id parameter from the request. - * - * @return boolean|unknown false if the show id wasn't - * provided, otherwise returns the id - */ - private function getShowId() - { - if (!$id = $this->_getParam('id', false)) { - $resp = $this->getResponse(); - $resp->setHttpResponseCode(400); - $resp->appendBody("ERROR: No show ID specified."); - return false; - } - return $id; - } - -} \ No newline at end of file diff --git a/airtime_mvc/application/modules/rest/controllers/ShowImageController.php b/airtime_mvc/application/modules/rest/controllers/ShowImageController.php new file mode 100644 index 000000000..856b7fb55 --- /dev/null +++ b/airtime_mvc/application/modules/rest/controllers/ShowImageController.php @@ -0,0 +1,287 @@ +view->layout()->disableLayout(); + // Remove reliance on .phtml files to render requests + $this->_helper->viewRenderer->setNoRender(true); + } + + public function indexAction() { + Logging::info("INDEX action received"); + } + + public function getAction() { + Logging::info("GET action received"); + } + + public function putAction() { + Logging::info("PUT action received"); + } + + public function postAction() { + if (!RestAuth::verifyAuth(true, true)) + { + $this->getResponse() + ->setHttpResponseCode(401) + ->appendBody("Authentication failed"); + return; + } + + $showId = $this->getShowId(); + + if (!$showId) { + $this->getResponse() + ->setHttpResponseCode(400) + ->appendBody("No show ID provided"); + return; + } + + try { + $path = $this->processUploadedImage($showId, $_FILES["file"]["tmp_name"], $_FILES["file"]["name"]); + } catch (Exception $e) { + $this->getResponse() + ->setHttpResponseCode(500) + ->appendBody("Error processing image: " . $e->getMessage()); + } + + $show = CcShowQuery::create()->findPk($showId); + + try { + $con = Propel::getConnection(); + $con->beginTransaction(); + + $show->setDbImagePath($path); + $show->save(); + + $con->commit(); + } catch (Exception $e) { + $con->rollBack(); + $this->getResponse() + ->setHttpResponseCode(500) + ->appendBody("Couldn't add show image: " . $e->getMessage()); + } + + $this->getResponse() + ->setHttpResponseCode(201); + } + + public function deleteAction() { + if (!RestAuth::verifyAuth(true, true)) + { + $this->getResponse() + ->setHttpResponseCode(401) + ->appendBody("Authentication failed"); + return; + } + + $showId = $this->getShowId(); + + if (!$showId) { + $this->getResponse() + ->setHttpResponseCode(400) + ->appendBody("No show ID provided"); + return; + } + + try { + self::deleteShowImagesFromStor($showId); + } catch (Exception $e) { + $this->getResponse() + ->setHttpResponseCode(500) + ->appendBody("Error processing image: " . $e->getMessage()); + } + + $show = CcShowQuery::create()->findPk($showId); + + try { + $con = Propel::getConnection(); + $con->beginTransaction(); + + $show->setDbImagePath(null); + $show->save(); + + $con->commit(); + } catch (Exception $e) { + $con->rollBack(); + $this->getResponse() + ->setHttpResponseCode(500) + ->appendBody("Couldn't remove show image: " . $e->getMessage()); + } + + $this->getResponse() + ->setHttpResponseCode(201); + } + + public function uploadImageAction() + { + } + + public function deleteImageAction() + { + } + + /** + * Verify and process an uploaded image file, copying it into + * .../stor/imported/:owner-id/show-images/:show-id/ to differentiate between + * individual users and shows + * + * @param unknown $tempFilePath + * - temporary filepath assigned to the upload generally of the form /tmp/:tmp_name + * @param unknown + * - $originalFilename the file name at time of upload + * @throws Exception + * - when a file with an unsupported file extension is uploaded or an + * error occurs in copyFileToStor + */ + private function processUploadedImage($showId, $tempFilePath, $originalFilename) + { + $ownerId = RestAuth::getOwnerId(); + + $CC_CONFIG = Config::getConfig(); + $apiKey = $CC_CONFIG["apiKey"][0]; + + $tempFileName = basename($tempFilePath); + + //Only accept files with a file extension that we support. + $fileExtension = $this->getFileExtension($originalFilename, $tempFilePath); + + if (!in_array(strtolower($fileExtension), explode(",", "jpg,png,gif,jpeg"))) + { + @unlink($tempFilePath); + throw new Exception("Bad file extension."); + } + + $storDir = Application_Model_MusicDir::getStorDir(); + $importedStorageDirectory = $storDir->getDirectory() . "imported/" . $ownerId . "/show-images/" . $showId; + + try { + $importedStorageDirectory = $this->copyFileToStor($tempFilePath, $importedStorageDirectory, $fileExtension); + } catch (Exception $e) { + @unlink($tempFilePath); + throw new Exception("Failed to copy file: " . $e->getMessage()); + } + + return $importedStorageDirectory; + } + + private function getFileExtension($originalFileName, $tempFilePath) + { + // Don't trust the extension - get the MIME-type instead + $fileInfo = finfo_open(); + $mime = finfo_file($fileInfo, $tempFilePath, FILEINFO_MIME_TYPE); + return $this->getExtensionFromMime($mime); + } + + private function getExtensionFromMime($mime) + { + $extensions = array( + 'image/jpeg' => 'jpg', + 'image/png' => 'png', + 'image/gif' => 'gif' + ); + + return $extensions[$mime]; + } + + private function copyFileToStor($tempFilePath, $importedStorageDirectory, $fileExtension) + { + $image_file = $tempFilePath; + + // check if show image dir exists and if not, create one + if (!file_exists($importedStorageDirectory)) { + if (!mkdir($importedStorageDirectory, 0777, true)) { + throw new Exception("Failed to create storage directory."); + } + } + + if (chmod($image_file, 0644) === false) { + Logging::info("Warning: couldn't change permissions of $image_file to 0644"); + } + + $newFileName = substr($tempFilePath, strrpos($tempFilePath, "/")).".".$fileExtension; + + // Did all the checks for real, now trying to copy + $image_stor = Application_Common_OsPath::join($importedStorageDirectory, $newFileName); + Logging::info("Adding image: " . $image_stor); + Logging::info("copyFileToStor: moving file $image_file to $image_stor"); + + if (@rename($image_file, $image_stor) === false) { + //something went wrong likely there wasn't enough space in . + //the audio_stor to move the file too warn the user that . + //the file wasn't uploaded and they should check if there . + //is enough disk space . + unlink($image_file); //remove the file after failed rename + + throw new Exception("The file was not uploaded, this error can occur if the computer " + ."hard drive does not have enough disk space or the stor " + ."directory does not have correct write permissions."); + } + + return $image_stor; + } + + // Should this be an endpoint instead? + public static function deleteShowImagesFromStor($showId) { + $ownerId = RestAuth::getOwnerId(); + + $storDir = Application_Model_MusicDir::getStorDir(); + $importedStorageDirectory = $storDir->getDirectory() . "imported/" . $ownerId . "/show-images/" . $showId; + + Logging::info("Deleting images from " . $importedStorageDirectory); + + // to be safe in case image uploading functionality is extended later + if (!file_exists($importedStorageDirectory)) { + Logging::info("No uploaded images for show with id " . $showId); + return true; + } else { + return self::delTree($importedStorageDirectory); + } + } + + // from a note @ http://php.net/manual/en/function.rmdir.php + private static function delTree($dir) { + $files = array_diff(scandir($dir), array('.','..')); + foreach ($files as $file) { + (is_dir("$dir/$file")) ? delTree("$dir/$file") : unlink("$dir/$file"); + } + return rmdir($dir); + } + + /** + * Fetch the id parameter from the request. + * + * @return boolean|unknown false if the show id wasn't + * provided, otherwise returns the id + */ + private function getShowId() + { + if (!$id = $this->_getParam('id', false)) { + $resp = $this->getResponse(); + $resp->setHttpResponseCode(400); + $resp->appendBody("ERROR: No show ID specified."); + return false; + } + return $id; + } + +} \ No newline at end of file diff --git a/airtime_mvc/application/services/ShowService.php b/airtime_mvc/application/services/ShowService.php index 9bd509bb8..ab4cb7ea2 100644 --- a/airtime_mvc/application/services/ShowService.php +++ b/airtime_mvc/application/services/ShowService.php @@ -209,7 +209,7 @@ class Application_Service_ShowService // Only delete the previous logo if a new one is being uploaded if (array_key_exists("add_show_logo_name", $showData) && $showData["add_show_logo_name"] !== "") { - if (!Rest_ShowController::deleteShowImagesFromStor($showId)) { + if (!Rest_ShowImageController::deleteShowImagesFromStor($showId)) { throw new Exception("Error deleting show images"); } } @@ -267,7 +267,7 @@ class Application_Service_ShowService Logging::info($e->getMessage()); } - // Added to pass along to the RESTful ShowController + // Added to pass along to the RESTful ShowImageController return $this->ccShow->getDbId(); } @@ -763,7 +763,7 @@ SQL; // Delete show images $showId = $ccShowInstance->getDbShowId(); - if (!Rest_ShowController::deleteShowImagesFromStor($showId)) { + if (!Rest_ShowImageController::deleteShowImagesFromStor($showId)) { throw new Exception("Error deleting show images"); } diff --git a/airtime_mvc/public/js/airtime/schedule/add-show.js b/airtime_mvc/public/js/airtime/schedule/add-show.js index d69d1f4c9..aee048fd8 100644 --- a/airtime_mvc/public/js/airtime/schedule/add-show.js +++ b/airtime_mvc/public/js/airtime/schedule/add-show.js @@ -652,7 +652,7 @@ function setAddShowEvents(form) { return true; } - // Duplicate of the function in ShowController - provide it as a GET endpoint? + // Duplicate of the function in ShowImageController function validateMimeType(mime) { var extensions = [ 'image/jpeg', @@ -668,12 +668,12 @@ function setAddShowEvents(form) { var showId = $("#add_show_id").attr("value"); if (showId && $("#add_show_logo_current").attr("src") !== "") { - var action = '/rest/show/' + showId + '/delete-image'; + var action = '/rest/show-image?id=' + showId; $.ajax({ url: action, data: '', - type: 'POST', + type: 'DELETE', success: function() { $("#add_show_logo_current").prop("src", "") $("[id^=add_show_logo_current]").hide(); @@ -748,7 +748,7 @@ function setAddShowEvents(form) { data: {format: "json", data: data, hosts: hosts, days: days}, success: function(json) { if (json.showId && image) { // Successfully added the show, and it contains an image to upload - var imageAction = '/rest/show/' + json.showId + '/upload-image'; + var imageAction = '/rest/show-image?id=' + json.showId; // perform a second xhttprequest in order to send the show image $.ajax({