/ PHP

Tree view based file explorer for the web using jQuery.

This post assumes that the reader has a basic understanding of PHP, jQuery and jQuery $.post

Recently I had to implement a windows-esque tree view based file explorer for one of my projects at work. I ended up using this wonderful jQuery plugin to help me. In this blog I'll be going through some basic code to get it working using PHP on the server side.

To whet your appetite, here's what we'll be achieving in this blog post - A complete tree view based file explorer that feeds from a folder on the server's file system.

File tree view

So let's get started!

To be able to render a proper tree using the aciTree plugin, we'll need to send JSON from server in the following format -

[
  {
    "id": "folder_1",
    "label": "ThisisFolder1",
    "inode": true,
    "open": false,
    "icon": "folder",
    "branch": [
      {
        "id": "sub-item_x",
        "label": "ThisisFileX",
        "inode": false,
        "icon": "file"
      },
      {
        "...": "..."
      },
      {
        "...": "..."
      }
    ]
  },
  {
    "id": "file_1",
    "label": "ThisisFile1",
    "inode": false,
    "icon": "file"
  },
  {
    "...": "..."
  },
  {
    "...": "..."
  }
]

Here's the list of things that we'll be doing -

  1. Server Side Code
  2. HTML
  3. JavaScript/jQuery code to communicate with the server.
  4. Basic event handling for the aciTree plugin.

1. Server side code

Let's move onto the PHP code. Our job is to read the files from the specified folder and return a JSON structure that resembles what's shown above. If you look closely you'll observe that the JSON above is basically an array of objects of with properties as - id, label, inode, icon, open

The following class is exactly similar to that of the class received via JSON. We'll be returning an array of NodeList objects from the server.

/**
 * Represents each node in the aci tree jquery plugin
 * 
 * @author abijeet
 */
class NodeList {
	public $id, $label, $inode, $open, $icon, $branch;
	private $openIfBranch;

	/**
	 * Constructor for NodeList
	 *
	 * @param string $label
	 *        	Label of the node
	 * @param boolean $open
	 *        	If this is a branch, should it be open
	 * @param string $icon
	 *        	Icon for the node
	 */
	public function __construct($label, $open, $id, $icon = '') {
		if ($id) {
			$this->id = $id;
		}
		$this->label = basename($label);
		$this->open = false;
		$this->openIfBranch = $open;
		$this->icon = $icon;
		$this->inode = false;
	}

	public function setBranch($branch) {
		$this->branch = $branch;
		$cntBranch = count($branch);
		if ($cntBranch > 0) {
			$this->inode = true;
			$this->label .= ' [' . $cntBranch . ']';
		}
		$this->open = $this->openIfBranch;
	}
}

Now to read the list of files and folders from a location on the server -

/**
 * Function that given a path, returns an array of nodeList
 * This can then be converted to a json format.
 * 
 * @param $path Path
 *        	of the folder from which to retrieve
 * @return multitype:NodeList Returns the json tree
 */
function jsonForResTree($path) {
	$dirArray = getAllFilesAndFolders($path);
	$nodeArray = array ();
	$node = '';
	$cnt = count($dirArray);
	for($i = 0; $i < $cnt; ++ $i) {
		$node = new NodeList($dirArray[$i], false);
		if (is_dir($dirArray[$i])) {
			// Recursion - It's a folder, get the array of nodeList for it.
			$nodeList = jsonForResTree($dirArray[$i]);
			// Add it as branch
			$node->setBranch($nodeList);
		}
		$nodeArray[] = $node;
	}
	return $nodeArray;
}

/**
 * Gets all files and folders from the specified path
 * 
 * @param unknown $path
 *        	Path of the folder from where files and folders are to be retrieved
 * @return multitype:
 */
function getAllFilesAndFolders($path) {
	if (! is_dir($path)) {
		return array ();
	}
	$path = $path . DIRECTORY_SEPARATOR . '*';
	return glob($path, GLOB_NOSORT);
}

Some code to handle the request that we will be making from the client side and then echoing the output in JSON format -

if (! empty($_POST['method'])) {
	// Do some check before handling the POST data.
	$methodToCall = $_POST['method'];
	ob_clean();
	// Call the method requested from the client side.
	$result = call_user_func($methodToCall);
	die(json_encode($result));
}

/**
 * Function that is call by the JQUERY post.
 * 
 * @return multitype:NodeList
 */
function getJsonTree() {
	// Folder Path from where we are going to show the tree view.
	$pathToGetAciTree = './cakephp';
	$jsonTree = jsonForResTree($pathToGetAciTree);
	return $jsonTree;
}

Okay so we have the PHP code in place. Next order of things -

  1. Server Side Code
  2. HTML
  3. JavaScript/jQuery code to communicate with the server.
  4. Basic event handling for the aciTree plugin.

2. HTML

The following HTML goes inside the body tag-

<!-- Scripts and CSS to be loaded. This will be avaliable when you download aciTree plugin --> 
<link href="css/aciTree.css" rel="stylesheet" type="text/css" /> 
<link href="css/demo.css" rel="stylesheet" type="text/css" /> 

<!-- Loading jQuery --> 
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> 
<!-- Loading the aciTree plugin --> 
<script type="text/javascript" src="js/jquery.aciPlugin.min.js"></script> 
<!-- Loading the aciTree core--> 
<script type="text/javascript" src="js/jquery.aciTree.core.js"></script> 
<!-- Loading the aciTree selectable plugin --> 
<script type="text/javascript" src="js/jquery.aciTree.selectable.js"></script> <!-- The div that will contain the ACI Tree --> 
<div id="fsTree"></div> <button id="btnGetTreeView">Get Tree View</button> <button id="btnRefreshTreeView">Refresh Tree View</button> 
<div id="currStatus"></div>

Next, the JavaScript/jQuery to get the aciTree plugin working. This should be put inside the document's ready handler -

3. jQuery code to communicate with the server

$currStatus = $('#currStatus');
// Makes the ajax call and fetches the json for the resource tree.
$('#btnGetTreeView').click(function() {
  $("#fsTree").aciTree({
    ajax: {
      type: 'POST',
      url: 'index.php',
      data: {
        // Notice that this is the method name that
        // we wish to call on the server side.
        'method': 'getJsonTree'
      }
    }
  });
});

// Refreshing the tree view - Destroy and recreate
$('#btnRefreshTreeView').click(function() {
  var api = $('#fsTree').aciTree('api');
  api.unload(null, {
    success: function() {
      this.ajaxLoad(null);
      // Triggering the click handler of the Get Tree View button.
      // This will make the ajax call again and bind the tree...
      $('#btnGetTreeView').trigger('click');
      $currStatus.text('');
    }
  });
});

4. Event handling for the aciTree plugin

And finally a simple event handler for the aciTree that is triggered whenever a node in the tree is selected -

// ACI Tree - event handler.
$('#fsTree').on('acitree', function(event, aciApi, item, eventName, opt) {
  switch (eventName) {
    case 'focused':
    case 'selected':
      // Fired when an item in the tree is selected.
      if (item) {
        $currStatus.text('Selected - ' + item.context.innerText);
      }
  }
});

You can find the whole code here and a working copy of the project here.