WordPress 外掛開發 – 使用者群組管理與邀請函管理

本文續接 WordPress 外掛開發 – 讀取CSV檔匯入使用者資訊

原本是提到透過CSV匯入客戶註冊後,需提供分群功能,以便日後針對不同客群寄送邀請信。因此已經開始想像我們可能需要新增一張Table customer_group,裡面有幾個關鍵欄位,例如群組ID、群組名稱、以 json 資料格式儲存所有使用者ID

以當時情境,並不需要以一對多來新增 群組ID 與 使用者ID 的關聯表

要發送邀請函時,則透過選擇群組來寄送,完全把功能往複雜了想。

作者同時間必須開發與維護許多功能,許多系統功能更是一次性使用便可能因為成效不佳而夭折,因此除非已經看到未來定會擴充,不然通常會保留許多彈性,但就是這些動作導致,額外花了很多不必要的時間。

然而,在經過多次會議討論,發現所謂的群組並沒有再利用性,也就是說建立群組完全是不需要的事情,因此調整後做法變成,使用者只要拿著那份從 CRM 匯出的 CSV 檔(這個檔案裡面就包含著這次參與的成員)依序操作:

  1. 上傳CSV匯入使用者
  2. 上傳CSV做一些批次更新的操作
  3. 上傳CSV寄信

有沒有覺得對要操作的管理人員來說省事很多,也由於我們用了 JS 解讀 CSV 檔案,在用戶端就能解決也不用上傳到Server,只需要透過這份名單,配合 Ajax 就能進行各項操作

相關程式碼附於下方

<?php

// 我們在使用者選單內建立一個子選單
add_action('admin_menu', function() {
    add_submenu_page(
        'users',                    // 父選單 slug
        '寄邀請信',                  // 頁面標題
        '寄邀請信',                  // 選單標題
        'manage_options',              // 權限
        'send-invitation-emails',      // 子選單 slug
        'render_send_invitation_page'  // 回呼函數
    );
});

function render_send_invitation_page() {
    ?>
    <div class="wrap">
        <h1>寄邀請信</h1>

        <!-- 收件者 CSV 上傳 -->
        <label for="recipients-csv">收件者:</label>
        <input type="file" id="recipients-csv" accept=".csv">
        <table id="recipients-table">
            <thead>
                <tr>
                    <th><input type="checkbox" id="select-all" /> 全選</th>
                    <th>Email</th>
                    <th>First Name</th>
                    <th>Last Name</th>
                    <th>操作結果</th>
                </tr>
            </thead>
            <tbody>
                <!-- 這裡將根據 CSV 載入動態產生行 -->
            </tbody>
        </table>


        <!-- 發送按鈕 -->
        <button id="send-invites">發送邀請信</button>

        <script>
            document.getElementById('recipients-csv').addEventListener('change', function(event) {
              const file = event.target.files[0];
              if (file) {
                const reader = new FileReader();
                reader.onload = function(e) {
                  const rows = e.target.result.split("\n");
                  const table = document.getElementById("recipients-table");
                  const tbody = table.querySelector("tbody"); // 取得 tbody 來清空資料行
                  tbody.innerHTML = ''; // 清空 tbody 內的資料行
                  const rowsData = rows.slice(1); // 跳過 CSV 第一筆資料(標題)
                  
                  rowsData.forEach((row, index) => {
                    // 檢查每一行是否是空的,去除前後空白並確保有內容才處理
                    const trimmedRow = row.trim();
                    if (trimmedRow === "") return; // 如果是空行則跳過

                    const cols = trimmedRow.split(",");
                    if (cols.length > 0) {
                      const tr = document.createElement("tr");

                      // 添加選擇框(第一列)
                      const checkboxTd = document.createElement("td");
                      const checkbox = document.createElement("input");
                      checkbox.type = "checkbox";
                      checkbox.classList.add("recipient-checkbox");
                      checkbox.setAttribute("data-row-index", index); // 標記行索引
                      checkboxTd.appendChild(checkbox);
                      tr.appendChild(checkboxTd);

                      // 只取需要的欄位 (第 2, 6, 7 欄) email, first name, last_name
                      const selectedCols = [cols[1], cols[5], cols[6]];

                      // 其他欄位
                      selectedCols.forEach((col, colIndex) => {
                        const td = document.createElement("td");
                        td.textContent = col.trim();
                        tr.appendChild(td);
                      });

                      // 將表格列加入表格
                      tbody.appendChild(tr);
                    }
                  });
                };
                reader.readAsText(file);
              }

              // 全選/全取消功能
              document.getElementById("select-all").addEventListener("change", function(e) {
                  const checkboxes = document.querySelectorAll(".recipient-checkbox");
                  checkboxes.forEach(checkbox => {
                      checkbox.checked = e.target.checked;
                  });
              });

              // 按下發送按鈕時只發送選中的收件人
              document.getElementById("send-invitation-btn").addEventListener("click", function() {
                  const selectedEmails = [];
                  const checkboxes = document.querySelectorAll(".recipient-checkbox:checked");
                  
                  checkboxes.forEach(checkbox => {
                      const row = checkbox.closest("tr");
                      const email = row.cells[1].textContent.trim(); // 假設第二欄是 email
                      selectedEmails.push({
                          email: email,
                          tr: row // 存儲 tr 元素以便後續更新
                      });
                  });

                  // 呼叫後端發送邀請信的函數,傳遞選中的 emails
                  sendEmails(selectedEmails);
              });

              function sendEmails(emails) {
                  emails.forEach(email => {
                      // 在發送前清空結果欄位
                      const resultTd = tr.querySelector("td:last-child"); // 取得該行最後一個 td
                      resultTd.textContent = "處理中..."; 
                      resultTd.style.color = "#ff9800"; // 橙色,表示處理中

                      $.post(ajaxurl, {
                          action: 'send_invitation_email',
                          recipient_email: email,
                          // 其他必要參數
                      }, function(response) {
                          // 顯示每封信發送的結果
                          console.log(response);
                      });
                  });
              }

            });

            document.getElementById('send-invites').addEventListener('click', function() {
              const rows = document.querySelectorAll("#recipients-table tr");
              rows.forEach(row => {
                const recipient = row.cells[0].textContent;
                const data = {
                  action: 'send_invitation_email',
                  recipient_email: recipient
                };

                jQuery.post(ajaxurl, data, function(response) {
                  console.log("發送給 " + recipient + ":" + response.data);
                  // 更新成功可以回寫剛剛的 table 操作結果欄位
                });
              });
            });
        </script>
    </div>
    <?php
}

add_action('wp_ajax_send_invitation_email', function() {
    $template_id = 1; // 以某篇 post 當作範本內容
    $sender_name = '寄件人名稱';
    $sender_email = 'no-reply@yourdomain';
    $recipient_email = sanitize_email($_POST['recipient_email']);

    // 取得信件範本內容
    $template = get_post($template_id);
    $subject = get_the_title($template);
    $message = apply_filters('the_content', $template->post_content);

    // 使用 WordPress wp_mail 發送郵件
    $headers = ['From: ' . $sender_name . ' <' . $sender_email . '>'];
    $result = wp_mail($recipient_email, $subject, $message, $headers);

    if ($result) {
      wp_send_json_success("成功發送");
    } else {
      wp_send_json_error("發送失敗");
    }
});