From 3e69d06474abb92a469d853944e0dbd3c2674f28 Mon Sep 17 00:00:00 2001 From: lalka2018 <99399973+lalka2016@users.noreply.github.com> Date: Tue, 13 Jun 2023 21:03:43 +0300 Subject: [PATCH] =?UTF-8?q?VKAPI:=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D1=8B?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=BE=D0=B4=D0=B0=D1=80=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2,=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=82=D0=BE=D0=BA,=20?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D1=82=D1=83=D1=81=D0=BE=D0=B2,=20=D0=BE?= =?UTF-8?q?=D0=B1=D1=81=D1=83=D0=B6=D0=B4=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=B8?= =?UTF-8?q?=20=D0=BD=D0=B5=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=84=D0=BE=D1=82=D0=BE=D0=BA=20=D0=B8=20=D0=B3=D1=80?= =?UTF-8?q?=D1=83=D0=BF=D0=BF=20(#876)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * API methods for gifts, notes, statuses * Some fixes Строки локализации у gifts.send теперь не костыльные и можно прикрепить до 10 аттачей к посту * Small imp Пофиксил пагинацию у заметков и подарок Перенёс структуру заметок Добавил аттачи к комментариям Добавил проверку на удалённость аттача Ну и пофиксил сортировку заметок * VKAPI: Some methods for topics and photos Добавлены методы для обсуждений (addTopic, closeTopic(), createComment(), deleteComment(), deleteTopic(), editTopic(), fixTopic(), getComments(), getTopics(), openTopic(), unfixTopic()) и для фотографий (createAlbum(), editAlbum(), getAlbums(), getAlbumsCount(), getById(), get(), deleteAlbum(), edit(), delete(), deleteComment(), createComment(), getAll(), getComments()) * fixsex --- VKAPI/Handlers/Board.php | 429 ++++++++++++++++++++++++ VKAPI/Handlers/Gifts.php | 174 ++++++++++ VKAPI/Handlers/Groups.php | 268 +++++++++++++++ VKAPI/Handlers/Notes.php | 271 ++++++++++++++++ VKAPI/Handlers/Photos.php | 505 ++++++++++++++++++++++++++++- VKAPI/Handlers/Status.php | 35 ++ VKAPI/Handlers/Wall.php | 126 +++++-- Web/Models/Entities/Album.php | 27 ++ Web/Models/Entities/Club.php | 39 ++- Web/Models/Entities/Comment.php | 33 ++ Web/Models/Entities/Note.php | 19 ++ Web/Models/Entities/Photo.php | 30 +- Web/Models/Entities/User.php | 17 + Web/Models/Entities/Video.php | 5 + Web/Models/Repositories/Albums.php | 10 + Web/Models/Repositories/Notes.php | 4 +- Web/Models/Repositories/Photos.php | 13 +- 17 files changed, 1958 insertions(+), 47 deletions(-) create mode 100644 VKAPI/Handlers/Board.php create mode 100644 VKAPI/Handlers/Gifts.php create mode 100644 VKAPI/Handlers/Notes.php create mode 100644 VKAPI/Handlers/Status.php diff --git a/VKAPI/Handlers/Board.php b/VKAPI/Handlers/Board.php new file mode 100644 index 00000000..3a441275 --- /dev/null +++ b/VKAPI/Handlers/Board.php @@ -0,0 +1,429 @@ +requireUser(); + $this->willExecuteWriteAction(); + + $club = (new ClubsRepo)->get($group_id); + + if(!$club) { + $this->fail(403, "Invalid club"); + } + + if(!$club->canBeModifiedBy($this->getUser()) && !$club->isEveryoneCanCreateTopics()) { + $this->fail(403, "Access to club denied"); + } + + $flags = 0; + + if($from_group == true && $club->canBeModifiedBy($this->getUser())) + $flags |= 0b10000000; + + $topic = new Topic; + $topic->setGroup($club->getId()); + $topic->setOwner($this->getUser()->getId()); + $topic->setTitle(ovk_proc_strtr($title, 127)); + $topic->setCreated(time()); + $topic->setFlags($flags); + $topic->save(); + + if(!empty($text)) { + $comment = new Comment; + $comment->setOwner($this->getUser()->getId()); + $comment->setModel(get_class($topic)); + $comment->setTarget($topic->getId()); + $comment->setContent($text); + $comment->setCreated(time()); + $comment->setFlags($flags); + $comment->save(); + + if(!empty($attachments)) { + $attachmentsArr = explode(",", $attachments); + # блин а мне это везде копировать типа + + if(sizeof($attachmentsArr) > 10) + $this->fail(50, "Error: too many attachments"); + + foreach($attachmentsArr as $attac) { + $attachmentType = NULL; + + if(str_contains($attac, "photo")) + $attachmentType = "photo"; + elseif(str_contains($attac, "video")) + $attachmentType = "video"; + else + $this->fail(205, "Unknown attachment type"); + + $attachment = str_replace($attachmentType, "", $attac); + + $attachmentOwner = (int)explode("_", $attachment)[0]; + $attachmentId = (int)end(explode("_", $attachment)); + + $attacc = NULL; + + if($attachmentType == "photo") { + $attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId); + if(!$attacc || $attacc->isDeleted()) + $this->fail(100, "Photo does not exists"); + if($attacc->getOwner()->getId() != $this->getUser()->getId()) + $this->fail(43, "You do not have access to this photo"); + + $comment->attach($attacc); + } elseif($attachmentType == "video") { + $attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId); + if(!$attacc || $attacc->isDeleted()) + $this->fail(100, "Video does not exists"); + if($attacc->getOwner()->getId() != $this->getUser()->getId()) + $this->fail(43, "You do not have access to this video"); + + $comment->attach($attacc); + } + + } + + } + + } + + return $topic->getId(); + } + + function closeTopic(int $group_id, int $topic_id) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $topic = (new TopicsRepo)->getTopicById($group_id, $topic_id); + + if(!$topic || !$topic->getClub() || !$topic->getClub()->canBeModifiedBy($this->getUser())) { + return 0; + } + + if(!$topic->isClosed()) { + $topic->setClosed(1); + $topic->save(); + } + + return 1; + } + + function createComment(int $group_id, int $topic_id, string $message = "", string $attachments = "", bool $from_group = true) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + if(empty($message) && empty($attachments)) { + $this->fail(100, "Required parameter 'message' missing."); + } + + $topic = (new TopicsRepo)->getTopicById($group_id, $topic_id); + + if(!$topic || $topic->isDeleted() || $topic->isClosed()) { + $this->fail(100, "Topic is deleted, closed or invalid."); + } + + $flags = 0; + + if($from_group != 0 && !is_null($topic->getClub()) && $topic->getClub()->canBeModifiedBy($this->user)) + $flags |= 0b10000000; + + if(strlen($message) > 300) { + $this->fail(20, "Comment is too long."); + } + + $comment = new Comment; + $comment->setOwner($this->getUser()->getId()); + $comment->setModel(get_class($topic)); + $comment->setTarget($topic->getId()); + $comment->setContent($message); + $comment->setCreated(time()); + $comment->setFlags($flags); + $comment->save(); + + if(!empty($attachments)) { + $attachmentsArr = explode(",", $attachments); + + if(sizeof($attachmentsArr) > 10) + $this->fail(50, "Error: too many attachments"); + + foreach($attachmentsArr as $attac) { + $attachmentType = NULL; + + if(str_contains($attac, "photo")) + $attachmentType = "photo"; + elseif(str_contains($attac, "video")) + $attachmentType = "video"; + else + $this->fail(205, "Unknown attachment type"); + + $attachment = str_replace($attachmentType, "", $attac); + + $attachmentOwner = (int)explode("_", $attachment)[0]; + $attachmentId = (int)end(explode("_", $attachment)); + + $attacc = NULL; + + if($attachmentType == "photo") { + $attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId); + if(!$attacc || $attacc->isDeleted()) + $this->fail(100, "Photo does not exists"); + if($attacc->getOwner()->getId() != $this->getUser()->getId()) + $this->fail(43, "You do not have access to this photo"); + + $comment->attach($attacc); + } elseif($attachmentType == "video") { + $attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId); + if(!$attacc || $attacc->isDeleted()) + $this->fail(100, "Video does not exists"); + if($attacc->getOwner()->getId() != $this->getUser()->getId()) + $this->fail(43, "You do not have access to this video"); + + $comment->attach($attacc); + } + } + } + + return $comment->getId(); + } + + function deleteComment(int $comment_id, int $group_id = 0, int $topic_id = 0) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $comment = (new CommentsRepo)->get($comment_id); + + if($comment->isDeleted() || !$comment || !$comment->canBeDeletedBy($this->getUser())) + $this->fail(403, "Access to comment denied"); + + $comment->delete(); + + return 1; + } + + function deleteTopic(int $group_id, int $topic_id) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $topic = (new TopicsRepo)->getTopicById($group_id, $topic_id); + + if(!$topic || !$topic->getClub() || $topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) { + return 0; + } + + $topic->deleteTopic(); + + return 1; + } + + function editComment(int $comment_id, int $group_id = 0, int $topic_id = 0, string $message, string $attachments) + { + /* + $this->requireUser(); + $this->willExecuteWriteAction(); + + $comment = (new CommentsRepo)->get($comment_id); + + if($comment->getOwner() != $this->getUser()->getId()) + $this->fail(15, "Access to comment denied"); + + $comment->setContent($message); + $comment->setEdited(time()); + $comment->save(); + */ + return 1; + } + + function editTopic(int $group_id, int $topic_id, string $title) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $topic = (new TopicsRepo)->getTopicById($group_id, $topic_id); + + if(!$topic || !$topic->getClub() || $topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) { + return 0; + } + + $topic->setTitle(ovk_proc_strtr($title, 127)); + + $topic->save(); + + return 1; + } + + function fixTopic(int $group_id, int $topic_id) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $topic = (new TopicsRepo)->getTopicById($group_id, $topic_id); + + if(!$topic || !$topic->getClub() || !$topic->getClub()->canBeModifiedBy($this->getUser())) { + return 0; + } + + $topic->setPinned(1); + + $topic->save(); + + return 1; + } + + function getComments(int $group_id, int $topic_id, bool $need_likes = false, int $start_comment_id = 0, int $offset = 0, int $count = 40, bool $extended = false, string $sort = "asc") + { + # start_comment_id ne robit + $this->requireUser(); + $this->willExecuteWriteAction(); + + $topic = (new TopicsRepo)->getTopicById($group_id, $topic_id); + + if(!$topic || !$topic->getClub() || $topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) { + $this->fail(5, "Invalid topic"); + } + + $arr = [ + "items" => [] + ]; + + $comms = array_slice(iterator_to_array($topic->getComments(1, $count + $offset)), $offset); + foreach($comms as $comm) { + $arr["items"][] = $this->getApiBoardComment($comm, $need_likes); + + if($extended) { + if($comm->getOwner() instanceof \openvk\Web\Models\Entities\User) { + $arr["profiles"][] = $comm->getOwner()->toVkApiStruct(); + } + + if($comm->getOwner() instanceof \openvk\Web\Models\Entities\Club) { + $arr["groups"][] = $comm->getOwner()->toVkApiStruct(); + } + } + } + + return $arr; + } + + function getTopics(int $group_id, string $topic_ids = "", int $order = 1, int $offset = 0, int $count = 40, bool $extended = false, int $preview = 0, int $preview_length = 90) + { + # order и extended ничё не делают + $this->requireUser(); + $this->willExecuteWriteAction(); + + $arr = []; + $club = (new ClubsRepo)->get($group_id); + $topics = array_slice(iterator_to_array((new TopicsRepo)->getClubTopics($club, 1, $count + $offset)), $offset); + $arr["count"] = (new TopicsRepo)->getClubTopicsCount($club); + $arr["items"] = []; + $arr["default_order"] = $order; + $arr["can_add_topics"] = $club->canBeModifiedBy($this->getUser()) ? true : $club->isEveryoneCanCreateTopics() ? true : false; + $arr["profiles"] = []; + + if(empty($topic_ids)) { + foreach($topics as $topic) { + if($topic->isDeleted()) continue; + $arr["items"][] = $topic->toVkApiStruct($preview, $preview_length); + } + } else { + $topics = explode(',', $topic_ids); + + foreach($topics as $topic) { + $id = explode("_", $topic); + $topicy = (new TopicsRepo)->getTopicById((int)$id[0], (int)$id[1]); + if($topicy) { + $arr["items"] = $topicy->toVkApiStruct(); + } + } + } + + return $arr; + } + + function openTopic(int $group_id, int $topic_id) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $topic = (new TopicsRepo)->getTopicById($group_id, $topic_id); + + if(!$topic || !$topic->getClub() || !$topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) { + return 0; + } + + if($topic->isClosed()) { + $topic->setClosed(0); + $topic->save(); + } + + return 1; + } + + function restoreComment(int $group_id, int $topic_id, int $comment_id) + { + $this->fail(501, "Not implemented"); + } + + function unfixTopic(int $group_id, int $topic_id) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $topic = (new TopicsRepo)->getTopicById($group_id, $topic_id); + + if(!$topic || !$topic->getClub() || !$topic->getClub()->canBeModifiedBy($this->getUser())) { + return 0; + } + + if($topic->isPinned()) { + $topic->setClosed(0); + $topic->save(); + } + + $topic->setPinned(0); + + $topic->save(); + + return 1; + } + + private function getApiBoardComment(?Comment $comment, bool $need_likes = false) + { + $res = (object) []; + + $res->id = $comment->getId(); + $res->from_id = $comment->getOwner()->getId(); + $res->date = $comment->getPublicationTime()->timestamp(); + $res->text = $comment->getText(); + $res->attachments = []; + $res->likes = []; + if($need_likes) { + $res->likes = [ + "count" => $comment->getLikesCount(), + "user_likes" => (int) $comment->hasLikeFrom($this->getUser()), + "can_like" => 1 # а чё типо не может ахахаххахах + ]; + } + + foreach($comment->getChildren() as $attachment) { + if($attachment->isDeleted()) + continue; + + $res->attachments[] = $attachment->toVkApiStruct(); + } + + return $res; + } +} \ No newline at end of file diff --git a/VKAPI/Handlers/Gifts.php b/VKAPI/Handlers/Gifts.php new file mode 100644 index 00000000..2702924d --- /dev/null +++ b/VKAPI/Handlers/Gifts.php @@ -0,0 +1,174 @@ +requireUser(); + + $i = 0; + + $i += $offset; + + $user = (new UsersRepo)->get($user_id); + + if(!$user || $user->isDeleted()) + $this->fail(177, "Invalid user"); + + $gift_item = []; + + $userGifts = array_slice(iterator_to_array($user->getGifts(1, $count, false)), $offset); + + if(sizeof($userGifts) < 0) { + return NULL; + } + + foreach($userGifts as $gift) { + if($i < $count) { + $gift_item[] = [ + "id" => $i, + "from_id" => $gift->anon == true ? 0 : $gift->sender->getId(), + "message" => $gift->caption == NULL ? "" : $gift->caption, + "date" => $gift->sent->timestamp(), + "gift" => [ + "id" => $gift->gift->getId(), + "thumb_256" => $gift->gift->getImage(2), + "thumb_96" => $gift->gift->getImage(2), + "thumb_48" => $gift->gift->getImage(2) + ], + "privacy" => 0 + ]; + } + $i+=1; + } + + return $gift_item; + } + + function send(int $user_ids, int $gift_id, string $message = "", int $privacy = 0) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $user = (new UsersRepo)->get((int) $user_ids); + + if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce']) + $this->fail(105, "Commerce is disabled on this instance"); + + if(!$user || $user->isDeleted()) + $this->fail(177, "Invalid user"); + + $gift = (new GiftsRepo)->get($gift_id); + + if(!$gift) + $this->fail(165, "Invalid gift"); + + $price = $gift->getPrice(); + $coinsLeft = $this->getUser()->getCoins() - $price; + + if(!$gift->canUse($this->getUser())) + return (object) + [ + "success" => 0, + "user_ids" => $user_ids, + "error" => "You don't have any more of these gifts." + ]; + + if($coinsLeft < 0) + return (object) + [ + "success" => 0, + "user_ids" => $user_ids, + "error" => "You don't have enough voices." + ]; + + $user->gift($this->getUser(), $gift, $message); + $gift->used(); + + $this->getUser()->setCoins($coinsLeft); + $this->getUser()->save(); + + $notification = new GiftNotification($user, $this->getUser(), $gift, $message); + $notification->emit(); + + return (object) + [ + "success" => 1, + "user_ids" => $user_ids, + "withdraw_votes" => $price + ]; + } + + function delete() + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $this->fail(501, "Not implemented"); + } + + # этих методов не было в ВК, но я их добавил чтобы можно было отобразить список подарков + function getCategories(bool $extended = false, int $page = 1) + { + $cats = (new GiftsRepo)->getCategories($page); + $categ = []; + $i = 0; + + if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce']) + $this->fail(105, "Commerce is disabled on this instance"); + + foreach($cats as $cat) { + $categ[$i] = [ + "name" => $cat->getName(), + "description" => $cat->getDescription(), + "id" => $cat->getId(), + "thumbnail" => $cat->getThumbnailURL(), + ]; + + if($extended == true) { + $categ[$i]["localizations"] = []; + foreach(getLanguages() as $lang) { + $code = $lang["code"]; + $categ[$i]["localizations"][$code] = + [ + "name" => $cat->getName($code), + "desc" => $cat->getDescription($code), + ]; + } + } + $i++; + } + + return $categ; + } + + function getGiftsInCategory(int $id, int $page = 1) + { + $this->requireUser(); + + if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce']) + $this->fail(105, "Commerce is disabled on this instance"); + + if(!(new GiftsRepo)->getCat($id)) + $this->fail(177, "Category not found"); + + $giftz = ((new GiftsRepo)->getCat($id))->getGifts($page); + $gifts = []; + + foreach($giftz as $gift) { + $gifts[] = [ + "name" => $gift->getName(), + "image" => $gift->getImage(2), + "usages_left" => (int)$gift->getUsagesLeft($this->getUser()), + "price" => $gift->getPrice(), # голосов + "is_free" => $gift->isFree() + ]; + } + + return $gifts; + } +} diff --git a/VKAPI/Handlers/Groups.php b/VKAPI/Handlers/Groups.php index 071ded81..e21c8a09 100644 --- a/VKAPI/Handlers/Groups.php +++ b/VKAPI/Handlers/Groups.php @@ -2,6 +2,7 @@ namespace openvk\VKAPI\Handlers; use openvk\Web\Models\Repositories\Clubs as ClubsRepo; use openvk\Web\Models\Repositories\Users as UsersRepo; +use openvk\Web\Models\Entities\Club; final class Groups extends VKAPIRequestHandler { @@ -263,4 +264,271 @@ final class Groups extends VKAPIRequestHandler return 1; } + + function create(string $title, string $description = "", string $type = "group", int $public_category = 1, int $public_subcategory = 1, int $subtype = 1) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $club = new Club; + + $club->setName($title); + $club->setAbout($description); + $club->setOwner($this->getUser()->getId()); + $club->save(); + + $club->toggleSubscription($this->getUser()); + + return $this->getById((string)$club->getId()); + } + + function edit( + int $group_id, + string $title = NULL, + string $description = NULL, + string $screen_name = NULL, + string $website = NULL, + int $wall = NULL, + int $topics = NULL, + int $adminlist = NULL, + int $topicsAboveWall = NULL, + int $hideFromGlobalFeed = NULL) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $club = (new ClubsRepo)->get($group_id); + + if(!$club) $this->fail(203, "Club not found"); + if(!$club || !$club->canBeModifiedBy($this->getUser())) $this->fail(15, "You can't modify this group."); + if(!empty($screen_name) && !$club->setShortcode($screen_name)) $this->fail(103, "Invalid shortcode."); + + !is_null($title) ? $club->setName($title) : NULL; + !is_null($description) ? $club->setAbout($description) : NULL; + !is_null($screen_name) ? $club->setShortcode($screen_name) : NULL; + !is_null($website) ? $club->setWebsite((!parse_url($website, PHP_URL_SCHEME) ? "https://" : "") . $website) : NULL; + !is_null($wall) ? $club->setWall($wall) : NULL; + !is_null($topics) ? $club->setEveryone_Can_Create_Topics($topics) : NULL; + !is_null($adminlist) ? $club->setAdministrators_List_Display($adminlist) : NULL; + !is_null($topicsAboveWall) ? $club->setDisplay_Topics_Above_Wall($topicsAboveWall) : NULL; + !is_null($hideFromGlobalFeed) ? $club->setHide_From_Global_Feed($hideFromGlobalFeed) : NULL; + + $club->save(); + + return 1; + } + + function getMembers(string $group_id, string $sort = "id_asc", int $offset = 0, int $count = 100, string $fields = "", string $filter = "any") + { + # bdate,can_post,can_see_all_posts,can_see_audio,can_write_private_message,city,common_count,connections,contacts,country,domain,education,has_mobile,last_seen,lists,online,online_mobile,photo_100,photo_200,photo_200_orig,photo_400_orig,photo_50,photo_max,photo_max_orig,relation,relatives,schools,sex,site,status,universities + $club = (new ClubsRepo)->get((int) $group_id); + if(!$club) + $this->fail(125, "Invalid group id"); + + $sorter = "follower ASC"; + + switch($sort) { + default: + case "time_asc": + case "id_asc": + $sorter = "follower ASC"; + break; + case "time_desc": + case "id_desc": + $sorter = "follower DESC"; + break; + } + + $members = array_slice(iterator_to_array($club->getFollowers(1, $count, $sorter)), $offset); + $arr = (object) [ + "count" => count($members), + "items" => array()]; + + $filds = explode(",", $fields); + + $i = 0; + foreach($members as $member) { + if($i > $count) { + break; + } + + $arr->items[] = (object) [ + "id" => $member->getId(), + "name" => $member->getCanonicalName(), + ]; + + foreach($filds as $fild) { + switch($fild) { + case "bdate": + $arr->items[$i]->bdate = $member->getBirthday()->format('%e.%m.%Y'); + break; + case "can_post": + $arr->items[$i]->can_post = $club->canBeModifiedBy($member); + break; + case "can_see_all_posts": + $arr->items[$i]->can_see_all_posts = 1; + break; + case "can_see_audio": + $arr->items[$i]->can_see_audio = 0; + break; + case "can_write_private_message": + $arr->items[$i]->can_write_private_message = 0; + break; + case "common_count": + $arr->items[$i]->common_count = 420; + break; + case "connections": + $arr->items[$i]->connections = 1; + break; + case "contacts": + $arr->items[$i]->contacts = $member->getContactEmail(); + break; + case "country": + $arr->items[$i]->country = 1; + break; + case "domain": + $arr->items[$i]->domain = ""; + break; + case "education": + $arr->items[$i]->education = ""; + break; + case "has_mobile": + $arr->items[$i]->has_mobile = false; + break; + case "last_seen": + $arr->items[$i]->last_seen = $member->getOnline()->timestamp(); + break; + case "lists": + $arr->items[$i]->lists = ""; + break; + case "online": + $arr->items[$i]->online = $member->isOnline(); + break; + case "online_mobile": + $arr->items[$i]->online_mobile = $member->getOnlinePlatform() == "android" || $member->getOnlinePlatform() == "iphone" || $member->getOnlinePlatform() == "mobile"; + break; + case "photo_100": + $arr->items[$i]->photo_100 = $member->getAvatarURL("tiny"); + break; + case "photo_200": + $arr->items[$i]->photo_200 = $member->getAvatarURL("normal"); + break; + case "photo_200_orig": + $arr->items[$i]->photo_200_orig = $member->getAvatarURL("normal"); + break; + case "photo_400_orig": + $arr->items[$i]->photo_400_orig = $member->getAvatarURL("normal"); + break; + case "photo_max": + $arr->items[$i]->photo_max = $member->getAvatarURL("original"); + break; + case "photo_max_orig": + $arr->items[$i]->photo_max_orig = $member->getAvatarURL(); + break; + case "relation": + $arr->items[$i]->relation = $member->getMaritalStatus(); + break; + case "relatives": + $arr->items[$i]->relatives = 0; + break; + case "schools": + $arr->items[$i]->schools = 0; + break; + case "sex": + $arr->items[$i]->sex = $member->isFemale() ? 1 : 2; + break; + case "site": + $arr->items[$i]->site = $member->getWebsite(); + break; + case "status": + $arr->items[$i]->status = $member->getStatus(); + break; + case "universities": + $arr->items[$i]->universities = 0; + break; + } + } + $i++; + } + return $arr; + } + + function getSettings(string $group_id) + { + $this->requireUser(); + $club = (new ClubsRepo)->get((int)$group_id); + + if(!$club || !$club->canBeModifiedBy($this->getUser())) + $this->fail(15, "You can't get settings of this group."); + + $arr = (object) [ + "title" => $club->getName(), + "description" => $club->getDescription() != NULL ? $club->getDescription() : "", + "address" => $club->getShortcode(), + "wall" => $club->canPost() == true ? 1 : 0, + "photos" => 1, + "video" => 0, + "audio" => 0, + "docs" => 0, + "topics" => $club->isEveryoneCanCreateTopics() == true ? 1 : 0, + "wiki" => 0, + "messages" => 0, + "obscene_filter" => 0, + "obscene_stopwords" => 0, + "obscene_words" => "", + "access" => 1, + "subject" => 1, + "subject_list" => [ + 0 => "в", + 1 => "опенвк", + 2 => "нет", + 3 => "категорий", + 4 => "групп", + ], + "rss" => "/club".$club->getId()."/rss", + "website" => $club->getWebsite(), + "age_limits" => 0, + "market" => [], + ]; + + return $arr; + } + + function isMember(string $group_id, int $user_id, string $user_ids = "", bool $extended = false) + { + $this->requireUser(); + $id = $user_id != NULL ? $user_id : explode(",", $user_ids); + + if($group_id < 0) + $this->fail(228, "Remove the minus from group_id"); + + $club = (new ClubsRepo)->get((int)$group_id); + $usver = (new UsersRepo)->get((int)$id); + + if(!$club || $group_id == 0) + $this->fail(203, "Invalid club"); + + if(!$usver || $usver->isDeleted() || $user_id == 0) + $this->fail(30, "Invalid user"); + + if($extended == false) { + return $club->getSubscriptionStatus($usver) ? 1 : 0; + } else { + return (object) + [ + "member" => $club->getSubscriptionStatus($usver) ? 1 : 0, + "request" => 0, + "invitation" => 0, + "can_invite" => 0, + "can_recall" => 0 + ]; + } + } + + function remove(int $group_id, int $user_id) + { + $this->requireUser(); + + $this->fail(501, "Not implemented"); + } } diff --git a/VKAPI/Handlers/Notes.php b/VKAPI/Handlers/Notes.php new file mode 100644 index 00000000..d3dc3468 --- /dev/null +++ b/VKAPI/Handlers/Notes.php @@ -0,0 +1,271 @@ +requireUser(); + $this->willExecuteWriteAction(); + + $note = new Note; + $note->setOwner($this->getUser()->getId()); + $note->setCreated(time()); + $note->setName($title); + $note->setSource($text); + $note->setEdited(time()); + $note->save(); + + return $note->getVirtualId(); + } + + function createComment(string $note_id, int $owner_id, string $message, int $reply_to = 0, string $attachments = "") + { + $this->requireUser(); + $this->willExecuteWriteAction(); + $note = (new NotesRepo)->getNoteById((int)$owner_id, (int)$note_id); + + if(!$note) + $this->fail(180, "Note not found"); + + if($note->isDeleted()) + $this->fail(189, "Note is deleted"); + + if($note->getOwner()->isDeleted()) + $this->fail(403, "Owner is deleted"); + + if(empty($message) && empty($attachments)) + $this->fail(100, "Required parameter 'message' missing."); + + $comment = new Comment; + $comment->setOwner($this->getUser()->getId()); + $comment->setModel(get_class($note)); + $comment->setTarget($note->getId()); + $comment->setContent($message); + $comment->setCreated(time()); + $comment->save(); + + if(!empty($attachments)) { + $attachmentsArr = explode(",", $attachments); + + if(sizeof($attachmentsArr) > 10) + $this->fail(50, "Error: too many attachments"); + + foreach($attachmentsArr as $attac) { + $attachmentType = NULL; + + if(str_contains($attac, "photo")) + $attachmentType = "photo"; + elseif(str_contains($attac, "video")) + $attachmentType = "video"; + else + $this->fail(205, "Unknown attachment type"); + + $attachment = str_replace($attachmentType, "", $attac); + + $attachmentOwner = (int)explode("_", $attachment)[0]; + $attachmentId = (int)end(explode("_", $attachment)); + + $attacc = NULL; + + if($attachmentType == "photo") { + $attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId); + if(!$attacc || $attacc->isDeleted()) + $this->fail(100, "Photo does not exists"); + if($attacc->getOwner()->getId() != $this->getUser()->getId()) + $this->fail(43, "You do not have access to this photo"); + + $comment->attach($attacc); + } elseif($attachmentType == "video") { + $attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId); + if(!$attacc || $attacc->isDeleted()) + $this->fail(100, "Video does not exists"); + if($attacc->getOwner()->getId() != $this->getUser()->getId()) + $this->fail(43, "You do not have access to this video"); + + $comment->attach($attacc); + } + } + } + + return $comment->getId(); + } + + function delete(string $note_id) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $note = (new NotesRepo)->get((int)$note_id); + + if(!$note) + $this->fail(180, "Note not found"); + + if(!$note->canBeModifiedBy($this->getUser())) + $this->fail(15, "Access to note denied"); + + $note->delete(); + + return 1; + } + + function deleteComment(int $comment_id, int $owner_id = 0) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $comment = (new CommentsRepo)->get($comment_id); + + if(!$comment || !$comment->canBeDeletedBy($this->getUser())) + $this->fail(403, "Access to comment denied"); + + $comment->delete(); + + return 1; + } + + function edit(string $note_id, string $title = "", string $text = "", int $privacy = 0, int $comment_privacy = 0, string $privacy_view = "", string $privacy_comment = "") + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $note = (new NotesRepo)->getNoteById($this->getUser()->getId(), (int)$note_id); + + if(!$note) + $this->fail(180, "Note not found"); + + if($note->isDeleted()) + $this->fail(189, "Note is deleted"); + + if(!$note->canBeModifiedBy($this->getUser())) + $this->fail(403, "No access"); + + !empty($title) ? $note->setName($title) : NULL; + !empty($text) ? $note->setSource($text) : NULL; + + $note->setCached_Content(NULL); + $note->setEdited(time()); + $note->save(); + + return 1; + } + + function editComment(int $comment_id, string $message, int $owner_id = NULL) + { + /* + $this->requireUser(); + $this->willExecuteWriteAction(); + + $comment = (new CommentsRepo)->get($comment_id); + + if($comment->getOwner() != $this->getUser()->getId()) + $this->fail(15, "Access to comment denied"); + + $comment->setContent($message); + $comment->setEdited(time()); + $comment->save(); + */ + + return 1; + } + + function get(int $user_id, string $note_ids = "", int $offset = 0, int $count = 10, int $sort = 0) + { + $this->requireUser(); + $user = (new UsersRepo)->get($user_id); + + if(!$user || $user->isDeleted()) + $this->fail(15, "Invalid user"); + + if(empty($note_ids)) { + $notes = array_slice(iterator_to_array((new NotesRepo)->getUserNotes($user, 1, $count + $offset, $sort == 0 ? "ASC" : "DESC")), $offset); + $nodez = (object) [ + "count" => (new NotesRepo)->getUserNotesCount((new UsersRepo)->get($user_id)), + "notes" => [] + ]; + + foreach($notes as $note) { + if($note->isDeleted()) continue; + + $nodez->notes[] = $note->toVkApiStruct(); + } + } else { + $notes = explode(',', $note_ids); + + foreach($notes as $note) + { + $id = explode("_", $note); + + $items = []; + + $note = (new NotesRepo)->getNoteById((int)$id[0], (int)$id[1]); + if($note) { + $nodez->notes[] = $note->toVkApiStruct(); + } + } + } + + return $nodez; + } + + function getById(int $note_id, int $owner_id, bool $need_wiki = false) + { + $this->requireUser(); + + $note = (new NotesRepo)->getNoteById($owner_id, $note_id); + + if(!$note) + $this->fail(180, "Note not found"); + + if($note->isDeleted()) + $this->fail(189, "Note is deleted"); + + if(!$note->getOwner() || $note->getOwner()->isDeleted()) + $this->fail(177, "Owner does not exists"); + + return $note->toVkApiStruct(); + } + + function getComments(int $note_id, int $owner_id, int $sort = 1, int $offset = 0, int $count = 100) + { + $this->requireUser(); + + $note = (new NotesRepo)->getNoteById($owner_id, $note_id); + + if(!$note) + $this->fail(180, "Note not found"); + + if($note->isDeleted()) + $this->fail(189, "Note is deleted"); + + if(!$note->getOwner()) + $this->fail(177, "Owner does not exists"); + + $arr = (object) [ + "count" => $note->getCommentsCount(), + "comments" => []]; + $comments = array_slice(iterator_to_array($note->getComments(1, $count + $offset)), $offset); + + foreach($comments as $comment) { + $arr->comments[] = $comment->toVkApiStruct($this->getUser(), false, false, $note); + } + + return $arr; + } + + function getFriendsNotes(int $offset = 0, int $count = 0) + { + $this->fail(501, "Not implemented"); + } + + function restoreComment(int $comment_id = 0, int $owner_id = 0) + { + $this->fail(501, "Not implemented"); + } +} diff --git a/VKAPI/Handlers/Photos.php b/VKAPI/Handlers/Photos.php index 9a02dd8c..4ab56832 100644 --- a/VKAPI/Handlers/Photos.php +++ b/VKAPI/Handlers/Photos.php @@ -3,9 +3,12 @@ namespace openvk\VKAPI\Handlers; use Nette\InvalidStateException; use Nette\Utils\ImageException; -use openvk\Web\Models\Entities\Photo; +use openvk\Web\Models\Entities\{Photo, Album, Comment}; use openvk\Web\Models\Repositories\Albums; +use openvk\Web\Models\Repositories\Photos as PhotosRepo; use openvk\Web\Models\Repositories\Clubs; +use openvk\Web\Models\Repositories\Users as UsersRepo; +use openvk\Web\Models\Repositories\Comments as CommentsRepo; final class Photos extends VKAPIRequestHandler { @@ -227,4 +230,504 @@ final class Photos extends VKAPIRequestHandler "items" => $images, ]; } + + function createAlbum(string $title, int $group_id = 0, string $description = "", int $privacy = 0) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + if($group_id != 0) { + $club = (new Clubs)->get((int) $group_id); + + if(!$club || !$club->canBeModifiedBy($this->getUser())) { + $this->fail(20, "Invalid club"); + } + } + + $album = new Album; + $album->setOwner(isset($club) ? $club->getId() * -1 : $this->getUser()->getId()); + $album->setName($title); + $album->setDescription($description); + $album->setCreated(time()); + $album->save(); + + return $album->toVkApiStruct($this->getUser()); + } + + function editAlbum(int $album_id, int $owner_id, string $title, string $description = "", int $privacy = 0) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $album = (new Albums)->getAlbumByOwnerAndId($owner_id, $album_id); + + if(!$album || $album->isDeleted()) { + $this->fail(2, "Invalid album"); + } + + if(empty($title)) { + $this->fail(25, "Title is empty"); + } + + if($album->isCreatedBySystem()) { + $this->fail(40, "You can't change system album"); + } + + if(!$album->canBeModifiedBy($this->getUser())) { + $this->fail(2, "Access to album denied"); + } + + $album->setName($title); + $album->setDescription($description); + + $album->save(); + + return $album->toVkApiStruct($this->getUser()); + } + + function getAlbums(int $owner_id, string $album_ids = "", int $offset = 0, int $count = 100, bool $need_system = true, bool $need_covers = true, bool $photo_sizes = false) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $res = []; + + if(empty($album_ids)) { + if($owner_id > 0) { + $user = (new UsersRepo)->get($owner_id); + + $res = [ + "count" => (new Albums)->getUserAlbumsCount($user), + "items" => [] + ]; + + if(!$user || $user->isDeleted()) + $this->fail(2, "Invalid user"); + + + if(!$user->getPrivacyPermission('photos.read', $this->getUser())) + $this->fail(21, "This user chose to hide his albums."); + + $albums = array_slice(iterator_to_array((new Albums)->getUserAlbums($user, 1, $count + $offset)), $offset); + + foreach($albums as $album) { + if(!$need_system && $album->isCreatedBySystem()) continue; + $res["items"][] = $album->toVkApiStruct($this->getUser(), $need_covers, $photo_sizes); + } + } + + else { + $club = (new Clubs)->get($owner_id * -1); + + $res = [ + "count" => (new Albums)->getClubAlbumsCount($club), + "items" => [] + ]; + + if(!$club) + $this->fail(2, "Invalid club"); + + $albums = array_slice(iterator_to_array((new Albums)->getClubAlbums($club, 1, $count + $offset)), $offset); + + foreach($albums as $album) { + if(!$need_system && $album->isCreatedBySystem()) continue; + $res["items"][] = $album->toVkApiStruct($this->getUser(), $need_covers, $photo_sizes); + } + } + + } else { + $albums = explode(',', $album_ids); + + $res = [ + "count" => sizeof($albums), + "items" => [] + ]; + + foreach($albums as $album) + { + $id = explode("_", $album); + + $album = (new Albums)->getAlbumByOwnerAndId((int)$id[0], (int)$id[1]); + if($album && !$album->isDeleted()) { + if(!$need_system && $album->isCreatedBySystem()) continue; + $res["items"][] = $album->toVkApiStruct($this->getUser(), $need_covers, $photo_sizes); + } + } + } + + return $res; + } + + function getAlbumsCount(int $user_id = 0, int $group_id = 0) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + if($user_id == 0 && $group_id == 0 || $user_id > 0 && $group_id > 0) { + $this->fail(21, "Select user_id or group_id"); + } + + if($user_id > 0) { + + $us = (new UsersRepo)->get($user_id); + if(!$us || $us->isDeleted()) { + $this->fail(21, "Invalid user"); + } + + if(!$us->getPrivacyPermission('photos.read', $this->getUser())) { + $this->fail(21, "This user chose to hide his albums."); + } + + return (new Albums)->getUserAlbumsCount($us); + } + + if($group_id > 0) + { + $cl = (new Clubs)->get($group_id); + if(!$cl) { + $this->fail(21, "Invalid club"); + } + + return (new Albums)->getClubAlbumsCount($cl); + } + } + + function getById(string $photos, bool $extended = false, bool $photo_sizes = false) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $phts = explode(",", $photos); + $res = []; + + foreach($phts as $phota) { + $ph = explode("_", $phota); + $photo = (new PhotosRepo)->getByOwnerAndVID((int)$ph[0], (int)$ph[1]); + + if(!$photo || $photo->isDeleted()) { + $this->fail(21, "Invalid photo"); + } + + if($photo->getOwner()->isDeleted()) { + $this->fail(21, "Owner of this photo is deleted"); + } + + if(!$photo->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) { + $this->fail(21, "This user chose to hide his photos."); + } + + $res[] = $photo->toVkApiStruct($photo_sizes, $extended); + } + + return $res; + } + + function get(int $owner_id, int $album_id, string $photo_ids = "", bool $extended = false, bool $photo_sizes = false, int $offset = 0, int $count = 10) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $res = []; + + if(empty($photo_ids)) { + $album = (new Albums)->getAlbumByOwnerAndId($owner_id, $album_id); + + if(!$album->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) { + $this->fail(21, "This user chose to hide his albums."); + } + + if(!$album || $album->isDeleted()) { + $this->fail(21, "Invalid album"); + } + + $photos = array_slice(iterator_to_array($album->getPhotos(1, $count + $offset)), $offset); + $res["count"] = sizeof($photos); + + foreach($photos as $photo) { + if(!$photo || $photo->isDeleted()) continue; + $res["items"][] = $photo->toVkApiStruct($photo_sizes, $extended); + } + + } else { + $photos = explode(',', $photo_ids); + + $res = [ + "count" => sizeof($photos), + "items" => [] + ]; + + foreach($photos as $photo) + { + $id = explode("_", $photo); + + $phot = (new PhotosRepo)->getByOwnerAndVID((int)$id[0], (int)$id[1]); + if($phot && !$phot->isDeleted()) { + $res["items"][] = $phot->toVkApiStruct($photo_sizes, $extended); + } + } + } + + return $res; + } + + function deleteAlbum(int $album_id, int $group_id = 0) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $album = (new Albums)->get($album_id); + + if(!$album || $album->canBeModifiedBy($this->getUser())) { + $this->fail(21, "Invalid album"); + } + + if($album->isDeleted()) { + $this->fail(22, "Album already deleted"); + } + + $album->delete(); + + return 1; + } + + function edit(int $owner_id, int $photo_id, string $caption = "") + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id); + + if(!$photo) { + $this->fail(21, "Invalid photo"); + } + + if($photo->isDeleted()) { + $this->fail(21, "Photo is deleted"); + } + + if(!empty($caption)) { + $photo->setDescription($caption); + $photo->save(); + } + + return 1; + } + + function delete(int $owner_id, int $photo_id, string $photos = "") + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + if(empty($photos)) { + $photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id); + + if($this->getUser()->getId() !== $photo->getOwner()->getId()) { + $this->fail(21, "You can't delete another's photo"); + } + + if(!$photo) { + $this->fail(21, "Invalid photo"); + } + + if($photo->isDeleted()) { + $this->fail(21, "Photo already deleted"); + } + + $photo->delete(); + } else { + $photozs = explode(',', $photos); + + foreach($photozs as $photo) + { + $id = explode("_", $photo); + + $phot = (new PhotosRepo)->getByOwnerAndVID((int)$id[0], (int)$id[1]); + + if($this->getUser()->getId() !== $phot->getOwner()->getId()) { + $this->fail(21, "You can't delete another's photo"); + } + + if(!$phot) { + $this->fail(21, "Invalid photo"); + } + + if($phot->isDeleted()) { + $this->fail(21, "Photo already deleted"); + } + + $phot->delete(); + } + } + + return 1; + } + + function getAllComments(int $owner_id, int $album_id, bool $need_likes = false, int $offset = 0, int $count = 100) + { + $this->fail(501, "Not implemented"); + } + + function deleteComment(int $comment_id, int $owner_id = 0) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $comment = (new CommentsRepo)->get($comment_id); + if(!$comment) { + $this->fail(21, "Invalid comment"); + } + + if(!$comment->canBeModifiedBy($this->getUser())) { + $this->fail(21, "Forbidden"); + } + + if($comment->isDeleted()) { + $this->fail(4, "Comment already deleted"); + } + + $comment->delete(); + + return 1; + } + + function createComment(int $owner_id, int $photo_id, string $message = "", string $attachments = "", bool $from_group = false) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + if(empty($message) && empty($attachments)) { + $this->fail(100, "Required parameter 'message' missing."); + } + + $photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id); + + if(!$photo->getAlbum()->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) { + $this->fail(21, "This user chose to hide his albums."); + } + + if(!$photo) + $this->fail(180, "Photo not found"); + if($photo->isDeleted()) + $this->fail(189, "Photo is deleted"); + + $comment = new Comment; + $comment->setOwner($this->getUser()->getId()); + $comment->setModel(get_class($photo)); + $comment->setTarget($photo->getId()); + $comment->setContent($message); + $comment->setCreated(time()); + $comment->save(); + + if(!empty($attachments)) { + $attachmentsArr = explode(",", $attachments); + + if(sizeof($attachmentsArr) > 10) + $this->fail(50, "Error: too many attachments"); + + foreach($attachmentsArr as $attac) { + $attachmentType = NULL; + + if(str_contains($attac, "photo")) + $attachmentType = "photo"; + elseif(str_contains($attac, "video")) + $attachmentType = "video"; + else + $this->fail(205, "Unknown attachment type"); + + $attachment = str_replace($attachmentType, "", $attac); + + $attachmentOwner = (int)explode("_", $attachment)[0]; + $attachmentId = (int)end(explode("_", $attachment)); + + $attacc = NULL; + + if($attachmentType == "photo") { + $attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId); + if(!$attacc || $attacc->isDeleted()) + $this->fail(100, "Photo does not exists"); + if($attacc->getOwner()->getId() != $this->getUser()->getId()) + $this->fail(43, "You do not have access to this photo"); + + $comment->attach($attacc); + } elseif($attachmentType == "video") { + $attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId); + if(!$attacc || $attacc->isDeleted()) + $this->fail(100, "Video does not exists"); + if($attacc->getOwner()->getId() != $this->getUser()->getId()) + $this->fail(43, "You do not have access to this video"); + + $comment->attach($attacc); + } + } + } + + return $comment->getId(); + } + + function getAll(int $owner_id, bool $extended = false, int $offset = 0, int $count = 100, bool $photo_sizes = false) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + if($owner_id < 0) { + $this->fail(4, "This method doesn't works with clubs"); + } + + $user = (new UsersRepo)->get($owner_id); + + if(!$user) { + $this->fail(4, "Invalid user"); + } + + if(!$user->getPrivacyPermission('photos.read', $this->getUser())) { + $this->fail(21, "This user chose to hide his albums."); + } + + $photos = array_slice(iterator_to_array((new PhotosRepo)->getEveryUserPhoto($user, 1, $count + $offset)), $offset); + $res = []; + + foreach($photos as $photo) { + if(!$photo || $photo->isDeleted()) continue; + $res["items"][] = $photo->toVkApiStruct($photo_sizes, $extended); + } + + return $res; + } + + function getComments(int $owner_id, int $photo_id, bool $need_likes = false, int $offset = 0, int $count = 100, bool $extended = false, string $fields = "") + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + $photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id); + $comms = array_slice(iterator_to_array($photo->getComments(1, $offset + $count)), $offset); + + if(!$photo) { + $this->fail(4, "Invalid photo"); + } + + if(!$photo->getAlbum()->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) { + $this->fail(21, "This user chose to hide his photos."); + } + + if($photo->isDeleted()) { + $this->fail(4, "Photo is deleted"); + } + + $res = [ + "count" => sizeof($comms), + "items" => [] + ]; + + foreach($comms as $comment) { + $res["items"][] = $comment->toVkApiStruct($this->getUser(), $need_likes, $extended); + if($extended) { + if($comment->getOwner() instanceof \openvk\Web\Models\Entities\User) { + $res["profiles"][] = $comment->getOwner()->toVkApiStruct(); + } + } + } + + return $res; + } } \ No newline at end of file diff --git a/VKAPI/Handlers/Status.php b/VKAPI/Handlers/Status.php new file mode 100644 index 00000000..843f42bd --- /dev/null +++ b/VKAPI/Handlers/Status.php @@ -0,0 +1,35 @@ +requireUser(); + if($user_id == 0 && $group_id == 0) { + return $this->getUser()->getStatus(); + } else { + if($group_id > 0) + $this->fail(501, "Group statuses are not implemented"); + else + return (new UsersRepo)->get($user_id)->getStatus(); + } + } + + function set(string $text, int $group_id = 0) + { + $this->requireUser(); + $this->willExecuteWriteAction(); + + if($group_id > 0) { + $this->fail(501, "Group statuses are not implemented"); + } else { + $this->getUser()->setStatus($text); + $this->getUser()->save(); + + return 1; + } + } +} diff --git a/VKAPI/Handlers/Wall.php b/VKAPI/Handlers/Wall.php index dcf63a26..ee07f3c1 100644 --- a/VKAPI/Handlers/Wall.php +++ b/VKAPI/Handlers/Wall.php @@ -9,6 +9,10 @@ use openvk\Web\Models\Entities\Post; use openvk\Web\Models\Repositories\Posts as PostsRepo; use openvk\Web\Models\Entities\Comment; use openvk\Web\Models\Repositories\Comments as CommentsRepo; +use openvk\Web\Models\Entities\Photo; +use openvk\Web\Models\Repositories\Photos as PhotosRepo; +use openvk\Web\Models\Entities\Video; +use openvk\Web\Models\Repositories\Videos as VideosRepo; final class Wall extends VKAPIRequestHandler { @@ -367,7 +371,7 @@ final class Wall extends VKAPIRequestHandler ]; } - function post(string $owner_id, string $message = "", int $from_group = 0, int $signed = 0): object + function post(string $owner_id, string $message = "", int $from_group = 0, int $signed = 0, string $attachments = ""): object { $this->requireUser(); $this->willExecuteWriteAction(); @@ -405,27 +409,7 @@ final class Wall extends VKAPIRequestHandler if($signed == 1) $flags |= 0b01000000; - # TODO: Compatible implementation of this - try { - $photo = NULL; - $video = NULL; - if($_FILES["photo"]["error"] === UPLOAD_ERR_OK) { - $album = NULL; - if(!$anon && $owner_id > 0 && $owner_id === $this->getUser()->getId()) - $album = (new AlbumsRepo)->getUserWallAlbum($wallOwner); - - $photo = Photo::fastMake($this->getUser()->getId(), $message, $_FILES["photo"], $album, $anon); - } - - if($_FILES["video"]["error"] === UPLOAD_ERR_OK) - $video = Video::fastMake($this->getUser()->getId(), $_FILES["video"]["name"], $message, $_FILES["video"], $anon); - } catch(\DomainException $ex) { - $this->fail(-156, "The media file is corrupted"); - } catch(ISE $ex) { - $this->fail(-156, "The media file is corrupted or too large "); - } - - if(empty($message) && !$photo && !$video) + if(empty($message) && empty($attachments)) $this->fail(100, "Required parameter 'message' missing."); try { @@ -441,11 +425,50 @@ final class Wall extends VKAPIRequestHandler $this->fail(100, "One of the parameters specified was missing or invalid"); } - if(!is_null($photo)) - $post->attach($photo); + if(!empty($attachments)) { + $attachmentsArr = explode(",", $attachments); + # Аттачи такого вида: [тип][id владельца]_[id вложения] + # Пример: photo1_1 - if(!is_null($video)) - $post->attach($video); + if(sizeof($attachmentsArr) > 10) + $this->fail(50, "Error: too many attachments"); + + foreach($attachmentsArr as $attac) { + $attachmentType = NULL; + + if(str_contains($attac, "photo")) + $attachmentType = "photo"; + elseif(str_contains($attac, "video")) + $attachmentType = "video"; + else + $this->fail(205, "Unknown attachment type"); + + $attachment = str_replace($attachmentType, "", $attac); + + $attachmentOwner = (int)explode("_", $attachment)[0]; + $attachmentId = (int)end(explode("_", $attachment)); + + $attacc = NULL; + + if($attachmentType == "photo") { + $attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId); + if(!$attacc || $attacc->isDeleted()) + $this->fail(100, "Photo does not exists"); + if($attacc->getOwner()->getId() != $this->getUser()->getId()) + $this->fail(43, "You do not have access to this photo"); + + $post->attach($attacc); + } elseif($attachmentType == "video") { + $attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId); + if(!$attacc || $attacc->isDeleted()) + $this->fail(100, "Video does not exists"); + if($attacc->getOwner()->getId() != $this->getUser()->getId()) + $this->fail(43, "You do not have access to this video"); + + $post->attach($attacc); + } + } + } if($wall > 0 && $wall !== $this->user->identity->getId()) (new WallPostNotification($wallOwner, $post, $this->user->identity))->emit(); @@ -632,16 +655,20 @@ final class Wall extends VKAPIRequestHandler return $response; } - function createComment(int $owner_id, int $post_id, string $message, int $from_group = 0) { + function createComment(int $owner_id, int $post_id, string $message, int $from_group = 0, string $attachments = "") { $this->requireUser(); $this->willExecuteWriteAction(); $post = (new PostsRepo)->getPostById($owner_id, $post_id); - if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid"); + if(!$post || $post->isDeleted()) $this->fail(100, "Invalid post"); if($post->getTargetWall() < 0) $club = (new ClubsRepo)->get(abs($post->getTargetWall())); + if(empty($message) && empty($attachments)) { + $this->fail(100, "Required parameter 'message' missing."); + } + $flags = 0; if($from_group != 0 && !is_null($club) && $club->canBeModifiedBy($this->user)) $flags |= 0b10000000; @@ -659,6 +686,49 @@ final class Wall extends VKAPIRequestHandler $this->fail(1, "ошибка про то что коммент большой слишком"); } + if(!empty($attachments)) { + $attachmentsArr = explode(",", $attachments); + + if(sizeof($attachmentsArr) > 10) + $this->fail(50, "Error: too many attachments"); + + foreach($attachmentsArr as $attac) { + $attachmentType = NULL; + + if(str_contains($attac, "photo")) + $attachmentType = "photo"; + elseif(str_contains($attac, "video")) + $attachmentType = "video"; + else + $this->fail(205, "Unknown attachment type"); + + $attachment = str_replace($attachmentType, "", $attac); + + $attachmentOwner = (int)explode("_", $attachment)[0]; + $attachmentId = (int)end(explode("_", $attachment)); + + $attacc = NULL; + + if($attachmentType == "photo") { + $attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId); + if(!$attacc || $attacc->isDeleted()) + $this->fail(100, "Photo does not exists"); + if($attacc->getOwner()->getId() != $this->getUser()->getId()) + $this->fail(43, "You do not have access to this photo"); + + $comment->attach($attacc); + } elseif($attachmentType == "video") { + $attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId); + if(!$attacc || $attacc->isDeleted()) + $this->fail(100, "Video does not exists"); + if($attacc->getOwner()->getId() != $this->getUser()->getId()) + $this->fail(43, "You do not have access to this video"); + + $comment->attach($attacc); + } + } + } + if($post->getOwner()->getId() !== $this->user->getId()) if(($owner = $post->getOwner()) instanceof User) (new CommentNotification($owner, $comment, $post, $this->user))->emit(); diff --git a/Web/Models/Entities/Album.php b/Web/Models/Entities/Album.php index b20f0388..150cc625 100644 --- a/Web/Models/Entities/Album.php +++ b/Web/Models/Entities/Album.php @@ -66,4 +66,31 @@ class Album extends MediaCollection { return $this->has($photo); } + + function toVkApiStruct(?User $user = NULL, bool $need_covers = false, bool $photo_sizes = false): object + { + $res = (object) []; + + $res->id = $this->getPrettyId(); + $res->thumb_id = !is_null($this->getCoverPhoto()) ? $this->getCoverPhoto()->getPrettyId() : 0; + $res->owner_id = $this->getOwner()->getId(); + $res->title = $this->getName(); + $res->description = $this->getDescription(); + $res->created = $this->getCreationTime()->timestamp(); + $res->updated = $this->getEditTime() ? $this->getEditTime()->timestamp() : NULL; + $res->size = $this->size(); + $res->privacy_comment = 1; + $res->upload_by_admins_only = 1; + $res->comments_disabled = 0; + $res->can_upload = $this->canBeModifiedBy($user); # thisUser недоступен в entities + if($need_covers) { + $res->thumb_src = $this->getCoverURL(); + + if($photo_sizes) { + $res->sizes = !is_null($this->getCoverPhoto()) ? $this->getCoverPhoto()->getVkApiSizes() : NULL; + } + } + + return $res; + } } diff --git a/Web/Models/Entities/Club.php b/Web/Models/Entities/Club.php index db5baa88..b321a476 100644 --- a/Web/Models/Entities/Club.php +++ b/Web/Models/Entities/Club.php @@ -160,7 +160,7 @@ class Club extends RowModel function canPost(): bool { - return (bool) $this->getRecord()->wall; + return (bool) $this->getRecord()->wall; } @@ -262,12 +262,12 @@ class Club extends RowModel return $subbed && ($this->getOpennesStatus() === static::CLOSED ? $this->isSubscriptionAccepted($user) : true); } - function getFollowersQuery(): GroupedSelection + function getFollowersQuery(string $sort = "follower ASC"): GroupedSelection { $query = $this->getRecord()->related("subscriptions.target"); if($this->getOpennesStatus() === static::OPEN) { - $query = $query->where("model", "openvk\\Web\\Models\\Entities\\Club"); + $query = $query->where("model", "openvk\\Web\\Models\\Entities\\Club")->order($sort); } else { return false; } @@ -280,9 +280,9 @@ class Club extends RowModel return sizeof($this->getFollowersQuery()); } - function getFollowers(int $page = 1): \Traversable + function getFollowers(int $page = 1, int $perPage = 6, string $sort = "follower ASC"): \Traversable { - $rels = $this->getFollowersQuery()->page($page, 6); + $rels = $this->getFollowersQuery($sort)->page($page, $perPage); foreach($rels as $rel) { $rel = (new Users)->get($rel->follower); @@ -360,6 +360,35 @@ class Club extends RowModel return $this->getRecord()->alert; } + function toVkApiStruct(?User $user = NULL): object + { + $res = []; + + $res->id = $this->getId(); + $res->name = $this->getName(); + $res->screen_name = $this->getShortCode(); + $res->is_closed = 0; + $res->deactivated = NULL; + $res->is_admin = $this->canBeModifiedBy($user); + + if($this->canBeModifiedBy($user)) { + $res->admin_level = 3; + } + + $res->is_member = $this->getSubscriptionStatus($user) ? 1 : 0; + + $res->type = "group"; + $res->photo_50 = $this->getAvatarUrl("miniscule"); + $res->photo_100 = $this->getAvatarUrl("tiny"); + $res->photo_200 = $this->getAvatarUrl("normal"); + + $res->can_create_topic = $this->canBeModifiedBy($user) ? 1 : $this->isEveryoneCanCreateTopics() ? 1 : 0; + + $res->can_post = $this->canBeModifiedBy($user) ? 1 : $this->canPost() ? 1 : 0; + + return (object) $res; + } + use Traits\TBackDrops; use Traits\TSubscribable; } diff --git a/Web/Models/Entities/Comment.php b/Web/Models/Entities/Comment.php index 9115ad3e..bd833a82 100644 --- a/Web/Models/Entities/Comment.php +++ b/Web/Models/Entities/Comment.php @@ -2,6 +2,7 @@ namespace openvk\Web\Models\Entities; use openvk\Web\Models\Repositories\Clubs; use openvk\Web\Models\RowModel; +use openvk\Web\Models\Entities\{Note}; class Comment extends Post { @@ -52,4 +53,36 @@ class Comment extends Post $this->getTarget() instanceof Post && $this->getTarget()->getTargetWall() < 0 && (new Clubs)->get(abs($this->getTarget()->getTargetWall()))->canBeModifiedBy($user) || $this->getTarget() instanceof Topic && $this->getTarget()->canBeModifiedBy($user); } + + function toVkApiStruct(?User $user = NULL, bool $need_likes = false, bool $extended = false, ?Note $note = NULL): object + { + $res = (object) []; + + $res->id = $this->getId(); + $res->from_id = $this->getOwner()->getId(); + $res->date = $this->getPublicationTime()->timestamp(); + $res->text = $this->getText(); + $res->attachments = []; + $res->parents_stack = []; + + if(!is_null($note)) { + $res->uid = $this->getOwner()->getId(); + $res->nid = $note->getId(); + $res->oid = $note->getOwner()->getId(); + } + + foreach($this->getChildren() as $attachment) { + if($attachment->isDeleted()) + continue; + + $res->attachments[] = $attachment->toVkApiStruct(); + } + + if($need_likes) { + $res->count = $this->getLikesCount(); + $res->user_likes = (int)$this->hasLikeFrom($user); + $res->can_like = 1; + } + return $res; + } } diff --git a/Web/Models/Entities/Note.php b/Web/Models/Entities/Note.php index a536602a..d259c2a5 100644 --- a/Web/Models/Entities/Note.php +++ b/Web/Models/Entities/Note.php @@ -118,4 +118,23 @@ class Note extends Postable { return $this->getRecord()->source; } + + function toVkApiStruct(): object + { + $res = (object) []; + + $res->id = $this->getId(); + $res->owner_id = $this->getOwner()->getId(); + $res->title = $this->getName(); + $res->text = $this->getText(); + $res->date = $this->getPublicationTime()->timestamp(); + $res->comments = $this->getCommentsCount(); + $res->read_comments = $this->getCommentsCount(); + $res->view_url = "/note".$this->getOwner()->getId()."_".$this->getId(); + $res->privacy_view = 1; + $res->can_comment = 1; + $res->text_wiki = "r"; + + return $res; + } } diff --git a/Web/Models/Entities/Photo.php b/Web/Models/Entities/Photo.php index 3c4db886..29526daa 100644 --- a/Web/Models/Entities/Photo.php +++ b/Web/Models/Entities/Photo.php @@ -296,25 +296,35 @@ class Photo extends Media return (new Albums)->getAlbumByPhotoId($this); } - function toVkApiStruct(): object + function toVkApiStruct(bool $photo_sizes = true, bool $extended = false): object { $res = (object) []; $res->id = $res->pid = $this->getId(); - $res->owner_id = $res->user_id = $this->getOwner()->getId()->getId(); + $res->owner_id = $res->user_id = $this->getOwner()->getId(); $res->aid = $res->album_id = NULL; $res->width = $this->getDimensions()[0]; $res->height = $this->getDimensions()[1]; $res->date = $res->created = $this->getPublicationTime()->timestamp(); - $res->sizes = $this->getVkApiSizes(); - $res->src_small = $res->photo_75 = $this->getURLBySizeId("miniscule"); - $res->src = $res->photo_130 = $this->getURLBySizeId("tiny"); - $res->src_big = $res->photo_604 = $this->getURLBySizeId("normal"); - $res->src_xbig = $res->photo_807 = $this->getURLBySizeId("large"); - $res->src_xxbig = $res->photo_1280 = $this->getURLBySizeId("larger"); - $res->src_xxxbig = $res->photo_2560 = $this->getURLBySizeId("original"); - $res->src_original = $res->url = $this->getURLBySizeId("UPLOADED_MAXRES"); + if($photo_sizes) { + $res->sizes = $this->getVkApiSizes(); + $res->src_small = $res->photo_75 = $this->getURLBySizeId("miniscule"); + $res->src = $res->photo_130 = $this->getURLBySizeId("tiny"); + $res->src_big = $res->photo_604 = $this->getURLBySizeId("normal"); + $res->src_xbig = $res->photo_807 = $this->getURLBySizeId("large"); + $res->src_xxbig = $res->photo_1280 = $this->getURLBySizeId("larger"); + $res->src_xxxbig = $res->photo_2560 = $this->getURLBySizeId("original"); + $res->src_original = $res->url = $this->getURLBySizeId("UPLOADED_MAXRES"); + } + + if($extended) { + $res->likes = $this->getLikesCount(); # их нету но пусть будут + $res->comments = $this->getCommentsCount(); + $res->tags = 0; + $res->can_comment = 1; + $res->can_repost = 0; + } return $res; } diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index ed1e3e60..348631e4 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -1109,6 +1109,23 @@ class User extends RowModel return true; } + + function toVkApiStruct(): object + { + $res = (object) []; + + $res->id = $this->getId(); + $res->first_name = $this->getFirstName(); + $res->last_name = $this->getLastName(); + $res->deactivated = $this->isDeactivated(); + $res->photo_50 = $this->getAvatarURL(); + $res->photo_100 = $this->getAvatarURL("tiny"); + $res->photo_200 = $this->getAvatarURL("normal"); + $res->photo_id = !is_null($this->getAvatarPhoto()) ? $this->getAvatarPhoto()->getPrettyId() : NULL; + # TODO: Perenesti syuda vsyo ostalnoyie + + return $res; + } use Traits\TBackDrops; use Traits\TSubscribable; diff --git a/Web/Models/Entities/Video.php b/Web/Models/Entities/Video.php index 45320efc..4c652fdd 100644 --- a/Web/Models/Entities/Video.php +++ b/Web/Models/Entities/Video.php @@ -165,6 +165,11 @@ class Video extends Media ]; } + function toVkApiStruct(): object + { + return $this->getApiStructure(); + } + function setLink(string $link): string { if(preg_match(file_get_contents(__DIR__ . "/../VideoDrivers/regex/youtube.txt"), $link, $matches)) { diff --git a/Web/Models/Repositories/Albums.php b/Web/Models/Repositories/Albums.php index 99c6c732..b38ee462 100644 --- a/Web/Models/Repositories/Albums.php +++ b/Web/Models/Repositories/Albums.php @@ -123,4 +123,14 @@ class Albums return $dbalbum->collection ? $this->get($dbalbum->collection) : null; } + + function getAlbumByOwnerAndId(int $owner, int $id) + { + $album = $this->albums->where([ + "owner" => $owner, + "id" => $id + ])->fetch(); + + return new Album($album); + } } diff --git a/Web/Models/Repositories/Notes.php b/Web/Models/Repositories/Notes.php index 7d95f975..0473070a 100644 --- a/Web/Models/Repositories/Notes.php +++ b/Web/Models/Repositories/Notes.php @@ -26,10 +26,10 @@ class Notes return $this->toNote($this->notes->get($id)); } - function getUserNotes(User $user, int $page = 1, ?int $perPage = NULL): \Traversable + function getUserNotes(User $user, int $page = 1, ?int $perPage = NULL, string $sort = "DESC"): \Traversable { $perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE; - foreach($this->notes->where("owner", $user->getId())->where("deleted", 0)->order("created DESC")->page($page, $perPage) as $album) + foreach($this->notes->where("owner", $user->getId())->where("deleted", 0)->order("created $sort")->page($page, $perPage) as $album) yield new Note($album); } diff --git a/Web/Models/Repositories/Photos.php b/Web/Models/Repositories/Photos.php index 4ff8a1b9..88c7e804 100644 --- a/Web/Models/Repositories/Photos.php +++ b/Web/Models/Repositories/Photos.php @@ -1,6 +1,6 @@ photos->where([ + "owner" => $user->getId() + ]); + + foreach($photos as $photo) { + yield new Photo($photo); + } + } }