partkeepr

fork of partkeepr
git clone https://git.e1e0.net/partkeepr.git
Log | Files | Refs | Submodules | README | LICENSE

commit 1f824b011d0df5b0b2d54e98e1b6549fe72611bd
parent 3e96fe94fdaf2392d110a2c6057412981f95e212
Author: Felicitus <felicitus@felicitus.org>
Date:   Mon,  6 Jul 2015 03:52:06 +0200

Refactored UploadedFile and Image as services, added annotation to mark properties as files to replace temporary files, fixed temporary file uploads for the manufacturer service

Diffstat:
Mapp/AppKernel.php | 1+
Mapp/config/config.yml | 9++++++++-
Mapp/config/partkeepr.yml | 2+-
Mcomposer.json | 4+++-
Mcomposer.lock | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/PartKeepr/ImageBundle/Controller/ImageController.php | 29++++++++++++++---------------
Msrc/PartKeepr/ImageBundle/Controller/TemporaryImageController.php | 30+++++++++++++-----------------
Msrc/PartKeepr/ImageBundle/DependencyInjection/Configuration.php | 11++---------
Msrc/PartKeepr/ImageBundle/DependencyInjection/PartKeeprExtension.php | 15++++++++++++---
Msrc/PartKeepr/ImageBundle/Entity/Image.php | 113+++++++++++++++++++++++++++++--------------------------------------------------
Msrc/PartKeepr/ImageBundle/Entity/TempImage.php | 12++++++------
Asrc/PartKeepr/ImageBundle/Resources/config/services.xml | 13+++++++++++++
Asrc/PartKeepr/ImageBundle/Services/ImageService.php | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/PartKeepr/ManufacturerBundle/Controller/ManufacturerIcLogoController.php | 11-----------
Msrc/PartKeepr/ManufacturerBundle/Entity/Manufacturer.php | 15+++++++++------
Msrc/PartKeepr/ManufacturerBundle/Entity/ManufacturerICLogo.php | 3+--
Asrc/PartKeepr/UploadedFileBundle/Annotation/UploadedFileCollection.php | 14++++++++++++++
Asrc/PartKeepr/UploadedFileBundle/DependencyInjection/PartKeeprUploadedFileExtension.php | 20++++++++++++++++++++
Asrc/PartKeepr/UploadedFileBundle/Entity/UploadedFile.php | 217+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/PartKeepr/UploadedFileBundle/EventListener/TemporaryFileEventListener.php | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/PartKeepr/UploadedFileBundle/PartKeeprUploadedFileBundle.php | 10++++++++++
Asrc/PartKeepr/UploadedFileBundle/Resources/config/services.xml | 12++++++++++++
Asrc/PartKeepr/UploadedFileBundle/Services/UploadedFileService.php | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/backend/PartKeepr/CronLogger/CronLogger.php | 2+-
Msrc/backend/PartKeepr/Image/CachedImage.php | 32--------------------------------
Msrc/backend/PartKeepr/Part/PartAttachment.php | 2+-
Msrc/backend/PartKeepr/Project/ProjectAttachment.php | 2+-
Msrc/backend/PartKeepr/SystemNotice/SystemNotice.php | 2+-
Msrc/backend/PartKeepr/UploadedFile/TempUploadedFile.php | 1+
Dsrc/backend/PartKeepr/UploadedFile/UploadedFile.php | 311-------------------------------------------------------------------------------
Msrc/backend/PartKeepr/Util/BaseEntity.php | 3++-
31 files changed, 839 insertions(+), 498 deletions(-)

diff --git a/app/AppKernel.php b/app/AppKernel.php @@ -72,6 +72,7 @@ class AppKernel extends Kernel } // PartKeepr bundles + $bundles[] = new PartKeepr\UploadedFileBundle\PartKeeprUploadedFileBundle(); $bundles[] = new PartKeepr\FrontendBundle\PartKeeprFrontendBundle(); $bundles[] = new PartKeepr\SiPrefixBundle\PartKeeprSiPrefixBundle(); $bundles[] = new PartKeepr\AuthBundle\PartKeeprAuthBundle(); diff --git a/app/config/config.yml b/app/config/config.yml @@ -103,6 +103,13 @@ nelmio_api_doc: json: "application/json" services: + my_event_listener: + class: "PartKeepr\\UploadedFileBundle\\EventListener\\TemporaryFileEventListener" + arguments: + - "@partkeepr_uploadedfile_service" + - "@annotation_reader" + - "@property_accessor" + tags: [ { name: "kernel.event_listener", event: "api.pre_update", method: "onPreUpdate" } ] resource.order_filter: parent: "api.doctrine.orm.order_filter" arguments: [ ~ ] # This line can also be omitted @@ -140,7 +147,7 @@ services: arguments: - "@resource.manufacturer_ic_logo" # Resource - [ "GET" ] # Methods - - "/manufacturter_i_c_logo/{id}/getImage" # Path + - "/manufacturer_i_c_logo/{id}/getImage" # Path - "PartKeeprManufacturerBundle:ManufacturerIcLogo:getImage" # Controller - "ManufacturerIcLogoGetImage" # Route name - # Context (will be present in Hydra documentation) diff --git a/app/config/partkeepr.yml b/app/config/partkeepr.yml @@ -1,5 +1,5 @@ partkeepr: image_cache_directory: %kernel.cache_dir%/imagecache/ - images: + directories: iclogo: %kernel.root_dir%/../data/images/iclogo/ temp: %kernel.root_dir%/../data/temp/ \ No newline at end of file diff --git a/composer.json b/composer.json @@ -47,7 +47,9 @@ "dunglas/api-bundle": "1.0.*@dev", "symfony/serializer": "2.7.1", "brainbits/fugue-icons-bundle": "^3.5", - "imagine/imagine": "^0.6.2" + "imagine/imagine": "^0.6.2", + "ramsey/uuid": "dev-master", + "moontoast/math": "^1.1" }, "autoload": { "psr-0": { "": "src/", diff --git a/composer.lock b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "901fa87432596dc49f7c9f3b41657a9b", + "hash": "9258045adbaa71263ad5f69963c207a0", "packages": [ { "name": "brainbits/fugue-icons-bundle", @@ -1079,12 +1079,12 @@ "source": { "type": "git", "url": "https://github.com/dunglas/DunglasApiBundle.git", - "reference": "e5d4db276c15da522e0e4f71a472af282936509d" + "reference": "bd5e6148af304de6c7ad17decb74cb41ce7b5680" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dunglas/DunglasApiBundle/zipball/e5d4db276c15da522e0e4f71a472af282936509d", - "reference": "e5d4db276c15da522e0e4f71a472af282936509d", + "url": "https://api.github.com/repos/dunglas/DunglasApiBundle/zipball/bd5e6148af304de6c7ad17decb74cb41ce7b5680", + "reference": "bd5e6148af304de6c7ad17decb74cb41ce7b5680", "shasum": "" }, "require": { @@ -1145,7 +1145,7 @@ "json", "rest" ], - "time": "2015-07-01 08:16:18" + "time": "2015-07-03 18:34:57" }, { "name": "dunglas/php-property-info", @@ -1815,6 +1815,42 @@ "time": "2015-06-19 13:29:54" }, { + "name": "moontoast/math", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/moontoast-math.git", + "reference": "fce28a9d1e73e73376cb44e5e581675d15fbe2f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/moontoast-math/zipball/fce28a9d1e73e73376cb44e5e581675d15fbe2f3", + "reference": "fce28a9d1e73e73376cb44e5e581675d15fbe2f3", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "psr-0": { + "Moontoast\\Math": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "A mathematics library, providing functionality for large numbers", + "homepage": "https://github.com/moontoast/math", + "keywords": [ + "bcmath", + "math" + ], + "time": "2013-01-19 17:42:34" + }, + { "name": "nelmio/api-doc-bundle", "version": "2.9.0", "target-dir": "Nelmio/ApiDocBundle", @@ -2171,6 +2207,79 @@ "time": "2012-12-21 11:40:51" }, { + "name": "ramsey/uuid", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "94ea59389111ceadf5f9ed2d1c658b0e35f4525d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/94ea59389111ceadf5f9ed2d1c658b0e35f4525d", + "reference": "94ea59389111ceadf5f9ed2d1c658b0e35f4525d", + "shasum": "" + }, + "require": { + "php": ">=5.4" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "ircmaxell/random-lib": "^1.1", + "jakub-onderka/php-parallel-lint": "^0.9.0", + "moontoast/math": "^1.1", + "phpunit/phpunit": "^4.7", + "satooshi/php-coveralls": "^0.6.1", + "squizlabs/php_codesniffer": "^2.3" + }, + "suggest": { + "ircmaxell/random-lib": "Provides RandomLib to use with the RandomLibAdapter", + "moontoast/math": "Support for converting UUID to 128-bit integer (in string form).", + "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid", + "ramsey/uuid-doctrine": "Allow the use of a UUID as Doctrine field type." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marijn Huizendveld", + "email": "marijn.huizendveld@gmail.com" + }, + { + "name": "Thibaud Fabre", + "email": "thibaud@aztech.io" + }, + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "http://benramsey.com" + } + ], + "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).", + "homepage": "https://github.com/ramsey/uuid", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "time": "2015-06-22 01:20:55" + }, + { "name": "sensio/distribution-bundle", "version": "v2.3.22", "target-dir": "Sensio/Bundle/DistributionBundle", @@ -3156,7 +3265,8 @@ "cbsi/doctrine2-nestedset": 20, "partkeepr/extjs6": 20, "symfony/doctrine-bridge": 20, - "dunglas/api-bundle": 20 + "dunglas/api-bundle": 20, + "ramsey/uuid": 20 }, "prefer-stable": false, "prefer-lowest": false, diff --git a/src/PartKeepr/ImageBundle/Controller/ImageController.php b/src/PartKeepr/ImageBundle/Controller/ImageController.php @@ -65,6 +65,7 @@ abstract class ImageController extends ResourceController /** * Returns the path to the image cache directory. + * * @return string */ public function getImageCacheDirectory() @@ -88,9 +89,10 @@ abstract class ImageController extends ResourceController /** * Scales the image to fit within the given size. * - * @param int $width The width - * @param int $height The height - * @param boolean $padding If true, pad the output image to the given size (transparent background). + * @param PartKeeprImage $image The image to scale + * @param int $width The width + * @param int $height The height + * @param boolean $padding If true, pad the output image to the given size (transparent background). * * @return string The path to the scaled file */ @@ -123,28 +125,31 @@ abstract class ImageController extends ResourceController /** * Returns the full path for the image + * * @param PartKeeprImage $image * * @return string */ public function getFilename(PartKeeprImage $image) { - return realpath($this->getImageStorageDirectory())."/".$image->getPlainFilename().".".$image->getExtension(); + $storageDirectory = $this->get("partkeepr_uploadedfile_service")->getStorageDirectory($image); + + return $storageDirectory."/".$image->getFilename().".".$image->getExtension(); } /** * Returns the path to an image which has been cached in a particular width, height and mode. * - * @param PartKeeprImage $image The image - * @param integer $width The width + * @param PartKeeprImage $image The image + * @param integer $width The width * @param integer $height The height - * @param string $mode The mode + * @param string $mode The mode * * @return string */ public function getImageCacheFilename(PartKeeprImage $image, $width, $height, $mode) { - $outputFile = realpath($this->getImageCacheDirectory()); + $outputFile = $this->getImageCacheDirectory(); $outputFile .= "/".sha1($image->getFilename()); $outputFile .= $width."x".$height."_".$mode.".png"; @@ -156,11 +161,5 @@ abstract class ImageController extends ResourceController * * @return string */ - abstract public function getEntityClass (); - - /** - * Returns the path to the images. - * @return string - */ - abstract public function getImageStorageDirectory (); + abstract public function getEntityClass(); } \ No newline at end of file diff --git a/src/PartKeepr/ImageBundle/Controller/TemporaryImageController.php b/src/PartKeepr/ImageBundle/Controller/TemporaryImageController.php @@ -1,20 +1,17 @@ <?php namespace PartKeepr\ImageBundle\Controller; -use Dunglas\ApiBundle\Controller\ResourceController; use FOS\RestBundle\Controller\Annotations\RequestParam; use FOS\RestBundle\Controller\Annotations\View; -use FOS\RestBundle\Controller\FOSRestController; -use Nelmio\ApiDocBundle\Annotation\ApiDoc; use PartKeepr\ImageBundle\Entity\TempImage; use PartKeepr\ImageBundle\Response\TemporaryImageUploadResponse; +use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; -class TemporaryImageController extends ImageController { +class TemporaryImageController extends ImageController +{ /** * Handles a temporary image upload @@ -22,19 +19,26 @@ class TemporaryImageController extends ImageController { * @RequestParam(name="url",description="An URL where the image is located") * ApiDoc(section="image",output="PartKeepr\ImageBundle\Response\TemporaryImageUploadResponse") * @View() + * + * @param Request $request The request to process + * + * @return JsonResponse The JSON response from the temporary image upload + * @throws \Exception An exception if neither the userfile form parameter or an URL was given */ - public function uploadAction (Request $request) { + public function uploadAction(Request $request) + { $image = new TempImage(); + $imageService = $this->get("partkeepr_image_service"); if ($request->files->has('userfile') && $request->files->get('userfile') != null) { $file = $request->files->get('userfile'); /** * @var $file UploadedFile */ - $image->replace($file->getPathname()); + $imageService->replace($image, new File($file->getPathname())); $image->setOriginalFilename($file->getClientOriginalName()); } elseif ($request->request->has("url")) { - $image->replaceFromURL($request->request->get("url")); + $imageService->replaceFromURL($image, $request->request->get("url")); } else { throw new \Exception("Error: No valid file given"); } @@ -55,14 +59,6 @@ class TemporaryImageController extends ImageController { /** * @inheritdoc */ - public function getImageStorageDirectory() - { - return $this->container->getParameter("partkeepr.images.temp"); - } - - /** - * @inheritdoc - */ public function getEntityClass() { return "PartKeepr\\ImageBundle\\Entity\\TempImage"; diff --git a/src/PartKeepr/ImageBundle/DependencyInjection/Configuration.php b/src/PartKeepr/ImageBundle/DependencyInjection/Configuration.php @@ -23,17 +23,10 @@ class Configuration implements ConfigurationInterface $rootNode-> children() ->scalarNode('image_cache_directory')->cannotBeEmpty()->isRequired()->info('The image cache directory')->end() - ->arrayNode('images') - ->addDefaultsIfNotSet() - ->children() - ->scalarNode('iclogo')->defaultValue('%kernel.root_dir%/../data/images/iclogo/')->cannotBeEmpty()->info('The directory which contains the uploaded ic logos')->end() - ->scalarNode('temp')->defaultValue('%kernel.root_dir%/../data/temp/')->cannotBeEmpty()->info('The directory which contains the temporary images')->end() - ->end() + ->arrayNode('directories') + ->prototype('scalar')->end() ->end() ->end(); - // Here you should define the parameters that are allowed to - // configure your bundle. See the documentation linked above for - // more information on that topic. return $treeBuilder; } diff --git a/src/PartKeepr/ImageBundle/DependencyInjection/PartKeeprExtension.php b/src/PartKeepr/ImageBundle/DependencyInjection/PartKeeprExtension.php @@ -3,6 +3,8 @@ namespace PartKeepr\ImageBundle\DependencyInjection; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\Loader; class PartKeeprExtension extends Extension { @@ -15,11 +17,18 @@ class PartKeeprExtension extends Extension $config = $this->processConfiguration($configuration, $configs); $container->setParameter('partkeepr.image_cache_directory', $config['image_cache_directory']); - $container->setParameter('partkeepr.images.iclogo', $config['images']['iclogo']); - $container->setParameter('partkeepr.images.temp', $config['images']['temp']); + + foreach ($config["directories"] as $key => $value) { + $container->setParameter("partkeepr.directories.".$key, $value); + } + + $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.xml'); + } - public function getAlias () { + public function getAlias() + { return "partkeepr"; } } \ No newline at end of file diff --git a/src/PartKeepr/ImageBundle/Entity/Image.php b/src/PartKeepr/ImageBundle/Entity/Image.php @@ -2,84 +2,53 @@ namespace PartKeepr\ImageBundle\Entity; use Doctrine\ORM\Mapping as ORM; -use PartKeepr\Image\CachedImage; use PartKeepr\Image\Exceptions\InvalidImageTypeException; -use PartKeepr\Image\ImageRenderer; -use PartKeepr\Image\RenderableImage; -use PartKeepr\ImageBundle\Entity\TempImage; -use PartKeepr\UploadedFile\UploadedFile; +use PartKeepr\UploadedFileBundle\Entity\UploadedFile; /** * This is only a storage class; actual image rendering is done by the ImageRenderer. - * + * * @ORM\MappedSuperclass */ -abstract class Image extends UploadedFile { - const IMAGE_ICLOGO = "iclogo"; - const IMAGE_TEMP = "temp"; - const IMAGE_PART = "part"; - const IMAGE_STORAGELOCATION = "storagelocation"; - const IMAGE_FOOTPRINT = "footprint"; +abstract class Image extends UploadedFile +{ + const IMAGE_ICLOGO = "iclogo"; + const IMAGE_TEMP = "temp"; + const IMAGE_PART = "part"; + const IMAGE_STORAGELOCATION = "storagelocation"; + const IMAGE_FOOTPRINT = "footprint"; - /** - * Constructs a new image object. - * - * @param string $type The type for the image, one of the IMAGE_ constants. - */ - public function __construct ($type = NULL) { - parent::__construct(); - $this->setType($type); - } + /** + * Constructs a new image object. + * + * @param string $type The type for the image, one of the IMAGE_ constants. + */ + public function __construct($type = null) + { + $this->setType($type); + parent::__construct(); + } - /** - * Sets the type of the image. Once the type is set, - * it may not be changed later. - * - * @param string $type The type for the image, one of the IMAGE_ constants. - * @throws InvalidImageTypeException - */ - protected function setType ($type) { - switch ($type) { - case Image::IMAGE_ICLOGO: - case Image::IMAGE_TEMP: - case Image::IMAGE_PART: - case Image::IMAGE_FOOTPRINT: - case Image::IMAGE_STORAGELOCATION: - parent::setType($type); - break; - default: - throw new InvalidImageTypeException($type); - } - } - - /** - * Replaces the current image with a new image. - * - * Automatically converts from one format to PNG, - * which is the default when dealing with images - * on the platform. - * - * @param string $path The path to the original image - */ - public function replace ($path) { - parent::replace($path); - - CachedImage::invalidate($this); - } - - /** - * Replaces the file with a given temporary file. - * @param string $id The temporary id (prefixed with TMP:) - */ - public function replaceFromTemporaryFile ($id) { - if (substr($id, 0, 4) === "TMP:") { - $tmpFileId = str_replace("TMP:", "", $id); - $tmpFile = TempImage::loadById($tmpFileId); - - $this->replace($tmpFile->getFilename()); - $this->setOriginalFilename($tmpFile->getOriginalFilename()); - - } - } - + /** + * Sets the type of the image. Once the type is set, + * it may not be changed later. + * + * @param string $type The type for the image, one of the IMAGE_ constants. + * + * @throws InvalidImageTypeException + */ + protected function setType($type) + { + switch ($type) { + case Image::IMAGE_ICLOGO: + case Image::IMAGE_TEMP: + case Image::IMAGE_PART: + case Image::IMAGE_FOOTPRINT: + case Image::IMAGE_STORAGELOCATION: + parent::setType($type); + break; + default: + throw new InvalidImageTypeException($type); + } + } } \ No newline at end of file diff --git a/src/PartKeepr/ImageBundle/Entity/TempImage.php b/src/PartKeepr/ImageBundle/Entity/TempImage.php @@ -2,8 +2,6 @@ namespace PartKeepr\ImageBundle\Entity; use Doctrine\ORM\Mapping as ORM; -use PartKeepr\ImageBundle\Entity\Image; -use PartKeepr\UploadedFile\UploadedFile; /** * Represents a temporary image. Temporary images are used when @@ -11,8 +9,10 @@ use PartKeepr\UploadedFile\UploadedFile; * * @ORM\Entity */ -class TempImage extends Image { - public function __construct () { - parent::__construct(Image::IMAGE_TEMP); - } +class TempImage extends Image +{ + public function __construct() + { + parent::__construct(Image::IMAGE_TEMP); + } } \ No newline at end of file diff --git a/src/PartKeepr/ImageBundle/Resources/config/services.xml b/src/PartKeepr/ImageBundle/Resources/config/services.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" ?> + +<container xmlns="http://symfony.com/schema/dic/services" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> + + <services> + <service id="partkeepr_image_service" class="PartKeepr\ImageBundle\Services\ImageService"> + <argument type="service" id="service_container"/> + </service> + + </services> +</container> diff --git a/src/PartKeepr/ImageBundle/Services/ImageService.php b/src/PartKeepr/ImageBundle/Services/ImageService.php @@ -0,0 +1,67 @@ +<?php + + +namespace PartKeepr\ImageBundle\Services; + +use Doctrine\ORM\EntityManager; +use PartKeepr\Image\CachedImage; +use PartKeepr\UploadedFileBundle\Entity\UploadedFile; +use PartKeepr\UploadedFileBundle\Services\UploadedFileService; +use Symfony\Component\HttpFoundation\File\File; + +class ImageService extends UploadedFileService +{ + /** + * Replaces the current image with a new image. + * + * @param UploadedFile $file The target file + * @param File $filesystemFile The source file + */ + public function replace(UploadedFile $file, File $filesystemFile) + { + parent::replaceFromFilesystem($file, $filesystemFile); + $this->invalidate($file); + } + + /** + * Replaces the file from an URL. Does some tricks to avoid 403 forbidden on some sites. + * + * @param UploadedFile $file The target file + * @param string $url The URL to replace from + */ + public function replaceFromURL(UploadedFile $file, $url) + { + parent::replaceFromURL($file, $url); + $this->invalidate($file); + } + + /** + * Invalidates any cached files + * + * @param UploadedFile $file The file to invalidate + */ + public function invalidate(UploadedFile $file) + { + /** + * @var $entityManager EntityManager + */ + $entityManager = $this->container->get("doctrine")->getManager(); + $queryBuilder = $entityManager->createQueryBuilder(); + $queryBuilder->select(array("c")) + ->from('PartKeepr\Image\CachedImage', 'c') + ->where("c.originalId = :id") + ->andWhere("c.originalType = :type") + ->setParameter("id", $file->getId()) + ->setParameter("type", $file->getType()); + + $query = $queryBuilder->getQuery(); + + foreach ($query->getResult() as $file) { + /** + * @var $file CachedImage + */ + unlink($file->getCacheFile()); + $entityManager->remove($file); + } + } +}+ \ No newline at end of file diff --git a/src/PartKeepr/ManufacturerBundle/Controller/ManufacturerIcLogoController.php b/src/PartKeepr/ManufacturerBundle/Controller/ManufacturerIcLogoController.php @@ -1,21 +1,10 @@ <?php namespace PartKeepr\ManufacturerBundle\Controller; - use PartKeepr\ImageBundle\Controller\ImageController; - class ManufacturerIcLogoController extends ImageController { - - /** - * @inheritdoc - */ - public function getImageStorageDirectory() - { - return $this->container->getParameter("partkeepr.images.iclogo"); - } - /** * @inheritdoc */ diff --git a/src/PartKeepr/ManufacturerBundle/Entity/Manufacturer.php b/src/PartKeepr/ManufacturerBundle/Entity/Manufacturer.php @@ -4,6 +4,7 @@ namespace PartKeepr\ManufacturerBundle\Entity; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; use PartKeepr\DoctrineReflectionBundle\Annotation\TargetService; +use PartKeepr\UploadedFileBundle\Annotation\UploadedFileCollection; use PartKeepr\Util\BaseEntity; use Symfony\Component\Serializer\Annotation\Groups; @@ -80,7 +81,9 @@ class Manufacturer extends BaseEntity /** * All ic logos of this manufacturer - * @ORM\OneToMany(targetEntity="PartKeepr\ManufacturerBundle\Entity\ManufacturerICLogo",mappedBy="manufacturer",cascade={"persist", "remove"}) + * @ORM\OneToMany(targetEntity="PartKeepr\ManufacturerBundle\Entity\ManufacturerICLogo",mappedBy="manufacturer",cascade={"persist","remove"}) + * + * @UploadedFileCollection() * @Groups({"default"}) */ private $icLogos; @@ -220,7 +223,7 @@ class Manufacturer extends BaseEntity */ public function setURL($url) { - //$this->url = $url; + $this->url = $url; } /** @@ -238,7 +241,7 @@ class Manufacturer extends BaseEntity * * @return ArrayCollection The array with all ic logos */ - public function getICLogos() + public function getIcLogos() { return $this->icLogos; } @@ -246,10 +249,10 @@ class Manufacturer extends BaseEntity /** * Sets the IC Logos * - * @param $array + * @param array $icLogos The icLogos to set */ - public function setICLogos($array) + public function setIcLogos($icLogos) { - $this->icLogos = $array; + $this->icLogos = $icLogos; } } \ No newline at end of file diff --git a/src/PartKeepr/ManufacturerBundle/Entity/ManufacturerICLogo.php b/src/PartKeepr/ManufacturerBundle/Entity/ManufacturerICLogo.php @@ -3,8 +3,6 @@ namespace PartKeepr\ManufacturerBundle\Entity; use Doctrine\ORM\Mapping as ORM; use PartKeepr\ImageBundle\Entity\Image; -use PartKeepr\Util\Deserializable; -use PartKeepr\Util\Serializable; /** * Holds a manufacturer IC logo @@ -16,6 +14,7 @@ class ManufacturerICLogo extends Image /** * The manufacturer object * @ORM\ManyToOne(targetEntity="PartKeepr\ManufacturerBundle\Entity\Manufacturer", inversedBy="icLogos") + * @ORM\JoinColumn(name="manufacturer_id",referencedColumnName="id") * * @var Manufacturer */ diff --git a/src/PartKeepr/UploadedFileBundle/Annotation/UploadedFileCollection.php b/src/PartKeepr/UploadedFileBundle/Annotation/UploadedFileCollection.php @@ -0,0 +1,14 @@ +<?php +namespace PartKeepr\UploadedFileBundle\Annotation; + +use Doctrine\ORM\Mapping\Annotation; + +/** + * @Annotation + * @Target("PROPERTY") + * + * Use this annotation on any property to replace any temporary images with their concrete implementation. + */ +final class UploadedFileCollection implements Annotation +{ +} diff --git a/src/PartKeepr/UploadedFileBundle/DependencyInjection/PartKeeprUploadedFileExtension.php b/src/PartKeepr/UploadedFileBundle/DependencyInjection/PartKeeprUploadedFileExtension.php @@ -0,0 +1,19 @@ +<?php +namespace PartKeepr\UploadedFileBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\Loader; + +class PartKeeprUploadedFileExtension extends Extension +{ + /** + * {@inheritDoc} + */ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.xml'); + } +}+ \ No newline at end of file diff --git a/src/PartKeepr/UploadedFileBundle/Entity/UploadedFile.php b/src/PartKeepr/UploadedFileBundle/Entity/UploadedFile.php @@ -0,0 +1,217 @@ +<?php +namespace PartKeepr\UploadedFileBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; +use PartKeepr\Util\BaseEntity; +use Ramsey\Uuid\Uuid; +use Symfony\Component\Serializer\Annotation\Groups; + +/** + * @ORM\MappedSuperclass + */ +abstract class UploadedFile extends BaseEntity +{ + /** + * Specifies the type of the file. + * + * @ORM\Column(type="string") + * @Groups({"default"}) + * + * @var string + **/ + private $type; + + /** + * The unique filename of the file. Note that the filename does not contain any extension or any path information. + * + * @ORM\Column(type="string") + * @Groups({"default"}) + * + * @var string + */ + private $filename; + + /** + * The original name of the file. Includes the extension of the file, but no path information. + * + * @ORM\Column(type="string",nullable=true,name="originalname") + * @Groups({"default"}) + * + * @var string + */ + private $originalFilename; + + /** + * The MimeType for the file + * + * @ORM\Column(type="string") + * @Groups({"default"}) + * + * @var string + */ + private $mimetype; + + /** + * The size of the uploaded file + * @ORM\Column(type="integer") + * @Groups({"default"}) + * + * @var integer + */ + private $size; + + /** + * Holds the extension of the given file + * @ORM\Column(type="string") + * @Groups({"default"}) + * + * @var string + */ + private $extension; + + public function __construct() + { + $this->filename = Uuid::uuid1()->toString(); + } + + /** + * Sets the type of the file. Once the type is set, + * it may not be changed later. + * + * @param string $type The type of the file + */ + protected function setType($type) + { + $this->type = $type; + } + + /** + * Returns the original filename + * + * @return string The original filename + */ + public function getOriginalFilename() + { + return $this->originalFilename; + } + + /** + * Sets the original filename + * + * @param string $filename The original filename + */ + public function setOriginalFilename($filename) + { + $this->originalFilename = $filename; + } + + /** + * Returns the size of this file + * + * @return integer The size in bytes + */ + public function getSize() + { + return $this->size; + } + + /** + * Sets the size of the file + * + * @param integer $size The size in bytes + */ + public function setSize($size) + { + $this->size = intval($size); + } + + /** + * Returns the type of the file + * + * @param none + * + * @return string The type of the file + */ + public function getType() + { + return $this->type; + } + + /** + * Returns the plain filename without path and suffix. + * + * @return string The plain filename without path and suffix + */ + public function getFilename() + { + return $this->filename; + } + + /** + * Returns the mime type for this file + * + * @return string The mimetype for this file, e.g. text/plain + */ + public function getMimeType() + { + return $this->mimetype; + } + + /** + * Sets the mimetype for this file + * + * @param string $mimeType The mimetype + */ + public function setMimeType($mimeType) + { + $this->mimetype = $mimeType; + } + + /** + * Returns the extension for the file + * + * @return string The extension + */ + public function getExtension() + { + if ($this->extension == "") { + /** @noinspection PhpDeprecationInspection */ + return $this->getLegacyExtension(); + } + + return $this->extension; + } + + /** + * Sets the extension for this file + * + * @param string $extension The extension + */ + public function setExtension($extension) + { + $this->extension = $extension; + } + + /** + * Returns the extension for the given mime type. + * + * This function simply extracts that information from the mime type; + * special cases are not handled. e.g. if you have image/foobar, it would + * return "foobar" as extension. + * + * @todo Implement conversion from mimetype to extension in the setup routine + * + * @return string The extension + * @deprecated + */ + public function getLegacyExtension() + { + $data = explode("/", $this->getMimeType()); + + if (array_key_exists(1, $data)) { + return $data[1]; + } else { + return "undefined"; + } + } +} diff --git a/src/PartKeepr/UploadedFileBundle/EventListener/TemporaryFileEventListener.php b/src/PartKeepr/UploadedFileBundle/EventListener/TemporaryFileEventListener.php @@ -0,0 +1,102 @@ +<?php +namespace PartKeepr\UploadedFileBundle\EventListener; + +use Doctrine\Common\Annotations\Reader; +use Dunglas\ApiBundle\Event\DataEvent; +use PartKeepr\ImageBundle\Entity\TempImage; +use PartKeepr\UploadedFileBundle\Entity\UploadedFile; +use PartKeepr\UploadedFileBundle\Services\UploadedFileService; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +class TemporaryFileEventListener +{ + /** + * @var UploadedFileService + */ + private $uploadedFileService; + + /** + * @var Reader + */ + private $reader; + + /** + * @var PropertyAccessorInterface + */ + private $propertyAccessor; + + public function __construct( + UploadedFileService $uploadedFileService, + Reader $reader, + PropertyAccessorInterface $propertyAccessor + ) { + $this->uploadedFileService = $uploadedFileService; + $this->reader = $reader; + $this->propertyAccessor = $propertyAccessor; + } + + /** + * Replaces any temporary images with actual instances of the configured UploadedFile collection. + * + * Automatically extracts the proper setters and getters from the metadata and instantiates the correct + * UploadedFile child class. + * + * @param DataEvent $event The event + */ + public function onPreUpdate(DataEvent $event) + { + $data = $event->getData(); + + $classReflection = new \ReflectionClass($data); + + foreach ($classReflection->getProperties() as $property) { + + $propertyAnnotation = $this->reader->getPropertyAnnotation( + $property, + 'PartKeepr\UploadedFileBundle\Annotation\UploadedFileCollection' + ); + + $manyToOneAnnotation = $this->reader->getPropertyAnnotation( + $property, + 'Doctrine\ORM\Mapping\OneToMany' + ); + + if ($propertyAnnotation !== null) { + $collection = $this->propertyAccessor->getValue($data, $property->getName()); + + foreach ($collection as $key => $item) { + if ($item instanceof TempImage) { + $targetEntity = $manyToOneAnnotation->targetEntity; + + /** + * @var $newImage UploadedFile + */ + $newImage = new $targetEntity(); + + $this->uploadedFileService->replaceFromUploadedFile($newImage, $item); + $newImage->setOriginalFilename($item->getOriginalFilename()); + + // Find the setter for the association + $inverseSideReflection = new \ReflectionClass($newImage); + + foreach ($inverseSideReflection->getProperties() as $inverseSideProperty) { + + $oneToManyAssociation = $this->reader->getPropertyAnnotation( + $inverseSideProperty, + 'Doctrine\ORM\Mapping\ManyToOne' + ); + + if ($oneToManyAssociation !== null && $oneToManyAssociation->targetEntity == $classReflection->getName()) { + $this->propertyAccessor->setValue($newImage, $inverseSideProperty->getName(), $data); + } + } + + $collection[$key] = $newImage; + } + } + + $this->propertyAccessor->setValue($data, $property->getName(), $collection); + } + } + } +}+ \ No newline at end of file diff --git a/src/PartKeepr/UploadedFileBundle/PartKeeprUploadedFileBundle.php b/src/PartKeepr/UploadedFileBundle/PartKeeprUploadedFileBundle.php @@ -0,0 +1,9 @@ +<?php +namespace PartKeepr\UploadedFileBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class PartKeeprUploadedFileBundle extends Bundle +{ + +}+ \ No newline at end of file diff --git a/src/PartKeepr/UploadedFileBundle/Resources/config/services.xml b/src/PartKeepr/UploadedFileBundle/Resources/config/services.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" ?> + +<container xmlns="http://symfony.com/schema/dic/services" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> + + <services> + <service id="partkeepr_uploadedfile_service" class="PartKeepr\UploadedFileBundle\Services\UploadedFileService"> + <argument type="service" id="service_container"/> + </service> + </services> +</container> diff --git a/src/PartKeepr/UploadedFileBundle/Services/UploadedFileService.php b/src/PartKeepr/UploadedFileBundle/Services/UploadedFileService.php @@ -0,0 +1,147 @@ +<?php + + +namespace PartKeepr\UploadedFileBundle\Services; + +use PartKeepr\UploadedFileBundle\Entity\UploadedFile; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerAware; +use Symfony\Component\HttpFoundation\File\File; + +class UploadedFileService extends ContainerAware +{ + /** + * @var Container + */ + protected $container; + + public function __construct(Container $container) + { + $this->setContainer($container); + } + + /** + * Replaces the current file with a new file. + * + * @param UploadedFile $file The target file + * @param File $filesystemFile The source file + */ + public function replaceFromFilesystem(UploadedFile $file, File $filesystemFile) + { + $oldFile = $this->getFullPath($file); + + $file->setOriginalFilename($filesystemFile->getBasename()); + $file->setExtension($filesystemFile->getExtension()); + $file->setMimeType($filesystemFile->getMimeType()); + $file->setSize($filesystemFile->getSize()); + + copy($filesystemFile->getPathname(), $this->getFullPath($file)); + + if (file_exists($oldFile)) { + unlink($oldFile); + } + } + + /** + * Replaces an existing uploaded file with another uploaded file. + * + * @param UploadedFile $target The target file + * @param UploadedFile $source The source file + */ + public function replaceFromUploadedFile(UploadedFile $target, UploadedFile $source) + { + $this->replaceFromFilesystem($target, new File($this->getFullPath($source))); + $target->setOriginalFilename($source->getOriginalFilename()); + } + + /** + * Returns the full path for a given UploadedFile. + * + * @param UploadedFile $file The file to get the full path for + * + * @return string The path + */ + public function getFullPath(UploadedFile $file) + { + $targetDirectory = $this->getStorageDirectory($file); + + $targetFilename = $file->getFilename().".".$file->getExtension(); + + return $targetDirectory."/".$targetFilename; + } + + /** + * Replaces the file from an URL. Does some tricks to avoid 403 forbidden on some sites, like setting a proper + * browser identification. + * + * @param UploadedFile $file The target file + * @param string $url The URL to replace from + * + * @throws \Exception An exception if the curl operation failed + * + * @todo Parse the filename from the return headers + */ + public function replaceFromURL(UploadedFile $file, $url) + { + /* Some sites don't like automated requests. But the internet is meant to be open for anybody, + * even for scripts. So we are evil and fake the headers. + * + * Credit goes to Ryan Rampersad from whom I copied most code. + * http://blog.ryanrampersad.com/2008/11/07/get-remote-html-with-curl-and-php/ + */ + $curl = curl_init(); + + $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,"; + $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; + $header[] = "Cache-Control: max-age=0"; + $header[] = "Connection: keep-alive"; + $header[] = "Keep-Alive: 300"; + $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7"; + $header[] = "Accept-Language: en-us,en;q=0.5"; + $header[] = "Pragma: "; + + $browser = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3"; + + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_USERAGENT, $browser); + curl_setopt($curl, CURLOPT_HTTPHEADER, $header); + curl_setopt($curl, CURLOPT_AUTOREFERER, true); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_TIMEOUT, 30); + curl_setopt($curl, CURLOPT_MAXREDIRS, 7); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + + $data = curl_exec($curl); + + if ($data === false) { + $curlError = curl_error($curl); + // Strip ANY tags from the error message. curl tends to spit out <url> is not valid, which then + // confuses the error message parser on the client side. + $curlError = str_replace(array(">", "<"), "", $curlError); + throw new \Exception("replaceFromURL error: ".$curlError); + } + + curl_close($curl); + + $tempName = tempnam("/tmp", "PARTKEEPR"); + + file_put_contents($tempName, $data); + + $this->replaceFromFilesystem($file, new File($tempName)); + $file->setOriginalFilename(basename($url)); + + unlink($tempName); + } + + /** + * Returns the storage directory for a given UploadedFile. + * + * @param UploadedFile $file The file to get the storage directory for + * + * @return string The storage directory for the type of the given UploadedFile + */ + public function getStorageDirectory(UploadedFile $file) + { + return $this->container->getParameter("partkeepr.directories.".$file->getType()); + } +}+ \ No newline at end of file diff --git a/src/backend/PartKeepr/CronLogger/CronLogger.php b/src/backend/PartKeepr/CronLogger/CronLogger.php @@ -1,7 +1,7 @@ <?php namespace PartKeepr\CronLogger; -use PartKeepr\UploadedFile\UploadedFile, +use PartKeepr\UploadedFileBundle\Entity\UploadedFile, PartKeepr\Util\BaseEntity, PartKeepr\Util\Serializable, PartKeepr\Util\Deserializable, diff --git a/src/backend/PartKeepr/Image/CachedImage.php b/src/backend/PartKeepr/Image/CachedImage.php @@ -66,36 +66,4 @@ class CachedImage { public function getCacheFile () { return $this->cacheFile; } - - /** - * Removes all caches for a specific image. This is usually called - * when a new version of an image is uploaded. - * - * Automatically calls "flush" on the entity manager. - * - * @param Image $image The image to invalidate - */ - public static function invalidate (Image $image) { - $qb = PartKeepr::getEM()->createQueryBuilder(); - $qb->select(array("c")) - ->from('PartKeepr\Image\CachedImage', 'c') - ->where("c.originalId = :id") - ->andWhere("c.originalType = :type") - ->setParameter("id", $image->getId()) - ->setParameter("type", $image->getType()); - - $query = $qb->getQuery(); - - $bImagesRemoved = false; - - foreach ($query->getResult() as $file) { - unlink($file->getCacheFile()); - PartKeepr::getEM()->remove($file); - $bImagesRemoved = true; - } - - if ($bImagesRemoved) { - PartKeepr::getEM()->flush(); - } - } } \ No newline at end of file diff --git a/src/backend/PartKeepr/Part/PartAttachment.php b/src/backend/PartKeepr/Part/PartAttachment.php @@ -3,7 +3,7 @@ namespace PartKeepr\Part; use PartKeepr\Util\Deserializable, PartKeepr\Util\Serializable, - PartKeepr\UploadedFile\UploadedFile, + PartKeepr\UploadedFileBundle\Entity\UploadedFile, Doctrine\ORM\Mapping as ORM; /** diff --git a/src/backend/PartKeepr/Project/ProjectAttachment.php b/src/backend/PartKeepr/Project/ProjectAttachment.php @@ -3,7 +3,7 @@ namespace PartKeepr\Project; use PartKeepr\Util\Deserializable, PartKeepr\Util\Serializable, - PartKeepr\UploadedFile\UploadedFile, + PartKeepr\UploadedFileBundle\Entity\UploadedFile, Doctrine\ORM\Mapping as ORM; /** diff --git a/src/backend/PartKeepr/SystemNotice/SystemNotice.php b/src/backend/PartKeepr/SystemNotice/SystemNotice.php @@ -1,7 +1,7 @@ <?php namespace PartKeepr\SystemNotice; -use PartKeepr\UploadedFile\UploadedFile, +use PartKeepr\UploadedFileBundle\Entity\UploadedFile, PartKeepr\Util\BaseEntity, PartKeepr\Util\Serializable, PartKeepr\Util\Deserializable, diff --git a/src/backend/PartKeepr/UploadedFile/TempUploadedFile.php b/src/backend/PartKeepr/UploadedFile/TempUploadedFile.php @@ -2,6 +2,7 @@ namespace PartKeepr\UploadedFile; use Doctrine\ORM\Mapping as ORM; +use PartKeepr\UploadedFileBundle\Entity\UploadedFile; /** * Represents a temporary file. Temporary files are used when diff --git a/src/backend/PartKeepr/UploadedFile/UploadedFile.php b/src/backend/PartKeepr/UploadedFile/UploadedFile.php @@ -1,311 +0,0 @@ -<?php -namespace PartKeepr\UploadedFile; - -use PartKeepr\Util\SerializableException, - PartKeepr\Util\BaseEntity, - PartKeepr\Util\Serializable, - PartKeepr\PartKeepr, - PartKeepr\UploadedFile\TempUploadedFile, - PartKeepr\Util\Configuration, - Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Serializer\Annotation\Groups; - -/** - * @ORM\MappedSuperclass - */ -abstract class UploadedFile extends BaseEntity { - /** - * Specifies the type of the file. - * - * @ORM\Column(type="string") - * @Groups({"default"}) - * - * @var string - **/ - private $type; - - /** - * The unique filename of the file - * - * @ORM\Column(type="string") - * @Groups({"default"}) - * - * @var string - */ - private $filename; - - /** - * The original name of the file - * - * @ORM\Column(type="string",nullable=true,name="originalname") - * @Groups({"default"}) - * - * @var string - */ - private $originalFilename; - - /** - * The MimeType for the file - * - * @ORM\Column(type="string") - * @Groups({"default"}) - * - * @var string - */ - private $mimetype; - - /** - * The size of the uploaded file - * @ORM\Column(type="integer") - * @Groups({"default"}) - * - * @var integer - */ - private $size; - - /** - * Constructs a new file object. - * - */ - public function __construct () { - $this->filename = PartKeepr::createGUIDv4(); - } - - /** - * Sets the type of the file. Once the type is set, - * it may not be changed later. - */ - protected function setType ($type) { - $this->type = $type; - } - - /** - * Returns the original filename - * @return string The original filename - */ - public function getOriginalFilename () { - return $this->originalFilename; - } - - /** - * Sets the original filename - * @param string $filename The original filename - */ - public function setOriginalFilename ($filename) { - $this->originalFilename = $filename; - } - - /** - * Replaces the current file with a new file. - * - * @param string $path The path to the original file - */ - public function replace ($path) { - // Parse the file's mimetype - $finfo = new \finfo(FILEINFO_MIME); - $this->mimetype = $finfo->file($path, FILEINFO_MIME_TYPE); - - // Get the file size - $this->size = filesize($path); - - $this->ensureFilePathExists(); - $this->checkPermissions(); - - copy($path, $this->getFilename()); - - $this->setOriginalFilename(basename($path)); - } - - /** - * Replaces the file from an URL. Does some tricks to avoid 403 forbidden on some sites. - * @param string $url - */ - public function replaceFromURL ($url) { - - /* Some sites don't like automated requests. But the internet is meant to be open for anybody, - * even for scripts. So we are evil and fake the headers. - * - * Credit goes to Ryan Rampersad from whom I copied most code. - * http://blog.ryanrampersad.com/2008/11/07/get-remote-html-with-curl-and-php/ - */ - $curl = \curl_init(); - - $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,"; - $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; - $header[] = "Cache-Control: max-age=0"; - $header[] = "Connection: keep-alive"; - $header[] = "Keep-Alive: 300"; - $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7"; - $header[] = "Accept-Language: en-us,en;q=0.5"; - $header[] = "Pragma: "; - - $browser = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3"; - - curl_setopt($curl, CURLOPT_URL, $url); - curl_setopt($curl, CURLOPT_USERAGENT, $browser); - curl_setopt($curl, CURLOPT_HTTPHEADER, $header); - curl_setopt($curl, CURLOPT_AUTOREFERER, true); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_TIMEOUT, 30); - curl_setopt($curl, CURLOPT_MAXREDIRS, 7); - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); - - $data = \curl_exec($curl); - - if ($data === false) { - $curlError = curl_error($curl); - // Strip ANY tags from the error message. curl tends to spit out <url> is not valid, which then - // confuses the error message parser on the client side. - $curlError = str_replace(array(">", "<"), "", $curlError); - throw new \Exception("replaceFromURL error: ".$curlError); - } - - curl_close($curl); - - $tempName = tempnam("/tmp", "PARTKEEPR"); - - file_put_contents($tempName, $data); - - $this->replace($tempName); - - $this->setOriginalFilename(basename($url)); - } - - /** - * Returns the size of this file - * @return integer The size in bytes - */ - public function getSize () { - return $this->size; - } - - /** - * Returns the type of the file - * @param none - * @return string The type of the file - */ - public function getType () { - return $this->type; - } - - /** - * Returns the full filename including path and suffix. - * @return string The full filename - */ - public function getFilename () { - return $this->getFilePath().$this->filename.".".$this->getExtension(); - } - - /** - * Returns the plain filename without path and suffix. - * @return string the plain filename - */ - public function getPlainFilename () { - return $this->filename; - } - - /** - * Returns the mime type for this file - * @return string The mimetype for this file, e.g. text/plain - */ - public function getMimeType () { - return $this->mimetype; - } - - /** - * Returns the extension for the given mime type. - * - * This function simply extracts that information from the mime type; - * special cases are not handled. e.g. if you have image/foobar, it would - * return "foobar" as extension. - * - * @return string The extension - */ - public function getExtension () { - $data = explode("/", $this->getMimeType()); - - if (array_key_exists(1, $data)) { - return $data[1]; - } else { - return "undefined"; - } - } - - /** - * Returns the path to the file. May be overridden by - * subclasses. - * - * @param none - * @return string The path to the file - */ - public function getFilePath () { - return Configuration::getOption( - "partkeepr.files.path", - PartKeepr::getRootDirectory() . "/data/") . $this->getType() . "/"; - } - - /** - * Ensures that the file path exists. This function - * is called every time a file is processed. - * It is maybe a bit overhead, but saves headaches later when - * introducing new types. - * - * @param none - * @return nothing - */ - public function ensureFilePathExists () { - if (!is_dir($this->getFilePath())) { - try { - mkdir($this->getFilePath(), 0777, true); - } catch (\Exception $e) { - throw new \Exception("Unable to create directory ".$this->getFilePath()); - } - } - } - - /** - * Creates a new entity from the given temporary id. - * - * @param string $id The temporary id (prefixed with TMP:) - * @return object a new instance of the file. - * @throws \Exception If the ID does not begin with TMP: - */ - public static function createFromTemporaryFile ($id) { - if (substr($id, 0, 4) === "TMP:") { - // It's a temporary file - $className = get_called_class(); - - $file = new $className(); - $file->replaceFromTemporaryFile($id); - return $file; - } else { - throw new \Exception("Given id $id is not a temporary file"); - } - } - - /** - * Replaces the file with a given temporary file. - * @param string $id The temporary id (prefixed with TMP:) - */ - public function replaceFromTemporaryFile ($id) { - if (substr($id, 0, 4) === "TMP:") { - $tmpFileId = str_replace("TMP:", "", $id); - $tmpFile = TempUploadedFile::loadById($tmpFileId); - - $this->replace($tmpFile->getFilename()); - $this->setOriginalFilename($tmpFile->getOriginalFilename()); - } - } - - /** - * Checks if the path where the file should be stored has sufficient permissions to do so. - * - * @throws SerializableException - */ - public function checkPermissions () { - if (!is_writable($this->getFilePath())) { - throw new SerializableException( - sprintf(PartKeepr::i18n("Unable to write to directory %s"), $this->getFilePath())); - } - } -} diff --git a/src/backend/PartKeepr/Util/BaseEntity.php b/src/backend/PartKeepr/Util/BaseEntity.php @@ -6,7 +6,8 @@ use Doctrine\ORM\Mapping as ORM; /** @ORM\MappedSuperclass */ abstract class BaseEntity { /** - * @ORM\Id @ORM\Column(type="integer") + * @ORM\Id + * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") * @var integer */