Skip to content

Instantly share code, notes, and snippets.

@kennydude
Last active September 27, 2015 19:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kennydude/cbc0e023aafc5284658f to your computer and use it in GitHub Desktop.
Save kennydude/cbc0e023aafc5284658f to your computer and use it in GitHub Desktop.
microgallery

Microgallery

Microgallery is the newer and better version of newb. Sadly it is slightly bigger (18kb vs 12kb) but it's got a few more features.

Could be better, but it does the job for me :)

  • OOP design
  • Darker oooh
  • Mass updater
  • Descriptions!
  • Remove rubbish I didn't use (Lightbox + minor others)
  • Posts with no tags page
  • No external libraries/css required!
  • Single file
<?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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment