sintonia/legacy/application/modules/rest/controllers/ShowImageController.php

332 lines
10 KiB
PHP
Raw Permalink Normal View History

<?php
/**
* Controller class for handling ShowImage-related functionality.
* Changelog:
* 16/09/2014 : v1.0 Created class skeleton, added image upload functionality
* 18/09/2014 : v1.1 Changed auth references to static calls
* 06/02/2015 : v1.2 Changed endpoints to be more RESTful, changed classname to
* better reflect functionality
2021-10-11 16:10:47 +02:00
* 09/02/2015 : v1.2.1 Added more comments.
*
* @author sourcefabric
2021-10-11 16:10:47 +02:00
*
* @version 1.2.1
*/
2021-10-11 16:10:47 +02:00
class Rest_ShowImageController extends Zend_Rest_Controller
{
public function init()
{
// Remove layout dependencies
$this->view->layout()->disableLayout();
// Remove reliance on .phtml files to render requests
$this->_helper->viewRenderer->setNoRender(true);
}
Vendorize ZF1, fix PHPUnit and configure travis This a a rather large commit due to the nature of the stuff it is touching. To get PHPUnit up and running again I had to update some deps and I did so by vendorizing them. The vendorizing of zf1 makes sense since distros are already considering to drop it from their repos. * [x] install vendorized zf1 with composer * [x] load composer autoloader before zf1 * [x] Implement headAction for all Zend_Rest_Controller based controllers * [x] switch to yml dataset to get around string only limitations of xml sets (also removed warning in readme) * [x] use year 2044 as hardcoded date for tests since it is in the future and has the same days like previously used 2016 * [x] make tests easier to run when accessing phpunit directly * [x] clean up test helper to always use airtime.conf * [x] switch test dbname to libretime_test * [x] test db username password switched to libretime/libretime * [x] install phpunit with composer in a clear version (make tests easier to reproduce on other platforms) * [x] remove local libs from airtime repo (most of airtime_mvc/library was not needed of in vendor already) * [x] configure composer autoloading and use it (also removed requires that are not needed anymore) * [x] add LibreTime prefix for FileNotFoundException (phing had a similar class and these are all pre-namespace style) * [x] add .travis.yml file * [x] make etc and logdir configurable with LIBRETIME_CONF_DIR and LIBRETIME_LOG_DIR env (so travis can change it) * [x] slight cleanup in config for travis not to fail * [x] add cloud_storage.conf for during test runs * [x] rewrite mvc testing docs and move them to docs/ folder * [x] don't use `static::class` in a class that does not have a parent class, use `__CLASS__` instead. * [x] don't use `<ClassName>::class`, since we already know what class we want `"<ClassName>"` ist just fine. * [x] fix "can't use method in write context" errors on 5.4 (also helps the optimizer) * [x] add build status badge on main README.md Fixes https://github.com/LibreTime/libretime/issues/4 The PHP parts of https://github.com/LibreTime/libretime/pull/10 get obsoleted by this change and it will need rebasing. This also contains https://github.com/LibreTime/libretime/pull/8, the late static binding compat code was broken for no reason and until CentOS drops php 5.4 there is no reason I'm aware of not to support it. I inlined #8 since the test would be failing on php 5.4 without the change. If you want to run tests you need to run `composer install` in the root directory and then `cd airtime_mvc/tests && ../../vendor/bin/phpunit`. For the tests to run the user `libretime` needs to be allowed to create the `libretime_test` database. See `docs/TESTING.md` for more info on getting set up.
2017-02-20 21:47:53 +01:00
/**
2021-10-11 16:10:47 +02:00
* headAction is needed as it is defined as an abstract function in the base controller.
Vendorize ZF1, fix PHPUnit and configure travis This a a rather large commit due to the nature of the stuff it is touching. To get PHPUnit up and running again I had to update some deps and I did so by vendorizing them. The vendorizing of zf1 makes sense since distros are already considering to drop it from their repos. * [x] install vendorized zf1 with composer * [x] load composer autoloader before zf1 * [x] Implement headAction for all Zend_Rest_Controller based controllers * [x] switch to yml dataset to get around string only limitations of xml sets (also removed warning in readme) * [x] use year 2044 as hardcoded date for tests since it is in the future and has the same days like previously used 2016 * [x] make tests easier to run when accessing phpunit directly * [x] clean up test helper to always use airtime.conf * [x] switch test dbname to libretime_test * [x] test db username password switched to libretime/libretime * [x] install phpunit with composer in a clear version (make tests easier to reproduce on other platforms) * [x] remove local libs from airtime repo (most of airtime_mvc/library was not needed of in vendor already) * [x] configure composer autoloading and use it (also removed requires that are not needed anymore) * [x] add LibreTime prefix for FileNotFoundException (phing had a similar class and these are all pre-namespace style) * [x] add .travis.yml file * [x] make etc and logdir configurable with LIBRETIME_CONF_DIR and LIBRETIME_LOG_DIR env (so travis can change it) * [x] slight cleanup in config for travis not to fail * [x] add cloud_storage.conf for during test runs * [x] rewrite mvc testing docs and move them to docs/ folder * [x] don't use `static::class` in a class that does not have a parent class, use `__CLASS__` instead. * [x] don't use `<ClassName>::class`, since we already know what class we want `"<ClassName>"` ist just fine. * [x] fix "can't use method in write context" errors on 5.4 (also helps the optimizer) * [x] add build status badge on main README.md Fixes https://github.com/LibreTime/libretime/issues/4 The PHP parts of https://github.com/LibreTime/libretime/pull/10 get obsoleted by this change and it will need rebasing. This also contains https://github.com/LibreTime/libretime/pull/8, the late static binding compat code was broken for no reason and until CentOS drops php 5.4 there is no reason I'm aware of not to support it. I inlined #8 since the test would be failing on php 5.4 without the change. If you want to run tests you need to run `composer install` in the root directory and then `cd airtime_mvc/tests && ../../vendor/bin/phpunit`. For the tests to run the user `libretime` needs to be allowed to create the `libretime_test` database. See `docs/TESTING.md` for more info on getting set up.
2017-02-20 21:47:53 +01:00
*/
public function headAction()
{
2021-10-11 16:10:47 +02:00
Logging::info('HEAD action received');
Vendorize ZF1, fix PHPUnit and configure travis This a a rather large commit due to the nature of the stuff it is touching. To get PHPUnit up and running again I had to update some deps and I did so by vendorizing them. The vendorizing of zf1 makes sense since distros are already considering to drop it from their repos. * [x] install vendorized zf1 with composer * [x] load composer autoloader before zf1 * [x] Implement headAction for all Zend_Rest_Controller based controllers * [x] switch to yml dataset to get around string only limitations of xml sets (also removed warning in readme) * [x] use year 2044 as hardcoded date for tests since it is in the future and has the same days like previously used 2016 * [x] make tests easier to run when accessing phpunit directly * [x] clean up test helper to always use airtime.conf * [x] switch test dbname to libretime_test * [x] test db username password switched to libretime/libretime * [x] install phpunit with composer in a clear version (make tests easier to reproduce on other platforms) * [x] remove local libs from airtime repo (most of airtime_mvc/library was not needed of in vendor already) * [x] configure composer autoloading and use it (also removed requires that are not needed anymore) * [x] add LibreTime prefix for FileNotFoundException (phing had a similar class and these are all pre-namespace style) * [x] add .travis.yml file * [x] make etc and logdir configurable with LIBRETIME_CONF_DIR and LIBRETIME_LOG_DIR env (so travis can change it) * [x] slight cleanup in config for travis not to fail * [x] add cloud_storage.conf for during test runs * [x] rewrite mvc testing docs and move them to docs/ folder * [x] don't use `static::class` in a class that does not have a parent class, use `__CLASS__` instead. * [x] don't use `<ClassName>::class`, since we already know what class we want `"<ClassName>"` ist just fine. * [x] fix "can't use method in write context" errors on 5.4 (also helps the optimizer) * [x] add build status badge on main README.md Fixes https://github.com/LibreTime/libretime/issues/4 The PHP parts of https://github.com/LibreTime/libretime/pull/10 get obsoleted by this change and it will need rebasing. This also contains https://github.com/LibreTime/libretime/pull/8, the late static binding compat code was broken for no reason and until CentOS drops php 5.4 there is no reason I'm aware of not to support it. I inlined #8 since the test would be failing on php 5.4 without the change. If you want to run tests you need to run `composer install` in the root directory and then `cd airtime_mvc/tests && ../../vendor/bin/phpunit`. For the tests to run the user `libretime` needs to be allowed to create the `libretime_test` database. See `docs/TESTING.md` for more info on getting set up.
2017-02-20 21:47:53 +01:00
}
2021-10-11 16:10:47 +02:00
public function indexAction()
{
Logging::info('INDEX action received');
}
2021-10-11 16:10:47 +02:00
public function getAction()
{
Logging::info('GET action received');
}
2021-10-11 16:10:47 +02:00
public function putAction()
{
Logging::info('PUT action received');
}
/**
2021-10-11 16:10:47 +02:00
* RESTful POST endpoint; used when uploading show images.
*/
2021-10-11 16:10:47 +02:00
public function postAction()
{
$showId = $this->getShowId();
if (!$showId) {
$this->getResponse()
->setHttpResponseCode(400)
->appendBody('No show ID provided');
2021-10-11 16:10:47 +02:00
return;
}
try {
2021-10-11 16:10:47 +02:00
$path = $this->processUploadedImage($showId, $_FILES['file']['tmp_name']);
} catch (Exception $e) {
$this->getResponse()
->setHttpResponseCode(500)
->appendBody('Error processing image: ' . $e->getMessage());
2021-10-11 16:10:47 +02:00
return;
}
$show = CcShowQuery::create()->findPk($showId);
$con = Propel::getConnection();
2021-10-11 16:10:47 +02:00
try {
$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);
}
/**
2021-10-11 16:10:47 +02:00
* RESTful DELETE endpoint; used when deleting show images.
*/
2021-10-11 16:10:47 +02:00
public function deleteAction()
{
$showId = $this->getShowId();
if (!$showId) {
$this->getResponse()
->setHttpResponseCode(400)
->appendBody('No show ID provided');
2021-10-11 16:10:47 +02:00
return;
}
try {
self::deleteShowImagesFromStor($showId);
} catch (Exception $e) {
$this->getResponse()
->setHttpResponseCode(500)
->appendBody('Error processing image: ' . $e->getMessage());
}
$show = CcShowQuery::create()->findPk($showId);
$con = Propel::getConnection();
2021-10-11 16:10:47 +02:00
try {
$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
2021-10-11 16:10:47 +02:00
* individual users and shows.
*
* @param int $showId the ID of the show we're adding the image to
* @param string $tempFilePath temporary filepath assigned to the upload generally of the form /tmp/:tmp_name
*
2022-09-12 13:16:14 +02:00
* @return string the path to the new location for the file
*
* @throws Exception
2021-10-11 16:10:47 +02:00
* - when a file with an unsupported file extension is uploaded or an
* error occurs in copyFileToStor
*/
2021-10-11 16:10:47 +02:00
private function processUploadedImage($showId, $tempFilePath)
{
$ownerId = RestAuth::getOwnerId();
// Only accept files with a file extension that we support.
$fileExtension = $this->getFileExtension($tempFilePath);
2021-10-11 16:10:47 +02:00
if (!in_array(strtolower($fileExtension), explode(',', 'jpg,png,gif,jpeg'))) {
@unlink($tempFilePath);
2021-10-11 16:10:47 +02:00
throw new Exception('Bad file extension.');
}
$importedStorageDirectory = Config::getStoragePath() . 'imported/' . $ownerId . '/show-images/' . $showId;
try {
$importedStorageDirectory = $this->copyFileToStor($tempFilePath, $importedStorageDirectory, $fileExtension);
} catch (Exception $e) {
@unlink($tempFilePath);
2021-10-11 16:10:47 +02:00
throw new Exception('Failed to copy file: ' . $e->getMessage());
}
return $importedStorageDirectory;
}
/**
2021-10-11 16:10:47 +02:00
* Check the MIME type of an uploaded file to determine what extension it should have.
*
* @param $tempFilePath the file path to the uploaded file in /tmp
*
* @return string the file extension for the new file based on its MIME type
*/
2021-10-11 16:10:47 +02:00
private function getFileExtension($tempFilePath)
{
// Don't trust the extension - get the MIME-type instead
$fileInfo = finfo_open();
$mime = finfo_file($fileInfo, $tempFilePath, FILEINFO_MIME_TYPE);
2021-10-11 16:10:47 +02:00
return $this->getExtensionFromMime($mime);
}
/**
2021-10-11 16:10:47 +02:00
* Use a hardcoded list of accepted MIME types to return a file extension.
*
* @param $mime the MIME type of the file
*
* @return string the file extension based on the given MIME type
*/
2021-10-11 16:10:47 +02:00
private function getExtensionFromMime($mime)
{
$extensions = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
2021-10-11 16:10:47 +02:00
'image/gif' => 'gif',
];
return $extensions[$mime];
}
/**
2021-10-11 16:10:47 +02:00
* Copy a given file in /tmp to the user's stor directory.
*
* @param string $tempFilePath the path to the file in /tmp
* @param string $importedStorageDirectory the path to the new location for the file
* @param string $fileExtension the file's extension based on its MIME type
*
2022-09-12 13:16:14 +02:00
* @return string the new full path to the file in stor
*
* @throws Exception if either the storage directory does not exist and cannot be
* created, the storage directory does not have write permissions
* enabled, or the user's hard drive does not have enough space to
* store the file
*/
2021-10-11 16:10:47 +02:00
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)) {
2021-10-11 16:10:47 +02:00
throw new Exception('Failed to create storage directory.');
}
}
if (chmod($image_file, 0644) === false) {
2021-10-11 16:10:47 +02:00
Logging::info("Warning: couldn't change permissions of {$image_file} to 0644");
}
2021-10-11 16:10:47 +02:00
$newFileName = substr($tempFilePath, strrpos($tempFilePath, '/')) . '.' . $fileExtension;
// Did all the checks for real, now trying to copy
$image_stor = Application_Common_OsPath::join($importedStorageDirectory, $newFileName);
2021-10-11 16:10:47 +02:00
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
2021-10-11 16:10:47 +02:00
throw new Exception('The file was not uploaded, this error can occur if the computer '
2022-07-07 20:01:15 +02:00
. '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?
/**
2021-10-11 16:10:47 +02:00
* Delete any images belonging to the show with the given ID.
*
* @param int $showId the ID of the show we're deleting images from
*
* @return bool true if the images were successfully deleted, otherwise false
*/
2021-10-11 16:10:47 +02:00
public static function deleteShowImagesFromStor($showId)
{
$ownerId = RestAuth::getOwnerId();
$importedStorageDirectory = Config::getStoragePath() . 'imported/' . $ownerId . '/show-images/' . $showId;
2021-10-11 16:10:47 +02:00
Logging::info('Deleting images from ' . $importedStorageDirectory);
// to be safe in case image uploading functionality is extended later
if (!file_exists($importedStorageDirectory)) {
2021-10-11 16:10:47 +02:00
Logging::info('No uploaded images for show with id ' . $showId);
return true;
}
2021-10-11 16:10:47 +02:00
return self::delTree($importedStorageDirectory);
}
2022-08-25 16:25:54 +02:00
// from a note @ https://php.net/manual/en/function.rmdir.php
2021-10-11 16:10:47 +02:00
private static function delTree($dir)
{
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
2021-10-11 16:10:47 +02:00
(is_dir("{$dir}/{$file}")) ? self::delTree("{$dir}/{$file}") : unlink("{$dir}/{$file}");
}
2021-10-11 16:10:47 +02:00
return rmdir($dir);
}
/**
* Fetch the id parameter from the request.
2021-10-11 16:10:47 +02:00
*
* @return bool|int false if the show id wasn't
* provided, otherwise returns the id
*/
2021-10-11 16:10:47 +02:00
private function getShowId()
{
if (!($id = $this->_getParam('id', false))) {
$resp = $this->getResponse();
$resp->setHttpResponseCode(400);
2021-10-11 16:10:47 +02:00
$resp->appendBody('ERROR: No show ID specified.');
return false;
}
2021-10-11 16:10:47 +02:00
$id = filter_var($id, FILTER_VALIDATE_INT);
if ($id === false) {
$resp = $this->getResponse();
$resp->setHttpResponseCode(400);
$resp->appendBody('ERROR: Invalid show ID specified.');
return false;
}
return $id;
}
2021-10-11 16:10:47 +02:00
}