Alter user profile page password fields in Drupal 7.x

The request seems simple, change the password field title in user edit form to “New Password” (or add placeholder etc)

From:

to:

Try to alter user_profile_form but no luck, there is a form element in <?php $form['account']['pass']['#type'] = 'password_confirm';?>
Don’t have #title to change

And actually ‘password_confirm’ is rendered in form_process_password_confirm()

with no alter available.

So I took another route in form API #pre_render

<?php
function hook_form_user_profile_form_alter(&$form, &$form_state) {
 
$form['#pre_render'][] = 'hook_form_user_profile_form_pre_render';
}
function
hook_form_user_profile_form_pre_render($elements) {
 
$elements['account']['pass']['pass1']['#title'] = t('New password');
 
$elements['account']['pass']['pass2']['#title'] = t('Confirm new password');
  return
$elements;
}
?>

Drupal commerce - Add to cart anonymously, programmatically


Feature: add an item to the shopping cart of every visitor so they can checkout immediately.

Actually a shopping cart can belongs to anonymous, linked by session ID
Commerce kickstart had already demonstrate such behaviour.

But the problem is,

<?php
 commerce_order_new
(0);
?>
do not actually link the cart with anonymous session

All you need is :

<?php
 commerce_cart_order_session_save
($order->order_id);
?>
AttachmentSize
Image icon download.png11.64 KB

Create Drupal commerce order programatically


Drupal commerce had become the major implementation of all-in-one ecommerce solution, from ubercart.

Ubercart was long from Drupal5.x era
To work with entities that build-in Drupal7, and also make Drupal's commerce solution brand-less, Drupal commerce was born.

But Drupal commerce, due to its large code base and dependence on third part entities modules, had been broken into more modules,
With lots of dependencies and make it difficult to just get start and get a feel of it.
To counter this, Drupal commerce kickstart is packaged as an installation profile, which make it tons more easier to start a shopping site from scratch.
You may just download, install and import sample products, you will have a working shopping site in 10 minutes!

Development side, a similar request as before, to create orders programmatically by getting a product first:

<?php
$cp
= commerce_product_new($type);
$cp->is_new = TRUE;
$cp->revision_id = NULL;
$cp->uid = $user->uid;
$cp->status = 1;
$cp->language = LANGUAGE_NONE;   
$cp->created = $cp->changed = time();
$cp->sku = 'shirt_od' . $extras['title'] . drupal_hash_base64(microtime());
$cp->title = $cp->sku;
?>

We will use field_attach_submit() to save the product, so other hooks and rules will be triggered as well:

<?php
$price
= array(LANGUAGE_NONE => array(0 => array(
   
'amount' => $price * 100,
   
'currency_code' => commerce_default_currency(),
)));
$form_state['values']['commerce_price'] = $price;

// custom fields
$form_state['values']['field_quantity']     = array(LANGUAGE_NONE => array(0 => array('value' => $quantity)));
$unit_price = array(LANGUAGE_NONE => array(0 => array(
   
'amount' => $unit_price * 100,
   
'currency_code' => commerce_default_currency(),
)));
$form_state['values']['field_unit_price']     = $unit_price;
field_attach_submit('commerce_product', $cp, $form, $form_state);

commerce_product_save($cp);
?>

About the cart:

<?php
$order
= commerce_order_new($uid, 'cart');
commerce_order_save($order);

$line_item = commerce_product_line_item_new($cp, 1, $order->order_id);
// Save the line item to get its ID.
commerce_line_item_save($line_item);

// Add the line item to the order using fago's rockin' wrapper.
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$order_wrapper->commerce_line_items[] = $line_item;

// Save the order again to update its line item reference field.
commerce_order_save($order);
?>

Drupal feeds module: FeedsProcessor with ubercart Order

Use case: need to import batch ubercart orders from a CSV

Feeds module have a CSV parser already, have batch process build in, can process a large CSV already, and proven code. The most import, there are already 4 type of content that can be import (nodes, users, terms, feednode?), so likely there are other ways to import new content as well.

Start from “The developer's guide to Feeds”: (https://www.drupal.org/node/622700)
Everything is a plugin: fetcher, parser and processor

  • Fetcher is “the way to obtain source data”, feeds module already have file upload fetcher for the use case.
  • Parser is “how to read the source”, feeds module already have CSV parser
  • Processer’s job is to convert well formatted data into Drupal content.

So we create a new module for this feeds plugin:
ubercart_order_feeds.module:

<?php
/**
 * Implementation of hook_feeds_plugins().
 * http://drupalcontrib.org/api/drupal/contributions%21feeds%21feeds.api.php/function/hook_feeds_plugins/6
 */
function ubercart_order_feeds_feeds_plugins() {
 
$path = drupal_get_path('module', 'ubercart_order_feeds');
 
$info = array();
 
$info['FeedsOrderProcessor'] = array(
   
'name' => 'Order processor',
   
'description' => 'Create orders.',
   
'help' => 'Create orders from parsed content.',
   
'handler' => array(
     
'parent' => 'FeedsProcessor',
     
'class' => 'FeedsOrderProcessor',
     
'file' => 'FeedsOrderProcessor.inc',
     
'path' => $path,
    ),
  );
  return
$info;
}
?>

Create a new file FeedsOrderProcessor.inc:
<?php
class FeedsOrderProcessor extends FeedsProcessor {
}
?>

First, define a list of fields that may be passed in by CSV in getMappingTargets():

<?php
 
/**
   * Return available mapping targets.
   */
 
public function getMappingTargets() {
   
$targets = array(
     
'sku' => array(
       
'name' => t('Product SKU'),
       
'description' => t('SKU of the product.'),
      ),
     
'quantity' => array(
       
'name' => t('Quantity'),
       
'description' => t(''),
      ),
     
'cost' => array(
       
'name' => t('Cost'),
       
'description' => t(''),
      ),
     
'delivery_first_name' => array(
       
'name' => t('Delivery: First name'),
       
'description' => t(''),
      ),
     
'delivery_last_name' => array(
       
'name' => t('Delivery: Last name'),
       
'description' => t(''),
      ),
   
//....
?>

which the key of the array will be used again to obtain the CSV value and name will be used in mappings admin page.

I had dump a native order object to find out the default attributes and use exact keys to save me some code mapping hazel. (i.e. delivery_first_name, delivery_last_name above and more)

process() method is the main entry point, and copied some code from NodeProcessor:

<?php
 
public function process(FeedsImportBatch $batch, FeedsSource $source) {
   
// Count number of created and updated nodes.
   
$created  = $updated = $failed = 0;

    while (
$item = $batch->shiftItem()) {
     
// Map item to a term.
     
$order = $this->map($batch);
     
//.......
?>

At this point a new function map() is defined, and should return an empty ubercart order object:

<?php
 
/**
   * Execute mapping on an item.
   */
 
protected function map(FeedsImportBatch $batch, $target_order = NULL) {
   
// Prepare user account object.
   
if (empty($target_order)) {
     
$order = uc_order_new($user->uid, 'completed');
    }

   
// Have parent class do the iterating.
   
return parent::map($batch, $order);
  }
?>

parent::map() function will assign data from CSV to order object

Create order programatically

The next step in process() is adding the order’s product, and save that to DB

Load a product by vid: <?php uc_product_load() ?>
http://drupalcontrib.org/api/drupal/contributions!ubercart!uc_product!uc...

Add product to order:
<?php $order->products[] = $product; ?>

after adding some basic mail and payment method, save the order, complete the sale:

<?php
uc_order_save
($order);
uc_cart_complete_sale($order, TRUE);
?>

Optionally, add payment method as well
<?php uc_payment_enter($order->order_id, 'bank_transfer', 0, 0, NULL, t('Checkout completed for a free order.')); ?>

Full source in Github repo: https://github.com/joetsuihk/ubercart_order_feeds

One more thing

custom settings can be added to the processor
In my case, the client need to deploy to multiple sites and they have slightly different webform IDs that need to be filled, so I put the ID into a settings so client can change that ID easily:

<?php
 
/**
   * Override parent::configDefaults().
   */
 
public function configDefaults() {
    return array(
     
'card_form_id' => '',
     
'mappings' => array(),
    );
  }

  public function
configForm(&$form_state) {
   
$form = array();
   
$form['card_form_id'] = array(
     
'#type' => 'textfield',
     
'#title' => t('Card webform node ID'),
     
'#default_value' => $this->config['card_form_id'],
    );
    return
$form;
  }
?>

Pages

Google