mirror of
https://github.com/openvk/openvk
synced 2025-04-23 00:23:01 +03:00
Merge branch 'master' into cool-fullscreen-video-reupload
This commit is contained in:
commit
2ce1e62b1b
238 changed files with 17091 additions and 1071 deletions
140
DBEntity.updated.php
Normal file
140
DBEntity.updated.php
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace Chandler\Database;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use Nette\Database\Table\Selection;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use Nette\InvalidStateException as ISE;
|
||||
use openvk\Web\Models\Repositories\CurrentUser;
|
||||
use openvk\Web\Models\Repositories\Logs;
|
||||
|
||||
|
||||
abstract class DBEntity
|
||||
{
|
||||
protected $record;
|
||||
protected $changes;
|
||||
protected $deleted;
|
||||
protected $user;
|
||||
|
||||
protected $tableName;
|
||||
|
||||
function __construct(?ActiveRow $row = NULL)
|
||||
{
|
||||
if(is_null($row)) return;
|
||||
|
||||
$_table = $row->getTable()->getName();
|
||||
if($_table !== $this->tableName)
|
||||
throw new ISE("Invalid data supplied for model: table $_table is not compatible with table" . $this->tableName);
|
||||
|
||||
$this->record = $row;
|
||||
}
|
||||
|
||||
function __call(string $fName, array $args)
|
||||
{
|
||||
if(substr($fName, 0, 3) === "set") {
|
||||
$field = mb_strtolower(substr($fName, 3));
|
||||
$this->stateChanges($field, $args[0]);
|
||||
} else {
|
||||
throw new \Error("Call to undefined method " . get_class($this) . "::$fName");
|
||||
}
|
||||
}
|
||||
|
||||
private function getTable(): Selection
|
||||
{
|
||||
return DatabaseConnection::i()->getContext()->table($this->tableName);
|
||||
}
|
||||
|
||||
protected function getRecord(): ?ActiveRow
|
||||
{
|
||||
return $this->record;
|
||||
}
|
||||
|
||||
protected function stateChanges(string $column, $value): void
|
||||
{
|
||||
if(!is_null($this->record))
|
||||
$t = $this->record->{$column}; #Test if column exists
|
||||
|
||||
$this->changes[$column] = $value;
|
||||
}
|
||||
|
||||
function getId()
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
}
|
||||
|
||||
function isDeleted(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->deleted;
|
||||
}
|
||||
|
||||
function unwrap(): object
|
||||
{
|
||||
return (object) $this->getRecord()->toArray();
|
||||
}
|
||||
|
||||
function delete(bool $softly = true): void
|
||||
{
|
||||
$user = CurrentUser::i()->getUser();
|
||||
$user_id = is_null($user) ? (int) OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"] : $user->getId();
|
||||
|
||||
if(is_null($this->record))
|
||||
throw new ISE("Can't delete a model, that hasn't been flushed to DB. Have you forgotten to call save() first?");
|
||||
|
||||
(new Logs)->create($user_id, $this->getTable()->getName(), get_class($this), 2, $this->record->toArray(), $this->changes);
|
||||
|
||||
if($softly) {
|
||||
$this->record = $this->getTable()->where("id", $this->record->id)->update(["deleted" => true]);
|
||||
} else {
|
||||
$this->record->delete();
|
||||
$this->deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
function undelete(): void
|
||||
{
|
||||
if(is_null($this->record))
|
||||
throw new ISE("Can't undelete a model, that hasn't been flushed to DB. Have you forgotten to call save() first?");
|
||||
|
||||
$user = CurrentUser::i()->getUser();
|
||||
$user_id = is_null($user) ? (int) OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"] : $user->getId();
|
||||
|
||||
(new Logs)->create($user_id, $this->getTable()->getName(), get_class($this), 3, $this->record->toArray(), ["deleted" => false]);
|
||||
|
||||
$this->getTable()->where("id", $this->record->id)->update(["deleted" => false]);
|
||||
}
|
||||
|
||||
function save(?bool $log = true): void
|
||||
{
|
||||
if ($log) {
|
||||
$user = CurrentUser::i();
|
||||
$user_id = is_null($user) ? (int)OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"] : $user->getUser()->getId();
|
||||
}
|
||||
|
||||
if(is_null($this->record)) {
|
||||
$this->record = $this->getTable()->insert($this->changes);
|
||||
|
||||
if ($log && $this->getTable()->getName() !== "logs") {
|
||||
(new Logs)->create($user_id, $this->getTable()->getName(), get_class($this), 0, $this->record->toArray(), $this->changes);
|
||||
}
|
||||
} else {
|
||||
if ($log && $this->getTable()->getName() !== "logs") {
|
||||
(new Logs)->create($user_id, $this->getTable()->getName(), get_class($this), 1, $this->record->toArray(), $this->changes);
|
||||
}
|
||||
|
||||
if ($this->deleted) {
|
||||
$this->record = $this->getTable()->insert((array)$this->record);
|
||||
} else {
|
||||
$this->getTable()->get($this->record->id)->update($this->changes);
|
||||
$this->record = $this->getTable()->get($this->record->id);
|
||||
}
|
||||
}
|
||||
|
||||
$this->changes = [];
|
||||
}
|
||||
|
||||
function getTableName(): string
|
||||
{
|
||||
return $this->getTable()->getName();
|
||||
}
|
||||
|
||||
use \Nette\SmartObject;
|
||||
}
|
|
@ -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)
|
||||
|
||||
* 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.
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ _[English](README.md)_
|
|||
|
||||
1. Установите PHP 7.4, веб-сервер, Composer, Node.js, Yarn и [Chandler](https://github.com/openvk/chandler)
|
||||
|
||||
* PHP 8 еще **не** тестировался, поэтому не стоит ожидать, что он будет работать (UPD: он не работает).
|
||||
* PHP 8 пока ещё тестируется, работоспособность движка на этой версии PHP пока не гарантируется.
|
||||
|
||||
2. Установите MySQL-совместимую базу данных.
|
||||
|
||||
|
|
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);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
namespace openvk\ServiceAPI;
|
||||
use openvk\Web\Models\Entities\Post;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\{Posts, Notes};
|
||||
use openvk\Web\Models\Repositories\{Posts, Notes, Videos};
|
||||
|
||||
class Wall implements Handler
|
||||
{
|
||||
|
@ -15,6 +15,7 @@ class Wall implements Handler
|
|||
$this->user = $user;
|
||||
$this->posts = new Posts;
|
||||
$this->notes = new Notes;
|
||||
$this->videos = new Videos;
|
||||
}
|
||||
|
||||
function getPost(int $id, callable $resolve, callable $reject): void
|
||||
|
@ -95,4 +96,45 @@ class Wall implements Handler
|
|||
|
||||
$resolve($arr);
|
||||
}
|
||||
|
||||
function getVideos(int $page = 1, callable $resolve, callable $reject)
|
||||
{
|
||||
$videos = $this->videos->getByUser($this->user, $page, 8);
|
||||
$count = $this->videos->getUserVideosCount($this->user);
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($videos as $video) {
|
||||
$res = json_decode(json_encode($video->toVkApiStruct()), true);
|
||||
$res["video"]["author_name"] = $video->getOwner()->getCanonicalName();
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
|
||||
function searchVideos(int $page = 1, string $query, callable $resolve, callable $reject)
|
||||
{
|
||||
$dbc = $this->videos->find($query);
|
||||
$videos = $dbc->page($page, 8);
|
||||
$count = $dbc->size();
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($videos as $video) {
|
||||
$res = json_decode(json_encode($video->toVkApiStruct()), true);
|
||||
$res["video"]["author_name"] = $video->getOwner()->getCanonicalName();
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,788 @@
|
|||
<?php declare(strict_types=1);
|
||||
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
|
||||
{
|
||||
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"];
|
||||
|
||||
return (object) [
|
||||
"count" => 1,
|
||||
"items" => [(object) [
|
||||
"id" => 1,
|
||||
"owner_id" => 1,
|
||||
"artist" => "В ОВК ПОКА НЕТ МУЗЫКИ",
|
||||
"title" => "ЖДИТЕ :)))",
|
||||
"duration" => 22,
|
||||
"url" => $serverUrl . "/assets/packages/static/openvk/audio/nomusic.mp3"
|
||||
]]
|
||||
];
|
||||
$this->requireUser();
|
||||
|
||||
$shuffleSeed = NULL;
|
||||
$shuffleSeedStr = NULL;
|
||||
if($shuffle == 1) {
|
||||
if(!$shuffle_seed) {
|
||||
if($need_seed == 1) {
|
||||
$shuffleSeed = openssl_random_pseudo_bytes(6);
|
||||
$shuffleSeedStr = base64_encode($shuffleSeed);
|
||||
$shuffleSeed = hexdec(bin2hex($shuffleSeed));
|
||||
} 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
|
||||
{
|
||||
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;
|
||||
$offset++;
|
||||
|
@ -13,6 +13,14 @@ final class Friends extends VKAPIRequestHandler
|
|||
$users = new UsersRepo;
|
||||
|
||||
$this->requireUser();
|
||||
|
||||
if ($user_id == 0) {
|
||||
$user_id = $this->getUser()->getId();
|
||||
}
|
||||
|
||||
if (is_null($users->get($user_id))) {
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid");
|
||||
}
|
||||
|
||||
foreach($users->get($user_id)->getFriends($offset, $count) as $friend) {
|
||||
$friends[$i] = $friend->getId();
|
||||
|
|
|
@ -6,12 +6,12 @@ use openvk\Web\Models\Entities\Club;
|
|||
|
||||
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();
|
||||
|
||||
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;
|
||||
$clbsCount = $this->getUser()->getClubCount();
|
||||
} else {
|
||||
|
@ -21,7 +21,7 @@ final class Groups extends VKAPIRequestHandler
|
|||
if(is_null($user))
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
foreach($user->getClubs($offset, false, $count, true) as $club)
|
||||
foreach($user->getClubs($offset, $filter == "admin", $count, true) as $club)
|
||||
$clbs[] = $club;
|
||||
|
||||
$clbsCount = $user->getClubCount();
|
||||
|
@ -292,7 +292,8 @@ final class Groups extends VKAPIRequestHandler
|
|||
int $topics = NULL,
|
||||
int $adminlist = NULL,
|
||||
int $topicsAboveWall = NULL,
|
||||
int $hideFromGlobalFeed = NULL)
|
||||
int $hideFromGlobalFeed = NULL,
|
||||
int $audio = NULL)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
@ -303,17 +304,22 @@ final class Groups extends VKAPIRequestHandler
|
|||
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.");
|
||||
|
||||
!is_null($title) ? $club->setName($title) : NULL;
|
||||
!is_null($description) ? $club->setAbout($description) : NULL;
|
||||
!is_null($screen_name) ? $club->setShortcode($screen_name) : NULL;
|
||||
!is_null($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;
|
||||
!is_null($adminlist) ? $club->setAdministrators_List_Display($adminlist) : NULL;
|
||||
!is_null($topicsAboveWall) ? $club->setDisplay_Topics_Above_Wall($topicsAboveWall) : NULL;
|
||||
!is_null($hideFromGlobalFeed) ? $club->setHide_From_Global_Feed($hideFromGlobalFeed) : NULL;
|
||||
!empty($title) ? $club->setName($title) : NULL;
|
||||
!empty($description) ? $club->setAbout($description) : NULL;
|
||||
!empty($screen_name) ? $club->setShortcode($screen_name) : NULL;
|
||||
!empty($website) ? $club->setWebsite((!parse_url($website, PHP_URL_SCHEME) ? "https://" : "") . $website) : NULL;
|
||||
!empty($wall) ? $club->setWall($wall) : NULL;
|
||||
!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(8, "Nothing changed");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -370,7 +376,7 @@ final class Groups extends VKAPIRequestHandler
|
|||
$arr->items[$i]->can_see_all_posts = 1;
|
||||
break;
|
||||
case "can_see_audio":
|
||||
$arr->items[$i]->can_see_audio = 0;
|
||||
$arr->items[$i]->can_see_audio = 1;
|
||||
break;
|
||||
case "can_write_private_message":
|
||||
$arr->items[$i]->can_write_private_message = 0;
|
||||
|
@ -469,7 +475,7 @@ final class Groups extends VKAPIRequestHandler
|
|||
"wall" => $club->canPost() == true ? 1 : 0,
|
||||
"photos" => 1,
|
||||
"video" => 0,
|
||||
"audio" => 0,
|
||||
"audio" => $club->isEveryoneCanUploadAudios() ? 1 : 0,
|
||||
"docs" => 0,
|
||||
"topics" => $club->isEveryoneCanCreateTopics() == true ? 1 : 0,
|
||||
"wiki" => 0,
|
||||
|
|
|
@ -7,6 +7,7 @@ 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
|
||||
{
|
||||
function add(string $type, int $owner_id, int $item_id): object
|
||||
|
@ -151,5 +152,52 @@ final class Likes extends VKAPIRequestHandler
|
|||
"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");
|
||||
|
||||
$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->willExecuteWriteAction();
|
||||
|
@ -79,7 +80,8 @@ final class Messages extends VKAPIRequestHandler
|
|||
$this->fail(946, "Chats are not implemented");
|
||||
else if($sticker_id !== -1)
|
||||
$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");
|
||||
|
||||
# lol recursion
|
||||
|
@ -117,6 +119,21 @@ final class Messages extends VKAPIRequestHandler
|
|||
if(!$msg)
|
||||
$this->fail(950, "Internal error");
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -393,4 +410,49 @@ final class Messages extends VKAPIRequestHandler
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,19 +161,17 @@ final class Notes extends VKAPIRequestHandler
|
|||
|
||||
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())
|
||||
if($comment->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(15, "Access to comment denied");
|
||||
|
||||
$comment->setContent($message);
|
||||
$comment->setEdited(time());
|
||||
$comment->save();
|
||||
*/
|
||||
$comment->save(true);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -211,7 +209,7 @@ final class Notes extends VKAPIRequestHandler
|
|||
$items = [];
|
||||
|
||||
$note = (new NotesRepo)->getNoteById((int)$id[0], (int)$id[1]);
|
||||
if($note) {
|
||||
if($note && !$note->isDeleted()) {
|
||||
$nodez->notes[] = $note->toVkApiStruct();
|
||||
}
|
||||
}
|
||||
|
|
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;
|
||||
}
|
||||
}
|
|
@ -432,13 +432,11 @@ final class Photos extends VKAPIRequestHandler
|
|||
if(empty($photo_ids)) {
|
||||
$album = (new Albums)->getAlbumByOwnerAndId($owner_id, $album_id);
|
||||
|
||||
if(!$album->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) {
|
||||
$this->fail(21, "This user chose to hide his albums.");
|
||||
}
|
||||
|
||||
if(!$album || $album->isDeleted()) {
|
||||
if(!$album || $album->isDeleted())
|
||||
$this->fail(21, "Invalid album");
|
||||
}
|
||||
|
||||
if(!$album->getOwner()->getPrivacyPermission('photos.read', $this->getUser()))
|
||||
$this->fail(21, "This user chose to hide his albums.");
|
||||
|
||||
$photos = array_slice(iterator_to_array($album->getPhotos(1, $count + $offset)), $offset);
|
||||
$res["count"] = sizeof($photos);
|
||||
|
@ -456,8 +454,7 @@ final class Photos extends VKAPIRequestHandler
|
|||
"items" => []
|
||||
];
|
||||
|
||||
foreach($photos as $photo)
|
||||
{
|
||||
foreach($photos as $photo) {
|
||||
$id = explode("_", $photo);
|
||||
|
||||
$phot = (new PhotosRepo)->getByOwnerAndVID((int)$id[0], (int)$id[1]);
|
||||
|
|
|
@ -104,4 +104,67 @@ final class Polls extends VKAPIRequestHandler
|
|||
$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,23 @@ final class Status extends VKAPIRequestHandler
|
|||
function get(int $user_id = 0, int $group_id = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
if($user_id == 0 && $group_id == 0) {
|
||||
return $this->getUser()->getStatus();
|
||||
} else {
|
||||
if($group_id > 0)
|
||||
$this->fail(501, "Group statuses are not implemented");
|
||||
else
|
||||
return (new UsersRepo)->get($user_id)->getStatus();
|
||||
|
||||
if($user_id == 0 && $group_id == 0)
|
||||
$user_id = $this->getUser()->getId();
|
||||
|
||||
if($group_id > 0)
|
||||
$this->fail(501, "Group statuses are not implemented");
|
||||
else {
|
||||
$user = (new UsersRepo)->get($user_id);
|
||||
$audioStatus = $user->getCurrentAudioStatus();
|
||||
if($audioStatus) {
|
||||
return [
|
||||
"status" => $user->getStatus(),
|
||||
"audio" => $audioStatus->toVkApiStruct(),
|
||||
];
|
||||
}
|
||||
|
||||
return $user->getStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<?php declare(strict_types=1);
|
||||
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\Reports;
|
||||
|
||||
final class Users extends VKAPIRequestHandler
|
||||
{
|
||||
|
@ -36,8 +37,8 @@ final class Users extends VKAPIRequestHandler
|
|||
} else if($usr->isBanned()) {
|
||||
$response[$i] = (object)[
|
||||
"id" => $usr->getId(),
|
||||
"first_name" => $usr->getFirstName(),
|
||||
"last_name" => $usr->getLastName(),
|
||||
"first_name" => $usr->getFirstName(true),
|
||||
"last_name" => $usr->getLastName(true),
|
||||
"deactivated" => "banned",
|
||||
"ban_reason" => $usr->getBanReason()
|
||||
];
|
||||
|
@ -46,8 +47,8 @@ final class Users extends VKAPIRequestHandler
|
|||
} else {
|
||||
$response[$i] = (object)[
|
||||
"id" => $usr->getId(),
|
||||
"first_name" => $usr->getFirstName(),
|
||||
"last_name" => $usr->getLastName(),
|
||||
"first_name" => $usr->getFirstName(true),
|
||||
"last_name" => $usr->getLastName(true),
|
||||
"is_closed" => false,
|
||||
"can_access_closed" => true,
|
||||
];
|
||||
|
@ -95,6 +96,12 @@ final class Users extends VKAPIRequestHandler
|
|||
case "status":
|
||||
if($usr->getStatus() != NULL)
|
||||
$response[$i]->status = $usr->getStatus();
|
||||
|
||||
$audioStatus = $usr->getCurrentAudioStatus();
|
||||
|
||||
if($audioStatus)
|
||||
$response[$i]->status_audio = $audioStatus->toVkApiStruct();
|
||||
|
||||
break;
|
||||
case "screen_name":
|
||||
if($usr->getShortCode() != NULL)
|
||||
|
@ -159,6 +166,18 @@ final class Users extends VKAPIRequestHandler
|
|||
case "interests":
|
||||
$response[$i]->interests = $usr->getInterests();
|
||||
break;
|
||||
case "quotes":
|
||||
$response[$i]->interests = $usr->getFavoriteQuote();
|
||||
break;
|
||||
case "email":
|
||||
$response[$i]->interests = $usr->getEmail();
|
||||
break;
|
||||
case "telegram":
|
||||
$response[$i]->interests = $usr->getTelegram();
|
||||
break;
|
||||
case "about":
|
||||
$response[$i]->interests = $usr->getDescription();
|
||||
break;
|
||||
case "rating":
|
||||
$response[$i]->rating = $usr->getRating();
|
||||
break;
|
||||
|
@ -289,4 +308,26 @@ final class Users extends VKAPIRequestHandler
|
|||
"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
|
||||
{
|
||||
return $this->platform;
|
||||
return $this->platform ?? "";
|
||||
}
|
||||
|
||||
protected function userAuthorized(): bool
|
||||
|
|
|
@ -11,11 +11,11 @@ use openvk\Web\Models\Repositories\Comments as CommentsRepo;
|
|||
|
||||
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();
|
||||
|
||||
if ($videos) {
|
||||
if(!empty($videos)) {
|
||||
$vids = explode(',', $videos);
|
||||
|
||||
foreach($vids as $vid)
|
||||
|
|
|
@ -15,6 +15,8 @@ use openvk\Web\Models\Entities\Video;
|
|||
use openvk\Web\Models\Repositories\Videos as VideosRepo;
|
||||
use openvk\Web\Models\Entities\Note;
|
||||
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
|
||||
{
|
||||
|
@ -58,6 +60,11 @@ final class Wall extends VKAPIRequestHandler
|
|||
$attachments[] = $attachment->getApiStructure($this->getUser());
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
||||
$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) {
|
||||
$repostAttachments = [];
|
||||
|
||||
|
@ -119,7 +126,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
"post_type" => "post",
|
||||
"text" => $post->getText(false),
|
||||
"copy_history" => $repost,
|
||||
"can_edit" => 0, # TODO
|
||||
"can_edit" => $post->canBeEditedBy($this->getUser()),
|
||||
"can_delete" => $post->canBeDeletedBy($this->getUser()),
|
||||
"can_pin" => $post->canBePinnedBy($this->getUser()),
|
||||
"can_archive" => false, # TODO MAYBE
|
||||
|
@ -233,6 +240,11 @@ final class Wall extends VKAPIRequestHandler
|
|||
$attachments[] = $attachment->getApiStructure($this->getUser());
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
||||
$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) {
|
||||
$repostAttachments = [];
|
||||
|
||||
|
@ -294,7 +306,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
"post_type" => "post",
|
||||
"text" => $post->getText(false),
|
||||
"copy_history" => $repost,
|
||||
"can_edit" => 0, # TODO
|
||||
"can_edit" => $post->canBeEditedBy($this->getUser()),
|
||||
"can_delete" => $post->canBeDeletedBy($user),
|
||||
"can_pin" => $post->canBePinnedBy($user),
|
||||
"can_archive" => false, # TODO MAYBE
|
||||
|
@ -433,6 +445,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
$this->fail(100, "One of the parameters specified was missing or invalid");
|
||||
}
|
||||
|
||||
# TODO use parseAttachments
|
||||
if(!empty($attachments)) {
|
||||
$attachmentsArr = explode(",", $attachments);
|
||||
# Аттачи такого вида: [тип][id владельца]_[id вложения]
|
||||
|
@ -440,6 +453,10 @@ final class Wall extends VKAPIRequestHandler
|
|||
|
||||
if(sizeof($attachmentsArr) > 10)
|
||||
$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) {
|
||||
$attachmentType = NULL;
|
||||
|
@ -450,6 +467,11 @@ final class Wall extends VKAPIRequestHandler
|
|||
$attachmentType = "video";
|
||||
elseif(str_contains($attac, "note"))
|
||||
$attachmentType = "note";
|
||||
elseif(str_contains($attac, "poll"))
|
||||
$attachmentType = "poll";
|
||||
elseif(str_contains($attac, "audio"))
|
||||
$attachmentType = "audio";
|
||||
|
||||
else
|
||||
$this->fail(205, "Unknown attachment type");
|
||||
|
||||
|
@ -463,28 +485,38 @@ final class Wall extends VKAPIRequestHandler
|
|||
if($attachmentType == "photo") {
|
||||
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Photo does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this photo");
|
||||
$this->fail(100, "Invalid photo");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('photos.read', $this->getUser()))
|
||||
$this->fail(43, "Access to photo denied");
|
||||
|
||||
$post->attach($attacc);
|
||||
} elseif($attachmentType == "video") {
|
||||
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Video does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this video");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('videos.read', $this->getUser()))
|
||||
$this->fail(43, "Access to video denied");
|
||||
|
||||
$post->attach($attacc);
|
||||
} elseif($attachmentType == "note") {
|
||||
$attacc = (new NotesRepo)->getNoteById($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Note does not exist");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
||||
$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 note");
|
||||
|
||||
if($attacc->getOwner()->getPrivacySetting("notes.read") < 1)
|
||||
$this->fail(11, "You can't attach note to post, because your notes list is closed. Change it in privacy settings in web-version.");
|
||||
$this->fail(43, "You do not have access to this poll");
|
||||
} elseif($attachmentType == "audio") {
|
||||
$attacc = (new AudiosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Audio does not exist");
|
||||
|
||||
$post->attach($attacc);
|
||||
}
|
||||
|
@ -565,6 +597,11 @@ final class Wall extends VKAPIRequestHandler
|
|||
$attachments[] = $this->getApiPhoto($attachment);
|
||||
} elseif($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
||||
$attachments[] = $attachment->toVkApiStruct();
|
||||
} elseif($attachment instanceof \openvk\Web\Models\Entities\Audio) {
|
||||
$attachments[] = [
|
||||
"type" => "audio",
|
||||
"audio" => $attachment->toVkApiStruct($this->getUser()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -624,6 +661,9 @@ final class Wall extends VKAPIRequestHandler
|
|||
|
||||
$comment = (new CommentsRepo)->get($comment_id); # один хуй айди всех комментов общий
|
||||
|
||||
if(!$comment || $comment->isDeleted())
|
||||
$this->fail(100, "Invalid comment");
|
||||
|
||||
$profiles = [];
|
||||
|
||||
$attachments = [];
|
||||
|
@ -631,6 +671,11 @@ final class Wall extends VKAPIRequestHandler
|
|||
foreach($comment->getChildren() as $attachment) {
|
||||
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||
$attachments[] = $this->getApiPhoto($attachment);
|
||||
} elseif($attachment instanceof \openvk\Web\Models\Entities\Audio) {
|
||||
$attachments[] = [
|
||||
"type" => "audio",
|
||||
"audio" => $attachment->toVkApiStruct($this->getUser()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -678,7 +723,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
return $response;
|
||||
}
|
||||
|
||||
function createComment(int $owner_id, int $post_id, string $message, int $from_group = 0, string $attachments = "") {
|
||||
function createComment(int $owner_id, int $post_id, string $message = "", int $from_group = 0, string $attachments = "") {
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
|
@ -722,6 +767,8 @@ final class Wall extends VKAPIRequestHandler
|
|||
$attachmentType = "photo";
|
||||
elseif(str_contains($attac, "video"))
|
||||
$attachmentType = "video";
|
||||
elseif(str_contains($attac, "audio"))
|
||||
$attachmentType = "audio";
|
||||
else
|
||||
$this->fail(205, "Unknown attachment type");
|
||||
|
||||
|
@ -736,16 +783,22 @@ final class Wall extends VKAPIRequestHandler
|
|||
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Photo does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this photo");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('photos.read', $this->getUser()))
|
||||
$this->fail(11, "Access to photo denied");
|
||||
|
||||
$comment->attach($attacc);
|
||||
} elseif($attachmentType == "video") {
|
||||
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Video does not exists");
|
||||
if($attacc->getOwner()->getId() != $this->getUser()->getId())
|
||||
$this->fail(43, "You do not have access to this video");
|
||||
if(!$attacc->getOwner()->getPrivacyPermission('videos.read', $this->getUser()))
|
||||
$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);
|
||||
}
|
||||
|
@ -776,11 +829,100 @@ final class Wall extends VKAPIRequestHandler
|
|||
return 1;
|
||||
}
|
||||
|
||||
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() && $attach->getOwner()->getId() == $this->getUser()->getId())
|
||||
$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() && $attach->getOwner()->getId() == $this->getUser()->getId())
|
||||
$comment->attach($attach);
|
||||
else
|
||||
$this->fail(52, "One of the attachments is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private function getApiPhoto($attachment) {
|
||||
return [
|
||||
"type" => "photo",
|
||||
"photo" => [
|
||||
"album_id" => $attachment->getAlbum() ? $attachment->getAlbum()->getId() : NULL,
|
||||
"album_id" => $attachment->getAlbum() ? $attachment->getAlbum()->getId() : 0,
|
||||
"date" => $attachment->getPublicationTime()->timestamp(),
|
||||
"id" => $attachment->getVirtualId(),
|
||||
"owner_id" => $attachment->getOwner()->getId(),
|
||||
|
|
|
@ -48,7 +48,7 @@ class APIToken extends RowModel
|
|||
$this->delete();
|
||||
}
|
||||
|
||||
function save(): void
|
||||
function save(?bool $log = false): void
|
||||
{
|
||||
if(is_null($this->getRecord()))
|
||||
$this->stateChanges("secret", bin2hex(openssl_random_pseudo_bytes(36)));
|
||||
|
|
|
@ -306,11 +306,14 @@ class Application extends RowModel
|
|||
function delete(bool $softly = true): void
|
||||
{
|
||||
if($softly)
|
||||
throw new \UnexpectedValueException("Can't delete apps softly.");
|
||||
throw new \UnexpectedValueException("Can't delete apps softly."); // why
|
||||
|
||||
$cx = DatabaseConnection::i()->getContext();
|
||||
$cx->table("app_users")->where("app", $this->getId())->delete();
|
||||
|
||||
parent::delete(false);
|
||||
}
|
||||
|
||||
function getPublicationTime(): string
|
||||
{ return tr("recently"); }
|
||||
}
|
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);
|
||||
}
|
||||
}
|
66
Web/Models/Entities/Ban.php
Normal file
66
Web/Models/Entities/Ban.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use openvk\Web\Util\DateTime;
|
||||
use openvk\Web\Models\Repositories\{Users};
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
|
||||
class Ban extends RowModel
|
||||
{
|
||||
protected $tableName = "bans";
|
||||
|
||||
function getId(): int
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getReason(): ?string
|
||||
{
|
||||
return $this->getRecord()->reason;
|
||||
}
|
||||
|
||||
function getUser(): ?User
|
||||
{
|
||||
return (new Users)->get($this->getRecord()->user);
|
||||
}
|
||||
|
||||
function getInitiator(): ?User
|
||||
{
|
||||
return (new Users)->get($this->getRecord()->initiator);
|
||||
}
|
||||
|
||||
function getStartTime(): int
|
||||
{
|
||||
return $this->getRecord()->iat;
|
||||
}
|
||||
|
||||
function getEndTime(): int
|
||||
{
|
||||
return $this->getRecord()->exp;
|
||||
}
|
||||
|
||||
function getTime(): int
|
||||
{
|
||||
return $this->getRecord()->time;
|
||||
}
|
||||
|
||||
function isPermanent(): bool
|
||||
{
|
||||
return $this->getEndTime() === 0;
|
||||
}
|
||||
|
||||
function isRemovedManually(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->removed_manually;
|
||||
}
|
||||
|
||||
function isOver(): bool
|
||||
{
|
||||
return $this->isRemovedManually();
|
||||
}
|
||||
|
||||
function whoRemoved(): ?User
|
||||
{
|
||||
return (new Users)->get($this->getRecord()->removed_by);
|
||||
}
|
||||
}
|
|
@ -224,7 +224,7 @@ class Club extends RowModel
|
|||
"shape" => "spline",
|
||||
"color" => "#597da3",
|
||||
],
|
||||
"name" => $unique ? "Полный охват" : "Все просмотры",
|
||||
"name" => $unique ? tr("full_coverage") : tr("all_views"),
|
||||
],
|
||||
"subs" => [
|
||||
"x" => array_reverse(range(1, 7)),
|
||||
|
@ -235,7 +235,7 @@ class Club extends RowModel
|
|||
"color" => "#b05c91",
|
||||
],
|
||||
"fill" => "tozeroy",
|
||||
"name" => $unique ? "Охват подписчиков" : "Просмотры подписчиков",
|
||||
"name" => $unique ? tr("subs_coverage") : tr("subs_views"),
|
||||
],
|
||||
"viral" => [
|
||||
"x" => array_reverse(range(1, 7)),
|
||||
|
@ -246,7 +246,7 @@ class Club extends RowModel
|
|||
"color" => "#4d9fab",
|
||||
],
|
||||
"fill" => "tozeroy",
|
||||
"name" => $unique ? "Виральный охват" : "Виральные просмотры",
|
||||
"name" => $unique ? tr("viral_coverage") : tr("viral_views"),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
@ -351,44 +351,80 @@ class Club extends RowModel
|
|||
}
|
||||
|
||||
function getWebsite(): ?string
|
||||
{
|
||||
return $this->getRecord()->website;
|
||||
}
|
||||
{
|
||||
return $this->getRecord()->website;
|
||||
}
|
||||
|
||||
function ban(string $reason): void
|
||||
{
|
||||
$this->setBlock_Reason($reason);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function unban(): void
|
||||
{
|
||||
$this->setBlock_Reason(null);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function getAlert(): ?string
|
||||
{
|
||||
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
|
||||
{
|
||||
$res = [];
|
||||
$res = (object) [];
|
||||
|
||||
$res->id = $this->getId();
|
||||
$res->name = $this->getName();
|
||||
$res->screen_name = $this->getShortCode();
|
||||
$res->is_closed = 0;
|
||||
$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->is_member = $this->getSubscriptionStatus($user) ? 1 : 0;
|
||||
$res->is_member = $user && $this->getSubscriptionStatus($user) ? 1 : 0;
|
||||
|
||||
$res->type = "group";
|
||||
$res->photo_50 = $this->getAvatarUrl("miniscule");
|
||||
$res->photo_100 = $this->getAvatarUrl("tiny");
|
||||
$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;
|
||||
}
|
||||
|
||||
use Traits\TBackDrops;
|
||||
use Traits\TSubscribable;
|
||||
use Traits\TAudioStatuses;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ class Comment extends Post
|
|||
|
||||
function getPrettyId(): string
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
return (string)$this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getVirtualId(): int
|
||||
|
@ -89,4 +89,30 @@ class Comment extends Post
|
|||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
function getURL(): string
|
||||
{
|
||||
return "/wall" . $this->getTarget()->getPrettyId() . "#_comment" . $this->getId();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if(!$user)
|
||||
return false;
|
||||
|
||||
return $user->getId() == $this->getOwner(false)->getId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ class Correspondence
|
|||
*/
|
||||
function getPreviewMessage(): ?Message
|
||||
{
|
||||
$messages = $this->getMessages(1, NULL, 1);
|
||||
$messages = $this->getMessages(1, NULL, 1, 0);
|
||||
return $messages[0] ?? NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ class IP extends RowModel
|
|||
$this->stateChanges("rate_limit_counter", $aCounter);
|
||||
$this->stateChanges("rate_limit_violation_counter_start", $vCounterSessionStart);
|
||||
$this->stateChanges("rate_limit_violation_counter", $vCounter);
|
||||
$this->save();
|
||||
$this->save(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,11 +105,11 @@ class IP extends RowModel
|
|||
$this->stateChanges("ip", $ip);
|
||||
}
|
||||
|
||||
function save(): void
|
||||
function save(?bool $log = false): void
|
||||
{
|
||||
if(is_null($this->getRecord()))
|
||||
$this->stateChanges("first_seen", time());
|
||||
|
||||
parent::save();
|
||||
parent::save($log);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,14 +121,14 @@ abstract class Media extends Postable
|
|||
$this->stateChanges("hash", $hash);
|
||||
}
|
||||
|
||||
function save(): void
|
||||
function save(?bool $log = false): void
|
||||
{
|
||||
if(!is_null($this->processingPlaceholder) && is_null($this->getRecord())) {
|
||||
$this->stateChanges("processed", 0);
|
||||
$this->stateChanges("last_checked", time());
|
||||
}
|
||||
|
||||
parent::save();
|
||||
parent::save($log);
|
||||
}
|
||||
|
||||
function delete(bool $softly = true): void
|
||||
|
|
|
@ -17,7 +17,17 @@ abstract class MediaCollection extends RowModel
|
|||
|
||||
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)
|
||||
{
|
||||
|
@ -70,18 +80,29 @@ abstract class MediaCollection extends RowModel
|
|||
}
|
||||
|
||||
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) {
|
||||
$media = $rel->ref($this->entityTableName, "media");
|
||||
if(!$media)
|
||||
continue;
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -110,7 +131,7 @@ abstract class MediaCollection extends RowModel
|
|||
{
|
||||
return $this->getRecord()->special_type !== 0;
|
||||
}
|
||||
|
||||
|
||||
function add(RowModel $entity): bool
|
||||
{
|
||||
$this->entitySuitable($entity);
|
||||
|
@ -118,6 +139,10 @@ abstract class MediaCollection extends RowModel
|
|||
if(!$this->allowDuplicates)
|
||||
if($this->has($entity))
|
||||
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([
|
||||
"collection" => $this->getId(),
|
||||
|
@ -127,14 +152,14 @@ abstract class MediaCollection extends RowModel
|
|||
return true;
|
||||
}
|
||||
|
||||
function remove(RowModel $entity): void
|
||||
function remove(RowModel $entity): bool
|
||||
{
|
||||
$this->entitySuitable($entity);
|
||||
|
||||
$this->relations->where([
|
||||
return $this->relations->where([
|
||||
"collection" => $this->getId(),
|
||||
"media" => $entity->getId(),
|
||||
])->delete();
|
||||
])->delete() > 0;
|
||||
}
|
||||
|
||||
function has(RowModel $entity): bool
|
||||
|
@ -148,6 +173,33 @@ abstract class MediaCollection extends RowModel
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -123,7 +123,11 @@ class Message extends RowModel
|
|||
],
|
||||
];
|
||||
} else {
|
||||
throw new \Exception("Unknown attachment type: " . get_class($attachment));
|
||||
$attachments[] = [
|
||||
"type" => "unknown"
|
||||
];
|
||||
|
||||
# throw new \Exception("Unknown attachment type: " . get_class($attachment));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
71
Web/Models/Entities/NoSpamLog.php
Normal file
71
Web/Models/Entities/NoSpamLog.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use openvk\Web\Util\DateTime;
|
||||
use openvk\Web\Models\Repositories\{Users};
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
|
||||
class NoSpamLog extends RowModel
|
||||
{
|
||||
protected $tableName = "noSpam_templates";
|
||||
|
||||
function getId(): int
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getUser(): ?User
|
||||
{
|
||||
return (new Users)->get($this->getRecord()->user);
|
||||
}
|
||||
|
||||
function getModel(): string
|
||||
{
|
||||
return $this->getRecord()->model;
|
||||
}
|
||||
|
||||
function getRegex(): ?string
|
||||
{
|
||||
return $this->getRecord()->regex;
|
||||
}
|
||||
|
||||
function getRequest(): ?string
|
||||
{
|
||||
return $this->getRecord()->request;
|
||||
}
|
||||
|
||||
function getCount(): int
|
||||
{
|
||||
return $this->getRecord()->count;
|
||||
}
|
||||
|
||||
function getTime(): DateTime
|
||||
{
|
||||
return new DateTime($this->getRecord()->time);
|
||||
}
|
||||
|
||||
function getItems(): ?array
|
||||
{
|
||||
return explode(",", $this->getRecord()->items);
|
||||
}
|
||||
|
||||
function getTypeRaw(): int
|
||||
{
|
||||
return $this->getRecord()->ban_type;
|
||||
}
|
||||
|
||||
function getType(): string
|
||||
{
|
||||
switch ($this->getTypeRaw()) {
|
||||
case 1: return "О";
|
||||
case 2: return "Б";
|
||||
case 3: return "ОБ";
|
||||
default: return (string) $this->getTypeRaw();
|
||||
}
|
||||
}
|
||||
|
||||
function isRollbacked(): bool
|
||||
{
|
||||
return !is_null($this->getRecord()->rollback);
|
||||
}
|
||||
}
|
|
@ -124,14 +124,14 @@ class Note extends Postable
|
|||
$res = (object) [];
|
||||
|
||||
$res->type = "note";
|
||||
$res->id = $this->getId();
|
||||
$res->id = $this->getVirtualId();
|
||||
$res->owner_id = $this->getOwner()->getId();
|
||||
$res->title = $this->getName();
|
||||
$res->text = $this->getText();
|
||||
$res->date = $this->getPublicationTime()->timestamp();
|
||||
$res->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->can_comment = 1;
|
||||
$res->text_wiki = "r";
|
||||
|
|
|
@ -132,4 +132,138 @@ QUERY;
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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("timestamp", time());
|
||||
|
||||
parent::save();
|
||||
parent::save($log);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -347,4 +347,20 @@ class Photo extends Media
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -279,12 +279,12 @@ class Poll extends Attachable
|
|||
return $poll;
|
||||
}
|
||||
|
||||
function save(): void
|
||||
function save(?bool $log = false): void
|
||||
{
|
||||
if(empty($this->choicesToPersist))
|
||||
throw new InvalidStateException;
|
||||
|
||||
parent::save();
|
||||
parent::save($log);
|
||||
foreach($this->choicesToPersist as $option) {
|
||||
DatabaseConnection::i()->getContext()->table("poll_options")->insert([
|
||||
"poll" => $this->getId(),
|
||||
|
|
|
@ -245,6 +245,37 @@ class Post extends Postable
|
|||
$this->unwire();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if(!$user)
|
||||
return false;
|
||||
|
||||
if($this->isDeactivationMessage() || $this->isUpdateAvatarMessage())
|
||||
return false;
|
||||
|
||||
if($this->getTargetWall() > 0)
|
||||
return $this->getPublicationTime()->timestamp() + WEEK > time() && $user->getId() == $this->getOwner(false)->getId();
|
||||
|
||||
return $user->getId() == $this->getOwner(false)->getId();
|
||||
}
|
||||
|
||||
use Traits\TRichText;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ abstract class Postable extends Attachable
|
|||
$oid = (int) $this->getRecord()->owner;
|
||||
if(!$real && $this->isAnonymous())
|
||||
$oid = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["account"];
|
||||
|
||||
|
||||
$oid = abs($oid);
|
||||
if($oid > 0)
|
||||
return (new Users)->get($oid);
|
||||
else
|
||||
|
@ -87,13 +88,14 @@ abstract class Postable extends Attachable
|
|||
])->group("origin"));
|
||||
}
|
||||
|
||||
# TODO add pagination
|
||||
function getLikers(): \Traversable
|
||||
function getLikers(int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
{
|
||||
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
||||
|
||||
$sel = DB::i()->getContext()->table("likes")->where([
|
||||
"model" => static::class,
|
||||
"target" => $this->getRecord()->id,
|
||||
]);
|
||||
])->page($page, $perPage);
|
||||
|
||||
foreach($sel as $like)
|
||||
yield (new Users)->get($like->origin);
|
||||
|
@ -156,7 +158,7 @@ abstract class Postable extends Attachable
|
|||
throw new ISE("Setting virtual id manually is forbidden");
|
||||
}
|
||||
|
||||
function save(): void
|
||||
function save(?bool $log = false): void
|
||||
{
|
||||
$vref = $this->upperNodeReferenceColumnName;
|
||||
|
||||
|
@ -171,11 +173,11 @@ abstract class Postable extends Attachable
|
|||
$this->stateChanges("created", time());
|
||||
|
||||
$this->stateChanges("virtual_id", $pCount + 1);
|
||||
} else {
|
||||
} /*else {
|
||||
$this->stateChanges("edited", time());
|
||||
}
|
||||
}*/
|
||||
|
||||
parent::save();
|
||||
parent::save($log);
|
||||
}
|
||||
|
||||
use Traits\TAttachmentHost;
|
||||
|
|
155
Web/Models/Entities/Report.php
Normal file
155
Web/Models/Entities/Report.php
Normal file
|
@ -0,0 +1,155 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use openvk\Web\Util\DateTime;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Repositories\{Applications, Comments, Notes, Reports, Audios, Users, Posts, Photos, Videos, Clubs};
|
||||
use Chandler\Database\DatabaseConnection as DB;
|
||||
use Nette\InvalidStateException as ISE;
|
||||
use Nette\Database\Table\Selection;
|
||||
|
||||
class Report extends RowModel
|
||||
{
|
||||
protected $tableName = "reports";
|
||||
|
||||
function getId(): int
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getStatus(): int
|
||||
{
|
||||
return $this->getRecord()->status;
|
||||
}
|
||||
|
||||
function getContentType(): string
|
||||
{
|
||||
return $this->getRecord()->type;
|
||||
}
|
||||
|
||||
function getReason(): string
|
||||
{
|
||||
return $this->getRecord()->reason;
|
||||
}
|
||||
|
||||
function getTime(): DateTime
|
||||
{
|
||||
return new DateTime($this->getRecord()->date);
|
||||
}
|
||||
|
||||
function isDeleted(): bool
|
||||
{
|
||||
if ($this->getRecord()->deleted === 0)
|
||||
{
|
||||
return false;
|
||||
} elseif ($this->getRecord()->deleted === 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function authorId(): int
|
||||
{
|
||||
return $this->getRecord()->user_id;
|
||||
}
|
||||
|
||||
function getUser(): User
|
||||
{
|
||||
return (new Users)->get((int) $this->getRecord()->user_id);
|
||||
}
|
||||
|
||||
function getContentId(): int
|
||||
{
|
||||
return (int) $this->getRecord()->target_id;
|
||||
}
|
||||
|
||||
function getContentObject()
|
||||
{
|
||||
if ($this->getContentType() == "post") return (new Posts)->get($this->getContentId());
|
||||
else if ($this->getContentType() == "photo") return (new Photos)->get($this->getContentId());
|
||||
else if ($this->getContentType() == "video") return (new Videos)->get($this->getContentId());
|
||||
else if ($this->getContentType() == "group") return (new Clubs)->get($this->getContentId());
|
||||
else if ($this->getContentType() == "comment") return (new Comments)->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() == "user") return (new Users)->get($this->getContentId());
|
||||
else if ($this->getContentType() == "audio") return (new Audios)->get($this->getContentId());
|
||||
else return null;
|
||||
}
|
||||
|
||||
function getAuthor(): RowModel
|
||||
{
|
||||
return (new Posts)->get($this->getContentId())->getOwner();
|
||||
}
|
||||
|
||||
function getReportAuthor(): User
|
||||
{
|
||||
return (new Users)->get($this->getRecord()->user_id);
|
||||
}
|
||||
|
||||
function banUser($initiator)
|
||||
{
|
||||
$reason = $this->getContentType() !== "user" ? ("**content-" . $this->getContentType() . "-" . $this->getContentId() . "**") : ("Подозрительная активность");
|
||||
$this->getAuthor()->ban($reason, false, time() + $this->getAuthor()->getNewBanTime(), $initiator);
|
||||
}
|
||||
|
||||
function deleteContent()
|
||||
{
|
||||
if ($this->getContentType() !== "user") {
|
||||
$pubTime = $this->getContentObject()->getPublicationTime();
|
||||
if (method_exists($this->getContentObject(), "getName")) {
|
||||
$name = $this->getContentObject()->getName();
|
||||
$placeholder = "$pubTime ($name)";
|
||||
} else {
|
||||
$placeholder = "$pubTime";
|
||||
}
|
||||
|
||||
if ($this->getAuthor() instanceof Club) {
|
||||
$name = $this->getAuthor()->getName();
|
||||
$this->getAuthor()->getOwner()->adminNotify("Ваш контент, который опубликовали $placeholder в созданной вами группе \"$name\" был удалён модераторами инстанса. За повторные или серьёзные нарушения группу могут заблокировать.");
|
||||
} else {
|
||||
$this->getAuthor()->adminNotify("Ваш контент, который вы опубликовали $placeholder был удалён модераторами инстанса. За повторные или серьёзные нарушения вас могут заблокировать.");
|
||||
}
|
||||
$this->getContentObject()->delete($this->getContentType() !== "app");
|
||||
}
|
||||
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
function getDuplicates(): \Traversable
|
||||
{
|
||||
return (new Reports)->getDuplicates($this->getContentType(), $this->getContentId(), $this->getId());
|
||||
}
|
||||
|
||||
function getDuplicatesCount(): int
|
||||
{
|
||||
return count(iterator_to_array($this->getDuplicates()));
|
||||
}
|
||||
|
||||
function hasDuplicates(): bool
|
||||
{
|
||||
return $this->getDuplicatesCount() > 0;
|
||||
}
|
||||
|
||||
function getContentName(): string
|
||||
{
|
||||
if (method_exists($this->getContentObject(), "getCanonicalName"))
|
||||
return $this->getContentObject()->getCanonicalName();
|
||||
|
||||
return $this->getContentType() . " #" . $this->getContentId();
|
||||
}
|
||||
|
||||
public function delete(bool $softly = true): void
|
||||
{
|
||||
if ($this->hasDuplicates()) {
|
||||
foreach ($this->getDuplicates() as $duplicate) {
|
||||
$duplicate->setDeleted(1);
|
||||
$duplicate->save();
|
||||
}
|
||||
}
|
||||
|
||||
$this->setDeleted(1);
|
||||
$this->save();
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
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;
|
||||
|
||||
trait TAttachmentHost
|
||||
|
@ -29,6 +30,46 @@ trait TAttachmentHost
|
|||
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
|
||||
{
|
||||
|
|
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,12 @@ use openvk\Web\Models\Entities\User;
|
|||
|
||||
trait TOwnable
|
||||
{
|
||||
function canBeViewedBy(?User $user): bool
|
||||
{
|
||||
// TODO implement normal check in master
|
||||
return true;
|
||||
}
|
||||
|
||||
function canBeModifiedBy(User $user): bool
|
||||
{
|
||||
if(method_exists($this, "isCreatedBySystem"))
|
||||
|
|
|
@ -4,8 +4,8 @@ use morphos\Gender;
|
|||
use openvk\Web\Themes\{Themepack, Themepacks};
|
||||
use openvk\Web\Util\DateTime;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use openvk\Web\Models\Entities\{Photo, Message, Correspondence, Gift};
|
||||
use openvk\Web\Models\Repositories\{Photos, Users, Clubs, Albums, Gifts, Notifications};
|
||||
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\Exceptions\InvalidUserNameException;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
@ -190,7 +190,7 @@ class User extends RowModel
|
|||
function getMorphedName(string $case = "genitive", bool $fullName = true): string
|
||||
{
|
||||
$name = $fullName ? ($this->getLastName() . " " . $this->getFirstName()) : $this->getFirstName();
|
||||
if(!preg_match("%^[А-яё\-]+$%", $name))
|
||||
if(!preg_match("%[А-яё\-]+$%", $name))
|
||||
return $name; # name is probably not russian
|
||||
|
||||
$inflected = inflectName($name, $case, $this->isFemale() ? Gender::FEMALE : Gender::MALE);
|
||||
|
@ -241,11 +241,60 @@ class User extends RowModel
|
|||
return $this->getRecord()->alert;
|
||||
}
|
||||
|
||||
function getBanReason(): ?string
|
||||
function getTextForContentBan(string $type): string
|
||||
{
|
||||
switch ($type) {
|
||||
case "post": return "за размещение от Вашего лица таких <b>записей</b>:";
|
||||
case "photo": return "за размещение от Вашего лица таких <b>фотографий</b>:";
|
||||
case "video": return "за размещение от Вашего лица таких <b>видеозаписей</b>:";
|
||||
case "group": return "за подозрительное вступление от Вашего лица <b>в группу:</b>";
|
||||
case "comment": return "за размещение от Вашего лица таких <b>комментариев</b>:";
|
||||
case "note": return "за размещение от Вашего лица таких <b>заметок</b>:";
|
||||
case "app": return "за создание от Вашего имени <b>подозрительных приложений</b>.";
|
||||
default: return "за размещение от Вашего лица такого <b>контента</b>:";
|
||||
}
|
||||
}
|
||||
|
||||
function getRawBanReason(): ?string
|
||||
{
|
||||
return $this->getRecord()->block_reason;
|
||||
}
|
||||
|
||||
function getBanReason(?string $for = null)
|
||||
{
|
||||
$ban = (new Bans)->get((int) $this->getRecord()->block_reason);
|
||||
if (!$ban || $ban->isOver()) return null;
|
||||
|
||||
$reason = $ban->getReason();
|
||||
|
||||
preg_match('/\*\*content-(post|photo|video|group|comment|note|app|noSpamTemplate|user)-(\d+)\*\*$/', $reason, $matches);
|
||||
if (sizeof($matches) === 3) {
|
||||
$content_type = $matches[1]; $content_id = (int) $matches[2];
|
||||
if (in_array($content_type, ["noSpamTemplate", "user"])) {
|
||||
$reason = "Подозрительная активность";
|
||||
} else {
|
||||
if ($for !== "banned") {
|
||||
$reason = "Подозрительная активность";
|
||||
} else {
|
||||
$reason = [$this->getTextForContentBan($content_type), $content_type];
|
||||
switch ($content_type) {
|
||||
case "post": $reason[] = (new Posts)->get($content_id); break;
|
||||
case "photo": $reason[] = (new Photos)->get($content_id); break;
|
||||
case "video": $reason[] = (new Videos)->get($content_id); break;
|
||||
case "group": $reason[] = (new Clubs)->get($content_id); break;
|
||||
case "comment": $reason[] = (new Comments)->get($content_id); break;
|
||||
case "note": $reason[] = (new Notes)->get($content_id); break;
|
||||
case "app": $reason[] = (new Applications)->get($content_id); break;
|
||||
case "user": $reason[] = (new Users)->get($content_id); break;
|
||||
default: $reason[] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $reason;
|
||||
}
|
||||
|
||||
function getBanInSupportReason(): ?string
|
||||
{
|
||||
return $this->getRecord()->block_in_support_reason;
|
||||
|
@ -299,10 +348,11 @@ class User extends RowModel
|
|||
return $this->getRecord()->marital_status;
|
||||
}
|
||||
|
||||
function getLocalizedMaritalStatus(): string
|
||||
function getLocalizedMaritalStatus(?bool $prefix = false): string
|
||||
{
|
||||
$status = $this->getMaritalStatus();
|
||||
$string = "relationship_$status";
|
||||
if ($prefix) $string .= "_prefix";
|
||||
if($this->isFemale()) {
|
||||
$res = tr($string . "_fem");
|
||||
if($res != ("@" . $string . "_fem"))
|
||||
|
@ -312,6 +362,17 @@ class User extends RowModel
|
|||
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
|
||||
{
|
||||
return $this->getRecord()->email_contact;
|
||||
|
@ -406,6 +467,7 @@ class User extends RowModel
|
|||
"length" => 1,
|
||||
"mappings" => [
|
||||
"photos",
|
||||
"audios",
|
||||
"videos",
|
||||
"messages",
|
||||
"notes",
|
||||
|
@ -413,6 +475,7 @@ class User extends RowModel
|
|||
"news",
|
||||
"links",
|
||||
"poster",
|
||||
"apps",
|
||||
],
|
||||
])->get($id);
|
||||
}
|
||||
|
@ -432,6 +495,7 @@ class User extends RowModel
|
|||
"friends.add",
|
||||
"wall.write",
|
||||
"messages.write",
|
||||
"audios.read",
|
||||
],
|
||||
])->get($id);
|
||||
}
|
||||
|
@ -670,8 +734,8 @@ class User extends RowModel
|
|||
|
||||
for($i = 0; $i < 10 - $this->get2faBackupCodeCount(); $i++) {
|
||||
$codes[] = [
|
||||
owner => $this->getId(),
|
||||
code => random_int(10000000, 99999999)
|
||||
"owner" => $this->getId(),
|
||||
"code" => random_int(10000000, 99999999)
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -833,7 +897,7 @@ class User extends RowModel
|
|||
]);
|
||||
}
|
||||
|
||||
function ban(string $reason, bool $deleteSubscriptions = true, ?int $unban_time = NULL): void
|
||||
function ban(string $reason, bool $deleteSubscriptions = true, $unban_time = NULL, ?int $initiator = NULL): void
|
||||
{
|
||||
if($deleteSubscriptions) {
|
||||
$subs = DatabaseConnection::i()->getContext()->table("subscriptions");
|
||||
|
@ -846,8 +910,33 @@ class User extends RowModel
|
|||
$subs->delete();
|
||||
}
|
||||
|
||||
$this->setBlock_Reason($reason);
|
||||
$this->setUnblock_time($unban_time);
|
||||
$iat = time();
|
||||
$ban = new Ban;
|
||||
$ban->setUser($this->getId());
|
||||
$ban->setReason($reason);
|
||||
$ban->setInitiator($initiator);
|
||||
$ban->setIat($iat);
|
||||
$ban->setExp($unban_time !== "permanent" ? $unban_time : 0);
|
||||
$ban->setTime($unban_time === "permanent" ? 0 : ($unban_time ? ($unban_time - $iat) : 0));
|
||||
$ban->save();
|
||||
|
||||
$this->setBlock_Reason($ban->getId());
|
||||
// $this->setUnblock_time($unban_time);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function unban(int $removed_by): void
|
||||
{
|
||||
$ban = (new Bans)->get((int) $this->getRawBanReason());
|
||||
if (!$ban || $ban->isOver())
|
||||
return;
|
||||
|
||||
$ban->setRemoved_Manually(true);
|
||||
$ban->setRemoved_By($removed_by);
|
||||
$ban->save();
|
||||
|
||||
$this->setBlock_Reason(NULL);
|
||||
// $user->setUnblock_time(NULL);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
|
@ -935,6 +1024,7 @@ class User extends RowModel
|
|||
"friends.add",
|
||||
"wall.write",
|
||||
"messages.write",
|
||||
"audios.read",
|
||||
],
|
||||
])->set($id, $status)->toInteger());
|
||||
}
|
||||
|
@ -945,6 +1035,7 @@ class User extends RowModel
|
|||
"length" => 1,
|
||||
"mappings" => [
|
||||
"photos",
|
||||
"audios",
|
||||
"videos",
|
||||
"messages",
|
||||
"notes",
|
||||
|
@ -952,6 +1043,7 @@ class User extends RowModel
|
|||
"news",
|
||||
"links",
|
||||
"poster",
|
||||
"apps",
|
||||
],
|
||||
])->set($id, (int) $status)->toInteger();
|
||||
|
||||
|
@ -1016,7 +1108,7 @@ class User extends RowModel
|
|||
{
|
||||
$this->setOnline(time());
|
||||
$this->setClient_name($platform);
|
||||
$this->save();
|
||||
$this->save(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1034,7 +1126,7 @@ class User extends RowModel
|
|||
|
||||
function adminNotify(string $message): bool
|
||||
{
|
||||
$admId = OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"];
|
||||
$admId = (int) OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"];
|
||||
if(!$admId)
|
||||
return false;
|
||||
else if(is_null($admin = (new Users)->get($admId)))
|
||||
|
@ -1099,7 +1191,11 @@ class User extends RowModel
|
|||
|
||||
function getUnbanTime(): ?string
|
||||
{
|
||||
return !is_null($this->getRecord()->unblock_time) ? date('d.m.Y', $this->getRecord()->unblock_time) : NULL;
|
||||
$ban = (new Bans)->get((int) $this->getRecord()->block_reason);
|
||||
if (!$ban || $ban->isOver() || $ban->isPermanent()) return null;
|
||||
if ($this->canUnbanThemself()) return tr("today");
|
||||
|
||||
return date('d.m.Y', $ban->getEndTime());
|
||||
}
|
||||
|
||||
function canUnbanThemself(): bool
|
||||
|
@ -1107,10 +1203,45 @@ class User extends RowModel
|
|||
if (!$this->isBanned())
|
||||
return false;
|
||||
|
||||
if ($this->getRecord()->unblock_time > time() || $this->getRecord()->unblock_time == 0)
|
||||
return false;
|
||||
$ban = (new Bans)->get((int) $this->getRecord()->block_reason);
|
||||
if (!$ban || $ban->isOver() || $ban->isPermanent()) return false;
|
||||
|
||||
return true;
|
||||
return $ban->getEndTime() <= time() && !$ban->isPermanent();
|
||||
}
|
||||
|
||||
function getNewBanTime()
|
||||
{
|
||||
$bans = iterator_to_array((new Bans)->getByUser($this->getid()));
|
||||
if (!$bans || count($bans) === 0)
|
||||
return 0;
|
||||
|
||||
$last_ban = end($bans);
|
||||
if (!$last_ban) return 0;
|
||||
|
||||
if ($last_ban->isPermanent()) return "permanent";
|
||||
|
||||
$values = [0, 3600, 7200, 86400, 172800, 604800, 1209600, 3024000, 9072000];
|
||||
$response = 0;
|
||||
$i = 0;
|
||||
|
||||
foreach ($values as $value) {
|
||||
$i++;
|
||||
if ($last_ban->getTime() === 0 && $value === 0) continue;
|
||||
if ($last_ban->getTime() < $value) {
|
||||
$response = $value;
|
||||
break;
|
||||
} else if ($last_ban->getTime() >= $value) {
|
||||
if ($i < count($values)) continue;
|
||||
$response = "permanent";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
function getRealId()
|
||||
{
|
||||
return $this->getId();
|
||||
}
|
||||
|
||||
function toVkApiStruct(): object
|
||||
|
@ -1129,7 +1260,47 @@ class User extends RowModel
|
|||
|
||||
return $res;
|
||||
}
|
||||
|
||||
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\TSubscribable;
|
||||
use Traits\TAudioStatuses;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
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 Nette\InvalidStateException as ISE;
|
||||
|
||||
|
@ -224,4 +224,23 @@ class Video extends Media
|
|||
|
||||
return $video;
|
||||
}
|
||||
|
||||
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,
|
||||
"id" => $id
|
||||
])->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);
|
||||
}
|
||||
}
|
33
Web/Models/Repositories/Bans.php
Normal file
33
Web/Models/Repositories/Bans.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use Chandler\Database\DatabaseConnection as DB;
|
||||
use Nette\Database\Table\{ActiveRow, Selection};
|
||||
use openvk\Web\Models\Entities\Ban;
|
||||
|
||||
class Bans
|
||||
{
|
||||
private $context;
|
||||
private $bans;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DB::i()->getContext();
|
||||
$this->bans = $this->context->table("bans");
|
||||
}
|
||||
|
||||
function toBan(?ActiveRow $ar): ?Ban
|
||||
{
|
||||
return is_null($ar) ? NULL : new Ban($ar);
|
||||
}
|
||||
|
||||
function get(int $id): ?Ban
|
||||
{
|
||||
return $this->toBan($this->bans->get($id));
|
||||
}
|
||||
|
||||
function getByUser(int $user_id): \Traversable
|
||||
{
|
||||
foreach ($this->bans->where("user", $user_id) as $ban)
|
||||
yield new Ban($ban);
|
||||
}
|
||||
}
|
|
@ -45,4 +45,9 @@ class ChandlerGroups
|
|||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,8 @@ class ChandlerUsers
|
|||
|
||||
function getById(string $UUID): ?ChandlerUser
|
||||
{
|
||||
return new ChandlerUser($this->users->where("id", $UUID)->fetch());
|
||||
$user = $this->users->where("id", $UUID)->fetch();
|
||||
return $user ? new ChandlerUser($user) : NULL;
|
||||
}
|
||||
|
||||
function getList(int $page = 1): \Traversable
|
||||
|
|
49
Web/Models/Repositories/CurrentUser.php
Normal file
49
Web/Models/Repositories/CurrentUser.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
|
||||
class CurrentUser
|
||||
{
|
||||
private static $instance = null;
|
||||
private $user;
|
||||
private $ip;
|
||||
private $useragent;
|
||||
|
||||
public function __construct(?User $user = NULL, ?string $ip = NULL, ?string $useragent = NULL)
|
||||
{
|
||||
if ($user)
|
||||
$this->user = $user;
|
||||
|
||||
if ($ip)
|
||||
$this->ip = $ip;
|
||||
|
||||
if ($useragent)
|
||||
$this->useragent = $useragent;
|
||||
}
|
||||
|
||||
public static function get($user, $ip, $useragent)
|
||||
{
|
||||
if (self::$instance === null) self::$instance = new self($user, $ip, $useragent);
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function getUser(): User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function getIP(): string
|
||||
{
|
||||
return $this->ip;
|
||||
}
|
||||
|
||||
public function getUserAgent(): string
|
||||
{
|
||||
return $this->useragent;
|
||||
}
|
||||
|
||||
public static function i()
|
||||
{
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
|
@ -42,4 +42,10 @@ class Gifts
|
|||
foreach($cats as $cat)
|
||||
yield new GiftCategory($cat);
|
||||
}
|
||||
|
||||
function getCategoriesCount(): int
|
||||
{
|
||||
$cats = $this->cats->where("deleted", false);
|
||||
return $cats->count();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class IPs
|
|||
if(!$res) {
|
||||
$res = new IP;
|
||||
$res->setIp($ip);
|
||||
$res->save();
|
||||
$res->save(false);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,6 @@ class Messages
|
|||
$query = file_get_contents(__DIR__ . "/../sql/get-correspondencies-count.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;
|
||||
bdump($count);
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
|
|
34
Web/Models/Repositories/NoSpamLogs.php
Normal file
34
Web/Models/Repositories/NoSpamLogs.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Entities\NoSpamLog;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
|
||||
class NoSpamLogs
|
||||
{
|
||||
private $context;
|
||||
private $noSpamLogs;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DatabaseConnection::i()->getContext();
|
||||
$this->noSpamLogs = $this->context->table("noSpam_templates");
|
||||
}
|
||||
|
||||
private function toNoSpamLog(?ActiveRow $ar): ?NoSpamLog
|
||||
{
|
||||
return is_null($ar) ? NULL : new NoSpamLog($ar);
|
||||
}
|
||||
|
||||
function get(int $id): ?NoSpamLog
|
||||
{
|
||||
return $this->toNoSpamLog($this->noSpamLogs->get($id));
|
||||
}
|
||||
|
||||
function getList(array $filter = []): \Traversable
|
||||
{
|
||||
foreach ($this->noSpamLogs->where($filter)->order("`id` DESC") as $log)
|
||||
yield new NoSpamLog($log);
|
||||
}
|
||||
}
|
|
@ -33,14 +33,26 @@ class Photos
|
|||
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([
|
||||
"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);
|
||||
}
|
||||
}
|
||||
|
||||
function getUserPhotosCount(User $user)
|
||||
{
|
||||
$photos = $this->photos->where([
|
||||
"owner" => $user->getId(),
|
||||
"deleted" => 0
|
||||
]);
|
||||
|
||||
return sizeof($photos);
|
||||
}
|
||||
}
|
||||
|
|
67
Web/Models/Repositories/Reports.php
Normal file
67
Web/Models/Repositories/Reports.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use openvk\Web\Models\Entities\Report;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
class Reports
|
||||
{
|
||||
private $context;
|
||||
private $reports;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DatabaseConnection::i()->getContext();
|
||||
$this->reports = $this->context->table("reports");
|
||||
}
|
||||
|
||||
private function toReport(?ActiveRow $ar): ?Report
|
||||
{
|
||||
return is_null($ar) ? NULL : new Report($ar);
|
||||
}
|
||||
|
||||
function getReports(int $state = 0, int $page = 1, ?string $type = NULL, ?bool $pagination = true): \Traversable
|
||||
{
|
||||
$filter = ["deleted" => 0];
|
||||
if ($type) $filter["type"] = $type;
|
||||
|
||||
$reports = $this->reports->where($filter)->order("created DESC")->group("target_id, type");
|
||||
if ($pagination)
|
||||
$reports = $reports->page($page, 15);
|
||||
|
||||
foreach($reports as $t)
|
||||
yield new Report($t);
|
||||
}
|
||||
|
||||
function getReportsCount(int $state = 0): int
|
||||
{
|
||||
return sizeof($this->reports->where(["deleted" => 0, "type" => $state])->group("target_id, type"));
|
||||
}
|
||||
|
||||
function get(int $id): ?Report
|
||||
{
|
||||
return $this->toReport($this->reports->get($id));
|
||||
}
|
||||
|
||||
function getByContentId(int $id): ?Report
|
||||
{
|
||||
$post = $this->reports->where(["deleted" => 0, "content_id" => $id])->fetch();
|
||||
|
||||
if($post)
|
||||
return new Report($post);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
function getDuplicates(string $type, int $target_id, ?int $orig = NULL, ?int $user_id = NULL): \Traversable
|
||||
{
|
||||
$filter = ["deleted" => 0, "type" => $type, "target_id" => $target_id];
|
||||
if ($orig) $filter[] = "id != $orig";
|
||||
if ($user_id) $filter["user_id"] = $user_id;
|
||||
|
||||
foreach ($this->reports->where($filter) as $report)
|
||||
yield new Report($report);
|
||||
}
|
||||
|
||||
use \Nette\SmartObject;
|
||||
}
|
|
@ -44,9 +44,9 @@ class Users
|
|||
return $alias->getUser();
|
||||
}
|
||||
|
||||
function getByChandlerUser(ChandlerUser $user): ?User
|
||||
function getByChandlerUser(?ChandlerUser $user): ?User
|
||||
{
|
||||
return $this->toUser($this->users->where("user", $user->getId())->fetch());
|
||||
return $user ? $this->toUser($this->users->where("user", $user->getId())->fetch()) : NULL;
|
||||
}
|
||||
|
||||
function find(string $query, array $pars = [], string $sort = "id DESC"): Util\EntityStream
|
||||
|
|
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"
|
||||
. "User-Agent: *\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: /invite\n"
|
||||
. "Disallow: /groups_create\n"
|
||||
|
|
|
@ -1,7 +1,21 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use Chandler\Database\Log;
|
||||
use Chandler\Database\Logs;
|
||||
use openvk\Web\Models\Entities\{Voucher, Gift, GiftCategory, User, BannedLink};
|
||||
use openvk\Web\Models\Repositories\{ChandlerGroups, ChandlerUsers, Users, Clubs, 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;
|
||||
|
||||
final class AdminPresenter extends OpenVKPresenter
|
||||
|
@ -12,8 +26,10 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
private $gifts;
|
||||
private $bannedLinks;
|
||||
private $chandlerGroups;
|
||||
private $audios;
|
||||
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->clubs = $clubs;
|
||||
|
@ -21,7 +37,9 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$this->gifts = $gifts;
|
||||
$this->bannedLinks = $bannedLinks;
|
||||
$this->chandlerGroups = $chandlerGroups;
|
||||
|
||||
$this->audios = $audios;
|
||||
$this->logs = DatabaseConnection::i()->getContext()->table("ChandlerLogs");
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
|
@ -39,6 +57,15 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$count = $repo->find($query)->size();
|
||||
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
|
||||
{
|
||||
|
@ -83,8 +110,10 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
if($user->onlineStatus() != $this->postParam("online")) $user->setOnline(intval($this->postParam("online")));
|
||||
$user->setVerified(empty($this->postParam("verify") ? 0 : 1));
|
||||
if($this->postParam("add-to-group")) {
|
||||
$query = "INSERT INTO `ChandlerACLRelations` (`user`, `group`) VALUES ('" . $user->getChandlerGUID() . "', '" . $this->postParam("add-to-group") . "')";
|
||||
DatabaseConnection::i()->getConnection()->query($query);
|
||||
if (!(new ChandlerGroups)->isUserAMember($user->getChandlerGUID(), $this->postParam("add-to-group"))) {
|
||||
$query = "INSERT INTO `ChandlerACLRelations` (`user`, `group`) VALUES ('" . $user->getChandlerGUID() . "', '" . $this->postParam("add-to-group") . "')";
|
||||
DatabaseConnection::i()->getConnection()->query($query);
|
||||
}
|
||||
}
|
||||
if($this->postParam("password")) {
|
||||
$user->getChandlerUser()->updatePassword($this->postParam("password"));
|
||||
|
@ -128,7 +157,8 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$club->save();
|
||||
break;
|
||||
case "ban":
|
||||
$club->setBlock_reason($this->postParam("ban_reason"));
|
||||
$reason = mb_strlen(trim($this->postParam("ban_reason"))) > 0 ? $this->postParam("ban_reason") : NULL;
|
||||
$club->setBlock_reason($reason);
|
||||
$club->save();
|
||||
break;
|
||||
}
|
||||
|
@ -278,7 +308,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$this->notFound();
|
||||
|
||||
$gift->delete();
|
||||
$this->flashFail("succ", "Gift moved successfully", "This gift will now be in <b>Recycle Bin</b>.");
|
||||
$this->flashFail("succ", tr("admin_gift_moved_successfully"), tr("admin_gift_moved_to_recycle"));
|
||||
break;
|
||||
case "copy":
|
||||
case "move":
|
||||
|
@ -297,7 +327,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$catTo->addGift($gift);
|
||||
|
||||
$name = $catTo->getName();
|
||||
$this->flash("succ", "Gift moved successfully", "This gift will now be in <b>$name</b>.");
|
||||
$this->flash("succ", tr("admin_gift_moved_successfully"), "This gift will now be in <b>$name</b>.");
|
||||
$this->redirect("/admin/gifts/" . $catTo->getSlug() . "." . $catTo->getId() . "/");
|
||||
break;
|
||||
default:
|
||||
|
@ -328,10 +358,10 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$gift->setUsages((int) $this->postParam("usages"));
|
||||
if(isset($_FILES["pic"]) && $_FILES["pic"]["error"] === UPLOAD_ERR_OK) {
|
||||
if(!$gift->setImage($_FILES["pic"]["tmp_name"]))
|
||||
$this->flashFail("err", "Не удалось сохранить подарок", "Изображение подарка кривое.");
|
||||
$this->flashFail("err", tr("error_when_saving_gift"), tr("error_when_saving_gift_bad_image"));
|
||||
} else if($gen) {
|
||||
# If there's no gift pic but it's newly created
|
||||
$this->flashFail("err", "Не удалось сохранить подарок", "Пожалуйста, загрузите изображение подарка.");
|
||||
$this->flashFail("err", tr("error_when_saving_gift"), tr("error_when_saving_gift_no_image"));
|
||||
}
|
||||
|
||||
$gift->save();
|
||||
|
@ -355,13 +385,19 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
{
|
||||
$this->assertNoCSRF();
|
||||
|
||||
$unban_time = strtotime($this->queryParam("date")) ?: NULL;
|
||||
if (str_contains($this->queryParam("reason"), "*"))
|
||||
exit(json_encode([ "error" => "Incorrect reason" ]));
|
||||
|
||||
$unban_time = strtotime($this->queryParam("date")) ?: "permanent";
|
||||
|
||||
$user = $this->users->get($id);
|
||||
if(!$user)
|
||||
exit(json_encode([ "error" => "User does not exist" ]));
|
||||
|
||||
$user->ban($this->queryParam("reason"), true, $unban_time);
|
||||
|
||||
if ($this->queryParam("incr"))
|
||||
$unban_time = time() + $user->getNewBanTime();
|
||||
|
||||
$user->ban($this->queryParam("reason"), true, $unban_time, $this->user->identity->getId());
|
||||
exit(json_encode([ "success" => true, "reason" => $this->queryParam("reason") ]));
|
||||
}
|
||||
|
||||
|
@ -372,9 +408,17 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$user = $this->users->get($id);
|
||||
if(!$user)
|
||||
exit(json_encode([ "error" => "User does not exist" ]));
|
||||
|
||||
|
||||
$ban = (new Bans)->get((int)$user->getRawBanReason());
|
||||
if (!$ban || $ban->isOver())
|
||||
exit(json_encode([ "error" => "User is not banned" ]));
|
||||
|
||||
$ban->setRemoved_Manually(true);
|
||||
$ban->setRemoved_By($this->user->identity->getId());
|
||||
$ban->save();
|
||||
|
||||
$user->setBlock_Reason(NULL);
|
||||
$user->setUnblock_time(NULL);
|
||||
// $user->setUnblock_time(NULL);
|
||||
$user->save();
|
||||
exit(json_encode([ "success" => true ]));
|
||||
}
|
||||
|
@ -460,6 +504,14 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$this->redirect("/admin/bannedLinks");
|
||||
}
|
||||
|
||||
function renderBansHistory(int $user_id) :void
|
||||
{
|
||||
$user = (new Users)->get($user_id);
|
||||
if (!$user) $this->notFound();
|
||||
|
||||
$this->template->bans = (new Bans)->getByUser($user_id);
|
||||
}
|
||||
|
||||
function renderChandlerGroups(): void
|
||||
{
|
||||
$this->template->groups = (new ChandlerGroups)->getList();
|
||||
|
@ -550,4 +602,86 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
|
||||
$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
|
||||
{
|
||||
$filter = [];
|
||||
|
||||
if ($this->queryParam("id")) {
|
||||
$id = (int) $this->queryParam("id");
|
||||
$filter["id"] = $id;
|
||||
$this->template->id = $id;
|
||||
}
|
||||
if ($this->queryParam("type") !== NULL && $this->queryParam("type") !== "any") {
|
||||
$type = in_array($this->queryParam("type"), [0, 1, 2, 3]) ? (int) $this->queryParam("type") : 0;
|
||||
$filter["type"] = $type;
|
||||
$this->template->type = $type;
|
||||
}
|
||||
if ($this->queryParam("uid")) {
|
||||
$user = $this->queryParam("uid");
|
||||
$filter["user"] = $user;
|
||||
$this->template->user = $user;
|
||||
}
|
||||
if ($this->queryParam("obj_id")) {
|
||||
$obj_id = (int) $this->queryParam("obj_id");
|
||||
$filter["object_id"] = $obj_id;
|
||||
$this->template->obj_id = $obj_id;
|
||||
}
|
||||
if ($this->queryParam("obj_type") !== NULL && $this->queryParam("obj_type") !== "any") {
|
||||
$obj_type = "openvk\\Web\\Models\\Entities\\" . $this->queryParam("obj_type");
|
||||
$filter["object_model"] = $obj_type;
|
||||
$this->template->obj_type = $obj_type;
|
||||
}
|
||||
|
||||
$this->template->logs = (new Logs)->search($filter);
|
||||
$this->template->object_types = (new Logs)->getTypes();
|
||||
}
|
||||
}
|
||||
|
|
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);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{IP, User, PasswordReset, EmailVerification};
|
||||
use openvk\Web\Models\Repositories\{IPs, Users, Restores, Verifications};
|
||||
use openvk\Web\Models\Repositories\{Bans, IPs, Users, Restores, Verifications};
|
||||
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||
use openvk\Web\Util\Validator;
|
||||
use Chandler\Session\Session;
|
||||
|
@ -110,7 +110,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", tr("failed_to_register"), tr("user_already_exists"));
|
||||
|
||||
$user->setUser($chUser->getId());
|
||||
$user->save();
|
||||
$user->save(false);
|
||||
|
||||
if(!is_null($referer)) {
|
||||
$user->toggleSubscription($referer);
|
||||
|
@ -131,6 +131,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
|
||||
$this->authenticator->authenticate($chUser->getId());
|
||||
$this->redirect("/id" . $user->getId());
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,9 +346,16 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$user = $this->users->get($this->user->id);
|
||||
$ban = (new Bans)->get((int)$user->getRawBanReason());
|
||||
if (!$ban || $ban->isOver() || $ban->isPermanent())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$ban->setRemoved_Manually(2);
|
||||
$ban->setRemoved_By($this->user->identity->getId());
|
||||
$ban->save();
|
||||
|
||||
$user->setBlock_Reason(NULL);
|
||||
$user->setUnblock_Time(NULL);
|
||||
// $user->setUnblock_Time(NULL);
|
||||
$user->save();
|
||||
|
||||
$this->flashFail("succ", tr("banned_unban_title"), tr("banned_unban_description"));
|
||||
|
|
|
@ -3,6 +3,8 @@ namespace openvk\Web\Presenters;
|
|||
|
||||
final class BlobPresenter extends OpenVKPresenter
|
||||
{
|
||||
protected $banTolerant = true;
|
||||
|
||||
private function getDirName($dir): string
|
||||
{
|
||||
if(gettype($dir) === "integer") {
|
||||
|
@ -16,6 +18,8 @@ final class BlobPresenter extends OpenVKPresenter
|
|||
|
||||
function renderFile(/*string*/ $dir, string $name, string $format)
|
||||
{
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
|
||||
$dir = $this->getDirName($dir);
|
||||
$base = realpath(OPENVK_ROOT . "/storage/$dir");
|
||||
$path = realpath(OPENVK_ROOT . "/storage/$dir/$name.$format");
|
||||
|
@ -35,5 +39,5 @@ final class BlobPresenter extends OpenVKPresenter
|
|||
|
||||
readfile($path);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{Comment, Notifications\MentionNotification, Photo, Video, User, Topic, Post};
|
||||
use openvk\Web\Models\Entities\Notifications\CommentNotification;
|
||||
use openvk\Web\Models\Repositories\{Comments, Clubs};
|
||||
use openvk\Web\Models\Repositories\{Comments, Clubs, Videos, Photos, Audios};
|
||||
|
||||
final class CommentPresenter extends OpenVKPresenter
|
||||
{
|
||||
|
@ -22,6 +22,9 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
|
||||
$comment = (new Comments)->get($id);
|
||||
if(!$comment || $comment->isDeleted()) $this->notFound();
|
||||
|
||||
if ($comment->getTarget() instanceof Post && $comment->getTarget()->getWallOwner()->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
if(!is_null($this->user)) $comment->toggleLike($this->user->identity);
|
||||
|
||||
|
@ -48,9 +51,9 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
else if($entity instanceof Topic)
|
||||
$club = $entity->getClub();
|
||||
|
||||
if($_FILES["_vid_attachment"] && OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
|
||||
$this->flashFail("err", tr("error"), "Video uploads are disabled by the system administrator.");
|
||||
|
||||
if ($entity instanceof Post && $entity->getWallOwner()->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$flags = 0;
|
||||
if($this->postParam("as_group") === "on" && !is_null($club) && $club->canBeModifiedBy($this->user->identity))
|
||||
$flags |= 0b10000000;
|
||||
|
@ -60,31 +63,68 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
try {
|
||||
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"]);
|
||||
} catch(ISE $ex) {
|
||||
$this->flashFail("err", "Не удалось опубликовать пост", "Файл изображения повреждён, слишком велик или одна сторона изображения в разы больше другой.");
|
||||
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_when_publishing_comment_description"));
|
||||
}
|
||||
}
|
||||
|
||||
# TODO move to trait
|
||||
try {
|
||||
$photo = NULL;
|
||||
$video = NULL;
|
||||
if($_FILES["_pic_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||
$album = NULL;
|
||||
if($wall > 0 && $wall === $this->user->id)
|
||||
$album = (new Albums)->getUserWallAlbum($wallOwner);
|
||||
|
||||
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album);
|
||||
$photos = [];
|
||||
if(!empty($this->postParam("photos"))) {
|
||||
$un = rtrim($this->postParam("photos"), ",");
|
||||
$arr = explode(",", $un);
|
||||
|
||||
if(sizeof($arr) < 11) {
|
||||
foreach($arr as $dat) {
|
||||
$ids = explode("_", $dat);
|
||||
$photo = (new Photos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
|
||||
|
||||
if(!$photo || $photo->isDeleted())
|
||||
continue;
|
||||
|
||||
$photos[] = $photo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$videos = [];
|
||||
|
||||
if(!empty($this->postParam("videos"))) {
|
||||
$un = rtrim($this->postParam("videos"), ",");
|
||||
$arr = explode(",", $un);
|
||||
|
||||
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||
$video = Video::fastMake($this->user->id, $_FILES["_vid_attachment"]["name"], $this->postParam("text"), $_FILES["_vid_attachment"]);
|
||||
if(sizeof($arr) < 11) {
|
||||
foreach($arr as $dat) {
|
||||
$ids = explode("_", $dat);
|
||||
$video = (new Videos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
|
||||
|
||||
if(!$video || $video->isDeleted())
|
||||
continue;
|
||||
|
||||
$videos[] = $video;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
} catch(ISE $ex) {
|
||||
$this->flashFail("err", "Не удалось опубликовать комментарий", "Файл медиаконтента повреждён или слишком велик.");
|
||||
}
|
||||
|
||||
if(empty($this->postParam("text")) && !$photo && !$video)
|
||||
$this->flashFail("err", "Не удалось опубликовать комментарий", "Комментарий пустой или слишком большой.");
|
||||
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"));
|
||||
|
||||
try {
|
||||
$comment = new Comment;
|
||||
|
@ -96,14 +136,18 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
$comment->setFlags($flags);
|
||||
$comment->save();
|
||||
} catch (\LengthException $ex) {
|
||||
$this->flashFail("err", "Не удалось опубликовать комментарий", "Комментарий слишком большой.");
|
||||
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_too_big"));
|
||||
}
|
||||
|
||||
if(!is_null($photo))
|
||||
$comment->attach($photo);
|
||||
foreach($photos as $photo)
|
||||
$comment->attach($photo);
|
||||
|
||||
if(!is_null($video))
|
||||
$comment->attach($video);
|
||||
if(sizeof($videos) > 0)
|
||||
foreach($videos as $vid)
|
||||
$comment->attach($vid);
|
||||
|
||||
foreach($audios as $audio)
|
||||
$comment->attach($audio);
|
||||
|
||||
if($entity->getOwner()->getId() !== $this->user->identity->getId())
|
||||
if(($owner = $entity->getOwner()) instanceof User)
|
||||
|
@ -118,7 +162,7 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
if($mentionee instanceof User)
|
||||
(new MentionNotification($mentionee, $entity, $comment->getOwner(), strip_tags($comment->getText())))->emit();
|
||||
|
||||
$this->flashFail("succ", "Комментарий добавлен", "Ваш комментарий появится на странице.");
|
||||
$this->flashFail("succ", tr("comment_is_added"), tr("comment_is_added_desc"));
|
||||
}
|
||||
|
||||
function renderDeleteComment(int $id): void
|
||||
|
@ -129,13 +173,15 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
$comment = (new Comments)->get($id);
|
||||
if(!$comment) $this->notFound();
|
||||
if(!$comment->canBeDeletedBy($this->user->identity))
|
||||
$this->throwError(403, "Forbidden", "У вас недостаточно прав чтобы редактировать этот ресурс.");
|
||||
|
||||
$this->throwError(403, "Forbidden", tr("error_access_denied"));
|
||||
if ($comment->getTarget() instanceof Post && $comment->getTarget()->getWallOwner()->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$comment->delete();
|
||||
$this->flashFail(
|
||||
"succ",
|
||||
"Успешно",
|
||||
"Этот комментарий больше не будет показыватся.<br/><a href='/al_comments/spam?$id'>Отметить как спам</a>?"
|
||||
tr("success"),
|
||||
tr("comment_will_not_appear")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
|
||||
$this->template->user = $user;
|
||||
$this->template->iterator = $cats;
|
||||
$this->template->count = $this->gifts->getCategoriesCount();
|
||||
$this->template->_template = "Gifts/Menu.xml";
|
||||
}
|
||||
|
||||
|
@ -49,7 +50,7 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
$user = $this->users->get((int) ($this->queryParam("user") ?? 0));
|
||||
$cat = $this->gifts->getCat((int) ($this->queryParam("pack") ?? 0));
|
||||
if(!$user || !$cat)
|
||||
$this->flashFail("err", "Не удалось подарить", "Пользователь или набор не существуют.");
|
||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_user_not_exists"));
|
||||
|
||||
$this->template->page = $page = (int) ($this->queryParam("p") ?? 1);
|
||||
$gifts = $cat->getGifts($page, null, $this->template->count);
|
||||
|
@ -66,14 +67,14 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
$gift = $this->gifts->get((int) ($this->queryParam("elid") ?? 0));
|
||||
$cat = $this->gifts->getCat((int) ($this->queryParam("pack") ?? 0));
|
||||
if(!$user || !$cat || !$gift || !$cat->hasGift($gift))
|
||||
$this->flashFail("err", "Не удалось подарить", "Не удалось подтвердить права на подарок.");
|
||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_no_rights_gifts"));
|
||||
|
||||
if(!$gift->canUse($this->user->identity))
|
||||
$this->flashFail("err", "Не удалось подарить", "У вас больше не осталось таких подарков.");
|
||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_no_more_gifts"));
|
||||
|
||||
$coinsLeft = $this->user->identity->getCoins() - $gift->getPrice();
|
||||
if($coinsLeft < 0)
|
||||
$this->flashFail("err", "Не удалось подарить", "Ору нищ не пук.");
|
||||
$this->flashFail("err", tr("error_when_gifting"), tr("error_no_money"));
|
||||
|
||||
$this->template->_template = "Gifts/Confirm.xml";
|
||||
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||
|
@ -91,7 +92,7 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
$user->gift($this->user->identity, $gift, $comment, !is_null($this->postParam("anonymous")));
|
||||
$gift->used();
|
||||
|
||||
$this->flash("succ", "Подарок отправлен", "Вы отправили подарок <b>" . $user->getFirstName() . "</b> за " . $gift->getPrice() . " голосов.");
|
||||
$this->flash("succ", tr("gift_sent"), tr("gift_sent_desc", $user->getFirstName(), $gift->getPrice()));
|
||||
$this->redirect($user->getURL());
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace openvk\Web\Presenters;
|
|||
use openvk\Web\Models\Entities\{Club, Photo, Post};
|
||||
use Nette\InvalidStateException;
|
||||
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};
|
||||
use Chandler\Security\Authenticator;
|
||||
|
||||
final class GroupPresenter extends OpenVKPresenter
|
||||
|
@ -24,10 +24,16 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
if(!$club) {
|
||||
$this->notFound();
|
||||
} else {
|
||||
$this->template->albums = (new Albums)->getClubAlbums($club, 1, 3);
|
||||
$this->template->albumsCount = (new Albums)->getClubAlbumsCount($club);
|
||||
$this->template->topics = (new Topics)->getLastTopics($club, 3);
|
||||
$this->template->topicsCount = (new Topics)->getClubTopicsCount($club);
|
||||
if ($club->isBanned()) {
|
||||
$this->template->_template = "Group/Banned.xml";
|
||||
} else {
|
||||
$this->template->albums = (new Albums)->getClubAlbums($club, 1, 3);
|
||||
$this->template->albumsCount = (new Albums)->getClubAlbumsCount($club);
|
||||
$this->template->topics = (new Topics)->getLastTopics($club, 3);
|
||||
$this->template->topicsCount = (new Topics)->getClubTopicsCount($club);
|
||||
$this->template->audios = (new Audios)->getRandomThreeAudiosByEntityId($club->getRealId());
|
||||
$this->template->audiosCount = (new Audios)->getClubCollectionSize($club);
|
||||
}
|
||||
|
||||
$this->template->club = $club;
|
||||
}
|
||||
|
@ -39,7 +45,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->willExecuteWriteAction();
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!empty($this->postParam("name")))
|
||||
if(!empty($this->postParam("name")) && mb_strlen(trim($this->postParam("name"))) > 0)
|
||||
{
|
||||
$club = new Club;
|
||||
$club->setName($this->postParam("name"));
|
||||
|
@ -50,7 +56,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$club->save();
|
||||
} catch(\PDOException $ex) {
|
||||
if($ex->getCode() == 23000)
|
||||
$this->flashFail("err", "Ошибка", "Произошла ошибка на стороне сервера. Обратитесь к системному администратору.");
|
||||
$this->flashFail("err", tr("error"), tr("error_on_server_side"));
|
||||
else
|
||||
throw $ex;
|
||||
}
|
||||
|
@ -58,7 +64,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$club->toggleSubscription($this->user->identity);
|
||||
$this->redirect("/club" . $club->getId());
|
||||
}else{
|
||||
$this->flashFail("err", "Ошибка", "Вы не ввели название группы.");
|
||||
$this->flashFail("err", tr("error"), tr("error_no_group_name"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +78,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
|
||||
$club = $this->clubs->get((int) $this->postParam("id"));
|
||||
if(!$club) exit("Invalid state");
|
||||
if ($club->isBanned()) $this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$club->toggleSubscription($this->user->identity);
|
||||
|
||||
|
@ -83,6 +90,8 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->assertUserLoggedIn();
|
||||
|
||||
$this->template->club = $this->clubs->get($id);
|
||||
if ($this->template->club->isBanned()) $this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$this->template->onlyShowManagers = $this->queryParam("onlyAdmins") == "1";
|
||||
if($this->template->onlyShowManagers) {
|
||||
$this->template->followers = NULL;
|
||||
|
@ -118,12 +127,14 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->badRequest();
|
||||
|
||||
$club = $this->clubs->get($id);
|
||||
if ($club->isBanned()) $this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$user = (new Users)->get((int) $user);
|
||||
if(!$user || !$club)
|
||||
$this->notFound();
|
||||
|
||||
if(!$club->canBeModifiedBy($this->user->identity ?? NULL))
|
||||
$this->flashFail("err", "Ошибка доступа", "У вас недостаточно прав, чтобы изменять этот ресурс.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if(!is_null($hidden)) {
|
||||
if($club->getOwner()->getId() == $user->getId()) {
|
||||
|
@ -141,9 +152,9 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
if($hidden) {
|
||||
$this->flashFail("succ", "Операция успешна", "Теперь " . $user->getCanonicalName() . " будет показываться как обычный подписчик всем кроме других администраторов");
|
||||
$this->flashFail("succ", tr("success_action"), tr("x_is_now_hidden", $user->getCanonicalName()));
|
||||
} else {
|
||||
$this->flashFail("succ", "Операция успешна", "Теперь все будут знать про то что " . $user->getCanonicalName() . " - администратор");
|
||||
$this->flashFail("succ", tr("success_action"), tr("x_is_now_showed", $user->getCanonicalName()));
|
||||
}
|
||||
} elseif($removeComment) {
|
||||
if($club->getOwner()->getId() == $user->getId()) {
|
||||
|
@ -155,11 +166,11 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$manager->save();
|
||||
}
|
||||
|
||||
$this->flashFail("succ", "Операция успешна", "Комментарий к администратору удален");
|
||||
$this->flashFail("succ", tr("success_action"), tr("comment_is_deleted"));
|
||||
} elseif($comment) {
|
||||
if(mb_strlen($comment) > 36) {
|
||||
$commentLength = (string) mb_strlen($comment);
|
||||
$this->flashFail("err", "Ошибка", "Комментарий слишком длинный ($commentLength символов вместо 36 символов)");
|
||||
$this->flashFail("err", tr("error"), tr("comment_is_too_long", $commentLength));
|
||||
}
|
||||
|
||||
if($club->getOwner()->getId() == $user->getId()) {
|
||||
|
@ -171,16 +182,16 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$manager->save();
|
||||
}
|
||||
|
||||
$this->flashFail("succ", "Операция успешна", "Комментарий к администратору изменён");
|
||||
$this->flashFail("succ", tr("success_action"), tr("comment_is_changed"));
|
||||
}else{
|
||||
if($club->canBeModifiedBy($user)) {
|
||||
$club->removeManager($user);
|
||||
$this->flashFail("succ", "Операция успешна", $user->getCanonicalName() . " более не администратор.");
|
||||
$this->flashFail("succ", tr("success_action"), tr("x_no_more_admin", $user->getCanonicalName()));
|
||||
} else {
|
||||
$club->addManager($user);
|
||||
|
||||
(new ClubModeratorNotification($user, $club, $this->user->identity))->emit();
|
||||
$this->flashFail("succ", "Операция успешна", $user->getCanonicalName() . " назначен(а) администратором.");
|
||||
$this->flashFail("succ", tr("success_action"), tr("x_is_admin", $user->getCanonicalName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,6 +205,8 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$club = $this->clubs->get($id);
|
||||
if(!$club || !$club->canBeModifiedBy($this->user->identity))
|
||||
$this->notFound();
|
||||
else if ($club->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
else
|
||||
$this->template->club = $club;
|
||||
|
||||
|
@ -201,12 +214,13 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
if(!$club->setShortcode( empty($this->postParam("shortcode")) ? NULL : $this->postParam("shortcode") ))
|
||||
$this->flashFail("err", tr("error"), tr("error_shorturl_incorrect"));
|
||||
|
||||
$club->setName(empty($this->postParam("name")) ? $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->setWall(empty($this->postParam("wall")) ? 0 : 1);
|
||||
$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->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);
|
||||
|
||||
$website = $this->postParam("website") ?? "";
|
||||
|
@ -234,7 +248,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
(new Albums)->getClubAvatarAlbum($club)->addPhoto($photo);
|
||||
} catch(ISE $ex) {
|
||||
$name = $album->getName();
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию.");
|
||||
$this->flashFail("err", tr("error"), tr("error_when_uploading_photo"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,12 +256,12 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$club->save();
|
||||
} catch(\PDOException $ex) {
|
||||
if($ex->getCode() == 23000)
|
||||
$this->flashFail("err", "Ошибка", "Произошла ошибка на стороне сервера. Обратитесь к системному администратору.");
|
||||
$this->flashFail("err", tr("error"), tr("error_on_server_side"));
|
||||
else
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
$this->flash("succ", "Изменения сохранены", "Новые данные появятся в вашей группе.");
|
||||
$this->flash("succ", tr("changes_saved"), tr("new_changes_desc"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,6 +269,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
{
|
||||
$photo = new Photo;
|
||||
$club = $this->clubs->get($id);
|
||||
if ($club->isBanned()) $this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST" && $_FILES["ava"]["error"] === UPLOAD_ERR_OK) {
|
||||
try {
|
||||
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
|
||||
|
@ -286,7 +301,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
|
||||
} catch(ISE $ex) {
|
||||
$name = $album->getName();
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию.");
|
||||
$this->flashFail("err", tr("error"), tr("error_when_uploading_photo"));
|
||||
}
|
||||
}
|
||||
$this->returnJson([
|
||||
|
@ -338,11 +353,13 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->assertUserLoggedIn();
|
||||
|
||||
if(!eventdb())
|
||||
$this->flashFail("err", "Ошибка подключения", "Не удалось подключится к службе телеметрии.");
|
||||
$this->flashFail("err", tr("connection_error"), tr("connection_error_desc"));
|
||||
|
||||
$club = $this->clubs->get($id);
|
||||
if(!$club->canBeModifiedBy($this->user->identity))
|
||||
$this->notFound();
|
||||
else if ($club->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
else
|
||||
$this->template->club = $club;
|
||||
|
||||
|
@ -375,6 +392,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", tr("error"), tr("incorrect_password"));
|
||||
|
||||
$club = $this->clubs->get($id);
|
||||
if ($club->isBanned()) $this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
$newOwner = (new Users)->get($newOwnerId);
|
||||
if($this->user->id !== $club->getOwner()->getId())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Repositories\{Posts, Comments};
|
||||
use MessagePack\MessagePack;
|
||||
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);
|
||||
} 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 = [];
|
||||
$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();
|
||||
|
||||
header("Content-Type: application/json");
|
||||
|
|
384
Web/Presenters/NoSpamPresenter.php
Normal file
384
Web/Presenters/NoSpamPresenter.php
Normal file
|
@ -0,0 +1,384 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace openvk\Web\Presenters;
|
||||
|
||||
use Nette\Database\DriverException;
|
||||
use Nette\Utils\Finder;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
use openvk\Web\Models\Entities\Comment;
|
||||
use Chandler\Database\Log;
|
||||
use openvk\Web\Models\Entities\NoSpamLog;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\ChandlerUsers;
|
||||
use Chandler\Database\Logs;
|
||||
use openvk\Web\Models\Repositories\NoSpamLogs;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
|
||||
final class NoSpamPresenter extends OpenVKPresenter
|
||||
{
|
||||
protected $banTolerant = true;
|
||||
protected $deactivationTolerant = true;
|
||||
protected $presenterName = "nospam";
|
||||
|
||||
const ENTITIES_NAMESPACE = "openvk\\Web\\Models\\Entities";
|
||||
|
||||
function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
function renderIndex(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0);
|
||||
|
||||
$targetDir = __DIR__ . '/../Models/Entities/';
|
||||
$mode = in_array($this->queryParam("act"), ["form", "templates", "rollback", "reports"]) ? $this->queryParam("act") : "form";
|
||||
|
||||
if ($mode === "form") {
|
||||
$this->template->_template = "NoSpam/Index";
|
||||
$foundClasses = [];
|
||||
foreach (Finder::findFiles('*.php')->from($targetDir) as $file) {
|
||||
$content = file_get_contents($file->getPathname());
|
||||
$namespacePattern = '/namespace\s+([^\s;]+)/';
|
||||
$classPattern = '/class\s+([^\s{]+)/';
|
||||
preg_match($namespacePattern, $content, $namespaceMatches);
|
||||
preg_match($classPattern, $content, $classMatches);
|
||||
|
||||
if (isset($namespaceMatches[1]) && isset($classMatches[1])) {
|
||||
$classNamespace = trim($namespaceMatches[1]);
|
||||
$className = trim($classMatches[1]);
|
||||
$fullClassName = $classNamespace . '\\' . $className;
|
||||
|
||||
if ($classNamespace === NoSpamPresenter::ENTITIES_NAMESPACE && class_exists($fullClassName)) {
|
||||
$foundClasses[] = $className;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$models = [];
|
||||
|
||||
foreach ($foundClasses as $class) {
|
||||
$r = new \ReflectionClass(NoSpamPresenter::ENTITIES_NAMESPACE . "\\$class");
|
||||
if (!$r->isAbstract() && $r->getName() !== NoSpamPresenter::ENTITIES_NAMESPACE . "\\Correspondence")
|
||||
$models[] = $class;
|
||||
}
|
||||
$this->template->models = $models;
|
||||
} else if ($mode === "templates") {
|
||||
$this->template->_template = "NoSpam/Templates.xml";
|
||||
$filter = [];
|
||||
if ($this->queryParam("id")) {
|
||||
$filter["id"] = (int)$this->queryParam("id");
|
||||
}
|
||||
$this->template->templates = iterator_to_array((new NoSpamLogs)->getList($filter));
|
||||
} else if ($mode === "reports") {
|
||||
$this->redirect("/scumfeed");
|
||||
} else {
|
||||
$template = (new NoSpamLogs)->get((int)$this->postParam("id"));
|
||||
if (!$template || $template->isRollbacked())
|
||||
$this->returnJson(["success" => false, "error" => "Шаблон не найден"]);
|
||||
|
||||
$model = NoSpamPresenter::ENTITIES_NAMESPACE . "\\" . $template->getModel();
|
||||
$items = $template->getItems();
|
||||
if (count($items) > 0) {
|
||||
$db = DatabaseConnection::i()->getContext();
|
||||
|
||||
$unbanned_ids = [];
|
||||
foreach ($items as $_item) {
|
||||
try {
|
||||
$item = new $model;
|
||||
$table_name = $item->getTableName();
|
||||
$item = $db->table($table_name)->get((int)$_item);
|
||||
if (!$item) continue;
|
||||
|
||||
$item = new $model($item);
|
||||
|
||||
if (key_exists("deleted", $item->unwrap()) && $item->isDeleted()) {
|
||||
$item->setDeleted(0);
|
||||
$item->save();
|
||||
}
|
||||
|
||||
if (in_array($template->getTypeRaw(), [2, 3])) {
|
||||
$owner = NULL;
|
||||
$methods = ["getOwner", "getUser", "getRecipient", "getInitiator"];
|
||||
|
||||
if (method_exists($item, "ban")) {
|
||||
$owner = $item;
|
||||
} else {
|
||||
foreach ($methods as $method) {
|
||||
if (method_exists($item, $method)) {
|
||||
$owner = $item->$method();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$_id = ($owner instanceof Club ? $owner->getId() * -1 : $owner->getId());
|
||||
|
||||
if (!in_array($_id, $unbanned_ids)) {
|
||||
$owner->unban($this->user->id);
|
||||
$unbanned_ids[] = $_id;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->returnJson(["success" => false, "error" => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->returnJson(["success" => false, "error" => "Объекты не найдены"]);
|
||||
}
|
||||
|
||||
$template->setRollback(true);
|
||||
$template->save();
|
||||
|
||||
$this->returnJson(["success" => true]);
|
||||
}
|
||||
}
|
||||
|
||||
function renderSearch(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0);
|
||||
$this->assertNoCSRF();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
function searchByAdditionalParams(?string $table = NULL, ?string $where = NULL, ?string $ip = NULL, ?string $useragent = NULL, ?int $ts = NULL, ?int $te = NULL, $user = NULL)
|
||||
{
|
||||
$db = DatabaseConnection::i()->getContext();
|
||||
if ($table && ($ip || $useragent || $ts || $te || $user)) {
|
||||
$conditions = [];
|
||||
|
||||
if ($ip) $conditions[] = "`ip` REGEXP '$ip'";
|
||||
if ($useragent) $conditions[] = "`useragent` REGEXP '$useragent'";
|
||||
if ($ts) $conditions[] = "`ts` < $ts";
|
||||
if ($te) $conditions[] = "`ts` > $te";
|
||||
if ($user) {
|
||||
$users = new Users;
|
||||
|
||||
$_user = $users->getByChandlerUser((new ChandlerUsers)->getById($user))
|
||||
?? $users->get((int)$user)
|
||||
?? $users->getByAddress($user)
|
||||
?? NULL;
|
||||
|
||||
if ($_user) {
|
||||
$conditions[] = "`user` = '" . $_user->getChandlerGUID() . "'";
|
||||
}
|
||||
}
|
||||
|
||||
$whereStart = "WHERE `object_table` = '$table'";
|
||||
if ($table === "profiles") {
|
||||
$whereStart .= "AND `type` = 0";
|
||||
}
|
||||
|
||||
$conditions = count($conditions) > 0 ? "AND (" . implode(" AND ", $conditions) . ")" : "";
|
||||
$response = [];
|
||||
|
||||
if ($conditions) {
|
||||
$logs = $db->query("SELECT * FROM `ChandlerLogs` $whereStart $conditions GROUP BY `object_id`, `object_model`");
|
||||
|
||||
foreach ($logs as $log) {
|
||||
$log = (new Logs)->get($log->id);
|
||||
$object = $log->getObject()->unwrap();
|
||||
|
||||
if (!$object) continue;
|
||||
if ($where) {
|
||||
if (str_starts_with($where, " AND")) {
|
||||
$where = substr_replace($where, "", 0, strlen(" AND"));
|
||||
}
|
||||
|
||||
$a = $db->query("SELECT * FROM `$table` WHERE $where")->fetchAll();
|
||||
foreach ($a as $o) {
|
||||
if ($object->id == $o["id"]) {
|
||||
$response[] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$response[] = $object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$response = [];
|
||||
$processed = 0;
|
||||
|
||||
$where = $this->postParam("where");
|
||||
$ip = addslashes($this->postParam("ip"));
|
||||
$useragent = addslashes($this->postParam("useragent"));
|
||||
$searchTerm = addslashes($this->postParam("q"));
|
||||
$ts = (int)$this->postParam("ts");
|
||||
$te = (int)$this->postParam("te");
|
||||
$user = addslashes($this->postParam("user"));
|
||||
|
||||
if ($where) {
|
||||
$where = explode(";", $where)[0];
|
||||
}
|
||||
|
||||
if (!$ip && !$useragent && !$searchTerm && !$ts && !$te && !$where && !$searchTerm && !$user)
|
||||
$this->returnJson(["success" => false, "error" => "Нет запроса. Заполните поле \"подстрока\" или введите запрос \"WHERE\" в поле под ним."]);
|
||||
|
||||
$models = explode(",", $this->postParam("models"));
|
||||
|
||||
foreach ($models as $_model) {
|
||||
$model_name = NoSpamPresenter::ENTITIES_NAMESPACE . "\\" . $_model;
|
||||
if (!class_exists($model_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$model = new $model_name;
|
||||
|
||||
$c = new \ReflectionClass($model_name);
|
||||
if ($c->isAbstract() || $c->getName() == NoSpamPresenter::ENTITIES_NAMESPACE . "\\Correspondence") {
|
||||
continue;
|
||||
}
|
||||
|
||||
$db = DatabaseConnection::i()->getContext();
|
||||
$table = $model->getTableName();
|
||||
$columns = $db->getStructure()->getColumns($table);
|
||||
|
||||
if ($searchTerm) {
|
||||
$conditions = [];
|
||||
$need_deleted = false;
|
||||
foreach ($columns as $column) {
|
||||
if ($column["name"] == "deleted") {
|
||||
$need_deleted = true;
|
||||
} else {
|
||||
$conditions[] = "`$column[name]` REGEXP '$searchTerm'";
|
||||
}
|
||||
}
|
||||
$conditions = implode(" OR ", $conditions);
|
||||
|
||||
$where = ($this->postParam("where") ? " AND ($conditions)" : "($conditions)");
|
||||
if ($need_deleted) $where .= " AND (`deleted` = 0)";
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
|
||||
if (str_starts_with($where, " AND")) {
|
||||
if ($searchTerm && !$this->postParam("where")) {
|
||||
$where = substr_replace($where, "", 0, strlen(" AND"));
|
||||
} else {
|
||||
$where = "(" . $this->postParam("where") . ")" . $where;
|
||||
}
|
||||
}
|
||||
|
||||
if ($ip || $useragent || $ts || $te || $user) {
|
||||
$rows = searchByAdditionalParams($table, $where, $ip, $useragent, $ts, $te, $user);
|
||||
} else {
|
||||
if (!$where) {
|
||||
$rows = [];
|
||||
} else {
|
||||
$result = $db->query("SELECT * FROM `$table` WHERE $where");
|
||||
$rows = $result->fetchAll();
|
||||
}
|
||||
}
|
||||
|
||||
if (!in_array((int)$this->postParam("ban"), [1, 2, 3])) {
|
||||
foreach ($rows as $key => $object) {
|
||||
$object = (array)$object;
|
||||
$_obj = [];
|
||||
foreach ($object as $key => $value) {
|
||||
foreach ($columns as $column) {
|
||||
if ($column["name"] === $key && in_array(strtoupper($column["nativetype"]), ["BLOB", "BINARY", "VARBINARY", "TINYBLOB", "MEDIUMBLOB", "LONGBLOB"])) {
|
||||
$value = "[BINARY]";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$_obj[$key] = $value;
|
||||
$_obj["__model_name"] = $_model;
|
||||
}
|
||||
$response[] = $_obj;
|
||||
}
|
||||
} else {
|
||||
$ids = [];
|
||||
|
||||
foreach ($rows as $object) {
|
||||
$object = new $model_name($db->table($table)->get($object->id));
|
||||
if (!$object) continue;
|
||||
$ids[] = $object->getId();
|
||||
}
|
||||
|
||||
$log = new NoSpamLog;
|
||||
$log->setUser($this->user->id);
|
||||
$log->setModel($_model);
|
||||
if ($searchTerm) {
|
||||
$log->setRegex($searchTerm);
|
||||
} else {
|
||||
$log->setRequest($where);
|
||||
}
|
||||
$log->setBan_Type((int)$this->postParam("ban"));
|
||||
$log->setCount(count($rows));
|
||||
$log->setTime(time());
|
||||
$log->setItems(implode(",", $ids));
|
||||
$log->save();
|
||||
|
||||
$banned_ids = [];
|
||||
foreach ($rows as $object) {
|
||||
$object = new $model_name($db->table($table)->get($object->id));
|
||||
if (!$object) continue;
|
||||
|
||||
$owner = NULL;
|
||||
$methods = ["getOwner", "getUser", "getRecipient", "getInitiator"];
|
||||
|
||||
if (method_exists($object, "ban")) {
|
||||
$owner = $object;
|
||||
} else {
|
||||
foreach ($methods as $method) {
|
||||
if (method_exists($object, $method)) {
|
||||
$owner = $object->$method();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($owner instanceof User && $owner->getId() === $this->user->id) {
|
||||
if (count($rows) === 1) {
|
||||
$this->returnJson(["success" => false, "error" => "\"Производственная травма\" — Вы не можете блокировать или удалять свой же контент"]);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array((int)$this->postParam("ban"), [2, 3])) {
|
||||
$reason = mb_strlen(trim($this->postParam("ban_reason"))) > 0 ? addslashes($this->postParam("ban_reason")) : ("**content-noSpamTemplate-" . $log->getId() . "**");
|
||||
$is_forever = (string)$this->postParam("is_forever") === "true";
|
||||
$unban_time = $is_forever ? 0 : (int)$this->postParam("unban_time") ?? NULL;
|
||||
|
||||
if ($owner) {
|
||||
$_id = ($owner instanceof Club ? $owner->getId() * -1 : $owner->getId());
|
||||
if (!in_array($_id, $banned_ids)) {
|
||||
if ($owner instanceof User) {
|
||||
if (!$unban_time && !$is_forever)
|
||||
$unban_time = time() + $owner->getNewBanTime();
|
||||
|
||||
$owner->ban($reason, false, $unban_time, $this->user->id);
|
||||
} else {
|
||||
$owner->ban("Подозрительная активность");
|
||||
}
|
||||
|
||||
$banned_ids[] = $_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array((int)$this->postParam("ban"), [1, 3]))
|
||||
$object->delete();
|
||||
}
|
||||
|
||||
$processed++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->returnJson(["success" => true, "processed" => $processed, "count" => count($response), "list" => $response]);
|
||||
} catch (\Throwable $e) {
|
||||
$this->returnJson(["success" => false, "error" => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -107,7 +107,7 @@ final class NotesPresenter extends OpenVKPresenter
|
|||
if(!$note || $note->getOwner()->getId() !== $owner || $note->isDeleted())
|
||||
$this->notFound();
|
||||
if(is_null($this->user) || !$note->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
$this->template->note = $note;
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
|
@ -135,11 +135,11 @@ final class NotesPresenter extends OpenVKPresenter
|
|||
if(!$note) $this->notFound();
|
||||
if($note->getOwner()->getId() . "_" . $note->getId() !== $owner . "_" . $id || $note->isDeleted()) $this->notFound();
|
||||
if(is_null($this->user) || !$note->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
$name = $note->getName();
|
||||
$note->delete();
|
||||
$this->flash("succ", "Заметка удалена", "Заметка \"$name\" была успешно удалена.");
|
||||
$this->flash("succ", tr("note_is_deleted"), tr("note_x_is_now_deleted", $name));
|
||||
$this->redirect("/notes" . $this->user->id);
|
||||
}
|
||||
}
|
||||
|
|
12
Web/Presenters/OpenVKPresenter.php
Executable file → Normal file
12
Web/Presenters/OpenVKPresenter.php
Executable file → Normal file
|
@ -7,7 +7,7 @@ use Chandler\Security\Authenticator;
|
|||
use Latte\Engine as TemplatingEngine;
|
||||
use openvk\Web\Models\Entities\IP;
|
||||
use openvk\Web\Themes\Themepacks;
|
||||
use openvk\Web\Models\Repositories\{IPs, Users, APITokens, Tickets};
|
||||
use openvk\Web\Models\Repositories\{IPs, Users, APITokens, Tickets, Reports, CurrentUser};
|
||||
use WhichBrowser;
|
||||
|
||||
abstract class OpenVKPresenter extends SimplePresenter
|
||||
|
@ -198,6 +198,9 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
{
|
||||
$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->isTimezoned = Session::i()->get("_timezoneOffset");
|
||||
|
||||
|
@ -211,6 +214,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
$this->user->id = $this->user->identity->getId();
|
||||
$this->template->thisUser = $this->user->identity;
|
||||
$this->template->userTainted = $user->isTainted();
|
||||
CurrentUser::get($this->user->identity, $_SERVER["REMOTE_ADDR"], $_SERVER["HTTP_USER_AGENT"]);
|
||||
|
||||
if($this->user->identity->isDeleted() && !$this->deactivationTolerant) {
|
||||
if($this->user->identity->isDeactivated()) {
|
||||
|
@ -255,12 +259,14 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
if($this->user->identity->onlineStatus() == 0 && !($this->user->identity->isDeleted() || $this->user->identity->isBanned())) {
|
||||
$this->user->identity->setOnline(time());
|
||||
$this->user->identity->setClient_name(NULL);
|
||||
$this->user->identity->save();
|
||||
$this->user->identity->save(false);
|
||||
}
|
||||
|
||||
$this->template->ticketAnsweredCount = (new Tickets)->getTicketsCountByUserId($this->user->id, 1);
|
||||
if($user->can("write")->model("openvk\Web\Models\Entities\TicketReply")->whichBelongsTo(0))
|
||||
if($user->can("write")->model("openvk\Web\Models\Entities\TicketReply")->whichBelongsTo(0)) {
|
||||
$this->template->helpdeskTicketNotAnsweredCount = (new Tickets)->getTicketCount(0);
|
||||
$this->template->reportNotAnsweredCount = (new Reports)->getReportsCount(0);
|
||||
}
|
||||
}
|
||||
|
||||
header("X-OpenVK-User-Validated: $userValidated");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{Club, Photo, Album};
|
||||
use openvk\Web\Models\Entities\{Club, Photo, Album, User};
|
||||
use openvk\Web\Models\Repositories\{Photos, Albums, Users, Clubs};
|
||||
use Nette\InvalidStateException as ISE;
|
||||
|
||||
|
@ -27,7 +27,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if(!$user) $this->notFound();
|
||||
if (!$user->getPrivacyPermission('photos.read', $this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
$this->template->albums = $this->albums->getUserAlbums($user, $this->queryParam("p") ?? 1);
|
||||
$this->template->albums = $this->albums->getUserAlbums($user, (int)($this->queryParam("p") ?? 1));
|
||||
$this->template->count = $this->albums->getUserAlbumsCount($user);
|
||||
$this->template->owner = $user;
|
||||
$this->template->canEdit = false;
|
||||
|
@ -36,7 +36,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
} else {
|
||||
$club = (new Clubs)->get(abs($owner));
|
||||
if(!$club) $this->notFound();
|
||||
$this->template->albums = $this->albums->getClubAlbums($club, $this->queryParam("p") ?? 1);
|
||||
$this->template->albums = $this->albums->getClubAlbums($club, (int)($this->queryParam("p") ?? 1));
|
||||
$this->template->count = $this->albums->getClubAlbumsCount($club);
|
||||
$this->template->owner = $club;
|
||||
$this->template->canEdit = false;
|
||||
|
@ -46,7 +46,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
|
||||
$this->template->paginatorConf = (object) [
|
||||
"count" => $this->template->count,
|
||||
"page" => $this->queryParam("p") ?? 1,
|
||||
"page" => (int)($this->queryParam("p") ?? 1),
|
||||
"amount" => NULL,
|
||||
"perPage" => OPENVK_DEFAULT_PER_PAGE,
|
||||
];
|
||||
|
@ -66,7 +66,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(empty($this->postParam("name")))
|
||||
if(empty($this->postParam("name")) || mb_strlen(trim($this->postParam("name"))) === 0)
|
||||
$this->flashFail("err", tr("error"), tr("error_segmentation"));
|
||||
else if(strlen($this->postParam("name")) > 36)
|
||||
$this->flashFail("err", tr("error"), tr("error_data_too_big", "name", 36, "bytes"));
|
||||
|
@ -94,19 +94,19 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if(!$album) $this->notFound();
|
||||
if($album->getPrettyId() !== $owner . "_" . $id || $album->isDeleted()) $this->notFound();
|
||||
if(is_null($this->user) || !$album->canBeModifiedBy($this->user->identity) || $album->isDeleted())
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
$this->template->album = $album;
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(strlen($this->postParam("name")) > 36)
|
||||
$this->flashFail("err", tr("error"), tr("error_data_too_big", "name", 36, "bytes"));
|
||||
|
||||
$album->setName(empty($this->postParam("name")) ? $album->getName() : $this->postParam("name"));
|
||||
$album->setName((empty($this->postParam("name")) || mb_strlen(trim($this->postParam("name"))) === 0) ? $album->getName() : $this->postParam("name"));
|
||||
$album->setDescription(empty($this->postParam("desc")) ? NULL : $this->postParam("desc"));
|
||||
$album->setEdited(time());
|
||||
$album->save();
|
||||
|
||||
$this->flash("succ", "Изменения сохранены", "Новые данные приняты.");
|
||||
$this->flash("succ", tr("changes_saved"), tr("new_data_accepted"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,13 +120,13 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if(!$album) $this->notFound();
|
||||
if($album->getPrettyId() !== $owner . "_" . $id || $album->isDeleted()) $this->notFound();
|
||||
if(is_null($this->user) || !$album->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
$name = $album->getName();
|
||||
$owner = $album->getOwner();
|
||||
$album->delete();
|
||||
|
||||
$this->flash("succ", "Альбом удалён", "Альбом $name был успешно удалён.");
|
||||
$this->flash("succ", tr("album_is_deleted"), tr("album_x_is_deleted", $name));
|
||||
$this->redirect("/albums" . ($owner instanceof Club ? "-" : "") . $owner->getId());
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$this->template->photos = iterator_to_array( $album->getPhotos( (int) ($this->queryParam("p") ?? 1), 20) );
|
||||
$this->template->paginatorConf = (object) [
|
||||
"count" => $album->getPhotosCount(),
|
||||
"page" => $this->queryParam("p") ?? 1,
|
||||
"page" => (int)($this->queryParam("p") ?? 1),
|
||||
"amount" => sizeof($this->template->photos),
|
||||
"perPage" => 20,
|
||||
"atBottom" => true
|
||||
|
@ -205,13 +205,13 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$photo = $this->photos->getByOwnerAndVID($ownerId, $photoId);
|
||||
if(!$photo) $this->notFound();
|
||||
if(is_null($this->user) || $this->user->id != $ownerId)
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$photo->setDescription(empty($this->postParam("desc")) ? NULL : $this->postParam("desc"));
|
||||
$photo->save();
|
||||
|
||||
$this->flash("succ", "Изменения сохранены", "Обновлённое описание появится на странице с фоткой.");
|
||||
$this->flash("succ", tr("changes_saved"), tr("new_description_will_appear"));
|
||||
$this->redirect("/photo" . $photo->getPrettyId());
|
||||
}
|
||||
|
||||
|
@ -221,39 +221,82 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
function renderUploadPhoto(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(is_null($this->queryParam("album")))
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию в <b>DELETED</b>.");
|
||||
|
||||
[$owner, $id] = explode("_", $this->queryParam("album"));
|
||||
$album = $this->albums->get((int) $id);
|
||||
$this->willExecuteWriteAction(true);
|
||||
|
||||
if(is_null($this->queryParam("album"))) {
|
||||
$album = $this->albums->getUserWallAlbum($this->user->identity);
|
||||
} else {
|
||||
[$owner, $id] = explode("_", $this->queryParam("album"));
|
||||
$album = $this->albums->get((int) $id);
|
||||
}
|
||||
|
||||
if(!$album)
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию в <b>DELETED</b>.");
|
||||
if(is_null($this->user) || !$album->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error"), tr("error_adding_to_deleted"), 500, true);
|
||||
|
||||
# Для быстрой загрузки фоток из пикера фотографий нужен альбом, но юзер не может загружать фото
|
||||
# в системные альбомы, так что так.
|
||||
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);
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!isset($_FILES["blob"]))
|
||||
$this->flashFail("err", "Нету фотографии", "Выберите файл.");
|
||||
|
||||
try {
|
||||
$photo = new Photo;
|
||||
$photo->setOwner($this->user->id);
|
||||
$photo->setDescription($this->postParam("desc"));
|
||||
$photo->setFile($_FILES["blob"]);
|
||||
$photo->setCreated(time());
|
||||
$photo->save();
|
||||
} catch(ISE $ex) {
|
||||
$name = $album->getName();
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию в <b>$name</b>.");
|
||||
}
|
||||
|
||||
$album->addPhoto($photo);
|
||||
$album->setEdited(time());
|
||||
$album->save();
|
||||
if($this->queryParam("act") == "finish") {
|
||||
$result = json_decode($this->postParam("photos"), true);
|
||||
|
||||
foreach($result as $photoId => $description) {
|
||||
$phot = $this->photos->get($photoId);
|
||||
|
||||
$this->redirect("/photo" . $photo->getPrettyId() . "?from=album" . $album->getId());
|
||||
if(!$phot || $phot->isDeleted() || $phot->getOwner()->getId() != $this->user->id)
|
||||
continue;
|
||||
|
||||
if(iconv_strlen($description) > 255)
|
||||
$this->flashFail("err", tr("error"), tr("description_too_long"), 500, true);
|
||||
|
||||
$phot->setDescription($description);
|
||||
$phot->save();
|
||||
|
||||
$album = $phot->getAlbum();
|
||||
}
|
||||
|
||||
$this->returnJson(["success" => true,
|
||||
"album" => $album->getId(),
|
||||
"owner" => $album->getOwner() instanceof User ? $album->getOwner()->getId() : $album->getOwner()->getId() * -1]);
|
||||
}
|
||||
|
||||
if(!isset($_FILES))
|
||||
$this->flashFail("err", tr("no_photo"), tr("select_file"), 500, true);
|
||||
|
||||
$photos = [];
|
||||
if((int)$this->postParam("count") > 10)
|
||||
$this->flashFail("err", tr("no_photo"), "ты еблан", 500, true);
|
||||
|
||||
for($i = 0; $i < $this->postParam("count"); $i++) {
|
||||
try {
|
||||
$photo = new Photo;
|
||||
$photo->setOwner($this->user->id);
|
||||
$photo->setDescription("");
|
||||
$photo->setFile($_FILES["photo_".$i]);
|
||||
$photo->setCreated(time());
|
||||
$photo->save();
|
||||
|
||||
$photos[] = [
|
||||
"url" => $photo->getURLBySizeId("tiny"),
|
||||
"id" => $photo->getId(),
|
||||
"vid" => $photo->getVirtualId(),
|
||||
"owner" => $photo->getOwner()->getId(),
|
||||
"link" => $photo->getURL()
|
||||
];
|
||||
} catch(ISE $ex) {
|
||||
$name = $album->getName();
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию в $name.", 500, true);
|
||||
}
|
||||
|
||||
$album->addPhoto($photo);
|
||||
$album->setEdited(time());
|
||||
$album->save();
|
||||
}
|
||||
|
||||
$this->returnJson(["success" => true,
|
||||
"photos" => $photos]);
|
||||
} else {
|
||||
$this->template->album = $album;
|
||||
}
|
||||
|
@ -269,7 +312,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if(!$album || !$photo) $this->notFound();
|
||||
if(!$album->hasPhoto($photo)) $this->notFound();
|
||||
if(is_null($this->user) || !$album->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$this->assertNoCSRF();
|
||||
|
@ -277,7 +320,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$album->setEdited(time());
|
||||
$album->save();
|
||||
|
||||
$this->flash("succ", "Фотография удалена", "Эта фотография была успешно удалена.");
|
||||
$this->flash("succ", tr("photo_is_deleted"), tr("photo_is_deleted_desc"));
|
||||
$this->redirect("/album" . $album->getPrettyId());
|
||||
}
|
||||
}
|
||||
|
@ -285,18 +328,26 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
function renderDeletePhoto(int $ownerId, int $photoId): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
$this->willExecuteWriteAction($_SERVER["REQUEST_METHOD"] === "POST");
|
||||
$this->assertNoCSRF();
|
||||
|
||||
$photo = $this->photos->getByOwnerAndVID($ownerId, $photoId);
|
||||
if(!$photo) $this->notFound();
|
||||
if(is_null($this->user) || $this->user->id != $ownerId)
|
||||
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
|
||||
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if(!is_null($album = $photo->getAlbum()))
|
||||
$redirect = $album->getOwner() instanceof User ? "/id0" : "/club" . $ownerId;
|
||||
else
|
||||
$redirect = "/id0";
|
||||
|
||||
$photo->isolate();
|
||||
$photo->delete();
|
||||
|
||||
$this->flash("succ", "Фотография удалена", "Эта фотография была успешно удалена.");
|
||||
$this->redirect("/id0");
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST")
|
||||
$this->returnJson(["success" => true]);
|
||||
|
||||
$this->flash("succ", tr("photo_is_deleted"), tr("photo_is_deleted_desc"));
|
||||
$this->redirect($redirect);
|
||||
}
|
||||
}
|
||||
|
|
151
Web/Presenters/ReportPresenter.php
Normal file
151
Web/Presenters/ReportPresenter.php
Normal file
|
@ -0,0 +1,151 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\Repositories\Reports;
|
||||
use openvk\Web\Models\Repositories\Posts;
|
||||
use openvk\Web\Models\Entities\Report;
|
||||
|
||||
final class ReportPresenter extends OpenVKPresenter
|
||||
{
|
||||
private $reports;
|
||||
|
||||
function __construct(Reports $reports)
|
||||
{
|
||||
$this->reports = $reports;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
function renderList(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0);
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST")
|
||||
$this->assertNoCSRF();
|
||||
|
||||
$act = in_array($this->queryParam("act"), ["post", "photo", "video", "group", "comment", "note", "app", "user", "audio"]) ? $this->queryParam("act") : NULL;
|
||||
|
||||
if (!$this->queryParam("orig")) {
|
||||
$this->template->reports = $this->reports->getReports(0, (int)($this->queryParam("p") ?? 1), $act, $_SERVER["REQUEST_METHOD"] !== "POST");
|
||||
$this->template->count = $this->reports->getReportsCount();
|
||||
} else {
|
||||
$orig = $this->reports->get((int) $this->queryParam("orig"));
|
||||
if (!$orig) $this->redirect("/scumfeed");
|
||||
|
||||
$this->template->reports = $orig->getDuplicates();
|
||||
$this->template->count = $orig->getDuplicatesCount();
|
||||
$this->template->orig = $orig->getId();
|
||||
}
|
||||
$this->template->paginatorConf = (object) [
|
||||
"count" => $this->template->count,
|
||||
"page" => $this->queryParam("p") ?? 1,
|
||||
"amount" => NULL,
|
||||
"perPage" => 15,
|
||||
];
|
||||
$this->template->mode = $act ?? "all";
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$reports = [];
|
||||
foreach ($this->reports->getReports(0, 0, $act, false) as $report) {
|
||||
$reports[] = [
|
||||
"id" => $report->getId(),
|
||||
"author" => [
|
||||
"id" => $report->getReportAuthor()->getId(),
|
||||
"url" => $report->getReportAuthor()->getURL(),
|
||||
"name" => $report->getReportAuthor()->getCanonicalName(),
|
||||
"is_female" => $report->getReportAuthor()->isFemale()
|
||||
],
|
||||
"content" => [
|
||||
"name" => $report->getContentName(),
|
||||
"type" => $report->getContentType(),
|
||||
"id" => $report->getContentId(),
|
||||
"url" => $report->getContentType() === "user" ? (new Users)->get((int) $report->getContentId())->getURL() : NULL
|
||||
],
|
||||
"duplicates" => $report->getDuplicatesCount(),
|
||||
];
|
||||
}
|
||||
$this->returnJson(["reports" => $reports]);
|
||||
}
|
||||
}
|
||||
|
||||
function renderView(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0);
|
||||
|
||||
$report = $this->reports->get($id);
|
||||
if(!$report || $report->isDeleted())
|
||||
$this->notFound();
|
||||
|
||||
$this->template->report = $report;
|
||||
}
|
||||
|
||||
function renderCreate(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(!$id)
|
||||
exit(json_encode([ "error" => tr("error_segmentation") ]));
|
||||
|
||||
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) {
|
||||
$report = new Report;
|
||||
$report->setUser_id($this->user->id);
|
||||
$report->setTarget_id($id);
|
||||
$report->setType($this->queryParam("type"));
|
||||
$report->setReason($this->queryParam("reason"));
|
||||
$report->setCreated(time());
|
||||
$report->save();
|
||||
}
|
||||
|
||||
exit(json_encode([ "reason" => $this->queryParam("reason") ]));
|
||||
} else {
|
||||
exit(json_encode([ "error" => "Unable to submit a report on this content type" ]));
|
||||
}
|
||||
}
|
||||
|
||||
function renderAction(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
$this->assertPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0);
|
||||
|
||||
$report = $this->reports->get($id);
|
||||
if(!$report || $report->isDeleted()) $this->notFound();
|
||||
|
||||
if ($this->postParam("ban")) {
|
||||
$report->deleteContent();
|
||||
$report->banUser($this->user->identity->getId());
|
||||
|
||||
$this->flash("suc", tr("death"), tr("user_successfully_banned"));
|
||||
} else if ($this->postParam("delete")) {
|
||||
$report->deleteContent();
|
||||
|
||||
$this->flash("suc", tr("nehay"), tr("content_is_deleted"));
|
||||
} else if ($this->postParam("ignore")) {
|
||||
$report->delete();
|
||||
|
||||
$this->flash("suc", tr("nehay"), tr("report_is_ignored"));
|
||||
} else if ($this->postParam("banClubOwner") || $this->postParam("banClub")) {
|
||||
if ($report->getContentType() !== "group")
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
$club = $report->getContentObject();
|
||||
if (!$club || $club->isBanned())
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"));
|
||||
|
||||
if ($this->postParam("banClubOwner")) {
|
||||
$club->getOwner()->ban("**content-" . $report->getContentType() . "-" . $report->getContentId() . "**", false, $club->getOwner()->getNewBanTime(), $this->user->identity->getId());
|
||||
} else {
|
||||
$club->ban("**content-" . $report->getContentType() . "-" . $report->getContentId() . "**");
|
||||
}
|
||||
|
||||
$report->delete();
|
||||
|
||||
$this->flash("suc", tr("death"), ($this->postParam("banClubOwner") ? tr("group_owner_is_banned") : tr("group_is_banned")));
|
||||
}
|
||||
|
||||
$this->redirect("/scumfeed");
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
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;
|
||||
|
||||
final class SearchPresenter extends OpenVKPresenter
|
||||
|
@ -13,6 +13,7 @@ final class SearchPresenter extends OpenVKPresenter
|
|||
private $videos;
|
||||
private $apps;
|
||||
private $notes;
|
||||
private $audios;
|
||||
|
||||
function __construct(Users $users, Clubs $clubs)
|
||||
{
|
||||
|
@ -23,22 +24,21 @@ final class SearchPresenter extends OpenVKPresenter
|
|||
$this->videos = new Videos;
|
||||
$this->apps = new Applications;
|
||||
$this->notes = new Notes;
|
||||
$this->audios = new Audios;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
function renderIndex(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$query = $this->queryParam("query") ?? "";
|
||||
$type = $this->queryParam("type") ?? "users";
|
||||
$sorter = $this->queryParam("sort") ?? "id";
|
||||
$invert = $this->queryParam("invert") == 1 ? "ASC" : "DESC";
|
||||
$page = (int) ($this->queryParam("p") ?? 1);
|
||||
|
||||
$this->willExecuteWriteAction();
|
||||
if($query != "")
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
# https://youtu.be/pSAWM5YuXx8
|
||||
|
||||
$repos = [
|
||||
|
@ -47,7 +47,7 @@ final class SearchPresenter extends OpenVKPresenter
|
|||
"posts" => "posts",
|
||||
"comments" => "comments",
|
||||
"videos" => "videos",
|
||||
"audios" => "posts",
|
||||
"audios" => "audios",
|
||||
"apps" => "apps",
|
||||
"notes" => "notes"
|
||||
];
|
||||
|
@ -62,7 +62,17 @@ final class SearchPresenter extends OpenVKPresenter
|
|||
break;
|
||||
case "rating":
|
||||
$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 = [
|
||||
|
@ -86,18 +96,21 @@ final class SearchPresenter extends OpenVKPresenter
|
|||
"hometown" => $this->queryParam("hometown") != "" ? $this->queryParam("hometown") : NULL,
|
||||
"before" => $this->queryParam("datebefore") != "" ? strtotime($this->queryParam("datebefore")) : 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,
|
||||
"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.");
|
||||
|
||||
$results = $this->{$repo}->find($query, $parameters, $sort);
|
||||
$iterator = $results->page($page);
|
||||
$iterator = $results->page($page, 14);
|
||||
$count = $results->size();
|
||||
|
||||
$this->template->iterator = iterator_to_array($iterator);
|
||||
$this->template->count = $count;
|
||||
$this->template->type = $type;
|
||||
$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);
|
||||
if($this->template->mode === "list") {
|
||||
$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")
|
||||
|
@ -385,7 +385,7 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
$agent->setNumerate((int) $this->postParam("number") ?? NULL);
|
||||
$agent->setIcon($this->postParam("avatar"));
|
||||
$agent->save();
|
||||
$this->flashFail("succ", "Успех", "Профиль отредактирован.");
|
||||
$this->flashFail("succ", tr("agent_profile_edited"));
|
||||
} else {
|
||||
$agent = new SupportAgent;
|
||||
$agent->setAgent($this->user->identity->getId());
|
||||
|
@ -393,7 +393,27 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
$agent->setNumerate((int) $this->postParam("number") ?? NULL);
|
||||
$agent->setIcon($this->postParam("avatar"));
|
||||
$agent->save();
|
||||
$this->flashFail("succ", "Успех", "Профиль создан. Теперь пользователи видят Ваши псевдоним и аватарку вместо стандартных аватарки и номера.");
|
||||
$this->flashFail("succ", tr("agent_profile_created_1"), tr("agent_profile_created_2"));
|
||||
}
|
||||
}
|
||||
|
||||
function renderCloseTicket(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertNoCSRF();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$ticket = $this->tickets->get($id);
|
||||
|
||||
if($ticket->isDeleted() === 1 || $ticket->getType() === 2 || $ticket->getUserId() !== $this->user->id) {
|
||||
header("HTTP/1.1 403 Forbidden");
|
||||
header("Location: /support/view/" . $id);
|
||||
exit;
|
||||
}
|
||||
|
||||
$ticket->setType(2);
|
||||
$ticket->save();
|
||||
|
||||
$this->flashFail("succ", tr("ticket_changed"), tr("ticket_changed_comment"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ final class TopicsPresenter extends OpenVKPresenter
|
|||
$video = Video::fastMake($this->user->id, $_FILES["_vid_attachment"]["name"], $this->postParam("text"), $_FILES["_vid_attachment"]);
|
||||
}
|
||||
} catch(ISE $ex) {
|
||||
$this->flash("err", "Не удалось опубликовать комментарий", "Файл медиаконтента повреждён или слишком велик.");
|
||||
$this->flash("err", tr("error_when_publishing_comment"), tr("error_comment_file_too_big"));
|
||||
$this->redirect("/topic" . $topic->getPrettyId());
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,7 @@ final class TopicsPresenter extends OpenVKPresenter
|
|||
$comment->setFlags($flags);
|
||||
$comment->save();
|
||||
} catch (\LengthException $ex) {
|
||||
$this->flash("err", "Не удалось опубликовать комментарий", "Комментарий слишком большой.");
|
||||
$this->flash("err", tr("error_when_publishing_comment"), tr("error_comment_too_big"));
|
||||
$this->redirect("/topic" . $topic->getPrettyId());
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use openvk\Web\Util\Sms;
|
|||
use openvk\Web\Themes\Themepacks;
|
||||
use openvk\Web\Models\Entities\{Photo, Post, EmailChangeVerification};
|
||||
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\Util\Validator;
|
||||
use Chandler\Security\Authenticator;
|
||||
|
@ -45,7 +45,10 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$this->template->videosCount = (new Videos)->getUserVideosCount($user);
|
||||
$this->template->notes = (new Notes)->getUserNotes($user, 1, 4);
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +58,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$this->assertUserLoggedIn();
|
||||
|
||||
$user = $this->users->get($id);
|
||||
$page = abs($this->queryParam("p") ?? 1);
|
||||
$page = abs((int)($this->queryParam("p") ?? 1));
|
||||
if(!$user)
|
||||
$this->notFound();
|
||||
elseif (!$user->getPrivacyPermission('friends.read', $this->user->identity ?? NULL))
|
||||
|
@ -72,7 +75,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
if(!is_null($this->user)) {
|
||||
if($this->template->mode !== "friends" && $this->user->id !== $id) {
|
||||
$name = $user->getFullName();
|
||||
$this->flash("err", "Ошибка доступа", "Вы не можете просматривать полный список подписок $name.");
|
||||
$this->flash("err", tr("error_access_denied_short"), tr("error_viewing_subs", $name));
|
||||
|
||||
$this->redirect($user->getURL());
|
||||
}
|
||||
|
@ -107,11 +110,11 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$this->notFound();
|
||||
|
||||
if(!$club->canBeModifiedBy($this->user->identity ?? NULL))
|
||||
$this->flashFail("err", "Ошибка доступа", "У вас недостаточно прав, чтобы изменять этот ресурс.", NULL, true);
|
||||
$this->flashFail("err", tr("error_access_denied_short"), tr("error_access_denied"), NULL, true);
|
||||
|
||||
$isClubPinned = $this->user->identity->isClubPinned($club);
|
||||
if(!$isClubPinned && $this->user->identity->getPinnedClubCount() > 10)
|
||||
$this->flashFail("err", "Ошибка", "Находится в левом меню могут максимум 10 групп", NULL, true);
|
||||
$this->flashFail("err", tr("error"), tr("error_max_pinned_clubs"), NULL, true);
|
||||
|
||||
if($club->getOwner()->getId() === $this->user->identity->getId()) {
|
||||
$club->setOwner_Club_Pinned(!$isClubPinned);
|
||||
|
@ -163,12 +166,26 @@ final class UserPresenter extends OpenVKPresenter
|
|||
|
||||
if ($this->postParam("marialstatus") <= 8 && $this->postParam("marialstatus") >= 0)
|
||||
$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)
|
||||
$user->setPolit_Views($this->postParam("politViews"));
|
||||
|
||||
if ($this->postParam("gender") <= 1 && $this->postParam("gender") >= 0)
|
||||
$user->setSex($this->postParam("gender"));
|
||||
$user->setAudio_broadcast_enabled($this->checkbox("broadcast_music"));
|
||||
|
||||
if(!empty($this->postParam("phone")) && $this->postParam("phone") !== $user->getPhone()) {
|
||||
if(!OPENVK_ROOT_CONF["openvk"]["credentials"]["smsc"]["enable"])
|
||||
|
@ -237,10 +254,11 @@ final class UserPresenter extends OpenVKPresenter
|
|||
} elseif($_GET['act'] === "status") {
|
||||
if(mb_strlen($this->postParam("status")) > 255) {
|
||||
$statusLength = (string) mb_strlen($this->postParam("status"));
|
||||
$this->flashFail("err", "Ошибка", "Статус слишком длинный ($statusLength символов вместо 255 символов)", NULL, true);
|
||||
$this->flashFail("err", tr("error"), tr("error_status_too_long", $statusLength), NULL, true);
|
||||
}
|
||||
|
||||
$user->setStatus(empty($this->postParam("status")) ? NULL : $this->postParam("status"));
|
||||
$user->setAudio_broadcast_enabled($this->postParam("broadcast") == 1);
|
||||
$user->save();
|
||||
|
||||
$this->returnJson([
|
||||
|
@ -281,7 +299,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!$user->verifyNumber($this->postParam("code") ?? 0))
|
||||
$this->flashFail("err", "Ошибка", "Не удалось подтвердить номер телефона: неверный код.");
|
||||
$this->flashFail("err", tr("error"), tr("invalid_code"));
|
||||
|
||||
$this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
|
||||
}
|
||||
|
@ -430,10 +448,11 @@ final class UserPresenter extends OpenVKPresenter
|
|||
"friends.add",
|
||||
"wall.write",
|
||||
"messages.write",
|
||||
"audios.read",
|
||||
];
|
||||
foreach($settings as $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))));
|
||||
}
|
||||
} else if($_GET['act'] === "finance.top-up") {
|
||||
$token = $this->postParam("key0") . $this->postParam("key1") . $this->postParam("key2") . $this->postParam("key3");
|
||||
|
@ -474,6 +493,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
} else if($_GET['act'] === "lMenu") {
|
||||
$settings = [
|
||||
"menu_bildoj" => "photos",
|
||||
"menu_muziko" => "audios",
|
||||
"menu_filmetoj" => "videos",
|
||||
"menu_mesagoj" => "messages",
|
||||
"menu_notatoj" => "notes",
|
||||
|
@ -481,6 +501,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
"menu_novajoj" => "news",
|
||||
"menu_ligiloj" => "links",
|
||||
"menu_standardo" => "poster",
|
||||
"menu_aplikoj" => "apps"
|
||||
];
|
||||
foreach($settings as $checkbox => $setting)
|
||||
$user->setLeftMenuItemStatus($setting, $this->checkbox($checkbox));
|
||||
|
|
|
@ -233,8 +233,13 @@ final class VKAPIPresenter extends OpenVKPresenter
|
|||
$this->badMethodCall($object, $method, $parameter->getName());
|
||||
}
|
||||
|
||||
settype($val, $parameter->getType()->getName());
|
||||
$params[] = $val;
|
||||
try {
|
||||
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);
|
||||
|
|
|
@ -58,7 +58,7 @@ final class VideosPresenter extends OpenVKPresenter
|
|||
$this->willExecuteWriteAction();
|
||||
|
||||
if(OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
|
||||
$this->flashFail("err", tr("error"), "Video uploads are disabled by the system administrator.");
|
||||
$this->flashFail("err", tr("error"), tr("video_uploads_disabled"));
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!empty($this->postParam("name"))) {
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace openvk\Web\Presenters;
|
|||
use openvk\Web\Models\Exceptions\TooMuchOptionsException;
|
||||
use openvk\Web\Models\Entities\{Poll, Post, Photo, Video, Club, User};
|
||||
use openvk\Web\Models\Entities\Notifications\{MentionNotification, RepostNotification, WallPostNotification};
|
||||
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums, Notes};
|
||||
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums, Notes, Videos, Comments, Photos, Audios};
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use Nette\InvalidStateException as ISE;
|
||||
use Bhaktaraz\RSSGenerator\Item;
|
||||
|
@ -46,13 +46,13 @@ final class WallPresenter extends OpenVKPresenter
|
|||
function renderWall(int $user, bool $embedded = false): void
|
||||
{
|
||||
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
|
||||
if ($owner->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
if(is_null($this->user)) {
|
||||
$canPost = false;
|
||||
} else if($user > 0) {
|
||||
if(!$owner->isBanned())
|
||||
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity);
|
||||
else
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity);
|
||||
} else if($user < 0) {
|
||||
if($owner->canBeModifiedBy($this->user->identity))
|
||||
$canPost = true;
|
||||
|
@ -100,6 +100,8 @@ final class WallPresenter extends OpenVKPresenter
|
|||
} else if($user < 0) {
|
||||
if($owner->canBeModifiedBy($this->user->identity))
|
||||
$canPost = true;
|
||||
else if ($owner->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
else
|
||||
$canPost = $owner->canPost();
|
||||
} else {
|
||||
|
@ -152,7 +154,7 @@ final class WallPresenter extends OpenVKPresenter
|
|||
$this->template->paginatorConf = (object) [
|
||||
"count" => sizeof($posts),
|
||||
"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,
|
||||
];
|
||||
$this->template->posts = [];
|
||||
|
@ -180,7 +182,7 @@ final class WallPresenter extends OpenVKPresenter
|
|||
$this->template->paginatorConf = (object) [
|
||||
"count" => $count,
|
||||
"page" => (int) ($_GET["p"] ?? 1),
|
||||
"amount" => sizeof($posts),
|
||||
"amount" => $posts->getRowCount(),
|
||||
"perPage" => $pPage,
|
||||
];
|
||||
foreach($posts as $post)
|
||||
|
@ -212,11 +214,12 @@ final class WallPresenter extends OpenVKPresenter
|
|||
|
||||
$wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1))
|
||||
?? $this->flashFail("err", tr("failed_to_publish_post"), tr("error_4"));
|
||||
|
||||
if ($wallOwner->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
if($wall > 0) {
|
||||
if(!$wallOwner->isBanned())
|
||||
$canPost = $wallOwner->getPrivacyPermission("wall.write", $this->user->identity);
|
||||
else
|
||||
$this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
|
||||
$canPost = $wallOwner->getPrivacyPermission("wall.write", $this->user->identity);
|
||||
} else if($wall < 0) {
|
||||
if($wallOwner->canBeModifiedBy($this->user->identity))
|
||||
$canPost = true;
|
||||
|
@ -228,10 +231,7 @@ final class WallPresenter extends OpenVKPresenter
|
|||
|
||||
if(!$canPost)
|
||||
$this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
|
||||
|
||||
if($_FILES["_vid_attachment"] && OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
|
||||
$this->flashFail("err", tr("error"), "Video uploads are disabled by the system administrator.");
|
||||
|
||||
|
||||
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
|
||||
if($wallOwner instanceof Club && $this->postParam("as_group") === "on" && $this->postParam("force_sign") !== "on" && $anon) {
|
||||
$manager = $wallOwner->getManager($this->user->identity);
|
||||
|
@ -249,23 +249,23 @@ final class WallPresenter extends OpenVKPresenter
|
|||
if($this->postParam("force_sign") === "on")
|
||||
$flags |= 0b01000000;
|
||||
|
||||
try {
|
||||
$photo = NULL;
|
||||
$video = NULL;
|
||||
if($_FILES["_pic_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||
$album = NULL;
|
||||
if(!$anon && $wall > 0 && $wall === $this->user->id)
|
||||
$album = (new Albums)->getUserWallAlbum($wallOwner);
|
||||
|
||||
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album, $anon);
|
||||
$photos = [];
|
||||
|
||||
if(!empty($this->postParam("photos"))) {
|
||||
$un = rtrim($this->postParam("photos"), ",");
|
||||
$arr = explode(",", $un);
|
||||
|
||||
if(sizeof($arr) < 11) {
|
||||
foreach($arr as $dat) {
|
||||
$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 {
|
||||
|
@ -292,8 +292,46 @@ final class WallPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", " ");
|
||||
}
|
||||
}
|
||||
|
||||
$videos = [];
|
||||
|
||||
if(!empty($this->postParam("videos"))) {
|
||||
$un = rtrim($this->postParam("videos"), ",");
|
||||
$arr = explode(",", $un);
|
||||
|
||||
if(sizeof($arr) < 11) {
|
||||
foreach($arr as $dat) {
|
||||
$ids = explode("_", $dat);
|
||||
$video = (new Videos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
|
||||
|
||||
if(!$video || $video->isDeleted())
|
||||
continue;
|
||||
|
||||
$videos[] = $video;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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 && !$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"));
|
||||
|
||||
try {
|
||||
|
@ -310,17 +348,21 @@ final class WallPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_too_big"));
|
||||
}
|
||||
|
||||
if(!is_null($photo))
|
||||
$post->attach($photo);
|
||||
foreach($photos as $photo)
|
||||
$post->attach($photo);
|
||||
|
||||
if(!is_null($video))
|
||||
$post->attach($video);
|
||||
if(sizeof($videos) > 0)
|
||||
foreach($videos as $vid)
|
||||
$post->attach($vid);
|
||||
|
||||
if(!is_null($poll))
|
||||
$post->attach($poll);
|
||||
|
||||
if(!is_null($note))
|
||||
$post->attach($note);
|
||||
|
||||
foreach($audios as $audio)
|
||||
$post->attach($audio);
|
||||
|
||||
if($wall > 0 && $wall !== $this->user->identity->getId())
|
||||
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
||||
|
@ -354,6 +396,9 @@ final class WallPresenter extends OpenVKPresenter
|
|||
} else {
|
||||
$this->template->wallOwner = (new Clubs)->get(abs($post->getTargetWall()));
|
||||
$this->template->isWallOfGroup = true;
|
||||
|
||||
if ($this->template->wallOwner->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
}
|
||||
$this->template->cCount = $post->getCommentsCount();
|
||||
$this->template->cPage = (int) ($_GET["p"] ?? 1);
|
||||
|
@ -368,7 +413,10 @@ final class WallPresenter extends OpenVKPresenter
|
|||
|
||||
$post = $this->posts->getPostById($wall, $post_id);
|
||||
if(!$post || $post->isDeleted()) $this->notFound();
|
||||
|
||||
|
||||
if ($post->getWallOwner()->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
if(!is_null($this->user)) {
|
||||
$post->toggleLike($this->user->identity);
|
||||
}
|
||||
|
@ -386,6 +434,9 @@ final class WallPresenter extends OpenVKPresenter
|
|||
|
||||
if(!$post || $post->isDeleted())
|
||||
$this->notFound();
|
||||
|
||||
if ($post->getWallOwner()->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$where = $this->postParam("type") ?? "wall";
|
||||
$groupId = NULL;
|
||||
|
@ -444,6 +495,9 @@ final class WallPresenter extends OpenVKPresenter
|
|||
$wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1))
|
||||
?? $this->flashFail("err", tr("failed_to_delete_post"), tr("error_4"));
|
||||
|
||||
if ($wallOwner->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
if($wall < 0) $canBeDeletedByOtherUser = $wallOwner->canBeModifiedBy($this->user->identity);
|
||||
else $canBeDeletedByOtherUser = false;
|
||||
|
||||
|
@ -467,6 +521,9 @@ final class WallPresenter extends OpenVKPresenter
|
|||
$post = $this->posts->getPostById($wall, $post_id);
|
||||
if(!$post)
|
||||
$this->notFound();
|
||||
|
||||
if ($post->getWallOwner()->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
if(!$post->canBePinnedBy($this->user->identity))
|
||||
$this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
|
||||
|
@ -480,4 +537,64 @@ final class WallPresenter extends OpenVKPresenter
|
|||
# TODO localize message based on language and ?act=(un)pin
|
||||
$this->flashFail("succ", tr("information_-1"), tr("changes_saved_comment"));
|
||||
}
|
||||
|
||||
function renderEdit()
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||
$this->redirect("/id0");
|
||||
|
||||
if($this->postParam("type") == "post")
|
||||
$post = $this->posts->get((int)$this->postParam("postid"));
|
||||
else
|
||||
$post = (new Comments)->get((int)$this->postParam("postid"));
|
||||
|
||||
if(!$post || $post->isDeleted())
|
||||
$this->returnJson(["error" => "Invalid post"]);
|
||||
|
||||
if(!$post->canBeEditedBy($this->user->identity))
|
||||
$this->returnJson(["error" => "Access denied"]);
|
||||
|
||||
$attachmentsCount = sizeof(iterator_to_array($post->getChildren()));
|
||||
|
||||
if(empty($this->postParam("newContent")) && $attachmentsCount < 1)
|
||||
$this->returnJson(["error" => "Empty post"]);
|
||||
|
||||
$post->setEdited(time());
|
||||
|
||||
try {
|
||||
$post->setContent($this->postParam("newContent"));
|
||||
} catch(\LengthException $e) {
|
||||
$this->returnJson(["error" => $e->getMessage()]);
|
||||
}
|
||||
|
||||
if($this->postParam("type") === "post") {
|
||||
$post->setNsfw($this->postParam("nsfw") == "true");
|
||||
$flags = 0;
|
||||
|
||||
if($post->getTargetWall() < 0 && $post->getWallOwner()->canBeModifiedBy($this->user->identity)) {
|
||||
if($this->postParam("fromgroup") == "true") {
|
||||
$flags |= 0b10000000;
|
||||
$post->setFlags($flags);
|
||||
} else
|
||||
$post->setFlags($flags);
|
||||
}
|
||||
}
|
||||
|
||||
$post->save(true);
|
||||
|
||||
$this->returnJson(["error" => "no",
|
||||
"new_content" => $post->getText(),
|
||||
"new_edited" => (string)$post->getEditTime(),
|
||||
"nsfw" => $this->postParam("type") === "post" ? (int)$post->isExplicit() : 0,
|
||||
"from_group" => $this->postParam("type") === "post" && $post->getTargetWall() < 0 ?
|
||||
((int)$post->isPostedOnBehalfOfGroup()) : "false",
|
||||
"new_text" => $post->getText(false),
|
||||
"author" => [
|
||||
"name" => $post->getOwner()->getCanonicalName(),
|
||||
"avatar" => $post->getOwner()->getAvatarUrl()
|
||||
]]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,19 @@
|
|||
<img src="/assets/packages/static/openvk/img/oof.apng" alt="{_banned_alt}" style="width: 20%;" />
|
||||
</center>
|
||||
<p>
|
||||
{tr("banned_1", htmlentities($thisUser->getCanonicalName()))|noescape}<br/>
|
||||
{tr("banned_2", htmlentities($thisUser->getBanReason()))|noescape}
|
||||
{var $ban = $thisUser->getBanReason("banned")}
|
||||
{if is_string($ban)}
|
||||
{tr("banned_1", htmlentities($thisUser->getCanonicalName()))|noescape}<br/>
|
||||
{tr("banned_2", htmlentities($thisUser->getBanReason()))|noescape}
|
||||
{else}
|
||||
{tr("banned_1", htmlentities($thisUser->getCanonicalName()))|noescape}
|
||||
<div>
|
||||
Эта страница была заморожена {$ban[0]|noescape}
|
||||
{if $ban[1] !== "app"}
|
||||
{include "Report/ViewContent.xml", type => $ban[1], object => $ban[2]}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{if !$thisUser->getUnbanTime()}
|
||||
{_banned_perm}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
{script "js/node_modules/umbrellajs/umbrella.min.js"}
|
||||
{script "js/l10n.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/border.css"}
|
||||
|
@ -39,9 +41,8 @@
|
|||
<body>
|
||||
<div id="sudo-banner" n:if="isset($thisUser) && $userTainted">
|
||||
<p>
|
||||
Вы вошли как <b>{$thisUser->getCanonicalName()}</b>. Пожалуйста, уважайте
|
||||
право на тайну переписки других людей и не злоупотребляйте подменой пользователя.
|
||||
Нажмите <a href="/setSID/unset?hash={rawurlencode($csrfToken)}">здесь</a>, чтобы выйти.
|
||||
{_you_entered_as} <b>{$thisUser->getCanonicalName()}</b>. {_please_rights}
|
||||
{_click_on} <a href="/setSID/unset?hash={rawurlencode($csrfToken)}">{_there}</a>, {_to_leave}.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
@ -124,6 +125,7 @@
|
|||
<option value="comments">{_s_by_comments}</option>
|
||||
<option value="videos">{_s_by_videos}</option>
|
||||
<option value="apps">{_s_by_apps}</option>
|
||||
<option value="audios">{_s_by_audios}</option>
|
||||
</select>
|
||||
</form>
|
||||
<div class="searchTips" id="srcht" hidden>
|
||||
|
@ -142,13 +144,13 @@
|
|||
<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="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>
|
||||
<button class="searchBtn"><span style="color:white;font-weight: 600;font-size:12px;">{_header_search}</span></button>
|
||||
</form>
|
||||
<script>
|
||||
let els = document.querySelectorAll("div.dec")
|
||||
for(const element of els)
|
||||
{
|
||||
for(const element of els) {
|
||||
element.style.display = "none"
|
||||
}
|
||||
</script>
|
||||
|
@ -177,13 +179,14 @@
|
|||
<a href="{$thisUser->getURL()}" class="link" title="{_my_page} [Alt+Shift+.]" accesskey=".">{_my_page}</a>
|
||||
<a href="/friends{$thisUser->getId()}" class="link">{_my_friends}
|
||||
<object type="internal/link" n:if="$thisUser->getFollowersCount() > 0">
|
||||
<a href="/friends{$thisUser->getId()}?act=incoming">
|
||||
<a href="/friends{$thisUser->getId()}?act=incoming" class="linkunderline">
|
||||
(<b>{$thisUser->getFollowersCount()}</b>)
|
||||
</a>
|
||||
</object>
|
||||
</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('audios')" href="/audios{$thisUser->getId()}" class="link">{_my_audios}</a>
|
||||
<a n:if="$thisUser->getLeftMenuItemStatus('messages')" href="/im" class="link">{_my_messages}
|
||||
<object type="internal/link" n:if="$thisUser->getUnreadMessagesCount() > 0">
|
||||
(<b>{$thisUser->getUnreadMessagesCount()}</b>)
|
||||
|
@ -197,7 +200,7 @@
|
|||
(<b>{$thisUser->getNotificationsCount()}</b>)
|
||||
{/if}
|
||||
</a>
|
||||
<a href="/apps?act=installed" class="link">{_my_apps}</a>
|
||||
<a n:if="$thisUser->getLeftMenuItemStatus('apps')" href="/apps?act=installed" class="link">{_my_apps}</a>
|
||||
<a href="/settings" class="link">{_my_settings}</a>
|
||||
|
||||
{var $canAccessAdminPanel = $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
||||
|
@ -210,8 +213,23 @@
|
|||
(<b>{$helpdeskTicketNotAnsweredCount}</b>)
|
||||
{/if}
|
||||
</a>
|
||||
|
||||
<a n:if="$thisUser->getLeftMenuItemStatus('links')" n:foreach="OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links'] as $menuItem" href="{$menuItem['url']}" target="_blank" class="link">{strpos($menuItem["name"], "@") === 0 ? tr(substr($menuItem["name"], 1)) : $menuItem["name"]}</a>
|
||||
<a n:if="$canAccessHelpdesk" href="/scumfeed" class="link">{tr("reports")}
|
||||
{if $reportNotAnsweredCount > 0}
|
||||
(<b>{$reportNotAnsweredCount}</b>)
|
||||
{/if}
|
||||
</a>
|
||||
<a n:if="$canAccessHelpdesk" href="/noSpam" class="link">
|
||||
noSpam
|
||||
</a>
|
||||
<a
|
||||
n:if="$thisUser->getLeftMenuItemStatus('links')"
|
||||
n:foreach="OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links'] as $menuItem"
|
||||
href="{$menuItem['url']}"
|
||||
target="_blank"
|
||||
class="link">
|
||||
{strpos($menuItem["name"], "@") === 0 ? tr(substr($menuItem["name"], 1)) : $menuItem["name"]}
|
||||
</a>
|
||||
<div id="_groupListPinnedGroups">
|
||||
|
||||
<div id="_groupListPinnedGroups">
|
||||
<div n:if="$thisUser->getPinnedClubCount() > 0" class="menu_divider"></div>
|
||||
|
@ -284,8 +302,13 @@
|
|||
{if !OPENVK_ROOT_CONF['openvk']['preferences']['security']['disablePasswordRestoring']}<a href="/restore">{_forgot_password}</a>{/if}
|
||||
</form>
|
||||
{/ifset}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{ifset $thisUser}
|
||||
{if !$thisUser->isBanned() && !$thisUser->isDeleted()}
|
||||
</div>
|
||||
{/if}
|
||||
{/ifset}
|
||||
|
||||
<div class="page_body">
|
||||
<div id="wrapH">
|
||||
|
@ -412,6 +435,12 @@
|
|||
//]]>
|
||||
</script>
|
||||
|
||||
<script>
|
||||
window.openvk = {
|
||||
"audio_genres": {\openvk\Web\Models\Entities\Audio::genres}
|
||||
}
|
||||
</script>
|
||||
|
||||
{ifset bodyScripts}
|
||||
{include bodyScripts}
|
||||
{/ifset}
|
||||
|
|
|
@ -12,16 +12,24 @@
|
|||
{include size, x => $dat}
|
||||
{/ifset}
|
||||
|
||||
{ifset before_content}
|
||||
{include before_content, x => $dat}
|
||||
{/ifset}
|
||||
|
||||
{ifset specpage}
|
||||
{include specpage, x => $dat}
|
||||
{else}
|
||||
<div class="container_gray">
|
||||
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||
|
||||
{ifset top}
|
||||
{include top, x => $dat}
|
||||
{/ifset}
|
||||
|
||||
{if sizeof($data) > 0}
|
||||
<div class="content" n:foreach="$data as $dat">
|
||||
<table>
|
||||
<tbody>
|
||||
<tbody n:attr="id => is_null($table_body_id) ? NULL : $table_body_id">
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<a href="{include link, x => $dat}">
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}Ваш браузер устарел{/block}
|
||||
{block title}{_deprecated_browser}{/block}
|
||||
|
||||
{block header}
|
||||
Устаревший браузер
|
||||
{_deprecated_browser}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
Для просмотра этого контента вам понадобится Firefox ESR 52+ или
|
||||
эквивалентный по функционалу навигатор по всемирной сети интернет.<br/>
|
||||
Сожалеем об этом.
|
||||
{_deprecated_browser_description}
|
||||
{/block}
|
||||
|
|
|
@ -9,5 +9,5 @@
|
|||
<div id="faqhead">Для кого этот сайт?</div>
|
||||
<div id="faqcontent">Сайт предназначен для поиска друзей и знакомых, а также просмотр данных пользователя. Это как справочник города, с помощью которого люди могут быстро найти актуальную информацию о человеке. Также этот сайт подойдёт для ностальгираторов и тех, кто решил слезть с трубы "ВКонтакте", которого клон и является.<br></div>
|
||||
Я попозже допишу ок ~~ veselcraft - 12.01.2020 - 22:05 GMT+3
|
||||
|
||||
Давай
|
||||
{/block}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
.navigation-lang .link_new {
|
||||
display: inline-block;
|
||||
padding: 25px 25px 20px 25px;
|
||||
padding: 20px 10px 5px 10px;
|
||||
text-decoration: none;
|
||||
border-top: 1px solid #fff;
|
||||
color: #000;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{block title}Sandbox{/block}
|
||||
|
||||
{block header}
|
||||
Sandbox для разработчиков
|
||||
{_sandbox_for_developers}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
|
|
@ -178,11 +178,29 @@
|
|||
<h2>{_tour_section_6_title_1|noescape}</h2>
|
||||
|
||||
<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 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>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -97,6 +97,9 @@
|
|||
<li>
|
||||
<a href="/admin/bannedLinks">{_admin_banned_links}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/music">{_admin_music}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="aui-nav-heading">
|
||||
<strong>Chandler</strong>
|
||||
|
@ -124,6 +127,9 @@
|
|||
<li>
|
||||
<a href="/admin/settings/tuning">{_admin_settings_tuning}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/logs">Логи</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/settings/appearance">{_admin_settings_appearance}</a>
|
||||
</li>
|
||||
|
|
86
Web/Presenters/templates/Admin/BansHistory.xml
Normal file
86
Web/Presenters/templates/Admin/BansHistory.xml
Normal file
|
@ -0,0 +1,86 @@
|
|||
{extends "./@layout.xml"}
|
||||
|
||||
{block title}
|
||||
{_bans_history}
|
||||
{/block}
|
||||
|
||||
{block heading}
|
||||
{include title}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<table class="aui aui-table-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>{_bans_history_blocked}</th>
|
||||
<th>{_bans_history_initiator}</th>
|
||||
<th>{_bans_history_start}</th>
|
||||
<th>{_bans_history_end}</th>
|
||||
<th>{_bans_history_time}</th>
|
||||
<th>{_bans_history_reason}</th>
|
||||
<th>{_bans_history_removed}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr n:foreach="$bans as $ban">
|
||||
<td>{$ban->getId()}</td>
|
||||
<td>
|
||||
<span class="aui-avatar aui-avatar-xsmall">
|
||||
<span class="aui-avatar-inner">
|
||||
<img src="{$ban->getUser()->getAvatarUrl('miniscule')}"
|
||||
alt="{$ban->getUser()->getCanonicalName()}" style="object-fit: cover;"
|
||||
role="presentation"/>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<a href="{$ban->getUser()->getURL()}">{$ban->getUser()->getCanonicalName()}</a>
|
||||
|
||||
<span n:if="$ban->getUser()->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="{$ban->getInitiator()->getAvatarUrl('miniscule')}"
|
||||
alt="{$ban->getInitiator()->getCanonicalName()}" style="object-fit: cover;"
|
||||
role="presentation"/>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<a href="{$ban->getInitiator()->getURL()}">{$ban->getInitiator()->getCanonicalName()}</a>
|
||||
|
||||
<span n:if="$ban->getInitiator()->isBanned()"
|
||||
class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">{_admin_banned}
|
||||
</span>
|
||||
</td>
|
||||
<td>{date('d.m.Y в H:i:s', $ban->getStartTime())}</td>
|
||||
<td>{date('d.m.Y в H:i:s', $ban->getEndTime())}</td>
|
||||
<td>{$ban->getTime()}</td>
|
||||
<td>
|
||||
{$ban->getReason()}
|
||||
</td>
|
||||
<td>
|
||||
{if $ban->isRemovedManually()}
|
||||
<span class="aui-avatar aui-avatar-xsmall">
|
||||
<span class="aui-avatar-inner">
|
||||
<img src="{$ban->whoRemoved()->getAvatarUrl('miniscule')}"
|
||||
alt="{$ban->whoRemoved()->getCanonicalName()}" style="object-fit: cover;"
|
||||
role="presentation"/>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<a href="{$ban->whoRemoved()->getURL()}">{$ban->whoRemoved()->getCanonicalName()}</a>
|
||||
|
||||
<span n:if="$ban->whoRemoved()->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">
|
||||
{_admin_banned}
|
||||
</span>
|
||||
{else}
|
||||
<b style="color: red;">{_bans_history_active}</b>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{/block}
|
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}
|
92
Web/Presenters/templates/Admin/Logs.xml
Normal file
92
Web/Presenters/templates/Admin/Logs.xml
Normal file
|
@ -0,0 +1,92 @@
|
|||
{extends "@layout.xml"}
|
||||
|
||||
{block title}
|
||||
{_logs}
|
||||
{/block}
|
||||
|
||||
{block heading}
|
||||
{_logs}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
{var $amount = sizeof($logs)}
|
||||
|
||||
<style>
|
||||
del, ins { text-decoration: none; color: #000; }
|
||||
del { background: #fdd; }
|
||||
ins { background: #dfd; }
|
||||
</style>
|
||||
<form class="aui">
|
||||
<div>
|
||||
<select class="select medium-field" type="number" id="type" name="type" placeholder="{_logs_change_type}">
|
||||
<option value="any" n:attr="selected => !$type">{_logs_anything}</option>
|
||||
<option value="0" n:attr="selected => $type === 0">{_logs_adding}</option>
|
||||
<option value="1" n:attr="selected => $type === 1">{_logs_editing}</option>
|
||||
<option value="2" n:attr="selected => $type === 2">{_logs_removing}</option>
|
||||
<option value="3" n:attr="selected => $type === 3">{_logs_restoring}</option>
|
||||
</select>
|
||||
<input class="text medium-field" type="number" id="id" name="id" placeholder="{_logs_id_post}" n:attr="value => $id"/>
|
||||
<input class="text medium-field" type="text" id="uid" name="uid" placeholder="{_logs_uuid_user}" n:attr="value => $user"/>
|
||||
</div>
|
||||
<div style="margin: 8px 0;" />
|
||||
<div>
|
||||
<select class="select medium-field" id="obj_type" name="obj_type" placeholder="{_logs_change_object}">
|
||||
<option value="any" n:attr="selected => !$obj_type">{_logs_anything}</option>
|
||||
<option n:foreach="$object_types as $type" n:attr="selected => $obj_type === $type">{$type}</option>
|
||||
</select>
|
||||
<input class="text medium-field" type="number" id="obj_id" name="obj_id" placeholder="{_logs_id_object}" n:attr="value => $obj_id"/>
|
||||
<input type="submit" class="aui-button aui-button-primary medium-field" value="Поиск" style="width: 165px;"/>
|
||||
</div>
|
||||
</form>
|
||||
<table class="aui aui-table-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>{_logs_user}</th>
|
||||
<th>{_logs_object}</th>
|
||||
<th>{_logs_type}</th>
|
||||
<th>{_logs_changes}</th>
|
||||
<th>{_logs_time}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr n:foreach="$logs as $log">
|
||||
<td>{$log->getId()}</td>
|
||||
<td>
|
||||
<a href="/admin/chandler/user/{$log->getUser()}" target="_blank">{$log->getUser()}</a>
|
||||
</td>
|
||||
<td>
|
||||
<span n:if="$log->getObjectAvatar()" class="aui-avatar aui-avatar-xsmall">
|
||||
<span class="aui-avatar-inner">
|
||||
<img src="{$log->getObjectAvatar()}" alt="{$log->getObjectName()}" style="object-fit: cover;" role="presentation" />
|
||||
</span>
|
||||
</span>
|
||||
<a href="{$log->getObjectURL()}">{$log->getObjectName()}</a>
|
||||
</td>
|
||||
<td>{_$log->getTypeNom()}</td>
|
||||
<td>
|
||||
{foreach $log->getChanges() as $change}
|
||||
<div>
|
||||
<b>{$change["field"]}</b>:
|
||||
{if array_key_exists('diff', $change)}
|
||||
{$change["diff"]|noescape}
|
||||
{else}
|
||||
<ins>{$change["old_value"]}</ins>
|
||||
{/if}
|
||||
</div>
|
||||
{/foreach}
|
||||
</td>
|
||||
<td>
|
||||
{=new openvk\Web\Util\DateTime($change["ts"])}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br/>
|
||||
<div align="right">
|
||||
{var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
||||
|
||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||
</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}
|
|
@ -1,4 +1,5 @@
|
|||
{extends "../@layout.xml"}
|
||||
{var $canReport = $owner->getId() !== $thisUser->getId()}
|
||||
|
||||
{block title}
|
||||
{$name}
|
||||
|
@ -6,6 +7,7 @@
|
|||
|
||||
{block header}
|
||||
{$name}
|
||||
<a style="float: right;" onClick="reportApp()" n:if="$canReport ?? false">{_report}</a>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
@ -33,5 +35,29 @@
|
|||
window.appOrigin = {$origin};
|
||||
</script>
|
||||
|
||||
<script n:if="$canReport ?? false">
|
||||
function reportApp() {
|
||||
uReportMsgTxt = {_going_to_report_app};
|
||||
uReportMsgTxt += "<br/>"+tr("report_question_text");
|
||||
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
|
||||
|
||||
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
|
||||
(function() {
|
||||
res = document.querySelector("#uReportMsgInput").value;
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "/report/" + {$id} + "?reason=" + res + "&type=app", true);
|
||||
xhr.onload = (function() {
|
||||
if(xhr.responseText.indexOf("reason") === -1)
|
||||
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
|
||||
else
|
||||
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
|
||||
});
|
||||
xhr.send(null);
|
||||
}),
|
||||
Function.noop
|
||||
]);
|
||||
}
|
||||
</script>
|
||||
|
||||
{script "js/al_games.js"}
|
||||
{/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>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue