- Created complete documentation in docs/ directory - Added PROJECT_OVERVIEW.md with feature highlights and getting started guide - Added ARCHITECTURE.md with system design and technical details - Added SECURITY.md with comprehensive security implementation guide - Added DEVELOPMENT.md with development workflows and best practices - Added DEPLOYMENT.md with production deployment instructions - Added API.md with complete REST API documentation - Added CONTRIBUTING.md with contribution guidelines - Added CHANGELOG.md with version history and migration notes - Reorganized all documentation files into docs/ directory for better organization - Updated README.md with proper documentation links and quick navigation - Enhanced project structure with professional documentation standards
865 lines
30 KiB
PHP
865 lines
30 KiB
PHP
<?php
|
|
/*******************************************************************************************************************
|
|
| Software Name : EasyStream
|
|
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
|
| Software Author : (c) Sami Ahmed
|
|
|*******************************************************************************************************************
|
|
|
|
|
|*******************************************************************************************************************
|
|
| This source file is subject to the EasyStream Proprietary License Agreement.
|
|
|
|
|
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
|
|*******************************************************************************************************************
|
|
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
|
|*******************************************************************************************************************/
|
|
|
|
defined('_ISVALID') or header('Location: /error');
|
|
|
|
/**
|
|
* Settings Management Class
|
|
* Provides unified interface for all system settings stored in db_settings table
|
|
*/
|
|
class Settings
|
|
{
|
|
private $pdo;
|
|
private $redis;
|
|
private $cache = [];
|
|
private $cacheLoaded = false;
|
|
private $redisKey = 'easystream:settings:all';
|
|
private $redisTTL = 3600; // 1 hour cache
|
|
|
|
// Sensitive settings that should be encrypted
|
|
private $encryptedKeys = [
|
|
'paypal_secret',
|
|
'paypal_client_id',
|
|
'stripe_secret_key',
|
|
'stripe_webhook_secret',
|
|
'mail_smtp_password',
|
|
'google_client_secret',
|
|
'fb_app_secret',
|
|
'twitter_api_secret',
|
|
];
|
|
|
|
// Validation rules for settings
|
|
private $validationRules = [
|
|
// Email settings
|
|
'backend_email' => ['type' => 'email', 'required' => true, 'label' => 'Admin Email'],
|
|
'mail_smtp_username' => ['type' => 'email', 'required' => false, 'label' => 'SMTP Username'],
|
|
'paypal_email' => ['type' => 'email', 'required' => false, 'label' => 'PayPal Email'],
|
|
|
|
// URL settings
|
|
'branding_logo_url' => ['type' => 'url', 'required' => false, 'label' => 'Logo URL'],
|
|
'branding_favicon_url' => ['type' => 'url', 'required' => false, 'label' => 'Favicon URL'],
|
|
'main_url' => ['type' => 'url', 'required' => true, 'label' => 'Main URL'],
|
|
|
|
// Color settings
|
|
'branding_primary_color' => ['type' => 'color', 'pattern' => '/^#[0-9A-F]{6}$/i', 'required' => true, 'label' => 'Primary Color'],
|
|
'branding_secondary_color' => ['type' => 'color', 'pattern' => '/^#[0-9A-F]{6}$/i', 'required' => true, 'label' => 'Secondary Color'],
|
|
'token_color' => ['type' => 'color', 'pattern' => '/^#[0-9A-F]{6}$/i', 'required' => false, 'label' => 'Token Color'],
|
|
|
|
// Integer settings
|
|
'mail_smtp_port' => ['type' => 'int', 'min' => 1, 'max' => 65535, 'required' => true, 'label' => 'SMTP Port'],
|
|
'creator_payout_percentage' => ['type' => 'int', 'min' => 0, 'max' => 100, 'required' => true, 'label' => 'Payout Percentage'],
|
|
'signup_min_age' => ['type' => 'int', 'min' => 13, 'max' => 100, 'required' => true, 'label' => 'Minimum Age'],
|
|
'signup_max_age' => ['type' => 'int', 'min' => 13, 'max' => 150, 'required' => true, 'label' => 'Maximum Age'],
|
|
'signup_min_password' => ['type' => 'int', 'min' => 4, 'max' => 50, 'required' => true, 'label' => 'Min Password Length'],
|
|
'signup_max_password' => ['type' => 'int', 'min' => 8, 'max' => 100, 'required' => true, 'label' => 'Max Password Length'],
|
|
'signup_min_username' => ['type' => 'int', 'min' => 3, 'max' => 20, 'required' => true, 'label' => 'Min Username Length'],
|
|
'signup_max_username' => ['type' => 'int', 'min' => 5, 'max' => 50, 'required' => true, 'label' => 'Max Username Length'],
|
|
|
|
// Float settings
|
|
'minimum_payout_amount' => ['type' => 'float', 'min' => 0, 'required' => true, 'label' => 'Minimum Payout'],
|
|
|
|
// Boolean settings
|
|
'video_module' => ['type' => 'bool', 'label' => 'Video Module'],
|
|
'live_module' => ['type' => 'bool', 'label' => 'Live Module'],
|
|
'short_module' => ['type' => 'bool', 'label' => 'Short Module'],
|
|
'image_module' => ['type' => 'bool', 'label' => 'Image Module'],
|
|
'audio_module' => ['type' => 'bool', 'label' => 'Audio Module'],
|
|
'document_module' => ['type' => 'bool', 'label' => 'Document Module'],
|
|
'blog_module' => ['type' => 'bool', 'label' => 'Blog Module'],
|
|
'stripe_enabled' => ['type' => 'bool', 'label' => 'Stripe Enabled'],
|
|
'paypal_test' => ['type' => 'bool', 'label' => 'PayPal Test Mode'],
|
|
'creator_payout_enabled' => ['type' => 'bool', 'label' => 'Creator Payouts Enabled'],
|
|
'login_remember' => ['type' => 'bool', 'label' => 'Remember Me Feature'],
|
|
'mail_smtp_auth' => ['type' => 'bool_string', 'label' => 'SMTP Authentication'],
|
|
|
|
// String with pattern
|
|
'stripe_publishable_key' => ['type' => 'string', 'pattern' => '/^pk_(test|live)_/', 'required' => false, 'label' => 'Stripe Publishable Key'],
|
|
'stripe_secret_key' => ['type' => 'string', 'pattern' => '/^sk_(test|live)_/', 'required' => false, 'label' => 'Stripe Secret Key'],
|
|
|
|
// String with length
|
|
'website_shortname' => ['type' => 'string', 'min_length' => 1, 'max_length' => 50, 'required' => true, 'label' => 'Website Name'],
|
|
'head_title' => ['type' => 'string', 'min_length' => 1, 'max_length' => 100, 'required' => true, 'label' => 'Site Title'],
|
|
'backend_email_fromname' => ['type' => 'string', 'min_length' => 1, 'max_length' => 50, 'required' => true, 'label' => 'Email From Name'],
|
|
];
|
|
|
|
/**
|
|
* Initialize settings manager
|
|
* @param PDO $pdo Database connection
|
|
*/
|
|
public function __construct($pdo = null)
|
|
{
|
|
if ($pdo) {
|
|
$this->pdo = $pdo;
|
|
} else {
|
|
// Use global PDO if available
|
|
global $db_pdo;
|
|
$this->pdo = $db_pdo ?? null;
|
|
}
|
|
|
|
// Initialize Redis if available
|
|
try {
|
|
if (class_exists('VRedis')) {
|
|
$this->redis = VRedis::getInstance();
|
|
}
|
|
} catch (Exception $e) {
|
|
$this->redis = null;
|
|
error_log("Settings: Redis not available - " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load all settings into cache
|
|
* @return bool Success status
|
|
*/
|
|
private function loadCache()
|
|
{
|
|
if ($this->cacheLoaded) {
|
|
return true;
|
|
}
|
|
|
|
// Try Redis first
|
|
if ($this->redis) {
|
|
try {
|
|
$cached = $this->redis->get($this->redisKey);
|
|
if ($cached) {
|
|
$this->cache = json_decode($cached, true);
|
|
$this->cacheLoaded = true;
|
|
return true;
|
|
}
|
|
} catch (Exception $e) {
|
|
error_log("Settings: Redis cache read error - " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
// Fallback to database
|
|
if (!$this->pdo) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$stmt = $this->pdo->query("SELECT cfg_name, cfg_data, cfg_info FROM db_settings");
|
|
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
$this->cache[$row['cfg_name']] = [
|
|
'value' => $row['cfg_data'],
|
|
'info' => $row['cfg_info']
|
|
];
|
|
}
|
|
$this->cacheLoaded = true;
|
|
|
|
// Store in Redis for next time
|
|
if ($this->redis) {
|
|
try {
|
|
$this->redis->set(
|
|
$this->redisKey,
|
|
json_encode($this->cache),
|
|
$this->redisTTL
|
|
);
|
|
} catch (Exception $e) {
|
|
error_log("Settings: Redis cache write error - " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
} catch (PDOException $e) {
|
|
error_log("Settings cache load error: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a setting value
|
|
* @param string $key Setting name
|
|
* @param mixed $default Default value if not found
|
|
* @return mixed Setting value or default
|
|
*/
|
|
public function get($key, $default = null)
|
|
{
|
|
$this->loadCache();
|
|
|
|
if (isset($this->cache[$key])) {
|
|
$value = $this->cache[$key]['value'];
|
|
|
|
// Decrypt if needed
|
|
if ($this->shouldEncrypt($key)) {
|
|
return $this->decrypt($value);
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
return $default;
|
|
}
|
|
|
|
/**
|
|
* Get multiple settings at once
|
|
* @param array $keys Array of setting names
|
|
* @return array Associative array of settings
|
|
*/
|
|
public function getMultiple(array $keys)
|
|
{
|
|
$this->loadCache();
|
|
$results = [];
|
|
|
|
foreach ($keys as $key) {
|
|
$results[$key] = $this->get($key);
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Get all settings in a category
|
|
* @param string $prefix Category prefix (e.g., 'backend_', 'mail_', 'token_')
|
|
* @return array Associative array of settings
|
|
*/
|
|
public function getByPrefix($prefix)
|
|
{
|
|
$this->loadCache();
|
|
$results = [];
|
|
|
|
foreach ($this->cache as $key => $data) {
|
|
if (strpos($key, $prefix) === 0) {
|
|
$results[$key] = $data['value'];
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Check if a setting should be encrypted
|
|
* @param string $key Setting name
|
|
* @return bool
|
|
*/
|
|
private function shouldEncrypt($key)
|
|
{
|
|
return in_array($key, $this->encryptedKeys);
|
|
}
|
|
|
|
/**
|
|
* Get encryption key from config
|
|
* @return string
|
|
*/
|
|
private function getEncryptionKey()
|
|
{
|
|
return defined('ENC_FIRSTKEY') ? base64_decode(ENC_FIRSTKEY) : '';
|
|
}
|
|
|
|
/**
|
|
* Get IV for encryption
|
|
* @return string
|
|
*/
|
|
private function getIV()
|
|
{
|
|
return defined('ENC_SECONDKEY') ? substr(base64_decode(ENC_SECONDKEY), 0, 16) : '';
|
|
}
|
|
|
|
/**
|
|
* Encrypt a value
|
|
* @param string $value Value to encrypt
|
|
* @return string Encrypted value
|
|
*/
|
|
private function encrypt($value)
|
|
{
|
|
if (empty($value)) {
|
|
return $value;
|
|
}
|
|
|
|
try {
|
|
$encrypted = openssl_encrypt(
|
|
$value,
|
|
'AES-256-CBC',
|
|
$this->getEncryptionKey(),
|
|
0,
|
|
$this->getIV()
|
|
);
|
|
return base64_encode($encrypted);
|
|
} catch (Exception $e) {
|
|
error_log("Settings encryption error: " . $e->getMessage());
|
|
return $value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Decrypt a value
|
|
* @param string $value Value to decrypt
|
|
* @return string Decrypted value
|
|
*/
|
|
private function decrypt($value)
|
|
{
|
|
if (empty($value)) {
|
|
return $value;
|
|
}
|
|
|
|
try {
|
|
$decrypted = openssl_decrypt(
|
|
base64_decode($value),
|
|
'AES-256-CBC',
|
|
$this->getEncryptionKey(),
|
|
0,
|
|
$this->getIV()
|
|
);
|
|
return $decrypted ?: $value;
|
|
} catch (Exception $e) {
|
|
error_log("Settings decryption error: " . $e->getMessage());
|
|
return $value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate a setting value
|
|
* @param string $key Setting name
|
|
* @param mixed $value Value to validate
|
|
* @return array ['valid' => bool, 'error' => string]
|
|
*/
|
|
public function validate($key, $value)
|
|
{
|
|
// No rules = allow
|
|
if (!isset($this->validationRules[$key])) {
|
|
return ['valid' => true];
|
|
}
|
|
|
|
$rule = $this->validationRules[$key];
|
|
|
|
// Check required
|
|
if (isset($rule['required']) && $rule['required'] && ($value === '' || $value === null)) {
|
|
return [
|
|
'valid' => false,
|
|
'error' => "{$rule['label']} is required"
|
|
];
|
|
}
|
|
|
|
// Skip validation if empty and not required
|
|
if (($value === '' || $value === null) && (!isset($rule['required']) || !$rule['required'])) {
|
|
return ['valid' => true];
|
|
}
|
|
|
|
// Type-specific validation
|
|
switch ($rule['type']) {
|
|
case 'email':
|
|
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} must be a valid email address"];
|
|
}
|
|
break;
|
|
|
|
case 'url':
|
|
if (!filter_var($value, FILTER_VALIDATE_URL)) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} must be a valid URL"];
|
|
}
|
|
break;
|
|
|
|
case 'color':
|
|
if (isset($rule['pattern']) && !preg_match($rule['pattern'], $value)) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} must be a valid hex color (e.g., #FF0000)"];
|
|
}
|
|
break;
|
|
|
|
case 'int':
|
|
if (!is_numeric($value) || (int)$value != $value) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} must be a whole number"];
|
|
}
|
|
$intVal = (int)$value;
|
|
if (isset($rule['min']) && $intVal < $rule['min']) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} must be at least {$rule['min']}"];
|
|
}
|
|
if (isset($rule['max']) && $intVal > $rule['max']) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} must be at most {$rule['max']}"];
|
|
}
|
|
break;
|
|
|
|
case 'float':
|
|
if (!is_numeric($value)) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} must be a number"];
|
|
}
|
|
$floatVal = (float)$value;
|
|
if (isset($rule['min']) && $floatVal < $rule['min']) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} must be at least {$rule['min']}"];
|
|
}
|
|
if (isset($rule['max']) && $floatVal > $rule['max']) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} must be at most {$rule['max']}"];
|
|
}
|
|
break;
|
|
|
|
case 'bool':
|
|
if (!in_array($value, ['0', '1', 0, 1, true, false], true)) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} must be 0 or 1"];
|
|
}
|
|
break;
|
|
|
|
case 'bool_string':
|
|
if (!in_array($value, ['true', 'false'], true)) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} must be 'true' or 'false'"];
|
|
}
|
|
break;
|
|
|
|
case 'string':
|
|
if (isset($rule['min_length']) && strlen($value) < $rule['min_length']) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} must be at least {$rule['min_length']} characters"];
|
|
}
|
|
if (isset($rule['max_length']) && strlen($value) > $rule['max_length']) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} must be at most {$rule['max_length']} characters"];
|
|
}
|
|
if (isset($rule['pattern']) && !preg_match($rule['pattern'], $value)) {
|
|
return ['valid' => false, 'error' => "{$rule['label']} has an invalid format"];
|
|
}
|
|
break;
|
|
}
|
|
|
|
return ['valid' => true];
|
|
}
|
|
|
|
/**
|
|
* Set a setting value
|
|
* @param string $key Setting name
|
|
* @param mixed $value Setting value
|
|
* @param string $info Optional description
|
|
* @return bool Success status
|
|
*/
|
|
public function set($key, $value, $info = '')
|
|
{
|
|
if (!$this->pdo) {
|
|
return false;
|
|
}
|
|
|
|
// Validate before saving
|
|
$validation = $this->validate($key, $value);
|
|
if (!$validation['valid']) {
|
|
throw new InvalidArgumentException($validation['error']);
|
|
}
|
|
|
|
try {
|
|
// Get old value for audit trail
|
|
$oldValue = $this->get($key);
|
|
|
|
// Encrypt sensitive values before saving
|
|
$saveValue = $this->shouldEncrypt($key) ? $this->encrypt($value) : $value;
|
|
|
|
// Check if setting exists
|
|
$stmt = $this->pdo->prepare("SELECT id FROM db_settings WHERE cfg_name = ?");
|
|
$stmt->execute([$key]);
|
|
$exists = $stmt->fetch();
|
|
|
|
if ($exists) {
|
|
// Update existing setting
|
|
$stmt = $this->pdo->prepare("UPDATE db_settings SET cfg_data = ?, cfg_info = ? WHERE cfg_name = ?");
|
|
$result = $stmt->execute([$saveValue, $info ?: $this->cache[$key]['info'] ?? '', $key]);
|
|
} else {
|
|
// Insert new setting
|
|
$stmt = $this->pdo->prepare("INSERT INTO db_settings (cfg_name, cfg_data, cfg_info) VALUES (?, ?, ?)");
|
|
$result = $stmt->execute([$key, $saveValue, $info]);
|
|
}
|
|
|
|
// Update cache
|
|
$this->cache[$key] = [
|
|
'value' => $value,
|
|
'info' => $info ?: ($this->cache[$key]['info'] ?? '')
|
|
];
|
|
|
|
// Log the change to audit trail
|
|
$this->logChange($key, $oldValue, $value);
|
|
|
|
// Invalidate Redis cache
|
|
if ($this->redis) {
|
|
try {
|
|
$this->redis->delete($this->redisKey);
|
|
} catch (Exception $e) {
|
|
error_log("Settings: Redis cache invalidation error - " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
} catch (PDOException $e) {
|
|
error_log("Settings set error: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set multiple settings at once
|
|
* @param array $settings Associative array of key => value pairs
|
|
* @return bool Success status
|
|
*/
|
|
public function setMultiple(array $settings)
|
|
{
|
|
if (!$this->pdo) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$this->pdo->beginTransaction();
|
|
|
|
foreach ($settings as $key => $value) {
|
|
$this->set($key, $value);
|
|
}
|
|
|
|
$this->pdo->commit();
|
|
return true;
|
|
} catch (Exception $e) {
|
|
$this->pdo->rollBack();
|
|
error_log("Settings setMultiple error: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a setting
|
|
* @param string $key Setting name
|
|
* @return bool Success status
|
|
*/
|
|
public function delete($key)
|
|
{
|
|
if (!$this->pdo) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$stmt = $this->pdo->prepare("DELETE FROM db_settings WHERE cfg_name = ?");
|
|
$result = $stmt->execute([$key]);
|
|
|
|
unset($this->cache[$key]);
|
|
|
|
return $result;
|
|
} catch (PDOException $e) {
|
|
error_log("Settings delete error: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all settings grouped by category
|
|
* @return array Multi-dimensional array of settings by category
|
|
*/
|
|
public function getAllGrouped()
|
|
{
|
|
$this->loadCache();
|
|
|
|
$grouped = [
|
|
'general' => [],
|
|
'backend' => [],
|
|
'frontend' => [],
|
|
'mail' => [],
|
|
'payment' => [],
|
|
'token' => [],
|
|
'branding' => [],
|
|
'security' => [],
|
|
'seo' => [],
|
|
'modules' => [],
|
|
'conversion' => [],
|
|
'social' => [],
|
|
'other' => []
|
|
];
|
|
|
|
foreach ($this->cache as $key => $data) {
|
|
$category = 'other';
|
|
|
|
if (strpos($key, 'backend_') === 0) {
|
|
$category = 'backend';
|
|
} elseif (strpos($key, 'mail_') === 0) {
|
|
$category = 'mail';
|
|
} elseif (strpos($key, 'paypal_') === 0 || strpos($key, 'stripe_') === 0 || strpos($key, 'payment_') === 0) {
|
|
$category = 'payment';
|
|
} elseif (strpos($key, 'token_') === 0) {
|
|
$category = 'token';
|
|
} elseif (strpos($key, 'branding_') === 0 || strpos($key, 'color_') === 0 || strpos($key, 'logo_') === 0) {
|
|
$category = 'branding';
|
|
} elseif (strpos($key, 'signup_') === 0 || strpos($key, 'login_') === 0 || strpos($key, 'username_') === 0) {
|
|
$category = 'security';
|
|
} elseif (strpos($key, 'head_') === 0 || strpos($key, 'metaname_') === 0 || strpos($key, 'website_') === 0) {
|
|
$category = 'seo';
|
|
} elseif (strpos($key, '_module') !== false || $key === 'paid_memberships') {
|
|
$category = 'modules';
|
|
} elseif (strpos($key, 'conversion_') === 0) {
|
|
$category = 'conversion';
|
|
} elseif (strpos($key, 'fb_') === 0 || strpos($key, 'google_') === 0 || strpos($key, 'twitter_') === 0) {
|
|
$category = 'social';
|
|
} else {
|
|
$category = 'general';
|
|
}
|
|
|
|
$grouped[$category][$key] = [
|
|
'value' => $data['value'],
|
|
'info' => $data['info']
|
|
];
|
|
}
|
|
|
|
return $grouped;
|
|
}
|
|
|
|
/**
|
|
* Export all settings as JSON
|
|
* @return string JSON string
|
|
*/
|
|
public function exportJSON()
|
|
{
|
|
$this->loadCache();
|
|
return json_encode($this->cache, JSON_PRETTY_PRINT);
|
|
}
|
|
|
|
/**
|
|
* Import settings from JSON
|
|
* @param string $json JSON string
|
|
* @return bool Success status
|
|
*/
|
|
public function importJSON($json)
|
|
{
|
|
$data = json_decode($json, true);
|
|
|
|
if (!$data) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$this->pdo->beginTransaction();
|
|
|
|
foreach ($data as $key => $item) {
|
|
$this->set($key, $item['value'], $item['info'] ?? '');
|
|
}
|
|
|
|
$this->pdo->commit();
|
|
return true;
|
|
} catch (Exception $e) {
|
|
$this->pdo->rollBack();
|
|
error_log("Settings import error: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a module is enabled
|
|
* @param string $module Module name (video, live, blog, etc.)
|
|
* @return bool Enabled status
|
|
*/
|
|
public function isModuleEnabled($module)
|
|
{
|
|
return $this->get($module . '_module', '0') === '1';
|
|
}
|
|
|
|
/**
|
|
* Enable or disable a module
|
|
* @param string $module Module name
|
|
* @param bool $enabled Enable/disable
|
|
* @return bool Success status
|
|
*/
|
|
public function setModuleEnabled($module, $enabled)
|
|
{
|
|
return $this->set($module . '_module', $enabled ? '1' : '0');
|
|
}
|
|
|
|
/**
|
|
* Get site branding information
|
|
* @return array Branding settings
|
|
*/
|
|
public function getBranding()
|
|
{
|
|
return [
|
|
'site_name' => $this->get('website_shortname', 'EasyStream'),
|
|
'site_title' => $this->get('head_title', 'EasyStream'),
|
|
'primary_color' => $this->get('branding_primary_color', '#1a73e8'),
|
|
'secondary_color' => $this->get('branding_secondary_color', '#34a853'),
|
|
'logo_url' => $this->get('branding_logo_url', ''),
|
|
'favicon_url' => $this->get('branding_favicon_url', ''),
|
|
'meta_description' => $this->get('metaname_description', ''),
|
|
'meta_keywords' => $this->get('metaname_keywords', '')
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get payment gateway configuration
|
|
* @return array Payment settings
|
|
*/
|
|
public function getPaymentConfig()
|
|
{
|
|
return [
|
|
'enabled_gateways' => explode(',', $this->get('payment_methods', 'Paypal')),
|
|
'paypal' => [
|
|
'email' => $this->get('paypal_email', ''),
|
|
'test_mode' => $this->get('paypal_test', '1') === '1',
|
|
'client_id' => $this->get('paypal_client_id', ''),
|
|
'secret' => $this->get('paypal_secret', '')
|
|
],
|
|
'stripe' => [
|
|
'enabled' => $this->get('stripe_enabled', '0') === '1',
|
|
'publishable_key' => $this->get('stripe_publishable_key', ''),
|
|
'secret_key' => $this->get('stripe_secret_key', ''),
|
|
'webhook_secret' => $this->get('stripe_webhook_secret', '')
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get email/SMTP configuration
|
|
* @return array Email settings
|
|
*/
|
|
public function getEmailConfig()
|
|
{
|
|
return [
|
|
'type' => $this->get('mail_type', 'smtp'),
|
|
'from_email' => $this->get('backend_email', ''),
|
|
'from_name' => $this->get('backend_email_fromname', 'Webmaster'),
|
|
'smtp' => [
|
|
'host' => $this->get('mail_smtp_host', 'smtp.gmail.com'),
|
|
'port' => (int)$this->get('mail_smtp_port', '587'),
|
|
'username' => $this->get('mail_smtp_username', ''),
|
|
'password' => $this->get('mail_smtp_password', ''),
|
|
'auth' => $this->get('mail_smtp_auth', 'true') === 'true',
|
|
'prefix' => $this->get('mail_smtp_prefix', 'tls')
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get token economy configuration
|
|
* @return array Token settings
|
|
*/
|
|
public function getTokenConfig()
|
|
{
|
|
return [
|
|
'enabled' => $this->get('token_system_enabled', '1') === '1',
|
|
'name' => $this->get('token_name', 'Tokens'),
|
|
'symbol' => $this->get('token_symbol', 'TKN'),
|
|
'color' => $this->get('token_color', '#FFD700'),
|
|
'icon_url' => $this->get('token_icon_url', ''),
|
|
'creator_payout_enabled' => $this->get('creator_payout_enabled', '0') === '1',
|
|
'creator_payout_percentage' => (int)$this->get('creator_payout_percentage', '70'),
|
|
'minimum_payout' => (float)$this->get('minimum_payout_amount', '50.00')
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Log a settings change to audit trail
|
|
* @param string $key Setting name
|
|
* @param mixed $oldValue Old value
|
|
* @param mixed $newValue New value
|
|
* @param string $reason Change reason
|
|
* @return bool Success status
|
|
*/
|
|
private function logChange($key, $oldValue, $newValue, $reason = '')
|
|
{
|
|
if (!$this->pdo) {
|
|
return false;
|
|
}
|
|
|
|
global $usr;
|
|
|
|
try {
|
|
$stmt = $this->pdo->prepare("
|
|
INSERT INTO db_settings_history
|
|
(cfg_name, old_value, new_value, changed_by, ip_address, user_agent, change_reason)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
");
|
|
|
|
return $stmt->execute([
|
|
$key,
|
|
$oldValue,
|
|
$newValue,
|
|
$usr['id'] ?? $_SESSION['admin_user_id'] ?? 0,
|
|
$_SERVER['REMOTE_ADDR'] ?? '',
|
|
substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 255),
|
|
$reason
|
|
]);
|
|
} catch (PDOException $e) {
|
|
error_log("Settings audit log error: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get change history for a specific setting
|
|
* @param string $key Setting name
|
|
* @param int $limit Number of records to return
|
|
* @return array History records
|
|
*/
|
|
public function getHistory($key, $limit = 20)
|
|
{
|
|
if (!$this->pdo) {
|
|
return [];
|
|
}
|
|
|
|
try {
|
|
$stmt = $this->pdo->prepare("
|
|
SELECT h.*, u.usr_user as username
|
|
FROM db_settings_history h
|
|
LEFT JOIN db_accountuser u ON h.changed_by = u.usr_id
|
|
WHERE h.cfg_name = ?
|
|
ORDER BY h.changed_at DESC
|
|
LIMIT ?
|
|
");
|
|
$stmt->execute([$key, $limit]);
|
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
} catch (PDOException $e) {
|
|
error_log("Settings history error: " . $e->getMessage());
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all recent settings changes
|
|
* @param int $limit Number of records to return
|
|
* @return array History records
|
|
*/
|
|
public function getAllRecentChanges($limit = 50)
|
|
{
|
|
if (!$this->pdo) {
|
|
return [];
|
|
}
|
|
|
|
try {
|
|
$stmt = $this->pdo->prepare("
|
|
SELECT h.*, u.usr_user as username, s.cfg_info
|
|
FROM db_settings_history h
|
|
LEFT JOIN db_accountuser u ON h.changed_by = u.usr_id
|
|
LEFT JOIN db_settings s ON h.cfg_name = s.cfg_name
|
|
ORDER BY h.changed_at DESC
|
|
LIMIT ?
|
|
");
|
|
$stmt->execute([$limit]);
|
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
} catch (PDOException $e) {
|
|
error_log("Settings recent changes error: " . $e->getMessage());
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Rollback a setting to a previous value from history
|
|
* @param string $key Setting name
|
|
* @param int $historyId History record ID to rollback to
|
|
* @return bool Success status
|
|
*/
|
|
public function rollback($key, $historyId)
|
|
{
|
|
if (!$this->pdo) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
// Get the historical value
|
|
$stmt = $this->pdo->prepare("SELECT old_value FROM db_settings_history WHERE id = ? AND cfg_name = ?");
|
|
$stmt->execute([$historyId, $key]);
|
|
$history = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$history) {
|
|
return false;
|
|
}
|
|
|
|
// Set the old value back
|
|
return $this->set($key, $history['old_value'], '', 'Rollback from history #' . $historyId);
|
|
} catch (PDOException $e) {
|
|
error_log("Settings rollback error: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
}
|