<?php
/**
* Copyright (C) 2019 Rhyme Digital - Borrowed from http://github.com/terminal42/contao-fineuploader
*
* @copyright Copyright (c) 2008-2017, terminal42 gmbh
* @author Blair Winans <blair@rhyme.digital>
* @author Adam Fisher <adam@rhyme.digital>
* @author terminal42 gmbh <info@terminal42.ch>
* @license http://opensource.org/licenses/lgpl-3.0.html LGPL
* @link http://github.com/terminal42/contao-fineuploader
*/
namespace Rhyme\WMassArtsHub\Widget;
use Contao\Widget;
use Contao\Message;
use Contao\Environment;
use Contao\FrontendUser;
/**
* Class DMUploaderBase
*
* Parent class for "DMUploaderWidgets" widgets.
*/
abstract class DMUploaderBase extends Widget
{
/**
* Temporary upload path
* @var string
*/
protected $strTemporaryPath = 'system/tmp';
/**
* Initialize the object
* @param array
*/
public function __construct($arrAttributes=null)
{
$arrAttributes['decodeEntities'] = true;
parent::__construct($arrAttributes);
}
/**
* Add the labels and messages.
*
* @param null $arrAttributes
*/
public function parse($arrAttributes = null)
{
// Messages (passed on to fineuploader JS)
$basicTextOptions = array(
'text' => array(
'formatProgress',
'failUpload',
'waitingForResponse',
'paused',
),
'messages' => array(
'typeError',
'sizeError',
'minSizeError',
'emptyError',
'noFilesError',
'tooManyItemsError',
'maxHeightImageError',
'maxWidthImageError',
'minHeightImageError',
'minWidthImageError',
'retryFailTooManyItems',
'onLeave',
'unsupportedBrowserIos8Safari',
),
'retry' => array(
'autoRetryNote',
),
'deleteFile' => array(
'confirmMessage',
'deletingStatusText',
'deletingFailedText',
),
'paste' => array(
'namePromptMessage',
)
);
$config = array();
foreach ($basicTextOptions as $category => $messages) {
foreach ($messages as $message) {
// Only translate if available, otherwise fall back to default (EN)
if (isset($GLOBALS['TL_LANG']['MSC']['dmuploder_trans'][$category][$message])) {
$config[$category][$message] = $GLOBALS['TL_LANG']['MSC']['dmuploder_trans'][$category][$message];
}
}
}
// Merge with custom options
$this->config = json_encode(array_merge($config, (array) $this->arrConfiguration['uploaderConfig']));
// Labels (in HTML)
$labels = array(
'drop',
'upload',
'processing',
'cancel',
'retry',
'delete',
'close',
'yes',
'no',
);
$preparedLabels = array();
foreach ($labels as $label) {
$preparedLabels[$label] = $GLOBALS['TL_LANG']['MSC']['dmuploder_' . $label];
}
// Set the upload button label
if ($this->uploadButtonLabel) {
$preparedLabels['upload'] = $this->uploadButtonLabel;
}
$this->labels = $preparedLabels;
return parent::parse($arrAttributes);
}
/**
* Add the required attribute if mandatory
*
* @param string
* @param mixed
*/
public function __set($strKey, $varValue)
{
switch ($strKey) {
case 'mandatory':
if ($varValue) {
$this->arrAttributes['required'] = 'required';
} else {
unset($this->arrAttributes['required']);
}
// DO NOT BREAK HERE
}
parent::__set($strKey, $varValue);
}
/**
* Validate the upload
* @return string
*/
public function validateUpload()
{
Message::reset();
$strTempName = $this->strName . '_dmuploader';
$objUploader = new \Haste\Util\FileUpload($this->strName);
// Convert the $_FILES array to Contao format
if (!empty($_FILES[$strTempName])) {
$arrFile = array
(
'name' => array($_FILES[$strTempName]['name']),
'type' => array($_FILES[$strTempName]['type']),
'tmp_name' => array($_FILES[$strTempName]['tmp_name']),
'error' => array($_FILES[$strTempName]['error']),
'size' => array($_FILES[$strTempName]['size']),
);
// Replace the comma character (#22)
$arrFile['name'] = str_replace(',', '_', $arrFile['name']);
// Check if the file exists
if (file_exists(TL_ROOT . '/' . $this->strTemporaryPath . '/' . $arrFile['name'][0])) {
$arrFile['name'][0] = $this->getFileName($arrFile['name'][0], $this->strTemporaryPath);
}
$_FILES[$this->strName] = $arrFile;
unset($_FILES[$strTempName]); // Unset the temporary file
}
$varInput = '';
// Validate the minlength
if ($this->arrConfiguration['minlength'] > 0) {
$objUploader->setMinFileSize($this->arrConfiguration['minlength']);
}
// Validate the maxlength
if ($this->arrConfiguration['maxlength'] > 0) {
$objUploader->setMaxFileSize($this->arrConfiguration['maxlength']);
}
// Set the maximum width
if ($this->arrConfiguration['maxWidth']) {
$objUploader->setImageWidth($this->arrConfiguration['maxWidth']);
}
// Set the maximum height
if ($this->arrConfiguration['maxHeight']) {
$objUploader->setImageHeight($this->arrConfiguration['maxHeight']);
}
try {
$varInput = $objUploader->uploadTo($this->strTemporaryPath);
if ($objUploader->hasError()) {
if (version_compare(VERSION, '4.2', '>=')) {
$session = \System::getContainer()->get('session');
foreach ($session->getFlashBag()->peek('contao.'.TL_MODE.'.error') as $strError) {
$this->addError($strError);
}
} else {
foreach ((array) $_SESSION['TL_ERROR'] as $strError) {
$this->addError($strError);
}
}
}
Message::reset();
} catch (\Exception $e) {
$this->addError($e->getMessage());
}
if (!is_array($varInput) || empty($varInput)) {
$this->addError($GLOBALS['TL_LANG']['MSC']['dmuploader_error']);
}
$varInput = $varInput[0];
// Validate and move the file immediately
if ($this->arrConfiguration['directUpload']) {
$varInput = $this->validatorSingle($varInput, $this->getDestinationFolder());
}
return $varInput;
}
/**
* Return an array if the "multiple" attribute is set
* @param mixed
* @return mixed
*/
protected function validator($varInput)
{
$varReturn = $this->blnIsMultiple ? array() : '';
$strDestination = $this->getDestinationFolder();
// Check if mandatory
if ($varInput == '' && $this->mandatory) {
$this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['mandatory'], $this->strLabel));
}
// Single file
elseif (strpos($varInput, ',') === false) {
$varInput = $this->validatorSingle($varInput, $strDestination);
$varReturn = $this->blnIsMultiple ? array($varInput) : $varInput;
}
// Multiple files
else {
$arrValues = array_filter(explode(',', $varInput));
// Limit the number of uploads
if ($this->arrConfiguration['uploaderLimit'] > 0) {
$arrValues = array_slice($arrValues, 0, $this->arrConfiguration['uploaderLimit']);
}
foreach ($arrValues as $k => $v) {
$arrValues[$k] = $this->validatorSingle($v, $strDestination);
}
$varReturn = $this->blnIsMultiple ? $arrValues : $arrValues[0];
}
return $varReturn;
}
/**
* Get the destination folder
*
* @return mixed
*/
protected function getDestinationFolder()
{
$destination = \Config::get('uploadPath');
$folder = null;
// Specify the target folder in the DCA (eval)
if (isset($this->arrConfiguration['uploadFolder'])) {
$folder = $this->arrConfiguration['uploadFolder'];
}
// Use the user's home directory
if ($this->arrConfiguration['useHomeDir'] && FE_USER_LOGGED_IN) {
$user = FrontendUser::getInstance();
if ($user->assignDir && $user->homeDir) {
$folder = $user->homeDir;
}
}
if ($folder !== null && \Validator::isUuid($folder)) {
$folderModel = \FilesModel::findByUuid($folder);
if ($folderModel !== null) {
$destination = $folderModel->path;
}
} else {
$destination = $folder;
}
return $destination;
}
/**
* Validate a single file.
*
* @param mixed
* @param string
* @return mixed
*/
protected function validatorSingle($varFile, $strDestination)
{
// Move the temporary file
if (!\Validator::isStringUuid($varFile) && is_file(TL_ROOT . '/' . $varFile)) {
$varFile = $this->moveTemporaryFile($varFile, $strDestination);
}
// Convert uuid to binary format
if (\Validator::isStringUuid($varFile)) {
$varFile = \StringUtil::uuidToBin($varFile);
}
return $varFile;
}
/**
* Move the temporary file to its destination
* @param string
* @param string
* @return string
*/
protected function moveTemporaryFile($strFile, $strDestination)
{
if (!is_file(TL_ROOT . '/' . $strFile)) {
return '';
}
// Do not store the file
if (!$this->arrConfiguration['storeFile']) {
return $strFile;
}
// The file is not temporary
if (stripos($strFile, $this->strTemporaryPath) === false) {
return $strFile;
}
$strNew = $strDestination . '/' . basename($strFile);
// Do not overwrite existing files
if ($this->arrConfiguration['doNotOverwrite']) {
$strNew = $strDestination . '/' . $this->getFileName(basename($strFile), $strDestination);
}
$blnRename = \Files::getInstance()->rename($strFile, $strNew);
\Files::getInstance()->chmod($strNew, \Config::get('defaultFileChmod'));
// Add the file to Dbafs
if ($this->arrConfiguration['addToDbafs'] && $blnRename) {
$objModel = \Dbafs::addResource($strNew);
if ($objModel !== null) {
$strNew = $objModel->uuid;
}
}
return $strNew;
}
/**
* Get the new file name if it already exists in the folder
* @param string
* @param string
* @return string
*/
protected function getFileName($strFile, $strFolder)
{
if (!file_exists(TL_ROOT . '/' . $strFolder . '/' . $strFile)) {
return $strFile;
}
$offset = 1;
$pathinfo = pathinfo($strFile);
$name = $pathinfo['filename'];
$arrAll = scan(TL_ROOT . '/' . $strFolder);
$arrFiles = preg_grep('/^' . preg_quote($name, '/') . '.*\.' . preg_quote($pathinfo['extension'], '/') . '/', $arrAll);
foreach ($arrFiles as $file) {
if (preg_match('/__[0-9]+\.' . preg_quote($pathinfo['extension'], '/') . '$/', $file)) {
$file = str_replace('.' . $pathinfo['extension'], '', $file);
$intValue = intval(substr($file, (strrpos($file, '_') + 1)));
$offset = max($offset, $intValue);
}
}
return str_replace($name, $name . '__' . ++$offset, $strFile);
}
/**
* Generate a file item and return it as HTML string
* @param string
* @return string
*/
protected function generateFileItem($strPath)
{
if (!is_file(TL_ROOT . '/' . $strPath)) {
return '';
}
$imageSize = $this->getImageSize();
$objFile = new \File($strPath, true);
$strInfo = $strPath . ' <span class="tl_gray">(' . \System::getReadableSize($objFile->size) . ($objFile->isGdImage ? ', ' . $objFile->width . 'x' . $objFile->height . ' px' : '') . ')</span>';
$allowedDownload = trimsplit(',', strtolower($GLOBALS['TL_CONFIG']['allowedDownload']));
$strReturn = '';
// Show files and folders
if (!$this->blnIsGallery && !$this->blnIsDownloads) {
if ($objFile->isGdImage) {
$strReturn = \Image::getHtml(\Image::get($strPath, $imageSize[0], $imageSize[1], $imageSize[2]), '', 'class="gimage" title="' . specialchars($strInfo) . '"');
} else {
$strReturn = \Image::getHtml($objFile->icon) . ' ' . $strInfo;
}
}
// Show a sortable list of files only
else {
if ($this->blnIsGallery) {
// Only show images
if ($objFile->isGdImage) {
$strReturn = \Image::getHtml(\Image::get($strPath, $imageSize[0], $imageSize[1], $imageSize[2]), '', 'class="gimage" title="' . specialchars($strInfo) . '"');
}
} else {
// Only show allowed download types
if (in_array($objFile->extension, $allowedDownload) && !preg_match('/^meta(_[a-z]{2})?\.txt$/', $objFile->basename)) {
$strReturn = \Image::getHtml($objFile->icon) . ' ' . $strPath;
}
}
}
return $strReturn;
}
/**
* Get the image size
*
* @return array
*/
protected function getImageSize()
{
if (is_array($this->imageSize)) {
return $this->imageSize;
}
return [80, 60, 'center_center'];
}
}