GitGram — print.php — GitGram
TaskGram / main / v1.00 / print.php13,701 B↓ Raw
<?php
declare(strict_types=1);

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

$cur_user = get_session_user();
$owner    = strtolower(trim($_GET['owner'] ?? ''));
$list_id  = preg_replace('/[^a-f0-9]/', '', $_GET['id'] ?? '');

if (!$owner || !$list_id) {
    header('Location: ' . ($cur_user ? 'dashboard.php' : 'index.php'));
    exit;
}

$list = get_list($owner, $list_id);

// Grant session access via share token if provided
if ($list && !empty($_GET['token']) && check_share_token($list, $_GET['token'])) {
    grant_list_access($list['id']);
}

if (!$list || !can_view_list($list, $cur_user)) {
    header('Location: ' . ($cur_user ? 'dashboard.php' : 'index.php'));
    exit;
}

// Check password lock
session_start_safe();
if (!empty($list['password_hash'])) {
    $access_key = 'list_access_' . $list_id;
    if (empty($_SESSION[$access_key]) && !can_edit_list($list, $cur_user)) {
        header('Location: ' . list_url($owner, $list_id));
        exit;
    }
}

$mode = $_GET['mode'] ?? 'print'; // 'print' or 'markdown'

/* ──────────────────────────────────────────────────────────────
   Markdown generation (plain text, no HTML)
────────────────────────────────────────────────────────────── */
if ($mode === 'markdown') {
    header('Content-Type: text/plain; charset=UTF-8');
    header('Content-Disposition: inline; filename="' . preg_replace('/[^a-z0-9_-]/i', '_', $list['title']) . '.md"');

    echo '# ' . $list['title'] . "\n";
    echo "\n";
    if ($list['description'] ?? '') {
        echo '> ' . $list['description'] . "\n\n";
    }
    echo '**Owner:** @' . $list['owner'] . '  ' . "\n";
    echo '**Updated:** ' . date('d F Y, H:i', strtotime($list['updated'])) . "  \n";
    echo '**Visibility:** ' . ($list['public'] ? 'Public' : 'Private') . "\n\n";
    echo "---\n\n";

    // Active tasks
    echo '## Active Tasks (' . count($list['active']) . ")\n\n";
    if (empty($list['active'])) {
        echo "_No active tasks._\n\n";
    } else {
        foreach ($list['active'] as $i => $task) {
            $pri = match($task['priority'] ?? 'normal') {
                'high'  => ' 🔴',
                'low'   => ' 🟢',
                default => '',
            };
            echo '- [ ] ' . $task['text'] . $pri . "\n";
            echo '  _Added ' . date('d M Y', strtotime($task['created'])) . '_' . "\n";
        }
    }

    echo "\n---\n\n";

    // Completed tasks
    echo '## Completed (' . count($list['completed']) . ")\n\n";
    if (empty($list['completed'])) {
        echo "_No completed tasks._\n\n";
    } else {
        foreach ($list['completed'] as $task) {
            echo '- [x] ' . $task['text'] . "\n";
            echo '  _Completed ' . date('d M Y', strtotime($task['completed'] ?? '')) . '_' . "\n";
        }
    }

    echo "\n---\n";
    echo '_Generated by TaskGram on ' . date('d F Y, H:i') . "_\n";
    exit;
}

/* ──────────────────────────────────────────────────────────────
   HTML print view
────────────────────────────────────────────────────────────── */
$now = date('d F Y, H:i');
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= h($list['title']) ?> — <?= APP_NAME ?></title>
<style>
/* ── Print base ──────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

body {
    font-family: 'Arial', sans-serif;
    font-size: 13px;
    color: #000;
    background: #fff;
    padding: 16px 20px;
    max-width: 860px;
    margin: 0 auto;
}

/* ── Screen-only toolbar ─────────────────────────────────── */
.screen-toolbar {
    background: #C0C0C0;
    border-top:   2px solid #FFF;
    border-left:  2px solid #FFF;
    border-bottom:2px solid #404040;
    border-right: 2px solid #404040;
    padding: 6px 10px;
    margin-bottom: 16px;
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: center;
}
.btn-print {
    background: #C0C0C0;
    border-top:   2px solid #FFF;
    border-left:  2px solid #FFF;
    border-bottom:2px solid #404040;
    border-right: 2px solid #404040;
    padding: 3px 14px;
    cursor: pointer;
    font-family: inherit;
    font-size: 13px;
    text-decoration: none;
    display: inline-block;
    color: #000;
}
.btn-print-primary {
    background: #000080;
    color: #FFF;
    border-top:   2px solid #2040A0;
    border-left:  2px solid #2040A0;
    border-bottom:2px solid #000040;
    border-right: 2px solid #000040;
}

/* ── Document header ─────────────────────────────────────── */
.doc-header {
    border-bottom: 2px solid #000;
    padding-bottom: 10px;
    margin-bottom: 14px;
}
.doc-title {
    font-size: 22px;
    font-weight: bold;
    margin-bottom: 4px;
}
.doc-meta {
    font-size: 11px;
    color: #555;
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
}
.doc-desc {
    font-style: italic;
    margin-top: 6px;
    color: #333;
    border-left: 3px solid #888;
    padding-left: 8px;
}

/* ── Two-column layout ───────────────────────────────────── */
.two-col {
    display: flex;
    gap: 20px;
    align-items: flex-start;
}
.col { flex: 1; min-width: 0; }

/* ── Section headings ────────────────────────────────────── */
.section-h {
    font-size: 13px;
    font-weight: bold;
    background: #000080;
    color: #FFF;
    padding: 3px 8px;
    margin-bottom: 6px;
    display: flex;
    justify-content: space-between;
}

/* ── Task rows ───────────────────────────────────────────── */
.task-row {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    padding: 4px 4px 4px 6px;
    border-bottom: 1px solid #DDD;
    page-break-inside: avoid;
}
.task-row:nth-child(even) { background: #F4F4F4; }
.task-check {
    flex-shrink: 0;
    width: 14px;
    height: 14px;
    border: 1px solid #000;
    margin-top: 1px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 10px;
}
.task-check.done { background: #E0E0E0; }
.task-body { flex: 1; min-width: 0; }
.task-txt { line-height: 1.4; word-break: break-word; }
.task-txt.completed-txt { text-decoration: line-through; color: #777; }
.task-date { font-size: 10px; color: #888; margin-top: 1px; }
.pri-dot {
    display: inline-block;
    width: 8px; height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
    margin-top: 4px;
}
.pri-high   .pri-dot { background: #CC0000; }
.pri-normal .pri-dot { background: #000080; }
.pri-low    .pri-dot { background: #006600; }

.empty-note { font-style: italic; color: #888; padding: 8px 4px; font-size: 12px; }

/* ── Markdown block (pre) ─────────────────────────────────── */
.md-source {
    display: none;
    background: #F8F8F8;
    border: 1px solid #CCC;
    padding: 12px;
    font-family: monospace;
    font-size: 12px;
    white-space: pre;
    overflow-x: auto;
    margin-top: 20px;
}
.md-source.visible { display: block; }

/* ── Footer ─────────────────────────────────────────────── */
.doc-footer {
    margin-top: 14px;
    border-top: 1px solid #CCC;
    padding-top: 6px;
    font-size: 10px;
    color: #888;
    display: flex;
    justify-content: space-between;
}

/* ── Print media ─────────────────────────────────────────── */
@media print {
    .screen-toolbar { display: none !important; }
    .md-source { display: none !important; }
    body { padding: 0; font-size: 11px; }
    .doc-title { font-size: 18px; }
    .two-col { gap: 12px; }
    @page { margin: 1.2cm; }
}

@media (max-width: 580px) {
    .two-col { flex-direction: column; }
}
</style>
</head>
<body>

<!-- Screen-only toolbar -->
<div class="screen-toolbar">
    <strong style="font-size:14px; color:#000080;">TaskGram</strong>
    <span style="flex:1;"></span>
    <a href="<?= h(list_url($owner, $list_id)) ?>" class="btn-print">&#8592; Back to List</a>
    <a href="print.php?id=<?= h($list_id) ?>&amp;owner=<?= h($owner) ?>&amp;mode=markdown"
       class="btn-print" target="_blank">&#128190; Download Markdown</a>
    <a href="#md-block" class="btn-print" id="toggle-md-btn"
       onclick="this.closest('.screen-toolbar').nextElementSibling && (function(){
           var b=document.getElementById('md-block');
           if(b){b.classList.toggle('visible');}
       })(); return false;">View Markdown</a>
    <button class="btn-print btn-print-primary" onclick="window.print()">&#128438; Print</button>
</div>

<!-- Document -->
<div class="doc-header">
    <div class="doc-title"><?= h($list['title']) ?></div>
    <?php if ($list['description'] ?? ''): ?>
    <div class="doc-desc"><?= h($list['description']) ?></div>
    <?php endif; ?>
    <div class="doc-meta" style="margin-top:6px;">
        <span>Owner: <strong>@<?= h($list['owner']) ?></strong></span>
        <span>Updated: <?= date('d F Y', strtotime($list['updated'])) ?></span>
        <span>Active: <?= count($list['active']) ?></span>
        <span>Completed: <?= count($list['completed']) ?></span>
        <span>Visibility: <?= $list['public'] ? 'Public' : 'Private' ?></span>
    </div>
</div>

<div class="two-col">

    <!-- Active tasks column -->
    <div class="col">
        <div class="section-h">
            <span>&#9632; Active Tasks</span>
            <span><?= count($list['active']) ?></span>
        </div>
        <?php if (empty($list['active'])): ?>
            <div class="empty-note">No active tasks.</div>
        <?php else: ?>
            <?php foreach ($list['active'] as $task): ?>
            <div class="task-row pri-<?= h($task['priority'] ?? 'normal') ?>">
                <div class="pri-dot" title="<?= h(ucfirst($task['priority'] ?? 'normal')) ?> priority"></div>
                <div class="task-check"></div>
                <div class="task-body">
                    <div class="task-txt"><?= h($task['text']) ?></div>
                    <div class="task-date">Added <?= date('d M Y', strtotime($task['created'])) ?></div>
                </div>
            </div>
            <?php endforeach; ?>
        <?php endif; ?>
    </div>

    <!-- Completed column -->
    <div class="col">
        <div class="section-h">
            <span>&#10003; Completed</span>
            <span><?= count($list['completed']) ?></span>
        </div>
        <?php if (empty($list['completed'])): ?>
            <div class="empty-note">No completed tasks.</div>
        <?php else: ?>
            <?php foreach ($list['completed'] as $task): ?>
            <div class="task-row completed-item">
                <div class="pri-dot" style="background:#888;"></div>
                <div class="task-check done">&#10003;</div>
                <div class="task-body">
                    <div class="task-txt completed-txt"><?= h($task['text']) ?></div>
                    <div class="task-date">Completed <?= date('d M Y', strtotime($task['completed'] ?? '')) ?></div>
                </div>
            </div>
            <?php endforeach; ?>
        <?php endif; ?>
    </div>

</div><!-- /two-col -->

<div class="doc-footer">
    <span>Printed from <?= APP_NAME ?></span>
    <span><?= h($now) ?></span>
</div>

<!-- Markdown source block (hidden by default on screen, never printed) -->
<pre class="md-source" id="md-block"><?php
$md  = '# ' . $list['title'] . "\n\n";
if ($list['description'] ?? '') $md .= '> ' . $list['description'] . "\n\n";
$md .= '**Owner:** @' . $list['owner'] . '  ' . "\n";
$md .= '**Updated:** ' . date('d F Y, H:i', strtotime($list['updated'])) . "  \n";
$md .= '**Visibility:** ' . ($list['public'] ? 'Public' : 'Private') . "\n\n";
$md .= "---\n\n## Active Tasks (" . count($list['active']) . ")\n\n";
if (empty($list['active'])) {
    $md .= "_No active tasks._\n\n";
} else {
    foreach ($list['active'] as $task) {
        $pri = match($task['priority'] ?? 'normal') { 'high' => ' [HIGH]', 'low' => ' [LOW]', default => '' };
        $md .= '- [ ] ' . $task['text'] . $pri . "\n";
        $md .= '  _Added ' . date('d M Y', strtotime($task['created'])) . "_\n";
    }
}
$md .= "\n---\n\n## Completed (" . count($list['completed']) . ")\n\n";
if (empty($list['completed'])) {
    $md .= "_No completed tasks._\n\n";
} else {
    foreach ($list['completed'] as $task) {
        $md .= '- [x] ' . $task['text'] . "\n";
        $md .= '  _Completed ' . date('d M Y', strtotime($task['completed'] ?? '')) . "_\n";
    }
}
$md .= "\n---\n_Generated by TaskGram on " . $now . "_\n";
echo h($md);
?></pre>

</body>
</html>
Ready
GitGram