This commit is contained in:
Xes
2025-08-14 22:41:49 +02:00
parent 2de81ccc46
commit 8ce45119b6
39774 changed files with 4309466 additions and 0 deletions

View File

@@ -0,0 +1,134 @@
<?php
namespace Gedmo\Uploadable\Event;
use Doctrine\Common\EventArgs;
use Doctrine\ORM\EntityManagerInterface;
use Gedmo\Uploadable\FileInfo\FileInfoInterface;
use Gedmo\Uploadable\UploadableListener;
/**
* Abstract Base Event to be extended by Uploadable events
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
abstract class UploadableBaseEventArgs extends EventArgs
{
/**
* The instance of the Uploadable listener that fired this event
*
* @var \Gedmo\Uploadable\UploadableListener
*/
private $uploadableListener;
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
private $em;
/**
* The Uploadable entity
*
* @var object $entity
*/
private $entity;
/**
* The configuration of the Uploadable extension for this entity class
*
* @var array $extensionConfiguration
*/
private $extensionConfiguration;
/**
* @var \Gedmo\Uploadable\FileInfo\FileInfoInterface
*/
private $fileInfo;
/**
* @var string $action - Is the file being created, updated or removed?
* This value can be: CREATE, UPDATE or DELETE.
*/
private $action;
/**
* @param UploadableListener $listener
* @param \Doctrine\ORM\EntityManagerInterface $em
* @param array $config
* @param FileInfoInterface $fileInfo
* @param object $entity
* @param string $action
*/
public function __construct(UploadableListener $listener, EntityManagerInterface $em, array $config, FileInfoInterface $fileInfo, $entity, $action)
{
$this->uploadableListener = $listener;
$this->em = $em;
$this->config = $config;
$this->fileInfo = $fileInfo;
$this->entity = $entity;
$this->action = $action;
}
/**
* Retrieve the associated listener
*
* @return \Gedmo\Uploadable\UploadableListener
*/
public function getListener()
{
return $this->uploadableListener;
}
/**
* Retrieve associated EntityManager
*
* @return \Doctrine\ORM\EntityManagerInterface
*/
public function getEntityManager()
{
return $this->em;
}
/**
* Retrieve associated Entity
*
* @return object
*/
public function getEntity()
{
return $this->entity;
}
/**
* Retrieve associated Uploadable extension configuration
*
* @return array
*/
public function getExtensionConfiguration()
{
return $this->extensionConfiguration;
}
/**
* Retrieve the FileInfo associated with this entity.
*
* @return \Gedmo\Uploadable\FileInfo\FileInfoInterface
*/
public function getFileInfo()
{
return $this->fileInfo;
}
/**
* Retrieve the action being performed to the entity: CREATE, UPDATE or DELETE
*
* @return string
*/
public function getAction()
{
return $this->action;
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Gedmo\Uploadable\Event;
/**
* Post File Process Event for the Uploadable extension
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class UploadablePostFileProcessEventArgs extends UploadableBaseEventArgs
{
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Gedmo\Uploadable\Event;
/**
* Pre File Process Event for the Uploadable extension
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class UploadablePreFileProcessEventArgs extends UploadableBaseEventArgs
{
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Gedmo\Uploadable;
/**
* Container for all Gedmo Uploadable events
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
final class Events
{
private function __construct()
{
}
/**
* The uploadablePreFileProcess event occurs before a file is processed inside
* the Uploadable listener. This means it happens before the file is validated and moved
* to the configured path.
*
* @var string
*/
const uploadablePreFileProcess = 'uploadablePreFileProcess';
/**
* The uploadablePostFileProcess event occurs after a file is processed inside
* the Uploadable listener. This means it happens after the file is validated and moved
* to the configured path.
*
* @var string
*/
const uploadablePostFileProcess = 'uploadablePostFileProcess';
}

View File

@@ -0,0 +1,62 @@
<?php
namespace Gedmo\Uploadable\FileInfo;
/**
* FileInfoArray
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class FileInfoArray implements FileInfoInterface
{
protected $fileInfo;
public function __construct(array $fileInfo)
{
$keys = array('error', 'size', 'type', 'tmp_name', 'name');
foreach ($keys as $k) {
if (!isset($fileInfo[$k])) {
$msg = 'There are missing keys in the fileInfo. ';
$msg .= 'Keys needed: '.implode(',', $keys);
throw new \RuntimeException($msg);
}
}
$this->fileInfo = $fileInfo;
}
public function getTmpName()
{
return $this->fileInfo['tmp_name'];
}
public function getName()
{
return $this->fileInfo['name'];
}
public function getSize()
{
return $this->fileInfo['size'];
}
public function getType()
{
return $this->fileInfo['type'];
}
public function getError()
{
return $this->fileInfo['error'];
}
public function isUploadedFile()
{
return true;
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Gedmo\Uploadable\FileInfo;
/**
* FileInfoInterface
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
interface FileInfoInterface
{
public function getTmpName();
public function getName();
public function getSize();
public function getType();
public function getError();
/**
* This method must return true if the file is coming from $_FILES, or false instead.
*
* @return bool
*/
public function isUploadedFile();
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Gedmo\Uploadable\FilenameGenerator;
/**
* FilenameGeneratorAlphanumeric
*
* This class generates a filename, leaving only lowercase
* alphanumeric characters
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class FilenameGeneratorAlphanumeric implements FilenameGeneratorInterface
{
/**
* @inheritDoc
*/
public static function generate($filename, $extension, $object = null)
{
return preg_replace('/[^a-z0-9]+/', '-', strtolower($filename)).$extension;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Gedmo\Uploadable\FilenameGenerator;
/**
* FilenameGeneratorInterface
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
interface FilenameGeneratorInterface
{
/**
* Generates a new filename
*
* @param string - Filename without extension
* @param string - Extension with dot: .jpg, .gif, etc
* @param $object
*
* @return string
*/
public static function generate($filename, $extension, $object = null);
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Gedmo\Uploadable\FilenameGenerator;
/**
* FilenameGeneratorSha1
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class FilenameGeneratorSha1 implements FilenameGeneratorInterface
{
/**
* @inheritDoc
*/
public static function generate($filename, $extension, $object = null)
{
return sha1(uniqid($filename.$extension, true)).$extension;
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace Gedmo\Uploadable\Mapping\Driver;
use Gedmo\Mapping\Driver\AbstractAnnotationDriver;
use Gedmo\Uploadable\Mapping\Validator;
/**
* This is an annotation mapping driver for Uploadable
* behavioral extension. Used for extraction of extended
* metadata from Annotations specifically for Uploadable
* extension.
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class Annotation extends AbstractAnnotationDriver
{
/**
* Annotation to define that this object is loggable
*/
const UPLOADABLE = 'Gedmo\\Mapping\\Annotation\\Uploadable';
const UPLOADABLE_FILE_MIME_TYPE = 'Gedmo\\Mapping\\Annotation\\UploadableFileMimeType';
const UPLOADABLE_FILE_NAME = 'Gedmo\\Mapping\\Annotation\\UploadableFileName';
const UPLOADABLE_FILE_PATH = 'Gedmo\\Mapping\\Annotation\\UploadableFilePath';
const UPLOADABLE_FILE_SIZE = 'Gedmo\\Mapping\\Annotation\\UploadableFileSize';
/**
* {@inheritDoc}
*/
public function readExtendedMetadata($meta, array &$config)
{
$class = $this->getMetaReflectionClass($meta);
// class annotations
if ($annot = $this->reader->getClassAnnotation($class, self::UPLOADABLE)) {
$config['uploadable'] = true;
$config['allowOverwrite'] = $annot->allowOverwrite;
$config['appendNumber'] = $annot->appendNumber;
$config['path'] = $annot->path;
$config['pathMethod'] = $annot->pathMethod;
$config['fileMimeTypeField'] = false;
$config['fileNameField'] = false;
$config['filePathField'] = false;
$config['fileSizeField'] = false;
$config['callback'] = $annot->callback;
$config['filenameGenerator'] = $annot->filenameGenerator;
$config['maxSize'] = (double) $annot->maxSize;
$config['allowedTypes'] = $annot->allowedTypes;
$config['disallowedTypes'] = $annot->disallowedTypes;
foreach ($class->getProperties() as $prop) {
if ($this->reader->getPropertyAnnotation($prop, self::UPLOADABLE_FILE_MIME_TYPE)) {
$config['fileMimeTypeField'] = $prop->getName();
}
if ($this->reader->getPropertyAnnotation($prop, self::UPLOADABLE_FILE_NAME)) {
$config['fileNameField'] = $prop->getName();
}
if ($this->reader->getPropertyAnnotation($prop, self::UPLOADABLE_FILE_PATH)) {
$config['filePathField'] = $prop->getName();
}
if ($this->reader->getPropertyAnnotation($prop, self::UPLOADABLE_FILE_SIZE)) {
$config['fileSizeField'] = $prop->getName();
}
}
Validator::validateConfiguration($meta, $config);
}
/*
// Code in case we need to identify entities which are not Uploadables, but have associations
// with other Uploadable entities
} else {
// We need to check if this class has a relation with Uploadable entities
$associations = $meta->getAssociationMappings();
foreach ($associations as $field => $association) {
$refl = new \ReflectionClass($association['targetEntity']);
if ($annot = $this->reader->getClassAnnotation($refl, self::UPLOADABLE)) {
$config['hasUploadables'] = true;
if (!isset($config['uploadables'])) {
$config['uploadables'] = array();
}
$config['uploadables'][] = array(
'class' => $association['targetEntity'],
'property' => $association['fieldName']
);
}
}
}*/
$this->validateFullMetadata($meta, $config);
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace Gedmo\Uploadable\Mapping\Driver;
use Gedmo\Mapping\Driver\Xml as BaseXml;
use Gedmo\Uploadable\Mapping\Validator;
/**
* This is a xml mapping driver for Uploadable
* behavioral extension. Used for extraction of extended
* metadata from xml specifically for Uploadable
* extension.
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @author Miha Vrhovnik <miha.vrhovnik@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class Xml extends BaseXml
{
/**
* {@inheritDoc}
*/
public function readExtendedMetadata($meta, array &$config)
{
/**
* @var \SimpleXmlElement $xml
*/
$xml = $this->_getMapping($meta->name);
$xmlDoctrine = $xml;
$xml = $xml->children(self::GEDMO_NAMESPACE_URI);
if ($xmlDoctrine->getName() == 'entity' || $xmlDoctrine->getName() == 'mapped-superclass') {
if (isset($xml->uploadable)) {
$xmlUploadable = $xml->uploadable;
$config['uploadable'] = true;
$config['allowOverwrite'] = $this->_isAttributeSet($xmlUploadable, 'allow-overwrite') ?
(bool) $this->_getAttribute($xmlUploadable, 'allow-overwrite') : false;
$config['appendNumber'] = $this->_isAttributeSet($xmlUploadable, 'append-number') ?
(bool) $this->_getAttribute($xmlUploadable, 'append-number') : false;
$config['path'] = $this->_isAttributeSet($xmlUploadable, 'path') ?
$this->_getAttribute($xml->{'uploadable'}, 'path') : '';
$config['pathMethod'] = $this->_isAttributeSet($xmlUploadable, 'path-method') ?
$this->_getAttribute($xml->{'uploadable'}, 'path-method') : '';
$config['callback'] = $this->_isAttributeSet($xmlUploadable, 'callback') ?
$this->_getAttribute($xml->{'uploadable'}, 'callback') : '';
$config['fileMimeTypeField'] = false;
$config['fileNameField'] = false;
$config['filePathField'] = false;
$config['fileSizeField'] = false;
$config['filenameGenerator'] = $this->_isAttributeSet($xmlUploadable, 'filename-generator') ?
$this->_getAttribute($xml->{'uploadable'}, 'filename-generator') :
Validator::FILENAME_GENERATOR_NONE;
$config['maxSize'] = $this->_isAttributeSet($xmlUploadable, 'max-size') ?
(double) $this->_getAttribute($xml->{'uploadable'}, 'max-size') :
(double) 0;
$config['allowedTypes'] = $this->_isAttributeSet($xmlUploadable, 'allowed-types') ?
$this->_getAttribute($xml->{'uploadable'}, 'allowed-types') :
'';
$config['disallowedTypes'] = $this->_isAttributeSet($xmlUploadable, 'disallowed-types') ?
$this->_getAttribute($xml->{'uploadable'}, 'disallowed-types') :
'';
if (isset($xmlDoctrine->field)) {
foreach ($xmlDoctrine->field as $mapping) {
$mappingDoctrine = $mapping;
$mapping = $mapping->children(self::GEDMO_NAMESPACE_URI);
$field = $this->_getAttribute($mappingDoctrine, 'name');
if (isset($mapping->{'uploadable-file-mime-type'})) {
$config['fileMimeTypeField'] = $field;
} elseif (isset($mapping->{'uploadable-file-size'})) {
$config['fileSizeField'] = $field;
} elseif (isset($mapping->{'uploadable-file-name'})) {
$config['fileNameField'] = $field;
} elseif (isset($mapping->{'uploadable-file-path'})) {
$config['filePathField'] = $field;
}
}
}
Validator::validateConfiguration($meta, $config);
}
}
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Gedmo\Uploadable\Mapping\Driver;
use Gedmo\Mapping\Driver\File;
use Gedmo\Mapping\Driver;
use Gedmo\Uploadable\Mapping\Validator;
/**
* This is a yaml mapping driver for Uploadable
* behavioral extension. Used for extraction of extended
* metadata from yaml specifically for Uploadable
* extension.
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class Yaml extends File implements Driver
{
/**
* File extension
* @var string
*/
protected $_extension = '.dcm.yml';
/**
* {@inheritDoc}
*/
public function readExtendedMetadata($meta, array &$config)
{
$mapping = $this->_getMapping($meta->name);
if (isset($mapping['gedmo'])) {
$classMapping = $mapping['gedmo'];
if (isset($classMapping['uploadable'])) {
$uploadable = $classMapping['uploadable'];
$config['uploadable'] = true;
$config['allowOverwrite'] = isset($uploadable['allowOverwrite']) ?
(bool) $uploadable['allowOverwrite'] : false;
$config['appendNumber'] = isset($uploadable['appendNumber']) ?
(bool) $uploadable['appendNumber'] : false;
$config['path'] = isset($uploadable['path']) ? $uploadable['path'] : '';
$config['pathMethod'] = isset($uploadable['pathMethod']) ? $uploadable['pathMethod'] : '';
$config['callback'] = isset($uploadable['callback']) ? $uploadable['callback'] : '';
$config['fileMimeTypeField'] = false;
$config['fileNameField'] = false;
$config['filePathField'] = false;
$config['fileSizeField'] = false;
$config['filenameGenerator'] = isset($uploadable['filenameGenerator']) ?
$uploadable['filenameGenerator'] :
Validator::FILENAME_GENERATOR_NONE;
$config['maxSize'] = isset($uploadable['maxSize']) ?
(double) $uploadable['maxSize'] :
(double) 0;
$config['allowedTypes'] = isset($uploadable['allowedTypes']) ?
$uploadable['allowedTypes'] :
'';
$config['disallowedTypes'] = isset($uploadable['disallowedTypes']) ?
$uploadable['disallowedTypes'] :
'';
if (isset($mapping['fields'])) {
foreach ($mapping['fields'] as $field => $info) {
if (isset($info['gedmo']) && array_key_exists(0, $info['gedmo'])) {
if ($info['gedmo'][0] === 'uploadableFileMimeType') {
$config['fileMimeTypeField'] = $field;
} elseif ($info['gedmo'][0] === 'uploadableFileSize') {
$config['fileSizeField'] = $field;
} elseif ($info['gedmo'][0] === 'uploadableFileName') {
$config['fileNameField'] = $field;
} elseif ($info['gedmo'][0] === 'uploadableFilePath') {
$config['filePathField'] = $field;
}
}
}
}
Validator::validateConfiguration($meta, $config);
}
}
}
/**
* {@inheritDoc}
*/
protected function _loadMappingFile($file)
{
return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file));
}
}

View File

@@ -0,0 +1,242 @@
<?php
namespace Gedmo\Uploadable\Mapping;
use Gedmo\Exception\InvalidMappingException;
use Gedmo\Exception\UploadableCantWriteException;
use Gedmo\Exception\UploadableInvalidPathException;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
/**
* This class is used to validate mapping information
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class Validator
{
const UPLOADABLE_FILE_MIME_TYPE = 'UploadableFileMimeType';
const UPLOADABLE_FILE_NAME = 'UploadableFileName';
const UPLOADABLE_FILE_PATH = 'UploadableFilePath';
const UPLOADABLE_FILE_SIZE = 'UploadableFileSize';
const FILENAME_GENERATOR_SHA1 = 'SHA1';
const FILENAME_GENERATOR_ALPHANUMERIC = 'ALPHANUMERIC';
const FILENAME_GENERATOR_NONE = 'NONE';
/**
* Determines if we should throw an exception in the case the "allowedTypes" and
* "disallowedTypes" options are BOTH set. Useful for testing purposes
*
* @var bool
*/
public static $enableMimeTypesConfigException = true;
/**
* List of types which are valid for UploadableFileMimeType field
*
* @var array
*/
public static $validFileMimeTypeTypes = array(
'string',
);
/**
* List of types which are valid for UploadableFileName field
*
* @var array
*/
public static $validFileNameTypes = array(
'string',
);
/**
* List of types which are valid for UploadableFilePath field
*
* @var array
*/
public static $validFilePathTypes = array(
'string',
);
/**
* List of types which are valid for UploadableFileSize field for ORM
*
* @var array
*/
public static $validFileSizeTypes = array(
'decimal',
);
/**
* List of types which are valid for UploadableFileSize field for ODM
*
* @var array
*/
public static $validFileSizeTypesODM = array(
'float',
);
/**
* Whether to validate if the directory of the file exists and is writable, useful to disable it when using
* stream wrappers which don't support is_dir (like Gaufrette)
*
* @var bool
*/
public static $validateWritableDirectory = true;
public static function validateFileNameField(ClassMetadata $meta, $field)
{
self::validateField($meta, $field, self::UPLOADABLE_FILE_NAME, self::$validFileNameTypes);
}
public static function validateFileMimeTypeField(ClassMetadata $meta, $field)
{
self::validateField($meta, $field, self::UPLOADABLE_FILE_MIME_TYPE, self::$validFileMimeTypeTypes);
}
public static function validateFilePathField(ClassMetadata $meta, $field)
{
self::validateField($meta, $field, self::UPLOADABLE_FILE_PATH, self::$validFilePathTypes);
}
public static function validateFileSizeField(ClassMetadata $meta, $field)
{
if ($meta instanceof \Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo) {
self::validateField($meta, $field, self::UPLOADABLE_FILE_SIZE, self::$validFileSizeTypesODM);
} else {
self::validateField($meta, $field, self::UPLOADABLE_FILE_SIZE, self::$validFileSizeTypes);
}
}
public static function validateField($meta, $field, $uploadableField, $validFieldTypes)
{
if ($meta->isMappedSuperclass) {
return;
}
$fieldMapping = $meta->getFieldMapping($field);
if (!in_array($fieldMapping['type'], $validFieldTypes)) {
$msg = 'Field "%s" to work as an "%s" field must be of one of the following types: "%s".';
throw new InvalidMappingException(sprintf($msg,
$field,
$uploadableField,
implode(', ', $validFieldTypes)
));
}
}
public static function validatePath($path)
{
if (!is_string($path) || $path === '') {
throw new UploadableInvalidPathException('Path must be a string containing the path to a valid directory.');
}
if (!self::$validateWritableDirectory) {
return;
}
if (!is_dir($path) && !@mkdir($path, 0777, true)) {
throw new UploadableInvalidPathException(sprintf('Unable to create "%s" directory.',
$path
));
}
if (!is_writable($path)) {
throw new UploadableCantWriteException(sprintf('Directory "%s" does is not writable.',
$path
));
}
}
public static function validateConfiguration(ClassMetadata $meta, array &$config)
{
if (!$config['filePathField'] && !$config['fileNameField']) {
throw new InvalidMappingException(sprintf('Class "%s" must have an UploadableFilePath or UploadableFileName field.',
$meta->name
));
}
$refl = $meta->getReflectionClass();
if ($config['pathMethod'] !== '' && !$refl->hasMethod($config['pathMethod'])) {
throw new InvalidMappingException(sprintf('Class "%s" doesn\'t have method "%s"!',
$meta->name,
$config['pathMethod']
));
}
if ($config['callback'] !== '' && !$refl->hasMethod($config['callback'])) {
throw new InvalidMappingException(sprintf('Class "%s" doesn\'t have method "%s"!',
$meta->name,
$config['callback']
));
}
$config['maxSize'] = (double) $config['maxSize'];
if ($config['maxSize'] < 0) {
throw new InvalidMappingException(sprintf('Option "maxSize" must be a number >= 0 for class "%s".',
$meta->name
));
}
if (self::$enableMimeTypesConfigException && ($config['allowedTypes'] !== '' && $config['disallowedTypes'] !== '')) {
$msg = 'You\'ve set "allowedTypes" and "disallowedTypes" options. You must set only one in class "%s".';
throw new InvalidMappingException(sprintf($msg,
$meta->name
));
}
$config['allowedTypes'] = $config['allowedTypes'] ? (strpos($config['allowedTypes'], ',') !== false ?
explode(',', $config['allowedTypes']) : array($config['allowedTypes'])) : false;
$config['disallowedTypes'] = $config['disallowedTypes'] ? (strpos($config['disallowedTypes'], ',') !== false ?
explode(',', $config['disallowedTypes']) : array($config['disallowedTypes'])) : false;
if ($config['fileNameField']) {
self::validateFileNameField($meta, $config['fileNameField']);
}
if ($config['filePathField']) {
self::validateFilePathField($meta, $config['filePathField']);
}
if ($config['fileMimeTypeField']) {
self::validateFileMimeTypeField($meta, $config['fileMimeTypeField']);
}
if ($config['fileSizeField']) {
self::validateFileSizeField($meta, $config['fileSizeField']);
}
switch ((string) $config['filenameGenerator']) {
case self::FILENAME_GENERATOR_ALPHANUMERIC:
case self::FILENAME_GENERATOR_SHA1:
case self::FILENAME_GENERATOR_NONE:
break;
default:
$ok = false;
if (class_exists($config['filenameGenerator'])) {
$refl = new \ReflectionClass($config['filenameGenerator']);
if ($refl->implementsInterface('Gedmo\Uploadable\FilenameGenerator\FilenameGeneratorInterface')) {
$ok = true;
}
}
if (!$ok) {
$msg = 'Class "%s" needs a valid value for filenameGenerator. It can be: SHA1, ALPHANUMERIC, NONE or ';
$msg .= 'a class implementing FileGeneratorInterface.';
throw new InvalidMappingException(sprintf($msg,
$meta->name
));
}
}
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Gedmo\Uploadable\MimeType;
use Gedmo\Exception\UploadableFileNotReadableException;
use Gedmo\Exception\UploadableInvalidFileException;
/**
* Mime type guesser
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class MimeTypeGuesser implements MimeTypeGuesserInterface
{
public function guess($filePath)
{
if (!is_file($filePath)) {
throw new UploadableInvalidFileException(sprintf('File "%s" does not exist.',
$filePath
));
}
if (!is_readable($filePath)) {
throw new UploadableFileNotReadableException(sprintf('File "%s" is not readable.',
$filePath
));
}
if (function_exists('finfo_open')) {
if (!$finfo = new \finfo(FILEINFO_MIME_TYPE)) {
return null;
}
return $finfo->file($filePath);
}
return null;
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Gedmo\Uploadable\MimeType;
/**
* Interface for mime type guessers
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
interface MimeTypeGuesserInterface
{
public function guess($filePath);
}

View File

@@ -0,0 +1,722 @@
<?php
namespace Gedmo\Uploadable\MimeType;
/**
* Class that holds a map of mime types and their default extensions
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
abstract class MimeTypesExtensionsMap
{
/**
* Map of mime types and their default extensions.
*
* @var array
*/
public static $map = array(
'application/andrew-inset' => 'ez',
'application/applixware' => 'aw',
'application/atom+xml' => 'atom',
'application/atomcat+xml' => 'atomcat',
'application/atomsvc+xml' => 'atomsvc',
'application/ccxml+xml' => 'ccxml',
'application/cdmi-capability' => 'cdmia',
'application/cdmi-container' => 'cdmic',
'application/cdmi-domain' => 'cdmid',
'application/cdmi-object' => 'cdmio',
'application/cdmi-queue' => 'cdmiq',
'application/cu-seeme' => 'cu',
'application/davmount+xml' => 'davmount',
'application/dssc+der' => 'dssc',
'application/dssc+xml' => 'xdssc',
'application/ecmascript' => 'ecma',
'application/emma+xml' => 'emma',
'application/epub+zip' => 'epub',
'application/exi' => 'exi',
'application/font-tdpfr' => 'pfr',
'application/hyperstudio' => 'stk',
'application/inkml+xml' => 'ink',
'application/ipfix' => 'ipfix',
'application/java-archive' => 'jar',
'application/java-serialized-object' => 'ser',
'application/java-vm' => 'class',
'application/javascript' => 'js',
'application/json' => 'json',
'application/lost+xml' => 'lostxml',
'application/mac-binhex40' => 'hqx',
'application/mac-compactpro' => 'cpt',
'application/mads+xml' => 'mads',
'application/marc' => 'mrc',
'application/marcxml+xml' => 'mrcx',
'application/mathematica' => 'ma',
'application/mathml+xml' => 'mathml',
'application/mbox' => 'mbox',
'application/mediaservercontrol+xml' => 'mscml',
'application/metalink4+xml' => 'meta4',
'application/mets+xml' => 'mets',
'application/mods+xml' => 'mods',
'application/mp21' => 'm21',
'application/mp4' => 'mp4s',
'application/msword' => 'doc',
'application/mxf' => 'mxf',
'application/octet-stream' => 'bin',
'application/oda' => 'oda',
'application/oebps-package+xml' => 'opf',
'application/ogg' => 'ogx',
'application/onenote' => 'onetoc',
'application/oxps' => 'oxps',
'application/patch-ops-error+xml' => 'xer',
'application/pdf' => 'pdf',
'application/pgp-encrypted' => 'pgp',
'application/pgp-signature' => 'asc',
'application/pics-rules' => 'prf',
'application/pkcs10' => 'p10',
'application/pkcs7-mime' => 'p7m',
'application/pkcs7-signature' => 'p7s',
'application/pkcs8' => 'p8',
'application/pkix-attr-cert' => 'ac',
'application/pkix-cert' => 'cer',
'application/pkix-crl' => 'crl',
'application/pkix-pkipath' => 'pkipath',
'application/pkixcmp' => 'pki',
'application/pls+xml' => 'pls',
'application/postscript' => 'ai',
'application/prs.cww' => 'cww',
'application/pskc+xml' => 'pskcxml',
'application/rdf+xml' => 'rdf',
'application/reginfo+xml' => 'rif',
'application/relax-ng-compact-syntax' => 'rnc',
'application/resource-lists+xml' => 'rl',
'application/resource-lists-diff+xml' => 'rld',
'application/rls-services+xml' => 'rs',
'application/rpki-ghostbusters' => 'gbr',
'application/rpki-manifest' => 'mft',
'application/rpki-roa' => 'roa',
'application/rsd+xml' => 'rsd',
'application/rss+xml' => 'rss',
'application/rtf' => 'rtf',
'application/sbml+xml' => 'sbml',
'application/scvp-cv-request' => 'scq',
'application/scvp-cv-response' => 'scs',
'application/scvp-vp-request' => 'spq',
'application/scvp-vp-response' => 'spp',
'application/sdp' => 'sdp',
'application/set-payment-initiation' => 'setpay',
'application/set-registration-initiation' => 'setreg',
'application/shf+xml' => 'shf',
'application/smil+xml' => 'smi',
'application/sparql-query' => 'rq',
'application/sparql-results+xml' => 'srx',
'application/srgs' => 'gram',
'application/srgs+xml' => 'grxml',
'application/sru+xml' => 'sru',
'application/ssml+xml' => 'ssml',
'application/tei+xml' => 'tei',
'application/thraud+xml' => 'tfi',
'application/timestamped-data' => 'tsd',
'application/vnd.3gpp.pic-bw-large' => 'plb',
'application/vnd.3gpp.pic-bw-small' => 'psb',
'application/vnd.3gpp.pic-bw-var' => 'pvb',
'application/vnd.3gpp2.tcap' => 'tcap',
'application/vnd.3m.post-it-notes' => 'pwn',
'application/vnd.accpac.simply.aso' => 'aso',
'application/vnd.accpac.simply.imp' => 'imp',
'application/vnd.acucobol' => 'acu',
'application/vnd.acucorp' => 'atc',
'application/vnd.adobe.air-application-installer-package+zip' => 'air',
'application/vnd.adobe.fxp' => 'fxp',
'application/vnd.adobe.xdp+xml' => 'xdp',
'application/vnd.adobe.xfdf' => 'xfdf',
'application/vnd.ahead.space' => 'ahead',
'application/vnd.airzip.filesecure.azf' => 'azf',
'application/vnd.airzip.filesecure.azs' => 'azs',
'application/vnd.amazon.ebook' => 'azw',
'application/vnd.americandynamics.acc' => 'acc',
'application/vnd.amiga.ami' => 'ami',
'application/vnd.android.package-archive' => 'apk',
'application/vnd.anser-web-certificate-issue-initiation' => 'cii',
'application/vnd.anser-web-funds-transfer-initiation' => 'fti',
'application/vnd.antix.game-component' => 'atx',
'application/vnd.apple.installer+xml' => 'mpkg',
'application/vnd.apple.mpegurl' => 'm3u8',
'application/vnd.aristanetworks.swi' => 'swi',
'application/vnd.astraea-software.iota' => 'iota',
'application/vnd.audiograph' => 'aep',
'application/vnd.blueice.multipass' => 'mpm',
'application/vnd.bmi' => 'bmi',
'application/vnd.businessobjects' => 'rep',
'application/vnd.chemdraw+xml' => 'cdxml',
'application/vnd.chipnuts.karaoke-mmd' => 'mmd',
'application/vnd.cinderella' => 'cdy',
'application/vnd.claymore' => 'cla',
'application/vnd.cloanto.rp9' => 'rp9',
'application/vnd.clonk.c4group' => 'c4g',
'application/vnd.cluetrust.cartomobile-config' => 'c11amc',
'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz',
'application/vnd.commonspace' => 'csp',
'application/vnd.contact.cmsg' => 'cdbcmsg',
'application/vnd.cosmocaller' => 'cmc',
'application/vnd.crick.clicker' => 'clkx',
'application/vnd.crick.clicker.keyboard' => 'clkk',
'application/vnd.crick.clicker.palette' => 'clkp',
'application/vnd.crick.clicker.template' => 'clkt',
'application/vnd.crick.clicker.wordbank' => 'clkw',
'application/vnd.criticaltools.wbs+xml' => 'wbs',
'application/vnd.ctc-posml' => 'pml',
'application/vnd.cups-ppd' => 'ppd',
'application/vnd.curl.car' => 'car',
'application/vnd.curl.pcurl' => 'pcurl',
'application/vnd.data-vision.rdz' => 'rdz',
'application/vnd.dece.data' => 'uvf',
'application/vnd.dece.ttml+xml' => 'uvt',
'application/vnd.dece.unspecified' => 'uvx',
'application/vnd.dece.zip' => 'uvz',
'application/vnd.denovo.fcselayout-link' => 'fe_launch',
'application/vnd.dna' => 'dna',
'application/vnd.dolby.mlp' => 'mlp',
'application/vnd.dpgraph' => 'dpg',
'application/vnd.dreamfactory' => 'dfac',
'application/vnd.dvb.ait' => 'ait',
'application/vnd.dvb.service' => 'svc',
'application/vnd.dynageo' => 'geo',
'application/vnd.ecowin.chart' => 'mag',
'application/vnd.enliven' => 'nml',
'application/vnd.epson.esf' => 'esf',
'application/vnd.epson.msf' => 'msf',
'application/vnd.epson.quickanime' => 'qam',
'application/vnd.epson.salt' => 'slt',
'application/vnd.epson.ssf' => 'ssf',
'application/vnd.eszigno3+xml' => 'es3',
'application/vnd.ezpix-album' => 'ez2',
'application/vnd.ezpix-package' => 'ez3',
'application/vnd.fdf' => 'fdf',
'application/vnd.fdsn.mseed' => 'mseed',
'application/vnd.fdsn.seed' => 'seed',
'application/vnd.flographit' => 'gph',
'application/vnd.fluxtime.clip' => 'ftc',
'application/vnd.framemaker' => 'fm',
'application/vnd.frogans.fnc' => 'fnc',
'application/vnd.frogans.ltf' => 'ltf',
'application/vnd.fsc.weblaunch' => 'fsc',
'application/vnd.fujitsu.oasys' => 'oas',
'application/vnd.fujitsu.oasys2' => 'oa2',
'application/vnd.fujitsu.oasys3' => 'oa3',
'application/vnd.fujitsu.oasysgp' => 'fg5',
'application/vnd.fujitsu.oasysprs' => 'bh2',
'application/vnd.fujixerox.ddd' => 'ddd',
'application/vnd.fujixerox.docuworks' => 'xdw',
'application/vnd.fujixerox.docuworks.binder' => 'xbd',
'application/vnd.fuzzysheet' => 'fzs',
'application/vnd.genomatix.tuxedo' => 'txd',
'application/vnd.geogebra.file' => 'ggb',
'application/vnd.geogebra.tool' => 'ggt',
'application/vnd.geometry-explorer' => 'gex',
'application/vnd.geonext' => 'gxt',
'application/vnd.geoplan' => 'g2w',
'application/vnd.geospace' => 'g3w',
'application/vnd.gmx' => 'gmx',
'application/vnd.google-earth.kml+xml' => 'kml',
'application/vnd.google-earth.kmz' => 'kmz',
'application/vnd.grafeq' => 'gqf',
'application/vnd.groove-account' => 'gac',
'application/vnd.groove-help' => 'ghf',
'application/vnd.groove-identity-message' => 'gim',
'application/vnd.groove-injector' => 'grv',
'application/vnd.groove-tool-message' => 'gtm',
'application/vnd.groove-tool-template' => 'tpl',
'application/vnd.groove-vcard' => 'vcg',
'application/vnd.hal+xml' => 'hal',
'application/vnd.handheld-entertainment+xml' => 'zmm',
'application/vnd.hbci' => 'hbci',
'application/vnd.hhe.lesson-player' => 'les',
'application/vnd.hp-hpgl' => 'hpgl',
'application/vnd.hp-hpid' => 'hpid',
'application/vnd.hp-hps' => 'hps',
'application/vnd.hp-jlyt' => 'jlt',
'application/vnd.hp-pcl' => 'pcl',
'application/vnd.hp-pclxl' => 'pclxl',
'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx',
'application/vnd.hzn-3d-crossword' => 'x3d',
'application/vnd.ibm.minipay' => 'mpy',
'application/vnd.ibm.modcap' => 'afp',
'application/vnd.ibm.rights-management' => 'irm',
'application/vnd.ibm.secure-container' => 'sc',
'application/vnd.iccprofile' => 'icc',
'application/vnd.igloader' => 'igl',
'application/vnd.immervision-ivp' => 'ivp',
'application/vnd.immervision-ivu' => 'ivu',
'application/vnd.insors.igm' => 'igm',
'application/vnd.intercon.formnet' => 'xpw',
'application/vnd.intergeo' => 'i2g',
'application/vnd.intu.qbo' => 'qbo',
'application/vnd.intu.qfx' => 'qfx',
'application/vnd.ipunplugged.rcprofile' => 'rcprofile',
'application/vnd.irepository.package+xml' => 'irp',
'application/vnd.is-xpr' => 'xpr',
'application/vnd.isac.fcs' => 'fcs',
'application/vnd.jam' => 'jam',
'application/vnd.jcp.javame.midlet-rms' => 'rms',
'application/vnd.jisp' => 'jisp',
'application/vnd.joost.joda-archive' => 'joda',
'application/vnd.kahootz' => 'ktz',
'application/vnd.kde.karbon' => 'karbon',
'application/vnd.kde.kchart' => 'chrt',
'application/vnd.kde.kformula' => 'kfo',
'application/vnd.kde.kivio' => 'flw',
'application/vnd.kde.kontour' => 'kon',
'application/vnd.kde.kpresenter' => 'kpr',
'application/vnd.kde.kspread' => 'ksp',
'application/vnd.kde.kword' => 'kwd',
'application/vnd.kenameaapp' => 'htke',
'application/vnd.kidspiration' => 'kia',
'application/vnd.kinar' => 'kne',
'application/vnd.koan' => 'skp',
'application/vnd.kodak-descriptor' => 'sse',
'application/vnd.las.las+xml' => 'lasxml',
'application/vnd.llamagraphics.life-balance.desktop' => 'lbd',
'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe',
'application/vnd.lotus-1-2-3' => '123',
'application/vnd.lotus-approach' => 'apr',
'application/vnd.lotus-freelance' => 'pre',
'application/vnd.lotus-notes' => 'nsf',
'application/vnd.lotus-organizer' => 'org',
'application/vnd.lotus-screencam' => 'scm',
'application/vnd.lotus-wordpro' => 'lwp',
'application/vnd.macports.portpkg' => 'portpkg',
'application/vnd.mcd' => 'mcd',
'application/vnd.medcalcdata' => 'mc1',
'application/vnd.mediastation.cdkey' => 'cdkey',
'application/vnd.mfer' => 'mwf',
'application/vnd.mfmp' => 'mfm',
'application/vnd.micrografx.flo' => 'flo',
'application/vnd.micrografx.igx' => 'igx',
'application/vnd.mif' => 'mif',
'application/vnd.mobius.daf' => 'daf',
'application/vnd.mobius.dis' => 'dis',
'application/vnd.mobius.mbk' => 'mbk',
'application/vnd.mobius.mqy' => 'mqy',
'application/vnd.mobius.msl' => 'msl',
'application/vnd.mobius.plc' => 'plc',
'application/vnd.mobius.txf' => 'txf',
'application/vnd.mophun.application' => 'mpn',
'application/vnd.mophun.certificate' => 'mpc',
'application/vnd.mozilla.xul+xml' => 'xul',
'application/vnd.ms-artgalry' => 'cil',
'application/vnd.ms-cab-compressed' => 'cab',
'application/vnd.ms-excel' => 'xls',
'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam',
'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb',
'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm',
'application/vnd.ms-excel.template.macroenabled.12' => 'xltm',
'application/vnd.ms-fontobject' => 'eot',
'application/vnd.ms-htmlhelp' => 'chm',
'application/vnd.ms-ims' => 'ims',
'application/vnd.ms-lrm' => 'lrm',
'application/vnd.ms-officetheme' => 'thmx',
'application/vnd.ms-pki.seccat' => 'cat',
'application/vnd.ms-pki.stl' => 'stl',
'application/vnd.ms-powerpoint' => 'ppt',
'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam',
'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm',
'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm',
'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm',
'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm',
'application/vnd.ms-project' => 'mpp',
'application/vnd.ms-word.document.macroenabled.12' => 'docm',
'application/vnd.ms-word.template.macroenabled.12' => 'dotm',
'application/vnd.ms-works' => 'wps',
'application/vnd.ms-wpl' => 'wpl',
'application/vnd.ms-xpsdocument' => 'xps',
'application/vnd.mseq' => 'mseq',
'application/vnd.musician' => 'mus',
'application/vnd.muvee.style' => 'msty',
'application/vnd.mynfc' => 'taglet',
'application/vnd.neurolanguage.nlu' => 'nlu',
'application/vnd.noblenet-directory' => 'nnd',
'application/vnd.noblenet-sealer' => 'nns',
'application/vnd.noblenet-web' => 'nnw',
'application/vnd.nokia.n-gage.data' => 'ngdat',
'application/vnd.nokia.n-gage.symbian.install' => 'n-gage',
'application/vnd.nokia.radio-preset' => 'rpst',
'application/vnd.nokia.radio-presets' => 'rpss',
'application/vnd.novadigm.edm' => 'edm',
'application/vnd.novadigm.edx' => 'edx',
'application/vnd.novadigm.ext' => 'ext',
'application/vnd.oasis.opendocument.chart' => 'odc',
'application/vnd.oasis.opendocument.chart-template' => 'otc',
'application/vnd.oasis.opendocument.database' => 'odb',
'application/vnd.oasis.opendocument.formula' => 'odf',
'application/vnd.oasis.opendocument.formula-template' => 'odft',
'application/vnd.oasis.opendocument.graphics' => 'odg',
'application/vnd.oasis.opendocument.graphics-template' => 'otg',
'application/vnd.oasis.opendocument.image' => 'odi',
'application/vnd.oasis.opendocument.image-template' => 'oti',
'application/vnd.oasis.opendocument.presentation' => 'odp',
'application/vnd.oasis.opendocument.presentation-template' => 'otp',
'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',
'application/vnd.oasis.opendocument.text' => 'odt',
'application/vnd.oasis.opendocument.text-master' => 'odm',
'application/vnd.oasis.opendocument.text-template' => 'ott',
'application/vnd.oasis.opendocument.text-web' => 'oth',
'application/vnd.olpc-sugar' => 'xo',
'application/vnd.oma.dd2+xml' => 'dd2',
'application/vnd.openofficeorg.extension' => 'oxt',
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx',
'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx',
'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx',
'application/vnd.osgeo.mapguide.package' => 'mgp',
'application/vnd.osgi.dp' => 'dp',
'application/vnd.palm' => 'pdb',
'application/vnd.pawaafile' => 'paw',
'application/vnd.pg.format' => 'str',
'application/vnd.pg.osasli' => 'ei6',
'application/vnd.picsel' => 'efif',
'application/vnd.pmi.widget' => 'wg',
'application/vnd.pocketlearn' => 'plf',
'application/vnd.powerbuilder6' => 'pbd',
'application/vnd.previewsystems.box' => 'box',
'application/vnd.proteus.magazine' => 'mgz',
'application/vnd.publishare-delta-tree' => 'qps',
'application/vnd.pvi.ptid1' => 'ptid',
'application/vnd.quark.quarkxpress' => 'qxd',
'application/vnd.realvnc.bed' => 'bed',
'application/vnd.recordare.musicxml' => 'mxl',
'application/vnd.recordare.musicxml+xml' => 'musicxml',
'application/vnd.rig.cryptonote' => 'cryptonote',
'application/vnd.rim.cod' => 'cod',
'application/vnd.rn-realmedia' => 'rm',
'application/vnd.route66.link66+xml' => 'link66',
'application/vnd.sailingtracker.track' => 'st',
'application/vnd.seemail' => 'see',
'application/vnd.sema' => 'sema',
'application/vnd.semd' => 'semd',
'application/vnd.semf' => 'semf',
'application/vnd.shana.informed.formdata' => 'ifm',
'application/vnd.shana.informed.formtemplate' => 'itp',
'application/vnd.shana.informed.interchange' => 'iif',
'application/vnd.shana.informed.package' => 'ipk',
'application/vnd.simtech-mindmapper' => 'twd',
'application/vnd.smaf' => 'mmf',
'application/vnd.smart.teacher' => 'teacher',
'application/vnd.solent.sdkm+xml' => 'sdkm',
'application/vnd.spotfire.dxp' => 'dxp',
'application/vnd.spotfire.sfs' => 'sfs',
'application/vnd.stardivision.calc' => 'sdc',
'application/vnd.stardivision.draw' => 'sda',
'application/vnd.stardivision.impress' => 'sdd',
'application/vnd.stardivision.math' => 'smf',
'application/vnd.stardivision.writer' => 'sdw',
'application/vnd.stardivision.writer-global' => 'sgl',
'application/vnd.stepmania.package' => 'smzip',
'application/vnd.stepmania.stepchart' => 'sm',
'application/vnd.sun.xml.calc' => 'sxc',
'application/vnd.sun.xml.calc.template' => 'stc',
'application/vnd.sun.xml.draw' => 'sxd',
'application/vnd.sun.xml.draw.template' => 'std',
'application/vnd.sun.xml.impress' => 'sxi',
'application/vnd.sun.xml.impress.template' => 'sti',
'application/vnd.sun.xml.math' => 'sxm',
'application/vnd.sun.xml.writer' => 'sxw',
'application/vnd.sun.xml.writer.global' => 'sxg',
'application/vnd.sun.xml.writer.template' => 'stw',
'application/vnd.sus-calendar' => 'sus',
'application/vnd.svd' => 'svd',
'application/vnd.symbian.install' => 'sis',
'application/vnd.syncml+xml' => 'xsm',
'application/vnd.syncml.dm+wbxml' => 'bdm',
'application/vnd.syncml.dm+xml' => 'xdm',
'application/vnd.tao.intent-module-archive' => 'tao',
'application/vnd.tcpdump.pcap' => 'pcap',
'application/vnd.tmobile-livetv' => 'tmo',
'application/vnd.trid.tpt' => 'tpt',
'application/vnd.triscape.mxs' => 'mxs',
'application/vnd.trueapp' => 'tra',
'application/vnd.ufdl' => 'ufd',
'application/vnd.uiq.theme' => 'utz',
'application/vnd.umajin' => 'umj',
'application/vnd.unity' => 'unityweb',
'application/vnd.uoml+xml' => 'uoml',
'application/vnd.vcx' => 'vcx',
'application/vnd.visio' => 'vsd',
'application/vnd.visionary' => 'vis',
'application/vnd.vsf' => 'vsf',
'application/vnd.wap.wbxml' => 'wbxml',
'application/vnd.wap.wmlc' => 'wmlc',
'application/vnd.wap.wmlscriptc' => 'wmlsc',
'application/vnd.webturbo' => 'wtb',
'application/vnd.wolfram.player' => 'nbp',
'application/vnd.wordperfect' => 'wpd',
'application/vnd.wqd' => 'wqd',
'application/vnd.wt.stf' => 'stf',
'application/vnd.xara' => 'xar',
'application/vnd.xfdl' => 'xfdl',
'application/vnd.yamaha.hv-dic' => 'hvd',
'application/vnd.yamaha.hv-script' => 'hvs',
'application/vnd.yamaha.hv-voice' => 'hvp',
'application/vnd.yamaha.openscoreformat' => 'osf',
'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg',
'application/vnd.yamaha.smaf-audio' => 'saf',
'application/vnd.yamaha.smaf-phrase' => 'spf',
'application/vnd.yellowriver-custom-menu' => 'cmp',
'application/vnd.zul' => 'zir',
'application/vnd.zzazz.deck+xml' => 'zaz',
'application/voicexml+xml' => 'vxml',
'application/widget' => 'wgt',
'application/winhlp' => 'hlp',
'application/wsdl+xml' => 'wsdl',
'application/wspolicy+xml' => 'wspolicy',
'application/x-7z-compressed' => '7z',
'application/x-abiword' => 'abw',
'application/x-ace-compressed' => 'ace',
'application/x-authorware-bin' => 'aab',
'application/x-authorware-map' => 'aam',
'application/x-authorware-seg' => 'aas',
'application/x-bcpio' => 'bcpio',
'application/x-bittorrent' => 'torrent',
'application/x-bzip' => 'bz',
'application/x-bzip2' => 'bz2',
'application/x-cdlink' => 'vcd',
'application/x-chat' => 'chat',
'application/x-chess-pgn' => 'pgn',
'application/x-cpio' => 'cpio',
'application/x-csh' => 'csh',
'application/x-debian-package' => 'deb',
'application/x-director' => 'dir',
'application/x-doom' => 'wad',
'application/x-dtbncx+xml' => 'ncx',
'application/x-dtbook+xml' => 'dtb',
'application/x-dtbresource+xml' => 'res',
'application/x-dvi' => 'dvi',
'application/x-font-bdf' => 'bdf',
'application/x-font-ghostscript' => 'gsf',
'application/x-font-linux-psf' => 'psf',
'application/x-font-otf' => 'otf',
'application/x-font-pcf' => 'pcf',
'application/x-font-snf' => 'snf',
'application/x-font-ttf' => 'ttf',
'application/x-font-type1' => 'pfa',
'application/x-font-woff' => 'woff',
'application/x-futuresplash' => 'spl',
'application/x-gnumeric' => 'gnumeric',
'application/x-gtar' => 'gtar',
'application/x-hdf' => 'hdf',
'application/x-java-jnlp-file' => 'jnlp',
'application/x-latex' => 'latex',
'application/x-mobipocket-ebook' => 'prc',
'application/x-ms-application' => 'application',
'application/x-ms-wmd' => 'wmd',
'application/x-ms-wmz' => 'wmz',
'application/x-ms-xbap' => 'xbap',
'application/x-msaccess' => 'mdb',
'application/x-msbinder' => 'obd',
'application/x-mscardfile' => 'crd',
'application/x-msclip' => 'clp',
'application/x-msdownload' => 'exe',
'application/x-msmediaview' => 'mvb',
'application/x-msmetafile' => 'wmf',
'application/x-msmoney' => 'mny',
'application/x-mspublisher' => 'pub',
'application/x-msschedule' => 'scd',
'application/x-msterminal' => 'trm',
'application/x-mswrite' => 'wri',
'application/x-netcdf' => 'nc',
'application/x-pkcs12' => 'p12',
'application/x-pkcs7-certificates' => 'p7b',
'application/x-pkcs7-certreqresp' => 'p7r',
'application/x-rar-compressed' => 'rar',
'application/x-sh' => 'sh',
'application/x-shar' => 'shar',
'application/x-shockwave-flash' => 'swf',
'application/x-silverlight-app' => 'xap',
'application/x-stuffit' => 'sit',
'application/x-stuffitx' => 'sitx',
'application/x-sv4cpio' => 'sv4cpio',
'application/x-sv4crc' => 'sv4crc',
'application/x-tar' => 'tar',
'application/x-tcl' => 'tcl',
'application/x-tex' => 'tex',
'application/x-tex-tfm' => 'tfm',
'application/x-texinfo' => 'texinfo',
'application/x-ustar' => 'ustar',
'application/x-wais-source' => 'src',
'application/x-x509-ca-cert' => 'der',
'application/x-xfig' => 'fig',
'application/x-xpinstall' => 'xpi',
'application/xcap-diff+xml' => 'xdf',
'application/xenc+xml' => 'xenc',
'application/xhtml+xml' => 'xhtml',
'application/xml' => 'xml',
'application/xml-dtd' => 'dtd',
'application/xop+xml' => 'xop',
'application/xslt+xml' => 'xslt',
'application/xspf+xml' => 'xspf',
'application/xv+xml' => 'mxml',
'application/yang' => 'yang',
'application/yin+xml' => 'yin',
'application/zip' => 'zip',
'audio/adpcm' => 'adp',
'audio/basic' => 'au',
'audio/midi' => 'mid',
'audio/mp4' => 'mp4a',
'audio/mpeg' => 'mpga',
'audio/ogg' => 'oga',
'audio/vnd.dece.audio' => 'uva',
'audio/vnd.digital-winds' => 'eol',
'audio/vnd.dra' => 'dra',
'audio/vnd.dts' => 'dts',
'audio/vnd.dts.hd' => 'dtshd',
'audio/vnd.lucent.voice' => 'lvp',
'audio/vnd.ms-playready.media.pya' => 'pya',
'audio/vnd.nuera.ecelp4800' => 'ecelp4800',
'audio/vnd.nuera.ecelp7470' => 'ecelp7470',
'audio/vnd.nuera.ecelp9600' => 'ecelp9600',
'audio/vnd.rip' => 'rip',
'audio/webm' => 'weba',
'audio/x-aac' => 'aac',
'audio/x-aiff' => 'aif',
'audio/x-mpegurl' => 'm3u',
'audio/x-ms-wax' => 'wax',
'audio/x-ms-wma' => 'wma',
'audio/x-pn-realaudio' => 'ram',
'audio/x-pn-realaudio-plugin' => 'rmp',
'audio/x-wav' => 'wav',
'chemical/x-cdx' => 'cdx',
'chemical/x-cif' => 'cif',
'chemical/x-cmdf' => 'cmdf',
'chemical/x-cml' => 'cml',
'chemical/x-csml' => 'csml',
'chemical/x-xyz' => 'xyz',
'image/bmp' => 'bmp',
'image/cgm' => 'cgm',
'image/g3fax' => 'g3',
'image/gif' => 'gif',
'image/ief' => 'ief',
'image/jpeg' => 'jpeg',
'image/ktx' => 'ktx',
'image/png' => 'png',
'image/prs.btif' => 'btif',
'image/svg+xml' => 'svg',
'image/tiff' => 'tiff',
'image/vnd.adobe.photoshop' => 'psd',
'image/vnd.dece.graphic' => 'uvi',
'image/vnd.dvb.subtitle' => 'sub',
'image/vnd.djvu' => 'djvu',
'image/vnd.dwg' => 'dwg',
'image/vnd.dxf' => 'dxf',
'image/vnd.fastbidsheet' => 'fbs',
'image/vnd.fpx' => 'fpx',
'image/vnd.fst' => 'fst',
'image/vnd.fujixerox.edmics-mmr' => 'mmr',
'image/vnd.fujixerox.edmics-rlc' => 'rlc',
'image/vnd.ms-modi' => 'mdi',
'image/vnd.net-fpx' => 'npx',
'image/vnd.wap.wbmp' => 'wbmp',
'image/vnd.xiff' => 'xif',
'image/webp' => 'webp',
'image/x-cmu-raster' => 'ras',
'image/x-cmx' => 'cmx',
'image/x-freehand' => 'fh',
'image/x-icon' => 'ico',
'image/x-pcx' => 'pcx',
'image/x-pict' => 'pic',
'image/x-portable-anymap' => 'pnm',
'image/x-portable-bitmap' => 'pbm',
'image/x-portable-graymap' => 'pgm',
'image/x-portable-pixmap' => 'ppm',
'image/x-rgb' => 'rgb',
'image/x-xbitmap' => 'xbm',
'image/x-xpixmap' => 'xpm',
'image/x-xwindowdump' => 'xwd',
'message/rfc822' => 'eml',
'model/iges' => 'igs',
'model/mesh' => 'msh',
'model/vnd.collada+xml' => 'dae',
'model/vnd.dwf' => 'dwf',
'model/vnd.gdl' => 'gdl',
'model/vnd.gtw' => 'gtw',
'model/vnd.mts' => 'mts',
'model/vnd.vtu' => 'vtu',
'model/vrml' => 'wrl',
'text/calendar' => 'ics',
'text/css' => 'css',
'text/csv' => 'csv',
'text/html' => 'html',
'text/n3' => 'n3',
'text/plain' => 'txt',
'text/prs.lines.tag' => 'dsc',
'text/richtext' => 'rtx',
'text/sgml' => 'sgml',
'text/tab-separated-values' => 'tsv',
'text/troff' => 't',
'text/turtle' => 'ttl',
'text/uri-list' => 'uri',
'text/vcard' => 'vcard',
'text/vnd.curl' => 'curl',
'text/vnd.curl.dcurl' => 'dcurl',
'text/vnd.curl.scurl' => 'scurl',
'text/vnd.curl.mcurl' => 'mcurl',
'text/vnd.dvb.subtitle' => 'sub',
'text/vnd.fly' => 'fly',
'text/vnd.fmi.flexstor' => 'flx',
'text/vnd.graphviz' => 'gv',
'text/vnd.in3d.3dml' => '3dml',
'text/vnd.in3d.spot' => 'spot',
'text/vnd.sun.j2me.app-descriptor' => 'jad',
'text/vnd.wap.wml' => 'wml',
'text/vnd.wap.wmlscript' => 'wmls',
'text/x-asm' => 's',
'text/x-c' => 'c',
'text/x-fortran' => 'f',
'text/x-pascal' => 'p',
'text/x-java-source' => 'java',
'text/x-setext' => 'etx',
'text/x-uuencode' => 'uu',
'text/x-vcalendar' => 'vcs',
'text/x-vcard' => 'vcf',
'video/3gpp' => '3gp',
'video/3gpp2' => '3g2',
'video/h261' => 'h261',
'video/h263' => 'h263',
'video/h264' => 'h264',
'video/jpeg' => 'jpgv',
'video/jpm' => 'jpm',
'video/mj2' => 'mj2',
'video/mp4' => 'mp4',
'video/mpeg' => 'mpeg',
'video/ogg' => 'ogv',
'video/quicktime' => 'qt',
'video/vnd.dece.hd' => 'uvh',
'video/vnd.dece.mobile' => 'uvm',
'video/vnd.dece.pd' => 'uvp',
'video/vnd.dece.sd' => 'uvs',
'video/vnd.dece.video' => 'uvv',
'video/vnd.dvb.file' => 'dvb',
'video/vnd.fvt' => 'fvt',
'video/vnd.mpegurl' => 'mxu',
'video/vnd.ms-playready.media.pyv' => 'pyv',
'video/vnd.uvvu.mp4' => 'uvu',
'video/vnd.vivo' => 'viv',
'video/webm' => 'webm',
'video/x-f4v' => 'f4v',
'video/x-fli' => 'fli',
'video/x-flv' => 'flv',
'video/x-m4v' => 'm4v',
'video/x-ms-asf' => 'asf',
'video/x-ms-wm' => 'wm',
'video/x-ms-wmv' => 'wmv',
'video/x-ms-wmx' => 'wmx',
'video/x-ms-wvx' => 'wvx',
'video/x-msvideo' => 'avi',
'video/x-sgi-movie' => 'movie',
'x-conference/x-cooltalk' => 'ice',
);
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Gedmo\Uploadable;
/**
* This interface is not necessary but can be implemented for
* Domain Objects which in some cases needs to be identified as
* Uploadable
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
interface Uploadable
{
// this interface is not necessary to implement
/**
* @gedmo:Uploadable
* to mark the class as Uploadable use class annotation @gedmo:Uploadable
* this object will be able Uploadable
* example:
*
* @gedmo:Uploadable
* class MyEntity
*/
}

View File

@@ -0,0 +1,781 @@
<?php
namespace Gedmo\Uploadable;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Gedmo\Mapping\MappedEventSubscriber;
use Doctrine\Common\EventArgs;
use Gedmo\Mapping\Event\AdapterInterface;
use Gedmo\Exception\UploadablePartialException;
use Gedmo\Exception\UploadableCantWriteException;
use Gedmo\Exception\UploadableExtensionException;
use Gedmo\Exception\UploadableFormSizeException;
use Gedmo\Exception\UploadableIniSizeException;
use Gedmo\Exception\UploadableNoFileException;
use Gedmo\Exception\UploadableNoTmpDirException;
use Gedmo\Exception\UploadableUploadException;
use Gedmo\Exception\UploadableFileAlreadyExistsException;
use Gedmo\Exception\UploadableNoPathDefinedException;
use Gedmo\Exception\UploadableMaxSizeException;
use Gedmo\Exception\UploadableInvalidMimeTypeException;
use Gedmo\Exception\UploadableCouldntGuessMimeTypeException;
use Gedmo\Uploadable\Mapping\Validator;
use Gedmo\Uploadable\FileInfo\FileInfoInterface;
use Gedmo\Uploadable\MimeType\MimeTypeGuesser;
use Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface;
use Doctrine\Common\NotifyPropertyChanged;
use Gedmo\Uploadable\Event\UploadablePreFileProcessEventArgs;
use Gedmo\Uploadable\Event\UploadablePostFileProcessEventArgs;
/**
* Uploadable listener
*
* @author Gustavo Falco <comfortablynumb84@gmail.com>
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class UploadableListener extends MappedEventSubscriber
{
const ACTION_INSERT = 'INSERT';
const ACTION_UPDATE = 'UPDATE';
/**
* Default path to move files in
*
* @var string
*/
private $defaultPath;
/**
* Mime type guesser
*
* @var \Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface
*/
private $mimeTypeGuesser;
/**
* Default FileInfoInterface class
*
* @var string
*/
private $defaultFileInfoClass = 'Gedmo\Uploadable\FileInfo\FileInfoArray';
/**
* Array of files to remove on postFlush
*
* @var array
*/
private $pendingFileRemovals = array();
/**
* Array of FileInfoInterface objects. The index is the hash of the entity owner
* of the FileInfoInterface object.
*
* @var array
*/
private $fileInfoObjects = array();
public function __construct(MimeTypeGuesserInterface $mimeTypeGuesser = null)
{
parent::__construct();
$this->mimeTypeGuesser = $mimeTypeGuesser ? $mimeTypeGuesser : new MimeTypeGuesser();
}
/**
* {@inheritdoc}
*/
public function getSubscribedEvents()
{
return array(
'loadClassMetadata',
'preFlush',
'onFlush',
'postFlush',
);
}
/**
* This event is needed in special cases where the entity needs to be updated, but it only has the
* file field modified. Since we can't mark an entity as "dirty" in the "addEntityFileInfo" method,
* doctrine thinks the entity has no changes, which produces that the "onFlush" event gets never called.
* Here we mark the entity as dirty, so the "onFlush" event gets called, and the file is processed.
*
* @param \Doctrine\Common\EventArgs $args
*/
public function preFlush(EventArgs $args)
{
if (empty($this->fileInfoObjects)) {
// Nothing to do
return;
}
$ea = $this->getEventAdapter($args);
$om = $ea->getObjectManager();
$uow = $om->getUnitOfWork();
$first = reset($this->fileInfoObjects);
$meta = $om->getClassMetadata(get_class($first['entity']));
$config = $this->getConfiguration($om, $meta->name);
foreach ($this->fileInfoObjects as $info) {
$entity = $info['entity'];
// If the entity is in the identity map, it means it will be updated. We need to force the
// "dirty check" here by "modifying" the path. We are actually setting the same value, but
// this will mark the entity as dirty, and the "onFlush" event will be fired, even if there's
// no other change in the entity's fields apart from the file itself.
if ($uow->isInIdentityMap($entity)) {
if ($config['filePathField']) {
$path = $this->getFilePathFieldValue($meta, $config, $entity);
$uow->propertyChanged($entity, $config['filePathField'], $path, $path);
} else {
$fileName = $this->getFileNameFieldValue($meta, $config, $entity);
$uow->propertyChanged($entity, $config['fileNameField'], $fileName, $fileName);
}
$uow->scheduleForUpdate($entity);
}
}
}
/**
* Handle file-uploading depending on the action
* being done with objects
*
* @param \Doctrine\Common\EventArgs $args
*/
public function onFlush(EventArgs $args)
{
$ea = $this->getEventAdapter($args);
$om = $ea->getObjectManager();
$uow = $om->getUnitOfWork();
// Do we need to upload files?
foreach ($this->fileInfoObjects as $info) {
$entity = $info['entity'];
$scheduledForInsert = $uow->isScheduledForInsert($entity);
$scheduledForUpdate = $uow->isScheduledForUpdate($entity);
$action = ($scheduledForInsert || $scheduledForUpdate) ?
($scheduledForInsert ? self::ACTION_INSERT : self::ACTION_UPDATE) :
false;
if ($action) {
$this->processFile($ea, $entity, $action);
}
}
// Do we need to remove any files?
foreach ($ea->getScheduledObjectDeletions($uow) as $object) {
$meta = $om->getClassMetadata(get_class($object));
if ($config = $this->getConfiguration($om, $meta->name)) {
if (isset($config['uploadable']) && $config['uploadable']) {
$this->addFileRemoval($meta, $config, $object);
}
}
}
}
/**
* Handle removal of files
*
* @param \Doctrine\Common\EventArgs $args
*/
public function postFlush(EventArgs $args)
{
if (!empty($this->pendingFileRemovals)) {
foreach ($this->pendingFileRemovals as $file) {
$this->removeFile($file);
}
$this->pendingFileRemovals = array();
}
$this->fileInfoObjects = array();
}
/**
* If it's a Uploadable object, verify if the file was uploaded.
* If that's the case, process it.
*
* @param \Gedmo\Mapping\Event\AdapterInterface $ea
* @param object $object
* @param string $action
*
* @throws \Gedmo\Exception\UploadableNoPathDefinedException
* @throws \Gedmo\Exception\UploadableCouldntGuessMimeTypeException
* @throws \Gedmo\Exception\UploadableMaxSizeException
* @throws \Gedmo\Exception\UploadableInvalidMimeTypeException
*/
public function processFile(AdapterInterface $ea, $object, $action)
{
$oid = spl_object_hash($object);
$om = $ea->getObjectManager();
$uow = $om->getUnitOfWork();
$meta = $om->getClassMetadata(get_class($object));
$config = $this->getConfiguration($om, $meta->name);
if (!$config || !isset($config['uploadable']) || !$config['uploadable']) {
// Nothing to do
return;
}
$refl = $meta->getReflectionClass();
$fileInfo = $this->fileInfoObjects[$oid]['fileInfo'];
$evm = $om->getEventManager();
if ($evm->hasListeners(Events::uploadablePreFileProcess)) {
$evm->dispatchEvent(Events::uploadablePreFileProcess, new UploadablePreFileProcessEventArgs(
$this,
$om,
$config,
$fileInfo,
$object,
$action
));
}
// Validations
if ($config['maxSize'] > 0 && $fileInfo->getSize() > $config['maxSize']) {
$msg = 'File "%s" exceeds the maximum allowed size of %d bytes. File size: %d bytes';
throw new UploadableMaxSizeException(sprintf($msg,
$fileInfo->getName(),
$config['maxSize'],
$fileInfo->getSize()
));
}
$mime = $this->mimeTypeGuesser->guess($fileInfo->getTmpName());
if (!$mime) {
throw new UploadableCouldntGuessMimeTypeException(sprintf('Couldn\'t guess mime type for file "%s".',
$fileInfo->getName()
));
}
if ($config['allowedTypes'] || $config['disallowedTypes']) {
$ok = $config['allowedTypes'] ? false : true;
$mimes = $config['allowedTypes'] ? $config['allowedTypes'] : $config['disallowedTypes'];
foreach ($mimes as $m) {
if ($mime === $m) {
$ok = $config['allowedTypes'] ? true : false;
break;
}
}
if (!$ok) {
throw new UploadableInvalidMimeTypeException(sprintf('Invalid mime type "%s" for file "%s".',
$mime,
$fileInfo->getName()
));
}
}
$path = $this->getPath($meta, $config, $object);
if ($action === self::ACTION_UPDATE) {
// First we add the original file to the pendingFileRemovals array
$this->addFileRemoval($meta, $config, $object);
}
// We generate the filename based on configuration
$generatorNamespace = 'Gedmo\Uploadable\FilenameGenerator';
switch ($config['filenameGenerator']) {
case Validator::FILENAME_GENERATOR_ALPHANUMERIC:
$generatorClass = $generatorNamespace.'\FilenameGeneratorAlphanumeric';
break;
case Validator::FILENAME_GENERATOR_SHA1:
$generatorClass = $generatorNamespace.'\FilenameGeneratorSha1';
break;
case Validator::FILENAME_GENERATOR_NONE:
$generatorClass = false;
break;
default:
$generatorClass = $config['filenameGenerator'];
}
$info = $this->moveFile($fileInfo, $path, $generatorClass, $config['allowOverwrite'], $config['appendNumber'], $object);
// We override the mime type with the guessed one
$info['fileMimeType'] = $mime;
if ($config['callback'] !== '') {
$callbackMethod = $refl->getMethod($config['callback']);
$callbackMethod->setAccessible(true);
$callbackMethod->invokeArgs($object, array($info));
}
if ($config['filePathField']) {
$this->updateField($object, $uow, $ea, $meta, $config['filePathField'], $info['filePath']);
}
if ($config['fileNameField']) {
$this->updateField($object, $uow, $ea, $meta, $config['fileNameField'], $info['fileName']);
}
if ($config['fileMimeTypeField']) {
$this->updateField($object, $uow, $ea, $meta, $config['fileMimeTypeField'], $info['fileMimeType']);
}
if ($config['fileSizeField']) {
$this->updateField($object, $uow, $ea, $meta, $config['fileSizeField'], $info['fileSize']);
}
$ea->recomputeSingleObjectChangeSet($uow, $meta, $object);
if ($evm->hasListeners(Events::uploadablePostFileProcess)) {
$evm->dispatchEvent(Events::uploadablePostFileProcess, new UploadablePostFileProcessEventArgs(
$this,
$om,
$config,
$fileInfo,
$object,
$action
));
}
unset($this->fileInfoObjects[$oid]);
}
/**
* @param ClassMetadata $meta
* @param array $config
* @param object $object Entity
*
* @return string
*
* @throws UploadableNoPathDefinedException
*/
protected function getPath(ClassMetadata $meta, array $config, $object)
{
$path = $config['path'];
if ($path === '') {
$defaultPath = $this->getDefaultPath();
if ($config['pathMethod'] !== '') {
$pathMethod = $meta->getReflectionClass()->getMethod($config['pathMethod']);
$pathMethod->setAccessible(true);
$path = $pathMethod->invoke($object, $defaultPath);
} elseif ($defaultPath !== null) {
$path = $defaultPath;
} else {
$msg = 'You have to define the path to save files either in the listener, or in the class "%s"';
throw new UploadableNoPathDefinedException(
sprintf($msg, $meta->name)
);
}
}
Validator::validatePath($path);
$path = rtrim($path, '\/');
return $path;
}
/**
* @param ClassMetadata $meta
* @param array $config
* @param object $object Entity
*/
protected function addFileRemoval($meta, $config, $object)
{
if ($config['filePathField']) {
$this->pendingFileRemovals[] = $this->getFilePathFieldValue($meta, $config, $object);
} else {
$path = $this->getPath($meta, $config, $object);
$fileName = $this->getFileNameFieldValue($meta, $config, $object);
$this->pendingFileRemovals[] = $path.DIRECTORY_SEPARATOR.$fileName;
}
}
/**
* @param string $filePath
*/
protected function cancelFileRemoval($filePath)
{
$k = array_search($filePath, $this->pendingFileRemovals, true);
if (false !== $k) {
unset($this->pendingFileRemovals[$k]);
}
}
/**
* Returns value of the entity's property
*
* @param ClassMetadata $meta
* @param string $propertyName
* @param object $object
*
* @return mixed
*/
protected function getPropertyValueFromObject(ClassMetadata $meta, $propertyName, $object)
{
$refl = $meta->getReflectionClass();
$filePathField = $refl->getProperty($propertyName);
$filePathField->setAccessible(true);
$filePath = $filePathField->getValue($object);
return $filePath;
}
/**
* Returns the path of the entity's file
*
* @param ClassMetadata $meta
* @param array $config
* @param object $object
*
* @return string
*/
protected function getFilePathFieldValue(ClassMetadata $meta, array $config, $object)
{
return $this->getPropertyValueFromObject($meta, $config['filePathField'], $object);
}
/**
* Returns the name of the entity's file
*
* @param ClassMetadata $meta
* @param array $config
* @param object $object
*
* @return string
*/
protected function getFileNameFieldValue(ClassMetadata $meta, array $config, $object)
{
return $this->getPropertyValueFromObject($meta, $config['fileNameField'], $object);
}
/**
* Simple wrapper for the function "unlink" to ease testing
*
* @param string $filePath
*
* @return bool
*/
public function removeFile($filePath)
{
if (is_file($filePath)) {
return @unlink($filePath);
}
return false;
}
/**
* Moves the file to the specified path
*
* @param FileInfoInterface $fileInfo
* @param string $path
* @param bool $filenameGeneratorClass
* @param bool $overwrite
* @param bool $appendNumber
* @param object $object
*
* @return array
*
* @throws \Gedmo\Exception\UploadableUploadException
* @throws \Gedmo\Exception\UploadableNoFileException
* @throws \Gedmo\Exception\UploadableExtensionException
* @throws \Gedmo\Exception\UploadableIniSizeException
* @throws \Gedmo\Exception\UploadableFormSizeException
* @throws \Gedmo\Exception\UploadableFileAlreadyExistsException
* @throws \Gedmo\Exception\UploadablePartialException
* @throws \Gedmo\Exception\UploadableNoTmpDirException
* @throws \Gedmo\Exception\UploadableCantWriteException
*/
public function moveFile(FileInfoInterface $fileInfo, $path, $filenameGeneratorClass = false, $overwrite = false, $appendNumber = false, $object)
{
if ($fileInfo->getError() > 0) {
switch ($fileInfo->getError()) {
case 1:
$msg = 'Size of uploaded file "%s" exceeds limit imposed by directive "upload_max_filesize" in php.ini';
throw new UploadableIniSizeException(sprintf($msg, $fileInfo->getName()));
case 2:
$msg = 'Size of uploaded file "%s" exceeds limit imposed by option MAX_FILE_SIZE in your form.';
throw new UploadableFormSizeException(sprintf($msg, $fileInfo->getName()));
case 3:
$msg = 'File "%s" was partially uploaded.';
throw new UploadablePartialException(sprintf($msg, $fileInfo->getName()));
case 4:
$msg = 'No file was uploaded!';
throw new UploadableNoFileException(sprintf($msg, $fileInfo->getName()));
case 6:
$msg = 'Upload failed. Temp dir is missing.';
throw new UploadableNoTmpDirException($msg);
case 7:
$msg = 'File "%s" couldn\'t be uploaded because directory is not writable.';
throw new UploadableCantWriteException(sprintf($msg, $fileInfo->getName()));
case 8:
$msg = 'A PHP Extension stopped the uploaded for some reason.';
throw new UploadableExtensionException(sprintf($msg, $fileInfo->getName()));
default:
throw new UploadableUploadException(sprintf('There was an unknown problem while uploading file "%s"',
$fileInfo->getName()
));
}
}
$info = array(
'fileName' => '',
'fileExtension' => '',
'fileWithoutExt' => '',
'origFileName' => '',
'filePath' => '',
'fileMimeType' => $fileInfo->getType(),
'fileSize' => $fileInfo->getSize(),
);
$info['fileName'] = basename($fileInfo->getName());
$info['filePath'] = $path.'/'.$info['fileName'];
$hasExtension = strrpos($info['fileName'], '.');
if ($hasExtension) {
$info['fileExtension'] = substr($info['filePath'], strrpos($info['filePath'], '.'));
$info['fileWithoutExt'] = substr($info['filePath'], 0, strrpos($info['filePath'], '.'));
} else {
$info['fileWithoutExt'] = $info['fileName'];
}
// Save the original filename for later use
$info['origFileName'] = $info['fileName'];
// Now we generate the filename using the configured class
if ($filenameGeneratorClass) {
$filename = $filenameGeneratorClass::generate(
str_replace($path.'/', '', $info['fileWithoutExt']),
$info['fileExtension'],
$object
);
$info['filePath'] = str_replace(
'/'.$info['fileName'],
'/'.$filename,
$info['filePath']
);
$info['fileName'] = $filename;
if ($pos = strrpos($info['filePath'], '.')) {
// ignores positions like "./file" at 0 see #915
$info['fileWithoutExt'] = substr($info['filePath'], 0, $pos);
} else {
$info['fileWithoutExt'] = $info['filePath'];
}
}
if (is_file($info['filePath'])) {
if ($overwrite) {
$this->cancelFileRemoval($info['filePath']);
$this->removeFile($info['filePath']);
} elseif ($appendNumber) {
$counter = 1;
$info['filePath'] = $info['fileWithoutExt'].'-'.$counter.$info['fileExtension'];
do {
$info['filePath'] = $info['fileWithoutExt'].'-'.(++$counter).$info['fileExtension'];
} while (is_file($info['filePath']));
} else {
throw new UploadableFileAlreadyExistsException(sprintf('File "%s" already exists!',
$info['filePath']
));
}
}
if (!$this->doMoveFile($fileInfo->getTmpName(), $info['filePath'], $fileInfo->isUploadedFile())) {
throw new UploadableUploadException(sprintf('File "%s" was not uploaded, or there was a problem moving it to the location "%s".',
$fileInfo->getName(),
$path
));
}
return $info;
}
/**
* Simple wrapper method used to move the file. If it's an uploaded file
* it will use the "move_uploaded_file method. If it's not, it will
* simple move it
*
* @param string $source Source file
* @param string $dest Destination file
* @param bool $isUploadedFile Whether this is an uploaded file?
*
* @return bool
*/
public function doMoveFile($source, $dest, $isUploadedFile = true)
{
return $isUploadedFile ? @move_uploaded_file($source, $dest) : @copy($source, $dest);
}
/**
* Maps additional metadata
*
* @param EventArgs $eventArgs
*/
public function loadClassMetadata(EventArgs $eventArgs)
{
$ea = $this->getEventAdapter($eventArgs);
$this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata());
}
/**
* Sets the default path
*
* @param string $path
*
* @return void
*/
public function setDefaultPath($path)
{
$this->defaultPath = $path;
}
/**
* Returns default path
*
* @return string
*/
public function getDefaultPath()
{
return $this->defaultPath;
}
/**
* Sets file info default class
*
* @param string $defaultFileInfoClass
*
* @return void
*/
public function setDefaultFileInfoClass($defaultFileInfoClass)
{
$fileInfoInterface = 'Gedmo\\Uploadable\\FileInfo\\FileInfoInterface';
$refl = is_string($defaultFileInfoClass) && class_exists($defaultFileInfoClass) ?
new \ReflectionClass($defaultFileInfoClass) :
false;
if (!$refl || !$refl->implementsInterface($fileInfoInterface)) {
$msg = sprintf('Default FileInfo class must be a valid class, and it must implement "%s".',
$fileInfoInterface
);
throw new \Gedmo\Exception\InvalidArgumentException($msg);
}
$this->defaultFileInfoClass = $defaultFileInfoClass;
}
/**
* Returns file info default class
*
* @return string
*/
public function getDefaultFileInfoClass()
{
return $this->defaultFileInfoClass;
}
/**
* Adds a FileInfoInterface object for the given entity
*
* @param object $entity
* @param array|FileInfoInterface $fileInfo
*
* @throws \RuntimeException
*/
public function addEntityFileInfo($entity, $fileInfo)
{
$fileInfoClass = $this->getDefaultFileInfoClass();
$fileInfo = is_array($fileInfo) ? new $fileInfoClass($fileInfo) : $fileInfo;
if (!$fileInfo instanceof FileInfoInterface) {
$msg = 'You must pass an instance of FileInfoInterface or a valid array for entity of class "%s".';
throw new \RuntimeException(sprintf($msg, get_class($entity)));
}
$this->fileInfoObjects[spl_object_hash($entity)] = array(
'entity' => $entity,
'fileInfo' => $fileInfo,
);
}
/**
* @param object $entity
*
* @return FileInfoInterface
*/
public function getEntityFileInfo($entity)
{
$oid = spl_object_hash($entity);
if (!isset($this->fileInfoObjects[$oid])) {
throw new \RuntimeException(sprintf('There\'s no FileInfoInterface object for entity of class "%s".',
get_class($entity)
));
}
return $this->fileInfoObjects[$oid]['fileInfo'];
}
/**
* {@inheritDoc}
*/
protected function getNamespace()
{
return __NAMESPACE__;
}
/**
* @param \Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface $mimeTypeGuesser
*/
public function setMimeTypeGuesser(MimeTypeGuesserInterface $mimeTypeGuesser)
{
$this->mimeTypeGuesser = $mimeTypeGuesser;
}
/**
* @return \Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface
*/
public function getMimeTypeGuesser()
{
return $this->mimeTypeGuesser;
}
/**
* @param object $object
* @param object $uow
* @param AdapterInterface $ea
* @param ClassMetadata $meta
* @param String $field
* @param mixed $value
* @param bool $notifyPropertyChanged
*/
protected function updateField($object, $uow, AdapterInterface $ea, ClassMetadata $meta, $field, $value, $notifyPropertyChanged = true)
{
$property = $meta->getReflectionProperty($field);
$oldValue = $property->getValue($object);
$property->setValue($object, $value);
if ($notifyPropertyChanged && $object instanceof NotifyPropertyChanged) {
$uow = $ea->getObjectManager()->getUnitOfWork();
$uow->propertyChanged($object, $field, $oldValue, $value);
}
}
}