enqueue_public_style();
return '
' . esc_html( Formii_Email_Notification::get_auto_reply_limited_notice_text() ) . '
';
}
/**
* フォームショートコードをレンダリング
*
* @since 1.0.0
* @param array $atts ショートコード属性.
* @return string レンダリングされたフォームHTML
*/
public function render_form( $atts ) {
// 属性をパース
$atts = shortcode_atts(
array(
'id' => 0,
),
$atts,
'formii_form'
);
// フォームIDをサニタイズ
$form_id = absint( $atts['id'] );
// フォームIDが無効な場合
if ( $form_id <= 0 ) {
return $this->render_error( __( 'Form ID is not specified.', 'formii' ) );
}
// フォームデータを取得
$form = $this->get_form_data( $form_id );
// フォームが存在しない場合
if ( ! $form ) {
return $this->render_error( __( 'The specified form was not found.', 'formii' ) );
}
// フォームが公開されていない場合
if ( isset( $form->status ) && $form->status !== 'published' ) {
// 管理者のみ下書きフォームを表示
if ( ! current_user_can( 'manage_options' ) ) {
return $this->render_error( __( 'This form is not currently published.', 'formii' ) );
}
}
// フォーム設定をパース
$form_config = $this->parse_form_config( $form->form_config );
// 受付期間・定員(判定は Formii Pro が formii_form_availability で行う)。
// 受付不可ならフォームの代わりに文言を出す。
$availability = Formii_Form_Availability::check(
$form_id,
is_array( $form_config ) ? $form_config : array(),
Formii_Form_Availability::CONTEXT_RENDER
);
if ( ! $availability['open'] ) {
$this->enqueue_public_style();
// JS 無効時の送信結果は、ここで読み出して消す。
$result = Formii_Form_Handler::consume_submission_result( $form_id );
// 最後の1枠が埋まった送信のリダイレクト先は、すでに受付終了になっている。
// 送信は成功しているため、締め切りの文言ではなく完了メッセージを見せる。
if ( is_array( $result ) && isset( $result['type'] ) && 'success' === $result['type'] ) {
return $this->render_success_html(
$form_id,
isset( $result['message'] ) ? (string) $result['message'] : '',
isset( $result['notice'] ) ? (string) $result['notice'] : ''
);
}
$not_sent = is_array( $result ) && isset( $result['code'] ) && 'form_closed' === $result['code'];
return Formii_Form_Availability::render_closed_html( $form_id, $availability, $not_sent );
}
// フォームをレンダリング
return $this->render_form_html( $form_id, $form->form_name, $form_config );
}
/**
* フォームデータを取得
*
* @since 1.0.0
* @access private
* @param int $form_id フォームID.
* @return object|false フォームデータ(存在しない場合はfalse)
*/
private function get_form_data( $form_id ) {
global $wpdb;
$table_name = $wpdb->prefix . 'formii_forms';
// キャッシュをチェック
$cache_key = 'form_' . $form_id;
$form = wp_cache_get( $cache_key, 'formii' );
if ( $form === false ) {
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
$form = $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$table_name} WHERE id = %d AND deleted_at IS NULL",
$form_id
)
);
if ( $form ) {
wp_cache_set( $cache_key, $form, 'formii', HOUR_IN_SECONDS );
}
}
return $form;
}
/**
* フォーム設定をパース
*
* @since 1.0.0
* @access private
* @param string $form_config_json JSON形式のフォーム設定.
* @return array パースされたフォーム設定
*/
private function parse_form_config( $form_config_json ) {
$config = json_decode( $form_config_json, true );
if ( json_last_error() !== JSON_ERROR_NONE ) {
return array(
'fields' => array(),
'settings' => array(),
);
}
return $config;
}
/**
* フォームHTMLをレンダリング
*
* @since 1.0.0
* @access private
* @param int $form_id フォームID.
* @param string $form_name フォーム名.
* @param array $form_config フォーム設定.
* @return string レンダリングされたHTML
*/
private function render_form_html( $form_id, $form_name, $form_config ) {
// 出力バッファリング開始
ob_start();
// サンクスページ設定を取得
$thanks_page_settings = Formii_Thanks_Page_Settings::get_settings( $form_id );
// CSS/JSを読み込み(サンクスページ設定も渡す)
$this->enqueue_form_assets( $form_id, $thanks_page_settings );
// テンプレート変数を設定
$fields = isset( $form_config['fields'] ) ? $form_config['fields'] : array();
$settings = isset( $form_config['settings'] ) ? $form_config['settings'] : array();
// デフォルト設定。
// 送信ボタン文言はビルダーが submitLabel で保存する(旧キー submitButtonText も後方互換で許容)。
if ( isset( $settings['submitLabel'] ) && '' !== $settings['submitLabel'] ) {
$submit_button_text = $settings['submitLabel'];
} elseif ( isset( $settings['submitButtonText'] ) && '' !== $settings['submitButtonText'] ) {
$submit_button_text = $settings['submitButtonText'];
} else {
$submit_button_text = __( 'Submit', 'formii' );
}
// 確認画面メッセージ(確認画面の説明として form-confirmation.php で使用)。
$confirmation_message = isset( $settings['confirmationMessage'] ) && '' !== $settings['confirmationMessage']
? $settings['confirmationMessage']
: '';
// JS 無効時の通常 POST の送信結果(リダイレクト先で一度だけ表示する)
$formii_result = Formii_Form_Handler::consume_submission_result( $form_id );
// テンプレートファイルを読み込み
if ( file_exists( FORMII_PLUGIN_DIR . 'public/partials/form-display.php' ) ) {
include FORMII_PLUGIN_DIR . 'public/partials/form-display.php';
} else {
// フォールバック: 基本的なHTMLを生成
$this->render_fallback_form( $form_id, $form_name, $fields, $submit_button_text );
}
// 出力バッファの内容を取得
return ob_get_clean();
}
/**
* フォールバック用の基本的なフォームをレンダリング
*
* @since 1.0.0
* @access private
* @param int $form_id フォームID.
* @param string $form_name フォーム名.
* @param array $fields フィールド配列.
* @param string $submit_button_text 送信ボタンテキスト.
*/
private function render_fallback_form( $form_id, $form_name, $fields, $submit_button_text ) {
?>
>
',
(int) $form_id,
esc_html( $message ),
'' !== $notice ? '' . esc_html( $notice ) . '
' : ''
);
}
/**
* 公開フォームの CSS を読み込む(締め切りの文言だけを出すときにも使う)
*
* @since 1.5.0
* @access private
*/
private function enqueue_public_style() {
if ( wp_style_is( 'formii-public', 'enqueued' ) ) {
return;
}
$css_path = FORMII_PLUGIN_DIR . 'public/css/formii-public.css';
$css_ver = file_exists( $css_path ) ? (string) filemtime( $css_path ) : FORMII_VERSION;
wp_enqueue_style(
'formii-public',
FORMII_PLUGIN_URL . 'public/css/formii-public.css',
array(),
$css_ver
);
}
/**
* フォーム用のアセット(CSS/JS)を読み込み
*
* @since 1.0.0
* @access private
* @param int $form_id フォームID
* @param array $thanks_page_settings サンクスページ設定
*/
private function enqueue_form_assets( $form_id = 0, $thanks_page_settings = array() ) {
// CSS
$this->enqueue_public_style();
// reCAPTCHA(Google への外部通信)は Formii Pro が読み込む。無料コアは外部通信を行わない。
// JS
if ( ! wp_script_is( 'formii-public', 'enqueued' ) ) {
$dependencies = array( 'jquery' );
// アセット更新時に確実に再取得させるため filemtime をバージョンに用いる。
$js_path = FORMII_PLUGIN_DIR . 'public/js/formii-public.js';
$js_ver = file_exists( $js_path ) ? (string) filemtime( $js_path ) : FORMII_VERSION;
wp_enqueue_script(
'formii-public',
FORMII_PLUGIN_URL . 'public/js/formii-public.js',
$dependencies,
$js_ver,
true
);
// REST API情報とサンクスページ設定をローカライズ
$localize_data = array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'restUrl' => rest_url( 'formii/v1/' ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'i18n' => array(
// エラーモーダルのタイトル(送信時バリデーションエラー等)。
'errorTitle' => __( 'Error', 'formii' ),
// 通信失敗・応答が読めないときのエラーモーダル本文。
'genericError' => __( 'An error occurred. Please try again.', 'formii' ),
// 入力確認付きメールのクライアント側一致ヒント(サーバでも検証)。
'emailMismatch' => __( 'Email addresses do not match.', 'formii' ),
/* translators: date format for the birthdate confirmation — %y = year, %m = month, %d = day. */
'birthdateFormat' => __( '%y-%m-%d', 'formii' ),
// クライアント側フィールド検証メッセージ(%d はプレースホルダ)。
'required' => __( 'This field is required', 'formii' ),
'invalidEmail' => __( 'Invalid email address format.', 'formii' ),
'invalidUrl' => __( 'Invalid URL format.', 'formii' ),
'invalidTel' => __( 'Invalid phone number format.', 'formii' ),
'invalidNumber' => __( 'Please enter a number.', 'formii' ),
'invalidFormat' => __( 'Invalid input format', 'formii' ),
'invalidKatakana' => __( 'Please enter in full-width katakana.', 'formii' ),
// 生年月日を一部だけ選んだとき(任意欄でも不完全な日付は送らせない)。
'incompleteDate' => __( 'Please select the year, month, and day.', 'formii' ),
// 確認画面の同意チェック表示(メール・問い合わせ詳細と同じ文言)。
'agreed' => __( 'Agreed', 'formii' ),
// 数値・日付の範囲と、確認入力の一致(Formii Pro の高度フィールドが出力する属性を検証する)。
/* translators: %s: 最小値 */
'numberMin' => __( 'Please enter a number greater than or equal to %s', 'formii' ),
/* translators: %s: 最大値 */
'numberMax' => __( 'Please enter a number less than or equal to %s', 'formii' ),
/* translators: %s: 選べる最初の日付 */
'dateMin' => __( 'Please enter a date on or after %s', 'formii' ),
/* translators: %s: 選べる最後の日付 */
'dateMax' => __( 'Please enter a date on or before %s', 'formii' ),
'confirmMismatch' => __( 'The values do not match. Please re-enter.', 'formii' ),
/* translators: %d: 最小文字数 */
'minLength' => __( 'Please enter at least %d characters', 'formii' ),
/* translators: %d: 最大文字数 */
'maxLength' => __( 'Please enter no more than %d characters', 'formii' ),
/* translators: %d: 最大ファイルサイズ(MB) */
'fileTooLarge' => __( 'File size must be less than %dMB.', 'formii' ),
'fileTypeNotAllowed' => __( 'This file type is not allowed.', 'formii' ),
// 受付期間外・定員到達でフォームを文言に差し替えるとき(Formii Pro の受付期間・定員)。
'formClosed' => Formii_Form_Availability::fallback_message(),
'notSent' => __( 'Your entry has not been sent.', 'formii' ),
),
);
wp_localize_script(
'formii-public',
'formiiPublic',
$localize_data
);
}
// サンクスページ設定はフォームごとに異なるため、スクリプトのエンキュー
// 判定(初回のみ true)の外で毎回注入する。これにより同一ページに複数の
// フォームを設置しても、各フォームの設定が formiiPublic.thanksPageSettings
// にマージされ、2つ目以降のリダイレクト/メッセージ設定が失われない。
if ( $form_id > 0 && ! empty( $thanks_page_settings ) ) {
$per_form = array(
$form_id => array(
'type' => isset( $thanks_page_settings['type'] ) ? $thanks_page_settings['type'] : 'message',
'url' => isset( $thanks_page_settings['url'] ) ? esc_url( $thanks_page_settings['url'] ) : '',
'message' => isset( $thanks_page_settings['message'] ) ? wp_kses_post( $thanks_page_settings['message'] ) : '',
),
);
wp_add_inline_script(
'formii-public',
'window.formiiPublic = window.formiiPublic || {};'
. 'window.formiiPublic.thanksPageSettings = Object.assign('
. 'window.formiiPublic.thanksPageSettings || {}, '
. wp_json_encode( $per_form ) . ');',
'before'
);
}
}
/**
* エラーメッセージをレンダリング
*
* @since 1.0.0
* @access private
* @param string $message エラーメッセージ.
* @return string レンダリングされたエラーHTML
*/
private function render_error( $message ) {
// 管理者のみエラーを表示
if ( ! current_user_can( 'manage_options' ) ) {
return '';
}
return sprintf(
'
%s %s
',
esc_html__( 'Formii Error:', 'formii' ),
esc_html( $message )
);
}
}