adm_program/system/classes/UploadHandlerPhoto.php
<?php
use Admidio\Exception;
/**
* Improved checks and update of database after upload of photos.
*
* This class extends the UploadHandler of the jquery-file-upload library. After
* the upload of a photo we do some checks on the file and if no check fails then
* the Admidio database will be updated. If you want to upload photos for the photos
* module just create an instance of this class.
*
* **Code example**
* ```
* // create object and do upload
* $uploadHandler = new UploadHandlerPhoto(array('upload_dir' => $uploadDir,
* 'upload_url' => $uploadUrl,
* 'image_versions' => array(),
* 'accept_file_types' => '/\.(jpe?g|png)$/i'), true,
* 'array('accept_file_types' => $gL10n->get('SYS_PHOTO_FORMAT_INVALID')));
* ```
* @copyright The Admidio Team
* @see https://www.admidio.org/
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2.0 only
*/
class UploadHandlerPhoto extends UploadHandler
{
/**
* Override the default method to handle the specific things of the photo module and
* update the database after file was successfully uploaded.
* This method has the same parameters as the default.
* @param string $uploaded_file
* @param string $name
* @param int $size
* @param $type
* @param $error
* @param $index
* @param $content_range
* @return stdClass
*/
protected function handle_file_upload($uploaded_file, $name, $size, $type, $error, $index = null, $content_range = null): stdClass
{
global $photoAlbum, $gSettingsManager, $gL10n, $gLogger;
$file = parent::handle_file_upload($uploaded_file, $name, $size, $type, $error, $index, $content_range);
if (!isset($file->error)) {
try {
$fileLocation = ADMIDIO_PATH . FOLDER_DATA . '/photos/upload/' . $file->name;
$albumFolder = ADMIDIO_PATH . FOLDER_DATA . '/photos/' . $photoAlbum->getValue('pho_begin', 'Y-m-d') . '_' . (int) $photoAlbum->getValue('pho_id');
// check filename and throw exception if something is wrong
StringUtils::strIsValidFileName($file->name, false);
// replace invalid characters in filename
$file->name = FileSystemUtils::removeInvalidCharsInFilename($file->name);
// create folder if not exists
if (!is_dir($albumFolder)) {
$error = $photoAlbum->createFolder();
if (is_array($error)) {
$file->error = $gL10n->get($error['text'], array($error['path']));
return $file;
}
}
$newPhotoFileNumber = $photoAlbum->getValue('pho_quantity') + 1;
// check if the file contains a valid image and read image properties
$imageProperties = getimagesize($fileLocation);
if ($imageProperties === false) {
throw new Exception('SYS_PHOTO_FORMAT_INVALID');
}
// check mime type and set file extension
switch ($imageProperties['mime']) {
case 'image/jpeg':
$fileExtension = 'jpg';
break;
case 'image/png':
$fileExtension = 'png';
break;
default:
throw new Exception('SYS_PHOTO_FORMAT_INVALID');
}
$imageDimensions = $imageProperties[0] * $imageProperties[1];
$processableImageSize = SystemInfoUtils::getProcessableImageSize();
if ($imageDimensions > $processableImageSize) {
throw new Exception('SYS_RESOLUTION_TOO_LARGE', array(round($processableImageSize / 1000000, 2)));
}
// create image object and scale image to defined size of preferences
$image = new Image($fileLocation);
$image->setImageType('jpeg');
$image->scale($gSettingsManager->getInt('photo_show_width'), $gSettingsManager->getInt('photo_show_height'));
$image->copyToFile(null, $albumFolder.'/'.$newPhotoFileNumber.'.jpg');
$image->delete();
// if enabled then save original image
if ($gSettingsManager->getBool('photo_keep_original')) {
try {
FileSystemUtils::createDirectoryIfNotExists($albumFolder . '/originals');
try {
FileSystemUtils::moveFile($fileLocation, $albumFolder.'/originals/'.$newPhotoFileNumber.'.'.$fileExtension);
} catch (RuntimeException $exception) {
$gLogger->error('Could not move file!', array('from' => $fileLocation, 'to' => $albumFolder.'/originals/'.$newPhotoFileNumber.'.'.$fileExtension));
// TODO
}
} catch (RuntimeException $exception) {
$gLogger->error('Could not create directory!', array('directoryPath' => $albumFolder . '/originals'));
// TODO
}
}
// save thumbnail
try {
FileSystemUtils::createDirectoryIfNotExists($albumFolder . '/thumbnails');
} catch (RuntimeException $exception) {
}
$image = new Image($fileLocation);
$image->scaleLargerSide($gSettingsManager->getInt('photo_thumbs_scale'));
$image->copyToFile(null, $albumFolder.'/thumbnails/'.$newPhotoFileNumber.'.jpg');
$image->delete();
// delete image from upload folder
try {
FileSystemUtils::deleteFileIfExists($fileLocation);
} catch (RuntimeException $exception) {
}
// if image was successfully saved in filesystem then update image count of album
if (is_file($albumFolder.'/'.$newPhotoFileNumber.'.jpg')) {
$photoAlbum->setValue('pho_quantity', (int) $photoAlbum->getValue('pho_quantity') + 1);
$photoAlbum->save();
} else {
throw new Exception('SYS_PHOTO_PROCESSING_ERROR');
}
} catch (Exception $e) {
try {
FileSystemUtils::deleteFileIfExists($this->options['upload_dir'].$file->name);
} catch (RuntimeException $exception) {
$gLogger->error('Could not delete file!', array('filePath' => $this->options['upload_dir'].$file->name));
// TODO
}
// remove XSS from filename before the name will be shown in the error message
$file->name = SecurityUtils::encodeHTML(StringUtils::strStripTags($file->name));
$file->error = $e->getMessage();
return $file;
}
}
return $file;
}
/**
* Override the default method to handle specific form data that will be set when creating the Javascript
* file upload object. Here we validate the CSRF token that will be set. If the check failed an error will
* be set and the file upload will be canceled.
* @param string $file
* @param int $index
*/
protected function handle_form_data($file, $index)
{
// ADM Start
try {
// check the CSRF token of the form against the session token
SecurityUtils::validateCsrfToken($_REQUEST['admidio-csrf-token']);
} catch (Exception $exception) {
$file->error = $exception->getMessage();
// => EXIT
}
// ADM End
}
}