$ git clone http://thingshare.ion.nu/thingshare.git
commit ca46826d07de83551cf2cb7be330a201e2fd7495
Author: Alicia <...>
Date: Wed Jul 29 23:06:06 2020 +0200
Added support for tags on things.
diff --git a/admin.php b/admin.php
index 93ad9de..4e69781 100644
--- a/admin.php
+++ b/admin.php
@@ -27,11 +27,13 @@ switch($path[2])
case 'federation': include('admin_federation.php'); break;
case 'filetypes': include('admin_filetypes.php'); break;
case 'licenses': include('admin_licenses.php'); break;
+ case 'tags': include('admin_tags.php'); break;
default:
if($privileges&PRIV_PRIVILEGES){print('<a href="'.BASEURL.'/admin/privileges">Manage user privileges</a><br />');}
if($privileges&PRIV_MODERATE){print('<a href="'.BASEURL.'/admin/moderate">Moderation options</a><br />');}
if($privileges&PRIV_FEDERATION){print('<a href="'.BASEURL.'/admin/federation">Manage peer federation</a><br />');}
if($privileges&PRIV_FILETYPES){print('<a href="'.BASEURL.'/admin/filetypes">Manage filetypes</a><br />');}
if($privileges&PRIV_LICENSES){print('<a href="'.BASEURL.'/admin/licenses">Manage the license list</a><br />');}
+ if($privileges&PRIV_TAGS){print('<a href="'.BASEURL.'/admin/tags">Manage tags</a><br />');}
}
?>
diff --git a/db.php b/db.php
index 1715fd6..5fca0d3 100644
--- a/db.php
+++ b/db.php
@@ -120,6 +120,8 @@ function db_create_tables()
latest boolean);');
mysqli_query($db, 'create table userblocks(user integer, blocked text);');
mysqli_query($db, 'create table loginfails(ip varchar(256), timestamp datetime);');
+ mysqli_query($db, 'create table tags(id integer primary key auto_increment, name text, blacklist boolean);');
+ mysqli_query($db, 'create table tagmaps(tag integer, thing integer);');
}
function db_getuser($id)
diff --git a/editthing.php b/editthing.php
index e28d40d..1521bf7 100644
--- a/editthing.php
+++ b/editthing.php
@@ -107,6 +107,23 @@ if(isset($_POST['name']) && isset($_POST['description']) && checknonce())
$name=mysqli_real_escape_string($db, $_FILES['files']['name'][$i]);
insertfile($thingid, $name, $hash, $_POST['previewfile']==$i);
}
+ // Save tags
+ foreach(explode(' ', $_POST['tags']) as $tag)
+ {
+ if($tag==''){continue;}
+ $tag=mysqli_real_escape_string($db, strtolower($tag));
+ $res=mysqli_query($db, 'select id, blacklist from tags where name="'.$tag.'"');
+ if(($res=mysqli_fetch_row($res)))
+ {
+ if($res[1]){continue;}
+ $tagid=$res[0];
+ }else{
+ if(!getoption('usertags', true)){continue;}
+ mysqli_query($db, 'insert into tags(name, blacklist) values("'.$tag.'", 0)');
+ $tagid=mysqli_insert_id($db);
+ }
+ mysqli_query($db, 'insert into tagmaps(tag, thing) values('.$tagid.', '.$thingid.')');
+ }
header('Location: '.BASEURL.'/thing/'.$id.'@'.DOMAIN);
system('php genpreviews.php > /dev/null &'); // Launch preview generation in the background
exit();
@@ -117,6 +134,7 @@ $name='';
$description='';
$files='';
$license='';
+$tags='';
if($id!='new') // Load from DB when editing a preexisting thing
{
$res=mysqli_query($db, 'select id, name, description, license from things where thingid='.(int)$id.' and latest');
@@ -136,11 +154,18 @@ if($id!='new') // Load from DB when editing a preexisting thing
$files.='<button onclick="this.parentNode.remove(); return false;">X</button></div>'."\n";
++$i;
}
+ // Gather tags
+ $res=mysqli_query($db, 'select tags.name from tagmaps, tags where tags.id=tagmaps.tag and tagmaps.thing='.(int)$thingid);
+ while($row=mysqli_fetch_row($res))
+ {
+ $tags.=($tags==''?'':' ').$row[0];
+ }
}
// If saving was attempted, retain changes
if(isset($_POST['name'])){$name=$_POST['name'];}
if(isset($_POST['description'])){$description=$_POST['description'];}
if(isset($_POST['license'])){$license=$_POST['license'];}
+if(isset($_POST['tags'])){$tags=$_POST['tags'];}
// Gather license options
$licenses='';
@@ -157,9 +182,9 @@ $selected=(($license=='other')?' selected':'');
$licenses.='<option value="other"'.$selected.'>'._('Other (see description)').'</option>';
$maxsize=-1;
-for(Array('upload_max_filesize','post_max_size') as $name)
+foreach(Array('upload_max_filesize','post_max_size') as $sname)
{
- $size=ini_get($name);
+ $size=ini_get($sname);
// Translate to bytes for the MAX_FILE_SIZE input
switch(strtoupper(substr($size,-1)))
{
@@ -222,6 +247,10 @@ function morefiles(prev)
<?=_('Description:')?><br />
<textarea name="description" rows="15" style="width:100%;" onchange="document.getElementById('mdpreview').innerHTML='Markdown preview:<br />'+Mdjs.md2html(this.value.replace(/&/g,'&amp;').replace(/</g,'&lt;'));" onkeyup="this.onchange();"><?=htmlentities($description)?></textarea><br />
<div id="mdpreview"></div>
+ <div class="paragraph">
+ <?=_('Tags')?>:
+ <input type="text" name="tags" value="<?=htmlentities($tags)?>" /> <?=_('(separate with spaces)')?>
+ </div>
<div class="paragraph">
<?=_('License')?>:
<select name="license">
diff --git a/head.php b/head.php
index 5b28e6f..4784a3f 100644
--- a/head.php
+++ b/head.php
@@ -43,7 +43,12 @@ if(isset($_COOKIE['PHPSESSID'])) // TODO: See if there's a better way to check i
}
$loginlink=BASEURL.'/login?returnto='.urlencode($_SERVER['REQUEST_URI']);
$logoutlink=BASEURL.'/logout?returnto='.urlencode($_SERVER['REQUEST_URI']);
-$search=htmlentities(isset($_GET['q'])?$_GET['q']:'');
+if($path[1]=='tag')
+{
+ $search='tag:'.htmlentities($path[2]);
+}else{
+ $search=htmlentities(isset($_GET['q'])?$_GET['q']:'');
+}
?>
<!DOCTYPE html>
<html lang="en">
diff --git a/index.php b/index.php
index 3f2b34c..b45be62 100644
--- a/index.php
+++ b/index.php
@@ -40,6 +40,7 @@ switch($path[1])
case 'user': include('user.php'); break;
case 'thing': include('thing.php'); break;
case 'browse':
+ case 'tag':
case 'search': include('search.php'); break;
case 'license': include('license.php'); break;
case 'editprofile': include('editprofile.php'); break;
diff --git a/rpc_search.php b/rpc_search.php
index 57b0a3f..2ce2b3e 100644
--- a/rpc_search.php
+++ b/rpc_search.php
@@ -21,7 +21,7 @@ include_once('db.php');
include_once('files.php');
// If you experience problem with slashes in searches you may need 'AllowEncodedSlashes NoDecode' in your Apache config (outside of <Directory>. More info at https://httpd.apache.org/docs/current/mod/core.html#allowencodedslashes )
$words=explode(' ', urldecode($path[3]));
-$order=$path[4]; // TODO: Test (at time of writing there was only one 'thing')
+$order=$path[4];
$count=(int)$path[5];
$skip=(int)$path[6];
if(!$count){$count=10;}
@@ -60,7 +60,8 @@ foreach($words as $word)
// Negative matches
$like='like';
$or='or';
- if(substr($word,0,1)=='-'){$like='not like'; $or='and'; $word=substr($word,1);}
+ $in='in';
+ if(substr($word,0,1)=='-'){$like='not like'; $or='and'; $in='not in'; $word=substr($word,1);}
// Escape
$word=addcslashes(mysqli_real_escape_string($db, $word), '%_');
if($q!=''){$q.=' and ';}
@@ -68,6 +69,12 @@ foreach($words as $word)
{
case 'name': $q.='(name '.$like.' "%'.$word.'%")'; break;
case 'description': $q.='(description '.$like.' "%'.$word.'%")'; break;
+ case 'tag':
+ $ids=Array();
+ $res=mysqli_query($db, 'select tagmaps.thing from tags, tagmaps where tagmaps.tag=tags.id and tags.name like "'.$word.'"');
+ while($row=mysqli_fetch_row($res)){$ids[]=$row[0];}
+ $q.='(id '.$in.' ('.implode(',', $ids).'))';
+ break;
default: $q.='(name '.$like.' "%'.$word.'%" '.$or.' description '.$like.' "%'.$word.'%")'; break;
}
}
diff --git a/rpc_thing.php b/rpc_thing.php
index 3c4a854..760d58b 100644
--- a/rpc_thing.php
+++ b/rpc_thing.php
@@ -31,6 +31,7 @@ $thing=Array('id'=>$id,
'files'=>Array(),
'license'=>Array('name'=>$row['license']));
$user=(int)$row['user'];
+$revid=(int)$row['id'];
// List files
$res=mysqli_query($db, 'select name, hash from files where thing='.(int)$row['id']);
while($row=mysqli_fetch_assoc($res))
@@ -55,5 +56,12 @@ if($thing['license']['name']!='other')
$res=mysqli_query($db, 'select simple from licenses where name="'.$name.'"');
if($row=mysqli_fetch_row($res)){$thing['license']['simple']=$row[0];}
}
+// Tags
+$thing['tags']=Array();
+$res=mysqli_query($db, 'select tags.name from tags, tagmaps where tags.id=tagmaps.tag and tagmaps.thing='.$revid);
+while($row=mysqli_fetch_row($res))
+{
+ $thing['tags'][]=$row[0];
+}
print(json_encode($thing));
?>
diff --git a/search.php b/search.php
index 1b7f112..373c751 100644
--- a/search.php
+++ b/search.php
@@ -29,6 +29,7 @@ $page=(int)(isset($_GET['page'])?$_GET['page']:0);
$perpeer=round($perpage/count($peers));
$sortby=(isset($_GET['sort'])?$_GET['sort']:'new');
if($path[1]=='browse'){$sortby=$path[2];} // Handle 'Browse' links
+else if($path[1]=='tag'){$_GET['q']='tag:'.$path[2];} // Handle 'Tag' links
$res=rpc_search($peers, 'search/'.urlencode($_GET['q']).'/'.urlencode($sortby).'/'.$perpeer.'/'.($page*$perpage));
// Construct navigation links for pages
$pagefull=false;
diff --git a/setup.php b/setup.php
index e0684c6..c0d3b8c 100644
--- a/setup.php
+++ b/setup.php
@@ -33,7 +33,7 @@ if($sessiontime<3*3600)
}
// Max upload filesize
$maxsize=-1;
-for(Array('upload_max_filesize','post_max_size') as $name)
+foreach(Array('upload_max_filesize','post_max_size') as $name)
{
$size=ini_get($name);
switch(strtoupper(substr($size,-1)))
diff --git a/thing.php b/thing.php
index b22126e..25eb61a 100644
--- a/thing.php
+++ b/thing.php
@@ -59,8 +59,15 @@ foreach($thingobj['files'] as $file)
if(isset($file['preview3d'])){$files.='<div class="boxtop" style="right:0px;"><a href="#" onclick="return threedview(this,'.PREVIEW_SIZE[0].','.PREVIEW_SIZE[1].');" data-file="https://'.$thing[1].$file['preview3d'].'">3D view</a></div>';}
$files.='<a href="https://'.$thing[1].$file['path'].'" download="'.htmlentities($file['name']).'" type="'.$type.'"><div class="boxbottom">'.htmlentities($file['name']).'</div><img src="https://'.$thing[1].$file['preview'].'" /></a></div>';
}
+$tags='';
+foreach($thingobj['tags'] as $tag)
+{
+ $tag=htmlentities($tag);
+ $tags.=' <a href="'.BASEURL.'/tag/'.$tag.'">'.$tag.'</a>';
+}
?>
<h1><?=$name?> <small class="subheader">by <?=$by?></small></h1>
<small><?=sprintf(_('Published on %s under the license %s'), '<span class="time">'.htmlentities($thingobj['date']).'</span>', $license)?></small><br />
<?=$description?><br />
+Tags: <?=$tags?><br />
<?=$files?>