iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 19
1
自我挑戰組

Wordpress 外掛開發系列 第 19

「Wordpress 外掛開發」商品限定VVIP,缺了些東西還是可以讓人入侵看光

我們經過十幾天的總總的了解,我們要開始實際的做第一個外掛的擴充,我本身公司裡是用到wooCommerce,所以對於寫這個的擴充都會比較多一些,這在講解起來會比較詳細些,而且較不會講錯誤人子弟XD我接下來可能挑個兩三次主題,會挑選wooCommerce那些要收錢貴姍姍(還有比較簡單一些)的外掛來做改寫,那我們今天就是這個模板Products Visibility by User Roles,那我們的目標就是達成簡單的限定用戶之餘,也能對要做出使用者限制與這個功能繼續延伸的可能性做一個探討。

限制購買 woocommerce_is_purchasable

在正常的使用woocommerce中,我就不加多以解釋,那麼平常大家對於自己設定是否可以開放購買,都有一定的認知,而今天是要使用hook的方式,來將商品無法購買,不過增加這一個因為是filter的關係,如果購物車中有原本的商品,是會被清空的。

add_filter('woocommerce_is_purchasable', 'go_ranger_purchasable_limt');

function go_ranger_purchasable_limt($is_purchasable,$product){
	if( in_array($product->get_id(),array(621,457,833)) ) { 
		return false;
	}else return $is_purchasable;
}

在array 之中是可以將所有要限制的產品代號來做,而我們可以加上使用者的過濾,來將特定的使用者或是role來做限制,那我們得確保使用者是否登入及是否能抓到值,而抓到在做in_array中進行判斷,那我們預設訂閱者與編輯者是沒有辦法看到我們的產品的,那我們就可以使用這個方式來做限制。

function go_ranger_get_current_user_roles() {
  $roles = array();
  if( is_user_logged_in() ) {
    $user = wp_get_current_user();
    $roles = ( array ) $user->roles;
    if(in_array($roles,array('subscriber','editor'))) {
      return false;
    } else return true;
  }else return false;
  
}

function go_ranger_purchasable_limt($is_purchasable,$product){
  if(true === go_ranger_get_current_user_roles())return $is_purchasable;
  if( in_array($product->get_id(),array(621,457,833)) ) { 
    return false;
  }

}

隱藏loop上的顯示

這樣我們就可以做到以使用者的role,來決定誰可以看見特定的商品,而這類的應用往往出現在所謂的VVIP的使用,特別好用,但有個美中不足的是,我本來的設計是想使用is_visible來將目錄中,不需要出現的商品隱藏,想採取使用is_visble來做,不過在archieve的頁面中,使用woocommerce_product_loop_start會讓目錄有跑版的問題,所以換了一個filter來製作,我們選定woocommerce_product_is_visible,老實說,woocommerce的文件看似寫得很齊全,實際上你真的要查什麼東西,找不到的機率很大,不如從core去推敲會更快些。

add_filter('woocommerce_product_is_visible','go_ranger_visible_product',10,2);

function go_ranger_visible_product( $visible, $product_id ){
  if(true === go_ranger_get_current_user_roles()) return $visible;
	if($product_id === 6226){
		return false;
	} else return $visible;
}

將全部產品加入限制的功能

加上這一些程式碼,並且在可以在meta中,加上要封閉的option,就可以對每一個產品做出限制了,而我們這次就不加上後台的程式碼,在寫的時候出現一點亂流,導致我測試環境掛掉查了兩小時,結果是git 因為rebose出現change log對不上找不出bug,而假設我們已經在後台做出個簡單的限制role則可以在我們後端改寫。

最後的邏輯調整成go_ranger_get_current_user_roles回傳true就可以使用,讓上下的邏輯一至,而我們設定我們的產品id來當post id,而每一個post id都有一個相對應得visible key 來顯示裡頭顯示的函式,而我們可以看見$visible_roles將包含該商品的被允許顯示,當然我們也可以設計成,誰不能看到,就與我們上半部寫的邏輯一至,不過我相信特定商品,應該還是少數人被允許看見的這種情況較常見,為了閱讀性go_ranger_purchasable_limtgo_ranger_visible_product並沒有整再一起。


add_filter('woocommerce_is_purchasable', 'go_ranger_purchasable_limt',10,2);
add_filter('woocommerce_product_is_visible','go_ranger_visible_product',10,2);

function visible_check($product_id){
  return get_post_meta( $product_id, '_go_ranger_is_restrict_visible',true);
}

function go_ranger_get_current_user_roles($product_id) {
  $roles = array();
  $visible_roles =  get_post_meta( $product_id, '_go_ranger_role_visible');
  if( is_user_logged_in() ) {
    $user = wp_get_current_user();
    $roles = ( array ) $user->roles;
    if(in_array($roles,$visible_roles)) {
      return true;
    } else return false;
  }else return false;
 
}

function go_ranger_purchasable_limt($is_purchasable,$product){
  if(false === visible_check()) return $is_purchasable;
  if(true === go_ranger_get_current_user_roles($product->get_id())) return true;
  return false;
}

function go_ranger_visible_product( $visible, $product_id ){
  if(false === visible_check()) return $visible;
  if(true === go_ranger_get_current_user_roles($product_id)) return true;
	return false;
}

就算這樣寫,他們還是找得到

我們寫了一大段的程式碼,除了確切的不能購買以及將visible給調掉,但還是可以在搜尋或是計算有幾個產品的片段露餡,可以在pre_get_postsget_terms中做更改,讓他們顯示的更正確。

那我們第二個缺的,就是下圖這樣,加入一個foreach的role清單,再將使用者加入meta來做到這個基本加入的功能,而在每一個product底下顯示則是加入add_meta_box來做到擴充:

https://ithelp.ithome.com.tw/upload/images/20201003/20104531sNxPX608nT.jpg

add_action( 'add_meta_boxes' , 'go_ranger_add_meta_box' , 10 , 1);
public function go_ranger_add_meta_box( $post_type ) {
  global $post;
  $post_types = array('product');   
  $product = get_product( $post->ID );
  if ( in_array( $post_type, $post_types ) && ($product->product_type == 'simple' ) ) {
      add_meta_box(
          'go_ranger_set_restrict'
          ,__( 'Go Ranger set restrict', 'woocommerce' )
          ,'你的模板套進去'
          ,$post_type
          ,'advanced'
          ,'high'
      );
  }
}

最後在save post打回自己的頁面,來完成這個設計,就可以簡單的完成使用user role來限制客戶的存取限制,而如果是要給特定用戶但商業邏輯並沒辦法使用role來解決,那麼在設計上可以改成get_users來做用id來確認客戶是否可以瀏覽到此產品,不過我們之中還有一項meta沒有提到,那就是get_post_meta( $product_id, '_go_ranger_is_restrict_visible')對應的,得有個checkbox來確定是否啟動,並且才有上面那個列表的出現,conditional logical可以enqueue自己寫的JS來做,我用ultimate member的截圖來當範例。

https://ithelp.ithome.com.tw/upload/images/20201003/20104531Q6F7boNSq8.jpg

而另外一個方式,就是增加更多的roles,不過這個可能需要使用套件來做到了,我們明天講完i18n後,再來講講如何實作多重的roles,而這個多重概念可以有幾個方向,第一個是使用meta來做出虛擬的role,並且在core get_role之類的函式做相關的取代,不過這個設定可能會有些capabilities的問題,是可以開一張表來設定roles與每個註冊的cap可以做的事情,這樣提升準確率。

而這個除了限定之外,其實對於那些惡意的admin subscriber是很有效的限制,你甚至是可以做出一個可以被搜尋到,到無法被下單的trap來做熱部署的缺口,以及寫定轉指到die 404的設定,404肯定比很多hook後,跑出來的結果還要更快速些,讓那些愛亂打人的攻擊者一堵小牆建立起來,也讓自己的購物系統(wooCommerce)更強壯些。

Reference

WooCommerce action hook
WooCommerce Code Reference
List WordPress Posts by Category
get_post_meta
Handle WooCommerce Product Settings
Add Custom Fields for WooCommerce Using Meta Box
WOOCOMMERCE HIDDEN PRODUCTS: HOW TO SET PRODUCT VISIBILITY IN YOUR STORE
2 user-friendly plugins to hide WooCommerce products


上一篇
「Wordpress 外掛開發」建立REST API以及避免原生的設計造成零時差攻擊 - WP-JSON
下一篇
「Wordpress 外掛開發」掛上多語系,大家都在international - i18n
系列文
Wordpress 外掛開發30

尚未有邦友留言

立即登入留言