Compare commits

...

10 commits

Author SHA1 Message Date
Artemka
86cc6af179
Merge f575b71bb0 into e4a79a8ffb 2024-11-26 01:48:29 +01:00
mrilyew
e4a79a8ffb feat(privacy): add ability to hide your like 2024-11-25 22:29:33 +03:00
mrilyew
6007a81546 fix(likes tooltip): fix width 2024-11-24 12:49:02 +03:00
Jillian Österreich
2d0c329a2a
fix(theme-midnight): "Liked by" tippy box 2024-11-24 12:05:49 +07:00
Jillian Österreich
4dd9088859
fix(theme-midnight): "Show more" and "Feed settings" 2024-11-24 11:39:05 +07:00
mrilyew
74e998add4 fix(wall): attempt to fix invisible post betwee...
...n page 1 and 2 with pinned post on wall
2024-11-23 13:20:21 +03:00
mrilyew
2af8447a0f feat(rss): new fields 2024-11-23 13:16:55 +03:00
mrilyew
aff19de2ea feat(wall): add likes tooltip
resolves #1094 resolves #998 resolves #31
2024-11-22 21:27:21 +03:00
mrilyew
cf0b4be3fb feat(wall): add new route /{type}/{id}/likes 2024-11-22 19:34:25 +03:00
Artemka
f575b71bb0 Некоторые измения в шаблонах писем (Не тестировал) 2024-07-23 19:12:46 +00:00
30 changed files with 456 additions and 186 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

View file

@ -53,9 +53,6 @@
<table class="row"> <table class="row">
<tr> <tr>
<td> <td>
<center>
<img src="pictures/lock.jpeg" align="center" class="float-center" width=128 height=128 />
</center>
<table class="spacer"> <table class="spacer">
<tr> <tr>
@ -94,7 +91,7 @@
<tr> <tr>
<td> <td>
<center> <center>
<a href="http://{$_SERVER['HTTP_HOST']}/settings/change_email?key={rawurlencode($key)}" align="center" class="float-center">Подтвердить Email!</a> <a href="https://{$_SERVER['HTTP_HOST']}/settings/change_email?key={rawurlencode($key)}" align="center" class="float-center">Подтвердить Email!</a>
</center> </center>
</td> </td>
</tr> </tr>
@ -118,8 +115,8 @@
<table class="callout"> <table class="callout">
<tr> <tr>
<th class="callout-inner primary"> <th class="callout-inner primary">
<a href="http://{$_SERVER['HTTP_HOST']}/settings/change_email?key={$key}" style="color: #000; text-decoration: none;"> <a href="https://{$_SERVER['HTTP_HOST']}/settings/change_email?key={$key}" style="color: #000; text-decoration: none;">
http://{$_SERVER['HTTP_HOST']}/settings/change_email?key={$key} https://{$_SERVER['HTTP_HOST']}/settings/change_email?key={$key}
</a> </a>
</th> </th>
</tr> </tr>
@ -130,7 +127,7 @@
</p> </p>
<ul> <ul>
<li>Передавать другим людям (даже друзьям, питомцам, соседам, любимым девушкам)</li> <li>Передавать другим людям (даже друзьям, питомцам, соседям, любимым девушкам)</li>
<li>Использовать, если прошло более двух дней с её генерации</li> <li>Использовать, если прошло более двух дней с её генерации</li>
</ul> </ul>

View file

@ -12,7 +12,7 @@
<tr> <tr>
<td class="float-center" align="center" valign="top"> <td class="float-center" align="center" valign="top">
<center> <center>
Добро пожаловать в OpenVK! Приятного времяприпровождения, надеюсь вам понравится.<br><br>Если появились вопросы, касаемые нашего сайта, пишите <a href="https://ovk.to/support?act=new">сюда</a> Добро пожаловать в OpenVK! Приятного времяприпровождения, надеюсь вам понравится.<br><br>Если появились вопросы, касаемые нашего сайта, пишите <a href="https://{$_SERVER['HTTP_HOST']}/support?act=new">сюда</a>
</center> </center>
</td> </td>
</tr> </tr>

View file

@ -53,9 +53,6 @@
<table class="row"> <table class="row">
<tr> <tr>
<td> <td>
<center>
<img src="pictures/lock.jpeg" align="center" class="float-center" width=128 height=128 />
</center>
<table class="spacer"> <table class="spacer">
<tr> <tr>
@ -94,7 +91,7 @@
<tr> <tr>
<td> <td>
<center> <center>
<a href="http://{$_SERVER['HTTP_HOST']}/restore?act=finish&key={rawurlencode($key)}" align="center" class="float-center">Сбросить пароль!</a> <a href="https://{$_SERVER['HTTP_HOST']}/restore?act=finish&key={rawurlencode($key)}" align="center" class="float-center">Сбросить пароль!</a>
</center> </center>
</td> </td>
</tr> </tr>
@ -118,8 +115,8 @@
<table class="callout"> <table class="callout">
<tr> <tr>
<th class="callout-inner primary"> <th class="callout-inner primary">
<a href="http://{$_SERVER['HTTP_HOST']}/restore?act=finish&key={$key}" style="color: #000; text-decoration: none;"> <a href="https://{$_SERVER['HTTP_HOST']}/restore?act=finish&key={$key}" style="color: #000; text-decoration: none;">
http://{$_SERVER['HTTP_HOST']}/restore?act=finish&key={$key} https://{$_SERVER['HTTP_HOST']}/restore?act=finish&key={$key}
</a> </a>
</th> </th>
</tr> </tr>
@ -130,7 +127,7 @@
</p> </p>
<ul> <ul>
<li>Передавать другим людям (даже друзьям, питомцам, соседам, любимым девушкам)</li> <li>Передавать другим людям (даже друзьям, питомцам, соседям, любимым девушкам)</li>
<li>Использовать, если прошло более двух дней с её генерации</li> <li>Использовать, если прошло более двух дней с её генерации</li>
</ul> </ul>

View file

@ -53,9 +53,6 @@
<table class="row"> <table class="row">
<tr> <tr>
<td> <td>
<center>
<img src="pictures/lock.jpeg" align="center" class="float-center" width=128 height=128 />
</center>
<table class="spacer"> <table class="spacer">
<tr> <tr>
@ -94,7 +91,7 @@
<tr> <tr>
<td> <td>
<center> <center>
<a href="http://{$_SERVER['HTTP_HOST']}/regFinish?key={rawurlencode($key)}" align="center" class="float-center">Подтвердить Email!</a> <a href="https://{$_SERVER['HTTP_HOST']}/regFinish?key={rawurlencode($key)}" align="center" class="float-center">Подтвердить Email!</a>
</center> </center>
</td> </td>
</tr> </tr>
@ -118,8 +115,8 @@
<table class="callout"> <table class="callout">
<tr> <tr>
<th class="callout-inner primary"> <th class="callout-inner primary">
<a href="http://{$_SERVER['HTTP_HOST']}/regFinish?key={$key}" style="color: #000; text-decoration: none;"> <a href="https://{$_SERVER['HTTP_HOST']}/regFinish?key={$key}" style="color: #000; text-decoration: none;">
http://{$_SERVER['HTTP_HOST']}/regFinish?key={$key} https://{$_SERVER['HTTP_HOST']}/regFinish?key={$key}
</a> </a>
</th> </th>
</tr> </tr>
@ -130,7 +127,7 @@
</p> </p>
<ul> <ul>
<li>Передавать другим людям (даже друзьям, питомцам, соседам, любимым девушкам)</li> <li>Передавать другим людям (даже друзьям, питомцам, соседям, любимым девушкам)</li>
<li>Использовать, если прошло более двух дней с её генерации</li> <li>Использовать, если прошло более двух дней с её генерации</li>
</ul> </ul>

View file

@ -198,7 +198,7 @@ final class Likes extends VKAPIRequestHandler
if(!$extended) if(!$extended)
$res->items[] = $liker->getId(); $res->items[] = $liker->getId();
else else
$res->items[] = $liker->toVkApiStruct(); $res->items[] = $liker->toVkApiStruct(NULL, 'photo_50');
} }
return $res; return $res;

View file

@ -28,6 +28,11 @@ class Comment extends Post
return $entity; return $entity;
} }
function getPageURL(): string
{
return '#';
}
/** /**
* May return fake owner (group), if flags are [1, (*)] * May return fake owner (group), if flags are [1, (*)]
* *

View file

@ -301,7 +301,7 @@ class Post extends Postable
{ {
$liked = parent::toggleLike($user); $liked = parent::toggleLike($user);
if($this->getOwner(false)->getId() !== $user->getId() && !($this->getOwner() instanceof Club) && !$this instanceof Comment) if(!$user->isPrivateLikes() && $this->getOwner(false)->getId() !== $user->getId() && !($this->getOwner() instanceof Club) && !$this instanceof Comment)
(new LikeNotification($this->getOwner(false), $this, $user))->emit(); (new LikeNotification($this->getOwner(false), $this, $user))->emit();
foreach($this->getChildren() as $attachment) foreach($this->getChildren() as $attachment)
@ -337,6 +337,11 @@ class Post extends Postable
return $this->getRecord()->suggested; return $this->getRecord()->suggested;
} }
function getPageURL(): string
{
return "/wall".$this->getPrettyId();
}
function toNotifApiStruct() function toNotifApiStruct()
{ {
$res = (object)[]; $res = (object)[];
@ -378,34 +383,70 @@ class Post extends Postable
{ {
$domain = ovk_scheme(true).$_SERVER["HTTP_HOST"]; $domain = ovk_scheme(true).$_SERVER["HTTP_HOST"];
$description = $this->getText(false); $description = $this->getText(false);
$title = str_replace("\n", "", ovk_proc_strtr($description, 79));
$description_html = $description; $description_html = $description;
$url = $domain."/wall".$this->getPrettyId(); $url = $domain."/wall".$this->getPrettyId();
if($this->isUpdateAvatarMessage())
$title = tr('upd_in_general');
if($this->isDeactivationMessage())
$title = tr('post_deact_in_general');
$author = $this->getOwner(); $author = $this->getOwner();
$author_name = htmlspecialchars($author->getCanonicalName(), ENT_DISALLOWED | ENT_XHTML); $target_wall = $this->getWallOwner();
$author_name = escape_html($author->getCanonicalName());
if($this->isExplicit()) if($this->isExplicit())
$description_html .= "<br /><b>".tr('contains_nsfw').".</b><br />"; $title = 'NSFW: ' . $title;
foreach($this->getChildren() as $child) { foreach($this->getChildren() as $child) {
if($child instanceof Photo) { if($child instanceof Photo) {
$child_page = $domain.$child->getPageURL(); $child_page = $domain.$child->getPageURL();
$child_url = $child->getURLBySizeId('large'); $child_url = $child->getURL();
$description_html .= "<br /><a href='$child_page'><img src='$child_url'></a>"; $description_html .= "<br /><a href='$child_page'><img src='$child_url'></a><br />";
} elseif($child instanceof Video) { } elseif($child instanceof Video) {
$child_page = $domain.'/video'.$child->getPrettyId(); $child_page = $domain.'/video'.$child->getPrettyId();
$description_html .= "<br /><a href='$child_page'>Video</a>";
if($child->getType() != 1) {
$description_html .= "".
"<br />".
"<video width=\"320\" height=\"240\" controls><source src=\"".$child->getURL()."\" type=\"video/mp4\"></video><br />".
"<b>".escape_html($child->getName())."</b><br />";
} else {
$description_html .= "".
"<br />".
"<a href=\"".$child->getVideoDriver()->getURL()."\"><b>".escape_html($child->getName())."</b></a><br />";
}
} elseif($child instanceof Audio) { } elseif($child instanceof Audio) {
$description_html .= "<br />Audio"; if(!$child->isWithdrawn()) {
$description_html .= "<br />"
."<b>".escape_html($child->getName())."</b>:"
."<br />"
."<audio controls>"
."<source src=\"".$child->getOriginalURL()."\" type=\"audio/mpeg\"></audio>"
."<br />";
}
} elseif($child instanceof Poll) {
$description_html .= "<br />".tr('poll').": ".escape_html($child->getTitle());
} elseif($child instanceof Note) {
$description_html .= "<br />".tr('note').": ".escape_html($child->getName());
} }
} }
$description_html .= "<br />".tr('author').": <img width='15px' src='".$author->getAvatarURL()."'><a href='".$author->getURL()."'>" . $author_name . "</a>"; $description_html .= "<br />".tr('author').": <img width='15px' src='".$author->getAvatarURL()."'><a href='".$author->getURL()."'>" . $author_name . "</a>";
if($this->hasSource()) {
$description_html .= "<br />".tr('source').": ".htmlspecialchars($this->getSource(), ENT_DISALLOWED | ENT_XHTML); if($target_wall->getRealId() != $author->getRealId())
$description_html .= "<br />".tr('on_wall').": <img width='15px' src='".$target_wall->getAvatarURL()."'><a href='".$target_wall->getURL()."'>" . escape_html($target_wall->getCanonicalName()) . "</a>";
if($this->isSigned()) {
$signer = $this->getOwner(false);
$description_html .= "<br />".tr('sign_short').": <img width='15px' src='".$signer->getAvatarURL()."'><a href='".$signer->getURL()."'>" . escape_html($signer->getCanonicalName()) . "</a>";
} }
if($this->hasSource())
$description_html .= "<br />".tr('source').": ".escape_html($this->getSource());
$item = new \Bhaktaraz\RSSGenerator\Item(); $item = new \Bhaktaraz\RSSGenerator\Item();
$item->title(str_replace("\n", "", ovk_proc_strtr($description, 79))) $item->title($title)
->url($url) ->url($url)
->guid($url) ->guid($url)
->creator($author_name) ->creator($author_name)

View file

@ -97,8 +97,14 @@ abstract class Postable extends Attachable
"target" => $this->getRecord()->id, "target" => $this->getRecord()->id,
])->page($page, $perPage); ])->page($page, $perPage);
foreach($sel as $like) foreach($sel as $like) {
yield (new Users)->get($like->origin); $user = (new Users)->get($like->origin);
if($user->isPrivateLikes() && OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"]) {
$user = (new Users)->get((int) OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["account"]);
}
yield $user;
}
} }
function isAnonymous(): bool function isAnonymous(): bool

View file

@ -498,6 +498,7 @@ class User extends RowModel
"wall.write", "wall.write",
"messages.write", "messages.write",
"audios.read", "audios.read",
"likes.read",
], ],
])->get($id); ])->get($id);
} }
@ -1062,6 +1063,7 @@ class User extends RowModel
"wall.write", "wall.write",
"messages.write", "messages.write",
"audios.read", "audios.read",
"likes.read",
], ],
])->set($id, $status)->toInteger()); ])->set($id, $status)->toInteger());
} }
@ -1338,6 +1340,11 @@ class User extends RowModel
return $this->getId(); return $this->getId();
} }
function isPrivateLikes(): bool
{
return $this->getPrivacySetting("likes.read") == User::PRIVACY_NO_ONE;
}
function toVkApiStruct(?User $user = NULL, string $fields = ''): object function toVkApiStruct(?User $user = NULL, string $fields = ''): object
{ {
$res = (object) []; $res = (object) [];

View file

@ -309,6 +309,11 @@ class Video extends Media
); );
} }
function getPageURL(): string
{
return "/video".$this->getPrettyId();
}
function canBeViewedBy(?User $user = NULL): bool function canBeViewedBy(?User $user = NULL): bool
{ {
if($this->isDeleted() || $this->getOwner()->isDeleted()) { if($this->isDeleted() || $this->getOwner()->isDeleted()) {

View file

@ -53,9 +53,9 @@ class Posts
$offset--; $offset--;
} }
} }
} /*else if(!is_null($offset)) { } else if(!is_null($offset) && $pinPost) {
$offset--; $offset--;
}*/ }
$sel = $this->posts->where([ $sel = $this->posts->where([
"wall" => $user, "wall" => $user,

View file

@ -503,6 +503,7 @@ final class UserPresenter extends OpenVKPresenter
"wall.write", "wall.write",
"messages.write", "messages.write",
"audios.read", "audios.read",
"likes.read",
]; ];
foreach($settings as $setting) { foreach($settings as $setting) {
$input = $this->postParam(str_replace(".", "_", $setting)); $input = $this->postParam(str_replace(".", "_", $setting));

View file

@ -549,66 +549,6 @@ final class WallPresenter extends OpenVKPresenter
$this->flashFail("succ", tr("information_-1"), tr("changes_saved_comment")); $this->flashFail("succ", tr("information_-1"), tr("changes_saved_comment"));
} }
function renderEdit()
{
$this->assertUserLoggedIn();
$this->willExecuteWriteAction();
if($_SERVER["REQUEST_METHOD"] !== "POST")
$this->redirect("/id0");
if($this->postParam("type") == "post")
$post = $this->posts->get((int)$this->postParam("postid"));
else
$post = (new Comments)->get((int)$this->postParam("postid"));
if(!$post || $post->isDeleted())
$this->returnJson(["error" => "Invalid post"]);
if(!$post->canBeEditedBy($this->user->identity))
$this->returnJson(["error" => "Access denied"]);
$attachmentsCount = sizeof(iterator_to_array($post->getChildren()));
if(empty($this->postParam("newContent")) && $attachmentsCount < 1)
$this->returnJson(["error" => "Empty post"]);
$post->setEdited(time());
try {
$post->setContent($this->postParam("newContent"));
} catch(\LengthException $e) {
$this->returnJson(["error" => $e->getMessage()]);
}
if($this->postParam("type") === "post") {
$post->setNsfw($this->postParam("nsfw") == "true");
$flags = 0;
if($post->getTargetWall() < 0 && $post->getWallOwner()->canBeModifiedBy($this->user->identity)) {
if($this->postParam("fromgroup") == "true") {
$flags |= 0b10000000;
$post->setFlags($flags);
} else
$post->setFlags($flags);
}
}
$post->save(true);
$this->returnJson(["error" => "no",
"new_content" => $post->getText(),
"new_edited" => (string)$post->getEditTime(),
"nsfw" => $this->postParam("type") === "post" ? (int)$post->isExplicit() : 0,
"from_group" => $this->postParam("type") === "post" && $post->getTargetWall() < 0 ?
((int)$post->isPostedOnBehalfOfGroup()) : "false",
"new_text" => $post->getText(false),
"author" => [
"name" => $post->getOwner()->getCanonicalName(),
"avatar" => $post->getOwner()->getAvatarUrl()
]]);
}
function renderAccept() { function renderAccept() {
$this->assertUserLoggedIn(); $this->assertUserLoggedIn();
$this->willExecuteWriteAction(true); $this->willExecuteWriteAction(true);
@ -697,4 +637,44 @@ final class WallPresenter extends OpenVKPresenter
"new_count" => (new Posts)->getSuggestedPostsCount($post->getWallOwner()->getId()) "new_count" => (new Posts)->getSuggestedPostsCount($post->getWallOwner()->getId())
]); ]);
} }
function renderLikers(string $type, int $owner_id, int $item_id)
{
$this->assertUserLoggedIn();
$item = NULL;
$display_name = $type;
switch($type) {
default:
$this->notFound();
break;
case 'wall':
$item = $this->posts->getPostById($owner_id, $item_id);
$display_name = 'post';
break;
case 'comment':
$item = (new \openvk\Web\Models\Repositories\Comments)->get($item_id);
break;
case 'photo':
$item = (new \openvk\Web\Models\Repositories\Photos)->getByOwnerAndVID($owner_id, $item_id);
break;
case 'video':
$item = (new \openvk\Web\Models\Repositories\Videos)->getByOwnerAndVID($owner_id, $item_id);
break;
}
if(!$item || $item->isDeleted() || !$item->canBeViewedBy($this->user->identity))
$this->notFound();
$page = (int)($this->queryParam('p') ?? 1);
$count = $item->getLikesCount();
$likers = iterator_to_array($item->getLikers($page, OPENVK_DEFAULT_PER_PAGE));
$this->template->item = $item;
$this->template->type = $display_name;
$this->template->iterator = $likers;
$this->template->count = $count;
$this->template->page = $page;
$this->template->perPage = OPENVK_DEFAULT_PER_PAGE;
}
} }

View file

@ -36,7 +36,7 @@
{var $liked = $photo->hasLikeFrom($thisUser)} {var $liked = $photo->hasLikeFrom($thisUser)}
{var $likesCount = $photo->getLikesCount()} {var $likesCount = $photo->getLikesCount()}
<div class='like_wrap tidy'> <div class='like_wrap tidy'>
<a href="/photo{$photo->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$likesCount}"> <a href="/photo{$photo->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$likesCount}" data-id="{$photo->getPrettyId()}" data-type='photo'>
<div class="heart" id="{if $liked}liked{/if}"></div> <div class="heart" id="{if $liked}liked{/if}"></div>
<span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span> <span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span>
</a> </a>

View file

@ -395,6 +395,17 @@
</select> </select>
</td> </td>
</tr> </tr>
<tr>
<td width="120" valign="top">
<span class="nobold">{_privacy_setting_see_likes}</span>
</td>
<td>
<select name="likes.read", style="width: 164px;">
<option value="2" {if $user->getPrivacySetting('likes.read') == 2}selected{/if}>{_privacy_value_anybody}</option>
<option value="0" {if $user->getPrivacySetting('likes.read') == 0}selected{/if}>{_privacy_value_only_me_dative}</option>
</select>
</td>
</tr>
<tr> <tr>
<td width="120" valign="top"> <td width="120" valign="top">
<span class="nobold">{_profile_type}</span> <span class="nobold">{_profile_type}</span>

View file

@ -38,7 +38,7 @@
{var $liked = $video->hasLikeFrom($thisUser)} {var $liked = $video->hasLikeFrom($thisUser)}
{var $likesCount = $video->getLikesCount()} {var $likesCount = $video->getLikesCount()}
<div class='like_wrap tidy'> <div class='like_wrap tidy'>
<a href="/video{$video->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$likesCount}"> <a href="/video{$video->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$likesCount}" data-id="{$video->getPrettyId()}" data-type='video'>
<div class="heart" id="{if $liked}liked{/if}"></div> <div class="heart" id="{if $liked}liked{/if}"></div>
<span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span> <span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span>
</a> </a>

View file

@ -0,0 +1,51 @@
{extends "../@listView.xml"}
{block title}
{_likers_list}
{/block}
{block header}
<a href='{$item->getPageURL()}'>{tr($type)}</a> »
{_likers_list}
{/block}
{block tabs}
<div class="tab" id="activetabs">
<a id="act_tab_a" href="#">{_liked_verb}</a>
</div>
{/block}
{block link|strip|stripHtml}
{$x->getURL()}
{/block}
{block preview}
<img src="{$x->getAvatarUrl('tiny')}" width="75" loading=lazy />
{/block}
{block name}
{$x->getCanonicalName()}
<img n:if="$x->isVerified()"
class="name-checkmark"
src="/assets/packages/static/openvk/img/checkmark.png"
/>
{/block}
{block description}
<table>
<tbody>
<tr>
<td width="120" valign="top"><span class="nobold">{_pronouns}: </span></td>
<td>{$x->isFemale() ? tr("female") : ($x->isNeutral() ? tr("neutral") : tr("male"))}</td>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">{_relationship}:</span></td>
<td>{$x->getLocalizedMaritalStatus()}</td>
</tr>
<tr>
<td width="120" valign="top"><span class="nobold">{_registration_date}: </span></td>
<td>{$x->getRegistrationTime()}</td>
</tr>
</tbody>
</table>
{/block}

View file

@ -60,7 +60,7 @@
<a href="javascript:reportComment()">{_report}</a> <a href="javascript:reportComment()">{_report}</a>
{/if} {/if}
<div style="float: right; font-size: .7rem;"> <div style="float: right; font-size: .7rem;">
<a class="post-like-button" href="/comment{$comment->getId()}/like?hash={rawurlencode($csrfToken)}"> <a class="post-like-button" href="/comment{$comment->getId()}/like?hash={rawurlencode($csrfToken)}" data-likes='{$likesCount}' data-id="1_{$comment->getPrettyId()}" data-type='comment'>
<div class="heart" style="{if $comment->hasLikeFrom($thisUser)}opacity: 1;{else}opacity: 0.4;{/if}"></div> <div class="heart" style="{if $comment->hasLikeFrom($thisUser)}opacity: 1;{else}opacity: 0.4;{/if}"></div>
<span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span> <span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span>
</a> </a>

View file

@ -131,7 +131,7 @@
{if !($forceNoLike ?? false)} {if !($forceNoLike ?? false)}
{var $liked = $post->hasLikeFrom($thisUser)} {var $liked = $post->hasLikeFrom($thisUser)}
<a href="/wall{$post->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$likesCount}"> <a href="/wall{$post->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$likesCount}" data-id="{$post->getPrettyId()}" data-type='post'>
<div class="heart" id="{if $liked}liked{/if}"></div> <div class="heart" id="{if $liked}liked{/if}"></div>
<span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span> <span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span>
</a> </a>

View file

@ -157,7 +157,7 @@
<div n:if="!($forceNoLike ?? false)" class="like_wrap"> <div n:if="!($forceNoLike ?? false)" class="like_wrap">
{ifset $thisUser} {ifset $thisUser}
{var $liked = $post->hasLikeFrom($thisUser)} {var $liked = $post->hasLikeFrom($thisUser)}
<a href="/wall{$post->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$likesCount}"> <a href="/wall{$post->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$likesCount}" data-id="{$post->getPrettyId()}" data-type='post'>
<div class="heart" id="{if $liked}liked{/if}"></div> <div class="heart" id="{if $liked}liked{/if}"></div>
<span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span> <span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span>
</a> </a>

View file

@ -143,6 +143,8 @@ routes:
handler: "Wall->accept" handler: "Wall->accept"
- url: "/wall/decline" - url: "/wall/decline"
handler: "Wall->decline" handler: "Wall->decline"
- url: "/{text}/{num}_{num}/likes"
handler: "Wall->likers"
- url: "/blob_{text}/{?path}.{text}" - url: "/blob_{text}/{?path}.{text}"
handler: "Blob->file" handler: "Blob->file"
placeholders: placeholders:

View file

@ -3783,3 +3783,77 @@ hr {
height: 206px; height: 206px;
overflow-x: hidden; overflow-x: hidden;
} }
.like_tooltip_wrapper {
box-shadow: 0px 2px 6px -5px rgba(0, 0, 0, 0.8);
}
.like_tooltip_wrapper .like_tooltip_head {
background: linear-gradient(180deg, #595959, #515151);
box-shadow: 0px 1px 0px 0px rgba(255, 255, 255, 0.18) inset;
border: solid 1px #575757;
padding: 4px 10px;
width: 180px;
display: flex;
justify-content: space-between;
align-items: center;
}
.like_tooltip_wrapper .like_tooltip_head span, .like_tooltip_wrapper .like_tooltip_head a {
font-size: 12px;
color: white;
height: 14px;
}
.like_tooltip_wrapper .like_tooltip_body {
background: white;
padding: 10px;
border: 1px solid #878787;
cursor: default;
}
.like_tooltip_wrapper .like_tooltip_body .like_tooltip_body_grid {
display: flex;
gap: 6px;
}
.like_tooltip_wrapper .like_tooltip_body img {
width: 25px;
height: 25px;
}
.like_tooltip_wrapper .like_tooltip_body a {
height: 25px;
}
.tippy-box[data-theme~="special"] {
margin: 0;
border: unset;
border-radius: 0px;
background-color: #fff;
}
.tippy-box[data-theme~="special"] .tippy-arrow {
z-index: 9;
}
.tippy-box[data-theme~="special"] .tippy-arrow::before {
border-top-color: white;
}
.tippy-box[data-theme~="special"][data-placement^=bottom] .tippy-arrow::before {
border-bottom-color: #525252;
}
.tippy-box[data-animation='up_down'] {
transition: all 50ms;
}
.tippy-box[data-animation='up_down'][data-state='hidden'] {
opacity: 0;
inset: auto auto 10px 0px;
}
.tippy-box[data-animation='up_down'][data-state='visible'] {
inset: auto auto 0px 0px;
}

View file

@ -581,6 +581,54 @@ tippy.delegate("body", {
} }
}); });
tippy.delegate('body', {
animation: 'up_down',
target: `.post-like-button[data-type]:not([data-likes="0"])`,
theme: "special vk",
content: "⌛",
allowHTML: true,
interactive: true,
interactiveDebounce: 500,
onCreate: async function(that) {
that._likesList = null;
},
onShow: async function(that) {
const id = that.reference.dataset.id
const type = that.reference.dataset.type
let final_type = type
if(type == 'post') {
final_type = 'wall'
}
if(!that._likesList) {
that._likesList = await window.OVKAPI.call('likes.getList', {'extended': 1, 'count': 6, 'type': type, 'owner_id': id.split('_')[0], 'item_id': id.split('_')[1]})
}
const final_template = u(`
<div style='margin: -6px -10px;'>
<div class='like_tooltip_wrapper'>
<a href="/${final_type}/${id}/likes" class='like_tooltip_head'>
<span>${tr('liked_by_x_people', that._likesList.count)}</span>
</a>
<div class='like_tooltip_body'>
<div class='like_tooltip_body_grid'></div>
</div>
</div>
</div>
`)
that._likesList.items.forEach(item => {
final_template.find('.like_tooltip_body .like_tooltip_body_grid').append(`
<a href='/id${item.id}'><img src='${item.photo_50}' alt='.'></a>
`)
})
that.setContent(final_template.nodes[0].outerHTML)
}
})
async function showArticle(note_id) { async function showArticle(note_id) {
u("body").addClass("dimmed"); u("body").addClass("dimmed");
let note = await API.Notes.getNote(note_id); let note = await API.Notes.getNote(note_id);
@ -838,7 +886,7 @@ async function __uploadToTextarea(file, textareaNode) {
if(filetype == 'photo') { if(filetype == 'photo') {
const temp_url = URL.createObjectURL(file) const temp_url = URL.createObjectURL(file)
const rand = random_int(0, 1000) const rand = random_int(0, 1000)
textareaNode.find('.post-horizontal').append(`<a id='temp_filler${rand}' class="upload-item"><img src='${temp_url}'></a>`) textareaNode.find('.post-horizontal').append(`<a id='temp_filler${rand}' class="upload-item lagged"><img src='${temp_url}'></a>`)
const res = await fetch(`/photos/upload`, { const res = await fetch(`/photos/upload`, {
method: 'POST', method: 'POST',

View file

@ -366,6 +366,11 @@ function check_copyright_link(string $link = ''): bool
return true; return true;
} }
function escape_html(string $unsafe): string
{
return htmlspecialchars($unsafe, ENT_DISALLOWED | ENT_XHTML);
}
return (function() { return (function() {
_ovk_check_environment(); _ovk_check_environment();
require __DIR__ . "/vendor/autoload.php"; require __DIR__ . "/vendor/autoload.php";

View file

@ -272,6 +272,15 @@
"show_more" = "Show more"; "show_more" = "Show more";
"has_repost" = "Has repost"; "has_repost" = "Has repost";
"likers_list" = "Likers list";
"liked_verb" = "Liked by";
"liked_by_x_people_one" = "Liked by $1 user";
"liked_by_x_people_few" = "Liked by $1 users";
"liked_by_x_people_many" = "Liked by $1 users";
"liked_by_x_people_other" = "Liked by $1 users";
"liked_by_x_people_zero" = "Nobody liked";
/* Friends */ /* Friends */
"friends" = "Friends"; "friends" = "Friends";
@ -689,6 +698,7 @@
"privacy_setting_write_wall" = "Who can publish posts on my wall"; "privacy_setting_write_wall" = "Who can publish posts on my wall";
"privacy_setting_write_messages" = "Who can write messages to me"; "privacy_setting_write_messages" = "Who can write messages to me";
"privacy_setting_view_audio" = "Who can see my audios"; "privacy_setting_view_audio" = "Who can see my audios";
"privacy_setting_see_likes" = "Who can see my likes";
"privacy_value_anybody" = "Anybody"; "privacy_value_anybody" = "Anybody";
"privacy_value_anybody_dative" = "Anybody"; "privacy_value_anybody_dative" = "Anybody";
"privacy_value_users" = "OpenVK users"; "privacy_value_users" = "OpenVK users";
@ -2227,3 +2237,10 @@
"roll_back" = "rollback"; "roll_back" = "rollback";
"roll_backed" = "rollbacked"; "roll_backed" = "rollbacked";
/* RSS */
"post_deact_in_general" = "Page deletion";
"upd_in_general" = "Avatar update";
"on_wall" = "On wall";
"sign_short" = "Sign";

View file

@ -251,6 +251,15 @@
"show_more" = "Показать больше"; "show_more" = "Показать больше";
"has_repost" = "Содержит репост"; "has_repost" = "Содержит репост";
"likers_list" = "Список лайкнувших";
"liked_verb" = "Понравилось";
"liked_by_x_people_one" = "Понравилось $1 человеку";
"liked_by_x_people_few" = "Понравилось $1 людям";
"liked_by_x_people_many" = "Понравилось $1 людям";
"liked_by_x_people_other" = "Понравилось $1 людям";
"liked_by_x_people_zero" = "Никому не понравилось";
/* Friends */ /* Friends */
"friends" = "Друзья"; "friends" = "Друзья";
@ -662,6 +671,7 @@
"privacy_setting_write_wall" = "Кто может писать у меня на стене"; "privacy_setting_write_wall" = "Кто может писать у меня на стене";
"privacy_setting_write_messages" = "Кто может писать мне сообщения"; "privacy_setting_write_messages" = "Кто может писать мне сообщения";
"privacy_setting_view_audio" = "Кому видно мои аудиозаписи"; "privacy_setting_view_audio" = "Кому видно мои аудиозаписи";
"privacy_setting_see_likes" = "Кому видны мои лайки";
"privacy_value_anybody" = "Все желающие"; "privacy_value_anybody" = "Все желающие";
"privacy_value_anybody_dative" = "Всем желающим"; "privacy_value_anybody_dative" = "Всем желающим";
"privacy_value_users" = "Пользователям OpenVK"; "privacy_value_users" = "Пользователям OpenVK";
@ -2118,3 +2128,9 @@
"roll_back" = "откатить"; "roll_back" = "откатить";
"roll_backed" = "откачено"; "roll_backed" = "откачено";
/* RSS */
"post_deact_in_general" = "Удаление страницы";
"upd_in_general" = "Обновление фотографии страницы";
"on_wall" = "На стене";
"sign_short" = "Подпись";

View file

@ -1,7 +1,7 @@
.page_header { .page_header {
background-image: url('/themepack/midnight/0.0.3.1/resource/xheader.png') !important; background-image: url('/themepack/midnight/0.0.3.3/resource/xheader.png') !important;
} }
.page_custom_header { .page_custom_header {
background-image: url('/themepack/midnight/0.0.3.1/resource/xheader_custom.png') !important; background-image: url('/themepack/midnight/0.0.3.3/resource/xheader_custom.png') !important;
} }

View file

@ -135,10 +135,18 @@ th,
.ovk-photo-view, .ovk-photo-view,
.page_wrap_content_main .def_row_content, .page_wrap_content_main .def_row_content,
.topGrayBlock, .topGrayBlock,
.bigPlayer { .bigPlayer,
input[type="number"],
.like_tooltip_wrapper .like_tooltip_body,
.like_tooltip_wrapper .like_tooltip_head {
border-color: #2c2640 !important; border-color: #2c2640 !important;
} }
.like_tooltip_wrapper .like_tooltip_head {
background: linear-gradient(180deg, #383052, #231e33) !important;
box-shadow: unset !important;
}
.post-upload, .post-upload,
.post-has-poll, .post-has-poll,
.post-has-note { .post-has-note {
@ -187,10 +195,8 @@ hr {
.ovk-diag-action, .ovk-diag-action,
.minilink .counter, .minilink .counter,
.topGrayBlock, .topGrayBlock,
.showMore, #show_more,
.showMoreAudiosPlaylist, .information {
#showMorePhotos,
#showMoreVideos {
background-color: #2c2640 !important; background-color: #2c2640 !important;
} }
@ -232,7 +238,8 @@ a,
.paginator a:hover, .paginator a:hover,
.post-share-button:hover, .post-share-button:hover,
.post-like-button:hover, .post-like-button:hover,
#search_box_button:active { #search_box_button:active,
.mb_tab:hover {
background-color: #272138 !important; background-color: #272138 !important;
} }
@ -288,7 +295,8 @@ td.e,
tr.e, tr.e,
.playlistListView:hover, .playlistListView:hover,
.playlistListView .playlistCover, .playlistListView .playlistCover,
.photosInsert > div { .photosInsert > div,
.attachment_selector #attachment_insert #attachment_insert_count {
background-color: #231e33 !important; background-color: #231e33 !important;
} }
@ -307,7 +315,8 @@ tr.v,
.expand_button, .expand_button,
#userContent blockquote, #userContent blockquote,
.tippy-box[data-theme~="vk"], .tippy-box[data-theme~="vk"],
.searchOptions { .searchOptions,
.like_tooltip_wrapper .like_tooltip_body {
background-color: #1e1a2b !important; background-color: #1e1a2b !important;
} }
@ -317,11 +326,11 @@ tr.v,
} }
.content_title_expanded { .content_title_expanded {
background-image: url("/themepack/midnight/0.0.3.1/resource/flex_arrow_open.png") !important; background-image: url("/themepack/midnight/0.0.3.3/resource/flex_arrow_open.png") !important;
} }
.content_title_unexpanded { .content_title_unexpanded {
background-image: url("/themepack/midnight/0.0.3.1/resource/flex_arrow_shut.gif") !important; background-image: url("/themepack/midnight/0.0.3.3/resource/flex_arrow_shut.gif") !important;
} }
.ovk-video>.preview, .ovk-video>.preview,
@ -348,17 +357,17 @@ tr.h {
.page_yellowheader { .page_yellowheader {
color: #c6d2e8; color: #c6d2e8;
background-image: url("/themepack/midnight/0.0.3.1/resource/header_purple.png") !important; background-image: url("/themepack/midnight/0.0.3.3/resource/header_purple.png") !important;
background-color: #231f34; background-color: #231f34;
border-color: #231f34; border-color: #231f34;
} }
.page_header { .page_header {
background-image: url("/themepack/midnight/0.0.3.1/resource/header.png") !important; background-image: url("/themepack/midnight/0.0.3.3/resource/header.png") !important;
} }
.page_custom_header { .page_custom_header {
background-image: url("/themepack/midnight/0.0.3.1/resource/header_custom.png") !important; background-image: url("/themepack/midnight/0.0.3.3/resource/header_custom.png") !important;
} }
.page_yellowheader span, .page_yellowheader span,
@ -390,17 +399,18 @@ input[type~="phone"],
input[type="search"], input[type="search"],
input[type~="search"], input[type~="search"],
input[type~="date"], input[type~="date"],
input[type="number"],
select, select,
.crp-entry--message.unread { .crp-entry--message.unread {
background-color: #181826 !important; background-color: #181826 !important;
} }
input[type="checkbox"] { input[type="checkbox"] {
background-image: url("/themepack/midnight/0.0.3.1/resource/checkbox.png") !important; background-image: url("/themepack/midnight/0.0.3.3/resource/checkbox.png") !important;
} }
input[type="radio"] { input[type="radio"] {
background-image: url("/themepack/midnight/0.0.3.1/resource/radio.png") !important; background-image: url("/themepack/midnight/0.0.3.3/resource/radio.png") !important;
} }
.header_navigation .link, .header_navigation .header_divider_stick { .header_navigation .link, .header_navigation .header_divider_stick {
@ -408,20 +418,20 @@ input[type="radio"] {
} }
.heart { .heart {
background-image: url("/themepack/midnight/0.0.3.1/resource/like.gif") !important; background-image: url("/themepack/midnight/0.0.3.3/resource/like.gif") !important;
} }
.pinned-mark, .pinned-mark,
.post-author .pin { .post-author .pin {
background-image: url("/themepack/midnight/0.0.3.1/resource/pin.png") !important; background-image: url("/themepack/midnight/0.0.3.3/resource/pin.png") !important;
} }
.repost-icon { .repost-icon {
background-image: url("/themepack/midnight/0.0.3.1/resource/published.gif") !important; background-image: url("/themepack/midnight/0.0.3.3/resource/published.gif") !important;
} }
.post-author .delete { .post-author .delete {
background-image: url("/themepack/midnight/0.0.3.1/resource/input_clear.gif") !important; background-image: url("/themepack/midnight/0.0.3.3/resource/input_clear.gif") !important;
} }
.user-alert { .user-alert {
@ -454,7 +464,7 @@ input[type="radio"] {
} }
#backdropEditor { #backdropEditor {
background-image: url("/themepack/midnight/0.0.3.1/resource/backdrop-editor.gif") !important; background-image: url("/themepack/midnight/0.0.3.3/resource/backdrop-editor.gif") !important;
border-color: #473e66 !important; border-color: #473e66 !important;
} }

View file

@ -1,5 +1,5 @@
id: midnight id: midnight
version: "0.0.3.1" version: "0.0.3.3"
openvk_version: 0 openvk_version: 0
enabled: 1 enabled: 1
metadata: metadata: