Category Archives: Programming

Fixed navigation (scrolling to content headings)

I used http://www.distractedbysquirrels.com/blog/one-page-layout-with-fixed-navigation-and-jquery/ as a foundation for a fixed navigation/scrolling type page, and modified it so it was a bit nicer and more adaptable, like this:

$(document).ready(function() {
	// Parameters
	var navOffset 	= 15,
		scrollSpeed = 500;
		animationSpeed = 100;
		cssClass = 'scrollNavOn';
		minTop = $('#content').offset().top,
		maxTop = $('#content').height() + minTop - $('#navigation').height();	
		
	// Place navigation
	var currentScroll = $(window).scrollTop();
	$('#navigation').css({'top' : minTop});
	
	// Align navigation after loading while scrolling through the content
	if( currentScroll > minTop && currentScroll < maxTop ) 
		$('#navigation').css({'top' : navOffset + 'px'});	
	
	// Adjust navigation top to content top and end of content
	if( currentScroll <= minTop ) 
		$('#navigation').css({'top' : minTop - currentScroll});
	if( currentScroll >= maxTop ) 
		$('#navigation').css({'top' : maxTop - currentScroll});
	
	// Get section positions to use later
	var sections = [];
	$('.scroller-sections').each(function() {
		sections.push([$(this).attr('id'), Math.floor($(this).offset().top)]);
		var currentSection = $(this);		
	});
	
	// Set the click functionality for the nav
	$("#scroller-navigation li:not(.notsection)").each(function(index) {
		$(this).click(function (event) {
			event.preventDefault();
			var targetOffset = sections[index][1] - navOffset;
			$('html,body').animate({scrollTop:targetOffset}, scrollSpeed);
		});
	});

	// On scroll highlight the correct nav item
	newNavIndex = 0;
	$(window).scroll(function() {
		// Current scroll
		var winScroll = $(window).scrollTop();
		
		// Align navigation to window scroll while scrolling through content
		if( winScroll > minTop && winScroll < maxTop )
			$('#scroller-navigation').css({'top' : navOffset + 'px'});
			
		// Adjust navigation top to content top and end
		if( winScroll <= minTop ) 
			$('#scroller-navigation').css({'top' : minTop - winScroll});
		if( winScroll >= maxTop ) 
			$('#scroller-navigation').css({'top' : maxTop - winScroll});
		
		// Iterate through the different sections and work out which nav element to highlight
		for(var i = 0; i < sections.length; i++) {
			// Work out which section we are on - get the top of our current section and the top of the next section
			var lowerLimit = sections[i][1] - navOffset
			var upperLimit = $(document).height(); // If we are on the last section  set upperlimit to be document height
			if(i < sections.length - 1) 
				upperLimit = sections[i + 1][1];

			// Run through each section height and see whether it's between the upper and lower limits, and also whether we've scrolled down to the end of the document
			if(	((winScroll + navOffset) < upperLimit && (winScroll + navOffset) > lowerLimit) || 
				(i == sections.length - 1 && (winScroll + $(window).height() == $(document).height()))	) {
				// $('#scroller-navigation li:nth-child(' + (i + 1) + ')').css({'font-weight' : 'bold'});

				$('#scroller-navigation li:nth-child(' + (i + 1) + ')').addClass(cssClass, animationSpeed);
				$("#scroller-navigation li:not(.notsection)").each(function(index) {
					if(i != index)
						$(this).removeClass(cssClass, animationSpeed/2);
				});				
			}
		}
	});		
});

With HTML like this:

<html> 
  <head> Add your CSS and JS here </head> 
  <body> 
    <div id="wrapper"> 
      <div id="sidebar"> 
        <ul id="scroller-navigation"> 
          <li>List your nav items here</li> 
        </ul> 
      </div> 
      <div id="content"> 
        <div class="scroller-sections" id="uniqueid"> 
          <h2>Heading</h2> 
          <p>Content</p> 
        </div> 
      </div> 
    </div> 
  </body> 
</html>

What someone really needs to do now is automatically build the <li>s based on how many h2s (or whatever, that should be passed in as a parameter I guess) there are in the text and set the nav up automatically.

Python script for Vortex

Vortex is used on the Conservation Biology MSc course at UCT to simulate the extinction process of a species stochastically. Its user interface in managing/renaming different scenarios isn’t great, and I was a bit lazy and ended up with a single vortex project file containing a huge number of scenarios that were redundant for my project. I certainly wasn’t going to create them all again from scratch, so I manually removed the chunks in my .vpj pertaining to the simulations I no longer wanted, and wrote a quick script which would fix all of the numbering and references throughout the .vpj program using Python and regex. I hope this will help someone else:

import re
btm = open('C:/yourpathto/vortexproject.vpj', 'r')
i = 0
outputdata = []

# Iterate through all of the scenario notes, keeping track of what number we are on and changing numbers as necessary
for line in btm:
 match = re.match('^\[(\d\d?\d?) Scenario Notes\]$', line)
 if match:
   i = i + 1
 match = re.search('^\[\d\d?\d?\d? ', line)
 if match:
   line = re.sub('^\[\d\d?\d?\d? ','[' + str(i) + ' ', line)
 outputdata.append(line)

# Create a new file to output the data
outputfile = open('C:/replacementvortexproject.vpj', 'w')
# Loop through each line in outputdata and add it to the new file
for line in outputdata:
  outputfile.write(line.lstrip('\r\n'))
# Close the file and print to screen to know the program has ended
btm.close()
outputfile.close()
print 'Finished'

Find a city in wunderground when there is more than one entry

Wunderground’s API website seem to cover every single possible thing you might want to do with their service, apart from, inexplicably, actually having a list of cities and corresponding codes for you to look at in order to get the weather as accurately as possible for outlying regions (in other words, to work out what the nearest city to the place you want is that they have data on).

What you have to do is go to their main website and search for your country, it’ll give you a list of names of cities. You can use these and just add %20 instead of spaces, etc, and shove it into the code example they give you, such as :

$json_string = file_get_contents("http://api.wunderground.com/api/417de1380f19b8bc/geolookup/forecast/q/ZA/Cape%20Town.json");
$parsed_json = json_decode($json_string);

But you’ll run into problems when there is more than one entry for the city you’re searching for, for example Pietermaritzberg or Nelspruit in South Africa both (oddly) have two entries – see http://www.wunderground.com/global/ZA.html?MR=1.

As far as I can see there’s nothing mentioned on their API documentation about this. It’d be fine if the json you got given back contained forecasts for both of the locations, but sadly it doesn’t. So what you have to do is cunningly look at the main website’s code, check various links to see if you can work out a possible way to query stuff, google fruitlessly, try random combinations and then (!!) take the zmw code for the city you want out of the array of responses you get, and use it like this:

$json_string = file_get_contents("http://api.wunderground.com/api/417de1380f19b8bc/geolookup/forecast/q/zmw:00000.1.68289.json");
 $parsed_json = json_decode($json_string);

In general their new website, service and api website seem very nice and organised so it’s entirely possible this information is somewhere and I was just blindly not seeing it, but I did look quite a bit and sometimes the most simple obvious things just get overlooked.

Drupal 6 Fatal error maximum execution time of x seconds exceeded

For users of date repeat api note that if you are using a calendar view and you enter a recurring date with a lot of exceptions you are going to get some really slow SQL queries and possibly some errors/max execution time exceeded errors. I found that after a while, probably as soon as views caching started kicking in, my website was pretty much unusable. So! On the offchance you have come to this post because your website is crashing, and you happen to be using date repeat + views calendar, it could be because you’ve recently published something which adds a lot of exceptions to your date repeat rule!

Python on windows 7 with Vim

  1. Download and install Vim
  2. Download this Vim script and put it in your <vim>/plugin  http://www.vim.org/scripts/script.php?script_id=127
  3. Download python syntax highlighting for Vim and put it in <vim>/syntax http://www.vim.org/scripts/script.php?script_id=790 (you could also look at http://code.google.com/p/vimcolorschemetest/)
  4. Download and install python 3.2 from here http://python.org/getit/releases/3.2.2/
  5. Under control panel > system click Edit environment variables and environment variables, and either edit the system path variable or add a new path variable for your user. Variable name is ‘Path’, and put in your Vim path and your python path separated by a semicolon, like this : “C:\Python32;C:\Program Files (x86)\Vim\vim73″
  6. Edit runscript.vim (downloaded in 2) and open it in notepad to edit line 40 to “let s:PathToExecutable = ‘python'” and line 44 to let s:mainfile = bufname(‘%’). I’m not sure why the last one is needed but it seems to be!
  7. Run cmd and type ‘vim helloworld.py’. Go into insert mode by pressing i, type ‘print(‘helloworld’)’, press esc and save the file by typing ‘:w’.
  8. Hit F9 to bring up the output buffer and F12 to run your program!

Fighting with Vim

So, the whole point of Vim is that it’s supposed to be easy  and require as few keystrokes as possible to make stuff happen, right? Well, isn’t it odd that simply saving your work requires double the number of keystrokes that it does in notepad++ or whatever text editor? The nervous tic, the repetitive twitch of the fingers to ctrl+s every few minutes whenever you stop to look over what you’ve typed is universal. So shift + ; and then w and then enter, vs ctrl+s? What’s with that! Weiiiiiiird.

Anyway, here’s a tutorial on how to do a quick key map for ctrl+s http://vim.wikia.com/wiki/Map_Ctrl-S_to_save_current_or_new_files

Complex workflows and multiple authors for Drupal nodes

A very common scenario: You have several users with different specialities who need to write nodes for your website. You have their colleagues who want to be able to comment on whatever they have written. Their supervisors wish to be able to look over their work before it gets passed through to your dedicated web editor/publisher. Individual users want their own task managers with only the pages related to them (in their capacity as author, viewer or approver), and their current tasks listed.

Oddly enough, this seems to be rather difficult to achieve with Drupal. There’s a Workflow module which lets you do something similar with user roles, but associating individual users with individual nodes is not something that’s intuitive using Drupal. Continue reading

Drupal views subqueries

I was using a views_handler_filter in order to add a subquery to a where clause, like so:

/* IN solution */
SELECT DISTINCT(node_revisions.vid) AS vid,
node.nid AS node_nid,
node.type AS node_type,
node.status AS node_status,
FROM node_revisions node_revisions
LEFT JOIN node node ON node_revisions.nid = node.nid
WHERE
node_revisions.vid in
(SELECT MAX(node_revisions.vid) FROM node_revisions GROUP BY node_revisions.nid)

But this is terribly slow. It’s got something to do with using ‘in’. I couldn’t work out how to successfully rewrite the query using ‘exists’, so I rewrote it as a left join like this:


/* LEFT JOIN solution */
SELECT DISTINCT(node_revisions.vid) AS vid,
node.nid AS node_nid,
node.type AS node_type,
node.status AS node_status,
FROM node_revisions node_revisions
LEFT JOIN ( SELECT max(nr.vid) as vid FROM node_revisions nr GROUP BY nr.nid ) nr ON nr.vid = node_revisions.vid
LEFT JOIN node node ON node_revisions.nid = node.nid
WHERE
nr.vid is not null

With the SQL fixed and running smoothly (I am still not sure why using ‘IN’ was such a big issue for mysql), I needed to edit the query somehow, and I wasn’t going to bother doing it with filter handlers. I could have used (http://drupal.org/node/1065554) hook_views_pre_execute(&$view), but I decided to use hook_views_query_alter(), like this:


/**
* Implementation of hook_views_query_alter().
*/
function workflow_views_query_alter(&$view, &$query) {
if($view->name != 'my_content')
return;

$subquery = '(SELECT max(nr.vid) as vid FROM node_revisions nr GROUP BY nr.nid )';
$join = new views_join('node_revisions', 'node_revisions', 'vid', 'vid');
$join->definition = array('table' => $subquery, 'left_field' => 'vid', 'field' => 'vid', 'left_table' => 'node_revisions');
$join->extra_type = 'AND';
$join->table = $subquery;
$join->left_field = 'node_revisions.vid';
$join->field = 'vid';
$join->type = 'LEFT';
$join->adjusted = true;
$query->table_queue['nr'] = array(
'table' => $subquery,
'alias' => 'nr',
'num' => 1,
'join' => $join,
'relationship' => 'node_revisions');
$query->where[2] = array( // Note: do print_r($query) here to find out where to place your where
'clauses' => array('nr.vid is not null'),
'args' => array(),
'type' => 'AND');
}

Drupal menu admin ‘admin/build/menu-customize’ with menu items collapsed

If you’re managing a lot of menu items it can get confusing (in the Drupal 6 interface at least) to see move things around. What would be good is if they were indented or collapsible. There’s a request for it floating about but it doesn’t seem to have generated that much interest. I’ve written a quick javascript solution for it which isn’t all that fast if you have lots of items, but it works for me. Something nicer is probably possible to do with the theme_menu_overview_form (modules/menu/menu.admin.inc), but I haven’t got the time at this point to investigate it further. See this thread http://drupal.org/node/521546#comment-4947478.

You need to add the javascript to your administration theme using the .info file, and substitute your theme name in the code where necessary.


Drupal.behaviors.sanbi_administration = function(context) {
$('#menu-overview tr').click( function() {
$indentationcount = $(this).find('.indentation').length;
if($(this).next().find('.indentation').length > $indentationcount) {
$show = false;
if($(this).hasClass('highlight'))
$show = true;

$(this).toggleClass('highlight');
recursiveHiding($(this).next(), $indentationcount, $show)
}
});

// Walk through table and hide rows which are more greatly indented than the row which was clicked
function recursiveHiding(element, $count, $show) {
if($(element).find('.indentation').length > $count) {
// Remove the highlight class for anything within the hierarchy
$(element).removeClass('highlight');
if($show)
$(element).show();
else
$(element).hide();
if($(element).next().length != 0)
recursiveHiding($(element).next(), $count, $show);
}
}
}

Because I am also adding a lot of custom javascript to certain pages for my administration theme, I actually included it only for the primary links menu like this:


/**
* Implementation of HOOK_preprocess_page().
*/
function mythemename_preprocess_page(&$vars) {
if (module_exists('path')) {
$path = drupal_get_path_alias($_GET['q']);
if($path == 'admin/build/menu-customize/primary-links') {
$vars['scripts'] .= '';
}
}

... Other stuff here...

fgetcsv and character encoding

There’s a lot of info on the net about solving problems parsing csvs: using iconv, utf8_encode and utf8_decode, using fget instead of fgetcsv and parsing your file as a string, even some regex stuff for finding and replacing weird characters.

What worked for me wasn’t any of those things. I am migrating content from a Joomla website (using a modified version of TinyMCE when storing certain fields) to a Drupal one, and I kept getting problem  characters appearing as next to some spaces, and other problem characters with quotes and inverted commas.

All I had to do was open the CSV with the exported content from the Joomla tables in notepad++ and convert it to ANSI encoding (in the menu under ‘Encoding’ I clicked ‘Convert encoding to ANSI’). Let’s just hope that non-utf8 encoding isn’t going to come back and bite me in the bum later.