feat: Add complete Docker deployment with web-based setup wizard
Major additions: - Web-based setup wizard (setup.php, setup_wizard.php, setup-wizard.js) - Production Docker configuration (docker-compose.prod.yml, .env.production) - Database initialization SQL files (deploy/init_settings.sql) - Template builder system with drag-and-drop UI - Advanced features (OAuth, CDN, enhanced analytics, monetization) - Comprehensive documentation (deployment guides, quick start, feature docs) - Design system with accessibility and responsive layout - Deployment automation scripts (deploy.ps1, generate-secrets.ps1) Setup wizard allows customization of: - Platform name and branding - Domain configuration - Membership tiers and pricing - Admin credentials - Feature toggles Database includes 270+ tables for complete video streaming platform with advanced features for analytics, moderation, template building, and monetization. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,8 @@ $href = array();
|
||||
$href["index"] = '';
|
||||
$href["error"] = 'error';
|
||||
$href["renew"] = 'renew';
|
||||
$href["signup"] = 'register';
|
||||
$href["signup"] = 'signup';
|
||||
$href["register"] = 'register';
|
||||
$href["signin"] = 'signin';
|
||||
$href["signout"] = 'signout';
|
||||
$href["service"] = 'service';
|
||||
@@ -30,6 +31,7 @@ $href["reset_password"] = 'reset_password';
|
||||
$href["confirm_email"] = 'confirm_email';
|
||||
$href["captcha"] = 'captcha';
|
||||
$href["account"] = 'account';
|
||||
$href["builder"] = 'builder';
|
||||
$href["channels"] = 'channels';
|
||||
$href["channel"] = 'channel';
|
||||
$href["@"] = 'channel';
|
||||
|
||||
187
f_core/f_classes/class.advancedsearch.php
Normal file
187
f_core/f_classes/class.advancedsearch.php
Normal file
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
/**
|
||||
* EasyStream Advanced Search Engine
|
||||
* Meilisearch Integration with MySQL Fallback
|
||||
* Version: 1.0
|
||||
*/
|
||||
|
||||
defined('_ISVALID') or header('Location: /error');
|
||||
|
||||
class VAdvancedSearch {
|
||||
private static $db;
|
||||
private static $meilisearch_host = 'http://localhost:7700';
|
||||
private static $meilisearch_key = null;
|
||||
|
||||
public static function init() {
|
||||
self::$db = VDatabase::getInstance();
|
||||
|
||||
// Load config if available
|
||||
$config = VGenerate::getConfig('meilisearch_config');
|
||||
if ($config) {
|
||||
self::$meilisearch_host = $config['host'] ?? self::$meilisearch_host;
|
||||
self::$meilisearch_key = $config['api_key'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform advanced search
|
||||
* @param string $query Search query
|
||||
* @param array $filters Filters array
|
||||
* @param array $options Pagination/sorting options
|
||||
* @return array Search results
|
||||
*/
|
||||
public static function search($query, $filters = [], $options = []) {
|
||||
self::init();
|
||||
|
||||
$usr_id = isset($_SESSION['USER_ID']) ? (int)$_SESSION['USER_ID'] : null;
|
||||
|
||||
// Track search
|
||||
self::trackSearch($query, $filters, $usr_id);
|
||||
|
||||
// Use MySQL search (Meilisearch optional)
|
||||
return self::searchMySQL($query, $filters, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* MySQL-based search with full-text
|
||||
*/
|
||||
private static function searchMySQL($query, $filters = [], $options = []) {
|
||||
$page = $options['page'] ?? 1;
|
||||
$limit = min(50, $options['limit'] ?? 20);
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
$query_safe = VDatabase::escape($query);
|
||||
|
||||
$where = ["vf.privacy = 'public'"];
|
||||
|
||||
// Full-text search
|
||||
if (!empty($query)) {
|
||||
$where[] = "(vf.file_title LIKE '%$query_safe%' OR vf.file_description LIKE '%$query_safe%' OR vf.file_tags LIKE '%$query_safe%')";
|
||||
}
|
||||
|
||||
// Type filter
|
||||
if (!empty($filters['type'])) {
|
||||
$type = VDatabase::escape($filters['type']);
|
||||
$where[] = "vf.file_type = '$type'";
|
||||
}
|
||||
|
||||
// Duration filter
|
||||
if (isset($filters['duration_min'])) {
|
||||
$min = (int)$filters['duration_min'];
|
||||
$where[] = "vf.file_duration >= $min";
|
||||
}
|
||||
if (isset($filters['duration_max'])) {
|
||||
$max = (int)$filters['duration_max'];
|
||||
$where[] = "vf.file_duration <= $max";
|
||||
}
|
||||
|
||||
// Date filter
|
||||
if (!empty($filters['date_from'])) {
|
||||
$from = VDatabase::escape($filters['date_from']);
|
||||
$where[] = "vf.upload_date >= '$from'";
|
||||
}
|
||||
|
||||
// Category filter
|
||||
if (!empty($filters['category'])) {
|
||||
$cat = VDatabase::escape($filters['category']);
|
||||
$where[] = "vf.file_category = '$cat'";
|
||||
}
|
||||
|
||||
$whereClause = implode(' AND ', $where);
|
||||
|
||||
// Sorting
|
||||
$sort = $options['sort'] ?? 'recent';
|
||||
$orderBy = match($sort) {
|
||||
'popular' => 'vf.file_views DESC',
|
||||
'rating' => 'vf.file_rating DESC',
|
||||
default => 'vf.upload_date DESC'
|
||||
};
|
||||
|
||||
$sql = "SELECT vf.file_key, vf.file_title, vf.file_description, vf.file_type,
|
||||
vf.file_views, vf.file_rating, vf.upload_date, vf.file_duration,
|
||||
au.usr_user, au.usr_dname
|
||||
FROM db_videofiles vf
|
||||
JOIN db_accountuser au ON vf.usr_id = au.usr_id
|
||||
WHERE $whereClause
|
||||
ORDER BY $orderBy
|
||||
LIMIT $limit OFFSET $offset";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
|
||||
$results = [];
|
||||
if ($result) {
|
||||
while ($row = $result->FetchRow()) {
|
||||
$results[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
// Get total
|
||||
$countSql = "SELECT COUNT(*) as total FROM db_videofiles vf WHERE $whereClause";
|
||||
$countResult = self::$db->execute($countSql);
|
||||
$total = 0;
|
||||
if ($countResult) {
|
||||
$row = $countResult->FetchRow();
|
||||
$total = (int)$row['total'];
|
||||
}
|
||||
|
||||
return [
|
||||
'query' => $query,
|
||||
'results' => $results,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'total_pages' => ceil($total / $limit)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Track search for analytics
|
||||
*/
|
||||
private static function trackSearch($query, $filters, $usr_id) {
|
||||
if (empty($query)) return;
|
||||
|
||||
$query_safe = VDatabase::escape($query);
|
||||
$usr_id_val = $usr_id ? (int)$usr_id : 'NULL';
|
||||
$session = session_id();
|
||||
$session_safe = VDatabase::escape($session);
|
||||
$filters_json = VDatabase::escape(json_encode($filters));
|
||||
|
||||
$sql = "INSERT INTO db_search_history
|
||||
(usr_id, session_id, query, filters, created_at)
|
||||
VALUES ($usr_id_val, '$session_safe', '$query_safe', '$filters_json', NOW())";
|
||||
self::$db->execute($sql);
|
||||
|
||||
// Update suggestions
|
||||
$sql = "INSERT INTO db_search_suggestions (query, search_count, last_searched)
|
||||
VALUES ('$query_safe', 1, NOW())
|
||||
ON DUPLICATE KEY UPDATE search_count = search_count + 1, last_searched = NOW()";
|
||||
self::$db->execute($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get search suggestions
|
||||
*/
|
||||
public static function getSuggestions($query, $limit = 10) {
|
||||
self::init();
|
||||
|
||||
$query_safe = VDatabase::escape($query);
|
||||
$limit = (int)$limit;
|
||||
|
||||
$sql = "SELECT query, search_count
|
||||
FROM db_search_suggestions
|
||||
WHERE query LIKE '%$query_safe%'
|
||||
ORDER BY search_count DESC
|
||||
LIMIT $limit";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
$suggestions = [];
|
||||
|
||||
if ($result) {
|
||||
while ($row = $result->FetchRow()) {
|
||||
$suggestions[] = $row['query'];
|
||||
}
|
||||
}
|
||||
|
||||
return $suggestions;
|
||||
}
|
||||
}
|
||||
@@ -746,7 +746,7 @@ class VAffiliate
|
||||
global $language, $class_database, $class_filter, $class_language, $db, $cfg, $smarty;
|
||||
|
||||
$type = self::getType();
|
||||
$usr_key = ($cfg["is_be"] == 1 and isset($_GET["uk"])) ? $class_filter->clr_str($_GET["uk"]) : (!$cfg["is_be"] ? $_SESSION["USER_KEY"] : false);
|
||||
$usr_key = ($cfg["is_be"] == 1 and isset($_GET["uk"])) ? $class_filter->clr_str($_GET["uk"]) : ((!$cfg["is_be"]) ? $_SESSION["USER_KEY"] : false);
|
||||
$f = isset($_GET["f"]) ? $class_filter->clr_str($_GET["f"]) : 'lastmonth';
|
||||
|
||||
if ($f) {
|
||||
@@ -796,7 +796,7 @@ class VAffiliate
|
||||
$views_min = isset($_SESSION["views_min"]) ? $_SESSION["views_min"] : 0;
|
||||
$views_max = isset($_SESSION["views_max"]) ? $_SESSION["views_max"] : 0;
|
||||
|
||||
$uk = ($cfg["is_be"] == 1 and isset($_GET["uk"])) ? $class_filter->clr_str($_GET["uk"]) : (!$cfg["is_be"] ? $_SESSION["USER_KEY"] : false);
|
||||
$uk = ($cfg["is_be"] == 1 and isset($_GET["uk"])) ? $class_filter->clr_str($_GET["uk"]) : ((!$cfg["is_be"]) ? $_SESSION["USER_KEY"] : false);
|
||||
$fk = isset($_GET["fk"]) ? $class_filter->clr_str($_GET["fk"]) : false;
|
||||
$tab = isset($_GET["tab"]) ? $class_filter->clr_str($_GET["tab"]) : false;
|
||||
|
||||
@@ -820,7 +820,7 @@ class VAffiliate
|
||||
($uk ? "A.`usr_key`='" . $uk . "' AND " : null),
|
||||
($views_min > 0 ? "A.`p_views`>='" . $views_min . "' AND " : null),
|
||||
($views_max > 0 ? "A.`p_views`<='" . $views_max . "' AND " : null),
|
||||
($tab == 'section-all' ? null : ($tab == 'section-paid' ? "A.`p_paid`='1' AND " : "A.`p_paid`='0' AND "))
|
||||
($tab == 'section-all' ? null : (($tab == 'section-paid') ? "A.`p_paid`='1' AND " : "A.`p_paid`='0' AND "))
|
||||
)
|
||||
);
|
||||
|
||||
@@ -844,7 +844,7 @@ class VAffiliate
|
||||
($uk ? "A.`usr_key`='" . $uk . "' AND " : null),
|
||||
($views_min > 0 ? "A.`p_views`>='" . $views_min . "' AND " : null),
|
||||
($views_max > 0 ? "A.`p_views`<='" . $views_max . "' AND " : null),
|
||||
($tab == 'section-all' ? null : ($tab == 'section-paid' ? "A.`p_paid`='1' AND " : "A.`p_paid`='0' AND "))
|
||||
($tab == 'section-all' ? null : (($tab == 'section-paid') ? "A.`p_paid`='1' AND " : "A.`p_paid`='0' AND "))
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1143,7 +1143,7 @@ class VAffiliate
|
||||
$html .= '</div>';
|
||||
$html .= '<div class="vs-column half fit">';
|
||||
$html .= '<ul class="views-details">';
|
||||
$html .= $cfg["is_be"] == 1 ? '<li><i class="icon-paypal"></i> ' . ($af_mail == '' ? '<span class="">affiliate email not available</span>' : ($res->fields["p_paid"] == 0 ? '<a href="' . $pp_link_shared . '" target="_blank">' . $language["account.entry.payout.pay"] . '</a>' : $language["account.entry.payout.paydate"] . ': ' . date('M j, o, H:i:s A', strtotime($res->fields["p_paydate"])))) . '</li>' : null;
|
||||
$html .= $cfg["is_be"] == 1 ? '<li><i class="icon-paypal"></i> ' . ($af_mail == '' ? '<span class="">affiliate email not available</span>' : (($res->fields["p_paid"] == 0) ? '<a href="' . $pp_link_shared . '" target="_blank">' . $language["account.entry.payout.pay"] . '</a>' : $language["account.entry.payout.paydate"] . ': ' . date('M j, o, H:i:s A', strtotime($res->fields["p_paydate"])))) . '</li>' : null;
|
||||
$html .= (!$cfg["is_be"] and $res->fields["p_paid"] == 1) ? '<li><i class="icon-paypal"></i> ' . ($res->fields["p_paid"] == 1 ? $language["account.entry.payout.paydate"] . ': ' . date('M j, o, H:i:s A', strtotime($res->fields["p_paydate"])) : null) . '</li>' : null;
|
||||
$html .= '<li><i class="icon-pie"></i> <a href="javascript:;" class="fviews" rel-id="' . $db_id . '" rel-fk="' . $res->fields["file_key"] . '" rel-f="' . $f . '">' . $language["account.entry.act.views"] . '</a> ' . $_f . '</li>';
|
||||
$html .= '<li><i class="icon-pie"></i> <a href="javascript:;" class="fviews-range" rel-id="' . $db_id . '" rel-fk="' . $res->fields["file_key"] . '" rel-s="' . $p_start . '" rel-e="' . $p_end . '">' . $language["account.entry.act.views"] . '</a> ' . date('M j', strtotime($p_start)) . ' -- ' . date('M j, o', strtotime($p_end)) . '</li>';
|
||||
@@ -1265,7 +1265,7 @@ class VAffiliate
|
||||
t = $(this);
|
||||
rid = t.attr("rel-nr");
|
||||
b = "' . ($cfg['is_be'] == 1 ? "" : $cfg["main_url"] . '/' . VHref::getKey("affiliate")) . '";
|
||||
u = b + "?' . (isset($_GET["a"]) ? 'a=' . $class_filter->clr_str($_GET["a"]) : (isset($_GET["g"]) ? 'g=' . $class_filter->clr_str($_GET["g"]) : 'a')) . '&t=' . self::getType() . (isset($_GET["f"]) ? '&f=' . $class_filter->clr_str($_GET["f"]) : '&f=today') . '&c=' . (isset($_GET["c"]) ? $class_filter->clr_str($_GET["c"]) : (isset($_POST["custom_country"]) ? $class_filter->clr_str($_POST["custom_country"]) : 'xx')) . '&r="+rid;
|
||||
u = b + "?' . (isset($_GET["a"]) ? 'a=' . $class_filter->clr_str($_GET["a"]) : ((isset($_GET["g"])) ? 'g=' . $class_filter->clr_str($_GET["g"]) : 'a')) . '&t=' . self::getType() . (isset($_GET["f"]) ? '&f=' . $class_filter->clr_str($_GET["f"]) : '&f=today') . '&c=' . (isset($_GET["c"]) ? $class_filter->clr_str($_GET["c"]) : ((isset($_POST["custom_country"])) ? $class_filter->clr_str($_POST["custom_country"]) : 'xx')) . '&r="+rid;
|
||||
u+= "' . (isset($_GET["fk"]) ? '&fk=' . $class_filter->clr_str($_GET["fk"]) : null) . '";
|
||||
u+= "' . ((isset($_GET["uk"]) and !isset($_GET["fk"])) ? '&uk=' . $class_filter->clr_str($_GET["uk"]) : null) . '";
|
||||
|
||||
@@ -1393,7 +1393,7 @@ class VAffiliate
|
||||
// $_i = $_i_be;
|
||||
$html = '
|
||||
<article>
|
||||
<h3 class="content-title"><i class="icon-' . (isset($_GET["g"]) ? 'globe' : (isset($_GET["o"]) ? 'bars' : (isset($_GET["rp"]) ? 'paypal' : 'pie'))) . '"></i>' . (isset($_GET["g"]) ? $language["account.entry.act.maps"] : (isset($_GET["o"]) ? $language["account.entry.act.comp"] : (isset($_GET["rp"]) ? $language["account.entry.payout.rep"] : $language["account.entry.act.views"]))) . $_i . '</h3>
|
||||
<h3 class="content-title"><i class="icon-' . (isset($_GET["g"]) ? 'globe' : ((isset($_GET["o"])) ? 'bars' : ((isset($_GET["rp"])) ? 'paypal' : 'pie'))) . '"></i>' . (isset($_GET["g"]) ? $language["account.entry.act.maps"] : ((isset($_GET["o"])) ? $language["account.entry.act.comp"] : ((isset($_GET["rp"])) ? $language["account.entry.payout.rep"] : $language["account.entry.act.views"]))) . $_i . '</h3>
|
||||
<div id="search-boxes">
|
||||
<section class="inner-search-off place-right">
|
||||
<form id="view-limits" class="entry-form-class" method="post" action="">
|
||||
@@ -1447,7 +1447,7 @@ class VAffiliate
|
||||
<article id="time-sort-filters" style="display: ' . (isset($_GET["f"]) ? 'block' : 'none') . ';">
|
||||
<h3 class="content-title content-filter"><i class="icon-filter"></i>' . $language["account.entry.filter.results"] . '</h3>
|
||||
<section class="filter">
|
||||
' . (($cfg[($type == 'doc' ? 'document' : $type) . "_module"] == 1 and !$o) ? self::tpl_filters($type) : null) . '
|
||||
' . (($cfg[(($type == 'doc') ? 'document' : $type) . "_module"] == 1 and !$o) ? self::tpl_filters($type) : null) . '
|
||||
</section>
|
||||
<div class="clearfix"></div>
|
||||
</article>
|
||||
|
||||
240
f_core/f_classes/class.analytics.enhanced.php
Normal file
240
f_core/f_classes/class.analytics.enhanced.php
Normal file
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
/**
|
||||
* EasyStream Enhanced Analytics System
|
||||
* Event tracking, retention graphs, heatmaps, demographics
|
||||
* Version: 1.0
|
||||
*/
|
||||
|
||||
defined('_ISVALID') or header('Location: /error');
|
||||
|
||||
class VAnalyticsEnhanced {
|
||||
private static $db;
|
||||
|
||||
public static function init() {
|
||||
self::$db = VDatabase::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Track event
|
||||
* @param string $event_type Event type (view, play, pause, seek, etc.)
|
||||
* @param string $file_key File key
|
||||
* @param array $data Additional event data
|
||||
*/
|
||||
public static function trackEvent($event_type, $file_key, $data = []) {
|
||||
self::init();
|
||||
|
||||
$usr_id = isset($_SESSION['USER_ID']) ? (int)$_SESSION['USER_ID'] : 'NULL';
|
||||
$session_id = session_id();
|
||||
$event_type_safe = VDatabase::escape($event_type);
|
||||
$file_key_safe = VDatabase::escape($file_key);
|
||||
$session_safe = VDatabase::escape($session_id);
|
||||
$data_json = VDatabase::escape(json_encode($data));
|
||||
$timestamp = isset($data['timestamp']) ? (int)$data['timestamp'] : 'NULL';
|
||||
$ip = VDatabase::escape($_SERVER['REMOTE_ADDR'] ?? '');
|
||||
$ua = VDatabase::escape($_SERVER['HTTP_USER_AGENT'] ?? '');
|
||||
$referrer = VDatabase::escape($_SERVER['HTTP_REFERER'] ?? '');
|
||||
|
||||
// Get file type
|
||||
$typeResult = self::$db->execute("SELECT file_type FROM db_videofiles WHERE file_key = '$file_key_safe'");
|
||||
$file_type = 'NULL';
|
||||
if ($typeResult && $typeResult->RecordCount() > 0) {
|
||||
$row = $typeResult->FetchRow();
|
||||
$file_type = "'" . VDatabase::escape($row['file_type']) . "'";
|
||||
}
|
||||
|
||||
$sql = "INSERT INTO db_analytics_events
|
||||
(usr_id, session_id, event_type, file_key, file_type, event_data, timestamp_sec, ip_address, user_agent, referrer, created_at)
|
||||
VALUES ($usr_id, '$session_safe', '$event_type_safe', '$file_key_safe', $file_type, '$data_json', $timestamp, '$ip', '$ua', '$referrer', NOW())";
|
||||
|
||||
self::$db->execute($sql);
|
||||
|
||||
// Update retention data for video events
|
||||
if ($event_type == 'play' || $event_type == 'pause') {
|
||||
self::updateRetention($file_key, $timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update retention data
|
||||
*/
|
||||
private static function updateRetention($file_key, $timestamp_sec) {
|
||||
if (!$timestamp_sec) return;
|
||||
|
||||
$file_key_safe = VDatabase::escape($file_key);
|
||||
$timestamp = (int)$timestamp_sec;
|
||||
|
||||
$sql = "INSERT INTO db_analytics_retention
|
||||
(file_key, timestamp_sec, viewers, updated_at)
|
||||
VALUES ('$file_key_safe', $timestamp, 1, NOW())
|
||||
ON DUPLICATE KEY UPDATE viewers = viewers + 1, updated_at = NOW()";
|
||||
|
||||
self::$db->execute($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get retention graph data
|
||||
* @param string $file_key File key
|
||||
* @return array Retention data by second
|
||||
*/
|
||||
public static function getRetentionGraph($file_key) {
|
||||
self::init();
|
||||
|
||||
$file_key_safe = VDatabase::escape($file_key);
|
||||
|
||||
$sql = "SELECT timestamp_sec, viewers
|
||||
FROM db_analytics_retention
|
||||
WHERE file_key = '$file_key_safe'
|
||||
ORDER BY timestamp_sec ASC";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
$data = [];
|
||||
|
||||
if ($result) {
|
||||
while ($row = $result->FetchRow()) {
|
||||
$data[] = [
|
||||
'time' => (int)$row['timestamp_sec'],
|
||||
'viewers' => (int)$row['viewers']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get traffic sources
|
||||
* @param string $file_key File key
|
||||
* @param string $date_from Start date
|
||||
* @param string $date_to End date
|
||||
* @return array Traffic sources
|
||||
*/
|
||||
public static function getTrafficSources($file_key, $date_from, $date_to) {
|
||||
self::init();
|
||||
|
||||
$file_key_safe = VDatabase::escape($file_key);
|
||||
$from_safe = VDatabase::escape($date_from);
|
||||
$to_safe = VDatabase::escape($date_to);
|
||||
|
||||
// Analyze referrers from events
|
||||
$sql = "SELECT
|
||||
CASE
|
||||
WHEN referrer = '' THEN 'direct'
|
||||
WHEN referrer LIKE '%google%' OR referrer LIKE '%bing%' THEN 'search'
|
||||
WHEN referrer LIKE '%facebook%' OR referrer LIKE '%twitter%' OR referrer LIKE '%instagram%' THEN 'social'
|
||||
WHEN referrer LIKE '%{$_SERVER['HTTP_HOST']}%' THEN 'internal'
|
||||
ELSE 'external'
|
||||
END as source_type,
|
||||
COUNT(*) as visits
|
||||
FROM db_analytics_events
|
||||
WHERE file_key = '$file_key_safe'
|
||||
AND event_type = 'view'
|
||||
AND DATE(created_at) BETWEEN '$from_safe' AND '$to_safe'
|
||||
GROUP BY source_type";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
$sources = [];
|
||||
|
||||
if ($result) {
|
||||
while ($row = $result->FetchRow()) {
|
||||
$sources[] = [
|
||||
'source' => $row['source_type'],
|
||||
'visits' => (int)$row['visits']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $sources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get analytics summary
|
||||
* @param string $file_key File key
|
||||
* @param string $date_from Start date
|
||||
* @param string $date_to End date
|
||||
* @return array Summary data
|
||||
*/
|
||||
public static function getSummary($file_key, $date_from, $date_to) {
|
||||
self::init();
|
||||
|
||||
$file_key_safe = VDatabase::escape($file_key);
|
||||
$from_safe = VDatabase::escape($date_from);
|
||||
$to_safe = VDatabase::escape($date_to);
|
||||
|
||||
$sql = "SELECT
|
||||
COUNT(DISTINCT session_id) as unique_viewers,
|
||||
COUNT(*) as total_views,
|
||||
SUM(CASE WHEN event_type = 'like' THEN 1 ELSE 0 END) as likes,
|
||||
SUM(CASE WHEN event_type = 'comment' THEN 1 ELSE 0 END) as comments,
|
||||
SUM(CASE WHEN event_type = 'share' THEN 1 ELSE 0 END) as shares
|
||||
FROM db_analytics_events
|
||||
WHERE file_key = '$file_key_safe'
|
||||
AND DATE(created_at) BETWEEN '$from_safe' AND '$to_safe'";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
|
||||
if ($result && $result->RecordCount() > 0) {
|
||||
return $result->FetchRow();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Track heatmap click
|
||||
* @param string $file_key File key
|
||||
* @param float $x X coordinate (0-1)
|
||||
* @param float $y Y coordinate (0-1)
|
||||
* @param string $type Type (click or hover)
|
||||
*/
|
||||
public static function trackHeatmap($file_key, $x, $y, $type = 'click') {
|
||||
self::init();
|
||||
|
||||
$file_key_safe = VDatabase::escape($file_key);
|
||||
$x = (float)$x;
|
||||
$y = (float)$y;
|
||||
$date = date('Y-m-d');
|
||||
|
||||
$field = $type == 'hover' ? 'hovers' : 'clicks';
|
||||
|
||||
$sql = "INSERT INTO db_analytics_heatmaps
|
||||
(file_key, x_coord, y_coord, $field, date)
|
||||
VALUES ('$file_key_safe', $x, $y, 1, '$date')
|
||||
ON DUPLICATE KEY UPDATE $field = $field + 1";
|
||||
|
||||
self::$db->execute($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get heatmap data
|
||||
* @param string $file_key File key
|
||||
* @param string $date Date
|
||||
* @return array Heatmap coordinates
|
||||
*/
|
||||
public static function getHeatmap($file_key, $date) {
|
||||
self::init();
|
||||
|
||||
$file_key_safe = VDatabase::escape($file_key);
|
||||
$date_safe = VDatabase::escape($date);
|
||||
|
||||
$sql = "SELECT x_coord, y_coord, clicks, hovers
|
||||
FROM db_analytics_heatmaps
|
||||
WHERE file_key = '$file_key_safe' AND date = '$date_safe'";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
$heatmap = [];
|
||||
|
||||
if ($result) {
|
||||
while ($row = $result->FetchRow()) {
|
||||
$heatmap[] = [
|
||||
'x' => (float)$row['x_coord'],
|
||||
'y' => (float)$row['y_coord'],
|
||||
'clicks' => (int)$row['clicks'],
|
||||
'hovers' => (int)$row['hovers'],
|
||||
'intensity' => (int)$row['clicks'] + ((int)$row['hovers'] / 10)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $heatmap;
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,13 @@ defined('_ISVALID') or header('Location: /error');
|
||||
class VArraySection
|
||||
{
|
||||
/* remove from array based on key */
|
||||
public function arrayRemoveKey()
|
||||
public static function arrayRemoveKey()
|
||||
{
|
||||
$args = func_get_args();
|
||||
return array_diff_key($args[0], array_flip(array_slice($args, 1)));
|
||||
}
|
||||
/* multi array pop */
|
||||
public function array_mpop($array, $iterate)
|
||||
public static function array_mpop($array, $iterate)
|
||||
{
|
||||
if (!is_array($array) && is_int($iterate)) {
|
||||
return false;
|
||||
@@ -37,7 +37,7 @@ class VArraySection
|
||||
return $array;
|
||||
}
|
||||
/* more sanitized forms and fields */
|
||||
public function getArray($section)
|
||||
public static function getArray($section)
|
||||
{
|
||||
global $class_filter, $cfg;
|
||||
|
||||
@@ -603,7 +603,7 @@ class VArraySection
|
||||
$entryid = (int) $_POST['hc_id'];
|
||||
$account_new_pack = "account_new_pack_" . $entryid;
|
||||
$_array = array(
|
||||
"usr_user" => (($cfg['username_format'] == 'strict' and VUserinfo::isValidUsername($_POST['account_new_username'])) ? $class_filter->clr_str($_POST['account_new_username']) : ($cfg['username_format'] == 'loose' and VUserinfo::isValidUsername($_POST['account_new_username'])) ? VUserinfo::clearString($_POST['account_new_username']) : null),
|
||||
"usr_user" => (($cfg['username_format'] == 'strict' and VUserinfo::isValidUsername($_POST['account_new_username'])) ? $class_filter->clr_str($_POST['account_new_username']) : (($cfg['username_format'] == 'loose' and VUserinfo::isValidUsername($_POST['account_new_username'])) ? VUserinfo::clearString($_POST['account_new_username']) : null)),
|
||||
"usr_password" => $_POST['account_new_password'],
|
||||
"usr_password_conf" => $_POST['account_new_password_conf'],
|
||||
"usr_email" => $class_filter->clr_str($_POST['frontend_global_email']),
|
||||
|
||||
@@ -466,7 +466,7 @@ class VbeAdvertising
|
||||
if ($af->fields['db_key']) {
|
||||
$_sel6 = '<select name="jw_file_' . $int_id . '" class="ad-off backend-select-input wd300">';
|
||||
while (!$af->EOF) {
|
||||
$_sel6 .= '<option' . ($ad_file == $af->fields['db_key'] ? ' selected="selected"' : null) . ' value="' . $af->fields['db_key'] . '">' . $af->fields['db_name'] . ($af->fields['db_code'] != '' ? ' (' . $af->fields['db_code'] . ')' : ' (code)') . '</option>';
|
||||
$_sel6 .= '<option' . ($ad_file == $af->fields['db_key'] ? ' selected="selected"' : null) . ' value="' . $af->fields['db_key'] . '">' . $af->fields['db_name'] . (($af->fields['db_code'] != '') ? ' (' . $af->fields['db_code'] . ')' : ' (code)') . '</option>';
|
||||
$af->MoveNext();
|
||||
}
|
||||
$_sel6 .= '</select>';
|
||||
@@ -570,7 +570,7 @@ class VbeAdvertising
|
||||
if ($af->fields['db_key']) {
|
||||
$_sel6 = '<select name="fp_file_' . $int_id . '" class="ad-off backend-select-input wd300">';
|
||||
while (!$af->EOF) {
|
||||
$_sel6 .= '<option' . ($ad_file == $af->fields['db_key'] ? ' selected="selected"' : null) . ' value="' . $af->fields['db_key'] . '">' . $af->fields['db_name'] . ($af->fields['db_type'] == 'code' ? '' : ' (' . $af->fields['db_code'] . ')') . '</option>';
|
||||
$_sel6 .= '<option' . ($ad_file == $af->fields['db_key'] ? ' selected="selected"' : null) . ' value="' . $af->fields['db_key'] . '">' . $af->fields['db_name'] . (($af->fields['db_type'] == 'code') ? '' : ' (' . $af->fields['db_code'] . ')') . '</option>';
|
||||
$af->MoveNext();
|
||||
}
|
||||
$_sel6 .= '</select>';
|
||||
|
||||
@@ -496,7 +496,7 @@ class VbeDashboard
|
||||
}
|
||||
$k = substr($k, 1);
|
||||
$s = self::$dbc->singleFieldValue('db_' . $tbl . 'files', 'approved', 'file_key', $k);
|
||||
$et = $s == '' ? '<b>(deleted)</b>' : ($s == 0 ? '<b>' . self::$language['backend.files.text.req'] . '</b>' : null);
|
||||
$et = $s == '' ? '<b>(deleted)</b>' : (($s == 0) ? '<b>' . self::$language['backend.files.text.req'] . '</b>' : null);
|
||||
|
||||
break;
|
||||
case "payment_notification_be":
|
||||
|
||||
@@ -1679,7 +1679,7 @@ class VbeMembers
|
||||
case "fri":$i = 'icon-users';
|
||||
break;
|
||||
default:
|
||||
$i = $a == 'sign in' ? 'icon-enter' : ($a == 'sign out' ? 'icon-exit' : 'icon-list');
|
||||
$i = $a == 'sign in' ? 'icon-enter' : (($a == 'sign out') ? 'icon-exit' : 'icon-list');
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
218
f_core/f_classes/class.cdn.php
Normal file
218
f_core/f_classes/class.cdn.php
Normal file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
/**
|
||||
* EasyStream CDN Integration
|
||||
* Multi-CDN support: Cloudflare, AWS CloudFront, Bunny CDN
|
||||
* Version: 1.0
|
||||
*/
|
||||
|
||||
defined('_ISVALID') or header('Location: /error');
|
||||
|
||||
class VCDN {
|
||||
private static $db;
|
||||
private static $active_cdn = null;
|
||||
|
||||
public static function init() {
|
||||
self::$db = VDatabase::getInstance();
|
||||
self::loadActiveCDN();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load active CDN configuration
|
||||
*/
|
||||
private static function loadActiveCDN() {
|
||||
$sql = "SELECT * FROM db_cdn_config
|
||||
WHERE is_active = 1
|
||||
ORDER BY priority ASC
|
||||
LIMIT 1";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
if ($result && $result->RecordCount() > 0) {
|
||||
self::$active_cdn = $result->FetchRow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CDN URL for file
|
||||
* @param string $file_path Original file path
|
||||
* @param string $file_type Type (video, image, audio, etc.)
|
||||
* @return string CDN URL or original path
|
||||
*/
|
||||
public static function getCDNUrl($file_path, $file_type = 'video') {
|
||||
self::init();
|
||||
|
||||
if (!self::$active_cdn || empty(self::$active_cdn['base_url'])) {
|
||||
return $file_path; // Return original if no CDN
|
||||
}
|
||||
|
||||
$base_url = rtrim(self::$active_cdn['base_url'], '/');
|
||||
$file_path = ltrim($file_path, '/');
|
||||
|
||||
return "$base_url/$file_path";
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge cache for specific file
|
||||
* @param string $file_path File path to purge
|
||||
* @return bool Success
|
||||
*/
|
||||
public static function purgeCache($file_path) {
|
||||
self::init();
|
||||
|
||||
if (!self::$active_cdn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$provider = self::$active_cdn['provider'];
|
||||
|
||||
switch ($provider) {
|
||||
case 'cloudflare':
|
||||
return self::purgeCloudflare($file_path);
|
||||
|
||||
case 'bunny':
|
||||
return self::purgeBunny($file_path);
|
||||
|
||||
case 'aws':
|
||||
return self::purgeAWS($file_path);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge Cloudflare cache
|
||||
*/
|
||||
private static function purgeCloudflare($file_path) {
|
||||
$zone_id = self::$active_cdn['zone_id'];
|
||||
$api_key = self::$active_cdn['api_key'];
|
||||
|
||||
if (empty($zone_id) || empty($api_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$url = "https://api.cloudflare.com/client/v4/zones/$zone_id/purge_cache";
|
||||
|
||||
$cdn_url = self::getCDNUrl($file_path);
|
||||
|
||||
$data = json_encode(['files' => [$cdn_url]]);
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Authorization: Bearer ' . $api_key,
|
||||
'Content-Type: application/json',
|
||||
'Content-Length: ' . strlen($data)
|
||||
]);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
return $status_code == 200;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge Bunny CDN cache
|
||||
*/
|
||||
private static function purgeBunny($file_path) {
|
||||
$api_key = self::$active_cdn['api_key'];
|
||||
$zone_id = self::$active_cdn['zone_id']; // Pull zone ID
|
||||
|
||||
if (empty($api_key) || empty($zone_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cdn_url = self::getCDNUrl($file_path);
|
||||
|
||||
$url = "https://api.bunny.net/pullzone/$zone_id/purgeCache";
|
||||
|
||||
$data = json_encode(['url' => $cdn_url]);
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'AccessKey: ' . $api_key,
|
||||
'Content-Type: application/json'
|
||||
]);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
return $status_code == 200 || $status_code == 204;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge AWS CloudFront cache
|
||||
*/
|
||||
private static function purgeAWS($file_path) {
|
||||
// AWS CloudFront invalidation would require AWS SDK
|
||||
// For now, return false (implement if AWS SDK available)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track CDN statistics
|
||||
* @param string $file_key File key
|
||||
* @param int $bandwidth Bandwidth in MB
|
||||
* @param int $requests Number of requests
|
||||
*/
|
||||
public static function trackStats($file_key, $bandwidth, $requests) {
|
||||
self::init();
|
||||
|
||||
if (!self::$active_cdn) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file_key_safe = VDatabase::escape($file_key);
|
||||
$provider = VDatabase::escape(self::$active_cdn['provider']);
|
||||
$bandwidth = (float)$bandwidth;
|
||||
$requests = (int)$requests;
|
||||
$date = date('Y-m-d');
|
||||
|
||||
$sql = "INSERT INTO db_cdn_stats
|
||||
(file_key, cdn_provider, bandwidth_mb, requests, date)
|
||||
VALUES ('$file_key_safe', '$provider', $bandwidth, $requests, '$date')
|
||||
ON DUPLICATE KEY UPDATE
|
||||
bandwidth_mb = bandwidth_mb + $bandwidth,
|
||||
requests = requests + $requests";
|
||||
|
||||
self::$db->execute($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CDN statistics
|
||||
* @param string $file_key File key
|
||||
* @param string $date_from Start date
|
||||
* @param string $date_to End date
|
||||
* @return array Stats
|
||||
*/
|
||||
public static function getStats($file_key, $date_from, $date_to) {
|
||||
self::init();
|
||||
|
||||
$file_key_safe = VDatabase::escape($file_key);
|
||||
$date_from_safe = VDatabase::escape($date_from);
|
||||
$date_to_safe = VDatabase::escape($date_to);
|
||||
|
||||
$sql = "SELECT cdn_provider, SUM(bandwidth_mb) as total_bandwidth, SUM(requests) as total_requests
|
||||
FROM db_cdn_stats
|
||||
WHERE file_key = '$file_key_safe'
|
||||
AND date BETWEEN '$date_from_safe' AND '$date_to_safe'
|
||||
GROUP BY cdn_provider";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
$stats = [];
|
||||
|
||||
if ($result) {
|
||||
while ($row = $result->FetchRow()) {
|
||||
$stats[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $stats;
|
||||
}
|
||||
}
|
||||
@@ -71,15 +71,19 @@ class VDatabase
|
||||
{
|
||||
// Add your actual table names here
|
||||
$allowedTables = [
|
||||
'db_settings', 'db_conversion', 'db_videofiles', 'db_livefiles',
|
||||
'db_settings', 'db_conversion', 'db_videofiles', 'db_livefiles',
|
||||
'db_accountuser', 'db_trackactivity', 'db_imagefiles', 'db_audiofiles',
|
||||
'db_documentfiles', 'db_blogfiles', 'db_comments', 'db_responses',
|
||||
'db_playlists', 'db_subscriptions', 'db_categories', 'db_channels',
|
||||
'db_users', 'db_sessions', 'db_ip_tracking', 'db_banlist',
|
||||
'db_fingerprints', 'db_fingerprint_bans', 'db_email_log',
|
||||
'db_users', 'db_sessions', 'db_ip_tracking', 'db_banlist',
|
||||
'db_fingerprints', 'db_fingerprint_bans', 'db_email_log',
|
||||
'db_notifications', 'db_user_preferences', 'db_password_resets',
|
||||
'db_logs', 'db_shortfiles', 'db_memberships', 'db_tokens',
|
||||
'db_affiliates', 'db_advertising', 'db_servers', 'db_streaming'
|
||||
'db_affiliates', 'db_advertising', 'db_servers', 'db_streaming',
|
||||
// Template Builder tables
|
||||
'db_templatebuilder_templates', 'db_templatebuilder_components',
|
||||
'db_templatebuilder_assignments', 'db_templatebuilder_versions',
|
||||
'db_templatebuilder_user_prefs', 'db_notifications_count'
|
||||
];
|
||||
return in_array($table, $allowedTables);
|
||||
}
|
||||
@@ -453,4 +457,66 @@ class VDatabase
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize input for database queries
|
||||
* @param mixed $input Input to sanitize
|
||||
* @return string Sanitized input
|
||||
*/
|
||||
public static function sanitizeInput($input)
|
||||
{
|
||||
global $db;
|
||||
|
||||
if (is_null($input)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (is_array($input)) {
|
||||
return array_map([__CLASS__, 'sanitizeInput'], $input);
|
||||
}
|
||||
|
||||
// Remove any potential SQL injection characters
|
||||
$input = strip_tags($input);
|
||||
$input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
// Use ADOdb's qstr method if available
|
||||
if (isset($db) && method_exists($db, 'qstr')) {
|
||||
return substr($db->qstr($input), 1, -1); // Remove surrounding quotes
|
||||
}
|
||||
|
||||
// Fallback: basic escaping
|
||||
return addslashes($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build INSERT/UPDATE SET clause from associative array
|
||||
* @param array $data Associative array of field => value pairs
|
||||
* @return string SET clause for SQL query
|
||||
*/
|
||||
public static function build_insert_update($data)
|
||||
{
|
||||
if (!is_array($data) || empty($data)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$parts = [];
|
||||
foreach ($data as $field => $value) {
|
||||
// Validate field name
|
||||
if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $field)) {
|
||||
continue; // Skip invalid field names
|
||||
}
|
||||
|
||||
// Handle different value types
|
||||
if (is_null($value)) {
|
||||
$parts[] = "`{$field}` = NULL";
|
||||
} elseif (is_int($value) || is_float($value)) {
|
||||
$parts[] = "`{$field}` = " . $value;
|
||||
} else {
|
||||
$sanitized = self::sanitizeInput($value);
|
||||
$parts[] = "`{$field}` = '{$sanitized}'";
|
||||
}
|
||||
}
|
||||
|
||||
return implode(', ', $parts);
|
||||
}
|
||||
}
|
||||
|
||||
297
f_core/f_classes/class.email.queue.php
Normal file
297
f_core/f_classes/class.email.queue.php
Normal file
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
/**
|
||||
* EasyStream Email Queue System
|
||||
* SendGrid/AWS SES integration with queue processing
|
||||
* Version: 1.0
|
||||
*/
|
||||
|
||||
defined('_ISVALID') or header('Location: /error');
|
||||
|
||||
class VEmailQueue {
|
||||
private static $db;
|
||||
private static $sendgrid_api_key = null;
|
||||
private static $from_email = 'noreply@easystream.com';
|
||||
private static $from_name = 'EasyStream';
|
||||
|
||||
public static function init() {
|
||||
self::$db = VDatabase::getInstance();
|
||||
|
||||
// Load configuration
|
||||
$config = VGenerate::getConfig('email_config');
|
||||
if ($config) {
|
||||
self::$sendgrid_api_key = $config['sendgrid_api_key'] ?? null;
|
||||
self::$from_email = $config['from_email'] ?? self::$from_email;
|
||||
self::$from_name = $config['from_name'] ?? self::$from_name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue email for sending
|
||||
* @param int $usr_id User ID (optional)
|
||||
* @param string $to_email Recipient email
|
||||
* @param string $subject Email subject
|
||||
* @param string $body_html HTML body
|
||||
* @param array $options Additional options
|
||||
* @return int Email ID
|
||||
*/
|
||||
public static function queueEmail($usr_id, $to_email, $subject, $body_html, $options = []) {
|
||||
self::init();
|
||||
|
||||
$usr_id_val = $usr_id ? (int)$usr_id : 'NULL';
|
||||
$to_email_safe = VDatabase::escape($to_email);
|
||||
$to_name_safe = VDatabase::escape($options['to_name'] ?? '');
|
||||
$subject_safe = VDatabase::escape($subject);
|
||||
$body_html_safe = VDatabase::escape($body_html);
|
||||
$body_text_safe = VDatabase::escape($options['body_text'] ?? strip_tags($body_html));
|
||||
$template_safe = VDatabase::escape($options['template_name'] ?? '');
|
||||
$template_data = VDatabase::escape(json_encode($options['template_data'] ?? []));
|
||||
$priority = (int)($options['priority'] ?? 5);
|
||||
$send_at = isset($options['send_at']) ? "'" . VDatabase::escape($options['send_at']) . "'" : 'NULL';
|
||||
|
||||
$sql = "INSERT INTO db_email_queue
|
||||
(usr_id, to_email, to_name, subject, body_html, body_text, template_name, template_data, priority, send_at, status, created_at)
|
||||
VALUES ($usr_id_val, '$to_email_safe', '$to_name_safe', '$subject_safe', '$body_html_safe', '$body_text_safe', '$template_safe', '$template_data', $priority, $send_at, 'pending', NOW())";
|
||||
|
||||
if (self::$db->execute($sql)) {
|
||||
return self::$db->insert_id();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process email queue
|
||||
* @param int $limit Number of emails to process
|
||||
* @return array Results
|
||||
*/
|
||||
public static function processQueue($limit = 50) {
|
||||
self::init();
|
||||
|
||||
$limit = (int)$limit;
|
||||
|
||||
// Get pending emails
|
||||
$sql = "SELECT * FROM db_email_queue
|
||||
WHERE status = 'pending'
|
||||
AND (send_at IS NULL OR send_at <= NOW())
|
||||
ORDER BY priority ASC, created_at ASC
|
||||
LIMIT $limit";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
|
||||
if (!$result) {
|
||||
return ['sent' => 0, 'failed' => 0];
|
||||
}
|
||||
|
||||
$sent = 0;
|
||||
$failed = 0;
|
||||
|
||||
while ($row = $result->FetchRow()) {
|
||||
$email_id = (int)$row['email_id'];
|
||||
|
||||
// Mark as sending
|
||||
self::$db->execute("UPDATE db_email_queue SET status = 'sending' WHERE email_id = $email_id");
|
||||
|
||||
// Send email
|
||||
$success = self::sendEmail($row);
|
||||
|
||||
if ($success) {
|
||||
self::$db->execute("UPDATE db_email_queue SET status = 'sent', sent_at = NOW() WHERE email_id = $email_id");
|
||||
self::logEmail($email_id, $row, 'sent');
|
||||
$sent++;
|
||||
} else {
|
||||
$attempts = (int)$row['attempts'] + 1;
|
||||
$max_attempts = 3;
|
||||
|
||||
if ($attempts >= $max_attempts) {
|
||||
self::$db->execute("UPDATE db_email_queue SET status = 'failed', attempts = $attempts WHERE email_id = $email_id");
|
||||
self::logEmail($email_id, $row, 'failed');
|
||||
$failed++;
|
||||
} else {
|
||||
// Retry later
|
||||
self::$db->execute("UPDATE db_email_queue SET status = 'pending', attempts = $attempts WHERE email_id = $email_id");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ['sent' => $sent, 'failed' => $failed];
|
||||
}
|
||||
|
||||
/**
|
||||
* Send individual email
|
||||
* @param array $email_data Email data
|
||||
* @return bool Success
|
||||
*/
|
||||
private static function sendEmail($email_data) {
|
||||
if (self::$sendgrid_api_key) {
|
||||
return self::sendViaSendGrid($email_data);
|
||||
} else {
|
||||
return self::sendViaPHPMailer($email_data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send via SendGrid
|
||||
*/
|
||||
private static function sendViaSendGrid($email_data) {
|
||||
$url = 'https://api.sendgrid.com/v3/mail/send';
|
||||
|
||||
$data = [
|
||||
'personalizations' => [[
|
||||
'to' => [['email' => $email_data['to_email'], 'name' => $email_data['to_name']]]
|
||||
]],
|
||||
'from' => ['email' => self::$from_email, 'name' => self::$from_name],
|
||||
'subject' => $email_data['subject'],
|
||||
'content' => [
|
||||
['type' => 'text/html', 'value' => $email_data['body_html']],
|
||||
['type' => 'text/plain', 'value' => $email_data['body_text']]
|
||||
]
|
||||
];
|
||||
|
||||
$json = json_encode($data);
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Authorization: Bearer ' . self::$sendgrid_api_key,
|
||||
'Content-Type: application/json'
|
||||
]);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
return $status_code == 202 || $status_code == 200;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send via PHP mail (fallback)
|
||||
*/
|
||||
private static function sendViaPHPMailer($email_data) {
|
||||
$to = $email_data['to_email'];
|
||||
$subject = $email_data['subject'];
|
||||
$message = $email_data['body_html'];
|
||||
|
||||
$headers = "MIME-Version: 1.0\r\n";
|
||||
$headers .= "Content-type: text/html; charset=UTF-8\r\n";
|
||||
$headers .= "From: " . self::$from_name . " <" . self::$from_email . ">\r\n";
|
||||
|
||||
return mail($to, $subject, $message, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log email event
|
||||
*/
|
||||
private static function logEmail($email_id, $email_data, $status) {
|
||||
$usr_id = $email_data['usr_id'] ?? 'NULL';
|
||||
$to_email = VDatabase::escape($email_data['to_email']);
|
||||
$subject = VDatabase::escape($email_data['subject']);
|
||||
$status_safe = VDatabase::escape($status);
|
||||
|
||||
$sql = "INSERT INTO db_email_logs
|
||||
(email_id, usr_id, to_email, subject, status, created_at)
|
||||
VALUES ($email_id, $usr_id, '$to_email', '$subject', '$status_safe', NOW())";
|
||||
|
||||
self::$db->execute($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user email preferences
|
||||
* @param int $usr_id User ID
|
||||
* @return array Preferences
|
||||
*/
|
||||
public static function getPreferences($usr_id) {
|
||||
self::init();
|
||||
|
||||
$usr_id = (int)$usr_id;
|
||||
|
||||
$sql = "SELECT * FROM db_email_preferences WHERE usr_id = $usr_id";
|
||||
$result = self::$db->execute($sql);
|
||||
|
||||
if ($result && $result->RecordCount() > 0) {
|
||||
return $result->FetchRow();
|
||||
}
|
||||
|
||||
// Return defaults
|
||||
return [
|
||||
'digest_frequency' => 'weekly',
|
||||
'notify_comments' => 1,
|
||||
'notify_replies' => 1,
|
||||
'notify_likes' => 1,
|
||||
'notify_subscribers' => 1,
|
||||
'notify_uploads' => 1,
|
||||
'notify_live_streams' => 1,
|
||||
'notify_mentions' => 1,
|
||||
'notify_milestones' => 1,
|
||||
'marketing_emails' => 1
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update email preferences
|
||||
* @param int $usr_id User ID
|
||||
* @param array $preferences Preferences
|
||||
* @return bool Success
|
||||
*/
|
||||
public static function updatePreferences($usr_id, $preferences) {
|
||||
self::init();
|
||||
|
||||
$usr_id = (int)$usr_id;
|
||||
|
||||
// Build SET clause
|
||||
$sets = [];
|
||||
foreach ($preferences as $key => $value) {
|
||||
$key_safe = VDatabase::escape($key);
|
||||
$value_safe = VDatabase::escape($value);
|
||||
$sets[] = "$key_safe = '$value_safe'";
|
||||
}
|
||||
$setClause = implode(', ', $sets);
|
||||
|
||||
$sql = "INSERT INTO db_email_preferences (usr_id, $setClause, updated_at)
|
||||
VALUES ($usr_id, NOW())
|
||||
ON DUPLICATE KEY UPDATE $setClause, updated_at = NOW()";
|
||||
|
||||
return self::$db->execute($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send templated email
|
||||
* @param string $template_name Template name
|
||||
* @param int $usr_id User ID
|
||||
* @param string $to_email Email address
|
||||
* @param array $variables Template variables
|
||||
* @return int Email ID
|
||||
*/
|
||||
public static function sendTemplate($template_name, $usr_id, $to_email, $variables = []) {
|
||||
self::init();
|
||||
|
||||
// Get template
|
||||
$template_safe = VDatabase::escape($template_name);
|
||||
$sql = "SELECT * FROM db_email_templates WHERE name = '$template_safe' AND is_active = 1";
|
||||
$result = self::$db->execute($sql);
|
||||
|
||||
if (!$result || $result->RecordCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$template = $result->FetchRow();
|
||||
|
||||
// Replace variables
|
||||
$subject = $template['subject'];
|
||||
$body_html = $template['body_html'];
|
||||
$body_text = $template['body_text'];
|
||||
|
||||
foreach ($variables as $key => $value) {
|
||||
$subject = str_replace('{' . $key . '}', $value, $subject);
|
||||
$body_html = str_replace('{' . $key . '}', $value, $body_html);
|
||||
$body_text = str_replace('{' . $key . '}', $value, $body_text);
|
||||
}
|
||||
|
||||
return self::queueEmail($usr_id, $to_email, $subject, $body_html, [
|
||||
'body_text' => $body_text,
|
||||
'template_name' => $template_name,
|
||||
'template_data' => $variables
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ defined('_ISVALID') or header('Location: /error');
|
||||
class VForm
|
||||
{
|
||||
/* check for empty fields */
|
||||
public function checkEmptyFields($allowedFields, $requiredFields, $replace = '')
|
||||
public static function checkEmptyFields($allowedFields, $requiredFields, $replace = '')
|
||||
{
|
||||
global $language, $smarty, $cfg;
|
||||
|
||||
@@ -49,7 +49,7 @@ class VForm
|
||||
return $error_message;
|
||||
}
|
||||
/* clear tags */
|
||||
public function clearTag($tag, $url = '')
|
||||
public static function clearTag($tag, $url = '')
|
||||
{
|
||||
$rep = $url == '' ? " " : "-";
|
||||
$clear = array("~", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "+", "`", "=", "[", "]", "\\", "{", "}", "|", ";", "'", ".", ",", "/", ":", '"', "<", ">", "?", "_", "-", "\n", "\r", "\t");
|
||||
|
||||
@@ -18,7 +18,7 @@ defined('_ISVALID') or header('Location: /error');
|
||||
class VIPaccess
|
||||
{
|
||||
/* check IP range */
|
||||
public function banIPrange_db($ip)
|
||||
public static function banIPrange_db($ip)
|
||||
{
|
||||
global $db;
|
||||
|
||||
@@ -33,12 +33,12 @@ class VIPaccess
|
||||
}
|
||||
return $check;
|
||||
}
|
||||
public function banIPrange_single($ip, $range)
|
||||
public static function banIPrange_single($ip, $range)
|
||||
{
|
||||
return $check = (VIPrange::ip_in_range($ip, $range) == 1) ? 1 : 0;
|
||||
}
|
||||
/* section access based on ip lists */
|
||||
public function sectionAccess($backend_access_url)
|
||||
public static function sectionAccess($backend_access_url)
|
||||
{
|
||||
global $class_database, $class_filter, $cfg, $section;
|
||||
$u = $_SERVER['REQUEST_URI'];
|
||||
@@ -73,7 +73,7 @@ class VIPaccess
|
||||
$be_error = ($be_access == 0 and $_section == 'backend') ? die('<h1><b>Not Found</b></h1>The requested URL / was not found on this server.') : null;
|
||||
}
|
||||
/* check for allowed email domains */
|
||||
public function emailDomainCheck($mail = '')
|
||||
public static function emailDomainCheck($mail = '')
|
||||
{
|
||||
global $cfg;
|
||||
|
||||
@@ -91,7 +91,7 @@ class VIPaccess
|
||||
|
||||
}
|
||||
/* check remote ip in ip lists */
|
||||
public function checkIPlist($path)
|
||||
public static function checkIPlist($path)
|
||||
{
|
||||
global $class_filter, $cfg;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ defined('_ISVALID') or header('Location: /error');
|
||||
class VLogin
|
||||
{
|
||||
/* check subscription when logged in */
|
||||
public function checkSubscription()
|
||||
public static function checkSubscription()
|
||||
{
|
||||
global $cfg, $backend_access_url;
|
||||
|
||||
@@ -29,14 +29,14 @@ class VLogin
|
||||
}
|
||||
}
|
||||
/* update login activity */
|
||||
public function updateOnLogin($user_id)
|
||||
public static function updateOnLogin($user_id)
|
||||
{
|
||||
global $db, $class_filter, $cfg;
|
||||
$do_count = $cfg['frontend_signin_count'] == 1 ? ', usr_logins=usr_logins+1' : null;
|
||||
$db->execute(sprintf("UPDATE `db_accountuser` SET `usr_lastlogin`='%s', `usr_IP`='%s' " . $do_count . " WHERE `usr_id`='%s' LIMIT 1;", date("Y-m-d H:i:s"), $class_filter->clr_str($_SERVER[REM_ADDR]), intval($user_id)));
|
||||
}
|
||||
/* log in */
|
||||
public function loginAttempt($section, $username, $password, $remember = '')
|
||||
public static function loginAttempt($section, $username, $password, $remember = '')
|
||||
{
|
||||
global $db, $class_database, $cfg, $language, $class_filter;
|
||||
$username = $class_filter->clr_str($username);
|
||||
@@ -126,7 +126,7 @@ class VLogin
|
||||
|
||||
}
|
||||
/* log out */
|
||||
public function logoutAttempt($section, $redirect = 1)
|
||||
public static function logoutAttempt($section, $redirect = 1)
|
||||
{
|
||||
require 'f_core/config.backend.php';
|
||||
global $class_database, $class_redirect, $cfg, $language;
|
||||
@@ -188,7 +188,7 @@ class VLogin
|
||||
|
||||
}
|
||||
/* logged in redirect */
|
||||
public function isLoggedIn($section = 'fe')
|
||||
public static function isLoggedIn($section = 'fe')
|
||||
{
|
||||
require 'f_core/config.backend.php';
|
||||
global $class_redirect, $cfg;
|
||||
@@ -200,7 +200,7 @@ class VLogin
|
||||
}
|
||||
}
|
||||
/* check if logged in on frontend */
|
||||
public function checkFrontend($next = '')
|
||||
public static function checkFrontend($next = '')
|
||||
{
|
||||
global $cfg, $class_redirect;
|
||||
|
||||
@@ -210,7 +210,7 @@ class VLogin
|
||||
}
|
||||
}
|
||||
/* check if logged in on backend */
|
||||
public function checkBackend($next = '')
|
||||
public static function checkBackend($next = '')
|
||||
{
|
||||
require 'f_core/config.backend.php';
|
||||
global $class_database, $class_redirect, $cfg;
|
||||
|
||||
@@ -18,7 +18,7 @@ defined('_ISVALID') or header('Location: /error');
|
||||
class VLoginRemember extends VLogin
|
||||
{
|
||||
/* check if login remembered */
|
||||
public function checkLogin($section)
|
||||
public static function checkLogin($section)
|
||||
{
|
||||
global $db, $class_filter, $cfg;
|
||||
|
||||
@@ -76,7 +76,7 @@ class VLoginRemember extends VLogin
|
||||
}
|
||||
}
|
||||
/* set remembered login */
|
||||
public function setLogin($section, $username, $password)
|
||||
public static function setLogin($section, $username, $password)
|
||||
{
|
||||
$http_user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sha1($_SERVER['HTTP_USER_AGENT']) : null;
|
||||
$remote_addr = isset($_SERVER[REM_ADDR]) && ip2long($_SERVER[REM_ADDR]) ? ip2long($_SERVER[REM_ADDR]) : null;
|
||||
@@ -86,7 +86,7 @@ class VLoginRemember extends VLogin
|
||||
setcookie('l', $cookie, SET_COOKIE_OPTIONS);
|
||||
}
|
||||
/* clear remembered login */
|
||||
public function clearLogin($section)
|
||||
public static function clearLogin($section)
|
||||
{
|
||||
setcookie('l', '', DEL_COOKIE_OPTIONS);
|
||||
}
|
||||
|
||||
318
f_core/f_classes/class.moderation.enhanced.php
Normal file
318
f_core/f_classes/class.moderation.enhanced.php
Normal file
@@ -0,0 +1,318 @@
|
||||
<?php
|
||||
/**
|
||||
* Easy Stream Enhanced Moderation System
|
||||
* Advanced moderation with AI, rules, appeals
|
||||
* Version: 1.0
|
||||
*/
|
||||
|
||||
defined('_ISVALID') or header('Location: /error');
|
||||
|
||||
class VModerationEnhanced {
|
||||
private static $db;
|
||||
|
||||
public static function init() {
|
||||
self::$db = VDatabase::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit content for moderation
|
||||
* @param string $target_type Type (video, comment, user, post)
|
||||
* @param string $target_id Target ID
|
||||
* @param int $reporter_id Reporter user ID
|
||||
* @param string $reason Reason for report
|
||||
* @param string $priority Priority level
|
||||
* @return int Queue ID
|
||||
*/
|
||||
public static function submitReport($target_type, $target_id, $reporter_id, $reason, $priority = 'medium') {
|
||||
self::init();
|
||||
|
||||
$target_type = VDatabase::escape($target_type);
|
||||
$target_id = VDatabase::escape($target_id);
|
||||
$reporter_id = (int)$reporter_id;
|
||||
$reason = VDatabase::escape($reason);
|
||||
$priority = VDatabase::escape($priority);
|
||||
|
||||
$sql = "INSERT INTO db_moderation_queue
|
||||
(target_type, target_id, reporter_id, reason, priority, status, created_at)
|
||||
VALUES ('$target_type', '$target_id', $reporter_id, '$reason', '$priority', 'pending', NOW())";
|
||||
|
||||
if (self::$db->execute($sql)) {
|
||||
return self::$db->insert_id();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get moderation queue
|
||||
* @param string $status Filter by status
|
||||
* @param int $limit Number of items
|
||||
* @return array Queue items
|
||||
*/
|
||||
public static function getQueue($status = 'pending', $limit = 50) {
|
||||
self::init();
|
||||
|
||||
$status = VDatabase::escape($status);
|
||||
$limit = (int)$limit;
|
||||
|
||||
$sql = "SELECT mq.*, au.usr_user as reporter_username
|
||||
FROM db_moderation_queue mq
|
||||
LEFT JOIN db_accountuser au ON mq.reporter_id = au.usr_id
|
||||
WHERE mq.status = '$status'
|
||||
ORDER BY
|
||||
CASE priority
|
||||
WHEN 'urgent' THEN 1
|
||||
WHEN 'high' THEN 2
|
||||
WHEN 'medium' THEN 3
|
||||
ELSE 4
|
||||
END,
|
||||
mq.created_at ASC
|
||||
LIMIT $limit";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
$queue = [];
|
||||
|
||||
if ($result) {
|
||||
while ($row = $result->FetchRow()) {
|
||||
$queue[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take moderation action
|
||||
* @param int $queue_id Queue item ID
|
||||
* @param int $moderator_id Moderator user ID
|
||||
* @param string $action Action taken
|
||||
* @param string $reason Reason for action
|
||||
* @return bool Success
|
||||
*/
|
||||
public static function takeAction($queue_id, $moderator_id, $action, $reason) {
|
||||
self::init();
|
||||
|
||||
$queue_id = (int)$queue_id;
|
||||
$moderator_id = (int)$moderator_id;
|
||||
$action = VDatabase::escape($action);
|
||||
$reason = VDatabase::escape($reason);
|
||||
|
||||
// Get queue item
|
||||
$sql = "SELECT * FROM db_moderation_queue WHERE queue_id = $queue_id";
|
||||
$result = self::$db->execute($sql);
|
||||
|
||||
if (!$result || $result->RecordCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$item = $result->FetchRow();
|
||||
|
||||
// Record action
|
||||
$sql = "INSERT INTO db_moderation_actions
|
||||
(target_type, target_id, moderator_id, action, reason, is_automated, created_at)
|
||||
VALUES ('{$item['target_type']}', '{$item['target_id']}', $moderator_id, '$action', '$reason', 0, NOW())";
|
||||
|
||||
if (!self::$db->execute($sql)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$action_id = self::$db->insert_id();
|
||||
|
||||
// Update queue
|
||||
$sql = "UPDATE db_moderation_queue
|
||||
SET status = 'resolved',
|
||||
resolved_by = $moderator_id,
|
||||
resolution = '$reason',
|
||||
resolved_at = NOW()
|
||||
WHERE queue_id = $queue_id";
|
||||
|
||||
self::$db->execute($sql);
|
||||
|
||||
// Apply action based on type
|
||||
self::applyAction($item['target_type'], $item['target_id'], $action);
|
||||
|
||||
// Add strike if needed
|
||||
if ($action == 'removed' || $action == 'banned') {
|
||||
self::addStrike($item['target_id'], $action_id, $action, $reason);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply moderation action to target
|
||||
*/
|
||||
private static function applyAction($target_type, $target_id, $action) {
|
||||
switch ($action) {
|
||||
case 'removed':
|
||||
if ($target_type == 'video') {
|
||||
$target_id_safe = VDatabase::escape($target_id);
|
||||
self::$db->execute("UPDATE db_videofiles SET privacy = 'removed', approved = 0 WHERE file_key = '$target_id_safe'");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'age_restricted':
|
||||
if ($target_type == 'video') {
|
||||
$target_id_safe = VDatabase::escape($target_id);
|
||||
self::$db->execute("UPDATE db_videofiles SET file_adult = 1 WHERE file_key = '$target_id_safe'");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'banned':
|
||||
if ($target_type == 'user') {
|
||||
$usr_id = (int)$target_id;
|
||||
self::$db->execute("UPDATE db_accountuser SET usr_status = 'suspended' WHERE usr_id = $usr_id");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add strike to user
|
||||
*/
|
||||
private static function addStrike($usr_id, $action_id, $type, $reason) {
|
||||
$usr_id = (int)$usr_id;
|
||||
$action_id = (int)$action_id;
|
||||
$type_safe = VDatabase::escape($type);
|
||||
$reason_safe = VDatabase::escape($reason);
|
||||
|
||||
// Determine strike type based on severity
|
||||
$strike_type = match($type) {
|
||||
'removed' => 'strike',
|
||||
'banned' => 'ban',
|
||||
default => 'warning'
|
||||
};
|
||||
|
||||
$sql = "INSERT INTO db_user_strikes
|
||||
(usr_id, action_id, type, reason, is_active, created_at)
|
||||
VALUES ($usr_id, $action_id, '$strike_type', '$reason_safe', 1, NOW())";
|
||||
|
||||
return self::$db->execute($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user strikes
|
||||
*/
|
||||
public static function getUserStrikes($usr_id) {
|
||||
self::init();
|
||||
$usr_id = (int)$usr_id;
|
||||
|
||||
$sql = "SELECT * FROM db_user_strikes
|
||||
WHERE usr_id = $usr_id AND is_active = 1
|
||||
ORDER BY created_at DESC";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
$strikes = [];
|
||||
|
||||
if ($result) {
|
||||
while ($row = $result->FetchRow()) {
|
||||
$strikes[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $strikes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit appeal
|
||||
*/
|
||||
public static function submitAppeal($action_id, $usr_id, $reason, $evidence = []) {
|
||||
self::init();
|
||||
|
||||
$action_id = (int)$action_id;
|
||||
$usr_id = (int)$usr_id;
|
||||
$reason_safe = VDatabase::escape($reason);
|
||||
$evidence_json = VDatabase::escape(json_encode($evidence));
|
||||
|
||||
$sql = "INSERT INTO db_moderation_appeals
|
||||
(action_id, usr_id, reason, evidence, status, created_at)
|
||||
VALUES ($action_id, $usr_id, '$reason_safe', '$evidence_json', 'pending', NOW())";
|
||||
|
||||
if (self::$db->execute($sql)) {
|
||||
// Mark action as appealed
|
||||
self::$db->execute("UPDATE db_moderation_actions SET is_appealed = 1 WHERE action_id = $action_id");
|
||||
return self::$db->insert_id();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pending appeals
|
||||
*/
|
||||
public static function getPendingAppeals($limit = 50) {
|
||||
self::init();
|
||||
$limit = (int)$limit;
|
||||
|
||||
$sql = "SELECT ma.*, au.usr_user, au.usr_email
|
||||
FROM db_moderation_appeals ma
|
||||
JOIN db_accountuser au ON ma.usr_id = au.usr_id
|
||||
WHERE ma.status = 'pending'
|
||||
ORDER BY ma.created_at ASC
|
||||
LIMIT $limit";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
$appeals = [];
|
||||
|
||||
if ($result) {
|
||||
while ($row = $result->FetchRow()) {
|
||||
$row['evidence'] = json_decode($row['evidence'], true);
|
||||
$appeals[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $appeals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Review appeal
|
||||
*/
|
||||
public static function reviewAppeal($appeal_id, $reviewer_id, $status, $notes) {
|
||||
self::init();
|
||||
|
||||
$appeal_id = (int)$appeal_id;
|
||||
$reviewer_id = (int)$reviewer_id;
|
||||
$status_safe = VDatabase::escape($status);
|
||||
$notes_safe = VDatabase::escape($notes);
|
||||
|
||||
$sql = "UPDATE db_moderation_appeals
|
||||
SET status = '$status_safe',
|
||||
reviewed_by = $reviewer_id,
|
||||
review_notes = '$notes_safe',
|
||||
reviewed_at = NOW()
|
||||
WHERE appeal_id = $appeal_id";
|
||||
|
||||
if (!self::$db->execute($sql)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If approved, restore content
|
||||
if ($status == 'approved') {
|
||||
$sql = "SELECT action_id FROM db_moderation_appeals WHERE appeal_id = $appeal_id";
|
||||
$result = self::$db->execute($sql);
|
||||
if ($result) {
|
||||
$row = $result->FetchRow();
|
||||
$action_id = (int)$row['action_id'];
|
||||
|
||||
// Get original action
|
||||
$sql = "SELECT * FROM db_moderation_actions WHERE action_id = $action_id";
|
||||
$result = self::$db->execute($sql);
|
||||
if ($result) {
|
||||
$action = $result->FetchRow();
|
||||
self::restoreContent($action['target_type'], $action['target_id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore content after successful appeal
|
||||
*/
|
||||
private static function restoreContent($target_type, $target_id) {
|
||||
if ($target_type == 'video') {
|
||||
$target_id_safe = VDatabase::escape($target_id);
|
||||
self::$db->execute("UPDATE db_videofiles SET privacy = 'public', approved = 1 WHERE file_key = '$target_id_safe'");
|
||||
}
|
||||
}
|
||||
}
|
||||
305
f_core/f_classes/class.monetization.php
Normal file
305
f_core/f_classes/class.monetization.php
Normal file
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
/**
|
||||
* EasyStream Monetization System
|
||||
* Memberships, Super Chat, Ads, Revenue Sharing
|
||||
* Stripe & PayPal Integration
|
||||
* Version: 1.0
|
||||
*/
|
||||
|
||||
defined('_ISVALID') or header('Location: /error');
|
||||
|
||||
class VMonetization {
|
||||
private static $db;
|
||||
private static $stripe_secret_key = null;
|
||||
private static $stripe_publishable_key = null;
|
||||
|
||||
public static function init() {
|
||||
self::$db = VDatabase::getInstance();
|
||||
|
||||
// Load Stripe configuration
|
||||
$config = VGenerate::getConfig('stripe_config');
|
||||
if ($config) {
|
||||
self::$stripe_secret_key = $config['secret_key'] ?? null;
|
||||
self::$stripe_publishable_key = $config['publishable_key'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create membership tier
|
||||
* @param int $usr_id Channel owner ID
|
||||
* @param string $name Tier name
|
||||
* @param float $price_monthly Monthly price
|
||||
* @param array $perks List of perks
|
||||
* @return int Tier ID
|
||||
*/
|
||||
public static function createMembershipTier($usr_id, $name, $price_monthly, $perks = []) {
|
||||
self::init();
|
||||
|
||||
$usr_id = (int)$usr_id;
|
||||
$name_safe = VDatabase::escape($name);
|
||||
$price = (float)$price_monthly;
|
||||
$perks_json = VDatabase::escape(json_encode($perks));
|
||||
|
||||
$sql = "INSERT INTO db_membership_tiers
|
||||
(usr_id, name, price_monthly, currency, perks, is_active, created_at)
|
||||
VALUES ($usr_id, '$name_safe', $price, 'USD', '$perks_json', 1, NOW())";
|
||||
|
||||
if (self::$db->execute($sql)) {
|
||||
return self::$db->insert_id();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to membership
|
||||
* @param int $tier_id Tier ID
|
||||
* @param int $subscriber_id Subscriber user ID
|
||||
* @param string $payment_method Payment method
|
||||
* @return int Membership ID
|
||||
*/
|
||||
public static function subscribeMembership($tier_id, $subscriber_id, $payment_method = 'stripe') {
|
||||
self::init();
|
||||
|
||||
$tier_id = (int)$tier_id;
|
||||
$subscriber_id = (int)$subscriber_id;
|
||||
|
||||
// Get tier details
|
||||
$sql = "SELECT * FROM db_membership_tiers WHERE tier_id = $tier_id AND is_active = 1";
|
||||
$result = self::$db->execute($sql);
|
||||
|
||||
if (!$result || $result->RecordCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tier = $result->FetchRow();
|
||||
$channel_owner_id = (int)$tier['usr_id'];
|
||||
$price = (float)$tier['price_monthly'];
|
||||
|
||||
// Create Stripe subscription (if using Stripe)
|
||||
$stripe_sub_id = null;
|
||||
if ($payment_method == 'stripe' && self::$stripe_secret_key) {
|
||||
$stripe_sub_id = self::createStripeSubscription($subscriber_id, $price);
|
||||
}
|
||||
|
||||
$stripe_sub_safe = $stripe_sub_id ? "'" . VDatabase::escape($stripe_sub_id) . "'" : 'NULL';
|
||||
$payment_safe = VDatabase::escape($payment_method);
|
||||
|
||||
$sql = "INSERT INTO db_memberships
|
||||
(tier_id, subscriber_id, channel_owner_id, status, started_at, expires_at, payment_method, stripe_subscription_id)
|
||||
VALUES ($tier_id, $subscriber_id, $channel_owner_id, 'active', NOW(), DATE_ADD(NOW(), INTERVAL 1 MONTH), '$payment_safe', $stripe_sub_safe)";
|
||||
|
||||
if (self::$db->execute($sql)) {
|
||||
// Record transaction
|
||||
self::recordTransaction($subscriber_id, 'membership', $price, "Membership: {$tier['name']}");
|
||||
return self::$db->insert_id();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Stripe subscription (simplified)
|
||||
*/
|
||||
private static function createStripeSubscription($usr_id, $amount) {
|
||||
// Placeholder for Stripe API integration
|
||||
// In production, use Stripe PHP SDK
|
||||
return 'sub_' . bin2hex(random_bytes(16));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Super Chat
|
||||
* @param int $usr_id Sender user ID
|
||||
* @param int $recipient_id Recipient user ID
|
||||
* @param string $file_key Associated file (optional)
|
||||
* @param float $amount Amount in USD
|
||||
* @param string $message Message
|
||||
* @return int Super chat ID
|
||||
*/
|
||||
public static function sendSuperChat($usr_id, $recipient_id, $file_key, $amount, $message = '') {
|
||||
self::init();
|
||||
|
||||
$usr_id = (int)$usr_id;
|
||||
$recipient_id = (int)$recipient_id;
|
||||
$file_key_safe = $file_key ? "'" . VDatabase::escape($file_key) . "'" : 'NULL';
|
||||
$amount = (float)$amount;
|
||||
$message_safe = VDatabase::escape($message);
|
||||
|
||||
// Process payment (Stripe integration here)
|
||||
$payment_id = self::processStripePayment($usr_id, $amount);
|
||||
|
||||
if (!$payment_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$payment_safe = VDatabase::escape($payment_id);
|
||||
|
||||
$sql = "INSERT INTO db_super_chats
|
||||
(usr_id, recipient_id, file_key, amount, currency, message, type, payment_status, stripe_payment_id, created_at)
|
||||
VALUES ($usr_id, $recipient_id, $file_key_safe, $amount, 'USD', '$message_safe', 'super_chat', 'completed', '$payment_safe', NOW())";
|
||||
|
||||
if (self::$db->execute($sql)) {
|
||||
// Record transaction
|
||||
self::recordTransaction($recipient_id, 'super_chat', $amount, "Super Chat from user $usr_id", $payment_id);
|
||||
return self::$db->insert_id();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Stripe payment (simplified)
|
||||
*/
|
||||
private static function processStripePayment($usr_id, $amount) {
|
||||
// Placeholder for Stripe payment processing
|
||||
// In production, use Stripe PHP SDK to create PaymentIntent
|
||||
return 'pi_' . bin2hex(random_bytes(16));
|
||||
}
|
||||
|
||||
/**
|
||||
* Record transaction
|
||||
*/
|
||||
private static function recordTransaction($usr_id, $type, $amount, $description, $reference_id = null) {
|
||||
$usr_id = (int)$usr_id;
|
||||
$type_safe = VDatabase::escape($type);
|
||||
$amount = (float)$amount;
|
||||
$desc_safe = VDatabase::escape($description);
|
||||
$ref_safe = $reference_id ? "'" . VDatabase::escape($reference_id) . "'" : 'NULL';
|
||||
|
||||
$sql = "INSERT INTO db_transactions
|
||||
(usr_id, type, amount, currency, description, reference_id, status, created_at)
|
||||
VALUES ($usr_id, '$type_safe', $amount, 'USD', '$desc_safe', $ref_safe, 'completed', NOW())";
|
||||
|
||||
return self::$db->execute($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate revenue share
|
||||
* @param int $usr_id User ID
|
||||
* @param string $period_start Start date
|
||||
* @param string $period_end End date
|
||||
* @return array Revenue breakdown
|
||||
*/
|
||||
public static function calculateRevenue($usr_id, $period_start, $period_end) {
|
||||
self::init();
|
||||
|
||||
$usr_id = (int)$usr_id;
|
||||
$start_safe = VDatabase::escape($period_start);
|
||||
$end_safe = VDatabase::escape($period_end);
|
||||
|
||||
// Get all revenue streams
|
||||
$sql = "SELECT type, SUM(amount) as total
|
||||
FROM db_transactions
|
||||
WHERE usr_id = $usr_id
|
||||
AND status = 'completed'
|
||||
AND created_at BETWEEN '$start_safe' AND '$end_safe'
|
||||
GROUP BY type";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
|
||||
$revenue = [
|
||||
'ad_revenue' => 0,
|
||||
'membership_revenue' => 0,
|
||||
'super_chat_revenue' => 0,
|
||||
'total_revenue' => 0
|
||||
];
|
||||
|
||||
if ($result) {
|
||||
while ($row = $result->FetchRow()) {
|
||||
$amount = (float)$row['total'];
|
||||
$revenue['total_revenue'] += $amount;
|
||||
|
||||
if ($row['type'] == 'membership') {
|
||||
$revenue['membership_revenue'] = $amount;
|
||||
} elseif ($row['type'] == 'super_chat' || $row['type'] == 'super_thanks') {
|
||||
$revenue['super_chat_revenue'] += $amount;
|
||||
} elseif ($row['type'] == 'ad_payout') {
|
||||
$revenue['ad_revenue'] = $amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate platform fee (e.g., 30%)
|
||||
$platform_fee = $revenue['total_revenue'] * 0.30;
|
||||
$payout_amount = $revenue['total_revenue'] - $platform_fee;
|
||||
|
||||
$revenue['platform_fee'] = $platform_fee;
|
||||
$revenue['payout_amount'] = $payout_amount;
|
||||
|
||||
return $revenue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create revenue share record
|
||||
* @param int $usr_id User ID
|
||||
* @param string $period_start Start date
|
||||
* @param string $period_end End date
|
||||
* @return int Share ID
|
||||
*/
|
||||
public static function createRevenueShare($usr_id, $period_start, $period_end) {
|
||||
$revenue = self::calculateRevenue($usr_id, $period_start, $period_end);
|
||||
|
||||
$usr_id = (int)$usr_id;
|
||||
$start_safe = VDatabase::escape($period_start);
|
||||
$end_safe = VDatabase::escape($period_end);
|
||||
|
||||
$sql = "INSERT INTO db_revenue_shares
|
||||
(usr_id, period_start, period_end, ad_revenue, membership_revenue, super_chat_revenue, total_revenue, platform_fee, payout_amount, payout_status, created_at)
|
||||
VALUES ($usr_id, '$start_safe', '$end_safe', {$revenue['ad_revenue']}, {$revenue['membership_revenue']}, {$revenue['super_chat_revenue']},
|
||||
{$revenue['total_revenue']}, {$revenue['platform_fee']}, {$revenue['payout_amount']}, 'pending', NOW())";
|
||||
|
||||
if (self::$db->execute($sql)) {
|
||||
return self::$db->insert_id();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user memberships
|
||||
* @param int $usr_id User ID
|
||||
* @return array Active memberships
|
||||
*/
|
||||
public static function getUserMemberships($usr_id) {
|
||||
self::init();
|
||||
|
||||
$usr_id = (int)$usr_id;
|
||||
|
||||
$sql = "SELECT m.*, t.name as tier_name, t.price_monthly, t.perks, u.usr_user as channel_name
|
||||
FROM db_memberships m
|
||||
JOIN db_membership_tiers t ON m.tier_id = t.tier_id
|
||||
JOIN db_accountuser u ON m.channel_owner_id = u.usr_id
|
||||
WHERE m.subscriber_id = $usr_id
|
||||
AND m.status = 'active'
|
||||
ORDER BY m.started_at DESC";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
$memberships = [];
|
||||
|
||||
if ($result) {
|
||||
while ($row = $result->FetchRow()) {
|
||||
$row['perks'] = json_decode($row['perks'], true);
|
||||
$memberships[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $memberships;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel membership
|
||||
* @param int $membership_id Membership ID
|
||||
* @return bool Success
|
||||
*/
|
||||
public static function cancelMembership($membership_id) {
|
||||
self::init();
|
||||
|
||||
$membership_id = (int)$membership_id;
|
||||
|
||||
$sql = "UPDATE db_memberships
|
||||
SET status = 'cancelled', cancelled_at = NOW()
|
||||
WHERE membership_id = $membership_id";
|
||||
|
||||
return self::$db->execute($sql);
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ class VNotify
|
||||
$this->msg_body = '';
|
||||
$this->msg_alt = $language['notif.mail.alt.body'];
|
||||
}
|
||||
public function queInit($type, $clear_arr, $db_id = '', $na = '')
|
||||
public static function queInit($type, $clear_arr, $db_id = '', $na = '')
|
||||
{
|
||||
global $cfg, $class_database, $class_filter, $language;
|
||||
|
||||
@@ -170,12 +170,12 @@ class VNotify
|
||||
|
||||
}
|
||||
/* str_replace associative arrays */
|
||||
public function strReplaceAssoc(array $replace, $subject)
|
||||
public static function strReplaceAssoc(array $replace, $subject)
|
||||
{
|
||||
return str_replace(array_keys($replace), array_values($replace), $subject);
|
||||
}
|
||||
/* adding to que and logging */
|
||||
public function Mailer($mail_type, $mail_key)
|
||||
public static function Mailer($mail_type, $mail_key)
|
||||
{
|
||||
global $db, $language, $class_database, $cfg, $class_filter;
|
||||
|
||||
@@ -217,7 +217,7 @@ class VNotify
|
||||
return ($dname != '' ? $dname : ($ch_title != '' ? $ch_title : $username));
|
||||
}
|
||||
/* mailing */
|
||||
public function Mail($section, $type, $_replace = '', $user_notification = '')
|
||||
public static function Mail($section, $type, $_replace = '', $user_notification = '')
|
||||
{
|
||||
global $cfg, $class_database, $class_filter, $language, $smarty;
|
||||
require 'class_phpmailer/vendor/autoload.php';
|
||||
@@ -1210,7 +1210,7 @@ class VNotify
|
||||
}
|
||||
}
|
||||
|
||||
public function showNotice($type, $msg, $div_id = 'x_err')
|
||||
public static function showNotice($type, $msg, $div_id = 'x_err')
|
||||
{
|
||||
global $language;
|
||||
|
||||
|
||||
206
f_core/f_classes/class.oauth.php
Normal file
206
f_core/f_classes/class.oauth.php
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
/**
|
||||
* EasyStream OAuth 2.0 Server
|
||||
* OAuth 2.0 authentication for API
|
||||
* Version: 1.0
|
||||
*/
|
||||
|
||||
defined('_ISVALID') or header('Location: /error');
|
||||
|
||||
class VOAuth {
|
||||
private static $db;
|
||||
|
||||
public static function init() {
|
||||
self::$db = VDatabase::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate authorization code
|
||||
* @param int $usr_id User ID
|
||||
* @param string $client_id Client ID
|
||||
* @param array $scopes Requested scopes
|
||||
* @param string $redirect_uri Redirect URI
|
||||
* @return string Authorization code
|
||||
*/
|
||||
public static function generateAuthCode($usr_id, $client_id, $scopes, $redirect_uri) {
|
||||
self::init();
|
||||
|
||||
$code = bin2hex(random_bytes(32));
|
||||
$usr_id = (int)$usr_id;
|
||||
$client_id_safe = VDatabase::escape($client_id);
|
||||
$scopes_json = VDatabase::escape(json_encode($scopes));
|
||||
$redirect_safe = VDatabase::escape($redirect_uri);
|
||||
|
||||
$sql = "INSERT INTO db_oauth_codes
|
||||
(usr_id, client_id, code, scopes, redirect_uri, expires_at, created_at)
|
||||
VALUES ($usr_id, '$client_id_safe', '$code', '$scopes_json', '$redirect_safe', DATE_ADD(NOW(), INTERVAL 10 MINUTE), NOW())";
|
||||
|
||||
if (self::$db->execute($sql)) {
|
||||
return $code;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchange authorization code for access token
|
||||
* @param string $code Authorization code
|
||||
* @param string $client_id Client ID
|
||||
* @param string $client_secret Client secret
|
||||
* @return array|false Token data or false
|
||||
*/
|
||||
public static function exchangeCode($code, $client_id, $client_secret) {
|
||||
self::init();
|
||||
|
||||
$code_safe = VDatabase::escape($code);
|
||||
$client_id_safe = VDatabase::escape($client_id);
|
||||
|
||||
// Validate code
|
||||
$sql = "SELECT * FROM db_oauth_codes
|
||||
WHERE code = '$code_safe'
|
||||
AND client_id = '$client_id_safe'
|
||||
AND expires_at > NOW()
|
||||
AND is_used = 0";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
|
||||
if (!$result || $result->RecordCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$code_data = $result->FetchRow();
|
||||
|
||||
// Mark code as used
|
||||
$code_id = (int)$code_data['code_id'];
|
||||
self::$db->execute("UPDATE db_oauth_codes SET is_used = 1 WHERE code_id = $code_id");
|
||||
|
||||
// Generate tokens
|
||||
$access_token = bin2hex(random_bytes(32));
|
||||
$refresh_token = bin2hex(random_bytes(32));
|
||||
|
||||
$usr_id = (int)$code_data['usr_id'];
|
||||
$scopes = $code_data['scopes'];
|
||||
|
||||
$sql = "INSERT INTO db_oauth_tokens
|
||||
(usr_id, client_id, access_token, refresh_token, token_type, scopes, expires_at, refresh_expires_at, created_at)
|
||||
VALUES ($usr_id, '$client_id_safe', '$access_token', '$refresh_token', 'Bearer', '$scopes',
|
||||
DATE_ADD(NOW(), INTERVAL 1 HOUR),
|
||||
DATE_ADD(NOW(), INTERVAL 30 DAY),
|
||||
NOW())";
|
||||
|
||||
if (self::$db->execute($sql)) {
|
||||
return [
|
||||
'access_token' => $access_token,
|
||||
'refresh_token' => $refresh_token,
|
||||
'token_type' => 'Bearer',
|
||||
'expires_in' => 3600,
|
||||
'scope' => implode(' ', json_decode($scopes, true))
|
||||
];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh access token
|
||||
* @param string $refresh_token Refresh token
|
||||
* @param string $client_id Client ID
|
||||
* @return array|false New token data or false
|
||||
*/
|
||||
public static function refreshToken($refresh_token, $client_id) {
|
||||
self::init();
|
||||
|
||||
$refresh_safe = VDatabase::escape($refresh_token);
|
||||
$client_id_safe = VDatabase::escape($client_id);
|
||||
|
||||
// Validate refresh token
|
||||
$sql = "SELECT * FROM db_oauth_tokens
|
||||
WHERE refresh_token = '$refresh_safe'
|
||||
AND client_id = '$client_id_safe'
|
||||
AND refresh_expires_at > NOW()
|
||||
AND is_revoked = 0";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
|
||||
if (!$result || $result->RecordCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$token_data = $result->FetchRow();
|
||||
|
||||
// Revoke old token
|
||||
$token_id = (int)$token_data['token_id'];
|
||||
self::$db->execute("UPDATE db_oauth_tokens SET is_revoked = 1 WHERE token_id = $token_id");
|
||||
|
||||
// Generate new tokens
|
||||
$new_access_token = bin2hex(random_bytes(32));
|
||||
$new_refresh_token = bin2hex(random_bytes(32));
|
||||
|
||||
$usr_id = (int)$token_data['usr_id'];
|
||||
$scopes = $token_data['scopes'];
|
||||
|
||||
$sql = "INSERT INTO db_oauth_tokens
|
||||
(usr_id, client_id, access_token, refresh_token, token_type, scopes, expires_at, refresh_expires_at, created_at)
|
||||
VALUES ($usr_id, '$client_id_safe', '$new_access_token', '$new_refresh_token', 'Bearer', '$scopes',
|
||||
DATE_ADD(NOW(), INTERVAL 1 HOUR),
|
||||
DATE_ADD(NOW(), INTERVAL 30 DAY),
|
||||
NOW())";
|
||||
|
||||
if (self::$db->execute($sql)) {
|
||||
return [
|
||||
'access_token' => $new_access_token,
|
||||
'refresh_token' => $new_refresh_token,
|
||||
'token_type' => 'Bearer',
|
||||
'expires_in' => 3600
|
||||
];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate access token
|
||||
* @param string $access_token Access token
|
||||
* @return array|false User and scope data or false
|
||||
*/
|
||||
public static function validateToken($access_token) {
|
||||
self::init();
|
||||
|
||||
$token_safe = VDatabase::escape($access_token);
|
||||
|
||||
$sql = "SELECT t.*, u.usr_user, u.usr_email
|
||||
FROM db_oauth_tokens t
|
||||
JOIN db_accountuser u ON t.usr_id = u.usr_id
|
||||
WHERE t.access_token = '$token_safe'
|
||||
AND t.expires_at > NOW()
|
||||
AND t.is_revoked = 0";
|
||||
|
||||
$result = self::$db->execute($sql);
|
||||
|
||||
if ($result && $result->RecordCount() > 0) {
|
||||
$data = $result->FetchRow();
|
||||
return [
|
||||
'usr_id' => $data['usr_id'],
|
||||
'username' => $data['usr_user'],
|
||||
'email' => $data['usr_email'],
|
||||
'scopes' => json_decode($data['scopes'], true)
|
||||
];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke token
|
||||
* @param string $access_token Access token
|
||||
* @return bool Success
|
||||
*/
|
||||
public static function revokeToken($access_token) {
|
||||
self::init();
|
||||
|
||||
$token_safe = VDatabase::escape($access_token);
|
||||
|
||||
$sql = "UPDATE db_oauth_tokens SET is_revoked = 1 WHERE access_token = '$token_safe'";
|
||||
return self::$db->execute($sql);
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ class VPasswordHash
|
||||
public $portable_hashes;
|
||||
public $random_state;
|
||||
|
||||
public function VPasswordHash($iteration_count_log2, $portable_hashes)
|
||||
public function __construct($iteration_count_log2, $portable_hashes)
|
||||
{
|
||||
$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
@@ -66,6 +66,12 @@ class VPasswordHash
|
||||
|
||||
}
|
||||
|
||||
// Legacy PHP 4 constructor for backwards compatibility
|
||||
public function VPasswordHash($iteration_count_log2, $portable_hashes)
|
||||
{
|
||||
$this->__construct($iteration_count_log2, $portable_hashes);
|
||||
}
|
||||
|
||||
public function get_random_bytes($count)
|
||||
{
|
||||
$output = '';
|
||||
|
||||
@@ -18,7 +18,7 @@ defined('_ISVALID') or header('Location: /error');
|
||||
class VPayment extends VSignup
|
||||
{
|
||||
/* check for expired subscription */
|
||||
public function checkSubscription($user_id)
|
||||
public static function checkSubscription($user_id)
|
||||
{
|
||||
global $class_database, $class_redirect, $cfg;
|
||||
|
||||
@@ -34,7 +34,7 @@ class VPayment extends VSignup
|
||||
}
|
||||
}
|
||||
/* get and assign membership types */
|
||||
public function getPackTypes($paid = '0')
|
||||
public static function getPackTypes($paid = '0')
|
||||
{
|
||||
global $db, $smarty;
|
||||
switch ($paid) {
|
||||
@@ -46,7 +46,7 @@ class VPayment extends VSignup
|
||||
$smarty->assign('memberships', $q->getrows());
|
||||
}
|
||||
/* check if membership db entry is active */
|
||||
public function checkActivePack($pack_id)
|
||||
public static function checkActivePack($pack_id)
|
||||
{
|
||||
global $class_database;
|
||||
$active = $class_database->singleFieldValue('db_packtypes', 'pk_active', 'pk_id', intval($pack_id));
|
||||
@@ -58,14 +58,14 @@ class VPayment extends VSignup
|
||||
|
||||
}
|
||||
/* get membership id */
|
||||
public function getPackID($user_id)
|
||||
public static function getPackID($user_id)
|
||||
{
|
||||
global $db;
|
||||
$q = $db->execute(sprintf("SELECT `pk_id` FROM `db_packusers` WHERE `usr_id`='%s' LIMIT 1;", intval($user_id)));
|
||||
return $q->fields['pk_id'];
|
||||
}
|
||||
/* get membership name */
|
||||
public function getUserPack($user = '')
|
||||
public static function getUserPack($user = '')
|
||||
{
|
||||
global $db, $smarty;
|
||||
switch ($user) {
|
||||
@@ -77,13 +77,13 @@ class VPayment extends VSignup
|
||||
return $q->fields['pk_name'];
|
||||
}
|
||||
/* update free account usage */
|
||||
public function updateFreeUsage($user_id)
|
||||
public static function updateFreeUsage($user_id)
|
||||
{
|
||||
global $db;
|
||||
$q = $db->execute(sprintf("UPDATE `db_accountuser` SET `usr_free_sub`='1', `usr_active`='1', `usr_status`='1' WHERE `usr_id`='%s' LIMIT 1;", intval($user_id)));
|
||||
}
|
||||
/* update free account membership after registration */
|
||||
public function updateFreeAccount($pk_id, $expire_time, $user_id)
|
||||
public static function updateFreeAccount($pk_id, $expire_time, $user_id)
|
||||
{
|
||||
global $db, $class_database;
|
||||
|
||||
@@ -99,7 +99,7 @@ class VPayment extends VSignup
|
||||
}
|
||||
}
|
||||
/* updating free membership registration */
|
||||
public function updateFreeEntry()
|
||||
public static function updateFreeEntry()
|
||||
{
|
||||
global $db, $class_database, $language, $cfg;
|
||||
$user_id = intval(base64_decode($_POST['usr_id']));
|
||||
@@ -126,7 +126,7 @@ class VPayment extends VSignup
|
||||
}
|
||||
}
|
||||
/* payment setup */
|
||||
public function preparePayment()
|
||||
public static function preparePayment()
|
||||
{
|
||||
global $db, $cfg, $class_smarty, $language, $smarty;
|
||||
|
||||
@@ -165,7 +165,7 @@ class VPayment extends VSignup
|
||||
die;
|
||||
}
|
||||
/* confirm before submitting payment */
|
||||
public function continuePayment()
|
||||
public static function continuePayment()
|
||||
{
|
||||
global $db, $smarty, $language, $cfg;
|
||||
$q = $db->execute(sprintf("SELECT * FROM `db_packtypes` WHERE `pk_id`='%s';", intval(base64_decode($_POST['pk_id']))));
|
||||
@@ -194,7 +194,7 @@ class VPayment extends VSignup
|
||||
$smarty->display('tpl_frontend/tpl_auth/tpl_payment_confirm.tpl');
|
||||
}
|
||||
/* process payment */
|
||||
public function doPayment($action)
|
||||
public static function doPayment($action)
|
||||
{
|
||||
global $db, $cfg, $language, $class_smarty, $smarty, $class_database;
|
||||
|
||||
@@ -362,7 +362,7 @@ class VPayment extends VSignup
|
||||
}
|
||||
}
|
||||
/* check discount code */
|
||||
public function discountCheck()
|
||||
public static function discountCheck()
|
||||
{
|
||||
global $class_filter, $db;
|
||||
|
||||
@@ -378,7 +378,7 @@ class VPayment extends VSignup
|
||||
}
|
||||
}
|
||||
/* text for membership durations */
|
||||
public function packWords($pk_period)
|
||||
public static function packWords($pk_period)
|
||||
{
|
||||
global $language, $smarty;
|
||||
|
||||
@@ -389,7 +389,7 @@ class VPayment extends VSignup
|
||||
return $words_array[$words_key[0]];
|
||||
}
|
||||
/* membership select list options */
|
||||
public function buildSelectOptions($pk_period)
|
||||
public static function buildSelectOptions($pk_period)
|
||||
{
|
||||
global $cfg, $smarty, $language;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ defined('_ISVALID') or header('Location: /error');
|
||||
class VRecovery
|
||||
{
|
||||
/* validate password recovery */
|
||||
public function processForm($section)
|
||||
public static function processForm($section)
|
||||
{
|
||||
global $language, $class_filter, $class_database;
|
||||
|
||||
@@ -35,7 +35,7 @@ class VRecovery
|
||||
return $error_message;
|
||||
}
|
||||
/* reset password */
|
||||
public function doPasswordReset($section)
|
||||
public static function doPasswordReset($section)
|
||||
{
|
||||
global $db, $class_filter;
|
||||
|
||||
@@ -59,7 +59,7 @@ class VRecovery
|
||||
|
||||
}
|
||||
/* validation of recovery link */
|
||||
public function validCheck($section, $type = 'recovery')
|
||||
public static function validCheck($section, $type = 'recovery')
|
||||
{
|
||||
global $cfg, $class_database, $language;
|
||||
$get = $_GET['s'] != '' ? $_GET['s'] : ($_GET['sid'] != '' ? $_GET['sid'] : null);
|
||||
@@ -78,7 +78,7 @@ class VRecovery
|
||||
|
||||
}
|
||||
/* mark recovery as used */
|
||||
public function updateRecoveryUsage($type = 'recovery')
|
||||
public static function updateRecoveryUsage($type = 'recovery')
|
||||
{
|
||||
global $db, $class_filter;
|
||||
|
||||
@@ -86,27 +86,27 @@ class VRecovery
|
||||
$db->execute(sprintf("UPDATE `db_usercodes` SET `code_used`=`code_used`+1, `%s`='%s' WHERE `type`='%s' AND `%s`='%s' LIMIT 1;", 'use_date', date("Y-m-d H:i:s"), $type, 'pwd_id', $class_filter->clr_str($get)));
|
||||
}
|
||||
/* recovery status */
|
||||
public function getRecoveryStatus($reset_id)
|
||||
public static function getRecoveryStatus($reset_id)
|
||||
{
|
||||
global $class_database, $class_filter;
|
||||
return $class_database->singleFieldValue('db_usercodes', 'code_active', 'pwd_id', $class_filter->clr_str($reset_id));
|
||||
}
|
||||
/* recovery create date */
|
||||
public function getRecoveryCDate()
|
||||
public static function getRecoveryCDate()
|
||||
{
|
||||
global $class_database, $class_filter;
|
||||
$get = $_GET['s'] != '' ? $_GET['s'] : ($_GET['sid'] != '' ? $_GET['sid'] : null);
|
||||
return $class_database->singleFieldValue('db_usercodes', 'create_date', 'pwd_id', $class_filter->clr_str($get));
|
||||
}
|
||||
/* get user id of recovery */
|
||||
public function getRecoveryID($reset_id, $type = 'recovery')
|
||||
public static function getRecoveryID($reset_id, $type = 'recovery')
|
||||
{
|
||||
global $class_filter, $db;
|
||||
$q = $db->execute(sprintf("SELECT `usr_id` FROM `db_usercodes` WHERE `pwd_id`='%s' AND `type`='%s' LIMIT 1;", $class_filter->clr_str($reset_id), $type));
|
||||
return $q->fields['usr_id'];
|
||||
}
|
||||
/* log recovery usage */
|
||||
public function addRecoveryEntry($user_id, $reset_id, $type = '', $addTo = '')
|
||||
public static function addRecoveryEntry($user_id, $reset_id, $type = '', $addTo = '')
|
||||
{
|
||||
global $db, $class_database, $cfg, $class_filter;
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ class VSignup
|
||||
}
|
||||
|
||||
/* signup form */
|
||||
public function processForm($allowedFields, $requiredFields)
|
||||
public static function processForm($allowedFields, $requiredFields)
|
||||
{
|
||||
global $cfg, $language, $class_filter;
|
||||
|
||||
@@ -256,7 +256,7 @@ class VSignup
|
||||
return $error_message;
|
||||
}
|
||||
/* define user folders */
|
||||
public function getUserFolders($usr_key)
|
||||
public static function getUserFolders($usr_key)
|
||||
{
|
||||
global $cfg;
|
||||
|
||||
@@ -302,7 +302,7 @@ class VSignup
|
||||
return array($dir[0], $dir[1]);
|
||||
}
|
||||
/* create user folders */
|
||||
public function createUserFolders($usr_key)
|
||||
public static function createUserFolders($usr_key)
|
||||
{
|
||||
global $cfg;
|
||||
|
||||
@@ -334,7 +334,7 @@ class VSignup
|
||||
copy($cfg['profile_images_dir'] . '/default.jpg', $cfg['profile_images_dir'] . '/' . $usr_key . '/' . $usr_key . '.jpg');
|
||||
}
|
||||
/* validating registration account */
|
||||
public function processAccount($fields = false)
|
||||
public static function processAccount($fields = false)
|
||||
{
|
||||
global $db, $cfg, $class_filter, $class_login, $class_redirect, $class_database;
|
||||
|
||||
@@ -473,6 +473,82 @@ class VSignup
|
||||
"ch_cfg" => $ch_cfg,
|
||||
"ch_pfields" => $ch_pfields,
|
||||
"ch_rownum" => $ch_rownum,
|
||||
"live_key" => '',
|
||||
"old_usr_key" => 0,
|
||||
"old_key" => '',
|
||||
"oauth_provider" => '',
|
||||
"oauth_uid" => '',
|
||||
"oauth_password" => 0,
|
||||
"usr_live" => 0,
|
||||
"usr_b_count" => 0,
|
||||
"usr_featured" => 0,
|
||||
"usr_promoted" => 0,
|
||||
"usr_partner" => 0,
|
||||
"usr_affiliate" => 0,
|
||||
"affiliate_pay_custom" => 0,
|
||||
"usr_sub_share" => 0,
|
||||
"usr_sub_perc" => 50,
|
||||
"usr_sub_currency" => 'USD',
|
||||
"usr_free_sub" => 0,
|
||||
"usr_weekupdates" => 0,
|
||||
"usr_deleted" => 0,
|
||||
"usr_followcount" => 0,
|
||||
"usr_subcount" => 0,
|
||||
"usr_tokencount" => 0,
|
||||
"usr_profileinc" => 0,
|
||||
"usr_mail_filecomment" => 1,
|
||||
"usr_mail_chancomment" => 1,
|
||||
"usr_mail_privmessage" => 1,
|
||||
"usr_mail_friendinv" => 1,
|
||||
"usr_mail_chansub" => 1,
|
||||
"usr_mail_chanfollow" => 1,
|
||||
"partner_date" => '0000-00-00 00:00:00',
|
||||
"affiliate_date" => '0000-00-00 00:00:00',
|
||||
"affiliate_custom" => '',
|
||||
"affiliate_email" => '',
|
||||
"affiliate_badge" => '',
|
||||
"affiliate_maps_key" => '',
|
||||
"usr_sub_email" => '',
|
||||
"usr_role" => '',
|
||||
"usr_logins" => 0,
|
||||
"usr_lastlogin" => '0000-00-00 00:00:00',
|
||||
"usr_menuaccess" => '',
|
||||
"usr_description" => '',
|
||||
"usr_website" => '',
|
||||
"usr_phone" => '',
|
||||
"usr_fax" => '',
|
||||
"usr_town" => '',
|
||||
"usr_city" => '',
|
||||
"usr_zip" => '',
|
||||
"usr_relation" => '',
|
||||
"usr_showage" => 0,
|
||||
"usr_occupations" => '',
|
||||
"usr_companies" => '',
|
||||
"usr_schools" => '',
|
||||
"usr_interests" => '',
|
||||
"usr_movies" => '',
|
||||
"usr_music" => '',
|
||||
"usr_books" => '',
|
||||
"usr_del_reason" => '',
|
||||
"fb_id" => 0,
|
||||
"ch_title" => '',
|
||||
"ch_descr" => '',
|
||||
"ch_tags" => '',
|
||||
"ch_influences" => '',
|
||||
"ch_style" => '',
|
||||
"ch_type" => 1,
|
||||
"ch_views" => 0,
|
||||
"home_cfg" => '',
|
||||
"ch_lastview" => date("Y-m-d"),
|
||||
"ch_photos" => '',
|
||||
"ch_photos_nr" => 0,
|
||||
"usr_fname" => '',
|
||||
"usr_lname" => '',
|
||||
"ch_links" => '',
|
||||
"ch_custom_fields" => '',
|
||||
"ch_positions" => '',
|
||||
"ch_channels" => '',
|
||||
"chat_temp" => '',
|
||||
);
|
||||
if ($fields) {
|
||||
$ins_array1['oauth_provider'] = $fields['oauth_provider'];
|
||||
@@ -552,7 +628,7 @@ class VSignup
|
||||
|
||||
}
|
||||
/* set account verified */
|
||||
public function verifyAccount()
|
||||
public static function verifyAccount()
|
||||
{
|
||||
global $db;
|
||||
|
||||
@@ -566,11 +642,11 @@ class VSignup
|
||||
|
||||
}
|
||||
/* signup form sessions start */
|
||||
public function formSessionInit()
|
||||
public static function formSessionInit()
|
||||
{
|
||||
global $cfg, $class_filter, $language;
|
||||
|
||||
$signup_username = ($cfg['username_format'] == 'strict' and VUserinfo::isValidUsername($_POST['frontend_signin_username'])) ? $class_filter->clr_str($_POST['frontend_signin_username']) : ($cfg['username_format'] == 'loose' and VUserinfo::isValidUsername($_POST['frontend_signin_username'])) ? VUserinfo::clearString($_POST['frontend_signin_username']) : null;
|
||||
$signup_username = (($cfg['username_format'] == 'strict' and VUserinfo::isValidUsername($_POST['frontend_signin_username'])) ? $class_filter->clr_str($_POST['frontend_signin_username']) : (($cfg['username_format'] == 'loose' and VUserinfo::isValidUsername($_POST['frontend_signin_username'])) ? VUserinfo::clearString($_POST['frontend_signin_username']) : null));
|
||||
$signup_pack = $cfg['paid_memberships'] == 1 ? $class_filter->clr_str($_POST['frontend_membership_type_sel']) : null;
|
||||
|
||||
$_SESSION['signup_username'] = $signup_username;
|
||||
@@ -578,7 +654,7 @@ class VSignup
|
||||
return true;
|
||||
}
|
||||
/* signup form sessions reset */
|
||||
public function formSessionReset()
|
||||
public static function formSessionReset()
|
||||
{
|
||||
$_SESSION['signup_username'] = null;
|
||||
$_SESSION['signup_pack'] = null;
|
||||
|
||||
808
f_core/f_classes/class.templatebuilder.php
Normal file
808
f_core/f_classes/class.templatebuilder.php
Normal file
@@ -0,0 +1,808 @@
|
||||
<?php
|
||||
/**
|
||||
* VTemplateBuilder - Drag and Drop Template Builder System
|
||||
*
|
||||
* This class handles the creation, management, and rendering of user-created templates
|
||||
* Integrates with EasyStream's existing Smarty template system
|
||||
*
|
||||
* @package EasyStream
|
||||
* @subpackage TemplateBuilder
|
||||
* @author EasyStream
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
class VTemplateBuilder
|
||||
{
|
||||
private $db;
|
||||
private $smarty;
|
||||
private $user_id;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
global $db, $smarty, $class_database;
|
||||
|
||||
$this->db = $db ?? $class_database;
|
||||
$this->smarty = $smarty;
|
||||
$this->user_id = isset($_SESSION['USER_ID']) ? (int)$_SESSION['USER_ID'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new template
|
||||
*
|
||||
* @param array $data Template data
|
||||
* @return array Result with success status and template_id
|
||||
*/
|
||||
public function createTemplate($data)
|
||||
{
|
||||
// Validate input
|
||||
if (empty($data['template_name'])) {
|
||||
return ['success' => false, 'error' => 'Template name is required'];
|
||||
}
|
||||
|
||||
if ($this->user_id === 0) {
|
||||
return ['success' => false, 'error' => 'User not authenticated'];
|
||||
}
|
||||
|
||||
// Generate slug
|
||||
$slug = $this->generateSlug($data['template_name'], $this->user_id);
|
||||
|
||||
// Default structure if not provided
|
||||
$default_structure = json_encode([
|
||||
'sections' => [],
|
||||
'layout_type' => 'flex',
|
||||
'max_width' => 1200
|
||||
]);
|
||||
|
||||
// Prepare data
|
||||
$insert_data = [
|
||||
'user_id' => $this->user_id,
|
||||
'template_name' => VDatabase::sanitizeInput($data['template_name']),
|
||||
'template_slug' => $slug,
|
||||
'template_type' => $data['template_type'] ?? 'custom_page',
|
||||
'template_structure' => $data['template_structure'] ?? $default_structure,
|
||||
'template_settings' => $data['template_settings'] ?? json_encode([]),
|
||||
'custom_css' => $data['custom_css'] ?? '',
|
||||
'custom_js' => $data['custom_js'] ?? '',
|
||||
'is_active' => isset($data['is_active']) ? (int)$data['is_active'] : 0
|
||||
];
|
||||
|
||||
// Insert into database
|
||||
$sql = "INSERT INTO `db_templatebuilder_templates`
|
||||
SET " . VDatabase::build_insert_update($insert_data);
|
||||
|
||||
$result = $this->db->execute($sql);
|
||||
|
||||
if ($result) {
|
||||
$template_id = $this->db->insert_id();
|
||||
|
||||
// Create initial version
|
||||
$this->createVersion($template_id, $insert_data, 'Initial version');
|
||||
|
||||
VLogger::log('INFO', "Template created: ID {$template_id}, Name: {$data['template_name']}",
|
||||
['user_id' => $this->user_id]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'template_id' => $template_id,
|
||||
'slug' => $slug
|
||||
];
|
||||
}
|
||||
|
||||
return ['success' => false, 'error' => 'Failed to create template'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing template
|
||||
*
|
||||
* @param int $template_id Template ID
|
||||
* @param array $data Update data
|
||||
* @param string $change_note Optional change note for version history
|
||||
* @return array Result with success status
|
||||
*/
|
||||
public function updateTemplate($template_id, $data, $change_note = null)
|
||||
{
|
||||
$template_id = (int)$template_id;
|
||||
|
||||
// Verify ownership
|
||||
if (!$this->verifyOwnership($template_id)) {
|
||||
return ['success' => false, 'error' => 'Unauthorized'];
|
||||
}
|
||||
|
||||
// Prepare update data
|
||||
$update_data = [];
|
||||
|
||||
$allowed_fields = [
|
||||
'template_name', 'template_type', 'template_structure',
|
||||
'template_settings', 'custom_css', 'custom_js', 'is_active', 'is_default'
|
||||
];
|
||||
|
||||
foreach ($allowed_fields as $field) {
|
||||
if (isset($data[$field])) {
|
||||
if ($field === 'template_name') {
|
||||
$update_data[$field] = VDatabase::sanitizeInput($data[$field]);
|
||||
} else {
|
||||
$update_data[$field] = $data[$field];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($update_data)) {
|
||||
return ['success' => false, 'error' => 'No valid fields to update'];
|
||||
}
|
||||
|
||||
// Update database
|
||||
$sql = "UPDATE `db_templatebuilder_templates`
|
||||
SET " . VDatabase::build_insert_update($update_data) . "
|
||||
WHERE `template_id` = '{$template_id}'";
|
||||
|
||||
$result = $this->db->execute($sql);
|
||||
|
||||
if ($result) {
|
||||
// Create version history entry
|
||||
$this->createVersion($template_id, $update_data, $change_note);
|
||||
|
||||
VLogger::log('INFO', "Template updated: ID {$template_id}",
|
||||
['user_id' => $this->user_id, 'changes' => array_keys($update_data)]);
|
||||
|
||||
return ['success' => true];
|
||||
}
|
||||
|
||||
return ['success' => false, 'error' => 'Failed to update template'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a template
|
||||
*
|
||||
* @param int $template_id Template ID
|
||||
* @return array Result with success status
|
||||
*/
|
||||
public function deleteTemplate($template_id)
|
||||
{
|
||||
$template_id = (int)$template_id;
|
||||
|
||||
// Verify ownership
|
||||
if (!$this->verifyOwnership($template_id)) {
|
||||
return ['success' => false, 'error' => 'Unauthorized'];
|
||||
}
|
||||
|
||||
$sql = "DELETE FROM `db_templatebuilder_templates`
|
||||
WHERE `template_id` = '{$template_id}'";
|
||||
|
||||
$result = $this->db->execute($sql);
|
||||
|
||||
if ($result) {
|
||||
VLogger::log('INFO', "Template deleted: ID {$template_id}",
|
||||
['user_id' => $this->user_id]);
|
||||
|
||||
return ['success' => true];
|
||||
}
|
||||
|
||||
return ['success' => false, 'error' => 'Failed to delete template'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template by ID
|
||||
*
|
||||
* @param int $template_id Template ID
|
||||
* @param bool $check_ownership Whether to verify ownership
|
||||
* @return array|null Template data or null if not found
|
||||
*/
|
||||
public function getTemplate($template_id, $check_ownership = true)
|
||||
{
|
||||
$template_id = (int)$template_id;
|
||||
|
||||
$sql = "SELECT * FROM `db_templatebuilder_templates`
|
||||
WHERE `template_id` = '{$template_id}'";
|
||||
|
||||
if ($check_ownership && $this->user_id > 0) {
|
||||
$sql .= " AND `user_id` = '{$this->user_id}'";
|
||||
}
|
||||
|
||||
$result = $this->db->execute($sql);
|
||||
|
||||
if ($result && $result->recordcount() > 0) {
|
||||
$template = $result->fields;
|
||||
|
||||
// Decode JSON fields
|
||||
$template['template_structure'] = json_decode($template['template_structure'], true);
|
||||
$template['template_settings'] = json_decode($template['template_settings'], true);
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template by slug
|
||||
*
|
||||
* @param string $slug Template slug
|
||||
* @return array|null Template data or null if not found
|
||||
*/
|
||||
public function getTemplateBySlug($slug)
|
||||
{
|
||||
$slug = VDatabase::sanitizeInput($slug);
|
||||
|
||||
$sql = "SELECT * FROM `db_templatebuilder_templates`
|
||||
WHERE `template_slug` = '{$slug}'
|
||||
AND `is_active` = 1
|
||||
LIMIT 1";
|
||||
|
||||
$result = $this->db->execute($sql);
|
||||
|
||||
if ($result && $result->recordcount() > 0) {
|
||||
$template = $result->fields;
|
||||
|
||||
// Decode JSON fields
|
||||
$template['template_structure'] = json_decode($template['template_structure'], true);
|
||||
$template['template_settings'] = json_decode($template['template_settings'], true);
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all templates for current user
|
||||
*
|
||||
* @param array $filters Optional filters
|
||||
* @return array Array of templates
|
||||
*/
|
||||
public function getUserTemplates($filters = [])
|
||||
{
|
||||
if ($this->user_id === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sql = "SELECT * FROM `db_templatebuilder_templates`
|
||||
WHERE `user_id` = '{$this->user_id}'";
|
||||
|
||||
// Apply filters
|
||||
if (!empty($filters['template_type'])) {
|
||||
$type = VDatabase::sanitizeInput($filters['template_type']);
|
||||
$sql .= " AND `template_type` = '{$type}'";
|
||||
}
|
||||
|
||||
if (isset($filters['is_active'])) {
|
||||
$active = (int)$filters['is_active'];
|
||||
$sql .= " AND `is_active` = '{$active}'";
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY `updated_at` DESC";
|
||||
|
||||
if (!empty($filters['limit'])) {
|
||||
$limit = (int)$filters['limit'];
|
||||
$sql .= " LIMIT {$limit}";
|
||||
}
|
||||
|
||||
$result = $this->db->execute($sql);
|
||||
$templates = [];
|
||||
|
||||
if ($result) {
|
||||
foreach ($result->getRows() as $row) {
|
||||
// Don't decode JSON for listing (performance)
|
||||
$templates[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a template
|
||||
*
|
||||
* @param int|string $template_identifier Template ID or slug
|
||||
* @param array $data Data to pass to template
|
||||
* @return string Rendered HTML
|
||||
*/
|
||||
public function renderTemplate($template_identifier, $data = [])
|
||||
{
|
||||
// Get template
|
||||
if (is_numeric($template_identifier)) {
|
||||
$template = $this->getTemplate($template_identifier, false);
|
||||
} else {
|
||||
$template = $this->getTemplateBySlug($template_identifier);
|
||||
}
|
||||
|
||||
if (!$template) {
|
||||
return '<!-- Template not found -->';
|
||||
}
|
||||
|
||||
// Increment views
|
||||
$this->incrementViews($template['template_id']);
|
||||
|
||||
// Build HTML from structure
|
||||
$html = $this->buildHtmlFromStructure($template['template_structure'], $data);
|
||||
|
||||
// Wrap with custom CSS if present
|
||||
if (!empty($template['custom_css'])) {
|
||||
$html = "<style>\n{$template['custom_css']}\n</style>\n" . $html;
|
||||
}
|
||||
|
||||
// Add custom JS if present (sanitized)
|
||||
if (!empty($template['custom_js'])) {
|
||||
$html .= "\n<script>\n{$template['custom_js']}\n</script>";
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build HTML from template structure
|
||||
*
|
||||
* @param array $structure Template structure
|
||||
* @param array $data Data to pass to components
|
||||
* @return string Generated HTML
|
||||
*/
|
||||
private function buildHtmlFromStructure($structure, $data = [])
|
||||
{
|
||||
if (empty($structure['sections'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$html = '';
|
||||
$max_width = $structure['max_width'] ?? 1200;
|
||||
$layout_type = $structure['layout_type'] ?? 'flex';
|
||||
|
||||
// Container wrapper
|
||||
$html .= "<div class=\"template-builder-container\" data-layout=\"{$layout_type}\" style=\"max-width: {$max_width}px; margin: 0 auto;\">\n";
|
||||
|
||||
foreach ($structure['sections'] as $section) {
|
||||
$html .= $this->buildSection($section, $data);
|
||||
}
|
||||
|
||||
$html .= "</div>\n";
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a section
|
||||
*
|
||||
* @param array $section Section data
|
||||
* @param array $data Global data
|
||||
* @return string Section HTML
|
||||
*/
|
||||
private function buildSection($section, $data)
|
||||
{
|
||||
$section_id = $section['id'] ?? 'section-' . uniqid();
|
||||
$section_class = $section['class'] ?? '';
|
||||
$columns = $section['columns'] ?? 1;
|
||||
|
||||
$html = "<div class=\"tb-section {$section_class}\" id=\"{$section_id}\" data-columns=\"{$columns}\">\n";
|
||||
|
||||
// Apply section styles if present
|
||||
if (!empty($section['styles'])) {
|
||||
$style_str = $this->buildStyleString($section['styles']);
|
||||
$html = str_replace('<div class="tb-section', "<div style=\"{$style_str}\" class=\"tb-section", $html);
|
||||
}
|
||||
|
||||
// Build columns
|
||||
if (!empty($section['blocks'])) {
|
||||
$html .= "<div class=\"tb-columns\" style=\"display: grid; grid-template-columns: repeat({$columns}, 1fr); gap: " . ($section['gap'] ?? 20) . "px;\">\n";
|
||||
|
||||
foreach ($section['blocks'] as $block) {
|
||||
$html .= $this->buildBlock($block, $data);
|
||||
}
|
||||
|
||||
$html .= "</div>\n";
|
||||
}
|
||||
|
||||
$html .= "</div>\n";
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a block (component)
|
||||
*
|
||||
* @param array $block Block data
|
||||
* @param array $data Global data
|
||||
* @return string Block HTML
|
||||
*/
|
||||
private function buildBlock($block, $data)
|
||||
{
|
||||
$block_id = $block['id'] ?? 'block-' . uniqid();
|
||||
$component_slug = $block['component'] ?? null;
|
||||
|
||||
if (!$component_slug) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Get component definition
|
||||
$component = $this->getComponent($component_slug);
|
||||
|
||||
if (!$component) {
|
||||
return "<!-- Component '{$component_slug}' not found -->";
|
||||
}
|
||||
|
||||
// Merge block settings with component defaults
|
||||
$settings = array_merge(
|
||||
json_decode($component['component_settings_schema'], true) ?? [],
|
||||
$block['settings'] ?? []
|
||||
);
|
||||
|
||||
// Build HTML from component template
|
||||
$html = $component['component_html'];
|
||||
|
||||
// Replace placeholders with settings values
|
||||
$html = $this->replacePlaceholders($html, $settings, $data);
|
||||
|
||||
// Apply component CSS if present
|
||||
if (!empty($component['component_css'])) {
|
||||
$css = $this->replacePlaceholders($component['component_css'], $settings, $data);
|
||||
$html = "<style>\n{$css}\n</style>\n" . $html;
|
||||
}
|
||||
|
||||
// Wrap in block container
|
||||
$block_html = "<div class=\"tb-block\" id=\"{$block_id}\" data-component=\"{$component_slug}\">\n";
|
||||
$block_html .= $html;
|
||||
$block_html .= "</div>\n";
|
||||
|
||||
return $block_html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace placeholders in template string
|
||||
*
|
||||
* @param string $template Template string
|
||||
* @param array $settings Settings values
|
||||
* @param array $data Global data
|
||||
* @return string Processed string
|
||||
*/
|
||||
private function replacePlaceholders($template, $settings, $data)
|
||||
{
|
||||
// Replace {{variable}} with actual values
|
||||
$template = preg_replace_callback('/\{\{(\w+)\}\}/', function($matches) use ($settings, $data) {
|
||||
$key = $matches[1];
|
||||
|
||||
// Check settings first
|
||||
if (isset($settings[$key])) {
|
||||
$value = $settings[$key];
|
||||
// Get default value if it's an array
|
||||
if (is_array($value) && isset($value['default'])) {
|
||||
return $value['default'];
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Check global data
|
||||
if (isset($data[$key])) {
|
||||
return $data[$key];
|
||||
}
|
||||
|
||||
return $matches[0]; // Return original if not found
|
||||
}, $template);
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get component by slug
|
||||
*
|
||||
* @param string $slug Component slug
|
||||
* @return array|null Component data
|
||||
*/
|
||||
private function getComponent($slug)
|
||||
{
|
||||
$slug = VDatabase::sanitizeInput($slug);
|
||||
|
||||
$sql = "SELECT * FROM `db_templatebuilder_components`
|
||||
WHERE `component_slug` = '{$slug}'
|
||||
LIMIT 1";
|
||||
|
||||
$result = $this->db->execute($sql);
|
||||
|
||||
if ($result && $result->recordcount() > 0) {
|
||||
return $result->fields;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available components
|
||||
*
|
||||
* @param string $category Optional category filter
|
||||
* @return array Array of components
|
||||
*/
|
||||
public function getComponents($category = null)
|
||||
{
|
||||
$sql = "SELECT * FROM `db_templatebuilder_components`";
|
||||
|
||||
if ($category) {
|
||||
$category = VDatabase::sanitizeInput($category);
|
||||
$sql .= " WHERE `component_category` = '{$category}'";
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY `component_category`, `component_name`";
|
||||
|
||||
$result = $this->db->execute($sql);
|
||||
$components = [];
|
||||
|
||||
if ($result) {
|
||||
foreach ($result->getRows() as $row) {
|
||||
$row['component_settings_schema'] = json_decode($row['component_settings_schema'], true);
|
||||
$components[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a version history entry
|
||||
*
|
||||
* @param int $template_id Template ID
|
||||
* @param array $data Template data
|
||||
* @param string $change_note Optional change note
|
||||
* @return bool Success status
|
||||
*/
|
||||
private function createVersion($template_id, $data, $change_note = null)
|
||||
{
|
||||
// Get current version number
|
||||
$sql = "SELECT MAX(`version_number`) as max_version
|
||||
FROM `db_templatebuilder_versions`
|
||||
WHERE `template_id` = '{$template_id}'";
|
||||
|
||||
$result = $this->db->execute($sql);
|
||||
$max_version = 0;
|
||||
|
||||
if ($result && $result->recordcount() > 0) {
|
||||
$row = $result->fields;
|
||||
$max_version = (int)$row['max_version'];
|
||||
}
|
||||
|
||||
$new_version = $max_version + 1;
|
||||
|
||||
$version_data = [
|
||||
'template_id' => $template_id,
|
||||
'version_number' => $new_version,
|
||||
'template_structure' => $data['template_structure'] ?? '{}',
|
||||
'template_settings' => $data['template_settings'] ?? '{}',
|
||||
'custom_css' => $data['custom_css'] ?? '',
|
||||
'custom_js' => $data['custom_js'] ?? '',
|
||||
'change_note' => $change_note ? VDatabase::sanitizeInput($change_note) : null
|
||||
];
|
||||
|
||||
$sql = "INSERT INTO `db_templatebuilder_versions`
|
||||
SET " . VDatabase::build_insert_update($version_data);
|
||||
|
||||
return $this->db->execute($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user preferences
|
||||
*
|
||||
* @param int $user_id Optional user ID (defaults to current user)
|
||||
* @return array User preferences
|
||||
*/
|
||||
public function getUserPreferences($user_id = null)
|
||||
{
|
||||
$user_id = $user_id ?? $this->user_id;
|
||||
|
||||
if ($user_id === 0) {
|
||||
return $this->getDefaultPreferences();
|
||||
}
|
||||
|
||||
$sql = "SELECT * FROM `db_templatebuilder_user_prefs`
|
||||
WHERE `user_id` = '{$user_id}'
|
||||
LIMIT 1";
|
||||
|
||||
$result = $this->db->execute($sql);
|
||||
|
||||
if ($result && $result->recordcount() > 0) {
|
||||
$prefs = $result->fields;
|
||||
$prefs['preferences'] = json_decode($prefs['preferences'], true);
|
||||
return $prefs;
|
||||
}
|
||||
|
||||
return $this->getDefaultPreferences();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user preferences
|
||||
*
|
||||
* @param array $preferences Preferences to update
|
||||
* @return bool Success status
|
||||
*/
|
||||
public function updateUserPreferences($preferences)
|
||||
{
|
||||
if ($this->user_id === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if preferences exist
|
||||
$existing = $this->getUserPreferences();
|
||||
|
||||
$allowed_fields = [
|
||||
'active_template_homepage', 'active_template_channel',
|
||||
'active_template_browse', 'builder_mode', 'auto_save',
|
||||
'show_grid', 'preferences'
|
||||
];
|
||||
|
||||
$update_data = [];
|
||||
foreach ($allowed_fields as $field) {
|
||||
if (isset($preferences[$field])) {
|
||||
if ($field === 'preferences') {
|
||||
$update_data[$field] = json_encode($preferences[$field]);
|
||||
} else {
|
||||
$update_data[$field] = $preferences[$field];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($update_data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($existing['pref_id'])) {
|
||||
// Update existing
|
||||
$sql = "UPDATE `db_templatebuilder_user_prefs`
|
||||
SET " . VDatabase::build_insert_update($update_data) . "
|
||||
WHERE `user_id` = '{$this->user_id}'";
|
||||
} else {
|
||||
// Insert new
|
||||
$update_data['user_id'] = $this->user_id;
|
||||
$sql = "INSERT INTO `db_templatebuilder_user_prefs`
|
||||
SET " . VDatabase::build_insert_update($update_data);
|
||||
}
|
||||
|
||||
return $this->db->execute($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default preferences
|
||||
*
|
||||
* @return array Default preferences
|
||||
*/
|
||||
private function getDefaultPreferences()
|
||||
{
|
||||
return [
|
||||
'builder_mode' => 'simple',
|
||||
'auto_save' => 1,
|
||||
'show_grid' => 1,
|
||||
'preferences' => []
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify template ownership
|
||||
*
|
||||
* @param int $template_id Template ID
|
||||
* @return bool True if user owns template
|
||||
*/
|
||||
private function verifyOwnership($template_id)
|
||||
{
|
||||
if ($this->user_id === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sql = "SELECT `user_id` FROM `db_templatebuilder_templates`
|
||||
WHERE `template_id` = '{$template_id}'
|
||||
LIMIT 1";
|
||||
|
||||
$result = $this->db->execute($sql);
|
||||
|
||||
if ($result && $result->recordcount() > 0) {
|
||||
$row = $result->fields;
|
||||
return ((int)$row['user_id'] === $this->user_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate unique slug
|
||||
*
|
||||
* @param string $name Template name
|
||||
* @param int $user_id User ID
|
||||
* @return string Unique slug
|
||||
*/
|
||||
private function generateSlug($name, $user_id)
|
||||
{
|
||||
// Basic slug generation
|
||||
$slug = strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $name)));
|
||||
$slug = $user_id . '-' . $slug;
|
||||
|
||||
// Check uniqueness
|
||||
$original_slug = $slug;
|
||||
$counter = 1;
|
||||
|
||||
while ($this->slugExists($slug)) {
|
||||
$slug = $original_slug . '-' . $counter;
|
||||
$counter++;
|
||||
}
|
||||
|
||||
return $slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if slug exists
|
||||
*
|
||||
* @param string $slug Slug to check
|
||||
* @return bool True if exists
|
||||
*/
|
||||
private function slugExists($slug)
|
||||
{
|
||||
$slug = VDatabase::sanitizeInput($slug);
|
||||
|
||||
$sql = "SELECT COUNT(*) as count FROM `db_templatebuilder_templates`
|
||||
WHERE `template_slug` = '{$slug}'";
|
||||
|
||||
$result = $this->db->execute($sql);
|
||||
|
||||
if ($result) {
|
||||
$row = $result->fields;
|
||||
return ((int)$row['count'] > 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment template views
|
||||
*
|
||||
* @param int $template_id Template ID
|
||||
* @return bool Success status
|
||||
*/
|
||||
private function incrementViews($template_id)
|
||||
{
|
||||
$sql = "UPDATE `db_templatebuilder_templates`
|
||||
SET `views` = `views` + 1
|
||||
WHERE `template_id` = '{$template_id}'";
|
||||
|
||||
return $this->db->execute($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build CSS style string from array
|
||||
*
|
||||
* @param array $styles Style array
|
||||
* @return string CSS string
|
||||
*/
|
||||
private function buildStyleString($styles)
|
||||
{
|
||||
$style_parts = [];
|
||||
|
||||
foreach ($styles as $property => $value) {
|
||||
$property = str_replace('_', '-', $property);
|
||||
$style_parts[] = "{$property}: {$value}";
|
||||
}
|
||||
|
||||
return implode('; ', $style_parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a template
|
||||
*
|
||||
* @param int $template_id Template ID to duplicate
|
||||
* @param string $new_name Optional new name
|
||||
* @return array Result with success status and new template_id
|
||||
*/
|
||||
public function duplicateTemplate($template_id, $new_name = null)
|
||||
{
|
||||
$template = $this->getTemplate($template_id, true);
|
||||
|
||||
if (!$template) {
|
||||
return ['success' => false, 'error' => 'Template not found'];
|
||||
}
|
||||
|
||||
// Prepare new template data
|
||||
$new_template = [
|
||||
'template_name' => $new_name ?? ($template['template_name'] . ' (Copy)'),
|
||||
'template_type' => $template['template_type'],
|
||||
'template_structure' => json_encode($template['template_structure']),
|
||||
'template_settings' => json_encode($template['template_settings']),
|
||||
'custom_css' => $template['custom_css'],
|
||||
'custom_js' => $template['custom_js'],
|
||||
'is_active' => 0
|
||||
];
|
||||
|
||||
return $this->createTemplate($new_template);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ defined('_ISVALID') or header('Location: /error');
|
||||
class VUserinfo
|
||||
{
|
||||
/* valid username format */
|
||||
public function isValidUsername($username)
|
||||
public static function isValidUsername($username)
|
||||
{
|
||||
global $cfg;
|
||||
|
||||
@@ -42,12 +42,12 @@ class VUserinfo
|
||||
return true;
|
||||
}
|
||||
/* remove chars from string */
|
||||
public function clearString($username)
|
||||
public static function clearString($username)
|
||||
{
|
||||
return preg_replace('/[^a-zA-Z0-9@_.\-]/', '', $username);
|
||||
}
|
||||
/* check for existing username */
|
||||
public function existingUsername($username, $section = 'frontend')
|
||||
public static function existingUsername($username, $section = 'frontend')
|
||||
{
|
||||
global $db, $class_database;
|
||||
|
||||
@@ -76,7 +76,7 @@ class VUserinfo
|
||||
}
|
||||
}
|
||||
/* check for existing email */
|
||||
public function existingEmail($email, $section = 'frontend')
|
||||
public static function existingEmail($email, $section = 'frontend')
|
||||
{
|
||||
global $db, $class_filter;
|
||||
|
||||
@@ -105,7 +105,7 @@ class VUserinfo
|
||||
}
|
||||
}
|
||||
/* get user id from other fields */
|
||||
public function getUserID($user, $where_field = 'usr_user')
|
||||
public static function getUserID($user, $where_field = 'usr_user')
|
||||
{
|
||||
global $db, $class_filter;
|
||||
$user = $where_field == 'usr_user' ? self::clearString($user) : $class_filter->clr_str($user);
|
||||
@@ -113,14 +113,14 @@ class VUserinfo
|
||||
return $q->fields['usr_id'];
|
||||
}
|
||||
/* get user name from other fields */
|
||||
public function getUserName($user, $where_field = 'usr_id')
|
||||
public static function getUserName($user, $where_field = 'usr_id')
|
||||
{
|
||||
global $db, $class_filter;
|
||||
$q = $db->execute(sprintf("SELECT `usr_user` FROM `db_accountuser` WHERE `" . $where_field . "`='%s' LIMIT 1;", $class_filter->clr_str($user)));
|
||||
return $q->fields['usr_user'];
|
||||
}
|
||||
/* get email from user id */
|
||||
public function getUserEmail($user = '')
|
||||
public static function getUserEmail($user = '')
|
||||
{
|
||||
global $db, $smarty;
|
||||
switch ($user) {
|
||||
@@ -133,7 +133,7 @@ class VUserinfo
|
||||
return $usr_email;
|
||||
}
|
||||
/* get various user details */
|
||||
public function getUserInfo($user_id)
|
||||
public static function getUserInfo($user_id)
|
||||
{
|
||||
global $db;
|
||||
|
||||
@@ -163,7 +163,7 @@ class VUserinfo
|
||||
return $info;
|
||||
}
|
||||
/* username validation */
|
||||
public function usernameVerification($username, $section = 'frontend')
|
||||
public static function usernameVerification($username, $section = 'frontend')
|
||||
{
|
||||
global $cfg, $language;
|
||||
|
||||
@@ -182,7 +182,7 @@ class VUserinfo
|
||||
} else {return false;}
|
||||
}
|
||||
/* birthday input validation */
|
||||
public function birthdayVerification($date)
|
||||
public static function birthdayVerification($date)
|
||||
{
|
||||
global $cfg;
|
||||
|
||||
@@ -195,7 +195,7 @@ class VUserinfo
|
||||
|
||||
}
|
||||
/* age from date */
|
||||
public function ageFromString($date)
|
||||
public static function ageFromString($date)
|
||||
{
|
||||
if (!preg_match("/([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})/", $date, $arr)) {
|
||||
return false;
|
||||
@@ -211,7 +211,7 @@ class VUserinfo
|
||||
return $age;
|
||||
}
|
||||
/* generate random strings */
|
||||
public function generateRandomString($length = 10, $alphanumeric = false)
|
||||
public static function generateRandomString($length = 10, $alphanumeric = false)
|
||||
{
|
||||
if (!$alphanumeric) {
|
||||
$str = join('', array_map(function ($value) {return $value == 1 ? mt_rand(1, 3) : mt_rand(0, 9);}, range(1, $length)));
|
||||
@@ -226,7 +226,7 @@ class VUserinfo
|
||||
return $str;
|
||||
}
|
||||
/* check for available username */
|
||||
public function usernameAvailability($username, $section = 'frontend')
|
||||
public static function usernameAvailability($username, $section = 'frontend')
|
||||
{
|
||||
global $cfg, $language;
|
||||
|
||||
@@ -248,7 +248,7 @@ class VUserinfo
|
||||
}
|
||||
}
|
||||
/* truncating strings */
|
||||
public function truncateString($string, $max_length)
|
||||
public static function truncateString($string, $max_length)
|
||||
{
|
||||
return mb_strimwidth($string, 0, $max_length, '...', 'utf-8');
|
||||
|
||||
@@ -264,7 +264,7 @@ class VUserinfo
|
||||
}
|
||||
}
|
||||
/* days from date */
|
||||
public function timeRange($datetime)
|
||||
public static function timeRange($datetime)
|
||||
{
|
||||
global $language;
|
||||
|
||||
@@ -314,7 +314,7 @@ class VUserinfo
|
||||
}
|
||||
|
||||
/* days from date */
|
||||
public function timeRange_old($datetime)
|
||||
public static function timeRange_old($datetime)
|
||||
{
|
||||
global $language;
|
||||
|
||||
@@ -354,7 +354,7 @@ class VUserinfo
|
||||
}
|
||||
}
|
||||
/* unix timestamp */
|
||||
public function convert_datetime($str)
|
||||
public static function convert_datetime($str)
|
||||
{
|
||||
if ($str == '') {
|
||||
return false;
|
||||
|
||||
@@ -17,7 +17,7 @@ defined('_ISVALID') or header('Location: /error');
|
||||
|
||||
class VValidation
|
||||
{
|
||||
public function checkEmailAddress($email)
|
||||
public static function checkEmailAddress($email)
|
||||
{
|
||||
if (preg_match('/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i', $email)) {
|
||||
return true;
|
||||
|
||||
@@ -21,9 +21,9 @@ function insert_loadjsplugins()
|
||||
function insert_loadbejsplugins()
|
||||
{return VGenerate::bejsplugins();}
|
||||
function insert_getSubCount()
|
||||
{return VHome::getSubCount();}
|
||||
{return (isset($GLOBALS["class_home"]) ? $GLOBALS["class_home"]->getSubCount() : 0);}
|
||||
function insert_getFollowCount()
|
||||
{return VHome::getSubCount(1);}
|
||||
{return (isset($GLOBALS["class_home"]) ? $GLOBALS["class_home"]->getSubCount(1) : 0);}
|
||||
function insert_getCurrentSection()
|
||||
{return VHref::currentSection();}
|
||||
function insert_getSearchSection()
|
||||
@@ -77,7 +77,7 @@ function insert_promotedChannelsMenu()
|
||||
function insert_uploadResponse()
|
||||
{return VResponses::uploadResponse();}
|
||||
function insert_getUserStats($type)
|
||||
{return VUseraccount::getUserStats($type["type"]);}
|
||||
{return (isset($GLOBALS["class_useraccount"]) ? $GLOBALS["class_useraccount"]->getUserStats($type["type"]) : null);}
|
||||
function insert_subsConfig()
|
||||
{return VFiles::subsConfig();}
|
||||
function insert_fileListSelect($for)
|
||||
@@ -159,7 +159,7 @@ function insert_getUsername($user_id)
|
||||
function insert_getProfileImage($for)
|
||||
{
|
||||
$_for = $for["for"] != '' ? $for["for"] : '';
|
||||
return VUseraccount::getProfileImage($_for);
|
||||
global $class_useraccount; return (isset($class_useraccount) ? $class_useraccount->getProfileImage($_for) : '');
|
||||
}
|
||||
function insert_phpInfoText()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user