Merge branch 'master' into feature-reports

This commit is contained in:
Ilya Prokopenko 2022-04-24 12:17:35 +07:00 committed by GitHub
commit 1b469c6e60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
102 changed files with 1022 additions and 598 deletions

View file

@ -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" />

View file

@ -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">

View file

@ -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.co](http://openvk.co)** - yet another official mirror of openvk.su without TLS (<https://t.me/openvkch/1654>)
* [social.fetbuk.ru](http://social.fetbuk.ru/)
* [vepurovk.xyz](http://vepurovk.xyz/)
## Can I create my own OpenVK instance?
@ -34,27 +36,33 @@ If you want, you can add your instance to the list above so that people can regi
* PHP 8 has **not** yet been tested, so you should not expect it to work. (edit: it does not work).
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):

View file

@ -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.co](http://openvk.co)** - ещё одно официальное зеркало openvk.su без TLS (<https://t.me/openvkch/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)).
@ -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>

View file

@ -62,8 +62,26 @@ final class Users extends VKAPIRequestHandler
$response[$i]->photo_max_orig = $usr->getAvatarURL();
break;
case 'photo_max':
$response[$i]->photo_max = $usr->getAvatarURL();
$response[$i]->photo_max = $usr->getAvatarURL("original");
break;
case 'photo_50':
$response[$i]->photo_50 = $usr->getAvatarURL();
break;
case 'photo_100':
$response[$i]->photo_50 = $usr->getAvatarURL("tiny");
break;
case 'photo_200':
$response[$i]->photo_50 = $usr->getAvatarURL("normal");
break;
case 'photo_200_orig': // вообще не ебу к чему эта строка ну пусть будет кек
$response[$i]->photo_50 = $usr->getAvatarURL("normal");
break;
case 'photo_400_orig':
$response[$i]->photo_50 = $usr->getAvatarURL("normal");
break;
// Она хочет быть выебанной видя матан
// Покайфу когда ты Виет а вокруг лишь дискриминант
case 'status':
if($usr->getStatus() != null)
$response[$i]->status = $usr->getStatus();

View file

@ -35,11 +35,42 @@ final class Wall extends VKAPIRequestHandler
"date" => $attachment->getPublicationTime()->timestamp(),
"id" => $attachment->getVirtualId(),
"owner_id" => $attachment->getOwner()->getId(),
"sizes" => array([
"height" => 500, // Для временного компросима оставляю статическое число. Если каждый раз обращаться к файлу за количеством пикселов, то наступает пuпuська полная с производительностью, так что пока так
"url" => $attachment->getURL(),
"sizes" => array(
[
"height" => 2560,
"url" => $attachment->getURLBySizeId("normal"),
"type" => "m",
"width" => 500,
"width" => 2560,
],
[
"height" => 130,
"url" => $attachment->getURLBySizeId("tiny"),
"type" => "o",
"width" => 130,
],
[
"height" => 604,
"url" => $attachment->getURLBySizeId("normal"),
"type" => "p",
"width" => 604,
],
[
"height" => 807,
"url" => $attachment->getURLBySizeId("large"),
"type" => "q",
"width" => 807,
],
[
"height" => 1280,
"url" => $attachment->getURLBySizeId("larger"),
"type" => "r",
"width" => 1280,
],
[
"height" => 75, // Для временного компросима оставляю статическое число. Если каждый раз обращаться к файлу за количеством пикселов, то наступает пuпuська полная с производительностью, так что пока так
"url" => $attachment->getURLBySizeId("miniscule"),
"type" => "s",
"width" => 75,
]),
"text" => "",
"has_tags" => false
@ -168,11 +199,42 @@ final class Wall extends VKAPIRequestHandler
"date" => $attachment->getPublicationTime()->timestamp(),
"id" => $attachment->getVirtualId(),
"owner_id" => $attachment->getOwner()->getId(),
"sizes" => array([
"height" => 500, // я ещё я заебался вставлять одинаковый код в два разных места
"url" => $attachment->getURL(),
"sizes" => array(
[
"height" => 2560,
"url" => $attachment->getURLBySizeId("normal"),
"type" => "m",
"width" => 500,
"width" => 2560,
],
[
"height" => 130,
"url" => $attachment->getURLBySizeId("tiny"),
"type" => "o",
"width" => 130,
],
[
"height" => 604,
"url" => $attachment->getURLBySizeId("normal"),
"type" => "p",
"width" => 604,
],
[
"height" => 807,
"url" => $attachment->getURLBySizeId("large"),
"type" => "q",
"width" => 807,
],
[
"height" => 1280,
"url" => $attachment->getURLBySizeId("larger"),
"type" => "r",
"width" => 1280,
],
[
"height" => 75, // Для временного компросима оставляю статическое число. Если каждый раз обращаться к файлу за количеством пикселов, то наступает пuпuська полная с производительностью, так что пока так
"url" => $attachment->getURLBySizeId("miniscule"),
"type" => "s",
"width" => 75,
]),
"text" => "",
"has_tags" => false

16
Vagrantfile vendored
View file

@ -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.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-12/install"
config.vm.provision "shell", inline: "/bin/tcsh /.ovk_release/install/automated/freebsd-13/install"
end

View file

@ -346,6 +346,11 @@ class Club extends RowModel
{
return $this->getRecord()->website;
}
function getAlert(): ?string
{
return $this->getRecord()->alert;
}
use Traits\TSubscribable;
}

View file

@ -6,7 +6,9 @@ abstract class Media extends Postable
{
protected $fileExtension = "oct"; #octet stream xddd
protected $upperNodeReferenceColumnName = "owner";
protected $processingPlaceholder = NULL;
protected $processingTime = 30;
function __destruct()
{
#Remove data, if model wasn't presisted
@ -22,6 +24,11 @@ abstract class Media extends Postable
else
return OPENVK_ROOT . "/storage/";
}
protected function checkIfFileIsProcessed(): bool
{
throw new \LogicException("checkIfFileIsProcessed is not implemented");
}
abstract protected function saveFile(string $filename, string $hash): bool;
@ -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"
@ -68,6 +79,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
{
@ -89,7 +120,17 @@ 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");

View file

@ -55,16 +55,21 @@ 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);
}

View file

@ -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
{
@ -166,50 +168,61 @@ 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->getRecord()->deleted)
return "DELETED";
else
return $this->getFirstName() . ' ' . $this->getLastName();
else
return $this->getFirstName() . " " . $this->getLastName();
}
function getPhone(): ?string
{
return $this->getRecord()->phone;
}
function getEmail(): ?string
{
return $this->getRecord()->email;
}
function getOnline(): DateTime
{
return new DateTime($this->getRecord()->online);
}
function getDescription(): ?string
{
return $this->getRecord()->about;
}
function getStatus(): ?string
{
return $this->getRecord()->status;
}
function getShortCode(): ?string
{
return $this->getRecord()->shortcode;
}
function getAlert(): ?string
{
return $this->getRecord()->alert;
}
function getBanReason(): ?string
{
return $this->getRecord()->block_reason;
@ -219,105 +232,105 @@ class User extends RowModel
{
return $this->getRecord()->block_in_support_reason;
}
function getType(): int
{
return $this->getRecord()->type;
}
function getCoins(): float
{
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
return 0.0;
return $this->getRecord()->coins;
}
function getRating(): int
{
return OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"] ? $this->getRecord()->rating : 0;
}
function getReputation(): int
{
return $this->getRecord()->reputation;
}
function getRegistrationTime(): DateTime
{
return new DateTime($this->getRecord()->since->getTimestamp());
}
function getRegistrationIP(): string
{
return $this->getRecord()->registering_ip;
}
function getHometown(): ?string
{
return $this->getRecord()->hometown;
}
function getPoliticalViews(): int
{
return $this->getRecord()->polit_views;
}
function getMaritalStatus(): int
{
return $this->getRecord()->marital_status;
}
function getContactEmail(): ?string
{
return $this->getRecord()->email_contact;
}
function getTelegram(): ?string
{
return $this->getRecord()->telegram;
}
function getInterests(): ?string
{
return $this->getRecord()->interests;
}
function getFavoriteMusic(): ?string
{
return $this->getRecord()->fav_music;
}
function getFavoriteFilms(): ?string
{
return $this->getRecord()->fav_films;
}
function getFavoriteShows(): ?string
{
return $this->getRecord()->fav_shows;
}
function getFavoriteBooks(): ?string
{
return $this->getRecord()->fav_books;
}
function getFavoriteQuote(): ?string
{
return $this->getRecord()->fav_quote;
}
function getCity(): ?string
{
return $this->getRecord()->city;
}
function getPhysicalAddress(): ?string
{
return $this->getRecord()->address;
}
function getNotificationOffset(): int
{
return $this->getRecord()->notification_offset;
@ -332,7 +345,7 @@ class User extends RowModel
{
return (int)floor((time() - $this->getBirthday()->timestamp()) / YEAR);
}
function get2faSecret(): ?string
{
return $this->getRecord()["2fa_secret"];
@ -347,7 +360,7 @@ class User extends RowModel
{
$this->stateChanges("notification_offset", time());
}
function getLeftMenuItemStatus(string $id): bool
{
return (bool) bmask($this->getRecord()->left_menu, [
@ -364,7 +377,7 @@ class User extends RowModel
],
])->get($id);
}
function getPrivacySetting(string $id): int
{
return (int) bmask($this->getRecord()->privacy, [
@ -383,7 +396,7 @@ class User extends RowModel
],
])->get($id);
}
function getPrivacyPermission(string $permission, ?User $user = NULL): bool
{
$permStatus = $this->getPrivacySetting($permission);
@ -391,7 +404,7 @@ class User extends RowModel
return $permStatus === User::PRIVACY_EVERYONE;
else if($user->getId() === $this->getId())
return true;
switch($permStatus) {
case User::PRIVACY_ONLY_FRIENDS:
return $this->getSubscriptionStatus($user) === User::SUBSCRIPTION_MUTUAL;
@ -402,12 +415,12 @@ class User extends RowModel
return false;
}
}
function getProfileCompletenessReport(): object
{
$incompleteness = 0;
$unfilled = [];
if(!$this->getRecord()->status) {
$unfilled[] = "status";
$incompleteness += 15;
@ -428,46 +441,46 @@ class User extends RowModel
$unfilled[] = "interests";
$incompleteness += 20;
}
$total = max(100 - $incompleteness + $this->getRating(), 0);
if(ovkGetQuirk("profile.rating-bar-behaviour") === 0)
if ($total >= 100)
$percent = round(($total / 10**strlen(strval($total))) * 100, 0);
else
$percent = min($total, 100);
return (object) [
"total" => $total,
"percent" => $percent,
"unfilled" => $unfilled,
];
}
function getFriends(int $page = 1, int $limit = 6): \Traversable
{
return $this->_abstractRelationGenerator("get-friends", $page, $limit);
}
function getFriendsCount(): int
{
return $this->_abstractRelationCount("get-friends");
}
function getFollowers(int $page = 1, int $limit = 6): \Traversable
{
return $this->_abstractRelationGenerator("get-followers", $page, $limit);
}
function getFollowersCount(): int
{
return $this->_abstractRelationCount("get-followers");
}
function getSubscriptions(int $page = 1, int $limit = 6): \Traversable
{
return $this->_abstractRelationGenerator("get-subscriptions-user", $page, $limit);
}
function getSubscriptionsCount(): int
{
return $this->_abstractRelationCount("get-subscriptions-user");
@ -477,7 +490,7 @@ class User extends RowModel
{
return sizeof(DatabaseConnection::i()->getContext()->table("messages")->where(["recipient_id" => $this->getId(), "unread" => 1]));
}
function getClubs(int $page = 1, bool $admin = false): \Traversable
{
if($admin) {
@ -502,7 +515,7 @@ class User extends RowModel
}
}
}
function getClubCount(bool $admin = false): int
{
if($admin) {
@ -517,7 +530,7 @@ class User extends RowModel
return sizeof($sel);
}
}
function getPinnedClubs(): \Traversable
{
foreach($this->getRecord()->related("groups.owner")->where("owner_club_pinned", true) as $target) {
@ -558,16 +571,16 @@ class User extends RowModel
foreach($sel as $target) {
$target = (new Clubs)->get($target->event);
if(!$target) continue;
yield $target;
}
}
function getMeetingCount(): int
{
return sizeof($this->getRecord()->related("event_turnouts.user"));
}
function getGifts(int $page = 1, ?int $perPage = NULL): \Traversable
{
$gifts = $this->getRecord()->related("gift_user_relations.receiver")->order("sent DESC")->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
@ -581,7 +594,7 @@ class User extends RowModel
];
}
}
function getGiftCount(): int
{
return sizeof($this->getRecord()->related("gift_user_relations.receiver"));
@ -616,9 +629,9 @@ class User extends RowModel
function use2faBackupCode(int $code): bool
{
return (bool) $this->getRecord()->related("2fa_backup_codes.owner")->where("code", $code)->delete();
return (bool) $this->getRecord()->related("2fa_backup_codes.owner")->where("code", $code)->delete();
}
function getSubscriptionStatus(User $user): int
{
$subbed = !is_null($this->getRecord()->related("subscriptions.follower")->where([
@ -629,51 +642,51 @@ class User extends RowModel
"model" => static::class,
"follower" => $user->getId(),
])->fetch());
if($subbed && $followed) return User::SUBSCRIPTION_MUTUAL;
if($subbed) return User::SUBSCRIPTION_INCOMING;
if($followed) return User::SUBSCRIPTION_OUTGOING;
return User::SUBSCRIPTION_ABSENT;
}
function getNotificationsCount(bool $archived = false): int
{
return (new Notifications)->getNotificationCountByUser($this, $this->getNotificationOffset(), $archived);
}
function getNotifications(int $page, bool $archived = false): \Traversable
{
return (new Notifications)->getNotificationsByUser($this, $this->getNotificationOffset(), $archived, $page);
}
function getPendingPhoneVerification(): ?ActiveRow
{
return $this->getRecord()->ref("number_verification", "id");
}
function getRefLinkId(): string
{
$hash = hash_hmac("Snefru", (string) $this->getId(), CHANDLER_ROOT_CONF["security"]["secret"], true);
return dechex($this->getId()) . " " . base64_encode($hash);
}
function getNsfwTolerance(): int
{
return $this->getRecord()->nsfw_tolerance;
}
function isFemale(): bool
{
return (bool) $this->getRecord()->sex;
}
function isVerified(): bool
{
return (bool) $this->getRecord()->verified;
}
function isBanned(): bool
{
return !is_null($this->getBanReason());
@ -683,22 +696,22 @@ class User extends RowModel
{
return !is_null($this->getBanInSupportReason());
}
function isOnline(): bool
{
return time() - $this->getRecord()->online <= 300;
}
function prefersNotToSeeRating(): bool
{
return !((bool) $this->getRecord()->show_rating);
}
function hasPendingNumberChange(): bool
{
return !is_null($this->getPendingPhoneVerification());
}
function gift(User $sender, Gift $gift, ?string $comment = NULL, bool $anonymous = false): void
{
DatabaseConnection::i()->getContext()->table("gift_user_relations")->insert([
@ -710,7 +723,7 @@ class User extends RowModel
"sent" => time(),
]);
}
function ban(string $reason, bool $deleteSubscriptions = true): void
{
if($deleteSubscriptions) {
@ -723,42 +736,42 @@ class User extends RowModel
);
$subs->delete();
}
$this->setBlock_Reason($reason);
$this->save();
}
function verifyNumber(string $code): bool
{
$ver = $this->getPendingPhoneVerification();
if(!$ver) return false;
try {
if(sodium_memcmp((string) $ver->code, $code) === -1) return false;
} catch(\SodiumException $ex) {
return false;
}
$this->setPhone($ver->number);
$this->save();
DatabaseConnection::i()->getContext()
->table("number_verification")
->where("user", $this->getId())
->delete();
return true;
}
function setFirst_Name(string $firstName): void
{
$firstName = mb_convert_case($firstName, MB_CASE_TITLE);
if(!preg_match('%^[\p{Lu}\p{Lo}]\p{Mn}?(?:[\p{L&}\p{Lo}]\p{Mn}?){1,16}$%u', $firstName))
throw new InvalidUserNameException;
$this->stateChanges("first_name", $firstName);
}
function setLast_Name(string $lastName): void
{
if(!empty($lastName))
@ -767,15 +780,15 @@ class User extends RowModel
if(!preg_match('%^[\p{Lu}\p{Lo}]\p{Mn}?([\p{L&}\p{Lo}]\p{Mn}?){1,16}(\-\g<1>+)?$%u', $lastName))
throw new InvalidUserNameException;
}
$this->stateChanges("last_name", $lastName);
}
function setNsfwTolerance(int $tolerance): void
{
$this->stateChanges("nsfw_tolerance", $tolerance);
}
function setPrivacySetting(string $id, int $status): void
{
$this->stateChanges("privacy", bmask($this->changes["privacy"] ?? $this->getRecord()->privacy, [
@ -794,7 +807,7 @@ class User extends RowModel
],
])->set($id, $status)->toInteger());
}
function setLeftMenuItemStatus(string $id, bool $status): void
{
$mask = bmask($this->changes["left_menu"] ?? $this->getRecord()->left_menu, [
@ -810,10 +823,10 @@ class User extends RowModel
"poster",
],
])->set($id, (int) $status)->toInteger();
$this->stateChanges("left_menu", $mask);
}
function setShortCode(?string $code = NULL, bool $force = false): ?bool
{
if(!is_null($code)) {
@ -825,20 +838,20 @@ class User extends RowModel
return false;
if(\Chandler\MVC\Routing\Router::i()->getMatchingRoute("/$code")[0]->presenter !== "UnknownTextRouteStrategy")
return false;
$pClub = DatabaseConnection::i()->getContext()->table("groups")->where("shortcode", $code)->fetch();
if(!is_null($pClub))
return false;
}
$this->stateChanges("shortcode", $code);
return true;
}
function setPhoneWithVerification(string $phone): string
{
$code = unpack("S", openssl_random_pseudo_bytes(2))[1];
if($this->hasPendingNumberChange()) {
DatabaseConnection::i()->getContext()
->table("number_verification")
@ -849,10 +862,10 @@ class User extends RowModel
->table("number_verification")
->insert(["user" => $this->getId(), "number" => $phone, "code" => $code]);
}
return (string) $code;
}
# KABOBSQL temporary fix
# Tuesday, the 7th of January 2020 @ 22:43 <Menhera>: implementing quick fix to this problem and monitoring
# NOTICE: this is an ongoing conversation, add your comments just above this line. Thanks!
@ -860,10 +873,10 @@ class User extends RowModel
{
$this->stateChanges("shortcode", $this->getRecord()->shortcode); #fix KABOBSQL
$this->stateChanges("online", $time);
return true;
}
function adminNotify(string $message): bool
{
$admId = OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"];
@ -871,12 +884,12 @@ class User extends RowModel
return false;
else if(is_null($admin = (new Users)->get($admId)))
return false;
$cor = new Correspondence($admin, $this);
$msg = new Message;
$msg->setContent($message);
$cor->sendMessage($msg, true);
return true;
}
@ -903,7 +916,7 @@ class User extends RowModel
case 2:
return 2;
break;
default:
return 0;
break;

View file

@ -14,6 +14,8 @@ class Video extends Media
protected $tableName = "videos";
protected $fileExtension = "ogv";
protected $processingPlaceholder = "video/rendering";
protected function saveFile(string $filename, string $hash): bool
{
@ -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();
@ -53,7 +55,23 @@ class Video extends Media
usleep(200100);
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();

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -126,4 +126,11 @@ final class AboutPresenter extends OpenVKPresenter
header("Location: https://github.com/openvk/openvk#readme");
exit;
}
function renderDev(): void
{
header("HTTP/1.1 302 Found");
header("Location: https://docs.openvk.su/");
exit;
}
}

View file

@ -17,20 +17,23 @@ final class BlobPresenter extends OpenVKPresenter
function renderFile(/*string*/ $dir, string $name, string $format)
{
$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 {
if(isset($_SERVER["HTTP_IF_NONE_MATCH"]))
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;
}
}
}

View file

@ -72,6 +72,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);
@ -100,6 +102,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());

View file

@ -481,6 +481,22 @@ 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("/", static::REDIRECT_TEMPORARY_PRESISTENT);
}
function renderCoinsTransfer(): void
{
$this->assertUserLoggedIn();

View file

@ -303,8 +303,6 @@ final class WallPresenter extends OpenVKPresenter
function renderPost(int $wall, int $post_id): void
{
$this->assertUserLoggedIn();
$post = $this->posts->getPostById($wall, $post_id);
if(!$post || $post->isDeleted())
$this->notFound();

View file

@ -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">

View file

@ -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">

View file

@ -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>

View file

@ -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}
@ -169,9 +169,9 @@
</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="/support/tickets" class="link" n:if="$canAccessHelpdesk">Helpdesk
@ -191,6 +191,14 @@
<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} &#xbb;</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;" />
</a>
@ -256,7 +264,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>
@ -330,6 +338,7 @@
{/ifset}
</body>
</html>
{/if}
{if isset($parentModule) && substr($parentModule, 0, 21) !== 'libchandler:absolute.'}
<!-- INCLUDING TEMPLATE FROM PARENTMODULE: {$parentModule} -->

View file

@ -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">

View file

@ -9,7 +9,7 @@
<table width="100%" cellspacing="0" cellpadding="0">
<tbody>
<tr valign="top">
<td width="250" {if sizeof($admins) > 0}style="padding-right: 10px;"{/if}>
<td width="250"{if sizeof($admins) > 0} style="padding-right: 10px;"{/if}>
<h4>{_statistics}</h4>
<div style="margin-top: 5px;">
{_on_this_instance_are}
@ -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>
<ol>
<li n:foreach="$popularClubs as $entry" style="margin-top: 5px;">
<a href="{$entry->club->getURL()}">{$entry->club->getName()}</a>
<div>
{tr("participants", $entry->subscriptions)}
</div>
</li>
</ol>
{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 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", $club->subscriptions)}
</div>
</li>
</ol>
</td>
</tr>
</tbody>
</table>
{/if}
<h4>{_rules}</h4>

View file

@ -3,7 +3,7 @@
<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>
@ -131,7 +131,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>

View file

@ -11,9 +11,9 @@
{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}
@ -134,7 +134,7 @@
<!-- 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">
@ -177,7 +177,7 @@
</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}">
⭁ туда

View file

@ -1,5 +1,5 @@
{extends "@layout.xml"}
{var search = true}
{var $search = true}
{block title}
Группы
@ -12,8 +12,8 @@
{block searchTitle}Поиск бутылок{/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>
@ -39,7 +39,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">
@ -61,7 +61,7 @@
</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}">
⭁ туда

View file

@ -45,7 +45,7 @@
{/if}
<div align="right">
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($categories)) < $count}
{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>

View file

@ -71,7 +71,7 @@
{/if}
<div align="right">
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($gifts)) < $count}
{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>

View file

@ -1,5 +1,5 @@
{extends "@layout.xml"}
{var search = true}
{var $search = true}
{block title}
Пользователи
@ -12,8 +12,8 @@
{block searchTitle}Поиск пиздюков{/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>
@ -60,7 +60,7 @@
</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}">
⭁ туда

View file

@ -50,7 +50,7 @@
<br/>
<div align="right">
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($vouchers)) < $count}
{var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($vouchers)) < $count}
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">
⭁ туда
</a>

View file

@ -47,7 +47,7 @@
<span>{_"gender"}: </span>
</td>
<td>
{var femalePreferred = OPENVK_ROOT_CONF["openvk"]["preferences"]["femaleGenderPriority"]}
{var $femalePreferred = OPENVK_ROOT_CONF["openvk"]["preferences"]["femaleGenderPriority"]}
<select name="sex" required>
<option n:attr="selected => !$femalePreferred" value="male">{_"male"}</option>
<option n:attr="selected => $femalePreferred" value="female">{_"female"}</option>

View file

@ -92,7 +92,7 @@
<span class="nobold">{_group_administrators_list}: </span>
</td>
<td>
{var areAllAdminsHidden = $club->getManagersCount(true) == 0}
{var $areAllAdminsHidden = $club->getManagersCount(true) == 0}
<input type="radio" name="administrators_list_display" value="0" n:attr="checked => $club->getAdministratorsListDisplay() == 0, disabled => $areAllAdminsHidden" /> {_group_display_only_creator}<br>
<input type="radio" name="administrators_list_display" value="1" n:attr="checked => $club->getAdministratorsListDisplay() == 1, disabled => $areAllAdminsHidden" /> {_group_display_all_administrators}<br>
<input type="radio" name="administrators_list_display" value="2" n:attr="checked => $club->getAdministratorsListDisplay() == 2" /> {_group_dont_display_administrators_list}<br>

View file

@ -1,9 +1,9 @@
{extends "../@listView.xml"}
{var $Manager = openvk\Web\Models\Entities\Manager::class}
{var iterator = $onlyShowManagers ? $managers : $followers}
{var count = $paginatorConf->count}
{var page = $paginatorConf->page}
{var perPage = 6}
{var $iterator = $onlyShowManagers ? $managers : $followers}
{var $count = $paginatorConf->count}
{var $page = $paginatorConf->page}
{var $perPage = 6}
{block title}{_followers} {$club->getCanonicalName()}{/block}
@ -49,8 +49,8 @@
{/block}
{block description}
{var user = $x instanceof $Manager ? $x->getUser() : $x}
{var manager = $x instanceof $Manager ? $x : $club->getManager($user, !$club->canBeModifiedBy($thisUser))}
{var $user = $x instanceof $Manager ? $x->getUser() : $x}
{var $manager = $x instanceof $Manager ? $x : $club->getManager($user, !$club->canBeModifiedBy($thisUser))}
<table>
<tbody>
<tr>
@ -106,8 +106,8 @@
{/block}
{block actions}
{var user = $x instanceof $Manager ? $x->getUser() : $x}
{var manager = $x instanceof $Manager ? $x : $club->getManager($user, !$club->canBeModifiedBy($thisUser))}
{var $user = $x instanceof $Manager ? $x->getUser() : $x}
{var $manager = $x instanceof $Manager ? $x : $club->getManager($user, !$club->canBeModifiedBy($thisUser))}
{if $club->canBeModifiedBy($thisUser ?? NULL)}
<a class="profile_link" href="/club{$club->getId()}/setAdmin?user={$user->getId()}&hash={rawurlencode($csrfToken)}" n:if="$club->getOwner()->getId() !== $user->getId()">
{if $manager}

View file

@ -14,6 +14,8 @@
{block content}
<div class="left_big_block">
<div n:if="!is_null($alert = $club->getAlert())" class="group-alert">{strpos($alert, "@") === 0 ? tr(substr($alert, 1)) : $alert}</div>
<div class="content_title_expanded" onclick="hidePanel(this);">
{_"information"}
</div>
@ -41,7 +43,7 @@
</table>
</div>
<div n:if="$club->getFollowersCount() > 0">
{var followersCount = $club->getFollowersCount()}
{var $followersCount = $club->getFollowersCount()}
<div class="content_title_expanded" onclick="hidePanel(this, {$followersCount});">
{_participants}
@ -91,8 +93,8 @@
{presenter "openvk!Wall->wallEmbedded", -$club->getId()}
</div>
<div class="right_small_block">
{var avatarPhoto = $club->getAvatarPhoto()}
{var avatarLink = ((is_null($avatarPhoto) ? FALSE : $avatarPhoto->isAnonymous()) ? "/photo" . ("s/" . base_convert((string) $avatarPhoto->getId(), 10, 32)) : $club->getAvatarLink())}
{var $avatarPhoto = $club->getAvatarPhoto()}
{var $avatarLink = ((is_null($avatarPhoto) ? FALSE : $avatarPhoto->isAnonymous()) ? "/photo" . ("s/" . base_convert((string) $avatarPhoto->getId(), 10, 32)) : $club->getAvatarLink())}
<a href="{$avatarLink|nocheck}">
<img src="{$club->getAvatarUrl('normal')}" style="width: 100%; image-rendering: -webkit-optimize-contrast;" />
</a>
@ -132,7 +134,7 @@
{_"creator"}
</div>
<div class="avatar-list-item" style="padding: 8px;">
{var author = $club->getOwner()}
{var $author = $club->getOwner()}
<div class="avatar">
<a href="{$author->getURL()}">
<img class="ava" src="{$author->getAvatarUrl()}" />
@ -149,7 +151,7 @@
</div>
</div>
<div n:if="$club->getAdministratorsListDisplay() == 1">
{var managersCount = $club->getManagersCount(true)}
{var $managersCount = $club->getManagersCount(true)}
<div class="content_title_expanded" onclick="hidePanel(this, {$managersCount});">
{_"administrators"}
@ -163,7 +165,7 @@
</div>
<div class="avatar-list">
<div class="avatar-list-item" n:if="!$club->isOwnerHidden()">
{var author = $club->getOwner()}
{var $author = $club->getOwner()}
<div class="avatar">
<a href="{$author->getURL()}">
<img class="ava" src="{$author->getAvatarUrl()}" />
@ -175,7 +177,7 @@
</div>
</div>
<div class="avatar-list-item" n:foreach="$club->getManagers(1, true) as $manager">
{var user = $manager->getUser()}
{var $user = $manager->getUser()}
<div class="avatar">
<a href="{$user->getURL()}">
<img height="32" class="ava" src="{$user->getAvatarUrl()}" />
@ -203,7 +205,7 @@
<div style="padding: 5px;">
<div class="ovk-album" style="display: inline-block;" n:foreach="$albums as $album">
<div style="text-align: center;float: left;height: 54pt;width: 100px;">
{var cover = $album->getCoverPhoto()}
{var $cover = $album->getCoverPhoto()}
<img
src="{is_null($cover)?'/assets/packages/static/openvk/img/camera_200.png':$cover->getURL()}"

View file

@ -6,7 +6,7 @@
<a href="/im">{_my_messages}</a> »
<a href="{$correspondent->getURL()}">{$correspondent->getCanonicalName()}</a>
<div n:if="($online = $correspondent->getOnline()->timestamp()) + 2505600 > time()" style="float: right;">
{var diff = date_diff(date_create(), date_create('@' . $online))}
{var $diff = date_diff(date_create(), date_create('@' . $online))}
{if 5 >= $diff->i}
<span><b>{_online}</b></span>
{else}

View file

@ -21,8 +21,8 @@
<div n:foreach="$corresps as $coresp"
class="crp-entry"
onmousedown="window.location.href = {$coresp->getURL()};" >
{var recipient = $coresp->getCorrespondents()[1]}
{var lastMsg = $coresp->getPreviewMessage()}
{var $recipient = $coresp->getCorrespondents()[1]}
{var $lastMsg = $coresp->getPreviewMessage()}
<div class="crp-entry--image">
<img src="{$recipient->getAvatarURL('miniscule')}"
@ -33,7 +33,7 @@
<span>{$lastMsg->getSendTime()->format("%e %B %G" . tr("time_at_sp") . "%X")}</span>
</div>
<div n:class="crp-entry--message, $lastMsg->getUnreadState() ? unread">
{var _author = $lastMsg->getSender()}
{var $_author = $lastMsg->getSender()}
<div class="crp-entry--message---av" n:if="$_author->getId() === $thisUser->getId()">
<img src="{$_author->getAvatarURL('miniscule')}"

View file

@ -3,7 +3,7 @@
{block title}{_edit_note}{/block}
{block header}
{var author = $note->getOwner()}
{var $author = $note->getOwner()}
<a href="{$author->getURL()}">{$author->getCanonicalName()}</a>
»
<a href="/notes{$author->getId()}">{_notes}</a>

View file

@ -1,6 +1,6 @@
{extends "../@listView.xml"}
{var iterator = iterator_to_array($notes)}
{var page = $paginatorConf->page}
{var $iterator = iterator_to_array($notes)}
{var $page = $paginatorConf->page}
{block title}{_notes}{/block}
@ -62,7 +62,7 @@
<div class="container_gray" style="background: white; border-top: none;">
{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 n:foreach="$data as $dat">

View file

@ -3,7 +3,7 @@
{block title}{$note->getName()}{/block}
{block header}
{var author = $note->getOwner()}
{var $author = $note->getOwner()}
<a href="{$author->getURL()}">{$author->getCanonicalName()}</a>
»
<a href="/notes{$author->getId()}">{_notes}</a>
@ -12,7 +12,7 @@
{/block}
{block content}
{var author = $note->getOwner()}
{var $author = $note->getOwner()}
<style>
#userContent img {
max-width: 245pt;

View file

@ -1,5 +1,5 @@
{extends "../@listView.xml"}
{var sorting = false}
{var $sorting = false}
{block title}
{_feedback}

View file

@ -3,7 +3,7 @@
{block title}Альбом {$album->getName()}{/block}
{block header}
{var isClub = ($album->getOwner() instanceof openvk\Web\Models\Entities\Club)}
{var $isClub = ($album->getOwner() instanceof openvk\Web\Models\Entities\Club)}
<a href="{$album->getOwner()->getURL()}">
{$album->getOwner()->getCanonicalName()}

View file

@ -1,6 +1,6 @@
{extends "../@listView.xml"}
{var iterator = iterator_to_array($albums)}
{var page = $paginatorConf->page}
{var $iterator = iterator_to_array($albums)}
{var $page = $paginatorConf->page}
{block title}{_"albums"} {$owner->getCanonicalName()}{/block}
@ -26,7 +26,7 @@
<span n:if="$canEdit" style="float: right;">
&nbsp;|&nbsp;
{var isClub = ($owner instanceof \openvk\Web\Models\Entities\Club)}
{var $isClub = ($owner instanceof \openvk\Web\Models\Entities\Club)}
<a href="/albums/create{$isClub ? '?gpid=' . $owner->getId() : ''}">{_create_album}</a>
</span>
</div>
@ -44,8 +44,8 @@
{/block}
{block preview}
{var cover = $x->getCoverPhoto()}
{var preview = is_null($cover) ? "/assets/packages/static/openvk/img/camera_200.png" : $cover->getURLBySizeId("normal")}
{var $cover = $x->getCoverPhoto()}
{var $preview = is_null($cover) ? "/assets/packages/static/openvk/img/camera_200.png" : $cover->getURLBySizeId("normal")}
<a href="/album{$x->getPrettyId()}">
<img src="{$preview}" alt="{$x->getName()}" style="height: 130px; width: 170px; object-fit: cover" />

View file

@ -70,7 +70,7 @@
{if $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
<a href="{$comment->getUser()->getURL()}">
<span class="nobold">
{var lastName = $comment->getUser()->getLastName()}
{var $lastName = $comment->getUser()->getLastName()}
{if empty(trim($lastName))}
({$comment->getUser()->getFirstName()})
{else}

View file

@ -6,9 +6,9 @@
{/block}
{block content}
{var isMain = $mode === 'faq'}
{var isNew = $mode === 'new'}
{var isList = $mode === 'list'}
{var $isMain = $mode === 'faq'}
{var $isNew = $mode === 'new'}
{var $isList = $mode === 'list'}
{if $thisUser}
<div class="tabs">

View file

@ -37,7 +37,7 @@
{/block}
{block description}
{var author = $x->getUser()}
{var $author = $x->getUser()}
{ovk_proc_strtr($x->getContext(), 50)}<br/>
<span class="nobold">{_author}: </span> <a href="{$author->getURL()}">{$author->getCanonicalName()}</a>

View file

@ -109,7 +109,7 @@
{if $comment->getUType() === 1}
<div class="post-menu">
{var isLikedByUser = $comment->isLikedByUser()}
{var $isLikedByUser = $comment->isLikedByUser()}
<strong id="markText-{$comment->getId()}">
{if !is_null($isLikedByUser)}
{if $comment->isLikedByUser()}

View file

@ -1,6 +1,6 @@
{extends "../@listView.xml"}
{var iterator = iterator_to_array($topics)}
{var page = $paginatorConf->page}
{var $iterator = iterator_to_array($topics)}
{var $page = $paginatorConf->page}
{block title}{_discussions} {$club->getCanonicalName()}{/block}
@ -46,7 +46,7 @@
<div style="float: left;">
{tr("messages", $x->getCommentsCount())}
</div>
{var lastComment = $x->getLastComment()}
{var $lastComment = $x->getLastComment()}
<div n:if="$lastComment" class="avatar-list-item" style="float: right;">
<div class="avatar">
<a href="{$lastComment->getOwner()->getURL()}">

View file

@ -7,10 +7,10 @@
{block content}
{var isMain = $mode === 'main'}
{var isContacts = $mode === 'contacts'}
{var isInterests = $mode === 'interests'}
{var isAvatar = $mode === 'avatar'}
{var $isMain = $mode === 'main'}
{var $isContacts = $mode === 'contacts'}
{var $isInterests = $mode === 'interests'}
{var $isAvatar = $mode === 'avatar'}
<div n:if="$user->hasPendingNumberChange()" class="msg">
<b>Подтверждение номера телефона</b><br/>
Введите код для подтверждения смены номера: <a href="/edit/verify_phone">ввести код</a>.

View file

@ -1,17 +1,17 @@
{extends "../@listView.xml"}
{var perPage = 6} {* Why 6? Check User::_abstractRelationGenerator *}
{var $perPage = 6} {* Why 6? Check User::_abstractRelationGenerator *}
{var act = $_GET["act"] ?? "friends"}
{var $act = $_GET["act"] ?? "friends"}
{if $act == "incoming"}
{var iterator = iterator_to_array($user->getFollowers($page))}
{var count = $user->getFollowersCount()}
{var $iterator = iterator_to_array($user->getFollowers($page))}
{var $count = $user->getFollowersCount()}
{elseif $act == "outcoming"}
{var iterator = iterator_to_array($user->getSubscriptions($page))}
{var count = $user->getSubscriptionsCount()}
{var $iterator = iterator_to_array($user->getSubscriptions($page))}
{var $count = $user->getSubscriptionsCount()}
{else}
{var iterator = iterator_to_array($user->getFriends($page))}
{var count = $user->getFriendsCount()}
{var $iterator = iterator_to_array($user->getFriends($page))}
{var $count = $user->getFriendsCount()}
{/if}
{block title}
@ -113,7 +113,7 @@
{block actions}
{if $x->getId() !== $thisUser->getId()}
{var subStatus = $x->getSubscriptionStatus($thisUser)}
{var $subStatus = $x->getSubscriptionStatus($thisUser)}
{if $subStatus === 0}
<form action="/setSub/user" method="post" class="profile_link_form">
<input type="hidden" name="act" value="add" />

View file

@ -1,6 +1,6 @@
{extends "../@listView.xml"}
{var iterator = $user->getClubs($page, $admin)}
{var count = $user->getClubCount($admin)}
{var $iterator = $user->getClubs($page, $admin)}
{var $count = $user->getClubCount($admin)}
{block title}
{_groups}
@ -73,7 +73,7 @@
{/block}
{block actions}
{var clubPinned = $thisUser->isClubPinned($x)}
{var $clubPinned = $thisUser->isClubPinned($x)}
{if $x->canBeModifiedBy($thisUser ?? NULL)}
<div class="navigation" style="width: 140px;">
<a class="link" href="{$x->getURL()}">

View file

@ -7,11 +7,11 @@
{block content}
{var isMain = $mode === 'main'}
{var isPrivacy = $mode === 'privacy'}
{var isFinance = $mode === 'finance'}
{var isFinanceTU = $mode === 'finance.top-up'}
{var isInterface = $mode === 'interface'}
{var $isMain = $mode === 'main'}
{var $isPrivacy = $mode === 'privacy'}
{var $isFinance = $mode === 'finance'}
{var $isFinanceTU = $mode === 'finance.top-up'}
{var $isInterface = $mode === 'interface'}
<div class="tabs">
<div n:attr="id => ($isMain ? 'activetabs' : 'ki')" class="tab">

View file

@ -107,7 +107,7 @@
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && $user->getGiftCount() == 0" href="/gifts?act=pick&user={$user->getId()}" class="profile_link">{_send_gift}</a>
<a n:if="$user->getPrivacyPermission('messages.write', $thisUser)" href="/im?sel={$user->getId()}" class="profile_link">{_"send_message"}</a>
{var subStatus = $user->getSubscriptionStatus($thisUser)}
{var $subStatus = $user->getSubscriptionStatus($thisUser)}
{if $subStatus === 0}
<form action="/setSub/user" method="post" class="profile_link_form">
<input type="hidden" name="act" value="add" />
@ -141,7 +141,7 @@
<a n:if="$user->getFollowersCount() > 0" href="/friends{$user->getId()}?act=incoming" class="profile_link">{tr("followers", $user->getFollowersCount())}</a>
</div>
<div n:if="isset($thisUser) && !$thisUser->prefersNotToSeeRating()" class="profile-hints">
{var completeness = $user->getProfileCompletenessReport()}
{var $completeness = $user->getProfileCompletenessReport()}
<div n:class="completeness-gauge, $completeness->total >= 100 ? completeness-gauge-gold">
<div style="width: {$completeness->percent}%"></div>
@ -174,7 +174,7 @@
</div>
<br />
<div n:if="$user->getFriendsCount() > 0 && $user->getPrivacyPermission('friends.read', $thisUser ?? NULL)">
{var friendCount = $user->getFriendsCount()}
{var $friendCount = $user->getFriendsCount()}
<div class="content_title_expanded" onclick="hidePanel(this, {$friendCount});">
{_"friends"}
@ -215,7 +215,7 @@
<div style="padding: 5px;">
<div class="ovk-album" style="display: inline-block;" n:foreach="$albums as $album">
<div style="text-align: center;float: left;height: 54pt;width: 100px;">
{var cover = $album->getCoverPhoto()}
{var $cover = $album->getCoverPhoto()}
<img
src="{is_null($cover)?'/assets/packages/static/openvk/img/camera_200.png':$cover->getURLBySizeId('small')}"
@ -284,7 +284,7 @@
</div>
</div>
<div n:if="$user->getClubCount() > 0 && $user->getPrivacyPermission('groups.read', $thisUser ?? NULL)">
{var clubsCount = $user->getClubCount()}
{var $clubsCount = $user->getClubCount()}
<div class="content_title_expanded" onclick="hidePanel(this, {$clubsCount})">
{_"groups"}
</div>
@ -303,7 +303,7 @@
</div>
</div>
<div n:if="$user->getMeetingCount() > 0 && $user->getPrivacyPermission('groups.read', $thisUser ?? NULL)">
{var meetingCount = $user->getMeetingCount()}
{var $meetingCount = $user->getMeetingCount()}
<div class="content_title_expanded" onclick="hidePanel(this, {$meetingCount})">
{_meetings}
</div>
@ -327,7 +327,7 @@
<div class="right_big_block">
<div class="page_info">
<div n:if="!is_null($alert = $user->getAlert())" class="user-alert">{strpos($alert, "@") === 0 ? tr(substr($alert, 1)) : $alert}</div>
{var thatIsThisUser = isset($thisUser) && $user->getId() == $thisUser->getId()}
{var $thatIsThisUser = isset($thisUser) && $user->getId() == $thisUser->getId()}
<div n:if="$thatIsThisUser" class="page_status_popup" id="status_editor" style="display: none;">
<form name="status_popup_form" onsubmit="changeStatus(); return false;">
<div style="margin-bottom: 10px;">
@ -495,7 +495,7 @@
</div>
<div class="content_list long">
<div class="cl_element" style="width: 25%;" n:foreach="$user->getGifts(1, 4) as $giftDescriptor">
{var hideInfo = !is_null($thisUser) ? ($giftDescriptor->anon ? $thisUser->getId() !== $user->getId() : false) : false}
{var $hideInfo = !is_null($thisUser) ? ($giftDescriptor->anon ? $thisUser->getId() !== $user->getId() : false) : false}
<div class="cl_avatar">
<a href="{$hideInfo ? 'javascript:false' : $giftDescriptor->sender->getURL()}">
<img style="width: 70px; max-height: 70px;"

View file

@ -4,82 +4,86 @@
{tr("user_banned", htmlentities($user->getFirstName()))|noescape}<br/>
{_"user_banned_comment"} <b>{$user->getBanReason()}</b>.
</p>
<p n:if="isset($thisUser) && $thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL) || $thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)">
<br />
<a n:if="$thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL)" href="javascript:unbanUser()" class="button">{_unban_user_action}</a>
<a n:if="$thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)" href="javascript:toggleBanInSupport()" class="button">
{if $user->isBannedInSupport()}
{_unban_in_support_user_action}
{else}
{_ban_in_support_user_action}
{/if}
</a>
</p>
{if isset($thisUser)}
<p n:if="$thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL) || $thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)">
<br />
<a n:if="$thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL)" href="javascript:unbanUser()" class="button">{_unban_user_action}</a>
<a n:if="$thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)" href="javascript:toggleBanInSupport()" class="button">
{if $user->isBannedInSupport()}
{_unban_in_support_user_action}
{else}
{_ban_in_support_user_action}
{/if}
</a>
</p>
{/if}
</center>
<script n:if="isset($thisUser) && $thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL)">
function unbanUser() {
uUnbanMsgTxt = "Вы собираетесь разбанить пользователя " + {$user->getCanonicalName()} + ".";
uUnbanMsgTxt += "<br/>Сейчас он заблокирован по причине: <strong>" + {$user->getBanReason()} + "</strong>.";
MessageBox("Разбанить " + {$user->getFirstName()}, uUnbanMsgTxt, ["Подтвердить", "Отмена"], [
(function() {
xhr = new XMLHttpRequest();
xhr.open("GET", "/admin/unban/" + {$user->getId()} + "?hash=" + {rawurlencode($csrfToken)}, true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("success") === -1)
MessageBox("Ошибка", "Не удалось разблокировать пользователя...", ["OK"], [Function.noop]);
else
MessageBox("Операция успешна", "Пользователь разблокирован", ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
</script>
<script n:if="isset($thisUser) && $thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)">
{if $user->isBannedInSupport()}
function toggleBanInSupport() {
uBanMsgTxt = "Вы собираетесь разблокировать в поддержке пользователя " + {$user->getCanonicalName()} + ".";
uBanMsgTxt += "<br/>Сейчас он заблокирован по причине <strong>" + {$user->getBanInSupportReason()} + "</strong>.";
MessageBox("Разблокировать в поддержке " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [
{if isset($thisUser)}
<script n:if="$thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL)">
function unbanUser() {
uUnbanMsgTxt = "Вы собираетесь разбанить пользователя " + {$user->getCanonicalName()} + ".";
uUnbanMsgTxt += "<br/>Сейчас он заблокирован по причине: <strong>" + {$user->getBanReason()} + "</strong>.";
MessageBox("Разбанить " + {$user->getFirstName()}, uUnbanMsgTxt, ["Подтвердить", "Отмена"], [
(function() {
xhr = new XMLHttpRequest();
xhr.open("GET", "/admin/support/unban/" + {$user->getId()} + "?hash=" + {rawurlencode($csrfToken)}, true);
xhr.open("GET", "/admin/unban/" + {$user->getId()} + "?hash=" + {rawurlencode($csrfToken)}, true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("success") === -1)
MessageBox("Ошибка", "Не удалось разблокировать пользователя в поддержке...", ["OK"], [Function.noop]);
MessageBox("Ошибка", "Не удалось разблокировать пользователя...", ["OK"], [Function.noop]);
else
MessageBox("Операция успешна", "Пользователь разблокирован в поддержке", ["OK"], [Function.noop]);
MessageBox("Операция успешна", "Пользователь разблокирован", ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
{else}
function toggleBanInSupport() {
uBanMsgTxt = "Вы собираетесь заблокировать в поддержке пользователя " + {$user->getCanonicalName()} + ".";
uBanMsgTxt += "<br/><br/><b>Причина бана</b>: <input type='text' id='uBanMsgInput' placeholder='придумайте что-нибудь крутое' />";
</script>
MessageBox("Заблокировать в поддержке " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [
(function() {
res = document.querySelector("#uBanMsgInput").value;
xhr = new XMLHttpRequest();
xhr.open("GET", "/admin/support/ban/" + {$user->getId()} + "?reason=" + res + "&hash=" + {rawurlencode($csrfToken)}, true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("success") === -1)
MessageBox("Ошибка", "Не удалось заблокировать пользователя в поддержке...", ["OK"], [Function.noop]);
else
MessageBox("Операция успешна", "Пользователь заблокирован в поддержке", ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
{/if}
</script>
<script n:if="$thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)">
{if $user->isBannedInSupport()}
function toggleBanInSupport() {
uBanMsgTxt = "Вы собираетесь разблокировать в поддержке пользователя " + {$user->getCanonicalName()} + ".";
uBanMsgTxt += "<br/>Сейчас он заблокирован по причине <strong>" + {$user->getBanInSupportReason()} + "</strong>.";
MessageBox("Разблокировать в поддержке " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [
(function() {
xhr = new XMLHttpRequest();
xhr.open("GET", "/admin/support/unban/" + {$user->getId()} + "?hash=" + {rawurlencode($csrfToken)}, true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("success") === -1)
MessageBox("Ошибка", "Не удалось разблокировать пользователя в поддержке...", ["OK"], [Function.noop]);
else
MessageBox("Операция успешна", "Пользователь разблокирован в поддержке", ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
{else}
function toggleBanInSupport() {
uBanMsgTxt = "Вы собираетесь заблокировать в поддержке пользователя " + {$user->getCanonicalName()} + ".";
uBanMsgTxt += "<br/><br/><b>Причина бана</b>: <input type='text' id='uBanMsgInput' placeholder='придумайте что-нибудь крутое' />";
MessageBox("Заблокировать в поддержке " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [
(function() {
res = document.querySelector("#uBanMsgInput").value;
xhr = new XMLHttpRequest();
xhr.open("GET", "/admin/support/ban/" + {$user->getId()} + "?reason=" + res + "&hash=" + {rawurlencode($csrfToken)}, true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("success") === -1)
MessageBox("Ошибка", "Не удалось заблокировать пользователя в поддержке...", ["OK"], [Function.noop]);
else
MessageBox("Операция успешна", "Пользователь заблокирован в поддержке", ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
{/if}
</script>
{/if}

View file

@ -1,7 +1,7 @@
{extends "../@listView.xml"}
{var iterator = $videos}
{var count = $paginatorConf->count}
{var page = $paginatorConf->page}
{var $iterator = $videos}
{var $count = $paginatorConf->count}
{var $page = $paginatorConf->page}
{block title}{_"videos"} {$user->getCanonicalName()}{/block}

View file

@ -15,7 +15,7 @@
{if $video->getType() === 0}
<video width="610" src="{$video->getURL()}" controls></video>
{else}
{var driver = $video->getVideoDriver()}
{var $driver = $video->getVideoDriver()}
{if !$driver}
Эта видеозапись не поддерживается в вашей версии OpenVK.
{else}

View file

@ -27,9 +27,9 @@
<div style="float: left; min-height: 100px; width: 32%;">
<h4>{_actions}</h4>
{if isset($thisUser)}
{var canDelete = $post->canBeDeletedBy($thisUser)}
{var $canDelete = $post->canBeDeletedBy($thisUser)}
{if $thisUser->getId() != $post->getOwner()->getId()}
{var canReport = true}
{var $canReport = true}
{/if}
{/if}

View file

@ -1,6 +1,6 @@
{if $attachment instanceof \openvk\Web\Models\Entities\Photo}
{if !$attachment->isDeleted()}
{var link = "/photo" . ($attachment->isAnonymous() ? ("s/" . base_convert((string) $attachment->getId(), 10, 32)) : $attachment->getPrettyId())}
{var $link = "/photo" . ($attachment->isAnonymous() ? ("s/" . base_convert((string) $attachment->getId(), 10, 32)) : $attachment->getPrettyId())}
<a href="{$link}">
<img class="media" src="{$attachment->getURLBySizeId('normal')}" alt="{$attachment->getDescription()}" />
</a>

View file

@ -1,6 +1,6 @@
{var author = $comment->getOwner()}
{var $author = $comment->getOwner()}
{var $Club = openvk\Web\Models\Entities\Club::class}
{var postId = $comment->getTarget() instanceof \openvk\Web\Models\Entities\Post ? $comment->getTarget()->getId() : NULL}
{var $postId = $comment->getTarget() instanceof \openvk\Web\Models\Entities\Post ? $comment->getTarget()->getId() : NULL}
<a name="cid={$comment->getId()}"></a>
<table border="0" style="font-size: 11px;" class="post comment" id="_comment{$comment->getId()}" data-comment-id="{$comment->getId()}" data-owner-id="{$author->getId()}" data-from-group="{$comment->getOwner() instanceof $Club}" n:attr="data-post-id => $postId">

View file

@ -1,8 +1,8 @@
<h4 n:if="$showTitle ?? true">{_comments} ({$count})</h4>
<div n:ifset="$thisUser">
{var commentsURL = "/al_comments/create/$model/" . $parent->getId()}
{var club = $parent instanceof \openvk\Web\Models\Entities\Post && $parent->getTargetWall() < 0 ? (new openvk\Web\Models\Repositories\Clubs)->get(abs($parent->getTargetWall())) : $club}
{var $commentsURL = "/al_comments/create/$model/" . $parent->getId()}
{var $club = $parent instanceof \openvk\Web\Models\Entities\Post && $parent->getTargetWall() < 0 ? (new openvk\Web\Models\Repositories\Clubs)->get(abs($parent->getTargetWall())) : $club}
{if !$readOnly}
{include "textArea.xml", route => $commentsURL, postOpts => false, graffiti => (bool) ovkGetQuirk("comments.allow-graffiti"), club => $club}
{/if}
@ -16,17 +16,6 @@
{include "paginator.xml", conf => (object) ["page" => $page, "count" => $count, "amount" => sizeof($comments), "perPage" => 10]}
</div>
{else}
<!-- {if $model === "photos"}
<p>Будьте первым, кто оставит комментарий к этой фотографии</p>
{elseif $model === "posts"}
<p>Будьте первым, кто оставит комментарий к этой записи</p>
{elseif $model === "notes"}
<p>Будьте первым, кто оставит комментарий к этой заметке</p>
{elseif $model === "videos"}
<p>Будьте первым, кто оставит комментарий к этой видеозаписи</p>
{else}
<p>Будьте первым кто оставит комментарий к этой дичи!</p>
{/if} -->
{_comments_tip}
{/if}

View file

@ -1,5 +1,5 @@
{var post = $notification->getModel(0)}
{var user = $notification->getModel(1)}
{var $post = $notification->getModel(0)}
{var $user = $notification->getModel(1)}
<a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a>
{$notification->getDateTime()} {_nt_liked_yours}

View file

@ -1,5 +1,5 @@
{var post = $notification->getModel(0)}
{var user = $notification->getModel(1)}
{var $post = $notification->getModel(0)}
{var $user = $notification->getModel(1)}
<a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a>
{$notification->getDateTime()} {_nt_shared_yours}

View file

@ -1,4 +1,4 @@
{var user = $notification->getModel(1)}
{var $user = $notification->getModel(1)}
<a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a>
{$notification->getDateTime()} {_nt_commented_yours} {include under}: "{$notification->getData()}".

View file

@ -1,5 +1,5 @@
{extends "@default.xml"}
{var post = $notification->getModel(0)}
{var $post = $notification->getModel(0)}
{block under}
{_nt_yours_feminitive_adjective} <a href="/note{$post->getPrettyId()}">{_nt_note_instrumental}</a>

View file

@ -1,5 +1,5 @@
{extends "@default.xml"}
{var post = $notification->getModel(0)}
{var $post = $notification->getModel(0)}
{block under}
{_nt_yours_feminitive_adjective} <a href="/photo{$post->getPrettyId()}">{_nt_photo_instrumental}</a>

View file

@ -1,5 +1,5 @@
{extends "@default.xml"}
{var post = $notification->getModel(0)}
{var $post = $notification->getModel(0)}
{block under}
{_nt_yours_adjective} <a href="/wall{$post->getPrettyId()}">{_nt_post_instrumental}</a> {_nt_from} {$post->getPublicationTime()}

View file

@ -1,5 +1,5 @@
{extends "@default.xml"}
{var post = $notification->getModel(0)}
{var $post = $notification->getModel(0)}
{block under}
{_nt_yours_adjective} <a href="/video{$post->getPrettyId()}">{_video}</a>

View file

@ -1,5 +1,5 @@
{extends "@default.xml"}
{var post = $notification->getModel(0)}
{var $post = $notification->getModel(0)}
{block under}
{_nt_yours_adjective} <a href="/topic{$post->getPrettyId()}">{_nt_topic_instrumental}</a>

View file

@ -1,5 +1,5 @@
{var post = $notification->getModel(0)}
{var user = $notification->getModel(1)}
{var $post = $notification->getModel(0)}
{var $user = $notification->getModel(1)}
<a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a>
{$notification->getDateTime()} {_nt_written_on_your_wall}

View file

@ -1,5 +1,5 @@
{var club = $notification->getModel(0)}
{var user = $notification->getModel(1)}
{var $club = $notification->getModel(0)}
{var $user = $notification->getModel(1)}
<a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a>
{_nt_made_you_admin}

View file

@ -1,4 +1,4 @@
{var gift = $notification->getModel(0)}
{var sender = $notification->getModel(1)}
{var $gift = $notification->getModel(0)}
{var $sender = $notification->getModel(1)}
<a href="{$sender->getURL()}"><b>{$sender->getCanonicalName()}</b></a> отправил вам {$notification->getDateTime()} подарок.

View file

@ -1,6 +1,6 @@
{var sender = $notification->getModel(1)}
{var value = (int) explode(" ", $notification->getData(), 2)[0]}
{var message = explode(" ", $notification->getData(), 2)[1]}
{var $sender = $notification->getModel(1)}
{var $value = (int) explode(" ", $notification->getData(), 2)[0]}
{var $message = explode(" ", $notification->getData(), 2)[1]}
<a href="{$sender->getURL()}"><b>{$sender->getCanonicalName()}</b></a> {_transferred_to_you} {tr("points_amount", $value)}.
{if !empty($message)}

View file

@ -1,6 +1,6 @@
{var sender = $notification->getModel(1)}
{var value = (int) explode(" ", $notification->getData(), 2)[0]}
{var message = explode(" ", $notification->getData(), 2)[1]}
{var $sender = $notification->getModel(1)}
{var $value = (int) explode(" ", $notification->getData(), 2)[0]}
{var $message = explode(" ", $notification->getData(), 2)[1]}
<a href="{$sender->getURL()}"><b>{$sender->getCanonicalName()}</b></a> {_increased_your_rating_by} {$value}%.
{if !empty($message)}

View file

@ -1,7 +1,7 @@
{var microblogEnabled = isset($thisUser) ? $thisUser->hasMicroblogEnabled() : false}
{var $microblogEnabled = isset($thisUser) ? $thisUser->hasMicroblogEnabled() : false}
{if $microblogEnabled}
{include "post/microblogpost.xml", post => $post, diff => $diff, commentSection => $commentSection}
{include "post/microblogpost.xml", post => $post, commentSection => $commentSection}
{else}
{include "post/oldpost.xml", post => $post, diff => $diff}
{include "post/oldpost.xml", post => $post}
{/if}

View file

@ -1,8 +1,8 @@
{var author = $post->getOwner()}
{var comments = $post->getLastComments(3)}
{var commentsCount = $post->getCommentsCount()}
{var $author = $post->getOwner()}
{var $comments = $post->getLastComments(3)}
{var $commentsCount = $post->getCommentsCount()}
{var commentTextAreaId = $post === null ? rand(1,300) : $post->getId()}
{var $commentTextAreaId = $post === null ? rand(1,300) : $post->getId()}
<table border="0" style="font-size: 11px;" n:class="post, !$compact ? post-divider, $post->isExplicit() ? post-nsfw">
<tbody>
@ -18,8 +18,8 @@
<a href="{$author->getURL()}"><b>{$author->getCanonicalName()}</b></a>
<img n:if="$author->isVerified()" class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png">
{if ($onWallOf ?? false) &&!$post->isPostedOnBehalfOfGroup() && $post->getOwnerPost() !== $post->getTargetWall()}
{var wallId = $post->getTargetWall()}
{var wallURL = $wallId > -1 ? "/id$wallId" : "/club" . abs($wallId)}
{var $wallId = $post->getTargetWall()}
{var $wallURL = $wallId > -1 ? "/id$wallId" : "/club" . abs($wallId)}
на
<a href="{$wallURL}">
<b>
@ -68,7 +68,7 @@
&nbsp;! Этот пост был размещён за взятку.
</div>
<div n:if="$post->isSigned()" class="post-signature">
{var actualAuthor = $post->getOwner(false)}
{var $actualAuthor = $post->getOwner(false)}
<span>
{_author}:
<a href="{$actualAuthor->getURL()}">
@ -91,7 +91,7 @@
</a>
{if !($forceNoLike ?? false)}
{var liked = $post->hasLikeFrom($thisUser)}
{var $liked = $post->hasLikeFrom($thisUser)}
<a href="/wall{$post->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$post->getLikesCount()}">
<div class="heart" id="{if $liked}liked{/if}"></div>
<span class="likeCnt">{if $post->getLikesCount() > 0}{$post->getLikesCount()}{/if}</span>
@ -106,8 +106,8 @@
{include "../comment.xml", comment => $comment, $compact => true}
{/foreach}
<div n:ifset="$thisUser" id="commentTextArea{$commentTextAreaId}" n:attr="style => ($commentsCount == 0 ? 'display: none;')" class="commentsTextFieldWrap">
{var commentsURL = "/al_comments/create/posts/" . $post->getId()}
{var club = is_null($club) ? ($post->getTargetWall() < 0 ? (new openvk\Web\Models\Repositories\Clubs)->get(abs($post->getTargetWall())) : NULL) : $club}
{var $commentsURL = "/al_comments/create/posts/" . $post->getId()}
{var $club = is_null($club) ? ($post->getTargetWall() < 0 ? (new openvk\Web\Models\Repositories\Clubs)->get(abs($post->getTargetWall())) : NULL) : $club}
{include "../textArea.xml", route => $commentsURL, postOpts => false, graffiti => (bool) ovkGetQuirk("comments.allow-graffiti"), post => $post, club => $club}
</div>
</div>

View file

@ -1,4 +1,4 @@
{var author = $post->getOwner()}
{var $author = $post->getOwner()}
<table border="0" style="font-size: 11px;" n:class="post, $post->isExplicit() ? post-nsfw">
<tbody>
@ -15,8 +15,8 @@
<img n:if="$author->isVerified()" class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png">
{$post->isPostedOnBehalfOfGroup() ? tr("post_writes_g") : ($author->isFemale() ? tr("post_writes_f") : tr("post_writes_m"))}
{if ($onWallOf ?? false) &&!$post->isPostedOnBehalfOfGroup() && $post->getOwnerPost() !== $post->getTargetWall()}
{var wallId = $post->getTargetWall()}
{var wallURL = $wallId > -1 ? "/id$wallId" : "/club" . abs($wallId)}
{var $wallId = $post->getTargetWall()}
{var $wallURL = $wallId > -1 ? "/id$wallId" : "/club" . abs($wallId)}
на
<a href="{$wallURL}">
<b>
@ -50,7 +50,7 @@
&nbsp;! Этот пост был размещён за взятку.
</div>
<div n:if="$post->isSigned()" class="post-signature">
{var actualAuthor = $post->getOwner(false)}
{var $actualAuthor = $post->getOwner(false)}
<span>
{_author}:
<a href="{$actualAuthor->getURL()}">
@ -92,7 +92,7 @@
</a>
<div n:if="!($forceNoLike ?? false)" class="like_wrap">
{var liked = $post->hasLikeFrom($thisUser)}
{var $liked = $post->hasLikeFrom($thisUser)}
<a href="/wall{$post->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$post->getLikesCount()}">
<div class="heart" id="{if $liked}liked{/if}"></div>
<span class="likeCnt">{if $post->getLikesCount() > 0}{$post->getLikesCount()}{/if}</span>

View file

@ -1,5 +1,5 @@
{php if(!isset($GLOBALS["textAreaCtr"])) $GLOBALS["textAreaCtr"] = 10;}
{var textAreaId = ($post ?? NULL) === null ? (++$GLOBALS["textAreaCtr"]) : $post->getId()}
{var $textAreaId = ($post ?? NULL) === null ? (++$GLOBALS["textAreaCtr"]) : $post->getId()}
<div id="write" style="padding: 5px 0;" onfocusin="expand_wall_textarea({$textAreaId});">
<form action="{$route}" method="post" enctype="multipart/form-data" style="margin:0;">
@ -12,7 +12,7 @@
{_attachment}: <span>(unknown)</span>
</div>
<div n:if="$postOpts ?? true" class="post-opts">
{var anonEnabled = OPENVK_ROOT_CONF['openvk']['preferences']['wall']['anonymousPosting']['enable']}
{var $anonEnabled = OPENVK_ROOT_CONF['openvk']['preferences']['wall']['anonymousPosting']['enable']}
{if !is_null($thisUser) && !is_null($club ?? NULL) && $owner < 0}
{if $club->canBeModifiedBy($thisUser)}

View file

@ -71,6 +71,8 @@ routes:
handler: "User->twoFactorAuthSettings"
- url: "/settings/2fa/disable"
handler: "User->disableTwoFactorAuth"
- url: "/settings/reset_theme"
handler: "User->resetThemepack"
- url: "/coins_transfer"
handler: "User->coinsTransfer"
- url: "/increase_social_credits"
@ -121,8 +123,10 @@ routes:
handler: "Wall->delete"
- url: "/wall{num}_{num}/pin"
handler: "Wall->pin"
- url: "/blob_{text}/{text}.{text}"
- url: "/blob_{text}/{?path}.{text}"
handler: "Blob->file"
placeholders:
path: "[A-z0-9\\-_\\/]{3,}"
- url: "/themepack/{text}/{?version}/{?resClass}/{?any}"
handler: "Themepacks->resource"
placeholders:
@ -301,6 +305,8 @@ routes:
handler: "About->robotsTxt"
- url: "/humans.txt"
handler: "About->humansTxt"
- url: "/dev"
handler: "About->dev"
- url: "/{?shortCode}"
handler: "UnknownTextRouteStrategy->delegate"
placeholders:

Binary file not shown.

BIN
Web/static/audio/notify.mp3 Normal file

Binary file not shown.

View file

@ -2,7 +2,7 @@
/* Design belongs to Pavel Durov & VK Group */
.post {
padding: 5px 0 5px 0;
padding: 5px 0;
}
.post-divider {
@ -12,7 +12,7 @@
.post-author {
background-color: transparent;
border: none;
padding: 0 3px 3px 3px;
padding: 0 3px 3px;
}
.post-author .date {
@ -25,7 +25,7 @@
}
.post-content .text {
padding: 0 4px 0 4px;
padding: 0 4px;
}
.post-menu {
@ -37,7 +37,7 @@
}
.comment {
padding: 5px 0 0 0;
padding: 5px 0 0;
border-top: 1px #ddd solid;
}
@ -46,16 +46,16 @@
}
.repost-icon {
background: url('/assets/packages/static/openvk/img/published.gif') no-repeat 0px 0px;
background: url('/assets/packages/static/openvk/img/published.gif') no-repeat 0 0;
height: 12px;
margin: 2px 3px 0px;
margin: 2px 3px 0;
width: 11px;
}
.heart {
background: url('/assets/packages/static/openvk/img/like.gif') no-repeat 1px 0px;
background: url('/assets/packages/static/openvk/img/like.gif') no-repeat 1px 0;
height: 10px;
margin: 2px 3px 0px;
margin: 2px 3px 0;
width: 11px;
float: none;
opacity: 0.4;
@ -73,24 +73,25 @@
opacity: 0.4 !important;
}
.post-share-button, .post-like-button {
.post-share-button,
.post-like-button {
display: flex;
padding: 2px;
border-radius: 2px;
transition-duration: 0.2s;
}
.post-share-button:hover, .post-like-button:hover {
.post-share-button:hover,
.post-like-button:hover {
background-color: rgb(240, 240, 240);
}
.post-author .delete {
float: right;
display: inline-block;
height: 16px;
width: 16px;
overflow: auto;
background: url("/assets/packages/static/openvk/img/input_clear.gif") no-repeat 0px 0px;
background: url("/assets/packages/static/openvk/img/input_clear.gif") no-repeat 0 0;
opacity: 0.1;
transition-duration: 0.3s;
}
@ -101,11 +102,10 @@
.post-author .pin {
float: right;
display: inline-block;
height: 16px;
width: 16px;
overflow: auto;
background: url("/assets/packages/static/openvk/img/pin.png") no-repeat 0px 0px;
background: url("/assets/packages/static/openvk/img/pin.png") no-repeat 0 0;
opacity: 0.1;
transition-duration: 0.3s;
}
@ -117,7 +117,7 @@
.expand_button {
background-color: #eee;
width: 100%;
display: inline-block;;
display: inline-block;
height: 30px;
line-height: 28px;
text-align: center;

View file

@ -13,12 +13,6 @@ body {
word-wrap: break-word;
}
span {
padding: 0 0 2px;
color: gray;
font-weight: bold;
}
.nobold,
nobold {
font-weight: normal;
@ -48,9 +42,9 @@ p {
position: relative;
width: 791px;
height: 45px;
background: url('../img/header.png');
background-repeat: no-repeat;
background-position: 0;
background: url('../img/header.png');
}
.page_custom_header {
@ -59,7 +53,7 @@ p {
#page_act {
border-bottom: 1px solid #d5dde6;
padding: 2px 10px 5px 10px;
padding: 2px 10px 5px;
color: #2B587A;
width: 608px;
margin-left: -10px;
@ -95,8 +89,8 @@ p {
display: inline-block;
height: 29px;
padding: 11px 4px 0 7px;
background-size: 1.5px 41px;
background: url('../img/divider.png') no-repeat;
background-size: 1.5px 41px;
}
.header_navigation .link a {
@ -164,7 +158,6 @@ p {
padding: 3px 3px 3px 6px;
text-decoration: none;
border-top: 1px solid #fff;
/* fix */
color: #000;
}
@ -184,17 +177,17 @@ p {
.wrap1 {
border: 1px solid #EBF0F4;
border-top: 0px;
border-top: 0;
width: auto;
}
.wrap2 {
border-right: 1px solid #F6F8FA;
border-top: 0px;
border-top: 0;
}
.page_yellowheader {
padding: 4px 10px 4px;
padding: 4px 10px;
font-weight: bold;
background: url('../img/header_yellow.png') repeat-x;
background-color: #EEE5B8;
@ -405,7 +398,7 @@ table {
.page_status {
font-weight: normal;
font-size: 11px;
padding: 3px 1px 3px;
padding: 3px 1px;
color: #000;
width: 380px;
height: auto !important;
@ -442,9 +435,9 @@ table {
outline: none;
white-space: nowrap;
background: #595959;
background-position: 0px -16px;
background-position: 0 -16px;
color: #fff;
padding: 4px 8px 4px;
padding: 4px 8px;
text-shadow: 0 1px 0 #686868;
cursor: pointer;
text-decoration: none;
@ -477,7 +470,7 @@ input[type=radio] {
outline: none;
cursor: pointer;
vertical-align: middle;
margin: 4px 3px 3px 3px;
margin: 4px 3px 3px;
}
input[type=checkbox] {
@ -520,17 +513,13 @@ input[type=radio]:checked {
width: 200px;
}
.right_big_block {
width: 399px;
float: right;
}
.content_title_expanded {
background-image: url('../img/flex_arrow_open2.png');
background-repeat: no-repeat;
background-color: #e6e6e6;
border-top: #8B8B8B solid 1px;
padding: 3px 8px 3px 24px;
margin-top: 5px;
font-weight: bold;
color: #626262;
font-size: 11px;
@ -554,7 +543,6 @@ input[type=radio]:checked {
.content_subtitle {
background-color: #F0F0F0;
padding: 0;
display: block;
font-size: 11px;
border-bottom: 1px solid #EEEEEE;
@ -568,7 +556,7 @@ input[type=radio]:checked {
}
.content-withouttop {
padding-top: 0px;
padding-top: 0;
}
input[type="text"],
@ -595,8 +583,8 @@ h4 {
color: #45688E;
font-size: 12px;
font-weight: bold;
margin: 0px;
padding: 0px 0px 3px;
margin: 0;
padding: 0 0 3px;
font-family: verdana, arial, sans-serif;
}
@ -619,7 +607,7 @@ h4 {
border-top: #8B8B8B solid 1px;
border-bottom: #ECECEC solid 1px;
font-size: 11px;
padding: 3px 5px 3px;
padding: 3px 5px;
line-height: 125%;
}
@ -769,7 +757,7 @@ span {
}
.content_list .cl_element .cl_avatar {
padding: 7px 7px 0 7px;
padding: 7px 7px 0;
text-align: center;
}
@ -807,13 +795,6 @@ table.User {
width: 603px;
}
.container_gray .content {
background: #fff;
padding: 5px;
border: #DEDEDE solid 1px;
clear: both;
}
.tabs {
border-bottom: 1px solid #707070;
padding: 0 10px;
@ -832,7 +813,7 @@ table.User {
display: inline-block;
padding: 5px 10px;
margin-right: 3px;
border-radius: 3px 3px 0px 0px;
border-radius: 3px 3px 0 0;
}
.tab:hover {
@ -995,11 +976,6 @@ table.User {
color: #404036;
}
.messenger-app--messages,
.messenger-app--input {
padding: 10px;
}
.messenger-app--messages,
.messenger-app--input {
padding: 10px 70px;
@ -1021,11 +997,6 @@ table.User {
margin-bottom: 1.2rem;
}
.messenger-app--messages---message .ava,
.messenger-app--input>.ava {
max-width: 64px;
}
.messenger-app--messages---message .ava,
.messenger-app--input>.ava {
width: 52px;
@ -1074,6 +1045,7 @@ table.User {
box-sizing: border-box;
padding: 0 10px;
width: calc(100% - 128px);
float: right;
}
.messenger-app--input---messagebox textarea {
@ -1083,10 +1055,6 @@ table.User {
margin-bottom: 8px !important;
}
.messenger-app--input---messagebox {
float: right;
}
.messenger-app--input .blocked {
width: 100%;
height: 100%;
@ -1190,16 +1158,16 @@ textarea {
#faqhead {
background: #fbf3c3;
margin: 0px 5px 0px 5px;
margin: 0 5px;
padding: 5px;
font-weight: bold;
border: 1px solid #d7cf9e;
border-bottom: 0px;
border-bottom: 0;
}
#faqcontent {
background: #fafafa;
margin: 0px 5px 10px 5px;
margin: 0 5px 10px;
padding: 5px 5px 5px 10px;
border: 1px solid #ddd;
}
@ -1307,12 +1275,6 @@ body.scrolled .toTop:hover {
vertical-align: super;
}
.ugc-table tr>td:nth-of-type(2) {
display: block;
width: 270px;
overflow: hidden;
}
.ugc-table.slim tr>td:nth-of-type(1) {
width: unset;
}
@ -1378,9 +1340,9 @@ body.scrolled .toTop:hover {
}
.heart {
background: url('/assets/packages/static/openvk/img/like.gif') no-repeat 1px 0px;
background: url('/assets/packages/static/openvk/img/like.gif') no-repeat 1px 0;
height: 10px;
margin: 2px 3px 0px;
margin: 2px 3px 0;
width: 11px;
float: left;
opacity: 0.4;
@ -1400,10 +1362,6 @@ body.scrolled .toTop:hover {
opacity: 1 !important;
}
.content_title_expanded {
margin-top: 5px;
}
.page-wrap {
border-bottom: solid 1px #C3CAD2;
border-left: solid 1px #DAE1E8;
@ -1411,33 +1369,12 @@ body.scrolled .toTop:hover {
padding: 10px;
}
.wrap1 {
width: auto;
border: 1px solid #EBF0F4;
border-top: 0px;
}
.wrap2 {
border-right: 1px solid #F6F8FA;
border-top: 0px;
}
#wrapH {
border-right: solid 1px #EAEEF3;
border-left: solid 1px #EAEEF3;
}
#wrapHI {
border-right: solid 1px #D5DDE6;
border-left: solid 1px #D5DDE6;
}
.ugc-table td {
vertical-align: top !important;
font-size: 11px;
line-height: 13px;
margin: 0px;
padding: 1px 0px 1px 0px;
margin: 0;
padding: 1px 0;
}
.label {
@ -1457,47 +1394,44 @@ body.scrolled .toTop:hover {
}
#basicInfo {
padding: 5px 0px 15px 8px;
padding: 5px 0 15px 8px;
}
.accountInfo {
padding: 0px;
margin: 0px 0px 0px 8px;
padding: 0;
margin: 0 0 0 8px;
border-bottom: solid 1px #DAE1E8;
display: block;
}
.profileName {
/* width: 225px; */
color: #45688E;
font-size: 11px;
font-weight: bold;
margin: 0px;
/* padding: 2px 5px 0px 0px; */
margin: 0;
}
.profileName h2 {
color: #45688E;
font-size: 13px;
padding-bottom: 0px;
margin: 0px;
padding: 0px;
margin: 0;
padding: 0;
}
.notes_titles {
margin: 0px;
padding: 0px;
margin: 0;
padding: 0;
list-style: none;
}
.written {
background: url('/assets/packages/static/openvk/img/note.gif') no-repeat 0px 1px;
padding: 0px 0px 6px 22px;
background: url('/assets/packages/static/openvk/img/note.gif') no-repeat 0 1px;
padding: 0 0 6px 22px;
}
.written a {
display: block;
padding: 0px 0px 1px 0px;
padding: 0 0 1px;
}
.notes_titles small {
@ -1517,8 +1451,8 @@ body.scrolled .toTop:hover {
}
.right_big_block h4 {
margin: 1px 0px 0px;
padding: 4px 0px 2px !important;
margin: 1px 0 0;
padding: 4px 0 2px !important;
}
.knowledgeBaseArticle ul {
@ -1577,7 +1511,7 @@ body.scrolled .toTop:hover {
align-content: center;
display: flex;
padding: 2px;
box-shadow: inset 0 0 0px 1px #ccc, inset 0 0 0px 2px #fff;
box-shadow: inset 0 0 0 1px #ccc, inset 0 0 0 2px #fff;
background-color: #000;
}
@ -1648,17 +1582,26 @@ body.scrolled .toTop:hover {
color: #58462a;
}
.group-alert {
margin-bottom: 8px;
padding: 4px;
border: 1px solid #c3a476;
font-weight: 900;
background-color: #f3ddbd;
color: #58462a;
}
.knowledgeBaseArticle {
margin-top: -11px;
/* this is very stupid fix but nah */
}
.avatar-list {
padding: 4px 8px 4px 8px;
padding: 4px 8px;
}
.avatar-list-item {
padding: 4px 0 4px 0;
padding: 4px 0;
}
.avatar-list-item::after {
@ -1763,9 +1706,9 @@ body.scrolled .toTop:hover {
outline: none;
white-space: nowrap;
background: #595959;
background-position: 0px -16px;
background-position: 0 -16px;
color: #fff;
padding: 4px 8px 4px;
padding: 4px 8px;
text-shadow: 0 1px 0 #686868;
cursor: pointer;
text-decoration: none;
@ -1810,7 +1753,7 @@ body.scrolled .toTop:hover {
height: 16px;
width: 16px;
overflow: auto;
background: url("/assets/packages/static/openvk/img/pin.png") no-repeat 0px 0px;
background: url("/assets/packages/static/openvk/img/pin.png") no-repeat 0 0;
vertical-align: middle;
}
@ -1858,7 +1801,7 @@ body.scrolled .toTop:hover {
background: #f7f7f7;
border-bottom: solid 1px #DAE1E8;
border-top: solid 1px #45688E;
padding: 4px 6px 5px 6px;
padding: 4px 6px 5px;
}
.note_header .note_title {
@ -1867,18 +1810,18 @@ body.scrolled .toTop:hover {
font-weight: bold;
line-height: 15px;
margin: 0;
padding: 0 0 1px 0;
padding: 0 0 1px;
}
.note_footer {
border-top: 1px solid #ddd;
clear: both;
margin-top: 10px;
padding: 0px 2px 0px 6px;
padding: 0 2px 0 6px;
}
.comments_count {
padding: 5px 0px 0px 0px;
padding: 5px 0 0;
}
.groups_options {
@ -1917,7 +1860,7 @@ table td[width="120"] {
}
.profile_thumb {
padding: 0px 10px 0px 0px;
padding: 0 10px 0 0;
width: 50px;
display: inline-block;
vertical-align: top;
@ -1941,23 +1884,6 @@ table td[width="120"] {
padding: 3px 7px;
}
#gp_container span {
display: block;
margin: 10px 0 15px;
}
#gp_container h4 {
font-size: 11px;
}
.container_gray .content:last-child {
margin-bottom: 0;
}
.group_info {
padding: 0 0 0 5px !important;
}
.mb_tab#active {
background-color: #898989;
}
@ -1970,15 +1896,8 @@ table td[width="120"] {
color: white;
}
.profile_thumb {
padding: 0px 10px 0px 0px;
width: 50px;
display: inline-block;
vertical-align: top;
}
.border-block {
box-shadow: inset 0px 0 0px 1px #b6bfca, inset 0px 0 0px 10px #d8dfe7;
box-shadow: inset 0 0 0 1px #b6bfca, inset 0 0 0 10px #d8dfe7;
width: 300px;
padding: 20px;
}
@ -1986,3 +1905,12 @@ table td[width="120"] {
.center {
margin: 0 auto;
}
#votesBalance {
margin-top: 50px;
padding: 7px;
background-color: #f6f6f6;
border-bottom: 1.5px solid #707070;
text-align: center;
user-select: none;
}

View file

@ -1,4 +1,4 @@
createjs.Sound.registerSound("/assets/packages/static/openvk/audio/Bruh.mp3", "notification");
createjs.Sound.registerSound("/assets/packages/static/openvk/audio/notify.mp3", "notification");
function __actualPlayNotifSound() {
createjs.Sound.play("notification");

View file

@ -117,3 +117,8 @@ function setupWallPostInputHandlers(id) {
// textArea.style.height = (newHeight > originalHeight ? (newHeight + boost) : originalHeight) + "px";
});
}
u("#write > form").on("keydown", function(event) {
if(event.ctrlKey && event.keyCode === 13)
this.submit();
});

View file

@ -2,7 +2,33 @@ Function.noop = () => {};
var _n_counter = 0;
function NewNotification(title, body, avatar = null, callback = () => {}, time = 5000) {
var _activeWindow = true;
const _pageTitle = u("title").nodes[0].innerText;
var counter = 0;
/* this fucking dumb shit is broken :c
window.addEventListener('focus', () => {
_activeWindow = true;
closeAllNotifications();
});
window.addEventListener('blur', () => {_activeWindow = false});
function closeAllNotifications() {
var notifications = u(".notifications_global_wrap").nodes[0].children;
for (var i = 0; i < notifications.length; i++) {
setTimeout(() => {
console.log(i);
notifications.item(i).classList.add('disappears');
setTimeout(() => {notifications.item(i).remove()}, 500).bind(this);
}, 5000).bind(this);
}
} */
function NewNotification(title, body, avatar = null, callback = () => {}, time = 5000, count = true) {
if(avatar != null) {
avatar = '<avatar>' +
'<img src="' + avatar + '">' +
@ -39,8 +65,15 @@ function NewNotification(title, body, avatar = null, callback = () => {}, time =
getPrototype().addClass('disappears');
setTimeout(() => {getPrototype().remove()}, 500);
}
setTimeout(() => {__closeNotification()}, time);
if(count == true) {
counter++;
document.title = `(${counter}) ${_pageTitle}`;
}
/* if(_activeWindow == true) { */
setTimeout(() => {__closeNotification()}, time);
/* } */
notification.children('notification_title').children('a.close').on('click', function(e) {
__closeNotification();

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

View file

@ -16,7 +16,9 @@
"erusev/parsedown": "dev-master",
"bhaktaraz/php-rss-generator": "dev-master",
"ext-simplexml": "*",
"symfony/console": "5.4.x-dev"
"symfony/console": "5.4.x-dev",
"wapmorgan/morphos": "dev-master",
"ext-sodium": "*"
},
"minimum-stability": "dev"
}

79
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "878bd996183ccbb15637a7399ba03ab9",
"content-hash": "63feb555e36a6e7ab5a0a5ec71adecdd",
"packages": [
{
"name": "al/emoji-detector",
@ -2222,6 +2222,80 @@
},
"time": "2018-09-26T17:10:59+00:00"
},
{
"name": "wapmorgan/morphos",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/wapmorgan/Morphos.git",
"reference": "ec18034d4a439139902c769a64bb67e59e3402a8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/wapmorgan/Morphos/zipball/ec18034d4a439139902c769a64bb67e59e3402a8",
"reference": "ec18034d4a439139902c769a64bb67e59e3402a8",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"suggest": {
"ext-readline": "For using interactive version of script"
},
"default-branch": true,
"type": "library",
"autoload": {
"files": [
"src/English/functions.php",
"src/Russian/functions.php"
],
"psr-4": {
"morphos\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Sergey Vanyushin",
"email": "wapmorgan@gmail.com",
"role": "developer"
}
],
"description": "A morphological solution for Russian and English language written completely in PHP. Provides classes to inflect personal names, geographical names, decline and pluralize nouns, generate cardinal and ordinal numerals, spell out money amounts and time.",
"homepage": "http://morphos.io",
"keywords": [
"Numerals",
"adjectives",
"cardinal",
"declension",
"english",
"geographical names",
"human-friendly",
"inflection",
"language",
"money",
"morphology",
"nouns",
"ordinal",
"personal names",
"pluralization",
"russian",
"spelling",
"time"
],
"support": {
"issues": "https://github.com/wapmorgan/Morphos/issues",
"source": "https://github.com/wapmorgan/Morphos/tree/master"
},
"time": "2021-11-22T09:15:18+00:00"
},
{
"name": "whichbrowser/parser",
"version": "dev-master",
@ -2356,7 +2430,8 @@
"vearutop/php-obscene-censor-rus": 20,
"erusev/parsedown": 20,
"bhaktaraz/php-rss-generator": 20,
"symfony/console": 20
"symfony/console": 20,
"wapmorgan/morphos": 20
},
"prefer-stable": false,
"prefer-lowest": false,

View file

@ -2,17 +2,23 @@ chandler:
debug: true
websiteUrl: null
rootApp: "openvk"
preferences:
appendExtension: "xhtml"
adminUrl: "/chandlerd"
exposeChandler: true
extensions:
path: null
allEnabled: false
database:
dsn: "mysql:unix_socket=/var/run/mysql/mysql.sock;dbname=openvk"
user: "root"
password: "justMonika"
security:
secret: "081906e04f2921e48751fbc5df44dbbcd7f6ecba065ade3d8ea28034cb8d6db24da10965845b5f3376847674e3bce61b5c0e439d12eef3c00d30f3b953657cac"
sessionDuration: 14
csrfProtection: "permissive"
extendedValidation: false

View file

@ -0,0 +1,105 @@
openvk:
debug: true
appearance:
name: "FreeBSD Instance"
motd: "Yet another OpenVK instance"
preferences:
femaleGenderPriority: true
nginxCacheTime: 120
uploads:
disableLargeUploads: false
mode: "basic"
api:
maxFilesPerDomain: 10
maxFileSize: 25000000
shortcodes:
minLength: 3 # won't affect existing short urls or the ones set via admin panel
forbiddenNames:
- "index.php"
photos:
upgradeStructure: false
security:
requireEmail: false
requirePhone: false
forcePhoneVerification: false
forceEmailVerification: false
enableSu: true
rateLimits:
actions: 5
time: 20
maxViolations: 50
maxViolationsAge: 120
autoban: true
registration:
enable: true
reason: "" # reason for disabling registration
support:
supportName: "Moderator"
adminAccount: 1 # Change this ok
fastAnswers:
- "This is a list of quick answers to common questions for support. Post your responses here and agents can send it quickly with just 3 clicks"
- "There can be as many answers as you want, but it is best to have a maximum of 10.\n\nYou can also remove all answers from the list to disable this feature"
- "Good luck filling! If you are a regular support agent, inform the administrator that he forgot to fill the config"
messages:
strict: false
wall:
christian: false
anonymousPosting:
enable: false
account: 100
postSizes:
maxSize: 60000
processingLimit: 3000
emojiProcessingLimit: 1000
commerce: false
menu:
links:
- name: "@left_menu_donate"
url: "/donate"
adPoster:
enable: false
src: "https://example.org/ad_poster.jpeg"
caption: "Ad caption"
link: "https://example.org/product.aspx?id=10&from=ovk"
bellsAndWhistles:
fartscroll: false
testLabel: false
defaultMobileTheme: ""
telemetry:
plausible:
enable: false
domain: ""
server: ""
piwik:
enable: false
container: ""
site: ""
layer: "dataLayer"
matomo:
enable: false
container: ""
site: ""
credentials:
smsc:
enable: false
client: ""
secret: "SECRET_KEY_HERE"
telegram:
enable: false
token: "TOKEN_HERE"
helpdeskChat: ""
eventDB:
enable: true # Better enable this
database:
dsn: "mysql:unix_socket=/var/run/mysql/mysql.sock;dbname=openvk-eventdb"
user: "root"
password: "justMonika"
notificationsBroker:
enable: false
kafka:
addr: "127.0.0.1"
port: 9092
topic: "OvkEvents"

View file

@ -1,10 +1,10 @@
#!/bin/tcsh
##########################################################################################################\
# OpenVK AutoInstallation Script for FreeBSD 12 # /( )` #
# OpenVK AutoInstallation Script for FreeBSD 13 # /( )` #
# ------------------------------------------------------------- # \ \___ / | #
# # /- _ `-/ ' #
# This script installs OpenVK on an empty FreeBSD 12 box. # (/\/ \ \ /\ #
# This script installs OpenVK on an empty FreeBSD 13 box. # (/\/ \ \ /\ #
# Copyright (c) 2020 OpenVK contributors # / / | ` \ #
# ------------------------------------------------------------- # O O ) / | #
# # `-^--'`< ' #
@ -27,28 +27,27 @@ if ($? == 1) then
endif
set osVer = `freebsd-version -r`
if ($osVer !~ "12.*") then
echo "Fatal Error: This installation supports only FreeBSD 12, but you have FreeBSD $osVer installed."
if ($osVer !~ "13.*") then
echo "Fatal Error: This installation supports only FreeBSD 13, but you have FreeBSD $osVer installed."
exit 201
endif
# Update system
pkg update
pkg upgrade
cd /tmp
mkdir ovkinstall
cd ovkinstall
# Install programming languages and Git
yes | pkg install git
yes | pkg install php74
yes | pkg install mod_php74 # will also install Apache2.4
yes | pkg install php74-gd php74-mbstring php74-iconv php74-json php74-sodium php74-calendar php74-curl php74-brotli php74-zip php74-openssl php74-ctype php74-dom php74-fileinfo php74-PDO php74-pdo_mysql php74-sqlite3 php74-pdo_sqlite php74-sockets php74-tokenizer php74-opcache php74-posix php74-pecl-yaml php74-phar php74-filter php74-zlib php74-session
# Install required packages
yes | pkg install git php74 mod_php74 php74-gd php74-mbstring php74-iconv php74-json php74-sodium php74-calendar php74-curl php74-brotli php74-zip php74-openssl php74-ctype php74-dom php74-fileinfo php74-PDO php74-pdo_mysql php74-sqlite3 php74-pdo_sqlite php74-sockets php74-tokenizer php74-opcache php74-posix php74-pecl-yaml php74-phar php74-filter php74-zlib php74-session php74-simplexml node www/npm mysql80-server mysql80-client ffmpeg
rehash
# Install package managers
npm i -g yarn
curl -LO https://getcomposer.org/installer
php ./installer --install-dir=/bin --filename=composer --preview
rm -f ./installer
yes | pkg install node npm
npm i -g yarn
rehash
# Download OVK distro
cd /tmp
git clone https://github.com/openvk/openvk.git
# Install chandler
@ -58,7 +57,7 @@ git clone https://github.com/openvk/chandler.git
cd chandler
yes | composer install
cp /tmp/openvk/install/automated/common/chandler.yml /opt/chandler/chandler.yml
cp /tmp/ovkinstall/openvk/install/automated/common/chandler.yml /opt/chandler/chandler.yml
chown -R www: .
chmod -R 777 .
@ -75,7 +74,7 @@ cd /opt/chandler
ln -s /opt/chandler/extensions/available/commitcaptcha /opt/chandler/extensions/enabled/commitcaptcha
# Install OpenVK
cp -r /tmp/openvk /opt/chandler/extensions/available/openvk
cp -r /tmp/ovkinstall/openvk /opt/chandler/extensions/available/openvk
ln -s /opt/chandler/extensions/available/openvk /opt/chandler/extensions/enabled/openvk
cd ./extensions/available/openvk
@ -84,36 +83,46 @@ yes | composer install
cd Web/static/js
yarn install
cp /tmp/openvk/install/automated/common/openvk.yml /opt/chandler/extensions/available/openvk/openvk.yml
cp /tmp/ovkinstall/openvk/install/automated/common/openvk.template.yml /opt/chandler/extensions/available/openvk/openvk.yml
cd ../../../
chown -R www: .
chmod -R 777 .
# Install MySQL
yes | pkg install mariadb105-server mariadb105-client
# Setup databases
sysrc mysql_enable="yes"
service mysql-server start
nohup /usr/local/libexec/mariadbd >& /dev/null & # service mysql-server start doesn't work
sleep 15
# Mount databases
cd /opt/chandler
mysql < /tmp/openvk/install/automated/common/mk_db.sql
mysql < /tmp/ovkinstall/openvk/install/automated/common/mk_db.sql
mysql -p'justMonika' openvk < install/init-db.sql
mysql -p'justMonika' openvk < /tmp/openvk/install/init-static-db.sql
mysql -p'justMonika' openvk-eventdb < /tmp/openvk/install/init-event-db.sql
mysql -p'justMonika' openvk < /tmp/ovkinstall/openvk/install/init-static-db.sql
mysql -p'justMonika' openvk-eventdb < /tmp/ovkinstall/openvk/install/init-event-db.sql
cd /tmp/ovkinstall/openvk/install/sqls
foreach migration (*.sql)
mysql -p'justMonika' openvk < $migration
end
# Change database settings
echo "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'justMonika'; FLUSH PRIVILEGES;" | mysql -p'justMonika';
sed -i'' -e 's:/var/run/mysql/mysql.sock:/tmp/mysql.sock:' /opt/chandler/chandler.yml
sed -i'' -e 's:/var/run/mysql/mysql.sock:/tmp/mysql.sock:' /opt/chandler/extensions/available/openvk/openvk.yml
# Alias folders
rm -rf /usr/local/www/apache24/data
ln -s /opt/chandler/htdocs /usr/local/www/apache24/data
# Configure Apache
cp /tmp/openvk/install/automated/common/httpd.conf /usr/local/etc/apache24/httpd.conf
cp /tmp/openvk/install/automated/common/httpd-php.conf /usr/local/etc/apache24/Includes/php.conf
cp /tmp/ovkinstall/openvk/install/automated/common/httpd.conf /usr/local/etc/apache24/httpd.conf
cp /tmp/ovkinstall/openvk/install/automated/common/httpd-php.conf /usr/local/etc/apache24/Includes/php.conf
sysrc apache24_enable="yes"
service apache24 start
# Cleanup
rm -rf /opt/ovkinstall
# Enjoy
echo "~================= OpenVK 2 Installed =================~"
echo " Use it: http://localhost:80/ "

View file

@ -1 +1 @@
ALTER TABLE groups ADD COLUMN administrators_list_display TINYINT(3) UNSIGNED NOT NULL DEFAULT 0;
ALTER TABLE `groups` ADD COLUMN administrators_list_display TINYINT(3) UNSIGNED NOT NULL DEFAULT 0;

View file

@ -1 +1 @@
ALTER TABLE groups ADD COLUMN owner_comment VARCHAR(36) AFTER owner;
ALTER TABLE `groups` ADD COLUMN owner_comment VARCHAR(36) AFTER owner;

View file

@ -3,7 +3,7 @@ ALTER TABLE `profiles` ADD `activated` tinyint(3) NULL DEFAULT '1' AFTER `2fa_se
CREATE TABLE IF NOT EXISTS `email_verifications` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`profile` bigint(20) unsigned NOT NULL,
`key` char(64) COLLATE utf8mb4_general_nopad_ci NOT NULL,
`key` char(64) COLLATE utf8mb4_unicode_520_ci NOT NULL,
`timestamp` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_nopad_ci;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;

View file

@ -0,0 +1 @@
ALTER TABLE `videos` ADD `processed` BOOLEAN NOT NULL DEFAULT FALSE AFTER `link`, ADD `last_checked` BIGINT UNSIGNED NOT NULL DEFAULT '0' AFTER `processed`;

View file

@ -0,0 +1 @@
ALTER TABLE `groups` ADD `alert` TEXT NULL DEFAULT NULL;

View file

@ -558,6 +558,8 @@
"points" = "Votes";
"points_count" = "votes";
"on_your_account" = "on your account";
"top_up_your_account" = "Get more";
"you_still_have_x_points" = "You have <b>$1</b> unused votes.";
"vouchers" = "Vouchers";
"have_voucher" = "Have voucher";
@ -779,6 +781,7 @@
"error_new_password" = "New password does not match";
"error_shorturl_incorrect" = "The short address has an incorrect format.";
"error_repost_fail" = "Failed to share post";
"error_data_too_big" = "Attribute '$1' must be at most $2 $3 long";
"forbidden" = "Access error";
"forbidden_comment" = "This user's privacy settings do not allow you to look at his page.";
@ -858,6 +861,8 @@
"rules" = "Rules";
"most_popular_groups" = "Most popular groups";
"on_this_instance_are" = "On this instance are:";
"about_links" = "Links";
"instance_links" = "Instance links:";
"about_users_one" = "<b>1</b> user";
"about_users_other" = "<b>$1</b> users";

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