Wall: add early suggestions

This commit is contained in:
lalka2016 2023-07-30 20:03:27 +03:00
parent a2384cc231
commit 10729d8294
19 changed files with 530 additions and 26 deletions

View file

@ -2,6 +2,7 @@
namespace openvk\ServiceAPI; namespace openvk\ServiceAPI;
use openvk\Web\Models\Entities\Post; use openvk\Web\Models\Entities\Post;
use openvk\Web\Models\Entities\User; 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};
class Wall implements Handler class Wall implements Handler
@ -95,4 +96,60 @@ class Wall implements Handler
$resolve($arr); $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->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->setFlags($flags);
$post->setContent($content);
$post->save();
(new PostAcceptedNotification($author, $post, $post->getWallOwner()))->emit();
$resolve(["id" => $post->getPrettyId(), "new_count" => $this->posts->getSuggestedPostsCount($post->getWallOwner()->getId())]);
}
} }

View file

@ -18,7 +18,7 @@ use openvk\Web\Models\Repositories\Notes as NotesRepo;
final class Wall extends VKAPIRequestHandler final class Wall extends VKAPIRequestHandler
{ {
function get(int $owner_id, string $domain = "", int $offset = 0, int $count = 30, int $extended = 0): object function get(int $owner_id, string $domain = "", int $offset = 0, int $count = 30, int $extended = 0, string $filter = "all"): object
{ {
$this->requireUser(); $this->requireUser();
@ -27,7 +27,7 @@ final class Wall extends VKAPIRequestHandler
$items = []; $items = [];
$profiles = []; $profiles = [];
$groups = []; $groups = [];
$cnt = $posts->getPostCountOnUserWall($owner_id); $cnt = 0;
if ($owner_id > 0) if ($owner_id > 0)
$wallOnwer = (new UsersRepo)->get($owner_id); $wallOnwer = (new UsersRepo)->get($owner_id);
@ -41,7 +41,43 @@ final class Wall extends VKAPIRequestHandler
if(!$wallOnwer) if(!$wallOnwer)
$this->fail(15, "Access denied: wall is disabled"); // Don't search for logic here pls $this->fail(15, "Access denied: wall is disabled"); // Don't search for logic here pls
foreach($posts->getPostsFromUsersWall($owner_id, 1, $count, $offset) as $post) { $iteratorv;
switch($filter) {
case "all":
$iteratorv = $posts->getPostsFromUsersWall($owner_id, 1, $count, $offset);
$cnt = $posts->getPostCountOnUserWall($owner_id);
break;
case "owner":
$this->fail(66666, "Not implemented :(");
break;
case "others":
$this->fail(66666, "Not implemented :(");
break;
case "postponed":
$this->fail(66666, "Otlojka is not implemented :)");
break;
# В апи, походу, нету метода, который бы публиковал запись из предложки
case "suggests":
if($owner_id < 0) {
if($wallOnwer->canBeModifiedBy($this->getUser())) {
$iteratorv = $posts->getSuggestedPosts($owner_id * -1, 1, $count, $offset);
$cnt = $posts->getSuggestedPostsCount($owner_id * -1);
} else {
$iteratorv = $posts->getSuggestedPosts($owner_id * -1, 1, $count, $offset);
$cnt = $posts->getSuggestedPostsCount($owner_id * -1);
}
} else {
$this->fail(528, "Suggested posts avaiable only at groups");
}
break;
default:
$this->fail(254, "Invalid filter");
break;
}
foreach($iteratorv as $post) {
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId(); $from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
$attachments = []; $attachments = [];
@ -428,6 +464,11 @@ final class Wall extends VKAPIRequestHandler
$post->setContent($message); $post->setContent($message);
$post->setFlags($flags); $post->setFlags($flags);
$post->setApi_Source_Name($this->getPlatform()); $post->setApi_Source_Name($this->getPlatform());
if($owner_id < 0 && !$wallOwner->canBeModifiedBy($this->getUser()) && $wallOwner->getWallType() == 2) {
$post->setSuggested(1);
}
$post->save(); $post->save();
} catch(\LogicException $ex) { } catch(\LogicException $ex) {
$this->fail(100, "One of the parameters specified was missing or invalid"); $this->fail(100, "One of the parameters specified was missing or invalid");
@ -494,6 +535,10 @@ final class Wall extends VKAPIRequestHandler
if($wall > 0 && $wall !== $this->user->identity->getId()) if($wall > 0 && $wall !== $this->user->identity->getId())
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit(); (new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
if($owner_id < 0 && !$wallOwner->canBeModifiedBy($this->getUser()) && $wallOwner->getWallType() == 2) {
return (object)["post_id" => "on_view"];
}
return (object)["post_id" => $post->getVirtualId()]; return (object)["post_id" => $post->getVirtualId()];
} }

View file

@ -24,6 +24,10 @@ class Club extends RowModel
const SUBSCRIBED = 1; const SUBSCRIBED = 1;
const REQUEST_SENT = 2; const REQUEST_SENT = 2;
const WALL_CLOSED = 0;
const WALL_OPEN = 1;
const WALL_LIMITED = 2;
function getId(): int function getId(): int
{ {
return $this->getRecord()->id; return $this->getRecord()->id;
@ -46,6 +50,11 @@ class Club extends RowModel
return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURLBySizeId($size); return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURLBySizeId($size);
} }
function getWallType(): int
{
return $this->getRecord()->wall;
}
function getAvatarLink(): string function getAvatarLink(): string
{ {
$avPhoto = $this->getAvatarPhoto(); $avPhoto = $this->getAvatarPhoto();
@ -183,6 +192,14 @@ class Club extends RowModel
return true; return true;
} }
function setWall(int $type)
{
if($type > 3 || $type < 0)
throw new \LogicException("Invalid wall");
$this->stateChanges("wall", $type);
}
function isSubscriptionAccepted(User $user): bool function isSubscriptionAccepted(User $user): bool
{ {
return !is_null($this->getRecord()->related("subscriptions.follower")->where([ return !is_null($this->getRecord()->related("subscriptions.follower")->where([

View file

@ -0,0 +1,13 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\{User, Club, Post};
final class PostAcceptedNotification extends Notification
{
protected $actionCode = 6;
function __construct(User $author, Post $post, Club $group)
{
parent::__construct($author, $post, $group, time(), "");
}
}

View file

@ -246,5 +246,10 @@ class Post extends Postable
$this->save(); $this->save();
} }
function getSuggestionType()
{
return $this->getRecord()->suggested;
}
use Traits\TRichText; use Traits\TRichText;
} }

View file

@ -58,9 +58,10 @@ class Posts
} }
$sel = $this->posts->where([ $sel = $this->posts->where([
"wall" => $user, "wall" => $user,
"pinned" => false, "pinned" => false,
"deleted" => false, "deleted" => false,
"suggested" => 0,
])->order("created DESC")->limit($perPage, $offset); ])->order("created DESC")->limit($perPage, $offset);
foreach($sel as $post) foreach($sel as $post)
@ -74,6 +75,7 @@ class Posts
->where("MATCH (content) AGAINST (? IN BOOLEAN MODE)", "+$hashtag") ->where("MATCH (content) AGAINST (? IN BOOLEAN MODE)", "+$hashtag")
->where("deleted", 0) ->where("deleted", 0)
->order("created DESC") ->order("created DESC")
->where("suggested", 0)
->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE); ->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
foreach($sel as $post) foreach($sel as $post)
@ -85,14 +87,22 @@ class Posts
$hashtag = "#$hashtag"; $hashtag = "#$hashtag";
$sel = $this->posts $sel = $this->posts
->where("content LIKE ?", "%$hashtag%") ->where("content LIKE ?", "%$hashtag%")
->where("deleted", 0); ->where("deleted", 0)
->where("suggested", 0);
return sizeof($sel); return sizeof($sel);
} }
function getPostById(int $wall, int $post): ?Post function getPostById(int $wall, int $post, bool $forceSuggestion = false): ?Post
{ {
$post = $this->posts->where(['wall' => $wall, 'virtual_id' => $post])->fetch(); $post = $this->posts->where(['wall' => $wall, 'virtual_id' => $post]);
if(!$forceSuggestion) {
$post->where("suggested", 0);
}
$post = $post->fetch();
if(!is_null($post)) if(!is_null($post))
return new Post($post); return new Post($post);
else else
@ -112,7 +122,7 @@ class Posts
else else
$paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL; $paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
$result = $this->posts->where("content LIKE ?", $query)->where("deleted", 0); $result = $this->posts->where("content LIKE ?", $query)->where("deleted", 0)->where("suggested", 0);
$nnparamsCount = sizeof($notNullParams); $nnparamsCount = sizeof($notNullParams);
if($nnparamsCount > 0) { if($nnparamsCount > 0) {
@ -134,7 +144,44 @@ class Posts
function getPostCountOnUserWall(int $user): int function getPostCountOnUserWall(int $user): int
{ {
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0])); return sizeof($this->posts->where(["wall" => $user, "deleted" => 0, "suggested" => 0]));
}
function getSuggestedPosts(int $club, int $page = 1, ?int $perPage = NULL, ?int $offset = NULL): \Traversable
{
$sel = $this->posts
->where("deleted", 0)
->where("wall", $club * -1)
->order("created DESC")
->where("suggested", 1)
->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
foreach($sel as $post)
yield new Post($post);
}
function getSuggestedPostsCount(int $club)
{
return sizeof($this->posts->where(["wall" => $club * -1, "deleted" => 0, "suggested" => 1]));
}
function getSuggestedPostsByUser(int $club, int $user, int $page = 1, ?int $perPage = NULL): \Traversable
{
$sel = $this->posts
->where("deleted", 0)
->where("wall", $club * -1)
->where("owner", $user)
->order("created DESC")
->where("suggested", 1)
->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
foreach($sel as $post)
yield new Post($post);
}
function getSuggestedPostsCountByUser(int $club, int $user): int
{
return sizeof($this->posts->where(["wall" => $club * -1, "deleted" => 0, "suggested" => 1, "owner" => $user]));
} }
function getCount(): int function getCount(): int

View file

@ -3,7 +3,7 @@ namespace openvk\Web\Presenters;
use openvk\Web\Models\Entities\{Club, Photo, Post}; use openvk\Web\Models\Entities\{Club, Photo, Post};
use Nette\InvalidStateException; use Nette\InvalidStateException;
use openvk\Web\Models\Entities\Notifications\ClubModeratorNotification; use openvk\Web\Models\Entities\Notifications\ClubModeratorNotification;
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers, Topics}; use openvk\Web\Models\Repositories\{Posts, Clubs, Users, Albums, Managers, Topics};
use Chandler\Security\Authenticator; use Chandler\Security\Authenticator;
final class GroupPresenter extends OpenVKPresenter final class GroupPresenter extends OpenVKPresenter
@ -29,6 +29,14 @@ final class GroupPresenter extends OpenVKPresenter
$this->template->topics = (new Topics)->getLastTopics($club, 3); $this->template->topics = (new Topics)->getLastTopics($club, 3);
$this->template->topicsCount = (new Topics)->getClubTopicsCount($club); $this->template->topicsCount = (new Topics)->getClubTopicsCount($club);
if(!is_null($this->user->identity) && !$club->canBeModifiedBy($this->user->identity) && $club->getWallType() == 2) {
$this->template->suggestedPostsCountByUser = (new Posts)->getSuggestedPostsCountByUser($club->getId(), $this->user->id);
}
if(!is_null($this->user->identity) && $club->canBeModifiedBy($this->user->identity) && $club->getWallType() == 2) {
$this->template->suggestedPostsCountByEveryone = (new Posts)->getSuggestedPostsCount($club->getId());
}
$this->template->club = $club; $this->template->club = $club;
} }
} }
@ -192,7 +200,7 @@ final class GroupPresenter extends OpenVKPresenter
$this->willExecuteWriteAction(); $this->willExecuteWriteAction();
$club = $this->clubs->get($id); $club = $this->clubs->get($id);
if(!$club || !$club->canBeModifiedBy($this->user->identity)) if(!$club || !$club->canBeModifiedBy($this->user->identity) || $club->isDeleted())
$this->notFound(); $this->notFound();
else else
$this->template->club = $club; $this->template->club = $club;
@ -396,4 +404,68 @@ final class GroupPresenter extends OpenVKPresenter
$this->flashFail("succ", tr("information_-1"), tr("group_owner_setted", $newOwner->getCanonicalName(), $club->getName())); $this->flashFail("succ", tr("information_-1"), tr("group_owner_setted", $newOwner->getCanonicalName(), $club->getName()));
} }
function renderSuggestedThisUser(int $id)
{
$this->assertUserLoggedIn();
$club = $this->clubs->get($id);
if(!$club || method_exists($club, "isDeleted") && $club->isDeleted())
$this->notFound();
else
$this->template->club = $club;
if($club->getWallType() == 1) {
$this->flash("err", tr("error_suggestions"), tr("error_suggestions_closed"));
$this->redirect("/club".$club->getId());
}
if($club->getWallType() == 0) {
$this->flash("err", tr("error_suggestions"), tr("error_suggestions_open"));
$this->redirect("/club".$club->getId());
}
if($club->canBeModifiedBy($this->user->identity)) {
$this->flash("err", tr("error_suggestions"), "No sense");
$this->redirect("/club".$club->getId());
}
$this->template->posts = (new Posts)->getSuggestedPostsByUser($club->getId(), $this->user->id, (int) ($this->queryParam("p") ?? 1));
$this->template->count = (new Posts)->getSuggestedPostsCountByUser($club->getId(), $this->user->id);
$this->template->type = "my";
$this->template->page = (int) ($this->queryParam("p") ?? 1);
$this->template->_template = "Group/Suggested.xml";
}
function renderSuggestedAll(int $id)
{
$this->assertUserLoggedIn();
$club = $this->clubs->get($id);
if(!$club || method_exists($club, "isDeleted") && $club->isDeleted())
$this->notFound();
else
$this->template->club = $club;
if($club->getWallType() == 1) {
$this->flash("err", tr("error_suggestions"), tr("error_suggestions_closed"));
$this->redirect("/club".$club->getId());
}
if($club->getWallType() == 0) {
$this->flash("err", tr("error_suggestions"), tr("error_suggestions_open"));
$this->redirect("/club".$club->getId());
}
if(!$club->canBeModifiedBy($this->user->identity)) {
$this->flash("err", tr("error_suggestions"), tr("error_suggestions_access"));
$this->redirect("/club".$club->getId());
}
$this->template->posts = (new Posts)->getSuggestedPosts($club->getId(), (int) ($this->queryParam("p") ?? 1));
$this->template->count = (new Posts)->getSuggestedPostsCount($club->getId());
$this->template->type = "everyone";
$this->template->page = (int) ($this->queryParam("p") ?? 1);
$this->template->_template = "Group/Suggested.xml";
}
} }

View file

@ -148,6 +148,7 @@ final class WallPresenter extends OpenVKPresenter
->select("id") ->select("id")
->where("wall IN (?)", $ids) ->where("wall IN (?)", $ids)
->where("deleted", 0) ->where("deleted", 0)
->where("suggested", 0)
->order("created DESC"); ->order("created DESC");
$this->template->paginatorConf = (object) [ $this->template->paginatorConf = (object) [
"count" => sizeof($posts), "count" => sizeof($posts),
@ -167,7 +168,7 @@ final class WallPresenter extends OpenVKPresenter
$page = (int) ($_GET["p"] ?? 1); $page = (int) ($_GET["p"] ?? 1);
$pPage = min((int) ($_GET["posts"] ?? OPENVK_DEFAULT_PER_PAGE), 50); $pPage = min((int) ($_GET["posts"] ?? OPENVK_DEFAULT_PER_PAGE), 50);
$queryBase = "FROM `posts` LEFT JOIN `groups` ON GREATEST(`posts`.`wall`, 0) = 0 AND `groups`.`id` = ABS(`posts`.`wall`) WHERE (`groups`.`hide_from_global_feed` = 0 OR `groups`.`name` IS NULL) AND `posts`.`deleted` = 0"; $queryBase = "FROM `posts` LEFT JOIN `groups` ON GREATEST(`posts`.`wall`, 0) = 0 AND `groups`.`id` = ABS(`posts`.`wall`) WHERE (`groups`.`hide_from_global_feed` = 0 OR `groups`.`name` IS NULL) AND `posts`.`deleted` = 0 AND `posts`.`suggested` = 0";
if($this->user->identity->getNsfwTolerance() === User::NSFW_INTOLERANT) if($this->user->identity->getNsfwTolerance() === User::NSFW_INTOLERANT)
$queryBase .= " AND `nsfw` = 0"; $queryBase .= " AND `nsfw` = 0";
@ -305,6 +306,11 @@ final class WallPresenter extends OpenVKPresenter
$post->setAnonymous($anon); $post->setAnonymous($anon);
$post->setFlags($flags); $post->setFlags($flags);
$post->setNsfw($this->postParam("nsfw") === "on"); $post->setNsfw($this->postParam("nsfw") === "on");
if($wall < 0 && !$wallOwner->canBeModifiedBy($this->user->identity) && $wallOwner->getWallType() == 2) {
$post->setSuggested(1);
}
$post->save(); $post->save();
} catch (\LengthException $ex) { } catch (\LengthException $ex) {
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_too_big")); $this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_too_big"));
@ -334,7 +340,11 @@ final class WallPresenter extends OpenVKPresenter
if($mentionee instanceof User) if($mentionee instanceof User)
(new MentionNotification($mentionee, $post, $post->getOwner(), strip_tags($post->getText())))->emit(); (new MentionNotification($mentionee, $post, $post->getOwner(), strip_tags($post->getText())))->emit();
$this->redirect($wallOwner->getURL()); if($wall < 0 && !$wallOwner->canBeModifiedBy($this->user->identity) && $wallOwner->getWallType() == 2) {
$this->redirect("/club".$wallOwner->getId()."/suggested");
} else {
$this->redirect($wallOwner->getURL());
}
} }
function renderPost(int $wall, int $post_id): void function renderPost(int $wall, int $post_id): void
@ -436,7 +446,7 @@ final class WallPresenter extends OpenVKPresenter
$this->assertUserLoggedIn(); $this->assertUserLoggedIn();
$this->willExecuteWriteAction(); $this->willExecuteWriteAction();
$post = $this->posts->getPostById($wall, $post_id); $post = $this->posts->getPostById($wall, $post_id, true);
if(!$post) if(!$post)
$this->notFound(); $this->notFound();
$user = $this->user->id; $user = $this->user->id;

View file

@ -77,7 +77,12 @@
<span class="nobold">{_wall}: </span> <span class="nobold">{_wall}: </span>
</td> </td>
<td> <td>
<input type="checkbox" name="wall" value="1" n:attr="checked => $club->canPost()" /> {_group_allow_post_for_everyone}<br> <select name="wall">
<option value="1" n:attr="selected => $club->getWallType() == 1" /> {_group_allow_post_for_everyone}</option>
<option value="2" n:attr="selected => $club->getWallType() == 2" /> {_group_limited_post}</option>
<option value="0" n:attr="selected => $club->getWallType() == 0" /> {_group_closed_post}</option>
<select>
<input type="checkbox" name="hide_from_global_feed" value="1" n:attr="checked => $club->isHideFromGlobalFeedEnabled()" /> {_group_hide_from_global_feed} <input type="checkbox" name="hide_from_global_feed" value="1" n:attr="checked => $club->isHideFromGlobalFeedEnabled()" /> {_group_hide_from_global_feed}
</td> </td>
</tr> </tr>

View file

@ -0,0 +1,26 @@
{extends "../@layout.xml"}
{block title}{_suggested} {$club->getCanonicalName()}{/block}
{block header}
<a href="{$club->getURL()}">{$club->getName()}</a>
» {if $type == "my"}{_suggested_posts_by_you}{else}{_suggested_posts_by_everyone}{/if}
{/block}
{block content}
{if $count < 1}
{include "../components/error.xml", title => "", description => $type == "my" ? tr("no_suggested_posts_by_you") : tr("no_suggested_posts_by_people")}
{else}
<h4 id="cound">{if $type == "my"}{tr("x_suggested_posts_in_group_by_you", $count)}{else}{tr("x_suggested_posts_in_group", $count)}{/if}</h4>
{foreach $posts as $post}
{include "../components/post/microblogpost.xml", post => $post, commentSection => false, suggestion => true, forceNoCommentsLink => true, forceNoPinLink => true, forceNoLike => true, forceNoShareLink => true}
{/foreach}
{/if}
{include "../components/paginator.xml", conf => (object) [
"page" => $page,
"count" => $count,
"amount" => sizeof($posts),
"perPage" => OPENVK_DEFAULT_PER_PAGE,
"atBottom" => true,
]}
{/block}

View file

@ -91,7 +91,19 @@
</div> </div>
</div> </div>
<div n:if="!is_null($suggestedPostsCountByUser) && $suggestedPostsCountByUser > 0" class="sugglist">
<a href="/club{$club->getId()}/suggested">{tr("show_suggested_posts", $suggestedPostsCountByUser)}</a>
</div>
<div n:if="!is_null($suggestedPostsCountByEveryone) && $suggestedPostsCountByEveryone > 0" class="sugglist">
<a href="/club{$club->getId()}/suggested/all">{tr("show_suggested_posts_everyone", $suggestedPostsCountByEveryone)}</a>
</div>
{presenter "openvk!Wall->wallEmbedded", -$club->getId()} {presenter "openvk!Wall->wallEmbedded", -$club->getId()}
<script n:if="isset($thisUser) && $club->getWallType() == 2 && !$club->canBeModifiedBy($thisUser)">
document.querySelector("textarea").setAttribute("placeholder", tr("suggest_new"))
</script>
</div> </div>
<div class="right_small_block"> <div class="right_small_block">
{var $avatarPhoto = $club->getAvatarPhoto()} {var $avatarPhoto = $club->getAvatarPhoto()}

View file

@ -0,0 +1,7 @@
{var $club = $notification->getModel(1)}
{var $post = $notification->getModel(0)}
{_group}
<a href="{$club->getURL()}"><b>{$club->getName()}</b></a>
{_nt_accepted_your_post}
<a href="/wall{$post->getPrettyId()}"><b>{_nt_post_small}</b></a>.

View file

@ -65,7 +65,7 @@
</div> </div>
<div class="post-content" id="{$post->getPrettyId()}"> <div class="post-content" id="{$post->getPrettyId()}">
<div class="text" id="text{$post->getPrettyId()}"> <div class="text" id="text{$post->getPrettyId()}">
{$post->getText()|noescape} <span class="really_text">{$post->getText()|noescape}</span>
<div n:ifcontent class="attachments_b"> <div n:ifcontent class="attachments_b">
<div class="attachment" n:foreach="$post->getChildren() as $attachment" data-localized-nsfw-text="{_nsfw_warning}"> <div class="attachment" n:foreach="$post->getChildren() as $attachment" data-localized-nsfw-text="{_nsfw_warning}">
@ -88,7 +88,7 @@
</div> </div>
</div> </div>
<div class="post-menu" n:if="$compact == false"> <div class="post-menu" n:if="$compact == false">
<a href="/wall{$post->getPrettyId()}" class="date">{$post->getPublicationTime()}</a> <a href="{if !$suggestion}/wall{$post->getPrettyId()}{else}javascript:void(0){/if}" class="date">{$post->getPublicationTime()}</a>
<a n:if="!empty($platform)" class="client_app" data-app-tag="{$platform}" data-app-name="{$platformDetails['name']}" data-app-url="{$platformDetails['url']}" data-app-img="{$platformDetails['img']}"> <a n:if="!empty($platform)" class="client_app" data-app-tag="{$platform}" data-app-name="{$platformDetails['name']}" data-app-url="{$platformDetails['url']}" data-app-img="{$platformDetails['img']}">
<img src="/assets/packages/static/openvk/img/app_icons_mini/{$post->getPlatform(this)}.svg"> <img src="/assets/packages/static/openvk/img/app_icons_mini/{$post->getPlatform(this)}.svg">
</a> </a>
@ -124,6 +124,10 @@
{include "../textArea.xml", route => $commentsURL, postOpts => false, graffiti => (bool) ovkGetQuirk("comments.allow-graffiti"), post => $post, club => $club} {include "../textArea.xml", route => $commentsURL, postOpts => false, graffiti => (bool) ovkGetQuirk("comments.allow-graffiti"), post => $post, club => $club}
</div> </div>
</div> </div>
<div n:if="$suggestion && $post->canBePinnedBy($thisUser ?? NULL)" class="suggestionControls">
<input type="button" class="button" id="publish_post" data-id="{$post->getId()}" value="{_publish_suggested}">
<input type="button" class="button" id="decline_post" data-id="{$post->getId()}" value="{_decline_suggested}">
</div>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View file

@ -201,6 +201,10 @@ routes:
handler: "Group->admin" handler: "Group->admin"
- url: "/club{num}/setAdmin" - url: "/club{num}/setAdmin"
handler: "Group->modifyAdmin" handler: "Group->modifyAdmin"
- url: "/club{num}/suggested"
handler: "Group->suggestedThisUser"
- url: "/club{num}/suggested/all"
handler: "Group->suggestedAll"
- url: "/groups{num}" - url: "/groups{num}"
handler: "User->groups" handler: "User->groups"
- url: "/groups_pin" - url: "/groups_pin"

View file

@ -2696,3 +2696,21 @@ body.article .floating_sidebar, body.article .page_content {
right: 22px; right: 22px;
font-size: 12px; font-size: 12px;
} }
.sugglist {
padding-bottom: 5px;
padding-top: 5px;
margin-bottom: -5px;
padding-left: 9px;
border-top: 1px solid gray;
background-color: #e6e6e6;
margin-top: 5px;
}
.sugglist a {
color: #626262;
}
.suggestionControls {
text-align: center;
}

View file

@ -192,7 +192,7 @@ tippy(".client_app", {
function addNote(textareaId, nid) function addNote(textareaId, nid)
{ {
if(nid > 0) { if(nid > 0) {
note.value = nid document.getElementById("note").value = nid
let noteObj = document.querySelector("#nd"+nid) let noteObj = document.querySelector("#nd"+nid)
let nortd = document.querySelector("#post-buttons"+textareaId+" .post-has-note"); let nortd = document.querySelector("#post-buttons"+textareaId+" .post-has-note");
@ -200,7 +200,7 @@ function addNote(textareaId, nid)
nortd.innerHTML = `${tr("note")} ${escapeHtml(noteObj.dataset.name)}` nortd.innerHTML = `${tr("note")} ${escapeHtml(noteObj.dataset.name)}`
} else { } else {
note.value = "none" document.getElementById("note").value = "none"
let nortd = document.querySelector("#post-buttons"+textareaId+" .post-has-note"); let nortd = document.querySelector("#post-buttons"+textareaId+" .post-has-note");
nortd.style.display = "none" nortd.style.display = "none"
@ -227,7 +227,7 @@ async function attachNote(id)
${tr("select_or_create_new")} ${tr("select_or_create_new")}
<div id="notesList">` <div id="notesList">`
if(note.value != "none") { if(document.getElementById("note").value != "none") {
body += ` body += `
<div class="ntSelect" onclick="addNote(${id}, 0)"> <div class="ntSelect" onclick="addNote(${id}, 0)">
<span>${tr("do_not_attach_note")}</span> <span>${tr("do_not_attach_note")}</span>
@ -263,3 +263,85 @@ async function showArticle(note_id) {
u("body").removeClass("dimmed"); u("body").removeClass("dimmed");
u("body").addClass("article"); u("body").addClass("article");
} }
$(document).on("click", "#publish_post", async (e) => {
let id = Number(e.currentTarget.dataset.id)
let post;
let body = `
<textarea id="pooblish" style="max-height:500px;resize:vertical;min-height:54px;"></textarea>
<label><input type="checkbox" id="signatr" checked>${tr("add_signature")}</label>
`
MessageBox(tr("publishing_suggested_post"), body, [tr("publish"), tr("cancel")], [(async () => {
let id = Number(e.currentTarget.dataset.id)
let post;
try {
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_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;
}
return 0;
}
NewNotification(tr("suggestion_succefully_published"), tr("suggestion_press_to_go"), null, () => {window.location.assign("/wall" + post.id)});
document.getElementById("cound").innerHTML = tr("x_suggested_posts_in_group", post.new_count)
e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode.outerHTML = ""
}), Function.noop]);
document.getElementById("pooblish").innerHTML = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode.querySelector(".really_text").innerHTML
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.parentNode.parentNode.insertAdjacentHTML("afterbegin", `<img id="deleteMe" src="/assets/packages/static/openvk/img/loading_mini.gif">`)
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;
}
return 0;
} finally {
u("#deleteMe").remove()
}
// а хули
NewNotification(tr("suggestion_succefully_declined"), "", null);
e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode.outerHTML = ""
document.getElementById("cound").innerHTML = tr("x_suggested_posts_in_group", post)
})

View file

@ -0,0 +1 @@
ALTER TABLE `posts` ADD `suggested` TINYINT(2) UNSIGNED NOT NULL DEFAULT '0' AFTER `deleted`;

View file

@ -260,6 +260,7 @@
/* Group */ /* Group */
"group" = "Group";
"name_group" = "Name"; "name_group" = "Name";
"subscribe" = "Subscribe"; "subscribe" = "Subscribe";
"unsubscribe" = "Unsubscribe"; "unsubscribe" = "Unsubscribe";
@ -297,8 +298,38 @@
"set_comment" = "Set comment"; "set_comment" = "Set comment";
"hidden_yes" = "Hidden: Yes"; "hidden_yes" = "Hidden: Yes";
"hidden_no" = "Hidden: No"; "hidden_no" = "Hidden: No";
"group_allow_post_for_everyone" = "Allow posting for everyone"; "group_allow_post_for_everyone" = "Open";
"group_limited_post" = "Suggestions";
"group_closed_post" = "Closed";
"suggest_new" = "Suggest post";
"show_suggested_posts" = "$1 suggested posts by you";
"show_suggested_posts_everyone" = "$1 suggested posts";
"group_hide_from_global_feed" = "Don't display posts in the global feed"; "group_hide_from_global_feed" = "Don't display posts in the global feed";
"suggested_posts_by_you" = "Suggested posts by you";
"suggested_posts_by_everyone" = "Suggested posts";
"suggested" = "Suggested";
"suggested_posts_everyone" = "Suggested by users posts";
"no_suggested_posts_by_you" = "You haven't suggested posts to this group yet.";
"no_suggested_posts_by_people" = "No posts have been suggested to this group yet.";
"publish_suggested" = "Accept";
"decline_suggested" = "Decline";
"publishing_suggested_post" = "Publishing suggested post";
"x_suggested_posts_in_group" = "This group has $1 suggested posts";
"x_suggested_posts_in_group_by_you" = "You suggested $1 posts to this group";
"suggestion_succefully_published" = "Post successfully published";
"suggestion_succefully_declined" = "Post successfully declined";
"suggestion_press_to_go" = "Click to show him";
"error_declining_invalid_post" = "Error when declining post: post does not exists";
"error_declining_not_suggested_post" = "Error when declining post: post is not suggested";
"error_declining_declined_post" = "Error when declining post: post is already declined";
"error_accepting_invalid_post" = "Error when accepting post: post does not exists";
"error_accepting_not_suggested_post" = "Error when accepting post: post is not suggested";
"error_accepting_declined_post" = "Error when accepting post: cant accept declined post";
"statistics" = "Statistics"; "statistics" = "Statistics";
"group_administrators_list" = "Admins list"; "group_administrators_list" = "Admins list";
"group_display_only_creator" = "Display only group creator"; "group_display_only_creator" = "Display only group creator";
@ -331,6 +362,11 @@
"search_by_groups" = "Search by groups"; "search_by_groups" = "Search by groups";
"search_group_desc" = "Here you can browse through the existing groups and choose a group to suit your needs..."; "search_group_desc" = "Here you can browse through the existing groups and choose a group to suit your needs...";
"error_suggestions" = "Error accessing to suggestions";
"error_suggestions_closed" = "This group has closed wall.";
"error_suggestions_open" = "This group has open wall.";
"error_suggestions_access" = "Only group's administrators can view all suggested posts.";
/* Albums */ /* Albums */
"create" = "Create"; "create" = "Create";
@ -675,6 +711,7 @@
"nt_shared_yours" = "shared your"; "nt_shared_yours" = "shared your";
"nt_commented_yours" = "commented"; "nt_commented_yours" = "commented";
"nt_written_on_your_wall" = "wrote on your wall"; "nt_written_on_your_wall" = "wrote on your wall";
"nt_accepted_your_post" = "accepted your suggested";
"nt_made_you_admin" = "appointed you in the community"; "nt_made_you_admin" = "appointed you in the community";
"nt_from" = "from"; "nt_from" = "from";
@ -694,6 +731,8 @@
"nt_mention_in_note" = "in discussion of this note"; "nt_mention_in_note" = "in discussion of this note";
"nt_mention_in_topic" = "in the discussion"; "nt_mention_in_topic" = "in the discussion";
"nt_post_small" = "post";
/* Time */ /* Time */
"time_at_sp" = " at "; "time_at_sp" = " at ";

View file

@ -245,6 +245,7 @@
/* Group */ /* Group */
"group" = "Сообщество";
"name_group" = "Название"; "name_group" = "Название";
"subscribe" = "Подписаться"; "subscribe" = "Подписаться";
"unsubscribe" = "Отписаться"; "unsubscribe" = "Отписаться";
@ -281,8 +282,40 @@
"set_comment" = "Изменить комментарий"; "set_comment" = "Изменить комментарий";
"hidden_yes" = "Скрыт: Да"; "hidden_yes" = "Скрыт: Да";
"hidden_no" = "Скрыт: Нет"; "hidden_no" = "Скрыт: Нет";
"group_allow_post_for_everyone" = "Разрешить публиковать записи всем";
"group_allow_post_for_everyone" = "Открытая";
"group_limited_post" = "Предложка";
"group_closed_post" = "Закрытая";
"suggest_new" = "Предложить новость";
"show_suggested_posts" = "$1 предложенных вами записей";
"show_suggested_posts_everyone" = "$1 предложенных записей";
"group_hide_from_global_feed" = "Не отображать публикации в глобальной ленте"; "group_hide_from_global_feed" = "Не отображать публикации в глобальной ленте";
"suggested_posts_by_you" = "Предложенные вами записи";
"suggested_posts_by_everyone" = "Предложенные записи";
"suggested" = "Предложено";
"suggested_posts_everyone" = "Предложенные пользователями записи";
"no_suggested_posts_by_you" = "Вы ещё не предлагали записей в эту группу.";
"no_suggested_posts_by_people" = "В эту группу ещё не предлагали записей.";
"publish_suggested" = "Опубликовать запись";
"decline_suggested" = "Отклонить";
"publishing_suggested_post" = "Публикация предложенной записи";
"x_suggested_posts_in_group" = "В эту группу предложили $1 записей";
"x_suggested_posts_in_group_by_you" = "Вы предложили в эту группу $1 записей";
"suggestion_succefully_published" = "Запись успешно опубликована";
"suggestion_succefully_declined" = "Запись успешно отклонена";
"suggestion_press_to_go" = "Нажмите, чтобы перейти к ней";
"error_declining_invalid_post" = "Не удалость отклонить пост: поста не существует";
"error_declining_not_suggested_post" = "Не удалость отклонить пост: пост не из предложки";
"error_declining_declined_post" = "Не удалость отклонить пост: пост уже отклонён";
"error_accepting_invalid_post" = "Не удалость принять пост: поста не существует";
"error_accepting_not_suggested_post" = "Не удалость принять пост: пост не из предложки";
"error_accepting_declined_post" = "Не удалость принять пост: пост отклонён";
"statistics" = "Статистика"; "statistics" = "Статистика";
"group_administrators_list" = "Список админов"; "group_administrators_list" = "Список админов";
"group_display_only_creator" = "Отображать только создателя группы"; "group_display_only_creator" = "Отображать только создателя группы";
@ -315,6 +348,11 @@
"search_by_groups" = "Поиск по группам"; "search_by_groups" = "Поиск по группам";
"search_group_desc" = "Здесь Вы можете просмотреть существующие группы и выбрать группу себе по вкусу..."; "search_group_desc" = "Здесь Вы можете просмотреть существующие группы и выбрать группу себе по вкусу...";
"error_suggestions" = "Ошибка доступа к предложенным";
"error_suggestions_closed" = "У этой группы закрытая стена.";
"error_suggestions_open" = "У этой группы открытая стена.";
"error_suggestions_access" = "Просматривать все предложенные записи могут только администраторы группы.";
/* Albums */ /* Albums */
"create" = "Создать"; "create" = "Создать";
@ -632,6 +670,7 @@
"nt_shared_yours" = "поделился(-ась) вашим"; "nt_shared_yours" = "поделился(-ась) вашим";
"nt_commented_yours" = "оставил(а) комментарий под"; "nt_commented_yours" = "оставил(а) комментарий под";
"nt_written_on_your_wall" = "написал(а) на вашей стене"; "nt_written_on_your_wall" = "написал(а) на вашей стене";
"nt_accepted_your_post" = "опубликовало вашу предложенную";
"nt_made_you_admin" = "назначил(а) вас руководителем сообщества"; "nt_made_you_admin" = "назначил(а) вас руководителем сообщества";
"nt_from" = "от"; "nt_from" = "от";
"nt_yours_adjective" = "вашим"; "nt_yours_adjective" = "вашим";
@ -649,6 +688,7 @@
"nt_mention_in_video" = "в обсуждении видеозаписи"; "nt_mention_in_video" = "в обсуждении видеозаписи";
"nt_mention_in_note" = "в обсуждении заметки"; "nt_mention_in_note" = "в обсуждении заметки";
"nt_mention_in_topic" = "в обсуждении"; "nt_mention_in_topic" = "в обсуждении";
"nt_post_small" = "запись";
/* Time */ /* Time */