jqGrid

Filed in Development Leave a comment

jqGrid is an excellent data grid plugin for jQuery which I wanted to use in my TV Schedule application. It went very smoothly except for the order in which the columns are displayed. jqGrid will display the columns in the order that it receives them. Continue Reading

, ,

Apache, SSL, Red Hat 4

Filed in Development Leave a comment

A new requirement came down for the Solo Tech application the other day. We need to serve it using SSL and in addition the SOAP calls to the API need to use SSL as well. There was a lot of hoop jumping in order to get this working. I had a previous install of Apache using DSO and I was hoping to load mod_ssl dynamically along with my other modules. However I could not get apxs to create the mod_ssl module. So I ended up recompiling Apache and PHP from scratch.

First I installed the latest version of openSSl from here.
(remember to do installs as root)

# gzip -d openssl.tar.gz
# tar -xvf openssl.tar
# cd openssl
# ./configure -fPIC os/compiler:gcc
# make
# make test
# make install

Then compile and install Apache to use ssl, php, rewrite and so (shared objects). Unzip and untar as above and switch into the new directory.

# ./configure --enable-ssl=shared --with-ssl=/usr/local/ssl/ --enable-rewrite=shared --enable-setenvif --enable-so
# make
# make install

I then created a key and certificate by doing the following and storing them in apache2/conf/certs

The last line creates a self signed certificate.

# openssl genrsa -out hostname.key 1024
# openssl req -new -key hostname.key -out hostname.csr
# openssl x509 -req -days 365 -in hostname.csr -signkey hostname.key -out hostname.crt

Open up apache2/conf/httpd.conf and uncomment the following line:

Include conf/extra/httpd-ssl.conf

Then open up apache2/conf/extra/httpd-ssl.conf and un-comment and set the paths to the certificate and key you just created.

SSLCertificateFile "/usr/local/apache2/conf/certs/hostname.crt"
SSLCertificateKeyFile "/usr/local/apache2/conf/certs/hostname.key"

Restart Apache. (I have apachectl in my path)

# apachectl -k restart

If everything is cool then you should be able to request a page from your server through https. The browser will complain about the certificate. You can accept the certificate and then you should be communicating through SSL.

The next step is setting up PHP. So stop Apache.

# apachectl -k stop

Repeat the unzip and untar process from above on the downloaded PHP archive and proceed with configuration and installation.

#  ./configure --with-apxs2=/usr/local/apache2/bin/apxs --with-mysql --with-curl --enable-soap --with-openssl=/usr/local/ssl
# make
# make test
# make install

Your path to openssl may differ to just verify its location with:

# whereis openssl

Move the PHP ini file to its final location:

# cp php.ini-dist /usr/local/lib/php.ini

Again this may differ on your system.
Open apache2/conf/httpd.conf again and add the following lines at the end.

          SetHandler application/x-httpd-php

Make sure the php module is being loaded by the conf file. You should see this:

LoadModule ssl_module modules/mod_ssl.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule php5_module        modules/libphp5.so

Restart the server.

For the SOAP stuff I set this in my app config.php file.

define('SOAP_URL', 'https:<URL>?wsdl');
define('SOAP_ORGNAME', 'ORG1002');
define('SOAP_API_VERSION', '3.2');
define('SOAP_WAIT', '0');
define('SSL_CERT_PATH', '/usr/local/apache2/certs/triad.pem');

And then when I instantiate the SOAP client I do this:

 
        try
        {
            $this->client = new SoapClient(
	            SOAP_URL,
		            array (
		            "trace"=>true
		            , "exceptions"=>true
		            , 'features'=>SOAP_SINGLE_ELEMENT_ARRAYS
					, 'local_cert' => SSL_CERT_PATH
		            )
            );
        }
        catch(SoapFault $f)
        {
            throw new Exception($f->getMessage());
        }

, , ,

Installing Apache and PHP on Red Hat 5

Filed in Development Leave a comment

As part of building up my dev environment at ETI I had to install Apache2.2 and PHP5 on Red Hat 5. Here are my install notes.

Do this as root.

First download the source for Apache and PHP. This command will get the file from the URL and put it at your present location.

#wget

Unzip and untar the Apache Archive

#gzip -d  
#tar xvf

Switch into the apache directory created from untarring the file.

Configure Apache with Dynamic Object support so that you add new modules easily at a later time.

#./configure ./configure --enable-so
#make
#make install

Done. Now you can test by starting and stopping the server

#/usr/local/apache2/bin/apachectl -k [start | stop]

If there are no errors when running it you should be able to browse to the test page:

http://<server ip>

PHP

Repeat the unzip and untar instructions above for the PHP archive.
Configure PHP with options you need. In my case I need mysql, cURL and SOAP. Notice the first option with the path to apxs. This is actually going to compile the PHP module and move it to Apache’s module directory. This is also used later when want to build and install new Apache modules.

#./configure --with-apxs2=/usr/local/apache2/bin/apxs --with-mysql --with-curl --enable-soap
#make
#make install

Then you want to move the distributed PHP config file to the final location. In my case it was this:

#cp php.ini-dist /usr/local/lib/php.ini

You also need to edit the Apache config file to tell it to process PHP files. I put this at the end of my httpd.conf file.

    SetHandler application/x-httpd-php

Finally I wanted to install mod_rewrite. In the directory that contains the source files look for the modules directory that contains the module you need. In my case it is mappers. Then I ran this command which compiles and installs the mod_rewrite module.

#/usr/local/apache2/bin/apxs -i -a -c mod_rewrite.c

Restart Apache and off you go.

Useful Links
http://httpd.apache.org/docs/2.0/programs/apxs.html
http://httpd.apache.org/docs/2.2/dso.html
http://www.php.net/manual/en/install.unix.apache2.php

, ,

Class Loader

Filed in Lab Leave a comment

I needed a class loader for my little framework as I dont want to include all the files in on every request which is what listing them in a long list of includes will do. So I checked out the PHP.net site which has this code sample. I used it as a starting point for this:

function __load_class($classname, $dir)
{
	$file = $dir . '/' . $classname . '.php';
	if (file_exists($file))
	{
		require_once ($file);
		return true;
	}
	return false;
}
 
function __autoload($classname)
{
	$inc[] = '../fwork';
	$inc[] = '../fwork/controllers';
	$inc[] = '../fwork/models';
	$inc[] = '../app';
	$inc[] = '../app/controllers';
	$inc[] = '../app/models';
 
	foreach ($inc as $dir)
	{
		if (__load_class($classname, $dir))
		{
			if(DEBUG_LEVEL == 2)
			{
				echo 'Loading class(' . $classname . ")<br>";
			}
			return;
		}
	}
}

I need to do some more testing and abstraction but its a good start for what I need.

Mod_rewrite

Filed in Lab Leave a comment

I wanted to start a small MVC framework to help build my next project. I needed to get mod_rewrite working and also build a simple class loader.

For mod_rewrite I setup the following .htaccess file (xp.htaccess to get around Windows’ filename issue)

RewriteEngine on
RewriteCond %{REQUEST_URI} !\.(php|css|js|gif|png|jpe?g)$
RewriteRule (.*)$ /index.php [L]

Then in my dispatcher file I can read the contents of the query string like this:

$request_uri = explode('/', $_SERVER['REQUEST_URI']);
$controller = htmlentities($request_uri[1]);
$action = htmlentities($request_uri[2]);
if($action == '') {$action = 'index'; }
$param = htmlentities($request_uri[3]);
if($param == '') {$param = ''; }

This is just temporary code and used to prove that it would work the way I expected. Sort of like tracer bullets described in The Pragmatic Programmer. But now I can use a URL like:

http://recipemanager/ingredient_type/update/5

Which will then let me know that I need to access the ingredient_type controller pass the parameter 5 to the update method.

Zend Framework

Filed in Lab Leave a comment

Anthony suggested that I try out the Zend Framework. It’s a pretty simple installation but I had a hard time findinga decent tutorial. The simple starter on the Zend site is set up to use SQL Lite and I just did not feel like installing and learning that at the same time. Then I stumbled upon this one that is quite good. Once you have completed it you will have a working CRUD for a single table. I found a couple of problems in the documentation vs. the example code though.

The tutorial shows how to set up your Apache virtual server for the project

ServerName zf-tutorial.localhost
DocumentRoot /var/www/html/zf-tutorial/public
AllowOverride All

This means that the server would serve files from the public directory. It then goes on to say to put the bootstrap (index.php) under the public directory. I was having some problems so I downloaded the example code to compare it with what I had done. I noticed that in the example code the index.php file was under the parent directory of public. This means the virtual host entry is wrong as it should point to zf-tutorial not public. I decided to leave the bootstrap file in the public directory as I did not want to have public access to the root of the application.

<?php
error_reporting(E_ALL|E_STRICT);
ini_set('display_errors', 1);
date_default_timezone_set('Europe/London');
 
include('../helpers/print_var.php');
include('../helpers/MyIterator.php');
 
 
// directory setup and class loading
set_include_path('.' . PATH_SEPARATOR . '../library/'
. PATH_SEPARATOR . '../application/models'
. PATH_SEPARATOR . get_include_path());
 
include "Zend/Loader.php";
Zend_Loader::registerAutoload();
 
// load configuration
$config = new Zend_Config_Ini('../application/config.ini', 'general');
$registry = Zend_Registry::getInstance();
$registry->set('config', $config);
 
// setup database
$db = Zend_Db::factory($config->db);
Zend_Db_Table::setDefaultAdapter($db);
 
// setup controller
$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setControllerDirectory('../application/controllers');
 
Zend_Layout::startMvc(array('layoutPath'=>'../application/layouts'));
 
// run!
$frontController->dispatch();

Apache Virtual Host on Windows XP

Filed in General Leave a comment

I wanted to set up some virtual hosts in my local environment. I found this very helpful page. Here is the lesson in a nutshell.

Assuming Apache 2.2 is installed in the default location open the following file:

C:\Program Files\Apache Software Foundation\Apache2.2\conf\httpd.conf

Find the lines that read:

# Virtual hosts
#Include conf/extra/httpd-vhosts.conf

and remove the comment (#) so that it looks like this:

# Virtual hosts
Include conf/extra/httpd-vhosts.conf

This enables virtual hosting in Apache. Note that it also shuts down the default host.

Open the following Windows file:

c:\WINDOWS\system32\drivers\etc\hosts

You should see something like the following:

127.0.0.1       localhost

Duplicate the line (keep the same IP number) and name a new host, for instance recipemanager and save the file.

Open the following file (this is the file you just enabled in Apache:

C:\Program Files\Apache Software Foundation\Apache2.2\conf\extra\httpd-vhosts.conf

Added the following:

<Directory c:/vhosts>
Options Indexes FollowSymLinks
AllowOverride All
Order Deny,Allow
Allow from all
</Directory>

This propagates the permissions you set in the directive to cascade to all foldes in c:/vhosts which means you can set up all your virtual servers under c:/vhosts.

Because the default localhost is disabled you need to create that like this:

<VirtualHost *:80>
DocumentRoot c:/htdocs
ServerName localhost
</VirtualHost>

This points localhost at c:/htdocs

Then you can create your recipemanager host:

<VirtualHost *:80>
DocumentRoot c:/vhosts/recipemanager/site
ServerName recipemanager
</VirtualHost>

Reboot Apache. Place a test file in c:/vhosts/recipemanager/site

Navigate with your browser to http://recipemanager/<TESTFILENAME>

And you should see the rendered file.

Autocomplete field with jQuery and PHP

Filed in Lab Leave a comment

For the recipe manager project I need to allow the entering of ingredients that the system does not know about. I decided to use an auto complete field to allow users to easily see what was already in the system. If the ingredient does not exist then the one they entered will added to the system.

To test this out I wrote a quick little program that assigns employees to an assigned employees table. The autocomplete does a look up in the employees table. If the entered employee is not found then the new employee is added to the employees table and then assigned.

I used the following jQuery stuff:

jQuery1.2.6
Autocomplete – jQuery plugin 1.0.2 (copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer)

Main interface: index.php

<?php
 
	include('config.php');
	$employees = array();
	$assigned_employees = array();
 
 
	$query = "select * from employees";
	$result = mysql_query($query) or die('Query failed: ' . mysql_error());
	while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) 
	{
		$employees[$line['id']] = $line['first_name'];
	}	
 
	$query = "select * from assigned_employees";
	$result = mysql_query($query) or die('Query failed: ' . mysql_error());
	while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) 
	{
		$assigned_employees[$line['id']] = $line['first_name'];
	}
 
?>
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
                    "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<script src="../javascript/jquery-latest.js"></script>
 	<link rel="stylesheet" href="../javascript/jquery.autocomplete.css" type="text/css" />
	<script type="text/javascript" src="../javascript/jquery.autocomplete.js"></script>
	<script>
		$(document).ready(function()
		{
			// var data = "Core Selectors Attributes Traversing Manipulation CSS Events Effects Ajax Utilities".split(" ");
			$("#first_name").autocomplete('autocomplete.php');
 
			$("#first_name").result(function() 
			{
				console.log(arguments);
			}
			);
		}
		);
  </script>
 
</head>
<body>
	<form action="insert.php" method="post">
		Assigned an employee: <input id="first_name" name="first_name" />
		<input type="submit" value="Submit" />
	</form>
 
	<h3>Employees</h3>
<?php
	foreach($employees as $employee)
	{
		echo "$employee <br>";
	}
?>	
 
<h3>Assigned Employees</h3>
<?php
	foreach($assigned_employees as $employee)
	{
		echo "$employee <br>";
	}
?>
 
<div>--------------------------------------------</div>
<a href="clear.php">Remove all assignments</a>
 
</body>
</html>

Auto Complete Function: autocomplete.php

<?php
include('config.php');
 
$q = $_GET['q'];
 
// Performing SQL query
$query = "SELECT id, first_name FROM employees";
$result = mysql_query($query) or die('Query failed: ' . mysql_error());
 
 
while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) 
{
	if (substr($line['first_name'], 0, strlen($q)) == $q) 
	{
		echo $line['first_name'] .'|'. $line['id'] . "\n";
	}
}
 
?>

Insert Function: insert.php

<?php
include('config.php');
 
//get the first_name from the form
$first_name = $_POST['first_name'];
 
// see if we have it in the database. If so then grab the ID
$query = "SELECT id FROM employees WHERE first_name = '$first_name'";
 
//print "$query<br>";
 
$result = mysql_query($query) or die('Query failed: ' . mysql_error());
 
if(!$row = mysql_fetch_assoc($result))
{
	// If not then insert it into the employees  table. Then get the ID of the new employee from the employees table.
	$query = "INSERT INTO employees(first_name) VALUES('$first_name')";
	//print "$query<br>";
	$result = mysql_query($query) or die('Query failed: ' . mysql_error());
 
	$id = mysql_insert_id();
}
else
{
	$id = $row['id'];
}
 
// Now insert into the ingredient list table.
 
$query = "INSERT INTO assigned_employees(id, first_name) VALUES($id, '$first_name')";
//print "$query<br>";
 
$result = mysql_query($query) or die('Query failed: ' . mysql_error());
 
header( 'Location: http://localhost/lab/autocomplete/index.php' ) ;
 
?>

Clear Function: clear.php

<?php
include('config.php');
 
$query = "delete FROM assigned_employees";
$result = mysql_query($query) or die('Query failed: ' . mysql_error());
header( 'Location: http://localhost/lab/autocomplete/index.php' );
?>

Database Schema

CREATE DATABASE /*!32312 IF NOT EXISTS*/`test` /*!40100 DEFAULT CHARACTER SET latin1 */;
 
USE `test`;
 
/*Table structure for table `assigned_employees` */
 
DROP TABLE IF EXISTS `assigned_employees`;
 
CREATE TABLE `assigned_employees` (
  `id` INT(10) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT,
  `first_name` VARCHAR(64) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;
 
/*Data for the table `assigned_employees` */
 
INSERT  INTO `assigned_employees`(`id`,`first_name`) VALUES (0000000002,'sam');
 
/*Table structure for table `employees` */
 
DROP TABLE IF EXISTS `employees`;
 
CREATE TABLE `employees` (
  `id` INT(10) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT,
  `first_name` VARCHAR(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;
 
/*Data for the table `employees` */
 
INSERT  INTO `employees`(`id`,`first_name`) VALUES (0000000001,'steve'),(0000000002,'sam'),(0000000003,'heather'),(0000000004,'heath'),(0000000005,'shamus'),(0000000006,'jennifer'),(0000000007,'geronimo'),(0000000008,'gerald');
 
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;

,

jQuery solution to the add ingredient row problem

Filed in Recipe Manager Leave a comment

Due to the fact that I could not find a reasonable solution within the CakePHP framework to add a new row of ingredient list inputs to recipe form I solved the problem using jQuery.

This is the script.

$('#add_ingredient_row').click(function()
{
	// Clone the last row of the table.
	var clonedRow = $("#ingredient_list tr:last").clone();
 
	// Generate an incremented index for the new row. -1 because there is a table header row
	var newIndex = (document.getElementById('ingredient_list').rows.length -1);
 
	// Set the ID of the row with the new index
	clonedRow[0].id = 'ingredient_' + newIndex;
 
 
	//console.log('ingredient_' + newIndex);
	// Loop through all of the inputs and select in the cloned row
	// Find the name and ID of the input/select element and find the number in it abd replace it with the
	// new index.
	$.each($('input, select', clonedRow), function(i, val)
	{
		//console.log(val);
 
		//console.log('Index -1: ' + (newIndex -1) + '. Index: ' + newIndex);
		val.name = val.name.replace((newIndex - 1), newIndex);
		val.id = val.id.replace((newIndex - 1), newIndex);
		val.value = '';
 
	});
	// put the row into the table
	$("#ingredient_list").append(clonedRow); 
}
);

This is the table that it operates on.

<table id="ingredient_list">
<tr>
	<th>Amount</th>
 
	<th>Measurement Type</th>
	<th>Description</th>
	<th>Ingredient</th>
</tr>
	<tr id="ingredient_0">
		<td>
			<div class="input text"><input name="data[IngredientList][0][amount]" type="text" maxlength="5" value="" id="IngredientList0Amount" /></div><input type="hidden" name="data[IngredientList][0][id]" value="" id="IngredientList0Id" />		</td>
 
		<td>
			<select name="data[IngredientList][0][measurement_type_id]" id="IngredientList0MeasurementTypeId">
				<option value=""></option>
				<option value="0000000002">cup</option>
				<option value="0000000005">fluid ounce</option>
				<option value="0000000001">ounce</option>
				<option value="0000000004">tablespoon</option>
				<option value="0000000003">teaspoon</option>
			</select>		
		</td>
 
		<td>
			<div class="input text"><input name="data[IngredientList][0][description]" type="text" maxlength="255" value="" id="IngredientList0Description" /></div>		</td>
		<td>
			<select name="data[IngredientList][0][ingredient_id]" id="IngredientList0IngredientId">
				<option value=""></option>
				<option value="0000000006">broccoli</option>
				<option value="0000000008">garlic</option>
				<option value="0000000009">olive oil</option>
				<option value="0000000010">peanut oil</option>
 
				<option value="0000000012">red onion</option>
				<option value="0000000007">white onion</option>
				<option value="0000000011">yellow onion</option>
			</select>		
		</td>
	</tr>
</table>

Here is the final solution I came up with. I abstracted out the cloning and manipulation code into a separate function so that it would be DRY.

 
$(document).ready(function()
{
	$('#add_recipe_row').click(function()
	{
		add_row('recipes_table', 'menu_item_');
	}); 
 
	$('#add_ingredient_row').click(function()
	{
		add_row('ingredient_list', 'ingredient_');
	}
	);
 
}); // ends document.ready
 
 
function add_row(tableID, rowIDprefix)
{
 
	// Clone the last row of the table.
	var clonedRow = $('#'+ tableID + ' tr:last').clone();
 
	// Generate an incremented index for the new row. -1 because there is a table header row
	var newIndex = (document.getElementById(tableID).rows.length -1);
 
	// Set the ID of the row with the new index
	clonedRow[0].id = rowIDprefix + newIndex;
 
 
	//console.log('ingredient_' + newIndex);
	// Loop through all of the inputs and select in the cloned row
	// Find the name and ID of the input/select element and find the number in it abd replace it with the
	// new index.
	$.each($('input, select', clonedRow), function(i, val)
	{
		//console.log(val);
 
		//console.log('Index -1: ' + (newIndex -1) + '. Index: ' + newIndex);
		val.name = val.name.replace((newIndex - 1), newIndex);
		val.id = val.id.replace((newIndex - 1), newIndex);
		val.value = '';
 
	});
	// put the row into the table
	$('#' + tableID).append(clonedRow);
}

MySQL Workbench

Filed in Dev Tools Leave a comment

Today I was going crazy trying to figure out a foreign key issue. So I found this great tool that reverse engineers a graphic model from sql statements. Very cool! It even outputs the model as an image.

TOP