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.