diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index ddc730d6..04a1b344 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1 +1 @@
-custom: "https://openvk.su/donate"
\ No newline at end of file
+custom: "https://ovk.to/donate"
diff --git a/.github/workflows/build-base.yaml b/.github/workflows/build-base.yaml
index 0a98503f..b053d398 100644
--- a/.github/workflows/build-base.yaml
+++ b/.github/workflows/build-base.yaml
@@ -6,7 +6,7 @@ on:
env:
BASE_IMAGE_NAME: php
- BASE_IMAGE_VERSION: "8.1"
+ BASE_IMAGE_VERSION: "8.2"
jobs:
build-cli:
@@ -24,12 +24,18 @@ jobs:
id: buildx
uses: docker/setup-buildx-action@v2
+ - name: Change repository string to lowercase
+ id: repositorystring
+ uses: Entepotenz/change-string-case-action-min-dependencies@v1.1.0
+ with:
+ string: ${{ github.repository }}
+
- name: Log into registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build cli image
run: |
- IMAGE_NAME=ghcr.io/${{ github.repository }}/$BASE_IMAGE_NAME:$BASE_IMAGE_VERSION-cli
+ IMAGE_NAME=ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/$BASE_IMAGE_NAME:$BASE_IMAGE_VERSION-cli
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/base-php-cli.Dockerfile --build-arg VERSION=$BASE_IMAGE_VERSION
@@ -47,12 +53,18 @@ jobs:
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
+
+ - name: Change repository string to lowercase
+ id: repositorystring
+ uses: Entepotenz/change-string-case-action-min-dependencies@v1.1.0
+ with:
+ string: ${{ github.repository }}
- name: Log into registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
-
+
- name: Build apache image
run: |
- IMAGE_NAME=ghcr.io/${{ github.repository }}/$BASE_IMAGE_NAME:$BASE_IMAGE_VERSION-apache
+ IMAGE_NAME=ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/$BASE_IMAGE_NAME:$BASE_IMAGE_VERSION-apache
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/base-php-apache.Dockerfile --build-arg VERSION=$BASE_IMAGE_VERSION
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index d4645520..24363f97 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -36,12 +36,18 @@ jobs:
id: buildx
uses: docker/setup-buildx-action@v2
+ - name: Change repository string to lowercase
+ id: repositorystring
+ uses: Entepotenz/change-string-case-action-min-dependencies@v1.1.0
+ with:
+ string: ${{ github.repository }}
+
- name: Log into registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build base image
run: |
- IMAGE_ID=ghcr.io/${{ github.repository }}/$BASE_IMAGE_NAME
+ IMAGE_ID=ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/$BASE_IMAGE_NAME
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
@@ -49,16 +55,16 @@ jobs:
echo IMAGE_ID=$IMAGE_ID
echo VERSION=$VERSION
- docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_ID:$VERSION . --push -f install/automated/docker/openvk.Dockerfile --build-arg GITREPO=${{ github.repository }}
+ docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_ID:$VERSION . --push -f install/automated/docker/openvk.Dockerfile --build-arg GITREPO=${{ steps.repositorystring.outputs.lowercase }}
- name: Build MariaDB primary image
run: |
- IMAGE_NAME=ghcr.io/${{ github.repository }}/$DB_IMAGE_NAME:$DB_VERSION-primary
+ IMAGE_NAME=ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/$DB_IMAGE_NAME:$DB_VERSION-primary
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/mariadb-primary.Dockerfile --build-arg VERSION=$DB_VERSION
- name: Build MariaDB event image
run: |
- IMAGE_NAME=ghcr.io/${{ github.repository }}/$EVENT_IMAGE_NAME:$DB_VERSION-eventdb
+ IMAGE_NAME=ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/$EVENT_IMAGE_NAME:$DB_VERSION-eventdb
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/mariadb-eventdb.Dockerfile --build-arg VERSION=$DB_VERSION
\ No newline at end of file
diff --git a/Email/hello.eml.latte b/Email/hello.eml.latte
index 48fd562e..8ae66499 100644
--- a/Email/hello.eml.latte
+++ b/Email/hello.eml.latte
@@ -12,7 +12,7 @@
- Добро пожаловать в OpenVK! Приятного времяприпровождения, надеюсь вам понравится.
Если появились вопросы, касаемые нашего сайта, пишите сюда
+ Добро пожаловать в OpenVK! Приятного времяприпровождения, надеюсь вам понравится.
Если появились вопросы, касаемые нашего сайта, пишите сюда
|
diff --git a/README.md b/README.md
index 934ff0b7..82df13e7 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# OpenVK
+# OpenVK
_[Русский](README_RU.md)_
@@ -6,7 +6,7 @@ _[Русский](README_RU.md)_
VKontakte belongs to Pavel Durov and VK Group.
-To be honest, we don't know whether if it even works. However, this version is maintained and we will be happy to accept your bugreports [in our bug tracker](https://github.com/openvk/openvk/projects/1). You should also be able to submit them using [ticketing system](https://openvk.su/support?act=new) (you will need an OpenVK account for this).
+To be honest, we don't know whether if it even works. However, this version is maintained and we will be happy to accept your bugreports [in our bug tracker](https://github.com/openvk/openvk/projects/1). You should also be able to submit them using [ticketing system](https://ovk.to/support?act=new) (you will need an OpenVK account for this).
## When's the release?
@@ -26,6 +26,14 @@ However, OVK makes use of Chandler Application Server. This software requires ex
If you want, you can add your instance to the list above so that people can register there.
+### System requirements
+
+Here is our minimum hardware recommendation:
+
+* **CPU: Recent** (AMD Zen2 or equivalent) quad-core 2GHz+ CPU
+* **RAM:** At least 2GB RAM (we recommend 6GB or 8GB for OpenVK with Kafka)
+* **Minimum database space:** 10GB
+
### Installation procedure
1. Install PHP 7.4, web-server, Composer, Node.js, Yarn and [Chandler](https://github.com/openvk/chandler)
@@ -66,12 +74,12 @@ Once you are done, you can login as a system administrator on the network itself
* **Password**: `admin`
* It is recommended to change the password of the built-in account or disable it.
-💡Confused? Full installation walkthrough is available [here](https://docs.openvk.uk/openvk_engine/centos8_installation/) (CentOS 8 [and](https://almalinux.org/) [family](https://yum.oracle.com/oracle-linux-isos.html)).
+💡 Confused? Full installation walkthrough is available [here](https://docs.ovk.to/openvk_engine/centos8_installation/) (CentOS 8 [and](https://almalinux.org/) [family](https://yum.oracle.com/oracle-linux-isos.html)).
### Looking for Docker or Kubernetes deployment?
See `install/automated/docker/README.md` and `install/automated/kubernetes/README.md` for Docker and Kubernetes deployment instructions.
-### If my website uses OpenVK, should I release it's sources?
+### If my website uses OpenVK, should I release its sources?
It depends. You can keep the sources to yourself if you do not plan to distribute your website binaries. If your website software must be distributed, it can stay non-OSS provided the OpenVK is not used as a primary application and is not modified. If you modified OpenVK for your needs or your work is based on it and you are planning to redistribute this, then you should license it under terms of any LGPL-compatible license (like OSL, GPL, LGPL etc).
@@ -80,7 +88,7 @@ It depends. You can keep the sources to yourself if you do not plan to distribut
You may reach out to us via:
* [Bug Tracker](https://github.com/openvk/openvk/projects/1)
-* [Ticketing System](https://openvk.su/support?act=new)
+* [Ticketing System](https://ovk.to/support?act=new)
* Telegram Chat: Go to [our channel](https://t.me/openvkenglish) and open discussion in our channel menu.
* [Reddit](https://www.reddit.com/r/openvk/)
* [GitHub Discussions](https://github.com/openvk/openvk/discussions)
diff --git a/README_RU.md b/README_RU.md
index 7de91c39..fa09cabe 100644
--- a/README_RU.md
+++ b/README_RU.md
@@ -1,4 +1,4 @@
-# OpenVK
+# OpenVK
_[English](README.md)_
@@ -6,7 +6,7 @@ _[English](README.md)_
ВКонтакте принадлежит Павлу Дурову и VK Group.
-Честно говоря, мы даже не знаем, работает ли она вообще. Однако, эта версия поддерживается, и мы будем рады принять ваши сообщения об ошибках [в нашем баг-трекере](https://github.com/openvk/openvk/projects/1). Вы также можете отправлять их через [вкладку "Помощь"](https://openvk.su/support?act=new) (для этого вам понадобится учетная запись OpenVK).
+Честно говоря, мы даже не знаем, работает ли она вообще. Однако, эта версия поддерживается, и мы будем рады принять ваши сообщения об ошибках [в нашем баг-трекере](https://github.com/openvk/openvk/projects/1). Вы также можете отправлять их через [вкладку "Помощь"](https://ovk.to/support?act=new) (для этого вам понадобится учетная запись OpenVK).
## Когда выйдет релизная версия?
@@ -66,7 +66,7 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
* **Пароль**: `admin`
* Перед использованием встроенной учетной записи рекомендуется сменить пароль или отключить её.
-💡Запутались? Полное руководство по установке доступно [здесь](https://docs.openvk.uk/openvk_engine/centos8_installation/) (CentOS 8 [и](https://almalinux.org/ru/) [семейство](https://yum.oracle.com/oracle-linux-isos.html)).
+💡Запутались? Полное руководство по установке доступно [здесь](https://docs.ovk.to/openvk_engine/centos8_installation/) (CentOS 8 [и](https://almalinux.org/ru/) [семейство](https://yum.oracle.com/oracle-linux-isos.html)).
# Установка в Docker/Kubernetes
Подробные иструкции можно найти в `install/automated/docker/README.md` и `install/automated/kubernetes/README.md` соответственно.
@@ -80,7 +80,7 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
Вы можете связаться с нами через:
* [Баг-трекер](https://github.com/openvk/openvk/projects/1)
-* [Помощь в OVK](https://openvk.su/support?act=new)
+* [Помощь в OVK](https://ovk.to/support?act=new)
* Telegram-чат: Перейдите на [наш канал](https://t.me/openvk) и откройте обсуждение в меню нашего канала.
* [Reddit](https://www.reddit.com/r/openvk/)
* [GitHub Discussions](https://github.com/openvk/openvk/discussions)
diff --git a/ServiceAPI/Apps.php b/ServiceAPI/Apps.php
index 6504c23e..a09dc49f 100644
--- a/ServiceAPI/Apps.php
+++ b/ServiceAPI/Apps.php
@@ -1,8 +1,11 @@
withdrawCoins();
$resolve($coins);
}
+
+ function getRegularToken(string $clientName, bool $acceptsStale, callable $resolve, callable $reject): void
+ {
+ $token = NULL;
+ $stale = true;
+ if($acceptsStale)
+ $token = (new APITokens)->getStaleByUser($this->user->getId(), $clientName);
+
+ if(is_null($token)) {
+ $stale = false;
+ $token = new APIToken;
+ $token->setUser($this->user);
+ $token->setPlatform($clientName ?? (new WhichBrowser\Parser(getallheaders()))->toString());
+ $token->save();
+ }
+
+ $resolve([
+ 'is_stale' => $stale,
+ 'token' => $token->getFormattedToken(),
+ ]);
+ }
}
\ No newline at end of file
diff --git a/ServiceAPI/Search.php b/ServiceAPI/Search.php
deleted file mode 100644
index de0f9d2c..00000000
--- a/ServiceAPI/Search.php
+++ /dev/null
@@ -1,76 +0,0 @@
-user = $user;
- $this->users = new Users;
- $this->clubs = new Clubs;
- $this->videos = new Videos;
- }
-
- function fastSearch(string $query, string $type = "users", callable $resolve, callable $reject)
- {
- if($query == "" || strlen($query) < 3)
- $reject(12, "No input or input < 3");
-
- $repo;
- $sort;
-
- switch($type) {
- default:
- case "users":
- $repo = (new Users);
- $sort = "rating DESC";
-
- break;
- case "groups":
- $repo = (new Clubs);
- $sort = "id ASC";
-
- break;
- case "videos":
- $repo = (new Videos);
- $sort = "created ASC";
-
- break;
- }
-
- $res = $repo->find($query, ["doNotSearchMe" => $this->user->getId(), "doNotSearchPrivate" => true,], $sort);
-
- $results = array_slice(iterator_to_array($res), 0, 5);
-
- $count = sizeof($results);
-
- $arr = [
- "count" => $count,
- "items" => []
- ];
-
- if(sizeof($results) < 1) {
- $reject(2, "No results");
- }
-
- foreach($results as $res) {
- $arr["items"][] = [
- "id" => $res->getId(),
- "name" => $type == "users" ? $res->getCanonicalName() : $res->getName(),
- "avatar" => $type != "videos" ? $res->getAvatarUrl() : $res->getThumbnailURL(),
- "url" => $type != "videos" ? $res->getUrl() : "/video".$res->getPrettyId(),
- "description" => ovk_proc_strtr($res->getDescription() ?? "...", 40)
- ];
- }
-
- $resolve($arr);
- }
-}
diff --git a/VKAPI/Handlers/Account.php b/VKAPI/Handlers/Account.php
index 16308b97..8c0cb343 100644
--- a/VKAPI/Handlers/Account.php
+++ b/VKAPI/Handlers/Account.php
@@ -7,20 +7,32 @@ final class Account extends VKAPIRequestHandler
function getProfileInfo(): object
{
$this->requireUser();
-
- return (object) [
- "first_name" => $this->getUser()->getFirstName(),
- "id" => $this->getUser()->getId(),
- "last_name" => $this->getUser()->getLastName(),
- "home_town" => $this->getUser()->getHometown(),
- "status" => $this->getUser()->getStatus(),
- "audio_status" => is_null($this->getUser()->getCurrentAudioStatus()) ? NULL : $this->getUser()->getCurrentAudioStatus()->toVkApiStruct($this->getUser()),
- "bdate" => is_null($this->getUser()->getBirthday()) ? '01.01.1970' : $this->getUser()->getBirthday()->format('%e.%m.%Y'),
- "bdate_visibility" => $this->getUser()->getBirthdayPrivacy(),
+ $user = $this->getUser();
+ $return_object = (object) [
+ "first_name" => $user->getFirstName(),
+ "photo_200" => $user->getAvatarURL("normal"),
+ "nickname" => $user->getPseudo(),
+ "is_service_account" => false,
+ "id" => $user->getId(),
+ "is_verified" => $user->isVerified(),
+ "verification_status" => $user->isVerified() ? 'verified' : 'unverified',
+ "last_name" => $user->getLastName(),
+ "home_town" => $user->getHometown(),
+ "status" => $user->getStatus(),
+ "bdate" => is_null($user->getBirthday()) ? '01.01.1970' : $user->getBirthday()->format('%e.%m.%Y'),
+ "bdate_visibility" => $user->getBirthdayPrivacy(),
"phone" => "+420 ** *** 228", # TODO
- "relation" => $this->getUser()->getMaritalStatus(),
- "sex" => $this->getUser()->isFemale() ? 1 : 2
+ "relation" => $user->getMaritalStatus(),
+ "screen_name" => $user->getShortCode(),
+ "sex" => $user->isFemale() ? 1 : 2,
+ #"email" => $user->getEmail(),
];
+
+ $audio_status = $user->getCurrentAudioStatus();
+ if(!is_null($audio_status))
+ $return_object->audio_status = $audio_status->toVkApiStruct($user);
+
+ return $return_object;
}
function getInfo(): object
@@ -152,4 +164,30 @@ final class Account extends VKAPIRequestHandler
return (object) $output;
}
+
+ function getBalance(): object
+ {
+ $this->requireUser();
+ if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce'])
+ $this->fail(105, "Commerce is disabled on this instance");
+
+ return (object) ['votes' => $this->getUser()->getCoins()];
+ }
+
+ function getOvkSettings(): object
+ {
+ $this->requireUser();
+ $user = $this->getUser();
+
+ $settings_list = (object)[
+ 'avatar_style' => $user->getStyleAvatar(),
+ 'style' => $user->getStyle(),
+ 'show_rating' => !$user->prefersNotToSeeRating(),
+ 'nsfw_tolerance' => $user->getNsfwTolerance(),
+ 'post_view' => $user->hasMicroblogEnabled() ? 'microblog' : 'old',
+ 'main_page' => $user->getMainPage() == 0 ? 'my_page' : 'news',
+ ];
+
+ return $settings_list;
+ }
}
diff --git a/VKAPI/Handlers/Audio.php b/VKAPI/Handlers/Audio.php
index 413a2a3a..e0991af9 100644
--- a/VKAPI/Handlers/Audio.php
+++ b/VKAPI/Handlers/Audio.php
@@ -485,7 +485,7 @@ final class Audio extends VKAPIRequestHandler
$this->requireUser();
$this->willExecuteWriteAction();
- if(!is_null($album_id))
+ if(!is_null($album_id))
$this->fail(10, "album_id not implemented");
// TODO get rid of dups
@@ -581,13 +581,18 @@ final class Audio extends VKAPIRequestHandler
];
}
- function searchAlbums(string $query, int $offset = 0, int $limit = 25, int $drop_private = 0): object
+ function searchAlbums(string $query = '', int $offset = 0, int $limit = 25, int $drop_private = 0, int $order = 0, int $from_me = 0): object
{
$this->requireUser();
$playlists = [];
- $search = (new Audios)->searchPlaylists($query)->offsetLimit($offset, $limit);
- foreach($search as $playlist) {
+ $params = [];
+ $order_str = (['id', 'length', 'listens'][$order] ?? 'id');
+ if($from_me === 1)
+ $params['from_me'] = $this->getUser()->getId();
+
+ $search = (new Audios)->findPlaylists($query, $params, ['type' => $order_str, 'invert' => false]);
+ foreach($search->offsetLimit($offset, $limit) as $playlist) {
if(!$playlist->canBeViewedBy($this->getUser())) {
if($drop_private == 0)
$playlists[] = NULL;
@@ -599,7 +604,7 @@ final class Audio extends VKAPIRequestHandler
}
return (object) [
- "count" => sizeof($playlists),
+ "count" => $search->size(),
"items" => $playlists,
];
}
diff --git a/VKAPI/Handlers/Friends.php b/VKAPI/Handlers/Friends.php
index 56af8d11..51046be4 100644
--- a/VKAPI/Handlers/Friends.php
+++ b/VKAPI/Handlers/Friends.php
@@ -147,7 +147,7 @@ final class Friends extends VKAPIRequestHandler
return $response;
}
- function getRequests(string $fields = "", int $offset = 0, int $count = 100, int $extended = 0): object
+ function getRequests(string $fields = "", int $out = 0, int $offset = 0, int $count = 100, int $extended = 0): object
{
if ($count >= 1000)
$this->fail(100, "One of the required parameters was not passed or is invalid.");
@@ -158,9 +158,18 @@ final class Friends extends VKAPIRequestHandler
$offset++;
$followers = [];
- foreach($this->getUser()->getFollowers($offset, $count) as $follower) {
- $followers[$i] = $follower->getId();
- $i++;
+ if ($out != 0) {
+ foreach($this->getUser()->getFollowers($offset, $count) as $follower) {
+ $followers[$i] = $follower->getId();
+ $i++;
+ }
+ }
+ else
+ {
+ foreach($this->getUser()->getRequests($offset, $count) as $follower) {
+ $followers[$i] = $follower->getId();
+ $i++;
+ }
}
$response = $followers;
diff --git a/VKAPI/Handlers/Gifts.php b/VKAPI/Handlers/Gifts.php
index dd6fa07a..0d1fc598 100644
--- a/VKAPI/Handlers/Gifts.php
+++ b/VKAPI/Handlers/Gifts.php
@@ -6,15 +6,18 @@ use openvk\Web\Models\Entities\Notifications\GiftNotification;
final class Gifts extends VKAPIRequestHandler
{
- function get(int $user_id, int $count = 10, int $offset = 0)
+ function get(int $user_id = NULL, int $count = 10, int $offset = 0)
{
$this->requireUser();
$i = 0;
-
$i += $offset;
+ $server_url = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
- $user = (new UsersRepo)->get($user_id);
+ if($user_id)
+ $user = (new UsersRepo)->get($user_id);
+ else
+ $user = $this->getUser();
if(!$user || $user->isDeleted())
$this->fail(177, "Invalid user");
@@ -47,9 +50,9 @@ final class Gifts extends VKAPIRequestHandler
"date" => $gift->sent->timestamp(),
"gift" => [
"id" => $gift->gift->getId(),
- "thumb_256" => $gift->gift->getImage(2),
- "thumb_96" => $gift->gift->getImage(2),
- "thumb_48" => $gift->gift->getImage(2)
+ "thumb_256" => $server_url. $gift->gift->getImage(2),
+ "thumb_96" => $server_url . $gift->gift->getImage(2),
+ "thumb_48" => $server_url . $gift->gift->getImage(2)
],
"privacy" => 0
];
@@ -125,12 +128,13 @@ final class Gifts extends VKAPIRequestHandler
$this->fail(501, "Not implemented");
}
- # этих методов не было в ВК, но я их добавил чтобы можно было отобразить список подарков
+ # в vk кстати называется gifts.getCatalog
function getCategories(bool $extended = false, int $page = 1)
{
$cats = (new GiftsRepo)->getCategories($page);
$categ = [];
$i = 0;
+ $server_url = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
if(!OPENVK_ROOT_CONF['openvk']['preferences']['commerce'])
$this->fail(105, "Commerce is disabled on this instance");
@@ -140,8 +144,8 @@ final class Gifts extends VKAPIRequestHandler
"name" => $cat->getName(),
"description" => $cat->getDescription(),
"id" => $cat->getId(),
- "thumbnail" => $cat->getThumbnailURL(),
- ];
+ "thumbnail" => $server_url . $cat->getThumbnailURL(),
+ ];
if($extended == true) {
$categ[$i]["localizations"] = [];
@@ -178,7 +182,7 @@ final class Gifts extends VKAPIRequestHandler
"name" => $gift->getName(),
"image" => $gift->getImage(2),
"usages_left" => (int)$gift->getUsagesLeft($this->getUser()),
- "price" => $gift->getPrice(), # голосов
+ "price" => $gift->getPrice(),
"is_free" => $gift->isFree()
];
}
diff --git a/VKAPI/Handlers/Groups.php b/VKAPI/Handlers/Groups.php
index ffa4fedd..f8706191 100644
--- a/VKAPI/Handlers/Groups.php
+++ b/VKAPI/Handlers/Groups.php
@@ -88,6 +88,10 @@ final class Groups extends VKAPIRequestHandler
case "can_suggest":
$rClubs[$i]->can_suggest = !$usr->canBeModifiedBy($this->getUser()) && $usr->getWallType() == 2;
break;
+ case "background":
+ $backgrounds = $usr->getBackDropPictureURLs();
+ $rClubs[$i]->background = $backgrounds;
+ break;
# unstandard feild
case "suggested_count":
if($usr->getWallType() != 2) {
@@ -208,6 +212,10 @@ final class Groups extends VKAPIRequestHandler
case "can_suggest":
$response[$i]->can_suggest = !$clb->canBeModifiedBy($this->getUser()) && $clb->getWallType() == 2;
break;
+ case "background":
+ $backgrounds = $clb->getBackDropPictureURLs();
+ $response[$i]->background = $backgrounds;
+ break;
# unstandard feild
case "suggested_count":
if($clb->getWallType() != 2) {
@@ -244,23 +252,30 @@ final class Groups extends VKAPIRequestHandler
return $response;
}
- function search(string $q, int $offset = 0, int $count = 100)
+ function search(string $q, int $offset = 0, int $count = 100, string $fields = "screen_name,is_admin,is_member,is_advertiser,photo_50,photo_100,photo_200")
{
+ if($count > 100) {
+ $this->fail(100, "One of the parameters specified was missing or invalid: count should be less or equal to 100");
+ }
+
$clubs = new ClubsRepo;
$array = [];
$find = $clubs->find($q);
- foreach ($find as $group)
+ foreach ($find->offsetLimit($offset, $count) as $group)
$array[] = $group->getId();
+
+ if(!$array || sizeof($array) < 1) {
+ return (object) [
+ "count" => 0,
+ "items" => [],
+ ];
+ }
return (object) [
"count" => $find->size(),
- "items" => $this->getById(implode(',', $array), "", "is_admin,is_member,is_advertiser,photo_50,photo_100,photo_200", $offset, $count)
- /*
- * As there is no thing as "fields" by the original documentation
- * i'll just bake this param by the example shown here: https://dev.vk.com/method/groups.search
- */
+ "items" => $this->getById(implode(',', $array), "", $fields)
];
}
@@ -347,7 +362,10 @@ final class Groups extends VKAPIRequestHandler
!empty($topics) ? $club->setEveryone_Can_Create_Topics($topics) : NULL;
!empty($adminlist) ? $club->setAdministrators_List_Display($adminlist) : NULL;
!empty($topicsAboveWall) ? $club->setDisplay_Topics_Above_Wall($topicsAboveWall) : NULL;
- !empty($hideFromGlobalFeed) ? $club->setHide_From_Global_Feed($hideFromGlobalFeed) : NULL;
+
+ if (!$club->isHidingFromGlobalFeedEnforced()) {
+ !empty($hideFromGlobalFeed) ? $club->setHide_From_Global_Feed($hideFromGlobalFeed) : NULL;
+ }
in_array($audio, [0, 1]) ? $club->setEveryone_can_upload_audios($audio) : NULL;
diff --git a/VKAPI/Handlers/Users.php b/VKAPI/Handlers/Users.php
index 961d0c5f..704823be 100644
--- a/VKAPI/Handlers/Users.php
+++ b/VKAPI/Handlers/Users.php
@@ -151,7 +151,6 @@ final class Users extends VKAPIRequestHandler
}
case "music":
if(!$canView) {
- $response[$i]->music = "secret";
break;
}
@@ -159,7 +158,6 @@ final class Users extends VKAPIRequestHandler
break;
case "movies":
if(!$canView) {
- $response[$i]->movies = "secret";
break;
}
@@ -167,7 +165,6 @@ final class Users extends VKAPIRequestHandler
break;
case "tv":
if(!$canView) {
- $response[$i]->tv = "secret";
break;
}
@@ -175,7 +172,6 @@ final class Users extends VKAPIRequestHandler
break;
case "books":
if(!$canView) {
- $response[$i]->books = "secret";
break;
}
@@ -183,7 +179,6 @@ final class Users extends VKAPIRequestHandler
break;
case "city":
if(!$canView) {
- $response[$i]->city = "Воскресенск";
break;
}
@@ -191,7 +186,6 @@ final class Users extends VKAPIRequestHandler
break;
case "interests":
if(!$canView) {
- $response[$i]->interests = "secret";
break;
}
@@ -199,7 +193,6 @@ final class Users extends VKAPIRequestHandler
break;
case "quotes":
if(!$canView) {
- $response[$i]->quotes = "secret";
break;
}
@@ -207,7 +200,6 @@ final class Users extends VKAPIRequestHandler
break;
case "email":
if(!$canView) {
- $response[$i]->email = "secret@gmail.com";
break;
}
@@ -215,7 +207,6 @@ final class Users extends VKAPIRequestHandler
break;
case "telegram":
if(!$canView) {
- $response[$i]->telegram = "@secret";
break;
}
@@ -223,7 +214,6 @@ final class Users extends VKAPIRequestHandler
break;
case "about":
if(!$canView) {
- $response[$i]->about = "secret";
break;
}
@@ -231,7 +221,6 @@ final class Users extends VKAPIRequestHandler
break;
case "rating":
if(!$canView) {
- $response[$i]->rating = 22;
break;
}
@@ -240,12 +229,43 @@ final class Users extends VKAPIRequestHandler
case "counters":
$response[$i]->counters = (object) [
"friends_count" => $usr->getFriendsCount(),
- "photos_count" => (new Albums)->getUserPhotosCount($usr),
+ "photos_count" => (new Photos)->getUserPhotosCount($usr),
"videos_count" => (new Videos)->getUserVideosCount($usr),
"audios_count" => (new Audios)->getUserCollectionSize($usr),
"notes_count" => (new Notes)->getUserNotesCount($usr)
];
break;
+ case "correct_counters":
+ $response[$i]->counters = (object) [
+ "friends" => $usr->getFriendsCount(),
+ "photos" => (new Photos)->getUserPhotosCount($usr),
+ "videos" => (new Videos)->getUserVideosCount($usr),
+ "audios" => (new Audios)->getUserCollectionSize($usr),
+ "notes" => (new Notes)->getUserNotesCount($usr),
+ "groups" => $usr->getClubCount(),
+ "online_friends" => $usr->getFriendsOnlineCount(),
+ ];
+ break;
+ case "guid":
+ $response[$i]->guid = $usr->getChandlerGUID();
+ break;
+ case 'background':
+ $backgrounds = $usr->getBackDropPictureURLs();
+ $response[$i]->background = $backgrounds;
+ break;
+ case 'reg_date':
+ if(!$canView) {
+ break;
+ }
+
+ $response[$i]->reg_date = $usr->getRegistrationTime()->timestamp();
+ break;
+ case 'is_dead':
+ $response[$i]->is_dead = $usr->isDead();
+ break;
+ case 'nickname':
+ $response[$i]->nickname = $usr->getPseudo();
+ break;
}
}
@@ -297,89 +317,90 @@ final class Users extends VKAPIRequestHandler
int $count = 100,
string $city = "",
string $hometown = "",
- int $sex = 2,
- int $status = 0, # это про marital status
+ int $sex = 3,
+ int $status = 0, # marital_status
bool $online = false,
- # дальше идут параметры которых нету в vkapi но есть на сайте
- string $profileStatus = "", # а это уже нормальный статус
+ # non standart params:
int $sort = 0,
- int $before = 0,
- int $politViews = 0,
- int $after = 0,
- string $interests = "",
+ int $polit_views = 0,
string $fav_music = "",
string $fav_films = "",
string $fav_shows = "",
- string $fav_books = "",
- string $fav_quotes = ""
+ string $fav_books = ""
)
{
- $users = new UsersRepo;
-
- $sortg = "id ASC";
+ if($count > 100) {
+ $this->fail(100, "One of the parameters specified was missing or invalid: count should be less or equal to 100");
+ }
- $nfilds = $fields;
+ $users = new UsersRepo;
+ $output_sort = ['type' => 'id', 'invert' => false];
+ $output_params = [
+ "ignore_private" => true,
+ ];
switch($sort) {
+ default:
case 0:
- $sortg = "id DESC";
+ $output_sort = ['type' => 'id', 'invert' => false];
break;
case 1:
- $sortg = "id ASC";
- break;
- case 2:
- $sortg = "first_name DESC";
- break;
- case 3:
- $sortg = "first_name ASC";
+ $output_sort = ['type' => 'id', 'invert' => true];
break;
case 4:
- $sortg = "rating DESC";
-
- if(!str_contains($nfilds, "rating")) {
- $nfilds .= "rating";
- }
-
- break;
- case 5:
- $sortg = "rating DESC";
-
- if(!str_contains($nfilds, "rating")) {
- $nfilds .= "rating";
- }
-
+ $output_sort = ['type' => 'rating', 'invert' => false];
break;
}
+ if(!empty($city))
+ $output_params['city'] = $city;
+
+ if(!empty($hometown))
+ $output_params['hometown'] = $hometown;
+
+ if($sex != 3)
+ $output_params['gender'] = $sex;
+
+ if($status != 0)
+ $output_params['marital_status'] = $status;
+
+ if($polit_views != 0)
+ $output_params['polit_views'] = $polit_views;
+
+ if(!empty($interests))
+ $output_params['interests'] = $interests;
+
+ if(!empty($fav_music))
+ $output_params['fav_music'] = $fav_music;
+
+ if(!empty($fav_films))
+ $output_params['fav_films'] = $fav_films;
+
+ if(!empty($fav_shows))
+ $output_params['fav_shows'] = $fav_shows;
+
+ if(!empty($fav_books))
+ $output_params['fav_books'] = $fav_books;
+
+ if($online)
+ $output_params['is_online'] = 1;
+
$array = [];
+ $find = $users->find($q, $output_params, $output_sort);
- $parameters = [
- "city" => !empty($city) ? $city : NULL,
- "hometown" => !empty($hometown) ? $hometown : NULL,
- "gender" => $sex < 2 ? $sex : NULL,
- "maritalstatus" => (bool)$status ? $status : NULL,
- "politViews" => (bool)$politViews ? $politViews : NULL,
- "is_online" => $online ? 1 : NULL,
- "status" => !empty($profileStatus) ? $profileStatus : NULL,
- "before" => $before != 0 ? $before : NULL,
- "after" => $after != 0 ? $after : NULL,
- "interests" => !empty($interests) ? $interests : NULL,
- "fav_music" => !empty($fav_music) ? $fav_music : NULL,
- "fav_films" => !empty($fav_films) ? $fav_films : NULL,
- "fav_shows" => !empty($fav_shows) ? $fav_shows : NULL,
- "fav_books" => !empty($fav_books) ? $fav_books : NULL,
- "fav_quotes" => !empty($fav_quotes) ? $fav_quotes : NULL,
- "doNotSearchPrivate" => true,
- ];
-
- $find = $users->find($q, $parameters, $sortg);
-
- foreach ($find as $user)
+ foreach ($find->offsetLimit($offset, $count) as $user)
$array[] = $user->getId();
+ if(!$array || sizeof($array) < 1) {
+ return (object) [
+ "count" => 0,
+ "items" => [],
+ ];
+ }
+
return (object) [
- "count" => $find->size(),
- "items" => $this->get(implode(',', $array), $nfilds, $offset, $count)
+ "count" => $find->size(),
+ "items" => $this->get(implode(',', $array), $fields)
];
}
diff --git a/VKAPI/Handlers/Utils.php b/VKAPI/Handlers/Utils.php
index 5350a64f..a1c4f3b5 100644
--- a/VKAPI/Handlers/Utils.php
+++ b/VKAPI/Handlers/Utils.php
@@ -22,7 +22,7 @@ final class Utils extends VKAPIRequestHandler
"object_id" => (int) substr($screen_name, strlen("club")),
"type" => "group"
];
- }
+ } else $this->fail(104, "Not found");
} else {
$user = (new Users)->getByShortURL($screen_name);
if($user) {
@@ -39,8 +39,17 @@ final class Utils extends VKAPIRequestHandler
"type" => "group"
];
}
-
- return (object) [];
+
+ $this->fail(104, "Not found");
}
}
+
+ function resolveGuid(string $guid): object
+ {
+ $user = (new Users)->getByChandlerUserId($guid);
+ if (is_null($user))
+ $this->fail(104, "Not found");
+
+ return $user->toVkApiStruct($this->getUser());
+ }
}
diff --git a/VKAPI/Handlers/Video.php b/VKAPI/Handlers/Video.php
index f468686d..d933f728 100755
--- a/VKAPI/Handlers/Video.php
+++ b/VKAPI/Handlers/Video.php
@@ -60,4 +60,60 @@ final class Video extends VKAPIRequestHandler
];
}
}
+
+ function search(string $q = '', int $sort = 0, int $offset = 0, int $count = 10, bool $extended = false, string $fields = ''): object
+ {
+ $this->requireUser();
+
+ $params = [];
+ $db_sort = ['type' => 'id', 'invert' => false];
+ $videos = (new VideosRepo)->find($q, $params, $db_sort);
+ $items = iterator_to_array($videos->offsetLimit($offset, $count));
+ $count = $videos->size();
+
+ $return_items = [];
+ $profiles = [];
+ $groups = [];
+ foreach($items as $item)
+ $return_item = $item->getApiStructure($this->getUser());
+ $return_item = $return_item->video;
+ $return_items[] = $return_item;
+
+ if($return_item['owner_id']) {
+ if($return_item['owner_id'] > 0)
+ $profiles[] = $return_item['owner_id'];
+ else
+ $groups[] = abs($return_item['owner_id']);
+ }
+
+ if($extended) {
+ $profiles = array_unique($profiles);
+ $groups = array_unique($groups);
+
+ $profilesFormatted = [];
+ $groupsFormatted = [];
+
+ foreach($profiles as $prof) {
+ $profile = (new UsersRepo)->get($prof);
+ $profilesFormatted[] = $profile->toVkApiStruct($this->getUser(), $fields);
+ }
+
+ foreach($groups as $gr) {
+ $group = (new ClubsRepo)->get($gr);
+ $groupsFormatted[] = $group->toVkApiStruct($this->getUser(), $fields);
+ }
+
+ return (object) [
+ "count" => $count,
+ "items" => $return_items,
+ "profiles" => $profilesFormatted,
+ "groups" => $groupsFormatted,
+ ];
+ }
+
+ return (object) [
+ "count" => $count,
+ "items" => $return_items,
+ ];
+ }
}
diff --git a/VKAPI/Handlers/Wall.php b/VKAPI/Handlers/Wall.php
index 3cbc7648..a305fde6 100644
--- a/VKAPI/Handlers/Wall.php
+++ b/VKAPI/Handlers/Wall.php
@@ -126,65 +126,41 @@ final class Wall extends VKAPIRequestHandler
else
$profiles[] = $attachment->getOwner()->getId();
- $post_source = [];
-
- if($attachment->getPlatform(true) === NULL) {
- $post_source = (object)["type" => "vk"];
- } else {
- $post_source = (object)[
- "type" => "api",
- "platform" => $attachment->getPlatform(true)
- ];
- }
-
$repost[] = [
"id" => $attachment->getVirtualId(),
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
"from_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
"date" => $attachment->getPublicationTime()->timestamp(),
- "post_type" => "post",
+ "post_type" => $attachment->getVkApiType(),
"text" => $attachment->getText(false),
"attachments" => $repostAttachments,
- "post_source" => $post_source,
+ "post_source" => $attachment->getPostSourceInfo(),
];
- if ($attachment->getVirtualId() > 0)
- $profiles[] = $attachment->getVirtualId();
+ if ($attachment->getTargetWall() > 0)
+ $profiles[] = $attachment->getTargetWall();
else
- $groups[] = $attachment->getVirtualId();
+ $groups[] = abs($attachment->getTargetWall());
if($post->isSigned())
$profiles[] = $attachment->getOwner()->getId();
}
}
- $post_source = [];
-
- if($post->getPlatform(true) === NULL) {
- $post_source = (object)["type" => "vk"];
- } else {
- $post_source = (object)[
- "type" => "api",
- "platform" => $post->getPlatform(true)
- ];
- }
-
- $postType = "post";
$signerId = NULL;
- if($post->getSuggestionType() != 0)
- $postType = "suggest";
-
-
if($post->isSigned()) {
$actualAuthor = $post->getOwner(false);
$signerId = $actualAuthor->getId();
}
- $items[] = (object)[
+ # TODO "can_pin", "copy_history" и прочее не должны возвращаться, если равны null или false
+ # Ну и ещё всё надо перенести в toVkApiStruct, а то слишком много дублированного кода
+
+ $post_temp_obj = (object)[
"id" => $post->getVirtualId(),
"from_id" => $from_id,
"owner_id" => $post->getTargetWall(),
"date" => $post->getPublicationTime()->timestamp(),
- "post_type" => $postType,
+ "post_type" => $post->getVkApiType(),
"text" => $post->getText(false),
"copy_history" => $repost,
"can_edit" => $post->canBeEditedBy($this->getUser()),
@@ -195,8 +171,7 @@ final class Wall extends VKAPIRequestHandler
"is_pinned" => $post->isPinned(),
"is_explicit" => $post->isExplicit(),
"attachments" => $attachments,
- "post_source" => $post_source,
- "signer_id" => $signerId,
+ "post_source" => $post->getPostSourceInfo(),
"comments" => (object)[
"count" => $post->getCommentsCount(),
"can_post" => 1
@@ -213,6 +188,14 @@ final class Wall extends VKAPIRequestHandler
]
];
+ if($signerId)
+ $post_temp_obj->signer_id = $signerId;
+
+ if($post->isDeactivationMessage())
+ $post_temp_obj->final_post = 1;
+
+ $items[] = $post_temp_obj;
+
if ($from_id > 0)
$profiles[] = $from_id;
else
@@ -332,17 +315,6 @@ final class Wall extends VKAPIRequestHandler
else
$profiles[] = $attachment->getOwner()->getId();
- $post_source = [];
-
- if($attachment->getPlatform(true) === NULL) {
- $post_source = (object)["type" => "vk"];
- } else {
- $post_source = (object)[
- "type" => "api",
- "platform" => $attachment->getPlatform(true)
- ];
- }
-
$repost[] = [
"id" => $attachment->getVirtualId(),
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
@@ -351,47 +323,29 @@ final class Wall extends VKAPIRequestHandler
"post_type" => "post",
"text" => $attachment->getText(false),
"attachments" => $repostAttachments,
- "post_source" => $post_source,
+ "post_source" => $attachment->getPostSourceInfo(),
];
- if ($attachment->getVirtualId() > 0)
- $profiles[] = $attachment->getVirtualId();
+ if ($attachment->getTargetWall() > 0)
+ $profiles[] = $attachment->getTargetWall();
else
- $groups[] = $attachment->getVirtualId();
+ $groups[] = abs($attachment->getTargetWall());
if($post->isSigned())
$profiles[] = $attachment->getOwner()->getId();
}
}
- $post_source = [];
-
- if($post->getPlatform(true) === NULL) {
- $post_source = (object)["type" => "vk"];
- } else {
- $post_source = (object)[
- "type" => "api",
- "platform" => $post->getPlatform(true)
- ];
- }
-
- # TODO: $post->getVkApiType()
- $postType = "post";
- $signerId = NULL;
- if($post->getSuggestionType() != 0)
- $postType = "suggest";
-
-
if($post->isSigned()) {
$actualAuthor = $post->getOwner(false);
$signerId = $actualAuthor->getId();
}
- $items[] = (object)[
+ $post_temp_obj = (object)[
"id" => $post->getVirtualId(),
"from_id" => $from_id,
"owner_id" => $post->getTargetWall(),
"date" => $post->getPublicationTime()->timestamp(),
- "post_type" => $postType,
+ "post_type" => $post->getVkApiType(),
"text" => $post->getText(false),
"copy_history" => $repost,
"can_edit" => $post->canBeEditedBy($this->getUser()),
@@ -401,8 +355,7 @@ final class Wall extends VKAPIRequestHandler
"is_archived" => false,
"is_pinned" => $post->isPinned(),
"is_explicit" => $post->isExplicit(),
- "post_source" => $post_source,
- "signer_id" => $signerId,
+ "post_source" => $post->getPostSourceInfo(),
"attachments" => $attachments,
"comments" => (object)[
"count" => $post->getCommentsCount(),
@@ -420,6 +373,14 @@ final class Wall extends VKAPIRequestHandler
]
];
+ if($signerId)
+ $post_temp_obj->signer_id = $signerId;
+
+ if($post->isDeactivationMessage())
+ $post_temp_obj->final_post = 1;
+
+ $items[] = $post_temp_obj;
+
if ($from_id > 0)
$profiles[] = $from_id;
else
@@ -792,6 +753,9 @@ final class Wall extends VKAPIRequestHandler
]
];
+ if($comment->isFromPostAuthor($post))
+ $item['is_from_post_author'] = true;
+
if($need_likes == true)
$item['likes'] = [
"can_like" => 1,
@@ -875,6 +839,9 @@ final class Wall extends VKAPIRequestHandler
]
];
+ if($comment->isFromPostAuthor())
+ $item['is_from_post_author'] = true;
+
if($extended == true)
$profiles[] = $comment->getOwner()->getId();
@@ -890,8 +857,6 @@ final class Wall extends VKAPIRequestHandler
$response['profiles'] = (!empty($profiles) ? (new Users)->get(implode(',', $profiles), $fields) : []);
}
-
-
return $response;
}
diff --git a/VKAPI/README.md b/VKAPI/README.md
index 75918afc..08c8408b 100644
--- a/VKAPI/README.md
+++ b/VKAPI/README.md
@@ -5,7 +5,7 @@ exceptions. It is still a work-in-progress functionality.
**Note**: requests to API are routed through
openvk.Web.Presenters.VKAPIPresenter, this dir contains only handlers.
-[Documentation for API clients](https://docs.openvk.uk/openvk_engine/api/description/)
+[Documentation for API clients](https://docs.ovk.to/openvk_engine/api/description/)
## Implementing API methods
diff --git a/Web/Models/Entities/Audio.php b/Web/Models/Entities/Audio.php
index 11d8502b..3b998f32 100644
--- a/Web/Models/Entities/Audio.php
+++ b/Web/Models/Entities/Audio.php
@@ -17,7 +17,7 @@ class Audio extends Media
# Taken from winamp :D
const genres = [
- 'Blues','Big Band','Classic Rock','Chorus','Country','Easy Listening','Dance','Acoustic','Disco','Humour','Funk','Speech','Grunge','Chanson','Hip-Hop','Opera','Jazz','Chamber Music','Metal','Sonata','New Age','Symphony','Oldies','Booty Bass','Other','Primus','Pop','Porn Groove','R&B','Satire','Rap','Slow Jam','Reggae','Club','Rock','Tango','Techno','Samba','Industrial','Folklore','Alternative','Ballad','Ska','Power Ballad','Death Metal','Rhythmic Soul','Pranks','Freestyle','Soundtrack','Duet','Euro-Techno','Punk Rock','Ambient','Drum Solo','Trip-Hop','A Cappella','Vocal','Euro-House','Jazz+Funk','Dance Hall','Fusion','Goa','Trance','Drum & Bass','Classical','Club-House','Instrumental','Hardcore','Acid','Terror','House','Indie','Game','BritPop','Sound Clip','Negerpunk','Gospel','Polsk Punk','Noise','Beat','AlternRock','Christian Gangsta Rap','Bass','Heavy Metal','Soul','Black Metal','Punk','Crossover','Space','Contemporary Christian','Meditative','Christian Rock','Instrumental Pop','Merengue','Instrumental Rock','Salsa','Ethnic','Thrash Metal','Gothic','Anime','Darkwave','JPop','Techno-Industrial','Synthpop','Electronic','Abstract','Pop-Folk','Art Rock','Eurodance','Baroque','Dream','Bhangra','Southern Rock','Big Beat','Comedy','Breakbeat','Cult','Chillout','Gangsta Rap','Downtempo','Top 40','Dub','Christian Rap','EBM','Pop / Funk','Eclectic','Jungle','Electro','Native American','Electroclash','Cabaret','Emo','New Wave','Experimental','Psychedelic','Garage','Rave','Global','Showtunes','IDM','Trailer','Illbient','Lo-Fi','Industro-Goth','Tribal','Jam Band','Acid Punk','Krautrock','Acid Jazz','Leftfield','Polka','Lounge','Retro','Math Rock','Musical','New Romantic','Rock & Roll','Nu-Breakz','Hard Rock','Post-Punk','Folk','Post-Rock','Folk-Rock','Psytrance','National Folk','Shoegaze','Swing','Space Rock','Fast Fusion','Trop Rock','Bebob','World Music','Latin','Neoclassical','Revival','Audiobook','Celtic','Audio Theatre','Bluegrass','Neue Deutsche Welle','Avantgarde','Podcast','Gothic Rock','Indie Rock','Progressive Rock','G-Funk','Psychedelic Rock','Dubstep','Symphonic Rock','Garage Rock','Slow Rock','Psybient','Psychobilly','Touhou'
+ 'A Cappella', 'Abstract', 'Acid', 'Acid Jazz', 'Acid Punk', 'Acoustic', 'AlternRock', 'Alternative', 'Ambient', 'Anime', 'Art Rock', 'Audio Theatre', 'Audiobook', 'Avantgarde', 'Ballad', 'Baroque', 'Bass', 'Beat', 'Bebob', 'Bhangra', 'Big Band', 'Big Beat', 'Black Metal', 'Bluegrass', 'Blues', 'Booty Bass', 'Breakbeat', 'BritPop', 'Cabaret', 'Celtic', 'Chamber Music', 'Chanson', 'Chillout', 'Chorus', 'Christian Gangsta Rap', 'Christian Rap', 'Christian Rock', 'Classic Rock', 'Classical', 'Club', 'Club-House', 'Comedy', 'Contemporary Christian', 'Country', 'Crossover', 'Cult', 'Dance', 'Dance Hall', 'Darkwave', 'Death Metal', 'Disco', 'Downtempo', 'Dream', 'Drum & Bass', 'Drum Solo', 'Dub', 'Dubstep', 'Duet', 'EBM', 'Easy Listening', 'Eclectic', 'Electro', 'Electroclash', 'Electronic', 'Emo', 'Ethnic', 'Euro-House', 'Euro-Techno', 'Eurodance', 'Experimental', 'Fast Fusion', 'Folk', 'Folk-Rock', 'Folklore', 'Freestyle', 'Funk', 'Fusion', 'G-Funk', 'Game', 'Gangsta Rap', 'Garage', 'Garage Rock', 'Global', 'Goa', 'Gospel', 'Gothic', 'Gothic Rock', 'Grunge', 'Hard Rock', 'Hardcore', 'Heavy Metal', 'Hip-Hop', 'House', 'Humour', 'IDM', 'Illbient', 'Indie', 'Indie Rock', 'Industrial', 'Industro-Goth', 'Instrumental', 'Instrumental Pop', 'Instrumental Rock', 'JPop', 'Jam Band', 'Jazz', 'Jazz+Funk', 'Jungle', 'Krautrock', 'Latin', 'Leftfield', 'Lo-Fi', 'Lounge', 'Math Rock', 'Meditative', 'Merengue', 'Metal', 'Musical', 'National Folk', 'Native American', 'Negerpunk', 'Neoclassical', 'Neue Deutsche Welle', 'New Age', 'New Romantic', 'New Wave', 'Noise', 'Nu-Breakz', 'Oldies', 'Opera', 'Other', 'Podcast', 'Polka', 'Polsk Punk', 'Pop', 'Pop / Funk', 'Pop-Folk', 'Porn Groove', 'Post-Punk', 'Post-Rock', 'Power Ballad', 'Pranks', 'Primus', 'Progressive Rock', 'Psybient', 'Psychedelic', 'Psychedelic Rock', 'Psychobilly', 'Psytrance', 'Punk', 'Punk Rock', 'R&B', 'Rap', 'Rave', 'Reggae', 'Retro', 'Revival', 'Rhythmic Soul', 'Rock', 'Rock & Roll', 'Salsa', 'Samba', 'Satire', 'Shoegaze', 'Showtunes', 'Ska', 'Slow Jam', 'Slow Rock', 'Sonata', 'Soul', 'Sound Clip', 'Soundtrack', 'Southern Rock', 'Space', 'Space Rock', 'Speech', 'Swing', 'Symphonic Rock', 'Symphony', 'Synthpop', 'Tango', 'Techno', 'Techno-Industrial', 'Terror', 'Thrash Metal', 'Top 40', 'Touhou', 'Trailer', 'Trance', 'Tribal', 'Trip-Hop', 'Trop Rock', 'Vocal', 'World Music'
];
# Taken from: https://web.archive.org/web/20220322153107/https://dev.vk.com/reference/objects/audio-genres
@@ -152,6 +152,11 @@ class Audio extends Media
return $this->getPerformer() . " — " . $this->getTitle();
}
+ function getDownloadName(): string
+ {
+ return preg_replace('/[\\/:*?"<>|]/', '_', str_replace(' ', '_', $this->getName()));
+ }
+
function getGenre(): ?string
{
return $this->getRecord()->genre;
diff --git a/Web/Models/Entities/Club.php b/Web/Models/Entities/Club.php
index cbbe622b..de5cb007 100644
--- a/Web/Models/Entities/Club.php
+++ b/Web/Models/Entities/Club.php
@@ -152,6 +152,11 @@ class Club extends RowModel
return (bool) $this->getRecord()->hide_from_global_feed;
}
+ function isHidingFromGlobalFeedEnforced(): bool
+ {
+ return (bool) $this->getRecord()->enforce_hiding_from_global_feed;
+ }
+
function getType(): int
{
return $this->getRecord()->type;
@@ -432,7 +437,7 @@ class Club extends RowModel
return (new \openvk\Web\Models\Repositories\Audios)->getClubCollectionSize($this);
}
- function toVkApiStruct(?User $user = NULL): object
+ function toVkApiStruct(?User $user = NULL, string $fields = ''): object
{
$res = (object) [];
diff --git a/Web/Models/Entities/Comment.php b/Web/Models/Entities/Comment.php
index 24fb2c1b..fd32c0b8 100644
--- a/Web/Models/Entities/Comment.php
+++ b/Web/Models/Entities/Comment.php
@@ -103,6 +103,22 @@ class Comment extends Post
return $this->getTarget()->canBeViewedBy($user);
}
+
+ function isFromPostAuthor($target = NULL)
+ {
+ if(!$target)
+ $target = $this->getTarget();
+
+ $target_owner = $target->getOwner();
+ $comment_owner = $this->getOwner();
+
+ if($target_owner->getRealId() === $comment_owner->getRealId())
+ return true;
+
+ # TODO: make it work with signer_id
+
+ return false;
+ }
function toNotifApiStruct()
{
@@ -124,4 +140,31 @@ class Comment extends Post
return $user->getId() == $this->getOwner(false)->getId();
}
+
+ function getTargetURL(): string
+ {
+ $target = $this->getTarget();
+ $target_name = 'wall';
+
+ if(!$target) {
+ return '/404';
+ }
+
+ switch(get_class($target)) {
+ case 'openvk\Web\Models\Entities\Note':
+ $target_name = 'note';
+ break;
+ case 'openvk\Web\Models\Entities\Photo':
+ $target_name = 'photo';
+ break;
+ case 'openvk\Web\Models\Entities\Video':
+ $target_name = 'video';
+ break;
+ case 'openvk\Web\Models\Entities\Topic':
+ $target_name = 'topic';
+ break;
+ }
+
+ return $target_name . $target->getPrettyId();
+ }
}
diff --git a/Web/Models/Entities/Message.php b/Web/Models/Entities/Message.php
index 00de6e98..29e6578e 100644
--- a/Web/Models/Entities/Message.php
+++ b/Web/Models/Entities/Message.php
@@ -66,7 +66,7 @@ class Message extends RowModel
$dateTime = new DateTime($this->getRecord()->created);
if($dateTime->format("%d.%m.%y") == ovk_strftime_safe("%d.%m.%y", time())) {
- return $dateTime->format("%T %p");
+ return $dateTime->format("%T");
} else {
return $dateTime->format("%d.%m.%y");
}
diff --git a/Web/Models/Entities/Photo.php b/Web/Models/Entities/Photo.php
index 4cba7734..3ea46939 100644
--- a/Web/Models/Entities/Photo.php
+++ b/Web/Models/Entities/Photo.php
@@ -114,7 +114,7 @@ class Photo extends Media
return true;
}
- function crop(real $left, real $top, real $width, real $height): void
+ function crop(float $left, float $top, float $width, float $height): void
{
if(isset($this->changes["hash"]))
$hash = $this->changes["hash"];
diff --git a/Web/Models/Entities/Playlist.php b/Web/Models/Entities/Playlist.php
index c027a038..ac36b4f3 100644
--- a/Web/Models/Entities/Playlist.php
+++ b/Web/Models/Entities/Playlist.php
@@ -41,6 +41,21 @@ class Playlist extends MediaCollection
{
return $this->getRecord()->length;
}
+
+ function fetchClassic(int $offset = 0, ?int $limit = NULL): \Traversable
+ {
+ $related = $this->getRecord()->related("$this->relTableName.collection")
+ ->limit($limit ?? OPENVK_DEFAULT_PER_PAGE, $offset)
+ ->order("index ASC");
+
+ foreach($related as $rel) {
+ $media = $rel->ref($this->entityTableName, "media");
+ if(!$media)
+ continue;
+
+ yield new $this->entityClassName($media);
+ }
+ }
function getAudios(int $offset = 0, ?int $limit = NULL, ?int $shuffleSeed = NULL): \Traversable
{
@@ -162,6 +177,7 @@ class Playlist extends MediaCollection
"bookmarked" => $this->isBookmarkedBy($user),
"listens" => $this->getListens(),
"cover_url" => $this->getCoverURL(),
+ "searchable" => !$this->isUnlisted(),
];
}
@@ -199,6 +215,11 @@ class Playlist extends MediaCollection
{
return $this->getRecord()->cover_photo_id;
}
+
+ function getCoverPhoto(): ?Photo
+ {
+ return (new Photos)->get((int) $this->getRecord()->cover_photo_id);
+ }
function canBeModifiedBy(User $user): bool
{
@@ -253,4 +274,9 @@ class Playlist extends MediaCollection
return implode(" • ", $props);
}
+
+ function isUnlisted(): bool
+ {
+ return (bool)$this->getRecord()->unlisted;
+ }
}
diff --git a/Web/Models/Entities/Post.php b/Web/Models/Entities/Post.php
index 51b42c07..5a75d99b 100644
--- a/Web/Models/Entities/Post.php
+++ b/Web/Models/Entities/Post.php
@@ -133,6 +133,10 @@ class Post extends Postable
case 'openvk_legacy_ios':
return 'iphone';
break;
+
+ case 'windows_phone':
+ return 'wphone';
+ break;
case 'vika_touch': // кика хохотач ахахахаххахахахахах
case 'vk4me':
@@ -175,6 +179,31 @@ class Post extends Postable
"img" => NULL
];
}
+
+ function getPostSourceInfo(): array
+ {
+ $post_source = ["type" => "vk"];
+ if($this->getPlatform(true) !== NULL) {
+ $post_source = [
+ "type" => "api",
+ "platform" => $this->getPlatform(true)
+ ];
+ }
+
+ if($this->isUpdateAvatarMessage())
+ $post_source['data'] = 'profile_photo';
+
+ return $post_source;
+ }
+
+ function getVkApiType(): string
+ {
+ $type = 'post';
+ if($this->getSuggestionType() != 0)
+ $type = 'suggest';
+
+ return $type;
+ }
function pin(): void
{
diff --git a/Web/Models/Entities/Report.php b/Web/Models/Entities/Report.php
index 5487056f..1b36678c 100644
--- a/Web/Models/Entities/Report.php
+++ b/Web/Models/Entities/Report.php
@@ -80,7 +80,7 @@ class Report extends RowModel
function getAuthor(): RowModel
{
- return (new Posts)->get($this->getContentId())->getOwner();
+ return $this->getContentObject()->getOwner();
}
function getReportAuthor(): User
diff --git a/Web/Models/Entities/Traits/TRichText.php b/Web/Models/Entities/Traits/TRichText.php
index dc78a034..0f02a8ab 100644
--- a/Web/Models/Entities/Traits/TRichText.php
+++ b/Web/Models/Entities/Traits/TRichText.php
@@ -123,7 +123,7 @@ trait TRichText
$text = preg_replace_callback("%([\n\r\s]|^)(\#([\p{L}_0-9][\p{L}_0-9\(\)\-\']+[\p{L}_0-9\(\)]|[\p{L}_0-9]{1,2}))%Xu", function($m) {
$slug = rawurlencode($m[3]);
- return "$m[1]$m[2]";
+ return "$m[1]$m[2]";
}, $text);
$text = $this->formatEmojis($text);
diff --git a/Web/Models/Entities/Traits/TSubscribable.php b/Web/Models/Entities/Traits/TSubscribable.php
index 802bc427..7d1c0d28 100644
--- a/Web/Models/Entities/Traits/TSubscribable.php
+++ b/Web/Models/Entities/Traits/TSubscribable.php
@@ -39,4 +39,25 @@ trait TSubscribable
$sub->delete();
return false;
}
+
+ function changeFlags(User $user, int $flags, bool $reverse): bool
+ {
+ $ctx = DatabaseConnection::i()->getContext();
+ $data = [
+ "follower" => $reverse ? $this->getId() : $user->getId(),
+ "model" => static::class,
+ "target" => $reverse ? $user->getId() : $this->getId(),
+ ];
+ $sub = $ctx->table("subscriptions")->where($data);
+
+ bdump($data);
+
+ if (!$sub)
+ return false;
+
+ $sub->update([
+ 'flags' => $flags
+ ]);
+ return true;
+ }
}
diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php
index 697abdde..64053145 100644
--- a/Web/Models/Entities/User.php
+++ b/Web/Models/Entities/User.php
@@ -592,6 +592,16 @@ class User extends RowModel
return $this->_abstractRelationCount("get-followers");
}
+ function getRequests(int $page = 1, int $limit = 6): \Traversable
+ {
+ return $this->_abstractRelationGenerator("get-requests", $page, $limit);
+ }
+
+ function getRequestsCount(): int
+ {
+ return $this->_abstractRelationCount("get-requests");
+ }
+
function getSubscriptions(int $page = 1, int $limit = 6): \Traversable
{
return $this->_abstractRelationGenerator("get-subscriptions-user", $page, $limit);
@@ -1214,6 +1224,11 @@ class User extends RowModel
return (bool) $this->getRecord()->activated;
}
+ function isDead(): bool
+ {
+ return $this->onlineStatus() == 2;
+ }
+
function getUnbanTime(): ?string
{
$ban = (new Bans)->get((int) $this->getRecord()->block_reason);
@@ -1306,17 +1321,22 @@ class User extends RowModel
return true;
}
- function isClosed()
+ function isClosed(): bool
{
return (bool) $this->getProfileType();
}
+
+ function isHideFromGlobalFeedEnabled(): bool
+ {
+ return $this->isClosed();
+ }
function getRealId()
{
return $this->getId();
}
- function toVkApiStruct(?User $user = NULL): object
+ function toVkApiStruct(?User $user = NULL, string $fields = ''): object
{
$res = (object) [];
@@ -1328,12 +1348,21 @@ class User extends RowModel
$res->photo_100 = $this->getAvatarURL("tiny");
$res->photo_200 = $this->getAvatarURL("normal");
$res->photo_id = !is_null($this->getAvatarPhoto()) ? $this->getAvatarPhoto()->getPrettyId() : NULL;
- # TODO: Perenesti syuda vsyo ostalnoyie
$res->is_closed = $this->isClosed();
- if(!is_null($user)) {
+ if(!is_null($user))
$res->can_access_closed = (bool)$this->canBeViewedBy($user);
+
+ if(!is_array($fields))
+ $fields = explode(',', $fields);
+
+ foreach($fields as $field) {
+ switch($field) {
+ case 'is_dead':
+ $res->is_dead = $user->isDead();
+ break;
+ }
}
return $res;
diff --git a/Web/Models/Entities/Video.php b/Web/Models/Entities/Video.php
index 2c1c0b05..a4d0b898 100644
--- a/Web/Models/Entities/Video.php
+++ b/Web/Models/Entities/Video.php
@@ -181,8 +181,8 @@ class Video extends Media
{
if(preg_match(file_get_contents(__DIR__ . "/../VideoDrivers/regex/youtube.txt"), $link, $matches)) {
$pointer = "YouTube:$matches[1]";
- } else if(preg_match(file_get_contents(__DIR__ . "/../VideoDrivers/regex/vimeo.txt"), $link, $matches)) {
- $pointer = "Vimeo:$matches[1]";
+ /*} else if(preg_match(file_get_contents(__DIR__ . "/../VideoDrivers/regex/vimeo.txt"), $link, $matches)) {
+ $pointer = "Vimeo:$matches[1]";*/
} else {
throw new ISE("Invalid link");
}
diff --git a/Web/Models/Repositories/APITokens.php b/Web/Models/Repositories/APITokens.php
index 592688a8..8a9f2870 100644
--- a/Web/Models/Repositories/APITokens.php
+++ b/Web/Models/Repositories/APITokens.php
@@ -23,4 +23,13 @@ class APITokens extends Repository
return $token;
}
+
+ function getStaleByUser(int $userId, string $platform, bool $withRevoked = false): ?APIToken
+ {
+ return $this->toEntity($this->table->where([
+ 'user' => $userId,
+ 'platform' => $platform,
+ 'deleted' => $withRevoked,
+ ])->fetch());
+ }
}
diff --git a/Web/Models/Repositories/Applications.php b/Web/Models/Repositories/Applications.php
index 0687856e..c0906067 100644
--- a/Web/Models/Repositories/Applications.php
+++ b/Web/Models/Repositories/Applications.php
@@ -67,11 +67,21 @@ class Applications
return sizeof($this->appRels->where("user", $user->getId()));
}
- function find(string $query, array $pars = [], string $sort = "id"): Util\EntityStream
+ function find(string $query = "", array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream
{
- $query = "%$query%";
+ $query = "%$query%";
$result = $this->apps->where("CONCAT_WS(' ', name, description) LIKE ?", $query)->where("enabled", 1);
+ $order_str = 'id';
+
+ switch($order['type']) {
+ case 'id':
+ $order_str = 'id ' . ($order['invert'] ? 'ASC' : 'DESC');
+ break;
+ }
+
+ if($order_str)
+ $result->order($order_str);
- return new Util\EntityStream("Application", $result->order("$sort"));
+ return new Util\EntityStream("Application", $result);
}
}
\ No newline at end of file
diff --git a/Web/Models/Repositories/Audios.php b/Web/Models/Repositories/Audios.php
index 64457299..73f5ac29 100644
--- a/Web/Models/Repositories/Audios.php
+++ b/Web/Models/Repositories/Audios.php
@@ -208,7 +208,7 @@ class Audios
$search = $this->audios->where([
"unlisted" => 0,
"deleted" => 0,
- ])->where("MATCH ($columns) AGAINST (? WITH QUERY EXPANSION)", $query)->order($order);
+ ])->where("MATCH ($columns) AGAINST (? IN BOOLEAN MODE)", "%$query%")->order($order);
if($withLyrics)
$search = $search->where("lyrics IS NOT NULL");
@@ -219,6 +219,7 @@ class Audios
function searchPlaylists(string $query): EntityStream
{
$search = $this->playlists->where([
+ "unlisted" => 0,
"deleted" => 0,
])->where("MATCH (`name`, `description`) AGAINST (? IN BOOLEAN MODE)", $query);
@@ -243,53 +244,72 @@ class Audios
])->fetch());
}
- function find(string $query, array $pars = [], string $sort = "id DESC", int $page = 1, ?int $perPage = NULL): \Traversable
+ function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false], int $page = 1, ?int $perPage = NULL): \Traversable
{
- $query = "%$query%";
+ $query = "%$query%";
$result = $this->audios->where([
"unlisted" => 0,
"deleted" => 0,
]);
+ $order_str = (in_array($order['type'], ['id', 'length', 'listens']) ? $order['type'] : 'id') . ' ' . ($order['invert'] ? 'ASC' : 'DESC');;
- $notNullParams = [];
-
- foreach($pars as $paramName => $paramValue)
- if($paramName != "before" && $paramName != "after" && $paramName != "only_performers")
- $paramValue != NULL ? $notNullParams+=["$paramName" => "%$paramValue%"] : NULL;
- else
- $paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
-
- $nnparamsCount = sizeof($notNullParams);
-
- if($notNullParams["only_performers"] == "1") {
+ if($params["only_performers"] == "1") {
$result->where("performer LIKE ?", $query);
} else {
$result->where("name LIKE ? OR performer LIKE ?", $query, $query);
}
- if($nnparamsCount > 0) {
- foreach($notNullParams as $paramName => $paramValue) {
- switch($paramName) {
- case "before":
- $result->where("created < ?", $paramValue);
- break;
- case "after":
- $result->where("created > ?", $paramValue);
- break;
- case "with_lyrics":
- $result->where("lyrics IS NOT NULL");
- break;
- }
+ foreach($params as $paramName => $paramValue) {
+ if(is_null($paramValue) || $paramValue == '') continue;
+
+ switch($paramName) {
+ case "before":
+ $result->where("created < ?", $paramValue);
+ break;
+ case "after":
+ $result->where("created > ?", $paramValue);
+ break;
+ case "with_lyrics":
+ $result->where("lyrics IS NOT NULL");
+ break;
+ case 'genre':
+ if($paramValue == 'any') break;
+
+ $result->where("genre", $paramValue);
+ break;
}
}
- return new Util\EntityStream("Audio", $result->order($sort));
+ if($order_str)
+ $result->order($order_str);
+
+ return new Util\EntityStream("Audio", $result);
}
- function findPlaylists(string $query, int $page = 1, ?int $perPage = NULL): \Traversable
+ function findPlaylists(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false]): \Traversable
{
- $query = "%$query%";
- $result = $this->playlists->where("name LIKE ?", $query);
+ $query = "%$query%";
+ $result = $this->playlists->where([
+ "deleted" => 0,
+ ])->where("CONCAT_WS(' ', name, description) LIKE ?", $query);
+ $order_str = (in_array($order['type'], ['id', 'length', 'listens']) ? $order['type'] : 'id') . ' ' . ($order['invert'] ? 'ASC' : 'DESC');
+
+ if(is_null($params['from_me']) || empty($params['from_me']))
+ $result->where(["unlisted" => 0]);
+
+ foreach($params as $paramName => $paramValue) {
+ if(is_null($paramValue) || $paramValue == '') continue;
+
+ switch($paramName) {
+ # БУДЬ МАКСИМАЛЬНО АККУРАТЕН С ДАННЫМ ПАРАМЕТРОМ
+ case "from_me":
+ $result->where("owner", $paramValue);
+ break;
+ }
+ }
+
+ if($order_str)
+ $result->order($order_str);
return new Util\EntityStream("Playlist", $result);
}
diff --git a/Web/Models/Repositories/Clubs.php b/Web/Models/Repositories/Clubs.php
index 04bb30ab..b393952f 100644
--- a/Web/Models/Repositories/Clubs.php
+++ b/Web/Models/Repositories/Clubs.php
@@ -42,18 +42,30 @@ class Clubs
{
return $this->toClub($this->clubs->get($id));
}
-
- function find(string $query, array $pars = [], string $sort = "id DESC", int $page = 1, ?int $perPage = NULL): \Traversable
+
+ function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false], int $page = 1, ?int $perPage = NULL): \Traversable
{
- $query = "%$query%";
- $result = $this->clubs->where("name LIKE ? OR about LIKE ?", $query, $query);
-
- return new Util\EntityStream("Club", $result->order($sort));
+ $query = "%$query%";
+ $result = $this->clubs;
+ $order_str = 'id';
+
+ switch($order['type']) {
+ case 'id':
+ $order_str = 'id ' . ($order['invert'] ? 'ASC' : 'DESC');
+ break;
+ }
+
+ $result = $result->where("name LIKE ? OR about LIKE ?", $query, $query);
+
+ if($order_str)
+ $result->order($order_str);
+
+ return new Util\EntityStream("Club", $result);
}
function getCount(): int
{
- return sizeof(clone $this->clubs);
+ return (clone $this->clubs)->count('*');
}
function getPopularClubs(): \Traversable
diff --git a/Web/Models/Repositories/Comments.php b/Web/Models/Repositories/Comments.php
index f4b8e5ac..811d1358 100644
--- a/Web/Models/Repositories/Comments.php
+++ b/Web/Models/Repositories/Comments.php
@@ -60,34 +60,31 @@ class Comments
]));
}
- function find(string $query = "", array $pars = [], string $sort = "id"): Util\EntityStream
+ function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream
{
- $query = "%$query%";
+ $result = $this->comments->where("content LIKE ?", "%$query%")->where("deleted", 0);
+ $order_str = 'id';
- $notNullParams = [];
+ switch($order['type']) {
+ case 'id':
+ $order_str = 'created ' . ($order['invert'] ? 'ASC' : 'DESC');
+ break;
+ }
- foreach($pars as $paramName => $paramValue)
- if($paramName != "before" && $paramName != "after")
- $paramValue != NULL ? $notNullParams+=["$paramName" => "%$paramValue%"] : NULL;
- else
- $paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
-
- $result = $this->comments->where("content LIKE ?", $query)->where("deleted", 0);
- $nnparamsCount = sizeof($notNullParams);
-
- if($nnparamsCount > 0) {
- foreach($notNullParams as $paramName => $paramValue) {
- switch($paramName) {
- case "before":
- $result->where("created < ?", $paramValue);
- break;
- case "after":
- $result->where("created > ?", $paramValue);
- break;
- }
+ foreach($params as $paramName => $paramValue) {
+ switch($paramName) {
+ case "before":
+ $result->where("created < ?", $paramValue);
+ break;
+ case "after":
+ $result->where("created > ?", $paramValue);
+ break;
}
}
- return new Util\EntityStream("Comment", $result->order("$sort"));
+ if($order_str)
+ $result->order($order_str);
+
+ return new Util\EntityStream("Comment", $result);
}
}
diff --git a/Web/Models/Repositories/Notifications.php b/Web/Models/Repositories/Notifications.php
index bec0b04f..ccbf7c27 100644
--- a/Web/Models/Repositories/Notifications.php
+++ b/Web/Models/Repositories/Notifications.php
@@ -30,7 +30,7 @@ class Notifications
return (new $repoClassName)->get($id);
}
- private function getQuery(User $user, bool $count = false, int $offset, bool $archived = false, int $page = 1, ?int $perPage = NULL): string
+ private function getQuery(User $user, bool $count, int $offset, bool $archived = false, int $page = 1, ?int $perPage = NULL): string
{
$query = "SELECT " . ($count ? "COUNT(*) AS cnt" : "*") . " FROM notifications WHERE recipientType=0 ";
$query .= "AND timestamp " . ($archived ? "<" : ">") . "$offset AND recipientId=" . $user->getId();
diff --git a/Web/Models/Repositories/Posts.php b/Web/Models/Repositories/Posts.php
index 89ee58ea..36082f24 100644
--- a/Web/Models/Repositories/Posts.php
+++ b/Web/Models/Repositories/Posts.php
@@ -154,36 +154,45 @@ class Posts
}
- function find(string $query = "", array $pars = [], string $sort = "id"): Util\EntityStream
+ function find(string $query = "", array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream
{
- $query = "%$query%";
-
- $notNullParams = [];
-
- foreach($pars as $paramName => $paramValue)
- if($paramName != "before" && $paramName != "after")
- $paramValue != NULL ? $notNullParams+=["$paramName" => "%$paramValue%"] : NULL;
- else
- $paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
-
+ $query = "%$query%";
$result = $this->posts->where("content LIKE ?", $query)->where("deleted", 0)->where("suggested", 0);
- $nnparamsCount = sizeof($notNullParams);
+ $order_str = 'id';
- if($nnparamsCount > 0) {
- foreach($notNullParams as $paramName => $paramValue) {
- switch($paramName) {
- case "before":
- $result->where("created < ?", $paramValue);
- break;
- case "after":
- $result->where("created > ?", $paramValue);
- break;
- }
+ switch($order['type']) {
+ case 'id':
+ $order_str = 'created ' . ($order['invert'] ? 'ASC' : 'DESC');
+ break;
+ }
+
+ foreach($params as $paramName => $paramValue) {
+ if(is_null($paramValue) || $paramValue == '') continue;
+
+ switch($paramName) {
+ case "before":
+ $result->where("created < ?", $paramValue);
+ break;
+ case "after":
+ $result->where("created > ?", $paramValue);
+ break;
+ /*case 'die_in_agony':
+ $result->where("nsfw", 1);
+ break;
+ case 'ads':
+ $result->where("ad", 1);
+ break;*/
+ # БУДЬ МАКСИМАЛЬНО АККУРАТЕН С ДАННЫМ ПАРАМЕТРОМ
+ case 'from_me':
+ $result->where("owner", $paramValue);
+ break;
}
}
+ if($order_str)
+ $result->order($order_str);
- return new Util\EntityStream("Post", $result->order("$sort"));
+ return new Util\EntityStream("Post", $result);
}
function getPostCountOnUserWall(int $user): int
@@ -252,6 +261,6 @@ class Posts
function getCount(): int
{
- return sizeof(clone $this->posts);
+ return (clone $this->posts)->count('*');
}
}
diff --git a/Web/Models/Repositories/Users.php b/Web/Models/Repositories/Users.php
index d2f4500e..cc75b50f 100644
--- a/Web/Models/Repositories/Users.php
+++ b/Web/Models/Repositories/Users.php
@@ -44,107 +44,94 @@ class Users
return $alias->getUser();
}
- function getByChandlerUser(?ChandlerUser $user): ?User
+ function getByChandlerUserId(string $cid): ?User
{
- return $user ? $this->toUser($this->users->where("user", $user->getId())->fetch()) : NULL;
+ return $this->toUser($this->users->where("user", $cid)->fetch());
}
- function find(string $query, array $pars = [], string $sort = "id DESC"): Util\EntityStream
+ function getByChandlerUser(?ChandlerUser $user): ?User
{
- $query = "%$query%";
+ return $user ? $this->getByChandlerUserId($user->getId()) : NULL;
+ }
+
+ function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream
+ {
+ $query = "%$query%";
$result = $this->users->where("CONCAT_WS(' ', first_name, last_name, pseudo, shortcode) LIKE ?", $query)->where("deleted", 0);
-
- $notNullParams = [];
- $nnparamsCount = 0;
-
- foreach($pars as $paramName => $paramValue)
- if($paramName != "before" && $paramName != "after" && $paramName != "gender" && $paramName != "maritalstatus" && $paramName != "politViews" && $paramName != "doNotSearchMe")
- $paramValue != NULL ? $notNullParams += ["$paramName" => "%$paramValue%"] : NULL;
- else
- $paramValue != NULL ? $notNullParams += ["$paramName" => "$paramValue"] : NULL;
+ $order_str = 'id';
- $nnparamsCount = sizeof($notNullParams);
+ switch($order['type']) {
+ case 'id':
+ case 'reg_date':
+ $order_str = 'id ' . ($order['invert'] ? 'ASC' : 'DESC');
+ break;
+ case 'rating':
+ $order_str = 'rating DESC';
+ break;
+ }
- if($nnparamsCount > 0) {
- foreach($notNullParams as $paramName => $paramValue) {
- switch($paramName) {
- case "hometown":
- $result->where("hometown LIKE ?", $paramValue);
- break;
- case "city":
- $result->where("city LIKE ?", $paramValue);
- break;
- case "maritalstatus":
- $result->where("marital_status ?", $paramValue);
- break;
- case "status":
- $result->where("status LIKE ?", $paramValue);
- break;
- case "politViews":
- $result->where("polit_views ?", $paramValue);
- break;
- case "email":
- $result->where("email_contact LIKE ?", $paramValue);
- break;
- case "telegram":
- $result->where("telegram LIKE ?", $paramValue);
- break;
- case "site":
- $result->where("telegram LIKE ?", $paramValue);
- break;
- case "address":
- $result->where("address LIKE ?", $paramValue);
- break;
- case "is_online":
- $result->where("online >= ?", time() - 900);
- break;
- case "interests":
- $result->where("interests LIKE ?", $paramValue);
- break;
- case "fav_mus":
- $result->where("fav_music LIKE ?", $paramValue);
- break;
- case "fav_films":
- $result->where("fav_films LIKE ?", $paramValue);
- break;
- case "fav_shows":
- $result->where("fav_shows LIKE ?", $paramValue);
- break;
- case "fav_books":
- $result->where("fav_books LIKE ?", $paramValue);
- break;
- case "fav_quote":
- $result->where("fav_quote LIKE ?", $paramValue);
- break;
- case "before":
- $result->where("UNIX_TIMESTAMP(since) < ?", $paramValue);
- break;
- case "after":
- $result->where("UNIX_TIMESTAMP(since) > ?", $paramValue);
- break;
- case "gender":
- $result->where("sex ?", $paramValue);
- break;
- case "doNotSearchMe":
- $result->where("id !=", $paramValue);
- break;
- case "doNotSearchPrivate":
- $result->where("profile_type", 0);
- break;
- }
+ foreach($params as $paramName => $paramValue) {
+ if(is_null($paramValue) || $paramValue == '') continue;
+
+ switch($paramName) {
+ case "hometown":
+ $result->where("hometown LIKE ?", "%$paramValue%");
+ break;
+ case "city":
+ $result->where("city LIKE ?", "%$paramValue%");
+ break;
+ case "marital_status":
+ $result->where("marital_status ?", $paramValue);
+ break;
+ case "polit_views":
+ $result->where("polit_views ?", $paramValue);
+ break;
+ case "is_online":
+ $result->where("online >= ?", time() - 900);
+ break;
+ case "fav_mus":
+ $result->where("fav_music LIKE ?", "%$paramValue%");
+ break;
+ case "fav_films":
+ $result->where("fav_films LIKE ?", "%$paramValue%");
+ break;
+ case "fav_shows":
+ $result->where("fav_shows LIKE ?", "%$paramValue%");
+ break;
+ case "fav_books":
+ $result->where("fav_books LIKE ?", "%$paramValue%");
+ break;
+ case "before":
+ $result->where("UNIX_TIMESTAMP(since) < ?", $paramValue);
+ break;
+ case "after":
+ $result->where("UNIX_TIMESTAMP(since) > ?", $paramValue);
+ break;
+ case "gender":
+ if((int) $paramValue == 3) break;
+ $result->where("sex ?", (int) $paramValue);
+ break;
+ case "ignore_id":
+ $result->where("id != ?", $paramValue);
+ break;
+ case "ignore_private":
+ $result->where("profile_type", 0);
+ break;
}
}
+ if($order_str)
+ $result->order($order_str);
- return new Util\EntityStream("User", $result->order($sort));
+ return new Util\EntityStream("User", $result);
}
function getStatistics(): object
{
return (object) [
- "all" => sizeof(clone $this->users),
- "active" => sizeof((clone $this->users)->where("online > 0")),
- "online" => sizeof((clone $this->users)->where("online >= ?", time() - 900)),
+ "all" => (clone $this->users)->count('*'),
+ "active" => (clone $this->users)->where("online > 0")->count('*'),
+ "online" => (clone $this->users)->where("online >= ?", time() - 900)->count('*'),
];
}
diff --git a/Web/Models/Repositories/Videos.php b/Web/Models/Repositories/Videos.php
index 2d41c3f9..e91c226d 100644
--- a/Web/Models/Repositories/Videos.php
+++ b/Web/Models/Repositories/Videos.php
@@ -46,36 +46,37 @@ class Videos
return sizeof($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0]));
}
- function find(string $query = "", array $pars = [], string $sort = "id"): Util\EntityStream
+ function find(string $query = "", array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream
{
- $query = "%$query%";
-
- $notNullParams = [];
-
- foreach($pars as $paramName => $paramValue)
- if($paramName != "before" && $paramName != "after")
- $paramValue != NULL ? $notNullParams+=["$paramName" => "%$paramValue%"] : NULL;
- else
- $paramValue != NULL ? $notNullParams+=["$paramName" => "$paramValue"] : NULL;
-
+ $query = "%$query%";
$result = $this->videos->where("CONCAT_WS(' ', name, description) LIKE ?", $query)->where("deleted", 0);
- $nnparamsCount = sizeof($notNullParams);
+ $order_str = 'id';
- if($nnparamsCount > 0) {
- foreach($notNullParams as $paramName => $paramValue) {
- switch($paramName) {
- case "before":
- $result->where("created < ?", $paramValue);
- break;
- case "after":
- $result->where("created > ?", $paramValue);
- break;
- }
+ switch($order['type']) {
+ case 'id':
+ $order_str = 'id ' . ($order['invert'] ? 'ASC' : 'DESC');
+ break;
+ }
+
+ foreach($params as $paramName => $paramValue) {
+ switch($paramName) {
+ case "before":
+ $result->where("created < ?", $paramValue);
+ break;
+ case "after":
+ $result->where("created > ?", $paramValue);
+ break;
+ case 'only_youtube':
+ if((int) $paramValue != 1) break;
+ $result->where("link != ?", 'NULL');
+ break;
}
}
+ if($order_str)
+ $result->order($order_str);
- return new Util\EntityStream("Video", $result->order("$sort"));
+ return new Util\EntityStream("Video", $result);
}
function getLastVideo(User $user)
diff --git a/Web/Models/sql/get-followers.tsql b/Web/Models/sql/get-followers.tsql
index 552aafb8..b07b1c69 100644
--- a/Web/Models/sql/get-followers.tsql
+++ b/Web/Models/sql/get-followers.tsql
@@ -1,5 +1,5 @@
(SELECT DISTINCT(follower) AS __id FROM
- (SELECT follower FROM subscriptions WHERE target=? AND model="openvk\\Web\\Models\\Entities\\User") u0
+ (SELECT follower, flags FROM subscriptions WHERE target=? AND model="openvk\\Web\\Models\\Entities\\User") u0
LEFT JOIN
(SELECT target FROM subscriptions WHERE follower=? AND model="openvk\\Web\\Models\\Entities\\User") u1
ON u0.follower = u1.target WHERE u1.target IS NULL) u2
diff --git a/Web/Models/sql/get-requests.tsql b/Web/Models/sql/get-requests.tsql
new file mode 100755
index 00000000..0220e340
--- /dev/null
+++ b/Web/Models/sql/get-requests.tsql
@@ -0,0 +1,6 @@
+ (SELECT DISTINCT(follower) AS __id FROM
+ (SELECT follower FROM subscriptions WHERE target=? AND flags=0 AND model="openvk\\Web\\Models\\Entities\\User") u0
+ LEFT JOIN
+ (SELECT target FROM subscriptions WHERE follower=? AND flags=0 AND model="openvk\\Web\\Models\\Entities\\User") u1
+ ON u0.follower = u1.target WHERE u1.target IS NULL) u2
+INNER JOIN profiles ON profiles.id = u2.__id
\ No newline at end of file
diff --git a/Web/Presenters/AboutPresenter.php b/Web/Presenters/AboutPresenter.php
index 73e4da7f..49ff1c32 100644
--- a/Web/Presenters/AboutPresenter.php
+++ b/Web/Presenters/AboutPresenter.php
@@ -145,6 +145,6 @@ final class AboutPresenter extends OpenVKPresenter
function renderDev(): void
{
- $this->redirect("https://docs.openvk.uk/");
+ $this->redirect("https://docs.ovk.to/");
}
}
diff --git a/Web/Presenters/AdminPresenter.php b/Web/Presenters/AdminPresenter.php
index 3d864e56..b8f480ca 100644
--- a/Web/Presenters/AdminPresenter.php
+++ b/Web/Presenters/AdminPresenter.php
@@ -48,6 +48,13 @@ final class AdminPresenter extends OpenVKPresenter
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
$this->flash("warn", tr("admin_commerce_disabled"), tr("admin_commerce_disabled_desc"));
}
+
+ private function warnIfLongpoolBroken(): void
+ {
+ bdump(is_writable(CHANDLER_ROOT . '/tmp/events.bin'));
+ if(file_exists(CHANDLER_ROOT . '/tmp/events.bin') == false || is_writable(CHANDLER_ROOT . '/tmp/events.bin') == false)
+ $this->flash("warn", tr("admin_longpool_broken"), tr("admin_longpool_broken_desc", CHANDLER_ROOT . '/tmp/events.bin'));
+ }
private function searchResults(object $repo, &$count)
{
@@ -76,7 +83,7 @@ final class AdminPresenter extends OpenVKPresenter
function renderIndex(): void
{
-
+ $this->warnIfLongpoolBroken();
}
function renderUsers(): void
@@ -154,6 +161,7 @@ final class AdminPresenter extends OpenVKPresenter
$club->setShortCode($this->postParam("shortcode"));
$club->setVerified(empty($this->postParam("verify") ? 0 : 1));
$club->setHide_From_Global_Feed(empty($this->postParam("hide_from_global_feed") ? 0 : 1));
+ $club->setEnforce_Hiding_From_Global_Feed(empty($this->postParam("enforce_hiding_from_global_feed") ? 0 : 1));
$club->save();
break;
case "ban":
@@ -681,7 +689,8 @@ final class AdminPresenter extends OpenVKPresenter
$this->template->obj_type = $obj_type;
}
- $this->template->logs = (new Logs)->search($filter);
+ $logs = iterator_to_array((new Logs)->search($filter));
+ $this->template->logs = $logs;
$this->template->object_types = (new Logs)->getTypes();
}
}
diff --git a/Web/Presenters/AudioPresenter.php b/Web/Presenters/AudioPresenter.php
index 8ec17012..7e2825c1 100644
--- a/Web/Presenters/AudioPresenter.php
+++ b/Web/Presenters/AudioPresenter.php
@@ -75,7 +75,7 @@ final class AudioPresenter extends OpenVKPresenter
if (!$entity || $entity->isBanned())
$this->redirect("/playlists" . $this->user->id);
- $playlists = $this->audios->getPlaylistsByClub($entity, $page, 10);
+ $playlists = $this->audios->getPlaylistsByClub($entity, $page, OPENVK_DEFAULT_PER_PAGE);
$playlistsCount = $this->audios->getClubPlaylistsCount($entity);
} else {
$entity = (new Users)->get($owner);
@@ -85,7 +85,7 @@ final class AudioPresenter extends OpenVKPresenter
if(!$entity->getPrivacyPermission("audios.read", $this->user->identity))
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
- $playlists = $this->audios->getPlaylistsByUser($entity, $page, 9);
+ $playlists = $this->audios->getPlaylistsByUser($entity, $page, OPENVK_DEFAULT_PER_PAGE);
$playlistsCount = $this->audios->getUserPlaylistsCount($entity);
}
@@ -109,8 +109,8 @@ final class AudioPresenter extends OpenVKPresenter
$this->template->mode = $mode;
$this->template->page = $page;
-
- if(in_array($mode, ["list", "new", "popular"]) && $this->user->identity)
+
+ if(in_array($mode, ["list", "new", "popular"]) && $this->user->identity && $page < 2)
$this->template->friendsAudios = $this->user->identity->getBroadcastList("all", true);
}
@@ -142,7 +142,13 @@ final class AudioPresenter extends OpenVKPresenter
$this->assertUserLoggedIn();
$group = NULL;
+ $playlist = NULL;
$isAjax = $this->postParam("ajax", false) == 1;
+
+ if(!is_null($this->queryParam("gid")) && !is_null($this->queryParam("playlist"))) {
+ $this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"), null, $isAjax);
+ }
+
if(!is_null($this->queryParam("gid"))) {
$gid = (int) $this->queryParam("gid");
$group = (new Clubs)->get($gid);
@@ -153,6 +159,19 @@ final class AudioPresenter extends OpenVKPresenter
$this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"), null, $isAjax);
}
+ if(!is_null($this->queryParam("playlist"))) {
+ $playlist_id = (int)$this->queryParam("playlist");
+ $playlist = (new Audios)->getPlaylist($playlist_id);
+ if(!$playlist || $playlist->isDeleted())
+ $this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"), null, $isAjax);
+
+ if(!$playlist->canBeModifiedBy($this->user->identity))
+ $this->flashFail("err", tr("forbidden"), tr("not_enough_permissions_comment"), null, $isAjax);
+
+ $this->template->playlist = $playlist;
+ $this->template->owner = $playlist->getOwner();
+ }
+
$this->template->group = $group;
if($_SERVER["REQUEST_METHOD"] !== "POST")
@@ -196,6 +215,8 @@ final class AudioPresenter extends OpenVKPresenter
$lyrics = $this->postParam("lyrics");
$genre = empty($this->postParam("genre")) ? "Other" : $this->postParam("genre");
$nsfw = ($this->postParam("explicit") ?? "off") === "on";
+ $is_unlisted = ($this->postParam("unlisted") ?? "off") === "on";
+
if(empty($performer) || empty($name) || iconv_strlen($performer . $name) > 128) # FQN of audio must not be more than 128 chars
$this->flashFail("err", tr("error"), tr("error_insufficient_info"), null, $isAjax);
@@ -206,6 +227,7 @@ final class AudioPresenter extends OpenVKPresenter
$audio->setLyrics(empty($lyrics) ? NULL : $lyrics);
$audio->setGenre($genre);
$audio->setExplicit($nsfw);
+ $audio->setUnlisted($is_unlisted);
try {
$audio->setFile($upload);
@@ -215,13 +237,18 @@ final class AudioPresenter extends OpenVKPresenter
} catch(\RuntimeException $ex) {
$this->flashFail("err", tr("error"), tr("ffmpeg_timeout"), null, $isAjax);
} catch(\BadMethodCallException $ex) {
- $this->flashFail("err", tr("error"), "Загрузка аудио под Linux на данный момент не реализована. Следите за обновлениями: https://github.com/openvk/openvk/pull/512/commits", null, $isAjax);
+ $this->flashFail("err", tr("error"), "хз", null, $isAjax);
} catch(\Exception $ex) {
$this->flashFail("err", tr("error"), tr("ffmpeg_not_installed"), null, $isAjax);
}
$audio->save();
- $audio->add($group ?? $this->user->identity);
+
+ if($playlist) {
+ $playlist->add($audio);
+ } else {
+ $audio->add($group ?? $this->user->identity);
+ }
if(!$isAjax)
$this->redirect(is_null($group) ? "/audios" . $this->user->id : "/audios-" . $group->getId());
@@ -233,9 +260,9 @@ final class AudioPresenter extends OpenVKPresenter
else
$redirectLink .= $this->user->id;
- $pagesCount = (int)ceil((new Audios)->getCollectionSizeByEntityId(isset($group) ? $group->getRealId() : $this->user->id) / 10);
- $redirectLink .= "?p=".$pagesCount;
-
+ if($playlist)
+ $redirectLink = "/playlist" . $playlist->getPrettyId();
+
$this->returnJson([
"success" => true,
"redirect_link" => $redirectLink,
@@ -279,7 +306,7 @@ final class AudioPresenter extends OpenVKPresenter
function renderSearch(): void
{
- $this->redirect("/search?type=audios");
+ $this->redirect("/search?section=audios");
}
function renderNewPlaylist(): void
@@ -304,6 +331,8 @@ final class AudioPresenter extends OpenVKPresenter
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$title = $this->postParam("title");
$description = $this->postParam("description");
+ $is_unlisted = (int)$this->postParam('is_unlisted');
+
$audios = !empty($this->postParam("audios")) ? array_slice(explode(",", $this->postParam("audios")), 0, 1000) : [];
if(empty($title) || iconv_strlen($title) < 1)
@@ -313,7 +342,9 @@ final class AudioPresenter extends OpenVKPresenter
$playlist->setOwner($owner);
$playlist->setName(substr($title, 0, 125));
$playlist->setDescription(substr($description, 0, 2045));
-
+ if($is_unlisted == 1)
+ $playlist->setUnlisted(true);
+
if($_FILES["cover"]["error"] === UPLOAD_ERR_OK) {
if(!str_starts_with($_FILES["cover"]["type"], "image"))
$this->flashFail("err", tr("error"), tr("not_a_photo"));
@@ -427,6 +458,7 @@ final class AudioPresenter extends OpenVKPresenter
$title = $this->postParam("title");
$description = $this->postParam("description");
+ $is_unlisted = (int)$this->postParam('is_unlisted');
$new_audios = !empty($this->postParam("audios")) ? explode(",", rtrim($this->postParam("audios"), ",")) : [];
if(empty($title) || iconv_strlen($title) < 1)
@@ -436,6 +468,7 @@ final class AudioPresenter extends OpenVKPresenter
$playlist->setDescription(ovk_proc_strtr($description, 2045));
$playlist->setEdited(time());
$playlist->resetLength();
+ $playlist->setUnlisted((bool)$is_unlisted);
if($_FILES["new_cover"]["error"] === UPLOAD_ERR_OK) {
if(!str_starts_with($_FILES["new_cover"]["type"], "image"))
@@ -475,12 +508,15 @@ final class AudioPresenter extends OpenVKPresenter
$this->template->playlist = $playlist;
$this->template->page = $page;
+ $this->template->cover = $playlist->getCoverPhoto();
+ $this->template->cover_url = $this->template->cover ? $this->template->cover->getURL() : "/assets/packages/static/openvk/img/song.jpg";
$this->template->audios = iterator_to_array($playlist->fetch($page, 10));
$this->template->ownerId = $owner_id;
$this->template->owner = $playlist->getOwner();
$this->template->isBookmarked = $this->user->identity && $playlist->isBookmarkedBy($this->user->identity);
$this->template->isMy = $this->user->identity && $playlist->getOwner()->getId() === $this->user->id;
- $this->template->canEdit = $this->user->identity && $playlist->canBeModifiedBy($this->user->identity);
+ $this->template->canEdit = $this->user->identity && $playlist->canBeModifiedBy($this->user->identity);
+ $this->template->count = $playlist->size();
}
function renderAction(int $audio_id): void
@@ -531,16 +567,65 @@ final class AudioPresenter extends OpenVKPresenter
break;
case "add_to_club":
- $club = (new Clubs)->get((int)$this->postParam("club"));
-
- if(!$club || !$club->canBeModifiedBy($this->user->identity))
- $this->flashFail("err", "error", tr("access_denied"), null, true);
-
- if(!$audio->isInLibraryOf($club))
- $audio->add($club);
- else
- $this->flashFail("err", "error", tr("group_has_audio"), null, true);
-
+ $detailed = [];
+ if($audio->isWithdrawn())
+ $this->flashFail("err", "error", tr("invalid_audio"), null, true);
+
+ if(empty($this->postParam("clubs")))
+ $this->flashFail("err", "error", 'clubs not passed', null, true);
+
+ $clubs_arr = explode(',', $this->postParam("clubs"));
+ $count = sizeof($clubs_arr);
+ if($count < 1 || $count > 10) {
+ $this->flashFail("err", "error", tr('too_many_or_to_lack'), null, true);
+ }
+
+ foreach($clubs_arr as $club_id) {
+ $club = (new Clubs)->get((int)$club_id);
+ if(!$club || !$club->canBeModifiedBy($this->user->identity))
+ continue;
+
+ if(!$audio->isInLibraryOf($club)) {
+ $detailed[$club_id] = true;
+ $audio->add($club);
+ } else {
+ $detailed[$club_id] = false;
+ continue;
+ }
+ }
+
+ $this->returnJson(["success" => true, 'detailed' => $detailed]);
+ break;
+ case "add_to_playlist":
+ $detailed = [];
+ if($audio->isWithdrawn())
+ $this->flashFail("err", "error", tr("invalid_audio"), null, true);
+
+ if(empty($this->postParam("playlists")))
+ $this->flashFail("err", "error", 'playlists not passed', null, true);
+
+ $playlists_arr = explode(',', $this->postParam("playlists"));
+ $count = sizeof($playlists_arr);
+ if($count < 1 || $count > 10) {
+ $this->flashFail("err", "error", tr('too_many_or_to_lack'), null, true);
+ }
+
+ foreach($playlists_arr as $playlist_id) {
+ $pid = explode('_', $playlist_id);
+ $playlist = (new Audios)->getPlaylistByOwnerAndVID((int)$pid[0], (int)$pid[1]);
+ if(!$playlist || !$playlist->canBeModifiedBy($this->user->identity))
+ continue;
+
+ if(!$playlist->hasAudio($audio)) {
+ $playlist->add($audio);
+ $detailed[$playlist_id] = true;
+ } else {
+ $detailed[$playlist_id] = false;
+ continue;
+ }
+ }
+
+ $this->returnJson(["success" => true, 'detailed' => $detailed]);
break;
case "delete":
if($audio->canBeModifiedBy($this->user->identity))
@@ -653,6 +738,28 @@ final class AudioPresenter extends OpenVKPresenter
$audios = $stream->page($page, 10);
$audiosCount = $stream->size();
break;
+ case "classic_search_context":
+ $data = json_decode($this->postParam("context_entity"), true);
+
+ $params = [];
+ $order = [
+ "type" => $data['order'] ?? 'id',
+ "invert" => (int)$data['invert'] == 1 ? true : false
+ ];
+
+ if($data['genre'] && $data['genre'] != 'any')
+ $params['genre'] = $data['genre'];
+
+ if($data['only_performers'] && (int)$data['only_performers'] == 1)
+ $params['only_performers'] = '1';
+
+ if($data['with_lyrics'] && (int)$data['with_lyrics'] == 1)
+ $params['with_lyrics'] = '1';
+
+ $stream = $this->audios->find($data['query'], $params, $order);
+ $audios = $stream->page($page, 10);
+ $audiosCount = $stream->size();
+ break;
}
$pagesCount = ceil($audiosCount / $perPage);
diff --git a/Web/Presenters/GroupPresenter.php b/Web/Presenters/GroupPresenter.php
index beeede13..88844fbe 100644
--- a/Web/Presenters/GroupPresenter.php
+++ b/Web/Presenters/GroupPresenter.php
@@ -233,7 +233,10 @@ final class GroupPresenter extends OpenVKPresenter
$club->setEveryone_Can_Create_Topics(empty($this->postParam("everyone_can_create_topics")) ? 0 : 1);
$club->setDisplay_Topics_Above_Wall(empty($this->postParam("display_topics_above_wall")) ? 0 : 1);
$club->setEveryone_can_upload_audios(empty($this->postParam("upload_audios")) ? 0 : 1);
- $club->setHide_From_Global_Feed(empty($this->postParam("hide_from_global_feed")) ? 0 : 1);
+
+ if (!$club->isHidingFromGlobalFeedEnforced()) {
+ $club->setHide_From_Global_Feed(empty($this->postParam("hide_from_global_feed") ? 0 : 1));
+ }
$website = $this->postParam("website") ?? "";
if(empty($website))
@@ -279,48 +282,99 @@ final class GroupPresenter extends OpenVKPresenter
function renderSetAvatar(int $id)
{
- $photo = new Photo;
+ $this->assertUserLoggedIn();
+ $this->willExecuteWriteAction();
+
$club = $this->clubs->get($id);
- if ($club->isBanned()) $this->flashFail("err", tr("error"), tr("forbidden"));
- if($_SERVER["REQUEST_METHOD"] === "POST" && $_FILES["ava"]["error"] === UPLOAD_ERR_OK) {
+
+ if(!$club || $club->isBanned() || !$club->canBeModifiedBy($this->user->identity))
+ $this->flashFail("err", tr("error"), tr("forbidden"), NULL, true);
+
+ if($_SERVER["REQUEST_METHOD"] === "POST" && $_FILES["blob"]["error"] === UPLOAD_ERR_OK) {
try {
+ $photo = new Photo;
+
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
if($anon && $this->user->id === $club->getOwner()->getId())
$anon = $club->isOwnerHidden();
else if($anon)
$anon = $club->getManager($this->user->identity)->isHidden();
+
$photo->setOwner($this->user->id);
$photo->setDescription("Club image");
- $photo->setFile($_FILES["ava"]);
+ $photo->setFile($_FILES["blob"]);
$photo->setCreated(time());
$photo->setAnonymous($anon);
$photo->save();
(new Albums)->getClubAvatarAlbum($club)->addPhoto($photo);
- $flags = 0;
- $flags |= 0b00010000;
- $flags |= 0b10000000;
+ if($this->postParam("on_wall") == 1) {
+ $post = new Post;
+
+ $post->setOwner($this->user->id);
+ $post->setWall($club->getId() * -1);
+ $post->setCreated(time());
+ $post->setContent("");
- $post = new Post;
- $post->setOwner($this->user->id);
- $post->setWall($club->getId()*-1);
- $post->setCreated(time());
- $post->setContent("");
- $post->setFlags($flags);
- $post->save();
- $post->attach($photo);
+ $flags = 0;
+ $flags |= 0b00010000;
+ $flags |= 0b10000000;
- } catch(ISE $ex) {
- $name = $album->getName();
- $this->flashFail("err", tr("error"), tr("error_when_uploading_photo"));
+ $post->setFlags($flags);
+ $post->save();
+
+ $post->attach($photo);
+ }
+
+ } catch(\Throwable $ex) {
+ $this->flashFail("err", tr("error"), tr("error_when_uploading_photo"), NULL, true);
}
+
+ $this->returnJson([
+ "success" => true,
+ "new_photo" => $photo->getPrettyId(),
+ "url" => $photo->getURL(),
+ ]);
+ } else {
+ return " ";
}
- $this->returnJson([
- "url" => $photo->getURL(),
- "id" => $photo->getPrettyId()
- ]);
}
+
+ function renderDeleteAvatar(int $id) {
+ $this->assertUserLoggedIn();
+ $this->willExecuteWriteAction();
+
+ $club = $this->clubs->get($id);
+
+ if(!$club || $club->isBanned() || !$club->canBeModifiedBy($this->user->identity))
+ $this->flashFail("err", tr("error"), tr("forbidden"), NULL, true);
+
+ $avatar = $club->getAvatarPhoto();
+
+ if(!$avatar)
+ $this->flashFail("succ", tr("error"), "no avatar bro", NULL, true);
+
+ $avatar->isolate();
+
+ $newAvatar = $club->getAvatarPhoto();
+
+ if(!$newAvatar)
+ $this->returnJson([
+ "success" => true,
+ "has_new_photo" => false,
+ "new_photo" => NULL,
+ "url" => "/assets/packages/static/openvk/img/camera_200.png",
+ ]);
+ else
+ $this->returnJson([
+ "success" => true,
+ "has_new_photo" => true,
+ "new_photo" => $newAvatar->getPrettyId(),
+ "url" => $newAvatar->getURL(),
+ ]);
+ }
+
function renderEditBackdrop(int $id): void
{
$this->assertUserLoggedIn();
diff --git a/Web/Presenters/OpenVKPresenter.php b/Web/Presenters/OpenVKPresenter.php
index a171c2e3..a5a76011 100644
--- a/Web/Presenters/OpenVKPresenter.php
+++ b/Web/Presenters/OpenVKPresenter.php
@@ -274,7 +274,7 @@ abstract class OpenVKPresenter extends SimplePresenter
setlocale(LC_TIME, ...(explode(";", tr("__locale"))));
if (!OPENVK_ROOT_CONF["openvk"]["preferences"]["maintenanceMode"]["all"]) {
- if (OPENVK_ROOT_CONF["openvk"]["preferences"]["maintenanceMode"][$this->presenterName]) {
+ if ($this->presenterName && OPENVK_ROOT_CONF["openvk"]["preferences"]["maintenanceMode"][$this->presenterName]) {
$this->pass("openvk!Maintenance->section", $this->presenterName);
}
} else {
@@ -307,7 +307,7 @@ abstract class OpenVKPresenter extends SimplePresenter
$theme = Themepacks::i()[Session::i()->get("_sessionTheme", "ovk")];
} else if($this->requestParam("themePreview")) {
$theme = Themepacks::i()[$this->requestParam("themePreview")];
- } else if($this->user->identity !== NULL && $this->user->identity->getTheme()) {
+ } else if($this->user !== NULL && $this->user->identity !== NULL && $this->user->identity->getTheme()) {
$theme = $this->user->identity->getTheme();
}
diff --git a/Web/Presenters/SearchPresenter.php b/Web/Presenters/SearchPresenter.php
index 02394503..72c34e89 100644
--- a/Web/Presenters/SearchPresenter.php
+++ b/Web/Presenters/SearchPresenter.php
@@ -1,7 +1,7 @@
users = $users;
- $this->clubs = $clubs;
+ $this->users = new Users;
+ $this->clubs = new Clubs;
$this->posts = new Posts;
- $this->comments = new Comments;
$this->videos = new Videos;
$this->apps = new Applications;
- $this->notes = new Notes;
$this->audios = new Audios;
parent::__construct();
@@ -33,85 +29,101 @@ final class SearchPresenter extends OpenVKPresenter
{
$this->assertUserLoggedIn();
- $query = $this->queryParam("query") ?? "";
- $type = $this->queryParam("type") ?? "users";
- $sorter = $this->queryParam("sort") ?? "id";
- $invert = $this->queryParam("invert") == 1 ? "ASC" : "DESC";
- $page = (int) ($this->queryParam("p") ?? 1);
-
+ $query = $this->queryParam("q") ?? "";
+ $section = $this->queryParam("section") ?? "users";
+ $order = $this->queryParam("order") ?? "id";
+ $invert = (int) ($this->queryParam("invert") ?? 0) == 1;
+ $page = (int) ($this->queryParam("p") ?? 1);
+
# https://youtu.be/pSAWM5YuXx8
+ # https://youtu.be/FfNZRhIn2Vk
$repos = [
"groups" => "clubs",
"users" => "users",
"posts" => "posts",
- "comments" => "comments",
"videos" => "videos",
"audios" => "audios",
"apps" => "apps",
- "notes" => "notes"
+ "audios_playlists" => "audios"
+ ];
+ $parameters = [
+ "ignore_private" => true,
];
- switch($sorter) {
- default:
- case "id":
- $sort = "id " . $invert;
- break;
- case "name":
- $sort = "first_name " . $invert;
- break;
- case "rating":
- $sort = "rating " . $invert;
- break;
- case "length":
- if($type != "audios") break;
-
- $sort = "length " . $invert;
- break;
- case "listens":
- if($type != "audios") break;
-
- $sort = "listens " . $invert;
- break;
+ foreach($_REQUEST as $param_name => $param_value) {
+ if(is_null($param_value)) continue;
+
+ switch($param_name) {
+ default:
+ $parameters[$param_name] = $param_value;
+ break;
+ case 'marital_status':
+ case 'polit_views':
+ if((int) $param_value == 0) continue;
+ $parameters[$param_name] = $param_value;
+
+ break;
+ case 'is_online':
+ if((int) $param_value == 1)
+ $parameters['is_online'] = 1;
+
+ break;
+ case 'only_performers':
+ if((int) $param_value == 1 || $param_value == 'on')
+ $parameters['only_performers'] = true;
+
+ break;
+ case 'with_lyrics':
+ if($param_value == 'on' || $param_value == '1')
+ $parameters['with_lyrics'] = true;
+
+ break;
+ # дай бог работал этот case
+ case 'from_me':
+ if((int) $param_value != 1) continue;
+ $parameters['from_me'] = $this->user->id;
+
+ break;
+ }
}
- $parameters = [
- "type" => $this->queryParam("type"),
- "city" => $this->queryParam("city") != "" ? $this->queryParam("city") : NULL,
- "maritalstatus" => $this->queryParam("maritalstatus") != 0 ? $this->queryParam("maritalstatus") : NULL,
- "with_photo" => $this->queryParam("with_photo"),
- "status" => $this->queryParam("status") != "" ? $this->queryParam("status") : NULL,
- "politViews" => $this->queryParam("politViews") != 0 ? $this->queryParam("politViews") : NULL,
- "email" => $this->queryParam("email"),
- "telegram" => $this->queryParam("telegram"),
- "site" => $this->queryParam("site") != "" ? "https://".$this->queryParam("site") : NULL,
- "address" => $this->queryParam("address"),
- "is_online" => $this->queryParam("is_online") == 1 ? 1 : NULL,
- "interests" => $this->queryParam("interests") != "" ? $this->queryParam("interests") : NULL,
- "fav_mus" => $this->queryParam("fav_mus") != "" ? $this->queryParam("fav_mus") : NULL,
- "fav_films" => $this->queryParam("fav_films") != "" ? $this->queryParam("fav_films") : NULL,
- "fav_shows" => $this->queryParam("fav_shows") != "" ? $this->queryParam("fav_shows") : NULL,
- "fav_books" => $this->queryParam("fav_books") != "" ? $this->queryParam("fav_books") : NULL,
- "fav_quote" => $this->queryParam("fav_quote") != "" ? $this->queryParam("fav_quote") : NULL,
- "hometown" => $this->queryParam("hometown") != "" ? $this->queryParam("hometown") : NULL,
- "before" => $this->queryParam("datebefore") != "" ? strtotime($this->queryParam("datebefore")) : NULL,
- "after" => $this->queryParam("dateafter") != "" ? strtotime($this->queryParam("dateafter")) : NULL,
- "gender" => $this->queryParam("gender") != "" && $this->queryParam("gender") != 2 ? $this->queryParam("gender") : NULL,
- "doNotSearchPrivate" => true,
- "only_performers" => $this->queryParam("only_performers") == "on" ? "1" : NULL,
- "with_lyrics" => $this->queryParam("with_lyrics") == "on" ? true : NULL,
- ];
-
- $repo = $repos[$type] or $this->throwError(400, "Bad Request", "Invalid search entity $type.");
+ $repo = $repos[$section] or $this->throwError(400, "Bad Request", "Invalid search entity $section.");
- $results = $this->{$repo}->find($query, $parameters, $sort);
- $iterator = $results->page($page, 14);
+ $results = NULL;
+ switch($section) {
+ default:
+ $results = $this->{$repo}->find($query, $parameters, ['type' => $order, 'invert' => $invert]);
+ break;
+ case 'audios_playlists':
+ $results = $this->{$repo}->findPlaylists($query, $parameters, ['type' => $order, 'invert' => $invert]);
+ break;
+ }
+
+ $iterator = $results->page($page, OPENVK_DEFAULT_PER_PAGE);
$count = $results->size();
- $this->template->iterator = iterator_to_array($iterator);
+ $this->template->order = $order;
+ $this->template->invert = $invert;
+ $this->template->data = $this->template->iterator = iterator_to_array($iterator);
$this->template->count = $count;
- $this->template->type = $type;
+ $this->template->section = $section;
$this->template->page = $page;
- $this->template->perPage = 14;
+ $this->template->perPage = OPENVK_DEFAULT_PER_PAGE;
+ $this->template->query = $query;
+ $this->template->atSearch = true;
+
+ $this->template->paginatorConf = (object) [
+ "page" => $page,
+ "count" => $count,
+ "amount" => sizeof($this->template->data),
+ "perPage" => $this->template->perPage,
+ "atBottom" => false,
+ "tidy" => true,
+ "space" => 6,
+ 'pageCount' => ceil($count / $this->template->perPage),
+ ];
+ $this->template->extendedPaginatorConf = clone $this->template->paginatorConf;
+ $this->template->extendedPaginatorConf->space = 12;
}
}
diff --git a/Web/Presenters/UserPresenter.php b/Web/Presenters/UserPresenter.php
index 3785e47b..baf8f046 100644
--- a/Web/Presenters/UserPresenter.php
+++ b/Web/Presenters/UserPresenter.php
@@ -329,13 +329,15 @@ final class UserPresenter extends OpenVKPresenter
$user = $this->users->get((int) $this->postParam("id"));
if(!$user) exit("Invalid state");
- $user->toggleSubscription($this->user->identity);
+ if ($this->postParam("act") == "rej")
+ $user->changeFlags($this->user->identity, 0b10000000, true);
+ else
+ $user->toggleSubscription($this->user->identity);
- $this->redirect($user->getURL());
+ $this->redirect($_SERVER['HTTP_REFERER']);
}
- function renderSetAvatar()
- {
+ function renderSetAvatar() {
$this->assertUserLoggedIn();
$this->willExecuteWriteAction();
@@ -346,8 +348,8 @@ final class UserPresenter extends OpenVKPresenter
$photo->setFile($_FILES["blob"]);
$photo->setCreated(time());
$photo->save();
- } catch(ISE $ex) {
- $this->flashFail("err", tr("error"), tr("error_upload_failed"));
+ } catch(\Throwable $ex) {
+ $this->flashFail("err", tr("error"), tr("error_upload_failed"), NULL, (int)$this->postParam("ajax", true) == 1);
}
$album = (new Albums)->getUserAvatarAlbum($this->user->identity);
@@ -358,23 +360,57 @@ final class UserPresenter extends OpenVKPresenter
$flags = 0;
$flags |= 0b00010000;
- $post = new Post;
- $post->setOwner($this->user->id);
- $post->setWall($this->user->id);
- $post->setCreated(time());
- $post->setContent("");
- $post->setFlags($flags);
- $post->save();
- $post->attach($photo);
- if($this->postParam("ava", true) == (int)1) {
+ if($this->postParam("on_wall") == 1) {
+ $post = new Post;
+ $post->setOwner($this->user->id);
+ $post->setWall($this->user->id);
+ $post->setCreated(time());
+ $post->setContent("");
+ $post->setFlags($flags);
+ $post->save();
+
+ $post->attach($photo);
+ }
+
+ if((int)$this->postParam("ajax", true) == 1) {
$this->returnJson([
- "url" => $photo->getURL(),
- "id" => $photo->getPrettyId()
+ "success" => true,
+ "new_photo" => $photo->getPrettyId(),
+ "url" => $photo->getURL(),
]);
} else {
$this->flashFail("succ", tr("photo_saved"), tr("photo_saved_comment"));
}
}
+
+ function renderDeleteAvatar() {
+ $this->assertUserLoggedIn();
+ $this->willExecuteWriteAction();
+
+ $avatar = $this->user->identity->getAvatarPhoto();
+
+ if(!$avatar)
+ $this->flashFail("succ", tr("error"), "no avatar bro", NULL, true);
+
+ $avatar->isolate();
+
+ $newAvatar = $this->user->identity->getAvatarPhoto();
+
+ if(!$newAvatar)
+ $this->returnJson([
+ "success" => true,
+ "has_new_photo" => false,
+ "new_photo" => NULL,
+ "url" => "/assets/packages/static/openvk/img/camera_200.png",
+ ]);
+ else
+ $this->returnJson([
+ "success" => true,
+ "has_new_photo" => true,
+ "new_photo" => $newAvatar->getPrettyId(),
+ "url" => $newAvatar->getURL(),
+ ]);
+ }
function renderSettings(): void
{
diff --git a/Web/Presenters/VKAPIPresenter.php b/Web/Presenters/VKAPIPresenter.php
index 963c9ccc..9eb4565a 100644
--- a/Web/Presenters/VKAPIPresenter.php
+++ b/Web/Presenters/VKAPIPresenter.php
@@ -186,8 +186,12 @@ final class VKAPIPresenter extends OpenVKPresenter
function renderRoute(string $object, string $method): void
{
+ $callback = $this->queryParam("callback");
$authMechanism = $this->queryParam("auth_mechanism") ?? "token";
if($authMechanism === "roaming") {
+ if($callback)
+ $this->fail(-1, "User authorization failed: roaming mechanism is unavailable with jsonp.", $object, $method);
+
if(!$this->user->identity)
$this->fail(5, "User authorization failed: roaming mechanism is selected, but user is not logged in.", $object, $method);
else
@@ -234,8 +238,14 @@ final class VKAPIPresenter extends OpenVKPresenter
}
try {
- settype($val, $parameter->getType()->getName());
- $params[] = $val;
+ // Проверка типа параметра
+ $type = $parameter->getType();
+ if (($type && !$type->isBuiltin()) || is_null($val)) {
+ $params[] = $val;
+ } else {
+ settype($val, $parameter->getType()->getName());
+ $params[] = $val;
+ }
} catch (\Throwable $e) {
// Just ignore the exception, since
// some args are intended for internal use
@@ -253,10 +263,16 @@ final class VKAPIPresenter extends OpenVKPresenter
$result = json_encode([
"response" => $res,
]);
+
+ if($callback) {
+ $result = $callback . '(' . $result . ')';
+ header('Content-Type: application/javascript');
+ } else
+ header("Content-Type: application/json");
$size = strlen($result);
- header("Content-Type: application/json");
header("Content-Length: $size");
+
exit($result);
}
@@ -286,17 +302,31 @@ final class VKAPIPresenter extends OpenVKPresenter
$this->fail(28, "Invalid 2FA code", "internal", "acquireToken");
}
- $platform = $this->requestParam("client_name");
-
- $token = new APIToken;
- $token->setUser($user);
- $token->setPlatform($platform ?? (new WhichBrowser\Parser(getallheaders()))->toString());
- $token->save();
+ $token = NULL;
+ $tokenIsStale = true;
+ $platform = $this->requestParam("client_name");
+ $acceptsStale = $this->requestParam("accepts_stale");
+ if($acceptsStale == "1") {
+ if(is_null($platform))
+ $this->fail(101, "accepts_stale can only be used with explicitly set client_name", "internal", "acquireToken");
+
+ $token = (new APITokens)->getStaleByUser($uId, $platform);
+ }
+
+ if(is_null($token)) {
+ $tokenIsStale = false;
+
+ $token = new APIToken;
+ $token->setUser($user);
+ $token->setPlatform($platform ?? (new WhichBrowser\Parser(getallheaders()))->toString());
+ $token->save();
+ }
$payload = json_encode([
"access_token" => $token->getFormattedToken(),
"expires_in" => 0,
"user_id" => $uId,
+ "is_stale" => $tokenIsStale,
]);
$size = strlen($payload);
@@ -304,4 +334,42 @@ final class VKAPIPresenter extends OpenVKPresenter
header("Content-Length: $size");
exit($payload);
}
+
+ function renderOAuthLogin() {
+ $this->assertUserLoggedIn();
+
+ $client = $this->queryParam("client_name");
+ $postmsg = $this->queryParam("prefers_postMessage") ?? '0';
+ $stale = $this->queryParam("accepts_stale") ?? '0';
+ $origin = NULL;
+ $url = $this->queryParam("redirect_uri");
+ if(is_null($url) || is_null($client))
+ exit("Error: redirect_uri and client_name params are required.");
+
+ if($url != "about:blank") {
+ if(!filter_var($url, FILTER_VALIDATE_URL))
+ exit("Error: Invalid URL passed to redirect_uri.");
+
+ $parsedUrl = (object) parse_url($url);
+ if($parsedUrl->scheme != 'https' && $parsedUrl->scheme != 'http')
+ exit("Error: redirect_uri should either point to about:blank or to a web resource.");
+
+ $origin = "$parsedUrl->scheme://$parsedUrl->host";
+ if(!is_null($parsedUrl->port ?? NULL))
+ $origin .= ":$parsedUrl->port";
+
+ $url .= strpos($url, '?') === false ? '?' : '&';
+ } else {
+ $url .= "#";
+ if($postmsg == '1') {
+ exit("Error: prefers_postMessage can only be set if redirect_uri is not about:blank");
+ }
+ }
+
+ $this->template->clientName = $client;
+ $this->template->usePostMessage = $postmsg == '1';
+ $this->template->acceptsStale = $stale == '1';
+ $this->template->origin = $origin;
+ $this->template->redirectUri = $url;
+ }
}
diff --git a/Web/Presenters/templates/@layout.xml b/Web/Presenters/templates/@layout.xml
index e7d62d4b..7edecea5 100644
--- a/Web/Presenters/templates/@layout.xml
+++ b/Web/Presenters/templates/@layout.xml
@@ -18,9 +18,11 @@
{script "js/l10n.js"}
{script "js/openvk.cls.js"}
{script "js/node_modules/dashjs/dist/dash.all.min.js"}
+
{script "js/al_music.js"}
{css "js/node_modules/tippy.js/dist/backdrop.css"}
+ {css "js/node_modules/cropperjs/dist/cropper.css"}
{css "js/node_modules/tippy.js/dist/border.css"}
{css "js/node_modules/tippy.js/dist/svg-arrow.css"}
{css "js/node_modules/tippy.js/themes/light.css"}
@@ -85,7 +87,7 @@