background image
HomeRecent PostsDrupalSearchTagsRSSContactAboutAccount
Eric.London's picture

I created a Drupal site to host my photography in CCK Imagefield nodes and used Lucene to enhance my search functionality. By default Drupal's search results are text-based so I decided to add some code to show image thumbnails in my search results. I checked out Drupal Lucene's hooks and decided to implement a hook_luceneapi_result_alter() function in my existing module.

<?php
function MYMODULE_luceneapi_result_alter(&$result, $module, $type = NULL) {
 
 
// check for node results
 
if ($type == 'node') {
 
   
// check node type
   
if ($result['node']->type == 'image') {
   
     
// define an imagecache image path for image thumbnail
     
$imagecache_path_thumbnail = file_directory_path() . '/imagecache/thumbnail' . str_replace(file_directory_path(),'',$result['node']->field_image[0]['filepath']);     
     
     
// define an imagecache image path for image (large)
     
$imagecache_path_large = file_directory_path() . '/imagecache/large' . str_replace(file_directory_path(),'',$result['node']->field_image[0]['filepath']);
   
     
// define theme_image() variables
     
$alt = check_plain($result['node']->title);
     
$title = check_plain($result['node']->title);
     
// add rel=lightbox to enable lightbox2 module
     
$attributes = array(
       
'rel' => 'lightbox',
      );
     
// let imagecache define the size
     
$getsize = FALSE;
     
// generate the image hml
     
$image_html = theme('image', $imagecache_path_thumbnail, $alt, $title, $attributes, $getsize);     
   
      if (
$image_html) {
               
       
// define lightbox link
       
$image_link = l(
         
$image_html,
         
$imagecache_path_large,
          array(
           
'html' => true,
           
'attributes' => array(
             
'rel' => 'lightbox',
            )
          )
        );

       
// add data to the result variable, passed by reference
       
$result['image_thumbnail'] = $image_link;
       
      }
   
    }
 
  }

}
?>

The above code adds additional data to my search results variables. I then implemented a hook_preprocess_search_result() function in my theme's template.php file to pass this data to the search-result.tpl.php template file.

<?php
function MYTHEME_preprocess_search_result(&$variables) {

 
// ...snip...

  // check for lucene node search results
 
if ($variables['type']=='luceneapi_node') {

   
// check for image
   
if ($variables['result']['image_thumbnail']) {   

     
// pass additional data to theme template file
     
$variables['image_thumbnail'] = $variables['result']['image_thumbnail'];

    }
   
  }

}
?>

And in my theme's search-result.tpl.php template file, I added the following PHP to show the new variable.

<div class="search-result <?php print $search_zebra; ?>">

  <?php if($image_thumbnail): ?>
    <?php print $image_thumbnail; ?>
  <?php endif; ?>

  <!-- ...snip... -->

I also added a few lines of CSS in my theme's style.css file to tidy up the layout.

.search-results.luceneapi_node-results .search-result {
  clear: both;
}

.search-results.luceneapi_node-results .search-result img {
  float: left;
  margin: 0px 20px 20px 0px;
}

The visual results can be seen here on my photo gallery.

Visual search results

This article will show you how to establish parent and child relationships between nodes. To start, you'll need to install and enable the CCK module, and node reference (which comes with CCK). Next, you'll need to create two node types (admin/content/types/add). For my example, I created two node types: Organization (parent) and Department (child), both consisting of a title and body (of course, you can add as many fields as you'd like).

Next, you'll want to add a new field to the parent node (organization) by editing the content type (admin/content/node-type/organization) and clicking on the "Manage fields" tab. Enter the information to add a new reference field on this screen. Add a label (ex: "Department Reference"), enter a field name (ex: field_ref_department), choose "Node reference" as the field type, and choose "Autocomplete text field" as the form element as shown below.

node reference add field 1

On the next screen you can choose the field options. For this example, I changed "Number of values" to "Unlimited" (to allow a one to many relationship), selected "Department" in the list of content types that can be referenced, and saved these settings.

node reference add field 2

Now, if you create a new parent node (Organization: node/add/organization), you'll have the option of adding pre-existing child (Department) nodes. As you start typing the title of the child nodes in the node reference auto-complete field, it will automatically populate the field with nodes that match. Of course this will only work if you have already created child nodes. At this point adding relationships is a two step process.

node reference add parent node 1

To improve usability, you can install the Popups API (http://drupal.org/project/popups) and Add and Reference (http://drupal.org/project/popups_reference) modules which allow you to add child nodes without leaving the node/add/PARENT-NODE screen. After installing and configuring these modules, you'll be able to add child nodes directly from the node/add/PARENT-NODE screen by clicking on the "Add new: Add Child Node link", which spawns a lightbox window to add a child node.

node reference add parent node 2

Here is a screenshot of a parent node view after adding child relationships:

node reference parent node view

The purpose of this code snippet is to show you how you can use the lightmodal functionality in the Lightbox2 module to embed a form in a fancy css popup window. But why would you want to do this? Because it looks great and it improves usability.

The first thing you'll need to do is register a menu item for the page callback that you'd like to appear in the lightbox window.

<?php
function MYMODULE_menu() {
 
$items[] = array();

 
$items['MY/URL'] = array(
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('MYCALLBACK_form'),
   
'type' => MENU_CALLBACK,
   
'title' => t('MY TITLE')
  );

  return
$items;
}
?>

Next, define a function to create the form

<?php
function MYCALLBACK_form($form_state) {
 
$form = array();

 
// add your form elements here. for example:
 
$form['MYTEXTFIELD'] = array(
   
'#type' => 'textfield',
   
'#title' => t('MY TEXT FIELD'),
  );

 
// add submit button
 
$form['submit'] = array(
   
'#value' => 'Submit',
   
'#type' => 'button'
 
);

 
// add cancel button
  // NOTE: the cancel button simply closes the lightbox window
 
$form['cancel'] = array(
   
'#value' => 'Cancel',
   
'#type' => 'button',
   
'#attributes' => array(
     
'onClick' => "Lightbox.end('forceClose'); return false;"
   
)
  );

  return
$form;
}
?>

Next, add a link to your page content to open up the lightbox window with your new form in it. By adding the rel=lightmodal property onto an A tag, you'll automatically load the contents of the href in a fancy lightbox window.

<?php
// NOTE: you should use the l() function here to properly generate your A tag
$page_contents .= '<a href="/MY/URL" rel="lightmodal">MY LINK TEXT</a>';
?>

I made my lightbox form more usable by using ajax to submit the form to another page callback. First, I had to add an onSubmit javascript event handler to my form to prevent it from submitting.

<?php
function MYCALLBACK_form($form_state) {
 
// ...code...
 
$form['#attributes'] = array(
   
'onsubmit' => 'return my_js_submit_function(this);'
 
);
 
// ...code...
}
?>

Now, define a javascript function to validate and submit the form using AJAX.

function my_js_submit_function(whichThis) {
  // validate your form here

  // define arguments to pass to ajax page callback
  // you'll actually want to collect the user submitted form data here
  var args = {
    myArg1: 'blah',
    myArg2: 'blah',
  };

  // submit the data using jQuery
  // in this case, I'm using the getJSON function which uses GET and expects a JSON object in return
  $.getJSON('/MY/URL/SUBMIT', args,
    function(json){
      // check for a return status, show a message, close the lightbox window, etc
      alert(json.message);
      if (json.status == true) {
        Lightbox.end('forceClose');
      }
    }
  );
}

You'll need a final page callback and menu item to process the AJAX

<?php
function MYMODULE_menu() {
 
// ...code...
 
$items['MY/URL/SUBMIT'] = array(
   
'page callback' => 'MYMODULE_CALLBACK_SUBMIT',
   
'type' => MENU_CALLBACK,
  );
 
// ...code...
}

function
MYMODULE_CALLBACK_SUBMIT() {
 
// 1. validate $_GET (not shown, use your imagination)
  // 2. process $_GET (not shown, use your imagination)
  // 3. return a JSON object

  // create return object
 
$returnO = new StdClass();
 
$returnO->status = true;
 
$returnO->message = 'MY INFORMATIVE MESSAGE HERE';

 
// output json result
 
print drupal_json($returnO);

 
// NOTE: if you don't die here, then the theme will be processed. we only want to return a JSON object
 
die;
}
?>

In the end, what we have is a link that opens up a lightbox window that contains a form that is submitted via ajax.

My last thoughts... because the lightmodal functionality loads the URL from the href in the A tag, you might want to create a template file for your page layout that is simplified to prevent your normal page layout from being rendered. For instance, you could create a theme file called, page-MY-URL.tpl.php that contains the following:

<?php
if ($title) print "<h1>$title</h1>";
if (
$messages) print $messages;
if (
$help) print $help;
if (
$content) print $content;
?>

Syndicate content