Forms In Drupal Overview

In this post, we cover Form API of Drupal including form creation, validation and submission.


Creating Form


There is essentially two ways with slight variation on how the form is declared and initiated.
a)Simple Form
Have a module with the moduleName.module containing the following:

/**
* Implements hook_menu().
*/
function MODULENAME_menu() {
$items['fruit/simple'] = array(
'title' => 'Form API examples',
'description' => 'Example of using the Form API.',
'page callback' => 'drupal_get_form',
'page arguments' => array('fruit_simple_form'),
'access callback' => TRUE,
);
return $items;
}

/**
* A simple form.
*/
function fruit_simple_form($form, &$form_submit) {
$form['fruit'] = array(
'#title' => t('Favorite fruit'),
'#type' => 'textfield',
'#required' => TRUE,
'#description' => t('What is your favorite fruit?'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}

The input_menu() function defines the page the end point of ‘fruit/simple’ where the form is going to be rendered (see more on Menu Sys in Drupal at post Add And Manipulate Pages).
The ‘drupal_get_form’ is an internal drupal function that returns a render array containing the form. We later define a function ‘fruit_simple_form’ that builds the render array. This function is passed as an argument to the ‘drupal_get_form’ function in line 9.

The function argument – ‘$&form_state’ (i.e. fruit_simple_form) passed to ‘drupal_get_form’ function will contain values submitted by form.

The ‘#’ symbol at beginning for each form elements are the attributes for the different input elements

If you submit this is not going to be processed till the submission function is declared

b)Creating Embedded Form
This way of creating form is good in situations when you want to add addition code or information around the form. In our example, if wanted to have a text ‘Please, choose your favorite fruit’ in front of form then:

/**
* Implements hook_menu().
*/
function MODULENAME_menu() {
$items['fruit/embedded/simple'] = array(
'title' => 'Simple',
'description' => 'Simple example using a page callback.',
'page callback' => 'fruit_simple_page',
'access callback' => TRUE,
);
return $items;
}


/**
* A simple form.
*/
function fruit_simple_form($form, &$form_submit) {
$form['fruit'] = array(
'#title' => t('Favorite fruit'),
'#type' => 'textfield',
'#required' => TRUE,
'#description' => t('What is your favorite fruit'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}


/**
* Page demonstrating embedding a form on a page.
*/
function fruit_simple_page() {
$build = array(
'header_text' => array(
'#type' => 'markup',
'#markup' => '<p>' . t('Please, choose your favorite fruit') . '</p>',
),
'example_form' => drupal_get_form('fruit_simple_form'),
);
return $build;
}

Here, the ‘fruit_simple_page’ returns the render array to our menu item with items that are going to be rendered to html page. After specifying the text around the form, we add another element on page – ‘example-form’ that is going to construct the form by calling drupal internal function drupal_get_form.

Note on drupal_render()

The drupal_render() is the function that is eventually called to render page. This function can be called on specific parts of our build array(in our case, ‘example_form’ or ‘header_text’). So if we wanted to render the form before passing it to the page callback we could do:

function fruit_simple_page() {
$build = array(
..
'example_form' => drupal_render(drupal_get_form('fruit_simple_form')),
);
return $build;

This would render the from and return to our page call back before all page is rendered. However, we want to retain the array structure of our page callback as long as possible, so the other modules can manipulate as long as they need to.

Drupal Form Validation

Drupal knows to call a validation function in two ways:

1. The name of the render array function or form id plus ‘_validate’ at the end

All functions with ‘_validate’ are searched automatically. We don’t have to register.

/**
* Validation for fruit_simple_form().
*/
function fruit_simple_form_validate($form, &$form_state) {
// Check for the fruit 'orange'.
if ($form_state['values']['fruit'] == 'orange') {
form_set_error('fruit', 'Sorry, your favorite fruit is actually apple.');
}
}
2. Register validation function by adding a function name to a from element ‘validate’

For validating some other form, we have to register our custom validation function or submission as well for that matter. There are multiple hooks to use for altering form with our custom validation function. One most general is using Hook_form_alter() as following:

function MODULE-NAME_form_alter(&$form, &$form_state, $form_id)
{
    switch ($form_id) {
        case 'OUR-FORM-ID':
            //assign validation
            $form['#validate'][] = 'our_custom_validate';
            //assign submition
            $form['#submit'][] = 'our_custom_submit';
            break;
    }
}

Since this is a general hook, we filter the form of our interest via switch statement. Afterwards, we set functions to be called for form validation and submission. In those functions(i.e. our_custom_validate, our_custom_submit) you would put logic for validating the form and form processing

Note On Validation Errors

To set validation error:

function some_validate($form, &$form_state) {
..
if (empty($form_state[values]['text'])) {
                            form_set_error('text', t('Please select an text for layer #'.$layer));
                        }
..
}

Here, we check weather the field ‘text’ is empty. If it is empty then display error message. In addition, it will highlight the form element you specified in our example ‘text’ in function form_set_error(). To find out exact element name(i.e. ‘text’) to pass in form_set_error(), look at the attribute ‘name’ value for the element

Drupal Form Submission

Drupal knows to call a submission function in two ways:
1. The name of form id plus ‘_submit’ this function will be searched automatically. We don’t have to register

function fruit_simple_form_submit($form, &$form_state) {
// Display a message upon successful submission.
drupal_set_message(t("I like @fruit, too!", array('@fruit' =>$form_state['values']['fruit'])));
}

2. Register submission function by adding to the form validate array
In the form build function add the name to handle submission

$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
'#submit' => 'name_of_function_handling_submission',
'#validate' => 'name_of_function_handling_validation',
);

function name_of_function_handling_submission($form, &$form_state){
...
}

function name_of_function_handling_validation($form, &$form_state){
...
}

Here, we first register functions to the submit element(please, see highlighted lines) to handle the submission and validation. Afterwards, those functions are implemented.

Set Variable or Save Node on Form Submission

Most of the time on submission you will save a variable or create new node with some kind of content type. To save variables:

function form_handling_submit($form, &$form_state){
variable_set('var_name',$form['field_some']['#value']);
}

Here, on form submission a value is saved for variable name ‘var_name’

To create new node instance and save it on form submission:

function collect_publication_info_form_submit($form, &$form_state) {
// Create a node object, and add node properties.
$newNode = new stdClass;//or (object) NULL
$newNode->type = 'publication';
$newNode->title = $form['first_name']['#value'].' Publication';
$newNode->language = 'und';//or LANGUAGE_NONE or language code if Locale module is enabled
$newNode->uid = 0;//or any id you wish
node_object_prepare($newNode);

//custom fields of node
$newNode->field_publisher_first_name[LANGUAGE_NONE][0]['value'] = $form['publisher_first_name']['#value'];

$newNode = node_submit($newNode);//prepare and return id
node_save($newNode);

Here a new node instance is created with content type ‘publication’ and saved in database

Explore More Form Elements and Attributes

a) Radios

$form['car'] = array(
'#title' => t('Do you own a car?'),
'#type' => 'radios',
'#options' => array('yes' => t('Yes'), 'no' => t('No')),
'#default_value' => 'yes',
'#required' => TRUE,
);

the key of the ‘#options’ is the value in the form

b) Fieldset with Textfield and Select Example
To encapsulate several other inputs.

// Demonstrating a fieldset.
$form['car_brands'] = array(
'#title' => 'Car brands',
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['car_brands']['ford'] = array(
'#title' => t('Ford'),
'#type' => 'textfield',
'#field_prefix' => t('Capital Letters'),
'#field_suffix' => t('(usa manufactured)'),
'#maxlength' => 3,
'#size' => 3,
);
$form['car_brands']['fiat'] = array(
'#title' => t('Fiat'),
'#type' => 'select',
'#options' => array(
'model1' => t('model 1.'),
'model2' => t('model 2'),
'model3' => t('model 3'),
),
);

By default, all the elements with the fieldset is passed at the same level in that from field array(in our case $form[‘car_brands’]). There is an option for ‘tree’ that will embed each fieldset element as another array within the fieldset array.

c) Help Markup Field
For situation when we want to add some info like a help info to the form without any input

$form['help'] = array(
'#type' => 'markup',
'#markup' => '<p>' . t('For more information about elements and attributes, see the <a href="!url">Form API reference page</a>', array('!url' => url('http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/7'))) . '.</p>',
);

The highlighted line contains attribute ‘#type’ that specifies ‘markup’ for any time we want to add html directly to the form. The ‘#markup’ includes the html itself

States Attribute

The state attribute tells the form what different actions is associated with different state

$form['car'] = array(
'#title' => t('Do you own a car?'),
'#type' => 'radios',
'#options' => array('yes' => t('Yes'), 'no' => t('No')),
'#required' => TRUE,
);

// Demonstrating a fieldset.
$form['car_brands'] = array(
'#title' => 'Car Brands',
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#states' => array(
'visible' => array(
':input[name="car"]' => array('value' => 'yes'),
),
),
);

Here, the highlighted lines makes the fieldset visible only if car input has selected value ‘yes’. There is also ‘invisible’ state and you can add more than one condition with multiple value in the array.

In case, you need a condition for text field being filled or not, then:

...
'#states' => array(
'visible' => array(
:input[name="child_first_name"]' => array('filled' => TRUE),
),
),
...

Here the input field ‘child_first_name’ can be of type ‘textfield’, ‘textarea’.

Finding Form ID

Here are two ways to Find Form Id:

1. Put the break points in ‘hook_form_alert(&$form, &$form_state, $form_id)’ and inspect $form_id variable

Drupal during the building process of a form before its rendered goes and looks in any modules for alter functions that manipulates the form at hand. That is one way to find

2. In Firebug, inspect the form element and look for ‘id’ attribute.

The form id is set to it:

<form id="commerce-cart-add-to-cart-form-2-8" class="commerce-add-to-cart commerce-.."

Here the form id is after replacing dashes with underscores commerce_cart_add_to_cart_form_2_8

Enforcing Constraints

Drupal has really good module for enforcing constraints – fapi validation on the user input. To install:

sudo drush dl fapi_validation
drush en fapi_validation

The FAPI Validation module comes with predefined constraints(i.e. ’email’, ‘length’,etc) that you can apply to user input as following:

...
$form['some_field'] = array(
'#type' => 'textfield',
'#title' => 'Some Field',
'#required' => TRUE,
'#rules' => array(
'email',
'length[10, 50]',
),
);
...

Here, we apply email constraint with the length limit as well. Drupal also enables to apply custom constrain defined and declared by you. For more info, please, see FAPI Validation

Redirecting After Submission

Often times you will need to redirect after successful form submission. Here is one of the may ways to redirect:

function some_form_submit($form, &$form_state){
...
$newNode = node_submit($newNode);//prepare and return id
node_save($newNode);
// Display a message upon successful submission.
drupal_set_message(t("Thank You @first_name @last_name for registering and becoming a member!", array('@first_name' =>$form_state['values']['member_first_name'],                                                                              '@last_name' =>$form_state['values']['member_last_name'])));
drupal_goto($destination);

//retrieving destination path to just created node
$destination = drupal_get_path_alias("node/".$newNode->nid);
drupal_goto($destination);
}

Here, we first create an new node – member from the information provided by user and then we redirect to the page of this particular node.

Adding Configuration Fields – hook_block_configure and hook_block_save

By default, every block comes with two fields – title and body, however. It may not be enough for some blocks that you are creating. In that case, there are two hooks – hook_block_configure and hook_block_save both to add additional fields to the block.

The hook_block_configure is used to add additional fields to the block configuration form as following:

function MODULE.NAME_block_configure($delta='') {
$form = array();
switch($delta) {
case 'twitter_feed' :
// Text field form element
$form['builder_twitter_invite'] = array(
'#type' => 'textarea',
'#rows' => '3',
'#title' => t('Enter twitter invite description displayed next to tweets'),
'#default_value' => variable_get('builder_twitter_invite', ''),
);
break;
}
return $form;
}

Here we are adding additional field of type ‘textarea’ with default value retrieved from variable ‘builder_twitter_invite’. This will enable anyone to input the value via the Admin->Blocks and configure UI form

Next, the new field needs to be saved and for that the hook_block_save is used as following:

function MODULE.NAME_block_save($delta = '', $edit = array()) {
switch($delta) {
case 'twitter_feed' :
variable_set('builder_twitter_invite', $edit['builder_twitter_invite']);
break;
}
}

Here, the field is taken from the form(i.e. $edit[‘builder_twitter_invite’]) and saved in the database via the function variable_set()

File Upload With and Without Managed_File

There are two ways to upload file via Drupal Form API. One is with the managed_file functionality and another without it.

1. Without Managed_file

Without Managed_file functionality, the form element is as follows:

function some_form_gen($element, $form_state, $complete_form){
   $element['image'] = array(
        '#type' => 'file',
        '#title' => t('Image'),
        '#description' => t('Upload a file, allowed extensions: jpg, jpeg, png, gif'),
        '#default_value' => variable_get('some_variable_'.$element['#delta'],''),
   );
return $element;
}

The element for file upload is of type – file. Also, the ‘default_value’ is retrieved from Drupal variable that contains the file FID set at the validation or submission as demonstrated next (perhaps, a better practice is to store File FID in the $form_state variable) Next, the file is saved on submission as follows:

function some_form_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors){
if (!empty($item['image'])) {
                    $file = file_save_upload('image', array(
                        // Validates file is really an image.
                        'file_validate_is_image' => array(),
                        // Validate extensions.
                        'file_validate_extensions' => array('png gif jpg jpeg'),
                    ));
                    // If the file passed validation:
                    if ($file) {
                        // Move the file into the Drupal file system.
                        if ($file = file_move($file, 'public://')) {
                            // Save the file for use in the submit handler.
                            $form_state['storage']['image'] = $file;
                        }
                        else {
                            form_set_error('image', t("Failed to write the uploaded file to the site's file folder."));
                        }
                    }
                }
...
}

Here, the file is saved or copied on form validation but it may be a better practice to do it so in save function

Upload File With Managed_File

It differs that with ‘managed_file’ functionality, the file is copied in the destination and the file FID is provided in $element[‘#value’][‘image’] variable.
To create form element:

define('REV_SLIDER_DEST', 'public://slider/revolution');
define('MAX_SIZE_LIMIT_DS', (int)(ini_get('upload_max_filesize')));

function some_form_process($element, $form_state, $complete_form){
...
$element['image'] = array(
        #title' => t('Image'),
        '#type' => 'managed_file',
        '#description' => t('Upload a file, allowed extensions: jpg, jpeg, png, gif'),
        '#default_value' => isset($element['#value']['image']) ? $element['#value']['image'] : '',
        '#upload_location' => REV_SLIDER_DEST.'/img',
        '#upload_validators' => array(
            'file_validate_extensions' => array('jpg jpeg png gif'),
            // Pass the maximum file size in bytes
            'file_validate_size' => array(MAX_SIZE_LIMIT_DS*1024*1024),
        ),
    );

Here the form element is of type ‘managed_file’. The default value is retrieved from the variable $element[‘#value’][‘image’]. Next, all is left to implement validation as following:

function some_form_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors){
...
 if($item['field_remove_item'] == 0){//check if it is no ajax remove taking place when using module field_remove_item 
                    if (!isset($item['image']) || !is_numeric($item['image']) || $item['image'] == 0) {
                        form_set_error('image', t('Please select an image to upload.'));
                    }else{
                        //make file permanent 
                        $file = file_load($item['image']);
                        // Change status to permanent.
                        $file->status = FILE_STATUS_PERMANENT;
                        //all permanent files need an entry in the 'file_usage' table
                        file_usage_add($file, 'MODULE-NAME', $entity_type, $entity->nid);
                        // Save.
                        file_save($file);
                    }
}

Here, the module field_remove_item is used to have ‘Remove’ button for field instances that are more than one. This is implemented as ajax call, so to avoid the general validation, there is check using $item[‘field_remove_item’]. Afterwards, the file FID is checked to see if file is present. At last, the ‘managed_file’ functionality does copy the file into destination location and save the file info in the table ‘file_managed’, however. The saved file entry in the table is saved with status – 0 which is temporary and is removed in some certain time. So to make it permanent, the file is loaded and status set to ‘FILE_STATUS_PERMANENT’. This will ensure the file is permanently saved

Other

1. How to add ‘class’, ‘title’, ‘name’ or any attribute value to a form element?
To add an extra value for ‘class’ attribute, add the following:

$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
'#attributes' => array(
    'class' => array('text'), // change to just 'text' for Drupal 6
    'title'=>'title',
  ),
);

Here the highlighted line would add ‘text’ to the ‘class’ attribute for the submit button

2. How to add placeholder value to the input field?

function ace_form_alter(&$form, &$form_state, $form_id){
if($form_id == 'user_login'){
...
$form['name']['#description'] = '';
$form['name']['#attributes']['placeholder'] = t('Username');
}

Here, for the login form the username input take placeholder value and the description field is removed

Add Color Wheel

You may have input for choosing color. While there are many different ways to add color wheel, here we demonstrate one powered by Spectrum.js
See details post Adding Color Wheel To Form Element In Drupal

Troubleshooting

The file used in the Image field may not be referenced.

This error comes up when the file usage information is not saved in the ‘file_usage’ table at the time the file is made permanent. By adding the following fixes:

file_usage_add($file, 'MODULE-USING-FILE', 'TYPE', 'INSTANCE_ID');

Here, the module using the file is specified with the type(i.e. node, user’, etc.) of instance along the id referencing the file
This also happened when we exported artifacts and saved their FIDs in file_manage table but forgot to update the file_usage table at the same time. As result, when editing the node, we weren’t able to save any more even tho the file was present and functioning as expected

Notice: Undefined index: #field_name in file_managed_file_save_upload()

Ensure the write permissions is set for the destination specified by ‘#upload_location’ attribute

Form Snippets

1. Comment form

$comment = new stdClass;
$comment->nid = $row->nid;
$form = drupal_get_form('comment_form', $comment);

Useful Links

  • For a full list of Form API elements and attributes
  • https://api.drupal.org/api/drupal/modules%21node%21node.module/function/node_save/7
  • http://timonweb.com/how-programmatically-create-nodes-comments-and-taxonomies-drupal-7
  • https://drupal.org/project/fapi_validation
  • http://fourkitchens.com/blog/2012/07/18/building-custom-blocks-drupal-7

Add and Manipulate Pages with Menu System in Drupal

In this post, we cover different ways to add and manipulate pages with Menu System in Drupal.

Every page loaded in Drupal:

domainname.com/path/of/somekind

is transformed as following request:

domainname.com/index.php?q=path/of/somekind

This is what you will see if the ‘clean url’ setting is turned off.
In Drupal, every path is routed through single page – index.php. This is called front controller design pattern seen in many other web frameworks today. Drupal determines where to route the request by looking at the path(everything on the right of domainname.com)

Drupal takes the path and references an index it has that assigns every path to a function call. If it finds that path in the index than it calls that function and expects the function to tell Drupal what to do next. Most of the time it will provide some content to built into the final page, but it may as well redirect to go to another page or present error message like access denied

Every module determines which paths are assigned to which call back functions and all that data is stored in db table ‘menu_router’

Simple Menu Callback

In your module file MODULENAME.module, add the following:

function MODULENAME_menu() {
    $items['pages'] = array(
        'title' => 'Menu system examples',
        'description' => 'Menu system example that returns a string.',
        'page callback' => 'register_member_page',
        'access callback' => TRUE,
    );

    return $items;
}

function register_member_page() {
    $build = array(
        'header_text' => array(
            '#type' => 'markup',
            '#markup' => '<p class="lead">' . t('Membership Registration') . '</p>',
        ),
       'example_form' => drupal_get_form('collect_member_info_form'),
    );
    return $build;
}

This is implementation of hook hook_menu(). The hook_menu() expects array of call back function(i.e. register_member_page) along other info. Here the Url path defined is ‘/pages’. The ‘page callback'(i.e.register_member_page()) is the call back function routed to this url – ‘/pages’ that will tell what to do next. The ‘access callback’ specifies permissions.

We can supply content to page request in 2 ways:
1. Through string containing html and text
2. Through the build array containing content in a structred array that can be later manipulated by other modules before rendered

Next implement the page callback function ‘pages_string’ that supplies the content as simple string(Nr.1)

function pages_string() {
    $output = '
    <p>Pages can be returned as strings.</p>
    <p>Pages can be returned as <em>render arrays</em>.</p>';
    $output .= theme('item_list', array(
            'title' => 'Render arrays are better because...',
            'items' => array(
                'They allow content to be modified as an array.',
                'Arrays are a lot easier to modify than HTML.',
            ))
    );

    return $output;
}

Here we structure the output as a string and use theme(‘item_list’…) function to help construct a list order ed or unordered with heading if you wish.

Next, make sure the module is installed and go to domainname.com/pages to verity it works

How to Use Render Arrays and Tabs

When callback function returns build array instead a string, then other modules can manipulate the content by adding/removing or reorder the elements in content before its rendered

function pages_menu() {
    $items['pages'] = array(
        'title' => 'Menu system examples',
        'description' => 'Menu system example that returns a string.',
        'page callback' => 'pages_string',
        'access callback' => TRUE,
    );
    $items['pages/default'] = array(
        'title' => 'String',
        'type' => MENU_DEFAULT_LOCAL_TASK,
        'weight' => -10,
    );
    $items['pages/render-array'] = array(
        'title' => 'Render array',
        'description' => 'Menu system example using a render array.',
        'page callback' => 'pages_render_array',
        'access arguments' => array('access content'),
        'weight' => 2,
        'type' => MENU_LOCAL_TASK,
    );

    return $items;
}

Here we taken the pages content page and added two tabs. The page ‘pages/default’ is the default tab by specifying the type to default. Important to note, that the default tab doesn’t have the page callback specified, so it falls back to the parent callback function – ‘pages_string’

Another tab(‘pages/render-array’) is added with new call back function ‘pages_render_array’. This tab has access arguments. Unless boolean is specified, it expects function. If no specified as it is here, then it uses default callback ‘user_access’ and it expect permission to be passed which we do here with ‘content access’

The build render array looks as following:

function pages_render_array() {
    $build = array(
        'string_paragraph' => array(
            '#type' => 'markup',
            '#markup' => '<p>Pages can be returned as strings.</p>',
        ),
        'render_array_paragraph' => array(
            '#type' => 'markup',
            '#markup' => '<p>Pages can be returned as <em>render arrays</em>.</p>',
        ),
        'why_render_arrays' => array(
            '#items' => array('They allow content to be modified as an array.', 'Arrays are a lot easier to modify than HTML.'),
            '#title' => 'Render arrays are better because...',
            '#theme' => 'item_list',
        ),
    );
    return $build;
}

Here we specify to element to be html strings and then the third is unorder list. This is being passed to theme() function ‘item_list’ as specified

Drupal caches menu registry, so you have to tell Drupal to rebuild the registry and you can do by clearing the cache resulting to rebuild

Sub-Tabs

function pages_menu() {
...
    $items['pages/render-array'] = array(
        'title' => 'Render array',
        'description' => 'Menu system example using a render array.',
        'page callback' => 'pages_render_array',
        'access arguments' => array('access content'),
        'weight' => 2,
        'type' => MENU_LOCAL_TASK,
    );
    $items['pages/render-array/tab1'] = array(
        'type' => MENU_DEFAULT_LOCAL_TASK,
        'title' => 'Tab 1',
    );
    $items['pages/render-array/tab2'] = array(
        'title' => 'Tab 2',
        'description' => 'Demonstrating secondary tabs.',
        'page callback' => 'pages_render_array',
        'access callback' => TRUE,
        'type' => MENU_LOCAL_TASK,
    );

    return $items;
}

Here we define two new items – ‘pages/render-array/tab1’ and ‘pages/render-array/tab2’ both of which are subpages of tab ‘pages/render-array’, so its important that the sub-tab path corresponds to the pages path its sub-tab from

How to Add Page Without Menu Item

function pages_menu() {
...
        $items['pages/callback'] = array(
        'title' => 'Example of a callback type',
        'page callback' => 'pages_render_array',
        'access callback' => TRUE,
        'type' => MENU_CALLBACK,
    );
...

Here we define separate page by specifying the type to be ‘MENU_CALLBACK’ instead ‘MENU_LOCAL_CALLBACK’. After clearing cash, the new page is displayed at domain.com/pages/callback end point

How to Pass Variables Through the Path

function pages_menu() {
...
 $items['pages/argument'] = array(
        'title' => 'Argument',
        'description' => 'Menu system example using an argument.',
        'page callback' => 'pages_argument',
        'access arguments' => array('access content'),
    );
}

function pages_argument($arg1) {
    $build['argument_paragraph'] = array(
        '#type' => 'markup',
        '#markup' => '<p>' . t('The argument passed was @arg1.', array('@arg1' => $arg1)) . '</p>',
    );

    return $build;
}

The only difference is to change the callback function to accept arguments. Here the callback function ‘pages_argument’ takes an arguments. So, with path domain.com/pages/argument/hello’, the argument ‘hello’ is going to be passed to the callback function ‘pages_argument’. For more arguments you just add them to the callback function parameter. Also can use the Drupals args() function

How To Use Placeholders to Pass Arguments in Middle of the Path

When you know the beginning and the end of the path but not the middle (i.e. node/*/edit)

function pages_menu() {
...
 $items['pages/%/placeholder'] = array(
        'title' => 'Placeholder',
        'description' => 'Menu system example using a placeholder.',
        'page callback' => 'pages_argument',
        'page arguments' => array(1),
        'access arguments' => array('access content'),
    );
}

There are two places to register the placeholder in hook_menu(). In the path, by specifying ‘%’ and ‘page arguments’ attributed as highlighted above. The number 1 specifies the location(starting from 0) of the path variable of placeholder. Afterwards, this variable is passed to the callback function

How To Create Dynamic Title With Title Callback

function pages_menu() {
...
  $items['pages/title/%'] = array(
        'description' => 'Example of a dynamic title.',
        'page callback' => 'pages_render_array',
        'access callback' => TRUE,
        'title callback' => 'pages_title_callback',
        'title arguments' => array(2),
    );
...
}

function pages_title_callback($arg) {
    return 'There is an argument in this title: ' . $arg;
}

Here, we specify the title callback and pass an argument. Afterward, we declare the callback that returns a string and is rendered as title

How To Modify Page Output with hook_page_alter()

How do adjust menu items that have been defined by other modules
How do we change the value returned by callback function defined by other modules

The idea of hook_page_alter() is that before content is rendered, modules have the last chance to manipulate the rendered page content.

function THEME_page_alter(&$page) {
    $page['content']['system_main']['why_render_arrays']['#weight'] = -10;
}

Here we reorder the array so that element item list named ‘why_render_arrays’ is moved up before other elements

How To Modify Menu Items With hook_menu_alter()

Instead modifying the render array, we modify the callback function all together. We will replace one page already exist with another we defined
Anything that is defined in hook_menu will be passed through the hook_menu_alter where it can be altered one more time

function pages_menu_alter(&$items) {
    $items['admin/content']['title'] = 'The Goods';
    $items['admin/content']['page callback'] = 'pages_render_array';
}

Here we override the admin page with our own defined page as declared in callback ‘pages_render_array’

How Define Path To Be Admin

Perhaps, you would like your new custom path to be part of Admin path whether its because you like to have the page in overlay or take advantage of the admin permissions, then:

function MODULE-NAME_admin_paths() {
    $paths = array(
        'path/*/edit' => TRUE,
    );
    return $paths;
}

here all the path of pattern – “‘path/*/edit” is going to be admin path with all the admin access rights, overlay functionality,etc.

How Use Include File To Improve Performance

On every page load, every .module file got loaded for every module currently installed. If we can make the code shorter in those .module files then we can improve the performance. One way doing it is by separating out the .module code into separate include files and then use special parameter in the hook_menu() item declaration goes in fetches that include file when needed. The idea, all of the code is not needed in the .module file at the same time.

function pages_menu() {
...
$items['pages/external'] = array(
        'title' => 'External file',
        'description' => 'Example of using an include for a page callback.',
        'page callback' => 'pages_external',
        'access callback' => TRUE,
        'file' => 'pages.external.inc',
        'file path' => drupal_get_path('module', 'NAMEOFMODULE'),
    );
...
}

Here, we specify the callback function ‘pages_external’ but the function is not in the .modules file. it is in the file ‘pages.external.inc’ that we specify to load in the highlighted lines. It will only include this file, when requesting ‘pages/external’

Blocks in Drupal

In this post, we cover how to programmatically create blocks in Drupal. To create manually block in Drupal, it has to be done in a separate module and cannot be done from theme space.

Add Block in Admin UI

To add block in admin ui(structure->blocks), add the following in the .module file:

function MODULENAME_block_info(){
    $blocks = array();

    $blocks['add_to_cart'] = array(
        'info' => t('Some Theme: Add to Cart Block'),//admin ui block name
    );

    return $blocks;
}

This will show the block in admin ui from which you can assign this block to particular region

Implementing Block

In the .module file add the following:

function MODULENAME_block_view($delta = ''){
    $blocks = array();
    switch($delta){
        case 'add_to_cart':{
            $blocks['subject'] = t('Add To Cart');
            $blocks['content'] = add_to_cart_block_contents($delta);
            break;
        }
    }
    return $blocks;
}

function add_to_cart_block_contents($delta){
    $output = '';
    switch($delta){
        case 'add_to_cart':{
            $output .= '<div> Add To Cart Content</div>';
            break;
        }
    }
    return $output;
}

Here, we add a block containing the string. If your block contains a lot of code, then you can override a template file in the theme or put in separate file and include it as following:

function add_to_cart_block_contents($delta){
...
        case 'add_to_cart':{
                        $block['content'] = theme_render_template(drupal_get_path('module', 'moduleName').'/inc/block--some-theme--contact-info.tpl.php', _config_contact_info());
            break;
        }
...
}

Here the content of block is put in the file ‘block–some-theme–contact-info.tpl.php’ and then rendered into the block content. The function _config_contact_info() is for passing the variables into the template file and returns array as following:

function _config_about_author(){
    return array(
        'title' => 'Contact Info',
        'content' => 'this is block content',
        'profile_photo' => '#',
    );
}

Here we are passing three variables – title, content and profile_photo into template file that is available as ‘$title’,’$content’ and ‘$profile_photo’
You can also return already rendered block instead array before rendered:

...
$block['content'] = array(
                 '#markup' => driver_config_content($delta),
              );
...

By putting in markup tag, it is already rendered content. This will not allow other module to alter/hook into your block

Include CSS and JS files

Sometimes your block will need Js or Css files, then you can include them as follows:

...
$blocks = array();
$blocks['content'] = array(
  '#markup' => mymodule_testblock_content(),
  '#attached' => array(
      'css' => array(
        drupal_get_path('module', 'mymodule') . '/css/mymodule.css',
      ),
      'js' => array(
        drupal_get_path('module', 'mymodule') . '/js/mymodule.js',
    ),
  ),
);
return blocks;
...

Adding a Form in the Block

Here is how to add the form:

function THEMENAME_block_view($delta = ''){
    $block = array();
    switch($delta){
        case 'add_to_cart':{
            $block['subject'] = t('Add To Cart');
            $block['content'] = drupal_get_form('fav_color_form');
            break;
        }
    }
}

function fav_color_form($form, &$form_submit){
    $form['color'] = array(
        '#title' => t('Favorite color'),
        '#type' => 'textfield',
        '#required' => TRUE,
        '#description' => t('What is your favorite color? Blue? No, wait-'),
    );
    $form['submit'] = array(
        '#type' => 'submit',
        '#value' => 'Submit',
    );
    return $form;
}

Here, we use drupal function ‘drupal_get_form’ to build the render array with an argument of name of function that constructs the render array

Pass and Access Variables

So if you want to pass a variable into the ‘variable’ array accessible in other parts (modules, themes, etc.) of the site, then:

function THEMENAME_block_view($delta = ''){
    $block = array();

    switch($delta){
        case 'add_to_cart':{
            $block['subject'] = t('Add To Cart');
            $block['some_product'] = t('Default product comes here');
            $block['default_product_price'] = some_default_product(drupal_set_page_content());
            $block['content'] = add_to_cart_block_contents($delta);
            break;
        }
}

Here, by setting $block[‘some_product’] value, it becomes available in the variables array in other parts of drupal
In case, you like to access the nodes variables in block, here we accomplish this by calling ‘drupal_set_page_contents()’ which returns variables array containing variables from the node, page and other levels

Retrieve Block To Render

Say, you want to render a block in particular place in some template file then you can retrieve and render any block as following:

...
<?php $block = block_load('MODULE_NAME','BLOCK_DELTA');?>
<?php $renderable_block =  _block_get_renderable_array(_block_render_blocks(array($block)));?>
<?php print render($renderable_block);?>

In first line, the block is loaded from module its declared. Next line converts it into renderable array before rendering in last line

Note: if the block utilizes template file that receives some variables then the above example will break because those variables are not passed to the template(see _block_get_renderable_array()). For that reason, consider using the content instead covered next.

The above example makes the block into renderable array, so you can update before rendering, however. If there is no need to make any changes, then just render content to avoid overhead of reengineering renderable array as following:

                $block_raw = block_load('MODULE-NAME', BLOCK-DELTA);
                $block_list = _block_render_blocks(array($block_raw));
                $renderable_block = array_shift($block_list);
                $variable['SOME-BLOCKE'] = render($renderable_block->content);

In line first, it loads the block. In the second line, it generates the content of the block. Afterwards, the content is extracted and rendered

Make Block Configurable

To make

function MODULE_NAME_block_configure($delta='') {
    $form = array();

    switch($delta) {
        case 'twitter_feed' :
            // Text field form element
            $form['builder_twitter_title'] = array(
                '#type' => 'textfield',
                '#prefix' => t('Title of Twitter Feed'),
                '#default_value' => variable_get('builder_twitter_title', ''),
            );
            $form['builder_twitter_invite'] = array(
                '#type' => 'textarea',
                '#rows' => '3',
                '#title' => t('Enter twitter invite description displayed next to tweets'),
                '#default_value' => variable_get('builder_twitter_invite', ''),
            );
            break;
...
return $form;
}

Here, two fields – builder_twitter_invite, builder_twitter_title is being added to the block with delta(ID) of twitter_feed’ edit form, so user can edit these fields

To process user input, function

 
function MODULE_NAME_block_save($delta = '', $edit = array()) {
    switch($delta) {
        case 'twitter_feed' :
            variable_set('builder_twitter_invite', $edit['builder_twitter_invite']);
            variable_set('builder_twitter_title', $edit['builder_twitter_title']);
            break;
...

Here, the user input is retrieved from the form and then saved into variables accordingly.

Troubleshooting

1. Notice: Undefined property: stdClass::$content in _block_get_renderable_array() (line 337 of

This was happening when calling “$ctx_plugin->block_get_blocks_by_region($region_key);”. The solution was cache the blocks before calling block_get_blocks_by_region($region_key) as following:

        $list = $ctx_plugin->block_list($region_key);
        _block_render_blocks($list);

        $ctx_blocks = $ctx_plugin->block_get_blocks_by_region($region_key);

This ensures that the ‘content’ is set before calling block_get_blocks_by_region solving the error

Useful Links

  • http://fourkitchens.com/blog/2012/07/18/building-custom-blocks-drupal-7
  • https://api.drupal.org/api/drupal/modules!block!block.api.php/function/hook_block_view/7
  • https://api.drupal.org/api/drupal/developer%21topics%21forms_api_reference.html/7#attached

Making Drupal Multilingual

This post covers steps to turn your site into multilingual site.

Installation

The following modules are required.

sudo drush dl l10n_update 
sudo drush en l10n_update 
sudo drush dl i18nviews
sudo drush en i18nviews
sudo drush dl variable
sudo drush en variable
sudo drush dl transliteration
sudo drush en transliteration
sudo drush dl i18n
sudo drush en i18n

Here, we use drush to download(dl) and enable(en) each module
The following modules also needed but you may already have it:

A quick way to verify:

drush pml | grep ctools 
drush pml | grep token 
drush pml | grep pathauto 
drush pml | grep views 

You can verify if all of the modules installed and enabled at Admin->modules:
1. Core
Contact
Locale
Content translation
2. Chaos tool suite
Chaos tools
3. Multilingual – Internationalization
Block languages
Contact translation
Internationalization
Menu translation
Multilingual content
Multilingual select
String translation
Synchronize translations
Taxonomy translation
Translation sets
Views translation
4. Other
Pathauto
Token
Transliteration
5. Variable
Variable
6. Views
Views
Views UI

1. Set Date and Time

The date is displayed different for different countries. To configure, go Admin->Configurations->Regional and language -> Data and Time and all the date formats your site is going to use

2. Add Languages

To add all of the different languages for your site by going to Admin->Configurations->Regional and language -> Languages and clicking ‘Add Language’

3. Configure Language Detection

There are several methods for your site to detect the language new visitor comes to your site. The most common is based on url(i.e. some-site.com/fr, some-site.com/lv). To configure your choice of method, go to admin->Configurations->Regional and language -> Languages -> Detection and Selection.

4. Add Language Switcher

The multilingual modules come with a block that enables user to switch the language. Incorporate the block in your site wherever you wish by going to Admin->Structor->Block and selecting the region for the Language switcher block

5. Turn on Content Types

Next, turn on multilingual support for each of the content by going to admin->structure->content types->’some type’. Then under ‘Publishing Options’ enable the multilingual support. You have to do this for all content types

6. Add Translation

Open to edit any of the content pages that you would like to translate. In the edit mode, there should be a tab – Translate. In the ‘Translate’ section, click on ‘Add Translation’

7. Make Admin Always One Language

By default, after you add translation to one of the content nodes, automatically the whole admin section is also in that language. To keep one default langauge for Admin section, install module ‘admin language’:

drush dl admin_language
drush en admin_language

Afterwards, configure the admin language under ‘Admin -> Configurations -> Region and Langauge -> Languages -> Admin Language’

Site Menu Altered for Multilingual Support

When multiple languages is enabled for a site and the language detection configured, then the main menu contains multiple links each different language for the same page. Here is custom code snippet from template.tpl.php to filter menu links specific to currently selected language:

    //load all menu links
    $mainMenuArray = menu_load_links('main-menu');
    $finalMainMenu = array();//array holding the final menu
    foreach($mainMenuArray as $key=>$menuItem){
        global $language;//retrieves all languages configured
        $currentLang = $language->language;
        if($menuItem['language'] == 'und'){
//for default language still set the language code
            $menuItem['language'] = language_default('language');
        }
//filter all menu links of currently selected language
        if($currentLang == $menuItem['language'] ){
            $linkPath = $menuItem['link_path'];
            $linkPathAlias = drupal_get_path_alias($linkPath, $currentLang);
            $currentUrl = drupal_get_path_alias($_GET['q'],$currentLang);

            if($currentUrl == $linkPathAlias){
//decorate the link of currently selected page
                $finalMainMenu[$key]['active'] = 'muted-img';
            }
            $finalMainMenu[$key]['url'] = $linkPathAlias;
            $finalMainMenu[$key]['title'] = $menuItem['link_title'];
        }
    }
    $vars['mainMenu'] = $finalMainMenu;

Here all links of the main menu is loaded including different language then traveled through and only set ones that are of the selected langauge

XML Sitemap for Each Language

Make user to generate XML sitemap for each language. Go to Admin->Configurations->Search And Metadate -> Xml Sitemap and click ‘Add New XML Sitemap’ to generate a new sitemap

Useful Sources

https://drupal.org/node/1268692

Drupal Commerce Install and Setup

Before installing, here is a list of modules required for Drupal Commerce module:

Installing and Enabling Drupal Commerce

We use Drush to install and enable commerce module as following:

sudo drush dl rules
sudo drush dl views
sudo drush dl entity
sudo drush dl addressfield
sudo drush en rules views entity addressfield
sudo drush dl commerce
sudo drush en commerce commerce_ui commerce_product commerce_product_ui commerce_product_reference commerce_cart commerce_customer_ui commerce_line_item_ui commerce_order_ui commerce_payment commerce_payment_ui commerce_product_pricing_ui commerce_product_pricing_ui commerce_tax commerce_tax_ui

To check which modules are enabled and which not but,perhaps, need to be, run the following:

drush pml | grep commerce

This will display all modules associated with drupal commerce along the status if enabled or not

Summary of all commerce modules:

commerce_tax, commerce_tax_ui, commerce_product_reference, commerce_product_pricing, commerce_product_pricing_ui, commerce_product, commerce_product_ui, commerce_price, commerce_payment_example, commerce_payment, commerce_payment_ui, commerce_order, commerce_order_ui, commerce_line_item, commerce_line_item_ui, commerce_customer, commerce_customer_ui, commerce_checkout, commerce_cart, commerce, commerce_ui.

Overview

In the following are steps to set up commerce functionality in the Drupal site:

1. Add Product Type
2. Add Taxanomy

Each taxanomy element is an product attribute such as size, color, etc. Later in the product display page, user will be able to select product attribute and that product will be displayed available to added in shopping cart a functionality that comes with the commerce module out of the box.

3. Add Content Types

This is a Content Type for displaying each product individually

Step-1: Creating Product Types

Go to ‘Admin/Store/Products/Product Types’ and add new product type such as ‘themes’. We are going to sell Drupal themes just like in our store Designs Square

Step-2: Adding Attributes

In order to add attributes to products, we first create these attributes by creating taxanomies for each attribute and adding items that are the attribute values. To add the taxanomies go to ‘Admin/Structure/Taxonomy/Add Taxonomy’

Step-3: Attach Attributes to Product

Go to Product Type(such as ‘theme’) and in ‘manage fields’ section, add a new field with type of ‘Term Reference’. Afterwards, specifying which taxonomy reference is this. Now your product have a product attribute.

!!!make sure select ‘Enable this field to function as an attribute field on Add to Cart forms’ under the ‘Attribute Field Settings’ section, so that when user selects this attribute in the display product page, the product with the particular attributed is displayed and available to add in shopping cart

Step-4: Add Products

To add a product, go to ‘Admin/Store/Products/Add Product’ and select the type of product(i.e. ‘theme’). Do all variations as each separate product

Step-5: Add Displays

1. Create Content Type(for example, ‘Product Display’)
To create Content Type, go to ‘Admin/Structure/Content Types/Add Content Type’ and type ‘Product Display’. Afterwards, add new field with type of ‘Product Reference’ field and select all the product types related to the display (in our case, we have only one product type – ‘theme’ )

2. Add Display for Each Product
Next, we add all of the products each at the time by going to ‘Admin/Content/Add Content/Product Display’, and create a product display(for example, Drupal Newspaper Theme – Newscast). Afterwards, select associated products under the ‘Products Reference’ select box (for example, for the Drupal Newspaper Theme – Newscast we have ‘Basic Light Newscast Theme’, ‘Plus Dark Newscast Theme’,etc )

Test Drive

Without the Payment System configured, you should be able to test it. If your page.tpl.php is customized, make sure you have the default page.tpl.php for the following templates files:

  • page–checkout.tpl.php
  • page–cart.tpl.php
  • page–YOUR-DISPLAY-TYPE.tpl.php

Go to your product display page(Admin/Content/Product_Name) and walk through the checkout process

Configuring Payment System

Required Modules: commerce_payment, commerce_payment_ui
!Consider enabling payments example(commerce_payment_example)

Payment Method 1 – PayPal
A. Set up PayPal account at developer.paypal.com
B. Install PayPal module as follows:

sudo drush dl commerce_paypal
sudo drush en commerce_paypal
sudo drush en commerce_paypal_ec
sudo drush en commerce_paypal_wps

C. Configure payment method. (store->configuration-payement methods)
a) Click ‘enable’
b) Click ‘edit’ and add the email under Payment Settings
!To turn on live, just select radio button for ‘live’
Payment Method 2 – Authorize.Net
1. Create Test Account with Authorize.net
2. Install Module ‘commerce_authnet’
3. following the same steps as configuring PayPal Payment method
Payment Method 3 – Stripe (Our Favorite)
1.create account
2. install drupal module ‘commerce_stripe’
3. Download strip-php lib and put it in sites/all/libraries/stripe-php
4. configuring Payment method
5. Enable Permissions for the not authenticated visitors. Otherwise, the ‘check out’ button will not show up
To see cards available for test see https://stripe.com/docs/testing

Taxes

Required Modules – commerce_tax, commerce_tax_ui

sudo drush dl commerce_tax
sudo drush en commerce_tax
sudo drush dl commerce_tax_ui
sudo drush en commerce_tax_ui

To add tax rate, go to ‘Admin/Store/Configuration/Taxes/Add tax rate’
Make sure the Rate is in decimal format
Drupal comes with two tax types – Sales and VAT each describing when applied
The ‘Tax rates’ deals with how much of the taxes
The taxes automatically show up at the checkout.

Order

All about orders are in ‘Admin/Store/Orders’
! You can add fields to the orders. To do so go to ‘Admin/Store/Configurations/Orders Settings’

Other

Commerce module also adds the following entities:

  • customer profiles
  • products
  • line items
  • payment transactions

Things to know about Line Items

  • show up in order
  • none taxable
  • cannot create but it is created by modules installed
  • To view line items, go to ‘Admin/Store/Configuration/line items’

Things about Rules

  • example – send email to customer after checkout
  • Good for applying taxes on certain location using rules see Lynda.com

To Set limits on products use Commerce_Stock and go to ‘Admin/Store/Configuration/Stock management’. Once enabled all products, it will have additional field to specify size

Setting Up Email To Notify When Order Received
Go to ‘Admin/Configuration/Workflow/Rules/Send an order notification e-mail/Add New Action/Send Mail’. This will send email whenever an new order is made

Each purchase create customer profile a snapshot of time. good for collecting additional info.

Commerce Product Display Manager Module
To better manage views/admin product displays. See more at https://drupal.org/project/commerce_product_display_manager

Commerce VBO Views
Edit/Create/administer products

Issues

1. the ‘attribute field setting’ is missing for the products field with ‘term reference’
Make sure the module ‘commerce_cart’ is enabled

Useful Links

  • http://www.youtube.com/watch?v=gLO7nt48omE&list=UUwY8NJJnQtMs1NBbOTvHdmQ
  • http://www.slideshare.net/willhall/things-i-wish-i-knew-about-drupal-commerce-12546226

Module Development in Drupal

In this post, we cover different aspects for developing modules in Drupal including creating modules, setting variables and other

Creating Module

Modules location

  • The /modules folder that is reserved for core is not good location for your custom modules
  • The good location is /sites/all/modules folder to keep the contributed and custom modules(/sites/all/modules/contr or /sites/all/modules/custom)

Modules Naming

  • keep lower case
  • its not good practice to include numbers

->the modules description along the name and core version is stored in .info files as following:

name = Sample Module
description = sample module description
core = 7.x
version = 7.x-0.1-dev ;to show up in the admin ui
package = Block Wrapper
files[] = nameOfModule.module
dependencies[] = module1
configure = admin/config/moduleName ;this adds configuration button next to module name in modules list

In order to show up in core admin UI, need .module file. It can be empty but required. Documentation can be included

/**
*@file
*Custom functionally for your custom module
*/

Try, clear cache and check to install the module from admin/modules interface

Create Module Admin Interface

Once installed, next we create an admin UI interface to be able edit configurations and settings of the custom module. To do so, we are going to use hook_config_menu:

function moduleName_config_menu(){
    $items = array();

    //Admin config group
    $items['admin/config/kapasoft'] = array(
        'title' => 'Kapasoft',
        'description' => 'Configure Interface with Driver',
        'access arguments' => array('administer kapasoft configurations'),
    );

    //admin configuraiton - settings
    $items['admin/config/kapasoft/manage'] = array(
        'title' => 'Kapasoft configuration',
        'description' => 'Manage Kapasoft Interface config settings',
        'access arguments' => array('administer kapasoft configurations'),
        'page callback' => 'drupal_get_form',
        'page arguments' => array('kapasoft_config_form'),
    );

    return $items;
}

Here we create the module’s admin page – ‘manage’ in admin UI section ‘admin/config/kapasoft’. Next, clear the cache and see if it appears in the admin menu

You should see an error, because in the ‘page callback’ we specify function that builds a form but its not yet declared. Lets do it now

Admin Menu Form

Here, we create a form displayed in admin UI for our module to manipulate settings. In last step, we specified the function(i.e kapasoft_config_form) that builds the form. Now, we implement it as follows:

function kapasoft_config_form($nodes, &$form_state){
    $form = array();

    $form['overview'] = array(
        '#markup' => t('This interface allows to manage KapaSoft modules Settings'),
        '#prefix' => '<p>',
        '#suffix' => '</p>',
    );

    $form['interface_config'] = array(
        '#title' => t('Interface Config'),
        '#description' => t('Configurations of Driver'),
        '#type' => 'fieldset',
        '#collapsible' => TRUE,
        '#collapsed' => FALSE,
    );

    $form['interface_config']['driver_url'] = array(
        '#title' => t('Driver Url'),
        '#description' => t('url of the driver'),
        '#type' => 'textfield',
        '#default_value' => 'localhost',
        '#required' => TRUE,
    );

    $form['interface_config']['driver_port'] = array(
        '#title' => t('Driver Port'),
        '#description' => t('port of the driver'),
        '#type' => 'textfield',
        '#default_value' => '3000',
        '#required' => TRUE,
    );

    $form['submit'] = array(
        '#type' => 'submit',
        '#value' => t('Save'),
    );
    return $form;
}

Here we add two fields and set default values. Afterwards, clear the cache and go to the admin UI (admin/config/kapasoft/manage) to see the form with default settings.

Make Data Persistent

One of the ways Drupal provides ability to persist almost any type of data are through the variable_get, variable_set and variable_del API. Good for settings but shouldn’t be used for content

Lets, adjust our default values:

function kapasoft_config_settings_form($nodes, &$form_state){
...
 $form['interface_config']['driver_url'] = array(
        '#title' => t('Driver Url'),
        '#description' => t('url of the driver'),
        '#type' => 'textfield',
        '#default_value' => variable_get('kapasoft_driver_url', 'localhost'),
        '#required' => TRUE,
    );

    $form['interface_config']['driver_port'] = array(
        '#title' => t('Driver Port'),
        '#description' => t('port of the driver'),
        '#type' => 'textfield',
        '#default_value' => variable_get('kapasoft_driver_port', 3000),
        '#required' => TRUE,
    );
}

In the highlighted lines, the default value is retrieved from variable. If the variables hasn’t been set,yet, then it sets to the default values as specified(i.e. ‘locahlost’, 3000))

At submission, lets save the new variables as well:

function kapasoft_config_form_submit($form, &$form_state){
    //rebuild the form
    $form_state['rebuild'] = TRUE;

    //Save kapasoft setting variables
    variable_set('kapasoft_driver_url', $form_state['values']['driver_url']);
    variable_set('kapasoft_driver_port', $form_state['values']['driver_port']);
 
    //notify user
    drupal_set_message(t('kapasoft driver settings are saved'));
}

At form submission, the values from the form is taken and saved using the variable_set API

Variable_get With Defaults

So far as implemented, it only works with the form. If the form is not submitted, variables are not saved.

In addition, defaults are set in dozen places that don’t scale. In another words, we would have to edit code for each instance of variable_get/variable_set if the default value changes

So, we going to use hook_install to set the default variables once at the time module is installed. This hook is placed in moduleName.install file as following:

function kapasoft_config_install(){
    //set default variables
    variable_set('kapasoft_driver_url', 'localhost');
    variable_set('kapasoft_driver_port', '3000');

    //get localization function for installation as t() may not be available
    $t = get_t();

    //give user feedback
    drupal_set_message($t('Kapasoft driver configurations created'));
}

function kapasoft_config_uninstall(){
    //Delete variables
    variable_del('kapasoft_driver_url');
    variable_del('kapasoft_driver_url');
    
    //get localization function for installation as t() may not be available
    $t = get_t();
    
    //give user feedback
    drupal_set_message($t('Kapasoft driver configurations removed'));    
}

Next, remove all default values from moduleName.module file, so there is single point of initialization for defaults in the moduleName.install file(i.e. make variable_get(‘kapasoft_driver_url’, 3000) to variable_get(‘kapasoft_driver_url’))

Adding Configuration Button

To add a configuration button next to module name in the modules list, add the following line in the modulesName.info file:

...
configure = admin/config/kapasoft/manage

Here the path ‘admin/config/kapasoft/manage’ is the path specified in the moduleName_config_menu() function above

Shorten Code

We can shorten code by completely removing submit function(i.e kapasoft_config_settings_form_submit()) and let the Drupal built in mechanism handle our form submission by updating the form build function as follows:

function kapasoft_config_form($nodes, &$form_state){
...
//    $form['submit'] = array(
//        '#type' => 'submit',
//        '#value' => t('Save'),
//    );

    return system_settings_form($form);
}

As you see, we uncomment the submit button and return Drupal function ‘system_settings_form() with an argument that contains our admin form. This will handle the submission, save the variables and display the message to user. Note, make sure the variable names are the same as specified in form.

Load Other Modules

To utilize other modules within your module, all you need is to load it as following:

module_load_include('inc', 'location', 'earth');
module_load_include('inc', 'gmap3_tools');

This loads the ‘location’ and gmap3_tools modules. Afterwards, you are able to use any of their modules API

How To add Content Type

Here is a great article Creating Custom Content Type…

How to add Block

see post Blocks in Drupal

Troubleshooting

1. Form structure and values
When implementing form validation, it may be useful to print out the form structure

function bazar_node_form_validate($form, &$form_state){
    //to see what validated - the structure
    dpm($form_state);
    //purposely set error
    form_set_error('','testing');

The ‘bazar_node_form_validation’ is custom form validation function added to the list of function names that are used to validate the form in hook_form_alter:

/**
 * implements hook_form_alter()
 */
function bazar_form_alter(&$form, &$form_state, $form_id){
    //like to add validation but i don't know the form id
    dpm($form_id);
    //i would like to see the structure of the form
    dpm($form);
    //return to browser, clear craches and go to form

    //Afterwards, we can turn on debuging only for our form of interest. From the form id we can figure out the validation callback function to be bazar_node_form_validate
    switch($form_id){
        case 'bazar_node_form':{
            //dpm($form);
            //look for #validate that takes list of function names that are used to validate the form. I can ad custom funciton to this array
            $form['#validate'][] = 'bazar_node_form_validate';
            break;
        }
    }
}

Here, we are adding custom validation function ‘bazar_node_form_validate’ to the form with id ‘bazar_node_form’

2. Verify variables loaded
Here is an example to find out what variables are loaded for your theme function(the theme hook specifies theme funciotn)

function theme_ThemeName_gmap($variables){
    dpm($variables);
}

This will print out all the variables passed to this theme. The theme funciton ‘theme_ThemeName_gmap’ was specified in the theme_hook

Useful Links

  • https://api.drupal.org/api/drupal/

Interacting with Redis Cheat Sheet

Redis is elegant solution to provide fast storage while its supported by Node.js. However, with its different data structures such as List, Set and Hashes, the API of Redis takes time to memorize. Here is quick cheat sheet to help

Installation

Step 1 – Download & Compile

To install Redis:

wget http://download.redis.io/redis-stable.tar.gz
tar xvzf redis-stable.tar.gz
cd redis-stable
make

Here, we installed Stable version of Redis, but if you need some older version then see Google Code Redis repo for version of your need

Step 2 – Test It

Try if your build works correctly by typing “make test”

Step 3 – Configure Redis

After the compilation the src directory inside the Redis distribution is populated with the different executables that are part of Redis:

  • redis-server is the Redis Server itself.
  • redis-sentinel is the Redis Sentinel executable (monitoring and failover).
  • redis-cli is the command line interface utility to talk with Redis.
  • redis-benchmark is used to check Redis performances.
  • redis-check-aof and redis-check-dump are useful in the rare event of corrupted data files.

It is a good idea to copy both the Redis server and the command line interface in proper places, either manually using the following commands:

  • sudo cp src/redis-server /usr/local/bin/
  • sudo cp src/redis-cli /usr/local/bin/

Or just using:

 cd src/
 make install.

NOTE: We assume that /usr/local/bin is in your PATH environment variable so that you can execute both the binaries without specifying the full path.

Starting Redis

To start redis with default configurations:

redis-server

Or if you like to start with custom settings then pass them in at the start up as following:

redis-server /etc/redis.conf

You should use the redis.conf file included in the root directory of the Redis source code distribution as a template to write your configuration file.

Testing Redis

Redis provides a command line utility “redis-cli” that can be used to send commands to Redis. To check if Redis is working properly is sending a PING command using redis-cli

redis-cli ping

This should respond with “PONG” if Redis working properly

To see all options with redis-cli utility such as running different port,host, etc:

redis-cli --help

To start Redis in interactive mode so you can type different commands and see their replies.

$ redis-cli                                                                
redis 127.0.0.1:6379> ping
PONG
redis 127.0.0.1:6379> set mykey somevalue
OK
redis 127.0.0.1:6379> get mykey
"somevalue"

If able set values, then Redis is working properly and your are ready to next step

Connecting And Other

Command Redis Node.js
CREATE CONNECTION
 redis-cli
db = redis.createClient();
CLOSE CONNECTION
 exit 
db.end
FLUSH DATABASE
 FLUSHALL 
db.flushdb(function (err, succes) {
...
}

Hashes

Purpose: good for saving objects that have properties
For example, we have object review ->[id:1,title:’some title’,…] that contains properties and values

Command Redis Node.js
SET
Single Attribute
 HSET myhash field1 "Hello"
db.hset('cache:review:1', 
   {id: '1', title: 'some title'}, 
   function(err, args){..})
GET
Singe Attributes
 HGET myhash field1
???
SET
Multiple Attributes
 HMSET myhash 
   field1 "Hello" 
   field2 "World"
db.hmset('review:2', 
   this, 
   function(err, args){..})
GET
Multiple Attributes
 HMGET myhash field1 field2 
db.hmget('cache:review:6', 
   'remote', 
   'local', 
   function(err, args){..})
LIST ALL
attribute = values
HGETALL myhash
db.hgetall('review:3',
    function(err,review){..}
LIST ALL
ONLY values
 HVALS myhash 
???
DELETE
 HDEL myhash field2 
db.del('review:2')

Sorted Set – non repeating collections of Strings

Purpose: Its good to keeping track of Objects Stored in Redis.
For example, we have set ‘Reviews’ -> [1,3,4,6] that contains all IDs of each review saved

Command Redis Node.js
ADD
ZADD myzset 1 "one"
db.zadd('myzset', 1, "one", function(err, args){
        if(err) {...});
LIST
 ZRANGE myzset 0 -1
db.zrange('reviews', 
  from, 
  to, 
  callBackFunc)
DELETE
 ZREM myzset "two"
db.zrem('Å—eviews', 
  this.id)

Lists

coming soon…

Security

Securing from CLI

Through command line you can setup security as follows:

redis-cli //to login redis cli
>CONFIG get requirepass //see if security & password setup already
1) "requirepass"
2) ""
>CONFIG set requirepass "MY_PASSWORD" //sets up password
OK
>AUTH "MY_PASSWORD" //this will test your new password
OK
>exit

This will setup your security with password

Securing from Configuration File

Instead of command line, the security can be configure via redis-config file as follows:

Either case the security is configured, once configured the apps need to authenticate. Here is sample from NodeJs app

     redis_client = redis.createClient();
     redis_client.port = port;
     redis_client.host = host;
     redis_client.auth('PASSWORD');

The highlighted line, authenticates the connection via the password provided in the code

Maintenance

To see Redis logs:

cat /var/log/redis_6379.log

To stop Redis server manually on Ubuntu:

/etc/init.d/redis-server restart

–OR–

redis-cli shutdown

–OR–

ps -ef | grep -i 'redis-server'
kill -9 PID owned by redis

To find version of Redis and other info:

redis-cli
>INFO

So, we login first Redis command line and then execute “INFO” to see info about current version of Redis

Useful Links

  • https://github.com/mranney/node_redis
  • http://redis.io/topics/data-types-intro
  • http://redis.io/topics/quickstart

Converting to CoffeeScript run on Node.js

JavaScript is great language but it has those parts like inheritance and manipulations of data, that can slow developer down. The CoffeScript is one of the solutions. In addition, the CoffeeScript also adds the syntax sugar to write code faster and cleaner. It similar in a way how Groovy complements Java in many ways

Hello World Example

Lets create hello.coffee as following:

introduction = (x) ->
	"Hello " + x

console.log introduction("World")

Installing in Node

To run the CoffeeScript from node, it’s needs to be installed as following:

npm install -g coffee-script

This will install CoffeeSript globally

Running CoffeeScript

To run CoffeeScript script:

coffee hello.coffee

This compiles the hello.coffee into javascript and then Node interprets to execute it
To see how the CoffeeScript code looks in JavaScript, compile it as:

coffee -c hello.coffee

This will compile the coffee script into js file hello.js containing the JS code of the script. This works well for debugging not only for reviewing code but also running a debugger with breakpoints

Embed JavasCript in CoffeeScript

There may be times, you would like to run JS within the CoffeeScript code then:

`var introduction = function(name){
   return "Hello " + name;
};`

console.log introduction("World")

As in the example, by adding (`) around the js code, you can embed the JS into CoffeeScript

Importing CoffeeScript in Node.js

Node.js uses ‘require’ to import modules and other JS code into the current code. If the module or code that need to be imported via the ‘require’ is CoffeeScript, then one way would be to compile the CoffeScript file. It would produce the JS file that will be included, however. To manually compile every single time the change is made in the CoffeeScript code, would not be productive. Solution is adding ‘require(“coffee-script”);’ at the top as following:

require('coffee-script');
var should = require('should')
    , Domain = require('../index');
...

In this example, the ‘../index’ is referencing a ‘index.coffee’ script. So by including ‘require(“coffee-script”)’ at the top, Node will compile the CoffeeScript for you before importing it.

Useful Links

  • http://coffeescriptcookbook.com/chapters/classes_and_objects/class-methods-and-instance-methods
  • http://coffeescript.org/#classes
  • http://stackoverflow.com/questions/4679782/can-i-use-coffeescript-instead-of-js-for-node-js
  • http://vimeo.com/18429839

Node.js Dev Modules Nice to Have

Here is a list of modules found particular useful for developing in Node.js. Those are installed globally as following:

sudo npm install nameOfModule -g

List of useful Node.js modules nice to have:

  • ‘node-dev’ —-is module that reruns your script any time change is made
  • ‘optimist’ —reads command arguments and runs in script
  • ‘readline’ —gives a way to read user inputs interactive way
  • ‘jquery’ –module let you run query from command line and parse the content
  • ‘jshint’ –run any JS file to see if there is any errors ‘jshint file.js’
  • ‘forever’ –makes it possible for Node.js run in background and it restarts automatically if error happened
  • ‘mocha’ –test framework of our choice
  • nodemon‘ –file monitor for node.js applications by Remy Sharp. It wraps your node.js application and then sits monitoring a folder, where you source files are located. As soon as it detects a change, it re-launches your application.

Starting with Mocha for Testing Node.js Modules

Mocha is our choice of testing framework for Node.js modules. We specially like the ease it provides for testing asynchronous executions that gives the Node.js its powers but may be challenging when testing. In addition, the ability to write and run tests with the TTD interface was another factor when choosing Mocha. In this post, we cover installation, configuration and writing tests with Mocha.

1. Install and Declare Dependency
To install:

sudo npm install mocha

This install the mocha module locally. Add ‘-g’ tag if you would like to be installed globally.

Afterwards, declare the dependency, so other projects consuming this module can run the tests by adding the following in ./package.json

...
 "dependencies": {
...
     "mocha": "~1.10.0"
}

2. Configure Mocha interface, etc
There are two ways configure Mocha
a)Manually by editing ./test/mocha.opts file

--require should
--reporter spec
--ui tdd

Here we specify Mocha to include ‘should’ module for the tests. We also specify the reporting and ui interface. We found the reporting type of ‘spec’ to be most descriptive while just enough verbose. The ‘–ui’ specifies the interface to be ‘TDD’ for writing tests.

b)Configure via Command
Alternative is to configure Mocha testing framework via command line:

mocha --ui tdd
mocha --reporter spec
mocha --require should

3. Enable Async Testing
Mocha makes it simple for testing async execution. All you have to do is pass paramater ‘done’ to the test function and then call ‘done()’ afterwards as following:

suite(...
...
test('ensure its saved in redis',function(done){
            db.hgetall('review:1', function(err, redisReview){
                if(err){
                    console.log('error when retrieving value');
                    done();
                }
                redisReview.should.have.property('id');
                redisReview.should.have.property('attOne');
                redisReview.should.have.property('attTwo');
                done();
            });
        });
...
);

The ‘done’ is the Mocha callback function. In the first highlighted line, it is passed into the test. Afterwards, it is called to let Mocha know the particular async process has completed

3. Running Test in CoffeeScript
If your module is developed in CoffeeScript instead JavaScript, then the first line of the test file has to include CoffeeScript as following:

require('coffee-script');
...

This is common practice for running CoffeeScript code in Node.js not only for Mocha test framework
Make sure to install CoffeeScript module and declare it as the dependency.

4. Running With “–watch” Parameter
if you want to run the test whenever the changes is made, then use the ‘–watch’ tag

Setup vs SuiteSetup
For TDD interface, the ‘Setup’ block is executed multiple times for each test declared. If you need to have only one time setup then ‘SuiteSetup’ is the solution. The same applies for teardown() and suiteTearDown()

Issues:

1. ReferenceError: suite is not defined
a)Make sure the proper TDD Interface has been configured mocha.opts(step-2)

2. ReferenceError: assert is not defined
It seems that configuring to import ‘assert’ from the configuration file doesn’t work. Fix is in the test file include it manually as following:

var assert = require('assert');

Useful Links

  • http://visionmedia.github.io/mocha/#mocha.opts
  • http://stackoverflow.com/questions/9795254/nodejs-mocha-suite-is-not-defined-error
  • http://net.tutsplus.com/tutorials/javascript-ajax/better-coffeescript-testing-with-mocha/
  • http://stackoverflow.com/questions/12159846/testing-asynchronous-function-with-mocha
  • http://codebetter.com/glennblock/2013/01/17/debugging-mocha-unit-tests-with-webstorm-step-by-step/