Compare commits

..

3 commits

Author SHA1 Message Date
mrilyew
fab39bd7fd
Posts: add source param (#934)
* Chopin

- Полностью переписана та часть где про источник поста
- Исправлен метод video.search
- Сокращено число запросов в БД у шаблона поста
- Удалена ссылка на прикреплятор заметок потому что low quality
- Исправлен баг с прикреплённой заметкой в api, но только если ты указал версию.
- Исправлены проблемы с кешированными спрайтшитами

* Chopin 2
2024-11-01 14:46:41 +03:00
ayato
9b220a88db
locales/en.strings: Rephrasing some strings and grammar check (#1098)
* locales: English: fix grammar

* locales: English: fix confusing labels in the repost modal

Clarified modal phrasing when reposting a user's post. The previous wording, 'Share to user's wall,' was ambiguous and could be interpreted as either sharing to your own wall or the original user's wall. Updated the language to make it clear that the post will be shared to your wall.

---------

Co-authored-by: Vladimir Barinov <veselcraft@icloud.com>
2024-11-01 14:00:09 +03:00
ayato
d1bcdaf7d7
Link directly to interests section of the edit page in completeness gauge (#1113) 2024-11-01 13:58:14 +03:00
26 changed files with 466 additions and 142 deletions

View file

@ -74,7 +74,7 @@ final class Video extends VKAPIRequestHandler
$return_items = [];
$profiles = [];
$groups = [];
foreach($items as $item)
foreach($items as $item) {
$return_item = $item->getApiStructure($this->getUser());
$return_item = $return_item->video;
$return_items[] = $return_item;
@ -85,6 +85,7 @@ final class Video extends VKAPIRequestHandler
else
$groups[] = abs($return_item['owner_id']);
}
}
if($extended) {
$profiles = array_unique($profiles);

View file

@ -102,7 +102,14 @@ final class Wall extends VKAPIRequestHandler
} else if ($attachment instanceof \openvk\Web\Models\Entities\Video) {
$attachments[] = $attachment->getApiStructure($this->getUser());
} else if ($attachment instanceof \openvk\Web\Models\Entities\Note) {
$attachments[] = $attachment->toVkApiStruct();
if(VKAPI_DECL_VER === '4.100') {
$attachments[] = $attachment->toVkApiStruct();
} else {
$attachments[] = [
'type' => 'note',
'note' => $attachment->toVkApiStruct()
];
}
} else if ($attachment instanceof \openvk\Web\Models\Entities\Audio) {
$attachments[] = [
"type" => "audio",
@ -188,6 +195,9 @@ final class Wall extends VKAPIRequestHandler
]
];
if($post->hasSource())
$post_temp_obj->copyright = $post->getVkApiCopyright();
if($signerId)
$post_temp_obj->signer_id = $signerId;
@ -291,7 +301,14 @@ final class Wall extends VKAPIRequestHandler
} else if ($attachment instanceof \openvk\Web\Models\Entities\Video) {
$attachments[] = $attachment->getApiStructure($this->getUser());
} else if ($attachment instanceof \openvk\Web\Models\Entities\Note) {
$attachments[] = $attachment->toVkApiStruct();
if(VKAPI_DECL_VER === '4.100') {
$attachments[] = $attachment->toVkApiStruct();
} else {
$attachments[] = [
'type' => 'note',
'note' => $attachment->toVkApiStruct()
];
}
} else if ($attachment instanceof \openvk\Web\Models\Entities\Audio) {
$attachments[] = [
"type" => "audio",
@ -373,6 +390,9 @@ final class Wall extends VKAPIRequestHandler
]
];
if($post->hasSource())
$post_temp_obj->copyright = $post->getVkApiCopyright();
if($signerId)
$post_temp_obj->signer_id = $signerId;
@ -543,6 +563,12 @@ final class Wall extends VKAPIRequestHandler
$post->setFlags($flags);
$post->setApi_Source_Name($this->getPlatform());
if(!is_null($copyright) && !empty($copyright)) {
try {
$post->setSource($copyright);
} catch(\Throwable) {}
}
if($owner_id < 0 && !$wallOwner->canBeModifiedBy($this->getUser()) && $wallOwner->getWallType() == 2)
$post->setSuggested(1);
@ -558,11 +584,11 @@ final class Wall extends VKAPIRequestHandler
# Пример: photo1_1
if(sizeof($attachmentsArr) > 10)
$this->fail(50, "Error: too many attachments");
$this->fail(50, "Too many attachments");
preg_match_all("/poll/m", $attachments, $matches, PREG_SET_ORDER, 0);
if(sizeof($matches) > 1)
$this->fail(85, "Error: too many polls");
$this->fail(85, "Too many polls");
foreach($attachmentsArr as $attac) {
$attachmentType = NULL;
@ -993,7 +1019,7 @@ final class Wall extends VKAPIRequestHandler
}
}
function edit(int $owner_id, int $post_id, string $message = "", string $attachments = "") {
function edit(int $owner_id, int $post_id, string $message = "", string $attachments = "", string $copyright = NULL) {
$this->requireUser();
$this->willExecuteWriteAction();
@ -1002,9 +1028,6 @@ final class Wall extends VKAPIRequestHandler
if(!$post || $post->isDeleted())
$this->fail(102, "Invalid post");
if(empty($message) && empty($attachments))
$this->fail(100, "Required parameter 'message' missing.");
if(!$post->canBeEditedBy($this->getUser()))
$this->fail(7, "Access to editing denied");
@ -1012,6 +1035,12 @@ final class Wall extends VKAPIRequestHandler
$post->setContent($message);
$post->setEdited(time());
if(!is_null($copyright) && !empty($copyright)) {
try {
$post->setSource($copyright);
} catch(\Throwable) {}
}
$post->save(true);
# todo добавить такое в веб версию
@ -1082,6 +1111,63 @@ final class Wall extends VKAPIRequestHandler
return 1;
}
function checkCopyrightLink(string $link): int
{
$this->requireUser();
try {
$result = check_copyright_link($link);
} catch(\InvalidArgumentException $e) {
$this->fail(3102, "Specified link is incorrect (can't find source)");
} catch(\LengthException $e) {
$this->fail(3103, "Specified link is incorrect (too long)");
} catch(\LogicException $e) {
$this->fail(3104, "Link is suspicious");
} catch(\Throwable $e) {
$this->fail(3102, "Specified link is incorrect");
}
return 1;
}
function pin(int $owner_id, int $post_id)
{
$this->requireUser();
$this->willExecuteWriteAction();
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
if(!$post || $post->isDeleted())
$this->fail(100, "One of the parameters specified was missing or invalid: post_id is undefined");
if(!$post->canBePinnedBy($this->getUser()))
return 0;
if($post->isPinned())
return 1;
$post->pin();
return 1;
}
function unpin(int $owner_id, int $post_id)
{
$this->requireUser();
$this->willExecuteWriteAction();
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
if(!$post || $post->isDeleted())
$this->fail(100, "One of the parameters specified was missing or invalid: post_id is undefined");
if(!$post->canBePinnedBy($this->getUser()))
return 0;
if(!$post->isPinned())
return 1;
$post->unpin();
return 1;
}
private function getApiPhoto($attachment) {
return [
"type" => "photo",

View file

@ -78,6 +78,40 @@ class Post extends Postable
{
return (bool) $this->getRecord()->pinned;
}
function hasSource(): bool
{
return $this->getRecord()->source != NULL;
}
function getSource(bool $format = false)
{
$orig_source = $this->getRecord()->source;
if(!str_contains($orig_source, "https://") && !str_contains($orig_source, "http://"))
$orig_source = "https://" . $orig_source;
if(!$format)
return $orig_source;
return $this->formatLinks($orig_source);
}
function setSource(string $source)
{
$result = check_copyright_link($source);
$this->stateChanges("source", $source);
}
function getVkApiCopyright(): object
{
return (object)[
'id' => 0,
'link' => $this->getSource(false),
'name' => $this->getSource(false),
'type' => 'link',
];
}
function isAd(): bool
{

View file

@ -38,8 +38,19 @@ trait TRichText
$href = str_replace("#", "&num;", $matches[1]);
$href = rawurlencode(str_replace(";", "&#59;", $href));
$link = str_replace("#", "&num;", $matches[3]);
# this string breaks ampersands
$link = str_replace(";", "&#59;", $link);
$rel = $this->isAd() ? "sponsored" : "ugc";
$server_domain = str_replace(':' . $_SERVER['SERVER_PORT'], '', $_SERVER['HTTP_HOST']);
if(str_contains($link, $server_domain)) {
$replaced_link = str_replace(':' . $_SERVER['SERVER_PORT'], '', $link);
$replaced_link = str_replace($server_domain, '', $replaced_link);
return "<a href='$replaced_link' rel='$rel'>$link</a>" . htmlentities($matches[4]);
}
$link = htmlentities(urldecode($link));
return "<a href='/away.php?to=$href' rel='$rel' target='_blank'>$link</a>" . htmlentities($matches[4]);
}),

View file

@ -367,6 +367,12 @@ final class WallPresenter extends OpenVKPresenter
$post->setFlags($flags);
$post->setNsfw($this->postParam("nsfw") === "on");
if(!empty($this->postParam("source")) && $this->postParam("source") != 'none') {
try {
$post->setSource($this->postParam("source"));
} catch(\Throwable) {}
}
if($wall < 0 && !$wallOwner->canBeModifiedBy($this->user->identity) && $wallOwner->getWallType() == 2)
$post->setSuggested(1);

View file

@ -18,7 +18,7 @@
{else}
<a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a>
»
<a href="/playlists{$ownerId}">{_playlists}</a>
<a href="/playlists{$owner->getRealId()}">{_playlists}</a>
»
<a href="/playlist{$playlist->getPrettyId()}">{_playlist}</a>
{/if}

View file

@ -17,7 +17,7 @@
<a n:if="!$isMy" n:attr="id => $mode === 'list' ? 'used' : 'ki'" href="/audios{$ownerId}">{if $ownerId > 0}{_music_user}{else}{_music_club}{/if}</a>
<a href="/player/upload?gid={abs($ownerId)}" n:if="isset($thisUser) && isset($club) && $club->canUploadAudio($thisUser)">{_upload_audio}</a>
<a n:attr="id => $mode === 'playlists' && $ownerId != $thisUser->getId() ? 'used' : 'ki'" href="/playlists{$ownerId}" n:if="isset($thisUser) && isset($ownerId) && !$isMy">{if $ownerId > 0}{_playlists_user}{else}{_playlists_club}{/if}</a>
<a n:attr="id => $mode === 'playlists' && $ownerId != $thisUser->getId() ? 'used' : 'ki'" class='noOverflow' href="/playlists{$ownerId}" n:if="isset($thisUser) && isset($ownerId) && !$isMy">{if $ownerId > 0}{_playlists_user}{else}{_playlists_club}{/if}</a>
<a href="/audios/newPlaylist{if $isMyClub}?gid={abs($ownerId)}{/if}" n:if="isset($thisUser) && $isMyClub">{_new_playlist}</a>
{/if}
</div>

View file

@ -202,7 +202,7 @@
{if isset($thisUser) && $user->getId() === $thisUser->getId() && sizeof($completeness->unfilled) > 0}
<br/>
<a n:if="in_array('interests', $completeness->unfilled)" href="/edit">
<a n:if="in_array('interests', $completeness->unfilled)" href="/edit?act=interests">
<img src="/assets/packages/static/openvk/img/icon1.gif" />
{_interests} (+20%)
</a>

View file

@ -18,7 +18,7 @@
</div>
<div n:class="postFeedWrapper, $thisUser->hasMicroblogEnabled() ? postFeedWrapperMicroblog">
{include "../components/textArea.xml", route => "/wall" . $thisUser->getId() . "/makePost", graffiti => true, polls => true, notes => true}
{include "../components/textArea.xml", route => "/wall" . $thisUser->getId() . "/makePost", graffiti => true, polls => true, notes => true, hasSource => true}
</div>
{foreach $posts as $post}

View file

@ -28,7 +28,7 @@
</div>
<div n:if="$canPost && $type == 'all'" class="content_subtitle">
{include "../components/textArea.xml", route => "/wall$owner/makePost"}
{include "../components/textArea.xml", route => "/wall$owner/makePost", hasSource => true}
</div>
<div class="content">

View file

@ -1,9 +1,11 @@
{var $author = $comment->getOwner()}
{var $Club = openvk\Web\Models\Entities\Club::class}
{var $postId = $comment->getTarget() instanceof \openvk\Web\Models\Entities\Post ? $comment->getTarget()->getId() : NULL}
{var $likesCount = $comment->getLikesCount()}
{var $target = $comment->getTarget()}
{var $postId = $target instanceof \openvk\Web\Models\Entities\Post ? $target->getId() : NULL}
<a name="cid={$comment->getId()}"></a>
<table border="0" style="font-size: 11px;" class="post comment" id="_comment{$comment->getId()}" data-comment-id="{$comment->getId()}" data-owner-id="{$author->getId()}" data-from-group="{$comment->getOwner() instanceof $Club}" n:attr="data-post-id => $postId">
<table border="0" style="font-size: 11px;" class="post comment" id="_comment{$comment->getId()}" data-comment-id="{$comment->getId()}" data-owner-id="{$author->getId()}" data-from-group="{$author instanceof $Club}" n:attr="data-post-id => $postId">
<tbody>
<tr>
<td width="30" valign="top">
@ -40,8 +42,8 @@
<span n:if="$comment->getEditTime()" class="edited editedMark">({_edited_short})</span>
</a>
{if !$timeOnly}
&nbsp;|
{if $comment->canBeDeletedBy($thisUser)}
|
<a href="/comment{$comment->getId()}/delete">{_delete}</a>
{/if}
{if $comment->canBeEditedBy($thisUser)}
@ -60,7 +62,7 @@
<div style="float: right; font-size: .7rem;">
<a class="post-like-button" href="/comment{$comment->getId()}/like?hash={rawurlencode($csrfToken)}">
<div class="heart" style="{if $comment->hasLikeFrom($thisUser)}opacity: 1;{else}opacity: 0.4;{/if}"></div>
<span class="likeCnt">{if $comment->getLikesCount() > 0}{$comment->getLikesCount()}{/if}</span>
<span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span>
</a>
</div>
{/if}
@ -101,4 +103,4 @@
Function.noop
]);
}
</script>
</script>

View file

@ -3,6 +3,11 @@
{var $commentsCount = $post->getCommentsCount()}
{var $platform = $post->getPlatform()}
{var $platformDetails = $post->getPlatformDetails()}
{var $likesCount = $post->getLikesCount()}
{var $repostsCount = $post->getRepostCount()}
{var $canBePinned = $post->canBePinnedBy($thisUser ?? NULL)}
{var $canBeDeleted = $post->canBeDeletedBy($thisUser)}
{var $wallOwner = $post->getWallOwner()}
{if $post->isDeactivationMessage() && $post->getText()}
{var $deac = "post_deact"}
{else}
@ -30,7 +35,6 @@
{$post->isUpdateAvatarMessage() && !$post->isPostedOnBehalfOfGroup() ? ($author->isFemale() ? tr("upd_f") : ($author->isNeutral() ? tr("upd_n") : tr("upd_m")))}
{$post->isUpdateAvatarMessage() && $post->isPostedOnBehalfOfGroup() ? tr("upd_g") : ""}
{if ($onWallOf ?? false) &&!$post->isPostedOnBehalfOfGroup() && $post->getOwnerPost() !== $post->getTargetWall()}
{var $wallOwner = $post->getWallOwner()}
<a href="{$wallOwner->getURL()}" class="mention" data-mention-ref="{$post->getTargetWall()}">
<b>
{if isset($thisUser) && $thisUser->getId() === $post->getTargetWall()}
@ -53,9 +57,9 @@
<span n:if="$post->isPinned()" class="nobold">{_pinned}</span>
<a n:if="$post->canBeDeletedBy($thisUser) && !($forceNoDeleteLink ?? false) && $compact == false" class="delete" href="/wall{$post->getPrettyId()}/delete"></a>
<a n:if="$canBeDeleted && !($forceNoDeleteLink ?? false) && $compact == false" class="delete" href="/wall{$post->getPrettyId()}/delete"></a>
{if $post->canBePinnedBy($thisUser) && !($forceNoPinLink ?? false) && $compact == false}
{if $canBePinned && !($forceNoPinLink ?? false) && $compact == false}
{if $post->isPinned()}
<a class="pin" href="/wall{$post->getPrettyId()}/pin?act=unpin&hash={rawurlencode($csrfToken)}"></a>
{else}
@ -67,7 +71,7 @@
<a class="edit" id="editPost"
data-id="{$post->getId()}"
data-nsfw="{(int)$post->isExplicit()}"
{if $post->getTargetWall() < 0 && $post->getWallOwner()->canBeModifiedBy($thisUser)}data-fromgroup="{(int)$post->isPostedOnBehalfOfGroup()}"{/if}></a>
{if $post->getTargetWall() < 0 && $wallOwner->canBeModifiedBy($thisUser)}data-fromgroup="{(int)$post->isPostedOnBehalfOfGroup()}"{/if}></a>
{/if}
</div>
<div class="post-content" id="{$post->getPrettyId()}">
@ -95,6 +99,9 @@
<br/>
&nbsp;! {_post_is_ad}
</div>
<div n:if="$post->hasSource()" class="sourceDiv">
<span>{_source}: {$post->getSource(true)|noescape}</span>
</div>
<div n:if="$post->isSigned()" class="post-signature">
{var $actualAuthor = $post->getOwner(false)}
<span>
@ -121,14 +128,14 @@
<div class="like_wrap">
<a n:if="!($forceNoShareLink ?? false)" id="reposts{$post->getPrettyId()}" class="post-share-button" href="javascript:repostPost('{$post->getPrettyId()}', '{rawurlencode($csrfToken)}')">
<div class="repost-icon" style="opacity: 0.4;"></div>
<span class="likeCnt" id="repostsCount{$post->getPrettyId()}">{if $post->getRepostCount() > 0}{$post->getRepostCount()}{/if}</span>
<span class="likeCnt" id="repostsCount{$post->getPrettyId()}">{if $repostsCount > 0}{$repostsCount}{/if}</span>
</a>
{if !($forceNoLike ?? false)}
{var $liked = $post->hasLikeFrom($thisUser)}
<a href="/wall{$post->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$post->getLikesCount()}">
<a href="/wall{$post->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$likesCount}">
<div class="heart" id="{if $liked}liked{/if}"></div>
<span class="likeCnt">{if $post->getLikesCount() > 0}{$post->getLikesCount()}{/if}</span>
<span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span>
</a>
{/if}
</div>
@ -145,7 +152,7 @@
{include "../textArea.xml", route => $commentsURL, postOpts => false, graffiti => (bool) ovkGetQuirk("comments.allow-graffiti"), post => $post, club => $club}
</div>
</div>
<div n:if="$suggestion && $post->canBePinnedBy($thisUser ?? NULL)" class="suggestionControls">
<div n:if="$suggestion && $canBePinned" 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>

View file

@ -1,6 +1,12 @@
{var $author = $post->getOwner()}
{var $platform = $post->getPlatform()}
{var $platformDetails = $post->getPlatformDetails()}
{var $wallOwner = $post->getWallOwner()}
{var $likesCount = $post->getLikesCount()}
{var $repostsCount = $post->getRepostCount()}
{var $commentsCount = $post->getCommentsCount()}
{var $canBePinned = $post->canBePinnedBy($thisUser ?? NULL)}
{var $canBeDeleted = $post->canBeDeletedBy($thisUser)}
{if $post->isDeactivationMessage() && $post->getText()}
{var $deac = "post_deact"}
{else}
@ -40,7 +46,6 @@
{/if}
{/if}
{if ($onWallOf ?? false) &&!$post->isPostedOnBehalfOfGroup() && $post->getOwnerPost() !== $post->getTargetWall()}
{var $wallOwner = $post->getWallOwner()}
<a href="{$wallOwner->getURL()}" class="mention" data-mention-ref="{$post->getTargetWall()}">
<b>
{if isset($thisUser) && $thisUser->getId() === $post->getTargetWall()}
@ -85,7 +90,7 @@
</div>
</div>
</div>
<div n:if="$suggestion && $post->canBePinnedBy($thisUser ?? NULL)" class="suggestionControls" style="margin-bottom: 7px;">
<div n:if="$suggestion && $canBePinned" class="suggestionControls" style="margin-bottom: 7px;">
<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>
@ -93,6 +98,9 @@
<br/>
&nbsp;! {_post_is_ad}
</div>
<div n:if="$post->hasSource()" class="sourceDiv">
<span>{_source}: {$post->getSource(true)|noescape}</span>
</div>
<div n:if="$post->isSigned()" class="post-signature">
{var $actualAuthor = $post->getOwner(false)}
<span>
@ -113,14 +121,14 @@
<a id="editPost"
data-id="{$post->getId()}"
data-nsfw="{(int)$post->isExplicit()}"
{if $post->getTargetWall() < 0 && $post->getWallOwner()->canBeModifiedBy($thisUser)}data-fromgroup="{(int)$post->isPostedOnBehalfOfGroup()}"{/if}>{_edit}</a> &nbsp;|&nbsp;
{if $post->getTargetWall() < 0 && $wallOwner->canBeModifiedBy($thisUser)}data-fromgroup="{(int)$post->isPostedOnBehalfOfGroup()}"{/if}>{_edit}</a> &nbsp;|&nbsp;
{/if}
{if !($forceNoDeleteLink ?? false) && $post->canBeDeletedBy($thisUser)}
{if !($forceNoDeleteLink ?? false) && $canBeDeleted}
<a href="/wall{$post->getPrettyId()}/delete">{_delete}</a> &nbsp;|&nbsp;
{/if}
{if !($forceNoPinLink ?? false) && $post->canBePinnedBy($thisUser)}
{if !($forceNoPinLink ?? false) && $canBePinned}
{if $post->isPinned()}
<a href="/wall{$post->getPrettyId()}/pin?act=unpin&hash={rawurlencode($csrfToken)}">{_unpin}</a>
{else}
@ -131,8 +139,8 @@
<a n:if="!($forceNoCommentsLink ?? false)" href="/wall{$post->getPrettyId()}#comments">
{_comments}
{if $post->getCommentsCount() > 0}
(<b>{$post->getCommentsCount()}</b>)
{if $commentsCount > 0}
(<b>{$commentsCount}</b>)
{/if}
</a>
@ -142,22 +150,22 @@
<a n:if="!($forceNoShareLink ?? false)" id="reposts{$post->getPrettyId()}" class="post-share-button" {ifset $thisUser} href="javascript:repostPost('{$post->getPrettyId()}', '{rawurlencode($csrfToken)}')"{/ifset}>
{_share}
{if $post->getRepostCount() > 0}
(<b id="repostsCount{$post->getPrettyId()}">{$post->getRepostCount()}</b>)
{if $repostsCount > 0}
(<b id="repostsCount{$post->getPrettyId()}">{$repostsCount}</b>)
{/if}
</a>
<div n:if="!($forceNoLike ?? false)" class="like_wrap">
{ifset $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="{$post->getLikesCount()}">
<a href="/wall{$post->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$likesCount}">
<div class="heart" id="{if $liked}liked{/if}"></div>
<span class="likeCnt">{if $post->getLikesCount() > 0}{$post->getLikesCount()}{/if}</span>
<span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span>
</a>
{else}
<a n:if="$post->getLikesCount() > 0" class="post-like-button">
<a n:if="$likesCount > 0" class="post-like-button">
<div class="heart"></div>
<span class="likeCnt">{$post->getLikesCount()}</span>
<span class="likeCnt">{$likesCount}</span>
</a>
{/ifset}
</div>

View file

@ -18,14 +18,15 @@
<div class="post-has-poll">
{_poll}
</div>
<div class="post-has-note">
</div>
<div class="post-has-note"></div>
<div class="post-has-videos"></div>
<div class="post-has-audios"></div>
<div class="post-source"></div>
<div n:if="$postOpts ?? true" class="post-opts">
{var $anonEnabled = OPENVK_ROOT_CONF['openvk']['preferences']['wall']['anonymousPosting']['enable']}
{if !is_null($thisUser) && !is_null($club ?? NULL) && $owner < 0}
{if $club->canBeModifiedBy($thisUser)}
<script>
@ -66,6 +67,7 @@
<input type="hidden" name="audios" value="" />
<input type="hidden" name="poll" value="none" />
<input type="hidden" id="note" name="note" value="none" />
<input type="hidden" id="source" name="source" value="none" />
<input type="hidden" name="type" value="1" />
<input type="hidden" name="hash" value="{$csrfToken}" />
<br/>
@ -91,10 +93,6 @@
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/audio-ac3.png" />
{_audio}
</a>
<a n:if="$notes ?? false" href="javascript:attachNote({$textAreaId})">
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-x-srt.png" />
{_note}
</a>
<a n:if="$graffiti ?? false" href="javascript:initGraffiti({$textAreaId});">
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/actions/draw-brush.png" />
{_graffiti}
@ -103,6 +101,10 @@
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/actions/office-chart-bar-stacked.png" />
{_poll}
</a>
<a n:if="$hasSource ?? false" id='__sourceAttacher'>
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/actions/insert-link.png" />
{_source}
</a>
</div>
</div>
</div>

View file

@ -10,7 +10,7 @@
<div class="insertThere" id="postz"></div>
<div id="underHeader">
<div n:if="$canPost" class="content_subtitle">
{include "../components/textArea.xml", route => "/wall$owner/makePost", graffiti => true, polls => true, notes => true}
{include "../components/textArea.xml", route => "/wall$owner/makePost", graffiti => true, polls => true, notes => true, hasSource => true}
</div>
<div class="content">

View file

@ -70,7 +70,7 @@ class Makima
$result->colSizes = [1];
$result->rowSizes = [1, 1];
$result->width = ceil($maxWidth);
$result->height = $computedHeight;
$result->height = $computedHeight * 2;
$result->tiles = [new ThumbTile(1, 1, $maxWidth, $computedHeight), new ThumbTile(1, 1, $maxWidth, $computedHeight)];
} else if(
$orients == [Makima::ORIENT_WIDE, Makima::ORIENT_WIDE]

View file

@ -9,7 +9,7 @@
}
.musicIcon {
background-image: url('/assets/packages/static/openvk/img/audios_controls.png');
background-image: url('/assets/packages/static/openvk/img/audios_controls.png?v=2');
background-repeat: no-repeat;
cursor: pointer;
}
@ -185,6 +185,7 @@
width: 81%;
height: 13px;
display: inline-block;
line-height: 14px;
}
.bigPlayer .paddingLayer .trackInfo .timer span {
@ -345,6 +346,7 @@
.audioEntry .status .mediaInfo {
cursor: pointer;
display: flex;
line-height: 14px;
width: 100%;
}

View file

@ -44,7 +44,7 @@
._add_image::before {
margin-top: 2px;
content: ' ';
background: url('/assets/packages/static/openvk/img/upload.png');
background: url('/assets/packages/static/openvk/img/upload.png?v=2');
width: 10px;
height: 10px;
display: inline-block;
@ -65,7 +65,7 @@
.avatarDelete::before {
content: ' ';
background: url('/assets/packages/static/openvk/img/upload.png');
background: url('/assets/packages/static/openvk/img/upload.png?v=2');
background-position: -10px 2px;
background-repeat: no-repeat;
width: 12px;

View file

@ -1454,6 +1454,24 @@ body.scrolled .toTop:hover {
color: #3c3c3c;
}
.post-source #remove_source_button {
display: inline-block;
background-repeat: no-repeat;
background: url('/assets/packages/static/openvk/img/arrows.png?v=2');
margin-bottom: -2px;
background-position: -18px 0px;
height: 11px;
width: 11px;
opacity: 0.6;
transition-duration: 0.3s;
cursor: pointer;
}
.post-source #remove_source_button:hover {
opacity: 0.8;
}
.post-upload::before, .post-has-poll::before, .post-has-note::before {
content: " ";
width: 8px;
@ -3019,6 +3037,26 @@ body.article .floating_sidebar, body.article .page_content {
font-size: 12px;
}
.post .sourceDiv {
margin-top: 3px;
margin-left: 4px;
}
.post .sourceDiv span {
color: grey;
font-size: 10px;
}
.post .sourceDiv a:hover {
text-decoration: underline;
}
#source_flex_kunteynir {
display: flex;
flex-direction: column;
gap: 22px;
}
.sugglist {
padding-bottom: 5px;
padding-top: 5px;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 316 B

View file

@ -66,16 +66,16 @@ u(`#search_box input[type='search']`).on('input', async (e) => {
switch(section) {
case 'users':
results = await fetch(`/method/users.search?auth_mechanism=roaming&q=${query}&count=10&sort=4&fields=photo_50,status,nickname`)
results = await fetch(`/method/users.search?auth_mechanism=roaming&q=${encodeURIComponent(query)}&count=10&sort=4&fields=photo_50,status,nickname`)
break
case 'groups':
results = await fetch(`/method/groups.search?auth_mechanism=roaming&q=${query}&count=10&sort=4&fields=photo_50,description`)
results = await fetch(`/method/groups.search?auth_mechanism=roaming&q=${encodeURIComponent(query)}&count=10&sort=4&fields=photo_50,description`)
break
case 'videos':
results = await fetch(`/method/video.search?auth_mechanism=roaming&q=${query}&count=10&sort=4&extended=1`)
results = await fetch(`/method/video.search?auth_mechanism=roaming&q=${encodeURIComponent(query)}&count=10&sort=4&extended=1`)
break
case 'audios_playlists':
results = await fetch(`/method/audio.searchAlbums?auth_mechanism=roaming&query=${query}&limit=10`)
results = await fetch(`/method/audio.searchAlbums?auth_mechanism=roaming&query=${encodeURIComponent(query)}&limit=10`)
break
}

View file

@ -1145,7 +1145,7 @@ $(document).on("click", "#editPost", (e) => {
}
post.querySelector(".post-avatar").setAttribute("src", result.author.avatar)
post.querySelector(".post-author-name").innerHTML = result.author.name
post.querySelector(".post-author-name").innerHTML = result.author.name.escapeHtml()
post.querySelector(".really_text").setAttribute("data-text", result.new_text)
} else {
MessageBox(tr("error"), result.error, [tr("ok")], [Function.noop])
@ -1626,7 +1626,7 @@ $(document).on("click", ".avatarDelete", (e) => {
u("body").removeClass("dimmed");
document.querySelector("html").style.overflowY = "scroll"
u(".ovk-diag-cont").remove();
u(".ovk-diag-cont").remove()
document.querySelector("#bigAvatar").src = response.url
document.querySelector("#bigAvatar").parentNode.href = response.new_photo ? ("/photo" + response.new_photo) : "javascript:void(0)"
@ -1643,3 +1643,77 @@ $(document).on("click", ".avatarDelete", (e) => {
}),
]);
})
u(document).on('click', '#__sourceAttacher', (e) => {
MessageBox(tr('add_source'), `
<div id='source_flex_kunteynir'>
<span>${tr('set_source_tip')}</span>
<!-- давай, копируй ссылку и переходи по ней -->
<input type='text' maxlength='400' placeholder='https://www.youtube.com/watch?v=lkWuk_nzzVA'>
</div>
`, [tr('cancel')], [
() => {Function.noop}
])
__removeDialog = () => {
u("body").removeClass("dimmed");
document.querySelector("html").style.overflowY = "scroll"
u(".ovk-diag-cont").remove()
}
u('.ovk-diag-action').append(`
<button class='button' id='__setsrcbutton'>${tr('set_source')}</button>
`)
u('.ovk-diag-action #__setsrcbutton').on('click', async (ev) => {
// Consts
const _u_target = u(e.target)
const nearest_textarea = _u_target.closest('#write')
const source_output = nearest_textarea.find(`input[name='source']`)
const source_input = u(`#source_flex_kunteynir input[type='text']`)
const source_value = source_input.nodes[0].value ?? ''
if(source_value.length < 1) {
return
}
ev.target.classList.add('lagged')
// Checking link
const __checkCopyrightLinkRes = await fetch(`/method/wall.checkCopyrightLink?auth_mechanism=roaming&link=${encodeURIComponent(source_value)}`)
const checkCopyrightLink = await __checkCopyrightLinkRes.json()
// todo переписать блять мессенджбоксы чтоб они классами были
if(checkCopyrightLink.error_code) {
__removeDialog()
switch(checkCopyrightLink.error_code) {
default:
case 3102:
fastError(tr('error_adding_source_regex'))
return
case 3103:
fastError(tr('error_adding_source_long'))
return
case 3104:
fastError(tr('error_adding_source_sus'))
return
}
}
// Making indicator
__removeDialog()
source_output.attr('value', source_value)
nearest_textarea.find('.post-source').html(`
<span>${tr('source')}: <a target='_blank' href='${source_value.escapeHtml()}'>${ovk_proc_strtr(source_value.escapeHtml(), 50)}</a></span>
<div id='remove_source_button'></div>
`)
nearest_textarea.find('.post-source #remove_source_button').on('click', () => {
nearest_textarea.find('.post-source').html('')
source_output.attr('value', 'none')
})
})
u('.ovk-diag-body').attr('style', `padding:8px;`)
u('.ovk-diag-cont').attr('style', 'width: 325px;')
u('#source_flex_kunteynir input').nodes[0].focus()
})

View file

@ -286,6 +286,31 @@ function ovk_scheme(bool $with_slashes = false): string
return $scheme;
}
function check_copyright_link(string $link = ''): bool
{
if(!str_contains($link, "https://") && !str_contains($link, "http://"))
$link = "https://" . $link;
# Existability
if(is_null($link) || empty($link))
throw new \InvalidArgumentException("Empty link");
# Length
if(iconv_strlen($link) < 2 || iconv_strlen($link) > 400)
throw new \LengthException("Link is too long");
# Match URL regex
# stolen from http://urlregex.com/
if (!preg_match("%^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@|\d{1,3}(?:\.\d{1,3}){3}|(?:(?:[a-z\d\x{00a1}-\x{ffff}]+-?)*[a-z\d\x{00a1}-\x{ffff}]+|xn--[a-z\d-]+)(?:\.(?:[a-z\d\x{00a1}-\x{ffff}]+-?)*[a-z\d\x{00a1}-\x{ffff}]+)*(?:\.(?:xn--[a-z\d-]+|[a-z\x{00a1}-\x{ffff}]{2,6})))(?::\d+)?(?:[^\s]*)?$%iu", $link))
throw new \InvalidArgumentException("Invalid link format");
$banEntries = (new openvk\Web\Models\Repositories\BannedLinks)->check($link);
if(sizeof($banEntries) > 0)
throw new \LogicException("Suspicious link");
return true;
}
return (function() {
_ovk_check_environment();
require __DIR__ . "/vendor/autoload.php";

View file

@ -0,0 +1,2 @@
ALTER TABLE `posts`
ADD COLUMN `source` TEXT NULL DEFAULT NULL AFTER `api_source_name`;

View file

@ -111,7 +111,7 @@
"relationship_4" = "Married";
"relationship_5" = "In a civil marriage";
"relationship_6" = "In love";
"relationship_7" = "Everything is complicated";
"relationship_7" = "It's complicated";
"relationship_8" = "Actively searching";
/* xd */
@ -122,7 +122,7 @@
"relationship_6_prefix" = "with";
"relationship_7_prefix" = "with";
"politViews" = "Polit. Views";
"politViews" = "Political views";
"politViews_0" = "Not Selected";
"politViews_1" = "Indifferent";
@ -156,7 +156,7 @@
"updated_at" = "Updated at $1";
"user_banned" = "Unfortunately, we had to block the <b>$1</b> user page.";
"user_banned" = "Unfortunately, we had to block <b>$1's</b> user page.";
"user_banned_comment" = "Moderator's comment:";
"verified_page" = "Verified page";
"user_is_blocked" = "User is blocked";
@ -201,11 +201,11 @@
"pin" = "Pin";
"unpin" = "Unpin";
"pinned" = "pinned";
"comments_tip" = "Be first, who leaves a comment at this post!";
"comments_tip" = "Be the first to leave a comment on this post!";
"your_comment" = "Your comment";
"auditory" = "Auditory";
"in_wall" = "to user's wall";
"in_group" = "to group";
"in_wall" = "to my wall";
"in_group" = "to a group";
"shown" = "Shown";
"x_out_of" = "$1 of";
"wall_zero" = "no posts";
@ -223,6 +223,12 @@
"attachment" = "Attachment";
"post_as_group" = "Post as group";
"comment_as_group" = "Comment as group";
"add_source" = "Add source";
"set_source" = "Apply source";
"source" = "Source";
"set_source_tip" = "If you are using content from other authors, it is important to provide a source to the original.<br>You can do it below.";
"add_signature" = "Add signature";
/* ^ can be translated as "author's signature". ^ */
"contains_nsfw" = "Contains NSFW content";
@ -236,7 +242,7 @@
"no_posts_abstract" = "Nobody wrote anything here... So far.";
"attach_no_longer_available" = "This attachment is no longer available.";
"open_post" = "Open post";
"version_incompatibility" = "This attachment could not be displayed. Probably the database is incompatible with the current version of OpenVK.";
"version_incompatibility" = "This attachment could not be displayed. Probably because the database is incompatible with the current version of OpenVK.";
"graffiti" = "Graffiti";
@ -317,8 +323,8 @@
"create_group" = "Create group";
"group_managers" = "Managers";
"group_type" = "Group type";
"group_type_open" = "This is an open group, anyone can enter it.";
"group_type_closed" = "This is an closed group. To enter, you must submit an request.";
"group_type_open" = "This is an open group. Anyone can enter it.";
"group_type_closed" = "This is a closed group. To enter, you must submit a request.";
"creator" = "Creator";
"administrators" = "Administrators";
"add_to_left_menu" = "Add to left menu";
@ -357,12 +363,12 @@
"suggested_by_everyone_many" = "$1 suggested posts";
"suggested_by_everyone_other" = "$1 suggested posts";
"group_hide_from_global_feed" = "Don't display posts in the global feed";
"suggested_posts_by_you" = "Suggested posts by you";
"group_hide_from_global_feed" = "Don't display posts in global feed";
"suggested_posts_by_you" = "Your suggested posts";
"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.";
"suggested_posts_everyone" = "Posts suggested by users";
"no_suggested_posts_by_you" = "You haven't suggested any post to this group yet.";
"no_suggested_posts_by_people" = "No posts have been suggested to this group yet.";
"publish_suggested" = "Accept";
@ -377,23 +383,24 @@
"suggested_posts_in_group_many" = "This group has $1 suggested posts";
"suggested_posts_in_group_other" = "This group has $1 suggested posts";
"suggested_posts_in_group_by_you_zero" = "You haven't suggested any posts to this group";
"suggested_posts_in_group_by_you_zero" = "You haven't suggested any post to this group";
"suggested_posts_in_group_by_you_one" = "You suggested one post to this group";
"suggested_posts_in_group_by_you_few" = "You suggested $1 posts to this group";
"suggested_posts_in_group_by_you_many" = "You suggested $1 posts to this group";
"suggested_posts_in_group_by_you_other" = "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 it";
"suggestion_successfully_published" = "Post successfully published";
"suggestion_successfully_declined" = "Post successfully declined";
"suggestion_press_to_go" = "Go to post";
"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_declining_invalid_post" = "The suggested post you attempted to decline is invalid";
"error_declining_not_suggested_post" = "The post you attempted to decline is not suggested";
"error_declining_declined_post" = "This suggested post has already been declined";
"error_accepting_invalid_post" = "The suggested post you attempted to accept is invalid";
"error_accepting_not_suggested_post" = "The post you attempted to accept is not suggested";
"error_accepting_declined_post" = "This suggested post has already been 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";
"group_administrators_list" = "Admins list";
"group_display_only_creator" = "Display only group creator";
@ -483,6 +490,7 @@
"deleting_avatar" = "Deleting photo";
"deleting_avatar_sure" = "Do you sure you want to delete avatar?";
"deleted_avatar_notification" = "Picture deleted successfully";
"save_changes" = "Save changes";
"upd_m" = "updated his profile picture";
@ -493,7 +501,7 @@
"add_photos" = "Add photos";
"upload_picts" = "Upload photos";
"end_uploading" = "Finish uploading";
"photos_successfully_uploaded" = "Photos successfully uploaded";
"photos_successfully_uploaded" = "Photos uploaded successfully";
"click_to_go_to_album" = "Click here to go to album.";
"error_uploading_photo" = "Error when uploading photo";
"too_many_pictures" = "No more than 10 pictures";
@ -553,10 +561,10 @@
"error_attaching_note" = "Error when attaching note";
"select_or_create_new" = "Select existing note or <a href='/notes/create'>create new one</a>";
"select_or_create_new" = "Select an existing note or <a href='/notes/create'>create a new one</a>";
"notes_closed" = "You can't attach note to post, because only you can see them.<br> You can change it in <a href=\"/settings?act=privacy\">settings</a>.";
"do_not_attach_note" = "Do not attach note";
"notes_closed" = "You can't attach a note to the post because only you can see them.<br> You can change this in <a href=\"/settings?act=privacy\">settings</a>.";
"do_not_attach_note" = "Do not attach a note";
"something" = "Something";
"supports_xhtml" = "from (X)HTML supported.";
@ -579,15 +587,15 @@
"my_feed" = "My Feed";
"my_feedback" = "My Feedback";
"my_settings" = "My Settings";
"bug_tracker" = "Bug-tracker";
"bug_tracker" = "Bug Tracker";
"menu_settings" = "Settings";
"menu_login" = "Login";
"menu_registration" = "Registration";
"menu_registration" = "Register";
"menu_help" = "Help";
"menu_logout" = "Logout";
"menu_logout" = "Log out";
"menu_support" = "Support";
"header_home" = "home";
@ -834,6 +842,7 @@
/* Audios */
"my" = "My";
"audios" = "Audios";
"audio" = "Audio";
"playlist" = "Playlist";
@ -849,16 +858,16 @@
"limits" = "Limits";
"select_audio" = "Select audio from your computer";
"audio_requirements" = "Audio must be between $1 seconds to $2 minutes, weights to $3 MB and contain audio stream.";
"audio_requirements_2" = "Audio must not infringe copyright and related rights";
"you_can_also_add_audio_using" = "You can also add audio from among the files you have already downloaded using";
"audio_requirements" = "Audio must be between $1 seconds and $2 minutes, with a file size up to $3 MB, and contain an audio stream.";
"audio_requirements_2" = "Audio must not infringe copyright and related rights.";
"you_can_also_add_audio_using" = "You can also add audio from files you have already downloaded using";
"search_audio_inst" = "audios search";
"audio_embed_not_found" = "Audio not found";
"audio_embed_deleted" = "Audio was deleted";
"audio_embed_withdrawn" = "The audio was withdrawn at the request of the copyright holder";
"audio_embed_deleted" = "Audio has been deleted";
"audio_embed_withdrawn" = "The audio has been withdrawn at the request of the copyright holder";
"audio_embed_forbidden" = "The user's privacy settings do not allow this audio to be embedded";
"audio_embed_processing" = "Audio is still being processed, or has not been processed correctly.";
"audio_embed_processing" = "Audio is still being processed or has not been processed correctly.";
"audios_count_zero" = "No audios";
"audios_count_one" = "One audio";
@ -871,7 +880,7 @@
"my_music" = "My music";
"music_user" = "User's music";
"music_club" = "Club's music";
"music_club" = "Group's music";
"audio_new" = "New";
"audio_popular" = "Popular";
"audio_search" = "Search";
@ -914,19 +923,19 @@
"delete_playlist" = "Delete playlist";
"playlist_cover" = "Playlist cover";
"playlists_user" = "Users playlists";
"playlists_club" = "Groups playlists";
"playlists_user" = "User's playlists";
"playlists_club" = "Group's playlists";
"change_cover" = "Change cover";
"playlist_cover" = "Playlist's cover";
"minutes_count_zero" = "lasts no minutes";
"minutes_count_one" = "lasts one minute";
"minutes_count_few" = "lasts $1 minutes";
"minutes_count_many" = "lasts $1 minutes";
"minutes_count_other" = "lasts $1 minutes";
"minutes_count_zero" = "Lasts no minutes";
"minutes_count_one" = "Lasts one minute";
"minutes_count_few" = "Lasts $1 minutes";
"minutes_count_many" = "Lasts $1 minutes";
"minutes_count_other" = "Lasts $1 minutes";
"listens_count_zero" = "no listens";
"listens_count_one" = "one listen";
"listens_count_zero" = "No listens";
"listens_count_one" = "One listen";
"listens_count_few" = "$1 listens";
"listens_count_many" = "$1 listens";
"listens_count_other" = "$1 listens";
@ -949,7 +958,7 @@
"audio_successfully_uploaded" = "Audio has been successfully uploaded and is currently being processed.";
"broadcast_audio" = "Broadcast audio to status";
"sure_delete_playlist" = "Do you sure want to delete this playlist?";
"sure_delete_playlist" = "Are you sure you want to delete this playlist?";
"edit_audio" = "Edit audio";
"audios_group" = "Audios from group";
"playlists_group" = "Playlists from group";
@ -1057,7 +1066,7 @@
"coins_count" = "Number of votes";
"message" = "Message";
"failed_to_tranfer_points" = "Failed to transfer votes";
"failed_to_transfer_points" = "Failed to transfer votes";
"points_transfer_successful" = "You have successfully transferred <b>$1</b> to <b><a href=\"$2\">$3</a></b>.";
"not_all_information_has_been_entered" = "Not all information has been entered.";
@ -1076,8 +1085,8 @@
"apply_voucher" = "Apply voucher";
"failed_to_increase_rating" = "Failed to increase rating";
"rating_increase_successful" = "You have successfully increased rating of <b><a href=\"$1\">$2</a></b> by <b>$3%</b>.";
"negative_rating_value" = "We cannot steal rating from another person, sorry.";
"rating_increase_successful" = "You have successfully increased the rating of <b><a href=\"$1\">$2</a></b> by <b>$3%</b>.";
"negative_rating_value" = "You can't steal ratings from another person.";
"increased_your_rating_by" = "increased your rating by";
@ -1138,43 +1147,43 @@
"app_withdrawal_empty" = "Sorry, withdrawal of emptiness is not possible.";
"app_withdrawal_created" = "A request to withdraw $1 votes has been created. Awaiting crediting.";
"appjs_payment" = "Purchase payment";
"appjs_payment" = "Purchase Payment";
"appjs_payment_intro" = "You are about to pay for an order in the application";
"appjs_order_items" = "Order items";
"appjs_payment_total" = "Total amount payable";
"appjs_payment_confirm" = "Pay";
"appjs_err_funds" = "Failed to pay: insufficient funds.";
"appjs_wall_post" = "Publish a post";
"appjs_wall_post" = "Publish a Post";
"appjs_wall_post_desc" = "wants to publish a post on your wall";
"appjs_act_friends" = "your Friends";
"appjs_act_friends" = "Your Friends";
"appjs_act_friends_desc" = "add users as friends and read your friends list";
"appjs_act_wall" = "your Wall";
"appjs_act_wall" = "Your Wall";
"appjs_act_wall_desc" = "see your news, your wall and create posts on it";
"appjs_act_messages" = "your Messages";
"appjs_act_messages" = "Your Messages";
"appjs_act_messages_desc" = "read and write messages on your behalf";
"appjs_act_groups" = "your Groups";
"appjs_act_groups_desc" = "see a list of your groups and subscribe you to other";
"appjs_act_likes" = "Likes feature";
"appjs_act_groups" = "Your Groups";
"appjs_act_groups_desc" = "see a list of your groups and subscribe you to others";
"appjs_act_likes" = "Likes Feature";
"appjs_act_likes_desc" = "give and take away likes to posts";
"appjs_act_request" = "Access request";
"appjs_act_request" = "Access Request";
"appjs_act_requests" = "requests access to";
"appjs_act_can" = "The app will be able to";
"appjs_act_allow" = "Allow";
"appjs_act_disallow" = "Disallow";
"app_uninstalled" = "Application is disabled";
"app_uninstalled" = "Application Disabled";
"app_uninstalled_desc" = "It will no longer be able to perform actions on your behalf.";
"app_err_not_found" = "Application not found";
"app_err_not_found" = "Application Not Found";
"app_err_not_found_desc" = "Incorrect identifier or it has been disabled.";
"app_err_forbidden_desc" = "This application is not yours.";
"app_err_url" = "Incorrect address";
"app_err_url_desc" = "The address of the application did not pass the check, make sure it is correct.";
"app_err_ava" = "Unable to upload an avatar";
"app_err_ava_desc" = "Avatar too big or wrong: general error #$res.";
"app_err_note" = "Failed to attach a news note";
"app_err_forbidden_desc" = "This application does not belong to you.";
"app_err_url" = "Incorrect Address";
"app_err_url_desc" = "The address of the application did not pass the check; make sure it is correct.";
"app_err_ava" = "Unable to Upload an Avatar";
"app_err_ava_desc" = "Avatar is too big or incorrect: general error #$res.";
"app_err_note" = "Failed to Attach a News Note";
"app_err_note_desc" = "Make sure the link is correct and the note belongs to you.";
"learn_more" = "Learn more";
@ -1182,7 +1191,7 @@
/* Support */
"support_opened" = "Opened";
"support_answered" = "With a response";
"support_answered" = "Has a response";
"support_closed" = "Closed";
"support_ticket" = "Ticket";
"support_tickets" = "Tickets";
@ -1190,28 +1199,28 @@
"support_status_1" = "There's a response";
"support_status_2" = "Closed";
"support_greeting_hi" = "Greetings, $1!";
"support_greeting_regards" = "Best regards,<br/>$1 support team.";
"support_greeting_regards" = "Best regards,<br/>$1 Support Team.";
"support_faq" = "Frequently Asked Questions";
"support_list" = "List of tickets";
"support_new" = "New ticket";
"support_list" = "List of Tickets";
"support_new" = "New Ticket";
"support_new_title" = "Enter the topic of your ticket";
"support_new_content" = "Describe the issue or suggestion";
"reports" = "Reports";
"support_rate_good_answer" = "This is good answer";
"support_rate_bad_answer" = "This is bad answer";
"support_good_answer_user" = "You left a positive feedback.";
"support_bad_answer_user" = "You left a negative feedback.";
"support_good_answer_agent" = "User left a positive feedback.";
"support_bad_answer_agent" = "User left a negative feedback.";
"support_rated_good" = "You left a positive feedback about the answer.";
"support_rated_bad" = "You left a negative feedback about the answer.";
"support_rate_good_answer" = "This is a good answer";
"support_rate_bad_answer" = "This is a bad answer";
"support_good_answer_user" = "You left positive feedback.";
"support_bad_answer_user" = "You left negative feedback.";
"support_good_answer_agent" = "User left positive feedback.";
"support_bad_answer_agent" = "User left negative feedback.";
"support_rated_good" = "You left positive feedback about the answer.";
"support_rated_bad" = "You left negative feedback about the answer.";
"wrong_parameters" = "Invalid request parameters.";
"fast_answers" = "Fast answers";
"fast_answers" = "Quick Answers";
"ignore_report" = "Ignore report";
"report_number" = "Report #";
@ -1306,7 +1315,7 @@
"poll_editor_tips" = "Pressing backspace in empty option will remove it. Use Tab/Enter (in last option) to create new options faster.";
"poll_embed" = "Embed code";
"poll_voter_count_zero" = "Be <b>the first one</b> to vote!";
"poll_voter_count_zero" = "Be <b>the first</b> to vote!";
"poll_voter_count_one" = "<b>Only one</b> user voted.";
"poll_voter_count_few" = "<b>$1</b> users voted.";
"poll_voter_count_many" = "<b>$1</b> users voted.";
@ -1333,7 +1342,7 @@
"messages_other" = "$1 messages";
"topic_messages_count_zero" = "Topic has no messages";
"topic_messages_count_one" = "There are one message in the topic";
"topic_messages_count_one" = "There is one message in the topic";
"topic_messages_count_other" = "There are $1 messages in the topic";
"replied" = "replied";
@ -1399,7 +1408,7 @@
"photo_saved" = "Photo saved";
"photo_saved_comment" = "New profile picture will appear on your page";
"shared_succ" = "The post will appear on your wall. Click on the notification to go to your wall.";
"shared_succ" = "The post will appear on your wall. Click on this notification to go there.";
"invalid_email_address" = "Invalid Email address";
"invalid_email_address_comment" = "The Email you entered is not correct.";
@ -1553,6 +1562,10 @@
"ffmpeg_not_installed" = "Failed to proccess the file. It looks like ffmpeg is not installed on this server.";
"too_many_or_to_lack" = "Too few or too many sources.";
"error_adding_source_regex" = "Error adding source: incorrect link.";
"error_adding_source_long" = "Error adding source: link is too long.";
"error_adding_source_sus" = "Error adding source: suspicious link.";
/* Admin actions */
"login_as" = "Login as $1";
@ -1993,6 +2006,7 @@
/* Search */
"s_params" = "Search params";
"s_people" = "Users";
"s_groups" = "Clubs";
"s_apps" = "Applications";

View file

@ -207,6 +207,12 @@
"attachment" = "Вложение";
"post_as_group" = "От имени сообщества";
"comment_as_group" = "От имени сообщества";
"add_source" = "Добавление источника";
"set_source" = "Указать источник";
"source" = "Источник";
"set_source_tip" = "Если вы используете материалы других авторов, важно указывать ссылку на оригинал.<br>Сделать это вы можете ниже.";
"add_signature" = "Подпись автора";
"contains_nsfw" = "Содержит NSFW-контент";
"nsfw_warning" = "Данный пост может содержать 18+ контент";
@ -791,6 +797,7 @@
/* Audios */
"my" = "Моё";
"audios" = "Аудиозаписи";
"audio" = "Аудиозапись";
"playlist" = "Плейлист";
@ -870,7 +877,7 @@
"remove_from_playlist" = "Удалить из плейлиста";
"delete_playlist" = "Удалить плейлист";
"playlist_cover" = "Обложка плейлиста";
"playlists_user" = "Плейлисты польз.";
"playlists_user" = "Плейлисты пользователя";
"playlists_club" = "Плейлисты группы";
"change_cover" = "Сменить обложку";
"playlist_cover" = "Обложка плейлиста";
@ -1455,6 +1462,10 @@
"ffmpeg_not_installed" = "Не удалось обработать файл. Похоже, на сервере не установлен ffmpeg.";
"too_many_or_to_lack" = "Слишком мало либо слишком много источников.";
"error_adding_source_regex" = "Ошибка добавления источника: некорректная ссылка.";
"error_adding_source_long" = "Ошибка добавления источника: слишком длинная ссылка.";
"error_adding_source_sus" = "Ошибка добавления источника: подозрительная ссылка.";
/* Admin actions */
"login_as" = "Войти как $1";
@ -1883,6 +1894,7 @@
/* Search */
"s_params" = "Параметры поиска";
"s_people" = "Пользователи";
"s_groups" = "Группы";
"s_apps" = "Приложения";