mirror of
https://github.com/openvk/openvk
synced 2025-04-23 08:33:02 +03:00
Merge branch 'master' into disco
This commit is contained in:
commit
3e119ccbc6
254 changed files with 9305 additions and 3330 deletions
|
@ -1,11 +1,8 @@
|
|||
<?xml encoding="UTF-8"?>
|
||||
|
||||
<!ELEMENT latte (tags,filters,variables,functions)>
|
||||
<!ATTLIST latte vendor #REQUIRED>
|
||||
<!ATTLIST latte version #REQUIRED>
|
||||
|
||||
<!ELEMENT tags (tag)+>
|
||||
|
||||
<!ELEMENT tag (arguments)?>
|
||||
<!ATTLIST tag name CDATA #REQUIRED>
|
||||
<!ATTLIST tag type (PAIR|UNPAIRED|UNPAIRED_ATTR|ATTR_ONLY|AUTO_EMPTY) #REQUIRED>
|
||||
|
@ -13,32 +10,24 @@
|
|||
<!ATTLIST tag arguments CDATA #IMPLIED>
|
||||
<!ATTLIST tag deprecatedMessage CDATA #IMPLIED>
|
||||
<!ATTLIST tag multiLine (true|false) #IMPLIED>
|
||||
|
||||
<!ELEMENT arguments (argument)+>
|
||||
|
||||
<!ELEMENT argument EMPTY>
|
||||
<!ATTLIST argument name #REQUIRED>
|
||||
<!ATTLIST argument types CDATA #REQUIRED>
|
||||
<!ATTLIST argument repeatable (true|false) #IMPLIED>
|
||||
<!ATTLIST argument required (true|false) #IMPLIED>
|
||||
<!ATTLIST argument validType #IMPLIED>
|
||||
|
||||
<!ELEMENT filters (filter)+>
|
||||
|
||||
<!ELEMENT filter EMPTY>
|
||||
<!ATTLIST filter name #REQUIRED>
|
||||
<!ATTLIST filter description CDATA #IMPLIED>
|
||||
<!ATTLIST filter arguments CDATA #IMPLIED>
|
||||
<!ATTLIST filter insertColons #IMPLIED>
|
||||
|
||||
<!ELEMENT variables (variable)+>
|
||||
|
||||
<!ELEMENT variable EMPTY>
|
||||
<!ATTLIST variable name #REQUIRED>
|
||||
<!ATTLIST variable type CDATA #REQUIRED>
|
||||
|
||||
<!ELEMENT functions (function)+>
|
||||
|
||||
<!ELEMENT function EMPTY>
|
||||
<!ATTLIST function name #REQUIRED>
|
||||
<!ATTLIST function arguments CDATA #REQUIRED>
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/console" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php80" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/service-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/wapmorgan/morphos" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
<path value="$PROJECT_DIR$/vendor/symfony/console" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php80" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/service-contracts" />
|
||||
<path value="$PROJECT_DIR$/vendor/wapmorgan/morphos" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="PhpProjectSharedConfiguration" php_language_level="7.4">
|
||||
|
|
103
CLI/FetchToncoinTransactions.php
Executable file
103
CLI/FetchToncoinTransactions.php
Executable file
|
@ -0,0 +1,103 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
11
CODE_OF_CONFLICT.md
Normal file
11
CODE_OF_CONFLICT.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
The OpenVK development effort is a very personal process compared to "traditional" ways of developing software.
|
||||
Your code and ideas behind it will be carefully reviewed, often resulting in critique and criticism.
|
||||
The review will almost always require improvements to the code before it can be included in the repo.
|
||||
Know that this happens because everyone involved wants to see the best possible solution for the overall success of OpenVK.
|
||||
|
||||
If however, anyone feels personally abused, threatened, or otherwise uncomfortable due to this process, that is not acceptable.
|
||||
If so, please contact the community manager @WerySkok and report the issue.
|
||||
|
||||
As a reviewer of code, please strive to keep things civil and focused on the technical issues involved. We are all humans,
|
||||
and frustrations can be high on both sides of the process.
|
||||
Try to keep in mind the immortal words of Bill and Ted, "Be excellent to each other."
|
277
CODE_STYLE.md
Normal file
277
CODE_STYLE.md
Normal file
|
@ -0,0 +1,277 @@
|
|||
# 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:
|
||||

|
||||
|
||||
+ 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";
|
||||
}
|
||||
```
|
204
Email/change-email.eml.latte
Normal file
204
Email/change-email.eml.latte
Normal file
|
@ -0,0 +1,204 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>Подтверждение изменения Email</title>
|
||||
<link rel="stylesheet" href="foundation.css" />
|
||||
<style>
|
||||
.container {
|
||||
border-top: 5px solid pink;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table class="body" data-made-with-foundation="">
|
||||
<tr>
|
||||
<td class="float-center" align="center" valign="top">
|
||||
<center>
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="container">
|
||||
<tr>
|
||||
<td>
|
||||
<table class="row header">
|
||||
<tr>
|
||||
<th class="small-12 large-12 columns first last">
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h4 class="text-center">Подтверждение изменения Email</h4>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="row">
|
||||
<tr>
|
||||
<th class="small-12 large-12 columns first last">
|
||||
<table class="row">
|
||||
<tr>
|
||||
<td>
|
||||
<center>
|
||||
<img src="pictures/lock.jpeg" align="center" class="float-center" width=128 height=128 />
|
||||
</center>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="text-left">
|
||||
Здравствуйте, {$name}! Вы вероятно изменили свой адрес электронной почты в OpenVK. Чтобы изменение вступило в силу, необходимо подтвердить ваш новый Email.
|
||||
</p>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="button large expand success">
|
||||
<tr>
|
||||
<td>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<center>
|
||||
<a href="http://{$_SERVER['HTTP_HOST']}/settings/change_email?key={rawurlencode($key)}" align="center" class="float-center">Подтвердить Email!</a>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="text-left">
|
||||
Если кнопка не работает, вы можете попробовать скопировать и вставить эту ссылку в адресную строку вашего веб-обозревателя:
|
||||
</p>
|
||||
|
||||
<table class="callout">
|
||||
<tr>
|
||||
<th class="callout-inner primary">
|
||||
<a href="http://{$_SERVER['HTTP_HOST']}/settings/change_email?key={$key}" style="color: #000; text-decoration: none;">
|
||||
http://{$_SERVER['HTTP_HOST']}/settings/change_email?key={$key}
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="text-left">
|
||||
Обратите внимание на то, что эту ссылку нельзя:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>Передавать другим людям (даже друзьям, питомцам, соседам, любимым девушкам)</li>
|
||||
<li>Использовать, если прошло более двух дней с её генерации</li>
|
||||
</ul>
|
||||
|
||||
<table class="callout">
|
||||
<tr>
|
||||
<th class="callout-inner alert">
|
||||
<p>
|
||||
Ещё раз <b>обратите внимание</b> на то, что данную ссылку или письмо <b>ни в коем случае нельзя</b> передавать другим людям! Даже если они представляются службой поддержки.<br/>
|
||||
Это письмо предназначено исключительно для одноразового, <b>непосредственного</b> использования владельцем аккаунта.
|
||||
</p>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="text-right">
|
||||
С уважением, овк-тян.
|
||||
</p>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="text-left">
|
||||
<small>
|
||||
Вы получили это письмо так как кто-то или вы изменили адрес электронной почты. Это не рассылка и от неё нельзя отписаться. Если вы всё равно хотите перестать получать подобные письма, деактивируйте ваш аккаунт.
|
||||
</small>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="spacer">
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
32
README.md
32
README.md
|
@ -12,13 +12,15 @@ To be honest, we don't know whether it even works. However, this version is main
|
|||
|
||||
We will release OpenVK as soon as it's ready. As for now you can:
|
||||
* `git clone` this repo's master branch (use `git pull` to update)
|
||||
* Grab a prebuilt OpenVK distro from [GitHub artifacts](https://github.com/openvk/archive/actions/workflows/nightly.yml)
|
||||
* Grab a prebuilt OpenVK distro from [GitHub artifacts](https://nightly.link/openvk/archive/workflows/nightly/master/OpenVK%20Archive.zip)
|
||||
|
||||
## Instances
|
||||
|
||||
* **[openvk.su](https://openvk.su/)**
|
||||
* **[openvk.uk](https://openvk.uk)** - official mirror of openvk.su (<https://t.me/openvkch/1609>)
|
||||
* **[openvk.uk](https://openvk.uk)** - official mirror of openvk.su (<https://t.me/openvk/1609>)
|
||||
* **[openvk.co](http://openvk.co)** - yet another official mirror of openvk.su without TLS (<https://t.me/openvk/1654>)
|
||||
* [social.fetbuk.ru](http://social.fetbuk.ru/)
|
||||
* [vepurovk.xyz](http://vepurovk.xyz/)
|
||||
|
||||
## Can I create my own OpenVK instance?
|
||||
|
||||
|
@ -32,29 +34,35 @@ If you want, you can add your instance to the list above so that people can regi
|
|||
|
||||
1. Install PHP 7.4, web-server, Composer, Node.js, Yarn and [Chandler](https://github.com/openvk/chandler)
|
||||
|
||||
* PHP 8 has **not** yet been tested, so you should not expect it to work. (edit: it does not work).
|
||||
* PHP 8.1 is supported, but it was not tested carefully, be aware of that.
|
||||
|
||||
2. Install [commitcaptcha](https://github.com/openvk/commitcaptcha) and OpenVK as Chandler extensions like this:
|
||||
2. Install MySQL-compatible database.
|
||||
|
||||
* We recommend using Percona Server, but any MySQL-compatible server should work
|
||||
* Server should be compatible with at least MySQL 5.6, MySQL 8.0+ recommended.
|
||||
* Support for MySQL 4.1+ is WIP, replace `utf8mb4` and `utf8mb4_unicode_520_ci` with `utf8` and `utf8_unicode_ci` in SQLs.
|
||||
|
||||
3. Install [commitcaptcha](https://github.com/openvk/commitcaptcha) and OpenVK as Chandler extensions like this:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk
|
||||
git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha
|
||||
```
|
||||
|
||||
3. And enable them:
|
||||
4. And enable them:
|
||||
|
||||
```bash
|
||||
ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/extensions/enabled/
|
||||
ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/
|
||||
```
|
||||
|
||||
4. Import `install/init-static-db.sql` to the **same database** you installed Chandler to and import all sqls from `install/sqls` to the **same database**
|
||||
5. Import `install/init-event-db.sql` to a **separate database** (Yandex.Clickhouse can also be used, higly recommended)
|
||||
6. Copy `openvk-example.yml` to `openvk.yml` and change options to your liking
|
||||
7. Run `composer install` in OpenVK directory
|
||||
8. Run `composer install` in commitcaptcha directory
|
||||
9. Move to `Web/static/js` and execute `yarn install`
|
||||
10. Set `openvk` as your root app in `chandler.yml`
|
||||
5. Import `install/init-static-db.sql` to the **same database** you installed Chandler to and import all sqls from `install/sqls` to the **same database**
|
||||
6. Import `install/init-event-db.sql` to a **separate database** (Yandex.Clickhouse can also be used, highly recommended)
|
||||
7. Copy `openvk-example.yml` to `openvk.yml` and change options to your liking
|
||||
8. Run `composer install` in OpenVK directory
|
||||
9. Run `composer install` in commitcaptcha directory
|
||||
10. Move to `Web/static/js` and execute `yarn install`
|
||||
11. Set `openvk` as your root app in `chandler.yml`
|
||||
|
||||
Once you are done, you can login as a system administrator on the network itself (no registration required):
|
||||
|
||||
|
|
44
README_RU.md
44
README_RU.md
|
@ -2,23 +2,25 @@
|
|||
|
||||
_[English](README.md)_
|
||||
|
||||
**OpenVK** это попытка создать простую CMS, которая ~~косплеит~~ имитирует старый ВКонтакте. Представленный здесь код пока не стабилен.
|
||||
**OpenVK** - это попытка создать простую CMS, которая ~~косплеит~~ имитирует старый ВКонтакте. На данный момент представленный здесь исходный код проекта пока не является стабильным.
|
||||
|
||||
ВКонтакте принадлежит Павлу Дурову и VK Group.
|
||||
|
||||
Честно говоря, мы даже не знаем, работает ли она вообще. Однако, эта версия поддерживается, и мы будем рады принять ваши сообщения об ошибках [в нашем баг-трекере](https://github.com/openvk/openvk/projects/1). Вы также можете отправлять их через [вкладку "Помощь"](https://openvk.su/support?act=new) (для этого вам понадобится учетная запись OVK).
|
||||
|
||||
## Когда релиз?
|
||||
## Когда выйдет релизная версия?
|
||||
|
||||
Мы выпустим OpenVK, как только он будет готов. На данный момент Вы можете:
|
||||
* Сделать `git clone` master ветки этой репозитории (используйте `git pull` для обновления)
|
||||
* Взять готовую сборку OpenVK из [GitHub Actions](https://github.com/openvk/archive/actions/workflows/nightly.yml)
|
||||
* Склонировать master ветку репозитория командой `git clone` (используйте `git pull` для обновления)
|
||||
* Взять готовую сборку OpenVK из [GitHub Actions](https://nightly.link/openvk/archive/workflows/nightly/master/OpenVK%20Archive.zip)
|
||||
|
||||
## Инстанции
|
||||
|
||||
* **[openvk.su](https://openvk.su/)**
|
||||
* **[openvk.uk](https://openvk.uk)** - официальное зеркало openvk.su (<https://t.me/openvkch/1609>)
|
||||
* **[openvk.uk](https://openvk.uk)** - официальное зеркало openvk.su (<https://t.me/openvk/1609>)
|
||||
* **[openvk.co](http://openvk.co)** - ещё одно официальное зеркало openvk.su без TLS (<https://t.me/openvk/1654>)
|
||||
* [social.fetbuk.ru](http://social.fetbuk.ru/)
|
||||
* [vepurovk.xyz](http://vepurovk.xyz/)
|
||||
|
||||
## Могу ли я создать свою собственную инстанцию OpenVK?
|
||||
|
||||
|
@ -32,35 +34,41 @@ _[English](README.md)_
|
|||
|
||||
1. Установите PHP 7.4, веб-сервер, Composer, Node.js, Yarn и [Chandler](https://github.com/openvk/chandler)
|
||||
|
||||
* PHP 8 еще **не** тестировался, поэтому не стоит ожидать, что он будет работать (обновление: он не работает).
|
||||
* PHP 8 еще **не** тестировался, поэтому не стоит ожидать, что он будет работать (UPD: он не работает).
|
||||
|
||||
2. Установите [commitcaptcha](https://github.com/openvk/commitcaptcha) и OpenVK в качестве расширений Chandler следующим образом:
|
||||
2. Установите MySQL-совместимую базу данных.
|
||||
|
||||
* Мы рекомендуем использовать Persona Server, но любая MySQL-совместимая база данных должна работать
|
||||
* Сервер должен поддерживать хотя бы MySQL 5.6, рекомендуется использовать MySQL 8.0+.
|
||||
* Поддержка для MySQL 4.1+ находится в процессе, а пока замените `utf8mb4` и `utf8mb4_unicode_520_ci` на `utf8` и `utf8_unicode_ci` в SQL-файлах, соответственно.
|
||||
|
||||
3. Установите [commitcaptcha](https://github.com/openvk/commitcaptcha) и OpenVK в качестве расширений Chandler:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk
|
||||
git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha
|
||||
```
|
||||
|
||||
3. И включите их:
|
||||
4. И включите их:
|
||||
|
||||
```bash
|
||||
ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/extensions/enabled/
|
||||
ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/
|
||||
```
|
||||
|
||||
4. Импортируйте `install/init-static-db.sql` в **ту же базу данных**, в которую вы установили Chandler, и импортируйте все SQL файлы из папки `install/sqls` в **ту же базу данных**
|
||||
5. Импортируйте `install/init-event-db.sql` в **отдельную базу данных** (Яндекс.Clickhouse также может быть использован, настоятельно рекомендуется)
|
||||
6. Скопируйте `openvk-example.yml` в `openvk.yml` и измените параметры
|
||||
7. Запустите `composer install` в директории OpenVK
|
||||
8. Запустите `composer install` в директории commitcaptcha
|
||||
9. Перейдите в `Web/static/js` и выполните `yarn install`
|
||||
10. Установите `openvk` в качестве корневого приложения в файле `chandler.yml`
|
||||
5. Импортируйте `install/init-static-db.sql` в **ту же базу данных**, в которую вы установили Chandler, и импортируйте все SQL файлы из папки `install/sqls` в **ту же базу данных**
|
||||
6. Импортируйте `install/init-event-db.sql` в **отдельную базу данных** (Яндекс.Clickhouse также может быть использован, настоятельно рекомендуется)
|
||||
7. Скопируйте `openvk-example.yml` в `openvk.yml` и измените параметры под свои нужды
|
||||
8. Запустите `composer install` в директории OpenVK
|
||||
9. Запустите `composer install` в директории commitcaptcha
|
||||
10. Перейдите в `Web/static/js` и выполните `yarn install`
|
||||
11. Установите `openvk` в качестве корневого приложения в файле `chandler.yml`
|
||||
|
||||
После этого вы можете войти как системный администратор в саму сеть (регистрация не требуется):
|
||||
|
||||
* **Логин**: `admin@localhost.localdomain6`
|
||||
* **Пароль**: `admin`
|
||||
* Перед использованием встроенной учетной записи рекомендуется сменить пароль.
|
||||
* Перед использованием встроенной учетной записи рекомендуется сменить пароль или отключить её.
|
||||
|
||||
💡Запутались? Полное руководство по установке доступно [здесь](https://docs.openvk.su/openvk_engine/centos8_installation/) (CentOS 8 [и](https://almalinux.org/ru/) [семейство](https://yum.oracle.com/oracle-linux-isos.html)).
|
||||
|
||||
|
@ -74,7 +82,7 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
|
|||
|
||||
* [Баг-трекер](https://github.com/openvk/openvk/projects/1)
|
||||
* [Помощь в OVK](https://openvk.su/support?act=new)
|
||||
* Telegram-чат: Перейдите на [наш канал](https://t.me/openvkch) и откройте обсуждение в меню нашего канала.
|
||||
* Telegram-чат: Перейдите на [наш канал](https://t.me/openvk) и откройте обсуждение в меню нашего канала.
|
||||
* [Reddit](https://www.reddit.com/r/openvk/)
|
||||
* [Обсуждения](https://github.com/openvk/openvk/discussions)
|
||||
* Чат в Matrix: #ovk:matrix.org
|
||||
|
@ -82,5 +90,5 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
|
|||
**Внимание**: баг-трекер, форум, телеграм- и matrix-чат являются публичными местами, и жалобы в OVK обслуживается волонтерами. Если вам нужно сообщить о чем-то, что не должно быть раскрыто широкой публике (например, сообщение об уязвимости), пожалуйста, свяжитесь с нами напрямую по этому адресу: **openvk [собака] tutanota [точка] com**.
|
||||
|
||||
<a href="https://codeberg.org/OpenVK/openvk">
|
||||
<img alt="Получить на Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">
|
||||
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">
|
||||
</a>
|
||||
|
|
87
ServiceAPI/Apps.php
Normal file
87
ServiceAPI/Apps.php
Normal file
|
@ -0,0 +1,87 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\ServiceAPI;
|
||||
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\Applications;
|
||||
|
||||
class Apps implements Handler
|
||||
{
|
||||
private $user;
|
||||
private $apps;
|
||||
|
||||
public function __construct(?User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->apps = new Applications;
|
||||
}
|
||||
|
||||
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,
|
||||
"name" => [
|
||||
"first" => $this->user->getFirstName(),
|
||||
"last" => $this->user->getLastName(),
|
||||
"full" => $this->user->getFullName(),
|
||||
],
|
||||
"ava" => $this->user->getAvatarUrl(),
|
||||
]);
|
||||
}
|
||||
|
||||
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()) {
|
||||
$reject("No application with this id found");
|
||||
return;
|
||||
}
|
||||
|
||||
$coinsLeft = $this->user->getCoins() - $amount;
|
||||
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
|
||||
{
|
||||
$app = $this->apps->get($appId);
|
||||
if(!$app) {
|
||||
$reject("No application with this id found");
|
||||
return;
|
||||
} else if($app->getOwner()->getId() != $this->user->getId()) {
|
||||
$reject("You don't have rights to edit this app");
|
||||
return;
|
||||
}
|
||||
|
||||
$coins = $app->getBalance();
|
||||
$app->withdrawCoins();
|
||||
$resolve($coins);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
<?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;
|
||||
|
||||
|
@ -55,4 +56,19 @@ class Wall implements Handler
|
|||
|
||||
$resolve((array) $res);
|
||||
}
|
||||
|
||||
function newStatus(string $text, callable $resolve, callable $reject): void
|
||||
{
|
||||
$post = new Post;
|
||||
$post->setOwner($this->user->getId());
|
||||
$post->setWall($this->user->getId());
|
||||
$post->setCreated(time());
|
||||
$post->setContent($text);
|
||||
$post->setAnonymous(false);
|
||||
$post->setFlags(0);
|
||||
$post->setNsfw(false);
|
||||
$post->save();
|
||||
|
||||
$resolve($post->getId());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ final class Account extends VKAPIRequestHandler
|
|||
"last_name" => $this->getUser()->getLastName(),
|
||||
"home_town" => $this->getUser()->getHometown(),
|
||||
"status" => $this->getUser()->getStatus(),
|
||||
"bdate" => "1.1.1970", // TODO
|
||||
"bdate_visibility" => 0, // TODO
|
||||
"phone" => "+420 ** *** 228", // TODO
|
||||
"bdate" => $this->getUser()->getBirthday()->format('%e.%m.%Y'),
|
||||
"bdate_visibility" => $this->getUser()->getBirthdayPrivacy(),
|
||||
"phone" => "+420 ** *** 228", # TODO
|
||||
"relation" => $this->getUser()->getMaritalStatus(),
|
||||
"sex" => $this->getUser()->isFemale() ? 1 : 2
|
||||
];
|
||||
|
@ -25,12 +25,10 @@ final class Account extends VKAPIRequestHandler
|
|||
{
|
||||
$this->requireUser();
|
||||
|
||||
// Цiй метод є заглушка
|
||||
|
||||
return (object) [
|
||||
"2fa_required" => 0,
|
||||
"country" => "CZ", // TODO
|
||||
"eu_user" => false, // TODO
|
||||
"2fa_required" => $this->getUser()->is2faEnabled() ? 1 : 0,
|
||||
"country" => "CZ", # TODO
|
||||
"eu_user" => false, # TODO
|
||||
"https_required" => 1,
|
||||
"intro" => 0,
|
||||
"community_comments" => false,
|
||||
|
@ -47,6 +45,7 @@ final class Account extends VKAPIRequestHandler
|
|||
$this->requireUser();
|
||||
|
||||
$this->getUser()->setOnline(time());
|
||||
$this->getUser()->save();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -55,7 +54,7 @@ final class Account extends VKAPIRequestHandler
|
|||
{
|
||||
$this->requireUser();
|
||||
|
||||
// Цiй метод є заглушка
|
||||
# Цiй метод є заглушка
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -73,6 +72,6 @@ final class Account extends VKAPIRequestHandler
|
|||
"messages" => $this->getUser()->getUnreadMessagesCount()
|
||||
];
|
||||
|
||||
// TODO: Filter
|
||||
# TODO: Filter
|
||||
}
|
||||
}
|
||||
|
|
|
@ -316,6 +316,20 @@ final class Audio extends VKAPIRequestHandler
|
|||
"count" => sizeof($items),
|
||||
"items" => $items,
|
||||
];
|
||||
|
||||
$serverUrl = ovk_scheme(true) . $_SERVER["SERVER_NAME"];
|
||||
|
||||
return (object) [
|
||||
"count" => 1,
|
||||
"items" => [(object) [
|
||||
"id" => 1,
|
||||
"owner_id" => 1,
|
||||
"artist" => "В ОВК ПОКА НЕТ МУЗЫКИ",
|
||||
"title" => "ЖДИТЕ :)))",
|
||||
"duration" => 22,
|
||||
"url" => $serverUrl . "/assets/packages/static/openvk/audio/nomusic.mp3"
|
||||
]]
|
||||
];
|
||||
}
|
||||
|
||||
function getLyrics(int $lyrics_id): object
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<?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 Friends extends VKAPIRequestHandler
|
||||
|
@ -24,9 +23,8 @@ final class Friends extends VKAPIRequestHandler
|
|||
|
||||
$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(),
|
||||
|
@ -70,7 +68,6 @@ final class Friends extends VKAPIRequestHandler
|
|||
$this->requireUser();
|
||||
|
||||
$users = new UsersRepo;
|
||||
|
||||
$user = $users->get(intval($user_id));
|
||||
|
||||
if(is_null($user)) {
|
||||
|
@ -83,20 +80,16 @@ final class Friends extends VKAPIRequestHandler
|
|||
case 0:
|
||||
$user->toggleSubscription($this->getUser());
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
$user->toggleSubscription($this->getUser());
|
||||
return 2;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
return 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,11 +105,9 @@ final class Friends extends VKAPIRequestHandler
|
|||
case 3:
|
||||
$user->toggleSubscription($this->getUser());
|
||||
return 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
fail(15, "Access denied: No friend or friend request found.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,22 +124,6 @@ final class Friends extends VKAPIRequestHandler
|
|||
for($i=0; $i < sizeof($friends); $i++) {
|
||||
$friend = $users->get(intval($friends[$i]));
|
||||
|
||||
$status = 0;
|
||||
switch ($friend->getSubscriptionStatus($this->getUser())) {
|
||||
case 3:
|
||||
case 0:
|
||||
$status = $friend->getSubscriptionStatus($this->getUser());
|
||||
break;
|
||||
|
||||
case 1:
|
||||
$status = 2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$status = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
$response[] = (object)[
|
||||
"friend_status" => $friend->getSubscriptionStatus($this->getUser()),
|
||||
"user_id" => $friend->getId()
|
||||
|
@ -157,4 +132,32 @@ final class Friends extends VKAPIRequestHandler
|
|||
|
||||
return $response;
|
||||
}
|
||||
|
||||
function getRequests(string $fields = "", int $offset = 0, int $count = 100): object
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$i = 0;
|
||||
$offset++;
|
||||
$followers = [];
|
||||
|
||||
foreach($this->getUser()->getFollowers() as $follower) {
|
||||
$followers[$i] = $follower->getId();
|
||||
$i++;
|
||||
}
|
||||
|
||||
$response = $followers;
|
||||
$usersApi = new Users($this->getUser());
|
||||
|
||||
if(!is_null($fields))
|
||||
$response = $usersApi->get(implode(',', $followers), $fields, 0, $count); # FIXME
|
||||
|
||||
foreach($response as $user)
|
||||
$user->user_id = $user->id;
|
||||
|
||||
return (object) [
|
||||
"count" => $this->getUser()->getFollowersCount(),
|
||||
"items" => $response
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,12 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Entities\Clubs;
|
||||
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||
use openvk\Web\Models\Entities\Post;
|
||||
use openvk\Web\Models\Entities\Postable;
|
||||
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
||||
|
||||
final class Groups extends VKAPIRequestHandler
|
||||
{
|
||||
|
@ -15,40 +10,40 @@ final class Groups extends VKAPIRequestHandler
|
|||
$this->requireUser();
|
||||
|
||||
if($user_id == 0) {
|
||||
foreach($this->getUser()->getClubs($offset+1) as $club) {
|
||||
foreach($this->getUser()->getClubs($offset+1) as $club)
|
||||
$clbs[] = $club;
|
||||
}
|
||||
$clbsCount = $this->getUser()->getClubCount();
|
||||
} else {
|
||||
$users = new UsersRepo;
|
||||
$user = $users->get($user_id);
|
||||
if (is_null($user)) {
|
||||
|
||||
if(is_null($user))
|
||||
$this->fail(15, "Access denied");
|
||||
}
|
||||
foreach($user->getClubs($offset+1) as $club) {
|
||||
|
||||
foreach($user->getClubs($offset+1) as $club)
|
||||
$clbs[] = $club;
|
||||
}
|
||||
|
||||
$clbsCount = $user->getClubCount();
|
||||
}
|
||||
|
||||
$rClubs;
|
||||
|
||||
$ic = sizeof($clbs);
|
||||
if(sizeof($clbs) > $count)
|
||||
$ic = $count;
|
||||
|
||||
if(sizeof($clbs) > $count) $ic = $count;
|
||||
|
||||
if(!empty($clbs)) {
|
||||
$clbs = array_slice($clbs, $offset * $count);
|
||||
|
||||
for($i=0; $i < $ic; $i++) {
|
||||
$usr = $clbs[$i];
|
||||
if(is_null($usr))
|
||||
{
|
||||
if(is_null($usr)) {
|
||||
$rClubs[$i] = (object)[
|
||||
"id" => $clbs[$i],
|
||||
"name" => "DELETED",
|
||||
"deactivated" => "deleted"
|
||||
];
|
||||
}else if($clbs[$i] == null){
|
||||
} else if($clbs[$i] == NULL) {
|
||||
|
||||
} else {
|
||||
$rClubs[$i] = (object) [
|
||||
|
@ -63,25 +58,43 @@ final class Groups extends VKAPIRequestHandler
|
|||
|
||||
foreach($flds as $field) {
|
||||
switch($field) {
|
||||
case 'verified':
|
||||
case "verified":
|
||||
$rClubs[$i]->verified = intval($usr->isVerified());
|
||||
break;
|
||||
case 'has_photo':
|
||||
case "has_photo":
|
||||
$rClubs[$i]->has_photo = is_null($usr->getAvatarPhoto()) ? 0 : 1;
|
||||
break;
|
||||
case 'photo_max_orig':
|
||||
case "photo_max_orig":
|
||||
$rClubs[$i]->photo_max_orig = $usr->getAvatarURL();
|
||||
break;
|
||||
case 'photo_max':
|
||||
$rClubs[$i]->photo_max = $usr->getAvatarURL();
|
||||
case "photo_max":
|
||||
$rClubs[$i]->photo_max = $usr->getAvatarURL("original"); // ORIGINAL ANDREI CHINITEL 🥵🥵🥵🥵
|
||||
break;
|
||||
case 'members_count':
|
||||
case "photo_50":
|
||||
$rClubs[$i]->photo_50 = $usr->getAvatarURL();
|
||||
break;
|
||||
case "photo_100":
|
||||
$rClubs[$i]->photo_100 = $usr->getAvatarURL("tiny");
|
||||
break;
|
||||
case "photo_200":
|
||||
$rClubs[$i]->photo_200 = $usr->getAvatarURL("normal");
|
||||
break;
|
||||
case "photo_200_orig":
|
||||
$rClubs[$i]->photo_200_orig = $usr->getAvatarURL("normal");
|
||||
break;
|
||||
case "photo_400_orig":
|
||||
$rClubs[$i]->photo_400_orig = $usr->getAvatarURL("normal");
|
||||
break;
|
||||
case "members_count":
|
||||
$rClubs[$i]->members_count = $usr->getFollowersCount();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$rClubs = [];
|
||||
}
|
||||
|
||||
return (object) [
|
||||
"count" => $clbsCount,
|
||||
|
@ -91,14 +104,12 @@ final class Groups extends VKAPIRequestHandler
|
|||
|
||||
function getById(string $group_ids = "", string $group_id = "", string $fields = ""): ?array
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$clubs = new ClubsRepo;
|
||||
|
||||
if ($group_ids == null && $group_id != null)
|
||||
if($group_ids == NULL && $group_id != NULL)
|
||||
$group_ids = $group_id;
|
||||
|
||||
if ($group_ids == null && $group_id == null)
|
||||
if($group_ids == NULL && $group_id == NULL)
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: group_ids is undefined");
|
||||
|
||||
$clbs = explode(',', $group_ids);
|
||||
|
@ -114,8 +125,7 @@ final class Groups extends VKAPIRequestHandler
|
|||
$this->fail(100, "ты ошибся чутка, у айди группы убери минус");
|
||||
|
||||
$clb = $clubs->get((int) $clbs[$i]);
|
||||
if(is_null($clb))
|
||||
{
|
||||
if(is_null($clb)) {
|
||||
$response[$i] = (object)[
|
||||
"id" => intval($clbs[$i]),
|
||||
"name" => "DELETED",
|
||||
|
@ -123,7 +133,7 @@ final class Groups extends VKAPIRequestHandler
|
|||
"type" => "group",
|
||||
"description" => "This group was deleted or it doesn't exist"
|
||||
];
|
||||
}else if($clbs[$i] == null){
|
||||
} else if($clbs[$i] == NULL) {
|
||||
|
||||
} else {
|
||||
$response[$i] = (object)[
|
||||
|
@ -139,44 +149,65 @@ final class Groups extends VKAPIRequestHandler
|
|||
|
||||
foreach($flds as $field) {
|
||||
switch($field) {
|
||||
case 'verified':
|
||||
case "verified":
|
||||
$response[$i]->verified = intval($clb->isVerified());
|
||||
break;
|
||||
case 'has_photo':
|
||||
case "has_photo":
|
||||
$response[$i]->has_photo = is_null($clb->getAvatarPhoto()) ? 0 : 1;
|
||||
break;
|
||||
case 'photo_max_orig':
|
||||
case "photo_max_orig":
|
||||
$response[$i]->photo_max_orig = $clb->getAvatarURL();
|
||||
break;
|
||||
case 'photo_max':
|
||||
case "photo_max":
|
||||
$response[$i]->photo_max = $clb->getAvatarURL();
|
||||
break;
|
||||
case 'members_count':
|
||||
case "photo_50":
|
||||
$response[$i]->photo_50 = $clb->getAvatarURL();
|
||||
break;
|
||||
case "photo_100":
|
||||
$response[$i]->photo_100 = $clb->getAvatarURL("tiny");
|
||||
break;
|
||||
case "photo_200":
|
||||
$response[$i]->photo_200 = $clb->getAvatarURL("normal");
|
||||
break;
|
||||
case "photo_200_orig":
|
||||
$response[$i]->photo_200_orig = $clb->getAvatarURL("normal");
|
||||
break;
|
||||
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':
|
||||
case "site":
|
||||
$response[$i]->site = $clb->getWebsite();
|
||||
break;
|
||||
case 'description':
|
||||
case "description":
|
||||
$response[$i]->desctiption = $clb->getDescription();
|
||||
break;
|
||||
case 'contacts':
|
||||
case "contacts":
|
||||
$contacts;
|
||||
$contactTmp = $clb->getManagers(1, true);
|
||||
foreach($contactTmp as $contact) {
|
||||
|
||||
foreach($contactTmp as $contact)
|
||||
$contacts[] = array(
|
||||
'user_id' => $contact->getUser()->getId(),
|
||||
'desc' => $contact->getComment()
|
||||
"user_id" => $contact->getUser()->getId(),
|
||||
"desc" => $contact->getComment()
|
||||
);
|
||||
}
|
||||
|
||||
$response[$i]->contacts = $contacts;
|
||||
break;
|
||||
case 'can_post':
|
||||
case "can_post":
|
||||
if(!is_null($this->getUser()))
|
||||
if($clb->canBeModifiedBy($this->getUser()))
|
||||
$response[$i]->can_post = true;
|
||||
else
|
||||
$response[$i]->can_post = $clb->canPost();
|
||||
break;
|
||||
case "is_member":
|
||||
if(!is_null($this->getUser()))
|
||||
$response[$i]->is_member = (int) $clb->getSubscriptionStatus($this->getUser());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,38 +10,37 @@ final class Likes extends VKAPIRequestHandler
|
|||
$this->requireUser();
|
||||
|
||||
switch($type) {
|
||||
case 'post':
|
||||
case "post":
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||
if (is_null($post)) $this->fail(100, 'One of the parameters specified was missing or invalid: object not found');
|
||||
if(is_null($post))
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
||||
|
||||
$post->setLike(true, $this->getUser());
|
||||
|
||||
return (object) [
|
||||
"likes" => $post->getLikesCount()
|
||||
];
|
||||
break;
|
||||
default:
|
||||
$this->fail(100, 'One of the parameters specified was missing or invalid: incorrect type');
|
||||
break;
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: incorrect type");
|
||||
}
|
||||
}
|
||||
|
||||
function remove(string $type, int $owner_id, int $item_id): object
|
||||
function delete(string $type, int $owner_id, int $item_id): object
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
switch($type) {
|
||||
case 'post':
|
||||
case "post":
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||
if (is_null($post)) $this->fail(100, 'One of the parameters specified was missing or invalid: object not found');
|
||||
if (is_null($post))
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
||||
|
||||
$post->setLike(false, $this->getUser());
|
||||
return (object) [
|
||||
"likes" => $post->getLikesCount()
|
||||
];
|
||||
break;
|
||||
default:
|
||||
$this->fail(100, 'One of the parameters specified was missing or invalid: incorrect type');
|
||||
break;
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: incorrect type");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,25 +49,25 @@ final class Likes extends VKAPIRequestHandler
|
|||
$this->requireUser();
|
||||
|
||||
switch($type) {
|
||||
case 'post':
|
||||
case "post":
|
||||
$user = (new UsersRepo)->get($user_id);
|
||||
if (is_null($user)) return (object)[
|
||||
if (is_null($user))
|
||||
return (object) [
|
||||
"liked" => 0,
|
||||
"copied" => 0,
|
||||
"sex" => 0
|
||||
];
|
||||
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||
if (is_null($post)) $this->fail(100, 'One of the parameters specified was missing or invalid: object not found');
|
||||
if (is_null($post))
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: object not found");
|
||||
|
||||
return (object) [
|
||||
"liked" => (int) $post->hasLikeFrom($user),
|
||||
"copied" => 0 // TODO: handle this
|
||||
"copied" => 0 # TODO: handle this
|
||||
];
|
||||
break;
|
||||
default:
|
||||
$this->fail(100, 'One of the parameters specified was missing or invalid: incorrect type');
|
||||
break;
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: incorrect type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ final class Messages extends VKAPIRequestHandler
|
|||
if(!$peer)
|
||||
$this->fail(936, "There is no peer with this id");
|
||||
|
||||
if($this->getUser()->getId() !== $peer->getId() && $peer->getSubscriptionStatus($this->getUser()) !== 3)
|
||||
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!
|
||||
|
@ -123,9 +123,8 @@ final class Messages extends VKAPIRequestHandler
|
|||
$items = [];
|
||||
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;
|
||||
|
@ -153,6 +152,7 @@ final class Messages extends VKAPIRequestHandler
|
|||
$this->requireUser();
|
||||
|
||||
$convos = (new MSGRepo)->getCorrespondencies($this->getUser(), -1, $count, $offset);
|
||||
$convosCount = (new MSGRepo)->getCorrespondenciesCount($this->getUser());
|
||||
$list = [];
|
||||
|
||||
$users = [];
|
||||
|
@ -198,7 +198,6 @@ final class Messages extends VKAPIRequestHandler
|
|||
$lastMessagePreview->emoji = true;
|
||||
|
||||
if($extended == 1) {
|
||||
$users[] = $lastMessage->getSender()->getId();
|
||||
$users[] = $author;
|
||||
}
|
||||
}
|
||||
|
@ -211,21 +210,81 @@ final class Messages extends VKAPIRequestHandler
|
|||
|
||||
if($extended == 0){
|
||||
return (object) [
|
||||
"count" => sizeof($list),
|
||||
"count" => $convosCount,
|
||||
"items" => $list,
|
||||
];
|
||||
} else {
|
||||
$users[] = $this->getUser()->getId();
|
||||
$users = array_unique($users);
|
||||
|
||||
return (object) [
|
||||
"count" => sizeof($list),
|
||||
"count" => $convosCount,
|
||||
"items" => $list,
|
||||
"profiles" => (new APIUsers)->get(implode(',', $users), $fields, $offset, $count)
|
||||
"profiles" => (!empty($users) ? (new APIUsers)->get(implode(',', $users), $fields, 0, $count+1) : [])
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
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): object
|
||||
function getConversationsById(string $peer_ids, int $extended = 0, string $fields = "")
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$peers = explode(',', $peer_ids);
|
||||
|
||||
$output = [
|
||||
"count" => 0,
|
||||
"items" => []
|
||||
];
|
||||
|
||||
$userslist = [];
|
||||
|
||||
foreach($peers as $peer) {
|
||||
if(key($peers) > 100)
|
||||
continue;
|
||||
|
||||
if(is_null($user_id = $this->resolvePeer((int) $peer)))
|
||||
$this->fail(-151, "Chats are not implemented");
|
||||
|
||||
$user = (new USRRepo)->get((int) $peer);
|
||||
|
||||
$dialogue = new Correspondence($this->getUser(), $user);
|
||||
$iterator = $dialogue->getMessages(Correspondence::CAP_BEHAVIOUR_START_MESSAGE_ID, 0, 1, 0, false);
|
||||
$msg = $iterator[0]->unwrap(); // шоб удобнее было
|
||||
$output['items'][] = [
|
||||
"peer" => [
|
||||
"id" => $user->getId(),
|
||||
"type" => "user",
|
||||
"local_id" => $user->getId()
|
||||
],
|
||||
"last_message_id" => $msg->id,
|
||||
"in_read" => $msg->id,
|
||||
"out_read" => $msg->id,
|
||||
"sort_id" => [
|
||||
"major_id" => 0,
|
||||
"minor_id" => $msg->id, // КОНЕЧНО ЖЕ
|
||||
],
|
||||
"last_conversation_message_id" => $user->getId(),
|
||||
"in_read_cmid" => $user->getId(),
|
||||
"out_read_cmid" => $user->getId(),
|
||||
"is_marked_unread" => $iterator[0]->isUnread(),
|
||||
"important" => false, // целестора когда релиз
|
||||
"can_write" => [
|
||||
"allowed" => ($user->getId() === $this->getUser()->getId() || $user->getPrivacyPermission('messages.write', $this->getUser()) === true)
|
||||
]
|
||||
];
|
||||
$userslist[] = $user->getId();
|
||||
}
|
||||
|
||||
if($extended == 1) {
|
||||
$userslist = array_unique($userslist);
|
||||
$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
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
|
@ -257,10 +316,18 @@ final class Messages extends VKAPIRequestHandler
|
|||
$results[] = $rMsg;
|
||||
}
|
||||
|
||||
return (object) [
|
||||
$output = [
|
||||
"count" => sizeof($results),
|
||||
"items" => $results,
|
||||
];
|
||||
|
||||
if ($extended == 1) {
|
||||
$users[] = $this->getUser()->getId();
|
||||
$users[] = $user_id;
|
||||
$output["profiles"] = (!empty($users) ? (new APIUsers($this->getUser()))->get(implode(',', $users), $fields) : []);
|
||||
}
|
||||
|
||||
return (object) $output;
|
||||
}
|
||||
|
||||
function getLongPollHistory(int $ts = -1, int $preview_length = 0, int $events_limit = 1000, int $msgs_limit = 1000): object
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Entities\Post;
|
||||
use openvk\Web\Models\Entities\Postable;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
||||
use openvk\VKAPI\Handlers\Wall;
|
||||
|
@ -13,8 +10,6 @@ final class Newsfeed extends VKAPIRequestHandler
|
|||
{
|
||||
$this->requireUser();
|
||||
|
||||
if($offset != 0) $start_from = $offset;
|
||||
|
||||
$id = $this->getUser()->getId();
|
||||
$subs = DatabaseConnection::i()
|
||||
->getContext()
|
||||
|
@ -31,12 +26,15 @@ final class Newsfeed extends VKAPIRequestHandler
|
|||
->select("id")
|
||||
->where("wall IN (?)", $ids)
|
||||
->where("deleted", 0)
|
||||
->where("id < (?)", empty($start_from) ? time()+1 : $start_from)
|
||||
->order("created DESC");
|
||||
|
||||
$rposts = [];
|
||||
foreach($posts->page((int) ($offset + 1), $count) as $post)
|
||||
$rposts[] = (new PostsRepo)->get($post->id)->getPrettyId();
|
||||
|
||||
return (new Wall)->getById(implode(',', $rposts), $extended, $fields, $this->getUser());
|
||||
$response = (new Wall)->getById(implode(',', $rposts), $extended, $fields, $this->getUser());
|
||||
$response->next_from = end(end($posts->page((int) ($offset + 1), $count))); // ну и костыли пиздец конечно)
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?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
|
||||
{
|
||||
|
@ -21,4 +22,54 @@ final class Ovk extends VKAPIRequestHandler
|
|||
{
|
||||
return "крылышки";
|
||||
}
|
||||
|
||||
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();
|
||||
$response->statistics = (object) [
|
||||
"users_count" => $usersStats->all,
|
||||
"online_users_count" => $usersStats->online,
|
||||
"active_users_count" => $usersStats->active,
|
||||
"groups_count" => $clubsCount,
|
||||
"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) {
|
||||
return $admin->getId();
|
||||
}, $admins)), $admin_fields, 0, sizeof($admins));
|
||||
$response->administrators = (object) [
|
||||
"count" => sizeof($admins),
|
||||
"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) {
|
||||
return $entry->club->getId();
|
||||
}, $popularClubs)), "", "members_count, " . $group_fields);
|
||||
|
||||
$response->popular_groups = (object) [
|
||||
"count" => sizeof($popularClubs),
|
||||
"items" => $clubsResponse
|
||||
];
|
||||
}
|
||||
|
||||
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']
|
||||
];
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
|
42
VKAPI/Handlers/Pay.php
Normal file
42
VKAPI/Handlers/Pay.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?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
|
||||
{
|
||||
[$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)
|
||||
$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
|
||||
{
|
||||
$this->requireUser();
|
||||
|
||||
$app = (new Applications())->get($app_id);
|
||||
if(!$app)
|
||||
$this->fail(26, "No app found with this id");
|
||||
else if($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)
|
||||
$this->fail(4, "Invalid order");
|
||||
} catch (\SodiumException $e) {
|
||||
$this->fail(4, "Invalid order");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -7,34 +7,33 @@ final class Users extends VKAPIRequestHandler
|
|||
{
|
||||
function get(string $user_ids = "0", string $fields = "", int $offset = 0, int $count = 100, User $authuser = null /* костыль(( */): array
|
||||
{
|
||||
// $this->requireUser();
|
||||
|
||||
if($authuser == null) $authuser = $this->getUser();
|
||||
if($authuser == NULL) $authuser = $this->getUser();
|
||||
|
||||
$users = new UsersRepo;
|
||||
if($user_ids == "0")
|
||||
$user_ids = (string) $authuser->getId();
|
||||
|
||||
$usrs = explode(',', $user_ids);
|
||||
$response;
|
||||
$response = array();
|
||||
|
||||
$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))
|
||||
{
|
||||
if(is_null($usr) || $usr->isDeleted()) {
|
||||
$response[$i] = (object)[
|
||||
"id" => $usrs[$i],
|
||||
"id" => (int) $usrs[$i],
|
||||
"first_name" => "DELETED",
|
||||
"last_name" => "",
|
||||
"deactivated" => "deleted"
|
||||
];
|
||||
}else if($usrs[$i] == null){
|
||||
} else if($usrs[$i] == NULL) {
|
||||
|
||||
} else {
|
||||
$response[$i] = (object)[
|
||||
|
@ -49,32 +48,54 @@ final class Users extends VKAPIRequestHandler
|
|||
|
||||
foreach($flds as $field) {
|
||||
switch($field) {
|
||||
case 'verified':
|
||||
case "verified":
|
||||
$response[$i]->verified = intval($usr->isVerified());
|
||||
break;
|
||||
case 'sex':
|
||||
case "sex":
|
||||
$response[$i]->sex = $usr->isFemale() ? 1 : 2;
|
||||
break;
|
||||
case 'has_photo':
|
||||
case "has_photo":
|
||||
$response[$i]->has_photo = is_null($usr->getAvatarPhoto()) ? 0 : 1;
|
||||
break;
|
||||
case 'photo_max_orig':
|
||||
case "photo_max_orig":
|
||||
$response[$i]->photo_max_orig = $usr->getAvatarURL();
|
||||
break;
|
||||
case 'photo_max':
|
||||
$response[$i]->photo_max = $usr->getAvatarURL();
|
||||
case "photo_max":
|
||||
$response[$i]->photo_max = $usr->getAvatarURL("original");
|
||||
break;
|
||||
case 'status':
|
||||
if($usr->getStatus() != null)
|
||||
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();
|
||||
break;
|
||||
case 'screen_name':
|
||||
if($usr->getShortCode() != null)
|
||||
case "screen_name":
|
||||
if($usr->getShortCode() != NULL)
|
||||
$response[$i]->screen_name = $usr->getShortCode();
|
||||
break;
|
||||
case 'friend_status':
|
||||
case "friend_status":
|
||||
switch($usr->getSubscriptionStatus($authuser)) {
|
||||
case 3:
|
||||
# NOTICE falling through
|
||||
case 0:
|
||||
$response[$i]->friend_status = $usr->getSubscriptionStatus($authuser);
|
||||
break;
|
||||
|
@ -86,50 +107,44 @@ final class Users extends VKAPIRequestHandler
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case 'last_seen':
|
||||
if ($usr->onlineStatus() == 0) {
|
||||
case "last_seen":
|
||||
if ($usr->onlineStatus() == 0)
|
||||
$response[$i]->last_seen = (object) [
|
||||
"platform" => 1,
|
||||
"time" => $usr->getOnline()->timestamp()
|
||||
];
|
||||
}
|
||||
case 'music':
|
||||
case "music":
|
||||
$response[$i]->music = $usr->getFavoriteMusic();
|
||||
break;
|
||||
case 'movies':
|
||||
case "movies":
|
||||
$response[$i]->movies = $usr->getFavoriteFilms();
|
||||
break;
|
||||
case 'tv':
|
||||
case "tv":
|
||||
$response[$i]->tv = $usr->getFavoriteShows();
|
||||
break;
|
||||
case 'books':
|
||||
case "books":
|
||||
$response[$i]->books = $usr->getFavoriteBooks();
|
||||
break;
|
||||
case 'city':
|
||||
case "city":
|
||||
$response[$i]->city = $usr->getCity();
|
||||
break;
|
||||
case 'interests':
|
||||
case "interests":
|
||||
$response[$i]->interests = $usr->getInterests();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($usr->getOnline()->timestamp() + 300 > time()) {
|
||||
if($usr->getOnline()->timestamp() + 300 > time())
|
||||
$response[$i]->online = 1;
|
||||
}else{
|
||||
else
|
||||
$response[$i]->online = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/* private function getUsersById(string $user_ids, string $fields = "", int $offset = 0, int $count = PHP_INT_MAX){
|
||||
|
||||
} */
|
||||
|
||||
function getFollowers(int $user_id, string $fields = "", int $offset = 0, int $count = 100): object
|
||||
{
|
||||
$offset++;
|
||||
|
@ -139,15 +154,13 @@ final class Users extends VKAPIRequestHandler
|
|||
|
||||
$this->requireUser();
|
||||
|
||||
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)) {
|
||||
if(!is_null($fields))
|
||||
$response = $this->get(implode(',', $followers), $fields, 0, $count);
|
||||
}
|
||||
|
||||
return (object) [
|
||||
"count" => $users->get($user_id)->getFollowersCount(),
|
||||
|
@ -155,16 +168,15 @@ final class Users extends VKAPIRequestHandler
|
|||
];
|
||||
}
|
||||
|
||||
function search(string $q, string $fields = '', int $offset = 0, int $count = 100)
|
||||
function search(string $q, string $fields = "", int $offset = 0, int $count = 100)
|
||||
{
|
||||
$users = new UsersRepo;
|
||||
|
||||
$array = [];
|
||||
$find = $users->find($q);
|
||||
|
||||
foreach ($find as $user) {
|
||||
foreach ($find as $user)
|
||||
$array[] = $user->getId();
|
||||
}
|
||||
|
||||
return (object) [
|
||||
"count" => $find->size(),
|
||||
|
|
|
@ -1,49 +1,66 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\VKAPI\Handlers;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Entities\Notifications\{WallPostNotification};
|
||||
use openvk\Web\Models\Entities\Notifications\{WallPostNotification, RepostNotification, CommentNotification};
|
||||
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
use openvk\Web\Models\Repositories\Clubs as ClubsRepo;
|
||||
use openvk\Web\Models\Entities\Post;
|
||||
use openvk\Web\Models\Entities\Postable;
|
||||
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
||||
use openvk\Web\Models\Entities\Comment;
|
||||
use openvk\Web\Models\Repositories\Comments as CommentsRepo;
|
||||
|
||||
final class Wall extends VKAPIRequestHandler
|
||||
{
|
||||
function get(string $owner_id, string $domain = "", int $offset = 0, int $count = 30, int $extended = 0): object
|
||||
function get(int $owner_id, string $domain = "", int $offset = 0, int $count = 30, int $extended = 0): object
|
||||
{
|
||||
$posts = new PostsRepo;
|
||||
|
||||
$items = [];
|
||||
$profiles = [];
|
||||
$groups = [];
|
||||
$count = $posts->getPostCountOnUserWall((int) $owner_id);
|
||||
$cnt = $posts->getPostCountOnUserWall($owner_id);
|
||||
|
||||
foreach ($posts->getPostsFromUsersWall((int)$owner_id, 1, $count, $offset) as $post) {
|
||||
$wallOnwer = (new UsersRepo)->get($owner_id);
|
||||
|
||||
if(!$wallOnwer || $wallOnwer->isDeleted() || $wallOnwer->isDeleted())
|
||||
$this->fail(18, "User was deleted or banned");
|
||||
|
||||
foreach($posts->getPostsFromUsersWall($owner_id, 1, $count, $offset) as $post) {
|
||||
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
||||
|
||||
$attachments;
|
||||
foreach($post->getChildren() as $attachment)
|
||||
{
|
||||
if($attachment instanceof \openvk\Web\Models\Entities\Photo)
|
||||
{
|
||||
$attachments[] = [
|
||||
"type" => "photo",
|
||||
"photo" => [
|
||||
"album_id" => $attachment->getAlbum() ? $attachment->getAlbum()->getId() : null,
|
||||
"date" => $attachment->getPublicationTime()->timestamp(),
|
||||
$attachments = [];
|
||||
$repost = [];
|
||||
foreach($post->getChildren() as $attachment) {
|
||||
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||
if($attachment->isDeleted())
|
||||
continue;
|
||||
|
||||
$attachments[] = $this->getApiPhoto($attachment);
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||
$repostAttachments = [];
|
||||
|
||||
foreach($attachment->getChildren() as $repostAttachment) {
|
||||
if($repostAttachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||
if($attachment->isDeleted())
|
||||
continue;
|
||||
|
||||
$repostAttachments[] = $this->getApiPhoto($repostAttachment);
|
||||
/* Рекурсии, сука! Заказывали? */
|
||||
}
|
||||
}
|
||||
|
||||
$repost[] = [
|
||||
"id" => $attachment->getVirtualId(),
|
||||
"owner_id" => $attachment->getOwner()->getId(),
|
||||
"sizes" => array([
|
||||
"height" => 500, // Для временного компросима оставляю статическое число. Если каждый раз обращаться к файлу за количеством пикселов, то наступает пuпuська полная с производительностью, так что пока так
|
||||
"url" => $attachment->getURL(),
|
||||
"type" => "m",
|
||||
"width" => 500,
|
||||
]),
|
||||
"text" => "",
|
||||
"has_tags" => false
|
||||
]
|
||||
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
||||
"from_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
||||
"date" => $attachment->getPublicationTime()->timestamp(),
|
||||
"post_type" => "post",
|
||||
"text" => $attachment->getText(false),
|
||||
"attachments" => $repostAttachments,
|
||||
"post_source" => [
|
||||
"type" => "vk"
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +71,12 @@ final class Wall extends VKAPIRequestHandler
|
|||
"owner_id" => $post->getTargetWall(),
|
||||
"date" => $post->getPublicationTime()->timestamp(),
|
||||
"post_type" => "post",
|
||||
"text" => $post->getText(),
|
||||
"can_edit" => 0, // TODO
|
||||
"text" => $post->getText(false),
|
||||
"copy_history" => $repost,
|
||||
"can_edit" => 0, # TODO
|
||||
"can_delete" => $post->canBeDeletedBy($this->getUser()),
|
||||
"can_pin" => $post->canBePinnedBy($this->getUser()),
|
||||
"can_archive" => false, // TODO MAYBE
|
||||
"can_archive" => false, # TODO MAYBE
|
||||
"is_archived" => false,
|
||||
"is_pinned" => $post->isPinned(),
|
||||
"attachments" => $attachments,
|
||||
|
@ -84,11 +102,10 @@ final class Wall extends VKAPIRequestHandler
|
|||
else
|
||||
$groups[] = $from_id * -1;
|
||||
|
||||
$attachments = null; // free attachments so it will not clone everythingg
|
||||
$attachments = NULL; # free attachments so it will not clone everythingg
|
||||
}
|
||||
|
||||
if($extended == 1)
|
||||
{
|
||||
if($extended == 1) {
|
||||
$profiles = array_unique($profiles);
|
||||
$groups = array_unique($groups);
|
||||
|
||||
|
@ -126,57 +143,65 @@ final class Wall extends VKAPIRequestHandler
|
|||
}
|
||||
|
||||
return (object) [
|
||||
"count" => $count,
|
||||
"items" => (array)$items,
|
||||
"profiles" => (array)$profilesFormatted,
|
||||
"groups" => (array)$groupsFormatted
|
||||
"count" => $cnt,
|
||||
"items" => $items,
|
||||
"profiles" => $profilesFormatted,
|
||||
"groups" => $groupsFormatted
|
||||
];
|
||||
}
|
||||
else
|
||||
} else
|
||||
return (object) [
|
||||
"count" => $count,
|
||||
"items" => (array)$items
|
||||
"count" => $cnt,
|
||||
"items" => $items
|
||||
];
|
||||
}
|
||||
|
||||
function getById(string $posts, int $extended = 0, string $fields = "", User $user = null)
|
||||
function getById(string $posts, int $extended = 0, string $fields = "", User $user = NULL)
|
||||
{
|
||||
if($user == null) $user = $this->getUser(); // костыли костыли крылышки
|
||||
if($user == NULL) {
|
||||
$this->requireUser();
|
||||
$user = $this->getUser(); # костыли костыли крылышки
|
||||
}
|
||||
|
||||
$items = [];
|
||||
$profiles = [];
|
||||
$groups = [];
|
||||
# $count = $posts->getPostCountOnUserWall((int) $owner_id);
|
||||
|
||||
$psts = explode(",", $posts);
|
||||
$psts = explode(',', $posts);
|
||||
|
||||
foreach($psts as $pst)
|
||||
{
|
||||
foreach($psts as $pst) {
|
||||
$id = explode("_", $pst);
|
||||
$post = (new PostsRepo)->getPostById(intval($id[0]), intval($id[1]));
|
||||
if($post) {
|
||||
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
||||
$attachments;
|
||||
foreach($post->getChildren() as $attachment)
|
||||
{
|
||||
if($attachment instanceof \openvk\Web\Models\Entities\Photo)
|
||||
{
|
||||
$attachments[] = [
|
||||
"type" => "photo",
|
||||
"photo" => [
|
||||
"album_id" => $attachment->getAlbum() ? $attachment->getAlbum()->getId() : null,
|
||||
"date" => $attachment->getPublicationTime()->timestamp(),
|
||||
$attachments = [];
|
||||
$repost = []; // чел высрал семь сигарет 😳 помянем 🕯
|
||||
foreach($post->getChildren() as $attachment) {
|
||||
if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||
$attachments[] = $this->getApiPhoto($attachment);
|
||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||
$repostAttachments = [];
|
||||
|
||||
foreach($attachment->getChildren() as $repostAttachment) {
|
||||
if($repostAttachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||
if($attachment->isDeleted())
|
||||
continue;
|
||||
|
||||
$repostAttachments[] = $this->getApiPhoto($repostAttachment);
|
||||
/* Рекурсии, сука! Заказывали? */
|
||||
}
|
||||
}
|
||||
|
||||
$repost[] = [
|
||||
"id" => $attachment->getVirtualId(),
|
||||
"owner_id" => $attachment->getOwner()->getId(),
|
||||
"sizes" => array([
|
||||
"height" => 500, // я ещё я заебался вставлять одинаковый код в два разных места
|
||||
"url" => $attachment->getURL(),
|
||||
"type" => "m",
|
||||
"width" => 500,
|
||||
]),
|
||||
"text" => "",
|
||||
"has_tags" => false
|
||||
]
|
||||
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
||||
"from_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
||||
"date" => $attachment->getPublicationTime()->timestamp(),
|
||||
"post_type" => "post",
|
||||
"text" => $attachment->getText(false),
|
||||
"attachments" => $repostAttachments,
|
||||
"post_source" => [
|
||||
"type" => "vk"
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -187,11 +212,12 @@ final class Wall extends VKAPIRequestHandler
|
|||
"owner_id" => $post->getTargetWall(),
|
||||
"date" => $post->getPublicationTime()->timestamp(),
|
||||
"post_type" => "post",
|
||||
"text" => $post->getText(),
|
||||
"can_edit" => 0, // TODO
|
||||
"text" => $post->getText(false),
|
||||
"copy_history" => $repost,
|
||||
"can_edit" => 0, # TODO
|
||||
"can_delete" => $post->canBeDeletedBy($user),
|
||||
"can_pin" => $post->canBePinnedBy($user),
|
||||
"can_archive" => false, // TODO MAYBE
|
||||
"can_archive" => false, # TODO MAYBE
|
||||
"is_archived" => false,
|
||||
"is_pinned" => $post->isPinned(),
|
||||
"post_source" => (object)["type" => "vk"],
|
||||
|
@ -217,12 +243,12 @@ final class Wall extends VKAPIRequestHandler
|
|||
else
|
||||
$groups[] = $from_id * -1;
|
||||
|
||||
$attachments = null; // free attachments so it will not clone everythingg
|
||||
$attachments = NULL; # free attachments so it will not clone everything
|
||||
$repost = NULL; # same
|
||||
}
|
||||
}
|
||||
|
||||
if($extended == 1)
|
||||
{
|
||||
if($extended == 1) {
|
||||
$profiles = array_unique($profiles);
|
||||
$groups = array_unique($groups);
|
||||
|
||||
|
@ -264,8 +290,7 @@ final class Wall extends VKAPIRequestHandler
|
|||
"profiles" => (array)$profilesFormatted,
|
||||
"groups" => (array)$groupsFormatted
|
||||
];
|
||||
}
|
||||
else
|
||||
} else
|
||||
return (object) [
|
||||
"items" => (array)$items
|
||||
];
|
||||
|
@ -308,12 +333,12 @@ final class Wall extends VKAPIRequestHandler
|
|||
if($signed == 1)
|
||||
$flags |= 0b01000000;
|
||||
|
||||
// TODO: Compatible implementation of this
|
||||
# TODO: Compatible implementation of this
|
||||
try {
|
||||
$photo = null;
|
||||
$video = null;
|
||||
$photo = NULL;
|
||||
$video = NULL;
|
||||
if($_FILES["photo"]["error"] === UPLOAD_ERR_OK) {
|
||||
$album = null;
|
||||
$album = NULL;
|
||||
if(!$anon && $owner_id > 0 && $owner_id === $this->getUser()->getId())
|
||||
$album = (new AlbumsRepo)->getUserWallAlbum($wallOwner);
|
||||
|
||||
|
@ -354,4 +379,201 @@ final class Wall extends VKAPIRequestHandler
|
|||
|
||||
return (object)["post_id" => $post->getVirtualId()];
|
||||
}
|
||||
|
||||
function repost(string $object, string $message = "") {
|
||||
$this->requireUser();
|
||||
|
||||
$postArray;
|
||||
if(preg_match('/wall((?:-?)[0-9]+)_([0-9]+)/', $object, $postArray) == 0)
|
||||
$this->fail(100, "One of the parameters specified was missing or invalid: object is incorrect");
|
||||
|
||||
$post = (new PostsRepo)->getPostById((int) $postArray[1], (int) $postArray[2]);
|
||||
if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid");
|
||||
|
||||
$nPost = new Post;
|
||||
$nPost->setOwner($this->user->getId());
|
||||
$nPost->setWall($this->user->getId());
|
||||
$nPost->setContent($message);
|
||||
$nPost->save();
|
||||
$nPost->attach($post);
|
||||
|
||||
if($post->getOwner(false)->getId() !== $this->user->getId() && !($post->getOwner() instanceof Club))
|
||||
(new RepostNotification($post->getOwner(false), $post, $this->user->identity))->emit();
|
||||
|
||||
return (object) [
|
||||
"success" => 1, // 👍
|
||||
"post_id" => $nPost->getVirtualId(),
|
||||
"reposts_count" => $post->getRepostCount(),
|
||||
"likes_count" => $post->getLikesCount()
|
||||
];
|
||||
}
|
||||
|
||||
function getComments(int $owner_id, int $post_id, bool $need_likes = true, int $offset = 0, int $count = 10, string $fields = "sex,screen_name,photo_50,photo_100,online_info,online", string $sort = "asc", bool $extended = false) {
|
||||
$this->requireUser();
|
||||
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
|
||||
if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid");
|
||||
|
||||
$comments = (new CommentsRepo)->getCommentsByTarget($post, $offset+1, $count, $sort == "desc" ? "DESC" : "ASC");
|
||||
|
||||
$items = [];
|
||||
$profiles = [];
|
||||
|
||||
foreach($comments as $comment) {
|
||||
$item = [
|
||||
"id" => $comment->getId(),
|
||||
"from_id" => $comment->getOwner()->getId(),
|
||||
"date" => $comment->getPublicationTime()->timestamp(),
|
||||
"text" => $comment->getText(false),
|
||||
"post_id" => $post->getVirtualId(),
|
||||
"owner_id" => $post->isPostedOnBehalfOfGroup() ? $post->getOwner()->getId() * -1 : $post->getOwner()->getId(),
|
||||
"parents_stack" => [],
|
||||
"thread" => [
|
||||
"count" => 0,
|
||||
"items" => [],
|
||||
"can_post" => false,
|
||||
"show_reply_button" => true,
|
||||
"groups_can_post" => false,
|
||||
]
|
||||
];
|
||||
|
||||
if($need_likes == true)
|
||||
$item['likes'] = [
|
||||
"can_like" => 1,
|
||||
"count" => $comment->getLikesCount(),
|
||||
"user_likes" => (int) $comment->hasLikeFrom($this->getUser()),
|
||||
"can_publish" => 1
|
||||
];
|
||||
|
||||
$items[] = $item;
|
||||
if($extended == true)
|
||||
$profiles[] = $comment->getOwner()->getId();
|
||||
}
|
||||
|
||||
$response = [
|
||||
"count" => (new CommentsRepo)->getCommentsCountByTarget($post),
|
||||
"items" => $items,
|
||||
"current_level_count" => (new CommentsRepo)->getCommentsCountByTarget($post),
|
||||
"can_post" => true,
|
||||
"show_reply_button" => true,
|
||||
"groups_can_post" => false
|
||||
];
|
||||
|
||||
if($extended == true) {
|
||||
$profiles = array_unique($profiles);
|
||||
$response['profiles'] = (!empty($profiles) ? (new Users)->get(implode(',', $profiles), $fields) : []);
|
||||
}
|
||||
|
||||
return (object) $response;
|
||||
}
|
||||
|
||||
function getComment(int $owner_id, int $comment_id, bool $extended = false, string $fields = "sex,screen_name,photo_50,photo_100,online_info,online") {
|
||||
$this->requireUser();
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id); // один хуй айди всех комментов общий
|
||||
|
||||
$profiles = [];
|
||||
|
||||
$item = [
|
||||
"id" => $comment->getId(),
|
||||
"from_id" => $comment->getOwner()->getId(),
|
||||
"date" => $comment->getPublicationTime()->timestamp(),
|
||||
"text" => $comment->getText(false),
|
||||
"post_id" => $comment->getTarget()->getVirtualId(),
|
||||
"owner_id" => $comment->getTarget()->isPostedOnBehalfOfGroup() ? $comment->getTarget()->getOwner()->getId() * -1 : $comment->getTarget()->getOwner()->getId(),
|
||||
"parents_stack" => [],
|
||||
"likes" => [
|
||||
"can_like" => 1,
|
||||
"count" => $comment->getLikesCount(),
|
||||
"user_likes" => (int) $comment->hasLikeFrom($this->getUser()),
|
||||
"can_publish" => 1
|
||||
],
|
||||
"thread" => [
|
||||
"count" => 0,
|
||||
"items" => [],
|
||||
"can_post" => false,
|
||||
"show_reply_button" => true,
|
||||
"groups_can_post" => false,
|
||||
]
|
||||
];
|
||||
|
||||
if($extended == true)
|
||||
$profiles[] = $comment->getOwner()->getId();
|
||||
|
||||
$response = [
|
||||
"items" => [$item],
|
||||
"can_post" => true,
|
||||
"show_reply_button" => true,
|
||||
"groups_can_post" => false
|
||||
];
|
||||
|
||||
if($extended == true) {
|
||||
$profiles = array_unique($profiles);
|
||||
$response['profiles'] = (!empty($profiles) ? (new Users)->get(implode(',', $profiles), $fields) : []);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
function createComment(int $owner_id, int $post_id, string $message, int $from_group = 0) {
|
||||
$post = (new PostsRepo)->getPostById($owner_id, $post_id);
|
||||
if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid");
|
||||
|
||||
if($post->getTargetWall() < 0)
|
||||
$club = (new ClubsRepo)->get(abs($post->getTargetWall()));
|
||||
|
||||
$flags = 0;
|
||||
if($from_group != 0 && !is_null($club) && $club->canBeModifiedBy($this->user))
|
||||
$flags |= 0b10000000;
|
||||
|
||||
try {
|
||||
$comment = new Comment;
|
||||
$comment->setOwner($this->user->getId());
|
||||
$comment->setModel(get_class($post));
|
||||
$comment->setTarget($post->getId());
|
||||
$comment->setContent($message);
|
||||
$comment->setCreated(time());
|
||||
$comment->setFlags($flags);
|
||||
$comment->save();
|
||||
} catch (\LengthException $ex) {
|
||||
$this->fail(1, "ошибка про то что коммент большой слишком");
|
||||
}
|
||||
|
||||
if($post->getOwner()->getId() !== $this->user->getId())
|
||||
if(($owner = $post->getOwner()) instanceof User)
|
||||
(new CommentNotification($owner, $comment, $post, $this->user))->emit();
|
||||
|
||||
return (object) [
|
||||
"comment_id" => $comment->getId(),
|
||||
"parents_stack" => []
|
||||
];
|
||||
}
|
||||
|
||||
function deleteComment(int $comment_id) {
|
||||
$this->requireUser();
|
||||
|
||||
$comment = (new CommentsRepo)->get($comment_id);
|
||||
if(!$comment) $this->fail(100, "One of the parameters specified was missing or invalid");;
|
||||
if(!$comment->canBeDeletedBy($this->user))
|
||||
$this->fail(7, "Access denied");
|
||||
|
||||
$comment->delete();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private function getApiPhoto($attachment) {
|
||||
return [
|
||||
"type" => "photo",
|
||||
"photo" => [
|
||||
"album_id" => $attachment->getAlbum() ? $attachment->getAlbum()->getId() : NULL,
|
||||
"date" => $attachment->getPublicationTime()->timestamp(),
|
||||
"id" => $attachment->getVirtualId(),
|
||||
"owner_id" => $attachment->getOwner()->getId(),
|
||||
"sizes" => array_values($attachment->getVkApiSizes()),
|
||||
"text" => "",
|
||||
"has_tags" => false
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
16
Vagrantfile
vendored
16
Vagrantfile
vendored
|
@ -1,16 +1,22 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "freebsd/FreeBSD-12.1-STABLE"
|
||||
config.vm.box = "freebsd/FreeBSD-13.1-RC2"
|
||||
config.vm.box_version = "2022.04.07"
|
||||
|
||||
config.vm.network "forwarded_port", guest: 80, host: 4000
|
||||
|
||||
config.vm.synced_folder ".", "/.ovk_release"
|
||||
|
||||
config.vm.provider "virtualbox" do |vb|
|
||||
vb.gui = true
|
||||
vb.memory = "1024"
|
||||
vb.cpus = 4
|
||||
vb.memory = "1568"
|
||||
end
|
||||
|
||||
config.vm.provision "shell", inline: "/bin/tcsh /.ovk_release/install/automated/freebsd-12/install"
|
||||
config.vm.provider "vmware_workstation" do |vwx|
|
||||
vwx.gui = true
|
||||
vwx.vmx["memsize"] = "1568"
|
||||
vwx.vmx["numvcpus"] = "4"
|
||||
end
|
||||
|
||||
config.vm.provision "shell", inline: "/bin/tcsh /.ovk_release/install/automated/freebsd-13/install"
|
||||
end
|
||||
|
|
316
Web/Models/Entities/Application.php
Normal file
316
Web/Models/Entities/Application.php
Normal file
|
@ -0,0 +1,316 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use Nette\Utils\Image;
|
||||
use Nette\Utils\UnknownImageFileException;
|
||||
use openvk\Web\Models\Repositories\Notes;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\RowModel;
|
||||
|
||||
class Application extends RowModel
|
||||
{
|
||||
protected $tableName = "apps";
|
||||
|
||||
const PERMS = [
|
||||
"notify",
|
||||
"friends",
|
||||
"photos",
|
||||
"audio",
|
||||
"video",
|
||||
"stories",
|
||||
"pages",
|
||||
"status",
|
||||
"notes",
|
||||
"messages",
|
||||
"wall",
|
||||
"ads",
|
||||
"docs",
|
||||
"groups",
|
||||
"notifications",
|
||||
"stats",
|
||||
"email",
|
||||
"market",
|
||||
];
|
||||
|
||||
private function getAvatarsDir(): string
|
||||
{
|
||||
$uploadSettings = OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"];
|
||||
if($uploadSettings["mode"] === "server" && $uploadSettings["server"]["kind"] === "cdn")
|
||||
return $uploadSettings["server"]["directory"];
|
||||
else
|
||||
return OPENVK_ROOT . "/storage/";
|
||||
}
|
||||
|
||||
function getId(): int
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getOwner(): User
|
||||
{
|
||||
return (new Users)->get($this->getRecord()->owner);
|
||||
}
|
||||
|
||||
function getName(): string
|
||||
{
|
||||
return $this->getRecord()->name;
|
||||
}
|
||||
|
||||
function getDescription(): string
|
||||
{
|
||||
return $this->getRecord()->description;
|
||||
}
|
||||
|
||||
function getAvatarUrl(): string
|
||||
{
|
||||
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
|
||||
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"]) {
|
||||
default:
|
||||
case "default":
|
||||
case "basic":
|
||||
return "$serverUrl/blob_" . substr($hash, 0, 2) . "/$hash" . "_app_avatar.png";
|
||||
case "accelerated":
|
||||
return "$serverUrl/openvk-datastore/$hash" . "_app_avatar.png";
|
||||
case "server":
|
||||
$settings = (object) OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["server"];
|
||||
return (
|
||||
$settings->protocol ?? ovk_scheme() .
|
||||
"://" . $settings->host .
|
||||
$settings->path .
|
||||
substr($hash, 0, 2) . "/$hash" . "_app_avatar.png"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getNote(): ?Note
|
||||
{
|
||||
if(!$this->getRecord()->news)
|
||||
return NULL;
|
||||
|
||||
return (new Notes)->get($this->getRecord()->news);
|
||||
}
|
||||
|
||||
function getNoteLink(): string
|
||||
{
|
||||
$note = $this->getNote();
|
||||
if(!$note)
|
||||
return "";
|
||||
|
||||
return ovk_scheme(true) . $_SERVER["HTTP_HOST"] . "/note" . $note->getPrettyId();
|
||||
}
|
||||
|
||||
function getBalance(): float
|
||||
{
|
||||
return $this->getRecord()->coins;
|
||||
}
|
||||
|
||||
function getURL(): string
|
||||
{
|
||||
return $this->getRecord()->address;
|
||||
}
|
||||
|
||||
function getOrigin(): string
|
||||
{
|
||||
$parsed = parse_url($this->getURL());
|
||||
|
||||
return (
|
||||
($parsed["scheme"] ?? "https") . "://"
|
||||
. ($parsed["host"] ?? "127.0.0.1") . ":"
|
||||
. ($parsed["port"] ?? "443")
|
||||
);
|
||||
}
|
||||
|
||||
function getUsersCount(): int
|
||||
{
|
||||
$cx = DatabaseConnection::i()->getContext();
|
||||
return sizeof($cx->table("app_users")->where("app", $this->getId()));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return $entry->toArray();
|
||||
}
|
||||
|
||||
function getPermissions(User $user): array
|
||||
{
|
||||
$permMask = 0;
|
||||
$installInfo = $this->getInstallationEntry($user);
|
||||
if(!$installInfo)
|
||||
$this->install($user);
|
||||
else
|
||||
$permMask = $installInfo["access"];
|
||||
|
||||
$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
|
||||
{
|
||||
return !is_null($this->getInstallationEntry($user));
|
||||
}
|
||||
|
||||
function setNoteLink(?string $link): bool
|
||||
{
|
||||
if(!$link) {
|
||||
$this->stateChanges("news", NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
preg_match("%note([0-9]+)_([0-9]+)$%", $link, $matches);
|
||||
if(sizeof($matches) != 3)
|
||||
return false;
|
||||
|
||||
$owner = is_null($this->getRecord()) ? $this->changes["owner"] : $this->getRecord()->owner;
|
||||
[, $ownerId, $vid] = $matches;
|
||||
if($ownerId != $owner)
|
||||
return false;
|
||||
|
||||
$note = (new Notes)->getNoteById((int) $ownerId, (int) $vid);
|
||||
if(!$note)
|
||||
return false;
|
||||
|
||||
$this->stateChanges("news", $note->getId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function setAvatar(array $file): int
|
||||
{
|
||||
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)))
|
||||
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
|
||||
{
|
||||
$permMask = 0;
|
||||
$installInfo = $this->getInstallationEntry($user);
|
||||
if(!$installInfo)
|
||||
$this->install($user);
|
||||
else
|
||||
$permMask = $installInfo["access"];
|
||||
|
||||
$index = array_search($perm, self::PERMS);
|
||||
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(),
|
||||
"user" => $user->getId(),
|
||||
])->update([
|
||||
"access" => $permMask,
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isEnabled(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->enabled;
|
||||
}
|
||||
|
||||
function enable(): void
|
||||
{
|
||||
$this->stateChanges("enabled", 1);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function disable(): void
|
||||
{
|
||||
$this->stateChanges("enabled", 0);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function install(User $user): void
|
||||
{
|
||||
if(!$this->getInstallationEntry($user)) {
|
||||
$cx = DatabaseConnection::i()->getContext();
|
||||
$cx->table("app_users")->insert([
|
||||
"app" => $this->getId(),
|
||||
"user" => $user->getId(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
function uninstall(User $user): void
|
||||
{
|
||||
$cx = DatabaseConnection::i()->getContext();
|
||||
$cx->table("app_users")->where([
|
||||
"app" => $this->getId(),
|
||||
"user" => $user->getId(),
|
||||
])->delete();
|
||||
}
|
||||
|
||||
function addCoins(float $coins): float
|
||||
{
|
||||
$res = $this->getBalance() + $coins;
|
||||
$this->stateChanges("coins", $res);
|
||||
$this->save();
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if($softly)
|
||||
throw new \UnexpectedValueException("Can't delete apps softly.");
|
||||
|
||||
$cx = DatabaseConnection::i()->getContext();
|
||||
$cx->table("app_users")->where("app", $this->getId())->delete();
|
||||
|
||||
parent::delete(false);
|
||||
}
|
||||
}
|
49
Web/Models/Entities/BannedLink.php
Normal file
49
Web/Models/Entities/BannedLink.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?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};
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
|
||||
class BannedLink extends RowModel
|
||||
{
|
||||
protected $tableName = "links_banned";
|
||||
private $overrideContentColumn = "reason";
|
||||
|
||||
function getId(): int
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getDomain(): string
|
||||
{
|
||||
return $this->getRecord()->domain;
|
||||
}
|
||||
|
||||
function getReason(): string
|
||||
{
|
||||
return $this->getRecord()->reason ?? tr("url_is_banned_default_reason");
|
||||
}
|
||||
|
||||
function getInitiator(): ?User
|
||||
{
|
||||
return (new Users)->get($this->getRecord()->initiator);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return addslashes("/" . $this->getDomain() . $this->getRawRegexp() . "/");
|
||||
}
|
||||
|
||||
function getRawRegexp(): string
|
||||
{
|
||||
return $this->getRecord()->regexp_rule;
|
||||
}
|
||||
}
|
|
@ -67,7 +67,7 @@ class Club extends RowModel
|
|||
|
||||
function getName(): string
|
||||
{
|
||||
return ovk_proc_strtr($this->getRecord()->name, 32);
|
||||
return $this->getRecord()->name;
|
||||
}
|
||||
|
||||
function getCanonicalName(): string
|
||||
|
@ -100,6 +100,14 @@ class Club extends RowModel
|
|||
return $this->getRecord()->about;
|
||||
}
|
||||
|
||||
function getDescriptionHtml(): ?string
|
||||
{
|
||||
if(!is_null($this->getDescription()))
|
||||
return nl2br(htmlspecialchars($this->getDescription(), ENT_DISALLOWED | ENT_XHTML));
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
function getShortCode(): ?string
|
||||
{
|
||||
return $this->getRecord()->shortcode;
|
||||
|
@ -302,8 +310,8 @@ class Club extends RowModel
|
|||
{
|
||||
$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;
|
||||
}
|
||||
|
@ -347,5 +355,10 @@ class Club extends RowModel
|
|||
return $this->getRecord()->website;
|
||||
}
|
||||
|
||||
function getAlert(): ?string
|
||||
{
|
||||
return $this->getRecord()->alert;
|
||||
}
|
||||
|
||||
use Traits\TSubscribable;
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ class Correspondence
|
|||
*/
|
||||
function getPreviewMessage(): ?Message
|
||||
{
|
||||
$messages = $this->getMessages(1, null, 1);
|
||||
$messages = $this->getMessages(1, NULL, 1);
|
||||
return $messages[0] ?? NULL;
|
||||
}
|
||||
|
||||
|
|
15
Web/Models/Entities/EmailChangeVerification.php
Normal file
15
Web/Models/Entities/EmailChangeVerification.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?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 EmailChangeVerification extends PasswordReset
|
||||
{
|
||||
protected $tableName = "email_change_verifications";
|
||||
|
||||
function getNewEmail(): string
|
||||
{
|
||||
return $this->getRecord()->new_email;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ abstract class Media extends Postable
|
|||
{
|
||||
protected $fileExtension = "oct"; #octet stream xddd
|
||||
protected $upperNodeReferenceColumnName = "owner";
|
||||
protected $processingPlaceholder = NULL;
|
||||
protected $processingTime = 30;
|
||||
|
||||
function __destruct()
|
||||
{
|
||||
|
@ -23,6 +25,11 @@ abstract class Media extends Postable
|
|||
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
|
||||
|
@ -41,6 +48,10 @@ abstract class Media extends Postable
|
|||
|
||||
function getURL(): string
|
||||
{
|
||||
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"]) {
|
||||
|
@ -55,7 +66,7 @@ abstract class Media extends Postable
|
|||
case "server":
|
||||
$settings = (object) OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["server"];
|
||||
return (
|
||||
$settings->protocol .
|
||||
$settings->protocol ?? ovk_scheme() .
|
||||
"://" . $settings->host .
|
||||
$settings->path .
|
||||
substr($hash, 0, 2) . "/$hash.$this->fileExtension"
|
||||
|
@ -69,6 +80,26 @@ abstract class Media extends Postable
|
|||
return $this->getRecord()->description;
|
||||
}
|
||||
|
||||
protected function isProcessed(): bool
|
||||
{
|
||||
if(is_null($this->processingPlaceholder))
|
||||
return true;
|
||||
|
||||
if($this->getRecord()->processed)
|
||||
return true;
|
||||
|
||||
$timeDiff = time() - $this->getRecord()->last_checked;
|
||||
if($timeDiff < $this->processingTime)
|
||||
return false;
|
||||
|
||||
$res = $this->checkIfFileIsProcessed();
|
||||
$this->stateChanges("last_checked", time());
|
||||
$this->stateChanges("processed", $res);
|
||||
$this->save();
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
function isDeleted(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->deleted;
|
||||
|
@ -90,6 +121,16 @@ abstract class Media extends Postable
|
|||
$this->stateChanges("hash", $hash);
|
||||
}
|
||||
|
||||
function save(): void
|
||||
{
|
||||
if(!is_null($this->processingPlaceholder) && is_null($this->getRecord())) {
|
||||
$this->stateChanges("processed", 0);
|
||||
$this->stateChanges("last_checked", time());
|
||||
}
|
||||
|
||||
parent::save();
|
||||
}
|
||||
|
||||
function delete(bool $softly = true): void
|
||||
{
|
||||
$deleteQuirk = ovkGetQuirk("blobs.erase-upon-deletion");
|
||||
|
|
|
@ -61,6 +61,17 @@ class Message extends RowModel
|
|||
return new DateTime($this->getRecord()->created);
|
||||
}
|
||||
|
||||
function getSendTimeHumanized(): string
|
||||
{
|
||||
$dateTime = new DateTime($this->getRecord()->created);
|
||||
|
||||
if($dateTime->format("%d.%m.%y") == ovk_strftime_safe("%d.%m.%y", time())) {
|
||||
return $dateTime->format("%T %p");
|
||||
} else {
|
||||
return $dateTime->format("%d.%m.%y");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date of last edit, if any edits were made, otherwise null.
|
||||
*
|
||||
|
@ -125,8 +136,8 @@ class Message extends RowModel
|
|||
"name" => $author->getFirstName().$unreadmsg,
|
||||
],
|
||||
"timing" => [
|
||||
"sent" => (string) $this->getSendTime()->format("%e %B %G" . tr("time_at_sp") . "%X"),
|
||||
"edited" => is_null($this->getEditTime()) ? null : (string) $this->getEditTime(),
|
||||
"sent" => (string) $this->getSendTimeHumanized(),
|
||||
"edited" => is_null($this->getEditTime()) ? NULL : (string) $this->getEditTime(),
|
||||
],
|
||||
"text" => $this->getText(),
|
||||
"read" => !$this->isUnread(),
|
||||
|
|
|
@ -75,8 +75,18 @@ class Note extends Postable
|
|||
"underline",
|
||||
]);
|
||||
|
||||
$source = NULL;
|
||||
if(is_null($this->getRecord())) {
|
||||
if(isset($this->changes["source"]))
|
||||
$source = $this->changes["source"];
|
||||
else
|
||||
throw new \LogicException("Can't render note without content set.");
|
||||
} else {
|
||||
$source = $this->getRecord()->source;
|
||||
}
|
||||
|
||||
$purifier = new HTMLPurifier($config);
|
||||
return $purifier->purify($this->getRecord()->source);
|
||||
return $purifier->purify($source);
|
||||
}
|
||||
|
||||
function getName(): string
|
||||
|
@ -91,6 +101,9 @@ class Note extends Postable
|
|||
|
||||
function getText(): string
|
||||
{
|
||||
if(is_null($this->getRecord()))
|
||||
return $this->renderHTML();
|
||||
|
||||
$cached = $this->getRecord()->cached_content;
|
||||
if(!$cached) {
|
||||
$cached = $this->renderHTML();
|
||||
|
|
|
@ -165,8 +165,11 @@ class Photo extends Media
|
|||
foreach($manifest->Size as $size)
|
||||
$mappings[(string) $size["id"]] = (string) $size["vkId"];
|
||||
|
||||
foreach($sizes as $id => $meta)
|
||||
$res[$mappings[$id] ?? $id] = $meta;
|
||||
foreach($sizes as $id => $meta) {
|
||||
$type = $mappings[$id] ?? $id;
|
||||
$meta->type = $type;
|
||||
$res[$type] = $meta;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
@ -240,8 +243,11 @@ 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();
|
||||
}
|
||||
|
||||
return $photo;
|
||||
}
|
||||
|
|
|
@ -85,6 +85,11 @@ class Post extends Postable
|
|||
return ($this->getRecord()->flags & 0b01000000) > 0;
|
||||
}
|
||||
|
||||
function isDeactivationMessage(): bool
|
||||
{
|
||||
return ($this->getRecord()->flags & 0b00100000) > 0;
|
||||
}
|
||||
|
||||
function isExplicit(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->nsfw;
|
||||
|
|
|
@ -87,7 +87,7 @@ abstract class Postable extends Attachable
|
|||
]));
|
||||
}
|
||||
|
||||
// TODO add pagination
|
||||
# TODO add pagination
|
||||
function getLikers(): \Traversable
|
||||
{
|
||||
$sel = DB::i()->getContext()->table("likes")->where([
|
||||
|
|
|
@ -35,12 +35,12 @@ trait TRichText
|
|||
"%(([A-z]++):\/\/(\S*?\.\S*?))([\s)\[\]{},\"\'<]|\.\s|$)%",
|
||||
(function (array $matches): string {
|
||||
$href = str_replace("#", "#", $matches[1]);
|
||||
$href = str_replace(";", ";", $matches[1]);
|
||||
$href = rawurlencode(str_replace(";", ";", $matches[1]));
|
||||
$link = str_replace("#", "#", $matches[3]);
|
||||
$link = str_replace(";", ";", $matches[3]);
|
||||
$rel = $this->isAd() ? "sponsored" : "ugc";
|
||||
|
||||
return "<a href='$href' rel='$rel' target='_blank'>$link</a>" . htmlentities($matches[4]);
|
||||
return "<a href='/away.php?to=$href' rel='$rel' target='_blank'>$link</a>" . htmlentities($matches[4]);
|
||||
}),
|
||||
$text
|
||||
);
|
||||
|
@ -55,21 +55,28 @@ trait TRichText
|
|||
{
|
||||
$contentColumn = property_exists($this, "overrideContentColumn") ? $this->overrideContentColumn : "content";
|
||||
|
||||
$text = htmlentities($this->getRecord()->{$contentColumn}, ENT_DISALLOWED | ENT_XHTML);
|
||||
$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) {
|
||||
$rel = $this->isAd() ? "sponsored" : "ugc";
|
||||
$text = $this->formatLinks($text);
|
||||
$text = preg_replace("%@([A-Za-z0-9]++) \(([\p{L} 0-9]+)\)%Xu", "[$1|$2]", $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} 0-9@]+)\]%Xu", "<a href='/$1'>$2</a>", $text);
|
||||
$text = preg_replace("%([\n\r\s]|^)(#([\p{L}_-]++[0-9]*[\p{L}_-]*))%Xu", "$1<a href='/feed/hashtag/$3'>$2</a>", $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) {
|
||||
$slug = rawurlencode($m[3]);
|
||||
|
||||
return "$m[1]<a href='/feed/hashtag/$slug'>$m[2]</a>";
|
||||
}, $text);
|
||||
|
||||
$text = $this->formatEmojis($text);
|
||||
}
|
||||
|
||||
$text = $this->removeZalgo($text);
|
||||
$text = nl2br($text);
|
||||
} else {
|
||||
$text = str_replace("\r\n","\n", $text);
|
||||
}
|
||||
|
||||
if(OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["christian"])
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use morphos\Gender;
|
||||
use openvk\Web\Themes\{Themepack, Themepacks};
|
||||
use openvk\Web\Util\DateTime;
|
||||
use openvk\Web\Models\RowModel;
|
||||
|
@ -9,6 +10,7 @@ use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
|||
use Nette\Database\Table\ActiveRow;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use Chandler\Security\User as ChandlerUser;
|
||||
use function morphos\Russian\inflectName;
|
||||
|
||||
class User extends RowModel
|
||||
{
|
||||
|
@ -84,6 +86,11 @@ class User extends RowModel
|
|||
return (bool) $this->getRecord()->microblog;
|
||||
}
|
||||
|
||||
function getMainPage(): int
|
||||
{
|
||||
return $this->getRecord()->main_page;
|
||||
}
|
||||
|
||||
function getChandlerGUID(): string
|
||||
{
|
||||
return $this->getRecord()->user;
|
||||
|
@ -138,24 +145,32 @@ class User extends RowModel
|
|||
return iterator_to_array($avPhotos)[0] ?? NULL;
|
||||
}
|
||||
|
||||
function getFirstName(): string
|
||||
function getFirstName(bool $pristine = false): string
|
||||
{
|
||||
return $this->getRecord()->deleted ? "DELETED" : mb_convert_case($this->getRecord()->first_name, MB_CASE_TITLE);
|
||||
$name = ($this->isDeleted() && !$this->isDeactivated() ? "DELETED" : mb_convert_case($this->getRecord()->first_name, MB_CASE_TITLE));
|
||||
if((($ts = tr("__transNames")) !== "@__transNames") && !$pristine)
|
||||
return mb_convert_case(transliterator_transliterate($ts, $name), MB_CASE_TITLE);
|
||||
else
|
||||
return $name;
|
||||
}
|
||||
|
||||
function getLastName(): string
|
||||
function getLastName(bool $pristine = false): string
|
||||
{
|
||||
return $this->getRecord()->deleted ? "DELETED" : mb_convert_case($this->getRecord()->last_name, MB_CASE_TITLE);
|
||||
$name = ($this->isDeleted() && !$this->isDeactivated() ? "DELETED" : mb_convert_case($this->getRecord()->last_name, MB_CASE_TITLE));
|
||||
if((($ts = tr("__transNames")) !== "@__transNames") && !$pristine)
|
||||
return mb_convert_case(transliterator_transliterate($ts, $name), MB_CASE_TITLE);
|
||||
else
|
||||
return $name;
|
||||
}
|
||||
|
||||
function getPseudo(): ?string
|
||||
{
|
||||
return $this->getRecord()->deleted ? "DELETED" : $this->getRecord()->pseudo;
|
||||
return ($this->isDeleted() && !$this->isDeactivated() ? "DELETED" : $this->getRecord()->pseudo);
|
||||
}
|
||||
|
||||
function getFullName(): string
|
||||
{
|
||||
if($this->getRecord()->deleted)
|
||||
if($this->isDeleted() && !$this->isDeactivated())
|
||||
return "DELETED";
|
||||
|
||||
$pseudo = $this->getPseudo();
|
||||
|
@ -167,12 +182,23 @@ class User extends RowModel
|
|||
return $this->getFirstName() . $pseudo . $this->getLastName();
|
||||
}
|
||||
|
||||
function getMorphedName(string $case = "genitive", bool $fullName = true): string
|
||||
{
|
||||
$name = $fullName ? ($this->getLastName() . " " . $this->getFirstName()) : $this->getFirstName();
|
||||
if(!preg_match("%^[А-яё\-]+$%", $name))
|
||||
return $name; # name is probably not russian
|
||||
|
||||
$inflected = inflectName($name, $case, $this->isFemale() ? Gender::FEMALE : Gender::MALE);
|
||||
|
||||
return $inflected ?: $name;
|
||||
}
|
||||
|
||||
function getCanonicalName(): string
|
||||
{
|
||||
if($this->getRecord()->deleted)
|
||||
if($this->isDeleted() && !$this->isDeactivated())
|
||||
return "DELETED";
|
||||
else
|
||||
return $this->getFirstName() . ' ' . $this->getLastName();
|
||||
return $this->getFirstName() . " " . $this->getLastName();
|
||||
}
|
||||
|
||||
function getPhone(): ?string
|
||||
|
@ -268,6 +294,19 @@ class User extends RowModel
|
|||
return $this->getRecord()->marital_status;
|
||||
}
|
||||
|
||||
function getLocalizedMaritalStatus(): string
|
||||
{
|
||||
$status = $this->getMaritalStatus();
|
||||
$string = "relationship_$status";
|
||||
if($this->isFemale()) {
|
||||
$res = tr($string . "_fem");
|
||||
if($res != ("@" . $string . "_fem"))
|
||||
return $res; # If fem version exists, return
|
||||
}
|
||||
|
||||
return tr($string);
|
||||
}
|
||||
|
||||
function getContactEmail(): ?string
|
||||
{
|
||||
return $this->getRecord()->email_contact;
|
||||
|
@ -325,9 +364,17 @@ class User extends RowModel
|
|||
|
||||
function getBirthday(): ?DateTime
|
||||
{
|
||||
if(is_null($this->getRecord()->birthday))
|
||||
return NULL;
|
||||
else
|
||||
return new DateTime($this->getRecord()->birthday);
|
||||
}
|
||||
|
||||
function getBirthdayPrivacy(): int
|
||||
{
|
||||
return $this->getRecord()->birthday_privacy;
|
||||
}
|
||||
|
||||
function getAge(): ?int
|
||||
{
|
||||
return (int)floor((time() - $this->getBirthday()->timestamp()) / YEAR);
|
||||
|
@ -453,6 +500,16 @@ class User extends RowModel
|
|||
return $this->_abstractRelationCount("get-friends");
|
||||
}
|
||||
|
||||
function getFriendsOnline(int $page = 1, int $limit = 6): \Traversable
|
||||
{
|
||||
return $this->_abstractRelationGenerator("get-online-friends", $page, $limit);
|
||||
}
|
||||
|
||||
function getFriendsOnlineCount(): int
|
||||
{
|
||||
return $this->_abstractRelationCount("get-online-friends");
|
||||
}
|
||||
|
||||
function getFollowers(int $page = 1, int $limit = 6): \Traversable
|
||||
{
|
||||
return $this->_abstractRelationGenerator("get-followers", $page, $limit);
|
||||
|
@ -711,7 +768,7 @@ class User extends RowModel
|
|||
]);
|
||||
}
|
||||
|
||||
function ban(string $reason, bool $deleteSubscriptions = true): void
|
||||
function ban(string $reason, bool $deleteSubscriptions = true, ?int $unban_time = NULL): void
|
||||
{
|
||||
if($deleteSubscriptions) {
|
||||
$subs = DatabaseConnection::i()->getContext()->table("subscriptions");
|
||||
|
@ -725,9 +782,31 @@ class User extends RowModel
|
|||
}
|
||||
|
||||
$this->setBlock_Reason($reason);
|
||||
$this->setUnblock_time($unban_time);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function deactivate(?string $reason): void
|
||||
{
|
||||
$this->setDeleted(1);
|
||||
$this->setDeact_Date(time() + (MONTH * 7));
|
||||
$this->setDeact_Reason($reason);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function reactivate(): void
|
||||
{
|
||||
$this->setDeleted(0);
|
||||
$this->setDeact_Date(0);
|
||||
$this->setDeact_Reason("");
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function getDeactivationDate(): DateTime
|
||||
{
|
||||
return new DateTime($this->getRecord()->deact_date);
|
||||
}
|
||||
|
||||
function verifyNumber(string $code): bool
|
||||
{
|
||||
$ver = $this->getPendingPhoneVerification();
|
||||
|
@ -864,6 +943,17 @@ class User extends RowModel
|
|||
return true;
|
||||
}
|
||||
|
||||
function changeEmail(string $email): void
|
||||
{
|
||||
DatabaseConnection::i()->getContext()->table("ChandlerUsers")
|
||||
->where("id", $this->getChandlerUser()->getId())->update([
|
||||
"login" => $email
|
||||
]);
|
||||
|
||||
$this->stateChanges("email", $email);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
function adminNotify(string $message): bool
|
||||
{
|
||||
$admId = OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"];
|
||||
|
@ -888,6 +978,14 @@ class User extends RowModel
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
function isDeactivated(): bool
|
||||
{
|
||||
if($this->getDeactivationDate()->timestamp() > time())
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 0 - Default status
|
||||
* 1 - Incognito online status
|
||||
|
@ -915,11 +1013,27 @@ class User extends RowModel
|
|||
return $this->getRecord()->website;
|
||||
}
|
||||
|
||||
// ты устрица
|
||||
# ты устрица
|
||||
function isActivated(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->activated;
|
||||
}
|
||||
|
||||
function getUnbanTime(): ?string
|
||||
{
|
||||
return !is_null($this->getRecord()->unblock_time) ? date('d.m.Y', $this->getRecord()->unblock_time) : NULL;
|
||||
}
|
||||
|
||||
function canUnbanThemself(): bool
|
||||
{
|
||||
if (!$this->isBanned())
|
||||
return false;
|
||||
|
||||
if ($this->getRecord()->unblock_time > time() || $this->getRecord()->unblock_time == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
use Traits\TSubscribable;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ class Video extends Media
|
|||
protected $tableName = "videos";
|
||||
protected $fileExtension = "ogv";
|
||||
|
||||
protected $processingPlaceholder = "video/rendering";
|
||||
|
||||
protected function saveFile(string $filename, string $hash): bool
|
||||
{
|
||||
if(!Shell::commandAvailable("ffmpeg") || !Shell::commandAvailable("ffprobe"))
|
||||
|
@ -37,7 +39,7 @@ class Video extends Media
|
|||
throw new \DomainException("$filename does not contain any meaningful video streams");
|
||||
|
||||
try {
|
||||
if(!is_dir($dirId = $this->pathFromHash($hash)))
|
||||
if(!is_dir($dirId = dirname($this->pathFromHash($hash))))
|
||||
mkdir($dirId);
|
||||
|
||||
$dir = $this->getBaseDir();
|
||||
|
@ -54,6 +56,22 @@ class Video extends Media
|
|||
return true;
|
||||
}
|
||||
|
||||
protected function checkIfFileIsProcessed(): bool
|
||||
{
|
||||
if($this->getType() != Video::TYPE_DIRECT)
|
||||
return true;
|
||||
|
||||
if(!file_exists($this->getFileName())) {
|
||||
if((time() - $this->getRecord()->last_checked) > 3600) {
|
||||
# TODO notify that video processor is probably dead
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getName(): string
|
||||
{
|
||||
return $this->getRecord()->name;
|
||||
|
@ -83,6 +101,9 @@ class Video extends Media
|
|||
function getThumbnailURL(): string
|
||||
{
|
||||
if($this->getType() === Video::TYPE_DIRECT) {
|
||||
if(!$this->isProcessed())
|
||||
return "/assets/packages/static/openvk/video/rendering.apng";
|
||||
|
||||
return preg_replace("%\.[A-z]++$%", ".gif", $this->getURL());
|
||||
} else {
|
||||
return $this->getVideoDriver()->getThumbnailURL();
|
||||
|
|
69
Web/Models/Repositories/Applications.php
Normal file
69
Web/Models/Repositories/Applications.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use openvk\Web\Models\Entities\Application;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
|
||||
class Applications
|
||||
{
|
||||
private $context;
|
||||
private $apps;
|
||||
private $appRels;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DatabaseConnection::i()->getContext();
|
||||
$this->apps = $this->context->table("apps");
|
||||
$this->appRels = $this->context->table("app_users");
|
||||
}
|
||||
|
||||
private function toApp(?ActiveRow $ar): ?Application
|
||||
{
|
||||
return is_null($ar) ? NULL : new Application($ar);
|
||||
}
|
||||
|
||||
function get(int $id): ?Application
|
||||
{
|
||||
return $this->toApp($this->apps->get($id));
|
||||
}
|
||||
|
||||
function getList(int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
{
|
||||
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||
$apps = $this->apps->where("enabled", 1)->page($page, $perPage);
|
||||
foreach($apps as $app)
|
||||
yield new Application($app);
|
||||
}
|
||||
|
||||
function getListCount(): int
|
||||
{
|
||||
return sizeof($this->apps->where("enabled", 1));
|
||||
}
|
||||
|
||||
function getByOwner(User $owner, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
{
|
||||
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||
$apps = $this->apps->where("owner", $owner->getId())->page($page, $perPage);
|
||||
foreach($apps as $app)
|
||||
yield new Application($app);
|
||||
}
|
||||
|
||||
function getOwnCount(User $owner): int
|
||||
{
|
||||
return sizeof($this->apps->where("owner", $owner->getId()));
|
||||
}
|
||||
|
||||
function getInstalled(User $user, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
{
|
||||
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||
$apps = $this->appRels->where("user", $user->getId())->page($page, $perPage);
|
||||
foreach($apps as $appRel)
|
||||
yield $this->get($appRel->app);
|
||||
}
|
||||
|
||||
function getInstalledCount(User $user): int
|
||||
{
|
||||
return sizeof($this->appRels->where("user", $user->getId()));
|
||||
}
|
||||
}
|
73
Web/Models/Repositories/BannedLinks.php
Normal file
73
Web/Models/Repositories/BannedLinks.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use Chandler\Database\DatabaseConnection as DB;
|
||||
use Nette\Database\Table\{ActiveRow, Selection};
|
||||
use openvk\Web\Models\Entities\BannedLink;
|
||||
|
||||
class BannedLinks
|
||||
{
|
||||
private $context;
|
||||
private $bannedLinks;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DB::i()->getContext();
|
||||
$this->bannedLinks = $this->context->table("links_banned");
|
||||
}
|
||||
|
||||
function toBannedLink(?ActiveRow $ar): ?BannedLink
|
||||
{
|
||||
return is_null($ar) ? NULL : new BannedLink($ar);
|
||||
}
|
||||
|
||||
function get(int $id): ?BannedLink
|
||||
{
|
||||
return $this->toBannedLink($this->bannedLinks->get($id));
|
||||
}
|
||||
|
||||
function getList(?int $page = 1): \Traversable
|
||||
{
|
||||
foreach($this->bannedLinks->order("id DESC")->page($page, OPENVK_DEFAULT_PER_PAGE) as $link)
|
||||
yield new BannedLink($link);
|
||||
}
|
||||
|
||||
function getCount(int $page = 1): int
|
||||
{
|
||||
return sizeof($this->bannedLinks->fetch());
|
||||
}
|
||||
|
||||
function getByDomain(string $domain): ?Selection
|
||||
{
|
||||
return $this->bannedLinks->where("domain", $domain);
|
||||
}
|
||||
|
||||
function isDomainBanned(string $domain): bool
|
||||
{
|
||||
return sizeof($this->bannedLinks->where(["link" => $domain, "regexp_rule" => ""])) > 0;
|
||||
}
|
||||
|
||||
function genLinks($rules): \Traversable
|
||||
{
|
||||
foreach ($rules as $rule)
|
||||
yield $this->get($rule->id);
|
||||
}
|
||||
|
||||
function genEntries($links, $uri): \Traversable
|
||||
{
|
||||
foreach($links as $link)
|
||||
if (preg_match($link->getRegexpRule(), $uri))
|
||||
yield $link->getId();
|
||||
}
|
||||
|
||||
function check(string $url): ?array
|
||||
{
|
||||
$uri = strstr(str_replace(["https://", "http://"], "", $url), "/", true);
|
||||
$domain = str_replace("www.", "", $uri);
|
||||
$rules = $this->getByDomain($domain);
|
||||
|
||||
if (is_null($rules))
|
||||
return NULL;
|
||||
|
||||
return iterator_to_array($this->genEntries($this->genLinks($rules), $uri));
|
||||
}
|
||||
}
|
|
@ -45,7 +45,7 @@ class Clubs
|
|||
|
||||
function getPopularClubs(): \Traversable
|
||||
{
|
||||
$query = "SELECT ROW_NUMBER() OVER (ORDER BY `subscriptions` DESC) as `place`, `target` as `id`, COUNT(`follower`) as `subscriptions` FROM `subscriptions` WHERE `model` = \"openvk\\\Web\\\Models\\\Entities\\\Club\" GROUP BY `target` ORDER BY `subscriptions` DESC, `id` LIMIT 10;";
|
||||
$query = "SELECT ROW_NUMBER() OVER (ORDER BY `subscriptions` DESC) as `place`, `target` as `id`, COUNT(`follower`) as `subscriptions` FROM `subscriptions` WHERE `model` = \"openvk\\\Web\\\Models\\\Entities\\\Club\" GROUP BY `target` ORDER BY `subscriptions` DESC, `id` LIMIT 30;";
|
||||
$entries = DatabaseConnection::i()->getConnection()->query($query);
|
||||
|
||||
foreach($entries as $entry)
|
||||
|
|
|
@ -26,13 +26,13 @@ class Comments
|
|||
return $this->toComment($this->comments->get($id));
|
||||
}
|
||||
|
||||
function getCommentsByTarget(Postable $target, int $page, ?int $perPage = NULL): \Traversable
|
||||
function getCommentsByTarget(Postable $target, int $page, ?int $perPage = NULL, ?string $sort = "ASC"): \Traversable
|
||||
{
|
||||
$comments = $this->comments->where([
|
||||
"model" => get_class($target),
|
||||
"target" => $target->getId(),
|
||||
"deleted" => false,
|
||||
])->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
|
||||
])->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE)->order("created ".$sort);;
|
||||
|
||||
foreach($comments as $comment)
|
||||
yield $this->toComment($comment);
|
||||
|
|
33
Web/Models/Repositories/EmailChangeVerifications.php
Normal file
33
Web/Models/Repositories/EmailChangeVerifications.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Entities\EmailChangeVerification;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
|
||||
class EmailChangeVerifications
|
||||
{
|
||||
private $context;
|
||||
private $verifications;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DatabaseConnection::i()->getContext();
|
||||
$this->verifications = $this->context->table("email_change_verifications");
|
||||
}
|
||||
|
||||
function toEmailChangeVerification(?ActiveRow $ar): ?EmailChangeVerification
|
||||
{
|
||||
return is_null($ar) ? NULL : new EmailChangeVerification($ar);
|
||||
}
|
||||
|
||||
function getByToken(string $token): ?EmailChangeVerification
|
||||
{
|
||||
return $this->toEmailChangeVerification($this->verifications->where("key", $token)->fetch());
|
||||
}
|
||||
|
||||
function getLatestByUser(User $user): ?EmailChangeVerification
|
||||
{
|
||||
return $this->toEmailChangeVerification($this->verifications->where("profile", $user->getId())->order("timestamp DESC")->fetch());
|
||||
}
|
||||
}
|
|
@ -44,4 +44,15 @@ class Messages
|
|||
yield new Correspondence($correspondent, $anotherCorrespondent);
|
||||
}
|
||||
}
|
||||
|
||||
function getCorrespondenciesCount(RowModel $correspondent): ?int
|
||||
{
|
||||
$id = $correspondent->getId();
|
||||
$class = get_class($correspondent);
|
||||
$query = file_get_contents(__DIR__ . "/../sql/get-correspondencies-count.tsql");
|
||||
DatabaseConnection::i()->getConnection()->query(file_get_contents(__DIR__ . "/../sql/mysql-msg-fix.tsql"));
|
||||
$count = DatabaseConnection::i()->getConnection()->query($query, $id, $class, $id, $class)->fetch()->cnt;
|
||||
bdump($count);
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ class Notes
|
|||
if(!is_null($note))
|
||||
return new Note($note);
|
||||
else
|
||||
return null;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
function getUserNotesCount(User $user): int
|
||||
|
|
|
@ -71,7 +71,7 @@ class Posts
|
|||
{
|
||||
$hashtag = "#$hashtag";
|
||||
$sel = $this->posts
|
||||
->where("content LIKE ?", "%$hashtag%")
|
||||
->where("MATCH (content) AGAINST (? IN BOOLEAN MODE)", "+$hashtag")
|
||||
->where("deleted", 0)
|
||||
->order("created DESC")
|
||||
->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
|
||||
|
@ -96,7 +96,7 @@ class Posts
|
|||
if(!is_null($post))
|
||||
return new Post($post);
|
||||
else
|
||||
return null;
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
// use openvk\Web\Models\Entities\Ticket;
|
||||
// use openvk\Web\Models\Entities\User;
|
||||
// use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\Entities\TicketComment;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
|
@ -22,32 +19,6 @@ class TicketComments
|
|||
foreach($this->comments->where(['ticket_id' => $ticket_id, 'deleted' => 0]) as $comment) yield new TicketComment($comment);
|
||||
}
|
||||
|
||||
// private function toTicket(?ActiveRow $ar): ?Ticket
|
||||
// {
|
||||
// return is_null($ar) ? NULL : new Ticket($ar);
|
||||
// }
|
||||
|
||||
// function getTicketsByuId(int $user_id): \Traversable
|
||||
// {
|
||||
// foreach($this->tickets->where(['user_id' => $user_id, 'deleted' => 0]) as $ticket) yield new Ticket($ticket);
|
||||
// }
|
||||
|
||||
// function getRequestById(int $req_id): ?Ticket
|
||||
// {
|
||||
// $requests = $this->tickets->where(['id' => $req_id])->fetch();
|
||||
// if(!is_null($requests))
|
||||
|
||||
// return new Req($requests);
|
||||
// else
|
||||
// return null;
|
||||
|
||||
// }
|
||||
|
||||
// function get(int $id): ?Ticket
|
||||
// {
|
||||
// return $this->toTicket($this->tickets->get($id));
|
||||
// }
|
||||
|
||||
function get(int $id): ?TicketComment
|
||||
{
|
||||
$comment = $this->comments->get($id);;
|
||||
|
|
|
@ -50,7 +50,7 @@ class Tickets
|
|||
if(!is_null($requests))
|
||||
return new Req($requests);
|
||||
else
|
||||
return null;
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ class Topics
|
|||
{
|
||||
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||
|
||||
// Get pinned topics first
|
||||
# Get pinned topics first
|
||||
$query = "SELECT `id` FROM `topics` WHERE `pinned` = 1 AND `group` = ? AND `deleted` = 0 UNION SELECT `id` FROM `topics` WHERE `pinned` = 0 AND `group` = ? AND `deleted` = 0";
|
||||
$query .= " LIMIT " . $perPage . " OFFSET " . ($page - 1) * $perPage;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ Move-Item $file $temp
|
|||
|
||||
# video stub logic was implicitly deprecated, so we start processing at once
|
||||
ffmpeg -i $temp -ss 00:00:01.000 -vframes 1 "$dir$hashT/$hash.gif"
|
||||
ffmpeg -i $temp -c:v libtheora -q:v 7 -c:a libvorbis -q:a 4 -vf scale=640x360,setsar=1:1 -y $temp2
|
||||
ffmpeg -i $temp -c:v libtheora -q:v 7 -c:a libvorbis -q:a 4 -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2,setsar=1" -y $temp2
|
||||
|
||||
Move-Item $temp2 "$dir$hashT/$hash.ogv"
|
||||
Remove-Item $temp
|
||||
|
|
|
@ -5,7 +5,7 @@ cp ../files/video/rendering.apng $3${4:0:2}/$4.gif
|
|||
cp ../files/video/rendering.ogv $3/${4:0:2}/$4.ogv
|
||||
|
||||
nice ffmpeg -i "/tmp/vid_$tmpfile.bin" -ss 00:00:01.000 -vframes 1 $3${4:0:2}/$4.gif
|
||||
nice -n 20 ffmpeg -i "/tmp/vid_$tmpfile.bin" -c:v libtheora -q:v 7 -c:a libvorbis -q:a 4 -vf scale=640x360,setsar=1:1 -y "/tmp/ffmOi$tmpfile.ogv"
|
||||
nice -n 20 ffmpeg -i "/tmp/vid_$tmpfile.bin" -c:v libtheora -q:v 7 -c:a libvorbis -q:a 4 -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2,setsar=1" -y "/tmp/ffmOi$tmpfile.ogv"
|
||||
|
||||
rm -rf $3${4:0:2}/$4.ogv
|
||||
mv "/tmp/ffmOi$tmpfile.ogv" $3${4:0:2}/$4.ogv
|
||||
|
|
20
Web/Models/sql/get-correspondencies-count.tsql
Normal file
20
Web/Models/sql/get-correspondencies-count.tsql
Normal file
|
@ -0,0 +1,20 @@
|
|||
SELECT COUNT(id) AS cnt FROM
|
||||
(
|
||||
(
|
||||
SELECT
|
||||
recipient_id AS id
|
||||
FROM messages
|
||||
WHERE
|
||||
sender_id = ?
|
||||
AND
|
||||
sender_type = ?
|
||||
) UNION (
|
||||
SELECT
|
||||
sender_id AS id
|
||||
FROM messages
|
||||
WHERE
|
||||
recipient_id = ?
|
||||
AND
|
||||
recipient_type = ?
|
||||
)
|
||||
) dt
|
6
Web/Models/sql/get-online-friends.tsql
Executable file
6
Web/Models/sql/get-online-friends.tsql
Executable file
|
@ -0,0 +1,6 @@
|
|||
(SELECT follower AS __id FROM
|
||||
(SELECT follower FROM subscriptions WHERE target=? AND model="openvk\\Web\\Models\\Entities\\User") u0
|
||||
INNER JOIN
|
||||
(SELECT target FROM subscriptions WHERE follower=? AND model="openvk\\Web\\Models\\Entities\\User") u1
|
||||
ON u0.follower = u1.target) u2
|
||||
INNER JOIN profiles ON profiles.id = u2.__id WHERE online > (UNIX_TIMESTAMP() - 300)
|
|
@ -9,19 +9,19 @@ final class AboutPresenter extends OpenVKPresenter
|
|||
{
|
||||
protected $banTolerant = true;
|
||||
protected $activationTolerant = true;
|
||||
protected $deactivationTolerant = true;
|
||||
|
||||
function renderIndex(): void
|
||||
{
|
||||
if(!is_null($this->user)) {
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: /id" . $this->user->id);
|
||||
exit;
|
||||
if($this->user->identity->getMainPage())
|
||||
$this->redirect("/feed");
|
||||
else
|
||||
$this->redirect($this->user->identity->getURL());
|
||||
}
|
||||
|
||||
if($_SERVER['REQUEST_URI'] == "/id0") {
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: /");
|
||||
exit;
|
||||
$this->redirect("/");
|
||||
}
|
||||
|
||||
$this->template->stats = (new Users)->getStatistics();
|
||||
|
@ -85,7 +85,7 @@ final class AboutPresenter extends OpenVKPresenter
|
|||
if(is_null($lg))
|
||||
$this->throwError(404, "Not found", "Language is not found");
|
||||
header("Content-Type: application/javascript");
|
||||
echo "window.lang = " . json_encode($localizer->export($lang)) . ";"; // привет хардкод :DDD
|
||||
echo "window.lang = " . json_encode($localizer->export($lang)) . ";"; # привет хардкод :DDD
|
||||
exit;
|
||||
}
|
||||
|
||||
|
@ -102,6 +102,15 @@ final class AboutPresenter extends OpenVKPresenter
|
|||
. "# covered from unauthorized persons (for example, due to\n"
|
||||
. "# lack of rights to access the admin panel)\n\n"
|
||||
. "User-Agent: *\n"
|
||||
. "Disallow: /albums/create\n"
|
||||
. "Disallow: /videos/upload\n"
|
||||
. "Disallow: /invite\n"
|
||||
. "Disallow: /groups_create\n"
|
||||
. "Disallow: /notifications\n"
|
||||
. "Disallow: /settings\n"
|
||||
. "Disallow: /edit\n"
|
||||
. "Disallow: /gifts\n"
|
||||
. "Disallow: /support\n"
|
||||
. "Disallow: /rpc\n"
|
||||
. "Disallow: /language\n"
|
||||
. "Disallow: /badbrowser.php\n"
|
||||
|
@ -120,10 +129,12 @@ final class AboutPresenter extends OpenVKPresenter
|
|||
|
||||
function renderHumansTxt(): void
|
||||
{
|
||||
// :D
|
||||
# :D
|
||||
$this->redirect("https://github.com/openvk/openvk#readme");
|
||||
}
|
||||
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: https://github.com/openvk/openvk#readme");
|
||||
exit;
|
||||
function renderDev(): void
|
||||
{
|
||||
$this->redirect("https://docs.openvk.su/");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{Voucher, Gift, GiftCategory, User};
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs, Vouchers, Gifts};
|
||||
use openvk\Web\Models\Entities\{Voucher, Gift, GiftCategory, User, BannedLink};
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs, Vouchers, Gifts, BannedLinks};
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
final class AdminPresenter extends OpenVKPresenter
|
||||
{
|
||||
|
@ -9,13 +10,15 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
private $clubs;
|
||||
private $vouchers;
|
||||
private $gifts;
|
||||
private $bannedLinks;
|
||||
|
||||
function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts)
|
||||
function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts, BannedLinks $bannedLinks)
|
||||
{
|
||||
$this->users = $users;
|
||||
$this->clubs = $clubs;
|
||||
$this->vouchers = $vouchers;
|
||||
$this->gifts = $gifts;
|
||||
$this->bannedLinks = $bannedLinks;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
@ -23,7 +26,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
private function warnIfNoCommerce(): void
|
||||
{
|
||||
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
|
||||
$this->flash("warn", "Коммерция отключена системным администратором", "Настройки ваучеров и подарков будут сохранены, но не будут оказывать никакого влияния.");
|
||||
$this->flash("warn", tr("admin_commerce_disabled"), tr("admin_commerce_disabled_desc"));
|
||||
}
|
||||
|
||||
private function searchResults(object $repo, &$count)
|
||||
|
@ -70,14 +73,14 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$user->setLast_Name($this->postParam("last_name"));
|
||||
$user->setPseudo($this->postParam("nickname"));
|
||||
$user->setStatus($this->postParam("status"));
|
||||
$user->setVerified(empty($this->postParam("verify") ? 0 : 1));
|
||||
if($user->onlineStatus() != $this->postParam("online")) $user->setOnline(intval($this->postParam("online")));
|
||||
if(!$user->setShortCode(empty($this->postParam("shortcode")) ? NULL : $this->postParam("shortcode")))
|
||||
$this->flash("err", tr("error"), tr("error_shorturl_incorrect"));
|
||||
$user->changeEmail($this->postParam("email"));
|
||||
if($user->onlineStatus() != $this->postParam("online")) $user->setOnline(intval($this->postParam("online")));
|
||||
$user->setVerified(empty($this->postParam("verify") ? 0 : 1));
|
||||
|
||||
$user->save();
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,8 +173,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
|
||||
$voucher->save();
|
||||
|
||||
$this->redirect("/admin/vouchers/id" . $voucher->getId(), static::REDIRECT_TEMPORARY);
|
||||
exit;
|
||||
$this->redirect("/admin/vouchers/id" . $voucher->getId());
|
||||
}
|
||||
|
||||
function renderGiftCategories(): void
|
||||
|
@ -193,7 +195,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
if(!$cat)
|
||||
$this->notFound();
|
||||
else if($cat->getSlug() !== $slug)
|
||||
$this->redirect("/admin/gifts/" . $cat->getSlug() . "." . $id . ".meta", static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/admin/gifts/" . $cat->getSlug() . "." . $id . ".meta");
|
||||
} else {
|
||||
$gen = true;
|
||||
$cat = new GiftCategory;
|
||||
|
@ -234,7 +236,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$cat->setDescription($code, $this->postParam("description_$code"));
|
||||
}
|
||||
|
||||
$this->redirect("/admin/gifts/" . $cat->getSlug() . "." . $cat->getId() . ".meta", static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/admin/gifts/" . $cat->getSlug() . "." . $cat->getId() . ".meta");
|
||||
}
|
||||
|
||||
function renderGifts(string $catSlug, int $catId): void
|
||||
|
@ -245,7 +247,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
if(!$cat)
|
||||
$this->notFound();
|
||||
else if($cat->getSlug() !== $catSlug)
|
||||
$this->redirect("/admin/gifts/" . $cat->getSlug() . "." . $catId . "/", static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/admin/gifts/" . $cat->getSlug() . "." . $catId . "/");
|
||||
|
||||
$this->template->cat = $cat;
|
||||
$this->template->gifts = iterator_to_array($cat->getGifts((int) ($this->queryParam("p") ?? 1), NULL, $this->template->count));
|
||||
|
@ -284,7 +286,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
|
||||
$name = $catTo->getName();
|
||||
$this->flash("succ", "Gift moved successfully", "This gift will now be in <b>$name</b>.");
|
||||
$this->redirect("/admin/gifts/" . $catTo->getSlug() . "." . $catTo->getId() . "/", static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/admin/gifts/" . $catTo->getSlug() . "." . $catTo->getId() . "/");
|
||||
break;
|
||||
default:
|
||||
case "edit":
|
||||
|
@ -328,7 +330,7 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$cat->addGift($gift);
|
||||
}
|
||||
|
||||
$this->redirect("/admin/gifts/id" . $gift->getId(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/admin/gifts/id" . $gift->getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,11 +343,13 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
{
|
||||
$this->assertNoCSRF();
|
||||
|
||||
$unban_time = strtotime($this->queryParam("date")) ?: NULL;
|
||||
|
||||
$user = $this->users->get($id);
|
||||
if(!$user)
|
||||
exit(json_encode([ "error" => "User does not exist" ]));
|
||||
|
||||
$user->ban($this->queryParam("reason"));
|
||||
$user->ban($this->queryParam("reason"), true, $unban_time);
|
||||
exit(json_encode([ "success" => true, "reason" => $this->queryParam("reason") ]));
|
||||
}
|
||||
|
||||
|
@ -357,7 +361,8 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
if(!$user)
|
||||
exit(json_encode([ "error" => "User does not exist" ]));
|
||||
|
||||
$user->setBlock_Reason(null);
|
||||
$user->setBlock_Reason(NULL);
|
||||
$user->setUnblock_time(NULL);
|
||||
$user->save();
|
||||
exit(json_encode([ "success" => true ]));
|
||||
}
|
||||
|
@ -373,4 +378,73 @@ final class AdminPresenter extends OpenVKPresenter
|
|||
$user->adminNotify("⚠️ " . $this->queryParam("message"));
|
||||
exit(json_encode([ "message" => $this->queryParam("message") ]));
|
||||
}
|
||||
|
||||
function renderBannedLinks(): void
|
||||
{
|
||||
$this->template->links = $this->bannedLinks->getList((int) $this->queryParam("p") ?: 1);
|
||||
$this->template->users = new Users;
|
||||
}
|
||||
|
||||
function renderBannedLink(int $id): void
|
||||
{
|
||||
$this->template->form = (object) [];
|
||||
|
||||
if($id === 0) {
|
||||
$this->template->form->id = 0;
|
||||
$this->template->form->link = NULL;
|
||||
$this->template->form->reason = NULL;
|
||||
} else {
|
||||
$link = (new BannedLinks)->get($id);
|
||||
if(!$link)
|
||||
$this->notFound();
|
||||
|
||||
$this->template->form->id = $link->getId();
|
||||
$this->template->form->link = $link->getDomain();
|
||||
$this->template->form->reason = $link->getReason();
|
||||
$this->template->form->regexp = $link->getRawRegexp();
|
||||
}
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||
return;
|
||||
|
||||
$link = (new BannedLinks)->get($id);
|
||||
|
||||
$new_domain = parse_url($this->postParam("link"))["host"];
|
||||
$new_reason = $this->postParam("reason") ?: NULL;
|
||||
|
||||
$lid = $id;
|
||||
|
||||
if ($link) {
|
||||
$link->setDomain($new_domain ?? $this->postParam("link"));
|
||||
$link->setReason($new_reason);
|
||||
$link->setRegexp_rule($this->postParam("regexp"));
|
||||
$link->save();
|
||||
} else {
|
||||
if (!$new_domain)
|
||||
$this->flashFail("err", tr("error"), tr("admin_banned_link_not_specified"));
|
||||
|
||||
$link = new BannedLink;
|
||||
$link->setDomain($new_domain);
|
||||
$link->setReason($new_reason);
|
||||
$link->setRegexp_rule($this->postParam("regexp"));
|
||||
$link->setInitiator($this->user->identity->getId());
|
||||
$link->save();
|
||||
|
||||
$lid = $link->getId();
|
||||
}
|
||||
|
||||
$this->redirect("/admin/bannedLink/id" . $lid);
|
||||
}
|
||||
|
||||
function renderUnbanLink(int $id): void
|
||||
{
|
||||
$link = (new BannedLinks)->get($id);
|
||||
|
||||
if (!$link)
|
||||
$this->flashFail("err", tr("error"), tr("admin_banned_link_not_found"));
|
||||
|
||||
$link->delete(false);
|
||||
|
||||
$this->redirect("/admin/bannedLinks");
|
||||
}
|
||||
}
|
||||
|
|
138
Web/Presenters/AppsPresenter.php
Normal file
138
Web/Presenters/AppsPresenter.php
Normal file
|
@ -0,0 +1,138 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\Application;
|
||||
use openvk\Web\Models\Repositories\Applications;
|
||||
|
||||
final class AppsPresenter extends OpenVKPresenter
|
||||
{
|
||||
private $apps;
|
||||
|
||||
function __construct(Applications $apps)
|
||||
{
|
||||
$this->apps = $apps;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
function renderPlay(int $app): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$app = $this->apps->get($app);
|
||||
if(!$app || !$app->isEnabled())
|
||||
$this->notFound();
|
||||
|
||||
$this->template->id = $app->getId();
|
||||
$this->template->name = $app->getName();
|
||||
$this->template->desc = $app->getDescription();
|
||||
$this->template->origin = $app->getOrigin();
|
||||
$this->template->url = $app->getURL();
|
||||
$this->template->owner = $app->getOwner();
|
||||
$this->template->news = $app->getNote();
|
||||
$this->template->perms = $app->getPermissions($this->user->identity);
|
||||
}
|
||||
|
||||
function renderUnInstall(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertNoCSRF();
|
||||
|
||||
$app = $this->apps->get((int) $this->queryParam("app"));
|
||||
if(!$app)
|
||||
$this->flashFail("err", tr("app_err_not_found"), tr("app_err_not_found_desc"));
|
||||
|
||||
$app->uninstall($this->user->identity);
|
||||
$this->flashFail("succ", tr("app_uninstalled"), tr("app_uninstalled_desc"));
|
||||
}
|
||||
|
||||
function renderEdit(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$app = NULL;
|
||||
if($this->queryParam("act") !== "create") {
|
||||
if(empty($this->queryParam("app")))
|
||||
$this->flashFail("err", tr("app_err_not_found"), tr("app_err_not_found_desc"));
|
||||
|
||||
$app = $this->apps->get((int) $this->queryParam("app"));
|
||||
if(!$app)
|
||||
$this->flashFail("err", tr("app_err_not_found"), tr("app_err_not_found_desc"));
|
||||
|
||||
if($app->getOwner()->getId() != $this->user->identity->getId())
|
||||
$this->flashFail("err", tr("forbidden"), tr("app_err_forbidden_desc"));
|
||||
}
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!$app) {
|
||||
$app = new Application;
|
||||
$app->setOwner($this->user->id);
|
||||
}
|
||||
|
||||
if(!filter_var($this->postParam("url"), FILTER_VALIDATE_URL))
|
||||
$this->flashFail("err", tr("app_err_url"), tr("app_err_url_desc"));
|
||||
|
||||
if(isset($_FILES["ava"]) && $_FILES["ava"]["size"] > 0) {
|
||||
if(($res = $app->setAvatar($_FILES["ava"])) !== 0)
|
||||
$this->flashFail("err", tr("app_err_ava"), tr("app_err_ava_desc", $res));
|
||||
}
|
||||
|
||||
if(empty($this->postParam("note"))) {
|
||||
$app->setNoteLink(NULL);
|
||||
} else {
|
||||
if(!$app->setNoteLink($this->postParam("note")))
|
||||
$this->flashFail("err", tr("app_err_note"), tr("app_err_note_desc"));
|
||||
}
|
||||
|
||||
$app->setName($this->postParam("name"));
|
||||
$app->setDescription($this->postParam("desc"));
|
||||
$app->setAddress($this->postParam("url"));
|
||||
if($this->postParam("enable") === "on")
|
||||
$app->enable();
|
||||
else
|
||||
$app->disable(); # no need to save since enable/disable will call save() internally
|
||||
|
||||
$this->redirect("/editapp?act=edit&app=" . $app->getId()); # will exit here
|
||||
}
|
||||
|
||||
if(!is_null($app)) {
|
||||
$this->template->create = false;
|
||||
$this->template->id = $app->getId();
|
||||
$this->template->name = $app->getName();
|
||||
$this->template->desc = $app->getDescription();
|
||||
$this->template->coins = $app->getBalance();
|
||||
$this->template->origin = $app->getOrigin();
|
||||
$this->template->url = $app->getURL();
|
||||
$this->template->note = $app->getNoteLink();
|
||||
$this->template->users = $app->getUsersCount();
|
||||
$this->template->on = $app->isEnabled();
|
||||
} else {
|
||||
$this->template->create = true;
|
||||
}
|
||||
}
|
||||
|
||||
function renderList(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$act = $this->queryParam("act");
|
||||
if(!in_array($act, ["list", "installed", "dev"]))
|
||||
$act = "installed";
|
||||
|
||||
$page = (int) ($this->queryParam("p") ?? 1);
|
||||
if($act == "list") {
|
||||
$apps = $this->apps->getList($page);
|
||||
$count = $this->apps->getListCount();
|
||||
} else if($act == "installed") {
|
||||
$apps = $this->apps->getInstalled($this->user->identity, $page);
|
||||
$count = $this->apps->getInstalledCount($this->user->identity);
|
||||
} else if($act == "dev") {
|
||||
$apps = $this->apps->getByOwner($this->user->identity, $page);
|
||||
$count = $this->apps->getOwnCount($this->user->identity);
|
||||
}
|
||||
|
||||
$this->template->act = $act;
|
||||
$this->template->iterator = $apps;
|
||||
$this->template->count = $count;
|
||||
$this->template->page = $page;
|
||||
}
|
||||
}
|
|
@ -1,14 +1,8 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\IP;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Entities\PasswordReset;
|
||||
use openvk\Web\Models\Entities\EmailVerification;
|
||||
use openvk\Web\Models\Entities\{IP, User, PasswordReset, EmailVerification};
|
||||
use openvk\Web\Models\Repositories\{IPs, Users, Restores, Verifications};
|
||||
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||
use openvk\Web\Models\Repositories\IPs;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\Repositories\Restores;
|
||||
use openvk\Web\Models\Repositories\Verifications;
|
||||
use openvk\Web\Util\Validator;
|
||||
use Chandler\Session\Session;
|
||||
use Chandler\Security\User as ChandlerUser;
|
||||
|
@ -20,6 +14,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
{
|
||||
protected $banTolerant = true;
|
||||
protected $activationTolerant = true;
|
||||
protected $deactivationTolerant = true;
|
||||
|
||||
private $authenticator;
|
||||
private $db;
|
||||
|
@ -50,7 +45,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
function renderRegister(): void
|
||||
{
|
||||
if(!is_null($this->user))
|
||||
$this->redirect("/id" . $this->user->id, static::REDIRECT_TEMPORARY);
|
||||
$this->redirect($this->user->identity->getURL());
|
||||
|
||||
if(!$this->hasPermission("user", "register", -1)) exit("Вас забанили");
|
||||
|
||||
|
@ -89,6 +84,9 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
if (strtotime($this->postParam("birthday")) > time())
|
||||
$this->flashFail("err", tr("invalid_birth_date"), tr("invalid_birth_date_comment"));
|
||||
|
||||
if (!$this->postParam("confirmation"))
|
||||
$this->flashFail("err", tr("error"), tr("checkbox_in_registration_unchecked"));
|
||||
|
||||
try {
|
||||
$user = new User;
|
||||
$user->setFirst_Name($this->postParam("first_name"));
|
||||
|
@ -97,7 +95,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$user->setEmail($this->postParam("email"));
|
||||
$user->setSince(date("Y-m-d H:i:s"));
|
||||
$user->setRegistering_Ip(CONNECTING_IP);
|
||||
$user->setBirthday(strtotime($this->postParam("birthday")));
|
||||
$user->setBirthday(empty($this->postParam("birthday")) ? NULL : strtotime($this->postParam("birthday")));
|
||||
$user->setActivated((int)!OPENVK_ROOT_CONF['openvk']['preferences']['security']['requireEmail']);
|
||||
} catch(InvalidUserNameException $ex) {
|
||||
$this->flashFail("err", tr("error"), tr("invalid_real_name"));
|
||||
|
@ -128,7 +126,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
$this->authenticator->authenticate($chUser->getId());
|
||||
$this->redirect("/id" . $user->getId(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/id" . $user->getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,12 +135,11 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$redirUrl = $this->requestParam("jReturnTo");
|
||||
|
||||
if(!is_null($this->user))
|
||||
$this->redirect($redirUrl ?? "/id" . $this->user->id, static::REDIRECT_TEMPORARY);
|
||||
$this->redirect($redirUrl ?? $this->user->identity->getURL());
|
||||
|
||||
if(!$this->hasPermission("user", "login", -1)) exit("Вас забанили");
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
|
||||
$user = $this->db->table("ChandlerUsers")->where("login", $this->postParam("login"))->fetch();
|
||||
if(!$user)
|
||||
$this->flashFail("err", tr("login_failed"), tr("invalid_username_or_password"));
|
||||
|
@ -151,7 +148,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", tr("login_failed"), tr("invalid_username_or_password"));
|
||||
|
||||
$ovkUser = new User($user->related("profiles.user")->fetch());
|
||||
if($ovkUser->isDeleted())
|
||||
if($ovkUser->isDeleted() && !$ovkUser->isDeactivated())
|
||||
$this->flashFail("err", tr("login_failed"), tr("invalid_username_or_password"));
|
||||
|
||||
$secret = $user->related("profiles.user")->fetch()["2fa_secret"];
|
||||
|
@ -171,8 +168,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
$this->authenticator->authenticate($user->id);
|
||||
$this->redirect($redirUrl ?? "/id" . $user->related("profiles.user")->fetch()->id, static::REDIRECT_TEMPORARY);
|
||||
exit;
|
||||
$this->redirect($redirUrl ?? $ovkUser->getURL());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,7 +179,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
|
||||
if($uuid === "unset") {
|
||||
Session::i()->set("_su", NULL);
|
||||
$this->redirect("/", static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/");
|
||||
}
|
||||
|
||||
if(!$this->db->table("ChandlerUsers")->where("id", $uuid))
|
||||
|
@ -192,8 +188,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$this->assertPermission('openvk\Web\Models\Entities\User', 'substitute', 0);
|
||||
Session::i()->set("_su", $uuid);
|
||||
$this->flash("succ", tr("profile_changed"), tr("profile_changed_comment"));
|
||||
$this->redirect("/", static::REDIRECT_TEMPORARY);
|
||||
exit;
|
||||
$this->redirect("/");
|
||||
}
|
||||
|
||||
function renderLogout(): void
|
||||
|
@ -203,7 +198,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$this->authenticator->logout();
|
||||
Session::i()->set("_su", NULL);
|
||||
|
||||
$this->redirect("/", static::REDIRECT_TEMPORARY_PRESISTENT);
|
||||
$this->redirect("/");
|
||||
}
|
||||
|
||||
function renderFinishRestoringPassword(): void
|
||||
|
@ -243,7 +238,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
function renderRestore(): void
|
||||
{
|
||||
if(!is_null($this->user))
|
||||
$this->redirect("/id" . $this->user->id, static::REDIRECT_TEMPORARY);
|
||||
$this->redirect($this->user->identity->getURL());
|
||||
|
||||
if(($this->queryParam("act") ?? "default") === "finish")
|
||||
$this->pass("openvk!Auth->finishRestoringPassword");
|
||||
|
@ -273,7 +268,6 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
];
|
||||
$this->sendmail($uRow->login, "password-reset", $params); #Vulnerability possible
|
||||
|
||||
|
||||
$this->flashFail("succ", tr("information_-1"), tr("password_reset_email_sent"));
|
||||
}
|
||||
}
|
||||
|
@ -281,7 +275,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
function renderResendEmail(): void
|
||||
{
|
||||
if(!is_null($this->user) && $this->user->identity->isActivated())
|
||||
$this->redirect("/id" . $this->user->id, static::REDIRECT_TEMPORARY);
|
||||
$this->redirect($this->user->identity->getURL());
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$user = $this->user->identity;
|
||||
|
@ -321,4 +315,49 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$this->redirect("/");
|
||||
}
|
||||
}
|
||||
|
||||
function renderReactivatePage(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$this->user->identity->reactivate();
|
||||
|
||||
$this->redirect("/");
|
||||
}
|
||||
|
||||
function renderUnbanThemself(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(!$this->user->identity->canUnbanThemself())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$user = $this->users->get($this->user->id);
|
||||
|
||||
$user->setBlock_Reason(NULL);
|
||||
$user->setUnblock_Time(NULL);
|
||||
$user->save();
|
||||
|
||||
$this->flashFail("succ", tr("banned_unban_title"), tr("banned_unban_description"));
|
||||
}
|
||||
|
||||
/*
|
||||
* This function will revoke all tokens, including API and Web tokens and except active one
|
||||
*
|
||||
* OF COURSE it requires CSRF
|
||||
*/
|
||||
function renderRevokeAllTokens(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
$this->assertNoCSRF();
|
||||
|
||||
// API tokens
|
||||
$this->db->table("api_tokens")->where("user", $this->user->identity->getId())->delete();
|
||||
// Web tokens
|
||||
$this->db->table("ChandlerTokens")->where("user", $this->user->identity->getChandlerGUID())->where("token != ?", Session::i()->get("tok"))->delete();
|
||||
$this->flashFail("succ", tr("information_-1"), tr("end_all_sessions_done"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,29 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Repositories\BannedLinks;
|
||||
use openvk\Web\Models\Entities\BannedLink;
|
||||
|
||||
final class AwayPresenter extends OpenVKPresenter
|
||||
{
|
||||
function renderAway(): void
|
||||
{
|
||||
$checkBanEntries = (new BannedLinks)->check($this->queryParam("to") . "/");
|
||||
if (OPENVK_ROOT_CONF["openvk"]["preferences"]["susLinks"]["warnings"])
|
||||
if (sizeof($checkBanEntries) > 0)
|
||||
$this->pass("openvk!Away->view", $checkBanEntries[0]);
|
||||
|
||||
header("HTTP/1.0 302 Found");
|
||||
header("X-Robots-Tag: noindex, nofollow, noarchive");
|
||||
header("Location: " . $this->queryParam("to"));
|
||||
exit;
|
||||
}
|
||||
|
||||
function renderView(int $lid) {
|
||||
$this->template->link = (new BannedLinks)->get($lid);
|
||||
|
||||
if (!$this->template->link)
|
||||
$this->notFound();
|
||||
|
||||
$this->template->to = $this->queryParam("to");
|
||||
}
|
||||
}
|
||||
|
|
13
Web/Presenters/BannedLinkPresenter.php
Normal file
13
Web/Presenters/BannedLinkPresenter.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
|
||||
use openvk\Web\Models\Entities\BannedLink;
|
||||
use openvk\Web\Models\Repositories\BannedLinks;
|
||||
|
||||
final class BannedLinkPresenter extends OpenVKPresenter
|
||||
{
|
||||
function renderView(int $lid) {
|
||||
$this->template->link = (new BannedLinks)->get($lid);
|
||||
$this->template->to = $this->queryParam("to");
|
||||
}
|
||||
}
|
|
@ -19,20 +19,23 @@ final class BlobPresenter extends OpenVKPresenter
|
|||
header("Access-Control-Allow-Origin: *");
|
||||
|
||||
$dir = $this->getDirName($dir);
|
||||
$name = preg_replace("%[^a-zA-Z0-9_\-]++%", "", $name);
|
||||
$path = OPENVK_ROOT . "/storage/$dir/$name.$format";
|
||||
if(!file_exists($path)) {
|
||||
$base = realpath(OPENVK_ROOT . "/storage/$dir");
|
||||
$path = realpath(OPENVK_ROOT . "/storage/$dir/$name.$format");
|
||||
if(!$path) # Will also check if file exists since realpath fails on ENOENT
|
||||
$this->notFound();
|
||||
} else {
|
||||
else if(strpos($path, $path) !== 0) # Prevent directory traversal and storage container escape
|
||||
$this->notFound();
|
||||
|
||||
if(isset($_SERVER["HTTP_IF_NONE_MATCH"]))
|
||||
exit(header("HTTP/1.1 304 Not Modified"));
|
||||
|
||||
header("Content-Type: " . mime_content_type($path));
|
||||
header("Content-Size: " . filesize($path));
|
||||
header("Cache-Control: public, max-age=1210000");
|
||||
header("X-Accel-Expires: 1210000");
|
||||
header("ETag: W/\"" . hash_file("snefru", $path) . "\"");
|
||||
|
||||
readfile($path);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
|
||||
if(!is_null($this->user)) $comment->toggleLike($this->user->identity);
|
||||
|
||||
$this->redirect($_SERVER["HTTP_REFERER"], static::REDIRECT_TEMPORARY);
|
||||
$this->redirect($_SERVER["HTTP_REFERER"]);
|
||||
}
|
||||
|
||||
function renderMakeComment(string $repo, int $eId): void
|
||||
|
@ -60,7 +60,7 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
}
|
||||
}
|
||||
|
||||
// TODO move to trait
|
||||
# TODO move to trait
|
||||
try {
|
||||
$photo = NULL;
|
||||
$video = NULL;
|
||||
|
|
|
@ -91,7 +91,7 @@ final class GiftsPresenter extends OpenVKPresenter
|
|||
$gift->used();
|
||||
|
||||
$this->flash("succ", "Подарок отправлен", "Вы отправили подарок <b>" . $user->getFirstName() . "</b> за " . $gift->getPrice() . " голосов.");
|
||||
$this->redirect($user->getURL(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect($user->getURL());
|
||||
}
|
||||
|
||||
function renderStub(): void
|
||||
|
|
|
@ -22,15 +22,12 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
if(!$club) {
|
||||
$this->notFound();
|
||||
} else {
|
||||
if($club->getShortCode())
|
||||
if(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH) !== "/" . $club->getShortCode())
|
||||
$this->redirect("/" . $club->getShortCode(), static::REDIRECT_TEMPORARY_PRESISTENT);
|
||||
|
||||
$this->template->club = $club;
|
||||
$this->template->albums = (new Albums)->getClubAlbums($club, 1, 3);
|
||||
$this->template->albumsCount = (new Albums)->getClubAlbumsCount($club);
|
||||
$this->template->topics = (new Topics)->getLastTopics($club, 3);
|
||||
$this->template->topicsCount = (new Topics)->getClubTopicsCount($club);
|
||||
|
||||
$this->template->club = $club;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,8 +54,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
$club->toggleSubscription($this->user->identity);
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: /club" . $club->getId());
|
||||
$this->redirect("/club" . $club->getId());
|
||||
}else{
|
||||
$this->flashFail("err", "Ошибка", "Вы не ввели название группы.");
|
||||
}
|
||||
|
@ -77,9 +73,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
|
||||
$club->toggleSubscription($this->user->identity);
|
||||
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: /club" . $club->getId());
|
||||
exit;
|
||||
$this->redirect($club->getURL());
|
||||
}
|
||||
|
||||
function renderFollowers(int $id): void
|
||||
|
@ -89,7 +83,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->template->club = $this->clubs->get($id);
|
||||
$this->template->onlyShowManagers = $this->queryParam("onlyAdmins") == "1";
|
||||
if($this->template->onlyShowManagers) {
|
||||
$this->template->followers = null;
|
||||
$this->template->followers = NULL;
|
||||
|
||||
$this->template->managers = $this->template->club->getManagers((int) ($this->queryParam("p") ?? 1), !$this->template->club->canBeModifiedBy($this->user->identity));
|
||||
if($this->template->club->canBeModifiedBy($this->user->identity) || !$this->template->club->isOwnerHidden()) {
|
||||
|
@ -99,7 +93,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->template->count = $this->template->club->getManagersCount();
|
||||
} else {
|
||||
$this->template->followers = $this->template->club->getFollowers((int) ($this->queryParam("p") ?? 1));
|
||||
$this->template->managers = null;
|
||||
$this->template->managers = NULL;
|
||||
$this->template->count = $this->template->club->getFollowersCount();
|
||||
}
|
||||
|
||||
|
@ -116,7 +110,7 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$user = is_null($this->queryParam("user")) ? $this->postParam("user") : $this->queryParam("user");
|
||||
$comment = $this->postParam("comment");
|
||||
$removeComment = $this->postParam("removeComment") === "1";
|
||||
$hidden = ["0" => false, "1" => true][$this->queryParam("hidden")] ?? null;
|
||||
$hidden = ["0" => false, "1" => true][$this->queryParam("hidden")] ?? NULL;
|
||||
//$index = $this->queryParam("index");
|
||||
if(!$user)
|
||||
$this->badRequest();
|
||||
|
@ -202,9 +196,11 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->template->club = $club;
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!$club->setShortcode( empty($this->postParam("shortcode")) ? NULL : $this->postParam("shortcode") ))
|
||||
$this->flashFail("err", tr("error"), tr("error_shorturl_incorrect"));
|
||||
|
||||
$club->setName(empty($this->postParam("name")) ? $club->getName() : $this->postParam("name"));
|
||||
$club->setAbout(empty($this->postParam("about")) ? NULL : $this->postParam("about"));
|
||||
$club->setShortcode(empty($this->postParam("shortcode")) ? NULL : $this->postParam("shortcode"));
|
||||
$club->setWall(empty($this->postParam("wall")) ? 0 : 1);
|
||||
$club->setAdministrators_List_Display(empty($this->postParam("administrators_list_display")) ? 0 : $this->postParam("administrators_list_display"));
|
||||
$club->setEveryone_Can_Create_Topics(empty($this->postParam("everyone_can_create_topics")) ? 0 : 1);
|
||||
|
|
|
@ -29,9 +29,10 @@ final class InternalAPIPresenter extends OpenVKPresenter
|
|||
|
||||
function renderRoute(): void
|
||||
{
|
||||
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||
header("HTTP/1.1 405 Method Not Allowed");
|
||||
exit("ты дебил это точка апи");
|
||||
|
||||
}
|
||||
try {
|
||||
$input = (object) MessagePack::unpack(file_get_contents("php://input"));
|
||||
} catch (\Exception $ex) {
|
||||
|
@ -71,20 +72,21 @@ final class InternalAPIPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
function renderTimezone() {
|
||||
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||
header("HTTP/1.1 405 Method Not Allowed");
|
||||
exit("ты дебил это метод апи");
|
||||
|
||||
}
|
||||
$sessionOffset = Session::i()->get("_timezoneOffset");
|
||||
if(is_numeric($this->postParam("timezone", false))) {
|
||||
$postTZ = intval($this->postParam("timezone", false));
|
||||
if ($postTZ != $sessionOffset || $sessionOffset == null) {
|
||||
Session::i()->set("_timezoneOffset", $postTZ ? $postTZ : 3 * MINUTE );
|
||||
$this->returnJson([
|
||||
"success" => 1 // If it's new value
|
||||
"success" => 1 # If it's new value
|
||||
]);
|
||||
} else {
|
||||
$this->returnJson([
|
||||
"success" => 2 // If it's the same value (if for some reason server will call this func)
|
||||
"success" => 2 # If it's the same value (if for some reason server will call this func)
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -34,10 +34,18 @@ final class MessengerPresenter extends OpenVKPresenter
|
|||
if(isset($_GET["sel"]))
|
||||
$this->pass("openvk!Messenger->app", $_GET["sel"]);
|
||||
|
||||
$page = $_GET["p"] ?? 1;
|
||||
$page = (int) ($_GET["p"] ?? 1);
|
||||
$correspondences = iterator_to_array($this->messages->getCorrespondencies($this->user->identity, $page));
|
||||
|
||||
// #КакаоПрокакалось
|
||||
|
||||
$this->template->corresps = $correspondences;
|
||||
$this->template->paginatorConf = (object) [
|
||||
"count" => $this->messages->getCorrespondenciesCount($this->user->identity),
|
||||
"page" => (int) ($_GET["p"] ?? 1),
|
||||
"amount" => sizeof($this->template->corresps),
|
||||
"perPage" => OPENVK_DEFAULT_PER_PAGE,
|
||||
];
|
||||
}
|
||||
|
||||
function renderApp(int $sel): void
|
||||
|
@ -106,7 +114,7 @@ final class MessengerPresenter extends OpenVKPresenter
|
|||
|
||||
$messages = [];
|
||||
$correspondence = new Correspondence($this->user->identity, $correspondent);
|
||||
foreach($correspondence->getMessages(1, $lastMsg === 0 ? null : $lastMsg) as $message)
|
||||
foreach($correspondence->getMessages(1, $lastMsg === 0 ? NULL : $lastMsg) as $message)
|
||||
$messages[] = $message->simplify();
|
||||
|
||||
header("Content-Type: application/json");
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\Repositories\Notes;
|
||||
use openvk\Web\Models\Repositories\{Users, Notes};
|
||||
use openvk\Web\Models\Entities\Note;
|
||||
|
||||
final class NotesPresenter extends OpenVKPresenter
|
||||
|
@ -47,6 +46,29 @@ final class NotesPresenter extends OpenVKPresenter
|
|||
$this->template->note = $note;
|
||||
}
|
||||
|
||||
function renderPreView(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||
header("HTTP/1.1 400 Bad Request");
|
||||
exit;
|
||||
}
|
||||
|
||||
if(empty($this->postParam("html")) || empty($this->postParam("title"))) {
|
||||
header("HTTP/1.1 400 Bad Request");
|
||||
exit(tr("note_preview_empty_err"));
|
||||
}
|
||||
|
||||
$note = new Note;
|
||||
$note->setSource($this->postParam("html"));
|
||||
|
||||
$this->flash("info", tr("note_preview_warn"), tr("note_preview_warn_details"));
|
||||
$this->template->title = $this->postParam("title");
|
||||
$this->template->html = $note->getText();
|
||||
}
|
||||
|
||||
function renderCreate(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
|
|
@ -8,10 +8,19 @@ final class NotificationPresenter extends OpenVKPresenter
|
|||
$this->assertUserLoggedIn();
|
||||
|
||||
$archive = $this->queryParam("act") === "archived";
|
||||
$this->template->mode = $archive ? "archived" : "new";
|
||||
$count = $this->user->identity->getNotificationsCount($archive);
|
||||
|
||||
if($count == 0 && $this->queryParam("act") == NULL) {
|
||||
$mode = "archived";
|
||||
$archive = true;
|
||||
} else {
|
||||
$mode = $archive ? "archived" : "new";
|
||||
}
|
||||
|
||||
$this->template->mode = $mode;
|
||||
$this->template->page = (int) ($this->queryParam("p") ?? 1);
|
||||
$this->template->iterator = iterator_to_array($this->user->identity->getNotifications($this->template->page, $archive));
|
||||
$this->template->count = $this->user->identity->getNotificationsCount($archive);
|
||||
$this->template->count = $count;
|
||||
|
||||
$this->user->identity->updateNotificationOffset();
|
||||
$this->user->identity->save();
|
||||
|
|
|
@ -14,6 +14,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
{
|
||||
protected $banTolerant = false;
|
||||
protected $activationTolerant = false;
|
||||
protected $deactivationTolerant = false;
|
||||
protected $errorTemplate = "@error";
|
||||
protected $user = NULL;
|
||||
|
||||
|
@ -60,9 +61,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
$this->flash($type, $title, $message, $code);
|
||||
$referer = $_SERVER["HTTP_REFERER"] ?? "/";
|
||||
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: $referer");
|
||||
exit;
|
||||
$this->redirect($referer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,9 +97,8 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
}
|
||||
|
||||
$this->flash("err", tr("login_required_error"), tr("login_required_error_comment"));
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: $loginUrl");
|
||||
exit;
|
||||
|
||||
$this->redirect($loginUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,15 +108,13 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
if($model !== "user") {
|
||||
$this->flash("info", tr("login_required_error"), tr("login_required_error_comment"));
|
||||
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: /login");
|
||||
exit;
|
||||
$this->redirect("/login");
|
||||
}
|
||||
|
||||
return ($action === "register" || $action === "login");
|
||||
}
|
||||
|
||||
return (bool) $this->user->raw->can($action)->model($model)->whichBelongsTo($context === -1 ? null : $context);
|
||||
return (bool) $this->user->raw->can($action)->model($model)->whichBelongsTo($context === -1 ? NULL : $context);
|
||||
}
|
||||
|
||||
protected function assertPermission(string $model, string $action, int $context, bool $throw = false): void
|
||||
|
@ -214,32 +210,21 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
$this->template->thisUser = $this->user->identity;
|
||||
$this->template->userTainted = $user->isTainted();
|
||||
|
||||
if($this->user->identity->isDeleted()) {
|
||||
/*
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⠶⠶⣶⠶⠶⠶⠶⠶⠶⠶⠶⠶⢶⠶⠶⠶⠤⠤⠤⠤⣄⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⣠⡾⠋⠀⠀⠊⠀⠀⠀⠀⠀⠀⠀⠀⠒⠒⠒⠀⠀⠀⠀⠤⢤⣤⣄⠉⠉⠛⠛⠷⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⣰⠟⠀⠀⠀⠀⠀⠐⠋⢑⣤⣶⣶⣤⡢⡀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣄⡂⠀⠀⠶⢄⠙⢷⣤⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⣸⡿⠚⠉⡀⠀⠀⠀⠀⢰⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⢢⠀⠀⡀⣰⣿⣿⣿⣿⣦⡀⠀⠀⠡⡀⢹⡆⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⢀⣴⠏⠀⣀⣀⣀⡤⢤⣄⣠⣿⣿⣿⣿⣻⣿⣿⣷⠀⢋⣾⠈⠙⣶⠒⢿⣿⣿⣿⣿⡿⠟⠃⠀⡀⠡⠼⣧⡀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⢀⣴⣿⢃⡴⢊⢽⣶⣤⣀⠀⠊⠉⠉⡛⢿⣿⣿⣿⠿⠋⢀⡀⠁⠀⠀⢸⣁⣀⣉⣉⣉⡉⠀⠩⡡⠀⣩⣦⠀⠈⠻⣦⡀⠀⠀⠀⠀
|
||||
⠀⢠⡟⢡⠇⡞⢀⠆⠀⢻⣿⣿⣷⣄⠀⢀⠈⠂⠈⢁⡤⠚⡟⠉⠀⣀⣀⠀⠈⠳⣍⠓⢆⢀⡠⢀⣨⣴⣿⣿⡏⢀⡆⠀⢸⡇⠀⠀⠀⠀
|
||||
⠀⣾⠁⢸⠀⠀⢸⠀⠀⠀⠹⣿⣿⣿⣿⣶⣬⣦⣤⡈⠀⠀⠇⠀⠛⠉⣩⣤⣤⣤⣿⣤⣤⣴⣾⣿⣿⣿⣿⣿⣧⠞⠀⠀⢸⡇⠀⠀⠀⠀
|
||||
⠀⢹⣆⠸⠀⠀⢸⠀⠀⠀⠀⠘⢿⣿⣿⣿⣿⣿⣿⣟⣛⠛⠛⣛⡛⠛⠛⣛⣋⡉⠉⣡⠶⢾⣿⣿⣿⣿⣿⣿⡇⠀⠀⢀⣾⠃⠀⠀⠀⠀
|
||||
⠀⠀⠻⣆⡀⠀⠈⢂⠀⠀⠀⠠⡈⢻⣿⣿⣿⣿⡟⠁⠈⢧⡼⠉⠙⣆⡞⠁⠈⢹⣴⠃⠀⢸⣿⣿⣿⣿⣿⣿⠃⠀⡆⣾⠃⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠈⢻⣇⠀⠀⠀⠀⠀⠀⢡⠀⠹⣿⣿⣿⣷⡀⠀⣸⡇⠀⠀⣿⠁⠀⠀⠘⣿⠀⠀⠘⣿⣿⣿⣿⣿⣿⠀⠀⣿⡇⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠹⣇⠀⠠⠀⠀⠀⠀⠡⠐⢬⡻⣿⣿⣿⣿⣿⣷⣶⣶⣿⣦⣤⣤⣤⣿⣦⣶⣿⣿⣿⣿⣿⣿⣿⠀⠀⣿⡇⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠹⣧⡀⠡⡀⠀⠀⠀⠑⠄⠙⢎⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⠀⢿⡇⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠈⠳⣤⡐⡄⠀⠀⠀⠈⠂⠀⠱⣌⠻⣿⣿⣿⣿⣿⣿⣿⠿⣿⠟⢻⡏⢻⣿⣿⣿⣿⣿⣿⣿⠀⢸⡇⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⢮⣦⡀⠂⠀⢀⠀⠀⠈⠳⣈⠻⣿⣿⣿⡇⠘⡄⢸⠀⠀⣇⠀⣻⣿⣿⣿⣿⣿⡏⠀⠸⡇⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⢶⣤⣄⡑⠄⠀⠀⠈⠑⠢⠙⠻⢷⣶⣵⣞⣑⣒⣋⣉⣁⣻⣿⠿⠟⠱⠃⡸⠀⣧⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠻⣷⣄⡀⠐⠢⣄⣀⡀⠀⠉⠉⠉⠉⠛⠙⠭⠭⠄⠒⠈⠀⠐⠁⢀⣿⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠷⢦⣤⣤⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣒⡠⠄⣠⡾⠃⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠛⠷⠶⣦⣤⣭⣤⣬⣭⣭⣴⠶⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
*/
|
||||
if($this->user->identity->isDeleted() && !$this->deactivationTolerant) {
|
||||
if($this->user->identity->isDeactivated()) {
|
||||
header("HTTP/1.1 403 Forbidden");
|
||||
$this->getTemplatingEngine()->render(__DIR__ . "/templates/@deactivated.xml", [
|
||||
"thisUser" => $this->user->identity,
|
||||
"csrfToken" => $GLOBALS["csrfToken"],
|
||||
"isTimezoned" => Session::i()->get("_timezoneOffset"),
|
||||
]);
|
||||
} else {
|
||||
Authenticator::i()->logout();
|
||||
Session::i()->set("_su", NULL);
|
||||
$this->flashFail("err", tr("error"), tr("profile_not_found"));
|
||||
$this->redirect("/", static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/");
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if($this->user->identity->isBanned() && !$this->banTolerant) {
|
||||
|
@ -252,7 +237,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
exit;
|
||||
}
|
||||
|
||||
// ето для емейл уже надо (и по хорошему надо бы избавится от повторяющегося кода мда)
|
||||
# ето для емейл уже надо (и по хорошему надо бы избавится от повторяющегося кода мда)
|
||||
if(!$this->user->identity->isActivated() && !$this->activationTolerant) {
|
||||
header("HTTP/1.1 403 Forbidden");
|
||||
$this->getTemplatingEngine()->render(__DIR__ . "/templates/@email.xml", [
|
||||
|
@ -265,7 +250,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
|
||||
$userValidated = 1;
|
||||
$cacheTime = 0; # Force no cache
|
||||
if ($this->user->identity->onlineStatus() == 0) {
|
||||
if($this->user->identity->onlineStatus() == 0 && !($this->user->identity->isDeleted() || $this->user->identity->isBanned())) {
|
||||
$this->user->identity->setOnline(time());
|
||||
$this->user->identity->save();
|
||||
}
|
||||
|
@ -288,7 +273,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
|
||||
$whichbrowser = new WhichBrowser\Parser(getallheaders());
|
||||
$mobiletheme = OPENVK_ROOT_CONF["openvk"]["preferences"]["defaultMobileTheme"];
|
||||
if($mobiletheme && $whichbrowser->isType('mobile') && Session::i()->get("_tempTheme") == null)
|
||||
if($mobiletheme && $whichbrowser->isType('mobile') && Session::i()->get("_tempTheme") == NULL)
|
||||
$this->setSessionTheme($mobiletheme);
|
||||
|
||||
$theme = NULL;
|
||||
|
@ -299,7 +284,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
$theme = Themepacks::i()[Session::i()->get("_sessionTheme", "ovk")];
|
||||
} else if($this->requestParam("themePreview")) {
|
||||
$theme = Themepacks::i()[$this->requestParam("themePreview")];
|
||||
} else if($this->user->identity !== null && $this->user->identity->getTheme()) {
|
||||
} else if($this->user->identity !== NULL && $this->user->identity->getTheme()) {
|
||||
$theme = $this->user->identity->getTheme();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
use openvk\Web\Models\Entities\Photo;
|
||||
use openvk\Web\Models\Entities\Album;
|
||||
use openvk\Web\Models\Repositories\Photos;
|
||||
use openvk\Web\Models\Repositories\Albums;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\Repositories\Clubs;
|
||||
use openvk\Web\Models\Entities\{Club, Photo, Album};
|
||||
use openvk\Web\Models\Repositories\{Photos, Albums, Users, Clubs};
|
||||
use Nette\InvalidStateException as ISE;
|
||||
|
||||
final class PhotosPresenter extends OpenVKPresenter
|
||||
|
@ -72,6 +67,8 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(empty($this->postParam("name")))
|
||||
$this->flashFail("err", tr("error"), tr("error_segmentation"));
|
||||
else if(strlen($this->postParam("name")) > 36)
|
||||
$this->flashFail("err", tr("error"), tr("error_data_too_big", "name", 36, "bytes"));
|
||||
|
||||
$album = new Album;
|
||||
$album->setOwner(isset($club) ? $club->getId() * -1 : $this->user->id);
|
||||
|
@ -81,9 +78,9 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$album->save();
|
||||
|
||||
if(isset($club))
|
||||
$this->redirect("/album-" . $album->getOwner()->getId() . "_" . $album->getId(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/album-" . $album->getOwner()->getId() . "_" . $album->getId());
|
||||
else
|
||||
$this->redirect("/album" . $album->getOwner()->getId() . "_" . $album->getId(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/album" . $album->getOwner()->getId() . "_" . $album->getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,6 +97,9 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$this->template->album = $album;
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(strlen($this->postParam("name")) > 36)
|
||||
$this->flashFail("err", tr("error"), tr("error_data_too_big", "name", 36, "bytes"));
|
||||
|
||||
$album->setName(empty($this->postParam("name")) ? $album->getName() : $this->postParam("name"));
|
||||
$album->setDescription(empty($this->postParam("desc")) ? NULL : $this->postParam("desc"));
|
||||
$album->setEdited(time());
|
||||
|
@ -124,6 +124,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$name = $album->getName();
|
||||
$owner = $album->getOwner();
|
||||
$album->delete();
|
||||
|
||||
$this->flash("succ", "Альбом удалён", "Альбом $name был успешно удалён.");
|
||||
$this->redirect("/albums" . ($owner instanceof Club ? "-" : "") . $owner->getId());
|
||||
}
|
||||
|
@ -198,7 +199,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$photo->save();
|
||||
|
||||
$this->flash("succ", "Изменения сохранены", "Обновлённое описание появится на странице с фоткой.");
|
||||
$this->redirect("/photo" . $photo->getPrettyId(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/photo" . $photo->getPrettyId());
|
||||
}
|
||||
|
||||
$this->template->photo = $photo;
|
||||
|
@ -236,7 +237,10 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
$album->addPhoto($photo);
|
||||
$this->redirect("/photo" . $photo->getPrettyId(), static::REDIRECT_TEMPORARY);
|
||||
$album->setEdited(time());
|
||||
$album->save();
|
||||
|
||||
$this->redirect("/photo" . $photo->getPrettyId() . "?from=album" . $album->getId());
|
||||
} else {
|
||||
$this->template->album = $album;
|
||||
}
|
||||
|
@ -257,9 +261,11 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$this->assertNoCSRF();
|
||||
$album->removePhoto($photo);
|
||||
$album->setEdited(time());
|
||||
$album->save();
|
||||
|
||||
$this->flash("succ", "Фотография удалена", "Эта фотография была успешно удалена.");
|
||||
$this->redirect("/album" . $album->getPrettyId(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/album" . $album->getPrettyId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,6 +284,6 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
$photo->delete();
|
||||
|
||||
$this->flash("succ", "Фотография удалена", "Эта фотография была успешно удалена.");
|
||||
$this->redirect("/id0", static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/id0");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\Entities\User;
|
||||
use openvk\Web\Models\Repositories\Clubs;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
use openvk\Web\Models\Entities\{User, Club};
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs};
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
final class SearchPresenter extends OpenVKPresenter
|
||||
|
@ -29,7 +27,7 @@ final class SearchPresenter extends OpenVKPresenter
|
|||
if($query != "")
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
// https://youtu.be/pSAWM5YuXx8
|
||||
# https://youtu.be/pSAWM5YuXx8
|
||||
|
||||
$repos = [ "groups" => "clubs", "users" => "users" ];
|
||||
$repo = $repos[$type] or $this->throwError(400, "Bad Request", "Invalid search entity $type.");
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\Ticket;
|
||||
use openvk\Web\Models\Repositories\{Tickets, Users};
|
||||
use openvk\Web\Models\Entities\TicketComment;
|
||||
use openvk\Web\Models\Repositories\TicketComments;
|
||||
use openvk\Web\Models\Entities\{Ticket, TicketComment};
|
||||
use openvk\Web\Models\Repositories\{Tickets, Users, TicketComments};
|
||||
use openvk\Web\Util\Telegram;
|
||||
use Chandler\Session\Session;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use Parsedown;
|
||||
|
||||
final class SupportPresenter extends OpenVKPresenter
|
||||
{
|
||||
protected $banTolerant = true;
|
||||
protected $deactivationTolerant = true;
|
||||
|
||||
private $tickets;
|
||||
private $comments;
|
||||
|
@ -28,6 +28,41 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
$this->assertUserLoggedIn();
|
||||
$this->template->mode = in_array($this->queryParam("act"), ["faq", "new", "list"]) ? $this->queryParam("act") : "faq";
|
||||
|
||||
if($this->template->mode === "faq") {
|
||||
$lang = Session::i()->get("lang", "ru");
|
||||
$base = OPENVK_ROOT . "/data/knowledgebase/faq";
|
||||
if(file_exists("$base.$lang.md"))
|
||||
$file = "$base.$lang.md";
|
||||
else if(file_exists("$base.md"))
|
||||
$file = "$base.md";
|
||||
else
|
||||
$file = NULL;
|
||||
|
||||
if(is_null($file)) {
|
||||
$this->template->faq = [];
|
||||
} else {
|
||||
$lines = file($file);
|
||||
$faq = [];
|
||||
$index = 0;
|
||||
|
||||
foreach($lines as $line) {
|
||||
if(strpos($line, "# ") === 0)
|
||||
++$index;
|
||||
|
||||
$faq[$index][] = $line;
|
||||
}
|
||||
|
||||
$this->template->faq = array_map(function($section) {
|
||||
$title = substr($section[0], 2);
|
||||
array_shift($section);
|
||||
return [
|
||||
$title,
|
||||
(new Parsedown())->text(implode("\n", $section))
|
||||
];
|
||||
}, $faq);
|
||||
}
|
||||
}
|
||||
|
||||
$this->template->count = $this->tickets->getTicketsCountByUserId($this->user->id);
|
||||
if($this->template->mode === "list") {
|
||||
$this->template->page = (int) ($this->queryParam("p") ?? 1);
|
||||
|
@ -63,8 +98,7 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
Telegram::send($helpdeskChat, $telegramText);
|
||||
}
|
||||
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: /support/view/" . $ticket->getId());
|
||||
$this->redirect("/support/view/" . $ticket->getId());
|
||||
} else {
|
||||
$this->flashFail("err", tr("error"), tr("you_have_not_entered_name_or_text"));
|
||||
}
|
||||
|
@ -79,6 +113,7 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
$act = $this->queryParam("act") ?? "open";
|
||||
switch($act) {
|
||||
default:
|
||||
# NOTICE falling through
|
||||
case "open":
|
||||
$state = 0;
|
||||
break;
|
||||
|
@ -154,8 +189,7 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
$comment->setCreated(time());
|
||||
$comment->save();
|
||||
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: /support/view/" . $id);
|
||||
$this->redirect("/support/view/" . $id);
|
||||
} else {
|
||||
$this->flashFail("err", tr("error"), tr("you_have_not_entered_text"));
|
||||
}
|
||||
|
@ -286,6 +320,10 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
|
||||
$user->setBlock_In_Support_Reason($this->queryParam("reason"));
|
||||
$user->save();
|
||||
|
||||
if($this->queryParam("close_tickets"))
|
||||
DatabaseConnection::i()->getConnection()->query("UPDATE tickets SET type = 2 WHERE user_id = ".$id);
|
||||
|
||||
$this->returnJson([ "success" => true, "reason" => $this->queryParam("reason") ]);
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ final class TopicsPresenter extends OpenVKPresenter
|
|||
$topic->setFlags($flags);
|
||||
$topic->save();
|
||||
|
||||
// TODO move to trait
|
||||
# TODO move to trait
|
||||
try {
|
||||
$photo = NULL;
|
||||
$video = NULL;
|
||||
|
@ -108,7 +108,7 @@ final class TopicsPresenter extends OpenVKPresenter
|
|||
}
|
||||
} catch(ISE $ex) {
|
||||
$this->flash("err", "Не удалось опубликовать комментарий", "Файл медиаконтента повреждён или слишком велик.");
|
||||
$this->redirect("/topic" . $topic->getPrettyId(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/topic" . $topic->getPrettyId());
|
||||
}
|
||||
|
||||
if(!empty($this->postParam("text")) || $photo || $video) {
|
||||
|
@ -123,7 +123,7 @@ final class TopicsPresenter extends OpenVKPresenter
|
|||
$comment->save();
|
||||
} catch (\LengthException $ex) {
|
||||
$this->flash("err", "Не удалось опубликовать комментарий", "Комментарий слишком большой.");
|
||||
$this->redirect("/topic" . $topic->getPrettyId(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/topic" . $topic->getPrettyId());
|
||||
}
|
||||
|
||||
if(!is_null($photo))
|
||||
|
@ -133,7 +133,7 @@ final class TopicsPresenter extends OpenVKPresenter
|
|||
$comment->attach($video);
|
||||
}
|
||||
|
||||
$this->redirect("/topic" . $topic->getPrettyId(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/topic" . $topic->getPrettyId());
|
||||
}
|
||||
|
||||
$this->template->club = $club;
|
||||
|
@ -167,7 +167,7 @@ final class TopicsPresenter extends OpenVKPresenter
|
|||
$topic->save();
|
||||
|
||||
$this->flash("succ", tr("changes_saved"), tr("topic_changes_saved_comment"));
|
||||
$this->redirect("/topic" . $topic->getPrettyId(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/topic" . $topic->getPrettyId());
|
||||
}
|
||||
|
||||
$this->template->topic = $topic;
|
||||
|
@ -189,6 +189,6 @@ final class TopicsPresenter extends OpenVKPresenter
|
|||
$this->willExecuteWriteAction();
|
||||
$topic->deleteTopic();
|
||||
|
||||
$this->redirect("/board" . $topic->getClub()->getId(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/board" . $topic->getClub()->getId());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\Repositories\Clubs;
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs};
|
||||
|
||||
final class UnknownTextRouteStrategyPresenter extends OpenVKPresenter
|
||||
{
|
||||
|
|
|
@ -2,24 +2,21 @@
|
|||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Util\Sms;
|
||||
use openvk\Web\Themes\Themepacks;
|
||||
use openvk\Web\Models\Entities\Photo;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\Repositories\Clubs;
|
||||
use openvk\Web\Models\Repositories\Albums;
|
||||
use openvk\Web\Models\Repositories\Videos;
|
||||
use openvk\Web\Models\Repositories\Notes;
|
||||
use openvk\Web\Models\Repositories\Vouchers;
|
||||
use openvk\Web\Models\Entities\{Photo, Post, EmailChangeVerification};
|
||||
use openvk\Web\Models\Entities\Notifications\{CoinsTransferNotification, RatingUpNotification};
|
||||
use openvk\Web\Models\Repositories\{Users, Clubs, Albums, Videos, Notes, Vouchers, EmailChangeVerifications};
|
||||
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||
use openvk\Web\Util\Validator;
|
||||
use openvk\Web\Models\Entities\Notifications\{CoinsTransferNotification, RatingUpNotification};
|
||||
use Chandler\Security\Authenticator;
|
||||
use lfkeitel\phptotp\{Base32, Totp};
|
||||
use chillerlan\QRCode\{QRCode, QROptions};
|
||||
use Nette\Database\UniqueConstraintViolationException;
|
||||
|
||||
final class UserPresenter extends OpenVKPresenter
|
||||
{
|
||||
private $users;
|
||||
|
||||
public $deactivationTolerant = false;
|
||||
function __construct(Users $users)
|
||||
{
|
||||
$this->users = $users;
|
||||
|
@ -30,13 +27,15 @@ final class UserPresenter extends OpenVKPresenter
|
|||
function renderView(int $id): void
|
||||
{
|
||||
$user = $this->users->get($id);
|
||||
if(!$user || $user->isDeleted())
|
||||
$this->template->_template = "User/deleted.xml";
|
||||
else {
|
||||
if($user->getShortCode())
|
||||
if(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH) !== "/" . $user->getShortCode())
|
||||
$this->redirect("/" . $user->getShortCode(), static::REDIRECT_TEMPORARY_PRESISTENT);
|
||||
if(!$user || $user->isDeleted()) {
|
||||
if($user->isDeactivated()) {
|
||||
$this->template->_template = "User/deactivated.xml";
|
||||
|
||||
$this->template->user = $user;
|
||||
} else {
|
||||
$this->template->_template = "User/deleted.xml";
|
||||
}
|
||||
} else {
|
||||
$this->template->albums = (new Albums)->getUserAlbums($user);
|
||||
$this->template->albumsCount = (new Albums)->getUserAlbumsCount($user);
|
||||
$this->template->videos = (new Videos)->getByUser($user, 1, 2);
|
||||
|
@ -72,7 +71,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$name = $user->getFullName();
|
||||
$this->flash("err", "Ошибка доступа", "Вы не можете просматривать полный список подписок $name.");
|
||||
|
||||
$this->redirect("/id$id", static::REDIRECT_TEMPORARY_PRESISTENT);
|
||||
$this->redirect($user->getURL());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +86,9 @@ final class UserPresenter extends OpenVKPresenter
|
|||
elseif (!$user->getPrivacyPermission('groups.read', $this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
else {
|
||||
if($this->queryParam("act") === "managed" && $this->user->id !== $user->getId())
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
|
||||
$this->template->user = $user;
|
||||
$this->template->page = (int) ($this->queryParam("p") ?? 1);
|
||||
$this->template->admin = $this->queryParam("act") == "managed";
|
||||
|
@ -151,7 +153,10 @@ final class UserPresenter extends OpenVKPresenter
|
|||
|
||||
|
||||
if (strtotime($this->postParam("birthday")) < time())
|
||||
$user->setBirthday(strtotime($this->postParam("birthday")));
|
||||
$user->setBirthday(empty($this->postParam("birthday")) ? NULL : strtotime($this->postParam("birthday")));
|
||||
|
||||
if ($this->postParam("birthday_privacy") <= 1 && $this->postParam("birthday_privacy") >= 0)
|
||||
$user->setBirthday_Privacy($this->postParam("birthday_privacy"));
|
||||
|
||||
if ($this->postParam("marialstatus") <= 8 && $this->postParam("marialstatus") >= 0)
|
||||
$user->setMarital_Status($this->postParam("marialstatus"));
|
||||
|
@ -267,9 +272,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
|
||||
$user->toggleSubscription($this->user->identity);
|
||||
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: /id" . $user->getId());
|
||||
exit;
|
||||
$this->redirect($user->getURL());
|
||||
}
|
||||
|
||||
function renderSetAvatar(): void
|
||||
|
@ -288,7 +291,11 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", tr("error"), tr("error_upload_failed"));
|
||||
}
|
||||
|
||||
(new Albums)->getUserAvatarAlbum($this->user->identity)->addPhoto($photo);
|
||||
$album = (new Albums)->getUserAvatarAlbum($this->user->identity);
|
||||
$album->addPhoto($photo);
|
||||
$album->setEdited(time());
|
||||
$album->save();
|
||||
|
||||
$this->flashFail("succ", tr("photo_saved"), tr("photo_saved_comment"));
|
||||
}
|
||||
|
||||
|
@ -312,7 +319,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
if($this->postParam("old_pass") && $this->postParam("new_pass") && $this->postParam("repeat_pass")) {
|
||||
if($this->postParam("new_pass") === $this->postParam("repeat_pass")) {
|
||||
if($this->user->identity->is2faEnabled()) {
|
||||
$code = $this->postParam("code");
|
||||
$code = $this->postParam("password_change_code");
|
||||
if(!($code === (new Totp)->GenerateToken(Base32::decode($this->user->identity->get2faSecret())) || $this->user->identity->use2faBackupCode((int) $code)))
|
||||
$this->flashFail("err", tr("error"), tr("incorrect_2fa_code"));
|
||||
}
|
||||
|
@ -324,6 +331,46 @@ final class UserPresenter extends OpenVKPresenter
|
|||
}
|
||||
}
|
||||
|
||||
if($this->postParam("new_email")) {
|
||||
if(!Validator::i()->emailValid($this->postParam("new_email")))
|
||||
$this->flashFail("err", tr("invalid_email_address"), tr("invalid_email_address_comment"));
|
||||
|
||||
if(!Authenticator::verifyHash($this->postParam("email_change_pass"), $user->getChandlerUser()->getRaw()->passwordHash))
|
||||
$this->flashFail("err", tr("error"), tr("incorrect_password"));
|
||||
|
||||
if($user->is2faEnabled()) {
|
||||
$code = $this->postParam("email_change_code");
|
||||
if(!($code === (new Totp)->GenerateToken(Base32::decode($user->get2faSecret())) || $user->use2faBackupCode((int) $code)))
|
||||
$this->flashFail("err", tr("error"), tr("incorrect_2fa_code"));
|
||||
}
|
||||
|
||||
if($this->postParam("new_email") !== $user->getEmail()) {
|
||||
if (OPENVK_ROOT_CONF['openvk']['preferences']['security']['requireEmail']) {
|
||||
$request = (new EmailChangeVerifications)->getLatestByUser($user);
|
||||
if(!is_null($request) && $request->isNew())
|
||||
$this->flashFail("err", tr("forbidden"), tr("email_rate_limit_error"));
|
||||
|
||||
$verification = new EmailChangeVerification;
|
||||
$verification->setProfile($user->getId());
|
||||
$verification->setNew_Email($this->postParam("new_email"));
|
||||
$verification->save();
|
||||
|
||||
$params = [
|
||||
"key" => $verification->getKey(),
|
||||
"name" => $user->getCanonicalName(),
|
||||
];
|
||||
$this->sendmail($this->postParam("new_email"), "change-email", $params); #Vulnerability possible
|
||||
$this->flashFail("succ", tr("information_-1"), tr("email_change_confirm_message"));
|
||||
}
|
||||
|
||||
try {
|
||||
$user->changeEmail($this->postParam("new_email"));
|
||||
} catch(UniqueConstraintViolationException $ex) {
|
||||
$this->flashFail("err", tr("error"), tr("user_already_exists"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!$user->setShortCode(empty($this->postParam("sc")) ? NULL : $this->postParam("sc")))
|
||||
$this->flashFail("err", tr("error"), tr("error_shorturl_incorrect"));
|
||||
} else if($_GET['act'] === "privacy") {
|
||||
|
@ -376,6 +423,9 @@ final class UserPresenter extends OpenVKPresenter
|
|||
|
||||
if(in_array($this->postParam("nsfw"), [0, 1, 2]))
|
||||
$user->setNsfwTolerance((int) $this->postParam("nsfw"));
|
||||
|
||||
if(in_array($this->postParam("main_page"), [0, 1]))
|
||||
$user->setMain_Page((int) $this->postParam("main_page"));
|
||||
} else if($_GET['act'] === "lMenu") {
|
||||
$settings = [
|
||||
"menu_bildoj" => "photos",
|
||||
|
@ -400,20 +450,54 @@ final class UserPresenter extends OpenVKPresenter
|
|||
throw $ex;
|
||||
}
|
||||
|
||||
$this->flash(
|
||||
"succ",
|
||||
"Изменения сохранены",
|
||||
"Новые данные появятся на вашей странице."
|
||||
);
|
||||
$this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
|
||||
}
|
||||
$this->template->mode = in_array($this->queryParam("act"), [
|
||||
"main", "privacy", "finance", "finance.top-up", "interface"
|
||||
"main", "security", "privacy", "finance", "finance.top-up", "interface"
|
||||
]) ? $this->queryParam("act")
|
||||
: "main";
|
||||
|
||||
if($this->template->mode == "finance") {
|
||||
$address = OPENVK_ROOT_CONF["openvk"]["preferences"]["ton"]["address"];
|
||||
$text = str_replace("$1", (string) $this->user->identity->getId(), OPENVK_ROOT_CONF["openvk"]["preferences"]["ton"]["hint"]);
|
||||
$qrCode = explode("base64,", (new QRCode(new QROptions([
|
||||
"imageTransparent" => false
|
||||
])))->render("ton://transfer/$address?text=$text"));
|
||||
|
||||
$this->template->qrCodeType = substr($qrCode[0], 5);
|
||||
$this->template->qrCodeData = $qrCode[1];
|
||||
}
|
||||
|
||||
$this->template->user = $user;
|
||||
$this->template->themes = Themepacks::i()->getThemeList();
|
||||
}
|
||||
|
||||
function renderDeactivate(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$flags = 0;
|
||||
$reason = $this->postParam("deactivate_reason");
|
||||
$share = $this->postParam("deactivate_share");
|
||||
|
||||
if($share) {
|
||||
$flags |= 0b00100000;
|
||||
|
||||
$post = new Post;
|
||||
$post->setOwner($this->user->id);
|
||||
$post->setWall($this->user->id);
|
||||
$post->setCreated(time());
|
||||
$post->setContent($reason);
|
||||
$post->setFlags($flags);
|
||||
$post->save();
|
||||
}
|
||||
|
||||
$this->user->identity->deactivate($reason);
|
||||
|
||||
$this->redirect("/");
|
||||
}
|
||||
|
||||
function renderTwoFactorAuthSettings(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
@ -456,7 +540,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$this->template->secret = $secret;
|
||||
}
|
||||
|
||||
// Why are these crutch? For some reason, the QR code is not displayed if you just pass the render output to the view
|
||||
# Why are these crutch? For some reason, the QR code is not displayed if you just pass the render output to the view
|
||||
|
||||
$issuer = OPENVK_ROOT_CONF["openvk"]["appearance"]["name"];
|
||||
$email = $this->user->identity->getEmail();
|
||||
|
@ -481,11 +565,30 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$this->flashFail("succ", tr("information_-1"), tr("two_factor_authentication_disabled_message"));
|
||||
}
|
||||
|
||||
function renderResetThemepack(): void
|
||||
{
|
||||
$this->assertNoCSRF();
|
||||
|
||||
$this->setSessionTheme(Themepacks::DEFAULT_THEME_ID);
|
||||
|
||||
if($this->user) {
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$this->user->identity->setStyle(Themepacks::DEFAULT_THEME_ID);
|
||||
$this->user->identity->save();
|
||||
}
|
||||
|
||||
$this->redirect("/");
|
||||
}
|
||||
|
||||
function renderCoinsTransfer(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
|
||||
$this->flashFail("err", tr("error"), tr("feature_disabled"));
|
||||
|
||||
$receiverAddress = $this->postParam("receiver");
|
||||
$value = (int) $this->postParam("value");
|
||||
$message = $this->postParam("message");
|
||||
|
@ -558,4 +661,24 @@ final class UserPresenter extends OpenVKPresenter
|
|||
|
||||
$this->flashFail("succ", tr("information_-1"), tr("rating_increase_successful", $receiver->getURL(), htmlentities($receiver->getCanonicalName()), $value));
|
||||
}
|
||||
|
||||
function renderEmailChangeFinish(): void
|
||||
{
|
||||
$request = (new EmailChangeVerifications)->getByToken(str_replace(" ", "+", $this->queryParam("key")));
|
||||
if(!$request || !$request->isStillValid()) {
|
||||
$this->flash("err", tr("token_manipulation_error"), tr("token_manipulation_error_comment"));
|
||||
$this->redirect("/settings");
|
||||
} else {
|
||||
$request->delete(false);
|
||||
|
||||
try {
|
||||
$request->getUser()->changeEmail($request->getNewEmail());
|
||||
} catch(UniqueConstraintViolationException $ex) {
|
||||
$this->flashFail("err", tr("error"), tr("user_already_exists"));
|
||||
}
|
||||
|
||||
$this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
|
||||
$this->redirect("/settings");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,24 @@ final class VKAPIPresenter extends OpenVKPresenter
|
|||
exit(json_encode($payload));
|
||||
}
|
||||
|
||||
private function twofaFail(int $userId): void
|
||||
{
|
||||
header("HTTP/1.1 401 Unauthorized");
|
||||
header("Content-Type: application/json");
|
||||
|
||||
$payload = [
|
||||
"error" => "need_validation",
|
||||
"error_description" => "use app code",
|
||||
"validation_type" => "2fa_app",
|
||||
"validation_sid" => "2fa_".$userId."_2839041_randommessdontread",
|
||||
"phone_mask" => "+374 ** *** 420",
|
||||
"redirect_url" => "https://http.cat/418", // Not implemented yet :( So there is a photo of cat :3
|
||||
"validation_resend" => "nowhere"
|
||||
];
|
||||
|
||||
exit(json_encode($payload));
|
||||
}
|
||||
|
||||
private function badMethod(string $object, string $method): void
|
||||
{
|
||||
$this->fail(3, "Unknown method passed.", $object, $method);
|
||||
|
@ -249,8 +267,12 @@ final class VKAPIPresenter extends OpenVKPresenter
|
|||
$user = (new Users)->get($uId);
|
||||
|
||||
$code = $this->requestParam("code");
|
||||
if($user->is2faEnabled() && !($code === (new Totp)->GenerateToken(Base32::decode($user->get2faSecret())) || $user->use2faBackupCode((int) $code)))
|
||||
if($user->is2faEnabled() && !($code === (new Totp)->GenerateToken(Base32::decode($user->get2faSecret())) || $user->use2faBackupCode((int) $code))) {
|
||||
if($this->requestParam("2fa_supported") == "1")
|
||||
$this->twofaFail($user->getId());
|
||||
else
|
||||
$this->fail(28, "Invalid 2FA code", "internal", "acquireToken");
|
||||
}
|
||||
|
||||
$token = new APIToken;
|
||||
$token->setUser($user);
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\Video;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\Repositories\Videos;
|
||||
use openvk\Web\Models\Repositories\{Users, Videos};
|
||||
use Nette\InvalidStateException as ISE;
|
||||
|
||||
final class VideosPresenter extends OpenVKPresenter
|
||||
|
@ -80,7 +79,7 @@ final class VideosPresenter extends OpenVKPresenter
|
|||
|
||||
$video->save();
|
||||
|
||||
$this->redirect("/video" . $video->getPrettyId(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/video" . $video->getPrettyId());
|
||||
} else {
|
||||
$this->flashFail("err", "Произошла ошибка", "Видео не может быть опубликовано без названия.");
|
||||
}
|
||||
|
@ -104,7 +103,7 @@ final class VideosPresenter extends OpenVKPresenter
|
|||
$video->save();
|
||||
|
||||
$this->flash("succ", "Изменения сохранены", "Обновлённое описание появится на странице с видосиком.");
|
||||
$this->redirect("/video" . $video->getPrettyId(), static::REDIRECT_TEMPORARY);
|
||||
$this->redirect("/video" . $video->getPrettyId());
|
||||
}
|
||||
|
||||
$this->template->video = $video;
|
||||
|
@ -128,7 +127,6 @@ final class VideosPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", "Не удалось удалить пост", "Вы не вошли в аккаунт.");
|
||||
}
|
||||
|
||||
$this->redirect("/videos".$owner, static::REDIRECT_TEMPORARY);
|
||||
exit;
|
||||
$this->redirect("/videos" . $owner);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,14 +113,14 @@ final class WallPresenter extends OpenVKPresenter
|
|||
$feed = new Feed();
|
||||
|
||||
$channel = new Channel();
|
||||
$channel->title(OPENVK_ROOT_CONF['openvk']['appearance']['name'])->url(ovk_scheme(true) . $_SERVER["SERVER_NAME"])->appendTo($feed);
|
||||
$channel->title($owner->getCanonicalName() . " — " . OPENVK_ROOT_CONF['openvk']['appearance']['name'])->url(ovk_scheme(true) . $_SERVER["HTTP_HOST"])->appendTo($feed);
|
||||
|
||||
foreach($posts as $post) {
|
||||
$item = new Item();
|
||||
$item
|
||||
->title($post->getOwner()->getCanonicalName())
|
||||
->description($post->getText())
|
||||
->url(ovk_scheme(true).$_SERVER["SERVER_NAME"]."/wall{$post->getPrettyId()}")
|
||||
->url(ovk_scheme(true).$_SERVER["HTTP_HOST"]."/wall{$post->getPrettyId()}")
|
||||
->pubDate($post->getPublicationTime()->timestamp())
|
||||
->appendTo($channel);
|
||||
}
|
||||
|
@ -294,17 +294,11 @@ final class WallPresenter extends OpenVKPresenter
|
|||
if($wall > 0 && $wall !== $this->user->identity->getId())
|
||||
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
|
||||
|
||||
if($wall > 0)
|
||||
$this->redirect("/id$wall", 2); #Will exit
|
||||
|
||||
$wall = $wall * -1;
|
||||
$this->redirect("/club$wall", 2);
|
||||
$this->redirect($wallOwner->getURL());
|
||||
}
|
||||
|
||||
function renderPost(int $wall, int $post_id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$post = $this->posts->getPostById($wall, $post_id);
|
||||
if(!$post || $post->isDeleted())
|
||||
$this->notFound();
|
||||
|
@ -339,10 +333,7 @@ final class WallPresenter extends OpenVKPresenter
|
|||
$post->toggleLike($this->user->identity);
|
||||
}
|
||||
|
||||
$this->redirect(
|
||||
"$_SERVER[HTTP_REFERER]#postGarter=" . $post->getId(),
|
||||
static::REDIRECT_TEMPORARY
|
||||
);
|
||||
$this->redirect("$_SERVER[HTTP_REFERER]#postGarter=" . $post->getId());
|
||||
}
|
||||
|
||||
function renderShare(int $wall, int $post_id): void
|
||||
|
@ -394,8 +385,7 @@ final class WallPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", tr("failed_to_delete_post"), tr("login_required_error_comment"));
|
||||
}
|
||||
|
||||
$this->redirect($wall < 0 ? "/club".($wall*-1) : "/id".$wall, static::REDIRECT_TEMPORARY);
|
||||
exit;
|
||||
$this->redirect($wall < 0 ? "/club" . ($wall*-1) : "/id" . $wall);
|
||||
}
|
||||
|
||||
function renderPin(int $wall, int $post_id): void
|
||||
|
@ -416,7 +406,7 @@ final class WallPresenter extends OpenVKPresenter
|
|||
$post->unpin();
|
||||
}
|
||||
|
||||
// TODO localize message based on language and ?act=(un)pin
|
||||
# TODO localize message based on language and ?act=(un)pin
|
||||
$this->flashFail("succ", tr("information_-1"), tr("changes_saved_comment"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
</div>
|
||||
|
||||
<div class="container_gray">
|
||||
{var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||
|
||||
{if sizeof($data) > 0}
|
||||
<div class="content" n:foreach="$data as $dat">
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{block wrap}
|
||||
<div class="ovk-lw-container">
|
||||
<div class="ovk-lw--list">
|
||||
{var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||
|
||||
{if sizeof($data) > 0}
|
||||
<table n:foreach="$data as $dat" border="0" style="font-size:11px;" class="post">
|
||||
|
@ -52,9 +52,9 @@
|
|||
{include actions}
|
||||
<hr/>
|
||||
<div n:if="$sorting ?? true" class="tile">
|
||||
<a href="?C=I;O=R" class="profile_link">{_"sort_randomly"}</a>
|
||||
<a href="?C=M;O=D" class="profile_link">{_"sort_up"}</a>
|
||||
<a href="?C=M;O=A" class="profile_link">{_"sort_down"}</a>
|
||||
<a href="?C=I;O=R" class="profile_link">{_sort_randomly}</a>
|
||||
<a href="?C=M;O=D" class="profile_link">{_sort_up}</a>
|
||||
<a href="?C=M;O=A" class="profile_link">{_sort_down}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,17 +1,27 @@
|
|||
{extends "@layout.xml"}
|
||||
{block title}{_"banned_title"}{/block}
|
||||
{block title}{_banned_title}{/block}
|
||||
|
||||
{block header}
|
||||
{_"banned_header"}
|
||||
{_banned_header}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<center>
|
||||
<img src="/assets/packages/static/openvk/img/oof.apng" alt="{_'banned_alt'}" style="width: 20%;" />
|
||||
<img src="/assets/packages/static/openvk/img/oof.apng" alt="{_banned_alt}" style="width: 20%;" />
|
||||
</center>
|
||||
<p>
|
||||
{tr("banned_1", htmlentities($thisUser->getCanonicalName()))|noescape}<br/>
|
||||
{tr("banned_2", htmlentities($thisUser->getBanReason()))|noescape}
|
||||
|
||||
{if !$thisUser->getUnbanTime()}
|
||||
{_banned_perm}
|
||||
{else}
|
||||
{tr("banned_until_time", $thisUser->getUnbanTime())|noescape}
|
||||
{/if}
|
||||
</p>
|
||||
<p n:if="$thisUser->canUnbanThemself()">
|
||||
<hr/>
|
||||
<center><a class="button" href="/unban.php">{_banned_unban_myself}</a></center>
|
||||
</p>
|
||||
<hr/>
|
||||
<p>
|
||||
|
|
34
Web/Presenters/templates/@deactivated.xml
Normal file
34
Web/Presenters/templates/@deactivated.xml
Normal file
|
@ -0,0 +1,34 @@
|
|||
{extends "@layout.xml"}
|
||||
{block title}{$thisUser->getCanonicalName()}{/block}
|
||||
|
||||
{block header}
|
||||
{$thisUser->getCanonicalName()}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<div class="container_gray bottom" style="margin: -10px -10px 10px;">
|
||||
{tr("profile_deactivated_msg", $thisUser->getDeactivationDate()->format("%e %B %G" . tr("time_at_sp") . "%R"))|noescape}
|
||||
</div>
|
||||
|
||||
<div class="left_small_block">
|
||||
<div>
|
||||
<img src="{$thisUser->getAvatarUrl('normal')}"
|
||||
alt="{$thisUser->getCanonicalName()}"
|
||||
style="width: 100%; image-rendering: -webkit-optimize-contrast;" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right_big_block">
|
||||
<div class="page_info">
|
||||
<div class="accountInfo clearFix">
|
||||
<div class="profileName">
|
||||
<h2>{$thisUser->getFullName()}</h2>
|
||||
<div class="page_status" style="color: #AAA;">{_profile_deactivated_status}</div>
|
||||
</div>
|
||||
</div>
|
||||
<center style="color: #AAA; margin: 40px 0; font-size: 13px;">
|
||||
{_profile_deactivated_info|noescape}
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
|
@ -1,5 +1,4 @@
|
|||
{var instance_name = OPENVK_ROOT_CONF['openvk']['appearance']['name']}
|
||||
|
||||
{var $instance_name = OPENVK_ROOT_CONF['openvk']['appearance']['name']}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{var instance_name = OPENVK_ROOT_CONF['openvk']['appearance']['name']}
|
||||
|
||||
{var $instance_name = OPENVK_ROOT_CONF['openvk']['appearance']['name']}
|
||||
{if !isset($parentModule) || substr($parentModule, 0, 21) === 'libchandler:absolute.'}
|
||||
<!DOCTYPE html>
|
||||
<html n:if="!isset($parentModule) || substr($parentModule, 0, 21) === 'libchandler:absolute.'">
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
{ifset title}{include title} - {/ifset}{$instance_name}
|
||||
|
@ -17,7 +17,7 @@
|
|||
{script "js/l10n.js"}
|
||||
{script "js/openvk.cls.js"}
|
||||
|
||||
{if $isTimezoned == null}
|
||||
{if $isTimezoned == NULL}
|
||||
{script "js/timezone.js"}
|
||||
{/if}
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
|||
{css "css/nsfw-posts.css"}
|
||||
{/if}
|
||||
|
||||
{if $theme !== null}
|
||||
{if $theme !== NULL}
|
||||
{if $theme->inheritDefault()}
|
||||
{css "css/style.css"}
|
||||
{css "css/dialog.css"}
|
||||
|
@ -102,8 +102,13 @@
|
|||
<a href="/" class="home_button{if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME} home_button_custom{/if}" title="{$instance_name}">{if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME}{$instance_name}{/if}</a>
|
||||
<div n:if="isset($thisUser) ? (!$thisUser->isBanned() XOR !$thisUser->isActivated()) : true" class="header_navigation">
|
||||
{ifset $thisUser}
|
||||
{if $thisUser->isDeactivated()}
|
||||
<div class="link">
|
||||
<a href="/" title="[Alt+Shift+,]" accesskey=",">{_header_home}</a>
|
||||
<a href="/logout?hash={urlencode($csrfToken)}">{_header_log_out}</a>
|
||||
</div>
|
||||
{else}
|
||||
<div class="link">
|
||||
<a href="/">{_header_home}</a>
|
||||
</div>
|
||||
<div class="link">
|
||||
<a href="/search?type=groups">{_header_groups}</a>
|
||||
|
@ -125,6 +130,7 @@
|
|||
<input type="search" name="query" placeholder="{_header_search}" style="height: 20px;background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px; background-color: #fff; padding-left: 18px;width: 120px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" />
|
||||
</form>
|
||||
</div>
|
||||
{/if}
|
||||
{else}
|
||||
<div class="link">
|
||||
<a href="/login">{_header_login}</a>
|
||||
|
@ -142,7 +148,7 @@
|
|||
<div class="sidebar">
|
||||
<div class="navigation">
|
||||
{ifset $thisUser}
|
||||
{if !$thisUser->isBanned() XOR !$thisUser->isActivated()}
|
||||
{if !$thisUser->isBanned() XOR !$thisUser->isActivated() XOR $thisUser->isDeactivated()}
|
||||
<a href="/edit" class="link edit-button">{_edit_button}</a>
|
||||
<a href="{$thisUser->getURL()}" class="link" title="{_my_page} [Alt+Shift+.]" accesskey=".">{_my_page}</a>
|
||||
<a href="/friends{$thisUser->getId()}" class="link">{_my_friends}
|
||||
|
@ -161,19 +167,20 @@
|
|||
</a>
|
||||
<a n:if="$thisUser->getLeftMenuItemStatus('notes')" href="/notes{$thisUser->getId()}" class="link">{_my_notes}</a>
|
||||
<a n:if="$thisUser->getLeftMenuItemStatus('groups')" href="/groups{$thisUser->getId()}" class="link">{_my_groups}</a>
|
||||
<a n:if="$thisUser->getLeftMenuItemStatus('news')" href="/feed" class="link" title="{_my_feed} [Alt+Shift+W]" accesskey="w">{_my_feed}</a>
|
||||
<a n:if="$thisUser->getLeftMenuItemStatus('news')" href="/feed" class="link" title="{_my_feed} [Alt+Shift+,]" accesskey=",">{_my_feed}</a>
|
||||
<a href="/notifications" class="link" title="{_my_feedback} [Alt+Shift+N]" accesskey="n">{_my_feedback}
|
||||
{if $thisUser->getNotificationsCount() > 0}
|
||||
(<b>{$thisUser->getNotificationsCount()}</b>)
|
||||
{/if}
|
||||
</a>
|
||||
<a href="/apps?act=installed" class="link">{_my_apps}</a>
|
||||
<a href="/settings" class="link">{_my_settings}</a>
|
||||
|
||||
{var canAccessAdminPanel = $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
||||
{var canAccessHelpdesk = $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
||||
{var menuLinksAvaiable = sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0 && $thisUser->getLeftMenuItemStatus('links')}
|
||||
{var $canAccessAdminPanel = $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
||||
{var $canAccessHelpdesk = $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
||||
{var $menuLinksAvaiable = sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0 && $thisUser->getLeftMenuItemStatus('links')}
|
||||
<div n:if="$canAccessAdminPanel || $canAccessHelpdesk || $menuLinksAvaiable" class="menu_divider"></div>
|
||||
<a href="/admin" class="link" n:if="$canAccessAdminPanel" title="Админ-панель [Alt+Shift+A]" accesskey="a">Админ-панель</a>
|
||||
<a href="/admin" class="link" n:if="$canAccessAdminPanel" title="{_admin} [Alt+Shift+A]" accesskey="a">{_admin}</a>
|
||||
<a href="/support/tickets" class="link" n:if="$canAccessHelpdesk">Helpdesk
|
||||
{if $helpdeskTicketNotAnsweredCount > 0}
|
||||
(<b>{$helpdeskTicketNotAnsweredCount}</b>)
|
||||
|
@ -186,9 +193,50 @@
|
|||
<div n:if="$thisUser->getPinnedClubCount() > 0" class="menu_divider"></div>
|
||||
<a n:foreach="$thisUser->getPinnedClubs() as $club" href="{$club->getURL()}" class="link group_link">{$club->getName()}</a>
|
||||
</div>
|
||||
|
||||
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && $thisUser->getCoins() != 0" id="votesBalance">
|
||||
{tr("you_still_have_x_points", $thisUser->getCoins())|noescape}
|
||||
<br /><br />
|
||||
|
||||
<a href="/settings?act=finance">{_top_up_your_account} »</a>
|
||||
</div>
|
||||
|
||||
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['enable'] && $thisUser->getLeftMenuItemStatus('poster')" href="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['link']}" >
|
||||
<img src="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['src']}" alt="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['caption']}" class="psa-poster" style="max-width: 100%; margin-top: 50px;" />
|
||||
<img src="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['src']}" alt="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['caption']}" class="psa-poster" style="max-width: 100%; margin-top: 10px;" />
|
||||
</a>
|
||||
|
||||
<div class="floating_sidebar">
|
||||
<a class="minilink" href="/friends{$thisUser->getId()}">
|
||||
<object type="internal/link" n:if="$thisUser->getFollowersCount() > 0">
|
||||
<div class="counter">
|
||||
+{$thisUser->getFollowersCount()}
|
||||
</div>
|
||||
</object>
|
||||
<img src="/assets/packages/static/openvk/img/friends.svg">
|
||||
</a>
|
||||
<a class="minilink" href="/albums{$thisUser->getId()}">
|
||||
<img src="/assets/packages/static/openvk/img/photos.svg">
|
||||
</a>
|
||||
<a class="minilink" href="/im">
|
||||
<object type="internal/link" n:if="$thisUser->getUnreadMessagesCount() > 0">
|
||||
<div class="counter">
|
||||
+{$thisUser->getUnreadMessagesCount()}
|
||||
</div>
|
||||
</object>
|
||||
<img src="/assets/packages/static/openvk/img/messages.svg">
|
||||
</a>
|
||||
<a class="minilink" href="/groups{$thisUser->getId()}">
|
||||
<img src="/assets/packages/static/openvk/img/groups.svg">
|
||||
</a>
|
||||
<a class="minilink" href="/notifications">
|
||||
<object type="internal/link" n:if="$thisUser->getNotificationsCount() > 0">
|
||||
<div class="counter">
|
||||
+{$thisUser->getNotificationsCount()}
|
||||
</div>
|
||||
</object>
|
||||
<img src="/assets/packages/static/openvk/img/feedback.svg">
|
||||
</a>
|
||||
</div>
|
||||
{elseif !$thisUser->isActivated()}
|
||||
<a href="/logout?hash={urlencode($csrfToken)}" class="link">{_menu_logout}</a>
|
||||
{else}
|
||||
|
@ -251,7 +299,7 @@
|
|||
</div>
|
||||
|
||||
<div class="page_footer">
|
||||
{var dbVersion = \Chandler\Database\DatabaseConnection::i()->getConnection()->getPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION)}
|
||||
{var $dbVersion = \Chandler\Database\DatabaseConnection::i()->getConnection()->getPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION)}
|
||||
|
||||
<div class="navigation_footer">
|
||||
<a href="/about" class="link">{_footer_about_instance}</a>
|
||||
|
@ -267,6 +315,8 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
{include "components/cookies.xml"}
|
||||
|
||||
{script "js/node_modules/msgpack-lite/dist/msgpack.min.js"}
|
||||
{script "js/node_modules/soundjs/lib/soundjs.min.js"}
|
||||
{script "js/node_modules/ky/umd.js"}
|
||||
|
@ -325,6 +375,7 @@
|
|||
{/ifset}
|
||||
</body>
|
||||
</html>
|
||||
{/if}
|
||||
|
||||
{if isset($parentModule) && substr($parentModule, 0, 21) !== 'libchandler:absolute.'}
|
||||
<!-- INCLUDING TEMPLATE FROM PARENTMODULE: {$parentModule} -->
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<div class="wrap2">
|
||||
<div class="wrap1">
|
||||
<div class="page_wrap padding_top">
|
||||
<div n:ifset="tabs" class="tabs">
|
||||
<div n:ifset="tabs" n:ifcontent class="tabs stupid-fix">
|
||||
{include tabs}
|
||||
</div>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
|||
{include specpage, x => $dat}
|
||||
{else}
|
||||
<div class="container_gray">
|
||||
{var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||
|
||||
{if sizeof($data) > 0}
|
||||
<div class="content" n:foreach="$data as $dat">
|
||||
|
@ -41,7 +41,7 @@
|
|||
{include description, x => $dat}
|
||||
{/ifset}
|
||||
</td>
|
||||
<td n:ifset="actions" valign="top" class="action_links" style="width: 150px; text-transform: lowercase;">
|
||||
<td n:ifset="actions" valign="top" class="action_links" style="width: 150px;">
|
||||
{include actions, x => $dat}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -21,6 +21,15 @@
|
|||
<li><span>{tr("about_wall_posts", $postsCount)|noescape}</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
{if OPENVK_ROOT_CONF['openvk']['preferences']['about']['links']}
|
||||
<h4>{_about_links}</h4>
|
||||
<div style="margin-top: 5px;">
|
||||
{_instance_links}
|
||||
<ul>
|
||||
<li n:foreach="OPENVK_ROOT_CONF['openvk']['preferences']['about']['links'] as $aboutLink"><a href="{$aboutLink['url']}" target="_blank" class="link">{$aboutLink["name"]}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
</td>
|
||||
<td n:if="sizeof($admins) > 0">
|
||||
<h4>{_administrators}</h4>
|
||||
|
@ -44,14 +53,23 @@
|
|||
|
||||
{if sizeof($popularClubs) !== 0}
|
||||
<h4>{_most_popular_groups}</h4>
|
||||
{var $entries = array_chunk($popularClubs, 10, true)}
|
||||
<table width="100%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr valign="top">
|
||||
<td n:foreach="$entries as $chunk">
|
||||
<ol>
|
||||
<li n:foreach="$popularClubs as $entry" style="margin-top: 5px;">
|
||||
<a href="{$entry->club->getURL()}">{$entry->club->getName()}</a>
|
||||
<li value="{$num+1}" style="margin-top: 5px;" n:foreach="$chunk as $num => $club">
|
||||
<a href="{$club->club->getURL()}">{$club->club->getName()}</a>
|
||||
<div>
|
||||
{tr("participants", $entry->subscriptions)}
|
||||
{tr("participants", $club->subscriptions)}
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{/if}
|
||||
|
||||
<h4>{_rules}</h4>
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}{_"welcome"}{/block}
|
||||
{block title}{_welcome}{/block}
|
||||
|
||||
{block header}
|
||||
{_"welcome"}
|
||||
{_welcome}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
{presenter "openvk!Support->knowledgeBaseArticle", "about"}
|
||||
<center>
|
||||
<a class="button" style="margin-right: 5px;cursor: pointer;" href="/login">{_"log_in"}</a>
|
||||
<a class="button" style="cursor: pointer;" href="/reg">{_"registration"}</a>
|
||||
<a class="button" style="margin-right: 5px;cursor: pointer;" href="/login">{_log_in}</a>
|
||||
<a class="button" style="cursor: pointer;" href="/reg">{_registration}</a>
|
||||
</center>
|
||||
{* TO-DO: Add statistics about this instance as on mastodon.social *}
|
||||
{/block}
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
{/block}
|
||||
|
||||
{block content}
|
||||
{_"you_can_invite"}<br><br>
|
||||
{_you_can_invite}<br><br>
|
||||
<center>
|
||||
<input type="text" readonly value="https://{$_SERVER["HTTP_HOST"]}/reg?ref={rawurlencode($thisUser->getRefLinkId())}" size="50" />
|
||||
</center>
|
||||
<p>{_"you_can_invite_2"}</p>
|
||||
<p>{_you_can_invite_2}</p>
|
||||
{/block}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}{_"select_language"}{/block}
|
||||
{block title}{_select_language}{/block}
|
||||
|
||||
{block header}
|
||||
{_"select_language"}
|
||||
{_select_language}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
|
|
@ -396,8 +396,8 @@
|
|||
<tr>
|
||||
<td class="e">
|
||||
Vladimir Barinov (veselcraft), Celestora, Konstantin Kichulkin (kosfurler),
|
||||
Nikita Volkov (sup_ban), Daniel Myslivets, Alexander Kotov (l-lacker),
|
||||
Alexey Assemblerov (BiosNod), Ilya Prokopenko (dsrev) and Maxim Leshchenko (maksales / maksalees)
|
||||
Nikita Volkov (sup_ban), Daniel Myslivets, Maxim Leshchenko (maksales / maksalees)
|
||||
and n1rwana
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -474,7 +474,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td class="e">Best barmaid</td>
|
||||
<td class="v">Jill</td> {* I can agree ~~ dsrev *}
|
||||
<td class="v">Jill</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="e">Initial Helpdesk implementation</td>
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
{var $instance_name = OPENVK_ROOT_CONF['openvk']['appearance']['name']}
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
|
||||
<style>
|
||||
{var css = file_get_contents(OPENVK_ROOT . "/Web/static/js/node_modules/@atlassian/aui/dist/aui/aui-prototyping.css")}
|
||||
{var $css = file_get_contents(OPENVK_ROOT . "/Web/static/js/node_modules/@atlassian/aui/dist/aui/aui-prototyping.css")}
|
||||
{str_replace("fonts/", "/assets/packages/static/openvk/js/node_modules/@atlassian/aui/dist/aui/fonts/", $css)|noescape}
|
||||
</style>
|
||||
<title>{include title} - Админ-панель {=OPENVK_ROOT_CONF['openvk']['appearance']['name']}</title>
|
||||
<title>{include title} - {_admin} {$instance_name}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
@ -15,23 +16,15 @@
|
|||
<div class="aui-header-inner">
|
||||
<div class="aui-header-primary">
|
||||
<h1 id="logo" class="aui-header-logo aui-header-logo-textonly">
|
||||
<a href="/admin">
|
||||
<span class="aui-header-logo-device">{=OPENVK_ROOT_CONF['openvk']['appearance']['name']}</span>
|
||||
<a href="/">
|
||||
<span class="aui-header-logo-device">{$instance_name}</span>
|
||||
</a>
|
||||
</h1>
|
||||
</div>
|
||||
<div n:if="$search ?? false" class="aui-header-secondary">
|
||||
<ul class="aui-nav">
|
||||
<form class="aui-quicksearch dont-default-focus ajs-dirty-warning-exempt">
|
||||
<input
|
||||
id="quickSearchInput"
|
||||
autocomplete="off"
|
||||
class="search"
|
||||
type="text"
|
||||
placeholder="{include searchTitle}"
|
||||
value="{$_GET['q'] ?? ''}"
|
||||
name="q"
|
||||
accesskey="Q" />
|
||||
<input id="quickSearchInput" autocomplete="off" class="search" type="text" placeholder="{include searchTitle}" value="{$_GET['q'] ?? ''}" name="q" accesskey="Q" />
|
||||
<input type="hidden" value=1 name=p />
|
||||
</form>
|
||||
</ul>
|
||||
|
@ -46,83 +39,67 @@
|
|||
<div class="aui-navgroup-inner">
|
||||
<div class="aui-navgroup-primary">
|
||||
<div class="aui-nav-heading">
|
||||
<strong>Обзор</strong>
|
||||
<strong>{_admin_overview}</strong>
|
||||
</div>
|
||||
<ul class="aui-nav">
|
||||
<li>
|
||||
<a href="/admin">
|
||||
Сводка
|
||||
</a>
|
||||
<a href="/admin">{_admin_overview_summary}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="aui-nav-heading">
|
||||
<strong>Пользовательский контент</strong>
|
||||
<strong>{_admin_content}</strong>
|
||||
</div>
|
||||
<ul class="aui-nav">
|
||||
<li>
|
||||
<a href="/admin/users">
|
||||
Пользователи
|
||||
</a>
|
||||
<a href="/admin/users">{_users}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/clubs">
|
||||
Группы
|
||||
</a>
|
||||
<a href="/admin/clubs">{_groups}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/bannedLinks">{_admin_banned_links}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="aui-nav-heading">
|
||||
<strong>Платные услуги</strong>
|
||||
<strong>{_admin_services}</strong>
|
||||
</div>
|
||||
<ul class="aui-nav">
|
||||
<li>
|
||||
<a href="/admin/vouchers">
|
||||
{_vouchers}
|
||||
</a>
|
||||
<a href="/admin/vouchers">{_vouchers}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/gifts">
|
||||
Подарки
|
||||
</a>
|
||||
<a href="/admin/gifts">{_gifts}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="aui-nav-heading">
|
||||
<strong>Настройки</strong>
|
||||
<strong>{_admin_settings}</strong>
|
||||
</div>
|
||||
<ul class="aui-nav">
|
||||
<li>
|
||||
<a href="/admin/settings/tuning">
|
||||
Общие
|
||||
</a>
|
||||
<a href="/admin/settings/tuning">{_admin_settings_tuning}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/settings/appearance">
|
||||
Внешний вид
|
||||
</a>
|
||||
<a href="/admin/settings/appearance">{_admin_settings_appearance}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/settings/security">
|
||||
Безопасность
|
||||
</a>
|
||||
<a href="/admin/settings/security">{_admin_settings_security}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/settings/integrations">
|
||||
Интеграции
|
||||
</a>
|
||||
<a href="/admin/settings/integrations">{_admin_settings_integrations}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/settings/system">
|
||||
Система
|
||||
</a>
|
||||
<a href="/admin/settings/system">{_admin_settings_system}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="aui-nav-heading">
|
||||
<strong>Об OpenVK</strong>
|
||||
<strong>{_admin_about}</strong>
|
||||
</div>
|
||||
<ul class="aui-nav">
|
||||
<li>
|
||||
<a href="/about:openvk">
|
||||
Версия
|
||||
</a>
|
||||
<a href="/about:openvk">{_admin_about_version}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/about">{_admin_about_instance}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -131,7 +108,7 @@
|
|||
</div>
|
||||
<section class="aui-page-panel-content">
|
||||
{ifset $flashMessage}
|
||||
{var type = ["err" => "error", "warn" => "warning", "info" => "basic", "succ" => "success"][$flashMessage->type]}
|
||||
{var $type = ["err" => "error", "warn" => "warning", "info" => "basic", "succ" => "success"][$flashMessage->type]}
|
||||
<div class="aui-message aui-message-{$type}" style="margin-bottom: 15px;">
|
||||
<p class="title">
|
||||
<strong>{$flashMessage->title}</strong>
|
||||
|
|
48
Web/Presenters/templates/Admin/BannedLink.xml
Normal file
48
Web/Presenters/templates/Admin/BannedLink.xml
Normal file
|
@ -0,0 +1,48 @@
|
|||
{extends "@layout.xml"}
|
||||
|
||||
{block title}
|
||||
{_edit}
|
||||
{/block}
|
||||
|
||||
{block heading}
|
||||
{_edit} #{$form->id ?? "undefined"}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<div style="margin: 8px -8px;" class="aui-tabs horizontal-tabs">
|
||||
<ul class="tabs-menu">
|
||||
<li class="menu-item active-tab">
|
||||
<a href="#info">{_admin_banned_link}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tabs-pane active-pane" id="info">
|
||||
<form class="aui" method="POST">
|
||||
<div class="field-group">
|
||||
<label for="id">ID</label>
|
||||
<input class="text long-field" type="number" id="id" name="id" disabled value="{$form->id}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="token">{_admin_banned_domain}</label>
|
||||
<input class="text long-field" type="text" id="link" name="link" value="{$form->link}" />
|
||||
<div class="description">{_admin_banned_link_description}</div>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="token">{_admin_banned_link_regexp}</label>
|
||||
<input class="text long-field" type="text" id="regexp" name="regexp" value="{$form->regexp}" />
|
||||
<div class="description">{_admin_banned_link_regexp_description}</div>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="coins">{_admin_banned_link_reason}</label>
|
||||
<input class="text long-field" type="text" id="reason" name="reason" value="{$form->reason}" />
|
||||
</div>
|
||||
<div class="buttons-container">
|
||||
<div class="buttons">
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input class="aui-button aui-button-primary submit" type="submit" value="{_save}">
|
||||
<a class="aui-button aui-button-secondary" href="/admin/bannedLink/id{$form->id}/unban">{_delete}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
46
Web/Presenters/templates/Admin/BannedLinks.xml
Normal file
46
Web/Presenters/templates/Admin/BannedLinks.xml
Normal file
|
@ -0,0 +1,46 @@
|
|||
{extends "@layout.xml"}
|
||||
|
||||
{block title}
|
||||
{_admin_banned_links}
|
||||
{/block}
|
||||
|
||||
{block heading}
|
||||
<a style="float: right;" class="aui-button aui-button-primary" href="/admin/bannedLink/id0">
|
||||
{_create}
|
||||
</a>
|
||||
|
||||
{_admin_banned_links}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<table class="aui aui-table-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>{_admin_banned_domain}</th>
|
||||
<th>REGEXP</th>
|
||||
<th>{_admin_banned_link_reason}</th>
|
||||
<th>{_admin_banned_link_initiator}</th>
|
||||
<th>{_admin_actions}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr n:foreach="$links as $link">
|
||||
<td>{$link->getId()}</td>
|
||||
<td>{$link->getDomain()}</td>
|
||||
<td>{$link->getRegexpRule()}</td>
|
||||
<td>{$link->getReason() ?? "-"}</td>
|
||||
<td>{$link->getInitiator()->getCanonicalName()}</td>
|
||||
<td>
|
||||
<a class="aui-button aui-button-primary" href="/admin/bannedLink/id{$link->getId()}">
|
||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div align="right">
|
||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||
<a class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||
</div>
|
||||
{/block}
|
|
@ -1,41 +1,35 @@
|
|||
{extends "@layout.xml"}
|
||||
|
||||
{block title}
|
||||
Редактировать {$club->getCanonicalName()}
|
||||
{_edit} {$club->getCanonicalName()}
|
||||
{/block}
|
||||
|
||||
{block heading}
|
||||
{$club->getCanonicalName()}
|
||||
{/block}
|
||||
|
||||
|
||||
{block content}
|
||||
|
||||
{var isMain = $mode === 'main'}
|
||||
{var isBan = $mode === 'ban'}
|
||||
{var isFollowers = $mode === 'followers'}
|
||||
{var $isMain = $mode === 'main'}
|
||||
{var $isBan = $mode === 'ban'}
|
||||
{var $isFollowers = $mode === 'followers'}
|
||||
|
||||
{if $isMain}
|
||||
|
||||
<!-- This main block -->
|
||||
|
||||
<div class="aui-tabs horizontal-tabs">
|
||||
<nav class="aui-navgroup aui-navgroup-horizontal">
|
||||
<div class="aui-navgroup-inner">
|
||||
<div class="aui-navgroup-primary">
|
||||
<ul class="aui-nav">
|
||||
<li class="aui-nav-selected"><a href="?act=main">Главное</a></li>
|
||||
<li><a href="?act=ban">Бан</a></li>
|
||||
<li><a href="?act=followers">Участники</a></li>
|
||||
<li class="aui-nav-selected"><a href="?act=main">{_admin_tab_main}</a></li>
|
||||
<li><a href="?act=ban">{_admin_tab_ban}</a></li>
|
||||
<li><a href="?act=followers">{_admin_tab_followers}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<form class="aui" method="POST">
|
||||
<div class="field-group">
|
||||
<label for="avatar">
|
||||
Аватарка
|
||||
</label>
|
||||
<label for="avatar">{_avatar}</label>
|
||||
<span id="avatar" class="aui-avatar aui-avatar-project aui-avatar-xlarge">
|
||||
<span class="aui-avatar-inner">
|
||||
<img src="{$club->getAvatarUrl('tiny')}" style="object-fit: cover;"></img>
|
||||
|
@ -43,53 +37,39 @@
|
|||
</span>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="id">
|
||||
ID
|
||||
</label>
|
||||
<label for="id">ID</label>
|
||||
<input class="text medium-field" type="number" id="id" disabled value="{$club->getId()}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="id_owner">
|
||||
ID владельца
|
||||
</label>
|
||||
<label for="id_owner">{_admin_ownerid}</label>
|
||||
<input class="text medium-field" type="text" id="id_owner" name="id_owner" value="{$club->getOwner()->getId()}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="name">
|
||||
Название
|
||||
</label>
|
||||
<label for="name">{_admin_title}</label>
|
||||
<input class="text medium-field" type="text" id="name" name="name" value="{$club->getName()}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="about">
|
||||
Описание
|
||||
</label>
|
||||
<label for="about">{_admin_description}</label>
|
||||
<input class="text medium-field" type="text" id="about" name="about" value="{$club->getDescription()}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="shortcode">
|
||||
Адрес
|
||||
</label>
|
||||
<label for="shortcode">{_admin_shortcode}</label>
|
||||
<input class="text medium-field" type="text" id="shortcode" name="shortcode" value="{$club->getShortCode()}" />
|
||||
</div>
|
||||
<br/>
|
||||
<div class="group">
|
||||
<input class="toggle-large" type="checkbox" id="verify" name="verify" value="1" {if $club->isVerified()} checked {/if} />
|
||||
<label for="verify">
|
||||
Верификация
|
||||
</label>
|
||||
<label for="verify">{_admin_verification}</label>
|
||||
</div>
|
||||
<div class="group">
|
||||
<input class="toggle-large" type="checkbox" id="hide_from_global_feed" name="hide_from_global_feed" value="1" {if $club->isHideFromGlobalFeedEnabled()} checked {/if} />
|
||||
<label for="hide_from_global_feed">
|
||||
Не отображать записи в глобальной ленте
|
||||
</label>
|
||||
<label for="hide_from_global_feed">{_admin_club_excludeglobalfeed}</label>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="buttons-container">
|
||||
<div class="buttons">
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input class="button submit" type="submit" value="Сохранить">
|
||||
<input class="button submit" type="submit" value="{_save}">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -97,33 +77,28 @@
|
|||
{/if}
|
||||
|
||||
{if $isBan}
|
||||
|
||||
<!-- This ban block -->
|
||||
|
||||
<div class="aui-tabs horizontal-tabs">
|
||||
<nav class="aui-navgroup aui-navgroup-horizontal">
|
||||
<div class="aui-navgroup-inner">
|
||||
<div class="aui-navgroup-primary">
|
||||
<ul class="aui-nav">
|
||||
<li><a href="?act=main">Главное</a></li>
|
||||
<li class="aui-nav-selected"><a href="?act=ban">Бан</a></li>
|
||||
<li><a href="?act=followers">Участники</a></li>
|
||||
<li><a href="?act=main">{_admin_tab_main}</a></li>
|
||||
<li class="aui-nav-selected"><a href="?act=ban">{_admin_tab_ban}</a></li>
|
||||
<li><a href="?act=followers">{_admin_tab_followers}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<form class="aui" method="POST">
|
||||
<div class="field-group">
|
||||
<label for="ban_reason">
|
||||
Причина бана
|
||||
</label>
|
||||
<label for="ban_reason">{_admin_banreason}</label>
|
||||
<input class="text" type="text" id="text-input" name="ban_reason" value="{$club->getBanReason()}" />
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="buttons-container">
|
||||
<div class="buttons">
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input class="button submit" type="submit" value="Сохранить">
|
||||
<input class="button submit" type="submit" value="{_save}">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -131,19 +106,16 @@
|
|||
{/if}
|
||||
|
||||
{if $isFollowers}
|
||||
|
||||
<!-- This followers block -->
|
||||
|
||||
{var followers = iterator_to_array($followers)}
|
||||
{var $followers = iterator_to_array($followers)}
|
||||
|
||||
<div class="aui-tabs horizontal-tabs">
|
||||
<nav class="aui-navgroup aui-navgroup-horizontal">
|
||||
<div class="aui-navgroup-inner">
|
||||
<div class="aui-navgroup-primary">
|
||||
<ul class="aui-nav">
|
||||
<li><a href="?act=main">Главное</a></li>
|
||||
<li><a href="?act=ban">Бан</a></li>
|
||||
<li class="aui-nav-selected"><a href="?act=followers">Участники</a></li>
|
||||
<li><a href="?act=main">{_admin_tab_main}</a></li>
|
||||
<li><a href="?act=ban">{_admin_tab_ban}</a></li>
|
||||
<li class="aui-nav-selected"><a href="?act=followers">{_admin_tab_followers}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -155,36 +127,30 @@
|
|||
<td>
|
||||
<span class="aui-avatar aui-avatar-xsmall">
|
||||
<span class="aui-avatar-inner">
|
||||
<img src="{$follower->getAvatarUrl('miniscule')}" alt="{$follower->getCanonicalName()}" role="presentation" />
|
||||
<img src="{$follower->getAvatarUrl()}" alt="{$follower->getCanonicalName()}" role="presentation" />
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<a href="{$follower->getURL()}">{$follower->getCanonicalName()}</a>
|
||||
|
||||
<span n:if="$follower->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">
|
||||
заблокирован
|
||||
</span>
|
||||
<span n:if="$follower->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">{_admin_banned}</span>
|
||||
</td>
|
||||
<td>{$follower->isFemale() ? "Женский" : "Мужской"}</td>
|
||||
<td>{$follower->getShortCode() ?? "(отсутствует)"}</td>
|
||||
<td>{$follower->isFemale() ? tr("female") : tr("male")}</td>
|
||||
<td>{$follower->getShortCode() ?? "(" . tr("none") . ")"}</td>
|
||||
<td>{$follower->getRegistrationTime()}</td>
|
||||
<td>
|
||||
<a class="aui-button aui-button-primary" href="/admin/users/id{$follower->getId()}">
|
||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">Редактировать</span>
|
||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div align="right">
|
||||
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
||||
{var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
||||
|
||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">
|
||||
⭁ туда
|
||||
</a>
|
||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">
|
||||
⭇ сюда
|
||||
</a>
|
||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -1,29 +1,31 @@
|
|||
{extends "@layout.xml"}
|
||||
{var search = true}
|
||||
{var $search = true}
|
||||
|
||||
{block title}
|
||||
Группы
|
||||
{_admin_club_search}
|
||||
{/block}
|
||||
|
||||
{block heading}
|
||||
Бутылки
|
||||
{_groups}
|
||||
{/block}
|
||||
|
||||
{block searchTitle}Поиск бутылок{/block}
|
||||
{block searchTitle}
|
||||
{include title}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
{var clubs = iterator_to_array($clubs)}
|
||||
{var amount = sizeof($clubs)}
|
||||
{var $clubs = iterator_to_array($clubs)}
|
||||
{var $amount = sizeof($clubs)}
|
||||
|
||||
<table class="aui aui-table-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Имя</th>
|
||||
<th>Автор</th>
|
||||
<th>Описание</th>
|
||||
<th>Короткий адрес</th>
|
||||
<th>Действия</th>
|
||||
<th>ID</th>
|
||||
<th>{_admin_title}</th>
|
||||
<th>{_admin_author}</th>
|
||||
<th>{_admin_description}</th>
|
||||
<th>{_admin_shortcode}</th>
|
||||
<th>{_admin_actions}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -39,7 +41,7 @@
|
|||
<a href="{$club->getURL()}">{$club->getCanonicalName()}</a>
|
||||
</td>
|
||||
<td>
|
||||
{var user = $club->getOwner()}
|
||||
{var $user = $club->getOwner()}
|
||||
|
||||
<span class="aui-avatar aui-avatar-xsmall">
|
||||
<span class="aui-avatar-inner">
|
||||
|
@ -49,11 +51,11 @@
|
|||
|
||||
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a>
|
||||
</td>
|
||||
<td>{$club->getDescription() ?? "(не указано)"}</td>
|
||||
<td>{$club->getDescription() ?? "(" . tr("none") . ")"}</td>
|
||||
<td>{$club->getShortCode()}</td>
|
||||
<td>
|
||||
<a class="aui-button aui-button-primary" href="/admin/clubs/id{$club->getId()}">
|
||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">Редактировать</span>
|
||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -61,13 +63,9 @@
|
|||
</table>
|
||||
<br/>
|
||||
<div align="right">
|
||||
{var isLast = ((10 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
||||
{var $isLast = ((10 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
||||
|
||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">
|
||||
⭁ туда
|
||||
</a>
|
||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">
|
||||
⭇ сюда
|
||||
</a>
|
||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||
</div>
|
||||
{/block}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
{block title}
|
||||
{if $form->id === 0}
|
||||
Новый подарок
|
||||
{_admin_newgift}
|
||||
{else}
|
||||
Подарок "{$form->name}"
|
||||
{_gift} "{$form->name}"
|
||||
{/if}
|
||||
{/block}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
|||
<form class="aui" method="POST" enctype="multipart/form-data">
|
||||
<div class="field-group">
|
||||
<label for="avatar">
|
||||
Изображение
|
||||
{_admin_image}
|
||||
<span n:if="$form->id === 0" class="aui-icon icon-required"></span>
|
||||
</label>
|
||||
{if $form->id === 0}
|
||||
|
@ -29,43 +29,39 @@
|
|||
</span>
|
||||
<input style="display: none;" id="picInput" type="file" name="pic" accept="image/jpeg,image/png,image/gif,image/webp" />
|
||||
<div class="description">
|
||||
<a id="picChange" href="javascript:false">Заменить изображение?</a>
|
||||
<a id="picChange" href="javascript:false">{_admin_image_replace}</a>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="field-group">
|
||||
<label for="id">
|
||||
ID
|
||||
</label>
|
||||
<label for="id">ID</label>
|
||||
<input class="text long-field" type="number" id="id" disabled="disabled" value="{$form->id}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="putin">
|
||||
Использований
|
||||
</label>
|
||||
<input class="text long-field" type="number" id="putin" disabled="disabled" value="{$form->usages}" />
|
||||
<label for="usages">{_admin_uses}</label>
|
||||
<input class="text long-field" type="number" id="usages" disabled="disabled" value="{$form->usages}" />
|
||||
<div n:if="$form->usages > 0" class="description">
|
||||
<a href="javascript:$('#putin').value(0);">Обнулить?</a>
|
||||
<a href="javascript:$('#usages').value(0);">{_admin_uses_reset}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="name">
|
||||
Внутренее имя
|
||||
{_admin_name}
|
||||
<span class="aui-icon icon-required"></span>
|
||||
</label>
|
||||
<input class="text long-field" type="text" id="name" name="name" value="{$form->name}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="price">
|
||||
Цена
|
||||
{_admin_price}
|
||||
<span class="aui-icon icon-required"></span>
|
||||
</label>
|
||||
<input class="text long-field" type="number" id="price" name="price" min="0" value="{$form->price}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="limit">
|
||||
Ограничение
|
||||
{_admin_limits}
|
||||
<span class="aui-icon icon-required"></span>
|
||||
</label>
|
||||
<input class="text long-field" type="number" min="-1" id="limit" name="limit" value="{$form->limit}" />
|
||||
|
@ -76,7 +72,7 @@
|
|||
<input n:attr="disabled => $form->id === 0, checked => $form->id === 0" class="checkbox" type="checkbox" name="reset_limit" id="reset_limit" />
|
||||
<span class="aui-form-glyph"></span>
|
||||
|
||||
<label for="reset_limit">Сбросить счётчик ограничений</label>
|
||||
<label for="reset_limit">{_admin_limits_reset}</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{extends "@layout.xml"}
|
||||
|
||||
{block title}
|
||||
Наборы подарков
|
||||
{_admin_giftsets}
|
||||
{/block}
|
||||
|
||||
{block headingWrap}
|
||||
|
@ -9,7 +9,7 @@
|
|||
{_create}
|
||||
</a>
|
||||
|
||||
<h1>Наборы подарков</h1>
|
||||
<h1>{_admin_giftsets}</h1>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
@ -27,12 +27,11 @@
|
|||
</td>
|
||||
<td style="vertical-align: middle; text-align: right;">
|
||||
<a class="aui-button aui-button-primary" href="/admin/gifts/{$cat->getSlug()}.{$cat->getId()}.meta">
|
||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">Редактировать</span>
|
||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||
</a>
|
||||
|
||||
<a class="aui-button" href="/admin/gifts/{$cat->getSlug()}.{$cat->getId()}/">
|
||||
<span class="aui-icon aui-icon-small aui-iconfont-gallery">Открыть</span>
|
||||
Открыть
|
||||
<span class="aui-icon aui-icon-small aui-iconfont-gallery">{_admin_open}</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -40,17 +39,13 @@
|
|||
</table>
|
||||
{else}
|
||||
<center>
|
||||
<p>Наборов подарков нету. Чтобы создать подарок, создайте набор.</p>
|
||||
<p>{_admin_giftsets_none}</p>
|
||||
</center>
|
||||
{/if}
|
||||
|
||||
<div align="right">
|
||||
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($categories)) < $count}
|
||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?act={$act}&p={($_GET['p'] ?? 1) - 1}">
|
||||
⭁ туда
|
||||
</a>
|
||||
<a n:if="$isLast" class="aui-button" href="?act={$act}&p={($_GET['p'] ?? 1) + 1}">
|
||||
⭇ сюда
|
||||
</a>
|
||||
{var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($categories)) < $count}
|
||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?act={$act}&p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||
<a n:if="$isLast" class="aui-button" href="?act={$act}&p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||
</div>
|
||||
{/block}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
{block title}
|
||||
{if $form->id === 0}
|
||||
Создать набор подарков
|
||||
{_admin_giftsets_create}
|
||||
{else}
|
||||
{$form->languages["master"]->name}
|
||||
{/if}
|
||||
|
@ -14,7 +14,7 @@
|
|||
|
||||
{block content}
|
||||
<form class="aui" method="POST">
|
||||
<h3>Общие настройки</h3>
|
||||
<h3>{_admin_commonsettings}</h3>
|
||||
<fieldset>
|
||||
<div class="field-group">
|
||||
<label for="id">
|
||||
|
@ -24,37 +24,37 @@
|
|||
</div>
|
||||
<div class="field-group">
|
||||
<label for="name_master">
|
||||
Наименование
|
||||
{_admin_name}
|
||||
<span class="aui-icon icon-required"></span>
|
||||
</label>
|
||||
<input class="text long-field" type="text" id="name_master" name="name_master" value="{$form->languages['master']->name}" />
|
||||
<div class="description">Внутреннее название набора, которое будет использоваться, если не удаётся найти название на языке пользователя.</div>
|
||||
<div class="description">{_admin_giftsets_title}</div>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="description_master">
|
||||
Описание
|
||||
{_admin_description}
|
||||
<span class="aui-icon icon-required"></span>
|
||||
</label>
|
||||
<input class="text long-field" type="text" id="description_master" name="description_master" value="{$form->languages['master']->description}" />
|
||||
<div class="description">Внутреннее описание набора, которое будет использоваться, если не удаётся найти название на языке пользователя.</div>
|
||||
<div class="description">{_admin_giftsets_description}</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<h3>Языко-зависимые настройки</h3>
|
||||
<h3>{_admin_langsettings}</h3>
|
||||
<fieldset>
|
||||
{foreach $form->languages as $locale => $data}
|
||||
{continueIf $locale === "master"}
|
||||
|
||||
<div class="field-group">
|
||||
<label for="name_{$locale}">
|
||||
Наименование
|
||||
{_admin_name}
|
||||
<img src="/assets/packages/static/openvk/img/flags/{$locale}.gif" alt="{$locale}" />
|
||||
</label>
|
||||
<input class="text long-field" type="text" id="name_{$locale}" name="name_{$locale}" value="{$data->name}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="description_{$locale}">
|
||||
Описание
|
||||
{_admin_description}
|
||||
<img src="/assets/packages/static/openvk/img/flags/{$locale}.gif" alt="{$locale}" />
|
||||
</label>
|
||||
<input class="text long-field" type="text" id="description_{$locale}" name="description_{$locale}" value="{$data->description}" />
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
{_create}
|
||||
</a>
|
||||
|
||||
<h1>Набор "{$cat->getName()}"</h1>
|
||||
<h1>{_admin_giftset} "{$cat->getName()}"</h1>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
@ -32,11 +32,11 @@
|
|||
<td style="vertical-align: middle;">
|
||||
{$gift->getName()}
|
||||
<span n:if="$gift->isFree()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-success">
|
||||
бесплатный
|
||||
{_admin_price_free}
|
||||
</span>
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
{$gift->getPrice()} голосов
|
||||
{tr("points_amount", $gift->getPrice())}
|
||||
</td>
|
||||
<td style="vertical-align: middle;">
|
||||
{$gift->getUsages()} раз
|
||||
|
@ -71,12 +71,9 @@
|
|||
{/if}
|
||||
|
||||
<div align="right">
|
||||
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($gifts)) < $count}
|
||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">
|
||||
⭁ туда
|
||||
</a>
|
||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">
|
||||
⭇ сюда
|
||||
</a>
|
||||
{var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($gifts)) < $count}
|
||||
|
||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||
</div>
|
||||
{/block}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{extends "@layout.xml"}
|
||||
|
||||
{block title}
|
||||
Сводка
|
||||
{_admin_overview_summary}
|
||||
{/block}
|
||||
|
||||
{block heading}
|
||||
Сводка
|
||||
{_admin_overview_summary}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
Да!
|
||||
┬─┬︵/(.□.)╯
|
||||
{/block}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{extends "@layout.xml"}
|
||||
|
||||
{block title}
|
||||
Редактировать {$user->getCanonicalName()}
|
||||
{_edit} {$user->getCanonicalName()}
|
||||
{/block}
|
||||
|
||||
{block heading}
|
||||
|
@ -12,9 +12,7 @@
|
|||
<div class="aui-tabs horizontal-tabs">
|
||||
<form class="aui" method="POST">
|
||||
<div class="field-group">
|
||||
<label for="avatar">
|
||||
Аватарка
|
||||
</label>
|
||||
<label for="avatar">{_avatar}</label>
|
||||
<span id="avatar" class="aui-avatar aui-avatar-project aui-avatar-xlarge">
|
||||
<span class="aui-avatar-inner">
|
||||
<img src="{$user->getAvatarUrl('tiny')}" style="object-fit: cover;"></img>
|
||||
|
@ -22,83 +20,60 @@
|
|||
</span>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="id">
|
||||
ID
|
||||
</label>
|
||||
<label for="id">ID</label>
|
||||
<input class="text medium-field" type="number" id="id" disabled value="{$user->getId()}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="guid">
|
||||
GUID
|
||||
</label>
|
||||
<label for="guid">GUID</label>
|
||||
<input class="text medium-field" id="guid" disabled value="{$user->getChandlerUser()->getId()}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="registration_ip">
|
||||
Первый IP
|
||||
</label>
|
||||
<input class="text medium-field" id="registration_ip" disabled value="{$user->getRegistrationIP()}" />
|
||||
<label for="registration_ip">{_admin_first_known_ip}</label>
|
||||
<input class="text medium-field" id="guid" disabled value="{$user->getRegistrationIP()}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="first_name">
|
||||
Имя
|
||||
</label>
|
||||
<label for="first_name">{_name}</label>
|
||||
<input class="text medium-field" type="text" id="first_name" name="first_name" value="{$user->getFirstName()}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="last_name">
|
||||
Фамилия
|
||||
</label>
|
||||
<label for="last_name">{_surname}</label>
|
||||
<input class="text medium-field" type="text" id="last_name" name="last_name" value="{$user->getLastName()}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="nickname">
|
||||
Никнейм
|
||||
</label>
|
||||
<label for="nickname">{_nickname}</label>
|
||||
<input class="text medium-field" type="text" id="nickname" name="nickname" value="{$user->getPseudo()}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="status">
|
||||
Статус
|
||||
</label>
|
||||
<label for="status">{_status}</label>
|
||||
<input class="text medium-field" type="text" id="status" name="status" value="{$user->getStatus()}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="email">
|
||||
E-Mail
|
||||
</label>
|
||||
<label for="email">E-Mail</label>
|
||||
<input class="text medium-field" type="email" id="email" name="email" value="{$user->getEmail()}" />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="shortcode">
|
||||
Адрес страницы
|
||||
</label>
|
||||
<label for="shortcode">{_admin_shortcode}</label>
|
||||
<input class="text medium-field" type="text" id="shortcode" name="shortcode" value="{$user->getShortCode()}" />
|
||||
</div>
|
||||
<hr>
|
||||
<div class="field-group">
|
||||
<label for="city">
|
||||
Верификация
|
||||
</label>
|
||||
<label for="city">{_admin_verification}</label>
|
||||
<input class="toggle-large" type="checkbox" id="verify" name="verify" value="1" {if $user->isVerified()} checked {/if} />
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="city">
|
||||
Онлайн статус
|
||||
</label>
|
||||
<label for="city">{_admin_user_online}</label>
|
||||
<select name="online" class="select">
|
||||
<option value="0" {if $user->onlineStatus() > 2}selected{/if}>По-умолчанию</option>
|
||||
<option value="1" {if $user->onlineStatus() == 1}selected{/if}>Инкогнито</option>
|
||||
<option value="2" {if $user->onlineStatus() == 2}selected{/if}>Юзер умер</option>
|
||||
<option value="0" {if $user->onlineStatus() > 2}selected{/if}>{_admin_user_online_default}</option>
|
||||
<option value="1" {if $user->onlineStatus() == 1}selected{/if}>{_admin_user_online_incognite}</option>
|
||||
<option value="2" {if $user->onlineStatus() == 2}selected{/if}>{_admin_user_online_deceased}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="buttons-container">
|
||||
<div class="buttons">
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input class="button submit" type="submit" value="Сохранить">
|
||||
<input class="button submit" type="submit" value="{_save}">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
{/block}
|
||||
|
|
|
@ -1,29 +1,31 @@
|
|||
{extends "@layout.xml"}
|
||||
{var search = true}
|
||||
{var $search = true}
|
||||
|
||||
{block title}
|
||||
Пользователи
|
||||
{_admin_user_search}
|
||||
{/block}
|
||||
|
||||
{block heading}
|
||||
Пиздюки
|
||||
{_users}
|
||||
{/block}
|
||||
|
||||
{block searchTitle}Поиск пиздюков{/block}
|
||||
{block searchTitle}
|
||||
{include title}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
{var users = iterator_to_array($users)}
|
||||
{var amount = sizeof($users)}
|
||||
{var $users = iterator_to_array($users)}
|
||||
{var $amount = sizeof($users)}
|
||||
|
||||
<table class="aui aui-table-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Имя</th>
|
||||
<th>Пол</th>
|
||||
<th>Короткий адрес</th>
|
||||
<th>Дата регистрации</th>
|
||||
<th>Действия</th>
|
||||
<th>ID</th>
|
||||
<th>{_admin_name}</th>
|
||||
<th>{_gender}</th>
|
||||
<th>{_admin_shortcode}</th>
|
||||
<th>{_registration_date}</th>
|
||||
<th>{_admin_actions}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -38,20 +40,18 @@
|
|||
|
||||
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a>
|
||||
|
||||
<span n:if="$user->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">
|
||||
заблокирован
|
||||
</span>
|
||||
<span n:if="$user->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">{_admin_banned}</span>
|
||||
</td>
|
||||
<td>{$user->isFemale() ? "Женский" : "Мужской"}</td>
|
||||
<td>{$user->getShortCode() ?? "(отсутствует)"}</td>
|
||||
<td>{$user->isFemale() ? tr("female") : tr("male")}</td>
|
||||
<td>{$user->getShortCode() ?? "(" . tr("none") . ")"}</td>
|
||||
<td>{$user->getRegistrationTime()}</td>
|
||||
<td>
|
||||
<a class="aui-button aui-button-primary" href="/admin/users/id{$user->getId()}">
|
||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">Редактировать</span>
|
||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||
</a>
|
||||
{if $thisUser->getChandlerUser()->can("substitute")->model('openvk\Web\Models\Entities\User')->whichBelongsTo(0)}
|
||||
<a class="aui-button" href="/setSID/{$user->getChandlerUser()->getId()}?hash={rawurlencode($csrfToken)}">
|
||||
<span class="aui-icon aui-icon-small aui-iconfont-sign-in">Войти как</span>
|
||||
<span class="aui-icon aui-icon-small aui-iconfont-sign-in">{_admin_loginas}</span>
|
||||
</a>
|
||||
{/if}
|
||||
</td>
|
||||
|
@ -60,13 +60,9 @@
|
|||
</table>
|
||||
<br/>
|
||||
<div align="right">
|
||||
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
||||
{var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
||||
|
||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">
|
||||
⭁ туда
|
||||
</a>
|
||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">
|
||||
⭇ сюда
|
||||
</a>
|
||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||
</div>
|
||||
{/block}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue