From f79d63be1b91334e12b2cd7c53078fa6111a0c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=BCbner?= Date: Thu, 6 Apr 2017 16:27:18 +0200 Subject: [PATCH] WIP --- app/AppKernel.php | 2 + app/Resources/assets/style/style.scss | 1 - .../views/ldraw/model/detail.html.twig | 7 +- app/Resources/views/macros/elements.html.twig | 2 +- app/autoload.php | 2 +- app/config/config.yml | 17 +- app/config/parameters.yml.dist | 5 +- app/config/routing.yml | 2 + app/config/service/loader.yml | 4 +- app/config/service/manager.yml | 4 - app/config/service/service.yml | 17 +- composer.json | 4 +- composer.lock | 331 +++++++++++++++++- .../Command/LoadLDRawLibraryCommand.php | 10 +- src/AppBundle/Controller/MediaController.php | 41 +-- src/AppBundle/Entity/LDraw/Model.php | 23 -- src/AppBundle/Entity/LDraw/Type.php | 91 ----- .../Exception/FileNotFoundException.php | 15 + .../Imagine/Loader/RemoteStreamLoader.php | 22 ++ src/AppBundle/Manager/LDraw/TypeManager.php | 37 -- src/AppBundle/Manager/LDrawManager.php | 15 +- src/AppBundle/Manager/RebrickableManager.php | 18 + .../Repository/LDraw/PartRepository.php | 9 - src/AppBundle/Service/LDViewService.php | 33 +- .../Service/Loader/LDrawLoaderService.php | 132 +++---- src/AppBundle/Service/ZipService.php | 8 + .../LDraw/CategoryControllerTest.php | 25 ++ .../Controller/LDraw/PartControllerTest.php | 25 ++ src/AppBundle/Twig/AppExtension.php | 35 ++ src/AppBundle/Utils/DatParser.php | 150 ++++---- var/SymfonyRequirements.php | 6 +- web/config.php | 2 +- 32 files changed, 679 insertions(+), 416 deletions(-) delete mode 100644 src/AppBundle/Entity/LDraw/Type.php create mode 100644 src/AppBundle/Exception/FileNotFoundException.php create mode 100644 src/AppBundle/Imagine/Loader/RemoteStreamLoader.php delete mode 100644 src/AppBundle/Manager/LDraw/TypeManager.php create mode 100644 src/AppBundle/Manager/RebrickableManager.php delete mode 100644 src/AppBundle/Repository/LDraw/PartRepository.php create mode 100644 src/AppBundle/Service/ZipService.php create mode 100644 src/AppBundle/Tests/Controller/LDraw/CategoryControllerTest.php create mode 100644 src/AppBundle/Tests/Controller/LDraw/PartControllerTest.php create mode 100644 src/AppBundle/Twig/AppExtension.php diff --git a/app/AppKernel.php b/app/AppKernel.php index 0718d4f..286cdd6 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -19,7 +19,9 @@ class AppKernel extends Kernel new Knp\Bundle\MenuBundle\KnpMenuBundle(), new Oneup\FlysystemBundle\OneupFlysystemBundle(), new Knp\Bundle\PaginatorBundle\KnpPaginatorBundle(), + new Knp\Bundle\GaufretteBundle\KnpGaufretteBundle(), new Lexik\Bundle\FormFilterBundle\LexikFormFilterBundle(), + new Liip\ImagineBundle\LiipImagineBundle(), ]; if (in_array($this->getEnvironment(), ['dev', 'test'], true)) { diff --git a/app/Resources/assets/style/style.scss b/app/Resources/assets/style/style.scss index 263739b..2cd9f12 100644 --- a/app/Resources/assets/style/style.scss +++ b/app/Resources/assets/style/style.scss @@ -1,4 +1,3 @@ @import "variables"; @import "main"; - diff --git a/app/Resources/views/ldraw/model/detail.html.twig b/app/Resources/views/ldraw/model/detail.html.twig index 5495401..35cd79b 100644 --- a/app/Resources/views/ldraw/model/detail.html.twig +++ b/app/Resources/views/ldraw/model/detail.html.twig @@ -8,7 +8,6 @@
number:
{{ model.number }}
name:
{{ model.name }}
category:
{{ model.category ? model.category.name }}
-
type:
{{ model.type ? model.type.name }}
model:
{{ model.path }}
author:
{{ model.author }}
keywords:
@@ -24,7 +23,7 @@ {% endfor %}
Download:
-
{{ model.number }}
+
{{ model.number }}
rebrickable parts ({{ rbParts|length }}):

@@ -42,7 +41,7 @@

- +

@@ -78,7 +77,7 @@ window.onload = function() { modelView = new ModelViewer(); var scene = modelView.initScene($('#model')); - modelView.loadStl('{{ path('model_stl', {'number' : model.number })}}'); + modelView.loadStl('{{ url('media_file', {'path': model.path }) }}'); modelView.render(); }; diff --git a/app/Resources/views/macros/elements.html.twig b/app/Resources/views/macros/elements.html.twig index b6df6ff..611b7ec 100644 --- a/app/Resources/views/macros/elements.html.twig +++ b/app/Resources/views/macros/elements.html.twig @@ -1,6 +1,6 @@ {% macro part(model) %}
- +

{{ model.number }}

{% endmacro %} diff --git a/app/autoload.php b/app/autoload.php index 31321fa..c92a7cc 100644 --- a/app/autoload.php +++ b/app/autoload.php @@ -1,7 +1,7 @@ =5.3.2" + }, + "require-dev": { + "sami/sami": "dev-master" + }, + "suggest": { + "ext-gd": "to use the GD implementation", + "ext-gmagick": "to use the Gmagick implementation", + "ext-imagick": "to use the Imagick implementation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-develop": "0.7-dev" + } + }, + "autoload": { + "psr-0": { + "Imagine": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bulat Shakirzyanov", + "email": "mallluhuct@gmail.com", + "homepage": "http://avalanche123.com" + } + ], + "description": "Image processing for PHP 5.3", + "homepage": "http://imagine.readthedocs.org/", + "keywords": [ + "drawing", + "graphics", + "image manipulation", + "image processing" + ], + "time": "2015-09-19T16:54:05+00:00" + }, { "name": "incenteev/composer-parameter-handler", "version": "v2.1.2", @@ -1053,6 +1110,94 @@ ], "time": "2014-01-12T16:20:24+00:00" }, + { + "name": "knplabs/gaufrette", + "version": "v0.3.1", + "source": { + "type": "git", + "url": "https://github.com/KnpLabs/Gaufrette.git", + "reference": "771ad16f4b2e7f9d35f44b201956e83c6fbf5dde" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/KnpLabs/Gaufrette/zipball/771ad16f4b2e7f9d35f44b201956e83c6fbf5dde", + "reference": "771ad16f4b2e7f9d35f44b201956e83c6fbf5dde", + "shasum": "" + }, + "require": { + "php": ">=5.4" + }, + "conflict": { + "microsoft/windowsazure": "<0.4.3" + }, + "require-dev": { + "amazonwebservices/aws-sdk-for-php": "1.5.*", + "aws/aws-sdk-php": "^2.4.12", + "doctrine/dbal": ">=2.3", + "dropbox-php/dropbox-php": "*", + "google/apiclient": "~1.1.3", + "herzult/php-ssh": "*", + "league/flysystem": "~1.0", + "mikey179/vfsstream": "~1.2.0", + "phpseclib/phpseclib": "^2.0", + "phpspec/phpspec": "~2.4", + "phpunit/phpunit": "3.7.*", + "rackspace/php-opencloud": "^1.9.2" + }, + "suggest": { + "amazonwebservices/aws-sdk-for-php": "to use the legacy Amazon S3 adapters", + "aws/aws-sdk-php": "to use the Amazon S3 adapter", + "doctrine/dbal": "to use the Doctrine DBAL adapter", + "dropbox-php/dropbox-php": "to use the Dropbox adapter", + "ext-apc": "to use the APC adapter", + "ext-curl": "*", + "ext-fileinfo": "This extension is used to automatically detect the content-type of a file in the AwsS3, OpenCloud, AzureBlogStorage and GoogleCloudStorage adapters", + "ext-mbstring": "*", + "ext-mongo": "*", + "ext-zip": "to use the Zip adapter", + "google/apiclient": "to use GoogleCloudStorage adapter", + "herzult/php-ssh": "to use SFtp adapter", + "knplabs/knp-gaufrette-bundle": "to use with Symfony2", + "league/flysystem": "to use Flysystem adapters", + "microsoft/windowsazure": "to use Microsoft Azure Blob Storage adapter", + "phpseclib/phpseclib": "to use PhpseclibSftp adapter", + "rackspace/php-opencloud": "to use Opencloud adapter" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Gaufrette": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "The contributors", + "homepage": "http://github.com/knplabs/Gaufrette/contributors" + }, + { + "name": "KnpLabs Team", + "homepage": "http://knplabs.com" + } + ], + "description": "PHP5 library that provides a filesystem abstraction layer", + "homepage": "http://knplabs.com", + "keywords": [ + "abstraction", + "file", + "filesystem", + "media" + ], + "time": "2017-03-20T01:23:34+00:00" + }, { "name": "knplabs/knp-components", "version": "1.3.4", @@ -1124,6 +1269,64 @@ ], "time": "2016-12-06T18:10:24+00:00" }, + { + "name": "knplabs/knp-gaufrette-bundle", + "version": "v0.4.0", + "source": { + "type": "git", + "url": "https://github.com/KnpLabs/KnpGaufretteBundle.git", + "reference": "06d91a8a575773cd0361c1246c9c499b6bdd5d68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/KnpLabs/KnpGaufretteBundle/zipball/06d91a8a575773cd0361c1246c9c499b6bdd5d68", + "reference": "06d91a8a575773cd0361c1246c9c499b6bdd5d68", + "shasum": "" + }, + "require": { + "knplabs/gaufrette": "~0.1.7|~0.2|~0.3", + "symfony/framework-bundle": "~2.0|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.2", + "symfony/console": "~2.0|~3.0", + "symfony/yaml": "~2.0|~3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "0.4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Knp\\Bundle\\GaufretteBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "The contributors", + "homepage": "https://github.com/knplabs/KnpGaufretteBundle/contributors" + }, + { + "name": "Antoine Hérault", + "email": "antoine.herault@gmail.com" + } + ], + "description": "Allows to easily use the Gaufrette library in a Symfony project", + "homepage": "http://knplabs.com", + "keywords": [ + "abstraction", + "file", + "filesystem", + "media" + ], + "time": "2017-03-16T21:01:25+00:00" + }, { "name": "knplabs/knp-menu", "version": "2.2.0", @@ -1394,16 +1597,16 @@ }, { "name": "lexik/form-filter-bundle", - "version": "v5.0.3", + "version": "v5.0.4", "source": { "type": "git", "url": "https://github.com/lexik/LexikFormFilterBundle.git", - "reference": "124a6c8e9eb109e7616a18d916bbc33137bb308d" + "reference": "28c09d6d9f278875ca9648c4b66eeb457c37d3b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lexik/LexikFormFilterBundle/zipball/124a6c8e9eb109e7616a18d916bbc33137bb308d", - "reference": "124a6c8e9eb109e7616a18d916bbc33137bb308d", + "url": "https://api.github.com/repos/lexik/LexikFormFilterBundle/zipball/28c09d6d9f278875ca9648c4b66eeb457c37d3b6", + "reference": "28c09d6d9f278875ca9648c4b66eeb457c37d3b6", "shasum": "" }, "require": { @@ -1444,13 +1647,113 @@ "description": "This bundle aim to provide classes to build some form filters and then build a doctrine query from this form filter.", "homepage": "https://github.com/lexik/LexikFormFilterBundle", "keywords": [ - "Symfony2", "bundle", "doctrine", "filter", - "form" + "form", + "symfony" ], - "time": "2017-01-24T13:03:45+00:00" + "time": "2017-03-27T07:28:34+00:00" + }, + { + "name": "liip/imagine-bundle", + "version": "1.7.4", + "source": { + "type": "git", + "url": "https://github.com/liip/LiipImagineBundle.git", + "reference": "105dd9c3446e3eb44e33161d4e636a3abafb6d7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/liip/LiipImagineBundle/zipball/105dd9c3446e3eb44e33161d4e636a3abafb6d7f", + "reference": "105dd9c3446e3eb44e33161d4e636a3abafb6d7f", + "shasum": "" + }, + "require": { + "imagine/imagine": "^0.6.3,<0.7", + "php": "^5.3.9|^7.0", + "symfony/asset": "~2.3|~3.0", + "symfony/filesystem": "~2.3|~3.0", + "symfony/finder": "~2.3|~3.0", + "symfony/framework-bundle": "~2.3|~3.0", + "symfony/options-resolver": "~2.3|~3.0", + "symfony/process": "~2.3|~3.0", + "symfony/templating": "~2.3|~3.0", + "symfony/translation": "~2.3|~3.0" + }, + "require-dev": { + "amazonwebservices/aws-sdk-for-php": "~1.0", + "aws/aws-sdk-php": "~2.4", + "doctrine/cache": "~1.1", + "doctrine/orm": "~2.3", + "ext-gd": "*", + "friendsofphp/php-cs-fixer": "~2.0", + "phpunit/phpunit": "~4.3|~5.0", + "psr/log": "~1.0", + "satooshi/php-coveralls": "~1.0", + "sllh/php-cs-fixer-styleci-bridge": "~2.1", + "symfony/browser-kit": "~2.3|~3.0", + "symfony/console": "~2.3|~3.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/form": "~2.3|~3.0", + "symfony/phpunit-bridge": "~2.3|~3.0", + "symfony/validator": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0", + "twig/twig": "~1.12|~2.0" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "required on PHP >= 7.0 to use mongo components with mongodb extension", + "amazonwebservices/aws-sdk-for-php": "required to use AWS version 1 cache resolver", + "aws/aws-sdk-php": "required to use AWS version 2/3 cache resolver", + "doctrine/mongodb-odm": "required to use mongodb-backed doctrine components", + "ext-exif": "required to read EXIF metadata from images", + "ext-gd": "required to use gd driver", + "ext-gmagick": "required to use gmagick driver", + "ext-imagick": "required to use imagick driver", + "ext-mongo": "required for mongodb components on PHP <7.0", + "ext-mongodb": "required for mongodb components on PHP >=7.0", + "league/flysystem": "required to use FlySystem data loader or cache resolver", + "monolog/monolog": "A psr/log compatible logger is required to enable logging", + "twig/twig": "required to use the provided Twig extension. Version 1.12 or greater needed" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-1.0": "1.7-dev" + } + }, + "autoload": { + "psr-4": { + "Liip\\ImagineBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Liip and other contributors", + "homepage": "https://github.com/liip/LiipImagineBundle/contributors" + } + ], + "description": "This bundle provides an image manipulation abstraction toolkit for Symfony-based projects.", + "homepage": "http://liip.ch", + "keywords": [ + "bundle", + "image", + "imagine", + "liip", + "manipulation", + "photos", + "pictures", + "symfony", + "transformation" + ], + "time": "2017-03-02T20:18:55+00:00" }, { "name": "monolog/monolog", @@ -1532,16 +1835,16 @@ }, { "name": "oneup/flysystem-bundle", - "version": "1.11.0", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/1up-lab/OneupFlysystemBundle.git", - "reference": "a68f83415e3af2313c529be6b22bfddfcfe8e90f" + "reference": "2addd1077360790a7722fef09388a003576d585c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/1up-lab/OneupFlysystemBundle/zipball/a68f83415e3af2313c529be6b22bfddfcfe8e90f", - "reference": "a68f83415e3af2313c529be6b22bfddfcfe8e90f", + "url": "https://api.github.com/repos/1up-lab/OneupFlysystemBundle/zipball/2addd1077360790a7722fef09388a003576d585c", + "reference": "2addd1077360790a7722fef09388a003576d585c", "shasum": "" }, "require": { @@ -1562,6 +1865,7 @@ "league/flysystem-ziparchive": "~1.0", "litipk/flysystem-fallback-adapter": "~0.1", "phpunit/phpunit": "^4.4", + "superbalist/flysystem-google-storage": "~4.0", "symfony/asset": "~2.0|~3.0", "symfony/browser-kit": "~2.0|~3.0", "symfony/finder": "~2.0|~3.0", @@ -1583,6 +1887,7 @@ "league/flysystem-webdav": "Allows you to use WebDAV storage", "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", "litipk/flysystem-fallback-adapter": "Allows you to use a fallback filesystem", + "superbalist/flysystem-google-storage": "Allows you to use Google Cloud Storage buckets", "twistor/flysystem-stream-wrapper": "Allows you to use stream wrapper" }, "type": "symfony-bundle", @@ -1611,7 +1916,7 @@ "abstraction", "filesystem" ], - "time": "2017-03-03T13:41:53+00:00" + "time": "2017-03-27T08:48:46+00:00" }, { "name": "paragonie/random_compat", diff --git a/src/AppBundle/Command/LoadLDRawLibraryCommand.php b/src/AppBundle/Command/LoadLDRawLibraryCommand.php index 0d94f07..f46823b 100644 --- a/src/AppBundle/Command/LoadLDRawLibraryCommand.php +++ b/src/AppBundle/Command/LoadLDRawLibraryCommand.php @@ -15,7 +15,7 @@ class LoadLDRawLibraryCommand extends ContainerAwareCommand ->setName('app:load:ldraw') ->setDescription('Loads LDraw library parts') ->setHelp('This command allows you to..') - ->addArgument('ldraw_path', InputArgument::OPTIONAL, 'Path to LDraw library folder'); + ->addArgument('file', InputArgument::OPTIONAL, 'Model to load'); } protected function execute(InputInterface $input, OutputInterface $output) @@ -27,11 +27,11 @@ class LoadLDRawLibraryCommand extends ContainerAwareCommand try { // TODO handle relative path to dir - if (($ldrawPath = $input->getArgument('ldraw_path')) == null) { - $ldrawPath = $ldrawLoader->downloadLibrary(); + if (($ldrawPath = $input->getArgument('file')) != null) { + $ldrawPath = $ldrawLoader->loadModel($ldrawPath); + } else { + $ldrawLoader->loadAllModels(); } - - $ldrawLoader->loadData($ldrawPath); } catch (\Exception $e) { printf($e->getMessage()); } diff --git a/src/AppBundle/Controller/MediaController.php b/src/AppBundle/Controller/MediaController.php index 83923e3..ea25151 100644 --- a/src/AppBundle/Controller/MediaController.php +++ b/src/AppBundle/Controller/MediaController.php @@ -8,6 +8,7 @@ use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\Routing\Annotation\Route; /** @@ -16,54 +17,28 @@ use Symfony\Component\Routing\Annotation\Route; class MediaController extends Controller { /** - * @Route("/model/stl/{number}", name="model_stl") + * @Route("/{path}", name="media_file", requirements={"path"=".+"}) * * @return Response */ - public function stlAction(Model $model) + public function stlAction($path) { $mediaFilesystem = $this->get('oneup_flysystem.media_filesystem'); - if ($mediaFilesystem->has($model->getPath())) { - $response = new BinaryFileResponse($mediaFilesystem->getAdapter()->getPathPrefix().DIRECTORY_SEPARATOR.$model->getPath()); - $response->headers->set('Content-Type', 'application/vnd.ms-pki.stl'); + if ($mediaFilesystem->has($path)) { + $response = new BinaryFileResponse($mediaFilesystem->getAdapter()->getPathPrefix().DIRECTORY_SEPARATOR.$path); + $response->headers->set('Content-Type', $mediaFilesystem->getMimetype($path)); // Create the disposition of the file $disposition = $response->headers->makeDisposition( ResponseHeaderBag::DISPOSITION_ATTACHMENT, - $model->getNumber().'.stl' + basename($path) ); $response->headers->set('Content-Disposition', $disposition); return $response; } - throw new FileNotFoundException($model->getPath()); - } - - /** - * @Route("/model/image/{number}", name="model_image") - * - * @return Response - */ - public function imageAction(Model $model) - { - $mediaFilesystem = $this->get('oneup_flysystem.media_filesystem'); - - if ($mediaFilesystem->has('ldraw'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.$model->getNumber().'.png')) { - $response = new BinaryFileResponse($mediaFilesystem->getAdapter()->getPathPrefix().DIRECTORY_SEPARATOR.'ldraw'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.$model->getNumber().'.png'); - $response->headers->set('Content-Type', 'image/png'); - - // Create the disposition of the file - $disposition = $response->headers->makeDisposition( - ResponseHeaderBag::DISPOSITION_ATTACHMENT, - $model->getNumber().'png' - ); - - $response->headers->set('Content-Disposition', $disposition); - - return $response; - } - throw new FileNotFoundException($model->getNumber().'png'); + throw new FileNotFoundException($path); } } diff --git a/src/AppBundle/Entity/LDraw/Model.php b/src/AppBundle/Entity/LDraw/Model.php index fb7e90a..a761fa6 100644 --- a/src/AppBundle/Entity/LDraw/Model.php +++ b/src/AppBundle/Entity/LDraw/Model.php @@ -17,13 +17,6 @@ class Model { use NumberTrait; - /** - * @var Type - * - * @ORM\ManyToOne(targetEntity="AppBundle\Entity\LDraw\Type", inversedBy="models", cascade={"persist"}) - */ - private $type; - /** * @var string * @@ -163,22 +156,6 @@ class Model return $this; } - /** - * @return string - */ - public function getType() - { - return $this->type; - } - - /** - * @param string $type - */ - public function setType($type) - { - $this->type = $type; - } - /** * @return string */ diff --git a/src/AppBundle/Entity/LDraw/Type.php b/src/AppBundle/Entity/LDraw/Type.php deleted file mode 100644 index c33ea34..0000000 --- a/src/AppBundle/Entity/LDraw/Type.php +++ /dev/null @@ -1,91 +0,0 @@ -models = new ArrayCollection(); - } - - /** - * @return string - */ - public function getName() - { - return $this->name; - } - - /** - * @param string $name - */ - public function setName($name) - { - $this->name = $name; - } - - /** - * Get models. - * - * @return ArrayCollection - */ - public function getModels() - { - return $this->models; - } - - /** - * @param Model $model - * - * @return Type - */ - public function addModel(Model $model) - { - $this->models->add($model); - - return $this; - } - - /** - * @param Model $model - * - * @return Type - */ - public function removeModel(Model $model) - { - $this->models->remove($model); - - return $this; - } -} diff --git a/src/AppBundle/Exception/FileNotFoundException.php b/src/AppBundle/Exception/FileNotFoundException.php new file mode 100644 index 0000000..dbacf85 --- /dev/null +++ b/src/AppBundle/Exception/FileNotFoundException.php @@ -0,0 +1,15 @@ +repository = $repository; - } - - /** - * Create new Keyword entity with $name or retrieve one. - * - * @param $name - * - * @return Type - */ - public function create($name) - { - if (($type = $this->repository->findByName($name)) == null) { - $type = new Type(); - $type->setName($name); - } - - return $type; - } -} diff --git a/src/AppBundle/Manager/LDrawManager.php b/src/AppBundle/Manager/LDrawManager.php index e452f93..84fb05b 100644 --- a/src/AppBundle/Manager/LDrawManager.php +++ b/src/AppBundle/Manager/LDrawManager.php @@ -17,9 +17,6 @@ class LDrawManager /** @var KeywordManager */ private $keywordManager; - /** @var TypeManager */ - private $typeManager; - /** @var SubpartManager */ private $subpartManager; @@ -34,16 +31,14 @@ class LDrawManager * * @param CategoryManager $categoryManager * @param KeywordManager $keywordManager - * @param TypeManager $typeManager * @param SubpartManager $subpartManager * @param ModelManager $modelManager * @param AliasManager $aliasManager */ - public function __construct(CategoryManager $categoryManager, KeywordManager $keywordManager, TypeManager $typeManager, SubpartManager $subpartManager, ModelManager $modelManager, AliasManager $aliasManager) + public function __construct(CategoryManager $categoryManager, KeywordManager $keywordManager, SubpartManager $subpartManager, ModelManager $modelManager, AliasManager $aliasManager) { $this->categoryManager = $categoryManager; $this->keywordManager = $keywordManager; - $this->typeManager = $typeManager; $this->subpartManager = $subpartManager; $this->modelManager = $modelManager; $this->aliasManager = $aliasManager; @@ -65,14 +60,6 @@ class LDrawManager return $this->keywordManager; } - /** - * @return TypeManager - */ - public function getTypeManager() - { - return $this->typeManager; - } - /** * @return SubpartManager */ diff --git a/src/AppBundle/Manager/RebrickableManager.php b/src/AppBundle/Manager/RebrickableManager.php new file mode 100644 index 0000000..9896d53 --- /dev/null +++ b/src/AppBundle/Manager/RebrickableManager.php @@ -0,0 +1,18 @@ +ldview = $ldview; $this->mediaFilesystem = $mediaFilesystem; + $this->ldrawLibraryFilesystem = $ldrawLibraryFilesystem; } /** * @param Filesystem $ldrawFilesystem */ - public function setLdrawFilesystem($ldrawFilesystem) + public function setLdrawFilesystem($ldrawLibraryFilesystem) { - $this->ldrawFilesystem = $ldrawFilesystem; + $this->ldrawLibraryFilesystem = $ldrawLibraryFilesystem; } /** @@ -53,18 +56,18 @@ class LDViewService * * @return File */ - public function datToStl($file, $LDrawDir = null) + public function datToStl($file) { if (!$this->mediaFilesystem->has('ldraw'.DIRECTORY_SEPARATOR.'models')) { $this->mediaFilesystem->createDir('ldraw'.DIRECTORY_SEPARATOR.'models'); } - $newFile = 'ldraw'.DIRECTORY_SEPARATOR.'models'.DIRECTORY_SEPARATOR.$file['filename'].'.stl'; + $newFile = 'ldraw'.DIRECTORY_SEPARATOR.'models'.DIRECTORY_SEPARATOR.basename($file,'.dat').'.stl'; - if (!$this->mediaFilesystem->has($newFile)) { + if (!file_exists($newFile)) { $this->runLDView([ - $this->ldrawFilesystem->getAdapter()->getPathPrefix().$file['path'], - '-LDrawDir='.$this->ldrawFilesystem->getAdapter()->getPathPrefix(), + $file, + '-LDrawDir='.$this->ldrawLibraryFilesystem->getAdapter()->getPathPrefix(), '-ExportFiles=1', '-ExportSuffix=.stl', '-ExportsDir='.$this->mediaFilesystem->getAdapter()->getPathPrefix().'ldraw'.DIRECTORY_SEPARATOR.'models', @@ -87,18 +90,18 @@ class LDViewService * * @return File */ - public function datToPng($file, $LDrawDir = null) + public function datToPng($file) { if (!$this->mediaFilesystem->has('ldraw'.DIRECTORY_SEPARATOR.'images')) { $this->mediaFilesystem->createDir('ldraw'.DIRECTORY_SEPARATOR.'images'); } - $newFile = 'ldraw'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.$file['filename'].'.png'; + $newFile = 'ldraw'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.basename($file,'.dat').'.png'; if (!$this->mediaFilesystem->has($newFile)) { $this->runLDView([ - $this->ldrawFilesystem->getAdapter()->getPathPrefix().$file['path'], - '-LDrawDir='.$this->ldrawFilesystem->getAdapter()->getPathPrefix(), + $file, + '-LDrawDir='.$this->ldrawLibraryFilesystem->getAdapter()->getPathPrefix(), '-AutoCrop=1', '-SaveAlpha=0', '-SnapshotSuffix=.png', diff --git a/src/AppBundle/Service/Loader/LDrawLoaderService.php b/src/AppBundle/Service/Loader/LDrawLoaderService.php index 402274f..bbf08cd 100644 --- a/src/AppBundle/Service/Loader/LDrawLoaderService.php +++ b/src/AppBundle/Service/Loader/LDrawLoaderService.php @@ -5,8 +5,8 @@ namespace AppBundle\Service\Loader; use AppBundle\Entity\LDraw\Category; use AppBundle\Entity\LDraw\Model; use AppBundle\Entity\LDraw\Type; +use AppBundle\Exception\FileNotFoundException; use AppBundle\Manager\LDrawManager; -use AppBundle\Service\LDrawService; use AppBundle\Service\LDViewService; use AppBundle\Utils\DatParser; use League\Flysystem\Adapter\Local; @@ -22,7 +22,7 @@ class LDrawLoaderService extends BaseLoaderService /** * @var Filesystem */ - private $ldraw; + private $ldrawLibraryFilesystem; /** * @var string download URL with current LDraw library @@ -41,67 +41,35 @@ class LDrawLoaderService extends BaseLoaderService private $datParser; /** - * @param array $ldraw_url + * LDrawLoaderService constructor. + * @param Filesystem $ldrawLibraryFilesystem + * @param LDViewService $LDViewService + * @param $ldraw_url + * @param LDrawManager $ldrawService + * @param DatParser $datParser */ - public function __construct(LDViewService $LDViewService, $ldraw_url, LDrawManager $ldrawService, $datParser) + public function __construct($ldrawLibraryFilesystem, $LDViewService, $ldraw_url, $ldrawService, $datParser) { $this->LDViewService = $LDViewService; $this->ldraw_url = $ldraw_url; $this->ldrawService = $ldrawService; $this->datParser = $datParser; + $this->ldrawLibraryFilesystem = $ldrawLibraryFilesystem; } - /** - * Download current LDraw library and extract it to system tmp directory. - * - * @return string Absolute path to temporary Ldraw library - */ - public function downloadLibrary() + public function loadAllModels() { - $temp = $this->downloadFile($this->ldraw_url); - - // Create unique temporary directory - $temp_dir = tempnam(sys_get_temp_dir(), 'printabrick.'); - mkdir($temp_dir); - - // Unzip downloaded library zip file to temporary directory - $zip = new \ZipArchive(); - if ($zip->open($temp) != 'true') { - throw new LogicException('Error :- Unable to open the Zip File'); - } - $zip->extractTo($temp_dir); - $zip->close(); - - // Unlink downloaded zip file - unlink($temp); - - return $temp_dir; - } - - /** - * @param $LDrawLibrary - */ - public function loadData($LDrawLibrary) - { - $adapter = new Local($LDrawLibrary); - $this->ldraw = new Filesystem($adapter); - - $this->LDViewService->setLdrawFilesystem($this->ldraw); - - $this->loadParts(); - } - - // TODO refactor - - public function loadParts() - { - $files = $this->ldraw->get('parts')->getContents(); + $files = $this->ldrawLibraryFilesystem->get('parts')->getContents(); + $modelManager = $this->ldrawService->getModelManager(); $this->initProgressBar(count($files)); foreach ($files as $file) { if ($file['type'] == 'file' && $file['extension'] == 'dat') { - $this->loadModel($file); + $model = $this->loadModel($this->ldrawLibraryFilesystem->getAdapter()->getPathPrefix().$file['path']); + + if($model) + $modelManager->getRepository()->save($model); } $this->progressBar->advance(); @@ -116,31 +84,32 @@ class LDrawLoaderService extends BaseLoaderService * * @return Model|null */ - private function loadModel($file) + public function loadModel($file) { $modelManager = $this->ldrawService->getModelManager(); $subpartManager = $this->ldrawService->getSubpartManager(); $aliasManager = $this->ldrawService->getAliasManager(); - $header = $this->datParser->parse($this->ldraw->get($file['path'])); + if($model = $modelManager->findByNumber(basename($file,'.dat'))) { + return $model; + } + + $header = $this->datParser->parse($file); if ($this->isModelIncluded($header)) { - if (isset($header['parent']) && ($parent = $this->getModelParent($header['parent'])) && ($parentFile = $this->getModelFile($parent))) { - $parentHeader = $this->datParser->parse($parentFile); + if (isset($header['parent']) && ($parent = $this->getModelParent($header['parent'])) && ($parentFile = $this->findModelFile($parent))) { + $parentModel = $this->loadModel($parentFile); - if ($this->isModelIncluded($parentHeader)) { - $model = $modelManager->create($parentHeader['id']); - $alias = $aliasManager->create($header['id'], $model); + if ($parentModel) { + $alias = $aliasManager->create($header['id'], $parentModel); $aliasManager->getRepository()->save($alias); - - return $model; + $this->progressBar->advance(); } + return $parentModel; } else { $model = $modelManager->create($header['id']); $model->setName($header['name']); - $model - ->setCategory($this->ldrawService->getCategoryManager()->create($header['category'])) - ->setType($this->ldrawService->getTypeManager()->create($header['type'])); + $model->setCategory($this->ldrawService->getCategoryManager()->create($header['category'])); if (isset($header['keywords'])) { foreach ($header['keywords'] as $keyword) { @@ -150,20 +119,19 @@ class LDrawLoaderService extends BaseLoaderService } if (isset($header['subparts'])) { - $model->setType($this->ldrawService->getTypeManager()->create('Shortcut')); + foreach ($header['subparts'] as $subpartId) { + $subpartId = $this->getModelParent($subpartId); - foreach ($header['subparts'] as $subpart) { - $subpart = $this->getModelParent($subpart); + $subModel = $modelManager->getRepository()->findOneBy(['number'=>$subpartId]); - if ($subpartFile = $this->getModelFile($subpart)) { - $subpartHeader = $this->datParser->parse($subpartFile); + if(!$subModel && ($subpartFile = $this->findModelFile($subpartId)) != null) { + $subModel = $this->loadModel($subpartFile); + } - if ($this->isModelIncluded($subpartHeader)) { - $subpartModel = $modelManager->create($subpartHeader['id']); - - $subpart = $subpartManager->create($model, $subpartModel); - $subpartManager->getRepository()->save($subpart); - } + if ($subModel) { + $subpart = $subpartManager->create($model, $subModel); + $subpartManager->getRepository()->save($subpart); + $this->progressBar->advance(); } } } @@ -172,13 +140,9 @@ class LDrawLoaderService extends BaseLoaderService $model->setModified($header['modified']); $model->setPath($this->loadStlModel($file)); - $modelManager->getRepository()->save($model); - return $model; } } - - return null; } /** @@ -186,13 +150,13 @@ class LDrawLoaderService extends BaseLoaderService * * @param $id * - * @return \League\Flysystem\Directory|File|\League\Flysystem\Handler|null + * @return string */ - private function getModelFile($id) + private function findModelFile($id) { $path = 'parts/'.strtolower($id).'.dat'; - if ($this->ldraw->has($path)) { - return $this->ldraw->get($path); + if ($this->ldrawLibraryFilesystem->has($path)) { + return $this->ldrawLibraryFilesystem->getAdapter()->getPathPrefix().$path; } return null; @@ -207,7 +171,7 @@ class LDrawLoaderService extends BaseLoaderService */ private function getModelParent($number) { - if (($file = $this->getModelFile($number)) == null) { + if (($file = $this->findModelFile($number)) == null) { return $number; } @@ -216,7 +180,7 @@ class LDrawLoaderService extends BaseLoaderService do { $parent = isset($header['parent']) ? $header['parent'] : null; - if ($file = $this->getModelFile($parent)) { + if ($file = $this->findModelFile($parent)) { $header = $this->datParser->parse($file); } else { break; @@ -240,7 +204,7 @@ class LDrawLoaderService extends BaseLoaderService strpos($header['id'], 's') !== 0 && $header['type'] != 'Subpart' && $header['type'] != 'Sticker' - && !(($header['type'] == 'Printed') && $this->getModelFile($header['parent'])) + && !(($header['type'] == 'Printed') && $this->findModelFile($header['parent'])) ) { return true; } @@ -260,7 +224,7 @@ class LDrawLoaderService extends BaseLoaderService private function loadStlModel($file) { try { - return $this->LDViewService->datToStl($file, $this->ldraw)->getPath(); + return $this->LDViewService->datToStl($file)->getPath(); } catch (\Exception $e) { throw $e; //TODO } diff --git a/src/AppBundle/Service/ZipService.php b/src/AppBundle/Service/ZipService.php new file mode 100644 index 0000000..7be9188 --- /dev/null +++ b/src/AppBundle/Service/ZipService.php @@ -0,0 +1,8 @@ +request('GET', '/ldraw_category/'); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET /ldraw_category/"); + + // Go to the show view + $crawler = $client->click($crawler->selectLink('show')->link()); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code"); + } + + */ +} diff --git a/src/AppBundle/Tests/Controller/LDraw/PartControllerTest.php b/src/AppBundle/Tests/Controller/LDraw/PartControllerTest.php new file mode 100644 index 0000000..4a1b64f --- /dev/null +++ b/src/AppBundle/Tests/Controller/LDraw/PartControllerTest.php @@ -0,0 +1,25 @@ +request('GET', '/ldraw_part/'); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET /ldraw_part/"); + + // Go to the show view + $crawler = $client->click($crawler->selectLink('show')->link()); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code"); + } + + */ +} diff --git a/src/AppBundle/Twig/AppExtension.php b/src/AppBundle/Twig/AppExtension.php new file mode 100644 index 0000000..326e233 --- /dev/null +++ b/src/AppBundle/Twig/AppExtension.php @@ -0,0 +1,35 @@ +rebrickableAPIManager = $rebrickableAPIManager; + } + + public function getFilters() + { + return [ + new \Twig_SimpleFilter('partImage', [$this, 'partImage']), + ]; + } + + public function partImage($number) + { + if ($part = $this->rebrickableAPIManager->getPart($number)) { + return $part->getImgUrl(); + } + } +} diff --git a/src/AppBundle/Utils/DatParser.php b/src/AppBundle/Utils/DatParser.php index 7ea6aa7..8c3cef0 100644 --- a/src/AppBundle/Utils/DatParser.php +++ b/src/AppBundle/Utils/DatParser.php @@ -2,6 +2,7 @@ namespace AppBundle\Utils; +use AppBundle\Exception\FileNotFoundException; use League\Flysystem\File; use Symfony\Component\Asset\Exception\LogicException; @@ -38,93 +39,98 @@ class DatParser * * @return array */ - public function parse(File $file) + public function parse($file) { $header = []; - $handle = $file->readStream(); + if(file_exists($file)) { + try { + $handle = fopen($file, 'r'); - if ($handle) { - $firstLine = false; + if ($handle) { + $firstLine = false; - while (($line = fgets($handle)) !== false) { - $line = trim($line); + while (($line = fgets($handle)) !== false) { + $line = trim($line); - // Comments or META Commands - if (strpos($line, '0 ') === 0) { - $line = preg_replace('/^0 /', '', $line); + // Comments or META Commands + if (strpos($line, '0 ') === 0) { + $line = preg_replace('/^0 /', '', $line); - // 0 - if (!$firstLine) { - $array = explode(' ', ltrim(trim($line, 2), '=_~')); - $header['category'] = isset($array[0]) ? $array[0] : ''; - $header['name'] = preg_replace('/ {2,}/', ' ', ltrim($line, '=_')); + // 0 + if (!$firstLine) { + $array = explode(' ', ltrim(trim($line, 2), '=_~')); + $header['category'] = isset($array[0]) ? $array[0] : ''; + $header['name'] = preg_replace('/ {2,}/', ' ', ltrim($line, '=_')); - $firstLine = true; - } - // 0 !CATEGORY - elseif (strpos($line, '!CATEGORY ') === 0) { - $header['category'] = trim(preg_replace('/^!CATEGORY /', '', $line)); - } - // 0 !KEYWORDS , , ..., - elseif (strpos($line, '!KEYWORDS ') === 0) { - $header['keywords'] = explode(', ', preg_replace('/^!KEYWORDS /', '', $line)); - } - // 0 Name: .dat - elseif (strpos($line, 'Name: ') === 0) { - if (!isset($header['id'])) { - $header['id'] = preg_replace('/(^Name: )(.*)(.dat)/', '$2', $line); + $firstLine = true; + } // 0 !CATEGORY + elseif (strpos($line, '!CATEGORY ') === 0) { + $header['category'] = trim(preg_replace('/^!CATEGORY /', '', $line)); + } // 0 !KEYWORDS , , ..., + elseif (strpos($line, '!KEYWORDS ') === 0) { + $header['keywords'] = explode(', ', preg_replace('/^!KEYWORDS /', '', $line)); + } // 0 Name: .dat + elseif (strpos($line, 'Name: ') === 0) { + if (!isset($header['id'])) { + $header['id'] = preg_replace('/(^Name: )(.*)(.dat)/', '$2', $line); + } + } // 0 Author: [] + elseif (strpos($line, 'Author: ') === 0) { + $header['author'] = preg_replace('/^Author: /', '', $line); + } // 0 !LDRAW_ORG Part|Subpart|Primitive|48_Primitive|Shortcut (optional qualifier(s)) ORIGINAL|UPDATE YYYY-RR + elseif (strpos($line, '!LDRAW_ORG ') === 0) { + $type = preg_replace('/(^!LDRAW_ORG )(.*)( UPDATE| ORIGINAL)(.*)/', '$2', $line); + + $header['type'] = $type; + + // Last modification date in format YYYY-RR + $date = preg_replace('/(^!LDRAW_ORG )(.*)( UPDATE | ORIGINAL )(.*)/', '$4', $line); + if (preg_match('/^[1-2][0-9]{3}-[0-9]{2}$/', $date)) { + $header['modified'] = \DateTime::createFromFormat('Y-m-d H:i:s', $date . '-01 00:00:00'); + } else { + $header['modified'] = null; + } + } + } elseif (strpos($line, '1 ') === 0) { + $header['subparts'][] = $this->getAlias($line); } } - // 0 Author: [] - elseif (strpos($line, 'Author: ') === 0) { - $header['author'] = preg_replace('/^Author: /', '', $line); - } - // 0 !LDRAW_ORG Part|Subpart|Primitive|48_Primitive|Shortcut (optional qualifier(s)) ORIGINAL|UPDATE YYYY-RR - elseif (strpos($line, '!LDRAW_ORG ') === 0) { - $type = preg_replace('/(^!LDRAW_ORG )(.*)( UPDATE| ORIGINAL)(.*)/', '$2', $line); - $header['type'] = $type; - - // Last modification date in format YYYY-RR - $date = preg_replace('/(^!LDRAW_ORG )(.*)( UPDATE | ORIGINAL )(.*)/', '$4', $line); - if (preg_match('/^[1-2][0-9]{3}-[0-9]{2}$/', $date)) { - $header['modified'] = \DateTime::createFromFormat('Y-m-d H:i:s', $date.'-01 00:00:00'); - } else { - $header['modified'] = null; - } + if ($this->isStickerShortcutPart($header['name'], $header['id'])) { + $header['type'] = 'Sticker'; + } elseif (($parent = $this->relationMapper->find($header['id'], 'alias_model')) != $header['id']) { + $header['type'] = 'Alias'; + $header['subparts'] = null; + $header['parent'] = $parent; + } elseif (isset($header['subparts']) && count($header['subparts']) == 1 && in_array($header['type'], ['Part Alias', 'Shortcut Physical_Colour', 'Shortcut Alias', 'Part Physical_Colour'])) { + $header['parent'] = $header['subparts'][0]; + $header['subparts'] = null; + } elseif ($parent = $this->getPrintedParentId($header['id'])) { + $header['type'] = 'Printed'; + $header['subparts'] = null; + $header['parent'] = $parent; + } elseif ($parent = $this->getObsoleteParentId($header['name'])) { + $header['type'] = 'Alias'; + $header['subparts'] = null; + $header['parent'] = $parent; + } elseif (strpos($header['name'], '~') === 0 && $header['type'] != 'Alias') { + $header['type'] = 'Obsolete/Subpart'; } - } elseif (strpos($line, '1 ') === 0) { - $header['subparts'][] = $this->getAlias($line); + + $header['name'] = ltrim($header['name'], '~'); + + fclose($handle); + + return $header; } + } catch (\Exception $exception) { + dump($exception->getMessage()); + + return null; } - - if ($this->isStickerShortcutPart($header['name'], $header['id'])) { - $header['type'] = 'Sticker'; - } elseif (($parent = $this->relationMapper->find($header['id'], 'alias_model')) != $header['id']) { - $header['type'] = 'Alias'; - $header['parent'] = $parent; - } elseif (isset($header['subparts']) && count($header['subparts']) == 1 && in_array($header['type'], ['Part Alias', 'Shortcut Physical_Colour', 'Shortcut Alias', 'Part Physical_Colour'])) { - $header['parent'] = $header['subparts'][0]; - $header['subparts'] = null; - } elseif ($parent = $this->getPrintedParentId($header['id'])) { - $header['type'] = 'Printed'; - $header['parent'] = $parent; - } elseif ($parent = $this->getObsoleteParentId($header['name'])) { - $header['type'] = 'Alias'; - $header['parent'] = $parent; - } elseif (strpos($header['name'], '~') === 0 && $header['type'] != 'Alias') { - $header['type'] = 'Obsolete/Subpart'; - } - - $header['name'] = ltrim($header['name'], '~'); - - fclose($handle); - - return $header; } - - throw new LogicException('Error parsing DAT file'); //TODO + return null; } /** diff --git a/var/SymfonyRequirements.php b/var/SymfonyRequirements.php index 7e7723a..7e7a99d 100644 --- a/var/SymfonyRequirements.php +++ b/var/SymfonyRequirements.php @@ -780,7 +780,11 @@ class SymfonyRequirements extends RequirementCollection { $size = ini_get('realpath_cache_size'); $size = trim($size); - $unit = strtolower(substr($size, -1, 1)); + $unit = ''; + if (!ctype_digit($size)) { + $unit = strtolower(substr($size, -1, 1)); + $size = (int) substr($size, 0, -1); + } switch ($unit) { case 'g': return $size * 1024 * 1024 * 1024; diff --git a/web/config.php b/web/config.php index a031a3a..69df43c 100644 --- a/web/config.php +++ b/web/config.php @@ -270,7 +270,7 @@ $hasMinorProblems = (bool) count($minorProblems); } .sf-reset ul a, .sf-reset ul a:hover { - background: url(../images/blue-arrow.png) no-repeat right 6px; + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAICAYAAAAx8TU7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFdJREFUeNpiYACBjjOhDEiACSggCKTLgXQ5TJARqhIkcReIKxgqTGYxwvV0nDEGkmeAOIwJySiQ4HsgvseIpGo3ELsCtZ9lRDIvDCiwhwHJPEFkJwEEGACq6hdnax8y1AAAAABJRU5ErkJggg==) no-repeat right 7px; padding-right: 10px; } .sf-reset ul, ol {