From e684d30c459d97e63f1b09026f84536d4cf6ff0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=BCbner?= Date: Fri, 5 May 2017 20:08:52 +0200 Subject: [PATCH] Add Imagine PartImage loader --- .gitignore | 1 + app/Resources/assets/images/noimage.png | Bin 0 -> 32095 bytes app/Resources/views/ajax.html.twig | 3 - .../views/brickset/description.html.twig | 3 + app/Resources/views/brickset/images.html.twig | 3 + .../views/brickset/instructions.html.twig | 3 + .../views/brickset/reviews.html.twig | 72 ++++++++----- app/Resources/views/macros/blocks.html.twig | 74 +++++++++---- app/Resources/views/model/detail.html.twig | 55 ++++++---- app/Resources/views/model/index.html.twig | 19 ++-- .../views/rebrickable/part/detail.html.twig | 17 +-- app/Resources/views/set/detail.html.twig | 101 +++++++++++------- app/Resources/views/set/index.html.twig | 14 +-- app/Resources/views/set/tabs/colors.html.twig | 2 +- .../views/set/tabs/inventory.html.twig | 7 +- app/Resources/views/set/tabs/models.html.twig | 64 +++++------ app/Resources/views/set/tabs/sets.html.twig | 30 +++--- app/config/config.yml | 72 ++++++------- app/config/service/service.yml | 7 +- src/AppBundle/Imagine/PartImageLoader.php | 79 ++++++++++++++ src/AppBundle/Twig/AppExtension.php | 39 +++---- 21 files changed, 417 insertions(+), 248 deletions(-) create mode 100644 app/Resources/assets/images/noimage.png create mode 100644 src/AppBundle/Imagine/PartImageLoader.php 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 0000000000000000000000000000000000000000..59d5601b0cbe74c23261ed798ec96fc64b7f514b GIT binary patch literal 32095 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

_b+~?9s4_)-M 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 literal 0 HcmV?d00001 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);