diff --git a/app/Resources/assets/js/style.js b/app/Resources/assets/js/style.js index 900bb01..7976122 100644 --- a/app/Resources/assets/js/style.js +++ b/app/Resources/assets/js/style.js @@ -4,8 +4,7 @@ $(document).ready(function(){ // ; // $('.select2.dropdown').select2({ - // placeholder: 'Select theme', - // allowClear: true + // // }); $('.image.load img') diff --git a/app/Resources/assets/style/homepage.scss b/app/Resources/assets/style/homepage.scss index 833d050..32014d2 100644 --- a/app/Resources/assets/style/homepage.scss +++ b/app/Resources/assets/style/homepage.scss @@ -3,8 +3,8 @@ } .masthead.segment { - min-height: 500px; - padding: 1em 0em; + min-height: 400px; + padding: 0.5em 0; } .masthead .logo.item img { margin-right: 1em; @@ -14,7 +14,7 @@ } .masthead h1.ui.header { margin-top: 1.5em; - margin-bottom: 0em; + margin-bottom: 0; font-size: 4em; font-weight: normal; } diff --git a/app/Resources/assets/style/main.scss b/app/Resources/assets/style/main.scss index 43ebc4c..119d452 100644 --- a/app/Resources/assets/style/main.scss +++ b/app/Resources/assets/style/main.scss @@ -1,8 +1,11 @@ .default-theme { + display: flex; + min-height: 100vh; + flex-direction: column; .ui.main { + flex: 1; padding-top: 50px; - min-height: calc(100% - 67px); } .ui.head { @@ -111,6 +114,10 @@ padding: 0.5em 1em !important; } + .unpadded { + padding: 0; + } + .spacing.top { margin-top: 1em; } diff --git a/app/Resources/relations/alias_model.yml b/app/Resources/relations/alias_model.yml index da32ac0..1f8a1c8 100644 --- a/app/Resources/relations/alias_model.yml +++ b/app/Resources/relations/alias_model.yml @@ -365,7 +365,6 @@ u9140p02c01: u9140p01c01 u9142p02c03: u9142p01c08 -u9151p02c03: u9151p01c04 u9106p02c02: u9106p01c03 diff --git a/app/Resources/relations/part_model.yml b/app/Resources/relations/part_model.yml index 449f5c4..7833911 100644 --- a/app/Resources/relations/part_model.yml +++ b/app/Resources/relations/part_model.yml @@ -427,3 +427,4 @@ x222: 13459 3068bor0323: 3068b 62363: 3815c01 + diff --git a/app/Resources/translations/messages.en.yml b/app/Resources/translations/messages.en.yml index 223ff72..79934fe 100644 --- a/app/Resources/translations/messages.en.yml +++ b/app/Resources/translations/messages.en.yml @@ -29,6 +29,8 @@ set: theme.all: All Themes download: + unicolor.button: Download set (Uni-color) + multicolor.button: Download set (Multi-color) button: Download set title: Download - %set% sorted: @@ -71,15 +73,20 @@ filter: category: Category homepage: + title.text: Web catalogue of LEGO® parts for 3D printing models: - title: %count% models - text: + title: models + text: Browse 3D models of LEGO® parts from LDraw™ Library browse: Browse models sets: - title: %count% sets - text: + title: sets + text: Browse LEGO® sets from Rebrickable browse: Browse sets view.all: sets: View all matched sets - models: View all matched models \ No newline at end of file + models: View all matched models + +flash: + warning.title: Warning + error.title: Error \ No newline at end of file diff --git a/app/Resources/views/base.html.twig b/app/Resources/views/base.html.twig index 2d16e84..e33c215 100644 --- a/app/Resources/views/base.html.twig +++ b/app/Resources/views/base.html.twig @@ -53,7 +53,7 @@ diff --git a/app/Resources/views/default/index.html.twig b/app/Resources/views/default/index.html.twig index c9dd5bd..c67b644 100644 --- a/app/Resources/views/default/index.html.twig +++ b/app/Resources/views/default/index.html.twig @@ -1,8 +1,6 @@ {% extends 'base.html.twig' %} {% block body %} - -
@@ -25,27 +23,46 @@

PrintABrick

-

{{ 'page.home.title.text' | trans }}

+

{{ 'homepage.title.text' | trans | raw }}

-
+
-

{{ 'homepage.models.title' | trans({'%count%' : models})}}

-

{{ 'homepage.models.text' | trans }}

+
+
+ {{ models }} +
+
+ {{ 'homepage.models.title' | trans }} +
+
+

{{ 'homepage.models.text' | trans | raw }}

{{ 'homepage.models.browse' | trans }}
-

{{ 'homepage.sets.title' | trans({'%count%' : sets}) }}

-

{{ 'homepage.sets.text' | trans }}

+
+
+ {{ sets }} +
+
+ {{ 'homepage.sets.title' | trans }} +
+
+

{{ 'homepage.sets.text' | trans | raw }}

{{ 'homepage.sets.browse' | trans }}
-
+ + {% endblock %} diff --git a/app/Resources/views/html.html.twig b/app/Resources/views/html.html.twig index 9a06548..3bbec57 100644 --- a/app/Resources/views/html.html.twig +++ b/app/Resources/views/html.html.twig @@ -9,7 +9,7 @@ {% endblock %} - + {% block body %} {% endblock %} diff --git a/app/Resources/views/macros/blocks.html.twig b/app/Resources/views/macros/blocks.html.twig index 25de935..06e3111 100644 --- a/app/Resources/views/macros/blocks.html.twig +++ b/app/Resources/views/macros/blocks.html.twig @@ -79,6 +79,11 @@
+ {% if quantity %} +
+
{{ quantity }}x
+
+ {% endif %} {% endmacro %} diff --git a/app/Resources/views/model/detail.html.twig b/app/Resources/views/model/detail.html.twig index e4c1c76..27a1467 100644 --- a/app/Resources/views/model/detail.html.twig +++ b/app/Resources/views/model/detail.html.twig @@ -60,58 +60,43 @@ {{ 'model.download'|trans }} - -
- -
+
- - {#
#} - {#

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

#} - {#
#} - -
- - -
- {{ render(path('set_models', {id: set.id})) }} -
-
- {{ render(path('set_colors', {id: set.id})) }} -
- {{ render(path('set_sets', {id: set.id})) }} +
+ {{ render(path('set_inventory', {id: set.id})) }}
{% if brset %} diff --git a/app/Resources/views/set/tabs/colors.html.twig b/app/Resources/views/set/tabs/colors.html.twig index 0e31dd7..d17d4f9 100644 --- a/app/Resources/views/set/tabs/colors.html.twig +++ b/app/Resources/views/set/tabs/colors.html.twig @@ -8,6 +8,10 @@ {% endfor %} {% endfor %} + + {% if colors|length > 0 %}

Models

diff --git a/app/Resources/views/set/tabs/inventory.html.twig b/app/Resources/views/set/tabs/inventory.html.twig index d42d63f..33b80d9 100644 --- a/app/Resources/views/set/tabs/inventory.html.twig +++ b/app/Resources/views/set/tabs/inventory.html.twig @@ -8,38 +8,36 @@ {% endfor %} {% endfor %} - {% if regularParts|length > 0 %} -

- Regular parts -

- -
- {% for inventoryPart in regularParts %} - {{ blocks.part(inventoryPart.part.id,inventoryPart.quantity, inventoryPart.color.id) }} - {{ inventoryPart.color.id }} + {% if inventorySets %} +

Sets

+
+ {% for inventorySet in inventorySets %} + {{ blocks.set(inventorySet.set, inventorySet.quantity) }} {% endfor %}
- {% endif %} + {% else %} + {% if missingCount > 0 %} +
+ +
+
+ {{ 'set.download.warning.title' | trans }} +
+

{{ 'set.download.warning.text' | trans({'%percent%':((1 - missingCount/partCount)*100)|number_format(2, '.', ','), '%total%':partCount, '%missing%': missingCount, '%unique%': missing|length}) | raw | nl2br }}

+
+
+ {% endif %} - {% if missing|length > 0 %} -

- Missing models -

-
- {% for inventoryPart in missing %} - {{ blocks.part(inventoryPart.part.id,inventoryPart.quantity, inventoryPart.color.id) }} - {% endfor %} + - {% endif %} - {% if spareParts|length > 0 %} -

- Spare parts -

-
- {% for inventoryPart in spareParts %} - {{ blocks.part(inventoryPart.part.id,inventoryPart.quantity, inventoryPart.color.id) }} - {% endfor %} +
+ {{ render(path('set_models', {id: set.id})) }} +
+
+ {{ render(path('set_colors', {id: set.id})) }}
{% endif %} {% endblock %} \ No newline at end of file diff --git a/app/Resources/views/set/tabs/models.html.twig b/app/Resources/views/set/tabs/models.html.twig index d241165..13a40a3 100644 --- a/app/Resources/views/set/tabs/models.html.twig +++ b/app/Resources/views/set/tabs/models.html.twig @@ -8,6 +8,10 @@ {% endfor %} {% endfor %} + + {% if models|length > 0 %}

Models

diff --git a/app/Resources/views/set/tabs/sets.html.twig b/app/Resources/views/set/tabs/sets.html.twig deleted file mode 100644 index 5312a70..0000000 --- a/app/Resources/views/set/tabs/sets.html.twig +++ /dev/null @@ -1,21 +0,0 @@ -{% import 'macros/blocks.html.twig' as blocks %} -{% import 'macros/elements.html.twig' as elements %} - -{% block content %} - {% for label, flashes in app.session.flashbag.all %} - {% for flash in flashes %} - {{ elements.flash(label,flash) }} - {% endfor %} - {% endfor %} - - {% if inventorySets %} -

Sets

-
-
- {% for inventorySet in inventorySets %} - {{ blocks.set(inventorySet.set, inventorySet.quantity) }} - {% endfor %} -
-
- {% endif %} -{% endblock %} \ No newline at end of file diff --git a/src/AppBundle/Controller/ModelController.php b/src/AppBundle/Controller/ModelController.php index bc487b5..5cd0213 100644 --- a/src/AppBundle/Controller/ModelController.php +++ b/src/AppBundle/Controller/ModelController.php @@ -93,10 +93,13 @@ class ModelController extends Controller $response = new BinaryFileResponse($zip); $response->headers->set('Content-Type', 'application/zip'); + // escape forbidden characters from filename + $filename = preg_replace('/[^a-z0-9\.]/i', '_',"model_{$model->getId()}_{$model->getName()}.zip"); + // Create the disposition of the file $disposition = $response->headers->makeDisposition( ResponseHeaderBag::DISPOSITION_ATTACHMENT, - "model_{$model->getId()}_{$model->getName()}.zip" + $filename ); $response->headers->set('Content-Disposition', $disposition); diff --git a/src/AppBundle/Controller/SearchController.php b/src/AppBundle/Controller/SearchController.php index 3c2ce27..7181b62 100644 --- a/src/AppBundle/Controller/SearchController.php +++ b/src/AppBundle/Controller/SearchController.php @@ -4,6 +4,8 @@ namespace AppBundle\Controller; use AppBundle\Entity\LDraw\Model; use AppBundle\Entity\Rebrickable\Set; +use AppBundle\Model\ModelSearch; +use AppBundle\Model\SetSearch; use AppBundle\Repository\Search\ModelRepository; use AppBundle\Repository\Search\SetRepository; use FOS\ElasticaBundle\HybridResult; @@ -34,12 +36,11 @@ class SearchController extends Controller /** @var SetRepository $setRepository */ $setRepository = $repositoryManager->getRepository(Set::class); - /** @var Repository $modelRepository */ + /** @var ModelRepository $modelRepository */ $modelRepository = $repositoryManager->getRepository(Model::class); - // Option 1. Returns all users who have example.net in any of their mapped fields - $setsResult = $setRepository->find($query, 8); - $modelResult = $modelRepository->find($query, 8); + $setsResult = $setRepository->search(new SetSearch($query), 1000); + $modelResult = $modelRepository->search(new ModelSearch($query), 1000); return $this->render('search/index.html.twig', [ 'sets' => $setsResult, @@ -67,8 +68,8 @@ class SearchController extends Controller $modelRepository = $repositoryManager->getRepository(Model::class); // Option 1. Returns all users who have example.net in any of their mapped fields - $setsResult = $setRepository->findHighlighted($query, 5); - $modelResult = $modelRepository->findHighlighted($query, 5); + $setsResult = $setRepository->findHighlighted($query, 4); + $modelResult = $modelRepository->findHighlighted($query, 4); $models = []; /** @var HybridResult $model */ diff --git a/src/AppBundle/Controller/SetController.php b/src/AppBundle/Controller/SetController.php index 7f3c53d..bdbadd8 100644 --- a/src/AppBundle/Controller/SetController.php +++ b/src/AppBundle/Controller/SetController.php @@ -61,8 +61,6 @@ class SetController extends Controller $bricksetSet = null; $partCount = $inventoryPartRepository->getPartCount($set, false); - $missingCount = $inventoryPartRepository->getPartCount($set, false, false); - $uniqueMissing = $setService->getParts($set, false, false); try { if (!($bricksetSet = $this->get('api.manager.brickset')->getSetByNumber($set->getId()))) { @@ -78,27 +76,33 @@ class SetController extends Controller 'set' => $set, 'brset' => $bricksetSet, 'partCount' => $partCount, - 'missingCount' => $missingCount, - 'uniqueMissing' => $uniqueMissing, ]); } /** - * @Route("/{id}/parts", name="set_parts") + * @Route("/{id}/inventory", name="set_inventory") */ - public function partsAction(Request $request, Set $set) + public function inventoryAction(Request $request, Set $set) { - $inventoryPartRepository = $this->get('repository.rebrickable.inventorypart'); + $em = $this->getDoctrine()->getManager(); - $regularParts = $inventoryPartRepository->findAllBySetNumber($set->getId(), false, true); - $spareParts = $inventoryPartRepository->findAllBySetNumber($set->getId(), true); + $inventorySets = $em->getRepository(Inventory_Set::class)->findAllBySetNumber($set->getId()); + $setService = $this->get('service.set'); + $inventoryPartRepository = $this->get('repository.rebrickable.inventoryPart'); - $missing = $inventoryPartRepository->findAllBySetNumber($set->getId(), false, false); + + $models = $setService->getModels($set, false); + $missing = $setService->getParts($set, false, false); + $missingCount = $inventoryPartRepository->getPartCount($set, false, false); + $partCount = $inventoryPartRepository->getPartCount($set, false); $template = $this->render('set/tabs/inventory.html.twig', [ - 'regularParts' => $regularParts, + 'inventorySets' => $inventorySets, + 'set' => $set, 'missing' => $missing, - 'spareParts' => $spareParts, + 'models' => $models, + 'missingCount' => $missingCount, + 'partCount' => $partCount, ]); if ($request->isXmlHttpRequest()) { @@ -117,16 +121,14 @@ class SetController extends Controller */ public function modelsAction(Request $request, Set $set) { + $setService = $this->get('service.set'); + $models = null; - $spareModels = null; $missing = null; - $missingSpare = null; try { - $models = $this->get('service.set')->getModels($set, false); - $spareModels = $this->get('service.set')->getModels($set, true); - $missing = $this->get('service.set')->getParts($set, false, false); - $missingSpare = $this->get('repository.rebrickable.inventorypart')->findAllBySetNumber($set->getId(), true, false); + $models = $setService->getModels($set, false); + $missing = $setService->getParts($set, false, false); } catch (\Exception $e) { $this->addFlash('error', $e->getMessage()); } @@ -135,8 +137,6 @@ class SetController extends Controller 'set' => $set, 'missing' => $missing, 'models' => $models, - 'spareModels' => $spareModels, - 'missingSpare' => $missingSpare, ]); if ($request->isXmlHttpRequest()) { @@ -155,14 +155,15 @@ class SetController extends Controller */ public function colorsAction(Request $request, Set $set) { + /** @var SetService $setService */ + $setService = $this->get('service.set'); + $colors = null; + $missing = null; try { - /** @var SetService $setService */ - $setService = $this->get('service.set'); $colors = $setService->getModelsGroupedByColor($set, false); $missing = $setService->getParts($set,false,false); - } catch (\Exception $e) { $this->addFlash('error', $e->getMessage()); } @@ -184,30 +185,6 @@ class SetController extends Controller return $template; } - /** - * @Route("/{id}/sets", name="set_sets") - */ - public function setsAction(Request $request, Set $set) - { - $em = $this->getDoctrine()->getManager(); - - $inventorySets = $em->getRepository(Inventory_Set::class)->findAllBySetNumber($set->getId()); - - $template = $this->render('set/tabs/sets.html.twig', [ - 'inventorySets' => $inventorySets, - ]); - - if ($request->isXmlHttpRequest()) { - $json = json_encode($template->getContent()); - $response = new Response($json, 200); - $response->headers->set('Content-Type', 'application/json'); - - return $response; - } - - return $template; - } - /** * @Route("/{id}/zip", name="set_zip") */ @@ -222,10 +199,13 @@ class SetController extends Controller $response = new BinaryFileResponse($zip); $response->headers->set('Content-Type', 'application/zip'); + // escape forbidden characters from filename + $filename = preg_replace('/[^a-z0-9\.]/i', '_', "set_{$set->getId()}_{$set->getName()}({$sort}).zip"); + // Create the disposition of the file $disposition = $response->headers->makeDisposition( ResponseHeaderBag::DISPOSITION_ATTACHMENT, - "set_{$set->getId()}_{$set->getName()}({$sort}).zip" + $filename ); $response->headers->set('Content-Disposition', $disposition); diff --git a/src/AppBundle/Exception/Loader/MissingContextException.php b/src/AppBundle/Exception/Loader/MissingContextException.php new file mode 100644 index 0000000..16a3cf0 --- /dev/null +++ b/src/AppBundle/Exception/Loader/MissingContextException.php @@ -0,0 +1,16 @@ +