Web Development

Posted by Eddie

This post shows you how to render the markup needed for an animated drop down menu in Wordpress based on your nested parent/child page hierarchy using PHP.

OK, first let's see an example in action of what exactly we are trying to do here. The way wp_list_pages() renders the menu list items needs to be rewritten in order to power the structure of this menu:

Dynamic Menu Demonstration

Click on the link above and interact with the menu. That should do some pretty interesting things. It has a nice info area when you mouse over the menu items.

Now the need arises to be able to manage the content of this menu without having to edit the code in the template. The wp_list_pages() template tag has some pretty nice parameters to work with, but in this case, it really isn't robust enough to render the necessary markup for this menu as is. So I had to write a script to explode wp_list_pages and check each menu item for possible submenu items based on the page structure define by the administrator in the wordpress administrative panel:

<div id="quicklinks">
  <ul class="kwicks horizontal" >
    <?php $toppages = wp_list_pages('echo=0&title_li=&depth=1');
        $menuitem = explode('<li class',$toppages); 
        $n = count($menuitem);
        for ($i = 1;$i<$n;$i++){
            preg_match('/page-item-[0-9]*/', $menuitem[$i],$id); 
            $id2 = str_replace("page-item-","",$id);
            $x = $id2[0];
            $submenu = wp_list_pages('echo=0&title_li=&depth=2&child_of='.$x);
            echo '<li id="kwick_'.$i.'">
					<div class="wrapper">
						<div class="col1"><h3><li class'.$menuitem[$i].'</h3>
						<ul class="menu2">'.$submenu.'</ul></div> 
						 <div id="info_'.$x.'" class="col2" style="display: none;"> This is an info area. </div>
					</div>
				     </li>';
	     
        }
     ?> 
  </ul>
</div>

The script basically takes the list of pages, explodes it into pieces delineated by the <li> tag, counts the number of pieces, then loops through each item, checking it to see if there are subpages for that menu item by determining the page ID, running wp_list_pages() again and rendering those subpages underneath - all with the proper markup needed for the menu to work.

This was a bit of a challenge, but a great exercise in PHP. Using the script above could likely be adapted and improved to whatever you would need for a unique menu of your own powered by the Wordpress paging system.

I'm sure there are plugins for drop downs - sometimes it's more rewarding to build it yourself.

Posted by Eddie

These are the tags assigned to a sample post. The tag "sonoma" appears slightly larger than the other tags assigned to this post because it appears more frequently throughout all other posts.

A recent development project called for a tag cloud on the right rail. Easy to do generally; drupal has plenty of taxonomy modules available to take care of this for the most part.

What I needed that was different than any existing functionality I found was a tag cloud that showed:

1) A list of the node's terms in a block. These would be terms assigned to this post rather than an entire vocabulary.
2) The relative weights of those terms (and displayed visually using font size) as in a typical tag cloud.

The challenge was combining parts one and two. If you needed to apply this, the only thing in theory you'd need to change would be the vid = 5 in the database query to what ever vocabulary ID in which you want to run your query. So here is some code that does the trick (it could be simplified I think):


<?php
class tag
{
    var $name = "";
    var $count = 0;
    var $tid = 0;

    function getCount()
    {
        return $this->count;
    }
    function getName()
    {
        return $this->name;
    }
    function setCount($var)
    {
        $this->count = $var;
    }
    function setName($var)
    {
        $this->name = $var;
    }
    function setTID($var)
    {
        $this->tid = $var;
    }
    function getTID()
    {
        return $this->tid;
    }
}
$count = 0; // Overall count of used tags
$threshold = 0; // How many tags are needed to get displayed
$font_size = 0.8;
$node = node_load(arg(1));
$nid = $node->nid;
$query = "SELECT d.name,d.tid, dn.nid FROM {term_data} d, {term_node} dn WHERE dn.tid=d.tid and d.vid='5';";
$result = db_query($query);
$tags['Test'] = 0;
while($node = db_fetch_array($result))
{
    
    if ($tags[$node['name']] == NULL)
    {
        $tags[$node['name']] = new tag();
        $tags[$node['name']]->setName($node['name']);
        $tags[$node['name']]->setCount(1);
        $tags[$node['name']]->setTID($node['tid']);
        $count = $count + 1;
    }
    else
    {
        $tags[$node['name']]->setCount(
            $tags[$node['name']]->getCount() + 1
        );
        $count = $count + 1;
    }
}

$query2 = "SELECT d.name,d.tid, dn.nid FROM {term_data} d, {term_node} dn WHERE dn.tid=d.tid and dn.nid = '$nid' and d.vid='5';";
$result2 = db_query($query2);
$match1 =  $node['name'];
$match2 = $tag->name;
	while($node = db_fetch_array($result2)) {
			$termnames[] = $node['name'];
	}

foreach($tags as $tag)
{
    foreach($termnames as $termname){              
                 if($tag->name == $termname){
                 $matches[] = $tag; 
				}	
		}
}

foreach($matches as $match)
 {
    $mycount = $match->count;

    if($mycount > $threshold) 

    {
        $fraction = ((int)(($mycount / $count) * 60)) / 10;
        if($fraction < $font_size)
        {
            $fraction = $font_size;
        }
        echo '<span style="font-size: ' . $fraction . 'em;">' . l($match->name,'taxonomy/term/' . $match->tid,array('title="' . $match->count . ' Nodes"')).'</span> ';
    }
}

?>

The first half of the code I lifted from a snippet or forum post on Drupal.org many months ago, and the second half I managed to cobble together somehow on a cloudy day when the code gods were looking the other way.

Continue reading...
Posted by Eddie

Though I personally loathe twitter because of the distraction it breeds, it has a very powerful API which allows developers to create cool apps like twhirl or tweetdeck. Here is one that runs in a browser.

Background:

The client has a website built on a Drupal 6 platform and needs users to be able to tweet to their account from his Website. The tweet box on his Web site has a sports hashtag already entered into the tweetbox, and entries with that hashtag will flow through the real-time stream of that sports category. The real time twitter stream is shown to the left of the tweet box on the homepage of the site, so users can see their tweet go through the twitter river in fairly short order.

I tried a few drupal modules, but they just aren't there yet to both allow users to sign in from twitter AND tweet from the drupal site. The twitter 6.x-3.x module is still in development and I don't know enough about module development to try to offer a patch to make it work for this purpose. This module does allow you to sign in with twitter, but I was unsuccessful in getting a tweet to post using it. The official release, 6.x-2.6 doesn't have oauth support, so that isn't possible for this task.

Application overview:

This application leverages a PHP code library developed by Jaison Mathai. In fact the library pretty much is the app, with a few tweaks for my own purposes.

The application uses oauth to let twitter handle all of the login and authentication stuff, so users don't have to enter in their password to anyone but twitter. Twitter then sends back a token to requesting website, which then sets a cookie in the user's browser. The user, with cookie stored now, can now tweet in the box on the site. No passwords are ever stored - just an authentication token that expires after you log out of twitter.

For more on the behavior and security of oauth, read about it here.

Here is the very cool code library I used.

Demonstration

If you'd like to test this out how it works, go ahead and authenticate my site by signing in with Twitter
.

Once that's done, you can tweet into this box here:


Extending the Application?

The Drupal twitter module is very powerful and hooks into the core user module to create a user account for someone signing into the drupal site using twitter sign in. However, what if you don't need or want to create a user account from every authorization? What if the goal is simply to allow users to tweet and not create an account on your site?

I just picked up a copy of Pro Drupal Development, and I'm finding it very resourceful and full of great knowledge. I have yet to wrap my head around building modules for Drupal and perhaps this would be a good exercise to turn this application into a plug-and-play module, even if it isn't committed back to drupal.org.

Continue reading...
Posted by Eddie

Amid South Carolina Gov. Mark Sanford's newsmaking affair with an Argentine woman last week, we decided to run an online poll asking our readers whether Sanford should resign. We used a widget developed by Vizu to run our unscientific poll. I like their service because it's very fast, fairly customizable and produces a cool Google map of voters' locations.

This was working great as is, until the top editor of the paper said we need to show the vote totals on the site. Well, that info doesn't come as part of the Vizu widget directly, but it is visible on the page where the poll was created - but that's on Vizu's site. On a fast-moving news day (or any other day), the last thing you want to do is update anything every fifteen minutes by hand when you don't have to.

Enter Simple_HMTL_Dom - a PHP parser that does what is says - it simply parses HTML from the DOM of a given Web page. In plainer English - it scrapes a Web page and allows you to extract the information you need.

Here is the end result of what I was trying to achieve:

Poll: Should Sanford resign?

 Click here to see map of votes

That vote total above is generated by a javascript which was generated using PHP. A cron hits the PHP script every 15 minutes, which in turn writes the result to javascript. The reason for that is to keep server hits to Vizu to a minimum. It also lightens the load on your own server because you're only serving a few lines of code and since we're using drupal to write the script, doesn't hit the MYSQL database in the process.

In any event, notice the Vizu page where this poll was created. It's here:

http://www.vizu.com//poll-results.html?n=170479

Notice just above the bar chart you'll see the vote totals. That's the information we want to show dynamically.

There's really not much to Simple_HTML_Dom. Just download the script and save it to your server, include() it in a PHP script and rock and roll.

Here you go:

<?php 
include ('simple_html_dom.php');
$html = file_get_html('http://www.vizu.com/poll-results.html?n=170479');
$es = $html->find('td[align=right]');
$votes = "document.write(".$es[0].");";
$FileName2 = "votecount.js";
$FileHandle2 = fopen($FileName2, 'w') or die("can't open file");
fwrite($FileHandle2, $votes);
fclose($FileHandle2);
?>

So a few things here. The line that says

$es = $html->find('td[align=right]'); 

is just looking at the source code of the Vizu page and matching elements it finds. In this case, the first match is the Vote total. This is signified in the next line, when we define the one line of javascript that will right the first matched element:

$votes = "document.write(".$es[0].");";

Finally, we write it to a file (don't forget to first create the blank file and save it with the correct read/write/execute permissions).

The last thing you'd want to do, is set up a cron job to hit the URL of the php script every so often - in our case 15 minutes. There is tons of documentation on this step and I'll leave you to google to sort that out.

Now, in the HTML of the page where you want the dynamic info to show up, it's simply:

<script src="http://blogs.islandpacket.com/sites/default/files/votecount.js?1" type="text/javascript">

There you have it. Once you've done this once or twice, it doesn't take long to do something like this on the fly under pressure.

Continue reading...
Tags: |  
Posted by Eddie

I was recently trying to figure out how to get certain categories of staff blog posts, run on drupal 6, out of the database and written to a javascript so they could be embedded on our main site, with the headline, content category and post date intact.

I started out going the PHP db_query() function route - which uses SQL and is frankly pretty intimidating to me. For those unfamiliar, it looks something like this:

$tidQuery = db_query('SELECT td.name FROM{term_node}tn, {term_data}td, {term_hierarchy}th WHERE tn.tid = td.tid AND tn.tid = th.tid AND th.parent = 0 AND tn.nid = $Row->nid');

This would then be looped and written to a javascript in some more code that I won't bother posting.

Everytime something needed to be tweaked, the database query would have to be modified, and I'd spend time trying to figure out which database table needed to match.

Um...while structured query language is starting to make more sense to me, and is something I'd like to learn, a faster method of getting the desired result was to leverage Drupal Views and the powerful templating system to output the views query as a javascript.

So again - the ultimate result of this to use drupal views module to produce an embeddable widget for other sites.

Step 1 - In your view, create a page and assign it a path that you'll use specifically for the purpose. I called mine myviewname/custom.js. Then, in the page.tpl.php file, use an if statement to determine if the URL ends with "custom.js". This is done using arguments and should look something like this:


<?php if(arg(1) !== 'custom.js') { ?>
STANDARD PAGE TEMPLATE HERE
<?php } else {
print $content;
} ?>

The last part tells us that the only thing needed for the URL ending in custom.js is the content (which the view will return when we built it)

Step 2 - In my view, I configured it to use fields of exactly what I want, and I created three new tpl.php files to alter the output of the view - stripping away even further the html wrappers and content unnecessary for this purpose and replacing it with a javascript document.write() wrapper.

For example, in the new view-view-fields.tpl.php I added to my theme folder, all I have is :

<?php foreach ($fields as $id => $field): ?>
document.write('<<?php print $field->inline_html;?>><?php print $field->content; ?>inline_html;?>>');
<?php endforeach; ?>

My view looks like this on the backend:

The other template files I created were more specific versions of

view-view.tpl.php

view-view-unformatted.tpl.php

Step 3 - So now we have a view that spits out its output like this:


document.write('
'); document.write('
USCB men's golf team awaits word on nationals
'); document.write('Sharkbites | '); document.write('May 8, 2009'); document.write('
'); document.write('
'); document.write('
Beaufort LB Justin Parker cracks Sporting News Today's list of top recruits
'); document.write('Footblog | '); document.write('May 7, 2009'); document.write('
'); document.write('
'); document.write('
Does your dog look like an Ewok? He could be a winner Saturday
'); document.write('The Pack | '); document.write('May 7, 2009'); document.write('
'); document.write('
'); document.write('
Bluffton continues search for new town manager
'); document.write('Bluffton Blog | '); document.write('May 7, 2009'); document.write('
');

So yeah, there are a few too many document.write() statements in there, but the result is the same.

What we want to do to keep our database hits to a minimum, is prevent views from running the db query everytime our main site is hit, so we'll write this view to another javascript, which will then be included on the main site. I created a drupal page to do that, using this php code to write it to file:


$o = file_get_contents('custom.js');
$FileName = "files/blogfeed.js";
$FileHandle = fopen($FileName, 'w') or die("can't open file");
fwrite($FileHandle, $o);
fclose($FileHandle);

A cron job then hits this new page every 10 minutes, and writes it to file. The result is a low-impact, view-created javascript widget of the latest posts that we can use on any other site.

There is probably a better way to do all of this. But this way just sort of clicked for me as something cool you can do with views and templating to export customized data out of drupal.

Continue reading...
Tags: |