Autosave: 20260225-233522
This commit is contained in:
parent
afb01a6973
commit
a2cdf745b4
@ -418,6 +418,75 @@ private function getReferralStats() {
|
||||
$this->redirect('/admin/apks');
|
||||
}
|
||||
|
||||
// Newsletter Management
|
||||
public function newsletter() {
|
||||
$this->checkAuth();
|
||||
$db = db_pdo();
|
||||
$subscribers = $db->query("SELECT * FROM newsletter_subscribers ORDER BY created_at DESC")->fetchAll();
|
||||
$this->view('admin/newsletter/index', ['subscribers' => $subscribers]);
|
||||
}
|
||||
|
||||
public function deleteSubscriber($params) {
|
||||
$this->checkAuth();
|
||||
$db = db_pdo();
|
||||
$stmt = $db->prepare("DELETE FROM newsletter_subscribers WHERE id = ?");
|
||||
$stmt->execute([$params['id']]);
|
||||
$this->redirect('/admin/newsletter');
|
||||
}
|
||||
|
||||
public function exportSubscribers() {
|
||||
$this->checkAuth();
|
||||
$db = db_pdo();
|
||||
$subscribers = $db->query("SELECT email, created_at FROM newsletter_subscribers ORDER BY created_at DESC")->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
header('Content-Type: text/csv; charset=utf-8');
|
||||
header('Content-Disposition: attachment; filename=subscribers_' . date('Y-m-d') . '.csv');
|
||||
$output = fopen('php://output', 'w');
|
||||
fputcsv($output, ['Email', 'Subscribed At']);
|
||||
foreach ($subscribers as $row) {
|
||||
fputcsv($output, $row);
|
||||
}
|
||||
fclose($output);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function sendNewsletterForm() {
|
||||
$this->checkAuth();
|
||||
$db = db_pdo();
|
||||
$total_subscribers = $db->query("SELECT COUNT(*) FROM newsletter_subscribers")->fetchColumn();
|
||||
$this->view('admin/newsletter/send', ['total_subscribers' => $total_subscribers]);
|
||||
}
|
||||
|
||||
public function sendNewsletter() {
|
||||
$this->checkAuth();
|
||||
$subject = $_POST['subject'] ?? '';
|
||||
$message = $_POST['message'] ?? '';
|
||||
|
||||
if (empty($subject) || empty($message)) {
|
||||
$this->view('admin/newsletter/send', ['error' => 'Subject and message are required.']);
|
||||
return;
|
||||
}
|
||||
|
||||
$db = db_pdo();
|
||||
$subscribers = $db->query("SELECT email FROM newsletter_subscribers")->fetchAll(\PDO::FETCH_COLUMN);
|
||||
|
||||
if (empty($subscribers)) {
|
||||
$this->view('admin/newsletter/send', ['error' => 'No subscribers found.']);
|
||||
return;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../../mail/MailService.php';
|
||||
|
||||
// We use BCC to prevent subscribers from seeing each other's emails
|
||||
$results = \MailService::sendMail(null, $subject, $message, null, ['bcc' => $subscribers]);
|
||||
|
||||
if ($results['success']) {
|
||||
$this->view('admin/newsletter/send', ['success' => 'Email sent to ' . count($subscribers) . ' subscribers.']);
|
||||
} else {
|
||||
$this->view('admin/newsletter/send', ['error' => 'Failed to send email: ' . $results['error']]);
|
||||
}
|
||||
}
|
||||
|
||||
private function slugify($text) {
|
||||
$text = preg_replace('~[^\pL\d]+~u', '-', $text);
|
||||
$text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
|
||||
|
||||
137
index.php
137
index.php
@ -1,74 +1,32 @@
|
||||
<?php
|
||||
|
||||
// Autoloader
|
||||
spl_autoload_register(function ($class) {
|
||||
$prefix = 'App\\';
|
||||
$base_dir = __DIR__ . '/app/';
|
||||
|
||||
$len = strlen($prefix);
|
||||
if (strncmp($prefix, $class, $len) !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$relative_class = substr($class, $len);
|
||||
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
|
||||
|
||||
if (file_exists($file)) {
|
||||
require $file;
|
||||
}
|
||||
});
|
||||
session_start();
|
||||
|
||||
require_once 'app/Helpers/functions.php';
|
||||
require_once 'db/config.php';
|
||||
|
||||
session_start();
|
||||
|
||||
// Initialize Language Service
|
||||
\App\Services\LanguageService::init();
|
||||
// Simple Autoloader
|
||||
spl_autoload_register(function ($class) {
|
||||
$prefix = 'App\\';
|
||||
$base_dir = __DIR__ . '/app/';
|
||||
$len = strlen($prefix);
|
||||
if (strncmp($prefix, $class, $len) !== 0) {
|
||||
return;
|
||||
}
|
||||
$relative_class = substr($class, $len);
|
||||
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
|
||||
if (file_exists($file)) {
|
||||
require_once $file;
|
||||
}
|
||||
});
|
||||
|
||||
use App\Core\Router;
|
||||
|
||||
// Maintenance Mode Check
|
||||
if (get_setting('maintenance_mode') === '1') {
|
||||
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
$isAdmin = strpos($uri, '/admin') === 0 || strpos($uri, '/login') === 0 || strpos($uri, '/logout') === 0;
|
||||
if (!$isAdmin && !isset($_SESSION['admin_id'])) {
|
||||
require_once 'views/maintenance.php';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$router = new Router();
|
||||
$router->post('/api/newsletter/subscribe', 'NewsletterController@subscribe');
|
||||
$router->post('/api/report', 'ContactController@ajaxReport');
|
||||
$router->post('/api/ai/chat', 'AIController@chat');
|
||||
|
||||
// Sitemap
|
||||
$router->get('/sitemap.xml', 'SitemapController@index');
|
||||
|
||||
// Language Switch
|
||||
$router->get('/lang/:code', function($params) {
|
||||
$code = $params['code'];
|
||||
\App\Services\LanguageService::setLang($code);
|
||||
header('Location: ' . ($_SERVER['HTTP_REFERER'] ?? '/'));
|
||||
exit;
|
||||
});
|
||||
|
||||
// Home & APKs
|
||||
// Routes
|
||||
$router->get('/', 'HomeController@index');
|
||||
$router->get('/apk/:slug', 'HomeController@apkDetail');
|
||||
$router->get('/download/:slug', 'HomeController@download');
|
||||
|
||||
// Blog
|
||||
$router->get('/blog', 'BlogController@index');
|
||||
$router->get('/blog/:slug', 'BlogController@detail');
|
||||
|
||||
// Static Pages
|
||||
$router->get('/contact', 'ContactController@index');
|
||||
$router->post('/contact', 'ContactController@submit');
|
||||
$router->get('/help-center', 'HomeController@helpCenter');
|
||||
$router->get('/privacy-policy', 'HomeController@privacyPolicy');
|
||||
$router->get('/terms-of-service', 'HomeController@termsOfService');
|
||||
$router->get('/apk/:slug', 'ApkController@detail');
|
||||
$router->get('/download/:slug', 'ApkController@download');
|
||||
|
||||
// Auth
|
||||
$router->get('/login', 'AuthController@loginForm');
|
||||
@ -77,36 +35,40 @@ $router->get('/register', 'AuthController@registerForm');
|
||||
$router->post('/register', 'AuthController@register');
|
||||
$router->get('/logout', 'AuthController@logout');
|
||||
$router->get('/profile', 'AuthController@profile');
|
||||
$router->post('/withdraw', 'AuthController@requestWithdrawal');
|
||||
$router->post('/profile', 'AuthController@updateProfile');
|
||||
|
||||
// Admin Auth
|
||||
// Admin Routes
|
||||
$router->get('/admin', 'AdminController@dashboard');
|
||||
$router->get('/admin/login', 'AdminController@loginForm');
|
||||
$router->post('/admin/login', 'AdminController@login');
|
||||
$router->get('/admin/logout', 'AdminController@logout');
|
||||
|
||||
// Admin Dashboard
|
||||
$router->get('/admin/dashboard', 'AdminController@dashboard');
|
||||
|
||||
// Admin Settings
|
||||
$router->get('/admin/settings', 'AdminController@settingsForm');
|
||||
$router->post('/admin/settings', 'AdminController@saveSettings');
|
||||
|
||||
// Admin Users
|
||||
$router->get('/admin/users', 'AdminController@users');
|
||||
$router->post('/admin/users/toggle-ban/:id', 'AdminController@toggleBan');
|
||||
|
||||
// Admin APKs
|
||||
$router->get('/admin/apks', 'AdminController@apks');
|
||||
$router->get('/admin/apks/mass-upload', 'AdminController@massUploadForm');
|
||||
$router->post('/admin/apks/mass-upload', 'AdminController@massUpload');
|
||||
$router->get('/admin/apks/add', 'AdminController@addApkForm');
|
||||
$router->post('/admin/apks/add', 'AdminController@addApk');
|
||||
$router->get('/admin/apks/edit/:id', 'AdminController@editApkForm');
|
||||
$router->post('/admin/apks/edit/:id', 'AdminController@editApk');
|
||||
$router->get('/admin/apks/delete/:id', 'AdminController@deleteApk');
|
||||
$router->post('/admin/apks/reorder', 'AdminController@updateOrder');
|
||||
|
||||
// Admin Posts (Blog)
|
||||
// Admin Categories
|
||||
$router->get('/admin/categories', 'AdminController@categories');
|
||||
$router->post('/admin/categories/add', 'AdminController@addCategory');
|
||||
$router->get('/admin/categories/delete/:id', 'AdminController@deleteCategory');
|
||||
|
||||
// Admin Users
|
||||
$router->get('/admin/users', 'AdminController@users');
|
||||
$router->get('/admin/users/delete/:id', 'AdminController@deleteUser');
|
||||
|
||||
// Admin Settings
|
||||
$router->get('/admin/settings', 'AdminController@settingsForm');
|
||||
$router->post('/admin/settings', 'AdminController@saveSettings');
|
||||
|
||||
// Blog
|
||||
$router->get('/blog', 'BlogController@index');
|
||||
$router->get('/blog/:slug', 'BlogController@detail');
|
||||
|
||||
// Admin Blog
|
||||
$router->get('/admin/posts', 'AdminController@posts');
|
||||
$router->get('/admin/posts/add', 'AdminController@addPostForm');
|
||||
$router->post('/admin/posts/add', 'AdminController@addPost');
|
||||
@ -114,14 +76,19 @@ $router->get('/admin/posts/edit/:id', 'AdminController@editPostForm');
|
||||
$router->post('/admin/posts/edit/:id', 'AdminController@editPost');
|
||||
$router->get('/admin/posts/delete/:id', 'AdminController@deletePost');
|
||||
|
||||
// Admin Categories
|
||||
$router->get('/admin/categories', 'AdminController@categories');
|
||||
$router->post('/admin/categories/add', 'AdminController@addCategory');
|
||||
$router->get('/admin/categories/delete/:id', 'AdminController@deleteCategory');
|
||||
// Newsletter
|
||||
$router->post('/newsletter/subscribe', 'NewsletterController@subscribe');
|
||||
$router->get('/admin/newsletter', 'AdminController@newsletter');
|
||||
$router->get('/admin/newsletter/delete/:id', 'AdminController@deleteSubscriber');
|
||||
$router->get('/admin/newsletter/export', 'AdminController@exportSubscribers');
|
||||
$router->get('/admin/newsletter/send', 'AdminController@sendNewsletterForm');
|
||||
$router->post('/admin/newsletter/send', 'AdminController@sendNewsletter');
|
||||
|
||||
// Admin Withdrawals
|
||||
$router->get('/admin/withdrawals', 'AdminController@withdrawals');
|
||||
$router->get('/admin/withdrawals/approve/:id', 'AdminController@approveWithdrawal');
|
||||
$router->get('/admin/withdrawals/reject/:id', 'AdminController@rejectWithdrawal');
|
||||
// Contact
|
||||
$router->get('/contact', 'ContactController@index');
|
||||
$router->post('/contact', 'ContactController@send');
|
||||
|
||||
// Sitemap
|
||||
$router->get('/sitemap.xml', 'SitemapController@index');
|
||||
|
||||
$router->dispatch();
|
||||
@ -194,6 +194,9 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 mb-1" href="/admin/posts"><i class="fas fa-newspaper me-1"></i> Blog</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 mb-1" href="/admin/newsletter"><i class="fas fa-envelope me-1"></i> Newsletter</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 mb-1" href="/admin/categories"><i class="fas fa-list me-1"></i> <?php echo __('categories'); ?></a>
|
||||
</li>
|
||||
@ -254,4 +257,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="min-vh-100">
|
||||
<main class="min-vh-100">
|
||||
66
views/admin/newsletter/index.php
Normal file
66
views/admin/newsletter/index.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php include 'views/admin/header.php'; ?>
|
||||
|
||||
<div class="container-fluid py-4">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header pb-0 d-flex justify-content-between align-items-center">
|
||||
<h6>Newsletter Subscribers</h6>
|
||||
<div>
|
||||
<a href="/admin/newsletter/send" class="btn btn-sm btn-primary me-2">
|
||||
<i class="fas fa-paper-plane me-1"></i> Send Email
|
||||
</a>
|
||||
<a href="/admin/newsletter/export" class="btn btn-sm btn-success">
|
||||
<i class="fas fa-file-export me-1"></i> Export CSV
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body px-0 pt-0 pb-2">
|
||||
<div class="table-responsive p-0">
|
||||
<table class="table align-items-center mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7">Email</th>
|
||||
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2">Subscribed At</th>
|
||||
<th class="text-secondary opacity-7"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($subscribers as $subscriber): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex px-2 py-1">
|
||||
<div class="d-flex flex-column justify-content-center">
|
||||
<h6 class="mb-0 text-sm"><?php echo htmlspecialchars($subscriber['email']); ?></h6>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<p class="text-xs font-weight-bold mb-0"><?php echo $subscriber['created_at']; ?></p>
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
<a href="/admin/newsletter/delete/<?php echo $subscriber['id']; ?>"
|
||||
class="text-secondary font-weight-bold text-xs"
|
||||
onclick="return confirm('Are you sure you want to delete this subscriber?')">
|
||||
Delete
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($subscribers)): ?>
|
||||
<tr>
|
||||
<td colspan="3" class="text-center py-4">
|
||||
<p class="text-xs font-weight-bold mb-0">No subscribers found.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'views/admin/footer.php'; ?>
|
||||
52
views/admin/newsletter/send.php
Normal file
52
views/admin/newsletter/send.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php include 'views/admin/header.php'; ?>
|
||||
|
||||
<div class="container-fluid py-4">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header pb-0 d-flex justify-content-between align-items-center">
|
||||
<h6>Send Newsletter</h6>
|
||||
<a href="/admin/newsletter" class="btn btn-sm btn-secondary">Back to List</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger text-white">
|
||||
<?php echo $error; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($success)): ?>
|
||||
<div class="alert alert-success text-white">
|
||||
<?php echo $success; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="alert alert-info text-white mb-4">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
This email will be sent to all <strong><?php echo $total_subscribers; ?></strong> subscribers via BCC.
|
||||
</div>
|
||||
|
||||
<form action="/admin/newsletter/send" method="POST">
|
||||
<div class="form-group mb-3">
|
||||
<label for="subject" class="form-control-label">Subject</label>
|
||||
<input class="form-control" type="text" name="subject" id="subject" required placeholder="Enter newsletter subject">
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="message" class="form-control-label">Message (HTML supported)</label>
|
||||
<textarea class="form-control" name="message" id="message" rows="10" required placeholder="Write your message here..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="text-end">
|
||||
<button type="submit" class="btn btn-primary" onclick="return confirm('Are you sure you want to send this email to all subscribers?')">
|
||||
<i class="fas fa-paper-plane me-2"></i> Send to All Subscribers
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'views/admin/footer.php'; ?>
|
||||
Loading…
x
Reference in New Issue
Block a user