Oct 21 2009

Sorting a Zend Navigation container by Page dates

I’ll introduce this topic with a warning – This article doesn’t show you how to completely reorder your entire Navigation, it does however show you how you could populate a new container from an existing one and reorder it.

Zend Navigation currently doesn’t allow you to nominate a default Navigation Container class. So while we could extend the Zend_Navigation_Container creating our own customised one, you’d almost have to duplicate the code for the Zend Page and Zend Container classes to do a thorough job, as they both call each other – so won’t know to use your new one.

My goal was to achieve:

  • a container of all pages in my navigation configuration (ignoring hierarchy)
  • exclude pages that had sub pages (so I only had the leaves on the branch so to speak)
  • order these pages by a date set for each page in the nav configuration

Step 1 – Add dates to Navigation configuration

You can add new properties to Zend Navigation Pages without needing to do anything. See my “dateadded” in the example below on some pages.

                    <security>
                        <label>Security</label>
                        <module>computers</module>
                        <controller>security</controller>
                        <action>index</action>
                        <pages>
		                    <password>
		                        <label>Password generator</label>
		                        <module>computers</module>
		                        <controller>security</controller>
		                        <action>password-generator</action>
		                        <dateadded>01/08/2009</dateadded>
		                    </password>
                            <encrypt>
		                        <label>Hash generator</label>
		                        <title>Hash generator (string encryption)</title>
		                        <module>computers</module>
		                        <controller>security</controller>
		                        <action>encrypt</action>
		                        <dateadded>25/08/2009</dateadded>
		                    </encrypt>
		                </pages>
                    </security>

Step 2 – Create a custom container type

By creating a custom container extending Zend_Navigation_Container we can add our own sorting function. The trick of this is converting the dates to the linux time stamp i.e. number of seconds since 1 January 1970. This is also a logical place to but build a function to extract lowest level pages from another container. This one class services all three of my requirements.

class Utilitiesman_Navigation_Container_Utilities extends Zend_Navigation_Container
{
	public function addLowestPages($page) {
		$iterator = new RecursiveIteratorIterator($page,RecursiveIteratorIterator::SELF_FIRST);
		foreach ($iterator as $page) {
			// don't add page if it has children
			if(!$page->hasPages()) {
				$lowestPages[] = $page;
			}
		}
		$this->addPages($lowestPages);
	}
 
    public function sortByDate($property = 'dateadded')
    {
        $newIndex = array();
        $index = 0;
 
        foreach ($this->_pages as $hash => $page) {
            $pageDate = $page->get($property);
            if(Zend_Date::isDate($pageDate)) {
                $date = new Zend_Date($pageDate);
                $timestamp = $date->getTimestamp();
                $newIndex[$hash] = $timestamp;                
            } else {
                // there wasn't a valid date for this page, use an incremental number start at 0
                $newIndex[$hash] = $index;
                $index++;
            }
        }
 
        //sort the array using those timestamp versions of the dates
        arsort($newIndex);
        $this->_index = $newIndex;
        $this->_dirtyIndex = false; // flag index as clean - prevent default sort
    }
 
}

Step 3 – Call it and render the result

This is all done within the context of a view script (the layout in my case) I should mention:

$containerByDate = new Utilitiesman_Navigation_Container_Utilities();
$rootPage = current($this->navigation()->getContainer()->getPages());
$containerByDate->addLowestPages($rootPage);
$containerByDate->sortByDate();
echo $this->navigation()->menu()->render($containerByDate);

To see a cool example of this in action check out the calculator section of my utilities man website, or any section mind you.

utilities man - calculators

My implementation is a little more complex than this though, I’m using a partial view script for the menu and I’m also only displaying pages under the section your on.

Share and Enjoy:
  • DZone
  • Digg
  • del.icio.us
  • Technorati

Aug 16 2009

Zend_Navigation Tricks: True tab navigation with sub menus – Part 3

Part 3: Implementing jsTree which uses jQuery (optional javascript expand/collapse)

This is a completely optional step in this series and really has nothing to do with Zend Framework because by this based on the client side we have all the HTML we need.

What we’ll be doing is setting up jsTree a jQuery tree component that can do much more than we’ll be using it for, it’s probably overkill but the best I came across when I was looking on this occasion.

You need jQuery:

$this->headScript()->appendFile($this->baseUrl . '/scripts/jquery-1.3.2.min.js', 'text/javascript');

Setup the jsTree javascript and css files if $nav2Container is set (see Part 2)

// append nav2 menu components if $activeContainer is set for the menu
if(isset($nav2Container)) {
    $this->headScript()->appendFile($this->baseUrl . '/scripts/jstree/css.js', 'text/javascript');
    $this->headScript()->appendFile($this->baseUrl . '/scripts/jstree/tree_component.min.js', 'text/javascript');
    $this->headLink()->appendStylesheet($this->baseUrl . '/scripts/jstree/tree_component.css', 'screen');
}

Render the Style and Script files we setup earlier:

        echo $this->headStyle() . "\n";
        echo $this->headScript() . "\n";

Some of this code below is a repeat from previous parts but its better to see this all in context. I’ve added a couple bit of JavaScript to achieve the following:

< ?php
        	// render nav2 container and menu if not on the homepage
        	if(isset($nav2Container)) {
        ?>
        <div id="nav2Container">
        	< ?php 
        		$this->navigation()->menu()->setMinDepth(null)->setMaxDepth(null);
        		echo $this->navigation()->menu()->renderMenu($nav2Container,array('ulClass' => 'nav2'));
        	?>
        	<script type="text/javascript">
        	$(function () {
        			// setup each active <li> with an id
        			var $active_li = $(".nav2 li.active");
        			var $active_li_id = new Array();
            		for( var i = 0, n = $active_li.length;  i < n;  ++i ) {
                		var $element = $active_li[i];
                		$active_li_id[i] = "nav2_active_" + i;
                		$($element).attr("id", $active_li_id[i]);
            		};
        			// configure and initialise 
        			$(".nav2").tree({
              			data  : {type : "predefined"},
            	  		opened : $active_li_id,
        	      		path : "<?php echo $this->baseUrl . '/scripts/jstree/'; ?>",
              			ui : {theme_name : "toolbox"},
        	      		rules : {   
                			metadata : "mdata",
                			use_inline : true
              			}
        			});
        		});
        	</li></script>
        </div>
        < ?php
        	}
        ?>

I’m using version 0.9.8 of jsTree but I’m looking forward to version 0.9.9 which will apparently alleviate some of my design/performance concerns, as the script loads this default theme and then the custom theme, rather than just the custom theme.

If I’ve missed anything and this doesn’t work, please let me know!

Share and Enjoy:
  • DZone
  • Digg
  • del.icio.us
  • Technorati

Aug 16 2009

Zend_Navigation Tricks: True tab navigation with sub menus – Part 2

Part 2: Rendering the sub menus (relevant to the active tab).

This is where we employ another trick because the Zend_Navigation menu view helper does provide an option to render the active menu, but it is meant literally not the active branch of the menu like we’re trying to achieve.

In the layout we need to get the root level pages then we assume the 1st one is Home and root of all other navigation items. After this we loop through the pages below Home to find the active one and set this as the $nav2Container which we’ll use next.

layout.phtml

// find the active page/container under the rootPage (Home) for nav2 menu
$rootPage = current($this->navigation()->getContainer()->getPages());
$pages = $rootPage->getPages();
foreach($pages as $page) { 
	if($page->isActive(true)) { 
		$nav2Container = $page; 
	}
}

Now that we know what the root of this sub menu should be we can use the helpers to render it (this is also in your layout file).

if(isset($nav2Container)) {
    echo $this->navigation()->menu()->renderMenu($nav2Container,array('ulClass' => 'nav2'));
};
Share and Enjoy:
  • DZone
  • Digg
  • del.icio.us
  • Technorati

Aug 16 2009

Zend_Navigation Tricks: True tab navigation with sub menus – Part 1

It is some what tricky with the Zend_Navigation menu helpers to set up your typical tab-based navigation with sub pages relevant to the tab your on, as seen below. But it’s not far difficult so don’t be put off, hopefully the view helpers will improve over time.

tab-menu-example

In the mean time this is a 3 part series in achieving the navigation I set up in utilitiesman.com as seen above.

Part 1: Setting up the navigation, and rendering the tabs.

Set up your navigation configuration, I’ve done this in xml.

< ?xml version="1.0" encoding="UTF-8"?>
<configdata>
    <nav>
        <label>Home</label>
        <module>default</module>
	<controller>index</controller>
	<action>index</action>
        <pages>
            <computers>
                <label>Computers</label>
                <module>computers</module>
                <controller>index</controller>
                <action>index</action>
                <pages>
                    <networking>
                        <label>Networking</label>
                        <module>computers</module>
                        <controller>networking</controller>
                        <action>index</action>
                        <pages>
		                    <ipaddress>
		                        <label>IP Address</label>
		                        <module>computers</module>
		                        <controller>networking</controller>
		                        <action>ipaddress</action>
		                    </ipaddress>
		                    <ping>
		                        <label>Ping</label>
		                        [... code removed for example ...]
		                    </ping>
     	                            [... code removed for example ...]
		                </pages>
                    </networking>
                    <security>
                        [... code removed for example ...]
                    </security>
                </pages>
            </computers>
            <measures>
                [... code removed for example ...]
            </measures>
        </pages>
    </nav>
</configdata>

In your bootstrap file you’ll need setup the navigation and load your navigation config:

$navigationConfig = new Zend_Config_Xml('./application/Config/navigation.xml');
$navigation = new Zend_Navigation($navigationConfig);

Assuming you’re making use of the Zend_Layout this would go in your template

$this->navigation()->menu()->setPartial(array('nav1.phtml','default'));
echo $this->navigation()->menu()->render();

This is my partial menu template which is where some minor trickery is performed to see the Home page and first level pages in a single <ul> for easy application of CSS to make the <li>’s look like tabs. This is the “nav1.phtml” referred to in the layout.

    echo '<ul id="nav1">';
    // loop root level (only has Home, but later may have an Admin root page?)
    foreach ($this->container as $page) {
        // check if it is active (not recursive)
        $isActive = $page->isActive(false);
        $liClass = $isActive ? ' class="active"' : '';
        echo '<li ' . $liClass . '>' . $this->menu()->htmlify($page) . '</li>', PHP_EOL;
        // loop next level
        foreach ($page as $page) {
            // check if it is active (recursive)
            $isActive = $page->isActive(true);
            $liClass = $isActive ? ' class="active"' : '';
            echo '<li ' . $liClass . '>' . $this->menu()->htmlify($page) . '</li>', PHP_EOL;
        }
    }
    echo '</ul>';

What the partial template above renders is something like this, neatly all in a single hieratical level..

<ul id="nav1">
    <li><a ...>Home</a></li>
    <li class="active"><a ...>Computers</a></li>
    <li><a ...>Measures</a></li>
</ul>
Share and Enjoy:
  • DZone
  • Digg
  • del.icio.us
  • Technorati

Aug 16 2009

Zend_Navigation Trick: Sub pages only breadcrumb

The most logical place to render your breadcrumb from is your master layout… However it’s unlikely you’ll ever want to render to on your home page right? I mean what is the point of seeing just “Home”.

This can be simply achieved using a partial template to render your breadcrumb. The code below should explain things for you, but leave a comment if you would like further explanation.

bootstrap.php

$navigationConfig = new Zend_Config_Xml('./application/config/navigation.xml');
$navigation = new Zend_Navigation($navigationConfig);

navigation.xml

< ?xml version="1.0" encoding="UTF-8"?>
<configdata>
    <nav>
        <label>Home</label>
        <module>default</module>
		<controller>index</controller>
		<action>index</action>
        <pages>
            <computers>
                <label>Computers</label>
                <module>computers</module>
                <controller>index</controller>
                <action>index</action>
                <pages>
                    <networking>
                        <label>Networking</label>
                        <module>computers</module>
                        <controller>networking</controller>
                        <action>index</action>
                        <pages>
                [...]
        </pages>
    </networking></pages></computers></pages></nav>
</configdata>

layout.phtml

echo $this->navigation()->breadcrumbs()->setPartial(array('breadcrumb.phtml', 'default'));

breadcrumb.phtml

// only render if there is more than 1 page in the breadcrumb
if (count($this->pages) > 1) {
    $links = array();     
    foreach ($this->pages as $page) {
        $links[] = $this->breadcrumbs()->htmlify($page);
    }
    echo implode(' &gt; ', $links);
}
Share and Enjoy:
  • DZone
  • Digg
  • del.icio.us
  • Technorati

Dec 11 2008

phpDocumentor (phpDoc) templates

Most professional PHP developers should be familiar with phpDocumentor, an “auto-documentation tool for the php language”. If you’re not please go check it out and start using it immediately! It has some great features, but that’s not in the scope of this post…

Unfortunately most of the phpDocumentor templates leave much to be desired! However I’ve started looking around for some others and was quite impressed by these:

I’ll update this post as I find more, so please feel free to contribute..

Share and Enjoy:
  • DZone
  • Digg
  • del.icio.us
  • Technorati

Dec 5 2008

Wheres the Model in Zend Frameworks MVC?

Don’t get me wrong ZF is my PHP framework of choice however it’s really lacking the Model concept from the MVC design pattern! The Model is where you should be implementing your business logic, data validation for example…

Zend Framework implements interfaces for filtering and validation on it’s Form components. The framework however lacks these interfaces on other components suitable to implement a true MVC design pattern where the validation would occur in the Model.

The interfaces on the Form component aren’t suitable for complex applications where a single model is used by multiple forms in the application. Say you store your email addresses in a single table in your database, but in your application, multiple ‘objects’ have email addresses associated with them… Should u have to setup the email address validation 10 times for each form the field is used in? I don’t think so…
I’m yet to work out the perfect solution to this unfortunately, but some ideas I’ve had are:

  1. Store you Form Elements in your model classes and add them to your forms in the Controller as required.
  2. Create you own model class with it’s own validation interfaces.

The second option is what I’ve done in past applications and I’m just about to start my next and decided to see if there is a better solution out there… The best article I’ve found was at techfounder and was proposing the second option as well and had some good examples. And model class approach on jmgtan.. However both still leave a lot to be desired :(

I’d be interested to hear any other ideas, or see any other bookmarks you have!

Share and Enjoy:
  • DZone
  • Digg
  • del.icio.us
  • Technorati

Dec 16 2007

Zend debugger – without Zend Core/Zend Platform

Zend debugger is basically the server side component that is used by Zend Studio. You need this to do your remote debugging!!

Normally you’d find this included in an installation of Zend Core or Zend Platform. However we aren’t using Zend’s apache php bundle so this little gem has to be loaded…

It’s hard to find on the net and not well advertised – I imagine because of Zend’s preference that you’d start using Zend Core.. Which looks great mind you but is missing some vital extensions some of our projects are using..

http://downloads.zend.com/pdt/server-debugger/

Share and Enjoy:
  • DZone
  • Digg
  • del.icio.us
  • Technorati

Oct 23 2007

Giving PHP exec() some privilege

My problem is I and working on a web interface which runs some limit shell command. Some of these normally require root access… And I don’t want to do something silly like force apache to run as root now do I?

Provided you have sudo installed (like most distro’s) the following is a good solution I came across!

Update your sudoer config (mines at /etc/sudoers) so your apache user can run the required command.For example:

Cmnd_Alias TOOLS=/usr/sbin/yourcommand,/usr/sbin/anotherone
www-data ALL=NOPASSWD: TOOLS

Then in your PHP you would execute the command like so:

exec("/usr/bin/sudo /usr/sbin/yourcommand");

If anyone can suggest a better method I’d love to hear!!

Share and Enjoy:
  • DZone
  • Digg
  • del.icio.us
  • Technorati

Oct 10 2007

PHP: Parse HTML returning links

My goal was more complex than what’s described here in, but I wanted to share a simple function for returning the links in some HTML (now that I know what I’m doing)… Hopefully someone finds this useful, it was a common question in forums I noticed.

Regular expressions are a power tool for working with strings. PHP provides support for a couple of different types but I’m using preg (aka the Perl compatible one).

The regular expression I put together for this was:

/<a\s[^>]*href=”(?P<href>[^"]*)”\s[^>]*>(?P<name>.*)<\/a>/si

What this means is:

  • / - perl regular expression patterns are enclosed in forward slashes (this is the opening one)
  • <a - is satisfied literally (the open of the html a tag)
  • \s - is a single whitespace character (includes line breaks etc)
  • [^>]* – satisfied by any characters except >, this can be satisfied zero – many times (allows for anything else inside the html a tag)
    • [ ] - a charter class
    • ^ – except the following
    • > – is satisfied literally
    • * – the charter class can occur zero of many times
  • href=” – is satisfied literally
  • (?P<href>[^"]*) – match and return as ‘href’ – any characters except “, this can be satisfied zero – many times (gets everything inside the href attribute)
    • ( ) - match and return
    • ?P<href> – nominate the name we’ll return it as ‘href’ could be anything you like!
    • [^"]* - satisfied by any characters except “, this can be satisfied zero – many times
  • > – is satisfied literally (the close of the html a tag)
  • (?P<name>.*) - match and return as name – any character, this can be satisfied zero – many times (gets everything inside the a tag)
    • ( ) - match and return
    • ?P<name> – nominate the name we’ll return it as ‘name’.
    • .* – satisfied by any character, this can be satisfied zero – many times
  • <\/a> - is satisfied literally (but we’re escaping the forward slash we don’t want to end up pattern here)
  • / - now we want to end our pattern!
  • si - the trailing s and i are modifiers to change the way the expression is interpreted
    • s - means the . we’ve used can also represent line breaks (normally it doesn’t)
    • i – means the entire thing is case insensitive!

A PHP function using this might look like so:

private function getLinks($responseBody){
    $_regexp = '/<a \s[^>]*href="(?P<href>[^"]*)"\s[^>]*>(?P<name>.*)< \/a>/si';
    preg_match_all($_regexp, $responseBody, $matches);
 
    $i = 0;
    foreach($matches['name'] as $name) {
        $links[$i]['name'] = trim($name);
        $i++;
    }
 
    $i = 0;
    foreach($matches['href'] as $href) {
        $links[$i]['href'] = $href;
        $i++;
    }
 
    return $links;
}</name></href></a>

Issues with this regular expression I know I haven’t address are:

  • You’re link may not be text, it could be an image or anything!
  • Not everyone using double quotes for their attributes.
  • Browsers support sloppy HTML this experession doesn’t! E.g. <a href = /link/>

Any corrections or feedback would be pleased to hear from you!

Share and Enjoy:
  • DZone
  • Digg
  • del.icio.us
  • Technorati