getenv('DABIR_CONNECTOR_SECRET') ?: '',
'site_name' => 'My Blog',
'db' => [
'dsn' => 'mysql:host=localhost;dbname=YOUR_DB;charset=utf8mb4',
'user' => 'YOUR_DB_USER',
'pass' => 'YOUR_DB_PASSWORD',
],
// Your posts table. Set a column to null if you don't have it.
'posts' => [
'table' => 'posts',
'id' => 'id',
'title' => 'title',
'slug' => 'slug',
'content' => 'content',
'excerpt' => 'excerpt', // or null
'meta_description' => 'meta_description', // or null
'image' => 'image', // featured image column, or null
'image_alt' => null, // or column name
'category_id' => 'category_id', // or null
'faq' => null, // JSON column for FAQ, or null
'status' => 'status', // or null (then every post is "published")
'published_at' => 'published_at', // or null
'created_at' => 'created_at', // or null
'updated_at' => 'updated_at', // or null
],
// What your status column stores for draft / published posts.
'status_values' => ['draft' => 'draft', 'publish' => 'published'],
// Categories table, or null if you don't have categories.
'categories' => ['table' => 'categories', 'id' => 'id', 'name' => 'name'],
// Public URL of a post. {slug} and {id} are replaced.
'post_url' => 'https://example.com/blog/{slug}',
// Where downloaded images go (must be writable) and their public URL.
'images_dir' => __DIR__ . '/uploads/dabir',
'images_url' => 'https://example.com/uploads/dabir',
// Store the featured image in the DB as the full 'url' or as a 'path' relative to images_dir.
'image_store_as' => 'url',
// Images are only downloaded from these hosts.
'image_hosts' => ['cloud.ali-programmer.ir'],
// false = your theme shows the FAQ from the 'faq' column itself (no FAQ block inside the body).
'faq_in_content' => true,
// true = prepend Dabir's \n" . $body;
}
return $body;
}
function dabir_faq_json(array $in): ?string
{
$faq = [];
foreach ((array)($in['faq'] ?? []) as $i) {
$q = trim((string)($i['question'] ?? ''));
$a = trim((string)($i['answer'] ?? ''));
if ($q !== '' && $a !== '') {
$faq[] = ['question' => $q, 'answer' => $a];
}
}
return $faq ? json_encode($faq, JSON_UNESCAPED_UNICODE) : null;
}
function dabir_category_id(PDO $pdo, array $c, string $name): ?string
{
$cat = $c['categories'] ?? null;
$name = trim($name);
if (!$cat || $name === '') {
return null;
}
$st = $pdo->prepare('SELECT ' . dabir_q($cat['id']) . ' FROM ' . dabir_q($cat['table']) . ' WHERE ' . dabir_q($cat['name']) . ' = ? LIMIT 1');
$st->execute([$name]);
$id = $st->fetchColumn();
return $id === false ? null : (string)$id;
}
function dabir_unique_slug(PDO $pdo, array $c, string $text): string
{
$p = $c['posts'];
$base = mb_strtolower(trim($text));
$base = preg_replace('/[^\p{L}\p{N}\s-]+/u', '', $base);
$base = trim((string)preg_replace('/[\s-]+/u', '-', $base), '-');
$base = mb_substr($base ?: 'post', 0, 180);
$st = $pdo->prepare('SELECT COUNT(*) FROM ' . dabir_q($p['table']) . ' WHERE ' . dabir_q($p['slug']) . ' = ?');
$slug = $base;
for ($i = 2; ; $i++) {
$st->execute([$slug]);
if ((int)$st->fetchColumn() === 0) {
return $slug;
}
$slug = $base . '-' . $i;
}
}
function dabir_post_url(array $c, array $row): string
{
return strtr($c['post_url'], [
'{slug}' => rawurlencode((string)($row['slug'] ?? '')),
'{id}' => rawurlencode((string)($row['id'] ?? '')),
]);
}
function dabir_status_out(array $c, $value): string
{
if ($value === null) {
return 'publish';
}
return (string)$value === (string)$c['status_values']['publish'] ? 'publish' : 'draft';
}
/* ---------------- images (HTTPS + allowed host + real image only) ---------------- */
/** @return array{path: string, url: string}|null */
function dabir_image(array $c, $url): ?array
{
static $copied = [];
if (!is_string($url) || $url === '') {
return null;
}
if (array_key_exists($url, $copied)) {
return $copied[$url];
}
$parts = parse_url($url);
$host = strtolower($parts['host'] ?? '');
if (($parts['scheme'] ?? '') !== 'https' || !in_array($host, array_map('strtolower', $c['image_hosts']), true)) {
return $copied[$url] = null;
}
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
CURLOPT_MAXFILESIZE => 10 * 1024 * 1024,
]);
$data = curl_exec($ch);
$code = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if (!is_string($data) || $code !== 200 || strlen($data) > 10 * 1024 * 1024) {
return $copied[$url] = null;
}
$info = @getimagesizefromstring($data);
$ext = [IMAGETYPE_WEBP => 'webp', IMAGETYPE_PNG => 'png', IMAGETYPE_JPEG => 'jpg'][$info[2] ?? 0] ?? null;
if (!$ext) {
return $copied[$url] = null;
}
$sub = date('Y/m');
$dir = rtrim($c['images_dir'], '/') . '/' . $sub;
if (!is_dir($dir) && !@mkdir($dir, 0755, true)) {
return $copied[$url] = null;
}
$name = bin2hex(random_bytes(12)) . '.' . $ext;
if (file_put_contents($dir . '/' . $name, $data) === false) {
return $copied[$url] = null;
}
return $copied[$url] = [
'path' => $sub . '/' . $name,
'url' => rtrim($c['images_url'], '/') . '/' . $sub . '/' . $name,
];
}
function dabir_localize_images(array $c, string $html): string
{
return (string)preg_replace_callback('/(
]*\ssrc=")([^"]+)(")/i', function ($m) use ($c) {
$local = dabir_image($c, html_entity_decode($m[2]));
return $m[1] . ($local ? htmlspecialchars($local['url'], ENT_QUOTES) : $m[2]) . $m[3];
}, $html);
}