Viewing File: /usr/local/cpanel/base/3rdparty/roundcube/plugins/formbricks/lib/FormbricksScriptGenerator.php

<?php

/**
 * Generates JavaScript for Formbricks integration in Roundcube.
 */

declare(strict_types=1);

namespace RoundcubeFormbricks;

/**
 * Handles generation of Formbricks initialization.
 */
class FormbricksScriptGenerator
{
    private const JSON_SAFE_FLAGS = JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT;

    /**
     * @var array
     */
    private $config;

    /**
     * @param array $config Plugin configuration
     */
    public function __construct(array $config)
    {
        $this->config = $config;
    }

    /**
     * @return string HTML script tag with initialization code
     */
    public function generate(string $user_id, string $language): string
    {
        $app_url = $this->config['app_url'] ?? '';
        $environment_id = $this->config['environment_id'] ?? '';

        // Get debug mode setting (default to false for production safety)
        $debug_mode = (bool)($this->config['debug_mode'] ?? false);

        // Create configuration array for safe JSON encoding
        $config = [
            'appUrl' => $app_url,
            'environmentId' => $environment_id,
            'targetUserId' => $user_id,
            'targetLanguage' => $language,
            'debugMode' => $debug_mode,
        ];

        $json_config = json_encode($config, self::JSON_SAFE_FLAGS);

        $script = <<<HTML
            <!-- START Formbricks Integration -->
            <script type="text/javascript">
            // Set global config and utilities for the Formbricks library to access
            window.formbricksConfig = window.formbricksConfig || {};
            window.formbricksConfig.debugMode = {$json_config}.debugMode;

            window.FormbricksUtils = window.FormbricksUtils || {
                /** Debug logging (only when debug mode enabled) */
                debugLog: function(message, data) {
                    if (window.formbricksConfig.debugMode && window.console && console.log) {
                        if (data !== undefined) {
                            console.log('Formbricks: ' + message, data);
                        } else {
                            console.log('Formbricks: ' + message);
                        }
                    }
                },

                /** Error logging (always enabled) */
                errorLog: function(message, error) {
                    if (window.console && console.error) {
                        console.error('Formbricks error: ' + message, error);
                    }
                },

                /** sessionStorage getter with error handling */
                lsGet: function(key) {
                    try { return sessionStorage.getItem(key); }
                    catch(e) { this.debugLog('sessionStorage read failed: ' + key, e); return null; }
                },

                /** sessionStorage setter with error handling */
                lsSet: function(key, value) {
                    try { sessionStorage.setItem(key, value); }
                    catch(e) { this.debugLog('sessionStorage write failed: ' + key, e); }
                },

                /** sessionStorage remove with error handling */
                lsRemove: function(key) {
                    try { sessionStorage.removeItem(key); this.debugLog('Removed: ' + key); }
                    catch(e) { this.errorLog('sessionStorage remove failed: ' + key, e); }
                },

                /** Logout and clear user tracking keys */
                performLogout: function() {
                    var self = this;
                    self.debugLog('performLogout called');

                    if (!window.formbricks || typeof window.formbricks.logout !== 'function') {
                        self.debugLog('Logout not available, clearing keys');
                        self.lsRemove('roundcube_formbricks_user');
                        return Promise.resolve();
                    }

                    return window.formbricks.logout()
                        .then(function() { self.debugLog('Logout completed'); })
                        .catch(function(error) { self.errorLog('Logout failed', error); })
                        .finally(function() {
                            self.lsRemove('roundcube_formbricks_user');
                            if (self.lsGet('formbricks-js')) self.lsRemove('formbricks-js');
                        });
                }
            };
            </script>
            <script type="text/javascript">
            (function() {
                var config = {$json_config};
                var appUrl = config.appUrl;
                var environmentId = config.environmentId;
                var targetUserId = config.targetUserId;
                var targetLanguage = config.targetLanguage;
                var debugMode = config.debugMode;

                // Timing constants (only used for fallback scenarios)
                var FORMBRICKS_INIT_DELAY = 500;  // Fallback delay if setup promise fails

                // Use global utilities
                var utils = window.FormbricksUtils;
                var debugLog = utils.debugLog.bind(utils);
                var errorLog = utils.errorLog.bind(utils);

                /** Track a Formbricks action (must exist in dashboard) */
                function trackAction(actionName) {
                    if (!window.formbricks || typeof window.formbricks.track !== 'function') {
                        errorLog('track() not available', new Error('Method not found'));
                        return Promise.reject(new Error('Method not found'));
                    }
                    debugLog('Tracking: ' + actionName);
                    return window.formbricks.track(actionName)
                        .then(function() { debugLog('Tracked: ' + actionName); })
                        .catch(function(e) { errorLog('Track failed: ' + actionName, e); throw e; });
                }

                /** Initialize Formbricks user with ID and language */
                function initializeUser() {
                    debugLog('Initializing user: ' + targetUserId);

                    return window.formbricks.setUserId(targetUserId)
                        .then(function() {
                            debugLog('User ID set');
                            utils.lsSet('roundcube_formbricks_user', targetUserId);
                        })
                        .catch(function(error) {
                            // Handle "already set" case (states out of sync)
                            var isAlreadySet = error && (error.code === 'forbidden' || 
                                (error.message && error.message.indexOf('userId is already set') !== -1));
                            if (isAlreadySet) {
                                debugLog('User already set, syncing state');
                                utils.lsSet('roundcube_formbricks_user', targetUserId);
                                return;
                            }
                            throw error;
                        })
                        .then(function() { return window.formbricks.setLanguage(targetLanguage); })
                        .then(function() {
                            debugLog('Language set');
                            return trackAction('roundcube_webmail_access');
                        })
                        .then(function() { debugLog('Formbricks ready', targetUserId); })
                        .catch(function(e) { errorLog('User init failed', e); });
                }

                /** Manage user session based on current state */
                function manageUserSession() {
                    var currentUserId = utils.lsGet('roundcube_formbricks_user');

                    if (currentUserId === targetUserId) {
                        // Already initialized - skip setUserId
                        debugLog('User initialized, skipping setUserId');
                        window.formbricks.setLanguage(targetLanguage)
                            .then(function() {
                                debugLog('Language set');
                                return trackAction('roundcube_webmail_access');
                            })
                            .then(function() { debugLog('Formbricks ready', targetUserId); })
                            .catch(function(e) { errorLog('Language/track failed', e); });
                        return;
                    }

                    // Different user or first login - logout first for clean state
                    debugLog('New session for: ' + targetUserId);
                    utils.performLogout()
                        .catch(function(e) { errorLog('Pre-init logout error', e); })
                        .then(function() {
                            debugLog('Proceeding with init');
                            return initializeUser();
                        });
                }

                /** Initialize Formbricks after script loads */
                function onFormbricksLoaded() {
                    if (!window.formbricks) return;

                    window.formbricks.setup({ environmentId: environmentId, appUrl: appUrl })
                        .then(function() {
                            debugLog('Setup complete');
                            manageUserSession();
                        })
                        .catch(function(error) {
                            errorLog('Setup failed', error);
                            setTimeout(manageUserSession, FORMBRICKS_INIT_DELAY);
                        });
                }

                // Load Formbricks script dynamically
                var script = document.createElement("script");
                script.type = "text/javascript";
                script.async = true;
                script.src = appUrl + "/js/formbricks.umd.cjs";
                script.onload = onFormbricksLoaded;

                var firstScript = document.getElementsByTagName("script")[0];
                if (firstScript && firstScript.parentNode) {
                    firstScript.parentNode.insertBefore(script, firstScript);
                } else {
                    document.head.appendChild(script);
                }
            })();
            </script>
            <script type="text/javascript">
            // Roundcube Integration
            (function(window, document) {
                'use strict';

                var utils = window.FormbricksUtils;
                var debugLog = utils.debugLog.bind(utils);
                var errorLog = utils.errorLog.bind(utils);

                if (!window.rcmail) {
                    if (window.console && console.warn) console.warn('Formbricks: rcmail not found');
                    return;
                }

                function initLogoutHandler() {
                    var originalCommand = window.rcmail.command;
                    var logoutInProgress = false;

                    window.rcmail.command = function(command, props) {
                        var self = this, args = arguments;

                        if (command === 'switch-task' && props === 'logout') {
                            debugLog('Logout detected');
                            if (logoutInProgress) { debugLog('Logout in progress'); return false; }
                            logoutInProgress = true;

                            utils.performLogout()
                                .then(function() {
                                    debugLog('Cleanup done, proceeding');
                                    originalCommand.apply(self, args);
                                })
                                .catch(function(error) {
                                    errorLog('Cleanup failed, proceeding anyway', error);
                                    originalCommand.apply(self, args);
                                })
                                .finally(function() { logoutInProgress = false; });
                            return false;
                        }
                        return originalCommand.apply(this, arguments);
                    };
                    debugLog('Logout interceptor registered');
                }

                // Initialize logout handler when DOM is ready
                if (document.readyState === 'loading') {
                    document.addEventListener('DOMContentLoaded', initLogoutHandler);
                } else {
                    initLogoutHandler();
                }

            })(window, document);
            </script>
            <!-- END Formbricks Integration -->
            HTML;

        return $script;
    }

    /**
     * Generate error script to display config validation errors in console
     * Called when validation fails and debug mode is enabled.
     */
    public function generateErrorScript(array $validation_errors): string
    {
        $json_validation_errors = json_encode($validation_errors, self::JSON_SAFE_FLAGS);

        $script = <<<HTML
            <!-- START Formbricks Integration (Configuration Errors) -->
            <script type="text/javascript">
            (function() {
                var validationErrors = {$json_validation_errors};

                if (window.console && console.error && validationErrors.length > 0) {
                    console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
                    console.error('Formbricks Plugin: Configuration Validation Failed');
                    console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
                    console.error('');
                    console.error('The following configuration errors must be fixed:');
                    console.error('');

                    for (var i = 0; i < validationErrors.length; i++) {
                        console.error('  ' + (i + 1) + '. ' + validationErrors[i]);
                    }

                    console.error('');
                    console.error('Location: plugins/formbricks/config.inc.php');
                    console.error('Server logs: Check Roundcube error logs for more details');
                    console.error('');
                    console.error('Fix these errors to enable Formbricks integration.');
                    console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
                }
            })();
            </script>
            <!-- END Formbricks Integration (Configuration Errors) -->
            HTML;

        return $script;
    }
}
Back to Directory File Manager