= h((string)$photo['code_name']) ?>
= h((string)($photo['description'] ?? '')) ?>
Комментарии
Комментарии может оставлять только пользователь с персональной ссылкой.
= nl2br(h($welcomeText)) ?>
В разделе пока нет фотографий.
0 && $text !== '') { $u = commenterByToken($token); if ($u) { try { $savedComment = commentAdd($photoId, (int)$u['id'], limitText($text, 1000)); $commentSaved = true; } catch (Throwable $e) { error_log('Comment add failed: ' . $e->getMessage()); $errorMessage = 'Не удалось отправить комментарий.'; $errorCode = 500; } } else { $errorMessage = 'Ссылка для комментариев недействительна.'; } } else { $errorMessage = 'Заполни текст комментария.'; } if ($isAjax) { header('Content-Type: application/json; charset=utf-8'); if ($commentSaved) { echo json_encode([ 'ok' => true, 'message' => 'Ваш комментарий отправлен.', 'comment' => $savedComment ? [ 'id' => (int)($savedComment['id'] ?? 0), 'comment_text' => (string)($savedComment['comment_text'] ?? ''), 'created_at' => (string)($savedComment['created_at'] ?? ''), 'display_name' => (string)($savedComment['display_name'] ?? 'Пользователь'), ] : null, ], JSON_UNESCAPED_UNICODE); exit; } http_response_code($errorCode); echo json_encode(['ok' => false, 'message' => $errorMessage !== '' ? $errorMessage : 'Не удалось отправить комментарий.'], JSON_UNESCAPED_UNICODE); exit; } $redirect = './?photo_id=' . $photoId; if ($sectionId > 0) { $redirect .= '§ion_id=' . $sectionId; } if ($topicId > 0) { $redirect .= '&topic_id=' . $topicId; } if ($token !== '') { $redirect .= '&viewer=' . urlencode($token); } header('Location: ' . $redirect); exit; } $sections = sectionsAll(); $activeSectionId = (int)($_GET['section_id'] ?? 0); $activePhotoId = (int)($_GET['photo_id'] ?? 0); $activeTopicId = (int)($_GET['topic_id'] ?? 0); $welcomeText = settingGet('welcome_text', 'Добро пожаловать в галерею. Выберите раздел слева, чтобы посмотреть фотографии.'); $hasVisibleSections = false; foreach ($sections as $s) { if ((int)($s['photos_count'] ?? 0) > 0) { $hasVisibleSections = true; break; } } $photo = $activePhotoId > 0 ? photoById($activePhotoId) : null; if ($photo && $activeSectionId < 1) { $activeSectionId = (int)$photo['section_id']; } $filterMode = $activeTopicId > 0 ? 'topic' : ($activeSectionId > 0 ? 'section' : 'none'); $comments = $photo ? commentsByPhoto($activePhotoId) : []; $topics = []; $topicCounts = []; $topicTree = []; $hasVisibleTopics = false; $visibleTopicTree = []; try { $topics = topicsAllForSelect(); if ($activeTopicId > 0) { if (!topicById($activeTopicId)) { $activeTopicId = 0; $filterMode = $activeSectionId > 0 ? 'section' : 'none'; } } $topicCounts = topicPhotoCounts(null); $topicTree = buildTopicTreePublic($topics); foreach ($topicTree as $root) { $rootCount = (int)($topicCounts[(int)$root['id']] ?? 0); $visibleChildren = []; foreach (($root['children'] ?? []) as $child) { $childCount = (int)($topicCounts[(int)$child['id']] ?? 0); if ($childCount < 1) { continue; } $child['visible_count'] = $childCount; $visibleChildren[] = $child; } if ($rootCount < 1 && $visibleChildren === []) { continue; } $root['visible_count'] = $rootCount; $root['children'] = $visibleChildren; $visibleTopicTree[] = $root; } $hasVisibleTopics = $visibleTopicTree !== []; } catch (Throwable) { $topics = []; $topicCounts = []; $topicTree = []; $activeTopicId = 0; $filterMode = $activeSectionId > 0 ? 'section' : 'none'; $hasVisibleTopics = false; $visibleTopicTree = []; } $photos = ($activeSectionId > 0 || $activeTopicId > 0) ? photosForPublic($filterMode === 'section' ? $activeSectionId : null, $filterMode === 'topic' ? $activeTopicId : null) : []; $photoCommentCounts = photoCommentCountsByPhotoIds(array_map(static fn(array $p): int => (int)$p['id'], $photos)); $isHomePage = $activeSectionId < 1 && $activePhotoId < 1 && $activeTopicId < 1; $isTopicMode = $filterMode === 'topic'; $isSectionMode = $filterMode === 'section'; $sectionNames = []; foreach ($sections as $s) { $sectionNames[(int)$s['id']] = (string)$s['name']; } $activeTopicName = ''; $activeTopicShortName = ''; foreach ($topics as $t) { if ((int)$t['id'] === $activeTopicId) { $activeTopicName = (string)$t['full_name']; $activeTopicShortName = (string)$t['name']; break; } } $detailTotal = 0; $detailIndex = 0; $prevPhotoId = 0; $nextPhotoId = 0; $firstPhotoId = 0; $lastPhotoId = 0; $detailSectionId = 0; $photoTopics = []; if ($photo) { $detailSectionId = (int)$photo['section_id']; try { $photoTopics = photoTopicsByPhotoId($activePhotoId); } catch (Throwable) { $photoTopics = []; } if ($isTopicMode) { $detailPhotos = photosForPublic(null, $activeTopicId); } else { $detailPhotos = photosForPublic($detailSectionId, null); } if ($activeTopicId > 0 && $detailPhotos !== []) { $foundInTopic = false; foreach ($detailPhotos as $d) { if ((int)$d['id'] === $activePhotoId) { $foundInTopic = true; break; } } if (!$foundInTopic) { $detailPhotos = photosForPublic($detailSectionId, null); $activeTopicId = 0; $isTopicMode = false; $isSectionMode = true; } } $detailTotal = count($detailPhotos); if ($detailTotal > 0) { $firstPhotoId = (int)$detailPhotos[0]['id']; $lastPhotoId = (int)$detailPhotos[$detailTotal - 1]['id']; } foreach ($detailPhotos as $i => $p) { if ((int)$p['id'] !== $activePhotoId) { continue; } $detailIndex = $i + 1; if ($i > 0) { $prevPhotoId = (int)$detailPhotos[$i - 1]['id']; } if ($i < $detailTotal - 1) { $nextPhotoId = (int)$detailPhotos[$i + 1]['id']; } break; } } $hasMobilePhotoNav = $activePhotoId > 0 && $photo && $detailTotal > 0; $hasMobileCatalogNav = !$photo && ($isTopicMode || $isSectionMode); $bodyClasses = [$isHomePage ? 'is-home' : 'is-inner']; if ($hasMobilePhotoNav || $hasMobileCatalogNav) { $bodyClasses[] = 'has-mobile-nav'; } $detailLocationLabel = ''; if ($activeTopicId > 0 && $activeTopicName !== '') { $detailLocationLabel = 'в тематике «' . $activeTopicName . '»'; } elseif ($detailSectionId > 0 && isset($sectionNames[$detailSectionId])) { $detailLocationLabel = 'в разделе «' . $sectionNames[$detailSectionId] . '»'; } $detailCounterLabel = $detailTotal > 0 ? ('Фото ' . $detailIndex . ' из ' . $detailTotal . ($detailLocationLabel !== '' ? ' ' . $detailLocationLabel : '')) : ''; $pageHeading = ''; if ($isTopicMode && $activeTopicShortName !== '') { $pageHeading = $activeTopicShortName; } elseif ($isSectionMode && isset($sectionNames[$activeSectionId])) { $pageHeading = $sectionNames[$activeSectionId]; } elseif ($photo && isset($sectionNames[$detailSectionId])) { $pageHeading = $sectionNames[$detailSectionId]; } $catalogLocationLabel = ''; if ($isTopicMode && $activeTopicName !== '') { $catalogLocationLabel = 'Тема: ' . $activeTopicName; } elseif ($isSectionMode && isset($sectionNames[$activeSectionId])) { $catalogLocationLabel = 'Раздел: ' . $sectionNames[$activeSectionId]; } $showCatalogOverview = !$photo && ($isTopicMode || $isSectionMode); $catalogOverviewTitle = $isTopicMode ? $activeTopicName : ($isSectionMode && isset($sectionNames[$activeSectionId]) ? $sectionNames[$activeSectionId] : ''); $catalogOverviewCountLabel = count($photos) . ' фото'; function h(string $v): string { return htmlspecialchars($v, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } function assetUrl(string $path): string { $f=__DIR__ . '/' . ltrim($path,'/'); $v=is_file($f)?(string)filemtime($f):(string)time(); return $path . '?v=' . rawurlencode($v); } function limitText(string $text, int $len): string { return function_exists('mb_substr') ? mb_substr($text, 0, $len) : substr($text, 0, $len); } function imageIntrinsicSize(int $fileId): array { if ($fileId < 1) { return ['width' => 0, 'height' => 0]; } $file = photoFileById($fileId); if (!$file) { return ['width' => 0, 'height' => 0]; } $path = __DIR__ . '/' . ltrim((string)$file['file_path'], '/'); if (!is_file($path)) { return ['width' => 0, 'height' => 0]; } $size = @getimagesize($path); if (!is_array($size)) { return ['width' => 0, 'height' => 0]; } return [ 'width' => max(0, (int)($size[0] ?? 0)), 'height' => max(0, (int)($size[1] ?? 0)), ]; } function commentCountLabel(int $count): string { $mod100 = $count % 100; $mod10 = $count % 10; if ($mod100 >= 11 && $mod100 <= 14) { return $count . ' комментариев'; } if ($mod10 === 1) { return $count . ' комментарий'; } if ($mod10 >= 2 && $mod10 <= 4) { return $count . ' комментария'; } return $count . ' комментариев'; } function buildTopicTreePublic(array $topics): array { $roots = []; $children = []; foreach ($topics as $topic) { $parentId = isset($topic['parent_id']) && $topic['parent_id'] !== null ? (int)$topic['parent_id'] : 0; if ($parentId === 0) { $roots[] = $topic; continue; } if (!isset($children[$parentId])) { $children[$parentId] = []; } $children[$parentId][] = $topic; } foreach ($roots as &$root) { $root['children'] = $children[(int)$root['id']] ?? []; } unset($root); return $roots; } function serveImage(): never { $fileId = (int)($_GET['file_id'] ?? 0); if ($fileId < 1) { http_response_code(404); exit; } $f = photoFileById($fileId); if (!$f) { http_response_code(404); exit; } $abs = __DIR__ . '/' . ltrim((string)$f['file_path'], '/'); if (!is_file($abs)) { http_response_code(404); exit; } if ((string)$f['kind'] !== 'after') { header('Content-Type: ' . ((string)$f['mime_type'] ?: 'application/octet-stream')); header('Content-Length: ' . (string)filesize($abs)); header('Cache-Control: private, max-age=60'); header('X-Robots-Tag: noindex, nofollow'); readfile($abs); exit; } outputWatermarked($abs, (string)$f['mime_type']); } function serveThumb(): never { $fileId = (int)($_GET['file_id'] ?? 0); if ($fileId < 1) { http_response_code(404); exit; } $f = photoFileById($fileId); if (!$f || (string)$f['kind'] !== 'before') { http_response_code(404); exit; } $thumbRel = ensureThumbForSource(__DIR__, (string)$f['file_path']); if ($thumbRel === null) { serveImage(); } $thumbAbs = __DIR__ . '/' . ltrim($thumbRel, '/'); if (!is_file($thumbAbs)) { serveImage(); } $modifiedAt = (int)(filemtime($thumbAbs) ?: time()); $ifModifiedSince = (string)($_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? ''); if ($ifModifiedSince !== '' && strtotime($ifModifiedSince) >= $modifiedAt) { http_response_code(304); exit; } header('Content-Type: image/jpeg'); header('Content-Length: ' . (string)filesize($thumbAbs)); header('Cache-Control: private, max-age=86400'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $modifiedAt) . ' GMT'); header('X-Robots-Tag: noindex, nofollow'); readfile($thumbAbs); exit; } function outputWatermarked(string $path, string $mime): never { $text = trim(settingGet('watermark_text', 'photo.andr33v.ru')); if ($text === '') { $text = 'photo.andr33v.ru'; } $brightness = max(5, min(100, (int)settingGet('watermark_brightness', '35'))); $angle = max(-75, min(75, (int)settingGet('watermark_angle', '-28'))); $imagickOpacity = 0.04 + ($brightness / 100) * 0.24; $gdAlpha = (int)round(127 - ($brightness * 0.9)); $gdAlpha = max(10, min(126, $gdAlpha)); if (extension_loaded('imagick')) { $im = new Imagick($path); $w = max(1, (int)$im->getImageWidth()); $h = max(1, (int)$im->getImageHeight()); $draw = new ImagickDraw(); $draw->setFillColor(new ImagickPixel('rgba(255,255,255,' . number_format($imagickOpacity, 3, '.', '') . ')')); $draw->setFontSize(max(12, (int)($w / 46))); $draw->setTextAntialias(true); $lineText = $text . ' ' . $text . ' ' . $text; $stepY = max(28, (int)($h / 10)); $stepX = max(120, (int)($w / 3)); for ($y = -$h; $y < $h * 2; $y += $stepY) { for ($x = -$w; $x < $w * 2; $x += $stepX) { $im->annotateImage($draw, $x, $y, $angle, $lineText); } } header('Content-Type: ' . ($mime !== '' ? $mime : 'image/jpeg')); $im->setImageCompressionQuality(88); echo $im; $im->clear(); $im->destroy(); exit; } [$w, $h, $type] = @getimagesize($path) ?: [0,0,0]; $img = match ($type) { IMAGETYPE_JPEG => @imagecreatefromjpeg($path), IMAGETYPE_PNG => @imagecreatefrompng($path), IMAGETYPE_GIF => @imagecreatefromgif($path), IMAGETYPE_WEBP => function_exists('imagecreatefromwebp') ? @imagecreatefromwebp($path) : null, default => null, }; if (!$img) { readfile($path); exit; } $font = 2; $color = imagecolorallocatealpha($img, 255, 255, 255, $gdAlpha); $lineText = $text . ' ' . $text . ' ' . $text; $stepY = max(16, imagefontheight($font) + 8); $stepX = max(120, (int)($w / 3)); $row = 0; $skew = max(6, min(48, (int)round(abs($angle)))); $dir = $angle < 0 ? 1 : -1; for ($y = -$h; $y < $h * 2; $y += $stepY) { $offset = ($row * $skew * $dir) % $stepX; for ($x = -$w - $offset; $x < $w * 2; $x += $stepX) { imagestring($img, $font, $x, $y, $lineText, $color); } $row++; } header('Content-Type: image/jpeg'); imagejpeg($img, null, 88); imagedestroy($img); exit; } ?>
= h((string)($photo['description'] ?? '')) ?>
Комментарии может оставлять только пользователь с персональной ссылкой.
= nl2br(h($welcomeText)) ?>
В разделе пока нет фотографий.
= nl2br(h((string)$c['comment_text'])) ?>