GitGram — document.php — GitGram
IndexGram / main / indexgram_v5.00 / document.php12,411 B↓ Raw
<?php
require_once __DIR__ . '/config.php';
if (!IS_INSTALLED) { header('Location: setup.php'); exit; }
require_once ROOT_PATH . '/includes/db.php';
require_once ROOT_PATH . '/includes/functions.php';
require_once ROOT_PATH . '/includes/auth.php';
require_once ROOT_PATH . '/includes/markdown.php';

$topicSlug = trim($_GET['topic'] ?? '');
$docSlug   = trim($_GET['slug']  ?? '');
$urlToken  = trim($_GET['token'] ?? '');
$topic     = $topicSlug ? get_topic($topicSlug) : null;
if (!$topic) { http_response_code(404); die('Topic not found.'); }

$doc = $docSlug ? get_document($topic['id'], $docSlug) : null;
if (!$doc || ($doc['status'] !== 'published' && !has_role('contributor'))) {
    http_response_code(404); die('Document not found.');
}

// ── Access control ────────────────────────────────────────────────────────────
$accessType = $doc['access_type'] ?? 'public';
$accessOk   = true;
$passwordError = '';

if (!has_role('contributor')) {
    if ($accessType === 'link') {
        if (empty($doc['access_token']) || $urlToken !== $doc['access_token']) {
            $accessOk = false;
        }
    } elseif ($accessType === 'password') {
        $sessionKey = 'doc_unlocked_' . $doc['id'];
        if (empty($_SESSION[$sessionKey])) {
            if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['doc_password'])) {
                verify_csrf();
                if (!empty($doc['access_password']) && password_verify($_POST['doc_password'], $doc['access_password'])) {
                    $_SESSION[$sessionKey] = true;
                    $redir = base_url('topic/' . rawurlencode($topic['slug']) . '/' . rawurlencode($doc['slug']));
                    header('Location: ' . $redir); exit;
                } else {
                    $passwordError = 'Incorrect password. Please try again.';
                }
            }
            $accessOk = false;
        }
    }
}

$tags      = get_document_tags($doc['id']);
$revisions = get_revisions($doc['id']);
$ogImage   = $doc['og_image'] ?: $topic['og_image'] ?: get_setting('og_image');

// Sibling document navigation within this topic
$siblingDocs = get_documents($topic['id']); // published, ordered by updated_at DESC
$siblingIdx  = array_search($doc['slug'], array_column($siblingDocs, 'slug'));
$prevSibling = ($siblingIdx !== false && $siblingIdx > 0)                       ? $siblingDocs[$siblingIdx - 1] : null;
$nextSibling = ($siblingIdx !== false && $siblingIdx < count($siblingDocs) - 1) ? $siblingDocs[$siblingIdx + 1] : null;

// Build the canonical URL (include token for link-type so OG links work)
$canonicalUrl = doc_url($topic['slug'], $doc['slug']);
if ($accessType === 'link' && !empty($doc['access_token'])) {
    $canonicalUrl .= '?token=' . rawurlencode($doc['access_token']);
}

$meta = build_meta([
    'title'    => $doc['title'] . ' — ' . $topic['title'],
    'og_title' => $doc['title'],
    'og_desc'  => $doc['meta_description'] ?: get_setting('og_description'),
    'og_image' => $ogImage,
    'og_type'  => 'article',
    'og_url'   => $canonicalUrl,
]);
include ROOT_PATH . '/includes/header.php';

// ── Access denied: password form ──────────────────────────────────────────────
if (!$accessOk && $accessType === 'password'):
?>
<div class="window access-gate" style="max-width:400px;margin:40px auto">
  <div class="win-titlebar">&#128274; Password Required</div>
  <div class="win-body">
    <p style="margin:0 0 12px">This document is password protected.</p>
    <?php if ($passwordError): ?>
      <div class="flash flash-error"><?= h($passwordError) ?></div>
    <?php endif; ?>
    <form method="post">
      <input type="hidden" name="csrf_token" value="<?= csrf_token() ?>">
      <div class="form-group">
        <label for="doc_password">Password</label>
        <input type="password" id="doc_password" name="doc_password" class="input-full" required autofocus>
      </div>
      <div class="form-actions">
        <button type="submit" class="button">Unlock Document</button>
        <a href="<?= h(topic_url($topic['slug'])) ?>" class="button" style="margin-left:8px">&#8592; Back</a>
      </div>
    </form>
  </div>
</div>
<?php include ROOT_PATH . '/includes/footer.php'; exit; endif; ?>

<?php
// ── Access denied: link only ──────────────────────────────────────────────────
if (!$accessOk && $accessType === 'link'):
?>
<div class="window access-gate" style="max-width:440px;margin:40px auto">
  <div class="win-titlebar">&#128279; Access Restricted</div>
  <div class="win-body">
    <p>This document is only accessible via a special private link.</p>
    <p style="font-size:13px;color:#555">If you have received the link, please use it to access this document. Generic URL access is not permitted.</p>
    <a href="<?= h(topic_url($topic['slug'])) ?>" class="button">&#8592; Back to Topic</a>
  </div>
</div>
<?php include ROOT_PATH . '/includes/footer.php'; exit; endif; ?>

<?php record_page_view((int)$doc['id']); ?>

<div class="breadcrumb">
  <a href="<?= h(base_url()) ?>">Home</a> &rsaquo;
  <?php foreach (get_topic_ancestors((int)$topic['id']) as $anc): ?>
    <a href="<?= h(topic_url($anc['slug'])) ?>"><?= h($anc['title']) ?></a> &rsaquo;
  <?php endforeach; ?>
  <a href="<?= h(topic_url($topic['slug'])) ?>"><?= h($topic['title']) ?></a> &rsaquo;
  <?= h($doc['title']) ?>
</div>

<?php if ($accessType === 'password' && !has_role('contributor')): ?>
  <div class="flash flash-info" style="margin-bottom:8px">&#128274; This document is password protected. Your access expires when your session ends.</div>
<?php endif; ?>

<div class="doc-layout">

  <!-- Main document window -->
  <div class="window doc-window">
    <div class="win-titlebar">
      &#128196; <?= h($doc['title']) ?>
      <div class="win-actions">
        <?php
        $printLink = print_url($topic['slug'], $doc['slug']);
        if ($accessType === 'link' && !empty($doc['access_token'])) {
            $printLink .= '?token=' . rawurlencode($doc['access_token']);
        }
        ?>
        <a href="<?= h($printLink) ?>" target="_blank" class="button btn-sm" title="Print">&#128424; Print</a>
        <?php if (has_role('contributor')): ?>
          <a href="<?= h(base_url('admin/document-edit.php?id=' . $doc['id'])) ?>" class="button btn-sm">&#9998; Edit</a>
        <?php endif; ?>
      </div>
    </div>
    <div class="win-body">

      <?php if ($doc['status'] === 'draft'): ?>
        <div class="flash flash-info">&#9888; This document is a draft and not publicly visible.</div>
      <?php endif; ?>

      <?php if ($doc['og_image']): ?>
        <img src="<?= h(base_url('uploads/og/' . $doc['og_image'])) ?>" alt="" class="doc-og-image">
      <?php endif; ?>

      <div class="markdown-body" id="doc-content">
        <?= md($doc['content']) ?>
      </div>

    </div>
  </div>

  <!-- Sidebar -->
  <aside class="doc-sidebar">

    <div class="window sidebar-section">
      <div class="win-titlebar">Document Info</div>
      <div class="win-body">
        <dl class="doc-meta-list">
          <dt>Topic</dt><dd><a href="<?= h(topic_url($topic['slug'])) ?>"><?= h($topic['title']) ?></a></dd>
          <?php if ($doc['author_name']): ?>
          <dt>Author</dt><dd><?= h($doc['author_name']) ?></dd>
          <?php endif; ?>
          <dt>Created</dt><dd><?= h(fmt_date($doc['created_at'])) ?></dd>
          <dt>Modified</dt><dd><?= h(fmt_date($doc['updated_at'])) ?></dd>
          <?php if ($accessType !== 'public'): ?>
          <dt>Access</dt>
          <dd>
            <?php if ($accessType === 'password'): ?>
              <span style="color:#800000">&#128274; Password</span>
            <?php else: ?>
              <span style="color:#007000">&#128279; Link Only</span>
            <?php endif; ?>
          </dd>
          <?php endif; ?>
        </dl>
      </div>
    </div>

    <?php if (!empty($tags)): ?>
    <div class="window sidebar-section">
      <div class="win-titlebar">Audience Tags</div>
      <div class="win-body">
        <?php foreach ($tags as $tg): ?>
          <a href="<?= h(topic_url($topic['slug'])) ?>?tag=<?= $tg['id'] ?>"
             class="tag-badge" style="background:<?= h($tg['color']) ?>;color:#FFF"
             title="<?= h($tg['description']) ?>"><?= h($tg['name']) ?></a>
        <?php endforeach; ?>
      </div>
    </div>
    <?php endif; ?>

    <div class="window sidebar-section">
      <div class="win-titlebar">Actions</div>
      <div class="win-body">
        <a href="<?= h($printLink) ?>" target="_blank" class="button full-width">&#128424; Print Document</a>
        <a href="<?= h(rss_url($topic['slug'])) ?>" class="button full-width" style="margin-top:6px">&#8277; Topic RSS Feed</a>
        <?php if ($accessType === 'link' && !empty($doc['access_token'])): ?>
          <div style="margin-top:8px">
            <label style="font-size:11px;font-weight:bold">Private Link:</label>
            <div style="display:flex;gap:4px;margin-top:3px">
              <input type="text" value="<?= h($canonicalUrl) ?>" id="share-link-pub" class="input-full" readonly style="font-size:11px;padding:2px 4px">
              <button type="button" class="button btn-sm" onclick="copyShareLink()" title="Copy to clipboard">&#128203;</button>
            </div>
          </div>
        <?php endif; ?>
      </div>
    </div>

  </aside>
</div>

<!-- Revision History -->
<?php if (!empty($revisions)): ?>
<div class="window revisions-window">
  <div class="win-titlebar">&#128196; Revision History</div>
  <div class="win-body">
    <div class="table-responsive">
    <table class="doc-table">
      <thead><tr><th>Date</th><th>Changed By</th><th>Note</th></tr></thead>
      <tbody>
      <?php foreach ($revisions as $rev): ?>
        <tr>
          <td><?= h(fmt_date($rev['created_at'], true)) ?></td>
          <td><?= h($rev['username'] ?? '—') ?></td>
          <td><?= h($rev['change_note'] ?: '—') ?></td>
        </tr>
      <?php endforeach; ?>
      </tbody>
    </table>
    </div>
  </div>
</div>
<?php endif; ?>

<script>
function copyShareLink() {
  var inp = document.getElementById('share-link-pub');
  if (!inp) return;
  if (navigator.clipboard) {
    navigator.clipboard.writeText(inp.value).then(function(){
      var btn = inp.nextElementSibling;
      var old = btn.innerHTML;
      btn.innerHTML = '&#10003;';
      setTimeout(function(){ btn.innerHTML = old; }, 2000);
    });
  } else {
    inp.select(); document.execCommand('copy');
  }
}
</script>

<?php if ($prevSibling || $nextSibling): ?>
<style>
.doc-nav-arrow {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  z-index: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 64px;
  background: rgba(0,0,0,0.55);
  color: #FFF;
  font-size: 20px;
  text-decoration: none;
  border: 1px solid rgba(255,255,255,0.25);
  transition: background 0.15s;
  cursor: pointer;
  user-select: none;
}
.doc-nav-arrow:hover { background: rgba(0,0,100,0.75); color: #FFF; border-color: rgba(255,255,255,0.5); }
.doc-nav-arrow.doc-nav-prev { left: 0; border-left: none; border-radius: 0 4px 4px 0; }
.doc-nav-arrow.doc-nav-next { right: 0; border-right: none; border-radius: 4px 0 0 4px; }
@media (max-width: 640px) {
  .doc-nav-arrow { width: 28px; height: 52px; font-size: 16px; opacity: 0.7; }
}
</style>
<?php if ($prevSibling): ?>
  <a href="<?= h(doc_url($topic['slug'], $prevSibling['slug'])) ?>"
     class="doc-nav-arrow doc-nav-prev"
     title="&#8592; <?= h($prevSibling['title']) ?>">&#9664;</a>
<?php endif; ?>
<?php if ($nextSibling): ?>
  <a href="<?= h(doc_url($topic['slug'], $nextSibling['slug'])) ?>"
     class="doc-nav-arrow doc-nav-next"
     title="&#8594; <?= h($nextSibling['title']) ?>">&#9654;</a>
<?php endif; ?>
<?php endif; ?>

<script>
window.docNav = {
  prev: <?= $prevSibling ? json_encode(doc_url($topic['slug'], $prevSibling['slug'])) : 'null' ?>,
  next: <?= $nextSibling ? json_encode(doc_url($topic['slug'], $nextSibling['slug'])) : 'null' ?>
};
</script>

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