Skip to content

Instantly share code, notes, and snippets.

@walterdavis
Created March 3, 2012 21:47
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 walterdavis/1968447 to your computer and use it in GitHub Desktop.
Save walterdavis/1968447 to your computer and use it in GitHub Desktop.
HTML index and RSS feed from a folder of photos
<?php
//CONFIGURATION:
//feed name & details
$title = 'My Fabulous Photos';
$subtitle = 'A fabulous collection of photos.';
$author = 'Richard Avedon';
$limit = 20; //only 20 photos
date_default_timezone_set ( 'America/New_York' );
/**
* Place this file in a folder full of images to publish an RSS feed of the photos.
*
* - Files are sorted latest-first by modification date.
*
* - If present, IPTC comments will be used for the captions. If not,
* file names are used, (underscore is replaced with a space, extension
* is removed).
*
* - JPEG, GIF, and PNG images are supported
*
* - Requires PHP5
*
* LICENSE (BSD License)
*
* Copyright (c) 2012, Walter Lee Davis <waltd@wdstudio.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
//FUNCTIONS
/**
* recursive variable replacement
*
* @param string $strText Input text including {{variable}} placeholders
* @return string with all templates replaced with existing global vars
* @author Walter Lee Davis
*/
function replace_vars($strText){
$out = preg_replace_callback('/\{\{(.+?)\}\}/im','replace_vars_callback',$strText);
if(strpos($out,'{{') !== false) {
return replace_vars($out);
}
return $out;
}
function replace_vars_callback($arrMatches){
$match = $arrMatches[1];
if(isset($GLOBALS[$match])) return $GLOBALS[$match];
//the spaces between the brackets here are Unicode Word Joiner characters (U+2060)
//not actual spaces. This keeps away the infinite loop.
return '{⁠{' . $match . '}⁠}';
}
/**
* test for variable present and not empty
*
* @param string $var Variable name
* @return void
* @author Walter Lee Davis
*/
function present($var){
return (isset($var) && !empty($var));
}
/**
* IPTC caption reader
*
* @param string $path path to file
* @return string
* @author Tim Plumb
*/
function readCaption($path){
$IPTC_Caption = "";
$size = getimagesize( $path, $info );
if (isset($info["APP13"])) {
if($iptc = iptcparse( $info["APP13"] ) ) {
$IPTC_Caption = present($iptc["2#005"][0]) ? $iptc["2#005"][0] : $iptc["2#120"][0];
}
}
return( $IPTC_Caption );
}
/**
* file extension from filename
*
* @param string $path
* @return string
* @author Walter Lee Davis
*/
function extension($path){
return strtolower(array_pop(explode('.',$path)));
}
/**
* read the current request and return the full external URL
*
* @return string
* @author Walter Lee Davis
*/
function absolute_url() {
$out = 'http' . (($_SERVER['HTTPS'] == 'on') ? 's' : '') . '://';
$out .= $_SERVER['SERVER_NAME'] . (($_SERVER['SERVER_PORT'] != '80') ? $_SERVER['SERVER_PORT'] : '');
return $out . $_SERVER['PHP_SELF'];
}
function h($key){
return htmlentities($key,ENT_COMPAT,'UTF-8');
}
//CONSTANTS
$url = dirname(absolute_url()) . '/';
$mimes = array('jpeg' => 'photo/jpeg', 'jpg' => 'photo/jpeg', 'png' => 'photo/png', 'gif' => 'photo/gif');
$dir = dirname(__FILE__);
$me = basename(__FILE__);
$files = $photos = $times = $entries = $figures = array();
$latest = $i = 0;
//MAIN LOOP
//read the directory
foreach(scandir($dir) as $file){
if(preg_match('/^[^\.].+?\.(jpe?g|png|gif)$/i', $file)){
$timestamp = filemtime($dir . '/' . $file);
$latest = ($timestamp > $latest) ? $timestamp : $latest;
$files[] = $file;
$times[] = $timestamp;
}
}
$page_updated = date('F j, Y, g:i a', $latest);
$pubdate = date('Y-m-d H:i\Z', $latest);
$latest = gmdate('Y-m-d\TH:i:s\Z',$latest);
//sort by date descending
arsort($times);
foreach($times as $k => $v){
$photos[] = array($files[$k], $times[$k]);
}
//templates
$feed = '<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>
{{title}}
</title>
<updated>
{{latest}}
</updated>
<author>
<name>
{{author}}
</name>
</author>
<id>
{{url}}
</id>
<subtitle>
{{subtitle}}
</subtitle>
<link rel="self" href="{{url}}feed.php"/>
{{entries}}
</feed>
';
$page = '<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>{{title}}</title>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link rel="alternate" type="application/rss+xml" title="RSS" href="{{url}}{{me}}?mode=rss" />
<style type="text/css">
body {
background-color: #ccc;
padding: 40px 0;
margin: 0;
font-family: "Lucida Grande", "Lucida Sans Unicode", Lucida, sans-serif;
font-size: 90%;
color: #333;
}
header, section {
width: 75%;
margin: auto;
}
figure {
padding: 2em;
margin: 2em 0;
border: 1px solid #999;
background-color: #fff;
box-shadow: 0 8px 20px #666;
}
figure img {
width: 100%;
border: 1px solid #ccc;
margin: 0 0 .5em
}
figcaption {
font-style: italic;
text-align: center;
}
.right {
float: right;
}
h1 {
overflow: hidden;
border-bottom: 1px solid #999;
padding-bottom: .2em
}
/* RSS feed icon in CSS3! */
span.rss{float:right;width:36px;height:36px;margin:0;font:0.475em/1 Arial,sans-serif;}
.rss a{width:32px;height:32px;display:inline-block;overflow:hidden;border:1px solid transparent;
line-height:32px;text-decoration:none;text-shadow:0 -1px 0 rgba(0,0,0,0.5);-moz-border-radius:6px;
-webkit-border-radius:6px;border-radius:6px;position:relative;padding:0 0 0 2px;border-color:#ea6635;
text-transform:lowercase;text-indent:-900px;font-size:22px;font-weight:bold;color:#fff;background:#e36443;
-moz-box-shadow:0 0 4px rgba(0,0,0,0.4);-webkit-box-shadow:0 0 4px rgba(0,0,0,0.4);
box-shadow:0 0 4px rgba(0,0,0,0.4);background:-webkit-gradient(linear,left top,left bottom,from(#f19242),to(#e36443));
background:-moz-linear-gradient(top,#f19242,#e36443);background:linear-gradient(top,#f19242,#e36443);}
.rss a:before{width:6px;height:6px;background:#fff;-moz-border-radius:6px;-webkit-border-radius:6px;
border-radius:6px;}.rss a:before,.rss a:after{content:"";position:absolute;bottom:5px;left:5px;}
.rss a:hover,.rss a:focus,.rss a:active{opacity:0.8;border-color:#000;}.rss a:after{width:11px;height:11px;
border-style:double;border-width:12px 12px 0 0;border-color:#fff;-moz-border-radius:0 25px 0 0;
-webkit-border-radius:0 25px 0 0;border-radius:0 25px 0 0;}
</style>
</head>
<body>
<header>
<h1><span class="rss"><a rel="alternate" type="application/rss+xml" title="Subscribe to our RSS feed" href="{{url}}{{me}}?mode=rss">RSS</a></span>{{title}}</h1>
<p id="subtitle">{{subtitle}}</p>
<p id="author">{{author}}</p>
<p id="pubdate">Last update: <time pubdate="{{pubdate}}">{{page_updated}}</time></p>
</header>
<section id="pictures">
{{figures}}
</section>
</body>
</html>
';
$entry = ' <entry>
<title type="html">
{{item_title}}
</title>
<id>
{{id}}
</id>
<link rel="enclosure" type="{{mime_type}}" href="{{url}}{{photo}}" />
<link rel="alternate" href="{{url}}{{photo}}" />
<summary type="html">
{{image_tag}}
</summary>
<published>
{{published}}
</published>
<updated>
{{published}}
</updated>
</entry>
';
$figure = ' <figure>
{{image}}
<figcaption>
{{figure_title}} — Posted <time datetime="{{datetime}}">{{posted}}</time>
</figure>
</figure>
';
//build the current set of entries as an array of strings
foreach($photos as $photo){
if($i < $limit){
$published = gmdate('Y-m-d\TH:i:s\Z',$photo[1]);
$datetime = date('Y-m-d H:i\Z', $photo[1]);
$posted = date('F j, Y, g:i a', $photo[1]);
$id = $url . $photo[0];
$item_title = readCaption($dir . '/' . $photo[0]);
$mime_type = $mimes[extension($photo[0])];
if ($item_title === ""){
$item_title = preg_replace('/_/', ' ', preg_replace('/\.[a-zA-Z]{3,4}$/','',$photo[0]));
}
$figure_title = $item_title;
$item_title = h($figure_title);
$image = '<img src="' . $id . '" alt="' . $item_title . '" />';
$image_tag = h($image . '<br /><br />');
$entries[] = replace_vars($entry);
$figures[] = replace_vars($figure);
$i++;
}else{
break;
}
}
//concatenate into a single string
$entries = implode("\n",$entries);
$figures = implode("\n",$figures);
//build the outer shell
$feed = replace_vars($feed);
$page = replace_vars($page);
//send the result
if(present($_GET['mode']) && $_GET['mode'] == 'rss'){
header('Content-type: application/xml; charset=utf-8');
print $feed;
}else{
header('Content-type: text/html; charset=utf-8');
print $page;
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment