Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
因為阿竣的建議,加上另一個本來列在待辦事項中的功能,在 1.1.0 版做了了兩項更新:
由於限制、排除區域的功能,原本 Denny Kuo 的函式庫中就已經實作了,因此只要提供相對應的資料就可以。但為了讓使用者可以更容易使用這項功能,因此在流程設計上還是有做一點改動。
內容目錄
在開始繼續說明前,先整理一下客製化 Contact Form 7 欄位的流程。
wpcf7_add_form_tag()
)事實上,只要做完步驟 1 跟 2,你的客製化功能就能以手動輸入的方式,出現在 CF7 的表單中,步驟 3 跟 4 是為了讓使用者可以更容易使用而加強的功能。
我在鐵人賽的文章〈WordPress 客製化實務:替 Contact Form 7 加上地址欄位〉中,便說明步驟 1-3 的實際執行方式。
在開始本次的改版前,我們需要先了解 CF7 儲存資料的方式。
項目 | 選項 (options) | 數值 (values) |
---|---|---|
代碼中呈現形式 | [tag_name option1 option2:attribute] | [tag_name "values1" "values2"] |
可接收資料 | 只接受英數與特定字元 (如 - 與 _ ) | 可以接受任何數值 |
範例 | ID 屬性、類別屬性、必填欄位 | 預設值、下拉式選單選項值 |
處理常式相關方法 | $tag->has_option('option_name') 、$tag->get_option('option_name') | $tag->values |
這邊需要特別注意的是,values
並沒有分類的功能,但可以透過標籤|值
的方式做到類似分類的效果。然而,由於這種方法的值也只接受特定字元,無法用中文作為值,因此最後仍決定用單純的字串值來儲存欄位屬性。
根據本次改版目標,將資料類型做以下定義:
隱藏區域、地址欄位,我們比照 1.0.0 版中的命名規則,直接取名作 district-hidden
跟 street-hidden
。
為了方便起見,我們將限制/排除區域的屬性值按照原本的函式庫命名:data-only
與 data-except
。
因此,我們預設的地址代碼就會呈現以下形式:
[address address-001 district-hidden street-hidden "data-only: values" "data-except: values"]
定義完我們的數值後,我們就可以來接著規劃處理常式。有關處理常式的寫法,會再後續另外補充。
待修改完處理常式後,我們便要來著手進行 CF7 介面的客製化了。
為了可以更直覺的輸入欄位值,因此在介面上希望使用文字區域 <textarea>
,讓使用者可以透過「換行」的方式,輸入想要限定、排除的地區。
要客製化 CF7 的代碼產生器,會使用到 wpcf7_admin_init
這個勾點 (hook)。
add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_address', 25, 0 ); function wpcf7_add_tag_generator_address() { $tag_generator = WPCF7_TagGenerator::get_instance(); $tag_generator -> add( 'address', esc_html__( 'address', 'wpcf7-twaddress' ), 'wpcf7_tag_generator_address' ); } function wpcf7_tag_generator_address( $contact_form, $args = '' ) { // 以下為代碼產生器的介面,後面再補充 }
在 wpcf7_tag_generator_address
這個函式中,便是用來定義代碼產生器表單的欄位。為了保持與 CF7 相同的使用體驗,因此在架構上我直接將 CF7 原外掛中 modules 內的欄位複製,並加以修改。
function wpcf7_add_tag_generator_address() { $tag_generator = WPCF7_TagGenerator::get_instance(); $tag_generator -> add( 'address', esc_html__( 'address', 'wpcf7-twaddress' ), 'wpcf7_tag_generator_address' ); } function wpcf7_tag_generator_address( $contact_form, $args = '' ) { $args = wp_parse_args( $args, array() ); $description = __( "Generate a form-tag for an address with Taiwan zipcode. For more details, see %s.", 'wpcf7-twaddress' ); $desc_link = wpcf7_link( __( 'https://github.com/huanyichuang/wpcf7-twaddress', 'wpcf7-twaddress' ), __( 'my GitHub repository', 'wpcf7-twaddress' ) ); ?> <div class="control-box"> <fieldset> <legend><?php echo sprintf( esc_html( $description ), $desc_link ); ?></legend> <table class="form-table"> <tbody> <tr> <th scope="row"><?php echo esc_html( __( 'Field type', 'contact-form-7' ) ); ?></th> <td> <fieldset> <legend class="screen-reader-text"><?php echo esc_html( __( 'Field type', 'contact-form-7' ) ); ?></legend> <label><input type="checkbox" name="required" /> <?php echo esc_html( __( 'Required field', 'contact-form-7' ) ); ?></label> </fieldset> </td> </tr> <tr> <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-name' ); ?>"><?php echo esc_html( __( 'Name', 'contact-form-7' ) ); ?></label></th> <td><input type="text" name="name" class="tg-name oneline" id="<?php echo esc_attr( $args['content'] . '-name' ); ?>" /></td> </tr> <tr> <th scope="row"><?php echo esc_html( __( 'Options', 'contact-form-7' ) ); ?></th> <td> <fieldset> <legend class="screen-reader-text"><?php echo esc_html( __( 'Options', 'contact-form-7' ) ); ?></legend> <label><input type="checkbox" name="zip-hidden" class="option" /> <?php echo esc_html( __( 'Hide zipcodes', 'wpcf7-twaddress' ) ); ?></label><br /> <label><input type="checkbox" name="district-hidden" class="option" /> <?php echo esc_html( __( 'Hide districts', 'wpcf7-twaddress' ) ); ?></label><br /> <label><input type="checkbox" name="street-hidden" class="option" /> <?php echo esc_html( __( 'Hide address details', 'wpcf7-twaddress' ) ); ?></label><br /> <label><input type="checkbox" name="full-addr-hidden" class="option" checked /> <?php echo esc_html( __( 'Hide full address', 'wpcf7-twaddress' ) ); ?></label><br /> </fieldset> </td> </tr> <tr> <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-id' ); ?>"><?php echo esc_html( __( 'Id attribute', 'contact-form-7' ) ); ?></label></th> <td><input type="text" name="id" class="idvalue oneline option" id="<?php echo esc_attr( $args['content'] . '-id' ); ?>" /></td> </tr> <tr> <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-class' ); ?>"><?php echo esc_html( __( 'Class attribute', 'contact-form-7' ) ); ?></label></th> <td><input type="text" name="class" class="classvalue oneline option" id="<?php echo esc_attr( $args['content'] . '-class' ); ?>" /></td> </tr> <tr> <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-range' ); ?>"><?php echo esc_html( __( 'Location Range', 'wpcf7-twaddress' ) ); ?></label></th> <td><textarea type="text" name="data-only" class="values" id="<?php echo esc_attr( $args['content'] . '-range' ); ?>" placeholder="<?php echo sprintf( /* translators: %s is the line break for the placeholder of textareas. */ esc_html__( '台北市%s台北市@中正區%s桃園市@龜山區|桃園區', 'wpcf7-twaddress' ), ' ','
' );?>"></textarea> </td> </tr> <tr> <th scope="row"><label for="<?php echo esc_attr( $args['content'] . '-exclusion' ); ?>"><?php echo esc_html( __( 'Location Excluded', 'wpcf7-twaddress' ) ); ?></label></th> <td><textarea type="text" name="data-except" class="values" id="<?php echo esc_attr( $args['content'] . '-exclusion' ); ?>" placeholder="<?php echo sprintf( /* translators: %s is the line break for the placeholder of textareas. */ esc_html__( '台北市%s台北市@中正區%s桃園市@龜山區|桃園區', 'wpcf7-twaddress' ), ' ','
' );?>"></textarea></td> </tr> </tbody> </table> </fieldset> </div> <div class="insert-box"> <input type="text" name="address" class="tag code" readonly="readonly" onfocus="this.select()" /> <div class="submitbox"> <input type="button" class="button button-primary insert-tag" value="<?php echo esc_attr( __( 'Insert Tag', 'contact-form-7' ) ); ?>" /> </div> <br class="clear" /> <p class="description mail-tag"><label for="<?php echo esc_attr( $args['content'] . '-mailtag' ); ?>"><?php echo sprintf( esc_html( __( "To use the value input through this field in a mail field, you need to insert the corresponding mail-tag (%s) into the field on the Mail tab.", 'contact-form-7' ) ), '<strong><span class="mail-tag"></span></strong>' ); ?><input type="text" class="mail-tag code hidden" readonly="readonly" id="<?php echo esc_attr( $args['content'] . '-mailtag' ); ?>" /></label></p> </div> <?php }
這裡面可以看到,在欄位中主要有兩種類別屬性 option
跟 values
,分別代表前面所提,CF7 代碼中的兩種數值屬性。
因此在上述的程式碼加入了兩個 checkbox
選項,以及兩個 <textarea>
區段。
然而,加入了 <textarea>
之後,發現代碼產生器並不會將其中的數值帶入代碼中。因此,在本次的改版中,需要能夠在標籤中插入額外字串。
分析了原本 CF7 的檔案後,發現代碼產生器是透過 wpcf7.taggen.compose
來建構代碼字串。為了能夠擴充這項功能,我參考了 Contact Form 7 – Conditional Fields 這款外掛的做法:
// ------------------------------------ // CF7 TAG GENERATOR OVERRIDE // ------------------------------------ if (_wpcf7 == null) { var _wpcf7 = wpcf7}; // wpcf7 4.8 fix var old_compose = _wpcf7.taggen.compose; // ...before overwriting the jQuery extension point _wpcf7.taggen.compose = function(tagType, $form) { jQuery('#tag-generator-panel-group-style-hidden').val(jQuery('#tag-generator-panel-group-style').val()); // original behavior - use function.apply to preserve context var ret = old_compose.apply(this, arguments); //tagType = arguments[0]; //$form = arguments[1]; // START: code here will be executed after the _wpcf7.taggen.update function if (tagType== 'group') ret += "[/group]"; if (tagType== 'repeater') ret += "[/repeater]"; // END if (tagType== 'togglebutton') { $val1 = jQuery('#tag-generator-panel-togglebutton-value-1'); $val2 = jQuery('#tag-generator-panel-togglebutton-value-2'); var val1 = $val1.val(); var val2 = $val2.val(); if (val1 == "") val1 = $val1.data('default'); if (val2 == "") val2 = $val2.data('default'); str_val = ' "'+val1+'" "'+val2+'"'; ret = ret.replace(']', str_val+']'); } return ret; };
先將原始的 wpcf7.taggen.compose
存在另一個變數後,用 apply()
的方式來繼承原本建構的結果。接著才做字串的變更。
將上述的程式邏輯稍加改寫後,就變成了 address-tag-generator.js 內的程式。這裡主要做的判斷,是將原本 <textarea>
中的斷行取代為 ,
,並將 address-range
與 address-exclusion
中的值儲存為 "data-only: 值"
與 "data-except: 值"
的形式後,取代原本的字串:
( function ( $ ) { 'use strict'; if ( typeof wpcf7 === 'undefined' || wpcf7 === null ) { return; } wpcf7.taggen = wpcf7.taggen || {}; const oldCompose = wpcf7.taggen.compose; wpcf7.taggen.compose = function ( tagType, $form ) { // original behavior - use function.apply to preserve context let ret = oldCompose.apply( this, arguments ); if ( 'address' === tagType ) { const range = $( '#tag-generator-panel-address-range' ).val(), exclusion = $( '#tag-generator-panel-address-exclusion' ).val(); ret = range.length ? [ ret.slice( 0, -1 ), ' "data-only:', range.replace( /[\n\r]/gi, ',' ), '"]', ].join( '' ) : ret; ret = exclusion.length ? [ ret.slice( 0, -1 ), ' "data-except:', exclusion.replace( /[\n\r]/gi, ',' ), '"]', ].join( '' ) : ret; } return ret; }; } )( jQuery );
接著透過 admin_enqueue_scripts
這個勾點,讓管理介面得以載入所需的 JavaScript 程式。
add_action( 'admin_enqueue_scripts', 'wpcf7_address_admin_script' ); function wpcf7_address_admin_script() { wp_enqueue_script( 'address-tag-generator', plugin_dir_url( __FILE__ ) . 'admin/js/address-tag-generator.js', array( 'jquery', 'thickbox', 'wpcf7-admin' ), false, true); }
雖然前面很冠冕堂皇的列出了 CF7 客製化的流程,但老實說,我這次並不是照這個流程走的。正因為沒有按照這個流程,先做 UI 才處理資料,導致在偵錯上花了不少時間,因此才會認真的反省開發流程。
藉由這次的實作,也了解到 CF7 針對多位元文字的處理,並沒有想像中的容易。