Add mention notifications

Draft implementation of mention notif
This commit is contained in:
celestora 2022-12-10 21:33:13 +02:00
parent 475f637413
commit 8a893daec0
18 changed files with 141 additions and 8 deletions

View file

@ -1,13 +1,14 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications; namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\Postable;
use openvk\Web\Models\Entities\User; use openvk\Web\Models\Entities\User;
final class MentionNotification extends Notification final class MentionNotification extends Notification
{ {
protected $actionCode = 4; protected $actionCode = 4;
function __construct(User $recipient, User $target, User $mentioner) function __construct(User $recipient, Postable $discussionHost, $mentioner, string $quote = "")
{ {
parent::__construct($recipient, $target, $mentioner, time(), ""); parent::__construct($recipient, $mentioner, $discussionHost, time(), $quote);
} }
} }

View file

@ -30,6 +30,11 @@ class Notification
return (int) json_decode(file_get_contents(__DIR__ . "/../../../../data/modelCodes.json"), true)[get_class($model)]; return (int) json_decode(file_get_contents(__DIR__ . "/../../../../data/modelCodes.json"), true)[get_class($model)];
} }
function reverseModelOrder(): bool
{
return false;
}
function getActionCode(): int function getActionCode(): int
{ {
return $this->actionCode; return $this->actionCode;

View file

@ -1,5 +1,6 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
namespace openvk\Web\Models\Entities\Traits; namespace openvk\Web\Models\Entities\Traits;
use openvk\Web\Models\Repositories\{Users, Clubs};
use Wkhooy\ObsceneCensorRus; use Wkhooy\ObsceneCensorRus;
trait TRichText trait TRichText
@ -51,6 +52,62 @@ trait TRichText
return preg_replace("%[\x{0300}-\x{036F}]{3,}%Xu", "<EFBFBD>", $text); return preg_replace("%[\x{0300}-\x{036F}]{3,}%Xu", "<EFBFBD>", $text);
} }
function resolveMentions(array $skipUsers = []): \Traversable
{
$contentColumn = property_exists($this, "overrideContentColumn") ? $this->overrideContentColumn : "content";
$text = $this->getRecord()->{$contentColumn};
$text = preg_replace("%@([A-Za-z0-9]++) \(((?:[\p{L&}\p{Lo} 0-9]\p{Mn}?)++)\)%Xu", "[$1|$2]", $text);
$text = preg_replace("%([\n\r\s]|^)(@([A-Za-z0-9]++))%Xu", "$1[$3|@$3]", $text);
$resolvedUsers = $skipUsers;
$resolvedClubs = [];
preg_match_all("%\[([A-Za-z0-9]++)\|((?:[\p{L&}\p{Lo} 0-9@]\p{Mn}?)++)\]%Xu", $text, $links, PREG_PATTERN_ORDER);
foreach($links[1] as $link) {
if(preg_match("%^id([0-9]++)$%", $link, $match)) {
$uid = (int) $match[1];
if(in_array($uid, $resolvedUsers))
continue;
$resolvedUsers[] = $uid;
$maybeUser = (new Users)->get($uid);
if($maybeUser)
yield $maybeUser;
} else if(preg_match("%^(?:club|public|event)([0-9]++)$%", $link, $match)) {
$cid = (int) $match[1];
if(in_array($cid, $resolvedClubs))
continue;
$resolvedClubs[] = $cid;
$maybeClub = (new Clubs)->get($cid);
if($maybeClub)
yield $maybeClub;
} else {
$maybeUser = (new Users)->getByShortURL($link);
if($maybeUser) {
$uid = $maybeUser->getId();
if(in_array($uid, $resolvedUsers))
continue;
else
$resolvedUsers[] = $uid;
yield $maybeUser;
continue;
}
$maybeClub = (new Clubs)->getByShortURL($link);
if($maybeClub) {
$cid = $maybeClub->getId();
if(in_array($cid, $resolvedClubs))
continue;
else
$resolvedClubs[] = $cid;
yield $maybeClub;
}
}
}
}
function getText(bool $html = true): string function getText(bool $html = true): string
{ {
$contentColumn = property_exists($this, "overrideContentColumn") ? $this->overrideContentColumn : "content"; $contentColumn = property_exists($this, "overrideContentColumn") ? $this->overrideContentColumn : "content";
@ -59,7 +116,6 @@ trait TRichText
$proc = iconv_strlen($this->getRecord()->{$contentColumn}) <= OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["processingLimit"]; $proc = iconv_strlen($this->getRecord()->{$contentColumn}) <= OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["processingLimit"];
if($html) { if($html) {
if($proc) { if($proc) {
$rel = $this->isAd() ? "sponsored" : "ugc";
$text = $this->formatLinks($text); $text = $this->formatLinks($text);
$text = preg_replace("%@([A-Za-z0-9]++) \(((?:[\p{L&}\p{Lo} 0-9]\p{Mn}?)++)\)%Xu", "[$1|$2]", $text); $text = preg_replace("%@([A-Za-z0-9]++) \(((?:[\p{L&}\p{Lo} 0-9]\p{Mn}?)++)\)%Xu", "[$1|$2]", $text);
$text = preg_replace("%([\n\r\s]|^)(@([A-Za-z0-9]++))%Xu", "$1[$3|@$3]", $text); $text = preg_replace("%([\n\r\s]|^)(@([A-Za-z0-9]++))%Xu", "$1[$3|@$3]", $text);

View file

@ -1,6 +1,6 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
namespace openvk\Web\Presenters; namespace openvk\Web\Presenters;
use openvk\Web\Models\Entities\{Comment, Photo, Video, User, Topic, Post}; use openvk\Web\Models\Entities\{Comment, Notifications\MentionNotification, Photo, Video, User, Topic, Post};
use openvk\Web\Models\Entities\Notifications\CommentNotification; use openvk\Web\Models\Entities\Notifications\CommentNotification;
use openvk\Web\Models\Repositories\{Comments, Clubs}; use openvk\Web\Models\Repositories\{Comments, Clubs};
@ -105,6 +105,11 @@ final class CommentPresenter extends OpenVKPresenter
if($entity->getOwner()->getId() !== $this->user->identity->getId()) if($entity->getOwner()->getId() !== $this->user->identity->getId())
if(($owner = $entity->getOwner()) instanceof User) if(($owner = $entity->getOwner()) instanceof User)
(new CommentNotification($owner, $comment, $entity, $this->user->identity))->emit(); (new CommentNotification($owner, $comment, $entity, $this->user->identity))->emit();
$mentions = iterator_to_array($comment->resolveMentions([$this->user->identity->getId()]));
foreach($mentions as $mentionee)
if($mentionee instanceof User)
(new MentionNotification($mentionee, $entity, $comment->getOwner(), strip_tags($comment->getText())))->emit();
$this->flashFail("succ", "Комментарий добавлен", "Ваш комментарий появится на странице."); $this->flashFail("succ", "Комментарий добавлен", "Ваш комментарий появится на странице.");
} }

View file

@ -2,7 +2,7 @@
namespace openvk\Web\Presenters; namespace openvk\Web\Presenters;
use openvk\Web\Models\Exceptions\TooMuchOptionsException; use openvk\Web\Models\Exceptions\TooMuchOptionsException;
use openvk\Web\Models\Entities\{Poll, Post, Photo, Video, Club, User}; use openvk\Web\Models\Entities\{Poll, Post, Photo, Video, Club, User};
use openvk\Web\Models\Entities\Notifications\{RepostNotification, WallPostNotification}; use openvk\Web\Models\Entities\Notifications\{MentionNotification, RepostNotification, WallPostNotification};
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums}; use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums};
use Chandler\Database\DatabaseConnection; use Chandler\Database\DatabaseConnection;
use Nette\InvalidStateException as ISE; use Nette\InvalidStateException as ISE;
@ -305,6 +305,11 @@ final class WallPresenter extends OpenVKPresenter
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();
$mentions = iterator_to_array($post->resolveMentions([$this->user->identity->getId()]));
foreach($mentions as $mentionee)
if($mentionee instanceof User)
(new MentionNotification($mentionee, $post, $post->getOwner(), strip_tags($post->getText())))->emit();
$this->redirect($wallOwner->getURL()); $this->redirect($wallOwner->getURL());
} }

View file

@ -24,9 +24,14 @@
<table class="post post-divider" border="0" style="font-size: 11px;" n:foreach="$data as $dat"> <table class="post post-divider" border="0" style="font-size: 11px;" n:foreach="$data as $dat">
<tbody> <tbody>
<tr> <tr>
{var $sxModel = $dat->getModel(1)}
{if !(method_exists($sxModel, "getURL") && method_exists($sxModel, "getAvatarUrl"))}
{var $sxModel = $dat->getModel(0)}
{/if}
<td width="54" valign="top"> <td width="54" valign="top">
<a href="/sysop"> <a href="/{$sxModel->getURL()}">
<img src="{$dat->getModel(1)->getAvatarUrl('miniscule')}" width=50 /> <img src="{$sxModel->getAvatarUrl('miniscule')}" width=50 />
</a> </a>
</td> </td>
<td width="100%" valign="top"> <td width="100%" valign="top">
@ -53,4 +58,4 @@
{include "../components/nothing.xml"} {include "../components/nothing.xml"}
{/ifset} {/ifset}
{/if} {/if}
{/block} {/block}

View file

@ -0,0 +1,4 @@
{var $user = $notification->getModel(0)}
{var $post = $notification->getModel(1)}
{_nt_you_were_mentioned_u} <a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a> {$notification->getDateTime()} <a href="/note{$post->getPrettyId()}"><b>{_nt_mention_in_note}</b></a>: "{$notification->getData()}"

View file

@ -0,0 +1,4 @@
{var $user = $notification->getModel(0)}
{var $post = $notification->getModel(1)}
{_nt_you_were_mentioned_u} <a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a> {$notification->getDateTime()} <a href="/photo{$post->getURL()}"><b>{_nt_mention_in_photo}</b></a>: "{$notification->getData()}"

View file

@ -0,0 +1,4 @@
{var $user = $notification->getModel(0)}
{var $post = $notification->getModel(1)}
{_nt_you_were_mentioned_u} <a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a> {$notification->getDateTime()} <a href="/wall{$post->getPrettyId()}"><b>{_nt_mention_in_post_or_comms}</b></a>: "{$notification->getData()}"

View file

@ -0,0 +1,4 @@
{var $user = $notification->getModel(0)}
{var $post = $notification->getModel(1)}
{_nt_you_were_mentioned_u} <a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a> {$notification->getDateTime()} <a href="/video{$post->getPrettyId()}"><b>{_nt_mention_in_video}</b></a>: "{$notification->getData()}"

View file

@ -0,0 +1,4 @@
{var $user = $notification->getModel(0)}
{var $post = $notification->getModel(1)}
{_nt_you_were_mentioned_u} <a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a> {$notification->getDateTime()} <a href="/topic{$post->getPrettyId()}"><b>{_nt_mention_in_topic}</b></a>: "{$notification->getData()}"

View file

@ -0,0 +1,4 @@
{var $user = $notification->getModel(0)}
{var $post = $notification->getModel(1)}
{_nt_you_were_mentioned_g} <a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a> {$notification->getDateTime()} <a href="/note{$post->getPrettyId()}"><b>{_nt_mention_in_note}</b></a>: "{$notification->getData()}"

View file

@ -0,0 +1,4 @@
{var $user = $notification->getModel(0)}
{var $post = $notification->getModel(1)}
{_nt_you_were_mentioned_g} <a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a> {$notification->getDateTime()} <a href="/photo{$post->getURL()}"><b>{_nt_mention_in_photo}</b></a>: "{$notification->getData()}"

View file

@ -0,0 +1,4 @@
{var $user = $notification->getModel(0)}
{var $post = $notification->getModel(1)}
{_nt_you_were_mentioned_g} <a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a> {$notification->getDateTime()} <a href="/wall{$post->getPrettyId()}"><b>{_nt_mention_in_post_or_comms}</b></a>: "{$notification->getData()}"

View file

@ -0,0 +1,4 @@
{var $user = $notification->getModel(0)}
{var $post = $notification->getModel(1)}
{_nt_you_were_mentioned_g} <a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a> {$notification->getDateTime()} <a href="/video{$post->getPrettyId()}"><b>{_nt_mention_in_video}</b></a>: "{$notification->getData()}"

View file

@ -0,0 +1,4 @@
{var $user = $notification->getModel(0)}
{var $post = $notification->getModel(1)}
{_nt_you_were_mentioned_g} <a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a> {$notification->getDateTime()} <a href="/topic{$post->getPrettyId()}"><b>{_nt_mention_in_topic}</b></a>: "{$notification->getData()}"

View file

@ -619,6 +619,14 @@
"nt_photo_instrumental" = "photo"; "nt_photo_instrumental" = "photo";
"nt_topic_instrumental" = "topic"; "nt_topic_instrumental" = "topic";
"nt_you_were_mentioned_u" = "You were mentioned by user";
"nt_you_were_mentioned_g" = "You were mentioned by group";
"nt_mention_in_post_or_comms" = "in post or one of its discussion threads";
"nt_mention_in_photo" = "in discussion of this photo";
"nt_mention_in_video" = "in discussion of this video";
"nt_mention_in_note" = "in discussion of this note";
"nt_mention_in_topic" = "in the discussion";
/* Time */ /* Time */
"time_at_sp" = " at "; "time_at_sp" = " at ";

View file

@ -572,6 +572,14 @@
"nt_photo_instrumental" = "фотографией"; "nt_photo_instrumental" = "фотографией";
"nt_topic_instrumental" = "темой"; "nt_topic_instrumental" = "темой";
"nt_you_were_mentioned_u" = "Вас упомянул пользователь";
"nt_you_were_mentioned_g" = "Вас упомянуло сообщество";
"nt_mention_in_post_or_comms" = "в посте или в одной из веток его обсуждения";
"nt_mention_in_photo" = "в обсуждении фотографии";
"nt_mention_in_video" = "в обсуждении видеозаписи";
"nt_mention_in_note" = "в обсуждении заметки";
"nt_mention_in_topic" = "в обсуждении";
/* Time */ /* Time */
"time_at_sp" = " в "; "time_at_sp" = " в ";