Saturday, January 15, 2011
New Android App: Ja Hoor!
It is a simple app, just a soundboard with sounds of the Ja Hoor! program that was on the Dutch television a couple of years ago.
You can check some of the items here: http://weblogs.vpro.nl/villa-achterwerk/2007/11/15/jahoor/.
If you have any questions about this app, please use the Comments below to ask them :)
It is a simple app, just a soundboard with sounds of the Ja Hoor! program that was on the Dutch television a couple of years ago.
You can check some of the items here: http://weblogs.vpro.nl/villa-achterwerk/2007/11/15/jahoor/.
If you have any questions about this app, please use the Comments below to ask them :)
Wednesday, August 11, 2010
Use jQuery to create Twitter and Growl-like notifications in CakePHP
We will go from these messages:

To these messages:

It is only tested on CakePHP 1.3, but I guess it should work on 1.2.x too :)
In CakePHP a controller can send a message to the user via the session, using the command: $this->Session->setFlash('Hello User!'); . One of the options of setFlash() is that you can override the template that it uses to display the message, by passing a second parameter that specifies the element. So if you use $this->Session->setFlash('Hello User!','flash_success'); it will use the template app/views/layouts/flash_success.ctp , and thus you can influence it's behavior.
The downsite to the approach is that you need change every setFlash() command in your application to reflect the change. In my current app that are more then 150 changes, not a nice idea. Another bad thing about it is that the setFlash() message really needs the templates there. If you copy over your controller (or plugin) to another app that doesn't use these templates you will get a 'white page of death' if the template is not found and setFlash calles it.
The default template that is used by CakePHP is hardcoded in the Session controller so there is no way to really override the default without extending the Session helper (or hacking the core files). Both no-goes for me again.
The solution I came up with is quite simple: Just display the message as you would normally, hide it using CSS and let jQuery grab the text and display it in a fancy way.
There are a couple of twitter-like notification plugins out there, I picked the solution mentioned here because of it's simplicity.
(Note, i will use inline CSS and Javascript code to demonstrate it more easily. In a production environment you will want to move the CSS and Javascript code to separate files)
1. Start using a custom layout (default.ctp)
If you don't already use your own default.ctp template start by copying the file cake/libs/view/layouts/default.ctp to app/views/layouts/default.ctp . If you do already use it you can edit your existing template.
This method needs the great jQuery library to do some work, so we need to download it in our app, and mention it in our template so the app will use it.
Download http://code.jquery.com/jquery-1.4.2.min.js and put it in app/webroot/js/
Now copy the following line into app/views/layouts/default.ctp , in the section, right below the $this->Html->css() line:
echo $this->Html->script('jquery-1.4.2.min');
CakePHP now will take care of loading the jQuery plugin on your page. This is a good moment to check if it works so far, load up your page in a browser and check the page-source to see if jQuery is linked.
3. Show us some messages
To easily test this behavior just create a file called app/controllers/notify_controller.php and enter the following content:
<?php
class NotifyController extends AppController {
var $uses = null;
function test() {
$this->Session->setFlash('This is a Twitter-like notification :D');
$this->redirect('/');
}
}
?>
When you now visit the url /notify/test in your application you will see the default notification message on your screen.
4. Hide the message
Next step is to hide the message, which is done by CSS.
Enter the following code to the <head> section of your app/views/layouts/default.ctp template, just before the </head> tag:
<style type='text/css'>
#flashMessage.message { display: none; }
</style>
This will tell your browser to hide all elements with the class 'message' in the element with id #flashMessage .
5. Intercept the message
After we hide the message we will run some Javascript code to get it. Place the following code in the app/views/layouts/default.ctp template , just before the </body> tag :
<script type="text/javascript">
jQuery(document).ready(function(){
if(jQuery('#flashMessage..message').length) {
alert($('#flashMessage..message').html());
}
});
</script>
<noscript>
<style type="text/css">
#flashMessage.message {
display: block;
}
</style>
</noscript>
There are actually two snippets here. The one in the <script> tag uses jQuery to wait until the document is finished loading, then fires the if-statement. The if-statement tests if te .message element exists by checking its' length. It the element is found (the test has not returned false) it will alert the content of this element.
The <noscript> part here facilitates the users that are browsing without Javascript, and will unhide the message for them.
If you visit your page now and test a setFlash message, it will show a Javascript alert to display it. Time to make this a little more fancy.
6. Show the pretty notification
To get this notification to work we create Javascript function that shows the message instead of the alert() function, and we will specify some CSS to style the message.
First put the following CSS code in your < style > tag you used to hide the message in step 3:
#notify_message {
font-family:Arial,Helvetica,sans-serif;
font-size:135%;
font-weight:bold;
overflow: hidden;
width: 100%;
text-align: center;
position: absolute;
top: 0;
left: 0;
background-color: #efefef;
padding: 8px 0px;
color: #333;
opacity: .9;
display:none;
}
And put the following function in your < script > tag that you used to intercept the message in step 4:
function showAlert(msg, timeout){
if(!timeout){
var timeout = 3000;
}
var $alertdiv = $('<div id="notify_message"/> ');
$alertdiv.bind('click', function() { $(this).fadeOut(200); });
$(document.body).append($alertdiv);
$("#notify_message").fadeIn(500);
setTimeout(function() { $alertdiv.fadeOut(500) }, timeout);
}
The final bit is to remove the alert message and use the function above, you do this by changing the line:
alert($('#flashMessage.message').html());
to
notify($('#flashMessage.message').html());
7. That's it, that's all.
Now generate a setFlash message and watch the notification bar enter your screen :)
Bonus: if you want the notifications to look more like Growl you can use the following CSS code:
#notify_message {
font-family:Arial,Helvetica,sans-serif;
font-size:135%;
font-weight:bold;
overflow: hidden;
width: 400px;
text-align: center;
position: absolute;
top: 0;
right: 0;
background-color: #222;
padding: 18px 0px;
margin: 10px;
color: #eee;
opacity: .9;
display:none;
-moz-border-radius:8px;
-webkit-border-radius:8px;
}
We will go from these messages:

To these messages:

It is only tested on CakePHP 1.3, but I guess it should work on 1.2.x too :)
In CakePHP a controller can send a message to the user via the session, using the command: $this->Session->setFlash('Hello User!'); . One of the options of setFlash() is that you can override the template that it uses to display the message, by passing a second parameter that specifies the element. So if you use $this->Session->setFlash('Hello User!','flash_success'); it will use the template app/views/layouts/flash_success.ctp , and thus you can influence it's behavior.
The downsite to the approach is that you need change every setFlash() command in your application to reflect the change. In my current app that are more then 150 changes, not a nice idea. Another bad thing about it is that the setFlash() message really needs the templates there. If you copy over your controller (or plugin) to another app that doesn't use these templates you will get a 'white page of death' if the template is not found and setFlash calles it.
The default template that is used by CakePHP is hardcoded in the Session controller so there is no way to really override the default without extending the Session helper (or hacking the core files). Both no-goes for me again.
The solution I came up with is quite simple: Just display the message as you would normally, hide it using CSS and let jQuery grab the text and display it in a fancy way.
There are a couple of twitter-like notification plugins out there, I picked the solution mentioned here because of it's simplicity.
(Note, i will use inline CSS and Javascript code to demonstrate it more easily. In a production environment you will want to move the CSS and Javascript code to separate files)
1. Start using a custom layout (default.ctp)
If you don't already use your own default.ctp template start by copying the file cake/libs/view/layouts/default.ctp to app/views/layouts/default.ctp . If you do already use it you can edit your existing template.
This method needs the great jQuery library to do some work, so we need to download it in our app, and mention it in our template so the app will use it.
Download http://code.jquery.com/jquery-1.4.2.min.js and put it in app/webroot/js/
Now copy the following line into app/views/layouts/default.ctp , in the section, right below the $this->Html->css() line:
echo $this->Html->script('jquery-1.4.2.min');
CakePHP now will take care of loading the jQuery plugin on your page. This is a good moment to check if it works so far, load up your page in a browser and check the page-source to see if jQuery is linked.
3. Show us some messages
To easily test this behavior just create a file called app/controllers/notify_controller.php and enter the following content:
<?php
class NotifyController extends AppController {
var $uses = null;
function test() {
$this->Session->setFlash('This is a Twitter-like notification :D');
$this->redirect('/');
}
}
?>
When you now visit the url /notify/test in your application you will see the default notification message on your screen.
4. Hide the message
Next step is to hide the message, which is done by CSS.
Enter the following code to the <head> section of your app/views/layouts/default.ctp template, just before the </head> tag:
<style type='text/css'>
#flashMessage.message { display: none; }
</style>
This will tell your browser to hide all elements with the class 'message' in the element with id #flashMessage .
5. Intercept the message
After we hide the message we will run some Javascript code to get it. Place the following code in the app/views/layouts/default.ctp template , just before the </body> tag :
<script type="text/javascript">
jQuery(document).ready(function(){
if(jQuery('#flashMessage..message').length) {
alert($('#flashMessage..message').html());
}
});
</script>
<noscript>
<style type="text/css">
#flashMessage.message {
display: block;
}
</style>
</noscript>
There are actually two snippets here. The one in the <script> tag uses jQuery to wait until the document is finished loading, then fires the if-statement. The if-statement tests if te .message element exists by checking its' length. It the element is found (the test has not returned false) it will alert the content of this element.
The <noscript> part here facilitates the users that are browsing without Javascript, and will unhide the message for them.
If you visit your page now and test a setFlash message, it will show a Javascript alert to display it. Time to make this a little more fancy.
6. Show the pretty notification
To get this notification to work we create Javascript function that shows the message instead of the alert() function, and we will specify some CSS to style the message.
First put the following CSS code in your < style > tag you used to hide the message in step 3:
#notify_message {
font-family:Arial,Helvetica,sans-serif;
font-size:135%;
font-weight:bold;
overflow: hidden;
width: 100%;
text-align: center;
position: absolute;
top: 0;
left: 0;
background-color: #efefef;
padding: 8px 0px;
color: #333;
opacity: .9;
display:none;
}
And put the following function in your < script > tag that you used to intercept the message in step 4:
function showAlert(msg, timeout){
if(!timeout){
var timeout = 3000;
}
var $alertdiv = $('<div id="notify_message"/> ');
$alertdiv.bind('click', function() { $(this).fadeOut(200); });
$(document.body).append($alertdiv);
$("#notify_message").fadeIn(500);
setTimeout(function() { $alertdiv.fadeOut(500) }, timeout);
}
The final bit is to remove the alert message and use the function above, you do this by changing the line:
alert($('#flashMessage.message').html());
to
notify($('#flashMessage.message').html());
7. That's it, that's all.
Now generate a setFlash message and watch the notification bar enter your screen :)
Bonus: if you want the notifications to look more like Growl you can use the following CSS code:
#notify_message {
font-family:Arial,Helvetica,sans-serif;
font-size:135%;
font-weight:bold;
overflow: hidden;
width: 400px;
text-align: center;
position: absolute;
top: 0;
right: 0;
background-color: #222;
padding: 18px 0px;
margin: 10px;
color: #eee;
opacity: .9;
display:none;
-moz-border-radius:8px;
-webkit-border-radius:8px;
}
Quick guide on setting up a CakePHP project (updated for 1.3)
1. Create database
First we create a database called cake13. Rename it as needed, but be sure to change it in the examples below.user@host:~$ mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 3890 Server version: 5.1.37-1ubuntu5.4 (Ubuntu) Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> create database cake13; Query OK, 1 row affected (0.00 sec) mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER -> ON cake13.* -> TO 'cake13user'@'localhost' IDENTIFIED BY 's3cr3t'; Query OK, 0 rows affected (0.00 sec) mysql> exit Bye
2. Download CakePHP
Next we download the latest CakePHP from http://github.com/cakephp/cakephp/archives/1.3. The version used in this example is 1.3.3 and the file cakephp-cakephp-1.3.3-43-gd1028a7.tar.gz is placed in the homedir.3. Extract download
CakePHP will be installed in the ~/public_html/cake13 directory, which is served using mod_userdir. The directory will be available at the URL http://yourhost/~username/cake13 . Of course it is possible to install it to another location, but be sure to change the locations in the commands below accordingly.Make sure mod_rewrite is working and that the AllowOverride property for ~/public_html is set to All.
user@host:~$ cd ~/public_html user@host:~/public_html$ tar xvzf ~/cakephp-cakephp-1.3.3-43-gd1028a7.tar.gz cakephp-cakephp-d1028a7/ .......... . SNAP . .......... user@host:~/public_html$ mv cakephp-cakephp-d1028a7/ cake13 user@host:~/public_html$ cd cake13/
4. Configure the database
Rename the default database config.user@host:~/public_html/cake13$ mv app/config/database.php.default app/config/database.phpEdit the file freshly renamed file and change the values of the database configuration '$default' to match those on your system (you should probably change the values for login, user, password).user@host:~/public_html/cake13$ vi app/config/database.php5. Edit the core configuration file
Open the file app/config/core.php and edit the settings of 'Security.salt' and 'Security.cipherSeed' (I always search for the word 'salt'). Change the strings by entering a new line of random characters, or by changing the existing ones. Note that the value of Security.salt can be alphanumeric, while Security.cipherSeed just uses digits..user@host:~/public_html/cake13$ vi app/config/core.phpSet Permissions
The tmp dir needs write permissions. Use chmod -R 777 only in development environments. In production environments the owner of the files need to be the user running the webserver.user@host:~/public_html/cake13$ cd app/ user@host:~/public_html/cake13/app$ chmod -R 777 tmp/ user@host:~/public_html/cake13/app$ cd ..
The CakePHP installation should now ready and available.
1. Create database
First we create a database called cake13. Rename it as needed, but be sure to change it in the examples below.user@host:~$ mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 3890 Server version: 5.1.37-1ubuntu5.4 (Ubuntu) Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> create database cake13; Query OK, 1 row affected (0.00 sec) mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER -> ON cake13.* -> TO 'cake13user'@'localhost' IDENTIFIED BY 's3cr3t'; Query OK, 0 rows affected (0.00 sec) mysql> exit Bye
2. Download CakePHP
Next we download the latest CakePHP from http://github.com/cakephp/cakephp/archives/1.3. The version used in this example is 1.3.3 and the file cakephp-cakephp-1.3.3-43-gd1028a7.tar.gz is placed in the homedir.3. Extract download
CakePHP will be installed in the ~/public_html/cake13 directory, which is served using mod_userdir. The directory will be available at the URL http://yourhost/~username/cake13 . Of course it is possible to install it to another location, but be sure to change the locations in the commands below accordingly.Make sure mod_rewrite is working and that the AllowOverride property for ~/public_html is set to All.
user@host:~$ cd ~/public_html user@host:~/public_html$ tar xvzf ~/cakephp-cakephp-1.3.3-43-gd1028a7.tar.gz cakephp-cakephp-d1028a7/ .......... . SNAP . .......... user@host:~/public_html$ mv cakephp-cakephp-d1028a7/ cake13 user@host:~/public_html$ cd cake13/
4. Configure the database
Rename the default database config.user@host:~/public_html/cake13$ mv app/config/database.php.default app/config/database.phpEdit the file freshly renamed file and change the values of the database configuration '$default' to match those on your system (you should probably change the values for login, user, password).user@host:~/public_html/cake13$ vi app/config/database.php5. Edit the core configuration file
Open the file app/config/core.php and edit the settings of 'Security.salt' and 'Security.cipherSeed' (I always search for the word 'salt'). Change the strings by entering a new line of random characters, or by changing the existing ones. Note that the value of Security.salt can be alphanumeric, while Security.cipherSeed just uses digits..user@host:~/public_html/cake13$ vi app/config/core.phpSet Permissions
The tmp dir needs write permissions. Use chmod -R 777 only in development environments. In production environments the owner of the files need to be the user running the webserver.user@host:~/public_html/cake13$ cd app/ user@host:~/public_html/cake13/app$ chmod -R 777 tmp/ user@host:~/public_html/cake13/app$ cd ..
The CakePHP installation should now ready and available.
Monday, February 8, 2010
CakePHP in Subversion: ignore the tmp dir
Here is the one-liner I use to let Subversion exclude my projects tmp dir, I fetched it somewhere from the net... I thought I'd share it with you :)
svn propset svn:ignore "*" tmp -R
You should run it from your app folder :)
Here is the one-liner I use to let Subversion exclude my projects tmp dir, I fetched it somewhere from the net... I thought I'd share it with you :)
svn propset svn:ignore "*" tmp -R
You should run it from your app folder :)
Linking models in a CakePHP plugin
Last weekend I finally found the solution to a problem I was facing for a couple of days. I was trying to add data to a model in my plugin from the main app. The problem was that the data validation of that model did not work in the main app, while it worked from within the plugin.
After looking at this problem and asking on #cakephp on Freenode, the user rich97 eventually figured out what my problem was.
In my plugin I have two models which have a relationship, one for Articles and one for the Comments on that article. The problem was the definition of the relationships. I was refering to the correct className but without a refering to the plugin it is in, like this: 'className'=> 'SystemComment' while I ofcourse should mention the plugin too, like this: 'className'=> 'System.SystemComment' (with System being the name of my plugin).
Below you see the models with the correct className references.
Articles Model<?php
class SystemArticle extends SystemAppModel {
/* ... */
var $hasMany = array(
'SystemComment' => array(
'className' => 'System.SystemComment',
'foreignKey' => 'article_id',
'dependent'=>true
)
);
}
?>
Comments Model
<?php
class SystemComment extends SystemAppModel {
/* ... */
var $validate = array(
'email' => array(
'rule' => array('email', true),
'message' => 'Please supply a valid email address.'
),
'comment' => array('notempty')
);
var $belongsTo = array(
'SystemArticle' => array(
'className' => 'System.SystemArticle',
'foreignKey' => 'article_id'
)
);
}
?>
Thanks rich97! :)
Last weekend I finally found the solution to a problem I was facing for a couple of days. I was trying to add data to a model in my plugin from the main app. The problem was that the data validation of that model did not work in the main app, while it worked from within the plugin.
After looking at this problem and asking on #cakephp on Freenode, the user rich97 eventually figured out what my problem was.
In my plugin I have two models which have a relationship, one for Articles and one for the Comments on that article. The problem was the definition of the relationships. I was refering to the correct className but without a refering to the plugin it is in, like this: 'className'=> 'SystemComment' while I ofcourse should mention the plugin too, like this: 'className'=> 'System.SystemComment' (with System being the name of my plugin).
Below you see the models with the correct className references.
Articles Model<?php
class SystemArticle extends SystemAppModel {
/* ... */
var $hasMany = array(
'SystemComment' => array(
'className' => 'System.SystemComment',
'foreignKey' => 'article_id',
'dependent'=>true
)
);
}
?>
Comments Model
<?php
class SystemComment extends SystemAppModel {
/* ... */
var $validate = array(
'email' => array(
'rule' => array('email', true),
'message' => 'Please supply a valid email address.'
),
'comment' => array('notempty')
);
var $belongsTo = array(
'SystemArticle' => array(
'className' => 'System.SystemArticle',
'foreignKey' => 'article_id'
)
);
}
?>
Thanks rich97! :)
Thursday, January 21, 2010
Cakestyle: Drop-in replacement for the default CakePHP stylesheet (cake.generic.css)
Cakestyle is a stylesheet that is designed to be used with the default CakePHP configuration.
Just place the updated stylesheet in app/webroot/css/ and off you go.
The stylesheet is found here.
Of course things are made more clear with some screenshots, so here we go:
This is the default CakePHP screen after installation, with this Cakestyle:
Compared to the default layout it looks way better, but i may be biased ;) :
If you have any additions or comments, they are more then welcome!
Cakestyle is a stylesheet that is designed to be used with the default CakePHP configuration.
Just place the updated stylesheet in app/webroot/css/ and off you go.
The stylesheet is found here.
Of course things are made more clear with some screenshots, so here we go:
This is the default CakePHP screen after installation, with this Cakestyle:
Compared to the default layout it looks way better, but i may be biased ;) :
If you have any additions or comments, they are more then welcome!
Thursday, November 19, 2009
Javascript Regular Expression to test the last character in string
Today my colleague Martin asked me to look over his shoulder at a small problem he was facing, he was building a Search function in his application but got stuck with certain input.
If the search string ended on a character like ")" or a space (and maybe other non-alphanumeric characters), the search function returned nothing, even though it should have some matches.
We decided that the easiest way to solve this would be to delete all the faulty characters from the end of the string. This had to be done in (Server-side) JavaScript.
I came up with the following function: (and check below for the updated version)
<script type="text/javascript">
function testString(input){
var regex = /[a-zA-Z0-9]$/;
if(input.match(regex)){
var match = input;
} else {
var nomatch = input.substring(0,input.length-1);
var match = testString(nomatch);
}
return match;
}
</script>
What it does is the following:
- We first define our regular expression
- If this matches our input string, we will just return it
- If it does not match our input string, we strip the last character off the string, and test it again.
This recursive test will continue as long as the regex is not macthed.
To test this functionality you can run the function like this:
document.write(testString("This is a string that end with some special chars & $ !!! ) ")); Which will output: "This is a string that end with some special chars".
Maybe someone enjoys this code, so I thought I'll share it :)
Update as my other colleague Marcus points out, there is a way more elegant way to implement this, making it so that is does not need to recurse. The updated code is below. Thanks Marcus :)
<script type="text/javascript">
function testString(input){
while (!input.match(/[a-zA-Z0-9]$/)) {
input = input.substring(0, input.length-1);
}
return input;
}
</script>
Today my colleague Martin asked me to look over his shoulder at a small problem he was facing, he was building a Search function in his application but got stuck with certain input.
If the search string ended on a character like ")" or a space (and maybe other non-alphanumeric characters), the search function returned nothing, even though it should have some matches.
We decided that the easiest way to solve this would be to delete all the faulty characters from the end of the string. This had to be done in (Server-side) JavaScript.
I came up with the following function: (and check below for the updated version)
<script type="text/javascript">
function testString(input){
var regex = /[a-zA-Z0-9]$/;
if(input.match(regex)){
var match = input;
} else {
var nomatch = input.substring(0,input.length-1);
var match = testString(nomatch);
}
return match;
}
</script>
What it does is the following:
- We first define our regular expression
- If this matches our input string, we will just return it
- If it does not match our input string, we strip the last character off the string, and test it again.
This recursive test will continue as long as the regex is not macthed.
To test this functionality you can run the function like this:
document.write(testString("This is a string that end with some special chars & $ !!! ) ")); Which will output: "This is a string that end with some special chars".
Maybe someone enjoys this code, so I thought I'll share it :)
Update as my other colleague Marcus points out, there is a way more elegant way to implement this, making it so that is does not need to recurse. The updated code is below. Thanks Marcus :)
<script type="text/javascript">
function testString(input){
while (!input.match(/[a-zA-Z0-9]$/)) {
input = input.substring(0, input.length-1);
}
return input;
}
</script>
Thursday, November 5, 2009
Easy trees with Tree Behavior in CakePHP
This tutorial shows how to use the Tree Behaviour in CakePHP.
The only requirement is a working CakePHP application. You can refer to this post to help you get one.
Step 1: Create the table in your database
First we need to create a table to store our tree. To keep things a bit generic we create a simple table called 'Nodes'. There are various ways to extend this table with other functionality or data, but that is beyond the scope of this tutorial.
Run the following SQL code to create a table called Nodes:
CREATE TABLE `nodes` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `parent_id` int(11) unsigned DEFAULT NULL, `lft` int(11) unsigned DEFAULT NULL, `rght` int(11) unsigned DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
The Tree Behaviour needs at least the fields parent_id, lft and rght, and they should all use type int().
Step 2: Create the model
To use table in the application we have to create the model. In this case the file will be app/models/node.php.
<?php
class Node extends AppModel {
var $name = 'Node';
var $actsAs = array('Tree');
}
?>
The model Node is defined as any other model in CakePHP. With the line var $actsAs = array('Tree'); we tell CakePHP to use this model as a tree.
Step 3: Create the controller
The controllers task is to take care of handling the data (adding, showing, updating, deleting, and more). At first we will create a controller with 2 functions, one called 'index' to display the tree and the other called 'add' to add items to the tree. The controller will be app/controllers/nodes_controller.php.
<?php
class NodesController extends AppController {
function index() {
$nodelist = $this->Node->generatetreelist(null,null,null," - ");
$this->set(compact('nodelist'));
}
function add() {
if (!empty($this->data)) {
$this->Node->save($this->data);
$this->redirect(array('action'=>'index'));
} else {
$parents[0] = "[ No Parent ]";
$nodelist = $this->Node->generatetreelist(null,null,null," - ");
if($nodelist) {
foreach ($nodelist as $key=>$value)
$parents[$key] = $value;
}
$this->set(compact('parents'));
}
}
}
?>
The index function is pretty straightforward, it uses the generatetreelist function to generate a formatted tree, and return it in an array.
The Add function is a bit more complex. It first checks if it received any data. If it did, it saves the data to the database and then redirects to the index again. If the add function did not receive any data, it will create the array needed to populate the 'Parent' select box in our Add screen (see below).
Step 4: Create the views
We now create 2 view files in the folder app/views/nodes/, which needs to be created first.
The file app/views/nodes/index.ctp is used to display the tree:
<?php
echo $html->link("Add Node",array('action'=>'add'));
echo "<ul>";
foreach($nodelist as $key=>$value){
echo "<li>$value</li>";
}
echo "</ul>";
?>
In the above code we first create a link to our 'add' functionality. After that we start a new Unordered list and fill it with the data from the tree.
For adding new items to the tree we will use the file app/views/nodes/add.ctp
<?php
echo $html->link('Back',array('action'=>'index'));
echo $form->create('Node');
echo $form->input('name',array('label'=>'Name'));
echo $form->input('parent_id',array('label'=>'Parent'));
echo $form->end('Add');
?>
First there is a link back to the index. After that a new HTML Form of the type Node is create. It then add two fields, one for the Node name, the other to select the parent of the new node. The last function add the submit button with the text 'Add'.
You should now be able to show your tree, and add nodes to it. Actually you first need to add a node because there is nothing to display yet. In the next step we will add some more functionality to it, but this seems a good moment to check if it all works so far :) ...
Step 5: Adding more functionality
In this step we will add the posibility to edit and delete the nodes, as wel as move them up and down. We do this by adding some links to the Index page, add the edit view and add some functions to the controller.
First we update the file app/views/nodes/index.ctp so it looks like this:
<?php
echo $html->link("Add Node",array('action'=>'add'));
echo "<ul>";
foreach($nodelist as $key=>$value){
$editurl = $html->link("Edit", array('action'=>'edit', $key));
$upurl = $html->link("Up", array('action'=>'moveup', $key));
$downurl = $html->link("Down", array('action'=>'movedown', $key));
$deleteurl = $html->link("Delete", array('action'=>'delete', $key));
echo "<li>[$editurl|$upurl|$downurl|$deleteurl] $value</li>";
}
echo "</ul>";
?>
The part that loops trough the nodelist is changed here. We define 4 URL's for the needed actions, and put them all in front of the Node name, seperated by a pipeline character. This is not the most elegant solution, but it will get the job done.
Next we add a view for the edit functionality by creating the file app/views/nodes/edit.ctp:
<?php
echo $html->link('Back',array('action'=>'index'));
echo $form->create('Node');
echo $form->hidden('id');
echo $form->input('name');
echo $form->input('parent_id', array('selected'=>$this->data['Node']['parent_id']));
echo $form->end('Update');
?>
This view is mostly the same as add.ctp. Two differences: the edit view needs a hidden field called 'id', and the parent_id selectbox has a 'selected' parameter, which selects the right parent when in Edit mode.
In the last step we add four functions (edit, delete, moveup, movedown) to the Controller, app/controllers/nodes_controller.php, so it will look like this:
<?php
class NodesController extends AppController {
function index() {
$nodelist = $this->Node->generatetreelist(null,null,null," - ");
$this->set(compact('nodelist'));
}
function add() {
if (!empty($this->data)) {
$this->Node->save($this->data);
$this->redirect(array('action'=>'index'));
} else {
$parents[0] = "[ No Parent ]";
$nodelist = $this->Node->generatetreelist(null,null,null," - ");
if($nodelist)
foreach ($nodelist as $key=>$value)
$parents[$key] = $value;
$this->set(compact('parents'));
}
}
function edit($id=null) {
if (!empty($this->data)) {
if($this->Node->save($this->data)==false)
$this->Session->setFlash('Error saving Node.');
$this->redirect(array('action'=>'index'));
} else {
if($id==null) die("No ID received");
$this->data = $this->Node->read(null, $id);
$parents[0] = "[ No Parent ]";
$nodelist = $this->Node->generatetreelist(null,null,null," - ");
if($nodelist)
foreach ($nodelist as $key=>$value)
$parents[$key] = $value;
$this->set(compact('parents'));
}
}
function delete($id=null) {
if($id==null)
die("No ID received");
$this->Node->id=$id;
if($this->Node->delete()==false)
$this->Session->setFlash('The Node could not be deleted.');
$this->redirect(array('action'=>'index'));
}
function moveup($id=null) {
if($id==null)
die("No ID received");
$this->Node->id=$id;
if($this->Node->moveup()==false)
$this->Session->setFlash('The Node could not be moved up.');
$this->redirect(array('action'=>'index'));
}
function movedown($id=null) {
if($id==null)
die("No ID received");
$this->Node->id=$id;
if($this->Node->movedown()==false)
$this->Session->setFlash('The Node could not be moved down.');
$this->redirect(array('action'=>'index'));
}
}
?>
The first part of the edit function works much like the add function, it checks on received data and tries to save it. When there is no data received, it checks if there is a paramater called ID. If not it dies with an error message. If the parameter is given it fetches the node data, and gets the data needed to populate the 'Parent' select box, just like in the Add screen
The rest of the three functions are almost identical, and don't need any matching views as the code will redirect to the index page anyway. All these functions check if the parameter ID is passed, and dies with an error if not. If the parameter is given it selects the right node and runs the action.
Done!
This should be it, you should now have a tree created in CakePHP, with complete Create, Read, Update and Delete functionality (and even more)...
Good luck using this code, and please use the comments if you have any questions about it.
This tutorial shows how to use the Tree Behaviour in CakePHP.
The only requirement is a working CakePHP application. You can refer to this post to help you get one.
Step 1: Create the table in your database
First we need to create a table to store our tree. To keep things a bit generic we create a simple table called 'Nodes'. There are various ways to extend this table with other functionality or data, but that is beyond the scope of this tutorial.
Run the following SQL code to create a table called Nodes:
CREATE TABLE `nodes` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `parent_id` int(11) unsigned DEFAULT NULL, `lft` int(11) unsigned DEFAULT NULL, `rght` int(11) unsigned DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
The Tree Behaviour needs at least the fields parent_id, lft and rght, and they should all use type int().
Step 2: Create the model
To use table in the application we have to create the model. In this case the file will be app/models/node.php.
<?php
class Node extends AppModel {
var $name = 'Node';
var $actsAs = array('Tree');
}
?>
The model Node is defined as any other model in CakePHP. With the line var $actsAs = array('Tree'); we tell CakePHP to use this model as a tree.
Step 3: Create the controller
The controllers task is to take care of handling the data (adding, showing, updating, deleting, and more). At first we will create a controller with 2 functions, one called 'index' to display the tree and the other called 'add' to add items to the tree. The controller will be app/controllers/nodes_controller.php.
<?php
class NodesController extends AppController {
function index() {
$nodelist = $this->Node->generatetreelist(null,null,null," - ");
$this->set(compact('nodelist'));
}
function add() {
if (!empty($this->data)) {
$this->Node->save($this->data);
$this->redirect(array('action'=>'index'));
} else {
$parents[0] = "[ No Parent ]";
$nodelist = $this->Node->generatetreelist(null,null,null," - ");
if($nodelist) {
foreach ($nodelist as $key=>$value)
$parents[$key] = $value;
}
$this->set(compact('parents'));
}
}
}
?>
The index function is pretty straightforward, it uses the generatetreelist function to generate a formatted tree, and return it in an array.
The Add function is a bit more complex. It first checks if it received any data. If it did, it saves the data to the database and then redirects to the index again. If the add function did not receive any data, it will create the array needed to populate the 'Parent' select box in our Add screen (see below).
Step 4: Create the views
We now create 2 view files in the folder app/views/nodes/, which needs to be created first.
The file app/views/nodes/index.ctp is used to display the tree:
<?php
echo $html->link("Add Node",array('action'=>'add'));
echo "<ul>";
foreach($nodelist as $key=>$value){
echo "<li>$value</li>";
}
echo "</ul>";
?>
In the above code we first create a link to our 'add' functionality. After that we start a new Unordered list and fill it with the data from the tree.
For adding new items to the tree we will use the file app/views/nodes/add.ctp
<?php
echo $html->link('Back',array('action'=>'index'));
echo $form->create('Node');
echo $form->input('name',array('label'=>'Name'));
echo $form->input('parent_id',array('label'=>'Parent'));
echo $form->end('Add');
?>
First there is a link back to the index. After that a new HTML Form of the type Node is create. It then add two fields, one for the Node name, the other to select the parent of the new node. The last function add the submit button with the text 'Add'.
You should now be able to show your tree, and add nodes to it. Actually you first need to add a node because there is nothing to display yet. In the next step we will add some more functionality to it, but this seems a good moment to check if it all works so far :) ...
Step 5: Adding more functionality
In this step we will add the posibility to edit and delete the nodes, as wel as move them up and down. We do this by adding some links to the Index page, add the edit view and add some functions to the controller.
First we update the file app/views/nodes/index.ctp so it looks like this:
<?php
echo $html->link("Add Node",array('action'=>'add'));
echo "<ul>";
foreach($nodelist as $key=>$value){
$editurl = $html->link("Edit", array('action'=>'edit', $key));
$upurl = $html->link("Up", array('action'=>'moveup', $key));
$downurl = $html->link("Down", array('action'=>'movedown', $key));
$deleteurl = $html->link("Delete", array('action'=>'delete', $key));
echo "<li>[$editurl|$upurl|$downurl|$deleteurl] $value</li>";
}
echo "</ul>";
?>
The part that loops trough the nodelist is changed here. We define 4 URL's for the needed actions, and put them all in front of the Node name, seperated by a pipeline character. This is not the most elegant solution, but it will get the job done.
Next we add a view for the edit functionality by creating the file app/views/nodes/edit.ctp:
<?php
echo $html->link('Back',array('action'=>'index'));
echo $form->create('Node');
echo $form->hidden('id');
echo $form->input('name');
echo $form->input('parent_id', array('selected'=>$this->data['Node']['parent_id']));
echo $form->end('Update');
?>
This view is mostly the same as add.ctp. Two differences: the edit view needs a hidden field called 'id', and the parent_id selectbox has a 'selected' parameter, which selects the right parent when in Edit mode.
In the last step we add four functions (edit, delete, moveup, movedown) to the Controller, app/controllers/nodes_controller.php, so it will look like this:
<?php
class NodesController extends AppController {
function index() {
$nodelist = $this->Node->generatetreelist(null,null,null," - ");
$this->set(compact('nodelist'));
}
function add() {
if (!empty($this->data)) {
$this->Node->save($this->data);
$this->redirect(array('action'=>'index'));
} else {
$parents[0] = "[ No Parent ]";
$nodelist = $this->Node->generatetreelist(null,null,null," - ");
if($nodelist)
foreach ($nodelist as $key=>$value)
$parents[$key] = $value;
$this->set(compact('parents'));
}
}
function edit($id=null) {
if (!empty($this->data)) {
if($this->Node->save($this->data)==false)
$this->Session->setFlash('Error saving Node.');
$this->redirect(array('action'=>'index'));
} else {
if($id==null) die("No ID received");
$this->data = $this->Node->read(null, $id);
$parents[0] = "[ No Parent ]";
$nodelist = $this->Node->generatetreelist(null,null,null," - ");
if($nodelist)
foreach ($nodelist as $key=>$value)
$parents[$key] = $value;
$this->set(compact('parents'));
}
}
function delete($id=null) {
if($id==null)
die("No ID received");
$this->Node->id=$id;
if($this->Node->delete()==false)
$this->Session->setFlash('The Node could not be deleted.');
$this->redirect(array('action'=>'index'));
}
function moveup($id=null) {
if($id==null)
die("No ID received");
$this->Node->id=$id;
if($this->Node->moveup()==false)
$this->Session->setFlash('The Node could not be moved up.');
$this->redirect(array('action'=>'index'));
}
function movedown($id=null) {
if($id==null)
die("No ID received");
$this->Node->id=$id;
if($this->Node->movedown()==false)
$this->Session->setFlash('The Node could not be moved down.');
$this->redirect(array('action'=>'index'));
}
}
?>
The first part of the edit function works much like the add function, it checks on received data and tries to save it. When there is no data received, it checks if there is a paramater called ID. If not it dies with an error message. If the parameter is given it fetches the node data, and gets the data needed to populate the 'Parent' select box, just like in the Add screen
The rest of the three functions are almost identical, and don't need any matching views as the code will redirect to the index page anyway. All these functions check if the parameter ID is passed, and dies with an error if not. If the parameter is given it selects the right node and runs the action.
Done!
This should be it, you should now have a tree created in CakePHP, with complete Create, Read, Update and Delete functionality (and even more)...
Good luck using this code, and please use the comments if you have any questions about it.
Saturday, October 31, 2009
Variable in Android's strings.xml
Today I was wondering how to create a variable in strings.xml, a file used in Android application development.
It is actually very simple. :)
First define a string in the strings.xml file (usually res/values/strings.xml).
<string name="unread_messages">You have %d unread messages</string>
This string has a variable %d that will be replaced in the next step.
In the Java code the string is fetched with the getString method and the variable is replaced with the right content using Java's String formatter.
String message = String.format(getString(R.string.unread_messages), 10);
The output will be You have 10 unread messages... :)
Update: As Romain Guy points out in the comments it is even simpler, documented here:
String message = getString(R.string.unread_messages, 10);
Today I was wondering how to create a variable in strings.xml, a file used in Android application development.
It is actually very simple. :)
First define a string in the strings.xml file (usually res/values/strings.xml).
<string name="unread_messages">You have %d unread messages</string>
This string has a variable %d that will be replaced in the next step.
In the Java code the string is fetched with the getString method and the variable is replaced with the right content using Java's String formatter.
String message = String.format(getString(R.string.unread_messages), 10);
The output will be You have 10 unread messages... :)
Update: As Romain Guy points out in the comments it is even simpler, documented here:
String message = getString(R.string.unread_messages, 10);
Monday, August 3, 2009
Add more swap space to a running Linux system
Today I was trying to install Oracle XE to a Centos 5 installation using this guide. While installing the packages i noticed that the Oracle XE Database was unable to install. The error message was quite clear:
My solution was to adding some temporary swap space to this system, which is really easy to do:This system does not meet the minimum requirements for swap space. Based on the amount of physical memory available on the system, Oracle Database 10g Express Edition requires 1024 MB of swap space. This system has 509 MB of swap space. Configure more swap space on the system and retry the installation. error: %pre(oracle-xe-univ-10.2.0.1-1.0.i386) scriptlet failed, exit status 1 error: install: %pre scriptlet failed (2), skipping oracle-xe-univ-10.2.0.1-1.0
Step 1. Create an empty file called /swapfile from about 600MB
root@bootux:~# dd if=/dev/zero of=/swapfile bs=1024000 count=600
600+0 records in
600+0 records out
614400000 bytes (614 MB) copied, 5.85153 seconds, 105 MB/Step 2. Format the new file to make it a swap file
root@bootux:~# mkswap /swapfile
Setting up swapspace version 1, size = 614395 kB
Step 3. Enable the new swapfile. Only the swapon command is needed, but with the free command you can clearly see the swap space is made available to the system.
Installation can now continue :)root@bootux:~# free -m | grep Swap Swap: 509 0 509 root@bootux:~# swapon /swapfile root@bootux:~# free -m | grep Swap Swap: 1095 0 1095
Today I was trying to install Oracle XE to a Centos 5 installation using this guide. While installing the packages i noticed that the Oracle XE Database was unable to install. The error message was quite clear:
My solution was to adding some temporary swap space to this system, which is really easy to do:This system does not meet the minimum requirements for swap space. Based on the amount of physical memory available on the system, Oracle Database 10g Express Edition requires 1024 MB of swap space. This system has 509 MB of swap space. Configure more swap space on the system and retry the installation. error: %pre(oracle-xe-univ-10.2.0.1-1.0.i386) scriptlet failed, exit status 1 error: install: %pre scriptlet failed (2), skipping oracle-xe-univ-10.2.0.1-1.0
Step 1. Create an empty file called /swapfile from about 600MB
root@bootux:~# dd if=/dev/zero of=/swapfile bs=1024000 count=600
600+0 records in
600+0 records out
614400000 bytes (614 MB) copied, 5.85153 seconds, 105 MB/Step 2. Format the new file to make it a swap file
root@bootux:~# mkswap /swapfile
Setting up swapspace version 1, size = 614395 kB
Step 3. Enable the new swapfile. Only the swapon command is needed, but with the free command you can clearly see the swap space is made available to the system.
Installation can now continue :)root@bootux:~# free -m | grep Swap Swap: 509 0 509 root@bootux:~# swapon /swapfile root@bootux:~# free -m | grep Swap Swap: 1095 0 1095
Friday, July 31, 2009
Quick guide on setting up a CakePHP project
Read the updated version for CakePHP 1.3 here
Create the database
First we create a database called authcake. Rename it as needed, but be sure to change it in the examples below.
user@host:~$ mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 385 Server version: 5.0.67-0ubuntu6 (Ubuntu) Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> create database authcake; Query OK, 1 row affected (0.00 sec) mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER -> ON authcake.* -> TO 'authcake'@'localhost' IDENTIFIED BY 's3cr3t'; Query OK, 0 rows affected (0.00 sec) mysql> exit Bye
Download CakePHP
Next we download the latest CakePHP from http://cakephp.org/. The version used in this example is 1.2.3.8166 Stable and the file cake_1.2.3.8166.tar.gz is placed in the homedir.
Extract download
CakePHP will be installed in the ~/public_html/authcake directory, which is served using mod_userdir. The directory will be available at the URL http://yourhost/~username/authcake . Of course it is possible to install it to another location, but be sure to change the locations in the commands below accordingly.
Make sure mod_rewrite is working and that the AllowOverride property for ~/public_html is set to All.
user@host:~$ cd ~/public_html user@host:~/public_html$ tar xvzf ~/cake_1.2.3.8166.tar.gz cake_1.2.3.8166/ .......... . SNAP . .......... user@host:~/public_html$ mv cake_1.2.3.8166/ authcake user@host:~/public_html$ cd authcake/
Configure Database
Rename the default database config and change the values to match those on your system.
user@host:~/public_html/authcake$ mv app/config/database.php.default app/config/database.php user@host:~/public_html/authcake$ vi app/config/database.php
Update Configuration
Change the setting Security.salt in the core.php file
user@host:~/public_html/authcake$ vi app/config/core.phpSet Permissions
The tmp dir needs write permissions. Use chmod -R 777 only in development environments. In production environments the owner of the files need to be the user running the webserver.
user@host:~/public_html/authcake$ cd app/ user@host:~/public_html/authcake/app$ chmod -R 777 tmp/ user@host:~/public_html/authcake/app$ cd ..
The CakePHP installation should now ready and available.
Read the updated version for CakePHP 1.3 here
Create the database
First we create a database called authcake. Rename it as needed, but be sure to change it in the examples below.
user@host:~$ mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 385 Server version: 5.0.67-0ubuntu6 (Ubuntu) Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> create database authcake; Query OK, 1 row affected (0.00 sec) mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER -> ON authcake.* -> TO 'authcake'@'localhost' IDENTIFIED BY 's3cr3t'; Query OK, 0 rows affected (0.00 sec) mysql> exit Bye
Download CakePHP
Next we download the latest CakePHP from http://cakephp.org/. The version used in this example is 1.2.3.8166 Stable and the file cake_1.2.3.8166.tar.gz is placed in the homedir.
Extract download
CakePHP will be installed in the ~/public_html/authcake directory, which is served using mod_userdir. The directory will be available at the URL http://yourhost/~username/authcake . Of course it is possible to install it to another location, but be sure to change the locations in the commands below accordingly.
Make sure mod_rewrite is working and that the AllowOverride property for ~/public_html is set to All.
user@host:~$ cd ~/public_html user@host:~/public_html$ tar xvzf ~/cake_1.2.3.8166.tar.gz cake_1.2.3.8166/ .......... . SNAP . .......... user@host:~/public_html$ mv cake_1.2.3.8166/ authcake user@host:~/public_html$ cd authcake/
Configure Database
Rename the default database config and change the values to match those on your system.
user@host:~/public_html/authcake$ mv app/config/database.php.default app/config/database.php user@host:~/public_html/authcake$ vi app/config/database.php
Update Configuration
Change the setting Security.salt in the core.php file
user@host:~/public_html/authcake$ vi app/config/core.phpSet Permissions
The tmp dir needs write permissions. Use chmod -R 777 only in development environments. In production environments the owner of the files need to be the user running the webserver.
user@host:~/public_html/authcake$ cd app/ user@host:~/public_html/authcake/app$ chmod -R 777 tmp/ user@host:~/public_html/authcake/app$ cd ..
The CakePHP installation should now ready and available.
Friday, July 10, 2009
Easily create a RAM-disk in Linux
$ mkdir /tmp/space $ sudo mount -t tmpfs tmpfs /tmp/space -o size=2G,nr_inodes=200k,mode=01777You should now have an extra 2GB storage device.
$ df -h | grep space $ tmpfs 2.0G 0 2.0G 0% /tmp/spaceYou access the temporarily device by it's mountpoint /tmp/space. Needless to say, once you unmount this volume (for instance, with a reboot), the data on it will be gone. Use with care! :) Tested on Ubuntu, should work with any 2.4 or 2.6 kernel...
$ mkdir /tmp/space $ sudo mount -t tmpfs tmpfs /tmp/space -o size=2G,nr_inodes=200k,mode=01777You should now have an extra 2GB storage device.
$ df -h | grep space $ tmpfs 2.0G 0 2.0G 0% /tmp/spaceYou access the temporarily device by it's mountpoint /tmp/space. Needless to say, once you unmount this volume (for instance, with a reboot), the data on it will be gone. Use with care! :) Tested on Ubuntu, should work with any 2.4 or 2.6 kernel...
Tuesday, October 28, 2008
Clearing Linux' Memory Cache
$ free $ sudo sync $ sudo echo 3 | sudo tee /proc/sys/vm/drop_caches $ freeOr, if you like one-liners:
free; sudo sync; sudo echo 3 | sudo tee /proc/sys/vm/drop_caches; freeThis is the ouput that i got. The values in bold are my commands, if you look at the underlined values, you will see the memory drop.
linux01:~ $ free
total used free shared buffers cached
Mem: 2061568 2029968 31600 0 429284 1247252
-/+ buffers/cache: 353432 1708136
Swap: 1036152 104 1036048
linux01:~ $ sudo sync
linux01:~ $ sudo echo 3 | sudo tee /proc/sys/vm/drop_caches
3
linux01:~ $ free
total used free shared buffers cached
Mem: 2061568 724828 1336740 0 340 392092
-/+ buffers/cache: 332396 1729172
Swap: 1036152 104 1036048
I tested this on SLES10 and Centos5 and Ubuntu8.04. My Ubuntu 6.06 and 7.10 boxes do not seem to have the file /proc/sys/vm/drop_caches .
Sources: 1 and 2.
$ free $ sudo sync $ sudo echo 3 | sudo tee /proc/sys/vm/drop_caches $ freeOr, if you like one-liners:
free; sudo sync; sudo echo 3 | sudo tee /proc/sys/vm/drop_caches; freeThis is the ouput that i got. The values in bold are my commands, if you look at the underlined values, you will see the memory drop.
linux01:~ $ free
total used free shared buffers cached
Mem: 2061568 2029968 31600 0 429284 1247252
-/+ buffers/cache: 353432 1708136
Swap: 1036152 104 1036048
linux01:~ $ sudo sync
linux01:~ $ sudo echo 3 | sudo tee /proc/sys/vm/drop_caches
3
linux01:~ $ free
total used free shared buffers cached
Mem: 2061568 724828 1336740 0 340 392092
-/+ buffers/cache: 332396 1729172
Swap: 1036152 104 1036048
I tested this on SLES10 and Centos5 and Ubuntu8.04. My Ubuntu 6.06 and 7.10 boxes do not seem to have the file /proc/sys/vm/drop_caches .
Sources: 1 and 2.
Monday, October 27, 2008
Enable Time Sync from inside a Linux VM
vmware-guestd --cmd "vmx.set_option synctime 1 0"To disable it you have to turn around the last two parameters, like this:
vmware-guestd --cmd "vmx.set_option synctime 0 1"If you want to apply a setting that is already current you will get the message:
"Invalid old value"This is tested on RedHat Enterprise Linux 5 and SUSE Linux Enterprise Server 10. Source: Timekeeping best practices for Linux on kb.vmware.com
vmware-guestd --cmd "vmx.set_option synctime 1 0"To disable it you have to turn around the last two parameters, like this:
vmware-guestd --cmd "vmx.set_option synctime 0 1"If you want to apply a setting that is already current you will get the message:
"Invalid old value"This is tested on RedHat Enterprise Linux 5 and SUSE Linux Enterprise Server 10. Source: Timekeeping best practices for Linux on kb.vmware.com
Thursday, March 20, 2008
Prototype + JSON + PHP - I Got It :)
test.html <html> <head> <title>Prototype/JSON/PHP test</title> <script language="Javascript" type="text/javascript" src="prototype-1.6.0.2.js"></script> <script language="Javascript" type="text/javascript"> function LoadFile(id,target){ new Ajax.Request("test.php?action=load&id="+id, { onSuccess: function(transport) { var theObject = transport.responseJSON; $(target).innerHTML = theObject.text; }, method: "get" }); } </script> </head> <body> <a href="javascript:LoadFile(1,'filelist')">File 1</a> <a href="javascript:LoadFile(2,'filelist')">File 2</a> <a href="javascript:LoadFile('third','filelist')">File 3</a> <div id="filelist"></div> </body> </html>
test.php <?php # Make sure this is the first output. header('Content-type: application/json'); # The list of files. $files[1]='This is the first file'; $files[2]='And this the second one!'; $files['third']='This must be the third then...'; # Get ID and create array. $id=$_GET['id']; $return['id']=$id; $return['text']=$files[$id]; # Return the JSON string. echo json_encode($return); ?>Code formatted with Format Code. Note: Make sure you don't send any other text (that includes spaces or newlines) before sending the header with PHP! This will cause errors that sound like: "Warning: Cannot modify header information - headers already sent by......" Good luck! :)
test.html <html> <head> <title>Prototype/JSON/PHP test</title> <script language="Javascript" type="text/javascript" src="prototype-1.6.0.2.js"></script> <script language="Javascript" type="text/javascript"> function LoadFile(id,target){ new Ajax.Request("test.php?action=load&id="+id, { onSuccess: function(transport) { var theObject = transport.responseJSON; $(target).innerHTML = theObject.text; }, method: "get" }); } </script> </head> <body> <a href="javascript:LoadFile(1,'filelist')">File 1</a> <a href="javascript:LoadFile(2,'filelist')">File 2</a> <a href="javascript:LoadFile('third','filelist')">File 3</a> <div id="filelist"></div> </body> </html>
test.php <?php # Make sure this is the first output. header('Content-type: application/json'); # The list of files. $files[1]='This is the first file'; $files[2]='And this the second one!'; $files['third']='This must be the third then...'; # Get ID and create array. $id=$_GET['id']; $return['id']=$id; $return['text']=$files[$id]; # Return the JSON string. echo json_encode($return); ?>Code formatted with Format Code. Note: Make sure you don't send any other text (that includes spaces or newlines) before sending the header with PHP! This will cause errors that sound like: "Warning: Cannot modify header information - headers already sent by......" Good luck! :)
Thursday, March 13, 2008
xampp-dsl: A Damn Small LAMP Appliance
Saturday, March 8, 2008
KQemu on Ubuntu Gutsy Gibbon
Could not open '/dev/kqemu' - QEMU acceleration layer not activatedThis howto shows how to get KQemu running on Ubuntu 7.10 (Gutsy Gibbon) First install the following packages:
sudo apt-get install kqemu-source kqemu-common build-essentialThen run the following commands:
sudo module-assistant prepare kqemu sudo module-assistant auto-install kqemuIf everything went right (so no errors occurred) you can load the module with this command:
sudo modprobe kqemuNow check if the module is loaded correctly. If you get a line of output starting with the word kqemu it means the module is loaded properly:
lsmod | grep qemuTo use this module as a user the right permission need to be set:
sudo chmod a+rw /dev/kqemuYou QEMU VM's should now run without the warning above and run a whole lot faster!
Could not open '/dev/kqemu' - QEMU acceleration layer not activatedThis howto shows how to get KQemu running on Ubuntu 7.10 (Gutsy Gibbon) First install the following packages:
sudo apt-get install kqemu-source kqemu-common build-essentialThen run the following commands:
sudo module-assistant prepare kqemu sudo module-assistant auto-install kqemuIf everything went right (so no errors occurred) you can load the module with this command:
sudo modprobe kqemuNow check if the module is loaded correctly. If you get a line of output starting with the word kqemu it means the module is loaded properly:
lsmod | grep qemuTo use this module as a user the right permission need to be set:
sudo chmod a+rw /dev/kqemuYou QEMU VM's should now run without the warning above and run a whole lot faster!
Friday, March 7, 2008
VirtualCenter 2.5 Passthrough Authentication
Stuart Radnidge from vinternals.com discovered how to do pass-through authentication in VMware VC 2.5. This means you will automatically log in to VC 2.5 when the Windows workstation credentials are the same as the ones VC is working with (so this works if you authenticate to the same Windows Domain on both platforms.) The setting is undocumented but is reported to work. You can find the post here. This is how it is done:
To use it, simply add -passthroughAuth -s vchostname to the end of the shortcut used to launch the VI 2.5 client. By default it uses the Negotiate SSPI provider, however since they have fully implemented the interface you can change that behaviour to use Kerberos by adding the following within the <vpxd> node in the vpxd.cfg file on the VC server: <sspiProtocol>Kerberos</sspiProtocol>Via virtualization.info.
Stuart Radnidge from vinternals.com discovered how to do pass-through authentication in VMware VC 2.5. This means you will automatically log in to VC 2.5 when the Windows workstation credentials are the same as the ones VC is working with (so this works if you authenticate to the same Windows Domain on both platforms.) The setting is undocumented but is reported to work. You can find the post here. This is how it is done:
To use it, simply add -passthroughAuth -s vchostname to the end of the shortcut used to launch the VI 2.5 client. By default it uses the Negotiate SSPI provider, however since they have fully implemented the interface you can change that behaviour to use Kerberos by adding the following within the <vpxd> node in the vpxd.cfg file on the VC server: <sspiProtocol>Kerberos</sspiProtocol>Via virtualization.info.
Sunday, March 2, 2008
Creating VM's in a breeze
At work I used this script to create VM's with static MAC addresses which are known in our DHCP server, and it helped me a lot to instantly have a clean VM while working on unattended OS installations.
The script is written in bash and runs on any system which supports bash. It depends on vmware-vdiskmanager , which is included in VMware Server and VMware Workstation.
To run the script you first need to download it.
wget http://createvm.googlecode.com/svn/trunk/createvm.sh -O createvm.sh
Once downloaded you have to flag it executable:
chmod +x createvm.sh
Now you can run the script. Run it without any parameters to see the options available:
./createvm.sh
The script has one parameter that needs to be set, and that is the guest OS you want to run in your VM. To see a list of OS'es that are supported run the script with parameter -l :
./createvm.sh -l
To create your first VM decide on which Guest OS you want to run and pass it as the first parameter to the script. In this example I am creating an Ubuntu virtual machine:
./createvm.sh ubuntu
To create the same VM with some more memory pass the -r (for ram) parameter with the wanted RAM value:
./createvm.sh ubuntu -r 512
The script will display a summary of what will be created (see screenshot). The defaults are displayed here. All the settings displayed here can be manipulated by the command line parameters.
To see some of the examples of creating Virtual Machines run the following command:
./createvm.sh -ex
Any major updates on this script will be displayed here.
Links:
At work I used this script to create VM's with static MAC addresses which are known in our DHCP server, and it helped me a lot to instantly have a clean VM while working on unattended OS installations.
The script is written in bash and runs on any system which supports bash. It depends on vmware-vdiskmanager , which is included in VMware Server and VMware Workstation.
To run the script you first need to download it.
wget http://createvm.googlecode.com/svn/trunk/createvm.sh -O createvm.sh
Once downloaded you have to flag it executable:
chmod +x createvm.sh
Now you can run the script. Run it without any parameters to see the options available:
./createvm.sh
The script has one parameter that needs to be set, and that is the guest OS you want to run in your VM. To see a list of OS'es that are supported run the script with parameter -l :
./createvm.sh -l
To create your first VM decide on which Guest OS you want to run and pass it as the first parameter to the script. In this example I am creating an Ubuntu virtual machine:
./createvm.sh ubuntu
To create the same VM with some more memory pass the -r (for ram) parameter with the wanted RAM value:
./createvm.sh ubuntu -r 512
The script will display a summary of what will be created (see screenshot). The defaults are displayed here. All the settings displayed here can be manipulated by the command line parameters.
To see some of the examples of creating Virtual Machines run the following command:
./createvm.sh -ex
Any major updates on this script will be displayed here.
Links:

