JavaScript

Quick Drupal Tips and Tricks

Easy enough to copy and paste

It's been a LONG time since my last post (roughly a year) so I thought it was time to finally post something new. I've been lucky enough to build quite a few Drupal themes lately and below are some bits of code that I use on a dailly basis when adding to or developing custom themes. The nice part is everything below should be easy enough to copy and paste! *Note: All code below is for Drupal 6.

Views horizontal scrollbar

You know that annoying horizontal scroll bar that appears on your browser everytime you hover over a View on your site? This short bit of CSS gets rid of that. Place the following code in your theme's CSS file and away goes the horizontal scroll. div.view div.views-admin-links { width: auto; }

Admin page columns

Ever built a theme only to have the "/admin" columns display one on top of each other? That's typically due to not enough width in the content area to properly float the two columns. Add this bit of CSS to your theme's CSS file and you will no longer have the problem of floating your admin page columns. div.admin .left, div.admin .right { margin-left: 1%; margin-right: 1%; }

Rounded tabs from Zen

I'm a big fan of the rounded "edit" and "view" tabs on the Zen theme and wanted to incorporate them into my own themes. There's a couple of files you'll need to edit but it's really quite simple copy and paste. Be sure to download the tabs images and place them in your themes folder in the "images/tabs" folder so the paths below match up.

CSS For Tabs

/* styling for node tabs (e.g., View, Edit) */ #content-tabs { margin: 0 0 1em 0; } #content-tabs ul.primary { background: url(images/tabs/tab-bar.png) repeat-x left bottom; border-width: 0; line-height: normal; list-style: none; margin: 0; padding: 0 0 0 10px; white-space: nowrap; } #content-tabs ul.primary li { float: left; margin: 0; padding: 0; } #content-tabs ul.primary li a { background-color: transparent; background: url(images/tabs/tab-left.png) no-repeat left -38px; border-width: 0; color: #777; display: block; font-weight: bold; height: 24px; margin: 0; padding: 0 0 0 5px; /* width of tab-left.png */ text-decoration: none; } #content-tabs ul.primary li a .tab { background: url(images/tabs/tab-right.png) no-repeat right -38px; border-width: 0; display: block; height: 20px; /* 24px (parent) - 4px (padding) */ line-height: 20px; margin: 0; padding: 4px 13px 0 6px; } #content-tabs ul.primary li a:hover, #content-tabs ul.primary li a:focus { background-color: transparent; background: url(images/tabs/tab-left.png) no-repeat left -76px; border-width: 0; color: #4e4e4e; } #content-tabs ul.primary li a:hover .tab { background: url(images/tabs/tab-right.png) no-repeat right -76px; } #content-tabs ul.primary li.active a, #content-tabs ul.primary li.active a:hover, #content-tabs ul.primary li.active a:focus { background-color: transparent; background: url(images/tabs/tab-left.png) no-repeat left 0; border-width: 0; color: #000; } #content-tabs ul.primary li.active a .tab, #content-tabs ul.primary li.active a:hover .tab { background: url(images/tabs/tab-right.png) no-repeat right 0; } #content-tabs ul.secondary { background: url(images/tabs/tab-secondary-bg.png) repeat-x left bottom; border-bottom: 1px solid #c0c0c0; font-size:0.8em; list-style: none; margin: 0; padding: 0; white-space: nowrap; } #content-tabs ul.secondary li { border-right: none; float: left; margin: 0 5px 0 0; padding: 3px 0; } #content-tabs ul.secondary a { background: url(images/tabs/tab-secondary.png) repeat-x left -56px; border: 1px solid #c0c0c0; color: #777; display: block; height: 24px; margin: 0; padding: 0; text-decoration: none; } #content-tabs ul.secondary a .tab { display: block; height: 18px; /* 24px (parent) - 6px (padding) */ line-height: 18px; margin: 0; padding: 3px 8px; } #content-tabs ul.secondary a:hover, #content-tabs ul.secondary a:focus { background: url(images/tabs/tab-secondary.png) repeat-x left bottom; } #content-tabs ul.secondary a.active, #content-tabs ul.secondary a.active:hover, #content-tabs ul.secondary a.active:focus { background: url(images/tabs/tab-secondary.png) repeat-x left top; border: 1px solid #c0c0c0; color: #000; }

CSS for tabs - IE7

#content-tabs ul.primary li a:hover { color: #555; cursor: pointer; text-decoration: none; } #content-tabs ul.secondary li a:hover{ color: #555; cursor: pointer; text-decoration: none; }

CSS for tabs - IE6

#content-tabs ul.primary li a, #content-tabs ul.primary li a .tab, #content-tabs ul.secondary li a, #content-tabs ul.secondary li a .tab { display: inline; /* Otherwise the blocks mistakenly get 100% width in IE5 */ display: inline-block; /* Otherwise the blocks mistakenly get 100% width in IE6 */ } #content-tabs ul.primary, #content-tabs ul.secondary { width: 100%; /* Otherwise IE5 treats the ul as floated */ width: auto; /* Reset to auto width for IE6 */ } #content-tabs ul.primary li a { background: url(images/tabs/tab-left-ie6.png) no-repeat left -38px; } #content-tabs ul.primary li a .tab { background: url(images/tabs/tab-right-ie6.png) no-repeat right -38px; } #content-tabs ul.primary li a:hover { background: url(images/tabs/tab-left-ie6.png) no-repeat left -76px; color: #555; cursor: pointer; /* Minor fix for primary and secondary tabs in IE */ text-decoration: none; } #content-tabs ul.secondary li a:hover{ color: #555; cursor: pointer; /* Minor fix for primary and secondary tabs in IE */ text-decoration: none; } #content-tabs ul.primary li a:hover .tab { background: url(images/tabs/tab-right-ie6.png) no-repeat right -76px; } #content-tabs ul.primary li.active a, #content-tabs ul.primary li.active a:hover { background: url(images/tabs/tab-left-ie6.png) no-repeat left 0; } #content-tabs ul.primary li.active a .tab, #content-tabs ul.primary li.active a:hover .tab { background: url(images/tabs/tab-right-ie6.png) no-repeat right 0; }

Template.php

/** * Adds class of "tab" to tab menu items so they can be styled. */ function phptemplate_menu_item_link($link) { if (empty($link['options'])) { $link['options'] = array(); } // If an item is a LOCAL TASK, render it as a tab if ($link['type'] & MENU_IS_LOCAL_TASK) { $link['title'] = ''. check_plain($link['title']) .''; $link['options']['html'] = TRUE; } if (empty($link['type'])) { $true = TRUE; } return l($link['title'], $link['href'], $link['options']); } /** * Duplicate of theme_menu_local_tasks() but adds clear-block to tabs. */ function phptemplate_menu_local_tasks() { $output = ''; if ($primary = menu_primary_local_tasks()) { $output .= "

    \n". $primary ."

\n"; } if ($secondary = menu_secondary_local_tasks()) { $output .= "

    \n". $secondary ."

\n"; } return $output; } After making these changes go to the Performance page and clear your site's cache "/admin/settings/performance".

Hover working on non-links in IE6

Since IE6 isn't capable of handling hovers on anything other than links (a href), it requires a specific bit of jQuery to get things working right. Thankfully Drupal 6 ships with jQuery so we can leverage it's power for the site's needs. For this example I am using a background image for my submit buttons on the site and I wanted to have the hover work for all browser, including IE6. You'll need to create a "script.js" file and add this bit of code. The reason I said to create a "script.js" file is that Drupal 6 picks that up by default, just as how it picks up the "style.css" file automatically.

script.js

Drupal.behaviors.myModuleBehavior = function (context) { // IE6 & less-specific functions // Add hover class to primary menu li elements on hover if ($.browser.msie && ($.browser.version < 7)) { $('.form-submit').hover(function() { $(this).addClass('hover'); }, function() { $(this).removeClass('hover'); }); }; }; Now that we have the code in place for the script, it's time to edit the CSS to make it all come together. Normally we do the ".form-submit:hover" to make the style change per hover on the submit button. Since IE6 doesn't understand that, we need to change that a little bit. We need to add a "form-submit.hover" along with the "form-submit:hover". The above Javascript code goes in and makes things work so IE6 can understand what we are trying to do.

CSS

.form-submit { background: url(images/input-submit.png) repeat-x top center; color: #000; } .form-submit:hover, .form-submit.hover { background-position: bottom center; color: #fff; } Well that should do it for now. I've got a LOT more tips and tricks with Drupal theming, just need to get them out of my mind an up here. Hopefully I'll get around to soon. If you have any tips or tricks and it's easy enough to copy and paste please leave a comment below. Would love to hear what other's are using with their themes.

Miscellaneous:

Creating a Google Maps/Amazon.com/Banned Books Mashup

Part One of God Only Knows How Many Steps...

And now for something completely different…

Sorry to disappoint all the little nerdlings who come around here only for the Drupal goods, but today’s post won’t be at all related to our favorite CMS.  We’ve talked about different Drupaly topics for weeks, now it’s time to shake things up a little.  Today, kids, we’re going to make a basic Google Maps mashup.

Yeah, I know.  The world needs another Google Maps mashup like I need a hole in my head, right?  Who cares.  They’re fun, and we’ll try to make our sample a little more interesting by throwing in a little Amazon.com action and some AJAX-y goodness.

So here’s what we’re shooting for:

http://www.nerdliness.com/ajaxdemo/

First of all, don’t laugh.  Jeremy’s the form guy, I only do function.  Sure, it ain’t much to look at, but it works and it’s kinda cool.  And if we wanted it to look nice, we’d pay Jeremy his surprisingly reasonable freelance rate to pretty it up for us.

Ok, so what’s the big deal?  What are we doing on that page?   In a nutshell, we’re plotting, on the fly, the locations of every public school in Texas that, according to the ACLU of Texas, banned a book during 2006.  I took the data from a PDF they release annually (http://www.aclutx.org/projects/bannedbookspg.php?pid=45) and used it to populate a MySQL table with the various data you see by clicking on the assorted links. 

The data is sent over to the fine people at Google to plot the little book icons on the map when you click on various links.  When you click on the icon on the map itself, we hit the database again to display the location’s info (address, etc).  At the same time, we send a request to Amazon.com to get the info we need to dynamically create links to their product info pages for the various book titles. 

Finally, we’re using some AJAX to narrow down your search results as you type.  For instance, if you click Book Info and start typing the word “The,” you’ll see that the list is updated with every press of the key to show just the titles that contain what you’ve typed so far.  You can then click on the title of the book in question to plot its location on the map.

Now, a couple of disclaimers.  First, this sample is far from perfect.  Not only is it about as pretty as the Sam, the three-time World’s Ugliest Dog champ (http://www.snopes.com/photos/animals/uglydog.asp), but it doesn’t function quite the way you’d expect.  Information you might think would appear in one area doesn’t show up unless you click on a book icon, for instance.  Not very intuitive and certainly not a finished product.  Still, though, it’s good enough to give you an idea of what you can do and how you might do it.

Second, as with everything we do here, the sample code should be accepted “as-is,” with no guarantee implied.  I’ll provide it all at the end, but use it at your own risk.  I don’t think anything will blow up if you use it, but consider yourself warned.

Finally, while this little tutorial is intended for the beginner, there are a few prerequisites if you want to play the home version:

  1. You need a Google Maps API key.  They’re free and easily obtained, but they’re also uniquely assigned based on your domain name.  While you might see the Nerdliness.com one in my samples, it simply won’t work for you.  Get your own here:  http://code.google.com/apis/maps/signup.html
  2. If you’re interested in the Amazon.com part, you’ll need to sign up with them, too.  It’s also free and you can earn a commission on any sales you generate.  Sign up for that here:  https://affiliate-program.amazon.com/
  3. Some sort of database.  I’m using MySQL for the backend database.  You can use whatever database you like, but you might need to adjust the various queries accordingly.
  4. PHP. 

Other than that, you just need your favorite text editor and the ability to follow directions.  A little HTML/SQL/PHP/JavaScript knowledge wouldn’t kill you, but it’s totally not necessary.  I'm assuming you don't know much.

One last thing before we get started…  this will be a multipart post, but I have no idea exactly how many parts we’re looking at.  Sorta depends on how things look as we get going.  Subsequent parts should be posted within a couple of days, a week tops, though they might be broken up by additional Drupalesque posts from Jeremy and/or Fredric.

Ok, so in today’s post, we’re just going to set things up.  If you haven’t obtained that Google Maps API key I mentioned above, do that now.  Go ahead.  I’ll wait…  If you want to play the Amazon game, then snag that, too.

Done?  Great.  Now, let’s get you some data.  Like I mentioned before, our example is plotting some banned book information strip out of a PDF posted on the ACLU of Texas website (http://www.aclutx.org).  Believe me when I say that massaging that data was a long, tedious process, one that I don’t care to repeat any time soon and that I wouldn’t wish on you, gentle reader.  Instead, I’ll give you a shortcut. 

Download this zip file:  http://www.nerdliness.com/files/sqlsetup.zip

Inside, you’ll find several SQL scripts that will create the tables we need for this experiment and fill them with the data stripped from the ACLU PDF.  Because of the relationships between the tables, you’ll need to make sure to run the scripts in the following order:

 

 

  1. createmysqltables.sql
  2. authors.sql
  3. books.sql
  4. locations.sql
  5. reasons.sql
  6. bookauthors.sql
  7. challenges.sql
  8. challengereasons.sql

Of course, being the astute and savvy Internet user you are, you’ll be looking at these scripts before you actual execute them, right?  I certainly wouldn’t trust some faceless yahoo…

And having looked at the scripts, you’ll notice a couple of things.  First, we’re creating a fairly normalized database consisting of seven tables.  The relationships look something like this:

 

Nerdliness.com Entity-Relationship Diagram for Banned Book Database

For those of you who aren’t database people, that there’s what ya call an Entity-Relationship Diagram.  As the name implies, it provides a visual representation of the relationships between various entities.  In this case, each entity is a table in our database. 

Each square is a table in our database, and the information listed in the square tells you a little something about that particular table.  For example, the CHALLENGES table is represented by a block with the word “challenges” at the top.  Simple, huh?  Within that block, you can see a list of the various columns within that table (challengeid, locationid, bookid, actiontaken, notes, challengeyear).  Any columns that are part of a Primary Key are listed at the top, underlined, and have the letters PK to the left.  Columns in a Foreign Key relationship (a value listed in this table that’s a Primary Key in another table) are labeled FK.  Finally, required columns (those created using the NOT NULL SQL clause) are in bold.

The lines between the different entities tell you about the relationship between them.  For instance, look at the line connecting the LOCATIONS table with the CHALLENGES table.  That particular relationship is called a one-to-many relationship, meaning that each CHALLENGE we log in the database is mapped to exactly one specific LOCATION.  However, each LOCATION could potentially issue one or more challenges.  But I digress…  if you want more on E-R Diagrams, talk to Google.

Second thing you might notice is that we’re not filling in all the data in the LOCATIONS table.  Why?  Well, it has to do with geocoding.

See, Google Maps can’t just plot any old location just by the address.  Instead, it plots them out according to the longitude and latitude of the place in question.  That process of translating a street address into its longitudinal and latitudinal coordinates is called geocoding.
 
The Google Maps API will allow you to geocode addresses on the fly.  In fact, that’s how I originally set up this example…  every time I clicked on a location, I’d send a request to Google to geocode that address.  When that one came back, I’d send another request to actually plot the point.

That method lends itself to a couple of problems.  First, it greatly increases the amount of time it takes to plot out points.  Geocoding is an expensive process (resource-wise, not necessarily monetarily).  When you geocode every point every time, loading the page can take a while.  In some situations, you might not have a choice.  Want to map a moving target?  You’ll have to geocode on the fly.  In our case, though, them schools ain’t going any where, so we’re pretty safe to geocode once and store the data in our table for future use. 

The second problem is that the code needs to be a little more robust if you’re geocoding on the fly.  What happens if you have a malformed address in the mix and your code fails trying to geocode it?  If you don’t take that into account, your code might choke every time you try to plot that point.  I’m lazy.  I like the idea of running the script one time, monitoring for failures, and dealing with them by simply ignoring any entries in the database missing the longitude and latitude info.

And this, my friends, is where we're going to leave off for today.  Tune in next time as we create our first little bit o' AJAX to geocode all our addresses and fill in the rest of the blanks in our table. 

SQL:

Web 2.0:

Miscellaneous:

Simplemenu Module Customizations

Making a great Drupal admin menu better

I'm a big fan of the Simplemenu module for Drupal as it's quite a time-saver when developing a site. It's really handy to have the entire admin menu right at the top of each page.

Clients also love it for it's ease of use and they don't have to navigate to "/admin" everytime they log in and want to make a change to the site. Now they can just hover over the option the want on the top navigation bar accross their browser and drill down to the specific page they want. Very handy and much quicker than manually drilling down to each page. It's been a real time-saver for me so far.

As soon as I installed Simplemenu I knew it was a module I was going to use for quite some time. That's the reason I wanted to skin it and make it look/work better for me and my clients. I also implemented it into a custom Drupal admin theme I am working on which I will release real soon. So check back for that one early next week.

My custom menu:

Custom Simplemenu

Although my changes are minor I would stil like to thank the author for creating such a useful module written by Ted Serbinski, aka, m3avrck. I took what he had supplied and modified the CSS, JavaScript (just a little) and added a few new images.

These changes look and work great in Safari, FireFox and IE7. It works in IE6 just fine but the top level menu background doesn't stay colored when you drill down to the sub menus. I'm sure it's an easy fix but I didn't bother researching it much since..well it's IE6. If anyone wants me to fix it then please let me know and I'll give it a shot.

So for now - if you'd like you can go ahead and download my modified version of the Simplemenu module for Drupal.

Please direct all feedback/comments/questions to me in this thread so I can update and improve it as needed.

Miscellaneous:

Drupal Theming

A standards approach to custom Drupal theme creation

After working with Drupal for over a year now I have learned many different ways to go about creating a custom Drupal theme. I have tried taking existing Drupal themes such as Garland and Blue Marine and hacking them to pieces while inserting my DIVs and classes trying to get things to work and look right. That just creates a huge mess of code and often contains unneeded elements (code bloat = not a good thing).

I've found that the best method for me to create custom Drupal themes is to first code up the site using static HTML/CSS, as in create an index.html file and go to town. I always code my sites using Notepad2 and then view/test them in FireFox. I also use the WebDev Toolbar so I can see how everything looks on-the-fly as I code my CSS. This really helps as I can code a LOT faster and see my CSS changes immediately.

Once I get everything looking decent (doesn't have to be perfect yet) in FireFox I then open the index.html file in IE7 and begin making IE7 specific changes to my CSS so everything looks decent there as well. I then load the index.html file in IE6 and do the same. Once things look the same across FireFox and IE7/IE6 I load it in Safari to see how it looks there. For this site I kinda ignored some issues with IE6 as it's a pain to work with and I'll get around to fixing them eventually.

So once the site looks the same in FireFox, IE6/IE7 and Safari it's time to add Drupal code to our static HTML file. This is much easier than it used to seem to me so if you follow these code snippets you should be all set...

Doctype to

*I included my CSS files for screen and print viewing (screen.css and print.css)

" lang="<?php print $language ?>"> <?php print $styles ?>

Search Box

<?php print $search_box ?>

Logo

Main Navigation

<?php if (isset($primary_links)) { ?><?php print theme('links', $primary_links) ?><?php } ?>

Header Content

<?php print $header ?>

Left Sidebar

<?php print $sidebar_left ?>

Right Sidebar

<?php print $sidebar_right ?>

Main Content

<?php print $title ?>

<?php print $tabs ?>

<?php print $help ?> <?php print $messages ?> <?php print $content; ?>

Footer

<?php print $footer_message ?>

Closing HTML (Don't forget this!)

<?php print $closure ?>

Once these snippets of code have been added in place of your existing HTML code in the index.html file, save the newly edited file as "page.tpl.php". This is your theme page and every page on your site will have the same layout as this one.

Different Home Page Layout

Say you want to have a custom home page that looks different than all of the other pages. To create a custom home page you need to copy the code from the "page.tpl.php" file and create a new file named "page-front.tpl.php" and paste that code in there.

Now you can edit the code for the "page-front.tpl.php" file to make it look however you'd like. Drupal will automatically detect and load the "page-front.tpl.php" file if it is present in your theme directory.

What that does is tell Drupal that if the user is on the home page to go ahead and load the file named "page-front.tpl.php" rather than the "page.tpl.php" file. Now you can go back to the "page-front.tpl.php" file and edit it as you see fit. This gives you the option to have a different layout for the home page and sub pages.

Now that you have a "page-front.tpl.php" and page.tpl.php" file it's time to copy over the images and CSS from your static HTML site to the newly created Drupal theme directory. So pretty much your directory structure should now look something like:

Drupal Theme Dir:
    page-front.tpl.php
    page.tpl.php
    images/
    css/

Now you need to copy the other files in which Drupal relies on to your theme directory. Be sure to copy these files over from the Blue Marine theme (that's just what theme I grab them from).

Blue Marine Theme:
    block.tpl.php
    comment.tpl.php
    node.tpl.php
    template.tpl.php
    screenshot.png

So now you should have a fully working Drupal theme. You can copy the files over to the server and place them in the "/themes/newtheme" and then log in to your Drupal site through your browser and select your newly created theme. As you can tell immediately there is quite a bit of editing needed to be done to get your theme to work perfectly in Drupal. This is where the Web Dev Toolbar comes in handy again.

So now it's time to edit/tweak your "*.tpl.php" files as well as your CSS. This is a quick and dirty way to get a custom theme up and running but hopefully you get the idea.

Anyone else have an ideas on what I can improve on here or what I didn't really cover much of? I have created somewhere around 10 custom themes in Drupal so I'm sure I overlooked something here. Leave a comment or question and I'll do my best to help you out.

Belorussian translation

Miscellaneous:

Subscribe to RSS - JavaScript