Posts tagged with FAPI

Avatar-eric-london
Created by Eric.London on 2010-11-23
Tags:
New Comment
 
Please note: the content on this page orginates from ericlondon.com.
In this tutorial I'll show how you to create a custom Lucene Search facet from taxonomy and integrate into the search form block. A great first step would be to review the Drupal Lucene API documentation (see luceneapi.api.php PHP file in the luceneapi module folder).

I assigned the search form block to a region in my theme (admin/build/block), which generates this form:

Search form block

I added a new vocabulary called "Topics", and added a few terms (admin/content/taxonomy).

Topic and terms

The goal of this code is to integrate the Topic vocabulary into the search form block to allow the user to select a taxonomy term as they search. To start, you need to implement a hook_luceneapi_facet_realm() and a callback function in your custom module.

<?php
/**
 * Implements hook_luceneapi_facet_realm()
 */
function MYMODULE_luceneapi_facet_realm() {

  $realms = array();

  $realms['form'] = array(
    'title' => t('Search form block'),
    'callback' => 'MYMODULE_luceneapi_facet_realm_callback_search_form_block',
    'callback arguments' => array(),
    'allow empty' => TRUE,
    'description' => t('Displays facets in the search form block.'),
  );
  
  return $realms;
  
}

/**
 * Implements hook_luceneapi_facet_realm() callback function
 */
function MYMODULE_luceneapi_facet_realm_callback_search_form_block($facets, $realm, $module) {

  $form = array();

  // loop through facets
  foreach ($facets as $name => $facet) {

    // NOTE: luceneapi_facet_to_fapi_convert() converts a Lucene facet to Drupal Form API data
    $form = array_merge_recursive($form, luceneapi_facet_to_fapi_convert($facet));

  }

  return $form;

}
?>


At this point, if you go to the facets admin page (admin/settings/luceneapi_node/facets), you can see the newly created realm. I assigned the taxonomy vocabulary "Topic" to this realm.

Assigning facets to realms

Up next is implementing a hook_form_alter() to integrate the facet into the search form.

<?php
/**
 * Implementation of hook_form_FORM_ID_alter().
 */
function MYMODULE_form_search_block_form_alter(&$form, &$form_state) {
  
  // get default search module (IE: luceneapi_node)  
  $module = luceneapi_setting_get('default_search');

  // check if default search module is defined in lucene searchable module list
  if (array_key_exists($module, luceneapi_searchable_module_list())) {

    // get index type (IE: node)
    $type = luceneapi_index_type_get($module);

    // fetch realm facets
    $elements = luceneapi_facet_realm_render('form', $module, $type);

    // if facet form elements exist, recursively merge with current form object
    if (!empty($elements)) {
      $form = array_merge_recursive($form, $elements);
    }

  }

}
?>


The search from block should now show an empty "Topic" facet.

Search form block, empty topic

The last piece of code implements hook_luceneapi_facet_postrender_alter() which gives you the opportunity to modify the facet, and in the this case, add its options.

Immediately after implementing this hook, if you krumo() or dsm() the $items argument, you'll see the form element has no options.

Using krumo to see empty form element

The next section of code copied the contrib module code in "Lucene Node". [See file: luceneapi/contrib/luceneapi_node/luceneapi_node.module; function: function luceneapi_node_luceneapi_facet_postrender_alter()]

<?php
/**
 * Implements hook_luceneapi_facet_postrender_alter()
 */
function MYMODULE_luceneapi_facet_postrender_alter(&$items, $realm, $module, $type = NULL) {

  if ($realm == 'form' && $module == 'luceneapi_node' && $type == 'node' && is_array($items['category'])) {
    
    // get taxonomy form data
    $taxonomy = module_invoke('taxonomy', 'form_all', 1);
    
    // get enabled facets
    $facets_enabled = luceneapi_facet_enabled_facets_get($module, $realm);
    
    // loop through enabled facets, validate, and fetch weight
    $weights = array();
    foreach ($facets_enabled as $name => $value) {
    
      // check for "category" facet
      // FORMAT: category_{VOCABID}
      if (preg_match('/^category_(\d+)$/', $name, $match)) {
      
        // load taxonomy vocabulary      
        if ($vocabulary = taxonomy_vocabulary_load($match[1])) {
        
          // ensure category and vocab id is enabled for this module and realm
          if (luceneapi_facet_enabled($match[0], $module, 'form')) {
                     
            // fetch weight
            $variable = sprintf('luceneapi_facet:%s:%s:%s:weight', $module, $realm, $name);
            $weights[$vocabulary->name] = variable_get($variable, 0);
          
          }
        
        }
      
      }
    
    // end foreach
    }
    
    // gets weighted taxonomy array
    asort($weights);
    $taxonomy_weighted = array();
    foreach ($weights as $vocab_name => $weight) {
      $taxonomy_weighted[$vocab_name] = $taxonomy[$vocab_name];
    }

    // create array of fapi data to override
    $category_data = array(
      '#prefix' => '<div class="criterion">',
      '#suffix' => '</div>',
      //'#size' => 10,
      '#options' => $taxonomy_weighted,
      '#multiple' => TRUE,
      '#default_value' => luceneapi_facet_value_get('category', array()),
      '#title' => NULL,
      '#description' => NULL,
    );

    // merge data
    $items['category'] = array_merge($items['category'], $category_data);
    
    // sets weight as the lowest weight of all taxonomy facets
    if (is_array($items['category']['#weight'])) {
      $items['category']['#weight'] = min($items['category']['#weight']);
    }    
          
  // end if
  }

}
?>


Reloading the page will now show the search form with a completed facet.

Search form with facet

Submitting the search form block with a selected taxonomy term will now take the user to the search results page with the taxonomy facet pre-selected!

Special thanks to Chris Pliakas for all of his great Lucene API work! (I miss working with you Chris)
Avatar-eric-london
Created by Eric.London on 2010-06-14
Tags:
New Comment
 
Please note: the content on this page orginates from ericlondon.com.
In this tutorial I'll show you how to upload an image using the Forms API, create a new node, and attach the image to the CCK (filefield/imagefield) field. I wrote this code to work with the modules I primarily use for image processing: cck, filefield, imageapi, imagecache, imagefield, mimedetect, and transliteration.

After I installed those modules, I created a new node type (admin/content/types/add) called "Image" and added a single imagefield field.

Image node fields

Next, I created a custom module with a hook_menu() implementation:

<?php

// NOTE: this variable is used through the code,
// so I thought it would be better to put it in a constant
define('IMAGE_UPLOAD_CONTAINER', 'image_upload');

/**
 * Implements hook_menu()
 */
function helper_menu() {

  // create a blank array of menu items
  $items = array();
  
  // define page callback for upload form
  // NOTE: you'll want to restrict permission better [see: access arguments]
  $items['upload'] = array(
    'title' => t('Upload'),
    'description' => t('Upload'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array('helper_page_callback_upload_form'),
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
  
  // return menu items
  return $items;

}
?>


I defined the form function page callback:

<?php

/**
 * Implements page callback for upload form
 */
function helper_page_callback_upload_form() {

  // create an empty form array
  $form = array();
  
  // set the form encoding type
  $form['#attributes']['enctype'] = "multipart/form-data";
  
  // add a file upload file
  $form[IMAGE_UPLOAD_CONTAINER] = array(
    '#type' => 'file',
    '#title' => t('Upload an image'),
  );
   
  // add a submit button
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Submit',
  );
  
  // return form array
  return $form;

}
?>


This page callback function results in the following form:

Image node form

Then I added the form validation and submit handler functions:

<?php

/**
 * Implements form validation handler
 */
function helper_page_callback_upload_form_validate($form, &$form_state) {

  // if a file was uploaded, process it.
  if (isset($_FILES['files']) && is_uploaded_file($_FILES['files']['tmp_name'][IMAGE_UPLOAD_CONTAINER])) {

    // validate file extension
    // NOTE: you can ellaborate on this code and add additional validation
    if ($_FILES['files']['type'][IMAGE_UPLOAD_CONTAINER] != 'image/jpeg') {
      form_set_error(IMAGE_UPLOAD_CONTAINER, 'Invalid file extension.');
      return;
    }

    // attempt to save the uploaded file
    $file = file_save_upload(IMAGE_UPLOAD_CONTAINER, array(), file_directory_path());

    // set error if file was not uploaded
    if (!$file) {
      form_set_error(IMAGE_UPLOAD_CONTAINER, 'Error uploading file.');
      return;
    }
       
    // set files to form_state, to process when form is submitted
    $form_state['storage'][IMAGE_UPLOAD_CONTAINER] = $file;
       
  }
  else {
    // set error
    form_set_error(IMAGE_UPLOAD_CONTAINER, 'Error uploading file.');
    return;   
  }

}

/**
 * Implements form submit handler
 */
function helper_page_callback_upload_form_submit($form, &$form_state) {
  
  // create new node object
  $new_node = (object) array(
    'type' => 'image',
    'uid' => $GLOBALS['user']->uid,
    'name' => $GLOBALS['user']->name,
    'title' => t('YOUR NODE TITLE'),
    'status' => 1,
    'field_image' => array(
      (array) $form_state['storage'][IMAGE_UPLOAD_CONTAINER],
    ),
  );
    
  // save node
  node_save($new_node);
  
  // clear form storage, to allow form to submit
  $form_state['storage'] = array();
  
  // redirect user, set message, etc!

}

?>


After using the form to upload an image, the following node was created:

New image node