# ivyforms/0.3/backend/src/Services/Shortcode/ShortcodeService.php

The Innovative Form Builder – IvyForms, version 0.3. 407 lines.

- Page: https://pluginprobe.com/plugins/ivyforms/0.3/code/backend/src/Services/Shortcode/ShortcodeService.php
- Raw: https://pluginprobe.com/plugins/ivyforms/0.3/raw/backend/src/Services/Shortcode/ShortcodeService.php
- Modified: 2025-08-20T10:20:12+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/ivyforms/0.3/code/backend/src/Services/Shortcode/ShortcodeService.php#L10-L20`.

```php
<?php

/**
 * @copyright © TMS-Plugins. All rights reserved.
 * @licence   See LICENCE.md for license details.
 */

namespace IvyForms\Services\Shortcode;

// phpcs:disable PSR1.Files.SideEffects
if (!defined('ABSPATH')) {
    exit; // Exit if accessed directly
}

use IvyForms\Common\Exceptions\ValidationException;
use IvyForms\Factory\Field\FieldFactory;
use IvyForms\Routes\Routes;
use IvyForms\Services\API\IvyFormsAPI;
use IvyForms\Services\Settings\SettingsService;
use IvyForms\Services\Translations\FrontendStrings;
use IvyForms\Factory\Security\SecurityServiceFactory;
use IvyForms\Services\Security\SecurityService;

/**
 * Class ShortcodeService
 *
 * @package IvyForms\Services\Shortcode
 */
class ShortcodeService
{
    public static int $counter = 0;
    /**
     * @var mixed[]
     */
    public static array $formList = [];

    /**
     * Shortcode handler
     *
     * @param mixed[] $atts
     *
     * @return string
     */
    public static function shortcodeHandler(array $atts): string
    {
        $atts = shortcode_atts(
            [
                'id'  => '0'
            ],
            $atts
        );

        if (empty($atts['id'])) {
            return '';
        }

        $formId = (int)$atts['id'];
        $form = IvyFormsAPI::getForm($formId);

        // Check if the form is published
        if (is_wp_error($form) || !($form->isPublished())) {
            return '';
        }

        $counter = self::$counter++;

        self::getFormList($formId, $counter);

        self::enqueueScripts();

        ob_start();
        include IVYFORMS_PATH . '/view/frontend/view.php';
        $html = ob_get_contents();
        ob_end_clean();

        return $html;
    }

    /**
     * Get form list
     *
     * @param int $formId
     * @param int $counter
     *
     * @return void
     */
    public static function getFormList(int $formId, int $counter)
    {
        $form = IvyFormsAPI::getForm($formId);

        $fields = IvyFormsAPI::getFields($form->getId());

        $confirmations = IvyFormsAPI::getConfirmations($form->getId());

        $fieldsMap = [];

        foreach ($fields as $field) {
            $fieldsMap[] = $field->toArray();
        }

        $confirmationsMap = [];

        foreach ($confirmations as $confirmation) {
            $confirmationsMap[] = is_object($confirmation) && method_exists($confirmation, 'toArray')
                ? $confirmation->toArray()
                : $confirmation;
        }

        $form->setFields($fieldsMap);

        $formArr = $form->toArray();

        self::$formList[$counter] = [
            'form'    => $formArr,
            'fields'  => $fieldsMap,
            'id'      => $form->getId(),
            'counter' => $counter,
            'nonce'   =>  wp_create_nonce('ivyformsFrontSubmissionNonce_' .  $form->getId()),
            'confirmations' => $confirmationsMap,
        ];
    }

    /**
     * Enqueue handler
     */
    public static function enqueueScripts(): void
    {
        $scriptId = IVYFORMS_DEV ? 'ivyforms_scripts_dev_vite' : 'ivyforms_script_index';   // @phpstan-ignore-line
        $scriptSrc = IVYFORMS_DEV ? // @phpstan-ignore-line
            'http://localhost:5173/src/assets/js/public/public.ts' :
            IVYFORMS_URL . 'frontend/dist/public.js';

        $styleSrc = IVYFORMS_DEV ? '' : IVYFORMS_URL . 'frontend/dist/index.css'; // @phpstan-ignore-line
        $stylePublicSrc = IVYFORMS_DEV ? '' : IVYFORMS_URL . 'frontend/dist/public.css'; // @phpstan-ignore-line
        $scriptVersion = IVYFORMS_DEV ? null : IVYFORMS_VERSION;// @phpstan-ignore-line

        wp_enqueue_script(
            $scriptId,
            $scriptSrc,
            [],
            $scriptVersion,
            true
        );

        // TODO: Improve this with merging styles
        if ($styleSrc) { // @phpstan-ignore-line
            wp_enqueue_style(
                'ivyforms_style_index',
                $styleSrc,
                [],
                $scriptVersion
            );
            wp_enqueue_style(
                'ivyforms_style_public',
                $stylePublicSrc,
                [],
                $scriptVersion
            );

            // TODO Create a helper service for adding styles
            wp_add_inline_style(
                'ivyforms_style_index',
                /** @lang CSS */
                '.ivyforms-skeleton {
                width: 100%;
                max-width: 720px;
                margin: 0 auto;
            }

            .ivyforms-skeleton-title {
                height: 28px;
                width: 40%;
                background: #dbdee3;
                margin-bottom: 12px;
            }

            .ivyforms-skeleton-field {
                margin-bottom: 16px;
            }

            .ivyforms-skeleton-label {
                height: 22px;
                width: 20%;
                background: #dbdee3;
                font-size: 14px;
                border-radius: 4px;
                margin-bottom: 4px;
            }

            .ivyforms-skeleton-input {
                line-height: 22px;
                font-size: 16px;
                background: #dbdee3;
                border-radius: 8px;
                width: 100%;
            }

            .ivyforms-skeleton-email,
            .ivyforms-skeleton-textarea,
            .ivyforms-skeleton-text,
            .ivyforms-skeleton-number,
            .ivyforms-skeleton-phone {
                height: 40px;
            }

            .ivyforms-skeleton-textarea {
                height: 60px;
            }

            .ivyforms-skeleton-button {
                height: 40px;
                width: 100px;
                background: #c0c5cb;
                border-radius: 6px;
                margin-top: 12px;
            }

            .ivyforms-skeleton-animate {
                animation: pulse 1.5s infinite ease-in-out;
            }

            @keyframes pulse {
                0%, 100% {
                    opacity: 1;
                }
                50% {
                    opacity: 0.4;
                }
            }

            .ivyforms-preview {
                width: 100%;
                max-width: 720px;
                margin: 0 auto;
                background: #fff;
                padding: 40px;
            }

            .ivyforms-preview h1 {
                font-size: 50px !important;
                font-weight: normal !important;
                margin-top: 0 !important;
                margin-left: 0 !important;
                margin-bottom: 20px !important;
                padding: 0 !important;
            }'
            );
        }
        self::enqueueRecaptchaScript($scriptId);

        wp_localize_script(
            $scriptId,
            'wpIvyFormDataList',
            self::$formList
        );

        wp_localize_script(
            $scriptId,
            'wpIvyLabels',
            array_merge(
                FrontendStrings::getFormRenderStrings(),
            )
        );

        wp_localize_script(
            $scriptId,
            'wpIvyApiSettings',
            [
                'root' => esc_url_raw(rest_url()),
                'nonce' => wp_create_nonce('wp_rest'),
                'namespace' => Routes::$routeNamespace,
            ]
        );
        wp_localize_script(
            $scriptId,
            'wpIvyDateFormat',
            [
                'dateFormat'     => get_option('date_format'),
                'timeFormat'     => get_option('time_format'),
                'dateTimeFormat' => get_option('date_format') . ' ' . get_option('time_format'),
                'locale'         => get_locale(),
            ]
        );
    }

    /**
     * Add script attribute for module type
     *
     * @param array<string, mixed> $attributes
     *
     * @return array<string, mixed>
     */
    public static function addScriptAttribute(array $attributes): array
    {
        $scriptArray = [
            'ivyforms_scripts_dev_main-js',
            'ivyforms_scripts_dev_vite-js',
            'ivyforms_script_index-js',
            'ivyforms_scripts_public-js',
        ];

        if (isset($attributes['id']) && in_array($attributes['id'], $scriptArray, true)) {
            $attributes['type'] = 'module';
        }

        return $attributes;
    }

    /**
     * Enqueue reCAPTCHA script if configured
     *
     * @param string $scriptId
     * @throws ValidationException
     */
    public static function enqueueRecaptchaScript(string $scriptId): void
    {
        $securityService = self::getSecurityService();
        $formFields = self::getAllFormFields();

        // Get security configuration using our enhanced service
        $securityConfig = $securityService->getFrontendSecurityConfig($formFields);

        // Enqueue Google script if CAPTCHA is configured and has fields
        if (self::shouldEnqueueCaptchaScript($securityConfig)) {
            self::enqueueGoogleRecaptchaScript($securityConfig['captcha']);
        }

        wp_localize_script(
            $scriptId,
            'wpIvyRecaptchaConfig',
            $securityConfig['captcha'] ?? []
        );
    }

    /**
     * Get security service instance
     *
     * @return SecurityService
     */
    private static function getSecurityService(): SecurityService
    {
        $settingsService = new SettingsService();
        $securityFactory = new SecurityServiceFactory($settingsService);
        return new SecurityService($securityFactory);
    }

    /**
     * Get all form fields from currently loaded forms
     *
     * @return array<object> Array of form field objects
     * @throws ValidationException
     */
    private static function getAllFormFields(): array
    {
        $allFields = [];

        foreach (self::$formList as $formData) {
            foreach ($formData['fields'] as $fieldData) {
                // Create a simple object to match the expected interface
                $field = FieldFactory::create($fieldData);

                $allFields[] = $field;
            }
        }

        return $allFields;
    }

    /**
     * Check if CAPTCHA script should be enqueued
     *
     * @param array<string, mixed> $securityConfig
     * @return bool
     */
    private static function shouldEnqueueCaptchaScript(array $securityConfig): bool
    {
        return isset($securityConfig['captcha']['recaptcha']) && $securityConfig['captcha']['enabled'];
    }

    /**
     * Enqueue Google reCAPTCHA script using security service
     *
     * @param array<string, mixed> $captchaConfig
     */
    private static function enqueueGoogleRecaptchaScript(array $captchaConfig): void
    {
        if (!isset($captchaConfig['recaptcha'])) {
            return;
        }

        $recaptchaConfig = $captchaConfig['recaptcha'];
        $scriptUrl = $recaptchaConfig['scriptUrl'] ?? '';

        if (empty($scriptUrl)) {
            return;
        }

        wp_enqueue_script(
            'google-recaptcha',
            $scriptUrl,
            [],
            null, // No version for external script
            true // Load in footer
        );
    }
}

```
