Posts tagged with node

Avatar-eric-london
Created by Eric.London on 2012-03-10
Tags:
New Comment
 
Please note: the content on this page orginates from ericlondon.com.
In this article, I'll share a drush script I wrote to export data from a Drupal site to JSON format. Scripts like this will require customization, but hopefully it will be helpful as a kick start for some. I used it to export users, nodes, comments, taxonomy, and files from a blog site.

<?php

// define data format
DEFINE('EXPORT_DATE_FORMAT', 'Y-m-d H:i:s');

// fetch desired node data
$sql = "select nid from {node} order by nid asc";
$resource = db_query($sql);
$nodes = array();
while($row = db_fetch_object($resource)) {
  $node = node_load($row->nid);
  if (is_object($node)) {
    $nodes[] = $node;
  }
}

// create a container to store all data
$data = new StdClass();

// loop through node objects, collect desired data
$data->nodes = new StdClass();
foreach ($nodes as $nid => $node) {
  
  $n = new StdClass();
  
  // basic properties
  $n->nid = $node->nid;
  $n->type = $node->type;
  $n->uid = $node->uid;
  $n->user_name = $node->name;
  $n->status = $node->status;
  $n->created = date(EXPORT_DATE_FORMAT, $node->created);
  $n->changed = date(EXPORT_DATE_FORMAT, $node->changed);
  $n->title = $node->title;
  $n->body = $node->body;
  $n->path = $node->path;

  // cck field [simple example, single value]
  if (!empty($node->field_example_single[0]['value'])) {
    $n->field_example_single = $node->field_example_single[0]['value'];
  }

  // cck field [simple example, multi-value]
  if (!empty($node->field_example_multi)) {
    $n->field_example_multi = array();
    foreach ($node->field_example_multi as $field_data) {
      $n->field_example_multi[] = $field_data['value'];
    }
  }

  // taxonomy
  if (!empty($node->taxonomy)) {
    $n->taxonomy = array();
    foreach ($node->taxonomy as $tid => $object) {
      $n->taxonomy[] = $object->name;
    }
  }

  // files
  if (!empty($node->files)) {
    $n->files = array();
    foreach ($node->files as $fid => $object) {
      $f = new StdClass();
      $f->fid = $fid;
      $f->filename = $object->filename;
      $f->filepath = $object->filepath;
      $f->filemime = $object->filemime;
      $f->filesize = $object->filesize;
      $f->timestamp = date(EXPORT_DATE_FORMAT, $object->timestamp);
      $n->files[] = $f;
    }

  }

  // comments (recursive)
  if ($node->comment_count) {
    $n->comments = get_node_comments_recursive($n->nid);
  }
  
  // process node type
  if (!isset($data->nodes->{$n->type})) {
    $data->nodes->{$n->type} = array();
  }
  $data->nodes->{$n->type}[$n->nid] = $n;
  
}

// fetch user object list
$sql = "select uid from {users} order by uid asc";
$resource = db_query($sql);
$users = array();
while($row = db_fetch_object($resource)) {
  $user = user_load($row->uid);
  if (is_object($user)) {
    $users[] = $user;
  }
}

// loop through user objects, collect desired data
$data->users = array();
foreach($users as $user) {

  $u = new StdClass();
  
  $u->uid = $user->uid;
  $u->name = $user->name;
  $u->pass = $user->pass;
  $u->email = $user->mail;
  $u->created = date(EXPORT_DATE_FORMAT, $user->created);
  $u->status = $user->status;
  $u->picture = $user->picture;
  $u->roles = array_values($user->roles);
  
  $data->users[$u->uid] = $u;

}

$json = json_encode($data);

file_put_contents('/non/docroot/path/drupal_export.json', $json);

// FUNCTIONS

// recursively fetch comments data
function get_node_comments_recursive($nid, $pid = 0) {
 
  $sql = "
    select *
    from {comments}
    where nid = %d and pid = %d
    order by thread asc
  ";
  $resource = db_query($sql, $nid, $pid);

  $comments = array();
  while ($row = db_fetch_object($resource)) {
    
    $c = new StdClass();
    $c->cid = $row->cid;
    $c->pid = $row->pid;
    $c->nid = $row->nid;
    $c->uid = $row->uid;
    $c->subject = $row->subject;
    $c->comment = $row->comment;
    $c->hostname = $row->hostname;
    $c->timestamp = date(EXPORT_DATE_FORMAT, $row->timestamp);
    $c->status = $row->status;
    $c->thread = $row->thread;
    $c->user_name = $row->name;
    
    $comments[$row->cid] = $c;
  }
  if (empty($comments)) {
    return array();
  }
  
  foreach ($comments as $key => $value) {
    $children = get_node_comments_recursive($nid, $value->cid);
    if (!empty($children)) {
      $comments[$key]->children = $children;
    }
  }
  
  return $comments;
}
?>


I put this script outside my Drupal docroot in a scripts directory. I called it via drush like this:

$ cd drupal_docroot
$ drush scr ../scripts/drupal_export.php


Avatar-eric-london
Created by Eric.London on 2011-07-24
Tags:
New Comment
 
Please note: the content on this page orginates from ericlondon.com.
In this article, I'll address an issue that has surfaced on every recent project I've been working on: how to update nodes when a CCK field's allowed values list changes. For my projects, I implemented a variety of batch processes and drush scripts to solve this problem. But for this article, I decided to implement a form submit handler solution to automatically update nodes when the CCK field settings page is submitted. Please be careful when implementing code like this, it has potential to update a lot of nodes, and hit memory limitation issues depending on how many nodes need to be updated. As I mentioned before, I implemented a combination of batch APIs and drush scripts to process production environments; I implemented this code in a development environment. Anyway..

I created a new content type "band", and added a CCK text/select field (genre) with an allowed values list:

Allowed value list before

I used Devel's auto-generate content functionality to create 20 nodes (/admin/generate/content), and then added a simple view to show the node's title and genre:

view of nodes before

Now that I had some working test data, I added some code to a custom module to modify the CCK field property settings page, and add a submit handler update the nodes:

<?php

// define module constants
define('CCK_FIELD_PROCESS', 'field_genre');
define('CCK_TYPE_PROCESS', 'band');

/**
 * Implements hook_form_alter()
 */
function helper_form_alter(&$form, &$form_state, $form_id) {
  
  // check for CCK form, CCK type, and CCK field (see above constants)
  if ($form_id == 'content_field_edit_form' && $form['type_name']['#value'] == CCK_TYPE_PROCESS && $form['field_name']['#value'] == CCK_FIELD_PROCESS) {
    
    // get original allowed values
    $original_allowed_values_string = $form['field']['allowed_values_fieldset']['allowed_values']['#default_value'];
    
    // explode values, get array of values    
    $exploded = explode("\n", $original_allowed_values_string);
    $original_allowed_values_array = array();
    foreach ($exploded as $data) {

      // explode data on "|"
      list($key, $value) = $exploded2 = explode('|', $data);      
      $original_allowed_values_array[$key] = $value;
      
    }
    
    // ensure data exists
    if (empty($original_allowed_values_array)) {
      return;
    }

    // store original values in form state storage
    $form_state['storage']['original_allowed_values'] = $original_allowed_values_array;
    
    // store other information in form state
    $form_state['storage']['cck_type'] = CCK_TYPE_PROCESS;
    $form_state['storage']['cck_field'] = CCK_FIELD_PROCESS;

    // add submit handler
    $form['#submit'][] = '_helper_form_alter_content_field_edit_form_submit';
        
  }

}

/**
 * Implements custom form submit handler
 */
function _helper_form_alter_content_field_edit_form_submit($form, &$form_state) {

  // get new allowed values list
  $new_allowed_values_string = $form_state['values']['allowed_values'];
  
  // ensure data exists
  if (empty($new_allowed_values_string)) {
    return;
  }

  // explode values, get array of values    
  $exploded = explode("\n", $new_allowed_values_string);
  $new_allowed_values_array = array();
  foreach ($exploded as $data) {

    // explode data on "|"
    list($key, $value) = $exploded2 = explode('|', $data);      
    $new_allowed_values_array[$key] = $value;
    
  }  

  // ensure data exists
  if (empty($new_allowed_values_array)) {
    return;
  }
  
  // NOTE: the next few lines gets the key values from the allowed values
  // and checks to see which are different.
  // This code assumes any change to the keys (including changing the order) will update the nodes.
  // Update to your heart's content
  $new_keys = array_keys($new_allowed_values_array);
  $old_keys = array_keys($form_state['storage']['original_allowed_values']);
  
  $key_diffs = array();
  foreach ($old_keys as $key => $value) {
    
    if ($new_keys[$key] != $value) {
      $key_diffs[$value] = $new_keys[$key];
    }
    
  }
  
  // ensure data has changed
  if (empty($key_diffs)) {
    unset($form_state['storage']);
    return;
  }
  
  // get data from form storage
  $cck_field = $form_state['storage']['cck_field'];
  $cck_type = $form_state['storage']['cck_type'];
  
  // loop through key diffs
  foreach ($key_diffs as $old => $new) {
  
    // get a list of node ids that need to be updated
    // NOTE: this sql assumes the field is NOT shared
    $sql = "
      select n.nid
      from {node} n
      join {content_type_%s} b on b.nid = n.nid and b.vid = n.vid
      where n.type = '%s' and b.%s_value = '%s'
    ";
    $resource = db_query($sql, $cck_type, $cck_type, $cck_field, $old);
    $node_ids = array();
    while ($row = db_fetch_object($resource)) {
      $node_ids[] = $row->nid;
    }
    
    // ensure results exist
    if (empty($node_ids)) {
      continue;
    }
    
    // loop through node ids
    // NOTE: got a lot of nodes? you might need a batch API instead
    foreach ($node_ids as $nid) {
      
      // load node
      $node = node_load($nid, NULL, TRUE);
      
      // ensure node loaded
      if (!is_object($node)) {
        continue;
      }
      
      // update node properties
      $node->{$cck_field}[0]['value'] = $new;
      $node->revision = TRUE;
      node_save($node);
      
      // remove cck cache for node
      db_query("DELETE FROM {cache_content} WHERE cid LIKE 'content:%d%'", $node->nid);
      
    }
  
  }
  
  // remove form storage
  unset($form_state['storage']);

}
?>


To test this code, I went back to the CCK field settings page (example: /admin/content/node-type/band/fields/field_genre), updated the allowed value list, and saved.

updated allowed value list

The form submit handler is triggered and the nodes are updated. Refreshing my view shows the updated genre fields on my test nodes:

Updated node list
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


Avatar-eric-london
Created by Eric.London on 2009-08-30
Tags:
New Comment
 
Please note: the content on this page orginates from ericlondon.com.
Last year I wrote a quick code snippet to Prevent the user from creating more than one node of a certain type for Drupal 5. I received a comment request to update this code for Drupal 6.

<?php
function MYMODULE_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {

  // define node type that a user will only be allowed to create one instance of 
  $singleNodeType = 'YOUR-NODE-TYPE';
  
  // test for node/add/NODETYPE page
  if ($node->type==$singleNodeType && $op=='prepare' && arg(0)=='node' && arg(1)=='add') {

    // define sql to create node table
    $sql = "select nid from {node} where type='%s' and uid='%d'";
    
    // execute sql
    $resource = db_query($sql, $singleNodeType, db_escape_string($GLOBALS['user']->uid));
    $result = db_result($resource);

    // test for result
    if (!empty($result)) {
      
      // set a message
      drupal_set_message("Sorry, you are only allow to create one $singleNodeType.");
      
      // redirect the user
      drupal_goto('node/add');
      
    }
        
  }
  
}
?>


The above code tests if the user is on the node/add/NODETYPE page and if the user has already created an instance of the node type, sets a message and redirects them away from the node/add page.
Avatar-eric-london
Created by Eric.London on 2009-08-09
Tags:
New Comment
 
Please note: the content on this page orginates from ericlondon.com.
In this tutorial I'll show you how you can add jQuery image carousel functionality to your CCK node. This tutorial requires you to install the following modules:


cck
filefield
imageapi
imagecache
imagefield


NOTE: The next few steps assume you have not already setup a content type and ImageCache preset. You can ignore them if you already have.

Once you installed the above modules and set permission accordingly, you'll need to define an ImageCache preset (/admin/build/imagecache/add). I called mine "thumbnail" for this example. I added a "Add Scale" action to set the width to 100 (pixels). This will ensure when you create an image, a thumbnail will be created automatically.



Create a new content type (/admin/content/types/add). I called mine "Carousel" for this example. I then clicked on "Manage Fields" (/admin/content/node-type/carousel/fields) to setup a new Image field. I called my new field "field_images", selected "File" for field type, and selected "Image" for the form element.



On the next screen, scroll down to the Global fieldset region and enable the "Required" checkbox, and set the "Number of Values" to "Unlimited". This will allow the user to upload numerous image files to the same CCK field.

Next, you'll want to set which image preset it shown when viewing the node, by clicking on "Display fields" (/admin/content/node-type/carousel/display). I chose the "thumbnail image" preset for both the teaser and full node displays.

I then created a new carousel node (/node/add/carousel). I used the "Add another item" button to upload three images to this node.



If you view the node, the images will be stacked vertically.



This is where the fun starts. You'll need to download the jQuery Carousel library (http://sorgalla.com/projects/jcarousel/). I downloaded the jcarousel.zip file and unpacked it. Copy the entire unpacked jcarousel folder into your theme directory.

Now, you'll need to add a preprocess_node function to your template.php file:

<?php
function YOURTHEME_preprocess_node(&$variables) {
  
  // test for carousel node type
  if ($variables['type'] == 'carousel') {

    // include jCarousel javascript
    drupal_add_js(path_to_theme() . '/jcarousel/lib/jquery.jcarousel.js');
    
    // add jquery in enable jQuery carousel on <ul>
    $js = "
      $(document).ready(function(){
        $('#mycarousel').jcarousel();
      });
    ";
    drupal_add_js($js, 'inline');
    
    // include jCarousel css
    drupal_add_css(path_to_theme() . '/jcarousel/lib/jquery.jcarousel.css');
    drupal_add_css(path_to_theme() . '/jcarousel/skins/tango/skin.css');
    
    // loop through images and create an item list
    $items = array();
    foreach ($variables['field_images'] as $key => $value) {
      $items[] = $value['view'];
    }
    
    // ensure images exist
    if (count($items)) {
      // add jQuery carousel html to $content variable
      $variables['content'] .= theme('item_list', $items, NULL, 'ul', array('id' => 'mycarousel', 'class' => 'jcarousel-skin-tango'));
    }
    
  }
  
}
?>


The previous code snippet should result in the following:



If you'd like you can hide the old image html by adding a single line of CSS (or by adding more elaborate code in your template preprocess function).

Example:

div.field-field-images {
  display: none;
}


Adding the previous CSS will result in this example:



UPDATE: This blog post is deprecated in favor of: http://drupal.org/project/jcarousel