Compare commits

...

7 commits

Author SHA1 Message Date
ayato
fb839757ee
Merge ee8b436185 into b7fc23bb52 2025-04-19 15:54:50 +00:00
Vladimir Barinov
b7fc23bb52
fix(pda), chore(locale): adapt for pda (#1276) 2025-04-19 17:37:59 +03:00
mrilyew
3e0c508ca1
fix(music, user, club): several patches (#1267)
- Исправлено 500 в плейлистах
- Заметил, что вместо причины бана отображается "подозрительная
активность", вернул показ оригинала сообщения

![1](https://github.com/user-attachments/assets/9a804076-8baf-4ab6-9294-82771480bcc0)
- Порядок подписчиков в группе идёт теперь от первых подписавшихся (об
этом вроде #1260)
- Удалены лишние методы deleteComment, поскольку есть
wall.deleteComment, временно~ урезано поле "attachments" в некоторых
методах. У фото теперь есть orig_photo
- Исправлена меняющаяся на один пиксель кнопка "воспроизвести" в плеере
в chrome и #1256,
2025-04-04 17:48:25 +03:00
mrilyew
be65f81a4a
fix(audio, favorites): big patch #1259
# Аудио
- Добавлена вкладка в аудио "загруженное" которую я забыл добавить в
октябре 2023. Она показывает загруженные аудио в порядке возрастания.
Есть так же идеи для вкладок "недавно прослушанное" и "ваши друзья
слушают" (последняя добавит иммерсивности, или как это называется,
персональности на сайт, хотя по факту она просто соберёт id всех друзей
и выдаст их недавние добавления в коллекцию), но тогда будет
нагромождение вкладок и придётся какое то сворачивание добавлять.
- Если аудио больше 10 и ты на странице аудио, то показывается мелкая
кнопка в правом нижнем углу которая развернёт счётчик и пагинатор.
- Если аудио обрабатывается (processed как я назвал в css) то появится
кнопка "всё равно хочу воспроизвести".
- При переключении трека меняется заголовок вкладки
- Если ты вызвал контекстное меню но оно ушло за экран, то оно будет
повыше.

# Уязвимости
- Убрана уязвимость в audio api что можно было посмотреть айди владельца
удалённого аудио. В целом непонятно использование id в тексте ошибки,
поскольку он не должен изменятся и быть обобщённым, по типу access to
audio denied. Но да ладно, коду три года всё таки.
- Удалённый контент заменяется "[deleted]" в /fave. Я бы на самом деле
не добавлял это, но меня испугал последний абзац fixed #1258 поэтому
добавил.
2025-03-28 17:04:13 +03:00
Vladimir Barinov
a12c77083b
fix(links, away) (fixes #1253) 2025-03-16 17:57:21 +03:00
Vladimir Barinov
4815186b79
chore(statistics): change behavior of active users count (#1254) 2025-03-16 17:53:58 +03:00
Vladimir Barinov
9ef7d2d7c4
fix(age): calculation (fixes #1252) 2025-03-16 16:51:05 +03:00
27 changed files with 408 additions and 462 deletions

View file

@ -18,7 +18,7 @@ final class Audio extends VKAPIRequestHandler
if (!$audio) {
$this->fail(0o404, "Audio not found");
} elseif (!$audio->canBeViewedBy($this->getUser())) {
$this->fail(201, "Access denied to audio(" . $audio->getPrettyId() . ")");
$this->fail(201, "Access denied to audio(" . $audio->getId() . ")");
}
# рофлан ебало
@ -201,7 +201,7 @@ final class Audio extends VKAPIRequestHandler
$this->fail(15, "Access denied");
}
if ($uploaded_only) {
if ($uploaded_only && $owner_id == $this->getUser()->getRealId()) {
return DatabaseConnection::i()->getContext()->table("audios")
->where([
"deleted" => false,
@ -283,7 +283,7 @@ final class Audio extends VKAPIRequestHandler
}
$dbCtx = DatabaseConnection::i()->getContext();
if ($uploaded_only == 1) {
if ($uploaded_only == 1 && $owner_id == $this->getUser()->getRealId()) {
if ($owner_id <= 0) {
$this->fail(8, "uploaded_only can only be used with owner_id > 0");
}

View file

@ -14,8 +14,7 @@ use openvk\Web\Models\Entities\{Topic, Comment, User, Photo, Video};
final class Board extends VKAPIRequestHandler
{
# 13/13
public function addTopic(int $group_id, string $title, string $text = "", bool $from_group = true, string $attachments = "")
public function addTopic(int $group_id, string $title, string $text = "", bool $from_group = true)
{
$this->requireUser();
$this->willExecuteWriteAction();
@ -23,15 +22,14 @@ final class Board extends VKAPIRequestHandler
$club = (new ClubsRepo())->get($group_id);
if (!$club) {
$this->fail(403, "Invalid club");
$this->fail(15, "Access denied");
}
if (!$club->canBeModifiedBy($this->getUser()) && !$club->isEveryoneCanCreateTopics()) {
$this->fail(403, "Access to club denied");
$this->fail(15, "Access denied");
}
$flags = 0;
if ($from_group == true && $club->canBeModifiedBy($this->getUser())) {
$flags |= 0b10000000;
}
@ -53,59 +51,6 @@ final class Board extends VKAPIRequestHandler
$comment->setCreated(time());
$comment->setFlags($flags);
$comment->save();
if (!empty($attachments)) {
$attachmentsArr = explode(",", $attachments);
# блин а мне это везде копировать типа
if (sizeof($attachmentsArr) > 10) {
$this->fail(50, "Error: too many attachments");
}
foreach ($attachmentsArr as $attac) {
$attachmentType = null;
if (str_contains($attac, "photo")) {
$attachmentType = "photo";
} elseif (str_contains($attac, "video")) {
$attachmentType = "video";
} else {
$this->fail(205, "Unknown attachment type");
}
$attachment = str_replace($attachmentType, "", $attac);
$attachmentOwner = (int) explode("_", $attachment)[0];
$attachmentId = (int) end(explode("_", $attachment));
$attacc = null;
if ($attachmentType == "photo") {
$attacc = (new PhotosRepo())->getByOwnerAndVID($attachmentOwner, $attachmentId);
if (!$attacc || $attacc->isDeleted()) {
$this->fail(100, "Photo does not exists");
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this photo");
}
$comment->attach($attacc);
} elseif ($attachmentType == "video") {
$attacc = (new VideosRepo())->getByOwnerAndVID($attachmentOwner, $attachmentId);
if (!$attacc || $attacc->isDeleted()) {
$this->fail(100, "Video does not exists");
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this video");
}
$comment->attach($attacc);
}
}
}
}
return $topic->getId();
@ -118,7 +63,7 @@ final class Board extends VKAPIRequestHandler
$topic = (new TopicsRepo())->getTopicById($group_id, $topic_id);
if (!$topic || !$topic->getClub() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
if (!$topic || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
return 0;
}
@ -140,21 +85,15 @@ final class Board extends VKAPIRequestHandler
}
$topic = (new TopicsRepo())->getTopicById($group_id, $topic_id);
if (!$topic || $topic->isDeleted() || $topic->isClosed()) {
$this->fail(100, "Topic is deleted, closed or invalid.");
$this->fail(15, "Access denied");
}
$flags = 0;
if ($from_group != 0 && !is_null($topic->getClub()) && $topic->getClub()->canBeModifiedBy($this->user)) {
$flags |= 0b10000000;
}
if (strlen($message) > 300) {
$this->fail(20, "Comment is too long.");
}
$comment = new Comment();
$comment->setOwner($this->getUser()->getId());
$comment->setModel(get_class($topic));
@ -164,74 +103,9 @@ final class Board extends VKAPIRequestHandler
$comment->setFlags($flags);
$comment->save();
if (!empty($attachments)) {
$attachmentsArr = explode(",", $attachments);
if (sizeof($attachmentsArr) > 10) {
$this->fail(50, "Error: too many attachments");
}
foreach ($attachmentsArr as $attac) {
$attachmentType = null;
if (str_contains($attac, "photo")) {
$attachmentType = "photo";
} elseif (str_contains($attac, "video")) {
$attachmentType = "video";
} else {
$this->fail(205, "Unknown attachment type");
}
$attachment = str_replace($attachmentType, "", $attac);
$attachmentOwner = (int) explode("_", $attachment)[0];
$attachmentId = (int) end(explode("_", $attachment));
$attacc = null;
if ($attachmentType == "photo") {
$attacc = (new PhotosRepo())->getByOwnerAndVID($attachmentOwner, $attachmentId);
if (!$attacc || $attacc->isDeleted()) {
$this->fail(100, "Photo does not exists");
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this photo");
}
$comment->attach($attacc);
} elseif ($attachmentType == "video") {
$attacc = (new VideosRepo())->getByOwnerAndVID($attachmentOwner, $attachmentId);
if (!$attacc || $attacc->isDeleted()) {
$this->fail(100, "Video does not exists");
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this video");
}
$comment->attach($attacc);
}
}
}
return $comment->getId();
}
public function deleteComment(int $comment_id, int $group_id = 0, int $topic_id = 0)
{
$this->requireUser();
$this->willExecuteWriteAction();
$comment = (new CommentsRepo())->get($comment_id);
if ($comment->isDeleted() || !$comment || !$comment->canBeDeletedBy($this->getUser())) {
$this->fail(403, "Access to comment denied");
}
$comment->delete();
return 1;
}
public function deleteTopic(int $group_id, int $topic_id)
{
$this->requireUser();
@ -248,25 +122,6 @@ final class Board extends VKAPIRequestHandler
return 1;
}
public function editComment(string $message, string $attachments, int $comment_id, int $group_id = 0, int $topic_id = 0)
{
# FIXME
/*
$this->requireUser();
$this->willExecuteWriteAction();
$comment = (new CommentsRepo)->get($comment_id);
if($comment->getOwner() != $this->getUser()->getId())
$this->fail(15, "Access to comment denied");
$comment->setContent($message);
$comment->setEdited(time());
$comment->save();
*/
return 1;
}
public function editTopic(int $group_id, int $topic_id, string $title)
{
$this->requireUser();

View file

@ -248,7 +248,7 @@ final class Photos extends VKAPIRequestHandler
];
}
public function createAlbum(string $title, int $group_id = 0, string $description = "", int $privacy = 0)
public function createAlbum(string $title, int $group_id = 0, string $description = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
@ -257,7 +257,7 @@ final class Photos extends VKAPIRequestHandler
$club = (new Clubs())->get((int) $group_id);
if (!$club || !$club->canBeModifiedBy($this->getUser())) {
$this->fail(20, "Invalid club");
$this->fail(15, "Access denied");
}
}
@ -271,162 +271,133 @@ final class Photos extends VKAPIRequestHandler
return $album->toVkApiStruct($this->getUser());
}
public function editAlbum(int $album_id, int $owner_id, string $title, string $description = "", int $privacy = 0)
public function editAlbum(int $album_id, int $owner_id, string $title = null, string $description = null, int $privacy = 0)
{
$this->requireUser();
$this->willExecuteWriteAction();
$album = (new Albums())->getAlbumByOwnerAndId($owner_id, $album_id);
if (!$album || $album->isDeleted()) {
$this->fail(2, "Invalid album");
if (!$album || $album->isDeleted() || $album->isCreatedBySystem()) {
$this->fail(114, "Invalid album id");
}
if (empty($title)) {
$this->fail(25, "Title is empty");
}
if ($album->isCreatedBySystem()) {
$this->fail(40, "You can't change system album");
}
if (!$album->canBeModifiedBy($this->getUser())) {
$this->fail(2, "Access to album denied");
$this->fail(15, "Access denied");
}
$album->setName($title);
$album->setDescription($description);
if (!is_null($title) && !empty($title) && !ctype_space($title)) {
$album->setName($title);
}
if (!is_null($description)) {
$album->setDescription($description);
}
$album->save();
try {
$album->save();
} catch (\Throwable $e) {
return 1;
}
return $album->toVkApiStruct($this->getUser());
return 1;
}
public function getAlbums(int $owner_id, string $album_ids = "", int $offset = 0, int $count = 100, bool $need_system = true, bool $need_covers = true, bool $photo_sizes = false)
public function getAlbums(int $owner_id = null, string $album_ids = "", int $offset = 0, int $count = 100, bool $need_system = true, bool $need_covers = true, bool $photo_sizes = false)
{
$this->requireUser();
$res = [];
$res = [
"count" => 0,
"items" => [],
];
$albums_list = [];
if ($owner_id == null && empty($album_ids)) {
$owner_id = $this->getUser()->getId();
}
if (empty($album_ids)) {
$owner = get_entity_by_id($owner_id);
if (!$owner || !$owner->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access denied");
}
if ($owner_id > 0 && !$owner->getPrivacyPermission('photos.read', $this->getUser())) {
$this->fail(15, "Access denied");
}
$albums_list = null;
if ($owner_id > 0) {
$user = (new UsersRepo())->get($owner_id);
$res = [
"count" => (new Albums())->getUserAlbumsCount($user),
"items" => [],
];
if (!$user || $user->isDeleted()) {
$this->fail(2, "Invalid user");
}
if (!$user->getPrivacyPermission('photos.read', $this->getUser())) {
$this->fail(21, "This user chose to hide his albums.");
}
$albums = array_slice(iterator_to_array((new Albums())->getUserAlbums($user, 1, $count + $offset)), $offset);
foreach ($albums as $album) {
if (!$need_system && $album->isCreatedBySystem()) {
continue;
}
$res["items"][] = $album->toVkApiStruct($this->getUser(), $need_covers, $photo_sizes);
}
# TODO rewrite to offset
$albums_list = array_slice(iterator_to_array((new Albums())->getUserAlbums($owner, 1, $count + $offset)), $offset);
$res["count"] = (new Albums())->getUserAlbumsCount($owner);
} else {
$club = (new Clubs())->get($owner_id * -1);
$res = [
"count" => (new Albums())->getClubAlbumsCount($club),
"items" => [],
];
if (!$club) {
$this->fail(2, "Invalid club");
}
$albums = array_slice(iterator_to_array((new Albums())->getClubAlbums($club, 1, $count + $offset)), $offset);
foreach ($albums as $album) {
if (!$need_system && $album->isCreatedBySystem()) {
continue;
}
$res["items"][] = $album->toVkApiStruct($this->getUser(), $need_covers, $photo_sizes);
}
$albums_list = array_slice(iterator_to_array((new Albums())->getClubAlbums($owner, 1, $count + $offset)), $offset);
$res["count"] = (new Albums())->getClubAlbumsCount($owner);
}
} else {
$albums = explode(',', $album_ids);
$res = [
"count" => sizeof($albums),
"items" => [],
];
foreach ($albums as $album) {
$id = explode("_", $album);
$album = (new Albums())->getAlbumByOwnerAndId((int) $id[0], (int) $id[1]);
if ($album && !$album->isDeleted()) {
if (!$need_system && $album->isCreatedBySystem()) {
continue;
}
$res["items"][] = $album->toVkApiStruct($this->getUser(), $need_covers, $photo_sizes);
$album_ids = explode(',', $album_ids);
foreach ($album_ids as $album_id) {
$album = (new Albums())->getAlbumByOwnerAndId((int) $owner_id, (int) $album_id);
if (!$album || $album->isDeleted() || !$album->canBeViewedBy($this->getUser())) {
continue;
}
$albums_list[] = $album;
}
}
foreach ($albums_list as $album) {
if (!$need_system && $album->isCreatedBySystem()) { # TODO use queries
continue;
}
$res["items"][] = $album->toVkApiStruct($this->getUser(), $need_covers, $photo_sizes);
}
return $res;
}
public function getAlbumsCount(int $user_id = 0, int $group_id = 0)
public function getAlbumsCount(int $user_id = null, int $group_id = null)
{
$this->requireUser();
if ($user_id == 0 && $group_id == 0 || $user_id > 0 && $group_id > 0) {
$this->fail(21, "Select user_id or group_id");
if (is_null($user_id) && is_null($group_id)) {
$user_id = $this->getUser()->getId();
}
if ($user_id > 0) {
$us = (new UsersRepo())->get($user_id);
if (!$us || $us->isDeleted()) {
$this->fail(21, "Invalid user");
if (!is_null($user_id)) {
$__user = (new UsersRepo())->get($user_id);
if (!$__user || $__user->isDeleted() || !$__user->getPrivacyPermission('photos.read', $this->getUser())) {
$this->fail(15, "Access denied");
}
if (!$us->getPrivacyPermission('photos.read', $this->getUser())) {
$this->fail(21, "This user chose to hide his albums.");
return (new Albums())->getUserAlbumsCount($__user);
}
if (!is_null($group_id)) {
$__club = (new Clubs())->get($group_id);
if (!$__club || !$__club->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access denied");
}
return (new Albums())->getUserAlbumsCount($us);
return (new Albums())->getClubAlbumsCount($__club);
}
if ($group_id > 0) {
$cl = (new Clubs())->get($group_id);
if (!$cl) {
$this->fail(21, "Invalid club");
}
return (new Albums())->getClubAlbumsCount($cl);
}
return 0;
}
public function getById(string $photos, bool $extended = false, bool $photo_sizes = false)
{
$this->requireUser();
$phts = explode(",", $photos);
$photos_splitted_list = explode(",", $photos);
$res = [];
if (sizeof($photos_splitted_list) > 78) {
$this->fail(-78, "Photos count must not exceed limit");
}
foreach ($phts as $phota) {
$ph = explode("_", $phota);
$photo = (new PhotosRepo())->getByOwnerAndVID((int) $ph[0], (int) $ph[1]);
if (!$photo || $photo->isDeleted()) {
$this->fail(21, "Invalid photo");
}
if (!$photo->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access denied");
foreach ($photos_splitted_list as $photo_id) {
$photo_s_id = explode("_", $photo_id);
$photo = (new PhotosRepo())->getByOwnerAndVID((int) $photo_s_id[0], (int) $photo_s_id[1]);
if (!$photo || $photo->isDeleted() || !$photo->canBeViewedBy($this->getUser())) {
continue;
}
$res[] = $photo->toVkApiStruct($photo_sizes, $extended);
@ -443,12 +414,7 @@ final class Photos extends VKAPIRequestHandler
if (empty($photo_ids)) {
$album = (new Albums())->getAlbumByOwnerAndId($owner_id, $album_id);
if (!$album || $album->isDeleted()) {
$this->fail(21, "Invalid album");
}
if (!$album->canBeViewedBy($this->getUser())) {
if (!$album || $album->isDeleted() || !$album->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access denied");
}
@ -459,11 +425,15 @@ final class Photos extends VKAPIRequestHandler
if (!$photo || $photo->isDeleted()) {
continue;
}
$res["items"][] = $photo->toVkApiStruct($photo_sizes, $extended);
}
} else {
$photos = explode(',', $photo_ids);
$photos = array_unique(explode(',', $photo_ids));
if (sizeof($photos) > 78) {
$this->fail(-78, "Photos count must not exceed limit");
}
$res = [
"count" => sizeof($photos),
@ -473,10 +443,12 @@ final class Photos extends VKAPIRequestHandler
foreach ($photos as $photo) {
$id = explode("_", $photo);
$phot = (new PhotosRepo())->getByOwnerAndVID((int) $id[0], (int) $id[1]);
if ($phot && !$phot->isDeleted() && $phot->canBeViewedBy($this->getUser())) {
$res["items"][] = $phot->toVkApiStruct($photo_sizes, $extended);
$photo_entity = (new PhotosRepo())->getByOwnerAndVID((int) $id[0], (int) $id[1]);
if (!$photo_entity || $photo_entity->isDeleted() || !$photo_entity->canBeViewedBy($this->getUser())) {
continue;
}
$res["items"][] = $photo_entity->toVkApiStruct($photo_sizes, $extended);
}
}
@ -490,12 +462,8 @@ final class Photos extends VKAPIRequestHandler
$album = (new Albums())->get($album_id);
if (!$album || $album->canBeModifiedBy($this->getUser())) {
$this->fail(21, "Invalid album");
}
if ($album->isDeleted()) {
$this->fail(22, "Album already deleted");
if (!$album || $album->isDeleted() || $album->isCreatedBySystem() || !$album->canBeModifiedBy($this->getUser())) {
$this->fail(15, "Access denied");
}
$album->delete();
@ -510,12 +478,8 @@ final class Photos extends VKAPIRequestHandler
$photo = (new PhotosRepo())->getByOwnerAndVID($owner_id, $photo_id);
if (!$photo) {
$this->fail(21, "Invalid photo");
}
if ($photo->isDeleted()) {
$this->fail(21, "Photo is deleted");
if (!$photo || $photo->isDeleted() || !$photo->canBeModifiedBy($this->getUser())) {
$this->fail(21, "Access denied");
}
if (!empty($caption)) {
@ -526,60 +490,48 @@ final class Photos extends VKAPIRequestHandler
return 1;
}
public function delete(int $owner_id, int $photo_id, string $photos = "")
public function delete(int $owner_id = null, int $photo_id = null, string $photos = null)
{
$this->requireUser();
$this->willExecuteWriteAction();
if (empty($photos)) {
if (!$owner_id) {
$owner_id = $this->getUser()->getId();
}
if (is_null($photos)) {
if (is_null($photo_id)) {
return 0;
}
$photo = (new PhotosRepo())->getByOwnerAndVID($owner_id, $photo_id);
if ($this->getUser()->getId() !== $photo->getOwner()->getId()) {
$this->fail(21, "You can't delete another's photo");
}
if (!$photo) {
$this->fail(21, "Invalid photo");
}
if ($photo->isDeleted()) {
$this->fail(21, "Photo is already deleted");
if (!$photo || $photo->isDeleted() || !$photo->canBeModifiedBy($this->getUser())) {
return 1;
}
$photo->delete();
} else {
$photozs = explode(',', $photos);
$photos_list = array_unique(explode(',', $photos));
if (sizeof($photos_list) > 10) {
$this->fail(-78, "Photos count must not exceed limit");
}
foreach ($photozs as $photo) {
$id = explode("_", $photo);
$phot = (new PhotosRepo())->getByOwnerAndVID((int) $id[0], (int) $id[1]);
if ($this->getUser()->getId() !== $phot->getOwner()->getId()) {
$this->fail(21, "You can't delete another's photo");
foreach ($photos_list as $photo_id) {
$id = explode("_", $photo_id);
$photo = (new PhotosRepo())->getByOwnerAndVID((int) $id[0], (int) $id[1]);
if (!$photo || $photo->isDeleted() || !$photo->canBeModifiedBy($this->getUser())) {
continue;
}
if (!$phot) {
$this->fail(21, "Invalid photo");
}
if ($phot->isDeleted()) {
$this->fail(21, "Photo already deleted");
}
$phot->delete();
$photo->delete();
}
}
return 1;
}
public function getAllComments(int $owner_id, int $album_id, bool $need_likes = false, int $offset = 0, int $count = 100)
{
$this->fail(501, "Not implemented");
}
public function deleteComment(int $comment_id, int $owner_id = 0)
# Поскольку комментарии едины, можно использовать метод "wall.deleteComment".
/*public function deleteComment(int $comment_id, int $owner_id = 0)
{
$this->requireUser();
$this->willExecuteWriteAction();
@ -596,9 +548,9 @@ final class Photos extends VKAPIRequestHandler
$comment->delete();
return 1;
}
}*/
public function createComment(int $owner_id, int $photo_id, string $message = "", string $attachments = "", bool $from_group = false)
public function createComment(int $owner_id, int $photo_id, string $message = "", bool $from_group = false)
{
$this->requireUser();
$this->willExecuteWriteAction();
@ -609,12 +561,8 @@ final class Photos extends VKAPIRequestHandler
$photo = (new PhotosRepo())->getByOwnerAndVID($owner_id, $photo_id);
if (!$photo || $photo->isDeleted()) {
$this->fail(180, "Invalid photo");
}
if (!$photo->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access to photo denied");
if (!$photo || $photo->isDeleted() || !$photo->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access denied");
}
$comment = new Comment();
@ -625,55 +573,6 @@ final class Photos extends VKAPIRequestHandler
$comment->setCreated(time());
$comment->save();
if (!empty($attachments)) {
$attachmentsArr = explode(",", $attachments);
if (sizeof($attachmentsArr) > 10) {
$this->fail(50, "Error: too many attachments");
}
foreach ($attachmentsArr as $attac) {
$attachmentType = null;
if (str_contains($attac, "photo")) {
$attachmentType = "photo";
} elseif (str_contains($attac, "video")) {
$attachmentType = "video";
} else {
$this->fail(205, "Unknown attachment type");
}
$attachment = str_replace($attachmentType, "", $attac);
$attachmentOwner = (int) explode("_", $attachment)[0];
$attachmentId = (int) end(explode("_", $attachment));
$attacc = null;
if ($attachmentType == "photo") {
$attacc = (new PhotosRepo())->getByOwnerAndVID($attachmentOwner, $attachmentId);
if (!$attacc || $attacc->isDeleted()) {
$this->fail(100, "Photo does not exists");
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this photo");
}
$comment->attach($attacc);
} elseif ($attachmentType == "video") {
$attacc = (new VideosRepo())->getByOwnerAndVID($attachmentOwner, $attachmentId);
if (!$attacc || $attacc->isDeleted()) {
$this->fail(100, "Video does not exists");
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this video");
}
$comment->attach($attacc);
}
}
}
return $comment->getId();
}
@ -682,16 +581,12 @@ final class Photos extends VKAPIRequestHandler
$this->requireUser();
if ($owner_id < 0) {
$this->fail(4, "This method doesn't works with clubs");
$this->fail(-413, "Clubs are not supported");
}
$user = (new UsersRepo())->get($owner_id);
if (!$user) {
$this->fail(4, "Invalid user");
}
if (!$user->getPrivacyPermission('photos.read', $this->getUser())) {
$this->fail(21, "This user chose to hide his albums.");
if (!$user || !$user->getPrivacyPermission('photos.read', $this->getUser())) {
$this->fail(15, "Access denied");
}
$photos = (new PhotosRepo())->getEveryUserPhoto($user, $offset, $count);
@ -717,12 +612,8 @@ final class Photos extends VKAPIRequestHandler
$photo = (new PhotosRepo())->getByOwnerAndVID($owner_id, $photo_id);
$comms = array_slice(iterator_to_array($photo->getComments(1, $offset + $count)), $offset);
if (!$photo || $photo->isDeleted()) {
$this->fail(4, "Invalid photo");
}
if (!$photo->canBeViewedBy($this->getUser())) {
$this->fail(21, "Access denied");
if (!$photo || $photo->isDeleted() || !$photo->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access denied");
}
$res = [

View file

@ -26,7 +26,9 @@ class Album extends MediaCollection
{
$coverPhoto = $this->getCoverPhoto();
if (!$coverPhoto) {
return "/assets/packages/static/openvk/img/camera_200.png";
$server_url = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
return $server_url . "/assets/packages/static/openvk/img/camera_200.png";
}
return $coverPhoto->getURL();
@ -92,14 +94,13 @@ class Album extends MediaCollection
{
$res = (object) [];
$res->id = $this->getPrettyId();
$res->vid = $this->getId();
$res->id = $this->getId();
$res->thumb_id = !is_null($this->getCoverPhoto()) ? $this->getCoverPhoto()->getPrettyId() : 0;
$res->owner_id = $this->getOwner()->getId();
$res->owner_id = $this->getOwner()->getRealId();
$res->title = $this->getName();
$res->description = $this->getDescription();
$res->created = $this->getCreationTime()->timestamp();
$res->updated = $this->getEditTime() ? $this->getEditTime()->timestamp() : null;
$res->updated = $this->getEditTime() ? $this->getEditTime()->timestamp() : $res->created;
$res->size = $this->size();
$res->privacy_comment = 1;
$res->upload_by_admins_only = 1;

View file

@ -323,7 +323,7 @@ class Club extends RowModel
return sizeof($this->getFollowersQuery());
}
public function getFollowers(int $page = 1, int $perPage = 6, string $sort = "follower ASC"): \Traversable
public function getFollowers(int $page = 1, int $perPage = 6, string $sort = "target DESC"): \Traversable
{
$rels = $this->getFollowersQuery($sort)->page($page, $perPage);

View file

@ -349,7 +349,6 @@ class Photo extends Media
$res->width = $this->getDimensions()[0];
$res->height = $this->getDimensions()[1];
$res->date = $res->created = $this->getPublicationTime()->timestamp();
if ($photo_sizes) {
$res->sizes = array_values($this->getVkApiSizes());
$res->src_small = $res->photo_75 = $this->getURLBySizeId("miniscule");
@ -359,14 +358,19 @@ class Photo extends Media
$res->src_xxbig = $res->photo_1280 = $this->getURLBySizeId("larger");
$res->src_xxxbig = $res->photo_2560 = $this->getURLBySizeId("original");
$res->src_original = $res->url = $this->getURLBySizeId("UPLOADED_MAXRES");
$res->orig_photo = [
"height" => $res->height,
"width" => $res->width,
"type" => "base",
"url" => $this->getURL(),
];
}
if ($extended) {
$res->likes = $this->getLikesCount(); # их нету но пусть будут
$res->likes = $this->getLikesCount();
$res->comments = $this->getCommentsCount();
$res->tags = 0;
$res->can_comment = 1;
$res->can_repost = 0;
$res->can_repost = 1;
}
return $res;

View file

@ -41,11 +41,11 @@ trait TRichText
return preg_replace_callback(
"%(([A-z]++):\/\/(\S*?\.\S*?))([\s)\[\]{},\"\'<]|\.\s|$)%",
(function (array $matches): string {
$href = str_replace("#", "&num;", $matches[1]);
$href = rawurlencode(str_replace(";", "&#59;", $href));
$link = str_replace("#", "&num;", $matches[3]);
$href = rawurlencode($matches[1]);
$href = str_replace("%26amp%3B", "%26", $href);
$link = $matches[3];
# this string breaks ampersands
$link = str_replace(";", "&#59;", $link);
# $link = str_replace(";", "&#59;", $link);
$rel = $this->isAd() ? "sponsored" : "ugc";
/*$server_domain = str_replace(':' . $_SERVER['SERVER_PORT'], '', $_SERVER['HTTP_HOST']);

View file

@ -306,10 +306,10 @@ class User extends RowModel
$content_type = $matches[1];
$content_id = (int) $matches[2];
if (in_array($content_type, ["noSpamTemplate", "user"])) {
$reason = "Подозрительная активность";
$reason = $this->getRawBanReason();
} else {
if ($for !== "banned") {
$reason = "Подозрительная активность";
$reason = $this->getRawBanReason();
} else {
$reason = [$this->getTextForContentBan($content_type), $content_type];
switch ($content_type) {
@ -524,7 +524,10 @@ class User extends RowModel
public function getAge(): ?int
{
return (int) floor((time() - $this->getBirthday()->timestamp()) / YEAR);
$birthday = new \DateTime();
$birthday->setTimestamp($this->getBirthday()->timestamp());
$today = new \DateTime();
return (int) $today->diff($birthday)->y;
}
public function get2faSecret(): ?string

View file

@ -51,6 +51,7 @@ class Photos
"deleted" => 0,
"system" => 0,
"private" => 0,
"anonymous" => 0,
])->order("id DESC");
foreach ($photos->limit($limit, $offset) as $photo) {
@ -65,6 +66,7 @@ class Photos
"deleted" => 0,
"system" => 0,
"private" => 0,
"anonymous" => 0,
]);
return sizeof($photos);

View file

@ -157,7 +157,7 @@ class Users
{
return (object) [
"all" => (clone $this->users)->count('*'),
"active" => (clone $this->users)->where("online > 0")->count('*'),
"active" => (clone $this->users)->where("online >= ?", time() - MONTH)->count('*'),
"online" => (clone $this->users)->where("online >= ?", time() - 900)->count('*'),
];
}

View file

@ -78,6 +78,10 @@ final class AudioPresenter extends OpenVKPresenter
} elseif ($mode === "new") {
$audios = $this->audios->getNew();
$audiosCount = $audios->size();
} elseif ($mode === "uploaded") {
$stream = $this->audios->getByUploader($this->user->identity);
$audios = $stream->page($page, 10);
$audiosCount = $stream->size();
} elseif ($mode === "playlists") {
if ($owner < 0) {
$entity = (new Clubs())->get(abs($owner));
@ -130,6 +134,11 @@ final class AudioPresenter extends OpenVKPresenter
}
}
public function renderUploaded()
{
$this->renderList(null, "uploaded");
}
public function renderEmbed(int $owner, int $id): void
{
$audio = $this->audios->getByOwnerAndVID($owner, $id);
@ -841,6 +850,10 @@ final class AudioPresenter extends OpenVKPresenter
$audios = [$found_audio];
$audiosCount = 1;
break;
case "uploaded":
$stream = $this->audios->getByUploader($this->user->identity);
$audios = $stream->page($page, $perPage);
$audiosCount = $stream->size();
}
$pagesCount = ceil($audiosCount / $perPage);

View file

@ -20,7 +20,7 @@ final class AwayPresenter extends OpenVKPresenter
header("HTTP/1.0 302 Found");
header("X-Robots-Tag: noindex, nofollow, noarchive");
header("Location: " . $this->queryParam("to"));
header("Location: " . rawurldecode($this->queryParam("to")));
exit;
}

View file

@ -371,7 +371,11 @@ abstract class OpenVKPresenter extends SimplePresenter
$whichbrowser->isEngine('NetFront') || // PSP and other japanese portable systems
$whichbrowser->isOs('Android') ||
$whichbrowser->isOs('iOS') ||
$whichbrowser->isBrowser('Internet Explorer', '<=', '8')) {
$whichbrowser->isBrowser('BlackBerry Browser') ||
$whichbrowser->isBrowser('Internet Explorer', '<=', '8') ||
$whichbrowser->isBrowser('Firefox', '<=', '47') ||
$whichbrowser->isBrowser('Safari', '<=', '7') ||
$whichbrowser->isBrowser('Google Chrome', '<=', '35')) {
// yeah, it's old, but ios and android are?
if ($whichbrowser->isOs('iOS') && $whichbrowser->isOs('iOS', '<=', '9')) {
return true;

View file

@ -9,6 +9,8 @@
{/if}
{elseif $mode == 'new'}
{_audio_new}
{elseif $mode == 'uploaded'}
{_my_audios_small_uploaded}
{elseif $mode == 'popular'}
{_audio_popular}
{elseif $mode == 'alone_audio'}
@ -32,6 +34,12 @@
</div>
</div>
<div n:if="$mode == 'uploaded'">
{_my_audios_small}
»
{_my_audios_small_uploaded}
</div>
<div n:if="$mode == 'new'">
{_audios}
»
@ -58,7 +66,7 @@
{block content}
{* ref: https://archive.li/P32em *}
{include "bigplayer.xml"}
{include "bigplayer.xml", buttonsShow_summary => $audiosCount > 10}
<script>
window.__current_page_audio_context = null
@ -68,6 +76,12 @@
entity_id: {$ownerId},
page: {$page}
}
{elseif $mode == 'uploaded'}
window.__current_page_audio_context = {
name: 'uploaded',
entity_id: 0,
page: {$page}
}
{elseif $mode == 'alone_audio'}
window.__current_page_audio_context = {
name: 'alone_audio',
@ -77,6 +91,22 @@
{/if}
</script>
<div n:if="isset($audios)" class='summaryBarHideable summaryBar summaryBarFlex padding' style="margin: 0px -10px;width: 99.5%;display: none;">
<div class='summary'>
<b>{tr("is_x_audio", $audiosCount)}</b>
</div>
{include "../components/paginator.xml", conf => (object) [
"page" => $page,
"count" => $audiosCount,
"amount" => sizeof($audios),
"perPage" => $perPage ?? OPENVK_DEFAULT_PER_PAGE,
"atTop" => true,
"space" => 6,
"tidy" => true,
]}
</div>
<div class="audiosDiv">
<div class="audiosContainer audiosSideContainer audiosPaddingContainer" n:if="$mode != 'playlists'">
<div n:if="$audiosCount <= 0" style='height: 100%;'>

View file

@ -52,5 +52,9 @@
<div class="shuffleButton musicIcon" data-tip='simple' data-title="{_shuffle_tip}"></div>
<div class="deviceButton musicIcon" data-tip='simple' data-title="{_mute_tip} [M]"></div>
</div>
<div class="absoluteButtons">
<div n:if="$buttonsShow_summary" id="summarySwitchButton">-</div>
</div>
</div>
</div>

View file

@ -2,6 +2,7 @@
<div class="verticalGrayTabs">
<div class='with_padding'>
<a n:if="isset($thisUser)" n:attr="id => $mode === 'list' && $isMy ? 'used' : 'ki'" href="/audios{$thisUser->getId()}">{_my_music}</a>
<a n:attr="id => $mode === 'uploaded' ? 'used' : 'ki'" href="/audios/uploaded">{_my_audios_small_uploaded}</a>
{* TODO: show upload link as and plusick (little plus) in button up*}
<a n:if="isset($thisUser)" href="/player/upload{if $isMyClub}?gid={abs($ownerId)}{/if}">{_upload_audio}</a>
<a n:if="isset($thisUser)" n:attr="id => $mode === 'new' ? 'used' : 'ki'" href="/search?section=audios">{_audio_new}</a>
@ -13,7 +14,7 @@
<a n:if="isset($thisUser)" href="/audios/newPlaylist">{_new_playlist}</a>
{if !$isMy && $mode !== 'popular' && $mode !== 'new' && $mode != 'alone_audio'}
{if !$isMy && $mode !== 'popular' && $mode !== 'new' && $mode != 'alone_audio' && $mode != 'uploaded'}
<hr>
<a n:if="!$isMy" n:attr="id => $mode === 'list' ? 'used' : 'ki'" href="/audios{$ownerId}">{if $ownerId > 0}{_music_user}{else}{_music_club}{/if}</a>

View file

@ -36,24 +36,30 @@
</style>
{if $count > 0}
{foreach $data as $dat}
{if $section == "posts"}
<div class="scroll_node">
{include "../components/post.xml", post => $dat, commentSection => true}
</div>
{elseif $section == "comments"}
<div class="scroll_node">
{include "../components/comment.xml", comment => $dat, correctLink => true, no_reply_button => true}
</div>
{elseif $section == "photos"}
<div class="album-photo scroll_node" onclick="OpenMiniature(event, {$dat->getURLBySizeId('larger')}, null, {$dat->getPrettyId()}, null)">
<a href="/photo{$dat->getPrettyId()}">
<img class="album-photo--image" src="{$dat->getURLBySizeId('tinier')}" alt="{$dat->getDescription()}" loading="lazy" />
</a>
</div>
{elseif $section == "videos"}
<div class="scroll_node">
{include "../components/video.xml", video => $dat}
{if $dat->isDeleted()}
<div n:class="deleted_mark, $section == 'photos' ? album-photo : deleted_mark_average">
<span>[deleted]</span>
</div>
{else}
{if $section == "posts"}
<div class="scroll_node">
{include "../components/post.xml", post => $dat, commentSection => true}
</div>
{elseif $section == "comments"}
<div class="scroll_node">
{include "../components/comment.xml", comment => $dat, correctLink => true, no_reply_button => true}
</div>
{elseif $section == "photos"}
<div class="album-photo scroll_node" onclick="OpenMiniature(event, {$dat->getURLBySizeId('larger')}, null, {$dat->getPrettyId()}, null)">
<a href="/photo{$dat->getPrettyId()}">
<img class="album-photo--image" src="{$dat->getURLBySizeId('tinier')}" alt="{$dat->getDescription()}" loading="lazy" />
</a>
</div>
{elseif $section == "videos"}
<div class="scroll_node">
{include "../components/video.xml", video => $dat}
</div>
{/if}
{/if}
{/foreach}
{else}

View file

@ -596,6 +596,17 @@
</table>
</form>
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center" id="_js_settings">
<tbody>
<tr>
<td width="120" valign="top" align="right"></td>
<td>
<a href="javascript:openJsSettings()">{_ui_settings_window}</a>
</td>
</tr>
</tbody>
</table>
<h4>{_ui_settings_sidebar}</h4>
<form action="/settings?act=lMenu" method="POST" enctype="multipart/form-data">
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">

View file

@ -201,6 +201,8 @@ routes:
handler: "Audio->upload"
- url: "/audios{num}"
handler: "Audio->list"
- url: "/audios/uploaded"
handler: "Audio->uploaded"
- url: "/audio{num}/listen"
handler: "Audio->listen"
- url: "/audio{num}_{num}"

View file

@ -52,6 +52,34 @@
height: 46px;
}
.bigPlayer .bigPlayerWrapper .absoluteButtons {
position: absolute;
bottom: 0;
right: 0;
}
.bigPlayer .bigPlayerWrapper .absoluteButtons > div {
width: 8px;
height: 8px;
font-size: 9px;
display: flex;
align-items: center;
justify-content: center;
background: #ebebeb;
border: 1px solid #c3c3c3;
border-bottom: unset;
border-right: unset;
color: #c3c3c3;
cursor: pointer;
user-select: none;
}
.bigPlayer .bigPlayerWrapper .absoluteButtons > div:active {
background: #c3c3c3;
color: #ebebeb;
}
/* Play button and arrows */
.bigPlayer .playButtons {
display: flex;
@ -313,7 +341,7 @@
opacity: 0.8;
}
.audioEmbed.processed {
.audioEmbed.processed .playerButton {
filter: opacity(0.6);
}

View file

@ -99,6 +99,7 @@ button.bsdn_playButton {
padding-left: 0;
font-size: 22px;
cursor: pointer;
width: 22px;
}
.bsdn_fullScreenButton, .bsdn_repeatButton {

View file

@ -2919,7 +2919,6 @@ a.poll-retract-vote {
position: relative;
}
/* не говновёрстка, а пиксель-пёрфект) */
.page_header.search_expanded.search_expanded_at_all #search_and_one_more_wrapper {
width: 547px;
}
@ -3039,6 +3038,10 @@ a.poll-retract-vote {
gap: 1px;
}
.verticalGrayTabsPad {
padding: 0px 0px 0px 8px;
}
.searchList hr, .verticalGrayTabs hr {
width: 153px;
margin-left: 0px;
@ -4278,3 +4281,7 @@ hr {
height: 30px;
background-position-y: 9px;
}
.deleted_mark_average {
padding: 5px 61px;
}

View file

@ -68,7 +68,7 @@ u(document).on('click', '#__feed_settings_link', (e) => {
`
MessageBox(tr("feed_settings"), body, [tr("close")], [Function.noop])
u('.ovk-diag-body').attr('style', 'padding:0px;height: 255px;')
u('.ovk-diag-body').attr('style', 'padding:0px;height: 255px;overflow: hidden;')
async function __switchTab(tab)
{
@ -84,8 +84,6 @@ u(document).on('click', '#__feed_settings_link', (e) => {
const CURRENT_PERPAGE = Number(__temp_url.searchParams.get('posts') ?? 10)
const CURRENT_PAGE = Number(__temp_url.searchParams.get('p') ?? 1)
const CURRENT_RETURN_BANNED = Number(__temp_url.searchParams.get('return_banned') ?? 0)
const CURRENT_AUTO_SCROLL = Number(localStorage.getItem('ux.auto_scroll') ?? 1)
const CURRENT_DISABLE_AJAX = Number(localStorage.getItem('ux.disable_ajax_routing') ?? 0)
const COUNT = [1, 5, 10, 20, 30, 40, 50]
u('#_feed_settings_container #__content').html(`
<table cellspacing="7" cellpadding="0" border="0" align="center">
@ -116,26 +114,6 @@ u(document).on('click', '#__feed_settings_link', (e) => {
<label for='showIgnored'>${tr('show_ignored_sources')}</label>
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">
<input type='checkbox' data-act='localstorage_item' data-inverse="1" name='ux.disable_ajax_routing' id="ux.disable_ajax_routing" ${CURRENT_DISABLE_AJAX == 0 ? 'checked' : ''}>
</span>
</td>
<td>
<label for='ux.disable_ajax_routing'>${tr('ajax_routing')}</label>
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">
<input type='checkbox' data-act='localstorage_item' name='ux.auto_scroll' id="ux.auto_scroll" ${CURRENT_AUTO_SCROLL == 1 ? 'checked' : ''}>
</span>
</td>
<td>
<label for='ux.auto_scroll'>${tr('auto_scroll')}</label>
</td>
</tr>
<tr>
<td width="120" valign="top">
</td>
@ -299,3 +277,32 @@ u(document).on('change', `input[data-act='localstorage_item']`, (e) => {
localStorage.setItem(e.target.name, Number(e.target.checked))
})
function openJsSettings() {
const CURRENT_AUTO_SCROLL = Number(localStorage.getItem('ux.auto_scroll') ?? 1)
const CURRENT_DISABLE_AJAX = Number(localStorage.getItem('ux.disable_ajax_routing') ?? 0)
u("#_js_settings td").remove()
u("#_js_settings").append(`
<tr>
<td width="120" valign="top">
<span class="nobold">
<input type='checkbox' data-act='localstorage_item' data-inverse="1" name='ux.disable_ajax_routing' id="ux.disable_ajax_routing" ${CURRENT_DISABLE_AJAX == 0 ? 'checked' : ''}>
</span>
</td>
<td>
<label for='ux.disable_ajax_routing'>${tr('ajax_routing')}</label>
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">
<input type='checkbox' data-act='localstorage_item' name='ux.auto_scroll' id="ux.auto_scroll" ${CURRENT_AUTO_SCROLL == 1 ? 'checked' : ''}>
</span>
</td>
<td>
<label for='ux.auto_scroll'>${tr('auto_scroll')}</label>
</td>
</tr>
`)
}

View file

@ -54,6 +54,9 @@ window.player = new class {
current_track_id = 0
tracks = []
// time type:
// 0 - shows remaining time before end
// 1 - shows full track time
get timeType() {
return localStorage.getItem('audio.timeType') ?? 0
}
@ -62,6 +65,7 @@ window.player = new class {
localStorage.setItem('audio.timeType', value)
}
// <audio> tag
get audioPlayer() {
return this.__realAudioPlayer
}
@ -231,6 +235,9 @@ window.player = new class {
'query': this.context.object.query,
}))
break
case "uploaded":
form_data.append('context', this.context.object.name)
break
case 'alone_audio':
form_data.append('context', this.context.object.name)
form_data.append('context_entity', this.context.object.entity_id)
@ -322,6 +329,8 @@ window.player = new class {
this.__updateFace()
u(this.audioPlayer).trigger('volumechange')
document.title = ovk_proc_strtr(escapeHtml(`${window.player.currentTrack.performer}${window.player.currentTrack.name}`), 255)
}
hasContext() {
@ -377,7 +386,7 @@ window.player = new class {
}
await this.setTrack(this.previousTrack.id)
if(!this.currentTrack.available || this.currentTrack.withdrawn) {
if(/*!this.currentTrack.available || */this.currentTrack.withdrawn) {
if(!this.previousTrack) {
return
}
@ -394,7 +403,7 @@ window.player = new class {
}
await this.setTrack(this.nextTrack.id)
if(!this.currentTrack.available || this.currentTrack.withdrawn) {
if(/*!this.currentTrack.available || */this.currentTrack.withdrawn) {
if(!this.nextTrack) {
return
}
@ -664,6 +673,8 @@ window.player = new class {
})
}
// the listen counts if you reach half of song
// but it doesnt checks on server normally so you can "накрутить" listens
async __countListen() {
let playlist = 0
if(!this.listen_coef) {
@ -787,6 +798,10 @@ window.player = new class {
}
})
}
toggleSummary() {
$(".summaryBarHideable").slideToggle(300, "linear")
}
}
document.addEventListener("DOMContentLoaded", async () => {
@ -1163,7 +1178,25 @@ u(document).on("drop", '.audiosContainer', function(e) {
}
})
u(document).on("click", "#summarySwitchButton", (e) => {
if(u(".summaryBarHideable").nodes[0].style.overflow == "hidden") {
return
}
if(u(e.target).html() == "-") {
u(e.target).html("+")
} else {
u(e.target).html("-")
}
window.player.toggleSummary()
})
u(document).on('contextmenu', '.bigPlayer, .audioEmbed, #ajax_audio_player', (e) => {
if(e.shiftKey) {
return
}
e.preventDefault()
u('#ctx_menu').remove()
@ -1179,6 +1212,10 @@ u(document).on('contextmenu', '.bigPlayer, .audioEmbed, #ajax_audio_player', (e)
x = e.pageX - rx
y = e.pageY - ry
if((rect.height + rect.top) + 100 > window.innerHeight) {
y = ((rect.height + 120) * -1)
}
const ctx_u = u(`
<div id='ctx_menu' style='top:${y}px;left:${x}px;' data-type='ctx_type'>
<a id='audio_ctx_copy'>${tr('copy_link_to_audio')}</a>
@ -1194,7 +1231,7 @@ u(document).on('contextmenu', '.bigPlayer, .audioEmbed, #ajax_audio_player', (e)
<a id='audio_ctx_add_to_playlist'>${tr('audio_ctx_add_to_playlist')}</a>
${ctx_type == 'main_player' ? `
<a id='audio_ctx_clear_context'>${tr('audio_ctx_clear_context')}</a>` : ''}
${ctx_type == 'main_player' ? `<a href='https://github.com/mrilyew' target='_blank'>BigPlayer v1.1 by MrIlyew</a>` : ''}
${ctx_type == 'main_player' ? `<a href='https://github.com/mrilyew' target='_blank'>BigPlayer v1.2 by MrIlyew</a>` : ''}
</div>
`)
u(parent).append(ctx_u)
@ -1948,8 +1985,12 @@ $(document).on("click", ".audioEmbed.processed .playerButton", (e) => {
title: tr('error'),
body: tr('audio_embed_processing'),
unique_name: 'processing_notify',
buttons: [tr('ok')],
callbacks: [Function.noop]
buttons: [tr("audio_embed_processing_bait"), tr('ok')],
callbacks: [() => {
const pl = u(e.target).closest(".audioEmbed")
pl.removeClass("processed")
pl.find(".playIcon").trigger("click")
}, Function.noop]
})
})

View file

@ -703,6 +703,7 @@
"round_avatars" = "Round";
"apply_style_for_this_device" = "Apply style only for this device";
"ui_settings_window" = "Advanced settings";
"search_for_groups" = "Search for groups";
"search_for_users" = "Search for users";
@ -928,7 +929,8 @@
"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 processing.";
"audio_embed_processing_bait" = "Play anyway";
"audios_count_zero" = "No audios";
"audios_count_one" = "One audio";
@ -947,6 +949,7 @@
"audio_search" = "Search";
"my_audios_small" = "My audios";
"my_audios_small_uploaded" = "Uploaded";
"my_playlists" = "My playlists";
"playlists" = "Playlists";
"audios_explicit" = "Contains obscene language";
@ -1038,6 +1041,12 @@
"audio_ctx_play_next" = "Play next";
"audio_ctx_clear_context" = "Clear tracks list";
"is_x_audio_zero" = "No audios";
"is_x_audio_one" = "Just one audio.";
"is_x_audio_few" = "Just $1 audios.";
"is_x_audio_many" = "Just $1 audios.";
"is_x_audio_other" = "Just $1 audios.";
/* Notifications */
"feedback" = "Feedback";
@ -2226,6 +2235,7 @@
"mobile_like" = "Like";
"mobile_user_info_hide" = "Hide";
"mobile_user_info_show_details" = "Show details";
"mobile_attachment_only_for_pc" = "This attachments is not implemented for PDA version. Please, view it on PC.";
/* Fullscreen player */

View file

@ -676,6 +676,7 @@
"cut" = "Квадратные";
"round_avatars" = "Круглые";
"apply_style_for_this_device" = "Применить стиль только для этого устройства";
"ui_settings_window" = "Дополнительные настройки";
"search_for_groups" = "Поиск групп";
"search_for_users" = "Поиск людей";
"search_for_posts" = "Поиск записей";
@ -883,7 +884,8 @@
"audio_embed_deleted" = "Аудиозапись была удалена";
"audio_embed_withdrawn" = "Аудиозапись была изъята по обращению правообладателя.";
"audio_embed_forbidden" = "Настройки приватности пользователя не позволяют встраивать эту композицию";
"audio_embed_processing" = "Аудио ещё обрабатывается, либо обработалось неправильно.";
"audio_embed_processing" = "Аудио находится в обработке.";
"audio_embed_processing_bait" = "Всё равно хочу воспроизвести";
"audios_count_zero" = "Нет аудиозаписей";
"audios_count_one" = "Одна аудиозапись"; /* сингл */
@ -902,6 +904,7 @@
"audio_search" = "Поиск";
"my_audios_small" = "Мои аудиозаписи";
"my_audios_small_uploaded" = "Загруженное";
"my_playlists" = "Мои плейлисты";
"playlists" = "Плейлисты";
"audios_explicit" = "Содержит нецензурную лексику";
@ -994,6 +997,12 @@
"audio_ctx_play_next" = "Воспроизвести следующим";
"audio_ctx_clear_context" = "Очистить список треков";
"is_x_audio_zero" = "Нету аудиозаписей";
"is_x_audio_one" = "Всего одна аудиозапись.";
"is_x_audio_few" = "Всего $1 аудиозаписи.";
"is_x_audio_many" = "Всего $1 аудиозаписей.";
"is_x_audio_other" = "Всего $1 аудиозаписей.";
/* Notifications */
"feedback" = "Ответы";
@ -2121,6 +2130,7 @@
"mobile_like" = "Нравится";
"mobile_user_info_hide" = "Скрыть";
"mobile_user_info_show_details" = "Показать подробнее";
"mobile_attachment_only_for_pc" = "Вложение недоступно в PDA версии, его просмотр возможен только с другого устройства";
/* Fullscreen player */
@ -2297,3 +2307,4 @@
"faves_few" = "$1 закладки";
"faves_many" = "$1 закладок";
"faves_other" = "$1 закладок";

View file

@ -660,3 +660,17 @@ ul {
.doc_icon.no_image span::before {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAKCAYAAABmBXS+AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsAAAA7AAWrWiQkAAABXSURBVChTY2RAAoFe9ZuEBOR93314uHn9tkY/qDADE5QGA5ACZBoGUBThAkQpYkyOmvcfysYJiDMJROAzbe6yJEZ4EGBTCFIAolHCCVkhTAFWgGkiAwMAzxkZ3qVQ7YEAAAAASUVORK5CYII=");
}
.bigPlayer .bigPlayerWrapper .absoluteButtons > div {
background: #1e1a2b;
border: 1px solid #2c2640;
}
.insertedPhoto {
background: #1e1a2b;
border: 1px solid #403a56;
}
.ovk-modal-player-window #ovk-player-info {
background: #0e0b1a;
}