Tuesday 15 November 2011

CentOS: Specifying DDNS hostname

A quick reminder. When using a CentOS machine on network with a DDNS then in order for the host name to appear correctly on the DDNS managed local zones, create or edit the file /etc/sysconfig/network-scripts/ifcfg-deviceID where deviceID is the actual device name of your network interface e.g. eth0, wlan0 etc. Make sure that the line DHCP_HOSTNAME appears as shown below

DEVICE="eth0"
NM_CONTROLLED="yes"
DHCP_HOSTNAME="my-machine"
NAME="System eth0"
BROADCAST=255.255.255.255
ONBOOT=yes

There can be one ifcfg-XXX file per network interface. That way you can have two different names for the same machine connecting to the same network using different network cards like Ethernet and Wi-fi.

Saturday 5 November 2011

CakePHP: Storing multi-dimentional arrays in cookies

II didn't know that cookies are basically plain text data. This makes it impossible to store complex data structures directly inside a cookie. More information can be found in the archives of the CakePHP google group following this link.

The bottom line is that complex data need to be serialized before being saved in a cookie and unserialized after they are read from one. The serialize() and unserialize() PHP functions are here to do the job and last but not least the third parameter of the Cookie::write call -- the one that instructs cake to encrypt the cookie data -- should be set to true.

So to save a controller's form data you need to write something like this:

    $dataToSave = serialize($this->data);
    $this->Cookie->write( self::SEARCH_DATA_KEY, $dataToSave, true, '1 year');

and to read them back ....

        if (empty($this->data)) {
            // try to see if we have a stored cookie
            $cookieData = $this->Cookie->read(self::SEARCH_DATA_KEY);                        
            if (empty($cookieData)) {
               // provide default values here 
               ...
            } else 
                $this->data = unserialize($cookieData);            
        }

Tuesday 1 November 2011

CakePHP: An edit form with a cancel button (1.3 and 2.x)

When developing database CRUD applications with CakePHP, sooner or later you end up writing view code looking more or less like this
<div class="my model form">
<?php echo $this->Form->create('MyModel');?>
 <fieldset>
  <legend></legend>
 <?php
  echo $this->Form->input('id');
  echo $this->Form->input('name');
 ?>
 </fieldset>
<?php echo $this->Form->end(__('Submit', true));?>
</div>
This creates a nice form with a submit button at the end that every self respecting user can press to create a new record or modify the data of an existing one. It is also logical that you place a link to the index page somewhere near the form, so your users know where to go in case they change their mind about altering the database data.
With my users this time is was different. The form had to contain a cancel button. So how does one do it? If you are using the cake bake script and wish to have two nice round green buttons right at the bottom of your form then replace the last two lines of the previous code fragment with the following:
     ...
     <div class="submit">
         <?php echo $this->Form->submit(__('Submit', true), array('name' => 'ok', 'div' => false)); ?>
         <?php echo $this->Form->submit(__('Cancel', true), array('name' => 'cancel','div' => false)); ?>
     </div>
     </fieldset>                   
 <?php echo $this->Form->end();?>
The next thing to know from inside the controller code, is which button was pressed before the data were posted and that is available inside the 'form' array of the Controller::params property.
So a simple modification like the one on the following code :
        public function edit($id = null)
        {
            if (!$id && empty($this->data)) {
               $this->Session->setFlash(__('Invalid property', true));
               $this->redirect(array('action' => 'index'));
            }
            if (!empty($this->data)) {
                // abort if cancel button was pressed  
                if (isset( $this->params['form']['cancel'])) {
                    $this->Session->setFlash(__('Changes were not saved. User cancelled.', true));
                    $this->redirect( array( 'action' => 'index' ));
                }

                // proceed to save changes as usual
        }
... and everyone is happy.

Edit: Alternatively, if working with CakePHP version 2.4 then the sane info is inside the data array of the controller's request property.
So the previous code gets rewritten like this
        public function edit($id = null)
        {
            if (!$this->MyModel->exists($id)) {
                throw new NotFoundException(__('Invalid record'));
            }
            if ($this->request->is(array( 'post','put'))) {
                if (isset($this->request->data['cancel'])) {
                    $this->Session->setFlash(__('Changes were not saved. User cancelled.'));
                    return $this->redirect( array( 'action' => 'index' ));
                }

                // proceed to save changes as usual
        }
There is one last thing though... the cancel button in your form should also indicate that no form validation should be performed at the browser level. This can be accomplished by setting the 'formnovalidate' key of the Form::input options parameter to TRUE. So the whole cancel button creation tag should now look like this:
        <div class="submit">
            <?php echo $this->Form->submit(__('Create Account'), array('name' => 'ok', 'div' => FALSE)); ?>
            <?php echo $this->Form->submit(__('Cancel'), array('name' => 'cancel', 'formnovalidate' => TRUE, 'div' => FALSE)); ?>
        </div>
    Form->end(); ?>