├── README.md ├── class-wc-email-awaiting-delivery-order.php ├── functions.php ├── ks_server_checker.php ├── plugins_list.json ├── wc-settings.php └── woocommerce └── emails └── customer-awaiting-delivery-order.php /README.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | 使用WordPress這套框架開發網站,以後端的角色跟前端夥伴合作,下方是一些經驗整理,常用外掛系列,裡面包含了很多組合,並非每個案子都會使用這些工具,根據情況調整與搭配自己或夥伴延伸開發的方式也是不少,但沒有這些工具,開發想必更為困難,感謝社群~ 4 | 5 | ## 主要項目 6 | 7 | 8 | ### Post Type (新型態)類 9 | 10 | WordPress 的核心架構,從這「型態」出發,延伸出各種內容來從前端顯示。 11 | 12 | 1. [Custom Post Type UI](https://tw.wordpress.org/plugins/custom-post-type-ui/) 建立新型態的文章內容,用來客製化不同內建文章/頁面的「標題」、「內文」結構形式。 13 | 2. [Advanced Custom Fields](https://tw.wordpress.org/plugins/advanced-custom-fields/) 當完成安裝與建立客製化新型態後,會發現那還只是跟內建的文章/頁面結構一樣,只是在後台選單上多了一個新的選項,這時候要搭配這款客製化定義欄位的工具,指定新型態格式,自定義該新型態內容使用哪些欄位。 14 | 15 | ### Capabilities (權限)類 16 | 17 | 扯到內容,就會有操作權限的管理,WordPress 在這部分並沒有像其他 CMS 有非常「計較」權限的管理,屬於大方向的命名權限,並針對該命名權限從程式中鎖定,不能說有強硬的限制開發或是使用某種框架(framework),這點既是彈性也是在開發給客戶時要注意的地方。 18 | 19 | 1. [User Role Editor](https://tw.wordpress.org/plugins/user-role-editor/) 從最基礎的權限(Capabilities)出發,可以配置給單獨的使用者或是角色,介面稍微陽春。 20 | 2. [Members](https://tw.wordpress.org/plugins/members/) 以角色(Role)出發,設定與編輯角色權限,把功能分類好的編輯界面滿舒適的。 21 | 3. [Groups](https://tw.wordpress.org/plugins/groups/) 群組的概念分配權限,提供群組的模式管理註冊者 22 | 23 | 三種權限管理類的外掛都有特色,以個人經驗區分使用情境為:客戶用戶數少且變化多適合第一種、用戶數多且單純適合二、三種。 24 | 25 | ### 類權限管理 類 26 | 27 | 從開發角度來去限制、管理其他自由開發的外掛本來就會造成架構難度提升,所以這個分類的概念就是「疊加」,透過別的外掛來更進階處理其他外掛的不足,真要說為什麼要這麼做,就是為了日後升級的彈性囉! 28 | 29 | 1. [Admin Menu Editor](https://tw.wordpress.org/plugins/admin-menu-editor/) 有時候因為要調整出客戶可讀性與方便性高的選單時就會需要他,擁有直接修改選單顯示名稱與排序功能 30 | 2. [Adminimize](https://tw.wordpress.org/plugins/adminimize/) WP 內建的權限設定沒有針對角色還有更細的增刪修查限制功能,同個權限下的使用者如果有希望不同的操作介面,就會需要使用這外掛去「隱藏」,做一些髒髒der事。 31 | 32 | > 至於更細部的權限微調大部分都是直接coding在子主題下了! 33 | 34 | 35 | ### 頁面客製化 類 36 | 37 | 設計頁面(page)會碰到個問題:求速度與精緻常使用一些主題(theme)作為前導,但大部分主題強化前端視覺使用短碼(short code)的方式,其實不適用一般使用者(end-user),常有非單純重複貼文功能的頁面要讓客戶修改的時候,避免客戶誤觸雷區與使用者體驗提升,針對輸入的內容會需要程式化挖空,這點大部分就要透過下面的外掛來搭配了 38 | 39 | 1. [Pagely MultiEdit](https://tw.wordpress.org/plugins/pagely-multiedit/) 當頁面中有多個欄位是希望提供給客戶使用時,這套外掛可以做挖空,提供填空的方式修正頁面,其他資源可以參考[官方](https://pagely.com/multiedit-plugin/)教學 40 | 2. [Custom Post Widget](https://tw.wordpress.org/plugins/custom-post-widget/) 同樣屬於挖空部分內容提供客戶有固定區域修改文案的功能,結合小工具(widgets)或是自行開發延伸的方式都很方便 41 | 3. [Custom Login](https://tw.wordpress.org/plugins/custom-login/) 登入後台的樣式客製化,強化客戶歸屬感強化XD 42 | 4. [Customize Login Image](https://tw.wordpress.org/plugins/customize-login-image/) 同上,歸屬感強化的換登入介面工具 43 | 44 | 45 | ## 常見項目 46 | 47 | 下面的項目比較偏向綜合來看,大多客戶或開發上會需要的輔助 48 | 49 | ### 聯絡表單 類 50 | 51 | 1. [Contact Form 7](https://tw.wordpress.org/plugins/contact-form-7/) 說到表單一定要用這款 52 | 2. [Contact Form DB](https://tw.wordpress.org/plugins/contact-form-7-to-database-extension/) 送出去的表單通常是直接寄信到客戶那邊,用這款外掛可以做資料管理 53 | 54 | ### 活動事件 類 55 | 56 | 1. [Event Organiser](https://tw.wordpress.org/plugins/event-organiser/) 客戶公開活動、行程等這類需求也不少,需要的包涵時間地點等資訊也滿完整的 57 | 2. [Another Events Calendar](https://tw.wordpress.org/plugins/another-events-calendar/) 最近發現的外掛,跟上面有的功能差不多 58 | 59 | ### 發信與訂閱 類 60 | 61 | 1. [Post SMTP Mailer](https://tw.wordpress.org/plugins/post-smtp/) 根據客戶需求,架構簡單的可以使用 SMTP 方式寄信,免費版本就支援 Log 記錄,有問題可以查看。 62 | 2. [Easy WP SMTP](https://tw.wordpress.org/plugins/easy-wp-smtp/) 同上差不多類型 63 | 3. [WP Mail SMTP](https://tw.wordpress.org/plugins/wp-mail-smtp/) 同上差不多類型 64 | 4. [WP SES](https://tw.wordpress.org/plugins/wp-ses/) 有需要大量發送或是希望更穩定服務的客戶可以推薦使用 Amazon SES 服務 65 | 5. [Mailgun for WordPress](https://tw.wordpress.org/plugins/mailgun/) 或是使用 MailGun 的介接也滿方便與穩定 66 | 6. [Email Subscribers & Newsletters](https://tw.wordpress.org/plugins/email-subscribers/) 免費強大的訂閱系統,適合搜集名單,至於要發有追蹤系統流程類的EDM,還是建議用付費的專業系統,其他資訊可以參考[這邊](http://www.wpbeginner.com/wp-tutorials/how-to-add-email-subscriptions-for-your-wordpress-blog/) 67 | 7. [MailPoet Newsletters](https://tw.wordpress.org/plugins/wysija-newsletters/) 有搭配 autoresponder 的服務,客戶如有需要整合在網站的專業EDM服務可以使用這款 68 | 69 | 70 | ### WooCommerce 類 71 | 72 | 1. [WooCommerce Social Login - WordPress plugin](https://codecanyon.net/item/woocommerce-social-login-wordpress-plugin/8495883) 測試過免費的外掛,想改登入成功後的導向不行要硬改、然後混合式登入模式邏輯也有點怪,這款付費一次搞定 73 | 2. [WooCommerce Dynamic Pricing & Discounts](https://codecanyon.net/item/woocommerce-dynamic-pricing-discounts/7119279) 目前套用各種客戶要求的折扣模式都還挺行的外掛 74 | 3. [WooCommerce Extended Coupon Features](https://tw.wordpress.org/plugins/woocommerce-auto-added-coupons/) 免費但也夠強的折扣外掛,組合上也很厲害 75 | 4. [WooCommerce Checkout Field Editor Pro](https://tw.wordpress.org/plugins/woo-checkout-field-editor-pro/) 結帳時的欄位調整 76 | 5. [WooCommerce Checkout Manager](https://tw.wordpress.org/plugins/woocommerce-checkout-manager/) 同上,調整結帳欄位時的方便工具 77 | 6. [Custom Related Products for WooCommerce](https://wordpress.org/plugins/custom-related-products-for-woocommerce/) woocommerce預設相關商品會抓取同分類下的所有商品,這外掛可以讓你自訂在前台呈現不同分類的相關商品 78 | 79 | > WooCommerce光是折扣部份就有很多內容可以寫不完了,但應該不會有一款外掛打趴全部的情況(因為這可能會有很複雜難用的後作用) 80 | 81 | 82 | ### 效能調校 類 83 | 84 | 1. [W3 Total Cache](https://tw.wordpress.org/plugins/w3-total-cache/) 搭配這款外掛也建議從伺服器上跟進,安裝[一些快取機制的應用](https://easyengine.io/tutorials/php/memcache/)來強化,速度可以大幅提升 85 | 2. [Redis Object Cache](https://tw.wordpress.org/plugins/redis-cache/) Redis 做快取有連續性特性,性能也不錯,可以對付高流量站 86 | 3. [Incapsula](https://www.incapsula.com/) 免費CDN的選擇,這款有個Aggressive模式夠強 87 | 4. [Cloudflare](https://www.cloudflare.com/) 一般般的免費CDN 88 | 89 | ### 圖片處理 類 90 | 1. [Enhanced Media Library](https://tw.wordpress.org/plugins/enhanced-media-library/) 媒體庫管理工具,包含下分類與標籤。 91 | 2. [Warp iMagick Image Compressor + Convert to WebP for WordPress](https://warp-imagick.pagespeed.club/) 支援 WebP 與 JPG/PNG 圖片縮圖,全部服務都在外掛內完成,專業的外掛! 92 | 3. [Stop Generating Image Sizes](https://tw.wordpress.org/plugins/image-sizes/) 取消無用途過多的縮圖大小選項 93 | 4. [Force Regenerate Thumbnails](https://tw.wordpress.org/plugins/force-regenerate-thumbnails/) 強制重新產生縮圖,可以搭配上面的外掛使用,藉此刪除無用的縮圖 94 | 5. [EWWW Image Optimizer](https://tw.wordpress.org/plugins/ewww-image-optimizer/) 方便的外掛,功能類似 Warp iMagick Image Compressor,免費版就很夠用了 95 | 96 | ### 其他強化 類 97 | 98 | 1. [TinyMCE Advanced](https://tw.wordpress.org/plugins/tinymce-advanced/) 加強所見即所得編輯介面 99 | 2. [Easing Slider](https://tw.wordpress.org/plugins/easing-slider/) 有些客戶對於輪播圖希望有其他選項時可以使用這款,在開發上的組合也滿棒的 100 | 3. [Add Admin JavaScript](https://tw.wordpress.org/plugins/add-admin-javascript/) 以類權限部分,第一層客製化使用第一、二款外掛沒問題,如要針對功能內頁內的項目修正,除了直接 Hard Code 就是使用 JavaScript 來修正顯示 101 | 4. [Add Admin CSS](https://tw.wordpress.org/plugins/add-admin-css/) 同上的處理外掛 102 | 5. [Breadcrumb NavXT](https://tw.wordpress.org/plugins/breadcrumb-navxt/) 很強大的麵包屑管理工具,通常用來藏一些不該出現的集合頁連結 103 | 104 | ### SEO 類 105 | 106 | 1. [Yoast SEO](https://tw.wordpress.org/plugins/wordpress-seo/) 功能強大、適合進階 107 | 2. [All in One SEO Pack](https://tw.wordpress.org/plugins/all-in-one-seo-pack/) 功能算強、簡單適合客戶 108 | 109 | ### 進階 類 110 | 111 | 1. [Shortcodes Ultimate](https://tw.wordpress.org/plugins/shortcodes-ultimate/) 不同於購買的主題樣板,這外掛提供一些開源實用的短碼(shortcode)可以為進階使用者強化多樣性 112 | 2. [WPML](https://wpml.org/) 多國語言的強大外掛,但網站效能會因為他的各種強大功能給導致速度變慢,需搭配伺服器部分評估 113 | 3. [Safe Redirect Manager](https://tw.wordpress.org/plugins/safe-redirect-manager/) 雖然轉址這件事我都建議從伺服器上下手,但如果碰到無權限的狀況下,這工具就有不少幫助了 114 | 4. [Transients Manager](https://tw.wordpress.org/plugins/transients-manager/) 管理背景執行程序的好幫手,在網站覺得慢的時候使用它來清理一下 115 | 5. [Heartbeat Control](https://tw.wordpress.org/plugins/heartbeat-control/) 同上為優化外掛,管理 WordPress AJAX 發生的行為,過多的 AJAX 發生會導致網站執行效率變得很差,相關資訊可以[點此](https://woorkup.com/diagnose-admin-ajax-php-causing-slow-load-times-wordpress/)參考 116 | 6. [Core Control](https://tw.wordpress.org/plugins/core-control/) 管理 WordPress 核心 HTTP 模組 與其他功能的好工具 117 | 7. [Disable All WordPress Updates](https://tw.wordpress.org/plugins/disable-wordpress-updates/) 當客戶屬於非使用手動安裝的WordPress架構,通常會有個隱藏危險:`自動更新`,開發的網站如有版本相容問題就必須使用這工具,改為手動更新 118 | 8. [Table of Contents Plus](https://tw.wordpress.org/plugins/table-of-contents-plus/) 寫文章自動根據``來分段落做章節索引,很棒的免費工具 119 | 9. [Query Monitor](https://tw.wordpress.org/plugins/query-monitor/) 觀察WordPress活動與資料庫行為的除錯優化工具 120 | 10. [Akeeba Backup for WordPress](https://www.akeebabackup.com/products/akeeba-backup-wordpress.html) 完成開發後的備份轉移有他簡單不少 121 | 122 | 123 | ### Elementor 類 124 | 1. [Elementor Custom Skin](https://wordpress.org/plugins/ele-custom-skin/) 客製化Elementor列表 結構樣式 125 | 126 | 127 | ### 其他 類 128 | 129 | 其實到這個段落,也就是剩下一些比較特殊案例,通常這些都會用另一種外掛的方式:子主題內的 [functions.php](./functions.php) 來控制,不過根據使用的主題與常用的組合都太特殊,這邊僅列出每次都會使用的部分! 130 | 131 | 132 | 再來還有 `wp-config.php` 內會改到的一些控制項 133 | 134 | `define('WP_MEMORY_LIMIT', '256M');` 135 | 136 | > 提升WordPress能夠使用的記憶體資源(常會是購物車或是內建縮圖機制吃資源的主題),可以參考[這裡](https://docs.woocommerce.com/document/increasing-the-wordpress-memory-limit/) 137 | 138 | `define('FS_METHOD', 'direct');` 139 | 140 | > 修正安裝外掛權限問題,如無法修正資料目錄權限時才使用,但這問題還是從伺服器端下手還是比較好 141 | 142 | `define('WP_POST_REVISIONS', 3);` 143 | 144 | > 限制3個文章版本存取,若改成 `false` 則是取消版本管理功能 145 | 146 | `define('DISALLOW_FILE_EDIT',true);` 147 | 148 | > 禁止從後台去對程式檔案直接修改功能 149 | 150 | `define('DISALLOW_FILE_MODS', true);` 151 | 152 | > 關閉新增與修改主題外掛檔案 153 | -------------------------------------------------------------------------------- /class-wc-email-awaiting-delivery-order.php: -------------------------------------------------------------------------------- 1 | id = 'awaiting_delivery_order'; 31 | $this->customer_email = true; 32 | 33 | $this->title = '已出貨訂單'; 34 | $this->description = '通知購買人商品已經出貨的信'; 35 | $this->template_html = 'emails/customer-awaiting-delivery-order.php'; 36 | $this->template_plain = 'emails/plain/customer-awaiting-delivery-order.php'; 37 | $this->placeholders = array( 38 | '{order_date}' => '', 39 | '{order_number}' => '', 40 | ); 41 | 42 | // Triggers for this email. 43 | add_action('woocommerce_order_status_processing_to_awaiting-delivery_notification', array($this, 'trigger'), 10, 2); 44 | add_action('woocommerce_order_status_cancelled_to_awaiting-delivery_notification', array($this, 'trigger'), 10, 2); 45 | add_action('woocommerce_order_status_failed_to_awaiting-delivery_notification', array($this, 'trigger'), 10, 2); 46 | add_action('woocommerce_order_status_on-hold_to_awaiting-delivery_notification', array($this, 'trigger'), 10, 2); 47 | add_action('woocommerce_order_status_pending_to_awaiting-delivery_notification', array($this, 'trigger'), 10, 2); 48 | 49 | // Call parent constructor. 50 | parent::__construct(); 51 | } 52 | 53 | /** 54 | * Get email subject. 55 | * 56 | * @since 3.1.0 57 | * @return string 58 | */ 59 | public function get_default_subject() { 60 | return '商品已出貨,感謝您的惠顧'; 61 | } 62 | 63 | /** 64 | * Get email heading. 65 | * 66 | * @since 3.1.0 67 | * @return string 68 | */ 69 | public function get_default_heading() { 70 | return '商品已出貨,感謝您的惠顧'; 71 | } 72 | 73 | /** 74 | * Trigger the sending of this email. 75 | * 76 | * @param int $order_id The order ID. 77 | * @param WC_Order|false $order Order object. 78 | */ 79 | public function trigger($order_id, $order = false) { 80 | $this->setup_locale(); 81 | 82 | if ($order_id && !is_a($order, 'WC_Order')) { 83 | $order = wc_get_order($order_id); 84 | } 85 | 86 | if (is_a($order, 'WC_Order')) { 87 | $this->object = $order; 88 | $this->recipient = $this->object->get_billing_email(); 89 | $this->placeholders['{order_date}'] = wc_format_datetime($this->object->get_date_created()); 90 | $this->placeholders['{order_number}'] = $this->object->get_order_number(); 91 | } 92 | 93 | if ($this->is_enabled() && $this->get_recipient()) { 94 | $this->send($this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments()); 95 | } 96 | 97 | $this->restore_locale(); 98 | } 99 | 100 | /** 101 | * Get content html. 102 | * 103 | * @return string 104 | */ 105 | public function get_content_html() { 106 | return wc_get_template_html( 107 | $this->template_html, 108 | array( 109 | 'order' => $this->object, 110 | 'email_heading' => $this->get_heading(), 111 | 'additional_content' => $this->get_additional_content(), 112 | 'sent_to_admin' => false, 113 | 'plain_text' => false, 114 | 'email' => $this, 115 | ) 116 | ); 117 | } 118 | 119 | /** 120 | * Get content plain. 121 | * 122 | * @return string 123 | */ 124 | public function get_content_plain() { 125 | return wc_get_template_html( 126 | $this->template_plain, 127 | array( 128 | 'order' => $this->object, 129 | 'email_heading' => $this->get_heading(), 130 | 'additional_content' => $this->get_additional_content(), 131 | 'sent_to_admin' => false, 132 | 'plain_text' => true, 133 | 'email' => $this, 134 | ) 135 | ); 136 | } 137 | 138 | /** 139 | * Default content to show below main email content. 140 | * 141 | * @since 3.7.0 142 | * @return string 143 | */ 144 | public function get_default_additional_content() { 145 | return __('Thanks for using {site_url}!', 'woocommerce'); 146 | } 147 | } 148 | 149 | endif; 150 | 151 | return new WC_Email_Awaiting_Delivery_Order(); 152 | -------------------------------------------------------------------------------- /functions.php: -------------------------------------------------------------------------------- 1 | $rewrite) { 15 | if (false !== strpos($rewrite, 'embed=true')) { 16 | unset($rules[$rule]); 17 | } 18 | } 19 | return $rules; 20 | } 21 | function mxp_disable_functions() { 22 | remove_action('wp_head', 'print_emoji_detection_script', 7); 23 | remove_action('admin_print_scripts', 'print_emoji_detection_script'); 24 | remove_action('wp_print_styles', 'print_emoji_styles'); 25 | remove_action('admin_print_styles', 'print_emoji_styles'); 26 | remove_filter('the_content_feed', 'wp_staticize_emoji'); 27 | remove_filter('comment_text_rss', 'wp_staticize_emoji'); 28 | remove_filter('wp_mail', 'wp_staticize_emoji_for_email'); 29 | // 區塊編輯器嵌入 YT 會使用到 30 | // remove_action('rest_api_init', 'wp_oembed_register_route'); 31 | add_filter('embed_oembed_discover', '__return_false'); 32 | remove_filter('oembed_dataparse', 'wp_filter_oembed_result', 10); 33 | remove_action('wp_head', 'wp_oembed_add_discovery_links'); 34 | remove_action('wp_head', 'wp_oembed_add_host_js'); 35 | add_filter('tiny_mce_plugins', 'disable_embeds_tiny_mce_plugin'); 36 | add_filter('rewrite_rules_array', 'disable_embeds_rewrites'); 37 | remove_filter('pre_oembed_result', 'wp_filter_pre_oembed_result', 10); 38 | add_filter('tiny_mce_plugins', 'mxp_disable_emojis_tinymce'); 39 | remove_action('wp_head', 'feed_links_extra', 3); 40 | remove_action('wp_head', 'feed_links', 2); 41 | } 42 | add_action('init', 'mxp_disable_functions'); 43 | 44 | function mxp_disable_rss_feed_function() { 45 | wp_die(__('無提供 RSS 服務,請返回 首頁!')); 46 | } 47 | add_action('do_feed', 'mxp_disable_rss_feed_function', 1); 48 | add_action('do_feed_rdf', 'mxp_disable_rss_feed_function', 1); 49 | add_action('do_feed_rss', 'mxp_disable_rss_feed_function', 1); 50 | add_action('do_feed_rss2', 'mxp_disable_rss_feed_function', 1); 51 | add_action('do_feed_atom', 'mxp_disable_rss_feed_function', 1); 52 | add_action('do_feed_rss2_comments', 'mxp_disable_rss_feed_function', 1); 53 | add_action('do_feed_atom_comments', 'mxp_disable_rss_feed_function', 1); 54 | 55 | function mxp_admin_menu_modify_for_user() { 56 | global $submenu, $menu, $pagenow; 57 | $user = wp_get_current_user(); 58 | $allowed_roles = array('administrator'); 59 | //不是管理員,都把下面的設定選項移除 60 | if (!array_intersect($allowed_roles, $user->roles)) { 61 | if (isset($submenu['themes.php'])) { 62 | foreach ($submenu['themes.php'] as $index => $menu_item) { 63 | foreach ($menu_item as $value) { 64 | if (strpos($value, 'customize') !== false) { 65 | unset($submenu['themes.php'][$index]); 66 | } 67 | // if (strpos($value, 'edit_theme_options') !== false) { 68 | // unset($submenu['themes.php'][$index]); 69 | // } 70 | } 71 | } 72 | } 73 | if (isset($submenu['options-general.php'])) { 74 | foreach ($submenu['options-general.php'] as $index => $menu_item) { 75 | foreach ($menu_item as $value) { 76 | if (strpos($value, 'menu_editor') !== false) { 77 | unset($submenu['options-general.php'][$index]); 78 | } 79 | if (strpos($value, 'adminimize-options') !== false) { 80 | unset($submenu['options-general.php'][$index]); 81 | } 82 | if (strpos($value, 'settings-user-role-editor.php') !== false) { 83 | unset($submenu['options-general.php'][$index]); 84 | } 85 | } 86 | } 87 | } 88 | if (isset($submenu['users.php'])) { 89 | foreach ($submenu['users.php'] as $index => $menu_item) { 90 | foreach ($menu_item as $value) { 91 | if (strpos($value, 'users-user-role-editor.php') !== false) { 92 | unset($submenu['users.php'][$index]); 93 | } 94 | } 95 | } 96 | } 97 | } 98 | } 99 | add_action('admin_init', 'mxp_admin_menu_modify_for_user', 100); 100 | 101 | // 給網站角色賦權的方法 102 | function mxp_add_theme_caps() { 103 | $roles = array('editor', 'contributor', 'author', 'shop_manager'); 104 | foreach ($roles as $key => $role) { 105 | //取得授權角色 106 | if ($role = get_role($role)) { 107 | //開通權限,對權限控管,可以補上預設的權限來避免設定疏失 108 | $role->add_cap('unfiltered_html'); 109 | $role->add_cap('edit_theme_options'); 110 | //開通 https://tw.wordpress.org/plugins/contact-form-cfdb7/ 這外掛使用權限 111 | $role->add_cap('cfdb7_access'); 112 | //開通 https://tw.wordpress.org/plugins/contact-form-entries/ 這外掛使用權限 113 | $role->add_cap('vxcf_leads_edit_entries'); 114 | $role->add_cap('vxcf_leads_edit_settings'); 115 | $role->add_cap('vxcf_leads_read_entries'); 116 | $role->add_cap('vxcf_leads_read_license'); 117 | $role->add_cap('vxcf_leads_read_settings'); 118 | $role->add_cap('vx_crmperks_view_plugins'); 119 | $role->add_cap('vx_crmperks_edit_addons'); 120 | $role->add_cap('vx_crmperks_view_addons'); 121 | //開通 WP Rocket v3.4.4 之後的使用權限 122 | $role->add_cap('rocket_manage_options'); 123 | $role->add_cap('rocket_purge_cache'); 124 | $role->add_cap('rocket_purge_opcache'); 125 | $role->add_cap('rocket_purge_cloudflare_cache'); 126 | $role->add_cap('rocket_preload_cache'); 127 | $role->add_cap('rocket_regenerate_critical_css'); 128 | } 129 | } 130 | } 131 | add_action('admin_init', 'mxp_add_theme_caps'); 132 | 133 | //如果使用CF7,5.1版後都會因為使用reCaptcha導致每頁都會顯示徽章,使用這方法避免 134 | function mxp_remove_recaptcha_badge() { 135 | echo ''; 136 | } 137 | add_action('wp_footer', 'mxp_remove_recaptcha_badge'); 138 | 139 | //使用 instant.page 加速網站頁面讀取 140 | function mxp_add_instant_page() { 141 | echo ''; 142 | } 143 | add_action('wp_footer', 'mxp_add_instant_page'); 144 | 145 | //開放 Post SMTP 設定頁面權限 146 | function mxp_option_page_capability_postman_group($cap) { 147 | return 'edit_pages'; 148 | } 149 | add_filter("option_page_capability_postman_group", 'mxp_option_page_capability_postman_group', 10, 1); 150 | 151 | // 關閉 heartbeat 功能 152 | function mxp_stop_heartbeat_function() { 153 | wp_deregister_script('heartbeat'); 154 | } 155 | add_action('init', 'mxp_stop_heartbeat_function', 1); 156 | 157 | // 關閉後臺的「網站活動」區塊 158 | function mxp_remove_dashboard_widgets() { 159 | remove_meta_box('dashboard_activity', 'dashboard', 'normal'); 160 | } 161 | add_action('wp_dashboard_setup', 'mxp_remove_dashboard_widgets'); 162 | 163 | // 不要發通知給終端使用者 164 | function mxp_wp_send_new_user_notification_to_user($bool, $user) { 165 | return false; 166 | } 167 | add_filter('wp_send_new_user_notification_to_user', 'mxp_wp_send_new_user_notification_to_user', 11, 2); 168 | 169 | // 改收件人為客戶信箱 170 | function mxp_wp_new_user_notification_email_admin($wp_new_user_notification_email_admin, $user, $blogname) { 171 | $wp_new_user_notification_email_admin['to'] = 'im@mxp.tw'; 172 | return $wp_new_user_notification_email_admin; 173 | } 174 | add_filter('wp_new_user_notification_email_admin', 'mxp_wp_new_user_notification_email_admin', 11, 3); 175 | 176 | // 取代管理員信箱為客戶信箱 177 | function mxp_password_change_email($pass_change_email, $user, $userdata) { 178 | $pass_change_email['message'] = str_replace('###ADMIN_EMAIL###', 'im@mxp.tw', $pass_change_email['message']); 179 | 180 | return $pass_change_email; 181 | } 182 | add_filter('password_change_email', 'mxp_password_change_email', 11, 3); 183 | add_filter('email_change_email', 'mxp_password_change_email', 11, 3); 184 | 185 | // 給發信標題全都加上請勿回覆字樣 186 | function mxp_wp_mail_add_subject_prefix($atts) { 187 | $atts['subject'] = "(請勿回覆/No-Reply) " . $atts['subject']; 188 | return $atts; 189 | } 190 | add_filter('wp_mail', 'mxp_wp_mail_add_subject_prefix', 11, 1); 191 | 192 | /** 193 | * 如果VC還沒解開角色授權使用的BUG,可以編輯外掛 plugins/js_composer/include/classes/core/access/class-vc-role-access-controller.php 檔案強制修正 can 方法中處理權限的部份。 194 | **/ 195 | // function ks_custom_post_type_support_vc($support, $type) { 196 | // $allow_post_type = array('post', 'page', 'content_block'); 197 | // return in_array($type, $allow_post_type); 198 | // } 199 | // add_filter('vc_is_valid_post_type_be', 'ks_custom_post_type_support_vc', 999, 2); 200 | 201 | /** 202 | * 下方註解的通用程式碼片段都已經整合到開發工具箱外掛裡,上方為個別網站或是有其他客製化需求的類型才會需要 203 | * https://tw.wordpress.org/plugins/mxp-dev-tools/ 204 | * **/ 205 | // //最佳化主題樣式相關 206 | // function mxp_optimize_theme_setup() { 207 | // //整理head資訊 208 | // remove_action('wp_head', 'wp_generator'); 209 | // remove_action('wp_head', 'wlwmanifest_link'); 210 | // remove_action('wp_head', 'rsd_link'); 211 | // remove_action('wp_head', 'wp_shortlink_wp_head'); 212 | // add_filter('the_generator', '__return_false'); 213 | // //管理員等級的角色不要隱藏 admin bar 214 | // $user = wp_get_current_user(); 215 | // $allowed_roles = array('editor', 'administrator', 'author'); 216 | // if (!array_intersect($allowed_roles, $user->roles)) { 217 | // add_filter('show_admin_bar', '__return_false'); 218 | // } 219 | // remove_action('wp_head', 'feed_links_extra', 3); 220 | // //移除css, js資源載入時的版本資訊 221 | // function remove_version_query($src) { 222 | // if (strpos($src, 'ver=')) { 223 | // $src = remove_query_arg('ver', $src); 224 | // } 225 | // return $src; 226 | // } 227 | // add_filter('style_loader_src', 'remove_version_query', 999); 228 | // add_filter('script_loader_src', 'remove_version_query', 999); 229 | // add_filter('widget_text', 'do_shortcode'); 230 | // // 讓主題支援使用 WC 的方法 231 | // if (class_exists('WooCommerce')) { 232 | // add_theme_support('woocommerce'); 233 | // add_theme_support('wc-product-gallery-zoom'); 234 | // add_theme_support('wc-product-gallery-lightbox'); 235 | // add_theme_support('wc-product-gallery-slider'); 236 | // } 237 | // } 238 | // add_action('after_setup_theme', 'mxp_optimize_theme_setup'); 239 | 240 | // // 停用自己站內的引用 241 | // function mxp_disable_self_ping(&$links) { 242 | // $home = get_option('home'); 243 | // foreach ($links as $l => $link) { 244 | // if (0 === strpos($link, $home)) { 245 | // unset($links[$l]); 246 | // } 247 | // } 248 | // } 249 | // add_action('pre_ping', 'mxp_disable_self_ping'); 250 | 251 | // // 隱藏主題「自訂」的連結方法 252 | // function mxp_remove_customize_link() { 253 | // $user = wp_get_current_user(); 254 | // $allowed_roles = array('administrator'); 255 | // //不是管理員,都把下面的設定選項移除 256 | // if (!array_intersect($allowed_roles, $user->roles)) { 257 | // $customize_url = add_query_arg('return', urlencode(remove_query_arg(wp_removable_query_args(), wp_unslash($_SERVER['REQUEST_URI']))), 'customize.php'); 258 | // remove_submenu_page('themes.php', $customize_url); 259 | // } 260 | // } 261 | // add_action('admin_menu', 'mxp_remove_customize_link'); 262 | 263 | //open content block for VC 264 | // add_filter('content_block_post_type', '__return_true'); 265 | 266 | //使用 content block 時會被當作一般的 post 被安插其他處理,自己包過來用 267 | //ref: https://tw.wordpress.org/plugins/custom-post-widget/ 268 | // function knockers_custom_post_widget_shortcode($atts) { 269 | // extract(shortcode_atts(array( 270 | // 'id' => '', 271 | // 'slug' => '', 272 | // 'class' => 'content_block', 273 | // 'suppress_content_filters' => 'yes', //預設不走 the_content 的事件,避免被其他方法給包過 274 | // 'title' => 'no', 275 | // 'title_tag' => 'h3', 276 | // 'only_img' => 'no', //僅輸出特色圖片連結 277 | // ), $atts)); 278 | 279 | // if ($slug) { 280 | // $block = get_page_by_path($slug, OBJECT, 'content_block'); 281 | // if ($block) { 282 | // $id = $block->ID; 283 | // } 284 | // } 285 | 286 | // $content = ""; 287 | 288 | // if ($id != "") { 289 | // $args = array( 290 | // 'post__in' => array($id), 291 | // 'post_type' => 'content_block', 292 | // ); 293 | 294 | // $content_post = get_posts($args); 295 | 296 | // foreach ($content_post as $post): 297 | // $content .= '
'; 298 | // if ($title === 'yes') { 299 | // $content .= '<' . esc_attr($title_tag) . '>' . $post->post_title . ''; 300 | // } 301 | // if ($suppress_content_filters === 'no') { 302 | // $content .= apply_filters('the_content', $post->post_content); 303 | // } else { 304 | // if (has_shortcode($post->post_content, 'content_block') || has_shortcode($post->post_content, 'ks_content_block')) { 305 | // $content .= $post->post_content; 306 | // } else { 307 | // $content .= do_shortcode($post->post_content); 308 | // } 309 | // } 310 | // $content .= '
'; 311 | // endforeach; 312 | // } 313 | // if ($only_img == "yes") { 314 | // $featured_image = get_the_post_thumbnail_url($id, 'full'); 315 | // return $featured_image ? $featured_image : $content; 316 | // } 317 | // return $content; 318 | // } 319 | // add_shortcode('ks_content_block', 'knockers_custom_post_widget_shortcode'); 320 | 321 | // //阻止縮圖浪費空間 322 | // function mxp_remove_attachment_metadata_size($data, $attachment_id) { 323 | // if (isset($data['sizes'])) { 324 | // // 清空設定的大小,強迫輸出原圖 325 | // $data['sizes'] = array(); 326 | // } 327 | // return $data; 328 | // } 329 | // add_filter('wp_get_attachment_metadata', 'mxp_remove_attachment_metadata_size', 11, 2); 330 | // add_filter('intermediate_image_sizes', '__return_empty_array'); 331 | // add_filter('intermediate_image_sizes_advanced', '__return_empty_array'); 332 | // add_filter('fallback_intermediate_image_sizes', '__return_empty_array'); 333 | // // 禁用 WC 背景縮圖功能 334 | // add_filter('woocommerce_background_image_regeneration', '__return_false'); 335 | // //取消預設 2560 寬高限制 336 | // add_filter('big_image_size_threshold', '__return_false'); 337 | 338 | // //上傳檔案時判斷為圖片時自動加上標題、替代標題、摘要、描述等內容 339 | // function ks_set_image_meta_upon_image_upload($post_ID) { 340 | 341 | // if (wp_attachment_is_image($post_ID)) { 342 | // $my_image_title = get_post($post_ID)->post_title; 343 | // $my_image_title = preg_replace('%\s*[-_\s]+\s*%', ' ', $my_image_title); 344 | // $my_image_title = ucwords(strtolower($my_image_title)); 345 | // $my_image_meta = array( 346 | // 'ID' => $post_ID, 347 | // 'post_title' => $my_image_title, 348 | // 'post_excerpt' => $my_image_title, 349 | // 'post_content' => $my_image_title, 350 | // ); 351 | // update_post_meta($post_ID, '_wp_attachment_image_alt', $my_image_title); 352 | // wp_update_post($my_image_meta); 353 | // } 354 | // } 355 | // add_action('add_attachment', 'ks_set_image_meta_upon_image_upload'); 356 | 357 | // //給CF7啟用短碼機制 358 | // function mxp_cf7_do_shortcode() { 359 | // add_filter('wpcf7_form_elements', 'do_shortcode'); 360 | // add_filter('wpcf7_autop_or_not', '__return_false'); 361 | // } 362 | // add_action('admin_init', 'mxp_cf7_do_shortcode'); 363 | 364 | // //開啟隱私權頁面修改權限 365 | // function add_privacy_page_edit_cap($caps, $cap, $user_id, $args) { 366 | // if ('manage_privacy_options' === $cap) { 367 | // $manage_name = is_multisite() ? 'manage_network' : 'manage_options'; 368 | // $caps = array_diff($caps, [$manage_name]); 369 | // } 370 | // return $caps; 371 | // } 372 | // add_filter('map_meta_cap', 'add_privacy_page_edit_cap', 10, 4); 373 | 374 | // //去除有管理權限之外人的通知訊息 375 | // function mxp_hide_update_msg_non_admins() { 376 | // $user = wp_get_current_user(); 377 | // if (!in_array('administrator', (array) $user->roles)) { 378 | // // non-admin users 379 | // echo ''; 380 | // } 381 | // //隱藏非管理人員的更新通知 382 | // if (!current_user_can('update_core')) { 383 | // remove_action('admin_notices', 'update_nag', 3); 384 | // } 385 | // } 386 | // add_action('admin_head', 'mxp_hide_update_msg_non_admins', 1); 387 | 388 | // //修改「網站遭遇技術性問題」通知信收件人 389 | // function mxp_change_recovery_mode_email($email, $url) { 390 | // $email['to'] = 'im@mxp.tw'; //收件人 391 | // // $email['subject'] //主旨 392 | // // $email['message'] //內文 393 | // // $email['headers'] //信件標頭 394 | // return $email; 395 | // } 396 | // add_filter('recovery_mode_email', 'mxp_change_recovery_mode_email', 11, 2); 397 | 398 | // // 刪除文章前的防呆提醒機制 399 | // function mxp_delete_post_confirm_hook() { 400 | // ?> 401 | // 431 | // =')) { 440 | // // v5.3 後已經內建 https://developer.wordpress.org/reference/classes/wp_image_editor_imagick/maybe_exif_rotate/ 441 | // mxp_apply_new_orientation($file['tmp_name']); 442 | // } 443 | // $is_image = strpos($file['type'], 'image') !== false; 444 | // if ($is_image && $size > $limit) { 445 | // $file['error'] = "上傳的影像大小請小於 {$limit}kb"; 446 | // } 447 | // return $file; 448 | // } 449 | // function mxp_apply_new_orientation($path_to_jpg) { 450 | // // 使用 GD 函式庫,沒的話就算了不處理 451 | // if (!extension_loaded('gd') || 452 | // !function_exists('gd_info') || 453 | // !function_exists('exif_imagetype') || 454 | // !function_exists('imagecreatefromjpeg') || 455 | // !function_exists('exif_read_data') || 456 | // !function_exists('imagerotate') || 457 | // !function_exists('imagejpeg') || 458 | // !function_exists('imagedestroy')) { 459 | // return false; 460 | // } 461 | // if (exif_imagetype($path_to_jpg) == IMAGETYPE_JPEG) { 462 | // $image = @imagecreatefromjpeg($path_to_jpg); 463 | // $exif = exif_read_data($path_to_jpg); 464 | // if (!empty($exif['Orientation'])) { 465 | // switch ($exif['Orientation']) { 466 | // case 3: 467 | // $image = imagerotate($image, 180, 0); 468 | // break; 469 | // case 6: 470 | // $image = imagerotate($image, 90, 0); 471 | // break; 472 | // case 8: 473 | // $image = imagerotate($image, -90, 0); 474 | // break; 475 | // } 476 | // } 477 | // if ($image) { 478 | // imagejpeg($image, $path_to_jpg); 479 | // imagedestroy($image); 480 | // } 481 | // } 482 | // } 483 | // add_filter('wp_handle_upload_prefilter', 'mxp_max_image_size_and_image_orientation'); 484 | 485 | // //預設關閉 XML_RPC 486 | // add_filter('xmlrpc_enabled', '__return_false'); 487 | // //輸出 X-Frame-Options HTTP Header 488 | // add_action('send_headers', 'send_frame_options_header', 10, 0); 489 | // //關閉 HTTP Header 中出現的 Links 490 | // add_filter('oembed_discovery_links', '__return_null'); 491 | // remove_action('wp_head', 'rest_output_link_wp_head', 10); 492 | // remove_action('template_redirect', 'rest_output_link_header', 11); 493 | // remove_action('template_redirect', 'wp_shortlink_header', 11); 494 | // // 關閉 wp-json 首頁顯示的 API 清單 495 | // add_filter('rest_index', function ($response, $request) { 496 | // $new_response = new \WP_REST_Response(); 497 | // // 將空資料設定給 WP_REST_Response 物件 498 | // $new_response->set_data(array()); 499 | // // 回傳 WP_REST_Response 物件 500 | // return $new_response; 501 | // }, 11, 2); 502 | // // 沒登入的使用者都無法呼叫 wp/users 這隻 API。不建議完全封鎖掉,會導致有些後台功能運作失靈 503 | // if (function_exists('is_user_logged_in') && !is_user_logged_in()) { 504 | // add_filter('rest_user_query', '__return_null'); 505 | // add_filter('rest_prepare_user', '__return_null'); 506 | // } 507 | 508 | // function mxp_security_headers($headers) { 509 | // $headers['X-XSS-Protection'] = '1; mode=block'; 510 | // $headers['X-Content-Type-Options'] = 'nosniff'; 511 | // $headers['X-Content-Security-Policy'] = "default-src 'self'; script-src 'self'; connect-src 'self'"; 512 | // $headers['X-Permitted-Cross-Domain-Policies'] = "none"; 513 | // $headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains; preload'; 514 | // return $headers; 515 | // } 516 | // add_filter('wp_headers', 'mxp_security_headers'); 517 | 518 | //內對外請求管制方法 519 | // function mxp_block_external_request($preempt, $parsed_args, $url) { 520 | // $block_urls = array( 521 | // "wpemaillog.com", 522 | // "cdn.aioseo.com", 523 | // "aioseo.com", 524 | // "licensing-cdn.aioseo.com", 525 | // "updates.wpbakery.com", 526 | // "my.yoast.com", 527 | // ); 528 | // // $whitelist_urls = array( 529 | // // "api.wordpress.org", 530 | // // "downloads.wordpress.org", 531 | // // ); 532 | // $request_domain = parse_url($url, PHP_URL_HOST); 533 | // if (in_array($request_domain, $block_urls, true)) { 534 | // return new WP_Error('http_request_block', '不允許的對外請求路徑' . "\n:: {$url}", $url); 535 | // } 536 | // return $preempt; 537 | // } 538 | // add_filter("pre_http_request", "mxp_block_external_request", 11, 3); 539 | 540 | // // 預設不直接更新大版本,僅安全性版本更新 541 | // add_filter('allow_major_auto_core_updates', '__return_false'); 542 | 543 | // // 後台不顯示 Freemius 的提示通知 544 | // add_filter('fs_show_admin_notice_post-smtp', function ($show, $msg) { 545 | // return false; 546 | // }, 11, 2); 547 | 548 | // // 預設不顯示出系統輸出的作者連結與頁面,避免資安問題 549 | // function mxp_hide_author_for_safe($link) { 550 | // return '小編'; 551 | // } 552 | // add_filter('the_author_posts_link', 'mxp_hide_author_for_safe', 11, 1); 553 | 554 | // 預設作者的連結都不顯示 555 | // function mxp_hide_author_link($link, $author_id, $author_nicename) { 556 | // return '#'; 557 | // } 558 | // add_filter('author_link', 'mxp_hide_author_link', 3, 100); 559 | 560 | // // 顯示使用者編號 561 | // function add_user_id_column($columns) { 562 | // $columns['mxp_user_id'] = 'ID'; 563 | // return $columns; 564 | // } 565 | // add_filter('manage_users_columns', 'add_user_id_column'); 566 | // function show_user_id_column_content($value, $column_name, $user_id) { 567 | // if ('mxp_user_id' === $column_name) { 568 | // return $user_id; 569 | // } 570 | // return $value; 571 | // } 572 | // add_action('manage_users_custom_column', 'show_user_id_column_content', 10, 3); 573 | 574 | // function mxp_add_custom_fields_for_user_search($user_query) { 575 | // global $wpdb; 576 | // if (isset($user_query->query_vars['search']) && !empty($user_query->query_vars['search']) && is_admin() && isset($_REQUEST['s']) && !empty($_REQUEST['s'])) { 577 | // $search_term = str_replace('*', '%', $user_query->query_vars['search']); 578 | // $user_query->query_from .= " JOIN {$wpdb->usermeta} usrmeta ON usrmeta.user_id = {$wpdb->users}.ID"; 579 | // // 加入客製化欄位條件 580 | // $custom_fields = array( 581 | // 'billing_address_1', 582 | // 'billing_address_2', 583 | // 'billing_city', 584 | // 'billing_company', 585 | // 'billing_country', 586 | // 'billing_email', 587 | // 'billing_first_name', 588 | // 'billing_last_name', 589 | // 'billing_phone', 590 | // 'billing_postcode', 591 | // 'billing_state', 592 | // 'billing_video_playback_gmail', 593 | // 'description', 594 | // 'first_name', 595 | // 'ks_address', 596 | // 'ks_birth_year', 597 | // 'ks_cell', 598 | // 'ks_city_district', 599 | // 'ks_cn_first_name', 600 | // 'ks_cn_last_name', 601 | // 'ks_country', 602 | // 'ks_country_code', 603 | // 'ks_en_first_name', 604 | // 'ks_en_last_name', 605 | // 'ks_gender', 606 | // 'ks_google_addr', 607 | // 'ks_line_id', 608 | // 'ks_postal_code', 609 | // 'ks_recommender', 610 | // 'ks_state_province_city', 611 | // 'ks_wechat_id', 612 | // 'last_name', 613 | // 'nickname', 614 | // 'shipping_address_1', 615 | // 'shipping_address_2', 616 | // 'shipping_city', 617 | // 'shipping_company', 618 | // 'shipping_country', 619 | // 'shipping_email', 620 | // 'shipping_first_name', 621 | // 'shipping_last_name', 622 | // 'shipping_method', 623 | // 'shipping_phone', 624 | // 'shipping_postcode', 625 | // 'shipping_state', 626 | // 'twitter', 627 | // ); 628 | // $where = array(); 629 | // foreach ($custom_fields as $key => $field) { 630 | // $where[] = "usrmeta.meta_key LIKE '" . esc_sql($field) . "' AND usrmeta.meta_value LIKE '" . esc_sql($search_term) . "'"; 631 | // } 632 | // // 組合搜尋條件 633 | // $user_query->query_where .= ' OR (' . implode(' OR ', $where) . ')'; 634 | // } 635 | // } 636 | // add_action('pre_user_query', 'mxp_add_custom_fields_for_user_search', 11, 1); 637 | 638 | // // 每次更新完後檢查 xmlrpc.php 還在不在,在就砍掉,避免後患 639 | // function mxp_after_upgrade_hook($upgrader_object, $options) { 640 | // // if ($options['action'] == 'update' && $options['type'] == 'plugin') { 641 | // // foreach ($options['plugins'] as $each_plugin) { 642 | // // if ($each_plugin == $current_plugin_path_name) { 643 | // // // 比對當前外掛的更新 644 | // // } 645 | // // } 646 | // // } 647 | // if (file_exists(ABSPATH . 'xmlrpc.php')) { 648 | // unlink(ABSPATH . 'xmlrpc.php'); 649 | // } 650 | // if (file_exists(ABSPATH . 'wp-admin/install.php')) { 651 | // unlink(ABSPATH . 'wp-admin/install.php'); 652 | // } 653 | // } 654 | // add_action('upgrader_process_complete', 'mxp_after_upgrade_hook', 10, 2); 655 | 656 | // // 取消站內的全球大頭貼功能,全改為預設大頭貼 657 | // function mxp_pre_get_empty_avatar_data($args, $id_or_email) { 658 | // //email md5 wordpress.gravatar@mxp.tw 659 | // if (is_ssl()) { 660 | // $url = 'https://secure.gravatar.com/avatar/fcb68cd8e48d96e2bc306b17422e34ea?s=192&d=mm&r=g'; 661 | // } else { 662 | // $url = 'http://0.gravatar.com/avatar/fcb68cd8e48d96e2bc306b17422e34ea?s=192&d=mm&r=g'; 663 | // } 664 | // $args['url'] = $url; 665 | // return $args; 666 | // } 667 | // add_filter('pre_get_avatar_data', 'mxp_pre_get_empty_avatar_data', PHP_INT_MAX, 2); 668 | 669 | // //遮蔽所有留言中留言人提供的網址 670 | // function mxp_get_comment_author_url($url, $id, $comment) { 671 | // return ""; 672 | // } 673 | // add_filter('get_comment_author_url', 'mxp_get_comment_author_url', 11, 3); 674 | 675 | // // 使用者登入後轉址回指定位置 676 | // function mxp_redirect_to_after_login() { 677 | // if (!is_user_logged_in()) { 678 | // $redirect_to = isset($_GET['redirect_to']) ? $_GET['redirect_to'] : ''; 679 | // if (strpos($redirect_to, get_site_url()) === 0) { 680 | // setcookie('mxp_redirect_to', $redirect_to); 681 | // setcookie('mxp_redirect_to_count', 0); 682 | // } 683 | // } else { 684 | // if (isset($_COOKIE['mxp_redirect_to']) && $_COOKIE['mxp_redirect_to'] != '' && isset($_COOKIE['mxp_redirect_to_count']) && $_COOKIE['mxp_redirect_to_count'] != 0) { 685 | // setcookie("mxp_redirect_to", "", time() - 3600); 686 | // setcookie('mxp_redirect_to_count', 1); 687 | // wp_redirect($_COOKIE['mxp_redirect_to']); 688 | // exit; 689 | // } 690 | // } 691 | // } 692 | // add_action('template_redirect', 'mxp_redirect_to_after_login', -1); 693 | 694 | /** 695 | ** 選擇性新增程式碼片段 696 | **/ 697 | // // 如果要關閉某些 CPT 的區塊編輯器可以啟用此區 698 | // function mxp_disable_gutenberg($current_status, $post_type) { 699 | // if ($post_type === 'page') { 700 | // return false; 701 | // } 702 | // return $current_status; 703 | // } 704 | // add_filter('use_block_editor_for_post_type', 'mxp_disable_gutenberg', 11, 2); 705 | // //修正管理後台頁尾顯示 706 | // function dashboard_footer_design() { 707 | // echo 'Design by Knockers'; 708 | // } 709 | // add_filter('admin_footer_text', 'dashboard_footer_design'); 710 | // //修正管理後台頁尾顯示 711 | // function dashboard_footer_developer() { 712 | // echo '
Developed by 一介資男'; 713 | // } 714 | // add_filter('admin_footer_text', 'dashboard_footer_developer'); 715 | // //修正管理後台顯示 716 | // function clean_my_admin_head() { 717 | // $screen = get_current_screen(); 718 | // $str = ''; 719 | // if (is_admin() && ($screen->id == 'dashboard')) { 720 | // $str .= ''; 721 | // } 722 | // echo $str; 723 | // } 724 | // add_action('admin_head', 'clean_my_admin_head'); 725 | 726 | // 補上客製化檔案格式支援 727 | // function mxp_custom_mime_types($mime_types) { 728 | // $mime_types['zip'] = 'application/zip'; 729 | // $mime_types['rar'] = 'application/x-rar-compressed'; 730 | // $mime_types['tar'] = 'application/x-tar'; 731 | // $mime_types['gz'] = 'application/x-gzip'; 732 | // $mime_types['gzip'] = 'application/x-gzip'; 733 | // $mime_types['tiff'] = 'image/tiff'; 734 | // $mime_types['tif'] = 'image/tiff'; 735 | // $mime_types['bmp'] = 'image/bmp'; 736 | // $mime_types['svg'] = 'image/svg+xml'; 737 | // $mime_types['psd'] = 'image/vnd.adobe.photoshop'; 738 | // $mime_types['ai'] = 'application/postscript'; 739 | // $mime_types['indd'] = 'application/x-indesign'; 740 | // $mime_types['eps'] = 'application/postscript'; 741 | // $mime_types['rtf'] = 'application/rtf'; 742 | // $mime_types['txt'] = 'text/plain'; 743 | // $mime_types['wav'] = 'audio/x-wav'; 744 | // $mime_types['csv'] = 'text/csv'; 745 | // $mime_types['xml'] = 'application/xml'; 746 | // $mime_types['flv'] = 'video/x-flv'; 747 | // $mime_types['swf'] = 'application/x-shockwave-flash'; 748 | // $mime_types['vcf'] = 'text/x-vcard'; 749 | // $mime_types['html'] = 'text/html'; 750 | // $mime_types['htm'] = 'text/html'; 751 | // $mime_types['css'] = 'text/css'; 752 | // $mime_types['js'] = 'application/javascript'; 753 | // $mime_types['json'] = 'application/json'; 754 | // $mime_types['ico'] = 'image/x-icon'; 755 | // $mime_types['otf'] = 'application/x-font-otf'; 756 | // $mime_types['ttf'] = 'application/x-font-ttf'; 757 | // $mime_types['woff'] = 'application/x-font-woff'; 758 | // $mime_types['ics'] = 'text/calendar'; 759 | // $mime_types['ppt'] = 'application/vnd.ms-powerpoint'; 760 | // $mime_types['pot'] = 'application/vnd.ms-powerpoint'; 761 | // $mime_types['pps'] = 'application/vnd.ms-powerpoint'; 762 | // $mime_types['pptx'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'; 763 | // $mime_types['doc'] = 'application/msword'; 764 | // $mime_types['docx'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; 765 | // return $mime_types; 766 | // } 767 | // add_filter('upload_mimes', 'mxp_custom_mime_types', 1, 1); 768 | 769 | // 修正針對 ico 上傳的解析問題 770 | // function mxp_getimagesize_mimes_to_exts($img_arr) { 771 | // $img_arr['image/x-icon'] = 'ico'; 772 | // $img_arr['image/vnd.microsoft.icon'] = 'ico'; 773 | // return $img_arr; 774 | // } 775 | // add_filter('getimagesize_mimes_to_exts', 'mxp_getimagesize_mimes_to_exts', 11, 1); 776 | 777 | // function logger($file, $data) { 778 | // file_put_contents( 779 | // ABSPATH . "wp-content/{$file}.txt", 780 | // '===' . date('Y-m-d H:i:s', time()) . '===' . PHP_EOL . $data . PHP_EOL, 781 | // FILE_APPEND 782 | // ); 783 | // } 784 | // //引用 WooCommerce 設定程式碼片段 785 | // include dirname(__FILE__). '/wc-settings.php'; 786 | // //引用 Knockers 網站狀態追蹤程式碼片段 787 | // include dirname(__FILE__) . '/ks_server_checker.php'; 788 | 789 | if (!function_exists('wpdb_bulk_insert')) { 790 | //一次大量新增資料的資料庫操作方法 Ref: https://gist.github.com/pauln/884e1a229d439640fbe35e848852fe0b 791 | function wpdb_bulk_insert($table, $rows) { 792 | global $wpdb; 793 | 794 | // Extract column list from first row of data 795 | $columns = array_keys($rows[0]); 796 | asort($columns); 797 | $columnList = '`' . implode('`, `', $columns) . '`'; 798 | 799 | // Start building SQL, initialise data and placeholder arrays 800 | $sql = "INSERT INTO `$table` ($columnList) VALUES\n"; 801 | $placeholders = array(); 802 | $data = array(); 803 | 804 | // Build placeholders for each row, and add values to data array 805 | foreach ($rows as $row) { 806 | ksort($row); 807 | $rowPlaceholders = array(); 808 | 809 | foreach ($row as $key => $value) { 810 | $data[] = $value; 811 | $rowPlaceholders[] = is_numeric($value) ? '%d' : '%s'; 812 | } 813 | 814 | $placeholders[] = '(' . implode(', ', $rowPlaceholders) . ')'; 815 | } 816 | 817 | // Stitch all rows together 818 | $sql .= implode(",\n", $placeholders); 819 | 820 | // Run the query. Returns number of affected rows. 821 | return $wpdb->query($wpdb->prepare($sql, $data)); 822 | } 823 | } 824 | 825 | if (!function_exists('mxp_get_error_backtrace')) { 826 | // WordPress PHP 偵錯用的方法 827 | function mxp_get_error_backtrace($last_error_file = __FILE__, $for_irc = false) { 828 | 829 | $backtrace = debug_backtrace(0); 830 | $call_path = array(); 831 | foreach ($backtrace as $bt_key => $call) { 832 | if (!isset($call['args'])) { 833 | $call['args'] = array(''); 834 | } 835 | 836 | if (in_array($call['function'], array(__FUNCTION__, 'mxp_get_error_backtrace'))) { 837 | continue; 838 | } 839 | 840 | $path = ''; 841 | if (!$for_irc) { 842 | $path = isset($call['file']) ? str_replace(ABSPATH, '', $call['file']) : ''; 843 | $path .= isset($call['line']) ? ':' . $call['line'] : ''; 844 | } 845 | 846 | if (isset($call['class'])) { 847 | $call_type = $call['type'] ? $call['type'] : '???'; 848 | $path .= " {$call['class']}{$call_type}{$call['function']}()"; 849 | } elseif (in_array($call['function'], array('do_action', 'apply_filters'))) { 850 | if (is_object($call['args'][0]) && !method_exists($call['args'][0], '__toString')) { 851 | $path .= " {$call['function']}(Object)"; 852 | } elseif (is_array($call['args'][0])) { 853 | $path .= " {$call['function']}(Array)"; 854 | } else { 855 | $path .= " {$call['function']}('{$call['args'][0]}')"; 856 | } 857 | } elseif (in_array($call['function'], array('include', 'include_once', 'require', 'require_once'))) { 858 | $file = 0 == $bt_key ? $last_error_file : $call['args'][0]; 859 | $path .= " {$call['function']}('" . str_replace(ABSPATH, '', $file) . "')"; 860 | } else { 861 | $path .= " {$call['function']}()"; 862 | } 863 | 864 | $call_path[] = trim($path); 865 | } 866 | 867 | return implode(', ' . PHP_EOL, $call_path); 868 | } 869 | } -------------------------------------------------------------------------------- /ks_server_checker.php: -------------------------------------------------------------------------------- 1 | > functions.php && chown www:www * -R 4 | ** 更新方式: curl https://raw.githubusercontent.com/nczz/work_with_wordpress/master/ks_server_checker.php > ks_server_checker.php 5 | **/ 6 | 7 | function mxp_wp_diagnostic_info() { 8 | global $table_prefix; 9 | global $wpdb; 10 | $diagnostic_info = array(); 11 | /* 12 | * WordPress & Server Environment 13 | */ 14 | 15 | $diagnostic_info['site_url'] = site_url(); 16 | $diagnostic_info['home_url'] = home_url(); 17 | $diagnostic_info['WordPress'] = get_bloginfo('version', 'display'); 18 | $diagnostic_info['Web_Server'] = !empty($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : ''; 19 | $diagnostic_info['PHP'] = ""; 20 | if (function_exists('phpversion')) { 21 | $diagnostic_info['PHP'] = phpversion(); 22 | } 23 | $diagnostic_info['MySQL'] = $wpdb->db_version(); 24 | $diagnostic_info['ext_mysqli'] = empty($wpdb->use_mysqli) ? 'no' : 'yes'; 25 | $diagnostic_info['PHP_Memory_Limit'] = ""; 26 | if (function_exists('ini_get')) { 27 | $diagnostic_info['PHP_Memory_Limit'] = ini_get('memory_limit'); 28 | } 29 | $diagnostic_info['WP_MEMORY_LIMIT'] = WP_MEMORY_LIMIT; 30 | $diagnostic_info['Memory_Usage'] = size_format(memory_get_usage(true)); 31 | 32 | $diagnostic_info['WP_HTTP_BLOCK_EXTERNAL'] = ""; 33 | if (!defined('WP_HTTP_BLOCK_EXTERNAL') || !WP_HTTP_BLOCK_EXTERNAL) { 34 | $diagnostic_info['WP_MEMORY_LIMIT'] = "none"; 35 | } else { 36 | $accessible_hosts = (defined('WP_ACCESSIBLE_HOSTS')) ? WP_ACCESSIBLE_HOSTS : ''; 37 | if (empty($accessible_hosts)) { 38 | $diagnostic_info['WP_ACCESSIBLE_HOSTS'] = "all"; 39 | } else { 40 | $diagnostic_info['WP_ACCESSIBLE_HOSTS'] = $accessible_hosts; 41 | } 42 | } 43 | $diagnostic_info['WP_Locale'] = get_locale(); 44 | $diagnostic_info['WP_UPLOADS_BY_MY'] = get_option('uploads_use_yearmonth_folders') ? 'Enabled' : 'Disabled'; 45 | $diagnostic_info['WP_DEBUG'] = (defined('WP_DEBUG') && WP_DEBUG) ? 'Yes' : 'No'; 46 | $diagnostic_info['WP_DEBUG_LOG'] = (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) ? 'Yes' : 'No'; 47 | $diagnostic_info['WP_DEBUG_DISPLAY'] = (defined('WP_DEBUG_DISPLAY') && WP_DEBUG_DISPLAY) ? 'Yes' : 'No'; 48 | $diagnostic_info['SCRIPT_DEBUG'] = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? 'Yes' : 'No'; 49 | $diagnostic_info['WP_MAX_UPLOAD_SIZE'] = size_format(wp_max_upload_size()); 50 | $diagnostic_info['PHP_max_execution_time'] = ""; 51 | if (function_exists('ini_get')) { 52 | $diagnostic_info['PHP_max_execution_time'] = ini_get('max_execution_time'); 53 | } 54 | $diagnostic_info['WP_CRON'] = (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON) ? 'Disabled' : 'Enabled'; 55 | 56 | $diagnostic_info['allow_url_fopen'] = ""; 57 | $allow_url_fopen = ini_get('allow_url_fopen'); 58 | if (empty($allow_url_fopen)) { 59 | $diagnostic_info['allow_url_fopen'] = "Disabled"; 60 | } else { 61 | $diagnostic_info['allow_url_fopen'] = "Enabled"; 62 | } 63 | 64 | $diagnostic_info['OpenSSL'] = ""; 65 | if (defined('OPENSSL_VERSION_TEXT')) { 66 | $diagnostic_info['OpenSSL'] = OPENSSL_VERSION_TEXT; 67 | } else { 68 | $diagnostic_info['OpenSSL'] = "Disabled"; 69 | } 70 | 71 | $diagnostic_info['PHP_GD'] = ""; 72 | if (extension_loaded('gd') && function_exists('gd_info')) { 73 | $gd_info = gd_info(); 74 | $diagnostic_info['PHP_GD'] = isset($gd_info['GD Version']) ? $gd_info['GD Version'] : 'Enabled'; 75 | } else { 76 | $diagnostic_info['PHP_GD'] = 'Disabled'; 77 | } 78 | 79 | $diagnostic_info['Imagick'] = ""; 80 | if (extension_loaded('imagick') && class_exists('Imagick') && class_exists('ImagickPixel')) { 81 | $diagnostic_info['Imagick'] = 'Enabled'; 82 | } else { 83 | $diagnostic_info['Imagick'] = 'Disabled'; 84 | } 85 | 86 | /* 87 | * Settings 88 | */ 89 | 90 | $theme_info = wp_get_theme(); 91 | $diagnostic_info['Active_Theme'] = array(); 92 | $diagnostic_info['Parent_Theme'] = array(); 93 | if (!empty($theme_info) && is_a($theme_info, 'WP_Theme')) { 94 | if (file_exists($theme_info->get_stylesheet_directory())) { 95 | $diagnostic_info['Active_Theme']['Name'] = $theme_info->get('Name'); 96 | $diagnostic_info['Active_Theme']['Version'] = $theme_info->get('Version'); 97 | $diagnostic_info['Active_Theme']['Folder'] = $theme_info->get_stylesheet(); 98 | } 99 | if (is_child_theme()) { 100 | $parent_info = $theme_info->parent(); 101 | if (!empty($parent_info) && is_a($parent_info, 'WP_Theme')) { 102 | $diagnostic_info['Parent_Theme']['Name'] = $parent_info->get('Name'); 103 | $diagnostic_info['Parent_Theme']['Version'] = $parent_info->get('Version'); 104 | $diagnostic_info['Parent_Theme']['Folder'] = $parent_info->get_stylesheet(); 105 | } 106 | } else { 107 | $diagnostic_info['Parent_Theme']['Name'] = ""; 108 | $diagnostic_info['Parent_Theme']['Version'] = ""; 109 | $diagnostic_info['Parent_Theme']['Folder'] = ""; 110 | } 111 | } 112 | 113 | $diagnostic_info['Active_Plugins'] = array(); 114 | $diagnostic_info['MU_Plugins'] = array(); 115 | $active_plugins = (array) get_option('active_plugins', array()); 116 | if (is_multisite()) { 117 | $network_active_plugins = wp_get_active_network_plugins(); 118 | $active_plugins = array_map(function ($path) { 119 | $plugin_dir = trailingslashit(WP_PLUGIN_DIR); 120 | $plugin = str_replace($plugin_dir, '', $path); 121 | return $plugin; 122 | }, $network_active_plugins); 123 | } 124 | 125 | foreach ($active_plugins as $plugin) { 126 | $diagnostic_info['Active_Plugins'][] = mxp_get_plugin_details(WP_PLUGIN_DIR . '/' . $plugin); 127 | } 128 | 129 | $mu_plugins = wp_get_mu_plugins(); 130 | if ($mu_plugins) { 131 | foreach ($mu_plugins as $mu_plugin) { 132 | $diagnostic_info['MU_Plugins'][] = mxp_get_plugin_details($mu_plugin); 133 | } 134 | } 135 | 136 | return $diagnostic_info; 137 | } 138 | 139 | function mxp_get_plugin_details($plugin_path, $suffix = '') { 140 | if (!function_exists('get_plugin_data')) { 141 | require_once ABSPATH . 'wp-admin/includes/plugin.php'; 142 | } 143 | $plugin_data = get_plugin_data($plugin_path); 144 | if (empty($plugin_data['Name'])) { 145 | return basename($plugin_path); 146 | } 147 | return array("Name" => $plugin_data['Name'], "Version" => $plugin_data['Version'], "Author" => strip_tags($plugin_data['AuthorName'])); 148 | } 149 | 150 | function mxp_add_cron_schedules($schedules) { 151 | $schedules['ks_custom_2h'] = array( 152 | 'interval' => 7200, // 兩小時檢查一次變化 153 | 'display' => "兩小時一次", 154 | ); 155 | return $schedules; 156 | } 157 | add_filter('cron_schedules', 'mxp_add_cron_schedules'); 158 | 159 | function mxp_cronjob_register() { 160 | if (!wp_next_scheduled('mxp_plugin_update_cron')) { 161 | wp_schedule_event(time(), 'ks_custom_2h', 'mxp_plugin_update_cron'); 162 | } 163 | } 164 | add_action('wp', 'mxp_cronjob_register'); 165 | 166 | function mxp_cronjob_do_action() { 167 | $diagnostic_info = mxp_wp_diagnostic_info(); 168 | $req = array( 169 | 'domain' => parse_url($diagnostic_info['site_url'], PHP_URL_HOST), 170 | 'php' => $diagnostic_info['PHP'], 171 | 'mysql' => $diagnostic_info['MySQL'], 172 | 'wp' => $diagnostic_info['WordPress'], 173 | 'theme' => $diagnostic_info['Active_Theme']['Name'] . "_" . $diagnostic_info['Active_Theme']['Version'], 174 | 'parent_theme' => $diagnostic_info['Parent_Theme']['Name'] . "_" . $diagnostic_info['Parent_Theme']['Version'], 175 | 'json' => json_encode($diagnostic_info), 176 | 'version' => '1.2', 177 | 'knockers' => 0, //站點分類 178 | 'email' => 'im@Mxp.tw', //比對異常時的通知人,可改其他通知人。「,」分隔多重聯絡人,總長度不得超過 100 字元 179 | ); 180 | $response = wp_remote_post('https://api.undo.im/wp-json/mxp_knockers/v1/app/register', array( 181 | 'method' => 'POST', 182 | 'timeout' => 10, 183 | 'redirection' => 5, 184 | 'httpversion' => '1.1', 185 | 'blocking' => false, 186 | 'headers' => array('Content-Type' => 'application/json'), 187 | 'body' => wp_json_encode($req), 188 | 'cookies' => array(), 189 | 'sslverify' => false, 190 | 'data_format' => 'body', 191 | ) 192 | ); 193 | 194 | if (is_wp_error($response)) { 195 | $error_message = $response->get_error_message(); 196 | if (defined('WP_DEBUG') && WP_DEBUG == true) { 197 | error_log($req['domain'] . "_CRONJOB_ERROR: $error_message"); 198 | } 199 | } 200 | if (defined('WP_DEBUG') && WP_DEBUG == true) { 201 | error_log($req['domain'] . "_CRONJOB_DONE"); 202 | } 203 | } 204 | add_action('mxp_plugin_update_cron', 'mxp_cronjob_do_action'); 205 | -------------------------------------------------------------------------------- /plugins_list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Custom Post Type UI", 4 | "slug": "custom-post-type-ui", 5 | "hosted": "wordpress.org", 6 | "description": "建立新型態的文章內容,用來客製化不同內建文章/頁面的「標題」、「內文」結構形式。", 7 | "dlink": "", 8 | "version": "" 9 | }, 10 | { 11 | "name": "Advanced Custom Fields", 12 | "slug": "advanced-custom-fields", 13 | "hosted": "wordpress.org", 14 | "description": "當完成安裝與建立客製化新型態後,會發現那還只是跟內建的文章/頁面結構一樣,只是在後台選單上多了一個新的選項,這時候要搭配這款客製化定義欄位的工具,指定新型態格式,自定義該新型態內容使用哪些欄位。", 15 | "dlink": "", 16 | "version": "" 17 | }, 18 | { 19 | "name": "WP Better Permalinks", 20 | "slug": "wp-better-permalinks", 21 | "hosted": "wordpress.org", 22 | "description": "可為網站設定更容易檢索的自訂永久連結,製作出 /post_type/term/post_name 的永久連結結構。", 23 | "dlink": "", 24 | "version": "" 25 | }, 26 | { 27 | "name": "User Role Editor", 28 | "slug": "user-role-editor", 29 | "hosted": "wordpress.org", 30 | "description": "從最基礎的權限(Capabilities)出發,可以配置給單獨的使用者或是角色,介面稍微陽春。", 31 | "dlink": "", 32 | "version": "" 33 | }, 34 | { 35 | "name": "Groups", 36 | "slug": "groups", 37 | "hosted": "wordpress.org", 38 | "description": "群組的概念分配權限,提供群組的模式管理註冊者", 39 | "dlink": "", 40 | "version": "" 41 | }, 42 | { 43 | "name": "User Switching", 44 | "slug": "user-switching", 45 | "hosted": "wordpress.org", 46 | "description": "更改使用者權限後會希望能夠預覽使用者權限的設定是否正確,使用這外掛快速切換", 47 | "dlink": "", 48 | "version": "" 49 | }, 50 | { 51 | "name": "Username Changer", 52 | "slug": "username-changer", 53 | "hosted": "wordpress.org", 54 | "description": "更改使用者名稱的小工具", 55 | "dlink": "", 56 | "version": "" 57 | }, 58 | { 59 | "name": "Nav Menu Roles", 60 | "slug": "nav-menu-roles", 61 | "hosted": "wordpress.org", 62 | "description": "根據角色權限切換不同選單展示", 63 | "dlink": "", 64 | "version": "" 65 | }, 66 | { 67 | "name": "Admin Menu Editor", 68 | "slug": "admin-menu-editor", 69 | "hosted": "wordpress.org", 70 | "description": "有時候因為要調整出客戶可讀性與方便性高的選單時就會需要他,擁有直接修改選單顯示名稱與排序功能", 71 | "dlink": "", 72 | "version": "" 73 | }, 74 | { 75 | "name": "Adminimize", 76 | "slug": "adminimize", 77 | "hosted": "wordpress.org", 78 | "description": "WP 內建的權限設定沒有針對角色還有更細的增刪修查限制功能,同個權限下的使用者如果有希望不同的操作介面,就會需要使用這外掛去「隱藏」,做一些髒髒der事。", 79 | "dlink": "", 80 | "version": "" 81 | }, 82 | { 83 | "name": "Custom Post Widget", 84 | "slug": "custom-post-widget", 85 | "hosted": "wordpress.org", 86 | "description": "同樣屬於挖空部分內容提供客戶有固定區域修改文案的功能,結合小工具(widgets)或是自行開發延伸的方式都很方便", 87 | "dlink": "", 88 | "version": "" 89 | }, 90 | { 91 | "name": "Duplicate Post", 92 | "slug": "duplicate-post", 93 | "hosted": "wordpress.org", 94 | "description": "重製內容的工具", 95 | "dlink": "", 96 | "version": "" 97 | }, 98 | { 99 | "name": "Category Order and Taxonomy Terms Order", 100 | "slug": "taxonomy-terms-order", 101 | "hosted": "wordpress.org", 102 | "description": "客製化分類的排序", 103 | "dlink": "", 104 | "version": "" 105 | }, 106 | { 107 | "name": "Custom Category/Post Type Post order", 108 | "slug": "custom-post-order-category", 109 | "hosted": "wordpress.org", 110 | "description": "客製化文章的排序", 111 | "dlink": "", 112 | "version": "" 113 | }, 114 | { 115 | "name": "Contact Form 7", 116 | "slug": "contact-form-7", 117 | "hosted": "wordpress.org", 118 | "description": "說到表單一定要用這款", 119 | "dlink": "", 120 | "version": "" 121 | }, 122 | { 123 | "name": "Contact Form Entries – Contact Form 7, WPforms and more", 124 | "slug": "contact-form-entries", 125 | "hosted": "wordpress.org", 126 | "description": "CF7 & WPforms 的表單資料庫", 127 | "dlink": "", 128 | "version": "" 129 | }, 130 | { 131 | "name": "Event Organiser", 132 | "slug": "event-organiser", 133 | "hosted": "wordpress.org", 134 | "description": "客戶公開活動、行程等這類需求也不少,需要的包涵時間地點等資訊也滿完整的", 135 | "dlink": "", 136 | "version": "" 137 | }, 138 | { 139 | "name": "Another Events Calendar", 140 | "slug": "another-events-calendar", 141 | "hosted": "wordpress.org", 142 | "description": "最近發現的外掛,跟上面有的功能差不多", 143 | "dlink": "", 144 | "version": "" 145 | }, 146 | { 147 | "name": "Post SMTP Mailer", 148 | "slug": "post-smtp", 149 | "hosted": "wordpress.org", 150 | "description": "根據客戶需求,架構簡單的可以使用 SMTP 方式寄信,GMail 每日 250 封的限制其實滿夠大部分形象網站,這款支援使用Gmail API為比較正確的請求方式,建議!", 151 | "dlink": "", 152 | "version": "" 153 | }, 154 | { 155 | "name": "FluentSMTP – WP SMTP Plugin with Amazon SES, SendGrid, MailGun, Postmark, Google and Any SMTP Provider", 156 | "slug": "fluent-smtp", 157 | "hosted": "wordpress.org", 158 | "description": "宣稱決不有廣告與付費方案的發信工具", 159 | "dlink": "", 160 | "version": "" 161 | }, 162 | { 163 | "name": "WooCommerce", 164 | "slug": "woocommerce", 165 | "hosted": "wordpress.org", 166 | "description": "WordPress 常用購物車", 167 | "dlink": "", 168 | "version": "" 169 | }, 170 | { 171 | "name": "Disable WooCommerce Bloat", 172 | "slug": "disable-dashboard-for-woocommerce", 173 | "hosted": "wordpress.org", 174 | "description": "WooCommerce 購物車減肥工具", 175 | "dlink": "", 176 | "version": "" 177 | }, 178 | { 179 | "name": "WooCommerce Extended Coupon Features", 180 | "slug": "woocommerce-auto-added-coupons", 181 | "hosted": "wordpress.org", 182 | "description": "免費但也夠強的折扣外掛,組合上也很厲害", 183 | "dlink": "", 184 | "version": "" 185 | }, 186 | { 187 | "name": "WooCommerce Print Invoice & Delivery Note", 188 | "slug": "woocommerce-delivery-notes", 189 | "hosted": "wordpress.org", 190 | "description": "訂單中新增揀貨單功能,方便客戶管理出貨用", 191 | "dlink": "", 192 | "version": "" 193 | }, 194 | { 195 | "name": "Enhanced Ecommerce Google Analytics Plugin for WooCommerce", 196 | "slug": "enhanced-e-commerce-for-woocommerce-store", 197 | "hosted": "wordpress.org", 198 | "description": "客戶於銷售行為中如果行銷團隊需要安插轉換碼與相對應GA商務的結合時可以使用", 199 | "dlink": "", 200 | "version": "" 201 | }, 202 | { 203 | "name": "Custom Related Products for WooCommerce", 204 | "slug": "custom-related-products-for-woocommerce", 205 | "hosted": "wordpress.org", 206 | "description": "WooCommerce預設相關商品會抓取同分類下的所有商品,這外掛可以讓你自訂在前台呈現不同分類的相關商品", 207 | "dlink": "", 208 | "version": "" 209 | }, 210 | { 211 | "name": "Add Admin JavaScript", 212 | "slug": "add-admin-javascript", 213 | "hosted": "wordpress.org", 214 | "description": "以類權限部分,第一層客製化使用第一、二款外掛沒問題,如要針對功能內頁內的項目修正,除了直接 Hard Code 就是使用 JavaScript 來修正顯示", 215 | "dlink": "", 216 | "version": "" 217 | }, 218 | { 219 | "name": "Add Admin CSS", 220 | "slug": "add-admin-css", 221 | "hosted": "wordpress.org", 222 | "description": "同上的處理外掛", 223 | "dlink": "", 224 | "version": "" 225 | }, 226 | { 227 | "name": "Breadcrumb NavXT", 228 | "slug": "breadcrumb-navxt", 229 | "hosted": "wordpress.org", 230 | "description": "很強大的導覽標記管理工具,通常用來藏一些不該出現的集合頁連結", 231 | "dlink": "", 232 | "version": "" 233 | }, 234 | { 235 | "name": "Yoast SEO", 236 | "slug": "wordpress-seo", 237 | "hosted": "wordpress.org", 238 | "description": "功能強大SEO工具、適合進階。搭配 Hide SEO Bloat最佳化", 239 | "dlink": "", 240 | "version": "" 241 | }, 242 | { 243 | "name": "All in One SEO Pack", 244 | "slug": "all-in-one-seo-pack", 245 | "hosted": "wordpress.org", 246 | "description": "同上,功能算強、簡單適合客戶", 247 | "dlink": "", 248 | "version": "" 249 | }, 250 | { 251 | "name": "Rank Math SEO", 252 | "slug": "seo-by-rank-math", 253 | "hosted": "wordpress.org", 254 | "description": "SEO 外掛新秀,對比上面兩家提供的免費功能較多。", 255 | "dlink": "", 256 | "version": "" 257 | }, 258 | { 259 | "name": "Disable Comments", 260 | "slug": "disable-comments", 261 | "hosted": "wordpress.org", 262 | "description": "關閉網站留言相關功能的工具", 263 | "dlink": "", 264 | "version": "" 265 | }, 266 | { 267 | "name": "Facebook Pixel by PixelYourSite - Events, WooCommerce & Easy Digital Downloads", 268 | "slug": "pixelyoursite", 269 | "hosted": "wordpress.org", 270 | "description": "FB 像素碼埋放工具", 271 | "dlink": "", 272 | "version": "" 273 | }, 274 | { 275 | "name": "DuracellTomi's Google Tag Manager for WordPress", 276 | "slug": "duracelltomi-google-tag-manager", 277 | "hosted": "wordpress.org", 278 | "description": "協助將 GTM 整合進 WordPress 網站的工具。能夠將許多常用的中繼資料 (meta) 如文章作者、發表日期、文章分類推送進資料層變數,行銷人員必備。", 279 | "dlink": "", 280 | "version": "" 281 | }, 282 | { 283 | "name": "Schema", 284 | "slug": "schema", 285 | "hosted": "wordpress.org", 286 | "description": "增加結構化標記的 JSON-LD ,提升收錄效果,這款為自動化較適合懶人。", 287 | "dlink": "", 288 | "version": "" 289 | }, 290 | { 291 | "name": "All In One Schema.org Rich Snippets", 292 | "slug": "all-in-one-schemaorg-rich-snippets", 293 | "hosted": "wordpress.org", 294 | "description": "增加結構化標記的 JSON-LD ,提升收錄效果,這款為自動化較適合有追求並且願意逐項填寫內容的有心人。", 295 | "dlink": "", 296 | "version": "" 297 | }, 298 | { 299 | "name": "Transients Manager", 300 | "slug": "transients-manager", 301 | "hosted": "wordpress.org", 302 | "description": "管理背景執行程序的好幫手,在網站覺得慢的時候使用它來清理一下", 303 | "dlink": "", 304 | "version": "" 305 | }, 306 | { 307 | "name": "Reset Customizer", 308 | "slug": "reset-customizer", 309 | "hosted": "wordpress.org", 310 | "description": "換子佈景主題時使用這套把原本主題的設定給匯出與匯入當前子主題。", 311 | "dlink": "", 312 | "version": "" 313 | }, 314 | { 315 | "name": "Heartbeat Control", 316 | "slug": "heartbeat-control", 317 | "hosted": "wordpress.org", 318 | "description": "同上為最佳化外掛,管理 WordPress AJAX 發生的行為,過多的 AJAX 發生會導致網站執行效率變得很差,相關資訊可以點此參考", 319 | "dlink": "", 320 | "version": "" 321 | }, 322 | { 323 | "name": "Disable All WordPress Updates", 324 | "slug": "disable-wordpress-updates", 325 | "hosted": "wordpress.org", 326 | "description": "當客戶屬於非使用手動安裝的WordPress架構,通常會有個隱藏危險:自動更新,開發的網站如有版本相容問題就必須使用這工具,改為手動更新", 327 | "dlink": "", 328 | "version": "" 329 | }, 330 | { 331 | "name": "Disable REST API", 332 | "slug": "disable-json-api", 333 | "hosted": "wordpress.org", 334 | "description": "停用 REST API 的工具", 335 | "dlink": "", 336 | "version": "" 337 | }, 338 | { 339 | "name": "Hide Broken Shortcodes", 340 | "slug": "hide-broken-shortcodes", 341 | "hosted": "wordpress.org", 342 | "description": "網站的多樣性有很大的功臣就是短碼,但如果有短碼失效就會顯示於前端,可能是客戶不能理解的地方,用此外掛避免", 343 | "dlink": "", 344 | "version": "" 345 | }, 346 | { 347 | "name": "Enhanced Media Library", 348 | "slug": "enhanced-media-library", 349 | "hosted": "wordpress.org", 350 | "description": "強化多媒體庫的使用,包含下分類與標籤", 351 | "dlink": "", 352 | "version": "" 353 | }, 354 | { 355 | "name": "Auto Upload Images", 356 | "slug": "auto-upload-images", 357 | "hosted": "wordpress.org", 358 | "description": "自動抓取內容中外部圖片回本機儲存的工具。", 359 | "dlink": "", 360 | "version": "" 361 | }, 362 | { 363 | "name": "Warp iMagick Image Compressor + Convert to WebP for WordPress", 364 | "slug": "warp-imagick", 365 | "hosted": "https://warp-imagick.pagespeed.club/", 366 | "description": "支援 WebP 與 JPG/PNG 圖片縮圖,全部服務都在外掛內完成,專業的外掛!檢查更新", 367 | "dlink": "https://warp-imagick.pagespeed.club/?action=download&slug=warp-imagick", 368 | "version": "1.10" 369 | }, 370 | { 371 | "name": "Force Regenerate Thumbnails", 372 | "slug": "force-regenerate-thumbnails", 373 | "hosted": "wordpress.org", 374 | "description": "重新產生縮圖", 375 | "dlink": "", 376 | "version": "" 377 | }, 378 | { 379 | "name": "AddToAny Share Buttons", 380 | "slug": "add-to-any", 381 | "hosted": "wordpress.org", 382 | "description": "為文章或頁面加上分享功能", 383 | "dlink": "", 384 | "version": "" 385 | }, 386 | { 387 | "name": "Query Monitor", 388 | "slug": "query-monitor", 389 | "hosted": "wordpress.org", 390 | "description": "觀察WordPress活動與資料庫行為的除錯最佳化,開發的必備工具", 391 | "dlink": "", 392 | "version": "" 393 | }, 394 | { 395 | "name": "Plugin Toggle", 396 | "slug": "plugin-toggle", 397 | "hosted": "wordpress.org", 398 | "description": "快速開關外掛的好用工具!", 399 | "dlink": "", 400 | "version": "" 401 | }, 402 | { 403 | "name": "Developer Debug Tools", 404 | "slug": "dev-debug-tools", 405 | "hosted": "wordpress.org", 406 | "description": "網站開發環境除錯工具", 407 | "dlink": "", 408 | "version": "" 409 | }, 410 | { 411 | "name": "System Dashboard", 412 | "slug": "system-dashboard", 413 | "hosted": "wordpress.org", 414 | "description": "網站開發與伺服器環境除錯觀看用工具,非常詳盡!", 415 | "dlink": "", 416 | "version": "" 417 | }, 418 | { 419 | "name": "WP Armour – Honeypot Anti Spam", 420 | "slug": "honeypot", 421 | "hosted": "wordpress.org", 422 | "description": "蜜罐方法防止機器人工具", 423 | "dlink": "", 424 | "version": "" 425 | }, 426 | { 427 | "name": "Simple Cloudflare Turnstile — 使用方便的 CAPTCHA 替代解決方案", 428 | "slug": "simple-cloudflare-turnstile", 429 | "hosted": "wordpress.org", 430 | "description": "Cloudflare 的 CAPTCHA 工具", 431 | "dlink": "", 432 | "version": "" 433 | }, 434 | { 435 | "name": "Index WP MySQL For Speed", 436 | "slug": "index-wp-mysql-for-speed", 437 | "hosted": "wordpress.org", 438 | "description": "WP MySQL 資料庫最佳化工具", 439 | "dlink": "", 440 | "version": "" 441 | }, 442 | { 443 | "name": "Wordfence Security", 444 | "slug": "wordfence", 445 | "hosted": "wordpress.org", 446 | "description": "應該算是 WordPress 外掛領域中防駭下載量第一個外掛了,是真的很強很精準也很多資訊,比較厚", 447 | "dlink": "", 448 | "version": "" 449 | }, 450 | { 451 | "name": "Anti-Malware Security and Brute-Force Firewall", 452 | "slug": "gotmls", 453 | "hosted": "wordpress.org", 454 | "description": "防毒、弱點掃描、後門、資料庫注入與威脅的偵測工具。身兼暴力攻擊的防火牆(Apache 專屬)", 455 | "dlink": "", 456 | "version": "" 457 | }, 458 | { 459 | "name": "Maintenance Mode", 460 | "slug": "lj-maintenance-mode", 461 | "hosted": "wordpress.org", 462 | "description": "切換維護模式的好幫手", 463 | "dlink": "", 464 | "version": "" 465 | }, 466 | { 467 | "name": "Simple History", 468 | "slug": "simple-history", 469 | "hosted": "wordpress.org", 470 | "description": "記錄用戶操作過程", 471 | "dlink": "", 472 | "version": "" 473 | }, 474 | { 475 | "name": "SULly", 476 | "slug": "sully", 477 | "hosted": "wordpress.org", 478 | "description": "記錄網站外掛升級的歷史紀錄,出問題時恢復版本可以查詢。", 479 | "dlink": "", 480 | "version": "" 481 | }, 482 | { 483 | "name": "Stream", 484 | "slug": "stream", 485 | "hosted": "wordpress.org", 486 | "description": "網站操作的歷史紀錄,出問題時可以知道找誰負責。", 487 | "dlink": "", 488 | "version": "" 489 | }, 490 | { 491 | "name": "Change Author Link Structure", 492 | "slug": "change-author-link-structure", 493 | "hosted": "wordpress.org", 494 | "description": "改變網站作者連結,避免資安風險。", 495 | "dlink": "", 496 | "version": "" 497 | }, 498 | { 499 | "name": "Temporary Login Without Password", 500 | "slug": "temporary-login-without-password", 501 | "hosted": "wordpress.org", 502 | "description": "提供臨時登入查看系統後台的功能", 503 | "dlink": "", 504 | "version": "" 505 | }, 506 | { 507 | "name": "Clearfy – disable unused features", 508 | "slug": "clearfy", 509 | "hosted": "wordpress.org", 510 | "description": "整理不需要使用的一些功能用", 511 | "dlink": "", 512 | "version": "" 513 | }, 514 | { 515 | "name": "Hide login page, Hide wp admin – stop attack on login page", 516 | "slug": "hide-login-page", 517 | "hosted": "wordpress.org", 518 | "description": "更改後台登入路徑", 519 | "dlink": "", 520 | "version": "" 521 | }, 522 | { 523 | "name": "WP Rollback", 524 | "slug": "wp-rollback", 525 | "hosted": "wordpress.org", 526 | "description": "升級外掛出事就靠它幫忙還原前一個版本號了", 527 | "dlink": "", 528 | "version": "" 529 | }, 530 | { 531 | "name": "WP Downgrade", 532 | "slug": "wp-downgrade", 533 | "hosted": "wordpress.org", 534 | "description": "指定 WordPress core 核心版本號升級、降級工具", 535 | "dlink": "", 536 | "version": "" 537 | }, 538 | { 539 | "name": "Akeeba Backup for WordPress", 540 | "slug": "akeebabackupwp", 541 | "hosted": "akeebabackup.com", 542 | "description": "完成開發後的備份轉移有他簡單不少。通常可以搭配資料庫內容取代工具操作。", 543 | "dlink": "https://cdn.akeeba.com/downloads/backupwp/7.7.2/akeebabackupwp-7.7.2-core.zip", 544 | "version": "7.7.2" 545 | }, 546 | { 547 | "name": "Elementor", 548 | "slug": "elementor", 549 | "hosted": "wordpress.org", 550 | "description": "頁面編輯器", 551 | "dlink": "", 552 | "version": "" 553 | }, 554 | { 555 | "name": "Elementor Custom Skin", 556 | "slug": "ele-custom-skin", 557 | "hosted": "wordpress.org", 558 | "description": "客製化Elementor列表 結構樣式", 559 | "dlink": "", 560 | "version": "" 561 | }] -------------------------------------------------------------------------------- /wc-settings.php: -------------------------------------------------------------------------------- 1 | version, $version, ">=")) { 6 | return true; 7 | } 8 | } 9 | return false; 10 | } 11 | 12 | function mxp_remove_default_useless_fields($fields) { 13 | //最上層總管欄位的方法 14 | //unset($fields['country']); 3.3.5 後不能遮蓋國家,會影響判斷運費 15 | unset($fields['last_name']); 16 | unset($fields['address_2']); 17 | //unset($fields['city']);//用來儲存下拉選單資料 18 | //unset($fields['state']);//用來儲存下拉選單資料 19 | return $fields; 20 | } 21 | add_filter('woocommerce_default_address_fields', 'mxp_remove_default_useless_fields', 999, 1); 22 | 23 | function mxp_custom_override_my_account_billing_fields($fields) { 24 | //補上欄位定義就好,不需指定權重 25 | $fields['billing_company'] = array( 26 | 'type' => 'text', 27 | 'label' => '公司名稱', 28 | 'placeholder' => '公司名稱', 29 | 'required' => false, 30 | 'class' => array('form-row-wide'), 31 | 'label_class' => array(), 32 | 'clear' => true, 33 | 'options' => array(), 34 | ); 35 | $fields['billing_company_tax_id'] = array( 36 | 'type' => 'text', 37 | 'label' => '公司統編', 38 | 'placeholder' => '公司統編', 39 | 'required' => false, 40 | 'class' => array('form-row-wide'), 41 | 'label_class' => array(), 42 | 'clear' => true, 43 | 'options' => array(), 44 | ); 45 | //指定排序權重 46 | $fields['billing_first_name']['priority'] = 1; 47 | $fields['billing_state']['priority'] = 2; 48 | $fields['billing_city']['priority'] = 3; 49 | $fields['billing_postcode']['priority'] = 4; 50 | $fields['billing_address_1']['priority'] = 5; 51 | 52 | $billing_display_fields_order = array( 53 | "billing_first_name" => "", 54 | "billing_state" => "", 55 | "billing_city" => "", 56 | "billing_postcode" => "", 57 | "billing_address_1" => "", 58 | "billing_email" => "", 59 | "billing_phone" => "", 60 | "billing_company" => "", 61 | "billing_company_tax_id" => "", 62 | ); 63 | foreach ($billing_display_fields_order as $field_key => $field_value) { 64 | if ($field_key != "") { 65 | $billing_display_fields_order[$field_key] = $fields[$field_key]; 66 | } 67 | } 68 | 69 | return $billing_display_fields_order; 70 | } 71 | add_filter('woocommerce_billing_fields', 'mxp_custom_override_my_account_billing_fields', 999, 1); 72 | 73 | function mxp_custom_override_my_account_shipping_fields($fields) { 74 | //補上欄位定義就好,不需指定權重 75 | $fields['shipping_phone'] = array( 76 | 'label' => '收件人電話', 77 | 'placeholder' => '收件人手機格式:0912345678', 78 | 'required' => true, 79 | 'class' => array('form-row-wide'), 80 | 'clear' => true, 81 | 'type' => 'text', 82 | 'label_class' => array(), 83 | 'options' => array(), 84 | ); 85 | $fields['shipping_email'] = array( 86 | 'label' => '收件人信箱', 87 | 'placeholder' => '請輸入收件人信箱', 88 | 'required' => false, 89 | 'class' => array('form-row-wide'), 90 | 'clear' => true, 91 | 'type' => 'text', 92 | 'label_class' => array(), 93 | 'options' => array(), 94 | ); 95 | $fields['shipping_company'] = array( 96 | 'label' => '公司名稱', 97 | 'placeholder' => '公司名稱', 98 | 'required' => false, 99 | 'class' => array('form-row-wide'), 100 | 'clear' => true, 101 | 'type' => 'text', 102 | 'label_class' => array(), 103 | 'options' => array(), 104 | ); 105 | $fields['shipping_company_tax_id'] = array( 106 | 'label' => '公司統編', 107 | 'placeholder' => '公司統編', 108 | 'required' => false, 109 | 'class' => array('form-row-wide'), 110 | 'clear' => true, 111 | 'type' => 'text', 112 | 'label_class' => array(), 113 | 'options' => array(), 114 | ); 115 | //指定排序 116 | $fields['shipping_first_name']['priority'] = 1; 117 | $fields['shipping_state']['priority'] = 2; 118 | $fields['shipping_city']['priority'] = 3; 119 | $fields['shipping_postcode']['priority'] = 4; 120 | $fields['shipping_address_1']['priority'] = 5; 121 | 122 | $shipping_display_fields_order = array( 123 | "shipping_first_name" => "", 124 | "shipping_state" => "", 125 | "shipping_city" => "", 126 | "shipping_postcode" => "", 127 | "shipping_address_1" => "", 128 | "shipping_email" => "", 129 | "shipping_phone" => "", 130 | "shipping_company" => "", 131 | "shipping_company_tax_id" => "", 132 | ); 133 | foreach ($shipping_display_fields_order as $field_key => $field_value) { 134 | if ($field_key != "") { 135 | $shipping_display_fields_order[$field_key] = $fields[$field_key]; 136 | } 137 | } 138 | return $shipping_display_fields_order; 139 | } 140 | add_filter('woocommerce_shipping_fields', 'mxp_custom_override_my_account_shipping_fields', 999, 1); 141 | 142 | function mxp_custom_override_checkout_fields($fields) { 143 | $fields['billing']['billing_first_name'] = array( 144 | 'label' => '姓名', 145 | 'placeholder' => '姓名', 146 | 'required' => true, 147 | 'class' => array('form-row-first'), 148 | 'clear' => true, 149 | 'type' => 'text', 150 | 'label_class' => array(), 151 | 'options' => array(), 152 | 'priority' => 1, 153 | ); 154 | $fields['billing']['billing_state'] = array( 155 | 'label' => '縣/市', 156 | 'placeholder' => '縣/市', 157 | 'required' => true, 158 | 'class' => array('form-row-wide'), 159 | 'clear' => true, 160 | 'type' => 'text', 161 | 'label_class' => array(), 162 | 'options' => array(), 163 | 'priority' => 2, 164 | ); 165 | $fields['billing']['billing_city'] = array( 166 | 'label' => '鄉鎮市區', 167 | 'placeholder' => '鄉鎮市區', 168 | 'required' => true, 169 | 'class' => array('form-row-wide'), 170 | 'clear' => true, 171 | 'type' => 'text', 172 | 'label_class' => array(), 173 | 'options' => array(), 174 | 'priority' => 3, 175 | ); 176 | $fields['billing']['billing_address_1'] = array( 177 | 'label' => '地址', 178 | 'placeholder' => '地址', 179 | 'required' => true, 180 | 'class' => array('form-row-wide'), 181 | 'clear' => true, 182 | 'type' => 'text', 183 | 'label_class' => array(), 184 | 'options' => array(), 185 | 'priority' => 4, 186 | ); 187 | $fields['billing']['billing_postcode'] = array( 188 | 'label' => '郵遞區號', 189 | 'placeholder' => '郵遞區號', 190 | 'required' => false, 191 | 'class' => array('form-row-wide'), 192 | 'clear' => true, 193 | 'type' => 'text', 194 | 'label_class' => array(), 195 | 'options' => array(), 196 | 'priority' => 5, 197 | ); 198 | $fields['billing']['billing_email'] = array( 199 | 'label' => '結帳信箱', 200 | 'placeholder' => '請輸入信箱', 201 | 'required' => true, 202 | 'class' => array('form-row-wide'), 203 | 'clear' => true, 204 | 'type' => 'text', 205 | 'label_class' => array(), 206 | 'options' => array(), 207 | 'priority' => 6, 208 | ); 209 | $fields['billing']['billing_phone'] = array( 210 | 'label' => '電話', 211 | 'placeholder' => '手機格式:0912345678', 212 | 'required' => true, 213 | 'class' => array('form-row-wide'), 214 | 'clear' => true, 215 | 'type' => 'text', 216 | 'label_class' => array(), 217 | 'options' => array(), 218 | 'priority' => 7, 219 | ); 220 | $fields['billing']['billing_tax_checkbox'] = array( 221 | 'label' => '是否需要統編(三聯發票)?', 222 | 'required' => false, 223 | 'class' => array('form-row-wide'), 224 | 'type' => 'checkbox', 225 | 'label_class' => array('woocommerce-form__label woocommerce-form__label-for-checkbox checkbox'), 226 | 'input_class' => array('woocommerce-form__input woocommerce-form__input-checkbox input-checkbox'), 227 | 'priority' => 8, 228 | ); 229 | $fields['billing']['billing_company'] = array( 230 | 'label' => '公司名稱(抬頭)', 231 | 'placeholder' => '公司名稱(抬頭)', 232 | 'required' => false, 233 | 'class' => array('form-row-wide', 'hidden'), 234 | 'clear' => true, 235 | 'type' => 'text', 236 | 'label_class' => array(), 237 | 'options' => array(), 238 | 'priority' => 9, 239 | ); 240 | $fields['billing']['billing_company_tax_id'] = array( 241 | 'label' => '公司統編', 242 | 'placeholder' => '公司統編', 243 | 'required' => false, 244 | 'class' => array('form-row-wide', 'hidden'), 245 | 'clear' => true, 246 | 'type' => 'text', 247 | 'label_class' => array(), 248 | 'options' => array(), 249 | 'priority' => 10, 250 | ); 251 | 252 | $sm = wc_get_chosen_shipping_method_ids(); 253 | if (!empty($sm)) { 254 | if ($sm[0] == 'ecpay_shipping') { 255 | $cvsInfo = WC()->session->get('ecpay_cvs_info'); 256 | $fields['billing']['purchaserStore'] = array( 257 | 'label' => '超商取貨門市名稱', 258 | 'default' => isset($cvsInfo['CVSStoreName']) ? sanitize_text_field($cvsInfo['CVSStoreName']) : '', 259 | 'required' => true, 260 | 'class' => array('hidden'), 261 | ); 262 | $fields['billing']['purchaserAddress'] = array( 263 | 'label' => '超商取貨門市地址', 264 | 'default' => isset($cvsInfo['CVSAddress']) ? sanitize_text_field($cvsInfo['CVSAddress']) : '', 265 | 'required' => true, 266 | 'class' => array('hidden'), 267 | ); 268 | $fields['billing']['purchaserPhone'] = array( 269 | 'label' => '超商取貨門市電話', 270 | 'default' => isset($cvsInfo['CVSTelephone']) ? sanitize_text_field($cvsInfo['CVSTelephone']) : '', 271 | 'class' => array('hidden'), 272 | ); 273 | $fields['billing']['CVSStoreID'] = array( 274 | 'label' => '超商取貨門市代號', 275 | 'default' => isset($cvsInfo['CVSStoreID']) ? sanitize_text_field($cvsInfo['CVSStoreID']) : '', 276 | 'required' => true, 277 | 'class' => array('hidden'), 278 | ); 279 | } 280 | } 281 | $fields['shipping']['shipping_first_name'] = array( 282 | 'label' => '姓名', 283 | 'placeholder' => '姓名', 284 | 'required' => true, 285 | 'class' => array('form-row-first'), 286 | 'clear' => true, 287 | 'type' => 'text', 288 | 'label_class' => array(), 289 | 'options' => array(), 290 | 'priority' => 1, 291 | ); 292 | $fields['shipping']['shipping_state'] = array( 293 | 'label' => '縣/市', 294 | 'placeholder' => '縣/市', 295 | 'required' => true, 296 | 'class' => array('form-row-wide'), 297 | 'clear' => true, 298 | 'type' => 'text', 299 | 'label_class' => array(), 300 | 'options' => array(), 301 | 'priority' => 2, 302 | ); 303 | $fields['shipping']['shipping_city'] = array( 304 | 'label' => '鄉鎮市區', 305 | 'placeholder' => '鄉鎮市區', 306 | 'required' => true, 307 | 'class' => array('form-row-wide'), 308 | 'clear' => true, 309 | 'type' => 'text', 310 | 'label_class' => array(), 311 | 'options' => array(), 312 | 'priority' => 3, 313 | ); 314 | $fields['shipping']['shipping_address_1'] = array( 315 | 'label' => '地址', 316 | 'placeholder' => '地址', 317 | 'required' => true, 318 | 'class' => array('form-row-wide'), 319 | 'clear' => true, 320 | 'type' => 'text', 321 | 'label_class' => array(), 322 | 'options' => array(), 323 | 'priority' => 4, 324 | ); 325 | $fields['shipping']['shipping_email'] = array( 326 | 'label' => '收件人信箱', 327 | 'placeholder' => '請輸入收件人信箱', 328 | 'required' => false, 329 | 'class' => array('form-row-wide'), 330 | 'clear' => true, 331 | 'type' => 'text', 332 | 'label_class' => array(), 333 | 'options' => array(), 334 | 'priority' => 5, 335 | ); 336 | $fields['shipping']['shipping_phone'] = array( 337 | 'label' => '收件人電話', 338 | 'placeholder' => '收件人手機格式:0912345678', 339 | 'required' => true, 340 | 'class' => array('form-row-wide'), 341 | 'clear' => true, 342 | 'type' => 'text', 343 | 'label_class' => array(), 344 | 'options' => array(), 345 | 'priority' => 6, 346 | ); 347 | $fields['shipping']['shipping_postcode'] = array( 348 | 'label' => '郵遞區號', 349 | 'placeholder' => '郵遞區號', 350 | 'required' => false, 351 | 'class' => array('form-row-wide'), 352 | 'clear' => true, 353 | 'type' => 'text', 354 | 'label_class' => array(), 355 | 'options' => array(), 356 | 'priority' => 7, 357 | ); 358 | // $fields['shipping']['shipping_company'] = array( 359 | // 'label' => '公司名稱', 360 | // 'placeholder' => '公司名稱', 361 | // 'required' => false, 362 | // 'class' => array('form-row-wide'), 363 | // 'clear' => true, 364 | // 'type' => 'text', 365 | // 'label_class' => array(), 366 | // 'options' => array(), 367 | // 'priority' => 8, 368 | // ); 369 | // $fields['shipping']['shipping_company_tax_id'] = array( 370 | // 'label' => '公司統編', 371 | // 'placeholder' => '公司統編', 372 | // 'required' => false, 373 | // 'class' => array('form-row-wide'), 374 | // 'clear' => true, 375 | // 'type' => 'text', 376 | // 'label_class' => array(), 377 | // 'options' => array(), 378 | // 'priority' => 9, 379 | // ); 380 | 381 | //reorder fields 382 | $billing_fields = array(); 383 | $shipping_fields = array(); 384 | $billing_display_fields_order = array( 385 | "billing_first_name", 386 | "billing_state", 387 | "billing_city", 388 | "billing_address_1", 389 | "billing_postcode", 390 | "billing_email", 391 | "billing_phone", 392 | "billing_tax_checkbox", 393 | "billing_company", 394 | "billing_company_tax_id", 395 | ); 396 | if (!empty($sm)) { 397 | if ($sm[0] == 'ecpay_shipping') { 398 | $billing_display_fields_order[] = "purchaserStore"; 399 | $billing_display_fields_order[] = "purchaserAddress"; 400 | $billing_display_fields_order[] = "purchaserPhone"; 401 | $billing_display_fields_order[] = "CVSStoreID"; 402 | } 403 | } 404 | foreach ($billing_display_fields_order as $field) { 405 | $billing_fields[$field] = $fields["billing"][$field]; 406 | } 407 | $shipping_display_fields_order = array( 408 | "shipping_first_name", 409 | "shipping_state", 410 | "shipping_city", 411 | "shipping_address_1", 412 | "shipping_postcode", 413 | "shipping_email", 414 | "shipping_phone", 415 | // "shipping_company", 416 | // "shipping_company_tax_id", 417 | ); 418 | foreach ($shipping_display_fields_order as $field) { 419 | $shipping_fields[$field] = $fields["shipping"][$field]; 420 | } 421 | $fields["billing"] = $billing_fields; 422 | $fields["shipping"] = $shipping_fields; 423 | return $fields; 424 | } 425 | add_filter('woocommerce_checkout_fields', 'mxp_custom_override_checkout_fields', 999, 1); 426 | 427 | function mxp_custom_checkout_field_display_admin_order_billing_meta($order) { 428 | $display_meta = '

開立發票: ' . (get_post_meta($order->get_id(), 'billing_tax_checkbox', true) == 1 ? '是' : '否') . '

'; 429 | $display_meta .= '

公司名稱: ' . get_post_meta($order->get_id(), '_billing_company', true) . '

'; 430 | $display_meta .= '

公司統編: ' . get_post_meta($order->get_id(), '_billing_company_tax_id', true) . '

'; 431 | echo $display_meta; 432 | } 433 | add_action('woocommerce_admin_order_data_after_billing_address', 'mxp_custom_checkout_field_display_admin_order_billing_meta', 10, 1); 434 | 435 | function mxp_woocommerce_save_account_details_required_fields($fields) { 436 | return array( 437 | 'account_first_name' => __('First name', 'woocommerce'), 438 | 'account_display_name' => __('Display name', 'woocommerce'), 439 | 'account_email' => __('Email address', 'woocommerce'), 440 | ); 441 | } 442 | add_filter('woocommerce_save_account_details_required_fields', 'mxp_woocommerce_save_account_details_required_fields', 11, 1); 443 | 444 | function mxp_custom_checkout_field_display_admin_order_shipping_meta($order) { 445 | $display_meta = ''; 446 | // $display_meta .= '

公司名稱: ' . get_post_meta($order->get_id(), '_shipping_company', true) . '

'; 447 | // $display_meta .= '

公司統編: ' . get_post_meta($order->get_id(), '_shipping_company_tax_id', true) . '

'; 448 | $display_meta .= '

收貨人手機: ' . get_post_meta($order->get_id(), '_shipping_phone', true) . '

'; 449 | $display_meta .= '

收貨人信箱: ' . get_post_meta($order->get_id(), '_shipping_email', true) . '

'; 450 | echo $display_meta; 451 | } 452 | add_action('woocommerce_admin_order_data_after_shipping_address', 'mxp_custom_checkout_field_display_admin_order_shipping_meta', 10, 1); 453 | 454 | function mxp_checkout_page_footer() { 455 | if (is_checkout() || is_account_page()): 456 | ?> 457 | 569 | $rate) { 578 | if ('free_shipping' === $rate->method_id) { 579 | $free = array(); 580 | $free[$rate_id] = $rate; 581 | break; 582 | } 583 | if ('flat_rate' === $rate->method_id) { 584 | if ($rate->cost == 0) { 585 | $free = array(); 586 | $rate->label = '免運費'; 587 | $free[$rate_id] = $rate; 588 | break; 589 | } 590 | } 591 | } 592 | return !empty($free) ? $free : $rates; 593 | } 594 | add_filter('woocommerce_package_rates', 'hide_shipping_when_free_is_available', 100, 1); 595 | 596 | //購物車自動更新數量 597 | function mxp_auto_cart_update_qty_script() { 598 | ?> 599 | 607 | $column) { 622 | // $ordered_columns[$key] = $column; 623 | // if ('order_date' == $key) { 624 | // $ordered_columns['order_notes'] = '備註'; 625 | // } 626 | // } 627 | // return $ordered_columns; 628 | // } 629 | 630 | // function custom_shop_order_list_column_content($column) { 631 | // global $post, $the_order; 632 | // $customer_note = $post->post_excerpt; 633 | 634 | // if ($column == 'order_notes') { 635 | // if ($the_order->get_customer_note()) { 636 | // echo '' . __('Yes', 'woocommerce') . ''; 637 | // } 638 | 639 | // if ($post->comment_count) { 640 | 641 | // $latest_notes = wc_get_order_notes(array( 642 | // 'order_id' => $post->ID, 643 | // 'limit' => 1, 644 | // 'orderby' => 'date_created_gmt', 645 | // )); 646 | 647 | // $latest_note = current($latest_notes); 648 | 649 | // if (isset($latest_note->content) && 1 == $post->comment_count) { 650 | // echo '' . __('Yes', 'woocommerce') . ''; 651 | // } elseif (isset($latest_note->content)) { 652 | // // translators: %d: notes count 653 | // echo '' . sprintf(_n('Plus %d other note', 'Plus %d other notes', ($post->comment_count - 1), 'woocommerce'), $post->comment_count - 1) . '') . '">' . __('Yes', 'woocommerce') . ''; 654 | // } else { 655 | // // translators: %d: notes count 656 | // echo '' . __('Yes', 'woocommerce') . ''; 657 | // } 658 | // } 659 | // } 660 | // } 661 | 662 | // // 設定樣式 663 | // function add_custom_order_status_actions_button_css() { 664 | // echo ''; 669 | // } 670 | // //判斷 WC 版本是否大於 v3.3 版才啟用附註功能 671 | // if (mxp_woocommerce_version_check('3.3')) { 672 | // //Ref: https://stackoverflow.com/a/49047149 673 | // add_filter('manage_edit-shop_order_columns', 'custom_shop_order_column', 90); 674 | // add_action('manage_shop_order_posts_custom_column', 'custom_shop_order_list_column_content', 10, 1); 675 | // add_action('admin_head', 'add_custom_order_status_actions_button_css'); 676 | // } 677 | 678 | // 訂單備註直接呈現版本 679 | function add_order_notes_column($columns) { 680 | $new_columns = (is_array($columns)) ? $columns : array(); 681 | $new_columns['order_notes'] = '訂單備註'; 682 | $new_columns['mxp_order_products'] = "購買商品"; 683 | return $new_columns; 684 | } 685 | add_filter('manage_edit-shop_order_columns', 'add_order_notes_column', 90); 686 | 687 | function add_order_notes_column_style() { 688 | $css = '.post-type-shop_order table.widefat.fixed { table-layout: auto; width: 100%; }'; 689 | $css .= 'table.wp-list-table .column-order_notes { min-width: 280px; text-align: left; }'; 690 | $css .= '.column-order_notes ul { margin: 0 0 0 18px; list-style-type: disc; }'; 691 | $css .= '.order_customer_note { color: #ee0000; }'; // red 692 | $css .= '.order_private_note { color: #0000ee; }'; // blue 693 | wp_add_inline_style('woocommerce_admin_styles', $css); 694 | } 695 | add_action('admin_print_styles', 'add_order_notes_column_style'); 696 | 697 | function add_order_notes_content($column) { 698 | if ($column != 'order_notes') { 699 | return; 700 | } 701 | 702 | global $post, $the_order; 703 | if (empty($the_order) || $the_order->get_id() != $post->ID) { 704 | $the_order = wc_get_order($post->ID); 705 | } 706 | $args = array(); 707 | $args['order_id'] = $the_order->get_id(); 708 | $args['order_by'] = 'date_created'; 709 | $args['order'] = 'ASC'; 710 | $notes = wc_get_order_notes($args); 711 | if ($notes) { 712 | print ''; 723 | } 724 | } 725 | add_action('manage_shop_order_posts_custom_column', 'add_order_notes_content', 10, 1); 726 | 727 | function mxp_order_items_column_cnt($colname) { 728 | global $the_order; 729 | if ($colname == 'mxp_order_products') { 730 | $order_items = $the_order->get_items(); 731 | if (!is_wp_error($order_items)) { 732 | foreach ($order_items as $order_item) { 733 | // $order_item['product_id'] 734 | // $order_item['name'] 735 | // $order_item['quantity'] 736 | echo $order_item['quantity'] . ' × ' . $order_item['name'] . '
'; 737 | } 738 | } 739 | } 740 | } 741 | add_action('manage_shop_order_posts_custom_column', 'mxp_order_items_column_cnt'); 742 | 743 | //移除在購物車計算運費的方法 744 | function disable_shipping_calc_on_cart($show_shipping) { 745 | if (is_cart()) { 746 | return false; 747 | } 748 | return $show_shipping; 749 | } 750 | add_filter('woocommerce_cart_ready_to_calc_shipping', 'disable_shipping_calc_on_cart', 99); 751 | 752 | //解決整合綠界物流會有結帳資料被清空的問題 Ref: https://www.mxp.tw/8582/ 還要補上自己觸發儲存的前端事件 753 | function mxp_wc_save_session_data($value) { 754 | $data = $_POST['post_data']; 755 | parse_str(html_entity_decode($data), $pdata); 756 | if (isset($pdata['billing_first_name']) && $pdata['billing_first_name'] != "") { 757 | WC()->session->set('billing_first_name', $pdata['billing_first_name']); 758 | } 759 | if (isset($pdata['billing_phone']) && $pdata['billing_phone'] != "") { 760 | WC()->session->set('billing_phone', $pdata['billing_phone']); 761 | } 762 | if (isset($pdata['billing_company']) && $pdata['billing_company'] != "") { 763 | WC()->session->set('billing_company', $pdata['billing_company']); 764 | } 765 | if (isset($pdata['billing_company_tax_id']) && $pdata['billing_company_tax_id'] != "") { 766 | WC()->session->set('billing_company_tax_id', $pdata['billing_company_tax_id']); 767 | } 768 | if (isset($pdata['billing_email']) && $pdata['billing_email'] != "") { 769 | WC()->session->set('billing_email', $pdata['billing_email']); 770 | } 771 | if (isset($pdata['billing_postcode']) && $pdata['billing_postcode'] != "") { 772 | WC()->session->set('billing_postcode', $pdata['billing_postcode']); 773 | } 774 | $value['#billing_first_name'] = ''; 775 | $value['#billing_phone'] = ''; 776 | $value['#billing_company'] = ''; 777 | $value['#billing_email'] = ''; 778 | $value['#billing_company_tax_id'] = ''; 779 | $value['#billing_postcode'] = ''; 780 | return $value; 781 | } 782 | add_filter('woocommerce_update_order_review_fragments', 'mxp_wc_save_session_data', 10, 1); 783 | 784 | // 主題繼承覆蓋翻譯(如有放置語言檔案才啟用覆蓋功能) 785 | function mxp_load_custom_wc_translation_file($mofile, $domain) { 786 | if ('woocommerce' === $domain) { 787 | $theme_mofile = get_stylesheet_directory() . '/languages/woocommerce/' . get_locale() . '.mo'; 788 | if (file_exists($theme_mofile)) { 789 | return $theme_mofile; 790 | } 791 | } 792 | return $mofile; 793 | } 794 | add_filter('load_textdomain_mofile', 'mxp_load_custom_wc_translation_file', 11, 2); 795 | 796 | // 檢查結帳表單送出資料 797 | function mxp_check_checkout_post_data() { 798 | if (!empty($_POST['billing_tax_checkbox']) && (empty($_POST['billing_company']) || empty($_POST['billing_company_tax_id']))) { 799 | wc_add_notice('請輸入三聯發票抬頭與統一編號', 'error'); 800 | } 801 | //整合綠界物流外掛的驗證補強 802 | $sm = wc_get_chosen_shipping_method_ids(); 803 | if (!empty($sm)) { 804 | if ($sm[0] == 'ecpay_shipping') { 805 | if ($_POST['billing_first_name']) { 806 | $s = $_POST['billing_first_name']; 807 | if (preg_match("/\p{Han}+/u", $s)) { 808 | if (mb_strlen($s) > 5) { 809 | wc_add_notice(__('帳單姓名請輸入最多 5 個中文字'), 'error'); 810 | } 811 | } else { 812 | if (mb_strlen($s) > 10) { 813 | wc_add_notice(__('帳單姓名請輸入最多 10 個英文字'), 'error'); 814 | } 815 | } 816 | } 817 | if ($_POST['shipping_first_name']) { 818 | $s = $_POST['shipping_first_name']; 819 | if (preg_match("/\p{Han}+/u", $s)) { 820 | if (mb_strlen($s) > 5) { 821 | wc_add_notice(__('收件人姓名請輸入最多 5 個中文字'), 'error'); 822 | } 823 | } else { 824 | if (mb_strlen($s) > 10) { 825 | wc_add_notice(__('收件人姓名請輸入最多 10 個英文字'), 'error'); 826 | } 827 | } 828 | } 829 | if ($_POST['billing_phone']) { 830 | $s = $_POST['billing_phone']; 831 | if (!preg_match('/^09[0-9]{8}$/', $s)) { 832 | wc_add_notice(__('帳單手機格式錯誤,格式為 0912345678'), 'error'); 833 | } 834 | } 835 | if ($_POST['shipping_phone']) { 836 | $s = $_POST['shipping_phone']; 837 | if (!preg_match('/^09[0-9]{8}$/', $s)) { 838 | wc_add_notice(__('收件人手機格式錯誤,格式為 0912345678'), 'error'); 839 | } 840 | } 841 | } 842 | } 843 | } 844 | add_action('woocommerce_checkout_process', 'mxp_check_checkout_post_data'); 845 | 846 | // 密碼強度改不建議 847 | function mxp_min_password_strength($strength) { 848 | return 0; 849 | } 850 | add_filter('woocommerce_min_password_strength', 'mxp_min_password_strength', 99, 1); 851 | 852 | // 後台支援搜尋包含 SKU 貨號的訂單 853 | function mxp_order_search_by_sku($order_ids, $term, $search_fields) { 854 | global $wpdb; 855 | if (!empty($term)) { 856 | // 確認有沒有這個商品,沒有就跳開了 857 | $product_id = wc_get_product_id_by_sku($wpdb->esc_like(wc_clean($term))); 858 | if (!$product_id) { 859 | return $order_ids; 860 | } 861 | // 找找看關聯資料,輸出不重複的訂單編號 862 | $order_ids = array_unique( 863 | $wpdb->get_col( 864 | $wpdb->prepare("SELECT order_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key IN ( '_product_id', '_variation_id' ) AND meta_value = %d ) AND order_item_type = 'line_item'", $product_id) 865 | ) 866 | ); 867 | } 868 | return $order_ids; 869 | } 870 | add_filter('woocommerce_shop_order_search_results', 'mxp_order_search_by_sku', 9999, 3); 871 | 872 | if (!function_exists('mxp_getOrderDetailById')) { 873 | 874 | //to get full order details 875 | function mxp_getOrderDetailById($id, $fields = null, $filter = array()) { 876 | 877 | if (is_wp_error($id)) { 878 | return false; 879 | } 880 | // Get the decimal precession 881 | $dp = (isset($filter['dp'])) ? intval($filter['dp']) : 2; 882 | $order = wc_get_order($id); //getting order Object 883 | if ($order === false) { 884 | return false; 885 | } 886 | $order_data = array( 887 | 'id' => $order->get_id(), 888 | 'order_number' => $order->get_order_number(), 889 | 'created_at' => $order->get_date_created()->date('Y-m-d H:i:s'), 890 | 'updated_at' => $order->get_date_modified()->date('Y-m-d H:i:s'), 891 | 'completed_at' => !empty($order->get_date_completed()) ? $order->get_date_completed()->date('Y-m-d H:i:s') : '', 892 | 'status' => $order->get_status(), 893 | 'currency' => $order->get_currency(), 894 | 'total' => wc_format_decimal($order->get_total(), $dp), 895 | 'subtotal' => wc_format_decimal($order->get_subtotal(), $dp), 896 | 'total_line_items_quantity' => $order->get_item_count(), 897 | 'total_tax' => wc_format_decimal($order->get_total_tax(), $dp), 898 | 'total_shipping' => wc_format_decimal($order->get_total_shipping(), $dp), 899 | 'cart_tax' => wc_format_decimal($order->get_cart_tax(), $dp), 900 | 'shipping_tax' => wc_format_decimal($order->get_shipping_tax(), $dp), 901 | 'total_discount' => wc_format_decimal($order->get_total_discount(), $dp), 902 | 'shipping_methods' => $order->get_shipping_method(), 903 | 'order_key' => $order->get_order_key(), 904 | 'payment_details' => array( 905 | 'method_id' => $order->get_payment_method(), 906 | 'method_title' => $order->get_payment_method_title(), 907 | 'paid_at' => !empty($order->get_date_paid()) ? $order->get_date_paid()->date('Y-m-d H:i:s') : '', 908 | ), 909 | 'billing_address' => array( 910 | 'first_name' => $order->get_billing_first_name(), 911 | 'last_name' => $order->get_billing_last_name(), 912 | 'company' => $order->get_billing_company(), 913 | 'address_1' => $order->get_billing_address_1(), 914 | 'address_2' => $order->get_billing_address_2(), 915 | 'city' => $order->get_billing_city(), 916 | 'state' => $order->get_billing_state(), 917 | 'formated_state' => isset(WC()->countries->states[$order->get_billing_country()]) ? WC()->countries->states[$order->get_billing_country()][$order->get_billing_state()] : '', //human readable formated state name 918 | 'postcode' => $order->get_billing_postcode(), 919 | 'country' => $order->get_billing_country(), 920 | 'formated_country' => WC()->countries->countries[$order->get_billing_country()], //human readable formated country name 921 | 'email' => $order->get_billing_email(), 922 | 'phone' => $order->get_billing_phone(), 923 | ), 924 | 'shipping_address' => array( 925 | 'first_name' => $order->get_shipping_first_name(), 926 | 'last_name' => $order->get_shipping_last_name(), 927 | 'company' => $order->get_shipping_company(), 928 | 'address_1' => $order->get_shipping_address_1(), 929 | 'address_2' => $order->get_shipping_address_2(), 930 | 'city' => $order->get_shipping_city(), 931 | 'state' => $order->get_shipping_state(), 932 | 'formated_state' => empty($order->get_shipping_state()) ? '' : WC()->countries->states[$order->get_shipping_country()][$order->get_shipping_state()], //human readable formated state name 933 | 'postcode' => $order->get_shipping_postcode(), 934 | 'country' => $order->get_shipping_country(), 935 | 'formated_country' => empty($order->get_shipping_country()) ? '' : WC()->countries->countries[$order->get_shipping_country()], //human readable formated country name 936 | ), 937 | 'note' => $order->get_customer_note(), 938 | 'customer_ip' => $order->get_customer_ip_address(), 939 | 'customer_user_agent' => $order->get_customer_user_agent(), 940 | 'customer_id' => $order->get_user_id(), 941 | 'view_order_url' => $order->get_view_order_url(), 942 | 'line_items' => array(), 943 | 'shipping_lines' => array(), 944 | 'tax_lines' => array(), 945 | 'fee_lines' => array(), 946 | 'coupon_lines' => array(), 947 | ); 948 | 949 | //getting all line items 950 | foreach ($order->get_items() as $item_id => $item) { 951 | 952 | $product = $item->get_product(); 953 | 954 | $product_id = null; 955 | $product_sku = null; 956 | // Check if the product exists. 957 | if (is_object($product)) { 958 | $product_id = $product->get_id(); 959 | $product_sku = $product->get_sku(); 960 | } 961 | 962 | $order_data['line_items'][] = array( 963 | 'id' => $item_id, 964 | 'subtotal' => wc_format_decimal($order->get_line_subtotal($item, false, false), $dp), 965 | 'subtotal_tax' => wc_format_decimal($item['line_subtotal_tax'], $dp), 966 | 'total' => wc_format_decimal($order->get_line_total($item, false, false), $dp), 967 | 'total_tax' => wc_format_decimal($item['line_tax'], $dp), 968 | 'price' => wc_format_decimal($order->get_item_total($item, false, false), $dp), 969 | 'quantity' => wc_stock_amount($item['qty']), 970 | 'tax_class' => (!empty($item['tax_class'])) ? $item['tax_class'] : null, 971 | 'name' => $item['name'], 972 | 'product_id' => (!empty($item->get_variation_id()) && ('product_variation' === $product->post_type)) ? $product->get_parent_id() : $product_id, 973 | 'variation_id' => (!empty($item->get_variation_id()) && ('product_variation' === $product->post_type)) ? $product_id : 0, 974 | 'product_url' => get_permalink($product_id), 975 | 'product_thumbnail_url' => wp_get_attachment_image_src(get_post_thumbnail_id($product_id), 'thumbnail', TRUE)[0], 976 | 'sku' => $product_sku, 977 | 'meta' => wc_display_item_meta($item, array('echo' => false)), 978 | ); 979 | } 980 | 981 | //getting shipping 982 | foreach ($order->get_shipping_methods() as $shipping_item_id => $shipping_item) { 983 | $order_data['shipping_lines'][] = array( 984 | 'id' => $shipping_item_id, 985 | 'method_id' => $shipping_item['method_id'], 986 | 'method_title' => $shipping_item['name'], 987 | 'total' => wc_format_decimal($shipping_item['cost'], $dp), 988 | ); 989 | } 990 | 991 | //getting taxes 992 | foreach ($order->get_tax_totals() as $tax_code => $tax) { 993 | $order_data['tax_lines'][] = array( 994 | 'id' => $tax->id, 995 | 'rate_id' => $tax->rate_id, 996 | 'code' => $tax_code, 997 | 'title' => $tax->label, 998 | 'total' => wc_format_decimal($tax->amount, $dp), 999 | 'compound' => (bool) $tax->is_compound, 1000 | ); 1001 | } 1002 | 1003 | //getting fees 1004 | foreach ($order->get_fees() as $fee_item_id => $fee_item) { 1005 | $order_data['fee_lines'][] = array( 1006 | 'id' => $fee_item_id, 1007 | 'title' => $fee_item['name'], 1008 | 'tax_class' => (!empty($fee_item['tax_class'])) ? $fee_item['tax_class'] : null, 1009 | 'total' => wc_format_decimal($order->get_line_total($fee_item), $dp), 1010 | 'total_tax' => wc_format_decimal($order->get_line_tax($fee_item), $dp), 1011 | ); 1012 | } 1013 | 1014 | //getting coupons 1015 | foreach ($order->get_items('coupon') as $coupon_item_id => $coupon_item) { 1016 | 1017 | $order_data['coupon_lines'][] = array( 1018 | 'id' => $coupon_item_id, 1019 | 'code' => $coupon_item['name'], 1020 | 'amount' => wc_format_decimal($coupon_item['discount_amount'], $dp), 1021 | ); 1022 | } 1023 | 1024 | return array('order' => apply_filters('woocommerce_api_order_response', $order_data, $order, $fields)); 1025 | } 1026 | 1027 | } 1028 | 1029 | // 訂單列表控制顯示的訂單狀態 1030 | function mxp_woocommerce_my_account_my_orders_query($args) { 1031 | $args['post_status'] = array('wc-failed', 'wc-refunded', 'wc-cancelled', 'wc-on-hold', 'wc-processing', 'wc-pending', 'wc-completed'); 1032 | // $order_statuses = array( 1033 | // 'wc-pending' => _x('Pending payment', 'Order status', 'woocommerce'), 1034 | // 'wc-processing' => _x('Processing', 'Order status', 'woocommerce'), 1035 | // 'wc-on-hold' => _x('On hold', 'Order status', 'woocommerce'), 1036 | // 'wc-completed' => _x('Completed', 'Order status', 'woocommerce'), 1037 | // 'wc-cancelled' => _x('Cancelled', 'Order status', 'woocommerce'), 1038 | // 'wc-refunded' => _x('Refunded', 'Order status', 'woocommerce'), 1039 | // 'wc-failed' => _x('Failed', 'Order status', 'woocommerce'), 1040 | // ); 1041 | return $args; 1042 | } 1043 | add_filter('woocommerce_my_account_my_orders_query', 'mxp_woocommerce_my_account_my_orders_query', 11, 1); 1044 | 1045 | // 自動完成只有虛擬商品的訂單狀態 1046 | // function mxp_check_order_status_completed($order_id, $old_status, $new_status) { 1047 | // if ($new_status == 'processing') { 1048 | // $check_virtual_product = true; 1049 | // $order = wc_get_order($order_id); 1050 | // foreach ($order->get_items() as $item_id => $item) { 1051 | // $product = $item->get_product(); // Get the product object 1052 | // // Check if the product is virtual 1053 | // if ($product && $product->is_virtual()) { 1054 | // // The product is virtual 1055 | // // $check_virtual_product = true; 1056 | // } else { 1057 | // // The product is not virtual 1058 | // $check_virtual_product = false; 1059 | // break; 1060 | // } 1061 | // } 1062 | // if ($order && $check_virtual_product === true) { 1063 | // $order->update_status('completed', '(系統)已自動完成訂單。', true); 1064 | // } 1065 | // } 1066 | // } 1067 | // add_action('woocommerce_order_status_changed', 'mxp_check_order_status_completed', 10, 3); 1068 | 1069 | // 確保驗證訂單的時候不要跑進去「處理中」狀態 1070 | function mxp_woocommerce_order_item_needs_processing($is_virtual_or_downloadable_item, $product, $line_item_id) { 1071 | // $is_virtual_or_downloadable_item => false 才是不需要經過「處理中」狀態 1072 | // 完成結帳時,商品是「下載」或是「虛擬」都不需要進入「處理中」的狀態,直接改「完成」 1073 | if ($product->is_downloadable() || $product->is_virtual()) { 1074 | return false; 1075 | } 1076 | return $is_virtual_or_downloadable_item; 1077 | } 1078 | add_filter('woocommerce_order_item_needs_processing', 'mxp_woocommerce_order_item_needs_processing', 11, 3); 1079 | 1080 | // 禁用 WC 背景縮圖功能 1081 | add_filter('woocommerce_background_image_regeneration', '__return_false'); 1082 | 1083 | // 顯示使用者帳號的註冊時間,移植此款外掛 https://tw.wordpress.org/plugins/recently-registered/ 1084 | function mxp_admin_init_for_user_recently_registered() { 1085 | if (is_admin()) { 1086 | add_filter('manage_users_columns', function ($columns) { 1087 | $columns['registerdate'] = '註冊時間'; 1088 | return $columns; 1089 | }); 1090 | add_action('manage_users_custom_column', function ($value, $column_name, $user_id) { 1091 | global $mode; 1092 | $list_mode = empty($_REQUEST['mode']) ? 'list' : sanitize_text_field($_REQUEST['mode']); 1093 | 1094 | if ('registerdate' !== $column_name) { 1095 | return $value; 1096 | } else { 1097 | $user = get_userdata($user_id); 1098 | if (is_multisite() && ('list' === $list_mode)) { 1099 | $formated_date = 'Y/m/d'; 1100 | } else { 1101 | $formated_date = 'Y/m/d g:i:s a'; 1102 | } 1103 | $registered = strtotime(get_date_from_gmt($user->user_registered)); 1104 | // If the date is negative or in the future, then something's wrong, so we'll be unknown. 1105 | if (($registered <= 0) || (time() <= $registered)) { 1106 | $registerdate = '未知時間'; 1107 | } else { 1108 | $registerdate = '' . date_i18n($formated_date, $registered) . ''; 1109 | } 1110 | return $registerdate; 1111 | } 1112 | }, 10, 3); 1113 | add_filter('manage_users_sortable_columns', function ($columns) { 1114 | $custom = array( 1115 | // meta column id => sortby value used in query 1116 | 'registerdate' => 'registered', 1117 | ); 1118 | return wp_parse_args($custom, $columns); 1119 | }); 1120 | add_filter('request', function ($vars) { 1121 | if (isset($vars['orderby']) && 'registerdate' == $vars['orderby']) { 1122 | $new_vars = array( 1123 | 'meta_key' => 'registerdate', 1124 | 'orderby' => 'meta_value', 1125 | ); 1126 | $vars = array_merge($vars, $new_vars); 1127 | } 1128 | return $vars; 1129 | }); 1130 | } 1131 | } 1132 | add_action('admin_init', 'mxp_admin_init_for_user_recently_registered'); 1133 | 1134 | // 後台設定看到的設定項目 1135 | function mxp_woocommerce_email_get_option_rewrite($value1, $email_obj, $value2, $key, $empty_value) { 1136 | // 批次修正 WooCommerce 發信收件人(網站主) 1137 | if ($key == 'recipient' && in_array($email_obj->id, array('new_order', 'cancelled_order', 'failed_order'))) { 1138 | // 不是網站管理員的話,就回傳設定值 1139 | if (trim($value1) !== trim(get_option('admin_email'))) { 1140 | return trim($value1); 1141 | } else { 1142 | $recipient = apply_filters('woocommerce_email_recipient_' . $email_obj->id, $value1, $email_obj->object, $email_obj); 1143 | $recipients = array_map('trim', explode(',', $recipient)); 1144 | $user_query = new WP_User_Query( 1145 | array( 1146 | 'fields' => array('ID', 'display_name', 'user_email'), 1147 | 'role__in' => array('shop_manager'), 1148 | 'exclude' => [] 1149 | ), 1150 | ); 1151 | $users = $user_query->get_results(); 1152 | foreach ($users as $user) { 1153 | $recipients[] = $user->user_email; 1154 | } 1155 | $recipients = array_unique(array_filter($recipients, 'is_email')); 1156 | $recipients = array_diff($recipients, [get_option('admin_email')]); 1157 | if (empty($recipients)) { 1158 | return get_option('admin_email'); 1159 | } 1160 | $emails = implode(', ', $recipients); 1161 | return $emails; 1162 | } 1163 | } 1164 | return $value1; 1165 | } 1166 | add_filter('woocommerce_email_get_option', 'mxp_woocommerce_email_get_option_rewrite', 11, 5); 1167 | 1168 | // 指定的「新訂單」、「取消的定單」與「失敗的訂單」收件者 1169 | function mxp_woocommerce_email_recipient_modify($recipient, $email_object, $email) { 1170 | if (empty($recipient)) { 1171 | return get_option('admin_email'); 1172 | } 1173 | if ($recipient !== get_option('admin_email')) { 1174 | return $recipient; 1175 | } 1176 | $recipients = array_map('trim', explode(',', $recipient)); 1177 | $user_query = new WP_User_Query( 1178 | array( 1179 | 'fields' => array('ID', 'display_name', 'user_email'), 1180 | 'role__in' => array('shop_manager'), 1181 | 'exclude' => [] 1182 | ), 1183 | ); 1184 | $users = $user_query->get_results(); 1185 | foreach ($users as $user) { 1186 | $recipients[] = $user->user_email; 1187 | } 1188 | $recipients = array_unique(array_filter($recipients, 'is_email')); 1189 | $recipients = array_diff($recipients, [get_option('admin_email')]); 1190 | if (empty($recipients)) { 1191 | return get_option('admin_email'); 1192 | } 1193 | $emails = implode(', ', $recipients); 1194 | return $emails; 1195 | } 1196 | add_filter('woocommerce_email_recipient_new_order', 'mxp_woocommerce_email_recipient_modify', 11, 3); 1197 | add_filter('woocommerce_email_recipient_cancelled_order', 'mxp_woocommerce_email_recipient_modify', 11, 3); 1198 | add_filter('woocommerce_email_recipient_failed_order', 'mxp_woocommerce_email_recipient_modify', 11, 3); 1199 | add_filter('woocommerce_email_recipient_low_stock', 'mxp_woocommerce_email_recipient_modify', 11, 3); 1200 | add_filter('woocommerce_email_recipient_no_stock', 'mxp_woocommerce_email_recipient_modify', 11, 3); 1201 | add_filter('woocommerce_email_recipient_backorder', 'mxp_woocommerce_email_recipient_modify', 11, 3); 1202 | 1203 | // 註冊新訂單狀態 1204 | function mxp_register_custom_order_status() { 1205 | register_post_status('wc-awaiting-delivery', array( 1206 | 'label' => '已出貨訂單', 1207 | 'public' => true, 1208 | 'exclude_from_search' => false, 1209 | 'show_in_admin_all_list' => true, 1210 | 'show_in_admin_status_list' => true, 1211 | 'label_count' => _n_noop('已出貨訂單 (%s)', '已出貨訂單 (%s)', 'woocommerce'), 1212 | )); 1213 | } 1214 | add_action('init', 'mxp_register_custom_order_status', 20); 1215 | 1216 | // 加入新訂單狀態 1217 | function mxp_custom_wc_order_statuses($order_statuses) { 1218 | $order_statuses['wc-awaiting-delivery'] = '已出貨訂單'; 1219 | return $order_statuses; 1220 | } 1221 | add_filter('wc_order_statuses', 'mxp_custom_wc_order_statuses'); 1222 | 1223 | // 批次處理訂單狀態 1224 | function mxp_custom_dropdown_bulk_actions_shop_order($actions) { 1225 | $actions['mark_awaiting-delivery'] = '變更狀態為已出貨訂單'; 1226 | return $actions; 1227 | } 1228 | add_filter('bulk_actions-edit-shop_order', 'mxp_custom_dropdown_bulk_actions_shop_order', 20, 1); 1229 | 1230 | // 註冊狀態信件類別 1231 | function mxp_woocommerce_email_classes($emails) { 1232 | $emails['WC_Email_Awaiting_Delivery_Order'] = include __DIR__ . '/class-wc-email-awaiting-delivery-order.php'; 1233 | return $emails; 1234 | } 1235 | add_filter('woocommerce_email_classes', 'mxp_woocommerce_email_classes', 11, 1); 1236 | 1237 | // 註冊發信事件 1238 | function mxp_woocommerce_email_actions($actions) { 1239 | $actions[] = 'woocommerce_order_status_processing_to_awaiting-delivery'; 1240 | $actions[] = 'woocommerce_order_status_pending_to_awaiting-delivery'; 1241 | $actions[] = 'woocommerce_order_status_failed_to_awaiting-delivery'; 1242 | $actions[] = 'woocommerce_order_status_cancelled_to_awaiting-delivery'; 1243 | $actions[] = 'woocommerce_order_status_on-hold_to_awaiting-delivery'; 1244 | return $actions; 1245 | } 1246 | add_filter('woocommerce_email_actions', 'mxp_woocommerce_email_actions', 11, 1); 1247 | 1248 | // 禁止未登入結帳 1249 | function mxp_checkout_check_login_or_not() { 1250 | if (!is_user_logged_in() && is_checkout()) { 1251 | $redirect_to = wc_get_page_permalink('myaccount'); 1252 | wp_redirect($redirect_to . '?redirect_to=' . wc_get_checkout_url()); 1253 | exit; 1254 | } 1255 | } 1256 | // add_action('template_redirect', 'mxp_checkout_check_login_or_not', -1); 1257 | 1258 | // function mxp_woocommerce_ecpay_available_payment_gateways($available_gateways) { 1259 | // // 判斷是否選取綠界物流,是的話取消「貨到付款」的選項避免錯誤。(此為超商取貨(無付款)功能處理) 1260 | // $sm = null; 1261 | // if (function_exists('wc_get_chosen_shipping_method_ids')) { 1262 | // $sm = wc_get_chosen_shipping_method_ids(); 1263 | // } 1264 | // if (!empty($sm)) { 1265 | // if ($sm[0] == 'ecpay_shipping') { 1266 | // unset($available_gateways['cod']); 1267 | // } 1268 | // } 1269 | // //回傳付款方式 1270 | // return $available_gateways; 1271 | // }; 1272 | // add_filter('woocommerce_available_payment_gateways', 'mxp_woocommerce_ecpay_available_payment_gateways', 11, 1); 1273 | 1274 | //取消選擇國家會改變本地化結帳欄位的功能(注意,此功能有機會跟主題衝突,要小心使用) 1275 | // function mxp_remove_wc_address_i18n_script() { 1276 | // if (is_checkout() || is_account_page()) { 1277 | // wp_deregister_script('wc-address-i18n'); 1278 | // } 1279 | // } 1280 | // add_action('wp_head', 'mxp_remove_wc_address_i18n_script'); 1281 | 1282 | // function mxp_shipping_fee_discount() { 1283 | // if (is_admin() && !defined('DOING_AJAX')) { 1284 | // // 避免在管理介面下被觸發 1285 | // return; 1286 | // } 1287 | // $total_price = 0; 1288 | // $total_price = intval(WC()->cart->get_cart_contents_total()); 1289 | // foreach (WC()->session->get('shipping_for_package_0')['rates'] as $method_id => $rate) { 1290 | // // 判斷當前選擇的運送方法與目前購物車總金額是否大於 700 1291 | // if (WC()->session->get('chosen_shipping_methods')[0] == $method_id && $total_price >= 700) { 1292 | // $rate_label = $rate->label; // 當前運費標籤名稱 1293 | // $rate_cost_excl_tax = floatval($rate->cost); // 不含稅率的運費 1294 | // // 紀錄稅率費用 1295 | // $rate_taxes = 0; 1296 | // foreach ($rate->taxes as $rate_tax) { 1297 | // $rate_taxes += floatval($rate_tax); 1298 | // } 1299 | // // 包含稅率費用的總運費 1300 | // $rate_cost_incl_tax = $rate_cost_excl_tax + $rate_taxes; 1301 | // if ($rate_cost_incl_tax != 0) { 1302 | // WC()->cart->add_fee('消費滿 700 免運費', -$rate_cost_incl_tax, false); 1303 | // } 1304 | // break; 1305 | // } 1306 | // } 1307 | // } 1308 | // add_action('woocommerce_cart_calculate_fees', 'mxp_shipping_fee_discount'); 1309 | -------------------------------------------------------------------------------- /woocommerce/emails/customer-awaiting-delivery-order.php: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 |

get_billing_first_name()));?>

22 | 23 |

24 | 25 |