diff --git a/ServiceAPI/Video.php b/ServiceAPI/Video.php new file mode 100644 index 00000000..d0eb4ee8 --- /dev/null +++ b/ServiceAPI/Video.php @@ -0,0 +1,156 @@ +user = $user; + $this->videos = new Videos; + $this->comments = new Comments; + $this->groups = new Clubs; + } + + function getVideo(int $id, callable $resolve, callable $reject) + { + $video = $this->videos->get($id); + + if(!$video || $video->isDeleted()) { + $reject(2, "Video does not exists"); + } + + if(method_exists($video, "canBeViewedBy") && !$video->canBeViewedBy($this->user)) { + $reject(4, "Access to video denied"); + } + + if(!$video->getOwner()->getPrivacyPermission('videos.read', $this->user)) { + $reject(8, "Access to video denied: this user chose to hide his videos"); + } + + $prevVideo = NULL; + $nextVideo = NULL; + $lastVideo = $this->videos->getLastVideo($video->getOwner()); + + if($video->getVirtualId() - 1 != 0) { + for($i = $video->getVirtualId(); $i != 0; $i--) { + $maybeVideo = (new Videos)->getByOwnerAndVID($video->getOwner()->getId(), $i); + + if(!is_null($maybeVideo) && !$maybeVideo->isDeleted() && $maybeVideo->getId() != $video->getId()) { + if(method_exists($maybeVideo, "canBeViewedBy") && !$maybeVideo->canBeViewedBy($this->user)) { + continue; + } + + $prevVideo = $maybeVideo; + break; + } + } + } + + if(is_null($lastVideo) || $lastVideo->getId() == $video->getId()) { + $nextVideo = NULL; + } else { + for($i = $video->getVirtualId(); $i <= $lastVideo->getVirtualId(); $i++) { + $maybeVideo = (new Videos)->getByOwnerAndVID($video->getOwner()->getId(), $i); + + if(!is_null($maybeVideo) && !$maybeVideo->isDeleted() && $maybeVideo->getId() != $video->getId()) { + if(method_exists($maybeVideo, "canBeViewedBy") && !$maybeVideo->canBeViewedBy($this->user)) { + continue; + } + + $nextVideo = $maybeVideo; + break; + } + } + } + + $res = [ + "id" => $video->getId(), + "title" => $video->getName(), + "owner" => $video->getOwner()->getId(), + "commentsCount" => $video->getCommentsCount(), + "description" => $video->getDescription(), + "type" => $video->getType(), + "name" => $video->getOwner()->getCanonicalName(), + "pretty_id" => $video->getPrettyId(), + "virtual_id" => $video->getVirtualId(), + "published" => (string)$video->getPublicationTime(), + "likes" => $video->getLikesCount(), + "has_like" => $video->hasLikeFrom($this->user), + "author" => $video->getOwner()->getCanonicalName(), + "canBeEdited" => $video->getOwner()->getId() == $this->user->getId(), + "isProcessing" => $video->getType() == 0 && $video->getURL() == "/assets/packages/static/openvk/video/rendering.mp4", + "prevVideo" => !is_null($prevVideo) ? $prevVideo->getId() : null, + "nextVideo" => !is_null($nextVideo) ? $nextVideo->getId() : null, + ]; + + if($video->getType() == 1) { + $res["embed"] = $video->getVideoDriver()->getEmbed(); + } else { + $res["url"] = $video->getURL(); + } + + $resolve($res); + } + + function shareVideo(int $owner, int $vid, int $type, string $message, int $club, bool $signed, bool $asGroup, callable $resolve, callable $reject) + { + $video = $this->videos->getByOwnerAndVID($owner, $vid); + + if(!$video || $video->isDeleted()) { + $reject(16, "Video does not exists"); + } + + if(method_exists($video, "canBeViewedBy") && !$video->canBeViewedBy($this->user)) { + $reject(32, "Access to video denied"); + } + + if(!$video->getOwner()->getPrivacyPermission('videos.read', $this->user)) { + $reject(8, "Access to video denied: this user chose to hide his videos"); + } + + $flags = 0; + + $nPost = new Post; + $nPost->setOwner($this->user->getId()); + + if($type == 0) { + $nPost->setWall($this->user->getId()); + } else { + $club = $this->groups->get($club); + + if(!$club || $club->isDeleted() || !$club->canBeModifiedBy($this->user)) { + $reject(64, "Can't do repost to this club"); + } + + if($asGroup) + $flags |= 0b10000000; + + if($signed) + $flags |= 0b01000000; + + $nPost->setWall($club->getId() * -1); + } + + $nPost->setContent($message); + $nPost->setFlags($flags); + $nPost->save(); + + $nPost->attach($video); + + $res = [ + "id" => $nPost->getId(), + "pretty_id" => $nPost->getPrettyId(), + ]; + + $resolve($res); + } +} diff --git a/VKAPI/Handlers/Likes.php b/VKAPI/Handlers/Likes.php index 08c4e6e4..fab127f2 100644 --- a/VKAPI/Handlers/Likes.php +++ b/VKAPI/Handlers/Likes.php @@ -1,72 +1,157 @@ requireUser(); + function add(string $type, int $owner_id, int $item_id): object + { + $this->requireUser(); $this->willExecuteWriteAction(); + $postable = NULL; switch($type) { case "post": $post = (new PostsRepo)->getPostById($owner_id, $item_id); - if(is_null($post)) - $this->fail(100, "One of the parameters specified was missing or invalid: object not found"); - - $post->setLike(true, $this->getUser()); - - return (object) [ - "likes" => $post->getLikesCount() - ]; + $postable = $post; + break; + case "comment": + $comment = (new CommentsRepo)->get($item_id); + $postable = $comment; + break; + case "video": + $video = (new VideosRepo)->getByOwnerAndVID($owner_id, $item_id); + $postable = $video; + break; + case "photo": + $photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $item_id); + $postable = $photo; + break; + case "note": + $note = (new NotesRepo)->getNoteById($owner_id, $item_id); + $postable = $note; + break; default: $this->fail(100, "One of the parameters specified was missing or invalid: incorrect type"); } - } - function delete(string $type, int $owner_id, int $item_id): object - { - $this->requireUser(); + if(is_null($postable) || $postable->isDeleted()) + $this->fail(100, "One of the parameters specified was missing or invalid: object not found"); + + if(method_exists($postable, "canBeViewedBy") && !$postable->canBeViewedBy($this->getUser() ?? NULL)) { + $this->fail(2, "Access to postable denied"); + } + + $postable->setLike(true, $this->getUser()); + + return (object) [ + "likes" => $postable->getLikesCount() + ]; + } + + function delete(string $type, int $owner_id, int $item_id): object + { + $this->requireUser(); $this->willExecuteWriteAction(); + $postable = NULL; switch($type) { case "post": $post = (new PostsRepo)->getPostById($owner_id, $item_id); - if (is_null($post)) - $this->fail(100, "One of the parameters specified was missing or invalid: object not found"); - - $post->setLike(false, $this->getUser()); - return (object) [ - "likes" => $post->getLikesCount() - ]; + $postable = $post; + break; + case "comment": + $comment = (new CommentsRepo)->get($item_id); + $postable = $comment; + break; + case "video": + $video = (new VideosRepo)->getByOwnerAndVID($owner_id, $item_id); + $postable = $video; + break; + case "photo": + $photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $item_id); + $postable = $photo; + break; + case "note": + $note = (new NotesRepo)->getNoteById($owner_id, $item_id); + $postable = $note; + break; default: $this->fail(100, "One of the parameters specified was missing or invalid: incorrect type"); } - } - + + if(is_null($postable) || $postable->isDeleted()) + $this->fail(100, "One of the parameters specified was missing or invalid: object not found"); + + if(method_exists($postable, "canBeViewedBy") && !$postable->canBeViewedBy($this->getUser() ?? NULL)) { + $this->fail(2, "Access to postable denied"); + } + + if(!is_null($postable)) { + $postable->setLike(false, $this->getUser()); + + return (object) [ + "likes" => $postable->getLikesCount() + ]; + } + } + function isLiked(int $user_id, string $type, int $owner_id, int $item_id): object - { - $this->requireUser(); + { + $this->requireUser(); + $user = (new UsersRepo)->get($user_id); + + if(is_null($user) || $user->isDeleted()) + $this->fail(100, "One of the parameters specified was missing or invalid: user not found"); + + if(method_exists($user, "canBeViewedBy") && !$user->canBeViewedBy($this->getUser())) { + $this->fail(1984, "Access denied: you can't see this user"); + } + + $postable = NULL; switch($type) { case "post": - $user = (new UsersRepo)->get($user_id); - if (is_null($user)) - $this->fail(100, "One of the parameters specified was missing or invalid: user not found"); - $post = (new PostsRepo)->getPostById($owner_id, $item_id); - if (is_null($post)) - $this->fail(100, "One of the parameters specified was missing or invalid: object not found"); - - return (object) [ - "liked" => (int) $post->hasLikeFrom($user), - "copied" => 0 # TODO: handle this - ]; + $postable = $post; + break; + case "comment": + $comment = (new CommentsRepo)->get($item_id); + $postable = $comment; + break; + case "video": + $video = (new VideosRepo)->getByOwnerAndVID($owner_id, $item_id); + $postable = $video; + break; + case "photo": + $photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $item_id); + $postable = $photo; + break; + case "note": + $note = (new NotesRepo)->getNoteById($owner_id, $item_id); + $postable = $note; + break; default: $this->fail(100, "One of the parameters specified was missing or invalid: incorrect type"); } + + if(is_null($postable) || $postable->isDeleted()) + $this->fail(100, "One of the parameters specified was missing or invalid: object not found"); + + if(!$postable->canBeViewedBy($this->getUser())) { + $this->fail(665, "Access to postable denied"); + } + + return (object) [ + "liked" => (int) $postable->hasLikeFrom($user), + "copied" => 0 + ]; } function getList(string $type, int $owner_id, int $item_id, bool $extended = false, int $offset = 0, int $count = 10, bool $skip_own = false) diff --git a/VKAPI/Handlers/Video.php b/VKAPI/Handlers/Video.php index c51fff3f..5d0967c7 100755 --- a/VKAPI/Handlers/Video.php +++ b/VKAPI/Handlers/Video.php @@ -26,7 +26,7 @@ final class Video extends VKAPIRequestHandler $video = (new VideosRepo)->getByOwnerAndVID(intval($id[0]), intval($id[1])); if($video) { - $items[] = $video->getApiStructure(); + $items[] = $video->getApiStructure($this->getUser()); } } @@ -38,14 +38,18 @@ final class Video extends VKAPIRequestHandler if ($owner_id > 0) $user = (new UsersRepo)->get($owner_id); else - $this->fail(1, "Not implemented"); + $this->fail(1, "Not implemented"); + + if(!$user->getPrivacyPermission('videos.read', $this->getUser())) { + $this->fail(20, "Access denied: this user chose to hide his videos"); + } $videos = (new VideosRepo)->getByUser($user, $offset + 1, $count); $videosCount = (new VideosRepo)->getUserVideosCount($user); $items = []; foreach ($videos as $video) { - $items[] = $video->getApiStructure(); + $items[] = $video->getApiStructure($this->getUser()); } return (object) [ diff --git a/VKAPI/Handlers/Wall.php b/VKAPI/Handlers/Wall.php index 907f06fa..2929beb9 100644 --- a/VKAPI/Handlers/Wall.php +++ b/VKAPI/Handlers/Wall.php @@ -57,7 +57,7 @@ final class Wall extends VKAPIRequestHandler } else if($attachment instanceof \openvk\Web\Models\Entities\Poll) { $attachments[] = $this->getApiPoll($attachment, $this->getUser()); } else if ($attachment instanceof \openvk\Web\Models\Entities\Video) { - $attachments[] = $attachment->getApiStructure(); + $attachments[] = $attachment->getApiStructure($this->getUser()); } else if ($attachment instanceof \openvk\Web\Models\Entities\Note) { $attachments[] = $attachment->toVkApiStruct(); } else if ($attachment instanceof \openvk\Web\Models\Entities\Audio) { @@ -237,7 +237,7 @@ final class Wall extends VKAPIRequestHandler } else if($attachment instanceof \openvk\Web\Models\Entities\Poll) { $attachments[] = $this->getApiPoll($attachment, $user); } else if ($attachment instanceof \openvk\Web\Models\Entities\Video) { - $attachments[] = $attachment->getApiStructure(); + $attachments[] = $attachment->getApiStructure($this->getUser()); } else if ($attachment instanceof \openvk\Web\Models\Entities\Note) { $attachments[] = $attachment->toVkApiStruct(); } else if ($attachment instanceof \openvk\Web\Models\Entities\Audio) { diff --git a/Web/Models/Entities/Comment.php b/Web/Models/Entities/Comment.php index 90057bdd..a553443e 100644 --- a/Web/Models/Entities/Comment.php +++ b/Web/Models/Entities/Comment.php @@ -74,8 +74,12 @@ class Comment extends Post foreach($this->getChildren() as $attachment) { if($attachment->isDeleted()) continue; - - $res->attachments[] = $attachment->toVkApiStruct(); + + if($attachment instanceof \openvk\Web\Models\Entities\Photo) { + $res->attachments[] = $attachment->toVkApiStruct(); + } else if($attachment instanceof \openvk\Web\Models\Entities\Video) { + $res->attachments[] = $attachment->toVkApiStruct($this->getUser()); + } } if($need_likes) { diff --git a/Web/Models/Entities/Postable.php b/Web/Models/Entities/Postable.php index 365c0f26..a6478605 100644 --- a/Web/Models/Entities/Postable.php +++ b/Web/Models/Entities/Postable.php @@ -131,10 +131,15 @@ abstract class Postable extends Attachable "target" => $this->getRecord()->id, ]; - if($liked) - DB::i()->getContext()->table("likes")->insert($searchData); - else - DB::i()->getContext()->table("likes")->where($searchData)->delete(); + if($liked) { + if(!$this->hasLikeFrom($user)) { + DB::i()->getContext()->table("likes")->insert($searchData); + } + } else { + if($this->hasLikeFrom($user)) { + DB::i()->getContext()->table("likes")->where($searchData)->delete(); + } + } } function hasLikeFrom(User $user): bool diff --git a/Web/Models/Entities/Video.php b/Web/Models/Entities/Video.php index a9d565c5..fa54392e 100644 --- a/Web/Models/Entities/Video.php +++ b/Web/Models/Entities/Video.php @@ -115,15 +115,15 @@ class Video extends Media return $this->getRecord()->owner; } - function getApiStructure(): object + function getApiStructure(?User $user = NULL): object { $fromYoutube = $this->getType() == Video::TYPE_EMBED; - return (object)[ + $res = (object)[ "type" => "video", "video" => [ "can_comment" => 1, - "can_like" => 0, // we don't h-have wikes in videos - "can_repost" => 0, + "can_like" => 1, // we don't h-have wikes in videos + "can_repost" => 1, "can_subscribe" => 1, "can_add_to_faves" => 0, "can_add" => 0, @@ -155,21 +155,26 @@ class Video extends Media "repeat" => 0, "type" => "video", "views" => 0, - "likes" => [ - "count" => 0, - "user_likes" => 0 - ], "reposts" => [ "count" => 0, "user_reposted" => 0 ] ] ]; + + if(!is_null($user)) { + $res->video["likes"] = [ + "count" => $this->getLikesCount(), + "user_likes" => $this->hasLikeFrom($user) + ]; + } + + return $res; } - function toVkApiStruct(): object + function toVkApiStruct(?User $user): object { - return $this->getApiStructure(); + return $this->getApiStructure($user); } function setLink(string $link): string diff --git a/Web/Models/Repositories/Videos.php b/Web/Models/Repositories/Videos.php index 63273349..2d41c3f9 100644 --- a/Web/Models/Repositories/Videos.php +++ b/Web/Models/Repositories/Videos.php @@ -77,4 +77,11 @@ class Videos return new Util\EntityStream("Video", $result->order("$sort")); } + + function getLastVideo(User $user) + { + $video = $this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0])->order("id DESC")->fetch(); + + return new Video($video); + } } diff --git a/Web/Presenters/VideosPresenter.php b/Web/Presenters/VideosPresenter.php index 9d2fddc6..57e73f11 100644 --- a/Web/Presenters/VideosPresenter.php +++ b/Web/Presenters/VideosPresenter.php @@ -74,18 +74,18 @@ final class VideosPresenter extends OpenVKPresenter else if(!empty($this->postParam("link"))) $video->setLink($this->postParam("link")); else - $this->flashFail("err", tr("no_video"), tr("no_video_desc")); + $this->flashFail("err", tr("no_video_error"), tr("no_video_description")); } catch(\DomainException $ex) { - $this->flashFail("err", tr("error_occured"), tr("error_video_damaged_file")); + $this->flashFail("err", tr("error_video"), tr("file_corrupted")); } catch(ISE $ex) { - $this->flashFail("err", tr("error_occured"), tr("error_video_incorrect_link")); + $this->flashFail("err", tr("error_video"), tr("link_incorrect")); } $video->save(); $this->redirect("/video" . $video->getPrettyId()); } else { - $this->flashFail("err", tr("error_occured"), tr("error_video_no_title")); + $this->flashFail("err", tr("error_video"), tr("no_name_error")); } } } @@ -99,14 +99,14 @@ final class VideosPresenter extends OpenVKPresenter if(!$video) $this->notFound(); if(is_null($this->user) || $this->user->id !== $owner) - $this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied")); + $this->flashFail("err", tr("access_denied_error"), tr("access_denied_error_description")); if($_SERVER["REQUEST_METHOD"] === "POST") { $video->setName(empty($this->postParam("name")) ? NULL : $this->postParam("name")); $video->setDescription(empty($this->postParam("desc")) ? NULL : $this->postParam("desc")); $video->save(); - $this->flash("succ", tr("changes_saved"), tr("new_data_video")); + $this->flash("succ", tr("changes_saved"), tr("changes_saved_video_comment")); $this->redirect("/video" . $video->getPrettyId()); } @@ -128,9 +128,29 @@ final class VideosPresenter extends OpenVKPresenter $video->deleteVideo($owner, $vid); } } else { - $this->flashFail("err", tr("error_deleting_video"), tr("login_please")); + $this->flashFail("err", tr("cant_delete_video"), tr("cant_delete_video_comment")); } $this->redirect("/videos" . $owner); } + + function renderLike(int $owner, int $video_id): void + { + $this->assertUserLoggedIn(); + $this->willExecuteWriteAction(); + $this->assertNoCSRF(); + + $video = $this->videos->getByOwnerAndVID($owner, $video_id); + if(!$video || $video->isDeleted() || $video->getOwner()->isDeleted()) $this->notFound(); + + if(method_exists($video, "canBeViewedBy") && !$video->canBeViewedBy($this->user->identity)) { + $this->flashFail("err", tr("error"), tr("forbidden")); + } + + if(!is_null($this->user)) { + $video->toggleLike($this->user->identity); + } + + $this->redirect("$_SERVER[HTTP_REFERER]"); + } } diff --git a/Web/Presenters/templates/@layout.xml b/Web/Presenters/templates/@layout.xml index 31e0bc5c..8f1a35f0 100644 --- a/Web/Presenters/templates/@layout.xml +++ b/Web/Presenters/templates/@layout.xml @@ -13,6 +13,7 @@ {script "js/node_modules/jquery/dist/jquery.min.js"} + {script "js/node_modules/jquery-ui/dist/jquery-ui.min.js"} {script "js/node_modules/umbrellajs/umbrella.min.js"} {script "js/l10n.js"} {script "js/openvk.cls.js"} @@ -370,6 +371,10 @@
+- {_view_video} + {_view_video} {if $x->getCommentsCount() > 0}| {_comments} ({$x->getCommentsCount()}){/if}
{/block} diff --git a/Web/Presenters/templates/Videos/View.xml b/Web/Presenters/templates/Videos/View.xml index 38967b5e..cdaaba11 100644 --- a/Web/Presenters/templates/Videos/View.xml +++ b/Web/Presenters/templates/Videos/View.xml @@ -29,7 +29,7 @@
-
-
-
-
-
-
-
- |
-
- {ifset infotable}
- {include infotable, x => $dat}
- {else}
-
-
- {$video->getName()}
-
-
- - - {$video->getDescription() ?? ""} - - {_video_uploaded} {$video->getPublicationTime()}- - - {_view_video} - {if $video->getCommentsCount() > 0}| {_comments} ({$video->getCommentsCount()}){/if} - - {/ifset} - |
-
+
+
+
+
+
+ |
+
+ {ifset infotable}
+ {include infotable, x => $dat}
+ {else}
+
+
+ {$video->getName()}
+
+
+ + + {$video->getDescription() ?? ""} + + {_video_uploaded} {$video->getPublicationTime()}+ + + {_view_video} + {if $video->getCommentsCount() > 0}| {_comments} ({$video->getCommentsCount()}){/if} + + {/ifset} + |
+