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 ) { ?>
render_field_fallback( $field ); ?>

>
', (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( '', esc_html__( 'Formii Error:', 'formii' ), esc_html( $message ) ); } }