v1.0.0.1
StocketBase
- <?php
-
- declare(strict_types=1);
-
- require_once __DIR__ . '/includes/config.php';
- require_once __DIR__ . '/includes/database.php';
- require_once __DIR__ . '/includes/auth.php';
- require_once __DIR__ . '/includes/csrf.php';
- require_once __DIR__ . '/includes/activity-log.php';
- require_once __DIR__ . '/includes/site-front.php';
- require_once __DIR__ . '/includes/language.php';
-
- Auth::boot();
- Auth::requireRoleAtLeast(Auth::ROLE_STORE_OWNER);
-
- $db = Database::site();
- Language::ensureKeys(Language::ADDED_DEFAULTS);
- $flashMessage = null;
- $flashType = 'success';
-
- if ($_SERVER['REQUEST_METHOD'] === 'POST') {
- if (!Csrf::verify($_POST['csrf_token'] ?? null)) {
- $flashMessage = 'Your session expired. Please try again.';
- $flashType = 'error';
- } else {
- $action = (string) ($_POST['action'] ?? '');
- $postedCode = strtolower(trim((string) ($_POST['code'] ?? '')));
- $knownCode = Languages::isValidCode($postedCode) && Languages::find($postedCode) !== null;
-
- if ($action === 'save_settings') {
- $defaultCode = strtolower(trim((string) ($_POST['multilang_default_code'] ?? 'en')));
- $defaultName = trim(strip_tags((string) ($_POST['multilang_default_name'] ?? '')));
-
- if (!Languages::isValidCode($defaultCode)) {
- $flashMessage = 'The language code should be 2 or 3 letters, like en, pl or pt-br.';
- $flashType = 'error';
- } elseif (Languages::find($defaultCode) !== null) {
- $flashMessage = 'You already added a language with that code.';
- $flashType = 'error';
- } else {
- Languages::setSetting('multilang_enabled', isset($_POST['multilang_enabled']) ? '1' : '0');
- Languages::setSetting('multilang_default_code', $defaultCode);
- Languages::setSetting('multilang_default_name', $defaultName !== '' ? mb_substr($defaultName, 0, 60) : 'English');
- Languages::setSetting('multilang_switcher', isset($_POST['multilang_switcher']) ? '1' : '0');
- ActivityLog::record('language.settings');
- $flashMessage = 'Changes saved.';
- }
- } elseif ($action === 'add_language') {
- $error = Languages::addLanguage((string) ($_POST['code'] ?? ''), (string) ($_POST['name'] ?? ''));
- if ($error !== null) {
- $flashMessage = $error;
- $flashType = 'error';
- } else {
- ActivityLog::record('language.add', null, null, $postedCode);
- $flashMessage = 'Language added. Translate the texts, then switch it on.';
- }
- } elseif ($action === 'toggle_language' && $knownCode) {
- $statement = $db->prepare('UPDATE site_languages SET is_active = 1 - is_active WHERE code = :code');
- $statement->execute(['code' => $postedCode]);
- ActivityLog::record('language.toggle', null, null, $postedCode);
- $flashMessage = 'Changes saved.';
- } elseif ($action === 'delete_language' && $knownCode) {
- $statement = $db->prepare('DELETE FROM site_languages WHERE code = :code');
- $statement->execute(['code' => $postedCode]);
- ActivityLog::record('language.delete', null, null, $postedCode);
- $flashMessage = 'Language removed. The translations are kept, so they come back if you add it again.';
- } elseif ($action === 'save_strings' && $knownCode) {
- $submitted = is_array($_POST['strings'] ?? null) ? $_POST['strings'] : [];
- if (Languages::saveStrings($postedCode, $submitted)) {
- ActivityLog::record('language.strings', null, null, $postedCode);
- $flashMessage = 'Changes saved.';
- } else {
- $flashMessage = 'We couldn\'t save the translation file. Ask your host to make the translations folder writable.';
- $flashType = 'error';
- }
- } elseif ($action === 'save_site' && $knownCode) {
- Languages::saveSiteValues($postedCode, is_array($_POST['site'] ?? null) ? $_POST['site'] : []);
- ActivityLog::record('language.site', null, null, $postedCode);
- $flashMessage = 'Changes saved.';
- } elseif ($action === 'copy_site' && $knownCode) {
- $added = Languages::copySiteMissing($postedCode);
- ActivityLog::record('language.site_copy', null, null, $postedCode);
- $flashMessage = $added > 0
- ? $added . ' missing text' . ($added === 1 ? '' : 's') . ' copied from the original. Existing translations were not changed.'
- : 'Everything is already translated.';
- } elseif ($action === 'copy_missing' && $knownCode) {
- $added = Languages::copyMissingStrings($postedCode);
- ActivityLog::record('language.copy', null, null, $postedCode);
- $flashMessage = $added > 0
- ? $added . ' missing text' . ($added === 1 ? '' : 's') . ' copied from the original language. Existing translations were not changed.'
- : 'Everything is already translated.';
- } else {
- $flashMessage = 'Something went wrong. Please try again.';
- $flashType = 'error';
- }
-
- Languages::resetCache();
- SiteFront::settings();
- }
- }
-
- Languages::resetCache();
- $settings = Database::site()->query('SELECT setting_key, setting_value FROM site_settings')->fetchAll(PDO::FETCH_KEY_PAIR);
- $multilangEnabled = ($settings['multilang_enabled'] ?? '0') === '1';
- $defaultCode = (string) ($settings['multilang_default_code'] ?? 'en');
- $defaultName = (string) ($settings['multilang_default_name'] ?? 'English');
- $switcherEnabled = ($settings['multilang_switcher'] ?? '1') === '1';
-
- $languages = Languages::all();
- $editCode = strtolower(trim((string) ($_GET['edit'] ?? '')));
- $editing = Languages::isValidCode($editCode) ? Languages::find($editCode) : null;
- $baseStrings = Language::base();
- $siteCode = strtolower(trim((string) ($_GET['site'] ?? '')));
- $siteEditing = $editing === null && Languages::isValidCode($siteCode) ? Languages::find($siteCode) : null;
- $siteValues = $siteEditing !== null ? Languages::siteValues($siteEditing['code']) : [];
- $siteOriginals = $siteEditing !== null ? Languages::siteOriginals() : [];
- $editStrings = $editing !== null ? Languages::strings($editing['code']) : [];
-
- $dashActivePage = 'languages';
- $dashPageTitle = 'Languages';
-
- require __DIR__ . '/includes/dash-header.php';
-
- ?>
-
- <?php if ($flashMessage !== null): ?>
- <div class="dash-flash dash-flash-<?= htmlspecialchars($flashType) ?>"><?= Icons::icon($flashType === 'error' ? 'x' : 'check', 'icon icon-sm') ?><?= htmlspecialchars($flashMessage) ?></div>
- <?php endif; ?>
-
- <div class="dash-header-row">
- <h1 class="dash-title"><?= Icons::icon('translate', 'icon icon-lg') ?>Languages</h1>
- </div>
-
- <div class="settings-tabs">
- <a href="languages.php" class="<?= $editing === null && $siteEditing === null ? 'active' : '' ?>"><?= Icons::icon('settings', 'icon icon-sm') ?>Overview</a>
- <?php if ($siteEditing !== null): ?>
- <a href="languages.php?site=<?= urlencode($siteEditing['code']) ?>" class="active"><?= Icons::icon('globe', 'icon icon-sm') ?>Store texts: <?= htmlspecialchars($siteEditing['name']) ?></a>
- <?php endif; ?>
- <?php if ($editing !== null): ?>
- <a href="languages.php?edit=<?= urlencode($editing['code']) ?>" class="active"><?= Icons::icon('translate', 'icon icon-sm') ?>Interface: <?= htmlspecialchars($editing['name']) ?></a>
- <?php endif; ?>
- </div>
-
- <?php if ($editing === null && $siteEditing === null): ?>
-
- <form method="post" class="settings-section">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="save_settings">
-
- <label><input type="checkbox" name="multilang_enabled" <?= $multilangEnabled ? 'checked' : '' ?>> Offer your store in more than one language</label>
-
- <div class="settings-grid">
- <div>
- <label>Main language code</label>
- <input type="text" name="multilang_default_code" value="<?= htmlspecialchars($defaultCode) ?>" maxlength="10" spellcheck="false">
- </div>
- <div>
- <label>Main language name</label>
- <input type="text" name="multilang_default_name" value="<?= htmlspecialchars($defaultName) ?>" maxlength="60">
- </div>
- </div>
- <p style="font-size:12.5px;color:var(--muted);margin:6px 0 0;">This is the language you write your products and pages in. It keeps your normal web address.</p>
-
- <label><input type="checkbox" name="multilang_switcher" <?= $switcherEnabled ? 'checked' : '' ?>> Show a language picker in the footer</label>
-
- <button type="submit" class="dash-btn dash-btn-primary" style="margin-top:20px;"><?= Icons::icon('check', 'icon icon-sm') ?>Save</button>
- </form>
-
- <h2 class="dash-subtitle"><?= Icons::icon('translate', 'icon icon-sm') ?>Languages</h2>
-
- <table class="dash-table">
- <thead>
- <tr>
- <th>Language</th>
- <th>Web address</th>
- <th>Store texts</th>
- <th>Status</th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td><?= htmlspecialchars($defaultName) ?> <span style="color:var(--muted);">(<?= htmlspecialchars($defaultCode) ?>)</span></td>
- <td>/</td>
- <td>Normal address</td>
- <td><span class="status-pill status-published">Main language</span></td>
- <td></td>
- </tr>
- <?php foreach ($languages as $language): ?>
- <?php $missing = Languages::missingStringCount($language['code']); ?>
- <tr>
- <td><?= htmlspecialchars($language['name']) ?> <span style="color:var(--muted);">(<?= htmlspecialchars($language['code']) ?>)</span></td>
- <td>/<?= htmlspecialchars($language['code']) ?>/</td>
- <td><?= $missing === 0 ? 'Complete' : $missing . ' texts missing' ?></td>
- <td><span class="status-pill status-<?= (int) $language['is_active'] === 1 ? 'published' : 'draft' ?>"><?= (int) $language['is_active'] === 1 ? 'Visible' : 'Hidden' ?></span></td>
- <td class="dash-table-actions">
- <a href="languages.php?edit=<?= urlencode($language['code']) ?>" class="dash-btn-small"><?= Icons::icon('edit', 'icon icon-sm') ?>Interface</a>
- <a href="languages.php?site=<?= urlencode($language['code']) ?>" class="dash-btn-small"><?= Icons::icon('globe', 'icon icon-sm') ?>Store texts</a>
- <form method="post" style="display:inline;">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="toggle_language">
- <input type="hidden" name="code" value="<?= htmlspecialchars($language['code'], ENT_QUOTES) ?>">
- <button type="submit" class="dash-btn-small"><?= Icons::icon((int) $language['is_active'] === 1 ? 'x' : 'check', 'icon icon-sm') ?><?= (int) $language['is_active'] === 1 ? 'Hide' : 'Show' ?></button>
- </form>
- <form method="post" style="display:inline;" onsubmit="return confirm('Remove this language? Don\'t worry, the translations come back if you add it again.');">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="delete_language">
- <input type="hidden" name="code" value="<?= htmlspecialchars($language['code'], ENT_QUOTES) ?>">
- <button type="submit" class="dash-btn-small dash-btn-danger"><?= Icons::icon('trash', 'icon icon-sm') ?>Remove</button>
- </form>
- </td>
- </tr>
- <?php endforeach; ?>
- </tbody>
- </table>
-
- <h2 class="dash-subtitle"><?= Icons::icon('plus', 'icon icon-sm') ?>Add language</h2>
- <form method="post" class="settings-section">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="add_language">
- <div class="settings-grid">
- <div>
- <label>Code (like pl, de or pt-br)</label>
- <input type="text" name="code" maxlength="10" required spellcheck="false" autocomplete="off">
- </div>
- <div>
- <label>Name customers see (like Polski)</label>
- <input type="text" name="name" maxlength="60" required>
- </div>
- </div>
- <p style="font-size:12.5px;color:var(--muted);margin:6px 0 0;">New languages start hidden. Translate the store texts and your content, then switch it on. You translate products, pages and categories from their own edit pages.</p>
- <button type="submit" class="dash-btn dash-btn-primary" style="margin-top:20px;"><?= Icons::icon('plus', 'icon icon-sm') ?>Add language</button>
- </form>
-
- <?php elseif ($siteEditing !== null): ?>
-
- <?php require __DIR__ . '/includes/language-site-texts.php'; ?>
-
- <?php else: ?>
-
- <?php $editMissing = Languages::missingStringCount($editing['code']); ?>
-
- <div class="sidebar-box" style="max-width:640px;margin-bottom:20px;">
- <p style="margin:0 0 12px;">Store texts in <strong><?= htmlspecialchars($editing['name']) ?></strong>. Anything you leave empty shows in your main language.</p>
- <form method="post">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="copy_missing">
- <input type="hidden" name="code" value="<?= htmlspecialchars($editing['code'], ENT_QUOTES) ?>">
- <button type="submit" class="dash-btn" <?= $editMissing === 0 ? 'disabled' : '' ?>><?= Icons::icon('copy', 'icon icon-sm') ?>Fill empty texts with the original (<?= $editMissing ?>)</button>
- </form>
- <p style="font-size:12.5px;color:var(--muted);margin:10px 0 0;">Only empty fields get filled, your translations stay as they are.</p>
- </div>
-
- <form method="post" class="settings-section" style="max-width:none;">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="save_strings">
- <input type="hidden" name="code" value="<?= htmlspecialchars($editing['code'], ENT_QUOTES) ?>">
-
- <?php foreach ($baseStrings as $key => $baseValue): ?>
- <div class="lang-row">
- <div class="lang-row-key"><?= htmlspecialchars((string) $key) ?><br><span style="color:var(--muted-dim);"><?= htmlspecialchars((string) $baseValue) ?></span></div>
- <input type="text" name="strings[<?= htmlspecialchars((string) $key, ENT_QUOTES) ?>]" value="<?= htmlspecialchars((string) ($editStrings[$key] ?? '')) ?>" placeholder="<?= htmlspecialchars((string) $baseValue, ENT_QUOTES) ?>">
- </div>
- <?php endforeach; ?>
-
- <div class="publish-actions">
- <button type="submit" class="dash-btn dash-btn-primary"><?= Icons::icon('check', 'icon icon-sm') ?>Save</button>
- <a href="languages.php" class="dash-btn"><?= Icons::icon('x', 'icon icon-sm') ?>Back</a>
- </div>
- </form>
-
- <?php endif; ?>
-
- <?php require __DIR__ . '/includes/dash-footer.php'; ?>
-