WebOrbiton

Code

Publisium

A modern, fast CMS for building article and blog websites.

Version history

Version 1.0.0.8 2026-10-01

Browse files
Files changed11
Lines added+51
Lines removed-8
assets/site.css Modified

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

category.php Modified +9 -0
⋮ 58 unchanged lines ⋮
  $pageDescription = $category['description'] ?? '';
  $showBanner = false;
  $pageType = 'category';
+ $rssFeedUrl = $settings['sitemap_rss_enabled'] === '1' && $settings['rss_category_feeds_enabled'] === '1'
+     ? 'rss.php?category=' . rawurlencode((string) $category['slug'])
+     : null;
  
  require __DIR__ . '/includes/front-header.php';
  
⋮ 1 unchanged line ⋮
  <h1 class="category-title"><?= htmlspecialchars($category['name']) ?></h1>
  <?php if (!empty($category['description'])): ?>
      <p class="category-description"><?= htmlspecialchars($category['description']) ?></p>
+ <?php endif; ?>
+ <?php if ($rssFeedUrl !== null): ?>
+     <a href="<?= htmlspecialchars($rssFeedUrl, ENT_QUOTES) ?>" class="category-rss-link">
+         <svg class="icon icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 11a9 9 0 0 1 9 9"/><path d="M4 4a16 16 0 0 1 16 16"/><circle cx="5" cy="19" r="1"/></svg>
+         <?= htmlspecialchars(Language::get('category_rss', 'RSS feed for this category')) ?>
+     </a>
  <?php endif; ?>
  
  <?= SiteFront::renderAds('category-top') ?>
⋮ 21 unchanged lines ⋮
dashboard-actions/save-settings.php Modified +1 -0
⋮ 116 unchanged lines ⋮
          'tags_enabled' => isset($_POST['tags_enabled']) ? '1' : '0',
          'related_articles_enabled' => isset($_POST['related_articles_enabled']) ? '1' : '0',
          'sitemap_rss_enabled' => isset($_POST['sitemap_rss_enabled']) ? '1' : '0',
+         'rss_category_feeds_enabled' => isset($_POST['rss_category_feeds_enabled']) ? '1' : '0',
          'article_loading_mode' => in_array($_POST['article_loading_mode'] ?? 'scroll', ['scroll', 'static'], true) ? $_POST['article_loading_mode'] : 'scroll',
          'pagination_style' => in_array($_POST['pagination_style'] ?? 'numbered', ['numbered', 'load_more', 'infinite_scroll'], true) ? $_POST['pagination_style'] : 'numbered',
          'articles_per_page_home' => (string) $articlesPerPageHome,
⋮ 118 unchanged lines ⋮
dashboard-views/profile.php Modified +1 -1
⋮ 72 unchanged lines ⋮
  <option value="Person" <?= $schemaType === 'Person' ? 'selected' : '' ?>>A person (Person)</option>
  <option value="Organization" <?= $schemaType === 'Organization' ? 'selected' : '' ?>>An organization, e.g. an editorial team or agency (Organization)</option>
  </select>
- <p style="font-size:12px;color:var(--muted);margin-top:6px;">Used in the article data Google reads. It changes right away, without review.</p>
+ <p style="font-size:12px;color:var(--muted);margin-top:6px;">Shown to search engines with your articles. It changes right away, without review.</p>
  <?php endif; ?>
  
  <p style="font-size:12px;color:var(--muted);margin-top:10px;">Your profile photo is set by an administrator in Team.</p>
⋮ 8 unchanged lines ⋮
includes/front-header.php Modified +3 -0
⋮ 36 unchanged lines ⋮
      <?php if (!empty($pageSchema)): ?>
          <?= $pageSchema ?>
      <?php endif; ?>
+     <?php if (!empty($rssFeedUrl)): ?>
+         <link rel="alternate" type="application/rss+xml" title="<?= htmlspecialchars($resolvedPageTitle, ENT_QUOTES) ?>" href="<?= htmlspecialchars($rssFeedUrl, ENT_QUOTES) ?>">
+     <?php endif; ?>
      <?= Asset::favicon() ?>
      <?php if (($settings['pwa_enabled'] ?? '0') === '1'): ?>
          <?php $pwaIconUrl = !empty($settings['pwa_icon_url']) ? $settings['pwa_icon_url'] : ($settings['site_logo_url'] ?? ''); ?>
⋮ 129 unchanged lines ⋮
includes/language.php Modified +1 -0
⋮ 99 unchanged lines ⋮
          'comments_coming_soon' => 'Comments are coming soon.',
          'category_not_found' => 'We couldn’t find this category.',
          'category_no_articles' => 'No articles in this category yet.',
+         'category_rss' => 'RSS feed for this category',
          'account_password_too_short' => 'Your new password needs at least 8 characters.',
          'account_profile_saved' => 'Your profile is saved.',
          'account_preferences_saved' => 'Your choices are saved.',
⋮ 99 unchanged lines ⋮
includes/site-front.php Modified +1 -0
⋮ 264 unchanged lines ⋮
              'indexnow_enabled' => '0',
              'indexnow_key' => '',
              'sitemap_rss_enabled' => '1',
+             'rss_category_feeds_enabled' => '0',
              'comments_enabled' => '0',
              'comments_provider' => 'disqus',
              'comments_shortname' => '',
⋮ 961 unchanged lines ⋮
rss.php Modified +33 -6
⋮ 15 unchanged lines ⋮
  $db = Database::site();
  $baseUrl = rtrim(Config::get('APP_URL', ''), '/') . Config::get('APP_BASE_PATH', '');
  
- $articles = $db->query(
-     "SELECT a.id, a.title, a.slug, a.excerpt, a.published_at FROM articles a WHERE a.status = 'published'" . Languages::articleFilterSql('a') . " ORDER BY a.published_at DESC LIMIT 30"
- )->fetchAll();
- $articles = Languages::articles($articles);
+ $category = null;
+ if (isset($_GET['category'])) {
+     if ($settings['rss_category_feeds_enabled'] !== '1') {
+         http_response_code(404);
+         exit;
+     }
  
+     $categoryStatement = $db->prepare('SELECT * FROM categories WHERE slug = :slug LIMIT 1');
+     $categoryStatement->execute(['slug' => trim((string) $_GET['category'])]);
+     $category = $categoryStatement->fetch() ?: null;
+     if ($category === null) {
+         http_response_code(404);
+         exit;
+     }
+     $category = Languages::categories([$category])[0];
+ }
+ 
+ $articlesStatement = $db->prepare(
+     "SELECT a.id, a.title, a.slug, a.excerpt, a.published_at, a.category_id, c.name AS category_name FROM articles a LEFT JOIN categories c ON c.id = a.category_id WHERE a.status = 'published'"
+     . ($category !== null ? ' AND a.category_id = :category_id' : '')
+     . Languages::articleFilterSql('a') . " ORDER BY a.published_at DESC LIMIT 30"
+ );
+ $articlesStatement->execute($category !== null ? ['category_id' => (int) $category['id']] : []);
+ $articles = Languages::articles($articlesStatement->fetchAll());
+ 
  header('Content-Type: application/rss+xml; charset=utf-8');
  
  echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
  echo '<rss version="2.0"><channel>' . "\n";
- echo '<title>' . htmlspecialchars($settings['site_name']) . '</title>' . "\n";
+ echo '<title>' . htmlspecialchars($category !== null ? $category['name'] . ' - ' . $settings['site_name'] : $settings['site_name']) . '</title>' . "\n";
  $channelLanguage = Languages::current();
  $channelLink = $channelLanguage === '' ? rtrim((string) Config::get('APP_URL', ''), '/') . SiteFront::homeUrl('') : rtrim((string) Config::get('APP_URL', ''), '/') . Languages::homeUrl($channelLanguage);
+ if ($category !== null) {
+     $channelLink = rtrim((string) Config::get('APP_URL', ''), '/') . rtrim((string) Config::get('APP_BASE_PATH', ''), '/') . Languages::prefix() . '/category.php?slug=' . rawurlencode((string) $category['slug']);
+ }
  echo '<link>' . htmlspecialchars($channelLink) . '</link>' . "\n";
  if ($channelLanguage !== '') {
      echo '<language>' . htmlspecialchars($channelLanguage) . '</language>' . "\n";
  }
- echo '<description>' . htmlspecialchars($settings['site_description']) . '</description>' . "\n";
+ $channelDescription = $category !== null && !empty($category['description']) ? (string) $category['description'] : $settings['site_description'];
+ echo '<description>' . htmlspecialchars($channelDescription) . '</description>' . "\n";
  
  foreach ($articles as $article) {
      $url = rtrim(Config::get('APP_URL', ''), '/') . SiteFront::articleUrl($article['slug']);
⋮ 2 unchanged lines ⋮
      echo '<link>' . htmlspecialchars($url) . '</link>';
      echo '<guid>' . htmlspecialchars($url) . '</guid>';
      echo '<description>' . htmlspecialchars((string) ($article['excerpt'] ?? '')) . '</description>';
+     if (!empty($article['category_name'])) {
+         echo '<category>' . htmlspecialchars((string) $article['category_name']) . '</category>';
+     }
      echo '<pubDate>' . htmlspecialchars(date(DATE_RSS, strtotime($article['published_at']))) . '</pubDate>';
      echo '</item>' . "\n";
  }
⋮ 3 unchanged lines ⋮
settings.php Modified

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

translations/language.php Modified +1 -0
⋮ 140 unchanged lines ⋮
      'comments_coming_soon' => 'Comments are coming soon.',
      'category_not_found' => 'We couldn’t find this category.',
      'category_no_articles' => 'No articles in this category yet.',
+     'category_rss' => 'RSS feed for this category',
      'account_password_too_short' => 'Your new password needs at least 8 characters.',
      'account_profile_saved' => 'Your profile is saved.',
      'account_preferences_saved' => 'Your choices are saved.',
⋮ 65 unchanged lines ⋮
version.txt Modified +1 -1
- 1.0.0.6
+ 1.0.0.8