mirror of
https://github.com/openvk/openvk
synced 2024-12-25 18:11:07 +03:00
Merge branch 'master' into ignored-sources
This commit is contained in:
commit
484b19dd8c
209 changed files with 16192 additions and 1075 deletions
|
@ -30,7 +30,7 @@ If you want, you can add your instance to the list above so that people can regi
|
||||||
|
|
||||||
1. Install PHP 7.4, web-server, Composer, Node.js, Yarn and [Chandler](https://github.com/openvk/chandler)
|
1. Install PHP 7.4, web-server, Composer, Node.js, Yarn and [Chandler](https://github.com/openvk/chandler)
|
||||||
|
|
||||||
* PHP 8.1 is supported too, however it was not tested carefully, so be aware.
|
* PHP 8 is still being tested; the functionality of the engine on this version of PHP is not yet guaranteed.
|
||||||
|
|
||||||
2. Install MySQL-compatible database.
|
2. Install MySQL-compatible database.
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ _[English](README.md)_
|
||||||
|
|
||||||
1. Установите PHP 7.4, веб-сервер, Composer, Node.js, Yarn и [Chandler](https://github.com/openvk/chandler)
|
1. Установите PHP 7.4, веб-сервер, Composer, Node.js, Yarn и [Chandler](https://github.com/openvk/chandler)
|
||||||
|
|
||||||
* PHP 8 еще **не** тестировался, поэтому не стоит ожидать, что он будет работать (UPD: он не работает).
|
* PHP 8 пока ещё тестируется, работоспособность движка на этой версии PHP пока не гарантируется.
|
||||||
|
|
||||||
2. Установите MySQL-совместимую базу данных.
|
2. Установите MySQL-совместимую базу данных.
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,9 @@ class Notes implements Handler
|
||||||
assert($noteOwner instanceof User);
|
assert($noteOwner instanceof User);
|
||||||
if(!$noteOwner->getPrivacyPermission("notes.read", $this->user))
|
if(!$noteOwner->getPrivacyPermission("notes.read", $this->user))
|
||||||
$reject(160, "You don't have permission to access this note");
|
$reject(160, "You don't have permission to access this note");
|
||||||
|
|
||||||
|
if(!$note->canBeViewedBy($this->user))
|
||||||
|
$reject(15, "Access to note denied");
|
||||||
|
|
||||||
$resolve([
|
$resolve([
|
||||||
"title" => $note->getName(),
|
"title" => $note->getName(),
|
||||||
|
|
92
ServiceAPI/Photos.php
Normal file
92
ServiceAPI/Photos.php
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\ServiceAPI;
|
||||||
|
use openvk\Web\Models\Entities\User;
|
||||||
|
use openvk\Web\Models\Repositories\{Photos as PhotosRepo, Albums, Clubs};
|
||||||
|
|
||||||
|
class Photos implements Handler
|
||||||
|
{
|
||||||
|
protected $user;
|
||||||
|
protected $photos;
|
||||||
|
|
||||||
|
function __construct(?User $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
$this->photos = new PhotosRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPhotos(int $page = 1, int $album = 0, callable $resolve, callable $reject)
|
||||||
|
{
|
||||||
|
if($album == 0) {
|
||||||
|
$photos = $this->photos->getEveryUserPhoto($this->user, $page, 24);
|
||||||
|
$count = $this->photos->getUserPhotosCount($this->user);
|
||||||
|
} else {
|
||||||
|
$album = (new Albums)->get($album);
|
||||||
|
|
||||||
|
if(!$album || $album->isDeleted())
|
||||||
|
$reject(55, "Invalid .");
|
||||||
|
|
||||||
|
if($album->getOwner() instanceof User) {
|
||||||
|
if($album->getOwner()->getId() != $this->user->getId())
|
||||||
|
$reject(555, "Access to album denied");
|
||||||
|
} else {
|
||||||
|
if(!$album->getOwner()->canBeModifiedBy($this->user))
|
||||||
|
$reject(555, "Access to album denied");
|
||||||
|
}
|
||||||
|
|
||||||
|
$photos = $album->getPhotos($page, 24);
|
||||||
|
$count = $album->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
$arr = [
|
||||||
|
"count" => $count,
|
||||||
|
"items" => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach($photos as $photo) {
|
||||||
|
$res = json_decode(json_encode($photo->toVkApiStruct()), true);
|
||||||
|
|
||||||
|
$arr["items"][] = $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolve($arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAlbums(int $club, callable $resolve, callable $reject)
|
||||||
|
{
|
||||||
|
$albumsRepo = (new Albums);
|
||||||
|
|
||||||
|
$count = $albumsRepo->getUserAlbumsCount($this->user);
|
||||||
|
$albums = $albumsRepo->getUserAlbums($this->user, 1, $count);
|
||||||
|
|
||||||
|
$arr = [
|
||||||
|
"count" => $count,
|
||||||
|
"items" => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach($albums as $album) {
|
||||||
|
$res = ["id" => $album->getId(), "name" => $album->getName()];
|
||||||
|
|
||||||
|
$arr["items"][] = $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($club > 0) {
|
||||||
|
$cluber = (new Clubs)->get($club);
|
||||||
|
|
||||||
|
if(!$cluber || !$cluber->canBeModifiedBy($this->user))
|
||||||
|
$reject(1337, "Invalid (club), or you can't modify him");
|
||||||
|
|
||||||
|
$clubCount = (new Albums)->getClubAlbumsCount($cluber);
|
||||||
|
$clubAlbums = (new Albums)->getClubAlbums($cluber, 1, $clubCount);
|
||||||
|
|
||||||
|
foreach($clubAlbums as $albumr) {
|
||||||
|
$res = ["id" => $albumr->getId(), "name" => $albumr->getName()];
|
||||||
|
|
||||||
|
$arr["items"][] = $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
$arr["count"] = $arr["count"] + $clubCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolve($arr);
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,7 +46,7 @@ class Search implements Handler
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$res = $repo->find($query, ["doNotSearchMe" => $this->user->getId()], $sort);
|
$res = $repo->find($query, ["doNotSearchMe" => $this->user->getId(), "doNotSearchPrivate" => true,], $sort);
|
||||||
|
|
||||||
$results = array_slice(iterator_to_array($res), 0, 5);
|
$results = array_slice(iterator_to_array($res), 0, 5);
|
||||||
|
|
||||||
|
|
156
ServiceAPI/Video.php
Normal file
156
ServiceAPI/Video.php
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\ServiceAPI;
|
||||||
|
|
||||||
|
use openvk\Web\Models\Entities\{User, Post};
|
||||||
|
use openvk\Web\Models\Repositories\{Videos, Comments, Clubs};
|
||||||
|
use Chandler\MVC\Routing\Router;
|
||||||
|
|
||||||
|
class Video implements Handler
|
||||||
|
{
|
||||||
|
protected $user;
|
||||||
|
protected $videos;
|
||||||
|
protected $comments;
|
||||||
|
protected $groups;
|
||||||
|
|
||||||
|
function __construct(?User $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
$this->videos = new Videos;
|
||||||
|
$this->comments = new Comments;
|
||||||
|
$this->groups = new Clubs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVideo(int $id, callable $resolve, callable $reject)
|
||||||
|
{
|
||||||
|
$video = $this->videos->get($id);
|
||||||
|
|
||||||
|
if(!$video || $video->isDeleted()) {
|
||||||
|
$reject(2, "Video does not exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(method_exists($video, "canBeViewedBy") && !$video->canBeViewedBy($this->user)) {
|
||||||
|
$reject(4, "Access to video denied");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$video->getOwner()->getPrivacyPermission('videos.read', $this->user)) {
|
||||||
|
$reject(8, "Access to video denied: this user chose to hide his videos");
|
||||||
|
}
|
||||||
|
|
||||||
|
$prevVideo = NULL;
|
||||||
|
$nextVideo = NULL;
|
||||||
|
$lastVideo = $this->videos->getLastVideo($video->getOwner());
|
||||||
|
|
||||||
|
if($video->getVirtualId() - 1 != 0) {
|
||||||
|
for($i = $video->getVirtualId(); $i != 0; $i--) {
|
||||||
|
$maybeVideo = (new Videos)->getByOwnerAndVID($video->getOwner()->getId(), $i);
|
||||||
|
|
||||||
|
if(!is_null($maybeVideo) && !$maybeVideo->isDeleted() && $maybeVideo->getId() != $video->getId()) {
|
||||||
|
if(method_exists($maybeVideo, "canBeViewedBy") && !$maybeVideo->canBeViewedBy($this->user)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$prevVideo = $maybeVideo;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_null($lastVideo) || $lastVideo->getId() == $video->getId()) {
|
||||||
|
$nextVideo = NULL;
|
||||||
|
} else {
|
||||||
|
for($i = $video->getVirtualId(); $i <= $lastVideo->getVirtualId(); $i++) {
|
||||||
|
$maybeVideo = (new Videos)->getByOwnerAndVID($video->getOwner()->getId(), $i);
|
||||||
|
|
||||||
|
if(!is_null($maybeVideo) && !$maybeVideo->isDeleted() && $maybeVideo->getId() != $video->getId()) {
|
||||||
|
if(method_exists($maybeVideo, "canBeViewedBy") && !$maybeVideo->canBeViewedBy($this->user)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nextVideo = $maybeVideo;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = [
|
||||||
|
"id" => $video->getId(),
|
||||||
|
"title" => $video->getName(),
|
||||||
|
"owner" => $video->getOwner()->getId(),
|
||||||
|
"commentsCount" => $video->getCommentsCount(),
|
||||||
|
"description" => $video->getDescription(),
|
||||||
|
"type" => $video->getType(),
|
||||||
|
"name" => $video->getOwner()->getCanonicalName(),
|
||||||
|
"pretty_id" => $video->getPrettyId(),
|
||||||
|
"virtual_id" => $video->getVirtualId(),
|
||||||
|
"published" => (string)$video->getPublicationTime(),
|
||||||
|
"likes" => $video->getLikesCount(),
|
||||||
|
"has_like" => $video->hasLikeFrom($this->user),
|
||||||
|
"author" => $video->getOwner()->getCanonicalName(),
|
||||||
|
"canBeEdited" => $video->getOwner()->getId() == $this->user->getId(),
|
||||||
|
"isProcessing" => $video->getType() == 0 && $video->getURL() == "/assets/packages/static/openvk/video/rendering.mp4",
|
||||||
|
"prevVideo" => !is_null($prevVideo) ? $prevVideo->getId() : null,
|
||||||
|
"nextVideo" => !is_null($nextVideo) ? $nextVideo->getId() : null,
|
||||||
|
];
|
||||||
|
|
||||||
|
if($video->getType() == 1) {
|
||||||
|
$res["embed"] = $video->getVideoDriver()->getEmbed();
|
||||||
|
} else {
|
||||||
|
$res["url"] = $video->getURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolve($res);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shareVideo(int $owner, int $vid, int $type, string $message, int $club, bool $signed, bool $asGroup, callable $resolve, callable $reject)
|
||||||
|
{
|
||||||
|
$video = $this->videos->getByOwnerAndVID($owner, $vid);
|
||||||
|
|
||||||
|
if(!$video || $video->isDeleted()) {
|
||||||
|
$reject(16, "Video does not exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(method_exists($video, "canBeViewedBy") && !$video->canBeViewedBy($this->user)) {
|
||||||
|
$reject(32, "Access to video denied");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$video->getOwner()->getPrivacyPermission('videos.read', $this->user)) {
|
||||||
|
$reject(8, "Access to video denied: this user chose to hide his videos");
|
||||||
|
}
|
||||||
|
|
||||||
|
$flags = 0;
|
||||||
|
|
||||||
|
$nPost = new Post;
|
||||||
|
$nPost->setOwner($this->user->getId());
|
||||||
|
|
||||||
|
if($type == 0) {
|
||||||
|
$nPost->setWall($this->user->getId());
|
||||||
|
} else {
|
||||||
|
$club = $this->groups->get($club);
|
||||||
|
|
||||||
|
if(!$club || $club->isDeleted() || !$club->canBeModifiedBy($this->user)) {
|
||||||
|
$reject(64, "Can't do repost to this club");
|
||||||
|
}
|
||||||
|
|
||||||
|
if($asGroup)
|
||||||
|
$flags |= 0b10000000;
|
||||||
|
|
||||||
|
if($signed)
|
||||||
|
$flags |= 0b01000000;
|
||||||
|
|
||||||
|
$nPost->setWall($club->getId() * -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$nPost->setContent($message);
|
||||||
|
$nPost->setFlags($flags);
|
||||||
|
$nPost->save();
|
||||||
|
|
||||||
|
$nPost->attach($video);
|
||||||
|
|
||||||
|
$res = [
|
||||||
|
"id" => $nPost->getId(),
|
||||||
|
"pretty_id" => $nPost->getPrettyId(),
|
||||||
|
];
|
||||||
|
|
||||||
|
$resolve($res);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,8 +22,14 @@ class Wall implements Handler
|
||||||
{
|
{
|
||||||
$post = $this->posts->get($id);
|
$post = $this->posts->get($id);
|
||||||
if(!$post || $post->isDeleted())
|
if(!$post || $post->isDeleted())
|
||||||
$reject("No post with id=$id");
|
$reject(53, "No post with id=$id");
|
||||||
|
|
||||||
|
if($post->getSuggestionType() != 0)
|
||||||
|
$reject(25, "Can't get suggested post");
|
||||||
|
|
||||||
|
if(!$post->canBeViewedBy($this->user))
|
||||||
|
$reject(12, "Access denied");
|
||||||
|
|
||||||
$res = (object) [];
|
$res = (object) [];
|
||||||
$res->id = $post->getId();
|
$res->id = $post->getId();
|
||||||
$res->wall = $post->getTargetWall();
|
$res->wall = $post->getTargetWall();
|
||||||
|
@ -96,7 +102,7 @@ class Wall implements Handler
|
||||||
|
|
||||||
$resolve($arr);
|
$resolve($arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideos(int $page = 1, callable $resolve, callable $reject)
|
function getVideos(int $page = 1, callable $resolve, callable $reject)
|
||||||
{
|
{
|
||||||
$videos = $this->videos->getByUser($this->user, $page, 8);
|
$videos = $this->videos->getByUser($this->user, $page, 8);
|
||||||
|
@ -108,7 +114,7 @@ class Wall implements Handler
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach($videos as $video) {
|
foreach($videos as $video) {
|
||||||
$res = json_decode(json_encode($video->toVkApiStruct()), true);
|
$res = json_decode(json_encode($video->toVkApiStruct($this->user)), true);
|
||||||
$res["video"]["author_name"] = $video->getOwner()->getCanonicalName();
|
$res["video"]["author_name"] = $video->getOwner()->getCanonicalName();
|
||||||
|
|
||||||
$arr["items"][] = $res;
|
$arr["items"][] = $res;
|
||||||
|
@ -129,7 +135,7 @@ class Wall implements Handler
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach($videos as $video) {
|
foreach($videos as $video) {
|
||||||
$res = json_decode(json_encode($video->toVkApiStruct()), true);
|
$res = json_decode(json_encode($video->toVkApiStruct($this->user)), true);
|
||||||
$res["video"]["author_name"] = $video->getOwner()->getCanonicalName();
|
$res["video"]["author_name"] = $video->getOwner()->getCanonicalName();
|
||||||
|
|
||||||
$arr["items"][] = $res;
|
$arr["items"][] = $res;
|
||||||
|
|
|
@ -14,6 +14,7 @@ final class Account extends VKAPIRequestHandler
|
||||||
"last_name" => $this->getUser()->getLastName(),
|
"last_name" => $this->getUser()->getLastName(),
|
||||||
"home_town" => $this->getUser()->getHometown(),
|
"home_town" => $this->getUser()->getHometown(),
|
||||||
"status" => $this->getUser()->getStatus(),
|
"status" => $this->getUser()->getStatus(),
|
||||||
|
"audio_status" => is_null($this->getUser()->getCurrentAudioStatus()) ? NULL : $this->getUser()->getCurrentAudioStatus()->toVkApiStruct($this->getUser()),
|
||||||
"bdate" => is_null($this->getUser()->getBirthday()) ? '01.01.1970' : $this->getUser()->getBirthday()->format('%e.%m.%Y'),
|
"bdate" => is_null($this->getUser()->getBirthday()) ? '01.01.1970' : $this->getUser()->getBirthday()->format('%e.%m.%Y'),
|
||||||
"bdate_visibility" => $this->getUser()->getBirthdayPrivacy(),
|
"bdate_visibility" => $this->getUser()->getBirthdayPrivacy(),
|
||||||
"phone" => "+420 ** *** 228", # TODO
|
"phone" => "+420 ** *** 228", # TODO
|
||||||
|
|
|
@ -1,22 +1,788 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\VKAPI\Handlers;
|
namespace openvk\VKAPI\Handlers;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use openvk\Web\Models\Entities\Audio as AEntity;
|
||||||
|
use openvk\Web\Models\Entities\Playlist;
|
||||||
|
use openvk\Web\Models\Repositories\Audios;
|
||||||
|
use openvk\Web\Models\Repositories\Clubs;
|
||||||
|
use openvk\Web\Models\Repositories\Util\EntityStream;
|
||||||
|
|
||||||
final class Audio extends VKAPIRequestHandler
|
final class Audio extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
function get(): object
|
private function toSafeAudioStruct(?AEntity $audio, ?string $hash = NULL, bool $need_user = false): object
|
||||||
|
{
|
||||||
|
if(!$audio)
|
||||||
|
$this->fail(0404, "Audio not found");
|
||||||
|
else if(!$audio->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(201, "Access denied to audio(" . $audio->getPrettyId() . ")");
|
||||||
|
|
||||||
|
# рофлан ебало
|
||||||
|
$privApi = $hash && $GLOBALS["csrfCheck"];
|
||||||
|
$audioObj = $audio->toVkApiStruct($this->getUser());
|
||||||
|
if(!$privApi) {
|
||||||
|
$audioObj->manifest = false;
|
||||||
|
$audioObj->keys = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($need_user) {
|
||||||
|
$user = (new \openvk\Web\Models\Repositories\Users)->get($audio->getOwner()->getId());
|
||||||
|
$audioObj->user = (object) [
|
||||||
|
"id" => $user->getId(),
|
||||||
|
"photo" => $user->getAvatarUrl(),
|
||||||
|
"name" => $user->getCanonicalName(),
|
||||||
|
"name_gen" => $user->getCanonicalName(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $audioObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function streamToResponse(EntityStream $es, int $offset, int $count, ?string $hash = NULL): object
|
||||||
|
{
|
||||||
|
$items = [];
|
||||||
|
foreach($es->offsetLimit($offset, $count) as $audio) {
|
||||||
|
$items[] = $this->toSafeAudioStruct($audio, $hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"count" => sizeof($items),
|
||||||
|
"items" => $items,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validateGenre(?string& $genre_str, ?int $genre_id): void
|
||||||
|
{
|
||||||
|
if(!is_null($genre_str)) {
|
||||||
|
if(!in_array($genre_str, AEntity::genres))
|
||||||
|
$this->fail(8, "Invalid genre_str");
|
||||||
|
} else if(!is_null($genre_id)) {
|
||||||
|
$genre_str = array_flip(AEntity::vkGenres)[$genre_id] ?? NULL;
|
||||||
|
if(!$genre_str)
|
||||||
|
$this->fail(8, "Invalid genre ID $genre_id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function audioFromAnyId(string $id): ?AEntity
|
||||||
|
{
|
||||||
|
$descriptor = explode("_", $id);
|
||||||
|
if(sizeof($descriptor) === 1) {
|
||||||
|
if(ctype_digit($descriptor[0])) {
|
||||||
|
$audio = (new Audios)->get((int) $descriptor[0]);
|
||||||
|
} else {
|
||||||
|
$aid = base64_decode($descriptor[0], true);
|
||||||
|
if(!$aid)
|
||||||
|
$this->fail(8, "Invalid audio $id");
|
||||||
|
|
||||||
|
$audio = (new Audios)->get((int) $aid);
|
||||||
|
}
|
||||||
|
} else if(sizeof($descriptor) === 2) {
|
||||||
|
$audio = (new Audios)->getByOwnerAndVID((int) $descriptor[0], (int) $descriptor[1]);
|
||||||
|
} else {
|
||||||
|
$this->fail(8, "Invalid audio $id");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getById(string $audios, ?string $hash = NULL, int $need_user = 0): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$audioIds = array_unique(explode(",", $audios));
|
||||||
|
if(sizeof($audioIds) === 1) {
|
||||||
|
$audio = $this->audioFromAnyId($audioIds[0]);
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"count" => 1,
|
||||||
|
"items" => [
|
||||||
|
$this->toSafeAudioStruct($audio, $hash, (bool) $need_user),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
} else if(sizeof($audioIds) > 6000) {
|
||||||
|
$this->fail(1980, "Can't get more than 6000 audios at once");
|
||||||
|
}
|
||||||
|
|
||||||
|
$audios = [];
|
||||||
|
foreach($audioIds as $id)
|
||||||
|
$audios[] = $this->getById($id, $hash)->items[0];
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"count" => sizeof($audios),
|
||||||
|
"items" => $audios,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLagtrain(string $audio_id): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$audio = $this->audioFromAnyId($audio_id);
|
||||||
|
if(!$audio)
|
||||||
|
$this->fail(0404, "Audio not found");
|
||||||
|
|
||||||
|
# Possible information disclosure risks are acceptable :D
|
||||||
|
return (int) (strpos($audio->getName(), "Lagtrain") !== false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO stub
|
||||||
|
function getRecommendations(): object
|
||||||
|
{
|
||||||
|
return (object) [
|
||||||
|
"count" => 0,
|
||||||
|
"items" => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPopular(?int $genre_id = NULL, ?string $genre_str = NULL, int $offset = 0, int $count = 100, ?string $hash = NULL): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->validateGenre($genre_str, $genre_id);
|
||||||
|
|
||||||
|
$results = (new Audios)->getGlobal(Audios::ORDER_POPULAR, $genre_str);
|
||||||
|
|
||||||
|
return $this->streamToResponse($results, $offset, $count, $hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFeed(?int $genre_id = NULL, ?string $genre_str = NULL, int $offset = 0, int $count = 100, ?string $hash = NULL): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->validateGenre($genre_str, $genre_id);
|
||||||
|
|
||||||
|
$results = (new Audios)->getGlobal(Audios::ORDER_NEW, $genre_str);
|
||||||
|
|
||||||
|
return $this->streamToResponse($results, $offset, $count, $hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
function search(string $q, int $auto_complete = 0, int $lyrics = 0, int $performer_only = 0, int $sort = 2, int $search_own = 0, int $offset = 0, int $count = 30, ?string $hash = NULL): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
if(($auto_complete + $search_own) != 0)
|
||||||
|
$this->fail(10, "auto_complete and search_own are not supported");
|
||||||
|
else if($count > 300 || $count < 1)
|
||||||
|
$this->fail(8, "count is invalid: $count");
|
||||||
|
|
||||||
|
$results = (new Audios)->search($q, $sort, (bool) $performer_only, (bool) $lyrics);
|
||||||
|
|
||||||
|
return $this->streamToResponse($results, $offset, $count, $hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCount(int $owner_id, int $uploaded_only = 0): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
if($owner_id < 0) {
|
||||||
|
$owner_id *= -1;
|
||||||
|
$group = (new Clubs)->get($owner_id);
|
||||||
|
if(!$group)
|
||||||
|
$this->fail(0404, "Group not found");
|
||||||
|
|
||||||
|
return (new Audios)->getClubCollectionSize($group);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = (new \openvk\Web\Models\Repositories\Users)->get($owner_id);
|
||||||
|
if(!$user)
|
||||||
|
$this->fail(0404, "User not found");
|
||||||
|
|
||||||
|
if(!$user->getPrivacyPermission("audios.read", $this->getUser()))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
|
if($uploaded_only) {
|
||||||
|
return DatabaseConnection::i()->getContext()->table("audios")
|
||||||
|
->where([
|
||||||
|
"deleted" => false,
|
||||||
|
"owner" => $owner_id,
|
||||||
|
])->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (new Audios)->getUserCollectionSize($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(int $owner_id = 0, int $album_id = 0, string $audio_ids = '', int $need_user = 1, int $offset = 0, int $count = 100, int $uploaded_only = 0, int $need_seed = 0, ?string $shuffle_seed = NULL, int $shuffle = 0, ?string $hash = NULL): object
|
||||||
{
|
{
|
||||||
$serverUrl = ovk_scheme(true) . $_SERVER["SERVER_NAME"];
|
$this->requireUser();
|
||||||
|
|
||||||
return (object) [
|
$shuffleSeed = NULL;
|
||||||
"count" => 1,
|
$shuffleSeedStr = NULL;
|
||||||
"items" => [(object) [
|
if($shuffle == 1) {
|
||||||
"id" => 1,
|
if(!$shuffle_seed) {
|
||||||
"owner_id" => 1,
|
if($need_seed == 1) {
|
||||||
"artist" => "В ОВК ПОКА НЕТ МУЗЫКИ",
|
$shuffleSeed = openssl_random_pseudo_bytes(6);
|
||||||
"title" => "ЖДИТЕ :)))",
|
$shuffleSeedStr = base64_encode($shuffleSeed);
|
||||||
"duration" => 22,
|
$shuffleSeed = hexdec(bin2hex($shuffleSeed));
|
||||||
"url" => $serverUrl . "/assets/packages/static/openvk/audio/nomusic.mp3"
|
} else {
|
||||||
]]
|
$hOffset = ((int) date("i") * 60) + (int) date("s");
|
||||||
];
|
$thisHour = time() - $hOffset;
|
||||||
|
$shuffleSeed = $thisHour + $this->getUser()->getId();
|
||||||
|
$shuffleSeedStr = base64_encode(hex2bin(dechex($shuffleSeed)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$shuffleSeed = hexdec(bin2hex(base64_decode($shuffle_seed)));
|
||||||
|
$shuffleSeedStr = $shuffle_seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($album_id != 0) {
|
||||||
|
$album = (new Audios)->getPlaylist($album_id);
|
||||||
|
if(!$album)
|
||||||
|
$this->fail(0404, "album_id invalid");
|
||||||
|
else if(!$album->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(600, "Can't open this album for reading");
|
||||||
|
|
||||||
|
$songs = [];
|
||||||
|
$list = $album->getAudios($offset, $count, $shuffleSeed);
|
||||||
|
|
||||||
|
foreach($list as $song)
|
||||||
|
$songs[] = $this->toSafeAudioStruct($song, $hash, $need_user == 1);
|
||||||
|
|
||||||
|
$response = (object) [
|
||||||
|
"count" => sizeof($songs),
|
||||||
|
"items" => $songs,
|
||||||
|
];
|
||||||
|
if(!is_null($shuffleSeed))
|
||||||
|
$response->shuffle_seed = $shuffleSeedStr;
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($audio_ids)) {
|
||||||
|
$audio_ids = explode(",", $audio_ids);
|
||||||
|
if(!$audio_ids)
|
||||||
|
$this->fail(10, "Audio::get@L0d186:explode(string): Unknown error");
|
||||||
|
else if(sizeof($audio_ids) < 1)
|
||||||
|
$this->fail(8, "Invalid audio_ids syntax");
|
||||||
|
|
||||||
|
if(!is_null($shuffleSeed))
|
||||||
|
$audio_ids = knuth_shuffle($audio_ids, $shuffleSeed);
|
||||||
|
|
||||||
|
$obj = $this->getById(implode(",", $audio_ids), $hash, $need_user);
|
||||||
|
if(!is_null($shuffleSeed))
|
||||||
|
$obj->shuffle_seed = $shuffleSeedStr;
|
||||||
|
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbCtx = DatabaseConnection::i()->getContext();
|
||||||
|
if($uploaded_only == 1) {
|
||||||
|
if($owner_id <= 0)
|
||||||
|
$this->fail(8, "uploaded_only can only be used with owner_id > 0");
|
||||||
|
|
||||||
|
$user = (new \openvk\Web\Models\Repositories\Users)->get($owner_id);
|
||||||
|
|
||||||
|
if(!$user)
|
||||||
|
$this->fail(0602, "Invalid user");
|
||||||
|
|
||||||
|
if(!$user->getPrivacyPermission("audios.read", $this->getUser()))
|
||||||
|
$this->fail(15, "Access denied: this user chose to hide his audios");
|
||||||
|
|
||||||
|
if(!is_null($shuffleSeed)) {
|
||||||
|
$audio_ids = [];
|
||||||
|
$query = $dbCtx->table("audios")->select("virtual_id")->where([
|
||||||
|
"owner" => $owner_id,
|
||||||
|
"deleted" => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach($query as $res)
|
||||||
|
$audio_ids[] = $res->virtual_id;
|
||||||
|
|
||||||
|
$audio_ids = knuth_shuffle($audio_ids, $shuffleSeed);
|
||||||
|
$audio_ids = array_slice($audio_ids, $offset, $count);
|
||||||
|
$audio_q = ""; # audio.getById query
|
||||||
|
foreach($audio_ids as $aid)
|
||||||
|
$audio_q .= ",$owner_id" . "_$aid";
|
||||||
|
|
||||||
|
$obj = $this->getById(substr($audio_q, 1), $hash, $need_user);
|
||||||
|
$obj->shuffle_seed = $shuffleSeedStr;
|
||||||
|
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = (new Audios)->getByUploader((new \openvk\Web\Models\Repositories\Users)->get($owner_id));
|
||||||
|
|
||||||
|
return $this->streamToResponse($res, $offset, $count, $hash, $need_user);
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = $dbCtx->table("audio_relations")->select("audio")->where("entity", $owner_id);
|
||||||
|
if(!is_null($shuffleSeed)) {
|
||||||
|
$audio_ids = [];
|
||||||
|
foreach($query as $aid)
|
||||||
|
$audio_ids[] = $aid->audio;
|
||||||
|
|
||||||
|
$audio_ids = knuth_shuffle($audio_ids, $shuffleSeed);
|
||||||
|
$audio_ids = array_slice($audio_ids, $offset, $count);
|
||||||
|
$audio_q = "";
|
||||||
|
foreach($audio_ids as $aid)
|
||||||
|
$audio_q .= ",$aid";
|
||||||
|
|
||||||
|
$obj = $this->getById(substr($audio_q, 1), $hash, $need_user);
|
||||||
|
$obj->shuffle_seed = $shuffleSeedStr;
|
||||||
|
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
if($owner_id > 0) {
|
||||||
|
$user = (new \openvk\Web\Models\Repositories\Users)->get($owner_id);
|
||||||
|
|
||||||
|
if(!$user)
|
||||||
|
$this->fail(50, "Invalid user");
|
||||||
|
|
||||||
|
if(!$user->getPrivacyPermission("audios.read", $this->getUser()))
|
||||||
|
$this->fail(15, "Access denied: this user chose to hide his audios");
|
||||||
|
}
|
||||||
|
|
||||||
|
$audios = (new Audios)->getByEntityID($owner_id, $offset, $count);
|
||||||
|
foreach($audios as $audio)
|
||||||
|
$items[] = $this->toSafeAudioStruct($audio, $hash, $need_user == 1);
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"count" => sizeof($items),
|
||||||
|
"items" => $items,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLyrics(int $lyrics_id): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$audio = (new Audios)->get($lyrics_id);
|
||||||
|
if(!$audio || !$audio->getLyrics())
|
||||||
|
$this->fail(0404, "Not found");
|
||||||
|
|
||||||
|
if(!$audio->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(201, "Access denied to lyrics");
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"lyrics_id" => $lyrics_id,
|
||||||
|
"text" => preg_replace("%\r\n?%", "\n", $audio->getLyrics()),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function beacon(int $aid, ?int $gid = NULL): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$audio = (new Audios)->get($aid);
|
||||||
|
if(!$audio)
|
||||||
|
$this->fail(0404, "Not Found");
|
||||||
|
else if(!$audio->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(201, "Insufficient permissions to listen this audio");
|
||||||
|
|
||||||
|
$group = NULL;
|
||||||
|
if(!is_null($gid)) {
|
||||||
|
$group = (new Clubs)->get($gid);
|
||||||
|
if(!$group)
|
||||||
|
$this->fail(0404, "Not Found");
|
||||||
|
else if(!$group->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(203, "Insufficient rights to this group");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) $audio->listen($group ?? $this->getUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBroadcast(string $audio, string $target_ids): array
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
[$owner, $aid] = explode("_", $audio);
|
||||||
|
$song = (new Audios)->getByOwnerAndVID((int) $owner, (int) $aid);
|
||||||
|
$ids = [];
|
||||||
|
foreach(explode(",", $target_ids) as $id) {
|
||||||
|
$id = (int) $id;
|
||||||
|
if($id > 0) {
|
||||||
|
if ($id != $this->getUser()->getId()) {
|
||||||
|
$this->fail(600, "Can't listen on behalf of $id");
|
||||||
|
} else {
|
||||||
|
$ids[] = $id;
|
||||||
|
$this->beacon($song->getId());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$group = (new Clubs)->get($id * -1);
|
||||||
|
if(!$group)
|
||||||
|
$this->fail(0404, "Not Found");
|
||||||
|
else if(!$group->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(203,"Insufficient rights to this group");
|
||||||
|
|
||||||
|
$ids[] = $id;
|
||||||
|
$this->beacon($song ? $song->getId() : 0, $id * -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBroadcastList(string $filter = "all", int $active = 0, ?string $hash = NULL): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
if(!in_array($filter, ["all", "friends", "groups"]))
|
||||||
|
$this->fail(8, "Invalid filter $filter");
|
||||||
|
|
||||||
|
$broadcastList = $this->getUser()->getBroadcastList($filter);
|
||||||
|
$items = [];
|
||||||
|
foreach($broadcastList as $res) {
|
||||||
|
$struct = $res->toVkApiStruct();
|
||||||
|
$status = $res->getCurrentAudioStatus();
|
||||||
|
|
||||||
|
$struct->status_audio = $status ? $this->toSafeAudioStruct($status) : NULL;
|
||||||
|
$items[] = $struct;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"count" => sizeof($items),
|
||||||
|
"items" => $items,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit(int $owner_id, int $audio_id, ?string $artist = NULL, ?string $title = NULL, ?string $text = NULL, ?int $genre_id = NULL, ?string $genre_str = NULL, int $no_search = 0): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$audio = (new Audios)->getByOwnerAndVID($owner_id, $audio_id);
|
||||||
|
if(!$audio)
|
||||||
|
$this->fail(0404, "Not Found");
|
||||||
|
else if(!$audio->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(201, "Insufficient permissions to edit this audio");
|
||||||
|
|
||||||
|
if(!is_null($genre_id)) {
|
||||||
|
$genre = array_flip(AEntity::vkGenres)[$genre_id] ?? NULL;
|
||||||
|
if(!$genre)
|
||||||
|
$this->fail(8, "Invalid genre ID $genre_id");
|
||||||
|
|
||||||
|
$audio->setGenre($genre);
|
||||||
|
} else if(!is_null($genre_str)) {
|
||||||
|
if(!in_array($genre_str, AEntity::genres))
|
||||||
|
$this->fail(8, "Invalid genre ID $genre_str");
|
||||||
|
|
||||||
|
$audio->setGenre($genre_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
$lyrics = 0;
|
||||||
|
if(!is_null($text)) {
|
||||||
|
$audio->setLyrics($text);
|
||||||
|
$lyrics = $audio->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!is_null($artist))
|
||||||
|
$audio->setPerformer($artist);
|
||||||
|
|
||||||
|
if(!is_null($title))
|
||||||
|
$audio->setName($title);
|
||||||
|
|
||||||
|
$audio->setSearchability(!((bool) $no_search));
|
||||||
|
$audio->setEdited(time());
|
||||||
|
$audio->save();
|
||||||
|
|
||||||
|
return $lyrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
function add(int $audio_id, int $owner_id, ?int $group_id = NULL, ?int $album_id = NULL): string
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
if(!is_null($album_id))
|
||||||
|
$this->fail(10, "album_id not implemented");
|
||||||
|
|
||||||
|
// TODO get rid of dups
|
||||||
|
$to = $this->getUser();
|
||||||
|
if(!is_null($group_id)) {
|
||||||
|
$group = (new Clubs)->get($group_id);
|
||||||
|
if(!$group)
|
||||||
|
$this->fail(0404, "Invalid group_id");
|
||||||
|
else if(!$group->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(203, "Insufficient rights to this group");
|
||||||
|
|
||||||
|
$to = $group;
|
||||||
|
}
|
||||||
|
|
||||||
|
$audio = (new Audios)->getByOwnerAndVID($owner_id, $audio_id);
|
||||||
|
if(!$audio)
|
||||||
|
$this->fail(0404, "Not found");
|
||||||
|
else if(!$audio->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(201, "Access denied to audio(owner=$owner_id, vid=$audio_id)");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$audio->add($to);
|
||||||
|
} catch(\OverflowException $ex) {
|
||||||
|
$this->fail(300, "Album is full");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $audio->getPrettyId();
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete(int $audio_id, int $owner_id, ?int $group_id = NULL): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$from = $this->getUser();
|
||||||
|
if(!is_null($group_id)) {
|
||||||
|
$group = (new Clubs)->get($group_id);
|
||||||
|
if(!$group)
|
||||||
|
$this->fail(0404, "Invalid group_id");
|
||||||
|
else if(!$group->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(203, "Insufficient rights to this group");
|
||||||
|
|
||||||
|
$from = $group;
|
||||||
|
}
|
||||||
|
|
||||||
|
$audio = (new Audios)->getByOwnerAndVID($owner_id, $audio_id);
|
||||||
|
if(!$audio)
|
||||||
|
$this->fail(0404, "Not found");
|
||||||
|
|
||||||
|
$audio->remove($from);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function restore(int $audio_id, int $owner_id, ?int $group_id = NULL, ?string $hash = NULL): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$vid = $this->add($audio_id, $owner_id, $group_id);
|
||||||
|
|
||||||
|
return $this->getById($vid, $hash)->items[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAlbums(int $owner_id = 0, int $offset = 0, int $count = 50, int $drop_private = 1): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$owner_id = $owner_id == 0 ? $this->getUser()->getId() : $owner_id;
|
||||||
|
$playlists = [];
|
||||||
|
|
||||||
|
if($owner_id > 0 && $owner_id != $this->getUser()->getId()) {
|
||||||
|
$user = (new \openvk\Web\Models\Repositories\Users)->get($owner_id);
|
||||||
|
|
||||||
|
if(!$user->getPrivacyPermission("audios.read", $this->getUser()))
|
||||||
|
$this->fail(50, "Access to playlists denied");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach((new Audios)->getPlaylistsByEntityId($owner_id, $offset, $count) as $playlist) {
|
||||||
|
if(!$playlist->canBeViewedBy($this->getUser())) {
|
||||||
|
if($drop_private == 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$playlists[] = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$playlists[] = $playlist->toVkApiStruct($this->getUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"count" => sizeof($playlists),
|
||||||
|
"items" => $playlists,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchAlbums(string $query, int $offset = 0, int $limit = 25, int $drop_private = 0): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$playlists = [];
|
||||||
|
$search = (new Audios)->searchPlaylists($query)->offsetLimit($offset, $limit);
|
||||||
|
foreach($search as $playlist) {
|
||||||
|
if(!$playlist->canBeViewedBy($this->getUser())) {
|
||||||
|
if($drop_private == 0)
|
||||||
|
$playlists[] = NULL;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$playlists[] = $playlist->toVkApiStruct($this->getUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"count" => sizeof($playlists),
|
||||||
|
"items" => $playlists,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAlbum(string $title, ?string $description = NULL, int $group_id = 0): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$group = NULL;
|
||||||
|
if($group_id != 0) {
|
||||||
|
$group = (new Clubs)->get($group_id);
|
||||||
|
if(!$group)
|
||||||
|
$this->fail(0404, "Invalid group_id");
|
||||||
|
else if(!$group->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(600, "Insufficient rights to this group");
|
||||||
|
}
|
||||||
|
|
||||||
|
$album = new Playlist;
|
||||||
|
$album->setName($title);
|
||||||
|
if(!is_null($group))
|
||||||
|
$album->setOwner($group_id * -1);
|
||||||
|
else
|
||||||
|
$album->setOwner($this->getUser()->getId());
|
||||||
|
|
||||||
|
if(!is_null($description))
|
||||||
|
$album->setDescription($description);
|
||||||
|
|
||||||
|
$album->save();
|
||||||
|
if(!is_null($group))
|
||||||
|
$album->bookmark($group);
|
||||||
|
else
|
||||||
|
$album->bookmark($this->getUser());
|
||||||
|
|
||||||
|
return $album->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
function editAlbum(int $album_id, ?string $title = NULL, ?string $description = NULL): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$album = (new Audios)->getPlaylist($album_id);
|
||||||
|
if(!$album)
|
||||||
|
$this->fail(0404, "Album not found");
|
||||||
|
else if(!$album->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(600, "Insufficient rights to this album");
|
||||||
|
|
||||||
|
if(!is_null($title))
|
||||||
|
$album->setName($title);
|
||||||
|
|
||||||
|
if(!is_null($description))
|
||||||
|
$album->setDescription($description);
|
||||||
|
|
||||||
|
$album->setEdited(time());
|
||||||
|
$album->save();
|
||||||
|
|
||||||
|
return (int) !(!$title && !$description);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteAlbum(int $album_id): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$album = (new Audios)->getPlaylist($album_id);
|
||||||
|
if(!$album)
|
||||||
|
$this->fail(0404, "Album not found");
|
||||||
|
else if(!$album->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(600, "Insufficient rights to this album");
|
||||||
|
|
||||||
|
$album->delete();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveToAlbum(int $album_id, string $audio_ids): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$album = (new Audios)->getPlaylist($album_id);
|
||||||
|
if(!$album)
|
||||||
|
$this->fail(0404, "Album not found");
|
||||||
|
else if(!$album->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(600, "Insufficient rights to this album");
|
||||||
|
|
||||||
|
$audios = [];
|
||||||
|
$audio_ids = array_unique(explode(",", $audio_ids));
|
||||||
|
if(sizeof($audio_ids) < 1 || sizeof($audio_ids) > 1000)
|
||||||
|
$this->fail(8, "audio_ids must contain at least 1 audio and at most 1000");
|
||||||
|
|
||||||
|
foreach($audio_ids as $audio_id) {
|
||||||
|
$audio = $this->audioFromAnyId($audio_id);
|
||||||
|
if(!$audio)
|
||||||
|
continue;
|
||||||
|
else if(!$audio->canBeViewedBy($this->getUser()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$audios[] = $audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sizeof($audios) < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
$res = 1;
|
||||||
|
try {
|
||||||
|
foreach ($audios as $audio)
|
||||||
|
$res = min($res, (int) $album->add($audio));
|
||||||
|
} catch(\OutOfBoundsException $ex) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeFromAlbum(int $album_id, string $audio_ids): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$album = (new Audios)->getPlaylist($album_id);
|
||||||
|
if(!$album)
|
||||||
|
$this->fail(0404, "Album not found");
|
||||||
|
else if(!$album->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(600, "Insufficient rights to this album");
|
||||||
|
|
||||||
|
$audios = [];
|
||||||
|
$audio_ids = array_unique(explode(",", $audio_ids));
|
||||||
|
if(sizeof($audio_ids) < 1 || sizeof($audio_ids) > 1000)
|
||||||
|
$this->fail(8, "audio_ids must contain at least 1 audio and at most 1000");
|
||||||
|
|
||||||
|
foreach($audio_ids as $audio_id) {
|
||||||
|
$audio = $this->audioFromAnyId($audio_id);
|
||||||
|
if(!$audio)
|
||||||
|
continue;
|
||||||
|
else if($audio->canBeViewedBy($this->getUser()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$audios[] = $audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sizeof($audios) < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
foreach($audios as $audio)
|
||||||
|
$album->remove($audio);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyToAlbum(int $album_id, string $audio_ids): int
|
||||||
|
{
|
||||||
|
return $this->moveToAlbum($album_id, $audio_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bookmarkAlbum(int $id): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$album = (new Audios)->getPlaylist($id);
|
||||||
|
if(!$album)
|
||||||
|
$this->fail(0404, "Not found");
|
||||||
|
|
||||||
|
if(!$album->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(600, "Access error");
|
||||||
|
|
||||||
|
return (int) $album->bookmark($this->getUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
function unBookmarkAlbum(int $id): int
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$album = (new Audios)->getPlaylist($id);
|
||||||
|
if(!$album)
|
||||||
|
$this->fail(0404, "Not found");
|
||||||
|
|
||||||
|
if(!$album->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(600, "Access error");
|
||||||
|
|
||||||
|
return (int) $album->unbookmark($this->getUser());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||||
|
|
||||||
final class Friends extends VKAPIRequestHandler
|
final class Friends extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
function get(int $user_id, string $fields = "", int $offset = 0, int $count = 100): object
|
function get(int $user_id = 0, string $fields = "", int $offset = 0, int $count = 100): object
|
||||||
{
|
{
|
||||||
$i = 0;
|
$i = 0;
|
||||||
$offset++;
|
$offset++;
|
||||||
|
@ -13,11 +13,23 @@ final class Friends extends VKAPIRequestHandler
|
||||||
$users = new UsersRepo;
|
$users = new UsersRepo;
|
||||||
|
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
foreach($users->get($user_id)->getFriends($offset, $count) as $friend) {
|
if ($user_id == 0) {
|
||||||
$friends[$i] = $friend->getId();
|
$user_id = $this->getUser()->getId();
|
||||||
$i++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$user = $users->get($user_id);
|
||||||
|
|
||||||
|
if(!$user || $user->isDeleted())
|
||||||
|
$this->fail(100, "Invalid user");
|
||||||
|
|
||||||
|
if(!$user->getPrivacyPermission("friends.read", $this->getUser()))
|
||||||
|
$this->fail(15, "Access denied: this user chose to hide his friends.");
|
||||||
|
|
||||||
|
foreach($user->getFriends($offset, $count) as $friend) {
|
||||||
|
$friends[$i] = $friend->getId();
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
$response = $friends;
|
$response = $friends;
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,17 @@ final class Gifts extends VKAPIRequestHandler
|
||||||
if(!$user || $user->isDeleted())
|
if(!$user || $user->isDeleted())
|
||||||
$this->fail(177, "Invalid user");
|
$this->fail(177, "Invalid user");
|
||||||
|
|
||||||
|
if(!$user->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
|
/*
|
||||||
|
if(!$user->getPrivacyPermission('gifts.read', $this->getUser()))
|
||||||
|
$this->fail(15, "Access denied: this user chose to hide his gifts");*/
|
||||||
|
|
||||||
|
|
||||||
|
if(!$user->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
$gift_item = [];
|
$gift_item = [];
|
||||||
|
|
||||||
$userGifts = array_slice(iterator_to_array($user->getGifts(1, $count, false)), $offset);
|
$userGifts = array_slice(iterator_to_array($user->getGifts(1, $count, false)), $offset);
|
||||||
|
@ -62,6 +73,9 @@ final class Gifts extends VKAPIRequestHandler
|
||||||
if(!$user || $user->isDeleted())
|
if(!$user || $user->isDeleted())
|
||||||
$this->fail(177, "Invalid user");
|
$this->fail(177, "Invalid user");
|
||||||
|
|
||||||
|
if(!$user->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
$gift = (new GiftsRepo)->get($gift_id);
|
$gift = (new GiftsRepo)->get($gift_id);
|
||||||
|
|
||||||
if(!$gift)
|
if(!$gift)
|
||||||
|
|
|
@ -2,26 +2,30 @@
|
||||||
namespace openvk\VKAPI\Handlers;
|
namespace openvk\VKAPI\Handlers;
|
||||||
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
||||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||||
|
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
||||||
use openvk\Web\Models\Entities\Club;
|
use openvk\Web\Models\Entities\Club;
|
||||||
|
|
||||||
final class Groups extends VKAPIRequestHandler
|
final class Groups extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
function get(int $user_id = 0, string $fields = "", int $offset = 0, int $count = 6, bool $online = false): object
|
function get(int $user_id = 0, string $fields = "", int $offset = 0, int $count = 6, bool $online = false, string $filter = "groups"): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
if($user_id == 0) {
|
if($user_id == 0) {
|
||||||
foreach($this->getUser()->getClubs($offset, false, $count, true) as $club)
|
foreach($this->getUser()->getClubs($offset, $filter == "admin", $count, true) as $club)
|
||||||
$clbs[] = $club;
|
$clbs[] = $club;
|
||||||
$clbsCount = $this->getUser()->getClubCount();
|
$clbsCount = $this->getUser()->getClubCount();
|
||||||
} else {
|
} else {
|
||||||
$users = new UsersRepo;
|
$users = new UsersRepo;
|
||||||
$user = $users->get($user_id);
|
$user = $users->get($user_id);
|
||||||
|
|
||||||
if(is_null($user))
|
if(is_null($user) || $user->isDeleted())
|
||||||
$this->fail(15, "Access denied");
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
foreach($user->getClubs($offset, false, $count, true) as $club)
|
if(!$user->getPrivacyPermission('groups.read', $this->getUser()))
|
||||||
|
$this->fail(15, "Access denied: this user chose to hide his groups.");
|
||||||
|
|
||||||
|
foreach($user->getClubs($offset, $filter == "admin", $count, true) as $club)
|
||||||
$clbs[] = $club;
|
$clbs[] = $club;
|
||||||
|
|
||||||
$clbsCount = $user->getClubCount();
|
$clbsCount = $user->getClubCount();
|
||||||
|
@ -80,6 +84,19 @@ final class Groups extends VKAPIRequestHandler
|
||||||
break;
|
break;
|
||||||
case "members_count":
|
case "members_count":
|
||||||
$rClubs[$i]->members_count = $usr->getFollowersCount();
|
$rClubs[$i]->members_count = $usr->getFollowersCount();
|
||||||
|
break;
|
||||||
|
case "can_suggest":
|
||||||
|
$rClubs[$i]->can_suggest = !$usr->canBeModifiedBy($this->getUser()) && $usr->getWallType() == 2;
|
||||||
|
break;
|
||||||
|
# unstandard feild
|
||||||
|
case "suggested_count":
|
||||||
|
if($usr->getWallType() != 2) {
|
||||||
|
$rClubs[$i]->suggested_count = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rClubs[$i]->suggested_count = $usr->getSuggestedPostsCount($this->getUser());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,7 +205,19 @@ final class Groups extends VKAPIRequestHandler
|
||||||
case "description":
|
case "description":
|
||||||
$response[$i]->description = $clb->getDescription();
|
$response[$i]->description = $clb->getDescription();
|
||||||
break;
|
break;
|
||||||
case "contacts":
|
case "can_suggest":
|
||||||
|
$response[$i]->can_suggest = !$clb->canBeModifiedBy($this->getUser()) && $clb->getWallType() == 2;
|
||||||
|
break;
|
||||||
|
# unstandard feild
|
||||||
|
case "suggested_count":
|
||||||
|
if($clb->getWallType() != 2) {
|
||||||
|
$response[$i]->suggested_count = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response[$i]->suggested_count = $clb->getSuggestedPostsCount($this->getUser());
|
||||||
|
break;
|
||||||
|
case "contacts":
|
||||||
$contacts;
|
$contacts;
|
||||||
$contactTmp = $clb->getManagers(1, true);
|
$contactTmp = $clb->getManagers(1, true);
|
||||||
|
|
||||||
|
@ -288,11 +317,12 @@ final class Groups extends VKAPIRequestHandler
|
||||||
string $description = NULL,
|
string $description = NULL,
|
||||||
string $screen_name = NULL,
|
string $screen_name = NULL,
|
||||||
string $website = NULL,
|
string $website = NULL,
|
||||||
int $wall = NULL,
|
int $wall = -1,
|
||||||
int $topics = NULL,
|
int $topics = NULL,
|
||||||
int $adminlist = NULL,
|
int $adminlist = NULL,
|
||||||
int $topicsAboveWall = NULL,
|
int $topicsAboveWall = NULL,
|
||||||
int $hideFromGlobalFeed = NULL)
|
int $hideFromGlobalFeed = NULL,
|
||||||
|
int $audio = NULL)
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
@ -303,17 +333,31 @@ final class Groups extends VKAPIRequestHandler
|
||||||
if(!$club || !$club->canBeModifiedBy($this->getUser())) $this->fail(15, "You can't modify this group.");
|
if(!$club || !$club->canBeModifiedBy($this->getUser())) $this->fail(15, "You can't modify this group.");
|
||||||
if(!empty($screen_name) && !$club->setShortcode($screen_name)) $this->fail(103, "Invalid shortcode.");
|
if(!empty($screen_name) && !$club->setShortcode($screen_name)) $this->fail(103, "Invalid shortcode.");
|
||||||
|
|
||||||
!is_null($title) ? $club->setName($title) : NULL;
|
!empty($title) ? $club->setName($title) : NULL;
|
||||||
!is_null($description) ? $club->setAbout($description) : NULL;
|
!empty($description) ? $club->setAbout($description) : NULL;
|
||||||
!is_null($screen_name) ? $club->setShortcode($screen_name) : NULL;
|
!empty($screen_name) ? $club->setShortcode($screen_name) : NULL;
|
||||||
!is_null($website) ? $club->setWebsite((!parse_url($website, PHP_URL_SCHEME) ? "https://" : "") . $website) : NULL;
|
!empty($website) ? $club->setWebsite((!parse_url($website, PHP_URL_SCHEME) ? "https://" : "") . $website) : NULL;
|
||||||
!is_null($wall) ? $club->setWall($wall) : NULL;
|
|
||||||
!is_null($topics) ? $club->setEveryone_Can_Create_Topics($topics) : NULL;
|
try {
|
||||||
!is_null($adminlist) ? $club->setAdministrators_List_Display($adminlist) : NULL;
|
$wall != -1 ? $club->setWall($wall) : NULL;
|
||||||
!is_null($topicsAboveWall) ? $club->setDisplay_Topics_Above_Wall($topicsAboveWall) : NULL;
|
} catch(\Exception $e) {
|
||||||
!is_null($hideFromGlobalFeed) ? $club->setHide_From_Global_Feed($hideFromGlobalFeed) : NULL;
|
$this->fail(50, "Invalid wall value");
|
||||||
|
}
|
||||||
|
|
||||||
|
!empty($topics) ? $club->setEveryone_Can_Create_Topics($topics) : NULL;
|
||||||
|
!empty($adminlist) ? $club->setAdministrators_List_Display($adminlist) : NULL;
|
||||||
|
!empty($topicsAboveWall) ? $club->setDisplay_Topics_Above_Wall($topicsAboveWall) : NULL;
|
||||||
|
!empty($hideFromGlobalFeed) ? $club->setHide_From_Global_Feed($hideFromGlobalFeed) : NULL;
|
||||||
|
|
||||||
|
in_array($audio, [0, 1]) ? $club->setEveryone_can_upload_audios($audio) : NULL;
|
||||||
|
|
||||||
$club->save();
|
try {
|
||||||
|
$club->save();
|
||||||
|
} catch(\TypeError $e) {
|
||||||
|
$this->fail(15, "Nothing changed");
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
$this->fail(18, "An unknown error occurred: maybe you set an incorrect value?");
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -359,9 +403,15 @@ final class Groups extends VKAPIRequestHandler
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach($filds as $fild) {
|
foreach($filds as $fild) {
|
||||||
|
$canView = $member->canBeViewedBy($this->getUser());
|
||||||
switch($fild) {
|
switch($fild) {
|
||||||
case "bdate":
|
case "bdate":
|
||||||
$arr->items[$i]->bdate = $member->getBirthday()->format('%e.%m.%Y');
|
if(!$canView) {
|
||||||
|
$arr->items[$i]->bdate = "01.01.1970";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$arr->items[$i]->bdate = $member->getBirthday() ? $member->getBirthday()->format('%e.%m.%Y') : NULL;
|
||||||
break;
|
break;
|
||||||
case "can_post":
|
case "can_post":
|
||||||
$arr->items[$i]->can_post = $club->canBeModifiedBy($member);
|
$arr->items[$i]->can_post = $club->canBeModifiedBy($member);
|
||||||
|
@ -370,7 +420,7 @@ final class Groups extends VKAPIRequestHandler
|
||||||
$arr->items[$i]->can_see_all_posts = 1;
|
$arr->items[$i]->can_see_all_posts = 1;
|
||||||
break;
|
break;
|
||||||
case "can_see_audio":
|
case "can_see_audio":
|
||||||
$arr->items[$i]->can_see_audio = 0;
|
$arr->items[$i]->can_see_audio = 1;
|
||||||
break;
|
break;
|
||||||
case "can_write_private_message":
|
case "can_write_private_message":
|
||||||
$arr->items[$i]->can_write_private_message = 0;
|
$arr->items[$i]->can_write_private_message = 0;
|
||||||
|
@ -382,6 +432,11 @@ final class Groups extends VKAPIRequestHandler
|
||||||
$arr->items[$i]->connections = 1;
|
$arr->items[$i]->connections = 1;
|
||||||
break;
|
break;
|
||||||
case "contacts":
|
case "contacts":
|
||||||
|
if(!$canView) {
|
||||||
|
$arr->items[$i]->contacts = "secret@gmail.com";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$arr->items[$i]->contacts = $member->getContactEmail();
|
$arr->items[$i]->contacts = $member->getContactEmail();
|
||||||
break;
|
break;
|
||||||
case "country":
|
case "country":
|
||||||
|
@ -397,15 +452,30 @@ final class Groups extends VKAPIRequestHandler
|
||||||
$arr->items[$i]->has_mobile = false;
|
$arr->items[$i]->has_mobile = false;
|
||||||
break;
|
break;
|
||||||
case "last_seen":
|
case "last_seen":
|
||||||
|
if(!$canView) {
|
||||||
|
$arr->items[$i]->last_seen = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$arr->items[$i]->last_seen = $member->getOnline()->timestamp();
|
$arr->items[$i]->last_seen = $member->getOnline()->timestamp();
|
||||||
break;
|
break;
|
||||||
case "lists":
|
case "lists":
|
||||||
$arr->items[$i]->lists = "";
|
$arr->items[$i]->lists = "";
|
||||||
break;
|
break;
|
||||||
case "online":
|
case "online":
|
||||||
|
if(!$canView) {
|
||||||
|
$arr->items[$i]->online = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$arr->items[$i]->online = $member->isOnline();
|
$arr->items[$i]->online = $member->isOnline();
|
||||||
break;
|
break;
|
||||||
case "online_mobile":
|
case "online_mobile":
|
||||||
|
if(!$canView) {
|
||||||
|
$arr->items[$i]->online_mobile = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$arr->items[$i]->online_mobile = $member->getOnlinePlatform() == "android" || $member->getOnlinePlatform() == "iphone" || $member->getOnlinePlatform() == "mobile";
|
$arr->items[$i]->online_mobile = $member->getOnlinePlatform() == "android" || $member->getOnlinePlatform() == "iphone" || $member->getOnlinePlatform() == "mobile";
|
||||||
break;
|
break;
|
||||||
case "photo_100":
|
case "photo_100":
|
||||||
|
@ -436,12 +506,27 @@ final class Groups extends VKAPIRequestHandler
|
||||||
$arr->items[$i]->schools = 0;
|
$arr->items[$i]->schools = 0;
|
||||||
break;
|
break;
|
||||||
case "sex":
|
case "sex":
|
||||||
|
if(!$canView) {
|
||||||
|
$arr->items[$i]->sex = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$arr->items[$i]->sex = $member->isFemale() ? 1 : 2;
|
$arr->items[$i]->sex = $member->isFemale() ? 1 : 2;
|
||||||
break;
|
break;
|
||||||
case "site":
|
case "site":
|
||||||
|
if(!$canView) {
|
||||||
|
$arr->items[$i]->site = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$arr->items[$i]->site = $member->getWebsite();
|
$arr->items[$i]->site = $member->getWebsite();
|
||||||
break;
|
break;
|
||||||
case "status":
|
case "status":
|
||||||
|
if(!$canView) {
|
||||||
|
$arr->items[$i]->status = "r";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$arr->items[$i]->status = $member->getStatus();
|
$arr->items[$i]->status = $member->getStatus();
|
||||||
break;
|
break;
|
||||||
case "universities":
|
case "universities":
|
||||||
|
@ -466,10 +551,10 @@ final class Groups extends VKAPIRequestHandler
|
||||||
"title" => $club->getName(),
|
"title" => $club->getName(),
|
||||||
"description" => $club->getDescription() != NULL ? $club->getDescription() : "",
|
"description" => $club->getDescription() != NULL ? $club->getDescription() : "",
|
||||||
"address" => $club->getShortcode(),
|
"address" => $club->getShortcode(),
|
||||||
"wall" => $club->canPost() == true ? 1 : 0,
|
"wall" => $club->getWallType(), # отличается от вкшных но да ладно
|
||||||
"photos" => 1,
|
"photos" => 1,
|
||||||
"video" => 0,
|
"video" => 0,
|
||||||
"audio" => 0,
|
"audio" => $club->isEveryoneCanUploadAudios() ? 1 : 0,
|
||||||
"docs" => 0,
|
"docs" => 0,
|
||||||
"topics" => $club->isEveryoneCanCreateTopics() == true ? 1 : 0,
|
"topics" => $club->isEveryoneCanCreateTopics() == true ? 1 : 0,
|
||||||
"wiki" => 0,
|
"wiki" => 0,
|
||||||
|
|
|
@ -2,70 +2,205 @@
|
||||||
namespace openvk\VKAPI\Handlers;
|
namespace openvk\VKAPI\Handlers;
|
||||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||||
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
||||||
|
use openvk\Web\Models\Repositories\Comments as CommentsRepo;
|
||||||
|
use openvk\Web\Models\Repositories\Videos as VideosRepo;
|
||||||
|
use openvk\Web\Models\Repositories\Photos as PhotosRepo;
|
||||||
|
use openvk\Web\Models\Repositories\Notes as NotesRepo;
|
||||||
|
|
||||||
|
|
||||||
final class Likes extends VKAPIRequestHandler
|
final class Likes extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
function add(string $type, int $owner_id, int $item_id): object
|
function add(string $type, int $owner_id, int $item_id): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$postable = NULL;
|
||||||
switch($type) {
|
switch($type) {
|
||||||
case "post":
|
case "post":
|
||||||
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||||
if(is_null($post))
|
$postable = $post;
|
||||||
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
break;
|
||||||
|
case "comment":
|
||||||
$post->setLike(true, $this->getUser());
|
$comment = (new CommentsRepo)->get($item_id);
|
||||||
|
$postable = $comment;
|
||||||
return (object) [
|
break;
|
||||||
"likes" => $post->getLikesCount()
|
case "video":
|
||||||
];
|
$video = (new VideosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||||
|
$postable = $video;
|
||||||
|
break;
|
||||||
|
case "photo":
|
||||||
|
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||||
|
$postable = $photo;
|
||||||
|
break;
|
||||||
|
case "note":
|
||||||
|
$note = (new NotesRepo)->getNoteById($owner_id, $item_id);
|
||||||
|
$postable = $note;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
$this->fail(100, "One of the parameters specified was missing or invalid: incorrect type");
|
$this->fail(100, "One of the parameters specified was missing or invalid: incorrect type");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function delete(string $type, int $owner_id, int $item_id): object
|
if(is_null($postable) || $postable->isDeleted())
|
||||||
{
|
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
||||||
$this->requireUser();
|
|
||||||
|
if(!$postable->canBeViewedBy($this->getUser() ?? NULL)) {
|
||||||
|
$this->fail(2, "Access to postable denied");
|
||||||
|
}
|
||||||
|
|
||||||
|
$postable->setLike(true, $this->getUser());
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"likes" => $postable->getLikesCount()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete(string $type, int $owner_id, int $item_id): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$postable = NULL;
|
||||||
switch($type) {
|
switch($type) {
|
||||||
case "post":
|
case "post":
|
||||||
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||||
if (is_null($post))
|
$postable = $post;
|
||||||
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
break;
|
||||||
|
case "comment":
|
||||||
$post->setLike(false, $this->getUser());
|
$comment = (new CommentsRepo)->get($item_id);
|
||||||
return (object) [
|
$postable = $comment;
|
||||||
"likes" => $post->getLikesCount()
|
break;
|
||||||
];
|
case "video":
|
||||||
|
$video = (new VideosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||||
|
$postable = $video;
|
||||||
|
break;
|
||||||
|
case "photo":
|
||||||
|
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||||
|
$postable = $photo;
|
||||||
|
break;
|
||||||
|
case "note":
|
||||||
|
$note = (new NotesRepo)->getNoteById($owner_id, $item_id);
|
||||||
|
$postable = $note;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
$this->fail(100, "One of the parameters specified was missing or invalid: incorrect type");
|
$this->fail(100, "One of the parameters specified was missing or invalid: incorrect type");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if(is_null($postable) || $postable->isDeleted())
|
||||||
|
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
||||||
|
|
||||||
|
if(!$postable->canBeViewedBy($this->getUser() ?? NULL)) {
|
||||||
|
$this->fail(2, "Access to postable denied");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!is_null($postable)) {
|
||||||
|
$postable->setLike(false, $this->getUser());
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"likes" => $postable->getLikesCount()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function isLiked(int $user_id, string $type, int $owner_id, int $item_id): object
|
function isLiked(int $user_id, string $type, int $owner_id, int $item_id): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
|
$user = (new UsersRepo)->get($user_id);
|
||||||
|
|
||||||
|
if(is_null($user) || $user->isDeleted())
|
||||||
|
$this->fail(100, "One of the parameters specified was missing or invalid: user not found");
|
||||||
|
|
||||||
|
if(!$user->canBeViewedBy($this->getUser())) {
|
||||||
|
$this->fail(1984, "Access denied: you can't see this user");
|
||||||
|
}
|
||||||
|
|
||||||
|
$postable = NULL;
|
||||||
switch($type) {
|
switch($type) {
|
||||||
case "post":
|
case "post":
|
||||||
$user = (new UsersRepo)->get($user_id);
|
|
||||||
if (is_null($user))
|
|
||||||
$this->fail(100, "One of the parameters specified was missing or invalid: user not found");
|
|
||||||
|
|
||||||
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||||
if (is_null($post))
|
$postable = $post;
|
||||||
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
break;
|
||||||
|
case "comment":
|
||||||
return (object) [
|
$comment = (new CommentsRepo)->get($item_id);
|
||||||
"liked" => (int) $post->hasLikeFrom($user),
|
$postable = $comment;
|
||||||
"copied" => 0 # TODO: handle this
|
break;
|
||||||
];
|
case "video":
|
||||||
|
$video = (new VideosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||||
|
$postable = $video;
|
||||||
|
break;
|
||||||
|
case "photo":
|
||||||
|
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||||
|
$postable = $photo;
|
||||||
|
break;
|
||||||
|
case "note":
|
||||||
|
$note = (new NotesRepo)->getNoteById($owner_id, $item_id);
|
||||||
|
$postable = $note;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
$this->fail(100, "One of the parameters specified was missing or invalid: incorrect type");
|
$this->fail(100, "One of the parameters specified was missing or invalid: incorrect type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(is_null($postable) || $postable->isDeleted())
|
||||||
|
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
||||||
|
|
||||||
|
if(!$postable->canBeViewedBy($this->getUser())) {
|
||||||
|
$this->fail(665, "Access to postable denied");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"liked" => (int) $postable->hasLikeFrom($user),
|
||||||
|
"copied" => 0
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getList(string $type, int $owner_id, int $item_id, bool $extended = false, int $offset = 0, int $count = 10, bool $skip_own = false)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$object = NULL;
|
||||||
|
|
||||||
|
switch($type) {
|
||||||
|
case "post":
|
||||||
|
$object = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||||
|
break;
|
||||||
|
case "comment":
|
||||||
|
$object = (new CommentsRepo)->get($item_id);
|
||||||
|
break;
|
||||||
|
case "photo":
|
||||||
|
$object = (new PhotosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||||
|
break;
|
||||||
|
case "video":
|
||||||
|
$object = (new VideosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$this->fail(58, "Invalid type");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$object || $object->isDeleted())
|
||||||
|
$this->fail(56, "Invalid postable");
|
||||||
|
|
||||||
|
if(!$object->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(665, "Access to postable denied");
|
||||||
|
|
||||||
|
$res = (object)[
|
||||||
|
"count" => $object->getLikesCount(),
|
||||||
|
"items" => []
|
||||||
|
];
|
||||||
|
|
||||||
|
$likers = array_slice(iterator_to_array($object->getLikers(1, $offset + $count)), $offset);
|
||||||
|
|
||||||
|
foreach($likers as $liker) {
|
||||||
|
if($skip_own && $liker->getId() == $this->getUser()->getId())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(!$extended)
|
||||||
|
$res->items[] = $liker->getId();
|
||||||
|
else
|
||||||
|
$res->items[] = $liker->toVkApiStruct();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,8 @@ final class Messages extends VKAPIRequestHandler
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function send(int $user_id = -1, int $peer_id = -1, string $domain = "", int $chat_id = -1, string $user_ids = "", string $message = "", int $sticker_id = -1, int $forGodSakePleaseDoNotReportAboutMyOnlineActivity = 0)
|
function send(int $user_id = -1, int $peer_id = -1, string $domain = "", int $chat_id = -1, string $user_ids = "", string $message = "", int $sticker_id = -1, int $forGodSakePleaseDoNotReportAboutMyOnlineActivity = 0,
|
||||||
|
string $attachment = "") # интересно почему не attachments
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
@ -79,7 +80,8 @@ final class Messages extends VKAPIRequestHandler
|
||||||
$this->fail(946, "Chats are not implemented");
|
$this->fail(946, "Chats are not implemented");
|
||||||
else if($sticker_id !== -1)
|
else if($sticker_id !== -1)
|
||||||
$this->fail(-151, "Stickers are not implemented");
|
$this->fail(-151, "Stickers are not implemented");
|
||||||
else if(empty($message))
|
|
||||||
|
if(empty($message) && empty($attachment))
|
||||||
$this->fail(100, "Message text is empty or invalid");
|
$this->fail(100, "Message text is empty or invalid");
|
||||||
|
|
||||||
# lol recursion
|
# lol recursion
|
||||||
|
@ -117,6 +119,21 @@ final class Messages extends VKAPIRequestHandler
|
||||||
if(!$msg)
|
if(!$msg)
|
||||||
$this->fail(950, "Internal error");
|
$this->fail(950, "Internal error");
|
||||||
else
|
else
|
||||||
|
if(!empty($attachment)) {
|
||||||
|
$attachs = parseAttachments($attachment);
|
||||||
|
|
||||||
|
# Работают только фотки, остальное просто не будет отображаться.
|
||||||
|
if(sizeof($attachs) >= 10)
|
||||||
|
$this->fail(15, "Too many attachments");
|
||||||
|
|
||||||
|
foreach($attachs as $attach) {
|
||||||
|
if($attach && !$attach->isDeleted() && $attach->getOwner()->getId() == $this->getUser()->getId())
|
||||||
|
$msg->attach($attach);
|
||||||
|
else
|
||||||
|
$this->fail(52, "One of the attachments is invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $msg->getId();
|
return $msg->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,4 +410,49 @@ final class Messages extends VKAPIRequestHandler
|
||||||
|
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function edit(int $message_id, string $message = "", string $attachment = "", int $peer_id = 0)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$msg = (new MSGRepo)->get($message_id);
|
||||||
|
|
||||||
|
if(empty($message) && empty($attachment))
|
||||||
|
$this->fail(100, "Required parameter 'message' missing.");
|
||||||
|
|
||||||
|
if(!$msg || $msg->isDeleted())
|
||||||
|
$this->fail(102, "Invalid message");
|
||||||
|
|
||||||
|
if($msg->getSender()->getId() != $this->getUser()->getId())
|
||||||
|
$this->fail(15, "Access to message denied");
|
||||||
|
|
||||||
|
if(!empty($message))
|
||||||
|
$msg->setContent($message);
|
||||||
|
|
||||||
|
$msg->setEdited(time());
|
||||||
|
$msg->save(true);
|
||||||
|
|
||||||
|
if(!empty($attachment)) {
|
||||||
|
$attachs = parseAttachments($attachment);
|
||||||
|
$newAttachmentsCount = sizeof($attachs);
|
||||||
|
|
||||||
|
$postsAttachments = iterator_to_array($msg->getChildren());
|
||||||
|
|
||||||
|
if(sizeof($postsAttachments) >= 10)
|
||||||
|
$this->fail(15, "Message have too many attachments");
|
||||||
|
|
||||||
|
if(($newAttachmentsCount + sizeof($postsAttachments)) > 10)
|
||||||
|
$this->fail(158, "Message will have too many attachments");
|
||||||
|
|
||||||
|
foreach($attachs as $attach) {
|
||||||
|
if($attach && !$attach->isDeleted() && $attach->getOwner()->getId() == $this->getUser()->getId())
|
||||||
|
$msg->attach($attach);
|
||||||
|
else
|
||||||
|
$this->fail(52, "One of the attachments is invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,8 @@ final class Newsfeed extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
$queryBase = "FROM `posts` LEFT JOIN `groups` ON GREATEST(`posts`.`wall`, 0) = 0 AND `groups`.`id` = ABS(`posts`.`wall`) WHERE (`groups`.`hide_from_global_feed` = 0 OR `groups`.`name` IS NULL) AND `posts`.`deleted` = 0";
|
$queryBase = "FROM `posts` LEFT JOIN `groups` ON GREATEST(`posts`.`wall`, 0) = 0 AND `groups`.`id` = ABS(`posts`.`wall`) LEFT JOIN `profiles` ON LEAST(`posts`.`wall`, 0) = 0 AND `profiles`.`id` = ABS(`posts`.`wall`)";
|
||||||
|
$queryBase .= "WHERE (`groups`.`hide_from_global_feed` = 0 OR `groups`.`name` IS NULL) AND (`profiles`.`profile_type` = 0 OR `profiles`.`first_name` IS NULL) AND `posts`.`deleted` = 0 AND `posts`.`suggested` = 0";
|
||||||
|
|
||||||
if($this->getUser()->getNsfwTolerance() === User::NSFW_INTOLERANT)
|
if($this->getUser()->getNsfwTolerance() === User::NSFW_INTOLERANT)
|
||||||
$queryBase .= " AND `nsfw` = 0";
|
$queryBase .= " AND `nsfw` = 0";
|
||||||
|
|
|
@ -40,6 +40,9 @@ final class Notes extends VKAPIRequestHandler
|
||||||
if($note->getOwner()->isDeleted())
|
if($note->getOwner()->isDeleted())
|
||||||
$this->fail(403, "Owner is deleted");
|
$this->fail(403, "Owner is deleted");
|
||||||
|
|
||||||
|
if(!$note->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
||||||
$this->fail(43, "No access");
|
$this->fail(43, "No access");
|
||||||
|
|
||||||
|
@ -118,21 +121,6 @@ final class Notes extends VKAPIRequestHandler
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteComment(int $comment_id, int $owner_id = 0)
|
|
||||||
{
|
|
||||||
$this->requireUser();
|
|
||||||
$this->willExecuteWriteAction();
|
|
||||||
|
|
||||||
$comment = (new CommentsRepo)->get($comment_id);
|
|
||||||
|
|
||||||
if(!$comment || !$comment->canBeDeletedBy($this->getUser()))
|
|
||||||
$this->fail(403, "Access to comment denied");
|
|
||||||
|
|
||||||
$comment->delete();
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function edit(string $note_id, string $title = "", string $text = "", int $privacy = 0, int $comment_privacy = 0, string $privacy_view = "", string $privacy_comment = "")
|
function edit(string $note_id, string $title = "", string $text = "", int $privacy = 0, int $comment_privacy = 0, string $privacy_view = "", string $privacy_comment = "")
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
@ -159,25 +147,6 @@ final class Notes extends VKAPIRequestHandler
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function editComment(int $comment_id, string $message, int $owner_id = NULL)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get(int $user_id, string $note_ids = "", int $offset = 0, int $count = 10, int $sort = 0)
|
function get(int $user_id, string $note_ids = "", int $offset = 0, int $count = 10, int $sort = 0)
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
@ -187,7 +156,10 @@ final class Notes extends VKAPIRequestHandler
|
||||||
$this->fail(15, "Invalid user");
|
$this->fail(15, "Invalid user");
|
||||||
|
|
||||||
if(!$user->getPrivacyPermission('notes.read', $this->getUser()))
|
if(!$user->getPrivacyPermission('notes.read', $this->getUser()))
|
||||||
$this->fail(43, "Access denied: this user chose to hide his notes");
|
$this->fail(15, "Access denied: this user chose to hide his notes");
|
||||||
|
|
||||||
|
if(!$user->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
if(empty($note_ids)) {
|
if(empty($note_ids)) {
|
||||||
$notes = array_slice(iterator_to_array((new NotesRepo)->getUserNotes($user, 1, $count + $offset, $sort == 0 ? "ASC" : "DESC")), $offset);
|
$notes = array_slice(iterator_to_array((new NotesRepo)->getUserNotes($user, 1, $count + $offset, $sort == 0 ? "ASC" : "DESC")), $offset);
|
||||||
|
@ -238,6 +210,9 @@ final class Notes extends VKAPIRequestHandler
|
||||||
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
||||||
$this->fail(40, "Access denied: this user chose to hide his notes");
|
$this->fail(40, "Access denied: this user chose to hide his notes");
|
||||||
|
|
||||||
|
if(!$note->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access to note denied");
|
||||||
|
|
||||||
return $note->toVkApiStruct();
|
return $note->toVkApiStruct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,6 +233,9 @@ final class Notes extends VKAPIRequestHandler
|
||||||
|
|
||||||
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
||||||
$this->fail(14, "No access");
|
$this->fail(14, "No access");
|
||||||
|
|
||||||
|
if(!$note->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access to note denied");
|
||||||
|
|
||||||
$arr = (object) [
|
$arr = (object) [
|
||||||
"count" => $note->getCommentsCount(),
|
"count" => $note->getCommentsCount(),
|
||||||
|
|
83
VKAPI/Handlers/Notifications.php
Normal file
83
VKAPI/Handlers/Notifications.php
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\VKAPI\Handlers;
|
||||||
|
use openvk\Web\Models\Entities\Club;
|
||||||
|
use openvk\Web\Models\Repositories\{Notifications as Notifs, Clubs, Users};
|
||||||
|
|
||||||
|
final class Notifications extends VKAPIRequestHandler
|
||||||
|
{
|
||||||
|
function get(int $count = 10,
|
||||||
|
string $from = "",
|
||||||
|
int $offset = 0,
|
||||||
|
string $start_from = "",
|
||||||
|
string $filters = "",
|
||||||
|
int $start_time = 0,
|
||||||
|
int $end_time = 0,
|
||||||
|
int $archived = 0)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$res = (object)[
|
||||||
|
"items" => [],
|
||||||
|
"profiles" => [],
|
||||||
|
"groups" => [],
|
||||||
|
"last_viewed" => $this->getUser()->getNotificationOffset()
|
||||||
|
];
|
||||||
|
|
||||||
|
if($count > 100)
|
||||||
|
$this->fail(125, "Count is too big");
|
||||||
|
|
||||||
|
if(!eventdb())
|
||||||
|
$this->fail(1289, "EventDB is disabled on this instance");
|
||||||
|
|
||||||
|
$notifs = array_slice(iterator_to_array((new Notifs)->getNotificationsByUser($this->getUser(), $this->getUser()->getNotificationOffset(), (bool)$archived, 1, $offset + $count)), $offset);
|
||||||
|
$tmpProfiles = [];
|
||||||
|
foreach($notifs as $notif) {
|
||||||
|
$sxModel = $notif->getModel(1);
|
||||||
|
|
||||||
|
if(!method_exists($sxModel, "getAvatarUrl"))
|
||||||
|
$sxModel = $notif->getModel(0);
|
||||||
|
|
||||||
|
|
||||||
|
$tmpProfiles[] = $sxModel instanceof Club ? $sxModel->getId() * -1 : $sxModel->getId();
|
||||||
|
$res->items[] = $notif->toVkApiStruct();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(array_unique($tmpProfiles) as $id) {
|
||||||
|
if($id > 0) {
|
||||||
|
$sxModel = (new Users)->get($id);
|
||||||
|
$result = (object)[
|
||||||
|
"uid" => $sxModel->getId(),
|
||||||
|
"first_name" => $sxModel->getFirstName(),
|
||||||
|
"last_name" => $sxModel->getLastName(),
|
||||||
|
"photo" => $sxModel->getAvatarUrl(),
|
||||||
|
"photo_medium_rec" => $sxModel->getAvatarUrl("tiny"),
|
||||||
|
"screen_name" => $sxModel->getShortCode()
|
||||||
|
];
|
||||||
|
|
||||||
|
$res->profiles[] = $result;
|
||||||
|
} else {
|
||||||
|
$sxModel = (new Clubs)->get(abs($id));
|
||||||
|
$result = $sxModel->toVkApiStruct($this->getUser());
|
||||||
|
|
||||||
|
$res->groups[] = $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function markAsViewed()
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->getUser()->updateNotificationOffset();
|
||||||
|
$this->getUser()->save();
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -304,7 +304,6 @@ final class Photos extends VKAPIRequestHandler
|
||||||
if(!$user || $user->isDeleted())
|
if(!$user || $user->isDeleted())
|
||||||
$this->fail(2, "Invalid user");
|
$this->fail(2, "Invalid user");
|
||||||
|
|
||||||
|
|
||||||
if(!$user->getPrivacyPermission('photos.read', $this->getUser()))
|
if(!$user->getPrivacyPermission('photos.read', $this->getUser()))
|
||||||
$this->fail(21, "This user chose to hide his albums.");
|
$this->fail(21, "This user chose to hide his albums.");
|
||||||
|
|
||||||
|
@ -363,26 +362,21 @@ final class Photos extends VKAPIRequestHandler
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
if($user_id == 0 && $group_id == 0 || $user_id > 0 && $group_id > 0) {
|
if($user_id == 0 && $group_id == 0 || $user_id > 0 && $group_id > 0)
|
||||||
$this->fail(21, "Select user_id or group_id");
|
$this->fail(21, "Select user_id or group_id");
|
||||||
}
|
|
||||||
|
|
||||||
if($user_id > 0) {
|
if($user_id > 0) {
|
||||||
|
|
||||||
$us = (new UsersRepo)->get($user_id);
|
$us = (new UsersRepo)->get($user_id);
|
||||||
if(!$us || $us->isDeleted()) {
|
if(!$us || $us->isDeleted())
|
||||||
$this->fail(21, "Invalid user");
|
$this->fail(21, "Invalid user");
|
||||||
}
|
|
||||||
|
|
||||||
if(!$us->getPrivacyPermission('photos.read', $this->getUser())) {
|
if(!$us->getPrivacyPermission('photos.read', $this->getUser()))
|
||||||
$this->fail(21, "This user chose to hide his albums.");
|
$this->fail(21, "This user chose to hide his albums.");
|
||||||
}
|
|
||||||
|
|
||||||
return (new Albums)->getUserAlbumsCount($us);
|
return (new Albums)->getUserAlbumsCount($us);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($group_id > 0)
|
if($group_id > 0) {
|
||||||
{
|
|
||||||
$cl = (new Clubs)->get($group_id);
|
$cl = (new Clubs)->get($group_id);
|
||||||
if(!$cl) {
|
if(!$cl) {
|
||||||
$this->fail(21, "Invalid club");
|
$this->fail(21, "Invalid club");
|
||||||
|
@ -404,17 +398,11 @@ final class Photos extends VKAPIRequestHandler
|
||||||
$ph = explode("_", $phota);
|
$ph = explode("_", $phota);
|
||||||
$photo = (new PhotosRepo)->getByOwnerAndVID((int)$ph[0], (int)$ph[1]);
|
$photo = (new PhotosRepo)->getByOwnerAndVID((int)$ph[0], (int)$ph[1]);
|
||||||
|
|
||||||
if(!$photo || $photo->isDeleted()) {
|
if(!$photo || $photo->isDeleted())
|
||||||
$this->fail(21, "Invalid photo");
|
$this->fail(21, "Invalid photo");
|
||||||
}
|
|
||||||
|
|
||||||
if($photo->getOwner()->isDeleted()) {
|
if(!$photo->canBeViewedBy($this->getUser()))
|
||||||
$this->fail(21, "Owner of this photo is deleted");
|
$this->fail(15, "Access denied");
|
||||||
}
|
|
||||||
|
|
||||||
if(!$photo->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) {
|
|
||||||
$this->fail(21, "This user chose to hide his photos.");
|
|
||||||
}
|
|
||||||
|
|
||||||
$res[] = $photo->toVkApiStruct($photo_sizes, $extended);
|
$res[] = $photo->toVkApiStruct($photo_sizes, $extended);
|
||||||
}
|
}
|
||||||
|
@ -432,13 +420,11 @@ final class Photos extends VKAPIRequestHandler
|
||||||
if(empty($photo_ids)) {
|
if(empty($photo_ids)) {
|
||||||
$album = (new Albums)->getAlbumByOwnerAndId($owner_id, $album_id);
|
$album = (new Albums)->getAlbumByOwnerAndId($owner_id, $album_id);
|
||||||
|
|
||||||
if(!$album->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) {
|
if(!$album || $album->isDeleted())
|
||||||
$this->fail(21, "This user chose to hide his albums.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$album || $album->isDeleted()) {
|
|
||||||
$this->fail(21, "Invalid album");
|
$this->fail(21, "Invalid album");
|
||||||
}
|
|
||||||
|
if(!$album->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
$photos = array_slice(iterator_to_array($album->getPhotos(1, $count + $offset)), $offset);
|
$photos = array_slice(iterator_to_array($album->getPhotos(1, $count + $offset)), $offset);
|
||||||
$res["count"] = sizeof($photos);
|
$res["count"] = sizeof($photos);
|
||||||
|
@ -456,12 +442,11 @@ final class Photos extends VKAPIRequestHandler
|
||||||
"items" => []
|
"items" => []
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach($photos as $photo)
|
foreach($photos as $photo) {
|
||||||
{
|
|
||||||
$id = explode("_", $photo);
|
$id = explode("_", $photo);
|
||||||
|
|
||||||
$phot = (new PhotosRepo)->getByOwnerAndVID((int)$id[0], (int)$id[1]);
|
$phot = (new PhotosRepo)->getByOwnerAndVID((int)$id[0], (int)$id[1]);
|
||||||
if($phot && !$phot->isDeleted()) {
|
if($phot && !$phot->isDeleted() && $phot->canBeViewedBy($this->getUser())) {
|
||||||
$res["items"][] = $phot->toVkApiStruct($photo_sizes, $extended);
|
$res["items"][] = $phot->toVkApiStruct($photo_sizes, $extended);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -477,13 +462,11 @@ final class Photos extends VKAPIRequestHandler
|
||||||
|
|
||||||
$album = (new Albums)->get($album_id);
|
$album = (new Albums)->get($album_id);
|
||||||
|
|
||||||
if(!$album || $album->canBeModifiedBy($this->getUser())) {
|
if(!$album || $album->canBeModifiedBy($this->getUser()))
|
||||||
$this->fail(21, "Invalid album");
|
$this->fail(21, "Invalid album");
|
||||||
}
|
|
||||||
|
|
||||||
if($album->isDeleted()) {
|
if($album->isDeleted())
|
||||||
$this->fail(22, "Album already deleted");
|
$this->fail(22, "Album already deleted");
|
||||||
}
|
|
||||||
|
|
||||||
$album->delete();
|
$album->delete();
|
||||||
|
|
||||||
|
@ -497,13 +480,11 @@ final class Photos extends VKAPIRequestHandler
|
||||||
|
|
||||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
||||||
|
|
||||||
if(!$photo) {
|
if(!$photo)
|
||||||
$this->fail(21, "Invalid photo");
|
$this->fail(21, "Invalid photo");
|
||||||
}
|
|
||||||
|
|
||||||
if($photo->isDeleted()) {
|
if($photo->isDeleted())
|
||||||
$this->fail(21, "Photo is deleted");
|
$this->fail(21, "Photo is deleted");
|
||||||
}
|
|
||||||
|
|
||||||
if(!empty($caption)) {
|
if(!empty($caption)) {
|
||||||
$photo->setDescription($caption);
|
$photo->setDescription($caption);
|
||||||
|
@ -521,17 +502,14 @@ final class Photos extends VKAPIRequestHandler
|
||||||
if(empty($photos)) {
|
if(empty($photos)) {
|
||||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
||||||
|
|
||||||
if($this->getUser()->getId() !== $photo->getOwner()->getId()) {
|
if($this->getUser()->getId() !== $photo->getOwner()->getId())
|
||||||
$this->fail(21, "You can't delete another's photo");
|
$this->fail(21, "You can't delete another's photo");
|
||||||
}
|
|
||||||
|
|
||||||
if(!$photo) {
|
if(!$photo)
|
||||||
$this->fail(21, "Invalid photo");
|
$this->fail(21, "Invalid photo");
|
||||||
}
|
|
||||||
|
|
||||||
if($photo->isDeleted()) {
|
if($photo->isDeleted())
|
||||||
$this->fail(21, "Photo already deleted");
|
$this->fail(21, "Photo is already deleted");
|
||||||
}
|
|
||||||
|
|
||||||
$photo->delete();
|
$photo->delete();
|
||||||
} else {
|
} else {
|
||||||
|
@ -543,17 +521,14 @@ final class Photos extends VKAPIRequestHandler
|
||||||
|
|
||||||
$phot = (new PhotosRepo)->getByOwnerAndVID((int)$id[0], (int)$id[1]);
|
$phot = (new PhotosRepo)->getByOwnerAndVID((int)$id[0], (int)$id[1]);
|
||||||
|
|
||||||
if($this->getUser()->getId() !== $phot->getOwner()->getId()) {
|
if($this->getUser()->getId() !== $phot->getOwner()->getId())
|
||||||
$this->fail(21, "You can't delete another's photo");
|
$this->fail(21, "You can't delete another's photo");
|
||||||
}
|
|
||||||
|
|
||||||
if(!$phot) {
|
if(!$phot)
|
||||||
$this->fail(21, "Invalid photo");
|
$this->fail(21, "Invalid photo");
|
||||||
}
|
|
||||||
|
|
||||||
if($phot->isDeleted()) {
|
if($phot->isDeleted())
|
||||||
$this->fail(21, "Photo already deleted");
|
$this->fail(21, "Photo already deleted");
|
||||||
}
|
|
||||||
|
|
||||||
$phot->delete();
|
$phot->delete();
|
||||||
}
|
}
|
||||||
|
@ -573,17 +548,11 @@ final class Photos extends VKAPIRequestHandler
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$comment = (new CommentsRepo)->get($comment_id);
|
$comment = (new CommentsRepo)->get($comment_id);
|
||||||
if(!$comment) {
|
if(!$comment)
|
||||||
$this->fail(21, "Invalid comment");
|
$this->fail(21, "Invalid comment");
|
||||||
}
|
|
||||||
|
|
||||||
if(!$comment->canBeModifiedBy($this->getUser())) {
|
if(!$comment->canBeModifiedBy($this->getUser()))
|
||||||
$this->fail(21, "Forbidden");
|
$this->fail(21, "Access denied");
|
||||||
}
|
|
||||||
|
|
||||||
if($comment->isDeleted()) {
|
|
||||||
$this->fail(4, "Comment already deleted");
|
|
||||||
}
|
|
||||||
|
|
||||||
$comment->delete();
|
$comment->delete();
|
||||||
|
|
||||||
|
@ -595,20 +564,16 @@ final class Photos extends VKAPIRequestHandler
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
if(empty($message) && empty($attachments)) {
|
if(empty($message) && empty($attachments))
|
||||||
$this->fail(100, "Required parameter 'message' missing.");
|
$this->fail(100, "Required parameter 'message' missing.");
|
||||||
}
|
|
||||||
|
|
||||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
||||||
|
|
||||||
if(!$photo->getAlbum()->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) {
|
if(!$photo || $photo->isDeleted())
|
||||||
$this->fail(21, "This user chose to hide his albums.");
|
$this->fail(180, "Invalid photo");
|
||||||
}
|
|
||||||
|
|
||||||
if(!$photo)
|
if(!$photo->canBeViewedBy($this->getUser()))
|
||||||
$this->fail(180, "Photo not found");
|
$this->fail(15, "Access to photo denied");
|
||||||
if($photo->isDeleted())
|
|
||||||
$this->fail(189, "Photo is deleted");
|
|
||||||
|
|
||||||
$comment = new Comment;
|
$comment = new Comment;
|
||||||
$comment->setOwner($this->getUser()->getId());
|
$comment->setOwner($this->getUser()->getId());
|
||||||
|
@ -669,22 +634,21 @@ final class Photos extends VKAPIRequestHandler
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
if($owner_id < 0) {
|
if($owner_id < 0)
|
||||||
$this->fail(4, "This method doesn't works with clubs");
|
$this->fail(4, "This method doesn't works with clubs");
|
||||||
}
|
|
||||||
|
|
||||||
$user = (new UsersRepo)->get($owner_id);
|
$user = (new UsersRepo)->get($owner_id);
|
||||||
|
|
||||||
if(!$user) {
|
if(!$user)
|
||||||
$this->fail(4, "Invalid user");
|
$this->fail(4, "Invalid user");
|
||||||
}
|
|
||||||
|
|
||||||
if(!$user->getPrivacyPermission('photos.read', $this->getUser())) {
|
if(!$user->getPrivacyPermission('photos.read', $this->getUser()))
|
||||||
$this->fail(21, "This user chose to hide his albums.");
|
$this->fail(21, "This user chose to hide his albums.");
|
||||||
}
|
|
||||||
|
|
||||||
$photos = array_slice(iterator_to_array((new PhotosRepo)->getEveryUserPhoto($user, 1, $count + $offset)), $offset);
|
$photos = array_slice(iterator_to_array((new PhotosRepo)->getEveryUserPhoto($user, 1, $count + $offset)), $offset);
|
||||||
$res = [];
|
$res = [
|
||||||
|
"items" => [],
|
||||||
|
];
|
||||||
|
|
||||||
foreach($photos as $photo) {
|
foreach($photos as $photo) {
|
||||||
if(!$photo || $photo->isDeleted()) continue;
|
if(!$photo || $photo->isDeleted()) continue;
|
||||||
|
@ -702,17 +666,11 @@ final class Photos extends VKAPIRequestHandler
|
||||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
||||||
$comms = array_slice(iterator_to_array($photo->getComments(1, $offset + $count)), $offset);
|
$comms = array_slice(iterator_to_array($photo->getComments(1, $offset + $count)), $offset);
|
||||||
|
|
||||||
if(!$photo) {
|
if(!$photo || $photo->isDeleted())
|
||||||
$this->fail(4, "Invalid photo");
|
$this->fail(4, "Invalid photo");
|
||||||
}
|
|
||||||
|
|
||||||
if(!$photo->getAlbum()->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) {
|
if(!$photo->canBeViewedBy($this->getUser()))
|
||||||
$this->fail(21, "This user chose to hide his photos.");
|
$this->fail(21, "Access denied");
|
||||||
}
|
|
||||||
|
|
||||||
if($photo->isDeleted()) {
|
|
||||||
$this->fail(4, "Photo is deleted");
|
|
||||||
}
|
|
||||||
|
|
||||||
$res = [
|
$res = [
|
||||||
"count" => sizeof($comms),
|
"count" => sizeof($comms),
|
||||||
|
@ -730,4 +688,4 @@ final class Photos extends VKAPIRequestHandler
|
||||||
|
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,4 +104,67 @@ final class Polls extends VKAPIRequestHandler
|
||||||
$this->fail(8, "how.to. ook.bacon.in.microwova.");
|
$this->fail(8, "how.to. ook.bacon.in.microwova.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getVoters(int $poll_id, int $answer_ids, int $offset = 0, int $count = 6)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$poll = (new PollsRepo)->get($poll_id);
|
||||||
|
|
||||||
|
if(!$poll)
|
||||||
|
$this->fail(251, "Invalid poll");
|
||||||
|
|
||||||
|
if($poll->isAnonymous())
|
||||||
|
$this->fail(251, "Access denied: poll is anonymous.");
|
||||||
|
|
||||||
|
$voters = array_slice($poll->getVoters($answer_ids, 1, $offset + $count), $offset);
|
||||||
|
$res = (object)[
|
||||||
|
"answer_id" => $answer_ids,
|
||||||
|
"users" => []
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach($voters as $voter)
|
||||||
|
$res->users[] = $voter->toVkApiStruct();
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function create(string $question, string $add_answers, bool $disable_unvote = false, bool $is_anonymous = false, bool $is_multiple = false, int $end_date = 0)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$options = json_decode($add_answers);
|
||||||
|
|
||||||
|
if(!$options || empty($options))
|
||||||
|
$this->fail(62, "Invalid options");
|
||||||
|
|
||||||
|
if(sizeof($options) > ovkGetQuirk("polls.max-opts"))
|
||||||
|
$this->fail(51, "Too many options");
|
||||||
|
|
||||||
|
$poll = new Poll;
|
||||||
|
$poll->setOwner($this->getUser());
|
||||||
|
$poll->setTitle($question);
|
||||||
|
$poll->setMultipleChoice($is_multiple);
|
||||||
|
$poll->setAnonymity($is_anonymous);
|
||||||
|
$poll->setRevotability(!$disable_unvote);
|
||||||
|
$poll->setOptions($options);
|
||||||
|
|
||||||
|
if($end_date > time()) {
|
||||||
|
if($end_date > time() + (DAY * 365))
|
||||||
|
$this->fail(89, "End date is too big");
|
||||||
|
|
||||||
|
$poll->setEndDate($end_date);
|
||||||
|
}
|
||||||
|
|
||||||
|
$poll->save();
|
||||||
|
|
||||||
|
return $this->getById($poll->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit()
|
||||||
|
{
|
||||||
|
#todo
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,27 @@ final class Status extends VKAPIRequestHandler
|
||||||
function get(int $user_id = 0, int $group_id = 0)
|
function get(int $user_id = 0, int $group_id = 0)
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
if($user_id == 0 && $group_id == 0) {
|
|
||||||
return $this->getUser()->getStatus();
|
if($user_id == 0 && $group_id == 0)
|
||||||
} else {
|
$user_id = $this->getUser()->getId();
|
||||||
if($group_id > 0)
|
|
||||||
$this->fail(501, "Group statuses are not implemented");
|
if($group_id > 0)
|
||||||
else
|
$this->fail(501, "Group statuses are not implemented");
|
||||||
return (new UsersRepo)->get($user_id)->getStatus();
|
else {
|
||||||
|
$user = (new UsersRepo)->get($user_id);
|
||||||
|
|
||||||
|
if(!$user || $user->isDeleted() || !$user->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Invalid user");
|
||||||
|
|
||||||
|
$audioStatus = $user->getCurrentAudioStatus();
|
||||||
|
if($audioStatus) {
|
||||||
|
return [
|
||||||
|
"status" => $user->getStatus(),
|
||||||
|
"audio" => $audioStatus->toVkApiStruct(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user->getStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\VKAPI\Handlers;
|
namespace openvk\VKAPI\Handlers;
|
||||||
use openvk\Web\Models\Entities\User;
|
use openvk\Web\Models\Entities\{User, Report};
|
||||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||||
|
use openvk\Web\Models\Repositories\{Photos, Clubs, Albums, Videos, Notes, Audios};
|
||||||
|
use openvk\Web\Models\Repositories\Reports;
|
||||||
|
|
||||||
final class Users extends VKAPIRequestHandler
|
final class Users extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
|
@ -36,8 +38,8 @@ final class Users extends VKAPIRequestHandler
|
||||||
} else if($usr->isBanned()) {
|
} else if($usr->isBanned()) {
|
||||||
$response[$i] = (object)[
|
$response[$i] = (object)[
|
||||||
"id" => $usr->getId(),
|
"id" => $usr->getId(),
|
||||||
"first_name" => $usr->getFirstName(),
|
"first_name" => $usr->getFirstName(true),
|
||||||
"last_name" => $usr->getLastName(),
|
"last_name" => $usr->getLastName(true),
|
||||||
"deactivated" => "banned",
|
"deactivated" => "banned",
|
||||||
"ban_reason" => $usr->getBanReason()
|
"ban_reason" => $usr->getBanReason()
|
||||||
];
|
];
|
||||||
|
@ -46,21 +48,21 @@ final class Users extends VKAPIRequestHandler
|
||||||
} else {
|
} else {
|
||||||
$response[$i] = (object)[
|
$response[$i] = (object)[
|
||||||
"id" => $usr->getId(),
|
"id" => $usr->getId(),
|
||||||
"first_name" => $usr->getFirstName(),
|
"first_name" => $usr->getFirstName(true),
|
||||||
"last_name" => $usr->getLastName(),
|
"last_name" => $usr->getLastName(true),
|
||||||
"is_closed" => false,
|
"is_closed" => $usr->isClosed(),
|
||||||
"can_access_closed" => true,
|
"can_access_closed" => (bool)$usr->canBeViewedBy($this->getUser()),
|
||||||
];
|
];
|
||||||
|
|
||||||
$flds = explode(',', $fields);
|
$flds = explode(',', $fields);
|
||||||
|
$canView = $usr->canBeViewedBy($this->getUser());
|
||||||
foreach($flds as $field) {
|
foreach($flds as $field) {
|
||||||
switch($field) {
|
switch($field) {
|
||||||
case "verified":
|
case "verified":
|
||||||
$response[$i]->verified = intval($usr->isVerified());
|
$response[$i]->verified = intval($usr->isVerified());
|
||||||
break;
|
break;
|
||||||
case "sex":
|
case "sex":
|
||||||
$response[$i]->sex = $usr->isFemale() ? 1 : 2;
|
$response[$i]->sex = $usr->isFemale() ? 1 : ($usr->isNeutral() ? 0 : 2);
|
||||||
break;
|
break;
|
||||||
case "has_photo":
|
case "has_photo":
|
||||||
$response[$i]->has_photo = is_null($usr->getAvatarPhoto()) ? 0 : 1;
|
$response[$i]->has_photo = is_null($usr->getAvatarPhoto()) ? 0 : 1;
|
||||||
|
@ -95,6 +97,12 @@ final class Users extends VKAPIRequestHandler
|
||||||
case "status":
|
case "status":
|
||||||
if($usr->getStatus() != NULL)
|
if($usr->getStatus() != NULL)
|
||||||
$response[$i]->status = $usr->getStatus();
|
$response[$i]->status = $usr->getStatus();
|
||||||
|
|
||||||
|
$audioStatus = $usr->getCurrentAudioStatus();
|
||||||
|
|
||||||
|
if($audioStatus)
|
||||||
|
$response[$i]->status_audio = $audioStatus->toVkApiStruct();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "screen_name":
|
case "screen_name":
|
||||||
if($usr->getShortCode() != NULL)
|
if($usr->getShortCode() != NULL)
|
||||||
|
@ -142,26 +150,102 @@ final class Users extends VKAPIRequestHandler
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
case "music":
|
case "music":
|
||||||
|
if(!$canView) {
|
||||||
|
$response[$i]->music = "secret";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$response[$i]->music = $usr->getFavoriteMusic();
|
$response[$i]->music = $usr->getFavoriteMusic();
|
||||||
break;
|
break;
|
||||||
case "movies":
|
case "movies":
|
||||||
|
if(!$canView) {
|
||||||
|
$response[$i]->movies = "secret";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$response[$i]->movies = $usr->getFavoriteFilms();
|
$response[$i]->movies = $usr->getFavoriteFilms();
|
||||||
break;
|
break;
|
||||||
case "tv":
|
case "tv":
|
||||||
|
if(!$canView) {
|
||||||
|
$response[$i]->tv = "secret";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$response[$i]->tv = $usr->getFavoriteShows();
|
$response[$i]->tv = $usr->getFavoriteShows();
|
||||||
break;
|
break;
|
||||||
case "books":
|
case "books":
|
||||||
|
if(!$canView) {
|
||||||
|
$response[$i]->books = "secret";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$response[$i]->books = $usr->getFavoriteBooks();
|
$response[$i]->books = $usr->getFavoriteBooks();
|
||||||
break;
|
break;
|
||||||
case "city":
|
case "city":
|
||||||
|
if(!$canView) {
|
||||||
|
$response[$i]->city = "Воскресенск";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$response[$i]->city = $usr->getCity();
|
$response[$i]->city = $usr->getCity();
|
||||||
break;
|
break;
|
||||||
case "interests":
|
case "interests":
|
||||||
|
if(!$canView) {
|
||||||
|
$response[$i]->interests = "secret";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$response[$i]->interests = $usr->getInterests();
|
$response[$i]->interests = $usr->getInterests();
|
||||||
break;
|
break;
|
||||||
|
case "quotes":
|
||||||
|
if(!$canView) {
|
||||||
|
$response[$i]->quotes = "secret";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response[$i]->quotes = $usr->getFavoriteQuote();
|
||||||
|
break;
|
||||||
|
case "email":
|
||||||
|
if(!$canView) {
|
||||||
|
$response[$i]->email = "secret@gmail.com";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response[$i]->email = $usr->getContactEmail();
|
||||||
|
break;
|
||||||
|
case "telegram":
|
||||||
|
if(!$canView) {
|
||||||
|
$response[$i]->telegram = "@secret";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response[$i]->telegram = $usr->getTelegram();
|
||||||
|
break;
|
||||||
|
case "about":
|
||||||
|
if(!$canView) {
|
||||||
|
$response[$i]->about = "secret";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response[$i]->about = $usr->getDescription();
|
||||||
|
break;
|
||||||
case "rating":
|
case "rating":
|
||||||
|
if(!$canView) {
|
||||||
|
$response[$i]->rating = 22;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$response[$i]->rating = $usr->getRating();
|
$response[$i]->rating = $usr->getRating();
|
||||||
break;
|
break;
|
||||||
|
case "counters":
|
||||||
|
$response[$i]->counters = (object) [
|
||||||
|
"friends_count" => $usr->getFriendsCount(),
|
||||||
|
"photos_count" => (new Albums)->getUserPhotosCount($usr),
|
||||||
|
"videos_count" => (new Videos)->getUserVideosCount($usr),
|
||||||
|
"audios_count" => (new Audios)->getUserCollectionSize($usr),
|
||||||
|
"notes_count" => (new Notes)->getUserNotesCount($usr)
|
||||||
|
];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +269,14 @@ final class Users extends VKAPIRequestHandler
|
||||||
|
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
|
$user = $users->get($user_id);
|
||||||
|
|
||||||
|
if(!$user || $user->isDeleted())
|
||||||
|
$this->fail(14, "Invalid user");
|
||||||
|
|
||||||
|
if(!$user->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
foreach($users->get($user_id)->getFollowers($offset, $count) as $follower)
|
foreach($users->get($user_id)->getFollowers($offset, $count) as $follower)
|
||||||
$followers[] = $follower->getId();
|
$followers[] = $follower->getId();
|
||||||
|
|
||||||
|
@ -277,6 +369,7 @@ final class Users extends VKAPIRequestHandler
|
||||||
"fav_shows" => !empty($fav_shows) ? $fav_shows : NULL,
|
"fav_shows" => !empty($fav_shows) ? $fav_shows : NULL,
|
||||||
"fav_books" => !empty($fav_books) ? $fav_books : NULL,
|
"fav_books" => !empty($fav_books) ? $fav_books : NULL,
|
||||||
"fav_quotes" => !empty($fav_quotes) ? $fav_quotes : NULL,
|
"fav_quotes" => !empty($fav_quotes) ? $fav_quotes : NULL,
|
||||||
|
"doNotSearchPrivate" => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
$find = $users->find($q, $parameters, $sortg);
|
$find = $users->find($q, $parameters, $sortg);
|
||||||
|
@ -289,4 +382,26 @@ final class Users extends VKAPIRequestHandler
|
||||||
"items" => $this->get(implode(',', $array), $nfilds, $offset, $count)
|
"items" => $this->get(implode(',', $array), $nfilds, $offset, $count)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function report(int $user_id, string $type = "spam", string $comment = "")
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
if($user_id == $this->getUser()->getId())
|
||||||
|
$this->fail(12, "Can't report yourself.");
|
||||||
|
|
||||||
|
if(sizeof(iterator_to_array((new Reports)->getDuplicates("user", $user_id, NULL, $this->getUser()->getId()))) > 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
$report = new Report;
|
||||||
|
$report->setUser_id($this->getUser()->getId());
|
||||||
|
$report->setTarget_id($user_id);
|
||||||
|
$report->setType("user");
|
||||||
|
$report->setReason($comment);
|
||||||
|
$report->setCreated(time());
|
||||||
|
$report->save();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ abstract class VKAPIRequestHandler
|
||||||
|
|
||||||
protected function getPlatform(): ?string
|
protected function getPlatform(): ?string
|
||||||
{
|
{
|
||||||
return $this->platform;
|
return $this->platform ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function userAuthorized(): bool
|
protected function userAuthorized(): bool
|
||||||
|
|
|
@ -11,11 +11,11 @@ use openvk\Web\Models\Repositories\Comments as CommentsRepo;
|
||||||
|
|
||||||
final class Video extends VKAPIRequestHandler
|
final class Video extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
function get(int $owner_id, string $videos, int $offset = 0, int $count = 30, int $extended = 0): object
|
function get(int $owner_id, string $videos = "", int $offset = 0, int $count = 30, int $extended = 0): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
if ($videos) {
|
if(!empty($videos)) {
|
||||||
$vids = explode(',', $videos);
|
$vids = explode(',', $videos);
|
||||||
|
|
||||||
foreach($vids as $vid)
|
foreach($vids as $vid)
|
||||||
|
@ -26,7 +26,7 @@ final class Video extends VKAPIRequestHandler
|
||||||
|
|
||||||
$video = (new VideosRepo)->getByOwnerAndVID(intval($id[0]), intval($id[1]));
|
$video = (new VideosRepo)->getByOwnerAndVID(intval($id[0]), intval($id[1]));
|
||||||
if($video) {
|
if($video) {
|
||||||
$items[] = $video->getApiStructure();
|
$items[] = $video->getApiStructure($this->getUser());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,16 +36,22 @@ final class Video extends VKAPIRequestHandler
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
if ($owner_id > 0)
|
if ($owner_id > 0)
|
||||||
$user = (new UsersRepo)->get($owner_id);
|
$user = (new UsersRepo)->get($owner_id);
|
||||||
else
|
else
|
||||||
$this->fail(1, "Not implemented");
|
$this->fail(1, "Not implemented");
|
||||||
|
|
||||||
|
if(!$user || $user->isDeleted())
|
||||||
|
$this->fail(14, "Invalid user");
|
||||||
|
|
||||||
|
if(!$user->getPrivacyPermission('videos.read', $this->getUser()))
|
||||||
|
$this->fail(21, "This user chose to hide his videos.");
|
||||||
|
|
||||||
$videos = (new VideosRepo)->getByUser($user, $offset + 1, $count);
|
$videos = (new VideosRepo)->getByUser($user, $offset + 1, $count);
|
||||||
$videosCount = (new VideosRepo)->getUserVideosCount($user);
|
$videosCount = (new VideosRepo)->getUserVideosCount($user);
|
||||||
|
|
||||||
$items = [];
|
$items = [];
|
||||||
foreach ($videos as $video) {
|
foreach ($videos as $video) {
|
||||||
$items[] = $video->getApiStructure();
|
$items[] = $video->getApiStructure($this->getUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
return (object) [
|
return (object) [
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\VKAPI\Handlers;
|
namespace openvk\VKAPI\Handlers;
|
||||||
use openvk\Web\Models\Entities\User;
|
use openvk\Web\Models\Entities\User;
|
||||||
use openvk\Web\Models\Entities\Notifications\{WallPostNotification, RepostNotification, CommentNotification};
|
use openvk\Web\Models\Entities\Notifications\{PostAcceptedNotification, WallPostNotification, NewSuggestedPostsNotification, RepostNotification, CommentNotification};
|
||||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||||
use openvk\Web\Models\Entities\Club;
|
use openvk\Web\Models\Entities\Club;
|
||||||
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
||||||
|
@ -15,10 +15,12 @@ use openvk\Web\Models\Entities\Video;
|
||||||
use openvk\Web\Models\Repositories\Videos as VideosRepo;
|
use openvk\Web\Models\Repositories\Videos as VideosRepo;
|
||||||
use openvk\Web\Models\Entities\Note;
|
use openvk\Web\Models\Entities\Note;
|
||||||
use openvk\Web\Models\Repositories\Notes as NotesRepo;
|
use openvk\Web\Models\Repositories\Notes as NotesRepo;
|
||||||
|
use openvk\Web\Models\Repositories\Polls as PollsRepo;
|
||||||
|
use openvk\Web\Models\Repositories\Audios as AudiosRepo;
|
||||||
|
|
||||||
final class Wall extends VKAPIRequestHandler
|
final class Wall extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
function get(int $owner_id, string $domain = "", int $offset = 0, int $count = 30, int $extended = 0): object
|
function get(int $owner_id, string $domain = "", int $offset = 0, int $count = 30, int $extended = 0, string $filter = "all"): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
|
@ -27,7 +29,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$items = [];
|
$items = [];
|
||||||
$profiles = [];
|
$profiles = [];
|
||||||
$groups = [];
|
$groups = [];
|
||||||
$cnt = $posts->getPostCountOnUserWall($owner_id);
|
$cnt = 0;
|
||||||
|
|
||||||
if ($owner_id > 0)
|
if ($owner_id > 0)
|
||||||
$wallOnwer = (new UsersRepo)->get($owner_id);
|
$wallOnwer = (new UsersRepo)->get($owner_id);
|
||||||
|
@ -37,11 +39,54 @@ final class Wall extends VKAPIRequestHandler
|
||||||
if ($owner_id > 0)
|
if ($owner_id > 0)
|
||||||
if(!$wallOnwer || $wallOnwer->isDeleted())
|
if(!$wallOnwer || $wallOnwer->isDeleted())
|
||||||
$this->fail(18, "User was deleted or banned");
|
$this->fail(18, "User was deleted or banned");
|
||||||
|
|
||||||
|
if(!$wallOnwer->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
else
|
else
|
||||||
if(!$wallOnwer)
|
if(!$wallOnwer)
|
||||||
$this->fail(15, "Access denied: wall is disabled"); // Don't search for logic here pls
|
$this->fail(15, "Access denied: wall is disabled"); // Don't search for logic here pls
|
||||||
|
|
||||||
foreach($posts->getPostsFromUsersWall($owner_id, 1, $count, $offset) as $post) {
|
$iteratorv;
|
||||||
|
|
||||||
|
switch($filter) {
|
||||||
|
case "all":
|
||||||
|
$iteratorv = $posts->getPostsFromUsersWall($owner_id, 1, $count, $offset);
|
||||||
|
$cnt = $posts->getPostCountOnUserWall($owner_id);
|
||||||
|
break;
|
||||||
|
case "owner":
|
||||||
|
$iteratorv = $posts->getOwnersPostsFromWall($owner_id, 1, $count, $offset);
|
||||||
|
$cnt = $posts->getOwnersCountOnUserWall($owner_id);
|
||||||
|
break;
|
||||||
|
case "others":
|
||||||
|
$iteratorv = $posts->getOthersPostsFromWall($owner_id, 1, $count, $offset);
|
||||||
|
$cnt = $posts->getOthersCountOnUserWall($owner_id);
|
||||||
|
break;
|
||||||
|
case "postponed":
|
||||||
|
$this->fail(42, "Postponed posts are not implemented.");
|
||||||
|
break;
|
||||||
|
case "suggests":
|
||||||
|
if($owner_id < 0) {
|
||||||
|
if($wallOnwer->getWallType() != 2)
|
||||||
|
$this->fail(125, "Group's wall type is open or closed");
|
||||||
|
|
||||||
|
if($wallOnwer->canBeModifiedBy($this->getUser())) {
|
||||||
|
$iteratorv = $posts->getSuggestedPosts($owner_id * -1, 1, $count, $offset);
|
||||||
|
$cnt = $posts->getSuggestedPostsCount($owner_id * -1);
|
||||||
|
} else {
|
||||||
|
$iteratorv = $posts->getSuggestedPostsByUser($owner_id * -1, $this->getUser()->getId(), 1, $count, $offset);
|
||||||
|
$cnt = $posts->getSuggestedPostsCountByUser($owner_id * -1, $this->getUser()->getId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->fail(528, "Suggested posts avaiable only at groups");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$this->fail(254, "Invalid filter");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($iteratorv as $post) {
|
||||||
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
||||||
|
|
||||||
$attachments = [];
|
$attachments = [];
|
||||||
|
@ -55,9 +100,14 @@ final class Wall extends VKAPIRequestHandler
|
||||||
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
||||||
$attachments[] = $this->getApiPoll($attachment, $this->getUser());
|
$attachments[] = $this->getApiPoll($attachment, $this->getUser());
|
||||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Video) {
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Video) {
|
||||||
$attachments[] = $attachment->getApiStructure();
|
$attachments[] = $attachment->getApiStructure($this->getUser());
|
||||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
||||||
$attachments[] = $attachment->toVkApiStruct();
|
$attachments[] = $attachment->toVkApiStruct();
|
||||||
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Audio) {
|
||||||
|
$attachments[] = [
|
||||||
|
"type" => "audio",
|
||||||
|
"audio" => $attachment->toVkApiStruct($this->getUser()),
|
||||||
|
];
|
||||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||||
$repostAttachments = [];
|
$repostAttachments = [];
|
||||||
|
|
||||||
|
@ -97,6 +147,13 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"attachments" => $repostAttachments,
|
"attachments" => $repostAttachments,
|
||||||
"post_source" => $post_source,
|
"post_source" => $post_source,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ($attachment->getVirtualId() > 0)
|
||||||
|
$profiles[] = $attachment->getVirtualId();
|
||||||
|
else
|
||||||
|
$groups[] = $attachment->getVirtualId();
|
||||||
|
if($post->isSigned())
|
||||||
|
$profiles[] = $attachment->getOwner()->getId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,15 +168,26 @@ final class Wall extends VKAPIRequestHandler
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$postType = "post";
|
||||||
|
$signerId = NULL;
|
||||||
|
if($post->getSuggestionType() != 0)
|
||||||
|
$postType = "suggest";
|
||||||
|
|
||||||
|
|
||||||
|
if($post->isSigned()) {
|
||||||
|
$actualAuthor = $post->getOwner(false);
|
||||||
|
$signerId = $actualAuthor->getId();
|
||||||
|
}
|
||||||
|
|
||||||
$items[] = (object)[
|
$items[] = (object)[
|
||||||
"id" => $post->getVirtualId(),
|
"id" => $post->getVirtualId(),
|
||||||
"from_id" => $from_id,
|
"from_id" => $from_id,
|
||||||
"owner_id" => $post->getTargetWall(),
|
"owner_id" => $post->getTargetWall(),
|
||||||
"date" => $post->getPublicationTime()->timestamp(),
|
"date" => $post->getPublicationTime()->timestamp(),
|
||||||
"post_type" => "post",
|
"post_type" => $postType,
|
||||||
"text" => $post->getText(false),
|
"text" => $post->getText(false),
|
||||||
"copy_history" => $repost,
|
"copy_history" => $repost,
|
||||||
"can_edit" => 0, # TODO
|
"can_edit" => $post->canBeEditedBy($this->getUser()),
|
||||||
"can_delete" => $post->canBeDeletedBy($this->getUser()),
|
"can_delete" => $post->canBeDeletedBy($this->getUser()),
|
||||||
"can_pin" => $post->canBePinnedBy($this->getUser()),
|
"can_pin" => $post->canBePinnedBy($this->getUser()),
|
||||||
"can_archive" => false, # TODO MAYBE
|
"can_archive" => false, # TODO MAYBE
|
||||||
|
@ -128,6 +196,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"is_explicit" => $post->isExplicit(),
|
"is_explicit" => $post->isExplicit(),
|
||||||
"attachments" => $attachments,
|
"attachments" => $attachments,
|
||||||
"post_source" => $post_source,
|
"post_source" => $post_source,
|
||||||
|
"signer_id" => $signerId,
|
||||||
"comments" => (object)[
|
"comments" => (object)[
|
||||||
"count" => $post->getCommentsCount(),
|
"count" => $post->getCommentsCount(),
|
||||||
"can_post" => 1
|
"can_post" => 1
|
||||||
|
@ -149,6 +218,9 @@ final class Wall extends VKAPIRequestHandler
|
||||||
else
|
else
|
||||||
$groups[] = $from_id * -1;
|
$groups[] = $from_id * -1;
|
||||||
|
|
||||||
|
if($post->isSigned())
|
||||||
|
$profiles[] = $post->getOwner(false)->getId();
|
||||||
|
|
||||||
$attachments = NULL; # free attachments so it will not clone everythingg
|
$attachments = NULL; # free attachments so it will not clone everythingg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,9 +237,9 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"first_name" => $user->getFirstName(),
|
"first_name" => $user->getFirstName(),
|
||||||
"id" => $user->getId(),
|
"id" => $user->getId(),
|
||||||
"last_name" => $user->getLastName(),
|
"last_name" => $user->getLastName(),
|
||||||
"can_access_closed" => false,
|
"can_access_closed" => (bool)$user->canBeViewedBy($this->getUser()),
|
||||||
"is_closed" => false,
|
"is_closed" => $user->isClosed(),
|
||||||
"sex" => $user->isFemale() ? 1 : 2,
|
"sex" => $user->isFemale() ? 1 : ($user->isNeutral() ? 0 : 2),
|
||||||
"screen_name" => $user->getShortCode(),
|
"screen_name" => $user->getShortCode(),
|
||||||
"photo_50" => $user->getAvatarUrl(),
|
"photo_50" => $user->getAvatarUrl(),
|
||||||
"photo_100" => $user->getAvatarUrl(),
|
"photo_100" => $user->getAvatarUrl(),
|
||||||
|
@ -220,7 +292,11 @@ final class Wall extends VKAPIRequestHandler
|
||||||
foreach($psts as $pst) {
|
foreach($psts as $pst) {
|
||||||
$id = explode("_", $pst);
|
$id = explode("_", $pst);
|
||||||
$post = (new PostsRepo)->getPostById(intval($id[0]), intval($id[1]));
|
$post = (new PostsRepo)->getPostById(intval($id[0]), intval($id[1]));
|
||||||
|
|
||||||
if($post && !$post->isDeleted()) {
|
if($post && !$post->isDeleted()) {
|
||||||
|
if(!$post->canBeViewedBy($this->getUser()))
|
||||||
|
continue;
|
||||||
|
|
||||||
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
||||||
$attachments = [];
|
$attachments = [];
|
||||||
$repost = []; // чел высрал семь сигарет 😳 помянем 🕯
|
$repost = []; // чел высрал семь сигарет 😳 помянем 🕯
|
||||||
|
@ -230,9 +306,14 @@ final class Wall extends VKAPIRequestHandler
|
||||||
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
||||||
$attachments[] = $this->getApiPoll($attachment, $user);
|
$attachments[] = $this->getApiPoll($attachment, $user);
|
||||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Video) {
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Video) {
|
||||||
$attachments[] = $attachment->getApiStructure();
|
$attachments[] = $attachment->getApiStructure($this->getUser());
|
||||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
||||||
$attachments[] = $attachment->toVkApiStruct();
|
$attachments[] = $attachment->toVkApiStruct();
|
||||||
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Audio) {
|
||||||
|
$attachments[] = [
|
||||||
|
"type" => "audio",
|
||||||
|
"audio" => $attachment->toVkApiStruct($this->getUser())
|
||||||
|
];
|
||||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||||
$repostAttachments = [];
|
$repostAttachments = [];
|
||||||
|
|
||||||
|
@ -272,6 +353,13 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"attachments" => $repostAttachments,
|
"attachments" => $repostAttachments,
|
||||||
"post_source" => $post_source,
|
"post_source" => $post_source,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ($attachment->getVirtualId() > 0)
|
||||||
|
$profiles[] = $attachment->getVirtualId();
|
||||||
|
else
|
||||||
|
$groups[] = $attachment->getVirtualId();
|
||||||
|
if($post->isSigned())
|
||||||
|
$profiles[] = $attachment->getOwner()->getId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,15 +374,27 @@ final class Wall extends VKAPIRequestHandler
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# TODO: $post->getVkApiType()
|
||||||
|
$postType = "post";
|
||||||
|
$signerId = NULL;
|
||||||
|
if($post->getSuggestionType() != 0)
|
||||||
|
$postType = "suggest";
|
||||||
|
|
||||||
|
|
||||||
|
if($post->isSigned()) {
|
||||||
|
$actualAuthor = $post->getOwner(false);
|
||||||
|
$signerId = $actualAuthor->getId();
|
||||||
|
}
|
||||||
|
|
||||||
$items[] = (object)[
|
$items[] = (object)[
|
||||||
"id" => $post->getVirtualId(),
|
"id" => $post->getVirtualId(),
|
||||||
"from_id" => $from_id,
|
"from_id" => $from_id,
|
||||||
"owner_id" => $post->getTargetWall(),
|
"owner_id" => $post->getTargetWall(),
|
||||||
"date" => $post->getPublicationTime()->timestamp(),
|
"date" => $post->getPublicationTime()->timestamp(),
|
||||||
"post_type" => "post",
|
"post_type" => $postType,
|
||||||
"text" => $post->getText(false),
|
"text" => $post->getText(false),
|
||||||
"copy_history" => $repost,
|
"copy_history" => $repost,
|
||||||
"can_edit" => 0, # TODO
|
"can_edit" => $post->canBeEditedBy($this->getUser()),
|
||||||
"can_delete" => $post->canBeDeletedBy($user),
|
"can_delete" => $post->canBeDeletedBy($user),
|
||||||
"can_pin" => $post->canBePinnedBy($user),
|
"can_pin" => $post->canBePinnedBy($user),
|
||||||
"can_archive" => false, # TODO MAYBE
|
"can_archive" => false, # TODO MAYBE
|
||||||
|
@ -302,6 +402,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"is_pinned" => $post->isPinned(),
|
"is_pinned" => $post->isPinned(),
|
||||||
"is_explicit" => $post->isExplicit(),
|
"is_explicit" => $post->isExplicit(),
|
||||||
"post_source" => $post_source,
|
"post_source" => $post_source,
|
||||||
|
"signer_id" => $signerId,
|
||||||
"attachments" => $attachments,
|
"attachments" => $attachments,
|
||||||
"comments" => (object)[
|
"comments" => (object)[
|
||||||
"count" => $post->getCommentsCount(),
|
"count" => $post->getCommentsCount(),
|
||||||
|
@ -324,6 +425,9 @@ final class Wall extends VKAPIRequestHandler
|
||||||
else
|
else
|
||||||
$groups[] = $from_id * -1;
|
$groups[] = $from_id * -1;
|
||||||
|
|
||||||
|
if($post->isSigned())
|
||||||
|
$profiles[] = $post->getOwner(false)->getId();
|
||||||
|
|
||||||
$attachments = NULL; # free attachments so it will not clone everything
|
$attachments = NULL; # free attachments so it will not clone everything
|
||||||
$repost = NULL; # same
|
$repost = NULL; # same
|
||||||
}
|
}
|
||||||
|
@ -338,19 +442,28 @@ final class Wall extends VKAPIRequestHandler
|
||||||
|
|
||||||
foreach($profiles as $prof) {
|
foreach($profiles as $prof) {
|
||||||
$user = (new UsersRepo)->get($prof);
|
$user = (new UsersRepo)->get($prof);
|
||||||
$profilesFormatted[] = (object)[
|
if($user) {
|
||||||
"first_name" => $user->getFirstName(),
|
$profilesFormatted[] = (object)[
|
||||||
"id" => $user->getId(),
|
"first_name" => $user->getFirstName(),
|
||||||
"last_name" => $user->getLastName(),
|
"id" => $user->getId(),
|
||||||
"can_access_closed" => false,
|
"last_name" => $user->getLastName(),
|
||||||
"is_closed" => false,
|
"can_access_closed" => (bool)$user->canBeViewedBy($this->getUser()),
|
||||||
"sex" => $user->isFemale() ? 1 : 2,
|
"is_closed" => $user->isClosed(),
|
||||||
"screen_name" => $user->getShortCode(),
|
"sex" => $user->isFemale() ? 1 : 2,
|
||||||
"photo_50" => $user->getAvatarUrl(),
|
"screen_name" => $user->getShortCode(),
|
||||||
"photo_100" => $user->getAvatarUrl(),
|
"photo_50" => $user->getAvatarUrl(),
|
||||||
"online" => $user->isOnline(),
|
"photo_100" => $user->getAvatarUrl(),
|
||||||
"verified" => $user->isVerified()
|
"online" => $user->isOnline(),
|
||||||
];
|
"verified" => $user->isVerified()
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$profilesFormatted[] = (object)[
|
||||||
|
"id" => (int) $prof,
|
||||||
|
"first_name" => "DELETED",
|
||||||
|
"last_name" => "",
|
||||||
|
"deactivated" => "deleted"
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($groups as $g) {
|
foreach($groups as $g) {
|
||||||
|
@ -379,7 +492,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function post(string $owner_id, string $message = "", int $from_group = 0, int $signed = 0, string $attachments = ""): object
|
function post(string $owner_id, string $message = "", int $from_group = 0, int $signed = 0, string $attachments = "", int $post_id = 0): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
@ -389,7 +502,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$wallOwner = ($owner_id > 0 ? (new UsersRepo)->get($owner_id) : (new ClubsRepo)->get($owner_id * -1))
|
$wallOwner = ($owner_id > 0 ? (new UsersRepo)->get($owner_id) : (new ClubsRepo)->get($owner_id * -1))
|
||||||
?? $this->fail(18, "User was deleted or banned");
|
?? $this->fail(18, "User was deleted or banned");
|
||||||
if($owner_id > 0)
|
if($owner_id > 0)
|
||||||
$canPost = $wallOwner->getPrivacyPermission("wall.write", $this->getUser());
|
$canPost = $wallOwner->getPrivacyPermission("wall.write", $this->getUser()) && $wallOwner->canBeViewedBy($this->getUser());
|
||||||
else if($owner_id < 0)
|
else if($owner_id < 0)
|
||||||
if($wallOwner->canBeModifiedBy($this->getUser()))
|
if($wallOwner->canBeModifiedBy($this->getUser()))
|
||||||
$canPost = true;
|
$canPost = true;
|
||||||
|
@ -400,6 +513,46 @@ final class Wall extends VKAPIRequestHandler
|
||||||
|
|
||||||
if($canPost == false) $this->fail(15, "Access denied");
|
if($canPost == false) $this->fail(15, "Access denied");
|
||||||
|
|
||||||
|
if($post_id > 0) {
|
||||||
|
if($owner_id > 0)
|
||||||
|
$this->fail(62, "Suggested posts available only at groups");
|
||||||
|
|
||||||
|
$post = (new PostsRepo)->getPostById($owner_id, $post_id, true);
|
||||||
|
|
||||||
|
if(!$post || $post->isDeleted())
|
||||||
|
$this->fail(32, "Invald post");
|
||||||
|
|
||||||
|
if($post->getSuggestionType() == 0)
|
||||||
|
$this->fail(20, "Post is not suggested");
|
||||||
|
|
||||||
|
if($post->getSuggestionType() == 2)
|
||||||
|
$this->fail(16, "Post is declined");
|
||||||
|
|
||||||
|
if(!$post->canBePinnedBy($this->getUser()))
|
||||||
|
$this->fail(51, "Access denied");
|
||||||
|
|
||||||
|
$author = $post->getOwner();
|
||||||
|
$flags = 0;
|
||||||
|
$flags |= 0b10000000;
|
||||||
|
|
||||||
|
if($signed == 1)
|
||||||
|
$flags |= 0b01000000;
|
||||||
|
|
||||||
|
$post->setSuggested(0);
|
||||||
|
$post->setCreated(time());
|
||||||
|
$post->setFlags($flags);
|
||||||
|
|
||||||
|
if(!empty($message) && iconv_strlen($message) > 0)
|
||||||
|
$post->setContent($message);
|
||||||
|
|
||||||
|
$post->save();
|
||||||
|
|
||||||
|
if($author->getId() != $this->getUser()->getId())
|
||||||
|
(new PostAcceptedNotification($author, $post, $post->getWallOwner()))->emit();
|
||||||
|
|
||||||
|
return (object)["post_id" => $post->getVirtualId()];
|
||||||
|
}
|
||||||
|
|
||||||
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
|
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
|
||||||
if($wallOwner instanceof Club && $from_group == 1 && $signed != 1 && $anon) {
|
if($wallOwner instanceof Club && $from_group == 1 && $signed != 1 && $anon) {
|
||||||
$manager = $wallOwner->getManager($this->getUser());
|
$manager = $wallOwner->getManager($this->getUser());
|
||||||
|
@ -428,11 +581,16 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$post->setContent($message);
|
$post->setContent($message);
|
||||||
$post->setFlags($flags);
|
$post->setFlags($flags);
|
||||||
$post->setApi_Source_Name($this->getPlatform());
|
$post->setApi_Source_Name($this->getPlatform());
|
||||||
|
|
||||||
|
if($owner_id < 0 && !$wallOwner->canBeModifiedBy($this->getUser()) && $wallOwner->getWallType() == 2)
|
||||||
|
$post->setSuggested(1);
|
||||||
|
|
||||||
$post->save();
|
$post->save();
|
||||||
} catch(\LogicException $ex) {
|
} catch(\LogicException $ex) {
|
||||||
$this->fail(100, "One of the parameters specified was missing or invalid");
|
$this->fail(100, "One of the parameters specified was missing or invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# TODO use parseAttachments
|
||||||
if(!empty($attachments)) {
|
if(!empty($attachments)) {
|
||||||
$attachmentsArr = explode(",", $attachments);
|
$attachmentsArr = explode(",", $attachments);
|
||||||
# Аттачи такого вида: [тип][id владельца]_[id вложения]
|
# Аттачи такого вида: [тип][id владельца]_[id вложения]
|
||||||
|
@ -440,6 +598,10 @@ final class Wall extends VKAPIRequestHandler
|
||||||
|
|
||||||
if(sizeof($attachmentsArr) > 10)
|
if(sizeof($attachmentsArr) > 10)
|
||||||
$this->fail(50, "Error: too many attachments");
|
$this->fail(50, "Error: 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");
|
||||||
|
|
||||||
foreach($attachmentsArr as $attac) {
|
foreach($attachmentsArr as $attac) {
|
||||||
$attachmentType = NULL;
|
$attachmentType = NULL;
|
||||||
|
@ -450,6 +612,11 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$attachmentType = "video";
|
$attachmentType = "video";
|
||||||
elseif(str_contains($attac, "note"))
|
elseif(str_contains($attac, "note"))
|
||||||
$attachmentType = "note";
|
$attachmentType = "note";
|
||||||
|
elseif(str_contains($attac, "poll"))
|
||||||
|
$attachmentType = "poll";
|
||||||
|
elseif(str_contains($attac, "audio"))
|
||||||
|
$attachmentType = "audio";
|
||||||
|
|
||||||
else
|
else
|
||||||
$this->fail(205, "Unknown attachment type");
|
$this->fail(205, "Unknown attachment type");
|
||||||
|
|
||||||
|
@ -483,6 +650,21 @@ final class Wall extends VKAPIRequestHandler
|
||||||
if(!$attacc->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
if(!$attacc->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
||||||
$this->fail(11, "Access to note denied");
|
$this->fail(11, "Access to note denied");
|
||||||
|
|
||||||
|
$post->attach($attacc);
|
||||||
|
} elseif($attachmentType == "poll") {
|
||||||
|
$attacc = (new PollsRepo)->get($attachmentId);
|
||||||
|
|
||||||
|
if(!$attacc || $attacc->isDeleted())
|
||||||
|
$this->fail(100, "Poll does not exist");
|
||||||
|
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||||
|
$this->fail(43, "You do not have access to this poll");
|
||||||
|
|
||||||
|
$post->attach($attacc);
|
||||||
|
} elseif($attachmentType == "audio") {
|
||||||
|
$attacc = (new AudiosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||||
|
if(!$attacc || $attacc->isDeleted())
|
||||||
|
$this->fail(100, "Audio does not exist");
|
||||||
|
|
||||||
$post->attach($attacc);
|
$post->attach($attacc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -491,6 +673,22 @@ final class Wall extends VKAPIRequestHandler
|
||||||
if($wall > 0 && $wall !== $this->user->identity->getId())
|
if($wall > 0 && $wall !== $this->user->identity->getId())
|
||||||
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
||||||
|
|
||||||
|
if($owner_id < 0 && !$wallOwner->canBeModifiedBy($this->getUser()) && $wallOwner->getWallType() == 2) {
|
||||||
|
$suggsCount = (new PostsRepo)->getSuggestedPostsCount($wallOwner->getId());
|
||||||
|
|
||||||
|
if($suggsCount % 10 == 0) {
|
||||||
|
$managers = $wallOwner->getManagers();
|
||||||
|
$owner = $wallOwner->getOwner();
|
||||||
|
(new NewSuggestedPostsNotification($owner, $wallOwner))->emit();
|
||||||
|
|
||||||
|
foreach($managers as $manager) {
|
||||||
|
(new NewSuggestedPostsNotification($manager->getUser(), $wallOwner))->emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object)["post_id" => "on_view"];
|
||||||
|
}
|
||||||
|
|
||||||
return (object)["post_id" => $post->getVirtualId()];
|
return (object)["post_id" => $post->getVirtualId()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -505,6 +703,9 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$post = (new PostsRepo)->getPostById((int) $postArray[1], (int) $postArray[2]);
|
$post = (new PostsRepo)->getPostById((int) $postArray[1], (int) $postArray[2]);
|
||||||
if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid");
|
if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid");
|
||||||
|
|
||||||
|
if(!$post->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
$nPost = new Post;
|
$nPost = new Post;
|
||||||
$nPost->setOwner($this->user->getId());
|
$nPost->setOwner($this->user->getId());
|
||||||
|
|
||||||
|
@ -543,6 +744,9 @@ final class Wall extends VKAPIRequestHandler
|
||||||
|
|
||||||
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
|
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
|
||||||
if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid");
|
if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid");
|
||||||
|
|
||||||
|
if(!$post->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
$comments = (new CommentsRepo)->getCommentsByTarget($post, $offset+1, $count, $sort == "desc" ? "DESC" : "ASC");
|
$comments = (new CommentsRepo)->getCommentsByTarget($post, $offset+1, $count, $sort == "desc" ? "DESC" : "ASC");
|
||||||
|
|
||||||
|
@ -562,6 +766,11 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$attachments[] = $this->getApiPhoto($attachment);
|
$attachments[] = $this->getApiPhoto($attachment);
|
||||||
} elseif($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
} elseif($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
||||||
$attachments[] = $attachment->toVkApiStruct();
|
$attachments[] = $attachment->toVkApiStruct();
|
||||||
|
} elseif($attachment instanceof \openvk\Web\Models\Entities\Audio) {
|
||||||
|
$attachments[] = [
|
||||||
|
"type" => "audio",
|
||||||
|
"audio" => $attachment->toVkApiStruct($this->getUser()),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,6 +830,12 @@ final class Wall extends VKAPIRequestHandler
|
||||||
|
|
||||||
$comment = (new CommentsRepo)->get($comment_id); # один хуй айди всех комментов общий
|
$comment = (new CommentsRepo)->get($comment_id); # один хуй айди всех комментов общий
|
||||||
|
|
||||||
|
if(!$comment || $comment->isDeleted())
|
||||||
|
$this->fail(100, "Invalid comment");
|
||||||
|
|
||||||
|
if(!$comment->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
$profiles = [];
|
$profiles = [];
|
||||||
|
|
||||||
$attachments = [];
|
$attachments = [];
|
||||||
|
@ -628,6 +843,11 @@ final class Wall extends VKAPIRequestHandler
|
||||||
foreach($comment->getChildren() as $attachment) {
|
foreach($comment->getChildren() as $attachment) {
|
||||||
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
|
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||||
$attachments[] = $this->getApiPhoto($attachment);
|
$attachments[] = $this->getApiPhoto($attachment);
|
||||||
|
} elseif($attachment instanceof \openvk\Web\Models\Entities\Audio) {
|
||||||
|
$attachments[] = [
|
||||||
|
"type" => "audio",
|
||||||
|
"audio" => $attachment->toVkApiStruct($this->getUser()),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,6 +902,9 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
|
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
|
||||||
if(!$post || $post->isDeleted()) $this->fail(100, "Invalid post");
|
if(!$post || $post->isDeleted()) $this->fail(100, "Invalid post");
|
||||||
|
|
||||||
|
if(!$post->canBeViewedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
if($post->getTargetWall() < 0)
|
if($post->getTargetWall() < 0)
|
||||||
$club = (new ClubsRepo)->get(abs($post->getTargetWall()));
|
$club = (new ClubsRepo)->get(abs($post->getTargetWall()));
|
||||||
|
|
||||||
|
@ -719,6 +942,8 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$attachmentType = "photo";
|
$attachmentType = "photo";
|
||||||
elseif(str_contains($attac, "video"))
|
elseif(str_contains($attac, "video"))
|
||||||
$attachmentType = "video";
|
$attachmentType = "video";
|
||||||
|
elseif(str_contains($attac, "audio"))
|
||||||
|
$attachmentType = "audio";
|
||||||
else
|
else
|
||||||
$this->fail(205, "Unknown attachment type");
|
$this->fail(205, "Unknown attachment type");
|
||||||
|
|
||||||
|
@ -744,6 +969,12 @@ final class Wall extends VKAPIRequestHandler
|
||||||
if(!$attacc->getOwner()->getPrivacyPermission('videos.read', $this->getUser()))
|
if(!$attacc->getOwner()->getPrivacyPermission('videos.read', $this->getUser()))
|
||||||
$this->fail(11, "Access to video denied");
|
$this->fail(11, "Access to video denied");
|
||||||
|
|
||||||
|
$comment->attach($attacc);
|
||||||
|
} elseif($attachmentType == "audio") {
|
||||||
|
$attacc = (new AudiosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||||
|
if(!$attacc || $attacc->isDeleted())
|
||||||
|
$this->fail(100, "Audio does not exist");
|
||||||
|
|
||||||
$comment->attach($attacc);
|
$comment->attach($attacc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -772,12 +1003,125 @@ final class Wall extends VKAPIRequestHandler
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function delete(int $owner_id, int $post_id)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$post = (new PostsRepo)->getPostById($owner_id, $post_id, true);
|
||||||
|
if(!$post || $post->isDeleted())
|
||||||
|
$this->fail(583, "Invalid post");
|
||||||
|
|
||||||
|
$wallOwner = $post->getWallOwner();
|
||||||
|
|
||||||
|
if($post->getTargetWall() < 0 && !$post->getWallOwner()->canBeModifiedBy($this->getUser()) && $post->getWallOwner()->getWallType() != 1 && $post->getSuggestionType() == 0)
|
||||||
|
$this->fail(12, "Access denied: you can't delete your accepted post.");
|
||||||
|
|
||||||
|
if($post->getOwnerPost() == $this->getUser()->getId() || $post->getTargetWall() == $this->getUser()->getId() || $owner_id < 0 && $wallOwner->canBeModifiedBy($this->getUser())) {
|
||||||
|
$post->unwire();
|
||||||
|
$post->delete();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
$this->fail(15, "Access denied");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit(int $owner_id, int $post_id, string $message = "", string $attachments = "") {
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
if(!empty($message))
|
||||||
|
$post->setContent($message);
|
||||||
|
|
||||||
|
$post->setEdited(time());
|
||||||
|
$post->save(true);
|
||||||
|
|
||||||
|
# todo добавить такое в веб версию
|
||||||
|
if(!empty($attachments)) {
|
||||||
|
$attachs = parseAttachments($attachments);
|
||||||
|
$newAttachmentsCount = sizeof($attachs);
|
||||||
|
|
||||||
|
$postsAttachments = iterator_to_array($post->getChildren());
|
||||||
|
|
||||||
|
if(sizeof($postsAttachments) >= 10)
|
||||||
|
$this->fail(15, "Post have too many attachments");
|
||||||
|
|
||||||
|
if(($newAttachmentsCount + sizeof($postsAttachments)) > 10)
|
||||||
|
$this->fail(158, "Post will have too many attachments");
|
||||||
|
|
||||||
|
foreach($attachs as $attach) {
|
||||||
|
if($attach && !$attach->isDeleted())
|
||||||
|
$post->attach($attach);
|
||||||
|
else
|
||||||
|
$this->fail(52, "One of the attachments is invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ["post_id" => $post->getVirtualId()];
|
||||||
|
}
|
||||||
|
|
||||||
|
function editComment(int $comment_id, int $owner_id = 0, string $message = "", string $attachments = "") {
|
||||||
|
$this->requireUser();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$comment = (new CommentsRepo)->get($comment_id);
|
||||||
|
|
||||||
|
if(empty($message) && empty($attachments))
|
||||||
|
$this->fail(100, "Required parameter 'message' missing.");
|
||||||
|
|
||||||
|
if(!$comment || $comment->isDeleted())
|
||||||
|
$this->fail(102, "Invalid comment");
|
||||||
|
|
||||||
|
if(!$comment->canBeEditedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access to editing comment denied");
|
||||||
|
|
||||||
|
if(!empty($message))
|
||||||
|
$comment->setContent($message);
|
||||||
|
|
||||||
|
$comment->setEdited(time());
|
||||||
|
$comment->save(true);
|
||||||
|
|
||||||
|
if(!empty($attachments)) {
|
||||||
|
$attachs = parseAttachments($attachments);
|
||||||
|
$newAttachmentsCount = sizeof($attachs);
|
||||||
|
|
||||||
|
$postsAttachments = iterator_to_array($comment->getChildren());
|
||||||
|
|
||||||
|
if(sizeof($postsAttachments) >= 10)
|
||||||
|
$this->fail(15, "Post have too many attachments");
|
||||||
|
|
||||||
|
if(($newAttachmentsCount + sizeof($postsAttachments)) > 10)
|
||||||
|
$this->fail(158, "Post will have too many attachments");
|
||||||
|
|
||||||
|
foreach($attachs as $attach) {
|
||||||
|
if($attach && !$attach->isDeleted())
|
||||||
|
$comment->attach($attach);
|
||||||
|
else
|
||||||
|
$this->fail(52, "One of the attachments is invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
private function getApiPhoto($attachment) {
|
private function getApiPhoto($attachment) {
|
||||||
return [
|
return [
|
||||||
"type" => "photo",
|
"type" => "photo",
|
||||||
"photo" => [
|
"photo" => [
|
||||||
"album_id" => $attachment->getAlbum() ? $attachment->getAlbum()->getId() : NULL,
|
"album_id" => $attachment->getAlbum() ? $attachment->getAlbum()->getId() : 0,
|
||||||
"date" => $attachment->getPublicationTime()->timestamp(),
|
"date" => $attachment->getPublicationTime()->timestamp(),
|
||||||
"id" => $attachment->getVirtualId(),
|
"id" => $attachment->getVirtualId(),
|
||||||
"owner_id" => $attachment->getOwner()->getId(),
|
"owner_id" => $attachment->getOwner()->getId(),
|
||||||
|
|
|
@ -48,7 +48,7 @@ class APIToken extends RowModel
|
||||||
$this->delete();
|
$this->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
function save(): void
|
function save(?bool $log = false): void
|
||||||
{
|
{
|
||||||
if(is_null($this->getRecord()))
|
if(is_null($this->getRecord()))
|
||||||
$this->stateChanges("secret", bin2hex(openssl_random_pseudo_bytes(36)));
|
$this->stateChanges("secret", bin2hex(openssl_random_pseudo_bytes(36)));
|
||||||
|
|
|
@ -67,6 +67,21 @@ class Album extends MediaCollection
|
||||||
return $this->has($photo);
|
return $this->has($photo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canBeViewedBy(?User $user = NULL): bool
|
||||||
|
{
|
||||||
|
if($this->isDeleted()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$owner = $this->getOwner();
|
||||||
|
|
||||||
|
if(get_class($owner) == "openvk\\Web\\Models\\Entities\\User") {
|
||||||
|
return $owner->canBeViewedBy($user) && $owner->getPrivacyPermission('photos.read', $user);
|
||||||
|
} else {
|
||||||
|
return $owner->canBeViewedBy($user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function toVkApiStruct(?User $user = NULL, bool $need_covers = false, bool $photo_sizes = false): object
|
function toVkApiStruct(?User $user = NULL, bool $need_covers = false, bool $photo_sizes = false): object
|
||||||
{
|
{
|
||||||
$res = (object) [];
|
$res = (object) [];
|
||||||
|
|
469
Web/Models/Entities/Audio.php
Normal file
469
Web/Models/Entities/Audio.php
Normal file
|
@ -0,0 +1,469 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use openvk\Web\Util\Shell\Exceptions\UnknownCommandException;
|
||||||
|
use openvk\Web\Util\Shell\Shell;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method setName(string)
|
||||||
|
* @method setPerformer(string)
|
||||||
|
* @method setLyrics(string)
|
||||||
|
* @method setExplicit(bool)
|
||||||
|
*/
|
||||||
|
class Audio extends Media
|
||||||
|
{
|
||||||
|
protected $tableName = "audios";
|
||||||
|
protected $fileExtension = "mpd";
|
||||||
|
|
||||||
|
# Taken from winamp :D
|
||||||
|
const genres = [
|
||||||
|
'Blues','Big Band','Classic Rock','Chorus','Country','Easy Listening','Dance','Acoustic','Disco','Humour','Funk','Speech','Grunge','Chanson','Hip-Hop','Opera','Jazz','Chamber Music','Metal','Sonata','New Age','Symphony','Oldies','Booty Bass','Other','Primus','Pop','Porn Groove','R&B','Satire','Rap','Slow Jam','Reggae','Club','Rock','Tango','Techno','Samba','Industrial','Folklore','Alternative','Ballad','Ska','Power Ballad','Death Metal','Rhythmic Soul','Pranks','Freestyle','Soundtrack','Duet','Euro-Techno','Punk Rock','Ambient','Drum Solo','Trip-Hop','A Cappella','Vocal','Euro-House','Jazz+Funk','Dance Hall','Fusion','Goa','Trance','Drum & Bass','Classical','Club-House','Instrumental','Hardcore','Acid','Terror','House','Indie','Game','BritPop','Sound Clip','Negerpunk','Gospel','Polsk Punk','Noise','Beat','AlternRock','Christian Gangsta Rap','Bass','Heavy Metal','Soul','Black Metal','Punk','Crossover','Space','Contemporary Christian','Meditative','Christian Rock','Instrumental Pop','Merengue','Instrumental Rock','Salsa','Ethnic','Thrash Metal','Gothic','Anime','Darkwave','JPop','Techno-Industrial','Synthpop','Electronic','Abstract','Pop-Folk','Art Rock','Eurodance','Baroque','Dream','Bhangra','Southern Rock','Big Beat','Comedy','Breakbeat','Cult','Chillout','Gangsta Rap','Downtempo','Top 40','Dub','Christian Rap','EBM','Pop / Funk','Eclectic','Jungle','Electro','Native American','Electroclash','Cabaret','Emo','New Wave','Experimental','Psychedelic','Garage','Rave','Global','Showtunes','IDM','Trailer','Illbient','Lo-Fi','Industro-Goth','Tribal','Jam Band','Acid Punk','Krautrock','Acid Jazz','Leftfield','Polka','Lounge','Retro','Math Rock','Musical','New Romantic','Rock & Roll','Nu-Breakz','Hard Rock','Post-Punk','Folk','Post-Rock','Folk-Rock','Psytrance','National Folk','Shoegaze','Swing','Space Rock','Fast Fusion','Trop Rock','Bebob','World Music','Latin','Neoclassical','Revival','Audiobook','Celtic','Audio Theatre','Bluegrass','Neue Deutsche Welle','Avantgarde','Podcast','Gothic Rock','Indie Rock','Progressive Rock','G-Funk','Psychedelic Rock','Dubstep','Symphonic Rock','Garage Rock','Slow Rock','Psybient','Psychobilly','Touhou'
|
||||||
|
];
|
||||||
|
|
||||||
|
# Taken from: https://web.archive.org/web/20220322153107/https://dev.vk.com/reference/objects/audio-genres
|
||||||
|
const vkGenres = [
|
||||||
|
"Rock" => 1,
|
||||||
|
"Pop" => 2,
|
||||||
|
"Rap" => 3,
|
||||||
|
"Hip-Hop" => 3, # VK API lists №3 as Rap & Hip-Hop, but these genres are distinct in OpenVK
|
||||||
|
"Easy Listening" => 4,
|
||||||
|
"House" => 5,
|
||||||
|
"Dance" => 5,
|
||||||
|
"Instrumental" => 6,
|
||||||
|
"Metal" => 7,
|
||||||
|
"Alternative" => 21,
|
||||||
|
"Dubstep" => 8,
|
||||||
|
"Jazz" => 1001,
|
||||||
|
"Blues" => 1001,
|
||||||
|
"Drum & Bass" => 10,
|
||||||
|
"Trance" => 11,
|
||||||
|
"Chanson" => 12,
|
||||||
|
"Ethnic" => 13,
|
||||||
|
"Acoustic" => 14,
|
||||||
|
"Vocal" => 14,
|
||||||
|
"Reggae" => 15,
|
||||||
|
"Classical" => 16,
|
||||||
|
"Indie Pop" => 17,
|
||||||
|
"Speech" => 19,
|
||||||
|
"Disco" => 22,
|
||||||
|
"Other" => 18,
|
||||||
|
];
|
||||||
|
|
||||||
|
private function fileLength(string $filename): int
|
||||||
|
{
|
||||||
|
if(!Shell::commandAvailable("ffmpeg") || !Shell::commandAvailable("ffprobe"))
|
||||||
|
throw new \Exception();
|
||||||
|
|
||||||
|
$error = NULL;
|
||||||
|
$streams = Shell::ffprobe("-i", $filename, "-show_streams", "-select_streams a", "-loglevel error")->execute($error);
|
||||||
|
if($error !== 0)
|
||||||
|
throw new \DomainException("$filename is not recognized as media container");
|
||||||
|
else if(empty($streams) || ctype_space($streams))
|
||||||
|
throw new \DomainException("$filename does not contain any audio streams");
|
||||||
|
|
||||||
|
$vstreams = Shell::ffprobe("-i", $filename, "-show_streams", "-select_streams v", "-loglevel error")->execute($error);
|
||||||
|
|
||||||
|
# check if audio has cover (attached_pic)
|
||||||
|
preg_match("%attached_pic=([0-1])%", $vstreams, $hasCover);
|
||||||
|
if(!empty($vstreams) && !ctype_space($vstreams) && ((int)($hasCover[1]) !== 1))
|
||||||
|
throw new \DomainException("$filename is a video");
|
||||||
|
|
||||||
|
$durations = [];
|
||||||
|
preg_match_all('%duration=([0-9\.]++)%', $streams, $durations);
|
||||||
|
if(sizeof($durations[1]) === 0)
|
||||||
|
throw new \DomainException("$filename does not contain any meaningful audio streams");
|
||||||
|
|
||||||
|
$length = 0;
|
||||||
|
foreach($durations[1] as $duration) {
|
||||||
|
$duration = floatval($duration);
|
||||||
|
if($duration < 1.0 || $duration > 65536.0)
|
||||||
|
throw new \DomainException("$filename does not contain any meaningful audio streams");
|
||||||
|
else
|
||||||
|
$length = max($length, $duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) round($length, 0, PHP_ROUND_HALF_EVEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
protected function saveFile(string $filename, string $hash): bool
|
||||||
|
{
|
||||||
|
$duration = $this->fileLength($filename);
|
||||||
|
|
||||||
|
$kid = openssl_random_pseudo_bytes(16);
|
||||||
|
$key = openssl_random_pseudo_bytes(16);
|
||||||
|
$tok = openssl_random_pseudo_bytes(28);
|
||||||
|
$ss = ceil($duration / 15);
|
||||||
|
|
||||||
|
$this->stateChanges("kid", $kid);
|
||||||
|
$this->stateChanges("key", $key);
|
||||||
|
$this->stateChanges("token", $tok);
|
||||||
|
$this->stateChanges("segment_size", $ss);
|
||||||
|
$this->stateChanges("length", $duration);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$args = [
|
||||||
|
str_replace("enabled", "available", OPENVK_ROOT),
|
||||||
|
str_replace("enabled", "available", $this->getBaseDir()),
|
||||||
|
$hash,
|
||||||
|
$filename,
|
||||||
|
|
||||||
|
bin2hex($kid),
|
||||||
|
bin2hex($key),
|
||||||
|
bin2hex($tok),
|
||||||
|
$ss,
|
||||||
|
];
|
||||||
|
|
||||||
|
if(Shell::isPowershell()) {
|
||||||
|
Shell::powershell("-executionpolicy bypass", "-File", __DIR__ . "/../shell/processAudio.ps1", ...$args)
|
||||||
|
->start();
|
||||||
|
} else {
|
||||||
|
Shell::bash(__DIR__ . "/../shell/processAudio.sh", ...$args) // Pls workkkkk
|
||||||
|
->start(); // idk, not tested :")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Wait until processAudio will consume the file
|
||||||
|
$start = time();
|
||||||
|
while(file_exists($filename))
|
||||||
|
if(time() - $start > 5)
|
||||||
|
throw new \RuntimeException("Timed out waiting FFMPEG");
|
||||||
|
|
||||||
|
} catch(UnknownCommandException $ucex) {
|
||||||
|
exit(OPENVK_ROOT_CONF["openvk"]["debug"] ? "bash/pwsh is not installed" : VIDEOS_FRIENDLY_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTitle(): string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPerformer(): string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->performer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getName(): string
|
||||||
|
{
|
||||||
|
return $this->getPerformer() . " — " . $this->getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGenre(): ?string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->genre;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLyrics(): ?string
|
||||||
|
{
|
||||||
|
return !is_null($this->getRecord()->lyrics) ? htmlspecialchars($this->getRecord()->lyrics, ENT_DISALLOWED | ENT_XHTML) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLength(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFormattedLength(): string
|
||||||
|
{
|
||||||
|
$len = $this->getLength();
|
||||||
|
$mins = floor($len / 60);
|
||||||
|
$secs = $len - ($mins * 60);
|
||||||
|
|
||||||
|
return (
|
||||||
|
str_pad((string) $mins, 2, "0", STR_PAD_LEFT)
|
||||||
|
. ":" .
|
||||||
|
str_pad((string) $secs, 2, "0", STR_PAD_LEFT)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSegmentSize(): float
|
||||||
|
{
|
||||||
|
return $this->getRecord()->segment_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getListens(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->listens;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOriginalURL(bool $force = false): string
|
||||||
|
{
|
||||||
|
$disallowed = !OPENVK_ROOT_CONF["openvk"]["preferences"]["music"]["exposeOriginalURLs"] && !$force;
|
||||||
|
if(!$this->isAvailable() || $disallowed)
|
||||||
|
return ovk_scheme(true)
|
||||||
|
. $_SERVER["HTTP_HOST"] . ":"
|
||||||
|
. $_SERVER["HTTP_PORT"]
|
||||||
|
. "/assets/packages/static/openvk/audio/nomusic.mp3";
|
||||||
|
|
||||||
|
$key = bin2hex($this->getRecord()->token);
|
||||||
|
|
||||||
|
return str_replace(".mpd", "_fragments", $this->getURL()) . "/original_$key.mp3";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getURL(?bool $force = false): string
|
||||||
|
{
|
||||||
|
if ($this->isWithdrawn()) return "";
|
||||||
|
|
||||||
|
return parent::getURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKeys(): array
|
||||||
|
{
|
||||||
|
$keys[bin2hex($this->getRecord()->kid)] = bin2hex($this->getRecord()->key);
|
||||||
|
|
||||||
|
return $keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAnonymous(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isExplicit(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->explicit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWithdrawn(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->withdrawn;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isUnlisted(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->unlisted;
|
||||||
|
}
|
||||||
|
|
||||||
|
# NOTICE may flush model to DB if it was just processed
|
||||||
|
function isAvailable(): bool
|
||||||
|
{
|
||||||
|
if($this->getRecord()->processed)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
# throttle requests to isAvailable to prevent DoS attack if filesystem is actually an S3 storage
|
||||||
|
if(time() - $this->getRecord()->checked < 5)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$fragments = str_replace(".mpd", "_fragments", $this->getFileName());
|
||||||
|
$original = "original_" . bin2hex($this->getRecord()->token) . ".mp3";
|
||||||
|
if(file_exists("$fragments/$original")) {
|
||||||
|
# Original gets uploaded after fragments
|
||||||
|
$this->stateChanges("processed", 0x01);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
$this->stateChanges("checked", time());
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isInLibraryOf($entity): bool
|
||||||
|
{
|
||||||
|
return sizeof(DatabaseConnection::i()->getContext()->table("audio_relations")->where([
|
||||||
|
"entity" => $entity->getId() * ($entity instanceof Club ? -1 : 1),
|
||||||
|
"audio" => $this->getId(),
|
||||||
|
])) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function add($entity): bool
|
||||||
|
{
|
||||||
|
if($this->isInLibraryOf($entity))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$entityId = $entity->getId() * ($entity instanceof Club ? -1 : 1);
|
||||||
|
$audioRels = DatabaseConnection::i()->getContext()->table("audio_relations");
|
||||||
|
if(sizeof($audioRels->where("entity", $entityId)) > 65536)
|
||||||
|
throw new \OverflowException("Can't have more than 65536 audios in a playlist");
|
||||||
|
|
||||||
|
$audioRels->insert([
|
||||||
|
"entity" => $entityId,
|
||||||
|
"audio" => $this->getId(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove($entity): bool
|
||||||
|
{
|
||||||
|
if(!$this->isInLibraryOf($entity))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DatabaseConnection::i()->getContext()->table("audio_relations")->where([
|
||||||
|
"entity" => $entity->getId() * ($entity instanceof Club ? -1 : 1),
|
||||||
|
"audio" => $this->getId(),
|
||||||
|
])->delete();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function listen($entity, Playlist $playlist = NULL): bool
|
||||||
|
{
|
||||||
|
$listensTable = DatabaseConnection::i()->getContext()->table("audio_listens");
|
||||||
|
$lastListen = $listensTable->where([
|
||||||
|
"entity" => $entity->getRealId(),
|
||||||
|
"audio" => $this->getId(),
|
||||||
|
])->order("index DESC")->fetch();
|
||||||
|
|
||||||
|
if(!$lastListen || (time() - $lastListen->time >= $this->getLength())) {
|
||||||
|
$listensTable->insert([
|
||||||
|
"entity" => $entity->getRealId(),
|
||||||
|
"audio" => $this->getId(),
|
||||||
|
"time" => time(),
|
||||||
|
"playlist" => $playlist ? $playlist->getId() : NULL,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if($entity instanceof User) {
|
||||||
|
$this->stateChanges("listens", ($this->getListens() + 1));
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
if($playlist) {
|
||||||
|
$playlist->incrementListens();
|
||||||
|
$playlist->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$entity->setLast_played_track($this->getId());
|
||||||
|
$entity->save();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lastListen->update([
|
||||||
|
"time" => time(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns compatible with VK API 4.x, 5.x structure.
|
||||||
|
*
|
||||||
|
* Always sets album(_id) to NULL at this time.
|
||||||
|
* If genre is not present in VK genre list, fallbacks to "Other".
|
||||||
|
* The url and manifest properties will be set to false if the audio can't be played (processing, removed).
|
||||||
|
*
|
||||||
|
* Aside from standard VK properties, this method will also return some OVK extended props:
|
||||||
|
* 1. added - Is in the library of $user?
|
||||||
|
* 2. editable - Can be edited by $user?
|
||||||
|
* 3. withdrawn - Removed due to copyright request?
|
||||||
|
* 4. ready - Can be played at this time?
|
||||||
|
* 5. genre_str - Full name of genre, NULL if it's undefined
|
||||||
|
* 6. manifest - URL to MPEG-DASH manifest
|
||||||
|
* 7. keys - ClearKey DRM keys
|
||||||
|
* 8. explicit - Marked as NSFW?
|
||||||
|
* 9. searchable - Can be found via search?
|
||||||
|
* 10. unique_id - Unique ID of audio
|
||||||
|
*
|
||||||
|
* @notice that in case if exposeOriginalURLs is set to false in config, "url" will always contain link to nomusic.mp3,
|
||||||
|
* unless $forceURLExposure is set to true.
|
||||||
|
*
|
||||||
|
* @notice may trigger db flush if the audio is not processed yet, use with caution on unsaved models.
|
||||||
|
*
|
||||||
|
* @param ?User $user user, relative to whom "added", "editable" will be set
|
||||||
|
* @param bool $forceURLExposure force set "url" regardless of config
|
||||||
|
*/
|
||||||
|
function toVkApiStruct(?User $user = NULL, bool $forceURLExposure = false): object
|
||||||
|
{
|
||||||
|
$obj = (object) [];
|
||||||
|
$obj->unique_id = base64_encode((string) $this->getId());
|
||||||
|
$obj->id = $obj->aid = $this->getVirtualId();
|
||||||
|
$obj->artist = $this->getPerformer();
|
||||||
|
$obj->title = $this->getTitle();
|
||||||
|
$obj->duration = $this->getLength();
|
||||||
|
$obj->album_id = $obj->album = NULL; # i forgor to implement
|
||||||
|
$obj->url = false;
|
||||||
|
$obj->manifest = false;
|
||||||
|
$obj->keys = false;
|
||||||
|
$obj->genre_id = $obj->genre = self::vkGenres[$this->getGenre() ?? ""] ?? 18; # return Other if no match
|
||||||
|
$obj->genre_str = $this->getGenre();
|
||||||
|
$obj->owner_id = $this->getOwner()->getId();
|
||||||
|
if($this->getOwner() instanceof Club)
|
||||||
|
$obj->owner_id *= -1;
|
||||||
|
|
||||||
|
$obj->lyrics = NULL;
|
||||||
|
if(!is_null($this->getLyrics()))
|
||||||
|
$obj->lyrics = $this->getId();
|
||||||
|
|
||||||
|
$obj->added = $user && $this->isInLibraryOf($user);
|
||||||
|
$obj->editable = $user && $this->canBeModifiedBy($user);
|
||||||
|
$obj->searchable = !$this->isUnlisted();
|
||||||
|
$obj->explicit = $this->isExplicit();
|
||||||
|
$obj->withdrawn = $this->isWithdrawn();
|
||||||
|
$obj->ready = $this->isAvailable() && !$obj->withdrawn;
|
||||||
|
if($obj->ready) {
|
||||||
|
$obj->url = $this->getOriginalURL($forceURLExposure);
|
||||||
|
$obj->manifest = $this->getURL();
|
||||||
|
$obj->keys = $this->getKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setOwner(int $oid): void
|
||||||
|
{
|
||||||
|
# WARNING: API implementation won't be able to handle groups like that, don't remove
|
||||||
|
if($oid <= 0)
|
||||||
|
throw new \OutOfRangeException("Only users can be owners of audio!");
|
||||||
|
|
||||||
|
$this->stateChanges("owner", $oid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGenre(string $genre): void
|
||||||
|
{
|
||||||
|
if(!in_array($genre, Audio::genres)) {
|
||||||
|
$this->stateChanges("genre", NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->stateChanges("genre", $genre);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCopyrightStatus(bool $withdrawn = true): void {
|
||||||
|
$this->stateChanges("withdrawn", $withdrawn);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSearchability(bool $searchable = true): void {
|
||||||
|
$this->stateChanges("unlisted", !$searchable);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setToken(string $tok): void {
|
||||||
|
throw new \LogicException("Changing keys is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setKid(string $kid): void {
|
||||||
|
throw new \LogicException("Changing keys is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setKey(string $key): void {
|
||||||
|
throw new \LogicException("Changing keys is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLength(int $len): void {
|
||||||
|
throw new \LogicException("Changing length is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSegment_Size(int $len): void {
|
||||||
|
throw new \LogicException("Changing length is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete(bool $softly = true): void
|
||||||
|
{
|
||||||
|
$ctx = DatabaseConnection::i()->getContext();
|
||||||
|
$ctx->table("audio_relations")->where("audio", $this->getId())
|
||||||
|
->delete();
|
||||||
|
$ctx->table("audio_listens")->where("audio", $this->getId())
|
||||||
|
->delete();
|
||||||
|
$ctx->table("playlist_relations")->where("media", $this->getId())
|
||||||
|
->delete();
|
||||||
|
|
||||||
|
parent::delete($softly);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ namespace openvk\Web\Models\Entities;
|
||||||
use openvk\Web\Util\DateTime;
|
use openvk\Web\Util\DateTime;
|
||||||
use openvk\Web\Models\RowModel;
|
use openvk\Web\Models\RowModel;
|
||||||
use openvk\Web\Models\Entities\{User, Manager};
|
use openvk\Web\Models\Entities\{User, Manager};
|
||||||
use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Managers};
|
use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Managers, Posts};
|
||||||
use Nette\Database\Table\{ActiveRow, GroupedSelection};
|
use Nette\Database\Table\{ActiveRow, GroupedSelection};
|
||||||
use Chandler\Database\DatabaseConnection as DB;
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
use Chandler\Security\User as ChandlerUser;
|
use Chandler\Security\User as ChandlerUser;
|
||||||
|
@ -23,6 +23,10 @@ class Club extends RowModel
|
||||||
const NOT_RELATED = 0;
|
const NOT_RELATED = 0;
|
||||||
const SUBSCRIBED = 1;
|
const SUBSCRIBED = 1;
|
||||||
const REQUEST_SENT = 2;
|
const REQUEST_SENT = 2;
|
||||||
|
|
||||||
|
const WALL_CLOSED = 0;
|
||||||
|
const WALL_OPEN = 1;
|
||||||
|
const WALL_LIMITED = 2;
|
||||||
|
|
||||||
function getId(): int
|
function getId(): int
|
||||||
{
|
{
|
||||||
|
@ -45,6 +49,11 @@ class Club extends RowModel
|
||||||
|
|
||||||
return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURLBySizeId($size);
|
return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURLBySizeId($size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWallType(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->wall;
|
||||||
|
}
|
||||||
|
|
||||||
function getAvatarLink(): string
|
function getAvatarLink(): string
|
||||||
{
|
{
|
||||||
|
@ -182,6 +191,14 @@ class Club extends RowModel
|
||||||
$this->stateChanges("shortcode", $code);
|
$this->stateChanges("shortcode", $code);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setWall(int $type)
|
||||||
|
{
|
||||||
|
if($type > 2 || $type < 0)
|
||||||
|
throw new \LogicException("Invalid wall");
|
||||||
|
|
||||||
|
$this->stateChanges("wall", $type);
|
||||||
|
}
|
||||||
|
|
||||||
function isSubscriptionAccepted(User $user): bool
|
function isSubscriptionAccepted(User $user): bool
|
||||||
{
|
{
|
||||||
|
@ -291,6 +308,21 @@ class Club extends RowModel
|
||||||
yield $rel;
|
yield $rel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSuggestedPostsCount(User $user = NULL)
|
||||||
|
{
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
if(is_null($user))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if($this->canBeModifiedBy($user))
|
||||||
|
$count = (new Posts)->getSuggestedPostsCount($this->getId());
|
||||||
|
else
|
||||||
|
$count = (new Posts)->getSuggestedPostsCountByUser($this->getId(), $user->getId());
|
||||||
|
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
|
|
||||||
function getManagers(int $page = 1, bool $ignoreHidden = false): \Traversable
|
function getManagers(int $page = 1, bool $ignoreHidden = false): \Traversable
|
||||||
{
|
{
|
||||||
|
@ -367,38 +399,66 @@ class Club extends RowModel
|
||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canBeViewedBy(?User $user = NULL)
|
||||||
|
{
|
||||||
|
return is_null($this->getBanReason());
|
||||||
|
}
|
||||||
|
|
||||||
function getAlert(): ?string
|
function getAlert(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->alert;
|
return $this->getRecord()->alert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRealId(): int
|
||||||
|
{
|
||||||
|
return $this->getId() * -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEveryoneCanUploadAudios(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->everyone_can_upload_audios;
|
||||||
|
}
|
||||||
|
|
||||||
|
function canUploadAudio(?User $user): bool
|
||||||
|
{
|
||||||
|
if(!$user)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return $this->isEveryoneCanUploadAudios() || $this->canBeModifiedBy($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAudiosCollectionSize()
|
||||||
|
{
|
||||||
|
return (new \openvk\Web\Models\Repositories\Audios)->getClubCollectionSize($this);
|
||||||
|
}
|
||||||
|
|
||||||
function toVkApiStruct(?User $user = NULL): object
|
function toVkApiStruct(?User $user = NULL): object
|
||||||
{
|
{
|
||||||
$res = [];
|
$res = (object) [];
|
||||||
|
|
||||||
$res->id = $this->getId();
|
$res->id = $this->getId();
|
||||||
$res->name = $this->getName();
|
$res->name = $this->getName();
|
||||||
$res->screen_name = $this->getShortCode();
|
$res->screen_name = $this->getShortCode();
|
||||||
$res->is_closed = 0;
|
$res->is_closed = 0;
|
||||||
$res->deactivated = NULL;
|
$res->deactivated = NULL;
|
||||||
$res->is_admin = $this->canBeModifiedBy($user);
|
$res->is_admin = $user && $this->canBeModifiedBy($user);
|
||||||
|
|
||||||
if($this->canBeModifiedBy($user)) {
|
if($user && $this->canBeModifiedBy($user)) {
|
||||||
$res->admin_level = 3;
|
$res->admin_level = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
$res->is_member = $this->getSubscriptionStatus($user) ? 1 : 0;
|
$res->is_member = $user && $this->getSubscriptionStatus($user) ? 1 : 0;
|
||||||
|
|
||||||
$res->type = "group";
|
$res->type = "group";
|
||||||
$res->photo_50 = $this->getAvatarUrl("miniscule");
|
$res->photo_50 = $this->getAvatarUrl("miniscule");
|
||||||
$res->photo_100 = $this->getAvatarUrl("tiny");
|
$res->photo_100 = $this->getAvatarUrl("tiny");
|
||||||
$res->photo_200 = $this->getAvatarUrl("normal");
|
$res->photo_200 = $this->getAvatarUrl("normal");
|
||||||
|
|
||||||
$res->can_create_topic = $this->canBeModifiedBy($user) ? 1 : ($this->isEveryoneCanCreateTopics() ? 1 : 0);
|
$res->can_create_topic = $user && $this->canBeModifiedBy($user) ? 1 : ($this->isEveryoneCanCreateTopics() ? 1 : 0);
|
||||||
|
|
||||||
$res->can_post = $this->canBeModifiedBy($user) ? 1 : ($this->canPost() ? 1 : 0);
|
$res->can_post = $user && $this->canBeModifiedBy($user) ? 1 : ($this->canPost() ? 1 : 0);
|
||||||
|
|
||||||
return (object) $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isIgnoredBy(User $user): bool
|
function isIgnoredBy(User $user): bool
|
||||||
|
@ -425,4 +485,5 @@ class Club extends RowModel
|
||||||
|
|
||||||
use Traits\TBackDrops;
|
use Traits\TBackDrops;
|
||||||
use Traits\TSubscribable;
|
use Traits\TSubscribable;
|
||||||
|
use Traits\TAudioStatuses;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ class Comment extends Post
|
||||||
|
|
||||||
function getPrettyId(): string
|
function getPrettyId(): string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->id;
|
return (string)$this->getRecord()->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVirtualId(): int
|
function getVirtualId(): int
|
||||||
|
@ -74,8 +74,12 @@ class Comment extends Post
|
||||||
foreach($this->getChildren() as $attachment) {
|
foreach($this->getChildren() as $attachment) {
|
||||||
if($attachment->isDeleted())
|
if($attachment->isDeleted())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$res->attachments[] = $attachment->toVkApiStruct();
|
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||||
|
$res->attachments[] = $attachment->toVkApiStruct();
|
||||||
|
} else if($attachment instanceof \openvk\Web\Models\Entities\Video) {
|
||||||
|
$res->attachments[] = $attachment->toVkApiStruct($this->getUser());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($need_likes) {
|
if($need_likes) {
|
||||||
|
@ -91,6 +95,28 @@ class Comment extends Post
|
||||||
return "/wall" . $this->getTarget()->getPrettyId() . "#_comment" . $this->getId();
|
return "/wall" . $this->getTarget()->getPrettyId() . "#_comment" . $this->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canBeViewedBy(?User $user = NULL): bool
|
||||||
|
{
|
||||||
|
if($this->isDeleted() || $this->getTarget()->isDeleted()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getTarget()->canBeViewedBy($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toNotifApiStruct()
|
||||||
|
{
|
||||||
|
$res = (object)[];
|
||||||
|
|
||||||
|
$res->id = $this->getId();
|
||||||
|
$res->owner_id = $this->getOwner()->getId();
|
||||||
|
$res->date = $this->getPublicationTime()->timestamp();
|
||||||
|
$res->text = $this->getText(false);
|
||||||
|
$res->post = NULL; # todo
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
function canBeEditedBy(?User $user = NULL): bool
|
function canBeEditedBy(?User $user = NULL): bool
|
||||||
{
|
{
|
||||||
if(!$user)
|
if(!$user)
|
||||||
|
|
|
@ -131,7 +131,7 @@ class Correspondence
|
||||||
*/
|
*/
|
||||||
function getPreviewMessage(): ?Message
|
function getPreviewMessage(): ?Message
|
||||||
{
|
{
|
||||||
$messages = $this->getMessages(1, NULL, 1);
|
$messages = $this->getMessages(1, NULL, 1, 0);
|
||||||
return $messages[0] ?? NULL;
|
return $messages[0] ?? NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ class IP extends RowModel
|
||||||
$this->stateChanges("ip", $ip);
|
$this->stateChanges("ip", $ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
function save($log): void
|
function save(?bool $log = false): void
|
||||||
{
|
{
|
||||||
if(is_null($this->getRecord()))
|
if(is_null($this->getRecord()))
|
||||||
$this->stateChanges("first_seen", time());
|
$this->stateChanges("first_seen", time());
|
||||||
|
|
|
@ -121,14 +121,14 @@ abstract class Media extends Postable
|
||||||
$this->stateChanges("hash", $hash);
|
$this->stateChanges("hash", $hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
function save(): void
|
function save(?bool $log = false): void
|
||||||
{
|
{
|
||||||
if(!is_null($this->processingPlaceholder) && is_null($this->getRecord())) {
|
if(!is_null($this->processingPlaceholder) && is_null($this->getRecord())) {
|
||||||
$this->stateChanges("processed", 0);
|
$this->stateChanges("processed", 0);
|
||||||
$this->stateChanges("last_checked", time());
|
$this->stateChanges("last_checked", time());
|
||||||
}
|
}
|
||||||
|
|
||||||
parent::save();
|
parent::save($log);
|
||||||
}
|
}
|
||||||
|
|
||||||
function delete(bool $softly = true): void
|
function delete(bool $softly = true): void
|
||||||
|
|
|
@ -17,7 +17,17 @@ abstract class MediaCollection extends RowModel
|
||||||
|
|
||||||
protected $specialNames = [];
|
protected $specialNames = [];
|
||||||
|
|
||||||
private $relations;
|
protected $relations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum amount of items Collection can have
|
||||||
|
*/
|
||||||
|
const MAX_ITEMS = INF;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum amount of Collections with same "owner" allowed
|
||||||
|
*/
|
||||||
|
const MAX_COUNT = INF;
|
||||||
|
|
||||||
function __construct(?ActiveRow $ar = NULL)
|
function __construct(?ActiveRow $ar = NULL)
|
||||||
{
|
{
|
||||||
|
@ -70,18 +80,29 @@ abstract class MediaCollection extends RowModel
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract function getCoverURL(): ?string;
|
abstract function getCoverURL(): ?string;
|
||||||
|
|
||||||
function fetch(int $page = 1, ?int $perPage = NULL): \Traversable
|
function fetchClassic(int $offset = 0, ?int $limit = NULL): \Traversable
|
||||||
{
|
{
|
||||||
$related = $this->getRecord()->related("$this->relTableName.collection")->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE)->order("media ASC");
|
$related = $this->getRecord()->related("$this->relTableName.collection")
|
||||||
|
->limit($limit ?? OPENVK_DEFAULT_PER_PAGE, $offset)
|
||||||
|
->order("media ASC");
|
||||||
|
|
||||||
foreach($related as $rel) {
|
foreach($related as $rel) {
|
||||||
$media = $rel->ref($this->entityTableName, "media");
|
$media = $rel->ref($this->entityTableName, "media");
|
||||||
if(!$media)
|
if(!$media)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
yield new $this->entityClassName($media);
|
yield new $this->entityClassName($media);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fetch(int $page = 1, ?int $perPage = NULL): \Traversable
|
||||||
|
{
|
||||||
|
$page = max(1, $page);
|
||||||
|
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
||||||
|
|
||||||
|
return $this->fetchClassic($perPage * ($page - 1), $perPage);
|
||||||
|
}
|
||||||
|
|
||||||
function size(): int
|
function size(): int
|
||||||
{
|
{
|
||||||
|
@ -110,7 +131,7 @@ abstract class MediaCollection extends RowModel
|
||||||
{
|
{
|
||||||
return $this->getRecord()->special_type !== 0;
|
return $this->getRecord()->special_type !== 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function add(RowModel $entity): bool
|
function add(RowModel $entity): bool
|
||||||
{
|
{
|
||||||
$this->entitySuitable($entity);
|
$this->entitySuitable($entity);
|
||||||
|
@ -118,6 +139,10 @@ abstract class MediaCollection extends RowModel
|
||||||
if(!$this->allowDuplicates)
|
if(!$this->allowDuplicates)
|
||||||
if($this->has($entity))
|
if($this->has($entity))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if(self::MAX_ITEMS != INF)
|
||||||
|
if(sizeof($this->relations->where("collection", $this->getId())) > self::MAX_ITEMS)
|
||||||
|
throw new \OutOfBoundsException("Collection is full");
|
||||||
|
|
||||||
$this->relations->insert([
|
$this->relations->insert([
|
||||||
"collection" => $this->getId(),
|
"collection" => $this->getId(),
|
||||||
|
@ -127,14 +152,14 @@ abstract class MediaCollection extends RowModel
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove(RowModel $entity): void
|
function remove(RowModel $entity): bool
|
||||||
{
|
{
|
||||||
$this->entitySuitable($entity);
|
$this->entitySuitable($entity);
|
||||||
|
|
||||||
$this->relations->where([
|
return $this->relations->where([
|
||||||
"collection" => $this->getId(),
|
"collection" => $this->getId(),
|
||||||
"media" => $entity->getId(),
|
"media" => $entity->getId(),
|
||||||
])->delete();
|
])->delete() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function has(RowModel $entity): bool
|
function has(RowModel $entity): bool
|
||||||
|
@ -148,6 +173,33 @@ abstract class MediaCollection extends RowModel
|
||||||
|
|
||||||
return !is_null($rel);
|
return !is_null($rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function save(?bool $log = false): void
|
||||||
|
{
|
||||||
|
$thisTable = DatabaseConnection::i()->getContext()->table($this->tableName);
|
||||||
|
if(self::MAX_COUNT != INF)
|
||||||
|
if(isset($this->changes["owner"]))
|
||||||
|
if(sizeof($thisTable->where("owner", $this->changes["owner"])) > self::MAX_COUNT)
|
||||||
|
throw new \OutOfBoundsException("Maximum amount of collections");
|
||||||
|
|
||||||
|
if(is_null($this->getRecord()))
|
||||||
|
if(!isset($this->changes["created"]))
|
||||||
|
$this->stateChanges("created", time());
|
||||||
|
else
|
||||||
|
$this->stateChanges("edited", time());
|
||||||
|
|
||||||
|
parent::save($log);
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete(bool $softly = true): void
|
||||||
|
{
|
||||||
|
if(!$softly) {
|
||||||
|
$this->relations->where("collection", $this->getId())
|
||||||
|
->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::delete($softly);
|
||||||
|
}
|
||||||
|
|
||||||
use Traits\TOwnable;
|
use Traits\TOwnable;
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,11 @@ class Message extends RowModel
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
throw new \Exception("Unknown attachment type: " . get_class($attachment));
|
$attachments[] = [
|
||||||
|
"type" => "unknown"
|
||||||
|
];
|
||||||
|
|
||||||
|
# throw new \Exception("Unknown attachment type: " . get_class($attachment));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,15 @@ class Note extends Postable
|
||||||
{
|
{
|
||||||
return $this->getRecord()->source;
|
return $this->getRecord()->source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canBeViewedBy(?User $user = NULL): bool
|
||||||
|
{
|
||||||
|
if($this->isDeleted() || $this->getOwner()->isDeleted()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getOwner()->getPrivacyPermission('notes.read', $user) && $this->getOwner()->canBeViewedBy($user);
|
||||||
|
}
|
||||||
|
|
||||||
function toVkApiStruct(): object
|
function toVkApiStruct(): object
|
||||||
{
|
{
|
||||||
|
@ -131,7 +140,7 @@ class Note extends Postable
|
||||||
$res->date = $this->getPublicationTime()->timestamp();
|
$res->date = $this->getPublicationTime()->timestamp();
|
||||||
$res->comments = $this->getCommentsCount();
|
$res->comments = $this->getCommentsCount();
|
||||||
$res->read_comments = $this->getCommentsCount();
|
$res->read_comments = $this->getCommentsCount();
|
||||||
$res->view_url = "/note".$this->getOwner()->getId()."_".$this->getId();
|
$res->view_url = "/note".$this->getOwner()->getId()."_".$this->getVirtualId();
|
||||||
$res->privacy_view = 1;
|
$res->privacy_view = 1;
|
||||||
$res->can_comment = 1;
|
$res->can_comment = 1;
|
||||||
$res->text_wiki = "r";
|
$res->text_wiki = "r";
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities\Notifications;
|
||||||
|
use openvk\Web\Models\Entities\{User, Club};
|
||||||
|
|
||||||
|
final class NewSuggestedPostsNotification extends Notification
|
||||||
|
{
|
||||||
|
protected $actionCode = 7;
|
||||||
|
|
||||||
|
function __construct(User $owner, Club $group)
|
||||||
|
{
|
||||||
|
parent::__construct($owner, $owner, $group, time(), "");
|
||||||
|
}
|
||||||
|
}
|
|
@ -132,4 +132,138 @@ QUERY;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getVkApiInfo()
|
||||||
|
{
|
||||||
|
$origin_m = $this->encodeType($this->originModel);
|
||||||
|
$target_m = $this->encodeType($this->targetModel);
|
||||||
|
|
||||||
|
$info = [
|
||||||
|
"type" => "",
|
||||||
|
"parent" => NULL,
|
||||||
|
"feedback" => NULL,
|
||||||
|
];
|
||||||
|
|
||||||
|
switch($this->getActionCode()) {
|
||||||
|
case 0:
|
||||||
|
$info["type"] = "like_post";
|
||||||
|
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
|
||||||
|
$info["feedback"] = $this->getModel(1)->toVkApiStruct();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
$info["type"] = "copy_post";
|
||||||
|
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
|
||||||
|
$info["feedback"] = NULL; # todo
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
switch($origin_m) {
|
||||||
|
case 19:
|
||||||
|
$info["type"] = "comment_video";
|
||||||
|
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
|
||||||
|
$info["feedback"] = NULL; # айди коммента не сохраняется в бд( ну пиздец блять
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
$info["type"] = "comment_photo";
|
||||||
|
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
|
||||||
|
$info["feedback"] = NULL;
|
||||||
|
break;
|
||||||
|
# unstandart (vk forgor about notes)
|
||||||
|
case 10:
|
||||||
|
$info["type"] = "comment_note";
|
||||||
|
$info["parent"] = $this->getModel(0)->toVkApiStruct();
|
||||||
|
$info["feedback"] = NULL;
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
$info["type"] = "comment_post";
|
||||||
|
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
|
||||||
|
$info["feedback"] = NULL;
|
||||||
|
break;
|
||||||
|
# unused (users don't have topics bruh)
|
||||||
|
case 21:
|
||||||
|
$info["type"] = "comment_topic";
|
||||||
|
$info["parent"] = $this->getModel(0)->toVkApiStruct(0, 90);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$info["type"] = "comment_unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
$info["type"] = "wall";
|
||||||
|
$info["feedback"] = $this->getModel(0)->toNotifApiStruct();
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
switch($target_m) {
|
||||||
|
case 14:
|
||||||
|
$info["type"] = "mention";
|
||||||
|
$info["feedback"] = $this->getModel(1)->toNotifApiStruct();
|
||||||
|
break;
|
||||||
|
case 19:
|
||||||
|
$info["type"] = "mention_comment_video";
|
||||||
|
$info["parent"] = $this->getModel(1)->toNotifApiStruct();
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
$info["type"] = "mention_comment_photo";
|
||||||
|
$info["parent"] = $this->getModel(1)->toNotifApiStruct();
|
||||||
|
break;
|
||||||
|
# unstandart
|
||||||
|
case 10:
|
||||||
|
$info["type"] = "mention_comment_note";
|
||||||
|
$info["parent"] = $this->getModel(1)->toVkApiStruct();
|
||||||
|
break;
|
||||||
|
case 21:
|
||||||
|
$info["type"] = "mention_comments";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$info["type"] = "mention_comment_unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
$info["type"] = "make_you_admin";
|
||||||
|
$info["parent"] = $this->getModel(0)->toVkApiStruct($this->getModel(1));
|
||||||
|
break;
|
||||||
|
# Нужно доделать после мержа #935
|
||||||
|
case 6:
|
||||||
|
$info["type"] = "wall_publish";
|
||||||
|
break;
|
||||||
|
# В вк не было такого уведомления, так что unstandart
|
||||||
|
case 7:
|
||||||
|
$info["type"] = "new_posts_in_club";
|
||||||
|
break;
|
||||||
|
# В вк при передаче подарков приходит сообщение, а не уведомление, так что unstandart
|
||||||
|
case 9601:
|
||||||
|
$info["type"] = "sent_gift";
|
||||||
|
$info["parent"] = $this->getModel(1)->toVkApiStruct($this->getModel(1));
|
||||||
|
break;
|
||||||
|
case 9602:
|
||||||
|
$info["type"] = "voices_transfer";
|
||||||
|
$info["parent"] = $this->getModel(1)->toVkApiStruct($this->getModel(1));
|
||||||
|
break;
|
||||||
|
case 9603:
|
||||||
|
$info["type"] = "up_rating";
|
||||||
|
$info["parent"] = $this->getModel(1)->toVkApiStruct($this->getModel(1));
|
||||||
|
$info["parent"]->count = $this->getData();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$info["type"] = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toVkApiStruct()
|
||||||
|
{
|
||||||
|
$res = (object)[];
|
||||||
|
|
||||||
|
$info = $this->getVkApiInfo();
|
||||||
|
$res->type = $info["type"];
|
||||||
|
$res->date = $this->getDateTime()->timestamp();
|
||||||
|
$res->parent = $info["parent"];
|
||||||
|
$res->feedback = $info["feedback"];
|
||||||
|
$res->reply = NULL; # Ответы на комментарии не реализованы
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities\Notifications;
|
||||||
|
use openvk\Web\Models\Entities\{User, Club, Post};
|
||||||
|
|
||||||
|
final class PostAcceptedNotification extends Notification
|
||||||
|
{
|
||||||
|
protected $actionCode = 6;
|
||||||
|
|
||||||
|
function __construct(User $author, Post $post, Club $group)
|
||||||
|
{
|
||||||
|
parent::__construct($author, $post, $group, time(), "");
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,11 +54,11 @@ class PasswordReset extends RowModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function save(): void
|
function save(?bool $log = false): void
|
||||||
{
|
{
|
||||||
$this->stateChanges("key", base64_encode(openssl_random_pseudo_bytes(46)));
|
$this->stateChanges("key", base64_encode(openssl_random_pseudo_bytes(46)));
|
||||||
$this->stateChanges("timestamp", time());
|
$this->stateChanges("timestamp", time());
|
||||||
|
|
||||||
parent::save();
|
parent::save($log);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -328,6 +328,19 @@ class Photo extends Media
|
||||||
|
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canBeViewedBy(?User $user = NULL): bool
|
||||||
|
{
|
||||||
|
if($this->isDeleted() || $this->getOwner()->isDeleted()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!is_null($this->getAlbum())) {
|
||||||
|
return $this->getAlbum()->canBeViewedBy($user);
|
||||||
|
} else {
|
||||||
|
return $this->getOwner()->canBeViewedBy($user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static function fastMake(int $owner, string $description = "", array $file, ?Album $album = NULL, bool $anon = false): Photo
|
static function fastMake(int $owner, string $description = "", array $file, ?Album $album = NULL, bool $anon = false): Photo
|
||||||
{
|
{
|
||||||
|
@ -347,4 +360,20 @@ class Photo extends Media
|
||||||
|
|
||||||
return $photo;
|
return $photo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toNotifApiStruct()
|
||||||
|
{
|
||||||
|
$res = (object)[];
|
||||||
|
|
||||||
|
$res->id = $this->getVirtualId();
|
||||||
|
$res->owner_id = $this->getOwner()->getId();
|
||||||
|
$res->aid = 0;
|
||||||
|
$res->src = $this->getURLBySizeId("tiny");
|
||||||
|
$res->src_big = $this->getURLBySizeId("normal");
|
||||||
|
$res->src_small = $this->getURLBySizeId("miniscule");
|
||||||
|
$res->text = $this->getDescription();
|
||||||
|
$res->created = $this->getPublicationTime()->timestamp();
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
256
Web/Models/Entities/Playlist.php
Normal file
256
Web/Models/Entities/Playlist.php
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use Nette\Database\Table\ActiveRow;
|
||||||
|
use openvk\Web\Models\Repositories\Audios;
|
||||||
|
use openvk\Web\Models\Repositories\Photos;
|
||||||
|
use openvk\Web\Models\RowModel;
|
||||||
|
use openvk\Web\Models\Entities\Photo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method setName(string $name)
|
||||||
|
* @method setDescription(?string $desc)
|
||||||
|
*/
|
||||||
|
class Playlist extends MediaCollection
|
||||||
|
{
|
||||||
|
protected $tableName = "playlists";
|
||||||
|
protected $relTableName = "playlist_relations";
|
||||||
|
protected $entityTableName = "audios";
|
||||||
|
protected $entityClassName = 'openvk\Web\Models\Entities\Audio';
|
||||||
|
protected $allowDuplicates = false;
|
||||||
|
|
||||||
|
private $importTable;
|
||||||
|
|
||||||
|
const MAX_COUNT = 1000;
|
||||||
|
const MAX_ITEMS = 10000;
|
||||||
|
|
||||||
|
function __construct(?ActiveRow $ar = NULL)
|
||||||
|
{
|
||||||
|
parent::__construct($ar);
|
||||||
|
|
||||||
|
$this->importTable = DatabaseConnection::i()->getContext()->table("playlist_imports");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCoverURL(string $size = "normal"): ?string
|
||||||
|
{
|
||||||
|
$photo = (new Photos)->get((int) $this->getRecord()->cover_photo_id);
|
||||||
|
return is_null($photo) ? "/assets/packages/static/openvk/img/song.jpg" : $photo->getURLBySizeId($size);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLength(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAudios(int $offset = 0, ?int $limit = NULL, ?int $shuffleSeed = NULL): \Traversable
|
||||||
|
{
|
||||||
|
if(!$shuffleSeed) {
|
||||||
|
foreach ($this->fetchClassic($offset, $limit) as $e)
|
||||||
|
yield $e; # No, I can't return, it will break with []
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = [];
|
||||||
|
foreach($this->relations->select("media AS i")->where("collection", $this->getId()) as $rel)
|
||||||
|
$ids[] = $rel->i;
|
||||||
|
|
||||||
|
$ids = knuth_shuffle($ids, $shuffleSeed);
|
||||||
|
$ids = array_slice($ids, $offset, $limit ?? OPENVK_DEFAULT_PER_PAGE);
|
||||||
|
foreach($ids as $id)
|
||||||
|
yield (new Audios)->get($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function add(RowModel $audio): bool
|
||||||
|
{
|
||||||
|
if($res = parent::add($audio)) {
|
||||||
|
$this->stateChanges("length", $this->getRecord()->length + $audio->getLength());
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(RowModel $audio): bool
|
||||||
|
{
|
||||||
|
if($res = parent::remove($audio)) {
|
||||||
|
$this->stateChanges("length", $this->getRecord()->length - $audio->getLength());
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBookmarkedBy(RowModel $entity): bool
|
||||||
|
{
|
||||||
|
$id = $entity->getId();
|
||||||
|
if($entity instanceof Club)
|
||||||
|
$id *= -1;
|
||||||
|
|
||||||
|
return !is_null($this->importTable->where([
|
||||||
|
"entity" => $id,
|
||||||
|
"playlist" => $this->getId(),
|
||||||
|
])->fetch());
|
||||||
|
}
|
||||||
|
|
||||||
|
function bookmark(RowModel $entity): bool
|
||||||
|
{
|
||||||
|
if($this->isBookmarkedBy($entity))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$id = $entity->getId();
|
||||||
|
if($entity instanceof Club)
|
||||||
|
$id *= -1;
|
||||||
|
|
||||||
|
if($this->importTable->where("entity", $id)->count() > self::MAX_COUNT)
|
||||||
|
throw new \OutOfBoundsException("Maximum amount of playlists");
|
||||||
|
|
||||||
|
$this->importTable->insert([
|
||||||
|
"entity" => $id,
|
||||||
|
"playlist" => $this->getId(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unbookmark(RowModel $entity): bool
|
||||||
|
{
|
||||||
|
$id = $entity->getId();
|
||||||
|
if($entity instanceof Club)
|
||||||
|
$id *= -1;
|
||||||
|
|
||||||
|
$count = $this->importTable->where([
|
||||||
|
"entity" => $id,
|
||||||
|
"playlist" => $this->getId(),
|
||||||
|
])->delete();
|
||||||
|
|
||||||
|
return $count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDescription(): ?string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDescriptionHTML(): ?string
|
||||||
|
{
|
||||||
|
return htmlspecialchars($this->getRecord()->description, ENT_DISALLOWED | ENT_XHTML);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getListens()
|
||||||
|
{
|
||||||
|
return $this->getRecord()->listens;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toVkApiStruct(?User $user = NULL): object
|
||||||
|
{
|
||||||
|
$oid = $this->getOwner()->getId();
|
||||||
|
if($this->getOwner() instanceof Club)
|
||||||
|
$oid *= -1;
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"id" => $this->getId(),
|
||||||
|
"owner_id" => $oid,
|
||||||
|
"title" => $this->getName(),
|
||||||
|
"description" => $this->getDescription(),
|
||||||
|
"size" => $this->size(),
|
||||||
|
"length" => $this->getLength(),
|
||||||
|
"created" => $this->getCreationTime()->timestamp(),
|
||||||
|
"modified" => $this->getEditTime() ? $this->getEditTime()->timestamp() : NULL,
|
||||||
|
"accessible" => $this->canBeViewedBy($user),
|
||||||
|
"editable" => $this->canBeModifiedBy($user),
|
||||||
|
"bookmarked" => $this->isBookmarkedBy($user),
|
||||||
|
"listens" => $this->getListens(),
|
||||||
|
"cover_url" => $this->getCoverURL(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLength(): void
|
||||||
|
{
|
||||||
|
throw new \LogicException("Can't set length of playlist manually");
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetLength(): bool
|
||||||
|
{
|
||||||
|
$this->stateChanges("length", 0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete(bool $softly = true): void
|
||||||
|
{
|
||||||
|
$ctx = DatabaseConnection::i()->getContext();
|
||||||
|
$ctx->table("playlist_imports")->where("playlist", $this->getId())
|
||||||
|
->delete();
|
||||||
|
|
||||||
|
parent::delete($softly);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasAudio(Audio $audio): bool
|
||||||
|
{
|
||||||
|
$ctx = DatabaseConnection::i()->getContext();
|
||||||
|
return !is_null($ctx->table("playlist_relations")->where([
|
||||||
|
"collection" => $this->getId(),
|
||||||
|
"media" => $audio->getId()
|
||||||
|
])->fetch());
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCoverPhotoId(): ?int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->cover_photo_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function canBeModifiedBy(User $user): bool
|
||||||
|
{
|
||||||
|
if(!$user)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if($this->getOwner() instanceof User)
|
||||||
|
return $user->getId() == $this->getOwner()->getId();
|
||||||
|
else
|
||||||
|
return $this->getOwner()->canBeModifiedBy($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLengthInMinutes(): int
|
||||||
|
{
|
||||||
|
return (int)round($this->getLength() / 60, PHP_ROUND_HALF_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fastMakeCover(int $owner, array $file)
|
||||||
|
{
|
||||||
|
$cover = new Photo;
|
||||||
|
$cover->setOwner($owner);
|
||||||
|
$cover->setDescription("Playlist cover image");
|
||||||
|
$cover->setFile($file);
|
||||||
|
$cover->setCreated(time());
|
||||||
|
$cover->save();
|
||||||
|
|
||||||
|
$this->setCover_photo_id($cover->getId());
|
||||||
|
|
||||||
|
return $cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getURL(): string
|
||||||
|
{
|
||||||
|
return "/playlist" . $this->getOwner()->getRealId() . "_" . $this->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
function incrementListens()
|
||||||
|
{
|
||||||
|
$this->stateChanges("listens", ($this->getListens() + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMetaDescription(): string
|
||||||
|
{
|
||||||
|
$length = $this->getLengthInMinutes();
|
||||||
|
|
||||||
|
$props = [];
|
||||||
|
$props[] = tr("audios_count", $this->size());
|
||||||
|
$props[] = "<span id='listensCount'>" . tr("listens_count", $this->getListens()) . "</span>";
|
||||||
|
if($length > 0) $props[] = tr("minutes_count", $length);
|
||||||
|
$props[] = tr("created_playlist") . " " . $this->getPublicationTime();
|
||||||
|
# if($this->getEditTime()) $props[] = tr("updated_playlist") . " " . $this->getEditTime();
|
||||||
|
|
||||||
|
return implode(" • ", $props);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ use openvk\Web\Models\Exceptions\TooMuchOptionsException;
|
||||||
use openvk\Web\Util\DateTime;
|
use openvk\Web\Util\DateTime;
|
||||||
use \UnexpectedValueException;
|
use \UnexpectedValueException;
|
||||||
use Nette\InvalidStateException;
|
use Nette\InvalidStateException;
|
||||||
use openvk\Web\Models\Repositories\Users;
|
use openvk\Web\Models\Repositories\{Users, Posts};
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
use openvk\Web\Models\Exceptions\PollLockedException;
|
use openvk\Web\Models\Exceptions\PollLockedException;
|
||||||
use openvk\Web\Models\Exceptions\AlreadyVotedException;
|
use openvk\Web\Models\Exceptions\AlreadyVotedException;
|
||||||
|
@ -165,7 +165,7 @@ class Poll extends Attachable
|
||||||
|
|
||||||
function canVote(User $user): bool
|
function canVote(User $user): bool
|
||||||
{
|
{
|
||||||
return !$this->hasEnded() && !$this->hasVoted($user);
|
return !$this->hasEnded() && !$this->hasVoted($user) && !is_null($this->getAttachedPost()) && $this->getAttachedPost()->getSuggestionType() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function vote(User $user, array $optionIds): void
|
function vote(User $user, array $optionIds): void
|
||||||
|
@ -278,13 +278,24 @@ class Poll extends Attachable
|
||||||
|
|
||||||
return $poll;
|
return $poll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canBeViewedBy(?User $user = NULL): bool
|
||||||
|
{
|
||||||
|
# waiting for #935 :(
|
||||||
|
/*if(!is_null($this->getAttachedPost())) {
|
||||||
|
return $this->getAttachedPost()->canBeViewedBy($user);
|
||||||
|
} else {*/
|
||||||
|
return true;
|
||||||
|
#}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function save(): void
|
function save(?bool $log = false): void
|
||||||
{
|
{
|
||||||
if(empty($this->choicesToPersist))
|
if(empty($this->choicesToPersist))
|
||||||
throw new InvalidStateException;
|
throw new InvalidStateException;
|
||||||
|
|
||||||
parent::save();
|
parent::save($log);
|
||||||
foreach($this->choicesToPersist as $option) {
|
foreach($this->choicesToPersist as $option) {
|
||||||
DatabaseConnection::i()->getContext()->table("poll_options")->insert([
|
DatabaseConnection::i()->getContext()->table("poll_options")->insert([
|
||||||
"poll" => $this->getId(),
|
"poll" => $this->getId(),
|
||||||
|
@ -292,4 +303,17 @@ class Poll extends Attachable
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAttachedPost()
|
||||||
|
{
|
||||||
|
$post = DatabaseConnection::i()->getContext()->table("attachments")
|
||||||
|
->where(
|
||||||
|
["attachable_type" => static::class,
|
||||||
|
"attachable_id" => $this->getId()])->fetch();
|
||||||
|
|
||||||
|
if(!is_null($post->target_id))
|
||||||
|
return (new Posts)->get($post->target_id);
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,6 +207,9 @@ class Post extends Postable
|
||||||
|
|
||||||
function canBeDeletedBy(User $user): bool
|
function canBeDeletedBy(User $user): bool
|
||||||
{
|
{
|
||||||
|
if($this->getTargetWall() < 0 && !$this->getWallOwner()->canBeModifiedBy($user) && $this->getWallOwner()->getWallType() != 1 && $this->getSuggestionType() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
return $this->getOwnerPost() === $user->getId() || $this->canBePinnedBy($user);
|
return $this->getOwnerPost() === $user->getId() || $this->canBePinnedBy($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,6 +249,37 @@ class Post extends Postable
|
||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canBeViewedBy(?User $user = NULL): bool
|
||||||
|
{
|
||||||
|
if($this->isDeleted()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getWallOwner()->canBeViewedBy($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSuggestionType()
|
||||||
|
{
|
||||||
|
return $this->getRecord()->suggested;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toNotifApiStruct()
|
||||||
|
{
|
||||||
|
$res = (object)[];
|
||||||
|
|
||||||
|
$res->id = $this->getVirtualId();
|
||||||
|
$res->to_id = $this->getOwner() instanceof Club ? $this->getOwner()->getId() * -1 : $this->getOwner()->getId();
|
||||||
|
$res->from_id = $res->to_id;
|
||||||
|
$res->date = $this->getPublicationTime()->timestamp();
|
||||||
|
$res->text = $this->getText(false);
|
||||||
|
$res->attachments = []; # todo
|
||||||
|
|
||||||
|
$res->copy_owner_id = NULL; # todo
|
||||||
|
$res->copy_post_id = NULL; # todo
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
function canBeEditedBy(?User $user = NULL): bool
|
function canBeEditedBy(?User $user = NULL): bool
|
||||||
{
|
{
|
||||||
if(!$user)
|
if(!$user)
|
||||||
|
@ -256,6 +290,12 @@ class Post extends Postable
|
||||||
|
|
||||||
if($this->getTargetWall() > 0)
|
if($this->getTargetWall() > 0)
|
||||||
return $this->getPublicationTime()->timestamp() + WEEK > time() && $user->getId() == $this->getOwner(false)->getId();
|
return $this->getPublicationTime()->timestamp() + WEEK > time() && $user->getId() == $this->getOwner(false)->getId();
|
||||||
|
else {
|
||||||
|
if($this->isPostedOnBehalfOfGroup())
|
||||||
|
return $this->getWallOwner()->canBeModifiedBy($user);
|
||||||
|
else
|
||||||
|
return $user->getId() == $this->getOwner(false)->getId();
|
||||||
|
}
|
||||||
|
|
||||||
return $user->getId() == $this->getOwner(false)->getId();
|
return $user->getId() == $this->getOwner(false)->getId();
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ abstract class Postable extends Attachable
|
||||||
{
|
{
|
||||||
$oid = (int) $this->getRecord()->owner;
|
$oid = (int) $this->getRecord()->owner;
|
||||||
if(!$real && $this->isAnonymous())
|
if(!$real && $this->isAnonymous())
|
||||||
$oid = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["account"];
|
$oid = (int) OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["account"];
|
||||||
|
|
||||||
$oid = abs($oid);
|
$oid = abs($oid);
|
||||||
if($oid > 0)
|
if($oid > 0)
|
||||||
|
@ -88,13 +88,14 @@ abstract class Postable extends Attachable
|
||||||
])->group("origin"));
|
])->group("origin"));
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO add pagination
|
function getLikers(int $page = 1, ?int $perPage = NULL): \Traversable
|
||||||
function getLikers(): \Traversable
|
|
||||||
{
|
{
|
||||||
|
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
||||||
|
|
||||||
$sel = DB::i()->getContext()->table("likes")->where([
|
$sel = DB::i()->getContext()->table("likes")->where([
|
||||||
"model" => static::class,
|
"model" => static::class,
|
||||||
"target" => $this->getRecord()->id,
|
"target" => $this->getRecord()->id,
|
||||||
]);
|
])->page($page, $perPage);
|
||||||
|
|
||||||
foreach($sel as $like)
|
foreach($sel as $like)
|
||||||
yield (new Users)->get($like->origin);
|
yield (new Users)->get($like->origin);
|
||||||
|
@ -130,10 +131,15 @@ abstract class Postable extends Attachable
|
||||||
"target" => $this->getRecord()->id,
|
"target" => $this->getRecord()->id,
|
||||||
];
|
];
|
||||||
|
|
||||||
if($liked)
|
if($liked) {
|
||||||
DB::i()->getContext()->table("likes")->insert($searchData);
|
if(!$this->hasLikeFrom($user)) {
|
||||||
else
|
DB::i()->getContext()->table("likes")->insert($searchData);
|
||||||
DB::i()->getContext()->table("likes")->where($searchData)->delete();
|
}
|
||||||
|
} else {
|
||||||
|
if($this->hasLikeFrom($user)) {
|
||||||
|
DB::i()->getContext()->table("likes")->where($searchData)->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasLikeFrom(User $user): bool
|
function hasLikeFrom(User $user): bool
|
||||||
|
@ -152,7 +158,7 @@ abstract class Postable extends Attachable
|
||||||
throw new ISE("Setting virtual id manually is forbidden");
|
throw new ISE("Setting virtual id manually is forbidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
function save(): void
|
function save(?bool $log = false): void
|
||||||
{
|
{
|
||||||
$vref = $this->upperNodeReferenceColumnName;
|
$vref = $this->upperNodeReferenceColumnName;
|
||||||
|
|
||||||
|
@ -171,7 +177,7 @@ abstract class Postable extends Attachable
|
||||||
$this->stateChanges("edited", time());
|
$this->stateChanges("edited", time());
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
parent::save();
|
parent::save($log);
|
||||||
}
|
}
|
||||||
|
|
||||||
use Traits\TAttachmentHost;
|
use Traits\TAttachmentHost;
|
||||||
|
|
|
@ -5,7 +5,7 @@ use Nette\Database\Table\ActiveRow;
|
||||||
use openvk\Web\Models\RowModel;
|
use openvk\Web\Models\RowModel;
|
||||||
use openvk\Web\Models\Entities\Club;
|
use openvk\Web\Models\Entities\Club;
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
use openvk\Web\Models\Repositories\{Applications, Comments, Notes, Reports, Users, Posts, Photos, Videos, Clubs};
|
use openvk\Web\Models\Repositories\{Applications, Comments, Notes, Reports, Audios, Users, Posts, Photos, Videos, Clubs};
|
||||||
use Chandler\Database\DatabaseConnection as DB;
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
use Nette\InvalidStateException as ISE;
|
use Nette\InvalidStateException as ISE;
|
||||||
use Nette\Database\Table\Selection;
|
use Nette\Database\Table\Selection;
|
||||||
|
@ -74,6 +74,7 @@ class Report extends RowModel
|
||||||
else if ($this->getContentType() == "note") return (new Notes)->get($this->getContentId());
|
else if ($this->getContentType() == "note") return (new Notes)->get($this->getContentId());
|
||||||
else if ($this->getContentType() == "app") return (new Applications)->get($this->getContentId());
|
else if ($this->getContentType() == "app") return (new Applications)->get($this->getContentId());
|
||||||
else if ($this->getContentType() == "user") return (new Users)->get($this->getContentId());
|
else if ($this->getContentType() == "user") return (new Users)->get($this->getContentId());
|
||||||
|
else if ($this->getContentType() == "audio") return (new Audios)->get($this->getContentId());
|
||||||
else return null;
|
else return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Entities\Traits;
|
namespace openvk\Web\Models\Entities\Traits;
|
||||||
use openvk\Web\Models\Entities\Attachable;
|
use openvk\Web\Models\Entities\{Attachable, Photo};
|
||||||
|
use openvk\Web\Util\Makima\Makima;
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
|
||||||
trait TAttachmentHost
|
trait TAttachmentHost
|
||||||
|
@ -29,6 +30,46 @@ trait TAttachmentHost
|
||||||
yield $repo->get($rel->attachable_id);
|
yield $repo->get($rel->attachable_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getChildrenWithLayout(int $w, int $h = -1): object
|
||||||
|
{
|
||||||
|
if($h < 0)
|
||||||
|
$h = $w;
|
||||||
|
|
||||||
|
$children = $this->getChildren();
|
||||||
|
$skipped = $photos = $result = [];
|
||||||
|
foreach($children as $child) {
|
||||||
|
if($child instanceof Photo) {
|
||||||
|
$photos[] = $child;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$skipped[] = $child;
|
||||||
|
}
|
||||||
|
|
||||||
|
$height = "unset";
|
||||||
|
$width = $w;
|
||||||
|
if(sizeof($photos) < 2) {
|
||||||
|
if(isset($photos[0]))
|
||||||
|
$result[] = ["100%", "unset", $photos[0], "unset"];
|
||||||
|
} else {
|
||||||
|
$mak = new Makima($photos);
|
||||||
|
$layout = $mak->computeMasonryLayout($w, $h);
|
||||||
|
$height = $layout->height;
|
||||||
|
$width = $layout->width;
|
||||||
|
for($i = 0; $i < sizeof($photos); $i++) {
|
||||||
|
$tile = $layout->tiles[$i];
|
||||||
|
$result[] = [$tile->width . "px", $tile->height . "px", $photos[$i], "left"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"width" => $width . "px",
|
||||||
|
"height" => $height . "px",
|
||||||
|
"tiles" => $result,
|
||||||
|
"extras" => $skipped,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
function attach(Attachable $attachment): void
|
function attach(Attachable $attachment): void
|
||||||
{
|
{
|
||||||
|
|
38
Web/Models/Entities/Traits/TAudioStatuses.php
Normal file
38
Web/Models/Entities/Traits/TAudioStatuses.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities\Traits;
|
||||||
|
use openvk\Web\Models\Repositories\Audios;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
|
||||||
|
trait TAudioStatuses
|
||||||
|
{
|
||||||
|
function isBroadcastEnabled(): bool
|
||||||
|
{
|
||||||
|
if($this->getRealId() < 0) return true;
|
||||||
|
return (bool) $this->getRecord()->audio_broadcast_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentAudioStatus()
|
||||||
|
{
|
||||||
|
if(!$this->isBroadcastEnabled()) return NULL;
|
||||||
|
|
||||||
|
$audioId = $this->getRecord()->last_played_track;
|
||||||
|
|
||||||
|
if(!$audioId) return NULL;
|
||||||
|
$audio = (new Audios)->get($audioId);
|
||||||
|
|
||||||
|
if(!$audio || $audio->isDeleted())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
$listensTable = DatabaseConnection::i()->getContext()->table("audio_listens");
|
||||||
|
$lastListen = $listensTable->where([
|
||||||
|
"entity" => $this->getRealId(),
|
||||||
|
"audio" => $audio->getId(),
|
||||||
|
"time >" => (time() - $audio->getLength()) - 10,
|
||||||
|
])->fetch();
|
||||||
|
|
||||||
|
if($lastListen)
|
||||||
|
return $audio;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,16 @@ use openvk\Web\Models\Entities\User;
|
||||||
|
|
||||||
trait TOwnable
|
trait TOwnable
|
||||||
{
|
{
|
||||||
|
function canBeViewedBy(?User $user = NULL): bool
|
||||||
|
{
|
||||||
|
# TODO: #950
|
||||||
|
if($this->isDeleted()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function canBeModifiedBy(User $user): bool
|
function canBeModifiedBy(User $user): bool
|
||||||
{
|
{
|
||||||
if(method_exists($this, "isCreatedBySystem"))
|
if(method_exists($this, "isCreatedBySystem"))
|
||||||
|
|
|
@ -4,7 +4,7 @@ use morphos\Gender;
|
||||||
use openvk\Web\Themes\{Themepack, Themepacks};
|
use openvk\Web\Themes\{Themepack, Themepacks};
|
||||||
use openvk\Web\Util\DateTime;
|
use openvk\Web\Util\DateTime;
|
||||||
use openvk\Web\Models\RowModel;
|
use openvk\Web\Models\RowModel;
|
||||||
use openvk\Web\Models\Entities\{Photo, Message, Correspondence, Gift};
|
use openvk\Web\Models\Entities\{Photo, Message, Correspondence, Gift, Audio};
|
||||||
use openvk\Web\Models\Repositories\{Applications, Bans, Comments, Notes, Posts, Users, Clubs, Albums, Gifts, Notifications, Videos, Photos};
|
use openvk\Web\Models\Repositories\{Applications, Bans, Comments, Notes, Posts, Users, Clubs, Albums, Gifts, Notifications, Videos, Photos};
|
||||||
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||||
use Nette\Database\Table\ActiveRow;
|
use Nette\Database\Table\ActiveRow;
|
||||||
|
@ -190,7 +190,7 @@ class User extends RowModel
|
||||||
function getMorphedName(string $case = "genitive", bool $fullName = true): string
|
function getMorphedName(string $case = "genitive", bool $fullName = true): string
|
||||||
{
|
{
|
||||||
$name = $fullName ? ($this->getLastName() . " " . $this->getFirstName()) : $this->getFirstName();
|
$name = $fullName ? ($this->getLastName() . " " . $this->getFirstName()) : $this->getFirstName();
|
||||||
if(!preg_match("%^[А-яё\-]+$%", $name))
|
if(!preg_match("%[А-яё\-]+$%", $name))
|
||||||
return $name; # name is probably not russian
|
return $name; # name is probably not russian
|
||||||
|
|
||||||
$inflected = inflectName($name, $case, $this->isFemale() ? Gender::FEMALE : Gender::MALE);
|
$inflected = inflectName($name, $case, $this->isFemale() ? Gender::FEMALE : Gender::MALE);
|
||||||
|
@ -348,10 +348,11 @@ class User extends RowModel
|
||||||
return $this->getRecord()->marital_status;
|
return $this->getRecord()->marital_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLocalizedMaritalStatus(): string
|
function getLocalizedMaritalStatus(?bool $prefix = false): string
|
||||||
{
|
{
|
||||||
$status = $this->getMaritalStatus();
|
$status = $this->getMaritalStatus();
|
||||||
$string = "relationship_$status";
|
$string = "relationship_$status";
|
||||||
|
if ($prefix) $string .= "_prefix";
|
||||||
if($this->isFemale()) {
|
if($this->isFemale()) {
|
||||||
$res = tr($string . "_fem");
|
$res = tr($string . "_fem");
|
||||||
if($res != ("@" . $string . "_fem"))
|
if($res != ("@" . $string . "_fem"))
|
||||||
|
@ -361,6 +362,17 @@ class User extends RowModel
|
||||||
return tr($string);
|
return tr($string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMaritalStatusUser(): ?User
|
||||||
|
{
|
||||||
|
if (!$this->getRecord()->marital_status_user) return NULL;
|
||||||
|
return (new Users)->get($this->getRecord()->marital_status_user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMaritalStatusUserPrefix(): ?string
|
||||||
|
{
|
||||||
|
return $this->getLocalizedMaritalStatus(true);
|
||||||
|
}
|
||||||
|
|
||||||
function getContactEmail(): ?string
|
function getContactEmail(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->email_contact;
|
return $this->getRecord()->email_contact;
|
||||||
|
@ -455,6 +467,7 @@ class User extends RowModel
|
||||||
"length" => 1,
|
"length" => 1,
|
||||||
"mappings" => [
|
"mappings" => [
|
||||||
"photos",
|
"photos",
|
||||||
|
"audios",
|
||||||
"videos",
|
"videos",
|
||||||
"messages",
|
"messages",
|
||||||
"notes",
|
"notes",
|
||||||
|
@ -462,7 +475,7 @@ class User extends RowModel
|
||||||
"news",
|
"news",
|
||||||
"links",
|
"links",
|
||||||
"poster",
|
"poster",
|
||||||
"apps"
|
"apps",
|
||||||
],
|
],
|
||||||
])->get($id);
|
])->get($id);
|
||||||
}
|
}
|
||||||
|
@ -482,6 +495,7 @@ class User extends RowModel
|
||||||
"friends.add",
|
"friends.add",
|
||||||
"wall.write",
|
"wall.write",
|
||||||
"messages.write",
|
"messages.write",
|
||||||
|
"audios.read",
|
||||||
],
|
],
|
||||||
])->get($id);
|
])->get($id);
|
||||||
}
|
}
|
||||||
|
@ -494,6 +508,9 @@ class User extends RowModel
|
||||||
else if($user->getId() === $this->getId())
|
else if($user->getId() === $this->getId())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if($permission != "messages.write" && !$this->canBeViewedBy($user))
|
||||||
|
return false;
|
||||||
|
|
||||||
switch($permStatus) {
|
switch($permStatus) {
|
||||||
case User::PRIVACY_ONLY_FRIENDS:
|
case User::PRIVACY_ONLY_FRIENDS:
|
||||||
return $this->getSubscriptionStatus($user) === User::SUBSCRIPTION_MUTUAL;
|
return $this->getSubscriptionStatus($user) === User::SUBSCRIPTION_MUTUAL;
|
||||||
|
@ -720,8 +737,8 @@ class User extends RowModel
|
||||||
|
|
||||||
for($i = 0; $i < 10 - $this->get2faBackupCodeCount(); $i++) {
|
for($i = 0; $i < 10 - $this->get2faBackupCodeCount(); $i++) {
|
||||||
$codes[] = [
|
$codes[] = [
|
||||||
owner => $this->getId(),
|
"owner" => $this->getId(),
|
||||||
code => random_int(10000000, 99999999)
|
"code" => random_int(10000000, 99999999)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,7 +798,29 @@ class User extends RowModel
|
||||||
|
|
||||||
function isFemale(): bool
|
function isFemale(): bool
|
||||||
{
|
{
|
||||||
return (bool) $this->getRecord()->sex;
|
return $this->getRecord()->sex == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNeutral(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->sex == 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLocalizedPronouns(): string
|
||||||
|
{
|
||||||
|
switch ($this->getRecord()->sex) {
|
||||||
|
case 0:
|
||||||
|
return tr('male');
|
||||||
|
case 1:
|
||||||
|
return tr('female');
|
||||||
|
case 2:
|
||||||
|
return tr('neutral');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPronouns(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->sex;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isVerified(): bool
|
function isVerified(): bool
|
||||||
|
@ -1010,6 +1049,7 @@ class User extends RowModel
|
||||||
"friends.add",
|
"friends.add",
|
||||||
"wall.write",
|
"wall.write",
|
||||||
"messages.write",
|
"messages.write",
|
||||||
|
"audios.read",
|
||||||
],
|
],
|
||||||
])->set($id, $status)->toInteger());
|
])->set($id, $status)->toInteger());
|
||||||
}
|
}
|
||||||
|
@ -1020,6 +1060,7 @@ class User extends RowModel
|
||||||
"length" => 1,
|
"length" => 1,
|
||||||
"mappings" => [
|
"mappings" => [
|
||||||
"photos",
|
"photos",
|
||||||
|
"audios",
|
||||||
"videos",
|
"videos",
|
||||||
"messages",
|
"messages",
|
||||||
"notes",
|
"notes",
|
||||||
|
@ -1027,7 +1068,7 @@ class User extends RowModel
|
||||||
"news",
|
"news",
|
||||||
"links",
|
"links",
|
||||||
"poster",
|
"poster",
|
||||||
"apps"
|
"apps",
|
||||||
],
|
],
|
||||||
])->set($id, (int) $status)->toInteger();
|
])->set($id, (int) $status)->toInteger();
|
||||||
|
|
||||||
|
@ -1222,8 +1263,60 @@ class User extends RowModel
|
||||||
}
|
}
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getProfileType(): int
|
||||||
|
{
|
||||||
|
# 0 — открытый профиль, 1 — закрытый
|
||||||
|
return $this->getRecord()->profile_type;
|
||||||
|
}
|
||||||
|
|
||||||
function toVkApiStruct(): object
|
function canBeViewedBy(?User $user = NULL): bool
|
||||||
|
{
|
||||||
|
if(!is_null($user)) {
|
||||||
|
if($this->getId() == $user->getId()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($user->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->getProfileType() == 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if($user->getSubscriptionStatus($this) == User::SUBSCRIPTION_MUTUAL) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if($this->getProfileType() == 0) {
|
||||||
|
if($this->getPrivacySetting("page.read") == 3) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isClosed()
|
||||||
|
{
|
||||||
|
return (bool) $this->getProfileType();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRealId()
|
||||||
|
{
|
||||||
|
return $this->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toVkApiStruct(?User $user = NULL): object
|
||||||
{
|
{
|
||||||
$res = (object) [];
|
$res = (object) [];
|
||||||
|
|
||||||
|
@ -1237,12 +1330,13 @@ class User extends RowModel
|
||||||
$res->photo_id = !is_null($this->getAvatarPhoto()) ? $this->getAvatarPhoto()->getPrettyId() : NULL;
|
$res->photo_id = !is_null($this->getAvatarPhoto()) ? $this->getAvatarPhoto()->getPrettyId() : NULL;
|
||||||
# TODO: Perenesti syuda vsyo ostalnoyie
|
# TODO: Perenesti syuda vsyo ostalnoyie
|
||||||
|
|
||||||
return $res;
|
$res->is_closed = $this->isClosed();
|
||||||
}
|
|
||||||
|
|
||||||
function getRealId()
|
if(!is_null($user)) {
|
||||||
{
|
$res->can_access_closed = (bool)$this->canBeViewedBy($user);
|
||||||
return $this->getId();
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getIgnoredSources(int $page = 1, int $perPage = 10, bool $onlyIds = false)
|
function getIgnoredSources(int $page = 1, int $perPage = 10, bool $onlyIds = false)
|
||||||
|
@ -1297,6 +1391,46 @@ class User extends RowModel
|
||||||
return sizeof(DatabaseConnection::i()->getContext()->table("ignored_sources")->where("ignored_source", $this->getId()));
|
return sizeof(DatabaseConnection::i()->getContext()->table("ignored_sources")->where("ignored_source", $this->getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAudiosCollectionSize()
|
||||||
|
{
|
||||||
|
return (new \openvk\Web\Models\Repositories\Audios)->getUserCollectionSize($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBroadcastList(string $filter = "friends", bool $shuffle = false)
|
||||||
|
{
|
||||||
|
$dbContext = DatabaseConnection::i()->getContext();
|
||||||
|
$entityIds = [];
|
||||||
|
$query = $dbContext->table("subscriptions")->where("follower", $this->getRealId());
|
||||||
|
|
||||||
|
if($filter != "all")
|
||||||
|
$query = $query->where("model = ?", "openvk\\Web\\Models\\Entities\\" . ($filter == "groups" ? "Club" : "User"));
|
||||||
|
|
||||||
|
foreach($query as $_rel) {
|
||||||
|
$entityIds[] = $_rel->model == "openvk\\Web\\Models\\Entities\\Club" ? $_rel->target * -1 : $_rel->target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($shuffle) {
|
||||||
|
$shuffleSeed = openssl_random_pseudo_bytes(6);
|
||||||
|
$shuffleSeed = hexdec(bin2hex($shuffleSeed));
|
||||||
|
|
||||||
|
$entityIds = knuth_shuffle($entityIds, $shuffleSeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
$entityIds = array_slice($entityIds, 0, 10);
|
||||||
|
|
||||||
|
$returnArr = [];
|
||||||
|
|
||||||
|
foreach($entityIds as $id) {
|
||||||
|
$entit = $id > 0 ? (new Users)->get($id) : (new Clubs)->get(abs($id));
|
||||||
|
|
||||||
|
if($id > 0 && $entit->isDeleted()) continue;
|
||||||
|
$returnArr[] = $entit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $returnArr;
|
||||||
|
}
|
||||||
|
|
||||||
use Traits\TBackDrops;
|
use Traits\TBackDrops;
|
||||||
use Traits\TSubscribable;
|
use Traits\TSubscribable;
|
||||||
|
use Traits\TAudioStatuses;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Entities;
|
namespace openvk\Web\Models\Entities;
|
||||||
use openvk\Web\Util\Shell\Shell;
|
use openvk\Web\Util\Shell\Shell;
|
||||||
use openvk\Web\Util\Shell\Shell\Exceptions\{ShellUnavailableException, UnknownCommandException};
|
use openvk\Web\Util\Shell\Exceptions\{ShellUnavailableException, UnknownCommandException};
|
||||||
use openvk\Web\Models\VideoDrivers\VideoDriver;
|
use openvk\Web\Models\VideoDrivers\VideoDriver;
|
||||||
use Nette\InvalidStateException as ISE;
|
use Nette\InvalidStateException as ISE;
|
||||||
|
|
||||||
|
@ -115,15 +115,15 @@ class Video extends Media
|
||||||
return $this->getRecord()->owner;
|
return $this->getRecord()->owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getApiStructure(): object
|
function getApiStructure(?User $user = NULL): object
|
||||||
{
|
{
|
||||||
$fromYoutube = $this->getType() == Video::TYPE_EMBED;
|
$fromYoutube = $this->getType() == Video::TYPE_EMBED;
|
||||||
return (object)[
|
$res = (object)[
|
||||||
"type" => "video",
|
"type" => "video",
|
||||||
"video" => [
|
"video" => [
|
||||||
"can_comment" => 1,
|
"can_comment" => 1,
|
||||||
"can_like" => 0, // we don't h-have wikes in videos
|
"can_like" => 1, // we don't h-have wikes in videos
|
||||||
"can_repost" => 0,
|
"can_repost" => 1,
|
||||||
"can_subscribe" => 1,
|
"can_subscribe" => 1,
|
||||||
"can_add_to_faves" => 0,
|
"can_add_to_faves" => 0,
|
||||||
"can_add" => 0,
|
"can_add" => 0,
|
||||||
|
@ -155,21 +155,26 @@ class Video extends Media
|
||||||
"repeat" => 0,
|
"repeat" => 0,
|
||||||
"type" => "video",
|
"type" => "video",
|
||||||
"views" => 0,
|
"views" => 0,
|
||||||
"likes" => [
|
|
||||||
"count" => 0,
|
|
||||||
"user_likes" => 0
|
|
||||||
],
|
|
||||||
"reposts" => [
|
"reposts" => [
|
||||||
"count" => 0,
|
"count" => 0,
|
||||||
"user_reposted" => 0
|
"user_reposted" => 0
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if(!is_null($user)) {
|
||||||
|
$res->video["likes"] = [
|
||||||
|
"count" => $this->getLikesCount(),
|
||||||
|
"user_likes" => $this->hasLikeFrom($user)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toVkApiStruct(): object
|
function toVkApiStruct(?User $user): object
|
||||||
{
|
{
|
||||||
return $this->getApiStructure();
|
return $this->getApiStructure($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLink(string $link): string
|
function setLink(string $link): string
|
||||||
|
@ -219,4 +224,37 @@ class Video extends Media
|
||||||
|
|
||||||
return $video;
|
return $video;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canBeViewedBy(?User $user = NULL): bool
|
||||||
|
{
|
||||||
|
if($this->isDeleted() || $this->getOwner()->isDeleted()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(get_class($this->getOwner()) == "openvk\\Web\\Models\\Entities\\User") {
|
||||||
|
return $this->getOwner()->canBeViewedBy($user) && $this->getOwner()->getPrivacyPermission('videos.read', $user);
|
||||||
|
} else {
|
||||||
|
# Groups doesn't have videos but ok
|
||||||
|
return $this->getOwner()->canBeViewedBy($user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toNotifApiStruct()
|
||||||
|
{
|
||||||
|
$fromYoutube = $this->getType() == Video::TYPE_EMBED;
|
||||||
|
$res = (object)[];
|
||||||
|
|
||||||
|
$res->id = $this->getVirtualId();
|
||||||
|
$res->owner_id = $this->getOwner()->getId();
|
||||||
|
$res->title = $this->getName();
|
||||||
|
$res->description = $this->getDescription();
|
||||||
|
$res->duration = "22";
|
||||||
|
$res->link = "/video".$this->getOwner()->getId()."_".$this->getVirtualId();
|
||||||
|
$res->image = $this->getThumbnailURL();
|
||||||
|
$res->date = $this->getPublicationTime()->timestamp();
|
||||||
|
$res->views = 0;
|
||||||
|
$res->player = !$fromYoutube ? $this->getURL() : $this->getVideoDriver()->getURL();
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,7 @@ class Albums
|
||||||
"owner" => $owner,
|
"owner" => $owner,
|
||||||
"id" => $id
|
"id" => $id
|
||||||
])->fetch();
|
])->fetch();
|
||||||
|
|
||||||
return new Album($album);
|
return $album ? new Album($album) : NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
296
Web/Models/Repositories/Audios.php
Normal file
296
Web/Models/Repositories/Audios.php
Normal file
|
@ -0,0 +1,296 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Repositories;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use openvk\Web\Models\Entities\Audio;
|
||||||
|
use openvk\Web\Models\Entities\Club;
|
||||||
|
use openvk\Web\Models\Entities\Playlist;
|
||||||
|
use openvk\Web\Models\Entities\User;
|
||||||
|
use openvk\Web\Models\Repositories\Util\EntityStream;
|
||||||
|
|
||||||
|
class Audios
|
||||||
|
{
|
||||||
|
private $context;
|
||||||
|
private $audios;
|
||||||
|
private $rels;
|
||||||
|
private $playlists;
|
||||||
|
private $playlistImports;
|
||||||
|
private $playlistRels;
|
||||||
|
|
||||||
|
const ORDER_NEW = 0;
|
||||||
|
const ORDER_POPULAR = 1;
|
||||||
|
|
||||||
|
const VK_ORDER_NEW = 0;
|
||||||
|
const VK_ORDER_LENGTH = 1;
|
||||||
|
const VK_ORDER_POPULAR = 2;
|
||||||
|
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->context = DatabaseConnection::i()->getContext();
|
||||||
|
$this->audios = $this->context->table("audios");
|
||||||
|
$this->rels = $this->context->table("audio_relations");
|
||||||
|
|
||||||
|
$this->playlists = $this->context->table("playlists");
|
||||||
|
$this->playlistImports = $this->context->table("playlist_imports");
|
||||||
|
$this->playlistRels = $this->context->table("playlist_relations");
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(int $id): ?Audio
|
||||||
|
{
|
||||||
|
$audio = $this->audios->get($id);
|
||||||
|
if(!$audio)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return new Audio($audio);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlaylist(int $id): ?Playlist
|
||||||
|
{
|
||||||
|
$playlist = $this->playlists->get($id);
|
||||||
|
if(!$playlist)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return new Playlist($playlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getByOwnerAndVID(int $owner, int $vId): ?Audio
|
||||||
|
{
|
||||||
|
$audio = $this->audios->where([
|
||||||
|
"owner" => $owner,
|
||||||
|
"virtual_id" => $vId,
|
||||||
|
])->fetch();
|
||||||
|
if(!$audio) return NULL;
|
||||||
|
|
||||||
|
return new Audio($audio);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlaylistByOwnerAndVID(int $owner, int $vId): ?Playlist
|
||||||
|
{
|
||||||
|
$playlist = $this->playlists->where([
|
||||||
|
"owner" => $owner,
|
||||||
|
"id" => $vId,
|
||||||
|
])->fetch();
|
||||||
|
if(!$playlist) return NULL;
|
||||||
|
|
||||||
|
return new Playlist($playlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getByEntityID(int $entity, int $offset = 0, ?int $limit = NULL, ?int& $deleted = nullptr): \Traversable
|
||||||
|
{
|
||||||
|
$limit ??= OPENVK_DEFAULT_PER_PAGE;
|
||||||
|
$iter = $this->rels->where("entity", $entity)->limit($limit, $offset)->order("index DESC");
|
||||||
|
foreach($iter as $rel) {
|
||||||
|
$audio = $this->get($rel->audio);
|
||||||
|
if(!$audio || $audio->isDeleted()) {
|
||||||
|
$deleted++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield $audio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlaylistsByEntityId(int $entity, int $offset = 0, ?int $limit = NULL, ?int& $deleted = nullptr): \Traversable
|
||||||
|
{
|
||||||
|
$limit ??= OPENVK_DEFAULT_PER_PAGE;
|
||||||
|
$iter = $this->playlistImports->where("entity", $entity)->limit($limit, $offset);
|
||||||
|
foreach($iter as $rel) {
|
||||||
|
$playlist = $this->getPlaylist($rel->playlist);
|
||||||
|
if(!$playlist || $playlist->isDeleted()) {
|
||||||
|
$deleted++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield $playlist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getByUser(User $user, int $page = 1, ?int $perPage = NULL, ?int& $deleted = nullptr): \Traversable
|
||||||
|
{
|
||||||
|
return $this->getByEntityID($user->getId(), ($perPage * ($page - 1)), $perPage, $deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomThreeAudiosByEntityId(int $id): Array
|
||||||
|
{
|
||||||
|
$iter = $this->rels->where("entity", $id);
|
||||||
|
$ids = [];
|
||||||
|
|
||||||
|
foreach($iter as $it)
|
||||||
|
$ids[] = $it->audio;
|
||||||
|
|
||||||
|
$shuffleSeed = openssl_random_pseudo_bytes(6);
|
||||||
|
$shuffleSeed = hexdec(bin2hex($shuffleSeed));
|
||||||
|
|
||||||
|
$ids = knuth_shuffle($ids, $shuffleSeed);
|
||||||
|
$ids = array_slice($ids, 0, 3);
|
||||||
|
$audios = [];
|
||||||
|
|
||||||
|
foreach($ids as $id) {
|
||||||
|
$audio = $this->get((int)$id);
|
||||||
|
|
||||||
|
if(!$audio || $audio->isDeleted())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$audios[] = $audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $audios;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getByClub(Club $club, int $page = 1, ?int $perPage = NULL, ?int& $deleted = nullptr): \Traversable
|
||||||
|
{
|
||||||
|
return $this->getByEntityID($club->getId() * -1, ($perPage * ($page - 1)), $perPage, $deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlaylistsByUser(User $user, int $page = 1, ?int $perPage = NULL, ?int& $deleted = nullptr): \Traversable
|
||||||
|
{
|
||||||
|
return $this->getPlaylistsByEntityId($user->getId(), ($perPage * ($page - 1)), $perPage, $deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlaylistsByClub(Club $club, int $page = 1, ?int $perPage = NULL, ?int& $deleted = nullptr): \Traversable
|
||||||
|
{
|
||||||
|
return $this->getPlaylistsByEntityId($club->getId() * -1, ($perPage * ($page - 1)), $perPage, $deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCollectionSizeByEntityId(int $id): int
|
||||||
|
{
|
||||||
|
return sizeof($this->rels->where("entity", $id));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserCollectionSize(User $user): int
|
||||||
|
{
|
||||||
|
return sizeof($this->rels->where("entity", $user->getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClubCollectionSize(Club $club): int
|
||||||
|
{
|
||||||
|
return sizeof($this->rels->where("entity", $club->getId() * -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserPlaylistsCount(User $user): int
|
||||||
|
{
|
||||||
|
return sizeof($this->playlistImports->where("entity", $user->getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClubPlaylistsCount(Club $club): int
|
||||||
|
{
|
||||||
|
return sizeof($this->playlistImports->where("entity", $club->getId() * -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getByUploader(User $user): EntityStream
|
||||||
|
{
|
||||||
|
$search = $this->audios->where([
|
||||||
|
"owner" => $user->getId(),
|
||||||
|
"deleted" => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return new EntityStream("Audio", $search);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGlobal(int $order, ?string $genreId = NULL): EntityStream
|
||||||
|
{
|
||||||
|
$search = $this->audios->where([
|
||||||
|
"deleted" => 0,
|
||||||
|
"unlisted" => 0,
|
||||||
|
"withdrawn" => 0,
|
||||||
|
])->order($order == Audios::ORDER_NEW ? "created DESC" : "listens DESC");
|
||||||
|
|
||||||
|
if(!is_null($genreId))
|
||||||
|
$search = $search->where("genre", $genreId);
|
||||||
|
|
||||||
|
return new EntityStream("Audio", $search);
|
||||||
|
}
|
||||||
|
|
||||||
|
function search(string $query, int $sortMode = 0, bool $performerOnly = false, bool $withLyrics = false): EntityStream
|
||||||
|
{
|
||||||
|
$columns = $performerOnly ? "performer" : "performer, name";
|
||||||
|
$order = (["created", "length", "listens"][$sortMode] ?? "") . " DESC";
|
||||||
|
|
||||||
|
$search = $this->audios->where([
|
||||||
|
"unlisted" => 0,
|
||||||
|
"deleted" => 0,
|
||||||
|
])->where("MATCH ($columns) AGAINST (? WITH QUERY EXPANSION)", $query)->order($order);
|
||||||
|
|
||||||
|
if($withLyrics)
|
||||||
|
$search = $search->where("lyrics IS NOT NULL");
|
||||||
|
|
||||||
|
return new EntityStream("Audio", $search);
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchPlaylists(string $query): EntityStream
|
||||||
|
{
|
||||||
|
$search = $this->playlists->where([
|
||||||
|
"deleted" => 0,
|
||||||
|
])->where("MATCH (`name`, `description`) AGAINST (? IN BOOLEAN MODE)", $query);
|
||||||
|
|
||||||
|
return new EntityStream("Playlist", $search);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNew(): EntityStream
|
||||||
|
{
|
||||||
|
return new EntityStream("Audio", $this->audios->where("created >= " . (time() - 259200))->where(["withdrawn" => 0, "deleted" => 0, "unlisted" => 0])->order("created DESC")->limit(25));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPopular(): EntityStream
|
||||||
|
{
|
||||||
|
return new EntityStream("Audio", $this->audios->where("listens > 0")->where(["withdrawn" => 0, "deleted" => 0, "unlisted" => 0])->order("listens DESC")->limit(25));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAdded(int $user_id, int $audio_id): bool
|
||||||
|
{
|
||||||
|
return !is_null($this->rels->where([
|
||||||
|
"entity" => $user_id,
|
||||||
|
"audio" => $audio_id
|
||||||
|
])->fetch());
|
||||||
|
}
|
||||||
|
|
||||||
|
function find(string $query, array $pars = [], string $sort = "id DESC", int $page = 1, ?int $perPage = NULL): \Traversable
|
||||||
|
{
|
||||||
|
$query = "%$query%";
|
||||||
|
$result = $this->audios->where([
|
||||||
|
"unlisted" => 0,
|
||||||
|
"deleted" => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$notNullParams = [];
|
||||||
|
|
||||||
|
foreach($pars as $paramName => $paramValue)
|
||||||
|
if($paramName != "before" && $paramName != "after" && $paramName != "only_performers")
|
||||||
|
$paramValue != NULL ? $notNullParams+=["$paramName" => "%$paramValue%"] : NULL;
|
||||||
|
else
|
||||||
|
$paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
|
||||||
|
|
||||||
|
$nnparamsCount = sizeof($notNullParams);
|
||||||
|
|
||||||
|
if($notNullParams["only_performers"] == "1") {
|
||||||
|
$result->where("performer LIKE ?", $query);
|
||||||
|
} else {
|
||||||
|
$result->where("name LIKE ? OR performer LIKE ?", $query, $query);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($nnparamsCount > 0) {
|
||||||
|
foreach($notNullParams as $paramName => $paramValue) {
|
||||||
|
switch($paramName) {
|
||||||
|
case "before":
|
||||||
|
$result->where("created < ?", $paramValue);
|
||||||
|
break;
|
||||||
|
case "after":
|
||||||
|
$result->where("created > ?", $paramValue);
|
||||||
|
break;
|
||||||
|
case "with_lyrics":
|
||||||
|
$result->where("lyrics IS NOT NULL");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Util\EntityStream("Audio", $result->order($sort));
|
||||||
|
}
|
||||||
|
|
||||||
|
function findPlaylists(string $query, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||||
|
{
|
||||||
|
$query = "%$query%";
|
||||||
|
$result = $this->playlists->where("name LIKE ?", $query);
|
||||||
|
|
||||||
|
return new Util\EntityStream("Playlist", $result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,4 +45,9 @@ class ChandlerGroups
|
||||||
{
|
{
|
||||||
foreach($this->perms->where("group", $UUID) as $perm) yield $perm;
|
foreach($this->perms->where("group", $UUID) as $perm) yield $perm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isUserAMember(string $GID, string $UID): bool
|
||||||
|
{
|
||||||
|
return ($this->context->query("SELECT * FROM `ChandlerACLRelations` WHERE `group` = ? AND `user` = ?", $GID, $UID)) !== NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,6 @@ class Messages
|
||||||
$query = file_get_contents(__DIR__ . "/../sql/get-correspondencies-count.tsql");
|
$query = file_get_contents(__DIR__ . "/../sql/get-correspondencies-count.tsql");
|
||||||
DatabaseConnection::i()->getConnection()->query(file_get_contents(__DIR__ . "/../sql/mysql-msg-fix.tsql"));
|
DatabaseConnection::i()->getConnection()->query(file_get_contents(__DIR__ . "/../sql/mysql-msg-fix.tsql"));
|
||||||
$count = DatabaseConnection::i()->getConnection()->query($query, $id, $class, $id, $class)->fetch()->cnt;
|
$count = DatabaseConnection::i()->getConnection()->query($query, $id, $class, $id, $class)->fetch()->cnt;
|
||||||
bdump($count);
|
|
||||||
return $count;
|
return $count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,14 +33,26 @@ class Photos
|
||||||
return new Photo($photo);
|
return new Photo($photo);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEveryUserPhoto(User $user): \Traversable
|
function getEveryUserPhoto(User $user, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||||
{
|
{
|
||||||
|
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||||
$photos = $this->photos->where([
|
$photos = $this->photos->where([
|
||||||
"owner" => $user->getId()
|
"owner" => $user->getId(),
|
||||||
]);
|
"deleted" => 0
|
||||||
|
])->order("id DESC");
|
||||||
|
|
||||||
foreach($photos as $photo) {
|
foreach($photos->page($page, $perPage) as $photo) {
|
||||||
yield new Photo($photo);
|
yield new Photo($photo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getUserPhotosCount(User $user)
|
||||||
|
{
|
||||||
|
$photos = $this->photos->where([
|
||||||
|
"owner" => $user->getId(),
|
||||||
|
"deleted" => 0
|
||||||
|
]);
|
||||||
|
|
||||||
|
return sizeof($photos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,14 +58,59 @@ class Posts
|
||||||
}
|
}
|
||||||
|
|
||||||
$sel = $this->posts->where([
|
$sel = $this->posts->where([
|
||||||
"wall" => $user,
|
"wall" => $user,
|
||||||
"pinned" => false,
|
"pinned" => false,
|
||||||
"deleted" => false,
|
"deleted" => false,
|
||||||
|
"suggested" => 0,
|
||||||
])->order("created DESC")->limit($perPage, $offset);
|
])->order("created DESC")->limit($perPage, $offset);
|
||||||
|
|
||||||
foreach($sel as $post)
|
foreach($sel as $post)
|
||||||
yield new Post($post);
|
yield new Post($post);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getOwnersPostsFromWall(int $user, int $page = 1, ?int $perPage = NULL, ?int $offset = NULL): \Traversable
|
||||||
|
{
|
||||||
|
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
||||||
|
$offset ??= $perPage * ($page - 1);
|
||||||
|
|
||||||
|
$sel = $this->posts->where([
|
||||||
|
"wall" => $user,
|
||||||
|
"deleted" => false,
|
||||||
|
"suggested" => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if($user > 0)
|
||||||
|
$sel->where("owner", $user);
|
||||||
|
else
|
||||||
|
$sel->where("flags !=", 0);
|
||||||
|
|
||||||
|
$sel->order("created DESC")->limit($perPage, $offset);
|
||||||
|
|
||||||
|
foreach($sel as $post)
|
||||||
|
yield new Post($post);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOthersPostsFromWall(int $user, int $page = 1, ?int $perPage = NULL, ?int $offset = NULL): \Traversable
|
||||||
|
{
|
||||||
|
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
||||||
|
$offset ??= $perPage * ($page - 1);
|
||||||
|
|
||||||
|
$sel = $this->posts->where([
|
||||||
|
"wall" => $user,
|
||||||
|
"deleted" => false,
|
||||||
|
"suggested" => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if($user > 0)
|
||||||
|
$sel->where("owner !=", $user);
|
||||||
|
else
|
||||||
|
$sel->where("flags", 0);
|
||||||
|
|
||||||
|
$sel->order("created DESC")->limit($perPage, $offset);
|
||||||
|
|
||||||
|
foreach($sel as $post)
|
||||||
|
yield new Post($post);
|
||||||
|
}
|
||||||
|
|
||||||
function getPostsByHashtag(string $hashtag, int $page = 1, ?int $perPage = NULL): \Traversable
|
function getPostsByHashtag(string $hashtag, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||||
{
|
{
|
||||||
|
@ -74,6 +119,7 @@ class Posts
|
||||||
->where("MATCH (content) AGAINST (? IN BOOLEAN MODE)", "+$hashtag")
|
->where("MATCH (content) AGAINST (? IN BOOLEAN MODE)", "+$hashtag")
|
||||||
->where("deleted", 0)
|
->where("deleted", 0)
|
||||||
->order("created DESC")
|
->order("created DESC")
|
||||||
|
->where("suggested", 0)
|
||||||
->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
|
->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
|
||||||
|
|
||||||
foreach($sel as $post)
|
foreach($sel as $post)
|
||||||
|
@ -85,14 +131,22 @@ class Posts
|
||||||
$hashtag = "#$hashtag";
|
$hashtag = "#$hashtag";
|
||||||
$sel = $this->posts
|
$sel = $this->posts
|
||||||
->where("content LIKE ?", "%$hashtag%")
|
->where("content LIKE ?", "%$hashtag%")
|
||||||
->where("deleted", 0);
|
->where("deleted", 0)
|
||||||
|
->where("suggested", 0);
|
||||||
|
|
||||||
return sizeof($sel);
|
return sizeof($sel);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPostById(int $wall, int $post): ?Post
|
function getPostById(int $wall, int $post, bool $forceSuggestion = false): ?Post
|
||||||
{
|
{
|
||||||
$post = $this->posts->where(['wall' => $wall, 'virtual_id' => $post])->fetch();
|
$post = $this->posts->where(['wall' => $wall, 'virtual_id' => $post]);
|
||||||
|
|
||||||
|
if(!$forceSuggestion) {
|
||||||
|
$post->where("suggested", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$post = $post->fetch();
|
||||||
|
|
||||||
if(!is_null($post))
|
if(!is_null($post))
|
||||||
return new Post($post);
|
return new Post($post);
|
||||||
else
|
else
|
||||||
|
@ -112,7 +166,7 @@ class Posts
|
||||||
else
|
else
|
||||||
$paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
|
$paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
|
||||||
|
|
||||||
$result = $this->posts->where("content LIKE ?", $query)->where("deleted", 0);
|
$result = $this->posts->where("content LIKE ?", $query)->where("deleted", 0)->where("suggested", 0);
|
||||||
$nnparamsCount = sizeof($notNullParams);
|
$nnparamsCount = sizeof($notNullParams);
|
||||||
|
|
||||||
if($nnparamsCount > 0) {
|
if($nnparamsCount > 0) {
|
||||||
|
@ -134,7 +188,66 @@ class Posts
|
||||||
|
|
||||||
function getPostCountOnUserWall(int $user): int
|
function getPostCountOnUserWall(int $user): int
|
||||||
{
|
{
|
||||||
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0]));
|
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0, "suggested" => 0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOwnersCountOnUserWall(int $user): int
|
||||||
|
{
|
||||||
|
if($user > 0)
|
||||||
|
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0, "owner" => $user]));
|
||||||
|
else
|
||||||
|
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0, "suggested" => 0])->where("flags !=", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOthersCountOnUserWall(int $user): int
|
||||||
|
{
|
||||||
|
if($user > 0)
|
||||||
|
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0])->where("owner !=", $user));
|
||||||
|
else
|
||||||
|
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0, "suggested" => 0])->where("flags", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSuggestedPosts(int $club, int $page = 1, ?int $perPage = NULL, ?int $offset = NULL): \Traversable
|
||||||
|
{
|
||||||
|
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
||||||
|
$offset ??= $perPage * ($page - 1);
|
||||||
|
|
||||||
|
$sel = $this->posts
|
||||||
|
->where("deleted", 0)
|
||||||
|
->where("wall", $club * -1)
|
||||||
|
->order("created DESC")
|
||||||
|
->where("suggested", 1)
|
||||||
|
->limit($perPage, $offset);
|
||||||
|
|
||||||
|
foreach($sel as $post)
|
||||||
|
yield new Post($post);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSuggestedPostsCount(int $club)
|
||||||
|
{
|
||||||
|
return sizeof($this->posts->where(["wall" => $club * -1, "deleted" => 0, "suggested" => 1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSuggestedPostsByUser(int $club, int $user, int $page = 1, ?int $perPage = NULL, ?int $offset = NULL): \Traversable
|
||||||
|
{
|
||||||
|
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
||||||
|
$offset ??= $perPage * ($page - 1);
|
||||||
|
|
||||||
|
$sel = $this->posts
|
||||||
|
->where("deleted", 0)
|
||||||
|
->where("wall", $club * -1)
|
||||||
|
->where("owner", $user)
|
||||||
|
->order("created DESC")
|
||||||
|
->where("suggested", 1)
|
||||||
|
->limit($perPage, $offset);
|
||||||
|
|
||||||
|
foreach($sel as $post)
|
||||||
|
yield new Post($post);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSuggestedPostsCountByUser(int $club, int $user): int
|
||||||
|
{
|
||||||
|
return sizeof($this->posts->where(["wall" => $club * -1, "deleted" => 0, "suggested" => 1, "owner" => $user]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCount(): int
|
function getCount(): int
|
||||||
|
|
|
@ -128,6 +128,9 @@ class Users
|
||||||
case "doNotSearchMe":
|
case "doNotSearchMe":
|
||||||
$result->where("id !=", $paramValue);
|
$result->where("id !=", $paramValue);
|
||||||
break;
|
break;
|
||||||
|
case "doNotSearchPrivate":
|
||||||
|
$result->where("profile_type", 0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,4 +77,11 @@ class Videos
|
||||||
|
|
||||||
return new Util\EntityStream("Video", $result->order("$sort"));
|
return new Util\EntityStream("Video", $result->order("$sort"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLastVideo(User $user)
|
||||||
|
{
|
||||||
|
$video = $this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0])->order("id DESC")->fetch();
|
||||||
|
|
||||||
|
return new Video($video);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
39
Web/Models/shell/processAudio.ps1
Normal file
39
Web/Models/shell/processAudio.ps1
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
$ovkRoot = $args[0]
|
||||||
|
$storageDir = $args[1]
|
||||||
|
$fileHash = $args[2]
|
||||||
|
$hashPart = $fileHash.substring(0, 2)
|
||||||
|
$filename = $args[3]
|
||||||
|
$audioFile = [System.IO.Path]::GetTempFileName()
|
||||||
|
$temp = [System.IO.Path]::GetTempFileName()
|
||||||
|
|
||||||
|
$keyID = $args[4]
|
||||||
|
$key = $args[5]
|
||||||
|
$token = $args[6]
|
||||||
|
$seg = $args[7]
|
||||||
|
|
||||||
|
$shell = Get-WmiObject Win32_process -filter "ProcessId = $PID"
|
||||||
|
$shell.SetPriority(16384) # because there's no "nice" program in Windows we just set a lower priority for entire tree
|
||||||
|
|
||||||
|
Remove-Item $temp
|
||||||
|
Remove-Item $audioFile
|
||||||
|
New-Item -ItemType "directory" $temp
|
||||||
|
New-Item -ItemType "directory" ("$temp/$fileHash" + '_fragments')
|
||||||
|
New-Item -ItemType "directory" ("$storageDir/$hashPart/$fileHash" + '_fragments')
|
||||||
|
Set-Location -Path $temp
|
||||||
|
|
||||||
|
Move-Item $filename $audioFile
|
||||||
|
ffmpeg -i $audioFile -f dash -encryption_scheme cenc-aes-ctr -encryption_key $key `
|
||||||
|
-encryption_kid $keyID -map 0:a -vn -c:a aac -ar 44100 -seg_duration $seg `
|
||||||
|
-use_timeline 1 -use_template 1 -init_seg_name ($fileHash + '_fragments/0_0.$ext$') `
|
||||||
|
-media_seg_name ($fileHash + '_fragments/chunk$Number%06d$_$RepresentationID$.$ext$') -adaptation_sets 'id=0,streams=a' `
|
||||||
|
"$fileHash.mpd"
|
||||||
|
|
||||||
|
ffmpeg -i $audioFile -vn -ar 44100 "original_$token.mp3"
|
||||||
|
Move-Item "original_$token.mp3" ($fileHash + '_fragments')
|
||||||
|
|
||||||
|
Get-ChildItem -Path ($fileHash + '_fragments/*') | Move-Item -Destination ("$storageDir/$hashPart/$fileHash" + '_fragments')
|
||||||
|
Move-Item -Path ("$fileHash.mpd") -Destination "$storageDir/$hashPart"
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
Remove-Item -Recurse $temp
|
||||||
|
Remove-Item $audioFile
|
35
Web/Models/shell/processAudio.sh
Normal file
35
Web/Models/shell/processAudio.sh
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
ovkRoot=$1
|
||||||
|
storageDir=$2
|
||||||
|
fileHash=$3
|
||||||
|
hashPart=$(echo $fileHash | cut -c1-2)
|
||||||
|
filename=$4
|
||||||
|
audioFile=$(mktemp)
|
||||||
|
temp=$(mktemp -d)
|
||||||
|
|
||||||
|
keyID=$5
|
||||||
|
key=$6
|
||||||
|
token=$7
|
||||||
|
seg=$8
|
||||||
|
|
||||||
|
trap 'rm -f "$temp" "$audioFile"' EXIT
|
||||||
|
|
||||||
|
mkdir -p "$temp/$fileHash"_fragments
|
||||||
|
mkdir -p "$storageDir/$hashPart/$fileHash"_fragments
|
||||||
|
cd "$temp"
|
||||||
|
|
||||||
|
mv "$filename" "$audioFile"
|
||||||
|
ffmpeg -i "$audioFile" -f dash -encryption_scheme cenc-aes-ctr -encryption_key "$key" \
|
||||||
|
-encryption_kid "$keyID" -map 0 -vn -c:a aac -ar 44100 -seg_duration "$seg" \
|
||||||
|
-use_timeline 1 -use_template 1 -init_seg_name "$fileHash"_fragments/0_0."\$ext\$" \
|
||||||
|
-media_seg_name "$fileHash"_fragments/chunk"\$Number"%06d\$_"\$RepresentationID\$"."\$ext\$" -adaptation_sets 'id=0,streams=a' \
|
||||||
|
"$fileHash.mpd"
|
||||||
|
|
||||||
|
ffmpeg -i "$audioFile" -vn -ar 44100 "original_$token.mp3"
|
||||||
|
mv "original_$token.mp3" "$fileHash"_fragments
|
||||||
|
|
||||||
|
mv "$fileHash"_fragments "$storageDir/$hashPart"
|
||||||
|
mv "$fileHash.mpd" "$storageDir/$hashPart"
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
rm -rf "$temp"
|
||||||
|
rm -f "$audioFile"
|
|
@ -109,6 +109,10 @@ final class AboutPresenter extends OpenVKPresenter
|
||||||
. "# lack of rights to access the admin panel)\n\n"
|
. "# lack of rights to access the admin panel)\n\n"
|
||||||
. "User-Agent: *\n"
|
. "User-Agent: *\n"
|
||||||
. "Disallow: /albums/create\n"
|
. "Disallow: /albums/create\n"
|
||||||
|
. "Disallow: /assets/packages/static/openvk/img/banned.jpg\n"
|
||||||
|
. "Disallow: /assets/packages/static/openvk/img/camera_200.png\n"
|
||||||
|
. "Disallow: /assets/packages/static/openvk/img/flags/\n"
|
||||||
|
. "Disallow: /assets/packages/static/openvk/img/oof.apng\n"
|
||||||
. "Disallow: /videos/upload\n"
|
. "Disallow: /videos/upload\n"
|
||||||
. "Disallow: /invite\n"
|
. "Disallow: /invite\n"
|
||||||
. "Disallow: /groups_create\n"
|
. "Disallow: /groups_create\n"
|
||||||
|
|
|
@ -3,7 +3,19 @@ namespace openvk\Web\Presenters;
|
||||||
use Chandler\Database\Log;
|
use Chandler\Database\Log;
|
||||||
use Chandler\Database\Logs;
|
use Chandler\Database\Logs;
|
||||||
use openvk\Web\Models\Entities\{Voucher, Gift, GiftCategory, User, BannedLink};
|
use openvk\Web\Models\Entities\{Voucher, Gift, GiftCategory, User, BannedLink};
|
||||||
use openvk\Web\Models\Repositories\{Bans, ChandlerGroups, ChandlerUsers, Photos, Posts, Users, Clubs, Videos, Vouchers, Gifts, BannedLinks};
|
use openvk\Web\Models\Repositories\{Audios,
|
||||||
|
ChandlerGroups,
|
||||||
|
ChandlerUsers,
|
||||||
|
Users,
|
||||||
|
Clubs,
|
||||||
|
Util\EntityStream,
|
||||||
|
Vouchers,
|
||||||
|
Gifts,
|
||||||
|
BannedLinks,
|
||||||
|
Bans,
|
||||||
|
Photos,
|
||||||
|
Posts,
|
||||||
|
Videos};
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
|
||||||
final class AdminPresenter extends OpenVKPresenter
|
final class AdminPresenter extends OpenVKPresenter
|
||||||
|
@ -14,9 +26,10 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
private $gifts;
|
private $gifts;
|
||||||
private $bannedLinks;
|
private $bannedLinks;
|
||||||
private $chandlerGroups;
|
private $chandlerGroups;
|
||||||
|
private $audios;
|
||||||
private $logs;
|
private $logs;
|
||||||
|
|
||||||
function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts, BannedLinks $bannedLinks, ChandlerGroups $chandlerGroups)
|
function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts, BannedLinks $bannedLinks, ChandlerGroups $chandlerGroups, Audios $audios)
|
||||||
{
|
{
|
||||||
$this->users = $users;
|
$this->users = $users;
|
||||||
$this->clubs = $clubs;
|
$this->clubs = $clubs;
|
||||||
|
@ -24,8 +37,9 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
$this->gifts = $gifts;
|
$this->gifts = $gifts;
|
||||||
$this->bannedLinks = $bannedLinks;
|
$this->bannedLinks = $bannedLinks;
|
||||||
$this->chandlerGroups = $chandlerGroups;
|
$this->chandlerGroups = $chandlerGroups;
|
||||||
|
$this->audios = $audios;
|
||||||
$this->logs = DatabaseConnection::i()->getContext()->table("ChandlerLogs");
|
$this->logs = DatabaseConnection::i()->getContext()->table("ChandlerLogs");
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +57,15 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
$count = $repo->find($query)->size();
|
$count = $repo->find($query)->size();
|
||||||
return $repo->find($query)->page($page, 20);
|
return $repo->find($query)->page($page, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function searchPlaylists(&$count)
|
||||||
|
{
|
||||||
|
$query = $this->queryParam("q") ?? "";
|
||||||
|
$page = (int) ($this->queryParam("p") ?? 1);
|
||||||
|
|
||||||
|
$count = $this->audios->findPlaylists($query)->size();
|
||||||
|
return $this->audios->findPlaylists($query)->page($page, 20);
|
||||||
|
}
|
||||||
|
|
||||||
function onStartup(): void
|
function onStartup(): void
|
||||||
{
|
{
|
||||||
|
@ -87,8 +110,10 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
if($user->onlineStatus() != $this->postParam("online")) $user->setOnline(intval($this->postParam("online")));
|
if($user->onlineStatus() != $this->postParam("online")) $user->setOnline(intval($this->postParam("online")));
|
||||||
$user->setVerified(empty($this->postParam("verify") ? 0 : 1));
|
$user->setVerified(empty($this->postParam("verify") ? 0 : 1));
|
||||||
if($this->postParam("add-to-group")) {
|
if($this->postParam("add-to-group")) {
|
||||||
$query = "INSERT INTO `ChandlerACLRelations` (`user`, `group`) VALUES ('" . $user->getChandlerGUID() . "', '" . $this->postParam("add-to-group") . "')";
|
if (!(new ChandlerGroups)->isUserAMember($user->getChandlerGUID(), $this->postParam("add-to-group"))) {
|
||||||
DatabaseConnection::i()->getConnection()->query($query);
|
$query = "INSERT INTO `ChandlerACLRelations` (`user`, `group`) VALUES ('" . $user->getChandlerGUID() . "', '" . $this->postParam("add-to-group") . "')";
|
||||||
|
DatabaseConnection::i()->getConnection()->query($query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if($this->postParam("password")) {
|
if($this->postParam("password")) {
|
||||||
$user->getChandlerUser()->updatePassword($this->postParam("password"));
|
$user->getChandlerUser()->updatePassword($this->postParam("password"));
|
||||||
|
@ -578,6 +603,54 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
$this->redirect("/admin/users/id" . $user->getId());
|
$this->redirect("/admin/users/id" . $user->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderMusic(): void
|
||||||
|
{
|
||||||
|
$this->template->mode = in_array($this->queryParam("act"), ["audios", "playlists"]) ? $this->queryParam("act") : "audios";
|
||||||
|
if ($this->template->mode === "audios")
|
||||||
|
$this->template->audios = $this->searchResults($this->audios, $this->template->count);
|
||||||
|
else
|
||||||
|
$this->template->playlists = $this->searchPlaylists($this->template->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderEditMusic(int $audio_id): void
|
||||||
|
{
|
||||||
|
$audio = $this->audios->get($audio_id);
|
||||||
|
$this->template->audio = $audio;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->template->owner = $audio->getOwner()->getId();
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
$this->template->owner = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
$audio->setName($this->postParam("name"));
|
||||||
|
$audio->setPerformer($this->postParam("performer"));
|
||||||
|
$audio->setLyrics($this->postParam("text"));
|
||||||
|
$audio->setGenre($this->postParam("genre"));
|
||||||
|
$audio->setOwner((int) $this->postParam("owner"));
|
||||||
|
$audio->setExplicit(!empty($this->postParam("explicit")));
|
||||||
|
$audio->setDeleted(!empty($this->postParam("deleted")));
|
||||||
|
$audio->setWithdrawn(!empty($this->postParam("withdrawn")));
|
||||||
|
$audio->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderEditPlaylist(int $playlist_id): void
|
||||||
|
{
|
||||||
|
$playlist = $this->audios->getPlaylist($playlist_id);
|
||||||
|
$this->template->playlist = $playlist;
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
$playlist->setName($this->postParam("name"));
|
||||||
|
$playlist->setDescription($this->postParam("description"));
|
||||||
|
$playlist->setCover_Photo_Id((int) $this->postParam("photo"));
|
||||||
|
$playlist->setOwner((int) $this->postParam("owner"));
|
||||||
|
$playlist->setDeleted(!empty($this->postParam("deleted")));
|
||||||
|
$playlist->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderLogs(): void
|
function renderLogs(): void
|
||||||
{
|
{
|
||||||
$filter = [];
|
$filter = [];
|
||||||
|
|
696
Web/Presenters/AudioPresenter.php
Normal file
696
Web/Presenters/AudioPresenter.php
Normal file
|
@ -0,0 +1,696 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Presenters;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use openvk\Web\Models\Entities\Audio;
|
||||||
|
use openvk\Web\Models\Entities\Club;
|
||||||
|
use openvk\Web\Models\Entities\Photo;
|
||||||
|
use openvk\Web\Models\Entities\Playlist;
|
||||||
|
use openvk\Web\Models\Repositories\Audios;
|
||||||
|
use openvk\Web\Models\Repositories\Clubs;
|
||||||
|
use openvk\Web\Models\Repositories\Users;
|
||||||
|
|
||||||
|
final class AudioPresenter extends OpenVKPresenter
|
||||||
|
{
|
||||||
|
private $audios;
|
||||||
|
protected $presenterName = "audios";
|
||||||
|
|
||||||
|
const MAX_AUDIO_SIZE = 25000000;
|
||||||
|
|
||||||
|
function __construct(Audios $audios)
|
||||||
|
{
|
||||||
|
$this->audios = $audios;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPopular(): void
|
||||||
|
{
|
||||||
|
$this->renderList(NULL, "popular");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderNew(): void
|
||||||
|
{
|
||||||
|
$this->renderList(NULL, "new");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderList(?int $owner = NULL, ?string $mode = "list"): void
|
||||||
|
{
|
||||||
|
$this->template->_template = "Audio/List.xml";
|
||||||
|
$page = (int)($this->queryParam("p") ?? 1);
|
||||||
|
$audios = [];
|
||||||
|
|
||||||
|
if ($mode === "list") {
|
||||||
|
$entity = NULL;
|
||||||
|
if ($owner < 0) {
|
||||||
|
$entity = (new Clubs)->get($owner * -1);
|
||||||
|
if (!$entity || $entity->isBanned())
|
||||||
|
$this->redirect("/audios" . $this->user->id);
|
||||||
|
|
||||||
|
$audios = $this->audios->getByClub($entity, $page, 10);
|
||||||
|
$audiosCount = $this->audios->getClubCollectionSize($entity);
|
||||||
|
} else {
|
||||||
|
$entity = (new Users)->get($owner);
|
||||||
|
if (!$entity || $entity->isDeleted() || $entity->isBanned())
|
||||||
|
$this->redirect("/audios" . $this->user->id);
|
||||||
|
|
||||||
|
if(!$entity->getPrivacyPermission("audios.read", $this->user->identity))
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||||
|
|
||||||
|
$audios = $this->audios->getByUser($entity, $page, 10);
|
||||||
|
$audiosCount = $this->audios->getUserCollectionSize($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$entity)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$this->template->owner = $entity;
|
||||||
|
$this->template->ownerId = $owner;
|
||||||
|
$this->template->club = $owner < 0 ? $entity : NULL;
|
||||||
|
$this->template->isMy = ($owner > 0 && ($entity->getId() === $this->user->id));
|
||||||
|
$this->template->isMyClub = ($owner < 0 && $entity->canBeModifiedBy($this->user->identity));
|
||||||
|
} else if ($mode === "new") {
|
||||||
|
$audios = $this->audios->getNew();
|
||||||
|
$audiosCount = $audios->size();
|
||||||
|
} else if ($mode === "playlists") {
|
||||||
|
if($owner < 0) {
|
||||||
|
$entity = (new Clubs)->get(abs($owner));
|
||||||
|
if (!$entity || $entity->isBanned())
|
||||||
|
$this->redirect("/playlists" . $this->user->id);
|
||||||
|
|
||||||
|
$playlists = $this->audios->getPlaylistsByClub($entity, $page, 10);
|
||||||
|
$playlistsCount = $this->audios->getClubPlaylistsCount($entity);
|
||||||
|
} else {
|
||||||
|
$entity = (new Users)->get($owner);
|
||||||
|
if (!$entity || $entity->isDeleted() || $entity->isBanned())
|
||||||
|
$this->redirect("/playlists" . $this->user->id);
|
||||||
|
|
||||||
|
if(!$entity->getPrivacyPermission("audios.read", $this->user->identity))
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||||
|
|
||||||
|
$playlists = $this->audios->getPlaylistsByUser($entity, $page, 9);
|
||||||
|
$playlistsCount = $this->audios->getUserPlaylistsCount($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->playlists = iterator_to_array($playlists);
|
||||||
|
$this->template->playlistsCount = $playlistsCount;
|
||||||
|
$this->template->owner = $entity;
|
||||||
|
$this->template->ownerId = $owner;
|
||||||
|
$this->template->club = $owner < 0 ? $entity : NULL;
|
||||||
|
$this->template->isMy = ($owner > 0 && ($entity->getId() === $this->user->id));
|
||||||
|
$this->template->isMyClub = ($owner < 0 && $entity->canBeModifiedBy($this->user->identity));
|
||||||
|
} else {
|
||||||
|
$audios = $this->audios->getPopular();
|
||||||
|
$audiosCount = $audios->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// $this->renderApp("owner=$owner");
|
||||||
|
if ($audios !== []) {
|
||||||
|
$this->template->audios = iterator_to_array($audios);
|
||||||
|
$this->template->audiosCount = $audiosCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->mode = $mode;
|
||||||
|
$this->template->page = $page;
|
||||||
|
|
||||||
|
if(in_array($mode, ["list", "new", "popular"]) && $this->user->identity)
|
||||||
|
$this->template->friendsAudios = $this->user->identity->getBroadcastList("all", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderEmbed(int $owner, int $id): void
|
||||||
|
{
|
||||||
|
$audio = $this->audios->getByOwnerAndVID($owner, $id);
|
||||||
|
if(!$audio) {
|
||||||
|
header("HTTP/1.1 404 Not Found");
|
||||||
|
exit("<b>" . tr("audio_embed_not_found") . ".</b>");
|
||||||
|
} else if($audio->isDeleted()) {
|
||||||
|
header("HTTP/1.1 410 Not Found");
|
||||||
|
exit("<b>" . tr("audio_embed_deleted") . ".</b>");
|
||||||
|
} else if($audio->isWithdrawn()) {
|
||||||
|
header("HTTP/1.1 451 Unavailable for legal reasons");
|
||||||
|
exit("<b>" . tr("audio_embed_withdrawn") . ".</b>");
|
||||||
|
} else if(!$audio->canBeViewedBy(NULL)) {
|
||||||
|
header("HTTP/1.1 403 Forbidden");
|
||||||
|
exit("<b>" . tr("audio_embed_forbidden") . ".</b>");
|
||||||
|
} else if(!$audio->isAvailable()) {
|
||||||
|
header("HTTP/1.1 425 Too Early");
|
||||||
|
exit("<b>" . tr("audio_embed_processing") . ".</b>");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->audio = $audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderUpload(): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
|
$group = NULL;
|
||||||
|
$isAjax = $this->postParam("ajax", false) == 1;
|
||||||
|
if(!is_null($this->queryParam("gid"))) {
|
||||||
|
$gid = (int) $this->queryParam("gid");
|
||||||
|
$group = (new Clubs)->get($gid);
|
||||||
|
if(!$group)
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"), null, $isAjax);
|
||||||
|
|
||||||
|
if(!$group->canUploadAudio($this->user->identity))
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"), null, $isAjax);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->group = $group;
|
||||||
|
|
||||||
|
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||||
|
return;
|
||||||
|
|
||||||
|
$upload = $_FILES["blob"];
|
||||||
|
if(isset($upload) && file_exists($upload["tmp_name"])) {
|
||||||
|
if($upload["size"] > self::MAX_AUDIO_SIZE)
|
||||||
|
$this->flashFail("err", tr("error"), tr("media_file_corrupted_or_too_large"), null, $isAjax);
|
||||||
|
} else {
|
||||||
|
$err = !isset($upload) ? 65536 : $upload["error"];
|
||||||
|
$err = str_pad(dechex($err), 9, "0", STR_PAD_LEFT);
|
||||||
|
$readableError = tr("error_generic");
|
||||||
|
|
||||||
|
switch($upload["error"]) {
|
||||||
|
default:
|
||||||
|
case UPLOAD_ERR_INI_SIZE:
|
||||||
|
case UPLOAD_ERR_FORM_SIZE:
|
||||||
|
$readableError = tr("file_too_big");
|
||||||
|
break;
|
||||||
|
case UPLOAD_ERR_PARTIAL:
|
||||||
|
$readableError = tr("file_loaded_partially");
|
||||||
|
break;
|
||||||
|
case UPLOAD_ERR_NO_FILE:
|
||||||
|
$readableError = tr("file_not_uploaded");
|
||||||
|
break;
|
||||||
|
case UPLOAD_ERR_NO_TMP_DIR:
|
||||||
|
$readableError = "Missing a temporary folder.";
|
||||||
|
break;
|
||||||
|
case UPLOAD_ERR_CANT_WRITE:
|
||||||
|
case UPLOAD_ERR_EXTENSION:
|
||||||
|
$readableError = "Failed to write file to disk. ";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->flashFail("err", tr("error"), $readableError . " " . tr("error_code", $err), null, $isAjax);
|
||||||
|
}
|
||||||
|
|
||||||
|
$performer = $this->postParam("performer");
|
||||||
|
$name = $this->postParam("name");
|
||||||
|
$lyrics = $this->postParam("lyrics");
|
||||||
|
$genre = empty($this->postParam("genre")) ? "Other" : $this->postParam("genre");
|
||||||
|
$nsfw = ($this->postParam("explicit") ?? "off") === "on";
|
||||||
|
if(empty($performer) || empty($name) || iconv_strlen($performer . $name) > 128) # FQN of audio must not be more than 128 chars
|
||||||
|
$this->flashFail("err", tr("error"), tr("error_insufficient_info"), null, $isAjax);
|
||||||
|
|
||||||
|
$audio = new Audio;
|
||||||
|
$audio->setOwner($this->user->id);
|
||||||
|
$audio->setName($name);
|
||||||
|
$audio->setPerformer($performer);
|
||||||
|
$audio->setLyrics(empty($lyrics) ? NULL : $lyrics);
|
||||||
|
$audio->setGenre($genre);
|
||||||
|
$audio->setExplicit($nsfw);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$audio->setFile($upload);
|
||||||
|
} catch(\DomainException $ex) {
|
||||||
|
$e = $ex->getMessage();
|
||||||
|
$this->flashFail("err", tr("error"), tr("media_file_corrupted_or_too_large") . " $e.", null, $isAjax);
|
||||||
|
} catch(\RuntimeException $ex) {
|
||||||
|
$this->flashFail("err", tr("error"), tr("ffmpeg_timeout"), null, $isAjax);
|
||||||
|
} catch(\BadMethodCallException $ex) {
|
||||||
|
$this->flashFail("err", tr("error"), "Загрузка аудио под Linux на данный момент не реализована. Следите за обновлениями: <a href='https://github.com/openvk/openvk/pull/512/commits'>https://github.com/openvk/openvk/pull/512/commits</a>", null, $isAjax);
|
||||||
|
} catch(\Exception $ex) {
|
||||||
|
$this->flashFail("err", tr("error"), tr("ffmpeg_not_installed"), null, $isAjax);
|
||||||
|
}
|
||||||
|
|
||||||
|
$audio->save();
|
||||||
|
$audio->add($group ?? $this->user->identity);
|
||||||
|
|
||||||
|
if(!$isAjax)
|
||||||
|
$this->redirect(is_null($group) ? "/audios" . $this->user->id : "/audios-" . $group->getId());
|
||||||
|
else {
|
||||||
|
$redirectLink = "/audios";
|
||||||
|
|
||||||
|
if(!is_null($group))
|
||||||
|
$redirectLink .= $group->getRealId();
|
||||||
|
else
|
||||||
|
$redirectLink .= $this->user->id;
|
||||||
|
|
||||||
|
$pagesCount = (int)ceil((new Audios)->getCollectionSizeByEntityId(isset($group) ? $group->getRealId() : $this->user->id) / 10);
|
||||||
|
$redirectLink .= "?p=".$pagesCount;
|
||||||
|
|
||||||
|
$this->returnJson([
|
||||||
|
"success" => true,
|
||||||
|
"redirect_link" => $redirectLink,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderListen(int $id): void
|
||||||
|
{
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
|
if(is_null($this->user))
|
||||||
|
$this->returnJson(["success" => false]);
|
||||||
|
|
||||||
|
$audio = $this->audios->get($id);
|
||||||
|
|
||||||
|
if ($audio && !$audio->isDeleted() && !$audio->isWithdrawn()) {
|
||||||
|
if(!empty($this->postParam("playlist"))) {
|
||||||
|
$playlist = (new Audios)->getPlaylist((int)$this->postParam("playlist"));
|
||||||
|
|
||||||
|
if(!$playlist || $playlist->isDeleted() || !$playlist->canBeViewedBy($this->user->identity) || !$playlist->hasAudio($audio))
|
||||||
|
$playlist = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$listen = $audio->listen($this->user->identity, $playlist);
|
||||||
|
|
||||||
|
$returnArr = ["success" => $listen];
|
||||||
|
|
||||||
|
if($playlist)
|
||||||
|
$returnArr["new_playlists_listens"] = $playlist->getListens();
|
||||||
|
|
||||||
|
$this->returnJson($returnArr);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->returnJson(["success" => false]);
|
||||||
|
} else {
|
||||||
|
$this->redirect("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSearch(): void
|
||||||
|
{
|
||||||
|
$this->redirect("/search?type=audios");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderNewPlaylist(): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
$this->willExecuteWriteAction(true);
|
||||||
|
|
||||||
|
$owner = $this->user->id;
|
||||||
|
|
||||||
|
if ($this->requestParam("gid")) {
|
||||||
|
$club = (new Clubs)->get((int) abs((int)$this->requestParam("gid")));
|
||||||
|
if (!$club || $club->isBanned() || !$club->canBeModifiedBy($this->user->identity))
|
||||||
|
$this->redirect("/audios" . $this->user->id);
|
||||||
|
|
||||||
|
$owner = ($club->getId() * -1);
|
||||||
|
|
||||||
|
$this->template->club = $club;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->owner = $owner;
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
$title = $this->postParam("title");
|
||||||
|
$description = $this->postParam("description");
|
||||||
|
$audios = !empty($this->postParam("audios")) ? array_slice(explode(",", $this->postParam("audios")), 0, 1000) : [];
|
||||||
|
|
||||||
|
if(empty($title) || iconv_strlen($title) < 1)
|
||||||
|
$this->flashFail("err", tr("error"), tr("set_playlist_name"));
|
||||||
|
|
||||||
|
$playlist = new Playlist;
|
||||||
|
$playlist->setOwner($owner);
|
||||||
|
$playlist->setName(substr($title, 0, 125));
|
||||||
|
$playlist->setDescription(substr($description, 0, 2045));
|
||||||
|
|
||||||
|
if($_FILES["cover"]["error"] === UPLOAD_ERR_OK) {
|
||||||
|
if(!str_starts_with($_FILES["cover"]["type"], "image"))
|
||||||
|
$this->flashFail("err", tr("error"), tr("not_a_photo"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$playlist->fastMakeCover($this->user->id, $_FILES["cover"]);
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
$this->flashFail("err", tr("error"), tr("invalid_cover_photo"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$playlist->save();
|
||||||
|
|
||||||
|
foreach($audios as $audio) {
|
||||||
|
$audio = $this->audios->get((int)$audio);
|
||||||
|
|
||||||
|
if(!$audio || $audio->isDeleted() || !$audio->canBeViewedBy($this->user->identity))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$playlist->add($audio);
|
||||||
|
}
|
||||||
|
|
||||||
|
$playlist->bookmark(isset($club) ? $club : $this->user->identity);
|
||||||
|
$this->redirect("/playlist" . $owner . "_" . $playlist->getId());
|
||||||
|
} else {
|
||||||
|
if(isset($club)) {
|
||||||
|
$this->template->audios = iterator_to_array($this->audios->getByClub($club, 1, 10));
|
||||||
|
$count = (new Audios)->getClubCollectionSize($club);
|
||||||
|
} else {
|
||||||
|
$this->template->audios = iterator_to_array($this->audios->getByUser($this->user->identity, 1, 10));
|
||||||
|
$count = (new Audios)->getUserCollectionSize($this->user->identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->pagesCount = ceil($count / 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPlaylistAction(int $id) {
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
$this->willExecuteWriteAction(true);
|
||||||
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||||
|
header("HTTP/1.1 405 Method Not Allowed");
|
||||||
|
$this->redirect("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
$playlist = $this->audios->getPlaylist($id);
|
||||||
|
|
||||||
|
if(!$playlist || $playlist->isDeleted())
|
||||||
|
$this->flashFail("err", "error", tr("invalid_playlist"), null, true);
|
||||||
|
|
||||||
|
switch ($this->queryParam("act")) {
|
||||||
|
case "bookmark":
|
||||||
|
if(!$playlist->isBookmarkedBy($this->user->identity))
|
||||||
|
$playlist->bookmark($this->user->identity);
|
||||||
|
else
|
||||||
|
$this->flashFail("err", "error", tr("playlist_already_bookmarked"), null, true);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "unbookmark":
|
||||||
|
if($playlist->isBookmarkedBy($this->user->identity))
|
||||||
|
$playlist->unbookmark($this->user->identity);
|
||||||
|
else
|
||||||
|
$this->flashFail("err", "error", tr("playlist_not_bookmarked"), null, true);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "delete":
|
||||||
|
if($playlist->canBeModifiedBy($this->user->identity)) {
|
||||||
|
$tmOwner = $playlist->getOwner();
|
||||||
|
$playlist->delete();
|
||||||
|
} else
|
||||||
|
$this->flashFail("err", "error", tr("access_denied"), null, true);
|
||||||
|
|
||||||
|
$this->returnJson(["success" => true, "id" => $tmOwner->getRealId()]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->returnJson(["success" => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderEditPlaylist(int $owner_id, int $virtual_id)
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$playlist = $this->audios->getPlaylistByOwnerAndVID($owner_id, $virtual_id);
|
||||||
|
$page = (int)($this->queryParam("p") ?? 1);
|
||||||
|
if (!$playlist || $playlist->isDeleted() || !$playlist->canBeModifiedBy($this->user->identity))
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$this->template->playlist = $playlist;
|
||||||
|
$this->template->page = $page;
|
||||||
|
|
||||||
|
$audios = iterator_to_array($playlist->fetch(1, $playlist->size()));
|
||||||
|
$this->template->audios = array_slice($audios, 0, 10);
|
||||||
|
$audiosIds = [];
|
||||||
|
|
||||||
|
foreach($audios as $aud)
|
||||||
|
$audiosIds[] = $aud->getId();
|
||||||
|
|
||||||
|
$this->template->audiosIds = implode(",", array_unique($audiosIds)) . ",";
|
||||||
|
$this->template->ownerId = $owner_id;
|
||||||
|
$this->template->owner = $playlist->getOwner();
|
||||||
|
$this->template->pagesCount = $pagesCount = ceil($playlist->size() / 10);
|
||||||
|
|
||||||
|
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||||
|
return;
|
||||||
|
|
||||||
|
$title = $this->postParam("title");
|
||||||
|
$description = $this->postParam("description");
|
||||||
|
$new_audios = !empty($this->postParam("audios")) ? explode(",", rtrim($this->postParam("audios"), ",")) : [];
|
||||||
|
|
||||||
|
if(empty($title) || iconv_strlen($title) < 1)
|
||||||
|
$this->flashFail("err", tr("error"), tr("set_playlist_name"));
|
||||||
|
|
||||||
|
$playlist->setName(ovk_proc_strtr($title, 125));
|
||||||
|
$playlist->setDescription(ovk_proc_strtr($description, 2045));
|
||||||
|
$playlist->setEdited(time());
|
||||||
|
$playlist->resetLength();
|
||||||
|
|
||||||
|
if($_FILES["new_cover"]["error"] === UPLOAD_ERR_OK) {
|
||||||
|
if(!str_starts_with($_FILES["new_cover"]["type"], "image"))
|
||||||
|
$this->flashFail("err", tr("error"), tr("not_a_photo"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$playlist->fastMakeCover($this->user->id, $_FILES["new_cover"]);
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
$this->flashFail("err", tr("error"), tr("invalid_cover_photo"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$playlist->save();
|
||||||
|
|
||||||
|
DatabaseConnection::i()->getContext()->table("playlist_relations")->where([
|
||||||
|
"collection" => $playlist->getId()
|
||||||
|
])->delete();
|
||||||
|
|
||||||
|
foreach ($new_audios as $new_audio) {
|
||||||
|
$audio = (new Audios)->get((int)$new_audio);
|
||||||
|
|
||||||
|
if(!$audio || $audio->isDeleted())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$playlist->add($audio);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->redirect("/playlist".$playlist->getPrettyId());
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPlaylist(int $owner_id, int $virtual_id): void
|
||||||
|
{
|
||||||
|
$playlist = $this->audios->getPlaylistByOwnerAndVID($owner_id, $virtual_id);
|
||||||
|
$page = (int)($this->queryParam("p") ?? 1);
|
||||||
|
if (!$playlist || $playlist->isDeleted())
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$this->template->playlist = $playlist;
|
||||||
|
$this->template->page = $page;
|
||||||
|
$this->template->audios = iterator_to_array($playlist->fetch($page, 10));
|
||||||
|
$this->template->ownerId = $owner_id;
|
||||||
|
$this->template->owner = $playlist->getOwner();
|
||||||
|
$this->template->isBookmarked = $this->user->identity && $playlist->isBookmarkedBy($this->user->identity);
|
||||||
|
$this->template->isMy = $this->user->identity && $playlist->getOwner()->getId() === $this->user->id;
|
||||||
|
$this->template->canEdit = $this->user->identity && $playlist->canBeModifiedBy($this->user->identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAction(int $audio_id): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
$this->willExecuteWriteAction(true);
|
||||||
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||||
|
header("HTTP/1.1 405 Method Not Allowed");
|
||||||
|
$this->redirect("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
$audio = $this->audios->get($audio_id);
|
||||||
|
|
||||||
|
if(!$audio || $audio->isDeleted())
|
||||||
|
$this->flashFail("err", "error", tr("invalid_audio"), null, true);
|
||||||
|
|
||||||
|
switch ($this->queryParam("act")) {
|
||||||
|
case "add":
|
||||||
|
if($audio->isWithdrawn())
|
||||||
|
$this->flashFail("err", "error", tr("invalid_audio"), null, true);
|
||||||
|
|
||||||
|
if(!$audio->isInLibraryOf($this->user->identity))
|
||||||
|
$audio->add($this->user->identity);
|
||||||
|
else
|
||||||
|
$this->flashFail("err", "error", tr("do_have_audio"), null, true);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "remove":
|
||||||
|
if($audio->isInLibraryOf($this->user->identity))
|
||||||
|
$audio->remove($this->user->identity);
|
||||||
|
else
|
||||||
|
$this->flashFail("err", "error", tr("do_not_have_audio"), null, true);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "remove_club":
|
||||||
|
$club = (new Clubs)->get((int)$this->postParam("club"));
|
||||||
|
|
||||||
|
if(!$club || !$club->canBeModifiedBy($this->user->identity))
|
||||||
|
$this->flashFail("err", "error", tr("access_denied"), null, true);
|
||||||
|
|
||||||
|
if($audio->isInLibraryOf($club))
|
||||||
|
$audio->remove($club);
|
||||||
|
else
|
||||||
|
$this->flashFail("err", "error", tr("group_hasnt_audio"), null, true);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "add_to_club":
|
||||||
|
$club = (new Clubs)->get((int)$this->postParam("club"));
|
||||||
|
|
||||||
|
if(!$club || !$club->canBeModifiedBy($this->user->identity))
|
||||||
|
$this->flashFail("err", "error", tr("access_denied"), null, true);
|
||||||
|
|
||||||
|
if(!$audio->isInLibraryOf($club))
|
||||||
|
$audio->add($club);
|
||||||
|
else
|
||||||
|
$this->flashFail("err", "error", tr("group_has_audio"), null, true);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "delete":
|
||||||
|
if($audio->canBeModifiedBy($this->user->identity))
|
||||||
|
$audio->delete();
|
||||||
|
else
|
||||||
|
$this->flashFail("err", "error", tr("access_denied"), null, true);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "edit":
|
||||||
|
$audio = $this->audios->get($audio_id);
|
||||||
|
if (!$audio || $audio->isDeleted() || $audio->isWithdrawn())
|
||||||
|
$this->flashFail("err", "error", tr("invalid_audio"), null, true);
|
||||||
|
|
||||||
|
if ($audio->getOwner()->getId() !== $this->user->id)
|
||||||
|
$this->flashFail("err", "error", tr("access_denied"), null, true);
|
||||||
|
|
||||||
|
$performer = $this->postParam("performer");
|
||||||
|
$name = $this->postParam("name");
|
||||||
|
$lyrics = $this->postParam("lyrics");
|
||||||
|
$genre = empty($this->postParam("genre")) ? "undefined" : $this->postParam("genre");
|
||||||
|
$nsfw = (int)($this->postParam("explicit") ?? 0) === 1;
|
||||||
|
$unlisted = (int)($this->postParam("unlisted") ?? 0) === 1;
|
||||||
|
if(empty($performer) || empty($name) || iconv_strlen($performer . $name) > 128) # FQN of audio must not be more than 128 chars
|
||||||
|
$this->flashFail("err", tr("error"), tr("error_insufficient_info"), null, true);
|
||||||
|
|
||||||
|
$audio->setName($name);
|
||||||
|
$audio->setPerformer($performer);
|
||||||
|
$audio->setLyrics(empty($lyrics) ? NULL : $lyrics);
|
||||||
|
$audio->setGenre($genre);
|
||||||
|
$audio->setExplicit($nsfw);
|
||||||
|
$audio->setSearchability($unlisted);
|
||||||
|
$audio->setEdited(time());
|
||||||
|
$audio->save();
|
||||||
|
|
||||||
|
$this->returnJson(["success" => true, "new_info" => [
|
||||||
|
"name" => ovk_proc_strtr($audio->getTitle(), 40),
|
||||||
|
"performer" => ovk_proc_strtr($audio->getPerformer(), 40),
|
||||||
|
"lyrics" => nl2br($audio->getLyrics() ?? ""),
|
||||||
|
"lyrics_unformatted" => $audio->getLyrics() ?? "",
|
||||||
|
"explicit" => $audio->isExplicit(),
|
||||||
|
"genre" => $audio->getGenre(),
|
||||||
|
"unlisted" => $audio->isUnlisted(),
|
||||||
|
]]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->returnJson(["success" => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPlaylists(int $owner)
|
||||||
|
{
|
||||||
|
$this->renderList($owner, "playlists");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderApiGetContext()
|
||||||
|
{
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||||
|
header("HTTP/1.1 405 Method Not Allowed");
|
||||||
|
$this->redirect("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
$ctx_type = $this->postParam("context");
|
||||||
|
$ctx_id = (int)($this->postParam("context_entity"));
|
||||||
|
$page = (int)($this->postParam("page") ?? 1);
|
||||||
|
$perPage = 10;
|
||||||
|
|
||||||
|
switch($ctx_type) {
|
||||||
|
default:
|
||||||
|
case "entity_audios":
|
||||||
|
if($ctx_id >= 0) {
|
||||||
|
$entity = $ctx_id != 0 ? (new Users)->get($ctx_id) : $this->user->identity;
|
||||||
|
|
||||||
|
if(!$entity || !$entity->getPrivacyPermission("audios.read", $this->user->identity))
|
||||||
|
$this->flashFail("err", "Error", "Can't get queue", 80, true);
|
||||||
|
|
||||||
|
$audios = $this->audios->getByUser($entity, $page, $perPage);
|
||||||
|
$audiosCount = $this->audios->getUserCollectionSize($entity);
|
||||||
|
} else {
|
||||||
|
$entity = (new Clubs)->get(abs($ctx_id));
|
||||||
|
|
||||||
|
if(!$entity || $entity->isBanned())
|
||||||
|
$this->flashFail("err", "Error", "Can't get queue", 80, true);
|
||||||
|
|
||||||
|
$audios = $this->audios->getByClub($entity, $page, $perPage);
|
||||||
|
$audiosCount = $this->audios->getClubCollectionSize($entity);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "new_audios":
|
||||||
|
$audios = $this->audios->getNew();
|
||||||
|
$audiosCount = $audios->size();
|
||||||
|
break;
|
||||||
|
case "popular_audios":
|
||||||
|
$audios = $this->audios->getPopular();
|
||||||
|
$audiosCount = $audios->size();
|
||||||
|
break;
|
||||||
|
case "playlist_context":
|
||||||
|
$playlist = $this->audios->getPlaylist($ctx_id);
|
||||||
|
|
||||||
|
if (!$playlist || $playlist->isDeleted())
|
||||||
|
$this->flashFail("err", "Error", "Can't get queue", 80, true);
|
||||||
|
|
||||||
|
$audios = $playlist->fetch($page, 10);
|
||||||
|
$audiosCount = $playlist->size();
|
||||||
|
break;
|
||||||
|
case "search_context":
|
||||||
|
$stream = $this->audios->search($this->postParam("query"), 2, $this->postParam("type") === "by_performer");
|
||||||
|
$audios = $stream->page($page, 10);
|
||||||
|
$audiosCount = $stream->size();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pagesCount = ceil($audiosCount / $perPage);
|
||||||
|
|
||||||
|
# костылёк для получения плееров в пикере аудиозаписей
|
||||||
|
if((int)($this->postParam("returnPlayers")) === 1) {
|
||||||
|
$this->template->audios = $audios;
|
||||||
|
$this->template->page = $page;
|
||||||
|
$this->template->pagesCount = $pagesCount;
|
||||||
|
$this->template->count = $audiosCount;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$audiosArr = [];
|
||||||
|
|
||||||
|
foreach($audios as $audio) {
|
||||||
|
$audiosArr[] = [
|
||||||
|
"id" => $audio->getId(),
|
||||||
|
"name" => $audio->getTitle(),
|
||||||
|
"performer" => $audio->getPerformer(),
|
||||||
|
"keys" => $audio->getKeys(),
|
||||||
|
"url" => $audio->getUrl(),
|
||||||
|
"length" => $audio->getLength(),
|
||||||
|
"available" => $audio->isAvailable(),
|
||||||
|
"withdrawn" => $audio->isWithdrawn(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$resultArr = [
|
||||||
|
"success" => true,
|
||||||
|
"page" => $page,
|
||||||
|
"perPage" => $perPage,
|
||||||
|
"pagesCount" => $pagesCount,
|
||||||
|
"count" => $audiosCount,
|
||||||
|
"items" => $audiosArr,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->returnJson($resultArr);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\{IP, User, PasswordReset, EmailVerification};
|
use openvk\Web\Models\Entities\{IP, User, PasswordReset, EmailVerification};
|
||||||
use openvk\Web\Models\Repositories\{Bans, IPs, Users, Restores, Verifications, Logs};
|
use openvk\Web\Models\Repositories\{Bans, IPs, Users, Restores, Verifications};
|
||||||
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||||
use openvk\Web\Util\Validator;
|
use openvk\Web\Util\Validator;
|
||||||
use Chandler\Session\Session;
|
use Chandler\Session\Session;
|
||||||
|
@ -95,7 +95,17 @@ final class AuthPresenter extends OpenVKPresenter
|
||||||
$user = new User;
|
$user = new User;
|
||||||
$user->setFirst_Name($this->postParam("first_name"));
|
$user->setFirst_Name($this->postParam("first_name"));
|
||||||
$user->setLast_Name($this->postParam("last_name"));
|
$user->setLast_Name($this->postParam("last_name"));
|
||||||
$user->setSex((int)($this->postParam("sex") === "female"));
|
switch ($this->postParam("pronouns")) {
|
||||||
|
case 'male':
|
||||||
|
$user->setSex(0);
|
||||||
|
break;
|
||||||
|
case 'female':
|
||||||
|
$user->setSex(1);
|
||||||
|
break;
|
||||||
|
case 'neutral':
|
||||||
|
$user->setSex(2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
$user->setEmail($this->postParam("email"));
|
$user->setEmail($this->postParam("email"));
|
||||||
$user->setSince(date("Y-m-d H:i:s"));
|
$user->setSince(date("Y-m-d H:i:s"));
|
||||||
$user->setRegistering_Ip(CONNECTING_IP);
|
$user->setRegistering_Ip(CONNECTING_IP);
|
||||||
|
@ -130,7 +140,6 @@ final class AuthPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->authenticator->authenticate($chUser->getId());
|
$this->authenticator->authenticate($chUser->getId());
|
||||||
(new Logs)->create($user->getId(), "profiles", "openvk\\Web\\Models\\Entities\\User", 0, $user, $user, $_SERVER["REMOTE_ADDR"], $_SERVER["HTTP_USER_AGENT"]);
|
|
||||||
$this->redirect("/id" . $user->getId());
|
$this->redirect("/id" . $user->getId());
|
||||||
$user->save();
|
$user->save();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ final class BlobPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
function renderFile(/*string*/ $dir, string $name, string $format)
|
function renderFile(/*string*/ $dir, string $name, string $format)
|
||||||
{
|
{
|
||||||
|
header("Access-Control-Allow-Origin: *");
|
||||||
|
|
||||||
$dir = $this->getDirName($dir);
|
$dir = $this->getDirName($dir);
|
||||||
$base = realpath(OPENVK_ROOT . "/storage/$dir");
|
$base = realpath(OPENVK_ROOT . "/storage/$dir");
|
||||||
$path = realpath(OPENVK_ROOT . "/storage/$dir/$name.$format");
|
$path = realpath(OPENVK_ROOT . "/storage/$dir/$name.$format");
|
||||||
|
@ -37,5 +39,5 @@ final class BlobPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
readfile($path);
|
readfile($path);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\{Comment, Notifications\MentionNotification, Photo, Video, User, Topic, Post};
|
use openvk\Web\Models\Entities\{Comment, Notifications\MentionNotification, Photo, Video, User, Topic, Post};
|
||||||
use openvk\Web\Models\Entities\Notifications\CommentNotification;
|
use openvk\Web\Models\Entities\Notifications\CommentNotification;
|
||||||
use openvk\Web\Models\Repositories\{Comments, Clubs, Videos};
|
use openvk\Web\Models\Repositories\{Comments, Clubs, Videos, Photos, Audios};
|
||||||
|
|
||||||
final class CommentPresenter extends OpenVKPresenter
|
final class CommentPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
|
@ -43,6 +43,10 @@ final class CommentPresenter extends OpenVKPresenter
|
||||||
$entity = $repo->get($eId);
|
$entity = $repo->get($eId);
|
||||||
if(!$entity) $this->notFound();
|
if(!$entity) $this->notFound();
|
||||||
|
|
||||||
|
if(!$entity->canBeViewedBy($this->user->identity)) {
|
||||||
|
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
|
}
|
||||||
|
|
||||||
if($entity instanceof Topic && $entity->isClosed())
|
if($entity instanceof Topic && $entity->isClosed())
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
|
|
||||||
|
@ -54,9 +58,6 @@ final class CommentPresenter extends OpenVKPresenter
|
||||||
if ($entity instanceof Post && $entity->getWallOwner()->isBanned())
|
if ($entity instanceof Post && $entity->getWallOwner()->isBanned())
|
||||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
|
|
||||||
if($_FILES["_vid_attachment"] && OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
|
|
||||||
$this->flashFail("err", tr("error"), tr("video_uploads_disabled"));
|
|
||||||
|
|
||||||
$flags = 0;
|
$flags = 0;
|
||||||
if($this->postParam("as_group") === "on" && !is_null($club) && $club->canBeModifiedBy($this->user->identity))
|
if($this->postParam("as_group") === "on" && !is_null($club) && $club->canBeModifiedBy($this->user->identity))
|
||||||
$flags |= 0b10000000;
|
$flags |= 0b10000000;
|
||||||
|
@ -70,18 +71,22 @@ final class CommentPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO move to trait
|
$photos = [];
|
||||||
try {
|
if(!empty($this->postParam("photos"))) {
|
||||||
$photo = NULL;
|
$un = rtrim($this->postParam("photos"), ",");
|
||||||
if($_FILES["_pic_attachment"]["error"] === UPLOAD_ERR_OK) {
|
$arr = explode(",", $un);
|
||||||
$album = NULL;
|
|
||||||
if($wall > 0 && $wall === $this->user->id)
|
if(sizeof($arr) < 11) {
|
||||||
$album = (new Albums)->getUserWallAlbum($wallOwner);
|
foreach($arr as $dat) {
|
||||||
|
$ids = explode("_", $dat);
|
||||||
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album);
|
$photo = (new Photos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
|
||||||
|
|
||||||
|
if(!$photo || $photo->isDeleted())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$photos[] = $photo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch(ISE $ex) {
|
|
||||||
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_file_too_big"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$videos = [];
|
$videos = [];
|
||||||
|
@ -102,8 +107,27 @@ final class CommentPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$audios = [];
|
||||||
|
|
||||||
|
if(!empty($this->postParam("audios"))) {
|
||||||
|
$un = rtrim($this->postParam("audios"), ",");
|
||||||
|
$arr = explode(",", $un);
|
||||||
|
|
||||||
|
if(sizeof($arr) < 11) {
|
||||||
|
foreach($arr as $dat) {
|
||||||
|
$ids = explode("_", $dat);
|
||||||
|
$audio = (new Audios)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
|
||||||
|
|
||||||
|
if(!$audio || $audio->isDeleted())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$audios[] = $audio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(empty($this->postParam("text")) && !$photo && !$video)
|
if(empty($this->postParam("text")) && sizeof($photos) < 1 && sizeof($videos) < 1 && sizeof($audios) < 1)
|
||||||
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_empty"));
|
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_empty"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -119,12 +143,15 @@ final class CommentPresenter extends OpenVKPresenter
|
||||||
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_too_big"));
|
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_too_big"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!is_null($photo))
|
foreach($photos as $photo)
|
||||||
$comment->attach($photo);
|
$comment->attach($photo);
|
||||||
|
|
||||||
if(sizeof($videos) > 0)
|
if(sizeof($videos) > 0)
|
||||||
foreach($videos as $vid)
|
foreach($videos as $vid)
|
||||||
$comment->attach($vid);
|
$comment->attach($vid);
|
||||||
|
|
||||||
|
foreach($audios as $audio)
|
||||||
|
$comment->attach($audio);
|
||||||
|
|
||||||
if($entity->getOwner()->getId() !== $this->user->identity->getId())
|
if($entity->getOwner()->getId() !== $this->user->identity->getId())
|
||||||
if(($owner = $entity->getOwner()) instanceof User)
|
if(($owner = $entity->getOwner()) instanceof User)
|
||||||
|
|
|
@ -20,9 +20,12 @@ final class GiftsPresenter extends OpenVKPresenter
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
$user = $this->users->get($user);
|
$user = $this->users->get($user);
|
||||||
if(!$user)
|
if(!$user || $user->isDeleted())
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
|
|
||||||
|
if(!$user->canBeViewedBy($this->user->identity ?? NULL))
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||||
|
|
||||||
$this->template->user = $user;
|
$this->template->user = $user;
|
||||||
$this->template->page = $page = (int) ($this->queryParam("p") ?? 1);
|
$this->template->page = $page = (int) ($this->queryParam("p") ?? 1);
|
||||||
$this->template->count = $user->getGiftCount();
|
$this->template->count = $user->getGiftCount();
|
||||||
|
@ -52,6 +55,9 @@ final class GiftsPresenter extends OpenVKPresenter
|
||||||
if(!$user || !$cat)
|
if(!$user || !$cat)
|
||||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_user_not_exists"));
|
$this->flashFail("err", tr("error_when_gifting"), tr("error_user_not_exists"));
|
||||||
|
|
||||||
|
if(!$user->canBeViewedBy($this->user->identity))
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||||
|
|
||||||
$this->template->page = $page = (int) ($this->queryParam("p") ?? 1);
|
$this->template->page = $page = (int) ($this->queryParam("p") ?? 1);
|
||||||
$gifts = $cat->getGifts($page, null, $this->template->count);
|
$gifts = $cat->getGifts($page, null, $this->template->count);
|
||||||
|
|
||||||
|
@ -72,6 +78,9 @@ final class GiftsPresenter extends OpenVKPresenter
|
||||||
if(!$gift->canUse($this->user->identity))
|
if(!$gift->canUse($this->user->identity))
|
||||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_no_more_gifts"));
|
$this->flashFail("err", tr("error_when_gifting"), tr("error_no_more_gifts"));
|
||||||
|
|
||||||
|
if(!$user->canBeViewedBy($this->user->identity ?? NULL))
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||||
|
|
||||||
$coinsLeft = $this->user->identity->getCoins() - $gift->getPrice();
|
$coinsLeft = $this->user->identity->getCoins() - $gift->getPrice();
|
||||||
if($coinsLeft < 0)
|
if($coinsLeft < 0)
|
||||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_no_money"));
|
$this->flashFail("err", tr("error_when_gifting"), tr("error_no_money"));
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\{Club, Photo, Post};
|
use openvk\Web\Models\Entities\{Club, Photo, Post};
|
||||||
use Nette\InvalidStateException;
|
use Nette\InvalidStateException;
|
||||||
use openvk\Web\Models\Entities\Notifications\ClubModeratorNotification;
|
use openvk\Web\Models\Entities\Notifications\ClubModeratorNotification;
|
||||||
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers, Topics};
|
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers, Topics, Audios, Posts};
|
||||||
use Chandler\Security\Authenticator;
|
use Chandler\Security\Authenticator;
|
||||||
|
|
||||||
final class GroupPresenter extends OpenVKPresenter
|
final class GroupPresenter extends OpenVKPresenter
|
||||||
|
@ -31,6 +31,15 @@ final class GroupPresenter extends OpenVKPresenter
|
||||||
$this->template->albumsCount = (new Albums)->getClubAlbumsCount($club);
|
$this->template->albumsCount = (new Albums)->getClubAlbumsCount($club);
|
||||||
$this->template->topics = (new Topics)->getLastTopics($club, 3);
|
$this->template->topics = (new Topics)->getLastTopics($club, 3);
|
||||||
$this->template->topicsCount = (new Topics)->getClubTopicsCount($club);
|
$this->template->topicsCount = (new Topics)->getClubTopicsCount($club);
|
||||||
|
$this->template->audios = (new Audios)->getRandomThreeAudiosByEntityId($club->getRealId());
|
||||||
|
$this->template->audiosCount = (new Audios)->getClubCollectionSize($club);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!is_null($this->user->identity) && $club->getWallType() == 2) {
|
||||||
|
if(!$club->canBeModifiedBy($this->user->identity))
|
||||||
|
$this->template->suggestedPostsCountByUser = (new Posts)->getSuggestedPostsCountByUser($club->getId(), $this->user->id);
|
||||||
|
else
|
||||||
|
$this->template->suggestedPostsCountByEveryone = (new Posts)->getSuggestedPostsCount($club->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->template->club = $club;
|
$this->template->club = $club;
|
||||||
|
@ -214,10 +223,16 @@ final class GroupPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
$club->setName((empty($this->postParam("name")) || mb_strlen(trim($this->postParam("name"))) === 0) ? $club->getName() : $this->postParam("name"));
|
$club->setName((empty($this->postParam("name")) || mb_strlen(trim($this->postParam("name"))) === 0) ? $club->getName() : $this->postParam("name"));
|
||||||
$club->setAbout(empty($this->postParam("about")) ? NULL : $this->postParam("about"));
|
$club->setAbout(empty($this->postParam("about")) ? NULL : $this->postParam("about"));
|
||||||
$club->setWall(empty($this->postParam("wall")) ? 0 : 1);
|
try {
|
||||||
|
$club->setWall(empty($this->postParam("wall")) ? 0 : (int)$this->postParam("wall"));
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
$this->flashFail("err", tr("error"), tr("error_invalid_wall_value"));
|
||||||
|
}
|
||||||
|
|
||||||
$club->setAdministrators_List_Display(empty($this->postParam("administrators_list_display")) ? 0 : $this->postParam("administrators_list_display"));
|
$club->setAdministrators_List_Display(empty($this->postParam("administrators_list_display")) ? 0 : $this->postParam("administrators_list_display"));
|
||||||
$club->setEveryone_Can_Create_Topics(empty($this->postParam("everyone_can_create_topics")) ? 0 : 1);
|
$club->setEveryone_Can_Create_Topics(empty($this->postParam("everyone_can_create_topics")) ? 0 : 1);
|
||||||
$club->setDisplay_Topics_Above_Wall(empty($this->postParam("display_topics_above_wall")) ? 0 : 1);
|
$club->setDisplay_Topics_Above_Wall(empty($this->postParam("display_topics_above_wall")) ? 0 : 1);
|
||||||
|
$club->setEveryone_can_upload_audios(empty($this->postParam("upload_audios")) ? 0 : 1);
|
||||||
$club->setHide_From_Global_Feed(empty($this->postParam("hide_from_global_feed")) ? 0 : 1);
|
$club->setHide_From_Global_Feed(empty($this->postParam("hide_from_global_feed")) ? 0 : 1);
|
||||||
|
|
||||||
$website = $this->postParam("website") ?? "";
|
$website = $this->postParam("website") ?? "";
|
||||||
|
@ -411,4 +426,37 @@ final class GroupPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
$this->flashFail("succ", tr("information_-1"), tr("group_owner_setted", $newOwner->getCanonicalName(), $club->getName()));
|
$this->flashFail("succ", tr("information_-1"), tr("group_owner_setted", $newOwner->getCanonicalName(), $club->getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderSuggested(int $id): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
|
$club = $this->clubs->get($id);
|
||||||
|
if(!$club)
|
||||||
|
$this->notFound();
|
||||||
|
else
|
||||||
|
$this->template->club = $club;
|
||||||
|
|
||||||
|
if($club->getWallType() == 0) {
|
||||||
|
$this->flash("err", tr("error_suggestions"), tr("error_suggestions_closed"));
|
||||||
|
$this->redirect("/club".$club->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if($club->getWallType() == 1) {
|
||||||
|
$this->flash("err", tr("error_suggestions"), tr("error_suggestions_open"));
|
||||||
|
$this->redirect("/club".$club->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$club->canBeModifiedBy($this->user->identity)) {
|
||||||
|
$this->template->posts = iterator_to_array((new Posts)->getSuggestedPostsByUser($club->getId(), $this->user->id, (int) ($this->queryParam("p") ?? 1)));
|
||||||
|
$this->template->count = (new Posts)->getSuggestedPostsCountByUser($club->getId(), $this->user->id);
|
||||||
|
$this->template->type = "my";
|
||||||
|
} else {
|
||||||
|
$this->template->posts = iterator_to_array((new Posts)->getSuggestedPosts($club->getId(), (int) ($this->queryParam("p") ?? 1)));
|
||||||
|
$this->template->count = (new Posts)->getSuggestedPostsCount($club->getId());
|
||||||
|
$this->template->type = "everyone";
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->page = (int) ($this->queryParam("p") ?? 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
|
use openvk\Web\Models\Repositories\{Posts, Comments};
|
||||||
use MessagePack\MessagePack;
|
use MessagePack\MessagePack;
|
||||||
use Chandler\Session\Session;
|
use Chandler\Session\Session;
|
||||||
|
|
||||||
|
@ -95,4 +96,41 @@ final class InternalAPIPresenter extends OpenVKPresenter
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderGetPhotosFromPost(int $owner_id, int $post_id) {
|
||||||
|
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||||
|
header("HTTP/1.1 405 Method Not Allowed");
|
||||||
|
exit("иди нахуй заебал");
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->postParam("parentType", false) == "post") {
|
||||||
|
$post = (new Posts)->getPostById($owner_id, $post_id, true);
|
||||||
|
} else {
|
||||||
|
$post = (new Comments)->get($post_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(is_null($post)) {
|
||||||
|
$this->returnJson([
|
||||||
|
"success" => 0
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$response = [];
|
||||||
|
$attachments = $post->getChildren();
|
||||||
|
foreach($attachments as $attachment)
|
||||||
|
{
|
||||||
|
if($attachment instanceof \openvk\Web\Models\Entities\Photo)
|
||||||
|
{
|
||||||
|
$response[] = [
|
||||||
|
"url" => $attachment->getURLBySizeId('normal'),
|
||||||
|
"id" => $attachment->getPrettyId()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->returnJson([
|
||||||
|
"success" => 1,
|
||||||
|
"body" => $response
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ final class MessengerPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
$messages = [];
|
$messages = [];
|
||||||
$correspondence = new Correspondence($this->user->identity, $correspondent);
|
$correspondence = new Correspondence($this->user->identity, $correspondent);
|
||||||
foreach($correspondence->getMessages(1, $lastMsg === 0 ? NULL : $lastMsg) as $message)
|
foreach($correspondence->getMessages(1, $lastMsg === 0 ? NULL : $lastMsg, NULL, 0) as $message)
|
||||||
$messages[] = $message->simplify();
|
$messages[] = $message->simplify();
|
||||||
|
|
||||||
header("Content-Type: application/json");
|
header("Content-Type: application/json");
|
||||||
|
|
|
@ -40,6 +40,8 @@ final class NotesPresenter extends OpenVKPresenter
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->user->identity ?? NULL))
|
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->user->identity ?? NULL))
|
||||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||||
|
if(!$note->canBeViewedBy($this->user->identity))
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||||
|
|
||||||
$this->template->cCount = $note->getCommentsCount();
|
$this->template->cCount = $note->getCommentsCount();
|
||||||
$this->template->cPage = (int) ($this->queryParam("p") ?? 1);
|
$this->template->cPage = (int) ($this->queryParam("p") ?? 1);
|
||||||
|
|
|
@ -198,6 +198,9 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
{
|
{
|
||||||
$user = Authenticator::i()->getUser();
|
$user = Authenticator::i()->getUser();
|
||||||
|
|
||||||
|
if(!$this->template)
|
||||||
|
$this->template = new \stdClass;
|
||||||
|
|
||||||
$this->template->isXmas = intval(date('d')) >= 1 && date('m') == 12 || intval(date('d')) <= 15 && date('m') == 1 ? true : false;
|
$this->template->isXmas = intval(date('d')) >= 1 && date('m') == 12 || intval(date('d')) <= 15 && date('m') == 1 ? true : false;
|
||||||
$this->template->isTimezoned = Session::i()->get("_timezoneOffset");
|
$this->template->isTimezoned = Session::i()->get("_timezoneOffset");
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,9 @@ final class PhotosPresenter extends OpenVKPresenter
|
||||||
if(!$album) $this->notFound();
|
if(!$album) $this->notFound();
|
||||||
if($album->getPrettyId() !== $owner . "_" . $id || $album->isDeleted())
|
if($album->getPrettyId() !== $owner . "_" . $id || $album->isDeleted())
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
|
|
||||||
|
if(!$album->canBeViewedBy($this->user->identity))
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||||
|
|
||||||
if($owner > 0 /* bc we currently don't have perms for clubs */) {
|
if($owner > 0 /* bc we currently don't have perms for clubs */) {
|
||||||
$ownerObject = (new Users)->get($owner);
|
$ownerObject = (new Users)->get($owner);
|
||||||
|
@ -158,7 +161,8 @@ final class PhotosPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
$photo = $this->photos->getByOwnerAndVID($ownerId, $photoId);
|
$photo = $this->photos->getByOwnerAndVID($ownerId, $photoId);
|
||||||
if(!$photo || $photo->isDeleted()) $this->notFound();
|
if(!$photo || $photo->isDeleted()) $this->notFound();
|
||||||
|
if(!$photo->canBeViewedBy($this->user->identity))
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||||
if(!is_null($this->queryParam("from"))) {
|
if(!is_null($this->queryParam("from"))) {
|
||||||
if(preg_match("%^album([0-9]++)$%", $this->queryParam("from"), $matches) === 1) {
|
if(preg_match("%^album([0-9]++)$%", $this->queryParam("from"), $matches) === 1) {
|
||||||
$album = $this->albums->get((int) $matches[1]);
|
$album = $this->albums->get((int) $matches[1]);
|
||||||
|
@ -222,15 +226,20 @@ final class PhotosPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
$this->willExecuteWriteAction(true);
|
$this->willExecuteWriteAction(true);
|
||||||
|
|
||||||
if(is_null($this->queryParam("album")))
|
if(is_null($this->queryParam("album"))) {
|
||||||
$this->flashFail("err", tr("error"), tr("error_adding_to_deleted"), 500, true);
|
$album = $this->albums->getUserWallAlbum($this->user->identity);
|
||||||
|
} else {
|
||||||
[$owner, $id] = explode("_", $this->queryParam("album"));
|
[$owner, $id] = explode("_", $this->queryParam("album"));
|
||||||
$album = $this->albums->get((int) $id);
|
$album = $this->albums->get((int) $id);
|
||||||
|
}
|
||||||
|
|
||||||
if(!$album)
|
if(!$album)
|
||||||
$this->flashFail("err", tr("error"), tr("error_adding_to_deleted"), 500, true);
|
$this->flashFail("err", tr("error"), tr("error_adding_to_deleted"), 500, true);
|
||||||
if(is_null($this->user) || !$album->canBeModifiedBy($this->user->identity))
|
|
||||||
|
# Для быстрой загрузки фоток из пикера фотографий нужен альбом, но юзер не может загружать фото
|
||||||
|
# в системные альбомы, так что так.
|
||||||
|
if(is_null($this->user) || !is_null($this->queryParam("album")) && !$album->canBeModifiedBy($this->user->identity))
|
||||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"), 500, true);
|
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"), 500, true);
|
||||||
|
|
||||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
@ -261,6 +270,9 @@ final class PhotosPresenter extends OpenVKPresenter
|
||||||
$this->flashFail("err", tr("no_photo"), tr("select_file"), 500, true);
|
$this->flashFail("err", tr("no_photo"), tr("select_file"), 500, true);
|
||||||
|
|
||||||
$photos = [];
|
$photos = [];
|
||||||
|
if((int)$this->postParam("count") > 10)
|
||||||
|
$this->flashFail("err", tr("no_photo"), "ты еблан", 500, true);
|
||||||
|
|
||||||
for($i = 0; $i < $this->postParam("count"); $i++) {
|
for($i = 0; $i < $this->postParam("count"); $i++) {
|
||||||
try {
|
try {
|
||||||
$photo = new Photo;
|
$photo = new Photo;
|
||||||
|
@ -328,7 +340,10 @@ final class PhotosPresenter extends OpenVKPresenter
|
||||||
if(is_null($this->user) || $this->user->id != $ownerId)
|
if(is_null($this->user) || $this->user->id != $ownerId)
|
||||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||||
|
|
||||||
$redirect = $photo->getAlbum()->getOwner() instanceof User ? "/id0" : "/club" . $ownerId;
|
if(!is_null($album = $photo->getAlbum()))
|
||||||
|
$redirect = $album->getOwner() instanceof User ? "/id0" : "/club" . $ownerId;
|
||||||
|
else
|
||||||
|
$redirect = "/id0";
|
||||||
|
|
||||||
$photo->isolate();
|
$photo->isolate();
|
||||||
$photo->delete();
|
$photo->delete();
|
||||||
|
|
|
@ -23,7 +23,7 @@ final class ReportPresenter extends OpenVKPresenter
|
||||||
if ($_SERVER["REQUEST_METHOD"] === "POST")
|
if ($_SERVER["REQUEST_METHOD"] === "POST")
|
||||||
$this->assertNoCSRF();
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
$act = in_array($this->queryParam("act"), ["post", "photo", "video", "group", "comment", "note", "app", "user"]) ? $this->queryParam("act") : NULL;
|
$act = in_array($this->queryParam("act"), ["post", "photo", "video", "group", "comment", "note", "app", "user", "audio"]) ? $this->queryParam("act") : NULL;
|
||||||
|
|
||||||
if (!$this->queryParam("orig")) {
|
if (!$this->queryParam("orig")) {
|
||||||
$this->template->reports = $this->reports->getReports(0, (int)($this->queryParam("p") ?? 1), $act, $_SERVER["REQUEST_METHOD"] !== "POST");
|
$this->template->reports = $this->reports->getReports(0, (int)($this->queryParam("p") ?? 1), $act, $_SERVER["REQUEST_METHOD"] !== "POST");
|
||||||
|
@ -88,7 +88,7 @@ final class ReportPresenter extends OpenVKPresenter
|
||||||
if(!$id)
|
if(!$id)
|
||||||
exit(json_encode([ "error" => tr("error_segmentation") ]));
|
exit(json_encode([ "error" => tr("error_segmentation") ]));
|
||||||
|
|
||||||
if(in_array($this->queryParam("type"), ["post", "photo", "video", "group", "comment", "note", "app", "user"])) {
|
if(in_array($this->queryParam("type"), ["post", "photo", "video", "group", "comment", "note", "app", "user", "audio"])) {
|
||||||
if (count(iterator_to_array($this->reports->getDuplicates($this->queryParam("type"), $id, NULL, $this->user->id))) <= 0) {
|
if (count(iterator_to_array($this->reports->getDuplicates($this->queryParam("type"), $id, NULL, $this->user->id))) <= 0) {
|
||||||
$report = new Report;
|
$report = new Report;
|
||||||
$report->setUser_id($this->user->id);
|
$report->setUser_id($this->user->id);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\{User, Club};
|
use openvk\Web\Models\Entities\{User, Club};
|
||||||
use openvk\Web\Models\Repositories\{Users, Clubs, Posts, Comments, Videos, Applications, Notes};
|
use openvk\Web\Models\Repositories\{Users, Clubs, Posts, Comments, Videos, Applications, Notes, Audios};
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
|
||||||
final class SearchPresenter extends OpenVKPresenter
|
final class SearchPresenter extends OpenVKPresenter
|
||||||
|
@ -13,6 +13,7 @@ final class SearchPresenter extends OpenVKPresenter
|
||||||
private $videos;
|
private $videos;
|
||||||
private $apps;
|
private $apps;
|
||||||
private $notes;
|
private $notes;
|
||||||
|
private $audios;
|
||||||
|
|
||||||
function __construct(Users $users, Clubs $clubs)
|
function __construct(Users $users, Clubs $clubs)
|
||||||
{
|
{
|
||||||
|
@ -23,22 +24,21 @@ final class SearchPresenter extends OpenVKPresenter
|
||||||
$this->videos = new Videos;
|
$this->videos = new Videos;
|
||||||
$this->apps = new Applications;
|
$this->apps = new Applications;
|
||||||
$this->notes = new Notes;
|
$this->notes = new Notes;
|
||||||
|
$this->audios = new Audios;
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderIndex(): void
|
function renderIndex(): void
|
||||||
{
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
$query = $this->queryParam("query") ?? "";
|
$query = $this->queryParam("query") ?? "";
|
||||||
$type = $this->queryParam("type") ?? "users";
|
$type = $this->queryParam("type") ?? "users";
|
||||||
$sorter = $this->queryParam("sort") ?? "id";
|
$sorter = $this->queryParam("sort") ?? "id";
|
||||||
$invert = $this->queryParam("invert") == 1 ? "ASC" : "DESC";
|
$invert = $this->queryParam("invert") == 1 ? "ASC" : "DESC";
|
||||||
$page = (int) ($this->queryParam("p") ?? 1);
|
$page = (int) ($this->queryParam("p") ?? 1);
|
||||||
|
|
||||||
$this->willExecuteWriteAction();
|
|
||||||
if($query != "")
|
|
||||||
$this->assertUserLoggedIn();
|
|
||||||
|
|
||||||
# https://youtu.be/pSAWM5YuXx8
|
# https://youtu.be/pSAWM5YuXx8
|
||||||
|
|
||||||
$repos = [
|
$repos = [
|
||||||
|
@ -47,7 +47,7 @@ final class SearchPresenter extends OpenVKPresenter
|
||||||
"posts" => "posts",
|
"posts" => "posts",
|
||||||
"comments" => "comments",
|
"comments" => "comments",
|
||||||
"videos" => "videos",
|
"videos" => "videos",
|
||||||
"audios" => "posts",
|
"audios" => "audios",
|
||||||
"apps" => "apps",
|
"apps" => "apps",
|
||||||
"notes" => "notes"
|
"notes" => "notes"
|
||||||
];
|
];
|
||||||
|
@ -62,7 +62,17 @@ final class SearchPresenter extends OpenVKPresenter
|
||||||
break;
|
break;
|
||||||
case "rating":
|
case "rating":
|
||||||
$sort = "rating " . $invert;
|
$sort = "rating " . $invert;
|
||||||
break;
|
break;
|
||||||
|
case "length":
|
||||||
|
if($type != "audios") break;
|
||||||
|
|
||||||
|
$sort = "length " . $invert;
|
||||||
|
break;
|
||||||
|
case "listens":
|
||||||
|
if($type != "audios") break;
|
||||||
|
|
||||||
|
$sort = "listens " . $invert;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$parameters = [
|
$parameters = [
|
||||||
|
@ -86,18 +96,22 @@ final class SearchPresenter extends OpenVKPresenter
|
||||||
"hometown" => $this->queryParam("hometown") != "" ? $this->queryParam("hometown") : NULL,
|
"hometown" => $this->queryParam("hometown") != "" ? $this->queryParam("hometown") : NULL,
|
||||||
"before" => $this->queryParam("datebefore") != "" ? strtotime($this->queryParam("datebefore")) : NULL,
|
"before" => $this->queryParam("datebefore") != "" ? strtotime($this->queryParam("datebefore")) : NULL,
|
||||||
"after" => $this->queryParam("dateafter") != "" ? strtotime($this->queryParam("dateafter")) : NULL,
|
"after" => $this->queryParam("dateafter") != "" ? strtotime($this->queryParam("dateafter")) : NULL,
|
||||||
"gender" => $this->queryParam("gender") != "" && $this->queryParam("gender") != 2 ? $this->queryParam("gender") : NULL
|
"gender" => $this->queryParam("gender") != "" && $this->queryParam("gender") != 2 ? $this->queryParam("gender") : NULL,
|
||||||
|
"doNotSearchPrivate" => true,
|
||||||
|
"only_performers" => $this->queryParam("only_performers") == "on" ? "1" : NULL,
|
||||||
|
"with_lyrics" => $this->queryParam("with_lyrics") == "on" ? true : NULL,
|
||||||
];
|
];
|
||||||
|
|
||||||
$repo = $repos[$type] or $this->throwError(400, "Bad Request", "Invalid search entity $type.");
|
$repo = $repos[$type] or $this->throwError(400, "Bad Request", "Invalid search entity $type.");
|
||||||
|
|
||||||
$results = $this->{$repo}->find($query, $parameters, $sort);
|
$results = $this->{$repo}->find($query, $parameters, $sort);
|
||||||
$iterator = $results->page($page);
|
$iterator = $results->page($page, 14);
|
||||||
$count = $results->size();
|
$count = $results->size();
|
||||||
|
|
||||||
$this->template->iterator = iterator_to_array($iterator);
|
$this->template->iterator = iterator_to_array($iterator);
|
||||||
$this->template->count = $count;
|
$this->template->count = $count;
|
||||||
$this->template->type = $type;
|
$this->template->type = $type;
|
||||||
$this->template->page = $page;
|
$this->template->page = $page;
|
||||||
|
$this->template->perPage = 14;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ final class SupportPresenter extends OpenVKPresenter
|
||||||
$this->template->count = $this->tickets->getTicketsCountByUserId($this->user->id);
|
$this->template->count = $this->tickets->getTicketsCountByUserId($this->user->id);
|
||||||
if($this->template->mode === "list") {
|
if($this->template->mode === "list") {
|
||||||
$this->template->page = (int) ($this->queryParam("p") ?? 1);
|
$this->template->page = (int) ($this->queryParam("p") ?? 1);
|
||||||
$this->template->tickets = $this->tickets->getTicketsByUserId($this->user->id, $this->template->page);
|
$this->template->tickets = iterator_to_array($this->tickets->getTicketsByUserId($this->user->id, $this->template->page));
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->template->mode === "new")
|
if($this->template->mode === "new")
|
||||||
|
|
|
@ -5,7 +5,7 @@ use openvk\Web\Util\Sms;
|
||||||
use openvk\Web\Themes\Themepacks;
|
use openvk\Web\Themes\Themepacks;
|
||||||
use openvk\Web\Models\Entities\{Photo, Post, EmailChangeVerification};
|
use openvk\Web\Models\Entities\{Photo, Post, EmailChangeVerification};
|
||||||
use openvk\Web\Models\Entities\Notifications\{CoinsTransferNotification, RatingUpNotification};
|
use openvk\Web\Models\Entities\Notifications\{CoinsTransferNotification, RatingUpNotification};
|
||||||
use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Videos, Notes, Vouchers, EmailChangeVerifications};
|
use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Videos, Notes, Vouchers, EmailChangeVerifications, Audios};
|
||||||
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||||
use openvk\Web\Util\Validator;
|
use openvk\Web\Util\Validator;
|
||||||
use Chandler\Security\Authenticator;
|
use Chandler\Security\Authenticator;
|
||||||
|
@ -29,10 +29,14 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
function renderView(int $id): void
|
function renderView(int $id): void
|
||||||
{
|
{
|
||||||
$user = $this->users->get($id);
|
$user = $this->users->get($id);
|
||||||
if(!$user || $user->isDeleted()) {
|
if(!$user || $user->isDeleted() || !$user->canBeViewedBy($this->user->identity)) {
|
||||||
if(!is_null($user) && $user->isDeactivated()) {
|
if(!is_null($user) && $user->isDeactivated()) {
|
||||||
$this->template->_template = "User/deactivated.xml";
|
$this->template->_template = "User/deactivated.xml";
|
||||||
|
|
||||||
|
$this->template->user = $user;
|
||||||
|
} else if(!$user->canBeViewedBy($this->user->identity)) {
|
||||||
|
$this->template->_template = "User/private.xml";
|
||||||
|
|
||||||
$this->template->user = $user;
|
$this->template->user = $user;
|
||||||
} else {
|
} else {
|
||||||
$this->template->_template = "User/deleted.xml";
|
$this->template->_template = "User/deleted.xml";
|
||||||
|
@ -45,7 +49,10 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
$this->template->videosCount = (new Videos)->getUserVideosCount($user);
|
$this->template->videosCount = (new Videos)->getUserVideosCount($user);
|
||||||
$this->template->notes = (new Notes)->getUserNotes($user, 1, 4);
|
$this->template->notes = (new Notes)->getUserNotes($user, 1, 4);
|
||||||
$this->template->notesCount = (new Notes)->getUserNotesCount($user);
|
$this->template->notesCount = (new Notes)->getUserNotesCount($user);
|
||||||
|
$this->template->audios = (new Audios)->getRandomThreeAudiosByEntityId($user->getId());
|
||||||
|
$this->template->audiosCount = (new Audios)->getUserCollectionSize($user);
|
||||||
|
$this->template->audioStatus = $user->getCurrentAudioStatus();
|
||||||
|
|
||||||
$this->template->user = $user;
|
$this->template->user = $user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +62,7 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
$user = $this->users->get($id);
|
$user = $this->users->get($id);
|
||||||
$page = abs($this->queryParam("p") ?? 1);
|
$page = abs((int)($this->queryParam("p") ?? 1));
|
||||||
if(!$user)
|
if(!$user)
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
elseif (!$user->getPrivacyPermission('friends.read', $this->user->identity ?? NULL))
|
elseif (!$user->getPrivacyPermission('friends.read', $this->user->identity ?? NULL))
|
||||||
|
@ -163,12 +170,36 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
if ($this->postParam("marialstatus") <= 8 && $this->postParam("marialstatus") >= 0)
|
if ($this->postParam("marialstatus") <= 8 && $this->postParam("marialstatus") >= 0)
|
||||||
$user->setMarital_Status($this->postParam("marialstatus"));
|
$user->setMarital_Status($this->postParam("marialstatus"));
|
||||||
|
|
||||||
|
if ($this->postParam("maritalstatus-user")) {
|
||||||
|
if (in_array((int) $this->postParam("marialstatus"), [0, 1, 8])) {
|
||||||
|
$user->setMarital_Status_User(NULL);
|
||||||
|
} else {
|
||||||
|
$mUser = (new Users)->getByAddress($this->postParam("maritalstatus-user"));
|
||||||
|
if ($mUser) {
|
||||||
|
if ($mUser->getId() !== $this->user->id) {
|
||||||
|
$user->setMarital_Status_User($mUser->getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->postParam("politViews") <= 9 && $this->postParam("politViews") >= 0)
|
if ($this->postParam("politViews") <= 9 && $this->postParam("politViews") >= 0)
|
||||||
$user->setPolit_Views($this->postParam("politViews"));
|
$user->setPolit_Views($this->postParam("politViews"));
|
||||||
|
|
||||||
if ($this->postParam("gender") <= 1 && $this->postParam("gender") >= 0)
|
if ($this->postParam("pronouns") <= 2 && $this->postParam("pronouns") >= 0)
|
||||||
$user->setSex($this->postParam("gender"));
|
switch ($this->postParam("pronouns")) {
|
||||||
|
case '0':
|
||||||
|
$user->setSex(0);
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
$user->setSex(1);
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
$user->setSex(2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$user->setAudio_broadcast_enabled($this->checkbox("broadcast_music"));
|
||||||
|
|
||||||
if(!empty($this->postParam("phone")) && $this->postParam("phone") !== $user->getPhone()) {
|
if(!empty($this->postParam("phone")) && $this->postParam("phone") !== $user->getPhone()) {
|
||||||
if(!OPENVK_ROOT_CONF["openvk"]["credentials"]["smsc"]["enable"])
|
if(!OPENVK_ROOT_CONF["openvk"]["credentials"]["smsc"]["enable"])
|
||||||
|
@ -241,6 +272,7 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
$user->setStatus(empty($this->postParam("status")) ? NULL : $this->postParam("status"));
|
$user->setStatus(empty($this->postParam("status")) ? NULL : $this->postParam("status"));
|
||||||
|
$user->setAudio_broadcast_enabled($this->postParam("broadcast") == 1);
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
$this->returnJson([
|
$this->returnJson([
|
||||||
|
@ -430,11 +462,16 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
"friends.add",
|
"friends.add",
|
||||||
"wall.write",
|
"wall.write",
|
||||||
"messages.write",
|
"messages.write",
|
||||||
|
"audios.read",
|
||||||
];
|
];
|
||||||
foreach($settings as $setting) {
|
foreach($settings as $setting) {
|
||||||
$input = $this->postParam(str_replace(".", "_", $setting));
|
$input = $this->postParam(str_replace(".", "_", $setting));
|
||||||
$user->setPrivacySetting($setting, min(3, abs($input ?? $user->getPrivacySetting($setting))));
|
$user->setPrivacySetting($setting, min(3, (int)abs((int)$input ?? $user->getPrivacySetting($setting))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$prof = $this->postParam("profile_type") == 1 || $this->postParam("profile_type") == 0 ? (int)$this->postParam("profile_type") : 0;
|
||||||
|
$user->setProfile_type($prof);
|
||||||
|
|
||||||
} else if($_GET['act'] === "finance.top-up") {
|
} else if($_GET['act'] === "finance.top-up") {
|
||||||
$token = $this->postParam("key0") . $this->postParam("key1") . $this->postParam("key2") . $this->postParam("key3");
|
$token = $this->postParam("key0") . $this->postParam("key1") . $this->postParam("key2") . $this->postParam("key3");
|
||||||
$voucher = (new Vouchers)->getByToken($token);
|
$voucher = (new Vouchers)->getByToken($token);
|
||||||
|
@ -474,6 +511,7 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
} else if($_GET['act'] === "lMenu") {
|
} else if($_GET['act'] === "lMenu") {
|
||||||
$settings = [
|
$settings = [
|
||||||
"menu_bildoj" => "photos",
|
"menu_bildoj" => "photos",
|
||||||
|
"menu_muziko" => "audios",
|
||||||
"menu_filmetoj" => "videos",
|
"menu_filmetoj" => "videos",
|
||||||
"menu_mesagoj" => "messages",
|
"menu_mesagoj" => "messages",
|
||||||
"menu_notatoj" => "notes",
|
"menu_notatoj" => "notes",
|
||||||
|
|
|
@ -233,8 +233,13 @@ final class VKAPIPresenter extends OpenVKPresenter
|
||||||
$this->badMethodCall($object, $method, $parameter->getName());
|
$this->badMethodCall($object, $method, $parameter->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
settype($val, $parameter->getType()->getName());
|
try {
|
||||||
$params[] = $val;
|
settype($val, $parameter->getType()->getName());
|
||||||
|
$params[] = $val;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// Just ignore the exception, since
|
||||||
|
// some args are intended for internal use
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
define("VKAPI_DECL_VER", $this->requestParam("v") ?? "4.100", false);
|
define("VKAPI_DECL_VER", $this->requestParam("v") ?? "4.100", false);
|
||||||
|
|
|
@ -39,11 +39,12 @@ final class VideosPresenter extends OpenVKPresenter
|
||||||
function renderView(int $owner, int $vId): void
|
function renderView(int $owner, int $vId): void
|
||||||
{
|
{
|
||||||
$user = $this->users->get($owner);
|
$user = $this->users->get($owner);
|
||||||
|
$video = $this->videos->getByOwnerAndVID($owner, $vId);
|
||||||
|
|
||||||
if(!$user) $this->notFound();
|
if(!$user) $this->notFound();
|
||||||
|
if(!$video || $video->isDeleted()) $this->notFound();
|
||||||
if(!$user->getPrivacyPermission('videos.read', $this->user->identity ?? NULL))
|
if(!$user->getPrivacyPermission('videos.read', $this->user->identity ?? NULL))
|
||||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||||
|
|
||||||
if($this->videos->getByOwnerAndVID($owner, $vId)->isDeleted()) $this->notFound();
|
|
||||||
|
|
||||||
$this->template->user = $user;
|
$this->template->user = $user;
|
||||||
$this->template->video = $this->videos->getByOwnerAndVID($owner, $vId);
|
$this->template->video = $this->videos->getByOwnerAndVID($owner, $vId);
|
||||||
|
@ -74,18 +75,18 @@ final class VideosPresenter extends OpenVKPresenter
|
||||||
else if(!empty($this->postParam("link")))
|
else if(!empty($this->postParam("link")))
|
||||||
$video->setLink($this->postParam("link"));
|
$video->setLink($this->postParam("link"));
|
||||||
else
|
else
|
||||||
$this->flashFail("err", tr("no_video"), tr("no_video_desc"));
|
$this->flashFail("err", tr("no_video_error"), tr("no_video_description"));
|
||||||
} catch(\DomainException $ex) {
|
} catch(\DomainException $ex) {
|
||||||
$this->flashFail("err", tr("error_occured"), tr("error_video_damaged_file"));
|
$this->flashFail("err", tr("error_video"), tr("file_corrupted"));
|
||||||
} catch(ISE $ex) {
|
} catch(ISE $ex) {
|
||||||
$this->flashFail("err", tr("error_occured"), tr("error_video_incorrect_link"));
|
$this->flashFail("err", tr("error_video"), tr("link_incorrect"));
|
||||||
}
|
}
|
||||||
|
|
||||||
$video->save();
|
$video->save();
|
||||||
|
|
||||||
$this->redirect("/video" . $video->getPrettyId());
|
$this->redirect("/video" . $video->getPrettyId());
|
||||||
} else {
|
} else {
|
||||||
$this->flashFail("err", tr("error_occured"), tr("error_video_no_title"));
|
$this->flashFail("err", tr("error_video"), tr("no_name_error"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,14 +100,14 @@ final class VideosPresenter extends OpenVKPresenter
|
||||||
if(!$video)
|
if(!$video)
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
if(is_null($this->user) || $this->user->id !== $owner)
|
if(is_null($this->user) || $this->user->id !== $owner)
|
||||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
$this->flashFail("err", tr("access_denied_error"), tr("access_denied_error_description"));
|
||||||
|
|
||||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
$video->setName(empty($this->postParam("name")) ? NULL : $this->postParam("name"));
|
$video->setName(empty($this->postParam("name")) ? NULL : $this->postParam("name"));
|
||||||
$video->setDescription(empty($this->postParam("desc")) ? NULL : $this->postParam("desc"));
|
$video->setDescription(empty($this->postParam("desc")) ? NULL : $this->postParam("desc"));
|
||||||
$video->save();
|
$video->save();
|
||||||
|
|
||||||
$this->flash("succ", tr("changes_saved"), tr("new_data_video"));
|
$this->flash("succ", tr("changes_saved"), tr("changes_saved_video_comment"));
|
||||||
$this->redirect("/video" . $video->getPrettyId());
|
$this->redirect("/video" . $video->getPrettyId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,9 +129,29 @@ final class VideosPresenter extends OpenVKPresenter
|
||||||
$video->deleteVideo($owner, $vid);
|
$video->deleteVideo($owner, $vid);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->flashFail("err", tr("error_deleting_video"), tr("login_please"));
|
$this->flashFail("err", tr("cant_delete_video"), tr("cant_delete_video_comment"));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->redirect("/videos" . $owner);
|
$this->redirect("/videos" . $owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderLike(int $owner, int $video_id): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
|
$video = $this->videos->getByOwnerAndVID($owner, $video_id);
|
||||||
|
if(!$video || $video->isDeleted() || $video->getOwner()->isDeleted()) $this->notFound();
|
||||||
|
|
||||||
|
if(method_exists($video, "canBeViewedBy") && !$video->canBeViewedBy($this->user->identity)) {
|
||||||
|
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!is_null($this->user)) {
|
||||||
|
$video->toggleLike($this->user->identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->returnJson(["success" => true]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Exceptions\TooMuchOptionsException;
|
use openvk\Web\Models\Exceptions\TooMuchOptionsException;
|
||||||
use openvk\Web\Models\Entities\{Poll, Post, Photo, Video, Club, User};
|
use openvk\Web\Models\Entities\{Poll, Post, Photo, Video, Club, User};
|
||||||
use openvk\Web\Models\Entities\Notifications\{MentionNotification, RepostNotification, WallPostNotification};
|
use openvk\Web\Models\Entities\Notifications\{MentionNotification, RepostNotification, WallPostNotification, PostAcceptedNotification, NewSuggestedPostsNotification};
|
||||||
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums, Notes, Videos, Comments};
|
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums, Notes, Videos, Comments, Photos, Audios};
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
use Nette\InvalidStateException as ISE;
|
use Nette\InvalidStateException as ISE;
|
||||||
use Bhaktaraz\RSSGenerator\Item;
|
use Bhaktaraz\RSSGenerator\Item;
|
||||||
|
@ -46,7 +46,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
function renderWall(int $user, bool $embedded = false): void
|
function renderWall(int $user, bool $embedded = false): void
|
||||||
{
|
{
|
||||||
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
|
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
|
||||||
if ($owner->isBanned())
|
if ($owner->isBanned() || !$owner->canBeViewedBy($this->user->identity))
|
||||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
|
|
||||||
if(is_null($this->user)) {
|
if(is_null($this->user)) {
|
||||||
|
@ -66,11 +66,32 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$this->template->oObj = $owner;
|
$this->template->oObj = $owner;
|
||||||
if($user < 0)
|
if($user < 0)
|
||||||
$this->template->club = $owner;
|
$this->template->club = $owner;
|
||||||
|
|
||||||
|
$iterator = NULL;
|
||||||
|
$count = 0;
|
||||||
|
$type = $this->queryParam("type") ?? "all";
|
||||||
|
|
||||||
|
switch($type) {
|
||||||
|
default:
|
||||||
|
case "all":
|
||||||
|
$iterator = $this->posts->getPostsFromUsersWall($user, (int) ($_GET["p"] ?? 1));
|
||||||
|
$count = $this->posts->getPostCountOnUserWall($user);
|
||||||
|
break;
|
||||||
|
case "owners":
|
||||||
|
$iterator = $this->posts->getOwnersPostsFromWall($user, (int) ($_GET["p"] ?? 1));
|
||||||
|
$count = $this->posts->getOwnersCountOnUserWall($user);
|
||||||
|
break;
|
||||||
|
case "others":
|
||||||
|
$iterator = $this->posts->getOthersPostsFromWall($user, (int) ($_GET["p"] ?? 1));
|
||||||
|
$count = $this->posts->getOthersCountOnUserWall($user);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$this->template->owner = $user;
|
$this->template->owner = $user;
|
||||||
$this->template->canPost = $canPost;
|
$this->template->canPost = $canPost;
|
||||||
$this->template->count = $this->posts->getPostCountOnUserWall($user);
|
$this->template->count = $count;
|
||||||
$this->template->posts = iterator_to_array($this->posts->getPostsFromUsersWall($user, (int) ($_GET["p"] ?? 1)));
|
$this->template->type = $type;
|
||||||
|
$this->template->posts = iterator_to_array($iterator);
|
||||||
$this->template->paginatorConf = (object) [
|
$this->template->paginatorConf = (object) [
|
||||||
"count" => $this->template->count,
|
"count" => $this->template->count,
|
||||||
"page" => (int) ($_GET["p"] ?? 1),
|
"page" => (int) ($_GET["p"] ?? 1),
|
||||||
|
@ -93,7 +114,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
if(is_null($this->user)) {
|
if(is_null($this->user)) {
|
||||||
$canPost = false;
|
$canPost = false;
|
||||||
} else if($user > 0) {
|
} else if($user > 0) {
|
||||||
if(!$owner->isBanned())
|
if(!$owner->isBanned() && $owner->canBeViewedBy($this->user->identity))
|
||||||
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity);
|
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity);
|
||||||
else
|
else
|
||||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
|
@ -150,11 +171,12 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
->select("id")
|
->select("id")
|
||||||
->where("wall IN (?)", $ids)
|
->where("wall IN (?)", $ids)
|
||||||
->where("deleted", 0)
|
->where("deleted", 0)
|
||||||
|
->where("suggested", 0)
|
||||||
->order("created DESC");
|
->order("created DESC");
|
||||||
$this->template->paginatorConf = (object) [
|
$this->template->paginatorConf = (object) [
|
||||||
"count" => sizeof($posts),
|
"count" => sizeof($posts),
|
||||||
"page" => (int) ($_GET["p"] ?? 1),
|
"page" => (int) ($_GET["p"] ?? 1),
|
||||||
"amount" => sizeof($posts->page((int) ($_GET["p"] ?? 1), $perPage)),
|
"amount" => $posts->page((int) ($_GET["p"] ?? 1), $perPage)->count(),
|
||||||
"perPage" => $perPage,
|
"perPage" => $perPage,
|
||||||
];
|
];
|
||||||
$this->template->posts = [];
|
$this->template->posts = [];
|
||||||
|
@ -168,8 +190,9 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
$page = (int) ($_GET["p"] ?? 1);
|
$page = (int) ($_GET["p"] ?? 1);
|
||||||
$pPage = min((int) ($_GET["posts"] ?? OPENVK_DEFAULT_PER_PAGE), 50);
|
$pPage = min((int) ($_GET["posts"] ?? OPENVK_DEFAULT_PER_PAGE), 50);
|
||||||
|
|
||||||
$queryBase = "FROM `posts` LEFT JOIN `groups` ON GREATEST(`posts`.`wall`, 0) = 0 AND `groups`.`id` = ABS(`posts`.`wall`) WHERE (`groups`.`hide_from_global_feed` = 0 OR `groups`.`name` IS NULL) AND `posts`.`deleted` = 0";
|
$queryBase = "FROM `posts` LEFT JOIN `groups` ON GREATEST(`posts`.`wall`, 0) = 0 AND `groups`.`id` = ABS(`posts`.`wall`) LEFT JOIN `profiles` ON LEAST(`posts`.`wall`, 0) = 0 AND `profiles`.`id` = ABS(`posts`.`wall`)";
|
||||||
|
$queryBase .= "WHERE (`groups`.`hide_from_global_feed` = 0 OR `groups`.`name` IS NULL) AND (`profiles`.`profile_type` = 0 OR `profiles`.`first_name` IS NULL) AND `posts`.`deleted` = 0 AND `posts`.`suggested` = 0";
|
||||||
|
|
||||||
if($this->user->identity->getNsfwTolerance() === User::NSFW_INTOLERANT)
|
if($this->user->identity->getNsfwTolerance() === User::NSFW_INTOLERANT)
|
||||||
$queryBase .= " AND `nsfw` = 0";
|
$queryBase .= " AND `nsfw` = 0";
|
||||||
|
@ -188,7 +211,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$this->template->paginatorConf = (object) [
|
$this->template->paginatorConf = (object) [
|
||||||
"count" => $count,
|
"count" => $count,
|
||||||
"page" => (int) ($_GET["p"] ?? 1),
|
"page" => (int) ($_GET["p"] ?? 1),
|
||||||
"amount" => sizeof($posts),
|
"amount" => $posts->getRowCount(),
|
||||||
"perPage" => $pPage,
|
"perPage" => $pPage,
|
||||||
];
|
];
|
||||||
foreach($posts as $post)
|
foreach($posts as $post)
|
||||||
|
@ -255,23 +278,23 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
if($this->postParam("force_sign") === "on")
|
if($this->postParam("force_sign") === "on")
|
||||||
$flags |= 0b01000000;
|
$flags |= 0b01000000;
|
||||||
|
|
||||||
try {
|
$photos = [];
|
||||||
$photo = NULL;
|
|
||||||
$video = NULL;
|
if(!empty($this->postParam("photos"))) {
|
||||||
if($_FILES["_pic_attachment"]["error"] === UPLOAD_ERR_OK) {
|
$un = rtrim($this->postParam("photos"), ",");
|
||||||
$album = NULL;
|
$arr = explode(",", $un);
|
||||||
if(!$anon && $wall > 0 && $wall === $this->user->id)
|
|
||||||
$album = (new Albums)->getUserWallAlbum($wallOwner);
|
if(sizeof($arr) < 11) {
|
||||||
|
foreach($arr as $dat) {
|
||||||
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album, $anon);
|
$ids = explode("_", $dat);
|
||||||
|
$photo = (new Photos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
|
||||||
|
|
||||||
|
if(!$photo || $photo->isDeleted())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$photos[] = $photo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK)
|
|
||||||
$video = Video::fastMake($this->user->id, $_FILES["_vid_attachment"]["name"], $this->postParam("text"), $_FILES["_vid_attachment"], $anon);*/
|
|
||||||
} catch(\DomainException $ex) {
|
|
||||||
$this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted"));
|
|
||||||
} catch(ISE $ex) {
|
|
||||||
$this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted_or_too_large"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -317,8 +340,27 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$audios = [];
|
||||||
|
|
||||||
|
if(!empty($this->postParam("audios"))) {
|
||||||
|
$un = rtrim($this->postParam("audios"), ",");
|
||||||
|
$arr = explode(",", $un);
|
||||||
|
|
||||||
|
if(sizeof($arr) < 11) {
|
||||||
|
foreach($arr as $dat) {
|
||||||
|
$ids = explode("_", $dat);
|
||||||
|
$audio = (new Audios)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
|
||||||
|
|
||||||
|
if(!$audio || $audio->isDeleted())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$audios[] = $audio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(empty($this->postParam("text")) && !$photo && sizeof($videos) < 1 && !$poll && !$note)
|
if(empty($this->postParam("text")) && sizeof($photos) < 1 && sizeof($videos) < 1 && sizeof($audios) < 1 && !$poll && !$note)
|
||||||
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big"));
|
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -330,13 +372,17 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$post->setAnonymous($anon);
|
$post->setAnonymous($anon);
|
||||||
$post->setFlags($flags);
|
$post->setFlags($flags);
|
||||||
$post->setNsfw($this->postParam("nsfw") === "on");
|
$post->setNsfw($this->postParam("nsfw") === "on");
|
||||||
|
|
||||||
|
if($wall < 0 && !$wallOwner->canBeModifiedBy($this->user->identity) && $wallOwner->getWallType() == 2)
|
||||||
|
$post->setSuggested(1);
|
||||||
|
|
||||||
$post->save();
|
$post->save();
|
||||||
} catch (\LengthException $ex) {
|
} catch (\LengthException $ex) {
|
||||||
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_too_big"));
|
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_too_big"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!is_null($photo))
|
foreach($photos as $photo)
|
||||||
$post->attach($photo);
|
$post->attach($photo);
|
||||||
|
|
||||||
if(sizeof($videos) > 0)
|
if(sizeof($videos) > 0)
|
||||||
foreach($videos as $vid)
|
foreach($videos as $vid)
|
||||||
|
@ -347,6 +393,9 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
if(!is_null($note))
|
if(!is_null($note))
|
||||||
$post->attach($note);
|
$post->attach($note);
|
||||||
|
|
||||||
|
foreach($audios as $audio)
|
||||||
|
$post->attach($audio);
|
||||||
|
|
||||||
if($wall > 0 && $wall !== $this->user->identity->getId())
|
if($wall > 0 && $wall !== $this->user->identity->getId())
|
||||||
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
||||||
|
@ -355,12 +404,32 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
if($wall > 0)
|
if($wall > 0)
|
||||||
$excludeMentions[] = $wall;
|
$excludeMentions[] = $wall;
|
||||||
|
|
||||||
$mentions = iterator_to_array($post->resolveMentions($excludeMentions));
|
if($wall < 0 && !$wallOwner->canBeModifiedBy($this->user->identity) && $wallOwner->getWallType() == 2) {
|
||||||
foreach($mentions as $mentionee)
|
# Чтобы не было упоминаний из предложки
|
||||||
if($mentionee instanceof User)
|
} else {
|
||||||
(new MentionNotification($mentionee, $post, $post->getOwner(), strip_tags($post->getText())))->emit();
|
$mentions = iterator_to_array($post->resolveMentions($excludeMentions));
|
||||||
|
|
||||||
|
foreach($mentions as $mentionee)
|
||||||
|
if($mentionee instanceof User)
|
||||||
|
(new MentionNotification($mentionee, $post, $post->getOwner(), strip_tags($post->getText())))->emit();
|
||||||
|
}
|
||||||
|
|
||||||
$this->redirect($wallOwner->getURL());
|
if($wall < 0 && !$wallOwner->canBeModifiedBy($this->user->identity) && $wallOwner->getWallType() == 2) {
|
||||||
|
$suggsCount = $this->posts->getSuggestedPostsCount($wallOwner->getId());
|
||||||
|
|
||||||
|
if($suggsCount % 10 == 0) {
|
||||||
|
$managers = $wallOwner->getManagers();
|
||||||
|
$owner = $wallOwner->getOwner();
|
||||||
|
(new NewSuggestedPostsNotification($owner, $wallOwner))->emit();
|
||||||
|
|
||||||
|
foreach($managers as $manager)
|
||||||
|
(new NewSuggestedPostsNotification($manager->getUser(), $wallOwner))->emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->redirect("/club".$wallOwner->getId()."/suggested");
|
||||||
|
} else {
|
||||||
|
$this->redirect($wallOwner->getURL());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPost(int $wall, int $post_id): void
|
function renderPost(int $wall, int $post_id): void
|
||||||
|
@ -368,22 +437,25 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$post = $this->posts->getPostById($wall, $post_id);
|
$post = $this->posts->getPostById($wall, $post_id);
|
||||||
if(!$post || $post->isDeleted())
|
if(!$post || $post->isDeleted())
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
|
|
||||||
|
if(!$post->canBeViewedBy($this->user->identity))
|
||||||
|
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
|
|
||||||
$this->logPostView($post, $wall);
|
$this->logPostView($post, $wall);
|
||||||
|
|
||||||
$this->template->post = $post;
|
$this->template->post = $post;
|
||||||
if ($post->getTargetWall() > 0) {
|
if ($post->getTargetWall() > 0) {
|
||||||
$this->template->wallOwner = (new Users)->get($post->getTargetWall());
|
$this->template->wallOwner = (new Users)->get($post->getTargetWall());
|
||||||
$this->template->isWallOfGroup = false;
|
$this->template->isWallOfGroup = false;
|
||||||
if($this->template->wallOwner->isBanned())
|
if($this->template->wallOwner->isBanned())
|
||||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
} else {
|
} else {
|
||||||
$this->template->wallOwner = (new Clubs)->get(abs($post->getTargetWall()));
|
$this->template->wallOwner = (new Clubs)->get(abs($post->getTargetWall()));
|
||||||
$this->template->isWallOfGroup = true;
|
$this->template->isWallOfGroup = true;
|
||||||
|
|
||||||
if ($this->template->wallOwner->isBanned())
|
if ($this->template->wallOwner->isBanned())
|
||||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
}
|
}
|
||||||
$this->template->cCount = $post->getCommentsCount();
|
$this->template->cCount = $post->getCommentsCount();
|
||||||
$this->template->cPage = (int) ($_GET["p"] ?? 1);
|
$this->template->cPage = (int) ($_GET["p"] ?? 1);
|
||||||
$this->template->comments = iterator_to_array($post->getComments($this->template->cPage));
|
$this->template->comments = iterator_to_array($post->getComments($this->template->cPage));
|
||||||
|
@ -471,7 +543,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$post = $this->posts->getPostById($wall, $post_id);
|
$post = $this->posts->getPostById($wall, $post_id, true);
|
||||||
if(!$post)
|
if(!$post)
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
$user = $this->user->id;
|
$user = $this->user->id;
|
||||||
|
@ -486,6 +558,9 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
else $canBeDeletedByOtherUser = false;
|
else $canBeDeletedByOtherUser = false;
|
||||||
|
|
||||||
if(!is_null($user)) {
|
if(!is_null($user)) {
|
||||||
|
if($post->getTargetWall() < 0 && !$post->getWallOwner()->canBeModifiedBy($this->user->identity) && $post->getWallOwner()->getWallType() != 1 && $post->getSuggestionType() == 0)
|
||||||
|
$this->flashFail("err", tr("failed_to_delete_post"), tr("error_deleting_suggested"));
|
||||||
|
|
||||||
if($post->getOwnerPost() == $user || $post->getTargetWall() == $user || $canBeDeletedByOtherUser) {
|
if($post->getOwnerPost() == $user || $post->getTargetWall() == $user || $canBeDeletedByOtherUser) {
|
||||||
$post->unwire();
|
$post->unwire();
|
||||||
$post->delete();
|
$post->delete();
|
||||||
|
@ -581,14 +656,14 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
"avatar" => $post->getOwner()->getAvatarUrl()
|
"avatar" => $post->getOwner()->getAvatarUrl()
|
||||||
]]);
|
]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderIgnoreSource()
|
function renderIgnoreSource()
|
||||||
{
|
{
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
$this->willExecuteWriteAction(true);
|
$this->willExecuteWriteAction(true);
|
||||||
|
|
||||||
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||||
exit("куда ты полез?");
|
exit("");
|
||||||
|
|
||||||
$owner = $this->user->id;
|
$owner = $this->user->id;
|
||||||
$ignoredSource = (int)$this->postParam("source");
|
$ignoredSource = (int)$this->postParam("source");
|
||||||
|
@ -643,4 +718,93 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$this->returnJson(["success" => true, "act" => "ignored", "text" => $tr]);
|
$this->returnJson(["success" => true, "act" => "ignored", "text" => $tr]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderAccept() {
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
$this->willExecuteWriteAction(true);
|
||||||
|
|
||||||
|
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||||
|
header("HTTP/1.1 405 Method Not Allowed");
|
||||||
|
exit("Ты дебил, это точка апи.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $this->postParam("id");
|
||||||
|
$sign = $this->postParam("sign") == 1;
|
||||||
|
$content = $this->postParam("new_content");
|
||||||
|
|
||||||
|
$post = (new Posts)->get((int)$id);
|
||||||
|
|
||||||
|
if(!$post || $post->isDeleted())
|
||||||
|
$this->flashFail("err", "Error", tr("error_accepting_invalid_post"), NULL, true);
|
||||||
|
|
||||||
|
if($post->getSuggestionType() == 0)
|
||||||
|
$this->flashFail("err", "Error", tr("error_accepting_not_suggested_post"), NULL, true);
|
||||||
|
|
||||||
|
if($post->getSuggestionType() == 2)
|
||||||
|
$this->flashFail("err", "Error", tr("error_accepting_declined_post"), NULL, true);
|
||||||
|
|
||||||
|
if(!$post->canBePinnedBy($this->user->identity))
|
||||||
|
$this->flashFail("err", "Error", "Can't accept this post.", NULL, true);
|
||||||
|
|
||||||
|
$author = $post->getOwner();
|
||||||
|
|
||||||
|
$flags = 0;
|
||||||
|
$flags |= 0b10000000;
|
||||||
|
|
||||||
|
if($sign)
|
||||||
|
$flags |= 0b01000000;
|
||||||
|
|
||||||
|
$post->setSuggested(0);
|
||||||
|
$post->setCreated(time());
|
||||||
|
$post->setApi_Source_Name(NULL);
|
||||||
|
$post->setFlags($flags);
|
||||||
|
|
||||||
|
if(mb_strlen($content) > 0)
|
||||||
|
$post->setContent($content);
|
||||||
|
|
||||||
|
$post->save();
|
||||||
|
|
||||||
|
if($author->getId() != $this->user->id)
|
||||||
|
(new PostAcceptedNotification($author, $post, $post->getWallOwner()))->emit();
|
||||||
|
|
||||||
|
$this->returnJson([
|
||||||
|
"success" => true,
|
||||||
|
"id" => $post->getPrettyId(),
|
||||||
|
"new_count" => (new Posts)->getSuggestedPostsCount($post->getWallOwner()->getId())
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDecline() {
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
$this->willExecuteWriteAction(true);
|
||||||
|
|
||||||
|
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||||
|
header("HTTP/1.1 405 Method Not Allowed");
|
||||||
|
exit("Ты дебил, это метод апи.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $this->postParam("id");
|
||||||
|
$post = (new Posts)->get((int)$id);
|
||||||
|
|
||||||
|
if(!$post || $post->isDeleted())
|
||||||
|
$this->flashFail("err", "Error", tr("error_declining_invalid_post"), NULL, true);
|
||||||
|
|
||||||
|
if($post->getSuggestionType() == 0)
|
||||||
|
$this->flashFail("err", "Error", tr("error_declining_not_suggested_post"), NULL, true);
|
||||||
|
|
||||||
|
if($post->getSuggestionType() == 2)
|
||||||
|
$this->flashFail("err", "Error", tr("error_declining_declined_post"), NULL, true);
|
||||||
|
|
||||||
|
if(!$post->canBePinnedBy($this->user->identity))
|
||||||
|
$this->flashFail("err", "Error", "Can't decline this post.", NULL, true);
|
||||||
|
|
||||||
|
$post->setSuggested(2);
|
||||||
|
$post->setDeleted(1);
|
||||||
|
$post->save();
|
||||||
|
|
||||||
|
$this->returnJson([
|
||||||
|
"success" => true,
|
||||||
|
"new_count" => (new Posts)->getSuggestedPostsCount($post->getWallOwner()->getId())
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,12 @@
|
||||||
|
|
||||||
<script src="/language/{php echo getLanguage()}.js" crossorigin="anonymous"></script>
|
<script src="/language/{php echo getLanguage()}.js" crossorigin="anonymous"></script>
|
||||||
{script "js/node_modules/jquery/dist/jquery.min.js"}
|
{script "js/node_modules/jquery/dist/jquery.min.js"}
|
||||||
|
{script "js/node_modules/jquery-ui/dist/jquery-ui.min.js"}
|
||||||
{script "js/node_modules/umbrellajs/umbrella.min.js"}
|
{script "js/node_modules/umbrellajs/umbrella.min.js"}
|
||||||
{script "js/l10n.js"}
|
{script "js/l10n.js"}
|
||||||
{script "js/openvk.cls.js"}
|
{script "js/openvk.cls.js"}
|
||||||
|
{script "js/node_modules/dashjs/dist/dash.all.min.js"}
|
||||||
|
{script "js/al_music.js"}
|
||||||
|
|
||||||
{css "js/node_modules/tippy.js/dist/backdrop.css"}
|
{css "js/node_modules/tippy.js/dist/backdrop.css"}
|
||||||
{css "js/node_modules/tippy.js/dist/border.css"}
|
{css "js/node_modules/tippy.js/dist/border.css"}
|
||||||
|
@ -122,6 +125,7 @@
|
||||||
<option value="comments">{_s_by_comments}</option>
|
<option value="comments">{_s_by_comments}</option>
|
||||||
<option value="videos">{_s_by_videos}</option>
|
<option value="videos">{_s_by_videos}</option>
|
||||||
<option value="apps">{_s_by_apps}</option>
|
<option value="apps">{_s_by_apps}</option>
|
||||||
|
<option value="audios">{_s_by_audios}</option>
|
||||||
</select>
|
</select>
|
||||||
</form>
|
</form>
|
||||||
<div class="searchTips" id="srcht" hidden>
|
<div class="searchTips" id="srcht" hidden>
|
||||||
|
@ -140,13 +144,13 @@
|
||||||
<option value="comments" {if str_contains($_SERVER['REQUEST_URI'], "type=comments")}selected{/if}>{_s_by_comments}</option>
|
<option value="comments" {if str_contains($_SERVER['REQUEST_URI'], "type=comments")}selected{/if}>{_s_by_comments}</option>
|
||||||
<option value="videos" {if str_contains($_SERVER['REQUEST_URI'], "type=videos")}selected{/if}>{_s_by_videos}</option>
|
<option value="videos" {if str_contains($_SERVER['REQUEST_URI'], "type=videos")}selected{/if}>{_s_by_videos}</option>
|
||||||
<option value="apps" {if str_contains($_SERVER['REQUEST_URI'], "type=apps")}selected{/if}>{_s_by_apps}</option>
|
<option value="apps" {if str_contains($_SERVER['REQUEST_URI'], "type=apps")}selected{/if}>{_s_by_apps}</option>
|
||||||
|
<option value="audios" {if str_contains($_SERVER['REQUEST_URI'], "type=audios")}selected{/if}>{_s_by_audios}</option>
|
||||||
</select>
|
</select>
|
||||||
<button class="searchBtn"><span style="color:white;font-weight: 600;font-size:12px;">{_header_search}</span></button>
|
<button class="searchBtn"><span style="color:white;font-weight: 600;font-size:12px;">{_header_search}</span></button>
|
||||||
</form>
|
</form>
|
||||||
<script>
|
<script>
|
||||||
let els = document.querySelectorAll("div.dec")
|
let els = document.querySelectorAll("div.dec")
|
||||||
for(const element of els)
|
for(const element of els) {
|
||||||
{
|
|
||||||
element.style.display = "none"
|
element.style.display = "none"
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -182,6 +186,7 @@
|
||||||
</a>
|
</a>
|
||||||
<a n:if="$thisUser->getLeftMenuItemStatus('photos')" href="/albums{$thisUser->getId()}" class="link">{_my_photos}</a>
|
<a n:if="$thisUser->getLeftMenuItemStatus('photos')" href="/albums{$thisUser->getId()}" class="link">{_my_photos}</a>
|
||||||
<a n:if="$thisUser->getLeftMenuItemStatus('videos')" href="/videos{$thisUser->getId()}" class="link">{_my_videos}</a>
|
<a n:if="$thisUser->getLeftMenuItemStatus('videos')" href="/videos{$thisUser->getId()}" class="link">{_my_videos}</a>
|
||||||
|
<a n:if="$thisUser->getLeftMenuItemStatus('audios')" href="/audios{$thisUser->getId()}" class="link">{_my_audios}</a>
|
||||||
<a n:if="$thisUser->getLeftMenuItemStatus('messages')" href="/im" class="link">{_my_messages}
|
<a n:if="$thisUser->getLeftMenuItemStatus('messages')" href="/im" class="link">{_my_messages}
|
||||||
<object type="internal/link" n:if="$thisUser->getUnreadMessagesCount() > 0">
|
<object type="internal/link" n:if="$thisUser->getUnreadMessagesCount() > 0">
|
||||||
(<b>{$thisUser->getUnreadMessagesCount()}</b>)
|
(<b>{$thisUser->getUnreadMessagesCount()}</b>)
|
||||||
|
@ -228,7 +233,16 @@
|
||||||
|
|
||||||
<div id="_groupListPinnedGroups">
|
<div id="_groupListPinnedGroups">
|
||||||
<div n:if="$thisUser->getPinnedClubCount() > 0" class="menu_divider"></div>
|
<div n:if="$thisUser->getPinnedClubCount() > 0" class="menu_divider"></div>
|
||||||
<a n:foreach="$thisUser->getPinnedClubs() as $club" href="{$club->getURL()}" class="link group_link">{$club->getName()}</a>
|
|
||||||
|
<a n:foreach="$thisUser->getPinnedClubs() as $club" href="{$club->getURL()}" class="link group_link">
|
||||||
|
{ovk_proc_strtr($club->getName(), 14)}
|
||||||
|
|
||||||
|
<object type="internal/link" style="white-space: normal;" id="sug{$club->getId()}" n:if="$club->getSuggestedPostsCount($thisUser) > 0 && $club->getWallType() == 2">
|
||||||
|
<a href="/club{$club->getId()}/suggested" class="linkunderline">
|
||||||
|
(<b>{$club->getSuggestedPostsCount($thisUser)}</b>)
|
||||||
|
</a>
|
||||||
|
</object>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && $thisUser->getCoins() != 0" id="votesBalance">
|
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && $thisUser->getCoins() != 0" id="votesBalance">
|
||||||
|
@ -366,6 +380,10 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="ajloader" class="loader">
|
||||||
|
<img src="/assets/packages/static/openvk/img/loading_mini.gif" style="width: 40px;">
|
||||||
|
</div>
|
||||||
|
|
||||||
{include "components/cookies.xml"}
|
{include "components/cookies.xml"}
|
||||||
|
|
||||||
{script "js/node_modules/msgpack-lite/dist/msgpack.min.js"}
|
{script "js/node_modules/msgpack-lite/dist/msgpack.min.js"}
|
||||||
|
@ -379,6 +397,7 @@
|
||||||
{script "js/al_api.js"}
|
{script "js/al_api.js"}
|
||||||
{script "js/al_mentions.js"}
|
{script "js/al_mentions.js"}
|
||||||
{script "js/al_polls.js"}
|
{script "js/al_polls.js"}
|
||||||
|
{script "js/al_suggestions.js"}
|
||||||
|
|
||||||
{ifset $thisUser}
|
{ifset $thisUser}
|
||||||
{script "js/al_notifs.js"}
|
{script "js/al_notifs.js"}
|
||||||
|
@ -427,6 +446,12 @@
|
||||||
//]]>
|
//]]>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.openvk = {
|
||||||
|
"audio_genres": {\openvk\Web\Models\Entities\Audio::genres}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
{ifset bodyScripts}
|
{ifset bodyScripts}
|
||||||
{include bodyScripts}
|
{include bodyScripts}
|
||||||
{/ifset}
|
{/ifset}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
.navigation-lang .link_new {
|
.navigation-lang .link_new {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 25px 25px 20px 25px;
|
padding: 20px 10px 5px 10px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border-top: 1px solid #fff;
|
border-top: 1px solid #fff;
|
||||||
color: #000;
|
color: #000;
|
||||||
|
|
|
@ -178,11 +178,29 @@
|
||||||
<h2>{_tour_section_6_title_1|noescape}</h2>
|
<h2>{_tour_section_6_title_1|noescape}</h2>
|
||||||
|
|
||||||
<ul class="listing">
|
<ul class="listing">
|
||||||
<li><span>{_tour_section_6_text_1|noescape}</span></li>
|
<li><span>{_tour_section_6_text_1|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_6_text_2|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_6_text_3|noescape}</span></li>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/audios.png" width="440">
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_6_text_4|noescape}</span></li>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/audios_search.png" width="440">
|
||||||
|
<li><span>{_tour_section_6_text_5|noescape}</span></li>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/audios_upload.png" width="440">
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="big">{_tour_section_6_bottom_text_1|noescape}</p>
|
||||||
|
|
||||||
|
<h2>{_tour_section_6_title_2|noescape}</h2>
|
||||||
|
|
||||||
|
<ul class="listing">
|
||||||
|
<li><span>{_tour_section_6_text_6|noescape}</span></li>
|
||||||
|
<li><span>{_tour_section_6_text_7|noescape}</span></li>
|
||||||
|
<img src="assets/packages/static/openvk/img/tour/audios_playlists.png" width="440">
|
||||||
|
</ul>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,9 @@
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/bannedLinks">{_admin_banned_links}</a>
|
<a href="/admin/bannedLinks">{_admin_banned_links}</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/admin/music">{_admin_music}</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="aui-nav-heading">
|
<div class="aui-nav-heading">
|
||||||
<strong>Chandler</strong>
|
<strong>Chandler</strong>
|
||||||
|
|
81
Web/Presenters/templates/Admin/EditMusic.xml
Normal file
81
Web/Presenters/templates/Admin/EditMusic.xml
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_edit} {$audio->getName()}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block heading}
|
||||||
|
{$audio->getName()}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<div class="aui-tabs horizontal-tabs">
|
||||||
|
<form class="aui" method="POST">
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="id">ID</label>
|
||||||
|
<input class="text medium-field" type="number" id="id" disabled value="{$audio->getId()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label>{_created}</label>
|
||||||
|
{$audio->getPublicationTime()}
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label>{_edited}</label>
|
||||||
|
{$audio->getEditTime() ?? "never"}
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="name">{_name}</label>
|
||||||
|
<input class="text medium-field" type="text" id="name" name="name" value="{$audio->getTitle()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="performer">{_performer}</label>
|
||||||
|
<input class="text medium-field" type="text" id="performer" name="performer" value="{$audio->getPerformer()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="ext">{_lyrics}</label>
|
||||||
|
<textarea class="text medium-field" type="text" id="text" name="text" style="resize: vertical;">{$audio->getLyrics()}</textarea>
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label>{_admin_audio_length}</label>
|
||||||
|
{$audio->getFormattedLength()}
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="ext">{_genre}</label>
|
||||||
|
<select class="select medium-field" name="genre">
|
||||||
|
<option n:foreach='\openvk\Web\Models\Entities\Audio::genres as $genre'
|
||||||
|
n:attr="selected: $genre == $audio->getGenre()" value="{$genre}">
|
||||||
|
{$genre}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label>{_admin_original_file}</label>
|
||||||
|
<audio controls src="{$audio->getOriginalURL(true)}">
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="owner">{_owner}</label>
|
||||||
|
<input class="text medium-field" type="number" id="owner_id" name="owner" value="{$owner}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="explicit">Explicit</label>
|
||||||
|
<input class="toggle-large" type="checkbox" id="explicit" name="explicit" value="1" {if $audio->isExplicit()} checked {/if} />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="deleted">{_deleted}</label>
|
||||||
|
<input class="toggle-large" type="checkbox" id="deleted" name="deleted" value="1" {if $audio->isDeleted()} checked {/if} />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="withdrawn">{_withdrawn}</label>
|
||||||
|
<input class="toggle-large" type="checkbox" id="withdrawn" name="withdrawn" value="1" {if $audio->isWithdrawn()} checked {/if} />
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="buttons-container">
|
||||||
|
<div class="buttons">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input class="button submit" type="submit" value="{_save}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{/block}
|
54
Web/Presenters/templates/Admin/EditPlaylist.xml
Normal file
54
Web/Presenters/templates/Admin/EditPlaylist.xml
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_edit} {$playlist->getName()}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block heading}
|
||||||
|
{$playlist->getName()}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<div class="aui-tabs horizontal-tabs">
|
||||||
|
<form class="aui" method="POST">
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="id">ID</label>
|
||||||
|
<input class="text medium-field" type="number" id="id" disabled value="{$playlist->getId()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="name">{_name}</label>
|
||||||
|
<input class="text medium-field" type="text" id="name" name="name" value="{$playlist->getName()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="ext">{_description}</label>
|
||||||
|
<textarea class="text medium-field" type="text" id="description" name="description" style="resize: vertical;">{$playlist->getDescription()}</textarea>
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="ext">{_admin_cover_id}</label>
|
||||||
|
<span id="avatar" class="aui-avatar aui-avatar-project aui-avatar-xlarge">
|
||||||
|
<span class="aui-avatar-inner">
|
||||||
|
<img src="{$playlist->getCoverUrl()}" style="object-fit: cover;"></img>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<br />
|
||||||
|
<input class="text medium-field" type="number" id="photo" name="photo" value="{$playlist->getCoverPhotoId()}" />
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="owner">{_owner}</label>
|
||||||
|
<input class="text medium-field" type="number" id="owner_id" name="owner" value="{$playlist->getOwner()->getId()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="deleted">{_deleted}</label>
|
||||||
|
<input class="toggle-large" type="checkbox" id="deleted" name="deleted" value="1" {if $playlist->isDeleted()} checked {/if} />
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="buttons-container">
|
||||||
|
<div class="buttons">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input class="button submit" type="submit" value="{_save}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{/block}
|
135
Web/Presenters/templates/Admin/Music.xml
Normal file
135
Web/Presenters/templates/Admin/Music.xml
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
{extends "@layout.xml"}
|
||||||
|
{var $search = $mode === "audios"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_audios}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block heading}
|
||||||
|
{_audios}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block searchTitle}
|
||||||
|
{include title}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<nav class="aui-navgroup aui-navgroup-horizontal">
|
||||||
|
<div class="aui-navgroup-inner">
|
||||||
|
<div class="aui-navgroup-primary">
|
||||||
|
<ul class="aui-nav" resolved="">
|
||||||
|
<li n:attr="class => $mode === 'audios' ? 'aui-nav-selected' : ''">
|
||||||
|
<a href="?act=audios">{_audios}</a>
|
||||||
|
</li>
|
||||||
|
<li n:attr="class => $mode === 'playlists' ? 'aui-nav-selected' : ''">
|
||||||
|
<a href="?act=playlists">{_playlists}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<table class="aui aui-table-list">
|
||||||
|
{if $mode === "audios"}
|
||||||
|
{var $audios = iterator_to_array($audios)}
|
||||||
|
{var $amount = sizeof($audios)}
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>{_admin_author}</th>
|
||||||
|
<th>{_peformer}</th>
|
||||||
|
<th>{_admin_title}</th>
|
||||||
|
<th>{_genre}</th>
|
||||||
|
<th>Explicit</th>
|
||||||
|
<th>{_withdrawn}</th>
|
||||||
|
<th>{_deleted}</th>
|
||||||
|
<th>{_created}</th>
|
||||||
|
<th>{_actions}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr n:foreach="$audios as $audio">
|
||||||
|
<td>{$audio->getId()}</td>
|
||||||
|
<td>
|
||||||
|
{var $owner = $audio->getOwner()}
|
||||||
|
<span class="aui-avatar aui-avatar-xsmall">
|
||||||
|
<span class="aui-avatar-inner">
|
||||||
|
<img src="{$owner->getAvatarUrl('miniscule')}" alt="{$owner->getCanonicalName()}" style="object-fit: cover;" role="presentation" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a>
|
||||||
|
|
||||||
|
<span n:if="$owner->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">{_admin_banned}</span>
|
||||||
|
</td>
|
||||||
|
<td>{$audio->getPerformer()}</td>
|
||||||
|
<td>{$audio->getTitle()}</td>
|
||||||
|
<td>{$audio->getGenre()}</td>
|
||||||
|
<td>{$audio->isExplicit() ? tr("yes") : tr("no")}</td>
|
||||||
|
<td n:attr="style => $audio->isWithdrawn() ? 'color: red;' : ''">
|
||||||
|
{$audio->isWithdrawn() ? tr("yes") : tr("no")}
|
||||||
|
</td>
|
||||||
|
<td n:attr="style => $audio->isDeleted() ? 'color: red;' : ''">
|
||||||
|
{$audio->isDeleted() ? tr("yes") : tr("no")}
|
||||||
|
</td>
|
||||||
|
<td>{$audio->getPublicationTime()}</td>
|
||||||
|
<td>
|
||||||
|
<a class="aui-button aui-button-primary" href="/admin/music/{$audio->getId()}/edit">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">Редактировать</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
{else}
|
||||||
|
{var $playlists = iterator_to_array($playlists)}
|
||||||
|
{var $amount = sizeof($playlists)}
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>{_admin_author}</th>
|
||||||
|
<th>{_name}</th>
|
||||||
|
<th>{_created_playlist}</th>
|
||||||
|
<th>{_actions}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr n:foreach="$playlists as $playlist">
|
||||||
|
<td>{$playlist->getId()}</td>
|
||||||
|
<td>
|
||||||
|
{var $owner = $playlist->getOwner()}
|
||||||
|
<span class="aui-avatar aui-avatar-xsmall">
|
||||||
|
<span class="aui-avatar-inner">
|
||||||
|
<img src="{$owner->getAvatarUrl('miniscule')}" alt="{$owner->getCanonicalName()}" style="object-fit: cover;" role="presentation" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a>
|
||||||
|
|
||||||
|
<span n:if="$owner->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">{_admin_banned}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="aui-avatar aui-avatar-xsmall">
|
||||||
|
<span class="aui-avatar-inner">
|
||||||
|
<img src="{$playlist->getCoverURL()}" alt="{$owner->getCanonicalName()}" style="object-fit: cover;" role="presentation" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
{ovk_proc_strtr($playlist->getName(), 30)}
|
||||||
|
</td>
|
||||||
|
<td>{$playlist->getCreationTime()}</td>
|
||||||
|
<td>
|
||||||
|
<a class="aui-button aui-button-primary" href="/admin/playlist/{$playlist->getId()}/edit">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
{/if}
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
<div align="right">
|
||||||
|
{var $isLast = ((10 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
||||||
|
|
||||||
|
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="/admin/music?act={($_GET['act'] ?? 'audios')}&p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||||
|
<a n:if="$isLast" class="aui-button" href="/admin/music?act={($_GET['act'] ?? 'audios')}&p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||||
|
</div>
|
||||||
|
{/block}
|
7
Web/Presenters/templates/Audio/ApiGetContext.xml
Normal file
7
Web/Presenters/templates/Audio/ApiGetContext.xml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<input type="hidden" name="count" value="{$count}">
|
||||||
|
<input type="hidden" name="pagesCount" value="{$pagesCount}">
|
||||||
|
<input type="hidden" name="page" value="{$page}">
|
||||||
|
|
||||||
|
{foreach $audios as $audio}
|
||||||
|
{include "player.xml", audio => $audio, hideButtons => true}
|
||||||
|
{/foreach}
|
95
Web/Presenters/templates/Audio/EditPlaylist.xml
Normal file
95
Web/Presenters/templates/Audio/EditPlaylist.xml
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block title}{_edit_playlist}{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
<a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a>
|
||||||
|
»
|
||||||
|
<a href="/audios{$ownerId}">{_audios}</a>
|
||||||
|
»
|
||||||
|
<a href="/playlist{$playlist->getPrettyId()}">{_playlist}</a>
|
||||||
|
»
|
||||||
|
{_edit_playlist}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<div class="playlistBlock" style="display: flex;margin-top: 0px;">
|
||||||
|
<div class="playlistCover">
|
||||||
|
<a>
|
||||||
|
<img src="{$playlist->getCoverURL('normal')}" alt="{_playlist_cover}">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="profile_links" style="width: 139px;">
|
||||||
|
<a class="profile_link" style="width: 98%;" id="_deletePlaylist" data-id="{$playlist->getId()}">{_delete_playlist}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="padding-left: 13px;width:75%">
|
||||||
|
<div class="playlistInfo">
|
||||||
|
<input value="{$playlist->getName()}" type="text" name="title" maxlength="125">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="moreInfo">
|
||||||
|
<textarea name="description" maxlength="2045" style="margin-top: 11px;">{$playlist->getDescription()}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top: 19px;">
|
||||||
|
<input id="playlist_query" type="text" style="height: 26px;" placeholder="{_header_search}">
|
||||||
|
<div class="playlistAudiosContainer editContainer">
|
||||||
|
<div id="newPlaylistAudios" n:foreach="$audios as $audio">
|
||||||
|
<div class="playerContainer">
|
||||||
|
{include "player.xml", audio => $audio, hideButtons => true}
|
||||||
|
</div>
|
||||||
|
<div class="attachAudio addToPlaylist" data-id="{$audio->getId()}">
|
||||||
|
<span>{_remove_from_playlist}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="showMoreAudiosPlaylist" data-page="2" data-playlist="{$playlist->getId()}" n:if="$pagesCount > 1">
|
||||||
|
{_show_more_audios}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="post" id="editPlaylistForm" data-id="{$playlist->getId()}" enctype="multipart/form-data">
|
||||||
|
<input type="hidden" name="title" maxlength="128" />
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<textarea style="display:none;" name="description" maxlength="2048" />
|
||||||
|
<input type="hidden" name="audios">
|
||||||
|
<input type="file" style="display:none;" name="new_cover" accept=".jpg,.png">
|
||||||
|
|
||||||
|
<div style="float:right;margin-top: 8px;">
|
||||||
|
<button class="button" type="submit">{_save}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.querySelector("input[name='audios']").value = {$audiosIds}
|
||||||
|
|
||||||
|
u("#editPlaylistForm").on("submit", (e) => {
|
||||||
|
document.querySelector("#editPlaylistForm input[name='title']").value = document.querySelector(".playlistInfo input[name='title']").value
|
||||||
|
document.querySelector("#editPlaylistForm textarea[name='description']").value = document.querySelector(".playlistBlock textarea[name='description']").value
|
||||||
|
})
|
||||||
|
|
||||||
|
u("#editPlaylistForm input[name='new_cover']").on("change", (e) => {
|
||||||
|
if(!e.currentTarget.files[0].type.startsWith("image/")) {
|
||||||
|
fastError(tr("not_a_photo"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let image = URL.createObjectURL(e.currentTarget.files[0])
|
||||||
|
|
||||||
|
document.querySelector(".playlistCover img").src = image
|
||||||
|
})
|
||||||
|
|
||||||
|
u(".playlistCover img").on("click", (e) => {
|
||||||
|
document.querySelector("input[name='new_cover']").click()
|
||||||
|
})
|
||||||
|
|
||||||
|
document.querySelector("#editPlaylistForm input[name='new_cover']").value = ""
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{script "js/al_playlists.js"}
|
||||||
|
{/block}
|
20
Web/Presenters/templates/Audio/Embed.xml
Normal file
20
Web/Presenters/templates/Audio/Embed.xml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
|
||||||
|
<link rel="icon">
|
||||||
|
<title>{$audio->getName()}</title>
|
||||||
|
|
||||||
|
{css "css/main.css"}
|
||||||
|
{css "css/audios.css"}
|
||||||
|
{script "js/node_modules/dashjs/dist/dash.all.min.js"}
|
||||||
|
{script "js/node_modules/jquery/dist/jquery.min.js"}
|
||||||
|
{script "js/node_modules/umbrellajs/umbrella.min.js"}
|
||||||
|
</head>
|
||||||
|
<body id="audioEmbed">
|
||||||
|
{include "player.xml", audio => $audio}
|
||||||
|
|
||||||
|
{script "js/al_music.js"}
|
||||||
|
</body>
|
||||||
|
</html>
|
126
Web/Presenters/templates/Audio/List.xml
Normal file
126
Web/Presenters/templates/Audio/List.xml
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{if $mode == 'list'}
|
||||||
|
{if $ownerId > 0}
|
||||||
|
{_audios} {$owner->getMorphedName("genitive", false)}
|
||||||
|
{else}
|
||||||
|
{_audios_group}
|
||||||
|
{/if}
|
||||||
|
{elseif $mode == 'new'}
|
||||||
|
{_audio_new}
|
||||||
|
{elseif $mode == 'popular'}
|
||||||
|
{_audio_popular}
|
||||||
|
{else}
|
||||||
|
{if $ownerId > 0}
|
||||||
|
{_playlists} {$owner->getMorphedName("genitive", false)}
|
||||||
|
{else}
|
||||||
|
{_playlists_group}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
<div n:if="$mode == 'list'">
|
||||||
|
<div n:if="$isMy">{_my_audios_small}</div>
|
||||||
|
<div n:if="!$isMy">
|
||||||
|
<a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a>
|
||||||
|
»
|
||||||
|
{_audios}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div n:if="$mode == 'new'">
|
||||||
|
{_audios}
|
||||||
|
»
|
||||||
|
{_audio_new}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div n:if="$mode == 'popular'">
|
||||||
|
{_audios}
|
||||||
|
»
|
||||||
|
{_audio_popular}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div n:if="$mode == 'playlists'">
|
||||||
|
{_audios}
|
||||||
|
»
|
||||||
|
{if $isMy}{_my_playlists}{else}{_playlists}{/if}
|
||||||
|
</div>
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
{* ref: https://archive.li/P32em *}
|
||||||
|
|
||||||
|
{include "bigplayer.xml"}
|
||||||
|
|
||||||
|
<input n:if="$mode == 'list'" type="hidden" name="bigplayer_context" data-type="entity_audios" data-entity="{$ownerId}" data-page="{$page}">
|
||||||
|
<input n:if="$mode == 'new'" type="hidden" name="bigplayer_context" data-type="new_audios" data-entity="0" data-page="1">
|
||||||
|
<input n:if="$mode == 'popular'" type="hidden" name="bigplayer_context" data-type="popular_audios" data-entity="0" data-page="1">
|
||||||
|
<div class="bigPlayerDetector"></div>
|
||||||
|
|
||||||
|
<div style="width: 100%;display: flex;margin-bottom: -10px;" class="audiosDiv">
|
||||||
|
<div style="width: 74%;" class="audiosContainer" n:if="$mode != 'playlists'">
|
||||||
|
<div style="padding: 8px;">
|
||||||
|
<div n:if="$audiosCount <= 0">
|
||||||
|
{include "../components/error.xml", description => $ownerId > 0 ? ($ownerId == $thisUser->getId() ? tr("no_audios_thisuser") : tr("no_audios_user")) : tr("no_audios_club")}
|
||||||
|
</div>
|
||||||
|
<div n:if="$audiosCount > 0" class="infContainer">
|
||||||
|
<div class="infObj" n:foreach="$audios as $audio">
|
||||||
|
{include "player.xml", audio => $audio, club => $club}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div n:if="$mode != 'new' && $mode != 'popular'">
|
||||||
|
{include "../components/paginator.xml", conf => (object) [
|
||||||
|
"page" => $page,
|
||||||
|
"count" => $audiosCount,
|
||||||
|
"amount" => sizeof($audios),
|
||||||
|
"perPage" => $perPage ?? OPENVK_DEFAULT_PER_PAGE,
|
||||||
|
"atBottom" => true,
|
||||||
|
]}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="width: 74%;" n:if="$mode == 'playlists'">
|
||||||
|
<div style="padding: 8px;">
|
||||||
|
<div n:if="$playlistsCount <= 0">
|
||||||
|
{include "../components/error.xml", description => $ownerId > 0 ? ($ownerId == $thisUser->getId() ? tr("no_playlists_thisuser") : tr("no_playlists_user")) : tr("no_playlists_club")}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="infContainer playlistContainer" n:if="$playlistsCount > 0">
|
||||||
|
<div class="infObj" n:foreach="$playlists as $playlist">
|
||||||
|
<a href="/playlist{$playlist->getPrettyId()}">
|
||||||
|
<div class="playlistCover">
|
||||||
|
<img src="{$playlist->getCoverURL()}" alt="{_playlist_cover}">
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="playlistInfo">
|
||||||
|
<a href="/playlist{$playlist->getPrettyId()}">
|
||||||
|
<span style="font-size: 12px" class="playlistName">
|
||||||
|
{ovk_proc_strtr($playlist->getName(), 15)}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="{$playlist->getOwner()->getURL()}">{ovk_proc_strtr($playlist->getOwner()->getCanonicalName(), 20)}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{include "../components/paginator.xml", conf => (object) [
|
||||||
|
"page" => $page,
|
||||||
|
"count" => $playlistsCount,
|
||||||
|
"amount" => sizeof($playlists),
|
||||||
|
"perPage" => $perPage ?? OPENVK_DEFAULT_PER_PAGE,
|
||||||
|
"atBottom" => true,
|
||||||
|
]}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{include "tabs.xml"}
|
||||||
|
</div>
|
||||||
|
{/block}
|
109
Web/Presenters/templates/Audio/NewPlaylist.xml
Normal file
109
Web/Presenters/templates/Audio/NewPlaylist.xml
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_new_playlist}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
{if !$_GET["gid"]}
|
||||||
|
<a href="{$thisUser->getURL()}">{$thisUser->getCanonicalName()}</a>
|
||||||
|
»
|
||||||
|
<a href="/audios{$thisUser->getId()}">{_audios}</a>
|
||||||
|
{else}
|
||||||
|
<a href="{$club->getURL()}">{$club->getCanonicalName()}</a>
|
||||||
|
»
|
||||||
|
<a href="/audios-{$club->getId()}">{_audios}</a>
|
||||||
|
{/if}
|
||||||
|
»
|
||||||
|
{_new_playlist}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<style>
|
||||||
|
textarea[name='description'] {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlistInfo {
|
||||||
|
width: 76%;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div style="display:flex;">
|
||||||
|
<div class="playlistCover" onclick="document.querySelector(`#newPlaylistForm input[name='cover']`).click()">
|
||||||
|
<a>
|
||||||
|
<img src="/assets/packages/static/openvk/img/song.jpg" alt="{_playlist_cover}">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="padding-left: 17px;width: 75%;" class="plinfo">
|
||||||
|
<div>
|
||||||
|
<input type="text" name="title" placeholder="{_title}" maxlength="125" />
|
||||||
|
</div>
|
||||||
|
<div class="moreInfo" style="margin-top: 11px;">
|
||||||
|
<textarea placeholder="{_description}" name="description" maxlength="2045" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top: 19px;">
|
||||||
|
<input id="playlist_query" type="text" style="height: 26px;" placeholder="{_header_search}">
|
||||||
|
<div class="playlistAudiosContainer editContainer">
|
||||||
|
<div id="newPlaylistAudios" n:foreach="$audios as $audio">
|
||||||
|
<div style="width: 78%;float: left;">
|
||||||
|
{include "player.xml", audio => $audio, hideButtons => true}
|
||||||
|
</div>
|
||||||
|
<div class="attachAudio addToPlaylist" data-id="{$audio->getId()}">
|
||||||
|
<span>{_add_to_playlist}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="showMoreAudiosPlaylist" data-page="2" {if !is_null($_GET["gid"])}data-club="{abs($_GET['gid'])}"{/if} n:if="$pagesCount > 1">
|
||||||
|
{_show_more_audios}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="post" id="newPlaylistForm" enctype="multipart/form-data">
|
||||||
|
<input type="hidden" name="title" maxlength="125" />
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<textarea style="display:none;" name="description" maxlength="2045" />
|
||||||
|
<input type="hidden" name="audios">
|
||||||
|
<input type="file" style="display:none;" name="cover" accept=".jpg,.png">
|
||||||
|
|
||||||
|
<div style="float: right;margin-top: 9px;">
|
||||||
|
<button class="button" type="submit">{_create}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.querySelector("input[name='audios']").value = ""
|
||||||
|
|
||||||
|
u("#newPlaylistForm").on("submit", (e) => {
|
||||||
|
document.querySelector("#newPlaylistForm input[name='title']").value = document.querySelector(".plinfo input[name='title']").value
|
||||||
|
document.querySelector("#newPlaylistForm textarea[name='description']").value = document.querySelector(".plinfo textarea[name='description']").value
|
||||||
|
})
|
||||||
|
|
||||||
|
u("#newPlaylistForm input[name='cover']").on("change", (e) => {
|
||||||
|
if(!e.currentTarget.files[0].type.startsWith("image/")) {
|
||||||
|
fastError(tr("not_a_photo"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let image = URL.createObjectURL(e.currentTarget.files[0])
|
||||||
|
|
||||||
|
document.querySelector(".playlistCover img").src = image
|
||||||
|
document.querySelector(".playlistCover img").style.display = "block"
|
||||||
|
})
|
||||||
|
|
||||||
|
u(".playlistCover img").on("click", (e) => {
|
||||||
|
document.querySelector("#newPlaylistForm input[name='cover']").value = ""
|
||||||
|
e.currentTarget.href = ""
|
||||||
|
})
|
||||||
|
|
||||||
|
document.querySelector("#newPlaylistForm input[name='cover']").value = ""
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{script "js/al_playlists.js"}
|
||||||
|
{/block}
|
81
Web/Presenters/templates/Audio/Playlist.xml
Normal file
81
Web/Presenters/templates/Audio/Playlist.xml
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block title}{_playlist}{/block}
|
||||||
|
|
||||||
|
{block headIncludes}
|
||||||
|
<meta property="og:type" content="music.album">
|
||||||
|
<meta property="og:title" content="{$playlist->getName()}">
|
||||||
|
<meta property="og:url" content="{$playlist->getURL()}">
|
||||||
|
<meta property="og:description" content="{$playlist->getDescription()}">
|
||||||
|
<meta property="og:image" content="{$playlist->getCoverURL()}">
|
||||||
|
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "http://schema.org/",
|
||||||
|
"type": "MusicAlbum",
|
||||||
|
"name": {$playlist->getName()},
|
||||||
|
"url": {$playlist->getURL()}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
<a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a>
|
||||||
|
»
|
||||||
|
<a href="/audios{$ownerId}">{_audios}</a>
|
||||||
|
»
|
||||||
|
{_playlist}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
{include "bigplayer.xml"}
|
||||||
|
|
||||||
|
{php $count = $playlist->size()}
|
||||||
|
|
||||||
|
<input type="hidden" name="bigplayer_context" data-type="playlist_context" data-entity="{$playlist->getId()}" data-page="{$page}">
|
||||||
|
<div class="playlistBlock">
|
||||||
|
<div class="playlistCover" style="float: left;">
|
||||||
|
<a href="{$playlist->getCoverURL()}" target="_blank">
|
||||||
|
<img src="{$playlist->getCoverURL('normal')}" alt="{_playlist_cover}">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="profile_links" style="width: 139px;" n:if="isset($thisUser)">
|
||||||
|
<a class="profile_link" style="width: 98%;" href="/playlist{$playlist->getPrettyId()}/edit" n:if="$playlist->canBeModifiedBy($thisUser)">{_edit_playlist}</a>
|
||||||
|
<a class="profile_link" style="width: 98%;" id="bookmarkPlaylist" data-id="{$playlist->getId()}" n:if="!$isBookmarked">{_bookmark}</a>
|
||||||
|
<a class="profile_link" style="width: 98%;" id="unbookmarkPlaylist" data-id="{$playlist->getId()}" n:if="$isBookmarked">{_unbookmark}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="float: left;padding-left: 13px;width:75%">
|
||||||
|
<div class="playlistInfo">
|
||||||
|
<h4 style="border-bottom:unset;">{$playlist->getName()}</h4>
|
||||||
|
|
||||||
|
<div class="moreInfo">
|
||||||
|
{$playlist->getMetaDescription()|noescape}
|
||||||
|
|
||||||
|
<div style="margin-top: 11px;">
|
||||||
|
<span>{nl2br($playlist->getDescriptionHTML())|noescape}</span>
|
||||||
|
</div>
|
||||||
|
<hr style="color: #f7f7f7;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="audiosContainer infContainer" style="margin-top: 14px;">
|
||||||
|
{if $count < 1}
|
||||||
|
{_empty_playlist}
|
||||||
|
{else}
|
||||||
|
<div class="infObj" n:foreach="$audios as $audio">
|
||||||
|
{include "player.xml", audio => $audio}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{include "../components/paginator.xml", conf => (object) [
|
||||||
|
"page" => $page,
|
||||||
|
"count" => $count,
|
||||||
|
"amount" => sizeof($audios),
|
||||||
|
"perPage" => $perPage ?? OPENVK_DEFAULT_PER_PAGE,
|
||||||
|
"atBottom" => true,
|
||||||
|
]}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/block}
|
212
Web/Presenters/templates/Audio/Upload.xml
Normal file
212
Web/Presenters/templates/Audio/Upload.xml
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_upload_audio}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
{if !is_null($group)}
|
||||||
|
<a href="{$group->getURL()}">{$group->getCanonicalName()}</a>
|
||||||
|
»
|
||||||
|
<a href="/audios-{$group->getId()}">{_audios}</a>
|
||||||
|
{else}
|
||||||
|
<a href="{$thisUser->getURL()}">{$thisUser->getCanonicalName()}</a>
|
||||||
|
»
|
||||||
|
<a href="/audios{$thisUser->getId()}">{_audios}</a>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
»
|
||||||
|
{_upload_audio}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<div class="container_gray" style="border: 0;margin-top: -10px;">
|
||||||
|
<div id="upload_container">
|
||||||
|
<div id="firstStep">
|
||||||
|
<h4>{_select_audio}</h4><br/>
|
||||||
|
<b><a href="javascript:void(0)">{_limits}</a></b>
|
||||||
|
<ul>
|
||||||
|
<li>{tr("audio_requirements", 1, 30, 25)}</li>
|
||||||
|
<li>{tr("audio_requirements_2")}</li>
|
||||||
|
</ul>
|
||||||
|
<div id="audio_upload">
|
||||||
|
<form enctype="multipart/form-data" method="POST">
|
||||||
|
<input type="hidden" name="name" />
|
||||||
|
<input type="hidden" name="performer" />
|
||||||
|
<input type="hidden" name="lyrics" />
|
||||||
|
<input type="hidden" name="genre" />
|
||||||
|
<input type="hidden" name="explicit" />
|
||||||
|
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input id="audio_input" type="file" name="blob" accept="audio/*" style="display:none" />
|
||||||
|
<input value="{_upload_button}" class="button" type="button" onclick="document.querySelector('#audio_input').click()">
|
||||||
|
</form>
|
||||||
|
</div><br/>
|
||||||
|
|
||||||
|
<span>{_you_can_also_add_audio_using} <b><a href="/search?type=audios">{_search_audio_inst}</a></b>.<span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="lastStep" style="display:none;">
|
||||||
|
<table cellspacing="7" cellpadding="0" border="0" align="center">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top"><span class="nobold">{_audio_name}:</span></td>
|
||||||
|
<td><input type="text" name="name" autocomplete="off" maxlength="80" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top"><span class="nobold">{_performer}:</span></td>
|
||||||
|
<td><input name="performer" type="text" autocomplete="off" maxlength="80" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top"><span class="nobold">{_genre}:</span></td>
|
||||||
|
<td>
|
||||||
|
<select name="genre">
|
||||||
|
<option n:foreach='\openvk\Web\Models\Entities\Audio::genres as $genre' n:attr="selected: $genre == 'Other'" value="{$genre}">
|
||||||
|
{$genre}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top"><span class="nobold">{_lyrics}:</span></td>
|
||||||
|
<td><textarea name="lyrics" style="resize: vertical;max-height: 300px;"></textarea></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top"></td>
|
||||||
|
<td>
|
||||||
|
<label><input type="checkbox" name="explicit">{_audios_explicit}</label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top"></td>
|
||||||
|
<td>
|
||||||
|
<input class="button" type="button" id="uploadMuziko" value="{_upload_button}">
|
||||||
|
<input class="button" type="button" id="backToUpload" value="{_select_another_file}">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import * as id3 from "/assets/packages/static/openvk/js/node_modules/id3js/lib/id3.js";
|
||||||
|
|
||||||
|
u("#audio_input").on("change", async function(e) {
|
||||||
|
let files = e.currentTarget.files
|
||||||
|
if(files.length <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
document.querySelector("#firstStep").style.display = "none"
|
||||||
|
document.querySelector("#lastStep").style.display = "block"
|
||||||
|
|
||||||
|
let tags = await id3.fromFile(files[0]);
|
||||||
|
if(tags != null) {
|
||||||
|
console.log("ID" + tags.kind + " detected, setting values...");
|
||||||
|
|
||||||
|
if(tags.title != null)
|
||||||
|
document.querySelector("#lastStep input[name=name]").value = tags.title;
|
||||||
|
else
|
||||||
|
document.querySelector("#lastStep input[name=name]").value = files[0].name
|
||||||
|
|
||||||
|
if(tags.artist != null)
|
||||||
|
document.querySelector("#lastStep input[name=performer]").value = tags.artist;
|
||||||
|
else
|
||||||
|
document.querySelector("#lastStep input[name=performer]").value = tr("track_unknown");
|
||||||
|
|
||||||
|
if(tags.genre != null) {
|
||||||
|
if(document.querySelector("#lastStep select[name=genre] > option[value='" + tags.genre + "']") != null) {
|
||||||
|
document.querySelector("#lastStep select[name=genre]").value = tags.genre;
|
||||||
|
} else {
|
||||||
|
console.warn("Unknown genre: " + tags.genre);
|
||||||
|
document.querySelector("#lastStep select[name=genre]").value = "Other"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.querySelector("#lastStep input[name=name]").value = files[0].name
|
||||||
|
document.querySelector("#lastStep select[name=genre]").value = "Other"
|
||||||
|
document.querySelector("#lastStep input[name=performer]").value = tr("track_unknown");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
u("#backToUpload").on("click", (e) => {
|
||||||
|
document.querySelector("#firstStep").style.display = "block"
|
||||||
|
document.querySelector("#lastStep").style.display = "none"
|
||||||
|
|
||||||
|
document.querySelector("#lastStep input[name=name]").value = ""
|
||||||
|
document.querySelector("#lastStep input[name=performer]").value = ""
|
||||||
|
document.querySelector("#lastStep select[name=genre]").value = ""
|
||||||
|
document.querySelector("#lastStep textarea[name=lyrics]").value = ""
|
||||||
|
document.querySelector("#audio_input").value = ""
|
||||||
|
})
|
||||||
|
|
||||||
|
u("#uploadMuziko").on("click", (e) => {
|
||||||
|
var name_ = document.querySelector("#audio_upload input[name=name]");
|
||||||
|
var perf_ = document.querySelector("#audio_upload input[name=performer]");
|
||||||
|
var genre_ = document.querySelector("#audio_upload input[name=genre]");
|
||||||
|
var lyrics_ = document.querySelector("#audio_upload input[name=lyrics]");
|
||||||
|
var explicit_ = document.querySelector("#audio_upload input[name=explicit]");
|
||||||
|
|
||||||
|
name_.value = document.querySelector("#lastStep input[name=name]").value
|
||||||
|
perf_.value = document.querySelector("#lastStep input[name=performer]").value
|
||||||
|
genre_.value = document.querySelector("#lastStep select[name=genre]").value
|
||||||
|
lyrics_.value = document.querySelector("#lastStep textarea[name=lyrics]").value
|
||||||
|
explicit_.value = document.querySelector("#lastStep input[name=explicit]").checked ? "on" : "off"
|
||||||
|
|
||||||
|
$("#audio_upload > form").trigger("submit");
|
||||||
|
})
|
||||||
|
|
||||||
|
$(document).on("dragover drop", (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
|
||||||
|
$(".container_gray").on("drop", (e) => {
|
||||||
|
e.originalEvent.dataTransfer.dropEffect = 'move';
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
let file = e.originalEvent.dataTransfer.files[0]
|
||||||
|
|
||||||
|
if(!file.type.startsWith('audio/')) {
|
||||||
|
MessageBox(tr("error"), tr("only_audios_accepted", escapeHtml(file.name)), [tr("ok")], [() => Function.noop])
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("audio_input").files = e.originalEvent.dataTransfer.files
|
||||||
|
u("#audio_input").trigger("change")
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#audio_upload").on("submit", "form", (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
let fd = new FormData(e.currentTarget)
|
||||||
|
fd.append("ajax", 1)
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: location.href,
|
||||||
|
contentType: false,
|
||||||
|
processData: false,
|
||||||
|
data: fd,
|
||||||
|
beforeSend: function() {
|
||||||
|
document.querySelector("#lastStep").classList.add("lagged")
|
||||||
|
document.querySelector("#upload_container").classList.add("uploading")
|
||||||
|
},
|
||||||
|
success: (response) => {
|
||||||
|
document.querySelector("#lastStep").classList.remove("lagged")
|
||||||
|
document.querySelector("#upload_container").classList.remove("uploading")
|
||||||
|
if(response.success) {
|
||||||
|
u("#backToUpload").trigger("click")
|
||||||
|
NewNotification(tr("success"), tr("audio_successfully_uploaded"), null, () => {
|
||||||
|
window.location.assign(response.redirect_link)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
fastError(response.flash.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{/block}
|
56
Web/Presenters/templates/Audio/bigplayer.xml
Normal file
56
Web/Presenters/templates/Audio/bigplayer.xml
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<div class="bigPlayer">
|
||||||
|
<audio class="audio" />
|
||||||
|
<div class="paddingLayer">
|
||||||
|
<div class="playButtons">
|
||||||
|
<div class="playButton musicIcon" title="{_play_tip} [Space]"></div>
|
||||||
|
|
||||||
|
<div class="arrowsButtons">
|
||||||
|
<div>
|
||||||
|
<div class="nextButton musicIcon"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="backButton musicIcon"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="trackPanel">
|
||||||
|
<div class="trackInfo">
|
||||||
|
<div class="trackName">
|
||||||
|
<b>{_track_unknown}</b> —
|
||||||
|
<span>{_track_noname}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="timer" style="float:right">
|
||||||
|
<span class="time">00:00</span>
|
||||||
|
<span>/</span>
|
||||||
|
<span class="elapsedTime" style="cursor:pointer">-00:00</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="track" style="margin-top: -2px;">
|
||||||
|
<div class="bigPlayerTip">00:00</div>
|
||||||
|
<div class="selectableTrack">
|
||||||
|
<div style="width: 95%;position: relative;">
|
||||||
|
<div class="slider"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="volumePanel">
|
||||||
|
<div class="selectableTrack">
|
||||||
|
<div style="position: relative;width:72%">
|
||||||
|
<div class="slider"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="additionalButtons">
|
||||||
|
<div class="repeatButton musicIcon" title="{_repeat_tip} [R]" ></div>
|
||||||
|
<div class="shuffleButton musicIcon" title="{_shuffle_tip}"></div>
|
||||||
|
<div class="deviceButton musicIcon" title="{_mute_tip} [M]"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
69
Web/Presenters/templates/Audio/player.xml
Normal file
69
Web/Presenters/templates/Audio/player.xml
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
{php $id = $audio->getId() . rand(0, 1000)}
|
||||||
|
{php $isWithdrawn = $audio->isWithdrawn()}
|
||||||
|
{php $editable = isset($thisUser) && $audio->canBeModifiedBy($thisUser)}
|
||||||
|
<div id="audioEmbed-{$id}" data-realid="{$audio->getId()}" {if $hideButtons}data-prettyid="{$audio->getPrettyId()}" data-name="{$audio->getName()}"{/if} data-genre="{$audio->getGenre()}" class="audioEmbed {if !$audio->isAvailable()}processed{/if} {if $isWithdrawn}withdrawn{/if}" data-length="{$audio->getLength()}" data-keys="{json_encode($audio->getKeys())}" data-url="{$audio->getURL()}">
|
||||||
|
<audio class="audio" />
|
||||||
|
|
||||||
|
<div id="miniplayer" class="audioEntry" style="min-height: 39px;">
|
||||||
|
<div style="display: flex;">
|
||||||
|
<div class="playerButton">
|
||||||
|
<div class="playIcon"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="status" style="margin-top: 12px;">
|
||||||
|
<div class="mediaInfo noOverflow" style="margin-bottom: -8px; cursor: pointer;display:flex;width: 85%;">
|
||||||
|
<div class="info">
|
||||||
|
<strong class="performer">
|
||||||
|
<a href="/search?query=&type=audios&sort=id&only_performers=on&query={$audio->getPerformer()}">{$audio->getPerformer()}</a>
|
||||||
|
</strong>
|
||||||
|
—
|
||||||
|
<span class="title {if !empty($audio->getLyrics())}withLyrics{/if}">{$audio->getTitle()}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="explicitMark" n:if="$audio->isExplicit()"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="volume" style="display: flex; flex-direction: column;width:14%;">
|
||||||
|
<span class="nobold {if !$hideButtons}hideOnHover{/if}" data-unformatted="{$audio->getLength()}" style="text-align: center;margin-top: 12px;">{$audio->getFormattedLength()}</span>
|
||||||
|
<div class="buttons" style="margin-top: 8px;">
|
||||||
|
{php $hasAudio = isset($thisUser) && $audio->isInLibraryOf($thisUser)}
|
||||||
|
|
||||||
|
{if !$hideButtons}
|
||||||
|
<div class="remove-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && $hasAudio" ></div>
|
||||||
|
<div class="add-icon musicIcon hovermeicon" data-id="{$audio->getId()}" n:if="isset($thisUser) && !$hasAudio && !$isWithdrawn" ></div>
|
||||||
|
<div class="remove-icon-group musicIcon" data-id="{$audio->getId()}" data-club="{$club->getId()}" n:if="isset($thisUser) && isset($club) && $club->canBeModifiedBy($thisUser)" ></div>
|
||||||
|
<div class="add-icon-group musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && !$isWithdrawn" ></div>
|
||||||
|
<div class="edit-icon musicIcon" data-lyrics="{$audio->getLyrics()}" data-title="{$audio->getTitle()}" data-performer="{$audio->getPerformer()}" data-explicit="{(int)$audio->isExplicit()}" data-searchable="{(int)!$audio->isUnlisted()}" n:if="isset($thisUser) && $editable && !$isWithdrawn" ></div>
|
||||||
|
<div class="report-icon musicIcon" data-id="{$audio->getId()}" n:if="isset($thisUser) && !$editable && !$isWithdrawn" ></div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="subTracks">
|
||||||
|
<div style="width: 100%;">
|
||||||
|
<div class="track lengthTrack" style="margin-top: 3px;display:none">
|
||||||
|
<div class="selectableTrack" style="width: 100%;" n:attr="style => $isWithdrawn ? 'display: none;' : ''">
|
||||||
|
<div style="position: relative;width: calc(100% - 18px);">
|
||||||
|
<div class="slider"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="width: 81px;margin-left: 16px;">
|
||||||
|
<div class="track volumeTrack" style="margin-top: 3px;display:none">
|
||||||
|
<div class="selectableTrack" style="width: 100%;" n:attr="style => $isWithdrawn ? 'display: none;' : ''">
|
||||||
|
<div style="position: relative;width: calc(100% - 18px);">
|
||||||
|
<div class="slider"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lyrics" n:if="!empty($audio->getLyrics())">
|
||||||
|
{nl2br($audio->getLyrics())|noescape}
|
||||||
|
</div>
|
||||||
|
</div>
|
40
Web/Presenters/templates/Audio/tabs.xml
Normal file
40
Web/Presenters/templates/Audio/tabs.xml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<div class="searchOptions newer">
|
||||||
|
<div class="searchList" style="margin-top:10px">
|
||||||
|
<a n:attr="id => $mode === 'list' && $isMy ? 'used' : 'ki'" href="/audios{$thisUser->getId()}" n:if="isset($thisUser)">{_my_music}</a>
|
||||||
|
<a href="/player/upload{if $isMyClub}?gid={abs($ownerId)}{/if}" n:if="isset($thisUser)">{_upload_audio}</a>
|
||||||
|
<a n:attr="id => $mode === 'new' ? 'used' : 'ki'" href="/audios/new">{_audio_new}</a>
|
||||||
|
<a n:attr="id => $mode === 'popular' ? 'used' : 'ki'" href="/audios/popular">{_audio_popular}</a>
|
||||||
|
<a href="/search?type=audios" n:if="isset($thisUser)">{_audio_search}</a>
|
||||||
|
|
||||||
|
<hr n:if="isset($thisUser)">
|
||||||
|
|
||||||
|
<a n:attr="id => $mode === 'playlists' && $ownerId == $thisUser->getId() ? 'used' : 'ki'" href="/playlists{$thisUser->getId()}" n:if="isset($thisUser)">{_my_playlists}</a>
|
||||||
|
|
||||||
|
<a n:if="isset($thisUser)" href="/audios/newPlaylist">{_new_playlist}</a>
|
||||||
|
|
||||||
|
{if !$isMy && $mode !== 'popular' && $mode !== 'new'}
|
||||||
|
<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>
|
||||||
|
<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 href="/audios/newPlaylist{if $isMyClub}?gid={abs($ownerId)}{/if}" n:if="isset($thisUser) && $isMyClub">{_new_playlist}</a>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{if $friendsAudios}
|
||||||
|
<div class="friendsAudiosList">
|
||||||
|
<a href="/audios{$fr->getRealId()}" style="width: 94%;padding-left: 10px;" n:foreach="$friendsAudios as $fr">
|
||||||
|
<div class="elem">
|
||||||
|
<img src="{$fr->getAvatarURL()}" />
|
||||||
|
|
||||||
|
<div class="additionalInfo">
|
||||||
|
{php $audioStatus = $fr->getCurrentAudioStatus()}
|
||||||
|
<span class="name">{$fr->getCanonicalName()}</span>
|
||||||
|
<span class="desc">{$audioStatus ? $audioStatus->getName() : tr("audios_count", $fr->getAudiosCollectionSize())}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -91,13 +91,13 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="regform-left">
|
<td class="regform-left">
|
||||||
<span class="nobold">{_gender}: </span>
|
<span class="nobold">{_pronouns}: </span>
|
||||||
</td>
|
</td>
|
||||||
<td class="regform-right">
|
<td class="regform-right">
|
||||||
{var $femalePreferred = OPENVK_ROOT_CONF["openvk"]["preferences"]["femaleGenderPriority"]}
|
<select name="pronouns" required>
|
||||||
<select name="sex" required>
|
<option value="male">{_male}</option>
|
||||||
<option n:attr="selected => !$femalePreferred" value="male">{_male}</option>
|
<option value="female">{_female}</option>
|
||||||
<option n:attr="selected => $femalePreferred" value="female">{_female}</option>
|
<option value="neutral">{_neutral}</option>
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -77,7 +77,12 @@
|
||||||
<span class="nobold">{_wall}: </span>
|
<span class="nobold">{_wall}: </span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="checkbox" name="wall" value="1" n:attr="checked => $club->canPost()" /> {_group_allow_post_for_everyone}<br>
|
<select name="wall">
|
||||||
|
<option value="1" n:attr="selected => $club->getWallType() == 1" /> {_group_allow_post_for_everyone}</option>
|
||||||
|
<option value="2" n:attr="selected => $club->getWallType() == 2" /> {_group_limited_post}</option>
|
||||||
|
<option value="0" n:attr="selected => $club->getWallType() == 0" /> {_group_closed_post}</option>
|
||||||
|
<select>
|
||||||
|
|
||||||
<input type="checkbox" name="hide_from_global_feed" value="1" n:attr="checked => $club->isHideFromGlobalFeedEnabled()" /> {_group_hide_from_global_feed}
|
<input type="checkbox" name="hide_from_global_feed" value="1" n:attr="checked => $club->isHideFromGlobalFeedEnabled()" /> {_group_hide_from_global_feed}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -102,6 +107,15 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top">
|
||||||
|
<span class="nobold">{_audios}: </span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<label><input type="checkbox" name="upload_audios" value="1" n:attr="checked => $club->isEveryoneCanUploadAudios()" /> {_everyone_can_upload_audios}</label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td width="120" valign="top"><span class="nobold">{_role}: </span></td>
|
<td width="120" valign="top"><span class="nobold">{_role}: </span></td>
|
||||||
<td>
|
<td>
|
||||||
{$club->getOwner()->getId() == $user->getId() ? !$club->isOwnerHidden() || $club->canBeModifiedBy($thisUser) : !is_null($manager) ? tr("administrator") : tr("follower")}
|
{($club->getOwner()->getId() == $user->getId() ? !$club->isOwnerHidden() || $club->canBeModifiedBy($thisUser) : !is_null($manager)) ? tr("administrator") : tr("follower")}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr n:if="$manager && !empty($manager->getComment()) || $club->getOwner()->getId() === $user->getId() && !empty($club->getOwnerComment()) && (!$club->isOwnerHidden() || $club->canBeModifiedBy($thisUser))">
|
<tr n:if="$manager && !empty($manager->getComment()) || $club->getOwner()->getId() === $user->getId() && !empty($club->getOwnerComment()) && (!$club->isOwnerHidden() || $club->canBeModifiedBy($thisUser))">
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue