Pages

Banner 468

Wednesday, 22 June 2011

HTML 5 & CSS 3

0 comments
 
This weeks topics are the much anticipated HTML5 and CSS3 specifications, the next generation in web page markup and styling.

Introduction

HTML5 is the successor to HTML4 which came out way back in 1999. Back then the internet was a very different place where notions such as web applications, e-commerce and social networking were yet unheard of. The web has changed a lot since then but the fundamental technology used to build it hasn't, and over the years its limitations were becoming ever more apparent where web designers were stretching HTML to its absolute limits, hacking it into submission.  Thankfully, in 2006, the Web Hypertext Application Technology Working Group (WHATWG) and the World Wide Web Consortium (W3C) - both of which were working on separate specifications - decided to cooperate to create HTML5. The principles behind HTML5 (as stated on w3schools.com) are:
  • New features should be based on HTML, CSS, DOM and Javascript
  • Reduce the need for external plugins such as flash
  • Better error handling
  • More markup to replace scripting
  • HTML5 should be device independent
  • The development process should be visible to the public 

Implementing HTML5

HTML5 is still a work in progress but W3C have announced that it will be complete by 2014. Since HTML5 is not yet an official standard, no browser has full HTML5 support, however, most major browsers continue to add support for HTML5 with every release. This means that we can (and are encouraged to) start using HTML5 features today.  HTML5 builds on the previous specification so drastic changes to existing markup is not required to start using some of the new features. HTML5 markup also makes websites more search engine friendly, can help improve accessibility and given that all the major browsers largely support the syntax, the business cost for adopting HTML5 is almost negligible.

So, What's new in HTML5?

HTML5 is loaded with new features aimed at improving user experience over the web.  It's a collection of various small improvements that collectively help web designers create something special.  Here are some the most noticeable features in HTML5:
  • A <canvas> element that allows for dynamic rendering of 2D shapes and images on web pages
  • Content specific elements (such as header and article) to improve web page semantics
  • Support for audio and video playback
  • New form controls for better input validation
  • Improved support for local storage (based on databases rather than cookies)
One of my favorite has to be the set of new form controls.  In contrast to HTML4 where we had the generic textbox, HTML5 gives us input controls for: email, URLs, numbers, ranges, dates, search boxes, even colour!    These controls will drastically improve the way user input is currently validated.  Client-side input validation in HTML4 was always a headache and in most cases only cosmetic since it was largely based on javascript which could be disabled at any time by the end user.  With these new controls however, these basic validations will be carried out by the browser itself, which means less javascript and more robustness.  This is not to say that we can do without server-side validation, far from it, but at least we are spared the cumbersome client-side equivalents.  Browser support for these new input types varies, however they can still be used since they will behave as normal text-boxes if they are not supported, which is brilliant.  That's the main thing about HTML5.  We do not need to wait for an official release date to start using HTML5 features, indeed there won't be such a date.  HTML5 is with us today, browser support is growing steadily so failing to embrace the new specification today, simply means being left behind.

What about Visuals? - Enter CSS 3

Cascading Style Sheets (CSS) are an integral part of web development adding layout and style to our HTML pages.  Way back in my very first post(s) I discussed CSS at length, highlighting how it can help us de-couple our web page content from its layout.  The current specification of CSS (2.1) is powerful but CSS 3 takes that power to a completely new level.  Just like HTML5, CSS 3 builds on its predecessor and is still under development by the W3C, however modern browsers already support most of the new properties introduced in CSS 3.  This means that we can start using CSS 3 today, just as we can HTML5.  There is one catch though.  Until CSS 3 specification is finalised, browsers are allowed to interpret a property any way they see fit.  These kind of properties are usually prefixed with a namespace (such as -moz- or -webkit-) to indicate that they are not yet standard.  To explain this better, let's take the new "border-radius" property as an example.

CSS 3 supports adding rounded corners to objects, something which was previously only (painstakingly) possible using images.  Suppose we wanted to add a border with rounded corners to every "<div>" element on our website.  Here's what the CSS 3 style rule would look like:

   div {
      border: 1px solid black;
      -moz-border-radius: 5px;
      -webkit-border-radius: 5px;
      border-radius: 5px;
   }

The first line in the rule simply sets a solid black border around our "<div>" element.  The next 3 lines all state that the border should have rounded corners, each 5 pixels in radius.  Why do we have 3 lines that seemingly state the same thing?  "border-radius" is the proper name of this new CSS 3 property and this is the name that will stick once the CSS 3 specification is finalised.  "-moz-border-radius" is the Mozilla (Firefox) team's interpretation of how the property should be implemented, while the "-webkit-border-radius-" property is the Webkit (Safari, Chrome) team's interpretation. It is important to note that the proper name of the property should always be defined after it's 'non-standard' counterparts such that it is the one that takes precedence.  This will ensure that your stylesheet is forward-compatible i.e. newer browsers that support the standard property will in fact apply the standard one as it always takes precedence, without you having to constantly update your stylesheet.  Furthermore, older browsers can still apply the non-standard version of the property as they have no knowledge of the standard name!  As a matter of fact, at the time of writing, all the latest versions of the major browsers including Internet Explorer, Chrome, Safari and Opera, now support the standard version of "border-radius".  However,  if you want to test this behavior out, try the "border-image" property.

Here's a list of the most common prefixes for CSS 3 properties which are not yet standard:

PrefixBrowser
-ms-Internet Explorer 9
-moz-Firefox
-webkit-Safari, Chrome
-o-Opera

Other Features

There's lots more to CSS 3 than fancy borders, much more in fact. Here are some of the more exciting features of CSS 3:

  • Fonts - you can now use any font you like on your webpage rather than sticking to web-safe fonts;
  • 2D and 3D Transformations;
  • Transitions; and
  • Animations

Fonts

How many times have you resorted to images just so that you could use a particular font for your website logo or headings, sacrificing flexibility for looks.  With CSS 3 this is no longer an issue, simply upload your chosen font to your website and it will be automatically downloaded as required.  Now you can have the looks, the flexibility and better accessibility on your website, neat.


Transformations

In CSS 3 we can apply 2D and 3D transformations to any element on our web page including:

  • Translation (move)
  • Rotation
  • Scaling (re-sizing)
  • Skewing
  • Matrix (any combination of the above)
At the moment, 2D transformations enjoy more browser support than 3D transformations.  In fact, at the time of writing, all major browsers support 2D transformations while only Safari and Chrome have support for 3D transformations.  Here are some examples followed by the CSS 3 styles used:

Examples of CSS 3 2D Transformations (as viewed in Google Chrome)

div{
   background-color:#F5F5F5;
   border:solid 1px black;   
   width:100px;
   height:100px;
   margin:30px;
   float:left;
   text-align:center;
   font-family:arial;
   line-height:30px;
   -webkit-border-radius:5px;
   -moz-border-radius:5px;
   border-radius:5px;
   -webkit-box-shadow: 5px 5px 12px cyan;
   -moz-box-shadow: 5px 5px 12px cyan;
    box-shadow: 5px 5px 12px cyan;
}
 
.rotate{
   -ms-transform:rotate(45deg);
   -moz-transform:rotate(45deg);
   -webkit-transform:rotate(45deg);
   -o-transform:rotate(45deg);
   transform:rotate(45deg);
}
 
.scale {
   -ms-transform:scale(1.5,1.5):
   -moz-transform:scale(1.5,1.5);
   -webkit-transform:scale(1.5,1.5);
   -0-transform-scale(1.5,1.5);
   transform:scale(1.5,1.5);
}
 
.skew {
   -ms-transform:skew(20deg, 15deg); 
   -moz-transform:skew(20deg, 15deg);
   -webkit-transform:skew(20deg, 15deg);
   -o-transform:skew(20deg, 15deg);
   transform:skew(20deg, 15deg);
}

All three <div> elements in this example have the same basic style: i00 pixels square, have a light grey background and a thin black border. I also added the new CSS 3 properties "border-radius" and "box-shadow". "Border-radius" we've already seen, "box-shadow" on the other hand is another border-related CSS 3 property that creates a drop-shadow around your elements by specifying X and Y offsets (how far you want the shadow to 'drop') and shadow distance - how soft/precise it is.

Each of the <div> elements however implements a different class according to the desired transformation. The rotate transformation rotates the element around its centre by the specified number of degrees. The scale transformation takes two parameters one for the width and another for the height. In this case the div is enlarged by a factor of 1.5 along both axis. Similarly the skew transformation takes two angles as parameters (for the x-axis and y-axis) and skews the object accordingly.

3D transformations work in a similar way, this time taking parameters across three dimensions, (X, Y and Z). You could also specify perspective properties and transformation origin in 3D transformations. However, I want to turn my attention to one of my personal favorites: Animation.

CSS 3 Animation

Until recently, the only way to add animations to your website was by using animated gifs or plugins such as Flash.  If you were really brave you could also animate using javascript - not for the feint hearted.  This led to all sorts of inconveniences such as lack of flexibility, accessibility and compatibility. Now we can add animations to our website "natively" using CSS 3. Let's take a simple example, try hovering over any of the columns below using Chrome or Safari:














Whenever the mouse hovers over any one of the columns, the column expands for a short period of time and goes back to its original size. This effect is created using CSS 3 keyframe animation which is currently only supported by webkit browsers. Here's how it's done:

a.anim{
    display:block;
    text-decoration:none;
    width:150px;
    height:120px;
    padding-top:80px;
    text-align:center;
    margin:1px;
    background:url(http://www.w3.org/html/logo/downloads/HTML5_Logo_64.png) no-repeat #F5F5F5 50% 10px;
    border:solid 1px black;
    float:left;
 }

 a:hover{
    /* Animate */
    -webkit-animation-name:grow;
    -webkit-animation-duration: .4s;
    -webkit-animation-iteration-count: 1;
    -webkit-animation-timing-function: ease-in-out;

    /* Forward Compatibility */
    animation-name:grow;
    animation-duration: .4s;
    animation-iteration-count: 1;
    animation-timing-function: ease-in-out;

 }
 
 /* Define the Animation */
 @-webkit-keyframes grow {
    0%   {-webkit-transform: scale(1,1);}
    50%  {-webkit-transform: scale(1.2, 1.2);}
    100% {-webkit-transform: scale(1, 1);}

 @keyframes grow { /* forward compatibility */
    0%   {transform: scale(1,1);}
    50%  {transform: scale(1.2, 1.2);}
    100% {transform: scale(1, 1);}

 }



Ok, so our three columns are in fact three anchor elements styled to look like columns, nothing new here. The animation is triggered by the "a:hover" selector where we specify:

  • The name of the animation to trigger: "grow" in this case;
  • How long the animation should take;
  • How many times the animation should run: once in this case
  • The timing/easing function which adds a more organic feel to the animation

Easing adds smoothness to our animation. In this case the easing function is set to "ease-in-out" which means that the animation start slowly, accelerate towards the middle and slow down again at the end. The final part is the animation definition itself which is based on three keyframes. Each keyframe applies a scale transformation to change the size of the object being animated over time. Animations must have at least two keyframes, one for the begining (0% or 'from') and one for the end (100% or 'to'), but you can have as many keyframes as you like between these two. In this case the animation is pretty simple so one additional keyframe set at the middle of the animation (50%) is enough to get the desired effect.  Of course you could add all sorts of effects to your animation such as changing colours, borders, shadows... you name it.  

Summing Up

Presenting all there is to know about HTML5 and CSS 3 in a single blog post is a ridiculous proposition, the subject is as vast as it is exciting - and it's still evolving. What strikes me the most is the fact that HTML5 and CSS 3 bring so much to web development and asks very little in return in terms of learning effort. If you know HTML you know HTML5, same goes for CSS 3. They are not new technologies but rather extensions to what we're already used to, and yet they bring so much more (power) to the table. I've never experienced anything like it with any other language I've used so far. The fact that all the major players in the software industry including Apple, Google and more recently Microsoft, have committed themselves to HTML5 is further testament to its significance to web development and beyond.
Readmore...
Saturday, 18 June 2011

More LSL

0 comments
 
My last post was all about Second Life's Linden Scriptng Language (LSL) and how to add LSL scripts to our objects. We built a simple elevating platform that moved along the z-axis when touched by an avatar. This week I will continue building on this example, introducing new LSL concepts along the way.

Inter-Object Communication


Currently, our elevator starts moving after being touched by an avatar but this is not very realistic. Most elevators move when someone presses a call button and I want to replicate this behavior on my virtual counterpart. The first thing I need is a call button. Since the aim of this blog is to demonstrate LSL rather than creating beautifully crafted objects a simple prim will do as a call button. I need to add a script to my call button to notify my elevator that it should start moving. One way of doing this is to broadcast a message over a specific channel and set the elevator to listen to messages coming from that channel. Here's the button's script:

default
{
    state_entry()
    {
    }

    touch_start(integer total_number)
    {
        llSay (1360, "elev-mov");
    }
    
}

Pretty simple. All the script does is broadcast the "elev-mov" message over channel 1360 whenever the object is touched. We now need to configure our elevator to listen for this message:

default
{
    state_entry()
    {
        llListen(1360, "es1",NULL_KEY, "");
        ...
        ...
        ...
    }
    
     listen(integer channel, string name, key id, string message)
    {
        if (channel == 1360 && name="es1" && message == "elev-mov")
        {
            state moving;
        }
        
    }


The first line in the state_entry script for the default state sets the object to listen to any messages coming in on channel 1360 from an object named "es1" ("es1" is the name I gave the call button). I could also configure the object to listen for messages coming from another object that has a specific Universally Unique Identifier (UUID)but I must admit I haven't yet found a way to do this reliably. Our elevator is now set to listen for messages coming from the call button. We now need to take action on those messages. To do this we add a 'listen' event to our default state script and add the required code. In this case the elevator checks the channel (1360), source (call button) and message and starts moving. You might notice that using this technique, your object could be configured to listen for messaged from multiple objects over multiple channels and react accordingly. I'm not too sure about performance though.

Linking Objects

So now we have a platform that moves when the call button is 'pressed', but it's only a platform. What if we wanted to add 'walls' to this platform to make it look more like an actual elevator. Simply placing these walls on top of the platform will not make them move with it when the call button is pressed! Here's where linking objects comes in handy. To link the walls to the floor hold down the shift key and click on the walls and floor in succession to select all the objects. It is important to select the floor last and I will explain why in a minute. After selecting all the objects click the 'Link' button in the "Build Toolbox" to link the objects together. Now when the call button is pressed, the walls will move together with the platform for a more realistic looking elevator. This will not work unless the floor was selected last when linking the objects because the floor's script would not be set as the parent script of the group.

Final Thoughts

That's it for this week and for my coverage of Second Life. There's much more to Second Life and Linden Scripting Language than I have covered over the past couple of posts, in fact I've just barely scratched the surface. The fact that there's much to discover in Second Life is, for me, beside the point. The real issue for me is how willing am I to scratch below the surface and dig deeper into this virtual world. The answer I'm afraid is I'm not. Although I can appreciate the effort it took to create such a world and admire the vision behind it I was sincerely let down by the whole experience. Second life looks dated (most places I've been to anyway) and feels sluggish. I'm running this on a Core i5 processor and a GeForce GTS 250 graphics card over a 10 megabit connection but it still feels slow. I don't want to sound too negative here, as I said I do admire the vision behind it and what can be achieved within it but I'm afraid that I will not log-back into it any time soon.
Readmore...
Saturday, 4 June 2011

LSL - Objects that Respond to Commands

0 comments
 
Welcome back. Today’s post is all about the Linden Scripting Language and how we can use LSL to make objects respond to simple commands. As with any form of programming, the best way to explain LSL concepts is through practical examples, so I've chosen to build a simple elevator. The elevator will respond to “Touch” events by move vertically between pre-defined minimum and maximum z-axis values.

In order to focus on the actual scripting, I will not spend too much time building and fiddling with the design of the elevator. A simple rectangular platform will do nicely for this example.

Adding a Script

The first thing to do is to add an actual script to our platform. To do this, go to the “Content” tab of the build toolbox and click the “New Script” button. Double-click the newly added script object to open the script editor which should contain the following default script:

default
{
   state_entry()
   {
      llSay(0, "Hello, Avatar!");
   }

   touch_start(integer total_number)
   {
      llSay(0, "Touched.");
   }
}  

States and Events

LSL is an event-driven language based on finite state machines. This means that every object in second life can have a pre-defined number of states and can transition from one state to another by reacting to events. All objects must have at least one state, the default state which is was we can see in the listing above – defined by the ‘default’ keyword. This script also contains two event handlers: ‘state_entry’ and ‘touch_start’. ‘State_entry’ is called whenever the object enters the current (in this case default) state, which in turn calls the LSL pre-defined ‘llSay’ function which basically broadcasts the “Hello Avatar!” message on channel 0, the public channel. On the other hand, ‘touch_start’ is triggered whenever the object is touched while in the default state, broadcasting “Touched” on the public channel.

Building our Elevator script


Going back to our example, our elevator can be in either one of two states: stationary (default) or moving. We want our elevator to start moving when it’s touched and we want it to stop moving when it gets to the target position. Here’s the basic skeleton for our script:

default
{
   state_entry()
   {
      //Determine the next Target position based on the current position
   }
   
   touch_start(integer total_number)
   {
      //Go to the moving state
   }
}

state moving
{
   state_entry()
   {
      //Start Moving and stop when target is reached
   }
}

We need four global variables to store values for the minimum and maximum Z-axis positions, the speed at which the elevator should move and the target position (up or down). These variables are placed at the very top of the script and given their default values. When entering the default state we want to set the target position according to the current position. In other words, if the elevator is currently at the bottom, the target will be the top and vice-versa. We also want the elevator to enter the ‘moving’ state when an avatar touches it:

integer MinZ = 23;  // Minimum Z position (bottom)
integer MaxZ = 27;  // Maximum Z position (top)
integer DeltaZ = 1; // Rate of change of Z (speed)
integer TargetZ;    // Target  Z position

default
{
   state_entry()
   {
      vector position = llGetPos();
      if (position.z >= MaxZ) 
      {
         TargetZ = MinZ;
      }
      else
      {
         TargetZ = MaxZ;
      }
   }
   
   touch_start(integer total_number)
   {
      //Go to the moving state
      state moving;
   }
}

state moving
{
   state_entry()
   {
      //Start Moving and stop when target is reached
   }
}

At line 10 the script calls the llGetPos() function that returns the current position of our object as a vector. A variable of type vector holds three floating point numbers for X, Y and Z values which in this case are our object’s position in 3D space (in metres). The current Z position is then used to determine the elevator’s next target position.

Animating Movement

In the moving state we could of course set the elevator’s position to the current target but that would not be very realistic. An elevator does not simply jump from one floor to another, it gradually moves between floors until the target is reached. To replicate this effect we need some sort of animation to smoothly transition the elevator from the current position to the target. There are multiple ways of doing this but I decided to use a timer event to control movement. Let’s have a look at the code for the moving state:

state moving
{
   state_entry()
   {
      llSetTimerEvent(0.1);
   }
    
   timer()
   {
      vector position = llGetPos();
        
      if (position.z < TargetZ) //Moving Up
      {
         if (position.z + DeltaZ > TargetZ)
         {
            position.z = TargetZ;
         }
         else
         {
            position.z = position.z + DeltaZ; 
         }
      }
      else // Moving down
      {
         if (position.z - DeltaZ < TargetZ)
         {
            position.z = TargetZ;
         }
         else
         {
            position.z = position.z - DeltaZ;
         }
      }        
    
      llSetPos (position);  //Set the new position        
                
      if (position.z == TargetZ) // Target reached
      {
         llSetTimerEvent(0);  //Cancel the timer
         state default;            // Goto the default state
      }
        
    }
}

This code might look slightly complex but in actual fact it’s fairly straight forward. When the object enters the moving state it calls the ‘llSetTimerEvent()’ function to trigger a timer event every tenth of a second. The timer event then does the actual movement. First it gets the object’s current position with the ‘llGetPos()’ function which is then compared to the target. If the current Z position is less than the target it means that the elevator is moving upwards and vice versa. Depending on the direction of movement, the value of ‘DeltaZ’ – the rate of change of vertical position (speed) – is added or subtracted from the current position. The script also makes sure that the maximum and minimum positions are not exceeded. After calculating the new position, the ‘llSetPos()’ function is called to ‘move’ the elevator to the new position. When the elevator reaches its target, the timer is cancelled and the object is moved back to the default state. And that’s it. Now, when the elevator is touched it will gradually move to its destination where it will stop until it is touched again to bring it back.

Further Improvements

Of course there are lots of ways to improve the script. For instance floating point numbers could be used for DeltaZ to make the animation smoother. The elevator itself could be designed much better rather than a simple platform by linking various prims together and use messaging to make them move as a single unit. I’ll be looking into messaging and linking in a future post where we’ll see how we can create a call button for our elevator.

Conclusion

Although building and scripting is one of the most enjoyable things in Second Life (for me at least) it can also be very, very frustrating at times. Given that scripts are compiled and run on the server, each time you modify a script it could take a while for the change to take effect depending on the amount of lag being experienced. When testing a script you usually need to continuously correct or tweak the code and having a bad response time can make this a rather frustrating experience. Also, the risk of being disconnected from second life without warning is also there (happened to me a couple of times) so I had to resort to writing code on a local text-editor and pasting it into Second Life to make sure I always had a backup I could fall back to. Although I have only begun to scratch the surface, it is already apparent that scripting in second life can be pretty powerful.
Readmore...
Wednesday, 1 June 2011

Building & Scripting Objects in Second Life

0 comments
 
This week we shall have a look at how to build (very basic) objects in Second Life and how we can use scripts to add interactivity to these objects.

So let’s get straight down to business…

Building Basics


Second Life provides tools that enable you to create whatever your imagination conjures up. The only limits are practice and your own imagination! The building blocks for most everything in second life are known as Primitives or ‘Prims’ for short. Prims are basic shapes, such as cubes and spheres, provided by the building tools that can be combined together to create the larger more complex shapes in your creation. In total, there are around 15 prims of different shapes at your disposal.

To start building, you must be in an area in which you are allowed to build. This could be your own land, someone else’s land (if the owner gives you permission) or a sandbox, a public area where people can build stuff to their heart’s content. One thing to note about sandboxes is that the objects built in them are only temporary since sandboxes are periodically cleaned by their owners. To avoid losing your creation, make sure to save it in your inventory!

Editing Objects


So, we know where to build and with what to build but how do we actually build stuff? To start building, right-click anywhere on the ground and select “Build” from the context menu. This brings up the build toolbox shown below:

With the magic wand tool selected, choose the desired prim shape and click anywhere on the ground to create the object, a process known as “rezzing” a prim.  Now that we have created our basic object we can go ahead and change pretty much anything about it. An object can be moved, rotated, resized, hollowed out, twisted, tapered… the list goes on. You can also change the texture of the object (the default texture is wood).

To move an object, select “Move” from the toolbox and use the arrows provided to move the object in the desired direction. Similarly selecting “Rotate” and “Stretch” from the toolbox will allow you to rotate and resize an object in 3 dimensions. The X, Y and Z axis’ in Second Life are colour-coded Red, Green and Blue respectively and the control surfaces provided to manipulate the object follow this standard as illustrated below:

Moving an object

Rotating an object

Resizing an object

You can also manually set numerical values for moving/rotating/re-sizing objects rather than using the control surfaces for more precise control.

Building a Chair

A chair is a good candidate to demonstrate how to use the build tools to create something that is actually useful. My chair will have four wooden legs and fabric-covered seat and back, nothing too fancy. Here’s the finished product:

The finished product
The first step is to create all the basic components of the chair independently of one-another starting with the seat. The seat is made from a cube prim which I flattened into the correct shape. I then took a copy of the seat and rotated it around the x-axis to make the back. I also re-sized the back to make it slightly narrower than the width of the seat. Finally I used another cube prim to create the first of the legs which I copied 3 times to create the set. At this point I should mention some pretty handy shortcuts I found for copying and rotating objects which make the process much quicker than having to go back to the build toolbox each time. To copy an object make sure it is selected, then hold down the ‘shift’ key and click and drag away from the object. This will create an exact copy of the selected object. To rotate an object, simply hold the ‘Ctrl’ key and use the rotation control surfaces to rotate along the desired axis. Here’s what the chair looks like before assembly:
The chair components

Before assembling the chair however I wanted to assign different textures to the various components. I wanted my chair to have a fabric seat and wooden legs. To change the texture of the currently selected part(s), goto the “Textures” tab in the build toolbox and click on the wooden texture thumbnail. This will open up the “Pick Texture” dialog box from where you can browse through all the available textures. I chose to go with the standard “Fabric Linen” texture found under the “Fabrics” folder.
Applying a texture

Similarly, a wooden texture is applied to the chair's legs.

The final step is assembling the chair, starting from the legs. To make it easier to see what I was doing, I opted to position the legs while the seat was still on the floor. I also made use of the “Snap To Grid” option to help me better position the legs relative to the seat. Snapping to the grid also made it much easier to equally space the legs.

Snapping to grid makes it easier to align objects

When I was happy with the position of the legs, I simply slid the seat along the Z-axis to bring it above them and correctly positioned (and slightly tapered) the back. Finally I selected all the parts and linked them together to make them behave like one object. Job done.

Introduction to Scripting

All scripts in Second Life are written in Linden Scripting Language, LSL for short, which is syntactically similar to C and Java. LSL scripts are interpreted and executed on the Second Life servers which send the results back to the viewer. LSL is state and event driven, meaning that an object can be in one of many states and transitions from one state to another by reacting to events. For instance a door can be open or closed (states) and can transition from one state to the other after being touched (event).

In my next post we will have a closer look at LSL and how we can add scripts to our objects. We shall also see how we can add interactivity to our objects by making them respond to commands.
Readmore...
Tuesday, 24 May 2011

Born Again... Virtually

0 comments
 
This week’s post is all about Second Life, an online virtual world created by a company called Linden Lab. Good or bad, I had never heard about Second Life before starting this course. Considering that as of 2011 Second Life has more than 20 million registered user accounts (Wikipedia) I had to ask myself how I could have missed it. I was never a fan of Massively Multiplayer Online Role-Playing Games (MMORPG) so that could explain why, but that’s beside the point. More importantly Second Life is more than just an MMORPG, firstly it’s not really a game and secondly it’s much more massive… from all angles. Its name says it all, Second Life allows you to lead a second, virtual life online.
I must admit that I found the prospect quite scary (for lack of a better word), and this was before I downloaded the client software or created an account. I mean scary in the sense that there is so much one can do and see in Second Life that it’s quite frankly overwhelming. But it only really hits you once you get in.

Creating An Account

I started off by creating an account, which is a task in itself. With more than 20 million registered users, trying to find a good user id is like looking for a particular pixel on the Second Life Grid (a needle in a haystack is easy in comparison). I have an obsession with my user/character names, I want them to be different from my real name, sound cool (to me at least) and make some sort of sense. Putting these constraints to the already limited possibilities was not helping. Each user name I tried was taken and what I found rather annoying was the fact that the website did not suggest alternatives. Those of you who follow my blog will know that I’m a stickler when it comes to User Experience (UX) and will understand my annoyance at such things. I finally settled for the name “Wyder” which stands for “Wayne” (my real name) “derivative” and which was available. Next I downloaded and installed the Second Life Viewer and I was good to go.

Virtually There

After logging in I found my virtual self (Avatar) on Welcome Island, a region on the Grid aimed at getting newbies like me up and running in Second Life within a claimed 10 minutes. Welcome Island lived up to its promises and I got a hang of the basic controls well within those 10 minutes, including walking/flying around, chatting, basic camera controls and object interaction. With the pleasantries out of the way it was time to take the plunge into the Grid proper. I popped out the destination guide and I was surprised at the sheer number of locations available. I spent the good part of an hour teleporting between locations, spending a few minutes in each just to get a feel for the interface and to assess performance across locations. It was immediately apparent that a good internet connection and even better hardware is required to experience this virtual world at its best.

Customising the Avatar

I also attempted to change the look of my avatar and was impressed with the level of customization that is available to you. It did take some trial and error to decipher what each setting did (I still cannot figure out how to change the eye colour) and to realize that your hair can be changed only by acquiring new hair styles (at least that’s what I concluded). I also found some preset ‘looks’ in the inventory but to my horror, the presets re-set the facial features I had painstakingly tweaked to my liking just before. Saying I was frustrated is an understatement, so I set everything back to default and let the whole customization thing be for a while. I’ll try again another time.

Conclusion

I have to say that I did feel quite a bit disorientated, out of place even, while logged into Second Life. There’s so much to take in that it will take me several other sessions to feel comfortable enough. What does Second Life tell me? Well, I don’t want to pass judgment just yet as I still have to get over my baby blues J so I’ll leave all philosophical considerations for another post. I can say however that I am looking forward to building stuff in this virtual world and take a look at Linden Scripting Language to see what I can accomplish. In the meantime, I’ll concentrate on getting a better hang of the interface, the places, the people and the life!
Readmore...
Thursday, 5 May 2011

Building a Web Space Management System - 4

0 comments
 
As promised, in this fourth (and final) post of the series I will explain how I went about building the Web Space Manager itself.

I must admit that when I first read the requirements for this assignment I thought that it would be quite challenging to do. I came to realise however that the real challenge was learning how to structure a PHP application in such a way as to achieve as much sepraration as possible between the presentation and application layers while keeping the code easy to follow (without overloading pages with PHP includes). Once I got the hang of it though, the rest was pretty easy, including the Space Manager page.

The Web Space Manager

As stated in my last post, I wanted the user to be able to access all the application's functionality from one 'main' page. This includes the ability to browse through the uploaded files, create folders, upload new files and delete existing files. I also wanted users to be able to see at a glance how much free space they had left. Here's the end result:

Preventing unauthorised access
The first thing the page does is verify that the user is authenticated by checking the appropriate session variables set were by the login page. If they are not, the user is re-directed to the login page. There are 2 such variables, a flag that signals if the user is authenticated and the serialised user class itself. For this project I opted to check just the flag and ignore the user class. For added security I could if need be, re-validate the username and password (using the class) against the database but that would mean a query being executed on the database each time the page was loaded which could affect performance. I figured checking the flag would suffice for this project.

The SpaceManager Class
The SpaceManager class is the 'core' of this whole project and is responsible for all operations performed on the user's web space. Whenever the class is instantiated, it sets the path to the user's home directory, the quota and calculates the used space. If the user has logged-in for the first time, it creates the user's space (home folder) beforehand. I'll be explaining how this class works in more detail while going through each piece of functionality.

The File Browser

The file browser initially displays the files and folders in the user's home directory. These 'Home' folders are created under a folder called 'vault' located in the website's root. To prevent users from browsing to the contents of this folder (through the browser's address bar) I created a '.htaccess' file within the 'vault' folder that contains a 'deny from all' rule. Simple, but effective.
The list shows the file/folder name, the size (in the case of files) and last modification date. Folder names can be clicked to display the files within that folder. While browsing, breadcrumb-style links are shown in the toolbar, from the home folder to the current directory, to serve as useful shortcuts. Different icons are used for the different file types and to distinguish between folders that are empty, compressed and those that contain files. The file browser also allows users to delete selected files and create new folders. Let's look at each of these bits of functionality individually.
The file browser

Listing Files & Folders
The 'listFiles()' function of the SpaceManager class iterates through the contents of the current directory (initially the user's home directory) and returns a multidimensional array describing the files and folders contained within. Here's the code for the function:

function listFiles(){
   $fileList = array();   
   $files = array();
 
   $path = $this->_home.DS;
   if ($_SESSION['currentdir'] !== "") $path .= $_SESSION['currentdir'].DS;  
  
      if (is_dir($path)) {
         if ($dirref = opendir($path)) {
            while (($file = readdir($dirref)) !== false) {
               if ($file !== '.' && $file !== '..') {     
                  $files['name'] = $file;
                  $files['type'] = filetype ($path.$file);
                  $files['size'] = filesize ($path.$file);
                  $files['time'] = date("F d Y H:i:s.", filemtime($path.$file));
     
                  if ($files['type'] == 'dir') {    
                     $files['count'] = count(glob($path.$file.DS."*")); 
                     $files['ext'] = "";
                     $files['icon'] = 'dir';
                     
                     if ($files['count'] == 0) $files['icon'] .= "-empty";
                  } else {      
                     $files['count'] = 1;
                     $files['ext']  = pathinfo ($path.$file, PATHINFO_EXTENSION);
                     $files['icon'] = $this->getIconForExtension($files['ext']);
                  }
                  array_push($fileList, $files);
               }         
            }
            closedir($dirref);
         }
      }
      // Sort the list by type (folders followed by files) and name  
      if (sizeof($fileList) > 0 ) {  
         foreach ($fileList as $key => $row) {
            $name[$key] = $row['name'];
            $type[$key] = $row['type'];
            $size[$key] = $row['size'];
         }  
         array_multisort($type, SORT_ASC, $name, SORT_ASC, $fileList);
      }
      return $fileList;
   }

The currently selected directory is stored as a session variable. The 'opendir' PHP function is used to open this directory and a while loop is used to iterate through the contents. For each file/folder found, the '$files' array is populated with its properties including the name, type (file or folder) size, last modification date etc. PHP functions are used to get this information, including 'filetype', 'filesize', 'filemtime' and 'pathinfo' which in this case is used to determine a file's extension. Certain properties (such as the icon) are set depending on whether the file is in fact a file or a folder. The SpaceManager class' getIconForExtension function returns the icon to use (basically a CSS class name) based on a simple 'switch' statement.
Although it might sound complex, it's actually quite straight forward. What's interesting however is how to sort the resulting multidimensional array '$fileList'. I wanted to sort the files by type - folders first - and name. To do this, I iterate through each file, extracting the name and type into separate arrays. I then pass these arrays to the 'array_multisort' PHP function in the order I need to sort my '$fileList' array which then is returned back to the main page. At this point it's just a question of building an HTML table while iterating through this array to display the files and related information:

...
// Get list of files from SpaceManager Class ($Space)
$files = $Space->listFiles();         
$index = 0;
$class = "";
$path = "";
   
if ($_SESSION['currentdir'] !== "") $path = $_SESSION['currentdir'].DS;       
     
foreach ($files as $file) {
 
   if ($file['type'] == 'dir'){
      $text = "".$file['name']." (" .$file['count']." files)";
   } else {
      // create download link 
      $text = "".$file['name']."";
   }      
        
   if ($index == 1) {
      $class = " class = 'alt' ";
   } else {
      $class = "";
   }
           
   echo "<tr".$class.">";
   echo "<td><input type='checkbox' name='chk_".$file['type']."[]' value='".$file['name']."'/>";        
   echo "$lt;td class='icon16 icon16-".$file['icon']."'></td>";
   echo "<td>".$text."</td>";
   echo "<td>".$file['type']."</td>";
   echo "<td class='text-right'>".$Space->formatBytes($file['size'],1)."</td>";
   echo "<td>".$file['time']."</td>";
   echo "</tr>";
        
   $index = 1-$index;       
}

As you might have noticed from the code, folder names and file names are created as links (HTML anchors) such that users are able to 'open' or browse the former and download the latter. Folder links point to the 'goto.php' script while file names point to the 'download.php' script. I will be explaining these two scripts in detail in the next sections.


Browsing Through Folders
As explained above, folder names are rendered as HTML anchors that link to the goto.php script, passing the location as a query string. The script simply sets the current directory session variable to the location in the query string and re-directs the browser to the main.php page which shows the content of the selected folder, as explained above. While browsing through the folders, a "breadcrumb trail"-style navigator is built to allow users to see where they are in the folder structure and 'hop' directly to any folder in the trail. These links work exactly like the folder links in the file browser, using the goto.php script. The 'getBreadCrumbs()' function of the SpaceManager function is responsible for building this trail of links. It uses the 'explode' PHP function to split the current directory path (by the directory delimiter) into an array of folder names. It then iterates through this array creating a string of links which it returns to the calling script.
Breadcrumbs in the file browser toolbar

function getBreadCrumbs() {

   $homeFolder = $_SESSION['user']->getUserName();
   $path = ''; 
   $delimiter = " ";
   $out = "";
   $index = 1;

   // Set the current directory. 
   if (!isset($_SESSION['currentdir']) || $_SESSION['currentdir'] == '') {  
      $_SESSION['currentdir'] = '';
      $this->_uploadPath = VAULT.DS.$this->_home.DS;   
   } else {
      $this->_uploadPath = VAULT.DS.$this->_home.DS.$_SESSION['currentdir'].DS;
      
      // Parse the current directory to obtain the trail
      $folders = explode (DS, $_SESSION['currentdir']);
      // Iterate through the folder names and create breadcrumbs for all except the home and current folders.  
      foreach ($folders as $folder) {
         if ($folder !== $homeFolder) {
            if ($index !== sizeof($folders)) {
               $index ++;
               if ($path == '') {
                  $path = $folder;
               } else {
                  $path .= DS.$folder;     
               }
               $out .= $delimiter." ".$folder."";
            } else { // Last folder name in the trail should not be a link
               $out .= $delimiter." ".$folder;
            }
         }
      } 
   }
   return $out;
} 

Downloading Files
Similarly to folders, file names are rendered as links to allow the user to download the files:

// create download link
$text = "<a href='download.php?x=".$path.$file['name']."'>".$file['name']."</a>";

The link points to the 'download.php' script that expects the path to the file as an argument in the query string. The script makes sure the user is logged in, checks that the file exists and initiates the download:

<?php
   include_once("./inc/shared.php");
   include_once ('.'.DS.'inc'.DS.'checklogin.php');
 
   if (isset($_GET['x']) && $_GET['x'] !== "") {
  
      $path = VAULT.DS.$_SESSION['user']->getUserName().DS.$_GET['x'];
  
      if (file_exists($path)) {
         header('Content-type: application/force-download');
         header('Content-Disposition: attachment; filename="'.basename ($path).'"');

         readfile($path);
        
      } else {
         header ("Location: ".HOME."main.php");
         die();
      }
  
   } else {
      header ("Location: ".HOME."main.php");
      die(); 
   } 


Users can only download files from their own 'space' since the path received by the script is relative to the user's home directory. At this point I would like to mention that setting the names of home folders to the registered usernames is not such a good idea. In a real project, I would probably generate some sort of GUID or a hash based on the username to set the name of the various home folders.

Creating Folders
To create a folder, a user enters the new folder name and clicks the "New Folder" button. The text-box and button are actually part of an HTML form that is set to post to the 'newfolder.php' script. This script gets the name of the folder from the '$_POST' asscociative array and creates an instance of the SpaceManager class. It then appends the new folder name to the current directory path and calls the 'createFolder()' function of the spaceManager class which in turn simply calls the mkdir PHP fucntion to create the folder. The script then re-directs the browser to the 'main.php' page which displays the new folder in the file browser.

Deleting Files and Folders
You may have noticed that there is a check-box next to each file and folder being listed (see File Browser further up this post). This check-box is used to mark the files and folders the user wishes to delete. Here's the code that's generating these check-boxes (taken from the file browser listing above):

echo "";      


The file list is contained within an HTML form whose action is set to 'delete.php'. This form is submitted when the user clicks the delete button in the file browser toolbar. As can be seen from the code-snipped above, the name of a check-box is set to either 'chk_file' or 'chk_dir' depending on whether the associated item is a file or a folder. Furthermore, the check-box value is set to the file/folder name. By using identical names for the check-boxes (per type) we can obtain an array of folder/file names when posting to the 'delete.php' script as shown below:

<?php
 
   include_once("./lib/shared.php");
 
   $SpaceMan = new SpaceManager($_SESSION['user']->getUserName());  

   // Loop through the list of files (if any) and delete. 
   if (isset($_POST['chk_file'])) {
      $files = $_POST['chk_file'];
      for ($i = 0; $i< count($files); $i++) {
         $SpaceMan->deleteFile($files[$i]);    
      }
   }
   // Loop through the list of folders (if any) and delete.
   if (isset($_POST['chk_dir'])){
      $folders = $_POST['chk_dir'];
         for ($i = 0; $i<count($folders); $i++) {
            //Build the full folder path
            $folder = VAULT.DS.$_SESSION['user']->getUserName().DS.$_SESSION['currentdir'].$folders[$i];
     $SpaceMan->deleteFolder($folder);  
         }  
   }
   // Redirect to the main page when finished. 
   header ("Location: ".HOME."main.php");
   die();

The 'delete.php' script is quite straight forward. It simply loops through the lists of check-boxes and calls the 'deleteFile' and 'deleteFolder' functions of the SpaceManager class.

The 'deleteFile' function simply checks that the file exists and calls the PHP 'unlink()' function to delete the file. 'deleteFolder' on the other hand is slightly more complex. A folder can only be deleted if it is empty, so if it's not we first need to delete its contents which might also be folders...recursion anyone? Here's the code:

function deleteFolder($folderPath) {
   
   if(!file_exists($folderPath) || !is_dir($folderPath)) { 
      return false; 
   } elseif(!is_readable($folderPath)) { 
      return false; 
   } else { 
      $directoryHandle = opendir($folderPath); 
        
      while ($contents = readdir($directoryHandle)) { 
         if($contents != '.' && $contents != '..') { 
            $path= $folderPath . DS . $contents; 
            if(is_dir($path)) { 
               $this->deleteFolder($path); 
            } else { 
               unlink($path); 
            } 
         } 
      } 
        
      closedir($directoryHandle); 
  
      if(!rmdir($folderPath)) {           
         return false; 
      } 
        
      return true;
   } 
 }  
 

The function recursively calls itself until all the contents of a directory (and any sub directories) are deleted.

And that's about it, all the main bits of functionality explained (understandably I hope). There are other bits an pieces which I have not mentioned, such as handling the quota and how the 'Space Utilisation Meter' was rendered, but since these bits are rather simple, I didn't want to bloat an already lengthy post. Having said that, if anyone is interested in learning more, drop a comment and I will be more than happy to explain further in another post.

Conclusion

I have to say that overall, this project was qiute enjoyable. As with any first encounter with a language, the start was quite frustrating especially until I settled on a way to structure the code which made sense to me and with which I was comfortable. Once past that hurdle it's just a matter of learning the new syntax. This does not mean however that the code is perfect (it never is) or that it is what you might call best-practise. There are lots of things to improve especially with respect to error/exception handling and security, which when pressed for time, are always the first things to drop off the list (unfortunately). Having said that, I plan to improve on these aspects (especially security) in the coming days. Things like implementing stricter validation of POSTed values and query string encryption are good learning opportunities. The project also implements features to make it as platform independent as possible such as using the directory separator global variable when building paths and URLs. Another nice feature to add would be to make the project database-independent. This could be achieved by encapsulating all database specific calls (such as mysql functions) into a separate classes (per database) each inheriting from a common abstract class that serves as an interface. The list goes on.

That's it for this series, I hope you enjoyed reading it as much as I did learning what's written in it!
Readmore...
Tuesday, 3 May 2011

Building a Web Space Management System - 3

0 comments
 
Last week I mentioned that I wasn't to happy with the way the code was structured so far. It was untidy to say the least and hard to maintain, even at this early stage. I also said that I was looking into MVC and OOP to instill some sense of structure and order to the project. We have a saying in Maltese that translates to: "the sauce will cost you more than the fish it's meant to garnish". Writing even a simple MVC framework for this project would have been massive overkill and would have taken too much time and effort for something that is not meant for production. Of course there are PHP frameworks out there that support MVC but using existing frameworks is beyond the scope of this blog.

However, I wasn't giving up. I still wanted to improve the way the code was written so I turned to good old Object Oriented Principles (OOP) and some other nifty tricks I picked up on the net while doing my research to help me turn my spaghetti code into neatly layered lasagne!

I wanted to spend just a little bit of time on how I re-structured the code I had so far before going further, as all the new stuff I added is obviously based on these changes. You will also notice changes in the look and feel of the app (being a UX buff I couldn't resist :) and the name (I've changed everything else so why not!).

1. Introducing Objects

Up to now we have functionality that allows users to register and log-into our system, and this sentence alone screams "UserAccount Class". My UserAccount class is structured as follows:

Properties

  • username
  • password
  • email
  • errors()  - an array that stores any errors thrown by the class
  • salt         - a random set of characters that are appended to the user's password before hashing it to improve security
Methods

  • login - accepts a username and password as arguments and handles authentication
  • register - attempts to register the user
  • isUsernameAvailable - private method used during registration to check whether the chosen username is unique or not.
The code in the UserAccount class methods was extracted from the login and registration pages.  This code remained largely intact, with only minor modifications required to suit the class.

I also added a small FormValidator class to handle common validations such as comparing strings (used in the registration form to confirm the user's password choice).

2. Re-structuring the Pages

With the authentication and registration code safely encapsulated in the UserAccount class, the login and registration pages became simpler. So much so that I decided to merge them into one page. The login page now contains two forms: the login form and the registration form. These forms post to the "processlogin.php" and "processreg.php" scripts respectively. These scripts contain all the calls to the UserAccount class that handle authentication and registration. Placing this code in these script files separates server-side code from the HTML which is exactly what I want. I also placed php code that is common to all pages (such as session management and database connection) in another script called "shared.php". Here's the code for the "shared.php" and "processlogin.php" scripts:

<?php
   define ('DS', DIRECTORY_SEPARATOR);
   define ('ROOT', dirname(dirname(__FILE__)));
   define ('MaxFiles', 3);

   include_once (ROOT.DS."lib".DS."dbconfig.php");

   function __autoload($className) {
      if (file_exists(ROOT . DS . 'lib' . DS . strtolower($className) . '.php')) {
         require_once(ROOT . DS . 'lib' . DS . strtolower($className) . '.php');
      } else {
         /* Error Generation Code Here */
      }
   }

   mysql_connect(DB_HOST, DB_USER, DB_PASSWORD);      
   mysql_select_db("db_webspace");

   session_start();


The shared.php script defines constants for the root folder of our website and the directory separator. The latter is useful because of the fact that Windows and Linux have different directory separators (backslash and forwardslash) so it makes our code slightly more platform independent which is nice. The scripts also makes use of the __autoload function in PHP to make sure that class files (such as our UserAccount.php) are "automagically" loaded whenever they are needed, which is pretty handy. It also connects to the database and starts the session. I've stripped all the error handling code from the script for simplicity's sake, but obviously there is some to handle those annoying exceptions.

The "processlogin.php" script below is called when posting the login form. In a nutshell, it reads the values for the username and password from the $_POST associative array and after making sure the values are not blank, it creates an instance of the UserAccount class and attempts authentication. If all is successful the script sets session variables to indicate that the user has been authenticated and re-directs to the main application page. If not, the login page is included so that the error messages can be displayed to the user.

<?php // processlogin.php
   include_once ('./lib/shared.php');
   $errMsg = "";
   
   if ($_POST['txtUname'] != "" && $_POST['txtPassword'] != "") {
      $User = new UserAccount ();
  
      if (!$User->login($_POST['txtUname'], $_POST['txtPassword'])) {
         $errMsg = 'Incorrect Username or password';
      }
   } else {
      $errMsg = "Please enter a username and password";  
   }

   if ($errMsg != "") {
      include 'login.php';
   } else {
      $_SESSION['loggedin'] = 1;
      $_SESSION['user'] = $User;
      header('Location: .' . DS . 'space' . DS . 'main.php');
      die();
   }


You may notice that in both these scripts, since they solely contain PHP code, the closing PHP tag "?>" was left out. This is purposely done to prevent any whitespace from being unintentionally sent into the response.

Here's what the combined login and registration page looks like:


3. The Web Space Manager


Past the login and into the 'private' area of the site which I refer to as the 'Web Space Manager'. I wanted a single interface through which users could browse through the files and folders of their web space, upload new files and see how much free space they had left. I also wanted to allow users to create folders in their space and delete unwanted files.  Quite a lot to do, so I want to dedicate the next post to describe how I went about it, in the meantime here's a little screenshot of the space manager page (still needs polishing up).


4. Final Thoughts

Although going back and restructuring the pages took some time and effort, the end result was definitely worth it.  The entire exercise helped me understand more how PHP works and how to get more out of it which after all is the whole point of this project.  I also picked up some nifty tips & tricks while going through countless articles on the web, which is always good.
Readmore...