mirror of
https://github.com/openvk/openvk
synced 2024-11-15 03:31:18 +03:00
Compare commits
173 commits
77d0603e67
...
cff7048261
Author | SHA1 | Date | |
---|---|---|---|
|
cff7048261 | ||
|
fab39bd7fd | ||
|
9b220a88db | ||
|
d1bcdaf7d7 | ||
f83c45ead9 | |||
|
96dad033ba | ||
b557f42daa | |||
|
250c022bde | ||
|
a3d535040d | ||
|
53f6b3e12b | ||
|
b3e57147b7 | ||
|
2795e1e1a4 | ||
|
bbef3a8518 | ||
|
c5644a51d6 | ||
|
4c78617a9c | ||
89157a832b | |||
|
91f30bd9d2 | ||
6013cd095e | |||
97c577732b | |||
188768594b | |||
a13d62e8fd | |||
|
9d62bfc12e | ||
|
8786bd36fb | ||
|
3707ae0772 | ||
|
bbbc8b7700 | ||
|
4521d4a2f1 | ||
3349fa8b01 | |||
8f76751f99 | |||
791da0f8df | |||
|
3952d8437e | ||
|
9fd8b22a95 | ||
|
ae689e1327 | ||
|
61b3982349 | ||
|
4218e02f4b | ||
|
2bdb4f03d0 | ||
|
0b80c0a6a8 | ||
|
939ea30262 | ||
|
c4fb793333 | ||
|
869712d73a | ||
|
eb95cef6d0 | ||
|
0f81a6a0c3 | ||
|
73ee4f5abf | ||
|
14b0b78cce | ||
|
c2be06b58f | ||
|
c863e07908 | ||
|
64827402ee | ||
|
d60df69825 | ||
|
49e061f563 | ||
44531fcbb0 | |||
|
fabf9ffd71 | ||
|
acc5459f90 | ||
|
ecb778b2db | ||
|
5617572967 | ||
|
4548e2290a | ||
e813549602 | |||
2eb15cb600 | |||
3d08bd1370 | |||
88d0ef3ef2 | |||
88d296a9c4 | |||
b2b6fe6966 | |||
f4ba39c6f2 | |||
6954cd9c55 | |||
2449d71595 | |||
|
1e7fdeff27 | ||
|
04eb724cd5 | ||
|
41e4383d3b | ||
|
1625557e24 | ||
|
5b8fe91a33 | ||
|
237206ccec | ||
|
453b002451 | ||
|
050afab816 | ||
|
35bd3ad1e0 | ||
|
93bfe6e886 | ||
|
3c624a9c78 | ||
|
2b9e316b80 | ||
|
4e3a6f35cb | ||
|
4699fcbeb9 | ||
|
3112372d01 | ||
|
704fdb113b | ||
|
9e7467ed97 | ||
|
0f0d3ee950 | ||
|
9fbf7f5bf5 | ||
|
359129bae3 | ||
|
dcf3631d89 | ||
|
71c59023cf | ||
6660cb8d94 | |||
|
92435ae6a8 | ||
|
1f340e392f | ||
|
2ea91360c8 | ||
|
e0cdd8070b | ||
|
ddeb95d383 | ||
|
85ecaa92bc | ||
|
e6a7ff6811 | ||
|
fddaea82dc | ||
|
71d05ea2e9 | ||
|
c4707215de | ||
|
c43e68ab70 | ||
|
55f7069193 | ||
|
63702d44d1 | ||
|
d183b1a8a3 | ||
|
626b5b49bb | ||
|
784b19aaf7 | ||
|
b7160d78a0 | ||
|
08499cd3b4 | ||
|
1632d54d52 | ||
|
5bb6e097fb | ||
|
f65d790654 | ||
|
eb64376c3a | ||
|
9d7a465d0d | ||
|
c9665ac77d | ||
|
49d62543ba | ||
|
a2473c68fe | ||
|
fd11dfcdd9 | ||
|
9a4309ae01 | ||
|
1a17a245c5 | ||
|
46f7e7ceeb | ||
|
abc307bfad | ||
|
f492353ae8 | ||
|
3f1c9cd5ff | ||
|
f57a470c5d | ||
|
e48b696aeb | ||
|
e2dee72c69 | ||
|
390b4f6c24 | ||
|
1f8f5cf6fa | ||
|
15a5f172d9 | ||
|
cd7b51fcf6 | ||
|
2757599492 | ||
|
ff976d022d | ||
|
fffa268316 | ||
|
6d1587471c | ||
|
5e3b06a05b | ||
|
672a8400c3 | ||
|
2922204b48 | ||
|
1320410f17 | ||
|
aba5a1b49a | ||
|
b70c6c96bb | ||
|
158f1938bb | ||
|
49a7047773 | ||
|
e3b9fb9f41 | ||
|
1c3d4d8429 | ||
|
fef0203aa4 | ||
|
e3311fbf97 | ||
|
ab1c6dc843 | ||
|
a859fa13a5 | ||
6632d070f5 | |||
|
db8e9d183f | ||
|
569a8e8bee | ||
5710d131fd | |||
75ce995df5 | |||
8483a2d343 | |||
edf10c4248 | |||
|
cc5a56917b | ||
|
43de40a0dc | ||
|
468eba80bd | ||
|
0ef413a5b9 | ||
|
2939936534 | ||
|
06f324f98c | ||
|
97a176c261 | ||
|
14d5caaf9f | ||
|
245f8690c6 | ||
|
e433e46b36 | ||
|
0d66c8e9d6 | ||
|
4c0deec5af | ||
|
0b7a2e1eda | ||
|
69d0739ef1 | ||
|
c2b6db1b8a | ||
|
1174ddfa4f | ||
|
a2c5896fa1 | ||
|
bddfbdc368 | ||
ee2e5e8d1c | |||
|
6e7f8833fe | ||
|
6159262026 | ||
|
8265dc0fc6 |
341 changed files with 28072 additions and 3143 deletions
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
|
@ -1 +1 @@
|
|||
custom: "https://openvk.su/donate"
|
||||
custom: "https://ovk.to/donate"
|
||||
|
|
20
.github/workflows/build-base.yaml
vendored
20
.github/workflows/build-base.yaml
vendored
|
@ -6,7 +6,7 @@ on:
|
|||
|
||||
env:
|
||||
BASE_IMAGE_NAME: php
|
||||
BASE_IMAGE_VERSION: "8.1"
|
||||
BASE_IMAGE_VERSION: "8.2"
|
||||
|
||||
jobs:
|
||||
build-cli:
|
||||
|
@ -24,12 +24,18 @@ jobs:
|
|||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Change repository string to lowercase
|
||||
id: repositorystring
|
||||
uses: Entepotenz/change-string-case-action-min-dependencies@v1.1.0
|
||||
with:
|
||||
string: ${{ github.repository }}
|
||||
|
||||
- name: Log into registry
|
||||
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
||||
|
||||
- name: Build cli image
|
||||
run: |
|
||||
IMAGE_NAME=ghcr.io/${{ github.repository }}/$BASE_IMAGE_NAME:$BASE_IMAGE_VERSION-cli
|
||||
IMAGE_NAME=ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/$BASE_IMAGE_NAME:$BASE_IMAGE_VERSION-cli
|
||||
|
||||
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/base-php-cli.Dockerfile --build-arg VERSION=$BASE_IMAGE_VERSION
|
||||
|
||||
|
@ -47,12 +53,18 @@ jobs:
|
|||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Change repository string to lowercase
|
||||
id: repositorystring
|
||||
uses: Entepotenz/change-string-case-action-min-dependencies@v1.1.0
|
||||
with:
|
||||
string: ${{ github.repository }}
|
||||
|
||||
- name: Log into registry
|
||||
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
||||
|
||||
|
||||
- name: Build apache image
|
||||
run: |
|
||||
IMAGE_NAME=ghcr.io/${{ github.repository }}/$BASE_IMAGE_NAME:$BASE_IMAGE_VERSION-apache
|
||||
IMAGE_NAME=ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/$BASE_IMAGE_NAME:$BASE_IMAGE_VERSION-apache
|
||||
|
||||
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/base-php-apache.Dockerfile --build-arg VERSION=$BASE_IMAGE_VERSION
|
||||
|
|
14
.github/workflows/build.yaml
vendored
14
.github/workflows/build.yaml
vendored
|
@ -36,12 +36,18 @@ jobs:
|
|||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Change repository string to lowercase
|
||||
id: repositorystring
|
||||
uses: Entepotenz/change-string-case-action-min-dependencies@v1.1.0
|
||||
with:
|
||||
string: ${{ github.repository }}
|
||||
|
||||
- name: Log into registry
|
||||
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
||||
|
||||
- name: Build base image
|
||||
run: |
|
||||
IMAGE_ID=ghcr.io/${{ github.repository }}/$BASE_IMAGE_NAME
|
||||
IMAGE_ID=ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/$BASE_IMAGE_NAME
|
||||
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
|
||||
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
|
||||
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
|
||||
|
@ -49,16 +55,16 @@ jobs:
|
|||
echo IMAGE_ID=$IMAGE_ID
|
||||
echo VERSION=$VERSION
|
||||
|
||||
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_ID:$VERSION . --push -f install/automated/docker/openvk.Dockerfile --build-arg GITREPO=${{ github.repository }}
|
||||
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_ID:$VERSION . --push -f install/automated/docker/openvk.Dockerfile --build-arg GITREPO=${{ steps.repositorystring.outputs.lowercase }}
|
||||
|
||||
- name: Build MariaDB primary image
|
||||
run: |
|
||||
IMAGE_NAME=ghcr.io/${{ github.repository }}/$DB_IMAGE_NAME:$DB_VERSION-primary
|
||||
IMAGE_NAME=ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/$DB_IMAGE_NAME:$DB_VERSION-primary
|
||||
|
||||
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/mariadb-primary.Dockerfile --build-arg VERSION=$DB_VERSION
|
||||
|
||||
- name: Build MariaDB event image
|
||||
run: |
|
||||
IMAGE_NAME=ghcr.io/${{ github.repository }}/$EVENT_IMAGE_NAME:$DB_VERSION-eventdb
|
||||
IMAGE_NAME=ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/$EVENT_IMAGE_NAME:$DB_VERSION-eventdb
|
||||
|
||||
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/mariadb-eventdb.Dockerfile --build-arg VERSION=$DB_VERSION
|
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;
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
<tr>
|
||||
<td class="float-center" align="center" valign="top">
|
||||
<center>
|
||||
Добро пожаловать в OpenVK! Приятного времяприпровождения, надеюсь вам понравится.<br><br>Если появились вопросы, касаемые нашего сайта, пишите <a href="https://openvk.su/support?act=new">сюда</a>
|
||||
Добро пожаловать в OpenVK! Приятного времяприпровождения, надеюсь вам понравится.<br><br>Если появились вопросы, касаемые нашего сайта, пишите <a href="https://ovk.to/support?act=new">сюда</a>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
20
README.md
20
README.md
|
@ -1,4 +1,4 @@
|
|||
# <img align="right" src="https://github.com/openvk/openvk/raw/master/Web/static/img/logo_shadow.png" alt="openvk" title="openvk" width="15%">OpenVK
|
||||
# <img align="right" src="/Web/static/img/logo_shadow.png" alt="openvk" title="openvk" width="15%">OpenVK
|
||||
|
||||
_[Русский](README_RU.md)_
|
||||
|
||||
|
@ -6,7 +6,7 @@ _[Русский](README_RU.md)_
|
|||
|
||||
VKontakte belongs to Pavel Durov and VK Group.
|
||||
|
||||
To be honest, we don't know whether if it even works. However, this version is maintained and we will be happy to accept your bugreports [in our bug tracker](https://github.com/openvk/openvk/projects/1). You should also be able to submit them using [ticketing system](https://openvk.su/support?act=new) (you will need an OpenVK account for this).
|
||||
To be honest, we don't know whether if it even works. However, this version is maintained and we will be happy to accept your bugreports [in our bug tracker](https://github.com/openvk/openvk/projects/1). You should also be able to submit them using [ticketing system](https://ovk.to/support?act=new) (you will need an OpenVK account for this).
|
||||
|
||||
## When's the release?
|
||||
|
||||
|
@ -26,11 +26,19 @@ However, OVK makes use of Chandler Application Server. This software requires ex
|
|||
|
||||
If you want, you can add your instance to the list above so that people can register there.
|
||||
|
||||
### System requirements
|
||||
|
||||
Here is our minimum hardware recommendation:
|
||||
|
||||
* **CPU: Recent** (AMD Zen2 or equivalent) quad-core 2GHz+ CPU
|
||||
* **RAM:** At least 2GB RAM (we recommend 6GB or 8GB for OpenVK with Kafka)
|
||||
* **Minimum database space:** 10GB
|
||||
|
||||
### Installation procedure
|
||||
|
||||
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.
|
||||
|
||||
|
@ -66,12 +74,12 @@ Once you are done, you can login as a system administrator on the network itself
|
|||
* **Password**: `admin`
|
||||
* It is recommended to change the password of the built-in account or disable it.
|
||||
|
||||
💡Confused? Full installation walkthrough is available [here](https://docs.openvk.uk/openvk_engine/centos8_installation/) (CentOS 8 [and](https://almalinux.org/) [family](https://yum.oracle.com/oracle-linux-isos.html)).
|
||||
💡 Confused? Full installation walkthrough is available [here](https://docs.ovk.to/openvk_engine/centos8_installation/) (CentOS 8 [and](https://almalinux.org/) [family](https://yum.oracle.com/oracle-linux-isos.html)).
|
||||
|
||||
### Looking for Docker or Kubernetes deployment?
|
||||
See `install/automated/docker/README.md` and `install/automated/kubernetes/README.md` for Docker and Kubernetes deployment instructions.
|
||||
|
||||
### If my website uses OpenVK, should I release it's sources?
|
||||
### If my website uses OpenVK, should I release its sources?
|
||||
|
||||
It depends. You can keep the sources to yourself if you do not plan to distribute your website binaries. If your website software must be distributed, it can stay non-OSS provided the OpenVK is not used as a primary application and is not modified. If you modified OpenVK for your needs or your work is based on it and you are planning to redistribute this, then you should license it under terms of any LGPL-compatible license (like OSL, GPL, LGPL etc).
|
||||
|
||||
|
@ -80,7 +88,7 @@ It depends. You can keep the sources to yourself if you do not plan to distribut
|
|||
You may reach out to us via:
|
||||
|
||||
* [Bug Tracker](https://github.com/openvk/openvk/projects/1)
|
||||
* [Ticketing System](https://openvk.su/support?act=new)
|
||||
* [Ticketing System](https://ovk.to/support?act=new)
|
||||
* Telegram Chat: Go to [our channel](https://t.me/openvkenglish) and open discussion in our channel menu.
|
||||
* [Reddit](https://www.reddit.com/r/openvk/)
|
||||
* [GitHub Discussions](https://github.com/openvk/openvk/discussions)
|
||||
|
|
10
README_RU.md
10
README_RU.md
|
@ -1,4 +1,4 @@
|
|||
# <img align="right" src="https://github.com/openvk/openvk/raw/master/Web/static/img/logo_shadow.png" alt="openvk" title="openvk" width="15%">OpenVK
|
||||
# <img align="right" src="/Web/static/img/logo_shadow.png" alt="openvk" title="openvk" width="15%">OpenVK
|
||||
|
||||
_[English](README.md)_
|
||||
|
||||
|
@ -6,7 +6,7 @@ _[English](README.md)_
|
|||
|
||||
ВКонтакте принадлежит Павлу Дурову и VK Group.
|
||||
|
||||
Честно говоря, мы даже не знаем, работает ли она вообще. Однако, эта версия поддерживается, и мы будем рады принять ваши сообщения об ошибках [в нашем баг-трекере](https://github.com/openvk/openvk/projects/1). Вы также можете отправлять их через [вкладку "Помощь"](https://openvk.su/support?act=new) (для этого вам понадобится учетная запись OpenVK).
|
||||
Честно говоря, мы даже не знаем, работает ли она вообще. Однако, эта версия поддерживается, и мы будем рады принять ваши сообщения об ошибках [в нашем баг-трекере](https://github.com/openvk/openvk/projects/1). Вы также можете отправлять их через [вкладку "Помощь"](https://ovk.to/support?act=new) (для этого вам понадобится учетная запись OpenVK).
|
||||
|
||||
## Когда выйдет релизная версия?
|
||||
|
||||
|
@ -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-совместимую базу данных.
|
||||
|
||||
|
@ -66,7 +66,7 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
|
|||
* **Пароль**: `admin`
|
||||
* Перед использованием встроенной учетной записи рекомендуется сменить пароль или отключить её.
|
||||
|
||||
💡Запутались? Полное руководство по установке доступно [здесь](https://docs.openvk.uk/openvk_engine/centos8_installation/) (CentOS 8 [и](https://almalinux.org/ru/) [семейство](https://yum.oracle.com/oracle-linux-isos.html)).
|
||||
💡Запутались? Полное руководство по установке доступно [здесь](https://docs.ovk.to/openvk_engine/centos8_installation/) (CentOS 8 [и](https://almalinux.org/ru/) [семейство](https://yum.oracle.com/oracle-linux-isos.html)).
|
||||
|
||||
# Установка в Docker/Kubernetes
|
||||
Подробные иструкции можно найти в `install/automated/docker/README.md` и `install/automated/kubernetes/README.md` соответственно.
|
||||
|
@ -80,7 +80,7 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
|
|||
Вы можете связаться с нами через:
|
||||
|
||||
* [Баг-трекер](https://github.com/openvk/openvk/projects/1)
|
||||
* [Помощь в OVK](https://openvk.su/support?act=new)
|
||||
* [Помощь в OVK](https://ovk.to/support?act=new)
|
||||
* Telegram-чат: Перейдите на [наш канал](https://t.me/openvk) и откройте обсуждение в меню нашего канала.
|
||||
* [Reddit](https://www.reddit.com/r/openvk/)
|
||||
* [GitHub Discussions](https://github.com/openvk/openvk/discussions)
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\ServiceAPI;
|
||||
|
||||
use openvk\Web\Models\Entities\APIToken;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\APITokens;
|
||||
use openvk\Web\Models\Repositories\Applications;
|
||||
use WhichBrowser;
|
||||
|
||||
class Apps implements Handler
|
||||
{
|
||||
|
@ -89,4 +92,25 @@ class Apps implements Handler
|
|||
$app->withdrawCoins();
|
||||
$resolve($coins);
|
||||
}
|
||||
|
||||
function getRegularToken(string $clientName, bool $acceptsStale, callable $resolve, callable $reject): void
|
||||
{
|
||||
$token = NULL;
|
||||
$stale = true;
|
||||
if($acceptsStale)
|
||||
$token = (new APITokens)->getStaleByUser($this->user->getId(), $clientName);
|
||||
|
||||
if(is_null($token)) {
|
||||
$stale = false;
|
||||
$token = new APIToken;
|
||||
$token->setUser($this->user);
|
||||
$token->setPlatform($clientName ?? (new WhichBrowser\Parser(getallheaders()))->toString());
|
||||
$token->save();
|
||||
}
|
||||
|
||||
$resolve([
|
||||
'is_stale' => $stale,
|
||||
'token' => $token->getFormattedToken(),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -25,6 +25,9 @@ class Notes implements Handler
|
|||
assert($noteOwner instanceof User);
|
||||
if(!$noteOwner->getPrivacyPermission("notes.read", $this->user))
|
||||
$reject(160, "You don't have permission to access this note");
|
||||
|
||||
if(!$note->canBeViewedBy($this->user))
|
||||
$reject(15, "Access to note denied");
|
||||
|
||||
$resolve([
|
||||
"title" => $note->getName(),
|
||||
|
|
92
ServiceAPI/Photos.php
Normal file
92
ServiceAPI/Photos.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\ServiceAPI;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\{Photos as PhotosRepo, Albums, Clubs};
|
||||
|
||||
class Photos implements Handler
|
||||
{
|
||||
protected $user;
|
||||
protected $photos;
|
||||
|
||||
function __construct(?User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->photos = new PhotosRepo;
|
||||
}
|
||||
|
||||
function getPhotos(int $page = 1, int $album = 0, callable $resolve, callable $reject)
|
||||
{
|
||||
if($album == 0) {
|
||||
$photos = $this->photos->getEveryUserPhoto($this->user, $page, 24);
|
||||
$count = $this->photos->getUserPhotosCount($this->user);
|
||||
} else {
|
||||
$album = (new Albums)->get($album);
|
||||
|
||||
if(!$album || $album->isDeleted())
|
||||
$reject(55, "Invalid .");
|
||||
|
||||
if($album->getOwner() instanceof User) {
|
||||
if($album->getOwner()->getId() != $this->user->getId())
|
||||
$reject(555, "Access to album denied");
|
||||
} else {
|
||||
if(!$album->getOwner()->canBeModifiedBy($this->user))
|
||||
$reject(555, "Access to album denied");
|
||||
}
|
||||
|
||||
$photos = $album->getPhotos($page, 24);
|
||||
$count = $album->size();
|
||||
}
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($photos as $photo) {
|
||||
$res = json_decode(json_encode($photo->toVkApiStruct()), true);
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
|
||||
function getAlbums(int $club, callable $resolve, callable $reject)
|
||||
{
|
||||
$albumsRepo = (new Albums);
|
||||
|
||||
$count = $albumsRepo->getUserAlbumsCount($this->user);
|
||||
$albums = $albumsRepo->getUserAlbums($this->user, 1, $count);
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($albums as $album) {
|
||||
$res = ["id" => $album->getId(), "name" => $album->getName()];
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
if($club > 0) {
|
||||
$cluber = (new Clubs)->get($club);
|
||||
|
||||
if(!$cluber || !$cluber->canBeModifiedBy($this->user))
|
||||
$reject(1337, "Invalid (club), or you can't modify him");
|
||||
|
||||
$clubCount = (new Albums)->getClubAlbumsCount($cluber);
|
||||
$clubAlbums = (new Albums)->getClubAlbums($cluber, 1, $clubCount);
|
||||
|
||||
foreach($clubAlbums as $albumr) {
|
||||
$res = ["id" => $albumr->getId(), "name" => $albumr->getName()];
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
$arr["count"] = $arr["count"] + $clubCount;
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\ServiceAPI;
|
||||
use openvk\Web\Models\Entities\{User, Club};
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs, Videos};
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
class Search implements Handler
|
||||
{
|
||||
protected $user;
|
||||
private $users;
|
||||
private $clubs;
|
||||
private $videos;
|
||||
|
||||
function __construct(?User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->users = new Users;
|
||||
$this->clubs = new Clubs;
|
||||
$this->videos = new Videos;
|
||||
}
|
||||
|
||||
function fastSearch(string $query, string $type = "users", callable $resolve, callable $reject)
|
||||
{
|
||||
if($query == "" || strlen($query) < 3)
|
||||
$reject(12, "No input or input < 3");
|
||||
|
||||
$repo;
|
||||
$sort;
|
||||
|
||||
switch($type) {
|
||||
default:
|
||||
case "users":
|
||||
$repo = (new Users);
|
||||
$sort = "rating DESC";
|
||||
|
||||
break;
|
||||
case "groups":
|
||||
$repo = (new Clubs);
|
||||
$sort = "id ASC";
|
||||
|
||||
break;
|
||||
case "videos":
|
||||
$repo = (new Videos);
|
||||
$sort = "created ASC";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$res = $repo->find($query, ["doNotSearchMe" => $this->user->getId()], $sort);
|
||||
|
||||
$results = array_slice(iterator_to_array($res), 0, 5);
|
||||
|
||||
$count = sizeof($results);
|
||||
|
||||
$arr = [
|
||||
"count" => $count,
|
||||
"items" => []
|
||||
];
|
||||
|
||||
if(sizeof($results) < 1) {
|
||||
$reject(2, "No results");
|
||||
}
|
||||
|
||||
foreach($results as $res) {
|
||||
$arr["items"][] = [
|
||||
"id" => $res->getId(),
|
||||
"name" => $type == "users" ? $res->getCanonicalName() : $res->getName(),
|
||||
"avatar" => $type != "videos" ? $res->getAvatarUrl() : $res->getThumbnailURL(),
|
||||
"url" => $type != "videos" ? $res->getUrl() : "/video".$res->getPrettyId(),
|
||||
"description" => ovk_proc_strtr($res->getDescription() ?? "...", 40)
|
||||
];
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
}
|
156
ServiceAPI/Video.php
Normal file
156
ServiceAPI/Video.php
Normal file
|
@ -0,0 +1,156 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\ServiceAPI;
|
||||
|
||||
use openvk\Web\Models\Entities\{User, Post};
|
||||
use openvk\Web\Models\Repositories\{Videos, Comments, Clubs};
|
||||
use Chandler\MVC\Routing\Router;
|
||||
|
||||
class Video implements Handler
|
||||
{
|
||||
protected $user;
|
||||
protected $videos;
|
||||
protected $comments;
|
||||
protected $groups;
|
||||
|
||||
function __construct(?User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->videos = new Videos;
|
||||
$this->comments = new Comments;
|
||||
$this->groups = new Clubs;
|
||||
}
|
||||
|
||||
function getVideo(int $id, callable $resolve, callable $reject)
|
||||
{
|
||||
$video = $this->videos->get($id);
|
||||
|
||||
if(!$video || $video->isDeleted()) {
|
||||
$reject(2, "Video does not exists");
|
||||
}
|
||||
|
||||
if(method_exists($video, "canBeViewedBy") && !$video->canBeViewedBy($this->user)) {
|
||||
$reject(4, "Access to video denied");
|
||||
}
|
||||
|
||||
if(!$video->getOwner()->getPrivacyPermission('videos.read', $this->user)) {
|
||||
$reject(8, "Access to video denied: this user chose to hide his videos");
|
||||
}
|
||||
|
||||
$prevVideo = NULL;
|
||||
$nextVideo = NULL;
|
||||
$lastVideo = $this->videos->getLastVideo($video->getOwner());
|
||||
|
||||
if($video->getVirtualId() - 1 != 0) {
|
||||
for($i = $video->getVirtualId(); $i != 0; $i--) {
|
||||
$maybeVideo = (new Videos)->getByOwnerAndVID($video->getOwner()->getId(), $i);
|
||||
|
||||
if(!is_null($maybeVideo) && !$maybeVideo->isDeleted() && $maybeVideo->getId() != $video->getId()) {
|
||||
if(method_exists($maybeVideo, "canBeViewedBy") && !$maybeVideo->canBeViewedBy($this->user)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$prevVideo = $maybeVideo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(is_null($lastVideo) || $lastVideo->getId() == $video->getId()) {
|
||||
$nextVideo = NULL;
|
||||
} else {
|
||||
for($i = $video->getVirtualId(); $i <= $lastVideo->getVirtualId(); $i++) {
|
||||
$maybeVideo = (new Videos)->getByOwnerAndVID($video->getOwner()->getId(), $i);
|
||||
|
||||
if(!is_null($maybeVideo) && !$maybeVideo->isDeleted() && $maybeVideo->getId() != $video->getId()) {
|
||||
if(method_exists($maybeVideo, "canBeViewedBy") && !$maybeVideo->canBeViewedBy($this->user)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$nextVideo = $maybeVideo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$res = [
|
||||
"id" => $video->getId(),
|
||||
"title" => $video->getName(),
|
||||
"owner" => $video->getOwner()->getId(),
|
||||
"commentsCount" => $video->getCommentsCount(),
|
||||
"description" => $video->getDescription(),
|
||||
"type" => $video->getType(),
|
||||
"name" => $video->getOwner()->getCanonicalName(),
|
||||
"pretty_id" => $video->getPrettyId(),
|
||||
"virtual_id" => $video->getVirtualId(),
|
||||
"published" => (string)$video->getPublicationTime(),
|
||||
"likes" => $video->getLikesCount(),
|
||||
"has_like" => $video->hasLikeFrom($this->user),
|
||||
"author" => $video->getOwner()->getCanonicalName(),
|
||||
"canBeEdited" => $video->getOwner()->getId() == $this->user->getId(),
|
||||
"isProcessing" => $video->getType() == 0 && $video->getURL() == "/assets/packages/static/openvk/video/rendering.mp4",
|
||||
"prevVideo" => !is_null($prevVideo) ? $prevVideo->getId() : null,
|
||||
"nextVideo" => !is_null($nextVideo) ? $nextVideo->getId() : null,
|
||||
];
|
||||
|
||||
if($video->getType() == 1) {
|
||||
$res["embed"] = $video->getVideoDriver()->getEmbed();
|
||||
} else {
|
||||
$res["url"] = $video->getURL();
|
||||
}
|
||||
|
||||
$resolve($res);
|
||||
}
|
||||
|
||||
function shareVideo(int $owner, int $vid, int $type, string $message, int $club, bool $signed, bool $asGroup, callable $resolve, callable $reject)
|
||||
{
|
||||
$video = $this->videos->getByOwnerAndVID($owner, $vid);
|
||||
|
||||
if(!$video || $video->isDeleted()) {
|
||||
$reject(16, "Video does not exists");
|
||||
}
|
||||
|
||||
if(method_exists($video, "canBeViewedBy") && !$video->canBeViewedBy($this->user)) {
|
||||
$reject(32, "Access to video denied");
|
||||
}
|
||||
|
||||
if(!$video->getOwner()->getPrivacyPermission('videos.read', $this->user)) {
|
||||
$reject(8, "Access to video denied: this user chose to hide his videos");
|
||||
}
|
||||
|
||||
$flags = 0;
|
||||
|
||||
$nPost = new Post;
|
||||
$nPost->setOwner($this->user->getId());
|
||||
|
||||
if($type == 0) {
|
||||
$nPost->setWall($this->user->getId());
|
||||
} else {
|
||||
$club = $this->groups->get($club);
|
||||
|
||||
if(!$club || $club->isDeleted() || !$club->canBeModifiedBy($this->user)) {
|
||||
$reject(64, "Can't do repost to this club");
|
||||
}
|
||||
|
||||
if($asGroup)
|
||||
$flags |= 0b10000000;
|
||||
|
||||
if($signed)
|
||||
$flags |= 0b01000000;
|
||||
|
||||
$nPost->setWall($club->getId() * -1);
|
||||
}
|
||||
|
||||
$nPost->setContent($message);
|
||||
$nPost->setFlags($flags);
|
||||
$nPost->save();
|
||||
|
||||
$nPost->attach($video);
|
||||
|
||||
$res = [
|
||||
"id" => $nPost->getId(),
|
||||
"pretty_id" => $nPost->getPrettyId(),
|
||||
];
|
||||
|
||||
$resolve($res);
|
||||
}
|
||||
}
|
|
@ -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,14 +15,21 @@ 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
|
||||
{
|
||||
$post = $this->posts->get($id);
|
||||
if(!$post || $post->isDeleted())
|
||||
$reject("No post with id=$id");
|
||||
$reject(53, "No post with id=$id");
|
||||
|
||||
if($post->getSuggestionType() != 0)
|
||||
$reject(25, "Can't get suggested post");
|
||||
|
||||
if(!$post->canBeViewedBy($this->user))
|
||||
$reject(12, "Access denied");
|
||||
|
||||
$res = (object) [];
|
||||
$res->id = $post->getId();
|
||||
$res->wall = $post->getTargetWall();
|
||||
|
@ -95,4 +102,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($this->user)), 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($this->user)), true);
|
||||
$res["video"]["author_name"] = $video->getOwner()->getCanonicalName();
|
||||
|
||||
$arr["items"][] = $res;
|
||||
}
|
||||
|
||||
$resolve($arr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,19 +7,32 @@ final class Account extends VKAPIRequestHandler
|
|||
function getProfileInfo(): object
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
return (object) [
|
||||
"first_name" => $this->getUser()->getFirstName(),
|
||||
"id" => $this->getUser()->getId(),
|
||||
"last_name" => $this->getUser()->getLastName(),
|
||||
"home_town" => $this->getUser()->getHometown(),
|
||||
"status" => $this->getUser()->getStatus(),
|
||||
"bdate" => is_null($this->getUser()->getBirthday()) ? '01.01.1970' : $this->getUser()->getBirthday()->format('%e.%m.%Y'),
|
||||
"bdate_visibility" => $this->getUser()->getBirthdayPrivacy(),
|
||||
$user = $this->getUser();
|
||||
$return_object = (object) [
|
||||
"first_name" => $user->getFirstName(),
|
||||
"photo_200" => $user->getAvatarURL("normal"),
|
||||
"nickname" => $user->getPseudo(),
|
||||
"is_service_account" => false,
|
||||
"id" => $user->getId(),
|
||||
"is_verified" => $user->isVerified(),
|
||||
"verification_status" => $user->isVerified() ? 'verified' : 'unverified',
|
||||
"last_name" => $user->getLastName(),
|
||||
"home_town" => $user->getHometown(),
|
||||
"status" => $user->getStatus(),
|
||||
"bdate" => is_null($user->getBirthday()) ? '01.01.1970' : $user->getBirthday()->format('%e.%m.%Y'),
|
||||
"bdate_visibility" => $user->getBirthdayPrivacy(),
|
||||
"phone" => "+420 ** *** 228", # TODO
|
||||
"relation" => $this->getUser()->getMaritalStatus(),
|
||||
"sex" => $this->getUser()->isFemale() ? 1 : 2
|
||||
"relation" => $user->getMaritalStatus(),
|
||||
"screen_name" => $user->getShortCode(),
|
||||
"sex" => $user->isFemale() ? 1 : 2,
|
||||
#"email" => $user->getEmail(),
|
||||
];
|
||||
|
||||
$audio_status = $user->getCurrentAudioStatus();
|
||||
if(!is_null($audio_status))
|
||||
$return_object->audio_status = $audio_status->toVkApiStruct($user);
|
||||
|
||||
return $return_object;
|
||||
}
|
||||
|
||||
function getInfo(): object
|
||||
|
@ -151,4 +164,30 @@ final class Account extends VKAPIRequestHandler
|
|||
|
||||
return (object) $output;
|
||||
}
|
||||
|
||||
function getBalance(): object
|
||||
{
|
||||
$this->requireUser();
|
||||
if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce'])
|
||||
$this->fail(105, "Commerce is disabled on this instance");
|
||||
|
||||
return (object) ['votes' => $this->getUser()->getCoins()];
|
||||
}
|
||||
|
||||
function getOvkSettings(): object
|
||||
{
|
||||
$this->requireUser();
|
||||
$user = $this->getUser();
|
||||
|
||||
$settings_list = (object)[
|
||||
'avatar_style' => $user->getStyleAvatar(),
|
||||
'style' => $user->getStyle(),
|
||||
'show_rating' => !$user->prefersNotToSeeRating(),
|
||||
'nsfw_tolerance' => $user->getNsfwTolerance(),
|
||||
'post_view' => $user->hasMicroblogEnabled() ? 'microblog' : 'old',
|
||||
'main_page' => $user->getMainPage() == 0 ? 'my_page' : 'news',
|
||||
];
|
||||
|
||||
return $settings_list;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,793 @@
|
|||
<?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, int $order = 0, int $from_me = 0): object
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$playlists = [];
|
||||
$params = [];
|
||||
$order_str = (['id', 'length', 'listens'][$order] ?? 'id');
|
||||
if($from_me === 1)
|
||||
$params['from_me'] = $this->getUser()->getId();
|
||||
|
||||
$search = (new Audios)->findPlaylists($query, $params, ['type' => $order_str, 'invert' => false]);
|
||||
foreach($search->offsetLimit($offset, $limit) as $playlist) {
|
||||
if(!$playlist->canBeViewedBy($this->getUser())) {
|
||||
if($drop_private == 0)
|
||||
$playlists[] = NULL;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$playlists[] = $playlist->toVkApiStruct($this->getUser());
|
||||
}
|
||||
|
||||
return (object) [
|
||||
"count" => $search->size(),
|
||||
"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,11 +13,23 @@ final class Friends extends VKAPIRequestHandler
|
|||
$users = new UsersRepo;
|
||||
|
||||
$this->requireUser();
|
||||
|
||||
foreach($users->get($user_id)->getFriends($offset, $count) as $friend) {
|
||||
$friends[$i] = $friend->getId();
|
||||
$i++;
|
||||
|
||||
if ($user_id == 0) {
|
||||
$user_id = $this->getUser()->getId();
|
||||
}
|
||||
|
||||
$user = $users->get($user_id);
|
||||
|
||||
if(!$user || $user->isDeleted())
|
||||
$this->fail(100, "Invalid user");
|
||||
|
||||
if(!$user->getPrivacyPermission("friends.read", $this->getUser()))
|
||||
$this->fail(15, "Access denied: this user chose to hide his friends.");
|
||||
|
||||
foreach($user->getFriends($offset, $count) as $friend) {
|
||||
$friends[$i] = $friend->getId();
|
||||
$i++;
|
||||
}
|
||||
|
||||
$response = $friends;
|
||||
|
||||
|
@ -135,7 +147,7 @@ final class Friends extends VKAPIRequestHandler
|
|||
return $response;
|
||||
}
|
||||
|
||||
function getRequests(string $fields = "", int $offset = 0, int $count = 100, int $extended = 0): object
|
||||
function getRequests(string $fields = "", int $out = 0, int $offset = 0, int $count = 100, int $extended = 0): object
|
||||
{
|
||||
if ($count >= 1000)
|
||||
$this->fail(100, "One of the required parameters was not passed or is invalid.");
|
||||
|
@ -146,9 +158,18 @@ final class Friends extends VKAPIRequestHandler
|
|||
$offset++;
|
||||
$followers = [];
|
||||
|
||||
foreach($this->getUser()->getFollowers($offset, $count) as $follower) {
|
||||
$followers[$i] = $follower->getId();
|
||||
$i++;
|
||||
if ($out != 0) {
|
||||
foreach($this->getUser()->getFollowers($offset, $count) as $follower) {
|
||||
$followers[$i] = $follower->getId();
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach($this->getUser()->getRequests($offset, $count) as $follower) {
|
||||
$followers[$i] = $follower->getId();
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
$response = $followers;
|
||||
|
|
|
@ -6,19 +6,33 @@ use openvk\Web\Models\Entities\Notifications\GiftNotification;
|
|||
|
||||
final class Gifts extends VKAPIRequestHandler
|
||||
{
|
||||
function get(int $user_id, int $count = 10, int $offset = 0)
|
||||
function get(int $user_id = NULL, int $count = 10, int $offset = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$i = 0;
|
||||
|
||||
$i += $offset;
|
||||
$server_url = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
|
||||
|
||||
$user = (new UsersRepo)->get($user_id);
|
||||
if($user_id)
|
||||
$user = (new UsersRepo)->get($user_id);
|
||||
else
|
||||
$user = $this->getUser();
|
||||
|
||||
if(!$user || $user->isDeleted())
|
||||
$this->fail(177, "Invalid user");
|
||||
|
||||
if(!$user->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
/*
|
||||
if(!$user->getPrivacyPermission('gifts.read', $this->getUser()))
|
||||
$this->fail(15, "Access denied: this user chose to hide his gifts");*/
|
||||
|
||||
|
||||
if(!$user->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
$gift_item = [];
|
||||
|
||||
$userGifts = array_slice(iterator_to_array($user->getGifts(1, $count, false)), $offset);
|
||||
|
@ -36,9 +50,9 @@ final class Gifts extends VKAPIRequestHandler
|
|||
"date" => $gift->sent->timestamp(),
|
||||
"gift" => [
|
||||
"id" => $gift->gift->getId(),
|
||||
"thumb_256" => $gift->gift->getImage(2),
|
||||
"thumb_96" => $gift->gift->getImage(2),
|
||||
"thumb_48" => $gift->gift->getImage(2)
|
||||
"thumb_256" => $server_url. $gift->gift->getImage(2),
|
||||
"thumb_96" => $server_url . $gift->gift->getImage(2),
|
||||
"thumb_48" => $server_url . $gift->gift->getImage(2)
|
||||
],
|
||||
"privacy" => 0
|
||||
];
|
||||
|
@ -62,6 +76,9 @@ final class Gifts extends VKAPIRequestHandler
|
|||
if(!$user || $user->isDeleted())
|
||||
$this->fail(177, "Invalid user");
|
||||
|
||||
if(!$user->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
$gift = (new GiftsRepo)->get($gift_id);
|
||||
|
||||
if(!$gift)
|
||||
|
@ -111,12 +128,13 @@ final class Gifts extends VKAPIRequestHandler
|
|||
$this->fail(501, "Not implemented");
|
||||
}
|
||||
|
||||
# этих методов не было в ВК, но я их добавил чтобы можно было отобразить список подарков
|
||||
# в vk кстати называется gifts.getCatalog
|
||||
function getCategories(bool $extended = false, int $page = 1)
|
||||
{
|
||||
$cats = (new GiftsRepo)->getCategories($page);
|
||||
$categ = [];
|
||||
$i = 0;
|
||||
$server_url = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
|
||||
|
||||
if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce'])
|
||||
$this->fail(105, "Commerce is disabled on this instance");
|
||||
|
@ -126,8 +144,8 @@ final class Gifts extends VKAPIRequestHandler
|
|||
"name" => $cat->getName(),
|
||||
"description" => $cat->getDescription(),
|
||||
"id" => $cat->getId(),
|
||||
"thumbnail" => $cat->getThumbnailURL(),
|
||||
];
|
||||
"thumbnail" => $server_url . $cat->getThumbnailURL(),
|
||||
];
|
||||
|
||||
if($extended == true) {
|
||||
$categ[$i]["localizations"] = [];
|
||||
|
@ -164,7 +182,7 @@ final class Gifts extends VKAPIRequestHandler
|
|||
"name" => $gift->getName(),
|
||||
"image" => $gift->getImage(2),
|
||||
"usages_left" => (int)$gift->getUsagesLeft($this->getUser()),
|
||||
"price" => $gift->getPrice(), # голосов
|
||||
"price" => $gift->getPrice(),
|
||||
"is_free" => $gift->isFree()
|
||||
];
|
||||
}
|
||||
|
|
|
@ -2,26 +2,30 @@
|
|||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
||||
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 {
|
||||
$users = new UsersRepo;
|
||||
$user = $users->get($user_id);
|
||||
|
||||
if(is_null($user))
|
||||
if(is_null($user) || $user->isDeleted())
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
foreach($user->getClubs($offset, false, $count, true) as $club)
|
||||
if(!$user->getPrivacyPermission('groups.read', $this->getUser()))
|
||||
$this->fail(15, "Access denied: this user chose to hide his groups.");
|
||||
|
||||
foreach($user->getClubs($offset, $filter == "admin", $count, true) as $club)
|
||||
$clbs[] = $club;
|
||||
|
||||
$clbsCount = $user->getClubCount();
|
||||
|
@ -80,6 +84,23 @@ final class Groups extends VKAPIRequestHandler
|
|||
break;
|
||||
case "members_count":
|
||||
$rClubs[$i]->members_count = $usr->getFollowersCount();
|
||||
break;
|
||||
case "can_suggest":
|
||||
$rClubs[$i]->can_suggest = !$usr->canBeModifiedBy($this->getUser()) && $usr->getWallType() == 2;
|
||||
break;
|
||||
case "background":
|
||||
$backgrounds = $usr->getBackDropPictureURLs();
|
||||
$rClubs[$i]->background = $backgrounds;
|
||||
break;
|
||||
# unstandard feild
|
||||
case "suggested_count":
|
||||
if($usr->getWallType() != 2) {
|
||||
$rClubs[$i]->suggested_count = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
$rClubs[$i]->suggested_count = $usr->getSuggestedPostsCount($this->getUser());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +209,23 @@ final class Groups extends VKAPIRequestHandler
|
|||
case "description":
|
||||
$response[$i]->description = $clb->getDescription();
|
||||
break;
|
||||
case "contacts":
|
||||
case "can_suggest":
|
||||
$response[$i]->can_suggest = !$clb->canBeModifiedBy($this->getUser()) && $clb->getWallType() == 2;
|
||||
break;
|
||||
case "background":
|
||||
$backgrounds = $clb->getBackDropPictureURLs();
|
||||
$response[$i]->background = $backgrounds;
|
||||
break;
|
||||
# unstandard feild
|
||||
case "suggested_count":
|
||||
if($clb->getWallType() != 2) {
|
||||
$response[$i]->suggested_count = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->suggested_count = $clb->getSuggestedPostsCount($this->getUser());
|
||||
break;
|
||||
case "contacts":
|
||||
$contacts;
|
||||
$contactTmp = $clb->getManagers(1, true);
|
||||
|
||||
|
@ -215,23 +252,30 @@ final class Groups extends VKAPIRequestHandler
|
|||
return $response;
|
||||
}
|
||||
|
||||
function search(string $q, int $offset = 0, int $count = 100)
|
||||
function search(string $q, int $offset = 0, int $count = 100, string $fields = "screen_name,is_admin,is_member,is_advertiser,photo_50,photo_100,photo_200")
|
||||
{
|
||||
if($count > 100) {
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: count should be less or equal to 100");
|
||||
}
|
||||
|
||||
$clubs = new ClubsRepo;
|
||||
|
||||
$array = [];
|
||||
$find = $clubs->find($q);
|
||||
|
||||
foreach ($find as $group)
|
||||
foreach ($find->offsetLimit($offset, $count) as $group)
|
||||
$array[] = $group->getId();
|
||||
|
||||
if(!$array || sizeof($array) < 1) {
|
||||
return (object) [
|
||||
"count" => 0,
|
||||
"items" => [],
|
||||
];
|
||||
}
|
||||
|
||||
return (object) [
|
||||
"count" => $find->size(),
|
||||
"items" => $this->getById(implode(',', $array), "", "is_admin,is_member,is_advertiser,photo_50,photo_100,photo_200", $offset, $count)
|
||||
/*
|
||||
* As there is no thing as "fields" by the original documentation
|
||||
* i'll just bake this param by the example shown here: https://dev.vk.com/method/groups.search
|
||||
*/
|
||||
"items" => $this->getById(implode(',', $array), "", $fields)
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -288,11 +332,12 @@ final class Groups extends VKAPIRequestHandler
|
|||
string $description = NULL,
|
||||
string $screen_name = NULL,
|
||||
string $website = NULL,
|
||||
int $wall = NULL,
|
||||
int $wall = -1,
|
||||
int $topics = NULL,
|
||||
int $adminlist = NULL,
|
||||
int $topicsAboveWall = NULL,
|
||||
int $hideFromGlobalFeed = NULL)
|
||||
int $hideFromGlobalFeed = NULL,
|
||||
int $audio = NULL)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
@ -303,17 +348,34 @@ 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;
|
||||
|
||||
try {
|
||||
$wall != -1 ? $club->setWall($wall) : NULL;
|
||||
} catch(\Exception $e) {
|
||||
$this->fail(50, "Invalid wall value");
|
||||
}
|
||||
|
||||
!empty($topics) ? $club->setEveryone_Can_Create_Topics($topics) : NULL;
|
||||
!empty($adminlist) ? $club->setAdministrators_List_Display($adminlist) : NULL;
|
||||
!empty($topicsAboveWall) ? $club->setDisplay_Topics_Above_Wall($topicsAboveWall) : NULL;
|
||||
|
||||
$club->save();
|
||||
if (!$club->isHidingFromGlobalFeedEnforced()) {
|
||||
!empty($hideFromGlobalFeed) ? $club->setHide_From_Global_Feed($hideFromGlobalFeed) : NULL;
|
||||
}
|
||||
|
||||
in_array($audio, [0, 1]) ? $club->setEveryone_can_upload_audios($audio) : NULL;
|
||||
|
||||
try {
|
||||
$club->save();
|
||||
} catch(\TypeError $e) {
|
||||
$this->fail(15, "Nothing changed");
|
||||
} catch(\Exception $e) {
|
||||
$this->fail(18, "An unknown error occurred: maybe you set an incorrect value?");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -359,9 +421,15 @@ final class Groups extends VKAPIRequestHandler
|
|||
];
|
||||
|
||||
foreach($filds as $fild) {
|
||||
$canView = $member->canBeViewedBy($this->getUser());
|
||||
switch($fild) {
|
||||
case "bdate":
|
||||
$arr->items[$i]->bdate = $member->getBirthday()->format('%e.%m.%Y');
|
||||
if(!$canView) {
|
||||
$arr->items[$i]->bdate = "01.01.1970";
|
||||
break;
|
||||
}
|
||||
|
||||
$arr->items[$i]->bdate = $member->getBirthday() ? $member->getBirthday()->format('%e.%m.%Y') : NULL;
|
||||
break;
|
||||
case "can_post":
|
||||
$arr->items[$i]->can_post = $club->canBeModifiedBy($member);
|
||||
|
@ -370,7 +438,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;
|
||||
|
@ -382,6 +450,11 @@ final class Groups extends VKAPIRequestHandler
|
|||
$arr->items[$i]->connections = 1;
|
||||
break;
|
||||
case "contacts":
|
||||
if(!$canView) {
|
||||
$arr->items[$i]->contacts = "secret@gmail.com";
|
||||
break;
|
||||
}
|
||||
|
||||
$arr->items[$i]->contacts = $member->getContactEmail();
|
||||
break;
|
||||
case "country":
|
||||
|
@ -397,15 +470,30 @@ final class Groups extends VKAPIRequestHandler
|
|||
$arr->items[$i]->has_mobile = false;
|
||||
break;
|
||||
case "last_seen":
|
||||
if(!$canView) {
|
||||
$arr->items[$i]->last_seen = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
$arr->items[$i]->last_seen = $member->getOnline()->timestamp();
|
||||
break;
|
||||
case "lists":
|
||||
$arr->items[$i]->lists = "";
|
||||
break;
|
||||
case "online":
|
||||
if(!$canView) {
|
||||
$arr->items[$i]->online = false;
|
||||
break;
|
||||
}
|
||||
|
||||
$arr->items[$i]->online = $member->isOnline();
|
||||
break;
|
||||
case "online_mobile":
|
||||
if(!$canView) {
|
||||
$arr->items[$i]->online_mobile = false;
|
||||
break;
|
||||
}
|
||||
|
||||
$arr->items[$i]->online_mobile = $member->getOnlinePlatform() == "android" || $member->getOnlinePlatform() == "iphone" || $member->getOnlinePlatform() == "mobile";
|
||||
break;
|
||||
case "photo_100":
|
||||
|
@ -436,12 +524,27 @@ final class Groups extends VKAPIRequestHandler
|
|||
$arr->items[$i]->schools = 0;
|
||||
break;
|
||||
case "sex":
|
||||
if(!$canView) {
|
||||
$arr->items[$i]->sex = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
$arr->items[$i]->sex = $member->isFemale() ? 1 : 2;
|
||||
break;
|
||||
case "site":
|
||||
if(!$canView) {
|
||||
$arr->items[$i]->site = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
$arr->items[$i]->site = $member->getWebsite();
|
||||
break;
|
||||
case "status":
|
||||
if(!$canView) {
|
||||
$arr->items[$i]->status = "r";
|
||||
break;
|
||||
}
|
||||
|
||||
$arr->items[$i]->status = $member->getStatus();
|
||||
break;
|
||||
case "universities":
|
||||
|
@ -466,10 +569,10 @@ final class Groups extends VKAPIRequestHandler
|
|||
"title" => $club->getName(),
|
||||
"description" => $club->getDescription() != NULL ? $club->getDescription() : "",
|
||||
"address" => $club->getShortcode(),
|
||||
"wall" => $club->canPost() == true ? 1 : 0,
|
||||
"wall" => $club->getWallType(), # отличается от вкшных но да ладно
|
||||
"photos" => 1,
|
||||
"video" => 0,
|
||||
"audio" => 0,
|
||||
"audio" => $club->isEveryoneCanUploadAudios() ? 1 : 0,
|
||||
"docs" => 0,
|
||||
"topics" => $club->isEveryoneCanCreateTopics() == true ? 1 : 0,
|
||||
"wiki" => 0,
|
||||
|
|
|
@ -2,70 +2,205 @@
|
|||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
||||
use openvk\Web\Models\Repositories\Comments as CommentsRepo;
|
||||
use openvk\Web\Models\Repositories\Videos as VideosRepo;
|
||||
use openvk\Web\Models\Repositories\Photos as PhotosRepo;
|
||||
use openvk\Web\Models\Repositories\Notes as NotesRepo;
|
||||
|
||||
|
||||
final class Likes extends VKAPIRequestHandler
|
||||
{
|
||||
function add(string $type, int $owner_id, int $item_id): object
|
||||
{
|
||||
$this->requireUser();
|
||||
function add(string $type, int $owner_id, int $item_id): object
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$postable = NULL;
|
||||
switch($type) {
|
||||
case "post":
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||
if(is_null($post))
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
||||
|
||||
$post->setLike(true, $this->getUser());
|
||||
|
||||
return (object) [
|
||||
"likes" => $post->getLikesCount()
|
||||
];
|
||||
$postable = $post;
|
||||
break;
|
||||
case "comment":
|
||||
$comment = (new CommentsRepo)->get($item_id);
|
||||
$postable = $comment;
|
||||
break;
|
||||
case "video":
|
||||
$video = (new VideosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||
$postable = $video;
|
||||
break;
|
||||
case "photo":
|
||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||
$postable = $photo;
|
||||
break;
|
||||
case "note":
|
||||
$note = (new NotesRepo)->getNoteById($owner_id, $item_id);
|
||||
$postable = $note;
|
||||
break;
|
||||
default:
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: incorrect type");
|
||||
}
|
||||
}
|
||||
|
||||
function delete(string $type, int $owner_id, int $item_id): object
|
||||
{
|
||||
$this->requireUser();
|
||||
if(is_null($postable) || $postable->isDeleted())
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
||||
|
||||
if(!$postable->canBeViewedBy($this->getUser() ?? NULL)) {
|
||||
$this->fail(2, "Access to postable denied");
|
||||
}
|
||||
|
||||
$postable->setLike(true, $this->getUser());
|
||||
|
||||
return (object) [
|
||||
"likes" => $postable->getLikesCount()
|
||||
];
|
||||
}
|
||||
|
||||
function delete(string $type, int $owner_id, int $item_id): object
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$postable = NULL;
|
||||
switch($type) {
|
||||
case "post":
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||
if (is_null($post))
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
||||
|
||||
$post->setLike(false, $this->getUser());
|
||||
return (object) [
|
||||
"likes" => $post->getLikesCount()
|
||||
];
|
||||
$postable = $post;
|
||||
break;
|
||||
case "comment":
|
||||
$comment = (new CommentsRepo)->get($item_id);
|
||||
$postable = $comment;
|
||||
break;
|
||||
case "video":
|
||||
$video = (new VideosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||
$postable = $video;
|
||||
break;
|
||||
case "photo":
|
||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||
$postable = $photo;
|
||||
break;
|
||||
case "note":
|
||||
$note = (new NotesRepo)->getNoteById($owner_id, $item_id);
|
||||
$postable = $note;
|
||||
break;
|
||||
default:
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: incorrect type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(is_null($postable) || $postable->isDeleted())
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
||||
|
||||
if(!$postable->canBeViewedBy($this->getUser() ?? NULL)) {
|
||||
$this->fail(2, "Access to postable denied");
|
||||
}
|
||||
|
||||
if(!is_null($postable)) {
|
||||
$postable->setLike(false, $this->getUser());
|
||||
|
||||
return (object) [
|
||||
"likes" => $postable->getLikesCount()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function isLiked(int $user_id, string $type, int $owner_id, int $item_id): object
|
||||
{
|
||||
$this->requireUser();
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$user = (new UsersRepo)->get($user_id);
|
||||
|
||||
if(is_null($user) || $user->isDeleted())
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: user not found");
|
||||
|
||||
if(!$user->canBeViewedBy($this->getUser())) {
|
||||
$this->fail(1984, "Access denied: you can't see this user");
|
||||
}
|
||||
|
||||
$postable = NULL;
|
||||
switch($type) {
|
||||
case "post":
|
||||
$user = (new UsersRepo)->get($user_id);
|
||||
if (is_null($user))
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: user not found");
|
||||
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||
if (is_null($post))
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
||||
|
||||
return (object) [
|
||||
"liked" => (int) $post->hasLikeFrom($user),
|
||||
"copied" => 0 # TODO: handle this
|
||||
];
|
||||
$postable = $post;
|
||||
break;
|
||||
case "comment":
|
||||
$comment = (new CommentsRepo)->get($item_id);
|
||||
$postable = $comment;
|
||||
break;
|
||||
case "video":
|
||||
$video = (new VideosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||
$postable = $video;
|
||||
break;
|
||||
case "photo":
|
||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||
$postable = $photo;
|
||||
break;
|
||||
case "note":
|
||||
$note = (new NotesRepo)->getNoteById($owner_id, $item_id);
|
||||
$postable = $note;
|
||||
break;
|
||||
default:
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: incorrect type");
|
||||
}
|
||||
|
||||
if(is_null($postable) || $postable->isDeleted())
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
||||
|
||||
if(!$postable->canBeViewedBy($this->getUser())) {
|
||||
$this->fail(665, "Access to postable denied");
|
||||
}
|
||||
|
||||
return (object) [
|
||||
"liked" => (int) $postable->hasLikeFrom($user),
|
||||
"copied" => 0
|
||||
];
|
||||
}
|
||||
|
||||
function getList(string $type, int $owner_id, int $item_id, bool $extended = false, int $offset = 0, int $count = 10, bool $skip_own = false)
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$object = NULL;
|
||||
|
||||
switch($type) {
|
||||
case "post":
|
||||
$object = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||
break;
|
||||
case "comment":
|
||||
$object = (new CommentsRepo)->get($item_id);
|
||||
break;
|
||||
case "photo":
|
||||
$object = (new PhotosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||
break;
|
||||
case "video":
|
||||
$object = (new VideosRepo)->getByOwnerAndVID($owner_id, $item_id);
|
||||
break;
|
||||
default:
|
||||
$this->fail(58, "Invalid type");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!$object || $object->isDeleted())
|
||||
$this->fail(56, "Invalid postable");
|
||||
|
||||
if(!$object->canBeViewedBy($this->getUser()))
|
||||
$this->fail(665, "Access to postable denied");
|
||||
|
||||
$res = (object)[
|
||||
"count" => $object->getLikesCount(),
|
||||
"items" => []
|
||||
];
|
||||
|
||||
$likers = array_slice(iterator_to_array($object->getLikers(1, $offset + $count)), $offset);
|
||||
|
||||
foreach($likers as $liker) {
|
||||
if($skip_own && $liker->getId() == $this->getUser()->getId())
|
||||
continue;
|
||||
|
||||
if(!$extended)
|
||||
$res->items[] = $liker->getId();
|
||||
else
|
||||
$res->items[] = $liker->toVkApiStruct();
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,8 @@ final class Messages extends VKAPIRequestHandler
|
|||
];
|
||||
}
|
||||
|
||||
function send(int $user_id = -1, int $peer_id = -1, string $domain = "", int $chat_id = -1, string $user_ids = "", string $message = "", int $sticker_id = -1, int $forGodSakePleaseDoNotReportAboutMyOnlineActivity = 0)
|
||||
function send(int $user_id = -1, int $peer_id = -1, string $domain = "", int $chat_id = -1, string $user_ids = "", string $message = "", int $sticker_id = -1, int $forGodSakePleaseDoNotReportAboutMyOnlineActivity = 0,
|
||||
string $attachment = "") # интересно почему не attachments
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,8 @@ final class Newsfeed extends VKAPIRequestHandler
|
|||
{
|
||||
$this->requireUser();
|
||||
|
||||
$queryBase = "FROM `posts` LEFT JOIN `groups` ON GREATEST(`posts`.`wall`, 0) = 0 AND `groups`.`id` = ABS(`posts`.`wall`) WHERE (`groups`.`hide_from_global_feed` = 0 OR `groups`.`name` IS NULL) AND `posts`.`deleted` = 0";
|
||||
$queryBase = "FROM `posts` LEFT JOIN `groups` ON GREATEST(`posts`.`wall`, 0) = 0 AND `groups`.`id` = ABS(`posts`.`wall`) LEFT JOIN `profiles` ON LEAST(`posts`.`wall`, 0) = 0 AND `profiles`.`id` = ABS(`posts`.`wall`)";
|
||||
$queryBase .= "WHERE (`groups`.`hide_from_global_feed` = 0 OR `groups`.`name` IS NULL) AND (`profiles`.`profile_type` = 0 OR `profiles`.`first_name` IS NULL) AND `posts`.`deleted` = 0 AND `posts`.`suggested` = 0";
|
||||
|
||||
if($this->getUser()->getNsfwTolerance() === User::NSFW_INTOLERANT)
|
||||
$queryBase .= " AND `nsfw` = 0";
|
||||
|
|
|
@ -40,6 +40,9 @@ final class Notes extends VKAPIRequestHandler
|
|||
if($note->getOwner()->isDeleted())
|
||||
$this->fail(403, "Owner is deleted");
|
||||
|
||||
if(!$note->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
||||
$this->fail(43, "No access");
|
||||
|
||||
|
@ -118,21 +121,6 @@ final class Notes extends VKAPIRequestHandler
|
|||
return 1;
|
||||
}
|
||||
|
||||
function deleteComment(int $comment_id, int $owner_id = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id);
|
||||
|
||||
if(!$comment || !$comment->canBeDeletedBy($this->getUser()))
|
||||
$this->fail(403, "Access to comment denied");
|
||||
|
||||
$comment->delete();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function edit(string $note_id, string $title = "", string $text = "", int $privacy = 0, int $comment_privacy = 0, string $privacy_view = "", string $privacy_comment = "")
|
||||
{
|
||||
$this->requireUser();
|
||||
|
@ -159,25 +147,6 @@ final class Notes extends VKAPIRequestHandler
|
|||
return 1;
|
||||
}
|
||||
|
||||
function editComment(int $comment_id, string $message, int $owner_id = NULL)
|
||||
{
|
||||
/*
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id);
|
||||
|
||||
if($comment->getOwner() != $this->getUser()->getId())
|
||||
$this->fail(15, "Access to comment denied");
|
||||
|
||||
$comment->setContent($message);
|
||||
$comment->setEdited(time());
|
||||
$comment->save();
|
||||
*/
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function get(int $user_id, string $note_ids = "", int $offset = 0, int $count = 10, int $sort = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
|
@ -187,7 +156,10 @@ final class Notes extends VKAPIRequestHandler
|
|||
$this->fail(15, "Invalid user");
|
||||
|
||||
if(!$user->getPrivacyPermission('notes.read', $this->getUser()))
|
||||
$this->fail(43, "Access denied: this user chose to hide his notes");
|
||||
$this->fail(15, "Access denied: this user chose to hide his notes");
|
||||
|
||||
if(!$user->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
if(empty($note_ids)) {
|
||||
$notes = array_slice(iterator_to_array((new NotesRepo)->getUserNotes($user, 1, $count + $offset, $sort == 0 ? "ASC" : "DESC")), $offset);
|
||||
|
@ -211,7 +183,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();
|
||||
}
|
||||
}
|
||||
|
@ -238,6 +210,9 @@ final class Notes extends VKAPIRequestHandler
|
|||
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
||||
$this->fail(40, "Access denied: this user chose to hide his notes");
|
||||
|
||||
if(!$note->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access to note denied");
|
||||
|
||||
return $note->toVkApiStruct();
|
||||
}
|
||||
|
||||
|
@ -258,6 +233,9 @@ final class Notes extends VKAPIRequestHandler
|
|||
|
||||
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
|
||||
$this->fail(14, "No access");
|
||||
|
||||
if(!$note->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access to note denied");
|
||||
|
||||
$arr = (object) [
|
||||
"count" => $note->getCommentsCount(),
|
||||
|
|
83
VKAPI/Handlers/Notifications.php
Normal file
83
VKAPI/Handlers/Notifications.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
use openvk\Web\Models\Repositories\{Notifications as Notifs, Clubs, Users};
|
||||
|
||||
final class Notifications extends VKAPIRequestHandler
|
||||
{
|
||||
function get(int $count = 10,
|
||||
string $from = "",
|
||||
int $offset = 0,
|
||||
string $start_from = "",
|
||||
string $filters = "",
|
||||
int $start_time = 0,
|
||||
int $end_time = 0,
|
||||
int $archived = 0)
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$res = (object)[
|
||||
"items" => [],
|
||||
"profiles" => [],
|
||||
"groups" => [],
|
||||
"last_viewed" => $this->getUser()->getNotificationOffset()
|
||||
];
|
||||
|
||||
if($count > 100)
|
||||
$this->fail(125, "Count is too big");
|
||||
|
||||
if(!eventdb())
|
||||
$this->fail(1289, "EventDB is disabled on this instance");
|
||||
|
||||
$notifs = array_slice(iterator_to_array((new Notifs)->getNotificationsByUser($this->getUser(), $this->getUser()->getNotificationOffset(), (bool)$archived, 1, $offset + $count)), $offset);
|
||||
$tmpProfiles = [];
|
||||
foreach($notifs as $notif) {
|
||||
$sxModel = $notif->getModel(1);
|
||||
|
||||
if(!method_exists($sxModel, "getAvatarUrl"))
|
||||
$sxModel = $notif->getModel(0);
|
||||
|
||||
|
||||
$tmpProfiles[] = $sxModel instanceof Club ? $sxModel->getId() * -1 : $sxModel->getId();
|
||||
$res->items[] = $notif->toVkApiStruct();
|
||||
}
|
||||
|
||||
foreach(array_unique($tmpProfiles) as $id) {
|
||||
if($id > 0) {
|
||||
$sxModel = (new Users)->get($id);
|
||||
$result = (object)[
|
||||
"uid" => $sxModel->getId(),
|
||||
"first_name" => $sxModel->getFirstName(),
|
||||
"last_name" => $sxModel->getLastName(),
|
||||
"photo" => $sxModel->getAvatarUrl(),
|
||||
"photo_medium_rec" => $sxModel->getAvatarUrl("tiny"),
|
||||
"screen_name" => $sxModel->getShortCode()
|
||||
];
|
||||
|
||||
$res->profiles[] = $result;
|
||||
} else {
|
||||
$sxModel = (new Clubs)->get(abs($id));
|
||||
$result = $sxModel->toVkApiStruct($this->getUser());
|
||||
|
||||
$res->groups[] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
function markAsViewed()
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
try {
|
||||
$this->getUser()->updateNotificationOffset();
|
||||
$this->getUser()->save();
|
||||
} catch(\Throwable $e) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -304,7 +304,6 @@ final class Photos extends VKAPIRequestHandler
|
|||
if(!$user || $user->isDeleted())
|
||||
$this->fail(2, "Invalid user");
|
||||
|
||||
|
||||
if(!$user->getPrivacyPermission('photos.read', $this->getUser()))
|
||||
$this->fail(21, "This user chose to hide his albums.");
|
||||
|
||||
|
@ -363,26 +362,21 @@ final class Photos extends VKAPIRequestHandler
|
|||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if($user_id == 0 && $group_id == 0 || $user_id > 0 && $group_id > 0) {
|
||||
if($user_id == 0 && $group_id == 0 || $user_id > 0 && $group_id > 0)
|
||||
$this->fail(21, "Select user_id or group_id");
|
||||
}
|
||||
|
||||
if($user_id > 0) {
|
||||
|
||||
$us = (new UsersRepo)->get($user_id);
|
||||
if(!$us || $us->isDeleted()) {
|
||||
if(!$us || $us->isDeleted())
|
||||
$this->fail(21, "Invalid user");
|
||||
}
|
||||
|
||||
if(!$us->getPrivacyPermission('photos.read', $this->getUser())) {
|
||||
if(!$us->getPrivacyPermission('photos.read', $this->getUser()))
|
||||
$this->fail(21, "This user chose to hide his albums.");
|
||||
}
|
||||
|
||||
return (new Albums)->getUserAlbumsCount($us);
|
||||
}
|
||||
|
||||
if($group_id > 0)
|
||||
{
|
||||
if($group_id > 0) {
|
||||
$cl = (new Clubs)->get($group_id);
|
||||
if(!$cl) {
|
||||
$this->fail(21, "Invalid club");
|
||||
|
@ -404,17 +398,11 @@ final class Photos extends VKAPIRequestHandler
|
|||
$ph = explode("_", $phota);
|
||||
$photo = (new PhotosRepo)->getByOwnerAndVID((int)$ph[0], (int)$ph[1]);
|
||||
|
||||
if(!$photo || $photo->isDeleted()) {
|
||||
if(!$photo || $photo->isDeleted())
|
||||
$this->fail(21, "Invalid photo");
|
||||
}
|
||||
|
||||
if($photo->getOwner()->isDeleted()) {
|
||||
$this->fail(21, "Owner of this photo is deleted");
|
||||
}
|
||||
|
||||
if(!$photo->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) {
|
||||
$this->fail(21, "This user chose to hide his photos.");
|
||||
}
|
||||
if(!$photo->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
$res[] = $photo->toVkApiStruct($photo_sizes, $extended);
|
||||
}
|
||||
|
@ -432,13 +420,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->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
$photos = array_slice(iterator_to_array($album->getPhotos(1, $count + $offset)), $offset);
|
||||
$res["count"] = sizeof($photos);
|
||||
|
@ -456,12 +442,11 @@ 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]);
|
||||
if($phot && !$phot->isDeleted()) {
|
||||
if($phot && !$phot->isDeleted() && $phot->canBeViewedBy($this->getUser())) {
|
||||
$res["items"][] = $phot->toVkApiStruct($photo_sizes, $extended);
|
||||
}
|
||||
}
|
||||
|
@ -477,13 +462,11 @@ final class Photos extends VKAPIRequestHandler
|
|||
|
||||
$album = (new Albums)->get($album_id);
|
||||
|
||||
if(!$album || $album->canBeModifiedBy($this->getUser())) {
|
||||
if(!$album || $album->canBeModifiedBy($this->getUser()))
|
||||
$this->fail(21, "Invalid album");
|
||||
}
|
||||
|
||||
if($album->isDeleted()) {
|
||||
if($album->isDeleted())
|
||||
$this->fail(22, "Album already deleted");
|
||||
}
|
||||
|
||||
$album->delete();
|
||||
|
||||
|
@ -497,13 +480,11 @@ final class Photos extends VKAPIRequestHandler
|
|||
|
||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
||||
|
||||
if(!$photo) {
|
||||
if(!$photo)
|
||||
$this->fail(21, "Invalid photo");
|
||||
}
|
||||
|
||||
if($photo->isDeleted()) {
|
||||
if($photo->isDeleted())
|
||||
$this->fail(21, "Photo is deleted");
|
||||
}
|
||||
|
||||
if(!empty($caption)) {
|
||||
$photo->setDescription($caption);
|
||||
|
@ -521,17 +502,14 @@ final class Photos extends VKAPIRequestHandler
|
|||
if(empty($photos)) {
|
||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
||||
|
||||
if($this->getUser()->getId() !== $photo->getOwner()->getId()) {
|
||||
if($this->getUser()->getId() !== $photo->getOwner()->getId())
|
||||
$this->fail(21, "You can't delete another's photo");
|
||||
}
|
||||
|
||||
if(!$photo) {
|
||||
if(!$photo)
|
||||
$this->fail(21, "Invalid photo");
|
||||
}
|
||||
|
||||
if($photo->isDeleted()) {
|
||||
$this->fail(21, "Photo already deleted");
|
||||
}
|
||||
if($photo->isDeleted())
|
||||
$this->fail(21, "Photo is already deleted");
|
||||
|
||||
$photo->delete();
|
||||
} else {
|
||||
|
@ -543,17 +521,14 @@ final class Photos extends VKAPIRequestHandler
|
|||
|
||||
$phot = (new PhotosRepo)->getByOwnerAndVID((int)$id[0], (int)$id[1]);
|
||||
|
||||
if($this->getUser()->getId() !== $phot->getOwner()->getId()) {
|
||||
if($this->getUser()->getId() !== $phot->getOwner()->getId())
|
||||
$this->fail(21, "You can't delete another's photo");
|
||||
}
|
||||
|
||||
if(!$phot) {
|
||||
if(!$phot)
|
||||
$this->fail(21, "Invalid photo");
|
||||
}
|
||||
|
||||
if($phot->isDeleted()) {
|
||||
if($phot->isDeleted())
|
||||
$this->fail(21, "Photo already deleted");
|
||||
}
|
||||
|
||||
$phot->delete();
|
||||
}
|
||||
|
@ -573,17 +548,11 @@ final class Photos extends VKAPIRequestHandler
|
|||
$this->willExecuteWriteAction();
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id);
|
||||
if(!$comment) {
|
||||
if(!$comment)
|
||||
$this->fail(21, "Invalid comment");
|
||||
}
|
||||
|
||||
if(!$comment->canBeModifiedBy($this->getUser())) {
|
||||
$this->fail(21, "Forbidden");
|
||||
}
|
||||
|
||||
if($comment->isDeleted()) {
|
||||
$this->fail(4, "Comment already deleted");
|
||||
}
|
||||
if(!$comment->canBeModifiedBy($this->getUser()))
|
||||
$this->fail(21, "Access denied");
|
||||
|
||||
$comment->delete();
|
||||
|
||||
|
@ -595,20 +564,16 @@ final class Photos extends VKAPIRequestHandler
|
|||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(empty($message) && empty($attachments)) {
|
||||
if(empty($message) && empty($attachments))
|
||||
$this->fail(100, "Required parameter 'message' missing.");
|
||||
}
|
||||
|
||||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
||||
|
||||
if(!$photo->getAlbum()->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) {
|
||||
$this->fail(21, "This user chose to hide his albums.");
|
||||
}
|
||||
if(!$photo || $photo->isDeleted())
|
||||
$this->fail(180, "Invalid photo");
|
||||
|
||||
if(!$photo)
|
||||
$this->fail(180, "Photo not found");
|
||||
if($photo->isDeleted())
|
||||
$this->fail(189, "Photo is deleted");
|
||||
if(!$photo->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access to photo denied");
|
||||
|
||||
$comment = new Comment;
|
||||
$comment->setOwner($this->getUser()->getId());
|
||||
|
@ -669,22 +634,21 @@ final class Photos extends VKAPIRequestHandler
|
|||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if($owner_id < 0) {
|
||||
if($owner_id < 0)
|
||||
$this->fail(4, "This method doesn't works with clubs");
|
||||
}
|
||||
|
||||
$user = (new UsersRepo)->get($owner_id);
|
||||
|
||||
if(!$user) {
|
||||
if(!$user)
|
||||
$this->fail(4, "Invalid user");
|
||||
}
|
||||
|
||||
if(!$user->getPrivacyPermission('photos.read', $this->getUser())) {
|
||||
if(!$user->getPrivacyPermission('photos.read', $this->getUser()))
|
||||
$this->fail(21, "This user chose to hide his albums.");
|
||||
}
|
||||
|
||||
$photos = array_slice(iterator_to_array((new PhotosRepo)->getEveryUserPhoto($user, 1, $count + $offset)), $offset);
|
||||
$res = [];
|
||||
$res = [
|
||||
"items" => [],
|
||||
];
|
||||
|
||||
foreach($photos as $photo) {
|
||||
if(!$photo || $photo->isDeleted()) continue;
|
||||
|
@ -702,17 +666,11 @@ final class Photos extends VKAPIRequestHandler
|
|||
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
|
||||
$comms = array_slice(iterator_to_array($photo->getComments(1, $offset + $count)), $offset);
|
||||
|
||||
if(!$photo) {
|
||||
if(!$photo || $photo->isDeleted())
|
||||
$this->fail(4, "Invalid photo");
|
||||
}
|
||||
|
||||
if(!$photo->getAlbum()->getOwner()->getPrivacyPermission('photos.read', $this->getUser())) {
|
||||
$this->fail(21, "This user chose to hide his photos.");
|
||||
}
|
||||
|
||||
if($photo->isDeleted()) {
|
||||
$this->fail(4, "Photo is deleted");
|
||||
}
|
||||
if(!$photo->canBeViewedBy($this->getUser()))
|
||||
$this->fail(21, "Access denied");
|
||||
|
||||
$res = [
|
||||
"count" => sizeof($comms),
|
||||
|
@ -730,4 +688,4 @@ final class Photos extends VKAPIRequestHandler
|
|||
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,27 @@ 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);
|
||||
|
||||
if(!$user || $user->isDeleted() || !$user->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Invalid user");
|
||||
|
||||
$audioStatus = $user->getCurrentAudioStatus();
|
||||
if($audioStatus) {
|
||||
return [
|
||||
"status" => $user->getStatus(),
|
||||
"audio" => $audioStatus->toVkApiStruct(),
|
||||
];
|
||||
}
|
||||
|
||||
return $user->getStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<?php declare(strict_types=1);
|
||||
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\{Photos, Clubs, Albums, Videos, Notes, Audios};
|
||||
use openvk\Web\Models\Repositories\Reports;
|
||||
|
||||
final class Users extends VKAPIRequestHandler
|
||||
{
|
||||
|
@ -36,8 +38,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,21 +48,21 @@ final class Users extends VKAPIRequestHandler
|
|||
} else {
|
||||
$response[$i] = (object)[
|
||||
"id" => $usr->getId(),
|
||||
"first_name" => $usr->getFirstName(),
|
||||
"last_name" => $usr->getLastName(),
|
||||
"is_closed" => false,
|
||||
"can_access_closed" => true,
|
||||
"first_name" => $usr->getFirstName(true),
|
||||
"last_name" => $usr->getLastName(true),
|
||||
"is_closed" => $usr->isClosed(),
|
||||
"can_access_closed" => (bool)$usr->canBeViewedBy($this->getUser()),
|
||||
];
|
||||
|
||||
$flds = explode(',', $fields);
|
||||
|
||||
foreach($flds as $field) {
|
||||
$canView = $usr->canBeViewedBy($this->getUser());
|
||||
foreach($flds as $field) {
|
||||
switch($field) {
|
||||
case "verified":
|
||||
$response[$i]->verified = intval($usr->isVerified());
|
||||
break;
|
||||
case "sex":
|
||||
$response[$i]->sex = $usr->isFemale() ? 1 : 2;
|
||||
$response[$i]->sex = $usr->isFemale() ? 1 : ($usr->isNeutral() ? 0 : 2);
|
||||
break;
|
||||
case "has_photo":
|
||||
$response[$i]->has_photo = is_null($usr->getAvatarPhoto()) ? 0 : 1;
|
||||
|
@ -95,6 +97,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)
|
||||
|
@ -142,26 +150,122 @@ final class Users extends VKAPIRequestHandler
|
|||
];
|
||||
}
|
||||
case "music":
|
||||
if(!$canView) {
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->music = $usr->getFavoriteMusic();
|
||||
break;
|
||||
case "movies":
|
||||
if(!$canView) {
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->movies = $usr->getFavoriteFilms();
|
||||
break;
|
||||
case "tv":
|
||||
if(!$canView) {
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->tv = $usr->getFavoriteShows();
|
||||
break;
|
||||
case "books":
|
||||
if(!$canView) {
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->books = $usr->getFavoriteBooks();
|
||||
break;
|
||||
case "city":
|
||||
if(!$canView) {
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->city = $usr->getCity();
|
||||
break;
|
||||
case "interests":
|
||||
if(!$canView) {
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->interests = $usr->getInterests();
|
||||
break;
|
||||
case "quotes":
|
||||
if(!$canView) {
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->quotes = $usr->getFavoriteQuote();
|
||||
break;
|
||||
case "email":
|
||||
if(!$canView) {
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->email = $usr->getContactEmail();
|
||||
break;
|
||||
case "telegram":
|
||||
if(!$canView) {
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->telegram = $usr->getTelegram();
|
||||
break;
|
||||
case "about":
|
||||
if(!$canView) {
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->about = $usr->getDescription();
|
||||
break;
|
||||
case "rating":
|
||||
if(!$canView) {
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->rating = $usr->getRating();
|
||||
break;
|
||||
break;
|
||||
case "counters":
|
||||
$response[$i]->counters = (object) [
|
||||
"friends_count" => $usr->getFriendsCount(),
|
||||
"photos_count" => (new Photos)->getUserPhotosCount($usr),
|
||||
"videos_count" => (new Videos)->getUserVideosCount($usr),
|
||||
"audios_count" => (new Audios)->getUserCollectionSize($usr),
|
||||
"notes_count" => (new Notes)->getUserNotesCount($usr)
|
||||
];
|
||||
break;
|
||||
case "correct_counters":
|
||||
$response[$i]->counters = (object) [
|
||||
"friends" => $usr->getFriendsCount(),
|
||||
"photos" => (new Photos)->getUserPhotosCount($usr),
|
||||
"videos" => (new Videos)->getUserVideosCount($usr),
|
||||
"audios" => (new Audios)->getUserCollectionSize($usr),
|
||||
"notes" => (new Notes)->getUserNotesCount($usr),
|
||||
"groups" => $usr->getClubCount(),
|
||||
"online_friends" => $usr->getFriendsOnlineCount(),
|
||||
];
|
||||
break;
|
||||
case "guid":
|
||||
$response[$i]->guid = $usr->getChandlerGUID();
|
||||
break;
|
||||
case 'background':
|
||||
$backgrounds = $usr->getBackDropPictureURLs();
|
||||
$response[$i]->background = $backgrounds;
|
||||
break;
|
||||
case 'reg_date':
|
||||
if(!$canView) {
|
||||
break;
|
||||
}
|
||||
|
||||
$response[$i]->reg_date = $usr->getRegistrationTime()->timestamp();
|
||||
break;
|
||||
case 'is_dead':
|
||||
$response[$i]->is_dead = $usr->isDead();
|
||||
break;
|
||||
case 'nickname':
|
||||
$response[$i]->nickname = $usr->getPseudo();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,6 +289,14 @@ final class Users extends VKAPIRequestHandler
|
|||
|
||||
$this->requireUser();
|
||||
|
||||
$user = $users->get($user_id);
|
||||
|
||||
if(!$user || $user->isDeleted())
|
||||
$this->fail(14, "Invalid user");
|
||||
|
||||
if(!$user->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
foreach($users->get($user_id)->getFollowers($offset, $count) as $follower)
|
||||
$followers[] = $follower->getId();
|
||||
|
||||
|
@ -205,88 +317,112 @@ final class Users extends VKAPIRequestHandler
|
|||
int $count = 100,
|
||||
string $city = "",
|
||||
string $hometown = "",
|
||||
int $sex = 2,
|
||||
int $status = 0, # это про marital status
|
||||
int $sex = 3,
|
||||
int $status = 0, # marital_status
|
||||
bool $online = false,
|
||||
# дальше идут параметры которых нету в vkapi но есть на сайте
|
||||
string $profileStatus = "", # а это уже нормальный статус
|
||||
# non standart params:
|
||||
int $sort = 0,
|
||||
int $before = 0,
|
||||
int $politViews = 0,
|
||||
int $after = 0,
|
||||
string $interests = "",
|
||||
int $polit_views = 0,
|
||||
string $fav_music = "",
|
||||
string $fav_films = "",
|
||||
string $fav_shows = "",
|
||||
string $fav_books = "",
|
||||
string $fav_quotes = ""
|
||||
string $fav_books = ""
|
||||
)
|
||||
{
|
||||
$users = new UsersRepo;
|
||||
|
||||
$sortg = "id ASC";
|
||||
if($count > 100) {
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: count should be less or equal to 100");
|
||||
}
|
||||
|
||||
$nfilds = $fields;
|
||||
$users = new UsersRepo;
|
||||
$output_sort = ['type' => 'id', 'invert' => false];
|
||||
$output_params = [
|
||||
"ignore_private" => true,
|
||||
];
|
||||
|
||||
switch($sort) {
|
||||
default:
|
||||
case 0:
|
||||
$sortg = "id DESC";
|
||||
$output_sort = ['type' => 'id', 'invert' => false];
|
||||
break;
|
||||
case 1:
|
||||
$sortg = "id ASC";
|
||||
break;
|
||||
case 2:
|
||||
$sortg = "first_name DESC";
|
||||
break;
|
||||
case 3:
|
||||
$sortg = "first_name ASC";
|
||||
$output_sort = ['type' => 'id', 'invert' => true];
|
||||
break;
|
||||
case 4:
|
||||
$sortg = "rating DESC";
|
||||
|
||||
if(!str_contains($nfilds, "rating")) {
|
||||
$nfilds .= "rating";
|
||||
}
|
||||
|
||||
break;
|
||||
case 5:
|
||||
$sortg = "rating DESC";
|
||||
|
||||
if(!str_contains($nfilds, "rating")) {
|
||||
$nfilds .= "rating";
|
||||
}
|
||||
|
||||
$output_sort = ['type' => 'rating', 'invert' => false];
|
||||
break;
|
||||
}
|
||||
|
||||
if(!empty($city))
|
||||
$output_params['city'] = $city;
|
||||
|
||||
if(!empty($hometown))
|
||||
$output_params['hometown'] = $hometown;
|
||||
|
||||
if($sex != 3)
|
||||
$output_params['gender'] = $sex;
|
||||
|
||||
if($status != 0)
|
||||
$output_params['marital_status'] = $status;
|
||||
|
||||
if($polit_views != 0)
|
||||
$output_params['polit_views'] = $polit_views;
|
||||
|
||||
if(!empty($interests))
|
||||
$output_params['interests'] = $interests;
|
||||
|
||||
if(!empty($fav_music))
|
||||
$output_params['fav_music'] = $fav_music;
|
||||
|
||||
if(!empty($fav_films))
|
||||
$output_params['fav_films'] = $fav_films;
|
||||
|
||||
if(!empty($fav_shows))
|
||||
$output_params['fav_shows'] = $fav_shows;
|
||||
|
||||
if(!empty($fav_books))
|
||||
$output_params['fav_books'] = $fav_books;
|
||||
|
||||
if($online)
|
||||
$output_params['is_online'] = 1;
|
||||
|
||||
$array = [];
|
||||
$find = $users->find($q, $output_params, $output_sort);
|
||||
|
||||
$parameters = [
|
||||
"city" => !empty($city) ? $city : NULL,
|
||||
"hometown" => !empty($hometown) ? $hometown : NULL,
|
||||
"gender" => $sex < 2 ? $sex : NULL,
|
||||
"maritalstatus" => (bool)$status ? $status : NULL,
|
||||
"politViews" => (bool)$politViews ? $politViews : NULL,
|
||||
"is_online" => $online ? 1 : NULL,
|
||||
"status" => !empty($profileStatus) ? $profileStatus : NULL,
|
||||
"before" => $before != 0 ? $before : NULL,
|
||||
"after" => $after != 0 ? $after : NULL,
|
||||
"interests" => !empty($interests) ? $interests : NULL,
|
||||
"fav_music" => !empty($fav_music) ? $fav_music : NULL,
|
||||
"fav_films" => !empty($fav_films) ? $fav_films : NULL,
|
||||
"fav_shows" => !empty($fav_shows) ? $fav_shows : NULL,
|
||||
"fav_books" => !empty($fav_books) ? $fav_books : NULL,
|
||||
"fav_quotes" => !empty($fav_quotes) ? $fav_quotes : NULL,
|
||||
];
|
||||
|
||||
$find = $users->find($q, $parameters, $sortg);
|
||||
|
||||
foreach ($find as $user)
|
||||
foreach ($find->offsetLimit($offset, $count) as $user)
|
||||
$array[] = $user->getId();
|
||||
|
||||
if(!$array || sizeof($array) < 1) {
|
||||
return (object) [
|
||||
"count" => 0,
|
||||
"items" => [],
|
||||
];
|
||||
}
|
||||
|
||||
return (object) [
|
||||
"count" => $find->size(),
|
||||
"items" => $this->get(implode(',', $array), $nfilds, $offset, $count)
|
||||
"count" => $find->size(),
|
||||
"items" => $this->get(implode(',', $array), $fields)
|
||||
];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ final class Utils extends VKAPIRequestHandler
|
|||
"object_id" => (int) substr($screen_name, strlen("club")),
|
||||
"type" => "group"
|
||||
];
|
||||
}
|
||||
} else $this->fail(104, "Not found");
|
||||
} else {
|
||||
$user = (new Users)->getByShortURL($screen_name);
|
||||
if($user) {
|
||||
|
@ -39,8 +39,17 @@ final class Utils extends VKAPIRequestHandler
|
|||
"type" => "group"
|
||||
];
|
||||
}
|
||||
|
||||
return (object) [];
|
||||
|
||||
$this->fail(104, "Not found");
|
||||
}
|
||||
}
|
||||
|
||||
function resolveGuid(string $guid): object
|
||||
{
|
||||
$user = (new Users)->getByChandlerUserId($guid);
|
||||
if (is_null($user))
|
||||
$this->fail(104, "Not found");
|
||||
|
||||
return $user->toVkApiStruct($this->getUser());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
@ -26,7 +26,7 @@ final class Video extends VKAPIRequestHandler
|
|||
|
||||
$video = (new VideosRepo)->getByOwnerAndVID(intval($id[0]), intval($id[1]));
|
||||
if($video) {
|
||||
$items[] = $video->getApiStructure();
|
||||
$items[] = $video->getApiStructure($this->getUser());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,16 +36,22 @@ final class Video extends VKAPIRequestHandler
|
|||
];
|
||||
} else {
|
||||
if ($owner_id > 0)
|
||||
$user = (new UsersRepo)->get($owner_id);
|
||||
$user = (new UsersRepo)->get($owner_id);
|
||||
else
|
||||
$this->fail(1, "Not implemented");
|
||||
$this->fail(1, "Not implemented");
|
||||
|
||||
if(!$user || $user->isDeleted())
|
||||
$this->fail(14, "Invalid user");
|
||||
|
||||
if(!$user->getPrivacyPermission('videos.read', $this->getUser()))
|
||||
$this->fail(21, "This user chose to hide his videos.");
|
||||
|
||||
$videos = (new VideosRepo)->getByUser($user, $offset + 1, $count);
|
||||
$videosCount = (new VideosRepo)->getUserVideosCount($user);
|
||||
|
||||
$items = [];
|
||||
foreach ($videos as $video) {
|
||||
$items[] = $video->getApiStructure();
|
||||
$items[] = $video->getApiStructure($this->getUser());
|
||||
}
|
||||
|
||||
return (object) [
|
||||
|
@ -54,4 +60,61 @@ final class Video extends VKAPIRequestHandler
|
|||
];
|
||||
}
|
||||
}
|
||||
|
||||
function search(string $q = '', int $sort = 0, int $offset = 0, int $count = 10, bool $extended = false, string $fields = ''): object
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$params = [];
|
||||
$db_sort = ['type' => 'id', 'invert' => false];
|
||||
$videos = (new VideosRepo)->find($q, $params, $db_sort);
|
||||
$items = iterator_to_array($videos->offsetLimit($offset, $count));
|
||||
$count = $videos->size();
|
||||
|
||||
$return_items = [];
|
||||
$profiles = [];
|
||||
$groups = [];
|
||||
foreach($items as $item) {
|
||||
$return_item = $item->getApiStructure($this->getUser());
|
||||
$return_item = $return_item->video;
|
||||
$return_items[] = $return_item;
|
||||
|
||||
if($return_item['owner_id']) {
|
||||
if($return_item['owner_id'] > 0)
|
||||
$profiles[] = $return_item['owner_id'];
|
||||
else
|
||||
$groups[] = abs($return_item['owner_id']);
|
||||
}
|
||||
}
|
||||
|
||||
if($extended) {
|
||||
$profiles = array_unique($profiles);
|
||||
$groups = array_unique($groups);
|
||||
|
||||
$profilesFormatted = [];
|
||||
$groupsFormatted = [];
|
||||
|
||||
foreach($profiles as $prof) {
|
||||
$profile = (new UsersRepo)->get($prof);
|
||||
$profilesFormatted[] = $profile->toVkApiStruct($this->getUser(), $fields);
|
||||
}
|
||||
|
||||
foreach($groups as $gr) {
|
||||
$group = (new ClubsRepo)->get($gr);
|
||||
$groupsFormatted[] = $group->toVkApiStruct($this->getUser(), $fields);
|
||||
}
|
||||
|
||||
return (object) [
|
||||
"count" => $count,
|
||||
"items" => $return_items,
|
||||
"profiles" => $profilesFormatted,
|
||||
"groups" => $groupsFormatted,
|
||||
];
|
||||
}
|
||||
|
||||
return (object) [
|
||||
"count" => $count,
|
||||
"items" => $return_items,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Entities\Notifications\{WallPostNotification, RepostNotification, CommentNotification};
|
||||
use openvk\Web\Models\Entities\Notifications\{PostAcceptedNotification, WallPostNotification, NewSuggestedPostsNotification, RepostNotification, CommentNotification};
|
||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
||||
|
@ -15,10 +15,12 @@ use openvk\Web\Models\Entities\Video;
|
|||
use openvk\Web\Models\Repositories\Videos as VideosRepo;
|
||||
use openvk\Web\Models\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
|
||||
{
|
||||
function get(int $owner_id, string $domain = "", int $offset = 0, int $count = 30, int $extended = 0): object
|
||||
function get(int $owner_id, string $domain = "", int $offset = 0, int $count = 30, int $extended = 0, string $filter = "all"): object
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
|
@ -27,7 +29,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
$items = [];
|
||||
$profiles = [];
|
||||
$groups = [];
|
||||
$cnt = $posts->getPostCountOnUserWall($owner_id);
|
||||
$cnt = 0;
|
||||
|
||||
if ($owner_id > 0)
|
||||
$wallOnwer = (new UsersRepo)->get($owner_id);
|
||||
|
@ -37,11 +39,54 @@ final class Wall extends VKAPIRequestHandler
|
|||
if ($owner_id > 0)
|
||||
if(!$wallOnwer || $wallOnwer->isDeleted())
|
||||
$this->fail(18, "User was deleted or banned");
|
||||
|
||||
if(!$wallOnwer->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access denied");
|
||||
else
|
||||
if(!$wallOnwer)
|
||||
$this->fail(15, "Access denied: wall is disabled"); // Don't search for logic here pls
|
||||
|
||||
foreach($posts->getPostsFromUsersWall($owner_id, 1, $count, $offset) as $post) {
|
||||
$iteratorv;
|
||||
|
||||
switch($filter) {
|
||||
case "all":
|
||||
$iteratorv = $posts->getPostsFromUsersWall($owner_id, 1, $count, $offset);
|
||||
$cnt = $posts->getPostCountOnUserWall($owner_id);
|
||||
break;
|
||||
case "owner":
|
||||
$iteratorv = $posts->getOwnersPostsFromWall($owner_id, 1, $count, $offset);
|
||||
$cnt = $posts->getOwnersCountOnUserWall($owner_id);
|
||||
break;
|
||||
case "others":
|
||||
$iteratorv = $posts->getOthersPostsFromWall($owner_id, 1, $count, $offset);
|
||||
$cnt = $posts->getOthersCountOnUserWall($owner_id);
|
||||
break;
|
||||
case "postponed":
|
||||
$this->fail(42, "Postponed posts are not implemented.");
|
||||
break;
|
||||
case "suggests":
|
||||
if($owner_id < 0) {
|
||||
if($wallOnwer->getWallType() != 2)
|
||||
$this->fail(125, "Group's wall type is open or closed");
|
||||
|
||||
if($wallOnwer->canBeModifiedBy($this->getUser())) {
|
||||
$iteratorv = $posts->getSuggestedPosts($owner_id * -1, 1, $count, $offset);
|
||||
$cnt = $posts->getSuggestedPostsCount($owner_id * -1);
|
||||
} else {
|
||||
$iteratorv = $posts->getSuggestedPostsByUser($owner_id * -1, $this->getUser()->getId(), 1, $count, $offset);
|
||||
$cnt = $posts->getSuggestedPostsCountByUser($owner_id * -1, $this->getUser()->getId());
|
||||
}
|
||||
} else {
|
||||
$this->fail(528, "Suggested posts avaiable only at groups");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
$this->fail(254, "Invalid filter");
|
||||
break;
|
||||
}
|
||||
|
||||
foreach($iteratorv as $post) {
|
||||
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
||||
|
||||
$attachments = [];
|
||||
|
@ -55,9 +100,21 @@ final class Wall extends VKAPIRequestHandler
|
|||
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
||||
$attachments[] = $this->getApiPoll($attachment, $this->getUser());
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Video) {
|
||||
$attachments[] = $attachment->getApiStructure();
|
||||
$attachments[] = $attachment->getApiStructure($this->getUser());
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
||||
$attachments[] = $attachment->toVkApiStruct();
|
||||
if(VKAPI_DECL_VER === '4.100') {
|
||||
$attachments[] = $attachment->toVkApiStruct();
|
||||
} else {
|
||||
$attachments[] = [
|
||||
'type' => 'note',
|
||||
'note' => $attachment->toVkApiStruct()
|
||||
];
|
||||
}
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Audio) {
|
||||
$attachments[] = [
|
||||
"type" => "audio",
|
||||
"audio" => $attachment->toVkApiStruct($this->getUser()),
|
||||
];
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||
$repostAttachments = [];
|
||||
|
||||
|
@ -76,50 +133,44 @@ final class Wall extends VKAPIRequestHandler
|
|||
else
|
||||
$profiles[] = $attachment->getOwner()->getId();
|
||||
|
||||
$post_source = [];
|
||||
|
||||
if($attachment->getPlatform(true) === NULL) {
|
||||
$post_source = (object)["type" => "vk"];
|
||||
} else {
|
||||
$post_source = (object)[
|
||||
"type" => "api",
|
||||
"platform" => $attachment->getPlatform(true)
|
||||
];
|
||||
}
|
||||
|
||||
$repost[] = [
|
||||
"id" => $attachment->getVirtualId(),
|
||||
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
||||
"from_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
||||
"date" => $attachment->getPublicationTime()->timestamp(),
|
||||
"post_type" => "post",
|
||||
"post_type" => $attachment->getVkApiType(),
|
||||
"text" => $attachment->getText(false),
|
||||
"attachments" => $repostAttachments,
|
||||
"post_source" => $post_source,
|
||||
"post_source" => $attachment->getPostSourceInfo(),
|
||||
];
|
||||
|
||||
if ($attachment->getTargetWall() > 0)
|
||||
$profiles[] = $attachment->getTargetWall();
|
||||
else
|
||||
$groups[] = abs($attachment->getTargetWall());
|
||||
if($post->isSigned())
|
||||
$profiles[] = $attachment->getOwner()->getId();
|
||||
}
|
||||
}
|
||||
|
||||
$post_source = [];
|
||||
|
||||
if($post->getPlatform(true) === NULL) {
|
||||
$post_source = (object)["type" => "vk"];
|
||||
} else {
|
||||
$post_source = (object)[
|
||||
"type" => "api",
|
||||
"platform" => $post->getPlatform(true)
|
||||
];
|
||||
$signerId = NULL;
|
||||
if($post->isSigned()) {
|
||||
$actualAuthor = $post->getOwner(false);
|
||||
$signerId = $actualAuthor->getId();
|
||||
}
|
||||
|
||||
$items[] = (object)[
|
||||
# TODO "can_pin", "copy_history" и прочее не должны возвращаться, если равны null или false
|
||||
# Ну и ещё всё надо перенести в toVkApiStruct, а то слишком много дублированного кода
|
||||
|
||||
$post_temp_obj = (object)[
|
||||
"id" => $post->getVirtualId(),
|
||||
"from_id" => $from_id,
|
||||
"owner_id" => $post->getTargetWall(),
|
||||
"date" => $post->getPublicationTime()->timestamp(),
|
||||
"post_type" => "post",
|
||||
"post_type" => $post->getVkApiType(),
|
||||
"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
|
||||
|
@ -127,7 +178,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
"is_pinned" => $post->isPinned(),
|
||||
"is_explicit" => $post->isExplicit(),
|
||||
"attachments" => $attachments,
|
||||
"post_source" => $post_source,
|
||||
"post_source" => $post->getPostSourceInfo(),
|
||||
"comments" => (object)[
|
||||
"count" => $post->getCommentsCount(),
|
||||
"can_post" => 1
|
||||
|
@ -144,11 +195,25 @@ final class Wall extends VKAPIRequestHandler
|
|||
]
|
||||
];
|
||||
|
||||
if($post->hasSource())
|
||||
$post_temp_obj->copyright = $post->getVkApiCopyright();
|
||||
|
||||
if($signerId)
|
||||
$post_temp_obj->signer_id = $signerId;
|
||||
|
||||
if($post->isDeactivationMessage())
|
||||
$post_temp_obj->final_post = 1;
|
||||
|
||||
$items[] = $post_temp_obj;
|
||||
|
||||
if ($from_id > 0)
|
||||
$profiles[] = $from_id;
|
||||
else
|
||||
$groups[] = $from_id * -1;
|
||||
|
||||
if($post->isSigned())
|
||||
$profiles[] = $post->getOwner(false)->getId();
|
||||
|
||||
$attachments = NULL; # free attachments so it will not clone everythingg
|
||||
}
|
||||
|
||||
|
@ -165,9 +230,9 @@ final class Wall extends VKAPIRequestHandler
|
|||
"first_name" => $user->getFirstName(),
|
||||
"id" => $user->getId(),
|
||||
"last_name" => $user->getLastName(),
|
||||
"can_access_closed" => false,
|
||||
"is_closed" => false,
|
||||
"sex" => $user->isFemale() ? 1 : 2,
|
||||
"can_access_closed" => (bool)$user->canBeViewedBy($this->getUser()),
|
||||
"is_closed" => $user->isClosed(),
|
||||
"sex" => $user->isFemale() ? 1 : ($user->isNeutral() ? 0 : 2),
|
||||
"screen_name" => $user->getShortCode(),
|
||||
"photo_50" => $user->getAvatarUrl(),
|
||||
"photo_100" => $user->getAvatarUrl(),
|
||||
|
@ -220,7 +285,11 @@ final class Wall extends VKAPIRequestHandler
|
|||
foreach($psts as $pst) {
|
||||
$id = explode("_", $pst);
|
||||
$post = (new PostsRepo)->getPostById(intval($id[0]), intval($id[1]));
|
||||
|
||||
if($post && !$post->isDeleted()) {
|
||||
if(!$post->canBeViewedBy($this->getUser()))
|
||||
continue;
|
||||
|
||||
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
||||
$attachments = [];
|
||||
$repost = []; // чел высрал семь сигарет 😳 помянем 🕯
|
||||
|
@ -230,9 +299,21 @@ final class Wall extends VKAPIRequestHandler
|
|||
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
||||
$attachments[] = $this->getApiPoll($attachment, $user);
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Video) {
|
||||
$attachments[] = $attachment->getApiStructure();
|
||||
$attachments[] = $attachment->getApiStructure($this->getUser());
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Note) {
|
||||
$attachments[] = $attachment->toVkApiStruct();
|
||||
if(VKAPI_DECL_VER === '4.100') {
|
||||
$attachments[] = $attachment->toVkApiStruct();
|
||||
} else {
|
||||
$attachments[] = [
|
||||
'type' => 'note',
|
||||
'note' => $attachment->toVkApiStruct()
|
||||
];
|
||||
}
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Audio) {
|
||||
$attachments[] = [
|
||||
"type" => "audio",
|
||||
"audio" => $attachment->toVkApiStruct($this->getUser())
|
||||
];
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||
$repostAttachments = [];
|
||||
|
||||
|
@ -251,17 +332,6 @@ final class Wall extends VKAPIRequestHandler
|
|||
else
|
||||
$profiles[] = $attachment->getOwner()->getId();
|
||||
|
||||
$post_source = [];
|
||||
|
||||
if($attachment->getPlatform(true) === NULL) {
|
||||
$post_source = (object)["type" => "vk"];
|
||||
} else {
|
||||
$post_source = (object)[
|
||||
"type" => "api",
|
||||
"platform" => $attachment->getPlatform(true)
|
||||
];
|
||||
}
|
||||
|
||||
$repost[] = [
|
||||
"id" => $attachment->getVirtualId(),
|
||||
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
||||
|
@ -270,38 +340,39 @@ final class Wall extends VKAPIRequestHandler
|
|||
"post_type" => "post",
|
||||
"text" => $attachment->getText(false),
|
||||
"attachments" => $repostAttachments,
|
||||
"post_source" => $post_source,
|
||||
"post_source" => $attachment->getPostSourceInfo(),
|
||||
];
|
||||
|
||||
if ($attachment->getTargetWall() > 0)
|
||||
$profiles[] = $attachment->getTargetWall();
|
||||
else
|
||||
$groups[] = abs($attachment->getTargetWall());
|
||||
if($post->isSigned())
|
||||
$profiles[] = $attachment->getOwner()->getId();
|
||||
}
|
||||
}
|
||||
|
||||
$post_source = [];
|
||||
|
||||
if($post->getPlatform(true) === NULL) {
|
||||
$post_source = (object)["type" => "vk"];
|
||||
} else {
|
||||
$post_source = (object)[
|
||||
"type" => "api",
|
||||
"platform" => $post->getPlatform(true)
|
||||
];
|
||||
if($post->isSigned()) {
|
||||
$actualAuthor = $post->getOwner(false);
|
||||
$signerId = $actualAuthor->getId();
|
||||
}
|
||||
|
||||
$items[] = (object)[
|
||||
$post_temp_obj = (object)[
|
||||
"id" => $post->getVirtualId(),
|
||||
"from_id" => $from_id,
|
||||
"owner_id" => $post->getTargetWall(),
|
||||
"date" => $post->getPublicationTime()->timestamp(),
|
||||
"post_type" => "post",
|
||||
"post_type" => $post->getVkApiType(),
|
||||
"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
|
||||
"is_archived" => false,
|
||||
"is_pinned" => $post->isPinned(),
|
||||
"is_explicit" => $post->isExplicit(),
|
||||
"post_source" => $post_source,
|
||||
"post_source" => $post->getPostSourceInfo(),
|
||||
"attachments" => $attachments,
|
||||
"comments" => (object)[
|
||||
"count" => $post->getCommentsCount(),
|
||||
|
@ -319,11 +390,25 @@ final class Wall extends VKAPIRequestHandler
|
|||
]
|
||||
];
|
||||
|
||||
if($post->hasSource())
|
||||
$post_temp_obj->copyright = $post->getVkApiCopyright();
|
||||
|
||||
if($signerId)
|
||||
$post_temp_obj->signer_id = $signerId;
|
||||
|
||||
if($post->isDeactivationMessage())
|
||||
$post_temp_obj->final_post = 1;
|
||||
|
||||
$items[] = $post_temp_obj;
|
||||
|
||||
if ($from_id > 0)
|
||||
$profiles[] = $from_id;
|
||||
else
|
||||
$groups[] = $from_id * -1;
|
||||
|
||||
if($post->isSigned())
|
||||
$profiles[] = $post->getOwner(false)->getId();
|
||||
|
||||
$attachments = NULL; # free attachments so it will not clone everything
|
||||
$repost = NULL; # same
|
||||
}
|
||||
|
@ -338,19 +423,28 @@ final class Wall extends VKAPIRequestHandler
|
|||
|
||||
foreach($profiles as $prof) {
|
||||
$user = (new UsersRepo)->get($prof);
|
||||
$profilesFormatted[] = (object)[
|
||||
"first_name" => $user->getFirstName(),
|
||||
"id" => $user->getId(),
|
||||
"last_name" => $user->getLastName(),
|
||||
"can_access_closed" => false,
|
||||
"is_closed" => false,
|
||||
"sex" => $user->isFemale() ? 1 : 2,
|
||||
"screen_name" => $user->getShortCode(),
|
||||
"photo_50" => $user->getAvatarUrl(),
|
||||
"photo_100" => $user->getAvatarUrl(),
|
||||
"online" => $user->isOnline(),
|
||||
"verified" => $user->isVerified()
|
||||
];
|
||||
if($user) {
|
||||
$profilesFormatted[] = (object)[
|
||||
"first_name" => $user->getFirstName(),
|
||||
"id" => $user->getId(),
|
||||
"last_name" => $user->getLastName(),
|
||||
"can_access_closed" => (bool)$user->canBeViewedBy($this->getUser()),
|
||||
"is_closed" => $user->isClosed(),
|
||||
"sex" => $user->isFemale() ? 1 : 2,
|
||||
"screen_name" => $user->getShortCode(),
|
||||
"photo_50" => $user->getAvatarUrl(),
|
||||
"photo_100" => $user->getAvatarUrl(),
|
||||
"online" => $user->isOnline(),
|
||||
"verified" => $user->isVerified()
|
||||
];
|
||||
} else {
|
||||
$profilesFormatted[] = (object)[
|
||||
"id" => (int) $prof,
|
||||
"first_name" => "DELETED",
|
||||
"last_name" => "",
|
||||
"deactivated" => "deleted"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach($groups as $g) {
|
||||
|
@ -379,7 +473,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
];
|
||||
}
|
||||
|
||||
function post(string $owner_id, string $message = "", int $from_group = 0, int $signed = 0, string $attachments = ""): object
|
||||
function post(string $owner_id, string $message = "", int $from_group = 0, int $signed = 0, string $attachments = "", int $post_id = 0): object
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
@ -389,7 +483,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
$wallOwner = ($owner_id > 0 ? (new UsersRepo)->get($owner_id) : (new ClubsRepo)->get($owner_id * -1))
|
||||
?? $this->fail(18, "User was deleted or banned");
|
||||
if($owner_id > 0)
|
||||
$canPost = $wallOwner->getPrivacyPermission("wall.write", $this->getUser());
|
||||
$canPost = $wallOwner->getPrivacyPermission("wall.write", $this->getUser()) && $wallOwner->canBeViewedBy($this->getUser());
|
||||
else if($owner_id < 0)
|
||||
if($wallOwner->canBeModifiedBy($this->getUser()))
|
||||
$canPost = true;
|
||||
|
@ -400,6 +494,46 @@ final class Wall extends VKAPIRequestHandler
|
|||
|
||||
if($canPost == false) $this->fail(15, "Access denied");
|
||||
|
||||
if($post_id > 0) {
|
||||
if($owner_id > 0)
|
||||
$this->fail(62, "Suggested posts available only at groups");
|
||||
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $post_id, true);
|
||||
|
||||
if(!$post || $post->isDeleted())
|
||||
$this->fail(32, "Invald post");
|
||||
|
||||
if($post->getSuggestionType() == 0)
|
||||
$this->fail(20, "Post is not suggested");
|
||||
|
||||
if($post->getSuggestionType() == 2)
|
||||
$this->fail(16, "Post is declined");
|
||||
|
||||
if(!$post->canBePinnedBy($this->getUser()))
|
||||
$this->fail(51, "Access denied");
|
||||
|
||||
$author = $post->getOwner();
|
||||
$flags = 0;
|
||||
$flags |= 0b10000000;
|
||||
|
||||
if($signed == 1)
|
||||
$flags |= 0b01000000;
|
||||
|
||||
$post->setSuggested(0);
|
||||
$post->setCreated(time());
|
||||
$post->setFlags($flags);
|
||||
|
||||
if(!empty($message) && iconv_strlen($message) > 0)
|
||||
$post->setContent($message);
|
||||
|
||||
$post->save();
|
||||
|
||||
if($author->getId() != $this->getUser()->getId())
|
||||
(new PostAcceptedNotification($author, $post, $post->getWallOwner()))->emit();
|
||||
|
||||
return (object)["post_id" => $post->getVirtualId()];
|
||||
}
|
||||
|
||||
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
|
||||
if($wallOwner instanceof Club && $from_group == 1 && $signed != 1 && $anon) {
|
||||
$manager = $wallOwner->getManager($this->getUser());
|
||||
|
@ -428,18 +562,33 @@ final class Wall extends VKAPIRequestHandler
|
|||
$post->setContent($message);
|
||||
$post->setFlags($flags);
|
||||
$post->setApi_Source_Name($this->getPlatform());
|
||||
|
||||
if(!is_null($copyright) && !empty($copyright)) {
|
||||
try {
|
||||
$post->setSource($copyright);
|
||||
} catch(\Throwable) {}
|
||||
}
|
||||
|
||||
if($owner_id < 0 && !$wallOwner->canBeModifiedBy($this->getUser()) && $wallOwner->getWallType() == 2)
|
||||
$post->setSuggested(1);
|
||||
|
||||
$post->save();
|
||||
} catch(\LogicException $ex) {
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid");
|
||||
}
|
||||
|
||||
# TODO use parseAttachments
|
||||
if(!empty($attachments)) {
|
||||
$attachmentsArr = explode(",", $attachments);
|
||||
# Аттачи такого вида: [тип][id владельца]_[id вложения]
|
||||
# Пример: photo1_1
|
||||
|
||||
if(sizeof($attachmentsArr) > 10)
|
||||
$this->fail(50, "Error: too many attachments");
|
||||
$this->fail(50, "Too many attachments");
|
||||
|
||||
preg_match_all("/poll/m", $attachments, $matches, PREG_SET_ORDER, 0);
|
||||
if(sizeof($matches) > 1)
|
||||
$this->fail(85, "Too many polls");
|
||||
|
||||
foreach($attachmentsArr as $attac) {
|
||||
$attachmentType = NULL;
|
||||
|
@ -450,6 +599,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 +617,40 @@ 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");
|
||||
|
||||
$post->attach($attacc);
|
||||
} elseif($attachmentType == "audio") {
|
||||
$attacc = (new AudiosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
|
||||
if(!$attacc || $attacc->isDeleted())
|
||||
$this->fail(100, "Audio does not exist");
|
||||
|
||||
$post->attach($attacc);
|
||||
}
|
||||
|
@ -494,6 +660,22 @@ final class Wall extends VKAPIRequestHandler
|
|||
if($wall > 0 && $wall !== $this->user->identity->getId())
|
||||
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
||||
|
||||
if($owner_id < 0 && !$wallOwner->canBeModifiedBy($this->getUser()) && $wallOwner->getWallType() == 2) {
|
||||
$suggsCount = (new PostsRepo)->getSuggestedPostsCount($wallOwner->getId());
|
||||
|
||||
if($suggsCount % 10 == 0) {
|
||||
$managers = $wallOwner->getManagers();
|
||||
$owner = $wallOwner->getOwner();
|
||||
(new NewSuggestedPostsNotification($owner, $wallOwner))->emit();
|
||||
|
||||
foreach($managers as $manager) {
|
||||
(new NewSuggestedPostsNotification($manager->getUser(), $wallOwner))->emit();
|
||||
}
|
||||
}
|
||||
|
||||
return (object)["post_id" => "on_view"];
|
||||
}
|
||||
|
||||
return (object)["post_id" => $post->getVirtualId()];
|
||||
}
|
||||
|
||||
|
@ -508,6 +690,9 @@ final class Wall extends VKAPIRequestHandler
|
|||
$post = (new PostsRepo)->getPostById((int) $postArray[1], (int) $postArray[2]);
|
||||
if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid");
|
||||
|
||||
if(!$post->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
$nPost = new Post;
|
||||
$nPost->setOwner($this->user->getId());
|
||||
|
||||
|
@ -546,6 +731,9 @@ final class Wall extends VKAPIRequestHandler
|
|||
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
|
||||
if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid");
|
||||
|
||||
if(!$post->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
$comments = (new CommentsRepo)->getCommentsByTarget($post, $offset+1, $count, $sort == "desc" ? "DESC" : "ASC");
|
||||
|
||||
|
@ -565,6 +753,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()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -586,6 +779,9 @@ final class Wall extends VKAPIRequestHandler
|
|||
]
|
||||
];
|
||||
|
||||
if($comment->isFromPostAuthor($post))
|
||||
$item['is_from_post_author'] = true;
|
||||
|
||||
if($need_likes == true)
|
||||
$item['likes'] = [
|
||||
"can_like" => 1,
|
||||
|
@ -624,6 +820,12 @@ final class Wall extends VKAPIRequestHandler
|
|||
|
||||
$comment = (new CommentsRepo)->get($comment_id); # один хуй айди всех комментов общий
|
||||
|
||||
if(!$comment || $comment->isDeleted())
|
||||
$this->fail(100, "Invalid comment");
|
||||
|
||||
if(!$comment->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
$profiles = [];
|
||||
|
||||
$attachments = [];
|
||||
|
@ -631,6 +833,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()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -658,6 +865,9 @@ final class Wall extends VKAPIRequestHandler
|
|||
]
|
||||
];
|
||||
|
||||
if($comment->isFromPostAuthor())
|
||||
$item['is_from_post_author'] = true;
|
||||
|
||||
if($extended == true)
|
||||
$profiles[] = $comment->getOwner()->getId();
|
||||
|
||||
|
@ -673,18 +883,19 @@ final class Wall extends VKAPIRequestHandler
|
|||
$response['profiles'] = (!empty($profiles) ? (new Users)->get(implode(',', $profiles), $fields) : []);
|
||||
}
|
||||
|
||||
|
||||
|
||||
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();
|
||||
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
|
||||
if(!$post || $post->isDeleted()) $this->fail(100, "Invalid post");
|
||||
|
||||
if(!$post->canBeViewedBy($this->getUser()))
|
||||
$this->fail(15, "Access denied");
|
||||
|
||||
if($post->getTargetWall() < 0)
|
||||
$club = (new ClubsRepo)->get(abs($post->getTargetWall()));
|
||||
|
||||
|
@ -722,6 +933,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 +949,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);
|
||||
}
|
||||
|
@ -775,12 +994,185 @@ final class Wall extends VKAPIRequestHandler
|
|||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function delete(int $owner_id, int $post_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $post_id, true);
|
||||
if(!$post || $post->isDeleted())
|
||||
$this->fail(583, "Invalid post");
|
||||
|
||||
$wallOwner = $post->getWallOwner();
|
||||
|
||||
if($post->getTargetWall() < 0 && !$post->getWallOwner()->canBeModifiedBy($this->getUser()) && $post->getWallOwner()->getWallType() != 1 && $post->getSuggestionType() == 0)
|
||||
$this->fail(12, "Access denied: you can't delete your accepted post.");
|
||||
|
||||
if($post->getOwnerPost() == $this->getUser()->getId() || $post->getTargetWall() == $this->getUser()->getId() || $owner_id < 0 && $wallOwner->canBeModifiedBy($this->getUser())) {
|
||||
$post->unwire();
|
||||
$post->delete();
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
$this->fail(15, "Access denied");
|
||||
}
|
||||
}
|
||||
|
||||
function edit(int $owner_id, int $post_id, string $message = "", string $attachments = "", string $copyright = NULL) {
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
|
||||
|
||||
if(!$post || $post->isDeleted())
|
||||
$this->fail(102, "Invalid post");
|
||||
|
||||
if(!$post->canBeEditedBy($this->getUser()))
|
||||
$this->fail(7, "Access to editing denied");
|
||||
|
||||
if(!empty($message))
|
||||
$post->setContent($message);
|
||||
|
||||
$post->setEdited(time());
|
||||
if(!is_null($copyright) && !empty($copyright)) {
|
||||
try {
|
||||
$post->setSource($copyright);
|
||||
} catch(\Throwable) {}
|
||||
}
|
||||
|
||||
$post->save(true);
|
||||
|
||||
# todo добавить такое в веб версию
|
||||
if(!empty($attachments)) {
|
||||
$attachs = parseAttachments($attachments);
|
||||
$newAttachmentsCount = sizeof($attachs);
|
||||
|
||||
$postsAttachments = iterator_to_array($post->getChildren());
|
||||
|
||||
if(sizeof($postsAttachments) >= 10)
|
||||
$this->fail(15, "Post have too many attachments");
|
||||
|
||||
if(($newAttachmentsCount + sizeof($postsAttachments)) > 10)
|
||||
$this->fail(158, "Post will have too many attachments");
|
||||
|
||||
foreach($attachs as $attach) {
|
||||
if($attach && !$attach->isDeleted())
|
||||
$post->attach($attach);
|
||||
else
|
||||
$this->fail(52, "One of the attachments is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
return ["post_id" => $post->getVirtualId()];
|
||||
}
|
||||
|
||||
function editComment(int $comment_id, int $owner_id = 0, string $message = "", string $attachments = "") {
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id);
|
||||
|
||||
if(empty($message) && empty($attachments))
|
||||
$this->fail(100, "Required parameter 'message' missing.");
|
||||
|
||||
if(!$comment || $comment->isDeleted())
|
||||
$this->fail(102, "Invalid comment");
|
||||
|
||||
if(!$comment->canBeEditedBy($this->getUser()))
|
||||
$this->fail(15, "Access to editing comment denied");
|
||||
|
||||
if(!empty($message))
|
||||
$comment->setContent($message);
|
||||
|
||||
$comment->setEdited(time());
|
||||
$comment->save(true);
|
||||
|
||||
if(!empty($attachments)) {
|
||||
$attachs = parseAttachments($attachments);
|
||||
$newAttachmentsCount = sizeof($attachs);
|
||||
|
||||
$postsAttachments = iterator_to_array($comment->getChildren());
|
||||
|
||||
if(sizeof($postsAttachments) >= 10)
|
||||
$this->fail(15, "Post have too many attachments");
|
||||
|
||||
if(($newAttachmentsCount + sizeof($postsAttachments)) > 10)
|
||||
$this->fail(158, "Post will have too many attachments");
|
||||
|
||||
foreach($attachs as $attach) {
|
||||
if($attach && !$attach->isDeleted())
|
||||
$comment->attach($attach);
|
||||
else
|
||||
$this->fail(52, "One of the attachments is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function checkCopyrightLink(string $link): int
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
try {
|
||||
$result = check_copyright_link($link);
|
||||
} catch(\InvalidArgumentException $e) {
|
||||
$this->fail(3102, "Specified link is incorrect (can't find source)");
|
||||
} catch(\LengthException $e) {
|
||||
$this->fail(3103, "Specified link is incorrect (too long)");
|
||||
} catch(\LogicException $e) {
|
||||
$this->fail(3104, "Link is suspicious");
|
||||
} catch(\Throwable $e) {
|
||||
$this->fail(3102, "Specified link is incorrect");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function pin(int $owner_id, int $post_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
|
||||
if(!$post || $post->isDeleted())
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: post_id is undefined");
|
||||
|
||||
if(!$post->canBePinnedBy($this->getUser()))
|
||||
return 0;
|
||||
|
||||
if($post->isPinned())
|
||||
return 1;
|
||||
|
||||
$post->pin();
|
||||
return 1;
|
||||
}
|
||||
|
||||
function unpin(int $owner_id, int $post_id)
|
||||
{
|
||||
$this->requireUser();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
|
||||
if(!$post || $post->isDeleted())
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: post_id is undefined");
|
||||
|
||||
if(!$post->canBePinnedBy($this->getUser()))
|
||||
return 0;
|
||||
|
||||
if(!$post->isPinned())
|
||||
return 1;
|
||||
|
||||
$post->unpin();
|
||||
return 1;
|
||||
}
|
||||
|
||||
private function getApiPhoto($attachment) {
|
||||
return [
|
||||
"type" => "photo",
|
||||
"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(),
|
||||
|
|
|
@ -5,7 +5,7 @@ exceptions. It is still a work-in-progress functionality.
|
|||
**Note**: requests to API are routed through
|
||||
openvk.Web.Presenters.VKAPIPresenter, this dir contains only handlers.
|
||||
|
||||
[Documentation for API clients](https://docs.openvk.uk/openvk_engine/api/description/)
|
||||
[Documentation for API clients](https://docs.ovk.to/openvk_engine/api/description/)
|
||||
|
||||
## Implementing API methods
|
||||
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -67,6 +67,21 @@ class Album extends MediaCollection
|
|||
return $this->has($photo);
|
||||
}
|
||||
|
||||
function canBeViewedBy(?User $user = NULL): bool
|
||||
{
|
||||
if($this->isDeleted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$owner = $this->getOwner();
|
||||
|
||||
if(get_class($owner) == "openvk\\Web\\Models\\Entities\\User") {
|
||||
return $owner->canBeViewedBy($user) && $owner->getPrivacyPermission('photos.read', $user);
|
||||
} else {
|
||||
return $owner->canBeViewedBy($user);
|
||||
}
|
||||
}
|
||||
|
||||
function toVkApiStruct(?User $user = NULL, bool $need_covers = false, bool $photo_sizes = false): object
|
||||
{
|
||||
$res = (object) [];
|
||||
|
|
|
@ -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"); }
|
||||
}
|
474
Web/Models/Entities/Audio.php
Normal file
474
Web/Models/Entities/Audio.php
Normal file
|
@ -0,0 +1,474 @@
|
|||
<?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 = [
|
||||
'A Cappella', 'Abstract', 'Acid', 'Acid Jazz', 'Acid Punk', 'Acoustic', 'AlternRock', 'Alternative', 'Ambient', 'Anime', 'Art Rock', 'Audio Theatre', 'Audiobook', 'Avantgarde', 'Ballad', 'Baroque', 'Bass', 'Beat', 'Bebob', 'Bhangra', 'Big Band', 'Big Beat', 'Black Metal', 'Bluegrass', 'Blues', 'Booty Bass', 'Breakbeat', 'BritPop', 'Cabaret', 'Celtic', 'Chamber Music', 'Chanson', 'Chillout', 'Chorus', 'Christian Gangsta Rap', 'Christian Rap', 'Christian Rock', 'Classic Rock', 'Classical', 'Club', 'Club-House', 'Comedy', 'Contemporary Christian', 'Country', 'Crossover', 'Cult', 'Dance', 'Dance Hall', 'Darkwave', 'Death Metal', 'Disco', 'Downtempo', 'Dream', 'Drum & Bass', 'Drum Solo', 'Dub', 'Dubstep', 'Duet', 'EBM', 'Easy Listening', 'Eclectic', 'Electro', 'Electroclash', 'Electronic', 'Emo', 'Ethnic', 'Euro-House', 'Euro-Techno', 'Eurodance', 'Experimental', 'Fast Fusion', 'Folk', 'Folk-Rock', 'Folklore', 'Freestyle', 'Funk', 'Fusion', 'G-Funk', 'Game', 'Gangsta Rap', 'Garage', 'Garage Rock', 'Global', 'Goa', 'Gospel', 'Gothic', 'Gothic Rock', 'Grunge', 'Hard Rock', 'Hardcore', 'Heavy Metal', 'Hip-Hop', 'House', 'Humour', 'IDM', 'Illbient', 'Indie', 'Indie Rock', 'Industrial', 'Industro-Goth', 'Instrumental', 'Instrumental Pop', 'Instrumental Rock', 'JPop', 'Jam Band', 'Jazz', 'Jazz+Funk', 'Jungle', 'Krautrock', 'Latin', 'Leftfield', 'Lo-Fi', 'Lounge', 'Math Rock', 'Meditative', 'Merengue', 'Metal', 'Musical', 'National Folk', 'Native American', 'Negerpunk', 'Neoclassical', 'Neue Deutsche Welle', 'New Age', 'New Romantic', 'New Wave', 'Noise', 'Nu-Breakz', 'Oldies', 'Opera', 'Other', 'Podcast', 'Polka', 'Polsk Punk', 'Pop', 'Pop / Funk', 'Pop-Folk', 'Porn Groove', 'Post-Punk', 'Post-Rock', 'Power Ballad', 'Pranks', 'Primus', 'Progressive Rock', 'Psybient', 'Psychedelic', 'Psychedelic Rock', 'Psychobilly', 'Psytrance', 'Punk', 'Punk Rock', 'R&B', 'Rap', 'Rave', 'Reggae', 'Retro', 'Revival', 'Rhythmic Soul', 'Rock', 'Rock & Roll', 'Salsa', 'Samba', 'Satire', 'Shoegaze', 'Showtunes', 'Ska', 'Slow Jam', 'Slow Rock', 'Sonata', 'Soul', 'Sound Clip', 'Soundtrack', 'Southern Rock', 'Space', 'Space Rock', 'Speech', 'Swing', 'Symphonic Rock', 'Symphony', 'Synthpop', 'Tango', 'Techno', 'Techno-Industrial', 'Terror', 'Thrash Metal', 'Top 40', 'Touhou', 'Trailer', 'Trance', 'Tribal', 'Trip-Hop', 'Trop Rock', 'Vocal', 'World Music'
|
||||
];
|
||||
|
||||
# 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 getDownloadName(): string
|
||||
{
|
||||
return preg_replace('/[\\/:*?"<>|]/', '_', str_replace(' ', '_', $this->getName()));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ namespace openvk\Web\Models\Entities;
|
|||
use openvk\Web\Util\DateTime;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use openvk\Web\Models\Entities\{User, Manager};
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Managers};
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Managers, Posts};
|
||||
use Nette\Database\Table\{ActiveRow, GroupedSelection};
|
||||
use Chandler\Database\DatabaseConnection as DB;
|
||||
use Chandler\Security\User as ChandlerUser;
|
||||
|
@ -23,6 +23,10 @@ class Club extends RowModel
|
|||
const NOT_RELATED = 0;
|
||||
const SUBSCRIBED = 1;
|
||||
const REQUEST_SENT = 2;
|
||||
|
||||
const WALL_CLOSED = 0;
|
||||
const WALL_OPEN = 1;
|
||||
const WALL_LIMITED = 2;
|
||||
|
||||
function getId(): int
|
||||
{
|
||||
|
@ -45,6 +49,11 @@ class Club extends RowModel
|
|||
|
||||
return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURLBySizeId($size);
|
||||
}
|
||||
|
||||
function getWallType(): int
|
||||
{
|
||||
return $this->getRecord()->wall;
|
||||
}
|
||||
|
||||
function getAvatarLink(): string
|
||||
{
|
||||
|
@ -143,6 +152,11 @@ class Club extends RowModel
|
|||
return (bool) $this->getRecord()->hide_from_global_feed;
|
||||
}
|
||||
|
||||
function isHidingFromGlobalFeedEnforced(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->enforce_hiding_from_global_feed;
|
||||
}
|
||||
|
||||
function getType(): int
|
||||
{
|
||||
return $this->getRecord()->type;
|
||||
|
@ -182,6 +196,14 @@ class Club extends RowModel
|
|||
$this->stateChanges("shortcode", $code);
|
||||
return true;
|
||||
}
|
||||
|
||||
function setWall(int $type)
|
||||
{
|
||||
if($type > 2 || $type < 0)
|
||||
throw new \LogicException("Invalid wall");
|
||||
|
||||
$this->stateChanges("wall", $type);
|
||||
}
|
||||
|
||||
function isSubscriptionAccepted(User $user): bool
|
||||
{
|
||||
|
@ -224,7 +246,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 +257,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 +268,7 @@ class Club extends RowModel
|
|||
"color" => "#4d9fab",
|
||||
],
|
||||
"fill" => "tozeroy",
|
||||
"name" => $unique ? "Виральный охват" : "Виральные просмотры",
|
||||
"name" => $unique ? tr("viral_coverage") : tr("viral_views"),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
@ -291,6 +313,21 @@ class Club extends RowModel
|
|||
yield $rel;
|
||||
}
|
||||
}
|
||||
|
||||
function getSuggestedPostsCount(User $user = NULL)
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
if(is_null($user))
|
||||
return NULL;
|
||||
|
||||
if($this->canBeModifiedBy($user))
|
||||
$count = (new Posts)->getSuggestedPostsCount($this->getId());
|
||||
else
|
||||
$count = (new Posts)->getSuggestedPostsCountByUser($this->getId(), $user->getId());
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
function getManagers(int $page = 1, bool $ignoreHidden = false): \Traversable
|
||||
{
|
||||
|
@ -351,44 +388,85 @@ 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 canBeViewedBy(?User $user = NULL)
|
||||
{
|
||||
return is_null($this->getBanReason());
|
||||
}
|
||||
|
||||
function getAlert(): ?string
|
||||
{
|
||||
return $this->getRecord()->alert;
|
||||
}
|
||||
|
||||
function toVkApiStruct(?User $user = NULL): object
|
||||
|
||||
function getRealId(): int
|
||||
{
|
||||
$res = [];
|
||||
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, string $fields = ''): object
|
||||
{
|
||||
$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
|
||||
|
@ -74,8 +74,12 @@ class Comment extends Post
|
|||
foreach($this->getChildren() as $attachment) {
|
||||
if($attachment->isDeleted())
|
||||
continue;
|
||||
|
||||
$res->attachments[] = $attachment->toVkApiStruct();
|
||||
|
||||
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||
$res->attachments[] = $attachment->toVkApiStruct();
|
||||
} else if($attachment instanceof \openvk\Web\Models\Entities\Video) {
|
||||
$res->attachments[] = $attachment->toVkApiStruct($this->getUser());
|
||||
}
|
||||
}
|
||||
|
||||
if($need_likes) {
|
||||
|
@ -85,4 +89,82 @@ class Comment extends Post
|
|||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
function getURL(): string
|
||||
{
|
||||
return "/wall" . $this->getTarget()->getPrettyId() . "#_comment" . $this->getId();
|
||||
}
|
||||
|
||||
function canBeViewedBy(?User $user = NULL): bool
|
||||
{
|
||||
if($this->isDeleted() || $this->getTarget()->isDeleted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->getTarget()->canBeViewedBy($user);
|
||||
}
|
||||
|
||||
function isFromPostAuthor($target = NULL)
|
||||
{
|
||||
if(!$target)
|
||||
$target = $this->getTarget();
|
||||
|
||||
$target_owner = $target->getOwner();
|
||||
$comment_owner = $this->getOwner();
|
||||
|
||||
if($target_owner->getRealId() === $comment_owner->getRealId())
|
||||
return true;
|
||||
|
||||
# TODO: make it work with signer_id
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
function getTargetURL(): string
|
||||
{
|
||||
$target = $this->getTarget();
|
||||
$target_name = 'wall';
|
||||
|
||||
if(!$target) {
|
||||
return '/404';
|
||||
}
|
||||
|
||||
switch(get_class($target)) {
|
||||
case 'openvk\Web\Models\Entities\Note':
|
||||
$target_name = 'note';
|
||||
break;
|
||||
case 'openvk\Web\Models\Entities\Photo':
|
||||
$target_name = 'photo';
|
||||
break;
|
||||
case 'openvk\Web\Models\Entities\Video':
|
||||
$target_name = 'video';
|
||||
break;
|
||||
case 'openvk\Web\Models\Entities\Topic':
|
||||
$target_name = 'topic';
|
||||
break;
|
||||
}
|
||||
|
||||
return $target_name . $target->getPrettyId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ class Message extends RowModel
|
|||
$dateTime = new DateTime($this->getRecord()->created);
|
||||
|
||||
if($dateTime->format("%d.%m.%y") == ovk_strftime_safe("%d.%m.%y", time())) {
|
||||
return $dateTime->format("%T %p");
|
||||
return $dateTime->format("%T");
|
||||
} else {
|
||||
return $dateTime->format("%d.%m.%y");
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -118,20 +118,29 @@ class Note extends Postable
|
|||
{
|
||||
return $this->getRecord()->source;
|
||||
}
|
||||
|
||||
function canBeViewedBy(?User $user = NULL): bool
|
||||
{
|
||||
if($this->isDeleted() || $this->getOwner()->isDeleted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->getOwner()->getPrivacyPermission('notes.read', $user) && $this->getOwner()->canBeViewedBy($user);
|
||||
}
|
||||
|
||||
function toVkApiStruct(): object
|
||||
{
|
||||
$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";
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities\Notifications;
|
||||
use openvk\Web\Models\Entities\{User, Club};
|
||||
|
||||
final class NewSuggestedPostsNotification extends Notification
|
||||
{
|
||||
protected $actionCode = 7;
|
||||
|
||||
function __construct(User $owner, Club $group)
|
||||
{
|
||||
parent::__construct($owner, $owner, $group, time(), "");
|
||||
}
|
||||
}
|
|
@ -132,4 +132,138 @@ QUERY;
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getVkApiInfo()
|
||||
{
|
||||
$origin_m = $this->encodeType($this->originModel);
|
||||
$target_m = $this->encodeType($this->targetModel);
|
||||
|
||||
$info = [
|
||||
"type" => "",
|
||||
"parent" => NULL,
|
||||
"feedback" => NULL,
|
||||
];
|
||||
|
||||
switch($this->getActionCode()) {
|
||||
case 0:
|
||||
$info["type"] = "like_post";
|
||||
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
|
||||
$info["feedback"] = $this->getModel(1)->toVkApiStruct();
|
||||
break;
|
||||
case 1:
|
||||
$info["type"] = "copy_post";
|
||||
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
|
||||
$info["feedback"] = NULL; # todo
|
||||
break;
|
||||
case 2:
|
||||
switch($origin_m) {
|
||||
case 19:
|
||||
$info["type"] = "comment_video";
|
||||
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
|
||||
$info["feedback"] = NULL; # айди коммента не сохраняется в бд( ну пиздец блять
|
||||
break;
|
||||
case 13:
|
||||
$info["type"] = "comment_photo";
|
||||
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
|
||||
$info["feedback"] = NULL;
|
||||
break;
|
||||
# unstandart (vk forgor about notes)
|
||||
case 10:
|
||||
$info["type"] = "comment_note";
|
||||
$info["parent"] = $this->getModel(0)->toVkApiStruct();
|
||||
$info["feedback"] = NULL;
|
||||
break;
|
||||
case 14:
|
||||
$info["type"] = "comment_post";
|
||||
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
|
||||
$info["feedback"] = NULL;
|
||||
break;
|
||||
# unused (users don't have topics bruh)
|
||||
case 21:
|
||||
$info["type"] = "comment_topic";
|
||||
$info["parent"] = $this->getModel(0)->toVkApiStruct(0, 90);
|
||||
break;
|
||||
default:
|
||||
$info["type"] = "comment_unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case 3:
|
||||
$info["type"] = "wall";
|
||||
$info["feedback"] = $this->getModel(0)->toNotifApiStruct();
|
||||
break;
|
||||
case 4:
|
||||
switch($target_m) {
|
||||
case 14:
|
||||
$info["type"] = "mention";
|
||||
$info["feedback"] = $this->getModel(1)->toNotifApiStruct();
|
||||
break;
|
||||
case 19:
|
||||
$info["type"] = "mention_comment_video";
|
||||
$info["parent"] = $this->getModel(1)->toNotifApiStruct();
|
||||
break;
|
||||
case 13:
|
||||
$info["type"] = "mention_comment_photo";
|
||||
$info["parent"] = $this->getModel(1)->toNotifApiStruct();
|
||||
break;
|
||||
# unstandart
|
||||
case 10:
|
||||
$info["type"] = "mention_comment_note";
|
||||
$info["parent"] = $this->getModel(1)->toVkApiStruct();
|
||||
break;
|
||||
case 21:
|
||||
$info["type"] = "mention_comments";
|
||||
break;
|
||||
default:
|
||||
$info["type"] = "mention_comment_unknown";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
$info["type"] = "make_you_admin";
|
||||
$info["parent"] = $this->getModel(0)->toVkApiStruct($this->getModel(1));
|
||||
break;
|
||||
# Нужно доделать после мержа #935
|
||||
case 6:
|
||||
$info["type"] = "wall_publish";
|
||||
break;
|
||||
# В вк не было такого уведомления, так что unstandart
|
||||
case 7:
|
||||
$info["type"] = "new_posts_in_club";
|
||||
break;
|
||||
# В вк при передаче подарков приходит сообщение, а не уведомление, так что unstandart
|
||||
case 9601:
|
||||
$info["type"] = "sent_gift";
|
||||
$info["parent"] = $this->getModel(1)->toVkApiStruct($this->getModel(1));
|
||||
break;
|
||||
case 9602:
|
||||
$info["type"] = "voices_transfer";
|
||||
$info["parent"] = $this->getModel(1)->toVkApiStruct($this->getModel(1));
|
||||
break;
|
||||
case 9603:
|
||||
$info["type"] = "up_rating";
|
||||
$info["parent"] = $this->getModel(1)->toVkApiStruct($this->getModel(1));
|
||||
$info["parent"]->count = $this->getData();
|
||||
break;
|
||||
default:
|
||||
$info["type"] = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
function toVkApiStruct()
|
||||
{
|
||||
$res = (object)[];
|
||||
|
||||
$info = $this->getVkApiInfo();
|
||||
$res->type = $info["type"];
|
||||
$res->date = $this->getDateTime()->timestamp();
|
||||
$res->parent = $info["parent"];
|
||||
$res->feedback = $info["feedback"];
|
||||
$res->reply = NULL; # Ответы на комментарии не реализованы
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities\Notifications;
|
||||
use openvk\Web\Models\Entities\{User, Club, Post};
|
||||
|
||||
final class PostAcceptedNotification extends Notification
|
||||
{
|
||||
protected $actionCode = 6;
|
||||
|
||||
function __construct(User $author, Post $post, Club $group)
|
||||
{
|
||||
parent::__construct($author, $post, $group, time(), "");
|
||||
}
|
||||
}
|
|
@ -54,11 +54,11 @@ class PasswordReset extends RowModel
|
|||
}
|
||||
}
|
||||
|
||||
function save(): void
|
||||
function save(?bool $log = false): void
|
||||
{
|
||||
$this->stateChanges("key", base64_encode(openssl_random_pseudo_bytes(46)));
|
||||
$this->stateChanges("timestamp", time());
|
||||
|
||||
parent::save();
|
||||
parent::save($log);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ class Photo extends Media
|
|||
return true;
|
||||
}
|
||||
|
||||
function crop(real $left, real $top, real $width, real $height): void
|
||||
function crop(float $left, float $top, float $width, float $height): void
|
||||
{
|
||||
if(isset($this->changes["hash"]))
|
||||
$hash = $this->changes["hash"];
|
||||
|
@ -328,6 +328,19 @@ class Photo extends Media
|
|||
|
||||
return $res;
|
||||
}
|
||||
|
||||
function canBeViewedBy(?User $user = NULL): bool
|
||||
{
|
||||
if($this->isDeleted() || $this->getOwner()->isDeleted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!is_null($this->getAlbum())) {
|
||||
return $this->getAlbum()->canBeViewedBy($user);
|
||||
} else {
|
||||
return $this->getOwner()->canBeViewedBy($user);
|
||||
}
|
||||
}
|
||||
|
||||
static function fastMake(int $owner, string $description = "", array $file, ?Album $album = NULL, bool $anon = false): Photo
|
||||
{
|
||||
|
@ -347,4 +360,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;
|
||||
}
|
||||
}
|
||||
|
|
282
Web/Models/Entities/Playlist.php
Normal file
282
Web/Models/Entities/Playlist.php
Normal file
|
@ -0,0 +1,282 @@
|
|||
<?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 fetchClassic(int $offset = 0, ?int $limit = NULL): \Traversable
|
||||
{
|
||||
$related = $this->getRecord()->related("$this->relTableName.collection")
|
||||
->limit($limit ?? OPENVK_DEFAULT_PER_PAGE, $offset)
|
||||
->order("index ASC");
|
||||
|
||||
foreach($related as $rel) {
|
||||
$media = $rel->ref($this->entityTableName, "media");
|
||||
if(!$media)
|
||||
continue;
|
||||
|
||||
yield new $this->entityClassName($media);
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
"searchable" => !$this->isUnlisted(),
|
||||
];
|
||||
}
|
||||
|
||||
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 getCoverPhoto(): ?Photo
|
||||
{
|
||||
return (new Photos)->get((int) $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);
|
||||
}
|
||||
|
||||
function isUnlisted(): bool
|
||||
{
|
||||
return (bool)$this->getRecord()->unlisted;
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ use openvk\Web\Models\Exceptions\TooMuchOptionsException;
|
|||
use openvk\Web\Util\DateTime;
|
||||
use \UnexpectedValueException;
|
||||
use Nette\InvalidStateException;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\Repositories\{Users, Posts};
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Exceptions\PollLockedException;
|
||||
use openvk\Web\Models\Exceptions\AlreadyVotedException;
|
||||
|
@ -165,7 +165,7 @@ class Poll extends Attachable
|
|||
|
||||
function canVote(User $user): bool
|
||||
{
|
||||
return !$this->hasEnded() && !$this->hasVoted($user);
|
||||
return !$this->hasEnded() && !$this->hasVoted($user) && !is_null($this->getAttachedPost()) && $this->getAttachedPost()->getSuggestionType() == 0;
|
||||
}
|
||||
|
||||
function vote(User $user, array $optionIds): void
|
||||
|
@ -278,13 +278,24 @@ class Poll extends Attachable
|
|||
|
||||
return $poll;
|
||||
}
|
||||
|
||||
function canBeViewedBy(?User $user = NULL): bool
|
||||
{
|
||||
# waiting for #935 :(
|
||||
/*if(!is_null($this->getAttachedPost())) {
|
||||
return $this->getAttachedPost()->canBeViewedBy($user);
|
||||
} else {*/
|
||||
return true;
|
||||
#}
|
||||
|
||||
}
|
||||
|
||||
function save(): void
|
||||
function save(?bool $log = false): void
|
||||
{
|
||||
if(empty($this->choicesToPersist))
|
||||
throw new InvalidStateException;
|
||||
|
||||
parent::save();
|
||||
parent::save($log);
|
||||
foreach($this->choicesToPersist as $option) {
|
||||
DatabaseConnection::i()->getContext()->table("poll_options")->insert([
|
||||
"poll" => $this->getId(),
|
||||
|
@ -292,4 +303,17 @@ class Poll extends Attachable
|
|||
]);
|
||||
}
|
||||
}
|
||||
|
||||
function getAttachedPost()
|
||||
{
|
||||
$post = DatabaseConnection::i()->getContext()->table("attachments")
|
||||
->where(
|
||||
["attachable_type" => static::class,
|
||||
"attachable_id" => $this->getId()])->fetch();
|
||||
|
||||
if(!is_null($post->target_id))
|
||||
return (new Posts)->get($post->target_id);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,40 @@ class Post extends Postable
|
|||
{
|
||||
return (bool) $this->getRecord()->pinned;
|
||||
}
|
||||
|
||||
function hasSource(): bool
|
||||
{
|
||||
return $this->getRecord()->source != NULL;
|
||||
}
|
||||
|
||||
function getSource(bool $format = false)
|
||||
{
|
||||
$orig_source = $this->getRecord()->source;
|
||||
if(!str_contains($orig_source, "https://") && !str_contains($orig_source, "http://"))
|
||||
$orig_source = "https://" . $orig_source;
|
||||
|
||||
if(!$format)
|
||||
return $orig_source;
|
||||
|
||||
return $this->formatLinks($orig_source);
|
||||
}
|
||||
|
||||
function setSource(string $source)
|
||||
{
|
||||
$result = check_copyright_link($source);
|
||||
|
||||
$this->stateChanges("source", $source);
|
||||
}
|
||||
|
||||
function getVkApiCopyright(): object
|
||||
{
|
||||
return (object)[
|
||||
'id' => 0,
|
||||
'link' => $this->getSource(false),
|
||||
'name' => $this->getSource(false),
|
||||
'type' => 'link',
|
||||
];
|
||||
}
|
||||
|
||||
function isAd(): bool
|
||||
{
|
||||
|
@ -133,6 +167,10 @@ class Post extends Postable
|
|||
case 'openvk_legacy_ios':
|
||||
return 'iphone';
|
||||
break;
|
||||
|
||||
case 'windows_phone':
|
||||
return 'wphone';
|
||||
break;
|
||||
|
||||
case 'vika_touch': // кика хохотач ахахахаххахахахахах
|
||||
case 'vk4me':
|
||||
|
@ -175,6 +213,31 @@ class Post extends Postable
|
|||
"img" => NULL
|
||||
];
|
||||
}
|
||||
|
||||
function getPostSourceInfo(): array
|
||||
{
|
||||
$post_source = ["type" => "vk"];
|
||||
if($this->getPlatform(true) !== NULL) {
|
||||
$post_source = [
|
||||
"type" => "api",
|
||||
"platform" => $this->getPlatform(true)
|
||||
];
|
||||
}
|
||||
|
||||
if($this->isUpdateAvatarMessage())
|
||||
$post_source['data'] = 'profile_photo';
|
||||
|
||||
return $post_source;
|
||||
}
|
||||
|
||||
function getVkApiType(): string
|
||||
{
|
||||
$type = 'post';
|
||||
if($this->getSuggestionType() != 0)
|
||||
$type = 'suggest';
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
function pin(): void
|
||||
{
|
||||
|
@ -207,6 +270,9 @@ class Post extends Postable
|
|||
|
||||
function canBeDeletedBy(User $user): bool
|
||||
{
|
||||
if($this->getTargetWall() < 0 && !$this->getWallOwner()->canBeModifiedBy($user) && $this->getWallOwner()->getWallType() != 1 && $this->getSuggestionType() == 0)
|
||||
return false;
|
||||
|
||||
return $this->getOwnerPost() === $user->getId() || $this->canBePinnedBy($user);
|
||||
}
|
||||
|
||||
|
@ -245,6 +311,57 @@ class Post extends Postable
|
|||
$this->unwire();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function canBeViewedBy(?User $user = NULL): bool
|
||||
{
|
||||
if($this->isDeleted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->getWallOwner()->canBeViewedBy($user);
|
||||
}
|
||||
|
||||
function getSuggestionType()
|
||||
{
|
||||
return $this->getRecord()->suggested;
|
||||
}
|
||||
|
||||
function toNotifApiStruct()
|
||||
{
|
||||
$res = (object)[];
|
||||
|
||||
$res->id = $this->getVirtualId();
|
||||
$res->to_id = $this->getOwner() instanceof Club ? $this->getOwner()->getId() * -1 : $this->getOwner()->getId();
|
||||
$res->from_id = $res->to_id;
|
||||
$res->date = $this->getPublicationTime()->timestamp();
|
||||
$res->text = $this->getText(false);
|
||||
$res->attachments = []; # todo
|
||||
|
||||
$res->copy_owner_id = NULL; # todo
|
||||
$res->copy_post_id = NULL; # todo
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
function canBeEditedBy(?User $user = NULL): bool
|
||||
{
|
||||
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();
|
||||
else {
|
||||
if($this->isPostedOnBehalfOfGroup())
|
||||
return $this->getWallOwner()->canBeModifiedBy($user);
|
||||
else
|
||||
return $user->getId() == $this->getOwner(false)->getId();
|
||||
}
|
||||
|
||||
return $user->getId() == $this->getOwner(false)->getId();
|
||||
}
|
||||
|
||||
use Traits\TRichText;
|
||||
}
|
||||
|
|
|
@ -33,8 +33,9 @@ abstract class Postable extends Attachable
|
|||
{
|
||||
$oid = (int) $this->getRecord()->owner;
|
||||
if(!$real && $this->isAnonymous())
|
||||
$oid = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["account"];
|
||||
|
||||
$oid = (int) OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["account"];
|
||||
|
||||
$oid = abs($oid);
|
||||
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);
|
||||
|
@ -129,10 +131,15 @@ abstract class Postable extends Attachable
|
|||
"target" => $this->getRecord()->id,
|
||||
];
|
||||
|
||||
if($liked)
|
||||
DB::i()->getContext()->table("likes")->insert($searchData);
|
||||
else
|
||||
DB::i()->getContext()->table("likes")->where($searchData)->delete();
|
||||
if($liked) {
|
||||
if(!$this->hasLikeFrom($user)) {
|
||||
DB::i()->getContext()->table("likes")->insert($searchData);
|
||||
}
|
||||
} else {
|
||||
if($this->hasLikeFrom($user)) {
|
||||
DB::i()->getContext()->table("likes")->where($searchData)->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hasLikeFrom(User $user): bool
|
||||
|
@ -151,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;
|
||||
|
||||
|
@ -166,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 $this->getContentObject()->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,16 @@ use openvk\Web\Models\Entities\User;
|
|||
|
||||
trait TOwnable
|
||||
{
|
||||
function canBeViewedBy(?User $user = NULL): bool
|
||||
{
|
||||
# TODO: #950
|
||||
if($this->isDeleted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function canBeModifiedBy(User $user): bool
|
||||
{
|
||||
if(method_exists($this, "isCreatedBySystem"))
|
||||
|
|
|
@ -38,8 +38,19 @@ trait TRichText
|
|||
$href = str_replace("#", "#", $matches[1]);
|
||||
$href = rawurlencode(str_replace(";", ";", $href));
|
||||
$link = str_replace("#", "#", $matches[3]);
|
||||
# this string breaks ampersands
|
||||
$link = str_replace(";", ";", $link);
|
||||
$rel = $this->isAd() ? "sponsored" : "ugc";
|
||||
|
||||
$server_domain = str_replace(':' . $_SERVER['SERVER_PORT'], '', $_SERVER['HTTP_HOST']);
|
||||
if(str_contains($link, $server_domain)) {
|
||||
$replaced_link = str_replace(':' . $_SERVER['SERVER_PORT'], '', $link);
|
||||
$replaced_link = str_replace($server_domain, '', $replaced_link);
|
||||
|
||||
return "<a href='$replaced_link' rel='$rel'>$link</a>" . htmlentities($matches[4]);
|
||||
}
|
||||
|
||||
$link = htmlentities(urldecode($link));
|
||||
|
||||
return "<a href='/away.php?to=$href' rel='$rel' target='_blank'>$link</a>" . htmlentities($matches[4]);
|
||||
}),
|
||||
|
@ -123,7 +134,7 @@ trait TRichText
|
|||
$text = preg_replace_callback("%([\n\r\s]|^)(\#([\p{L}_0-9][\p{L}_0-9\(\)\-\']+[\p{L}_0-9\(\)]|[\p{L}_0-9]{1,2}))%Xu", function($m) {
|
||||
$slug = rawurlencode($m[3]);
|
||||
|
||||
return "$m[1]<a href='/feed/hashtag/$slug'>$m[2]</a>";
|
||||
return "$m[1]<a href='/search?section=posts&q=%23$slug'>$m[2]</a>";
|
||||
}, $text);
|
||||
|
||||
$text = $this->formatEmojis($text);
|
||||
|
|
|
@ -39,4 +39,25 @@ trait TSubscribable
|
|||
$sub->delete();
|
||||
return false;
|
||||
}
|
||||
|
||||
function changeFlags(User $user, int $flags, bool $reverse): bool
|
||||
{
|
||||
$ctx = DatabaseConnection::i()->getContext();
|
||||
$data = [
|
||||
"follower" => $reverse ? $this->getId() : $user->getId(),
|
||||
"model" => static::class,
|
||||
"target" => $reverse ? $user->getId() : $this->getId(),
|
||||
];
|
||||
$sub = $ctx->table("subscriptions")->where($data);
|
||||
|
||||
bdump($data);
|
||||
|
||||
if (!$sub)
|
||||
return false;
|
||||
|
||||
$sub->update([
|
||||
'flags' => $flags
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
@ -444,6 +508,9 @@ class User extends RowModel
|
|||
else if($user->getId() === $this->getId())
|
||||
return true;
|
||||
|
||||
if($permission != "messages.write" && !$this->canBeViewedBy($user))
|
||||
return false;
|
||||
|
||||
switch($permStatus) {
|
||||
case User::PRIVACY_ONLY_FRIENDS:
|
||||
return $this->getSubscriptionStatus($user) === User::SUBSCRIPTION_MUTUAL;
|
||||
|
@ -525,6 +592,16 @@ class User extends RowModel
|
|||
return $this->_abstractRelationCount("get-followers");
|
||||
}
|
||||
|
||||
function getRequests(int $page = 1, int $limit = 6): \Traversable
|
||||
{
|
||||
return $this->_abstractRelationGenerator("get-requests", $page, $limit);
|
||||
}
|
||||
|
||||
function getRequestsCount(): int
|
||||
{
|
||||
return $this->_abstractRelationCount("get-requests");
|
||||
}
|
||||
|
||||
function getSubscriptions(int $page = 1, int $limit = 6): \Traversable
|
||||
{
|
||||
return $this->_abstractRelationGenerator("get-subscriptions-user", $page, $limit);
|
||||
|
@ -670,8 +747,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)
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -731,7 +808,29 @@ class User extends RowModel
|
|||
|
||||
function isFemale(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->sex;
|
||||
return $this->getRecord()->sex == 1;
|
||||
}
|
||||
|
||||
function isNeutral(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->sex == 2;
|
||||
}
|
||||
|
||||
function getLocalizedPronouns(): string
|
||||
{
|
||||
switch ($this->getRecord()->sex) {
|
||||
case 0:
|
||||
return tr('male');
|
||||
case 1:
|
||||
return tr('female');
|
||||
case 2:
|
||||
return tr('neutral');
|
||||
}
|
||||
}
|
||||
|
||||
function getPronouns(): int
|
||||
{
|
||||
return $this->getRecord()->sex;
|
||||
}
|
||||
|
||||
function isVerified(): bool
|
||||
|
@ -833,7 +932,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 +945,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 +1059,7 @@ class User extends RowModel
|
|||
"friends.add",
|
||||
"wall.write",
|
||||
"messages.write",
|
||||
"audios.read",
|
||||
],
|
||||
])->set($id, $status)->toInteger());
|
||||
}
|
||||
|
@ -945,6 +1070,7 @@ class User extends RowModel
|
|||
"length" => 1,
|
||||
"mappings" => [
|
||||
"photos",
|
||||
"audios",
|
||||
"videos",
|
||||
"messages",
|
||||
"notes",
|
||||
|
@ -952,6 +1078,7 @@ class User extends RowModel
|
|||
"news",
|
||||
"links",
|
||||
"poster",
|
||||
"apps",
|
||||
],
|
||||
])->set($id, (int) $status)->toInteger();
|
||||
|
||||
|
@ -1016,7 +1143,7 @@ class User extends RowModel
|
|||
{
|
||||
$this->setOnline(time());
|
||||
$this->setClient_name($platform);
|
||||
$this->save();
|
||||
$this->save(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1034,7 +1161,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)))
|
||||
|
@ -1097,9 +1224,18 @@ class User extends RowModel
|
|||
return (bool) $this->getRecord()->activated;
|
||||
}
|
||||
|
||||
function isDead(): bool
|
||||
{
|
||||
return $this->onlineStatus() == 2;
|
||||
}
|
||||
|
||||
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,18 +1243,100 @@ 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 $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 getProfileType(): int
|
||||
{
|
||||
# 0 — открытый профиль, 1 — закрытый
|
||||
return $this->getRecord()->profile_type;
|
||||
}
|
||||
|
||||
function canBeViewedBy(?User $user = NULL): bool
|
||||
{
|
||||
if(!is_null($user)) {
|
||||
if($this->getId() == $user->getId()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if($user->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if($this->getProfileType() == 0) {
|
||||
return true;
|
||||
} else {
|
||||
if($user->getSubscriptionStatus($this) == User::SUBSCRIPTION_MUTUAL) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if($this->getProfileType() == 0) {
|
||||
if($this->getPrivacySetting("page.read") == 3) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getPaginatorType()
|
||||
function isClosed(): bool
|
||||
{
|
||||
return $this->getRecord()->paginator_type;
|
||||
return (bool) $this->getProfileType();
|
||||
}
|
||||
|
||||
function toVkApiStruct(): object
|
||||
function isHideFromGlobalFeedEnabled(): bool
|
||||
{
|
||||
return $this->isClosed();
|
||||
}
|
||||
|
||||
function getRealId()
|
||||
{
|
||||
return $this->getId();
|
||||
}
|
||||
|
||||
function toVkApiStruct(?User $user = NULL, string $fields = ''): object
|
||||
{
|
||||
$res = (object) [];
|
||||
|
||||
|
@ -1130,11 +1348,66 @@ class User extends RowModel
|
|||
$res->photo_100 = $this->getAvatarURL("tiny");
|
||||
$res->photo_200 = $this->getAvatarURL("normal");
|
||||
$res->photo_id = !is_null($this->getAvatarPhoto()) ? $this->getAvatarPhoto()->getPrettyId() : NULL;
|
||||
# TODO: Perenesti syuda vsyo ostalnoyie
|
||||
|
||||
$res->is_closed = $this->isClosed();
|
||||
|
||||
if(!is_null($user))
|
||||
$res->can_access_closed = (bool)$this->canBeViewedBy($user);
|
||||
|
||||
if(!is_array($fields))
|
||||
$fields = explode(',', $fields);
|
||||
|
||||
foreach($fields as $field) {
|
||||
switch($field) {
|
||||
case 'is_dead':
|
||||
$res->is_dead = $user->isDead();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -115,15 +115,15 @@ class Video extends Media
|
|||
return $this->getRecord()->owner;
|
||||
}
|
||||
|
||||
function getApiStructure(): object
|
||||
function getApiStructure(?User $user = NULL): object
|
||||
{
|
||||
$fromYoutube = $this->getType() == Video::TYPE_EMBED;
|
||||
return (object)[
|
||||
$res = (object)[
|
||||
"type" => "video",
|
||||
"video" => [
|
||||
"can_comment" => 1,
|
||||
"can_like" => 0, // we don't h-have wikes in videos
|
||||
"can_repost" => 0,
|
||||
"can_like" => 1, // we don't h-have wikes in videos
|
||||
"can_repost" => 1,
|
||||
"can_subscribe" => 1,
|
||||
"can_add_to_faves" => 0,
|
||||
"can_add" => 0,
|
||||
|
@ -155,29 +155,34 @@ class Video extends Media
|
|||
"repeat" => 0,
|
||||
"type" => "video",
|
||||
"views" => 0,
|
||||
"likes" => [
|
||||
"count" => 0,
|
||||
"user_likes" => 0
|
||||
],
|
||||
"reposts" => [
|
||||
"count" => 0,
|
||||
"user_reposted" => 0
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
if(!is_null($user)) {
|
||||
$res->video["likes"] = [
|
||||
"count" => $this->getLikesCount(),
|
||||
"user_likes" => $this->hasLikeFrom($user)
|
||||
];
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
function toVkApiStruct(): object
|
||||
function toVkApiStruct(?User $user): object
|
||||
{
|
||||
return $this->getApiStructure();
|
||||
return $this->getApiStructure($user);
|
||||
}
|
||||
|
||||
function setLink(string $link): string
|
||||
{
|
||||
if(preg_match(file_get_contents(__DIR__ . "/../VideoDrivers/regex/youtube.txt"), $link, $matches)) {
|
||||
$pointer = "YouTube:$matches[1]";
|
||||
} else if(preg_match(file_get_contents(__DIR__ . "/../VideoDrivers/regex/vimeo.txt"), $link, $matches)) {
|
||||
$pointer = "Vimeo:$matches[1]";
|
||||
/*} else if(preg_match(file_get_contents(__DIR__ . "/../VideoDrivers/regex/vimeo.txt"), $link, $matches)) {
|
||||
$pointer = "Vimeo:$matches[1]";*/
|
||||
} else {
|
||||
throw new ISE("Invalid link");
|
||||
}
|
||||
|
@ -219,4 +224,37 @@ class Video extends Media
|
|||
|
||||
return $video;
|
||||
}
|
||||
|
||||
function canBeViewedBy(?User $user = NULL): bool
|
||||
{
|
||||
if($this->isDeleted() || $this->getOwner()->isDeleted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(get_class($this->getOwner()) == "openvk\\Web\\Models\\Entities\\User") {
|
||||
return $this->getOwner()->canBeViewedBy($user) && $this->getOwner()->getPrivacyPermission('videos.read', $user);
|
||||
} else {
|
||||
# Groups doesn't have videos but ok
|
||||
return $this->getOwner()->canBeViewedBy($user);
|
||||
}
|
||||
}
|
||||
|
||||
function toNotifApiStruct()
|
||||
{
|
||||
$fromYoutube = $this->getType() == Video::TYPE_EMBED;
|
||||
$res = (object)[];
|
||||
|
||||
$res->id = $this->getVirtualId();
|
||||
$res->owner_id = $this->getOwner()->getId();
|
||||
$res->title = $this->getName();
|
||||
$res->description = $this->getDescription();
|
||||
$res->duration = "22";
|
||||
$res->link = "/video".$this->getOwner()->getId()."_".$this->getVirtualId();
|
||||
$res->image = $this->getThumbnailURL();
|
||||
$res->date = $this->getPublicationTime()->timestamp();
|
||||
$res->views = 0;
|
||||
$res->player = !$fromYoutube ? $this->getURL() : $this->getVideoDriver()->getURL();
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,4 +23,13 @@ class APITokens extends Repository
|
|||
|
||||
return $token;
|
||||
}
|
||||
|
||||
function getStaleByUser(int $userId, string $platform, bool $withRevoked = false): ?APIToken
|
||||
{
|
||||
return $this->toEntity($this->table->where([
|
||||
'user' => $userId,
|
||||
'platform' => $platform,
|
||||
'deleted' => $withRevoked,
|
||||
])->fetch());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ class Albums
|
|||
"owner" => $owner,
|
||||
"id" => $id
|
||||
])->fetch();
|
||||
|
||||
return new Album($album);
|
||||
|
||||
return $album ? new Album($album) : NULL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,11 +67,21 @@ class Applications
|
|||
return sizeof($this->appRels->where("user", $user->getId()));
|
||||
}
|
||||
|
||||
function find(string $query, array $pars = [], string $sort = "id"): Util\EntityStream
|
||||
function find(string $query = "", array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream
|
||||
{
|
||||
$query = "%$query%";
|
||||
$query = "%$query%";
|
||||
$result = $this->apps->where("CONCAT_WS(' ', name, description) LIKE ?", $query)->where("enabled", 1);
|
||||
$order_str = 'id';
|
||||
|
||||
switch($order['type']) {
|
||||
case 'id':
|
||||
$order_str = 'id ' . ($order['invert'] ? 'ASC' : 'DESC');
|
||||
break;
|
||||
}
|
||||
|
||||
if($order_str)
|
||||
$result->order($order_str);
|
||||
|
||||
return new Util\EntityStream("Application", $result->order("$sort"));
|
||||
return new Util\EntityStream("Application", $result);
|
||||
}
|
||||
}
|
316
Web/Models/Repositories/Audios.php
Normal file
316
Web/Models/Repositories/Audios.php
Normal file
|
@ -0,0 +1,316 @@
|
|||
<?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 (? IN BOOLEAN MODE)", "%$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([
|
||||
"unlisted" => 0,
|
||||
"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 $params = [], array $order = ['type' => 'id', 'invert' => false], int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
{
|
||||
$query = "%$query%";
|
||||
$result = $this->audios->where([
|
||||
"unlisted" => 0,
|
||||
"deleted" => 0,
|
||||
]);
|
||||
$order_str = (in_array($order['type'], ['id', 'length', 'listens']) ? $order['type'] : 'id') . ' ' . ($order['invert'] ? 'ASC' : 'DESC');;
|
||||
|
||||
if($params["only_performers"] == "1") {
|
||||
$result->where("performer LIKE ?", $query);
|
||||
} else {
|
||||
$result->where("name LIKE ? OR performer LIKE ?", $query, $query);
|
||||
}
|
||||
|
||||
foreach($params as $paramName => $paramValue) {
|
||||
if(is_null($paramValue) || $paramValue == '') continue;
|
||||
|
||||
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;
|
||||
case 'genre':
|
||||
if($paramValue == 'any') break;
|
||||
|
||||
$result->where("genre", $paramValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($order_str)
|
||||
$result->order($order_str);
|
||||
|
||||
return new Util\EntityStream("Audio", $result);
|
||||
}
|
||||
|
||||
function findPlaylists(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false]): \Traversable
|
||||
{
|
||||
$query = "%$query%";
|
||||
$result = $this->playlists->where([
|
||||
"deleted" => 0,
|
||||
])->where("CONCAT_WS(' ', name, description) LIKE ?", $query);
|
||||
$order_str = (in_array($order['type'], ['id', 'length', 'listens']) ? $order['type'] : 'id') . ' ' . ($order['invert'] ? 'ASC' : 'DESC');
|
||||
|
||||
if(is_null($params['from_me']) || empty($params['from_me']))
|
||||
$result->where(["unlisted" => 0]);
|
||||
|
||||
foreach($params as $paramName => $paramValue) {
|
||||
if(is_null($paramValue) || $paramValue == '') continue;
|
||||
|
||||
switch($paramName) {
|
||||
# БУДЬ МАКСИМАЛЬНО АККУРАТЕН С ДАННЫМ ПАРАМЕТРОМ
|
||||
case "from_me":
|
||||
$result->where("owner", $paramValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($order_str)
|
||||
$result->order($order_str);
|
||||
|
||||
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
|
||||
|
|
|
@ -42,18 +42,30 @@ class Clubs
|
|||
{
|
||||
return $this->toClub($this->clubs->get($id));
|
||||
}
|
||||
|
||||
function find(string $query, array $pars = [], string $sort = "id DESC", int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
|
||||
function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false], int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
{
|
||||
$query = "%$query%";
|
||||
$result = $this->clubs->where("name LIKE ? OR about LIKE ?", $query, $query);
|
||||
|
||||
return new Util\EntityStream("Club", $result->order($sort));
|
||||
$query = "%$query%";
|
||||
$result = $this->clubs;
|
||||
$order_str = 'id';
|
||||
|
||||
switch($order['type']) {
|
||||
case 'id':
|
||||
$order_str = 'id ' . ($order['invert'] ? 'ASC' : 'DESC');
|
||||
break;
|
||||
}
|
||||
|
||||
$result = $result->where("name LIKE ? OR about LIKE ?", $query, $query);
|
||||
|
||||
if($order_str)
|
||||
$result->order($order_str);
|
||||
|
||||
return new Util\EntityStream("Club", $result);
|
||||
}
|
||||
|
||||
function getCount(): int
|
||||
{
|
||||
return sizeof(clone $this->clubs);
|
||||
return (clone $this->clubs)->count('*');
|
||||
}
|
||||
|
||||
function getPopularClubs(): \Traversable
|
||||
|
|
|
@ -60,34 +60,31 @@ class Comments
|
|||
]));
|
||||
}
|
||||
|
||||
function find(string $query = "", array $pars = [], string $sort = "id"): Util\EntityStream
|
||||
function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream
|
||||
{
|
||||
$query = "%$query%";
|
||||
$result = $this->comments->where("content LIKE ?", "%$query%")->where("deleted", 0);
|
||||
$order_str = 'id';
|
||||
|
||||
$notNullParams = [];
|
||||
switch($order['type']) {
|
||||
case 'id':
|
||||
$order_str = 'created ' . ($order['invert'] ? 'ASC' : 'DESC');
|
||||
break;
|
||||
}
|
||||
|
||||
foreach($pars as $paramName => $paramValue)
|
||||
if($paramName != "before" && $paramName != "after")
|
||||
$paramValue != NULL ? $notNullParams+=["$paramName" => "%$paramValue%"] : NULL;
|
||||
else
|
||||
$paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
|
||||
|
||||
$result = $this->comments->where("content LIKE ?", $query)->where("deleted", 0);
|
||||
$nnparamsCount = sizeof($notNullParams);
|
||||
|
||||
if($nnparamsCount > 0) {
|
||||
foreach($notNullParams as $paramName => $paramValue) {
|
||||
switch($paramName) {
|
||||
case "before":
|
||||
$result->where("created < ?", $paramValue);
|
||||
break;
|
||||
case "after":
|
||||
$result->where("created > ?", $paramValue);
|
||||
break;
|
||||
}
|
||||
foreach($params as $paramName => $paramValue) {
|
||||
switch($paramName) {
|
||||
case "before":
|
||||
$result->where("created < ?", $paramValue);
|
||||
break;
|
||||
case "after":
|
||||
$result->where("created > ?", $paramValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Util\EntityStream("Comment", $result->order("$sort"));
|
||||
if($order_str)
|
||||
$result->order($order_str);
|
||||
|
||||
return new Util\EntityStream("Comment", $result);
|
||||
}
|
||||
}
|
||||
|
|
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);
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ class Notifications
|
|||
return (new $repoClassName)->get($id);
|
||||
}
|
||||
|
||||
private function getQuery(User $user, bool $count = false, int $offset, bool $archived = false, int $page = 1, ?int $perPage = NULL): string
|
||||
private function getQuery(User $user, bool $count, int $offset, bool $archived = false, int $page = 1, ?int $perPage = NULL): string
|
||||
{
|
||||
$query = "SELECT " . ($count ? "COUNT(*) AS cnt" : "*") . " FROM notifications WHERE recipientType=0 ";
|
||||
$query .= "AND timestamp " . ($archived ? "<" : ">") . "$offset AND recipientId=" . $user->getId();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,14 +58,59 @@ class Posts
|
|||
}
|
||||
|
||||
$sel = $this->posts->where([
|
||||
"wall" => $user,
|
||||
"pinned" => false,
|
||||
"deleted" => false,
|
||||
"wall" => $user,
|
||||
"pinned" => false,
|
||||
"deleted" => false,
|
||||
"suggested" => 0,
|
||||
])->order("created DESC")->limit($perPage, $offset);
|
||||
|
||||
foreach($sel as $post)
|
||||
yield new Post($post);
|
||||
}
|
||||
|
||||
function getOwnersPostsFromWall(int $user, int $page = 1, ?int $perPage = NULL, ?int $offset = NULL): \Traversable
|
||||
{
|
||||
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
||||
$offset ??= $perPage * ($page - 1);
|
||||
|
||||
$sel = $this->posts->where([
|
||||
"wall" => $user,
|
||||
"deleted" => false,
|
||||
"suggested" => 0,
|
||||
]);
|
||||
|
||||
if($user > 0)
|
||||
$sel->where("owner", $user);
|
||||
else
|
||||
$sel->where("flags !=", 0);
|
||||
|
||||
$sel->order("created DESC")->limit($perPage, $offset);
|
||||
|
||||
foreach($sel as $post)
|
||||
yield new Post($post);
|
||||
}
|
||||
|
||||
function getOthersPostsFromWall(int $user, int $page = 1, ?int $perPage = NULL, ?int $offset = NULL): \Traversable
|
||||
{
|
||||
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
||||
$offset ??= $perPage * ($page - 1);
|
||||
|
||||
$sel = $this->posts->where([
|
||||
"wall" => $user,
|
||||
"deleted" => false,
|
||||
"suggested" => 0,
|
||||
]);
|
||||
|
||||
if($user > 0)
|
||||
$sel->where("owner !=", $user);
|
||||
else
|
||||
$sel->where("flags", 0);
|
||||
|
||||
$sel->order("created DESC")->limit($perPage, $offset);
|
||||
|
||||
foreach($sel as $post)
|
||||
yield new Post($post);
|
||||
}
|
||||
|
||||
function getPostsByHashtag(string $hashtag, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
{
|
||||
|
@ -74,6 +119,7 @@ class Posts
|
|||
->where("MATCH (content) AGAINST (? IN BOOLEAN MODE)", "+$hashtag")
|
||||
->where("deleted", 0)
|
||||
->order("created DESC")
|
||||
->where("suggested", 0)
|
||||
->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
|
||||
|
||||
foreach($sel as $post)
|
||||
|
@ -85,14 +131,22 @@ class Posts
|
|||
$hashtag = "#$hashtag";
|
||||
$sel = $this->posts
|
||||
->where("content LIKE ?", "%$hashtag%")
|
||||
->where("deleted", 0);
|
||||
->where("deleted", 0)
|
||||
->where("suggested", 0);
|
||||
|
||||
return sizeof($sel);
|
||||
}
|
||||
|
||||
function getPostById(int $wall, int $post): ?Post
|
||||
function getPostById(int $wall, int $post, bool $forceSuggestion = false): ?Post
|
||||
{
|
||||
$post = $this->posts->where(['wall' => $wall, 'virtual_id' => $post])->fetch();
|
||||
$post = $this->posts->where(['wall' => $wall, 'virtual_id' => $post]);
|
||||
|
||||
if(!$forceSuggestion) {
|
||||
$post->where("suggested", 0);
|
||||
}
|
||||
|
||||
$post = $post->fetch();
|
||||
|
||||
if(!is_null($post))
|
||||
return new Post($post);
|
||||
else
|
||||
|
@ -100,45 +154,113 @@ class Posts
|
|||
|
||||
}
|
||||
|
||||
function find(string $query = "", array $pars = [], string $sort = "id"): Util\EntityStream
|
||||
function find(string $query = "", array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream
|
||||
{
|
||||
$query = "%$query%";
|
||||
$query = "%$query%";
|
||||
$result = $this->posts->where("content LIKE ?", $query)->where("deleted", 0)->where("suggested", 0);
|
||||
$order_str = 'id';
|
||||
|
||||
$notNullParams = [];
|
||||
switch($order['type']) {
|
||||
case 'id':
|
||||
$order_str = 'created ' . ($order['invert'] ? 'ASC' : 'DESC');
|
||||
break;
|
||||
}
|
||||
|
||||
foreach($pars as $paramName => $paramValue)
|
||||
if($paramName != "before" && $paramName != "after")
|
||||
$paramValue != NULL ? $notNullParams+=["$paramName" => "%$paramValue%"] : NULL;
|
||||
else
|
||||
$paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
|
||||
foreach($params as $paramName => $paramValue) {
|
||||
if(is_null($paramValue) || $paramValue == '') continue;
|
||||
|
||||
$result = $this->posts->where("content LIKE ?", $query)->where("deleted", 0);
|
||||
$nnparamsCount = sizeof($notNullParams);
|
||||
|
||||
if($nnparamsCount > 0) {
|
||||
foreach($notNullParams as $paramName => $paramValue) {
|
||||
switch($paramName) {
|
||||
case "before":
|
||||
$result->where("created < ?", $paramValue);
|
||||
break;
|
||||
case "after":
|
||||
$result->where("created > ?", $paramValue);
|
||||
break;
|
||||
}
|
||||
switch($paramName) {
|
||||
case "before":
|
||||
$result->where("created < ?", $paramValue);
|
||||
break;
|
||||
case "after":
|
||||
$result->where("created > ?", $paramValue);
|
||||
break;
|
||||
/*case 'die_in_agony':
|
||||
$result->where("nsfw", 1);
|
||||
break;
|
||||
case 'ads':
|
||||
$result->where("ad", 1);
|
||||
break;*/
|
||||
# БУДЬ МАКСИМАЛЬНО АККУРАТЕН С ДАННЫМ ПАРАМЕТРОМ
|
||||
case 'from_me':
|
||||
$result->where("owner", $paramValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($order_str)
|
||||
$result->order($order_str);
|
||||
|
||||
return new Util\EntityStream("Post", $result->order("$sort"));
|
||||
return new Util\EntityStream("Post", $result);
|
||||
}
|
||||
|
||||
function getPostCountOnUserWall(int $user): int
|
||||
{
|
||||
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0]));
|
||||
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0, "suggested" => 0]));
|
||||
}
|
||||
|
||||
function getOwnersCountOnUserWall(int $user): int
|
||||
{
|
||||
if($user > 0)
|
||||
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0, "owner" => $user]));
|
||||
else
|
||||
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0, "suggested" => 0])->where("flags !=", 0));
|
||||
}
|
||||
|
||||
function getOthersCountOnUserWall(int $user): int
|
||||
{
|
||||
if($user > 0)
|
||||
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0])->where("owner !=", $user));
|
||||
else
|
||||
return sizeof($this->posts->where(["wall" => $user, "deleted" => 0, "suggested" => 0])->where("flags", 0));
|
||||
}
|
||||
|
||||
function getSuggestedPosts(int $club, int $page = 1, ?int $perPage = NULL, ?int $offset = NULL): \Traversable
|
||||
{
|
||||
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
||||
$offset ??= $perPage * ($page - 1);
|
||||
|
||||
$sel = $this->posts
|
||||
->where("deleted", 0)
|
||||
->where("wall", $club * -1)
|
||||
->order("created DESC")
|
||||
->where("suggested", 1)
|
||||
->limit($perPage, $offset);
|
||||
|
||||
foreach($sel as $post)
|
||||
yield new Post($post);
|
||||
}
|
||||
|
||||
function getSuggestedPostsCount(int $club)
|
||||
{
|
||||
return sizeof($this->posts->where(["wall" => $club * -1, "deleted" => 0, "suggested" => 1]));
|
||||
}
|
||||
|
||||
function getSuggestedPostsByUser(int $club, int $user, int $page = 1, ?int $perPage = NULL, ?int $offset = NULL): \Traversable
|
||||
{
|
||||
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
|
||||
$offset ??= $perPage * ($page - 1);
|
||||
|
||||
$sel = $this->posts
|
||||
->where("deleted", 0)
|
||||
->where("wall", $club * -1)
|
||||
->where("owner", $user)
|
||||
->order("created DESC")
|
||||
->where("suggested", 1)
|
||||
->limit($perPage, $offset);
|
||||
|
||||
foreach($sel as $post)
|
||||
yield new Post($post);
|
||||
}
|
||||
|
||||
function getSuggestedPostsCountByUser(int $club, int $user): int
|
||||
{
|
||||
return sizeof($this->posts->where(["wall" => $club * -1, "deleted" => 0, "suggested" => 1, "owner" => $user]));
|
||||
}
|
||||
|
||||
function getCount(): int
|
||||
{
|
||||
return sizeof(clone $this->posts);
|
||||
return (clone $this->posts)->count('*');
|
||||
}
|
||||
}
|
||||
|
|
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,104 +44,94 @@ class Users
|
|||
return $alias->getUser();
|
||||
}
|
||||
|
||||
function getByChandlerUser(ChandlerUser $user): ?User
|
||||
function getByChandlerUserId(string $cid): ?User
|
||||
{
|
||||
return $this->toUser($this->users->where("user", $user->getId())->fetch());
|
||||
return $this->toUser($this->users->where("user", $cid)->fetch());
|
||||
}
|
||||
|
||||
function find(string $query, array $pars = [], string $sort = "id DESC"): Util\EntityStream
|
||||
function getByChandlerUser(?ChandlerUser $user): ?User
|
||||
{
|
||||
$query = "%$query%";
|
||||
return $user ? $this->getByChandlerUserId($user->getId()) : NULL;
|
||||
}
|
||||
|
||||
function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream
|
||||
{
|
||||
$query = "%$query%";
|
||||
$result = $this->users->where("CONCAT_WS(' ', first_name, last_name, pseudo, shortcode) LIKE ?", $query)->where("deleted", 0);
|
||||
|
||||
$notNullParams = [];
|
||||
$nnparamsCount = 0;
|
||||
|
||||
foreach($pars as $paramName => $paramValue)
|
||||
if($paramName != "before" && $paramName != "after" && $paramName != "gender" && $paramName != "maritalstatus" && $paramName != "politViews" && $paramName != "doNotSearchMe")
|
||||
$paramValue != NULL ? $notNullParams += ["$paramName" => "%$paramValue%"] : NULL;
|
||||
else
|
||||
$paramValue != NULL ? $notNullParams += ["$paramName" => "$paramValue"] : NULL;
|
||||
$order_str = 'id';
|
||||
|
||||
$nnparamsCount = sizeof($notNullParams);
|
||||
switch($order['type']) {
|
||||
case 'id':
|
||||
case 'reg_date':
|
||||
$order_str = 'id ' . ($order['invert'] ? 'ASC' : 'DESC');
|
||||
break;
|
||||
case 'rating':
|
||||
$order_str = 'rating DESC';
|
||||
break;
|
||||
}
|
||||
|
||||
if($nnparamsCount > 0) {
|
||||
foreach($notNullParams as $paramName => $paramValue) {
|
||||
switch($paramName) {
|
||||
case "hometown":
|
||||
$result->where("hometown LIKE ?", $paramValue);
|
||||
break;
|
||||
case "city":
|
||||
$result->where("city LIKE ?", $paramValue);
|
||||
break;
|
||||
case "maritalstatus":
|
||||
$result->where("marital_status ?", $paramValue);
|
||||
break;
|
||||
case "status":
|
||||
$result->where("status LIKE ?", $paramValue);
|
||||
break;
|
||||
case "politViews":
|
||||
$result->where("polit_views ?", $paramValue);
|
||||
break;
|
||||
case "email":
|
||||
$result->where("email_contact LIKE ?", $paramValue);
|
||||
break;
|
||||
case "telegram":
|
||||
$result->where("telegram LIKE ?", $paramValue);
|
||||
break;
|
||||
case "site":
|
||||
$result->where("telegram LIKE ?", $paramValue);
|
||||
break;
|
||||
case "address":
|
||||
$result->where("address LIKE ?", $paramValue);
|
||||
break;
|
||||
case "is_online":
|
||||
$result->where("online >= ?", time() - 900);
|
||||
break;
|
||||
case "interests":
|
||||
$result->where("interests LIKE ?", $paramValue);
|
||||
break;
|
||||
case "fav_mus":
|
||||
$result->where("fav_music LIKE ?", $paramValue);
|
||||
break;
|
||||
case "fav_films":
|
||||
$result->where("fav_films LIKE ?", $paramValue);
|
||||
break;
|
||||
case "fav_shows":
|
||||
$result->where("fav_shows LIKE ?", $paramValue);
|
||||
break;
|
||||
case "fav_books":
|
||||
$result->where("fav_books LIKE ?", $paramValue);
|
||||
break;
|
||||
case "fav_quote":
|
||||
$result->where("fav_quote LIKE ?", $paramValue);
|
||||
break;
|
||||
case "before":
|
||||
$result->where("UNIX_TIMESTAMP(since) < ?", $paramValue);
|
||||
break;
|
||||
case "after":
|
||||
$result->where("UNIX_TIMESTAMP(since) > ?", $paramValue);
|
||||
break;
|
||||
case "gender":
|
||||
$result->where("sex ?", $paramValue);
|
||||
break;
|
||||
case "doNotSearchMe":
|
||||
$result->where("id !=", $paramValue);
|
||||
break;
|
||||
}
|
||||
foreach($params as $paramName => $paramValue) {
|
||||
if(is_null($paramValue) || $paramValue == '') continue;
|
||||
|
||||
switch($paramName) {
|
||||
case "hometown":
|
||||
$result->where("hometown LIKE ?", "%$paramValue%");
|
||||
break;
|
||||
case "city":
|
||||
$result->where("city LIKE ?", "%$paramValue%");
|
||||
break;
|
||||
case "marital_status":
|
||||
$result->where("marital_status ?", $paramValue);
|
||||
break;
|
||||
case "polit_views":
|
||||
$result->where("polit_views ?", $paramValue);
|
||||
break;
|
||||
case "is_online":
|
||||
$result->where("online >= ?", time() - 900);
|
||||
break;
|
||||
case "fav_mus":
|
||||
$result->where("fav_music LIKE ?", "%$paramValue%");
|
||||
break;
|
||||
case "fav_films":
|
||||
$result->where("fav_films LIKE ?", "%$paramValue%");
|
||||
break;
|
||||
case "fav_shows":
|
||||
$result->where("fav_shows LIKE ?", "%$paramValue%");
|
||||
break;
|
||||
case "fav_books":
|
||||
$result->where("fav_books LIKE ?", "%$paramValue%");
|
||||
break;
|
||||
case "before":
|
||||
$result->where("UNIX_TIMESTAMP(since) < ?", $paramValue);
|
||||
break;
|
||||
case "after":
|
||||
$result->where("UNIX_TIMESTAMP(since) > ?", $paramValue);
|
||||
break;
|
||||
case "gender":
|
||||
if((int) $paramValue == 3) break;
|
||||
$result->where("sex ?", (int) $paramValue);
|
||||
break;
|
||||
case "ignore_id":
|
||||
$result->where("id != ?", $paramValue);
|
||||
break;
|
||||
case "ignore_private":
|
||||
$result->where("profile_type", 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($order_str)
|
||||
$result->order($order_str);
|
||||
|
||||
return new Util\EntityStream("User", $result->order($sort));
|
||||
return new Util\EntityStream("User", $result);
|
||||
}
|
||||
|
||||
function getStatistics(): object
|
||||
{
|
||||
return (object) [
|
||||
"all" => sizeof(clone $this->users),
|
||||
"active" => sizeof((clone $this->users)->where("online > 0")),
|
||||
"online" => sizeof((clone $this->users)->where("online >= ?", time() - 900)),
|
||||
"all" => (clone $this->users)->count('*'),
|
||||
"active" => (clone $this->users)->where("online > 0")->count('*'),
|
||||
"online" => (clone $this->users)->where("online >= ?", time() - 900)->count('*'),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -46,35 +46,43 @@ class Videos
|
|||
return sizeof($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0]));
|
||||
}
|
||||
|
||||
function find(string $query = "", array $pars = [], string $sort = "id"): Util\EntityStream
|
||||
function find(string $query = "", array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream
|
||||
{
|
||||
$query = "%$query%";
|
||||
|
||||
$notNullParams = [];
|
||||
|
||||
foreach($pars as $paramName => $paramValue)
|
||||
if($paramName != "before" && $paramName != "after")
|
||||
$paramValue != NULL ? $notNullParams+=["$paramName" => "%$paramValue%"] : NULL;
|
||||
else
|
||||
$paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
|
||||
|
||||
$query = "%$query%";
|
||||
$result = $this->videos->where("CONCAT_WS(' ', name, description) LIKE ?", $query)->where("deleted", 0);
|
||||
$nnparamsCount = sizeof($notNullParams);
|
||||
$order_str = 'id';
|
||||
|
||||
if($nnparamsCount > 0) {
|
||||
foreach($notNullParams as $paramName => $paramValue) {
|
||||
switch($paramName) {
|
||||
case "before":
|
||||
$result->where("created < ?", $paramValue);
|
||||
break;
|
||||
case "after":
|
||||
$result->where("created > ?", $paramValue);
|
||||
break;
|
||||
}
|
||||
switch($order['type']) {
|
||||
case 'id':
|
||||
$order_str = 'id ' . ($order['invert'] ? 'ASC' : 'DESC');
|
||||
break;
|
||||
}
|
||||
|
||||
foreach($params as $paramName => $paramValue) {
|
||||
switch($paramName) {
|
||||
case "before":
|
||||
$result->where("created < ?", $paramValue);
|
||||
break;
|
||||
case "after":
|
||||
$result->where("created > ?", $paramValue);
|
||||
break;
|
||||
case 'only_youtube':
|
||||
if((int) $paramValue != 1) break;
|
||||
$result->where("link != ?", 'NULL');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($order_str)
|
||||
$result->order($order_str);
|
||||
|
||||
return new Util\EntityStream("Video", $result->order("$sort"));
|
||||
return new Util\EntityStream("Video", $result);
|
||||
}
|
||||
|
||||
function getLastVideo(User $user)
|
||||
{
|
||||
$video = $this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0])->order("id DESC")->fetch();
|
||||
|
||||
return new Video($video);
|
||||
}
|
||||
}
|
||||
|
|
39
Web/Models/shell/processAudio.ps1
Normal file
39
Web/Models/shell/processAudio.ps1
Normal file
|
@ -0,0 +1,39 @@
|
|||
$ovkRoot = $args[0]
|
||||
$storageDir = $args[1]
|
||||
$fileHash = $args[2]
|
||||
$hashPart = $fileHash.substring(0, 2)
|
||||
$filename = $args[3]
|
||||
$audioFile = [System.IO.Path]::GetTempFileName()
|
||||
$temp = [System.IO.Path]::GetTempFileName()
|
||||
|
||||
$keyID = $args[4]
|
||||
$key = $args[5]
|
||||
$token = $args[6]
|
||||
$seg = $args[7]
|
||||
|
||||
$shell = Get-WmiObject Win32_process -filter "ProcessId = $PID"
|
||||
$shell.SetPriority(16384) # because there's no "nice" program in Windows we just set a lower priority for entire tree
|
||||
|
||||
Remove-Item $temp
|
||||
Remove-Item $audioFile
|
||||
New-Item -ItemType "directory" $temp
|
||||
New-Item -ItemType "directory" ("$temp/$fileHash" + '_fragments')
|
||||
New-Item -ItemType "directory" ("$storageDir/$hashPart/$fileHash" + '_fragments')
|
||||
Set-Location -Path $temp
|
||||
|
||||
Move-Item $filename $audioFile
|
||||
ffmpeg -i $audioFile -f dash -encryption_scheme cenc-aes-ctr -encryption_key $key `
|
||||
-encryption_kid $keyID -map 0:a -vn -c:a aac -ar 44100 -seg_duration $seg `
|
||||
-use_timeline 1 -use_template 1 -init_seg_name ($fileHash + '_fragments/0_0.$ext$') `
|
||||
-media_seg_name ($fileHash + '_fragments/chunk$Number%06d$_$RepresentationID$.$ext$') -adaptation_sets 'id=0,streams=a' `
|
||||
"$fileHash.mpd"
|
||||
|
||||
ffmpeg -i $audioFile -vn -ar 44100 "original_$token.mp3"
|
||||
Move-Item "original_$token.mp3" ($fileHash + '_fragments')
|
||||
|
||||
Get-ChildItem -Path ($fileHash + '_fragments/*') | Move-Item -Destination ("$storageDir/$hashPart/$fileHash" + '_fragments')
|
||||
Move-Item -Path ("$fileHash.mpd") -Destination "$storageDir/$hashPart"
|
||||
|
||||
cd ..
|
||||
Remove-Item -Recurse $temp
|
||||
Remove-Item $audioFile
|
35
Web/Models/shell/processAudio.sh
Normal file
35
Web/Models/shell/processAudio.sh
Normal file
|
@ -0,0 +1,35 @@
|
|||
ovkRoot=$1
|
||||
storageDir=$2
|
||||
fileHash=$3
|
||||
hashPart=$(echo $fileHash | cut -c1-2)
|
||||
filename=$4
|
||||
audioFile=$(mktemp)
|
||||
temp=$(mktemp -d)
|
||||
|
||||
keyID=$5
|
||||
key=$6
|
||||
token=$7
|
||||
seg=$8
|
||||
|
||||
trap 'rm -f "$temp" "$audioFile"' EXIT
|
||||
|
||||
mkdir -p "$temp/$fileHash"_fragments
|
||||
mkdir -p "$storageDir/$hashPart/$fileHash"_fragments
|
||||
cd "$temp"
|
||||
|
||||
mv "$filename" "$audioFile"
|
||||
ffmpeg -i "$audioFile" -f dash -encryption_scheme cenc-aes-ctr -encryption_key "$key" \
|
||||
-encryption_kid "$keyID" -map 0 -vn -c:a aac -ar 44100 -seg_duration "$seg" \
|
||||
-use_timeline 1 -use_template 1 -init_seg_name "$fileHash"_fragments/0_0."\$ext\$" \
|
||||
-media_seg_name "$fileHash"_fragments/chunk"\$Number"%06d\$_"\$RepresentationID\$"."\$ext\$" -adaptation_sets 'id=0,streams=a' \
|
||||
"$fileHash.mpd"
|
||||
|
||||
ffmpeg -i "$audioFile" -vn -ar 44100 "original_$token.mp3"
|
||||
mv "original_$token.mp3" "$fileHash"_fragments
|
||||
|
||||
mv "$fileHash"_fragments "$storageDir/$hashPart"
|
||||
mv "$fileHash.mpd" "$storageDir/$hashPart"
|
||||
|
||||
cd ..
|
||||
rm -rf "$temp"
|
||||
rm -f "$audioFile"
|
|
@ -1,5 +1,5 @@
|
|||
(SELECT DISTINCT(follower) AS __id FROM
|
||||
(SELECT follower FROM subscriptions WHERE target=? AND model="openvk\\Web\\Models\\Entities\\User") u0
|
||||
(SELECT follower, flags FROM subscriptions WHERE target=? AND model="openvk\\Web\\Models\\Entities\\User") u0
|
||||
LEFT JOIN
|
||||
(SELECT target FROM subscriptions WHERE follower=? AND model="openvk\\Web\\Models\\Entities\\User") u1
|
||||
ON u0.follower = u1.target WHERE u1.target IS NULL) u2
|
||||
|
|
6
Web/Models/sql/get-requests.tsql
Executable file
6
Web/Models/sql/get-requests.tsql
Executable file
|
@ -0,0 +1,6 @@
|
|||
(SELECT DISTINCT(follower) AS __id FROM
|
||||
(SELECT follower FROM subscriptions WHERE target=? AND flags=0 AND model="openvk\\Web\\Models\\Entities\\User") u0
|
||||
LEFT JOIN
|
||||
(SELECT target FROM subscriptions WHERE follower=? AND flags=0 AND model="openvk\\Web\\Models\\Entities\\User") u1
|
||||
ON u0.follower = u1.target WHERE u1.target IS NULL) u2
|
||||
INNER JOIN profiles ON profiles.id = u2.__id
|
|
@ -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"
|
||||
|
@ -141,6 +145,6 @@ final class AboutPresenter extends OpenVKPresenter
|
|||
|
||||
function renderDev(): void
|
||||
{
|
||||
$this->redirect("https://docs.openvk.uk/");
|
||||
$this->redirect("https://docs.ovk.to/");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -30,6 +48,13 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
|
||||
$this->flash("warn", tr("admin_commerce_disabled"), tr("admin_commerce_disabled_desc"));
|
||||
}
|
||||
|
||||
private function warnIfLongpoolBroken(): void
|
||||
{
|
||||
bdump(is_writable(CHANDLER_ROOT . '/tmp/events.bin'));
|
||||
if(file_exists(CHANDLER_ROOT . '/tmp/events.bin') == false || is_writable(CHANDLER_ROOT . '/tmp/events.bin') == false)
|
||||
$this->flash("warn", tr("admin_longpool_broken"), tr("admin_longpool_broken_desc", CHANDLER_ROOT . '/tmp/events.bin'));
|
||||
}
|
||||
|
||||
private function searchResults(object $repo, &$count)
|
||||
{
|
||||
|
@ -39,6 +64,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
|
||||
{
|
||||
|
@ -49,7 +83,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
|
||||
function renderIndex(): void
|
||||
{
|
||||
|
||||
$this->warnIfLongpoolBroken();
|
||||
}
|
||||
|
||||
function renderUsers(): void
|
||||
|
@ -83,8 +117,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"));
|
||||
|
@ -125,6 +161,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$club->setShortCode($this->postParam("shortcode"));
|
||||
$club->setVerified(empty($this->postParam("verify") ? 0 : 1));
|
||||
$club->setHide_From_Global_Feed(empty($this->postParam("hide_from_global_feed") ? 0 : 1));
|
||||
$club->setEnforce_Hiding_From_Global_Feed(empty($this->postParam("enforce_hiding_from_global_feed") ? 0 : 1));
|
||||
$club->save();
|
||||
break;
|
||||
case "ban":
|
||||
|
@ -279,7 +316,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":
|
||||
|
@ -298,7 +335,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:
|
||||
|
@ -329,10 +366,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();
|
||||
|
@ -356,13 +393,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") ]));
|
||||
}
|
||||
|
||||
|
@ -373,9 +416,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 ]));
|
||||
}
|
||||
|
@ -461,6 +512,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();
|
||||
|
@ -551,4 +610,87 @@ 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;
|
||||
}
|
||||
|
||||
$logs = iterator_to_array((new Logs)->search($filter));
|
||||
$this->template->logs = $logs;
|
||||
$this->template->object_types = (new Logs)->getTypes();
|
||||
}
|
||||
}
|
||||
|
|
803
Web/Presenters/AudioPresenter.php
Normal file
803
Web/Presenters/AudioPresenter.php
Normal file
|
@ -0,0 +1,803 @@
|
|||
<?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, OPENVK_DEFAULT_PER_PAGE);
|
||||
$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, OPENVK_DEFAULT_PER_PAGE);
|
||||
$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 && $page < 2)
|
||||
$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;
|
||||
$playlist = NULL;
|
||||
$isAjax = $this->postParam("ajax", false) == 1;
|
||||
|
||||
if(!is_null($this->queryParam("gid")) && !is_null($this->queryParam("playlist"))) {
|
||||
$this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"), null, $isAjax);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if(!is_null($this->queryParam("playlist"))) {
|
||||
$playlist_id = (int)$this->queryParam("playlist");
|
||||
$playlist = (new Audios)->getPlaylist($playlist_id);
|
||||
if(!$playlist || $playlist->isDeleted())
|
||||
$this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"), null, $isAjax);
|
||||
|
||||
if(!$playlist->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"), null, $isAjax);
|
||||
|
||||
$this->template->playlist = $playlist;
|
||||
$this->template->owner = $playlist->getOwner();
|
||||
}
|
||||
|
||||
$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";
|
||||
$is_unlisted = ($this->postParam("unlisted") ?? "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);
|
||||
$audio->setUnlisted($is_unlisted);
|
||||
|
||||
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"), "хз", null, $isAjax);
|
||||
} catch(\Exception $ex) {
|
||||
$this->flashFail("err", tr("error"), tr("ffmpeg_not_installed"), null, $isAjax);
|
||||
}
|
||||
|
||||
$audio->save();
|
||||
|
||||
if($playlist) {
|
||||
$playlist->add($audio);
|
||||
} else {
|
||||
$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;
|
||||
|
||||
if($playlist)
|
||||
$redirectLink = "/playlist" . $playlist->getPrettyId();
|
||||
|
||||
$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?section=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");
|
||||
$is_unlisted = (int)$this->postParam('is_unlisted');
|
||||
|
||||
$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($is_unlisted == 1)
|
||||
$playlist->setUnlisted(true);
|
||||
|
||||
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");
|
||||
$is_unlisted = (int)$this->postParam('is_unlisted');
|
||||
$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();
|
||||
$playlist->setUnlisted((bool)$is_unlisted);
|
||||
|
||||
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->cover = $playlist->getCoverPhoto();
|
||||
$this->template->cover_url = $this->template->cover ? $this->template->cover->getURL() : "/assets/packages/static/openvk/img/song.jpg";
|
||||
$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);
|
||||
$this->template->count = $playlist->size();
|
||||
}
|
||||
|
||||
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":
|
||||
$detailed = [];
|
||||
if($audio->isWithdrawn())
|
||||
$this->flashFail("err", "error", tr("invalid_audio"), null, true);
|
||||
|
||||
if(empty($this->postParam("clubs")))
|
||||
$this->flashFail("err", "error", 'clubs not passed', null, true);
|
||||
|
||||
$clubs_arr = explode(',', $this->postParam("clubs"));
|
||||
$count = sizeof($clubs_arr);
|
||||
if($count < 1 || $count > 10) {
|
||||
$this->flashFail("err", "error", tr('too_many_or_to_lack'), null, true);
|
||||
}
|
||||
|
||||
foreach($clubs_arr as $club_id) {
|
||||
$club = (new Clubs)->get((int)$club_id);
|
||||
if(!$club || !$club->canBeModifiedBy($this->user->identity))
|
||||
continue;
|
||||
|
||||
if(!$audio->isInLibraryOf($club)) {
|
||||
$detailed[$club_id] = true;
|
||||
$audio->add($club);
|
||||
} else {
|
||||
$detailed[$club_id] = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->returnJson(["success" => true, 'detailed' => $detailed]);
|
||||
break;
|
||||
case "add_to_playlist":
|
||||
$detailed = [];
|
||||
if($audio->isWithdrawn())
|
||||
$this->flashFail("err", "error", tr("invalid_audio"), null, true);
|
||||
|
||||
if(empty($this->postParam("playlists")))
|
||||
$this->flashFail("err", "error", 'playlists not passed', null, true);
|
||||
|
||||
$playlists_arr = explode(',', $this->postParam("playlists"));
|
||||
$count = sizeof($playlists_arr);
|
||||
if($count < 1 || $count > 10) {
|
||||
$this->flashFail("err", "error", tr('too_many_or_to_lack'), null, true);
|
||||
}
|
||||
|
||||
foreach($playlists_arr as $playlist_id) {
|
||||
$pid = explode('_', $playlist_id);
|
||||
$playlist = (new Audios)->getPlaylistByOwnerAndVID((int)$pid[0], (int)$pid[1]);
|
||||
if(!$playlist || !$playlist->canBeModifiedBy($this->user->identity))
|
||||
continue;
|
||||
|
||||
if(!$playlist->hasAudio($audio)) {
|
||||
$playlist->add($audio);
|
||||
$detailed[$playlist_id] = true;
|
||||
} else {
|
||||
$detailed[$playlist_id] = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->returnJson(["success" => true, 'detailed' => $detailed]);
|
||||
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;
|
||||
case "classic_search_context":
|
||||
$data = json_decode($this->postParam("context_entity"), true);
|
||||
|
||||
$params = [];
|
||||
$order = [
|
||||
"type" => $data['order'] ?? 'id',
|
||||
"invert" => (int)$data['invert'] == 1 ? true : false
|
||||
];
|
||||
|
||||
if($data['genre'] && $data['genre'] != 'any')
|
||||
$params['genre'] = $data['genre'];
|
||||
|
||||
if($data['only_performers'] && (int)$data['only_performers'] == 1)
|
||||
$params['only_performers'] = '1';
|
||||
|
||||
if($data['with_lyrics'] && (int)$data['with_lyrics'] == 1)
|
||||
$params['with_lyrics'] = '1';
|
||||
|
||||
$stream = $this->audios->find($data['query'], $params, $order);
|
||||
$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;
|
||||
|
@ -95,7 +95,17 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$user = new User;
|
||||
$user->setFirst_Name($this->postParam("first_name"));
|
||||
$user->setLast_Name($this->postParam("last_name"));
|
||||
$user->setSex((int)($this->postParam("sex") === "female"));
|
||||
switch ($this->postParam("pronouns")) {
|
||||
case 'male':
|
||||
$user->setSex(0);
|
||||
break;
|
||||
case 'female':
|
||||
$user->setSex(1);
|
||||
break;
|
||||
case 'neutral':
|
||||
$user->setSex(2);
|
||||
break;
|
||||
}
|
||||
$user->setEmail($this->postParam("email"));
|
||||
$user->setSince(date("Y-m-d H:i:s"));
|
||||
$user->setRegistering_Ip(CONNECTING_IP);
|
||||
|
@ -110,7 +120,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 +141,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
|
||||
$this->authenticator->authenticate($chUser->getId());
|
||||
$this->redirect("/id" . $user->getId());
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,9 +356,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
|
||||
{
|
||||
|
@ -43,6 +43,10 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
$entity = $repo->get($eId);
|
||||
if(!$entity) $this->notFound();
|
||||
|
||||
if(!$entity->canBeViewedBy($this->user->identity)) {
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
}
|
||||
|
||||
if($entity instanceof Topic && $entity->isClosed())
|
||||
$this->notFound();
|
||||
|
||||
|
@ -54,9 +58,6 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
if ($entity instanceof Post && $entity->getWallOwner()->isBanned())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
if($_FILES["_vid_attachment"] && OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
|
||||
$this->flashFail("err", tr("error"), "Video uploads are disabled by the system administrator.");
|
||||
|
||||
$flags = 0;
|
||||
if($this->postParam("as_group") === "on" && !is_null($club) && $club->canBeModifiedBy($this->user->identity))
|
||||
$flags |= 0b10000000;
|
||||
|
@ -66,31 +67,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;
|
||||
|
@ -102,14 +140,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)
|
||||
|
@ -124,7 +166,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
|
||||
|
@ -135,15 +177,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")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,12 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
$this->assertUserLoggedIn();
|
||||
|
||||
$user = $this->users->get($user);
|
||||
if(!$user)
|
||||
if(!$user || $user->isDeleted())
|
||||
$this->notFound();
|
||||
|
||||
if(!$user->canBeViewedBy($this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
|
||||
$this->template->user = $user;
|
||||
$this->template->page = $page = (int) ($this->queryParam("p") ?? 1);
|
||||
$this->template->count = $user->getGiftCount();
|
||||
|
@ -41,6 +44,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 +53,10 @@ 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"));
|
||||
|
||||
if(!$user->canBeViewedBy($this->user->identity))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
|
||||
$this->template->page = $page = (int) ($this->queryParam("p") ?? 1);
|
||||
$gifts = $cat->getGifts($page, null, $this->template->count);
|
||||
|
@ -66,14 +73,17 @@ 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"));
|
||||
|
||||
if(!$user->canBeViewedBy($this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
|
||||
$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 +101,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, Posts};
|
||||
use Chandler\Security\Authenticator;
|
||||
|
||||
final class GroupPresenter extends OpenVKPresenter
|
||||
|
@ -31,6 +31,15 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$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);
|
||||
}
|
||||
|
||||
if(!is_null($this->user->identity) && $club->getWallType() == 2) {
|
||||
if(!$club->canBeModifiedBy($this->user->identity))
|
||||
$this->template->suggestedPostsCountByUser = (new Posts)->getSuggestedPostsCountByUser($club->getId(), $this->user->id);
|
||||
else
|
||||
$this->template->suggestedPostsCountByEveryone = (new Posts)->getSuggestedPostsCount($club->getId());
|
||||
}
|
||||
|
||||
$this->template->club = $club;
|
||||
|
@ -54,7 +63,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;
|
||||
}
|
||||
|
@ -62,7 +71,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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +141,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$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()) {
|
||||
|
@ -150,9 +159,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()) {
|
||||
|
@ -164,11 +173,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()) {
|
||||
|
@ -180,16 +189,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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,11 +223,20 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
|
||||
$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);
|
||||
try {
|
||||
$club->setWall(empty($this->postParam("wall")) ? 0 : (int)$this->postParam("wall"));
|
||||
} catch(\Exception $e) {
|
||||
$this->flashFail("err", tr("error"), tr("error_invalid_wall_value"));
|
||||
}
|
||||
|
||||
$club->setAdministrators_List_Display(empty($this->postParam("administrators_list_display")) ? 0 : $this->postParam("administrators_list_display"));
|
||||
$club->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->setHide_From_Global_Feed(empty($this->postParam("hide_from_global_feed")) ? 0 : 1);
|
||||
$club->setEveryone_can_upload_audios(empty($this->postParam("upload_audios")) ? 0 : 1);
|
||||
|
||||
if (!$club->isHidingFromGlobalFeedEnforced()) {
|
||||
$club->setHide_From_Global_Feed(empty($this->postParam("hide_from_global_feed") ? 0 : 1));
|
||||
}
|
||||
|
||||
$website = $this->postParam("website") ?? "";
|
||||
if(empty($website))
|
||||
|
@ -245,7 +263,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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,59 +271,110 @@ 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"));
|
||||
}
|
||||
}
|
||||
|
||||
function renderSetAvatar(int $id)
|
||||
{
|
||||
$photo = new Photo;
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$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) {
|
||||
|
||||
if(!$club || $club->isBanned() || !$club->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"), NULL, true);
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST" && $_FILES["blob"]["error"] === UPLOAD_ERR_OK) {
|
||||
try {
|
||||
$photo = new Photo;
|
||||
|
||||
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
|
||||
if($anon && $this->user->id === $club->getOwner()->getId())
|
||||
$anon = $club->isOwnerHidden();
|
||||
else if($anon)
|
||||
$anon = $club->getManager($this->user->identity)->isHidden();
|
||||
|
||||
$photo->setOwner($this->user->id);
|
||||
$photo->setDescription("Club image");
|
||||
$photo->setFile($_FILES["ava"]);
|
||||
$photo->setFile($_FILES["blob"]);
|
||||
$photo->setCreated(time());
|
||||
$photo->setAnonymous($anon);
|
||||
$photo->save();
|
||||
|
||||
(new Albums)->getClubAvatarAlbum($club)->addPhoto($photo);
|
||||
|
||||
$flags = 0;
|
||||
$flags |= 0b00010000;
|
||||
$flags |= 0b10000000;
|
||||
if($this->postParam("on_wall") == 1) {
|
||||
$post = new Post;
|
||||
|
||||
$post->setOwner($this->user->id);
|
||||
$post->setWall($club->getId() * -1);
|
||||
$post->setCreated(time());
|
||||
$post->setContent("");
|
||||
|
||||
$post = new Post;
|
||||
$post->setOwner($this->user->id);
|
||||
$post->setWall($club->getId()*-1);
|
||||
$post->setCreated(time());
|
||||
$post->setContent("");
|
||||
$post->setFlags($flags);
|
||||
$post->save();
|
||||
$post->attach($photo);
|
||||
$flags = 0;
|
||||
$flags |= 0b00010000;
|
||||
$flags |= 0b10000000;
|
||||
|
||||
} catch(ISE $ex) {
|
||||
$name = $album->getName();
|
||||
$this->flashFail("err", "Неизвестная ошибка", "Не удалось сохранить фотографию.");
|
||||
$post->setFlags($flags);
|
||||
$post->save();
|
||||
|
||||
$post->attach($photo);
|
||||
}
|
||||
|
||||
} catch(\Throwable $ex) {
|
||||
$this->flashFail("err", tr("error"), tr("error_when_uploading_photo"), NULL, true);
|
||||
}
|
||||
|
||||
$this->returnJson([
|
||||
"success" => true,
|
||||
"new_photo" => $photo->getPrettyId(),
|
||||
"url" => $photo->getURL(),
|
||||
]);
|
||||
} else {
|
||||
return " ";
|
||||
}
|
||||
$this->returnJson([
|
||||
"url" => $photo->getURL(),
|
||||
"id" => $photo->getPrettyId()
|
||||
]);
|
||||
}
|
||||
|
||||
function renderDeleteAvatar(int $id) {
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$club = $this->clubs->get($id);
|
||||
|
||||
if(!$club || $club->isBanned() || !$club->canBeModifiedBy($this->user->identity))
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"), NULL, true);
|
||||
|
||||
$avatar = $club->getAvatarPhoto();
|
||||
|
||||
if(!$avatar)
|
||||
$this->flashFail("succ", tr("error"), "no avatar bro", NULL, true);
|
||||
|
||||
$avatar->isolate();
|
||||
|
||||
$newAvatar = $club->getAvatarPhoto();
|
||||
|
||||
if(!$newAvatar)
|
||||
$this->returnJson([
|
||||
"success" => true,
|
||||
"has_new_photo" => false,
|
||||
"new_photo" => NULL,
|
||||
"url" => "/assets/packages/static/openvk/img/camera_200.png",
|
||||
]);
|
||||
else
|
||||
$this->returnJson([
|
||||
"success" => true,
|
||||
"has_new_photo" => true,
|
||||
"new_photo" => $newAvatar->getPrettyId(),
|
||||
"url" => $newAvatar->getURL(),
|
||||
]);
|
||||
}
|
||||
|
||||
function renderEditBackdrop(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
@ -350,7 +419,7 @@ 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))
|
||||
|
@ -411,4 +480,37 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
|
||||
$this->flashFail("succ", tr("information_-1"), tr("group_owner_setted", $newOwner->getCanonicalName(), $club->getName()));
|
||||
}
|
||||
|
||||
function renderSuggested(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$club = $this->clubs->get($id);
|
||||
if(!$club)
|
||||
$this->notFound();
|
||||
else
|
||||
$this->template->club = $club;
|
||||
|
||||
if($club->getWallType() == 0) {
|
||||
$this->flash("err", tr("error_suggestions"), tr("error_suggestions_closed"));
|
||||
$this->redirect("/club".$club->getId());
|
||||
}
|
||||
|
||||
if($club->getWallType() == 1) {
|
||||
$this->flash("err", tr("error_suggestions"), tr("error_suggestions_open"));
|
||||
$this->redirect("/club".$club->getId());
|
||||
}
|
||||
|
||||
if(!$club->canBeModifiedBy($this->user->identity)) {
|
||||
$this->template->posts = iterator_to_array((new Posts)->getSuggestedPostsByUser($club->getId(), $this->user->id, (int) ($this->queryParam("p") ?? 1)));
|
||||
$this->template->count = (new Posts)->getSuggestedPostsCountByUser($club->getId(), $this->user->id);
|
||||
$this->template->type = "my";
|
||||
} else {
|
||||
$this->template->posts = iterator_to_array((new Posts)->getSuggestedPosts($club->getId(), (int) ($this->queryParam("p") ?? 1)));
|
||||
$this->template->count = (new Posts)->getSuggestedPostsCount($club->getId());
|
||||
$this->template->type = "everyone";
|
||||
}
|
||||
|
||||
$this->template->page = (int) ($this->queryParam("p") ?? 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
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, true);
|
||||
} else {
|
||||
$post = (new Comments)->get($post_id);
|
||||
}
|
||||
|
||||
|
||||
if(is_null($post)) {
|
||||
$this->returnJson([
|
||||
"success" => 0
|
||||
]);
|
||||
} else {
|
||||
$response = [];
|
||||
$attachments = $post->getChildren();
|
||||
foreach($attachments as $attachment)
|
||||
{
|
||||
if($attachment instanceof \openvk\Web\Models\Entities\Photo)
|
||||
{
|
||||
$response[] = [
|
||||
"url" => $attachment->getURLBySizeId('normal'),
|
||||
"id" => $attachment->getPrettyId()
|
||||
];
|
||||
}
|
||||
}
|
||||
$this->returnJson([
|
||||
"success" => 1,
|
||||
"body" => $response
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ final class MessengerPresenter extends OpenVKPresenter
|
|||
|
||||
$messages = [];
|
||||
$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()]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,6 +40,8 @@ final class NotesPresenter extends OpenVKPresenter
|
|||
$this->notFound();
|
||||
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
if(!$note->canBeViewedBy($this->user->identity))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
|
||||
$this->template->cCount = $note->getCommentsCount();
|
||||
$this->template->cPage = (int) ($this->queryParam("p") ?? 1);
|
||||
|
@ -107,7 +109,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 +137,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);
|
||||
}
|
||||
}
|
||||
|
|
16
Web/Presenters/OpenVKPresenter.php
Executable file → Normal file
16
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");
|
||||
|
@ -268,7 +274,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
setlocale(LC_TIME, ...(explode(";", tr("__locale"))));
|
||||
|
||||
if (!OPENVK_ROOT_CONF["openvk"]["preferences"]["maintenanceMode"]["all"]) {
|
||||
if (OPENVK_ROOT_CONF["openvk"]["preferences"]["maintenanceMode"][$this->presenterName]) {
|
||||
if ($this->presenterName && OPENVK_ROOT_CONF["openvk"]["preferences"]["maintenanceMode"][$this->presenterName]) {
|
||||
$this->pass("openvk!Maintenance->section", $this->presenterName);
|
||||
}
|
||||
} else {
|
||||
|
@ -301,7 +307,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
$theme = Themepacks::i()[Session::i()->get("_sessionTheme", "ovk")];
|
||||
} else if($this->requestParam("themePreview")) {
|
||||
$theme = Themepacks::i()[$this->requestParam("themePreview")];
|
||||
} else if($this->user->identity !== NULL && $this->user->identity->getTheme()) {
|
||||
} else if($this->user !== NULL && $this->user->identity !== NULL && $this->user->identity->getTheme()) {
|
||||
$theme = $this->user->identity->getTheme();
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue