0 && $text !== '') {
$u = commenterByToken($token);
if ($u) {
commentAdd($photoId, (int)$u['id'], limitText($text, 1000));
$commentSaved = true;
} else {
$errorMessage = 'Ссылка для комментариев недействительна.';
}
} else {
$errorMessage = 'Заполни текст комментария.';
}
if ($isAjax) {
header('Content-Type: application/json; charset=utf-8');
if ($commentSaved) {
echo json_encode(['ok' => true, 'message' => 'Ваш комментарий отправлен.'], JSON_UNESCAPED_UNICODE);
exit;
}
http_response_code(422);
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;
$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);
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] . '»';
}
$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];
}
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 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;
}
?>
Фотогалерея
0 && $photo): ?>
= h((string)$photo['code_name']) ?>
= h((string)($photo['description'] ?? '')) ?>
Комментарии может оставлять только пользователь с персональной ссылкой.
= h((string)($c['display_name'] ?? 'Пользователь')) ?> · = h((string)$c['created_at']) ?>
= nl2br(h((string)$c['comment_text'])) ?>
0): ?>
= nl2br(h($welcomeText)) ?>
В разделе пока нет фотографий.