diff --git a/php/.htaccess b/php/.htaccess new file mode 100644 index 0000000..c69da62 --- /dev/null +++ b/php/.htaccess @@ -0,0 +1,25 @@ +# Prevent directory browsing +Options -Indexes + +# Enable rewrite engine (optional, for clean URLs) +RewriteEngine On + +# Redirect /semesterapparat to semesterapparat.php +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^semesterapparat$ semesterapparat.php [L] + +# Redirect /elsa to elsa.php +RewriteRule ^elsa$ elsa.php [L] + +# Protect config files + + Order allow,deny + Deny from all + + +# Set default charset +AddDefaultCharset UTF-8 + +# Error pages (customize as needed) +ErrorDocument 404 /index.php diff --git a/php/README.md b/php/README.md new file mode 100644 index 0000000..54126cb --- /dev/null +++ b/php/README.md @@ -0,0 +1,155 @@ +# PHP Deployment Guide for SemapForm + +## Requirements + +- PHP 7.4 or higher (PHP 8+ recommended) +- PHP extensions: + - `dom` (for XML generation) + - `mbstring` (for string handling) + - `openssl` (for SMTP if using SSL/TLS) +- Web server (Apache, Nginx, or any PHP-capable server) +- Optional: PHPMailer library for advanced SMTP support + +## Installation Steps + +1. **Upload files to your PHP server:** + ``` + php/ + ├── index.php + ├── semesterapparat.php + ├── submit_semesterapparat.php + ├── config.php + ├── functions.php + ├── .htaccess (if using Apache) + └── static/ + └── styles.css + ``` + +2. **Configure email settings:** + + Edit `config.php` or set environment variables: + - `MAIL_ENABLED`: Set to `true` to enable email sending + - `SMTP_HOST`: Your SMTP server (e.g., smtp.ph-freiburg.de) + - `SMTP_PORT`: SMTP port (465 for SSL, 587 for TLS, 25 for plain) + - `SMTP_USERNAME`: Your SMTP username + - `SMTP_PASSWORD`: Your SMTP password + - `MAIL_FROM`: Sender email address + - `MAIL_TO`: Recipient email address + +3. **Set permissions:** + ```bash + chmod 755 *.php + chmod 644 config.php + chmod 644 static/styles.css + ``` + +4. **Test the installation:** + - Navigate to your server URL (e.g., `https://yourserver.com/php/`) + - Try submitting a test form + - Check email delivery or server logs + +## Email Configuration Options + +### Option 1: PHP's built-in mail() function +- Simplest setup +- Requires server to have mail transfer agent (MTA) configured +- No additional configuration needed in PHP + +### Option 2: SMTP with PHP's native functions +- Configure SMTP settings in `config.php` +- Works with basic authentication +- Current implementation in `functions.php` + +### Option 3: PHPMailer (Recommended for production) +- Install PHPMailer via Composer: + ```bash + composer require phpmailer/phpmailer + ``` +- Better error handling and SMTP support +- Already integrated in `functions.php` (will auto-detect if available) + +## Server-Specific Notes + +### Apache +- The `.htaccess` file is included for URL rewriting and security +- Ensure `mod_rewrite` is enabled + +### Nginx +- Add this to your nginx.conf: + ```nginx + location /php { + index index.php; + try_files $uri $uri/ /index.php?$query_string; + + location ~ \.php$ { + fastcgi_pass unix:/var/run/php/php-fpm.sock; + fastcgi_index index.php; + include fastcgi_params; + } + } + ``` + +### Shared Hosting +- Most shared hosts have PHP and mail() pre-configured +- Upload files via FTP/SFTP +- Set environment variables through hosting control panel +- Test mail() function first before adding SMTP + +## Troubleshooting + +### Emails not sending +1. Check `MAIL_ENABLED` is set to `true` +2. Verify SMTP credentials are correct +3. Check server error logs: `tail -f /var/log/apache2/error.log` +4. Test with `MAIL_ENABLED=false` to see XML output in logs + +### Form validation errors +- Ensure all required fields have values +- Check email format validation +- Verify POST data is being received + +### Permission errors +- Ensure PHP has read access to all files +- Check web server user/group permissions + +## Security Recommendations + +1. **Move config.php outside web root** if possible +2. **Use environment variables** for sensitive data +3. **Enable HTTPS** for form submissions +4. **Sanitize all inputs** (already implemented in `functions.php`) +5. **Set production error reporting** in `config.php`: + ```php + error_reporting(0); + ini_set('display_errors', 0); + ``` +6. **Regular updates**: Keep PHP and server software updated + +## Migrating from Docker/Python + +The PHP version maintains feature parity with the Python/FastAPI version: +- ✅ Same form fields and validation +- ✅ XML generation with identical structure +- ✅ Email sending with SMTP support +- ✅ Same CSS and frontend behavior +- ✅ Theme toggle functionality +- ✅ Multi-book/media support +- ✅ Optional fields (title, signature, Dauerapparat) + +## Environment Variables + +You can set these as server environment variables instead of editing `config.php`: + +```bash +export MAIL_ENABLED=true +export SMTP_HOST=smtp.ph-freiburg.de +export SMTP_PORT=465 +export MAIL_USERNAME=your_username +export MAIL_PASSWORD=your_password +export MAIL_FROM=alexander.kirchner@ph-freiburg.de +export MAIL_TO=semesterapparate@ph-freiburg.de +``` + +## Support + +For issues specific to your hosting environment, consult your hosting provider's PHP documentation. diff --git a/php/config.php b/php/config.php new file mode 100644 index 0000000..56db04c --- /dev/null +++ b/php/config.php @@ -0,0 +1,28 @@ + + + + + ELSA - Elektronischer Semesterapparat + + + + + + + +
+
+

ELSA - Elektronischer Semesterapparat

+ + + +
+

Allgemeine Informationen

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +

Medien

+
+ + + +
+ +
+ +
+ + +
+ +
+ +
+
+
+
+ + + + diff --git a/php/functions.php b/php/functions.php new file mode 100644 index 0000000..aad384d --- /dev/null +++ b/php/functions.php @@ -0,0 +1,218 @@ +formatOutput = true; + + $root = $xml->createElement('form_submission'); + $xml->appendChild($root); + + // Static data + $static = $xml->createElement('static'); + $root->appendChild($static); + + foreach (['name', 'lastname', 'title', 'telno', 'mail', 'apparatsname', 'subject', 'semester', 'dauerapparat'] as $field) { + if (isset($data[$field])) { + $element = $xml->createElement($field, htmlspecialchars($data[$field])); + $static->appendChild($element); + } + } + + if (!empty($data['message'])) { + $messageEl = $xml->createElement('message', htmlspecialchars($data['message'])); + $static->appendChild($messageEl); + } + + // Books + if (isset($data['books']) && is_array($data['books'])) { + $booksNode = $xml->createElement('books'); + $root->appendChild($booksNode); + + foreach ($data['books'] as $book) { + $bookNode = $xml->createElement('book'); + $booksNode->appendChild($bookNode); + + foreach (['authorname', 'year', 'title', 'signature'] as $field) { + $value = isset($book[$field]) ? htmlspecialchars($book[$field]) : ''; + $fieldNode = $xml->createElement($field, $value); + $bookNode->appendChild($fieldNode); + } + } + } + + return $xml->saveXML(); +} + +/** + * Generate XML for ELSA form + */ +function generateELSAXML($data) { + $xml = new DOMDocument('1.0', 'UTF-8'); + $xml->formatOutput = true; + + $root = $xml->createElement('elsa_submission'); + $xml->appendChild($root); + + // General info + $generalInfo = $xml->createElement('general_info'); + $root->appendChild($generalInfo); + + $generalFields = ['name', 'lastname', 'title', 'mail', 'subject', 'classname', + 'usage_date_from', 'usage_date_to', 'availability_date']; + + foreach ($generalFields as $field) { + if (isset($data[$field])) { + $element = $xml->createElement($field, htmlspecialchars($data[$field])); + $generalInfo->appendChild($element); + } + } + + if (!empty($data['message'])) { + $messageEl = $xml->createElement('message', htmlspecialchars($data['message'])); + $generalInfo->appendChild($messageEl); + } + + // Media sections + $mediaRoot = $xml->createElement('media'); + $root->appendChild($mediaRoot); + + // Add different media types (monografie, zeitschrift, herausgeber) + $mediaTypes = [ + 'monografien' => $data['monografien'] ?? [], + 'zeitschriftenartikel' => $data['zeitschriftenartikel'] ?? [], + 'herausgeberwerke' => $data['herausgeberwerke'] ?? [] + ]; + + foreach ($mediaTypes as $type => $entries) { + if (!empty($entries)) { + $section = $xml->createElement($type); + $mediaRoot->appendChild($section); + + foreach ($entries as $entry) { + $entryNode = $xml->createElement('entry'); + $section->appendChild($entryNode); + + foreach ($entry as $key => $value) { + $fieldNode = $xml->createElement($key, htmlspecialchars($value)); + $entryNode->appendChild($fieldNode); + } + } + } + } + + return $xml->saveXML(); +} + +/** + * Send email with XML attachment + * Uses PHP's mail() function or SMTP if configured + */ +function sendEmail($subject, $xmlContent, $toEmail = null) { + $to = $toEmail ?? MAIL_TO; + $from = MAIL_FROM; + + if (!MAIL_ENABLED) { + // Log instead of sending + error_log("=========================================================="); + error_log("MAIL SENDING DISABLED - Would have sent:"); + error_log("From: " . $from); + error_log("To: " . $to); + error_log("Subject: " . $subject); + error_log("----------------------------------------------------------"); + error_log($xmlContent); + error_log("=========================================================="); + return true; + } + + // Try using SMTP if credentials are configured + if (SMTP_USERNAME && SMTP_PASSWORD && class_exists('PHPMailer\PHPMailer\PHPMailer')) { + return sendEmailSMTP($subject, $xmlContent, $to, $from); + } + + // Fallback to PHP mail() + $headers = "From: " . $from . "\r\n"; + $headers .= "Content-Type: application/xml; charset=UTF-8\r\n"; + $headers .= "X-Mailer: PHP/" . phpversion(); + + return mail($to, $subject, $xmlContent, $headers); +} + +/** + * Send email via SMTP using PHPMailer (if available) + */ +function sendEmailSMTP($subject, $xmlContent, $to, $from) { + if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { + error_log("PHPMailer not available, falling back to mail()"); + return false; + } + + $mail = new PHPMailer\PHPMailer\PHPMailer(true); + + try { + // Server settings + $mail->isSMTP(); + $mail->Host = SMTP_HOST; + $mail->SMTPAuth = true; + $mail->Username = SMTP_USERNAME; + $mail->Password = SMTP_PASSWORD; + $mail->SMTPSecure = SMTP_ENCRYPTION; + $mail->Port = SMTP_PORT; + $mail->CharSet = 'UTF-8'; + + // Recipients + $mail->setFrom($from); + $mail->addAddress($to); + + // Content + $mail->Subject = $subject; + $mail->Body = $xmlContent; + $mail->ContentType = 'application/xml'; + + $mail->send(); + return true; + } catch (Exception $e) { + error_log("Email sending failed: " . $mail->ErrorInfo); + return false; + } +} + +/** + * Redirect to URL + */ +function redirect($url) { + header("Location: " . $url); + exit; +} + +/** + * Get POST value with default + */ +function post($key, $default = '') { + return isset($_POST[$key]) ? sanitizeInput($_POST[$key]) : $default; +} + +/** + * Get all POST values matching a pattern (for arrays) + */ +function postArray($key) { + return isset($_POST[$key]) && is_array($_POST[$key]) ? + array_map('sanitizeInput', $_POST[$key]) : []; +} diff --git a/php/index.php b/php/index.php new file mode 100644 index 0000000..243edaa --- /dev/null +++ b/php/index.php @@ -0,0 +1,138 @@ + + + + + PH Freiburg Bibliothek - Formulare + + + + + + + +
+
+

Willkommen

+

Bitte wählen Sie den gewünschten Service:

+
+ +
+ +
+ +
+

Semesterapparat

+

Antrag für die Einrichtung eines Semesterapparats

+
+ +
+
+ + +
+ +
+

ELSA

+

Elektronischer Semesterapparat

+
+ +
+
+
+ +
+
+

Hinweise

+

+ Weitere Informationen zu den Semesterapparaten und elektronischen Angeboten finden Sie auf den Seiten der Hochschulbibliothek. +

+ +
+
+
+ +
+
+ +
+
+
Erfolgreich gesendet
+
Mail wurde geschickt, Sie erhalten demnächst mehr Informationen
+
+ +
+ + + + diff --git a/php/semesterapparat.php b/php/semesterapparat.php new file mode 100644 index 0000000..064c03e --- /dev/null +++ b/php/semesterapparat.php @@ -0,0 +1,198 @@ + + + + + Semesterapparatsantrag + + + + + + + +
+
+

Semesterapparatsinformationen

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+
+ + + +
+
+ + +
+
+ +
+ + + +
+
+ +
+
+
+ +

Medien

+ + + + + + + + + + +
AutorennameJahr/AuflageTitelSignatur (wenn vorhanden)
+ + +
+ + +
+ +
+ +
+
+ + +
+
+ + + + diff --git a/php/static/styles.css b/php/static/styles.css new file mode 100644 index 0000000..e7b0a98 --- /dev/null +++ b/php/static/styles.css @@ -0,0 +1,602 @@ +:root { + --bg: #f7f8fb; + --card-bg: #ffffff; + --text: #1f2937; + --muted: #6b7280; + --primary: #0b7bd6; /* Accessible blue */ + --primary-600: #0a6ec0; + --primary-700: #095fa6; + --border: #e5e7eb; + --ring: rgba(11, 123, 214, 0.35); + --table-head-bg: #f3f4f6; + --input-bg: #ffffff; + --control-accent: var(--primary); +} + +/* Dark theme variables */ +[data-theme="dark"] { + --bg: #0b1220; + --card-bg: #121a2a; + --text: #e5e7eb; + --muted: #9aa4b2; + --primary: #5aa1ff; + --primary-600: #4b90ea; + --primary-700: #3c7ace; + --border: #243045; + --ring: rgba(90, 161, 255, 0.35); + --table-head-bg: #172136; + --input-bg: #0f1726; + --control-accent: #2a3448; +} + +* { box-sizing: border-box; } + +html, body { height: 100%; } + +body { + margin: 0; + font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; + color: var(--text); + background: var(--bg); + line-height: 1.5; +} + +/* Header */ +.site-header { + background: var(--card-bg); + border-bottom: 1px solid var(--border); + position: sticky; + top: 0; + z-index: 10; +} +.header-inner { + display: flex; + align-items: center; + gap: 14px; + height: 72px; + padding: 14px 0; + justify-content: space-between; +} +.header-left { + display: flex; + align-items: center; + gap: 14px; +} +.logo { height: 48px; width: auto; } +.brand-title { font-weight: 700; letter-spacing: .2px; } +.brand-sub { color: var(--muted); font-size: .95rem; } + +.theme-toggle { + display: inline-flex; + align-items: center; + gap: 6px; + scale: 2; + border-radius: 9999px; + border: none; + background: var(--card-bg); + color: var(--text); + cursor: pointer; +} +.theme-toggle:hover { filter: brightness(1.05); } +.theme-toggle:focus-visible { outline: none; box-shadow: 0 0 0 4px var(--ring); } + +/* Layout */ +.container { + max-width: 1250px; + margin: 0 auto; + padding: 0 20px; +} + +.card { + background: var(--card-bg); + margin: 24px 0; + padding: 22px; + border: 1px solid var(--border); + border-radius: 14px; + box-shadow: 0 8px 30px rgba(0,0,0,.05); +} + +h1 { + margin: 0 0 14px; + font-size: 1.5rem; +} + +h2 { + margin: 20px 0 10px; + font-size: 1.25rem; +} + +h3 { + margin: 14px 0 10px; + font-size: 1.1rem; +} + +/* Tables */ +.table-wrapper { overflow-x: auto; } + +.form-table, +.data-table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + background: var(--card-bg); + border: none; + border-radius: 12px; + overflow: hidden; + position: relative; +} + +.form-table::after, +.data-table::after { + content: ""; + position: absolute; + inset: 0; + border: 1px solid var(--border); + border-radius: 12px; + pointer-events: none; +} + +.form-table td, +.form-table th { + padding: 10px 12px; + border-bottom: 1px solid var(--border); +} + +.data-table td, +.data-table th { + padding: 10px 12px; + border-bottom: 0; +} + +.form-table tr:last-child td, +.data-table tr:last-child td { border-bottom: 0; } + +.data-table th { + background: var(--table-head-bg); + text-align: left; + font-weight: 600; +} + +/* Inputs */ +input[type="text"], +input[type="email"], +input[type="number"], +input[type="tel"], +input[type="date"], +select, +textarea { + width: 100%; + padding: 10px 12px; + border: 1px solid var(--border); + border-radius: 10px; + background: var(--input-bg); + color: var(--text); + outline: none; + transition: border-color .2s ease, box-shadow .2s ease; + font-family: inherit; +} + +input:focus, +select:focus, +textarea:focus { + border-color: var(--primary); + box-shadow: 0 0 0 4px var(--ring); +} + +input[type="radio"], +input[type="checkbox"] { + accent-color: var(--control-accent); +} + +.radio { margin-right: 12px; } + +/* Buttons */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 10px 16px; + border-radius: 10px; + border: 1px solid transparent; + cursor: pointer; + font-weight: 600; + transition: background-color .2s ease, color .2s ease, border-color .2s ease, transform .02s ease; +} + +.btn:active { transform: translateY(1px); } + +.btn-primary { + background: var(--primary); + color: #fff; +} +.btn-primary:hover { background: var(--primary-600); } +.btn-primary:focus { box-shadow: 0 0 0 4px var(--ring); } + +.btn-secondary { + background: #e7eef8; + color: var(--primary-700); + border-color: #cfd9ea; +} +.btn-secondary:hover { background: #dfe8f6; } +.btn-secondary:disabled { + opacity: 0.5; + cursor: not-allowed; + background: #e7eef8; + color: var(--muted); +} +.btn-secondary:disabled:hover { + background: #e7eef8; +} + +[data-theme="dark"] .btn-secondary { + background: #1c2637; + color: var(--text); + border-color: #2a3852; +} +[data-theme="dark"] .btn-secondary:hover { background: #1f2b3f; } +[data-theme="dark"] .btn-secondary:disabled { + opacity: 0.5; + background: #1c2637; + color: var(--muted); +} +[data-theme="dark"] .btn-secondary:disabled:hover { + background: #1c2637; +} + +.actions { + margin-top: 16px; + display: flex; + justify-content: flex-end; +} + +.footer-note { + margin-top: 18px; + color: var(--muted); +} +.footer-note a { color: var(--primary-700); text-decoration: none; } +.footer-note a:hover { text-decoration: underline; } + +/* Static Information grid */ +.grid-form { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 12px 16px; + margin-bottom: 16px; +} +.form-field { + display: flex; + flex-direction: column; + gap: 6px; +} +.form-field label { + font-size: 0.9rem; + color: var(--muted); +} + +.field-info { + margin-top: 4px; + font-size: 0.85rem; + color: var(--primary-700); +} +[data-theme="dark"] .field-info { color: #a9c8ff; } +.hidden { display: none !important; } + +.inline-controls { + display: flex; + align-items: center; + gap: 12px; + justify-content: center; + text-align: center; +} +.inline-controls.start { + justify-content: flex-start; + text-align: left; +} +.inline-controls input[type="number"] { + width: 120px; + text-align: center; +} + +/* Landing page styles */ +.landing-hero { + text-align: center; + margin: 40px 0 30px; +} +.landing-hero h1 { + font-size: 2.5rem; + margin-bottom: 10px; +} +.hero-subtitle { + font-size: 1.1rem; + color: var(--muted); +} + +.service-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 20px; + margin: 30px 0; +} + +.service-card { + background: var(--card-bg); + border: 1px solid var(--border); + border-radius: 14px; + padding: 24px; + text-decoration: none; + color: var(--text); + transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease; + display: flex; + flex-direction: column; + position: relative; +} + +.service-card:hover { + transform: translateY(-4px); + box-shadow: 0 12px 40px rgba(0,0,0,.1); + border-color: var(--primary); +} + +.service-icon { + font-size: 3rem; + color: var(--primary); + margin-bottom: 12px; +} + +.service-card h2 { + margin: 0 0 8px; + font-size: 1.4rem; +} + +.service-card p { + color: var(--muted); + margin: 0; + flex-grow: 1; +} + +.service-arrow { + position: absolute; + bottom: 20px; + right: 20px; + font-size: 1.5rem; + color: var(--primary); + opacity: 0; + transition: opacity .2s ease, transform .2s ease; +} + +.service-card:hover .service-arrow { + opacity: 1; + transform: translateX(4px); +} + +.info-section { + margin-top: 40px; +} + +.info-links { + display: flex; + gap: 20px; + margin-top: 12px; + flex-wrap: wrap; +} + +.info-links a { + display: inline-flex; + align-items: center; + gap: 6px; + color: var(--primary); + text-decoration: none; + font-weight: 500; +} + +.info-links a:hover { + text-decoration: underline; +} + +/* Toast notification */ +.toast { + position: fixed; + top: 20px; + right: 20px; + background: var(--card-bg); + border: 1px solid var(--border); + border-radius: 12px; + padding: 16px; + box-shadow: 0 8px 30px rgba(0,0,0,.15); + display: flex; + align-items: center; + gap: 12px; + max-width: 400px; + z-index: 1000; + opacity: 0; + transform: translateY(-20px); + transition: opacity .3s ease, transform .3s ease; +} + +.toast.show { + opacity: 1; + transform: translateY(0); +} + +.toast-icon { + font-size: 1.5rem; + color: #10b981; +} + +.toast-content { + flex-grow: 1; +} + +.toast-title { + font-weight: 600; + margin-bottom: 2px; +} + +.toast-message { + font-size: 0.9rem; + color: var(--muted); +} + +.toast-close { + background: none; + border: none; + color: var(--muted); + cursor: pointer; + font-size: 1.2rem; + padding: 4px; + line-height: 1; +} + +.toast-close:hover { + color: var(--text); +} + +/* Responsive */ +@media (max-width: 1024px) { + .grid-form { grid-template-columns: repeat(2, minmax(0, 1fr)); } +} + +@media (max-width: 720px) { + .header-inner { padding: 12px 0; height: auto; } + .logo { height: 40px; } + .form-table td:first-child { width: 40%; } + .landing-hero h1 { font-size: 2rem; } +} + +@media (max-width: 640px) { + .grid-form { grid-template-columns: 1fr; } + .inline-controls { flex-wrap: wrap; } + .toast { + left: 10px; + right: 10px; + max-width: none; + } +} + +.data-table + .btn { margin-top: 14px; } + +/* ELSA-specific styles */ +.legal-notice { + background: rgba(11, 123, 214, 0.1); + border-left: 4px solid var(--primary); + padding: 16px 20px; + margin: 20px 0; + border-radius: 8px; +} + +.legal-notice h3 { + display: flex; + align-items: center; + gap: 8px; + margin: 0 0 10px; + color: var(--primary); +} + +.legal-notice p { + margin: 0; + line-height: 1.6; +} + +[data-theme="dark"] .legal-notice { + background: rgba(90, 161, 255, 0.15); + border-left-color: var(--primary); +} + +.media-controls { + display: flex; + gap: 12px; + margin: 20px 0; + flex-wrap: wrap; +} + +.media-section { + background: var(--card-bg); + border: 1px solid var(--border); + border-radius: 12px; + padding: 20px; + margin: 20px 0; +} + +.media-section-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 16px; +} + +.media-section-header h3 { + display: flex; + align-items: center; + gap: 10px; + margin: 0; + font-size: 1.2rem; +} + +.media-table { + margin-bottom: 12px; +} + +.media-table input[type="text"], +.media-table input[type="number"] { + min-width: 80px; +} + +.btn-icon { + background: none; + border: none; + color: var(--muted); + cursor: pointer; + padding: 6px; + border-radius: 6px; + transition: background-color .2s ease, color .2s ease; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 1.2rem; +} + +.btn-icon:hover { + background: rgba(0,0,0,.05); + color: var(--text); +} + +[data-theme="dark"] .btn-icon:hover { + background: rgba(255,255,255,.05); +} + +.btn-sm { + padding: 8px 12px; + font-size: 0.9rem; +} + +/* Signature validation states */ +.signature-validating { + border-color: #f59e0b !important; + background-image: linear-gradient(45deg, rgba(245, 158, 11, 0.1) 25%, transparent 25%, transparent 50%, rgba(245, 158, 11, 0.1) 50%, rgba(245, 158, 11, 0.1) 75%, transparent 75%, transparent); + background-size: 20px 20px; + animation: signature-loading 1s linear infinite; +} + +@keyframes signature-loading { + 0% { background-position: 0 0; } + 100% { background-position: 20px 20px; } +} + +.signature-valid { + border-color: #10b981 !important; + background-color: rgba(16, 185, 129, 0.05); +} + +.signature-invalid { + border-color: #ef4444 !important; + background-color: rgba(239, 68, 68, 0.05); +} + +.threshold-exceeded { + background-color: rgba(239, 68, 68, 0.08); +} + +[data-theme="dark"] .threshold-exceeded { + background-color: rgba(239, 68, 68, 0.12); +} diff --git a/php/submit_elsa.php b/php/submit_elsa.php new file mode 100644 index 0000000..0cb1b58 --- /dev/null +++ b/php/submit_elsa.php @@ -0,0 +1,139 @@ + $authors[$i] ?? '', + 'year' => $years[$i] ?? '', + 'edition' => $editions[$i] ?? '', + 'title' => $titles[$i] ?? '', + 'signature' => $signatures[$i] ?? '', + 'pages_from' => $pagesFrom[$i] ?? '', + 'pages_to' => $pagesTo[$i] ?? '' + ]; + } +} + +// Process Zeitschriftenartikel entries +if (isset($_POST['zeitschrift_author']) && is_array($_POST['zeitschrift_author'])) { + $authors = postArray('zeitschrift_author'); + $years = postArray('zeitschrift_year'); + $volumes = postArray('zeitschrift_volume'); + $articleTitles = postArray('zeitschrift_article_title'); + $journalTitles = postArray('zeitschrift_journal_title'); + $signatures = postArray('zeitschrift_signature'); + $pagesFrom = postArray('zeitschrift_pages_from'); + $pagesTo = postArray('zeitschrift_pages_to'); + + for ($i = 0; $i < count($authors); $i++) { + $zeitschriftenartikel[] = [ + 'author' => $authors[$i] ?? '', + 'year' => $years[$i] ?? '', + 'volume' => $volumes[$i] ?? '', + 'article_title' => $articleTitles[$i] ?? '', + 'journal_title' => $journalTitles[$i] ?? '', + 'signature' => $signatures[$i] ?? '', + 'pages_from' => $pagesFrom[$i] ?? '', + 'pages_to' => $pagesTo[$i] ?? '' + ]; + } +} + +// Process Herausgeberwerk entries +if (isset($_POST['herausgeber_publisher']) && is_array($_POST['herausgeber_publisher'])) { + $publishers = postArray('herausgeber_publisher'); + $workTitles = postArray('herausgeber_work_title'); + $years = postArray('herausgeber_year'); + $editions = postArray('herausgeber_edition'); + $articleAuthors = postArray('herausgeber_article_author'); + $articleTitles = postArray('herausgeber_article_title'); + $signatures = postArray('herausgeber_signature'); + $pagesFrom = postArray('herausgeber_pages_from'); + $pagesTo = postArray('herausgeber_pages_to'); + + for ($i = 0; $i < count($publishers); $i++) { + $herausgeberwerke[] = [ + 'publisher' => $publishers[$i] ?? '', + 'work_title' => $workTitles[$i] ?? '', + 'year' => $years[$i] ?? '', + 'edition' => $editions[$i] ?? '', + 'article_author' => $articleAuthors[$i] ?? '', + 'article_title' => $articleTitles[$i] ?? '', + 'signature' => $signatures[$i] ?? '', + 'pages_from' => $pagesFrom[$i] ?? '', + 'pages_to' => $pagesTo[$i] ?? '' + ]; + } +} + +// Prepare data for XML generation +$data = [ + 'name' => $name, + 'lastname' => $lastname, + 'title' => $title, + 'mail' => $mail, + 'subject' => $subject, + 'classname' => $classname, + 'usage_date_from' => $usage_date_from, + 'usage_date_to' => $usage_date_to, + 'availability_date' => $availability_date, + 'message' => $message, + 'monografien' => $monografien, + 'zeitschriftenartikel' => $zeitschriftenartikel, + 'herausgeberwerke' => $herausgeberwerke +]; + +// Generate XML +$xml = generateELSAXML($data); + +// Send email +$emailSent = sendEmail('New ELSA Form Submission', $xml); + +if ($emailSent || !MAIL_ENABLED) { + redirect('index.php?success=true'); +} else { + die('Fehler: E-Mail konnte nicht gesendet werden. Bitte versuchen Sie es später erneut.'); +} diff --git a/php/submit_semesterapparat.php b/php/submit_semesterapparat.php new file mode 100644 index 0000000..7c91425 --- /dev/null +++ b/php/submit_semesterapparat.php @@ -0,0 +1,80 @@ + $authornames[$i] ?? '', + 'year' => $years[$i] ?? '', + 'title' => $booktitles[$i] ?? '', + 'signature' => $signatures[$i] ?? '' + ]; +} + +// Prepare data for XML generation +$data = [ + 'name' => $name, + 'lastname' => $lastname, + 'title' => $title, + 'telno' => $telno, + 'mail' => $mail, + 'apparatsname' => $apparatsname, + 'subject' => $subject, + 'semester' => $semester_type . ' ' . $semester_year, + 'dauerapparat' => $dauerapparat, + 'message' => $message, + 'books' => $books +]; + +// Generate XML +$xml = generateXML($data); + +// Send email +$emailSent = sendEmail('Antrag für neuen Semesterapparat', $xml); + +if ($emailSent || !MAIL_ENABLED) { + redirect('index.php?success=true'); +} else { + die('Fehler: E-Mail konnte nicht gesendet werden. Bitte versuchen Sie es später erneut.'); +}