Web Development

Posted by Eddie

A great thing about Drupal is that you can extend the functionality of a site without manipulating the core code upon which the software is built.

A situation arose recently where a client wanted to change the behavior of the core comment module. They didn't say as much, but that was what needed to be done in order to achieve the desired outcome.

So the comment module allows anonymous users to leave comments when permissions are set for that. The anonymous user may leave their name in the name field instead of the default value 'anonymous'. This becomes a problem, however, when the name they enter is an exact match of an existing registered user on the site.

So, say the registered user's name is 'sam' and the anonymous user's name is 'sam', the anonymous user cannot leave their name as such because Drupal won't allow it.

So I built a module I'm calling 'comment display name'. This essentially runs a check to determine if the user is anonymous, and if it is, then defaults the required name field to 'anonymous' but hides it. It uses new database tables to store a 'display name', which the user will be entering in place of the original name field (which is now hidden). This new field doesn't show up for registered users, nor does it change any core behavior.

Here is the module for download if you'd like to try it out on Drupal 6.

Use Case:

1) You want anonymous users to comment
2) You have registered users on your site
3) You want to avoid name conflicts between registered users and anonymous users.

Continue reading...
Posted by Eddie

Here is a snippet that will allow you to configure the NextGen gallery plugin as an ad rotator. A client requested this functionality and this is the way I went about it. I chose NextGen because of it's ease of use for the client.

The javascript below extracts the description of the image, which the client will use as a destination URL, and supplants that as the new hyperlink. It rotates through each image in the gallery every 5 seconds. When it gets to the end, it starts over.

 

<script type="text/javascript">
var x = 0;
var length = jQuery('.ngg-gallery-thumbnail').size();

function countUp() {
	if (x < (length - 1)) { 
		x = x + 1
	} else {
	x = 0
	}
} 

function showImage() { 
	countUp();
	jQuery('.ngg-galleryoverview img').css({'display' : 'none'});
	jQuery('.ngg-galleryoverview span').css({'display' : 'none'});	
	jQuery('.ngg-galleryoverview img:eq(' + x + ')').show();
	var description = jQuery('.ngg-galleryoverview span:eq(' + x + ')').html();
	var href = jQuery('.ngg-galleryoverview img:eq(' + x + ')').parent().attr({'href' : description});
}

jQuery(document).ready(function() {
	showImage();
});

setInterval('showImage()', 5000);

</script>

The next step is to embed this into the template.

That's as easy as

 
<?php 
echo do_shortcode('[nggallery id=1 template=caption]');
?>

Here is a live example, located on the right sidebar.

Continue reading...
Posted by Eddie

I recently helped The Kloud Agency with a new Wordpress site. On one of the artist pages, they needed a custom pagination solution to thumb through the artists' works. If you click the artist page, you'll see six thumbnail images, with pagination options below.

Here's how I went about creating that solution.


<div id="pagination-container">
		<div id="backward">Previous</div>
		<div id="pagination">Displaying <span id="low"></span> - <span id="high"></span> of <span id="total"></span></div>

		<div id="forward">Next</div>
	</div>

This code above creates the pagination HTML. It starts out initially empty and when the DOM is ready, the values are updated using the javascript below.

 	 	<script type="text/javascript">
		var imagesVisible = 6;
		var totalImages = jQuery('.gallery dl').size();		
		var totalPages = Math.floor(totalImages / imagesVisible);
		var lowBase =0;
		var highBase= imagesVisible;
		var size = jQuery('.gallery-item').size();

		function updatePagination() {
			jQuery("span#low").html(lowBase + 1);
			if(highBase <= size) {
				jQuery("span#high").html(highBase);
			} else {
				jQuery("span#high").html(size);
			}
			jQuery("span#total").html(size);
		}

 	
		jQuery(document).ready(function() {
			jQuery('.gallery br').remove();
			
			jQuery('<br style="clear:both"/>').appendTo('#artist_thumbnails');
			
			jQuery('.gallery-item').css({'display':'none'});
			jQuery('.gallery-item:lt(' + highBase + ')').css({'display':'block'});			

			jQuery('#backward').css({'display':'none'});
		});

		jQuery('#backward').click(function(){
			
			lowBase = lowBase - imagesVisible; 	
			highBase = highBase - imagesVisible;
			jQuery('.gallery-item').css({'display':'none'});
			if (lowBase != 0) {
			jQuery('.gallery-item:gt(' + (lowBase - 1) + ')').css({'display':'block'});
			} else {
				jQuery('.gallery-item:lt(' + (highBase + 1) + ')').css({'display':'block'});
     		}
			
			jQuery('.gallery-item:gt(' + (highBase - 1) + ')').css({'display':'none'});			
			if (lowBase > 1) {
				jQuery('#forward').css({'display' : 'inline' });
			}
			
			if (lowBase <= 1) {
				jQuery('#backward').css({'display':'none'});
			}
			updatePagination();
		});


		jQuery('#forward').click(function(){
			jQuery('#backward').css({'display':'inline'});
			lowBase = lowBase + imagesVisible; 	
			highBase = highBase + imagesVisible;
			jQuery('.gallery-item').css({'display':'none'});
			jQuery('.gallery-item:gt(' + (lowBase - 1) + ')').css({'display':'block'});
			jQuery('.gallery-item:gt(' + (highBase - 1) + ')').css({'display':'none'});			
			if (highBase > size) {
				jQuery('#forward').css({'display' : 'none' });
			}
			updatePagination();
		});

		updatePagination();

 	</script>	

So a bit about the script. You can change the imagesVisible variable to whichever value makes the most sense for you. Assuming you are starting with a list or container of images, you need to determine the total amount of images. For that, we use jQuery's size() to count our total images. Now we can determine how many pages of images we'll have by dividing the number of total images by the amount of images visible we want and rounding down.

There are a few more functions to handle forward and backward traversal through the gallery.

Continue reading...
Posted by Eddie

The following code shows how I went about limiting core drupal search results to two content types, and then grouping content from those types together. In this case I stacked the content types so all of content type a would show first, followed by all content from content type b and so on. You could display the results side by side or whatever. This was done at the theme level in search-results.tpl.php


<?php 
$i=count($results);
if ($i > 0) { ?>
<h1>Search Results</h1>
<h3>You searched for <span id="query"></span></h3>
<?php } ?>

<?php print $keys; 


 for($x=0;$x<=$i;$x++){ 
	if ($results[$x]['node']->type == 'product') { ?>
	    <div class="search-box">
	        <div class="search-image">
				<img src="/<?php print $results[$x]['node']->field_productimage[0]['filepath']?>"/>
	        </div>
	        <div class="search-text">
				<div class="product-headline">
					<div class="product-headline-title">
						<a href="/node/<?php print $results[$x]['node']->nid;?>"><?php print $results[$x]['node']->field_productname[0]['safe'];?></a>
					</div>
					<div class="product-code">
						<?php print $results[$x]['node']->field_productcode[0]['value'];?>
					</div>
				</div>
				<div class="product-body">
					<?php if ($results[$x]['node']->content['body']['#value'] != NULL) { 
						print $results[$x]['node']->content['body']['#value'];
						} else {
						print 'Product information coming soon';
						} ?>
						<div class="product-details"><a href="/node/<?php print $results[$x]['node']->nid;?>">View Product Details</a></div>
				</div>
				
			</div>
		</div>
	<?php 	}
} ?>
<?php
 for($x=0;$x<=$i;$x++){ 
	if ($results[$x]['node']->type == 'animal') { ?>
	<div class="search-box animal">
		<div class="search-image">
			<img src="/<?php print $results[$x]['node']->field_thumbnail_photo[0]['filepath']?>"/>
		</div>
		<div class="search-text">
				<div class="product-headline">
					<div class="product-headline-title">
						<a href="/animals/<?php print $results[$x]['node']->title;?>"><?php print $results[$x]['node']->title;?> Care Info and FAQs</a>
					</div>

				</div>
				<div class="product-body">
					Find out more about this animal and the care needed
					<div class="product-details"><a href="/animals/<?php print $results[$x]['node']->title;?>">View <?php print $results[$x]['node']->title;?> Information</a></div>
				</div>
		</div>
	</div>
<?php	
	}
}


?>




</dl>
<?php print $pager; ?>

<script type="text/javascript">
$(document).ready(function() {
    var query = $('#edit-keys-wrapper input.form-text').val();
	$('#query').replaceWith('<span id="query">' + query + '</span>');
});

</script>

The jQuery at the end basically is just a quick way of showing the user what they searched for without having to split atoms in template.php

Posted by Eddie

One of the sites I'm working on needs a photo gallery consisting of about 300 images. However, the images they need in the gallery are stored on their old server which we don't readily have access to. So rather than copying each photo by hand, we can use PHP to copy them directly to our server without having to move each one manually. Here is a script I wrote yesterday that saved me a ton of time.

Once again, I opted to use the simple_html_dom parser, which uses selectors like jQuery to match elements on a page and allows one to do fun stuff to manipulate the data.

On the site I need to take the images from, they are listed like this:

<a href='full/photo055.jpg' rel='lightbox' style='color:#ffffff;'><img src='thumbs/photo055.jpg' border='0' width='100' height='100'>	055	</a>
<a href='full/photo294.jpg' rel='lightbox' style='color:#ffffff;'><img src='thumbs/photo294.jpg' border='0' width='100' height='100'>	294	</a>
...

And so on about 300 more times. It's a thumbnail image linked to a larger image that appears in a lightbox when the user clicks. All we're interested in, really, is grabbing the large image and copying to our server.

So here's a script that parses this page and matches each <a> tag. It examines each match again, this time only using matches that end with 'jpg'. Then each of those matches are striped of extraneous code, starting with <a href='full/photo055.jpg' rel='lightbox' style='color:#ffffff;'><img src='thumbs/photo055.jpg' border='0' width='100' height='100'> 055 </a> and whittling down to just 'full/photo055.jpg'. Once that's done, we can add on a new path of where we want the copied photo to live on our server.

<?php 
include ('includes/simple_html_dom.php');
$html = file_get_html('http://www.somedomain.com/photos/index.html');
$frontremove = "<a href='";
$backremove = "' rel='lightbox' style='color:#ffffff;'>";
$x=0;
foreach($html->find('a') as $element) {
	if(preg_match('/jpg/',$element)) {
		$image[$x] = str_replace($frontremove,"",$element);
		$image[$x] = str_replace($backremove,"",$image[$x]);
		$image[$x] = substr($image[$x], 0, 17);
		$x++;
	}
}
for($n=0;$n<=$x;$n++) {
	$file = 'http://www.somedomain.com/photos/'.$image[$n];
	$newfile = $_SERVER['DOCUMENT_ROOT'] . '/photos/'.$image[$n];
	echo $newfile;
	fopen($newfile, "w");
	if (!copy($file, $newfile)) {
		echo "failed to copy $file...\n";
	}
	fclose($newfile);
}
?>

Now that I'm looking at this script again under a well-rested mindset, I think I could simplify this and avoid using DOM parsing altogether since we know the remote directory. This simpler method would save memory and probably work more efficiently. I probably overcomplicated this script.

However, an advantage to using DOM parsing is that you would only copy over the files that you want from the page, rather than running the risk of copying any other files that may be living in that directory that you may not want.

Oh well, it's all a learning experience. I'll play with a simplified version later.