diff --git a/.gitignore b/.gitignore index ad6ac8c..806985c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,5 +18,6 @@ /node_modules/ /app/Resources/libs/semantic/dist /web/resources/ +/var/media/ /web/media/ .php_cs.cache \ No newline at end of file diff --git a/app/Resources/assets/images/noimage.png b/app/Resources/assets/images/noimage.png new file mode 100644 index 0000000..59d5601 Binary files /dev/null and b/app/Resources/assets/images/noimage.png differ diff --git a/app/Resources/views/ajax.html.twig b/app/Resources/views/ajax.html.twig index 42aa693..8cbf23a 100644 --- a/app/Resources/views/ajax.html.twig +++ b/app/Resources/views/ajax.html.twig @@ -1,6 +1,3 @@ -{% import 'macros/blocks.html.twig' as blocks %} -{% import 'macros/elements.html.twig' as elements %} - {% for label, flashes in app.session.flashbag.all %} {% for flash in flashes %} {{ elements.flash(label,flash) }} diff --git a/app/Resources/views/brickset/description.html.twig b/app/Resources/views/brickset/description.html.twig index 3903491..8beaaf8 100644 --- a/app/Resources/views/brickset/description.html.twig +++ b/app/Resources/views/brickset/description.html.twig @@ -1,5 +1,8 @@ {% extends 'ajax.html.twig' %} +{% import 'macros/blocks.html.twig' as blocks %} +{% import 'macros/elements.html.twig' as elements %} + {% block content %} {% if description|length %} {{ description|raw }} diff --git a/app/Resources/views/brickset/images.html.twig b/app/Resources/views/brickset/images.html.twig index 0e1f501..66bad9f 100644 --- a/app/Resources/views/brickset/images.html.twig +++ b/app/Resources/views/brickset/images.html.twig @@ -1,5 +1,8 @@ {% extends 'ajax.html.twig' %} +{% import 'macros/blocks.html.twig' as blocks %} +{% import 'macros/elements.html.twig' as elements %} + {% block content %} {% if images|length %}
diff --git a/app/Resources/views/brickset/instructions.html.twig b/app/Resources/views/brickset/instructions.html.twig index 639168b..74348bb 100644 --- a/app/Resources/views/brickset/instructions.html.twig +++ b/app/Resources/views/brickset/instructions.html.twig @@ -1,5 +1,8 @@ {% extends 'ajax.html.twig' %} +{% import 'macros/blocks.html.twig' as blocks %} +{% import 'macros/elements.html.twig' as elements %} + {% block content %} {% if instructions|length != 0 %}

{{ 'set.instructions.text' | trans | raw | nl2br }}

diff --git a/app/Resources/views/brickset/reviews.html.twig b/app/Resources/views/brickset/reviews.html.twig index 252101f..dbba662 100644 --- a/app/Resources/views/brickset/reviews.html.twig +++ b/app/Resources/views/brickset/reviews.html.twig @@ -1,35 +1,59 @@ {% extends 'ajax.html.twig' %} +{% import 'macros/blocks.html.twig' as blocks %} +{% import 'macros/elements.html.twig' as elements %} + {% block content %} {% if reviews|length != 0 %}
{% for review in reviews %} -
- +
+
+
+
+
+ {% if review.overallRating %} +
Overall rating
+
+ {% endif %} + {% if review.valueForMoney %} +
Value for money
+
+ {% endif %} + {% if review.playability %} +
Playability
+
+ {% endif %} + {% if review.parts %} +
Parts
+
+ {% endif %} + {% if review.buildingExperience %} +
Building experience
+
+ {% endif %} +
+
-
-
-
-

{{ review.title }}

-
- {{ review.author }} -
diff --git a/app/Resources/views/macros/blocks.html.twig b/app/Resources/views/macros/blocks.html.twig index 1374d42..a1c4c5e 100644 --- a/app/Resources/views/macros/blocks.html.twig +++ b/app/Resources/views/macros/blocks.html.twig @@ -1,39 +1,70 @@ -{% macro modelImageMin(model, color) %} +{% macro partImage(number, filter, color = -1) %} + {% if filter == 'part_large' %} + {% set placeholder = asset("resources/images/unknown_large.png") %} + {% else %} + {% set placeholder = asset("resources/images/unknown.png") %} + {% endif %} +
- +
{% endmacro %} -{% macro modelImageLarge(model, color) %} -
- +{% macro setImage(number, filter) %} +
+ {% if filter == 'set_large' %} + {% set placeholder = asset("resources/images/unknown_large.png") %} + {% else %} + {% set placeholder = asset("resources/images/unknown.png") %} + {% endif %} + +
{% endmacro %} -{% macro model(model, quantity = null) %} +{% macro model(model, quantity = null, color = -1) %} + +{% endmacro %} + +{% macro part(part, quantity = null, color = -1) %} -{% endmacro %} - -{% macro part(part, quantity, color = -1) %} -
- {% endmacro %} +{% macro set(set) %} + +{% endmacro %} + {% macro empty(message) %}

{{ message }}

{% endmacro %} @@ -41,3 +72,4 @@ {% macro ccal2_license(title,author) %} "{{ title }}" by {{ author }}, used under CC BY 2.0 / Converted to stl from original {% endmacro %} + diff --git a/app/Resources/views/model/detail.html.twig b/app/Resources/views/model/detail.html.twig index 67116c5..68bb64a 100644 --- a/app/Resources/views/model/detail.html.twig +++ b/app/Resources/views/model/detail.html.twig @@ -1,5 +1,7 @@ {% extends 'base.html.twig' %} +{% import 'macros/blocks.html.twig' as blocks %} + {% block title %}#{{ model.number }} - {{ model.name }}{% endblock %} {% block header %}#{{ model.number }} - {{ model.name }}{% endblock %} @@ -9,16 +11,15 @@
- {{ blocks.modelImageLarge(model, -1) }} + {{ blocks.partImage(model.number,'part_large') }}
- {{ 'model.download'|trans }} - - + + @@ -41,16 +42,19 @@ {% if model.aliases|length %} - - - - + + + + {% endif %}
{{ 'model.category' | trans }}{{ model.category ? model.category.name }}{{ 'model.category' | trans }}{{ model.category ? model.category.name }}
{{ 'model.author' | trans }}{{ model.author.name }}
{{ 'model.aliases' | trans }} - {% for alias in model.aliases | slice(0,10)%} - {{ alias.number }}{% if not loop.last %},{% endif %} - {% endfor %} -
{{ 'model.aliases' | trans }} + {% for alias in model.aliases | slice(0,10)%} + {{ alias.number }}{% if not loop.last %},{% endif %} + {% endfor %} +
+ + {{ 'model.download'|trans }} +
@@ -66,12 +70,8 @@
- {% for subpart in model.subparts %} -
- {{ blocks.part(subpart.subpart) }} -

{{ subpart.count }}

-

color: {{ subpart.color.name }}

-
+ {% for subpart in subparts %} + {{ blocks.model(subpart['model'], subpart['quantity']) }} {% endfor %}
@@ -80,7 +80,7 @@
{% for subpart in related %}
- {{ blocks.part(subpart) }} + {{ blocks.model(subpart) }}
{% endfor %}
@@ -90,16 +90,25 @@
{% for subpart in model.parents %}
- {{ blocks.part(subpart.parent) }} + {{ blocks.model(subpart.parent) }}
{% endfor %}
- {% for set in sets %} - {{ set.number }} - {% endfor %} +
+
+ {#{% for set in sets %}#} + {##} + {#{% endfor %}#} +
+
diff --git a/app/Resources/views/model/index.html.twig b/app/Resources/views/model/index.html.twig index d42f494..04710f3 100644 --- a/app/Resources/views/model/index.html.twig +++ b/app/Resources/views/model/index.html.twig @@ -1,5 +1,7 @@ {% extends 'base.html.twig' %} +{% import 'macros/blocks.html.twig' as blocks %} + {% block title %}{{ 'page.model.index' | trans }}{% endblock %} {% block header %}{{ 'page.model.index' | trans }}{% endblock %} @@ -14,7 +16,7 @@ {{ form_row(form.search) }}
@@ -23,16 +25,15 @@
-
-
- {% for model in models %} - {{ blocks.model(model) }} - {% endfor %} -
+
+ {% for model in models %} + {{ blocks.model(model) }} + {% endfor %}
- {{ knp_pagination_render(models) }} -

{{ models.getTotalItemCount }}

+
+ {{ knp_pagination_render(models) }} +
diff --git a/app/Resources/views/rebrickable/part/detail.html.twig b/app/Resources/views/rebrickable/part/detail.html.twig index 600e043..60551db 100644 --- a/app/Resources/views/rebrickable/part/detail.html.twig +++ b/app/Resources/views/rebrickable/part/detail.html.twig @@ -1,6 +1,6 @@ {% extends 'base.html.twig' %} -{% import 'macros/elements.html.twig' as elements %} +{% import 'macros/blocks.html.twig' as blocks %} {% block header %}#{{ part.number }} - {{ part.name }}{% endblock %} @@ -8,7 +8,7 @@
- + {{ blocks.partImage(part.number,'part_large') }}
@@ -21,7 +21,7 @@ {{ 'part.name' | trans }}{{ part.name}} - {{ 'part.category' | trans }}{{ part.category.name }} + {{ 'part.category' | trans }}{{ part.category ? part.category.name }} {{ 'part.model' | trans }} @@ -48,6 +48,13 @@ {% endfor %} + + + Rebrickable + + + {{ dump(apiPart.externalIds, apiPart) }} + {% endif %}
@@ -63,9 +70,7 @@
diff --git a/app/Resources/views/set/detail.html.twig b/app/Resources/views/set/detail.html.twig index 765e30c..29b8faf 100644 --- a/app/Resources/views/set/detail.html.twig +++ b/app/Resources/views/set/detail.html.twig @@ -1,44 +1,38 @@ {% extends 'base.html.twig' %} -{% block title %}{{ set ? set.number }} {{ set ? set.name }}{% endblock %} +{% import 'macros/blocks.html.twig' as blocks %} -{% block header %}{{ set ? set.number }} {{ set ? set.name | escape('html') }}{% endblock %} +{% block title %}{{ set.number }} {{ set.name }}{% endblock %} + +{% block header %}{{ set.number }} {{ set.name | escape('html') }}{% endblock %} {% block content %}
-
- {% if brset %} - - {% else %} - - {% endif %} +
+ {{ blocks.setImage(set.number,'set_large') }}
- + - + - + - {% if set %} - - {% elseif brset %} - - {% endif %} + {% if brset %} @@ -53,33 +47,50 @@ {% endif %} - - - + + - + {% endif %} + +
{{ 'set.number' | trans }}{{ brset ? brset.legoSetID : set ? set.number : null}}{{ 'set.number' | trans }}{{ set.number }}
{{ 'set.name' | trans }}{{ brset ? brset.name : set ? set.name : null}}{{ 'set.name' | trans }}{{ set.name }}
{{ 'set.year' | trans }}{{ brset ? brset.year : set ? set.year : null}}{{ 'set.year' | trans }}{{ set.year }}
{{ 'set.theme' | trans }}{{ set.theme.parent ? set.theme.parent.name }} {{ set.theme.name }} {{ brset.theme }}{{ set.theme.parent ? set.theme.parent.name }} > {{ set.theme ? set.theme.name }}
{{ 'set.parts' | trans }} - {{ set ? set.partCount }} {{ brset ? '('~brset.pieces~')' }} + {{ set.partCount }} {{ brset ? '('~brset.pieces~')' }}
- Rebrickable - {% if brset %} +
+ Rebrickable + {% if brset %} Brickset - {% endif %} -
- {{ 'set.download'|trans }} - +
{{ 'set.download' | trans }}
+ + {% embed 'embeds/modal.html.twig' %} + {% block title %} + {{ 'set.download.title' | trans({'%set%': set.number~' '~set.name}) }} + {% endblock %} + {% block content %} + {#
#} + {##} + {#
#} + {#
#} + {#{{ 'set.download.warning.title' | trans }}#} + {#
#} + {#

{{ 'set.download.warning.text' | trans }}

#} + {#
#} + {#
#} + {% endblock %} + {% block actions %} +
+
+

{{ 'set.download.sorted.title' | trans }}

+

{{ 'set.download.sorted.text' | trans }}

+ Download ZIP +
+
+

{{ 'set.download.unsorted.title' | trans }}

+

{{ 'set.download.unsorted.text' | trans }}

+ Download ZIP +
+
+ {% endblock %} + {% endembed %}
@@ -97,11 +108,19 @@
+ {#
#} + {##} + {##} + {#
#} + +

{{ 'set.models.text' | trans({'%rebrickable%' : partCount, '%brickset%' : brset ? brset.pieces }) | nl2br }}

+
- {#{{ render(path('set_colors', {number: set.number})) }}#} + {#{{ render(path('set_models', {number: set.number})) }}#} + {{ render(path('set_sets', {number: set.number})) }}
{% if brset %}
diff --git a/app/Resources/views/set/index.html.twig b/app/Resources/views/set/index.html.twig index 5653de3..5814fd2 100644 --- a/app/Resources/views/set/index.html.twig +++ b/app/Resources/views/set/index.html.twig @@ -1,5 +1,7 @@ {% extends 'base.html.twig' %} +{% import 'macros/blocks.html.twig' as blocks %} + {% block title %}{{ 'page.set.index' | trans }}{% endblock %} {% block header %}{{ 'page.set.index' | trans }}{% endblock %} @@ -18,6 +20,7 @@ {{ form_widget(form.theme.id) }}
+
{{ form_row(form.partCount) }}
@@ -33,15 +36,10 @@
-
+
{% for set in sets %} - + {{ set(set) }} {% endfor %}
@@ -56,6 +54,4 @@ {% block javascripts %} {{ parent() }} - - {% endblock %} \ No newline at end of file diff --git a/app/Resources/views/set/tabs/colors.html.twig b/app/Resources/views/set/tabs/colors.html.twig index 9c4aefd..c7f1b47 100644 --- a/app/Resources/views/set/tabs/colors.html.twig +++ b/app/Resources/views/set/tabs/colors.html.twig @@ -10,7 +10,7 @@
{% for model in color['models'] %} - {{ blocks.model(model['model'],model['quantity']) }} + {{ blocks.model(model['model'],model['quantity'], color['color'].id) }} {% endfor %}
diff --git a/app/Resources/views/set/tabs/inventory.html.twig b/app/Resources/views/set/tabs/inventory.html.twig index 597f347..216f5cd 100644 --- a/app/Resources/views/set/tabs/inventory.html.twig +++ b/app/Resources/views/set/tabs/inventory.html.twig @@ -8,7 +8,8 @@
{% for inventoryPart in regularParts %} - {{ blocks.part(inventoryPart.part,inventoryPart.quantity, inventoryPart.color) }} + {{ blocks.part(inventoryPart.part.number,inventoryPart.quantity, inventoryPart.color.id) }} + {{ inventoryPart.color.id }} {% endfor %}
{% endif %} @@ -19,7 +20,7 @@
{% for inventoryPart in missing %} - {{ blocks.part(inventoryPart.part,inventoryPart.quantity, inventoryPart.color) }} + {{ blocks.part(inventoryPart.part.number,inventoryPart.quantity, inventoryPart.color.id) }} {% endfor %}
{% endif %} @@ -30,7 +31,7 @@
{% for inventoryPart in spareParts %} - {{ blocks.part(inventoryPart.part,inventoryPart.quantity, inventoryPart.color) }} + {{ blocks.part(inventoryPart.part.number,inventoryPart.quantity, inventoryPart.color.id) }} {% endfor %}
{% endif %} diff --git a/app/Resources/views/set/tabs/models.html.twig b/app/Resources/views/set/tabs/models.html.twig index 9ad9408..8ded142 100644 --- a/app/Resources/views/set/tabs/models.html.twig +++ b/app/Resources/views/set/tabs/models.html.twig @@ -1,13 +1,14 @@ {% extends 'ajax.html.twig' %} +{% import 'macros/blocks.html.twig' as blocks %} + {% block content %} -

{{ 'set.models.text' | trans | nl2br }}

{% if models|length > 0 %}

- Regular parts + Models

-
+
{% for model in models %} @@ -17,44 +18,43 @@
{% endif %} - {% if missing|length > 0 %}

- Missing regular models + Missing models

-
+
- {% for inventoryPart in missing %} - {{ blocks.part(inventoryPart.part,inventoryPart.quantity) }} + {% for part in missing %} + {{ blocks.part(part['part'],part['quantity']) }} {% endfor %}
{% endif %} - {% if spareModels|length > 0 %} -

- Spare parts -

+ {#{% if spareModels|length > 0 %}#} + {#

#} + {#Spare parts#} + {#

#} -
-
- {% for model in spareModels %} - {{ blocks.model(model['model'],model['quantity']) }} - {% endfor %} -
-
- {% endif %} + {#
#} + {#
#} + {#{% for model in spareModels %}#} + {#{{ blocks.model(model['model'],model['quantity']) }}#} + {#{% endfor %}#} + {#
#} + {#
#} + {#{% endif %}#} - {% if missingSpare|length > 0 %} -

- Missing spare models -

-
-
- {% for inventoryPart in missingSpare %} - {{ blocks.part(inventoryPart.part,inventoryPart.quantity) }} - {% endfor %} -
-
- {% endif %} + {#{% if missingSpare|length > 0 %}#} + {#

#} + {#Missing spare models#} + {#

#} + {#
#} + {#
#} + {#{% for inventoryPart in missingSpare %}#} + {#{{ blocks.part(inventoryPart.part.number,inventoryPart.quantity, inventoryPart.color.id) }}#} + {#{% endfor %}#} + {#
#} + {#
#} + {#{% endif %}#} {% endblock %} \ No newline at end of file diff --git a/app/Resources/views/set/tabs/sets.html.twig b/app/Resources/views/set/tabs/sets.html.twig index d5962d3..3752342 100644 --- a/app/Resources/views/set/tabs/sets.html.twig +++ b/app/Resources/views/set/tabs/sets.html.twig @@ -1,20 +1,20 @@ {% extends 'ajax.html.twig' %} {% block content %} -

Sets

-
-
- {% for set in inventorySets %} - - {% endfor %} + {% if inventorySets %} +

Sets

+ -
+ {% endif %} {% endblock %} \ No newline at end of file diff --git a/app/config/config.yml b/app/config/config.yml index 4a83fc1..3418fe3 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -111,9 +111,6 @@ knp_paginator: liip_imagine: loaders: - media: - flysystem: - filesystem_service: oneup_flysystem.media_filesystem rebrickable: stream: wrapper: 'http://rebrickable.com/media/' @@ -121,49 +118,11 @@ liip_imagine: stream: wrapper: 'https://images.brickset.com/' - resolvers: default: web_path: ~ filter_sets: - media: - data_loader: media - media_min: - data_loader: media - cache: ~ - quality: 80 - filters: - thumbnail: { size: [200, 200], mode: inset } - background: { size: [250, 250], position: center, color: '#FFFFFF' } - media_large: - data_loader: media - cache: ~ - quality: 92 - filters: - thumbnail: { size: [840, 580], mode: inset } - background: { size: [900, 600], position: center, color: '#FFFFFF' } - - rebrickable: - data_loader: rebrickable - rebrickable_min: - quality: 80 - data_loader: rebrickable - default_image: '/resources/images/unknown_image.png' - cache: ~ - filters: - thumbnail: { size: [200, 200], mode: inset } - background: { size: [250, 250], position: center, color: '#FFFFFF' } - rebrickable_large: - data_loader: rebrickable - cache: ~ - quality: 92 - filters: - thumbnail: { size: [840, 580], mode: inset } - background: { size: [900, 600], position: center, color: '#FFFFFF' } - - brickset: - data_loader: brickset brickset_large: data_loader: brickset cache: ~ @@ -171,6 +130,37 @@ liip_imagine: filters: thumbnail: { size: [840, 580], mode: inset } background: { size: [900, 600], position: center, color: '#FFFFFF' } + + + set_min: + quality: 80 + data_loader: rebrickable + cache: ~ + filters: + thumbnail: { size: [200, 200], mode: inset } + background: { size: [250, 250], position: center, color: '#FFFFFF' } + set_large: + data_loader: rebrickable + cache: ~ + quality: 92 + filters: + thumbnail: { size: [840, 580], mode: inset } + background: { size: [900, 600], position: center, color: '#FFFFFF' } + part_large: + data_loader: part_image_loader + cache: ~ + quality: 92 + filters: + thumbnail: { size: [840, 580], mode: inset } + background: { size: [900, 600], position: center, color: '#FFFFFF' } + part_min: + quality: 80 + data_loader: part_image_loader + default_image: "noimage.png" + cache: ~ + filters: + thumbnail: { size: [200, 200], mode: inset } + background: { size: [250, 250], position: center, color: '#FFFFFF' } oneup_flysystem: adapters: media: diff --git a/app/config/service/service.yml b/app/config/service/service.yml index 3acdaf9..09e611f 100644 --- a/app/config/service/service.yml +++ b/app/config/service/service.yml @@ -17,4 +17,9 @@ services: service.set: class: AppBundle\Service\SetService - arguments: ['@repository.rebrickable.inventorypart'] \ No newline at end of file + arguments: ['@repository.rebrickable.inventorypart'] arguments: ['@repository.rebrickable.inventorypart'] app.part_image_loader: + app.part_image_loader: + class: AppBundle\Imagine\PartImageLoader + arguments: ['@api.manager.rebrickable', '@oneup_flysystem.media_filesystem'] + tags: + - { name: liip_imagine.binary.loader, loader: part_image_loader } \ No newline at end of file diff --git a/src/AppBundle/Imagine/PartImageLoader.php b/src/AppBundle/Imagine/PartImageLoader.php new file mode 100644 index 0000000..2910079 --- /dev/null +++ b/src/AppBundle/Imagine/PartImageLoader.php @@ -0,0 +1,79 @@ +mediaFilesystem = $mediaFilesystem; + $this->rebrickableManager = $rebrickableManager; + } + + public function find($path) + { + // try to load image from local mediaFilesystem + if ($this->mediaFilesystem->has('/images/'.$path)) { + return $this->mediaFilesystem->read('/images/'.$path); + } + + // try to load image from rebrickable website + try { + if ($this->remoteFileExists($this->rebrickableContext.$path)) { + return file_get_contents($this->rebrickableContext.$path); + } + } catch (\Exception $e) { + throw new NotLoadableException(sprintf('Source image %s could not be loaded.', $path), $e->getCode(), $e); + } + + // Load part entity form rebrickable api and get image path from response + try { + if (preg_match('/^(.*)\/(.*).png$/', $path, $match)) { + $part = $this->rebrickableManager->getPart($match[2]); + + if ($part && $part->getImgUrl()) { + return file_get_contents($part->getImgUrl()); + } + } + } catch (\Exception $e) { + throw new NotLoadableException(sprintf('Source image %s could not be loaded.', $path), $e->getCode(), $e); + } + + return $this->mediaFilesystem->read('noimage.png'); + } + + /** + * @param string $url + * + * @return bool + */ + public function remoteFileExists($url) + { + $resource = curl_init($url); + curl_setopt($resource, CURLOPT_NOBODY, true); + curl_exec($resource); + $status = curl_getinfo($resource, CURLINFO_HTTP_CODE); + curl_close($resource); + + return $status === 200 ? true : false; + } +} diff --git a/src/AppBundle/Twig/AppExtension.php b/src/AppBundle/Twig/AppExtension.php index 43e534d..f3b0542 100644 --- a/src/AppBundle/Twig/AppExtension.php +++ b/src/AppBundle/Twig/AppExtension.php @@ -2,33 +2,26 @@ namespace AppBundle\Twig; -use AppBundle\Api\Manager\RebrickableManager; use AppBundle\Transformer\FormatTransformer; class AppExtension extends \Twig_Extension { - /** @var RebrickableManager */ - private $rebrickableAPIManager; - /** @var FormatTransformer */ private $formatTransformer; /** * AppExtension constructor. * - * @param RebrickableManager $rebrickableAPIManager + * @param FormatTransformer $formatTransformer */ - public function __construct($rebrickableAPIManager, $formatTransformer) + public function __construct($formatTransformer) { - $this->rebrickableAPIManager = $rebrickableAPIManager; $this->formatTransformer = $formatTransformer; } public function getFilters() { return [ - new \Twig_SimpleFilter('partImage', [$this, 'partImage']), - new \Twig_SimpleFilter('setImage', [$this, 'setImage']), new \Twig_SimpleFilter('bytesToSize', [$this, 'bytesToSize']), ]; } @@ -38,23 +31,31 @@ class AppExtension extends \Twig_Extension return [ new \Twig_SimpleFunction('remoteSize', [$this, 'remoteSize']), new \Twig_SimpleFunction('remoteFilename', [$this, 'remoteFilename']), + new \Twig_SimpleFunction('remoteFileExists', [$this, 'remoteFileExists']), ]; } - public function partImage($number, $color = null) + public function bytesToSize($bytes, $precision = 2) { - return '/parts/ldraw/'.($color !== null ? $color : '-1').'/'.$number.'.png'; - } - - public function setImage($number) - { - return '/sets/'.strtolower($number).'.jpg'; - } - - public function bytesToSize($bytes, $precision = 2) { return $this->formatTransformer->bytesToSize($bytes, $precision); } + /** + * @param string $url + * + * @return bool + */ + public function remoteFileExists($url) + { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_NOBODY, true); + curl_exec($ch); + $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + return $status === 200 ? true : false; + } + public function remoteSize($url) { $ch = curl_init($url);