diff --git a/ServiceAPI/Wall.php b/ServiceAPI/Wall.php index 5677f7ba..787a998e 100644 --- a/ServiceAPI/Wall.php +++ b/ServiceAPI/Wall.php @@ -2,7 +2,7 @@ namespace openvk\ServiceAPI; use openvk\Web\Models\Entities\Post; use openvk\Web\Models\Entities\User; -use openvk\Web\Models\Repositories\{Posts, Notes}; +use openvk\Web\Models\Repositories\{Posts, Notes, Videos}; class Wall implements Handler { @@ -15,6 +15,7 @@ class Wall implements Handler $this->user = $user; $this->posts = new Posts; $this->notes = new Notes; + $this->videos = new Videos; } function getPost(int $id, callable $resolve, callable $reject): void @@ -95,4 +96,45 @@ class Wall implements Handler $resolve($arr); } + + function getVideos(int $page = 1, callable $resolve, callable $reject) + { + $videos = $this->videos->getByUser($this->user, $page, 8); + $count = $this->videos->getUserVideosCount($this->user); + + $arr = [ + "count" => $count, + "items" => [], + ]; + + foreach($videos as $video) { + $res = json_decode(json_encode($video->toVkApiStruct()), true); + $res["video"]["author_name"] = $video->getOwner()->getCanonicalName(); + + $arr["items"][] = $res; + } + + $resolve($arr); + } + + function searchVideos(int $page = 1, string $query, callable $resolve, callable $reject) + { + $dbc = $this->videos->find($query); + $videos = $dbc->page($page, 8); + $count = $dbc->size(); + + $arr = [ + "count" => $count, + "items" => [], + ]; + + foreach($videos as $video) { + $res = json_decode(json_encode($video->toVkApiStruct()), true); + $res["video"]["author_name"] = $video->getOwner()->getCanonicalName(); + + $arr["items"][] = $res; + } + + $resolve($arr); + } } diff --git a/VKAPI/Handlers/Wall.php b/VKAPI/Handlers/Wall.php index d52dfce1..6b78a0b0 100644 --- a/VKAPI/Handlers/Wall.php +++ b/VKAPI/Handlers/Wall.php @@ -463,28 +463,25 @@ final class Wall extends VKAPIRequestHandler 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"); + $this->fail(100, "Invalid photo"); + if(!$attacc->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) + $this->fail(43, "Access to photo denied"); $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"); + if(!$attacc->getOwner()->getPrivacyPermission('videos.read', $this->getUser())) + $this->fail(43, "Access to video denied"); $post->attach($attacc); } elseif($attachmentType == "note") { $attacc = (new NotesRepo)->getNoteById($attachmentOwner, $attachmentId); if(!$attacc || $attacc->isDeleted()) $this->fail(100, "Note does not exist"); - if($attacc->getOwner()->getId() != $this->getUser()->getId()) - $this->fail(43, "You do not have access to this note"); - - if($attacc->getOwner()->getPrivacySetting("notes.read") < 1) - $this->fail(11, "You can't attach note to post, because your notes list is closed. Change it in privacy settings in web-version."); + if(!$attacc->getOwner()->getPrivacyPermission('notes.read', $this->getUser())) + $this->fail(11, "Access to note denied"); $post->attach($attacc); } @@ -678,7 +675,7 @@ final class Wall extends VKAPIRequestHandler return $response; } - function createComment(int $owner_id, int $post_id, string $message, int $from_group = 0, string $attachments = "") { + function createComment(int $owner_id, int $post_id, string $message = "", int $from_group = 0, string $attachments = "") { $this->requireUser(); $this->willExecuteWriteAction(); @@ -736,16 +733,16 @@ final class Wall extends VKAPIRequestHandler $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"); + if(!$attacc->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) + $this->fail(11, "Access to photo denied"); $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"); + if(!$attacc->getOwner()->getPrivacyPermission('videos.read', $this->getUser())) + $this->fail(11, "Access to video denied"); $comment->attach($attacc); } diff --git a/Web/Presenters/CommentPresenter.php b/Web/Presenters/CommentPresenter.php index 29c54c78..b68c7d11 100644 --- a/Web/Presenters/CommentPresenter.php +++ b/Web/Presenters/CommentPresenter.php @@ -2,7 +2,7 @@ namespace openvk\Web\Presenters; use openvk\Web\Models\Entities\{Comment, Notifications\MentionNotification, Photo, Video, User, Topic, Post}; use openvk\Web\Models\Entities\Notifications\CommentNotification; -use openvk\Web\Models\Repositories\{Comments, Clubs}; +use openvk\Web\Models\Repositories\{Comments, Clubs, Videos}; final class CommentPresenter extends OpenVKPresenter { @@ -73,7 +73,6 @@ final class CommentPresenter extends OpenVKPresenter # TODO move to trait try { $photo = NULL; - $video = NULL; if($_FILES["_pic_attachment"]["error"] === UPLOAD_ERR_OK) { $album = NULL; if($wall > 0 && $wall === $this->user->id) @@ -81,13 +80,28 @@ final class CommentPresenter extends OpenVKPresenter $photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album); } - - if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) { - $video = Video::fastMake($this->user->id, $_FILES["_vid_attachment"]["name"], $this->postParam("text"), $_FILES["_vid_attachment"]); - } } catch(ISE $ex) { $this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_file_too_big")); } + + $videos = []; + + if(!empty($this->postParam("videos"))) { + $un = rtrim($this->postParam("videos"), ","); + $arr = explode(",", $un); + + if(sizeof($arr) < 11) { + foreach($arr as $dat) { + $ids = explode("_", $dat); + $video = (new Videos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]); + + if(!$video || $video->isDeleted()) + continue; + + $videos[] = $video; + } + } + } if(empty($this->postParam("text")) && !$photo && !$video) $this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_empty")); @@ -108,8 +122,9 @@ final class CommentPresenter extends OpenVKPresenter if(!is_null($photo)) $comment->attach($photo); - if(!is_null($video)) - $comment->attach($video); + if(sizeof($videos) > 0) + foreach($videos as $vid) + $comment->attach($vid); if($entity->getOwner()->getId() !== $this->user->identity->getId()) if(($owner = $entity->getOwner()) instanceof User) diff --git a/Web/Presenters/WallPresenter.php b/Web/Presenters/WallPresenter.php index 32ac421e..ef9e4689 100644 --- a/Web/Presenters/WallPresenter.php +++ b/Web/Presenters/WallPresenter.php @@ -3,7 +3,7 @@ namespace openvk\Web\Presenters; use openvk\Web\Models\Exceptions\TooMuchOptionsException; use openvk\Web\Models\Entities\{Poll, Post, Photo, Video, Club, User}; use openvk\Web\Models\Entities\Notifications\{MentionNotification, RepostNotification, WallPostNotification}; -use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums, Notes, Comments}; +use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums, Notes, Videos, Comments}; use Chandler\Database\DatabaseConnection; use Nette\InvalidStateException as ISE; use Bhaktaraz\RSSGenerator\Item; @@ -231,10 +231,7 @@ final class WallPresenter extends OpenVKPresenter if(!$canPost) $this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment")); - - if($_FILES["_vid_attachment"] && OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading']) - $this->flashFail("err", tr("error"), tr("video_uploads_disabled")); - + $anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"]; if($wallOwner instanceof Club && $this->postParam("as_group") === "on" && $this->postParam("force_sign") !== "on" && $anon) { $manager = $wallOwner->getManager($this->user->identity); @@ -263,8 +260,8 @@ final class WallPresenter extends OpenVKPresenter $photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album, $anon); } - if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) - $video = Video::fastMake($this->user->id, $_FILES["_vid_attachment"]["name"], $this->postParam("text"), $_FILES["_vid_attachment"], $anon); + /*if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) + $video = Video::fastMake($this->user->id, $_FILES["_vid_attachment"]["name"], $this->postParam("text"), $_FILES["_vid_attachment"], $anon);*/ } catch(\DomainException $ex) { $this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted")); } catch(ISE $ex) { @@ -295,8 +292,27 @@ final class WallPresenter extends OpenVKPresenter $this->flashFail("err", " "); } } + + $videos = []; + + if(!empty($this->postParam("videos"))) { + $un = rtrim($this->postParam("videos"), ","); + $arr = explode(",", $un); + + if(sizeof($arr) < 11) { + foreach($arr as $dat) { + $ids = explode("_", $dat); + $video = (new Videos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]); + + if(!$video || $video->isDeleted()) + continue; + + $videos[] = $video; + } + } + } - if(empty($this->postParam("text")) && !$photo && !$video && !$poll && !$note) + if(empty($this->postParam("text")) && !$photo && sizeof($videos) < 1 && !$poll && !$note) $this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big")); try { @@ -316,8 +332,9 @@ final class WallPresenter extends OpenVKPresenter if(!is_null($photo)) $post->attach($photo); - if(!is_null($video)) - $post->attach($video); + if(sizeof($videos) > 0) + foreach($videos as $vid) + $post->attach($vid); if(!is_null($poll)) $post->attach($poll); diff --git a/Web/Presenters/templates/components/textArea.xml b/Web/Presenters/templates/components/textArea.xml index f76649d6..939e5ad5 100644 --- a/Web/Presenters/templates/components/textArea.xml +++ b/Web/Presenters/templates/components/textArea.xml @@ -17,6 +17,8 @@
+
+
{var $anonEnabled = OPENVK_ROOT_CONF['openvk']['preferences']['wall']['anonymousPosting']['enable']} {if !is_null($thisUser) && !is_null($club ?? NULL) && $owner < 0} @@ -55,7 +57,7 @@
- + @@ -75,7 +77,7 @@ {_photo} - + {_video} @@ -105,6 +107,8 @@ setupWallPostInputHandlers({$textAreaId}); }); + + u("#post-buttons{$textAreaId} input[name='videos']")["nodes"].at(0).value = "" {if $graffiti} diff --git a/Web/static/css/main.css b/Web/static/css/main.css index 46ad74b0..caa49283 100644 --- a/Web/static/css/main.css +++ b/Web/static/css/main.css @@ -1466,6 +1466,12 @@ body.scrolled .toTop:hover { display: none; } +.post-has-videos { + margin-top: 11px; + margin-left: 3px; + color: #3c3c3c; +} + .post-upload::before, .post-has-poll::before, .post-has-note::before { content: " "; width: 8px; @@ -1477,6 +1483,28 @@ body.scrolled .toTop:hover { margin-left: 2px; } +.post-has-video { + padding-bottom: 4px; + cursor: pointer; +} + +.post-has-video:hover span { + text-decoration: underline; +} + +.post-has-video::before { + content: " "; + width: 14px; + height: 15px; + display: inline-block; + vertical-align: bottom; + background-image: url("/assets/packages/static/openvk/img/video.png"); + background-repeat: no-repeat; + margin: 3px; + margin-left: 2px; + margin-bottom: -1px; +} + .post-opts { margin-top: 10px; } @@ -2702,6 +2730,12 @@ body.article .floating_sidebar, body.article .page_content { font-size: 12px; } +.topGrayBlock { + background: #F0F0F0; + height: 37px; + border-bottom: 1px solid #C7C7C7; +} + .edited { color: #9b9b9b; } diff --git a/Web/static/img/video.png b/Web/static/img/video.png new file mode 100644 index 00000000..5c115f1c Binary files /dev/null and b/Web/static/img/video.png differ diff --git a/Web/static/js/al_wall.js b/Web/static/js/al_wall.js index 4c8bb933..ef3d5dba 100644 --- a/Web/static/js/al_wall.js +++ b/Web/static/js/al_wall.js @@ -264,6 +264,160 @@ async function showArticle(note_id) { u("body").addClass("article"); } +$(document).on("click", "#videoAttachment", async (e) => { + e.preventDefault() + + let body = ` +
+
+ ${tr("upload_new_video")} + +
+
+ +
+ ` + + let form = e.currentTarget.closest("form") + + MessageBox(tr("selecting_video"), body, [tr("close")], [Function.noop]); + + // styles for messageboxx + document.querySelector(".ovk-diag-body").style.padding = "0" + document.querySelector(".ovk-diag-cont").style.width = "580px" + document.querySelector(".ovk-diag-body").style.height = "335px" + + async function insertVideos(page, query = "") { + document.querySelector(".videosInsert").insertAdjacentHTML("beforeend", ``) + + let vidoses + let noVideosText = tr("no_videos") + if(query == "") { + vidoses = await API.Wall.getVideos(page) + } else { + vidoses = await API.Wall.searchVideos(page, query) + noVideosText = tr("no_videos_results") + } + + if(vidoses.count < 1) { + document.querySelector(".videosInsert").innerHTML = `${noVideosText}` + } + + let pagesCount = Math.ceil(Number(vidoses.count) / 8) + u("#loader").remove() + let insert = document.querySelector(".videosInsert") + + for(const vid of vidoses.items) { + let isAttached = (form.querySelector("input[name='videos']").value.includes(`${vid.video.owner_id}_${vid.video.id},`)) + + insert.insertAdjacentHTML("beforeend", ` +
+ + + + + + + + +
+ +
+ ${escapeHtml(vid.video.title)} +
+
+
+ + + ${ovk_proc_strtr(escapeHtml(vid.video.title), 30)} + + +
+

+ ${ovk_proc_strtr(escapeHtml(vid.video.description ?? ""), 140)} +

+ ${escapeHtml(vid.video.author_name ?? "")} +
+
+ `) + } + + if(page < pagesCount) { + document.querySelector(".videosInsert").insertAdjacentHTML("beforeend", ` +
+ more... +
`) + } + } + + $(".videosInsert").on("click", "#showMoreVideos", (e) => { + u(e.currentTarget).remove() + insertVideos(Number(e.currentTarget.dataset.page), document.querySelector(".topGrayBlock #vquery").value) + }) + + $(".topGrayBlock #vquery").on("change", async (e) => { + await new Promise(r => setTimeout(r, 1000)); + + if(e.currentTarget.value === document.querySelector(".topGrayBlock #vquery").value) { + document.querySelector(".videosInsert").innerHTML = "" + insertVideos(1, e.currentTarget.value) + return; + } else { + console.info("skipping") + } + }) + + insertVideos(1) + + function insertAttachment(id) { + let videos = form.querySelector("input[name='videos']") + + if(!videos.value.includes(id + ",")) { + if(videos.value.split(",").length > 10) { + NewNotification(tr("error"), tr("max_attached_videos")) + return false + } + + form.querySelector("input[name='videos']").value += (id + ",") + + console.info(id + " attached") + return true + } else { + form.querySelector("input[name='videos']").value = form.querySelector("input[name='videos']").value.replace(id + ",", "") + + console.info(id + " detached") + return false + } + } + + $(".videosInsert").on("click", "#attachvid", (ev) => { + // откреплено от псто + if(!insertAttachment(ev.currentTarget.dataset.attachmentdata)) { + u(`.post-has-videos .post-has-video[data-id='${ev.currentTarget.dataset.attachmentdata}']`).remove() + ev.currentTarget.innerHTML = tr("attach") + } else { + ev.currentTarget.innerHTML = tr("detach") + + form.querySelector(".post-has-videos").insertAdjacentHTML("beforeend", ` +
+ ${tr("video")} "${ovk_proc_strtr(escapeHtml(ev.currentTarget.dataset.name), 20)}" +
+ `) + + u(`#unattachVideo[data-id='${ev.currentTarget.dataset.attachmentdata}']`).on("click", (e) => { + let id = ev.currentTarget.dataset.attachmentdata + form.querySelector("input[name='videos']").value = form.querySelector("input[name='videos']").value.replace(id + ",", "") + + console.info(id + " detached") + + u(e.currentTarget).remove() + }) + } + }) +}) + $(document).on("click", "#editPost", (e) => { let post = e.currentTarget.closest("table") let content = post.querySelector(".text") diff --git a/Web/static/js/messagebox.js b/Web/static/js/messagebox.js index 45791fd3..368311dd 100644 --- a/Web/static/js/messagebox.js +++ b/Web/static/js/messagebox.js @@ -3,6 +3,7 @@ Function.noop = () => {}; function MessageBox(title, body, buttons, callbacks) { if(u(".ovk-diag-cont").length > 0) return false; + document.querySelector("html").style.overflowY = "hidden" let dialog = u( `
@@ -21,6 +22,7 @@ function MessageBox(title, body, buttons, callbacks) { let __closeDialog = () => { u("body").removeClass("dimmed"); u(".ovk-diag-cont").remove(); + document.querySelector("html").style.overflowY = "scroll" }; Reflect.apply(callbacks[callback], { diff --git a/locales/en.strings b/locales/en.strings index f09ee65e..22cee453 100644 --- a/locales/en.strings +++ b/locales/en.strings @@ -206,6 +206,7 @@ "nsfw_warning" = "This post may have NSFW-content"; "report" = "Report"; "attach" = "Attach"; +"detach" = "Detach"; "attach_photo" = "Attach photo"; "attach_video" = "Attach video"; "draw_graffiti" = "Draw graffiti"; @@ -692,6 +693,13 @@ "videos_other" = "$1 videos"; "view_video" = "View"; + +"selecting_video" = "Selecting videos"; +"upload_new_video" = "Upload new video"; +"max_attached_videos" = "Max is 10 videos"; +"no_videos" = "You don't have uploaded videos."; +"no_videos_results" = "No results."; + "change_video" = "Change video"; "unknown_video" = "This video is not supported in your version of OpenVK."; diff --git a/locales/ru.strings b/locales/ru.strings index 787f1648..2dfadb8f 100644 --- a/locales/ru.strings +++ b/locales/ru.strings @@ -186,6 +186,7 @@ "nsfw_warning" = "Данный пост может содержать 18+ контент"; "report" = "Пожаловаться"; "attach" = "Прикрепить"; +"detach" = "Открепить"; "attach_photo" = "Прикрепить фото"; "attach_video" = "Прикрепить видео"; "draw_graffiti" = "Нарисовать граффити"; @@ -652,6 +653,12 @@ "change_video" = "Изменить видеозапись"; "unknown_video" = "Эта видеозапись не поддерживается в вашей версии OpenVK."; +"selecting_video" = "Выбор видеозаписей"; +"upload_new_video" = "Загрузить новое видео"; +"max_attached_videos" = "Максимум 10 видеозаписей"; +"no_videos" = "У вас нет видео."; +"no_videos_results" = "Нет результатов."; + /* Notifications */ "feedback" = "Ответы";