修改用戶頁面的密碼欄位

要求看似簡單,將用戶頁面的密碼欄位改成 “New Password” (或加 placeholder 等)

由:

改成:

試過使用最簡單的 hook_form_alter user_profile_form 但不成功,其中一個元素 <?php $form['account']['pass']['#type'] = 'password_confirm';?>
沒有 #title 可以修改

而原來 ‘password_confirm’ 是由 form_process_password_confirm() 輸出的,其中並沒有提供 hook

所以便要另找途徑,使用 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;
}
?>

完成

訪客購物車 programmatically


需求:每個訪客都會預設幫他們將一件貨物加到購物車
他們便可以直接完成交易

Commerce kickstart已經是一個現成的示範
訪客就算不注冊仍然可以使用購物車
而且一定情況之下關閉瀏覽器之後再回到網站仍然可以找回上次的紀錄繼續購物

但在 init 的時候使用

<?php
 commerce_order_new
(0);
?>
是不會將 order 連接到現有的瀏覽器進程的

你需要的是:

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

Drupal commerce - 從代碼建立訂單


Drupal commerce 己經成為 Drupal 電子商務應用的主流了,ubercart 落伍了

Ubercart 是一個源自 Drupal5.x 世代的方案
而為了使用 Drupal7 方便的 entities,和令電子商務方案使用一個「弱品牌化」的名字,Drupal commerce 誕生了

但因為 Drupal commerce 龐大的代碼量,為了方便分散工作,代碼都打散到不同的模組,各自有他們的維護者
再加上為數不少的 dependencies 令上手的難度過大
所以 Drupal commerce kickstart 便將一個完整的電子商務網站包裝起來
只需要下載安裝,匯入範例便可以立即使用了

這次的開發和前一次類似,從代碼建立將貨物放到訂單:

<?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;
?>

使用

<?php
 field_attach_submit
()
?>
儲存貨品,便可以觸發相對應的 hooks 了:
<?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);
?>

購物車:

<?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

使用者情境:批量使用 CSV 匯入 ubercart 訂單

Feeds 模組已經有一個 CSV 格式讀取 parser,又有批量 batch 支持,能處理大檔案 CSV ,Feeds 的代碼也有很多使用的經驗,而且最重要的是,模組自己已經有四種內容可以匯入:(nodes, users, terms, feednode?),有更高的機會支持其他內容 (ubercart orders)

先從 “The developer's guide to Feeds” 開始研究:(https://www.drupal.org/node/622700)
有三種 plugin: fetcher,parser 和 processor

  • Fetcher 是「獲得匯入源的方法」feeds module 已經有一個檔案上載的 fetcher
  • Parser 是「如何分析,讀取匯入源」feeds module 已經有一個 CSV parser
  • Processer 的工作是將處理過的資料儲存成為 Drupal 的內容

建立一個新模組 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;
}
?>

建立一個新檔案 FeedsOrderProcessor.inc:
<?php
class FeedsOrderProcessor extends FeedsProcessor {
}
?>

首先在 getMappingTargets() 定義 CSV 中會用到的欄位:

<?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(''),
      ),
   
//....
?>

在 CSV 內的資料處理過之後,資料的值會根據 Array 的 key 放到 object 之內.

我輸出了一個原生的 order object,找出原本就有的預設欄名,定義 mappings 的時候重用它們,以省卻一點點麻煩 (i.e. delivery_first_name, delivery_last_name above and more)

process() 是主要的程式進入點,複製一點 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);
     
//.......
?>

來到這一點出現一個新的函數 map() 來返回一個全新的 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() 函數會將 CSV 內的資料處理過之後,根據 mapping array 的 key 放到 object

程式化的建立 order

process() 的下一步是將 product 加到 order,並儲存之

使用 vid 載入一個 product: <?php uc_product_load() ?>
http://drupalcontrib.org/api/drupal/contributions!ubercart!uc_product!uc...

加 product 到 order:
<?php $order->products[] = $product; ?>

再填入一些 order 的基本 mail 和 payment method 之後,儲存,完成 sale:

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

你亦可以加入 payment method
<?php uc_payment_enter($order->order_id, 'bank_transfer', 0, 0, NULL, t('Checkout completed for a free order.')); ?>

Github 源碼 repo: https://github.com/joetsuihk/ubercart_order_feeds

One more thing

Processor 可以有自己的設定頁面
在我的情境,因為客戶會將同一個模組放到不同的網站,而他們有不同的 webform node ID
所以我便將 ID 放到設定頁面令客戶可以更方便的修改設定了

<?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;
  }
?>

Audi Home of Quattro 技術回顧

https://www.audiquattro.hk/

這是一個在維港海上的車展
展示最新的 S3 Sportback, RS6 Avant 和讓用戶可以分享他們最喜愛的駕駛路線

在桌面和手持裝置上,有活力的頁面設計和動畫過場都能將品牌的形象展現
在互動性的地圖上帶出享受的體驗,不論在船上或網站上

基本架構: PHP codeigniter, jQuery mobile,

Mobile site

這次沒有使用 responsive design 而使用一個獨立的手機版網頁 (https://www.audiquattro.hk/?device=mobile 直接在桌面看手機版)
然後在每一個頁面根據裝置跳轉
使用Mobile detect做裝置檢測
並使瀏覽器跳轉到正確的 URL

Google map 樣式客製

我們使用了自定義樣式的地圖
Google map 本身有提供一個強大的樣式功能 (https://developers.google.com/maps/documentation/javascript/styling?csw=1)
可修改道路,海面,公園等的顏色
令我們可以將地圖完美配合到網站的色調

Google map 路徑API

這個網站中一個互動的功能是用戶可以建立並分享他們覺得有趣和有挑戰的行車路線
我們使用自定的錨點和 Google directions API (https://developers.google.com/maps/documentation/directions/)
令用戶可以點擊兩個地點而劃出行車路線

也對應手機版本
我們使用手機上的 html5 geolocation API 令用戶可以使用用戶現在的位置設計路線

這一次我們深入地客製google maps 和使用它的數個週邊 API
是一個全新的挑戰
而且得出的效果也非常好,也體驗到 Google map API 的強大
我們可以在上面建立順暢的過場動畫,錨點,markers,overlays等等
就像是一個客製的地圖一樣

Video playing

每一個知名的車手都有4段視頻,車手們之間使用 slideshow 的形式表現
我們使用的是 html5 video plugin http://www.videojs.com/ 播放伺服器上的 mp4, ogg
所以 html 上會有12段視頻
但我們發覺 browser 是有一個 video 的緩存限制
令 video 的 buffer ready event, onload event 不會啟動
(我們只會在 4段 video 都 buffer 完成的時候才會播放)
但這個情況只會在一個頁面放多於 10 個 video 的時候出現
而我們又找不到 browser 相關的技術文件
所以我們只好在 slideshow event 後 insert/remove video tags 來處理「一個頁面不多於 10 個視頻」的限制

Pages

Google