From 1da3d4e6882a5c3fdf5ab398eac2d90b9952c27c Mon Sep 17 00:00:00 2001
From: Dominik Hebeler <dominik@suma-ev.de>
Date: Tue, 5 Feb 2019 15:13:21 +0100
Subject: [PATCH] Added an image search to MetaGer

---
 app/Jobs/Searcher.php                         | 12 ++-
 app/MetaGer.php                               |  5 +-
 app/Models/Searchengine.php                   |  4 +-
 app/Models/parserSkripte/BingBilder.php       | 77 +++++++++++++++++++
 .../metager/pages/resultpage/result-page.less | 30 +++++++-
 resources/views/parts/foki.blade.php          |  5 ++
 .../resultpages/results_images.blade.php      | 12 +--
 7 files changed, 134 insertions(+), 11 deletions(-)
 create mode 100644 app/Models/parserSkripte/BingBilder.php

diff --git a/app/Jobs/Searcher.php b/app/Jobs/Searcher.php
index 2d5d73937..5605492bd 100644
--- a/app/Jobs/Searcher.php
+++ b/app/Jobs/Searcher.php
@@ -12,7 +12,7 @@ class Searcher implements ShouldQueue
 {
     use InteractsWithQueue, Queueable, SerializesModels;
 
-    protected $name, $ch, $pid, $counter, $lastTime, $connectionInfo, $user, $password;
+    protected $name, $ch, $pid, $counter, $lastTime, $connectionInfo, $user, $password, $headers;
     # Each Searcher will shutdown after a specified time(s) or number of requests
     protected $MAX_REQUESTS = 100;
     # This value should always be below the retry_after value in config/queue.php
@@ -32,7 +32,7 @@ class Searcher implements ShouldQueue
      * keep-alive requests.
      * @return void
      */
-    public function __construct($name, $user = null, $password = null)
+    public function __construct($name, $user = null, $password = null, $headers = null)
     {
         $this->name = $name;
         $this->pid = getmypid();
@@ -40,6 +40,7 @@ class Searcher implements ShouldQueue
         $this->startTime = microtime(true);
         $this->user = $user;
         $this->password = $password;
+        $this->headers = $headers;
         // Submit this worker to the Redis System
         Redis::expire($this->name, 5);
     }
@@ -201,6 +202,13 @@ class Searcher implements ShouldQueue
             curl_setopt($ch, CURLOPT_USERPWD, $this->user . ":" . $this->password);
         }
 
+        if ($this->headers !== null) {
+            # Headers are in the Form:
+            # <key>:<value>;<key>:<value>
+            $headerArray = explode(";", $this->headers);
+            curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArray);
+        }
+
         return $ch;
     }
 }
diff --git a/app/MetaGer.php b/app/MetaGer.php
index 73565382e..ad21554bc 100644
--- a/app/MetaGer.php
+++ b/app/MetaGer.php
@@ -131,7 +131,10 @@ class MetaGer
                         ->with('errors', $this->errors)
                         ->with('apiAuthorized', $this->apiAuthorized)
                         ->with('metager', $this)
-                        ->with('browser', (new Agent())->browser());
+                        ->with('browser', (new Agent())->browser())
+                        ->with('quicktips', $quicktipResults)
+                        ->with('focus', $this->fokus)
+                        ->with('resultcount', count($this->results));
             }
         } else {
             switch ($this->out) {
diff --git a/app/Models/Searchengine.php b/app/Models/Searchengine.php
index f3242cbc5..b6be56e91 100644
--- a/app/Models/Searchengine.php
+++ b/app/Models/Searchengine.php
@@ -34,6 +34,8 @@ abstract class Searchengine
     private $user; # Username für HTTP-Auth (falls angegeben)
     private $password; # Passwort für HTTP-Auth (falls angegeben)
 
+    private $headers; # Headers to add
+
     public $fp; # Wird für Artefakte benötigt
     public $socketNumber = null; # Wird für Artefakte benötigt
     public $counter = 0; # Wird eventuell für Artefakte benötigt
@@ -174,7 +176,7 @@ abstract class Searchengine
             }
             if ($needSearcher && Redis::get($this->name) !== "locked") {
                 Redis::set($this->name, "locked");
-                $this->dispatch(new Searcher($this->name, $this->user, $this->password));
+                $this->dispatch(new Searcher($this->name, $this->user, $this->password, $this->headers));
             }
         }
     }
diff --git a/app/Models/parserSkripte/BingBilder.php b/app/Models/parserSkripte/BingBilder.php
new file mode 100644
index 000000000..cd360e2fe
--- /dev/null
+++ b/app/Models/parserSkripte/BingBilder.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace app\Models\parserSkripte;
+
+use App\Models\Searchengine;
+use Log;
+
+class BingBilder extends Searchengine
+{
+    public $results = [];
+
+    public function __construct(\SimpleXMLElement $engine, \App\MetaGer $metager)
+    {
+        parent::__construct($engine, $metager);
+    }
+
+    public function loadResults($result)
+    {
+        try {
+            $results = json_decode($result);
+            $results = $results->value;
+
+            foreach ($results as $result) {
+                $title = $result->name;
+                $link = $result->hostPageUrl;
+                $anzeigeLink = $link;
+                $descr = "";
+                $image = $result->thumbnailUrl;
+                $this->counter++;
+                $this->results[] = new \App\Models\Result(
+                    $this->engine,
+                    $title,
+                    $link,
+                    $anzeigeLink,
+                    $descr,
+                    $this->displayName, $this->homepage,
+                    $this->counter,
+                    ['image' => $image]
+                );
+
+            }
+
+        } catch (\Exception $e) {
+            Log::error("A problem occurred parsing results from $this->name:");
+            Log::error($e->getMessage());
+            return;
+        }
+    }
+
+    public function getNext(\App\MetaGer $metager, $result)
+    {
+        try {
+            $results = json_decode($result);
+
+            $totalMatches = $results->totalEstimatedMatches;
+            $nextOffset = $results->nextOffset;
+
+            if ($nextOffset >= $totalMatches) {
+                return;
+            }
+
+            $next = new BingBilder(simplexml_load_string($this->engine), $metager);
+            if (\str_contains($next->getString, "&offset=")) {
+                $next->getString = preg_replace("/&offset=.*/si", "", $next->getString);
+            }
+            $next->getString .= "&offset=" . $nextOffset;
+            $next->hash = md5($next->host . $next->getString . $next->port . $next->name);
+            $this->next = $next;
+
+        } catch (\Exception $e) {
+            Log::error("A problem occurred parsing results from $this->name:");
+            Log::error($e->getMessage());
+            return;
+        }
+
+    }
+}
diff --git a/resources/less/metager/pages/resultpage/result-page.less b/resources/less/metager/pages/resultpage/result-page.less
index c4d35251b..233e37af6 100644
--- a/resources/less/metager/pages/resultpage/result-page.less
+++ b/resources/less/metager/pages/resultpage/result-page.less
@@ -208,9 +208,35 @@ a {
 }
 
 .image-container {
-    margin: 10px auto;
     display: flex;
-    flex-wrap: wrap;
+    flex-flow: row wrap;
+    > div.image {
+        flex: auto;
+        margin: 8px;
+        text-align: center;
+        > a > div {
+            display: inline-block;
+            background-color: white;
+            text-align: center;
+            box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
+            > img {
+                max-width: 150px;
+            }
+            @media(max-width: 370px){
+                > img {
+                    max-width: 100%;
+                }
+            }
+            > div {
+                font-size: 12px;
+                color: #777;
+            }
+        }
+
+        > a > div:hover {
+            box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22);
+        }
+    }
 }
 
 /* Placeholder für ladende Tabs */
diff --git a/resources/views/parts/foki.blade.php b/resources/views/parts/foki.blade.php
index 468bd57ab..ea799c11d 100644
--- a/resources/views/parts/foki.blade.php
+++ b/resources/views/parts/foki.blade.php
@@ -3,6 +3,11 @@
 	<a href="@if($metager->getFokus() === "web")#@else{!!$metager->generateSearchLink('web')!!}@endif" target="_self" tabindex="2">@lang('index.foki.web')</a>
 </div>
 @endif
+@if( array_has($metager->getAvailableFoki(), "bilder"))
+<div id="bilder" @if($metager->getFokus() === "bilder")class="active"@endif>
+	<a href="@if($metager->getFokus() === "bilder")#@else{!!$metager->generateSearchLink('bilder')!!}@endif" target="_self" tabindex="2">@lang('index.foki.bilder')</a>
+</div>
+@endif
 @if( array_has($metager->getAvailableFoki(), "nachrichten"))
 <div id="nachrichten" @if($metager->getFokus() === "nachrichten")class="active"@endif>
 	<a href="@if($metager->getFokus() === "nachrichten")#@else{!!$metager->generateSearchLink('nachrichten')!!}@endif" target="_self" tabindex="3">@lang('index.foki.nachrichten')</a>
diff --git a/resources/views/resultpages/results_images.blade.php b/resources/views/resultpages/results_images.blade.php
index ca7a2c7ee..2eedcf651 100644
--- a/resources/views/resultpages/results_images.blade.php
+++ b/resources/views/resultpages/results_images.blade.php
@@ -2,11 +2,13 @@
 @include('parts.warnings')
 <div id="container" class="image-container">
 	@foreach($metager->getResults() as $result)
-		<div class="item image-item">
-			<div class="img">
-				<a href="{{ $result->link }}" target="{{ $metager->getNewtab() }}"><img src="{{ $metager->getImageProxyLink($result->image) }}" width="150px" alt="" rel="noopener"/></a>
-				<span class="label label-default hostlabel">{{ strip_tags($result->gefVon) }}</span>
-			</div>
+		<div class="image">
+			<a href="{{ $result->link }}" target="_blank">
+				<div title="{{ $result->titel }}">
+					<img src="{{ $metager->getImageProxyLink($result->image)}}" alt="{{ $result->titel }}"/>
+					<div>{{ $result->gefVon }}</div>
+				</div>
+			</a>
 		</div>
 	@endforeach
 </div>
-- 
GitLab