Passing GET parameters to CakePHP’s paginator

I had an issue where I had a search page which relied on GET parameters. When I added CakePHP’s pagination, the paginator did not pass along my GET variables.

Here’s my solution:

$urlParams = $this->params['url'];
unset($urlParams['url']);
$this->Paginator->options(array('url' => array('?' => http_build_query($urlParams))));

CakePHP Table Helper

Recently, I’ve been developing a lot of sites that require the creation of a table that lists entries and has links to actions. In order to speed up development, I created a helper for CakePHP to create these tables for me.

For example, let’s say I have a list of properties in the form:

Array
(
    [0] => Array
        (
            [Property] => Array
                (
                    [id] => 12
                    [address_id] => 96
                    [description] => An okay apartment
                    [price] => 300
                    [type] => apartment
                    [num_bedrooms] => 1
                )
 
            [Address] => Array
                (
                    [id] => 96
                    [address1] => 650 Columbus Ave
                    [address2] => 
                    [city] => Boston
                    [state] => MA
                    [zip] => 1104
                )
 
        )
 
    [1] => Array
        (
            [Property] => Array
                (
                    [id] => 19
                    [address_id] => 98
                    [description] => One great apartment
                    [price] => 500
                    [type] => apartment
                    [num_bedrooms] => 1
                )
 
            [Address] => Array
                (
                    [id] => 98
                    [address1] => 731 Parker St.
                    [address2] => 
                    [city] => Roxbury Crossing
                    [state] => MA
                    [zip] => 1104
                )
 
        )
)

I’d like to use the table creator to create a table which lists all the entries and provides action links to view the property and to edit the address. To do so, I include the helper in my controller, and then use the following code in my view.

Note that the createTable function takes in five parameters:
– the default model name
– the entries for the table
– the columns for the header. An associative array with Column Name => Database field
– the actions for the columns. An array in the form Action Name => (Action URL, Action database field)
– text to display when there are no entries.

$displayFields = array('Type' => 'type',
                       'Address1' => 'Address.address1',
                       'City' => 'Address.city',
                       'State' => 'Address.city',
                       'Price' => 'price',
                       'Bedrooms' => 'num_bedrooms');
 
$actions = array('View' => array('/properties/view/', 'Property.id'),
                 'Edit Address' => array('/address/edit/', 'Address.id'));
 
echo $this->Table->createTable('Property',
                               $properties,
                               $displayFields,
                               $actions,
                               'You currently have no properties listed'
                               );

This code generates the following table:

For the first entry, the ‘View’ link goes to /properties/view/12 and the ‘Edit Address’ link goes to /address/edit/96.

Having this helper has definitely increased my productivity in making a lot of CRUD-based admin areas. I hope you find it helpful too. To download the source code and use it yourself, check out the github repository.

On the road to remote SVN syncing: svn_directory_sync.py

Fore more than one project that I’m working on right now, I’m managing the source code using Subversion. Unfortunately, I only have FTP access to the production server that the code lives on. Therefore, it’s not a trivial process to update the code on the production server to the latest revision.

Many of the repositories are very large, and therefore it’s too time and bandwidth consuming to re-upload the entire directory. I attempted to use weex to sync my local repository with the FTP server, but ran into two issues:

  • Weex sometimes timesout and throws errors while creating directories
  • Weex takes a long time for large repositories with deep file structures

Eventually, I’d like to write a complete alternative solution to weex in python with direct SVN integration. But, for now, I’ve written svn_directory_sync: a python script that makes things a bit easier.

For now, I keep a log of what subversion repository is on the production FTP server. Then, I use svn_directory_sync to move all the files that have been modified or added to the repository since the production revision to a separate local folder on my development machine. I can then upload those files manually or use weex to sync that ftp folder (setting weex to not delete remote files if they are not in the local folder).

For example, let’s say I have a local copy of a svn repository located at ~/www/mywebsite. I last updated the production server at revision 60. Since then, I’ve made several subversion commits: I’ve modified two files and added one directory and added one file to the local subversion repository. I now can run the following command to put those files in a separate folder to upload manually or using weex.

I run the following command, telling svn_directory_sync to move the modified and added files since revision 60 into a new folder located at ~/tmp/files_to_upload

python svn_directory_sync.py -s ~/www/mywebsite/ -o ~/tmp/files_to_upload -r 60

The script then outputs:

Copying /home/steve/www/mywebsite/modified_file1.html to /home/steve/tmp/files_to_upload/modified_file1.html
Copying /home/steve/www/mywebsite/modified_file2.html to /home/steve/tmp/files_to_upload/modified_file2.html
Creating directory /home/steve/tmp/files_to_upload/new_directory
Copying /home/steve/www/mywebsite/new_directory/newfile.html to /home/steve/tmp/files_to_upload/new_directory/newfile.html

You can now manually upload the files in ~/tmp/files_to_upload to the web server or use weex to sync the local and remote folders.

To download the script and view the source check out svn_directory_sync on github

CakePHP: Get pagination page count as variable, Hide page count if only one page

This isn’t mentioned in the CakePHP book, but you can get the number of pages in a pagination by using the following code:

$this->params['paging'][<MODEL NAME>]['pageCount']

I recently used this to only display a page count if there is more than one page. Below is the code, note that my model name is “Property” in this example.

$hasPages = ($this->params['paging']['Property']['pageCount'] > 1);
 
if ($hasPages)
{
    echo $this->Paginator->counter();
}

Target Filesystem doesn’t have /sbin/init – Ubuntu 10.04

My parent’s computer recently refused to boot, with the error message “Target Filesystem doesn’t have /sbin/init”, following by booting to BusyBox.

I was able to fix this by:
– Booting into Ubuntu from a Live CD
– Going into GParted and figuring out what the mount location of my linux partiton is on (it was /dev/sda6).
– Then running the following command from the terminal:

e2fsck -f -y -v /dev/sda6

This command repaired the file system, and when I rebooted Ubuntu was working again.

Install XMonad with Gnome on Ubuntu 10.04 as a separate session

I tried doing this for a while, and a lot of the blog posts and tutorials did not work, or they did not allow me to choose between Xmonad+Gnome and just Gnome on login.

The closest one I found that worked was http://miggysmith.wordpress.com/2010/10/03/xmonad-and-gnome/ Below you’ll find my slightly modified version.

First, get xmonad using:

sudo apt-get install xmonad

This installs xmonad and gives an entry in our xsessions. But, we need to do some work to have xmonad and gnome work together.

First, let’s create ~/.xmonad/xmonad.hs and paste the following into it:

import XMonad
import XMonad.Util.EZConfig
import XMonad.Config.Gnome
main = xmonad $ gnomeConfig { terminal = "gnome-terminal" }

This tells xmonad to use the gnomeConfig they’ve supplied, and additionally to use the gnome terminal.

Now, let’s create a custom startup script to get xmonad and gnome to work together. You can create the script wherever. Since I’m the only user on this machine, I created mine in ~/xmonadstart.sh

Paste the following code in your ~/xmonadstart.sh script:

#!/bin/bash
export WINDOW_MANAGER=xmonad
gconftool-2 -u /desktop/gnome/session/required_components/windowmanager
gnome-session

This tells gnome to use the xmonad window manager. Also, it unsets the current windowmanager in gconf. Other tutorials forgot to mention to do this! Finally, it starts gnome-session which now uses xmonad.

In order for this to work we need to make the script executable, so run:

chmod +x ~/xmonadstart.sh

We’re almost done. The last step is to modify /usr/share/xsessions/xmonad.desktop. Change it the following:

[Desktop Entry]
Encoding=UTF-8
Name=XMonad
Comment=Lightweight tiling window manager
Exec=~/xmonadstart.sh
Icon=xmonad.png
Type=XSession

This tells xmonad to run the custom xmonadstart.sh script.

Now we’re done. Logout, click your username to login, and on the bottom choose ‘Xmonad’ from the sessions. You should be running xmonad within gnome! You’ll notice there aren’t title bars on windows and things have a red border. See the Default Keyboard Bindings to start understanding how xmonad works.

Customizing XMonad while keeping gnome configurations
If you’d like to go a bit farther, here’s my customized xmonad.hs which keeps the gnome configurations. It changes xmonad so that when you cycle through layouts (mod space), you cycle through the default “tiled” layout and then you cycle through a full screen tabbed layout.

import XMonad
import XMonad.Util.EZConfig
import XMonad.Config.Desktop
import XMonad.Config.Gnome
import XMonad.Layout.Tabbed
 
 
myLayout = (desktopLayoutModifiers $ tiled) ||| simpleTabbed
  where
    -- default tiling algorithm partitions the screen into two panes
    tiled   = Tall nmaster delta ratio
 
    -- The default number of windows in the master pane
    nmaster = 1
 
    -- Default proportion of screen occupied by master pane
    ratio   = 1/2
 
    -- Percent of screen to increment by when resizing panes
    delta   = 3/100
 
 
main = xmonad $ gnomeConfig{ terminal = "gnome-terminal",layoutHook = myLayout}

CakePHP: Use Component In Model

I know it would make some Cake purists cringe, but I had to use a Component in a Model.

I wanted to use a component called ‘Geocoder’ in a model called ‘Address’.

In order for this to work, the controller that calls the function in Address must use the desired Component. Because I wanted to use the Geocoder component’s function in my beforeSave, I wanted to make sure that the call will work from every controller, and therefore added ‘Geocoder’ to the $uses in my custom app_controller.php, i.e:

<?php
class AppController extends Controller
{
    public $components = array('Auth', 'Session', 'GeoCoder');
 
}
?>

Then, in my beforeSave function of my Address model I call the geocode method of my Geocoder component as follows, after using App::import and instantiating the component by its full name. It looked something like this:

function beforeSave()
{
   App::import('Component','GeoCoder');
   $gc = new GeoCoderComponent();
   $this->data['geocoded'] = $gc->geocode($this->data['address']);
   return true;
}

CakePHP: Model save function returning false without an error or SQL statements

I recently was running into an issue where the function $this->User->save($this->data); was returning false without any indication of an error, and also there was no SQL statement in the debugging section.

With some help from this stack overflow post, I found the error by putting the following after the save function in my controller:

echo var_dump($this->User->invalidFields());

This allowed me to see that the form was invalid because of a missing field, a field which I forgot to include the HTML for in the view and therefore the error was not displaying.

Loop Flowplayer

In Flowplayer 3.2.1, in order to the loop a clip you have to add a function onBeforeFinish which returns false. For example:

<script>
$f("player", "flowplayer.swf", {
    clip:  {
        autoPlay: true,
        onBeforeFinish: function() {
             return false;
       }
});
</script>

CakePHP: Get html/output of view without view displaying or outputting using render

Looking to render a view output into a variable without the view actually displaying?  You need to create a new instance of a view with a second paramter of false, and then continue to use this variable to set variables and view paths.  When you call render, you can store the output in a variable and it won’t automatically display.  If autoRender is true in your controller, you’ve got to set it to false before proceeding.

Here’s an example of storing the html of an element called ‘box’ with the variable of ‘text’ without the view displaying or outputting:

/* Make sure the controller doesn't auto render. */
$this->autoRender = false;
 
/* Set up new view that won't enter the ClassRegistry */
$view = new View($this, false);
$view->set('text', 'Hello World');
$view->viewPath = 'elements';
 
/* Grab output into variable without the view actually outputting! */
$view_output = $view->render('box');