CSS, Firefox, Internet Explorer, JQuery, MySQL, PHP

Zune Quickplay Inspired JQuery Gallery with Tooltips

Hello and welcome I have been off recently working on something new for my video site.

I thought I would produce some sort of tutorial on here so others can understand how to customize their JQuery galleries. I will be starting to talk you through the process of producing the end product in a couple of days but I thought I would post the initial screen shot now so you can all see what the end product will look like:

Zune Inspired JQuery Gallery
Zune Inspired JQuery Gallery

What we are looking at is the big gray strip across the middle of the page, ignore the header or footer. There are three parts to the gallery, “hot” (popular) videos, default spotlight videos and editors pick. The JQuery gallery will scroll between each part when one the links is clicked and the links will change accordingly (I could have done it all by normal AJAX but I wanted the scroll effect so meh).

The sections themselves are broken down into videos. There is one main highlight video which has information about it (length and rating) and four smaller image representing other videos. When the user hovers over one of the images and tooltip will show displaying the title of the video.

In this tutorial I will be using, HTML, CSS, JQuery (Scroll and AJAX), PHP and MySQL and I will be checking this product in IE7 (IE8 is emulated as IE7) and Firefox (just the two browsers I have at hand, no IE is not running under Wine, it’s in a VM :P).

One last tip before I say bye for a couple of days whilst I get the finishing touches done, this tutorial is really only for professionals I would recommend the newer users stick to the JQuery gallery post I uploaded some time ago. This tutorial will be using the full Monty.

MySQL, PHP

File Upload in PHP with Progress Bar

Even bigger note: This page is outdated See here for an updated version

Note: The bugs within this post have been fixed, however, this article is still old I will be updating with the fixed and perfect version as soon as I get my server running correctly again.

I see a lot of web developers asking the same question, “How do you make a PHP AJAX style file upload script with a progress bar?”. In the old days of PHP progress bars used to be unthinkable, the very thought of having a user friendly uploading interface seemed to scare PHP programmers. That is not the case any more! With being able to add a class that attaches to the lately concieved file upload progress hooks the uploadprogress class sure packs a punch for easy progress bar making.

Tutorial Note:

This tutorial was completed under Ubuntu 9.04 and likewise reflects Linux processes. If you wish for a Windows version I will do one.

I would recommend this tutorial only for advanced web developers. For new people it would be best to chew into something that won’t cause a chronic headache.

Bug #00: File upload progress for new files halts all previous upload progress bars despite the fact they are not completed. (being resolved now)

Introduction

This tutorial will show the user how to create an upload script very much like the new one found on YouTube (still in Beta I believe, so not instantly accessible). This tutorial was taken from a few websites I found. I have combined the knowledge with some of my own. This is going to look really messy at first but towards the end we will build it all into a script like the new upload script for YouTube and might have better validation than theirs as well.

I will begin with stating the websites/articles that helped me produce this upload script. These links are essential reading if you wish to succeed in this area:

1. JQuery CSS Documentation (Random but true)

2. pecl page for uploadprogress extension (required to understand so get reading :P)

3. Tips for Twits. A very nice tutorial for anyone who’s not a noob but not as good as the rest

4. Moki Systems. Another AJAX upload tutorial people will find useful.

5. Ajax F1. Another Ajax upload tutorial you should take a look at.

Prologue

The very first thing I did was to set-up my server. I have broken these down into easy steps:

1. install your LAMP server. This is covered in earlier posts.

2. Use the following command within terminal. This command will give you the extension which allows for php to monitor the progress of file uploads. The extension is called uploadprogress:

Sudo pecl install uploadprogress

Tutorial Note:

  • It might tell you that you need Phpiz or something. Just type in terminal “sudo apt-get install php5-dev”. If you do not use sudo (root) then you will not have permission to install.
  • Further edit: There are times when the computer might say you don’t have pecl installed. I will add solutions in a while since I also had that problem but forgot how I fixed it now.

3. After having installed the extension add it to the php.ini by entering into terminal:

sudo gedit /etc/php5/apache2/php.ini

and at the bottom adding:

extension=uploadprogress.so

After this you will need to restart Apache so key in terminal:

sudo /etc/init.d/apache2 restart

4. Now for the final part; installing JQuery. If you have never heard of JQuery before I strongly suggest you update yourself because you haven’t in the last 3 years. JQuery allows you to do many things but most commonly access quick JSON, AJAX and special effects for pages to make them more user friendly to the end-user.

It just so happens Tips for Twits has a nice download link for what we need right here. Once this has been downloaded I strongly recommend you only extract jquery.js to a folder within your website root directory called “js”.

And viola, it’s all set-up :).

The Dirty Part

Ok, we have the environment set-up. The original version of this tutorial used the Tip for Twits tutorial. When I actually tried to use the tutorial code it did not function all that well and kept freezing the browser periodically so I created my own from a base provided by the makers of the uploadprogress extension to PHP. There are three files involved within the script (not including user management etc, only the upload script):

  • videoUploadMain.htm (videoUpload.php as well if you are like me and use a template framework such as Smarty)
  • server.php
  • info.php
  • jxSV_VID.php
  • uploadIDMake.php

Now I should explain how each of them work and how it all comes together, it is rather confusing however. The main page that contains all the working and the form is videoUploadMain.htm/php. From this the info and server pages uses PHP echo’s in order to send Javascript messages back to the main page when they are called, it’s rather ingenious coding really gets around the need for AJAX. This makes the page much faster and no longer freezes for brief periods whilst uploading.

So hopefully I have explained that ok but most likely not. The main page calls the info.php for upload progress and the info.php responds by sending a Javascript call via an iframe which is then enacted within the main page to update the progress bar. After the upload is completed the form then feeds server.php through an iframe which then can bounce back a Javascript call to the main page meaning that you could do everything needed without ever needing to refresh the page or redirect. The two extra pages jxSV_VID.php and uploadIDMake.php allow for a new upload ID to be made (uploadIDMake.php) and for the video file details to be saved via AJAX post (jxSV_VID.php)

Ok, so I begin with the front end and specifically the HTML. Below you will see the html to the actual upload page itself:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style type="text/css">
@import url("styles/default/css/stylesheet.css");
@import url("../css/stylesheet.css"); /*CSS Imports*/

</style>
<script src="js/jquery.js"></script>
<script src="jquery.form.js"></script>
  
  {literal}
 
<script type="text/javascript">


//AJAX to add video file details
function AddDetails(uploadID){

	//gets the needed details about the video file from the form inputs
 	var title = "input#title"+uploadID;
 	var desc = "#description"+uploadID;
 	var tags = "input#tags"+uploadID;
 	var rate = "#rate"+uploadID;
 	var rate1 = "#rate1"+uploadID;
 	var sex = "#SEXOPT"+uploadID;
 	var upIDFirst = "input#uploadTI"+uploadID;

 	var titleFin = $(title).val();
 	var descFin = $(desc).val();
 	var tagsFin = $(tags).val();
 	
 	var upID = $(upIDFirst).val();

 	//this uses the check attribute on option boxes and check boxes to detect whether they are checked or not. This is the only (decent) way to do this in JQuery.
 	if($(sex).is(":checked")){
 		var sexFin = $(sex).val();
 	}else{
 	 	var sexFin = "";
 	}

 	if($(rate).is(":checked")){
 		var rateFin = $(rate).val();
 	}else{
		rateFin = "";
 	}

 	if($(rate1).is(":checked")){
 		var rate1Fin = $(rate1).val();
 	}else{
		rate1Fin = "";
 	}

 	//Build the string for use with AJAX
	  var dataString = 'title='+ titleFin + '&amp;amp;amp;amp;amp;description=' + descFin + '&amp;amp;amp;amp;amp;tags=' + tagsFin + '&amp;amp;amp;amp;amp;rate=' + rateFin + '&amp;amp;amp;amp;amp;rate1=' + rate1Fin + '&amp;amp;amp;amp;amp;Nud=' + sexFin + '&amp;amp;amp;amp;amp;id=' + uploadID + '&amp;amp;amp;amp;amp;upID=' + upID;
	  alert (dataString);
	  $.ajax({
		  //Post the formed AJAX string to jxSV_VID.php
	    type: "POST",
	    url: "jxSV_VID.php",
	    data: dataString,
	    success: function(msg) {
		    if(!msg) return;

		    var response;
		    eval("response = "+msg);
			
		    if(!response) return;

		    //Judge whether to respond with error or success. This is determined from a JSON string formed within the PHP file.
		    var DivInQ = "#eC_Err"+uploadID;
			var Error = parseInt(response['ErrorCode']);
			
			if(Error == 0){
				$(DivInQ).css({"display" : "block", "color" : "green", "text-align" : "center"});
		    	$(DivInQ).html("<span><b>Success:</b> "+response['Message']+"</span>");
			}else{
				$(DivInQ).css({"display" : "block", "color" : "red", "text-align" : "center"});
		    	$(DivInQ).html("<span><b>Error:</b> "+response['Message']+"</span>");
			}
	  	}
	  });
}

//Add our new upload
function addElement() {
	//determine if user has already made 5 uploads using the numi control
	var numi = document.getElementById('UploadTotal');
	numofupload = numi.value;
	if(numofupload>3){
		ShowMaxUpPanel();
	}else{
		$.ajax({
			//AJAX post to uploadIDMake.php to get the new upload id
		   type: "POST",
		   url: "uploadIDMake.php",
		   data: "",
		   success: function(msg){

			//Add our new upload
				   
			   var ni = document.getElementById('addFFRM');
			   var num = (document.getElementById('UploadTotal').value -1)+ 2;
					  
			   numi.value = num;
			   UID = msg;
					  
			   var newdiv = document.createElement('div');
			   var divIdName = UID;
					  
			   newdiv.setAttribute('id',divIdName);
			   newdiv.innerHTML = "<div id='upload_frmCon"+UID+"' style='margin:10px; border:1px solid #363636; background:#131313; padding:7px; padding-top:15px;'>"+
			   						"<form onsubmit='UP.start(\""+UID+"\")' target='ifr2" + UID + "' action='server.php?id="+UID+"' enctype='multipart/form-data' method='post'>"+
			   							"<input type='hidden' name='UPLOAD_IDENTIFIER' value='"+UID+"' /><span class='non-pText' style='font-weight:bold; font-size:15px;'>Select a video to upload</span>"+
			   							"<div style='padding-top:5px; padding-bottom:2px;'><input type='file' name='"+UID+"' size='30'/><input type='submit' value='Upload File' class='button7' style='right:0; margin-left:220px; margin-right:0;' />"+
			   							"</div>"+
			   						 "</form>"+
			   						 "<iframe id='ifr"+UID+"' src='info.php?ID="+UID+"' style='display:none;' name='ifr"+UID+"'></iframe>"+
			   						 "<iframe name='ifr2"+UID+"' style='display:none;' id='ifr2"+UID+"'></iframe>"+
			   					  "</div><div id='upload_prog"+UID+"' style='margin:10px; border:1px solid #363636; background:#131313; padding:7px; padding-top:15px; display:none;'>"+
								 	 "<span id='uploadTitle"+UID+"' class='non-pText' style='font-weight:bold; font-size:15px;'>Your video is being uploaded</span>"+
			   					  		"<div id='progressuploadouter"+UID+"' style='margin-top:5px; background:#000; border:1px solid #363636; padding:2px;'>"+
			   					  			"<div id='progressUpload"+UID+"' style='width:10%; background:url(styles/default/images/progressback.png); height:30px;'></div>"+
			   					  		"</div>"+
				   					  	"<div id='status"+UID+"' style='color:white; padding:3px;'>Connecting to Server...</div>"+
				   				  
				   				  "<div id='pst_UPActs"+UID+"' style='display:none; margin-top:5px;'>"+
				   				  	"<div align='right'>"+
				   				  		"<span class='non-pText'>"+
				   				  			"<a id='hypforenterdetails"+UID+"' class='normalHyp' href='javascript:Ent_UPDetails(\""+UID+"\");' style='cursor:pointer;'>You can add details about this video now</a> or </span>"+
				   				  			"<input type='submit' id='Cont_Profile' onclick='javascript:ShowExitConfirmPanel();' class='button7' value='Continue To Your Profile'/>"+
				   				  	"</div>"+
						   			"<div style='display:none; margin-top:5px;' id='entUpDetails"+UID+"'>"+
						   				"<div align='center'>"+
						   					"<div style='margin-left:5px; margin-right:5px;'>"+
					   							"<div style='text-align:left; background:url(styles/default/images/speachtop.png); height:10px;'>"+
				   								"</div>"+
			   									"<div style='text-align:left; background:black; border-bottom:1px solid #363636; border-left:1px solid #363636; border-right:1px solid #363636;'>"+
													 "<div style='padding:5px;'>"+
														 "<form id='adddetails'>"+
														 	"<div style='width:100%; padding:5px;'>"+
														 		"<span class='non-pText' style='font-size:15px;'>Video File Details</span>"+
														 		"<a href='javascript:Ent_UPDetails(\""+UID+"\");' class='normalHyp' style='border:0; margin-left:36em;'>Close X</a>"+
														 	"</div>"+
														 	"<div id='eC_Err"+UID+"' style='display:none;'>"+
														 	"</div>"+
														 	"<div style='width:100%;'>"+
															 	"<div id='detailsUploadCon'>"+
															 		"<div id='detailsUploadLeft'>"+
													                    "<fieldset class='fields6'>"+
														                    "<input type='hidden' name='uploadTI"+UID+"' id='uploadTI"+UID+"' value=''>"+ 
													                        "<dl>"+
													                        	"<dt style=''><label for='vidtitle'>Title:</label></dt>"+
													                        	"<dd style=''><input class='inputbox' type='text' id='title"+UID+"' name='title"+UID+"' style='width:100%;'/></dd>"+
													                        	"<dt><label for='videsc'>Description:</label></dt>"+
													                        	"<dd><textarea name='description"+UID+"' id='description"+UID+"' class='textarea' rows='10' cols='30'></textarea></dd>"+
													                        	"<dt style=''><label for='vidtags'>Tags:</label></dt>"+
													                        	"<dd style=''><input class='inputbox' type='text' id='tags"+UID+"' name='tags"+UID+"' style='width:100%;'/></dd>"+
													                        	"<dt></dt>"+
													                        	'<dd>Up to five tags seperated by a comma ",".</dd>'+
													                        "</dl>"+
													                    "</fieldset>"+
															 		"</div>"+
															 		"<div id='detailsUploadRight'>"+
													                    "<fieldset class='fields6'>"+
										                                    "<dl class='fields6' style='padding-left:10px;'>"+
										                                        "<dt id='dtradio' style='width:2em; text-align:left; color:white;'><input type='radio' id='rate"+UID+"' name='rate"+UID+"' value='0'/></dt>"+
										                                        "<dd id='dtradio' style='margin-left:2em; color:white;'><label>Video is appropiate for everyone</label></dd>"+
										                                        "<dt id='dtradio' style='width:2em; text-align:left; color:white;'><input type='radio' id='rate1"+UID+"' name='rate1"+UID+"' value='1'/></dt>"+
										                                        "<dd id='dtradio' style='margin-left:2em; color:white;'><label>Video is NOT appropiate for children</label></dd>"+
										                                        "<dl>"+
											                                        "<dt style='width:2em; text-align:left;'><input type='checkbox' id='SEXOPT"+UID+"' name='SEXOPT"+UID+"' value='1'></dt>"+ 
											                                        "<dd style='margin-left:2em;'>This video contains nudity and/or sexual situations</dd>"+
										                                        "</dl>"+
										                                        "<dt style='width:2em; text-align:left;'></dt>"+
										                                        "<dd style='margin-left:2em;'>Inappropiately marking content for everyone when it is not may ensure your account is subject to deletion</dd>"+
										                                    "</dl>"+
													                    "</fieldset>"+
															 		"</div>"+
															 	"</div>"+
														 	"</div>"+
														 "</form>"+
												 	"</div>"+
												 	"<div style='background:#131313; border-top:1px solid #363636; padding:5px; padding-top:7px; color:#fff; text-align:right;'>"+
												 		"<input type='submit' onclick='javascript:AddDetails(\""+UID+"\");' class='button7' value='Save Video Details' style='margin-right:1em;'/>"+
												 	"</div>"+
			   									"</div>"+
			   								"</div>"+
			   							"</div>"+
			   						"</div>"+
			   					"</div>"+
			   				"</div>";
			   ni.appendChild(newdiv);
		   }
		});	
	}
}

//retry uplod
function tryAgain(uploadID){

	//hides the upload progress and reshows the upload form
	prog = "upload_prog"+uploadID;
	upload = "upload_frmCon"+uploadID;
	
	document.getElementById(prog).style.display = "none";
	document.getElementById(upload).style.display = "block";
}

//this function still does not work, I am working on it. It works if you wanna stop the whole window but not a single form
function Abort(uploadID){
	//Needs to be redone. This will not work for multiple uploads
	if(navigator.appName == "Microsoft Internet Explorer"){
		UP.stop(true, 1);
		window.document.execCommand('Stop');
	}else{
		UP.stop(true, 1);
		window.stop();
	}	
}

//function to use JQuery to produce an effect when showing the enter video details form
function Ent_UPDetails(uploadID){
	var detailsDiv = "#entUpDetails"+uploadID;
	
	if ( $(detailsDiv).is(':hidden') ) {
		$(detailsDiv).slideDown("slow"); 
	}else{
		$(detailsDiv).slideUp("slow");  
	}	
}

//function to show an exit confirm message so the user has to confirm they wish to leave the upload page
function ShowExitConfirmPanel(){

	document.getElementById("darkBackgroundLayer").style.display = "block";
	document.getElementById("exitConfirm_D").style.display = "block";
	
	var newsletter_panel = document.getElementById("exitConfirm_D");
	
	w = 700;
	h = 346;
	
	xc = Math.round((document.body.clientWidth/2)-(w/2));
	yc = Math.round((document.body.clientHeight/2)-(h/2));
	
	var height = yc.toString();
	var temp = height.split("-");
	
	newsletter_panel.style.left = xc + "px";
	newsletter_panel.style.top = "10" + "em";

}

//error panel that tells the user they have reached the maximum uploads at that time
function ShowMaxUpPanel(){

	document.getElementById("darkBackgroundLayer").style.display = "block";
	document.getElementById("Max_UP_S").style.display = "block";
	
	var newsletter_panel = document.getElementById("Max_UP_S");
	
	w = 700;
	h = 346;
	
	xc = Math.round((document.body.clientWidth/2)-(w/2));
	yc = Math.round((document.body.clientHeight/2)-(h/2));
	
	var height = yc.toString();
	var temp = height.split("-");
	
	newsletter_panel.style.left = xc + "px";
	newsletter_panel.style.top = "10" + "em";

}

$(document).ready(function(){
	//continue to profile button
	$("#Cont_Profile").click(function(){
		ShowExitConfirmPanel();
	});

	//return to uploads button in the exit confirmation message
	$("#N_RtnUP").click(function(){
		document.getElementById("darkBackgroundLayer").style.display = "none";
		document.getElementById("exitConfirm_D").style.display = "none";		

	});

	//ok button to say the user understands they hagve reached their max uploads
	$("#UP_MAX_OK").click(function(){
		document.getElementById("darkBackgroundLayer").style.display = "none";
		document.getElementById("Max_UP_S").style.display = "none";		

	});	
});
	
function showUpload(uploadID){
	//stops showing the upload form and shows the upload progress bar.
	var uploadmain = "upload_frmCon"+uploadID;
	var progress = "upload_prog"+uploadID;
	
	document.getElementById(uploadmain).style.display = "none";
	document.getElementById(progress).style.display = "block";
	document.getElementById("add_UP").style.display = "block";
}


//this is where all the magic happens. This function does all the uploading
var UP = function() {
    
    /* private variables */
    
    var ifr = null;
    
    var startTime = null;
    var upload_max_filesize = {/literal}{$uploadmax}{literal};
    
    var infoUpdated = 0;

    //write the upload status whether progess or error message
    var writeStatus = function(text,color, uploadID) {
        var stat = "status"+uploadID;
        var title = "uploadTitle"+uploadID;
        
        var statDiv = document.getElementById(stat);
        var titleDiv = document.getElementById(title);
        
        statDiv.style.color = "white";
        titleDiv.innerHTML = "Your video is being uploaded";
        
        if(color == 3){
            titleDiv.innerHTML = "An error has occured";
            statDiv.style.color = "red";
			//statDiv.style.background = "#fbda54";
			//statDiv.style.border = "1px solid #f6cc24";
        }
        statDiv.innerHTML = text;
    }

    //function and tasks to do after successful completion of the upload
    var completeUpload = function(uploadID, UPID){
        progessupload = "progressUpload"+uploadID;
        stat = "status"+uploadID;
        postupac = "pst_UPActs"+uploadID;
        var title = "uploadTitle"+uploadID;
        var titleDiv = document.getElementById(title);

        titleDiv.innerHTML = "Your video has been uploaded";
    	document.getElementById(progessupload).style.width = "100%";
    	document.getElementById(stat).style.display = "none";	
    	document.getElementById(postupac).style.display = "block";

    	var UPIDField = "#uploadTI"+uploadID;
    	var UPID = UPID;
    	$(UPIDField).val(UPID);
    }

    //allows for a return string
    return {

        //starts the upload
        start: function(uploadID) {
           ifr = document.getElementById("ifr"+uploadID);
           startTime = new Date();
           infoUpdated = 0;
           showUpload(uploadID); 
           this.requestInfo(uploadID);
        },
        //stops the upload
        stop: function(files, error, uploadID, UPID) {
           if (typeof files == 'undefined' || files) {
               	if(error == 0){
				 completeUpload(uploadID, UPID);
               	}else{
                   	stat = "The upload did not succeed. Most likely the file was not a divx video file and failed the tests.<br><br><a class='normalHyp' style='cursor:pointer;' id='retryupload' href='javascript:tryAgain(\""+uploadID+"\");'>If you wish to try again then please click here</a> ";
					writeStatus(stat, 3, uploadID)
               	}
           } else {
               stat = "There was an unkown problem with the upload. This error is normally caused when you attempt to upload:<ul id='generalul'><li>A file which is too large. The max size is 2GB.</li><li>A file which has not got the .divx extension or is not a video file</li></ul><br><a href='javascript:tryAgain(\""+uploadID+"\");' id='retryupload' class='normalHyp' style='cursor:pointer; color:#22a76d;'>If you wish to try again then please click here</a>";
               writeStatus(stat,3, uploadID);
           }
           startTime = null;
        },
        //gets current progress of upload
        requestInfo: function(uploadID) {
            var src = "info.php?ID="+uploadID+"&amp;amp;amp;amp;amp;"+new Date();
                ifr.src= src;
        },
        //updates the status to track current progress
        updateInfo: function(uploadID, uploaded, total, estimatedSeconds) {
            if (startTime) {
                if (uploaded) {
                    infoUpdated++;
                    if (total > upload_max_filesize) {
                        writeStatus("The file is too large and won't be available for PHP after the upload<br/> Your file size is " + total + " bytes. Allowed is " + upload_max_filesize + " bytes. That's " + Math.round (total / upload_max_filesize * 100) + "% too large<br/> Download started since " + (new Date() - startTime)/1000 + " seconds. " + Math.floor(uploaded / total * 100) + "% done, " + estimatedSeconds + "  seconds to go",2, uploadID);
                    } else {
                        var percent = Math.floor(uploaded / total * 100);
                        writeStatus("Download started since " + (new Date() - startTime)/1000 + " seconds. " + Math.floor(uploaded / total * 100) + "% done, " + estimatedSeconds + "  seconds to go - <a href='javascript:Abort(\""+uploadID+"\");' class='normalHyp' style='cursor:pointer;' id='abortthisuploadnow'>Abort this Upload</a>",0,uploadID);
						//changes the upload bar to match the polled progress of the upload
                        document.getElementById("progressUpload"+uploadID).style.width = percent + "%";
                    }
                } else {
                    writeStatus("Download started since " + (new Date() - startTime)/1000 + " seconds. No progress info yet (Negotiating Connection to Server)",0,uploadID);
                }
                //sets a timer for the next poll for getting upload progress
                window.setTimeout('UP.requestInfo("'+uploadID+'")',750);
            }
        }        
    }
}()


</script>
{/literal}
</head>

<body>
{include file="newHeader.htm"}

<div id="darkBackgroundLayer" class="darkenBackground" style="display:none;"></div>

<div id="exitConfirm_D" class="loginboxhover" style="display:none;" align="center">
	<div id="uploadconfirmleave" style="width:712px; z-index:1;">
		 <div class="defaultContainerOuter">
	     	<h2 class="regTop" id="">Hey You! Yeah! You There!</h2>
		     <div class="defaultContainerInner" style="text-align:left; padding:0;">
			     <div style="padding-left:5px; padding-right:5px;">
				     <p>This page is attempting to redirect away from itself to another page. In order to stop my servers from exploding this action will stop any files you are currently uploading.</p>
				     <p>Are you sure you wish to navigate away from this page?</p>
			     </div>
			     <div style="background:#131313; border-top:1px solid #363636; padding:5px; padding-top:7px; color:#fff;">
				     <form action="usrcp.php?home">
					     <div align="right">
					     	<input type="submit" value="Yes, Take Me There" class="button7" style="margin-right:3px;"> Or <a id="N_RtnUP" class="normalHyp" style="cursor:pointer;">No, Take Me Back to The Uploads</a>
					     </div>
				     </form>
			     </div>
			 </div>
		</div>
	</div>
</div>

<div id="Max_UP_S" class="loginboxhover" style="display:none;" align="center">
	<div id="uploadconfirmleave" style="width:712px; z-index:1;">
		 <div class="defaultContainerOuter">
	     	<h2 class="regTop" id="">Maximum Uploads Reached</h2>
		     <div class="defaultContainerInner" style="text-align:left; padding:0;">
			     <div style="padding-left:5px; padding-right:5px;">
				     <p>You can only upload five video files at a time at the moment.</p>
			     </div>
			     <div style="background:#131313; border-top:1px solid #363636; padding:5px; padding-top:7px; color:#fff;">
			     	<div align="right">
					 	<input type="submit" value="OK" class="button7" id="UP_MAX_OK" style="margin-right:3px;">
					</div>
			     </div>
			 </div>
		</div>
	</div>
</div>

<div align="center">
	<div id="MainBody">
		<div class="MainBodyLower LoginBodyLower">
			<div id="vdUPCON">
				<div id="vdUPLeft">
					<div class="defaultContainerOuter">
		     			<h2 class="regTop">Upload Video(s) to StageX</h2>
			    	 <div class="defaultContainerInner" >
			    	 
			    	     <!-- Link to add more upload forms -->
			     		 <div id="add_UP" style="margin:10px; padding:7px; display: none;">
			 				<input type="hidden" value="0" id="UploadTotal" />
							<img src="styles/default/images/plusadd.png" style="vertical-align:middle; margin-right:5px;"/><a href="javascript:;" onclick="addElement();" class="normalHyp">Add More Files to the Queue</a>
		
						 </div>
								
					     <div id="upload_frmCon{$id}" style="margin:10px; border:1px solid #363636; background:#131313; padding:7px; padding-top:15px;">
					     
							 <form onsubmit='UP.start("{$id}")' target="ifr2{$id}" action="server.php?id={$id}" enctype="multipart/form-data" method="post">
								 <input type="hidden" name="UPLOAD_IDENTIFIER" value="{$id}" />
								 <span class="non-pText" style="font-weight:bold; font-size:15px;">Select a video to upload</span>
								 
								 <div style="padding-top:5px; padding-bottom:2px;">
								 	<input type="file" name="{$id}" size="30"/><input type="submit" value="Upload File" class="button7" style="right:0; margin-left:220px; margin-right:0;" />
							 	 </div>
							 </form>
							 
							 <iframe id="ifr{$id}" src="info.php?ID={$id}" style="display:none;" name="ifr{$id}"></iframe>
				  			 <iframe name="ifr2{$id}" style="display:none;" id="ifr2{$id}"></iframe>
				  			 
					 	 </div>
					 	 
					 	 <div id="upload_prog{$id}" style="margin:10px; border:1px solid #363636; background:#131313; padding:7px; padding-top:15px; display:none;">
						 	 <span id="uploadTitle{$id}" class="non-pText" style="font-weight:bold; font-size:15px;">Your video is being uploaded</span>
						 	 <!-- The actual progress bar HTML  -->
						 	 <div id="progressuploadouter{$id}" style="border:1px solid #363636; padding:2px; margin-top:5px; background:#000;">
						 	 <div id="progressUpload{$id}" style="width:10%; background:url(styles/default/images/progressback.png); height:30px;"></div>
						 	 </div>
						 	 
						 	 <!-- Status of the upload -->
						 	 <div id="status{$id}" style="color:white; padding:3px;">Connecting to Server...</div>
						 	 
							 <!-- Post Upload actions -->
							 <div id="pst_UPActs{$id}" style="display:none; margin-top:5px;">
							
								<!-- Post upload choices -->
							 	<div align="right">
									<span class="non-pText">
									<a id="hyp_entDetails" href='javascript:Ent_UPDetails("{$id}");' class="normalHyp" style="cursor:pointer;">You can add details about this video now</a> or </span>
									<input type="submit" id="Cont_Profile" class="button7" value="Continue To Your Profile"/>
								</div>
								
								 <!-- Speech bubble -->
								 <div style="background:transparent; display:none; margin-top:5px;" id="entUpDetails{$id}">
									 <div align="center">
									 	 <div style="margin-left:5px; margin-right:5px;">
											 <div style="text-align:left; background:url(styles/default/images/speachtop.png); height:10px; width:100%;"></div>
											 <div style="text-align:left; background:black; border-bottom:1px solid #363636; border-left:1px solid #363636; border-right:1px solid #363636; width:100%;">
												 <div style="padding:5px;">
													 <form id="adddetails{$id}">
													 	<div style="width:100%; padding:5px;">
													 	
													 		<span class="non-pText" style="font-size:15px;">Video File Details</span>
													 		
													 		<a href='javascript:Ent_UPDetails("{$id}");' class="normalHyp" style="border:0; margin-left:36em;">Close X</a>
													 		
													 	</div>
													 	<div id="eC_Err{$id}" style="display:none;">

													 	</div>
													 	<div style="width:100%;">
														 	<div id="detailsUploadCon">
														 		<div id="detailsUploadLeft">
												                    <fieldset class="fields6">
												                    <input type="hidden" name="uploadTI{$id}" id="uploadTI{$id}" value=""> 
												                        <dl>
												                        	<dt style=""><label for="vidtitle">Title:</label></dt>
												                        	<dd style=""><input class="inputbox" type="text" id="title{$id}" name="title{$id}" style="width:100%;"/></dd>
												                        	<dt><label for="videsc">Description:</label></dt>
												                        	<dd><textarea name="description{$id}" id="description{$id}" class="textarea" rows="10" cols="30"></textarea></dd>
												                        	<dt style=""><label for="vidtags">Tags:</label></dt>
												                        	<dd style=""><input class="inputbox" type="text" id="tags{$id}" name="tags{$id}" style="width:100%;"/></dd>
												                        	<dt></dt>
												                        	<dd>Up to five tags seperated by a comma ",".</dd>
												                        </dl>
												                    </fieldset>
														 		</div>
														 		<div id="detailsUploadRight">
												                    <fieldset class="fields6">
									                                    <dl class="fields6" style="padding-left:10px;">
									                                        <dt id="dtradio" style="width:2em; text-align:left; color:white;"><input type="radio" id="rate{$id}" name="rate{$id}" value="0"/></dt>
									                                        <dd id="dtradio" style="margin-left:2em; color:white;"><label>Video is appropiate for everyone</label></dd>
									                                        <dt id="dtradio" style="width:2em; text-align:left; color:white;"><input type="radio" id="rate1{$id}" name="rate1{$id}" value="1"/></dt>
									                                        <dd id="dtradio" style="margin-left:2em; color:white;"><label>Video is NOT appropiate for children</label></dd>
									                                        
									                                        <dl>
										                                        <dt style="width:2em; text-align:left;"><input type="checkbox" id="SEXOPT{$id}" name="SEXOPT{$id}" value="1"></dt> 
										                                        <dd style="margin-left:2em;">This video contains nudity and/or sexual situations</dd>
									                                        </dl>
									                                        
									                                        <dt style="width:2em; text-align:left;"></dt>
									                                        <dd style="margin-left:2em;">Inappropiately marking content for everyone when it is not may ensure your account is subject to deletion</dd>
									                                    </dl>
												                    </fieldset>
														 		</div>
														 	</div>
													 	</div>
													 </form>
												 </div>
											 	<div style="background:#131313; border-top:1px solid #363636; padding:5px; padding-top:7px; color:#fff; text-align:right;">
											 		<input type="submit" onclick='javascript:AddDetails("{$id}");' class="button7" value="Save Video Details" style="margin-right:1em;"/>
											 	</div>
											 </div>
										 </div>
									 </div>
								 </div>
						
							 </div>
						 </div>
						<div id="addFFRM"> </div>
						 
			  		 </div>
			  		</div>
		
					<h2 class="titlenomargin">Uploading a Video</h2> 
					<ul id="generalul" style="font-size:13px; margin-bottom:5px;">
					<li>Upload High Definition videos</li>
					<li>Upload up to 5 videos at a time</li>
					<li>Videos can be upto 2 GB in size each</li>
					<li>Video can be any length of time</li>
					</ul>
				
					<p style="font-size:13px;">Please respect all copyright laws for media.<br>This script is still in Beta so it may still have bugs in, please report any bugs</p>
				</div>
				<div id="vdUPRight">  
				</div>
			</div>  
		</div>
	</div>
</div>
{include file="footer.htm"}
</body>
</html>

I will now attempt to explain this code to you all, attempt. Firstly allow me to apologise for the crude coding and for certain redundant functions still existing, I have not yet cleaned the code up since as you know I still have a couple of bits to do.

All variables you see within {$} are template variables which are gained from a back-end PHP file. If you do not understand template variables there are articles on my blog that will show you this stuff. You can see I am using two iframes and controlling these iframes using a return Javascript function, UP. This UP function allows us to over come AJAX/JSON etc by displaying a page within an iframe and just "echo"ing back a Javascript response function (i.e. echo "UP.completeUpload({$id});").

JQuery is used at the top of this page to add styling to certain parts of the script. I should note that this script will not work off the mark. This is because it is set-up to work with my own individual server. If you are a regular follower of this site then this script will most likely work from copy and paste, however, if you are not then it won’t.

Ok now we need the initial back-end for this page:

<?php

require_once 'includes/header.php';

if($_SESSION['logged']){
	//makes new ID
  $randName = md5(rand() * time());

  //checks ID has not been used against the database
	$lookUPN = true;
	while ($lookUPN == true){
		$lookupSQL = "SELECT * FROM tblvideo where VideoFile = '$randName/'";
		$resultLookup = $user->db->getRow($lookupSQL);
								
		if(is_object($resultLookup)){
			$randName = md5(rand() * time());
		}else{
			$lookUPN = false;		
		}
	}

	$id = $randName;
  //return bytes of something
  function return_bytes($val) {
    $val = trim($val);
    $last = strtolower($val[strlen($val)-1]);
    switch($last) {
        // The 'G' modifier is available since PHP 5.1.0
        case 'g':
            $val *= 1024;
        case 'm':
            $val *= 1024;
        case 'k':
            $val *= 1024;
    }

    return $val;
  }

  //Basic checks to ensure things work (mainly used for debugging and such like)
  $templateini =  ini_get("uploadprogress.file.filename_template");
  $testid = "thisisjustatest";
  $template = sprintf($templateini,$testid);
  $templateerror = false;
  if ($template &amp;amp;amp;amp;&amp;amp;amp;amp; $template != $templateini &amp;amp;amp;amp;&amp;amp;amp;amp; @touch ($template) &amp;amp;amp;amp;&amp;amp;amp;amp; file_exists($template)) {
    //    print '('.$templateini.' is writable. The realpath is ' . str_replace($testid,"%s",realpath($template)) .')';  
        unlink($template);
  } else {
        $templateerror = true;   
  }

   if (function_exists("uploadprogress_get_info")) {  
       if ($templateerror) {
           $style= 'background-color: red;\">Urgh! Not a Problem! ';
           if ($template == $templateini) {
               $style = $style . " uploadprogress.file.filename_template ($templateini) doesn't have an %s in it for making unique temporary files. Please adjust.<br/>";
           } else {
               $style = $style . "$templateini is NOT writable. <br/>Please make sure the directory exists and is writable for the webserver. <br/>
               Or adjust the ini setting 'uploadprogress.file.filename_template' to a correct path.";
           }
       } else {
           $style = 'background-color: green;\">All Needed Checks have been completed you are clear for take off';
       }
   } else {
       $style = "background-color: red;\">The uploadprogress extension is not installed.";
   } 
   
   //assign template variables
   
   $smarty->assign('id', $id);
   $smarty->assign('uploadmax',return_bytes(ini_get('upload_max_filesize')));
   $smarty->assign('style', $style);
   $smarty->display('videoUploadMain.htm');
   
}else{
	header("Location: login.php");
}
?>

This just basically sets and makes the page and its contents.

Now that you have the initial front-end of the upload script we need to grab information about the upload progress. Fortunately this is easy as hell (this goes as info.php). This script, unlike the others, was pulled straight from the example of the PHP extension so it carries the authors copyright:

<?php
/*
  +----------------------------------------------------------------------+
  | Uploadprogress extension                                             |
  +----------------------------------------------------------------------+
  | Copyright (c) 2006-2008 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.php.net/license/3_01.txt.                                 |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | license@php.net so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Author: Christian Stocker (chregu@php.net)                           |
  +----------------------------------------------------------------------+
*/

require_once('includes/header.php');

if($_SESSION['logged']){

if (function_exists("uploadprogress_get_info")) {
    
    $info = uploadprogress_get_info($_GET['ID']);
} else {
    $info = false;
}

?>
<html>
<head>
<script type="text/javascript">
<?php


if ($info !== null) {
$print = 'parent.UP.updateInfo('.$info['bytes_uploaded'].','.$info['bytes_total'].','.$info['est_sec'].',"'.$_GET['ID'].'")';
    print 'parent.UP.updateInfo("'.$_GET['ID'].'",'.$info['bytes_uploaded'].','.$info['bytes_total'].','.$info['est_sec'].')';
} else {
    print 'parent.UP.updateInfo("'.$_GET['ID'].'")';
}
?>
</script>

</head>

<body>
<pre>
<?php
print "Date : " . date("c",time())."\n";
print "ID   : ". $_GET['ID'] ."\n";
print 'var_dump($info): '. "\n";
var_dump($info);
?>
</pre>
</body>
<?php 
}else{
	header('Location: login.php');
}
?>

Now you have the upload page and grabbing information about the upload progress. All you need now is the actual page that uploads. The information page uses uploadprogress_get_info to get the upload information for a specific $ID (or upload). All uploads are ID’d making it very easy to create multiple uploads.

Now you need the actual uploading page itself (server.php):

<?php
require_once 'includes/header.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta name="generator" content="HTML Tidy for Linux/x86 (vers 1 September 2005), see www.w3.org" />
<script type="text/javascript">
<?php
if($_SESSION['logged']){
	if(isset($_GET['id'])){
		$id = $_GET['id'];
		function handleError() {
				//echo  'parent.UP.stop(true, 1, "'.$id.'");';   
		}
		
		if (count($_FILES) > 0) {
			
						$uploadedby = $_SESSION['uid'];
						$fileName = $_FILES[$id]['name'];
						$tmpName = $_FILES[$id]['tmp_name'];
						$fileSize = $_FILES[$id]['size'];
						$fileType = $_FILES[$id]['type'];
						$name = substr($fileName, 0, strrpos($fileName, '.'));
					    $ext = substr(strrchr($fileName, "."), 1);
					    if($ext == "divx"){        
			                
							$randName = md5(rand() * time());
							//check for folder existing
							$lookUPN = true;
							while ($lookUPN == true){
								$lookupSQL = "SELECT * FROM tblvideo where VideoFile = '$randName/'";
								$resultLookup = $user->db->getRow($lookupSQL);
								
								if(is_object($resultLookup)){
									$randName = md5(rand() * time());
								}else{
									$lookUPN = false;		
								}
							}
							
							$rs = @mkdir( "upload/".$randName , 0777 );
							@handleError();
							
							if( $rs ){
								$randThing = $randName;
								$randName = $randName . "/";
								
								$filePath = "upload/" . $randName . $fileName;
							
							 
								if(@move_uploaded_file($_FILES[$id]['tmp_name'], $filePath)){
				
									if(!get_magic_quotes_gpc()){
										$fileName = addslashes($fileName);
										$filePath = addslashes($filePath);
									}
			
									chmod($filePath, 0777 );
									
									
									$comm = 'ffmpeg -itsoffset -00:01:15 -i "/home/sam/inetpub/htdocs/the_stage/'.$filePath.'" -vcodec png -vframes 1 -an -f rawvideo -s 800x600 "/home/sam/inetpub/htdocs/the_stage/upload/'.$randThing.'.png"';
									system($comm);	
											    
									$command = sprintf('ffmpeg -i "/home/sam/inetpub/htdocs/the_stage/'.$filePath.'" 2>&amp;amp;amp;1');
									exec($command,$output);
										    
							    	$v = array();
							    		    
							    	// Now match output with options
									$text = implode("\r",$output);
									$v['file_ffmpeg_info_says'] = $text;
									        
							        if (preg_match('!Duration: ([0-9:.]*)[, ]!', $text, $matches)){
							        	list ($v_hours,$v_minutes,$v_seconds) = explode (":",$matches[1]);
							        	$v['file_duration'] = $v_hours . ":" . $v_minutes . ":" . $v_seconds;
							    	}		
							    		    
							    	$length = $v['file_duration'];
					                        
					                $actualLength = explode(".",$length);
					                        
					                $length = $actualLength[0];
					                        
									$imageurl = "upload/$randThing.png"; 
									
									$uploadedby = $user->db->quote($uploadedby);
									$name = $user->db->quote($name);
									$length = $user->db->quote($length);
									$fileSize = $user->db->quote($fileSize);
									$filePath = $user->db->quote($filePath);
									$randName = $user->db->quote($randName);
									$imageurl = $user->db->quote($imageurl);	 
									
									$newID = md5(((((rand() * time())/2) + ((rand() * time())/2)) * rand())/(time()-rand()));
									$newIDQ = $user->db->quote($newID);
									
										    //insert all data into the table
									$query = "INSERT INTO tblvideo (UserID, VideoTitle, VideoLength, VideoSize, VideoUrl, VideoFile, VideoThumbUrl, upID ) ".
									"VALUES ($uploadedby, $name, $length, $fileSize, $filePath, $randName, $imageurl, $newIDQ)";
										    
									$user->db->query($query);
									
									echo  'parent.UP.stop(true, 0, "'.$id.'", "'.$newID.'");';
				
								}else{
									echo  'parent.UP.stop(true, 1, "'.$id.'");';
								}
							}else{
								echo  'parent.UP.stop(true, 1, "'.$id.'");';
							}
						}else{
							echo  'parent.UP.stop(true, 2, "'.$id.'");';
						}
		  
		} else {
		  echo  'parent.UP.stop(true, 0, "'.$id.'");';
		}
	}else{
		header("Location: VideoUpload.php");
	}
}else{
	header("Location: login.php");
}
?>
</script>

<title></title>
</head>

<body>
  File uploaded:
  <pre>
<?php
var_dump($_FILES);
?>

</pre>
</body>
</html>

This page is specifically designed to upload a video however it is easy to change. If you are looking to do this kind of script exactly then I will explain:

This script uses checks to ensure that the video file is one I want. I then uses ffmpeg to not only take a thumbnail of the video but also get information such as the videos length (hrs:mins:secs) and then writes all this information to the database.

Now that we have the actual page the uploads the file to server we need to finish the touch up by adding the uploadIDMake.php (for new uploads) and jxSV_VID.php (for adding video file details). So lets begin with uploadIDMake.php:

<?php
	require_once('includes/header.php');

	$randName = md5(rand() * time());
	
	$lookUPN = true;
	while ($lookUPN == true){
		$lookupSQL = "SELECT * FROM tblvideo where VideoFile = '$randName/'";
		$resultLookup = $user->db->getRow($lookupSQL);
								
		if(is_object($resultLookup)){
			$randName = md5(rand() * time());
		}else{
			$lookUPN = false;		
		}
	}
  
	echo $randName;
  
?>

This script is pretty basic and it just echos the ID back to the JQuery AJAX in the Javascript of the main upload page.

jxSV_VID.php:

<?php
require_once 'includes/header.php';

header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");

if($_SESSION['logged']){
	$req_fields = array("title"=>"Video Title", "description"=>"Video Description", "tags"=>"Video Tags");
	
	if(check_empty_fields()){
		if(($_POST['rate']=="") &amp;amp;amp;&amp;amp;amp; ($_POST['rate1']=="")){	
			$returnArray = array("ErrorCode"=>1, "Message"=>"You must confirm whether the video is suitable for children or not manually.");
			echo json_encode($returnArray);
			exit;			
		}
		
			if((isset($_POST['upID'])) &amp;amp;amp;&amp;amp;amp; (isset($_POST['id']))){
				
				$upID = $user->db->quote($_POST['upID']);
				
				$sql = "SELECT * FROM tblvideo WHERE upID = $upID";
				
				$result = $user->db->getRow($sql);
				
				$title = $user->db->quote($_POST['title']);
				$description = $user->db->quote($_POST['description']);
				$videotags = $user->db->quote($_POST['tags']);
				$videorating = $user->db->quote($_POST['rate']);
				
				if($_POST['rate1']=="1"){
					$videorating = $user->db->quote($_POST['rate1']);
				}
				
				$videoNudity = $user->db->quote($_POST['Nud']);
				
				$sql = "UPDATE tblvideo SET VideoTitle=$title, VideoDescription=$description, VideoTags=$videotags, AudienceRating=$videorating, Nudity=$videoNudity WHERE upID = $upID";
				
				$user->db->query($sql);
				
				$returnArray = array("ErrorCode"=>0, "Message"=>"The file details have been appended. For more detailed editing please visit your videos through your profile.");
				echo json_encode($returnArray);
				exit;
				
			}else{
				$returnArray = array("ErrorCode"=>1, "Message"=>"The script encountered an unokwn error. This has been reported and will be dealt with as soon as possible.");
				echo json_encode($returnArray);
				exit;
			}

	}else{
		$returnArray = array("ErrorCode"=>1, "Message"=>$msg);
		echo json_encode($returnArray);
		exit;		
	}
}else{
	header("Location: login.php");
}

?>

This script is not all that complex either. It just grabs the video details entered and puts them into database ensuring the user is logged in.

And thats it, you have the script check out the gallery for the pics of this thing in action.

As a word of warning I would suggest the use of either an infinite session (bad idea) or a cookie that expires on browser close in order to make this script work perfectly fine or you could just use no session (noob style).

Anyway if you have any questions feel free to post a comment, stay safe.

MySQL, PHP

PHP Database Sessions

This tutorial will teach the reader how to create database sessions for their PHP and MySQL website. By the end of the tutorial the user should be able to confidently add PHP database sessions to their website and understand how using these type of sessions is better than normal text file based sessions.

Tutorial Note:

This tutorial was completed in Ubuntu 9.04 and so reflects the processes required to complete this task within Linux. If some one requests a Windows version I will be happy to provide one.

Introduction

To begin this (as a noob to PHP) I searched around on Google. I read up one site in particular [Dev Shed – Storing PHP Sessions in a Database]. Dev Shed provides and very neat tutorial on database sessions. I will quickly provide a 5 point overview of the immediate up shots you will like and notice:

  • Security – It is harder to hack the web server and then the database than it is to just hack the web server
  • Speed – It is surprisingly fast, almost as fast as normal sessions. In fact I am not sure if you will tell the difference
  • Ease of learning – Database session handling is very easy to learn making it very easy to pick up and manipulate to your own advantage
  • Data Storage – The standardised data storage within the table allows the custom management and data manipulation of the session information allowing for the administrator to choose when sessions expire. This point also allows for you to judge which users are on line etc.
  • Ease of use – You don’t have to make up some special code to use it. Just include the handler class and use PHP sessions as standard.

The Dev Shed site will cover all the intricates of the positive sides to using PHP database sessions. What I am going to do now I move straight onto the coding, trying to make this tutorial as simple and concise to read as possible.

As a further note before I begin I use a strange database layout for some noobs. This database format is called PEAR:DB. This has been explained within other areas of my blog if you wish to read up on that, there are also up shots to using PEAR:DB as well as down sides. The first step, as always, is to produce the database table:


CREATE TABLE IF NOT EXISTS `sessions` (
  `session_id` varchar(250) NOT NULL,
  `UserID` int(11) default NULL,
  `session_data` longtext NOT NULL,
  `expires` varchar(250) NOT NULL,
  PRIMARY KEY  (`session_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='session database storage';

This is not the best formatted table but it will give you a good idea of what is needed. This is formatted as SQL create table statement (created from phpMyAdmin) so it can be posted straight into your database client for usage. Now most of the fields are self explanatory but I will go through each one:

  • session_id – This houses the session id of the current session. This is the PHP session id not your own.
  • UserID – This is a field created by yours truly. This allows me to store the user identification number and use it later to understand which user that session belongs to (detect on line users).
  • session_data – This field just contains all the session data for that session (all the random stuff you put into $_SESSION)
  • expires – This field is compared for garbage collection to understand whether a session has expired or not and if it has the script will delete the session detailing that the user is off-line.

Now that the table for database sessions has been explained below you will see the main file that houses the database session management. After explaining this I will show you how to implement these sessions into your site. Remember this class is a handler, it is not the session itself it just reads and saves the session and deletes it if necessary.

<?php

//=================================================
// Filename: Session.php
//=================================================

//Adds my database. I have explained earlier in the blog how naming your connection dbconnection will get you hacked.
require_once('dbconnection.php');

//Handler class begins
class SessionManager {

   var $life_time;
   var $db = null;
	
   //class function. This houses the actual save handler (session_set_save_handler) which then houses an array telling the sessions how to save (what methods to use)
   function SessionManager(&amp;amp;amp;amp;amp;amp;amp;amp;amp;$db) {

      // Read the maxlifetime setting from PHP
      $this->life_time = get_cfg_var("session.gc_maxlifetime");

      // Register this object as the session handler
      session_set_save_handler( 
        array( &amp;amp;amp;amp;amp;amp;amp;amp;amp;$this, "open" ), 
        array( &amp;amp;amp;amp;amp;amp;amp;amp;amp;$this, "close" ),
        array( &amp;amp;amp;amp;amp;amp;amp;amp;amp;$this, "read" ),
        array( &amp;amp;amp;amp;amp;amp;amp;amp;amp;$this, "write"),
        array( &amp;amp;amp;amp;amp;amp;amp;amp;amp;$this, "destroy"),
        array( &amp;amp;amp;amp;amp;amp;amp;amp;amp;$this, "gc" )
      );
      
      //read in the db object my api uses
      $this->db = $db;

	}
	
	//this opens the session for reading or writing
   function open( $save_path, $session_name ) {

      global $sess_save_path;

      $sess_save_path = $save_path;

      // Don't need to do anything. Just return TRUE.
      return true;

   }

   function close() {

      return true;

   }

   //this reads all you session data from the table into $_SESSION whilst checking the session has not expires. Also makes a new session if needed.
   function read( $id ) {

      // Set empty result
      $data = '';

      // Fetch session data from the selected database
      $time = time();

      $newid = $this->db->quote($id);
      $sql = "SELECT session_data FROM sessions WHERE
			session_id = $newid AND expires > $time";

      //add PEAR here
		$result = $this->db->getRow($sql);
		
		if ( is_object($result) ) {
        	$data = $result->session_data;
      	}

      return $data;

   }

   //writes all your $_SESSION data to database
   function write( $id, $data ) {

      // Build query                
      $time = time() + $this->life_time;

      $newid = $this->db->quote($id);
      $newdata = $this->db->quote($data);

      $sql = "REPLACE sessions
		(session_id, session_data, expires) VALUES($newid, $newdata, $time)";
		
	  if(isset($_SESSION['uid'])){
	  		$userIden = $this->db->quote($_SESSION['uid']);
	  		$sql = "REPLACE sessions
		(session_id, UserID, session_data,expires) VALUES($newid, $userIden, $newdata, $time)";
	  }
     	
	  $this->db->query($sql);

      return TRUE;

   }

   //Now this is complex because normally you can just use session_destroy or whatever but due to a bug in PHP which is meant to be fixed you had to logout the user manually. Best to look at my user class for help on this.
   function destroy( $id ) {

      // Build query
      $newid = $this->db->quote($id);
      $sql = "DELETE FROM `sessions` WHERE `session_id` = '$newid'";

      //and again
      $this->db->query($sql);

      return TRUE;

   }

   //This will delete any old sessions which should no longer exist. For this to work properly you must set carbage collection within your php.ini to 100% of the time.
   //this is done within the session.gc_probability value in your php.ini, set it to 100 for 100%
   function gc() {
      // Garbage Collection

      // Build DELETE query.  Delete all records who have passed the expiration time
      $sql = 'DELETE FROM sessions WHERE expires < UNIX_TIMESTAMP();';
      
      //and finally here
      $this->db->query($sql);

      // Always return TRUE
      return true;

   }

}

?>

Now if you read the code above you will see I explained not only the file name but also the processes for the session handler. I should explain my concerned comment at “function destroy($id)”. The reason I said you cannot use conventional session_destroy to logout a user when using database sessions is because of a bug that existed within one of the PHP versions (and I have never decided to check if this bug has been fixed since my manual version works perfect). This bug would mean that when using database sessions if you were to log the user out with “session_destroy();” the session could not be recreated…meaning that the user would have to close their browser before they could login again (or just redirect away and back again). I am not sure which version this bug has been fixed on so I just stick with my manual logout.

The session save handler has been explained along with its table. It is time to finish this tutorial with the usage and an example. I find a lot of tutorials are missing detailed explanations about these sort of things and expect you to almost know from thin air how to use this stuff (luckily I am not one of those people).

<?php

        //include the session.php database session handler from above
	require_once('session.php');

        //Set the ini values so that the script works properly
        ini_set('session.gc_probability', 100);
        ini_set('session.gc_divisor', 100);

        //me passing my database object to it. This can also be done (and should be) with normal MySQL
	$sess = new SessionManager($db);

        //start session as you would normally
	session_start();

        //Add and ech as you would normally. The session.php is only a handler for the storage of data it does not over ride the default session.
        $_SESSION['username'] = "hello, I am the User. This session is using database sessions.";

        echo $_SESSION['username'];
?>

And there you have it database sessions and as stated in the example script above there is no need to change your session type! Enjoy 🙂

Linux, MySQL, PHP

Beginners Guide to LAMP server Setup

LAMP – Linux Apache MySQL PHP server which can often have parts of the removed for other languages such as Perl or Prostge SQL but most commonly known as Linux Apache MySQL and PHP.

Lamp is the first letter combination of the build up of a typical Linux based server. In this tutorial we will learn how to set-up one of these server and get it working properly.

Tutorial Warning

This tutorial does not follow through Apache or PHP security protocols and although it might be fine on a normal server that would host, say, a video site I would not recommend this guide for commercial set-ups which require absolute security.

As another note this is under Ubuntu 9.04 and most Ubuntu versions are the same interface-wise, however, the instructions may differ from different distros and even Operating Systems.

First things first you must install Apache, so terminal:

sudo apt-get install apache

After that follow this tutorial here (Okay, so I could have actually wrote the tutorial but I like this tutorial so I thought I would link this instead =]).

Okay, so you have just followed the tutorial I linked and came back here, good. You are now ready to begin everything else since setting up Apache is only a fraction of the process. We will immediately move onto PHP and MySQL installation.

Both of these can be download from the universe just:

sudo apt-get install mysql-server
sudo apt-get install php5

At some point throughout the PHP installation you will be asked to state which server PHP is being installed to, just say Apache 2.x (or 2.2 as it’s sometimes stated). There is also a stoppage within the MySQL set-up when the installer asks for a password, if you try enter a slightly secure yet memorable password then it should be okay.

Now just open phpmyadmin (localhost/phpmyadmin) and enter root as the user and your password you entered on the MySQL set-up. You should now see the database server and the databases upon it. If you don’t just drop a comment and I’ll get back to you.

Wooo, you have the actual server set-up, not that hard was it. Now we need to set-up the environment itself with extensions and such like, all of these are optional, however useful.

PHP PEAR:DB

What is PEAR:DB? DB is a database abstract layer. Still a bit confused? Okay, basically imagine your PHP code with the SQL in it, underneath that is the SQL server right? Wrong, DB acts as a layer between those taking in standardised functions and spitting them out as database queries only difference is that DB can spit these queries out for almost every main stream server out there.

This means in layman’s terms that you would never have to rewrite SQL code so long as you live and not only that you get some pretty cool functions that make your life so much easier. I love easy, I never have time for hard.

To install DB simply type into terminal:

sudo apt-get install php-db

Once this is completed you have the extension. Now you need an example along with a little bit of a tutorial on how to use this extension.

I have decided to explain the rest of this section in a new post. It is a bit long to post here.

PHP mcrypt

PHP mcrypt controls encrytion within any PHP script. To install this extension simply type:

sudo apt-get install php5-mcrypt

Then for its usage:

	function linEncrypt($pass) {
		$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
		$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); //Creating the vector
		$cryptedpass = mcrypt_encrypt (MCRYPT_RIJNDAEL_256, $this->mykey, $pass, MCRYPT_MODE_ECB, $iv);
		return $cryptedpass;
	}

Most of the things here are just default in built commands, except for $mykey. This is a key that the encrypt-or uses to encrypt with (i.e. “S3M_LL4N”). Except for the key everything else is pretty much set default for my basic RIJNDAEL encryption.

Smarty

This has already been written here

uploadprogress

An essential user friendly function for any website which requires an upload page. This extension will effectively allow the developer to track the progress (all the details, even remaining seconds) of an upload.

For an accurate description on it’s usage and such like check out this page. In this section I will hopefully go over the basics of installing the upload progress function. There is an advanced tutorial for readers who are looking for something more intensive to do with this extension.

Okay, first we need to download and install the package. If you enter:

sudo pecl install uploadprogress

I found that for some reason sometimes you need sudo and sometimes you don’t (I am unsure as to the reason this is). You will find that when you first install there will be missing packages and the installer will fail. So to fix this problem we simply:

sudo apt-get install php5-dev

If you are running a server version you may also be missing make package but that one is simple, the phpize command is not instantly understandable (due to it actually being in package: php5-dev). After you have run the uploadprogress setup correctly without faults you will see the uploadprogress extension on your phpinfo(); page and this will signify you are ready to use the extension.

Ffmpeg

Ffmpeg is perhaps one of the greatest thumb nailing extensions for PHP in existence. it is not the only one but it’s the only one that has done exactly what I want it to have done without exploding.

Now to install Ffmpeg simply type:

sudo apt-get install ffmpeg

And to test do this:

ffmpeg

The result should be:

FFmpeg version 0.5-svn17737+3:0.svn20090303-1ubuntu6, Copyright (c) 2000-2009 Fabrice Bellard, et al.
  configuration: --enable-gpl --enable-postproc --enable-swscale --enable-x11grab --extra-version=svn17737+3:0.svn20090303-1ubuntu6 --prefix=/usr --enable-avfilter --enable-avfilter-lavf --enable-libgsm --enable-libschroedinger --enable-libspeex --enable-libtheora --enable-libvorbis --enable-pthreads --disable-stripping --disable-vhook --enable-libdc1394 --disable-armv5te --disable-armv6 --disable-armv6t2 --disable-armvfp --disable-neon --disable-altivec --disable-vis --enable-shared --disable-static
  libavutil     49.15. 0 / 49.15. 0
  libavcodec    52.20. 0 / 52.20. 0
  libavformat   52.31. 0 / 52.31. 0
  libavdevice   52. 1. 0 / 52. 1. 0
  libavfilter    0. 4. 0 /  0. 4. 0
  libswscale     0. 7. 1 /  0. 7. 1
  libpostproc   51. 2. 0 / 51. 2. 0
  built on Apr 10 2009 23:18:41, gcc: 4.3.3
At least one output file must be specified

Ok so you have the extension now you need an example of its usage:

$comm = 'ffmpeg -itsoffset -00:01:15 -i "/home/sam/inetpub/htdocs/the_stage/'.$filePath.'" -vcodec png -vframes 1 -an -f rawvideo -s 800x600 "/home/sam/inetpub/htdocs/the_stage/upload/'.$randThing.'.png"';
									system($comm);

$comm string forms a system command, a terminal command and system() executes that command. You can always use a different command which will allow you to return an object like an array:

									$command = sprintf('ffmpeg -i "/home/sam/inetpub/htdocs/the_stage/'.$filePath.'" 2>&amp;amp;amp;1');
									exec($command,$output);

But let’s get back to the example. You should notice that in the example I am actually grabbing a image of the film (frame) at 1 min and 15 seconds and saving it to a png file. You always start every command with ffmpeg and then build on the commands from there. Now the actual example is quite easy and straight forward to explain. I set a parameter of time (00:01:15) and a video file (-i “blah/blah/$file.divx”), set certain codec and image size (800×600) and then state the path and name of the file (blah/blah/blah/$file.png).

Ffmpeg is in fact very simple once you understand the basic format of commands. Most of the complicated looking parameters are just instruction saying what image format I want and stating default codec to open the video with. Anyway now you have had a small taste of Ffmpeg.