|
<?php |
|
/* |
|
Micro Gallery by Joe |
|
|
|
OOP flat-file micro single-file gallery for php |
|
|
|
requires php5 for generators, oop etc |
|
*/ |
|
define('PER_PAGE', 20); |
|
|
|
function array_empty($a){ |
|
return count($a) == 0; |
|
} |
|
|
|
// Each item in the gallery |
|
class GalleryItem { |
|
public static $videoFormats = array( |
|
'mp4', 'avi' |
|
); |
|
|
|
public function __construct($data){ |
|
$this->file = $data['file']; |
|
$this->tags = $data['tags']; |
|
$this->description = $data['description']; |
|
$this->mtime = $data['mtime']; |
|
$this->id = md5($this->file); |
|
} |
|
|
|
public function export(){ |
|
return array( |
|
"file" => $this->file, |
|
"tags" => $this->tags, |
|
"description" => $this->description, |
|
"mtime" => $this->mtime |
|
); |
|
} |
|
|
|
public function is_video(){ |
|
$ext = pathinfo($this->file, PATHINFO_EXTENSION); |
|
return in_array($ext, self::$videoFormats); |
|
} |
|
|
|
public function image(){ |
|
if($this->is_video()){ |
|
echo '<video src="'. $this->file .'" controls></video>'; |
|
} else{ |
|
echo '<img src="' . $this->file . '" />'; |
|
} |
|
} |
|
|
|
public function render(){ |
|
echo '<label class="btnEdit" title="mark to mass edit"><input type="checkbox" data-id="'. $this->id .'" class="massEdit" /></label>'; |
|
$this->image(); |
|
echo '<div id="view'. $this->id .'" class="view">'; |
|
echo '<a class="btnEdit" onclick="editMode(\'' . $this->id . '\')">edit</a>'; |
|
echo '<div class="description">'; |
|
echo $this->description; |
|
echo '</div>'; |
|
echo '<div class="tags">'; |
|
foreach ($this->tags as $tag) { |
|
echo '<a class="tag" href="' . GalleryTagPage::getUrl($tag) . '">'; |
|
echo $tag; |
|
echo '</a>'; |
|
} |
|
if(array_empty($this->tags)){ |
|
echo 'no tags'; |
|
} |
|
echo '</div>'; |
|
echo '</div>'; |
|
echo '<div id="edit'. $this->id .'" class="edit">'; |
|
echo '<form method="post" action="' . GalleryItemUpdatePage::getUrl($this->id) . '">'; |
|
echo '<textarea class="description" placeholder="description (html)" name="description">' . $this->description . '</textarea>'; |
|
echo '<input type="text" name="tags" value="' . implode(',', $this->tags) . '" placeholder="tags" />'; |
|
echo '<a href="'.GalleryItemDeletePage::getUrl($this->id).'" class="btnEdit">rm</a>'; |
|
echo '<button class="btnEdit" type="submit">save</button>'; |
|
echo '</form>'; |
|
echo '</div>'; |
|
} |
|
|
|
public function hasTag($tag){ |
|
if($tag == 'noTag'){ |
|
return array_empty($this->tags); |
|
} else{ |
|
return in_array($tag, $this->tags); |
|
} |
|
} |
|
|
|
public function remove(){ |
|
$dir = dirname(__file__); |
|
unlink("$dir/" . $this->file); |
|
} |
|
} |
|
|
|
// Gallery itself |
|
class Gallery { |
|
public function __construct($file){ |
|
$this->file = $file; |
|
$this->contents = array(); |
|
|
|
$data = json_decode(file_get_contents($file), true); |
|
foreach ($data as $k => $item) { |
|
$this->contents[$k] = new GalleryItem($item); |
|
} |
|
|
|
usort($this->contents, function($a, $b) { |
|
return $a->mtime < $b->mtime; |
|
}); |
|
} |
|
|
|
public function tags(){ |
|
$tags = []; |
|
foreach ($this->contents as $file) { |
|
foreach ($file->tags as $tag) { |
|
if(!$tags[$tag]){ |
|
$tags[$tag] = [ |
|
"title" => $tag, |
|
"c" => 0 |
|
]; |
|
} |
|
$tags[$tag]['c'] += 1; |
|
} |
|
} |
|
|
|
usort($tags, function($a, $b) { |
|
return $a['c'] < $b['c']; |
|
}); |
|
return $tags; |
|
} |
|
|
|
public function get($start = 0, $length = PER_PAGE){ |
|
return array_slice($this->contents, $start, $length); |
|
} |
|
|
|
public function tag($tag, $start = 0, $length = PER_PAGE){ |
|
foreach ($this->contents as $file) { |
|
if($file->hasTag($tag)){ |
|
yield $file; |
|
} |
|
} |
|
} |
|
|
|
public function id($id){ |
|
foreach ($this->contents as $file) { |
|
if($file->id == $id){ |
|
return $file; |
|
} |
|
} |
|
} |
|
|
|
public function has($filename){ |
|
foreach ($this->contents as $file) { |
|
if($file->file == $filename){ |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
public function remove($file){ |
|
$file->remove(); |
|
$key = array_search($file, $this->contents); |
|
if($key !== false){ |
|
unset($this->contents[$key]); |
|
} |
|
} |
|
|
|
public function add($file){ |
|
$this->contents[] = $file; |
|
} |
|
|
|
public function save(){ |
|
$output = array(); |
|
foreach ($this->contents as $file) { |
|
$output[] = $file->export(); |
|
} |
|
file_put_contents($this->file, json_encode($output)); |
|
} |
|
} |
|
|
|
// Base page rendered |
|
class GalleryPage { |
|
public function __construct($gallery){ |
|
$this->gallery = $gallery; |
|
$this->from = $_GET['from']; |
|
} |
|
|
|
public function render(){ |
|
?> |
|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, minimum-scale=1.0"> |
|
|
|
<title>microgallery</title> |
|
|
|
<style type="text/css"> |
|
body{ |
|
font-family: "Lucida Grande", sans-serif; |
|
background: #333; |
|
color: #FFF; |
|
padding-top: 70px; |
|
} |
|
*{ |
|
box-sizing: border-box; |
|
} |
|
#content{ |
|
margin: 0 auto; |
|
max-width: 600px; |
|
} |
|
a{ |
|
color: #FFF; |
|
} |
|
a.tag{ |
|
background: #666; |
|
padding: 5px; |
|
display: inline-block; |
|
margin-right: 5px; |
|
margin-bottom: 5px; |
|
} |
|
.item img, .item video{ |
|
max-width: 100%; |
|
} |
|
.gallery li{ |
|
list-style-type: none; |
|
padding-bottom: 10px; |
|
border-bottom: 1px solid #444; |
|
margin-bottom: 10px; |
|
} |
|
.gallery{ |
|
padding: 0px; |
|
} |
|
.btnEdit{ |
|
float: right; |
|
padding: 5px; |
|
background: #aaa; |
|
cursor: pointer; |
|
outline: none; |
|
border: 0px; |
|
color: #fff; |
|
vertical-align: middle; |
|
font-size: 14px; |
|
text-decoration: underline; |
|
display: inline-block; |
|
margin: 0; |
|
margin-right: 5px; |
|
margin-bottom: 5px; |
|
} |
|
.yes{ |
|
background: #993300; |
|
} |
|
.i{ |
|
padding: 10px; |
|
background: #339900; |
|
} |
|
.edit{ |
|
display: none; |
|
} |
|
textarea.description{ |
|
display: block; |
|
width:100%; |
|
height: 50px; |
|
} |
|
.description{ |
|
margin-bottom: 10px; |
|
} |
|
#tabs{ |
|
position: fixed; |
|
top: 0; left: 0; right: 0; |
|
background: #444; |
|
padding: 5px; |
|
text-align: center; |
|
} |
|
.q{ |
|
background: #CC3300; |
|
padding: 10px; |
|
} |
|
.inline{display: inline-block;} |
|
hr{ border: 0; border-bottom: 1px solid #ccc; } |
|
.clear{ clear: both; } |
|
.main.tags .tag{ |
|
font-size: 11px; |
|
padding: 3px; |
|
margin-bottom: 1px; |
|
} |
|
td img, td video{ |
|
width: 100%; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div id="content"> |
|
<?php |
|
$msg = ''; |
|
switch($_GET['message']){ |
|
case "deleted": |
|
$msg = 'Item deleted'; |
|
break; |
|
case "updated": |
|
$msg = 'Item updated'; |
|
break; |
|
case "mass-update": |
|
$msg = 'All items updated'; |
|
break; |
|
} |
|
if($msg != ''){ |
|
echo '<div class="i">' . $msg . '</div>'; |
|
} |
|
|
|
$this->header(); |
|
$this->page(); |
|
?> |
|
</div> |
|
|
|
<script type="text/javascript"> |
|
function editMode(id){ |
|
document.getElementById("edit" + id).style.display = "block"; |
|
document.getElementById("view" + id).style.display = "none"; |
|
return false; |
|
} |
|
function massEdit(){ |
|
var meds = document.getElementsByClassName("massEdit"); |
|
var url = "?action=mass"; |
|
for(var i = 0; i < meds.length; i++){ |
|
var m = meds[i]; |
|
if(m.checked){ |
|
url += "&id[]=" + m.getAttribute("data-id"); |
|
} |
|
} |
|
document.location.href = url; |
|
} |
|
</script> |
|
</body> |
|
</html> |
|
<?php |
|
} |
|
|
|
public function header(){ |
|
?> |
|
<div id="tabs"> |
|
<a class="tag" href="<?php echo GalleryHomePage::getUrl(); ?>">Home</a> |
|
<a class="tag" href="<?php echo GalleryUpdatePage::getUrl(); ?>">Update</a> |
|
<a class="tag" href="<?php echo GalleryNoTagPage::getUrl(); ?>">Posts with no tags</a> |
|
<form method="get" class="inline" href="<?php echo GalleryTagPage::getUrl(''); ?>"> |
|
<input placeholder="jump to tag" class="inline" type="text" name="tag" list="tags" /> |
|
<datalist id="tags"> |
|
<?php foreach ($this->gallery->tags() as $tag) { ?> |
|
<option value="<?php echo $tag; ?>" /> |
|
<?php } ?> |
|
</datalist> |
|
<input type="hidden" name="action" value="tag"/> |
|
</form> |
|
<a class="tag massEditButton" href="#" onclick="massEdit()">mass edit</a> |
|
</div> |
|
<?php |
|
echo '<h1><a href="?">microgallery</a></h1>'; |
|
echo '<div class="main tags">'; |
|
foreach ($this->gallery->tags() as $tag) { |
|
echo '<a class="tag" href="'; |
|
echo GalleryTagPage::getUrl($tag['title']); |
|
echo '">'; |
|
echo $tag['title'] . ' (' . $tag['c'] . ')'; |
|
echo '</a>'; |
|
} |
|
echo '</div>'; |
|
} |
|
|
|
public function gallery($contents){ |
|
echo '<ul class="gallery">'; |
|
$i = 0; |
|
foreach ($contents as $item) { |
|
echo '<li class="item">'; |
|
$item->render(); |
|
echo '</li>'; |
|
$i++; |
|
} |
|
echo '</ul>'; |
|
if($i == PER_PAGE){ |
|
echo '<a href="' . $this->getUrl() . '&from=' . ($this->from+PER_PAGE) . '">'; |
|
echo 'View more</a>'; |
|
} |
|
} |
|
|
|
public function redirect($url){ |
|
header("Location: " . $url); |
|
echo 'Redirecting...'; |
|
} |
|
} |
|
|
|
// Home page |
|
class GalleryHomePage extends GalleryPage { |
|
public function page(){ |
|
$this->gallery($this->gallery->get($this->from)); |
|
} |
|
|
|
public static function getUrl(){ |
|
return '?action=home'; |
|
} |
|
} |
|
|
|
// Item update page |
|
class GalleryItemUpdatePage extends GalleryPage { |
|
public static function getUrl($id = null){ |
|
return '?action=update&id=' . $id; |
|
} |
|
|
|
public function render(){ |
|
$file = $this->gallery->id($this->id); |
|
$file->description = $_POST['description']; |
|
$file->tags = explode(",", $_POST['tags']); |
|
$this->gallery->save(); |
|
$this->redirect(GalleryHomePage::getUrl() . "&message=updated"); |
|
} |
|
} |
|
|
|
// Item delete page |
|
class GalleryItemDeletePage extends GalleryPage { |
|
public static function getUrl($id = null){ |
|
return '?action=delete&id=' . $id; |
|
} |
|
|
|
public function render(){ |
|
$this->file = $this->gallery->id($this->id); |
|
if(!$_POST['sure']){ |
|
return parent::render(); |
|
} else{ |
|
$this->gallery->remove($this->file); |
|
$this->gallery->save(); |
|
$this->redirect(GalleryHomePage::getUrl() . "&message=deleted"); |
|
} |
|
} |
|
|
|
public function header(){} |
|
|
|
public function page(){ |
|
?> |
|
<form method="post" action="<?php echo $this->getUrl($this->id); ?>"> |
|
<div class="q"> |
|
Are you sure you wish to delete this post? This cannot be undone |
|
<hr/> |
|
<input type="hidden" name="sure" value="yes" /> |
|
<button class="btnEdit yes">Yes</button> |
|
<a class="btnEdit" href="<?php echo GalleryHomePage::getUrl(); ?>">No</a> |
|
<div class="clear"></div> |
|
</div> |
|
</form> |
|
<?php |
|
$this->file->render(); |
|
} |
|
} |
|
|
|
// Tag page |
|
class GalleryTagPage extends GalleryPage { |
|
public static function getUrl($tag = null){ |
|
if($tag == null){ |
|
$tag = $_GET['tag']; |
|
} |
|
return "?action=tag&tag=" . $tag; |
|
} |
|
|
|
public function page(){ |
|
echo '<h2>posts with tag ' . $this->tag . '</h2>'; |
|
$this->gallery($this->gallery->tag($this->tag, $this->from)); |
|
} |
|
} |
|
|
|
// Posts with no tags |
|
class GalleryNoTagPage extends GalleryPage{ |
|
public static function getUrl(){ |
|
return "?action=notags"; |
|
} |
|
|
|
public function page(){ |
|
echo '<h2>posts with no tags</h2>'; |
|
$this->gallery($this->gallery->tag("noTag", $this->from)); |
|
} |
|
} |
|
|
|
// Update from folder |
|
class GalleryUpdatePage extends GalleryPage{ |
|
public static function getUrl(){ |
|
return "?action=newfiles"; |
|
} |
|
|
|
public static $allowedExtensions = [ |
|
"png", "mp4", "jpg", "gif" |
|
]; |
|
|
|
public function page(){ |
|
$files = array_merge( |
|
glob('*'), |
|
glob('**/*') |
|
); |
|
|
|
echo "<h2>update newb database</h2>"; |
|
$o = ''; $new = 0; |
|
$url = "?action=mass"; |
|
|
|
foreach ($files as $file) { |
|
$o_file = $file; |
|
$ext = pathinfo($o_file, PATHINFO_EXTENSION); |
|
if(!in_array($ext, self::$allowedExtensions)){ |
|
continue; |
|
} |
|
if(is_dir($o_file)) continue; |
|
if(stripos($o_file, "newb.php") != FALSE) continue; |
|
if(stripos($o_file, "newb.json") !== FALSE) continue; |
|
|
|
if($this->gallery->has($o_file)) continue; |
|
|
|
$o .= "[new] "; |
|
$o .= "$o_file<br/>"; |
|
$new += 1; |
|
$r = new GalleryItem([ |
|
"file" => $file, |
|
"mtime" => filemtime($o_file), |
|
"tags" => array(), |
|
"id" => count($data) |
|
]); |
|
$url .= "&id[]=" . $r->id; |
|
$this->gallery->add($r); |
|
} |
|
$this->gallery->save(); |
|
|
|
echo "<div class='i'>$new new files were added "; |
|
echo "<a href='$url'>edit them</a>"; |
|
echo "</div>"; |
|
|
|
echo "<pre>$o</pre>"; |
|
} |
|
} |
|
|
|
// Mass update |
|
class GalleryMassUpdatePage extends GalleryPage { |
|
public function render(){ |
|
if(!$_POST){ |
|
return parent::render(); |
|
} else{ |
|
foreach ($_POST['items'] as $id => $value) { |
|
$file = $this->gallery->id($id); |
|
$file->tags = explode(",", $value['tags']); |
|
$file->description = $value['description']; |
|
} |
|
$this->gallery->save(); |
|
$this->redirect(GalleryHomePage::getUrl() . "&message=mass-update"); |
|
} |
|
} |
|
|
|
public function page(){ |
|
echo "<h2>mass update</h2>"; |
|
echo "<form method='post' action='?action=mass'>"; |
|
echo "<table style='width: 100%'>"; |
|
foreach($_GET['id'] as $id){ |
|
$file = $this->gallery->id($id); |
|
echo "<tr>"; |
|
echo "<td style='width: 200px'>"; |
|
$file->image(); |
|
echo "</td><td style='width: auto'>"; |
|
echo '<textarea class="description" placeholder="description (html)" name="items['.$file->id.'][description]">' . $file->description . '</textarea>'; |
|
echo '<input type="text" name="items['.$file->id.'][tags]" value="' . implode(',', $file->tags) . '" placeholder="tags" />'; |
|
echo "</td>"; |
|
echo "</tr>"; |
|
} |
|
echo "</table>"; |
|
echo '<button class="btnEdit" type="submit">save</button>'; |
|
echo '</form>'; |
|
} |
|
} |
|
|
|
$g = new Gallery("newb.json"); |
|
|
|
if(!$_GET['action']){ |
|
$_GET['action'] = 'home'; |
|
} |
|
switch ($_GET['action']) { |
|
case 'home': |
|
$page = new GalleryHomePage($g); |
|
$page->render(); |
|
break; |
|
|
|
case 'tag': |
|
$page = new GalleryTagPage($g); |
|
$page->tag = $_GET['tag']; |
|
$page->render(); |
|
break; |
|
|
|
case 'delete': |
|
$page = new GalleryItemDeletePage($g); |
|
$page->id = $_GET['id']; |
|
$page->render(); |
|
break; |
|
|
|
case 'update': |
|
$page = new GalleryItemUpdatePage($g); |
|
$page->id = $_GET['id']; |
|
$page->render(); |
|
break; |
|
|
|
case 'notags': |
|
$page = new GalleryNoTagPage($g); |
|
$page->render(); |
|
break; |
|
|
|
case 'newfiles': |
|
$page = new GalleryUpdatePage($g); |
|
$page->render(); |
|
break; |
|
|
|
case 'mass': |
|
$page = new GalleryMassUpdatePage($g); |
|
$page->render(); |
|
break; |
|
|
|
default: |
|
die('Action ' . $_GET['action'] . ' not found?'); |
|
break; |
|
} |