Ryanhub - file viewer
filename: views/public/upload.php
branch: main
back to repo
<?php
if (!isLoggedIn()) {
    header('Location: ' . url('signin'));
    exit;
}

$pageTitle = "Contribute – Seven O'Clock Dinner Club";
$errors = [];
$successMessage = null;

if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
    
    //var_dump($_FILES['image']);
    //exit;

    if (!empty($errors)) {
        var_dump($file['size'], $errors);
        exit;
    }

    $quote = trim($_POST['quote'] ?? '');
    $attribution = trim($_POST['attribution'] ?? '');
    $userId = currentUserId();

    if ($quote === '') {
        $errors[] = 'Please add a quote.';
    }

    if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
        $errors[] = 'Please attach a picture.';
    } else {
        $file = $_FILES['image'];
        if ($file['size'] > 20 * 1024 * 1024) {
            $errors[] = 'Images are limited to 20MB.';
        }
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime = finfo_file($finfo, $file['tmp_name']);
        finfo_close($finfo);

        // Allow common photo formats including Apple HEIC/HEIF
        $allowedMimes = [
            'image/jpeg' => 'jpg',
            'image/png' => 'png',
            'image/heic' => 'heic',
            'image/heif' => 'heic',
            'image/heic-sequence' => 'heic',
            'image/heif-sequence' => 'heic',
        ];

        if (!array_key_exists($mime, $allowedMimes)) {
            $errors[] = 'Please upload a JPEG, PNG, or HEIC file.';
        }
    }

    if (!$errors) {
        // Choose the file extension based on MIME type
        $allowedMimes = [
            'image/jpeg' => 'jpg',
            'image/png' => 'png',
            'image/heic' => 'heic',
            'image/heif' => 'heic',
            'image/heic-sequence' => 'heic',
            'image/heif-sequence' => 'heic',
        ];
        $ext = $allowedMimes[$mime] ?? 'jpg';

        // Store uploads in a web-accessible /uploads directory at the app root
        $uploadsDir = __DIR__ . '/../../uploads';
        if (!is_dir($uploadsDir)) {
            mkdir($uploadsDir, 0775, true);
        }
        $basename = bin2hex(random_bytes(12)) . '.' . $ext;
        $target = $uploadsDir . '/' . $basename;
        if (!move_uploaded_file($file['tmp_name'], $target)) {
            $errors[] = 'We were unable to save the image.';
        } else {
            $publicPath = url('uploads/' . $basename);
            // Automatically visible on the site; admins can still remove uploads from the dashboard
            $nowStr = (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');
            $stmt = $db->prepare('INSERT INTO uploads (user_id, image_url, quote_text, attribution, is_approved, created_at) VALUES (?, ?, ?, ?, 1, ?)');
            $stmt->bind_param('issss', $userId, $publicPath, $quote, $attribution, $nowStr);
            $stmt->execute();

            // After a successful upload, redirect to the main page and jump to the new post.
            $newUploadId = (int)$db->insert_id;
            header('Location: ' . url('/') . '?highlight_upload=' . $newUploadId . '#upload-' . $newUploadId);
            exit;
        }
    }
}

$userId = currentUserId();
$stmt = $db->prepare('
    SELECT
        u.id,
        u.image_url,
        u.quote_text,
        u.attribution,
        u.is_approved,
        u.created_at,
        m.display_name,
        (SELECT COUNT(*) FROM upload_reactions r WHERE r.upload_id = u.id AND r.reaction = "like") AS like_count,
        (SELECT COUNT(*) FROM upload_reactions r WHERE r.upload_id = u.id AND r.reaction = "dislike") AS dislike_count,
        (SELECT COUNT(*) FROM upload_comments c WHERE c.upload_id = u.id) AS comment_count,
        0 AS my_reaction_unused
    FROM uploads u
    LEFT JOIN member_profiles m ON m.user_id = u.user_id
    WHERE u.user_id = ?
    ORDER BY u.created_at DESC
    LIMIT 12
');
$uid = currentUserId();
$stmt->bind_param('i', $userId);
$stmt->execute();
$myUploads = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
?>

<section class="page-grid">
    <div class="card" data-animate-initial>
        <div class="muted" style="font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 10px;">
            Contribute
        </div>
        <h1 style="font-family: 'Georgia', 'Times New Roman', serif; font-weight: 400; font-size: 26px; margin: 0 0 12px;">
            Share a moment from dinner.
        </h1>
        <?php if ($errors): ?>
            <ul style="margin-top: 12px; padding-left: 18px; color: #9b2c2c; font-size: 13px;">
                <?php foreach ($errors as $err): ?>
                    <li><?= htmlspecialchars($err, ENT_QUOTES, 'UTF-8') ?></li>
                <?php endforeach; ?>
            </ul>
        <?php endif; ?>
        <?php if ($successMessage): ?>
            <p style="margin-top: 12px; font-size: 13px; color: #22543d;">
                <?= htmlspecialchars($successMessage, ENT_QUOTES, 'UTF-8') ?>
            </p>
        <?php endif; ?>
    </div>

    <div class="card" data-animate>
        <form method="post" enctype="multipart/form-data" style="display: grid; gap: 10px; font-size: 13px; margin-bottom: 0;">
            <label>
                Photograph<br>
                <input type="file" name="image" accept="image/jpeg,image/png" required style="width: 100%; padding: 6px 0;">
            </label>
            <label>
                Quote<br>
                <textarea name="quote" rows="3" required style="width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid rgba(0,0,0,0.12); background: rgba(255,255,255,0.8);"></textarea>
            </label>
            <label>
                Attribution (optional)<br>
                <input name="attribution" type="text" placeholder="Guest name, occasion, etc." style="width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid rgba(0,0,0,0.12); background: rgba(255,255,255,0.8);">
            </label>
            <button type="submit" class="pill pill-accent" style="margin-top: 6px; justify-content: center;">
                Submit
            </button>
        </form>
    </div>

    <?php if ($myUploads): ?>
        <div class="card" data-animate>
            <div class="muted" style="font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; margin-bottom: 8px;">
                Your contributions
            </div>
            <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 14px;">
                <?php foreach ($myUploads as $upload): ?>
                    <?php $posterName = $upload['display_name'] ?? 'You'; ?>
                    <figure style="margin: 0; border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08); background: var(--bg-elevated);">
                        <button type="button" data-image-full="<?= htmlspecialchars($upload['image_url'], ENT_QUOTES, 'UTF-8') ?>" style="all: unset; display: block; width: 100%; cursor: zoom-in;">
                            <img src="<?= htmlspecialchars($upload['image_url'], ENT_QUOTES, 'UTF-8') ?>" alt="" style="display:block; width:100%; height:auto; aspect-ratio:4 / 5; object-fit:cover; object-position:center center; filter:grayscale(30%); border-radius: 12px 12px 0 0;">
                        </button>
                        <figcaption style="padding: 10px 12px 12px; background: #ffffff; font-size: 12px; line-height: 1.5;">
                            <div style="font-size: 13px; margin-bottom: 4px;">
                                “<?= htmlspecialchars($upload['quote_text'], ENT_QUOTES, 'UTF-8') ?>”
                            </div>
                            <?php if (!empty($upload['attribution'])): ?>
                                <div class="muted" style="font-size: 12px; margin-bottom: 4px;">
                                    <?= htmlspecialchars($upload['attribution'], ENT_QUOTES, 'UTF-8') ?>
                                </div>
                            <?php endif; ?>
                            <div style="display: flex; justify-content: space-between; align-items: center; font-size: 11px; margin-top: 6px;">
                                <span class="muted">
                                    <?= $upload['is_approved'] ? 'Approved' : 'Pending' ?>
                                </span>
                                <span style="opacity: 0.7;">
                                    <?= htmlspecialchars(date('M j', strtotime($upload['created_at'])), ENT_QUOTES, 'UTF-8') ?>
                                </span>
                            </div>
                            <div class="muted" style="margin-top: 6px; font-size: 11px;">
                                Uploaded by <?= htmlspecialchars($posterName, ENT_QUOTES, 'UTF-8') ?>
                            </div>
                            <div style="margin-top: 10px; display:flex; gap: 8px; flex-wrap: nowrap; align-items:center; justify-content: space-between; width: 100%;">
                                <div style="display:flex; gap: 8px; align-items:center;">
                                    <button
                                        type="button"
                                        class="pill"
                                        style="font-size: 12px; padding: 6px 12px;"
                                        aria-label="Upvote"
                                        data-open-upload-interactions
                                        data-upload-id="<?= htmlspecialchars((string)($upload['id'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"
                                        data-reaction="like"
                                    >
                                        ▲ <?= (int)($upload['like_count'] ?? 0) ?>
                                    </button>
                                    <button
                                        type="button"
                                        class="pill"
                                        style="font-size: 12px; padding: 6px 12px;"
                                        aria-label="Downvote"
                                        data-open-upload-interactions
                                        data-upload-id="<?= htmlspecialchars((string)($upload['id'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"
                                        data-reaction="dislike"
                                    >
                                        ▼ <?= (int)($upload['dislike_count'] ?? 0) ?>
                                    </button>
                                </div>

                                <button
                                    type="button"
                                    class="pill"
                                    style="font-size: 11px; padding: 6px 12px;"
                                    data-open-upload-interactions
                                    data-upload-id="<?= htmlspecialchars((string)($upload['id'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"
                                    data-open-comments="1"
                                >
                                    Comments (<?= (int)($upload['comment_count'] ?? 0) ?>)
                                </button>
                            </div>
                        </figcaption>
                    </figure>
                <?php endforeach; ?>
            </div>
        </div>
    <?php endif; ?>
</section>