feat: Add comprehensive documentation suite and reorganize project structure
- 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
This commit is contained in:
338
f_core/f_functions/functions.branding.php
Normal file
338
f_core/f_functions/functions.branding.php
Normal file
@@ -0,0 +1,338 @@
|
||||
<?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');
|
||||
|
||||
/**
|
||||
* Branding Helper Functions
|
||||
* These functions provide easy access to branding features in templates and modules
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get branding instance
|
||||
*/
|
||||
function getBranding() {
|
||||
return VBranding::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a branding setting value
|
||||
*/
|
||||
function brandingGet($key, $default = null) {
|
||||
return getBranding()->get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate site logo HTML
|
||||
*/
|
||||
function siteLogo($type = 'main', $class = '', $alt = null) {
|
||||
return getBranding()->getLogo($type, $class, $alt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate badge HTML
|
||||
*/
|
||||
function badge($type, $text = null, $class = '') {
|
||||
return getBranding()->getBadge($type, $text, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get site information array
|
||||
*/
|
||||
function siteInfo() {
|
||||
return getBranding()->getSiteInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate dynamic CSS link tag
|
||||
*/
|
||||
function dynamicCSSLink() {
|
||||
$timestamp = time(); // For cache busting
|
||||
return "<link rel=\"stylesheet\" href=\"/dynamic_theme.php?v=$timestamp\" id=\"dynamic-theme\">";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate inline CSS variables for quick access
|
||||
*/
|
||||
function inlineCSSVariables() {
|
||||
$branding = getBranding();
|
||||
|
||||
$css = "<style id=\"branding-vars\">\n:root {\n";
|
||||
$css .= " --color-primary: " . $branding->get('color_primary', '#007bff') . ";\n";
|
||||
$css .= " --color-primary-dark: " . $branding->get('color_primary_dark', '#0056b3') . ";\n";
|
||||
$css .= " --color-bg-main: " . $branding->get('color_bg_main', '#ffffff') . ";\n";
|
||||
$css .= " --color-text-primary: " . $branding->get('color_text_primary', '#212529') . ";\n";
|
||||
$css .= " --font-family-primary: " . $branding->get('font_family_primary', 'Arial, sans-serif') . ";\n";
|
||||
$css .= " --layout-max-width: " . $branding->get('layout_max_width', 1200) . "px;\n";
|
||||
$css .= "}\n</style>\n";
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate meta tags for branding
|
||||
*/
|
||||
function brandingMetaTags() {
|
||||
$siteInfo = siteInfo();
|
||||
|
||||
$meta = "<meta name=\"theme-color\" content=\"" . brandingGet('color_primary', '#007bff') . "\">\n";
|
||||
$meta .= "<meta name=\"description\" content=\"" . htmlspecialchars($siteInfo['description']) . "\">\n";
|
||||
$meta .= "<meta property=\"og:site_name\" content=\"" . htmlspecialchars($siteInfo['name']) . "\">\n";
|
||||
$meta .= "<meta property=\"og:description\" content=\"" . htmlspecialchars($siteInfo['description']) . "\">\n";
|
||||
$meta .= "<link rel=\"icon\" href=\"" . htmlspecialchars($siteInfo['favicon']) . "\">\n";
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate header HTML with branding
|
||||
*/
|
||||
function brandedHeader($includeNav = true) {
|
||||
$siteInfo = siteInfo();
|
||||
$branding = getBranding();
|
||||
|
||||
$html = "<header class=\"site-header\" style=\"background-color: " . $branding->get('color_bg_header', '#ffffff') . ";\">\n";
|
||||
$html .= " <div class=\"container\">\n";
|
||||
$html .= " <div class=\"header-content\">\n";
|
||||
$html .= " <div class=\"logo-section\">\n";
|
||||
$html .= " " . siteLogo('main', 'main-logo') . "\n";
|
||||
$html .= " <span class=\"site-name\">" . htmlspecialchars($siteInfo['name']) . "</span>\n";
|
||||
$html .= " </div>\n";
|
||||
|
||||
if ($includeNav) {
|
||||
$html .= " <nav class=\"main-nav\">\n";
|
||||
$html .= " <a href=\"/\" class=\"nav-link\">Home</a>\n";
|
||||
$html .= " <a href=\"/videos\" class=\"nav-link\">Videos</a>\n";
|
||||
$html .= " <a href=\"/live\" class=\"nav-link\">Live</a>\n";
|
||||
$html .= " <a href=\"/upload\" class=\"nav-link btn btn-primary\">Upload</a>\n";
|
||||
$html .= " </nav>\n";
|
||||
}
|
||||
|
||||
$html .= " </div>\n";
|
||||
$html .= " </div>\n";
|
||||
$html .= "</header>\n";
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate footer HTML with branding
|
||||
*/
|
||||
function brandedFooter() {
|
||||
$siteInfo = siteInfo();
|
||||
$branding = getBranding();
|
||||
|
||||
$html = "<footer class=\"site-footer\" style=\"background-color: " . $branding->get('color_bg_footer', '#343a40') . "; color: " . $branding->get('color_text_light', '#ffffff') . ";\">\n";
|
||||
$html .= " <div class=\"container\">\n";
|
||||
$html .= " <div class=\"footer-content\">\n";
|
||||
$html .= " <div class=\"footer-brand\">\n";
|
||||
$html .= " " . siteLogo('small', 'footer-logo') . "\n";
|
||||
$html .= " <p>" . htmlspecialchars($siteInfo['tagline']) . "</p>\n";
|
||||
$html .= " </div>\n";
|
||||
$html .= " <div class=\"footer-links\">\n";
|
||||
$html .= " <a href=\"/about\">About</a>\n";
|
||||
$html .= " <a href=\"/privacy\">Privacy</a>\n";
|
||||
$html .= " <a href=\"/terms\">Terms</a>\n";
|
||||
$html .= " <a href=\"/contact\">Contact</a>\n";
|
||||
$html .= " </div>\n";
|
||||
$html .= " </div>\n";
|
||||
$html .= " <div class=\"footer-bottom\">\n";
|
||||
$html .= " <p>" . htmlspecialchars($siteInfo['footer_text']) . "</p>\n";
|
||||
$html .= " </div>\n";
|
||||
$html .= " </div>\n";
|
||||
$html .= "</footer>\n";
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate video card HTML with branding
|
||||
*/
|
||||
function videoCard($video, $showBadges = true) {
|
||||
$branding = getBranding();
|
||||
|
||||
$html = "<div class=\"video-card\">\n";
|
||||
$html .= " <div class=\"video-thumbnail\">\n";
|
||||
$html .= " <img src=\"" . htmlspecialchars($video['thumbnail'] ?? brandingGet('default_thumbnail')) . "\" alt=\"" . htmlspecialchars($video['title']) . "\">\n";
|
||||
|
||||
if ($showBadges && isset($video['badges'])) {
|
||||
$html .= " <div class=\"video-badges\">\n";
|
||||
foreach ($video['badges'] as $badgeType) {
|
||||
$html .= " " . badge($badgeType) . "\n";
|
||||
}
|
||||
$html .= " </div>\n";
|
||||
}
|
||||
|
||||
$html .= " </div>\n";
|
||||
$html .= " <div class=\"video-info\">\n";
|
||||
$html .= " <h3 class=\"video-title\">" . htmlspecialchars($video['title']) . "</h3>\n";
|
||||
$html .= " <p class=\"video-author\">" . htmlspecialchars($video['author'] ?? 'Unknown') . "</p>\n";
|
||||
$html .= " <div class=\"video-stats\">\n";
|
||||
$html .= " <span class=\"views\">" . number_format($video['views'] ?? 0) . " views</span>\n";
|
||||
$html .= " <span class=\"date\">" . htmlspecialchars($video['date'] ?? '') . "</span>\n";
|
||||
$html .= " </div>\n";
|
||||
$html .= " </div>\n";
|
||||
$html .= "</div>\n";
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate user avatar with branding
|
||||
*/
|
||||
function userAvatar($user, $size = 'medium', $showBadges = true) {
|
||||
$branding = getBranding();
|
||||
$sizes = ['small' => 32, 'medium' => 48, 'large' => 64];
|
||||
$pixelSize = $sizes[$size] ?? 48;
|
||||
|
||||
$avatar = $user['avatar'] ?? brandingGet('default_avatar');
|
||||
|
||||
$html = "<div class=\"user-avatar user-avatar-$size\">\n";
|
||||
$html .= " <img src=\"" . htmlspecialchars($avatar) . "\" alt=\"" . htmlspecialchars($user['username'] ?? 'User') . "\" width=\"$pixelSize\" height=\"$pixelSize\">\n";
|
||||
|
||||
if ($showBadges && isset($user['verified']) && $user['verified']) {
|
||||
$html .= " " . badge('verified') . "\n";
|
||||
}
|
||||
|
||||
if ($showBadges && isset($user['premium']) && $user['premium']) {
|
||||
$html .= " " . badge('premium') . "\n";
|
||||
}
|
||||
|
||||
$html .= "</div>\n";
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate notification HTML with branding
|
||||
*/
|
||||
function notification($message, $type = 'info', $dismissible = true) {
|
||||
$typeClasses = [
|
||||
'success' => 'notification-success',
|
||||
'error' => 'notification-error',
|
||||
'warning' => 'notification-warning',
|
||||
'info' => 'notification-info'
|
||||
];
|
||||
|
||||
$class = $typeClasses[$type] ?? 'notification-info';
|
||||
|
||||
$html = "<div class=\"notification $class\">\n";
|
||||
$html .= " <div class=\"notification-content\">\n";
|
||||
$html .= " " . htmlspecialchars($message) . "\n";
|
||||
$html .= " </div>\n";
|
||||
|
||||
if ($dismissible) {
|
||||
$html .= " <button class=\"notification-close\" onclick=\"this.parentElement.remove()\">×</button>\n";
|
||||
}
|
||||
|
||||
$html .= "</div>\n";
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate button HTML with branding
|
||||
*/
|
||||
function button($text, $type = 'primary', $href = null, $attributes = []) {
|
||||
$tag = $href ? 'a' : 'button';
|
||||
$class = "btn btn-$type";
|
||||
|
||||
if (isset($attributes['class'])) {
|
||||
$class .= ' ' . $attributes['class'];
|
||||
unset($attributes['class']);
|
||||
}
|
||||
|
||||
$attrs = '';
|
||||
foreach ($attributes as $key => $value) {
|
||||
$attrs .= " $key=\"" . htmlspecialchars($value) . "\"";
|
||||
}
|
||||
|
||||
if ($href) {
|
||||
$attrs .= " href=\"" . htmlspecialchars($href) . "\"";
|
||||
}
|
||||
|
||||
return "<$tag class=\"$class\"$attrs>" . htmlspecialchars($text) . "</$tag>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate form input with branding
|
||||
*/
|
||||
function formInput($name, $type = 'text', $value = '', $attributes = []) {
|
||||
$class = 'form-input';
|
||||
|
||||
if (isset($attributes['class'])) {
|
||||
$class .= ' ' . $attributes['class'];
|
||||
unset($attributes['class']);
|
||||
}
|
||||
|
||||
$attrs = "class=\"$class\" name=\"" . htmlspecialchars($name) . "\" type=\"$type\"";
|
||||
|
||||
if ($value) {
|
||||
$attrs .= " value=\"" . htmlspecialchars($value) . "\"";
|
||||
}
|
||||
|
||||
foreach ($attributes as $key => $val) {
|
||||
$attrs .= " $key=\"" . htmlspecialchars($val) . "\"";
|
||||
}
|
||||
|
||||
return "<input $attrs>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if dark mode is enabled
|
||||
*/
|
||||
function isDarkMode() {
|
||||
return brandingGet('enable_dark_mode', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current theme preset name
|
||||
*/
|
||||
function getCurrentTheme() {
|
||||
// This could be enhanced to track the current active theme
|
||||
return brandingGet('current_theme', 'Default Blue');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate theme switcher HTML
|
||||
*/
|
||||
function themeSwitcher() {
|
||||
if (!isDarkMode()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$html = "<div class=\"theme-switcher\">\n";
|
||||
$html .= " <button class=\"theme-toggle\" onclick=\"toggleTheme()\" title=\"Toggle Dark Mode\">\n";
|
||||
$html .= " <span class=\"theme-icon\">🌙</span>\n";
|
||||
$html .= " </button>\n";
|
||||
$html .= "</div>\n";
|
||||
|
||||
$html .= "<script>\n";
|
||||
$html .= "function toggleTheme() {\n";
|
||||
$html .= " document.body.classList.toggle('dark-theme');\n";
|
||||
$html .= " const icon = document.querySelector('.theme-icon');\n";
|
||||
$html .= " icon.textContent = document.body.classList.contains('dark-theme') ? '☀️' : '🌙';\n";
|
||||
$html .= " localStorage.setItem('theme', document.body.classList.contains('dark-theme') ? 'dark' : 'light');\n";
|
||||
$html .= "}\n";
|
||||
$html .= "// Load saved theme\n";
|
||||
$html .= "if (localStorage.getItem('theme') === 'dark') {\n";
|
||||
$html .= " document.body.classList.add('dark-theme');\n";
|
||||
$html .= " document.querySelector('.theme-icon').textContent = '☀️';\n";
|
||||
$html .= "}\n";
|
||||
$html .= "</script>\n";
|
||||
|
||||
return $html;
|
||||
}
|
||||
?>
|
||||
34
f_core/f_functions/functions.core.php
Normal file
34
f_core/f_functions/functions.core.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
// Core utility functions
|
||||
if (!defined("_VALID_ACCESS")) { exit("Direct access not allowed"); }
|
||||
|
||||
function sanitize_input($input) {
|
||||
if (is_array($input)) {
|
||||
return array_map("sanitize_input", $input);
|
||||
}
|
||||
return htmlspecialchars(trim($input), ENT_QUOTES, "UTF-8");
|
||||
}
|
||||
|
||||
function redirect($url, $code = 302) {
|
||||
header("Location: " . $url, true, $code);
|
||||
exit();
|
||||
}
|
||||
|
||||
function json_response($data, $code = 200) {
|
||||
http_response_code($code);
|
||||
header("Content-Type: application/json");
|
||||
echo json_encode($data);
|
||||
exit();
|
||||
}
|
||||
|
||||
function error_response($message, $code = 400) {
|
||||
json_response(["error" => $message], $code);
|
||||
}
|
||||
|
||||
function success_response($data = null, $message = "Success") {
|
||||
$response = ["success" => true, "message" => $message];
|
||||
if ($data !== null) {
|
||||
$response["data"] = $data;
|
||||
}
|
||||
json_response($response);
|
||||
}
|
||||
40
f_core/f_functions/functions.database.php
Normal file
40
f_core/f_functions/functions.database.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
// Database utility functions
|
||||
if (!defined("_VALID_ACCESS")) { exit("Direct access not allowed"); }
|
||||
|
||||
function get_db_connection() {
|
||||
global $db;
|
||||
if ($db === null) {
|
||||
try {
|
||||
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4";
|
||||
$db = new PDO($dsn, DB_USER, DB_PASS, [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false
|
||||
]);
|
||||
} catch (PDOException $e) {
|
||||
error_log("Database connection failed: " . $e->getMessage());
|
||||
throw new Exception("Database connection failed");
|
||||
}
|
||||
}
|
||||
return $db;
|
||||
}
|
||||
|
||||
function db_query($sql, $params = []) {
|
||||
$db = get_db_connection();
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
function db_fetch($sql, $params = []) {
|
||||
return db_query($sql, $params)->fetch();
|
||||
}
|
||||
|
||||
function db_fetch_all($sql, $params = []) {
|
||||
return db_query($sql, $params)->fetchAll();
|
||||
}
|
||||
|
||||
function db_execute($sql, $params = []) {
|
||||
return db_query($sql, $params)->rowCount();
|
||||
}
|
||||
364
f_core/f_functions/functions.general.php
Normal file
364
f_core/f_functions/functions.general.php
Normal file
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
defined('_ISVALID') or header('Location: /error');
|
||||
|
||||
use Akismet\Akismet;
|
||||
use Akismet\Comment;
|
||||
use GeoIp2\Database\Reader;
|
||||
use IPQualityScore\IPQualityScore;
|
||||
|
||||
function insert_swiperJS($p)
|
||||
{return VGenerate::swiperjs($p["for"]);}
|
||||
function insert_socialMediaLinks()
|
||||
{return VGenerate::socialMediaLinks();}
|
||||
function insert_themeSwitch()
|
||||
{return VGenerate::themeSwitch();}
|
||||
function insert_loadcssplugins()
|
||||
{return VGenerate::cssplugins();}
|
||||
function insert_loadbecssplugins()
|
||||
{return VGenerate::becssplugins();}
|
||||
function insert_loadjsplugins()
|
||||
{return VGenerate::jsplugins();}
|
||||
function insert_loadbejsplugins()
|
||||
{return VGenerate::bejsplugins();}
|
||||
function insert_getSubCount()
|
||||
{return VHome::getSubCount();}
|
||||
function insert_getFollowCount()
|
||||
{return VHome::getSubCount(1);}
|
||||
function insert_getCurrentSection()
|
||||
{return VHref::currentSection();}
|
||||
function insert_getSearchSection()
|
||||
{return VSearch::getSearchSection();}
|
||||
function insert_newMessages()
|
||||
{return VMessages::getnew_nr();}
|
||||
function insert_viewFileColumn()
|
||||
{return VView::sideColumn();}
|
||||
function insert_bePlayerAdCounts($p)
|
||||
{return VbeAdvertising::playerAdCount($p["for"]);}
|
||||
function insert_H2span($t)
|
||||
{return VGenerate::H2span($t["for"], $t["f"]);}
|
||||
function insert_beUserCount($p)
|
||||
{return VbeMembers::userCount($p["for"]);}
|
||||
function insert_beFileCount($p)
|
||||
{return VbeFiles::fileCount($p["for"]);}
|
||||
function insert_beSectionList($p)
|
||||
{return VbeSettings::sectionList($p["for"]);}
|
||||
function insert_beAdvCount_banner($p)
|
||||
{return VbeAdvertising::advCount_banner($p["for"]);}
|
||||
function insert_beAdvCount_grp($p)
|
||||
{return VbeAdvertising::advCount_group($p["for"]);}
|
||||
function insert_beMenuToggle()
|
||||
{return VGenerate::menuToggle();}
|
||||
function insert_beMenuEntries()
|
||||
{return VGenerate::menuEntries();}
|
||||
function insert_advHTML($id)
|
||||
{return VGenerate::advHTML($id["id"]);}
|
||||
function insert_getVJSJS($p)
|
||||
{return VPlayers::VJSJS('view', $p["usr_key"], $p["file_key"], $p["file_hd"], $p["next"], $p["pl_key"]);}
|
||||
function insert_getJWJS($p)
|
||||
{return VPlayers::JWJS('view', $p["usr_key"], $p["file_key"], $p["file_hd"], $p["next"], $p["pl_key"]);}
|
||||
function insert_getFPJS($p)
|
||||
{return VPlayers::FPJS('view', $p["usr_key"], $p["file_key"], $p["file_hd"], $p["next"], $p["pl_key"]);}
|
||||
function insert_getDOCJS($p)
|
||||
{return VPlayers::DOCJS('view', $p["usr_key"], $p["file_key"], $p["file_hd"], $p["next"], $p["pl_key"]);}
|
||||
function insert_getFLEXJS($p)
|
||||
{return VPlayers::FLEXJS('view', $p["usr_key"], $p["file_key"], $p["file_hd"], $p["next"], $p["pl_key"]);}
|
||||
function insert_getFREEJS($p)
|
||||
{return VPlayers::FREEJS('view', $p["usr_key"], $p["file_key"], $p["file_hd"], $p["next"], $p["pl_key"]);}
|
||||
function insert_getImageJS($p)
|
||||
{return VPlayers::imageJS('view', $p["pl_key"]);}
|
||||
function insert_getPageMeta($for)
|
||||
{return VHref::getPageMeta($for["for"]);}
|
||||
function insert_categoryMenu()
|
||||
{$a = new VBrowse;return VBrowse::categoryMenu();}
|
||||
function insert_channelsMenu()
|
||||
{$a = new VChannels;return VChannels::categoryMenu();}
|
||||
function insert_promotedChannelsMenu()
|
||||
{$c = new VChannels;return VChannels::promotedMenu();}
|
||||
function insert_uploadResponse()
|
||||
{return VResponses::uploadResponse();}
|
||||
function insert_getUserStats($type)
|
||||
{return VUseraccount::getUserStats($type["type"]);}
|
||||
function insert_subsConfig()
|
||||
{return VFiles::subsConfig();}
|
||||
function insert_fileListSelect($for)
|
||||
{return VMessages::fileListSelect($for["for"]);}
|
||||
function insert_updateSubsCount()
|
||||
{return VFiles::userSubs(1);}
|
||||
function insert_updateOsubCount()
|
||||
{return VFiles::userSubs(2);}
|
||||
function insert_getUserSubs()
|
||||
{$a = new VFiles;return VFiles::userSubs();}
|
||||
function insert_getPlaylistEditID($key)
|
||||
{return VFiles::userPlaylistEditID($key["key"], $key["get"]);}
|
||||
function insert_getUserPlaylists($for)
|
||||
{return VFiles::userPlaylists($for["for"]);}
|
||||
function insert_getChannelTabs()
|
||||
{return VUserpage::channelTabs();}
|
||||
function insert_getChannelLayout()
|
||||
{return VUserpage::channelLayout();}
|
||||
function insert_getCustomStyles()
|
||||
{return VUserpage::customStyles();}
|
||||
function insert_userLabelCheckboxes()
|
||||
{return VContacts::labelCheckboxes();}
|
||||
function insert_sectionLabel($for)
|
||||
{return VMessages::sectionLabel($for["for"]);}
|
||||
function insert_addToLabel($for)
|
||||
{return VMessages::addToLabel($for["for"]);}
|
||||
function insert_getLabelName()
|
||||
{return VMessages::getLabelName();}
|
||||
function insert_fileCount($for)
|
||||
{return VFiles::fileCount($for["for"]);}
|
||||
function insert_msgCount($for)
|
||||
{return VMessages::messageCount($for["for"]);}
|
||||
function insert_getMessageSubject($msg_id)
|
||||
{return VMessages::getMessageInfo('msg_subj', $msg_id["msg_id"]);}
|
||||
function insert_beFileCategories($type)
|
||||
{return VbeFiles::fileCategories($type["type"]);}
|
||||
function insert_footerInit()
|
||||
{return VGenerate::footerInit();}
|
||||
function insert_langInit()
|
||||
{return VGenerate::langInit();}
|
||||
function insert_langInit_be()
|
||||
{return VGenerate::langInit_be();}
|
||||
function insert_footerText($p)
|
||||
{return VGenerate::footerText($p["ct"]);}
|
||||
function insert_getUserNameKey($key)
|
||||
{
|
||||
global $class_database, $class_filter;
|
||||
$k = strlen($key["key"]) == 11 ? substr($key["key"], 1) : $key["key"];
|
||||
return $class_database->singleFieldValue('db_accountuser', 'usr_user', 'usr_key', $class_filter->clr_str($k));
|
||||
}
|
||||
function insert_phpInfo()
|
||||
{
|
||||
ob_start();
|
||||
phpinfo();
|
||||
$i = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
return $i;
|
||||
}
|
||||
function insert_currentMenuEntry($for)
|
||||
{
|
||||
return VMessages::currentMenuEntry($for["for"]);
|
||||
}
|
||||
function insert_getMessageDate($msg_id)
|
||||
{
|
||||
global $class_database;
|
||||
return $for = $_GET["f"] == 'comm' ? $class_database->singleFieldValue('db_channelcomments', 'c_datetime', 'c_id', $msg_id["msg_id"]) : VMessages::getMessageInfo('msg_date', $msg_id["msg_id"]);
|
||||
}
|
||||
function insert_getMessageText($msg_id)
|
||||
{
|
||||
global $class_database;
|
||||
return '<pre>' . ($_GET["f"] == 'comm' ? $class_database->singleFieldValue('db_channelcomments', 'c_body', 'c_id', $msg_id["msg_id"]) : VMessages::getMessageInfo('msg_body', $msg_id["msg_id"])) . '</pre>';
|
||||
}
|
||||
function insert_getUsername($user_id)
|
||||
{
|
||||
$user_details = VUserinfo::getUserInfo($user_id["user_id"]);
|
||||
return $user_details["uname"];
|
||||
}
|
||||
function insert_getProfileImage($for)
|
||||
{
|
||||
$_for = $for["for"] != '' ? $for["for"] : '';
|
||||
return VUseraccount::getProfileImage($_for);
|
||||
}
|
||||
function insert_phpInfoText()
|
||||
{
|
||||
global $language;
|
||||
|
||||
return '<span class="">' . $language["backend.menu.entry1.sub7.active"] . '</span> <b>post_max_size ' . ini_get("post_max_size") . '</b> <span class="">' . $language["frontend.global.and"] . '</span> <b>upload_max_filesize ' . ini_get("upload_max_filesize") . '</b>';
|
||||
}
|
||||
function insert_generateCountryList()
|
||||
{
|
||||
global $cfg, $language;
|
||||
include_once 'f_core/config.countries.php';
|
||||
|
||||
$i = 0;
|
||||
$disabled = $_SESSION["signup_location"] != '' ? 'disabled="disabled"' : null;
|
||||
$disabled = $cfg['global_signup'] == 0 ? 'disabled="disabled"' : $disabled;
|
||||
$disabled = ($cfg["signup_ip_access"] == 1 and !VIPaccess::checkIPlist($cfg["list_ip_signup"])) ? 'disabled="disabled"' : $disabled;
|
||||
$select = '<select ' . $disabled . ' name="frontend_signup_location_sel" class="signup-select" onChange="$(\'#input-loc\').val(\'' . $language["frontend.signup.location"] . ': \'+this.value);">';
|
||||
|
||||
foreach ($_countries as $value) {
|
||||
$selected = $_SESSION["signup_location"] == $value ? ' selected="selected"' : ($_POST["frontend_signup_location"] == $value ? ' selected="selected"' : null);
|
||||
$selected = $i == 0 ? ' disabled="disabled" selected="selected"' : $selected;
|
||||
$option .= '<option value="' . $value . '"' . $selected . '>' . $value . '</option>';
|
||||
$i += 1;
|
||||
}
|
||||
$select .= $option . '</select>';
|
||||
return $select;
|
||||
}
|
||||
function insert_getListContent($from)
|
||||
{
|
||||
global $class_database, $smarty;
|
||||
$cfg = $class_database->getConfigurations('list_signup_terms,list_ip_signup,list_email_domains,list_reserved_users,list_ip_access,list_ip_backend');
|
||||
|
||||
switch ($from["from"]) {
|
||||
case 'terms':$url = $cfg["list_signup_terms"];
|
||||
break;
|
||||
case 'ip-backend':$url = $cfg["list_ip_backend"];
|
||||
break;
|
||||
case 'ip-access':$url = $cfg["list_ip_access"];
|
||||
break;
|
||||
case 'ip-signup':$url = $cfg["list_ip_signup"];
|
||||
break;
|
||||
case 'email-domains':$url = $cfg["list_email_domains"];
|
||||
break;
|
||||
case 'usernames':$url = $cfg["list_reserved_users"];
|
||||
break;
|
||||
}
|
||||
return $smarty->fetch($url);
|
||||
}
|
||||
function insert_arrayFromString($opt)
|
||||
{
|
||||
global $cfg, $language;
|
||||
include_once 'f_data/data_languages/' . $_SESSION["fe_lang"] . '/lang_frontend/' . $opt["from"] . '.php';
|
||||
|
||||
$array = explode(',', $language[$opt["entry"]]);
|
||||
return $array;
|
||||
}
|
||||
function insert_sizeFormat($size)
|
||||
{
|
||||
global $cfg, $language;
|
||||
|
||||
$dlm = $cfg["numeric_delimiter"];
|
||||
$size["size"] = $size["size"] * (1024 * 1024);
|
||||
|
||||
if ($size["size"] < 1024) {return number_format($size["size"], 0, $dlm, $dlm) . $language["frontend.sizeformat.bytes"];} elseif ($size["size"] < (1024 * 1024)) {$size2 = round($size["size"] / 1024, 1);return number_format(($size["size"] / 1024), 0, $dlm, $dlm) . $language["frontend.sizeformat.kb"];} elseif ($size["size"] < (1024 * 1024 * 1024)) {return number_format(($size["size"] / (1024 * 1024)), 0, $dlm, $dlm) . $language["frontend.sizeformat.mb"];} else { $size2 = round($size["size"] / (1024 * 1024 * 1024), 1);return number_format(($size["size"] / (1024 * 1024 * 1024)), 0, $dlm, $dlm) . $language["frontend.sizeformat.gb"];}
|
||||
}
|
||||
|
||||
function secured_encrypt($data)
|
||||
{
|
||||
$method = "aes-256-cbc";
|
||||
$first_key = base64_decode(ENC_FIRSTKEY);
|
||||
$second_key = base64_decode(ENC_SECONDKEY);
|
||||
|
||||
$iv_length = openssl_cipher_iv_length($method);
|
||||
$iv = openssl_random_pseudo_bytes($iv_length);
|
||||
$first_encrypted = openssl_encrypt($data, $method, $first_key, OPENSSL_RAW_DATA, $iv);
|
||||
$second_encrypted = hash_hmac('sha3-512', $first_encrypted, $second_key, true);
|
||||
$output = base64_encode($iv . $second_encrypted . $first_encrypted);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
function secured_decrypt($input)
|
||||
{
|
||||
$method = "aes-256-cbc";
|
||||
$first_key = base64_decode(ENC_FIRSTKEY);
|
||||
$second_key = base64_decode(ENC_SECONDKEY);
|
||||
$mix = base64_decode($input);
|
||||
|
||||
$iv_length = openssl_cipher_iv_length($method);
|
||||
$iv = substr($mix, 0, $iv_length);
|
||||
$second_encrypted = substr($mix, $iv_length, 64);
|
||||
$first_encrypted = substr($mix, $iv_length + 64);
|
||||
$data = openssl_decrypt($first_encrypted, $method, $first_key, OPENSSL_RAW_DATA, $iv);
|
||||
$second_encrypted_new = hash_hmac('sha3-512', $first_encrypted, $second_key, true);
|
||||
|
||||
if ($second_encrypted and hash_equals($second_encrypted, $second_encrypted_new)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function check_ip_quality_score($ip)
|
||||
{
|
||||
global $cfg;
|
||||
|
||||
include 'f_core/f_classes/class_ipquality/vendor/autoload.php';
|
||||
|
||||
$key = $cfg['ipqualityscore_api'];
|
||||
|
||||
try {
|
||||
$qualityScore = new IPQualityScore($key);
|
||||
$result = $qualityScore->IPAddressVerification
|
||||
->setUserLanguage($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '')
|
||||
->setUserAgent($_SERVER['HTTP_USER_AGENT'] ?? '')
|
||||
->getResponse($ip);
|
||||
|
||||
return $result;
|
||||
} catch (Exception $e) {
|
||||
//echo 'Caught exception: ', $e->getMessage(), "\n";
|
||||
}
|
||||
}
|
||||
|
||||
function akismet_spam_check($c)
|
||||
{
|
||||
global $cfg;
|
||||
|
||||
include 'f_core/f_classes/class_akismet/vendor/autoload.php';
|
||||
|
||||
$key = $cfg['akismet_api'];
|
||||
$site = $cfg['akismet_site'];
|
||||
|
||||
try {
|
||||
$akismet = new Akismet($key, $site);
|
||||
$comment = new Comment();
|
||||
$comment->setContent($c);
|
||||
$comment->includeServerVariables();
|
||||
$result = $akismet->check($comment);
|
||||
|
||||
if ($result->isSpam) {
|
||||
return true;
|
||||
}
|
||||
if ($result->isDiscardable) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
//echo 'Caught exception: ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function maxmind_country()
|
||||
{
|
||||
global $cfg;
|
||||
|
||||
include 'f_core/f_classes/class_maxmind/vendor/autoload.php';
|
||||
|
||||
try {
|
||||
$reader = new Reader($cfg['maxmind_db']);
|
||||
$record = $reader->country($_SERVER[REM_ADDR]);
|
||||
|
||||
return $record->country->isoCode;
|
||||
} catch (Exception $e) {
|
||||
//echo 'Caught exception: ', $e->getMessage(), "\n";
|
||||
}
|
||||
}
|
||||
|
||||
function proxy_check($address)
|
||||
{
|
||||
global $cfg;
|
||||
|
||||
include 'f_core/f_classes/class_proxycheck/vendor/autoload.php';
|
||||
|
||||
try {
|
||||
$proxycheck_options = array(
|
||||
'API_KEY' => $cfg['proxycheck_api'], // Your API Key.
|
||||
'ASN_DATA' => 1, // Enable ASN data response.
|
||||
'DAY_RESTRICTOR' => 7, // Restrict checking to proxies seen in the past # of days.
|
||||
'VPN_DETECTION' => 1, // Check for both VPN's and Proxies instead of just Proxies.
|
||||
'RISK_DATA' => 1, // 0 = Off, 1 = Risk Score (0-100), 2 = Risk Score & Attack History.
|
||||
'INF_ENGINE' => 0, // Enable or disable the real-time inference engine.
|
||||
'TLS_SECURITY' => 0, // Enable or disable transport security (TLS).
|
||||
'QUERY_TAGGING' => 1, // Enable or disable query tagging.
|
||||
'MASK_ADDRESS' => 1, // Anonymises the local-part of an email address (e.g. anonymous@domain.tld)
|
||||
'CUSTOM_TAG' => '', // Specify a custom query tag instead of the default (Domain+Page).
|
||||
'BLOCKED_COUNTRIES' => array(), // Specify an array of countries or isocodes to be blocked.
|
||||
'ALLOWED_COUNTRIES' => array(), // Specify an array of countries or isocodes to be allowed.
|
||||
);
|
||||
|
||||
$result_array = \proxycheck\proxycheck::check($address, $proxycheck_options);
|
||||
|
||||
if (is_array($result_array) and ($result_array[$address]["proxy"] == 'yes' or $result_array["block"] == 'yes')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
//echo 'Caught exception: ', $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
69
f_core/f_functions/functions.queue.php
Normal file
69
f_core/f_functions/functions.queue.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
defined('_ISVALID') or header('Location: /error');
|
||||
|
||||
/**
|
||||
* Lightweight queue helpers with Redis fallback to filesystem.
|
||||
*/
|
||||
|
||||
function queue_redis_client()
|
||||
{
|
||||
static $redis = null;
|
||||
if ($redis !== null) return $redis;
|
||||
|
||||
$host = getenv('REDIS_HOST') ?: ($GLOBALS['cfg']['redis_host'] ?? null);
|
||||
$port = (int) (getenv('REDIS_PORT') ?: ($GLOBALS['cfg']['redis_port'] ?? 6379));
|
||||
$db = (int) (getenv('REDIS_DB') ?: ($GLOBALS['cfg']['redis_db'] ?? 0));
|
||||
|
||||
if (!$host) return $redis = false;
|
||||
|
||||
if (!class_exists('Redis')) return $redis = false;
|
||||
|
||||
try {
|
||||
$r = new Redis();
|
||||
if (!$r->connect($host, $port, 2.0)) return $redis = false;
|
||||
if ($db) $r->select($db);
|
||||
return $redis = $r;
|
||||
} catch (Exception $e) {
|
||||
return $redis = false;
|
||||
}
|
||||
}
|
||||
|
||||
function queue_enqueue($queue, array $payload)
|
||||
{
|
||||
$data = json_encode(['t' => time(), 'payload' => $payload]);
|
||||
if ($r = queue_redis_client()) {
|
||||
return (bool) $r->lPush("q:$queue", $data);
|
||||
}
|
||||
// FS fallback
|
||||
$dir = 'f_data/queues/' . preg_replace('/[^a-z0-9:_-]/i', '_', $queue);
|
||||
if (!is_dir($dir)) @mkdir($dir, 0755, true);
|
||||
$fn = $dir . '/' . microtime(true) . '-' . bin2hex(random_bytes(4)) . '.json';
|
||||
return (bool) @file_put_contents($fn, $data);
|
||||
}
|
||||
|
||||
function queue_dequeue($queue, $timeout = 2)
|
||||
{
|
||||
if ($r = queue_redis_client()) {
|
||||
$res = $r->brPop(["q:$queue"], $timeout);
|
||||
if (is_array($res) && count($res) === 2) {
|
||||
return json_decode($res[1], true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// FS fallback (non-blocking, best effort)
|
||||
$dir = 'f_data/queues/' . preg_replace('/[^a-z0-9:_-]/i', '_', $queue);
|
||||
if (!is_dir($dir)) return null;
|
||||
$files = glob($dir . '/*.json');
|
||||
if (!$files) return null;
|
||||
sort($files);
|
||||
$fn = $files[0];
|
||||
$raw = @file_get_contents($fn);
|
||||
@unlink($fn);
|
||||
return $raw ? json_decode($raw, true) : null;
|
||||
}
|
||||
|
||||
function queue_available()
|
||||
{
|
||||
return (bool) queue_redis_client();
|
||||
}
|
||||
|
||||
344
f_core/f_functions/functions.rbac.php
Normal file
344
f_core/f_functions/functions.rbac.php
Normal file
@@ -0,0 +1,344 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| RBAC Helper Functions
|
||||
| Convenience functions for role-based access control
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
defined('_ISVALID') or header('Location: /error');
|
||||
|
||||
/**
|
||||
* Check if current user has permission
|
||||
* @param string $permission Permission name
|
||||
* @param array $context Additional context
|
||||
* @return bool True if user has permission
|
||||
*/
|
||||
function has_permission($permission, $context = [])
|
||||
{
|
||||
$rbac = VRBAC::getInstance();
|
||||
return $rbac->hasPermission('current', $permission, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has permission
|
||||
* @param int $userId User ID
|
||||
* @param string $permission Permission name
|
||||
* @param array $context Additional context
|
||||
* @return bool True if user has permission
|
||||
*/
|
||||
function user_has_permission($userId, $permission, $context = [])
|
||||
{
|
||||
$rbac = VRBAC::getInstance();
|
||||
return $rbac->hasPermission($userId, $permission, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Require permission or redirect/exit
|
||||
* @param string $permission Permission name
|
||||
* @param array $context Additional context
|
||||
* @param string $redirectUrl Redirect URL on failure
|
||||
* @param string $errorMessage Error message
|
||||
*/
|
||||
function require_permission($permission, $context = [], $redirectUrl = '/error', $errorMessage = 'Access denied')
|
||||
{
|
||||
if (!has_permission($permission, $context)) {
|
||||
if ($redirectUrl) {
|
||||
header("Location: {$redirectUrl}");
|
||||
exit;
|
||||
} else {
|
||||
http_response_code(403);
|
||||
die($errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current user roles
|
||||
* @return array User roles
|
||||
*/
|
||||
function get_user_roles($userId = null)
|
||||
{
|
||||
$rbac = VRBAC::getInstance();
|
||||
$userId = $userId ?: ($_SESSION['USER_ID'] ?? null);
|
||||
|
||||
if (!$userId) {
|
||||
return [VRBAC::ROLE_GUEST];
|
||||
}
|
||||
|
||||
return $rbac->getUserRoles($userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has role
|
||||
* @param string $role Role name
|
||||
* @param int $userId User ID (optional, defaults to current user)
|
||||
* @return bool True if user has role
|
||||
*/
|
||||
function user_has_role($role, $userId = null)
|
||||
{
|
||||
$userRoles = get_user_roles($userId);
|
||||
return in_array($role, $userRoles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current user is admin
|
||||
* @return bool True if user is admin
|
||||
*/
|
||||
function is_admin()
|
||||
{
|
||||
return user_has_role(VRBAC::ROLE_ADMIN) || user_has_role(VRBAC::ROLE_SUPER_ADMIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current user is moderator or higher
|
||||
* @return bool True if user is moderator or higher
|
||||
*/
|
||||
function is_moderator()
|
||||
{
|
||||
return user_has_role(VRBAC::ROLE_MODERATOR) || is_admin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current user is verified
|
||||
* @return bool True if user is verified
|
||||
*/
|
||||
function is_verified()
|
||||
{
|
||||
$roles = get_user_roles();
|
||||
return !in_array(VRBAC::ROLE_GUEST, $roles) && !empty(array_intersect($roles, [
|
||||
VRBAC::ROLE_VERIFIED, VRBAC::ROLE_CREATOR, VRBAC::ROLE_MODERATOR,
|
||||
VRBAC::ROLE_ADMIN, VRBAC::ROLE_SUPER_ADMIN
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign role to user
|
||||
* @param int $userId User ID
|
||||
* @param string $role Role name
|
||||
* @param string $reason Reason for assignment
|
||||
* @return bool Success status
|
||||
*/
|
||||
function assign_user_role($userId, $role, $reason = '')
|
||||
{
|
||||
$rbac = VRBAC::getInstance();
|
||||
$assignedBy = $_SESSION['USER_ID'] ?? null;
|
||||
|
||||
return $rbac->assignRole($userId, $role, $assignedBy, $reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove role from user
|
||||
* @param int $userId User ID
|
||||
* @param string $role Role name
|
||||
* @param string $reason Reason for removal
|
||||
* @return bool Success status
|
||||
*/
|
||||
function remove_user_role($userId, $role, $reason = '')
|
||||
{
|
||||
$rbac = VRBAC::getInstance();
|
||||
$removedBy = $_SESSION['USER_ID'] ?? null;
|
||||
|
||||
return $rbac->removeRole($userId, $role, $removedBy, $reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get permission-based navigation menu
|
||||
* @return array Navigation menu items
|
||||
*/
|
||||
function get_permission_menu()
|
||||
{
|
||||
$menu = [];
|
||||
|
||||
// Public menu items
|
||||
$menu[] = ['title' => 'Home', 'url' => '/', 'permission' => 'content.view'];
|
||||
$menu[] = ['title' => 'Browse', 'url' => '/videos', 'permission' => 'content.view'];
|
||||
$menu[] = ['title' => 'Search', 'url' => '/search', 'permission' => 'content.search'];
|
||||
|
||||
// Member menu items
|
||||
if (has_permission('user.upload.basic')) {
|
||||
$menu[] = ['title' => 'Upload', 'url' => '/upload', 'permission' => 'user.upload.basic'];
|
||||
}
|
||||
|
||||
// Creator menu items
|
||||
if (has_permission('live.stream.basic')) {
|
||||
$menu[] = ['title' => 'Go Live', 'url' => '/live', 'permission' => 'live.stream.basic'];
|
||||
}
|
||||
|
||||
if (has_permission('analytics.view')) {
|
||||
$menu[] = ['title' => 'Analytics', 'url' => '/analytics', 'permission' => 'analytics.view'];
|
||||
}
|
||||
|
||||
// Admin menu items
|
||||
if (has_permission('admin.dashboard')) {
|
||||
$menu[] = ['title' => 'Admin', 'url' => '/admin', 'permission' => 'admin.dashboard'];
|
||||
}
|
||||
|
||||
// Filter menu items based on permissions
|
||||
return array_filter($menu, function($item) {
|
||||
return has_permission($item['permission']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user role display name
|
||||
* @param string $role Role name
|
||||
* @return string Display name
|
||||
*/
|
||||
function get_role_display_name($role)
|
||||
{
|
||||
$displayNames = [
|
||||
VRBAC::ROLE_GUEST => 'Guest',
|
||||
VRBAC::ROLE_MEMBER => 'Member',
|
||||
VRBAC::ROLE_VERIFIED => 'Verified User',
|
||||
VRBAC::ROLE_CREATOR => 'Content Creator',
|
||||
VRBAC::ROLE_MODERATOR => 'Moderator',
|
||||
VRBAC::ROLE_ADMIN => 'Administrator',
|
||||
VRBAC::ROLE_SUPER_ADMIN => 'Super Administrator'
|
||||
];
|
||||
|
||||
return $displayNames[$role] ?? ucfirst(str_replace('_', ' ', $role));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available roles for assignment
|
||||
* @return array Available roles
|
||||
*/
|
||||
function get_assignable_roles()
|
||||
{
|
||||
$roles = [
|
||||
VRBAC::ROLE_MEMBER => 'Member',
|
||||
VRBAC::ROLE_VERIFIED => 'Verified User',
|
||||
VRBAC::ROLE_CREATOR => 'Content Creator'
|
||||
];
|
||||
|
||||
// Add moderator and admin roles if user has permission
|
||||
if (has_permission('admin.users.manage')) {
|
||||
$roles[VRBAC::ROLE_MODERATOR] = 'Moderator';
|
||||
|
||||
if (user_has_role(VRBAC::ROLE_SUPER_ADMIN)) {
|
||||
$roles[VRBAC::ROLE_ADMIN] = 'Administrator';
|
||||
}
|
||||
}
|
||||
|
||||
return $roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check content ownership or permission
|
||||
* @param string $resourceType Resource type (video, channel, playlist)
|
||||
* @param string $resourceId Resource ID
|
||||
* @param string $permission Required permission
|
||||
* @return bool True if user owns resource or has permission
|
||||
*/
|
||||
function can_access_resource($resourceType, $resourceId, $permission)
|
||||
{
|
||||
$context = [
|
||||
'resource_type' => $resourceType,
|
||||
'resource_id' => $resourceId
|
||||
];
|
||||
|
||||
return has_permission($permission, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user's highest role
|
||||
* @param int $userId User ID (optional)
|
||||
* @return string Highest role
|
||||
*/
|
||||
function get_highest_role($userId = null)
|
||||
{
|
||||
$roles = get_user_roles($userId);
|
||||
|
||||
$roleHierarchy = [
|
||||
VRBAC::ROLE_SUPER_ADMIN => 7,
|
||||
VRBAC::ROLE_ADMIN => 6,
|
||||
VRBAC::ROLE_MODERATOR => 5,
|
||||
VRBAC::ROLE_CREATOR => 4,
|
||||
VRBAC::ROLE_VERIFIED => 3,
|
||||
VRBAC::ROLE_MEMBER => 2,
|
||||
VRBAC::ROLE_GUEST => 1
|
||||
];
|
||||
|
||||
$highestRole = VRBAC::ROLE_GUEST;
|
||||
$highestLevel = 0;
|
||||
|
||||
foreach ($roles as $role) {
|
||||
$level = $roleHierarchy[$role] ?? 0;
|
||||
if ($level > $highestLevel) {
|
||||
$highestLevel = $level;
|
||||
$highestRole = $role;
|
||||
}
|
||||
}
|
||||
|
||||
return $highestRole;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create permission middleware for routes
|
||||
* @param string $permission Required permission
|
||||
* @param array $context Permission context
|
||||
* @return callable Middleware function
|
||||
*/
|
||||
function permission_middleware($permission, $context = [])
|
||||
{
|
||||
return function() use ($permission, $context) {
|
||||
if (!has_permission($permission, $context)) {
|
||||
http_response_code(403);
|
||||
|
||||
if (isset($_SESSION['USER_ID'])) {
|
||||
// Logged in user without permission
|
||||
include _FPATH . 'f_templates/tpl_frontend/tpl_error/tpl_403.tpl';
|
||||
} else {
|
||||
// Not logged in - redirect to login
|
||||
header('Location: /signin?redirect=' . urlencode($_SERVER['REQUEST_URI']));
|
||||
}
|
||||
exit;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get permission-based upload limits
|
||||
* @return array Upload limits
|
||||
*/
|
||||
function get_upload_limits()
|
||||
{
|
||||
$limits = [
|
||||
'max_file_size' => 50 * 1024 * 1024, // 50MB default
|
||||
'max_duration' => 600, // 10 minutes default
|
||||
'allowed_formats' => ['mp4', 'avi', 'mov'],
|
||||
'max_uploads_per_day' => 5
|
||||
];
|
||||
|
||||
if (has_permission('user.upload.advanced')) {
|
||||
$limits['max_file_size'] = 500 * 1024 * 1024; // 500MB
|
||||
$limits['max_duration'] = 3600; // 1 hour
|
||||
$limits['allowed_formats'] = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv'];
|
||||
$limits['max_uploads_per_day'] = 20;
|
||||
}
|
||||
|
||||
if (user_has_role(VRBAC::ROLE_CREATOR)) {
|
||||
$limits['max_file_size'] = 2 * 1024 * 1024 * 1024; // 2GB
|
||||
$limits['max_duration'] = 7200; // 2 hours
|
||||
$limits['max_uploads_per_day'] = 50;
|
||||
}
|
||||
|
||||
if (is_admin()) {
|
||||
$limits['max_file_size'] = -1; // Unlimited
|
||||
$limits['max_duration'] = -1; // Unlimited
|
||||
$limits['max_uploads_per_day'] = -1; // Unlimited
|
||||
}
|
||||
|
||||
return $limits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log security event
|
||||
* @param string $event Event description
|
||||
* @param array $context Event context
|
||||
*/
|
||||
if (!function_exists('log_security_event')) {
|
||||
function log_security_event($event, $context = [])
|
||||
{
|
||||
VSecurity::logSecurityEvent($event, $context);
|
||||
}
|
||||
}
|
||||
169
f_core/f_functions/functions.security.php
Normal file
169
f_core/f_functions/functions.security.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?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');
|
||||
|
||||
/**
|
||||
* Security helper functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Secure output escaping for templates
|
||||
* @param string $string String to escape
|
||||
* @return string Escaped string
|
||||
*/
|
||||
function secure_output($string)
|
||||
{
|
||||
return VSecurity::escapeOutput($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Secure JavaScript output escaping
|
||||
* @param string $string String to escape for JS
|
||||
* @return string Escaped string
|
||||
*/
|
||||
function secure_js($string)
|
||||
{
|
||||
return VSecurity::escapeJS($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate CSRF token field for forms
|
||||
* @param string $action Action name
|
||||
* @return string HTML input field
|
||||
*/
|
||||
function csrf_field($action = 'default')
|
||||
{
|
||||
return VSecurity::getCSRFField($action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate CSRF token from current request
|
||||
* @param string $action Action name
|
||||
* @return bool True if valid
|
||||
*/
|
||||
function validate_csrf($action = 'default')
|
||||
{
|
||||
return VSecurity::validateCSRFFromPost($action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get secure parameter from GET
|
||||
* @param string $key Parameter name
|
||||
* @param string $type Parameter type
|
||||
* @param mixed $default Default value
|
||||
* @param array $options Validation options
|
||||
* @return mixed Sanitized value
|
||||
*/
|
||||
function get_param($key, $type = 'string', $default = null, $options = [])
|
||||
{
|
||||
return VSecurity::getParam($key, $type, $default, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get secure parameter from POST
|
||||
* @param string $key Parameter name
|
||||
* @param string $type Parameter type
|
||||
* @param mixed $default Default value
|
||||
* @param array $options Validation options
|
||||
* @return mixed Sanitized value
|
||||
*/
|
||||
function post_param($key, $type = 'string', $default = null, $options = [])
|
||||
{
|
||||
return VSecurity::postParam($key, $type, $default, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check rate limiting
|
||||
* @param string $key Unique identifier
|
||||
* @param int $maxAttempts Maximum attempts
|
||||
* @param int $timeWindow Time window in seconds
|
||||
* @return bool True if within limits
|
||||
*/
|
||||
function check_rate_limit($key, $maxAttempts = 10, $timeWindow = 300)
|
||||
{
|
||||
return VSecurity::checkRateLimit($key, $maxAttempts, $timeWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate file upload securely
|
||||
* @param array $file $_FILES array element
|
||||
* @param array $allowedTypes Allowed MIME types
|
||||
* @param int $maxSize Maximum file size
|
||||
* @return array Validation result
|
||||
*/
|
||||
function validate_file_upload($file, $allowedTypes = [], $maxSize = 10485760)
|
||||
{
|
||||
return VSecurity::validateFileUpload($file, $allowedTypes, $maxSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log security events
|
||||
* @param string $event Event description
|
||||
* @param array $context Additional context
|
||||
*/
|
||||
function log_security_event($event, $context = [])
|
||||
{
|
||||
$logger = VLogger::getInstance();
|
||||
$logger->logSecurityEvent($event, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log application errors with context
|
||||
* @param string $message Error message
|
||||
* @param array $context Additional context
|
||||
*/
|
||||
function log_app_error($message, $context = [])
|
||||
{
|
||||
$errorHandler = VErrorHandler::getInstance();
|
||||
$errorHandler->logApplicationError($message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log validation errors
|
||||
* @param string $field Field name
|
||||
* @param mixed $value Field value
|
||||
* @param string $rule Validation rule
|
||||
* @param array $context Additional context
|
||||
*/
|
||||
function log_validation_error($field, $value, $rule, $context = [])
|
||||
{
|
||||
$errorHandler = VErrorHandler::getInstance();
|
||||
$errorHandler->logValidationError($field, $value, $rule, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log authentication errors
|
||||
* @param string $message Error message
|
||||
* @param string $username Username (if available)
|
||||
* @param array $context Additional context
|
||||
*/
|
||||
function log_auth_error($message, $username = null, $context = [])
|
||||
{
|
||||
$errorHandler = VErrorHandler::getInstance();
|
||||
$errorHandler->logAuthError($message, $username, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log performance issues
|
||||
* @param string $message Performance issue description
|
||||
* @param float $executionTime Execution time in seconds
|
||||
* @param array $context Additional context
|
||||
*/
|
||||
function log_performance_issue($message, $executionTime, $context = [])
|
||||
{
|
||||
$logger = VLogger::getInstance();
|
||||
$logger->logPerformanceIssue($message, $executionTime, $context);
|
||||
}
|
||||
304
f_core/f_functions/functions.videoprocessing.php
Normal file
304
f_core/f_functions/functions.videoprocessing.php
Normal file
@@ -0,0 +1,304 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Video Processing Helper Functions
|
||||
| Convenience functions for video processing integration
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
defined('_ISVALID') or header('Location: /error');
|
||||
|
||||
/**
|
||||
* Queue video for processing
|
||||
* @param string $videoKey Video key
|
||||
* @param string $inputFile Input file path
|
||||
* @param array $options Processing options
|
||||
* @return string|false Job ID or false on failure
|
||||
*/
|
||||
function queue_video_processing($videoKey, $inputFile, $options = [])
|
||||
{
|
||||
try {
|
||||
$processor = new VVideoProcessor();
|
||||
return $processor->queueVideoProcessing($inputFile, $videoKey, $options);
|
||||
} catch (Exception $e) {
|
||||
VLogger::getInstance()->error('Failed to queue video processing', [
|
||||
'video_key' => $videoKey,
|
||||
'input_file' => $inputFile,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process video immediately (synchronous)
|
||||
* @param string $videoKey Video key
|
||||
* @param string $inputFile Input file path
|
||||
* @param array $options Processing options
|
||||
* @return array Processing result
|
||||
*/
|
||||
function process_video_sync($videoKey, $inputFile, $options = [])
|
||||
{
|
||||
try {
|
||||
$processor = new VVideoProcessor();
|
||||
return $processor->processVideo($inputFile, $videoKey, $options);
|
||||
} catch (Exception $e) {
|
||||
VLogger::getInstance()->error('Synchronous video processing failed', [
|
||||
'video_key' => $videoKey,
|
||||
'input_file' => $inputFile,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => $e->getMessage(),
|
||||
'video_key' => $videoKey
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get video processing status
|
||||
* @param string $videoKey Video key
|
||||
* @return array|false Processing status or false if not found
|
||||
*/
|
||||
function get_video_processing_status($videoKey)
|
||||
{
|
||||
try {
|
||||
$db = VDatabase::getInstance();
|
||||
|
||||
$query = "SELECT processing_status, processed_at, processing_error,
|
||||
available_formats, hls_available, thumbnails_generated
|
||||
FROM db_videofiles WHERE file_key = ?";
|
||||
$result = $db->doQuery($query, [$videoKey]);
|
||||
$row = $db->doFetch($result);
|
||||
|
||||
if ($row) {
|
||||
return [
|
||||
'status' => $row['processing_status'],
|
||||
'processed_at' => $row['processed_at'],
|
||||
'error' => $row['processing_error'],
|
||||
'available_formats' => json_decode($row['available_formats'], true) ?: [],
|
||||
'hls_available' => (bool)$row['hls_available'],
|
||||
'thumbnails_generated' => (bool)$row['thumbnails_generated']
|
||||
];
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
} catch (Exception $e) {
|
||||
VLogger::getInstance()->error('Failed to get video processing status', [
|
||||
'video_key' => $videoKey,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue thumbnail generation
|
||||
* @param string $fileKey File key
|
||||
* @param string $filePath File path
|
||||
* @param string $fileType File type (video, image)
|
||||
* @param array $sizes Thumbnail sizes
|
||||
* @return string|false Job ID or false on failure
|
||||
*/
|
||||
function queue_thumbnail_generation($fileKey, $filePath, $fileType, $sizes = [])
|
||||
{
|
||||
try {
|
||||
$queue = new VQueue();
|
||||
|
||||
$jobData = [
|
||||
'file_key' => $fileKey,
|
||||
'file_path' => $filePath,
|
||||
'file_type' => $fileType,
|
||||
'sizes' => $sizes ?: [
|
||||
'small' => [160, 120],
|
||||
'medium' => [320, 240],
|
||||
'large' => [640, 480]
|
||||
]
|
||||
];
|
||||
|
||||
return $queue->enqueue('ThumbnailGenerationJob', $jobData, 'thumbnails');
|
||||
|
||||
} catch (Exception $e) {
|
||||
VLogger::getInstance()->error('Failed to queue thumbnail generation', [
|
||||
'file_key' => $fileKey,
|
||||
'file_path' => $filePath,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue notification
|
||||
* @param string $type Notification type (email, push, in_app, sms, webhook)
|
||||
* @param string $recipient Recipient (email, user ID, phone, URL)
|
||||
* @param string $subject Subject/title
|
||||
* @param string $message Message content
|
||||
* @param array $templateData Additional template data
|
||||
* @param string $priority Priority (low, normal, high, urgent)
|
||||
* @return string|false Job ID or false on failure
|
||||
*/
|
||||
function queue_notification($type, $recipient, $subject, $message, $templateData = [], $priority = 'normal')
|
||||
{
|
||||
try {
|
||||
$queue = new VQueue();
|
||||
|
||||
$jobData = [
|
||||
'type' => $type,
|
||||
'recipient' => $recipient,
|
||||
'subject' => $subject,
|
||||
'message' => $message,
|
||||
'template_data' => $templateData,
|
||||
'priority' => $priority
|
||||
];
|
||||
|
||||
$queuePriority = $priority === 'urgent' ? 2 : ($priority === 'high' ? 1 : 0);
|
||||
|
||||
return $queue->enqueue('NotificationJob', $jobData, 'notifications', 0, $queuePriority);
|
||||
|
||||
} catch (Exception $e) {
|
||||
VLogger::getInstance()->error('Failed to queue notification', [
|
||||
'type' => $type,
|
||||
'recipient' => $recipient,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get queue statistics
|
||||
* @param string $queue Queue name (optional)
|
||||
* @return array Queue statistics
|
||||
*/
|
||||
function get_queue_stats($queue = null)
|
||||
{
|
||||
try {
|
||||
$queueInstance = new VQueue();
|
||||
return $queueInstance->getQueueStats($queue);
|
||||
} catch (Exception $e) {
|
||||
VLogger::getInstance()->error('Failed to get queue statistics', [
|
||||
'queue' => $queue,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if video processing is available
|
||||
* @return bool True if FFmpeg is available
|
||||
*/
|
||||
function is_video_processing_available()
|
||||
{
|
||||
static $available = null;
|
||||
|
||||
if ($available === null) {
|
||||
// Check if FFmpeg is available
|
||||
$output = shell_exec('ffmpeg -version 2>&1');
|
||||
$available = $output && strpos($output, 'ffmpeg version') !== false;
|
||||
}
|
||||
|
||||
return $available;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get supported video formats
|
||||
* @return array Supported formats
|
||||
*/
|
||||
function get_supported_video_formats()
|
||||
{
|
||||
return [
|
||||
'mp4' => 'MP4 Video',
|
||||
'avi' => 'AVI Video',
|
||||
'mov' => 'QuickTime Video',
|
||||
'wmv' => 'Windows Media Video',
|
||||
'flv' => 'Flash Video',
|
||||
'webm' => 'WebM Video',
|
||||
'mkv' => 'Matroska Video',
|
||||
'3gp' => '3GP Video'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get video processing formats
|
||||
* @return array Available processing formats
|
||||
*/
|
||||
function get_video_processing_formats()
|
||||
{
|
||||
return [
|
||||
'1080p' => ['width' => 1920, 'height' => 1080, 'bitrate' => '5000k'],
|
||||
'720p' => ['width' => 1280, 'height' => 720, 'bitrate' => '2500k'],
|
||||
'480p' => ['width' => 854, 'height' => 480, 'bitrate' => '1000k'],
|
||||
'360p' => ['width' => 640, 'height' => 360, 'bitrate' => '750k'],
|
||||
'240p' => ['width' => 426, 'height' => 240, 'bitrate' => '400k']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate video processing time
|
||||
* @param string $inputFile Input file path
|
||||
* @param array $formats Formats to process
|
||||
* @return int Estimated processing time in seconds
|
||||
*/
|
||||
function estimate_video_processing_time($inputFile, $formats = ['720p', '480p', '360p'])
|
||||
{
|
||||
if (!file_exists($inputFile)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$fileSize = filesize($inputFile);
|
||||
$fileSizeMB = $fileSize / (1024 * 1024);
|
||||
|
||||
// Rough estimation: 1MB takes about 2-5 seconds per format
|
||||
$baseTimePerMB = 3; // seconds
|
||||
$timePerFormat = $fileSizeMB * $baseTimePerMB;
|
||||
|
||||
return (int)($timePerFormat * count($formats));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up old processed files
|
||||
* @param int $daysOld Days old threshold
|
||||
* @return int Number of files cleaned
|
||||
*/
|
||||
function cleanup_old_processed_files($daysOld = 30)
|
||||
{
|
||||
try {
|
||||
$processedDir = _FPATH . 'f_data/processed/';
|
||||
$cutoffTime = time() - ($daysOld * 24 * 60 * 60);
|
||||
$cleanedCount = 0;
|
||||
|
||||
if (!is_dir($processedDir)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($processedDir),
|
||||
RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isFile() && $file->getMTime() < $cutoffTime) {
|
||||
if (unlink($file->getPathname())) {
|
||||
$cleanedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VLogger::getInstance()->info('Cleaned up old processed files', [
|
||||
'days_old' => $daysOld,
|
||||
'files_cleaned' => $cleanedCount
|
||||
]);
|
||||
|
||||
return $cleanedCount;
|
||||
|
||||
} catch (Exception $e) {
|
||||
VLogger::getInstance()->error('Failed to cleanup old processed files', [
|
||||
'days_old' => $daysOld,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user