GitGram — profile.php — GitGram
TaskGram / main / v1.00 / profile.php12,618 B↓ Raw
<?php
declare(strict_types=1);

require_once 'includes/config.php';
require_once 'includes/functions.php';

$cur_user    = get_session_user();
$view_user   = strtolower(trim($_GET['user'] ?? ($cur_user['username'] ?? '')));

if ($view_user === '') {
    redirect($cur_user ? 'profile.php?user=' . urlencode($cur_user['username']) : 'index.php');
}

$profile = get_user_profile($view_user);
if (!$profile) {
    flash('error', 'User not found.');
    redirect('directory.php');
}

$is_own  = $cur_user && $cur_user['username'] === $view_user;
$lists   = get_lists_for_user($view_user);

// Filter lists visible to current user
$visible = array_filter($lists, function($l) use ($cur_user, $view_user) {
    if ($l['public'] ?? false) return true;
    return $cur_user && $cur_user['username'] === $view_user;
});

/* ── Handle profile updates ───────────────────────────────────── */
if ($is_own && $_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!verify_csrf()) {
        flash('error', 'Invalid request.');
        redirect('profile.php?user=' . urlencode($view_user));
    }

    $action = $_POST['action'] ?? '';

    if ($action === 'update_display_name') {
        $dn = trim($_POST['display_name'] ?? '');
        if ($dn === '') $dn = $view_user;
        update_display_name($view_user, $dn);
        // Update session
        if ($cur_user) {
            $cur_user['display_name'] = $dn;
            login_user($cur_user);
        }
        flash('success', 'Display name updated.');
        redirect('profile.php?user=' . urlencode($view_user));
    }

    if ($action === 'upload_avatar') {
        $file = $_FILES['avatar'] ?? null;
        if (!$file || $file['error'] === UPLOAD_ERR_NO_FILE) {
            flash('error', 'No file selected.');
        } else {
            $result = save_user_avatar($view_user, $file);
            if ($result === false) {
                flash('error', 'Upload failed. Use JPG, PNG, GIF or WebP, max 2 MB.');
            } else {
                flash('success', 'Profile logo updated.');
            }
        }
        redirect('profile.php?user=' . urlencode($view_user));
    }

    if ($action === 'remove_avatar') {
        remove_user_avatar($view_user);
        flash('success', 'Profile logo removed.');
        redirect('profile.php?user=' . urlencode($view_user));
    }

    if ($action === 'change_password') {
        $old = $_POST['old_password'] ?? '';
        $new = $_POST['new_password'] ?? '';
        $new2= $_POST['new_password2'] ?? '';
        if ($new !== $new2) {
            flash('error', 'New passwords do not match.');
        } else {
            $result = update_password($view_user, $old, $new);
            if ($result === true) {
                flash('success', 'Password changed.');
            } else {
                flash('error', $result);
            }
        }
        redirect('profile.php?user=' . urlencode($view_user));
    }
}

$page_title   = ($is_own ? 'My Profile' : $profile['display_name'] . '\'s Profile');
$og_image_url = get_user_avatar_url($view_user) ?: get_og_logo_url();
require_once 'includes/header.php';
?>

<div class="page-wrap" style="max-width:860px; margin-top:12px;">

    <div style="display:flex; gap:10px; flex-wrap:wrap; align-items:flex-start;">

        <!-- ── Profile card ─────────────────────────────── -->
        <div class="window" style="min-width:240px; flex:0 0 240px;">
            <div class="window-title"><span>&#128100; Profile</span></div>
            <div class="window-body">
                <?php $p_avatar = get_user_avatar_url($view_user); ?>
                <?php if ($p_avatar !== ''): ?>
                <div style="margin-bottom:8px;">
                    <img src="<?= h($p_avatar) ?>" alt="Profile logo" class="avatar-lg">
                </div>
                <?php endif; ?>
                <div style="font-size:18px; font-weight:bold; margin-bottom:4px;">
                    <?= h($profile['display_name']) ?>
                </div>
                <div class="text-muted">@<?= h($profile['username']) ?></div>
                <div class="sep"></div>
                <div class="text-muted">Member since <?= date('F Y', strtotime($profile['created'])) ?></div>
                <div class="text-muted" style="margin-top:4px;">
                    <?= count(array_filter($lists, fn($l) => $l['public'])) ?> public list<?= count(array_filter($lists, fn($l) => $l['public'])) !== 1 ? 's' : '' ?>
                    <?php if ($is_own): ?>
                        &middot; <?= count($lists) ?> total
                    <?php endif; ?>
                </div>
            </div>
        </div>

        <!-- ── Right column ─────────────────────────────── -->
        <div style="flex:1; min-width:0; display:flex; flex-direction:column; gap:10px;">

            <?php if ($is_own): ?>
            <!-- Profile logo upload -->
            <div class="window">
                <div class="window-title"><span>&#128247; Profile Logo</span></div>
                <div class="window-body">
                    <p style="font-size:12px; color:var(--chrome-darker); margin-bottom:8px;">
                        Your profile logo is used as the OG share image on your lists and profile page,
                        overriding the site logo. JPG, PNG, GIF or WebP, max 2 MB.
                    </p>
                    <?php if ($p_avatar !== ''): ?>
                    <div style="display:flex; align-items:flex-start; gap:10px; margin-bottom:10px;">
                        <img src="<?= h($p_avatar) ?>" alt="Profile logo" class="avatar-lg">
                        <form method="post" action="profile.php?user=<?= h($view_user) ?>">
                            <?= csrf_field() ?>
                            <input type="hidden" name="action" value="remove_avatar">
                            <button type="submit" class="btn btn-sm btn-danger">Remove</button>
                        </form>
                    </div>
                    <?php endif; ?>
                    <form method="post" action="profile.php?user=<?= h($view_user) ?>"
                          enctype="multipart/form-data">
                        <?= csrf_field() ?>
                        <input type="hidden" name="action" value="upload_avatar">
                        <div class="form-row-inline" style="margin:0;">
                            <div class="stretch">
                                <input type="file" name="avatar"
                                       accept=".jpg,.jpeg,.png,.gif,.webp,image/*"
                                       style="border:none; background:transparent; padding:2px 0; width:100%;">
                            </div>
                            <div>
                                <button type="submit" class="btn btn-primary btn-sm">Upload</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>

            <!-- Edit profile -->
            <div class="window">
                <div class="window-title"><span>Edit Profile</span></div>
                <div class="window-body">
                    <form method="post" action="profile.php?user=<?= h($view_user) ?>">
                        <?= csrf_field() ?>
                        <input type="hidden" name="action" value="update_display_name">
                        <div class="form-row-inline">
                            <div class="stretch">
                                <label for="dn_input">Display Name</label>
                                <input type="text" id="dn_input" name="display_name"
                                       value="<?= h($profile['display_name']) ?>" maxlength="64">
                            </div>
                            <div style="padding-top:18px;">
                                <button type="submit" class="btn btn-primary btn-sm">Save</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>

            <!-- Change password -->
            <div class="window">
                <div class="window-title"><span>Change Password</span></div>
                <div class="window-body">
                    <form method="post" action="profile.php?user=<?= h($view_user) ?>">
                        <?= csrf_field() ?>
                        <input type="hidden" name="action" value="change_password">
                        <div class="form-row-inline">
                            <div>
                                <label for="old_pw">Current Password</label>
                                <input type="password" id="old_pw" name="old_password" maxlength="128" required>
                            </div>
                            <div>
                                <label for="new_pw">New Password</label>
                                <input type="password" id="new_pw" name="new_password"
                                       minlength="<?= MIN_PASSWORD_LEN ?>" maxlength="128" required>
                            </div>
                            <div>
                                <label for="new_pw2">Confirm New</label>
                                <input type="password" id="new_pw2" name="new_password2" maxlength="128" required>
                            </div>
                            <div style="padding-top:18px;">
                                <button type="submit" class="btn btn-primary btn-sm">Change</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
            <?php endif; ?>

        </div>
    </div>

    <!-- ── Visible lists ──────────────────────────────── -->
    <div class="window mt-10" style="margin-bottom:16px;">
        <div class="window-title">
            <span><?= $is_own ? 'My Lists' : h($profile['display_name']) . '\'s Lists' ?></span>
            <span style="font-size:11px;opacity:.8;"><?= count($visible) ?> visible</span>
        </div>
        <div class="window-body">
            <?php if (empty($visible)): ?>
                <div class="text-center" style="padding:16px; color:var(--chrome-dark);">No lists to show.</div>
            <?php else: ?>
            <div class="list-grid">
                <?php foreach ($visible as $lst): ?>
                <div class="list-card">
                    <div class="list-card-title">
                        <a href="<?= h(list_url($view_user, $lst['id'])) ?>"><?= h($lst['title']) ?></a>
                        <?php if ($lst['public']): ?>
                            <span class="badge badge-pub">PUBLIC</span>
                        <?php else: ?>
                            <span class="badge badge-priv">PRIVATE</span>
                        <?php endif; ?>
                        <?php if (!empty($lst['password_hash'])): ?>
                            <span class="badge" style="background:#774400;color:#fff;" title="Password protected">&#128274;</span>
                        <?php endif; ?>
                    </div>
                    <div class="list-card-body">
                        <?= $lst['description'] ? h($lst['description']) : '<em style="color:var(--chrome-dark)">No description</em>' ?>
                    </div>
                    <div class="list-card-footer">
                        <span class="task-counts">
                            <span title="Active" style="color:var(--normal);">&#9632; <?= $lst['active_count'] ?></span>
                            &nbsp;
                            <span title="Completed" style="color:var(--low);">&#10003; <?= $lst['completed_count'] ?></span>
                        </span>
                        <div class="d-flex gap-4">
                            <a href="<?= h(list_url($view_user, $lst['id'])) ?>" class="btn btn-sm">Open</a>
                            <a href="print.php?id=<?= h($lst['id']) ?>&amp;owner=<?= h($view_user) ?>"
                               class="btn btn-sm" target="_blank">Print</a>
                        </div>
                    </div>
                </div>
                <?php endforeach; ?>
            </div>
            <?php endif; ?>
        </div>
    </div>

</div>

<?php require_once 'includes/footer.php'; ?>
Ready
GitGram