diff --git a/app/Http/Controllers/MetaGerSearch.php b/app/Http/Controllers/MetaGerSearch.php index 79213bfe430b25aaf039b8211d4f03314fae0cf7..5baa8820dace04957012061fe27de8230c3243bb 100644 --- a/app/Http/Controllers/MetaGerSearch.php +++ b/app/Http/Controllers/MetaGerSearch.php @@ -24,7 +24,9 @@ class MetaGerSearch extends Controller $metager->checkSpecialSearches($request); } # Alle Suchmaschinen erstellen + $metager->createSearchEngines($request); + # Ergebnisse der Suchmaschinen kombinieren: $metager->combineResults(); # Die Ausgabe erstellen: diff --git a/app/MetaGer.php b/app/MetaGer.php index 2131b3028f314db3004b0851eed244be842523a5..87fadc046ad38c3c08d420e74b227f0db0f35913 100644 --- a/app/MetaGer.php +++ b/app/MetaGer.php @@ -3,6 +3,7 @@ namespace App; use Illuminate\Http\Request; use Jenssegers\Agent\Agent; +use App\Models\SocketRocket; use App; use Storage; use Log; @@ -41,6 +42,8 @@ class MetaGer function __construct() { + define('CRLF', "\r\n"); + define('BUFFER_LENGTH', 8192); if( file_exists(config_path() . "/blacklistDomains.txt") && file_exists(config_path() . "/blacklistUrl.txt") ) { # Blacklists einlesen: @@ -79,8 +82,9 @@ class MetaGer public function createSearchEngines (Request $request) { - # Curl-Multihandle um die Ergebnisse abzurufen: - $mh = curl_multi_init(); + + #die(SocketRocket::get("tls", "dominik-pfennig.de", "", 443)); + # Überprüfe, welche Sumas eingeschaltet sind $xml = simplexml_load_file($this->sumaFile); @@ -93,12 +97,13 @@ class MetaGer foreach($sumas as $suma) { if($request->has($suma["service"]) - || ( $this->fokus !== "bilder" - && ($suma["name"]->__toString() === "qualigo" - || $suma["name"]->__toString() === "similar_product_ads" - || ( !$overtureEnabled && $suma["name"]->__toString() === "overtureAds" ) - ) - ) + #|| ( $this->fokus !== "bilder" + # && ($suma["name"]->__toString() === "qualigo" + # || $suma["name"]->__toString() === "similar_product_ads" + # || ( !$overtureEnabled && $suma["name"]->__toString() === "overtureAds" ) + # ) + # ) + #|| 1 === 1 #Todo: entfernen ){ if(!(isset($suma['disabled']) && $suma['disabled']->__toString() === "1")) @@ -107,6 +112,7 @@ class MetaGer { $overtureEnabled = TRUE; } + $enabledSearchengines[] = $suma; } } @@ -116,12 +122,12 @@ class MetaGer foreach($sumas as $suma){ $types = explode(",",$suma["type"]); if(in_array($this->fokus, $types) - || ( $this->fokus !== "bilder" - && ($suma["name"]->__toString() === "qualigo" - || $suma["name"]->__toString() === "similar_product_ads" - || ( !$overtureEnabled && $suma["name"]->__toString() === "overtureAds" ) - ) - ) + #|| ( $this->fokus !== "bilder" + # && ($suma["name"]->__toString() === "qualigo" + # || $suma["name"]->__toString() === "similar_product_ads" + # || ( !$overtureEnabled && $suma["name"]->__toString() === "overtureAds" ) + # ) + # ) ){ if(!(isset($suma['disabled']) && $suma['disabled']->__toString() === "1")) { @@ -142,37 +148,22 @@ class MetaGer $engines = []; foreach($enabledSearchengines as $engine){ - $path = "App\Models\parserSkripte\\" . ucfirst($engine["name"]->__toString()); - $tmp = new $path($engine, $mh, $this); - if($tmp) + + $path = "App\Models\parserSkripte\\" . ucfirst($engine["package"]->__toString()); + + $tmp = new $path($engine, $this); + + if($tmp->isEnabled()) { $engines[] = $tmp; } } - - # Nun führen wir die Get-Requests aus und warten auf alle Ergebnisse: - $running = null; - do - { - curL_multi_exec($mh, $running); - }while($running); - - # Und beenden noch alle Handles - foreach($engines as $engine) - { - $engine->removeCurlHandle($mh); - $engine->loadResults(); - } - - # Und auch den Multicurl-Handle: - curl_multi_close($mh); - $string = ["Curl-Timings:"]; foreach($engines as $engine) { - $string[] = $engine->getCurlInfo(); + $engine->retrieveResults(); + # $engine->closeFp(); } - Log::debug($string); - + $this->engines = $engines; } @@ -396,4 +387,9 @@ class MetaGer { return $this->category; } + + public function getSumaFile () + { + return $this->sumaFile; + } } \ No newline at end of file diff --git a/app/Models/Result.php b/app/Models/Result.php index f05e1a31508552e93d9d266d333e35e74d88c009..d667fd367d19cf8d3515019191e5e1e1be4ffbf9 100644 --- a/app/Models/Result.php +++ b/app/Models/Result.php @@ -7,10 +7,11 @@ class Result function __construct ( $titel, $link, $anzeigeLink , $descr, $gefVon ) { - $this->titel = trim($titel); + $this->titel = strip_tags(trim($titel)); $this->link = trim($link); $this->anzeigeLink = trim($anzeigeLink); - $this->descr = trim($descr); + $this->descr = strip_tags(trim($descr)); + $this->descr = preg_replace("/\n+/si", " ", $this->descr); $this->gefVon = trim($gefVon); } } \ No newline at end of file diff --git a/app/Models/Search.php b/app/Models/Search.php index c3efb159898399dd4968507df0129d40102b15ad..343f0ac9ec15f610f9224780f9c614751cc0724c 100644 --- a/app/Models/Search.php +++ b/app/Models/Search.php @@ -6,6 +6,7 @@ use App\MetaGer\Searchengine; class Search { + public static function loadSearchEngines(Request $request) { diff --git a/app/Models/Searchengine.php b/app/Models/Searchengine.php index 92e8ea6c456a66b0d2c4e1d8b78f0c5c0af48c53..376bf11fb9d94159256fe49c81d1cad868ab4edc 100644 --- a/app/Models/Searchengine.php +++ b/app/Models/Searchengine.php @@ -2,18 +2,39 @@ namespace App\Models; use App\MetaGer; +use Log; +use Redis; abstract class Searchengine { protected $ch; # Curl Handle zum erhalten der Ergebnisse + protected $fp; + protected $getString = ""; + protected $engine; + protected $counter = 0; + protected $socketNumber = null; + protected $enabled = true; public $results = []; - function __construct(\SimpleXMLElement $engine, $mh, MetaGer $metager) + function __construct(\SimpleXMLElement $engine, MetaGer $metager) { + foreach($engine->attributes() as $key => $value){ $this->$key = $value->__toString(); } + + # Eine Suchmaschine kann automatisch temporär deaktiviert werden, wenn es Verbindungsprobleme gab: + if(isset($this->disabled) && strtotime($this->disabled) <= time() ) + { + # In diesem Fall ist der Timeout der Suchmaschine abgelaufen. + $this->enable($metager->getSumaFile(), "Die Suchmaschine " . $this->name . " wurde wieder eingeschaltet."); + }elseif (isset($this->disabled) && strtotime($this->disabled) > time()) + { + $this->enabled = false; + return; + } + # User-Agent definieren: if( isset($_SERVER['HTTP_USER_AGENT'])) { @@ -23,32 +44,339 @@ abstract class Searchengine $this->useragent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1"; } $this->ip = $metager->getIp(); - $this->ch = curl_init($this->generateGetString($metager->getEingabe(), $metager->getUrl(), $metager->getLanguage(), $metager->getCategory()) ); - curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($this->ch, CURLOPT_USERAGENT, $this->useragent); // set browser/user agent - curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, 1); // automatically follow Location: headers (ie redirects) - curl_setopt($this->ch, CURLOPT_AUTOREFERER, 1); // auto set the referer in the event of a redirect - curl_setopt($this->ch, CURLOPT_MAXREDIRS, 5); // make sure we dont get stuck in a loop - curl_setopt($this->ch, CURLOPT_CONNECTTIMEOUT , $metager->getTime()); - curl_setopt($this->ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 ); - curl_setopt($this->ch, CURLOPT_TIMEOUT, 1); // 10s timeout time for cURL connection - if($this->port ==="443") + $this->gefVon = "<a href=\"" . $this->homepage . "\" target=\"_blank\">" . $this->displayName . "</a>"; + $this->startTime = microtime(); + $this->getString = $this->generateGetString($metager->getEingabe(), $metager->getUrl(), $metager->getLanguage(), $metager->getCategory()); + $counter = 0; + + # Wir benötigen einen verfügbaren Socket, über den wir kommunizieren können: + $this->fp = $this->getFreeSocket(); + + do + { + #try + #{ + + + /*}catch(\Exception $e) + { + # Connection Timeout: Wir schalten die Suchmaschine aus: + $this->disable($metager->getSumaFile(), "Die Suchmaschine " . $this->name . " wurde deaktiviert, weil keine Verbindung aufgebaut werden konnte"); + break; + }*/ + if(!$this->fp) + { + // Mache etwas + $this->disable($metager->getSumaFile(), "Die Suchmaschine " . $this->name . " wurde für 1h deaktiviert, weil keine Verbindung aufgebaut werden konnte"); + break; + }else + { + $out = "GET " . $this->getString . " HTTP/1.1\r\n"; + $out .= "Host: " . $this->host . "\r\n"; + $out .= "User-Agent: " . $this->useragent . "\r\n"; + $out .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"; + $out .= "Accept-Language: de,en-US;q=0.7,en;q=0.3\r\n"; + $out .= "Accept-Encoding: gzip, deflate, br\r\n"; + $out .= "Connection: keep-alive\r\n\r\n"; + // Daten senden: + #try + #{ + if(fwrite($this->fp, $out)) + { + break; + }else + { + abort(500, "Fehler beim schreiben."); + } + + #}catch(\Exception $e) + #{ + # fclose($this->fp); + # if($counter >= 5) + # { +# + # Log::error("Konnte auch nach 6 Versuchen keine schreibbare Verbindung zum Server \"" . $this->host . "\" aufbauen."); + # break; + # } + #} + } + }while(true); + } + + public abstract function loadResults(String $result); + + private function getFreeSocket() + { + # Je nach Auslastung des Servers ( gleichzeitige Abfragen ), kann es sein, dass wir mehrere Sockets benötigen um die Abfragen ohne Wartezeit beantworten zu können. + # pfsockopen öffnet dabei einen persistenten Socket, der also auch zwischen den verschiedenen php Prozessen geteilt werden kann. + # Wenn der Hostname mit einem bereits erstellten Socket übereinstimmt, wird die Verbindung also aufgegriffen und fortgeführt. + # Allerdings dürfen wir diesen nur verwenden, wenn er nicht bereits von einem anderen Prozess zur Kommunikation verwendet wird. + # Wenn dem so ist, probieren wir den nächsten Socket zu verwenden. + # Dies festzustellen ist komplizierter, als man sich das vorstellt. Folgendes System sollte funktionieren: + # 1. Stelle fest, ob dieser Socket neu erstellt wurde, oder ob ein existierender geöffnet wurde. + + $counter = 0; + do + { + if(intval(Redis::getBit($this->name, $counter)) === 0) + { + + #die($counter . "Socket."); + Redis::setBit($this->name, $counter, 1); + $this->socketNumber = $counter; + + try + { + $fp = pfsockopen($this->getHost() . ":" . $this->port . "/$counter", $this->port, $errstr, $errno, 1); + return $fp; + }catch(\ErrorException $e) + { + return null; + } + + + } + $counter++; + }while($counter < 20); + + # Wenn wir hier hin kommen, läuft etwas falsch! Wir kappen alle bestehenden Verbindungen: + $connectionError = false; + for($i = 0; $i < 20; $i++) + { + if(!$connectionError) + { + try + { + $fp = pfsockopen($this->getHost() . ":" . $this->port . "/$counter", $this->port, $errstr, $errno, 1); + fclose($fp); + }catch(\ErrorException $e) + { + $connectionError = true; + } + } + + Redis::setBit($this->name, $i, 0); + + } + + return null; + } + + public function disable(string $sumaFile, string $message) + { + Log::info($message); + $xml = simplexml_load_file($sumaFile); + $xml->xpath("//sumas/suma[@name='" . $this->name . "']")['0']['disabled'] = date(DATE_RFC822, mktime(date("H")+1,date("i"), date("s"), date("m"), date("d"), date("Y"))); + $xml->saveXML($sumaFile); + } + + public function enable(string $sumaFile, string $message) + { + Log::info($message); + $xml = simplexml_load_file($sumaFile); + unset($xml->xpath("//sumas/suma[@name='" . $this->name . "']")['0']['disabled']); + $xml->saveXML($sumaFile); + } + + public function closeFp() + { + fclose($this->fp); + } + + public function retrieveResults() + { + + $headers = ''; + $body = ''; + $length = 0; + if(!$this->fp) + { + return; + } + // We need a waiting Loop to wait till the server is ready + // get headers FIRST + stream_set_blocking($this->fp, 1); + do + { + // use fgets() not fread(), fgets stops reading at first newline + // or buffer which ever one is reached first + $data = fgets($this->fp, BUFFER_LENGTH); + // a sincle CRLF indicates end of headers + if ($data === false || $data == CRLF || feof($this->fp)) { + // break BEFORE OUTPUT + break; + } + if( sizeof(($tmp = explode(": ", $data))) === 2 ) + $headers[trim($tmp[0])] = trim($tmp[1]); + } + while (true); + // end of headers + $bodySize = 0; + if( isset($headers["Transfer-Encoding"]) && $headers["Transfer-Encoding"] === "chunked" ) + { + $body = $this->readChunked(); + + }elseif( isset($headers['Content-Length']) ) + { + $length = trim($headers['Content-Length']); + if(is_numeric($length) && $length >= 1) + $body = $this->readBody($length); + $bodySize = strlen($body); + }else { - curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, true); // allow https verification if true - curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 2); // check common name and verify with host name - curl_setopt($this->ch, CURLOPT_SSLVERSION,3); // verify ssl version 2 or 3 + abort(500, "Konnte nicht herausfinden, wie ich die Serverantwort von: " . $this->name . " auslesen soll. Header war: " . print_r($headers)); } + Redis::setBit($this->name, $this->socketNumber, 0 ); + + if( isset($headers["Content-Encoding"]) && $headers['Content-Encoding'] === "gzip") + { + $body = $this->gunzip($body); + } + #print_r($headers); + #print($body); + #print("\r\n". $bodySize); + #exit; + #die(print_r($headers)); + // $body and $headers should contain your stream data + $this->loadResults($body); + #print(print_r($headers, TRUE) . $body); + #exit; + } + + private function readBody(int $length) + { + $theData = ''; + $done = false; + stream_set_blocking($this->fp, 0); + $startTime = time(); + $lastTime = $startTime; + while (!feof($this->fp) && !$done && (($startTime + 1) > time()) && $length !== 0) + { + usleep(100); + $theNewData = fgets($this->fp, BUFFER_LENGTH); + $theData .= $theNewData; + $length -= strlen($theNewData); + $done = (trim($theNewData) === '0'); + + } + return $theData; + } + + private function readChunked() + { + $body = ''; + // read from chunked stream + // loop though the stream + do + { + // NOTE: for chunked encoding to work properly make sure + // there is NOTHING (besides newlines) before the first hexlength + + // get the line which has the length of this chunk (use fgets here) + $line = fgets($this->fp, BUFFER_LENGTH); + + // if it's only a newline this normally means it's read + // the total amount of data requested minus the newline + // continue to next loop to make sure we're done + if ($line == CRLF) { + continue; + } + + // the length of the block is sent in hex decode it then loop through + // that much data get the length + // NOTE: hexdec() ignores all non hexadecimal chars it finds + $length = hexdec($line); + + if (!is_int($length)) { + trigger_error('Most likely not chunked encoding', E_USER_ERROR); + } + + // zero is sent when at the end of the chunks + // or the end of the stream or error + if ($line === false || $length < 1 || feof($this->fp)) { + if($length <= 0) + fgets($this->fp, BUFFER_LENGTH); + // break out of the streams loop + break; + } + + // loop though the chunk + do + { + // read $length amount of data + // (use fread here) + $data = fread($this->fp, $length); - $this->addCurlHandle($mh); + // remove the amount received from the total length on the next loop + // it'll attempt to read that much less data + $length -= strlen($data); + + // PRINT out directly + #print $data; + #flush(); + // you could also save it directly to a file here + + // store in string for later use + $body .= $data; + + // zero or less or end of connection break + if ($length <= 0 || feof($this->fp)) + { + // break out of the chunk loop + if($length <= 0) + fgets($this->fp, BUFFER_LENGTH); + break; + } + } + while (true); + // end of chunk loop + } + while (true); + // end of stream loop + return $body; } - public abstract function loadResults(); + private function gunzip($zipped) { + $offset = 0; + if (substr($zipped,0,2) == "\x1f\x8b") + $offset = 2; + if (substr($zipped,$offset,1) == "\x08") + { + try + { + return gzinflate(substr($zipped, $offset + 8)); + } catch (\Exception $e) + { + abort(500, "Fehler beim unzip des Ergebnisses von folgendem Anbieter: " . $this->name); + } + } + return "Unknown Format"; + } + + protected function getHost() + { + $return = ""; + if( $this->port === "443" ) + { + $return .= "tls://"; + }else + { + $return .= "tcp://"; + } + $return .= $this->host; + return $return; + } public function getCurlInfo() { return curl_getinfo($this->ch); } + public function getCurlErrors() + { + return curl_errno($this->ch); + } + public function addCurlHandle ($mh) { curl_multi_add_handle($mh, $this->ch); @@ -62,20 +390,15 @@ abstract class Searchengine private function generateGetString($query, $url, $language, $category) { $getString = ""; - # Protokoll: - if($this->port === "443"){ - $getString .= "https://"; - }else{ - $getString .= "http://"; - } - # Host: - $getString .= $this->host; - # Port: - $getString .= ":" . $this->port; + # Skript: - $getString .= $this->skript; + if(strlen($this->skript) > 0) + $getString .= $this->skript; + else + $getString .= "/"; # FormData: - $getString .= "?" . $this->formData; + if(strlen($this->formData) > 0) + $getString .= "?" . $this->formData; # Wir müssen noch einige Platzhalter in dem GET-String ersetzen: if( strpos($getString, "<<USERAGENT>>") ){ @@ -133,4 +456,9 @@ abstract class Searchengine return "&affilData=" . $affilDataValue . "&serveUrl=" . $serveUrl; } + + public function isEnabled () + { + return $this->enabled; + } } \ No newline at end of file diff --git a/app/Models/parserSkripte/Allesklar.php b/app/Models/parserSkripte/Allesklar.php new file mode 100644 index 0000000000000000000000000000000000000000..70abc29dea3f80142732fe953bd4159a8f05890f --- /dev/null +++ b/app/Models/parserSkripte/Allesklar.php @@ -0,0 +1,60 @@ +<?php + +namespace App\Models\parserSkripte; + +use App\Models\Searchengine; +use Symfony\Component\DomCrawler\Crawler; + +class Allesklar extends Searchengine +{ + protected $tds = ""; + function __construct (\SimpleXMLElement $engine, $mh, \App\MetaGer $metager) + { + parent::__construct($engine, $mh, $metager); + } + + public function loadResults (String $result) + { + $crawler = new Crawler($result); + $crawler = $crawler + ->filter('table[width=585]') + ->reduce(function(Crawler $node, $i) { + if($i < 5) + { + return false; + } + }); + + $this->counter = 0; + $crawler->filter('table')->each(function (Crawler $node, $i) + { + try { + $this->string = ""; + $titleTag = $node->filter('tr > td > a')->first(); + $title = trim($titleTag->filter('a')->text()); + $link = $titleTag->filter('a')->attr('href'); + if($i === 0) + { + $descr = trim($node->filter('tr > td.bodytext')->eq(3)->text()); + }else + { + $descr = trim($node->filter('tr > td.bodytext')->eq(2)->text()); + } + $this->counter++; + $this->results[] = new \App\Models\Result( + $title, + $link, + $link, + $descr, + $this->gefVon, + $this->counter + ); + } catch (\InvalidArgumentException $e) + { + + } + + }); + } + +} \ No newline at end of file diff --git a/app/Models/parserSkripte/BASE.php b/app/Models/parserSkripte/BASE.php new file mode 100644 index 0000000000000000000000000000000000000000..1b6826ef8c66bfe36ec6322045ac452d32dafb4e --- /dev/null +++ b/app/Models/parserSkripte/BASE.php @@ -0,0 +1,35 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; + +class BASE extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine,\App\MetaGer $metager) + { + parent::__construct($engine, $metager); + } + + public function loadResults (String $result) + { + + $title = ""; + $link = ""; + $anzeigeLink = $link; + $descr = ""; + + #die($result); + + /*$this->counter++; + $this->results[] = new \App\Models\Result( + $title, + $link, + $anzeigeLink, + $descr, + $this->gefVon, + $this->counter + );*/ + } +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Bing.php b/app/Models/parserSkripte/Bing.php new file mode 100644 index 0000000000000000000000000000000000000000..6b0a778791f5fba9f221b1c56c4a4510f6943cbc --- /dev/null +++ b/app/Models/parserSkripte/Bing.php @@ -0,0 +1,43 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; +use Symfony\Component\DomCrawler\Crawler; + +class Bing extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine, $mh, \App\MetaGer $metager) + { + parent::__construct($engine, $mh, $metager); + } + + public function loadResults (String $result) + { + + $crawler = new Crawler($result); + $crawler->filter('ol#b_results > li.b_algo')->each(function (Crawler $node, $i) + { + $title = $node->filter('li h2 > a')->text(); + $link = $node->filter('li h2 > a')->attr('href'); + $anzeigeLink = $link; + $descr = $node->filter('li div > p')->text(); + + #die($result); + + $this->counter++; + $this->results[] = new \App\Models\Result( + $title, + $link, + $anzeigeLink, + $descr, + $this->gefVon, + $this->counter + ); + } ); + + + + } +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Dmoznebel.php b/app/Models/parserSkripte/Dmoznebel.php new file mode 100644 index 0000000000000000000000000000000000000000..00ef71c55bd4196a54ef67834fe69dfc8ffae56f --- /dev/null +++ b/app/Models/parserSkripte/Dmoznebel.php @@ -0,0 +1,35 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; + +class Dmoznebel extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine, \App\MetaGer $metager) + { + parent::__construct($engine, $metager); + } + + public function loadResults (String $result) + { + + $title = ""; + $link = ""; + $anzeigeLink = $link; + $descr = ""; + + die($result); + + $this->counter++; + $this->results[] = new \App\Models\Result( + $title, + $link, + $anzeigeLink, + $descr, + $this->gefVon, + $this->counter + ); + } +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Exalead.php b/app/Models/parserSkripte/Exalead.php new file mode 100644 index 0000000000000000000000000000000000000000..ee25306849dcfcbda057b10be16cc6f46e1a4786 --- /dev/null +++ b/app/Models/parserSkripte/Exalead.php @@ -0,0 +1,35 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; + +class Exalead extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine, \App\MetaGer $metager) + { + parent::__construct($engine, $metager); + } + + public function loadResults (String $result) + { + + $title = ""; + $link = ""; + $anzeigeLink = $link; + $descr = ""; + + #die($result); + + /*$this->counter++; + $this->results[] = new \App\Models\Result( + $title, + $link, + $anzeigeLink, + $descr, + $this->gefVon, + $this->counter + ); */ + } +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Fastbot.php b/app/Models/parserSkripte/Fastbot.php index 01d1af4720bb7c9023f545a95966511c429a6ae2..a6b36f830209510a2e54547cf02678649c429be5 100644 --- a/app/Models/parserSkripte/Fastbot.php +++ b/app/Models/parserSkripte/Fastbot.php @@ -7,19 +7,19 @@ class Fastbot extends Searchengine { public $results = []; - function __construct (\SimpleXMLElement $engine, $mh, \App\MetaGer $metager) + function __construct (\SimpleXMLElement $engine, \App\MetaGer $metager) { - parent::__construct($engine, $mh, $metager); + parent::__construct($engine, $metager); if ( strpos($this->urlEncode($metager->getEingabe()), "%") !== FALSE ) { - $this->removeCurlHandle($mh); - return FALSE; + return null; } } - public function loadResults () + public function loadResults (String $result) { - $result = utf8_encode(curl_multi_getcontent($this->ch)); + $result = utf8_encode($result); + $counter = 0; foreach( explode("\n", $result) as $line ) { $line = trim($line); @@ -29,12 +29,14 @@ class Fastbot extends Searchengine $link = $result[1]; $link = substr($link, strpos($link, "href=\"") + 6); $link = substr($link, 0, strpos($link, "\"")); + $counter++; $this->results[] = new \App\Models\Result( trim(strip_tags($result[1])), $link, $result[3], $result[2], - "<a href=\"http://www.fastbot.de\">fastbot</a>" + $this->gefVon, + $counter ); } diff --git a/app/Models/parserSkripte/Minisucher.php b/app/Models/parserSkripte/Minisucher.php new file mode 100644 index 0000000000000000000000000000000000000000..0e435f0769af85701ebfd53875f87b3d0d955863 --- /dev/null +++ b/app/Models/parserSkripte/Minisucher.php @@ -0,0 +1,55 @@ +<?php + +namespace App\Models\parserSkripte; + +use App\Models\Searchengine; + +class Minisucher extends Searchengine +{ + + function __construct (\SimpleXMLElement $engine,\App\MetaGer $metager) + { + parent::__construct($engine, $metager); + } + + public function loadResults (String $content) + { + $content = simplexml_load_string($content); + if(!$content) + { + return; + } + $results = $content->xpath('//response/result/doc'); + + $string = ""; + + $counter = 0; + foreach($results as $result) + { + $counter++; + $result = simplexml_load_string($result->saveXML()); + $title = $result->xpath('//doc/arr[@name="title"]/str')[0]->__toString(); + $link = $result->xpath('//doc/str[@name="url"]')[0]->__toString(); + $anzeigeLink = $link; + $descr = ""; + $descriptions = $content->xpath("//response/lst[@name='highlighting']/lst[@name='$link']/arr[@name='content']/str"); + foreach($descriptions as $description) + { + $descr .= $description->__toString(); + } + $descr = strip_tags($descr); + $provider = $result->xpath('//doc/str[@name="subcollection"]')[0]->__toString(); + $this->results[] = new \App\Models\Result( + $title, + $link, + $link, + $descr, + $this->gefVon, + $counter + ); + } + + + } + +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Mnogosearch.php b/app/Models/parserSkripte/Mnogosearch.php new file mode 100644 index 0000000000000000000000000000000000000000..5e31e6fc8d1d34cc237d8265edbb4eeca3361a1f --- /dev/null +++ b/app/Models/parserSkripte/Mnogosearch.php @@ -0,0 +1,51 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; +use Symfony\Component\DomCrawler\Crawler; + +class Mnogosearch extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine, $mh, \App\MetaGer $metager) + { + parent::__construct($engine, $mh, $metager); + } + + public function loadResults () + { + $result = curl_multi_getcontent($this->ch); + $counter = 0; + $crawler = new Crawler($result); + $crawler->filter('table[width=600]') + ->reduce(function (Crawler $node, $i) + { + if(strpos($node->text(), "Result pages:") !== FALSE) + { + return false; + } + }) + ->each(function(Crawler $node, $i) + { + $title = $node->filter('table > tr > td ')->eq(1)->filter('td > div')->text(); + $title = preg_replace("/\s+/si", " ", $title); + + $link = $node->filter('table > tr > td ')->eq(1)->filter('td > div > a')->attr('href'); + $anzeigeLink = $link; + $descr = $node->filter('table > tr > td ')->eq(1)->filter('td > div')->eq(1)->text(); + $this->counter++; + + $this->results[] = new \App\Models\Result( + $title, + $link, + $anzeigeLink, + $descr, + $this->gefVon, + $this->counter + ); + }); + + + } +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Nebel.php b/app/Models/parserSkripte/Nebel.php new file mode 100644 index 0000000000000000000000000000000000000000..28c6c7f12ea85422fec1497403a4753432411927 --- /dev/null +++ b/app/Models/parserSkripte/Nebel.php @@ -0,0 +1,41 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; + +class Nebel extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine,\App\MetaGer $metager) + { + parent::__construct($engine, $metager); + } + + public function loadResults (String $result) + { + $results = trim($result); + foreach( explode("\n", $results) as $result ) + { + $res = explode("|", $result); + if(sizeof($res) < 3) + continue; + $title = $res[2]; + $link = $res[0]; + $anzeigeLink = $link; + $descr = $res[1]; + + $this->counter++; + $this->results[] = new \App\Models\Result( + $title, + $link, + $anzeigeLink, + $descr, + $this->gefVon, + $this->counter + ); + } + + + } +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Onenewspage.php b/app/Models/parserSkripte/Onenewspage.php new file mode 100644 index 0000000000000000000000000000000000000000..b50361f3df24964b0999a0f34b3060534e76198c --- /dev/null +++ b/app/Models/parserSkripte/Onenewspage.php @@ -0,0 +1,41 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; + +class Onenewspage extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine, \App\MetaGer $metager) + { + parent::__construct($engine, $metager); + } + + public function loadResults (String $result) + { + $results = trim($result); + + foreach( explode("\n", $results) as $result ) + { + $res = explode("|", $result); + + $title = $res[0]; + $link = $res[2]; + $anzeigeLink = $link; + $descr = $res[1]; + + $this->counter++; + $this->results[] = new \App\Models\Result( + $title, + $link, + $anzeigeLink, + $descr, + $this->gefVon, + $this->counter + ); + } + + + } +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Onenewspagegermany.php b/app/Models/parserSkripte/Onenewspagegermany.php index 7bc8eea604fcd5c2bd06cb77b20e985aa6206795..d7f8feb028cbeaeaf7e3353c63a7071300b9a354 100644 --- a/app/Models/parserSkripte/Onenewspagegermany.php +++ b/app/Models/parserSkripte/Onenewspagegermany.php @@ -8,26 +8,28 @@ class Onenewspagegermany extends Searchengine { public $results = []; - function __construct (\SimpleXMLElement $engine, $mh, $query, $time, $ip, $url) + function __construct (\SimpleXMLElement $engine, \App\MetaGer $metager) { - parent::__construct($engine, $mh, $query, $time, $ip, $url); + parent::__construct($engine, $metager); } - public function loadResults () + public function loadResults (String $result) { - $result = curl_multi_getcontent($this->ch); + $counter = 0; foreach( explode("\n", $result) as $line ) { $line = trim($line); if( strlen($line) > 0 ){ # Hier bekommen wir jedes einzelne Ergebnis $result = explode("|", $line); + $counter++; $this->results[] = new Result( trim(strip_tags($result[0])), $result[2], $result[2], $result[1], - "<a href=\"http://www.newsdeutschland.com/videos.htm\">newsdeutschland.com</a>" + $this->gefVon, + $counter ); } diff --git a/app/Models/parserSkripte/Onenewspagevideo.php b/app/Models/parserSkripte/Onenewspagevideo.php new file mode 100644 index 0000000000000000000000000000000000000000..f8e8179e23f6f19ae279921497738294ade235e2 --- /dev/null +++ b/app/Models/parserSkripte/Onenewspagevideo.php @@ -0,0 +1,43 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; + +class Onenewspagevideo extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine, \App\MetaGer $metager) + { + parent::__construct($engine, $metager); + } + + public function loadResults (String $result) + { + $results = trim($result); + foreach( explode("\n", $results) as $result ) + { + $res = explode("|", $result); + if(sizeof($res) < 3) + { + continue; + } + $title = $res[0]; + $link = $res[2]; + $anzeigeLink = $link; + $descr = $res[1]; + + $this->counter++; + $this->results[] = new \App\Models\Result( + $title, + $link, + $anzeigeLink, + $descr, + $this->gefVon, + $this->counter + ); + } + + + } +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Overture.php b/app/Models/parserSkripte/Overture.php new file mode 100644 index 0000000000000000000000000000000000000000..2fe0dbef4c7399cc7d86d052a108421109e713f7 --- /dev/null +++ b/app/Models/parserSkripte/Overture.php @@ -0,0 +1,48 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; +use Log; + +class Overture extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine, \App\MetaGer $metager) + { + parent::__construct($engine, $metager); + } + + public function loadResults (String $result) + { + $result = preg_replace("/\r\n/si", "", $result); + try { + $content = simplexml_load_string($result); + } catch (\Exception $e) { + abort(500, "$result is not a valid xml string"); + } + + if(!$content) + { + return; + } + $results = $content->xpath('//Results/ResultSet[@id="inktomi"]/Listing'); + + foreach($results as $result) + { + $title = $result["title"]; + $link = $result->{"ClickUrl"}->__toString(); + $anzeigeLink = $result["siteHost"]; + $descr = $result["description"]; + $this->counter++; + $this->results[] = new \App\Models\Result( + $title, + $link, + $anzeigeLink, + $descr, + $this->gefVon, + $this->counter + ); + } + } +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Wikipedia.php b/app/Models/parserSkripte/Wikipedia.php new file mode 100644 index 0000000000000000000000000000000000000000..3327a6d85fa27374a7fee94d4fc171fc41250d24 --- /dev/null +++ b/app/Models/parserSkripte/Wikipedia.php @@ -0,0 +1,30 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; + +class Wikipedia extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine,\App\MetaGer $metager) + { + parent::__construct($engine, $metager); + } + + public function loadResults (String $result) + { + $result = utf8_decode($result); + $counter = 0; + + #die($crawler); + $this->results[] = new \App\Models\Result( + trim(strip_tags($result[1])), + $link, + $result[3], + $result[2], + $this->gefVon, + $counter + ); + } +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Witch.php b/app/Models/parserSkripte/Witch.php new file mode 100644 index 0000000000000000000000000000000000000000..5aa23c72ea8ec0939bf27652d9ff9c39e09bfea6 --- /dev/null +++ b/app/Models/parserSkripte/Witch.php @@ -0,0 +1,42 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; + +class Witch extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine, \App\MetaGer $metager) + { + parent::__construct($engine, $metager); + } + + public function loadResults (String $result) + { + $result = trim(utf8_encode($result)); + $results = explode("\n", $result); + array_shift($results); + foreach($results as $res) + { + $res = explode(";", $res); + $title = trim($res[0], "'"); + $link = trim($res[2], "'"); + $anzeigeLink = $link; + $descr = trim($res[1], "'"); + + + $this->counter++; + $this->results[] = new \App\Models\Result( + $title, + $link, + $anzeigeLink, + $descr, + $this->gefVon, + $this->counter + ); + } + + + } +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Yacy.php b/app/Models/parserSkripte/Yacy.php new file mode 100644 index 0000000000000000000000000000000000000000..5dfc72d51dabeaca2def2cf3b038ccc3f16f2a71 --- /dev/null +++ b/app/Models/parserSkripte/Yacy.php @@ -0,0 +1,45 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; + +class Yacy extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine, \App\MetaGer $metager) + { + parent::__construct($engine, $metager); + } + + public function loadResults (String $result) + { + + # die($result); + try { + $content = simplexml_load_string($result); + } catch (\Exception $e) { + abort(500, "$result is not a valid xml string"); + } + + $results = $content->xpath("//rss/channel/item"); + + foreach($results as $res) + { + $title = $res->{"title"}; + $link = $res->{"link"}; + $anzeigeLink = $link; + $descr = $res->{"description"}; + + $this->counter++; + $this->results[] = new \App\Models\Result( + $title, + $link, + $anzeigeLink, + $descr, + $this->gefVon, + $this->counter + ); + } + } +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Yandex.php b/app/Models/parserSkripte/Yandex.php new file mode 100644 index 0000000000000000000000000000000000000000..0f2bc6b7342f78bbf7f6a7314bd3e2a730adcf31 --- /dev/null +++ b/app/Models/parserSkripte/Yandex.php @@ -0,0 +1,35 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; + +class Yandex extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine, \App\MetaGer $metager) + { + parent::__construct($engine, $metager); + } + + public function loadResults (String $result) + { + + $title = ""; + $link = ""; + $anzeigeLink = $link; + $descr = ""; + + #die($result); + + /*$this->counter++; + $this->results[] = new \App\Models\Result( + $title, + $link, + $anzeigeLink, + $descr, + $this->gefVon, + $this->counter + );*/ + } +} \ No newline at end of file diff --git a/app/Models/parserSkripte/Zeitde.php b/app/Models/parserSkripte/Zeitde.php new file mode 100644 index 0000000000000000000000000000000000000000..cd0bf092c851192395ab0a68cebd22fbd1225ed4 --- /dev/null +++ b/app/Models/parserSkripte/Zeitde.php @@ -0,0 +1,41 @@ +<?php + +namespace app\Models\parserSkripte; +use App\Models\Searchengine; + +class Zeitde extends Searchengine +{ + public $results = []; + + function __construct (\SimpleXMLElement $engine, \App\MetaGer $metager) + { + parent::__construct($engine, $metager); + } + + public function loadResults (String $result) + { + + $results = json_decode($result); + + foreach( $results->{"matches"} as $result ) + { + $title = $result->{"title"}; + $link = $result->{"href"}; + $anzeigeLink = $link; + $descr = $result->{"snippet"}; + + $this->counter++; + $this->results[] = new \App\Models\Result( + $title, + $link, + $anzeigeLink, + $descr, + $this->gefVon, + $this->counter + ); + } + + + + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index 272eb3492c4da549e488ed3d56336aaa5b204601..33730d16b9479411aea7fd56244fda215e826893 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,8 @@ "mockery/mockery": "0.9.*", "phpunit/phpunit": "~4.0", "symfony/css-selector": "2.8.*|3.0.*", - "symfony/dom-crawler": "2.8.*|3.0.*" + "symfony/dom-crawler": "2.8.*|3.0.*", + "predis/predis": "~1.1@dev" }, "autoload": { "classmap": [ diff --git a/composer.lock b/composer.lock index 9d9d1e86136fbf89189e776b7d41f0c0a207dbc7..8205ad207506f2a01b8950d20953e0d851b69ec2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "ac43207ede0f63c9fafc0f63d04346ae", - "content-hash": "db9cfe1e3caebe3afb5890033afb9452", + "hash": "a5625c89574373a28838231ec9326946", + "content-hash": "ede6c673894b3df9d137acec400d9125", "packages": [ { "name": "classpreloader/classpreloader", @@ -2853,6 +2853,61 @@ ], "time": "2015-10-02 06:51:40" }, + { + "name": "predis/predis", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/nrk/predis.git", + "reference": "9398a793a5f0280d789c19fc70e479207dc48da8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nrk/predis/zipball/9398a793a5f0280d789c19fc70e479207dc48da8", + "reference": "9398a793a5f0280d789c19fc70e479207dc48da8", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "ext-curl": "Allows access to Webdis when paired with phpiredis", + "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Predis\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniele Alessandri", + "email": "suppakilla@gmail.com", + "homepage": "http://clorophilla.net" + } + ], + "description": "Flexible and feature-complete Redis client for PHP and HHVM", + "homepage": "http://github.com/nrk/predis", + "keywords": [ + "nosql", + "predis", + "redis" + ], + "time": "2016-05-25 09:25:12" + }, { "name": "sebastian/comparator", "version": "1.2.0", @@ -3385,7 +3440,9 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "predis/predis": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/config/app.php b/config/app.php index 22ac17845b905b4a2585aef2266f4f9bcb04509e..088dc4ddc9cc880a6ac8dfa6188cc4c0dd64efd3 100644 --- a/config/app.php +++ b/config/app.php @@ -52,7 +52,7 @@ return [ | */ - 'timezone' => 'UTC', + 'timezone' => 'Europe/Berlin', /* |-------------------------------------------------------------------------- diff --git a/resources/views/metager3.blade.php b/resources/views/metager3.blade.php index 52069f144d4af2f82b8855c11d7529f999c2251b..329a805ed9b072ade94a32fb747755be806b79c8 100644 --- a/resources/views/metager3.blade.php +++ b/resources/views/metager3.blade.php @@ -3,37 +3,54 @@ @section('results') @foreach($results as $result) <div class="result"> - <div class="number" style="color:#FF4000;">1 )</div> - <div class="resultInformation"> - <p class="title"><a class="title" href="{{ $result['link'] }}" target="_blank">{{ $result['titel'] }}</a> - </p> - <div class="link"> - <div> - <div class="link-link"><a data-count="1" data-host="test.de" data-hoster="fastbot" href="https://www.fastbot.de/red.php?red=3365000124457707395+http://www.test.de" target="_blank">{{ $result['anzeigeLink'] }}</a> - </div> - <div class="options"><a data-container="body" data-html="true" data-placement="auto bottom" data-title="<span class='glyphicon glyphicon-cog'></span> Optionen" data-toggle="popover" data-trigger="focus" tabindex="0"><span class="glyphicon glyphicon-triangle-bottom"></span></a> - <div class="content hidden"> - <ul class="options-list list-unstyled"> - <li><a href="https://metager.de/meta/meta.ger3?focus=web&eingabe=test%20site%3Awww.test.de&encoding=utf8&lang=all">Suche auf dieser Domain neu starten</a> - </li> - <li> - <form method="get" action="/meta/meta.ger3" enctype="multipart/form-data" accept-charset="UTF-8" class="form"> - <input type="hidden" name="focus" value="web" /> - <input type="hidden" name="eingabe" value="test -host:test.de" /> - <input type="hidden" name="encoding" value="utf8" /> - <input type="hidden" name="lang" value="all" /> - <input type="hidden" name="mobile" value="0" /> - <input type="submit" name="" value="test.de ausblenden" /> - </form> - </li> - </ul> - </div> - </div> - </div> <span class="hoster ">von <a href="http://www.fastbot.de">fastbot</a></span> - <a class="proxy" data-container="body" data-content="Der Link wird anonymisiert geöffnet. Ihre Daten werden nicht zum Zielserver übetragen. Möglicherweise funktionieren manche Webseiten nicht wie gewohnt." data-placement="auto right" data-toggle="popover" href="https://proxy.suma-ev.de/cgi-bin/nph-proxy.cgi/en/I0/https/www.fastbot.de/red.php?red=3365000124457707395+http://www.test.de" onmouseout="$(this).popover('hide');" onmouseover="$(this).popover('show');" target="_blank"><img alt="Proxy-Icon" src="/img/proxyicon.png" />anonym öffnen</a> + <div class="number" style="color:#FF4000;">1 )</div> + <div class="resultInformation"> + <p class="title"> + <a class="title" href="{{ $result['link'] }}" target="_blank"> + {{ $result['titel'] }} + </a> + </p> + <div class="link"> + <div> + <div class="link-link"> + <a data-count="1" data-host="test.de" data-hoster="fastbot" href="{{ $result['link'] }}" target="_blank"> + {{ $result['anzeigeLink'] }} + </a> + </div> + <div class="options"> + <a data-container="body" data-html="true" data-placement="auto bottom" data-title="<span class='glyphicon glyphicon-cog'></span> Optionen" data-toggle="popover" data-trigger="focus" tabindex="0"> + <span class="glyphicon glyphicon-triangle-bottom"></span> + </a> + <div class="content hidden"> + <ul class="options-list list-unstyled"> + <li> + <a href="https://metager.de/meta/meta.ger3?focus=web&eingabe=test%20site%3Awww.test.de&encoding=utf8&lang=all"> + Suche auf dieser Domain neu starten + </a> + </li> + <li> + <form method="get" action="/meta/meta.ger3" enctype="multipart/form-data" accept-charset="UTF-8" class="form"> + <input type="hidden" name="focus" value="web" /> + <input type="hidden" name="eingabe" value="test -host:test.de" /> + <input type="hidden" name="encoding" value="utf8" /> + <input type="hidden" name="lang" value="all" /> + <input type="hidden" name="mobile" value="0" /> + <input type="submit" name="" value="test.de ausblenden" /> + </form> + </li> + </ul> </div> - <div class="description">{{ $result['descr'] }}</div> </div> - </div> + </div> + <span class="hoster "> + von {!! $result['gefVon'] !!} + </span> + <a class="proxy" data-container="body" data-content="Der Link wird anonymisiert geöffnet. Ihre Daten werden nicht zum Zielserver übetragen. Möglicherweise funktionieren manche Webseiten nicht wie gewohnt." data-placement="auto right" data-toggle="popover" href="https://proxy.suma-ev.de/cgi-bin/nph-proxy.cgi/en/I0/https/www.fastbot.de/red.php?red=3365000124457707395+http://www.test.de" onmouseout="$(this).popover('hide');" onmouseover="$(this).popover('show');" target="_blank"> + <img alt="Proxy-Icon" src="/img/proxyicon.png" />anonym öffnen + </a> + </div> + <div class="description">{{ $result['descr'] }}</div> + </div> + </div> @endforeach @endsection