+.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>