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