I’m proud to announce the release of phtagr version 2.1.3
You can download the new version from sourceforge at Sourceforge. Read How To Migrate for migration help.
Here are the improvements since 2.1.2
- Bread Crumb Search The search term is splitted into single search bread crumbs. The search crumbs simplifies the customization of the search
- Fast preview file responder Now the images are loaded faster and are fast as hell. It by-passes the MVC CakePHP framework stack based on the HTTP session
- Translations Added Estonian (Craig), Polish (Bartosz Fenski aka fEnIo), Italian (Francesco Piraneo G) translation
- Mobile Device Support If your are open the gallery with your mobile device a new customized mobile theme is loaded automatically.
- Email Notification Add email notification for new media which could be triggered by cron job. The notification interval could be set from half an hour to once a month
- Improved Group Handing The creation and managing of groups is simplified which makes the gallery more social. Try it!
- SMTP Configuration You can now configure your own SMTP configuration in app/config/core.php to enable email functionality to your web gallery
- Improved the preview manager. Now a preview might depend on the next larger preview and a preview is not always calculated by the original file which saves a lot of time for smaller previews.
- Add interval search in the explorer. If you already set a time limit of your search you can easily add another time bound for an interval search.
- View media based on upload path
- Add sync shell task to sync all media at once (console based)
- Add fallback of image size. In rare cases the image size could not extracted correcly. Now getimagesize() should fix this gap.
- Auto completion for tag, category, and location – also in bread crumbs.
- And lots of bug fixes…
The next release should get a face lift with a backend and easy customizable frontend – besides tagging, sharing and searching improvements
Stay tuned!
My quest for an open source photo gallery started when my flickr pro account needed renewal, I already had a domain and website, and I thought to myself: “I don’t need to pay flickr, I can do it myself“. So I downloaded all of my flickr photos (about 2 gigs worth) to my local computer and they were organized in directories as they were on flickr. Then I started the search for an open source solution that would be easy to migrate all of my photos to, and what I settled on
was zen photo because I could basically just upload everything with FTP and it would be organized as it was on flickr. It was quick and easy to get setup, however over time what I found was that managing the photos in zen photo was a real pain. The user interface is very cumbersome, and clunky, often times I would make some sort of change only to find later that the change never took because I didn’t scroll waaaay down and hit the “save” button. I also found zenphoto visually
unappealing, even though I could potentially create my own theme, I am not a web designer or an artist so any theme I come up with would be even uglier.
After about a year with zen photo, I decided to start looking around again and that is when I found phtagr, it is a very attractive solution, mass editing and mass uploads are easy, the user interface is intuitive, and is much more flickr like. The best part is that when I first started using it, I couldn’t get it to work on my webhost, so I e-mailed the phtagr mailing list and Xemle responded quickly and was able to fix my problem. Now it works flawlessly…
Thanks for the hard work.
– by Chris Desjardins
Happy New Year to All!
By today phTagr has now an Italian translation – thanks to Francesco Piraneo G. for his great contribution. The newly updated zip file could be downloaded from SourceForge.
If your language is missing, see How To Translate in the wiki.
Enjoy!

Baker – The Simple and Free Online Source Editor for CakePHP is published! Baker is a basic online text editor to write text, HTML, or PHP server files online. It’s a CakePHP application and you can do most basic text editor operations.
You can view syntax highlighted source files or edit them. Create, copy, rename, move and delete of files and directories are supported, too. You can upload files and extract ZIP archive. Baker has everything of your need to have a quick change of your PHP or HTML source code.
Baker’s Core Features
- Syntax highlighting of PHP and HTML files (through Geshi, thanks to Mark for his plugin)
- Create, Edit, Copy, Rename, Move, Delete files and directories
- Upload files
- Extract ZIP archive on server side
- HTTP User Authentication
- Filter file list
- Different virtual directory roots
You can visit the demo page at http://baker.phtagr.org or download the source from GitHub. Screenshots are available here. See here for installation instructions.
Feel free to use it and to modify it.
Baker was very useful when I developed on phTagr, the free social web gallery, while being in a Internet café far far away with a slow Internet connection. I would love if someone adds SkyWrite Editor (AKA Bespin) to it. For sure Code ‘n Run is more fancy, but it’s not free and coding is fun

Baker - The Simple and Free Online Source Editor for CakePHP

Directory actions with move, copy, and delete

Syntax Highlighting in the View Mode
Recently the preview handling was improved and partly rewritten. The speed boost by the Fast File Responder was previously described here. Before r632 each preview was rendered by the original image which consumed a lot of time. Now an image preview manager was introduced which reuses existing down sampled images to gain speed. While each conversion from the original image costs about 4.4s, the down sampling from an 600px to 220px requires only 0.2s Further each preview configuration (defined in the Preview Manager Component) might be depend on a greater preview image. These dependencies might trigger the generation of all previews before calculating the requested one.
Unfortunately, the preview cache file schema changed and all previews have to be recalculated again. However, since r639 phTagr supports the batch generation of previews with a CakePHP shell script preview. You can create these previews at the command with
$ cd <phtagr base>
$ cake/console/cake preview
Help screen
---------------------------------------------------------------
This shell generates preview images in batch mode.
generate
Generate previews.
---------------------------------------------------------------
Options:
-max count
Maximum generation count. Default is 10. Use 0 to generate all previews.
-start-chunk number
Set the start chunk number. A chunk has a size of 100 media. Default is 1.
-size (mini|thumb|preview|high)
Set the minimum preview size. Default is thumb.
-user username
Generate only previews for given user.
-verbose
Be verbose
---------------------------------------------------------------
$ preview generate -size mini -verbose
Page 1/180 (0.56%)
Page 2/180 (1.11%)
Page 3/180 (1.67%)
...
This article is very useful for all CakePHP applications which deal with lots of photos or other files which are shown in the resulting page. I will introduce an Fast File Responder for CakePHP which immediately sends the file to the client without stepping into the CakePHP stack. It increases the performances drastically to an minimum and got its inspiration from Lightning Fast Caching for CakePHP. The provided approach of the Fast File Responder is used in the open source social web gallery phTagr.
The web gallery phTagr is written on top of the great MVC framework CakePHP and deals with lots of photos. Its photo explorer displays by default 12 photos at once (see demo page). Therefore the CakePHP framework is called 13 times. The first request handles the authorized and the selection of the 12 photos. Then each photo is requested by the client to fetch it and to display it into the page. These requests check again the authorization and correctness of the user for each photo and requires a lot of time since it traverses again the whole MVC stack.
time = 1 x explorer page + 12 x image request = 13 x CakePHP stack = 13 x ~0.40s = 3,40s
Since the first request already checked the authorization of the photos the following 12 requests and checks are redundant and can be omitted. The user session can be used to store these authorization information for the 12 media requests and the photos could be send immediately before the CakePHP stack is called.
time = 1 x explorer page + 12 x fast image request = 1 x CakePHP stack = 0.45s
To do so a Fast File Responder component adds the file information to the session. It adds for each photo request the filename of the preview and an expiration date for safety.
<?php
class FastFileResponderComponent extens Object {
var $controller = null;
var $components = array('Session', 'FileCache');
var $sessionKey = 'fastFile.items';
var $expireOffset = 10; // seconds
function initialize(&$controller) {
$this->controller = $controller;
}
function add($key, $filename) {
if (!is_readable($filename)) {
return false;
}
$files = (array) $this->Session->read($this->sessionKey);
$files[$key] = array('expires' => time() + $this->expireOffset, 'file' => $filename);
$this->Session->write($this->sessionKey, $files);
return true;
}
function addAll($data) {
foreach ($data as $key => $filename) {
$this->add($key, $filename);
}
}
}
?>
Than the app/webroot/index.php is modified to check for fast files before the MVC stack is called.
// ...
if (!defined('CORE_PATH')) {
// ...
}
// Check the request URI if it matches the URI for photos
if (isset($_GET['url']) && preg_match('/media\/\w+\/\d+/', $_GET['url'])) {
require ROOT . DS . APP_DIR . DS . 'fast_file_responder.php';
$fileResponder = new FastFileResponder();
if ($fileResponder->exists()) {
$fileResponder->send();
} else {
$fileResponder->close();
unset($fileResponder);
}
}
if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) {
// ...
}
// ...
Finally the Fast File Responder itself located in app/fast_file_responder.php
<?php
/** This class enables a fast file response without the framework stack of
* CakePHP. It checks the session and the URL and returns a valid file */
class FastFileResponder {
/** Should be same as in app/config/core.php Session.cookie */
var $sessionCookie = 'CAKEPHP';
var $sessionKey = 'fastFile';
var $items = array();
function __construct() {
$this->startSession();
}
/** Starts the session if the session sessionCookie is set */
function startSession() {
if (!isset($_COOKIE[$this->sessionCookie])) {
return;
}
session_id($_COOKIE[$this->sessionCookie]);
session_start();
if (isset($_SESSION[$this->sessionKey])) {
$this->items = (array) $_SESSION[$this->sessionKey]['items'];
$this->deleteExpiredItems();
}
}
/** Deletes expired itemes from the session list */
function deleteExpiredItems() {
if (!count($this->items)) {
return;
}
$now = time();
foreach ($this->items as $key => $item) {
if ($item['expires'] < $now) {
unset($_SESSION[$this->sessionKey][$key]);
}
}
}
/** Simple log function for debug purpos */
function log($msg) {
$h = @fopen(dirname(__FILE__) . DS . 'fast_file_responder.log', 'a');
@fwrite($h, sprintf("%s %s\n", date('Y-M-d h:i:s', time()), $msg));
@fclose($h);
}
/** Extracts the item key from the url and returns it. Returns false if no
* key could be found. This function must be adapted for others */
function getItemKey() {
if (!isset($_GET['url'])) {
return false;
}
$url = $_GET['url'];
if (!preg_match('/media\/(\w+)\/(\d+)/', $url, $matches)) {
return false;
}
return $matches[1] . '-' . $matches[2];
}
/** Returns the file of the media request */
function getFilename() {
$key = $this->getItemKey();
if (!$key || !isset($this->items[$key])) {
return false;
}
$item = $this->items[$key];
if ($item['expires'] < time() || !is_readable($item['file'])) {
return false;
}
return $item['file'];
}
/** Returns an array of request headers */
function getRequestHeaders() {
$headers = array();
if (function_exists('apache_request_headers')) {
$headers = apache_request_headers();
foreach($headers as $h => $v) {
$headers[strtolower($h)] = $v;
}
} else {
$headers = array();
foreach($_SERVER as $h => $v) {
if(ereg('HTTP_(.+)', $h, $hp)) {
$headers[strtolower($hp[1])] = $v;
}
}
}
return $headers;
}
/** Evaluates the client file cache and response if the client has still a
* valid file
* @param filename Current cache file */
function checkClientCache($filename) {
$cacheTime = filemtime($filename);
$headers = $this->getRequestHeaders();
if (isset($headers['if-modified-since']) &&
(strtotime($headers['if-modified-since']) == $cacheTime)) {
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $cacheTime).' GMT', true, 304);
// Allow further caching for 30 days
header('Cache-Control: max-age=2592000, must-revalidate');
exit;
}
}
function sendResponseHeaders($file) {
$fileSize = @filesize($file);
header('Content-Type: image/jpg');
header('Content-Length: ' . $fileSize);
header('Cache-Control: max-age=2592000, must-revalidate');
header('Pragma: cache');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($file)));
}
/** Evaluates if a valid cache file exists */
function exists() {
return $this->getFilename() != false;
}
/** Sends the cache file if it exists and exit. If it returns an error
* occured */
function send() {
$filename = $this->getFilename();
if (!$filename) {
return false;
}
$this->checkClientCache($filename);
$this->sendResponseHeaders($filename);
$chunkSize = 1024;
$buffer = '';
$handle = fopen($filename, 'rb');
while (!feof($handle)) {
$buffer = fread($handle, $chunkSize);
echo $buffer;
}
fclose($handle);
//$this->log("File send: $filename");
exit(0);
}
/** Closes the session */
function close() {
session_write_close();
}
}
?>
Now the requested images are shown almost simultaneously with the explorer photo page. Awesome!
Please leave a comment if you liked this proposal.
Today I published two significant improvements of this great gallery which boosts the user experience and the social interaction.
Until now the powerful access control of your media was just fun for you – but now it is easier than ever to share your photos in groups with your friends. A group got a new description text and some flag for the management. Other users can now see your groups and can subscribe the them by just a single click. It depends on you if your friends are able join the group immediately or the new subscriber needs a confirmation. Further a group can be shared, too. Other group members can assign a shared group to their media as well and all group members are able to see these photos or videos.

The other great feature is a shell task which sends notification emails of new media for users. It should be uses in a cron job and will inform your uses of your gallery on new uploads. Since your shell has no clue about your gallery URL, you have to configure it in the core.php with the parameter Notification.url. To test is you can use one of the parameters verbose, dryrun, noemail, or force.
8 15 * * * /var/www/phtagr/cake/console/cake -app /var/www/phtagr/app notify run
Please run the setup to upgrade your database (and do not forget to make a backup before – just in case).
I’m pleased to announce that phTagr has now an Polish translation – many thanks to Bartosz Fenski (aka fEnIo) for his time. The fresh baked zip file could be downloaded from SourceForge.

Recently a (German) news channel reported about the social micro payment system Flattr where you can share your love to thousand articles, songs, videos, or software by a simple click while having the opportunity to receive love from others. It’s a donation without troubles.
I think this payment-love sharing is a really really great idea! You can pay back your loved content sources or loved projects easily while receiving honors of others. All you need is an account at Flattr.com and some monthly donation input to give love and to receive love. If you love 10 things your monthly donation is shared to these 10 things, if you love 100, all 100 receive a little love – but at the end all you loved things receive something!
It’s a good idea for all the content creators, all song writers, all video editors, all software hobby writers like me. Therefore, you are able to tell your love for this great hobby project now! Just Flattr phTagr…

I’m happy to announce that phTagr has now an Estonian translation. Thanks to Craig for his great work who found also lots of missing translation texts. The fresh baked zip file could be downloaded from SourceForge. For upgrade information please have a look at our wiki page.

Further, phTagr supports now the media selection by the upload folder. This feature is very useful for albums which are ordered by folder. The folder link could be found by media detail tab in the media view.