Archive for 2010

Free Online HTML and PHP Editor for CakePHP

December 11th, 2010 Comments off

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 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

Categories: cakephp, info Tags: ,

Improved Preview Handling

October 10th, 2010 1 comment

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>
$ cakephp/lib/Cake/Console/cake preview generate
Generate preview files
cake preview [subcommand] [options]
generate  Create preview files
To see help on a subcommand use `cake preview [subcommand] --help`
--help, -h         Display this help.
--verbose, -v      Enable verbose output.
--quiet, -q        Enable quiet output.
--max              Maximum generation count. Default is 10. Use 0 to
                   generate all previews
--start-chunk      Set the start chunk number. A chunk has a size of 100
                   media. Default is 1
--size             Set the minimum preview size. Default is thumb
                   (choices: mini|thumb|preview|high)
--user             Generate only previews for given user
$ cakephp/lib/Cake/Console/cake preview generate --size mini -v
Page 1/180 (0.56%)
Page 2/180 (1.11%)
Page 3/180 (1.67%)

Update 2012-07-22: Updated commands to version 2.2-dev.

Categories: tools, user Tags: ,

Fast File Responder for CakePHP

October 3rd, 2010 1 comment

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.

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()) {
    } else {
  if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) {
    // ...
  // ...

Finally the Fast File Responder itself located in app/fast_file_responder.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() {
  /** Starts the session if the session sessionCookie is set */
  function startSession() {
    if (!isset($_COOKIE[$this->sessionCookie])) {
    if (isset($_SESSION[$this->sessionKey])) {
      $this->items = (array) $_SESSION[$this->sessionKey]['items'];
  /** Deletes expired itemes from the session list */
  function deleteExpiredItems() {
    if (!count($this->items)) {
    $now = time();
    foreach ($this->items as $key => $item) {
      if ($item['expires'] < $now) {
  /** 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));
  /** 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');
  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;
    $chunkSize = 1024;
    $buffer = '';
    $handle = fopen($filename, 'rb');
    while (!feof($handle)) {
      $buffer = fread($handle, $chunkSize);
      echo $buffer;
    //$this->log("File send: $filename");
  /** Closes the session */
  function close() {

Now the requested images are shown almost simultaneously with the explorer photo page. Awesome!

Please leave a comment if you liked this proposal.

Categories: cakephp Tags:

Improved Groups and Email Notifications

September 4th, 2010 1 comment

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).

Categories: announce, Related Links, user Tags: ,

phTagr z językiem polskim

September 3rd, 2010 2 comments

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.

Categories: user Tags:

Love the phTagr gallery through Flattr

August 14th, 2010 Comments off

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 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…

Flattr this

Categories: Nicht kategorisiert Tags: ,

phTagr räägib nüüd eesti keeles

August 13th, 2010 Comments off

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.

Categories: user Tags:

Public API now available!

July 31st, 2010 Comments off

Finally, the code of the open source social web gallery phTagr has now an API page available at The API is extracted from source using the API generator plugin for CakePHP which makes the documentation pretty easy. Futher the plugin provides a nice web interface for all classes.

The phTagr API contains the API of cakePHP and the API of phTagr classes. So if you need a code documentation of a class, controller, view, component, helper, theme, etc have a look at, make use of the search function, and get things quicker done.

Categories: Related Links, source Tags: ,

Theme Your CakePHP Application – Make it Mobile Device Aware

July 26th, 2010 4 comments

This post shows how easy your CakePHP applications becomes mobile device aware by using CakePHP theming feature. At the end of this post the mobile theme is selected automatically on requests of mobile devices.

Theming in CakePHP is really a piece of cake! Since CakePHP is a Model-View-Control framework, the data, the data logic and the view is well separated. For theming you just need to replace your view templates without changing the logic or data handling. Theming is quite easy and you don’t need to be a professional software programmer. While a nice theme needs some knowledge of HTML and CSS theming CakePHP requires just basic PHP skills.

CakePHP’s Layout and Views

In standard cases all view templates are located in ./app/views and CakePHP selects by its magic the correct page layout and the view for the current logic. So you have your layouts (the basic document structure) in ./app/views/layouts and your view templates (the specific view of one action) of your current action in ./app/views/[controller]/[action].ctp. For detailed information please have a look at

The CSS and JS files have a little different place. The style sheets are located in ./app/webroot/css and the javascript files are located in ./app/webroot/js.

The important structure of CakePHP’s view templates is shown below:

        layouts/           <-- Location of page layouts
        helpers/           <-- View template helpers
        elements/          <-- Little view templates
            index.ctp      <-- Specific view template of the index action in the home controller
        /themed            <-- Directory of CakePHP themes
    webroot/               <-- Application's webroot (like htdocs for apache)
        css/               <-- Style sheets
        js/                <-- Javascript directory

Now the cool part: You can theme all your views with a very similar view structure. But you don’t have to theme all layouts and views. If your theme does not have a required view CakePHP uses the original one. So you can just theme required layouts and/or views.

The Birth of a New Theme

Now we want to create a mobile version of our application. As example application I use the open source social photo gallery phTagr from I assume that your phTagr gallery is installed and accessible at http://localhost/phtagr. But you can easily adapt these steps to any other CakePHP application.

Basic Folder Structure of a CakePHP Theme

First we need to create a theme folder with its basic theme structure. Goto theme directory ./app/views/themed and create your own theme, I call it “mobile”. Further some required directories are also required. We theme the default layout and some basic actions, for phTagr it is the home, the photo explorer and the image view.

cd app/views/
mkdir -d themed/mobile
cd themed/mobile
mkdir -p layouts elements webroot/css home explorer images

Now we have following folder structure

        layouts/           <-- Default theme layouts
        elements/          <-- Folder for element templates
            css/           <-- Folder for mobile CSS
        home/              <-- Your themed views of home controller
        explorer/          <-- Your themed views of explorer controller
        images/            <-- Your themed views of images controller

Note: ./app/views/themed/mobile is now referred as ./mobile.

Activate Your Theme

To activate the new theme you set it in before_render() at ./app/app_controller.php. Later we automate this theme selection but for now the hard coded version is just fine. Important is, that the views are theme aware by this->view = 'Theme'; and of cause your theme name.

  function beforeRender() {
    // ... other code
    $this->view = "Theme";
    $this->theme = "mobile";

Your Themed Page Layout

If we call now our side http://localhost/phtagr nothing is changed. Thats fine, than CakePHP did not find any themed layouts or views to use and uses the standard one. Therefore we want to change the default layout in ./mobile/layouts/default.ctp to see some differences.

Edit ./mobile/layouts/default.ctp

<?php echo $html->docType('xhtml-strict'); ?>
<html xmlns="">

<title><?php echo $title_for_layout; ?></title>
  echo $html->charset('UTF-8');
  echo $html->meta('icon');

<?php echo $content_for_layout; ?>

Now we see some changes http://localhost/phtagr with a plain layout.

Layout Your Views

My profession is not quite a web designer but here I show you howto style your mobile version.

You create your css in ./mobile/webroot/css/mobile.css and add it to your default theme in the <head> section.

  echo $html->charset('UTF-8');
  echo $html->meta('icon');
  echo $html->css('mobile');
* {
  margin: 0;
  padding: 0;

body {
  width: 100%;
  color: black;
  font-family: Druid, Verdana, Sans;
  font-size: 10pt;
  background: white;

h1,h2,h3 { font-weight: bold; }
h1 { font-size: 130%; }
h2 { font-size: 120%; }
h3 { font-size: 110%; }

a { text-decoration: none; }
a img { border: 1px black solid; }

Add Header and Footer

Now we want to add some containers for header and footer. These parts could be rendered in special containers, CakePHP calls them elements template. Header and footer are perfect candidates for such elements.

Adapt your default HTML body layout in ./mobile/layouts/default.ctp

<div><?php echo View::element('header'); ?></div>
<div><?php echo $content_for_layout; ?></div>
<div><?php echo View::element('header'); ?></div>

Now create the elements ./mobile/elements/header.ctp and ./mobile/elements/footer.ctp.




<p>Social Web Gallery <a href="">phTagr</a> - mobile version.</p>

And we add new styles to ./mobile/webroot/css/mobile.css:

.header * {
  display: inline;
.header {
  display: block;
  background: #3d3;
  padding: 2px 5px;
.header h1 span.subheader {
  color: white;
  font-size: 80%;
  font-style: italic;
.footer {
  display: block;
  background: #888;

Adapt Views

Now some views will be adapted from the original ones. The original versions are located in ./app/views/[controller]/. In the theme they are located in ./app/themed/[theme]/[controller], in ./mobile/[controller].

For a theme view of the index action of the home controller CakePHP will look first in ./mobile/home/index.ctp. If it does not exists CakePHP take the standard one. So I copied the original version index.php to the theme folder ./mobile/home and made some changes.

Further I copied and adapt ./app/views/explorer/index.ctp and ./app/views/images/index.ctp. All other themes are not very important to adapt. The same with the view action of images controller.

For the forms I added some basic form style definitions to ./mobile/webroot/css/mobile.css:

fieldset {
  margin: 5pt;
  border: none;
fieldset legend {
  font-weight: bold;
fieldset div label {
  display: block;
  width: 100%;
fieldset div input[type=textfield],
fieldset div input[type=text] {
  width: 100%;
  font-size: 150%;
input[type=checkbox] + label {
  display: inline;
form * input[type=submit] {
  font-size: 150%;
  border: 2px solid black;
  background: #3d3;
  -moz-border-radius: 3px;
  -webkit-border-radius: 3px;

And styled the flag message:

.message {
  display: block;
  font-size: 120%;
  margin: 5pt;
  padding: 5pt;
  border: 1px solid black;
  background: #fd3;
  -moz-border-radius: 3px;
  -webkit-border-radius: 3px;

Fix iPhone

The iPhone (and iPod Touch) requires a special viewport meta information to render without any scaling. This is added to the default layout in the head section:

<title><?php echo $title_for_layout; ?></title>
<meta name="viewport" content="initial-scale=1.0">

Automatic Theme Selection

After adjusting the views and improving the style sheets the automatic theme switch is build in. For this we need the RequestHandler component in our basic app_controller.php which is added to the $components variable:

  var $components = array('RequestHandler');

We use the isMobile() function of RequestHandler component to evaluate the client device type and select the mobile theme for mobile devices

  function beforeRender() {
    // ... other code
    if ($this->RequestHandler->isMobile()) {
      $this->view = "Theme";
      $this->theme = "mobile";


The theme feature of CakePHP is straight forward and easy to use. You can customize your CakePHP application easily by changing the page layout or partial theming by writing single view templates of specific controller actions. The example showed how easy your application becomes mobile device aware.

You can see the full mobile theme of phTagr at

Categories: tools, user Tags: ,

phTagr 2.1.2 released!

July 11th, 2010 Comments off

2.1.1 phTagr is released! It has a simplified upload function which is enabled by default and replaces the advance file browser. Users have now an upload menu entry where uploaded files are stored in a daily upload directory. ZIP archives are extracted automatically. Uploaded and extracted files are imported and are shown immediately. Therefore, users can upload their media within three clicks and the upload form has now five upload field instead of one.

The next big improvement is the quick search which covers sub word. The quick search of ‘ice‘ returns also media with tags of ‘slice‘ or ‘rice‘.

phTagr is build on top of the MVC framework CakePHP and uses now CakePHP 1.3 instead of CakePHP 1.2. You have to upgrade your CakePHP if you use SVN trunk. See How To Migrate Phtagr for more details.

An admin user can now see the access level of other users and can change these. This feature was requested to handle private or malicious media.

phTagr supports now Dutch as new language – Thanks to Remy Wetzels. See also How To Translate.

Following Tickets where closed since 2.1.1

  • #14 – Automatic media import on upload
  • #63 – Multiple inputs for HTML upload
  • #90 – Wish: search on part of a tag
  • #110 – Admin rights: Change access level of others
  • #89 – Wish: change “Welcome to phTagr” via system preferences
  • #83 – Wrong import of files with UTF-8 multi-byte characters
  • #86 – PEAR path is missing in include_path variable
  • #85 – User with 0 quota, file uploaded shows up in “My Files” even it said “Could not upload”
  • #87 – User can see root of filesystem in “My Files”
  • #91 – Search on unknown string returns “Array” as search key
  • #93 – Shorten the user authentication keys
  • #112 – Broken Unzip functionality