diff --git a/.idea/openvk.iml b/.idea/openvk.iml index 3d72b6b1..fe58bea6 100644 --- a/.idea/openvk.iml +++ b/.idea/openvk.iml @@ -38,6 +38,7 @@ + diff --git a/.idea/php.xml b/.idea/php.xml index 5aabe8cd..7b4f97fd 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -39,6 +39,7 @@ + diff --git a/README.md b/README.md index f56a7fc0..e67043d7 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,15 @@ To be honest, we don't know whether it even works. However, this version is main We will release OpenVK as soon as it's ready. As for now you can: * `git clone` this repo's master branch (use `git pull` to update) -* Grab a prebuilt OpenVK distro from [GitHub artifacts](https://github.com/openvk/archive/actions/workflows/nightly.yml) +* Grab a prebuilt OpenVK distro from [GitHub artifacts](https://nightly.link/openvk/archive/workflows/nightly/master/OpenVK%20Archive.zip) ## Instances * **[openvk.su](https://openvk.su/)** * **[openvk.uk](https://openvk.uk)** - official mirror of openvk.su () +* **[openvk.co](http://openvk.co)** - yet another official mirror of openvk.su without TLS () * [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): diff --git a/README_RU.md b/README_RU.md index dda9e989..6bf29502 100644 --- a/README_RU.md +++ b/README_RU.md @@ -2,23 +2,25 @@ _[English](README.md)_ -**OpenVK** это попытка создать простую CMS, которая ~~косплеит~~ имитирует старый ВКонтакте. Представленный здесь код пока не стабилен. +**OpenVK** - это попытка создать простую CMS, которая ~~косплеит~~ имитирует старый ВКонтакте. На данный момент представленный здесь исходный код проекта пока не является стабильным. ВКонтакте принадлежит Павлу Дурову и VK Group. Честно говоря, мы даже не знаем, работает ли она вообще. Однако, эта версия поддерживается, и мы будем рады принять ваши сообщения об ошибках [в нашем баг-трекере](https://github.com/openvk/openvk/projects/1). Вы также можете отправлять их через [вкладку "Помощь"](https://openvk.su/support?act=new) (для этого вам понадобится учетная запись OVK). -## Когда релиз? +## Когда выйдет релизная версия? Мы выпустим OpenVK, как только он будет готов. На данный момент Вы можете: -* Сделать `git clone` master ветки этой репозитории (используйте `git pull` для обновления) -* Взять готовую сборку OpenVK из [GitHub Actions](https://github.com/openvk/archive/actions/workflows/nightly.yml) +* Склонировать master ветку репозитория командой `git clone` (используйте `git pull` для обновления) +* Взять готовую сборку OpenVK из [GitHub Actions](https://nightly.link/openvk/archive/workflows/nightly/master/OpenVK%20Archive.zip) ## Инстанции * **[openvk.su](https://openvk.su/)** * **[openvk.uk](https://openvk.uk)** - официальное зеркало openvk.su () +* **[openvk.co](http://openvk.co)** - ещё одно официальное зеркало openvk.su без TLS () * [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**. - Получить на Codeberg + Get it on Codeberg diff --git a/VKAPI/Handlers/Users.php b/VKAPI/Handlers/Users.php index 2f968803..3664903f 100644 --- a/VKAPI/Handlers/Users.php +++ b/VKAPI/Handlers/Users.php @@ -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(); diff --git a/VKAPI/Handlers/Wall.php b/VKAPI/Handlers/Wall.php index 59f8c751..7635342b 100644 --- a/VKAPI/Handlers/Wall.php +++ b/VKAPI/Handlers/Wall.php @@ -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 diff --git a/Vagrantfile b/Vagrantfile index bcbf34d9..f8b3e250 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -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 diff --git a/Web/Models/Entities/Club.php b/Web/Models/Entities/Club.php index 382332dd..f306a8e6 100644 --- a/Web/Models/Entities/Club.php +++ b/Web/Models/Entities/Club.php @@ -346,6 +346,11 @@ class Club extends RowModel { return $this->getRecord()->website; } + + function getAlert(): ?string + { + return $this->getRecord()->alert; + } use Traits\TSubscribable; } diff --git a/Web/Models/Entities/Media.php b/Web/Models/Entities/Media.php index d86e6441..9377f3e8 100644 --- a/Web/Models/Entities/Media.php +++ b/Web/Models/Entities/Media.php @@ -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"); diff --git a/Web/Models/Entities/Traits/TRichText.php b/Web/Models/Entities/Traits/TRichText.php index 3eaaeaff..c8a85e70 100644 --- a/Web/Models/Entities/Traits/TRichText.php +++ b/Web/Models/Entities/Traits/TRichText.php @@ -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", "$2", $text); - $text = preg_replace("%([\n\r\s]|^)(#([\p{L}_-]++[0-9]*[\p{L}_-]*))%Xu", "$1$2", $text); + $text = preg_replace("%\[([A-Za-z0-9]++)\|((?:[\p{L&}\p{Lo} 0-9@]\p{Mn}?)++)\]%Xu", "$2", $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]$m[2]"; + }, $text); + $text = $this->formatEmojis($text); } diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index e3307bf9..68ad6a77 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -1,5 +1,6 @@ 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 : 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; diff --git a/Web/Models/Entities/Video.php b/Web/Models/Entities/Video.php index f57159b7..7bf1b010 100644 --- a/Web/Models/Entities/Video.php +++ b/Web/Models/Entities/Video.php @@ -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(); diff --git a/Web/Models/Repositories/Clubs.php b/Web/Models/Repositories/Clubs.php index bea73cef..b7b59251 100644 --- a/Web/Models/Repositories/Clubs.php +++ b/Web/Models/Repositories/Clubs.php @@ -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) diff --git a/Web/Models/shell/processVideo.ps1 b/Web/Models/shell/processVideo.ps1 index 4ad382dd..254f619d 100644 --- a/Web/Models/shell/processVideo.ps1 +++ b/Web/Models/shell/processVideo.ps1 @@ -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 diff --git a/Web/Models/shell/processVideo.sh b/Web/Models/shell/processVideo.sh index 43441b9e..f5542c63 100644 --- a/Web/Models/shell/processVideo.sh +++ b/Web/Models/shell/processVideo.sh @@ -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 diff --git a/Web/Presenters/AboutPresenter.php b/Web/Presenters/AboutPresenter.php index e7351897..cf4572b8 100644 --- a/Web/Presenters/AboutPresenter.php +++ b/Web/Presenters/AboutPresenter.php @@ -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; + } } diff --git a/Web/Presenters/BlobPresenter.php b/Web/Presenters/BlobPresenter.php index 117eebca..21bba2da 100644 --- a/Web/Presenters/BlobPresenter.php +++ b/Web/Presenters/BlobPresenter.php @@ -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; - } } } diff --git a/Web/Presenters/PhotosPresenter.php b/Web/Presenters/PhotosPresenter.php index 01093be7..aed5fbfd 100644 --- a/Web/Presenters/PhotosPresenter.php +++ b/Web/Presenters/PhotosPresenter.php @@ -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()); diff --git a/Web/Presenters/UserPresenter.php b/Web/Presenters/UserPresenter.php index 1eca5f57..b9e8b714 100644 --- a/Web/Presenters/UserPresenter.php +++ b/Web/Presenters/UserPresenter.php @@ -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(); diff --git a/Web/Presenters/WallPresenter.php b/Web/Presenters/WallPresenter.php index 98e8a4cd..9da0311c 100644 --- a/Web/Presenters/WallPresenter.php +++ b/Web/Presenters/WallPresenter.php @@ -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(); diff --git a/Web/Presenters/templates/@CanonicalListView.xml b/Web/Presenters/templates/@CanonicalListView.xml index 9ad98739..a0c8f7d7 100644 --- a/Web/Presenters/templates/@CanonicalListView.xml +++ b/Web/Presenters/templates/@CanonicalListView.xml @@ -4,7 +4,7 @@
- {var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)} + {var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)} {if sizeof($data) > 0}
diff --git a/Web/Presenters/templates/@MilkshakeListView.xml b/Web/Presenters/templates/@MilkshakeListView.xml index 4473e056..31e366fa 100644 --- a/Web/Presenters/templates/@MilkshakeListView.xml +++ b/Web/Presenters/templates/@MilkshakeListView.xml @@ -3,7 +3,7 @@ {block wrap}
- {var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)} + {var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)} {if sizeof($data) > 0} diff --git a/Web/Presenters/templates/@error.xml b/Web/Presenters/templates/@error.xml index c1e99bc1..64359f7e 100644 --- a/Web/Presenters/templates/@error.xml +++ b/Web/Presenters/templates/@error.xml @@ -1,5 +1,4 @@ -{var instance_name = OPENVK_ROOT_CONF['openvk']['appearance']['name']} - +{var $instance_name = OPENVK_ROOT_CONF['openvk']['appearance']['name']} diff --git a/Web/Presenters/templates/@layout.xml b/Web/Presenters/templates/@layout.xml index 2e0ab71b..bd8de7a0 100644 --- a/Web/Presenters/templates/@layout.xml +++ b/Web/Presenters/templates/@layout.xml @@ -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.'} - + {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} »</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} --> diff --git a/Web/Presenters/templates/@listView.xml b/Web/Presenters/templates/@listView.xml index f07375a5..7d69bf00 100644 --- a/Web/Presenters/templates/@listView.xml +++ b/Web/Presenters/templates/@listView.xml @@ -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"> diff --git a/Web/Presenters/templates/About/AboutInstance.xml b/Web/Presenters/templates/About/AboutInstance.xml index af63d9ee..00cddfcc 100644 --- a/Web/Presenters/templates/About/AboutInstance.xml +++ b/Web/Presenters/templates/About/AboutInstance.xml @@ -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> diff --git a/Web/Presenters/templates/Admin/@layout.xml b/Web/Presenters/templates/Admin/@layout.xml index ec8a213c..5213802c 100644 --- a/Web/Presenters/templates/Admin/@layout.xml +++ b/Web/Presenters/templates/Admin/@layout.xml @@ -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']} @@ -131,7 +131,7 @@
{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]}

{$flashMessage->title} diff --git a/Web/Presenters/templates/Admin/Club.xml b/Web/Presenters/templates/Admin/Club.xml index 55f3f2ad..91d7c6c6 100644 --- a/Web/Presenters/templates/Admin/Club.xml +++ b/Web/Presenters/templates/Admin/Club.xml @@ -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 @@ -{var followers = iterator_to_array($followers)} +{var $followers = iterator_to_array($followers)}

- {var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count} + {var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count} ⭁ туда diff --git a/Web/Presenters/templates/Admin/Clubs.xml b/Web/Presenters/templates/Admin/Clubs.xml index 33093fa5..cbe5d80b 100644 --- a/Web/Presenters/templates/Admin/Clubs.xml +++ b/Web/Presenters/templates/Admin/Clubs.xml @@ -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)} @@ -39,7 +39,7 @@ {$club->getCanonicalName()}
- {var user = $club->getOwner()} + {var $user = $club->getOwner()} @@ -61,7 +61,7 @@

- {var isLast = ((10 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count} + {var $isLast = ((10 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count} ⭁ туда diff --git a/Web/Presenters/templates/Admin/GiftCategories.xml b/Web/Presenters/templates/Admin/GiftCategories.xml index 2dd41ad9..113d45b6 100644 --- a/Web/Presenters/templates/Admin/GiftCategories.xml +++ b/Web/Presenters/templates/Admin/GiftCategories.xml @@ -45,7 +45,7 @@ {/if}
- {var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($categories)) < $count} + {var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($categories)) < $count} ⭁ туда diff --git a/Web/Presenters/templates/Admin/Gifts.xml b/Web/Presenters/templates/Admin/Gifts.xml index c068e067..b1c9cf90 100644 --- a/Web/Presenters/templates/Admin/Gifts.xml +++ b/Web/Presenters/templates/Admin/Gifts.xml @@ -71,7 +71,7 @@ {/if}
- {var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($gifts)) < $count} + {var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($gifts)) < $count} ⭁ туда diff --git a/Web/Presenters/templates/Admin/Users.xml b/Web/Presenters/templates/Admin/Users.xml index e50ed795..51a3ea15 100644 --- a/Web/Presenters/templates/Admin/Users.xml +++ b/Web/Presenters/templates/Admin/Users.xml @@ -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)} @@ -60,7 +60,7 @@

- {var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count} + {var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count} ⭁ туда diff --git a/Web/Presenters/templates/Admin/Vouchers.xml b/Web/Presenters/templates/Admin/Vouchers.xml index 2f28fe21..319b8de7 100644 --- a/Web/Presenters/templates/Admin/Vouchers.xml +++ b/Web/Presenters/templates/Admin/Vouchers.xml @@ -50,7 +50,7 @@
- {var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($vouchers)) < $count} + {var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($vouchers)) < $count} ⭁ туда diff --git a/Web/Presenters/templates/Auth/Register.xml b/Web/Presenters/templates/Auth/Register.xml index bf6ba594..ca47e712 100644 --- a/Web/Presenters/templates/Auth/Register.xml +++ b/Web/Presenters/templates/Auth/Register.xml @@ -47,7 +47,7 @@ {_"gender"}: - {var femalePreferred = OPENVK_ROOT_CONF["openvk"]["preferences"]["femaleGenderPriority"]} + {var $femalePreferred = OPENVK_ROOT_CONF["openvk"]["preferences"]["femaleGenderPriority"]} {_group_display_only_creator}
{_group_display_all_administrators}
{_group_dont_display_administrators_list}
diff --git a/Web/Presenters/templates/Group/Followers.xml b/Web/Presenters/templates/Group/Followers.xml index d451daff..90db51c4 100644 --- a/Web/Presenters/templates/Group/Followers.xml +++ b/Web/Presenters/templates/Group/Followers.xml @@ -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))} @@ -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)} {if $manager} diff --git a/Web/Presenters/templates/Group/View.xml b/Web/Presenters/templates/Group/View.xml index 6ab0c055..5e9acaac 100644 --- a/Web/Presenters/templates/Group/View.xml +++ b/Web/Presenters/templates/Group/View.xml @@ -14,6 +14,8 @@ {block content}
+
{strpos($alert, "@") === 0 ? tr(substr($alert, 1)) : $alert}
+
{_"information"}
@@ -41,7 +43,7 @@
- {var followersCount = $club->getFollowersCount()} + {var $followersCount = $club->getFollowersCount()}
{_participants} @@ -91,8 +93,8 @@ {presenter "openvk!Wall->wallEmbedded", -$club->getId()}
- {var author = $club->getOwner()} + {var $author = $club->getOwner()}
- {var managersCount = $club->getManagersCount(true)} + {var $managersCount = $club->getManagersCount(true)}
{_"administrators"} @@ -163,7 +165,7 @@
- {var user = $manager->getUser()} + {var $user = $manager->getUser()}
@@ -203,7 +205,7 @@
- {var cover = $album->getCoverPhoto()} + {var $cover = $album->getCoverPhoto()} {_my_messages} » {$correspondent->getCanonicalName()}
- {var diff = date_diff(date_create(), date_create('@' . $online))} + {var $diff = date_diff(date_create(), date_create('@' . $online))} {if 5 >= $diff->i} {_online} {else} diff --git a/Web/Presenters/templates/Messenger/Index.xml b/Web/Presenters/templates/Messenger/Index.xml index f0a12371..5397fde4 100644 --- a/Web/Presenters/templates/Messenger/Index.xml +++ b/Web/Presenters/templates/Messenger/Index.xml @@ -21,8 +21,8 @@
- {var recipient = $coresp->getCorrespondents()[1]} - {var lastMsg = $coresp->getPreviewMessage()} + {var $recipient = $coresp->getCorrespondents()[1]} + {var $lastMsg = $coresp->getPreviewMessage()}
{$lastMsg->getSendTime()->format("%e %B %G" . tr("time_at_sp") . "%X")}
- {var _author = $lastMsg->getSender()} + {var $_author = $lastMsg->getSender()}
getOwner()} + {var $author = $note->getOwner()} {$author->getCanonicalName()} » {_notes} diff --git a/Web/Presenters/templates/Notes/List.xml b/Web/Presenters/templates/Notes/List.xml index 838b6d8b..36c48fd3 100644 --- a/Web/Presenters/templates/Notes/List.xml +++ b/Web/Presenters/templates/Notes/List.xml @@ -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 @@
- {var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)} + {var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)} {if sizeof($data) > 0}
diff --git a/Web/Presenters/templates/Notes/View.xml b/Web/Presenters/templates/Notes/View.xml index d87bddfb..e168c237 100644 --- a/Web/Presenters/templates/Notes/View.xml +++ b/Web/Presenters/templates/Notes/View.xml @@ -3,7 +3,7 @@ {block title}{$note->getName()}{/block} {block header} - {var author = $note->getOwner()} + {var $author = $note->getOwner()} {$author->getCanonicalName()} » {_notes} @@ -12,7 +12,7 @@ {/block} {block content} - {var author = $note->getOwner()} + {var $author = $note->getOwner()}