本篇文章更新時間:2023/04/15
如有資訊過時或語誤之處,歡迎使用 Contact 功能通知或向一介資男的 LINE 社群反應。
如果本站內容對你有幫助,歡迎贊助支持


本系列文為翻譯文章,翻譯 WordPress 官方提供的開發外掛指引文件。前篇 [WordPress] 外掛開發,貫穿系統核心的設計奧義 – Hook 勾點 分享完外掛開發指引中核心的勾點後,接下來就是關於開發一個外掛是否符合當地法規使用,以及要怎麼與 WordPress 核心互動。

Privacy 隱私權

你正在撰寫一個處理個人資料的外掛嗎——例如名字、地址和其他可以用來辨識個人的資料?你需要小心處理這些資料,保護你的使用者和訪客的隱私。

什麼是隱私權?

WordPress.org 在歐盟的一般資料保護規則(GDPR)規範下增進了幾個功能。在推出這項規則後,我們已經將「隱私權」做為核心開發的焦點,這將允許我們在特定立法以外繼續改進隱私權和資料保護。

但是,什麼問題可能被定義為「隱私權」問題,我們如何定義它呢?雖然隱私權要求在各個國家、文化和法律體系中差異很大,但有幾個適用於任何情況的一般原則:

  • 同意及選擇: 供使用者(及網站訪客)選擇和掌握其資料使用的選項,要求明確、具體和知情同意。
  • 目的合法性和明確性: 僅為其旨在收集和使用個人資料的目的,以及事先明確告知使用者的目的而收集和使用資料。
  • 收集限制: 僅收集所需的使用者資料;如果可能,避免將你的資料與其他外掛的資料結合使用或產生額外的資料副本。
  • 資料最小化: 將資料處理和接觸的人員以及使用資料的方式限制在最少量和必要人員之內。
  • 使用、保留和披露限制: 刪除使用者不再需要的資料,包括主要和備份存檔中的資料,由接收者以及任何第三方。
  • 精確度和品質: 確保收集和使用的資料正確、相關和最新,特別是如果不正確或低品質的資料可能對使用者產生不良影響。
  • 公開、透明和通知: 告知使用者其資料是如何收集、使用和分享的,以及他們在這些用途上擁有的權利。
  • 個人參與和訪問: 為使用者提供訪問或下載其資料的方式;
  • 責任制: 文件化資料的使用情況,通過技術和安全措施在傳輸和第三方使用中保護它,並盡可能防止濫用和違規。
  • 資訊安全: 通過適當的技術和安全措施保護資料。
  • 隱私合規性: 確保工作符合將其用於收集和處理個人資料的地點的隱私法規。

(來源: ISO 29100/Privacy Framework standard)

儘管這些原則並非所有情況和使用場景都適用,但在開發過程中使用它們可以幫助確保使用者的信任。

隱私權的設計

這些原則中的許多都被提倡在隱私權設計框架中,該框架強調:

  • 隱私權應該是積極主動的,而不是被動的,應該在使用者接觸之前預見到隱私權的問題,也應該是預防性的,而非補救性的。
  • 預設應該是隱私權的保護,使用者不需要採取行動來保護其隱私權,而且不應該假設使用者同意資料共享。
  • 隱私權應該作為核心功能內建於設計中,而不是附加功能。
  • 隱私權應該是正向加總的:不應該在隱私權和資安、隱私權和安全或隱私權和服務提供之間有所取捨。
  • 隱私權應該透過最小化資料留存和定期刪除不再需要的資料來提供資料生命週期的保護。
  • 在你的外掛(或服務)上使用的隱私權標準應該是可見的、透明的、開放的、記錄的並且可以獨立驗證。
  • 隱私權應該以使用者為中心。應該給予人們選項,如細緻的隱私權選擇、最大化的詳細的隱私權資訊通知、使用者友好的選項和對更改的清晰通知。

針對你外掛提供的思考糧食

為協助你為外掛做好準備,我們建議對每個外掛進行以下問題的檢視:

  1. 你的外掛如何處理個人資料?請使用 wp_add_privacy_policy_content 向使用者揭露以下內容:
    • 外掛是否與第三方共享個人資料(例如向外部 API/伺服器傳送資料)?如果是,外掛與哪些第三方共享資料,並且他們是否有可提供的隱私權政策連結?
    • 外掛是否收集個人資料?如果是,它收集哪些資料,並儲存在哪裡?可以思考使用者資料/中繼資料、設定選項、文章中繼資料、自定義表格、文件等存放位置。
    • 外掛是否使用其他人收集的個人資料?如果是,使用哪些資料?外掛是否將個人資料傳遞給 SDK?該 SDK 如何處理這些資料?
    • 外掛是否收集遙測資料,直接或間接地?例如,在每次安裝中從第三方來源載入圖片可能會間接記錄並追蹤所有外掛安裝的使用資料。
    • 外掛是否從第三方中載入 JavasSript、追蹤像素或嵌入 iframes?第三方 JS、追蹤像素和 iframes 可以收集訪問者的資料/操作,留下 cookies 等。
    • 外掛是否在瀏覽器中儲存資料?如果是,儲存在哪裡,儲存了哪些資料?可以思考像是 cookies、本地儲存等這些東西。
  2. 如果你的外掛收集個人資料…
    • 是否提供個人資料匯出工具?
    • 是否提供個人資料清除回呼方法?
    • 外掛是否因某些原因(例如未完成訂單等)拒絕刪除個人資料?這些也應該予以披露。
  3. 外掛是否使用錯誤日誌紀錄?如可能,它是否避免記錄個人資料?是否可以使用 wp_privacy_anonymize_data 之類的方法最小化記錄的個人資料?日誌條目的保存時間有多長?誰可以訪問這些條目?
  4. 在 wp-admin 中,訪問/查看個人資料需要什麼角色/權限?這些是否足夠?
  5. 外掛在前端網站上公開了哪些個人資料?是針對登錄和未登錄使用者公開的嗎?是否應該這樣?
  6. 外掛在 REST API 端點中公開了哪些個人資料?它顯示在登錄和未登錄使用者的界面上嗎?查看它需要什麼角色/權限?這些是否適當?
  7. 外掛是否正確地刪除/清除資料,包括特別是個人資料:
    • 在移除外掛時?
    • 當相關項目被刪除時(例如從 Post Meta 或另一個表中關聯的 post 等資料)?
    • 刪除使用者時(例如來自任何使用者關聯的資料表)?
  8. 外掛是否提供控制以減少所需的個人資料的量?
  9. 外掛是否僅當 SDK 或 API 需要時才共享個人資料,還是會與外掛本身共享個人資料?
  10. 假如安裝了某些其他外掛,此外掛收集或共享的個人資料量是否會發生變化?

外部資源

提供網站隱私政策的文字建議

每個收集、使用或儲存使用者資料,或將其傳送給外部來源或第三方的外掛,都應該增加建議文字的部分到隱私權政策章節段落。最好使用 wp_add_privacy_policy_content( $plugin_name, $policy_text ) 方法來完成。這將使網站管理員能夠將該資訊拉入其網站的隱私權政策中。

為了使使用者更容易理解,該文字應回答預設隱私權政策中提供的問題:

  • 我們收集哪些個人資料,以及為什麼收集這些資料
    • 他們自己手動輸入資訊
    • WP:聯絡表單
    • WP:留言
    • WP:Cookies
    • WP:第三方嵌入
    • 網站分析
  • 我們與誰分享你的資料
  • 我們保留你的資料多長時間
  • 你對你的資料擁有什麼權利
  • 我們向何處發送你的資料
  • 我們的聯絡資訊
  • 我們如何保護你的資料
  • 我們有哪些資料違規程序
  • 我們從哪些第三方接收資料
  • 我們對使用者資料做出的任何自動決策和/或概括
  • 任何行業監管披露要求

雖然並非所有問題都適用於所有外掛,但我們建議特別注意關於資料分享的部分。

範例程式

建議在 admin_init action 中呼叫 wp_add_privacy_policy_content。在 action hook 之外呼叫此函數可能會導致問題,更多細節參考 Ticket #44142

Note: 透過使用專門的.privacy-policy-tutorial CSS 類別可提供補充資訊。將此 CSS 類別應用於任何 HTML 元素中包含的內容將在複製部分內容時從剪貼簿中省略。

/**
 * Adds a privacy policy statement.
 */
function wporg_add_privacy_policy_content() {
    if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) {
        return;
    }
    $content = '<p class="privacy-policy-tutorial">' . __( 'Some introductory content for the suggested text.', 'text-domain' ) . '</p>'
            . '<strong class="privacy-policy-tutorial">' . __( 'Suggested Text:', 'my_plugin_textdomain' ) . '</strong> '
            . sprintf(
                __( 'When you leave a comment on this site, we send your name, email address, IP address and comment text to example.com. Example.com does not retain your personal data. The example.com privacy policy is <a href="%1$s" target="_blank">here</a>.', 'text-domain' ),
                'https://example.com/privacy-policy'
            );
    wp_add_privacy_policy_content( 'Example Plugin', wp_kses_post( wpautop( $content, false ) ) );
}

add_action( 'admin_init', 'wporg_add_privacy_policy_content' );

新增個人資料匯出工具至你的外掛程式

在 WordPress 4.9.6 版本後,新增了工具來協助遵守歐盟的一般資料保護規則(簡稱GDPR)等法規。新增的工具之一是個人資料匯出工具,可支援以 ZIP 檔案匯出給定使用者的所有個人資料。除了儲存在 WordPress 留言中的個人資料之外,外掛還可以連接到匯出工具來匯出它們收集的個人資料,無論是在類似於 postmeta 的內容中還是在全新的自訂內容類型(CPT)中。

所有匯出的“關鍵”是使用者的電子郵件地址——這是因為它支援匯出完全註冊使用者和未註冊使用者(例如未註冊的留言者)的個人資料。

然而,由於製作個人資料匯出可能是一個費時的過程,而且可能包含敏感的資料,因此我們不想只是產生並將其發送到請求人的電子郵件,而是希望在確認請求之前,管理面向的使用者界面會要求管理員輸入請求人的使用者名稱或電子郵件地址,然後發送一個連結給他們,請他們點擊確認請求。

一旦確認請求,管理員可以為使用者產生並下載或直接電子郵件提供個人資料匯出 ZIP 文件,或在需要時進行匯出。使用者收到的 ZIP 文件中,將包含一個“迷你網站”,其中包含一個 HTML 頁面,該頁面將其個人資料組織成群組(例如包含留言的一個群組等)。

管理員無論是下載個人資料匯出 ZIP 檔案還是直接將其發送給請求者,個人資料匯出的方式都是相同的,並依靠“匯出工具”回呼來收集匯出的所有資料。當管理員點擊下載或郵件連結時,會開始一個 AJAX 循環,一次遍歷系統中註冊的所有匯出工具。除了核心內建的匯出工具外,外掛可以註冊自己的匯出工具回呼函數。

匯出工具回呼介面被設計得盡可能簡單。匯出工具回呼接收我們使用的電子郵件地址和頁面參數。頁面參數(從1開始)用於避免外掛嘗試一次性匯出所有已收集的個人資料而導致超時。行為良好的外掛將限制每頁嘗試刪除的資料量(例如100篇文章、200條留言等)。

匯出工具回呼返回該電子郵件地址和頁面的所有可用資料,以及是否完成的資訊。如果匯出工具回呼報告尚未完成,則會使用增加1的頁面參數再次調用它(在一個獨立的請求中)。匯出工具回呼應返回匯出的項目組數。每個項目都包含一個分組標識,該項目所屬的組名(例如留言、文章、訂單等)可選的組標籤,一個項目標識(例如留言-133)以及包含該項目要匯出的資料的一組名稱/值對。

值得一提的是,value 可能是媒體路徑,此時在匯出的索引 HTML 頁面中會增加到該媒體文件的連結。

當所有的匯出工具都完成後,WordPress 會先組裝一個「索引」HTML 文檔,這是匯出報告的核心。如果一個外掛報告了 WordPress 或其他外掛已經增加的項目的附加資料,該項目的所有資料將被一起呈現。

匯出文件在服務器上暫存 3 天,然後會被刪除。

一個外掛可以註冊一個或多個匯出工具,但大多數外掛只需要一個。現在,假設有一個假想的外掛,它向留言中增加了留言者的位置資料。

首先,假設該外掛已使用 add_comment_meta 使用 meta_key latitudelongitude 增加了位置資料。

外掛需要做的第一件事是建立一個匯出工具函式,接受電子郵件地址和頁面作為參數,例如:

/**
 * Export user meta for a user using the supplied email.
 *
 * @param string $email_address   email address to manipulate
 * @param int    $page            pagination
 *
 * @return array
 */
function wporg_export_user_data_by_email( $email_address, $page = 1 ) {
    $number = 500; // Limit us to avoid timing out
    $page   = (int) $page;

    $export_items = array();

    $comments = get_comments(
        array(
            'author_email' => $email_address,
            'number'       => $number,
            'paged'        => $page,
            'order_by'     => 'comment_ID',
            'order'        => 'ASC',
        )
    );

    foreach ( (array) $comments as $comment ) {
        $latitude  = get_comment_meta( $comment->comment_ID, 'latitude', true );
        $longitude = get_comment_meta( $comment->comment_ID, 'longitude', true );

        // Only add location data to the export if it is not empty.
        if ( ! empty( $latitude ) ) {
            // Most item IDs should look like postType-postID. If you don't have a post, comment or other ID to work with,
            // use a unique value to avoid having this item's export combined in the final report with other items
            // of the same id.
            $item_id = "comment-{$comment->comment_ID}";

            // Core group IDs include 'comments', 'posts', etc. But you can add your own group IDs as needed
            $group_id = 'comments';

            // Optional group label. Core provides these for core groups. If you define your own group, the first
            // exporter to include a label will be used as the group label in the final exported report.
            $group_label = __( 'Comments', 'text-domain' );

            // Plugins can add as many items in the item data array as they want.
            $data = array(
                array(
                    'name'  => __( 'Commenter Latitude', 'text-domain' ),
                    'value' => $latitude,
                ),
                array(
                    'name'  => __( 'Commenter Longitude', 'text-domain' ),
                    'value' => $longitude,
                ),
            );

            $export_items[] = array(
                'group_id'    => $group_id,
                'group_label' => $group_label,
                'item_id'     => $item_id,
                'data'        => $data,
            );
        }
    }

    // Tell core if we have more comments to work on still.
    $done = count( $comments ) > $number;
    return array(
        'data' => $export_items,
        'done' => $done,
    );
}

接下來,外掛需要透過使用 wp_privacy_personal_data_exporters 過濾器來註冊回呼函式(callback)。

當註冊時,你需要為匯出提供一個友好的名稱(以利除錯,此名稱不會向任何人顯示),以及回呼函式(callback),例如:

/**
 * Registers all data exporters.
 *
 * @param array $exporters
 *
 * @return mixed
 */
function wporg_register_user_data_exporters( $exporters ) {
    $exporters['my-plugin-slug'] = array(
        'exporter_friendly_name' => __( 'Comment Location Plugin', 'text-domain' ),
        'callback'               => 'my_plugin_exporter',
    );
    return $exporters;
}

add_filter( 'wp_privacy_personal_data_exporters', 'wporg_register_user_data_exporters' );

就是這樣了!現在你的外掛已經能提供資料匯出使用了!

新增個人資料清除工具至你的外掛程式

WordPress 4.9.6 還新增了一些工具,讓遵從歐盟的《一般資料保護規則》(簡稱 GDPR)等法律變得更加容易。這工具是個人資料刪除工具,支援以匿名方式刪除一位特定使用者的個人資料。它不會刪除註冊的使用者帳戶 – 這仍是管理員可以自行選擇是否完成的獨立步驟。除了儲存在 WordPress 留言等內容中的個人資料外,外掛還可以接入「橡皮擦」功能,以刪除它們收集的個人資料,無論是在像是 postmeta 之類的內容中還是在全新的自訂內容類型中(CPT)。與匯出工具一樣,“關鍵”是使用者的電子郵件地址 – 這是因為它支援刪除已經完成註冊的使用者以及未完成註冊的使用者(例如,未註冊的留言者)。

然而,由於進行個人資料清除是一個破壞性的過程,我們不想在未確認請求的情況下執行它,因此管理員使用者界面始於要求管理員輸入發出請求的使用者名稱或電子郵件地址,然後發送連結以便使用者點擊確認其請求。一旦確認了一個請求,管理員就可以為使用者啟動或強制執行個人資料清除操作。

個人資料匯出的刪除方式類似於個人資料匯出程序,並且依賴於掛勾“橡皮擦”回呼函式來完成刪除資料的麻煩工作。當管理員點擊刪除個人資料連結時,一個AJAX循環開始遍歷系統中註冊的所有清除程序,一次處理一個。除了核心內置的清除程序外,外掛可以註冊自己的清除回呼函式。

清除程序回呼界面的設計盡可能簡單。清除程序回呼函式接收正在使用的電子郵件地址和頁面參數。頁面參數(從1開始)用於避免外掛嘗試立即刪除收集到的所有個人資料而引起超時。一個行為良好的外掛將限制每頁嘗試刪除的資料量(例如 100 篇文章,200 個留言等)。

「橡皮擦」回呼函式回覆含有個人資料的項目是否已被刪除、是否還留有任何含有個人資料的項目、一個訊息陣列以供管理員檢視(解釋為何該保留的項目還需保留),以及是否完成。如果「橡皮擦」回呼函式回報還沒完成,它會再次被呼叫(在另一個請求中),而其 page 參數會遞增 1。

當所有匯出工具都被呼叫完成後,管理員使用者介面會被更新,以顯示是否已刪除所有找到的個人資料以及任何解釋為何該保留的個人資料的訊息。

現在,我們來設計一個假想的外掛,這個外掛可以將留言者的位置資料新增至留言中。假定該外掛已使用 add_comment_meta 函式,將位置資料以 latitudelongitude 兩種 meta_key 儲存。

第一件事情是,該外掛需要建立一個橡皮擦函式,接受一個電子郵件地址和一個分頁參數,例如:

/**
 * Removes any stored location data from a user's comment meta for the supplied email address.
 *
 * @param string $email_address   email address to manipulate
 * @param int    $page            pagination
 *
 * @return array
 */
function wporg_remove_location_meta_from_comments_for_email( $email_address, $page = 1 ) {
    $number = 500; // Limit us to avoid timing out
    $page   = (int) $page;

    $comments = get_comments(
        array(
            'author_email' => $email_address,
            'number'       => $number,
            'paged'        => $page,
            'order_by'     => 'comment_ID',
            'order'        => 'ASC',
        )
    );

    $items_removed = false;

    foreach ( (array) $comments as $comment ) {
        $latitude  = get_comment_meta( $comment->comment_ID, 'latitude', true );
        $longitude = get_comment_meta( $comment->comment_ID, 'longitude', true );

        if ( ! empty( $latitude ) ) {
            delete_comment_meta( $comment->comment_ID, 'latitude' );
            $items_removed = true;
        }

        if ( ! empty( $longitude ) ) {
            delete_comment_meta( $comment->comment_ID, 'longitude' );
            $items_removed = true;
        }
    }

    // Tell core if we have more comments to work on still
    $done = count( $comments ) < $number;
    return array(
        'items_removed'  => $items_removed,
        'items_retained' => false, // always false in this example
        'messages'       => array(), // no messages in this example
        'done'           => $done,
    );
}

接下來,外掛需要註冊 wp_privacy_personal_data_erasers 過濾器回呼函式。

在註冊時,你需要為「橡皮擦」指定一個友好的名稱(以便於除錯,此名稱不會顯示給任何人),以及回呼函式,例如:

/**
 * Registers all data erasers.
 *
 * @param array $exporters
 *
 * @return mixed
 */
function wporg_register_privacy_erasers( $erasers ) {
    $erasers['my-plugin-slug'] = array(
        'eraser_friendly_name' => __( 'Comment Location Plugin', 'text-domain' ),
        'callback'             => 'wporg_remove_location_meta_from_comments_for_email',
    );
    return $erasers;
}

add_filter( 'wp_privacy_personal_data_erasers', 'wporg_register_privacy_erasers' );

就是這樣了!你的外掛現在可以清除它的個人資料了!

隱私權相關的設定、勾子和權限

隱私權工具最初是在 WordPress 4.9.6 中推出。這些工具旨在讓(並鼓勵)開發人員將其作為個資匯出工具、個資刪除工具與隱私權政策指南的一部分使用。

自那時以來,已經引入了一些新的勾子來擴展可用的功能。這些勾子可以讓開發人員在匯出和清除請求中包含其他個人資料,並為隱私權政策指南引入建議的內容。

除了控制這些工具的能力之外,還有幾個新的過濾器可用於請求和確認電子郵件,使這些通知的控制更加精細。

設定

wp_page_for_privacy_policy – 包含網站隱私政策頁面的頁面編號

事件動作 Action (勾子名稱)

user_request_action_confirmed – fired when a user confirms a privacy request

wp_privacy_delete_old_export_files – a scheduled action used to prune old exports from the personal data exports folder

wp_privacy_personal_data_erased – fired after the last page of the last eraser is complete

wp_privacy_personal_data_export_file – used to create a personal data export file as part of the export flow

wp_privacy_personal_data_export_file_created – fires after a personal data export file has been created

事件過濾 Filter (勾子名稱)

privacy_policy_url – filters the URL of the privacy policy page.

the_privacy_policy_link – filters the privacy policy page link HTML.

wp_get_default_privacy_policy_content – filters the default content suggested for inclusion through the privacy policy guide.

user_request_action_confirmed_message – allows modifying the action confirmation message displayed to the user

user_request_action_description – filters the user action description.

user_request_action_email_content – filters the text of the email sent when an account action is attempted.

user_request_action_email_headers – filters the headers of the email sent when an account action is attempted.

user_request_action_email_subject – filters the subject of the email sent when an account action is attempted.

user_request_confirmed_email_content – filters the body of the user request confirmation email.

user_request_confirmed_email_headers – filters the headers of the user request confirmation email.

user_request_confirmed_email_subject – filters the subject of the user request confirmation email.

user_request_confirmed_email_to – filters the recipient of the data request confirmation notification.

user_request_key_expiration – filters the expiration time of confirmation keys for user requests.

wp_privacy_additional_user_profile_data – filter to extend the user’s profile data for the privacy exporter.

wp_privacy_export_expiration – controls how old export files are allowed to get, default is 3 days

wp_privacy_personal_data_email_content – allows modifying the email message send to users with their personal data export file link

wp_privacy_personal_data_email_headers – filters the headers of the email sent with a personal data export file.

wp_privacy_personal_data_email_subject – filters the subject of the email sent when an export request is completed.

wp_privacy_personal_data_email_to – filters the recipient of the personal data export email notification.

注意: wp_privacy_personal_data_email_to 應該小心使用,以避免將資料匯出連結發送給錯誤的收件人電子郵件地址。

wp_privacy_personal_data_erasers – supports registration of core and plugin personal data erasers

wp_privacy_personal_data_erasure_page – Filters a page of personal data eraser data. Allows the erasure response to be consumed by destinations in addition to Ajax.

wp_privacy_personal_data_exporters – supports registration of core and plugin personal data exporters

wp_privacy_personal_data_export_page – filters a page of personal data exporter data. Used to build the export report. Allows the export response to be consumed by destinations in addition to Ajax.

wp_privacy_anonymize_data – filters the anonymous data for each type.

wp_privacy_exports_dir – filters the directory used to store personal data export files.

wp_privacy_exports_url – filters the URL of the directory used to store personal data export files.

user_confirmed_action_email_content – Filters the body of the user request confirmation email. The email is sent to an administrator when an user request is confirmed.

user_erasure_fulfillment_email_to – Filters the recipient of the data erasure fulfillment notification.

user_erasure_complete_email_subject – Filters the subject of the email sent when an erasure request is completed.

user_confirmed_action_email_content – Filters the body of the data erasure fulfillment notification. The email is sent to a user when a their data erasure request is fulfilled by an administrator.

user_erasure_complete_email_headers – Filters the headers of the data erasure fulfillment notification.

權限 Capability

使用隱私權工具的存取權限由一些新能力控制,系統管理員(非多站點式安裝)預設擁有這些能力。這些能力包括:

erase_others_personal_data – determines if the Erase Personal Data sub-menu is available under Tools

export_others_personal_data – determines if the Export Personal Data sub-menu is available under Tools

manage_privacy_options – determines if the Privacy sub-menu is available under Settings


Share:

作者: Chun

WordPress 社群貢獻者、開源社群推廣者。專注於 WordPress 外掛開發、網站效能最佳化、伺服器管理,以及 iDempiere 開源 ERP 導入與客製開發。曾參與 WordCamp Taipei 等社群活動,GitHub Arctic Code Vault Contributor。提供資訊顧問、WordPress 開發教學、主機最佳化與企業 ERP 整合服務。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *


文章
Filter
Apply Filters
Mastodon