From 0e8db9006757d9204321b96bc0337dd3f748e273 Mon Sep 17 00:00:00 2001 From: mrcatmann Date: Fri, 31 Jul 2026 17:21:37 +0300 Subject: [PATCH 1/3] feat(photos): saved photos --- VKAPI/Handlers/Photos.php | 10 +-- VKAPI/Handlers/Users.php | 2 +- Web/Models/Entities/Album.php | 15 +++- Web/Models/Entities/Photo.php | 5 ++ Web/Models/Entities/User.php | 46 +++++------ Web/Models/Repositories/Albums.php | 82 +++++++++++--------- Web/Presenters/PhotosPresenter.php | 50 +++++++++++- Web/Presenters/UserPresenter.php | 19 +---- Web/Presenters/templates/Photos/Photo.latte | 8 ++ Web/Presenters/templates/User/Settings.latte | 13 ++++ Web/routes.yml | 4 + Web/static/css/main.css | 9 +++ Web/static/js/al_photos.js | 4 + Web/static/js/router.js | 30 +++---- locales/ru.strings | 4 + 15 files changed, 195 insertions(+), 106 deletions(-) diff --git a/VKAPI/Handlers/Photos.php b/VKAPI/Handlers/Photos.php index e401b70cb..dba3d7991 100644 --- a/VKAPI/Handlers/Photos.php +++ b/VKAPI/Handlers/Photos.php @@ -334,11 +334,11 @@ public function getAlbums(int $owner_id = null, string $album_ids = "", int $off $albums_list = null; if ($owner_id > 0) { # TODO rewrite to offset - $albums_list = array_slice(iterator_to_array((new Albums())->getUserAlbums($owner, 1, $count + $offset)), $offset); - $res["count"] = (new Albums())->getUserAlbumsCount($owner); + $albums_list = array_slice(iterator_to_array((new Albums())->getUserAlbums($owner, 1, $count + $offset, $this->getUser())), $offset); + $res["count"] = (new Albums())->getUserAlbumsCount($owner, $this->getUser()); } else { - $albums_list = array_slice(iterator_to_array((new Albums())->getClubAlbums($owner, 1, $count + $offset)), $offset); - $res["count"] = (new Albums())->getClubAlbumsCount($owner); + $albums_list = array_slice(iterator_to_array((new Albums())->getClubAlbums($owner, 1, $count + $offset, $this->getUser())), $offset); + $res["count"] = (new Albums())->getClubAlbumsCount($owner, $this->getUser()); } } else { $album_ids = explode(',', $album_ids); @@ -377,7 +377,7 @@ public function getAlbumsCount(int $user_id = null, int $group_id = null) $this->fail(15, "Access denied"); } - return (new Albums())->getUserAlbumsCount($__user); + return (new Albums())->getUserAlbumsCount($__user, $this->getUser()); } if (!is_null($group_id)) { $__club = (new Clubs())->get($group_id); diff --git a/VKAPI/Handlers/Users.php b/VKAPI/Handlers/Users.php index b09e82a55..70f7a011f 100644 --- a/VKAPI/Handlers/Users.php +++ b/VKAPI/Handlers/Users.php @@ -299,7 +299,7 @@ public function get(string $user_ids = "0", string $fields = "", int $offset = 0 "online_friends" => $usr->getFriendsOnlineCount(), "mutual_friends" => 0, // FIXME: not implemented "user_photos" => 0, // FIXME: not implemented - "albums" => (new Albums())->getUserAlbumsCount($usr), + "albums" => (new Albums())->getUserAlbumsCount($usr, $authuser), "followers" => $usr->getFollowersCount(), "gifts" => $usr->getGiftCount(), ]; diff --git a/Web/Models/Entities/Album.php b/Web/Models/Entities/Album.php index 707b955ac..b938c7de6 100644 --- a/Web/Models/Entities/Album.php +++ b/Web/Models/Entities/Album.php @@ -10,6 +10,7 @@ class Album extends MediaCollection { public const SPECIAL_AVATARS = 16; public const SPECIAL_WALL = 32; + public const SPECIAL_SAVED = 64; protected $tableName = "albums"; protected $relTableName = "album_relations"; @@ -17,9 +18,9 @@ class Album extends MediaCollection protected $entityClassName = 'openvk\Web\Models\Entities\Photo'; protected $specialNames = [ - 16 => "_avatar_album", - 32 => "_wall_album", - 64 => "_saved_photos_album", + self::SPECIAL_AVATARS => "_avatar_album", + self::SPECIAL_WALL => "_wall_album", + self::SPECIAL_SAVED => "_saved_photos_album", ]; public function getCoverURL(): ?string @@ -75,6 +76,11 @@ public function hasPhoto(Photo $photo): bool return $this->has($photo); } + public function getSpecialType(): ?int + { + return $this->getRecord()->special_type; + } + public function canBeViewedBy(?User $user = null): bool { if ($this->isDeleted()) { @@ -82,6 +88,9 @@ public function canBeViewedBy(?User $user = null): bool } $owner = $this->getOwner(); + if ($this->getSpecialType() === self::SPECIAL_SAVED && !$owner->getPrivacyPermission('photos.read_saved', $user)) { + return false; + } if (get_class($owner) == "openvk\\Web\\Models\\Entities\\User") { return $owner->canBeViewedBy($user) && $owner->getPrivacyPermission('photos.read', $user); diff --git a/Web/Models/Entities/Photo.php b/Web/Models/Entities/Photo.php index aee284b46..c81bbb106 100644 --- a/Web/Models/Entities/Photo.php +++ b/Web/Models/Entities/Photo.php @@ -436,4 +436,9 @@ public function toNotifApiStruct() return $res; } + + public function isAvailableForSaving(): bool + { + return $this->canBeViewedBy(); // разрешаем сохранять только общедоступные фото (?) + } } diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index 819efdd40..67f28d9ac 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -42,6 +42,22 @@ class User extends RowModel public const NSFW_TOLERANT = 1; public const NSFW_FULL_TOLERANT = 2; + public const SETTINGS_PRIVACY = [ + "page.read", + "page.info.read", + "groups.read", + "photos.read", + "videos.read", + "notes.read", + "friends.read", + "friends.add", + "wall.write", + "messages.write", + "audios.read", + "likes.read", + "photos.read_saved", + ]; + /* aggressive caching */ private $_avatarAlbum = null; private $_avatarPhoto = false; // false - not resolved, null - no avatar @@ -598,20 +614,7 @@ public function getPrivacySetting(string $id): int { return (int) bmask($this->getRecord()->privacy, [ "length" => 2, - "mappings" => [ - "page.read", - "page.info.read", - "groups.read", - "photos.read", - "videos.read", - "notes.read", - "friends.read", - "friends.add", - "wall.write", - "messages.write", - "audios.read", - "likes.read", - ], + "mappings" => User::SETTINGS_PRIVACY, ])->get($id); } @@ -1306,20 +1309,7 @@ public function setPrivacySetting(string $id, int $status): void { $this->stateChanges("privacy", bmask($this->changes["privacy"] ?? $this->getRecord()->privacy, [ "length" => 2, - "mappings" => [ - "page.read", - "page.info.read", - "groups.read", - "photos.read", - "videos.read", - "notes.read", - "friends.read", - "friends.add", - "wall.write", - "messages.write", - "audios.read", - "likes.read", - ], + "mappings" => self::SETTINGS_PRIVACY, ])->set($id, $status)->toInteger()); } diff --git a/Web/Models/Repositories/Albums.php b/Web/Models/Repositories/Albums.php index 4a5348594..e73091307 100644 --- a/Web/Models/Repositories/Albums.php +++ b/Web/Models/Repositories/Albums.php @@ -44,18 +44,29 @@ public function get(int $id): ?Album return self::$cache[$id] ??= $this->toAlbum($this->albums->get($id)); } - public function getUserAlbums(User $user, int $page = 1, ?int $perPage = null): \Traversable + private function getUserQuery(User $user, ?User $for): \Nette\Database\Table\Selection { - $perPage ??= OPENVK_DEFAULT_PER_PAGE; $albums = $this->albums->where("owner", $user->getId())->where("deleted", false); + + if (!$user->getPrivacyPermission('photos.read_saved', $for)) { + $albums->where("special_type NOT", [Album::SPECIAL_SAVED]); + } + return $albums; + } + + public function getUserAlbums(User $user, int $page = 1, ?int $perPage = null, ?User $for): \Traversable + { + $perPage ??= OPENVK_DEFAULT_PER_PAGE; + $albums = $this->getUserQuery($user, $for); + foreach ($albums->page($page, $perPage) as $album) { yield new Album($album); } } - public function getUserAlbumsCount(User $user): int + public function getUserAlbumsCount(User $user, ?User $for): int { - $albums = $this->albums->where("owner", $user->getId())->where("deleted", false); + $albums = $this->getUserQuery($user, $for); return sizeof($albums); } @@ -76,23 +87,8 @@ public function getClubAlbumsCount(Club $club): int public function getAvatarAlbumById(int $id, int $regTime): Album { - $data = $this->getSpecialConditions($id, 16); - $album = $this->albums->where([ - "owner" => $id, - "special_type" => 16, - ])->fetch(); - if (!$album) { - $album = new Album(); - $album->setName("[!!! internal album]"); - $album->setOwner($id); - $album->setSpecial_Type(16); - $album->setCreated($regTime); - $album->save(); - - return $album; - } - - return new Album($album); + $data = $this->getSpecialConditions($id, Album::SPECIAL_AVATARS); + return $this->getOrCreateSpecialAlbum($id, Album::SPECIAL_AVATARS, $regTime); } public function getUserAvatarAlbum(User $user): Album @@ -107,23 +103,9 @@ public function getClubAvatarAlbum(Club $club): Album public function getUserWallAlbum(User $user): Album { - $data = $this->getSpecialConditions($user->getId(), 32); - $album = $this->albums->where([ - "owner" => $user->getId(), - "special_type" => 32, - ])->fetch(); - if (!$album) { - $album = new Album(); - $album->setName("[!!! internal album]"); - $album->setOwner($user->getId()); - $album->setSpecial_Type(32); - $album->setCreated($user->getRegistrationTime()->timestamp()); - $album->save(); + $data = $this->getSpecialConditions($user->getId(), Album::SPECIAL_WALL); - return $album; - } - - return new Album($album); + return $this->getOrCreateSpecialAlbum($user->getId(), Album::SPECIAL_WALL, $user->getRegistrationTime()->timestamp()); } public function getAlbumByPhotoId(Photo $photo): ?Album @@ -142,4 +124,30 @@ public function getAlbumByOwnerAndId(int $owner, int $id) return $album ? new Album($album) : null; } + + public function getUserSavedAlbum(User $user): Album + { + return $this->getOrCreateSpecialAlbum($user->getId(), Album::SPECIAL_SAVED, $user->getRegistrationTime()->timestamp()); + } + + private function getOrCreateSpecialAlbum(int $ownerId, int $specialType, int $regTime): Album + { + $album = $this->albums->where([ + "owner" => $ownerId, + "special_type" => $specialType, + ])->fetch(); + + if (!$album) { + $album = new Album(); + $album->setName("[!!! internal album]"); + $album->setOwner($ownerId); + $album->setSpecial_Type($specialType); + $album->setCreated($regTime); + $album->save(); + + return $album; + } + + return new Album($album); + } } diff --git a/Web/Presenters/PhotosPresenter.php b/Web/Presenters/PhotosPresenter.php index 63abc53d5..484584ba0 100644 --- a/Web/Presenters/PhotosPresenter.php +++ b/Web/Presenters/PhotosPresenter.php @@ -35,8 +35,8 @@ public function renderAlbumList(int $owner): void $this->flashFail("err", tr("forbidden"), tr("forbidden_comment")); } - $this->template->albums = $this->albums->getUserAlbums($user, (int) ($this->queryParam("p") ?? 1)); - $this->template->count = $this->albums->getUserAlbumsCount($user); + $this->template->albums = $this->albums->getUserAlbums($user, (int) ($this->queryParam("p") ?? 1), null, $this->user->identity); + $this->template->count = $this->albums->getUserAlbumsCount($user, $this->user->identity); $this->template->owner = $user; $this->template->canEdit = false; if (!is_null($this->user->identity)) { @@ -203,6 +203,7 @@ public function renderPhoto(int $ownerId, int $photoId): void $this->flashFail("err", tr("forbidden"), tr("forbidden_comment")); } + $album = null; if (!is_null($this->queryParam("from"))) { if (preg_match("%^album([0-9]++)$%", $this->queryParam("from"), $matches) === 1) { $album = $this->albums->get((int) $matches[1]); @@ -219,6 +220,9 @@ public function renderPhoto(int $ownerId, int $photoId): void $this->template->cPage = (int) ($this->queryParam("p") ?? 1); $this->template->comments = iterator_to_array($photo->getComments($this->template->cPage)); $this->template->owner = $photo->getOwner(); + + $this->template->canSave = $this->user->identity && (!$album || $album->getSpecialType() != Album::SPECIAL_SAVED) && $photo->isAvailableForSaving(); + $this->template->canDeleteSaved = $this->user->identity && $album && $this->user->identity->getId() == $album->getOwner()->getId() && $album->getSpecialType() == Album::SPECIAL_SAVED; } public function renderAbsolutePhoto($id): void @@ -450,4 +454,46 @@ public function renderLike(int $wall, int $post_id): void $this->redirect("$_SERVER[HTTP_REFERER]"); } + + public function renderSavePhoto(int $owner, int $photoId): void + { + $this->assertUserLoggedIn(); + $this->willExecuteWriteAction(); + $this->assertNoCSRF(); + + $photo = $this->photos->getByOwnerAndVID($owner, $photoId); + if (!$photo) { + $this->notFound(); + } + if (!$photo->canBeViewedBy($this->user->identity)) { + $this->flashFail("err", tr("forbidden"), tr("forbidden_comment")); + } + + $album = $this->albums->getUserSavedAlbum($this->user->identity); + $album->addPhoto($photo); + + header("HTTP/1.1 204 No Content"); + exit(""); + } + + public function renderDeleteSavedPhoto(int $owner, int $photoId): void + { + $this->assertUserLoggedIn(); + $this->willExecuteWriteAction(); + $this->assertNoCSRF(); + + $album = $this->albums->getUserSavedAlbum($this->user->identity); + $photo = $this->photos->getByOwnerAndVID($owner, $photoId); + if (!$photo) { + $this->notFound(); + } + + if ($album->hasPhoto($photo)) { + $album->removePhoto($photo); + } + $album->setEdited(time()); + $album->save(); + + $this->redirect("/album" . $album->getPrettyId()); + } } diff --git a/Web/Presenters/UserPresenter.php b/Web/Presenters/UserPresenter.php index 4a7e759df..381d98d14 100644 --- a/Web/Presenters/UserPresenter.php +++ b/Web/Presenters/UserPresenter.php @@ -7,7 +7,7 @@ use Nette\InvalidStateException; use openvk\Web\Util\Sms; use openvk\Web\Themes\Themepacks; -use openvk\Web\Models\Entities\{Photo, Post, EmailChangeVerification}; +use openvk\Web\Models\Entities\{Photo, Post, EmailChangeVerification, User}; use openvk\Web\Models\Entities\Notifications\{CoinsTransferNotification, RatingUpNotification}; use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Videos, Notes, Vouchers, EmailChangeVerifications, Audios, Faves}; use openvk\Web\Models\Exceptions\InvalidUserNameException; @@ -59,7 +59,7 @@ public function renderView(int $id): void } } else { $this->template->avatarAlbum = (new Albums())->getUserAvatarAlbum($user); - $this->template->albums = array_values(array_filter(iterator_to_array((new Albums())->getUserAlbums($user)), function ($album) { + $this->template->albums = array_values(array_filter(iterator_to_array((new Albums())->getUserAlbums($user, 1, null, null)), function ($album) { return !$album->isCreatedBySystem(); })); $this->template->albumsCount = count($this->template->albums); @@ -618,20 +618,7 @@ public function renderSettings(): void $this->flashFail("err", tr("error"), tr("error_shorturl_incorrect")); } } elseif ($_GET['act'] === "privacy") { - $settings = [ - "page.read", - "page.info.read", - "groups.read", - "photos.read", - "videos.read", - "notes.read", - "friends.read", - "friends.add", - "wall.write", - "messages.write", - "audios.read", - "likes.read", - ]; + $settings = User::SETTINGS_PRIVACY; foreach ($settings as $setting) { $input = $this->postParam(str_replace(".", "_", $setting)); $user->setPrivacySetting($setting, min(3, (int) abs((int) $input ?? $user->getPrivacySetting($setting)))); diff --git a/Web/Presenters/templates/Photos/Photo.latte b/Web/Presenters/templates/Photos/Photo.latte index 0a475d807..98c9a8264 100644 --- a/Web/Presenters/templates/Photos/Photo.latte +++ b/Web/Presenters/templates/Photos/Photo.latte @@ -78,6 +78,14 @@ {_edit} {_delete} +
+ + +
+
+ + +
{_"open_original"} {_report} diff --git a/Web/Presenters/templates/User/Settings.latte b/Web/Presenters/templates/User/Settings.latte index b63316285..1a283cdc9 100644 --- a/Web/Presenters/templates/User/Settings.latte +++ b/Web/Presenters/templates/User/Settings.latte @@ -316,6 +316,19 @@ + + + {_privacy_setting_see_saved_photos} + + + + + {_privacy_setting_see_videos} diff --git a/Web/routes.yml b/Web/routes.yml index fc35abe67..cf4c4b654 100644 --- a/Web/routes.yml +++ b/Web/routes.yml @@ -181,6 +181,10 @@ routes: handler: "Photos->editPhoto" - url: "/photo{num}_{num}/delete" handler: "Photos->deletePhoto" + - url: "/photo{num}_{num}/save" + handler: "Photos->savePhoto" + - url: "/photo{num}_{num}/delete_saved" + handler: "Photos->deleteSavedPhoto" - url: "/al_avatars" handler: "User->setAvatar" - url: "/delete_avatar" diff --git a/Web/static/css/main.css b/Web/static/css/main.css index c8674ead4..84bfc0b60 100644 --- a/Web/static/css/main.css +++ b/Web/static/css/main.css @@ -469,6 +469,15 @@ h1 { font-family: tahoma, verdana, arial, sans-serif; } +#profile_link:disabled, .profile_link:disabled { + cursor: default; + color: unset; +} + +#profile_link:disabled:hover, .profile_link:disabled:hover { + background: unset; +} + .profile_link_form { margin-bottom: 0; } diff --git a/Web/static/js/al_photos.js b/Web/static/js/al_photos.js index 335495018..524b65e8f 100644 --- a/Web/static/js/al_photos.js +++ b/Web/static/js/al_photos.js @@ -195,3 +195,7 @@ u(document).on("paste", ".photo_upload_container", (e) => { u("#uploadButton").trigger("change") } }) + +$(document).on("submitted", ".save_photo", (e) => { + $(e.target).find('input[type=submit]').attr('disabled', 'disabled').val(tr("photo_saved")); +}); \ No newline at end of file diff --git a/Web/static/js/router.js b/Web/static/js/router.js index d727d8deb..caffd10dd 100644 --- a/Web/static/js/router.js +++ b/Web/static/js/router.js @@ -432,23 +432,25 @@ u(document).on('submit', 'form', async (e) => { break } - const parser = new DOMParser - const parsed_content = parser.parseFromString(form_result, 'text/html') + if (form_res.status !== 204) { + const parser = new DOMParser + const parsed_content = parser.parseFromString(form_result, 'text/html') - if(form_res.redirected) { - history.replaceState({'from_router': 1}, '', form_res.url) - } else { - const __new_url = new URL(form_res.url) - __new_url.searchParams.delete('al') - __new_url.searchParams.delete('hash') + if (form_res.redirected) { + history.replaceState({'from_router': 1}, '', form_res.url) + } else { + const __new_url = new URL(form_res.url) + __new_url.searchParams.delete('al') + __new_url.searchParams.delete('hash') - history.pushState({'from_router': 1}, '', __new_url) - } - - window.router.__appendPage(parsed_content) - window.router.__closeMsgs() - await window.router.__integratePage() + history.pushState({'from_router': 1}, '', __new_url) + } + window.router.__appendPage(parsed_content) + window.router.__closeMsgs() + await window.router.__integratePage() + } + $(e.target).trigger('submitted'); u('#ajloader').removeClass('shown') }) diff --git a/locales/ru.strings b/locales/ru.strings index e91cb0c45..2107c96be 100644 --- a/locales/ru.strings +++ b/locales/ru.strings @@ -514,8 +514,11 @@ "photo" = "Фотография"; "upload_button" = "Загрузить"; "open_original" = "Открыть оригинал"; +"save_photo" = "Сохранить к себе"; +"photo_saved" = "Сохранено"; "avatar_album" = "Фотографии со страницы"; "wall_album" = "Фотографии со стены"; +"saved_photos_album" = "Сохранённые фотографии"; "albums_zero" = "Ни одного альбома"; "albums_one" = "$1 альбом"; "albums_few" = "$1 альбома"; @@ -723,6 +726,7 @@ "privacy_setting_read_info" = "Кому видно основную информацию моей страницы"; "privacy_setting_see_groups" = "Кому видно мои группы и встречи"; "privacy_setting_see_photos" = "Кому видно мои фотографии"; +"privacy_setting_see_saved_photos" = "Кому видно мои сохранённые фотографии"; "privacy_setting_see_videos" = "Кому видно мои видеозаписи"; "privacy_setting_see_notes" = "Кому видно мои заметки"; "privacy_setting_see_friends" = "Кому видно моих друзей"; From b98de9965c7af01f624fc1c28f01fdb5c7411d53 Mon Sep 17 00:00:00 2001 From: mrcatmann Date: Sun, 2 Aug 2026 01:53:46 +0300 Subject: [PATCH 2/3] params order fix --- VKAPI/Handlers/Photos.php | 4 ++-- Web/Models/Repositories/Albums.php | 2 +- Web/Presenters/UserPresenter.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VKAPI/Handlers/Photos.php b/VKAPI/Handlers/Photos.php index dba3d7991..57b8e3756 100644 --- a/VKAPI/Handlers/Photos.php +++ b/VKAPI/Handlers/Photos.php @@ -334,10 +334,10 @@ public function getAlbums(int $owner_id = null, string $album_ids = "", int $off $albums_list = null; if ($owner_id > 0) { # TODO rewrite to offset - $albums_list = array_slice(iterator_to_array((new Albums())->getUserAlbums($owner, 1, $count + $offset, $this->getUser())), $offset); + $albums_list = array_slice(iterator_to_array((new Albums())->getUserAlbums($owner, $this->getUser(), 1, $count + $offset)), $offset); $res["count"] = (new Albums())->getUserAlbumsCount($owner, $this->getUser()); } else { - $albums_list = array_slice(iterator_to_array((new Albums())->getClubAlbums($owner, 1, $count + $offset, $this->getUser())), $offset); + $albums_list = array_slice(iterator_to_array((new Albums())->getClubAlbums($owner, 1, $count + $offset)), $offset); $res["count"] = (new Albums())->getClubAlbumsCount($owner, $this->getUser()); } } else { diff --git a/Web/Models/Repositories/Albums.php b/Web/Models/Repositories/Albums.php index e73091307..ee5445f09 100644 --- a/Web/Models/Repositories/Albums.php +++ b/Web/Models/Repositories/Albums.php @@ -54,7 +54,7 @@ private function getUserQuery(User $user, ?User $for): \Nette\Database\Table\Sel return $albums; } - public function getUserAlbums(User $user, int $page = 1, ?int $perPage = null, ?User $for): \Traversable + public function getUserAlbums(User $user, ?User $for, int $page = 1, ?int $perPage = null): \Traversable { $perPage ??= OPENVK_DEFAULT_PER_PAGE; $albums = $this->getUserQuery($user, $for); diff --git a/Web/Presenters/UserPresenter.php b/Web/Presenters/UserPresenter.php index 381d98d14..0ab1c2798 100644 --- a/Web/Presenters/UserPresenter.php +++ b/Web/Presenters/UserPresenter.php @@ -59,7 +59,7 @@ public function renderView(int $id): void } } else { $this->template->avatarAlbum = (new Albums())->getUserAvatarAlbum($user); - $this->template->albums = array_values(array_filter(iterator_to_array((new Albums())->getUserAlbums($user, 1, null, null)), function ($album) { + $this->template->albums = array_values(array_filter(iterator_to_array((new Albums())->getUserAlbums($user, null, 1)), function ($album) { return !$album->isCreatedBySystem(); })); $this->template->albumsCount = count($this->template->albums); From cfa89152e3a474f864bf5e414bbef2c30a6c9c40 Mon Sep 17 00:00:00 2001 From: mrcatmann Date: Sun, 2 Aug 2026 17:37:15 +0300 Subject: [PATCH 3/3] - change save photo mechanism - albums list fix --- Web/Models/Entities/Photo.php | 11 +++++++ Web/Presenters/PhotosPresenter.php | 33 ++++++--------------- Web/Presenters/templates/Photos/Photo.latte | 4 --- Web/routes.yml | 2 -- 4 files changed, 20 insertions(+), 30 deletions(-) diff --git a/Web/Models/Entities/Photo.php b/Web/Models/Entities/Photo.php index c81bbb106..cdb193fb4 100644 --- a/Web/Models/Entities/Photo.php +++ b/Web/Models/Entities/Photo.php @@ -441,4 +441,15 @@ public function isAvailableForSaving(): bool { return $this->canBeViewedBy(); // разрешаем сохранять только общедоступные фото (?) } + + public function copyFrom(Photo $photo): void + { + $record = $photo->getRecord(); + + $this->stateChanges("hash", $record->hash); + $this->setSizes($record->sizes); + $this->setWidth($record->width); + $this->setHeight($record->height); + } + } diff --git a/Web/Presenters/PhotosPresenter.php b/Web/Presenters/PhotosPresenter.php index 484584ba0..58bc8b99b 100644 --- a/Web/Presenters/PhotosPresenter.php +++ b/Web/Presenters/PhotosPresenter.php @@ -35,7 +35,7 @@ public function renderAlbumList(int $owner): void $this->flashFail("err", tr("forbidden"), tr("forbidden_comment")); } - $this->template->albums = $this->albums->getUserAlbums($user, (int) ($this->queryParam("p") ?? 1), null, $this->user->identity); + $this->template->albums = $this->albums->getUserAlbums($user, $this->user->identity, (int) ($this->queryParam("p") ?? 1)); $this->template->count = $this->albums->getUserAlbumsCount($user, $this->user->identity); $this->template->owner = $user; $this->template->canEdit = false; @@ -222,7 +222,6 @@ public function renderPhoto(int $ownerId, int $photoId): void $this->template->owner = $photo->getOwner(); $this->template->canSave = $this->user->identity && (!$album || $album->getSpecialType() != Album::SPECIAL_SAVED) && $photo->isAvailableForSaving(); - $this->template->canDeleteSaved = $this->user->identity && $album && $this->user->identity->getId() == $album->getOwner()->getId() && $album->getSpecialType() == Album::SPECIAL_SAVED; } public function renderAbsolutePhoto($id): void @@ -470,30 +469,16 @@ public function renderSavePhoto(int $owner, int $photoId): void } $album = $this->albums->getUserSavedAlbum($this->user->identity); - $album->addPhoto($photo); - header("HTTP/1.1 204 No Content"); - exit(""); - } - - public function renderDeleteSavedPhoto(int $owner, int $photoId): void - { - $this->assertUserLoggedIn(); - $this->willExecuteWriteAction(); - $this->assertNoCSRF(); - - $album = $this->albums->getUserSavedAlbum($this->user->identity); - $photo = $this->photos->getByOwnerAndVID($owner, $photoId); - if (!$photo) { - $this->notFound(); - } + $saved_photo = new Photo(); + $saved_photo->copyFrom($photo); + $saved_photo->setOwner($this->user->id); + $saved_photo->setCreated(time()); + $saved_photo->save(); - if ($album->hasPhoto($photo)) { - $album->removePhoto($photo); - } - $album->setEdited(time()); - $album->save(); + $album->addPhoto($saved_photo); - $this->redirect("/album" . $album->getPrettyId()); + header("HTTP/1.1 204 No Content"); + exit(""); } } diff --git a/Web/Presenters/templates/Photos/Photo.latte b/Web/Presenters/templates/Photos/Photo.latte index 98c9a8264..2bdd1a8f1 100644 --- a/Web/Presenters/templates/Photos/Photo.latte +++ b/Web/Presenters/templates/Photos/Photo.latte @@ -78,10 +78,6 @@ {_edit} {_delete} -
- - -
diff --git a/Web/routes.yml b/Web/routes.yml index cf4c4b654..9301b3ed7 100644 --- a/Web/routes.yml +++ b/Web/routes.yml @@ -183,8 +183,6 @@ routes: handler: "Photos->deletePhoto" - url: "/photo{num}_{num}/save" handler: "Photos->savePhoto" - - url: "/photo{num}_{num}/delete_saved" - handler: "Photos->deleteSavedPhoto" - url: "/al_avatars" handler: "User->setAvatar" - url: "/delete_avatar"