diff --git a/ServiceAPI/Wall.php b/ServiceAPI/Wall.php index 467d59fe..f8d9fca2 100644 --- a/ServiceAPI/Wall.php +++ b/ServiceAPI/Wall.php @@ -2,8 +2,6 @@ namespace openvk\ServiceAPI; use openvk\Web\Models\Entities\Post; use openvk\Web\Models\Entities\User; -use openvk\Web\Models\Entities\Notifications\PostAcceptedNotification; -use openvk\Web\Models\Repositories\{Posts, Notes}; use openvk\Web\Models\Repositories\{Posts, Notes, Videos}; class Wall implements Handler @@ -101,67 +99,7 @@ class Wall implements Handler $resolve($arr); } - - function declinePost(int $id, callable $resolve, callable $reject) - { - $post = $this->posts->get($id); - if(!$post || $post->isDeleted()) - $reject(11, "No post with id=$id"); - - if($post->getSuggestionType() == 0) - $reject(19, "Post is not suggested"); - - if($post->getSuggestionType() == 2) - $reject(10, "Post is already declined"); - - if(!$post->canBePinnedBy($this->user)) - $reject(22, "Access to post denied"); - - $post->setSuggested(2); - $post->setDeleted(1); - $post->save(); - - $resolve($this->posts->getSuggestedPostsCount($post->getWallOwner()->getId())); - } - - function acceptPost(int $id, bool $sign, string $content, callable $resolve, callable $reject) - { - $post = $this->posts->get($id); - if(!$post || $post->isDeleted()) - $reject(11, "No post with id=$id"); - - if($post->getSuggestionType() == 0) - $reject(19, "Post is not suggested"); - - if($post->getSuggestionType() == 2) - $reject(10, "Post is declined"); - - if(!$post->canBePinnedBy($this->user)) - $reject(22, "Access to post denied"); - - $author = $post->getOwner(); - $flags = 0; - $flags |= 0b10000000; - - if($sign) - $flags |= 0b01000000; - - $post->setSuggested(0); - $post->setCreated(time()); - $post->setApi_Source_Name(NULL); - $post->setFlags($flags); - - if(mb_strlen($content) > 0) - $post->setContent($content); - - $post->save(); - - if($author->getId() != $this->user->getId()) - (new PostAcceptedNotification($author, $post, $post->getWallOwner()))->emit(); - $resolve(["id" => $post->getPrettyId(), "new_count" => $this->posts->getSuggestedPostsCount($post->getWallOwner()->getId())]); - } - function getVideos(int $page = 1, callable $resolve, callable $reject) { $videos = $this->videos->getByUser($this->user, $page, 8); diff --git a/Web/Models/Entities/Post.php b/Web/Models/Entities/Post.php index a43a8b47..a53ede22 100644 --- a/Web/Models/Entities/Post.php +++ b/Web/Models/Entities/Post.php @@ -264,6 +264,12 @@ class Post extends Postable if($this->getTargetWall() > 0) return $this->getPublicationTime()->timestamp() + WEEK > time() && $user->getId() == $this->getOwner(false)->getId(); + else { + if($this->isPostedOnBehalfOfGroup()) + return $this->getWallOwner()->canBeModifiedBy($user); + else + return $user->getId() == $this->getOwner(false)->getId(); + } return $user->getId() == $this->getOwner(false)->getId(); } diff --git a/Web/Presenters/InternalAPIPresenter.php b/Web/Presenters/InternalAPIPresenter.php index e2e6b50e..dca5db04 100644 --- a/Web/Presenters/InternalAPIPresenter.php +++ b/Web/Presenters/InternalAPIPresenter.php @@ -104,7 +104,7 @@ final class InternalAPIPresenter extends OpenVKPresenter } if($this->postParam("parentType", false) == "post") { - $post = (new Posts)->getPostById($owner_id, $post_id); + $post = (new Posts)->getPostById($owner_id, $post_id, true); } else { $post = (new Comments)->get($post_id); } diff --git a/Web/Presenters/WallPresenter.php b/Web/Presenters/WallPresenter.php index 39bc86a3..9c7bfaa4 100644 --- a/Web/Presenters/WallPresenter.php +++ b/Web/Presenters/WallPresenter.php @@ -2,7 +2,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, NewSuggestedPostsNotification, RepostNotification, WallPostNotification}; +use openvk\Web\Models\Entities\Notifications\{MentionNotification, NewSuggestedPostsNotification, RepostNotification, WallPostNotification, PostAcceptedNotification}; use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums, Notes, Videos, Comments, Photos}; use Chandler\Database\DatabaseConnection; use Nette\InvalidStateException as ISE; @@ -624,4 +624,93 @@ final class WallPresenter extends OpenVKPresenter "avatar" => $post->getOwner()->getAvatarUrl() ]]); } + + function renderAccept() { + $this->assertUserLoggedIn(); + $this->willExecuteWriteAction(true); + + if($_SERVER["REQUEST_METHOD"] !== "POST") { + header("HTTP/1.1 405 Method Not Allowed"); + exit("Ты дебил, это точка апи."); + } + + $id = $this->postParam("id"); + $sign = $this->postParam("sign") == 1; + $content = $this->postParam("new_content"); + + $post = (new Posts)->get((int)$id); + + if(!$post || $post->isDeleted()) + $this->flashFail("err", "Error", tr("error_accepting_invalid_post"), NULL, true); + + if($post->getSuggestionType() == 0) + $this->flashFail("err", "Error", tr("error_accepting_not_suggested_post"), NULL, true); + + if($post->getSuggestionType() == 2) + $this->flashFail("err", "Error", tr("error_accepting_declined_post"), NULL, true); + + if(!$post->canBePinnedBy($this->user->identity)) + $this->flashFail("err", "Error", "Can't accept this post.", NULL, true); + + $author = $post->getOwner(); + + $flags = 0; + $flags |= 0b10000000; + + if($sign) + $flags |= 0b01000000; + + $post->setSuggested(0); + $post->setCreated(time()); + $post->setApi_Source_Name(NULL); + $post->setFlags($flags); + + if(mb_strlen($content) > 0) + $post->setContent($content); + + $post->save(); + + if($author->getId() != $this->user->id) + (new PostAcceptedNotification($author, $post, $post->getWallOwner()))->emit(); + + $this->returnJson([ + "success" => true, + "id" => $post->getPrettyId(), + "new_count" => (new Posts)->getSuggestedPostsCount($post->getWallOwner()->getId()) + ]); + } + + function renderDecline() { + $this->assertUserLoggedIn(); + $this->willExecuteWriteAction(true); + + if($_SERVER["REQUEST_METHOD"] !== "POST") { + header("HTTP/1.1 405 Method Not Allowed"); + exit("Ты дебил, это метод апи."); + } + + $id = $this->postParam("id"); + $post = (new Posts)->get((int)$id); + + if(!$post || $post->isDeleted()) + $this->flashFail("err", "Error", tr("error_declining_invalid_post"), NULL, true); + + if($post->getSuggestionType() == 0) + $this->flashFail("err", "Error", tr("error_declining_not_suggested_post"), NULL, true); + + if($post->getSuggestionType() == 2) + $this->flashFail("err", "Error", tr("error_declining_declined_post"), NULL, true); + + if(!$post->canBePinnedBy($this->user->identity)) + $this->flashFail("err", "Error", "Can't decline this post.", NULL, true); + + $post->setSuggested(2); + $post->setDeleted(1); + $post->save(); + + $this->returnJson([ + "success" => true, + "new_count" => (new Posts)->getSuggestedPostsCount($post->getWallOwner()->getId()) + ]); + } } diff --git a/Web/routes.yml b/Web/routes.yml index 5703fd28..755ec2f3 100644 --- a/Web/routes.yml +++ b/Web/routes.yml @@ -141,6 +141,10 @@ routes: handler: "Wall->delete" - url: "/wall{num}_{num}/pin" handler: "Wall->pin" + - url: "/wall/accept" + handler: "Wall->accept" + - url: "/wall/decline" + handler: "Wall->decline" - url: "/blob_{text}/{?path}.{text}" handler: "Blob->file" placeholders: diff --git a/Web/static/js/al_suggestions.js b/Web/static/js/al_suggestions.js index 4b702cb5..1c3da3e8 100644 --- a/Web/static/js/al_suggestions.js +++ b/Web/static/js/al_suggestions.js @@ -1,3 +1,32 @@ +function endSuggestAction(new_count, post_node) { + if(document.getElementById("cound") != null) + document.getElementById("cound").innerHTML = tr("suggested_posts_in_group", new_count) + else + document.getElementById("cound_r").innerHTML = tr("suggested_by_everyone", new_count) + + if(document.querySelector("object a[href='"+location.pathname+"'] b") != null) { + document.querySelector("object a[href='"+location.pathname+"'] b").innerHTML = new_count + + if(new_count < 1) { + u("object a[href='"+location.pathname+"']").remove() + } + } + + if(new_count < 1 && document.querySelector(".sugglist") != null) { + $(".sugglist a").click() + $(".sugglist").remove() + } + + post_node.style.transition = "opacity 300ms ease-in-out"; + post_node.style.opacity = "0"; + post_node.classList.remove("post") + + setTimeout(() => {post_node.outerHTML = ""}, 300) + + if(document.querySelectorAll("#postz .post").length < 1 && new_count > 0 && document.querySelector(".paginator") != null) + loadMoreSuggestedPosts() +} + // "Опубликовать запись" $(document).on("click", "#publish_post", async (e) => { let id = Number(e.currentTarget.dataset.id) @@ -11,178 +40,122 @@ $(document).on("click", "#publish_post", async (e) => { let id = Number(e.currentTarget.dataset.id) let post; - try { - e.currentTarget.classList.add("loaded") - e.currentTarget.setAttribute("value", "") - e.currentTarget.setAttribute("id", "") - post = await API.Wall.acceptPost(id, document.getElementById("signatr").checked, document.getElementById("pooblish").value) - } catch(ex) { - switch(ex.code) { - case 11: - MessageBox(tr("error"), tr("error_accepting_invalid_post"), [tr("ok")], [Function.noop]); - break; - case 19: - MessageBox(tr("error"), tr("error_accepting_not_suggested_post"), [tr("ok")], [Function.noop]); - break; - case 10: - MessageBox(tr("error"), tr("error_accepting_declined_post"), [tr("ok")], [Function.noop]); - break; - case 22: - MessageBox(tr("error"), "Access denied", [tr("ok")], [Function.noop]); - break; - default: - MessageBox(tr("error"), "Unknown error "+ex.code+": "+ex.message, [tr("ok")], [Function.noop]); - break; - } + let formData = new FormData() + formData.append("id", id) + formData.append("sign", document.getElementById("signatr").checked ? 1 : 0) + formData.append("new_content", document.getElementById("pooblish").value) + formData.append("hash", u("meta[name=csrf]").attr("value")) - e.currentTarget.setAttribute("value", tr("publish_suggested")) - e.currentTarget.classList.remove("loaded") - e.currentTarget.setAttribute("id", "publish_post") - return 0; - } + ky.post("/wall/accept", { + hooks: { + beforeRequest: [ + (_request) => { + e.currentTarget.classList.add("loaded") + e.currentTarget.setAttribute("value", "") + e.currentTarget.setAttribute("id", "") + } + ], + afterResponse: [ + async (_request, _options, response) => { + json = await response.json() - NewNotification(tr("suggestion_succefully_published"), tr("suggestion_press_to_go"), null, () => {window.location.assign("/wall" + post.id)}); - - if(document.getElementById("cound") != null) - document.getElementById("cound").innerHTML = tr("suggested_posts_in_group", post.new_count) - else - document.getElementById("cound_r").innerHTML = tr("suggested_by_everyone", post.new_count) + if(json.success) { + NewNotification(tr("suggestion_succefully_published"), tr("suggestion_press_to_go"), null, () => {window.location.assign("/wall" + json.id)}); + endSuggestAction(json.new_count, e.currentTarget.closest("table")) + } else { + MessageBox(tr("error"), json.flash.message, [tr("ok")], [Function.noop]); + } - if(document.querySelector("object a[href='"+location.pathname+"'] b") != null) { - document.querySelector("object a[href='"+location.pathname+"'] b").innerHTML = post.new_count - - if(post.new_count < 1) { - u("object a[href='"+location.pathname+"']").remove() - } - } - - if(post.new_count < 1 && document.querySelector(".sugglist") != null) { - $(".sugglist a").click() - $(".sugglist").remove() - } - - let post_node = e.currentTarget.closest("table") - post_node.style.transition = "opacity 300ms ease-in-out"; - post_node.style.opacity = "0"; - post_node.classList.remove("post") - - setTimeout(() => {post_node.outerHTML = ""}, 300) - - if(document.querySelectorAll("#postz .post").length < 1 && post.new_count > 0 && document.querySelector(".paginator") != null) - loadMoreSuggestedPosts() + e.currentTarget.setAttribute("value", tr("publish_suggested")) + e.currentTarget.classList.remove("loaded") + e.currentTarget.setAttribute("id", "publish_post") + } + ] + }, + body: formData + }) }), Function.noop]); - document.getElementById("pooblish").innerHTML = e.currentTarget.closest("table").querySelector(".really_text").innerHTML.replace(/(<([^>]+)>)/gi, '') + document.getElementById("pooblish").innerHTML = e.currentTarget.closest("table").querySelector(".really_text").dataset.text document.querySelector(".ovk-diag-body").style.padding = "9px"; }) // "Отклонить" $(document).on("click", "#decline_post", async (e) => { let id = Number(e.currentTarget.dataset.id) - let post; - try { - e.currentTarget.classList.add("loaded") - e.currentTarget.setAttribute("value", "") - e.currentTarget.setAttribute("id", "") - post = await API.Wall.declinePost(id) - } catch(ex) { - switch(ex.code) { - case 11: - MessageBox(tr("error"), tr("error_declining_invalid_post"), [tr("ok")], [Function.noop]); - break; - case 19: - MessageBox(tr("error"), tr("error_declining_not_suggested_post"), [tr("ok")], [Function.noop]); - break; - case 10: - MessageBox(tr("error"), tr("error_declining_declined_post"), [tr("ok")], [Function.noop]); - break; - case 22: - MessageBox(tr("error"), "Access denied", [tr("ok")], [Function.noop]); - break; - default: - MessageBox(tr("error"), "Unknown error "+ex.code+": "+ex.message, [tr("ok")], [Function.noop]); - break; - } + let formData = new FormData() + formData.append("id", id) + formData.append("hash", u("meta[name=csrf]").attr("value")) - e.currentTarget.setAttribute("value", tr("decline_suggested")) - e.currentTarget.setAttribute("id", "decline_post") - e.currentTarget.classList.remove("loaded") - return 0; - } - - //NewNotification(tr("suggestion_succefully_declined"), "", null); + ky.post("/wall/decline", { + hooks: { + beforeRequest: [ + (_request) => { + e.currentTarget.classList.add("loaded") + e.currentTarget.setAttribute("value", "") + e.currentTarget.setAttribute("id", "") + } + ], + afterResponse: [ + async (_request, _options, response) => { + json = await response.json() - let post_node = e.currentTarget.closest("table") - post_node.style.transition = "opacity 300ms ease-in-out"; - post_node.style.opacity = "0"; - post_node.classList.remove("post") + if(json.success) { + endSuggestAction(json.new_count, e.currentTarget.closest("table")) + } else { + MessageBox(tr("error"), json.flash.message, [tr("ok")], [Function.noop]); + } - setTimeout(() => {post_node.outerHTML = ""}, 300) - - if(document.getElementById("cound") != null) - document.getElementById("cound").innerHTML = tr("suggested_posts_in_group", post) - else - document.getElementById("cound_r").innerHTML = tr("suggested_by_everyone", post) - - if(document.querySelector("object a[href='"+location.pathname+"'] b") != null) { - document.querySelector("object a[href='"+location.pathname+"'] b").innerHTML = post - - if(post < 1) { - u("object a[href='"+location.pathname+"']").remove() - } - } - - if(post < 1 && document.querySelector(".sugglist") != null) { - $(".sugglist a").click() - $(".sugglist").remove() - } - - if(document.querySelectorAll("#postz .post").length < 1 && post > 0 && document.querySelector(".paginator") != null) - loadMoreSuggestedPosts() + e.currentTarget.setAttribute("value", tr("decline_suggested")) + e.currentTarget.setAttribute("id", "decline_post") + e.currentTarget.classList.remove("loaded") + } + ] + }, + body: formData + }) }) -function loadMoreSuggestedPosts() -{ - let xhr = new XMLHttpRequest +function loadMoreSuggestedPosts() { let link = location.href if(!link.includes("/suggested")) { link += "/suggested" } - xhr.open("GET", link) + ky.get(link, { + hooks: { + beforeRequest: [ + (_request) => { + document.getElementById("postz").innerHTML = `` + } + ], + afterResponse: [ + async (_request, _options, response) => { + let text = await response.text() + let parser = new DOMParser() + let body = parser.parseFromString(text, "text/html") - xhr.onloadstart = () => { - document.getElementById("postz").innerHTML = `` - } + if(body.querySelectorAll(".post").length < 1) { + let url = new URL(location.href) + url.searchParams.set("p", url.searchParams.get("p") - 1) + + if(url.searchParams.get("p") < 1) { + return 0; + } - xhr.onload = () => { - let parser = new DOMParser() - let body = parser.parseFromString(xhr.responseText, "text/html").getElementById("postz") + history.pushState({}, "", url) + + loadMoreSuggestedPosts() + } - if(body.querySelectorAll(".post").length < 1) { - let url = new URL(location.href) - url.searchParams.set("p", url.searchParams.get("p") - 1) - - if(url.searchParams.get("p") < 1) { - return 0; - } - - // OVK AJAX ROUTING ?????????? - history.pushState({}, "", url) - - loadMoreSuggestedPosts() + body.querySelectorAll(".bsdn").forEach(bsdnInitElement) + document.getElementById("postz").innerHTML = body.getElementById("postz").innerHTML + } + ] } - - document.getElementById("postz").innerHTML = body.innerHTML - } - - xhr.onerror = () => { - document.getElementById("postz").innerHTML = tr("error_loading_suggest") - } - - xhr.send() + }) } // нажатие на "x предложенных записей" @@ -198,29 +171,24 @@ $(document).on("click", ".sugglist a", (e) => { // если ещё ничего не подгружалось if(document.querySelector(".insertThere").innerHTML == "") { - let xhr = new XMLHttpRequest - xhr.open("GET", e.currentTarget.href) - - xhr.onloadstart = () => { - // лоадер - document.querySelector(".insertThere").insertAdjacentHTML("afterbegin", ``) - } - - xhr.onload = () => { - let parser = new DOMParser - let result = parser.parseFromString(xhr.responseText, 'text/html').querySelector(".infContainer") - // парсинг результата и вставка постов - document.querySelector(".insertThere").innerHTML = result.innerHTML - } - - function errorl() { - document.getElementById("postz").innerHTML = tr("error_loading_suggest") - } - - xhr.onerror = () => {errorl()} - xhr.ontimeout = () => {errorl()} - - xhr.send() + ky(e.currentTarget.href, { + hooks: { + beforeRequest: [ + (_request) => { + document.querySelector(".insertThere").insertAdjacentHTML("afterbegin", ``) + } + ], + afterResponse: [ + async (_request, _options, response) => { + let parser = new DOMParser + let result = parser.parseFromString(await response.text(), 'text/html').querySelector(".infContainer") + + result.querySelectorAll(".bsdn").forEach(bsdnInitElement) + document.querySelector(".insertThere").innerHTML = result.innerHTML + } + ] + } + }) } } else { // переключение на нормальную стену @@ -232,37 +200,32 @@ $(document).on("click", ".sugglist a", (e) => { } }) -// нажатие на пагинатор у постов пъедложки +// нажатие на пагинатор у постов предложки $(document).on("click", "#postz .paginator a", (e) => { e.preventDefault() - let xhr = new XMLHttpRequest - xhr.open("GET", e.currentTarget.href) - - xhr.onloadstart = () => { - if(document.querySelector(".sugglist") != null) { - document.querySelector(".sugglist").scrollIntoView({behavior: "smooth"}) - } else { - document.querySelector(".infContainer").scrollIntoView({behavior: "smooth"}) + ky(e.currentTarget.href, { + hooks: { + beforeRequest: [ + (_request) => { + if(document.querySelector(".sugglist") != null) { + document.querySelector(".sugglist").scrollIntoView({behavior: "smooth"}) + } else { + document.querySelector(".infContainer").scrollIntoView({behavior: "smooth"}) + } + + setTimeout(() => {document.getElementById("postz").innerHTML = ``}, 500) + } + ], + afterResponse: [ + async (_request, _options, response) => { + let result = (new DOMParser).parseFromString(await response.text(), "text/html").querySelector(".infContainer") + result.querySelectorAll(".bsdn").forEach(bsdnInitElement) + + document.getElementById("postz").innerHTML = result.innerHTML + history.pushState({}, "", e.currentTarget.href) + } + ] } - // после того как долистали наверх, добавляем лоадер - setTimeout(() => {document.getElementById("postz").innerHTML = ``}, 500) - } - - xhr.onload = () => { - // опять парс - let result = (new DOMParser).parseFromString(xhr.responseText, "text/html").querySelector(".infContainer") - // опять вставка - document.getElementById("postz").innerHTML = result.innerHTML - history.pushState({}, "", e.currentTarget.href) - } - - function errorl() { - document.getElementById("postz").innerHTML = tr("error_loading_suggest") - } - - xhr.onerror = () => {errorl()} - xhr.ontimeout = () => {errorl()} - - xhr.send() + }) })