feat: add linting of code (#1220)

* feat(lint): add php-cs-fixer for linting

Removing previous CODE_STYLE as it was not enforced anyway and using PER-CS 2.0.

This is not the reformatting commit.

* style: format code according to PER-CS 2.0 with php-cs-fixer

* ci(actions): add lint action

Resolves #1132.
This commit is contained in:
Alexander Minkin 2025-01-31 18:20:13 +03:00 committed by GitHub
parent 228f14e384
commit 6ec54a379d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
204 changed files with 13964 additions and 10020 deletions

28
.github/workflows/lint.yaml vendored Normal file
View file

@ -0,0 +1,28 @@
name: Lint
on:
push:
pull_request:
jobs:
lint:
runs-on: ubuntu-20.04
permissions:
contents: read
steps:
- name: Code Checkout
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.2"
extensions: gd, zip, intl, yaml, pdo_mysql, rdkafka, imagick
tools: composer:v2
coverage: none
- name: Install dependencies
run: composer install --no-interaction --no-progress --no-suggest --prefer-dist
- name: PHP CS Fixer
run: vendor/bin/php-cs-fixer fix --dry-run --diff

3
.gitignore vendored
View file

@ -15,4 +15,5 @@ themepacks/*
storage/*
!storage/.gitkeep
.idea
.idea
.php-cs-fixer.cache

14
.php-cs-fixer.dist.php Normal file
View file

@ -0,0 +1,14 @@
<?php
$finder = (new PhpCsFixer\Finder())
->in(__DIR__)
;
return (new PhpCsFixer\Config())
->setRules([
'@PER-CS2.0' => true,
'@PHP82Migration' => true,
])
->setFinder($finder)
->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect())
;

View file

@ -1,103 +1,107 @@
<?php declare(strict_types=1);
namespace openvk\CLI;
use Chandler\Database\DatabaseConnection;
use openvk\Web\Models\Repositories\Users;
use openvk\Web\Models\Entities\Notifications\CoinsTransferNotification;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Nette\Utils\ImageException;
define("NANOTON", 1000000000);
class FetchToncoinTransactions extends Command
{
private $images;
protected static $defaultName = "fetch-ton";
function __construct()
{
$this->transactions = DatabaseConnection::i()->getContext()->table("cryptotransactions");
parent::__construct();
}
protected function configure(): void
{
$this->setDescription("Fetches TON transactions to top up the users' balance")
->setHelp("This command checks for new transactions on TON Wallet and then top up the balance of specified users");
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$header = $output->section();
$header->writeln([
"TONCOIN Fetcher",
"=====================",
"",
]);
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["ton"]["enabled"]) {
$header->writeln("Sorry, but you handn't enabled the TON support in your config file yet.");
return Command::FAILURE;
}
$testnetSubdomain = OPENVK_ROOT_CONF["openvk"]["preferences"]["ton"]["testnet"] ? "testnet." : "";
$url = "https://" . $testnetSubdomain . "toncenter.com/api/v2/getTransactions?";
$opts = [
"http" => [
"method" => "GET",
"header" => "Accept: application/json"
]
];
$selection = $this->transactions->select('hash, lt')->order("id DESC")->limit(1)->fetch();
$trHash = $selection->hash ?? NULL;
$trLt = $selection->lt ?? NULL;
$data = http_build_query([
"address" => OPENVK_ROOT_CONF["openvk"]["preferences"]["ton"]["address"],
"limit" => 100,
"hash" => $trHash,
"to_lt" => $trLt
]);
$response = file_get_contents($url . $data, false, stream_context_create($opts));
$response = json_decode($response, true);
$header->writeln("Gonna up the balance of users");
foreach($response["result"] as $transfer) {
$outputArray;
preg_match('/' . OPENVK_ROOT_CONF["openvk"]["preferences"]["ton"]["regex"] . '/', $transfer["in_msg"]["message"], $outputArray);
$userId = ctype_digit($outputArray[1]) ? intval($outputArray[1]) : NULL;
if(is_null($userId)) {
$header->writeln("Well, that's a donation. Thanks! XD");
} else {
$user = (new Users)->get($userId);
if(!$user) {
$header->writeln("Well, that's a donation. Thanks! XD");
} else {
$value = ($transfer["in_msg"]["value"] / NANOTON) / OPENVK_ROOT_CONF["openvk"]["preferences"]["ton"]["rate"];
$user->setCoins($user->getCoins() + $value);
$user->save();
(new CoinsTransferNotification($user, (new Users)->get(OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"]), (int) $value, "Via TON cryptocurrency"))->emit();
$header->writeln($value . " coins are added to " . $user->getId() . " user id");
$this->transactions->insert([
"id" => NULL,
"hash" => $transfer["transaction_id"]["hash"],
"lt" => $transfer["transaction_id"]["lt"]
]);
}
}
}
$header->writeln("Processing finished :3");
return Command::SUCCESS;
}
}
<?php
declare(strict_types=1);
namespace openvk\CLI;
use Chandler\Database\DatabaseConnection;
use openvk\Web\Models\Repositories\Users;
use openvk\Web\Models\Entities\Notifications\CoinsTransferNotification;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Nette\Utils\ImageException;
define("NANOTON", 1000000000);
class FetchToncoinTransactions extends Command
{
private $images;
protected static $defaultName = "fetch-ton";
public function __construct()
{
$this->transactions = DatabaseConnection::i()->getContext()->table("cryptotransactions");
parent::__construct();
}
protected function configure(): void
{
$this->setDescription("Fetches TON transactions to top up the users' balance")
->setHelp("This command checks for new transactions on TON Wallet and then top up the balance of specified users");
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$header = $output->section();
$header->writeln([
"TONCOIN Fetcher",
"=====================",
"",
]);
if (!OPENVK_ROOT_CONF["openvk"]["preferences"]["ton"]["enabled"]) {
$header->writeln("Sorry, but you handn't enabled the TON support in your config file yet.");
return Command::FAILURE;
}
$testnetSubdomain = OPENVK_ROOT_CONF["openvk"]["preferences"]["ton"]["testnet"] ? "testnet." : "";
$url = "https://" . $testnetSubdomain . "toncenter.com/api/v2/getTransactions?";
$opts = [
"http" => [
"method" => "GET",
"header" => "Accept: application/json",
],
];
$selection = $this->transactions->select('hash, lt')->order("id DESC")->limit(1)->fetch();
$trHash = $selection->hash ?? null;
$trLt = $selection->lt ?? null;
$data = http_build_query([
"address" => OPENVK_ROOT_CONF["openvk"]["preferences"]["ton"]["address"],
"limit" => 100,
"hash" => $trHash,
"to_lt" => $trLt,
]);
$response = file_get_contents($url . $data, false, stream_context_create($opts));
$response = json_decode($response, true);
$header->writeln("Gonna up the balance of users");
foreach ($response["result"] as $transfer) {
$outputArray;
preg_match('/' . OPENVK_ROOT_CONF["openvk"]["preferences"]["ton"]["regex"] . '/', $transfer["in_msg"]["message"], $outputArray);
$userId = ctype_digit($outputArray[1]) ? intval($outputArray[1]) : null;
if (is_null($userId)) {
$header->writeln("Well, that's a donation. Thanks! XD");
} else {
$user = (new Users())->get($userId);
if (!$user) {
$header->writeln("Well, that's a donation. Thanks! XD");
} else {
$value = ($transfer["in_msg"]["value"] / NANOTON) / OPENVK_ROOT_CONF["openvk"]["preferences"]["ton"]["rate"];
$user->setCoins($user->getCoins() + $value);
$user->save();
(new CoinsTransferNotification($user, (new Users())->get(OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"]), (int) $value, "Via TON cryptocurrency"))->emit();
$header->writeln($value . " coins are added to " . $user->getId() . " user id");
$this->transactions->insert([
"id" => null,
"hash" => $transfer["transaction_id"]["hash"],
"lt" => $transfer["transaction_id"]["lt"],
]);
}
}
}
$header->writeln("Processing finished :3");
return Command::SUCCESS;
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\CLI;
use Chandler\Database\DatabaseConnection;
use openvk\Web\Models\Repositories\Photos;
use Symfony\Component\Console\Command\Command;
@ -14,7 +18,7 @@ class RebuildImagesCommand extends Command
protected static $defaultName = "build-images";
function __construct()
public function __construct()
{
$this->images = DatabaseConnection::i()->getContext()->table("photos");
@ -40,8 +44,9 @@ class RebuildImagesCommand extends Command
]);
$filter = ["deleted" => false];
if($input->getOption("upgrade-only"))
$filter["sizes"] = NULL;
if ($input->getOption("upgrade-only")) {
$filter["sizes"] = null;
}
$selection = $this->images->select("id")->where($filter);
$totalPics = $selection->count();
@ -52,24 +57,25 @@ class RebuildImagesCommand extends Command
$errors = 0;
$count = 0;
$avgTime = NULL;
$avgTime = null;
$begin = new \DateTimeImmutable("now");
foreach($selection as $idHolder) {
foreach ($selection as $idHolder) {
$start = microtime(true);
try {
$photo = (new Photos)->get($idHolder->id);
$photo = (new Photos())->get($idHolder->id);
$photo->getSizes(true, true);
$photo->getDimensions();
} catch(ImageException $ex) {
} catch (ImageException $ex) {
$errors++;
}
$timeConsumed = microtime(true) - $start;
if(!$avgTime)
if (!$avgTime) {
$avgTime = $timeConsumed;
else
} else {
$avgTime = ($avgTime + $timeConsumed) / 2;
}
$eta = $begin->getTimestamp() + ceil($totalPics * $avgTime);
$int = (new \DateTimeImmutable("now"))->diff(new \DateTimeImmutable("@$eta"));
@ -83,4 +89,4 @@ class RebuildImagesCommand extends Command
return Command::SUCCESS;
}
}
}

View file

@ -1,277 +0,0 @@
# Names
## Namespace Names
Namespaces should be written in PascalCase.
## File Names
Code directories should have their name written in PascalCase. Code files should contain only one class and have the name of that class.
In case of multiple class definitions in one file, it's name should be the same as the "primary" class name.
Non-code directories, non-class and non-code files should be named in lisp-case.
## Variable Names
Variable names should be written in camelCase. This also applies to function arguments, class instance names and methods.
## Constant Names
Constants are written in SCREAMING_SNAKE_CASE, but should be declared case-insensetive.
## Class Names
Classes in OpenVK should belong to `openvk\` namespace and be in the corresponding directory (according to PSR-4). Names of classes should be written in PascalCase.
## Function Names
camelCase and snake_case are allowed, but first one is the recommended way. This rule does not apply to class methods, which are written in camelCase only.
---
# Coding Rules
## File header
All OpenVK files must start with `<?php` open-tag followed by `declare(strict_types=1);` on the same line. The next line must contain namespace.
The lines after must contain use-declarations, each on it's own line (usage of {} operator is OK), if there is any. Then there must be an empty line. Example:
```php
<?php declare(strict_types=1);
namespace openvk;
use Chandler\Database\DatabaseConnection;
use Nette\Utils\{Image, FileSystem};
class ...
```
## NULL
Null should be written as constant, all-uppercase: `NULL`.
## Nullable (optional) pointer arguments
Optional pointer arguments should default to `nullptr`: `function something(&int? $myPointer = nullptr)`. `nullptr` must be written in lisp-case (lowercase).
## Comparing to NULL
In OpenVK `is_null` is the preferred way to check for equality to NULL. `??` must be used in assignments and where else possible.
In case if value can either be NULL or truthy, "boolean not" should be used to check if value is not null: `if(!$var)`.
## Arrays
Arrays must be defined with modern syntax: `[1, 2, 3]` (NOT `array(1, 2, 3)`).
Same applies to `list` construction: use `[$a, , $b] = $arr;` instead of `list($a, , $b) = $arr;`
## Casts
Typecasts must be done with modern syntax where possible: `(type) $var`. Int-to-string, string-to-int, etc conversions should also be dont with modern casting
syntax where possible, but should use `ctype_` functions where needed. `gettype`, `settype` should be used in dynamic programming only.
## Goto
```goto``` should be avoided.
## `continue n; `
It is preferable to use `continue n`, `break n` instead of guarding flags:
```php
# COOL AND GOOD
foreach($a as $b)
foreach($b as $c)
if($b == $c)
break 2;
# BRUH
foreach($a as $b) {
$shouldBreak = false;
foreach($b as $c)
if($b == $c)
$shouldBreak = true;
if($shouldBreak)
break;
}
```
## Comments
In OpenVK we use Perl-style `#` for single-line comments.
---
# Formatting
## Variables
It is preferable to declare only one variable per line in the code.
## Indentation
All things in OpenVK must be properly indented by a sequence of 4 spaces. Not tabs. \
When there are several variable declarations listed together, line up the variables:
```php
# OK
$photos = (new Photos)->where("meow", true);
$photo = $photos->fetch();
$arr = [
"a" => 10,
"bb" => true,
];
# NOT OK
$photos = (new Photos)->where("meow", true);
$photo = $photos->fetch();
$arr = [
"a" => 10,
"bb" => true,
];
```
## Tab/Space
+ **Do not use tabs**. Use spaces, as tabs are defined differently for different editors and printers.
+ Put one space after a comma and semicolons: `exp(1, 2)` `for($i = 1; $i < 100; $i++)`
+ Put one space around assignment operators: `$a = 1`
+ Always put a space around conditional operators: `$a = ($a > $b) ? $a : $b`
+ Do not put spaces between unary operators and their operands, primary operators and keywords:
```php
# OK
-$a;
$a++;
$b[1] = $a;
fun($b);
if($a) { ... }
# NOT OK
- $a;
$a ++;
$b [1] = $a;
fun ($b);
if ($a) { ... }
```
## Blank Lines
+ Use blank lines to create paragraphs in the code or comments to make the code more understandable
+ Use blank lines before `return` statement if it isn't the only statement in the block
+ Use blank lines after shorthand if/else/etc
```php
# OK
if($a)
return $x;
doSomething();
return "yay";
# NOT OK
if($a) return $x; # return must be on separate line
doSomething(); # doSomething must be separated by an extra blank line after short if/else
return "yay"; # do use blank lines before return statement
```
## Method/Function Arguments
+ When all arguments for a function do not fit on one line, try to line up the first argument in each line:
![image](https://user-images.githubusercontent.com/34442450/167248563-21fb01be-181d-48b9-ac0c-dc953c0a12cf.png)
+ If the argument lists are still too long to fit on the line, you may line up the arguments with the method name instead.
## Maximum characters per line
Lines should be no more than 80 characters long.
## Usage of curly braces
+ Curly braces should be on separate line for class, method, and function definitions.
+ In loops, if/else, try/catch, switch constructions the opening brace should be on the same line as the operator.
+ Braces must be ommited if the block contains only one statement **AND** the related blocks are also single statemented.
+ Nested single-statement+operator blocks must not be surrounded by braces.
```php
# OK
class A
{
function doSomethingFunny(): int
{
return 2;
}
}
if(true) {
doSomething();
doSomethingElse();
} else {
doSomethingFunny();
}
if($a)
return false;
else
doSomething();
foreach($b as $c => $d)
if($c == $d)
unset($b[$c]);
# NOT OK
class A {
function doSomethingFunny(): int {
return 2;
}
}
if(true) {
doSomething();
doSomethingElse();
} else
doSomethingFunny(); # why?
if($a) {
return false;
} else {
doSomething();
}
foreach($b as $c => $d) {
if($c == $d)
unset($b[$c]);
}
# lmao
if($a) { doSomething(); } else doSomethingElse();
```
## if/else, try/catch
+ Operators must not be indented with space from their operands but must have 1-space margin from braces:
```php
# OK
if($a) {
doSomething();
doSomethingElse();
} else if($b) {
try {
nukeSaintPetersburg('😈');
} finally {
return PEACE;
}
}
# NOT OK
if ($a) { # do not add space between control flow operator IF and it's operand
doSomething();
doSomethingElse();
}elseif($b){ # do add margin from braces; also ELSE and IF should be separate here
try{
nukeSaintPetersburg('😈');
}finally{
return PEACE;
}
}
```
## Switches
+ `break` must be on same indentation level as the code of le case (not the case definiton itself)
+ If there is no need to `break` a comment `# NOTICE falling through` must be places instead
```php
# OK
switch($a) {
case 1:
echo $a;
break;
case 2:
echo $a++;
# NOTICE falling through
default:
echo "c";
}
# NOT OK
switch($a) {
case 1:
echo $a;
break;
case 2:
echo $a++;
default:
echo "c";
}
```

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace Chandler\Database;
use Chandler\Database\DatabaseConnection;
use Nette\Database\Table\Selection;
use Nette\Database\Table\ActiveRow;
@ -7,9 +11,9 @@ use Nette\InvalidStateException as ISE;
use openvk\Web\Models\Repositories\CurrentUser;
use openvk\Web\Models\Repositories\Logs;
abstract class DBEntity
{
use \Nette\SmartObject;
protected $record;
protected $changes;
protected $deleted;
@ -17,20 +21,23 @@ abstract class DBEntity
protected $tableName;
function __construct(?ActiveRow $row = NULL)
public function __construct(?ActiveRow $row = null)
{
if(is_null($row)) return;
if (is_null($row)) {
return;
}
$_table = $row->getTable()->getName();
if($_table !== $this->tableName)
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)
public function __call(string $fName, array $args)
{
if(substr($fName, 0, 3) === "set") {
if (substr($fName, 0, 3) === "set") {
$field = mb_strtolower(substr($fName, 3));
$this->stateChanges($field, $args[0]);
} else {
@ -50,38 +57,40 @@ abstract class DBEntity
protected function stateChanges(string $column, $value): void
{
if(!is_null($this->record))
$t = $this->record->{$column}; #Test if column exists
if (!is_null($this->record)) {
$t = $this->record->{$column};
} #Test if column exists
$this->changes[$column] = $value;
}
function getId()
public function getId()
{
return $this->getRecord()->id;
}
function isDeleted(): bool
public function isDeleted(): bool
{
return (bool) $this->getRecord()->deleted;
}
function unwrap(): object
public function unwrap(): object
{
return (object) $this->getRecord()->toArray();
}
function delete(bool $softly = true): void
public 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))
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);
(new Logs())->create($user_id, $this->getTable()->getName(), get_class($this), 2, $this->record->toArray(), $this->changes);
if($softly) {
if ($softly) {
$this->record = $this->getTable()->where("id", $this->record->id)->update(["deleted" => true]);
} else {
$this->record->delete();
@ -89,39 +98,40 @@ abstract class DBEntity
}
}
function undelete(): void
public function undelete(): void
{
if(is_null($this->record))
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]);
(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
public 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();
$user_id = is_null($user) ? (int) OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"] : $user->getUser()->getId();
}
if(is_null($this->record)) {
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);
(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);
(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);
$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);
@ -131,10 +141,8 @@ abstract class DBEntity
$this->changes = [];
}
function getTableName(): string
public function getTableName(): string
{
return $this->getTable()->getName();
}
use \Nette\SmartObject;
}

View file

@ -1,4 +1,7 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\ServiceAPI;
use openvk\Web\Models\Entities\APIToken;
@ -11,19 +14,19 @@ class Apps implements Handler
{
private $user;
private $apps;
public function __construct(?User $user)
{
$this->user = $user;
$this->apps = new Applications;
$this->apps = new Applications();
}
function getUserInfo(callable $resolve, callable $reject): void
public function getUserInfo(callable $resolve, callable $reject): void
{
$hexId = dechex($this->user->getId());
$sign = hash_hmac("sha512/224", $hexId, CHANDLER_ROOT_CONF["security"]["secret"], true);
$marketingId = $hexId . "_" . base64_encode($sign);
$resolve([
"id" => $this->user->getId(),
"marketing_id" => $marketingId,
@ -35,82 +38,84 @@ class Apps implements Handler
"ava" => $this->user->getAvatarUrl(),
]);
}
function updatePermission(int $app, string $perm, string $state, callable $resolve, callable $reject): void
public function updatePermission(int $app, string $perm, string $state, callable $resolve, callable $reject): void
{
$app = $this->apps->get($app);
if(!$app || !$app->isEnabled()) {
$reject("No application with this id found");
return;
}
if(!$app->setPermission($this->user, $perm, $state == "yes"))
$reject("Invalid permission $perm");
$resolve(1);
}
function pay(int $appId, float $amount, callable $resolve, callable $reject): void
{
$app = $this->apps->get($appId);
if(!$app || !$app->isEnabled()) {
if (!$app || !$app->isEnabled()) {
$reject("No application with this id found");
return;
}
if($amount < 0) {
if (!$app->setPermission($this->user, $perm, $state == "yes")) {
$reject("Invalid permission $perm");
}
$resolve(1);
}
public function pay(int $appId, float $amount, callable $resolve, callable $reject): void
{
$app = $this->apps->get($appId);
if (!$app || !$app->isEnabled()) {
$reject("No application with this id found");
return;
}
if ($amount < 0) {
$reject(552, "Payment amount is invalid");
return;
}
$coinsLeft = $this->user->getCoins() - $amount;
if($coinsLeft < 0) {
if ($coinsLeft < 0) {
$reject(41, "Not enough money");
return;
}
$this->user->setCoins($coinsLeft);
$this->user->save();
$app->addCoins($amount);
$t = time();
$resolve($t . "," . hash_hmac("whirlpool", "$appId:$amount:$t", CHANDLER_ROOT_CONF["security"]["secret"]));
}
function withdrawFunds(int $appId, callable $resolve, callable $reject): void
public function withdrawFunds(int $appId, callable $resolve, callable $reject): void
{
$app = $this->apps->get($appId);
if(!$app) {
if (!$app) {
$reject("No application with this id found");
return;
} else if($app->getOwner()->getId() != $this->user->getId()) {
} elseif ($app->getOwner()->getId() != $this->user->getId()) {
$reject("You don't have rights to edit this app");
return;
}
$coins = $app->getBalance();
$app->withdrawCoins();
$resolve($coins);
}
function getRegularToken(string $clientName, bool $acceptsStale, callable $resolve, callable $reject): void
public function getRegularToken(string $clientName, bool $acceptsStale, callable $resolve, callable $reject): void
{
$token = NULL;
$token = null;
$stale = true;
if($acceptsStale)
$token = (new APITokens)->getStaleByUser($this->user->getId(), $clientName);
if(is_null($token)) {
if ($acceptsStale) {
$token = (new APITokens())->getStaleByUser($this->user->getId(), $clientName);
}
if (is_null($token)) {
$stale = false;
$token = new APIToken;
$token = new APIToken();
$token->setUser($this->user);
$token->setPlatform($clientName ?? (new WhichBrowser\Parser(getallheaders()))->toString());
$token->save();
}
$resolve([
'is_stale' => $stale,
'token' => $token->getFormattedToken(),
]);
}
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\ServiceAPI;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Repositories\Clubs;
@ -7,30 +11,30 @@ class Groups implements Handler
{
protected $user;
protected $groups;
function __construct(?User $user)
public function __construct(?User $user)
{
$this->user = $user;
$this->groups = new Clubs;
$this->groups = new Clubs();
}
function getWriteableClubs(callable $resolve, callable $reject)
public function getWriteableClubs(callable $resolve, callable $reject)
{
$clubs = [];
$wclubs = $this->groups->getWriteableClubs($this->user->getId());
$count = $this->groups->getWriteableClubsCount($this->user->getId());
if(!$count) {
if (!$count) {
$reject("You don't have any groups with write access");
return;
}
foreach($wclubs as $club) {
foreach ($wclubs as $club) {
$clubs[] = [
"name" => $club->getName(),
"id" => $club->getId(),
"avatar" => $club->getAvatarUrl() # если в овк когда-нибудь появится крутой список с аватарками, то можно использовать это поле
"avatar" => $club->getAvatarUrl(), # если в овк когда-нибудь появится крутой список с аватарками, то можно использовать это поле
];
}

View file

@ -1,8 +1,12 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\ServiceAPI;
use openvk\Web\Models\Entities\User;
interface Handler
{
function __construct(?User $user);
}
public function __construct(?User $user);
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\ServiceAPI;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Repositories\{Users, Clubs};
@ -7,16 +11,16 @@ class Mentions implements Handler
{
protected $user;
function __construct(?User $user)
public function __construct(?User $user)
{
$this->user = $user;
}
function resolve(int $id, callable $resolve, callable $reject): void
public function resolve(int $id, callable $resolve, callable $reject): void
{
if($id > 0) {
$user = (new Users)->get($id);
if(!$user) {
if ($id > 0) {
$user = (new Users())->get($id);
if (!$user) {
$reject("Not found");
return;
}
@ -32,8 +36,8 @@ class Mentions implements Handler
return;
}
$club = (new Clubs)->get(abs($id));
if(!$club) {
$club = (new Clubs())->get(abs($id));
if (!$club) {
$reject("Not found");
return;
}

View file

@ -1,6 +1,7 @@
<?php
namespace openvk\ServiceAPI;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Repositories\Notes as NoteRepo;
@ -8,27 +9,30 @@ class Notes implements Handler
{
protected $user;
protected $notes;
function __construct(?User $user)
public function __construct(?User $user)
{
$this->user = $user;
$this->notes = new NoteRepo;
$this->notes = new NoteRepo();
}
function getNote(int $noteId, callable $resolve, callable $reject): void
public function getNote(int $noteId, callable $resolve, callable $reject): void
{
$note = $this->notes->get($noteId);
if(!$note || $note->isDeleted())
if (!$note || $note->isDeleted()) {
$reject(83, "Note is gone");
}
$noteOwner = $note->getOwner();
assert($noteOwner instanceof User);
if(!$noteOwner->getPrivacyPermission("notes.read", $this->user))
if (!$noteOwner->getPrivacyPermission("notes.read", $this->user)) {
$reject(160, "You don't have permission to access this note");
}
if(!$note->canBeViewedBy($this->user))
if (!$note->canBeViewedBy($this->user)) {
$reject(15, "Access to note denied");
}
$resolve([
"title" => $note->getName(),
"link" => "/note" . $note->getPrettyId(),

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\ServiceAPI;
use Latte\Engine as TemplatingEngine;
use RdKafka\{Conf as RDKConf, KafkaConsumer};
use openvk\Web\Models\Entities\User;
@ -9,55 +13,55 @@ class Notifications implements Handler
{
protected $user;
protected $notifs;
function __construct(?User $user)
public function __construct(?User $user)
{
$this->user = $user;
$this->notifs = new N;
$this->notifs = new N();
}
function ack(callable $resolve, callable $reject): void
public function ack(callable $resolve, callable $reject): void
{
$this->user->updateNotificationOffset();
$this->user->save();
$resolve("OK");
}
function fetch(callable $resolve, callable $reject): void
public function fetch(callable $resolve, callable $reject): void
{
$kafkaConf = OPENVK_ROOT_CONF["openvk"]["credentials"]["notificationsBroker"];
if(!$kafkaConf["enable"]) {
if (!$kafkaConf["enable"]) {
$reject(1999, "Disabled");
return;
}
$kafkaConf = $kafkaConf["kafka"];
$conf = new RDKConf();
$conf->set("metadata.broker.list", $kafkaConf["addr"] . ":" . $kafkaConf["port"]);
$conf->set("group.id", "UserFetch-" . $this->user->getId()); # Чтобы уведы приходили только на разные устройства одного чебупелика
$conf->set("auto.offset.reset", "latest");
set_time_limit(30);
$consumer = new KafkaConsumer($conf);
$consumer->subscribe([ $kafkaConf["topic"] ]);
while(true) {
$message = $consumer->consume(30*1000);
while (true) {
$message = $consumer->consume(30 * 1000);
switch ($message->err) {
case RD_KAFKA_RESP_ERR_NO_ERROR:
$descriptor = $message->payload;
[,$user,] = explode(",", $descriptor);
if(((int) $user) === $this->user->getId()) {
if (((int) $user) === $this->user->getId()) {
$data = (object) [];
$notification = $this->notifs->fromDescriptor($descriptor, $data);
if(!$notification) {
if (!$notification) {
$reject(1982, "Server Error");
return;
}
$tplDir = __DIR__ . "/../Web/Presenters/templates/components/notifications/";
$tplId = "$tplDir$data->actionCode/_$data->originModelType" . "_" . $data->targetModelType . "_.xml";
$latte = new TemplatingEngine;
$latte = new TemplatingEngine();
$latte->setTempDirectory(CHANDLER_ROOT . "/tmp/cache/templates");
$latte->addFilter("translate", fn($trId) => tr($trId));
$resolve([
@ -68,7 +72,7 @@ class Notifications implements Handler
]);
return;
}
break;
case RD_KAFKA_RESP_ERR__TIMED_OUT:
case RD_KAFKA_RESP_ERR__PARTITION_EOF:

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\ServiceAPI;
use Chandler\MVC\Routing\Router;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Exceptions\{AlreadyVotedException, InvalidOptionException, PollLockedException};
@ -10,61 +14,61 @@ class Polls implements Handler
{
protected $user;
protected $polls;
function __construct(?User $user)
public function __construct(?User $user)
{
$this->user = $user;
$this->polls = new PollRepo;
$this->polls = new PollRepo();
}
private function getPollHtml(int $poll): string
{
return Router::i()->execute("/poll$poll", "SAPI");
}
function vote(int $pollId, string $options, callable $resolve, callable $reject): void
public function vote(int $pollId, string $options, callable $resolve, callable $reject): void
{
$poll = $this->polls->get($pollId);
if(!$poll) {
if (!$poll) {
$reject("Poll not found");
return;
}
try {
$options = explode(",", $options);
$poll->vote($this->user, $options);
} catch(AlreadyVotedException $ex) {
} catch (AlreadyVotedException $ex) {
$reject("Poll state changed: user has already voted.");
return;
} catch(PollLockedException $ex) {
} catch (PollLockedException $ex) {
$reject("Poll state changed: poll has ended.");
return;
} catch(InvalidOptionException $ex) {
} catch (InvalidOptionException $ex) {
$reject("Foreign options passed.");
return;
} catch(UnexpectedValueException $ex) {
} catch (UnexpectedValueException $ex) {
$reject("Too much options passed.");
return;
}
$resolve(["html" => $this->getPollHtml($pollId)]);
}
function unvote(int $pollId, callable $resolve, callable $reject): void
public function unvote(int $pollId, callable $resolve, callable $reject): void
{
$poll = $this->polls->get($pollId);
if(!$poll) {
if (!$poll) {
$reject("Poll not found");
return;
}
try {
$poll->revokeVote($this->user);
} catch(PollLockedException $ex) {
} catch (PollLockedException $ex) {
$reject("Votes can't be revoked from this poll.");
return;
}
$resolve(["html" => $this->getPollHtml($pollId)]);
}
}
}

View file

@ -1,23 +1,27 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\ServiceAPI;
use openvk\Web\Models\Entities\User;
use openvk\Web\Util\DateTime;
class Service implements Handler
{
protected $user;
function __construct(?User $user)
public function __construct(?User $user)
{
$this->user = $user;
}
function getTime(callable $resolve, callable $reject): void
public function getTime(callable $resolve, callable $reject): void
{
$resolve(trim((new DateTime)->format("%e %B %G" . tr("time_at_sp") . "%X")));
$resolve(trim((new DateTime())->format("%e %B %G" . tr("time_at_sp") . "%X")));
}
function getServerVersion(callable $resolve, callable $reject): void
public function getServerVersion(callable $resolve, callable $reject): void
{
$resolve("OVK " . OPENVK_VERSION);
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\ServiceAPI;
use openvk\Web\Models\Entities\Post;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Repositories\{Posts, Notes, Videos};
@ -9,26 +13,29 @@ class Wall implements Handler
protected $user;
protected $posts;
protected $notes;
function __construct(?User $user)
public function __construct(?User $user)
{
$this->user = $user;
$this->posts = new Posts;
$this->notes = new Notes;
$this->videos = new Videos;
$this->posts = new Posts();
$this->notes = new Notes();
$this->videos = new Videos();
}
function getPost(int $id, callable $resolve, callable $reject): void
public function getPost(int $id, callable $resolve, callable $reject): void
{
$post = $this->posts->get($id);
if(!$post || $post->isDeleted())
if (!$post || $post->isDeleted()) {
$reject(53, "No post with id=$id");
}
if($post->getSuggestionType() != 0)
if ($post->getSuggestionType() != 0) {
$reject(25, "Can't get suggested post");
if(!$post->canBeViewedBy($this->user))
}
if (!$post->canBeViewedBy($this->user)) {
$reject(12, "Access denied");
}
$res = (object) [];
$res->id = $post->getId();
@ -36,21 +43,22 @@ class Wall implements Handler
$res->author = (($owner = $post->getOwner())) instanceof User
? ($owner->getId())
: ($owner->getId() * -1);
if($post->isSigned())
if ($post->isSigned()) {
$res->signedOffBy = $post->getOwnerPost();
}
$res->pinned = $post->isPinned();
$res->sponsored = $post->isAd();
$res->nsfw = $post->isExplicit();
$res->text = $post->getText();
$res->likes = [
"count" => $post->getLikesCount(),
"hasLike" => $post->hasLikeFrom($this->user),
"likedBy" => [],
];
foreach($post->getLikers() as $liker) {
foreach ($post->getLikers() as $liker) {
$res->likes["likedBy"][] = [
"id" => $liker->getId(),
"url" => $liker->getURL(),
@ -58,17 +66,17 @@ class Wall implements Handler
"avatar" => $liker->getAvatarURL(),
];
}
$res->created = (string) $post->getPublicationTime();
$res->canPin = $post->canBePinnedBy($this->user);
$res->canEdit = $res->canDelete = $post->canBeDeletedBy($this->user);
$resolve((array) $res);
}
function newStatus(string $text, callable $resolve, callable $reject): void
public function newStatus(string $text, callable $resolve, callable $reject): void
{
$post = new Post;
$post = new Post();
$post->setOwner($this->user->getId());
$post->setWall($this->user->getId());
$post->setCreated(time());
@ -77,7 +85,7 @@ class Wall implements Handler
$post->setFlags(0);
$post->setNsfw(false);
$post->save();
$resolve($post->getId());
}
}

View file

@ -1,5 +1,7 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Exceptions;
class APIErrorException extends \Exception
{}
class APIErrorException extends \Exception {}

View file

@ -1,10 +1,14 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Models\Exceptions\InvalidUserNameException;
final class Account extends VKAPIRequestHandler
{
function getProfileInfo(): object
public function getProfileInfo(): object
{
$this->requireUser();
$user = $this->getUser();
@ -29,13 +33,14 @@ final class Account extends VKAPIRequestHandler
];
$audio_status = $user->getCurrentAudioStatus();
if(!is_null($audio_status))
if (!is_null($audio_status)) {
$return_object->audio_status = $audio_status->toVkApiStruct($user);
}
return $return_object;
}
function getInfo(): object
public function getInfo(): object
{
$this->requireUser();
@ -50,58 +55,58 @@ final class Account extends VKAPIRequestHandler
"is_new_live_streaming_enabled" => false,
"lang" => 1,
"no_wall_replies" => 0,
"own_posts_default" => 0
"own_posts_default" => 0,
];
}
function setOnline(): int
public function setOnline(): int
{
$this->requireUser();
$this->getUser()->updOnline($this->getPlatform());
return 1;
}
function setOffline(): int
public function setOffline(): int
{
$this->requireUser();
# Цiй метод є заглушка
return 1;
}
function getAppPermissions(): int
public function getAppPermissions(): int
{
return 9355263;
}
function getCounters(string $filter = ""): object
public function getCounters(string $filter = ""): object
{
$this->requireUser();
return (object) [
"friends" => $this->getUser()->getFollowersCount(),
"notifications" => $this->getUser()->getNotificationsCount(),
"messages" => $this->getUser()->getUnreadMessagesCount()
"messages" => $this->getUser()->getUnreadMessagesCount(),
];
# TODO: Filter
}
function saveProfileInfo(string $first_name = "", string $last_name = "", string $screen_name = "", int $sex = -1, int $relation = -1, string $bdate = "", int $bdate_visibility = -1, string $home_town = "", string $status = ""): object
public function saveProfileInfo(string $first_name = "", string $last_name = "", string $screen_name = "", int $sex = -1, int $relation = -1, string $bdate = "", int $bdate_visibility = -1, string $home_town = "", string $status = ""): object
{
$this->requireUser();
$this->willExecuteWriteAction();
$user = $this->getUser();
$output = [
"changed" => 0,
];
if(!empty($first_name) || !empty($last_name)) {
if (!empty($first_name) || !empty($last_name)) {
$output["name_request"] = [
"id" => random_int(1, 2048), # For compatibility with original VK API
"status" => "success",
@ -110,37 +115,44 @@ final class Account extends VKAPIRequestHandler
];
try {
if(!empty($first_name))
if (!empty($first_name)) {
$user->setFirst_name($first_name);
if(!empty($last_name))
}
if (!empty($last_name)) {
$user->setLast_Name($last_name);
}
} catch (InvalidUserNameException $e) {
$output["name_request"]["status"] = "declined";
return (object) $output;
}
}
if(!empty($screen_name))
if (!$user->setShortCode($screen_name))
if (!empty($screen_name)) {
if (!$user->setShortCode($screen_name)) {
$this->fail(1260, "Invalid screen name");
}
}
# For compatibility with original VK API
if($sex > 0)
if ($sex > 0) {
$user->setSex($sex == 1 ? 1 : 0);
if($relation > -1)
$user->setMarital_Status($relation);
}
if(!empty($bdate)) {
if ($relation > -1) {
$user->setMarital_Status($relation);
}
if (!empty($bdate)) {
$birthday = strtotime($bdate);
if (!is_int($birthday))
if (!is_int($birthday)) {
$this->fail(100, "invalid value of bdate.");
}
$user->setBirthday($birthday);
}
# For compatibility with original VK API
switch($bdate_visibility) {
switch ($bdate_visibility) {
case 0:
$this->fail(946, "Hiding date of birth is not implemented.");
break;
@ -150,14 +162,16 @@ final class Account extends VKAPIRequestHandler
case 2:
$user->setBirthday_privacy(1);
}
if(!empty($home_town))
$user->setHometown($home_town);
if(!empty($status))
if (!empty($home_town)) {
$user->setHometown($home_town);
}
if (!empty($status)) {
$user->setStatus($status);
if($sex > 0 || $relation > -1 || $bdate_visibility > 1 || !empty("$first_name$last_name$screen_name$bdate$home_town$status")) {
}
if ($sex > 0 || $relation > -1 || $bdate_visibility > 1 || !empty("$first_name$last_name$screen_name$bdate$home_town$status")) {
$output["changed"] = 1;
$user->save();
}
@ -165,21 +179,22 @@ final class Account extends VKAPIRequestHandler
return (object) $output;
}
function getBalance(): object
public function getBalance(): object
{
$this->requireUser();
if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce'])
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
public function getOvkSettings(): object
{
$this->requireUser();
$user = $this->getUser();
$settings_list = (object)[
$settings_list = (object) [
'avatar_style' => $user->getStyleAvatar(),
'style' => $user->getStyle(),
'show_rating' => !$user->prefersNotToSeeRating(),
@ -191,32 +206,39 @@ final class Account extends VKAPIRequestHandler
return $settings_list;
}
function sendVotes(int $receiver, int $value, string $message = ""): object
public function sendVotes(int $receiver, int $value, string $message = ""): object
{
$this->requireUser();
$this->willExecuteWriteAction();
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
if (!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"]) {
$this->fail(-105, "Commerce is disabled on this instance");
}
if($receiver < 0)
if ($receiver < 0) {
$this->fail(-248, "Invalid receiver id");
}
if($value < 1)
if ($value < 1) {
$this->fail(-248, "Invalid value");
}
if(iconv_strlen($message) > 255)
if (iconv_strlen($message) > 255) {
$this->fail(-249, "Message is too long");
}
if($this->getUser()->getCoins() < $value)
if ($this->getUser()->getCoins() < $value) {
$this->fail(-252, "Not enough votes");
}
$receiver_entity = (new \openvk\Web\Models\Repositories\Users)->get($receiver);
if(!$receiver_entity || $receiver_entity->isDeleted() || !$receiver_entity->canBeViewedBy($this->getUser()))
$receiver_entity = (new \openvk\Web\Models\Repositories\Users())->get($receiver);
if (!$receiver_entity || $receiver_entity->isDeleted() || !$receiver_entity->canBeViewedBy($this->getUser())) {
$this->fail(-250, "Invalid receiver");
}
if($receiver_entity->getId() === $this->getUser()->getId())
if ($receiver_entity->getId() === $this->getUser()->getId()) {
$this->fail(-251, "Can't transfer votes to yourself");
}
$this->getUser()->setCoins($this->getUser()->getCoins() - $value);
$this->getUser()->save();
@ -229,116 +251,126 @@ final class Account extends VKAPIRequestHandler
return (object) ['votes' => $this->getUser()->getCoins()];
}
function ban(int $owner_id): int
public function ban(int $owner_id): int
{
$this->requireUser();
$this->willExecuteWriteAction();
if($owner_id < 0)
return 1;
if($owner_id == $this->getUser()->getId())
if ($owner_id < 0) {
return 1;
}
if ($owner_id == $this->getUser()->getId()) {
$this->fail(15, "Access denied: cannot blacklist yourself");
}
$config_limit = OPENVK_ROOT_CONF['openvk']['preferences']['blacklists']['limit'] ?? 100;
$user_blocks = $this->getUser()->getBlacklistSize();
if(($user_blocks + 1) > $config_limit)
if (($user_blocks + 1) > $config_limit) {
$this->fail(-7856, "Blacklist limit exceeded");
}
$entity = get_entity_by_id($owner_id);
if(!$entity || $entity->isDeleted())
if (!$entity || $entity->isDeleted()) {
return 0;
}
if($entity->isBlacklistedBy($this->getUser()))
if ($entity->isBlacklistedBy($this->getUser())) {
return 1;
}
$this->getUser()->addToBlacklist($entity);
return 1;
}
function unban(int $owner_id): int
public function unban(int $owner_id): int
{
$this->requireUser();
$this->willExecuteWriteAction();
if($owner_id < 0)
return 1;
if($owner_id == $this->getUser()->getId())
if ($owner_id < 0) {
return 1;
}
if ($owner_id == $this->getUser()->getId()) {
return 1;
}
$entity = get_entity_by_id($owner_id);
if(!$entity)
if (!$entity) {
return 0;
}
if(!$entity->isBlacklistedBy($this->getUser()))
if (!$entity->isBlacklistedBy($this->getUser())) {
return 1;
}
$this->getUser()->removeFromBlacklist($entity);
return 1;
}
function getBanned(int $offset = 0, int $count = 100, string $fields = ""): object
public function getBanned(int $offset = 0, int $count = 100, string $fields = ""): object
{
$this->requireUser();
$result = (object)[
$result = (object) [
'count' => $this->getUser()->getBlacklistSize(),
'items' => [],
];
$banned = $this->getUser()->getBlacklist($offset, $count);
foreach($banned as $ban) {
if(!$ban) continue;
foreach ($banned as $ban) {
if (!$ban) {
continue;
}
$result->items[] = $ban->toVkApiStruct($this->getUser(), $fields);
}
return $result;
}
function saveInterestsInfo(
string $interests = NULL,
string $fav_music = NULL,
string $fav_films = NULL,
string $fav_shows = NULL,
string $fav_books = NULL,
string $fav_quote = NULL,
string $fav_games = NULL,
string $about = NULL,
)
{
public function saveInterestsInfo(
string $interests = null,
string $fav_music = null,
string $fav_films = null,
string $fav_shows = null,
string $fav_books = null,
string $fav_quote = null,
string $fav_games = null,
string $about = null,
) {
$this->requireUser();
$this->willExecuteWriteAction();
$user = $this->getUser();
$changes = 0;
$changes_array = [
"interests" => $interests,
"fav_music" => $fav_music,
"fav_films" => $fav_films,
"fav_books" => $fav_books,
"fav_shows" => $fav_shows,
"fav_quote" => $fav_quote,
"fav_games" => $fav_games,
"interests" => $interests,
"fav_music" => $fav_music,
"fav_films" => $fav_films,
"fav_books" => $fav_books,
"fav_shows" => $fav_shows,
"fav_quote" => $fav_quote,
"fav_games" => $fav_games,
"about" => $about,
];
foreach($changes_array as $change_name => $change_value) {
$set_name = "set".ucfirst($change_name);
$get_name = "get".str_replace("Fav", "Favorite", str_replace("_", "", ucfirst($change_name)));
if(!is_null($change_value) && $change_value !== $user->$get_name()) {
foreach ($changes_array as $change_name => $change_value) {
$set_name = "set" . ucfirst($change_name);
$get_name = "get" . str_replace("Fav", "Favorite", str_replace("_", "", ucfirst($change_name)));
if (!is_null($change_value) && $change_value !== $user->$get_name()) {
$user->$set_name(ovk_proc_strtr($change_value, 1000));
$changes += 1;
}
}
if($changes > 0) {
if ($changes > 0) {
$user->save();
}
return (object) [
"changed" => (int)($changes > 0),
"changed" => (int) ($changes > 0),
];
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\VKAPI\Handlers\Wall;
use openvk\Web\Models\Repositories\Topics as TopicsRepo;
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
@ -11,27 +15,28 @@ use openvk\Web\Models\Entities\{Topic, Comment, User, Photo, Video};
final class Board extends VKAPIRequestHandler
{
# 13/13
function addTopic(int $group_id, string $title, string $text = "", bool $from_group = true, string $attachments = "")
public function addTopic(int $group_id, string $title, string $text = "", bool $from_group = true, string $attachments = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
$club = (new ClubsRepo)->get($group_id);
$club = (new ClubsRepo())->get($group_id);
if(!$club) {
if (!$club) {
$this->fail(403, "Invalid club");
}
if(!$club->canBeModifiedBy($this->getUser()) && !$club->isEveryoneCanCreateTopics()) {
if (!$club->canBeModifiedBy($this->getUser()) && !$club->isEveryoneCanCreateTopics()) {
$this->fail(403, "Access to club denied");
}
$flags = 0;
if($from_group == true && $club->canBeModifiedBy($this->getUser()))
if ($from_group == true && $club->canBeModifiedBy($this->getUser())) {
$flags |= 0b10000000;
$topic = new Topic;
}
$topic = new Topic();
$topic->setGroup($club->getId());
$topic->setOwner($this->getUser()->getId());
$topic->setTitle(ovk_proc_strtr($title, 127));
@ -39,8 +44,8 @@ final class Board extends VKAPIRequestHandler
$topic->setFlags($flags);
$topic->save();
if(!empty($text)) {
$comment = new Comment;
if (!empty($text)) {
$comment = new Comment();
$comment->setOwner($this->getUser()->getId());
$comment->setModel(get_class($topic));
$comment->setTarget($topic->getId());
@ -48,45 +53,51 @@ final class Board extends VKAPIRequestHandler
$comment->setCreated(time());
$comment->setFlags($flags);
$comment->save();
if(!empty($attachments)) {
if (!empty($attachments)) {
$attachmentsArr = explode(",", $attachments);
# блин а мне это везде копировать типа
if(sizeof($attachmentsArr) > 10)
if (sizeof($attachmentsArr) > 10) {
$this->fail(50, "Error: too many attachments");
foreach($attachmentsArr as $attac) {
$attachmentType = NULL;
}
if(str_contains($attac, "photo"))
foreach ($attachmentsArr as $attac) {
$attachmentType = null;
if (str_contains($attac, "photo")) {
$attachmentType = "photo";
elseif(str_contains($attac, "video"))
} elseif (str_contains($attac, "video")) {
$attachmentType = "video";
else
} else {
$this->fail(205, "Unknown attachment type");
}
$attachment = str_replace($attachmentType, "", $attac);
$attachmentOwner = (int)explode("_", $attachment)[0];
$attachmentId = (int)end(explode("_", $attachment));
$attachmentOwner = (int) explode("_", $attachment)[0];
$attachmentId = (int) end(explode("_", $attachment));
$attacc = NULL;
$attacc = null;
if($attachmentType == "photo") {
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
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())
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this photo");
}
$comment->attach($attacc);
} elseif($attachmentType == "video") {
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
} 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())
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this video");
}
$comment->attach($attacc);
}
@ -100,18 +111,18 @@ final class Board extends VKAPIRequestHandler
return $topic->getId();
}
function closeTopic(int $group_id, int $topic_id)
public function closeTopic(int $group_id, int $topic_id)
{
$this->requireUser();
$this->willExecuteWriteAction();
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
$topic = (new TopicsRepo())->getTopicById($group_id, $topic_id);
if(!$topic || !$topic->getClub() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
if (!$topic || !$topic->getClub() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
return 0;
}
if(!$topic->isClosed()) {
if (!$topic->isClosed()) {
$topic->setClosed(1);
$topic->save();
}
@ -119,31 +130,32 @@ final class Board extends VKAPIRequestHandler
return 1;
}
function createComment(int $group_id, int $topic_id, string $message = "", string $attachments = "", bool $from_group = true)
public function createComment(int $group_id, int $topic_id, string $message = "", string $attachments = "", bool $from_group = true)
{
$this->requireUser();
$this->willExecuteWriteAction();
if(empty($message) && empty($attachments)) {
if (empty($message) && empty($attachments)) {
$this->fail(100, "Required parameter 'message' missing.");
}
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
$topic = (new TopicsRepo())->getTopicById($group_id, $topic_id);
if(!$topic || $topic->isDeleted() || $topic->isClosed()) {
if (!$topic || $topic->isDeleted() || $topic->isClosed()) {
$this->fail(100, "Topic is deleted, closed or invalid.");
}
$flags = 0;
if($from_group != 0 && !is_null($topic->getClub()) && $topic->getClub()->canBeModifiedBy($this->user))
if ($from_group != 0 && !is_null($topic->getClub()) && $topic->getClub()->canBeModifiedBy($this->user)) {
$flags |= 0b10000000;
}
if(strlen($message) > 300) {
if (strlen($message) > 300) {
$this->fail(20, "Comment is too long.");
}
$comment = new Comment;
$comment = new Comment();
$comment->setOwner($this->getUser()->getId());
$comment->setModel(get_class($topic));
$comment->setTarget($topic->getId());
@ -152,43 +164,49 @@ final class Board extends VKAPIRequestHandler
$comment->setFlags($flags);
$comment->save();
if(!empty($attachments)) {
if (!empty($attachments)) {
$attachmentsArr = explode(",", $attachments);
if(sizeof($attachmentsArr) > 10)
if (sizeof($attachmentsArr) > 10) {
$this->fail(50, "Error: too many attachments");
foreach($attachmentsArr as $attac) {
$attachmentType = NULL;
}
if(str_contains($attac, "photo"))
foreach ($attachmentsArr as $attac) {
$attachmentType = null;
if (str_contains($attac, "photo")) {
$attachmentType = "photo";
elseif(str_contains($attac, "video"))
} elseif (str_contains($attac, "video")) {
$attachmentType = "video";
else
} else {
$this->fail(205, "Unknown attachment type");
}
$attachment = str_replace($attachmentType, "", $attac);
$attachmentOwner = (int)explode("_", $attachment)[0];
$attachmentId = (int)end(explode("_", $attachment));
$attachmentOwner = (int) explode("_", $attachment)[0];
$attachmentId = (int) end(explode("_", $attachment));
$attacc = NULL;
$attacc = null;
if($attachmentType == "photo") {
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
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())
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this photo");
}
$comment->attach($attacc);
} elseif($attachmentType == "video") {
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
} 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())
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this video");
}
$comment->attach($attacc);
}
@ -198,29 +216,30 @@ final class Board extends VKAPIRequestHandler
return $comment->getId();
}
function deleteComment(int $comment_id, int $group_id = 0, int $topic_id = 0)
public function deleteComment(int $comment_id, int $group_id = 0, int $topic_id = 0)
{
$this->requireUser();
$this->willExecuteWriteAction();
$comment = (new CommentsRepo)->get($comment_id);
$comment = (new CommentsRepo())->get($comment_id);
if($comment->isDeleted() || !$comment || !$comment->canBeDeletedBy($this->getUser()))
if ($comment->isDeleted() || !$comment || !$comment->canBeDeletedBy($this->getUser())) {
$this->fail(403, "Access to comment denied");
}
$comment->delete();
return 1;
}
function deleteTopic(int $group_id, int $topic_id)
public function deleteTopic(int $group_id, int $topic_id)
{
$this->requireUser();
$this->willExecuteWriteAction();
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
$topic = (new TopicsRepo())->getTopicById($group_id, $topic_id);
if(!$topic || !$topic->getClub() || $topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
if (!$topic || !$topic->getClub() || $topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
return 0;
}
@ -229,7 +248,7 @@ final class Board extends VKAPIRequestHandler
return 1;
}
function editComment(int $comment_id, int $group_id = 0, int $topic_id = 0, string $message, string $attachments)
public function editComment(int $comment_id, int $group_id = 0, int $topic_id = 0, string $message, string $attachments)
{
/*
$this->requireUser();
@ -239,7 +258,7 @@ final class Board extends VKAPIRequestHandler
if($comment->getOwner() != $this->getUser()->getId())
$this->fail(15, "Access to comment denied");
$comment->setContent($message);
$comment->setEdited(time());
$comment->save();
@ -247,14 +266,14 @@ final class Board extends VKAPIRequestHandler
return 1;
}
function editTopic(int $group_id, int $topic_id, string $title)
public function editTopic(int $group_id, int $topic_id, string $title)
{
$this->requireUser();
$this->willExecuteWriteAction();
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
$topic = (new TopicsRepo())->getTopicById($group_id, $topic_id);
if(!$topic || !$topic->getClub() || $topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
if (!$topic || !$topic->getClub() || $topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
return 0;
}
@ -265,14 +284,14 @@ final class Board extends VKAPIRequestHandler
return 1;
}
function fixTopic(int $group_id, int $topic_id)
public function fixTopic(int $group_id, int $topic_id)
{
$this->requireUser();
$this->willExecuteWriteAction();
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
$topic = (new TopicsRepo())->getTopicById($group_id, $topic_id);
if(!$topic || !$topic->getClub() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
if (!$topic || !$topic->getClub() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
return 0;
}
@ -283,31 +302,31 @@ final class Board extends VKAPIRequestHandler
return 1;
}
function getComments(int $group_id, int $topic_id, bool $need_likes = false, int $start_comment_id = 0, int $offset = 0, int $count = 40, bool $extended = false, string $sort = "asc")
public function getComments(int $group_id, int $topic_id, bool $need_likes = false, int $start_comment_id = 0, int $offset = 0, int $count = 40, bool $extended = false, string $sort = "asc")
{
# start_comment_id ne robit
$this->requireUser();
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
if(!$topic || !$topic->getClub() || $topic->isDeleted()) {
$topic = (new TopicsRepo())->getTopicById($group_id, $topic_id);
if (!$topic || !$topic->getClub() || $topic->isDeleted()) {
$this->fail(5, "Invalid topic");
}
$arr = [
"items" => []
"items" => [],
];
$comms = array_slice(iterator_to_array($topic->getComments(1, $count + $offset)), $offset);
foreach($comms as $comm) {
foreach ($comms as $comm) {
$arr["items"][] = $this->getApiBoardComment($comm, $need_likes);
if($extended) {
if($comm->getOwner() instanceof \openvk\Web\Models\Entities\User) {
if ($extended) {
if ($comm->getOwner() instanceof \openvk\Web\Models\Entities\User) {
$arr["profiles"][] = $comm->getOwner()->toVkApiStruct();
}
if($comm->getOwner() instanceof \openvk\Web\Models\Entities\Club) {
if ($comm->getOwner() instanceof \openvk\Web\Models\Entities\Club) {
$arr["groups"][] = $comm->getOwner()->toVkApiStruct();
}
}
@ -316,34 +335,36 @@ final class Board extends VKAPIRequestHandler
return $arr;
}
function getTopics(int $group_id, string $topic_ids = "", int $order = 1, int $offset = 0, int $count = 40, bool $extended = false, int $preview = 0, int $preview_length = 90)
public function getTopics(int $group_id, string $topic_ids = "", int $order = 1, int $offset = 0, int $count = 40, bool $extended = false, int $preview = 0, int $preview_length = 90)
{
# order и extended ничё не делают
$this->requireUser();
$arr = [];
$club = (new ClubsRepo)->get($group_id);
$club = (new ClubsRepo())->get($group_id);
$topics = array_slice(iterator_to_array((new TopicsRepo)->getClubTopics($club, 1, $count + $offset)), $offset);
$arr["count"] = (new TopicsRepo)->getClubTopicsCount($club);
$topics = array_slice(iterator_to_array((new TopicsRepo())->getClubTopics($club, 1, $count + $offset)), $offset);
$arr["count"] = (new TopicsRepo())->getClubTopicsCount($club);
$arr["items"] = [];
$arr["default_order"] = $order;
$arr["can_add_topics"] = $club->canBeModifiedBy($this->getUser()) ? true : ($club->isEveryoneCanCreateTopics() ? true : false);
$arr["profiles"] = [];
if(empty($topic_ids)) {
foreach($topics as $topic) {
if($topic->isDeleted()) continue;
if (empty($topic_ids)) {
foreach ($topics as $topic) {
if ($topic->isDeleted()) {
continue;
}
$arr["items"][] = $topic->toVkApiStruct($preview, $preview_length > 1 ? $preview_length : 90);
}
} else {
$topics = explode(',', $topic_ids);
foreach($topics as $topic) {
foreach ($topics as $topic) {
$id = explode("_", $topic);
$topicy = (new TopicsRepo)->getTopicById((int)$id[0], (int)$id[1]);
$topicy = (new TopicsRepo())->getTopicById((int) $id[0], (int) $id[1]);
if($topicy && !$topicy->isDeleted()) {
if ($topicy && !$topicy->isDeleted()) {
$arr["items"][] = $topicy->toVkApiStruct($preview, $preview_length > 1 ? $preview_length : 90);
}
}
@ -352,18 +373,18 @@ final class Board extends VKAPIRequestHandler
return $arr;
}
function openTopic(int $group_id, int $topic_id)
public function openTopic(int $group_id, int $topic_id)
{
$this->requireUser();
$this->willExecuteWriteAction();
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
$topic = (new TopicsRepo())->getTopicById($group_id, $topic_id);
if(!$topic || !$topic->getClub() || !$topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
if (!$topic || !$topic->getClub() || !$topic->isDeleted() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
return 0;
}
if($topic->isClosed()) {
if ($topic->isClosed()) {
$topic->setClosed(0);
$topic->save();
}
@ -371,23 +392,23 @@ final class Board extends VKAPIRequestHandler
return 1;
}
function restoreComment(int $group_id, int $topic_id, int $comment_id)
public function restoreComment(int $group_id, int $topic_id, int $comment_id)
{
$this->fail(501, "Not implemented");
}
function unfixTopic(int $group_id, int $topic_id)
public function unfixTopic(int $group_id, int $topic_id)
{
$this->requireUser();
$this->willExecuteWriteAction();
$topic = (new TopicsRepo)->getTopicById($group_id, $topic_id);
$topic = (new TopicsRepo())->getTopicById($group_id, $topic_id);
if(!$topic || !$topic->getClub() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
if (!$topic || !$topic->getClub() || !$topic->getClub()->canBeModifiedBy($this->getUser())) {
return 0;
}
if($topic->isPinned()) {
if ($topic->isPinned()) {
$topic->setClosed(0);
$topic->save();
}
@ -409,21 +430,22 @@ final class Board extends VKAPIRequestHandler
$res->text = $comment->getText(false);
$res->attachments = [];
$res->likes = [];
if($need_likes) {
if ($need_likes) {
$res->likes = [
"count" => $comment->getLikesCount(),
"user_likes" => (int) $comment->hasLikeFrom($this->getUser()),
"can_like" => 1 # а чё типо не может ахахаххахах
"can_like" => 1, # а чё типо не может ахахаххахах
];
}
foreach($comment->getChildren() as $attachment) {
if($attachment->isDeleted())
foreach ($comment->getChildren() as $attachment) {
if ($attachment->isDeleted()) {
continue;
}
$res->attachments[] = $attachment->toVkApiStruct();
}
return $res;
}
}
}

View file

@ -1,48 +1,57 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use Chandler\Database\DatabaseConnection;
use openvk\Web\Models\Entities\Document;
use openvk\Web\Models\Repositories\Documents;
final class Docs extends VKAPIRequestHandler
{
function add(int $owner_id, int $doc_id, ?string $access_key): string
public function add(int $owner_id, int $doc_id, ?string $access_key): string
{
$this->requireUser();
$this->willExecuteWriteAction();
$doc = (new Documents)->getDocumentById($owner_id, $doc_id, $access_key);
if(!$doc || $doc->isDeleted())
$doc = (new Documents())->getDocumentById($owner_id, $doc_id, $access_key);
if (!$doc || $doc->isDeleted()) {
$this->fail(1150, "Invalid document id");
}
if(!$doc->checkAccessKey($access_key))
if (!$doc->checkAccessKey($access_key)) {
$this->fail(15, "Access denied");
}
if($doc->isCopiedBy($this->getUser()))
if ($doc->isCopiedBy($this->getUser())) {
$this->fail(100, "One of the parameters specified was missing or invalid: this document already added");
}
$new_doc = $doc->copy($this->getUser());
return $new_doc->getPrettyId();
}
function delete(int $owner_id, int $doc_id): int
public function delete(int $owner_id, int $doc_id): int
{
$this->requireUser();
$this->willExecuteWriteAction();
$doc = (new Documents)->getDocumentByIdUnsafe($owner_id, $doc_id);
if(!$doc || $doc->isDeleted())
$doc = (new Documents())->getDocumentByIdUnsafe($owner_id, $doc_id);
if (!$doc || $doc->isDeleted()) {
$this->fail(1150, "Invalid document id");
}
if(!$doc->canBeModifiedBy($this->getUser()))
if (!$doc->canBeModifiedBy($this->getUser())) {
$this->fail(1153, "Access to document is denied");
}
$doc->delete();
return 1;
}
function restore(int $owner_id, int $doc_id): int
public function restore(int $owner_id, int $doc_id): int
{
$this->requireUser();
$this->willExecuteWriteAction();
@ -50,77 +59,87 @@ final class Docs extends VKAPIRequestHandler
return $this->add($owner_id, $doc_id, "");
}
function edit(int $owner_id, int $doc_id, ?string $title = "", ?string $tags = "", ?int $folder_id = 0, int $owner_hidden = -1): int
public function edit(int $owner_id, int $doc_id, ?string $title = "", ?string $tags = "", ?int $folder_id = 0, int $owner_hidden = -1): int
{
$this->requireUser();
$this->willExecuteWriteAction();
$doc = (new Documents)->getDocumentByIdUnsafe($owner_id, $doc_id);
if(!$doc || $doc->isDeleted())
$doc = (new Documents())->getDocumentByIdUnsafe($owner_id, $doc_id);
if (!$doc || $doc->isDeleted()) {
$this->fail(1150, "Invalid document id");
if(!$doc->canBeModifiedBy($this->getUser()))
}
if (!$doc->canBeModifiedBy($this->getUser())) {
$this->fail(1153, "Access to document is denied");
if(iconv_strlen($title ?? "") > 128 || iconv_strlen($title ?? "") < 0)
}
if (iconv_strlen($title ?? "") > 128 || iconv_strlen($title ?? "") < 0) {
$this->fail(1152, "Invalid document title");
if(iconv_strlen($tags ?? "") > 256)
}
if (iconv_strlen($tags ?? "") > 256) {
$this->fail(1154, "Invalid tags");
}
if($title)
if ($title) {
$doc->setName($title);
}
$doc->setTags($tags);
if(in_array($folder_id, [0, 3]))
if (in_array($folder_id, [0, 3])) {
$doc->setFolder_id($folder_id);
if(in_array($owner_hidden, [0, 1]))
}
if (in_array($owner_hidden, [0, 1])) {
$doc->setOwner_hidden($owner_hidden);
}
try {
$doc->setEdited(time());
$doc->save();
} catch(\Throwable $e) {
} catch (\Throwable $e) {
return 0;
}
return 1;
}
function get(int $count = 30, int $offset = 0, int $type = -1, int $owner_id = NULL, int $return_tags = 0, int $order = 0): object
public function get(int $count = 30, int $offset = 0, int $type = -1, int $owner_id = null, int $return_tags = 0, int $order = 0): object
{
$this->requireUser();
if(!$owner_id)
if (!$owner_id) {
$owner_id = $this->getUser()->getId();
if($owner_id > 0 && $owner_id != $this->getUser()->getId())
$this->fail(15, "Access denied");
}
$documents = (new Documents)->getDocumentsByOwner($owner_id, $order, $type);
$res = (object)[
if ($owner_id > 0 && $owner_id != $this->getUser()->getId()) {
$this->fail(15, "Access denied");
}
$documents = (new Documents())->getDocumentsByOwner($owner_id, $order, $type);
$res = (object) [
"count" => $documents->size(),
"items" => [],
];
foreach($documents->offsetLimit($offset, $count) as $doc) {
foreach ($documents->offsetLimit($offset, $count) as $doc) {
$res->items[] = $doc->toVkApiStruct($this->getUser(), $return_tags == 1);
}
return $res;
}
function getById(string $docs, int $return_tags = 0): array
public function getById(string $docs, int $return_tags = 0): array
{
$this->requireUser();
$item_ids = explode(",", $docs);
$response = [];
if(sizeof($item_ids) < 1) {
if (sizeof($item_ids) < 1) {
$this->fail(100, "One of the parameters specified was missing or invalid: docs is undefined");
}
foreach($item_ids as $id) {
foreach ($item_ids as $id) {
$splitted_id = explode("_", $id);
$doc = (new Documents)->getDocumentById((int)$splitted_id[0], (int)$splitted_id[1], $splitted_id[2]);
if(!$doc || $doc->isDeleted())
$doc = (new Documents())->getDocumentById((int) $splitted_id[0], (int) $splitted_id[1], $splitted_id[2]);
if (!$doc || $doc->isDeleted()) {
continue;
}
$response[] = $doc->toVkApiStruct($this->getUser(), $return_tags === 1);
}
@ -128,76 +147,76 @@ final class Docs extends VKAPIRequestHandler
return $response;
}
function getTypes(?int $owner_id)
public function getTypes(?int $owner_id)
{
$this->requireUser();
if(!$owner_id)
if (!$owner_id) {
$owner_id = $this->getUser()->getId();
if($owner_id > 0 && $owner_id != $this->getUser()->getId())
}
if ($owner_id > 0 && $owner_id != $this->getUser()->getId()) {
$this->fail(15, "Access denied");
$types = (new Documents)->getTypes($owner_id);
}
$types = (new Documents())->getTypes($owner_id);
return [
"count" => sizeof($types),
"items" => $types,
];
}
function getTags(?int $owner_id, ?int $type = 0)
public function getTags(?int $owner_id, ?int $type = 0)
{
$this->requireUser();
if(!$owner_id)
if (!$owner_id) {
$owner_id = $this->getUser()->getId();
if($owner_id > 0 && $owner_id != $this->getUser()->getId())
}
if ($owner_id > 0 && $owner_id != $this->getUser()->getId()) {
$this->fail(15, "Access denied");
$tags = (new Documents)->getTags($owner_id, $type);
}
$tags = (new Documents())->getTags($owner_id, $type);
return $tags;
}
function search(string $q = "", int $search_own = -1, int $order = -1, int $count = 30, int $offset = 0, int $return_tags = 0, int $type = 0, ?string $tags = NULL): object
public function search(string $q = "", int $search_own = -1, int $order = -1, int $count = 30, int $offset = 0, int $return_tags = 0, int $type = 0, ?string $tags = null): object
{
$this->requireUser();
$params = [];
$o_order = ["type" => "id", "invert" => false];
if(iconv_strlen($q) > 512)
if (iconv_strlen($q) > 512) {
$this->fail(100, "One of the parameters specified was missing or invalid: q should be not more 512 letters length");
}
if(in_array($type, [1,2,3,4,5,6,7,8]))
if (in_array($type, [1,2,3,4,5,6,7,8])) {
$params["type"] = $type;
}
if(iconv_strlen($tags ?? "") < 512)
if (iconv_strlen($tags ?? "") < 512) {
$params["tags"] = $tags;
}
if($search_own === 1)
if ($search_own === 1) {
$params["from_me"] = $this->getUser()->getId();
}
$documents = (new Documents)->find($q, $params, $o_order);
$res = (object)[
$documents = (new Documents())->find($q, $params, $o_order);
$res = (object) [
"count" => $documents->size(),
"items" => [],
];
foreach($documents->offsetLimit($offset, $count) as $doc) {
foreach ($documents->offsetLimit($offset, $count) as $doc) {
$res->items[] = $doc->toVkApiStruct($this->getUser(), $return_tags == 1);
}
return $res;
}
function getUploadServer(?int $group_id = NULL)
{
$this->requireUser();
$this->willExecuteWriteAction();
return 0;
}
function getWallUploadServer(?int $group_id = NULL)
public function getUploadServer(?int $group_id = null)
{
$this->requireUser();
$this->willExecuteWriteAction();
@ -205,7 +224,15 @@ final class Docs extends VKAPIRequestHandler
return 0;
}
function save(string $file, string $title, string $tags, ?int $return_tags = 0)
public function getWallUploadServer(?int $group_id = null)
{
$this->requireUser();
$this->willExecuteWriteAction();
return 0;
}
public function save(string $file, string $title, string $tags, ?int $return_tags = 0)
{
$this->requireUser();
$this->willExecuteWriteAction();

View file

@ -1,188 +1,195 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Models\Repositories\Users as UsersRepo;
final class Friends extends VKAPIRequestHandler
{
function get(int $user_id = 0, string $fields = "", int $offset = 0, int $count = 100): object
{
$i = 0;
$offset++;
$friends = [];
public function get(int $user_id = 0, string $fields = "", int $offset = 0, int $count = 100): object
{
$i = 0;
$offset++;
$friends = [];
$users = new UsersRepo;
$users = new UsersRepo();
$this->requireUser();
if ($user_id == 0) {
$user_id = $this->getUser()->getId();
}
$this->requireUser();
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()))
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) {
}
foreach ($user->getFriends($offset, $count) as $friend) {
$friends[$i] = $friend->getId();
$i++;
}
$response = $friends;
$response = $friends;
$usersApi = new Users($this->getUser());
$usersApi = new Users($this->getUser());
if(!is_null($fields))
$response = $usersApi->get(implode(',', $friends), $fields, 0, $count); # FIXME
if (!is_null($fields)) {
$response = $usersApi->get(implode(',', $friends), $fields, 0, $count);
} # FIXME
return (object) [
"count" => $users->get($user_id)->getFriendsCount(),
"items" => $response
];
}
return (object) [
"count" => $users->get($user_id)->getFriendsCount(),
"items" => $response,
];
}
function getLists(): object
{
$this->requireUser();
public function getLists(): object
{
$this->requireUser();
return (object) [
"count" => 0,
"items" => (array)[]
];
}
return (object) [
"count" => 0,
"items" => (array) [],
];
}
function deleteList(): int
{
$this->requireUser();
public function deleteList(): int
{
$this->requireUser();
return 1;
}
return 1;
}
function edit(): int
{
$this->requireUser();
public function edit(): int
{
$this->requireUser();
return 1;
}
return 1;
}
function editList(): int
{
$this->requireUser();
public function editList(): int
{
$this->requireUser();
return 1;
}
return 1;
}
function add(string $user_id): int
{
$this->requireUser();
public function add(string $user_id): int
{
$this->requireUser();
$this->willExecuteWriteAction();
$users = new UsersRepo;
$user = $users->get(intval($user_id));
if(is_null($user)) {
$this->fail(177, "Cannot add this user to friends as user not found");
} else if($user->getId() == $this->getUser()->getId()) {
$this->fail(174, "Cannot add user himself as friend");
}
$users = new UsersRepo();
$user = $users->get(intval($user_id));
switch($user->getSubscriptionStatus($this->getUser())) {
case 0:
$user->toggleSubscription($this->getUser());
return 1;
if (is_null($user)) {
$this->fail(177, "Cannot add this user to friends as user not found");
} elseif ($user->getId() == $this->getUser()->getId()) {
$this->fail(174, "Cannot add user himself as friend");
}
case 1:
$user->toggleSubscription($this->getUser());
return 2;
switch ($user->getSubscriptionStatus($this->getUser())) {
case 0:
$user->toggleSubscription($this->getUser());
return 1;
case 3:
return 2;
default:
return 1;
}
}
case 1:
$user->toggleSubscription($this->getUser());
return 2;
function delete(string $user_id): int
{
$this->requireUser();
case 3:
return 2;
default:
return 1;
}
}
public function delete(string $user_id): int
{
$this->requireUser();
$this->willExecuteWriteAction();
$users = new UsersRepo;
$users = new UsersRepo();
$user = $users->get(intval($user_id));
$user = $users->get(intval($user_id));
switch($user->getSubscriptionStatus($this->getUser())) {
case 3:
$user->toggleSubscription($this->getUser());
return 1;
default:
$this->fail(15, "Access denied: No friend or friend request found.");
}
}
switch ($user->getSubscriptionStatus($this->getUser())) {
case 3:
$user->toggleSubscription($this->getUser());
return 1;
function areFriends(string $user_ids): array
{
$this->requireUser();
default:
$this->fail(15, "Access denied: No friend or friend request found.");
}
}
$users = new UsersRepo;
public function areFriends(string $user_ids): array
{
$this->requireUser();
$friends = explode(',', $user_ids);
$users = new UsersRepo();
$response = [];
$friends = explode(',', $user_ids);
for($i=0; $i < sizeof($friends); $i++) {
$friend = $users->get(intval($friends[$i]));
$response = [];
$response[] = (object)[
"friend_status" => $friend->getSubscriptionStatus($this->getUser()),
"user_id" => $friend->getId()
];
}
for ($i = 0; $i < sizeof($friends); $i++) {
$friend = $users->get(intval($friends[$i]));
return $response;
}
$response[] = (object) [
"friend_status" => $friend->getSubscriptionStatus($this->getUser()),
"user_id" => $friend->getId(),
];
}
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.");
return $response;
}
$this->requireUser();
public 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.");
}
$i = 0;
$offset++;
$followers = [];
$this->requireUser();
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++;
}
}
$i = 0;
$offset++;
$followers = [];
$response = $followers;
$usersApi = new Users($this->getUser());
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 = $usersApi->get(implode(',', $followers), $fields, 0, $count);
$response = $followers;
$usersApi = new Users($this->getUser());
foreach($response as $user)
$user->user_id = $user->id;
$response = $usersApi->get(implode(',', $followers), $fields, 0, $count);
return (object) [
"count" => $this->getUser()->getFollowersCount(),
"items" => $response
];
}
foreach ($response as $user) {
$user->user_id = $user->id;
}
return (object) [
"count" => $this->getUser()->getFollowersCount(),
"items" => $response,
];
}
}

View file

@ -1,12 +1,16 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Models\Repositories\Users as UsersRepo;
use openvk\Web\Models\Repositories\Gifts as GiftsRepo;
use openvk\Web\Models\Entities\Notifications\GiftNotification;
final class Gifts extends VKAPIRequestHandler
{
function get(int $user_id = NULL, int $count = 10, int $offset = 0)
public function get(int $user_id = null, int $count = 10, int $offset = 0)
{
$this->requireUser();
@ -14,94 +18,104 @@ final class Gifts extends VKAPIRequestHandler
$i += $offset;
$server_url = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
if($user_id)
$user = (new UsersRepo)->get($user_id);
else
if ($user_id) {
$user = (new UsersRepo())->get($user_id);
} else {
$user = $this->getUser();
}
if(!$user || $user->isDeleted())
if (!$user || $user->isDeleted()) {
$this->fail(177, "Invalid user");
}
if(!$user->canBeViewedBy($this->getUser()))
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()))
if (!$user->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access denied");
}
$gift_item = [];
$userGifts = array_slice(iterator_to_array($user->getGifts(1, $count, false)), $offset);
if(sizeof($userGifts) < 0) {
return NULL;
if (sizeof($userGifts) < 0) {
return null;
}
foreach($userGifts as $gift) {
if($i < $count) {
foreach ($userGifts as $gift) {
if ($i < $count) {
$gift_item[] = [
"id" => $i,
"from_id" => $gift->anon == true ? 0 : $gift->sender->getId(),
"message" => $gift->caption == NULL ? "" : $gift->caption,
"message" => $gift->caption == null ? "" : $gift->caption,
"date" => $gift->sent->timestamp(),
"gift" => [
"id" => $gift->gift->getId(),
"thumb_256" => $server_url. $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)
"thumb_48" => $server_url . $gift->gift->getImage(2),
],
"privacy" => 0
"privacy" => 0,
];
}
$i+=1;
$i += 1;
}
return $gift_item;
}
function send(int $user_ids, int $gift_id, string $message = "", int $privacy = 0)
public function send(int $user_ids, int $gift_id, string $message = "", int $privacy = 0)
{
$this->requireUser();
$this->willExecuteWriteAction();
$user = (new UsersRepo)->get((int) $user_ids);
$user = (new UsersRepo())->get((int) $user_ids);
if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce'])
if (!OPENVK_ROOT_CONF['openvk']['preferences']['commerce']) {
$this->fail(105, "Commerce is disabled on this instance");
if(!$user || $user->isDeleted())
}
if (!$user || $user->isDeleted()) {
$this->fail(177, "Invalid user");
}
if(!$user->canBeViewedBy($this->getUser()))
if (!$user->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access denied");
}
$gift = (new GiftsRepo)->get($gift_id);
$gift = (new GiftsRepo())->get($gift_id);
if(!$gift)
if (!$gift) {
$this->fail(165, "Invalid gift");
}
$price = $gift->getPrice();
$coinsLeft = $this->getUser()->getCoins() - $price;
if(!$gift->canUse($this->getUser()))
if (!$gift->canUse($this->getUser())) {
return (object)
[
"success" => 0,
"user_ids" => $user_ids,
"error" => "You don't have any more of these gifts."
"error" => "You don't have any more of these gifts.",
];
}
if($coinsLeft < 0)
if ($coinsLeft < 0) {
return (object)
[
"success" => 0,
"user_ids" => $user_ids,
"error" => "You don't have enough voices."
"error" => "You don't have enough voices.",
];
}
$user->gift($this->getUser(), $gift, $message);
$gift->used();
@ -116,11 +130,11 @@ final class Gifts extends VKAPIRequestHandler
[
"success" => 1,
"user_ids" => $user_ids,
"withdraw_votes" => $price
"withdraw_votes" => $price,
];
}
function delete()
public function delete()
{
$this->requireUser();
$this->willExecuteWriteAction();
@ -129,27 +143,28 @@ final class Gifts extends VKAPIRequestHandler
}
# в vk кстати называется gifts.getCatalog
function getCategories(bool $extended = false, int $page = 1)
public function getCategories(bool $extended = false, int $page = 1)
{
$cats = (new GiftsRepo)->getCategories($page);
$cats = (new GiftsRepo())->getCategories($page);
$categ = [];
$i = 0;
$server_url = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce'])
if (!OPENVK_ROOT_CONF['openvk']['preferences']['commerce']) {
$this->fail(105, "Commerce is disabled on this instance");
}
foreach($cats as $cat) {
foreach ($cats as $cat) {
$categ[$i] = [
"name" => $cat->getName(),
"description" => $cat->getDescription(),
"id" => $cat->getId(),
"thumbnail" => $server_url . $cat->getThumbnailURL(),
];
if($extended == true) {
if ($extended == true) {
$categ[$i]["localizations"] = [];
foreach(getLanguages() as $lang) {
foreach (getLanguages() as $lang) {
$code = $lang["code"];
$categ[$i]["localizations"][$code] =
[
@ -160,30 +175,32 @@ final class Gifts extends VKAPIRequestHandler
}
$i++;
}
return $categ;
}
function getGiftsInCategory(int $id, int $page = 1)
public function getGiftsInCategory(int $id, int $page = 1)
{
$this->requireUser();
if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce'])
if (!OPENVK_ROOT_CONF['openvk']['preferences']['commerce']) {
$this->fail(105, "Commerce is disabled on this instance");
}
if(!(new GiftsRepo)->getCat($id))
if (!(new GiftsRepo())->getCat($id)) {
$this->fail(177, "Category not found");
}
$giftz = ((new GiftsRepo)->getCat($id))->getGifts($page);
$giftz = ((new GiftsRepo())->getCat($id))->getGifts($page);
$gifts = [];
foreach($giftz as $gift) {
foreach ($giftz as $gift) {
$gifts[] = [
"name" => $gift->getName(),
"image" => $gift->getImage(2),
"usages_left" => (int)$gift->getUsagesLeft($this->getUser()),
"usages_left" => (int) $gift->getUsagesLeft($this->getUser()),
"price" => $gift->getPrice(),
"is_free" => $gift->isFree()
"is_free" => $gift->isFree(),
];
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
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;
@ -7,46 +11,51 @@ 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, string $filter = "groups"): object
public function get(int $user_id = 0, string $fields = "", int $offset = 0, int $count = 6, bool $online = false, string $filter = "groups"): object
{
$this->requireUser();
# InfoApp fix
if($filter == "admin" && ($user_id != 0 && $user_id != $this->getUser()->getId())) {
if ($filter == "admin" && ($user_id != 0 && $user_id != $this->getUser()->getId())) {
$this->fail(15, 'Access denied: filter admin is available only for current user');
}
$clbs = [];
if($user_id == 0) {
foreach($this->getUser()->getClubs($offset, $filter == "admin", $count, true) as $club)
$clbs[] = $club;
$clbsCount = $this->getUser()->getClubCount();
if ($user_id == 0) {
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);
$users = new UsersRepo();
$user = $users->get($user_id);
if(is_null($user) || $user->isDeleted())
$this->fail(15, "Access denied");
if (is_null($user) || $user->isDeleted()) {
$this->fail(15, "Access denied");
}
if(!$user->getPrivacyPermission('groups.read', $this->getUser()))
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();
foreach ($user->getClubs($offset, $filter == "admin", $count, true) as $club) {
$clbs[] = $club;
}
$clbsCount = $user->getClubCount();
}
$rClubs;
$ic = sizeof($clbs);
if(sizeof($clbs) > $count)
if (sizeof($clbs) > $count) {
$ic = $count;
}
if(!empty($clbs)) {
for($i=0; $i < $ic; $i++) {
if (!empty($clbs)) {
for ($i = 0; $i < $ic; $i++) {
$usr = $clbs[$i];
if(is_null($usr)) {
if (is_null($usr)) {
} else {
$rClubs[$i] = (object) [
@ -59,8 +68,8 @@ final class Groups extends VKAPIRequestHandler
$flds = explode(',', $fields);
foreach($flds as $field) {
switch($field) {
foreach ($flds as $field) {
switch ($field) {
case "verified":
$rClubs[$i]->verified = intval($usr->isVerified());
break;
@ -98,15 +107,15 @@ final class Groups extends VKAPIRequestHandler
$backgrounds = $usr->getBackDropPictureURLs();
$rClubs[$i]->background = $backgrounds;
break;
# unstandard feild
# unstandard feild
case "suggested_count":
if($usr->getWallType() != 2) {
$rClubs[$i]->suggested_count = NULL;
if ($usr->getWallType() != 2) {
$rClubs[$i]->suggested_count = null;
break;
}
$rClubs[$i]->suggested_count = $usr->getSuggestedPostsCount($this->getUser());
break;
}
}
@ -117,58 +126,63 @@ final class Groups extends VKAPIRequestHandler
}
return (object) [
"count" => $clbsCount,
"items" => $rClubs
"count" => $clbsCount,
"items" => $rClubs,
];
}
function getById(string $group_ids = "", string $group_id = "", string $fields = "", int $offset = 0, int $count = 500): ?array
public function getById(string $group_ids = "", string $group_id = "", string $fields = "", int $offset = 0, int $count = 500): ?array
{
/* Both offset and count SHOULD be used only in OpenVK code,
/* Both offset and count SHOULD be used only in OpenVK code,
not in your app or script, since it's not oficially documented by VK */
$clubs = new ClubsRepo;
if(empty($group_ids) && !empty($group_id))
$clubs = new ClubsRepo();
if (empty($group_ids) && !empty($group_id)) {
$group_ids = $group_id;
if(empty($group_ids) && empty($group_id))
}
if (empty($group_ids) && empty($group_id)) {
$this->fail(100, "One of the parameters specified was missing or invalid: group_ids is undefined");
}
$clbs = explode(',', $group_ids);
$response = array();
$response = [];
$ic = sizeof($clbs);
if(sizeof($clbs) > $count)
$ic = $count;
if (sizeof($clbs) > $count) {
$ic = $count;
}
$clbs = array_slice($clbs, $offset * $count);
for($i=0; $i < $ic; $i++) {
if($i > 500 || $clbs[$i] == 0)
for ($i = 0; $i < $ic; $i++) {
if ($i > 500 || $clbs[$i] == 0) {
break;
}
if($clbs[$i] < 0)
if ($clbs[$i] < 0) {
$this->fail(100, "ты ошибся чутка, у айди группы убери минус");
}
$clb = $clubs->get((int) $clbs[$i]);
if(is_null($clb)) {
$response[$i] = (object)[
if (is_null($clb)) {
$response[$i] = (object) [
"id" => intval($clbs[$i]),
"name" => "DELETED",
"screen_name" => "club".intval($clbs[$i]),
"screen_name" => "club" . intval($clbs[$i]),
"type" => "group",
"description" => "This group was deleted or it doesn't exist"
];
} else if($clbs[$i] == NULL) {
"description" => "This group was deleted or it doesn't exist",
];
} elseif ($clbs[$i] == null) {
} else {
$response[$i] = (object)[
$response[$i] = (object) [
"id" => $clb->getId(),
"name" => $clb->getName(),
"screen_name" => $clb->getShortCode() ?? "club".$clb->getId(),
"screen_name" => $clb->getShortCode() ?? "club" . $clb->getId(),
"is_closed" => false,
"type" => "group",
"is_member" => !is_null($this->getUser()) ? (int) $clb->getSubscriptionStatus($this->getUser()) : 0,
@ -177,20 +191,20 @@ final class Groups extends VKAPIRequestHandler
$flds = explode(',', $fields);
foreach($flds as $field) {
switch($field) {
case "verified":
$response[$i]->verified = intval($clb->isVerified());
break;
case "has_photo":
$response[$i]->has_photo = is_null($clb->getAvatarPhoto()) ? 0 : 1;
break;
case "photo_max_orig":
$response[$i]->photo_max_orig = $clb->getAvatarURL();
break;
case "photo_max":
$response[$i]->photo_max = $clb->getAvatarURL();
break;
foreach ($flds as $field) {
switch ($field) {
case "verified":
$response[$i]->verified = intval($clb->isVerified());
break;
case "has_photo":
$response[$i]->has_photo = is_null($clb->getAvatarPhoto()) ? 0 : 1;
break;
case "photo_max_orig":
$response[$i]->photo_max_orig = $clb->getAvatarURL();
break;
case "photo_max":
$response[$i]->photo_max = $clb->getAvatarURL();
break;
case "photo_50":
$response[$i]->photo_50 = $clb->getAvatarURL();
break;
@ -206,14 +220,14 @@ final class Groups extends VKAPIRequestHandler
case "photo_400_orig":
$response[$i]->photo_400_orig = $clb->getAvatarURL("normal");
break;
case "members_count":
$response[$i]->members_count = $clb->getFollowersCount();
break;
case "site":
$response[$i]->site = $clb->getWebsite();
break;
case "members_count":
$response[$i]->members_count = $clb->getFollowersCount();
break;
case "site":
$response[$i]->site = $clb->getWebsite();
break;
case "description":
$response[$i]->description = $clb->getDescription();
$response[$i]->description = $clb->getDescription();
break;
case "can_suggest":
$response[$i]->can_suggest = !$clb->canBeModifiedBy($this->getUser()) && $clb->getWallType() == 2;
@ -222,10 +236,10 @@ final class Groups extends VKAPIRequestHandler
$backgrounds = $clb->getBackDropPictureURLs();
$response[$i]->background = $backgrounds;
break;
# unstandard feild
# unstandard feild
case "suggested_count":
if($clb->getWallType() != 2) {
$response[$i]->suggested_count = NULL;
if ($clb->getWallType() != 2) {
$response[$i]->suggested_count = null;
break;
}
@ -235,20 +249,23 @@ final class Groups extends VKAPIRequestHandler
$contacts;
$contactTmp = $clb->getManagers(1, true);
foreach($contactTmp as $contact)
$contacts[] = array(
foreach ($contactTmp as $contact) {
$contacts[] = [
"user_id" => $contact->getUser()->getId(),
"desc" => $contact->getComment()
);
"desc" => $contact->getComment(),
];
}
$response[$i]->contacts = $contacts;
break;
$response[$i]->contacts = $contacts;
break;
case "can_post":
if(!is_null($this->getUser()))
if($clb->canBeModifiedBy($this->getUser()))
if (!is_null($this->getUser())) {
if ($clb->canBeModifiedBy($this->getUser())) {
$response[$i]->can_post = true;
else
} else {
$response[$i]->can_post = $clb->canPost();
}
}
break;
}
}
@ -258,21 +275,22 @@ final class Groups extends VKAPIRequestHandler
return $response;
}
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")
public 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) {
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);
$clubs = new ClubsRepo();
foreach ($find->offsetLimit($offset, $count) as $group)
$array = [];
$find = $clubs->find($q);
foreach ($find->offsetLimit($offset, $count) as $group) {
$array[] = $group->getId();
if(!$array || sizeof($array) < 1) {
}
if (!$array || sizeof($array) < 1) {
return (object) [
"count" => 0,
"items" => [],
@ -280,47 +298,49 @@ final class Groups extends VKAPIRequestHandler
}
return (object) [
"count" => $find->size(),
"items" => $this->getById(implode(',', $array), "", $fields)
"count" => $find->size(),
"items" => $this->getById(implode(',', $array), "", $fields),
];
}
function join(int $group_id)
public function join(int $group_id)
{
$this->requireUser();
$this->willExecuteWriteAction();
$club = (new ClubsRepo)->get($group_id);
$club = (new ClubsRepo())->get($group_id);
$isMember = !is_null($this->getUser()) ? (int) $club->getSubscriptionStatus($this->getUser()) : 0;
if($isMember == 0)
if ($isMember == 0) {
$club->toggleSubscription($this->getUser());
}
return 1;
}
function leave(int $group_id)
public function leave(int $group_id)
{
$this->requireUser();
$this->willExecuteWriteAction();
$club = (new ClubsRepo)->get($group_id);
$club = (new ClubsRepo())->get($group_id);
$isMember = !is_null($this->getUser()) ? (int) $club->getSubscriptionStatus($this->getUser()) : 0;
if($isMember == 1)
if ($isMember == 1) {
$club->toggleSubscription($this->getUser());
}
return 1;
}
function create(string $title, string $description = "", string $type = "group", int $public_category = 1, int $public_subcategory = 1, int $subtype = 1)
public function create(string $title, string $description = "", string $type = "group", int $public_category = 1, int $public_subcategory = 1, int $subtype = 1)
{
$this->requireUser();
$this->willExecuteWriteAction();
$club = new Club;
$club = new Club();
$club->setName($title);
$club->setAbout($description);
@ -329,73 +349,80 @@ final class Groups extends VKAPIRequestHandler
$club->toggleSubscription($this->getUser());
return $this->getById((string)$club->getId());
return $this->getById((string) $club->getId());
}
function edit(
int $group_id,
string $title = NULL,
string $description = NULL,
string $screen_name = NULL,
string $website = NULL,
int $wall = -1,
int $topics = NULL,
int $adminlist = NULL,
int $topicsAboveWall = NULL,
int $hideFromGlobalFeed = NULL,
int $audio = NULL)
{
public function edit(
int $group_id,
string $title = null,
string $description = null,
string $screen_name = null,
string $website = null,
int $wall = -1,
int $topics = null,
int $adminlist = null,
int $topicsAboveWall = null,
int $hideFromGlobalFeed = null,
int $audio = null
) {
$this->requireUser();
$this->willExecuteWriteAction();
$club = (new ClubsRepo)->get($group_id);
$club = (new ClubsRepo())->get($group_id);
if(!$club) $this->fail(203, "Club not found");
if(!$club || !$club->canBeModifiedBy($this->getUser())) $this->fail(15, "You can't modify this group.");
if(!empty($screen_name) && !$club->setShortcode($screen_name)) $this->fail(103, "Invalid shortcode.");
if (!$club) {
$this->fail(203, "Club not found");
}
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.");
}
!empty($title) ? $club->setName($title) : null;
!empty($description) ? $club->setAbout($description) : null;
!empty($screen_name) ? $club->setShortcode($screen_name) : null;
!empty($website) ? $club->setWebsite((!parse_url($website, PHP_URL_SCHEME) ? "https://" : "") . $website) : null;
!empty($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) {
$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;
!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;
if (!$club->isHidingFromGlobalFeedEnforced()) {
!empty($hideFromGlobalFeed) ? $club->setHide_From_Global_Feed($hideFromGlobalFeed) : NULL;
!empty($hideFromGlobalFeed) ? $club->setHide_From_Global_Feed($hideFromGlobalFeed) : null;
}
in_array($audio, [0, 1]) ? $club->setEveryone_can_upload_audios($audio) : NULL;
in_array($audio, [0, 1]) ? $club->setEveryone_can_upload_audios($audio) : null;
try {
$club->save();
} catch(\TypeError $e) {
} catch (\TypeError $e) {
$this->fail(15, "Nothing changed");
} catch(\Exception $e) {
} catch (\Exception $e) {
$this->fail(18, "An unknown error occurred: maybe you set an incorrect value?");
}
return 1;
}
function getMembers(string $group_id, string $sort = "id_asc", int $offset = 0, int $count = 100, string $fields = "", string $filter = "any")
public function getMembers(string $group_id, string $sort = "id_asc", int $offset = 0, int $count = 100, string $fields = "", string $filter = "any")
{
# bdate,can_post,can_see_all_posts,can_see_audio,can_write_private_message,city,common_count,connections,contacts,country,domain,education,has_mobile,last_seen,lists,online,online_mobile,photo_100,photo_200,photo_200_orig,photo_400_orig,photo_50,photo_max,photo_max_orig,relation,relatives,schools,sex,site,status,universities
$club = (new ClubsRepo)->get((int) $group_id);
if(!$club)
$club = (new ClubsRepo())->get((int) $group_id);
if (!$club) {
$this->fail(125, "Invalid group id");
}
$sorter = "follower ASC";
switch($sort) {
switch ($sort) {
default:
case "time_asc":
case "id_asc":
@ -409,14 +436,14 @@ final class Groups extends VKAPIRequestHandler
$members = array_slice(iterator_to_array($club->getFollowers(1, $count, $sorter)), $offset);
$arr = (object) [
"count" => count($members),
"items" => array()];
"count" => count($members),
"items" => []];
$filds = explode(",", $fields);
$i = 0;
foreach($members as $member) {
if($i > $count) {
foreach ($members as $member) {
if ($i > $count) {
break;
}
@ -426,16 +453,16 @@ final class Groups extends VKAPIRequestHandler
"last_name" => $member->getLastName(),
];
foreach($filds as $fild) {
foreach ($filds as $fild) {
$canView = $member->canBeViewedBy($this->getUser());
switch($fild) {
switch ($fild) {
case "bdate":
if(!$canView) {
if (!$canView) {
$arr->items[$i]->bdate = "01.01.1970";
break;
}
$arr->items[$i]->bdate = $member->getBirthday() ? $member->getBirthday()->format('%e.%m.%Y') : NULL;
$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);
@ -456,7 +483,7 @@ final class Groups extends VKAPIRequestHandler
$arr->items[$i]->connections = 1;
break;
case "contacts":
if(!$canView) {
if (!$canView) {
$arr->items[$i]->contacts = "secret@gmail.com";
break;
}
@ -476,7 +503,7 @@ final class Groups extends VKAPIRequestHandler
$arr->items[$i]->has_mobile = false;
break;
case "last_seen":
if(!$canView) {
if (!$canView) {
$arr->items[$i]->last_seen = 0;
break;
}
@ -487,7 +514,7 @@ final class Groups extends VKAPIRequestHandler
$arr->items[$i]->lists = "";
break;
case "online":
if(!$canView) {
if (!$canView) {
$arr->items[$i]->online = false;
break;
}
@ -495,7 +522,7 @@ final class Groups extends VKAPIRequestHandler
$arr->items[$i]->online = $member->isOnline();
break;
case "online_mobile":
if(!$canView) {
if (!$canView) {
$arr->items[$i]->online_mobile = false;
break;
}
@ -530,7 +557,7 @@ final class Groups extends VKAPIRequestHandler
$arr->items[$i]->schools = 0;
break;
case "sex":
if(!$canView) {
if (!$canView) {
$arr->items[$i]->sex = -1;
break;
}
@ -538,15 +565,15 @@ final class Groups extends VKAPIRequestHandler
$arr->items[$i]->sex = $member->isFemale() ? 1 : 2;
break;
case "site":
if(!$canView) {
$arr->items[$i]->site = NULL;
if (!$canView) {
$arr->items[$i]->site = null;
break;
}
$arr->items[$i]->site = $member->getWebsite();
break;
case "status":
if(!$canView) {
if (!$canView) {
$arr->items[$i]->status = "r";
break;
}
@ -563,17 +590,18 @@ final class Groups extends VKAPIRequestHandler
return $arr;
}
function getSettings(string $group_id)
public function getSettings(string $group_id)
{
$this->requireUser();
$club = (new ClubsRepo)->get((int)$group_id);
$club = (new ClubsRepo())->get((int) $group_id);
if(!$club || !$club->canBeModifiedBy($this->getUser()))
if (!$club || !$club->canBeModifiedBy($this->getUser())) {
$this->fail(15, "You can't get settings of this group.");
}
$arr = (object) [
"title" => $club->getName(),
"description" => $club->getDescription() != NULL ? $club->getDescription() : "",
"description" => $club->getDescription() != null ? $club->getDescription() : "",
"address" => $club->getShortcode(),
"wall" => $club->getWallType(), # отличается от вкшных но да ладно
"photos" => 1,
@ -589,13 +617,13 @@ final class Groups extends VKAPIRequestHandler
"access" => 1,
"subject" => 1,
"subject_list" => [
0 => "в",
1 => "опенвк",
2 => "нет",
3 => "категорий",
4 => "групп",
0 => "в",
1 => "опенвк",
2 => "нет",
3 => "категорий",
4 => "групп",
],
"rss" => "/club".$club->getId()."/rss",
"rss" => "/club" . $club->getId() . "/rss",
"website" => $club->getWebsite(),
"age_limits" => 0,
"market" => [],
@ -604,24 +632,27 @@ final class Groups extends VKAPIRequestHandler
return $arr;
}
function isMember(string $group_id, int $user_id, string $user_ids = "", bool $extended = false)
public function isMember(string $group_id, int $user_id, string $user_ids = "", bool $extended = false)
{
$this->requireUser();
$id = $user_id != NULL ? $user_id : explode(",", $user_ids);
$id = $user_id != null ? $user_id : explode(",", $user_ids);
if($group_id < 0)
if ($group_id < 0) {
$this->fail(228, "Remove the minus from group_id");
}
$club = (new ClubsRepo)->get((int)$group_id);
$usver = (new UsersRepo)->get((int)$id);
$club = (new ClubsRepo())->get((int) $group_id);
$usver = (new UsersRepo())->get((int) $id);
if(!$club || $group_id == 0)
if (!$club || $group_id == 0) {
$this->fail(203, "Invalid club");
}
if(!$usver || $usver->isDeleted() || $user_id == 0)
if (!$usver || $usver->isDeleted() || $user_id == 0) {
$this->fail(30, "Invalid user");
}
if($extended == false) {
if ($extended == false) {
return $club->getSubscriptionStatus($usver) ? 1 : 0;
} else {
return (object)
@ -630,12 +661,12 @@ final class Groups extends VKAPIRequestHandler
"request" => 0,
"invitation" => 0,
"can_invite" => 0,
"can_recall" => 0
"can_recall" => 0,
];
}
}
function remove(int $group_id, int $user_id)
public function remove(int $group_id, int $user_id)
{
$this->requireUser();

View file

@ -1,206 +1,217 @@
<?php declare(strict_types=1);
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();
$this->willExecuteWriteAction();
$postable = NULL;
switch($type) {
case "post":
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
$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");
}
$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);
$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();
$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":
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
$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(NULL, 'photo_50');
}
return $res;
}
}
<?php
declare(strict_types=1);
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
{
public 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);
$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");
}
$postable->setLike(true, $this->getUser());
return (object) [
"likes" => $postable->getLikesCount(),
];
}
public 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);
$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(),
];
}
}
public function isLiked(int $user_id, string $type, int $owner_id, int $item_id): object
{
$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":
$post = (new PostsRepo())->getPostById($owner_id, $item_id);
$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,
];
}
public 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(null, 'photo_50');
}
}
return $res;
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Events\NewMessageEvent;
use openvk\Web\Models\Entities\{Correspondence, Message};
use openvk\Web\Models\Repositories\{Messages as MSGRepo, Users as USRRepo};
@ -11,37 +15,39 @@ final class Messages extends VKAPIRequestHandler
{
private function resolvePeer(int $user_id = -1, int $peer_id = -1): ?int
{
if($user_id === -1) {
if($peer_id === -1)
return NULL;
else if($peer_id < 0)
return NULL;
else if(($peer_id - 2000000000) > 0)
return NULL;
if ($user_id === -1) {
if ($peer_id === -1) {
return null;
} elseif ($peer_id < 0) {
return null;
} elseif (($peer_id - 2000000000) > 0) {
return null;
}
return $peer_id;
}
return $user_id;
}
function getById(string $message_ids, int $preview_length = 0, int $extended = 0): object
public function getById(string $message_ids, int $preview_length = 0, int $extended = 0): object
{
$this->requireUser();
$msgs = new MSGRepo;
$msgs = new MSGRepo();
$ids = preg_split("%, ?%", $message_ids);
$items = [];
foreach($ids as $id) {
foreach ($ids as $id) {
$message = $msgs->get((int) $id);
if(!$message)
if (!$message) {
continue;
else if($message->getSender()->getId() !== $this->getUser()->getId() && $message->getRecipient()->getId() !== $this->getUser()->getId())
} elseif ($message->getSender()->getId() !== $this->getUser()->getId() && $message->getRecipient()->getId() !== $this->getUser()->getId()) {
continue;
}
$author = $message->getSender()->getId() === $this->getUser()->getId() ? $message->getRecipient()->getId() : $message->getSender()->getId();
$rMsg = new APIMsg;
$rMsg = new APIMsg();
$rMsg->id = $message->getId();
$rMsg->user_id = $author;
$rMsg->from_id = $message->getSender()->getId();
@ -51,167 +57,186 @@ final class Messages extends VKAPIRequestHandler
$rMsg->body = $message->getText(false);
$rMsg->text = $message->getText(false);
$rMsg->emoji = true;
if($preview_length > 0)
if ($preview_length > 0) {
$rMsg->body = ovk_proc_strtr($rMsg->body, $preview_length);
$rMsg->text = ovk_proc_strtr($rMsg->text, $preview_length);
}
$rMsg->text = ovk_proc_strtr($rMsg->text, $preview_length);
$items[] = $rMsg;
}
return (object) [
"count" => sizeof($items),
"items" => $items,
];
}
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
{
public 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();
if($forGodSakePleaseDoNotReportAboutMyOnlineActivity == 0)
{
if ($forGodSakePleaseDoNotReportAboutMyOnlineActivity == 0) {
$this->getUser()->updOnline($this->getPlatform());
}
if($chat_id !== -1)
if ($chat_id !== -1) {
$this->fail(946, "Chats are not implemented");
else if($sticker_id !== -1)
} elseif ($sticker_id !== -1) {
$this->fail(-151, "Stickers are not implemented");
if(empty($message) && empty($attachment))
}
if (empty($message) && empty($attachment)) {
$this->fail(100, "Message text is empty or invalid");
}
# lol recursion
if(!empty($user_ids)) {
if (!empty($user_ids)) {
$rIds = [];
$ids = preg_split("%, ?%", $user_ids);
if(sizeof($ids) > 100)
if (sizeof($ids) > 100) {
$this->fail(913, "Too many recipients");
foreach($ids as $id)
$rIds[] = $this->send(-1, $id, "", -1, "", $message);
return $rIds;
}
if(!empty($domain)) {
$peer = (new USRRepo)->getByShortCode($domain);
} else {
$peer = $this->resolvePeer($user_id, $peer_id);
$peer = (new USRRepo)->get($peer);
}
if(!$peer)
$this->fail(936, "There is no peer with this id");
if($this->getUser()->getId() !== $peer->getId() && !$peer->getPrivacyPermission('messages.write', $this->getUser()))
$this->fail(945, "This chat is disabled because of privacy settings");
# Finally we get to send a message!
$chat = new Correspondence($this->getUser(), $peer);
$msg = new Message;
$msg->setContent($message);
$msg = $chat->sendMessage($msg, true);
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();
foreach ($ids as $id) {
$rIds[] = $this->send(-1, $id, "", -1, "", $message);
}
return $rIds;
}
if (!empty($domain)) {
$peer = (new USRRepo())->getByShortCode($domain);
} else {
$peer = $this->resolvePeer($user_id, $peer_id);
$peer = (new USRRepo())->get($peer);
}
if (!$peer) {
$this->fail(936, "There is no peer with this id");
}
if ($this->getUser()->getId() !== $peer->getId() && !$peer->getPrivacyPermission('messages.write', $this->getUser())) {
$this->fail(945, "This chat is disabled because of privacy settings");
}
# Finally we get to send a message!
$chat = new Correspondence($this->getUser(), $peer);
$msg = new Message();
$msg->setContent($message);
$msg = $chat->sendMessage($msg, true);
if (!$msg) {
$this->fail(950, "Internal error");
} elseif (!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();
}
function delete(string $message_ids, int $spam = 0, int $delete_for_all = 0): object
public function delete(string $message_ids, int $spam = 0, int $delete_for_all = 0): object
{
$this->requireUser();
$this->willExecuteWriteAction();
$msgs = new MSGRepo;
$msgs = new MSGRepo();
$ids = preg_split("%, ?%", $message_ids);
$items = [];
foreach($ids as $id) {
foreach ($ids as $id) {
$message = $msgs->get((int) $id);
if(!$message || $message->getSender()->getId() !== $this->getUser()->getId() && $message->getRecipient()->getId() !== $this->getUser()->getId())
if (!$message || $message->getSender()->getId() !== $this->getUser()->getId() && $message->getRecipient()->getId() !== $this->getUser()->getId()) {
$items[$id] = 0;
}
$message->delete();
$items[$id] = 1;
}
return (object) $items;
}
function restore(int $message_id): int
public function restore(int $message_id): int
{
$this->requireUser();
$this->willExecuteWriteAction();
$msg = (new MSGRepo)->get($message_id);
if(!$msg)
$msg = (new MSGRepo())->get($message_id);
if (!$msg) {
return 0;
else if($msg->getSender()->getId() !== $this->getUser()->getId())
} elseif ($msg->getSender()->getId() !== $this->getUser()->getId()) {
return 0;
}
$msg->undelete();
return 1;
}
function getConversations(int $offset = 0, int $count = 20, string $filter = "all", int $extended = 0, string $fields = ""): object
public function getConversations(int $offset = 0, int $count = 20, string $filter = "all", int $extended = 0, string $fields = ""): object
{
$this->requireUser();
$convos = (new MSGRepo)->getCorrespondencies($this->getUser(), -1, $count, $offset);
$convosCount = (new MSGRepo)->getCorrespondenciesCount($this->getUser());
$convos = (new MSGRepo())->getCorrespondencies($this->getUser(), -1, $count, $offset);
$convosCount = (new MSGRepo())->getCorrespondenciesCount($this->getUser());
$list = [];
$users = [];
foreach($convos as $convo) {
foreach ($convos as $convo) {
$correspondents = $convo->getCorrespondents();
if($correspondents[0]->getId() === $this->getUser()->getId())
if ($correspondents[0]->getId() === $this->getUser()->getId()) {
$peer = $correspondents[1];
else
} else {
$peer = $correspondents[0];
}
$lastMessage = $convo->getPreviewMessage();
$listConvo = new APIConvo;
$listConvo = new APIConvo();
$listConvo->peer = [
"id" => $peer->getId(),
"type" => "user",
"local_id" => $peer->getId(),
];
$canWrite = $peer->getSubscriptionStatus($this->getUser()) === 3;
$listConvo->can_write = [
"allowed" => $canWrite,
];
$lastMessagePreview = NULL;
if(!is_null($lastMessage)) {
$lastMessagePreview = null;
if (!is_null($lastMessage)) {
$listConvo->last_message_id = $lastMessage->getId();
if($lastMessage->getSender()->getId() === $this->getUser()->getId())
if ($lastMessage->getSender()->getId() === $this->getUser()->getId()) {
$author = $lastMessage->getRecipient()->getId();
else
} else {
$author = $lastMessage->getSender()->getId();
$lastMessagePreview = new APIMsg;
}
$lastMessagePreview = new APIMsg();
$lastMessagePreview->id = $lastMessage->getId();
$lastMessagePreview->user_id = $author;
$lastMessagePreview->from_id = $lastMessage->getSender()->getId();
@ -221,19 +246,19 @@ final class Messages extends VKAPIRequestHandler
$lastMessagePreview->body = $lastMessage->getText(false);
$lastMessagePreview->text = $lastMessage->getText(false);
$lastMessagePreview->emoji = true;
if($extended == 1) {
if ($extended == 1) {
$users[] = $author;
}
}
$list[] = [
"conversation" => $listConvo,
"last_message" => $lastMessagePreview,
];
}
if($extended == 0){
if ($extended == 0) {
return (object) [
"count" => $convosCount,
"items" => $list,
@ -245,12 +270,12 @@ final class Messages extends VKAPIRequestHandler
return (object) [
"count" => $convosCount,
"items" => $list,
"profiles" => (!empty($users) ? (new APIUsers)->get(implode(',', $users), $fields, 0, $count+1) : [])
"profiles" => (!empty($users) ? (new APIUsers())->get(implode(',', $users), $fields, 0, $count + 1) : []),
];
}
}
function getConversationsById(string $peer_ids, int $extended = 0, string $fields = "")
public function getConversationsById(string $peer_ids, int $extended = 0, string $fields = "")
{
$this->requireUser();
@ -258,21 +283,23 @@ final class Messages extends VKAPIRequestHandler
$output = [
"count" => 0,
"items" => []
"items" => [],
];
$userslist = [];
foreach($peers as $peer) {
if(key($peers) > 100)
foreach ($peers as $peer) {
if (key($peers) > 100) {
continue;
}
if(is_null($user_id = $this->resolvePeer((int) $peer)))
if (is_null($user_id = $this->resolvePeer((int) $peer))) {
$this->fail(-151, "Chats are not implemented");
}
$user = (new USRRepo)->get((int) $peer);
$user = (new USRRepo())->get((int) $peer);
if($user) {
if ($user) {
$dialogue = new Correspondence($this->getUser(), $user);
$iterator = $dialogue->getMessages(Correspondence::CAP_BEHAVIOUR_START_MESSAGE_ID, 0, 1, 0, false);
$msg = $iterator[0]->unwrap(); // шоб удобнее было
@ -280,7 +307,7 @@ final class Messages extends VKAPIRequestHandler
"peer" => [
"id" => $user->getId(),
"type" => "user",
"local_id" => $user->getId()
"local_id" => $user->getId(),
],
"last_message_id" => $msg->id,
"in_read" => $msg->id,
@ -295,41 +322,43 @@ final class Messages extends VKAPIRequestHandler
"is_marked_unread" => $iterator[0]->isUnread(),
"important" => false, // целестора когда релиз
"can_write" => [
"allowed" => ($user->getId() === $this->getUser()->getId() || $user->getPrivacyPermission('messages.write', $this->getUser()) === true)
]
"allowed" => ($user->getId() === $this->getUser()->getId() || $user->getPrivacyPermission('messages.write', $this->getUser()) === true),
],
];
$userslist[] = $user->getId();
}
}
if($extended == 1) {
if ($extended == 1) {
$userslist = array_unique($userslist);
$output['profiles'] = (!empty($userslist) ? (new APIUsers)->get(implode(',', $userslist), $fields) : []);
$output['profiles'] = (!empty($userslist) ? (new APIUsers())->get(implode(',', $userslist), $fields) : []);
}
$output['count'] = sizeof($output['items']);
return (object) $output;
}
function getHistory(int $offset = 0, int $count = 20, int $user_id = -1, int $peer_id = -1, int $start_message_id = 0, int $rev = 0, int $extended = 0, string $fields = ""): object
public function getHistory(int $offset = 0, int $count = 20, int $user_id = -1, int $peer_id = -1, int $start_message_id = 0, int $rev = 0, int $extended = 0, string $fields = ""): object
{
$this->requireUser();
if(is_null($user_id = $this->resolvePeer($user_id, $peer_id)))
if (is_null($user_id = $this->resolvePeer($user_id, $peer_id))) {
$this->fail(-151, "Chats are not implemented");
$peer = (new USRRepo)->get($user_id);
if(!$peer)
}
$peer = (new USRRepo())->get($user_id);
if (!$peer) {
$this->fail(1, "ошибка про то что пира нет");
}
$results = [];
$dialogue = new Correspondence($this->getUser(), $peer);
$iterator = $dialogue->getMessages(Correspondence::CAP_BEHAVIOUR_START_MESSAGE_ID, $start_message_id, $count, abs($offset), $rev === 1);
foreach($iterator as $message) {
foreach ($iterator as $message) {
$msgU = $message->unwrap(); # Why? As of OpenVK 2 Public Preview Two database layer doesn't work correctly and refuses to cache entities.
# UPDATE: the issue seems to be caused by debug mode and json_encode (bruh_encode). ~~Dorothy
$rMsg = new APIMsg;
# UPDATE: the issue seems to be caused by debug mode and json_encode (bruh_encode). ~~Dorothy
$rMsg = new APIMsg();
$rMsg->id = $msgU->id;
$rMsg->user_id = $msgU->sender_id === $this->getUser()->getId() ? $msgU->recipient_id : $msgU->sender_id;
$rMsg->from_id = $msgU->sender_id;
@ -339,10 +368,10 @@ final class Messages extends VKAPIRequestHandler
$rMsg->body = $message->getText(false);
$rMsg->text = $message->getText(false);
$rMsg->emoji = true;
$results[] = $rMsg;
}
$output = [
"count" => sizeof($results),
"items" => $results,
@ -356,100 +385,111 @@ final class Messages extends VKAPIRequestHandler
return (object) $output;
}
function getLongPollHistory(int $ts = -1, int $preview_length = 0, int $events_limit = 1000, int $msgs_limit = 1000): object
public function getLongPollHistory(int $ts = -1, int $preview_length = 0, int $events_limit = 1000, int $msgs_limit = 1000): object
{
$this->requireUser();
$res = [
"history" => [],
"messages" => [],
"profiles" => [],
"new_pts" => 0,
];
$manager = SignalManager::i();
$events = $manager->getHistoryFor($this->getUser()->getId(), $ts === -1 ? NULL : $ts, min($events_limit, $msgs_limit));
foreach($events as $event) {
if(!($event instanceof NewMessageEvent))
$events = $manager->getHistoryFor($this->getUser()->getId(), $ts === -1 ? null : $ts, min($events_limit, $msgs_limit));
foreach ($events as $event) {
if (!($event instanceof NewMessageEvent)) {
continue;
}
$message = $this->getById((string) $event->getLongPoolSummary()->message["uuid"], $preview_length, 1)->items[0];
if(!$message)
if (!$message) {
continue;
}
$res["messages"][] = $message;
$res["history"][] = $event->getVKAPISummary($this->getUser()->getId());
}
$res["messages"] = [
"count" => sizeof($res["messages"]),
"items" => $res["messages"],
];
return (object) $res;
}
function getLongPollServer(int $need_pts = 1, int $lp_version = 3, ?int $group_id = NULL): array
public function getLongPollServer(int $need_pts = 1, int $lp_version = 3, ?int $group_id = null): array
{
$this->requireUser();
if($group_id > 0)
if ($group_id > 0) {
$this->fail(-151, "Not implemented");
}
$url = "http" . (ovk_is_ssl() ? "s" : "") . "://$_SERVER[HTTP_HOST]/nim" . $this->getUser()->getId();
$key = openssl_random_pseudo_bytes(8);
$key = bin2hex($key) . bin2hex($key ^ ( ~CHANDLER_ROOT_CONF["security"]["secret"] | ((string) $this->getUser()->getId()) ));
$key = bin2hex($key) . bin2hex($key ^ (~CHANDLER_ROOT_CONF["security"]["secret"] | ((string) $this->getUser()->getId())));
$res = [
"key" => $key,
"server" => $url,
"ts" => time(),
];
if($need_pts === 1)
if ($need_pts === 1) {
$res["pts"] = -1;
}
return $res;
}
function edit(int $message_id, string $message = "", string $attachment = "", int $peer_id = 0)
public function edit(int $message_id, string $message = "", string $attachment = "", int $peer_id = 0)
{
$this->requireUser();
$this->willExecuteWriteAction();
$msg = (new MSGRepo)->get($message_id);
$msg = (new MSGRepo())->get($message_id);
if(empty($message) && empty($attachment))
if (empty($message) && empty($attachment)) {
$this->fail(100, "Required parameter 'message' missing.");
}
if(!$msg || $msg->isDeleted())
if (!$msg || $msg->isDeleted()) {
$this->fail(102, "Invalid message");
}
if($msg->getSender()->getId() != $this->getUser()->getId())
if ($msg->getSender()->getId() != $this->getUser()->getId()) {
$this->fail(15, "Access to message denied");
if(!empty($message))
}
if (!empty($message)) {
$msg->setContent($message);
}
$msg->setEdited(time());
$msg->save(true);
if(!empty($attachment)) {
if (!empty($attachment)) {
$attachs = parseAttachments($attachment);
$newAttachmentsCount = sizeof($attachs);
$postsAttachments = iterator_to_array($msg->getChildren());
if(sizeof($postsAttachments) >= 10)
if (sizeof($postsAttachments) >= 10) {
$this->fail(15, "Message have too many attachments");
}
if(($newAttachmentsCount + sizeof($postsAttachments)) > 10)
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())
foreach ($attachs as $attach) {
if ($attach && !$attach->isDeleted() && $attach->getOwner()->getId() == $this->getUser()->getId()) {
$msg->attach($attach);
else
} else {
$this->fail(52, "One of the attachments is invalid");
}
}
}

View file

@ -1,254 +1,273 @@
<?php declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use Chandler\Database\DatabaseConnection;
use openvk\Web\Models\Repositories\Posts as PostsRepo;
use openvk\Web\Models\Entities\User;
use openvk\VKAPI\Handlers\Wall;
final class Newsfeed extends VKAPIRequestHandler
{
function get(string $fields = "", int $start_from = 0, int $start_time = 0, int $end_time = 0, int $offset = 0, int $count = 30, int $extended = 0, int $forGodSakePleaseDoNotReportAboutMyOnlineActivity = 0)
{
$this->requireUser();
if($forGodSakePleaseDoNotReportAboutMyOnlineActivity == 0)
{
$this->getUser()->updOnline($this->getPlatform());
}
$id = $this->getUser()->getId();
$subs = DatabaseConnection::i()
->getContext()
->table("subscriptions")
->where("follower", $id);
$ids = array_map(function($rel) {
return $rel->target * ($rel->model === "openvk\Web\Models\Entities\User" ? 1 : -1);
}, iterator_to_array($subs));
$ids[] = $this->getUser()->getId();
$posts = DatabaseConnection::i()
->getContext()
->table("posts")
->select("id")
->where("wall IN (?)", $ids)
->where("deleted", 0)
->where("suggested", 0)
->where("id < (?)", empty($start_from) ? PHP_INT_MAX : $start_from)
->where("? <= created", empty($start_time) ? 0 : $start_time)
->where("? >= created", empty($end_time) ? PHP_INT_MAX : $end_time)
->order("created DESC");
$rposts = [];
foreach($posts->page((int) ($offset + 1), $count) as $post)
$rposts[] = (new PostsRepo)->get($post->id)->getPrettyId();
$response = (new Wall)->getById(implode(',', $rposts), $extended, $fields, $this->getUser());
$response->next_from = end(end($posts->page((int) ($offset + 1), $count))); // ну и костыли пиздец конечно)
return $response;
}
function getGlobal(string $fields = "", int $start_from = 0, int $start_time = 0, int $end_time = 0, int $offset = 0, int $count = 30, int $extended = 0, int $rss = 0)
{
$this->requireUser();
$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";
if($return_banned == 0) {
$ignored_sources_ids = $this->getUser()->getIgnoredSources(0, OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50, true);
if(sizeof($ignored_sources_ids) > 0) {
$imploded_ids = implode("', '", $ignored_sources_ids);
$queryBase .= " AND `posts`.`wall` NOT IN ('$imploded_ids')";
}
}
$start_from = empty($start_from) ? PHP_INT_MAX : $start_from;
$start_time = empty($start_time) ? 0 : $start_time;
$end_time = empty($end_time) ? PHP_INT_MAX : $end_time;
$posts = DatabaseConnection::i()->getConnection()->query("SELECT `posts`.`id` " . $queryBase . " AND `posts`.`id` <= " . $start_from . " AND " . $start_time . " <= `posts`.`created` AND `posts`.`created` <= " . $end_time . " ORDER BY `created` DESC LIMIT " . $count . " OFFSET " . $offset);
$rposts = [];
$ids = [];
if($rss == 1) {
$channel = new \Bhaktaraz\RSSGenerator\Channel();
$channel->title("Global Feed — " . OPENVK_ROOT_CONF['openvk']['appearance']['name'])
->description('OVK Global feed')
->url(ovk_scheme(true) . $_SERVER["HTTP_HOST"] . "/feed/all");
foreach($posts as $item) {
$post = (new PostsRepo)->get($item->id);
if(!$post || $post->isDeleted()) {
continue;
}
$output = $post->toRss();
$output->appendTo($channel);
}
return $channel;
}
foreach($posts as $post) {
$rposts[] = (new PostsRepo)->get($post->id)->getPrettyId();
$ids[] = $post->id;
}
$response = (new Wall)->getById(implode(',', $rposts), $extended, $fields, $this->getUser());
$response->next_from = end($ids);
return $response;
}
function getByType(string $feed_type = 'top', string $fields = "", int $start_from = 0, int $start_time = 0, int $end_time = 0, int $offset = 0, int $count = 30, int $extended = 0, int $return_banned = 0)
{
$this->requireUser();
switch($feed_type) {
case 'top':
return $this->getGlobal($fields, $start_from, $start_time, $end_time, $offset, $count, $extended, $return_banned);
break;
default:
return $this->get($fields, $start_from, $start_time, $end_time, $offset, $count, $extended);
break;
}
}
function getBanned(int $extended = 0, string $fields = "", string $name_case = "nom", int $merge = 0): object
{
$this->requireUser();
$offset = 0;
$count = OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50;
$banned = $this->getUser()->getIgnoredSources($offset, $count, ($extended != 1));
$return_object = (object) [
'groups' => [],
'members' => [],
];
if($extended == 0) {
foreach($banned as $ban) {
if($ban > 0)
$return_object->members[] = $ban;
else
$return_object->groups[] = $ban;
}
} else {
if($merge == 1) {
$return_object = (object) [
'count' => sizeof($banned),
'items' => [],
];
foreach($banned as $ban) {
$return_object->items[] = $ban->toVkApiStruct($this->getUser(), $fields);
}
} else {
$return_object = (object) [
'groups' => [],
'profiles' => [],
];
foreach($banned as $ban) {
if($ban->getRealId() > 0)
$return_object->profiles[] = $ban->toVkApiStruct($this->getUser(), $fields);
else
$return_object->groups[] = $ban->toVkApiStruct($this->getUser(), $fields);
}
}
}
return $return_object;
}
function addBan(string $user_ids = "", string $group_ids = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
# Formatting input ids
if(!empty($user_ids)) {
$user_ids = array_map(function($el) {
return (int)$el;
}, explode(',', $user_ids));
$user_ids = array_unique($user_ids);
} else
$user_ids = [];
if(!empty($group_ids)) {
$group_ids = array_map(function($el) {
return abs((int)$el) * -1;
}, explode(',', $group_ids));
$group_ids = array_unique($group_ids);
} else
$group_ids = [];
$ids = array_merge($user_ids, $group_ids);
if(sizeof($ids) < 1)
return 0;
if(sizeof($ids) > 10)
$this->fail(-10, "Limit of 'ids' is 10");
$config_limit = OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50;
$user_ignores = $this->getUser()->getIgnoredSourcesCount();
if(($user_ignores + sizeof($ids)) > $config_limit) {
$this->fail(-50, "Ignoring limit exceeded");
}
$entities = get_entities($ids);
$successes = 0;
foreach($entities as $entity) {
if(!$entity || $entity->getRealId() === $this->getUser()->getRealId() || $entity->isHideFromGlobalFeedEnabled() || $entity->isIgnoredBy($this->getUser())) continue;
$entity->addIgnore($this->getUser());
$successes += 1;
}
return 1;
}
function deleteBan(string $user_ids = "", string $group_ids = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
if(!empty($user_ids)) {
$user_ids = array_map(function($el) {
return (int)$el;
}, explode(',', $user_ids));
$user_ids = array_unique($user_ids);
} else
$user_ids = [];
if(!empty($group_ids)) {
$group_ids = array_map(function($el) {
return abs((int)$el) * -1;
}, explode(',', $group_ids));
$group_ids = array_unique($group_ids);
} else
$group_ids = [];
$ids = array_merge($user_ids, $group_ids);
if(sizeof($ids) < 1)
return 0;
if(sizeof($ids) > 10)
$this->fail(-10, "Limit of ids is 10");
$entities = get_entities($ids);
$successes = 0;
foreach($entities as $entity) {
if(!$entity || $entity->getRealId() === $this->getUser()->getRealId() || !$entity->isIgnoredBy($this->getUser())) continue;
$entity->removeIgnore($this->getUser());
$successes += 1;
}
return 1;
}
}
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use Chandler\Database\DatabaseConnection;
use openvk\Web\Models\Repositories\Posts as PostsRepo;
use openvk\Web\Models\Entities\User;
use openvk\VKAPI\Handlers\Wall;
final class Newsfeed extends VKAPIRequestHandler
{
public function get(string $fields = "", int $start_from = 0, int $start_time = 0, int $end_time = 0, int $offset = 0, int $count = 30, int $extended = 0, int $forGodSakePleaseDoNotReportAboutMyOnlineActivity = 0)
{
$this->requireUser();
if ($forGodSakePleaseDoNotReportAboutMyOnlineActivity == 0) {
$this->getUser()->updOnline($this->getPlatform());
}
$id = $this->getUser()->getId();
$subs = DatabaseConnection::i()
->getContext()
->table("subscriptions")
->where("follower", $id);
$ids = array_map(function ($rel) {
return $rel->target * ($rel->model === "openvk\Web\Models\Entities\User" ? 1 : -1);
}, iterator_to_array($subs));
$ids[] = $this->getUser()->getId();
$posts = DatabaseConnection::i()
->getContext()
->table("posts")
->select("id")
->where("wall IN (?)", $ids)
->where("deleted", 0)
->where("suggested", 0)
->where("id < (?)", empty($start_from) ? PHP_INT_MAX : $start_from)
->where("? <= created", empty($start_time) ? 0 : $start_time)
->where("? >= created", empty($end_time) ? PHP_INT_MAX : $end_time)
->order("created DESC");
$rposts = [];
foreach ($posts->page((int) ($offset + 1), $count) as $post) {
$rposts[] = (new PostsRepo())->get($post->id)->getPrettyId();
}
$response = (new Wall())->getById(implode(',', $rposts), $extended, $fields, $this->getUser());
$response->next_from = end(end($posts->page((int) ($offset + 1), $count))); // ну и костыли пиздец конечно)
return $response;
}
public function getGlobal(string $fields = "", int $start_from = 0, int $start_time = 0, int $end_time = 0, int $offset = 0, int $count = 30, int $extended = 0, int $rss = 0)
{
$this->requireUser();
$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";
}
if ($return_banned == 0) {
$ignored_sources_ids = $this->getUser()->getIgnoredSources(0, OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50, true);
if (sizeof($ignored_sources_ids) > 0) {
$imploded_ids = implode("', '", $ignored_sources_ids);
$queryBase .= " AND `posts`.`wall` NOT IN ('$imploded_ids')";
}
}
$start_from = empty($start_from) ? PHP_INT_MAX : $start_from;
$start_time = empty($start_time) ? 0 : $start_time;
$end_time = empty($end_time) ? PHP_INT_MAX : $end_time;
$posts = DatabaseConnection::i()->getConnection()->query("SELECT `posts`.`id` " . $queryBase . " AND `posts`.`id` <= " . $start_from . " AND " . $start_time . " <= `posts`.`created` AND `posts`.`created` <= " . $end_time . " ORDER BY `created` DESC LIMIT " . $count . " OFFSET " . $offset);
$rposts = [];
$ids = [];
if ($rss == 1) {
$channel = new \Bhaktaraz\RSSGenerator\Channel();
$channel->title("Global Feed — " . OPENVK_ROOT_CONF['openvk']['appearance']['name'])
->description('OVK Global feed')
->url(ovk_scheme(true) . $_SERVER["HTTP_HOST"] . "/feed/all");
foreach ($posts as $item) {
$post = (new PostsRepo())->get($item->id);
if (!$post || $post->isDeleted()) {
continue;
}
$output = $post->toRss();
$output->appendTo($channel);
}
return $channel;
}
foreach ($posts as $post) {
$rposts[] = (new PostsRepo())->get($post->id)->getPrettyId();
$ids[] = $post->id;
}
$response = (new Wall())->getById(implode(',', $rposts), $extended, $fields, $this->getUser());
$response->next_from = end($ids);
return $response;
}
public function getByType(string $feed_type = 'top', string $fields = "", int $start_from = 0, int $start_time = 0, int $end_time = 0, int $offset = 0, int $count = 30, int $extended = 0, int $return_banned = 0)
{
$this->requireUser();
switch ($feed_type) {
case 'top':
return $this->getGlobal($fields, $start_from, $start_time, $end_time, $offset, $count, $extended, $return_banned);
break;
default:
return $this->get($fields, $start_from, $start_time, $end_time, $offset, $count, $extended);
break;
}
}
public function getBanned(int $extended = 0, string $fields = "", string $name_case = "nom", int $merge = 0): object
{
$this->requireUser();
$offset = 0;
$count = OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50;
$banned = $this->getUser()->getIgnoredSources($offset, $count, ($extended != 1));
$return_object = (object) [
'groups' => [],
'members' => [],
];
if ($extended == 0) {
foreach ($banned as $ban) {
if ($ban > 0) {
$return_object->members[] = $ban;
} else {
$return_object->groups[] = $ban;
}
}
} else {
if ($merge == 1) {
$return_object = (object) [
'count' => sizeof($banned),
'items' => [],
];
foreach ($banned as $ban) {
$return_object->items[] = $ban->toVkApiStruct($this->getUser(), $fields);
}
} else {
$return_object = (object) [
'groups' => [],
'profiles' => [],
];
foreach ($banned as $ban) {
if ($ban->getRealId() > 0) {
$return_object->profiles[] = $ban->toVkApiStruct($this->getUser(), $fields);
} else {
$return_object->groups[] = $ban->toVkApiStruct($this->getUser(), $fields);
}
}
}
}
return $return_object;
}
public function addBan(string $user_ids = "", string $group_ids = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
# Formatting input ids
if (!empty($user_ids)) {
$user_ids = array_map(function ($el) {
return (int) $el;
}, explode(',', $user_ids));
$user_ids = array_unique($user_ids);
} else {
$user_ids = [];
}
if (!empty($group_ids)) {
$group_ids = array_map(function ($el) {
return abs((int) $el) * -1;
}, explode(',', $group_ids));
$group_ids = array_unique($group_ids);
} else {
$group_ids = [];
}
$ids = array_merge($user_ids, $group_ids);
if (sizeof($ids) < 1) {
return 0;
}
if (sizeof($ids) > 10) {
$this->fail(-10, "Limit of 'ids' is 10");
}
$config_limit = OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50;
$user_ignores = $this->getUser()->getIgnoredSourcesCount();
if (($user_ignores + sizeof($ids)) > $config_limit) {
$this->fail(-50, "Ignoring limit exceeded");
}
$entities = get_entities($ids);
$successes = 0;
foreach ($entities as $entity) {
if (!$entity || $entity->getRealId() === $this->getUser()->getRealId() || $entity->isHideFromGlobalFeedEnabled() || $entity->isIgnoredBy($this->getUser())) {
continue;
}
$entity->addIgnore($this->getUser());
$successes += 1;
}
return 1;
}
public function deleteBan(string $user_ids = "", string $group_ids = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
if (!empty($user_ids)) {
$user_ids = array_map(function ($el) {
return (int) $el;
}, explode(',', $user_ids));
$user_ids = array_unique($user_ids);
} else {
$user_ids = [];
}
if (!empty($group_ids)) {
$group_ids = array_map(function ($el) {
return abs((int) $el) * -1;
}, explode(',', $group_ids));
$group_ids = array_unique($group_ids);
} else {
$group_ids = [];
}
$ids = array_merge($user_ids, $group_ids);
if (sizeof($ids) < 1) {
return 0;
}
if (sizeof($ids) > 10) {
$this->fail(-10, "Limit of ids is 10");
}
$entities = get_entities($ids);
$successes = 0;
foreach ($entities as $entity) {
if (!$entity || $entity->getRealId() === $this->getUser()->getRealId() || !$entity->isIgnoredBy($this->getUser())) {
continue;
}
$entity->removeIgnore($this->getUser());
$successes += 1;
}
return 1;
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Models\Repositories\Notes as NotesRepo;
use openvk\Web\Models\Repositories\Users as UsersRepo;
use openvk\Web\Models\Repositories\Comments as CommentsRepo;
@ -9,12 +13,12 @@ use openvk\Web\Models\Entities\{Note, Comment};
final class Notes extends VKAPIRequestHandler
{
function add(string $title, string $text, int $privacy = 0, int $comment_privacy = 0, string $privacy_view = "", string $privacy_comment = "")
public function add(string $title, string $text, int $privacy = 0, int $comment_privacy = 0, string $privacy_view = "", string $privacy_comment = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
$note = new Note;
$note = new Note();
$note->setOwner($this->getUser()->getId());
$note->setCreated(time());
$note->setName($title);
@ -25,31 +29,37 @@ final class Notes extends VKAPIRequestHandler
return $note->getVirtualId();
}
function createComment(string $note_id, int $owner_id, string $message, int $reply_to = 0, string $attachments = "")
public function createComment(string $note_id, int $owner_id, string $message, int $reply_to = 0, string $attachments = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
$note = (new NotesRepo)->getNoteById((int)$owner_id, (int)$note_id);
$note = (new NotesRepo())->getNoteById((int) $owner_id, (int) $note_id);
if(!$note)
if (!$note) {
$this->fail(180, "Note not found");
if($note->isDeleted())
}
if ($note->isDeleted()) {
$this->fail(189, "Note is deleted");
if($note->getOwner()->isDeleted())
}
if ($note->getOwner()->isDeleted()) {
$this->fail(403, "Owner is deleted");
}
if(!$note->canBeViewedBy($this->getUser()))
if (!$note->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access denied");
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
}
if (!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser())) {
$this->fail(43, "No access");
}
if(empty($message) && empty($attachments))
if (empty($message) && empty($attachments)) {
$this->fail(100, "Required parameter 'message' missing.");
}
$comment = new Comment;
$comment = new Comment();
$comment->setOwner($this->getUser()->getId());
$comment->setModel(get_class($note));
$comment->setTarget($note->getId());
@ -57,43 +67,49 @@ final class Notes extends VKAPIRequestHandler
$comment->setCreated(time());
$comment->save();
if(!empty($attachments)) {
if (!empty($attachments)) {
$attachmentsArr = explode(",", $attachments);
if(sizeof($attachmentsArr) > 10)
if (sizeof($attachmentsArr) > 10) {
$this->fail(50, "Error: too many attachments");
foreach($attachmentsArr as $attac) {
$attachmentType = NULL;
}
if(str_contains($attac, "photo"))
foreach ($attachmentsArr as $attac) {
$attachmentType = null;
if (str_contains($attac, "photo")) {
$attachmentType = "photo";
elseif(str_contains($attac, "video"))
} elseif (str_contains($attac, "video")) {
$attachmentType = "video";
else
} else {
$this->fail(205, "Unknown attachment type");
}
$attachment = str_replace($attachmentType, "", $attac);
$attachmentOwner = (int)explode("_", $attachment)[0];
$attachmentId = (int)end(explode("_", $attachment));
$attachmentOwner = (int) explode("_", $attachment)[0];
$attachmentId = (int) end(explode("_", $attachment));
$attacc = NULL;
$attacc = null;
if($attachmentType == "photo") {
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
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())
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this photo");
}
$comment->attach($attacc);
} elseif($attachmentType == "video") {
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
} 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())
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this video");
}
$comment->attach($attacc);
}
@ -103,87 +119,96 @@ final class Notes extends VKAPIRequestHandler
return $comment->getId();
}
function delete(string $note_id)
public function delete(string $note_id)
{
$this->requireUser();
$this->willExecuteWriteAction();
$note = (new NotesRepo)->get((int)$note_id);
$note = (new NotesRepo())->get((int) $note_id);
if(!$note)
if (!$note) {
$this->fail(180, "Note not found");
if(!$note->canBeModifiedBy($this->getUser()))
}
if (!$note->canBeModifiedBy($this->getUser())) {
$this->fail(15, "Access to note denied");
}
$note->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 = "")
public function edit(string $note_id, string $title = "", string $text = "", int $privacy = 0, int $comment_privacy = 0, string $privacy_view = "", string $privacy_comment = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
$note = (new NotesRepo)->getNoteById($this->getUser()->getId(), (int)$note_id);
$note = (new NotesRepo())->getNoteById($this->getUser()->getId(), (int) $note_id);
if(!$note)
if (!$note) {
$this->fail(180, "Note not found");
if($note->isDeleted())
}
if ($note->isDeleted()) {
$this->fail(189, "Note is deleted");
}
if(!$note->canBeModifiedBy($this->getUser()))
if (!$note->canBeModifiedBy($this->getUser())) {
$this->fail(403, "No access");
}
!empty($title) ? $note->setName($title) : NULL;
!empty($text) ? $note->setSource($text) : NULL;
!empty($title) ? $note->setName($title) : null;
!empty($text) ? $note->setSource($text) : null;
$note->setCached_Content(NULL);
$note->setCached_Content(null);
$note->setEdited(time());
$note->save();
return 1;
}
function get(int $user_id, string $note_ids = "", int $offset = 0, int $count = 10, int $sort = 0)
public function get(int $user_id, string $note_ids = "", int $offset = 0, int $count = 10, int $sort = 0)
{
$this->requireUser();
$user = (new UsersRepo)->get($user_id);
$user = (new UsersRepo())->get($user_id);
if(!$user || $user->isDeleted())
if (!$user || $user->isDeleted()) {
$this->fail(15, "Invalid user");
if(!$user->getPrivacyPermission('notes.read', $this->getUser()))
$this->fail(15, "Access denied: this user chose to hide his notes");
}
if(!$user->canBeViewedBy($this->getUser()))
if (!$user->getPrivacyPermission('notes.read', $this->getUser())) {
$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);
}
if (empty($note_ids)) {
$notes = array_slice(iterator_to_array((new NotesRepo())->getUserNotes($user, 1, $count + $offset, $sort == 0 ? "ASC" : "DESC")), $offset);
$nodez = (object) [
"count" => (new NotesRepo)->getUserNotesCount((new UsersRepo)->get($user_id)),
"notes" => []
"count" => (new NotesRepo())->getUserNotesCount((new UsersRepo())->get($user_id)),
"notes" => [],
];
foreach($notes as $note) {
if($note->isDeleted()) continue;
foreach ($notes as $note) {
if ($note->isDeleted()) {
continue;
}
$nodez->notes[] = $note->toVkApiStruct();
}
} else {
$notes = explode(',', $note_ids);
foreach($notes as $note)
{
foreach ($notes as $note) {
$id = explode("_", $note);
$items = [];
$note = (new NotesRepo)->getNoteById((int)$id[0], (int)$id[1]);
if($note && !$note->isDeleted()) {
$note = (new NotesRepo())->getNoteById((int) $id[0], (int) $id[1]);
if ($note && !$note->isDeleted()) {
$nodez->notes[] = $note->toVkApiStruct();
}
}
@ -192,69 +217,79 @@ final class Notes extends VKAPIRequestHandler
return $nodez;
}
function getById(int $note_id, int $owner_id, bool $need_wiki = false)
public function getById(int $note_id, int $owner_id, bool $need_wiki = false)
{
$this->requireUser();
$note = (new NotesRepo)->getNoteById($owner_id, $note_id);
$note = (new NotesRepo())->getNoteById($owner_id, $note_id);
if(!$note)
if (!$note) {
$this->fail(180, "Note not found");
if($note->isDeleted())
}
if ($note->isDeleted()) {
$this->fail(189, "Note is deleted");
}
if(!$note->getOwner() || $note->getOwner()->isDeleted())
if (!$note->getOwner() || $note->getOwner()->isDeleted()) {
$this->fail(177, "Owner does not exists");
}
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
if (!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser())) {
$this->fail(40, "Access denied: this user chose to hide his notes");
}
if(!$note->canBeViewedBy($this->getUser()))
if (!$note->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access to note denied");
}
return $note->toVkApiStruct();
}
function getComments(int $note_id, int $owner_id, int $sort = 1, int $offset = 0, int $count = 100)
public function getComments(int $note_id, int $owner_id, int $sort = 1, int $offset = 0, int $count = 100)
{
$this->requireUser();
$note = (new NotesRepo)->getNoteById($owner_id, $note_id);
$note = (new NotesRepo())->getNoteById($owner_id, $note_id);
if(!$note)
if (!$note) {
$this->fail(180, "Note not found");
if($note->isDeleted())
}
if ($note->isDeleted()) {
$this->fail(189, "Note is deleted");
if(!$note->getOwner())
}
if (!$note->getOwner()) {
$this->fail(177, "Owner does not exists");
}
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
if (!$note->getOwner()->getPrivacyPermission('notes.read', $this->getUser())) {
$this->fail(14, "No access");
}
if(!$note->canBeViewedBy($this->getUser()))
if (!$note->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access to note denied");
}
$arr = (object) [
"count" => $note->getCommentsCount(),
"count" => $note->getCommentsCount(),
"comments" => []];
$comments = array_slice(iterator_to_array($note->getComments(1, $count + $offset)), $offset);
foreach($comments as $comment) {
foreach ($comments as $comment) {
$arr->comments[] = $comment->toVkApiStruct($this->getUser(), false, false, $note);
}
return $arr;
}
function getFriendsNotes(int $offset = 0, int $count = 0)
public function getFriendsNotes(int $offset = 0, int $count = 0)
{
$this->fail(501, "Not implemented");
}
function restoreComment(int $comment_id = 0, int $owner_id = 0)
public function restoreComment(int $comment_id = 0, int $owner_id = 0)
{
$this->fail(501, "Not implemented");
}

View file

@ -1,62 +1,70 @@
<?php declare(strict_types=1);
<?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)
{
public 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)[
$res = (object) [
"items" => [],
"profiles" => [],
"groups" => [],
"last_viewed" => $this->getUser()->getNotificationOffset()
"last_viewed" => $this->getUser()->getNotificationOffset(),
];
if($count > 100)
if ($count > 100) {
$this->fail(125, "Count is too big");
}
if(!eventdb())
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);
$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) {
foreach ($notifs as $notif) {
$sxModel = $notif->getModel(1);
if(!method_exists($sxModel, "getAvatarUrl"))
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)[
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()
"screen_name" => $sxModel->getShortCode(),
];
$res->profiles[] = $result;
} else {
$sxModel = (new Clubs)->get(abs($id));
$sxModel = (new Clubs())->get(abs($id));
$result = $sxModel->toVkApiStruct($this->getUser());
$res->groups[] = $result;
@ -66,7 +74,7 @@ final class Notifications extends VKAPIRequestHandler
return $res;
}
function markAsViewed()
public function markAsViewed()
{
$this->requireUser();
$this->willExecuteWriteAction();
@ -74,7 +82,7 @@ final class Notifications extends VKAPIRequestHandler
try {
$this->getUser()->updateNotificationOffset();
$this->getUser()->save();
} catch(\Throwable $e) {
} catch (\Throwable $e) {
return 0;
}

View file

@ -1,15 +1,19 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Models\Repositories\{Users as UsersRepo, Clubs as ClubsRepo, Posts as PostsRepo};
final class Ovk extends VKAPIRequestHandler
{
function version(): string
public function version(): string
{
return OPENVK_VERSION;
}
function test(): object
public function test(): object
{
return (object) [
"authorized" => $this->userAuthorized(),
@ -17,58 +21,59 @@ final class Ovk extends VKAPIRequestHandler
"version" => VKAPI_DECL_VER,
];
}
function chickenWings(): string
public function chickenWings(): string
{
return "крылышки";
}
function aboutInstance(string $fields = "statistics,administrators,popular_groups,links", string $admin_fields = "", string $group_fields = ""): object
public function aboutInstance(string $fields = "statistics,administrators,popular_groups,links", string $admin_fields = "", string $group_fields = ""): object
{
$fields = explode(',', $fields);
$response = (object) [];
if(in_array("statistics", $fields)) {
$usersStats = (new UsersRepo)->getStatistics();
$clubsCount = (new ClubsRepo)->getCount();
$postsCount = (new PostsRepo)->getCount();
if (in_array("statistics", $fields)) {
$usersStats = (new UsersRepo())->getStatistics();
$clubsCount = (new ClubsRepo())->getCount();
$postsCount = (new PostsRepo())->getCount();
$response->statistics = (object) [
"users_count" => $usersStats->all,
"online_users_count" => $usersStats->online,
"active_users_count" => $usersStats->active,
"groups_count" => $clubsCount,
"wall_posts_count" => $postsCount
"wall_posts_count" => $postsCount,
];
}
if(in_array("administrators", $fields)) {
$admins = iterator_to_array((new UsersRepo)->getInstanceAdmins());
$adminsResponse = (new Users($this->getUser()))->get(implode(',', array_map(function($admin) {
if (in_array("administrators", $fields)) {
$admins = iterator_to_array((new UsersRepo())->getInstanceAdmins());
$adminsResponse = (new Users($this->getUser()))->get(implode(',', array_map(function ($admin) {
return $admin->getId();
}, $admins)), $admin_fields, 0, sizeof($admins));
$response->administrators = (object) [
"count" => sizeof($admins),
"items" => $adminsResponse
"items" => $adminsResponse,
];
}
if(in_array("popular_groups", $fields)) {
$popularClubs = iterator_to_array((new ClubsRepo)->getPopularClubs());
$clubsResponse = (new Groups($this->getUser()))->getById(implode(',', array_map(function($entry) {
if (in_array("popular_groups", $fields)) {
$popularClubs = iterator_to_array((new ClubsRepo())->getPopularClubs());
$clubsResponse = (new Groups($this->getUser()))->getById(implode(',', array_map(function ($entry) {
return $entry->club->getId();
}, $popularClubs)), "", "members_count, " . $group_fields);
$response->popular_groups = (object) [
"count" => sizeof($popularClubs),
"items" => $clubsResponse
"items" => $clubsResponse,
];
}
if(in_array("links", $fields))
if (in_array("links", $fields)) {
$response->links = (object) [
"count" => sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['about']['links']),
"items" => is_null(OPENVK_ROOT_CONF['openvk']['preferences']['about']['links']) ? [] : OPENVK_ROOT_CONF['openvk']['preferences']['about']['links']
"items" => is_null(OPENVK_ROOT_CONF['openvk']['preferences']['about']['links']) ? [] : OPENVK_ROOT_CONF['openvk']['preferences']['about']['links'],
];
}
return $response;
}

View file

@ -1,42 +1,49 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Models\Repositories\Applications;
final class Pay extends VKAPIRequestHandler
{
function getIdByMarketingId(string $marketing_id): int
public function getIdByMarketingId(string $marketing_id): int
{
[$hexId, $signature] = explode("_", $marketing_id);
try {
$key = CHANDLER_ROOT_CONF["security"]["secret"];
if(sodium_memcmp(base64_decode($signature), hash_hmac("sha512/224", $hexId, $key, true)) == -1)
if (sodium_memcmp(base64_decode($signature), hash_hmac("sha512/224", $hexId, $key, true)) == -1) {
$this->fail(4, "Invalid marketing id");
}
} catch (\SodiumException $e) {
$this->fail(4, "Invalid marketing id");
}
return hexdec($hexId);
}
function verifyOrder(int $app_id, float $amount, string $signature): bool
public function verifyOrder(int $app_id, float $amount, string $signature): bool
{
$this->requireUser();
$app = (new Applications())->get($app_id);
if(!$app)
if (!$app) {
$this->fail(26, "No app found with this id");
else if($app->getOwner()->getId() != $this->getUser()->getId())
} elseif ($app->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(15, "Access error");
}
[$time, $signature] = explode(",", $signature);
try {
$key = CHANDLER_ROOT_CONF["security"]["secret"];
if(sodium_memcmp($signature, hash_hmac("whirlpool", "$app_id:$amount:$time", $key)) == -1)
if (sodium_memcmp($signature, hash_hmac("whirlpool", "$app_id:$amount:$time", $key)) == -1) {
$this->fail(4, "Invalid order");
}
} catch (\SodiumException $e) {
$this->fail(4, "Invalid order");
}
return true;
}
}
}

View file

@ -1,4 +1,7 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use Nette\InvalidStateException;
@ -33,31 +36,34 @@ final class Photos extends VKAPIRequestHandler
return ovk_scheme(true) . $_SERVER["HTTP_HOST"] . "/upload/photo/$uploadHash?$uploadInfo";
}
private function getImagePath(string $photo, string $hash, ?string& $up = NULL, ?string& $group = NULL): string
private function getImagePath(string $photo, string $hash, ?string& $up = null, ?string& $group = null): string
{
$secret = CHANDLER_ROOT_CONF["security"]["secret"];
if(!hash_equals(hash_hmac("sha3-224", $photo, $secret), $hash))
if (!hash_equals(hash_hmac("sha3-224", $photo, $secret), $hash)) {
$this->fail(121, "Incorrect hash");
}
[$up, $image, $group] = explode("|", $photo);
$imagePath = __DIR__ . "/../../tmp/api-storage/photos/$up" . "_$image.oct";
if(!file_exists($imagePath))
if (!file_exists($imagePath)) {
$this->fail(10, "Invalid image");
}
return $imagePath;
}
function getOwnerPhotoUploadServer(int $owner_id = 0): object
public function getOwnerPhotoUploadServer(int $owner_id = 0): object
{
$this->requireUser();
if($owner_id < 0) {
$club = (new Clubs)->get(abs($owner_id));
if(!$club)
$this->fail(0404, "Club not found");
else if(!$club->canBeModifiedBy($this->getUser()))
if ($owner_id < 0) {
$club = (new Clubs())->get(abs($owner_id));
if (!$club) {
$this->fail(0o404, "Club not found");
} elseif (!$club->canBeModifiedBy($this->getUser())) {
$this->fail(200, "Access: Club can't be 'written' by user");
}
}
return (object) [
@ -65,19 +71,19 @@ final class Photos extends VKAPIRequestHandler
];
}
function saveOwnerPhoto(string $photo, string $hash): object
public function saveOwnerPhoto(string $photo, string $hash): object
{
$imagePath = $this->getImagePath($photo, $hash, $uploader, $group);
if($group == 0) {
$user = (new \openvk\Web\Models\Repositories\Users)->get((int) $uploader);
$album = (new Albums)->getUserAvatarAlbum($user);
if ($group == 0) {
$user = (new \openvk\Web\Models\Repositories\Users())->get((int) $uploader);
$album = (new Albums())->getUserAvatarAlbum($user);
} else {
$club = (new Clubs)->get((int) $group);
$album = (new Albums)->getClubAvatarAlbum($club);
$club = (new Clubs())->get((int) $group);
$album = (new Albums())->getClubAvatarAlbum($club);
}
try {
$avatar = new Photo;
$avatar = new Photo();
$avatar->setOwner((int) $uploader);
$avatar->setDescription("Profile photo");
$avatar->setCreated(time());
@ -88,30 +94,31 @@ final class Photos extends VKAPIRequestHandler
$avatar->save();
$album->addPhoto($avatar);
unlink($imagePath);
} catch(ImageException | InvalidStateException $e) {
} catch (ImageException | InvalidStateException $e) {
unlink($imagePath);
$this->fail(129, "Invalid image file");
}
return (object) [
"photo_hash" => NULL,
"photo_hash" => null,
"photo_src" => $avatar->getURL(),
];
}
function getWallUploadServer(?int $group_id = NULL): object
public function getWallUploadServer(?int $group_id = null): object
{
$this->requireUser();
$album = NULL;
if(!is_null($group_id)) {
$club = (new Clubs)->get(abs($group_id));
if(!$club)
$this->fail(0404, "Club not found");
else if(!$club->canBeModifiedBy($this->getUser()))
$album = null;
if (!is_null($group_id)) {
$club = (new Clubs())->get(abs($group_id));
if (!$club) {
$this->fail(0o404, "Club not found");
} elseif (!$club->canBeModifiedBy($this->getUser())) {
$this->fail(200, "Access: Club can't be 'written' by user");
}
} else {
$album = (new Albums)->getUserWallAlbum($this->getUser());
$album = (new Albums())->getUserWallAlbum($this->getUser());
}
return (object) [
@ -121,20 +128,21 @@ final class Photos extends VKAPIRequestHandler
];
}
function saveWallPhoto(string $photo, string $hash, int $group_id = 0, ?string $caption = NULL): array
public function saveWallPhoto(string $photo, string $hash, int $group_id = 0, ?string $caption = null): array
{
$imagePath = $this->getImagePath($photo, $hash, $uploader, $group);
if($group_id != $group)
if ($group_id != $group) {
$this->fail(8, "group_id doesn't match");
}
$album = NULL;
if($group_id != 0) {
$uploader = (new \openvk\Web\Models\Repositories\Users)->get((int) $uploader);
$album = (new Albums)->getUserWallAlbum($uploader);
$album = null;
if ($group_id != 0) {
$uploader = (new \openvk\Web\Models\Repositories\Users())->get((int) $uploader);
$album = (new Albums())->getUserWallAlbum($uploader);
}
try {
$photo = new Photo;
$photo = new Photo();
$photo->setOwner((int) $uploader);
$photo->setCreated(time());
$photo->setFile([
@ -142,25 +150,27 @@ final class Photos extends VKAPIRequestHandler
"error" => 0,
]);
if (!is_null($caption))
if (!is_null($caption)) {
$photo->setDescription($caption);
}
$photo->save();
unlink($imagePath);
} catch(ImageException | InvalidStateException $e) {
} catch (ImageException | InvalidStateException $e) {
unlink($imagePath);
$this->fail(129, "Invalid image file");
}
if(!is_null($album))
if (!is_null($album)) {
$album->addPhoto($photo);
}
return [
$photo->toVkApiStruct(),
];
}
function getUploadServer(?int $album_id = NULL): object
public function getUploadServer(?int $album_id = null): object
{
$this->requireUser();
@ -172,34 +182,37 @@ final class Photos extends VKAPIRequestHandler
];
}
function save(string $photos_list, string $hash, int $album_id = 0, ?string $caption = NULL): object
public function save(string $photos_list, string $hash, int $album_id = 0, ?string $caption = null): object
{
$this->requireUser();
$secret = CHANDLER_ROOT_CONF["security"]["secret"];
if(!hash_equals(hash_hmac("sha3-224", $photos_list, $secret), $hash))
if (!hash_equals(hash_hmac("sha3-224", $photos_list, $secret), $hash)) {
$this->fail(121, "Incorrect hash");
}
$album = NULL;
if($album_id != 0) {
$album_ = (new Albums)->get($album_id);
if(!$album_)
$this->fail(0404, "Invalid album");
else if(!$album_->canBeModifiedBy($this->getUser()))
$album = null;
if ($album_id != 0) {
$album_ = (new Albums())->get($album_id);
if (!$album_) {
$this->fail(0o404, "Invalid album");
} elseif (!$album_->canBeModifiedBy($this->getUser())) {
$this->fail(15, "Access: Album can't be 'written' by user");
}
$album = $album_;
}
$pList = json_decode($photos_list);
$imagePaths = [];
foreach($pList as $pDesc)
foreach ($pList as $pDesc) {
$imagePaths[] = __DIR__ . "/../../tmp/api-storage/photos/$pDesc->keyholder" . "_$pDesc->resource.oct";
}
$images = [];
try {
foreach($imagePaths as $imagePath) {
$photo = new Photo;
foreach ($imagePaths as $imagePath) {
$photo = new Photo();
$photo->setOwner($this->getUser()->getId());
$photo->setCreated(time());
$photo->setFile([
@ -207,20 +220,23 @@ final class Photos extends VKAPIRequestHandler
"error" => 0,
]);
if (!is_null($caption))
if (!is_null($caption)) {
$photo->setDescription($caption);
}
$photo->save();
unlink($imagePath);
if(!is_null($album))
if (!is_null($album)) {
$album->addPhoto($photo);
}
$images[] = $photo->toVkApiStruct();
}
} catch(ImageException | InvalidStateException $e) {
foreach($imagePaths as $imagePath)
} catch (ImageException | InvalidStateException $e) {
foreach ($imagePaths as $imagePath) {
unlink($imagePath);
}
$this->fail(129, "Invalid image file");
}
@ -231,20 +247,20 @@ final class Photos extends VKAPIRequestHandler
];
}
function createAlbum(string $title, int $group_id = 0, string $description = "", int $privacy = 0)
public function createAlbum(string $title, int $group_id = 0, string $description = "", int $privacy = 0)
{
$this->requireUser();
$this->willExecuteWriteAction();
if($group_id != 0) {
$club = (new Clubs)->get((int) $group_id);
if ($group_id != 0) {
$club = (new Clubs())->get((int) $group_id);
if(!$club || !$club->canBeModifiedBy($this->getUser())) {
if (!$club || !$club->canBeModifiedBy($this->getUser())) {
$this->fail(20, "Invalid club");
}
}
$album = new Album;
$album = new Album();
$album->setOwner(isset($club) ? $club->getId() * -1 : $this->getUser()->getId());
$album->setName($title);
$album->setDescription($description);
@ -254,26 +270,26 @@ final class Photos extends VKAPIRequestHandler
return $album->toVkApiStruct($this->getUser());
}
function editAlbum(int $album_id, int $owner_id, string $title, string $description = "", int $privacy = 0)
public function editAlbum(int $album_id, int $owner_id, string $title, string $description = "", int $privacy = 0)
{
$this->requireUser();
$this->willExecuteWriteAction();
$album = (new Albums)->getAlbumByOwnerAndId($owner_id, $album_id);
$album = (new Albums())->getAlbumByOwnerAndId($owner_id, $album_id);
if(!$album || $album->isDeleted()) {
if (!$album || $album->isDeleted()) {
$this->fail(2, "Invalid album");
}
if(empty($title)) {
if (empty($title)) {
$this->fail(25, "Title is empty");
}
if($album->isCreatedBySystem()) {
if ($album->isCreatedBySystem()) {
$this->fail(40, "You can't change system album");
}
if(!$album->canBeModifiedBy($this->getUser())) {
if (!$album->canBeModifiedBy($this->getUser())) {
$this->fail(2, "Access to album denied");
}
@ -285,50 +301,55 @@ final class Photos extends VKAPIRequestHandler
return $album->toVkApiStruct($this->getUser());
}
function getAlbums(int $owner_id, string $album_ids = "", int $offset = 0, int $count = 100, bool $need_system = true, bool $need_covers = true, bool $photo_sizes = false)
public function getAlbums(int $owner_id, string $album_ids = "", int $offset = 0, int $count = 100, bool $need_system = true, bool $need_covers = true, bool $photo_sizes = false)
{
$this->requireUser();
$res = [];
if(empty($album_ids)) {
if($owner_id > 0) {
$user = (new UsersRepo)->get($owner_id);
if (empty($album_ids)) {
if ($owner_id > 0) {
$user = (new UsersRepo())->get($owner_id);
$res = [
"count" => (new Albums)->getUserAlbumsCount($user),
"items" => []
"count" => (new Albums())->getUserAlbumsCount($user),
"items" => [],
];
if(!$user || $user->isDeleted())
if (!$user || $user->isDeleted()) {
$this->fail(2, "Invalid user");
if(!$user->getPrivacyPermission('photos.read', $this->getUser()))
}
if (!$user->getPrivacyPermission('photos.read', $this->getUser())) {
$this->fail(21, "This user chose to hide his albums.");
}
$albums = array_slice(iterator_to_array((new Albums)->getUserAlbums($user, 1, $count + $offset)), $offset);
$albums = array_slice(iterator_to_array((new Albums())->getUserAlbums($user, 1, $count + $offset)), $offset);
foreach($albums as $album) {
if(!$need_system && $album->isCreatedBySystem()) continue;
foreach ($albums as $album) {
if (!$need_system && $album->isCreatedBySystem()) {
continue;
}
$res["items"][] = $album->toVkApiStruct($this->getUser(), $need_covers, $photo_sizes);
}
}
else {
$club = (new Clubs)->get($owner_id * -1);
} else {
$club = (new Clubs())->get($owner_id * -1);
$res = [
"count" => (new Albums)->getClubAlbumsCount($club),
"items" => []
"count" => (new Albums())->getClubAlbumsCount($club),
"items" => [],
];
if(!$club)
if (!$club) {
$this->fail(2, "Invalid club");
$albums = array_slice(iterator_to_array((new Albums)->getClubAlbums($club, 1, $count + $offset)), $offset);
}
foreach($albums as $album) {
if(!$need_system && $album->isCreatedBySystem()) continue;
$albums = array_slice(iterator_to_array((new Albums())->getClubAlbums($club, 1, $count + $offset)), $offset);
foreach ($albums as $album) {
if (!$need_system && $album->isCreatedBySystem()) {
continue;
}
$res["items"][] = $album->toVkApiStruct($this->getUser(), $need_covers, $photo_sizes);
}
}
@ -338,16 +359,17 @@ final class Photos extends VKAPIRequestHandler
$res = [
"count" => sizeof($albums),
"items" => []
"items" => [],
];
foreach($albums as $album)
{
foreach ($albums as $album) {
$id = explode("_", $album);
$album = (new Albums)->getAlbumByOwnerAndId((int)$id[0], (int)$id[1]);
if($album && !$album->isDeleted()) {
if(!$need_system && $album->isCreatedBySystem()) continue;
$album = (new Albums())->getAlbumByOwnerAndId((int) $id[0], (int) $id[1]);
if ($album && !$album->isDeleted()) {
if (!$need_system && $album->isCreatedBySystem()) {
continue;
}
$res["items"][] = $album->toVkApiStruct($this->getUser(), $need_covers, $photo_sizes);
}
}
@ -356,50 +378,55 @@ final class Photos extends VKAPIRequestHandler
return $res;
}
function getAlbumsCount(int $user_id = 0, int $group_id = 0)
public function getAlbumsCount(int $user_id = 0, int $group_id = 0)
{
$this->requireUser();
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())
$this->fail(21, "Invalid user");
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) {
$cl = (new Clubs)->get($group_id);
if(!$cl) {
if ($user_id > 0) {
$us = (new UsersRepo())->get($user_id);
if (!$us || $us->isDeleted()) {
$this->fail(21, "Invalid user");
}
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) {
$cl = (new Clubs())->get($group_id);
if (!$cl) {
$this->fail(21, "Invalid club");
}
return (new Albums)->getClubAlbumsCount($cl);
return (new Albums())->getClubAlbumsCount($cl);
}
}
function getById(string $photos, bool $extended = false, bool $photo_sizes = false)
public function getById(string $photos, bool $extended = false, bool $photo_sizes = false)
{
$this->requireUser();
$phts = explode(",", $photos);
$res = [];
foreach($phts as $phota) {
foreach ($phts as $phota) {
$ph = explode("_", $phota);
$photo = (new PhotosRepo)->getByOwnerAndVID((int)$ph[0], (int)$ph[1]);
if(!$photo || $photo->isDeleted())
$this->fail(21, "Invalid photo");
$photo = (new PhotosRepo())->getByOwnerAndVID((int) $ph[0], (int) $ph[1]);
if(!$photo->canBeViewedBy($this->getUser()))
if (!$photo || $photo->isDeleted()) {
$this->fail(21, "Invalid photo");
}
if (!$photo->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access denied");
}
$res[] = $photo->toVkApiStruct($photo_sizes, $extended);
}
@ -407,26 +434,30 @@ final class Photos extends VKAPIRequestHandler
return $res;
}
function get(int $owner_id, int $album_id, string $photo_ids = "", bool $extended = false, bool $photo_sizes = false, int $offset = 0, int $count = 10)
public function get(int $owner_id, int $album_id, string $photo_ids = "", bool $extended = false, bool $photo_sizes = false, int $offset = 0, int $count = 10)
{
$this->requireUser();
$res = [];
if(empty($photo_ids)) {
$album = (new Albums)->getAlbumByOwnerAndId($owner_id, $album_id);
if (empty($photo_ids)) {
$album = (new Albums())->getAlbumByOwnerAndId($owner_id, $album_id);
if(!$album || $album->isDeleted())
if (!$album || $album->isDeleted()) {
$this->fail(21, "Invalid album");
if(!$album->canBeViewedBy($this->getUser()))
}
if (!$album->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access denied");
}
$photos = array_slice(iterator_to_array($album->getPhotos(1, $count + $offset)), $offset);
$res["count"] = $album->size();
foreach($photos as $photo) {
if(!$photo || $photo->isDeleted()) continue;
foreach ($photos as $photo) {
if (!$photo || $photo->isDeleted()) {
continue;
}
$res["items"][] = $photo->toVkApiStruct($photo_sizes, $extended);
}
@ -435,14 +466,14 @@ final class Photos extends VKAPIRequestHandler
$res = [
"count" => sizeof($photos),
"items" => []
"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() && $phot->canBeViewedBy($this->getUser())) {
$phot = (new PhotosRepo())->getByOwnerAndVID((int) $id[0], (int) $id[1]);
if ($phot && !$phot->isDeleted() && $phot->canBeViewedBy($this->getUser())) {
$res["items"][] = $phot->toVkApiStruct($photo_sizes, $extended);
}
}
@ -451,38 +482,42 @@ final class Photos extends VKAPIRequestHandler
return $res;
}
function deleteAlbum(int $album_id, int $group_id = 0)
public function deleteAlbum(int $album_id, int $group_id = 0)
{
$this->requireUser();
$this->willExecuteWriteAction();
$album = (new Albums)->get($album_id);
$album = (new Albums())->get($album_id);
if(!$album || $album->canBeModifiedBy($this->getUser()))
if (!$album || $album->canBeModifiedBy($this->getUser())) {
$this->fail(21, "Invalid album");
}
if($album->isDeleted())
if ($album->isDeleted()) {
$this->fail(22, "Album already deleted");
}
$album->delete();
return 1;
}
function edit(int $owner_id, int $photo_id, string $caption = "")
public function edit(int $owner_id, int $photo_id, string $caption = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
$photo = (new PhotosRepo())->getByOwnerAndVID($owner_id, $photo_id);
if(!$photo)
if (!$photo) {
$this->fail(21, "Invalid photo");
}
if($photo->isDeleted())
if ($photo->isDeleted()) {
$this->fail(21, "Photo is deleted");
}
if(!empty($caption)) {
if (!empty($caption)) {
$photo->setDescription($caption);
$photo->save();
}
@ -490,41 +525,46 @@ final class Photos extends VKAPIRequestHandler
return 1;
}
function delete(int $owner_id, int $photo_id, string $photos = "")
public function delete(int $owner_id, int $photo_id, string $photos = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
if(empty($photos)) {
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
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())
if ($photo->isDeleted()) {
$this->fail(21, "Photo is already deleted");
}
$photo->delete();
} else {
$photozs = explode(',', $photos);
foreach($photozs as $photo)
{
foreach ($photozs as $photo) {
$id = explode("_", $photo);
$phot = (new PhotosRepo)->getByOwnerAndVID((int)$id[0], (int)$id[1]);
if($this->getUser()->getId() !== $phot->getOwner()->getId())
$phot = (new PhotosRepo())->getByOwnerAndVID((int) $id[0], (int) $id[1]);
if ($this->getUser()->getId() !== $phot->getOwner()->getId()) {
$this->fail(21, "You can't delete another's photo");
}
if(!$phot)
if (!$phot) {
$this->fail(21, "Invalid photo");
if($phot->isDeleted())
}
if ($phot->isDeleted()) {
$this->fail(21, "Photo already deleted");
}
$phot->delete();
}
@ -533,45 +573,50 @@ final class Photos extends VKAPIRequestHandler
return 1;
}
function getAllComments(int $owner_id, int $album_id, bool $need_likes = false, int $offset = 0, int $count = 100)
public function getAllComments(int $owner_id, int $album_id, bool $need_likes = false, int $offset = 0, int $count = 100)
{
$this->fail(501, "Not implemented");
}
function deleteComment(int $comment_id, int $owner_id = 0)
public function deleteComment(int $comment_id, int $owner_id = 0)
{
$this->requireUser();
$this->willExecuteWriteAction();
$comment = (new CommentsRepo)->get($comment_id);
if(!$comment)
$comment = (new CommentsRepo())->get($comment_id);
if (!$comment) {
$this->fail(21, "Invalid comment");
}
if(!$comment->canBeModifiedBy($this->getUser()))
if (!$comment->canBeModifiedBy($this->getUser())) {
$this->fail(21, "Access denied");
}
$comment->delete();
return 1;
}
function createComment(int $owner_id, int $photo_id, string $message = "", string $attachments = "", bool $from_group = false)
public function createComment(int $owner_id, int $photo_id, string $message = "", string $attachments = "", bool $from_group = false)
{
$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);
$photo = (new PhotosRepo())->getByOwnerAndVID($owner_id, $photo_id);
if(!$photo || $photo->isDeleted())
if (!$photo || $photo->isDeleted()) {
$this->fail(180, "Invalid photo");
}
if(!$photo->canBeViewedBy($this->getUser()))
if (!$photo->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access to photo denied");
}
$comment = new Comment;
$comment = new Comment();
$comment->setOwner($this->getUser()->getId());
$comment->setModel(get_class($photo));
$comment->setTarget($photo->getId());
@ -579,43 +624,49 @@ final class Photos extends VKAPIRequestHandler
$comment->setCreated(time());
$comment->save();
if(!empty($attachments)) {
if (!empty($attachments)) {
$attachmentsArr = explode(",", $attachments);
if(sizeof($attachmentsArr) > 10)
if (sizeof($attachmentsArr) > 10) {
$this->fail(50, "Error: too many attachments");
foreach($attachmentsArr as $attac) {
$attachmentType = NULL;
}
if(str_contains($attac, "photo"))
foreach ($attachmentsArr as $attac) {
$attachmentType = null;
if (str_contains($attac, "photo")) {
$attachmentType = "photo";
elseif(str_contains($attac, "video"))
} elseif (str_contains($attac, "video")) {
$attachmentType = "video";
else
} else {
$this->fail(205, "Unknown attachment type");
}
$attachment = str_replace($attachmentType, "", $attac);
$attachmentOwner = (int)explode("_", $attachment)[0];
$attachmentId = (int)end(explode("_", $attachment));
$attachmentOwner = (int) explode("_", $attachment)[0];
$attachmentId = (int) end(explode("_", $attachment));
$attacc = NULL;
$attacc = null;
if($attachmentType == "photo") {
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
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())
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this photo");
}
$comment->attach($attacc);
} elseif($attachmentType == "video") {
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
} 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())
}
if ($attacc->getOwner()->getId() != $this->getUser()->getId()) {
$this->fail(43, "You do not have access to this video");
}
$comment->attach($attacc);
}
@ -625,56 +676,63 @@ final class Photos extends VKAPIRequestHandler
return $comment->getId();
}
function getAll(int $owner_id, bool $extended = false, int $offset = 0, int $count = 100, bool $photo_sizes = false)
public function getAll(int $owner_id, bool $extended = false, int $offset = 0, int $count = 100, bool $photo_sizes = false)
{
$this->requireUser();
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)
$user = (new UsersRepo())->get($owner_id);
if (!$user) {
$this->fail(4, "Invalid user");
if(!$user->getPrivacyPermission('photos.read', $this->getUser()))
$this->fail(21, "This user chose to hide his albums.");
}
$photos = (new PhotosRepo)->getEveryUserPhoto($user, $offset, $count);
if (!$user->getPrivacyPermission('photos.read', $this->getUser())) {
$this->fail(21, "This user chose to hide his albums.");
}
$photos = (new PhotosRepo())->getEveryUserPhoto($user, $offset, $count);
$res = [
"count" => (new PhotosRepo)->getUserPhotosCount($user),
"count" => (new PhotosRepo())->getUserPhotosCount($user),
"items" => [],
];
foreach($photos as $photo) {
if(!$photo || $photo->isDeleted()) continue;
foreach ($photos as $photo) {
if (!$photo || $photo->isDeleted()) {
continue;
}
$res["items"][] = $photo->toVkApiStruct($photo_sizes, $extended);
}
return $res;
}
function getComments(int $owner_id, int $photo_id, bool $need_likes = false, int $offset = 0, int $count = 100, bool $extended = false, string $fields = "")
public function getComments(int $owner_id, int $photo_id, bool $need_likes = false, int $offset = 0, int $count = 100, bool $extended = false, string $fields = "")
{
$this->requireUser();
$photo = (new PhotosRepo)->getByOwnerAndVID($owner_id, $photo_id);
$photo = (new PhotosRepo())->getByOwnerAndVID($owner_id, $photo_id);
$comms = array_slice(iterator_to_array($photo->getComments(1, $offset + $count)), $offset);
if(!$photo || $photo->isDeleted())
if (!$photo || $photo->isDeleted()) {
$this->fail(4, "Invalid photo");
}
if(!$photo->canBeViewedBy($this->getUser()))
if (!$photo->canBeViewedBy($this->getUser())) {
$this->fail(21, "Access denied");
}
$res = [
"count" => sizeof($comms),
"items" => []
"items" => [],
];
foreach($comms as $comment) {
foreach ($comms as $comment) {
$res["items"][] = $comment->toVkApiStruct($this->getUser(), $need_likes, $extended);
if($extended) {
if($comment->getOwner() instanceof \openvk\Web\Models\Entities\User) {
if ($extended) {
if ($comment->getOwner() instanceof \openvk\Web\Models\Entities\User) {
$res["profiles"][] = $comment->getOwner()->toVkApiStruct();
}
}

View file

@ -1,170 +1,184 @@
<?php declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Repositories\Users as UsersRepo;
use openvk\Web\Models\Entities\Poll;
use openvk\Web\Models\Exceptions\AlreadyVotedException;
use openvk\Web\Models\Exceptions\InvalidOptionException;
use openvk\Web\Models\Exceptions\PollLockedException;
use openvk\Web\Models\Repositories\Polls as PollsRepo;
final class Polls extends VKAPIRequestHandler
{
function getById(int $poll_id, bool $extended = false, string $fields = "sex,screen_name,photo_50,photo_100,online_info,online")
{
$poll = (new PollsRepo)->get($poll_id);
if (!$poll)
$this->fail(100, "One of the parameters specified was missing or invalid: poll_id is incorrect");
$users = array();
$answers = array();
foreach($poll->getResults()->options as $answer) {
$answers[] = (object)[
"id" => $answer->id,
"rate" => $answer->pct,
"text" => $answer->name,
"votes" => $answer->votes
];
}
$userVote = array();
foreach($poll->getUserVote($this->getUser()) as $vote)
$userVote[] = $vote[0];
$response = [
"multiple" => $poll->isMultipleChoice(),
"end_date" => $poll->endsAt() == NULL ? 0 : $poll->endsAt()->timestamp(),
"closed" => $poll->hasEnded(),
"is_board" => false,
"can_edit" => false,
"can_vote" => $poll->canVote($this->getUser()),
"can_report" => false,
"can_share" => true,
"created" => 0,
"id" => $poll->getId(),
"owner_id" => $poll->getOwner()->getId(),
"question" => $poll->getTitle(),
"votes" => $poll->getVoterCount(),
"disable_unvote" => $poll->isRevotable(),
"anonymous" => $poll->isAnonymous(),
"answer_ids" => $userVote,
"answers" => $answers,
"author_id" => $poll->getOwner()->getId(),
];
if ($extended) {
$response["profiles"] = (new Users)->get(strval($poll->getOwner()->getId()), $fields, 0, 1);
/* Currently there is only one person that can be shown trough "Extended" param.
* As "friends" param will be implemented, "profiles" will show more users
*/
}
return (object) $response;
}
function addVote(int $poll_id, string $answers_ids)
{
$this->requireUser();
$this->willExecuteWriteAction();
$poll = (new PollsRepo)->get($poll_id);
if(!$poll)
$this->fail(251, "Invalid poll id");
try {
$poll->vote($this->getUser(), explode(",", $answers_ids));
return 1;
} catch(AlreadyVotedException $ex) {
return 0;
} catch(PollLockedException $ex) {
return 0;
} catch(InvalidOptionException $ex) {
$this->fail(8, "бдсм вибратор купить в киеве");
}
}
function deleteVote(int $poll_id)
{
$this->requireUser();
$this->willExecuteWriteAction();
$poll = (new PollsRepo)->get($poll_id);
if(!$poll)
$this->fail(251, "Invalid poll id");
try {
$poll->revokeVote($this->getUser());
return 1;
} catch(PollLockedException $ex) {
$this->fail(15, "Access denied: Poll is locked or isn't revotable");
} catch(InvalidOptionException $ex) {
$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;
}
}
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Repositories\Users as UsersRepo;
use openvk\Web\Models\Entities\Poll;
use openvk\Web\Models\Exceptions\AlreadyVotedException;
use openvk\Web\Models\Exceptions\InvalidOptionException;
use openvk\Web\Models\Exceptions\PollLockedException;
use openvk\Web\Models\Repositories\Polls as PollsRepo;
final class Polls extends VKAPIRequestHandler
{
public function getById(int $poll_id, bool $extended = false, string $fields = "sex,screen_name,photo_50,photo_100,online_info,online")
{
$poll = (new PollsRepo())->get($poll_id);
if (!$poll) {
$this->fail(100, "One of the parameters specified was missing or invalid: poll_id is incorrect");
}
$users = [];
$answers = [];
foreach ($poll->getResults()->options as $answer) {
$answers[] = (object) [
"id" => $answer->id,
"rate" => $answer->pct,
"text" => $answer->name,
"votes" => $answer->votes,
];
}
$userVote = [];
foreach ($poll->getUserVote($this->getUser()) as $vote) {
$userVote[] = $vote[0];
}
$response = [
"multiple" => $poll->isMultipleChoice(),
"end_date" => $poll->endsAt() == null ? 0 : $poll->endsAt()->timestamp(),
"closed" => $poll->hasEnded(),
"is_board" => false,
"can_edit" => false,
"can_vote" => $poll->canVote($this->getUser()),
"can_report" => false,
"can_share" => true,
"created" => 0,
"id" => $poll->getId(),
"owner_id" => $poll->getOwner()->getId(),
"question" => $poll->getTitle(),
"votes" => $poll->getVoterCount(),
"disable_unvote" => $poll->isRevotable(),
"anonymous" => $poll->isAnonymous(),
"answer_ids" => $userVote,
"answers" => $answers,
"author_id" => $poll->getOwner()->getId(),
];
if ($extended) {
$response["profiles"] = (new Users())->get(strval($poll->getOwner()->getId()), $fields, 0, 1);
/* Currently there is only one person that can be shown trough "Extended" param.
* As "friends" param will be implemented, "profiles" will show more users
*/
}
return (object) $response;
}
public function addVote(int $poll_id, string $answers_ids)
{
$this->requireUser();
$this->willExecuteWriteAction();
$poll = (new PollsRepo())->get($poll_id);
if (!$poll) {
$this->fail(251, "Invalid poll id");
}
try {
$poll->vote($this->getUser(), explode(",", $answers_ids));
return 1;
} catch (AlreadyVotedException $ex) {
return 0;
} catch (PollLockedException $ex) {
return 0;
} catch (InvalidOptionException $ex) {
$this->fail(8, "бдсм вибратор купить в киеве");
}
}
public function deleteVote(int $poll_id)
{
$this->requireUser();
$this->willExecuteWriteAction();
$poll = (new PollsRepo())->get($poll_id);
if (!$poll) {
$this->fail(251, "Invalid poll id");
}
try {
$poll->revokeVote($this->getUser());
return 1;
} catch (PollLockedException $ex) {
$this->fail(15, "Access denied: Poll is locked or isn't revotable");
} catch (InvalidOptionException $ex) {
$this->fail(8, "how.to. ook.bacon.in.microwova.");
}
}
public 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;
}
public 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());
}
public function edit()
{
#todo
return 1;
}
}

View file

@ -1,42 +1,46 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Models\Entities\Report;
use openvk\Web\Models\Repositories\Reports as ReportsRepo;
final class Reports extends VKAPIRequestHandler
{
function add(int $owner_id = 0, string $comment = "", int $reason = 0, string $type = "", string $report_source = ""): int
public function add(int $owner_id = 0, string $comment = "", int $reason = 0, string $type = "", string $report_source = ""): int
{
$this->requireUser();
$this->willExecuteWriteAction();
$allowed_types = ["post", "photo", "video", "group", "comment", "note", "app", "user", "audio"];
if($type == "" || !in_array($type, $allowed_types)) {
$this->fail(100, "One of the parameters specified was missing or invalid: type should be ".implode(", ", $allowed_types));
if ($type == "" || !in_array($type, $allowed_types)) {
$this->fail(100, "One of the parameters specified was missing or invalid: type should be " . implode(", ", $allowed_types));
}
if($owner_id <= 0) {
if ($owner_id <= 0) {
$this->fail(100, "One of the parameters specified was missing or invalid: Bad input");
}
if(mb_strlen($comment) === 0) {
if (mb_strlen($comment) === 0) {
$this->fail(100, "One of the parameters specified was missing or invalid: Comment can't be empty");
}
if($type == "user" && $owner_id == $this->getUser()->getId()) {
if ($type == "user" && $owner_id == $this->getUser()->getId()) {
return 1;
}
if($this->getUser()->isBannedInSupport()) {
if ($this->getUser()->isBannedInSupport()) {
return 0;
}
if(sizeof(iterator_to_array((new ReportsRepo)->getDuplicates($type, $owner_id, NULL, $this->getUser()->getId()))) > 0) {
if (sizeof(iterator_to_array((new ReportsRepo())->getDuplicates($type, $owner_id, null, $this->getUser()->getId()))) > 0) {
return 1;
}
try {
$report = new Report;
$report = new Report();
$report->setUser_id($this->getUser()->getId());
$report->setTarget_id($owner_id);
$report->setType($type);
@ -44,7 +48,7 @@ final class Reports extends VKAPIRequestHandler
$report->setCreated(time());
$report->save();
} catch(\Throwable $e) {
} catch (\Throwable $e) {
$this->fail(-1, "Unknown error failed");
}

View file

@ -1,27 +1,33 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Repositories\Users as UsersRepo;
final class Status extends VKAPIRequestHandler
{
function get(int $user_id = 0, int $group_id = 0)
public function get(int $user_id = 0, int $group_id = 0)
{
$this->requireUser();
if($user_id == 0 && $group_id == 0)
if ($user_id == 0 && $group_id == 0) {
$user_id = $this->getUser()->getId();
}
if($group_id > 0)
if ($group_id > 0) {
$this->fail(501, "Group statuses are not implemented");
else {
$user = (new UsersRepo)->get($user_id);
} else {
$user = (new UsersRepo())->get($user_id);
if(!$user || $user->isDeleted() || !$user->canBeViewedBy($this->getUser()))
if (!$user || $user->isDeleted() || !$user->canBeViewedBy($this->getUser())) {
$this->fail(15, "Invalid user");
}
$audioStatus = $user->getCurrentAudioStatus();
if($audioStatus) {
if ($audioStatus) {
return [
"status" => $user->getStatus(),
"audio" => $audioStatus->toVkApiStruct(),
@ -32,17 +38,17 @@ final class Status extends VKAPIRequestHandler
}
}
function set(string $text, int $group_id = 0)
public function set(string $text, int $group_id = 0)
{
$this->requireUser();
$this->willExecuteWriteAction();
if($group_id > 0) {
if ($group_id > 0) {
$this->fail(501, "Group statuses are not implemented");
} else {
$this->getUser()->setStatus($text);
$this->getUser()->save();
return 1;
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
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};
@ -7,378 +11,392 @@ use openvk\Web\Models\Repositories\Reports;
final class Users extends VKAPIRequestHandler
{
function get(string $user_ids = "0", string $fields = "", int $offset = 0, int $count = 100, User $authuser = null /* костыль(( */): array
public function get(string $user_ids = "0", string $fields = "", int $offset = 0, int $count = 100, User $authuser = null /* костыль(( */): array
{
if($authuser == NULL) $authuser = $this->getUser();
if ($authuser == null) {
$authuser = $this->getUser();
}
$users = new UsersRepo();
if ($user_ids == "0") {
if (!$authuser) {
return [];
}
$user_ids = (string) $authuser->getId();
}
$users = new UsersRepo;
if($user_ids == "0") {
if(!$authuser) {
return [];
}
$user_ids = (string) $authuser->getId();
}
$usrs = explode(',', $user_ids);
$response = array();
$response = [];
$ic = sizeof($usrs);
if(sizeof($usrs) > $count)
$ic = $count;
if (sizeof($usrs) > $count) {
$ic = $count;
}
$usrs = array_slice($usrs, $offset * $count);
for($i=0; $i < $ic; $i++) {
if($usrs[$i] != 0) {
$usr = $users->get((int) $usrs[$i]);
if(is_null($usr) || $usr->isDeleted()) {
$response[$i] = (object)[
"id" => (int) $usrs[$i],
"first_name" => "DELETED",
"last_name" => "",
"deactivated" => "deleted"
];
} else if($usr->isBanned()) {
$response[$i] = (object)[
"id" => $usr->getId(),
"first_name" => $usr->getFirstName(true),
"last_name" => $usr->getLastName(true),
"deactivated" => "banned",
"ban_reason" => $usr->getBanReason()
];
} else if($usrs[$i] == NULL) {
for ($i = 0; $i < $ic; $i++) {
if ($usrs[$i] != 0) {
$usr = $users->get((int) $usrs[$i]);
if (is_null($usr) || $usr->isDeleted()) {
$response[$i] = (object) [
"id" => (int) $usrs[$i],
"first_name" => "DELETED",
"last_name" => "",
"deactivated" => "deleted",
];
} elseif ($usr->isBanned()) {
$response[$i] = (object) [
"id" => $usr->getId(),
"first_name" => $usr->getFirstName(true),
"last_name" => $usr->getLastName(true),
"deactivated" => "banned",
"ban_reason" => $usr->getBanReason(),
];
} elseif ($usrs[$i] == null) {
} else {
$response[$i] = (object)[
"id" => $usr->getId(),
"first_name" => $usr->getFirstName(true),
"last_name" => $usr->getLastName(true),
"is_closed" => $usr->isClosed(),
"can_access_closed" => (bool)$usr->canBeViewedBy($this->getUser()),
];
} else {
$response[$i] = (object) [
"id" => $usr->getId(),
"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);
$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 : ($usr->isNeutral() ? 0 : 2);
break;
case "has_photo":
$response[$i]->has_photo = is_null($usr->getAvatarPhoto()) ? 0 : 1;
break;
case "photo_max_orig":
$response[$i]->photo_max_orig = $usr->getAvatarURL();
break;
case "photo_max":
$response[$i]->photo_max = $usr->getAvatarURL("original");
break;
case "photo_50":
$response[$i]->photo_50 = $usr->getAvatarURL();
break;
case "photo_100":
$response[$i]->photo_100 = $usr->getAvatarURL("tiny");
break;
case "photo_200":
$response[$i]->photo_200 = $usr->getAvatarURL("normal");
break;
case "photo_200_orig": # вообще не ебу к чему эта строка ну пусть будет кек
$response[$i]->photo_200_orig = $usr->getAvatarURL("normal");
break;
case "photo_400_orig":
$response[$i]->photo_400_orig = $usr->getAvatarURL("normal");
break;
# Она хочет быть выебанной видя матан
# Покайфу когда ты Виет а вокруг лишь дискриминант
$flds = explode(',', $fields);
$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 : ($usr->isNeutral() ? 0 : 2);
break;
case "has_photo":
$response[$i]->has_photo = is_null($usr->getAvatarPhoto()) ? 0 : 1;
break;
case "photo_max_orig":
$response[$i]->photo_max_orig = $usr->getAvatarURL();
break;
case "photo_max":
$response[$i]->photo_max = $usr->getAvatarURL("original");
break;
case "photo_50":
$response[$i]->photo_50 = $usr->getAvatarURL();
break;
case "photo_100":
$response[$i]->photo_100 = $usr->getAvatarURL("tiny");
break;
case "photo_200":
$response[$i]->photo_200 = $usr->getAvatarURL("normal");
break;
case "photo_200_orig": # вообще не ебу к чему эта строка ну пусть будет кек
$response[$i]->photo_200_orig = $usr->getAvatarURL("normal");
break;
case "photo_400_orig":
$response[$i]->photo_400_orig = $usr->getAvatarURL("normal");
break;
# ору а когда я это успел написать
# вова кстати не матерись в коде мамка же спалит азщазаззазщазазаззазазазх
case "status":
if($usr->getStatus() != NULL)
$response[$i]->status = $usr->getStatus();
$audioStatus = $usr->getCurrentAudioStatus();
# Она хочет быть выебанной видя матан
# Покайфу когда ты Виет а вокруг лишь дискриминант
if($audioStatus)
$response[$i]->status_audio = $audioStatus->toVkApiStruct();
# ору а когда я это успел написать
# вова кстати не матерись в коде мамка же спалит азщазаззазщазазаззазазазх
case "status":
if ($usr->getStatus() != null) {
$response[$i]->status = $usr->getStatus();
}
break;
case "screen_name":
if($usr->getShortCode() != NULL)
$response[$i]->screen_name = $usr->getShortCode();
break;
case "friend_status":
switch($usr->getSubscriptionStatus($authuser)) {
case 3:
# NOTICE falling through
case 0:
$response[$i]->friend_status = $usr->getSubscriptionStatus($authuser);
break;
case 1:
$response[$i]->friend_status = 2;
break;
case 2:
$response[$i]->friend_status = 1;
break;
}
break;
case "last_seen":
if ($usr->onlineStatus() == 0) {
$platform = $usr->getOnlinePlatform(true);
switch ($platform) {
case 'iphone':
$platform = 2;
break;
$audioStatus = $usr->getCurrentAudioStatus();
case 'android':
$platform = 4;
break;
if ($audioStatus) {
$response[$i]->status_audio = $audioStatus->toVkApiStruct();
}
case NULL:
$platform = 7;
break;
default:
$platform = 1;
break;
}
break;
case "screen_name":
if ($usr->getShortCode() != null) {
$response[$i]->screen_name = $usr->getShortCode();
}
break;
case "friend_status":
switch ($usr->getSubscriptionStatus($authuser)) {
case 3:
# NOTICE falling through
case 0:
$response[$i]->friend_status = $usr->getSubscriptionStatus($authuser);
break;
case 1:
$response[$i]->friend_status = 2;
break;
case 2:
$response[$i]->friend_status = 1;
break;
}
break;
case "last_seen":
if ($usr->onlineStatus() == 0) {
$platform = $usr->getOnlinePlatform(true);
switch ($platform) {
case 'iphone':
$platform = 2;
break;
$response[$i]->last_seen = (object) [
"platform" => $platform,
"time" => $usr->getOnline()->timestamp()
];
}
case "music":
if(!$canView) {
break;
}
case 'android':
$platform = 4;
break;
$response[$i]->music = $usr->getFavoriteMusic();
break;
case "movies":
if(!$canView) {
break;
}
case null:
$platform = 7;
break;
$response[$i]->movies = $usr->getFavoriteFilms();
break;
case "tv":
if(!$canView) {
break;
}
default:
$platform = 1;
break;
}
$response[$i]->tv = $usr->getFavoriteShows();
break;
case "books":
if(!$canView) {
break;
}
$response[$i]->last_seen = (object) [
"platform" => $platform,
"time" => $usr->getOnline()->timestamp(),
];
}
// no break
case "music":
if (!$canView) {
break;
}
$response[$i]->books = $usr->getFavoriteBooks();
break;
case "city":
if(!$canView) {
break;
}
$response[$i]->music = $usr->getFavoriteMusic();
break;
case "movies":
if (!$canView) {
break;
}
$response[$i]->city = $usr->getCity();
break;
case "interests":
if(!$canView) {
break;
}
$response[$i]->movies = $usr->getFavoriteFilms();
break;
case "tv":
if (!$canView) {
break;
}
$response[$i]->interests = $usr->getInterests();
break;
case "quotes":
if(!$canView) {
break;
}
$response[$i]->tv = $usr->getFavoriteShows();
break;
case "books":
if (!$canView) {
break;
}
$response[$i]->quotes = $usr->getFavoriteQuote();
break;
case "games":
if(!$canView) {
break;
}
$response[$i]->books = $usr->getFavoriteBooks();
break;
case "city":
if (!$canView) {
break;
}
$response[$i]->games = $usr->getFavoriteGames();
break;
case "email":
if(!$canView) {
break;
}
$response[$i]->city = $usr->getCity();
break;
case "interests":
if (!$canView) {
break;
}
$response[$i]->email = $usr->getContactEmail();
break;
case "telegram":
if(!$canView) {
break;
}
$response[$i]->interests = $usr->getInterests();
break;
case "quotes":
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]->quotes = $usr->getFavoriteQuote();
break;
case "games":
if (!$canView) {
break;
}
$response[$i]->rating = $usr->getRating();
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;
$response[$i]->games = $usr->getFavoriteGames();
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;
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;
case 'blacklisted_by_me':
if(!$authuser) {
continue;
}
case 'background':
$backgrounds = $usr->getBackDropPictureURLs();
$response[$i]->background = $backgrounds;
break;
case 'reg_date':
if (!$canView) {
break;
}
$response[$i]->blacklisted_by_me = (int)$usr->isBlacklistedBy($this->getUser());
break;
case 'blacklisted':
if(!$authuser) {
continue;
}
$response[$i]->blacklisted = (int)$this->getUser()->isBlacklistedBy($usr);
break;
case "custom_fields":
if(sizeof($usrs) > 1)
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;
case 'blacklisted_by_me':
if (!$authuser) {
continue;
}
$c_fields = \openvk\Web\Models\Entities\UserInfoEntities\AdditionalField::getByOwner($usr->getId());
$append_array = [];
foreach($c_fields as $c_field)
$append_array[] = $c_field->toVkApiStruct();
$response[$i]->blacklisted_by_me = (int) $usr->isBlacklistedBy($this->getUser());
break;
case 'blacklisted':
if (!$authuser) {
continue;
}
$response[$i]->custom_fields = $append_array;
break;
}
}
$response[$i]->blacklisted = (int) $this->getUser()->isBlacklistedBy($usr);
break;
case "custom_fields":
if (sizeof($usrs) > 1) {
break;
}
if($usr->getOnline()->timestamp() + 300 > time())
$response[$i]->online = 1;
else
$response[$i]->online = 0;
}
}
$c_fields = \openvk\Web\Models\Entities\UserInfoEntities\AdditionalField::getByOwner($usr->getId());
$append_array = [];
foreach ($c_fields as $c_field) {
$append_array[] = $c_field->toVkApiStruct();
}
$response[$i]->custom_fields = $append_array;
break;
}
}
if ($usr->getOnline()->timestamp() + 300 > time()) {
$response[$i]->online = 1;
} else {
$response[$i]->online = 0;
}
}
}
}
return $response;
}
function getFollowers(int $user_id, string $fields = "", int $offset = 0, int $count = 100): object
public function getFollowers(int $user_id, string $fields = "", int $offset = 0, int $count = 100): object
{
$offset++;
$followers = [];
$users = new UsersRepo;
$users = new UsersRepo();
$this->requireUser();
$user = $users->get($user_id);
if(!$user || $user->isDeleted())
if (!$user || $user->isDeleted()) {
$this->fail(14, "Invalid user");
}
if(!$user->canBeViewedBy($this->getUser()))
if (!$user->canBeViewedBy($this->getUser())) {
$this->fail(15, "Access denied");
}
foreach($users->get($user_id)->getFollowers($offset, $count) as $follower)
foreach ($users->get($user_id)->getFollowers($offset, $count) as $follower) {
$followers[] = $follower->getId();
}
$response = $followers;
if(!is_null($fields))
$response = $this->get(implode(',', $followers), $fields, 0, $count);
if (!is_null($fields)) {
$response = $this->get(implode(',', $followers), $fields, 0, $count);
}
return (object) [
"count" => $users->get($user_id)->getFollowersCount(),
"items" => $response
"items" => $response,
];
}
function search(string $q,
string $fields = "",
int $offset = 0,
int $count = 100,
string $city = "",
string $hometown = "",
int $sex = 3,
int $status = 0, # marital_status
bool $online = false,
# non standart params:
int $sort = 0,
int $polit_views = 0,
string $fav_music = "",
string $fav_films = "",
string $fav_shows = "",
string $fav_books = ""
)
{
if($count > 100) {
public function search(
string $q,
string $fields = "",
int $offset = 0,
int $count = 100,
string $city = "",
string $hometown = "",
int $sex = 3,
int $status = 0, # marital_status
bool $online = false,
# non standart params:
int $sort = 0,
int $polit_views = 0,
string $fav_music = "",
string $fav_films = "",
string $fav_shows = "",
string $fav_books = ""
) {
if ($count > 100) {
$this->fail(100, "One of the parameters specified was missing or invalid: count should be less or equal to 100");
}
$users = new UsersRepo;
$users = new UsersRepo();
$output_sort = ['type' => 'id', 'invert' => false];
$output_params = [
"ignore_private" => true,
];
$output_params = [
"ignore_private" => true,
];
switch($sort) {
default:
switch ($sort) {
default:
case 0:
$output_sort = ['type' => 'id', 'invert' => false];
break;
@ -386,50 +404,62 @@ final class Users extends VKAPIRequestHandler
$output_sort = ['type' => 'id', 'invert' => true];
break;
case 4:
$output_sort = ['type' => 'rating', 'invert' => false];
$output_sort = ['type' => 'rating', 'invert' => false];
break;
}
if(!empty($city))
if (!empty($city)) {
$output_params['city'] = $city;
}
if(!empty($hometown))
if (!empty($hometown)) {
$output_params['hometown'] = $hometown;
}
if($sex != 3)
$output_params['gender'] = $sex;
if ($sex != 3) {
$output_params['gender'] = $sex;
}
if($status != 0)
if ($status != 0) {
$output_params['marital_status'] = $status;
if($polit_views != 0)
$output_params['polit_views'] = $polit_views;
}
if(!empty($interests))
if ($polit_views != 0) {
$output_params['polit_views'] = $polit_views;
}
if (!empty($interests)) {
$output_params['interests'] = $interests;
}
if(!empty($fav_music))
if (!empty($fav_music)) {
$output_params['fav_music'] = $fav_music;
}
if(!empty($fav_films))
if (!empty($fav_films)) {
$output_params['fav_films'] = $fav_films;
}
if(!empty($fav_shows))
if (!empty($fav_shows)) {
$output_params['fav_shows'] = $fav_shows;
if(!empty($fav_books))
$output_params['fav_books'] = $fav_books;
}
if($online)
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);
foreach ($find->offsetLimit($offset, $count) as $user)
foreach ($find->offsetLimit($offset, $count) as $user) {
$array[] = $user->getId();
}
if(!$array || sizeof($array) < 1) {
if (!$array || sizeof($array) < 1) {
return (object) [
"count" => 0,
"items" => [],
@ -438,29 +468,31 @@ final class Users extends VKAPIRequestHandler
return (object) [
"count" => $find->size(),
"items" => $this->get(implode(',', $array), $fields)
"items" => $this->get(implode(',', $array), $fields),
];
}
function report(int $user_id, string $type = "spam", string $comment = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
public 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 ($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;
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();
$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;
}
return 1;
}
}

View file

@ -1,55 +1,62 @@
<?php declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Models\Repositories\{Users, Clubs};
final class Utils extends VKAPIRequestHandler
{
function getServerTime(): int
{
return time();
}
function resolveScreenName(string $screen_name): object
{
if(\Chandler\MVC\Routing\Router::i()->getMatchingRoute("/$screen_name")[0]->presenter !== "UnknownTextRouteStrategy") {
if(substr($screen_name, 0, strlen("id")) === "id") {
return (object) [
"object_id" => (int) substr($screen_name, strlen("id")),
"type" => "user"
];
} else if(substr($screen_name, 0, strlen("club")) === "club") {
return (object) [
"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) {
return (object) [
"object_id" => $user->getId(),
"type" => "user"
];
}
$club = (new Clubs)->getByShortURL($screen_name);
if($club) {
return (object) [
"object_id" => $club->getId(),
"type" => "group"
];
}
$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());
}
}
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Models\Repositories\{Users, Clubs};
final class Utils extends VKAPIRequestHandler
{
public function getServerTime(): int
{
return time();
}
public function resolveScreenName(string $screen_name): object
{
if (\Chandler\MVC\Routing\Router::i()->getMatchingRoute("/$screen_name")[0]->presenter !== "UnknownTextRouteStrategy") {
if (substr($screen_name, 0, strlen("id")) === "id") {
return (object) [
"object_id" => (int) substr($screen_name, strlen("id")),
"type" => "user",
];
} elseif (substr($screen_name, 0, strlen("club")) === "club") {
return (object) [
"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) {
return (object) [
"object_id" => $user->getId(),
"type" => "user",
];
}
$club = (new Clubs())->getByShortURL($screen_name);
if ($club) {
return (object) [
"object_id" => $club->getId(),
"type" => "group",
];
}
$this->fail(104, "Not found");
}
}
public function resolveGuid(string $guid): object
{
$user = (new Users())->getByChandlerUserId($guid);
if (is_null($user)) {
$this->fail(104, "Not found");
}
return $user->toVkApiStruct($this->getUser());
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\VKAPI\Exceptions\APIErrorException;
use openvk\Web\Models\Entities\IP;
use openvk\Web\Models\Entities\User;
@ -9,50 +13,51 @@ abstract class VKAPIRequestHandler
{
protected $user;
protected $platform;
function __construct(?User $user = NULL, ?string $platform = NULL)
public function __construct(?User $user = null, ?string $platform = null)
{
$this->user = $user;
$this->platform = $platform;
}
protected function fail(int $code, string $message): void
{
throw new APIErrorException($message, $code);
}
protected function getUser(): ?User
{
return $this->user;
}
protected function getPlatform(): ?string
{
return $this->platform ?? "";
}
protected function userAuthorized(): bool
{
return !is_null($this->getUser());
}
protected function requireUser(): void
{
if(!$this->userAuthorized())
if (!$this->userAuthorized()) {
$this->fail(5, "User authorization failed: no access_token passed.");
}
}
protected function willExecuteWriteAction(): void
{
$ip = (new IPs)->get(CONNECTING_IP);
$ip = (new IPs())->get(CONNECTING_IP);
$res = $ip->rateLimit();
if(!($res === IP::RL_RESET || $res === IP::RL_CANEXEC)) {
if($res === IP::RL_BANNED && OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["autoban"]) {
if (!($res === IP::RL_RESET || $res === IP::RL_CANEXEC)) {
if ($res === IP::RL_BANNED && OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["autoban"]) {
$this->user->ban("User account has been suspended for breaking API terms of service", false);
$this->fail(18, "User account has been suspended due to repeated violation of API rate limits.");
}
$this->fail(29, "You have been rate limited.");
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Handlers;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Repositories\Users as UsersRepo;
use openvk\Web\Models\Entities\Club;
@ -11,48 +15,49 @@ use openvk\Web\Models\Repositories\Comments as CommentsRepo;
final class Video extends VKAPIRequestHandler
{
function get(int $owner_id = 0, string $videos = "", string $fields = "", int $offset = 0, int $count = 30, int $extended = 0): object
public function get(int $owner_id = 0, string $videos = "", string $fields = "", int $offset = 0, int $count = 30, int $extended = 0): object
{
$this->requireUser();
if(!empty($videos)) {
if (!empty($videos)) {
$vids = explode(',', $videos);
$profiles = [];
$groups = [];
foreach($vids as $vid) {
foreach ($vids as $vid) {
$id = explode("_", $vid);
$items = [];
$video = (new VideosRepo)->getByOwnerAndVID(intval($id[0]), intval($id[1]));
if($video && !$video->isDeleted()) {
$video = (new VideosRepo())->getByOwnerAndVID(intval($id[0]), intval($id[1]));
if ($video && !$video->isDeleted()) {
$out_video = $video->getApiStructure($this->getUser())->video;
$items[] = $out_video;
if($out_video['owner_id']) {
if($out_video['owner_id'] > 0)
if ($out_video['owner_id']) {
if ($out_video['owner_id'] > 0) {
$profiles[] = $out_video['owner_id'];
else
} else {
$groups[] = abs($out_video['owner_id']);
}
}
}
}
if($extended == 1) {
if ($extended == 1) {
$profiles = array_unique($profiles);
$groups = array_unique($groups);
$profilesFormatted = [];
$groupsFormatted = [];
foreach($profiles as $prof) {
$profile = (new UsersRepo)->get($prof);
foreach ($profiles as $prof) {
$profile = (new UsersRepo())->get($prof);
$profilesFormatted[] = $profile->toVkApiStruct($this->getUser(), $fields);
}
foreach($groups as $gr) {
$group = (new ClubsRepo)->get($gr);
foreach ($groups as $gr) {
$group = (new ClubsRepo())->get($gr);
$groupsFormatted[] = $group->toVkApiStruct($this->getUser(), $fields);
}
return (object) [
"count" => sizeof($items),
"items" => $items,
@ -60,57 +65,61 @@ final class Video extends VKAPIRequestHandler
"groups" => $groupsFormatted,
];
}
return (object) [
"count" => count($items),
"items" => $items
"items" => $items,
];
} else {
if ($owner_id > 0)
$user = (new UsersRepo)->get($owner_id);
else
if ($owner_id > 0) {
$user = (new UsersRepo())->get($owner_id);
} else {
$this->fail(1, "Not implemented");
if(!$user || $user->isDeleted())
}
if (!$user || $user->isDeleted()) {
$this->fail(14, "Invalid user");
}
if(!$user->getPrivacyPermission('videos.read', $this->getUser()))
if (!$user->getPrivacyPermission('videos.read', $this->getUser())) {
$this->fail(21, "This user chose to hide his videos.");
}
$videos = (new VideosRepo())->getByUserLimit($user, $offset, $count);
$videosCount = (new VideosRepo())->getUserVideosCount($user);
$videos = (new VideosRepo)->getByUserLimit($user, $offset, $count);
$videosCount = (new VideosRepo)->getUserVideosCount($user);
$items = [];
$profiles = [];
$groups = [];
foreach($videos as $video) {
foreach ($videos as $video) {
$video = $video->getApiStructure($this->getUser())->video;
$items[] = $video;
if($video['owner_id']) {
if($video['owner_id'] > 0)
if ($video['owner_id']) {
if ($video['owner_id'] > 0) {
$profiles[] = $video['owner_id'];
else
} else {
$groups[] = abs($video['owner_id']);
}
}
}
if($extended == 1) {
if ($extended == 1) {
$profiles = array_unique($profiles);
$groups = array_unique($groups);
$profilesFormatted = [];
$groupsFormatted = [];
foreach($profiles as $prof) {
$profile = (new UsersRepo)->get($prof);
foreach ($profiles as $prof) {
$profile = (new UsersRepo())->get($prof);
$profilesFormatted[] = $profile->toVkApiStruct($this->getUser(), $fields);
}
foreach($groups as $gr) {
$group = (new ClubsRepo)->get($gr);
foreach ($groups as $gr) {
$group = (new ClubsRepo())->get($gr);
$groupsFormatted[] = $group->toVkApiStruct($this->getUser(), $fields);
}
return (object) [
"count" => $videosCount,
"items" => $items,
@ -118,54 +127,55 @@ final class Video extends VKAPIRequestHandler
"groups" => $groupsFormatted,
];
}
return (object) [
"count" => $videosCount,
"items" => $items
"items" => $items,
];
}
}
function search(string $q = '', int $sort = 0, int $offset = 0, int $count = 10, bool $extended = false, string $fields = ''): object
public 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);
$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) {
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)
if ($return_item['owner_id']) {
if ($return_item['owner_id'] > 0) {
$profiles[] = $return_item['owner_id'];
else
} else {
$groups[] = abs($return_item['owner_id']);
}
}
}
if($extended) {
if ($extended) {
$profiles = array_unique($profiles);
$groups = array_unique($groups);
$profilesFormatted = [];
$groupsFormatted = [];
foreach($profiles as $prof) {
$profile = (new UsersRepo)->get($prof);
foreach ($profiles as $prof) {
$profile = (new UsersRepo())->get($prof);
$profilesFormatted[] = $profile->toVkApiStruct($this->getUser(), $fields);
}
foreach($groups as $gr) {
$group = (new ClubsRepo)->get($gr);
foreach ($groups as $gr) {
$group = (new ClubsRepo())->get($gr);
$groupsFormatted[] = $group->toVkApiStruct($this->getUser(), $fields);
}

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,7 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Structures;
final class Conversation

View file

@ -1,4 +1,7 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\VKAPI\Structures;
final class Message
@ -17,5 +20,5 @@ final class Message
public $emoji;
public $important = true;
public $deleted = 0;
public $random_id = NULL;
public $random_id = null;
}

View file

@ -1,7 +1,10 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Events;
interface ILPEmitable
{
function getLongPoolSummary(): object;
public function getLongPoolSummary(): object;
}

View file

@ -1,32 +1,37 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Events;
use openvk\Web\Models\Entities\Message;
use openvk\Web\Models\Repositories\Messages;
class NewMessageEvent implements ILPEmitable
{
protected $payload;
function __construct(Message $message)
public function __construct(Message $message)
{
$this->payload = $message->simplify();
}
function getLongPoolSummary(): object
public function getLongPoolSummary(): object
{
return (object) [
"type" => "newMessage",
"message" => $this->payload,
];
}
function getVKAPISummary(int $userId): array
public function getVKAPISummary(int $userId): array
{
$msg = (new Messages)->get($this->payload["uuid"]);
$msg = (new Messages())->get($this->payload["uuid"]);
$peer = $msg->getSender()->getId();
if($peer === $userId)
if ($peer === $userId) {
$peer = $msg->getRecipient()->getId();
}
/*
* Source:
* https://github.com/danyadev/longpoll-doc
@ -43,7 +48,7 @@ class NewMessageEvent implements ILPEmitable
[], # empty attachments
$msg->getId() << 2, # id as random_id
$peer, # conversation id
0 # not edited yet
0, # not edited yet
];
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Repositories\Users;
use Nette\InvalidStateException as ISE;
@ -7,52 +11,53 @@ use Nette\InvalidStateException as ISE;
class APIToken extends RowModel
{
protected $tableName = "api_tokens";
function getUser(): User
public function getUser(): User
{
return (new Users)->get($this->getRecord()->user);
return (new Users())->get($this->getRecord()->user);
}
function getSecret(): string
public function getSecret(): string
{
return $this->getRecord()->secret;
}
function getFormattedToken(): string
public function getFormattedToken(): string
{
return $this->getId() . "-" . chunk_split($this->getSecret(), 8, "-") . "jill";
}
function getPlatform(): ?string
public function getPlatform(): ?string
{
return $this->getRecord()->platform;
}
function isRevoked(): bool
public function isRevoked(): bool
{
return $this->isDeleted();
}
function setUser(User $user): void
public function setUser(User $user): void
{
$this->stateChanges("user", $user->getId());
}
function setSecret(string $secret): void
public function setSecret(string $secret): void
{
throw new ISE("Setting secret manually is prohbited");
}
function revoke(): void
public function revoke(): void
{
$this->delete();
}
function save(?bool $log = false): void
public function save(?bool $log = false): void
{
if(is_null($this->getRecord()))
if (is_null($this->getRecord())) {
$this->stateChanges("secret", bin2hex(openssl_random_pseudo_bytes(36)));
}
parent::save();
}
}

View file

@ -1,88 +1,94 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\Repositories\Photos;
class Album extends MediaCollection
{
const SPECIAL_AVATARS = 16;
const SPECIAL_WALL = 32;
public const SPECIAL_AVATARS = 16;
public const SPECIAL_WALL = 32;
protected $tableName = "albums";
protected $relTableName = "album_relations";
protected $entityTableName = "photos";
protected $entityClassName = 'openvk\Web\Models\Entities\Photo';
protected $specialNames = [
16 => "_avatar_album",
32 => "_wall_album",
64 => "_saved_photos_album",
];
function getCoverURL(): ?string
public function getCoverURL(): ?string
{
$coverPhoto = $this->getCoverPhoto();
if(!$coverPhoto)
if (!$coverPhoto) {
return "/assets/packages/static/openvk/img/camera_200.png";
}
return $coverPhoto->getURL();
}
function getCoverPhoto(): ?Photo
public function getCoverPhoto(): ?Photo
{
$cover = $this->getRecord()->cover_photo;
if(!$cover) {
if (!$cover) {
$photos = iterator_to_array($this->getPhotos(1, 1));
$photo = $photos[0] ?? NULL;
if(!$photo || $photo->isDeleted())
return NULL;
else
$photo = $photos[0] ?? null;
if (!$photo || $photo->isDeleted()) {
return null;
} else {
return $photo;
}
}
return (new Photos)->get($cover);
return (new Photos())->get($cover);
}
function getPhotos(int $page = 1, ?int $perPage = NULL): \Traversable
public function getPhotos(int $page = 1, ?int $perPage = null): \Traversable
{
return $this->fetch($page, $perPage);
}
function getPhotosCount(): int
public function getPhotosCount(): int
{
return $this->size();
}
function addPhoto(Photo $photo): void
public function addPhoto(Photo $photo): void
{
$this->add($photo);
}
function removePhoto(Photo $photo): void
public function removePhoto(Photo $photo): void
{
$this->remove($photo);
}
function hasPhoto(Photo $photo): bool
public function hasPhoto(Photo $photo): bool
{
return $this->has($photo);
}
function canBeViewedBy(?User $user = NULL): bool
public function canBeViewedBy(?User $user = null): bool
{
if($this->isDeleted()) {
if ($this->isDeleted()) {
return false;
}
$owner = $this->getOwner();
if(get_class($owner) == "openvk\\Web\\Models\\Entities\\User") {
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
public function toVkApiStruct(?User $user = null, bool $need_covers = false, bool $photo_sizes = false): object
{
$res = (object) [];
@ -93,17 +99,17 @@ class Album extends MediaCollection
$res->title = $this->getName();
$res->description = $this->getDescription();
$res->created = $this->getCreationTime()->timestamp();
$res->updated = $this->getEditTime() ? $this->getEditTime()->timestamp() : NULL;
$res->updated = $this->getEditTime() ? $this->getEditTime()->timestamp() : null;
$res->size = $this->size();
$res->privacy_comment = 1;
$res->upload_by_admins_only = 1;
$res->comments_disabled = 0;
$res->can_upload = $this->canBeModifiedBy($user); # thisUser недоступен в entities
if($need_covers) {
if ($need_covers) {
$res->thumb_src = $this->getCoverURL();
if($photo_sizes) {
$res->sizes = !is_null($this->getCoverPhoto()) ? $this->getCoverPhoto()->getVkApiSizes() : NULL;
if ($photo_sizes) {
$res->sizes = !is_null($this->getCoverPhoto()) ? $this->getCoverPhoto()->getVkApiSizes() : null;
}
}

View file

@ -1,4 +1,7 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\RowModel;
@ -8,27 +11,28 @@ use openvk\Web\Models\Repositories\{Users, Clubs};
class Alias extends RowModel
{
protected $tableName = "aliases";
function getOwnerId(): int
public function getOwnerId(): int
{
return $this->getRecord()->owner_id;
}
function getType(): string
public function getType(): string
{
if ($this->getOwnerId() < 0)
if ($this->getOwnerId() < 0) {
return "club";
}
return "user";
}
function getUser(): ?User
public function getUser(): ?User
{
return (new Users)->get($this->getOwnerId());
return (new Users())->get($this->getOwnerId());
}
function getClub(): ?Club
public function getClub(): ?Club
{
return (new Clubs)->get($this->getOwnerId() * -1);
return (new Clubs())->get($this->getOwnerId() * -1);
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use Chandler\Database\DatabaseConnection;
use Nette\Utils\Image;
use Nette\Utils\UnknownImageFileException;
@ -10,8 +14,8 @@ use openvk\Web\Models\RowModel;
class Application extends RowModel
{
protected $tableName = "apps";
const PERMS = [
public const PERMS = [
"notify",
"friends",
"photos",
@ -31,44 +35,46 @@ class Application extends RowModel
"email",
"market",
];
private function getAvatarsDir(): string
{
$uploadSettings = OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"];
if($uploadSettings["mode"] === "server" && $uploadSettings["server"]["kind"] === "cdn")
if ($uploadSettings["mode"] === "server" && $uploadSettings["server"]["kind"] === "cdn") {
return $uploadSettings["server"]["directory"];
else
} else {
return OPENVK_ROOT . "/storage/";
}
}
function getId(): int
public function getId(): int
{
return $this->getRecord()->id;
}
function getOwner(): User
public function getOwner(): User
{
return (new Users)->get($this->getRecord()->owner);
return (new Users())->get($this->getRecord()->owner);
}
function getName(): string
public function getName(): string
{
return $this->getRecord()->name;
}
function getDescription(): string
public function getDescription(): string
{
return $this->getRecord()->description;
}
function getAvatarUrl(): string
public function getAvatarUrl(): string
{
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
if(is_null($this->getRecord()->avatar_hash))
if (is_null($this->getRecord()->avatar_hash)) {
return "$serverUrl/assets/packages/static/openvk/img/camera_200.png";
}
$hash = $this->getRecord()->avatar_hash;
switch(OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["mode"]) {
switch (OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["mode"]) {
default:
case "default":
case "basic":
@ -85,155 +91,168 @@ class Application extends RowModel
);
}
}
function getNote(): ?Note
public function getNote(): ?Note
{
if(!$this->getRecord()->news)
return NULL;
return (new Notes)->get($this->getRecord()->news);
if (!$this->getRecord()->news) {
return null;
}
return (new Notes())->get($this->getRecord()->news);
}
function getNoteLink(): string
public function getNoteLink(): string
{
$note = $this->getNote();
if(!$note)
if (!$note) {
return "";
}
return ovk_scheme(true) . $_SERVER["HTTP_HOST"] . "/note" . $note->getPrettyId();
}
function getBalance(): float
public function getBalance(): float
{
return $this->getRecord()->coins;
}
function getURL(): string
public function getURL(): string
{
return $this->getRecord()->address;
}
function getOrigin(): string
public function getOrigin(): string
{
$parsed = parse_url($this->getURL());
return (
($parsed["scheme"] ?? "https") . "://"
. ($parsed["host"] ?? "127.0.0.1") . ":"
. ($parsed["port"] ?? "443")
);
}
function getUsersCount(): int
public function getUsersCount(): int
{
$cx = DatabaseConnection::i()->getContext();
return sizeof($cx->table("app_users")->where("app", $this->getId()));
}
function getInstallationEntry(User $user): ?array
public function getInstallationEntry(User $user): ?array
{
$cx = DatabaseConnection::i()->getContext();
$entry = $cx->table("app_users")->where([
"app" => $this->getId(),
"user" => $user->getId(),
])->fetch();
if(!$entry)
return NULL;
if (!$entry) {
return null;
}
return $entry->toArray();
}
function getPermissions(User $user): array
public function getPermissions(User $user): array
{
$permMask = 0;
$installInfo = $this->getInstallationEntry($user);
if(!$installInfo)
if (!$installInfo) {
$this->install($user);
else
} else {
$permMask = $installInfo["access"];
$res = [];
for($i = 0; $i < sizeof(self::PERMS); $i++) {
$checkVal = 1 << $i;
if(($permMask & $checkVal) > 0)
$res[] = self::PERMS[$i];
}
$res = [];
for ($i = 0; $i < sizeof(self::PERMS); $i++) {
$checkVal = 1 << $i;
if (($permMask & $checkVal) > 0) {
$res[] = self::PERMS[$i];
}
}
return $res;
}
function isInstalledBy(User $user): bool
public function isInstalledBy(User $user): bool
{
return !is_null($this->getInstallationEntry($user));
}
function setNoteLink(?string $link): bool
public function setNoteLink(?string $link): bool
{
if(!$link) {
$this->stateChanges("news", NULL);
if (!$link) {
$this->stateChanges("news", null);
return true;
}
preg_match("%note([0-9]+)_([0-9]+)$%", $link, $matches);
if(sizeof($matches) != 3)
if (sizeof($matches) != 3) {
return false;
}
$owner = is_null($this->getRecord()) ? $this->changes["owner"] : $this->getRecord()->owner;
[, $ownerId, $vid] = $matches;
if($ownerId != $owner)
if ($ownerId != $owner) {
return false;
$note = (new Notes)->getNoteById((int) $ownerId, (int) $vid);
if(!$note)
}
$note = (new Notes())->getNoteById((int) $ownerId, (int) $vid);
if (!$note) {
return false;
}
$this->stateChanges("news", $note->getId());
return true;
}
function setAvatar(array $file): int
public function setAvatar(array $file): int
{
if($file["error"] !== UPLOAD_ERR_OK)
if ($file["error"] !== UPLOAD_ERR_OK) {
return -1;
}
try {
$image = Image::fromFile($file["tmp_name"]);
} catch (UnknownImageFileException $e) {
return -2;
}
$hash = hash_file("adler32", $file["tmp_name"]);
if(!is_dir($this->getAvatarsDir() . substr($hash, 0, 2)))
if(!mkdir($this->getAvatarsDir() . substr($hash, 0, 2)))
if (!is_dir($this->getAvatarsDir() . substr($hash, 0, 2))) {
if (!mkdir($this->getAvatarsDir() . substr($hash, 0, 2))) {
return -3;
}
}
$image->resize(140, 140, Image::STRETCH);
$image->save($this->getAvatarsDir() . substr($hash, 0, 2) . "/$hash" . "_app_avatar.png");
$this->stateChanges("avatar_hash", $hash);
return 0;
}
function setPermission(User $user, string $perm, bool $enabled): bool
public function setPermission(User $user, string $perm, bool $enabled): bool
{
$permMask = 0;
$installInfo = $this->getInstallationEntry($user);
if(!$installInfo)
if (!$installInfo) {
$this->install($user);
else
} else {
$permMask = $installInfo["access"];
}
$index = array_search($perm, self::PERMS);
if($index === false)
if ($index === false) {
return false;
}
$permVal = 1 << $index;
$permMask = $enabled ? ($permMask | $permVal) : ($permMask ^ $permVal);
$cx = DatabaseConnection::i()->getContext();
$cx->table("app_users")->where([
"app" => $this->getId(),
@ -241,30 +260,30 @@ class Application extends RowModel
])->update([
"access" => $permMask,
]);
return true;
}
function isEnabled(): bool
public function isEnabled(): bool
{
return (bool) $this->getRecord()->enabled;
}
function enable(): void
public function enable(): void
{
$this->stateChanges("enabled", 1);
$this->save();
}
function disable(): void
public function disable(): void
{
$this->stateChanges("enabled", 0);
$this->save();
}
function install(User $user): void
public function install(User $user): void
{
if(!$this->getInstallationEntry($user)) {
if (!$this->getInstallationEntry($user)) {
$cx = DatabaseConnection::i()->getContext();
$cx->table("app_users")->insert([
"app" => $this->getId(),
@ -272,8 +291,8 @@ class Application extends RowModel
]);
}
}
function uninstall(User $user): void
public function uninstall(User $user): void
{
$cx = DatabaseConnection::i()->getContext();
$cx->table("app_users")->where([
@ -281,39 +300,42 @@ class Application extends RowModel
"user" => $user->getId(),
])->delete();
}
function addCoins(float $coins): float
public function addCoins(float $coins): float
{
$res = $this->getBalance() + $coins;
$this->stateChanges("coins", $res);
$this->save();
return $res;
}
function withdrawCoins(): void
public function withdrawCoins(): void
{
$balance = $this->getBalance();
$tax = ($balance / 100) * OPENVK_ROOT_CONF["openvk"]["preferences"]["apps"]["withdrawTax"];
$owner = $this->getOwner();
$owner->setCoins($owner->getCoins() + ($balance - $tax));
$this->setCoins(0.0);
$this->save();
$owner->save();
}
function delete(bool $softly = true): void
public function delete(bool $softly = true): void
{
if($softly)
throw new \UnexpectedValueException("Can't delete apps softly."); // why
if ($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"); }
}
public function getPublicationTime(): string
{
return tr("recently");
}
}

View file

@ -1,38 +1,42 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\RowModel;
abstract class Attachable extends RowModel
{
function getId(): int
public function getId(): int
{
return $this->getRecord()->id;
}
function getParents(): \Traversable
public function getParents(): \Traversable
{
$sel = $this->getRecord()
->related("attachments.attachable_id")
->where("attachments.attachable_type", get_class($this));
foreach($sel as $rel) {
foreach ($sel as $rel) {
$repoName = $rel->target_type . "s";
$repoName = str_replace("Entities", "Repositories", $repoName);
$repo = new $repoName;
$repo = new $repoName();
yield $repo->get($rel->target_id);
}
}
/**
* Deletes together with all references.
*/
function delete(bool $softly = true): void
public function delete(bool $softly = true): void
{
$this->getRecord()
->related("attachments.attachable_id")
->where("attachments.attachable_type", get_class($this))
->delete();
parent::delete();
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?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;
@ -16,12 +20,12 @@ class Audio extends Media
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'
public 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 = [
public const vkGenres = [
"Rock" => 1,
"Pop" => 2,
"Rap" => 3,
@ -51,35 +55,40 @@ class Audio extends Media
private function fileLength(string $filename): int
{
if(!Shell::commandAvailable("ffmpeg") || !Shell::commandAvailable("ffprobe"))
if (!Shell::commandAvailable("ffmpeg") || !Shell::commandAvailable("ffprobe")) {
throw new \Exception();
}
$error = NULL;
$error = null;
$streams = Shell::ffprobe("-i", $filename, "-show_streams", "-select_streams a", "-loglevel error")->execute($error);
if($error !== 0)
if ($error !== 0) {
throw new \DomainException("$filename is not recognized as media container");
else if(empty($streams) || ctype_space($streams))
} elseif (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))
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)
if (sizeof($durations[1]) === 0) {
throw new \DomainException("$filename does not contain any meaningful audio streams");
}
$length = 0;
foreach($durations[1] as $duration) {
foreach ($durations[1] as $duration) {
$duration = floatval($duration);
if($duration < 1.0 || $duration > 65536.0)
if ($duration < 1.0 || $duration > 65536.0) {
throw new \DomainException("$filename does not contain any meaningful audio streams");
else
} else {
$length = max($length, $duration);
}
}
return (int) round($length, 0, PHP_ROUND_HALF_EVEN);
@ -116,7 +125,7 @@ class Audio extends Media
$ss,
];
if(Shell::isPowershell()) {
if (Shell::isPowershell()) {
Shell::powershell("-executionpolicy bypass", "-File", __DIR__ . "/../shell/processAudio.ps1", ...$args)
->start();
} else {
@ -126,58 +135,60 @@ class Audio extends Media
# Wait until processAudio will consume the file
$start = time();
while(file_exists($filename))
if(time() - $start > 5)
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);
}
} catch (UnknownCommandException $ucex) {
exit(OPENVK_ROOT_CONF["openvk"]["debug"] ? "bash/pwsh is not installed" : VIDEOS_FRIENDLY_ERROR);
}
return true;
}
function getTitle(): string
public function getTitle(): string
{
return $this->getRecord()->name;
}
function getPerformer(): string
public function getPerformer(): string
{
return $this->getRecord()->performer;
}
function getPerformers(): array
public function getPerformers(): array
{
return explode(", ", $this->getRecord()->performer);
}
function getName(): string
public function getName(): string
{
return $this->getPerformer() . "" . $this->getTitle();
}
function getDownloadName(): string
public function getDownloadName(): string
{
return preg_replace('/[\\/:*?"<>|]/', '_', str_replace(' ', '_', $this->getName()));
}
function getGenre(): ?string
public function getGenre(): ?string
{
return $this->getRecord()->genre;
}
function getLyrics(): ?string
public function getLyrics(): ?string
{
return !is_null($this->getRecord()->lyrics) ? htmlspecialchars($this->getRecord()->lyrics, ENT_DISALLOWED | ENT_XHTML) : NULL;
return !is_null($this->getRecord()->lyrics) ? htmlspecialchars($this->getRecord()->lyrics, ENT_DISALLOWED | ENT_XHTML) : null;
}
function getLength(): int
public function getLength(): int
{
return $this->getRecord()->length;
}
function getFormattedLength(): string
public function getFormattedLength(): string
{
$len = $this->getLength();
$mins = floor($len / 60);
@ -190,78 +201,83 @@ class Audio extends Media
);
}
function getSegmentSize(): float
public function getSegmentSize(): float
{
return $this->getRecord()->segment_size;
}
function getListens(): int
public function getListens(): int
{
return $this->getRecord()->listens;
}
function getOriginalURL(bool $force = false): string
public function getOriginalURL(bool $force = false): string
{
$disallowed = !OPENVK_ROOT_CONF["openvk"]["preferences"]["music"]["exposeOriginalURLs"] && !$force;
if(!$this->isAvailable() || $disallowed)
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
public function getURL(?bool $force = false): string
{
if ($this->isWithdrawn()) return "";
if ($this->isWithdrawn()) {
return "";
}
return parent::getURL();
}
function getKeys(): array
public function getKeys(): array
{
$keys[bin2hex($this->getRecord()->kid)] = bin2hex($this->getRecord()->key);
return $keys;
}
function isAnonymous(): bool
public function isAnonymous(): bool
{
return false;
}
function isExplicit(): bool
public function isExplicit(): bool
{
return (bool) $this->getRecord()->explicit;
}
function isWithdrawn(): bool
public function isWithdrawn(): bool
{
return (bool) $this->getRecord()->withdrawn;
}
function isUnlisted(): bool
public function isUnlisted(): bool
{
return (bool) $this->getRecord()->unlisted;
}
# NOTICE may flush model to DB if it was just processed
function isAvailable(): bool
public function isAvailable(): bool
{
if($this->getRecord()->processed)
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)
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")) {
if (file_exists("$fragments/$original")) {
# Original gets uploaded after fragments
$this->stateChanges("processed", 0x01);
@ -275,7 +291,7 @@ class Audio extends Media
return false;
}
function isInLibraryOf($entity): bool
public function isInLibraryOf($entity): bool
{
return sizeof(DatabaseConnection::i()->getContext()->table("audio_relations")->where([
"entity" => $entity->getId() * ($entity instanceof Club ? -1 : 1),
@ -283,15 +299,17 @@ class Audio extends Media
])) != 0;
}
function add($entity): bool
public function add($entity): bool
{
if($this->isInLibraryOf($entity))
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)
if (sizeof($audioRels->where("entity", $entityId)) > 65536) {
throw new \OverflowException("Can't have more than 65536 audios in a playlist");
}
$audioRels->insert([
"entity" => $entityId,
@ -301,10 +319,11 @@ class Audio extends Media
return true;
}
function remove($entity): bool
public function remove($entity): bool
{
if(!$this->isInLibraryOf($entity))
if (!$this->isInLibraryOf($entity)) {
return false;
}
DatabaseConnection::i()->getContext()->table("audio_relations")->where([
"entity" => $entity->getId() * ($entity instanceof Club ? -1 : 1),
@ -314,27 +333,27 @@ class Audio extends Media
return true;
}
function listen($entity, Playlist $playlist = NULL): bool
public 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())) {
if (!$lastListen || (time() - $lastListen->time >= $this->getLength())) {
$listensTable->insert([
"entity" => $entity->getRealId(),
"audio" => $this->getId(),
"time" => time(),
"playlist" => $playlist ? $playlist->getId() : NULL,
"playlist" => $playlist ? $playlist->getId() : null,
]);
if($entity instanceof User) {
if ($entity instanceof User) {
$this->stateChanges("listens", ($this->getListens() + 1));
$this->save();
if($playlist) {
if ($playlist) {
$playlist->incrementListens();
$playlist->save();
}
@ -380,7 +399,7 @@ class Audio extends Media
* @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
public function toVkApiStruct(?User $user = null, bool $forceURLExposure = false): object
{
$obj = (object) [];
$obj->unique_id = base64_encode((string) $this->getId());
@ -388,19 +407,21 @@ class Audio extends Media
$obj->artist = $this->getPerformer();
$obj->title = $this->getTitle();
$obj->duration = $this->getLength();
$obj->album_id = $obj->album = NULL; # i forgor to implement
$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)
if ($this->getOwner() instanceof Club) {
$obj->owner_id *= -1;
}
$obj->lyrics = NULL;
if(!is_null($this->getLyrics()))
$obj->lyrics = null;
if (!is_null($this->getLyrics())) {
$obj->lyrics = $this->getId();
}
$obj->added = $user && $this->isInLibraryOf($user);
$obj->editable = $user && $this->canBeModifiedBy($user);
@ -408,7 +429,7 @@ class Audio extends Media
$obj->explicit = $this->isExplicit();
$obj->withdrawn = $this->isWithdrawn();
$obj->ready = $this->isAvailable() && !$obj->withdrawn;
if($obj->ready) {
if ($obj->ready) {
$obj->url = $this->getOriginalURL($forceURLExposure);
$obj->manifest = $this->getURL();
$obj->keys = $this->getKeys();
@ -417,54 +438,62 @@ class Audio extends Media
return $obj;
}
function setOwner(int $oid): void
public function setOwner(int $oid): void
{
# WARNING: API implementation won't be able to handle groups like that, don't remove
if($oid <= 0)
if ($oid <= 0) {
throw new \OutOfRangeException("Only users can be owners of audio!");
}
$this->stateChanges("owner", $oid);
}
function setGenre(string $genre): void
public function setGenre(string $genre): void
{
if(!in_array($genre, Audio::genres)) {
$this->stateChanges("genre", NULL);
if (!in_array($genre, Audio::genres)) {
$this->stateChanges("genre", null);
return;
}
$this->stateChanges("genre", $genre);
}
function setCopyrightStatus(bool $withdrawn = true): void {
public function setCopyrightStatus(bool $withdrawn = true): void
{
$this->stateChanges("withdrawn", $withdrawn);
}
function setSearchability(bool $searchable = true): void {
public function setSearchability(bool $searchable = true): void
{
$this->stateChanges("unlisted", !$searchable);
}
function setToken(string $tok): void {
public function setToken(string $tok): void
{
throw new \LogicException("Changing keys is not supported.");
}
function setKid(string $kid): void {
public function setKid(string $kid): void
{
throw new \LogicException("Changing keys is not supported.");
}
function setKey(string $key): void {
public function setKey(string $key): void
{
throw new \LogicException("Changing keys is not supported.");
}
function setLength(int $len): void {
public function setLength(int $len): void
{
throw new \LogicException("Changing length is not supported.");
}
function setSegment_Size(int $len): void {
public function setSegment_Size(int $len): void
{
throw new \LogicException("Changing length is not supported.");
}
function delete(bool $softly = true): void
public function delete(bool $softly = true): void
{
$ctx = DatabaseConnection::i()->getContext();
$ctx->table("audio_relations")->where("audio", $this->getId())

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?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};
@ -9,58 +13,58 @@ class Ban extends RowModel
{
protected $tableName = "bans";
function getId(): int
public function getId(): int
{
return $this->getRecord()->id;
}
function getReason(): ?string
public function getReason(): ?string
{
return $this->getRecord()->reason;
}
function getUser(): ?User
public function getUser(): ?User
{
return (new Users)->get($this->getRecord()->user);
return (new Users())->get($this->getRecord()->user);
}
function getInitiator(): ?User
public function getInitiator(): ?User
{
return (new Users)->get($this->getRecord()->initiator);
return (new Users())->get($this->getRecord()->initiator);
}
function getStartTime(): int
public function getStartTime(): int
{
return $this->getRecord()->iat;
}
function getEndTime(): int
public function getEndTime(): int
{
return $this->getRecord()->exp;
}
function getTime(): int
public function getTime(): int
{
return $this->getRecord()->time;
}
function isPermanent(): bool
public function isPermanent(): bool
{
return $this->getEndTime() === 0;
}
function isRemovedManually(): bool
public function isRemovedManually(): bool
{
return (bool) $this->getRecord()->removed_manually;
}
function isOver(): bool
public function isOver(): bool
{
return $this->isRemovedManually();
}
function whoRemoved(): ?User
public function whoRemoved(): ?User
{
return (new Users)->get($this->getRecord()->removed_by);
return (new Users())->get($this->getRecord()->removed_by);
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Entities\{User};
use openvk\Web\Models\Repositories\{Users};
@ -10,39 +14,39 @@ class BannedLink extends RowModel
protected $tableName = "links_banned";
private $overrideContentColumn = "reason";
function getId(): int
public function getId(): int
{
return $this->getRecord()->id;
}
function getDomain(): string
public function getDomain(): string
{
return $this->getRecord()->domain;
}
function getReason(): string
public function getReason(): string
{
return $this->getRecord()->reason ?? tr("url_is_banned_default_reason");
}
function getInitiator(): ?User
public function getInitiator(): ?User
{
return (new Users)->get($this->getRecord()->initiator);
return (new Users())->get($this->getRecord()->initiator);
}
function getComment(): string
public function getComment(): string
{
return OPENVK_ROOT_CONF["openvk"]["preferences"]["susLinks"]["showReason"]
? tr("url_is_banned_comment_r", OPENVK_ROOT_CONF["openvk"]["appearance"]["name"], $this->getReason())
: tr("url_is_banned_comment", OPENVK_ROOT_CONF["openvk"]["appearance"]["name"]);
}
function getRegexpRule(): string
public function getRegexpRule(): string
{
return addslashes("/" . $this->getDomain() . $this->getRawRegexp() . "/");
return addslashes("/" . $this->getDomain() . $this->getRawRegexp() . "/");
}
function getRawRegexp(): string
public function getRawRegexp(): string
{
return $this->getRecord()->regexp_rule;
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Util\DateTime;
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Entities\{User, Manager};
@ -10,234 +14,250 @@ use Chandler\Security\User as ChandlerUser;
class Club extends RowModel
{
use Traits\TBackDrops;
use Traits\TSubscribable;
use Traits\TAudioStatuses;
use Traits\TIgnorable;
protected $tableName = "groups";
const TYPE_GROUP = 1;
const TYPE_PUBLIC = 1;
const TYPE_EVENT = 2;
const OPEN = 0;
const CLOSED = 1;
const PRIVATE = 2;
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
public const TYPE_GROUP = 1;
public const TYPE_PUBLIC = 1;
public const TYPE_EVENT = 2;
public const OPEN = 0;
public const CLOSED = 1;
public const PRIVATE = 2;
public const NOT_RELATED = 0;
public const SUBSCRIBED = 1;
public const REQUEST_SENT = 2;
public const WALL_CLOSED = 0;
public const WALL_OPEN = 1;
public const WALL_LIMITED = 2;
public function getId(): int
{
return $this->getRecord()->id;
}
function getAvatarPhoto(): ?Photo
public function getAvatarPhoto(): ?Photo
{
$avAlbum = (new Albums)->getClubAvatarAlbum($this);
$avAlbum = (new Albums())->getClubAvatarAlbum($this);
$avCount = $avAlbum->getPhotosCount();
$avPhotos = $avAlbum->getPhotos($avCount, 1);
return iterator_to_array($avPhotos)[0] ?? NULL;
return iterator_to_array($avPhotos)[0] ?? null;
}
function getAvatarUrl(string $size = "miniscule", $avPhoto = NULL): string
public function getAvatarUrl(string $size = "miniscule", $avPhoto = null): string
{
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
if(!$avPhoto)
if (!$avPhoto) {
$avPhoto = $this->getAvatarPhoto();
}
return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURLBySizeId($size);
}
function getWallType(): int
public function getWallType(): int
{
return $this->getRecord()->wall;
}
function getAvatarLink(): string
public function getAvatarLink(): string
{
$avPhoto = $this->getAvatarPhoto();
if(!$avPhoto) return "javascript:void(0)";
if (!$avPhoto) {
return "javascript:void(0)";
}
$pid = $avPhoto->getPrettyId();
$aid = (new Albums)->getClubAvatarAlbum($this)->getId();
$aid = (new Albums())->getClubAvatarAlbum($this)->getId();
return "/photo$pid?from=album$aid";
}
function getURL(): string
public function getURL(): string
{
if(!is_null($this->getShortCode()))
if (!is_null($this->getShortCode())) {
return "/" . $this->getShortCode();
else
} else {
return "/club" . $this->getId();
}
}
function getName(): string
public function getName(): string
{
return $this->getRecord()->name;
}
function getCanonicalName(): string
public function getCanonicalName(): string
{
return $this->getName();
}
function getOwner(): ?User
public function getOwner(): ?User
{
return (new Users)->get($this->getRecord()->owner);
return (new Users())->get($this->getRecord()->owner);
}
function getOwnerComment(): string
public function getOwnerComment(): string
{
return is_null($this->getRecord()->owner_comment) ? "" : $this->getRecord()->owner_comment;
}
function isOwnerHidden(): bool
public function isOwnerHidden(): bool
{
return (bool) $this->getRecord()->owner_hidden;
}
function isOwnerClubPinned(): bool
public function isOwnerClubPinned(): bool
{
return (bool) $this->getRecord()->owner_club_pinned;
}
function getDescription(): ?string
public function getDescription(): ?string
{
return $this->getRecord()->about;
}
function getDescriptionHtml(): ?string
public function getDescriptionHtml(): ?string
{
if(!is_null($this->getDescription()))
if (!is_null($this->getDescription())) {
return nl2br(htmlspecialchars($this->getDescription(), ENT_DISALLOWED | ENT_XHTML));
else
return NULL;
} else {
return null;
}
}
function getShortCode(): ?string
public function getShortCode(): ?string
{
return $this->getRecord()->shortcode;
}
function getBanReason(): ?string
public function getBanReason(): ?string
{
return $this->getRecord()->block_reason;
}
function getOpennesStatus(): int
public function getOpennesStatus(): int
{
return $this->getRecord()->closed;
}
function getAdministratorsListDisplay(): int
public function getAdministratorsListDisplay(): int
{
return $this->getRecord()->administrators_list_display;
}
function isEveryoneCanCreateTopics(): bool
public function isEveryoneCanCreateTopics(): bool
{
return (bool) $this->getRecord()->everyone_can_create_topics;
}
function isDisplayTopicsAboveWallEnabled(): bool
public function isDisplayTopicsAboveWallEnabled(): bool
{
return (bool) $this->getRecord()->display_topics_above_wall;
}
function isHideFromGlobalFeedEnabled(): bool
public function isHideFromGlobalFeedEnabled(): bool
{
return (bool) $this->getRecord()->hide_from_global_feed;
}
function isHidingFromGlobalFeedEnforced(): bool
public function isHidingFromGlobalFeedEnforced(): bool
{
return (bool) $this->getRecord()->enforce_hiding_from_global_feed;
}
function getType(): int
public function getType(): int
{
return $this->getRecord()->type;
}
function isVerified(): bool
public function isVerified(): bool
{
return (bool) $this->getRecord()->verified;
}
function isBanned(): bool
public function isBanned(): bool
{
return !is_null($this->getBanReason());
}
function canPost(): bool
public function canPost(): bool
{
return (bool) $this->getRecord()->wall;
return (bool) $this->getRecord()->wall;
}
function setShortCode(?string $code = NULL): ?bool
public function setShortCode(?string $code = null): ?bool
{
if(!is_null($code)) {
if(!preg_match("%^[a-z][a-z0-9\\.\\_]{0,30}[a-z0-9]$%", $code))
if (!is_null($code)) {
if (!preg_match("%^[a-z][a-z0-9\\.\\_]{0,30}[a-z0-9]$%", $code)) {
return false;
if(in_array($code, OPENVK_ROOT_CONF["openvk"]["preferences"]["shortcodes"]["forbiddenNames"]))
}
if (in_array($code, OPENVK_ROOT_CONF["openvk"]["preferences"]["shortcodes"]["forbiddenNames"])) {
return false;
if(\Chandler\MVC\Routing\Router::i()->getMatchingRoute("/$code")[0]->presenter !== "UnknownTextRouteStrategy")
}
if (\Chandler\MVC\Routing\Router::i()->getMatchingRoute("/$code")[0]->presenter !== "UnknownTextRouteStrategy") {
return false;
}
$pUser = DB::i()->getContext()->table("profiles")->where("shortcode", $code)->fetch();
if(!is_null($pUser))
if (!is_null($pUser)) {
return false;
}
}
$this->stateChanges("shortcode", $code);
return true;
}
function setWall(int $type)
public function setWall(int $type)
{
if($type > 2 || $type < 0)
if ($type > 2 || $type < 0) {
throw new \LogicException("Invalid wall");
}
$this->stateChanges("wall", $type);
}
function isSubscriptionAccepted(User $user): bool
public function isSubscriptionAccepted(User $user): bool
{
return !is_null($this->getRecord()->related("subscriptions.follower")->where([
"follower" => $this->getId(),
"target" => $user->getId(),
])->fetch());;
])->fetch());
;
}
function getPostViewStats(bool $unique = false): ?array
public function getPostViewStats(bool $unique = false): ?array
{
$edb = eventdb();
if(!$edb)
return NULL;
if (!$edb) {
return null;
}
$subs = [];
$viral = [];
$total = [];
for($i = 1; $i < 8; $i++) {
for ($i = 1; $i < 8; $i++) {
$begin = strtotime("-" . $i . "day midnight");
$end = $i === 1 ? time() + 10 : strtotime("-" . ($i - 1) . "day midnight");
$query = "SELECT COUNT(" . ($unique ? "DISTINCT profile" : "*") . ") AS cnt FROM postViews";
$query .= " WHERE `group`=1 AND owner=" . $this->getId();
$query .= " AND timestamp > $begin AND timestamp < $end";
$sub = $edb->getConnection()->query("$query AND NOT subscribed=0")->fetch()->cnt;
$vir = $edb->getConnection()->query("$query AND subscribed=0")->fetch()->cnt;
$subs[] = $sub;
$viral[] = $vir;
$total[] = $sub + $vir;
}
return [
"total" => [
"x" => array_reverse(range(1, 7)),
@ -273,96 +293,105 @@ class Club extends RowModel
],
];
}
function getSubscriptionStatus(User $user): bool
public function getSubscriptionStatus(User $user): bool
{
$subbed = !is_null($this->getRecord()->related("subscriptions.target")->where([
"target" => $this->getId(),
"model" => static::class,
"follower" => $user->getId(),
])->fetch());
return $subbed && ($this->getOpennesStatus() === static::CLOSED ? $this->isSubscriptionAccepted($user) : true);
}
function getFollowersQuery(string $sort = "follower ASC"): GroupedSelection
public function getFollowersQuery(string $sort = "follower ASC"): GroupedSelection
{
$query = $this->getRecord()->related("subscriptions.target");
if($this->getOpennesStatus() === static::OPEN) {
if ($this->getOpennesStatus() === static::OPEN) {
$query = $query->where("model", "openvk\\Web\\Models\\Entities\\Club")->order($sort);
} else {
return false;
}
return $query->group("follower");
}
function getFollowersCount(): int
public function getFollowersCount(): int
{
return sizeof($this->getFollowersQuery());
}
function getFollowers(int $page = 1, int $perPage = 6, string $sort = "follower ASC"): \Traversable
public function getFollowers(int $page = 1, int $perPage = 6, string $sort = "follower ASC"): \Traversable
{
$rels = $this->getFollowersQuery($sort)->page($page, $perPage);
foreach($rels as $rel) {
$rel = (new Users)->get($rel->follower);
if(!$rel) continue;
foreach ($rels as $rel) {
$rel = (new Users())->get($rel->follower);
if (!$rel) {
continue;
}
yield $rel;
}
}
function getSuggestedPostsCount(User $user = NULL)
public function getSuggestedPostsCount(User $user = null)
{
$count = 0;
if(is_null($user))
return NULL;
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());
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
public function getManagers(int $page = 1, bool $ignoreHidden = false): \Traversable
{
$rels = $this->getRecord()->related("group_coadmins.club")->page($page, 6);
if($ignoreHidden)
if ($ignoreHidden) {
$rels = $rels->where("hidden", false);
foreach($rels as $rel) {
$rel = (new Managers)->get($rel->id);
if(!$rel) continue;
}
foreach ($rels as $rel) {
$rel = (new Managers())->get($rel->id);
if (!$rel) {
continue;
}
yield $rel;
}
}
function getManager(User $user, bool $ignoreHidden = false): ?Manager
public function getManager(User $user, bool $ignoreHidden = false): ?Manager
{
$manager = (new Managers)->getByUserAndClub($user->getId(), $this->getId());
$manager = (new Managers())->getByUserAndClub($user->getId(), $this->getId());
if ($ignoreHidden && $manager !== NULL && $manager->isHidden())
return NULL;
if ($ignoreHidden && $manager !== null && $manager->isHidden()) {
return null;
}
return $manager;
}
function getManagersCount(bool $ignoreHidden = false): int
public function getManagersCount(bool $ignoreHidden = false): int
{
if($ignoreHidden)
if ($ignoreHidden) {
return sizeof($this->getRecord()->related("group_coadmins.club")->where("hidden", false)) + (int) !$this->isOwnerHidden();
}
return sizeof($this->getRecord()->related("group_coadmins.club")) + 1;
}
function addManager(User $user, ?string $comment = NULL): void
public function addManager(User $user, ?string $comment = null): void
{
DB::i()->getContext()->table("group_coadmins")->insert([
"club" => $this->getId(),
@ -370,103 +399,107 @@ class Club extends RowModel
"comment" => $comment,
]);
}
function removeManager(User $user): void
public function removeManager(User $user): void
{
DB::i()->getContext()->table("group_coadmins")->where([
"club" => $this->getId(),
"user" => $user->getId(),
])->delete();
}
function canBeModifiedBy(User $user): bool
public function canBeModifiedBy(User $user): bool
{
$id = $user->getId();
if($this->getOwner()->getId() === $id)
if ($this->getOwner()->getId() === $id) {
return true;
}
return !is_null($this->getRecord()->related("group_coadmins.club")->where("user", $id)->fetch());
}
function getWebsite(): ?string
{
return $this->getRecord()->website;
}
public function getWebsite(): ?string
{
return $this->getRecord()->website;
}
function ban(string $reason): void
public function ban(string $reason): void
{
$this->setBlock_Reason($reason);
$this->save();
}
function unban(): void
public function unban(): void
{
$this->setBlock_Reason(null);
$this->save();
}
function canBeViewedBy(?User $user = NULL)
public function canBeViewedBy(?User $user = null)
{
return is_null($this->getBanReason());
}
function getAlert(): ?string
public function getAlert(): ?string
{
return $this->getRecord()->alert;
}
function getRealId(): int
public function getRealId(): int
{
return $this->getId() * -1;
}
function isEveryoneCanUploadAudios(): bool
public function isEveryoneCanUploadAudios(): bool
{
return (bool) $this->getRecord()->everyone_can_upload_audios;
}
function canUploadAudio(?User $user): bool
public function canUploadAudio(?User $user): bool
{
if(!$user)
return NULL;
if (!$user) {
return null;
}
return $this->isEveryoneCanUploadAudios() || $this->canBeModifiedBy($user);
}
function canUploadDocs(?User $user): bool
public function canUploadDocs(?User $user): bool
{
if(!$user)
if (!$user) {
return false;
}
return $this->canBeModifiedBy($user);
}
function getAudiosCollectionSize()
public function getAudiosCollectionSize()
{
return (new \openvk\Web\Models\Repositories\Audios)->getClubCollectionSize($this);
return (new \openvk\Web\Models\Repositories\Audios())->getClubCollectionSize($this);
}
function toVkApiStruct(?User $user = NULL, string $fields = ''): object
public function toVkApiStruct(?User $user = null, string $fields = ''): object
{
$res = (object) [];
$res->id = $this->getId();
$res->name = $this->getName();
$res->screen_name = $this->getShortCode() ?? "club".$this->getId();
$res->screen_name = $this->getShortCode() ?? "club" . $this->getId();
$res->is_closed = false;
$res->type = 'group';
$res->is_member = $user ? (int)$this->getSubscriptionStatus($user) : 0;
$res->deactivated = NULL;
$res->is_member = $user ? (int) $this->getSubscriptionStatus($user) : 0;
$res->deactivated = null;
$res->can_access_closed = true;
if(!is_array($fields))
if (!is_array($fields)) {
$fields = explode(',', $fields);
}
$avatar_photo = $this->getAvatarPhoto();
foreach($fields as $field) {
switch($field) {
foreach ($fields as $field) {
switch ($field) {
case 'verified':
$res->verified = (int)$this->isVerified();
$res->verified = (int) $this->isVerified();
break;
case 'site':
$res->site = $this->getWebsite();
@ -500,9 +533,4 @@ class Club extends RowModel
return $res;
}
use Traits\TBackDrops;
use Traits\TSubscribable;
use Traits\TAudioStatuses;
use Traits\TIgnorable;
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\Repositories\Clubs;
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Entities\{Note};
@ -8,61 +12,64 @@ class Comment extends Post
{
protected $tableName = "comments";
protected $upperNodeReferenceColumnName = "owner";
function getPrettyId(): string
public function getPrettyId(): string
{
return (string)$this->getRecord()->id;
return (string) $this->getRecord()->id;
}
function getVirtualId(): int
public function getVirtualId(): int
{
return 0;
}
function getTarget(): ?Postable
public function getTarget(): ?Postable
{
$entityClassName = $this->getRecord()->model;
$repoClassName = str_replace("Entities", "Repositories", $entityClassName) . "s";
$entity = (new $repoClassName)->get($this->getRecord()->target);
$entity = (new $repoClassName())->get($this->getRecord()->target);
return $entity;
}
function getPageURL(): string
public function getPageURL(): string
{
return '#';
}
/**
* May return fake owner (group), if flags are [1, (*)]
*
*
* @param bool $honourFlags - check flags
*/
function getOwner(bool $honourFlags = true, bool $real = false): RowModel
public function getOwner(bool $honourFlags = true, bool $real = false): RowModel
{
if($honourFlags && $this->isPostedOnBehalfOfGroup()) {
if($this->getTarget() instanceof Post)
return (new Clubs)->get(abs($this->getTarget()->getTargetWall()));
if ($honourFlags && $this->isPostedOnBehalfOfGroup()) {
if ($this->getTarget() instanceof Post) {
return (new Clubs())->get(abs($this->getTarget()->getTargetWall()));
}
if($this->getTarget() instanceof Topic)
if ($this->getTarget() instanceof Topic) {
return $this->getTarget()->getClub();
}
}
return parent::getOwner($honourFlags, $real);
}
function canBeDeletedBy(User $user = NULL): bool
public function canBeDeletedBy(User $user = null): bool
{
if(!$user)
if (!$user) {
return false;
}
return $this->getOwner()->getId() == $user->getId() ||
$this->getTarget()->getOwner()->getId() == $user->getId() ||
$this->getTarget() instanceof Post && $this->getTarget()->getTargetWall() < 0 && (new Clubs)->get(abs($this->getTarget()->getTargetWall()))->canBeModifiedBy($user) ||
$this->getTarget() instanceof Post && $this->getTarget()->getTargetWall() < 0 && (new Clubs())->get(abs($this->getTarget()->getTargetWall()))->canBeModifiedBy($user) ||
$this->getTarget() instanceof Topic && $this->getTarget()->canBeModifiedBy($user);
}
function toVkApiStruct(?User $user = NULL, bool $need_likes = false, bool $extended = false, ?Note $note = NULL): object
public function toVkApiStruct(?User $user = null, bool $need_likes = false, bool $extended = false, ?Note $note = null): object
{
$res = (object) [];
@ -72,93 +79,97 @@ class Comment extends Post
$res->text = $this->getText(false);
$res->attachments = [];
$res->parents_stack = [];
if(!is_null($note)) {
if (!is_null($note)) {
$res->uid = $this->getOwner()->getId();
$res->nid = $note->getId();
$res->oid = $note->getOwner()->getId();
}
foreach($this->getChildren() as $attachment) {
if($attachment->isDeleted())
foreach ($this->getChildren() as $attachment) {
if ($attachment->isDeleted()) {
continue;
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
}
if ($attachment instanceof \openvk\Web\Models\Entities\Photo) {
$res->attachments[] = $attachment->toVkApiStruct();
} else if($attachment instanceof \openvk\Web\Models\Entities\Video) {
} elseif ($attachment instanceof \openvk\Web\Models\Entities\Video) {
$res->attachments[] = $attachment->toVkApiStruct($this->getUser());
}
}
if($need_likes) {
if ($need_likes) {
$res->count = $this->getLikesCount();
$res->user_likes = (int)$this->hasLikeFrom($user);
$res->user_likes = (int) $this->hasLikeFrom($user);
$res->can_like = 1;
}
return $res;
}
function getURL(): string
public function getURL(): string
{
return "/wall" . $this->getTarget()->getPrettyId() . "#_comment" . $this->getId();
}
function canBeViewedBy(?User $user = NULL): bool
public function canBeViewedBy(?User $user = null): bool
{
if($this->isDeleted() || $this->getTarget()->isDeleted()) {
if ($this->isDeleted() || $this->getTarget()->isDeleted()) {
return false;
}
return $this->getTarget()->canBeViewedBy($user);
}
function isFromPostAuthor($target = NULL)
public function isFromPostAuthor($target = null)
{
if(!$target)
if (!$target) {
$target = $this->getTarget();
}
$target_owner = $target->getOwner();
$comment_owner = $this->getOwner();
if($target_owner->getRealId() === $comment_owner->getRealId())
if ($target_owner->getRealId() === $comment_owner->getRealId()) {
return true;
}
# TODO: make it work with signer_id
return false;
}
function toNotifApiStruct()
public function toNotifApiStruct()
{
$res = (object)[];
$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
$res->post = null; # todo
return $res;
}
function canBeEditedBy(?User $user = NULL): bool
public function canBeEditedBy(?User $user = null): bool
{
if(!$user)
if (!$user) {
return false;
}
return $user->getId() == $this->getOwner(false)->getId();
}
function getTargetURL(): string
public function getTargetURL(): string
{
$target = $this->getTarget();
$target_name = 'wall';
if(!$target) {
if (!$target) {
return '/404';
}
switch(get_class($target)) {
switch (get_class($target)) {
case 'openvk\Web\Models\Entities\Note':
$target_name = 'note';
break;

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use Chandler\Database\DatabaseConnection;
use Chandler\Signaling\SignalManager;
use Chandler\Security\Authenticator;
@ -12,7 +16,7 @@ use Nette\Database\Table\ActiveRow;
/**
* A repository of messages sent between correspondents.
*
*
* A pseudo-repository that operates with messages
* sent between users.
*/
@ -26,136 +30,141 @@ class Correspondence
* @var \Nette\Database\Table\Selection Messages table
*/
private $messages;
const CAP_BEHAVIOUR_END_MESSAGE_ID = 1;
const CAP_BEHAVIOUR_START_MESSAGE_ID = 2;
public const CAP_BEHAVIOUR_END_MESSAGE_ID = 1;
public const CAP_BEHAVIOUR_START_MESSAGE_ID = 2;
/**
* Correspondence constructor.
*
*
* Requires two users/clubs to construct.
*
*
* @param $correspondent - first correspondent
* @param $anotherCorrespondents - another correspondent
*/
function __construct(RowModel $correspondent, RowModel $anotherCorrespondent)
public function __construct(RowModel $correspondent, RowModel $anotherCorrespondent)
{
$this->correspondents = [$correspondent, $anotherCorrespondent];
$this->messages = DatabaseConnection::i()->getContext()->table("messages");
}
/**
* Get /im?sel url.
*
*
* @returns string - URL
*/
function getURL(): string
public function getURL(): string
{
$id = $this->correspondents[1]->getId();
$id = get_class($this->correspondents[1]) === 'openvk\Web\Models\Entities\Club' ? $id * -1 : $id;
return "/im?sel=$id";
}
function getID(): int
public function getID(): int
{
$id = $this->correspondents[1]->getId();
$id = get_class($this->correspondents[1]) === 'openvk\Web\Models\Entities\Club' ? $id * -1 : $id;
return $id;
}
/**
* Get correspondents as array.
*
*
* @returns RowModel[] Array of correspondents (usually two)
*/
function getCorrespondents(): array
public function getCorrespondents(): array
{
return $this->correspondents;
}
/**
* Fetch messages.
*
*
* Fetch messages on per page basis.
*
*
* @param $cap - page (defaults to first)
* @param $limit - messages per page (defaults to default per page count)
* @returns \Traversable - iterable messages cursor
*/
function getMessages(int $capBehavior = 1, ?int $cap = NULL, ?int $limit = NULL, ?int $padding = NULL, bool $reverse = false): array
public function getMessages(int $capBehavior = 1, ?int $cap = null, ?int $limit = null, ?int $padding = null, bool $reverse = false): array
{
$query = file_get_contents(__DIR__ . "/../sql/get-messages.tsql");
$params = [
[get_class($this->correspondents[0]), get_class($this->correspondents[1])],
[$this->correspondents[0]->getId(), $this->correspondents[1]->getId()],
[$limit ?? OPENVK_DEFAULT_PER_PAGE]
[$limit ?? OPENVK_DEFAULT_PER_PAGE],
];
$params = array_merge($params[0], $params[1], array_reverse($params[0]), array_reverse($params[1]), $params[2]);
if ($limit === NULL)
DatabaseConnection::i()->getConnection()->query("UPDATE messages SET unread = 0 WHERE sender_id = ".$this->correspondents[1]->getId());
if(is_null($cap)) {
if ($limit === null) {
DatabaseConnection::i()->getConnection()->query("UPDATE messages SET unread = 0 WHERE sender_id = " . $this->correspondents[1]->getId());
}
if (is_null($cap)) {
$query = str_replace("\n AND (`id` > ?)", "", $query);
} else {
if($capBehavior === 1)
if ($capBehavior === 1) {
$query = str_replace("\n AND (`id` > ?)", "\n AND (`id` < ?)", $query);
}
array_unshift($params, $cap);
}
if(is_null($padding))
if (is_null($padding)) {
$query = str_replace("\nOFFSET\n?", "", $query);
else
} else {
$params[] = $padding;
if($reverse)
}
if ($reverse) {
$query = str_replace("`created` DESC", "`created` ASC", $query);
}
$msgs = DatabaseConnection::i()->getConnection()->query($query, ...$params);
$msgs = array_map(function($message) {
$msgs = array_map(function ($message) {
$message = new ActiveRow((array) $message, $this->messages); #Directly creating ActiveRow is faster than making query
return new Message($message);
}, iterator_to_array($msgs));
return $msgs;
}
/**
* Get last message from correspondence.
*
*
* @returns Message|null - message, if any
*/
function getPreviewMessage(): ?Message
public function getPreviewMessage(): ?Message
{
$messages = $this->getMessages(1, NULL, 1, 0);
return $messages[0] ?? NULL;
$messages = $this->getMessages(1, null, 1, 0);
return $messages[0] ?? null;
}
/**
* Send message.
*
*
* @deprecated
* @returns Message|false - resulting message, or false in case of non-successful transaction
*/
function sendMessage(Message $message, bool $dontReverse = false)
public function sendMessage(Message $message, bool $dontReverse = false)
{
if(!$dontReverse) {
$user = (new Users)->getByChandlerUser(Authenticator::i()->getUser());
if(!$user)
if (!$dontReverse) {
$user = (new Users())->getByChandlerUser(Authenticator::i()->getUser());
if (!$user) {
return false;
}
}
$ids = [$this->correspondents[0]->getId(), $this->correspondents[1]->getId()];
$classes = [get_class($this->correspondents[0]), get_class($this->correspondents[1])];
if(!$dontReverse && $ids[1] === $user->getId()) {
if (!$dontReverse && $ids[1] === $user->getId()) {
$ids = array_reverse($ids);
$classes = array_reverse($classes);
}
$message->setSender_Id($ids[0]);
$message->setRecipient_Id($ids[1]);
$message->setSender_Type($classes[0]);
@ -163,15 +172,15 @@ class Correspondence
$message->setCreated(time());
$message->setUnread(1);
$message->save();
DatabaseConnection::i()->getConnection()->query("UPDATE messages SET unread = 0 WHERE sender_id = ".$this->correspondents[1]->getId());
DatabaseConnection::i()->getConnection()->query("UPDATE messages SET unread = 0 WHERE sender_id = " . $this->correspondents[1]->getId());
# да
if($ids[0] !== $ids[1]) {
if ($ids[0] !== $ids[1]) {
$event = new NewMessageEvent($message);
(SignalManager::i())->triggerEvent($event, $ids[1]);
}
return $message;
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\Repositories\{Clubs, Users, Photos};
use openvk\Web\Models\Entities\{Photo};
use openvk\Web\Models\RowModel;
@ -10,45 +14,46 @@ class Document extends Media
{
protected $tableName = "documents";
protected $fileExtension = "gif";
private $tmp_format = NULL;
private $tmp_format = null;
const VKAPI_TYPE_TEXT = 1;
const VKAPI_TYPE_ARCHIVE = 2;
const VKAPI_TYPE_GIF = 3;
const VKAPI_TYPE_IMAGE = 4;
const VKAPI_TYPE_AUDIO = 5;
const VKAPI_TYPE_VIDEO = 6;
const VKAPI_TYPE_BOOKS = 7;
const VKAPI_TYPE_UNKNOWN = 8;
public const VKAPI_TYPE_TEXT = 1;
public const VKAPI_TYPE_ARCHIVE = 2;
public const VKAPI_TYPE_GIF = 3;
public const VKAPI_TYPE_IMAGE = 4;
public const VKAPI_TYPE_AUDIO = 5;
public const VKAPI_TYPE_VIDEO = 6;
public const VKAPI_TYPE_BOOKS = 7;
public const VKAPI_TYPE_UNKNOWN = 8;
const VKAPI_FOLDER_PRIVATE = 0;
const VKAPI_FOLDER_STUDY = 1;
const VKAPI_FOLDER_BOOK = 2;
const VKAPI_FOLDER_PUBLIC = 3;
public const VKAPI_FOLDER_PRIVATE = 0;
public const VKAPI_FOLDER_STUDY = 1;
public const VKAPI_FOLDER_BOOK = 2;
public const VKAPI_FOLDER_PUBLIC = 3;
protected function pathFromHash(string $hash): string
{
$dir = $this->getBaseDir() . substr($hash, 0, 2);
if(!is_dir($dir))
if (!is_dir($dir)) {
mkdir($dir);
}
return "$dir/$hash." . $this->getFileExtension();
}
function getURL(): string
public function getURL(): string
{
$hash = $this->getRecord()->hash;
$filetype = $this->getFileExtension();
switch(OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["mode"]) {
switch (OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["mode"]) {
default:
case "default":
case "basic":
return "http://" . $_SERVER['HTTP_HOST'] . "/blob_" . substr($hash, 0, 2) . "/$hash.$filetype";
break;
break;
case "accelerated":
return "http://" . $_SERVER['HTTP_HOST'] . "/openvk-datastore/$hash.$filetype";
break;
break;
case "server":
$settings = (object) OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["server"];
return (
@ -57,7 +62,7 @@ class Document extends Media
$settings->path .
substr($hash, 0, 2) . "/$hash.$filetype"
);
break;
break;
}
}
@ -69,7 +74,7 @@ class Document extends Media
protected function makePreview(string $tmp_name, string $filename, int $owner): bool
{
$preview_photo = new Photo;
$preview_photo = new Photo();
$preview_photo->setOwner($owner);
$preview_photo->setDescription("internal use");
$preview_photo->setCreated(time());
@ -79,7 +84,7 @@ class Document extends Media
"error" => 0,
]);
$preview_photo->save();
$this->stateChanges("preview", "photo_".$preview_photo->getId());
$this->stateChanges("preview", "photo_" . $preview_photo->getId());
return true;
}
@ -90,25 +95,29 @@ class Document extends Media
return true;
}
function setFile(array $file): void
public function setFile(array $file): void
{
if($file["error"] !== UPLOAD_ERR_OK)
if ($file["error"] !== UPLOAD_ERR_OK) {
throw new ISE("File uploaded is corrupted");
}
$original_name = $file["name"];
$file_format = end(explode(".", $original_name));
$file_size = $file["size"];
$type = Document::detectTypeByFormat($file_format);
if(!$file_format)
if (!$file_format) {
throw new \TypeError("No file format");
}
if(!in_array(mb_strtolower($file_format), OPENVK_ROOT_CONF["openvk"]["preferences"]["docs"]["allowedFormats"]))
if (!in_array(mb_strtolower($file_format), OPENVK_ROOT_CONF["openvk"]["preferences"]["docs"]["allowedFormats"])) {
throw new \TypeError("Forbidden file format");
}
if($file_size < 1 || $file_size > (OPENVK_ROOT_CONF["openvk"]["preferences"]["docs"]["maxSize"] * 1024 * 1024))
if ($file_size < 1 || $file_size > (OPENVK_ROOT_CONF["openvk"]["preferences"]["docs"]["maxSize"] * 1024 * 1024)) {
throw new \ValueError("Invalid filesize");
}
$hash = hash_file("whirlpool", $file["tmp_name"]);
$this->stateChanges("original_name", ovk_proc_strtr($original_name, 255));
@ -119,76 +128,78 @@ class Document extends Media
$this->stateChanges("access_key", bin2hex(random_bytes(9)));
$this->stateChanges("type", $type);
if(in_array($type, [3, 4])) {
if (in_array($type, [3, 4])) {
$this->makePreview($file["tmp_name"], $original_name, $file["preview_owner"]);
}
$this->saveFile($file["tmp_name"], $hash);
}
function hasPreview(): bool
public function hasPreview(): bool
{
return $this->getRecord()->preview != NULL;
return $this->getRecord()->preview != null;
}
function isOwnerHidden(): bool
public function isOwnerHidden(): bool
{
return (bool) $this->getRecord()->owner_hidden;
}
function isCopy(): bool
public function isCopy(): bool
{
return $this->getRecord()->copy_of != NULL;
return $this->getRecord()->copy_of != null;
}
function isLicensed(): bool
public function isLicensed(): bool
{
return false;
}
function isUnsafe(): bool
public function isUnsafe(): bool
{
return false;
}
function isAnonymous(): bool
public function isAnonymous(): bool
{
return false;
}
function isPrivate(): bool
public function isPrivate(): bool
{
return $this->getFolder() == Document::VKAPI_FOLDER_PRIVATE;
}
function isImage(): bool
public function isImage(): bool
{
return in_array($this->getVKAPIType(), [3, 4]);
}
function isBook(): bool
public function isBook(): bool
{
return in_array($this->getFileExtension(), ["pdf"]);
}
function isAudio(): bool
public function isAudio(): bool
{
return in_array($this->getVKAPIType(), [Document::VKAPI_TYPE_AUDIO]);
}
function isGif(): bool
public function isGif(): bool
{
return $this->getVKAPIType() == 3;
}
function isCopiedBy($user = NULL): bool
public function isCopiedBy($user = null): bool
{
if(!$user)
if (!$user) {
return false;
}
if($user->getRealId() === $this->getOwnerID())
if ($user->getRealId() === $this->getOwnerID()) {
return true;
}
return DatabaseConnection::i()->getContext()->table("documents")->where([
"owner" => $user->getRealId(),
"copy_of" => $this->getId(),
@ -196,19 +207,19 @@ class Document extends Media
])->count() > 0;
}
function copy(User $user): Document
public function copy(User $user): Document
{
$item = DatabaseConnection::i()->getContext()->table("documents")->where([
"owner" => $user->getId(),
"copy_of" => $this->getId(),
]);
if($item->count() > 0) {
if ($item->count() > 0) {
$older = new Document($item->fetch());
}
$this_document_array = $this->getRecord()->toArray();
$new_document = new Document;
$new_document = new Document();
$new_document->setOwner($user->getId());
$new_document->updateHash($this_document_array["hash"]);
$new_document->setOwner_hidden(1);
@ -228,21 +239,21 @@ class Document extends Media
return $new_document;
}
function setTags(?string $tags): bool
public function setTags(?string $tags): bool
{
if(is_null($tags)) {
$this->stateChanges("tags", NULL);
if (is_null($tags)) {
$this->stateChanges("tags", null);
return true;
}
$parsed = explode(",", $tags);
if(sizeof($parsed) < 1 || $parsed[0] == "") {
$this->stateChanges("tags", NULL);
if (sizeof($parsed) < 1 || $parsed[0] == "") {
$this->stateChanges("tags", null);
return true;
}
$result = "";
foreach($parsed as $tag) {
foreach ($parsed as $tag) {
$result .= trim($tag) . ($tag != end($parsed) ? "," : '');
}
@ -250,99 +261,101 @@ class Document extends Media
return true;
}
function getOwner(bool $real = false): RowModel
public function getOwner(bool $real = false): RowModel
{
$oid = (int) $this->getRecord()->owner;
if($oid > 0)
return (new Users)->get($oid);
else
return (new Clubs)->get($oid * -1);
if ($oid > 0) {
return (new Users())->get($oid);
} else {
return (new Clubs())->get($oid * -1);
}
}
function getFileExtension(): string
public function getFileExtension(): string
{
if($this->tmp_format) {
if ($this->tmp_format) {
return $this->tmp_format;
}
return $this->getRecord()->format;
}
function getPrettyId(): string
public function getPrettyId(): string
{
return $this->getVirtualId() . "_" . $this->getId();
}
function getPrettiestId(): string
public function getPrettiestId(): string
{
return $this->getVirtualId() . "_" . $this->getId() . "_" . $this->getAccessKey();
}
function getOriginal(): Document
public function getOriginal(): Document
{
return $this->getRecord()->copy_of;
}
function getName(): string
public function getName(): string
{
return $this->getRecord()->name;
}
function getOriginalName(): string
public function getOriginalName(): string
{
return $this->getRecord()->original_name;
}
function getVKAPIType(): int
public function getVKAPIType(): int
{
return $this->getRecord()->type;
}
function getFolder(): int
public function getFolder(): int
{
return $this->getRecord()->folder_id;
}
function getTags(): array
public function getTags(): array
{
$tags = $this->getRecord()->tags;
if(!$tags)
if (!$tags) {
return [];
}
return explode(",", $tags ?? "");
}
function getFilesize(): int
public function getFilesize(): int
{
return $this->getRecord()->filesize;
}
function getPreview(): ?RowModel
public function getPreview(): ?RowModel
{
$preview_array = $this->getRecord()->preview;
$preview = explode(",", $this->getRecord()->preview)[0];
$model = NULL;
$model = null;
$exploded = explode("_", $preview);
switch($exploded[0]) {
switch ($exploded[0]) {
case "photo":
$model = (new Photos)->get((int)$exploded[1]);
$model = (new Photos())->get((int) $exploded[1]);
break;
}
return $model;
}
function getOwnerID(): int
public function getOwnerID(): int
{
return $this->getRecord()->owner;
}
function toApiPreview(): object
public function toApiPreview(): object
{
$preview = $this->getPreview();
if($preview instanceof Photo) {
return (object)[
if ($preview instanceof Photo) {
return (object) [
"photo" => [
"sizes" => array_values($preview->getVkApiSizes()),
],
@ -350,20 +363,22 @@ class Document extends Media
}
}
function canBeModifiedBy(User $user = NULL): bool
public function canBeModifiedBy(User $user = null): bool
{
if(!$user)
if (!$user) {
return false;
}
if ($this->getOwnerID() < 0) {
return (new Clubs())->get(abs($this->getOwnerID()))->canBeModifiedBy($user);
}
if($this->getOwnerID() < 0)
return (new Clubs)->get(abs($this->getOwnerID()))->canBeModifiedBy($user);
return $this->getOwnerID() === $user->getId();
}
function toVkApiStruct(?User $user = NULL, bool $return_tags = false): object
public function toVkApiStruct(?User $user = null, bool $return_tags = false): object
{
$res = new \stdClass;
$res = new \stdClass();
$res->id = $this->getId();
$res->owner_id = $this->getVirtualId();
$res->title = $this->getName();
@ -378,30 +393,33 @@ class Document extends Media
$res->folder_id = (int) $this->getFolder();
$res->access_key = $this->getAccessKey();
$res->private_url = "";
if($user)
if ($user) {
$res->can_manage = $this->canBeModifiedBy($user);
}
if($this->hasPreview())
if ($this->hasPreview()) {
$res->preview = $this->toApiPreview();
}
if($return_tags)
if ($return_tags) {
$res->tags = $this->getTags();
}
return $res;
}
function delete(bool $softly = true, bool $all_copies = false): void
public function delete(bool $softly = true, bool $all_copies = false): void
{
if($all_copies) {
if ($all_copies) {
$ctx = DatabaseConnection::i()->getContext();
$ctx->table("documents")->where("copy_of", $this->getId())->delete();
}
parent::delete($softly);
}
static function detectTypeByFormat(string $format)
public static function detectTypeByFormat(string $format)
{
switch(mb_strtolower($format)) {
switch (mb_strtolower($format)) {
case "txt": case "docx": case "doc": case "odt": case "pptx": case "ppt": case "xlsx": case "xls": case "md":
return 1;
case "zip": case "rar": case "7z":

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\Repositories\Users;
use openvk\Web\Models\RowModel;
use openvk\Web\Util\DateTime;
@ -8,7 +12,7 @@ class EmailChangeVerification extends PasswordReset
{
protected $tableName = "email_change_verifications";
function getNewEmail(): string
public function getNewEmail(): string
{
return $this->getRecord()->new_email;
}

View file

@ -1,10 +1,14 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\Repositories\Users;
use openvk\Web\Models\RowModel;
use openvk\Web\Util\DateTime;
class EmailVerification extends PasswordReset
{
protected $tableName = "email_verifications";
}
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\Repositories\Users;
use openvk\Web\Models\RowModel;
use openvk\Web\Util\DateTime;
class EmailVerification extends PasswordReset
{
protected $tableName = "email_verifications";
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Util\DateTime;
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Entities\User;
@ -7,53 +11,54 @@ use Nette\Utils\{Image, ImageException};
class Gift extends RowModel
{
const IMAGE_MAXSIZE = 131072;
const IMAGE_BINARY = 0;
const IMAGE_BASE64 = 1;
const IMAGE_URL = 2;
const PERIOD_IGNORE = 0;
const PERIOD_SET = 1;
const PERIOD_SET_IF_NONE = 2;
public const IMAGE_MAXSIZE = 131072;
public const IMAGE_BINARY = 0;
public const IMAGE_BASE64 = 1;
public const IMAGE_URL = 2;
public const PERIOD_IGNORE = 0;
public const PERIOD_SET = 1;
public const PERIOD_SET_IF_NONE = 2;
protected $tableName = "gifts";
function getName(): string
public function getName(): string
{
return $this->getRecord()->internal_name;
}
function getPrice(): int
public function getPrice(): int
{
return $this->getRecord()->price;
}
function getUsages(): int
public function getUsages(): int
{
return $this->getRecord()->usages;
}
function getUsagesBy(User $user, ?int $since = NULL): int
public function getUsagesBy(User $user, ?int $since = null): int
{
$sent = $this->getRecord()
->related("gift_user_relations.gift")
->where("sender", $user->getId())
->where("sent >= ?", $since ?? $this->getRecord()->limit_period ?? 0);
return sizeof($sent);
}
function getUsagesLeft(User $user): float
public function getUsagesLeft(User $user): float
{
if($this->getLimit() === INF)
if ($this->getLimit() === INF) {
return INF;
}
return max(0, $this->getLimit() - $this->getUsagesBy($user));
}
function getImage(int $type = 0): /* ?binary */ string
public function getImage(int $type = 0): /* ?binary */ string
{
switch($type) {
switch ($type) {
default:
case static::IMAGE_BINARY:
return $this->getRecord()->image ?? "";
@ -66,99 +71,100 @@ class Gift extends RowModel
break;
}
}
function getLimit(): float
public function getLimit(): float
{
$limit = $this->getRecord()->limit;
return !$limit ? INF : (float) $limit;
}
function getLimitResetTime(): ?DateTime
public function getLimitResetTime(): ?DateTime
{
return is_null($t = $this->getRecord()->limit_period) ? NULL : new DateTime($t);
return is_null($t = $this->getRecord()->limit_period) ? null : new DateTime($t);
}
function getUpdateDate(): DateTime
public function getUpdateDate(): DateTime
{
return new DateTime($this->getRecord()->updated);
}
function canUse(User $user): bool
public function canUse(User $user): bool
{
return $this->getUsagesLeft($user) > 0;
}
function isFree(): bool
public function isFree(): bool
{
return $this->getPrice() === 0;
}
function used(): void
public function used(): void
{
$this->stateChanges("usages", $this->getUsages() + 1);
$this->save();
}
function setName(string $name): void
public function setName(string $name): void
{
$this->stateChanges("internal_name", $name);
}
function setImage(string $file): bool
public function setImage(string $file): bool
{
$imgBlob;
try {
$image = Image::fromFile($file);
$image->resize(512, 512, Image::SHRINK_ONLY);
$imgBlob = $image->toString(Image::PNG);
} catch(ImageException $ex) {
} catch (ImageException $ex) {
return false;
}
if(strlen($imgBlob) > (2**24 - 1)) {
if (strlen($imgBlob) > (2 ** 24 - 1)) {
return false;
} else {
$this->stateChanges("updated", time());
$this->stateChanges("image", $imgBlob);
}
return true;
}
function setLimit(?float $limit = NULL, int $periodBehaviour = 0): void
public function setLimit(?float $limit = null, int $periodBehaviour = 0): void
{
$limit ??= $this->getLimit();
$limit = $limit === INF ? NULL : (int) $limit;
$limit = $limit === INF ? null : (int) $limit;
$this->stateChanges("limit", $limit);
if(!$limit) {
$this->stateChanges("limit_period", NULL);
if (!$limit) {
$this->stateChanges("limit_period", null);
return;
}
switch($periodBehaviour) {
switch ($periodBehaviour) {
default:
case static::PERIOD_IGNORE:
break;
case static::PERIOD_SET:
$this->stateChanges("limit_period", time());
break;
case static::PERIOD_SET_IF_NONE:
if(is_null($this->getRecord()) || is_null($this->getRecord()->limit_period))
if (is_null($this->getRecord()) || is_null($this->getRecord()->limit_period)) {
$this->stateChanges("limit_period", time());
}
break;
}
}
function delete(bool $softly = true): void
public function delete(bool $softly = true): void
{
$this->getRecord()->related("gift_relations.gift")->delete();
parent::delete($softly);
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use Chandler\Database\DatabaseConnection as DB;
use openvk\Web\Models\Repositories\Gifts;
use openvk\Web\Models\RowModel;
@ -8,19 +12,20 @@ use Transliterator;
class GiftCategory extends RowModel
{
protected $tableName = "gift_categories";
private function getLocalization(string $language): object
{
return $this->getRecord()
->related("gift_categories_locales.category")
->where("language", $language);
}
private function createLocalizationIfNotExists(string $language): void
{
if(!is_null($this->getLocalization($language)->fetch()))
if (!is_null($this->getLocalization($language)->fetch())) {
return;
}
DB::i()->getContext()->table("gift_categories_locales")->insert([
"category" => $this->getId(),
"language" => $language,
@ -28,8 +33,8 @@ class GiftCategory extends RowModel
"description" => "Sample Text",
]);
}
function getSlug(): string
public function getSlug(): string
{
return str_replace("ʹ", "-", Transliterator::createFromRules(
":: Any-Latin;"
@ -41,116 +46,123 @@ class GiftCategory extends RowModel
. "[:Separator:] > '-'"
)->transliterate($this->getName()));
}
function getThumbnailURL(): string
public function getThumbnailURL(): string
{
$primeGift = iterator_to_array($this->getGifts(1, 1))[0];
$serverUrl = ovk_scheme(true) . $_SERVER["SERVER_NAME"];
if(!$primeGift)
if (!$primeGift) {
return "$serverUrl/assets/packages/static/openvk/img/camera_200.png";
}
return $primeGift->getImage(Gift::IMAGE_URL);
}
function getName(string $language = "_", bool $returnNull = false): ?string
public function getName(string $language = "_", bool $returnNull = false): ?string
{
$loc = $this->getLocalization($language)->fetch();
if(!$loc) {
if($returnNull)
return NULL;
if (!$loc) {
if ($returnNull) {
return null;
}
return $language === "_" ? "Unlocalized" : $this->getName();
}
return $loc->name;
}
function getDescription(string $language = "_", bool $returnNull = false): ?string
public function getDescription(string $language = "_", bool $returnNull = false): ?string
{
$loc = $this->getLocalization($language)->fetch();
if(!$loc) {
if($returnNull)
return NULL;
if (!$loc) {
if ($returnNull) {
return null;
}
return $language === "_" ? "Unlocalized" : $this->getDescription();
}
return $loc->description;
}
function getGifts(int $page = -1, ?int $perPage = NULL, &$count = nullptr): \Traversable
public function getGifts(int $page = -1, ?int $perPage = null, &$count = nullptr): \Traversable
{
$gifts = $this->getRecord()->related("gift_relations.category");
if($page !== -1) {
if ($page !== -1) {
$count = $gifts->count();
$gifts = $gifts->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
}
foreach($gifts as $rel)
yield (new Gifts)->get($rel->gift);
foreach ($gifts as $rel) {
yield (new Gifts())->get($rel->gift);
}
}
function isMagical(): bool
public function isMagical(): bool
{
return !is_null($this->getRecord()->autoquery);
}
function hasGift(Gift $gift): bool
public function hasGift(Gift $gift): bool
{
$rels = $this->getRecord()->related("gift_relations.category");
return $rels->where("gift", $gift->getId())->count() > 0;
}
function addGift(Gift $gift): void
public function addGift(Gift $gift): void
{
if($this->hasGift($gift))
if ($this->hasGift($gift)) {
return;
}
DB::i()->getContext()->table("gift_relations")->insert([
"category" => $this->getId(),
"gift" => $gift->getId(),
]);
}
function removeGift(Gift $gift): void
public function removeGift(Gift $gift): void
{
if(!$this->hasGift($gift))
if (!$this->hasGift($gift)) {
return;
}
DB::i()->getContext()->table("gift_relations")->where([
"category" => $this->getId(),
"gift" => $gift->getId(),
])->delete();
}
function setName(string $language, string $name): void
public function setName(string $language, string $name): void
{
$this->createLocalizationIfNotExists($language);
$this->getLocalization($language)->update([
"name" => $name,
]);
}
function setDescription(string $language, string $description): void
public function setDescription(string $language, string $description): void
{
$this->createLocalizationIfNotExists($language);
$this->getLocalization($language)->update([
"description" => $description,
]);
}
function setAutoQuery(?array $query = NULL): void
public function setAutoQuery(?array $query = null): void
{
if(is_null($query)) {
$this->stateChanges("autoquery", NULL);
if (is_null($query)) {
$this->stateChanges("autoquery", null);
return;
}
$allowedColumns = ["price", "usages"];
if(array_diff_key($query, array_flip($allowedColumns)))
if (array_diff_key($query, array_flip($allowedColumns))) {
throw new \LogicException("Invalid query");
}
$this->stateChanges("autoquery", serialize($query));
}
}

View file

@ -1,45 +1,49 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\RowModel;
use openvk\Web\Util\DateTime;
class IP extends RowModel
{
protected $tableName = "ip";
const RL_RESET = 0;
const RL_CANEXEC = 1;
const RL_VIOLATION = 2;
const RL_BANNED = 3;
function getIp(): string
public const RL_RESET = 0;
public const RL_CANEXEC = 1;
public const RL_VIOLATION = 2;
public const RL_BANNED = 3;
public function getIp(): string
{
return inet_ntop($this->getRecord()->ip);
}
function getDiscoveryDate(): DateTime
public function getDiscoveryDate(): DateTime
{
return new DateTime($this->getRecord()->first_seen);
}
function isBanned(): bool
public function isBanned(): bool
{
return (bool) $this->getRecord()->banned;
}
function ban(): void
public function ban(): void
{
$this->stateChanges("banned", true);
$this->save();
}
function pardon(): void
public function pardon(): void
{
$this->stateChanges("banned", false);
$this->save();
}
function clear(): void
public function clear(): void
{
$this->stateChanges("rate_limit_counter_start", 0);
$this->stateChanges("rate_limit_counter", 0);
@ -47,45 +51,45 @@ class IP extends RowModel
$this->stateChanges("rate_limit_violation_counter", 0);
$this->save();
}
function rateLimit(int $actionComplexity = 1): int
public function rateLimit(int $actionComplexity = 1): int
{
$counterSessionStart = $this->getRecord()->rate_limit_counter_start;
$vCounterSessionStart = $this->getRecord()->rate_limit_violation_counter_start;
$aCounter = $this->getRecord()->rate_limit_counter;
$vCounter = $this->getRecord()->rate_limit_violation_counter;
$config = (object) OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"];
try {
if((time() - $config->time) > $counterSessionStart) {
if ((time() - $config->time) > $counterSessionStart) {
$counterSessionStart = time();
$aCounter = $actionComplexity;
return static::RL_RESET;
}
if(($aCounter + $actionComplexity) <= $config->actions) {
if (($aCounter + $actionComplexity) <= $config->actions) {
$aCounter += $actionComplexity;
return static::RL_CANEXEC;
}
if((time() - $config->maxViolationsAge) > $vCounterSessionStart) {
if ((time() - $config->maxViolationsAge) > $vCounterSessionStart) {
$vCounterSessionStart = time();
$vCounter = 1;
return static::RL_VIOLATION;
}
$vCounter += 1;
if($vCounter >= $config->maxViolations) {
if ($vCounter >= $config->maxViolations) {
$this->stateChanges("banned", true);
return static::RL_BANNED;
}
return static::RL_VIOLATION;
} finally {
$this->stateChanges("rate_limit_counter_start", $counterSessionStart);
@ -95,21 +99,23 @@ class IP extends RowModel
$this->save(false);
}
}
function setIp(string $ip): void
public function setIp(string $ip): void
{
$ip = inet_pton($ip);
if(!$ip)
if (!$ip) {
throw new \UnexpectedValueException("Malformed IP address");
}
$this->stateChanges("ip", $ip);
}
function save(?bool $log = false): void
public function save(?bool $log = false): void
{
if(is_null($this->getRecord()))
if (is_null($this->getRecord())) {
$this->stateChanges("first_seen", time());
}
parent::save($log);
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Util\DateTime;
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Entities\{Photo, Message, Correspondence};
@ -10,47 +14,46 @@ use Chandler\Security\User as ChandlerUser;
class Manager extends RowModel
{
use Traits\TSubscribable;
protected $tableName = "group_coadmins";
function getId(): int
public function getId(): int
{
return $this->getRecord()->id;
}
function getUserId(): int
public function getUserId(): int
{
return $this->getRecord()->user;
}
function getUser(): ?User
public function getUser(): ?User
{
return (new Users)->get($this->getRecord()->user);
return (new Users())->get($this->getRecord()->user);
}
function getClubId(): int
public function getClubId(): int
{
return $this->getRecord()->club;
}
function getClub(): ?Club
public function getClub(): ?Club
{
return (new Clubs)->get($this->getRecord()->club);
return (new Clubs())->get($this->getRecord()->club);
}
function getComment(): string
public function getComment(): string
{
return is_null($this->getRecord()->comment) ? "" : $this->getRecord()->comment;
}
function isHidden(): bool
public function isHidden(): bool
{
return (bool) $this->getRecord()->hidden;
}
function isClubPinned(): bool
public function isClubPinned(): bool
{
return (bool) $this->getRecord()->club_pinned;
}
use Traits\TSubscribable;
}

View file

@ -1,68 +1,77 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use Nette\InvalidStateException as ISE;
abstract class Media extends Postable
{
protected $fileExtension = "oct"; #octet stream xddd
protected $upperNodeReferenceColumnName = "owner";
protected $processingPlaceholder = NULL;
protected $processingPlaceholder = null;
protected $processingTime = 30;
function __destruct()
public function __destruct()
{
#Remove data, if model wasn't presisted
if(isset($this->changes["hash"]))
if (isset($this->changes["hash"])) {
unlink($this->pathFromHash($this->changes["hash"]));
}
}
protected function getBaseDir(): string
{
$uploadSettings = OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"];
if($uploadSettings["mode"] === "server" && $uploadSettings["server"]["kind"] === "cdn")
if ($uploadSettings["mode"] === "server" && $uploadSettings["server"]["kind"] === "cdn") {
return $uploadSettings["server"]["directory"];
else
} else {
return OPENVK_ROOT . "/storage/";
}
}
protected function checkIfFileIsProcessed(): bool
{
throw new \LogicException("checkIfFileIsProcessed is not implemented");
}
abstract protected function saveFile(string $filename, string $hash): bool;
protected function pathFromHash(string $hash): string
{
$dir = $this->getBaseDir() . substr($hash, 0, 2);
if(!is_dir($dir))
if (!is_dir($dir)) {
mkdir($dir);
}
return "$dir/$hash." . $this->fileExtension;
}
function getFileName(): string
public function getFileName(): string
{
return $this->pathFromHash($this->getRecord()->hash);
}
function getURL(): string
public function getURL(): string
{
if(!is_null($this->processingPlaceholder))
if(!$this->isProcessed())
if (!is_null($this->processingPlaceholder)) {
if (!$this->isProcessed()) {
return "/assets/packages/static/openvk/$this->processingPlaceholder.$this->fileExtension";
}
}
$hash = $this->getRecord()->hash;
switch(OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["mode"]) {
switch (OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["mode"]) {
default:
case "default":
case "basic":
return "http://" . $_SERVER['HTTP_HOST'] . "/blob_" . substr($hash, 0, 2) . "/$hash.$this->fileExtension";
break;
break;
case "accelerated":
return "http://" . $_SERVER['HTTP_HOST'] . "/openvk-datastore/$hash.$this->fileExtension";
break;
break;
case "server":
$settings = (object) OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["server"];
return (
@ -71,26 +80,29 @@ abstract class Media extends Postable
$settings->path .
substr($hash, 0, 2) . "/$hash.$this->fileExtension"
);
break;
break;
}
}
function getDescription(): ?string
public function getDescription(): ?string
{
return $this->getRecord()->description;
}
protected function isProcessed(): bool
{
if(is_null($this->processingPlaceholder))
if (is_null($this->processingPlaceholder)) {
return true;
}
if($this->getRecord()->processed)
if ($this->getRecord()->processed) {
return true;
}
$timeDiff = time() - $this->getRecord()->last_checked;
if($timeDiff < $this->processingTime)
if ($timeDiff < $this->processingTime) {
return false;
}
$res = $this->checkIfFileIsProcessed();
$this->stateChanges("last_checked", time());
@ -99,31 +111,32 @@ abstract class Media extends Postable
return $res;
}
function isDeleted(): bool
public function isDeleted(): bool
{
return (bool) $this->getRecord()->deleted;
}
function setHash(string $hash): void
public function setHash(string $hash): void
{
throw new ISE("Setting file hash manually is forbidden");
}
function setFile(array $file): void
public function setFile(array $file): void
{
if($file["error"] !== UPLOAD_ERR_OK)
if ($file["error"] !== UPLOAD_ERR_OK) {
throw new ISE("File uploaded is corrupted");
}
$hash = hash_file("whirlpool", $file["tmp_name"]);
$this->saveFile($file["tmp_name"], $hash);
$this->stateChanges("hash", $hash);
}
function save(?bool $log = false): void
public function save(?bool $log = false): void
{
if(!is_null($this->processingPlaceholder) && is_null($this->getRecord())) {
if (!is_null($this->processingPlaceholder) && is_null($this->getRecord())) {
$this->stateChanges("processed", 0);
$this->stateChanges("last_checked", time());
}
@ -131,20 +144,22 @@ abstract class Media extends Postable
parent::save($log);
}
function delete(bool $softly = true): void
public function delete(bool $softly = true): void
{
$deleteQuirk = ovkGetQuirk("blobs.erase-upon-deletion");
if($deleteQuirk === 2 || ($deleteQuirk === 1 && !$softly))
if ($deleteQuirk === 2 || ($deleteQuirk === 1 && !$softly)) {
@unlink($this->getFileName());
}
parent::delete($softly);
}
function undelete(): void
public function undelete(): void
{
if(ovkGetQuirk("blobs.erase-upon-deletion") === 2)
if (ovkGetQuirk("blobs.erase-upon-deletion") === 2) {
throw new \LogicException("Can't undelete model which is tied to blob, because of config constraint (quriks.yml:blobs.erase-upon-deletion)");
}
parent::undelete();
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use Nette\Database\Table\ActiveRow;
use openvk\Web\Util\DateTime;
use openvk\Web\Models\RowModel;
@ -10,196 +14,212 @@ use Chandler\Database\DatabaseConnection;
abstract class MediaCollection extends RowModel
{
use Traits\TOwnable;
protected $relTableName;
protected $entityTableName;
protected $entityClassName;
protected $allowDuplicates = true;
protected $specialNames = [];
protected $relations;
/**
* Maximum amount of items Collection can have
*/
const MAX_ITEMS = INF;
public const MAX_ITEMS = INF;
/**
* Maximum amount of Collections with same "owner" allowed
*/
const MAX_COUNT = INF;
function __construct(?ActiveRow $ar = NULL)
public const MAX_COUNT = INF;
public function __construct(?ActiveRow $ar = null)
{
parent::__construct($ar);
$this->relations = DatabaseConnection::i()->getContext()->table($this->relTableName);
}
private function entitySuitable(RowModel $entity): bool
{
if(($class = get_class($entity)) !== $this->entityClassName)
if (($class = get_class($entity)) !== $this->entityClassName) {
throw new \UnexpectedValueException("This MediaCollection can only store '$this->entityClassName' (not '$class').");
}
return true;
}
function getOwner(): RowModel
public function getOwner(): RowModel
{
$oid = $this->getRecord()->owner;
if($oid > 0)
return (new Users)->get($oid);
else
return (new Clubs)->get($oid * -1);
if ($oid > 0) {
return (new Users())->get($oid);
} else {
return (new Clubs())->get($oid * -1);
}
}
function getPrettyId(): string
public function getPrettyId(): string
{
return $this->getRecord()->owner . "_" . $this->getRecord()->id;
}
function getName(): string
public function getName(): string
{
$special = $this->getRecord()->special_type;
if($special === 0)
if ($special === 0) {
return $this->getRecord()->name;
}
$sName = $this->specialNames[$special];
if(!$sName)
if (!$sName) {
return $this->getRecord()->name;
if($sName[0] === "_")
}
if ($sName[0] === "_") {
$sName = tr(substr($sName, 1));
}
return $sName;
}
function getDescription(): ?string
public function getDescription(): ?string
{
return $this->getRecord()->description;
}
abstract function getCoverURL(): ?string;
function fetchClassic(int $offset = 0, ?int $limit = NULL): \Traversable
abstract public function getCoverURL(): ?string;
public function fetchClassic(int $offset = 0, ?int $limit = null): \Traversable
{
$related = $this->getRecord()->related("$this->relTableName.collection")
->limit($limit ?? OPENVK_DEFAULT_PER_PAGE, $offset)
->order("media ASC");
foreach($related as $rel) {
foreach ($related as $rel) {
$media = $rel->ref($this->entityTableName, "media");
if(!$media)
if (!$media) {
continue;
}
yield new $this->entityClassName($media);
}
}
function fetch(int $page = 1, ?int $perPage = NULL): \Traversable
public 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
public function size(): int
{
return sizeof($this->getRecord()->related("$this->relTableName.collection"));
}
function getCreationTime(): DateTime
public function getCreationTime(): DateTime
{
return new DateTime($this->getRecord()->created);
}
function getPublicationTime(): DateTime
public function getPublicationTime(): DateTime
{
return $this->getCreationTime();
}
function getEditTime(): ?DateTime
public function getEditTime(): ?DateTime
{
$edited = $this->getRecord()->edited;
if(is_null($edited)) return NULL;
if (is_null($edited)) {
return null;
}
return new DateTime($edited);
}
function isCreatedBySystem(): bool
public function isCreatedBySystem(): bool
{
return $this->getRecord()->special_type !== 0;
}
function add(RowModel $entity): bool
public function add(RowModel $entity): bool
{
$this->entitySuitable($entity);
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)
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(),
"media" => $entity->getId(),
]);
return true;
}
function remove(RowModel $entity): bool
public function remove(RowModel $entity): bool
{
$this->entitySuitable($entity);
return $this->relations->where([
"collection" => $this->getId(),
"media" => $entity->getId(),
])->delete() > 0;
}
function has(RowModel $entity): bool
public function has(RowModel $entity): bool
{
$this->entitySuitable($entity);
$rel = $this->relations->where([
"collection" => $this->getId(),
"media" => $entity->getId(),
])->fetch();
return !is_null($rel);
}
function save(?bool $log = false): void
public 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)
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"]))
if (is_null($this->getRecord())) {
if (!isset($this->changes["created"])) {
$this->stateChanges("created", time());
else
$this->stateChanges("edited", time());
} else {
$this->stateChanges("edited", time());
}
}
parent::save($log);
}
function delete(bool $softly = true): void
public function delete(bool $softly = true): void
{
if(!$softly) {
if (!$softly) {
$this->relations->where("collection", $this->getId())
->delete();
}
parent::delete($softly);
}
use Traits\TOwnable;
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use Chandler\Database\DatabaseConnection;
use openvk\Web\Models\Repositories\Clubs;
use openvk\Web\Models\Repositories\Users;
@ -12,108 +16,114 @@ use openvk\Web\Util\DateTime;
*/
class Message extends RowModel
{
use Traits\TRichText;
use Traits\TAttachmentHost;
protected $tableName = "messages";
/**
* Get origin of the message.
*
*
* Returns either user or club.
*
*
* @returns User|Club
*/
function getSender(): ?RowModel
public function getSender(): ?RowModel
{
if($this->getRecord()->sender_type === 'openvk\Web\Models\Entities\User')
return (new Users)->get($this->getRecord()->sender_id);
else if($this->getRecord()->sender_type === 'openvk\Web\Models\Entities\Club')
return (new Clubs)->get($this->getRecord()->sender_id);
if ($this->getRecord()->sender_type === 'openvk\Web\Models\Entities\User') {
return (new Users())->get($this->getRecord()->sender_id);
} elseif ($this->getRecord()->sender_type === 'openvk\Web\Models\Entities\Club') {
return (new Clubs())->get($this->getRecord()->sender_id);
}
}
/**
* Get the destination of the message.
*
*
* Returns either user or club.
*
*
* @returns User|Club
*/
function getRecipient(): ?RowModel
public function getRecipient(): ?RowModel
{
if($this->getRecord()->recipient_type === 'openvk\Web\Models\Entities\User')
return (new Users)->get($this->getRecord()->recipient_id);
else if($this->getRecord()->recipient_type === 'openvk\Web\Models\Entities\Club')
return (new Clubs)->get($this->getRecord()->recipient_id);
if ($this->getRecord()->recipient_type === 'openvk\Web\Models\Entities\User') {
return (new Users())->get($this->getRecord()->recipient_id);
} elseif ($this->getRecord()->recipient_type === 'openvk\Web\Models\Entities\Club') {
return (new Clubs())->get($this->getRecord()->recipient_id);
}
}
function getUnreadState(): int
public function getUnreadState(): int
{
trigger_error("TODO: use isUnread", E_USER_DEPRECATED);
return (int) $this->isUnread();
}
/**
* Get date of initial publication.
*
*
* @returns DateTime
*/
function getSendTime(): DateTime
public function getSendTime(): DateTime
{
return new DateTime($this->getRecord()->created);
}
function getSendTimeHumanized(): string
public function getSendTimeHumanized(): string
{
$dateTime = new DateTime($this->getRecord()->created);
if($dateTime->format("%d.%m.%y") == ovk_strftime_safe("%d.%m.%y", time())) {
if ($dateTime->format("%d.%m.%y") == ovk_strftime_safe("%d.%m.%y", time())) {
return $dateTime->format("%T");
} else {
return $dateTime->format("%d.%m.%y");
}
}
/**
* Get date of last edit, if any edits were made, otherwise null.
*
*
* @returns DateTime|null
*/
function getEditTime(): ?DateTime
public function getEditTime(): ?DateTime
{
$edited = $this->getRecord()->edited;
if(is_null($edited)) return NULL;
if (is_null($edited)) {
return null;
}
return new DateTime($edited);
}
/**
* Is this message an ad?
*
*
* Messages can never be ads.
*
*
* @returns false
*/
function isAd(): bool
public function isAd(): bool
{
return false;
}
function isUnread(): bool
public function isUnread(): bool
{
return (bool) $this->getRecord()->unread;
}
/**
* Simplify to array
*
*
* @returns array
*/
function simplify(): array
public function simplify(): array
{
$author = $this->getSender();
$attachments = [];
foreach($this->getChildren() as $attachment) {
if($attachment instanceof Photo) {
foreach ($this->getChildren() as $attachment) {
if ($attachment instanceof Photo) {
$attachments[] = [
"type" => "photo",
"link" => "/photo" . $attachment->getPrettyId(),
@ -124,31 +134,28 @@ class Message extends RowModel
];
} else {
$attachments[] = [
"type" => "unknown"
"type" => "unknown",
];
# throw new \Exception("Unknown attachment type: " . get_class($attachment));
}
}
return [
"uuid" => $this->getId(),
"sender" => [
"id" => $author->getId(),
"link" => $_SERVER['REQUEST_SCHEME'] . "://" . $_SERVER['HTTP_HOST'] . $author->getURL(),
"avatar" => $author->getAvatarUrl(),
"name" => $author->getFirstName().$unreadmsg,
"name" => $author->getFirstName() . $unreadmsg,
],
"timing" => [
"sent" => (string) $this->getSendTimeHumanized(),
"edited" => is_null($this->getEditTime()) ? NULL : (string) $this->getEditTime(),
"edited" => is_null($this->getEditTime()) ? null : (string) $this->getEditTime(),
],
"text" => $this->getText(),
"read" => !$this->isUnread(),
"attachments" => $attachments,
];
}
use Traits\TRichText;
use Traits\TAttachmentHost;
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?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};
@ -9,52 +13,52 @@ class NoSpamLog extends RowModel
{
protected $tableName = "noSpam_templates";
function getId(): int
public function getId(): int
{
return $this->getRecord()->id;
}
function getUser(): ?User
public function getUser(): ?User
{
return (new Users)->get($this->getRecord()->user);
return (new Users())->get($this->getRecord()->user);
}
function getModel(): string
public function getModel(): string
{
return $this->getRecord()->model;
}
function getRegex(): ?string
public function getRegex(): ?string
{
return $this->getRecord()->regex;
}
function getRequest(): ?string
public function getRequest(): ?string
{
return $this->getRecord()->request;
}
function getCount(): int
public function getCount(): int
{
return $this->getRecord()->count;
}
function getTime(): DateTime
public function getTime(): DateTime
{
return new DateTime($this->getRecord()->time);
}
function getItems(): ?array
public function getItems(): ?array
{
return explode(",", $this->getRecord()->items);
}
function getTypeRaw(): int
public function getTypeRaw(): int
{
return $this->getRecord()->ban_type;
}
function getType(): string
public function getType(): string
{
switch ($this->getTypeRaw()) {
case 1: return "О";
@ -64,7 +68,7 @@ class NoSpamLog extends RowModel
}
}
function isRollbacked(): bool
public function isRollbacked(): bool
{
return !is_null($this->getRecord()->rollback);
}

View file

@ -1,12 +1,16 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use HTMLPurifier_Config;
use HTMLPurifier;
class Note extends Postable
{
protected $tableName = "notes";
protected function renderHTML(): string
{
$config = HTMLPurifier_Config::createDefault();
@ -74,61 +78,63 @@ class Note extends Postable
$config->set("Attr.AllowedClasses", [
"underline",
]);
$source = NULL;
if(is_null($this->getRecord())) {
if(isset($this->changes["source"]))
$source = null;
if (is_null($this->getRecord())) {
if (isset($this->changes["source"])) {
$source = $this->changes["source"];
else
} else {
throw new \LogicException("Can't render note without content set.");
}
} else {
$source = $this->getRecord()->source;
}
$purifier = new HTMLPurifier($config);
return $purifier->purify($source);
}
function getName(): string
public function getName(): string
{
return $this->getRecord()->name;
}
function getPreview(int $length = 25): string
public function getPreview(int $length = 25): string
{
return ovk_proc_strtr(strip_tags($this->getRecord()->source), $length);
}
function getText(): string
public function getText(): string
{
if(is_null($this->getRecord()))
if (is_null($this->getRecord())) {
return $this->renderHTML();
}
$cached = $this->getRecord()->cached_content;
if(!$cached) {
if (!$cached) {
$cached = $this->renderHTML();
$this->setCached_Content($cached);
$this->save();
}
return $cached;
}
function getSource(): string
public function getSource(): string
{
return $this->getRecord()->source;
}
function canBeViewedBy(?User $user = NULL): bool
public function canBeViewedBy(?User $user = null): bool
{
if($this->isDeleted() || $this->getOwner()->isDeleted()) {
if ($this->isDeleted() || $this->getOwner()->isDeleted()) {
return false;
}
return $this->getOwner()->getPrivacyPermission('notes.read', $user) && $this->getOwner()->canBeViewedBy($user);
}
function toVkApiStruct(): object
public function toVkApiStruct(): object
{
$res = (object) [];
@ -140,7 +146,7 @@ class Note extends Postable
$res->date = $this->getPublicationTime()->timestamp();
$res->comments = $this->getCommentsCount();
$res->read_comments = $this->getCommentsCount();
$res->view_url = "/note".$this->getOwner()->getId()."_".$this->getVirtualId();
$res->view_url = "/note" . $this->getOwner()->getId() . "_" . $this->getVirtualId();
$res->privacy_view = 1;
$res->can_comment = 1;
$res->text_wiki = "r";

View file

@ -1,12 +1,16 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\{User, Club};
final class ClubModeratorNotification extends Notification
{
protected $actionCode = 5;
function __construct(User $recipient, Club $group, User $admin)
public function __construct(User $recipient, Club $group, User $admin)
{
parent::__construct($recipient, $group, $admin, time(), "");
}

View file

@ -1,12 +1,16 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\User;
final class CoinsTransferNotification extends Notification
{
protected $actionCode = 9602;
function __construct(User $receiver, User $sender, int $value, string $message)
public function __construct(User $receiver, User $sender, int $value, string $message)
{
parent::__construct($receiver, $receiver, $sender, time(), $value . " " . $message);
}

View file

@ -1,12 +1,16 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\{User, Post, Comment};
final class CommentNotification extends Notification
{
protected $actionCode = 2;
function __construct(User $recipient, Comment $comment, $postable, User $commenter)
public function __construct(User $recipient, Comment $comment, $postable, User $commenter)
{
parent::__construct($recipient, $postable, $commenter, time(), ovk_proc_strtr(strip_tags($comment->getText()), 400));
}

View file

@ -1,12 +1,16 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\User;
final class FriendRemovalNotification extends Notification
{
protected $actionCode = 4;
function __construct(User $recipient, User $friend, User $remover)
public function __construct(User $recipient, User $friend, User $remover)
{
parent::__construct($recipient, $friend, $remover, time(), "");
}

View file

@ -1,12 +1,16 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\{User, Gift};
final class GiftNotification extends Notification
{
protected $actionCode = 9601;
function __construct(User $receiver, User $sender, Gift $gift, ?string $comment)
public function __construct(User $receiver, User $sender, Gift $gift, ?string $comment)
{
parent::__construct($receiver, $gift, $sender, time(), $comment ?? "");
}

View file

@ -1,13 +1,17 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\{User, Post};
final class LikeNotification extends Notification
{
protected $actionCode = 0;
protected $threshold = 120;
function __construct(User $recipient, Post $post, User $liker)
public function __construct(User $recipient, Post $post, User $liker)
{
parent::__construct($recipient, $post, $liker, time(), "");
}

View file

@ -1,13 +1,17 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\Postable;
use openvk\Web\Models\Entities\User;
final class MentionNotification extends Notification
{
protected $actionCode = 4;
function __construct(User $recipient, Postable $discussionHost, $mentioner, string $quote = "")
public function __construct(User $recipient, Postable $discussionHost, $mentioner, string $quote = "")
{
parent::__construct($recipient, $mentioner, $discussionHost, time(), $quote);
}

View file

@ -1,12 +1,16 @@
<?php declare(strict_types=1);
<?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)
public function __construct(User $owner, Club $group)
{
parent::__construct($owner, $owner, $group, time(), "");
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Entities\{User};
use openvk\Web\Util\DateTime;
@ -12,11 +16,11 @@ class Notification
private $targetModel;
private $time;
private $data;
protected $actionCode = NULL;
protected $actionCode = null;
protected $threshold = -1;
function __construct(User $recipient, $originModel, $targetModel, ?int $time = NULL, string $data = "")
public function __construct(User $recipient, $originModel, $targetModel, ?int $time = null, string $data = "")
{
$this->recipient = $recipient;
$this->originModel = $originModel;
@ -24,67 +28,68 @@ class Notification
$this->time = $time ?? time();
$this->data = $data;
}
private function encodeType(object $model): int
{
return (int) json_decode(file_get_contents(__DIR__ . "/../../../../data/modelCodes.json"), true)[get_class($model)];
}
function reverseModelOrder(): bool
public function reverseModelOrder(): bool
{
return false;
}
function getActionCode(): int
public function getActionCode(): int
{
return $this->actionCode;
}
function setActionCode(int $code): void
public function setActionCode(int $code): void
{
$this->actionCode = $this->actionCode ?? $code;
$this->actionCode ??= $code;
}
function getTemplatePath(): string
public function getTemplatePath(): string
{
return implode("_", [
"./../components/notifications/$this->actionCode/",
$this->encodeType($this->originModel),
$this->encodeType($this->targetModel),
".xml"
".xml",
]);
}
function getRecipient(): User
public function getRecipient(): User
{
return $this->recipient;
}
function getModel(int $index): RowModel
public function getModel(int $index): RowModel
{
switch($index) {
switch ($index) {
case 0:
return $this->originModel;
case 1:
return $this->targetModel;
}
}
function getData(): string
public function getData(): string
{
return $this->data;
}
function getDateTime(): DateTime
public function getDateTime(): DateTime
{
return new DateTime($this->time);
}
function emit(): bool
public function emit(): bool
{
if(!($e = eventdb()))
if (!($e = eventdb())) {
return false;
}
$data = [
"recipient" => $this->recipient->getId(),
"originModelType" => $this->encodeType($this->originModel),
@ -95,56 +100,57 @@ class Notification
"additionalPayload" => $this->data,
"timestamp" => $this->time,
];
$edb = $e->getConnection();
if($this->threshold !== -1) {
if ($this->threshold !== -1) {
# Event is thersholded, check if there is similar event
$query = <<<'QUERY'
SELECT * FROM `notifications` WHERE `recipientType`=0 AND `recipientId`=? AND `originModelType`=? AND `originModelId`=? AND `targetModelType`=? AND `targetModelId`=? AND `modelAction`=? AND `additionalData`=? AND `timestamp` > (? - ?)
QUERY;
SELECT * FROM `notifications` WHERE `recipientType`=0 AND `recipientId`=? AND `originModelType`=? AND `originModelId`=? AND `targetModelType`=? AND `targetModelId`=? AND `modelAction`=? AND `additionalData`=? AND `timestamp` > (? - ?)
QUERY;
$result = $edb->query($query, ...array_merge(array_values($data), [ $this->threshold ]));
if($result->getRowCount() > 0)
if ($result->getRowCount() > 0) {
return false;
}
}
$edb->query("INSERT INTO notifications VALUES (0, ?, ?, ?, ?, ?, ?, ?, ?)", ...array_values($data));
$kafkaConf = OPENVK_ROOT_CONF["openvk"]["credentials"]["notificationsBroker"];
if($kafkaConf["enable"]) {
if ($kafkaConf["enable"]) {
$kafkaConf = $kafkaConf["kafka"];
$brokerConf = new Conf();
$brokerConf->set("log_level", (string) LOG_DEBUG);
$brokerConf->set("debug", "all");
$producer = new Producer($brokerConf);
$producer->addBrokers($kafkaConf["addr"] . ":" . $kafkaConf["port"]);
$descriptor = implode(",", [
str_replace("\\", ".", get_class($this)),
$this->recipient->getId(),
base64_encode(serialize((object) $data)),
]);
$notifTopic = $producer->newTopic($kafkaConf["topic"]);
$notifTopic->produce(RD_KAFKA_PARTITION_UA, RD_KAFKA_MSG_F_BLOCK, $descriptor);
$producer->flush(100);
}
return true;
}
function getVkApiInfo()
public function getVkApiInfo()
{
$origin_m = $this->encodeType($this->originModel);
$target_m = $this->encodeType($this->targetModel);
$info = [
"type" => "",
"parent" => NULL,
"feedback" => NULL,
"parent" => null,
"feedback" => null,
];
switch($this->getActionCode()) {
switch ($this->getActionCode()) {
case 0:
$info["type"] = "like_post";
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
@ -153,32 +159,32 @@ QUERY;
case 1:
$info["type"] = "copy_post";
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
$info["feedback"] = NULL; # todo
$info["feedback"] = null; # todo
break;
case 2:
switch($origin_m) {
switch ($origin_m) {
case 19:
$info["type"] = "comment_video";
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
$info["feedback"] = NULL; # айди коммента не сохраняется в бд( ну пиздец блять
$info["feedback"] = null; # айди коммента не сохраняется в бд( ну пиздец блять
break;
case 13:
$info["type"] = "comment_photo";
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
$info["feedback"] = NULL;
$info["feedback"] = null;
break;
# unstandart (vk forgor about notes)
# unstandart (vk forgor about notes)
case 10:
$info["type"] = "comment_note";
$info["parent"] = $this->getModel(0)->toVkApiStruct();
$info["feedback"] = NULL;
$info["feedback"] = null;
break;
case 14:
$info["type"] = "comment_post";
$info["parent"] = $this->getModel(0)->toNotifApiStruct();
$info["feedback"] = NULL;
$info["feedback"] = null;
break;
# unused (users don't have topics bruh)
# unused (users don't have topics bruh)
case 21:
$info["type"] = "comment_topic";
$info["parent"] = $this->getModel(0)->toVkApiStruct(0, 90);
@ -194,7 +200,7 @@ QUERY;
$info["feedback"] = $this->getModel(0)->toNotifApiStruct();
break;
case 4:
switch($target_m) {
switch ($target_m) {
case 14:
$info["type"] = "mention";
$info["feedback"] = $this->getModel(1)->toNotifApiStruct();
@ -207,7 +213,7 @@ QUERY;
$info["type"] = "mention_comment_photo";
$info["parent"] = $this->getModel(1)->toNotifApiStruct();
break;
# unstandart
# unstandart
case 10:
$info["type"] = "mention_comment_note";
$info["parent"] = $this->getModel(1)->toVkApiStruct();
@ -224,15 +230,15 @@ QUERY;
$info["type"] = "make_you_admin";
$info["parent"] = $this->getModel(0)->toVkApiStruct($this->getModel(1));
break;
# Нужно доделать после мержа #935
# Нужно доделать после мержа #935
case 6:
$info["type"] = "wall_publish";
break;
# В вк не было такого уведомления, так что unstandart
# В вк не было такого уведомления, так что unstandart
case 7:
$info["type"] = "new_posts_in_club";
break;
# В вк при передаче подарков приходит сообщение, а не уведомление, так что unstandart
# В вк при передаче подарков приходит сообщение, а не уведомление, так что unstandart
case 9601:
$info["type"] = "sent_gift";
$info["parent"] = $this->getModel(1)->toVkApiStruct($this->getModel(1));
@ -247,23 +253,23 @@ QUERY;
$info["parent"]->count = $this->getData();
break;
default:
$info["type"] = NULL;
$info["type"] = null;
break;
}
return $info;
}
function toVkApiStruct()
public function toVkApiStruct()
{
$res = (object)[];
$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; # Ответы на комментарии не реализованы
$res->reply = null; # Ответы на комментарии не реализованы
return $res;
}
}

View file

@ -1,12 +1,16 @@
<?php declare(strict_types=1);
<?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)
public function __construct(User $author, Post $post, Club $group)
{
parent::__construct($author, $post, $group, time(), "");
}

View file

@ -1,12 +1,16 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\User;
final class RatingUpNotification extends Notification
{
protected $actionCode = 9603;
function __construct(User $receiver, User $sender, int $value, string $message)
public function __construct(User $receiver, User $sender, int $value, string $message)
{
parent::__construct($receiver, $receiver, $sender, time(), $value . " " . $message);
}

View file

@ -1,13 +1,17 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\{User, Post};
final class RepostNotification extends Notification
{
protected $actionCode = 1;
protected $threshold = 120;
function __construct(User $recipient, Post $post, User $reposter)
public function __construct(User $recipient, Post $post, User $reposter)
{
parent::__construct($recipient, $post, $reposter, time(), "");
}

View file

@ -1,12 +1,16 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Notifications;
use openvk\Web\Models\Entities\{User, Post};
final class WallPostNotification extends Notification
{
protected $actionCode = 3;
function __construct(User $recipient, Post $post, User $poster)
public function __construct(User $recipient, Post $post, User $poster)
{
parent::__construct($recipient, $post, $poster, time(), ovk_proc_strtr($post->getText(), 10));
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\Repositories\Users;
use openvk\Web\Models\RowModel;
use openvk\Web\Util\DateTime;
@ -7,58 +11,58 @@ use openvk\Web\Util\DateTime;
class PasswordReset extends RowModel
{
protected $tableName = "password_resets";
function getUser(): User
public function getUser(): User
{
return (new Users)->get($this->getRecord()->profile);
return (new Users())->get($this->getRecord()->profile);
}
function getKey(): string
public function getKey(): string
{
return $this->getRecord()->key;
}
function getToken(): string
public function getToken(): string
{
return $this->getKey();
}
function getCreationTime(): DateTime
public function getCreationTime(): DateTime
{
return new DateTime($this->getRecord()->timestamp);
}
/**
* User can request password reset only if he does not have any "new" password resets.
* Password reset becomes "old" after 5 minutes and one second.
*/
function isNew(): bool
public function isNew(): bool
{
return $this->getRecord()->timestamp > (time() - (5 * MINUTE));
}
/**
* Token is valid only for 3 days.
*/
function isStillValid(): bool
public function isStillValid(): bool
{
return $this->getRecord()->timestamp > (time() - (3 * DAY));
}
function verify(string $token): bool
public function verify(string $token): bool
{
try {
return $this->isStillValid() ? sodium_memcmp($this->getKey(), $token) : false;
} catch(\SodiumException $ex) {
} catch (\SodiumException $ex) {
return false;
}
}
function save(?bool $log = false): void
public function save(?bool $log = false): void
{
$this->stateChanges("key", base64_encode(openssl_random_pseudo_bytes(46)));
$this->stateChanges("timestamp", time());
parent::save($log);
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use MessagePack\MessagePack;
use Nette\Utils\ImageException;
use Nette\Utils\UnknownImageFileException;
@ -14,8 +18,8 @@ class Photo extends Media
protected $tableName = "photos";
protected $fileExtension = "jpeg";
const ALLOWED_SIDE_MULTIPLIER = 7;
public const ALLOWED_SIDE_MULTIPLIER = 7;
/**
* @throws \ImagickException
* @throws ImageException
@ -25,70 +29,78 @@ class Photo extends Media
{
$res = [false];
$requiresProportion = ((string) $size["requireProp"]) != "none";
if($requiresProportion) {
if ($requiresProportion) {
$props = explode(":", (string) $size["requireProp"]);
$px = (int) $props[0];
$py = (int) $props[1];
if(($image->getImageWidth() / $image->getImageHeight()) > ($px / $py)) {
if (($image->getImageWidth() / $image->getImageHeight()) > ($px / $py)) {
$height = (int) ceil(($px * $image->getImageWidth()) / $py);
$image->cropImage($image->getImageWidth(), $height, 0, 0);
$res[0] = true;
}
}
if(isset($size["maxSize"])) {
if (isset($size["maxSize"])) {
$maxSize = (int) $size["maxSize"];
$sizes = Image::calculateSize($image->getImageWidth(), $image->getImageHeight(), $maxSize, $maxSize, Image::SHRINK_ONLY | Image::FIT);
$image->resizeImage($sizes[0], $sizes[1], \Imagick::FILTER_HERMITE, 1);
} else if(isset($size["maxResolution"])) {
} elseif (isset($size["maxResolution"])) {
$resolution = explode("x", (string) $size["maxResolution"]);
$sizes = Image::calculateSize(
$image->getImageWidth(), $image->getImageHeight(), (int) $resolution[0], (int) $resolution[1], Image::SHRINK_ONLY | Image::FIT
$image->getImageWidth(),
$image->getImageHeight(),
(int) $resolution[0],
(int) $resolution[1],
Image::SHRINK_ONLY | Image::FIT
);
$image->resizeImage($sizes[0], $sizes[1], \Imagick::FILTER_HERMITE, 1);
} else {
throw new \RuntimeException("Malformed size description: " . (string) $size["id"]);
}
$res[1] = $image->getImageWidth();
$res[2] = $image->getImageHeight();
if($res[1] <= 300 || $res[2] <= 300)
if ($res[1] <= 300 || $res[2] <= 300) {
$image->writeImage("$outputDir/$size[id].gif");
else
} else {
$image->writeImage("$outputDir/$size[id].jpeg");
}
$res[3] = true;
$image->destroy();
unset($image);
return $res;
}
private function saveImageResizedCopies(?\Imagick $image, string $filename, string $hash): void
{
if(!$image) {
$image = new \Imagick;
if (!$image) {
$image = new \Imagick();
$image->readImage($filename);
}
$dir = dirname($this->pathFromHash($hash));
$dir = "$dir/$hash" . "_cropped";
if(!is_dir($dir)) {
if (!is_dir($dir)) {
@unlink($dir); # Added to transparently bypass issues with dead pesudofolders summoned by buggy SWIFT impls (selectel)
mkdir($dir);
}
$sizes = simplexml_load_file(OPENVK_ROOT . "/data/photosizes.xml");
if(!$sizes)
if (!$sizes) {
throw new \RuntimeException("Could not load photosizes.xml!");
}
$sizesMeta = [];
if(OPENVK_ROOT_CONF["openvk"]["preferences"]["photos"]["photoSaving"] === "quick") {
foreach($sizes->Size as $size)
$sizesMeta[(string)$size["id"]] = [false, false, false, false];
if (OPENVK_ROOT_CONF["openvk"]["preferences"]["photos"]["photoSaving"] === "quick") {
foreach ($sizes->Size as $size) {
$sizesMeta[(string) $size["id"]] = [false, false, false, false];
}
} else {
foreach($sizes->Size as $size)
$sizesMeta[(string)$size["id"]] = $this->resizeImage(clone $image, $dir, $size);
foreach ($sizes->Size as $size) {
$sizesMeta[(string) $size["id"]] = $this->resizeImage(clone $image, $dir, $size);
}
}
$sizesMeta = MessagePack::pack($sizesMeta);
@ -97,16 +109,18 @@ class Photo extends Media
protected function saveFile(string $filename, string $hash): bool
{
$input_image = new \Imagick;
$input_image = new \Imagick();
$input_image->readImage($filename);
$h = $input_image->getImageHeight();
$w = $input_image->getImageWidth();
if(($h >= ($w * Photo::ALLOWED_SIDE_MULTIPLIER)) || ($w >= ($h * Photo::ALLOWED_SIDE_MULTIPLIER)))
if (($h >= ($w * Photo::ALLOWED_SIDE_MULTIPLIER)) || ($w >= ($h * Photo::ALLOWED_SIDE_MULTIPLIER))) {
throw new ISE("Invalid layout: image is too wide/short");
}
# gif fix 10.01.2025
if($input_image->getImageFormat() === 'GIF')
if ($input_image->getImageFormat() === 'GIF') {
$input_image->setIteratorIndex(0);
}
# png workaround (transparency to white)
$image = new \Imagick();
@ -115,66 +129,72 @@ class Photo extends Media
$image->compositeImage($input_image, \Imagick::COMPOSITE_OVER, 0, 0);
$sizes = Image::calculateSize(
$image->getImageWidth(), $image->getImageHeight(), 8192, 4320, Image::SHRINK_ONLY | Image::FIT
$image->getImageWidth(),
$image->getImageHeight(),
8192,
4320,
Image::SHRINK_ONLY | Image::FIT
);
$image->resizeImage($sizes[0], $sizes[1], \Imagick::FILTER_HERMITE, 1);
$image->writeImage($this->pathFromHash($hash));
$this->saveImageResizedCopies($image, $filename, $hash);
return true;
}
function crop(float $left, float $top, float $width, float $height): void
public function crop(float $left, float $top, float $width, float $height): void
{
if(isset($this->changes["hash"]))
if (isset($this->changes["hash"])) {
$hash = $this->changes["hash"];
else if(!is_null($this->getRecord()))
} elseif (!is_null($this->getRecord())) {
$hash = $this->getRecord()->hash;
else
} else {
throw new ISE("Cannot crop uninitialized image. Please call setFile(\$_FILES[...]) first.");
}
$image = Image::fromFile($this->pathFromHash($hash));
$image->crop($left, $top, $width, $height);
$image->save($this->pathFromHash($hash));
}
function isolate(): void
public function isolate(): void
{
if(is_null($this->getRecord()))
if (is_null($this->getRecord())) {
throw new ISE("Cannot isolate unpresisted image. Please save() it first.");
}
DB::i()->getContext()->table("album_relations")->where("media", $this->getRecord()->id)->delete();
}
function getSizes(bool $upgrade = false, bool $forceUpdate = false): ?array
public function getSizes(bool $upgrade = false, bool $forceUpdate = false): ?array
{
$sizes = $this->getRecord()->sizes;
if(!$sizes || $forceUpdate) {
if($forceUpdate || $upgrade || OPENVK_ROOT_CONF["openvk"]["preferences"]["photos"]["upgradeStructure"]) {
if (!$sizes || $forceUpdate) {
if ($forceUpdate || $upgrade || OPENVK_ROOT_CONF["openvk"]["preferences"]["photos"]["upgradeStructure"]) {
$hash = $this->getRecord()->hash;
$this->saveImageResizedCopies(NULL, $this->pathFromHash($hash), $hash);
$this->saveImageResizedCopies(null, $this->pathFromHash($hash), $hash);
$this->save();
return $this->getSizes();
}
return NULL;
return null;
}
$res = [];
$sizes = MessagePack::unpack($sizes);
foreach($sizes as $id => $meta) {
if(isset($meta[3]) && !$meta[3]) {
foreach ($sizes as $id => $meta) {
if (isset($meta[3]) && !$meta[3]) {
$res[$id] = (object) [
"url" => ovk_scheme(true) . $_SERVER["HTTP_HOST"] . "/photos/thumbnails/" . $this->getId() . "_$id.jpeg",
"width" => NULL,
"height" => NULL,
"crop" => NULL
"width" => null,
"height" => null,
"crop" => null,
];
continue;
}
$url = $this->getURL();
$url = str_replace(".$this->fileExtension", "_cropped/$id.", $url);
$url .= ($meta[1] <= 300 || $meta[2] <= 300) ? "gif" : "jpeg";
@ -183,7 +203,7 @@ class Photo extends Media
"url" => $url,
"width" => $meta[1],
"height" => $meta[2],
"crop" => $meta[0]
"crop" => $meta[0],
];
}
@ -192,69 +212,78 @@ class Photo extends Media
"url" => $this->getURL(),
"width" => $x,
"height" => $y,
"crop" => false
"crop" => false,
];
return $res;
}
function forceSize(string $sizeName): bool
public function forceSize(string $sizeName): bool
{
$hash = $this->getRecord()->hash;
$sizes = MessagePack::unpack($this->getRecord()->sizes);
$size = $sizes[$sizeName] ?? false;
if(!$size)
if (!$size) {
return $size;
if(!isset($size[3]) || $size[3] === true)
}
if (!isset($size[3]) || $size[3] === true) {
return true;
}
$path = $this->pathFromHash($hash);
$dir = dirname($this->pathFromHash($hash));
$dir = "$dir/$hash" . "_cropped";
if(!is_dir($dir)) {
if (!is_dir($dir)) {
@unlink($dir);
mkdir($dir);
}
$sizeMetas = simplexml_load_file(OPENVK_ROOT . "/data/photosizes.xml");
if(!$sizeMetas)
if (!$sizeMetas) {
throw new \RuntimeException("Could not load photosizes.xml!");
$sizeInfo = NULL;
foreach($sizeMetas->Size as $size)
if($size["id"] == $sizeName)
}
$sizeInfo = null;
foreach ($sizeMetas->Size as $size) {
if ($size["id"] == $sizeName) {
$sizeInfo = $size;
if(!$sizeInfo)
}
}
if (!$sizeInfo) {
return false;
$pic = new \Imagick;
}
$pic = new \Imagick();
$pic->readImage($path);
$sizes[$sizeName] = $this->resizeImage($pic, $dir, $sizeInfo);
$this->stateChanges("sizes", MessagePack::pack($sizes));
$this->save();
return $sizes[$sizeName][3];
}
function getVkApiSizes(): ?array
public function getVkApiSizes(): ?array
{
$res = [];
$sizes = $this->getSizes();
if(!$sizes)
return NULL;
if (!$sizes) {
return null;
}
$manifest = simplexml_load_file(OPENVK_ROOT . "/data/photosizes.xml");
if(!$manifest)
return NULL;
if (!$manifest) {
return null;
}
$mappings = [];
foreach($manifest->Size as $size)
foreach ($manifest->Size as $size) {
$mappings[(string) $size["id"]] = (string) $size["vkId"];
}
foreach($sizes as $id => $meta) {
foreach ($sizes as $id => $meta) {
$type = $mappings[$id] ?? $id;
$meta->type = $type;
$res[$type] = $meta;
@ -263,24 +292,26 @@ class Photo extends Media
return $res;
}
function getURLBySizeId(string $size): string
public function getURLBySizeId(string $size): string
{
$sizes = $this->getSizes();
if(!$sizes)
if (!$sizes) {
return $this->getURL();
}
$size = $sizes[$size];
if(!$size)
if (!$size) {
return $this->getURL();
}
return $size->url;
}
function getDimensions(): array
public function getDimensions(): array
{
$x = $this->getRecord()->width;
$y = $this->getRecord()->height;
if(!$x) { # no sizes in database
if (!$x) { # no sizes in database
$hash = $this->getRecord()->hash;
$image = Image::fromFile($this->pathFromHash($hash));
@ -294,31 +325,32 @@ class Photo extends Media
return [$x, $y];
}
function getPageURL(): string
public function getPageURL(): string
{
if($this->isAnonymous())
if ($this->isAnonymous()) {
return "/photos/" . base_convert((string) $this->getId(), 10, 32);
}
return "/photo" . $this->getPrettyId();
}
function getAlbum(): ?Album
public function getAlbum(): ?Album
{
return (new Albums)->getAlbumByPhotoId($this);
return (new Albums())->getAlbumByPhotoId($this);
}
function toVkApiStruct(bool $photo_sizes = true, bool $extended = false): object
public function toVkApiStruct(bool $photo_sizes = true, bool $extended = false): object
{
$res = (object) [];
$res->id = $res->pid = $this->getVirtualId();
$res->owner_id = $res->user_id = $this->getOwner()->getId();
$res->aid = $res->album_id = NULL;
$res->aid = $res->album_id = null;
$res->width = $this->getDimensions()[0];
$res->height = $this->getDimensions()[1];
$res->date = $res->created = $this->getPublicationTime()->timestamp();
if($photo_sizes) {
if ($photo_sizes) {
$res->sizes = array_values($this->getVkApiSizes());
$res->src_small = $res->photo_75 = $this->getURLBySizeId("miniscule");
$res->src = $res->photo_130 = $this->getURLBySizeId("tiny");
@ -329,7 +361,7 @@ class Photo extends Media
$res->src_original = $res->url = $this->getURLBySizeId("UPLOADED_MAXRES");
}
if($extended) {
if ($extended) {
$res->likes = $this->getLikesCount(); # их нету но пусть будут
$res->comments = $this->getCommentsCount();
$res->tags = 0;
@ -339,23 +371,23 @@ class Photo extends Media
return $res;
}
function canBeViewedBy(?User $user = NULL): bool
public function canBeViewedBy(?User $user = null): bool
{
if($this->isDeleted() || $this->getOwner()->isDeleted()) {
if ($this->isDeleted() || $this->getOwner()->isDeleted()) {
return false;
}
if(!is_null($this->getAlbum())) {
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
public static function fastMake(int $owner, string $description = "", array $file, ?Album $album = null, bool $anon = false): Photo
{
$photo = new static;
$photo = new static();
$photo->setOwner($owner);
$photo->setDescription(iconv_substr($description, 0, 36) . "...");
$photo->setAnonymous($anon);
@ -363,7 +395,7 @@ class Photo extends Media
$photo->setFile($file);
$photo->save();
if(!is_null($album)) {
if (!is_null($album)) {
$album->addPhoto($photo);
$album->setEdited(time());
$album->save();
@ -372,10 +404,10 @@ class Photo extends Media
return $photo;
}
function toNotifApiStruct()
public function toNotifApiStruct()
{
$res = (object)[];
$res = (object) [];
$res->id = $this->getVirtualId();
$res->owner_id = $this->getOwner()->getId();
$res->aid = 0;

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?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;
@ -21,64 +25,68 @@ class Playlist extends MediaCollection
private $importTable;
const MAX_COUNT = 1000;
const MAX_ITEMS = 10000;
public const MAX_COUNT = 1000;
public const MAX_ITEMS = 10000;
function __construct(?ActiveRow $ar = NULL)
public function __construct(?ActiveRow $ar = null)
{
parent::__construct($ar);
$this->importTable = DatabaseConnection::i()->getContext()->table("playlist_imports");
}
function getCoverURL(string $size = "normal"): ?string
public function getCoverURL(string $size = "normal"): ?string
{
$photo = (new Photos)->get((int) $this->getRecord()->cover_photo_id);
$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
public function getLength(): int
{
return $this->getRecord()->length;
}
function fetchClassic(int $offset = 0, ?int $limit = NULL): \Traversable
public 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) {
foreach ($related as $rel) {
$media = $rel->ref($this->entityTableName, "media");
if(!$media)
if (!$media) {
continue;
}
yield new $this->entityClassName($media);
}
}
function getAudios(int $offset = 0, ?int $limit = NULL, ?int $shuffleSeed = NULL): \Traversable
public 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 []
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)
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);
foreach ($ids as $id) {
yield (new Audios())->get($id);
}
}
function add(RowModel $audio): bool
public function add(RowModel $audio): bool
{
if($res = parent::add($audio)) {
if ($res = parent::add($audio)) {
$this->stateChanges("length", $this->getRecord()->length + $audio->getLength());
$this->save();
}
@ -86,9 +94,9 @@ class Playlist extends MediaCollection
return $res;
}
function remove(RowModel $audio): bool
public function remove(RowModel $audio): bool
{
if($res = parent::remove($audio)) {
if ($res = parent::remove($audio)) {
$this->stateChanges("length", $this->getRecord()->length - $audio->getLength());
$this->save();
}
@ -96,11 +104,12 @@ class Playlist extends MediaCollection
return $res;
}
function isBookmarkedBy(RowModel $entity): bool
public function isBookmarkedBy(RowModel $entity): bool
{
$id = $entity->getId();
if($entity instanceof Club)
if ($entity instanceof Club) {
$id *= -1;
}
return !is_null($this->importTable->where([
"entity" => $id,
@ -108,17 +117,20 @@ class Playlist extends MediaCollection
])->fetch());
}
function bookmark(RowModel $entity): bool
public function bookmark(RowModel $entity): bool
{
if($this->isBookmarkedBy($entity))
if ($this->isBookmarkedBy($entity)) {
return false;
}
$id = $entity->getId();
if($entity instanceof Club)
if ($entity instanceof Club) {
$id *= -1;
}
if($this->importTable->where("entity", $id)->count() > self::MAX_COUNT)
if ($this->importTable->where("entity", $id)->count() > self::MAX_COUNT) {
throw new \OutOfBoundsException("Maximum amount of playlists");
}
$this->importTable->insert([
"entity" => $id,
@ -128,11 +140,12 @@ class Playlist extends MediaCollection
return true;
}
function unbookmark(RowModel $entity): bool
public function unbookmark(RowModel $entity): bool
{
$id = $entity->getId();
if($entity instanceof Club)
if ($entity instanceof Club) {
$id *= -1;
}
$count = $this->importTable->where([
"entity" => $id,
@ -141,27 +154,28 @@ class Playlist extends MediaCollection
return $count > 0;
}
function getDescription(): ?string
public function getDescription(): ?string
{
return $this->getRecord()->description;
}
function getDescriptionHTML(): ?string
public function getDescriptionHTML(): ?string
{
return htmlspecialchars($this->getRecord()->description, ENT_DISALLOWED | ENT_XHTML);
}
function getListens()
public function getListens()
{
return $this->getRecord()->listens;
}
function toVkApiStruct(?User $user = NULL): object
public function toVkApiStruct(?User $user = null): object
{
$oid = $this->getOwner()->getId();
if($this->getOwner() instanceof Club)
if ($this->getOwner() instanceof Club) {
$oid *= -1;
}
return (object) [
"id" => $this->getId(),
@ -171,7 +185,7 @@ class Playlist extends MediaCollection
"size" => $this->size(),
"length" => $this->getLength(),
"created" => $this->getCreationTime()->timestamp(),
"modified" => $this->getEditTime() ? $this->getEditTime()->timestamp() : NULL,
"modified" => $this->getEditTime() ? $this->getEditTime()->timestamp() : null,
"accessible" => $this->canBeViewedBy($user),
"editable" => $this->canBeModifiedBy($user),
"bookmarked" => $this->isBookmarkedBy($user),
@ -181,19 +195,19 @@ class Playlist extends MediaCollection
];
}
function setLength(): void
public function setLength(): void
{
throw new \LogicException("Can't set length of playlist manually");
}
function resetLength(): bool
public function resetLength(): bool
{
$this->stateChanges("length", 0);
return true;
}
function delete(bool $softly = true): void
public function delete(bool $softly = true): void
{
$ctx = DatabaseConnection::i()->getContext();
$ctx->table("playlist_imports")->where("playlist", $this->getId())
@ -202,44 +216,46 @@ class Playlist extends MediaCollection
parent::delete($softly);
}
function hasAudio(Audio $audio): bool
public function hasAudio(Audio $audio): bool
{
$ctx = DatabaseConnection::i()->getContext();
return !is_null($ctx->table("playlist_relations")->where([
"collection" => $this->getId(),
"media" => $audio->getId()
"media" => $audio->getId(),
])->fetch());
}
function getCoverPhotoId(): ?int
public function getCoverPhotoId(): ?int
{
return $this->getRecord()->cover_photo_id;
}
function getCoverPhoto(): ?Photo
public function getCoverPhoto(): ?Photo
{
return (new Photos)->get((int) $this->getRecord()->cover_photo_id);
return (new Photos())->get((int) $this->getRecord()->cover_photo_id);
}
function canBeModifiedBy(User $user): bool
public function canBeModifiedBy(User $user): bool
{
if(!$user)
if (!$user) {
return false;
}
if($this->getOwner() instanceof User)
if ($this->getOwner() instanceof User) {
return $user->getId() == $this->getOwner()->getId();
else
} else {
return $this->getOwner()->canBeModifiedBy($user);
}
}
function getLengthInMinutes(): int
public function getLengthInMinutes(): int
{
return (int)round($this->getLength() / 60, PHP_ROUND_HALF_DOWN);
return (int) round($this->getLength() / 60, PHP_ROUND_HALF_DOWN);
}
function fastMakeCover(int $owner, array $file)
public function fastMakeCover(int $owner, array $file)
{
$cover = new Photo;
$cover = new Photo();
$cover->setOwner($owner);
$cover->setDescription("Playlist cover image");
$cover->setFile($file);
@ -251,32 +267,34 @@ class Playlist extends MediaCollection
return $cover;
}
function getURL(): string
public function getURL(): string
{
return "/playlist" . $this->getOwner()->getRealId() . "_" . $this->getId();
}
function incrementListens()
public function incrementListens()
{
$this->stateChanges("listens", ($this->getListens() + 1));
}
function getMetaDescription(): string
public 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);
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
public function isUnlisted(): bool
{
return (bool)$this->getRecord()->unlisted;
return (bool) $this->getRecord()->unlisted;
}
}

View file

@ -1,8 +1,12 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\Exceptions\TooMuchOptionsException;
use openvk\Web\Util\DateTime;
use \UnexpectedValueException;
use UnexpectedValueException;
use Nette\InvalidStateException;
use openvk\Web\Models\Repositories\{Users, Posts};
use Chandler\Database\DatabaseConnection;
@ -13,178 +17,198 @@ use openvk\Web\Models\Exceptions\InvalidOptionException;
class Poll extends Attachable
{
protected $tableName = "polls";
private $choicesToPersist = [];
function getTitle(): string
public function getTitle(): string
{
return $this->getRecord()->title;
}
function getMetaDescription(): string
public function getMetaDescription(): string
{
$props = [];
$props[] = tr($this->isAnonymous() ? "poll_anon" : "poll_public");
if($this->isMultipleChoice()) $props[] = tr("poll_multi");
if(!$this->isRevotable()) $props[] = tr("poll_lock");
if(!is_null($this->endsAt())) $props[] = tr("poll_until", $this->endsAt());
if ($this->isMultipleChoice()) {
$props[] = tr("poll_multi");
}
if (!$this->isRevotable()) {
$props[] = tr("poll_lock");
}
if (!is_null($this->endsAt())) {
$props[] = tr("poll_until", $this->endsAt());
}
return implode("", $props);
}
function getOwner(): User
public function getOwner(): User
{
return (new Users)->get($this->getRecord()->owner);
return (new Users())->get($this->getRecord()->owner);
}
function getOptions(): array
public function getOptions(): array
{
$options = $this->getRecord()->related("poll_options.poll");
$res = [];
foreach($options as $opt)
foreach ($options as $opt) {
$res[$opt->id] = $opt->name;
}
return $res;
}
function getUserVote(User $user): ?array
public function getUserVote(User $user): ?array
{
$ctx = DatabaseConnection::i()->getContext();
$votedOpts = $ctx->table("poll_votes")
->where(["user" => $user->getId(), "poll" => $this->getId()]);
if($votedOpts->count() == 0)
return NULL;
if ($votedOpts->count() == 0) {
return null;
}
$res = [];
foreach($votedOpts as $votedOpt) {
foreach ($votedOpts as $votedOpt) {
$option = $ctx->table("poll_options")->get($votedOpt->option);
$res[] = [$option->id, $option->name];
}
return $res;
}
function getVoters(int $optionId, int $page = 1, ?int $perPage = NULL): array
public function getVoters(int $optionId, int $page = 1, ?int $perPage = null): array
{
$res = [];
$ctx = DatabaseConnection::i()->getContext();
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
$voters = $ctx->table("poll_votes")->where(["poll" => $this->getId(), "option" => $optionId]);
foreach($voters->page($page, $perPage) as $vote)
$res[] = (new Users)->get($vote->user);
foreach ($voters->page($page, $perPage) as $vote) {
$res[] = (new Users())->get($vote->user);
}
return $res;
}
function getVoterCount(?int $optionId = NULL): int
public function getVoterCount(?int $optionId = null): int
{
$votes = DatabaseConnection::i()->getContext()->table("poll_votes");
if(!$optionId)
if (!$optionId) {
return $votes->select("COUNT(DISTINCT user) AS c")->where("poll", $this->getId())->fetch()->c;
}
return $votes->where(["poll" => $this->getId(), "option" => $optionId])->count();
}
function getResults(?User $user = NULL): object
public function getResults(?User $user = null): object
{
$ctx = DatabaseConnection::i()->getContext();
$voted = NULL;
if(!is_null($user))
$voted = null;
if (!is_null($user)) {
$voted = $this->getUserVote($user);
}
$result = (object) [];
$result->totalVotes = $this->getVoterCount();
$unsOptions = [];
foreach($this->getOptions() as $id => $title) {
foreach ($this->getOptions() as $id => $title) {
$option = (object) [];
$option->id = $id;
$option->name = $title;
$option->votes = $this->getVoterCount($id);
$option->pct = $result->totalVotes == 0 ? 0 : min(100, floor(($option->votes / $result->totalVotes) * 100));
$option->voters = $this->getVoters($id, 1, 10);
if(!$user || !$voted)
$option->voted = NULL;
else
if (!$user || !$voted) {
$option->voted = null;
} else {
$option->voted = in_array([$id, $title], $voted);
}
$unsOptions[$id] = $option;
}
$optionsC = sizeof($unsOptions);
$sOptions = $unsOptions;
usort($sOptions, function($a, $b) { return $a->votes <=> $b->votes; });
for($i = 0; $i < $optionsC; $i++)
usort($sOptions, function ($a, $b) { return $a->votes <=> $b->votes; });
for ($i = 0; $i < $optionsC; $i++) {
$unsOptions[$id]->rate = $optionsC - $i - 1;
}
$result->options = array_values($unsOptions);
return $result;
}
function isAnonymous(): bool
public function isAnonymous(): bool
{
return (bool) $this->getRecord()->is_anonymous;
}
function isMultipleChoice(): bool
public function isMultipleChoice(): bool
{
return (bool) $this->getRecord()->allows_multiple;
}
function isRevotable(): bool
public function isRevotable(): bool
{
return (bool) $this->getRecord()->can_revote;
}
function endsAt(): ?DateTime
public function endsAt(): ?DateTime
{
if(!$this->getRecord()->until)
return NULL;
if (!$this->getRecord()->until) {
return null;
}
return new DateTime($this->getRecord()->until);
}
function hasEnded(): bool
public function hasEnded(): bool
{
if($this->getRecord()->ended)
if ($this->getRecord()->ended) {
return true;
if(!is_null($this->getRecord()->until))
}
if (!is_null($this->getRecord()->until)) {
return time() >= $this->getRecord()->until;
}
return false;
}
function hasVoted(User $user): bool
public function hasVoted(User $user): bool
{
return !is_null($this->getUserVote($user));
}
function canVote(User $user): bool
public function canVote(User $user): bool
{
return !$this->hasEnded() && !$this->hasVoted($user) && !is_null($this->getAttachedPost()) && $this->getAttachedPost()->getSuggestionType() == 0;
}
function vote(User $user, array $optionIds): void
public function vote(User $user, array $optionIds): void
{
if($this->hasEnded())
throw new PollLockedException;
if($this->hasVoted($user))
throw new AlreadyVotedException;
$optionIds = array_map(function($x) { return (int) $x; }, array_unique($optionIds));
if ($this->hasEnded()) {
throw new PollLockedException();
}
if ($this->hasVoted($user)) {
throw new AlreadyVotedException();
}
$optionIds = array_map(function ($x) { return (int) $x; }, array_unique($optionIds));
$validOpts = array_keys($this->getOptions());
if(empty($optionIds) || (sizeof($optionIds) > 1 && !$this->isMultipleChoice()))
throw new UnexpectedValueException;
if(sizeof(array_diff($optionIds, $validOpts)) > 0)
throw new InvalidOptionException;
foreach($optionIds as $opt) {
if (empty($optionIds) || (sizeof($optionIds) > 1 && !$this->isMultipleChoice())) {
throw new UnexpectedValueException();
}
if (sizeof(array_diff($optionIds, $validOpts)) > 0) {
throw new InvalidOptionException();
}
foreach ($optionIds as $opt) {
DatabaseConnection::i()->getContext()->table("poll_votes")->insert([
"user" => $user->getId(),
"poll" => $this->getId(),
@ -192,64 +216,69 @@ class Poll extends Attachable
]);
}
}
function revokeVote(User $user): void
public function revokeVote(User $user): void
{
if(!$this->isRevotable())
throw new PollLockedException;
if (!$this->isRevotable()) {
throw new PollLockedException();
}
$this->getRecord()->related("poll_votes.poll")
->where("user", $user->getId())->delete();
}
function setOwner(User $owner): void
public function setOwner(User $owner): void
{
$this->stateChanges("owner", $owner->getId());
}
function setEndDate(int $timestamp): void
public function setEndDate(int $timestamp): void
{
if(!is_null($this->getRecord()))
throw new PollLockedException;
if (!is_null($this->getRecord())) {
throw new PollLockedException();
}
$this->stateChanges("until", $timestamp);
}
function setEnded(): void
public function setEnded(): void
{
$this->stateChanges("ended", 1);
}
function setOptions(array $options): void
public function setOptions(array $options): void
{
if(!is_null($this->getRecord()))
throw new PollLockedException;
if(sizeof($options) > ovkGetQuirk("polls.max-opts"))
throw new TooMuchOptionsException;
if (!is_null($this->getRecord())) {
throw new PollLockedException();
}
if (sizeof($options) > ovkGetQuirk("polls.max-opts")) {
throw new TooMuchOptionsException();
}
$this->choicesToPersist = $options;
}
function setRevotability(bool $canReVote): void
public function setRevotability(bool $canReVote): void
{
if(!is_null($this->getRecord()))
throw new PollLockedException;
if (!is_null($this->getRecord())) {
throw new PollLockedException();
}
$this->stateChanges("can_revote", $canReVote);
}
function setAnonymity(bool $anonymous): void
public function setAnonymity(bool $anonymous): void
{
$this->stateChanges("is_anonymous", $anonymous);
}
function setMultipleChoice(bool $mc): void
public function setMultipleChoice(bool $mc): void
{
$this->stateChanges("allows_multiple", $mc);
}
function importXML(User $owner, string $xml): void
public function importXML(User $owner, string $xml): void
{
$xml = simplexml_load_string($xml);
$this->setOwner($owner);
@ -257,46 +286,50 @@ class Poll extends Attachable
$this->setMultipleChoice(($xml["multiple"] ?? "no") == "yes");
$this->setAnonymity(($xml["anonymous"] ?? "no") == "yes");
$this->setRevotability(($xml["locked"] ?? "no") == "no");
if(ctype_digit((string) ($xml["duration"] ?? "")))
if (ctype_digit((string) ($xml["duration"] ?? ""))) {
$this->setEndDate(time() + ((86400 * (int) $xml["duration"])));
}
$options = [];
foreach($xml->options->option as $opt)
foreach ($xml->options->option as $opt) {
$options[] = (string) $opt;
if(empty($options))
throw new UnexpectedValueException;
}
if (empty($options)) {
throw new UnexpectedValueException();
}
$this->setOptions($options);
}
static function import(User $owner, string $xml): Poll
public static function import(User $owner, string $xml): Poll
{
$poll = new Poll;
$poll = new Poll();
$poll->importXML($owner, $xml);
$poll->save();
return $poll;
}
function canBeViewedBy(?User $user = NULL): bool
public function canBeViewedBy(?User $user = null): bool
{
# waiting for #935 :(
/*if(!is_null($this->getAttachedPost())) {
return $this->getAttachedPost()->canBeViewedBy($user);
} else {*/
return true;
return true;
#}
}
function save(?bool $log = false): void
public function save(?bool $log = false): void
{
if(empty($this->choicesToPersist))
throw new InvalidStateException;
if (empty($this->choicesToPersist)) {
throw new InvalidStateException();
}
parent::save($log);
foreach($this->choicesToPersist as $option) {
foreach ($this->choicesToPersist as $option) {
DatabaseConnection::i()->getContext()->table("poll_options")->insert([
"poll" => $this->getId(),
"name" => $option,
@ -304,16 +337,18 @@ class Poll extends Attachable
}
}
function getAttachedPost()
public function getAttachedPost()
{
$post = DatabaseConnection::i()->getContext()->table("attachments")
->where(
["attachable_type" => static::class,
"attachable_id" => $this->getId()])->fetch();
["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;
if (!is_null($post->target_id)) {
return (new Posts())->get($post->target_id);
} else {
return null;
}
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use Chandler\Database\DatabaseConnection as DB;
use openvk\Web\Models\Repositories\{Clubs, Users};
use openvk\Web\Models\RowModel;
@ -7,6 +11,7 @@ use openvk\Web\Models\Entities\Notifications\LikeNotification;
class Post extends Postable
{
use Traits\TRichText;
protected $tableName = "posts";
protected $upperNodeReferenceColumnName = "wall";
@ -18,54 +23,60 @@ class Post extends Postable
"target" => $this->getRecord()->id,
];
if((sizeof(DB::i()->getContext()->table("likes")->where($searchData)) > 0) !== $liked) {
if($this->getOwner(false)->getId() !== $user->getId() && !($this->getOwner() instanceof Club) && !$this instanceof Comment)
if ((sizeof(DB::i()->getContext()->table("likes")->where($searchData)) > 0) !== $liked) {
if ($this->getOwner(false)->getId() !== $user->getId() && !($this->getOwner() instanceof Club) && !$this instanceof Comment) {
(new LikeNotification($this->getOwner(false), $this, $user))->emit();
}
parent::setLike($liked, $user);
}
if($depth < ovkGetQuirk("wall.repost-liking-recursion-limit"))
foreach($this->getChildren() as $attachment)
if($attachment instanceof Post)
if ($depth < ovkGetQuirk("wall.repost-liking-recursion-limit")) {
foreach ($this->getChildren() as $attachment) {
if ($attachment instanceof Post) {
$attachment->setLikeRecursively($liked, $user, $depth + 1);
}
}
}
}
/**
* May return fake owner (group), if flags are [1, (*)]
*
*
* @param bool $honourFlags - check flags
*/
function getOwner(bool $honourFlags = true, bool $real = false): RowModel
public function getOwner(bool $honourFlags = true, bool $real = false): RowModel
{
if($honourFlags && $this->isPostedOnBehalfOfGroup()) {
if($this->getRecord()->wall < 0)
return (new Clubs)->get(abs($this->getRecord()->wall));
if ($honourFlags && $this->isPostedOnBehalfOfGroup()) {
if ($this->getRecord()->wall < 0) {
return (new Clubs())->get(abs($this->getRecord()->wall));
}
}
return parent::getOwner($real);
}
function getPrettyId(): string
public function getPrettyId(): string
{
return $this->getRecord()->wall . "_" . $this->getVirtualId();
}
function getTargetWall(): int
public function getTargetWall(): int
{
return $this->getRecord()->wall;
}
function getWallOwner()
public function getWallOwner()
{
$w = $this->getRecord()->wall;
if($w < 0)
return (new Clubs)->get(abs($w));
if ($w < 0) {
return (new Clubs())->get(abs($w));
}
return (new Users)->get($w);
return (new Users())->get($w);
}
function getRepostCount(): int
public function getRepostCount(): int
{
return sizeof(
$this->getRecord()
@ -73,95 +84,97 @@ class Post extends Postable
->where("attachable_type", get_class($this))
);
}
function isPinned(): bool
public function isPinned(): bool
{
return (bool) $this->getRecord()->pinned;
}
function hasSource(): bool
public function hasSource(): bool
{
return $this->getRecord()->source != NULL;
return $this->getRecord()->source != null;
}
function getSource(bool $format = false)
public function getSource(bool $format = false)
{
$orig_source = $this->getRecord()->source;
if(!str_contains($orig_source, "https://") && !str_contains($orig_source, "http://"))
if (!str_contains($orig_source, "https://") && !str_contains($orig_source, "http://")) {
$orig_source = "https://" . $orig_source;
}
if(!$format)
if (!$format) {
return $orig_source;
}
return $this->formatLinks($orig_source);
}
function setSource(string $source)
public function setSource(string $source)
{
$result = check_copyright_link($source);
$this->stateChanges("source", $source);
}
function resetSource()
public function resetSource()
{
$this->stateChanges("source", NULL);
$this->stateChanges("source", null);
}
function getVkApiCopyright(): object
public function getVkApiCopyright(): object
{
return (object)[
return (object) [
'id' => 0,
'link' => $this->getSource(false),
'name' => $this->getSource(false),
'type' => 'link',
];
}
function isAd(): bool
public function isAd(): bool
{
return (bool) $this->getRecord()->ad;
}
function isPostedOnBehalfOfGroup(): bool
public function isPostedOnBehalfOfGroup(): bool
{
return ($this->getRecord()->flags & 0b10000000) > 0;
}
function isSigned(): bool
public function isSigned(): bool
{
return ($this->getRecord()->flags & 0b01000000) > 0;
}
function isDeactivationMessage(): bool
public function isDeactivationMessage(): bool
{
return (($this->getRecord()->flags & 0b00100000) > 0) && ($this->getRecord()->owner > 0);
}
function isUpdateAvatarMessage(): bool
public function isUpdateAvatarMessage(): bool
{
return (($this->getRecord()->flags & 0b00010000) > 0) && ($this->getRecord()->owner > 0);
}
function isExplicit(): bool
public function isExplicit(): bool
{
return (bool) $this->getRecord()->nsfw;
}
function isDeleted(): bool
public function isDeleted(): bool
{
return (bool) $this->getRecord()->deleted;
}
function getOwnerPost(): int
public function getOwnerPost(): int
{
return $this->getOwner(false)->getId();
}
function getPlatform(bool $forAPI = false): ?string
public function getPlatform(bool $forAPI = false): ?string
{
$platform = $this->getRecord()->api_source_name;
if($forAPI) {
if ($forAPI) {
switch ($platform) {
case 'openvk_refresh_android':
case 'openvk_legacy_android':
@ -176,16 +189,16 @@ class Post extends Postable
case 'windows_phone':
return 'wphone';
break;
case 'vika_touch': // кика хохотач ахахахаххахахахахах
case 'vk4me':
return 'mobile';
break;
case NULL:
return NULL;
case null:
return null;
break;
default:
return 'api';
break;
@ -195,17 +208,17 @@ class Post extends Postable
}
}
function getPlatformDetails(): array
public function getPlatformDetails(): array
{
$clients = simplexml_load_file(OPENVK_ROOT . "/data/clients.xml");
foreach($clients as $client) {
if($client['tag'] == $this->getPlatform()) {
foreach ($clients as $client) {
if ($client['tag'] == $this->getPlatform()) {
return [
"tag" => $client['tag'],
"name" => $client['name'],
"url" => $client['url'],
"img" => $client['img']
"img" => $client['img'],
];
break;
}
@ -213,38 +226,40 @@ class Post extends Postable
return [
"tag" => $this->getPlatform(),
"name" => NULL,
"url" => NULL,
"img" => NULL
"name" => null,
"url" => null,
"img" => null,
];
}
function getPostSourceInfo(): array
public function getPostSourceInfo(): array
{
$post_source = ["type" => "vk"];
if($this->getPlatform(true) !== NULL) {
if ($this->getPlatform(true) !== null) {
$post_source = [
"type" => "api",
"platform" => $this->getPlatform(true)
"platform" => $this->getPlatform(true),
];
}
if($this->isUpdateAvatarMessage())
if ($this->isUpdateAvatarMessage()) {
$post_source['data'] = 'profile_photo';
}
return $post_source;
}
function getVkApiType(): string
public function getVkApiType(): string
{
$type = 'post';
if($this->getSuggestionType() != 0)
if ($this->getSuggestionType() != 0) {
$type = 'suggest';
}
return $type;
}
function pin(): void
public function pin(): void
{
DB::i()
->getContext()
@ -254,98 +269,106 @@ class Post extends Postable
"pinned" => true,
])
->update(["pinned" => false]);
$this->stateChanges("pinned", true);
$this->save();
}
function unpin(): void
public function unpin(): void
{
$this->stateChanges("pinned", false);
$this->save();
}
function canBePinnedBy(User $user = NULL): bool
{
if(!$user)
return false;
if($this->getTargetWall() < 0)
return (new Clubs)->get(abs($this->getTargetWall()))->canBeModifiedBy($user);
public function canBePinnedBy(User $user = null): bool
{
if (!$user) {
return false;
}
if ($this->getTargetWall() < 0) {
return (new Clubs())->get(abs($this->getTargetWall()))->canBeModifiedBy($user);
}
return $this->getTargetWall() === $user->getId();
}
function canBeDeletedBy(User $user = NULL): bool
public function canBeDeletedBy(User $user = null): bool
{
if(!$user)
if (!$user) {
return false;
if($this->getTargetWall() < 0 && !$this->getWallOwner()->canBeModifiedBy($user) && $this->getWallOwner()->getWallType() != 1 && $this->getSuggestionType() == 0)
}
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);
}
function setContent(string $content): void
public function setContent(string $content): void
{
if(ctype_space($content))
if (ctype_space($content)) {
throw new \LengthException("Content length must be at least 1 character (not counting whitespaces).");
else if(iconv_strlen($content) > OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxSize"])
} elseif (iconv_strlen($content) > OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxSize"]) {
throw new \LengthException("Content is too large.");
}
$this->stateChanges("content", $content);
}
function toggleLike(User $user): bool
public function toggleLike(User $user): bool
{
$liked = parent::toggleLike($user);
if(!$user->isPrivateLikes() && $this->getOwner(false)->getId() !== $user->getId() && !($this->getOwner() instanceof Club) && !$this instanceof Comment)
if (!$user->isPrivateLikes() && $this->getOwner(false)->getId() !== $user->getId() && !($this->getOwner() instanceof Club) && !$this instanceof Comment) {
(new LikeNotification($this->getOwner(false), $this, $user))->emit();
}
foreach($this->getChildren() as $attachment)
if($attachment instanceof Post)
foreach ($this->getChildren() as $attachment) {
if ($attachment instanceof Post) {
$attachment->setLikeRecursively($liked, $user, 2);
}
}
return $liked;
}
function setLike(bool $liked, User $user): void
public function setLike(bool $liked, User $user): void
{
$this->setLikeRecursively($liked, $user, 1);
}
function deletePost(): void
public function deletePost(): void
{
$this->setDeleted(1);
$this->unwire();
$this->save();
}
function canBeViewedBy(?User $user = NULL): bool
public function canBeViewedBy(?User $user = null): bool
{
if($this->isDeleted()) {
if ($this->isDeleted()) {
return false;
}
return $this->getWallOwner()->canBeViewedBy($user);
}
function getSuggestionType()
public function getSuggestionType()
{
return $this->getRecord()->suggested;
}
function getPageURL(): string
public function getPageURL(): string
{
return "/wall".$this->getPrettyId();
return "/wall" . $this->getPrettyId();
}
function toNotifApiStruct()
public function toNotifApiStruct()
{
$res = (object)[];
$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;
@ -353,97 +376,105 @@ class Post extends Postable
$res->text = $this->getText(false);
$res->attachments = []; # todo
$res->copy_owner_id = NULL; # todo
$res->copy_post_id = NULL; # todo
$res->copy_owner_id = null; # todo
$res->copy_post_id = null; # todo
return $res;
}
function canBeEditedBy(?User $user = NULL): bool
public function canBeEditedBy(?User $user = null): bool
{
if(!$user)
if (!$user) {
return false;
}
if($this->isDeactivationMessage() || $this->isUpdateAvatarMessage())
if ($this->isDeactivationMessage() || $this->isUpdateAvatarMessage()) {
return false;
}
if($this->getTargetWall() > 0)
if ($this->getTargetWall() > 0) {
return $this->getPublicationTime()->timestamp() + WEEK > time() && $user->getId() == $this->getOwner(false)->getId();
else {
if($this->isPostedOnBehalfOfGroup())
} else {
if ($this->isPostedOnBehalfOfGroup()) {
return $this->getWallOwner()->canBeModifiedBy($user);
else
} else {
return $user->getId() == $this->getOwner(false)->getId();
}
}
return $user->getId() == $this->getOwner(false)->getId();
}
function toRss(): \Bhaktaraz\RSSGenerator\Item
public function toRss(): \Bhaktaraz\RSSGenerator\Item
{
$domain = ovk_scheme(true).$_SERVER["HTTP_HOST"];
$domain = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
$description = $this->getText(false);
$title = str_replace("\n", "", ovk_proc_strtr($description, 79));
$description_html = $description;
$url = $domain."/wall".$this->getPrettyId();
$url = $domain . "/wall" . $this->getPrettyId();
if($this->isUpdateAvatarMessage())
if ($this->isUpdateAvatarMessage()) {
$title = tr('upd_in_general');
if($this->isDeactivationMessage())
}
if ($this->isDeactivationMessage()) {
$title = tr('post_deact_in_general');
}
$author = $this->getOwner();
$target_wall = $this->getWallOwner();
$author_name = escape_html($author->getCanonicalName());
if($this->isExplicit())
if ($this->isExplicit()) {
$title = 'NSFW: ' . $title;
}
foreach($this->getChildren() as $child) {
if($child instanceof Photo) {
$child_page = $domain.$child->getPageURL();
foreach ($this->getChildren() as $child) {
if ($child instanceof Photo) {
$child_page = $domain . $child->getPageURL();
$child_url = $child->getURL();
$description_html .= "<br /><a href='$child_page'><img src='$child_url'></a><br />";
} elseif($child instanceof Video) {
$child_page = $domain.'/video'.$child->getPrettyId();
} elseif ($child instanceof Video) {
$child_page = $domain . '/video' . $child->getPrettyId();
if($child->getType() != 1) {
$description_html .= "".
"<br />".
"<video width=\"320\" height=\"240\" controls><source src=\"".$child->getURL()."\" type=\"video/mp4\"></video><br />".
"<b>".escape_html($child->getName())."</b><br />";
if ($child->getType() != 1) {
$description_html .= "" .
"<br />" .
"<video width=\"320\" height=\"240\" controls><source src=\"" . $child->getURL() . "\" type=\"video/mp4\"></video><br />" .
"<b>" . escape_html($child->getName()) . "</b><br />";
} else {
$description_html .= "".
"<br />".
"<a href=\"".$child->getVideoDriver()->getURL()."\"><b>".escape_html($child->getName())."</b></a><br />";
$description_html .= "" .
"<br />" .
"<a href=\"" . $child->getVideoDriver()->getURL() . "\"><b>" . escape_html($child->getName()) . "</b></a><br />";
}
} elseif($child instanceof Audio) {
if(!$child->isWithdrawn()) {
} elseif ($child instanceof Audio) {
if (!$child->isWithdrawn()) {
$description_html .= "<br />"
."<b>".escape_html($child->getName())."</b>:"
."<br />"
."<audio controls>"
."<source src=\"".$child->getOriginalURL()."\" type=\"audio/mpeg\"></audio>"
."<br />";
. "<b>" . escape_html($child->getName()) . "</b>:"
. "<br />"
. "<audio controls>"
. "<source src=\"" . $child->getOriginalURL() . "\" type=\"audio/mpeg\"></audio>"
. "<br />";
}
} elseif($child instanceof Poll) {
$description_html .= "<br />".tr('poll').": ".escape_html($child->getTitle());
} elseif($child instanceof Note) {
$description_html .= "<br />".tr('note').": ".escape_html($child->getName());
} elseif ($child instanceof Poll) {
$description_html .= "<br />" . tr('poll') . ": " . escape_html($child->getTitle());
} elseif ($child instanceof Note) {
$description_html .= "<br />" . tr('note') . ": " . escape_html($child->getName());
}
}
$description_html .= "<br />".tr('author').": <img width='15px' src='".$author->getAvatarURL()."'><a href='".$author->getURL()."'>" . $author_name . "</a>";
if($target_wall->getRealId() != $author->getRealId())
$description_html .= "<br />".tr('on_wall').": <img width='15px' src='".$target_wall->getAvatarURL()."'><a href='".$target_wall->getURL()."'>" . escape_html($target_wall->getCanonicalName()) . "</a>";
if($this->isSigned()) {
$signer = $this->getOwner(false);
$description_html .= "<br />".tr('sign_short').": <img width='15px' src='".$signer->getAvatarURL()."'><a href='".$signer->getURL()."'>" . escape_html($signer->getCanonicalName()) . "</a>";
$description_html .= "<br />" . tr('author') . ": <img width='15px' src='" . $author->getAvatarURL() . "'><a href='" . $author->getURL() . "'>" . $author_name . "</a>";
if ($target_wall->getRealId() != $author->getRealId()) {
$description_html .= "<br />" . tr('on_wall') . ": <img width='15px' src='" . $target_wall->getAvatarURL() . "'><a href='" . $target_wall->getURL() . "'>" . escape_html($target_wall->getCanonicalName()) . "</a>";
}
if($this->hasSource())
$description_html .= "<br />".tr('source').": ".escape_html($this->getSource());
if ($this->isSigned()) {
$signer = $this->getOwner(false);
$description_html .= "<br />" . tr('sign_short') . ": <img width='15px' src='" . $signer->getAvatarURL() . "'><a href='" . $signer->getURL() . "'>" . escape_html($signer->getCanonicalName()) . "</a>";
}
if ($this->hasSource()) {
$description_html .= "<br />" . tr('source') . ": " . escape_html($this->getSource());
}
$item = new \Bhaktaraz\RSSGenerator\Item();
$item->title($title)
@ -456,39 +487,41 @@ class Post extends Postable
return $item;
}
function getGeo(): ?object
public function getGeo(): ?object
{
if (!$this->getRecord()->geo) return NULL;
if (!$this->getRecord()->geo) {
return null;
}
return (object) json_decode($this->getRecord()->geo, true, JSON_UNESCAPED_UNICODE);
}
function setGeo($encoded_object): void
public function setGeo($encoded_object): void
{
$final_geo = $encoded_object['name'];
$neutral_names = ["Россия", "Russia", "Росія", "Россія", "Украина", "Ukraine", "Україна", "Украіна"];
foreach($neutral_names as $name) {
if(str_contains($final_geo, $name.", ")) {
$final_geo = str_replace($name.", ", "", $final_geo);
foreach ($neutral_names as $name) {
if (str_contains($final_geo, $name . ", ")) {
$final_geo = str_replace($name . ", ", "", $final_geo);
}
}
$encoded_object['name'] = ovk_proc_strtr($final_geo, 255);
$encoded = json_encode($encoded_object);
$this->stateChanges("geo", $encoded);
}
function getLat(): ?float
public function getLat(): ?float
{
return (float) $this->getRecord()->geo_lat ?? NULL;
return (float) $this->getRecord()->geo_lat ?? null;
}
function getLon(): ?float
public function getLon(): ?float
{
return (float) $this->getRecord()->geo_lon ?? NULL;
return (float) $this->getRecord()->geo_lon ?? null;
}
function getVkApiGeo(): object
public function getVkApiGeo(): object
{
return (object) [
'type' => 'point',
@ -496,6 +529,4 @@ class Post extends Postable
'name' => $this->getGeo()->name,
];
}
use Traits\TRichText;
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Util\DateTime;
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Entities\User;
@ -12,83 +16,89 @@ use Nette\Database\Table\Selection;
abstract class Postable extends Attachable
{
use Traits\TAttachmentHost;
use Traits\TOwnable;
/**
* Column name, that references to an object, that
* is hieararchically higher than this Postable.
*
*
* For example: Images belong to User, but Posts belong to Wall.
* Formally users still own posts, but walls also own posts and they are
* their direct parent.
*
*
* @var string
*/
protected $upperNodeReferenceColumnName = "owner";
private function getTable(): Selection
{
return DB::i()->getContext()->table($this->tableName);
}
function getOwner(bool $real = false): RowModel
public function getOwner(bool $real = false): RowModel
{
$oid = (int) $this->getRecord()->owner;
if(!$real && $this->isAnonymous())
if (!$real && $this->isAnonymous()) {
$oid = (int) OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["account"];
}
$oid = abs($oid);
if($oid > 0)
return (new Users)->get($oid);
else
return (new Clubs)->get($oid * -1);
if ($oid > 0) {
return (new Users())->get($oid);
} else {
return (new Clubs())->get($oid * -1);
}
}
function getVirtualId(): int
public function getVirtualId(): int
{
return $this->getRecord()->virtual_id;
}
function getPrettyId(): string
public function getPrettyId(): string
{
return $this->getRecord()->owner . "_" . $this->getVirtualId();
}
function getPublicationTime(): DateTime
public function getPublicationTime(): DateTime
{
return new DateTime($this->getRecord()->created);
}
function getEditTime(): ?DateTime
public function getEditTime(): ?DateTime
{
$edited = $this->getRecord()->edited;
if(is_null($edited)) return NULL;
if (is_null($edited)) {
return null;
}
return new DateTime($edited);
}
function getComments(int $page, ?int $perPage = NULL): \Traversable
public function getComments(int $page, ?int $perPage = null): \Traversable
{
return (new Comments)->getCommentsByTarget($this, $page, $perPage);
}
function getCommentsCount(): int
{
return (new Comments)->getCommentsCountByTarget($this);
return (new Comments())->getCommentsByTarget($this, $page, $perPage);
}
function getLastComments(int $count): \Traversable
public function getCommentsCount(): int
{
return (new Comments)->getLastCommentsByTarget($this, $count);
return (new Comments())->getCommentsCountByTarget($this);
}
function getLikesCount(): int
public function getLastComments(int $count): \Traversable
{
return (new Comments())->getLastCommentsByTarget($this, $count);
}
public function getLikesCount(): int
{
return sizeof(DB::i()->getContext()->table("likes")->where([
"model" => static::class,
"target" => $this->getRecord()->id,
])->group("origin"));
}
function getLikers(int $page = 1, ?int $perPage = NULL): \Traversable
public function getLikers(int $page = 1, ?int $perPage = null): \Traversable
{
$perPage ??= OPENVK_DEFAULT_PER_PAGE;
@ -96,42 +106,42 @@ abstract class Postable extends Attachable
"model" => static::class,
"target" => $this->getRecord()->id,
])->page($page, $perPage);
foreach($sel as $like) {
$user = (new Users)->get($like->origin);
if($user->isPrivateLikes() && OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"]) {
$user = (new Users)->get((int) OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["account"]);
foreach ($sel as $like) {
$user = (new Users())->get($like->origin);
if ($user->isPrivateLikes() && OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"]) {
$user = (new Users())->get((int) OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["account"]);
}
yield $user;
}
}
function getAccessKey(): string
public function getAccessKey(): string
{
return $this->getRecord()->access_key;
}
function checkAccessKey(?string $access_key): bool
public function checkAccessKey(?string $access_key): bool
{
if($this->getAccessKey() === $access_key) {
if ($this->getAccessKey() === $access_key) {
return true;
}
return !$this->isPrivate();
}
function isPrivate(): bool
public function isPrivate(): bool
{
return (bool) $this->getRecord()->unlisted;
}
function isAnonymous(): bool
public function isAnonymous(): bool
{
return (bool) $this->getRecord()->anonymous;
}
function toggleLike(User $user): bool
public function toggleLike(User $user): bool
{
$searchData = [
"origin" => $user->getId(),
@ -139,7 +149,7 @@ abstract class Postable extends Attachable
"target" => $this->getRecord()->id,
];
if(sizeof(DB::i()->getContext()->table("likes")->where($searchData)) > 0) {
if (sizeof(DB::i()->getContext()->table("likes")->where($searchData)) > 0) {
DB::i()->getContext()->table("likes")->where($searchData)->delete();
return false;
}
@ -148,7 +158,7 @@ abstract class Postable extends Attachable
return true;
}
function setLike(bool $liked, User $user): void
public function setLike(bool $liked, User $user): void
{
$searchData = [
"origin" => $user->getId(),
@ -156,55 +166,54 @@ abstract class Postable extends Attachable
"target" => $this->getRecord()->id,
];
if($liked) {
if(!$this->hasLikeFrom($user)) {
DB::i()->getContext()->table("likes")->insert($searchData);
if ($liked) {
if (!$this->hasLikeFrom($user)) {
DB::i()->getContext()->table("likes")->insert($searchData);
}
} else {
if($this->hasLikeFrom($user)) {
if ($this->hasLikeFrom($user)) {
DB::i()->getContext()->table("likes")->where($searchData)->delete();
}
}
}
}
function hasLikeFrom(User $user): bool
public function hasLikeFrom(User $user): bool
{
$searchData = [
"origin" => $user->getId(),
"model" => static::class,
"target" => $this->getRecord()->id,
];
return sizeof(DB::i()->getContext()->table("likes")->where($searchData)) > 0;
}
function setVirtual_Id(int $id): void
public function setVirtual_Id(int $id): void
{
throw new ISE("Setting virtual id manually is forbidden");
}
function save(?bool $log = false): void
public function save(?bool $log = false): void
{
$vref = $this->upperNodeReferenceColumnName;
$vid = $this->getRecord()->{$vref} ?? $this->changes[$vref];
if(!$vid)
if (!$vid) {
throw new ISE("Can't presist post due to inability to calculate it's $vref post count. Have you set it?");
}
$pCount = sizeof($this->getTable()->where($vref, $vid));
if(is_null($this->getRecord())) {
if (is_null($this->getRecord())) {
# lol allow ppl to taint created value
if(!isset($this->changes["created"]))
if (!isset($this->changes["created"])) {
$this->stateChanges("created", time());
}
$this->stateChanges("virtual_id", $pCount + 1);
} /*else {
$this->stateChanges("edited", time());
}*/
parent::save($log);
}
use Traits\TAttachmentHost;
use Traits\TOwnable;
}

View file

@ -1,161 +1,177 @@
<?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, Documents, 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 if ($this->getContentType() == "doc") return (new Documents)->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
{
$content_object = $this->getContentObject();
if(!$content_object) {
return 'unknown';
}
if (method_exists($content_object, "getCanonicalName"))
return $content_object->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();
}
}
<?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, Documents, 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";
public function getId(): int
{
return $this->getRecord()->id;
}
public function getStatus(): int
{
return $this->getRecord()->status;
}
public function getContentType(): string
{
return $this->getRecord()->type;
}
public function getReason(): string
{
return $this->getRecord()->reason;
}
public function getTime(): DateTime
{
return new DateTime($this->getRecord()->date);
}
public function isDeleted(): bool
{
if ($this->getRecord()->deleted === 0) {
return false;
} elseif ($this->getRecord()->deleted === 1) {
return true;
}
}
public function authorId(): int
{
return $this->getRecord()->user_id;
}
public function getUser(): User
{
return (new Users())->get((int) $this->getRecord()->user_id);
}
public function getContentId(): int
{
return (int) $this->getRecord()->target_id;
}
public function getContentObject()
{
if ($this->getContentType() == "post") {
return (new Posts())->get($this->getContentId());
} elseif ($this->getContentType() == "photo") {
return (new Photos())->get($this->getContentId());
} elseif ($this->getContentType() == "video") {
return (new Videos())->get($this->getContentId());
} elseif ($this->getContentType() == "group") {
return (new Clubs())->get($this->getContentId());
} elseif ($this->getContentType() == "comment") {
return (new Comments())->get($this->getContentId());
} elseif ($this->getContentType() == "note") {
return (new Notes())->get($this->getContentId());
} elseif ($this->getContentType() == "app") {
return (new Applications())->get($this->getContentId());
} elseif ($this->getContentType() == "user") {
return (new Users())->get($this->getContentId());
} elseif ($this->getContentType() == "audio") {
return (new Audios())->get($this->getContentId());
} elseif ($this->getContentType() == "doc") {
return (new Documents())->get($this->getContentId());
} else {
return null;
}
}
public function getAuthor(): RowModel
{
return $this->getContentObject()->getOwner();
}
public function getReportAuthor(): User
{
return (new Users())->get($this->getRecord()->user_id);
}
public function banUser($initiator)
{
$reason = $this->getContentType() !== "user" ? ("**content-" . $this->getContentType() . "-" . $this->getContentId() . "**") : ("Подозрительная активность");
$this->getAuthor()->ban($reason, false, time() + $this->getAuthor()->getNewBanTime(), $initiator);
}
public 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();
}
public function getDuplicates(): \Traversable
{
return (new Reports())->getDuplicates($this->getContentType(), $this->getContentId(), $this->getId());
}
public function getDuplicatesCount(): int
{
return count(iterator_to_array($this->getDuplicates()));
}
public function hasDuplicates(): bool
{
return $this->getDuplicatesCount() > 0;
}
public function getContentName(): string
{
$content_object = $this->getContentObject();
if (!$content_object) {
return 'unknown';
}
if (method_exists($content_object, "getCanonicalName")) {
return $content_object->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();
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\Repositories\Users;
use openvk\Web\Models\RowModel;
@ -7,33 +11,33 @@ class SupportAgent extends RowModel
{
protected $tableName = "support_names";
function getAgentId(): int
public function getAgentId(): int
{
return $this->getRecord()->agent;
}
function getName(): ?string
public function getName(): ?string
{
return $this->getRecord()->name;
}
function getCanonicalName(): string
public function getCanonicalName(): string
{
return $this->getName();
}
function getAvatarURL(): ?string
public function getAvatarURL(): ?string
{
return $this->getRecord()->icon;
}
function isShowNumber(): int
public function isShowNumber(): int
{
return $this->getRecord()->numerate;
}
function getRealName(): string
public function getRealName(): string
{
return (new Users)->get($this->getAgentId())->getCanonicalName();
return (new Users())->get($this->getAgentId())->getCanonicalName();
}
}
}

View file

@ -1,38 +1,42 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Repositories\Users;
class SupportAlias extends RowModel
{
protected $tableName = "support_names";
function getUser(): User
public function getUser(): User
{
return (new Users)->get($this->getRecord()->agent);
return (new Users())->get($this->getRecord()->agent);
}
function getName(): string
public function getName(): string
{
return $this->getRecord()->name;
}
function getIcon(): ?string
public function getIcon(): ?string
{
return $this->getRecord()->icon;
}
function shouldAppendNumber(): bool
public function shouldAppendNumber(): bool
{
return (bool) $this->getRecord()->numerate;
}
function setAgent(User $agent): void
public function setAgent(User $agent): void
{
$this->stateChanges("agent", $agent->getId());
}
function setNumeration(bool $numerate): void
public function setNumeration(bool $numerate): void
{
$this->stateChanges("numerate", $numerate);
}

View file

@ -1,36 +1,41 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Util\DateTime;
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Repositories\Users;
class Ticket extends RowModel
{
use Traits\TRichText;
protected $tableName = "tickets";
private $overrideContentColumn = "text";
function getId(): int
public function getId(): int
{
return $this->getRecord()->id;
}
function getStatus(): string
public function getStatus(): string
{
return tr("support_status_" . $this->getRecord()->type);
}
function getType(): int
public function getType(): int
{
return $this->getRecord()->type;
}
function getName(): string
public function getName(): string
{
return ovk_proc_strtr($this->getRecord()->name, 100);
}
function getContext(): string
public function getContext(): string
{
$text = $this->getRecord()->text;
$text = $this->formatLinks($text);
@ -38,31 +43,29 @@ class Ticket extends RowModel
$text = nl2br($text);
return $text;
}
function getTime(): DateTime
public function getTime(): DateTime
{
return new DateTime($this->getRecord()->created);
}
function isDeleted(): bool
public function isDeleted(): bool
{
return (bool) $this->getRecord()->deleted;
}
function getUser(): user
public function getUser(): user
{
return (new Users)->get($this->getRecord()->user_id);
return (new Users())->get($this->getRecord()->user_id);
}
function getUserId(): int
public function getUserId(): int
{
return $this->getRecord()->user_id;
}
function isAd(): bool /* Эх, костыли... */
public function isAd(): bool /* Эх, костыли... */
{
return false;
return false;
}
use Traits\TRichText;
}

View file

@ -1,100 +1,112 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Util\DateTime;
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Repositories\{Users, SupportAliases, Tickets};
class TicketComment extends RowModel
{
use Traits\TRichText;
protected $tableName = "tickets_comments";
private $overrideContentColumn = "text";
private function getSupportAlias(): ?SupportAlias
{
return (new SupportAliases)->get($this->getUser()->getId());
return (new SupportAliases())->get($this->getUser()->getId());
}
function getId(): int
public function getId(): int
{
return $this->getRecord()->id;
}
function getUType(): int
public function getUType(): int
{
return $this->getRecord()->user_type;
}
function getUser(): User
{
return (new Users)->get($this->getRecord()->user_id);
public function getUser(): User
{
return (new Users())->get($this->getRecord()->user_id);
}
function getTicket(): Ticket
public function getTicket(): Ticket
{
return (new Tickets)->get($this->getRecord()->ticket_id);
return (new Tickets())->get($this->getRecord()->ticket_id);
}
function getAuthorName(): string
public function getAuthorName(): string
{
if($this->getUType() === 0)
if ($this->getUType() === 0) {
return $this->getUser()->getCanonicalName();
}
$alias = $this->getSupportAlias();
if(!$alias)
if (!$alias) {
return tr("helpdesk_agent") . " #" . $this->getAgentNumber();
}
$name = $alias->getName();
if($alias->shouldAppendNumber())
if ($alias->shouldAppendNumber()) {
$name .= "" . $this->getAgentNumber();
}
return $name;
}
function getAvatar(): string
public function getAvatar(): string
{
if($this->getUType() === 0)
if ($this->getUType() === 0) {
return $this->getUser()->getAvatarUrl();
}
$default = "/assets/packages/static/openvk/img/support.jpeg";
$alias = $this->getSupportAlias();
return is_null($alias) ? $default : ($alias->getIcon() ?? $default);
}
function getAgentNumber(): ?string
public function getAgentNumber(): ?string
{
if($this->getUType() === 0)
return NULL;
if ($this->getUType() === 0) {
return null;
}
$salt = "kiraMiki";
$hash = $this->getUser()->getId() . CHANDLER_ROOT_CONF["security"]["secret"] . $salt;
$hash = hexdec(substr(hash("adler32", $hash), 0, 3));
$hash = ceil(($hash * 999) / 4096); # proportionalize to 0-999
return str_pad((string) $hash, 3, "0", STR_PAD_LEFT);
}
function getColorRotation(): ?int
public function getColorRotation(): ?int
{
if(is_null($agent = $this->getAgentNumber()))
return NULL;
if(!is_null($this->getSupportAlias()))
if (is_null($agent = $this->getAgentNumber())) {
return null;
}
if (!is_null($this->getSupportAlias())) {
return 0;
}
$agent = (int) $agent;
$rotation = $agent > 500 ? ( ($agent * 360) / 999 ) : $agent; # cap at 360deg
$rotation = $agent > 500 ? (($agent * 360) / 999) : $agent; # cap at 360deg
$values = [0, 45, 160, 220, 310, 345]; # good looking colors
usort($values, function($x, $y) use ($rotation) {
usort($values, function ($x, $y) use ($rotation) {
# find closest
return abs($x - $rotation) - abs($y - $rotation);
});
return array_shift($values);
}
function getContext(): string
public function getContext(): string
{
$text = $this->getRecord()->text;
$text = $this->formatLinks($text);
@ -102,35 +114,34 @@ class TicketComment extends RowModel
$text = nl2br($text);
return $text;
}
function getTime(): DateTime
public function getTime(): DateTime
{
return new DateTime($this->getRecord()->created);
}
function isAd(): bool
{
return false; # Кооостыыыль!!!
}
public function isAd(): bool
{
return false; # Кооостыыыль!!!
}
function getMark(): ?int
public function getMark(): ?int
{
return $this->getRecord()->mark;
}
function isLikedByUser(): ?bool
public function isLikedByUser(): ?bool
{
$mark = $this->getMark();
if(is_null($mark))
return NULL;
else
if (is_null($mark)) {
return null;
} else {
return $mark === 1;
}
}
function isDeleted(): bool
public function isDeleted(): bool
{
return (bool) $this->getRecord()->deleted;
}
use Traits\TRichText;
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities;
use openvk\Web\Models\RowModel;
use openvk\Web\Models\Repositories\Clubs;
use openvk\Web\Util\DateTime;
@ -11,116 +15,118 @@ class Topic extends Postable
/**
* May return fake owner (group), if flags are [1, (*)]
*
*
* @param bool $honourFlags - check flags
*/
function getOwner(bool $honourFlags = true, bool $real = false): RowModel
public function getOwner(bool $honourFlags = true, bool $real = false): RowModel
{
if($honourFlags && $this->isPostedOnBehalfOfGroup())
if ($honourFlags && $this->isPostedOnBehalfOfGroup()) {
return $this->getClub();
}
return parent::getOwner($real);
}
function getClub(): Club
public function getClub(): Club
{
return (new Clubs)->get($this->getRecord()->group);
return (new Clubs())->get($this->getRecord()->group);
}
function getTitle(): string
public function getTitle(): string
{
return $this->getRecord()->title;
}
function isClosed(): bool
public function isClosed(): bool
{
return (bool) $this->getRecord()->closed;
}
function isPinned(): bool
public function isPinned(): bool
{
return (bool) $this->getRecord()->pinned;
}
function getPrettyId(): string
public function getPrettyId(): string
{
return $this->getRecord()->group . "_" . $this->getVirtualId();
}
function isPostedOnBehalfOfGroup(): bool
public function isPostedOnBehalfOfGroup(): bool
{
return ($this->getRecord()->flags & 0b10000000) > 0;
}
function isDeleted(): bool
public function isDeleted(): bool
{
return (bool) $this->getRecord()->deleted;
}
function canBeModifiedBy(User $user): bool
public function canBeModifiedBy(User $user): bool
{
return $this->getOwner(false)->getId() === $user->getId() || $this->getClub()->canBeModifiedBy($user);
}
function getLastComment(): ?Comment
public function getLastComment(): ?Comment
{
$array = iterator_to_array($this->getLastComments(1));
return isset($array[0]) ? $array[0] : NULL;
return $array[0] ?? null;
}
function getFirstComment(): ?Comment
public function getFirstComment(): ?Comment
{
$array = iterator_to_array($this->getComments(1));
return $array[0] ?? NULL;
return $array[0] ?? null;
}
function getUpdateTime(): DateTime
public function getUpdateTime(): DateTime
{
$lastComment = $this->getLastComment();
if(!is_null($lastComment))
if (!is_null($lastComment)) {
return $lastComment->getPublicationTime();
else
} else {
return $this->getEditTime() ?? $this->getPublicationTime();
}
}
function deleteTopic(): void
public function deleteTopic(): void
{
$this->setDeleted(1);
$this->unwire();
$this->save();
}
function toVkApiStruct(int $preview = 0, int $preview_length = 90): object
public function toVkApiStruct(int $preview = 0, int $preview_length = 90): object
{
$res = (object)[];
$res = (object) [];
$res->id = $this->getId();
$res->title = $this->getTitle();
$res->created = $this->getPublicationTime()->timestamp();
if($this->getOwner() instanceof User) {
if ($this->getOwner() instanceof User) {
$res->created_by = $this->getOwner()->getId();
} else {
$res->created_by = $this->getOwner()->getId() * -1;
}
$res->updated = $this->getUpdateTime()->timestamp();
if($this->getLastComment()) {
if($this->getLastComment()->getOwner() instanceof User) {
if ($this->getLastComment()) {
if ($this->getLastComment()->getOwner() instanceof User) {
$res->updated_by = $this->getLastComment()->getOwner()->getId();
} else {
$res->updated_by = $this->getLastComment()->getOwner()->getId() * -1;
}
}
$res->is_closed = (int)$this->isClosed();
$res->is_fixed = (int)$this->isPinned();
$res->is_closed = (int) $this->isClosed();
$res->is_fixed = (int) $this->isPinned();
$res->comments = $this->getCommentsCount();
if($preview == 1) {
$res->first_comment = $this->getFirstComment() ? ovk_proc_strtr($this->getFirstComment()->getText(false), $preview_length) : NULL;
$res->last_comment = $this->getLastComment() ? ovk_proc_strtr($this->getLastComment()->getText(false), $preview_length) : NULL;
if ($preview == 1) {
$res->first_comment = $this->getFirstComment() ? ovk_proc_strtr($this->getFirstComment()->getText(false), $preview_length) : null;
$res->last_comment = $this->getLastComment() ? ovk_proc_strtr($this->getLastComment()->getText(false), $preview_length) : null;
}
return $res;

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Traits;
use openvk\Web\Models\Entities\{Attachable, Photo, Video};
use openvk\Web\Util\Makima\Makima;
use Chandler\Database\DatabaseConnection;
@ -15,31 +19,32 @@ trait TAttachmentHost
"attachable_id" => $attachment->getId(),
];
}
function getChildren(): \Traversable
public function getChildren(): \Traversable
{
$sel = DatabaseConnection::i()->getContext()
->table("attachments")
->where("target_id", $this->getId())
->where("attachments.target_type", get_class($this));
foreach($sel as $rel) {
foreach ($sel as $rel) {
$repoName = $rel->attachable_type . "s";
$repoName = str_replace("Entities", "Repositories", $repoName);
$repo = new $repoName;
$repo = new $repoName();
yield $repo->get($rel->attachable_id);
}
}
function getChildrenWithLayout(int $w, int $h = -1): object
public function getChildrenWithLayout(int $w, int $h = -1): object
{
if($h < 0)
if ($h < 0) {
$h = $w;
}
$children = iterator_to_array($this->getChildren());
$skipped = $photos = $result = [];
foreach($children as $child) {
if($child instanceof Photo || $child instanceof Video && $child->getDimensions()) {
foreach ($children as $child) {
if ($child instanceof Photo || $child instanceof Video && $child->getDimensions()) {
$photos[] = $child;
continue;
}
@ -49,15 +54,16 @@ trait TAttachmentHost
$height = "unset";
$width = $w;
if(sizeof($photos) < 2) {
if(isset($photos[0]))
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++) {
for ($i = 0; $i < sizeof($photos); $i++) {
$tile = $layout->tiles[$i];
$result[] = [$tile->width . "px", $tile->height . "px", $photos[$i], "left"];
}
@ -70,25 +76,25 @@ trait TAttachmentHost
"extras" => $skipped,
];
}
function attach(Attachable $attachment): void
public function attach(Attachable $attachment): void
{
DatabaseConnection::i()->getContext()
->table("attachments")
->insert($this->composeAttachmentRequestData($attachment));
}
function detach(Attachable $attachment): bool
public function detach(Attachable $attachment): bool
{
$res = DatabaseConnection::i()->getContext()
->table("attachments")
->where($this->composeAttachmentRequestData($attachment))
->delete();
return $res > 0;
}
function unwire(): void
public function unwire(): void
{
$this->getRecord()
->related("attachments.target_id")

View file

@ -1,27 +1,38 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Traits;
use openvk\Web\Models\Repositories\Audios;
use Chandler\Database\DatabaseConnection;
trait TAudioStatuses
trait TAudioStatuses
{
function isBroadcastEnabled(): bool
public function isBroadcastEnabled(): bool
{
if($this->getRealId() < 0) return true;
if ($this->getRealId() < 0) {
return true;
}
return (bool) $this->getRecord()->audio_broadcast_enabled;
}
function getCurrentAudioStatus()
public function getCurrentAudioStatus()
{
if(!$this->isBroadcastEnabled()) return NULL;
if (!$this->isBroadcastEnabled()) {
return null;
}
$audioId = $this->getRecord()->last_played_track;
if(!$audioId) return NULL;
$audio = (new Audios)->get($audioId);
if (!$audioId) {
return null;
}
$audio = (new Audios())->get($audioId);
if(!$audio || $audio->isDeleted())
return NULL;
if (!$audio || $audio->isDeleted()) {
return null;
}
$listensTable = DatabaseConnection::i()->getContext()->table("audio_listens");
$lastListen = $listensTable->where([
@ -30,9 +41,10 @@ trait TAudioStatuses
"time >" => (time() - $audio->getLength()) - 10,
])->fetch();
if($lastListen)
if ($lastListen) {
return $audio;
}
return NULL;
return null;
}
}

View file

@ -1,44 +1,54 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Traits;
use openvk\Web\Models\Entities\Photo;
use openvk\Web\Models\Repositories\Photos;
trait TBackDrops {
function getBackDropPictureURLs(): ?array
trait TBackDrops
{
public function getBackDropPictureURLs(): ?array
{
$photo1 = $this->getRecord()->backdrop_1;
$photo2 = $this->getRecord()->backdrop_2;
if(is_null($photo1) && is_null($photo2))
return NULL;
$photo1obj = $photo2obj = NULL;
if(!is_null($photo1))
$photo1obj = (new Photos)->get($photo1);
if(!is_null($photo2))
$photo2obj = (new Photos)->get($photo2);
if(is_null($photo1obj) && is_null($photo2obj))
return NULL;
if (is_null($photo1) && is_null($photo2)) {
return null;
}
$photo1obj = $photo2obj = null;
if (!is_null($photo1)) {
$photo1obj = (new Photos())->get($photo1);
}
if (!is_null($photo2)) {
$photo2obj = (new Photos())->get($photo2);
}
if (is_null($photo1obj) && is_null($photo2obj)) {
return null;
}
return [
is_null($photo1obj) ? "" : $photo1obj->getURL(),
is_null($photo2obj) ? "" : $photo2obj->getURL(),
];
}
function setBackDropPictures(?Photo $first, ?Photo $second): void
public function setBackDropPictures(?Photo $first, ?Photo $second): void
{
if(!is_null($first))
if (!is_null($first)) {
$this->stateChanges("backdrop_1", $first->getId());
if(!is_null($second))
}
if (!is_null($second)) {
$this->stateChanges("backdrop_2", $second->getId());
}
}
function unsetBackDropPictures(): void
public function unsetBackDropPictures(): void
{
$this->stateChanges("backdrop_1", NULL);
$this->stateChanges("backdrop_2", NULL);
$this->stateChanges("backdrop_1", null);
$this->stateChanges("backdrop_2", null);
}
}
}

View file

@ -1,15 +1,20 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Traits;
use Chandler\Database\DatabaseConnection;
use openvk\Web\Models\Entities\User;
trait TIgnorable
trait TIgnorable
{
function isIgnoredBy(User $user = NULL): bool
public function isIgnoredBy(User $user = null): bool
{
if(!$user)
if (!$user) {
return false;
}
$ctx = DatabaseConnection::i()->getContext();
$data = [
"owner" => $user->getId(),
@ -20,29 +25,29 @@ trait TIgnorable
return $sub->count() > 0;
}
function addIgnore(User $for_user): bool
public function addIgnore(User $for_user): bool
{
DatabaseConnection::i()->getContext()->table("ignored_sources")->insert([
"owner" => $for_user->getId(),
"source" => $this->getRealId(),
]);
return true;
}
function removeIgnore(User $for_user): bool
public function removeIgnore(User $for_user): bool
{
DatabaseConnection::i()->getContext()->table("ignored_sources")->where([
"owner" => $for_user->getId(),
"source" => $this->getRealId(),
])->delete();
return true;
}
function toggleIgnore(User $for_user): bool
public function toggleIgnore(User $for_user): bool
{
if($this->isIgnoredBy($for_user)) {
if ($this->isIgnoredBy($for_user)) {
$this->removeIgnore($for_user);
return false;

View file

@ -1,28 +1,35 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Traits;
use openvk\Web\Models\Entities\User;
trait TOwnable
{
function canBeViewedBy(?User $user = NULL): bool
public function canBeViewedBy(?User $user = null): bool
{
# TODO: #950
if($this->isDeleted()) {
if ($this->isDeleted()) {
return false;
}
return true;
}
function canBeModifiedBy(User $user): bool
public function canBeModifiedBy(User $user): bool
{
if(method_exists($this, "isCreatedBySystem"))
if($this->isCreatedBySystem())
if (method_exists($this, "isCreatedBySystem")) {
if ($this->isCreatedBySystem()) {
return false;
if($this->getRecord()->owner > 0)
}
}
if ($this->getRecord()->owner > 0) {
return $this->getRecord()->owner === $user->getId();
else
} else {
return $this->getOwner()->canBeModifiedBy($user);
}
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Traits;
use openvk\Web\Models\Repositories\{Users, Clubs};
use Wkhooy\ObsceneCensorRus;
@ -8,28 +12,30 @@ trait TRichText
private function formatEmojis(string $text): string
{
$contentColumn = property_exists($this, "overrideContentColumn") ? $this->overrideContentColumn : "content";
if(iconv_strlen($this->getRecord()->{$contentColumn}) > OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["emojiProcessingLimit"])
if (iconv_strlen($this->getRecord()->{$contentColumn}) > OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["emojiProcessingLimit"]) {
return $text;
}
$emojis = \Emoji\detect_emoji($text);
$replaced = []; # OVK-113
foreach($emojis as $emoji) {
foreach ($emojis as $emoji) {
$point = explode("-", strtolower($emoji["hex_str"]))[0];
if(in_array($point, $replaced))
if (in_array($point, $replaced)) {
continue;
else
} else {
$replaced[] = $point;
}
$image = "https://abs.twimg.com/emoji/v2/72x72/$point.png";
$image = "<img src='$image' alt='$emoji[emoji]' ";
$image .= "style='max-height:12px; padding-left: 2pt; padding-right: 2pt; vertical-align: bottom;' />";
$text = str_replace($emoji["emoji"], $image, $text);
}
return $text;
}
private function formatLinks(string &$text): string
{
return preg_replace_callback(
@ -46,109 +52,116 @@ trait TRichText
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]);
}),
$text
);
}
private function removeZalgo(string $text): string
{
return preg_replace("%\p{M}{3,}%Xu", "", $text);
}
function resolveMentions(array $skipUsers = []): \Traversable
public function resolveMentions(array $skipUsers = []): \Traversable
{
$contentColumn = property_exists($this, "overrideContentColumn") ? $this->overrideContentColumn : "content";
$text = $this->getRecord()->{$contentColumn};
$text = preg_replace("%@([A-Za-z0-9]++) \(((?:[\p{L&}\p{Lo} 0-9]\p{Mn}?)++)\)%Xu", "[$1|$2]", $text);
$text = preg_replace("%([\n\r\s]|^)(@([A-Za-z0-9]++))%Xu", "$1[$3|@$3]", $text);
$resolvedUsers = $skipUsers;
$resolvedClubs = [];
preg_match_all("%\[([A-Za-z0-9]++)\|((?:[\p{L&}\p{Lo} 0-9@]\p{Mn}?)++)\]%Xu", $text, $links, PREG_PATTERN_ORDER);
foreach($links[1] as $link) {
if(preg_match("%^id([0-9]++)$%", $link, $match)) {
foreach ($links[1] as $link) {
if (preg_match("%^id([0-9]++)$%", $link, $match)) {
$uid = (int) $match[1];
if(in_array($uid, $resolvedUsers))
if (in_array($uid, $resolvedUsers)) {
continue;
}
$resolvedUsers[] = $uid;
$maybeUser = (new Users)->get($uid);
if($maybeUser)
$maybeUser = (new Users())->get($uid);
if ($maybeUser) {
yield $maybeUser;
} else if(preg_match("%^(?:club|public|event)([0-9]++)$%", $link, $match)) {
}
} elseif (preg_match("%^(?:club|public|event)([0-9]++)$%", $link, $match)) {
$cid = (int) $match[1];
if(in_array($cid, $resolvedClubs))
if (in_array($cid, $resolvedClubs)) {
continue;
}
$resolvedClubs[] = $cid;
$maybeClub = (new Clubs)->get($cid);
if($maybeClub)
$maybeClub = (new Clubs())->get($cid);
if ($maybeClub) {
yield $maybeClub;
}
} else {
$maybeUser = (new Users)->getByShortURL($link);
if($maybeUser) {
$maybeUser = (new Users())->getByShortURL($link);
if ($maybeUser) {
$uid = $maybeUser->getId();
if(in_array($uid, $resolvedUsers))
if (in_array($uid, $resolvedUsers)) {
continue;
else
} else {
$resolvedUsers[] = $uid;
}
yield $maybeUser;
continue;
}
$maybeClub = (new Clubs)->getByShortURL($link);
if($maybeClub) {
$maybeClub = (new Clubs())->getByShortURL($link);
if ($maybeClub) {
$cid = $maybeClub->getId();
if(in_array($cid, $resolvedClubs))
if (in_array($cid, $resolvedClubs)) {
continue;
else
} else {
$resolvedClubs[] = $cid;
}
yield $maybeClub;
}
}
}
}
function getText(bool $html = true): string
public function getText(bool $html = true): string
{
$contentColumn = property_exists($this, "overrideContentColumn") ? $this->overrideContentColumn : "content";
$text = htmlspecialchars($this->getRecord()->{$contentColumn}, ENT_DISALLOWED | ENT_XHTML);
$proc = iconv_strlen($this->getRecord()->{$contentColumn}) <= OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["processingLimit"];
if($html) {
if($proc) {
if ($html) {
if ($proc) {
$text = $this->formatLinks($text);
$text = preg_replace("%@([A-Za-z0-9]++) \(((?:[\p{L&}\p{Lo} 0-9]\p{Mn}?)++)\)%Xu", "[$1|$2]", $text);
$text = preg_replace("%([\n\r\s]|^)(@([A-Za-z0-9]++))%Xu", "$1[$3|@$3]", $text);
$text = preg_replace("%\[([A-Za-z0-9]++)\|((?:[\p{L&}\p{Lo} 0-9@]\p{Mn}?)++)\]%Xu", "<a href='/$1'>$2</a>", $text);
$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) {
$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='/search?section=posts&q=%23$slug'>$m[2]</a>";
}, $text);
$text = $this->formatEmojis($text);
}
$text = $this->removeZalgo($text);
$text = nl2br($text);
} else {
$text = str_replace("\r\n","\n", $text);
$text = str_replace("\r\n", "\n", $text);
}
if(OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["christian"])
if (OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["christian"]) {
ObsceneCensorRus::filterText($text);
}
return $text;
}
}

View file

@ -1,5 +1,9 @@
<?php declare(strict_types=1);
<?php
declare(strict_types=1);
namespace openvk\Web\Models\Entities\Traits;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Repositories\Users;
use Chandler\Database\DatabaseConnection;
@ -12,16 +16,16 @@ trait TSubscribable
"model" => static::class,
"target" => $this->getId(),
]);
foreach($subs as $sub) {
$sub = (new Users)->get($sub->follower);
if(!$sub) continue;
yield $sub;
}
}*/
function toggleSubscription(User $user): bool
public function toggleSubscription(User $user): bool
{
$ctx = DatabaseConnection::i()->getContext();
$data = [
@ -30,17 +34,17 @@ trait TSubscribable
"target" => $this->getId(),
];
$sub = $ctx->table("subscriptions")->where($data);
if(!($sub->fetch())) {
if (!($sub->fetch())) {
$ctx->table("subscriptions")->insert($data);
return true;
}
$sub->delete();
return false;
}
function changeFlags(User $user, int $flags, bool $reverse): bool
public function changeFlags(User $user, int $flags, bool $reverse): bool
{
$ctx = DatabaseConnection::i()->getContext();
$data = [
@ -51,12 +55,13 @@ trait TSubscribable
$sub = $ctx->table("subscriptions")->where($data);
bdump($data);
if (!$sub)
if (!$sub) {
return false;
}
$sub->update([
'flags' => $flags
'flags' => $flags,
]);
return true;
}

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more