HEX
Server: nginx/1.28.3
System: Linux ip-172-31-12-242 6.17.0-1007-aws #7~24.04.1-Ubuntu SMP Thu Jan 22 21:04:49 UTC 2026 x86_64
User: root (0)
PHP: 7.4.33
Disabled: passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
Upload Files
File: //proc/self/cwd/wp-content/plugins/quick-message/quick-message.php
<?php
/**
 * Plugin Name: 快速留言管理
 * Description: 接收前台快速留言表单提交,并在后台提供管理菜单查看记录
 * Version: 1.0.0
 * Author: Dev
 */

if (!defined('ABSPATH')) exit;

define('QM_VERSION', '1.0.0');
define('QM_TABLE', 'quick_messages');

register_activation_hook(__FILE__, 'qm_create_table');

function qm_create_table() {
    global $wpdb;
    $table = $wpdb->prefix . QM_TABLE;
    $charset = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE $table (
        id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
        name varchar(100) NOT NULL DEFAULT '',
        contact_type varchar(20) NOT NULL DEFAULT '',
        contact_number varchar(200) NOT NULL DEFAULT '',
        message text NOT NULL,
        url varchar(500) NOT NULL DEFAULT '',
        sys_area_id varchar(100) NOT NULL DEFAULT '',
        customer_service_id varchar(100) NOT NULL DEFAULT '',
        office_id varchar(100) NOT NULL DEFAULT '',
        status tinyint(1) NOT NULL DEFAULT 0,
        created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_status (status),
        KEY idx_created (created_at)
    ) $charset;";

    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    dbDelta($sql);
}

add_action('rest_api_init', 'qm_register_routes');

function qm_register_routes() {
    register_rest_route('quick-message/v1', '/submit', array(
        'methods' => 'POST',
        'callback' => 'qm_handle_submit',
        'permission_callback' => '__return_true',
    ));
}

function qm_handle_submit(WP_REST_Request $request) {
    $params = $request->get_json_params();
    if (!$params) {
        $params = $request->get_body_params();
    }

    $name = sanitize_text_field($params['name'] ?? '');
    $contact_type = sanitize_text_field($params['contactType'] ?? '');
    $contact_number = sanitize_text_field($params['number'] ?? '');
    $message = sanitize_textarea_field($params['messageContents'] ?? '');
    $url = esc_url_raw($params['url'] ?? '');
    $sys_area_id = sanitize_text_field($params['sysAreaId'] ?? '');
    $customer_service_id = sanitize_text_field($params['customerServiceId'] ?? '');
    $office_id = sanitize_text_field($params['officeId'] ?? '');

    if (empty($name) || empty($contact_type) || empty($contact_number)) {
        return new WP_REST_Response(array('success' => false, 'message' => '必填欄位不能為空'), 400);
    }

    global $wpdb;
    $table = $wpdb->prefix . QM_TABLE;

    $inserted = $wpdb->insert($table, array(
        'name' => $name,
        'contact_type' => $contact_type,
        'contact_number' => $contact_number,
        'message' => $message,
        'url' => $url,
        'sys_area_id' => $sys_area_id,
        'customer_service_id' => $customer_service_id,
        'office_id' => $office_id,
        'status' => 0,
        'created_at' => current_time('mysql'),
    ), array('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s'));

    if ($inserted === false) {
        return new WP_REST_Response(array('success' => false, 'message' => '提交失敗,請稍後重試'), 500);
    }

    return new WP_REST_Response(array('success' => true, 'message' => '提交成功'), 200);
}

add_action('admin_menu', 'qm_admin_menu');

function qm_admin_menu() {
    add_menu_page(
        '快速留言',
        '快速留言',
        'manage_options',
        'quick-messages',
        'qm_admin_page',
        'dashicons-feedback',
        26
    );
}

function qm_admin_page() {
    global $wpdb;
    $table = $wpdb->prefix . QM_TABLE;

    $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : 'list';
    $msg_id = isset($_GET['id']) ? intval($_GET['id']) : 0;

    if ($action === 'delete' && $msg_id > 0 && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'qm_delete_' . $msg_id)) {
        $wpdb->delete($table, array('id' => $msg_id), array('%d'));
        wp_redirect(admin_url('admin.php?page=quick-messages&qm_deleted=1'));
        exit;
    }

    if ($action === 'mark_read' && $msg_id > 0 && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'qm_read_' . $msg_id)) {
        $wpdb->update($table, array('status' => 1), array('id' => $msg_id), array('%d'), array('%d'));
        wp_redirect(admin_url('admin.php?page=quick-messages&qm_updated=1'));
        exit;
    }

    if ($action === 'mark_unread' && $msg_id > 0 && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'qm_unread_' . $msg_id)) {
        $wpdb->update($table, array('status' => 0), array('id' => $msg_id), array('%d'), array('%d'));
        wp_redirect(admin_url('admin.php?page=quick-messages&qm_updated=1'));
        exit;
    }

    $contact_type_map = array('1' => 'WhatsApp', '2' => 'Line', '3' => 'WeChat', '4' => '電話');

    $paged = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
    $per_page = 20;
    $offset = ($paged - 1) * $per_page;

    $filter_status = isset($_GET['filter_status']) && $_GET['filter_status'] !== '' ? intval($_GET['filter_status']) : null;
    $search = isset($_GET['s']) ? sanitize_text_field($_GET['s']) : '';

    $where = '1=1';
    if ($filter_status !== null) {
        $where .= $wpdb->prepare(' AND status = %d', $filter_status);
    }
    if ($search !== '') {
        $where .= $wpdb->prepare(' AND (name LIKE %s OR contact_number LIKE %s OR message LIKE %s)', '%' . $wpdb->esc_like($search) . '%', '%' . $wpdb->esc_like($search) . '%', '%' . $wpdb->esc_like($search) . '%');
    }

    $total = $wpdb->get_var("SELECT COUNT(*) FROM $table WHERE $where");
    $total_pages = ceil($total / $per_page);

    $rows = $wpdb->get_results($wpdb->prepare("SELECT * FROM $table WHERE $where ORDER BY created_at DESC LIMIT %d OFFSET %d", $per_page, $offset));

    $unread_count = $wpdb->get_var("SELECT COUNT(*) FROM $table WHERE status = 0");

    if (isset($_GET['qm_deleted'])) {
        echo '<div class="notice notice-success is-dismissible"><p>留言已刪除。</p></div>';
    }
    if (isset($_GET['qm_updated'])) {
        echo '<div class="notice notice-success is-dismissible"><p>狀態已更新。</p></div>';
    }

    ?>
    <div class="wrap">
        <h1 class="wp-heading-inline">快速留言 <?php if ($unread_count > 0) echo '<span class="update-plugins count-' . $unread_count . '"><span class="update-count">' . $unread_count . '</span></span>'; ?></h1>

        <form method="get" style="margin-top:10px;">
            <input type="hidden" name="page" value="quick-messages" />
            <select name="filter_status">
                <option value="">全部狀態</option>
                <option value="0" <?php selected($filter_status, 0); ?>>未讀</option>
                <option value="1" <?php selected($filter_status, 1); ?>>已讀</option>
            </select>
            <input type="search" name="s" value="<?php echo esc_attr($search); ?>" placeholder="搜尋姓名、聯絡方式、留言內容" />
            <button class="button" type="submit">篩選</button>
        </form>

        <table class="wp-list-table widefat fixed striped" style="margin-top:10px;">
            <thead>
                <tr>
                    <th style="width:50px;">ID</th>
                    <th style="width:90px;">稱呼</th>
                    <th style="width:90px;">聯絡方式</th>
                    <th style="width:180px;">號碼/鏈接</th>
                    <th>留言內容</th>
                    <th style="width:100px;">來源頁面</th>
                    <th style="width:70px;">狀態</th>
                    <th style="width:140px;">提交時間</th>
                    <th style="width:180px;">操作</th>
                </tr>
            </thead>
            <tbody>
            <?php if (empty($rows)): ?>
                <tr><td colspan="9" style="text-align:center;padding:20px;">暫無留言記錄</td></tr>
            <?php else: ?>
                <?php foreach ($rows as $row): ?>
                <tr<?php if ($row->status == 0) echo ' style="background:#fff8e1;font-weight:600;"'; ?>>
                    <td><?php echo $row->id; ?></td>
                    <td><?php echo esc_html($row->name); ?></td>
                    <td><?php echo esc_html($contact_type_map[$row->contact_type] ?? $row->contact_type); ?></td>
                    <td><?php echo esc_html($row->contact_number); ?></td>
                    <td><?php echo esc_html(mb_substr($row->message, 0, 80)) . (mb_strlen($row->message) > 80 ? '...' : ''); ?></td>
                    <td><a href="<?php echo esc_url($row->url); ?>" target="_blank" title="<?php echo esc_attr($row->url); ?>"><?php echo esc_html(mb_substr(basename($row->url), 0, 20)); ?></a></td>
                    <td><?php echo $row->status == 1 ? '<span style="color:#888;">已讀</span>' : '<span style="color:#e65100;">未讀</span>'; ?></td>
                    <td><?php echo esc_html($row->created_at); ?></td>
                    <td>
                        <?php if ($row->status == 0): ?>
                            <a href="<?php echo wp_nonce_url(admin_url('admin.php?page=quick-messages&action=mark_read&id=' . $row->id), 'qm_read_' . $row->id); ?>" class="button button-small">標記已讀</a>
                        <?php else: ?>
                            <a href="<?php echo wp_nonce_url(admin_url('admin.php?page=quick-messages&action=mark_unread&id=' . $row->id), 'qm_unread_' . $row->id); ?>" class="button button-small">標記未讀</a>
                        <?php endif; ?>
                        <a href="<?php echo wp_nonce_url(admin_url('admin.php?page=quick-messages&action=delete&id=' . $row->id), 'qm_delete_' . $row->id); ?>" class="button button-small" onclick="return confirm('確定要刪除這條留言嗎?');">刪除</a>
                    </td>
                </tr>
                <?php endforeach; ?>
            <?php endif; ?>
            </tbody>
        </table>

        <?php if ($total_pages > 1): ?>
        <div class="tablenav bottom">
            <div class="tablenav-pages">
                <span class="displaying-num">共 <?php echo $total; ?> 條</span>
                <span class="pagination-links">
                <?php
                if ($paged > 1) {
                    echo '<a class="button" href="' . esc_url(add_query_arg(array('paged' => $paged - 1, 'filter_status' => $filter_status, 's' => $search))) . '">&laquo; 上一頁</a> ';
                }
                echo '<span class="paging-input">' . $paged . ' / ' . $total_pages . '</span>';
                if ($paged < $total_pages) {
                    echo ' <a class="button" href="' . esc_url(add_query_arg(array('paged' => $paged + 1, 'filter_status' => $filter_status, 's' => $search))) . '">下一頁 &raquo;</a>';
                }
                ?>
                </span>
            </div>
        </div>
        <?php endif; ?>
    </div>
    <?php
}