+.linkthumbnail {
+       max-height: 200px;
+}
\ No newline at end of file
 
 
 require('config.php');
 require('lib/summoner.class.php');
+require('lib/management.class.php');
 
 ## main vars
 $Summoner = new Summoner();
 $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']);
 
--- /dev/null
+<?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
 
         * 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();
                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);
        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;
+       }
 }
 
 ?>
 
--- /dev/null
+<?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
 
 $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) {
 
     # 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
 
 <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]">
 
 <?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>
     <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>