From: Banana <banana@optimus.de>
Date: Sun, 25 Dec 2016 11:45:31 +0000 (+0100)
Subject: submitting a new URL and finding the info via curl.
X-Git-Tag: 2.1-alpha-2019-0-29~57
X-Git-Url: http://91.132.146.200/gitweb/?a=commitdiff_plain;h=647111b9c067c56503482bada1f197430013ca23;p=insipid.git

submitting a new URL and finding the info via curl.
---

diff --git a/webroot/asset/css/app.css b/webroot/asset/css/app.css
index e69de29..3e8e7f6 100644
--- a/webroot/asset/css/app.css
+++ b/webroot/asset/css/app.css
@@ -0,0 +1,3 @@
+.linkthumbnail {
+	max-height: 200px;
+}
\ No newline at end of file
diff --git a/webroot/index.php b/webroot/index.php
index c8bd963..af5a94d 100644
--- a/webroot/index.php
+++ b/webroot/index.php
@@ -55,6 +55,7 @@ else {
 
 require('config.php');
 require('lib/summoner.class.php');
+require('lib/management.class.php');
 
 ## main vars
 $Summoner = new Summoner();
@@ -74,6 +75,9 @@ if ($DB->connect_errno) exit('Can not connect to MySQL Server');
 $DB->set_charset("utf8mb4");
 $DB->query("SET collation_connection = 'utf8mb4_bin'");
 
+# management needs the DB object
+$Management = new Management($DB);
+
 /*
 if(isset($_GET['p']) && !empty($_GET['p'])) {
     $_requestMode = trim($_GET['p']);
diff --git a/webroot/lib/management.class.php b/webroot/lib/management.class.php
new file mode 100644
index 0000000..dbdcb71
--- /dev/null
+++ b/webroot/lib/management.class.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Insipid
+ * Personal web-bookmark-system
+ *
+ * Copyright 2016-2017 Johannes Keßler
+ *
+ * Development starting from 2011: Johannes Keßler
+ * https://www.bananas-playground.net/projekt/insipid/
+ *
+ * creator:
+ * Luke Reeves <luke@neuro-tech.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/gpl-3.0.
+ *
+ */
+
+class Management {
+    /**
+     * the database object
+     * @var object
+     */
+    private $DB;
+
+    public function __construct($databaseConnectionObject) {
+        $this->DB = $databaseConnectionObject;
+    }
+
+    /**
+     * get all the available categories from the DB.
+     * optinal limit
+     * @param int $limit
+     */
+    public function categories($limit=false) {
+        $ret = array();
+
+        $queryStr = "SELECT * FROM `".DB_PREFIX."_category` ORDER BY `name`";
+        if(!empty($limit)) {
+            $queryStr .= " LIMIT $limit";
+        }
+        $query = $this->DB->query($queryStr);
+        if(!empty($query)) {
+            $ret = $query->fetch_all(MYSQLI_ASSOC);
+        }
+
+        return $ret;
+    }
+
+    /**
+     * get all the available tags from the DB.
+     * optional limit
+     * @param int $limit
+     */
+    public function tags($limit=false) {
+        $ret = array();
+
+        $queryStr = "SELECT * FROM `".DB_PREFIX."_tag` ORDER BY `name`";
+        if(!empty($limit)) {
+            $queryStr .= " LIMIT $limit";
+        }
+        $query = $this->DB->query($queryStr);
+        if(!empty($query)) {
+            $ret = $query->fetch_all(MYSQLI_ASSOC);
+        }
+
+        return $ret;
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/webroot/lib/summoner.class.php b/webroot/lib/summoner.class.php
index ce66c6d..dac6c0f 100644
--- a/webroot/lib/summoner.class.php
+++ b/webroot/lib/summoner.class.php
@@ -156,7 +156,7 @@ class Summoner {
 	 * execute a curl call to the fiven $url
 	 * @param string $curl The request url
 	 */
-	static function curlCall($url,$port=80) {
+	static function curlCall($url,$port=false) {
 		$ret = false;
 
 		$ch = curl_init();
@@ -165,14 +165,18 @@ class Summoner {
 		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
 		curl_setopt($ch, CURLOPT_TIMEOUT, 30);
-		curl_setopt($ch, CURLOPT_PORT, $port);
+		if(!empty($port)) {
+		  curl_setopt($ch, CURLOPT_PORT, $port);
+		}
 
 		$do = curl_exec($ch);
+
 		if(is_string($do) === true) {
 			$ret = $do;
 		}
 		else {
 			$ret = false;
+			error_log(var_export(curl_error($ch),true));
 		}
 
 		curl_close($ch);
@@ -220,6 +224,113 @@ class Summoner {
 	static function ifset($array,$key) {
 	    return isset($array[$key]) ? $array[$key] : false;
 	}
+
+	/**
+	 * try to gather meta information from given URL
+	 * @param string $url
+	 */
+	static function gatherInfoFromURL($url) {
+	    $ret = false;
+
+	    if(self::validate($url,'url')) {
+	        $data = self::curlCall($url);
+	        if(!empty($data)) {
+	            $ret = self::socialMetaInfos($data);
+	        }
+	    }
+
+	    return $ret;
+	}
+
+	/**
+	 * get as much as possible solcial meta infos from given string
+	 * the string is usually a HTML source
+	 * @param string $string
+	 */
+	static function socialMetaInfos($string) {
+	    #http://www.w3bees.com/2013/11/fetch-facebook-og-meta-tags-with-php.html
+	    #http://www.9lessons.info/2014/01/social-meta-tags-for-google-twitter-and.html
+	    #http://ogp.me/
+	    #https://moz.com/blog/meta-data-templates-123
+
+	    $dom = new DomDocument;
+	    # surpress invalid html warnings
+	    @$dom->loadHTML($string);
+
+	    $xpath = new DOMXPath($dom);
+	    $metas = $xpath->query('//*/meta');
+
+	    $mediaInfos = array();
+
+	    # meta tags
+	    foreach($metas as $meta) {
+	        if($meta->getAttribute('property')) {
+	            $prop = $meta->getAttribute('property');
+	            $prop = mb_strtolower($prop);
+
+	            # minimum required information
+	            # http://ogp.me/#metadata
+	            if($prop == "og:title") {
+
+	                $mediaInfos['title'] = $meta->getAttribute('content');
+	            }
+	            elseif($prop == "og:image") {
+	                $mediaInfos['image'] = $meta->getAttribute('content');
+	            }
+	            elseif($prop == "og:url") {
+	                $mediaInfos['link'] = $meta->getAttribute('content');
+	            }
+	            elseif($prop == "og:description") {
+	                $mediaInfos['description'] = $meta->getAttribute('content');
+	            }
+	        }
+	        elseif($meta->getAttribute('name')) {
+	            $name = $meta->getAttribute('name');
+	            $name = mb_strtolower($name);
+
+	            # twitter
+	            # https://dev.twitter.com/cards/overview
+
+	            if($name == "twitter:title") {
+	                $mediaInfos['title'] = $meta->getAttribute('content');
+	            }
+	            elseif($name == "twitter:description") {
+	                $mediaInfos['description'] = $meta->getAttribute('content');
+	            }
+	            elseif($name == "twitter:image") {
+	                $mediaInfos['image'] = $meta->getAttribute('content');
+	            }
+	            elseif($name == "description") {
+	                $mediaInfos['description'] = $meta->getAttribute('content');
+	            }
+
+	        }
+	        elseif($meta->getAttribute('itemprop')) {
+	            $itemprop = $meta->getAttribute('itemprop');
+	            $itemprop = mb_strtolower($itemprop);
+
+	            # google plus
+	            if($itemprop == "name") {
+	                $mediaInfos['title'] = $meta->getAttribute('content');
+	            }
+	            elseif($itemprop == "description") {
+	                $mediaInfos['description'] = $meta->getAttribute('content');
+	            }
+	            elseif($itemprop == "image") {
+	                $mediaInfos['image'] = $meta->getAttribute('content');
+	            }
+
+	        }
+	    }
+
+
+	    if(!isset($mediaInfos['title'])) {
+	        $titleDom = $xpath->query('//html/head/title');
+	        $mediaInfos['title'] = $titleDom->item(0)->nodeValue;
+	    }
+
+	    return $mediaInfos;
+	}
 }
 
 ?>
diff --git a/webroot/lib/tag.class.php b/webroot/lib/tag.class.php
new file mode 100644
index 0000000..6f33597
--- /dev/null
+++ b/webroot/lib/tag.class.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Insipid
+ * Personal web-bookmark-system
+ *
+ * Copyright 2016-2017 Johannes Keßler
+ *
+ * Development starting from 2011: Johannes Keßler
+ * https://www.bananas-playground.net/projekt/insipid/
+ *
+ * creator:
+ * Luke Reeves <luke@neuro-tech.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/gpl-3.0.
+ *
+ */
+ ?>
\ No newline at end of file
diff --git a/webroot/view/home.inc.php b/webroot/view/home.inc.php
index 3f63929..af4b756 100644
--- a/webroot/view/home.inc.php
+++ b/webroot/view/home.inc.php
@@ -33,8 +33,14 @@ $queryStr = false;
 $searchResult = false;
 $showAddForm = false;
 $formData = false;
+$honeypotCheck = false;
 
-if(isset($_POST['data']) && !empty($_POST['data']) && isset($_POST['submitsearch'])) {
+if((isset($_POST['password']) && !empty($_POST['password'])) || (isset($_POST['username']) && !empty($_POST['username']))) {
+    # those are hidden fields. A robot my input these. A valid user does not.
+    $honeypotCheck = true;
+}
+
+if(isset($_POST['data']) && !empty($_POST['data']) && isset($_POST['submitsearch']) && $honeypotCheck === false) {
     $searchValue = trim($_POST['data']['searchfield']);
     $isUrl = Summoner::validate($searchValue,'url');
     if($isUrl === true) {
@@ -55,8 +61,18 @@ if(isset($_POST['data']) && !empty($_POST['data']) && isset($_POST['submitsearch
 
     # new one?
     if(empty($searchResult) && $isUrl === true) {
+        # try to gather some information automatically
+        $linkInfo = Summoner::gatherInfoFromURL($searchValue);
+        if(!empty($linkInfo)) {
+            $formData['description'] = $linkInfo['description'];
+            $formData['title'] = $linkInfo['title'];
+            $formData['image'] = $linkInfo['image'];
+        }
         # show the add form
         $showAddForm = true;
         $formData['url'] = $searchValue;
     }
-}
\ No newline at end of file
+}
+
+$existingCategories = $Management->categories();
+$existingTags = $Management->tags();
\ No newline at end of file
diff --git a/webroot/view/home.php b/webroot/view/home.php
index be4fbd1..60ff137 100644
--- a/webroot/view/home.php
+++ b/webroot/view/home.php
@@ -35,6 +35,8 @@
 <div class="row">
 	<div class="large-12 columns">
 		<form method="post">
+			<input type="hidden" name="password" />
+			<input type="hidden" name="username" />
     		<div class="input-group">
     			<span class="input-group-label"><i class="fi-link"></i></span>
     			<input class="input-group-field" type="url" name="data[searchfield]">
@@ -66,6 +68,8 @@
 
 <?php if($showAddForm) { ?>
 <form method="post">
+	<input type="hidden" name="password" />
+	<input type="hidden" name="username" />
 	<div class="row">
     	<div class="large-12 columns">
     		<h3>This URL was not found. Want to add it?</h3>
@@ -82,35 +86,73 @@
     <div class="row">
     	<div class="large-6 columns">
     		<label>
-    			Username
-    			<input type="text" name="data[username]" />
+    			Description
+    			<input type="text" name="data[description]" value="<?php echo Summoner::ifset($formData, 'description'); ?>" />
     		</label>
     	</div>
+    	<div class="large-6 columns">
+			<label>
+				Title
+				<input type="text" name="data[title]" value="<?php echo Summoner::ifset($formData, 'title'); ?>" />
+			</label>
+    	</div>
+    </div>
+    <div class="row">
     	<div class="large-6 columns">
     		<label>
-    			Password
-    			<input type="password" name="data[password]" />
+    			Image Link
+    			<input type="url" name="data[image]" value="<?php echo Summoner::ifset($formData, 'image'); ?>" />
     		</label>
     	</div>
+    	<div class="large-6 columns">
+			<img class="linkthumbnail" src="<?php echo Summoner::ifset($formData, 'image'); ?>" alt="Image from provided link" />
+    	</div>
     </div>
-
     <div class="row">
     	<div class="large-6 columns">
     		<label>
     			Category
-    			<select name="data[category]"></select>
+    			<input type="text" name="data[category]" list="categorylist" />
+    			<datalist id="categorylist">
+				<?php foreach($existingCategories as $c) { ?>
+					<option value="<?php echo $c; ?>">
+				<?php } ?>
+                </datalist>
     		</label>
     	</div>
     	<div class="large-6 columns">
     		<label>
     			Tag
-    			<select name="data[tag]"></select>
+    			<input type="text" name="data[tag]" list="taglist" />
+    			<datalist id="taglist">
+    			<?php foreach($existingTags as $t) { ?>
+					<option value="<?php echo $t; ?>">
+				<?php } ?>
+                </datalist>
     		</label>
     	</div>
     </div>
 
     <div class="row">
-    	<div class="large-12 columns">
+    	<div class="large-6 columns">
+    		<label>
+    			Username
+    			<input type="text" name="data[username]" />
+    		</label>
+    	</div>
+    	<div class="large-6 columns">
+    		<label>
+    			Password
+    			<input type="password" name="data[password]" />
+    		</label>
+    	</div>
+    </div>
+
+    <div class="row">
+    	<div class="large-8 columns">
+    		<input type="checkbox" name="data[private]" value="1" /><label>Private</label>
+    	</div>
+    	<div class="large-4 columns text-right" >
     		<input type="submit" class="button" value="Add new Link">
     	</div>
     </div>