WebOrbiton

Code

FlatlyPage

A modern CMS for building landing pages.

Version history

Version 3.0.0.2 2026-09-26

Browse files
Files changed18
Lines added+135
Lines removed-135
admin/account.php Modified

Not shown (binary file or too large to diff).

admin/ai-helper-lib.php Modified

Not shown (binary file or too large to diff).

admin/dashboard.php Modified

Not shown (binary file or too large to diff).

admin/generate-hash.php Modified +1 -1
⋮ 90 unchanged lines ⋮
                          </svg>
                          <div class="text-sm text-amber-800">
                              <p class="font-medium">Security Notice</p>
-                             <p class="mt-1">Do not use this tool in production. Passwords should only be hashed during registration or password changes.</p>
+                             <p class="mt-1">Don't use this tool on a live site. Passwords should only be hashed when an account is created or a password is changed.</p>
                          </div>
                      </div>
                  </div>
⋮ 27 unchanged lines ⋮
admin/index.php Modified +10 -10
⋮ 19 unchanged lines ⋮
      $clientIP = $loginTracker->getClientIP();
      if ($loginTracker->isIPBanned($clientIP)) {
          http_response_code(403);
-         die('<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Access Denied</title><style>body{font-family:Arial,sans-serif;background:#f1f5f9;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;}div{background:#fff;padding:40px;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.1);text-align:center;}h1{color:#dc2626;margin:0 0 10px;}p{color:#64748b;margin:0;}</style></head><body><div><h1>Access Denied</h1><p>Your IP address has been banned.</p></div></body></html>');
+         die('<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Access Denied</title><style>body{font-family:Arial,sans-serif;background:#f1f5f9;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;}div{background:#fff;padding:40px;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.1);text-align:center;}h1{color:#dc2626;margin:0 0 10px;}p{color:#64748b;margin:0;}</style></head><body><div><h1>Access Denied</h1><p>Access from your IP address has been blocked.</p></div></body></html>');
      }
  } catch (Exception $e) {
      error_log('Login tracker initialization failed: ' . $e->getMessage());
⋮ 69 unchanged lines ⋮
      } elseif (strlen($password) < 8) {
          $error = 'Password must be at least 8 characters.';
      } elseif ($password !== $confirmPassword) {
-         $error = 'Passwords do not match.';
+         $error = "The passwords don't match.";
      } elseif ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
          $error = 'Please enter a valid email address.';
      } else {
⋮ 5 unchanged lines ⋮
          ];
          
          if (file_put_contents($adminFile, json_encode($adminData, JSON_PRETTY_PRINT))) {
-             $success = 'Admin account created successfully. You can now log in.';
+             $success = 'Your admin account is ready. You can sign in now.';
              $adminExists = true;
          } else {
-             $error = 'Failed to create account. Check write permissions.';
+             $error = "Couldn't create the account. Make sure the data folder on your server can be written to.";
          }
      }
  }
⋮ 1 unchanged line ⋮
  if ($adminExists && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'login') {
      $rateLimitCheck = checkRateLimit();
      if (!$rateLimitCheck['allowed']) {
-         $error = 'Too many login attempts. Please try again in ' . $rateLimitCheck['timeLeft'] . ' minute(s).';
+         $error = 'Too many attempts. Please wait ' . $rateLimitCheck['timeLeft'] . ' minute(s) and try again.';
      } else {
          $username = trim($_POST['username'] ?? '');
          $password = $_POST['password'] ?? '';
⋮ 59 unchanged lines ⋮
  <div style='max-width:520px;margin:0 auto;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,.08);'>
    <div style='background:#1e293b;padding:28px 32px;'>
      <h1 style='margin:0;color:#fff;font-size:18px;font-weight:600;'>FlatlyPage CMS</h1>
-     <p style='margin:4px 0 0;color:#94a3b8;font-size:13px;'>Security Notification</p>
+     <p style='margin:4px 0 0;color:#94a3b8;font-size:13px;'>Security alert</p>
    </div>
    <div style='padding:28px 32px 32px;'>
-     <p style='margin:0 0 20px;font-size:15px;color:#1e293b;font-weight:600;'>A new login was detected on your account.</p>
+     <p style='margin:0 0 20px;font-size:15px;color:#1e293b;font-weight:600;'>Someone just signed in to your account.</p>
      <table style='width:100%;border-collapse:collapse;'>
        <tr><td colspan='2' style='padding:0 0 4px;font-size:12px;color:#94a3b8;text-transform:uppercase;letter-spacing:.05em;'>Current login</td></tr>
        <tr>
⋮ 11 unchanged lines ⋮
        {$prevBlock}
      </table>
      <hr style='border:none;border-top:1px solid #e2e8f0;margin:24px 0 20px;'>
-     <p style='margin:0;font-size:12px;color:#94a3b8;'>If you did not log in, secure your account immediately by changing your password.</p>
+     <p style='margin:0;font-size:12px;color:#94a3b8;'>If this wasn't you, change your password right away.</p>
    </div>
  </div>
  </body></html>";
⋮ 30 unchanged lines ⋮
                  header('Location: ' . BASE_URL . '/admin/dashboard.php');
                  exit;
              } else {
-                 $error = 'Invalid username or password.';
+                 $error = "That username or password isn't right.";
                  recordLoginAttempt(false);
              }
          }
⋮ 96 unchanged lines ⋮
  
                      <div>
                          <label for="reg_email" class="block text-sm font-medium text-gray-700 mb-2">
-                             Email <span class="text-gray-400 font-normal">(optional – login alerts)</span>
+                             Email <span class="text-gray-400 font-normal">(optional, for login alerts)</span>
                          </label>
                          <input type="email"
                                 id="reg_email"
⋮ 55 unchanged lines ⋮
admin/publish-schedule-fields.php Modified +3 -3
⋮ 32 unchanged lines ⋮
              value="1"
              onchange="document.getElementById('publishScheduleFields').style.display = this.checked ? 'block' : 'none';"
              <?= $publish_enabled ? 'checked' : '' ?>>
-         <span>Schedule publish date (optional)</span>
+         <span>Publish later (optional)</span>
      </label>
-     <p class="form-hint">Hide this page from visitors until the selected date and time. Visible to you (CEO) in the dashboard at any time.</p>
+     <p class="form-hint">Keep this page hidden from visitors until the date and time you pick. You can still see it here in the dashboard.</p>
  
      <div id="publishScheduleFields" class="publish-schedule-fields-wrapper" style="display: <?= $publish_enabled ? 'block' : 'none' ?>; margin-top: 12px;">
          <div class="form-row-3">
⋮ 44 unchanged lines ⋮
                      <option value="<?= e($tz) ?>" <?= $publish_timezone === $tz ? 'selected' : '' ?>><?= e($tz) ?></option>
                  <?php endforeach; ?>
              </select>
-             <p class="form-hint">All comparisons use this timezone, independent of visitors' local time.</p>
+             <p class="form-hint">The publish time follows this timezone, not your visitors' local time.</p>
          </div>
      </div>
  </div>
⋮ 82 unchanged lines ⋮
admin/system.php Modified +10 -10
⋮ 24 unchanged lines ⋮
  
  if ($_SERVER['REQUEST_METHOD'] === 'POST') {
      if (!verify_csrf_token($_POST['csrf_token'] ?? '')) {
-         $message = 'Invalid request. Please try again.';
+         $message = 'Your session timed out while the page was open. Please try again.';
          $message_type = 'error';
      } else {
          $provider = (string) ($_POST['translator_provider'] ?? '');
⋮ 16 unchanged lines ⋮
          ];
  
          if (save_page('system', array_merge($system_settings, $new_settings))) {
-             $_SESSION['admin_msg'] = 'System settings have been saved!';
+             $_SESSION['admin_msg'] = 'Settings saved.';
              $_SESSION['admin_msg_type'] = 'success';
          } else {
-             $_SESSION['admin_msg'] = 'Error saving system settings.';
+             $_SESSION['admin_msg'] = "Couldn't save the settings. Please try again.";
              $_SESSION['admin_msg_type'] = 'error';
          }
          header('Location: ' . BASE_URL . '/admin/system');
⋮ 81 unchanged lines ⋮
                                      <input type="checkbox" id="glass_effect" checked>
                                      <label for="glass_effect"></label>
                                  </div>
-                                 <p class="form-hint">Blurred, translucent surfaces in the dashboard (top bar, dialogs, AI button). Turn it off for a flat look or on slower devices. This is saved in this browser only, not on the server.</p>
+                                 <p class="form-hint">Frosted, see-through backgrounds for the top bar, dialogs and AI button. Turn it off for a flatter look or if the dashboard feels slow. Only applies to this browser.</p>
                              </div>
                              <div class="form-group">
                                  <label class="form-label">Minimalism</label>
⋮ 1 unchanged line ⋮
                                      <input type="checkbox" id="minimalism">
                                      <label for="minimalism"></label>
                                  </div>
-                                 <p class="form-hint">Removes all transitions, animations, shadows and glows, and replaces gradients with flat colors. Lighter for older computers and slow devices. This is saved in this browser only, not on the server.</p>
+                                 <p class="form-hint">Turns off animations, shadows and gradients for a simple, flat look. Helps on older or slower computers. Only applies to this browser.</p>
                              </div>
                              <script>
                                  (function() {
⋮ 14 unchanged lines ⋮
                                          <option value="<?= e($font_name) ?>" <?= $current_font === $font_name ? 'selected' : '' ?>><?= e($font_name) ?></option>
                                      <?php endforeach; ?>
                                  </select>
-                                 <p class="form-hint">Font of the dashboard. Add .ttf files to the fonts/ folder — Font_Name.ttf appears as "Font Name".</p>
+                                 <p class="form-hint">The font used in this dashboard. To add your own, put a .ttf file in the fonts folder, and Font_Name.ttf shows up here as "Font Name".</p>
                              </div>
                          </div>
                      </div>
⋮ 9 unchanged lines ⋮
                                      <input type="checkbox" name="translator_enabled" id="translator_enabled" value="true" <?= $system_settings['translator_enabled'] ? 'checked' : '' ?>>
                                      <label for="translator_enabled"></label>
                                  </div>
-                                 <p class="form-hint">Show the translate button next to text fields in the editor</p>
+                                 <p class="form-hint">Show a translate button next to text fields in the editor</p>
                              </div>
  
                              <div class="form-row">
                                  <div class="form-group">
-                                     <label class="form-label">API Key Provider</label>
+                                     <label class="form-label">Translation service</label>
                                      <select name="translator_provider" class="form-input">
                                          <?php foreach ($providers as $id => $provider): ?>
                                              <option value="<?= e($id) ?>" <?= $system_settings['translator_provider'] === $id ? 'selected' : '' ?>><?= e($provider['name']) ?><?= $provider['requiresKey'] ? '' : ' (no key needed)' ?></option>
⋮ 2 unchanged lines ⋮
                                  </div>
                                  <div class="form-group">
                                      <label class="form-label">Translator API Key</label>
-                                     <input type="password" name="translator_api_key" class="form-input" autocomplete="new-password" placeholder="<?= $has_key ? 'Saved — leave empty to keep' : 'Enter API key' ?>">
+                                     <input type="password" name="translator_api_key" class="form-input" autocomplete="new-password" placeholder="<?= $has_key ? 'Saved, leave empty to keep it' : 'Paste your API key' ?>">
                                      <?php if ($has_key): ?>
-                                         <p class="form-hint"><label><input type="checkbox" name="translator_api_key_clear" value="1"> Remove saved key</label></p>
+                                         <p class="form-hint"><label><input type="checkbox" name="translator_api_key_clear" value="1"> Delete the saved key</label></p>
                                      <?php endif; ?>
                                  </div>
                              </div>
⋮ 12 unchanged lines ⋮
admin/themes.php Modified +17 -17
⋮ 10 unchanged lines ⋮
      $token = $_POST['csrf_token'] ?? '';
      
      if (!verify_csrf_token($token)) {
-         $message = 'Invalid request. Please try again.';
+         $message = 'Your session timed out while the page was open. Please try again.';
          $message_type = 'error';
      } else {
          $action = $_POST['action'] ?? '';
⋮ 6 unchanged lines ⋮
                      $target = __DIR__ . '/../css/theme.css';
                      
                      if (copy($source, $target)) {
-                         $message = 'Theme activated successfully!';
+                         $message = 'Theme activated.';
                          $message_type = 'success';
                      } else {
-                         $message = 'Failed to activate theme.';
+                         $message = "Couldn't activate the theme.";
                          $message_type = 'error';
                      }
                  } else {
-                     $message = 'Theme file not found.';
+                     $message = "Couldn't find that theme file.";
                      $message_type = 'error';
                  }
                  break;
⋮ 2 unchanged lines ⋮
                  $target = __DIR__ . '/../css/theme.css';
                  
                  if (!file_exists($target) || unlink($target)) {
-                     $message = 'Theme deactivated successfully!';
+                     $message = 'Theme turned off. Your site is back to the default look.';
                      $message_type = 'success';
                  } else {
-                     $message = 'Failed to deactivate theme.';
+                     $message = "Couldn't turn off the theme.";
                      $message_type = 'error';
                  }
                  break;
⋮ 2 unchanged lines ⋮
                  $theme_file = $_POST['theme_file'] ?? '';
                  
                  if (!$theme_file || !file_exists(__DIR__ . '/../themes/' . $theme_file)) {
-                     $message = 'Theme file not found.';
+                     $message = "Couldn't find that theme file.";
                      $message_type = 'error';
                  } else {
                      $current_theme = getCurrentThemeFile();
⋮ 4 unchanged lines ⋮
                      }
                      
                      if (unlink(__DIR__ . '/../themes/' . $theme_file)) {
-                         $message = 'Theme deleted successfully!';
+                         $message = 'Theme deleted.';
                          $message_type = 'success';
                      } else {
-                         $message = 'Failed to delete theme.';
+                         $message = "Couldn't delete the theme.";
                          $message_type = 'error';
                      }
                  }
⋮ 5 unchanged lines ⋮
                      $file_ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
                      
                      if ($file_ext !== 'css') {
-                         $message = 'Please upload a CSS file.';
+                         $message = 'Please choose a .css file.';
                          $message_type = 'error';
                      } else {
                          $safe_filename = preg_replace('/[^a-z0-9-_]/i', '', pathinfo($file['name'], PATHINFO_FILENAME));
                          $target_file = __DIR__ . '/../themes/' . $safe_filename . '.css';
                          
                          if (file_exists($target_file)) {
-                             $message = 'Theme with this name already exists.';
+                             $message = 'A theme with this name already exists.';
                              $message_type = 'error';
                          } elseif (move_uploaded_file($file['tmp_name'], $target_file)) {
-                             $message = 'Theme uploaded successfully!';
+                             $message = 'Theme uploaded.';
                              $message_type = 'success';
                          } else {
-                             $message = 'Failed to upload theme.';
+                             $message = "Couldn't upload the theme.";
                              $message_type = 'error';
                          }
                      }
                  } else {
-                     $message = 'Please select a valid CSS file.';
+                     $message = 'Please choose a valid .css file.';
                      $message_type = 'error';
                  }
                  break;
⋮ 281 unchanged lines ⋮
                                                  <input type="hidden" name="action" value="deactivate">
                                                  <button type="submit" class="btn btn-secondary btn-sm" style="width: 100%;">Deactivate</button>
                                              </form>
-                                             <form method="POST" action="" onsubmit="return confirm('Are you sure you want to delete this theme? It will be deactivated first.');">
+                                             <form method="POST" action="" onsubmit="return confirm('Delete this theme? It will be turned off first.');">
                                                  <input type="hidden" name="csrf_token" value="<?= e($csrf_token) ?>">
                                                  <input type="hidden" name="action" value="delete">
                                                  <input type="hidden" name="theme_file" value="<?= e($theme['file']) ?>">
⋮ 8 unchanged lines ⋮
                                                  <input type="hidden" name="theme_file" value="<?= e($theme['file']) ?>">
                                                  <button type="submit" class="btn btn-primary btn-sm" style="width: 100%;">Activate</button>
                                              </form>
-                                             <form method="POST" action="" onsubmit="return confirm('Are you sure you want to delete this theme?');">
+                                             <form method="POST" action="" onsubmit="return confirm('Delete this theme?');">
                                                  <input type="hidden" name="csrf_token" value="<?= e($csrf_token) ?>">
                                                  <input type="hidden" name="action" value="delete">
                                                  <input type="hidden" name="theme_file" value="<?= e($theme['file']) ?>">
⋮ 31 unchanged lines ⋮
                      <div class="form-group">
                          <label class="form-label">Theme CSS File</label>
                          <input type="file" name="theme_file" class="form-input" accept=".css" required>
-                         <p class="form-hint">Upload a CSS file with theme styles</p>
+                         <p class="form-hint">A .css file with the theme styles</p>
                      </div>
                      
                      <div class="modal-actions">
⋮ 10 unchanged lines ⋮
admin/updater-lib.php Modified

Not shown (binary file or too large to diff).

admin/updater.php Modified +40 -40
⋮ 13 unchanged lines ⋮
  
  if ($_SERVER['REQUEST_METHOD'] === 'POST') {
      if (!verify_csrf_token($_POST['csrf_token'] ?? '')) {
-         $message = 'Invalid request. Please try again.';
+         $message = 'Your session timed out while the page was open. Please try again.';
          $message_type = 'error';
      } else {
          $action = (string) ($_POST['action'] ?? '');
⋮ 4 unchanged lines ⋮
                  $message = $updaterEnabled ? 'Updater enabled.' : 'Updater disabled.';
              } else {
                  $updaterEnabled = !$updaterEnabled;
-                 $message = 'Could not save the updater setting.';
+                 $message = "Couldn't save the setting.";
                  $message_type = 'error';
              }
          }
⋮ 2 unchanged lines ⋮
              $turnOn = ($_POST['enabled'] ?? '0') === '1';
              if (Updater::setAutoCleanup($turnOn)) {
                  $message = $turnOn
-                     ? 'Automatic cleanup enabled.'
-                     : 'Automatic cleanup disabled.';
+                     ? 'Old backups will now be deleted automatically.'
+                     : 'Old backups will be kept.';
                  if ($turnOn) {
                      $removed = Updater::cleanupBackups();
                      $message .= $removed > 0 ? ' Removed ' . $removed . ' old backup(s).' : '';
                  }
              } else {
-                 $message = 'Could not save the setting.';
+                 $message = "Couldn't save the setting.";
                  $message_type = 'error';
              }
          }
⋮ 1 unchanged line ⋮
          if ($action === 'toggle_auto_update') {
              $turnOn = ($_POST['enabled'] ?? '0') === '1';
              if ($turnOn && !$updaterEnabled) {
-                 $message = 'Enable the updater first.';
+                 $message = 'Turn on the updater first.';
                  $message_type = 'error';
              } elseif ($turnOn && ($_POST['accept_risk'] ?? '') !== '1') {
-                 $message = 'Confirm that you understand the risks to turn on automatic updates.';
+                 $message = 'Please tick the box to confirm you understand the risks.';
                  $message_type = 'error';
              } elseif (Updater::setAutoUpdate($turnOn)) {
                  $message = $turnOn
-                     ? 'Automatic updates enabled. Add the cron job below, otherwise nothing will run.'
-                     : 'Automatic updates disabled.';
+                     ? "Automatic updates are on. Add the cron job below, or they won't run."
+                     : 'Automatic updates are off.';
              } else {
-                 $message = 'Could not save the setting.';
+                 $message = "Couldn't save the setting.";
                  $message_type = 'error';
              }
          }
  
          if ($action === 'regenerate_cron_token') {
              Updater::regenerateCronToken();
-             $message = 'A new cron URL was generated. Update the URL in your cron job; the old one no longer works.';
+             $message = "New cron link created. Update it in your cron job, the old one won't work anymore.";
          }
  
          if ($action === 'check_updates' || $action === 'download_release') {
              if (!$updaterEnabled) {
-                 $message = 'The updater is disabled.';
+                 $message = 'The updater is turned off.';
                  $message_type = 'error';
              } else {
                  session_write_close();
⋮ 9 unchanged lines ⋮
  
          if (in_array($action, ['install_release', 'restore_backup', 'delete_backup'], true)) {
              if ($action === 'install_release' && !$updaterEnabled) {
-                 $message = 'The updater is disabled.';
+                 $message = 'The updater is turned off.';
                  $message_type = 'error';
              } else {
                  session_write_close();
⋮ 3 unchanged lines ⋮
                  if ($action === 'install_release') {
                      $outcome = Updater::install($currentVersion, ($_POST['overwrite_htaccess'] ?? '') === '1');
                      $message = $outcome['ok']
-                         ? 'Updated to ' . $outcome['version'] . ' (' . $outcome['installed'] . ' files). A backup was saved as ' . $outcome['backup'] . '.'
+                         ? 'Updated to ' . $outcome['version'] . ' (' . $outcome['installed'] . ' files). Your old files were backed up as ' . $outcome['backup'] . '.'
                          : $outcome['error'];
                  } elseif ($action === 'restore_backup') {
                      $outcome = Updater::restore($backupId);
                      $message = $outcome['ok']
-                         ? 'Backup restored (' . $outcome['restored'] . ' files). Version is now ' . $outcome['version'] . '.'
+                         ? 'Backup restored (' . $outcome['restored'] . ' files). You are now on version ' . $outcome['version'] . '.'
                          : $outcome['error'];
                  } else {
                      $outcome = ['ok' => Updater::deleteBackup($backupId)];
-                     $message = $outcome['ok'] ? 'Backup deleted.' : 'Backup not found.';
+                     $message = $outcome['ok'] ? 'Backup deleted.' : "Couldn't find that backup.";
                  }
  
                  $message_type = $outcome['ok'] ? 'success' : 'error';
⋮ 97 unchanged lines ⋮
                              </form>
                          </div>
                          <?php if (!$updaterEnabled): ?>
-                             <p class="form-hint">The updater is off by default. While it is off, this site sends no requests to the update server.</p>
+                             <p class="form-hint">The updater is off by default. While it's off, your site never contacts the update server.</p>
                          <?php endif; ?>
  
                          <form method="POST" class="upd-actions">
⋮ 2 unchanged lines ⋮
                              <input type="hidden" name="enabled" value="<?= $autoCleanup ? '0' : '1' ?>">
                              <button type="submit" class="btn btn-secondary"><?= $autoCleanup ? 'Turn off backup cleanup' : 'Turn on backup cleanup' ?></button>
                          </form>
-                         <p class="form-hint">Backup cleanup: <?= $autoCleanup ? 'on. Backups older than 30 days are deleted automatically.' : 'off. Turn it on to delete backups older than 30 days automatically.' ?></p>
+                         <p class="form-hint"><?= $autoCleanup ? 'Backup cleanup is on, so backups older than 30 days are deleted automatically.' : 'Backup cleanup is off. Turn it on to delete backups older than 30 days automatically.' ?></p>
                      </div>
                  </div>
  
⋮ 10 unchanged lines ⋮
                                  <line x1="12" y1="17" x2="12.01" y2="17" />
                              </svg>
                              <div>
-                                 <strong>Warning: automatic updates can be dangerous for your site.</strong>
-                                 <p>When this is on, a cron job installs every new FlatlyPage release by itself, <strong>without asking anyone and without notifying anyone</strong>. Nobody reviews the changes before they go live.</p>
+                                 <strong>Automatic updates can break your site.</strong>
+                                 <p>When this is on, every new FlatlyPage version is installed on its own, <strong>without asking you and without telling you</strong>. Nobody checks the changes before they go live.</p>
                                  <ul>
-                                     <li>An update can break the site or parts of it.</li>
-                                     <li>It can overwrite files you changed yourself (edited templates, custom fixes, your own code in CMS files) and replace or change existing functions and behaviour, silently.</li>
-                                     <li>You will only find out when you open this page or notice a problem on the site.</li>
+                                     <li>An update can break your site, or parts of it.</li>
+                                     <li>It can overwrite files you changed yourself, like edited templates, fixes or your own code, and change how things work without you noticing.</li>
+                                     <li>You'll only find out when you open this page or spot a problem on your site.</li>
                                  </ul>
-                                 <p>Safety nets: the replaced files are backed up before every update, .htaccess is never overwritten, and if the homepage or admin page returns a server error right after the update, the backup is restored automatically. These checks do not catch every problem.</p>
-                                 <p>Only turn this on if you do not modify the CMS files and you check your site regularly.</p>
+                                 <p>To limit the damage, your files are backed up before each update, .htaccess is never touched, and if the homepage or admin stops working right after an update, the backup is put back automatically. This won't catch every problem.</p>
+                                 <p>Only turn this on if you don't edit the CMS files and you check your site regularly.</p>
                              </div>
                          </div>
  
⋮ 6 unchanged lines ⋮
                              </form>
  
                              <h4 class="upd-heading">Cron job</h4>
-                             <p class="form-hint">Add one of these to your hosting's cron jobs, for example once a day. Automatic updates do nothing until a cron job calls them.</p>
+                             <p class="form-hint">Add one of these as a cron job in your hosting panel, for example once a day. Without a cron job, nothing will run.</p>
                              <label class="form-label" for="updCronCli">Command (recommended)</label>
                              <input type="text" id="updCronCli" class="form-input upd-copy" readonly value="<?= e('php ' . BASE_DIR . '/cron-update.php') ?>" onclick="this.select()">
-                             <label class="form-label" for="updCronUrl" style="margin-top: 12px;">Or call this URL (keep it secret)</label>
+                             <label class="form-label" for="updCronUrl" style="margin-top: 12px;">Or use this link (keep it private)</label>
                              <input type="text" id="updCronUrl" class="form-input upd-copy" readonly value="<?= e($cronUrl) ?>" onclick="this.select()">
                              <p class="form-hint">Example: <code>0 4 * * * wget -qO- "<?= e($cronUrl) ?>"</code></p>
-                             <form method="POST" class="upd-actions" onsubmit="return confirm('Generate a new cron URL? The current URL will stop working.');">
+                             <form method="POST" class="upd-actions" onsubmit="return confirm('Create a new cron link? The current one will stop working.');">
                                  <input type="hidden" name="csrf_token" value="<?= e($csrf_token) ?>">
                                  <input type="hidden" name="action" value="regenerate_cron_token">
-                                 <button type="submit" class="btn btn-secondary btn-sm">Generate new URL</button>
+                                 <button type="submit" class="btn btn-secondary btn-sm">Create new link</button>
                              </form>
                          <?php elseif (!$updaterEnabled): ?>
-                             <p class="form-hint">Enable the updater above to use automatic updates.<?= $autoUpdate ? ' Automatic updates are switched on but will not run while the updater is disabled.' : '' ?></p>
+                             <p class="form-hint">Turn on the updater above to use automatic updates.<?= $autoUpdate ? ' Automatic updates are on, but they won\'t run while the updater is off.' : '' ?></p>
                          <?php else: ?>
                              <form method="POST" class="upd-auto-form">
                                  <input type="hidden" name="csrf_token" value="<?= e($csrf_token) ?>">
⋮ 1 unchanged line ⋮
                                  <input type="hidden" name="enabled" value="1">
                                  <label class="checkbox-label">
                                      <input type="checkbox" name="accept_risk" value="1" required>
-                                     <span>I understand that updates will be installed without my approval and may break the site or overwrite my changes.</span>
+                                     <span>I understand that updates will be installed without asking me, and could break my site or overwrite my changes.</span>
                                  </label>
                                  <button type="submit" class="btn btn-secondary">Turn on automatic updates</button>
                              </form>
⋮ 20 unchanged lines ⋮
                              <?php elseif ($result['status'] === 'current'): ?>
                                  <p><strong>You are running the latest version.</strong></p>
                              <?php else: ?>
-                                 <p>This installation (<?= e($currentVersion) ?>) is newer than the published version (<?= e($result['remote_version']) ?>).</p>
+                                 <p>Your version (<?= e($currentVersion) ?>) is newer than the latest release (<?= e($result['remote_version']) ?>).</p>
                              <?php endif; ?>
  
                              <?php if (!empty($result['changelog'])): ?>
⋮ 11 unchanged lines ⋮
                                      <input type="hidden" name="action" value="download_release">
                                      <button type="submit" class="btn btn-primary">Download and show changes</button>
                                  </form>
-                                 <p class="form-hint">The release is downloaded temporarily and compared with your files. Nothing is installed or changed.</p>
+                                 <p class="form-hint">This downloads the new version and compares it with your files. Nothing is installed or changed yet.</p>
                              <?php elseif (empty($result['files'])): ?>
-                                 <p class="form-hint">Your files are identical to the release.</p>
+                                 <p class="form-hint">Your files already match this version.</p>
                              <?php elseif ($result['status'] !== 'ahead'): ?>
                                  <?php $installLabel = $result['status'] === 'update' ? 'Install version' : 'Sync your files with version'; ?>
-                                 <form method="POST" class="upd-actions" onsubmit="return confirm(<?= e(json_encode($installLabel . ' ' . $result['remote_version'] . '? A backup of the files being replaced is saved first, and everything is rolled back if a file cannot be written.')) ?>);">
+                                 <form method="POST" class="upd-actions" onsubmit="return confirm(<?= e(json_encode($installLabel . ' ' . $result['remote_version'] . '? Your current files are backed up first, and if anything goes wrong, everything is put back the way it was.')) ?>);">
                                      <input type="hidden" name="csrf_token" value="<?= e($csrf_token) ?>">
                                      <input type="hidden" name="action" value="install_release">
                                      <?php if (in_array('.htaccess', array_column($result['files'], 'path'), true)): ?>
                                          <label class="checkbox-label">
                                              <input type="checkbox" name="overwrite_htaccess" value="1">
-                                             <span>Overwrite .htaccess (your own rules in it will be lost)</span>
+                                             <span>Also replace .htaccess (any rules you added to it will be lost)</span>
                                          </label>
                                      <?php endif; ?>
                                      <button type="submit" class="btn btn-primary"><?= $result['status'] === 'update' ? 'Install update' : 'Sync files with release' ?></button>
                                  </form>
-                                 <p class="form-hint">Files are backed up to updater-files/backups before being replaced. Your content (media/ and your own files in data/) is never touched; only data/default* files are updated.</p>
+                                 <p class="form-hint">Your current files are backed up before anything is replaced. Your content, like uploaded media, pages and settings, is never touched.</p>
                              <?php endif; ?>
                          </div>
                      </div>
⋮ 51 unchanged lines ⋮
                          <?php endforeach; ?>
  
                          <?php if ($result['skipped'] > 0): ?>
-                             <p class="form-hint"><?= (int) $result['skipped'] ?> files were skipped because they are identical to yours or have an invalid path.</p>
+                             <p class="form-hint"><?= (int) $result['skipped'] ?> files were skipped because they already match yours or couldn't be placed safely.</p>
                          <?php endif; ?>
                      <?php endif; ?>
                  <?php endif; ?>
⋮ 29 unchanged lines ⋮
                                              <?php endif; ?>
                                          </td>
                                          <td class="upd-row-actions">
-                                             <form method="POST" onsubmit="return confirm('Restore the files from this backup? Files installed by the update will be replaced with the saved versions.');">
+                                             <form method="POST" onsubmit="return confirm('Restore this backup? The files from the update will be swapped back to the saved versions.');">
                                                  <input type="hidden" name="csrf_token" value="<?= e($csrf_token) ?>">
                                                  <input type="hidden" name="action" value="restore_backup">
                                                  <input type="hidden" name="backup_id" value="<?= e($backup['id']) ?>">
                                                  <button type="submit" class="btn btn-secondary btn-sm">Restore</button>
                                              </form>
-                                             <form method="POST" onsubmit="return confirm('Delete this backup permanently?');">
+                                             <form method="POST" onsubmit="return confirm('Delete this backup? You can\'t undo this.');">
                                                  <input type="hidden" name="csrf_token" value="<?= e($csrf_token) ?>">
                                                  <input type="hidden" name="action" value="delete_backup">
                                                  <input type="hidden" name="backup_id" value="<?= e($backup['id']) ?>">
⋮ 16 unchanged lines ⋮
admin/upload.php Modified +19 -19
⋮ 44 unchanged lines ⋮
  if ($_SERVER['REQUEST_METHOD'] === 'GET') {
      $kind = $_GET['list'] ?? 'image';
      if (!isset($types[$kind])) {
-         media_respond(['success' => false, 'error' => 'Unknown media type.'], 400);
+         media_respond(['success' => false, 'error' => "This file type isn't supported here."], 400);
      }
  
      $items = [];
⋮ 22 unchanged lines ⋮
  
  $postMax = media_ini_bytes((string) ini_get('post_max_size'));
  if (empty($_FILES) && empty($_POST) && (int) ($_SERVER['CONTENT_LENGTH'] ?? 0) > 0 && $postMax > 0) {
-     media_respond(['success' => false, 'error' => 'File is too large for the server (post_max_size = ' . ini_get('post_max_size') . ').'], 413);
+     media_respond(['success' => false, 'error' => 'This file is too big for your server. The limit is ' . ini_get('post_max_size') . '.'], 413);
  }
  
  if (!verify_csrf_token((string) ($_POST['csrf_token'] ?? ''))) {
-     media_respond(['success' => false, 'error' => 'Invalid request. Refresh the page and try again.'], 403);
+     media_respond(['success' => false, 'error' => 'Your session timed out. Refresh the page and try again.'], 403);
  }
  
  $kind = (string) ($_POST['kind'] ?? '');
  if (!isset($types[$kind])) {
-     media_respond(['success' => false, 'error' => 'Unknown media type.'], 400);
+     media_respond(['success' => false, 'error' => "This file type isn't supported here."], 400);
  }
  
  $file = $_FILES['file'] ?? null;
  if (!$file || !is_array($file) || is_array($file['name'] ?? null)) {
-     media_respond(['success' => false, 'error' => 'No file received.'], 400);
+     media_respond(['success' => false, 'error' => "The file didn't arrive. Please try again."], 400);
  }
  
  if ($file['error'] !== UPLOAD_ERR_OK) {
      $messages = [
-         UPLOAD_ERR_INI_SIZE => 'File is too large for the server (upload_max_filesize = ' . ini_get('upload_max_filesize') . ').',
-         UPLOAD_ERR_FORM_SIZE => 'File is too large.',
-         UPLOAD_ERR_PARTIAL => 'Upload was interrupted. Try again.',
-         UPLOAD_ERR_NO_FILE => 'No file received.',
-         UPLOAD_ERR_NO_TMP_DIR => 'Server temporary folder is missing.',
-         UPLOAD_ERR_CANT_WRITE => 'Server could not write the file.',
-         UPLOAD_ERR_EXTENSION => 'Upload blocked by a server extension.',
+         UPLOAD_ERR_INI_SIZE => 'This file is too big for your server. The limit is ' . ini_get('upload_max_filesize') . '.',
+         UPLOAD_ERR_FORM_SIZE => 'This file is too big.',
+         UPLOAD_ERR_PARTIAL => "The upload was cut off. Please try again.",
+         UPLOAD_ERR_NO_FILE => "The file didn't arrive. Please try again.",
+         UPLOAD_ERR_NO_TMP_DIR => "Your server is missing its temporary folder. Ask your hosting provider to check it.",
+         UPLOAD_ERR_CANT_WRITE => "Your server couldn't save the file. Check the folder permissions.",
+         UPLOAD_ERR_EXTENSION => "Your server blocked this upload.",
      ];
      media_respond(['success' => false, 'error' => $messages[$file['error']] ?? 'Upload failed.'], 400);
  }
  
  if (!is_uploaded_file($file['tmp_name'])) {
-     media_respond(['success' => false, 'error' => 'Invalid upload.'], 400);
+     media_respond(['success' => false, 'error' => "The upload didn't work. Please try again."], 400);
  }
  
  if ($file['size'] > MEDIA_MAX_BYTES) {
-     media_respond(['success' => false, 'error' => 'File is larger than ' . media_format_bytes(MEDIA_MAX_BYTES) . '.'], 413);
+     media_respond(['success' => false, 'error' => 'This file is bigger than ' . media_format_bytes(MEDIA_MAX_BYTES) . '.'], 413);
  }
  
  $original = (string) $file['name'];
  $ext = strtolower(pathinfo($original, PATHINFO_EXTENSION));
  if (!isset($types[$kind][$ext])) {
-     media_respond(['success' => false, 'error' => 'Unsupported ' . $kind . ' format: .' . $ext . '. Allowed: ' . implode(', ', array_keys($types[$kind])) . '.'], 415);
+     media_respond(['success' => false, 'error' => '.' . $ext . ' files can\'t be used here. You can upload: ' . implode(', ', array_keys($types[$kind])) . '.'], 415);
  }
  
  if (function_exists('finfo_open')) {
⋮ 1 unchanged line ⋮
      $mime = $finfo ? (string) finfo_file($finfo, $file['tmp_name']) : '';
      unset($finfo);
      if ($mime !== '' && !in_array($mime, $types[$kind][$ext], true) && $mime !== 'application/octet-stream') {
-         media_respond(['success' => false, 'error' => 'File content does not match its extension (' . $mime . ').'], 415);
+         media_respond(['success' => false, 'error' => "This file's content doesn't match its extension."], 415);
      }
  }
  
  if ($kind === 'image' && $ext !== 'ico' && $ext !== 'avif' && @getimagesize($file['tmp_name']) === false) {
-     media_respond(['success' => false, 'error' => 'File is not a valid image.'], 415);
+     media_respond(['success' => false, 'error' => "This file isn't a valid image."], 415);
  }
  
  if (!is_dir($mediaDir) && !mkdir($mediaDir, 0755, true) && !is_dir($mediaDir)) {
-     media_respond(['success' => false, 'error' => 'Could not create the media folder.'], 500);
+     media_respond(['success' => false, 'error' => "Couldn't create the media folder. Check the folder permissions on your server."], 500);
  }
  
  $base = slugify(pathinfo($original, PATHINFO_FILENAME));
⋮ 4 unchanged lines ⋮
  } while (file_exists($target));
  
  if (!move_uploaded_file($file['tmp_name'], $target)) {
-     media_respond(['success' => false, 'error' => 'Could not save the file.'], 500);
+     media_respond(['success' => false, 'error' => "Couldn't save the file. Please try again."], 500);
  }
  @chmod($target, 0644);
  
⋮ 8 unchanged lines ⋮
api/ai-helper.php Modified +7 -7
⋮ 18 unchanged lines ⋮
  }
  
  if (!is_logged_in()) {
-     ai_respond(401, ['ok' => false, 'error' => 'Your session has expired. Reload the page and log in again.']);
+     ai_respond(401, ['ok' => false, 'error' => 'Your session expired. Reload the page and sign in again.']);
  }
  check_ip_ban();
  
  if (!verify_csrf_token((string) ($_SERVER['HTTP_X_CSRF_TOKEN'] ?? ''))) {
-     ai_respond(403, ['ok' => false, 'error' => 'Invalid security token. Reload the page and try again.']);
+     ai_respond(403, ['ok' => false, 'error' => 'Your session timed out. Reload the page and try again.']);
  }
  
  $maxBody = 400000;
  if ((int) ($_SERVER['CONTENT_LENGTH'] ?? 0) > $maxBody) {
-     ai_respond(413, ['ok' => false, 'error' => 'The request is too large.']);
+     ai_respond(413, ['ok' => false, 'error' => 'This request is too large.']);
  }
  $input = json_decode((string) file_get_contents('php://input', false, null, 0, $maxBody + 1), true);
  if (!is_array($input)) {
-     ai_respond(400, ['ok' => false, 'error' => 'Invalid request.']);
+     ai_respond(400, ['ok' => false, 'error' => 'Something went wrong. Reload the page and try again.']);
  }
  
  $now = time();
  $hits = array_values(array_filter((array) ($_SESSION['ai_helper_hits'] ?? []), static fn($time) => is_int($time) && $now - $time < 600));
  if (count($hits) >= 30) {
-     ai_respond(429, ['ok' => false, 'error' => 'Too many AI requests. Please wait a few minutes.']);
+     ai_respond(429, ['ok' => false, 'error' => 'Too many AI requests in a short time. Please wait a few minutes.']);
  }
  $hits[] = $now;
  $_SESSION['ai_helper_hits'] = $hits;
⋮ 2 unchanged lines ⋮
  
  $settings = AiHelper::settings();
  if (!$settings['enabled'] || $settings['key'] === '') {
-     ai_respond(400, ['ok' => false, 'error' => 'The AI helper is not enabled. Add an API key in Settings > AI Helper.']);
+     ai_respond(400, ['ok' => false, 'error' => 'The AI helper is turned off. Turn it on and add an API key in Settings, under AI Helper.']);
  }
  
  $action = (string) ($input['action'] ?? 'chat');
⋮ 9 unchanged lines ⋮
  
  $prompt = trim((string) ($input['prompt'] ?? ''));
  if ($prompt === '' || mb_strlen($prompt) > 4000) {
-     ai_respond(400, ['ok' => false, 'error' => 'Write a request of up to 4000 characters.']);
+     ai_respond(400, ['ok' => false, 'error' => 'Type a message of up to 4000 characters.']);
  }
  
  if (($input['mode'] ?? '') === 'settings') {
⋮ 44 unchanged lines ⋮
api/analytics.php Modified +5 -5
⋮ 17 unchanged lines ⋮
  }
  
  if (!is_logged_in()) {
-     analytics_respond(401, ['ok' => false, 'error' => 'Your session has expired. Reload the page and log in again.']);
+     analytics_respond(401, ['ok' => false, 'error' => 'Your session expired. Reload the page and sign in again.']);
  }
  check_ip_ban();
  
  if (!verify_csrf_token((string) ($_SERVER['HTTP_X_CSRF_TOKEN'] ?? ''))) {
-     analytics_respond(403, ['ok' => false, 'error' => 'Invalid security token. Reload the page and try again.']);
+     analytics_respond(403, ['ok' => false, 'error' => 'Your session timed out. Reload the page and try again.']);
  }
  
  $maxBody = 4194304;
  if ((int) ($_SERVER['CONTENT_LENGTH'] ?? 0) > $maxBody) {
-     analytics_respond(413, ['ok' => false, 'error' => 'The scripts are too large.']);
+     analytics_respond(413, ['ok' => false, 'error' => 'Your scripts are too large to save.']);
  }
  
  $input = json_decode((string) file_get_contents('php://input', false, null, 0, $maxBody + 1), true);
  $decoded = is_array($input) && is_string($input['payload'] ?? null) ? base64_decode($input['payload'], true) : false;
  $data = $decoded !== false ? json_decode($decoded, true) : null;
  if (!is_array($data)) {
-     analytics_respond(400, ['ok' => false, 'error' => 'Invalid request.']);
+     analytics_respond(400, ['ok' => false, 'error' => 'Something went wrong. Reload the page and try again.']);
  }
  
  $system = get_system_settings();
  $system['analytics'] = flatly_clean_analytics($data);
  if (!save_page('system', $system)) {
-     analytics_respond(500, ['ok' => false, 'error' => 'Could not write data/system.php. Check the folder permissions.']);
+     analytics_respond(500, ['ok' => false, 'error' => "Couldn't save. Check the folder permissions on your server."]);
  }
  
  analytics_respond(200, ['ok' => true, 'analytics' => $system['analytics']]);
⋮ 1 unchanged line ⋮
assets/js/admin-editor.js Modified

Not shown (binary file or too large to diff).

assets/js/ai-helper.js Modified +9 -9
⋮ 117 unchanged lines ⋮
      header.append(headerText, undoButton, closeButton);
  
      const messages = el('div', 'ai-messages');
-     messages.appendChild(el('div', 'ai-note', 'Your page text is sent to ' + cfg.provider + '. Changes are only proposals: nothing is applied until you press Apply, and you can undo it afterwards.'));
+     messages.appendChild(el('div', 'ai-note', 'Your page text is sent to ' + cfg.provider + '. The AI only suggests changes, nothing happens until you click Apply, and you can undo it.'));
  
      const chips = el('div', 'ai-chips');
      const scopeRow = el('div', 'ai-scope');
⋮ 121 unchanged lines ⋮
      function refreshScope() {
          if (isSettings) {
              const tab = activeTab();
-             scopeTab.textContent = SETTINGS_TABS.includes(tab) ? TAB_LABELS[tab] + ' tab only' : 'Not available on this tab';
+             scopeTab.textContent = SETTINGS_TABS.includes(tab) ? TAB_LABELS[tab] + ' tab only' : "Doesn't work on this tab";
              return;
          }
          const selectedIndex = scopeBlock ? blocks.indexOf(scopeBlock) : -1;
⋮ 2 unchanged lines ⋮
          whole.value = 'all';
          scopeSelect.appendChild(whole);
          blocks.forEach((block, index) => {
-             const option = el('option', null, (index + 1) + '. ' + block.type + ' – ' + String(getBlockTitle(block) || '').slice(0, 40));
+             const option = el('option', null, (index + 1) + '. ' + block.type + ': ' + String(getBlockTitle(block) || '').slice(0, 40));
              option.value = String(index);
              scopeSelect.appendChild(option);
          });
⋮ 95 unchanged lines ⋮
              refresh();
          }
          undoButton.disabled = undoStack.length === 0;
-         addMessage('note', 'Last applied change was undone.');
+         addMessage('note', 'Undid the last change.');
      }
  
      function renderProposal(proposal) {
⋮ 17 unchanged lines ⋮
              actions.remove();
              card.appendChild(el('div', result.done ? 'ai-applied' : 'ai-warning',
                  result.done
-                     ? 'Applied' + (result.skipped ? ' (' + result.skipped + ' skipped because the page changed).' : '.')
-                     : 'Nothing was applied because the page has changed since the proposal.'));
+                     ? 'Applied' + (result.skipped ? ', but ' + result.skipped + ' skipped because the page changed in the meantime.' : '.')
+                     : 'Nothing was applied, the page changed after this suggestion was made.'));
              history.push({ role: 'assistant', text: 'Applied: ' + proposal.operations.map(op => describeOp(op, proposal)).join('; ') });
          });
          dismissButton.addEventListener('click', () => {
⋮ 67 unchanged lines ⋮
              pending.classList.remove('ai-pending');
              if (!data || !data.ok) {
                  pending.classList.add('ai-msg-error');
-                 pending.textContent = (data && data.error) || 'The request failed (HTTP ' + response.status + ').';
+                 pending.textContent = (data && data.error) || 'Something went wrong (error ' + response.status + ').';
                  return;
              }
  
              history.push({ role: 'user', text: prompt });
-             pending.textContent = data.message || (data.operations.length ? 'Here is what I propose.' : 'No changes needed.');
+             pending.textContent = data.message || (data.operations.length ? "Here's what I suggest." : 'No changes needed.');
              history.push({ role: 'assistant', text: pending.textContent });
              if (data.operations.length) {
                  renderProposal(Object.assign({ operations: data.operations, warnings: data.warnings }, context));
⋮ 3 unchanged lines ⋮
          } catch (error) {
              pending.classList.remove('ai-pending');
              pending.classList.add('ai-msg-error');
-             pending.textContent = 'Could not reach the server.';
+             pending.textContent = "Couldn't reach the server.";
          } finally {
              busy = false;
              sendButton.disabled = false;
⋮ 51 unchanged lines ⋮
newsletter/manager.php Modified

Not shown (binary file or too large to diff).

newsletter/settings.php Modified +13 -13
⋮ 12 unchanged lines ⋮
      $confirm_password = $_POST['confirm_password'];
      
      if (empty($username) || empty($password)) {
-         $message = 'Username and password are required';
+         $message = 'Please enter a username and password.';
          $message_type = 'error';
      } elseif (strlen($username) < 3) {
-         $message = 'Username must be at least 3 characters';
+         $message = 'The username needs at least 3 characters.';
          $message_type = 'error';
      } elseif (strlen($password) < 8) {
-         $message = 'Password must be at least 8 characters';
+         $message = 'The password needs at least 8 characters.';
          $message_type = 'error';
      } elseif ($password !== $confirm_password) {
-         $message = 'Passwords do not match';
+         $message = "The passwords don't match.";
          $message_type = 'error';
      } elseif (!preg_match('/^[a-zA-Z0-9_-]+$/', $username)) {
-         $message = 'Username can only contain letters, numbers, underscore and dash';
+         $message = 'Use only letters, numbers, underscores and hyphens in the username.';
          $message_type = 'error';
      } else {
          $accounts = file_exists('hash.php') ? include('hash.php') : [];
          
          if (isset($accounts[$username])) {
-             $message = 'Username already exists';
+             $message = 'That username is already taken.';
              $message_type = 'error';
          } else {
  
⋮ 6 unchanged lines ⋮
              $php_content = "<?php\n// hash.php - Hashed Admin Credentials\n// DO NOT EDIT MANUALLY - Use create-acc.php to manage accounts\n\nreturn " . var_export($accounts, true) . ";\n";
              
              if (file_put_contents('hash.php', $php_content)) {
-                 $message = 'Account created successfully! You can now login to manager.php';
+                 $message = 'Account created. You can now sign in to the newsletter manager.';
                  $message_type = 'success';
                  
                  $_POST = [];
              } else {
-                 $message = 'Error: Could not write to hash.php (check permissions)';
+                 $message = "Couldn't save the account. Check the folder permissions on your server.";
                  $message_type = 'error';
              }
          }
⋮ 10 unchanged lines ⋮
          if (empty($accounts)) {
              
              if (unlink('hash.php')) {
-                 $message = 'Last account deleted. hash.php removed.';
+                 $message = 'Account deleted. There are no newsletter accounts left.';
                  $message_type = 'success';
              }
          } else {
              $php_content = "<?php\n// hash.php - Hashed Admin Credentials\n// DO NOT EDIT MANUALLY - Use create-acc.php to manage accounts\n\nreturn " . var_export($accounts, true) . ";\n";
              
              if (file_put_contents('hash.php', $php_content)) {
-                 $message = 'Account deleted successfully';
+                 $message = 'Account deleted.';
                  $message_type = 'success';
              }
          }
⋮ 275 unchanged lines ⋮
                  <line x1="23" y1="11" x2="17" y2="11"></line>
              </svg>
              <h1>Account Creator</h1>
-             <p>Create and manage admin accounts for Newsletter Manager</p>
+             <p>Add or remove the accounts that can use the newsletter manager</p>
          </div>
          
          <?php if ($message): ?>
⋮ 87 unchanged lines ⋮
                          <td><strong><?= htmlspecialchars($username) ?></strong></td>
                          <td><?= htmlspecialchars($account['created_at']) ?></td>
                          <td>
-                             <a href="?delete=<?= urlencode($username) ?>" class="btn btn-danger btn-sm" onclick="return confirm('Delete account \'<?= htmlspecialchars($username) ?>\'?\n\nThis action cannot be undone.')">
+                             <a href="?delete=<?= urlencode($username) ?>" class="btn btn-danger btn-sm" onclick="return confirm('Delete account \'<?= htmlspecialchars($username) ?>\'?\n\nYou can\'t undo this.')">
                                  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                      <polyline points="3 6 5 6 21 6"></polyline>
                                      <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
⋮ 15 unchanged lines ⋮
                      <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
                      <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
                  </svg>
-                 <p>No accounts created yet.<br>Create your first admin account above.</p>
+                 <p>No accounts yet.<br>Add your first one above.</p>
              </div>
          </div>
          <?php endif; ?>
⋮ 54 unchanged lines ⋮
version.txt Modified +1 -1
- 3.0.0.1
+ 3.0.0.2