implemented step 18 by Gemini
- Contact formular and emailer script
This commit is contained in:
@ -71,18 +71,44 @@ class Renderer
|
||||
}
|
||||
|
||||
$projectExportDir = $this->exportsPath . DIRECTORY_SEPARATOR . $projectId;
|
||||
$assetsDir = $projectExportDir . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR . 'css';
|
||||
$assetsDir = $projectExportDir . DIRECTORY_SEPARATOR . 'assets';
|
||||
$cssDir = $assetsDir . DIRECTORY_SEPARATOR . 'css';
|
||||
$jsDir = $assetsDir . DIRECTORY_SEPARATOR . 'js';
|
||||
|
||||
// 1. Create directory structure
|
||||
if (!is_dir($assetsDir)) {
|
||||
mkdir($assetsDir, 0777, true);
|
||||
}
|
||||
if (!is_dir($cssDir)) mkdir($cssDir, 0777, true);
|
||||
if (!is_dir($jsDir)) mkdir($jsDir, 0777, true);
|
||||
|
||||
// 2. Copy static assets
|
||||
$siteCssSource = $this->templatesPath . DIRECTORY_SEPARATOR . 'css' . DIRECTORY_SEPARATOR . 'site.css';
|
||||
copy($siteCssSource, $assetsDir . DIRECTORY_SEPARATOR . 'site.css');
|
||||
copy($this->templatesPath . '/css/site.css', $cssDir . '/site.css');
|
||||
copy($this->templatesPath . '/js/site.js', $jsDir . '/site.js');
|
||||
|
||||
// 3. Prepare relative paths for assets
|
||||
// 3. Handle Contact Form Logic
|
||||
$formConfig = $projectData['wizard_data']['modules']['contact_form'] ?? [];
|
||||
if (!empty($formConfig['enabled'])) {
|
||||
// Copy handler
|
||||
copy($this->templatesPath . '/emailer.php', $projectExportDir . '/ajax.php');
|
||||
|
||||
// Create config for handler
|
||||
$siteConfig = [
|
||||
'site_name' => $projectData['wizard_data']['identity']['business_name'],
|
||||
'form_mode' => $formConfig['mode'] ?? 'local',
|
||||
'smtp' => $formConfig['smtp'] ?? null,
|
||||
'local_password_hash' => $formConfig['local_viewer']['password_hash'] ?? null
|
||||
];
|
||||
file_put_contents($projectExportDir . '/config.json', json_encode($siteConfig, JSON_PRETTY_PRINT));
|
||||
|
||||
if ($siteConfig['form_mode'] === 'local') {
|
||||
// Copy viewer
|
||||
copy($this->templatesPath . '/form-viewer.php', $projectExportDir . '/form-viewer.php');
|
||||
// Create messages dir
|
||||
$msgDir = $projectExportDir . '/messages';
|
||||
if (!is_dir($msgDir)) mkdir($msgDir, 0777, true);
|
||||
file_put_contents($msgDir . '/.htaccess', "Deny from all");
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Prepare relative paths for assets
|
||||
if (!empty($projectData['wizard_data']['assets']['logo'])) {
|
||||
$projectData['wizard_data']['assets']['logo'] = $this->makePathRelative($projectData['wizard_data']['assets']['logo'], $projectId);
|
||||
}
|
||||
|
||||
@ -82,5 +82,6 @@ $assets = $project['wizard_data']['assets'] ?? [];
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="assets/js/site.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
82
src/Templates/emailer.php
Normal file
82
src/Templates/emailer.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* Universal Form Handler for WebWizard exported sites.
|
||||
* Handles both SMTP mailing and local file storage.
|
||||
*/
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$config = [];
|
||||
if (file_exists('config.json')) {
|
||||
$config = json_decode(file_get_contents('config.json'), true);
|
||||
}
|
||||
|
||||
function sendResponse(bool $success, string $message) {
|
||||
echo json_encode(['success' => $success, 'message' => $message]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
sendResponse(false, 'Method not allowed.');
|
||||
}
|
||||
|
||||
// Honeypot spam protection
|
||||
if (!empty($_POST['website'])) {
|
||||
sendResponse(true, 'Spam detected.'); // Fake success for bots
|
||||
}
|
||||
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
$message = trim($_POST['message'] ?? '');
|
||||
|
||||
if (!$name || !$email || !$message) {
|
||||
sendResponse(false, 'Prosím vyplňte všetky povinné polia.');
|
||||
}
|
||||
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
sendResponse(false, 'Neplatná emailová adresa.');
|
||||
}
|
||||
|
||||
$mode = $config['form_mode'] ?? 'local';
|
||||
|
||||
if ($mode === 'local') {
|
||||
// Save to messages directory
|
||||
$msgDir = 'messages';
|
||||
if (!is_dir($msgDir)) {
|
||||
mkdir($msgDir, 0777, true);
|
||||
}
|
||||
|
||||
$msgId = date('Ymd-His') . '-' . bin2hex(random_bytes(4));
|
||||
$msgData = [
|
||||
'id' => $msgId,
|
||||
'timestamp' => date('c'),
|
||||
'sender_name' => $name,
|
||||
'sender_email' => $email,
|
||||
'message' => $message
|
||||
];
|
||||
|
||||
if (file_put_contents($msgDir . '/' . $msgId . '.json', json_encode($msgData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) {
|
||||
sendResponse(true, 'Vaša správa bola úspešne uložená.');
|
||||
} else {
|
||||
sendResponse(false, 'Nepodarilo sa uložiť správu.');
|
||||
}
|
||||
} else {
|
||||
// SMTP / Mail mode
|
||||
$to = $config['smtp']['recipient'] ?? '';
|
||||
if (!$to) {
|
||||
sendResponse(false, 'Cieľový email nie je nakonfigurovaný.');
|
||||
}
|
||||
|
||||
$subject = "Nová správa z webu: " . ($config['site_name'] ?? 'WebWizard');
|
||||
$body = "Meno: $name\nEmail: $email\n\nSpráva:\n$message";
|
||||
$headers = "From: webwizard@{$_SERVER['HTTP_HOST']}\r\n" .
|
||||
"Reply-To: $email\r\n" .
|
||||
"X-Mailer: PHP/" . phpversion();
|
||||
|
||||
// Using standard mail() as fallback for MVP if no complex SMTP is provided
|
||||
if (mail($to, $subject, $body, $headers)) {
|
||||
sendResponse(true, 'Vaša správa bola úspešne odoslaná.');
|
||||
} else {
|
||||
sendResponse(false, 'Nepodarilo sa odoslať email.');
|
||||
}
|
||||
}
|
||||
95
src/Templates/form-viewer.php
Normal file
95
src/Templates/form-viewer.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* Local Message Viewer for WebWizard exported sites.
|
||||
*/
|
||||
|
||||
session_start();
|
||||
|
||||
$config = [];
|
||||
if (file_exists('config.json')) {
|
||||
$config = json_decode(file_get_contents('config.json'), true);
|
||||
}
|
||||
|
||||
$passwordHash = $config['local_password_hash'] ?? null;
|
||||
|
||||
// Handle Logout
|
||||
if (isset($_GET['logout'])) {
|
||||
session_destroy();
|
||||
header('Location: form-viewer.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Handle Login
|
||||
$error = '';
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['password'])) {
|
||||
if ($passwordHash && password_verify($_POST['password'], $passwordHash)) {
|
||||
$_SESSION['ww_authorized'] = true;
|
||||
} else {
|
||||
$error = 'Nesprávne heslo.';
|
||||
}
|
||||
}
|
||||
|
||||
$isAuthorized = $_SESSION['ww_authorized'] ?? false;
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="sk">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Prehliadač správ - WebWizard</title>
|
||||
<style>
|
||||
body { font-family: sans-serif; background: #f4f4f4; padding: 2rem; color: #333; }
|
||||
.container { max-width: 800px; margin: 0 auto; background: #fff; padding: 2rem; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
|
||||
h1 { border-bottom: 2px solid #eee; padding-bottom: 1rem; }
|
||||
.msg-item { border: 1px solid #eee; padding: 1rem; margin-bottom: 1rem; border-radius: 4px; }
|
||||
.msg-header { display: flex; justify-content: space-between; font-size: 0.85rem; color: #666; margin-bottom: 0.5rem; }
|
||||
.msg-sender { font-weight: bold; color: #2563eb; }
|
||||
.login-form { text-align: center; padding: 2rem 0; }
|
||||
input[type="password"] { padding: 0.75rem; width: 250px; border: 1px solid #ddd; border-radius: 4px; }
|
||||
button { padding: 0.75rem 1.5rem; background: #2563eb; color: #fff; border: none; border-radius: 4px; cursor: pointer; }
|
||||
.error { color: #dc2626; margin-bottom: 1rem; }
|
||||
.no-messages { text-align: center; color: #666; padding: 2rem; }
|
||||
.logout-link { float: right; font-size: 0.9rem; color: #666; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<?php if (!$isAuthorized): ?>
|
||||
<div class="login-form">
|
||||
<h1>Prihlásenie</h1>
|
||||
<?php if ($error): ?><div class="error"><?= $error ?></div><?php endif; ?>
|
||||
<form method="POST">
|
||||
<input type="password" name="password" placeholder="Heslo k správam" required autofocus>
|
||||
<button type="submit">Vstúpiť</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<a href="?logout=1" class="logout-link">Odhlásiť sa</a>
|
||||
<h1>Prijaté správy</h1>
|
||||
|
||||
<?php
|
||||
$files = glob('messages/*.json');
|
||||
rsort($files); // Newest first
|
||||
|
||||
if (empty($files)): ?>
|
||||
<p class="no-messages">Zatiaľ ste neprijali žiadne správy.</p>
|
||||
<?php else:
|
||||
foreach ($files as $file):
|
||||
$data = json_decode(file_get_contents($file), true);
|
||||
if ($data): ?>
|
||||
<div class="msg-item">
|
||||
<div class="msg-header">
|
||||
<span><?= htmlspecialchars($data['timestamp']) ?></span>
|
||||
<span>ID: <?= htmlspecialchars($data['id']) ?></span>
|
||||
</div>
|
||||
<div>Od: <span class="msg-sender"><?= htmlspecialchars($data['sender_name']) ?></span> (<<?= htmlspecialchars($data['sender_email']) ?>>)</div>
|
||||
<div style="margin-top: 1rem; white-space: pre-wrap;"><?= htmlspecialchars($data['message']) ?></div>
|
||||
</div>
|
||||
<?php endif;
|
||||
endforeach;
|
||||
endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
40
src/Templates/js/site.js
Normal file
40
src/Templates/js/site.js
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* WebWizard Site Scripts
|
||||
*/
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const contactForm = document.getElementById('site-contact-form');
|
||||
|
||||
if (contactForm) {
|
||||
contactForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(contactForm);
|
||||
const submitBtn = contactForm.querySelector('button[type="submit"]');
|
||||
const originalBtnText = submitBtn.textContent;
|
||||
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = 'Odosielam...';
|
||||
|
||||
try {
|
||||
const response = await fetch(contactForm.action, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
alert(result.message);
|
||||
|
||||
if (result.success) {
|
||||
contactForm.reset();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Form submission error:', error);
|
||||
alert('Vyskytla sa chyba pri odosielaní formulára. Skúste to prosím neskôr.');
|
||||
} finally {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = originalBtnText;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -33,6 +33,9 @@ $formEnabled = $project['wizard_data']['modules']['contact_form']['enabled'] ??
|
||||
<div class="contact-form">
|
||||
<h3>Napíšte nám</h3>
|
||||
<form action="ajax.php" method="POST" id="site-contact-form">
|
||||
<!-- Honeypot -->
|
||||
<input type="text" name="website" style="display:none !important" tabindex="-1" autocomplete="off">
|
||||
|
||||
<div class="form-group">
|
||||
<input type="text" name="name" placeholder="Vaše meno" required>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user