← Back to Home

Source Code - short.php

<?php
// short.php

// 1. Connect to SQLite3 database
$db = new SQLite3('data.db');

// 2. Create the table if it doesn't exist
$db->exec("CREATE TABLE IF NOT EXISTS entries (
    id INTEGER PRIMARY KEY,
    content TEXT UNIQUE NOT NULL,
    type TEXT NOT NULL,
    label TEXT
)");

// Silent upgrade for existing databases (adds label column if missing)
@$db->exec("ALTER TABLE entries ADD COLUMN label TEXT");

// ==========================================
// FEATURE A: VIEW TEXT OR REDIRECT URL
// ==========================================
if (isset($_GET['id'])) {
    $id = (int)$_GET['id'];
    $stmt = $db->prepare('SELECT content, type, label FROM entries WHERE id = :id');
    $stmt->bindValue(':id', $id, SQLITE3_INTEGER);
    $result = $stmt->execute();

    if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
        if ($row['type'] === 'url') {
            // Redirect URLs
            header("Location: " . $row['content'], true, 301);
            exit;
        } else {
            // Render plain text
            $safe_text = htmlspecialchars($row['content'], ENT_QUOTES, 'UTF-8');
            $display_title = !empty($row['label']) ? htmlspecialchars($row['label'], ENT_QUOTES, 'UTF-8') : "Snippet #{$id}";
            
            echo "<!DOCTYPE html>
            <html lang='en'>
            <head>
                <meta charset='UTF-8'>
                <meta name='viewport' content='width=device-width, initial-scale=1.0'>
                <title>{$display_title}</title>
                <style>
                    body { font-family: system-ui, sans-serif; max-width: 900px; margin: 2rem auto; padding: 1rem; color: #333; }
                    a { color: #007bff; text-decoration: none; }
                    .top-nav { display: inline-block; margin-bottom: 1.5rem; }
                    
                    /* Header Layout */
                    .header-bar { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 1rem; border-bottom: 1px solid #eaeaea; padding-bottom: 1rem; margin-bottom: 1rem; }
                    .header-bar h2 { margin: 0; font-size: 1.5rem; }
                    .actions { display: flex; gap: 0.5rem; }
                    .btn-action { background: #fff; color: #111; border: 1px solid #ccc; padding: 0.4rem 0.8rem; border-radius: 6px; cursor: pointer; font-size: 0.9rem; transition: background 0.2s; }
                    .btn-action:hover { background: #f4f4f5; }
                    
                    pre { background: #f4f4f5; padding: 1rem; border-radius: 8px; white-space: pre-wrap; word-wrap: break-word; font-size: 0.65rem; margin: 0; }
                </style>
            </head>
            <body>
                <a href='?' class='top-nav'>&larr; Back to Home</a>
                
                <div class='header-bar'>
                    <h2>{$display_title}</h2>
                    <div class='actions'>
                        <button class='btn-action' onclick='copyText(this)'>Copy</button>
                        <button class='btn-action' onclick='downloadText()'>Download</button>
                    </div>
                </div>

                <pre id='snippet-content'>{$safe_text}</pre>

                <script>
                    function copyText(btn) {
                        const text = document.getElementById('snippet-content').textContent;
                        navigator.clipboard.writeText(text).then(() => {
                            const originalText = btn.innerText;
                            btn.innerText = 'Copied!';
                            setTimeout(() => btn.innerText = originalText, 2000);
                        }).catch(() => alert('Failed to copy text.'));
                    }

                    function downloadText() {
                        const text = document.getElementById('snippet-content').textContent;
                        const blob = new Blob([text], { type: 'text/plain' });
                        const url = URL.createObjectURL(blob);
                        
                        const a = document.createElement('a');
                        a.href = url;
                        a.download = 'snippet_{$id}.txt';
                        document.body.appendChild(a);
                        a.click();
                        
                        document.body.removeChild(a);
                        URL.revokeObjectURL(url);
                    }
                </script>
            </body>
            </html>";
            exit;
        }
    } else {
        die("Error: Entry not found.");
    }
}

// ==========================================
// FEATURE B: DELETE ENTRY (POST)
// ==========================================
if (isset($_POST['delete_id'])) {
    $stmt = $db->prepare('DELETE FROM entries WHERE id = :id');
    $stmt->bindValue(':id', (int)$_POST['delete_id'], SQLITE3_INTEGER);
    $stmt->execute();
    
    header("Location: " . $_SERVER['PHP_SELF']);
    exit;
}

// ==========================================
// FEATURE C: CREATE ENTRY (POST)
// ==========================================
$message = '';
if (isset($_POST['content'])) {
    $content = trim($_POST['content']);
    $label_input = isset($_POST['label']) ? trim($_POST['label']) : '';
    
    if ($content !== '') {
        $type = filter_var($content, FILTER_VALIDATE_URL) ? 'url' : 'text';

        // Auto-generate label if empty
        if ($label_input === '') {
            $normalized_content = preg_replace('/\s+/', ' ', $content);
            $label_input = substr($normalized_content, 0, 35);
            if (strlen($normalized_content) > 35) {
                $label_input .= '...';
            }
        }

        // Check for exact duplicates
        $stmt = $db->prepare('SELECT id FROM entries WHERE content = :content');
        $stmt->bindValue(':content', $content, SQLITE3_TEXT);
        $result = $stmt->execute();

        if ($row = $result->fetchArray(SQLITE3_ASSOC)) {
            $message = "<div class='alert'>Entry already exists! ID: <a href='?id={$row['id']}'><strong>{$row['id']}</strong></a></div>";
        } else {
            // Generate unique 4-digit ID
            $unique = false;
            while (!$unique) {
                $new_id = random_int(1000, 9999);
                $check = $db->prepare('SELECT id FROM entries WHERE id = :id');
                $check->bindValue(':id', $new_id, SQLITE3_INTEGER);
                if (!$check->execute()->fetchArray(SQLITE3_ASSOC)) {
                    $unique = true;
                }
            }

            // Insert new entry
            $insert = $db->prepare('INSERT INTO entries (id, content, type, label) VALUES (:id, :content, :type, :label)');
            $insert->bindValue(':id', $new_id, SQLITE3_INTEGER);
            $insert->bindValue(':content', $content, SQLITE3_TEXT);
            $insert->bindValue(':type', $type, SQLITE3_TEXT);
            $insert->bindValue(':label', $label_input, SQLITE3_TEXT);
            
            if ($insert->execute()) {
                $message = "<div class='alert success'>Success! Your ID is: <a href='?id={$new_id}'><strong>{$new_id}</strong></a></div>";
            } else {
                $message = "<div class='alert error'>Error saving entry.</div>";
            }
        }
    }
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="author" content="https://github.com/xthukuh">
    <title>SHORT | Text Content Shortener</title>
    <link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
    <link rel="apple-touch-icon-precomposed" href="icon.png">
    <style>
        body { font-family: system-ui, sans-serif; max-width: 900px; margin: 2rem auto; padding: 1rem; color: #111; text-align: center; }
        h1 { margin-bottom: 0.5rem; }
        p { color: #555; margin-bottom: 1.5rem; }
        form { margin-bottom: 2rem; }
        
        input[type="text"], textarea { 
            width: 100%; 
            padding: 0.75rem; 
            border: 1px solid #ccc; 
            border-radius: 8px; 
            box-sizing: border-box; 
            font-family: inherit; 
            margin-bottom: 1rem; 
        }
        textarea { height: 120px; resize: vertical; }
        
        button[type="submit"] { background: #000; color: #fff; border: none; padding: 0.75rem 1.5rem; border-radius: 8px; cursor: pointer; font-size: 1rem; font-weight: 500; }
        button[type="submit"]:hover { background: #333; }
        
        .alert { background: #f4f4f5; padding: 1rem; border-radius: 8px; margin-bottom: 1.5rem; }
        .alert.success { background: #dcfce7; color: #166534; }
        .alert.error { background: #fee2e2; color: #991b1b; }
        .alert a { color: inherit; }
        
        table { width: 100%; border-collapse: collapse; margin-top: 1rem; text-align: left; }
        th, td { border-bottom: 1px solid #eaeaea; padding: 0.75rem; font-size: 0.9rem; }
        td.content-cell { max-width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .badge { background: #e2e8f0; padding: 0.2rem 0.5rem; border-radius: 4px; font-size: 0.75rem; text-transform: uppercase; }
        .btn-delete { background: #ef4444; color: white; padding: 0.4rem 0.6rem; border-radius: 4px; font-size: 0.8rem; text-decoration: none; border: none; cursor: pointer; }
        .btn-delete:hover { background: #dc2626; }
        .delete-form { margin: 0; }
    </style>
</head>
<body>

    <h1>Simple Drop</h1>
    <p>Paste a URL to shorten it, or text to create a snippet.</p>

    <?= $message ?>

    <form method="POST" action="">
        <input type="text" name="label" placeholder="Optional label/title...">
        <textarea name="content" placeholder="Paste URL or text here..." required></textarea>
        <button type="submit">Shorten / Save</button>
    </form>

    <hr style="border: 0; border-top: 1px solid #eaeaea; margin: 2rem 0;">

    <div style="overflow-x: auto;">
        <table>
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Type</th>
                    <th>Label</th>
                    <th style="text-align: right;">Action</th>
                </tr>
            </thead>
            <tbody>
                <?php
                $entries = $db->query('SELECT * FROM entries ORDER BY id DESC');
                while ($row = $entries->fetchArray(SQLITE3_ASSOC)): 
                    $display_label = !empty($row['label']) ? $row['label'] : $row['content'];
                ?>
                <tr>
                    <td><a href="?id=<?= $row['id'] ?>"><?= $row['id'] ?></a></td>
                    <td><span class="badge"><?= $row['type'] ?></span></td>
                    <td class="content-cell" title="<?= htmlspecialchars($row['content'], ENT_QUOTES, 'UTF-8') ?>">
                        <?= htmlspecialchars($display_label, ENT_QUOTES, 'UTF-8') ?>
                    </td>
                    <td style="text-align: right;">
                        <form method="POST" class="delete-form" onsubmit="return confirm('Delete this entry?');">
                            <input type="hidden" name="delete_id" value="<?= $row['id'] ?>">
                            <button type="submit" class="btn-delete">Delete</button>
                        </form>
                    </td>
                </tr>
                <?php endwhile; ?>
            </tbody>
        </table>
    </div>

</body>
</html>