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 針對多位元文字的處理,並沒有想像中的容易。