HexabeeNovel
RC Sürüm: Bu site geliştirilme aşamasındadır. Hata/öneri için buradan bildirimde bulunun.
2026 - 2027 İlk Öğretim Yılı Başladı

Eklenti Geliştirme

Her içerik bir eklentidir. Eklenti = plugins/<slug>/ klasörü + data/ altında dosya. SQL yok, kurulum yok — dosya düşünce sistem tanır.

İskelet

plugins/ornek/
  plugin.json   # panel listesi için kimlik
  plugin.php    # kayıt + fonksiyonlar

plugin.json:

{
  "name": "Örnek",
  "description": "Ne işe yarar, tek cümle.",
  "version": "1.0.0",
  "content_type": "ornek",
  "data_dir": "data/ornek",
  "route": "/ornek/{slug}"
}

plugin.php en azından türü kaydeder:

<?php
cms_register_content_type('ornek', [
    'label' => 'Örnek', 'data_dir' => 'data/ornek',
    'plugin' => 'ornek', 'route' => '/ornek/{slug}', 'icon' => 'bi-stars',
    'stat' => 'Örnekler', 'count' => fn() => count(getOrnekler()),
]);

Kurallar

Kural Neden
Fonksiyonları if (!function_exists(...)) ile sar Çift yüklemede fatal vermez
count içinde başka eklentinin fonksiyonunu çağırıyorsan function_exists kontrol et O eklenti kapalıyken dashboard patlamasın
Veri okuma HEXABEE_ROOT . '/data/...' üzerinden Kök değişse bile yol şaşmaz
Slug = dosya adı, tarih = dosya zamanı Felsefe değişmez (bkz. Mimari)

Tam Örnek

Aşağıdaki plugin.php kopyala-çalıştır düzeyindedir: kayıt + liste + tekil + HTML + blok hepsi içinde. getOrnekler() ve ornekHtml() burada tanımlı.

<?php
// plugins/ornek/plugin.php — tam örnek (SQL YOK: data/ornek/*.md)
cms_register_content_type('ornek', [
    'label' => 'Örnek', 'data_dir' => 'data/ornek',
    'plugin' => 'ornek', 'route' => '/ornek/{slug}', 'icon' => 'bi-stars',
    'stat' => 'Örnekler', 'count' => fn() => count(getOrnekler()),
]);

if (!function_exists('getOrnekler')) {
function getOrnekler() {
    $dir = HEXABEE_ROOT . '/data/ornek';
    $out = [];
    if (!is_dir($dir)) return $out;
    foreach (glob($dir . '/*.md') as $f) {
        $p = parseMdFile($f);
        if (!$p) continue;
        $slug = basename($f, '.md');
        $out[] = [
            'slug' => $slug,
            'title' => $p['meta']['title'] ?? $slug,
            'content' => $p['body'],
            'file' => $f,
            'comments' => $p['meta']['comments'] ?? true,
            'meta' => $p['meta'],
        ];
    }
    usort($out, fn($a, $b) => filemtime($b['file']) <=> filemtime($a['file']));
    return apply_filters('ornekler_list', $out);
}
}

if (!function_exists('getOrnek')) {
function getOrnek($slug) {
    $slug = basename((string)$slug);
    $path = HEXABEE_ROOT . '/data/ornek/' . $slug . '.md';
    if (!is_file($path)) return null;
    $p = parseMdFile($path);
    if (!$p) return null;
    return [
        'slug' => $slug,
        'title' => $p['meta']['title'] ?? $slug,
        'content' => $p['body'],
        'comments' => $p['meta']['comments'] ?? true,
        'meta' => $p['meta'],
    ];
}
}

if (!function_exists('ornekHtml')) {
// Liste kartı — hem {% ornek %} bloğunda hem ornek.php'de kullanılır
function ornekHtml($limit = 3) {
    $items = array_slice(getOrnekler(), 0, (int)$limit);
    if (empty($items)) return '';
    $h = '<div class="ornek-liste">';
    foreach ($items as $it) {
        $h .= '<a href="/ornek/' . e($it['slug']) . '">' . e($it['title']) . '</a>';
    }
    return $h . '</div>';
}
}

cms_register_block('ornek', fn() => ornekHtml());

Örnek veri (data/ornek/merhaba.md):

---
title: Merhaba
comments: true
---

İlk örnek kayıt. Burası gövde — Markdown serbest.

Ön yüz iskeleti (ornek.php — liste + tekil tek dosyada):

<?php
require_once 'config.php';
require_once 'includes/functions.php';
if (!cms_plugin_on('ornek')) cms_404('Bulunamadı.');
$slug = basename($_GET['slug'] ?? '');
if ($slug === '') {
    foreach (getOrnekler() as $it) echo ornekHtml(99);
} else {
    $it = getOrnek($slug);
    if (!$it) cms_404('Bulunamadı.');
    echo '<h1>' . e($it['title']) . '</h1>';
    echo ornekHtml(3);
}

Kancalar

// Filtre: listeyi değiştirip geri ver
add_filter('novels_list', fn($list) => $list);

// Aksiyon: <head> içine CSS bas (tema tarafı da bunu kullanır)
add_action('cms_head', fn() => print('<link rel="stylesheet" href="/x.css">'));

// Blok: index.md içine {% ornek %} olarak gömülür
cms_register_block('ornek', fn() => ornekHtml());

Hazır filtreler: novels_list, search_index, blog_tags, comments_list, cms_blocks_map. Hazır aksiyonlar: cms_head, cms_theme_loaded, cms_plugins_loaded.

Kapılar (graceful degradation)

Kapalı eklentinin sayfası 500 değil 404 verir, menüsü basılmaz:

if (!cms_plugin_on('ornek')) cms_404('Bulunamadı.');
echo cms_ad('slot-adi'); // ads kapalıysa boş döner, şablon ellemeden

members, pages, settings kilitlidir — panelden kapatılamaz (giriş/menü/ayar ölmesin diye).

Aktivasyon

data/plugins.json içindeki active listesi + Panel → Eklentiler. Yeni klasör düşünce listede belirir; aç/kapa anında geçerlidir, önbellek yoktur.

Soru Sor