background image

Content tagged with: module

Eric's picture

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

Eric's picture

In this tutorial, I'll show how you can make a SOAP call from a Drupal page callback using the nusoap library.

For this example, I decided to create a sample soap server instance for testing purposes. I created the following directory structure for my new module: sites/all/modules/custom/nusoap/. I then download the nusoap library (nusoap-0.7.3.zip), extracted the archive, and put the "lib" folder in my module directory (sites/all/modules/custom/nusoap/lib).

In this directory, I created a file called "soap-server.php" to contain my soap server instance and added the following code:

<?php
// define namespace
define('NUSOAP_NAME_SPACE', 'erl.dev');

// define path to nusoap library file
$nu_soap_path = 'lib/nusoap.php';

// ensure nu_soap library exsists
if (!file_exists($nu_soap_path)) {
  die(
'An error has occurred initializing the soap server.');
}

// include nu_soap library
require_once ($nu_soap_path);

// create new soap server instance
$soap_server = new nusoap_server();

// configure wsdl
$soap_server->configureWSDL(NUSOAP_NAME_SPACE, 'urn:'. NUSOAP_NAME_SPACE);

// add a custom data type: person
$soap_server->wsdl->addComplexType(
 
'person',
 
'complexType',
 
'struct',
 
'all',
 
'',
  array(
   
'firstName' => array(
     
'name' => 'firstName',
     
'type' => 'xsd:string',
    ),
   
'lastName' => array(
     
'name' => 'lastName',
     
'type' => 'xsd:string',
    ),
  )
);

// register method: personTransfer
$soap_server->register(
 
// method name
 
'personTransfer',
 
// input args
 
array('person' => 'tns:person'),
 
// output args
 
array('return' => 'tns:person'),
 
// namespace
 
'uri:'. NUSOAP_NAME_SPACE,
 
// SOAPAction
 
'uri:'. NUSOAP_NAME_SPACE .'#personTransfer',
 
// style
 
'rpc',
 
// use
 
'encoded'
);

// process raw post data
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$soap_server->service($HTTP_RAW_POST_DATA);

/**
* Define soap methods
*/
function personTransfer($person = array()) {

 
// per testing, modify data
 
foreach ($person as $key => $value) {
   
$person[$key] = $value . "!";
  };

  return
$person;

}
?>

Now, if I browse directly to my soap-server.php file (for example: http://drupal.erl.dev/sites/all/modules/custom/nusoap/soap-server.php), I see the following screen:

soap server

Clicking on the WSDL link will show my automatically generated WSDL/XML; clicking on the method name "personTransfer" will show more details about the soap server method. Thanks nusoap!

Next, I created the custom Drupal module file...

<?php
// define namespace
define('NUSOAP_NAME_SPACE', 'erl.dev');

/**
* Implements hook_perm()
*/
function nusoap_perm() {
  return array(
'access soap');
}

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

 
$items = array();
 
 
$items['soap-client'] = array(
   
'title' => t('Soap client'),
   
'description' => t('Soap client'),
   
'page callback' => 'nusoap_page_callback_soap_client',
   
'access arguments' => array('access soap'),
   
'type' => MENU_CALLBACK
 
);
 
  return
$items;

}

/**
* Implements custom page callback for soap client
*/
function nusoap_page_callback_soap_client() {

 
// include nu_soap library
 
require_once(drupal_get_path('module', 'nusoap') .'/lib/nusoap.php');

 
// define wsdl path
 
$wsdl_path = 'http://' . $_SERVER['HTTP_HOST'] . base_path() . drupal_get_path('module', 'nusoap') . '/soap-server.php?wsdl';

 
// create new soap client instance
 
$soap_client = new nusoap_client($wsdl_path, true);

 
// check for error
 
$error = $soap_client->getError();
  if (
$error) {
   
// handle error
 
}

 
// define method arguments
 
$args = array(
   
'person' => array(
     
'firstName' => 'Eric',
     
'lastName' => 'London'
   
)
  );

 
// call soap server method
 
$result = $soap_client->call('personTransfer', $args);

 
// debug output:
 
$output = "";
 
$output .= "<pre>";
 
$output .= "SENT: ";
 
$output .= print_r($args, true);
 
$output .= "RECEIVED: ";
 
$output .= print_r($result, true);
 
$output .= "</pre>";
   
  return
$output;

}
?>

Browsing to the new page callback (ex: http://drupal.erl.dev/soap-client) shows the following debug output. sweet.

soap client

Eric's picture

In this code snippet, I'll show how you can parse a (large) CSV file using Drupal's Batch API. The purpose of batching an operation is to avoid PHP memory limits and time outs. Before you begin, I recommend reviewing the following two articles. Be sure to review the additional batch parameters outlined in the documentation, you might need to use them.

Batch API
Batch Operations

We can start by defining the arguments that will be passed into the batch_set() function. For this example, I added this code in an arbitrary page callback function.

<?php
function MYMODULE_callback_csv_import() {

 
// define path to CSV file
 
$csv_file_path = file_directory_path() . '/import_path/myfile.csv';

 
// define a redirect path upon batch completion
 
$redirect_path = 'admin/import-csv';

 
// define batch array structure
  // NOTE: minimal parameters defined to simplify code
 
$batch = array(
   
'title' => t('Reading File'),
   
'operations' => array(
      array(
       
'_MYMODULE_batch_read', array($csv_file_path),
      ),
    ),
  );

 
// set batch
 
batch_set($batch);

 
// process batch
 
batch_process($redirect_path);

}
?>

Next, we'll define the batch callback function. This function will be called repeatedly until the $context['finished'] variable is set to "1".

<?php
function _MYMODULE_batch_read($csv_file_path, &$context) {
 
 
// define batch limit
 
$batch_limit = 100;

 
// assume the batch process has not completed
 
$context['finished'] = 0;

 
// open the file for reading
 
$file_handle = fopen($csv_file_path, 'r');

 
// check if file pointer position exists in the sandbox, and jump to location in file
 
if ($context['sandbox']['file_pointer_position']) {
   
fseek($file_handle, $context['sandbox']['file_pointer_position']);
  }
 
 
// loop through the file and stop at batch limit
 
for ($i = 0; $i < $batch_limit; $i++) {

   
// get file line as csv
   
$csv_line = fgetcsv($file_handle);

   
// NOTE: at this point, do what ever you'd like with the CSV array data!
   
if (is_array($csv_line)) {
     
// db_query(), etc   
   
}

   
// retain current file pointer position
   
$context['sandbox']['file_pointer_position'] = ftell($file_handle);

   
// check for EOF
   
if (feof($file_handle)) {
     
// complete the batch process
     
$context['finished'] = 1;

     
// end loop
     
break;
    }

  }

}
?>

The batch operation will be called until the end of the CSV file is reached. The $context variable is passed by reference into the batch callback so you can maintain data through each iteration; in this case, the position of the file pointer. When the batch operation is complete, the user will be redirected to the batch_process() path argument.

It's important to read the full Batch API documentation so you can take advantage of its additional features: finished callback, init_message, progress_message, error_message, etc.

Eric's picture

In this tutorial I'll show how you can programmatically and dynamically remove a column from your view. In my example, I chose to hide an email column from unauthenticated users, but you could apply this code to do pretty much anything.

To get things started, I defined a content type of "Profile" to contain some user details (using the Content Profile module) and used the Devel module's generate users and nodes functionality to create some sample data. I then created a view to show the data:

Here is my page view display:

Now you can add the hook_views_pre_build() function in your module. Since the $view object is large, I recommend using krumo() (which comes with the Devel module) to browse through the $view object's properties.

<?php
function MYMODULE_views_pre_build(&$view) {

 
// check for your view name (in my example: people)
 
if ($view->name=='people') {
   
krumo($view);
  }

}
?>

The above code will neatly format the $view object in a hierarchical display. You can click to expand each class property.

As you can see, the $view object has a lot of data in it. At this point, you'll need to get acquainted with the structure of the handler object of the view ($view->display['default']->handler) and determine what to modify. To remove the email column for unauthenticated users, you can add the following code:

<?php
function MYMODULE_views_pre_build(&$view) {

 
// check for your view name (in my example: people)
 
if ($view->name=='people') {
  
   
// check if the user is anon
   
if (in_array('anonymous user',$GLOBALS['user']->roles)) {

     
// remove view hander properties for email column
     
unset(
       
$view->display['default']->handler->options['fields']['mail'],
       
$view->display['default']->handler->options['style_options']['columns']['mail'],
       
$view->display['default']->handler->options['style_options']['info']['mail']
      );

    }

  }

}
?>

When I logout, my view no longer shows the email column:

Eric's picture

There are some great development modules for Drupal (Devel, Coder, Reroute_Email, Demo, etc), but you probably don't want to have them enabled in a production environment. Deployments to production can be simplified by adding a hook_update_N function in your module's .install file. In this function you can take care of administrative functions such as importing views and CCK node type definitions (essentially, anything exportable). In this quick code snippet, I'll show how you can create a module update function to disable your development modules on update.

<?php
// NOTE: see the documentation on hook_update_N for version naming conventions
function MYMODULE_update_6100() {
 
 
// check for production environment hostname
 
if ($_SERVER['HTTP_HOST'] == 'your-production-hostname') {
   
   
// rebuild the module cache
   
module_rebuild_cache();
   
   
// define a list of development modules to disable
   
$modules_disable = array(
     
'reroute_email',
     
'coder',
     
'demo',
     
'performance',
     
'devel_node_access',
     
'devel_generate',
     
'devel_themer',
     
'devel',
    );
   
   
// disable modules
   
module_disable($modules_disable);

  }

}
?>

Now that your module update function is created, you can deploy your file updates to production (preferably using subversion) and run the update.php script, apply your module update, and disable your development modules.