Merge branch 'master' into infinityscroll2

This commit is contained in:
mrilyew 2024-11-17 13:18:40 +03:00 committed by GitHub
commit e3b5d60f89
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
91 changed files with 5045 additions and 3952 deletions

View file

@ -1,14 +1,6 @@
name: Build images name: Build images
on: on: [push, pull_request]
push:
# Publish `master` as Docker `latest` image.
branches:
- master
# Publish `v1.2.3` tags as releases.
tags:
- v*
env: env:
BASE_IMAGE_NAME: openvk BASE_IMAGE_NAME: openvk
@ -17,24 +9,21 @@ env:
DB_VERSION: "10.9" DB_VERSION: "10.9"
jobs: jobs:
build: buildbase:
runs-on: ubuntu-latest name: Build base images
strategy: strategy:
matrix: matrix:
arch: ['x86_64'] platform: [amd64, arm64]
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps: steps:
- uses: actions/checkout@v3
with:
lfs: false
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v3
- name: Change repository string to lowercase - name: Change repository string to lowercase
id: repositorystring id: repositorystring
@ -42,29 +31,114 @@ jobs:
with: with:
string: ${{ github.repository }} string: ${{ github.repository }}
- name: Base image meta
id: basemeta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/${{env.BASE_IMAGE_NAME}}
labels: |
org.opencontainers.image.documentation=https://github.com/OpenVK/openvk/blob/master/install/automated/docker/Readme.md
tags: |
type=sha
type=ref,event=branch
type=ref,event=pr
type=ref,event=tag
type=raw,value=latest,enable={{is_default_branch}}
- name: Log into registry - name: Log into registry
if: github.event_name != 'pull_request'
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build base image - name: Build base image
run: | uses: docker/build-push-action@v6
IMAGE_ID=ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/$BASE_IMAGE_NAME with:
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') platforms: linux/${{matrix.platform}}
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') file: install/automated/docker/openvk.Dockerfile
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') tags: ${{ steps.basemeta.outputs.tags }}
[ "$VERSION" == "master" ] && VERSION=latest labels: ${{ steps.basemeta.outputs.labels }}
echo IMAGE_ID=$IMAGE_ID push: ${{ github.event_name != 'pull_request' }}
echo VERSION=$VERSION build-args: |
GITREPO=${{ steps.repositorystring.outputs.lowercase }}
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 }} builddb:
name: Build DB images
strategy:
matrix:
platform: [amd64, arm64]
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
- name: Change repository string to lowercase
id: repositorystring
uses: Entepotenz/change-string-case-action-min-dependencies@v1.1.0
with:
string: ${{ github.repository }}
- name: MariaDB primary meta
id: db-primarymeta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/${{env.DB_IMAGE_NAME}}
labels: |
org.opencontainers.image.title=OpenVK MariaDB (Primary)
org.opencontainers.image.description=OpenVK's image for MariaDB for primary database.
org.opencontainers.image.documentation=https://github.com/OpenVK/openvk/blob/master/install/automated/docker/Readme.md
tags: |
type=sha,prefix=${{env.DB_VERSION}}-primary-sha-
type=ref,event=branch,prefix=${{env.DB_VERSION}}-primary-
type=ref,event=pr,prefix=${{env.DB_VERSION}}-primary-pr-
type=ref,event=tag,prefix=${{env.DB_VERSION}}-primary-
type=raw,value=${{env.DB_VERSION}}-primary,enable={{is_default_branch}}
- name: MariaDB event meta
id: db-eventmeta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/${{env.DB_IMAGE_NAME}}
labels: |
org.opencontainers.image.title=OpenVK MariaDB (EventDB)
org.opencontainers.image.description=OpenVK's image for MariaDB for event database.
org.opencontainers.image.documentation=https://github.com/OpenVK/openvk/blob/master/install/automated/docker/Readme.md
tags: |
type=sha,prefix=${{env.DB_VERSION}}-eventdb-sha-
type=ref,event=branch,prefix=${{env.DB_VERSION}}-eventdb-
type=ref,event=pr,prefix=${{env.DB_VERSION}}-eventdb-pr-
type=ref,event=tag,prefix=${{env.DB_VERSION}}-eventdb-
type=raw,value=${{env.DB_VERSION}}-eventdb,enable={{is_default_branch}}
- name: Log into registry
if: github.event_name != 'pull_request'
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build MariaDB primary image - name: Build MariaDB primary image
run: | uses: docker/build-push-action@v6
IMAGE_NAME=ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/$DB_IMAGE_NAME:$DB_VERSION-primary with:
push: ${{ github.event_name != 'pull_request' }}
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/mariadb-primary.Dockerfile --build-arg VERSION=$DB_VERSION platforms: linux/${{matrix.platform}}
file: install/automated/docker/mariadb-primary.Dockerfile
tags: ${{ steps.db-primarymeta.outputs.tags }}
labels: ${{ steps.db-primarymeta.outputs.labels }}
build-args: |
VERSION=${{env.DB_VERSION}}
- name: Build MariaDB event image - name: Build MariaDB event image
run: | uses: docker/build-push-action@v6
IMAGE_NAME=ghcr.io/${{ steps.repositorystring.outputs.lowercase }}/$EVENT_IMAGE_NAME:$DB_VERSION-eventdb with:
push: ${{ github.event_name != 'pull_request' }}
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/mariadb-eventdb.Dockerfile --build-arg VERSION=$DB_VERSION platforms: linux/${{matrix.platform}}
file: install/automated/docker/mariadb-eventdb.Dockerfile
tags: ${{ steps.db-eventmeta.outputs.tags }}
labels: ${{ steps.db-eventmeta.outputs.labels }}
build-args: |
VERSION=${{env.DB_VERSION}}

View file

@ -4,7 +4,7 @@ _[Русский](README_RU.md)_
**OpenVK** is an attempt to create a simple CMS that ~~cosplays~~ imitates old VKontakte. Code provided here is not stable yet. **OpenVK** is an attempt to create a simple CMS that ~~cosplays~~ imitates old VKontakte. Code provided here is not stable yet.
VKontakte belongs to Pavel Durov and VK Group. VKontakte belongs to VK (formerly Mail.ru 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://ovk.to/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).
@ -36,7 +36,7 @@ Here is our minimum hardware recommendation:
### Installation procedure ### Installation procedure
1. Install PHP 7.4, web-server, Composer, Node.js, Yarn and [Chandler](https://github.com/openvk/chandler) 1. Install PHP 7.4, web-server, Composer, Node.js, NPM and [Chandler](https://github.com/openvk/chandler)
* PHP 8 is still being tested; the functionality of the engine on this version of PHP is not yet guaranteed. * PHP 8 is still being tested; the functionality of the engine on this version of PHP is not yet guaranteed.
@ -65,7 +65,7 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
7. Copy `openvk-example.yml` to `openvk.yml` and change options to your liking 7. Copy `openvk-example.yml` to `openvk.yml` and change options to your liking
8. Run `composer install` in OpenVK directory 8. Run `composer install` in OpenVK directory
9. Run `composer install` in commitcaptcha directory 9. Run `composer install` in commitcaptcha directory
10. Move to `Web/static/js` and execute `yarn install` 10. Move to `Web/static/js` and execute `npm install`
11. Set `openvk` as your root app in `chandler.yml` 11. Set `openvk` as your root app in `chandler.yml`
Once you are done, you can login as a system administrator on the network itself (no registration required): Once you are done, you can login as a system administrator on the network itself (no registration required):

View file

@ -4,7 +4,7 @@ _[English](README.md)_
**OpenVK** — это попытка создать простую CMS, которая ~~косплеит~~ имитирует старый ВКонтакте. На данный момент, представленный здесь исходный код проекта пока не является стабильным. **OpenVK** — это попытка создать простую CMS, которая ~~косплеит~~ имитирует старый ВКонтакте. На данный момент, представленный здесь исходный код проекта пока не является стабильным.
ВКонтакте принадлежит Павлу Дурову и VK Group. ВКонтакте принадлежит VK (в прошлом Mail.ru Group).
Честно говоря, мы даже не знаем, работает ли она вообще. Однако, эта версия поддерживается, и мы будем рады принять ваши сообщения об ошибках [в нашем баг-трекере](https://github.com/openvk/openvk/projects/1). Вы также можете отправлять их через [вкладку "Помощь"](https://ovk.to/support?act=new) (для этого вам понадобится учетная запись OpenVK). Честно говоря, мы даже не знаем, работает ли она вообще. Однако, эта версия поддерживается, и мы будем рады принять ваши сообщения об ошибках [в нашем баг-трекере](https://github.com/openvk/openvk/projects/1). Вы также можете отправлять их через [вкладку "Помощь"](https://ovk.to/support?act=new) (для этого вам понадобится учетная запись OpenVK).
@ -28,7 +28,7 @@ _[English](README.md)_
### Процедура установки ### Процедура установки
1. Установите PHP 7.4, веб-сервер, Composer, Node.js, Yarn и [Chandler](https://github.com/openvk/chandler) 1. Установите PHP 7.4, веб-сервер, Composer, Node.js, NPM и [Chandler](https://github.com/openvk/chandler)
* PHP 8 пока ещё тестируется, работоспособность движка на этой версии PHP пока не гарантируется. * PHP 8 пока ещё тестируется, работоспособность движка на этой версии PHP пока не гарантируется.
@ -57,7 +57,7 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
7. Скопируйте `openvk-example.yml` в `openvk.yml` и измените параметры под свои нужды 7. Скопируйте `openvk-example.yml` в `openvk.yml` и измените параметры под свои нужды
8. Запустите `composer install` в директории OpenVK 8. Запустите `composer install` в директории OpenVK
9. Запустите `composer install` в директории commitcaptcha 9. Запустите `composer install` в директории commitcaptcha
10. Перейдите в `Web/static/js` и выполните `yarn install` 10. Перейдите в `Web/static/js` и выполните `npm install`
11. Установите `openvk` в качестве корневого приложения в файле `chandler.yml` 11. Установите `openvk` в качестве корневого приложения в файле `chandler.yml`
После этого вы можете войти как системный администратор в саму сеть (регистрация не требуется): После этого вы можете войти как системный администратор в саму сеть (регистрация не требуется):

View file

@ -1,92 +0,0 @@
<?php declare(strict_types=1);
namespace openvk\ServiceAPI;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Repositories\{Photos as PhotosRepo, Albums, Clubs};
class Photos implements Handler
{
protected $user;
protected $photos;
function __construct(?User $user)
{
$this->user = $user;
$this->photos = new PhotosRepo;
}
function getPhotos(int $page = 1, int $album = 0, callable $resolve, callable $reject)
{
if($album == 0) {
$photos = $this->photos->getEveryUserPhoto($this->user, $page, 24);
$count = $this->photos->getUserPhotosCount($this->user);
} else {
$album = (new Albums)->get($album);
if(!$album || $album->isDeleted())
$reject(55, "Invalid .");
if($album->getOwner() instanceof User) {
if($album->getOwner()->getId() != $this->user->getId())
$reject(555, "Access to album denied");
} else {
if(!$album->getOwner()->canBeModifiedBy($this->user))
$reject(555, "Access to album denied");
}
$photos = $album->getPhotos($page, 24);
$count = $album->size();
}
$arr = [
"count" => $count,
"items" => [],
];
foreach($photos as $photo) {
$res = json_decode(json_encode($photo->toVkApiStruct()), true);
$arr["items"][] = $res;
}
$resolve($arr);
}
function getAlbums(int $club, callable $resolve, callable $reject)
{
$albumsRepo = (new Albums);
$count = $albumsRepo->getUserAlbumsCount($this->user);
$albums = $albumsRepo->getUserAlbums($this->user, 1, $count);
$arr = [
"count" => $count,
"items" => [],
];
foreach($albums as $album) {
$res = ["id" => $album->getId(), "name" => $album->getName()];
$arr["items"][] = $res;
}
if($club > 0) {
$cluber = (new Clubs)->get($club);
if(!$cluber || !$cluber->canBeModifiedBy($this->user))
$reject(1337, "Invalid (club), or you can't modify him");
$clubCount = (new Albums)->getClubAlbumsCount($cluber);
$clubAlbums = (new Albums)->getClubAlbums($cluber, 1, $clubCount);
foreach($clubAlbums as $albumr) {
$res = ["id" => $albumr->getId(), "name" => $albumr->getName()];
$arr["items"][] = $res;
}
$arr["count"] = $arr["count"] + $clubCount;
}
$resolve($arr);
}
}

View file

@ -1,156 +0,0 @@
<?php declare(strict_types=1);
namespace openvk\ServiceAPI;
use openvk\Web\Models\Entities\{User, Post};
use openvk\Web\Models\Repositories\{Videos, Comments, Clubs};
use Chandler\MVC\Routing\Router;
class Video implements Handler
{
protected $user;
protected $videos;
protected $comments;
protected $groups;
function __construct(?User $user)
{
$this->user = $user;
$this->videos = new Videos;
$this->comments = new Comments;
$this->groups = new Clubs;
}
function getVideo(int $id, callable $resolve, callable $reject)
{
$video = $this->videos->get($id);
if(!$video || $video->isDeleted()) {
$reject(2, "Video does not exists");
}
if(method_exists($video, "canBeViewedBy") && !$video->canBeViewedBy($this->user)) {
$reject(4, "Access to video denied");
}
if(!$video->getOwner()->getPrivacyPermission('videos.read', $this->user)) {
$reject(8, "Access to video denied: this user chose to hide his videos");
}
$prevVideo = NULL;
$nextVideo = NULL;
$lastVideo = $this->videos->getLastVideo($video->getOwner());
if($video->getVirtualId() - 1 != 0) {
for($i = $video->getVirtualId(); $i != 0; $i--) {
$maybeVideo = (new Videos)->getByOwnerAndVID($video->getOwner()->getId(), $i);
if(!is_null($maybeVideo) && !$maybeVideo->isDeleted() && $maybeVideo->getId() != $video->getId()) {
if(method_exists($maybeVideo, "canBeViewedBy") && !$maybeVideo->canBeViewedBy($this->user)) {
continue;
}
$prevVideo = $maybeVideo;
break;
}
}
}
if(is_null($lastVideo) || $lastVideo->getId() == $video->getId()) {
$nextVideo = NULL;
} else {
for($i = $video->getVirtualId(); $i <= $lastVideo->getVirtualId(); $i++) {
$maybeVideo = (new Videos)->getByOwnerAndVID($video->getOwner()->getId(), $i);
if(!is_null($maybeVideo) && !$maybeVideo->isDeleted() && $maybeVideo->getId() != $video->getId()) {
if(method_exists($maybeVideo, "canBeViewedBy") && !$maybeVideo->canBeViewedBy($this->user)) {
continue;
}
$nextVideo = $maybeVideo;
break;
}
}
}
$res = [
"id" => $video->getId(),
"title" => $video->getName(),
"owner" => $video->getOwner()->getId(),
"commentsCount" => $video->getCommentsCount(),
"description" => $video->getDescription(),
"type" => $video->getType(),
"name" => $video->getOwner()->getCanonicalName(),
"pretty_id" => $video->getPrettyId(),
"virtual_id" => $video->getVirtualId(),
"published" => (string)$video->getPublicationTime(),
"likes" => $video->getLikesCount(),
"has_like" => $video->hasLikeFrom($this->user),
"author" => $video->getOwner()->getCanonicalName(),
"canBeEdited" => $video->getOwner()->getId() == $this->user->getId(),
"isProcessing" => $video->getType() == 0 && $video->getURL() == "/assets/packages/static/openvk/video/rendering.mp4",
"prevVideo" => !is_null($prevVideo) ? $prevVideo->getId() : null,
"nextVideo" => !is_null($nextVideo) ? $nextVideo->getId() : null,
];
if($video->getType() == 1) {
$res["embed"] = $video->getVideoDriver()->getEmbed();
} else {
$res["url"] = $video->getURL();
}
$resolve($res);
}
function shareVideo(int $owner, int $vid, int $type, string $message, int $club, bool $signed, bool $asGroup, callable $resolve, callable $reject)
{
$video = $this->videos->getByOwnerAndVID($owner, $vid);
if(!$video || $video->isDeleted()) {
$reject(16, "Video does not exists");
}
if(method_exists($video, "canBeViewedBy") && !$video->canBeViewedBy($this->user)) {
$reject(32, "Access to video denied");
}
if(!$video->getOwner()->getPrivacyPermission('videos.read', $this->user)) {
$reject(8, "Access to video denied: this user chose to hide his videos");
}
$flags = 0;
$nPost = new Post;
$nPost->setOwner($this->user->getId());
if($type == 0) {
$nPost->setWall($this->user->getId());
} else {
$club = $this->groups->get($club);
if(!$club || $club->isDeleted() || !$club->canBeModifiedBy($this->user)) {
$reject(64, "Can't do repost to this club");
}
if($asGroup)
$flags |= 0b10000000;
if($signed)
$flags |= 0b01000000;
$nPost->setWall($club->getId() * -1);
}
$nPost->setContent($message);
$nPost->setFlags($flags);
$nPost->save();
$nPost->attach($video);
$res = [
"id" => $nPost->getId(),
"pretty_id" => $nPost->getPrettyId(),
];
$resolve($res);
}
}

View file

@ -80,67 +80,4 @@ class Wall implements Handler
$resolve($post->getId()); $resolve($post->getId());
} }
function getMyNotes(callable $resolve, callable $reject)
{
$count = $this->notes->getUserNotesCount($this->user);
$myNotes = $this->notes->getUserNotes($this->user, 1, $count);
$arr = [
"count" => $count,
"closed" => $this->user->getPrivacySetting("notes.read"),
"items" => [],
];
foreach($myNotes as $note) {
$arr["items"][] = [
"id" => $note->getId(),
"name" => ovk_proc_strtr($note->getName(), 30),
#"preview" => $note->getPreview()
];
}
$resolve($arr);
}
function getVideos(int $page = 1, callable $resolve, callable $reject)
{
$videos = $this->videos->getByUser($this->user, $page, 8);
$count = $this->videos->getUserVideosCount($this->user);
$arr = [
"count" => $count,
"items" => [],
];
foreach($videos as $video) {
$res = json_decode(json_encode($video->toVkApiStruct($this->user)), true);
$res["video"]["author_name"] = $video->getOwner()->getCanonicalName();
$arr["items"][] = $res;
}
$resolve($arr);
}
function searchVideos(int $page = 1, string $query, callable $resolve, callable $reject)
{
$dbc = $this->videos->find($query);
$videos = $dbc->page($page, 8);
$count = $dbc->size();
$arr = [
"count" => $count,
"items" => [],
];
foreach($videos as $video) {
$res = json_decode(json_encode($video->toVkApiStruct($this->user)), true);
$res["video"]["author_name"] = $video->getOwner()->getCanonicalName();
$arr["items"][] = $res;
}
$resolve($arr);
}
} }

View file

@ -11,6 +11,12 @@ final class Groups extends VKAPIRequestHandler
{ {
$this->requireUser(); $this->requireUser();
# InfoApp fix
if($filter == "admin" && ($user_id != 0 && $user_id != $this->getUser()->getId())) {
$this->fail(15, 'Access denied: filter admin is available only for current user');
}
$clbs = [];
if($user_id == 0) { if($user_id == 0) {
foreach($this->getUser()->getClubs($offset, $filter == "admin", $count, true) as $club) foreach($this->getUser()->getClubs($offset, $filter == "admin", $count, true) as $club)
$clbs[] = $club; $clbs[] = $club;

View file

@ -32,6 +32,7 @@ final class Newsfeed extends VKAPIRequestHandler
->select("id") ->select("id")
->where("wall IN (?)", $ids) ->where("wall IN (?)", $ids)
->where("deleted", 0) ->where("deleted", 0)
->where("suggested", 0)
->where("id < (?)", empty($start_from) ? PHP_INT_MAX : $start_from) ->where("id < (?)", empty($start_from) ? PHP_INT_MAX : $start_from)
->where("? <= created", empty($start_time) ? 0 : $start_time) ->where("? <= created", empty($start_time) ? 0 : $start_time)
->where("? >= created", empty($end_time) ? PHP_INT_MAX : $end_time) ->where("? >= created", empty($end_time) ? PHP_INT_MAX : $end_time)
@ -56,6 +57,15 @@ final class Newsfeed extends VKAPIRequestHandler
if($this->getUser()->getNsfwTolerance() === User::NSFW_INTOLERANT) if($this->getUser()->getNsfwTolerance() === User::NSFW_INTOLERANT)
$queryBase .= " AND `nsfw` = 0"; $queryBase .= " AND `nsfw` = 0";
if($return_banned == 0) {
$ignored_sources_ids = $this->getUser()->getIgnoredSources(0, OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50, true);
if(sizeof($ignored_sources_ids) > 0) {
$imploded_ids = implode("', '", $ignored_sources_ids);
$queryBase .= " AND `posts`.`wall` NOT IN ('$imploded_ids')";
}
}
$start_from = empty($start_from) ? PHP_INT_MAX : $start_from; $start_from = empty($start_from) ? PHP_INT_MAX : $start_from;
$start_time = empty($start_time) ? 0 : $start_time; $start_time = empty($start_time) ? 0 : $start_time;
@ -74,4 +84,152 @@ final class Newsfeed extends VKAPIRequestHandler
return $response; return $response;
} }
function getByType(string $feed_type = 'top', string $fields = "", int $start_from = 0, int $start_time = 0, int $end_time = 0, int $offset = 0, int $count = 30, int $extended = 0, int $return_banned = 0)
{
$this->requireUser();
switch($feed_type) {
case 'top':
return $this->getGlobal($fields, $start_from, $start_time, $end_time, $offset, $count, $extended, $return_banned);
break;
default:
return $this->get($fields, $start_from, $start_time, $end_time, $offset, $count, $extended);
break;
}
}
function getBanned(int $extended = 0, string $fields = "", string $name_case = "nom", int $merge = 0): object
{
$this->requireUser();
$offset = 0;
$count = OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50;
$banned = $this->getUser()->getIgnoredSources($offset, $count, ($extended != 1));
$return_object = (object) [
'groups' => [],
'members' => [],
];
if($extended == 0) {
foreach($banned as $ban) {
if($ban > 0)
$return_object->members[] = $ban;
else
$return_object->groups[] = $ban;
}
} else {
if($merge == 1) {
$return_object = (object) [
'count' => sizeof($banned),
'items' => [],
];
foreach($banned as $ban) {
$return_object->items[] = $ban->toVkApiStruct($this->getUser(), $fields);
}
} else {
$return_object = (object) [
'groups' => [],
'profiles' => [],
];
foreach($banned as $ban) {
if($ban->getRealId() > 0)
$return_object->profiles[] = $ban->toVkApiStruct($this->getUser(), $fields);
else
$return_object->groups[] = $ban->toVkApiStruct($this->getUser(), $fields);
}
}
}
return $return_object;
}
function addBan(string $user_ids = "", string $group_ids = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
# Formatting input ids
if(!empty($user_ids)) {
$user_ids = array_map(function($el) {
return (int)$el;
}, explode(',', $user_ids));
$user_ids = array_unique($user_ids);
} else
$user_ids = [];
if(!empty($group_ids)) {
$group_ids = array_map(function($el) {
return abs((int)$el) * -1;
}, explode(',', $group_ids));
$group_ids = array_unique($group_ids);
} else
$group_ids = [];
$ids = array_merge($user_ids, $group_ids);
if(sizeof($ids) < 1)
return 0;
if(sizeof($ids) > 10)
$this->fail(-10, "Limit of 'ids' is 10");
$config_limit = OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50;
$user_ignores = $this->getUser()->getIgnoredSourcesCount();
if(($user_ignores + sizeof($ids)) > $config_limit) {
$this->fail(-50, "Ignoring limit exceeded");
}
$entities = get_entities($ids);
$successes = 0;
foreach($entities as $entity) {
if(!$entity || $entity->getRealId() === $this->getUser()->getRealId() || $entity->isHideFromGlobalFeedEnabled() || $entity->isIgnoredBy($this->getUser())) continue;
$entity->addIgnore($this->getUser());
$successes += 1;
}
return 1;
}
function deleteBan(string $user_ids = "", string $group_ids = "")
{
$this->requireUser();
$this->willExecuteWriteAction();
if(!empty($user_ids)) {
$user_ids = array_map(function($el) {
return (int)$el;
}, explode(',', $user_ids));
$user_ids = array_unique($user_ids);
} else
$user_ids = [];
if(!empty($group_ids)) {
$group_ids = array_map(function($el) {
return abs((int)$el) * -1;
}, explode(',', $group_ids));
$group_ids = array_unique($group_ids);
} else
$group_ids = [];
$ids = array_merge($user_ids, $group_ids);
if(sizeof($ids) < 1)
return 0;
if(sizeof($ids) > 10)
$this->fail(-10, "Limit of ids is 10");
$entities = get_entities($ids);
$successes = 0;
foreach($entities as $entity) {
if(!$entity || $entity->getRealId() === $this->getUser()->getRealId() || !$entity->isIgnoredBy($this->getUser())) continue;
$entity->removeIgnore($this->getUser());
$successes += 1;
}
return 1;
}
} }

View file

@ -427,7 +427,7 @@ final class Photos extends VKAPIRequestHandler
$this->fail(15, "Access denied"); $this->fail(15, "Access denied");
$photos = array_slice(iterator_to_array($album->getPhotos(1, $count + $offset)), $offset); $photos = array_slice(iterator_to_array($album->getPhotos(1, $count + $offset)), $offset);
$res["count"] = sizeof($photos); $res["count"] = $album->size();
foreach($photos as $photo) { foreach($photos as $photo) {
if(!$photo || $photo->isDeleted()) continue; if(!$photo || $photo->isDeleted()) continue;
@ -638,15 +638,15 @@ final class Photos extends VKAPIRequestHandler
$this->fail(4, "This method doesn't works with clubs"); $this->fail(4, "This method doesn't works with clubs");
$user = (new UsersRepo)->get($owner_id); $user = (new UsersRepo)->get($owner_id);
if(!$user) if(!$user)
$this->fail(4, "Invalid user"); $this->fail(4, "Invalid user");
if(!$user->getPrivacyPermission('photos.read', $this->getUser())) if(!$user->getPrivacyPermission('photos.read', $this->getUser()))
$this->fail(21, "This user chose to hide his albums."); $this->fail(21, "This user chose to hide his albums.");
$photos = array_slice(iterator_to_array((new PhotosRepo)->getEveryUserPhoto($user, 1, $count + $offset)), $offset); $photos = (new PhotosRepo)->getEveryUserPhoto($user, $offset, $count);
$res = [ $res = [
"count" => (new PhotosRepo)->getUserPhotosCount($user),
"items" => [], "items" => [],
]; ];

View file

@ -11,24 +11,55 @@ use openvk\Web\Models\Repositories\Comments as CommentsRepo;
final class Video extends VKAPIRequestHandler final class Video extends VKAPIRequestHandler
{ {
function get(int $owner_id, string $videos = "", int $offset = 0, int $count = 30, int $extended = 0): object function get(int $owner_id = 0, string $videos = "", string $fields = "", int $offset = 0, int $count = 30, int $extended = 0): object
{ {
$this->requireUser(); $this->requireUser();
if(!empty($videos)) { if(!empty($videos)) {
$vids = explode(',', $videos); $vids = explode(',', $videos);
$profiles = [];
foreach($vids as $vid) $groups = [];
{ foreach($vids as $vid) {
$id = explode("_", $vid); $id = explode("_", $vid);
$items = []; $items = [];
$video = (new VideosRepo)->getByOwnerAndVID(intval($id[0]), intval($id[1])); $video = (new VideosRepo)->getByOwnerAndVID(intval($id[0]), intval($id[1]));
if($video) { if($video && !$video->isDeleted()) {
$items[] = $video->getApiStructure($this->getUser()); $out_video = $video->getApiStructure($this->getUser())->video;
$items[] = $out_video;
if($out_video['owner_id']) {
if($out_video['owner_id'] > 0)
$profiles[] = $out_video['owner_id'];
else
$groups[] = abs($out_video['owner_id']);
}
} }
} }
if($extended == 1) {
$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" => sizeof($items),
"items" => $items,
"profiles" => $profilesFormatted,
"groups" => $groupsFormatted,
];
}
return (object) [ return (object) [
"count" => count($items), "count" => count($items),
@ -46,12 +77,46 @@ final class Video extends VKAPIRequestHandler
if(!$user->getPrivacyPermission('videos.read', $this->getUser())) if(!$user->getPrivacyPermission('videos.read', $this->getUser()))
$this->fail(21, "This user chose to hide his videos."); $this->fail(21, "This user chose to hide his videos.");
$videos = (new VideosRepo)->getByUser($user, $offset + 1, $count); $videos = (new VideosRepo)->getByUserLimit($user, $offset, $count);
$videosCount = (new VideosRepo)->getUserVideosCount($user); $videosCount = (new VideosRepo)->getUserVideosCount($user);
$items = []; $items = [];
foreach ($videos as $video) { $profiles = [];
$items[] = $video->getApiStructure($this->getUser()); $groups = [];
foreach($videos as $video) {
$video = $video->getApiStructure($this->getUser())->video;
$items[] = $video;
if($video['owner_id']) {
if($video['owner_id'] > 0)
$profiles[] = $video['owner_id'];
else
$groups[] = abs($video['owner_id']);
}
}
if($extended == 1) {
$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" => $videosCount,
"items" => $items,
"profiles" => $profilesFormatted,
"groups" => $groupsFormatted,
];
} }
return (object) [ return (object) [

View file

@ -284,12 +284,16 @@ final class Wall extends VKAPIRequestHandler
foreach($psts as $pst) { foreach($psts as $pst) {
$id = explode("_", $pst); $id = explode("_", $pst);
$post = (new PostsRepo)->getPostById(intval($id[0]), intval($id[1])); $post = (new PostsRepo)->getPostById(intval($id[0]), intval($id[1]), true);
if($post && !$post->isDeleted()) { if($post && !$post->isDeleted()) {
if(!$post->canBeViewedBy($this->getUser())) if(!$post->canBeViewedBy($this->getUser()))
continue; continue;
if($post->getSuggestionType() != 0 && !$post->canBeEditedBy($this->getUser())) {
continue;
}
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId(); $from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
$attachments = []; $attachments = [];
$repost = []; // чел высрал семь сигарет 😳 помянем 🕯 $repost = []; // чел высрал семь сигарет 😳 помянем 🕯
@ -473,7 +477,7 @@ final class Wall extends VKAPIRequestHandler
]; ];
} }
function post(string $owner_id, string $message = "", int $from_group = 0, int $signed = 0, string $attachments = "", int $post_id = 0): object function post(string $owner_id, string $message = "", string $copyright = "", int $from_group = 0, int $signed = 0, string $attachments = "", int $post_id = 0): object
{ {
$this->requireUser(); $this->requireUser();
$this->willExecuteWriteAction(); $this->willExecuteWriteAction();
@ -551,7 +555,17 @@ final class Wall extends VKAPIRequestHandler
if($signed == 1) if($signed == 1)
$flags |= 0b01000000; $flags |= 0b01000000;
if(empty($message) && empty($attachments)) $parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'poll', 'audio']);
$final_attachments = [];
$should_be_suggested = $owner_id < 0 && !$wallOwner->canBeModifiedBy($this->getUser()) && $wallOwner->getWallType() == 2;
foreach($parsed_attachments as $attachment) {
if($attachment && !$attachment->isDeleted() && $attachment->canBeViewedBy($this->getUser()) &&
!(method_exists($attachment, 'getVoters') && $attachment->getOwner()->getId() != $this->getUser()->getId())) {
$final_attachments[] = $attachment;
}
}
if((empty($message) && (empty($attachments) || sizeof($final_attachments) < 1)))
$this->fail(100, "Required parameter 'message' missing."); $this->fail(100, "Required parameter 'message' missing.");
try { try {
@ -569,7 +583,7 @@ final class Wall extends VKAPIRequestHandler
} catch(\Throwable) {} } catch(\Throwable) {}
} }
if($owner_id < 0 && !$wallOwner->canBeModifiedBy($this->getUser()) && $wallOwner->getWallType() == 2) if($should_be_suggested)
$post->setSuggested(1); $post->setSuggested(1);
$post->save(); $post->save();
@ -577,134 +591,70 @@ final class Wall extends VKAPIRequestHandler
$this->fail(100, "One of the parameters specified was missing or invalid"); $this->fail(100, "One of the parameters specified was missing or invalid");
} }
# TODO use parseAttachments foreach($final_attachments as $attachment) {
if(!empty($attachments)) { $post->attach($attachment);
$attachmentsArr = explode(",", $attachments);
# Аттачи такого вида: [тип][id владельца]_[id вложения]
# Пример: photo1_1
if(sizeof($attachmentsArr) > 10)
$this->fail(50, "Too many attachments");
preg_match_all("/poll/m", $attachments, $matches, PREG_SET_ORDER, 0);
if(sizeof($matches) > 1)
$this->fail(85, "Too many polls");
foreach($attachmentsArr as $attac) {
$attachmentType = NULL;
if(str_contains($attac, "photo"))
$attachmentType = "photo";
elseif(str_contains($attac, "video"))
$attachmentType = "video";
elseif(str_contains($attac, "note"))
$attachmentType = "note";
elseif(str_contains($attac, "poll"))
$attachmentType = "poll";
elseif(str_contains($attac, "audio"))
$attachmentType = "audio";
else
$this->fail(205, "Unknown attachment type");
$attachment = str_replace($attachmentType, "", $attac);
$attachmentOwner = (int)explode("_", $attachment)[0];
$attachmentId = (int)end(explode("_", $attachment));
$attacc = NULL;
if($attachmentType == "photo") {
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
$this->fail(100, "Invalid photo");
if(!$attacc->getOwner()->getPrivacyPermission('photos.read', $this->getUser()))
$this->fail(43, "Access to photo denied");
$post->attach($attacc);
} elseif($attachmentType == "video") {
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
$this->fail(100, "Video does not exists");
if(!$attacc->getOwner()->getPrivacyPermission('videos.read', $this->getUser()))
$this->fail(43, "Access to video denied");
$post->attach($attacc);
} elseif($attachmentType == "note") {
$attacc = (new NotesRepo)->getNoteById($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
$this->fail(100, "Note does not exist");
if(!$attacc->getOwner()->getPrivacyPermission('notes.read', $this->getUser()))
$this->fail(11, "Access to note denied");
$post->attach($attacc);
} elseif($attachmentType == "poll") {
$attacc = (new PollsRepo)->get($attachmentId);
if(!$attacc || $attacc->isDeleted())
$this->fail(100, "Poll does not exist");
if($attacc->getOwner()->getId() != $this->getUser()->getId())
$this->fail(43, "You do not have access to this poll");
$post->attach($attacc);
} elseif($attachmentType == "audio") {
$attacc = (new AudiosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
$this->fail(100, "Audio does not exist");
$post->attach($attacc);
}
}
} }
if($wall > 0 && $wall !== $this->user->identity->getId()) if($wall > 0 && $wall !== $this->user->identity->getId())
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit(); (new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
if($owner_id < 0 && !$wallOwner->canBeModifiedBy($this->getUser()) && $wallOwner->getWallType() == 2) {
$suggsCount = (new PostsRepo)->getSuggestedPostsCount($wallOwner->getId());
if($suggsCount % 10 == 0) {
$managers = $wallOwner->getManagers();
$owner = $wallOwner->getOwner();
(new NewSuggestedPostsNotification($owner, $wallOwner))->emit();
foreach($managers as $manager) {
(new NewSuggestedPostsNotification($manager->getUser(), $wallOwner))->emit();
}
}
return (object)["post_id" => "on_view"];
}
return (object)["post_id" => $post->getVirtualId()]; return (object)["post_id" => $post->getVirtualId()];
} }
function repost(string $object, string $message = "", int $group_id = 0) { function repost(string $object, string $message = "", string $attachments = "", int $group_id = 0, int $as_group = 0, int $signed = 0) {
$this->requireUser(); $this->requireUser();
$this->willExecuteWriteAction(); $this->willExecuteWriteAction();
$postArray; $postArray;
if(preg_match('/wall((?:-?)[0-9]+)_([0-9]+)/', $object, $postArray) == 0) if(preg_match('/(wall|video|photo)((?:-?)[0-9]+)_([0-9]+)/', $object, $postArray) == 0)
$this->fail(100, "One of the parameters specified was missing or invalid: object is incorrect"); $this->fail(100, "One of the parameters specified was missing or invalid: object is incorrect");
$post = (new PostsRepo)->getPostById((int) $postArray[1], (int) $postArray[2]); $parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'audio']);
if(!$post || $post->isDeleted()) $this->fail(100, "One of the parameters specified was missing or invalid"); $final_attachments = [];
foreach($parsed_attachments as $attachment) {
if(!$post->canBeViewedBy($this->getUser())) if($attachment && !$attachment->isDeleted() && $attachment->canBeViewedBy($this->getUser()) &&
$this->fail(15, "Access denied"); !(method_exists($attachment, 'getVoters') && $attachment->getOwner()->getId() != $this->getUser()->getId())) {
$final_attachments[] = $attachment;
}
}
$repost_entity = NULL;
$repost_type = $postArray[1];
switch($repost_type) {
default:
case 'wall':
$repost_entity = (new PostsRepo)->getPostById((int) $postArray[2], (int) $postArray[3]);
break;
case 'photo':
$repost_entity = (new PhotosRepo)->getByOwnerAndVID((int) $postArray[2], (int) $postArray[3]);
break;
case 'video':
$repost_entity = (new VideosRepo)->getByOwnerAndVID((int) $postArray[2], (int) $postArray[3]);
break;
}
if(!$repost_entity || $repost_entity->isDeleted() || !$repost_entity->canBeViewedBy($this->getUser())) $this->fail(100, "One of the parameters specified was missing or invalid");
$nPost = new Post; $nPost = new Post;
$nPost->setOwner($this->user->getId()); $nPost->setOwner($this->user->getId());
if($group_id > 0) { if($group_id > 0) {
$club = (new ClubsRepo)->get($group_id); $club = (new ClubsRepo)->get($group_id);
if(!$club) if(!$club)
$this->fail(42, "Invalid group"); $this->fail(42, "Invalid group");
if(!$club->canBeModifiedBy($this->user)) if(!$club->canBeModifiedBy($this->user))
$this->fail(16, "Access to group denied"); $this->fail(16, "Access to group denied");
$nPost->setWall($group_id * -1); $nPost->setWall($club->getRealId());
$flags = 0;
if($as_group === 1 || $signed === 1)
$flags |= 0b10000000;
if($signed === 1)
$flags |= 0b01000000;
$nPost->setFlags($flags);
} else { } else {
$nPost->setWall($this->user->getId()); $nPost->setWall($this->user->getId());
} }
@ -712,16 +662,27 @@ final class Wall extends VKAPIRequestHandler
$nPost->setContent($message); $nPost->setContent($message);
$nPost->setApi_Source_Name($this->getPlatform()); $nPost->setApi_Source_Name($this->getPlatform());
$nPost->save(); $nPost->save();
$nPost->attach($post);
$nPost->attach($repost_entity);
if($post->getOwner(false)->getId() !== $this->user->getId() && !($post->getOwner() instanceof Club))
(new RepostNotification($post->getOwner(false), $post, $this->user))->emit(); foreach($parsed_attachments as $attachment) {
$nPost->attach($attachment);
}
if($repost_type == 'wall' && $repost_entity->getOwner(false)->getId() !== $this->user->getId() && !($repost_entity->getOwner() instanceof Club))
(new RepostNotification($repost_entity->getOwner(false), $repost_entity, $this->user))->emit();
$repost_count = 1;
if($repost_type == 'wall') {
$repost_count = $repost_entity->getRepostCount();
}
return (object) [ return (object) [
"success" => 1, // 👍 "success" => 1, // 👍
"post_id" => $nPost->getVirtualId(), "post_id" => $nPost->getVirtualId(),
"reposts_count" => $post->getRepostCount(), "pretty_id" => $nPost->getPrettyId(),
"likes_count" => $post->getLikesCount() "reposts_count" => $repost_count,
"likes_count" => $repost_entity->getLikesCount()
]; ];
} }
@ -767,7 +728,7 @@ final class Wall extends VKAPIRequestHandler
"date" => $comment->getPublicationTime()->timestamp(), "date" => $comment->getPublicationTime()->timestamp(),
"text" => $comment->getText(false), "text" => $comment->getText(false),
"post_id" => $post->getVirtualId(), "post_id" => $post->getVirtualId(),
"owner_id" => $post->isPostedOnBehalfOfGroup() ? $post->getOwner()->getId() * -1 : $post->getOwner()->getId(), "owner_id" => method_exists($post, 'isPostedOnBehalfOfGroup') && $post->isPostedOnBehalfOfGroup() ? $post->getOwner()->getId() * -1 : $post->getOwner()->getId(),
"parents_stack" => [], "parents_stack" => [],
"attachments" => $attachments, "attachments" => $attachments,
"thread" => [ "thread" => [
@ -833,6 +794,13 @@ final class Wall extends VKAPIRequestHandler
foreach($comment->getChildren() as $attachment) { foreach($comment->getChildren() as $attachment) {
if($attachment instanceof \openvk\Web\Models\Entities\Photo) { if($attachment instanceof \openvk\Web\Models\Entities\Photo) {
$attachments[] = $this->getApiPhoto($attachment); $attachments[] = $this->getApiPhoto($attachment);
} elseif($attachment instanceof \openvk\Web\Models\Entities\Video) {
$attachments[] = $attachment->getApiStructure();
} elseif($attachment instanceof \openvk\Web\Models\Entities\Note) {
$attachments[] = [
'type' => 'note',
'note' => $attachment->toVkApiStruct()
];
} elseif($attachment instanceof \openvk\Web\Models\Entities\Audio) { } elseif($attachment instanceof \openvk\Web\Models\Entities\Audio) {
$attachments[] = [ $attachments[] = [
"type" => "audio", "type" => "audio",
@ -847,7 +815,7 @@ final class Wall extends VKAPIRequestHandler
"date" => $comment->getPublicationTime()->timestamp(), "date" => $comment->getPublicationTime()->timestamp(),
"text" => $comment->getText(false), "text" => $comment->getText(false),
"post_id" => $comment->getTarget()->getVirtualId(), "post_id" => $comment->getTarget()->getVirtualId(),
"owner_id" => $comment->getTarget()->isPostedOnBehalfOfGroup() ? $comment->getTarget()->getOwner()->getId() * -1 : $comment->getTarget()->getOwner()->getId(), "owner_id" => method_exists($comment->getTarget(), 'isPostedOnBehalfOfGroup') && $comment->getTarget()->isPostedOnBehalfOfGroup() ? $comment->getTarget()->getOwner()->getId() * -1 : $comment->getTarget()->getOwner()->getId(),
"parents_stack" => [], "parents_stack" => [],
"attachments" => $attachments, "attachments" => $attachments,
"likes" => [ "likes" => [
@ -899,7 +867,16 @@ final class Wall extends VKAPIRequestHandler
if($post->getTargetWall() < 0) if($post->getTargetWall() < 0)
$club = (new ClubsRepo)->get(abs($post->getTargetWall())); $club = (new ClubsRepo)->get(abs($post->getTargetWall()));
if(empty($message) && empty($attachments)) { $parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'audio']);
$final_attachments = [];
foreach($parsed_attachments as $attachment) {
if($attachment && !$attachment->isDeleted() && $attachment->canBeViewedBy($this->getUser()) &&
!(method_exists($attachment, 'getVoters') && $attachment->getOwner()->getId() != $this->getUser()->getId())) {
$final_attachments[] = $attachment;
}
}
if((empty($message) && (empty($attachments) || sizeof($final_attachments) < 1))) {
$this->fail(100, "Required parameter 'message' missing."); $this->fail(100, "Required parameter 'message' missing.");
} }
@ -920,55 +897,8 @@ final class Wall extends VKAPIRequestHandler
$this->fail(1, "ошибка про то что коммент большой слишком"); $this->fail(1, "ошибка про то что коммент большой слишком");
} }
if(!empty($attachments)) { foreach($final_attachments as $attachment) {
$attachmentsArr = explode(",", $attachments); $comment->attach($attachment);
if(sizeof($attachmentsArr) > 10)
$this->fail(50, "Error: too many attachments");
foreach($attachmentsArr as $attac) {
$attachmentType = NULL;
if(str_contains($attac, "photo"))
$attachmentType = "photo";
elseif(str_contains($attac, "video"))
$attachmentType = "video";
elseif(str_contains($attac, "audio"))
$attachmentType = "audio";
else
$this->fail(205, "Unknown attachment type");
$attachment = str_replace($attachmentType, "", $attac);
$attachmentOwner = (int)explode("_", $attachment)[0];
$attachmentId = (int)end(explode("_", $attachment));
$attacc = NULL;
if($attachmentType == "photo") {
$attacc = (new PhotosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
$this->fail(100, "Photo does not exists");
if(!$attacc->getOwner()->getPrivacyPermission('photos.read', $this->getUser()))
$this->fail(11, "Access to photo denied");
$comment->attach($attacc);
} elseif($attachmentType == "video") {
$attacc = (new VideosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
$this->fail(100, "Video does not exists");
if(!$attacc->getOwner()->getPrivacyPermission('videos.read', $this->getUser()))
$this->fail(11, "Access to video denied");
$comment->attach($attacc);
} elseif($attachmentType == "audio") {
$attacc = (new AudiosRepo)->getByOwnerAndVID($attachmentOwner, $attachmentId);
if(!$attacc || $attacc->isDeleted())
$this->fail(100, "Audio does not exist");
$comment->attach($attacc);
}
}
} }
if($post->getOwner()->getId() !== $this->user->getId()) if($post->getOwner()->getId() !== $this->user->getId())
@ -1019,11 +949,24 @@ final class Wall extends VKAPIRequestHandler
} }
} }
function edit(int $owner_id, int $post_id, string $message = "", string $attachments = "", string $copyright = NULL) { function edit(int $owner_id, int $post_id, string $message = "", string $attachments = "", string $copyright = NULL, int $explicit = -1, int $from_group = 0, int $signed = 0) {
$this->requireUser(); $this->requireUser();
$this->willExecuteWriteAction(); $this->willExecuteWriteAction();
$post = (new PostsRepo)->getPostById($owner_id, $post_id); $parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'audio', 'poll']);
$final_attachments = [];
foreach($parsed_attachments as $attachment) {
if($attachment && !$attachment->isDeleted() && $attachment->canBeViewedBy($this->getUser()) &&
!(method_exists($attachment, 'getVoters') && $attachment->getOwner()->getId() != $this->getUser()->getId())) {
$final_attachments[] = $attachment;
}
}
if(empty($message) && sizeof($final_attachments) < 1) {
$this->fail(-66, "Post will be empty, don't saving.");
}
$post = (new PostsRepo)->getPostById($owner_id, $post_id, true);
if(!$post || $post->isDeleted()) if(!$post || $post->isDeleted())
$this->fail(102, "Invalid post"); $this->fail(102, "Invalid post");
@ -1031,36 +974,43 @@ final class Wall extends VKAPIRequestHandler
if(!$post->canBeEditedBy($this->getUser())) if(!$post->canBeEditedBy($this->getUser()))
$this->fail(7, "Access to editing denied"); $this->fail(7, "Access to editing denied");
if(!empty($message)) if(!empty($message) || (empty($message) && sizeof($final_attachments) > 0))
$post->setContent($message); $post->setContent($message);
$post->setEdited(time()); $post->setEdited(time());
if(!is_null($copyright) && !empty($copyright)) { if(!is_null($copyright) && !empty($copyright)) {
try { if($copyright == 'remove') {
$post->setSource($copyright); $post->resetSource();
} catch(\Throwable) {} } else {
try {
$post->setSource($copyright);
} catch(\Throwable) {}
}
} }
if($explicit != -1) {
$post->setNsfw($explicit == 1);
}
$wallOwner = ($owner_id > 0 ? (new UsersRepo)->get($owner_id) : (new ClubsRepo)->get($owner_id * -1));
$flags = 0;
if($from_group == 1 && $wallOwner instanceof Club && $wallOwner->canBeModifiedBy($this->getUser()))
$flags |= 0b10000000;
/*if($signed == 1)
$flags |= 0b01000000;*/
$post->setFlags($flags);
$post->save(true); $post->save(true);
# todo добавить такое в веб версию if($attachments == 'remove' || sizeof($final_attachments) > 0) {
if(!empty($attachments)) { foreach($post->getChildren() as $att) {
$attachs = parseAttachments($attachments); if(!($att instanceof Post)) {
$newAttachmentsCount = sizeof($attachs); $post->detach($att);
}
}
$postsAttachments = iterator_to_array($post->getChildren()); foreach($final_attachments as $attachment) {
$post->attach($attachment);
if(sizeof($postsAttachments) >= 10)
$this->fail(15, "Post have too many attachments");
if(($newAttachmentsCount + sizeof($postsAttachments)) > 10)
$this->fail(158, "Post will have too many attachments");
foreach($attachs as $attach) {
if($attach && !$attach->isDeleted())
$post->attach($attach);
else
$this->fail(52, "One of the attachments is invalid");
} }
} }
@ -1072,8 +1022,16 @@ final class Wall extends VKAPIRequestHandler
$this->willExecuteWriteAction(); $this->willExecuteWriteAction();
$comment = (new CommentsRepo)->get($comment_id); $comment = (new CommentsRepo)->get($comment_id);
$parsed_attachments = parseAttachments($attachments, ['photo', 'video', 'note', 'audio']);
$final_attachments = [];
foreach($parsed_attachments as $attachment) {
if($attachment && !$attachment->isDeleted() && $attachment->canBeViewedBy($this->getUser()) &&
!(method_exists($attachment, 'getVoters') && $attachment->getOwner()->getId() != $this->getUser()->getId())) {
$final_attachments[] = $attachment;
}
}
if(empty($message) && empty($attachments)) if(empty($message) && sizeof($final_attachments) < 1)
$this->fail(100, "Required parameter 'message' missing."); $this->fail(100, "Required parameter 'message' missing.");
if(!$comment || $comment->isDeleted()) if(!$comment || $comment->isDeleted())
@ -1082,29 +1040,16 @@ final class Wall extends VKAPIRequestHandler
if(!$comment->canBeEditedBy($this->getUser())) if(!$comment->canBeEditedBy($this->getUser()))
$this->fail(15, "Access to editing comment denied"); $this->fail(15, "Access to editing comment denied");
if(!empty($message)) if(!empty($message) || (empty($message) && sizeof($final_attachments) > 0))
$comment->setContent($message); $comment->setContent($message);
$comment->setEdited(time()); $comment->setEdited(time());
$comment->save(true); $comment->save(true);
if(!empty($attachments)) {
$attachs = parseAttachments($attachments);
$newAttachmentsCount = sizeof($attachs);
$postsAttachments = iterator_to_array($comment->getChildren()); if(sizeof($final_attachments) > 0) {
$comment->unwire();
if(sizeof($postsAttachments) >= 10) foreach($final_attachments as $attachment) {
$this->fail(15, "Post have too many attachments"); $comment->attach($attachment);
if(($newAttachmentsCount + sizeof($postsAttachments)) > 10)
$this->fail(158, "Post will have too many attachments");
foreach($attachs as $attach) {
if($attach && !$attach->isDeleted())
$comment->attach($attach);
else
$this->fail(52, "One of the attachments is invalid");
} }
} }

View file

@ -87,6 +87,7 @@ class Album extends MediaCollection
$res = (object) []; $res = (object) [];
$res->id = $this->getPrettyId(); $res->id = $this->getPrettyId();
$res->vid = $this->getId();
$res->thumb_id = !is_null($this->getCoverPhoto()) ? $this->getCoverPhoto()->getPrettyId() : 0; $res->thumb_id = !is_null($this->getCoverPhoto()) ? $this->getCoverPhoto()->getPrettyId() : 0;
$res->owner_id = $this->getOwner()->getId(); $res->owner_id = $this->getOwner()->getId();
$res->title = $this->getName(); $res->title = $this->getName();

View file

@ -42,10 +42,11 @@ class Club extends RowModel
return iterator_to_array($avPhotos)[0] ?? NULL; return iterator_to_array($avPhotos)[0] ?? NULL;
} }
function getAvatarUrl(string $size = "miniscule"): string function getAvatarUrl(string $size = "miniscule", $avPhoto = NULL): string
{ {
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"]; $serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
$avPhoto = $this->getAvatarPhoto(); if(!$avPhoto)
$avPhoto = $this->getAvatarPhoto();
return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURLBySizeId($size); return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURLBySizeId($size);
} }
@ -443,30 +444,57 @@ class Club extends RowModel
$res->id = $this->getId(); $res->id = $this->getId();
$res->name = $this->getName(); $res->name = $this->getName();
$res->screen_name = $this->getShortCode(); $res->screen_name = $this->getShortCode() ?? "club".$this->getId();
$res->is_closed = 0; $res->is_closed = false;
$res->type = 'group';
$res->is_member = $user ? (int)$this->getSubscriptionStatus($user) : 0;
$res->deactivated = NULL; $res->deactivated = NULL;
$res->is_admin = $user && $this->canBeModifiedBy($user); $res->can_access_closed = true;
if($user && $this->canBeModifiedBy($user)) { if(!is_array($fields))
$res->admin_level = 3; $fields = explode(',', $fields);
$avatar_photo = $this->getAvatarPhoto();
foreach($fields as $field) {
switch($field) {
case 'verified':
$res->verified = (int)$this->isVerified();
break;
case 'site':
$res->site = $this->getWebsite();
break;
case 'description':
$res->description = $this->getDescription();
break;
case 'background':
$res->background = $this->getBackDropPictureURLs();
break;
case 'photo_50':
$res->photo_50 = $this->getAvatarUrl('miniscule', $avatar_photo);
break;
case 'photo_100':
$res->photo_100 = $this->getAvatarUrl('tiny', $avatar_photo);
break;
case 'photo_200':
$res->photo_200 = $this->getAvatarUrl('normal', $avatar_photo);
break;
case 'photo_max':
$res->photo_max = $this->getAvatarUrl('original', $avatar_photo);
break;
case 'members_count':
$res->members_count = $this->getFollowersCount();
break;
case 'real_id':
$res->real_id = $this->getRealId();
break;
}
} }
$res->is_member = $user && $this->getSubscriptionStatus($user) ? 1 : 0;
$res->type = "group";
$res->photo_50 = $this->getAvatarUrl("miniscule");
$res->photo_100 = $this->getAvatarUrl("tiny");
$res->photo_200 = $this->getAvatarUrl("normal");
$res->can_create_topic = $user && $this->canBeModifiedBy($user) ? 1 : ($this->isEveryoneCanCreateTopics() ? 1 : 0);
$res->can_post = $user && $this->canBeModifiedBy($user) ? 1 : ($this->canPost() ? 1 : 0);
return $res; return $res;
} }
use Traits\TBackDrops; use Traits\TBackDrops;
use Traits\TSubscribable; use Traits\TSubscribable;
use Traits\TAudioStatuses; use Traits\TAudioStatuses;
use Traits\TIgnorable;
} }

View file

@ -46,8 +46,11 @@ class Comment extends Post
return parent::getOwner($honourFlags, $real); return parent::getOwner($honourFlags, $real);
} }
function canBeDeletedBy(User $user): bool function canBeDeletedBy(User $user = NULL): bool
{ {
if(!$user)
return false;
return $this->getOwner()->getId() == $user->getId() || return $this->getOwner()->getId() == $user->getId() ||
$this->getTarget()->getOwner()->getId() == $user->getId() || $this->getTarget()->getOwner()->getId() == $user->getId() ||
$this->getTarget() instanceof Post && $this->getTarget()->getTargetWall() < 0 && (new Clubs)->get(abs($this->getTarget()->getTargetWall()))->canBeModifiedBy($user) || $this->getTarget() instanceof Post && $this->getTarget()->getTargetWall() < 0 && (new Clubs)->get(abs($this->getTarget()->getTargetWall()))->canBeModifiedBy($user) ||

View file

@ -308,7 +308,7 @@ class Photo extends Media
$res->date = $res->created = $this->getPublicationTime()->timestamp(); $res->date = $res->created = $this->getPublicationTime()->timestamp();
if($photo_sizes) { if($photo_sizes) {
$res->sizes = $this->getVkApiSizes(); $res->sizes = array_values($this->getVkApiSizes());
$res->src_small = $res->photo_75 = $this->getURLBySizeId("miniscule"); $res->src_small = $res->photo_75 = $this->getURLBySizeId("miniscule");
$res->src = $res->photo_130 = $this->getURLBySizeId("tiny"); $res->src = $res->photo_130 = $this->getURLBySizeId("tiny");
$res->src_big = $res->photo_604 = $this->getURLBySizeId("normal"); $res->src_big = $res->photo_604 = $this->getURLBySizeId("normal");

View file

@ -103,6 +103,11 @@ class Post extends Postable
$this->stateChanges("source", $source); $this->stateChanges("source", $source);
} }
function resetSource()
{
$this->stateChanges("source", NULL);
}
function getVkApiCopyright(): object function getVkApiCopyright(): object
{ {
return (object)[ return (object)[
@ -260,16 +265,22 @@ class Post extends Postable
$this->save(); $this->save();
} }
function canBePinnedBy(User $user): bool function canBePinnedBy(User $user = NULL): bool
{ {
if(!$user)
return false;
if($this->getTargetWall() < 0) if($this->getTargetWall() < 0)
return (new Clubs)->get(abs($this->getTargetWall()))->canBeModifiedBy($user); return (new Clubs)->get(abs($this->getTargetWall()))->canBeModifiedBy($user);
return $this->getTargetWall() === $user->getId(); return $this->getTargetWall() === $user->getId();
} }
function canBeDeletedBy(User $user): bool function canBeDeletedBy(User $user = NULL): bool
{ {
if(!$user)
return false;
if($this->getTargetWall() < 0 && !$this->getWallOwner()->canBeModifiedBy($user) && $this->getWallOwner()->getWallType() != 1 && $this->getSuggestionType() == 0) if($this->getTargetWall() < 0 && !$this->getWallOwner()->canBeModifiedBy($user) && $this->getWallOwner()->getWallType() != 1 && $this->getSuggestionType() == 0)
return false; return false;

View file

@ -134,8 +134,13 @@ class Report extends RowModel
function getContentName(): string function getContentName(): string
{ {
if (method_exists($this->getContentObject(), "getCanonicalName")) $content_object = $this->getContentObject();
return $this->getContentObject()->getCanonicalName(); if(!$content_object) {
return 'unknown';
}
if (method_exists($content_object, "getCanonicalName"))
return $content_object->getCanonicalName();
return $this->getContentType() . " #" . $this->getContentId(); return $this->getContentType() . " #" . $this->getContentId();
} }

View file

@ -1,6 +1,6 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
namespace openvk\Web\Models\Entities\Traits; namespace openvk\Web\Models\Entities\Traits;
use openvk\Web\Models\Entities\{Attachable, Photo}; use openvk\Web\Models\Entities\{Attachable, Photo, Video};
use openvk\Web\Util\Makima\Makima; use openvk\Web\Util\Makima\Makima;
use Chandler\Database\DatabaseConnection; use Chandler\Database\DatabaseConnection;
@ -36,10 +36,10 @@ trait TAttachmentHost
if($h < 0) if($h < 0)
$h = $w; $h = $w;
$children = $this->getChildren(); $children = iterator_to_array($this->getChildren());
$skipped = $photos = $result = []; $skipped = $photos = $result = [];
foreach($children as $child) { foreach($children as $child) {
if($child instanceof Photo) { if($child instanceof Photo || $child instanceof Video && $child->getDimensions()) {
$photos[] = $child; $photos[] = $child;
continue; continue;
} }

View file

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

View file

@ -112,7 +112,7 @@ class User extends RowModel
return "/id" . $this->getId(); return "/id" . $this->getId();
} }
function getAvatarUrl(string $size = "miniscule"): string function getAvatarUrl(string $size = "miniscule", $avPhoto = NULL): string
{ {
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"]; $serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
@ -121,7 +121,9 @@ class User extends RowModel
else if($this->isBanned()) else if($this->isBanned())
return "$serverUrl/assets/packages/static/openvk/img/banned.jpg"; return "$serverUrl/assets/packages/static/openvk/img/banned.jpg";
$avPhoto = $this->getAvatarPhoto(); if(!$avPhoto)
$avPhoto = $this->getAvatarPhoto();
if(is_null($avPhoto)) if(is_null($avPhoto))
return "$serverUrl/assets/packages/static/openvk/img/camera_200.png"; return "$serverUrl/assets/packages/static/openvk/img/camera_200.png";
else else
@ -1344,11 +1346,6 @@ class User extends RowModel
$res->first_name = $this->getFirstName(); $res->first_name = $this->getFirstName();
$res->last_name = $this->getLastName(); $res->last_name = $this->getLastName();
$res->deactivated = $this->isDeactivated(); $res->deactivated = $this->isDeactivated();
$res->photo_50 = $this->getAvatarURL();
$res->photo_100 = $this->getAvatarURL("tiny");
$res->photo_200 = $this->getAvatarURL("normal");
$res->photo_id = !is_null($this->getAvatarPhoto()) ? $this->getAvatarPhoto()->getPrettyId() : NULL;
$res->is_closed = $this->isClosed(); $res->is_closed = $this->isClosed();
if(!is_null($user)) if(!is_null($user))
@ -1357,17 +1354,60 @@ class User extends RowModel
if(!is_array($fields)) if(!is_array($fields))
$fields = explode(',', $fields); $fields = explode(',', $fields);
$avatar_photo = $this->getAvatarPhoto();
foreach($fields as $field) { foreach($fields as $field) {
switch($field) { switch($field) {
case 'is_dead': case 'is_dead':
$res->is_dead = $user->isDead(); $res->is_dead = $this->isDead();
break;
case 'verified':
$res->verified = (int)$this->isVerified();
break;
case 'sex':
$res->sex = $this->isFemale() ? 1 : ($this->isNeutral() ? 0 : 2);
break;
case 'photo_50':
$res->photo_50 = $this->getAvatarUrl('miniscule', $avatar_photo);
break;
case 'photo_100':
$res->photo_100 = $this->getAvatarUrl('tiny', $avatar_photo);
break;
case 'photo_200':
$res->photo_200 = $this->getAvatarUrl('normal', $avatar_photo);
break;
case 'photo_max':
$res->photo_max = $this->getAvatarUrl('original', $avatar_photo);
break;
case 'photo_id':
$res->photo_id = $avatar_photo ? $avatar_photo->getPrettyId() : NULL;
break;
case 'background':
$res->background = $this->getBackDropPictureURLs();
break;
case 'reg_date':
$res->reg_date = $this->getRegistrationTime()->timestamp();
break;
case 'nickname':
$res->nickname = $this->getPseudo();
break;
case 'rating':
$res->rating = $this->getRating();
break;
case 'status':
$res->status = $this->getStatus();
break;
case 'screen_name':
$res->screen_name = $this->getShortCode() ?? "id".$this->getId();
break;
case 'real_id':
$res->real_id = $this->getRealId();
break; break;
} }
} }
return $res; return $res;
} }
function getAudiosCollectionSize() function getAudiosCollectionSize()
{ {
return (new \openvk\Web\Models\Repositories\Audios)->getUserCollectionSize($this); return (new \openvk\Web\Models\Repositories\Audios)->getUserCollectionSize($this);
@ -1407,7 +1447,40 @@ class User extends RowModel
return $returnArr; return $returnArr;
} }
function getIgnoredSources(int $offset = 0, int $limit = 10, bool $onlyIds = false)
{
$sources = DatabaseConnection::i()->getContext()->table("ignored_sources")->where("owner", $this->getId())->limit($limit, $offset)->order('id DESC');
$output_array = [];
foreach($sources as $source) {
if($onlyIds) {
$output_array[] = (int)$source->source;
} else {
$ignored_source_model = NULL;
$ignored_source_id = (int)$source->source;
if($ignored_source_id > 0)
$ignored_source_model = (new Users)->get($ignored_source_id);
else
$ignored_source_model = (new Clubs)->get(abs($ignored_source_id));
if(!$ignored_source_model)
continue;
$output_array[] = $ignored_source_model;
}
}
return $output_array;
}
function getIgnoredSourcesCount()
{
return DatabaseConnection::i()->getContext()->table("ignored_sources")->where("owner", $this->getId())->count();
}
use Traits\TBackDrops; use Traits\TBackDrops;
use Traits\TSubscribable; use Traits\TSubscribable;
use Traits\TAudioStatuses; use Traits\TAudioStatuses;
use Traits\TIgnorable;
} }

View file

@ -34,9 +34,23 @@ class Video extends Media
if(sizeof($durations[1]) === 0) if(sizeof($durations[1]) === 0)
throw new \DomainException("$filename does not contain any meaningful video streams"); throw new \DomainException("$filename does not contain any meaningful video streams");
foreach($durations[1] as $duration) $length = 0;
if(floatval($duration) < 1.0) foreach($durations[1] as $duration) {
$duration = floatval($duration);
if($duration < 1.0)
throw new \DomainException("$filename does not contain any meaningful video streams"); throw new \DomainException("$filename does not contain any meaningful video streams");
else
$length = max($length, $duration);
}
$this->stateChanges("length", (int) round($length, 0, PHP_ROUND_HALF_EVEN));
preg_match('%width=([0-9\.]++)%', $streams, $width);
preg_match('%height=([0-9\.]++)%', $streams, $height);
if(!empty($width) && !empty($height)) {
$this->stateChanges("width", $width[1]);
$this->stateChanges("height", $height[1]);
}
try { try {
if(!is_dir($dirId = dirname($this->pathFromHash($hash)))) if(!is_dir($dirId = dirname($this->pathFromHash($hash))))
@ -118,6 +132,7 @@ class Video extends Media
function getApiStructure(?User $user = NULL): object function getApiStructure(?User $user = NULL): object
{ {
$fromYoutube = $this->getType() == Video::TYPE_EMBED; $fromYoutube = $this->getType() == Video::TYPE_EMBED;
$dimensions = $this->getDimensions();
$res = (object)[ $res = (object)[
"type" => "video", "type" => "video",
"video" => [ "video" => [
@ -130,7 +145,7 @@ class Video extends Media
"comments" => $this->getCommentsCount(), "comments" => $this->getCommentsCount(),
"date" => $this->getPublicationTime()->timestamp(), "date" => $this->getPublicationTime()->timestamp(),
"description" => $this->getDescription(), "description" => $this->getDescription(),
"duration" => 0, // я хуй знает как получить длину видео "duration" => $this->getLength(),
"image" => [ "image" => [
[ [
"url" => $this->getThumbnailURL(), "url" => $this->getThumbnailURL(),
@ -139,8 +154,8 @@ class Video extends Media
"with_padding" => 1 "with_padding" => 1
] ]
], ],
"width" => 640, "width" => $dimensions ? $dimensions[0] : 640,
"height" => 480, "height" => $dimensions ? $dimensions[1] : 480,
"id" => $this->getVirtualId(), "id" => $this->getVirtualId(),
"owner_id" => $this->getOwner()->getId(), "owner_id" => $this->getOwner()->getId(),
"user_id" => $this->getOwner()->getId(), "user_id" => $this->getOwner()->getId(),
@ -155,6 +170,7 @@ class Video extends Media
"repeat" => 0, "repeat" => 0,
"type" => "video", "type" => "video",
"views" => 0, "views" => 0,
"is_processed" => $this->isProcessed(),
"reposts" => [ "reposts" => [
"count" => 0, "count" => 0,
"user_reposted" => 0 "user_reposted" => 0
@ -224,6 +240,74 @@ class Video extends Media
return $video; return $video;
} }
function fillDimensions()
{
$hash = $this->getRecord()->hash;
$path = $this->pathFromHash($hash);
if(!file_exists($path)) {
$this->stateChanges("width", 0);
$this->stateChanges("height", 0);
$this->stateChanges("length", 0);
$this->save();
return false;
}
$streams = Shell::ffprobe("-i", $path, "-show_streams", "-select_streams v", "-loglevel error")->execute($error);
$durations = [];
preg_match_all('%duration=([0-9\.]++)%', $streams, $durations);
$length = 0;
foreach($durations[1] as $duration) {
$duration = floatval($duration);
if($duration < 1.0)
continue;
else
$length = max($length, $duration);
}
$this->stateChanges("length", (int) round($length, 0, PHP_ROUND_HALF_EVEN));
preg_match('%width=([0-9\.]++)%', $streams, $width);
preg_match('%height=([0-9\.]++)%', $streams, $height);
if(!empty($width) && !empty($height)) {
$this->stateChanges("width", $width[1]);
$this->stateChanges("height", $height[1]);
}
$this->save();
return true;
}
function getDimensions()
{
if($this->getType() == Video::TYPE_EMBED) return [320, 180];
$width = $this->getRecord()->width;
$height = $this->getRecord()->height;
if(!$width) return NULL;
return $width != 0 ? [$width, $height] : NULL;
}
function getLength()
{
return $this->getRecord()->length;
}
function getFormattedLength(): string
{
$len = $this->getLength();
if(!$len) return "00:00";
$mins = floor($len / 60);
$secs = $len - ($mins * 60);
return (
str_pad((string) $mins, 2, "0", STR_PAD_LEFT)
. ":" .
str_pad((string) $secs, 2, "0", STR_PAD_LEFT)
);
}
function canBeViewedBy(?User $user = NULL): bool function canBeViewedBy(?User $user = NULL): bool
{ {
@ -248,7 +332,7 @@ class Video extends Media
$res->owner_id = $this->getOwner()->getId(); $res->owner_id = $this->getOwner()->getId();
$res->title = $this->getName(); $res->title = $this->getName();
$res->description = $this->getDescription(); $res->description = $this->getDescription();
$res->duration = "22"; $res->duration = $this->getLength();
$res->link = "/video".$this->getOwner()->getId()."_".$this->getVirtualId(); $res->link = "/video".$this->getOwner()->getId()."_".$this->getVirtualId();
$res->image = $this->getThumbnailURL(); $res->image = $this->getThumbnailURL();
$res->date = $this->getPublicationTime()->timestamp(); $res->date = $this->getPublicationTime()->timestamp();

View file

@ -43,6 +43,18 @@ class Clubs
return $this->toClub($this->clubs->get($id)); return $this->toClub($this->clubs->get($id));
} }
function getByIds(array $ids = []): array
{
$clubs = $this->clubs->select('*')->where('id IN (?)', $ids);
$clubs_array = [];
foreach($clubs as $club) {
$clubs_array[] = $this->toClub($club);
}
return $clubs_array;
}
function find(string $query, array $params = [], array $order = ['type' => 'id', 'invert' => false], 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%";

View file

@ -33,7 +33,7 @@ class Photos
return new Photo($photo); return new Photo($photo);
} }
function getEveryUserPhoto(User $user, int $page = 1, ?int $perPage = NULL): \Traversable function getEveryUserPhoto(User $user, int $offset = 0, int $limit = 10): \Traversable
{ {
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE; $perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
$photos = $this->photos->where([ $photos = $this->photos->where([
@ -41,7 +41,7 @@ class Photos
"deleted" => 0 "deleted" => 0
])->order("id DESC"); ])->order("id DESC");
foreach($photos->page($page, $perPage) as $photo) { foreach($photos->limit($limit, $offset) as $photo) {
yield new Photo($photo); yield new Photo($photo);
} }
} }

View file

@ -28,6 +28,18 @@ class Users
{ {
return $this->toUser($this->users->get($id)); return $this->toUser($this->users->get($id));
} }
function getByIds(array $ids = []): array
{
$users = $this->users->select('*')->where('id IN (?)', $ids);
$users_array = [];
foreach($users as $user) {
$users_array[] = $this->toUser($user);
}
return $users_array;
}
function getByShortURL(string $url): ?User function getByShortURL(string $url): ?User
{ {

View file

@ -39,6 +39,13 @@ class Videos
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE; $perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
foreach($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0])->page($page, $perPage)->order("created DESC") as $video) foreach($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0])->page($page, $perPage)->order("created DESC") as $video)
yield new Video($video); yield new Video($video);
}
function getByUserLimit(User $user, int $offset = 0, int $limit = 10): \Traversable
{
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
foreach($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0])->limit($limit, $offset)->order("created DESC") as $video)
yield new Video($video);
} }
function getUserVideosCount(User $user): int function getUserVideosCount(User $user): int
@ -49,7 +56,7 @@ class Videos
function find(string $query = "", array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream function find(string $query = "", array $params = [], array $order = ['type' => 'id', 'invert' => false]): Util\EntityStream
{ {
$query = "%$query%"; $query = "%$query%";
$result = $this->videos->where("CONCAT_WS(' ', name, description) LIKE ?", $query)->where("deleted", 0); $result = $this->videos->where("CONCAT_WS(' ', name, description) LIKE ?", $query)->where("deleted", 0)->where("unlisted", 0);
$order_str = 'id'; $order_str = 'id';
switch($order['type']) { switch($order['type']) {

View file

@ -33,6 +33,7 @@ final class AudioPresenter extends OpenVKPresenter
function renderList(?int $owner = NULL, ?string $mode = "list"): void function renderList(?int $owner = NULL, ?string $mode = "list"): void
{ {
$this->assertUserLoggedIn();
$this->template->_template = "Audio/List.xml"; $this->template->_template = "Audio/List.xml";
$page = (int)($this->queryParam("p") ?? 1); $page = (int)($this->queryParam("p") ?? 1);
$audios = []; $audios = [];
@ -501,6 +502,7 @@ final class AudioPresenter extends OpenVKPresenter
function renderPlaylist(int $owner_id, int $virtual_id): void function renderPlaylist(int $owner_id, int $virtual_id): void
{ {
$this->assertUserLoggedIn();
$playlist = $this->audios->getPlaylistByOwnerAndVID($owner_id, $virtual_id); $playlist = $this->audios->getPlaylistByOwnerAndVID($owner_id, $virtual_id);
$page = (int)($this->queryParam("p") ?? 1); $page = (int)($this->queryParam("p") ?? 1);
if (!$playlist || $playlist->isDeleted()) if (!$playlist || $playlist->isDeleted())

View file

@ -27,7 +27,12 @@ final class CommentPresenter extends OpenVKPresenter
$this->flashFail("err", tr("error"), tr("forbidden")); $this->flashFail("err", tr("error"), tr("forbidden"));
if(!is_null($this->user)) $comment->toggleLike($this->user->identity); if(!is_null($this->user)) $comment->toggleLike($this->user->identity);
if($_SERVER["REQUEST_METHOD"] === "POST") {
$this->returnJson([
'success' => true,
]);
}
$this->redirect($_SERVER["HTTP_REFERER"]); $this->redirect($_SERVER["HTTP_REFERER"]);
} }
@ -70,64 +75,24 @@ final class CommentPresenter extends OpenVKPresenter
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_when_publishing_comment_description")); $this->flashFail("err", tr("error_when_publishing_comment"), tr("error_when_publishing_comment_description"));
} }
} }
$photos = [];
if(!empty($this->postParam("photos"))) {
$un = rtrim($this->postParam("photos"), ",");
$arr = explode(",", $un);
if(sizeof($arr) < 11) { $horizontal_attachments = [];
foreach($arr as $dat) { $vertical_attachments = [];
$ids = explode("_", $dat); if(!empty($this->postParam("horizontal_attachments"))) {
$photo = (new Photos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]); $horizontal_attachments_array = array_slice(explode(",", $this->postParam("horizontal_attachments")), 0, OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxAttachments"]);
if(sizeof($horizontal_attachments_array) > 0) {
if(!$photo || $photo->isDeleted()) $horizontal_attachments = parseAttachments($horizontal_attachments_array, ['photo', 'video']);
continue;
$photos[] = $photo;
}
} }
} }
$videos = []; if(!empty($this->postParam("vertical_attachments"))) {
$vertical_attachments_array = array_slice(explode(",", $this->postParam("vertical_attachments")), 0, OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxAttachments"]);
if(!empty($this->postParam("videos"))) { if(sizeof($vertical_attachments_array) > 0) {
$un = rtrim($this->postParam("videos"), ","); $vertical_attachments = parseAttachments($vertical_attachments_array, ['audio', 'note']);
$arr = explode(",", $un);
if(sizeof($arr) < 11) {
foreach($arr as $dat) {
$ids = explode("_", $dat);
$video = (new Videos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
if(!$video || $video->isDeleted())
continue;
$videos[] = $video;
}
}
}
$audios = [];
if(!empty($this->postParam("audios"))) {
$un = rtrim($this->postParam("audios"), ",");
$arr = explode(",", $un);
if(sizeof($arr) < 11) {
foreach($arr as $dat) {
$ids = explode("_", $dat);
$audio = (new Audios)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
if(!$audio || $audio->isDeleted())
continue;
$audios[] = $audio;
}
} }
} }
if(empty($this->postParam("text")) && sizeof($photos) < 1 && sizeof($videos) < 1 && sizeof($audios) < 1) if(empty($this->postParam("text")) && sizeof($horizontal_attachments) < 1 && sizeof($vertical_attachments) < 1)
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_empty")); $this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_empty"));
try { try {
@ -143,15 +108,21 @@ final class CommentPresenter extends OpenVKPresenter
$this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_too_big")); $this->flashFail("err", tr("error_when_publishing_comment"), tr("error_comment_too_big"));
} }
foreach($photos as $photo) foreach($horizontal_attachments as $horizontal_attachment) {
$comment->attach($photo); if(!$horizontal_attachment || $horizontal_attachment->isDeleted() || !$horizontal_attachment->canBeViewedBy($this->user->identity)) {
continue;
if(sizeof($videos) > 0) }
foreach($videos as $vid)
$comment->attach($vid);
foreach($audios as $audio) $comment->attach($horizontal_attachment);
$comment->attach($audio); }
foreach($vertical_attachments as $vertical_attachment) {
if(!$vertical_attachment || $vertical_attachment->isDeleted() || !$vertical_attachment->canBeViewedBy($this->user->identity)) {
continue;
}
$comment->attach($vertical_attachment);
}
if($entity->getOwner()->getId() !== $this->user->identity->getId()) if($entity->getOwner()->getId() !== $this->user->identity->getId())
if(($owner = $entity->getOwner()) instanceof User) if(($owner = $entity->getOwner()) instanceof User)

View file

@ -43,6 +43,7 @@ final class GroupPresenter extends OpenVKPresenter
} }
$this->template->club = $club; $this->template->club = $club;
$this->template->ignore_status = $club->isIgnoredBy($this->user->identity);
} }
} }

View file

@ -121,9 +121,9 @@ final class InternalAPIPresenter extends OpenVKPresenter
{ {
if($attachment instanceof \openvk\Web\Models\Entities\Photo) if($attachment instanceof \openvk\Web\Models\Entities\Photo)
{ {
$response[] = [ $response[$attachment->getPrettyId()] = [
"url" => $attachment->getURLBySizeId('normal'), "url" => $attachment->getURLBySizeId('larger'),
"id" => $attachment->getPrettyId() "id" => $attachment->getPrettyId(),
]; ];
} }
} }
@ -133,4 +133,35 @@ final class InternalAPIPresenter extends OpenVKPresenter
]); ]);
} }
} }
function renderGetPostTemplate(int $owner_id, int $post_id) {
if($_SERVER["REQUEST_METHOD"] !== "POST") {
header("HTTP/1.1 405 Method Not Allowed");
exit("ты‍ не по адресу");
}
$type = $this->queryParam("type", false);
if($type == "post") {
$post = (new Posts)->getPostById($owner_id, $post_id, true);
} else {
$post = (new Comments)->get($post_id);
}
if(!$post || !$post->canBeEditedBy($this->user->identity)) {
exit('');
}
header("Content-Type: text/plain");
if($type == 'post') {
$this->template->_template = 'components/post.xml';
$this->template->post = $post;
$this->template->commentSection = false;
} elseif($type == 'comment') {
$this->template->_template = 'components/comment.xml';
$this->template->comment = $post;
} else {
exit('');
}
}
} }

View file

@ -176,6 +176,7 @@ final class PhotosPresenter extends OpenVKPresenter
$this->template->cCount = $photo->getCommentsCount(); $this->template->cCount = $photo->getCommentsCount();
$this->template->cPage = (int) ($this->queryParam("p") ?? 1); $this->template->cPage = (int) ($this->queryParam("p") ?? 1);
$this->template->comments = iterator_to_array($photo->getComments($this->template->cPage)); $this->template->comments = iterator_to_array($photo->getComments($this->template->cPage));
$this->template->owner = $photo->getOwner();
} }
function renderAbsolutePhoto($id): void function renderAbsolutePhoto($id): void
@ -287,7 +288,8 @@ final class PhotosPresenter extends OpenVKPresenter
"id" => $photo->getId(), "id" => $photo->getId(),
"vid" => $photo->getVirtualId(), "vid" => $photo->getVirtualId(),
"owner" => $photo->getOwner()->getId(), "owner" => $photo->getOwner()->getId(),
"link" => $photo->getURL() "link" => $photo->getURL(),
"pretty_id" => $photo->getPrettyId(),
]; ];
} catch(ISE $ex) { } catch(ISE $ex) {
$name = $album->getName(); $name = $album->getName();
@ -354,4 +356,26 @@ final class PhotosPresenter extends OpenVKPresenter
$this->flash("succ", tr("photo_is_deleted"), tr("photo_is_deleted_desc")); $this->flash("succ", tr("photo_is_deleted"), tr("photo_is_deleted_desc"));
$this->redirect($redirect); $this->redirect($redirect);
} }
function renderLike(int $wall, int $post_id): void
{
$this->assertUserLoggedIn();
$this->willExecuteWriteAction();
$this->assertNoCSRF();
$photo = $this->photos->getByOwnerAndVID($wall, $post_id);
if(!$photo || $photo->isDeleted() || !$photo->canBeViewedBy($this->user->identity)) $this->notFound();
if(!is_null($this->user)) {
$photo->toggleLike($this->user->identity);
}
if($_SERVER["REQUEST_METHOD"] === "POST") {
$this->returnJson([
'success' => true,
]);
}
$this->redirect("$_SERVER[HTTP_REFERER]");
}
} }

View file

@ -54,6 +54,10 @@ final class UserPresenter extends OpenVKPresenter
$this->template->audioStatus = $user->getCurrentAudioStatus(); $this->template->audioStatus = $user->getCurrentAudioStatus();
$this->template->user = $user; $this->template->user = $user;
if($id !== $this->user->id) {
$this->template->ignore_status = $user->isIgnoredBy($this->user->identity);
}
} }
} }

View file

@ -62,6 +62,7 @@ final class VideosPresenter extends OpenVKPresenter
$this->flashFail("err", tr("error"), tr("video_uploads_disabled")); $this->flashFail("err", tr("error"), tr("video_uploads_disabled"));
if($_SERVER["REQUEST_METHOD"] === "POST") { if($_SERVER["REQUEST_METHOD"] === "POST") {
$is_ajax = (int)($this->postParam('ajax') ?? '0') == 1;
if(!empty($this->postParam("name"))) { if(!empty($this->postParam("name"))) {
$video = new Video; $video = new Video;
$video->setOwner($this->user->id); $video->setOwner($this->user->id);
@ -75,18 +76,29 @@ final class VideosPresenter extends OpenVKPresenter
else if(!empty($this->postParam("link"))) else if(!empty($this->postParam("link")))
$video->setLink($this->postParam("link")); $video->setLink($this->postParam("link"));
else else
$this->flashFail("err", tr("no_video_error"), tr("no_video_description")); $this->flashFail("err", tr("no_video_error"), tr("no_video_description"), 10, $is_ajax);
} catch(\DomainException $ex) { } catch(\DomainException $ex) {
$this->flashFail("err", tr("error_video"), tr("file_corrupted")); $this->flashFail("err", tr("error_video"), tr("file_corrupted"), 10, $is_ajax);
} catch(ISE $ex) { } catch(ISE $ex) {
$this->flashFail("err", tr("error_video"), tr("link_incorrect")); $this->flashFail("err", tr("error_video"), tr("link_incorrect"), 10, $is_ajax);
} }
if((int)($this->postParam("unlisted") ?? '0') == 1) {
$video->setUnlisted(true);
}
$video->save(); $video->save();
if($is_ajax) {
$object = $video->getApiStructure();
$this->returnJson([
'payload' => $object->video,
]);
}
$this->redirect("/video" . $video->getPrettyId()); $this->redirect("/video" . $video->getPrettyId());
} else { } else {
$this->flashFail("err", tr("error_video"), tr("no_name_error")); $this->flashFail("err", tr("error_video"), tr("no_name_error"), 10, $is_ajax);
} }
} }
} }
@ -151,7 +163,13 @@ final class VideosPresenter extends OpenVKPresenter
if(!is_null($this->user)) { if(!is_null($this->user)) {
$video->toggleLike($this->user->identity); $video->toggleLike($this->user->identity);
} }
$this->returnJson(["success" => true]); if($_SERVER["REQUEST_METHOD"] === "POST") {
$this->returnJson([
'success' => true,
]);
}
$this->redirect("$_SERVER[HTTP_REFERER]");
} }
} }

View file

@ -197,6 +197,16 @@ final class WallPresenter extends OpenVKPresenter
if($this->user->identity->getNsfwTolerance() === User::NSFW_INTOLERANT) if($this->user->identity->getNsfwTolerance() === User::NSFW_INTOLERANT)
$queryBase .= " AND `nsfw` = 0"; $queryBase .= " AND `nsfw` = 0";
if(((int)$this->queryParam('return_banned')) == 0) {
$ignored_sources_ids = $this->user->identity->getIgnoredSources(0, OPENVK_ROOT_CONF['openvk']['preferences']['newsfeed']['ignoredSourcesLimit'] ?? 50, true);
if(sizeof($ignored_sources_ids) > 0) {
$imploded_ids = implode("', '", $ignored_sources_ids);
$queryBase .= " AND `posts`.`wall` NOT IN ('$imploded_ids')";
}
}
$posts = DatabaseConnection::i()->getConnection()->query("SELECT `posts`.`id` " . $queryBase . " ORDER BY `created` DESC LIMIT " . $pPage . " OFFSET " . ($page - 1) * $pPage); $posts = DatabaseConnection::i()->getConnection()->query("SELECT `posts`.`id` " . $queryBase . " ORDER BY `created` DESC LIMIT " . $pPage . " OFFSET " . ($page - 1) * $pPage);
$count = DatabaseConnection::i()->getConnection()->query("SELECT COUNT(*) " . $queryBase)->fetch()->{"COUNT(*)"}; $count = DatabaseConnection::i()->getConnection()->query("SELECT COUNT(*) " . $queryBase)->fetch()->{"COUNT(*)"};
@ -272,25 +282,22 @@ final class WallPresenter extends OpenVKPresenter
if($this->postParam("force_sign") === "on") if($this->postParam("force_sign") === "on")
$flags |= 0b01000000; $flags |= 0b01000000;
$photos = []; $horizontal_attachments = [];
$vertical_attachments = [];
if(!empty($this->postParam("photos"))) { if(!empty($this->postParam("horizontal_attachments"))) {
$un = rtrim($this->postParam("photos"), ","); $horizontal_attachments_array = array_slice(explode(",", $this->postParam("horizontal_attachments")), 0, OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxAttachments"]);
$arr = explode(",", $un); if(sizeof($horizontal_attachments_array) > 0) {
$horizontal_attachments = parseAttachments($horizontal_attachments_array, ['photo', 'video']);
if(sizeof($arr) < 11) {
foreach($arr as $dat) {
$ids = explode("_", $dat);
$photo = (new Photos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
if(!$photo || $photo->isDeleted())
continue;
$photos[] = $photo;
}
} }
} }
if(!empty($this->postParam("vertical_attachments"))) {
$vertical_attachments_array = array_slice(explode(",", $this->postParam("vertical_attachments")), 0, OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxAttachments"]);
if(sizeof($vertical_attachments_array) > 0) {
$vertical_attachments = parseAttachments($vertical_attachments_array, ['audio', 'note']);
}
}
try { try {
$poll = NULL; $poll = NULL;
$xml = $this->postParam("poll"); $xml = $this->postParam("poll");
@ -302,61 +309,10 @@ final class WallPresenter extends OpenVKPresenter
$this->flashFail("err", tr("failed_to_publish_post"), "Poll format invalid"); $this->flashFail("err", tr("failed_to_publish_post"), "Poll format invalid");
} }
$note = NULL; if(empty($this->postParam("text")) && sizeof($horizontal_attachments) < 1 && sizeof($vertical_attachments) < 1 && !$poll)
if(!is_null($this->postParam("note")) && $this->postParam("note") != "none") {
$note = (new Notes)->get((int)$this->postParam("note"));
if(!$note || $note->isDeleted() || $note->getOwner()->getId() != $this->user->id) {
$this->flashFail("err", tr("error"), tr("error_attaching_note"));
}
if($note->getOwner()->getPrivacySetting("notes.read") < 1) {
$this->flashFail("err", " ");
}
}
$videos = [];
if(!empty($this->postParam("videos"))) {
$un = rtrim($this->postParam("videos"), ",");
$arr = explode(",", $un);
if(sizeof($arr) < 11) {
foreach($arr as $dat) {
$ids = explode("_", $dat);
$video = (new Videos)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
if(!$video || $video->isDeleted())
continue;
$videos[] = $video;
}
}
}
$audios = [];
if(!empty($this->postParam("audios"))) {
$un = rtrim($this->postParam("audios"), ",");
$arr = explode(",", $un);
if(sizeof($arr) < 11) {
foreach($arr as $dat) {
$ids = explode("_", $dat);
$audio = (new Audios)->getByOwnerAndVID((int)$ids[0], (int)$ids[1]);
if(!$audio || $audio->isDeleted())
continue;
$audios[] = $audio;
}
}
}
if(empty($this->postParam("text")) && sizeof($photos) < 1 && sizeof($videos) < 1 && sizeof($audios) < 1 && !$poll && !$note)
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big")); $this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big"));
$should_be_suggested = $wall < 0 && !$wallOwner->canBeModifiedBy($this->user->identity) && $wallOwner->getWallType() == 2;
try { try {
$post = new Post; $post = new Post;
$post->setOwner($this->user->id); $post->setOwner($this->user->id);
@ -373,7 +329,7 @@ final class WallPresenter extends OpenVKPresenter
} catch(\Throwable) {} } catch(\Throwable) {}
} }
if($wall < 0 && !$wallOwner->canBeModifiedBy($this->user->identity) && $wallOwner->getWallType() == 2) if($should_be_suggested)
$post->setSuggested(1); $post->setSuggested(1);
$post->save(); $post->save();
@ -381,21 +337,24 @@ final class WallPresenter extends OpenVKPresenter
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_too_big")); $this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_too_big"));
} }
foreach($photos as $photo) foreach($horizontal_attachments as $horizontal_attachment) {
$post->attach($photo); if(!$horizontal_attachment || $horizontal_attachment->isDeleted() || !$horizontal_attachment->canBeViewedBy($this->user->identity)) {
continue;
if(sizeof($videos) > 0) }
foreach($videos as $vid)
$post->attach($vid); $post->attach($horizontal_attachment);
}
foreach($vertical_attachments as $vertical_attachment) {
if(!$vertical_attachment || $vertical_attachment->isDeleted() || !$vertical_attachment->canBeViewedBy($this->user->identity)) {
continue;
}
$post->attach($vertical_attachment);
}
if(!is_null($poll)) if(!is_null($poll))
$post->attach($poll); $post->attach($poll);
if(!is_null($note))
$post->attach($note);
foreach($audios as $audio)
$post->attach($audio);
if($wall > 0 && $wall !== $this->user->identity->getId()) if($wall > 0 && $wall !== $this->user->identity->getId())
(new WallPostNotification($wallOwner, $post, $this->user->identity))->emit(); (new WallPostNotification($wallOwner, $post, $this->user->identity))->emit();
@ -404,9 +363,7 @@ final class WallPresenter extends OpenVKPresenter
if($wall > 0) if($wall > 0)
$excludeMentions[] = $wall; $excludeMentions[] = $wall;
if($wall < 0 && !$wallOwner->canBeModifiedBy($this->user->identity) && $wallOwner->getWallType() == 2) { if(!$should_be_suggested) {
# Чтобы не было упоминаний из предложки
} else {
$mentions = iterator_to_array($post->resolveMentions($excludeMentions)); $mentions = iterator_to_array($post->resolveMentions($excludeMentions));
foreach($mentions as $mentionee) foreach($mentions as $mentionee)
@ -414,18 +371,7 @@ final class WallPresenter extends OpenVKPresenter
(new MentionNotification($mentionee, $post, $post->getOwner(), strip_tags($post->getText())))->emit(); (new MentionNotification($mentionee, $post, $post->getOwner(), strip_tags($post->getText())))->emit();
} }
if($wall < 0 && !$wallOwner->canBeModifiedBy($this->user->identity) && $wallOwner->getWallType() == 2) { if($should_be_suggested) {
$suggsCount = $this->posts->getSuggestedPostsCount($wallOwner->getId());
if($suggsCount % 10 == 0) {
$managers = $wallOwner->getManagers();
$owner = $wallOwner->getOwner();
(new NewSuggestedPostsNotification($owner, $wallOwner))->emit();
foreach($managers as $manager)
(new NewSuggestedPostsNotification($manager->getUser(), $wallOwner))->emit();
}
$this->redirect("/club".$wallOwner->getId()."/suggested"); $this->redirect("/club".$wallOwner->getId()."/suggested");
} else { } else {
$this->redirect($wallOwner->getURL()); $this->redirect($wallOwner->getURL());
@ -476,6 +422,12 @@ final class WallPresenter extends OpenVKPresenter
if(!is_null($this->user)) { if(!is_null($this->user)) {
$post->toggleLike($this->user->identity); $post->toggleLike($this->user->identity);
} }
if($_SERVER["REQUEST_METHOD"] === "POST") {
$this->returnJson([
'success' => true,
]);
}
$this->redirect("$_SERVER[HTTP_REFERER]#postGarter=" . $post->getId()); $this->redirect("$_SERVER[HTTP_REFERER]#postGarter=" . $post->getId());
} }

View file

@ -17,6 +17,7 @@
{script "js/node_modules/umbrellajs/umbrella.min.js"} {script "js/node_modules/umbrellajs/umbrella.min.js"}
{script "js/l10n.js"} {script "js/l10n.js"}
{script "js/openvk.cls.js"} {script "js/openvk.cls.js"}
{script "js/utils.js"}
{script "js/node_modules/dashjs/dist/dash.all.min.js"} {script "js/node_modules/dashjs/dist/dash.all.min.js"}
<script src="/assets/packages/static/openvk/js/node_modules/cropperjs/dist/cropper.js" type="module"></script> <script src="/assets/packages/static/openvk/js/node_modules/cropperjs/dist/cropper.js" type="module"></script>
{script "js/al_music.js"} {script "js/al_music.js"}
@ -26,6 +27,7 @@
{css "js/node_modules/tippy.js/dist/border.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/dist/svg-arrow.css"}
{css "js/node_modules/tippy.js/themes/light.css"} {css "js/node_modules/tippy.js/themes/light.css"}
{css "js/node_modules/jquery-ui/themes/base/resizable.css"}
{script "js/node_modules/@popperjs/core/dist/umd/popper.min.js"} {script "js/node_modules/@popperjs/core/dist/umd/popper.min.js"}
{script "js/node_modules/tippy.js/dist/tippy-bundle.umd.min.js"} {script "js/node_modules/tippy.js/dist/tippy-bundle.umd.min.js"}
{script "js/node_modules/handlebars/dist/handlebars.min.js"} {script "js/node_modules/handlebars/dist/handlebars.min.js"}
@ -51,7 +53,10 @@
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['bellsAndWhistles']['testLabel']" id="test-label">FOR TESTING PURPOSES ONLY</div> <div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['bellsAndWhistles']['testLabel']" id="test-label">FOR TESTING PURPOSES ONLY</div>
<div class="notifications_global_wrap"></div> <div class="notifications_global_wrap"></div>
<div class="dimmer"></div> <div class="dimmer">
<div id='absolute_territory'></div>
</div>
<div class="upLeftErrors"></div>
<div class="articleView"> <div class="articleView">
<a id="articleCloseButton" class="button" href="javascript:void(u('body').removeClass('article'));">{_close}</a> <a id="articleCloseButton" class="button" href="javascript:void(u('body').removeClass('article'));">{_close}</a>
@ -245,9 +250,9 @@
<div class="floating_sidebar"> <div class="floating_sidebar">
<a id="minilink-friends" class="minilink" href="/friends{$thisUser->getId()}"> <a id="minilink-friends" class="minilink" href="/friends{$thisUser->getId()}">
<object type="internal/link" n:if="$thisUser->getFollowersCount() > 0"> <object type="internal/link" n:if="$thisUser->getRequestsCount() > 0">
<div class="counter"> <div class="counter">
+{$thisUser->getFollowersCount()} +{$thisUser->getRequestsCount()}
</div> </div>
</object> </object>
<img src="/assets/packages/static/openvk/img/friends.svg"> <img src="/assets/packages/static/openvk/img/friends.svg">
@ -368,7 +373,7 @@
</div> </div>
<div id="ajloader" class="loader"> <div id="ajloader" class="loader">
<img src="/assets/packages/static/openvk/img/loading_mini.gif" style="width: 40px;"> <img src="/assets/packages/static/openvk/img/loading_mini.gif">
</div> </div>
{include "components/cookies.xml"} {include "components/cookies.xml"}
@ -389,6 +394,7 @@
{ifset $thisUser} {ifset $thisUser}
{script "js/al_notifs.js"} {script "js/al_notifs.js"}
{script "js/al_feed.js"}
{/ifset} {/ifset}
<script>bsdnHydrate();</script> <script>bsdnHydrate();</script>
@ -430,6 +436,9 @@
window.openvk = { window.openvk = {
"audio_genres": {\openvk\Web\Models\Entities\Audio::genres}, "audio_genres": {\openvk\Web\Models\Entities\Audio::genres},
"at_search": {$atSearch ?? false}, "at_search": {$atSearch ?? false},
"max_attachments": {\OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxAttachments"] ?? 10},
"max_filesize_mb": 5,
"current_id": {$thisUser ? $thisUser->getId() : 0},
} }
</script> </script>

View file

@ -385,8 +385,8 @@
<tr> <tr>
<td class="e"> <td class="e">
Vladimir Barinov (veselcraft), Celestora, Konstantin Kichulkin (kosfurler), Vladimir Barinov (veselcraft), Celestora, Konstantin Kichulkin (kosfurler),
Nikita Volkov (sup_ban), Daniel Myslivets, Maxim Leshchenko (maksales / maksalees) Daniel Myslivets, Maxim Leshchenko (maksales / maksalees), n1rwana and
and n1rwana Jillian Österreich (Lumaeris)
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -400,7 +400,7 @@
<tr> <tr>
<td class="e"> <td class="e">
Vladimir Barinov (veselcraft) and Konstantin Kichulkin (kosfurler)<br/> Vladimir Barinov (veselcraft) and Konstantin Kichulkin (kosfurler)<br/>
OpenVK is a free open source software that "cosplays" (or imitates) older versions of a Russian social network called VKontakte. VKontakte belongs to Pavel Durov and VK Group. OpenVK is a free open source software that "cosplays" (or imitates) older versions of a Russian social network called VKontakte. VKontakte belongs to VK (formerly Mail.ru Group).
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -447,7 +447,7 @@
</tr> </tr>
<tr> <tr>
<td class="e">Initial hosting</td> <td class="e">Initial hosting</td>
<td class="v">Lumaeris and Celestora</td> <td class="v">Jillian Österreich (Lumaeris) and Celestora</td>
</tr> </tr>
<tr> <tr>
<td class="e">Initial bug-tracker hosting</td> <td class="e">Initial bug-tracker hosting</td>
@ -459,7 +459,7 @@
</tr> </tr>
<tr> <tr>
<td class="e">Illustrations</td> <td class="e">Illustrations</td>
<td class="v">Ash Defenders, Polina Katunina (RousPhaul)</td> <td class="v">Ash Defenders, Polina Katunina (ktp0li)</td>
</tr> </tr>
<tr> <tr>
<td class="e">Best barmaid</td> <td class="e">Best barmaid</td>
@ -479,9 +479,9 @@
</tr> </tr>
<tr class="e"> <tr class="e">
<td> <td>
kovaltim, Vladimir Lapskiy (0x7d5), Alexander Minkin (WerySkok), Polina Katunina (RousPhaul), veth, kovaltim, Vladimir Lapskiy (0x7d5), Alexander Minkin (WerySkok), Polina Katunina (ktp0li), veth,
Egor Shevchenko, Vadim Korovin (yuni), Ash Defenders, Egor Shevchenko, Vadim Korovin (yuni), Ash Defenders,
Pavel Silaev, Dmitriy Daemon, Lumaeris, Pavel Silaev, Dmitriy Daemon, Jillian Österreich (Lumaeris),
cmed404 and unknown tester, who disappeared shortly after trying to upload post with cat. cmed404 and unknown tester, who disappeared shortly after trying to upload post with cat.
</td> </td>
</tr> </tr>

View file

@ -5,17 +5,17 @@
<div id="audioEmbed-{$id}" data-realid="{$audio->getId()}" {if $hideButtons}data-prettyid="{$audio->getPrettyId()}" data-name="{$audio->getName()}"{/if} data-genre="{$audio->getGenre()}" class="audioEmbed {if !$isAvailable}processed{/if} {if $isWithdrawn}withdrawn{/if}" data-length="{$audio->getLength()}" data-keys="{json_encode($audio->getKeys())}" data-url="{$audio->getURL()}"> <div id="audioEmbed-{$id}" data-realid="{$audio->getId()}" {if $hideButtons}data-prettyid="{$audio->getPrettyId()}" data-name="{$audio->getName()}"{/if} data-genre="{$audio->getGenre()}" class="audioEmbed {if !$isAvailable}processed{/if} {if $isWithdrawn}withdrawn{/if}" data-length="{$audio->getLength()}" data-keys="{json_encode($audio->getKeys())}" data-url="{$audio->getURL()}">
<audio class="audio" /> <audio class="audio" />
<div id="miniplayer" class="audioEntry" style="min-height: 39px;"> <div id="miniplayer" class="audioEntry">
<div style="display: flex;"> <div class='audioEntryWrapper' style="display: flex;" draggable='true'>
<div class="playerButton"> <div class="playerButton">
<div class="playIcon"></div> <div class="playIcon"></div>
</div> </div>
<div class="status" style="margin-top: 12px;"> <div class="status" draggable='false'>
<div class="mediaInfo noOverflow"> <div class="mediaInfo noOverflow">
<div class="info"> <div class="info">
<strong class="performer"> <strong class="performer">
<a href="/search?query=&section=audios&order=listens&only_performers=on&q={$audio->getPerformer()}">{$audio->getPerformer()}</a> <a href="/search?section=audios&order=listens&only_performers=on&q={$audio->getPerformer()}">{$audio->getPerformer()}</a>
</strong> </strong>
<span class="title {if !empty($audio->getLyrics())}withLyrics{/if}">{$audio->getTitle()}</span> <span class="title {if !empty($audio->getLyrics())}withLyrics{/if}">{$audio->getTitle()}</span>
@ -27,8 +27,8 @@
</div> </div>
</div> </div>
<div class="volume" style="display: flex; flex-direction: column;width:14%;"> <div class="volume">
<span class="nobold {if !$hideButtons}hideOnHover{/if}" data-unformatted="{$audio->getLength()}" style="text-align: center;margin-top: 12px;">{$audio->getFormattedLength()}</span> <span class="nobold {if !$hideButtons}hideOnHover{/if}" data-unformatted="{$audio->getLength()}">{$audio->getFormattedLength()}</span>
<div class="buttons"> <div class="buttons">
{php $hasAudio = isset($thisUser) && $audio->isInLibraryOf($thisUser)} {php $hasAudio = isset($thisUser) && $audio->isInLibraryOf($thisUser)}
@ -44,7 +44,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="subTracks"> <div class="subTracks" draggable='false'>
<div class="lengthTrackWrapper"> <div class="lengthTrackWrapper">
<div class="track lengthTrack"> <div class="track lengthTrack">
<div class="selectableTrack" n:attr="style => $isWithdrawn ? 'display: none;' : ''"> <div class="selectableTrack" n:attr="style => $isWithdrawn ? 'display: none;' : ''">

View file

@ -168,10 +168,10 @@
{/if} {/if}
{var $canReport = $thisUser->getId() != $club->getOwner()->getId()} {var $canReport = $thisUser->getId() != $club->getOwner()->getId()}
{if $canReport} {if $canReport}
<a class="profile_link" style="display:block;" href="javascript:reportVideo()">{_report}</a> <a class="profile_link" style="display:block;" href="javascript:reportClub()">{_report}</a>
<script> <script>
function reportVideo() { function reportClub() {
uReportMsgTxt = tr("going_to_report_club"); uReportMsgTxt = tr("going_to_report_club");
uReportMsgTxt += "<br/>"+tr("report_question_text"); uReportMsgTxt += "<br/>"+tr("report_question_text");
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />" uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
@ -194,6 +194,9 @@
} }
</script> </script>
{/if} {/if}
<a n:if="!$club->isHideFromGlobalFeedEnabled()" class="profile_link" style="display:block;" id="__ignoreSomeone" data-val='{!$ignore_status ? 1 : 0}' data-id="{$club->getRealId()}">
{if !$ignore_status}{_ignore_club}{else}{_unignore_club}{/if}
</a>
</div> </div>
<div> <div>
<div class="content_title_expanded" onclick="hidePanel(this);"> <div class="content_title_expanded" onclick="hidePanel(this);">

View file

@ -4,76 +4,72 @@
{block header} {block header}
{ifset $album} {ifset $album}
<a href="{$album->getOwner()->getURL()}"> {var $album_owner = $album->getOwner()}
{$album->getOwner()->getCanonicalName()} <a href="{$album_owner->getURL()}">
{$album_owner->getCanonicalName()}
</a> </a>
{if ($album->getOwner() instanceof openvk\Web\Models\Entities\Club)} {if ($album_owner instanceof openvk\Web\Models\Entities\Club)}
» <a href="/albums{$album->getOwner()->getId() * -1}">{_albums}</a> » <a href="/albums{$album_owner->getId() * -1}">{_albums}</a>
{else} {else}
» <a href="/albums{$album->getOwner()->getId()}">{_albums}</a> » <a href="/albums{$album_owner->getId()}">{_albums}</a>
{/if} {/if}
» <a href="/album{$album->getPrettyId()}">{$album->getName()}</a> » <a href="/album{$album->getPrettyId()}">{$album->getName()}</a>
{else} {else}
<a href="{$photo->getOwner()->getURL()}">{$photo->getOwner()->getCanonicalName()}</a> <a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a>
{/ifset} {/ifset}
» {_photo} » {_photo}
{/block} {/block}
{block content} {block content}
<center style="margin-bottom: 8pt;"> <div class='media-page-wrapper photo-page-wrapper'>
<img src="{$photo->getURLBySizeId('large')}" style="max-width: 80%; max-height: 60vh;" /> <div class='photo-page-wrapper-photo'>
</center> <img src="{$photo->getURLBySizeId('large')}" />
<hr/>
<div style="width: 100%; min-height: 100px;" class="ovk-photo-details">
<div style="float: left; min-height: 100px; width: 68%;margin-left: 3px;">
{include "../components/comments.xml", comments => $comments, count => $cCount, page => $cPage, model => "photos", parent => $photo, custom_id => 999}
</div> </div>
<div style="float:right;min-height: 100px;width: 30%;margin-left: 1px;">
<div> <div class='ovk-photo-details'>
<h4>{_information}</h4> <div class='media-page-wrapper-description'>
<span style="color: grey;">{_info_description}:</span> <p n:if='!empty($photo->getDescription())'>{$photo->getDescription()}</p>
{$photo->getDescription() ?? "(" . tr("none") . ")"}<br/> <div class='upload_time'>
<span style="color: grey;">{_info_uploaded_by}:</span> {_info_upload_date}: {$photo->getPublicationTime()}
<a href="{$photo->getOwner()->getURL()}">{$photo->getOwner()->getFullName()}</a><br/> {if isset($thisUser)}
<span style="color: grey;">{_info_upload_date}:</span> |
{$photo->getPublicationTime()} {var $liked = $photo->hasLikeFrom($thisUser)}
{var $likesCount = $photo->getLikesCount()}
<div class='like_wrap tidy'>
<a href="/photo{$photo->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$likesCount}">
<div class="heart" id="{if $liked}liked{/if}"></div>
<span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span>
</a>
</div>
{/if}
</div>
</div> </div>
<br/>
<h4>{_actions}</h4>
{if isset($thisUser) && $thisUser->getId() != $photo->getOwner()->getId()}
{var canReport = true}
{/if}
<div n:if="isset($thisUser) && $thisUser->getId() === $photo->getOwner()->getId()">
<a href="/photo{$photo->getPrettyId()}/edit" class="profile_link" style="display:block;width:96%;">{_edit}</a>
<a id="_photoDelete" href="/photo{$photo->getPrettyId()}/delete" class="profile_link" style="display:block;width:96%;">{_delete}</a>
</div>
<a href="{$photo->getURL()}" class="profile_link" target="_blank" style="display:block;width:96%;">{_"open_original"}</a>
<a n:if="$canReport ?? false" class="profile_link" style="display:block;width:96%;" href="javascript:reportPhoto()">{_report}</a>
<script n:if="$canReport ?? false">
function reportPhoto() {
uReportMsgTxt = tr("going_to_report_photo");
uReportMsgTxt += "<br/>"+tr("report_question_text");
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [ <hr/>
(function() {
res = document.querySelector("#uReportMsgInput").value; <div class="media-page-wrapper-details">
xhr = new XMLHttpRequest(); <div class='media-page-wrapper-comments'>
xhr.open("GET", "/report/" + {$photo->getId()} + "?reason=" + res + "&type=photo", true); {include "../components/comments.xml", comments => $comments, count => $cCount, page => $cPage, model => "photos", parent => $photo, custom_id => 999}
xhr.onload = (function() { </div>
if(xhr.responseText.indexOf("reason") === -1) <div class='media-page-wrapper-actions'>
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]); <a href="{$owner->getURL()}" class='media-page-author-block'>
else <img class='cCompactAvatars' src="{$owner->getAvatarURL('miniscule')}">
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
}); <div class='media-page-author-block-name'>
xhr.send(null); <b>{$owner->getCanonicalName()}</b>
}), </div>
Function.noop </a>
]); <div n:if="isset($thisUser) && $thisUser->getId() === $photo->getOwner()->getId()">
} <a href="/photo{$photo->getPrettyId()}/edit" class="profile_link" style="display:block;width:96%;">{_edit}</a>
</script> <a id="_photoDelete" href="/photo{$photo->getPrettyId()}/delete" class="profile_link" style="display:block;width:96%;">{_delete}</a>
</div>
<a href="{$photo->getURL()}" class="profile_link" target="_blank" style="display:block;width:96%;">{_"open_original"}</a>
<a n:if="isset($thisUser) && $thisUser->getId() != $photo->getOwner()->getId()" class="profile_link" style="display:block;width:96%;" href="javascript:reportPhoto({$photo->getId()})">{_report}</a>
<a n:if="isset($thisUser)" onclick="javascript:repost({$photo->getPrettyId()}, 'photo')" class="profile_link" style="display:block;width:96%;">
{_share}
</a>
</div>
</div>
</div> </div>
</div> </div>
{/block} {/block}

View file

@ -166,6 +166,9 @@
{/if} {/if}
<a class="profile_link" style="display:block;width:96%;" href="javascript:reportUser()">{_report}</a> <a class="profile_link" style="display:block;width:96%;" href="javascript:reportUser()">{_report}</a>
<a n:if="!$user->isHideFromGlobalFeedEnabled()" class="profile_link" style="display:block;width:96%;" id="__ignoreSomeone" data-val='{!$ignore_status ? 1 : 0}' data-id="{$user->getId()}">
{if !$ignore_status}{_ignore_user}{else}{_unignore_user}{/if}
</a>
<script> <script>
function reportUser() { function reportUser() {
uReportMsgTxt = tr("going_to_report_user"); uReportMsgTxt = tr("going_to_report_user");
@ -322,13 +325,13 @@
</div> </div>
<div style="padding: 5px;"> <div style="padding: 5px;">
<div class="ovk-video" n:foreach="$videos as $video"> <div class="ovk-video" n:foreach="$videos as $video">
<a href="/video{$video->getPrettyId()}" class="preview" align="center" id="videoOpen" data-id="{$video->getId()}"> <a href="/video{$video->getPrettyId()}" class="preview" align="center" id="videoOpen" data-id="{$video->getPrettyId()}">
<img <img
src="{$video->getThumbnailURL()}" src="{$video->getThumbnailURL()}"
style="max-width: 170px; max-height: 127px; margin: auto;" /> style="max-width: 170px; max-height: 127px; margin: auto;" />
</a> </a>
<div> <div>
<b><a href="/video{$video->getPrettyId()}" id="videoOpen" data-id="{$video->getId()}">{ovk_proc_strtr($video->getName(), 30)}</a></b><br> <b><a href="/video{$video->getPrettyId()}" id="videoOpen" data-id="{$video->getPrettyId()}">{ovk_proc_strtr($video->getName(), 30)}</a></b><br>
<span style="font-size: 10px;">{$video->getPublicationTime()} | {_comments} ({$video->getCommentsCount()})</span> <span style="font-size: 10px;">{$video->getPublicationTime()} | {_comments} ({$video->getCommentsCount()})</span>
</div> </div>
</div> </div>

View file

@ -11,91 +11,88 @@
{/block} {/block}
{block content} {block content}
<center style="margin-bottom: 8pt;"> <div class='media-page-wrapper video-page-wrapper'>
{if $video->getType() === 0} <div class='video-page-wrapper-video'>
<div class="bsdn" data-name="{$video->getName()}" data-author="{$user->getCanonicalName()}"> {if $video->getType() === 0}
<video src="{$video->getURL()}"></video> <div class="bsdn" data-name="{$video->getName()}" data-author="{$user->getCanonicalName()}">
</div> <video src="{$video->getURL()}"></video>
{else}
{var $driver = $video->getVideoDriver()}
{if !$driver}
{_unknown_video}
{else}
{$driver->getEmbed()|noescape}
{/if}
{/if}
</center>
<hr/>
<div style="width: 100%; min-height: 100px;">
<div style="float: left; min-height: 100px; width: 68%; margin-right: 2%;" id="comments">
{include "../components/comments.xml",
comments => $comments,
count => $cCount,
page => $cPage,
model => "videos",
parent => $video}
</div>
<div style="float: left; min-height: 100px; width: 30%; overflow: hidden; overflow-wrap: break-word;">
<div>
<h4>{_information}</h4>
<span style="color: grey;">{_info_name}:</span>
{$video->getName()}<br/>
<span style="color: grey;">{_info_description}:</span>
{$video->getDescription() ?? "(" . tr("none") . ")"}<br/>
<span style="color: grey;">{_info_uploaded_by}:</span>
<a href="{$user->getURL()}">{$user->getFullName()}</a><br/>
<span style="color: grey;">{_info_upload_date}:</span>
{$video->getPublicationTime()}
</div>
<br/>
<div>
<div n:if="isset($thisUser) && $thisUser->getId() === $user->getId()">
<h4>{_actions}</h4>
<a href="/video{$video->getPrettyId()}/edit" class="profile_link" style="display:block;width:96%;">
{_edit}
</a>
<a href="/video{$video->getPrettyId()}/remove" class="profile_link" style="display:block;width:96%;">
{_delete}
</a>
</div> </div>
<a href="/video{$video->getPrettyId()}" class="profile_link" id="videoOpen" data-id="{$video->getId()}" style="display:block;width:96%;"> {else}
{_watch_in_window} {var $driver = $video->getVideoDriver()}
</a> {if !$driver}
</div> {_unknown_video}
{else}
{if isset($thisUser)} {$driver->getEmbed()|noescape}
{if $thisUser->getId() != $video->getOwner()->getId()}
{var canReport = true}
{/if} {/if}
{/if} {/if}
<a n:if="$canReport ?? false" class="profile_link" style="display:block;width:96%;" href="javascript:reportVideo()">{_report}</a>
<script n:if="$canReport ?? false">
function reportVideo() {
uReportMsgTxt = tr("going_to_report_video");
uReportMsgTxt += "<br/>"+tr("report_question_text");
uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"
MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
(function() {
res = document.querySelector("#uReportMsgInput").value;
xhr = new XMLHttpRequest();
xhr.open("GET", "/report/" + {$video->getId()} + "?reason=" + res + "&type=video", true);
xhr.onload = (function() {
if(xhr.responseText.indexOf("reason") === -1)
MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
else
MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
});
xhr.send(null);
}),
Function.noop
]);
}
</script>
</div> </div>
<div class='ovk-vid-details'>
<div class='media-page-wrapper-description'>
<p><b>{$video->getName()}</b></p>
<p n:if='!empty($video->getDescription())'>{$video->getDescription()}</p>
<div class='upload_time'>
{_info_upload_date}: {$video->getPublicationTime()}
{if isset($thisUser)}
|
{var $liked = $video->hasLikeFrom($thisUser)}
{var $likesCount = $video->getLikesCount()}
<div class='like_wrap tidy'>
<a href="/video{$video->getPrettyId()}/like?hash={rawurlencode($csrfToken)}" class="post-like-button" data-liked="{(int) $liked}" data-likes="{$likesCount}">
<div class="heart" id="{if $liked}liked{/if}"></div>
<span class="likeCnt">{if $likesCount > 0}{$likesCount}{/if}</span>
</a>
</div>
{/if}
</div>
</div>
<hr/>
<div class="media-page-wrapper-details">
<div class='media-page-wrapper-comments' id="comments">
{include "../components/comments.xml",
comments => $comments,
count => $cCount,
page => $cPage,
model => "videos",
parent => $video}
</div>
<div class='media-page-wrapper-actions'>
<a href="{$user->getURL()}" class='media-page-author-block'>
<img class='cCompactAvatars' src="{$user->getAvatarURL('miniscule')}">
<div class='media-page-author-block-name'>
<b>{$user->getCanonicalName()}</b>
</div>
</a>
<div>
<div n:if="isset($thisUser) && $thisUser->getId() === $user->getId()">
<a href="/video{$video->getPrettyId()}/edit" class="profile_link" style="display:block;width:96%;">
{_edit}
</a>
<a id='_videoDelete' href="/video{$video->getPrettyId()}/remove" class="profile_link" style="display:block;width:96%;">
{_delete}
</a>
</div>
<a n:if="isset($thisUser)" onclick="javascript:repost({$video->getPrettyId()}, 'video')" class="profile_link" style="display:block;width:96%;">
{_share}
</a>
</div>
{if isset($thisUser)}
{if $thisUser->getId() != $video->getOwner()->getId()}
{var canReport = true}
{/if}
{/if}
<a n:if="$canReport ?? false" class="profile_link" style="display:block;width:96%;" href="javascript:reportVideo({$video->getId()})">{_report}</a>
<a n:if="$video->getType() == 0" href="{$video->getURL()}" download="" class="profile_link" style="display:block;width:96%;">{_download_video}</a>
</div>
</div>
</div>
</div> </div>
{/block} {/block}

View file

@ -15,6 +15,8 @@
<div n:attr="id => (isset($globalFeed) ? 'activetabs' : 'ki')" class="tab"> <div n:attr="id => (isset($globalFeed) ? 'activetabs' : 'ki')" class="tab">
<a n:attr="id => (isset($globalFeed) ? 'act_tab_a' : 'ki')" href="/feed/all">{_all_news}</a> <a n:attr="id => (isset($globalFeed) ? 'act_tab_a' : 'ki')" href="/feed/all">{_all_news}</a>
</div> </div>
<a href='#' id="__feed_settings_link" data-pagescount='{ceil($paginatorConf->count / $paginatorConf->perPage)}'>{_feed_settings}</a>
</div> </div>
<div n:class="postFeedWrapper, $thisUser->hasMicroblogEnabled() ? postFeedWrapperMicroblog"> <div n:class="postFeedWrapper, $thisUser->hasMicroblogEnabled() ? postFeedWrapperMicroblog">

View file

@ -1,7 +1,7 @@
{if $attachment instanceof \openvk\Web\Models\Entities\Photo} {if $attachment instanceof \openvk\Web\Models\Entities\Photo}
{if !$attachment->isDeleted()} {if !$attachment->isDeleted()}
{var $link = "/photo" . ($attachment->isAnonymous() ? ("s/" . base_convert((string) $attachment->getId(), 10, 32)) : $attachment->getPrettyId())} {var $link = "/photo" . ($attachment->isAnonymous() ? ("s/" . base_convert((string) $attachment->getId(), 10, 32)) : $attachment->getPrettyId())}
<a href="{$link}" onclick="OpenMiniature(event, {$attachment->getURLBySizeId('normal')}, {$parent->getPrettyId()}, {$attachment->getPrettyId()}, {$parentType})"> <a href="{$link}" onclick="OpenMiniature(event, {$attachment->getURLBySizeId('larger')}, {$parent->getPrettyId()}, {$attachment->getPrettyId()}, {$parentType})">
<img class="media media_makima" src="{$attachment->getURLBySizeId('normal')}" alt="{$attachment->getDescription()}" loading=lazy /> <img class="media media_makima" src="{$attachment->getURLBySizeId('normal')}" alt="{$attachment->getDescription()}" loading=lazy />
</a> </a>
{else} {else}
@ -10,39 +10,50 @@
</a> </a>
{/if} {/if}
{elseif $attachment instanceof \openvk\Web\Models\Entities\Video} {elseif $attachment instanceof \openvk\Web\Models\Entities\Video}
{if $attachment->getType() === 0} {if $tilesCount <= 1}
<div class="bsdn media" data-name="{$attachment->getName()}" data-author="{$attachment->getOwner()->getCanonicalName()}"> {if $attachment->getType() === 0}
<video class="media" src="{$attachment->getURL()}"></video> <div class="bsdn media" data-name="{$attachment->getName()}" data-author="{$attachment->getOwner()->getCanonicalName()}">
<video class="media" src="{$attachment->getURL()}"></video>
</div>
{else}
{var $driver = $attachment->getVideoDriver()}
{if !$driver}
<span style="color:red;">{_version_incompatibility}</span>
{else}
{$driver->getEmbed("100%")|noescape}
{/if}
{/if}
<div class="video-wowzer">
<div class="small-video-ico"></div>
<a href="/video{$attachment->getPrettyId()}" id="videoOpen" data-id="{$attachment->getPrettyId()}">{$attachment->getName()}</a>
<span class="video-wowzer-length" n:if="$attachment->getLength() != NULL">({$attachment->getFormattedLength()})</span>
</div> </div>
{else} {else}
{var $driver = $attachment->getVideoDriver()} <a class='compact_video' id='videoOpen' data-id='{$attachment->getPrettyId()}' href="/video{$attachment->getPrettyId()}">
{if !$driver} <div class='play-button'>
<span style="color:red;">{_version_incompatibility}</span> <div class='play-button-ico'></div>
{else} </div>
{$driver->getEmbed("100%")|noescape} <div class='video-length' n:if="$attachment->getLength() != NULL">
{/if} {$attachment->getFormattedLength()}
{/if} </div>
<div class="video-wowzer"> <img class="media media_makima" src="{$attachment->getThumbnailURL()}" loading=lazy />
<img src="/assets/packages/static/openvk/img/videoico.png" /> </a>
<a href="/video{$attachment->getPrettyId()}" id="videoOpen" data-id="{$attachment->getId()}">{$attachment->getName()}</a> {/if}
</div>
{elseif $attachment instanceof \openvk\Web\Models\Entities\Poll} {elseif $attachment instanceof \openvk\Web\Models\Entities\Poll}
{presenter "openvk!Poll->view", $attachment->getId()} {presenter "openvk!Poll->view", $attachment->getId()}
{elseif $attachment instanceof \openvk\Web\Models\Entities\Note} {elseif $attachment instanceof \openvk\Web\Models\Entities\Note}
{if !$attachment->isDeleted()} <div data-att_type='note' data-att_id="{$attachment->getPrettyId()}">
<div class="attachment_note"> <div class="attachment_note">
<img class="attachment_note_icon" src="/assets/packages/static/openvk/img/note.svg"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 10"><polygon points="0 0 0 10 8 10 8 4 4 4 4 0 0 0"/><polygon points="5 0 5 3 8 3 5 0"/></svg>
<span class="attachment_note_text">{_note}</span>
<span class="attachment_note_name"><a href="javascript:void(showArticle({$attachment->getId()}));">{ovk_proc_strtr($attachment->getName(), 66)}</a></span> <div class='attachment_note_content'>
<span class="attachment_note_text">{_note}</span>
<span class="attachment_note_name"><a href="javascript:void(showArticle({$attachment->getId()}));">{ovk_proc_strtr($attachment->getName(), 66)}</a></span>
</div>
</div> </div>
{else} </div>
<div class="attachment_note">
<img class="attachment_note_icon" src="/assets/packages/static/openvk/img/note.svg">
<span class="attachment_note_text">{_note}</span>
<span class="attachment_note_name">{_deleted}</span>
</div>
{/if}
{elseif $attachment instanceof \openvk\Web\Models\Entities\Post} {elseif $attachment instanceof \openvk\Web\Models\Entities\Post}
{php $GLOBALS["_nesAttGloCou"] = (isset($GLOBALS["_nesAttGloCou"]) ? $GLOBALS["_nesAttGloCou"] : 0) + 1} {php $GLOBALS["_nesAttGloCou"] = (isset($GLOBALS["_nesAttGloCou"]) ? $GLOBALS["_nesAttGloCou"] : 0) + 1}
{if $GLOBALS["_nesAttGloCou"] > 2} {if $GLOBALS["_nesAttGloCou"] > 2}
@ -51,7 +62,7 @@
{include "post.xml", post => $attachment, compact => true} {include "post.xml", post => $attachment, compact => true}
{/if} {/if}
{elseif $attachment instanceof \openvk\Web\Models\Entities\Audio} {elseif $attachment instanceof \openvk\Web\Models\Entities\Audio}
<div style="width:100%;"> <div style="width:100%;" data-att_type='audio' data-att_id="{$attachment->getPrettyId()}">
{include "../Audio/player.xml", audio => $attachment} {include "../Audio/player.xml", audio => $attachment}
</div> </div>
{else} {else}

View file

@ -5,7 +5,7 @@
{var $postId = $target instanceof \openvk\Web\Models\Entities\Post ? $target->getId() : NULL} {var $postId = $target instanceof \openvk\Web\Models\Entities\Post ? $target->getId() : NULL}
<a name="cid={$comment->getId()}"></a> <a name="cid={$comment->getId()}"></a>
<table border="0" style="font-size: 11px;" class="post comment" id="_comment{$comment->getId()}" data-comment-id="{$comment->getId()}" data-owner-id="{$author->getId()}" data-from-group="{$author instanceof $Club}" n:attr="data-post-id => $postId"> <table data-id="1_{$comment->getId()}" border="0" style="font-size: 11px;" class="post comment" id="_comment{$comment->getId()}" data-comment-id="{$comment->getId()}" data-owner-id="{$author->getId()}" data-from-group="{$author instanceof $Club}" n:attr="data-post-id => $postId">
<tbody> <tbody>
<tr> <tr>
<td width="30" valign="top"> <td width="30" valign="top">
@ -22,12 +22,12 @@
</div> </div>
<div class="post-content" id="{$comment->getId()}"> <div class="post-content" id="{$comment->getId()}">
<div class="text" id="text{$comment->getId()}"> <div class="text" id="text{$comment->getId()}">
<span data-text="{$comment->getText(false)}" class="really_text">{$comment->getText()|noescape}</span> <span class="really_text">{$comment->getText()|noescape}</span>
{var $attachmentsLayout = $comment->getChildrenWithLayout(288)} {var $attachmentsLayout = $comment->getChildrenWithLayout(288)}
<div n:ifcontent class="attachments attachments_b" style="height: {$attachmentsLayout->height|noescape}; width: {$attachmentsLayout->width|noescape};"> <div n:ifcontent class="attachments attachments_b" style="height: {$attachmentsLayout->height|noescape}; width: {$attachmentsLayout->width|noescape};">
<div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};" data-localized-nsfw-text="{_nsfw_warning}"> <div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};" data-localized-nsfw-text="{_nsfw_warning}">
{include "attachment.xml", attachment => $attachment[2], parent => $comment, parentType => "comment"} {include "attachment.xml", attachment => $attachment[2], parent => $comment, parentType => "comment", tilesCount => sizeof($attachmentsLayout->tiles)}
</div> </div>
</div> </div>
@ -77,6 +77,7 @@
</span> </span>
</div> </div>
</div> </div>
<div class='post-edit'></div>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View file

@ -18,7 +18,7 @@
{var $commentTextAreaId = $post === NULL ? rand(1,300) : $post->getId()} {var $commentTextAreaId = $post === NULL ? rand(1,300) : $post->getId()}
<table border="0" style="font-size: 11px;" n:class="post, !$compact ? post-divider, $post->isExplicit() ? post-nsfw"> <table border="0" style="font-size: 11px;" data-id="{$post->getPrettyId()}" n:class="post, !$compact ? post-divider, $post->isExplicit() ? post-nsfw">
<tbody> <tbody>
<tr> <tr>
<td width="54" valign="top"> <td width="54" valign="top">
@ -68,13 +68,10 @@
{/if} {/if}
{if $post->canBeEditedBy($thisUser) && !($forceNoEditLink ?? false) && $compact == false} {if $post->canBeEditedBy($thisUser) && !($forceNoEditLink ?? false) && $compact == false}
<a class="edit" id="editPost" <a class="edit" id="editPost"></a>
data-id="{$post->getId()}"
data-nsfw="{(int)$post->isExplicit()}"
{if $post->getTargetWall() < 0 && $wallOwner->canBeModifiedBy($thisUser)}data-fromgroup="{(int)$post->isPostedOnBehalfOfGroup()}"{/if}></a>
{/if} {/if}
</div> </div>
<div class="post-content" id="{$post->getPrettyId()}"> <div class="post-content" id="{$post->getPrettyId()}" data-localized-nsfw-text="{_nsfw_warning}">
<div class="text" id="text{$post->getPrettyId()}"> <div class="text" id="text{$post->getPrettyId()}">
<span data-text="{$post->getText(false)}" class="really_text">{$post->getText()|noescape}</span> <span data-text="{$post->getText(false)}" class="really_text">{$post->getText()|noescape}</span>
@ -84,8 +81,8 @@
{/if} {/if}
{var $attachmentsLayout = $post->getChildrenWithLayout($width)} {var $attachmentsLayout = $post->getChildrenWithLayout($width)}
<div n:ifcontent class="attachments attachments_b" style="height: {$attachmentsLayout->height|noescape}; width: {$attachmentsLayout->width|noescape};"> <div n:ifcontent class="attachments attachments_b" style="height: {$attachmentsLayout->height|noescape}; width: {$attachmentsLayout->width|noescape};">
<div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};" data-localized-nsfw-text="{_nsfw_warning}"> <div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};">
{include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post"} {include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post", tilesCount => sizeof($attachmentsLayout->tiles)}
</div> </div>
</div> </div>
@ -112,6 +109,7 @@
</span> </span>
</div> </div>
</div> </div>
<div class='post-edit' n:if='!$compact'></div>
<div class="post-menu" n:if="$compact == false"> <div class="post-menu" n:if="$compact == false">
<a href="{if !$suggestion}/wall{$post->getPrettyId()}{else}javascript:void(0){/if}" class="date">{$post->getPublicationTime()} <a href="{if !$suggestion}/wall{$post->getPrettyId()}{else}javascript:void(0){/if}" class="date">{$post->getPublicationTime()}
<span n:if="$post->getEditTime()" class="edited editedMark">({_edited_short})</span> <span n:if="$post->getEditTime()" class="edited editedMark">({_edited_short})</span>
@ -126,7 +124,7 @@
<a n:if="!($forceNoCommentsLink ?? false) && $commentsCount == 0" href="javascript:expand_comment_textarea({$commentTextAreaId})">{_comment}</a> <a n:if="!($forceNoCommentsLink ?? false) && $commentsCount == 0" href="javascript:expand_comment_textarea({$commentTextAreaId})">{_comment}</a>
<div class="like_wrap"> <div class="like_wrap">
<a n:if="!($forceNoShareLink ?? false)" id="reposts{$post->getPrettyId()}" class="post-share-button" href="javascript:repostPost('{$post->getPrettyId()}', '{rawurlencode($csrfToken)}')"> <a n:if="!($forceNoShareLink ?? false)" id="reposts{$post->getPrettyId()}" class="post-share-button" href="javascript:repost('{$post->getPrettyId()}', 'post')">
<div class="repost-icon" style="opacity: 0.4;"></div> <div class="repost-icon" style="opacity: 0.4;"></div>
<span class="likeCnt" id="repostsCount{$post->getPrettyId()}">{if $repostsCount > 0}{$repostsCount}{/if}</span> <span class="likeCnt" id="repostsCount{$post->getPrettyId()}">{if $repostsCount > 0}{$repostsCount}{/if}</span>
</a> </a>

View file

@ -15,7 +15,7 @@
<table border="0" style="font-size: 11px;" n:class="post, $post->isExplicit() ? post-nsfw"> <table border="0" style="font-size: 11px;" data-id="{$post->getPrettyId()}" n:class="post, $post->isExplicit() ? post-nsfw">
<tbody> <tbody>
<tr> <tr>
<td width="54" valign="top"> <td width="54" valign="top">
@ -67,7 +67,7 @@
</a> </a>
</a> </a>
</div> </div>
<div class="post-content" id="{$post->getPrettyId()}"> <div class="post-content" id="{$post->getPrettyId()}" data-localized-nsfw-text="{_nsfw_warning}">
<div class="text" id="text{$post->getPrettyId()}"> <div class="text" id="text{$post->getPrettyId()}">
{var $owner = $author->getId()} {var $owner = $author->getId()}
@ -79,8 +79,8 @@
{/if} {/if}
{var $attachmentsLayout = $post->getChildrenWithLayout($width)} {var $attachmentsLayout = $post->getChildrenWithLayout($width)}
<div n:ifcontent class="attachments attachments_b" style="height: {$attachmentsLayout->height|noescape}; width: {$attachmentsLayout->width|noescape};"> <div n:ifcontent class="attachments attachments_b" style="height: {$attachmentsLayout->height|noescape}; width: {$attachmentsLayout->width|noescape};">
<div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};" data-localized-nsfw-text="{_nsfw_warning}"> <div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};">
{include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post"} {include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post", tilesCount => sizeof($attachmentsLayout->tiles)}
</div> </div>
</div> </div>
@ -111,6 +111,7 @@
</span> </span>
</div> </div>
</div> </div>
<div class='post-edit' n:if='!$compact'></div>
<div n:if="!($compact ?? false)" class="post-menu"> <div n:if="!($compact ?? false)" class="post-menu">
{if is_null($thisUser)} {if is_null($thisUser)}
{var $forceNoDeleteLink = true} {var $forceNoDeleteLink = true}
@ -119,9 +120,7 @@
{if !($forceNoEditLink ?? false) && $post->canBeEditedBy($thisUser)} {if !($forceNoEditLink ?? false) && $post->canBeEditedBy($thisUser)}
<a id="editPost" <a id="editPost"
data-id="{$post->getId()}" data-id="{$post->getPrettyId()}">{_edit}</a> &nbsp;|&nbsp;
data-nsfw="{(int)$post->isExplicit()}"
{if $post->getTargetWall() < 0 && $wallOwner->canBeModifiedBy($thisUser)}data-fromgroup="{(int)$post->isPostedOnBehalfOfGroup()}"{/if}>{_edit}</a> &nbsp;|&nbsp;
{/if} {/if}
{if !($forceNoDeleteLink ?? false) && $canBeDeleted} {if !($forceNoDeleteLink ?? false) && $canBeDeleted}
@ -148,7 +147,7 @@
&nbsp;|&nbsp; &nbsp;|&nbsp;
{/if} {/if}
<a n:if="!($forceNoShareLink ?? false)" id="reposts{$post->getPrettyId()}" class="post-share-button" {ifset $thisUser} href="javascript:repostPost('{$post->getPrettyId()}', '{rawurlencode($csrfToken)}')"{/ifset}> <a n:if="!($forceNoShareLink ?? false)" id="reposts{$post->getPrettyId()}" class="post-share-button" {ifset $thisUser} href="javascript:repost('{$post->getPrettyId()}', 'post')"{/ifset}>
{_share} {_share}
{if $repostsCount > 0} {if $repostsCount > 0}
(<b id="repostsCount{$post->getPrettyId()}">{$repostsCount}</b>) (<b id="repostsCount{$post->getPrettyId()}">{$repostsCount}</b>)

View file

@ -2,26 +2,18 @@
{var $textAreaId = ($post ?? NULL) === NULL ? (++$GLOBALS["textAreaCtr"]) : $post->getId()} {var $textAreaId = ($post ?? NULL) === NULL ? (++$GLOBALS["textAreaCtr"]) : $post->getId()}
{var $textAreaId = ($custom_id ?? NULL) === NULL ? $textAreaId : $custom_id} {var $textAreaId = ($custom_id ?? NULL) === NULL ? $textAreaId : $custom_id}
<div id="write" style="padding: 5px 0;" onfocusin="expand_wall_textarea({$textAreaId});"> <div id="write" class='model_content_textarea' style="padding: 5px 0;">
<form action="{$route}" method="post" enctype="multipart/form-data" style="margin:0;"> <form action="{$route}" method="post" enctype="multipart/form-data" style="margin:0;">
<textarea id="wall-post-input{$textAreaId}" placeholder="{_write}" name="text" style="width: 100%;resize: none;" class="small-textarea"></textarea> <textarea id="wall-post-input{$textAreaId}" placeholder="{_write}" name="text" style="width: 100%;resize: none;" class="small-textarea"></textarea>
<div> <div>
<!-- padding to fix <br/> bug --> <!-- padding to fix <br/> bug -->
</div> </div>
<div id="post-buttons{$textAreaId}" style="display: none;"> <div id="post-buttons{$textAreaId}" class='post-buttons' style="display: none;">
<div class="upload"> <div class="post-horizontal"></div>
<div class="post-vertical"></div>
</div>
<div class="post-upload">
<span style="color: inherit;"></span>
</div>
<div class="post-has-poll"> <div class="post-has-poll">
{_poll} {_poll}
</div> </div>
<div class="post-has-note"></div>
<div class="post-has-videos"></div>
<div class="post-has-audios"></div>
<div class="post-source"></div> <div class="post-source"></div>
<div n:if="$postOpts ?? true" class="post-opts"> <div n:if="$postOpts ?? true" class="post-opts">
@ -62,42 +54,44 @@
<input type="checkbox" name="as_group" /> {_comment_as_group} <input type="checkbox" name="as_group" /> {_comment_as_group}
</label> </label>
</div> </div>
<input type="hidden" name="photos" value="" /> <input type="hidden" name="horizontal_attachments" value="" />
<input type="hidden" name="videos" value="" /> <input type="hidden" name="vertical_attachments" value="" />
<input type="hidden" name="audios" value="" />
<input type="hidden" name="poll" value="none" /> <input type="hidden" name="poll" value="none" />
<input type="hidden" id="note" name="note" value="none" />
<input type="hidden" id="source" name="source" value="none" /> <input type="hidden" id="source" name="source" value="none" />
<input type="hidden" name="type" value="1" /> <input type="hidden" name="type" value="1" />
<input type="hidden" name="hash" value="{$csrfToken}" /> <input type="hidden" name="hash" value="{$csrfToken}" />
<br/> <br/>
<input type="submit" value="{_write}" class="button" /> <input type="submit" value="{_write}" class="button" />
<div style="float: right; display: flex; flex-direction: column;"> <div style="float: right; display: flex; flex-direction: column;">
<a href="javascript:toggleMenu({$textAreaId});"> <a class='menu_toggler'>
{_attach} {_attach}
</a> </a>
<div id="wallAttachmentMenu" class="hidden"> <div id="wallAttachmentMenu" class="hidden">
<a class="header" href="javascript:toggleMenu({$textAreaId});"> <a class="header menu_toggler">
{_attach} {_attach}
</a> </a>
<a id="photosAttachments" {if !is_null($club ?? NULL) && $club->canBeModifiedBy($thisUser)}data-club="{$club->getId()}"{/if}> <a id="__photoAttachment" {if !is_null($club ?? NULL) && $club->canBeModifiedBy($thisUser)}data-club="{$club->getId()}"{/if}>
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-x-egon.png" /> <img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-x-egon.png" />
{_photo} {_photo}
</a> </a>
<a id="videoAttachment"> <a id="__videoAttachment">
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-vnd.rn-realmedia.png" /> <img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-vnd.rn-realmedia.png" />
{_video} {_video}
</a> </a>
<a id="_audioAttachment"> <a id="__audioAttachment">
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/audio-ac3.png" /> <img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/audio-ac3.png" />
{_audio} {_audio}
</a> </a>
<a n:if="$graffiti ?? false" href="javascript:initGraffiti({$textAreaId});"> <a n:if="$notes ?? false" id="__notesAttachment">
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-x-srt.png" />
{_note}
</a>
<a n:if="$graffiti ?? false" onclick="initGraffiti(event);">
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/actions/draw-brush.png" /> <img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/actions/draw-brush.png" />
{_graffiti} {_graffiti}
</a> </a>
<a n:if="$polls ?? false" href="javascript:initPoll({$textAreaId})"> <a n:if="$polls ?? false" onclick="initPoll(event);">
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/actions/office-chart-bar-stacked.png" /> <img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/actions/office-chart-bar-stacked.png" />
{_poll} {_poll}
</a> </a>
@ -112,13 +106,8 @@
</div> </div>
<script> <script>
$(document).ready(() => { u("#post-buttons{$textAreaId} input[name='horizontal_attachments']")["nodes"].at(0).value = ""
setupWallPostInputHandlers({$textAreaId}); u("#post-buttons{$textAreaId} input[name='vertical_attachments']")["nodes"].at(0).value = ""
});
u("#post-buttons{$textAreaId} input[name='videos']")["nodes"].at(0).value = ""
u("#post-buttons{$textAreaId} input[name='photos']")["nodes"].at(0).value = ""
u("#post-buttons{$textAreaId} input[name='audios']")["nodes"].at(0).value = ""
</script> </script>
{if $graffiti} {if $graffiti}

View file

@ -3,7 +3,7 @@
<tbody> <tbody>
<tr> <tr>
<td valign="top"> <td valign="top">
<a href="/video{$video->getPrettyId()}" data-id="{$video->getId()}"> <a href="/video{$video->getPrettyId()}" id='videoOpen' data-id="{$video->getPrettyId()}">
<div class="video-preview"> <div class="video-preview">
<img src="{$video->getThumbnailURL()}" <img src="{$video->getThumbnailURL()}"
style="max-width: 170px; max-height: 127px; margin: auto;" > style="max-width: 170px; max-height: 127px; margin: auto;" >
@ -15,10 +15,13 @@
{include infotable, x => $dat} {include infotable, x => $dat}
{else} {else}
<a href="/video{$video->getPrettyId()}"> <a href="/video{$video->getPrettyId()}">
<b class='video_name' {$videoModal ? "id='videoOpen'" : ''} data-id="{$video->getId()}"> <b class='video_name' id='videoOpen' data-id="{$video->getPrettyId()}">
{$video->getName()} {$video->getName()}
</b> </b>
</a> </a>
{if $video->getLength()}
({$video->getFormattedLength()})
{/if}
<br/> <br/>
<p> <p>
<span class='video_description'>{$video->getDescription() ?? ""}</span> <span class='video_description'>{$video->getDescription() ?? ""}</span>
@ -26,7 +29,7 @@
<span style="color: grey;">{_video_uploaded} {$video->getPublicationTime()}</span><br/> <span style="color: grey;">{_video_uploaded} {$video->getPublicationTime()}</span><br/>
<p> <p>
<a href="/video{$video->getPrettyId()}" {$videoModal ? "id='videoOpen'" : ''} data-id="{$video->getId()}">{_view_video}</a> <a href="/video{$video->getPrettyId()}" id='videoOpen' data-id="{$video->getPrettyId()}">{_view_video}</a>
{if $video->getCommentsCount() > 0}| <a href="/video{$video->getPrettyId()}#comments">{_comments} ({$video->getCommentsCount()})</a>{/if} {if $video->getCommentsCount() > 0}| <a href="/video{$video->getPrettyId()}#comments">{_comments} ({$video->getCommentsCount()})</a>{/if}
</p> </p>
{/ifset} {/ifset}

View file

@ -18,7 +18,7 @@ class Makima
$this->photos = $photos; $this->photos = $photos;
} }
private function getOrientation(Photo $photo, &$ratio): int private function getOrientation($photo, &$ratio): int
{ {
[$width, $height] = $photo->getDimensions(); [$width, $height] = $photo->getDimensions();
$ratio = $width / $height; $ratio = $width / $height;

View file

@ -129,8 +129,6 @@ routes:
handler: "Wall->rss" handler: "Wall->rss"
- url: "/wall{num}/makePost" - url: "/wall{num}/makePost"
handler: "Wall->makePost" handler: "Wall->makePost"
- url: "/wall/edit"
handler: "Wall->edit"
- url: "/wall{num}_{num}" - url: "/wall{num}_{num}"
handler: "Wall->post" handler: "Wall->post"
- url: "/wall{num}_{num}/like" - url: "/wall{num}_{num}/like"
@ -171,6 +169,8 @@ routes:
handler: "Photos->uploadPhoto" handler: "Photos->uploadPhoto"
- url: "/photo{num}_{num}" - url: "/photo{num}_{num}"
handler: "Photos->photo" handler: "Photos->photo"
- url: "/photo{num}_{num}/like"
handler: "Photos->like"
- url: "/photos/thumbnails/{num}_{text}.jpeg" - url: "/photos/thumbnails/{num}_{text}.jpeg"
handler: "Photos->thumbnail" handler: "Photos->thumbnail"
- url: "/photos/{text}" - url: "/photos/{text}"
@ -403,6 +403,8 @@ routes:
handler: "About->dev" handler: "About->dev"
- url: "/iapi/getPhotosFromPost/{num}_{num}" - url: "/iapi/getPhotosFromPost/{num}_{num}"
handler: "InternalAPI->getPhotosFromPost" handler: "InternalAPI->getPhotosFromPost"
- url: "/iapi/getPostTemplate/{num}_{num}"
handler: "InternalAPI->getPostTemplate"
- url: "/tour" - url: "/tour"
handler: "About->tour" handler: "About->tour"
- url: "/{?shortCode}" - url: "/{?shortCode}"

View file

@ -267,6 +267,17 @@
color: white; color: white;
} }
.audioEntry .volume {
display: flex;
flex-direction: column;
width: 14%;
}
.audioEntry .nobold {
text-align: center;
margin-top: 12px;
}
.audioEntry.nowPlaying .volume .nobold { .audioEntry.nowPlaying .volume .nobold {
color: white !important; color: white !important;
} }
@ -303,6 +314,7 @@
height: 100%; height: 100%;
position: relative; position: relative;
width: 100%; width: 100%;
min-height: 39px;
} }
.audioEntry .playerButton { .audioEntry .playerButton {
@ -341,6 +353,7 @@
grid-template-columns: 1fr; grid-template-columns: 1fr;
width: 85%; width: 85%;
height: 23px; height: 23px;
margin-top: 12px;
} }
.audioEntry .status .mediaInfo { .audioEntry .status .mediaInfo {
@ -588,7 +601,6 @@
} }
.attachAudio { .attachAudio {
float: left;
width: 28%; width: 28%;
height: 26px; height: 26px;
padding-top: 11px; padding-top: 11px;
@ -771,6 +783,7 @@
.addToPlaylist { .addToPlaylist {
width: 22%; width: 22%;
float: left;
} }
#_addAudioAdditional { #_addAudioAdditional {

View file

@ -88,3 +88,7 @@ div.ovk-video > div > img
height: 31px; height: 31px;
min-width: 30px; min-width: 30px;
} }
.entity_vertical_list .entity_vertical_list_item img {
border-radius: 20px;
}

View file

@ -24,6 +24,7 @@
gap: 6px; gap: 6px;
z-index: 2; z-index: 2;
opacity: 0; opacity: 0;
height: 43px;
} }
.bsdn-player.bsdn-dirty > .bsdn_controls { .bsdn-player.bsdn-dirty > .bsdn_controls {

View file

@ -7,6 +7,11 @@ body.dimmed > .dimmer {
opacity: .5; opacity: .5;
} }
body.dimmed > .dimmer #absolute_territory {
margin: 100px;
height: 100%;
}
.ovk-diag-cont { .ovk-diag-cont {
z-index: 1024; z-index: 1024;
position: fixed; position: fixed;
@ -59,243 +64,147 @@ body.dimmed > .dimmer {
margin-left: 10px; margin-left: 10px;
} }
/* Modal player */
/* fullscreen player */ .ovk-modal-player-window {
box-shadow: 0px 0px 9px 2px rgba(0, 0, 0, 0.2);
.ovk-fullscreen-player {
top: 9%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, 0%);
z-index: 6667;
position: absolute;
width: 823px; width: 823px;
min-height: 400px; min-height: 400px;
box-shadow: 0px 0px 9px 2px rgba(0, 0, 0, 0.2); margin: 9vh auto 0 auto;
} }
.top-part span { .ovk-modal-player-window #ovk-player-part {
height: 70vh;
background: black;
padding: 15px 20px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.ovk-modal-player-window #ovk-player-part .top-part {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.ovk-modal-player-window #ovk-player-part .top-part b {
color: #515151; color: #515151;
font-size: 13px; font-size: 12px;
transition: color 200ms ease-in-out;
} }
.top-part .clickable:hover { .ovk-modal-player-window #ovk-player-part .top-part .top-part-buttons, .ovk-modal-player-window #ovk-player-part .bottom-part {
color: #ffffff; color: #515151;
} }
.ovk-fullscreen-player .bsdn_teaserTitleBox span { .ovk-modal-player-window #ovk-player-part .top-part .top-part-buttons a, .ovk-modal-player-window #ovk-player-part .bottom-part a {
color: unset; color: #515151;
font-size: unset;
} }
.ovk-fullscreen-player .bsdn-player { .ovk-modal-player-window #ovk-player-info {
max-width: 80%;
max-height: 350px;
}
.inner-player {
background: #000000;
min-height: 438px;
max-height: 439px;
position: relative;
padding-top: 11px;
}
.top-part-name {
font-size: 15px;
font-weight: bolder;
margin-left: 20px;
margin-top: 5px;
}
.top-part-buttons {
float: right;
margin-right: 20px;
}
.top-part-buttons span {
cursor: pointer;
user-select: none;
}
.fplayer {
text-align: center;
margin-top: 20px;
}
.top-part-bottom-buttons {
position: absolute;
margin-left: 20px;
bottom: 0;
margin-bottom: 20px;
}
.top-part-bottom-buttons span {
user-select: none;
}
.top-part .clickable {
cursor: pointer;
}
.bottom-part {
display: none; display: none;
}
.ovk-modal-player-window #ovk-player-info.shown {
display: block;
}
.ovk-modal-player-window #ovk-player-info {
background: white; background: white;
padding-bottom: 20px; min-height: 200px;
padding-top: 30px; padding: 5px 15px;
} }
.left_block { .ovk-modal-player-window .media-page-wrapper-comments {
padding-left: 20px; width: 100%;
/*padding-top: 20px;*/
width: 75%;
float: left;
background: white;
padding-right: 6px;
max-height: 400px;
overflow-y: auto;
} }
.right_block { .ovk-modal-player-window .center-part {
padding-left: 10px; text-align: center;
/*padding-top: 20px;*/ height: 90%;
width: 20%; display: flex;
border-left: 1px solid gray; align-items: center;
float: right; justify-content: center;
} }
.bottom-part span { .ovk-modal-player-window .center-part .gray {
font-size: 13px;
}
.bottom-part .gray {
color: gray; color: gray;
} }
.ovk-fullscreen-dimmer { .ovk-modal-player-window .center-part .bsdn, .ovk-modal-player-window .center-part .bsdn-player {
/* спижжено у пулла с несколькими картинками там где просмотрщик фоток */ height: 100%;
position: fixed; width: 100%;
left: 0px;
top: 0px;
right: 0px;
bottom: 0px;
overflow: auto;
padding-bottom: 20px;
z-index: 300;
}
.v_author {
margin-top: 20px;
} }
.miniplayer { .miniplayer {
position: absolute; position: absolute;
top:0; background: rgba(54, 54, 54, 0.95);
background: rgba(54, 54, 54, 0.9);
border-radius: 3px; border-radius: 3px;
min-width: 299px; min-width: 299px;
min-height: 192px; min-height: 192px;
padding-top: 3px; z-index: 7777;
z-index: 9999; padding: 2px 7px;
display: flex;
flex-direction: column;
} }
.miniplayer .bsdn-player { .miniplayer .miniplayer-head {
max-height: 150px; display: flex;
justify-content: space-between;
align-items: center;
} }
.miniplayer .fplayer { .miniplayer .miniplayer-head b {
max-width: 286px; color: white;
margin-left: 6px;
margin-top: 10px;
}
.miniplayer-actions {
float: right;
margin-right: 8px;
margin-top: 4px;
}
.miniplayer-name {
color: #8a8a8a;
font-size: 14px;
margin-left: 7px;
margin-top: -6px;
font-weight: bolder;
user-select: none; user-select: none;
} }
.ui-draggable { .miniplayer .miniplayer-head .miniplayer-head-buttons {
position:fixed !important; display: flex;
align-items: center;
gap: 4px;
} }
.miniplayer-actions img { .miniplayer .miniplayer-head .miniplayer-head-buttons div {
max-width: 11px; width: 16px;
height: 16px;
background: url(/assets/packages/static/openvk/img/wall.png) no-repeat 1px 0;
background-repeat: no-repeat;
opacity: 0.5;
cursor: pointer; cursor: pointer;
transition: opacity 200ms ease-in-out;
opacity: 70%;
} }
.miniplayer .fplayer iframe { .miniplayer .miniplayer-head .miniplayer-head-buttons div:hover {
max-width: 260px; opacity: 1;
max-height: 160px;
} }
.miniplayer-actions img:hover { .miniplayer .miniplayer-head .miniplayer-head-buttons #__miniplayer_return {
opacity: 100%; background-position: -28px 0px;
} }
#vidComments { .miniplayer .miniplayer-head .miniplayer-head-buttons #__miniplayer_close {
margin-top: 10px; background-position: -12px 0px;
} }
.showMoreComments { .miniplayer .miniplayer-head-buttons {
background: #eaeaea; height: 20px;
cursor: pointer;
text-align: center;
padding: 10px;
user-select: none;
margin-top: 10px;
} }
.loader { .miniplayer .miniplayer-body {
display: none; height: 100%;
position: fixed;
top: -10%;
background: rgba(26, 26, 26, 0.9);;
padding-top: 12px;
width: 91px;
height: 25px;
text-align: center;
border-radius: 1px;
margin: auto;
left: 0;
right: 0;
bottom: 0;
z-index: 5555;
} }
.right-arrow, .left-arrow { .miniplayer .miniplayer-body .bsdn {
position: absolute; height: 100%;
cursor: pointer;
transition: all 200ms ease-in-out;
margin-left: -50px;
background: none;
height: 449px;
width: 57px;
user-select: none;
} }
.right-arrow img, .left-arrow img { .miniplayer .miniplayer-body .bsdn .bsdn-player {
user-select: none; height: 100%;
opacity: 5%;
transition: all 200ms ease-in-out;
} }
.right-arrow:hover, .left-arrow:hover { .miniplayer .miniplayer-body iframe {
background: rgba(0, 0, 0, 0.5); width: 100%;
} height: 100%;
.right-arrow img:hover, .left-arrow img:hover {
opacity: 50%;
} }

View file

@ -13,6 +13,10 @@ body {
word-wrap: break-word; word-wrap: break-word;
} }
body, .ovk-fullscreen-dimmer, .ovk-photo-view-dimmer {
scrollbar-gutter: stable both-edges;
}
.nobold, .nobold,
nobold { nobold {
font-weight: normal; font-weight: normal;
@ -41,6 +45,16 @@ h1 {
margin-top: 0; margin-top: 0;
} }
.display_flex_column {
display: flex;
flex-direction: column;
}
.display_flex_row {
display: flex;
flex-direction: row;
}
.layout { .layout {
width: 791px; width: 791px;
margin: 0 auto; margin: 0 auto;
@ -254,6 +268,28 @@ h1 {
width: calc(550px - 16px - 38px - 171px); width: calc(550px - 16px - 38px - 171px);
} }
.upLeftErrors {
position: fixed;
display: flex;
flex-direction: column;
z-index: 1025;
}
.upLeftErrors .upLeftError {
padding: 2px 2px 2px 2px;
display: inline-block;
border-radius: 0px 0px 5px 0px;
font-size: 15px;
}
.upLeftErrors .upLeftError.upLeftErrorRed {
background: #ff7474;
}
.upLeftErrors .upLeftError.upLeftErrorGreen {
background: #74ff92;
}
.page_body { .page_body {
width: 632px; width: 632px;
float: right; float: right;
@ -621,6 +657,16 @@ input[type=checkbox]:checked:disabled {
background-position: 0 -42px; background-position: 0 -42px;
} }
input[type="number"] {
border: 1px solid #C0CAD5;
padding: 3px;
font-size: 11px;
font-family: tahoma, verdana, arial, sans-serif;
width: 100%;
box-sizing: border-box;
outline: none;
}
#auth { #auth {
padding: 10px; padding: 10px;
} }
@ -762,6 +808,14 @@ h4 {
font-size: 9px; font-size: 9px;
} }
.editing .post-content, .post-edit {
display: none;
}
.editing .post-edit {
display: block;
}
.post-content .text { .post-content .text {
padding: 4px; padding: 4px;
font-size: 11px; font-size: 11px;
@ -1479,7 +1533,7 @@ body.scrolled .toTop:hover, .toTop.has_down:hover {
color: #3c3c3c; color: #3c3c3c;
} }
.post-source #remove_source_button { .post-source #remove_source_button, #small_remove_button {
display: inline-block; display: inline-block;
background-repeat: no-repeat; background-repeat: no-repeat;
background: url('/assets/packages/static/openvk/img/arrows.png?v=2'); background: url('/assets/packages/static/openvk/img/arrows.png?v=2');
@ -1493,7 +1547,7 @@ body.scrolled .toTop:hover, .toTop.has_down:hover {
cursor: pointer; cursor: pointer;
} }
.post-source #remove_source_button:hover { .post-source #remove_source_button:hover, #small_remove_button:hover {
opacity: 0.8; opacity: 0.8;
} }
@ -1563,6 +1617,15 @@ body.scrolled .toTop:hover, .toTop.has_down:hover {
float: right; float: right;
} }
.like_wrap.tidy {
float: none;
display: inline-block;
}
.like_wrap.tidy .post-like-button {
display: flex;
}
.heart { .heart {
background: url('/assets/packages/static/openvk/img/like.gif') no-repeat 1px 0; background: url('/assets/packages/static/openvk/img/like.gif') no-repeat 1px 0;
height: 10px; height: 10px;
@ -1572,12 +1635,22 @@ body.scrolled .toTop:hover, .toTop.has_down:hover {
opacity: 0.4; opacity: 0.4;
} }
.like_wrap.tidy .heart {
float: none;
display: inline-block;
}
.likeCnt { .likeCnt {
font-size: 10px; font-size: 10px;
margin-right: 3px; margin-right: 3px;
float: left; float: left;
} }
.like_wrap.tidy .likeCnt {
float: none;
display: inline;
}
.heart:hover { .heart:hover {
opacity: 1 !important; opacity: 1 !important;
} }
@ -1741,12 +1814,14 @@ body.scrolled .toTop:hover, .toTop.has_down:hover {
#wallAttachmentMenu { #wallAttachmentMenu {
position: absolute; position: absolute;
min-width: 94px;
border: 1px solid darkgrey; border: 1px solid darkgrey;
background-color: white; background-color: white;
z-index: 32; z-index: 32;
margin-top: -7px; margin-top: -7px;
margin-left: -16px; margin-left: -16px;
box-shadow: 0 1px 3px -1px; box-shadow: 0 1px 3px -1px;
user-select: none;
} }
#wallAttachmentMenu>a { #wallAttachmentMenu>a {
@ -2145,6 +2220,10 @@ table td[width="120"] {
border-radius: 2px; border-radius: 2px;
} }
.mb_tab:hover {
background: #e2e0e0;
}
.mb_tab div, .mb_tab > a { .mb_tab div, .mb_tab > a {
padding: 5px 9px; padding: 5px 9px;
display: block; display: block;
@ -2400,26 +2479,66 @@ a.poll-retract-vote {
} }
} }
.upload { .post-horizontal, .post-vertical {
margin-top: 8px; margin-top: 0px;
} }
.upload .upload-item { .post-horizontal {
width: 75px; display: flex;
height: 60px; flex-direction: row;
flex-wrap: wrap;
gap: 3px;
}
.post-vertical {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: 3px;
}
.post-horizontal .upload-item {
/*width: 75px;
height: 60px;*/
overflow: hidden; overflow: hidden;
display: inline-block; display: inline-flex;
margin-right: 3px; justify-content: center;
align-items: center;
position: relative;
min-height: 63px;
} }
.upload-item .upload-delete { .post-horizontal .upload-item .play-button, .compact_video .play-button {
position: absolute;
height: 30px;
width: 30px;
background: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
}
.post-horizontal .upload-item .play-button .play-button-ico, .compact_video .play-button .play-button-ico {
background: url(/assets/packages/static/openvk/img/wall.png) no-repeat 1px 0;
display: inline-block;
height: 15px;
width: 12px;
}
.upload-item.dragged {
filter: contrast(0.5);
}
.post-horizontal .upload-item .upload-delete {
position: absolute; position: absolute;
background: rgba(0,0,0,0.5); background: rgba(0,0,0,0.5);
padding: 2px 5px; padding: 2px 5px;
text-decoration: none; text-decoration: none;
color: #fff; color: #fff;
font-size: 11px; font-size: 11px;
margin-left: 57px; /* мне лень переделывать :DDDD */ right: 0px;
top: 0px;
opacity: 0; opacity: 0;
transition: 0.25s; transition: 0.25s;
} }
@ -2428,13 +2547,68 @@ a.poll-retract-vote {
opacity: 1; opacity: 1;
} }
.upload-item img { .post-horizontal .upload-item img {
width: 100%; width: 100%;
max-height: 60px; max-height: 60px;
object-fit: cover; object-fit: cover;
border-radius: 3px; border-radius: 3px;
} }
.post-vertical .vertical-attachment {
display: grid;
grid-template-columns: 1fr 0fr;
}
.post-vertical .vertical-attachment:first-of-type {
margin-top: 7px;
}
.post-vertical .vertical-attachment .audioEntry {
max-height: 28px;
min-height: 18px;
}
.post-vertical .vertical-attachment .audioEntry:hover {
background: unset !important;
}
.post-vertical .vertical-attachment .audioEntry .playerButton {
padding: 0px 3px 0px 0px;
}
.post-vertical .vertical-attachment .audioEntry .buttons {
display: none;
}
.post-vertical .vertical-attachment .audioEntry .status {
margin-top: 1px;
margin-left: 2px;
height: 15px;
}
.post-vertical .vertical-attachment .audioEntry .nobold {
margin-top: 1px;
}
.post-vertical .vertical-attachment .audioEntry .subTracks {
padding-bottom: 2px;
padding-left: 3px;
padding-right: 0px;
}
.post-vertical .vertical-attachment .audioEntry .audioEntryWrapper {
height: 18px;
}
.post-vertical .vertical-attachment .vertical-attachment-content {
max-height: 27px;
}
.post-vertical .vertical-attachment .vertical-attachment-content .overflowedName {
position: initial;
width: 100% !important;
}
/* https://imgur.com/a/ihB3JZ4 */ /* https://imgur.com/a/ihB3JZ4 */
.ovk-photo-view-dimmer { .ovk-photo-view-dimmer {
@ -2452,21 +2626,33 @@ a.poll-retract-vote {
position: relative; position: relative;
z-index: 999; z-index: 999;
background: #fff; background: #fff;
width: 610px; min-width: 600px;
padding: 20px; width: fit-content;
padding: 25px;
padding-top: 15px; padding-top: 15px;
padding-bottom: 10px; padding-bottom: 10px;
box-shadow: 0px 0px 3px 1px #222; box-shadow: 0px 0px 3px 1px #222;
margin: 15px auto 0 auto; margin: 10px auto 0 auto;
} }
.ovk-photo-details { .ovk-photo-view .photo_viewer_wrapper {
overflow: auto; position: relative;
height: 80vh;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.ovk-photo-view #ovk-photo-img {
max-width: 100%;
max-height: 80vh;
user-select: none;
} }
.photo_com_title { .photo_com_title {
font-weight: bold; font-weight: bold;
padding-bottom: 20px; padding-bottom: 16px;
} }
.photo_com_title div { .photo_com_title div {
@ -2478,7 +2664,6 @@ a.poll-retract-vote {
left: 0; left: 0;
width: 35%; width: 35%;
height: 100%; height: 100%;
max-height: 60vh;
position: absolute; position: absolute;
cursor: pointer; cursor: pointer;
} }
@ -2487,11 +2672,27 @@ a.poll-retract-vote {
right: 0; right: 0;
width: 35%; width: 35%;
height: 100%; height: 100%;
max-height: 60vh;
position: absolute; position: absolute;
cursor: pointer; cursor: pointer;
} }
.ovk-photo-view .media-page-wrapper-details {
display: flex;
gap: 10px;
}
.ovk-photo-view .media-page-wrapper-comments {
overflow-y: auto;
overflow-x: hidden;
min-height: 300px;
width: 100%;
padding: 0px 2px;
}
.ovk-photo-view .ovk-photo-details {
margin-top: 10px;
}
.client_app > img { .client_app > img {
top: 3px; top: 3px;
position: relative; position: relative;
@ -2549,12 +2750,13 @@ a.poll-retract-vote {
padding: 3px 0; padding: 3px 0;
} }
.video-wowzer a::before { .video-wowzer .small-video-ico {
content: "b"; vertical-align: bottom;
color: transparent; display: inline-block;
width: 12px; width: 11px;
background-image: url(/assets/packages/static/openvk/img/videoico.png); height: 14px;
display: none; background: url(/assets/packages/static/openvk/img/wall.png?v=2);
background-position: -87px 0px;
} }
/* Da search */ /* Da search */
@ -2900,17 +3102,33 @@ a.poll-retract-vote {
width: 100%; width: 100%;
} }
.attachment_note_icon { .attachment_note .attachment_note_icon {
max-width: 9px; max-width: 9px;
} }
.attachment_note_text { .attachment_note .attachment_note_text {
color: #605F63; color: #605F63;
margin-left: 2px; margin-left: 2px;
} }
.attachment_note { .attachment_note {
user-select: none; user-select: none;
display: flex;
align-items: center;
gap: 1px;
}
.attachment_note svg {
width: 8px;
height: 10px;
fill: #605f63;
margin-top: 2px;
}
.attachment_note .attachment_note_content {
display: flex;
gap: 4px;
height: 12px;
} }
#notesList #notesList
@ -3135,10 +3353,81 @@ body.article .floating_sidebar, body.article .page_content {
font-size: 11px; font-size: 11px;
} }
.topGrayBlock { .attachment_selector .topGrayBlock {
background: #F0F0F0; background: #F0F0F0;
height: 37px; height: 37px;
border-bottom: 1px solid #C7C7C7; border-bottom: 1px solid #C7C7C7;
padding: 3px 10px;
align-items: center;
justify-content: center;
justify-content: space-between;
}
.attachment_selector .topGrayBlock #video_query {
width: 160px;
}
.attachment_selector .topGrayBlock #albumSelect {
width: 150px;
}
.attachment_selector #attachment_insert {
padding: 5px;
height: 281px;
overflow-y: scroll;
}
.attachment_selector #attachment_insert #attachment_insert_count {
position: fixed;
z-index: 1007;
width: 92%;
background: white;
margin-top: -5px;
padding-top: 6px;
}
#show_more {
width: 100%;
text-align: center;
background: #f0f0f0;
height: 22px;
padding-top: 9px;
cursor: pointer;
margin-top: 4px;
}
#show_more:hover {
background: #f2f2f2;
}
.attachment_selector #attachment_insert .photosList {
margin-top: 20px;
gap: 3px 6px;
}
.attachment_selector #attachment_insert .photosList .album-photo.selected img {
background-color: #646464;
}
.attachment_selector #attachment_insert .photosList .album-photo {
width: 15.8%;
margin: unset;
}
.attachment_selector #attachment_insert .videosInsert .video_list .video-preview {
height: 75px;
width: 133px;
overflow: hidden;
}
.attachment_selector #attachment_insert .videosInsert .video_list .video-preview img {
max-width: 133px;
height: 75px;
margin: auto;
}
.attachment_selector #attachment_insert .videosInsert .video_list .action_links {
width: 150px;
} }
.edited { .edited {
@ -3156,16 +3445,20 @@ body.article .floating_sidebar, body.article .page_content {
user-select: none; user-select: none;
} }
.editMenu.loading { .edit_menu.loading {
filter: opacity(0.5); filter: opacity(0.5);
cursor: progress; cursor: progress;
user-select: none; user-select: none;
} }
.editMenu.loading * { .edit_menu.loading * {
pointer-events: none; pointer-events: none;
} }
.edit_menu .edit_menu_buttons {
margin-top: 10px;
}
.lagged *, .lagged { .lagged *, .lagged {
pointer-events: none; pointer-events: none;
} }
@ -3288,6 +3581,7 @@ hr {
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
gap: 4px; gap: 4px;
cursor: pointer;
} }
.entity_vertical_list .entity_vertical_list_item .first_column { .entity_vertical_list .entity_vertical_list_item .first_column {
@ -3325,3 +3619,167 @@ hr {
width: 30px; width: 30px;
height: 7px; height: 7px;
} }
.compact_video {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
position: relative;
margin-left: -2px;
margin-top: -2px;
}
.compact_video .video-length {
text-align: center;
position: absolute;
width: 34px;
height: 14px;
bottom: 5px;
right: 5px;
background: rgba(0,0,0,0.5);
color: white;
}
.media-page-wrapper .photo-page-wrapper-photo {
margin-bottom: 8pt;
text-align: center;
}
.media-page-wrapper .video-page-wrapper-video iframe {
width: 100%;
}
.media-page-wrapper .photo-page-wrapper-photo img {
max-width: 85%;
max-height: 60vh;
}
.media-page-wrapper-details {
width: 100%;
min-height: 100px;
display: flex;
justify-content: space-between;
gap: 10px;
}
.media-page-wrapper-comments {
min-height: 100px;
width: 68%;
margin-left: 3px;
}
.media-page-wrapper-actions {
min-height: 100px;
width: 30%;
margin-left: 1px;
}
.media-page-wrapper-description p {
margin: 0px;
}
.media-page-wrapper-description .upload_time {
color: gray;
display: inline-flex;
align-items: center;
height: 16px;
}
.media-page-author-block {
display: flex;
align-items: center;
gap: 7px;
margin-bottom: 5px;
}
.media-page-author-block img {
width: 30px;
}
.media-page-author-block .media-page-author-block-name {
display: flex;
flex-direction: column;
justify-content: center;
}
.media-page-author-block .media-page-author-block-name b {
font-weight: bold;
}
.media-page-author-block .media-page-author-block-name span {
text-transform: lowercase;
}
#ajloader {
display: none;
position: fixed;
top: -10%;
background: rgba(26, 26, 26, 0.9);;
padding-top: 12px;
width: 91px;
height: 25px;
text-align: center;
border-radius: 1px;
margin: auto;
left: 0;
right: 0;
bottom: 0;
z-index: 5555;
}
#ajloader.shown {
display: block;
}
.hoverable_color {
transition: all 0.17s ease-in-out;
}
.hoverable_color:hover {
color: white !important;
}
#__feed_settings_link {
float: right;
margin-right: 3px;
margin-top: 2px;
}
#__feed_settings_link:hover {
text-decoration: underline;
}
#_feed_settings_container {
height: 100%;
}
#_feed_settings_container #__content {
display: flex;
flex-direction: column;
gap: 4px;
padding: 5px;
height: 100%;
}
#_feed_settings_container #__content .settings_item {
display: flex;
flex-direction: column;
}
#_feed_settings_container #__content .final_settings_item {
align-self: end;
margin-top: 74px;
}
#_feed_settings_container #pageNumber {
width: 70px;
}
#_feed_settings_container .entity_vertical_list {
height: 206px;
overflow-x: hidden;
}

View file

@ -1,28 +1,28 @@
.post-nsfw .post-content .media { .post-nsfw .post-content {
filter: saturate(0.8) blur(15px);
}
.post-nsfw .post-content .attachment {
overflow: hidden;
position: relative; position: relative;
overflow: hidden;
cursor: pointer;
} }
.post-nsfw .post-content .attachment:active .media { .post-nsfw .post-content .text {
filter: none; filter: saturate(0.8) blur(28px);
pointer-events: none;
} }
.post-nsfw .post-content .attachment::after { .post-nsfw .post-content::after {
position: absolute; position: absolute;
top: calc(50% - 16px); top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
padding: 8px 0; height: 100%;
background-color: hsla(0, 0%, 0%, .5); z-index: 1;
background-color: hsla(0, 0%, 0%, .7);
color: #fff; color: #fff;
text-align: center; text-align: center;
content: attr(data-localized-nsfw-text); content: attr(data-localized-nsfw-text);
}
.post-nsfw .post-content .attachment:active::after { display: flex;
display: none; flex-direction: row;
justify-content: center;
align-items: center;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 932 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 932 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 B

View file

@ -1 +0,0 @@
<svg id="note" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35 40"><defs><style>.cls-1{fill:#a0a0a0;}</style></defs><title>note</title><polygon class="cls-1" points="0 0 0 40 35 40 35 20 17.5 20 17.5 0 0 0"/><polygon id="block" class="cls-1" points="20.26 1 35 18 20.26 18 20.26 1"/></svg>

Before

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

BIN
Web/static/img/wall.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -34,4 +34,22 @@ window.API = new Proxy(Object.create(null), {
window.API.Types = {}; window.API.Types = {};
window.API.Types.Message = (class Message { window.API.Types.Message = (class Message {
}); });
window.OVKAPI = new class {
async call(method, params) {
if(!method) {
return
}
const url = `/method/${method}?auth_mechanism=roaming&${new URLSearchParams(params).toString()}&v=5.200`
const res = await fetch(url)
const json_response = await res.json()
if(json_response.response) {
return json_response.response
} else {
throw new Error(json_response.error_msg)
}
}
}

270
Web/static/js/al_feed.js Normal file
View file

@ -0,0 +1,270 @@
//u('.postFeedPageSelect').attr('style', 'display:none')
// Source ignoring
u(document).on("click", "#__ignoreSomeone", async (e) => {
e.preventDefault()
const TARGET = u(e.target)
const ENTITY_ID = Number(e.target.dataset.id)
const VAL = Number(e.target.dataset.val)
const ACT = VAL == 1 ? 'ignore' : 'unignore'
const METHOD_NAME = ACT == 'ignore' ? 'addBan' : 'deleteBan'
const PARAM_NAME = ENTITY_ID < 0 ? 'group_ids' : 'user_ids'
const ENTITY_NAME = ENTITY_ID < 0 ? 'club' : 'user'
const URL = `/method/newsfeed.${METHOD_NAME}?auth_mechanism=roaming&${PARAM_NAME}=${Math.abs(ENTITY_ID)}`
TARGET.addClass('lagged')
const REQ = await fetch(URL)
const RES = await REQ.json()
TARGET.removeClass('lagged')
if(RES.error_code) {
switch(RES.error_code) {
case -10:
fastError(';/')
break
case -50:
fastError(tr('ignored_sources_limit'))
break
default:
fastError(res.error_msg)
break
}
return
}
if(RES.response == 1) {
if(ACT == 'unignore') {
TARGET.attr('data-val', '1')
TARGET.html(tr(`ignore_${ENTITY_NAME}`))
} else {
TARGET.attr('data-val', '0')
TARGET.html(tr(`unignore_${ENTITY_NAME}`))
}
}
})
u(document).on('click', '#__feed_settings_link', (e) => {
e.preventDefault()
let current_tab = 'main';
const body = `
<div id='_feed_settings_container'>
<div id='_tabs'>
<div class="mb_tabs">
<div class="mb_tab" data-name='main'>
<a>
${tr('main')}
</a>
</div>
<div class="mb_tab" data-name='ignored'>
<a>
${tr('ignored_sources')}
</a>
</div>
</div>
</div>
<div id='__content'></div>
</div>
`
MessageBox(tr("feed_settings"), body, [tr("close")], [Function.noop])
u('.ovk-diag-body').attr('style', 'padding:0px;height: 255px;')
async function __switchTab(tab)
{
current_tab = tab
u(`#_feed_settings_container .mb_tab`).attr('id', 'ki')
u(`#_feed_settings_container .mb_tab[data-name='${tab}']`).attr('id', 'active')
u(`#_feed_settings_container .mb_tabs input`).remove()
switch(current_tab) {
case 'main':
const __temp_url = new URL(location.href)
const PAGES_COUNT = Number(e.target.dataset.pagescount ?? '10')
const CURRENT_PERPAGE = Number(__temp_url.searchParams.get('posts') ?? 10)
const CURRENT_PAGE = Number(__temp_url.searchParams.get('p') ?? 1)
const CURRENT_RETURN_BANNED = Number(__temp_url.searchParams.get('return_banned') ?? 0)
const COUNT = [1, 5, 10, 20, 30, 40, 50]
u('#_feed_settings_container #__content').html(`
<table cellspacing="7" cellpadding="0" border="0" align="center">
<tbody>
<tr>
<td width="120" valign="top">
<span class="nobold">${tr('posts_per_page')}</span>
</td>
<td>
<select id="pageSelect"></select>
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">${tr('start_from_page')}</span>
</td>
<td>
<input type='number' min='1' max='${PAGES_COUNT}' id='pageNumber' value='${CURRENT_PAGE}' placeholder='${CURRENT_PAGE}'>
</td>
</tr>
<tr>
<td width="120" valign="top">
<span class="nobold">
<input type='checkbox' id="showIgnored" ${CURRENT_RETURN_BANNED == 1 ? 'checked' : ''}>
</span>
</td>
<td>
${tr('show_ignored_sources')}
</td>
</tr>
<tr>
<td width="120" valign="top">
</td>
<td>
<input class='button' type='button' value='${tr('apply')}'>
</td>
</tr>
</tbody>
</table>
`)
u(`#_feed_settings_container #__content input[type='button']`).on('click', (e) => {
const INPUT_PAGES_COUNT = parseInt(u('#_feed_settings_container #__content #pageSelect').nodes[0].selectedOptions[0].value ?? '10')
const INPUT_PAGE = parseInt(u('#_feed_settings_container #__content #pageNumber').nodes[0].value ?? '1')
const INPUT_IGNORED = Number(u('#_feed_settings_container #__content #showIgnored').nodes[0].checked ?? false)
const FINAL_URL = new URL(location.href)
if(CURRENT_PERPAGE != INPUT_PAGES_COUNT) {
FINAL_URL.searchParams.set('posts', INPUT_PAGES_COUNT)
}
if(CURRENT_PAGE != INPUT_PAGE && INPUT_PAGE <= PAGES_COUNT) {
FINAL_URL.searchParams.set('p', Math.max(1, INPUT_PAGE))
}
if(INPUT_IGNORED == 1) {
FINAL_URL.searchParams.set('return_banned', 1)
} else {
FINAL_URL.searchParams.delete('return_banned')
}
window.location.assign(FINAL_URL.href)
})
COUNT.forEach(item => {
u('#_feed_settings_container #pageSelect').append(`
<option value="${item}" ${item == CURRENT_PERPAGE ? 'selected' : ''}>${item}</option>
`)
})
break
case 'ignored':
u('#_feed_settings_container #__content').html(`
<div id='gif_loader'></div>
`)
if(!window.openvk.ignored_list) {
const IGNORED_RES = await fetch('/method/newsfeed.getBanned?auth_mechanism=roaming&extended=1&fields=real_id,screen_name,photo_50&merge=1')
const IGNORED_LIST = await IGNORED_RES.json()
window.openvk.ignored_list = IGNORED_LIST
}
u('#_feed_settings_container #__content').html(`
<div class='entity_vertical_list mini'></div>
`)
u('#_feed_settings_container .mb_tabs').append(`
<input class='button lagged' id='_remove_ignores' type='button' value='${tr('stop_ignore')}'>
`)
if(window.openvk.ignored_list.error_code) {
fastError(IGNORED_LIST.error_msg)
return
}
if(window.openvk.ignored_list.response.items.length < 1) {
u('#_feed_settings_container #__content').html(`
<div class="information">
${tr('no_ignores_count')}
</div>`)
u('#_remove_ignores').remove()
}
window.openvk.ignored_list.response.items.forEach(ignore_item => {
let name = ignore_item.name
if(!name) {
name = ignore_item.first_name + ' ' + ignore_item.last_name
}
u('#_feed_settings_container #__content .entity_vertical_list').append(`
<label class='entity_vertical_list_item with_third_column' data-id='${ignore_item.real_id}'>
<div class='first_column'>
<a href='/${ignore_item.screen_name}' class='avatar'>
<img src='${ignore_item.photo_50}'>
</a>
<div class='info'>
<b class='noOverflow'>
<a href="/${ignore_item.screen_name}">
${ovk_proc_strtr(escapeHtml(name), 100)}
</a>
</b>
</div>
</div>
<div class='third_column' style="display: grid; align-items: center;">
<input type='checkbox' name='remove_me'>
</div>
</label>
`)
})
u("#_feed_settings_container").on("click", "input[name='remove_me']", async (e) => {
const checks_count = u(`input[name='remove_me']:checked`).length
if(checks_count > 0) {
u('.mb_tabs #_remove_ignores').removeClass('lagged')
} else {
u('.mb_tabs #_remove_ignores').addClass('lagged')
}
if(checks_count > 10) {
e.preventDefault()
}
})
u('#_feed_settings_container').on('click', '#_remove_ignores', async (e) => {
e.target.classList.add('lagged')
const ids = []
u('#__content .entity_vertical_list label').nodes.forEach(item => {
const _checkbox = item.querySelector(`input[type='checkbox'][name='remove_me']`)
if(_checkbox.checked) {
ids.push(item.dataset.id)
}
})
const user_ids = []
const group_ids = []
ids.forEach(id => {
id > 0 ? user_ids.push(id) : group_ids.push(Math.abs(id))
})
const res = await fetch(`/method/newsfeed.deleteBan?auth_mechanism=roaming&user_ids=${user_ids.join(',')}&group_ids=${group_ids.join(',')}`)
const resp = await res.json()
if(resp.error_code) {
console.error(resp.error_msg)
return
}
window.openvk.ignored_list = null
__switchTab('ignored')
})
break
}
}
u("#_feed_settings_container").on("click", ".mb_tab a", async (e) => {
await __switchTab(u(e.target).closest('.mb_tab').attr('data-name'))
})
__switchTab('main')
})

View file

@ -1,13 +1,3 @@
function fmtTime(time) {
const mins = String(Math.floor(time / 60)).padStart(2, '0');
const secs = String(Math.floor(time % 60)).padStart(2, '0');
return `${ mins}:${ secs}`;
}
function fastError(message) {
MessageBox(tr("error"), message, [tr("ok")], [Function.noop])
}
// elapsed это вроде прошедшие, а оставшееся это remaining но ладно уже // elapsed это вроде прошедшие, а оставшееся это remaining но ладно уже
function getElapsedTime(fullTime, time) { function getElapsedTime(fullTime, time) {
let timer = fullTime - time let timer = fullTime - time
@ -1354,8 +1344,8 @@ $(document).on("click", "#_deletePlaylist", (e) => {
}, Function.noop]) }, Function.noop])
}) })
$(document).on("click", "#_audioAttachment", (e) => { $(document).on("click", "#__audioAttachment", (e) => {
let form = e.currentTarget.closest("form") const form = e.target.closest("form")
let body = ` let body = `
<div class="searchBox"> <div class="searchBox">
<input name="query" type="text" maxlength="50" placeholder="${tr("header_search")}"> <input name="query" type="text" maxlength="50" placeholder="${tr("header_search")}">
@ -1367,7 +1357,7 @@ $(document).on("click", "#_audioAttachment", (e) => {
<div class="audiosInsert"></div> <div class="audiosInsert"></div>
` `
MessageBox(tr("select_audio"), body, [tr("ok")], [Function.noop]) MessageBox(tr("select_audio"), body, [tr("close")], [Function.noop])
document.querySelector(".ovk-diag-body").style.padding = "0" document.querySelector(".ovk-diag-body").style.padding = "0"
document.querySelector(".ovk-diag-cont").style.width = "580px" document.querySelector(".ovk-diag-cont").style.width = "580px"
@ -1388,23 +1378,24 @@ $(document).on("click", "#_audioAttachment", (e) => {
result.querySelectorAll(".audioEmbed").forEach(el => { result.querySelectorAll(".audioEmbed").forEach(el => {
let id = el.dataset.prettyid let id = el.dataset.prettyid
let name = el.dataset.name const is_attached = (u(form).find(`.post-vertical .vertical-attachment[data-id='${id}']`)).length > 0
let isAttached = (form.querySelector("input[name='audios']").value.includes(`${id},`))
document.querySelector(".audiosInsert").insertAdjacentHTML("beforeend", ` document.querySelector(".audiosInsert").insertAdjacentHTML("beforeend", `
<div style="display: table;width: 100%;clear: both;"> <div class='audio_attachment_header' style="display: flex;width: 100%;">
<div style="width: 72%;float: left;">${el.outerHTML}</div> <div class='player_part' style="width: 72%;">${el.outerHTML}</div>
<div class="attachAudio" data-attachmentdata="${id}" data-name="${name}"> <div class="attachAudio" data-attachmentdata="${id}">
<span>${isAttached ? tr("detach_audio") : tr("attach_audio")}</span> <span>${is_attached ? tr("detach_audio") : tr("attach_audio")}</span>
</div> </div>
</div> </div>
`) `)
}) })
u("#loader").remove() u("#loader").remove()
u('#show_more').remove()
if(thisc.page < pagesCount) { if(thisc.page < pagesCount) {
document.querySelector(".audiosInsert").insertAdjacentHTML("beforeend", ` document.querySelector(".audiosInsert").insertAdjacentHTML("beforeend", `
<div id="showMoreAudios" data-pagesCount="${pagesCount}" data-page="${thisc.page + 1}" class="showMore"> <div id="show_more" data-pagesCount="${pagesCount}" data-page="${thisc.page + 1}" class="showMore">
<span>${tr("show_more_audios")}</span> <span>${tr("show_more_audios")}</span>
</div>`) </div>`)
} }
@ -1424,14 +1415,12 @@ $(document).on("click", "#_audioAttachment", (e) => {
searcher.movePage(1) searcher.movePage(1)
$(".audiosInsert").on("click", "#showMoreAudios", (e) => { u(".audiosInsert").on("click", "#show_more", async (e) => {
u(e.currentTarget).remove() u(e.target).closest('#show_more').addClass('lagged')
searcher.movePage(Number(e.currentTarget.dataset.page)) searcher.movePage(Number(e.currentTarget.dataset.page))
}) })
$(".searchBox input").on("change", async (e) => { $(".searchBox input").on("change", async (e) => {
await new Promise(r => setTimeout(r, 500));
if(e.currentTarget.value === document.querySelector(".searchBox input").value) { if(e.currentTarget.value === document.querySelector(".searchBox input").value) {
searcher.clearContainer() searcher.clearContainer()
@ -1462,44 +1451,34 @@ $(document).on("click", "#_audioAttachment", (e) => {
return; return;
}) })
function insertAttachment(id) { u(".audiosInsert").on("click", ".attachAudio", (ev) => {
let audios = form.querySelector("input[name='audios']") const id = ev.currentTarget.dataset.attachmentdata
const is_attached = u(form).find(`.post-vertical .vertical-attachment[data-id='${id}']`).length > 0
if(!audios.value.includes(id + ",")) { // 04.11.2024 19:03
if(audios.value.split(",").length > 10) { if(is_attached) {
NewNotification(tr("error"), tr("max_attached_audios")) u(form).find(`.post-vertical .vertical-attachment[data-id='${id}']`).remove()
return false u(ev.currentTarget).find("span").html(tr("attach_audio"))
} else {
if(u(form).find(`.upload-item`).length > window.openvk.max_attachments) {
makeError(tr('too_many_attachments'), 'Red', 10000, 1)
return
} }
form.querySelector("input[name='audios']").value += (id + ",") u(ev.currentTarget).find("span").html(tr("detach_audio"))
return true const header = u(ev.currentTarget).closest('.audio_attachment_header')
} else { const player = header.find('.player_part')
form.querySelector("input[name='audios']").value = form.querySelector("input[name='audios']").value.replace(id + ",", "") u(form).find(".post-vertical").append(`
<div class="vertical-attachment upload-item" data-type='audio' data-id="${ev.currentTarget.dataset.attachmentdata}">
return false <div class='vertical-attachment-content'>
} ${player.html()}
} </div>
<div class='vertical-attachment-remove'>
$(".audiosInsert").on("click", ".attachAudio", (ev) => { <div id='small_remove_button'></div>
if(!insertAttachment(ev.currentTarget.dataset.attachmentdata)) { </div>
u(`.post-has-audios .post-has-audio[data-id='${ev.currentTarget.dataset.attachmentdata}']`).remove()
ev.currentTarget.querySelector("span").innerHTML = tr("attach_audio")
} else {
ev.currentTarget.querySelector("span").innerHTML = tr("detach_audio")
form.querySelector(".post-has-audios").insertAdjacentHTML("beforeend", `
<div class="post-has-audio" id="unattachAudio" data-id="${ev.currentTarget.dataset.attachmentdata}">
<span>${ovk_proc_strtr(escapeHtml(ev.currentTarget.dataset.name), 40)}</span>
</div> </div>
`) `)
u(`#unattachAudio[data-id='${ev.currentTarget.dataset.attachmentdata}']`).on("click", (e) => {
let id = ev.currentTarget.dataset.attachmentdata
form.querySelector("input[name='audios']").value = form.querySelector("input[name='audios']").value.replace(id + ",", "")
u(e.currentTarget).remove()
})
} }
}) })
}) })
@ -1509,7 +1488,13 @@ $(document).on("click", ".audioEmbed.processed .playerButton", (e) => {
}) })
$(document).on("click", ".audioEmbed.withdrawn", (e) => { $(document).on("click", ".audioEmbed.withdrawn", (e) => {
MessageBox(tr("error"), tr("audio_embed_withdrawn"), [tr("ok")], [Function.noop]) const msg = new CMessageBox({
title: tr('error'),
body: tr('audio_embed_withdrawn'),
unique_name: 'withdrawn_notify',
buttons: [tr('ok')],
callbacks: [Function.noop]
})
}) })
$(document).on("click", ".musicIcon.report-icon", (e) => { $(document).on("click", ".musicIcon.report-icon", (e) => {

View file

@ -64,8 +64,9 @@ function pollRadioPressed(radio) {
form.submit(); form.submit();
} }
function initPoll(id) { function initPoll(event) {
let form = $(`#wall-post-input${id}`).parent(); let form = $(event.target.closest('.post-buttons')).parent();
const id = random_int(0, 100)
let mBody = ` let mBody = `
<div id="poll_editor${id}"> <div id="poll_editor${id}">
@ -87,9 +88,9 @@ function initPoll(id) {
</div> </div>
`; `;
MessageBox(tr("create_poll"), mBody, [tr("attach"), tr("cancel")], [ const msg = MessageBox(tr("create_poll"), mBody, [tr("attach"), tr("cancel")], [
function() { function() {
let dialog = $(this.$dialog().nodes[0]); let dialog = $(msg.getNode().nodes[0]);
$("input", dialog).unbind(); $("input", dialog).unbind();
let title = $("input[name=title]", dialog).val(); let title = $("input[name=title]", dialog).val();
@ -117,9 +118,9 @@ function initPoll(id) {
$(".post-has-poll", form).show(); $(".post-has-poll", form).show();
}, },
function() { function() {
$("input", $(this.$dialog().nodes[0])).unbind(); $("input", $(msg.getNode())).unbind();
} }
]); ], true);
let editor = $(`#poll_editor${id}`); let editor = $(`#poll_editor${id}`);
$("input[name=newOption]", editor).bind("focus", function() { $("input[name=newOption]", editor).bind("focus", function() {

File diff suppressed because it is too large Load diff

View file

@ -1,42 +1,177 @@
Function.noop = () => {}; Function.noop = () => {};
function MessageBox(title, body, buttons, callbacks) { class CMessageBox {
if(u(".ovk-diag-cont").length > 0) return false; constructor(options = {}) {
const title = options.title ?? 'Untitled'
document.querySelector("html").style.overflowY = "hidden" const body = options.body ?? '<hr>'
let dialog = u( const buttons = options.buttons ?? []
`<div class="ovk-diag-cont"> const callbacks = options.callbacks ?? []
<div class="ovk-diag"> const close_on_buttons = options.close_on_buttons ?? true
<div class="ovk-diag-head">${title}</div> const unique_name = options.unique_name ?? null
<div class="ovk-diag-body">${body}</div> const warn_on_exit = options.warn_on_exit ?? false
<div class="ovk-diag-action"></div> const custom_template = options.custom_template ?? null
</div> if(unique_name && window.messagebox_stack.find(item => item.unique_name == unique_name) != null) {
</div>`); return
u("body").addClass("dimmed").append(dialog); }
buttons.forEach((text, callback) => { this.title = title
u(".ovk-diag-action").append(u(`<button class="button">${text}</button>`)); this.body = body
let button = u(u(".ovk-diag-action > button.button").last()); this.id = random_int(0, 10000)
this.close_on_buttons = close_on_buttons
this.unique_name = unique_name
this.warn_on_exit = warn_on_exit
if(!custom_template) {
u('body').addClass('dimmed').append(this.__getTemplate())
} else {
custom_template.addClass('ovk-msg-all')
custom_template.attr('data-id', this.id)
u('body').addClass('dimmed').append(custom_template)
}
button.on("click", function(e) { u('html').attr('style', 'overflow-y:hidden')
let __closeDialog = () => {
buttons.forEach((text, callback) => {
if(document.querySelector(".ovk-photo-view-dimmer") == null && document.querySelector(".ovk-fullscreen-player") == null) { this.getNode().find('.ovk-diag-action').append(u(`<button class="button">${text}</button>`))
u("body").removeClass("dimmed"); let button = u(this.getNode().find('.ovk-diag-action > button.button').last())
document.querySelector("html").style.overflowY = "scroll" button.on("click", (e) => {
callbacks[callback]()
if(close_on_buttons) {
this.close()
} }
})
u(".ovk-diag-cont").remove(); })
};
window.messagebox_stack.push(this)
Reflect.apply(callbacks[callback], { }
closeDialog: () => __closeDialog(),
$dialog: () => u(".ovk-diag-cont") __getTemplate() {
}, [e]); return u(
`<div class="ovk-diag-cont ovk-msg-all" data-id="${this.id}">
<div class="ovk-diag">
<div class="ovk-diag-head">${this.title}</div>
<div class="ovk-diag-body">${this.body}</div>
<div class="ovk-diag-action"></div>
</div>
</div>`)
}
getNode() {
return u(`.ovk-msg-all[data-id='${this.id}']`)
}
async __showCloseConfirmationDialog() {
return new Promise((resolve, reject) => {
const msg = new CMessageBox({
title: tr('exit_noun'),
body: tr('exit_confirmation'),
warn_on_exit: false,
unique_name: 'close_confirmation',
buttons: [tr('no'), tr('yes')],
callbacks: [() => {
msg.close()
resolve(false)
}, () => {
this.__exitDialog()
resolve(true)
}]
})
})
}
__exitDialog() {
this.getNode().remove()
if(u('.ovk-msg-all').length < 1) {
u('body').removeClass('dimmed')
u('html').attr('style', 'overflow-y:scroll')
}
const current_item = window.messagebox_stack.find(item => item.id == this.id)
const index_of_item = window.messagebox_stack.indexOf(current_item)
window.messagebox_stack = array_splice(window.messagebox_stack, index_of_item)
__closeDialog(); delete this
}); }
});
close() {
return u(".ovk-diag-cont"); this.__exitDialog()
}
hide() {
u('body').removeClass('dimmed')
u('html').attr('style', 'overflow-y:scroll')
this.getNode().attr('style', 'display: none;')
}
reveal() {
u('body').addClass('dimmed')
u('html').attr('style', 'overflow-y:hidden')
this.getNode().attr('style', 'display: block;')
}
static toggleLoader() {
u('#ajloader').toggleClass('shown')
}
} }
window.messagebox_stack = []
function MessageBox(title, body, buttons, callbacks, return_msg = false) {
const msg = new CMessageBox({
title: title,
body: body,
buttons: buttons,
callbacks: callbacks,
})
if(return_msg) {
return msg
}
return msg.getNode()
}
// Close on 'Escape' key
u(document).on('keyup', async (e) => {
if(e.keyCode == 27 && window.messagebox_stack.length > 0) {
const msg = window.messagebox_stack[window.messagebox_stack.length - 1]
if(!msg) {
return
}
if(msg.close_on_buttons) {
msg.close()
return
}
if(msg.warn_on_exit) {
const res = await msg.__showCloseConfirmationDialog()
if(res === true) {
msg.close()
}
}
}
})
// Close when clicking on shadow
u(document).on('click', 'body.dimmed .dimmer', async (e) => {
if(u(e.target).hasClass('dimmer')) {
const msg = window.messagebox_stack[window.messagebox_stack.length - 1]
if(!msg) {
return
}
if(msg.close_on_buttons) {
msg.close()
return
}
if(msg.warn_on_exit) {
const res = await msg.__showCloseConfirmationDialog()
if(res === true) {
msg.close()
}
}
}
})

View file

@ -1,9 +1,5 @@
 if(typeof u == 'undefined') {
function expand_wall_textarea(id) { console.error('!!! You forgot to install NPM packages !!!')
var el = document.getElementById('post-buttons'+id);
var wi = document.getElementById('wall-post-input'+id);
el.style.display = "block";
wi.className = "expanded-textarea";
} }
function expand_comment_textarea(id) { function expand_comment_textarea(id) {
@ -13,19 +9,6 @@ function expand_comment_textarea(id) {
wi.focus(); wi.focus();
} }
function edit_post(id, wid) {
var el = document.getElementById('text'+wid+'_'+id);
var ed = document.getElementById('text_edit'+wid+'_'+id);
if (el.style.display == "none") {
el.style.display = "block";
ed.style.display = "none";
} else {
el.style.display = "none";
ed.style.display = "block";
}
}
function hidePanel(panel, count = 0) function hidePanel(panel, count = 0)
{ {
$(panel).toggleClass("content_title_expanded content_title_unexpanded"); $(panel).toggleClass("content_title_expanded content_title_unexpanded");
@ -56,19 +39,9 @@ function parseAjaxResponse(responseString) {
} }
} }
function toggleMenu(id) {
if($(`#post-buttons${id} #wallAttachmentMenu`).is('.hidden')) {
$(`#post-buttons${id} #wallAttachmentMenu`).css({ opacity: 0 });
$(`#post-buttons${id} #wallAttachmentMenu`).toggleClass('hidden').fadeTo(250, 1);
} else {
$(`#post-buttons${id} #wallAttachmentMenu`).fadeTo(250, 0, function () {
$(this).toggleClass('hidden');
});
}
}
document.addEventListener("DOMContentLoaded", function() { //BEGIN document.addEventListener("DOMContentLoaded", function() { //BEGIN
$(document).on("click", "#_photoDelete", function(e) { $(document).on("click", "#_photoDelete, #_videoDelete", function(e) {
var formHtml = "<form id='tmpPhDelF' action='" + u(this).attr("href") + "' >"; var formHtml = "<form id='tmpPhDelF' action='" + u(this).attr("href") + "' >";
formHtml += "<input type='hidden' name='hash' value='" + u("meta[name=csrf]").attr("value") + "' />"; formHtml += "<input type='hidden' name='hash' value='" + u("meta[name=csrf]").attr("value") + "' />";
formHtml += "</form>"; formHtml += "</form>";
@ -186,74 +159,6 @@ document.addEventListener("DOMContentLoaded", function() { //BEGIN
}) })
}); //END ONREADY DECLS }); //END ONREADY DECLS
async function repostPost(id, hash) {
uRepostMsgTxt = `
<b>${tr('auditory')}:</b> <br/>
<input type="radio" name="type" onchange="signs.setAttribute('hidden', 'hidden');document.getElementById('groupId').setAttribute('hidden', 'hidden')" value="wall" checked>${tr("in_wall")}<br/>
<input type="radio" name="type" onchange="signs.removeAttribute('hidden');document.getElementById('groupId').removeAttribute('hidden')" value="group" id="group">${tr("in_group")}<br/>
<select style="width:50%;" id="groupId" name="groupId" hidden>
</select><br/>
<b>${tr('your_comment')}:</b>
<textarea id='uRepostMsgInput_${id}'></textarea>
<div id="signs" hidden>
<label><input onchange="signed.checked ? signed.checked = false : null" type="checkbox" id="asgroup" name="asGroup">${tr('post_as_group')}</label><br>
<label><input onchange="asgroup.checked = true" type="checkbox" id="signed" name="signed">${tr('add_signature')}</label>
</div>
<br/><br/>`;
let clubs = [];
repostsCount = document.getElementById("repostsCount"+id)
prevVal = repostsCount != null ? Number(repostsCount.innerHTML) : 0;
MessageBox(tr('share'), uRepostMsgTxt, [tr('send'), tr('cancel')], [
(function() {
text = document.querySelector("#uRepostMsgInput_"+id).value;
type = "user";
radios = document.querySelectorAll('input[name="type"]')
for(const r of radios)
{
if(r.checked)
{
type = r.value;
break;
}
}
groupId = document.querySelector("#groupId").value;
asGroup = asgroup.checked == true ? 1 : 0;
signed = signed.checked == true ? 1 : 0;
hash = encodeURIComponent(hash);
xhr = new XMLHttpRequest();
xhr.open("POST", "/wall"+id+"/repost?hash="+hash, true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.onload = (function() {
if(xhr.responseText.indexOf("wall_owner") === -1)
MessageBox(tr('error'), tr('error_repost_fail'), [tr('ok')], [Function.noop]);
else {
let jsonR = JSON.parse(xhr.responseText);
NewNotification(tr('information_-1'), tr('shared_succ'), null, () => {window.location.href = "/wall" + jsonR.wall_owner});
repostsCount != null ?
repostsCount.innerHTML = prevVal+1 :
document.getElementById("reposts"+id).insertAdjacentHTML("beforeend", "(<b id='repostsCount"+id+"'>1</b>)") //для старого вида постов
}
});
xhr.send('text='+encodeURI(text) + '&type='+type + '&groupId='+groupId + "&asGroup="+asGroup + "&signed="+signed);
}),
Function.noop
]);
try
{
clubs = await API.Groups.getWriteableClubs();
for(const el of clubs) {
document.getElementById("groupId").insertAdjacentHTML("beforeend", `<option value="${el.id}">${escapeHtml(el.name)}</option>`)
}
} catch(rejection) {
console.error(rejection)
document.getElementById("group").setAttribute("disabled", "disabled")
}
}
function setClubAdminComment(clubId, adminId, hash) { function setClubAdminComment(clubId, adminId, hash) {
MessageBox("Изменить комментарий к администратору", ` MessageBox("Изменить комментарий к администратору", `
<form action="/club${clubId}/setAdmin" method="post" id="uClubAdminCommentForm_${clubId}_${adminId}"> <form action="/club${clubId}/setAdmin" method="post" id="uClubAdminCommentForm_${clubId}_${adminId}">
@ -319,17 +224,6 @@ function showCoinsTransferDialog(coinsCount, hash) {
]); ]);
} }
function chunkSubstr(string, size) {
const numChunks = Math.ceil(string.length / size);
const chunks = new Array(numChunks);
for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
chunks[i] = string.substr(o, size);
}
return chunks;
}
function autoTab(original, next, previous) { function autoTab(original, next, previous) {
if(original.getAttribute && original.value.length == original.getAttribute("maxlength") && next !== undefined) if(original.getAttribute && original.value.length == original.getAttribute("maxlength") && next !== undefined)
next.focus(); next.focus();
@ -361,11 +255,6 @@ function supportFastAnswerDialogOnClick(answer) {
answerInput.focus(); answerInput.focus();
} }
function ovk_proc_strtr(string, length = 0) {
const newString = string.substring(0, length);
return newString + (string !== newString ? "…" : "");
}
function showProfileDeactivateDialog(hash) { function showProfileDeactivateDialog(hash) {
MessageBox(tr("profile_deactivate"), ` MessageBox(tr("profile_deactivate"), `
<div class="messagebox-content-header"> <div class="messagebox-content-header">
@ -488,57 +377,6 @@ function showIncreaseRatingDialog(coinsCount, userUrl, hash) {
}; };
} }
function escapeHtml(text) {
var map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
function highlightText(searchText, container_selector, selectors = []) {
const container = u(container_selector)
const regexp = new RegExp(`(${searchText})`, 'gi')
function highlightNode(node) {
if(node.nodeType == 3) {
let newNode = escapeHtml(node.nodeValue)
newNode = newNode.replace(regexp, (match, ...args) => {
return `<span class='highlight'>${escapeHtml(match)}</span>`
})
const tempDiv = document.createElement('div')
tempDiv.innerHTML = newNode
while(tempDiv.firstChild) {
node.parentNode.insertBefore(tempDiv.firstChild, node)
}
node.parentNode.removeChild(node)
} else if(node.nodeType === 1 && node.tagName !== 'SCRIPT' && node.tagName !== 'BR' && node.tagName !== 'STYLE' && !node.classList.contains('highlight')) {
Array.from(node.childNodes).forEach(highlightNode);
}
}
selectors.forEach(selector => {
elements = container.find(selector)
if(!elements || elements.length < 1) return;
elements.nodes.forEach(highlightNode)
})
}
String.prototype.escapeHtml = function() {
try {
return escapeHtml(this)
} catch(e) {
return ''
}
}
$(document).on("scroll", () => { $(document).on("scroll", () => {
if($(document).scrollTop() > $(".sidebar").height() + 50) { if($(document).scrollTop() > $(".sidebar").height() + 50) {
$(".floating_sidebar")[0].classList.add("show"); $(".floating_sidebar")[0].classList.add("show");

File diff suppressed because it is too large Load diff

175
Web/static/js/utils.js Normal file
View file

@ -0,0 +1,175 @@
function escapeHtml(text) {
var map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
function highlightText(searchText, container_selector, selectors = []) {
const container = u(container_selector)
const regexp = new RegExp(`(${searchText})`, 'gi')
function highlightNode(node) {
if(node.nodeType == 3) {
let newNode = escapeHtml(node.nodeValue)
newNode = newNode.replace(regexp, (match, ...args) => {
return `<span class='highlight'>${escapeHtml(match)}</span>`
})
const tempDiv = document.createElement('div')
tempDiv.innerHTML = newNode
while(tempDiv.firstChild) {
node.parentNode.insertBefore(tempDiv.firstChild, node)
}
node.parentNode.removeChild(node)
} else if(node.nodeType === 1 && node.tagName !== 'SCRIPT' && node.tagName !== 'BR' && node.tagName !== 'STYLE' && !node.classList.contains('highlight')) {
Array.from(node.childNodes).forEach(highlightNode);
}
}
selectors.forEach(selector => {
elements = container.find(selector)
if(!elements || elements.length < 1) return;
elements.nodes.forEach(highlightNode)
})
}
String.prototype.escapeHtml = function() {
try {
return escapeHtml(this)
} catch(e) {
return ''
}
}
function fmtTime(time) {
const mins = String(Math.floor(time / 60)).padStart(2, '0');
const secs = String(Math.floor(time % 60)).padStart(2, '0');
return `${ mins}:${ secs}`;
}
function fastError(message) {
MessageBox(tr("error"), message, [tr("ok")], [Function.noop])
}
function humanFileSize(bytes, si) {
var thresh = si ? 1000 : 1024;
if(Math.abs(bytes) < thresh) {
return bytes + ' B';
}
var units = si
? ['kB','MB','GB','TB','PB','EB','ZB','YB']
: ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
var u = -1;
do {
bytes /= thresh;
++u;
} while(Math.abs(bytes) >= thresh && u < units.length - 1);
return bytes.toFixed(1)+' '+units[u];
}
function trim(string) {
var newStr = string.substring(0, 10);
if(newStr.length !== string.length)
newStr += "…";
return newStr;
}
function trimNum(string, num) {
return ovk_proc_strtr(string, num);
}
function ovk_proc_strtr(string, length = 0) {
const newString = string.substring(0, length);
return newString + (string !== newString ? "…" : "");
}
function chunkSubstr(string, size) {
const numChunks = Math.ceil(string.length / size);
const chunks = new Array(numChunks);
for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
chunks[i] = string.substr(o, size);
}
return chunks;
}
function random_int(min, max) {
return Math.round(Math.random() * (max - min) + min)
}
function makeError(text, color = 'Red', timeout = 10000, uid = 0) {
const rand_id = uid != 0 ? uid : random_int(0, 10000)
if(uid != 0 && u(`.upLeftErrors .upLeftError[data-id='${uid}']`).length > 0) {
return
}
u('.upLeftErrors').append(`
<div class='upLeftError upLeftError${color}' data-id='${rand_id}'>${escapeHtml(text)}</div>
`)
setTimeout(() => {
u(`.upLeftError[data-id='${rand_id}']`).remove()
}, timeout)
}
function array_splice(array, key)
{
let resultArray = [];
for(let i = 0; i < array.length; i++){
if(i != key){
resultArray.push(array[i]);
}
}
return resultArray;
}
function strip_tags(text)
{
return text.replace(/(<([^>]+)>)/gi, "")
}
function find_author(id, profiles, groups)
{
if(id > 0) {
const profile = profiles.find(prof => prof.id == id)
if(profile) {
return profile
}
} else {
const group = groups.find(grou => grou.id == Math.abs(id))
if(group) {
return group
}
}
return null
}
function collect_attachments(target) {
const horizontal_array = []
const horizontal_attachments = target.find(`.post-horizontal > a`)
horizontal_attachments.nodes.forEach(_node => {
horizontal_array.push(`${_node.dataset.type}${_node.dataset.id}`)
})
const vertical_array = []
const vertical_attachments = target.find(`.post-vertical > .vertical-attachment`)
vertical_attachments.nodes.forEach(_node => {
vertical_array.push(`${_node.dataset.type}${_node.dataset.id}`)
})
return horizontal_array.concat(vertical_array)
}

View file

@ -1,424 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@atlassian/aui@^9.6.0":
version "9.6.0"
resolved "https://registry.yarnpkg.com/@atlassian/aui/-/aui-9.6.0.tgz#9f35e67359022f1e6d5efa2653e79aeec95b9e77"
integrity sha512-o/bCufj0tUU6pRk3AWoXlcyVMTMx4QswB1UY5oJWSjopA+z/QUx0fhc4rRIIbxP0MrJMNRDpgPyuzkoPb7Z7ow==
dependencies:
"@atlassian/tipsy" "1.3.3"
"@popperjs/core" "2.11.6"
backbone "1.4.1"
css.escape "1.5.1"
dompurify "2.4.5"
fancy-file-input "2.0.4"
jquery-ui "1.13.2"
skatejs "0.13.17"
skatejs-template-html "0.0.0"
trim-extra-html-whitespace "1.3.0"
underscore "1.13.6"
"@atlassian/tipsy@1.3.3":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@atlassian/tipsy/-/tipsy-1.3.3.tgz#3f77754c4c70324c5c938e41abaa2ca682f22036"
integrity sha512-6jd9wdoiPdCbwsNi1Xrn/oMdGz22dKPeCoZ/cCGKqjnh+UYkBKb5W3spW+WNqRSxGvVtfUEEg6TXotRK/FPDaw==
"@popperjs/core@2.11.6", "@popperjs/core@^2.9.0":
version "2.11.6"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
asap@~2.0.3:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
backbone@1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/backbone/-/backbone-1.4.1.tgz#099a78184bc07b034048a8332229c2ccca1e3e62"
integrity sha512-ADy1ztN074YkWbHi8ojJVFe3vAanO/lrzMGZWUClIP7oDD/Pjy2vrASraUP+2EVCfIiTtCW4FChVow01XneivA==
dependencies:
underscore ">=1.8.3"
codem-isoboxer@0.3.6:
version "0.3.6"
resolved "https://registry.yarnpkg.com/codem-isoboxer/-/codem-isoboxer-0.3.6.tgz#867f670459b881d44f39168d5ff2a8f14c16151d"
integrity sha512-LuO8/7LW6XuR5ERn1yavXAfodGRhuY2yP60JTZIw5yNYMCE5lUVbk3NFUCJxjnphQH+Xemp5hOGb1LgUXm00Xw==
core-js@^1.0.0:
version "1.2.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
create-react-class@^15.7.0:
version "15.7.0"
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.7.0.tgz#7499d7ca2e69bb51d13faf59bd04f0c65a1d6c1e"
integrity sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng==
dependencies:
loose-envify "^1.3.1"
object-assign "^4.1.1"
cropperjs@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/cropperjs/-/cropperjs-1.6.1.tgz#fd132021d93b824b1b0f2c2c3b763419fb792d89"
integrity sha512-F4wsi+XkDHCOMrHMYjrTEE4QBOrsHHN5/2VsVAaRq8P7E5z7xQpT75S+f/9WikmBEailas3+yo+6zPIomW+NOA==
css.escape@1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==
dashjs@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/dashjs/-/dashjs-4.3.0.tgz#cccda5a490cabf6c3b48aa887ec8c8ac0df1a233"
integrity sha512-cqpnJaPQpEY4DsEdF9prwD00+5dp5EGHCFc7yo9n2uuAH9k4zPkZJwXQ8dXmVRhPf3M89JfKSoAYIP3dbXmqcg==
dependencies:
codem-isoboxer "0.3.6"
es6-promise "^4.2.8"
fast-deep-equal "2.0.1"
html-entities "^1.2.1"
imsc "^1.0.2"
localforage "^1.7.1"
dompurify@2.4.5:
version "2.4.5"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.5.tgz#0e89a27601f0bad978f9a924e7a05d5d2cccdd87"
integrity sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==
encoding@^0.1.11:
version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
dependencies:
iconv-lite "^0.6.2"
es6-promise@^4.2.8:
version "4.2.8"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
event-lite@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/event-lite/-/event-lite-0.1.2.tgz#838a3e0fdddef8cc90f128006c8e55a4e4e4c11b"
integrity sha512-HnSYx1BsJ87/p6swwzv+2v6B4X+uxUteoDfRxsAb1S1BePzQqOLevVmkdA15GHJVd9A9Ok6wygUR18Hu0YeV9g==
fancy-file-input@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/fancy-file-input/-/fancy-file-input-2.0.4.tgz#698c216482e07649a827681c4db3054fddc9a32b"
integrity sha512-l+J0WwDl4nM/zMJ/C8qleYnXMUJKsLng7c5uWH/miAiHoTvPDtEoLW1tmVO6Cy2O8i/1VfA+2YOwg/Q3+kgO6w==
fast-deep-equal@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
fbjs@^0.8.0:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
dependencies:
core-js "^1.0.0"
isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
handlebars@^4.7.7:
version "4.7.7"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==
dependencies:
minimist "^1.2.5"
neo-async "^2.6.0"
source-map "^0.6.1"
wordwrap "^1.0.0"
optionalDependencies:
uglify-js "^3.1.4"
html-entities@^1.2.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc"
integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==
iconv-lite@^0.6.2:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
id3js@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/id3js/-/id3js-2.1.1.tgz#0c307d0d2f194bc5fa7a809bbed0b1a93577f16d"
integrity sha512-9Gi+sG0RHSa5qn8hkwi2KCl+2jV8YrtiZidXbOO3uLfRAxc2jilRg0fiQ3CbeoAmR7G7ap3RVs1kqUVhIyZaog==
ieee754@^1.1.8:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
immediate@~3.0.5:
version "3.0.6"
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
imsc@^1.0.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/imsc/-/imsc-1.1.3.tgz#e96a60a50d4000dd7b44097272768b9fd6a4891d"
integrity sha512-IY0hMkVTNoqoYwKEp5UvNNKp/A5jeJUOrIO7judgOyhHT+xC6PA4VBOMAOhdtAYbMRHx9DTgI8p6Z6jhYQPFDA==
dependencies:
sax "1.2.1"
int64-buffer@^0.1.9:
version "0.1.10"
resolved "https://registry.yarnpkg.com/int64-buffer/-/int64-buffer-0.1.10.tgz#277b228a87d95ad777d07c13832022406a473423"
integrity sha1-J3siiofZWtd30HwTgyAiQGpHNCM=
is-stream@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
isarray@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
isomorphic-fetch@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=
dependencies:
node-fetch "^1.0.1"
whatwg-fetch ">=0.10.0"
jquery-ui@1.13.2:
version "1.13.2"
resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.13.2.tgz#de03580ae6604773602f8d786ad1abfb75232034"
integrity sha512-wBZPnqWs5GaYJmo1Jj0k/mrSkzdQzKDwhXNtHKcBdAcKVxMM3KNYFq+iJ2i1rwiG53Z8M4mTn3Qxrm17uH1D4Q==
dependencies:
jquery ">=1.8.0 <4.0.0"
"jquery@>=1.8.0 <4.0.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470"
integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==
jquery@^3.0.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.0.tgz#fe2c01a05da500709006d8790fe21c8a39d75612"
integrity sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ==
"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
knockout@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/knockout/-/knockout-3.5.1.tgz#62c81e81843bea2008fd23c575edd9ca978e75cf"
integrity sha512-wRJ9I4az0QcsH7A4v4l0enUpkS++MBx0BnL/68KaLzJg7x1qmbjSlwEoCNol7KTYZ+pmtI7Eh2J0Nu6/2Z5J/Q==
ky@^0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/ky/-/ky-0.19.0.tgz#d6ad117e89efe2d85a1c2e91462d48ca1cda1f7a"
integrity sha512-RkDgbg5ahMv1MjHfJI2WJA2+Qbxq0iNSLWhreYiCHeHry9Q12sedCnP5KYGPt7sydDvsyH+8UcG6Kanq5mpsyw==
lie@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=
dependencies:
immediate "~3.0.5"
literallycanvas@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/literallycanvas/-/literallycanvas-0.5.2.tgz#7d4800a8d9c4b38a593e91695d52466689586abd"
integrity sha512-SPxZ0DfzUWOXBPnsFD/Y1OulGKpv33IKGB+DCoUkNKQsc38kRDy1mGjbWbs/JAAyIGM1C9Y0CYI23SQmtXR5aw==
dependencies:
react-addons-pure-render-mixin "^15.1"
localforage@^1.7.1:
version "1.10.0"
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4"
integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==
dependencies:
lie "3.1.1"
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
minimist@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
monaco-editor@^0.20.0:
version "0.20.0"
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.20.0.tgz#5d5009343a550124426cb4d965a4d27a348b4dea"
integrity sha512-hkvf4EtPJRMQlPC3UbMoRs0vTAFAYdzFQ+gpMb8A+9znae1c43q8Mab9iVsgTcg/4PNiLGGn3SlDIa8uvK1FIQ==
msgpack-lite@^0.1.26:
version "0.1.26"
resolved "https://registry.yarnpkg.com/msgpack-lite/-/msgpack-lite-0.1.26.tgz#dd3c50b26f059f25e7edee3644418358e2a9ad89"
integrity sha1-3TxQsm8FnyXn7e42REGDWOKprYk=
dependencies:
event-lite "^0.1.1"
ieee754 "^1.1.8"
int64-buffer "^0.1.9"
isarray "^1.0.0"
neo-async@^2.6.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
node-fetch@^1.0.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
dependencies:
encoding "^0.1.11"
is-stream "^1.0.1"
object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
plotly.js-dist@^1.52.3:
version "1.52.3"
resolved "https://registry.yarnpkg.com/plotly.js-dist/-/plotly.js-dist-1.52.3.tgz#4c16c6da6adab6cdba169087b5005bdddbf10834"
integrity sha512-kpuNwveRk6M/5cCW1ZgJTbMLD0bRZhJlLK0cUHVkTsP/PWKCVJqO3QiiqrypFGE/xEhWfHCY+YKAKjMmEbo4Gw==
promise@^7.1.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
dependencies:
asap "~2.0.3"
react-addons-pure-render-mixin@^15.1:
version "15.6.3"
resolved "https://registry.yarnpkg.com/react-addons-pure-render-mixin/-/react-addons-pure-render-mixin-15.6.3.tgz#5dc73af0fa32186dbc4887f20667b46d3f9caed7"
integrity sha512-e7F2OsLiyYGr9SHWHGlI/FfHRh+kbYx0hNfdN5zivHIf4vzeno7gsRJKXg71E35CpUCnre+JfM6UgWWgsvJBzA==
dependencies:
object-assign "^4.1.0"
react-dom-factories@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/react-dom-factories/-/react-dom-factories-1.0.2.tgz#eb7705c4db36fb501b3aa38ff759616aa0ff96e0"
integrity sha1-63cFxNs2+1AbOqOP91lhaqD/luA=
react-dom@15.1:
version "15.1.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.1.0.tgz#d0c2b24c8b47a41a2b9ec766662d4e686f353153"
integrity sha1-0MKyTItHpBornsdmZi1OaG81MVM=
react@15.1:
version "15.1.0"
resolved "https://registry.yarnpkg.com/react/-/react-15.1.0.tgz#5f7a9f085a00509898efd2b24cb12ea1dfaf8b40"
integrity sha1-X3qfCFoAUJiY79KyTLEuod+vi0A=
dependencies:
fbjs "^0.8.0"
loose-envify "^1.1.0"
object-assign "^4.1.0"
requirejs@^2.3.6:
version "2.3.6"
resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.6.tgz#e5093d9601c2829251258c0b9445d4d19fa9e7c9"
integrity sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==
"safer-buffer@>= 2.1.2 < 3.0.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sax@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o=
setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
skatejs-template-html@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/skatejs-template-html/-/skatejs-template-html-0.0.0.tgz#e990c1a7d4b58b7305ffcc3338939bf402023df7"
integrity sha1-6ZDBp9S1i3MF/8wzOJOb9AICPfc=
skatejs@0.13.17:
version "0.13.17"
resolved "https://registry.yarnpkg.com/skatejs/-/skatejs-0.13.17.tgz#7a21fbb3434da45e52b47b61647168ee9e778071"
integrity sha1-eiH7s0NNpF5StHthZHFo7p53gHE=
soundjs@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/soundjs/-/soundjs-1.0.1.tgz#99970542d28d0df2a1ebd061ae75c961a98c8180"
integrity sha512-MgFPvmKYfpcNiE3X5XybNvScie3DMQlZgmNzUn4puBcpw64f4LqjH/fhM8Sb/eTJ8hK57Crr7mWy0bfJOqPj6Q==
source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
textfit@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/textfit/-/textfit-2.4.0.tgz#80cba8006bfb9c3d9d552739257957bdda95c79c"
integrity sha512-/x4aoY5+/tJmu+iwpBH1yw75TFp86M6X15SvaaY/Eep7YySQYtqdOifEtfvVyMwzl7SZ+G4RQw00FD9g5R6i1Q==
tippy.js@^6.3.7:
version "6.3.7"
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c"
integrity sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==
dependencies:
"@popperjs/core" "^2.9.0"
trim-extra-html-whitespace@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/trim-extra-html-whitespace/-/trim-extra-html-whitespace-1.3.0.tgz#b47efb0d1a5f2a56a85cc45cea525651e93404cf"
integrity sha1-tH77DRpfKlaoXMRc6lJWUek0BM8=
ua-parser-js@^0.7.18:
version "0.7.33"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532"
integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==
uglify-js@^3.1.4:
version "3.17.3"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.3.tgz#f0feedf019c4510f164099e8d7e72ff2d7304377"
integrity sha512-JmMFDME3iufZnBpyKL+uS78LRiC+mK55zWfM5f/pWBJfpOttXAqYfdDGRukYhJuyRinvPVAtUhvy7rlDybNtFg==
umbrellajs@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/umbrellajs/-/umbrellajs-3.1.0.tgz#a4e6f0f6381f9d93110b5eee962e0e0864b10bd0"
integrity sha512-3qichMg1Q6EetLweBAT0L55O2W6CJe9qyiSt1RBnf+bcOqwJ4R7e2PDcoIUrCsg+uRo3DXOvurWdklBu0ia7fg==
underscore@1.13.6:
version "1.13.6"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441"
integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==
underscore@>=1.8.3:
version "1.13.1"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1"
integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==
whatwg-fetch@>=0.10.0:
version "3.6.2"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c"
integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==
wordwrap@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==

View file

@ -232,49 +232,104 @@ function ovk_is_ssl(): bool
return $GLOBALS["requestIsSSL"]; return $GLOBALS["requestIsSSL"];
} }
function parseAttachments(string $attachments): array function parseAttachments($attachments, array $allow_types = ['photo', 'video', 'note', 'audio']): array
{ {
$attachmentsArr = explode(",", $attachments); $exploded_attachments = is_array($attachments) ? $attachments : explode(",", $attachments);
$returnArr = []; $exploded_attachments = array_slice($exploded_attachments, 0, OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["maxAttachments"] ?? 10);
$exploded_attachments = array_unique($exploded_attachments);
$imploded_types = implode('|', $allow_types);
$output_attachments = [];
$repositories = [
'photo' => [
'repo' => 'openvk\Web\Models\Repositories\Photos',
'method' => 'getByOwnerAndVID',
],
'video' => [
'repo' => 'openvk\Web\Models\Repositories\Videos',
'method' => 'getByOwnerAndVID',
],
'audio' => [
'repo' => 'openvk\Web\Models\Repositories\Audios',
'method' => 'getByOwnerAndVID',
],
'note' => [
'repo' => 'openvk\Web\Models\Repositories\Notes',
'method' => 'getNoteById',
],
'poll' => [
'repo' => 'openvk\Web\Models\Repositories\Polls',
'method' => 'get',
'onlyId' => true,
],
];
foreach($attachmentsArr as $attachment) { foreach($exploded_attachments as $attachment_string) {
$attachmentType = NULL; if(preg_match("/$imploded_types/", $attachment_string, $matches) == 1) {
try {
if(str_contains($attachment, "photo")) $attachment_type = $matches[0];
$attachmentType = "photo"; if(!$repositories[$attachment_type])
elseif(str_contains($attachment, "video")) continue;
$attachmentType = "video";
elseif(str_contains($attachment, "note")) $attachment_ids = str_replace($attachment_type, '', $attachment_string);
$attachmentType = "note"; if($repositories[$attachment_type]['onlyId']) {
elseif(str_contains($attachment, "audio")) [$attachment_id] = array_map('intval', explode('_', $attachment_ids));
$attachmentType = "audio";
$repository_class = $repositories[$attachment_type]['repo'];
$attachmentIds = str_replace($attachmentType, "", $attachment); if(!$repository_class) continue;
$attachmentOwner = (int) explode("_", $attachmentIds)[0]; $attachment_model = (new $repository_class)->{$repositories[$attachment_type]['method']}($attachment_id);
$gatoExplotano = explode("_", $attachmentIds); $output_attachments[] = $attachment_model;
$attachmentId = (int) end($gatoExplotano); } else {
[$attachment_owner, $attachment_id] = array_map('intval', explode('_', $attachment_ids));
switch($attachmentType) {
case "photo": $repository_class = $repositories[$attachment_type]['repo'];
$attachmentObj = (new openvk\Web\Models\Repositories\Photos)->getByOwnerAndVID($attachmentOwner, $attachmentId); if(!$repository_class) continue;
$returnArr[] = $attachmentObj; $attachment_model = (new $repository_class)->{$repositories[$attachment_type]['method']}($attachment_owner, $attachment_id);
break; $output_attachments[] = $attachment_model;
case "video": }
$attachmentObj = (new openvk\Web\Models\Repositories\Videos)->getByOwnerAndVID($attachmentOwner, $attachmentId); } catch(\Throwable) {continue;}
$returnArr[] = $attachmentObj;
break;
case "note":
$attachmentObj = (new openvk\Web\Models\Repositories\Notes)->getNoteById($attachmentOwner, $attachmentId);
$returnArr[] = $attachmentObj;
break;
case "audio":
$attachmentObj = (new openvk\Web\Models\Repositories\Audios)->getByOwnerAndVID($attachmentOwner, $attachmentId);
$returnArr[] = $attachmentObj;
break;
} }
} }
return $returnArr; return $output_attachments;
}
function get_entity_by_id(int $id)
{
if($id > 0)
return (new openvk\Web\Models\Repositories\Users)->get($id);
return (new openvk\Web\Models\Repositories\Clubs)->get(abs($id));
}
function get_entities(array $ids = []): array
{
$main_result = [];
$users = [];
$clubs = [];
foreach($ids as $id) {
$id = (int)$id;
if($id < 0)
$clubs[] = abs($id);
if($id > 0)
$users[] = $id;
}
if(sizeof($users) > 0) {
$users_tmp = (new openvk\Web\Models\Repositories\Users)->getByIds($users);
foreach($users_tmp as $user) {
$main_result[] = $user;
}
}
if(sizeof($clubs) > 0) {
$clubs_tmp = (new openvk\Web\Models\Repositories\Clubs)->getByIds($clubs);
foreach($clubs_tmp as $club) {
$main_result[] = $club;
}
}
return $main_result;
} }
function ovk_scheme(bool $with_slashes = false): string function ovk_scheme(bool $with_slashes = false): string

373
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "0d3b0eb1916eaf088444ef637743284a", "content-hash": "987e26c5520b71fccd0cd31de00eead2",
"packages": [ "packages": [
{ {
"name": "al/emoji-detector", "name": "al/emoji-detector",
@ -197,16 +197,16 @@
}, },
{ {
"name": "chillerlan/php-settings-container", "name": "chillerlan/php-settings-container",
"version": "3.2.0", "version": "3.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/chillerlan/php-settings-container.git", "url": "https://github.com/chillerlan/php-settings-container.git",
"reference": "8f93648fac8e6bacac8e00a8d325eba4950295e6" "reference": "95ed3e9676a1d47cab2e3174d19b43f5dbf52681"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/8f93648fac8e6bacac8e00a8d325eba4950295e6", "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/95ed3e9676a1d47cab2e3174d19b43f5dbf52681",
"reference": "8f93648fac8e6bacac8e00a8d325eba4950295e6", "reference": "95ed3e9676a1d47cab2e3174d19b43f5dbf52681",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -214,15 +214,16 @@
"php": "^8.1" "php": "^8.1"
}, },
"require-dev": { "require-dev": {
"phan/phan": "^5.4",
"phpmd/phpmd": "^2.15", "phpmd/phpmd": "^2.15",
"phpstan/phpstan": "^1.11",
"phpstan/phpstan-deprecation-rules": "^1.2",
"phpunit/phpunit": "^10.5", "phpunit/phpunit": "^10.5",
"squizlabs/php_codesniffer": "^3.9" "squizlabs/php_codesniffer": "^3.10"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"chillerlan\\Settings\\": "src/" "chillerlan\\Settings\\": "src"
} }
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
@ -258,7 +259,7 @@
"type": "ko_fi" "type": "ko_fi"
} }
], ],
"time": "2024-03-02T20:07:15+00:00" "time": "2024-07-16T11:13:48+00:00"
}, },
{ {
"name": "erusev/parsedown", "name": "erusev/parsedown",
@ -266,20 +267,20 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/erusev/parsedown.git", "url": "https://github.com/erusev/parsedown.git",
"reference": "1ff038273949df7d6a455352659a878f3c89b29c" "reference": "582f9f9cd12a894a0901bef1652854a3c5a1d874"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/erusev/parsedown/zipball/1ff038273949df7d6a455352659a878f3c89b29c", "url": "https://api.github.com/repos/erusev/parsedown/zipball/582f9f9cd12a894a0901bef1652854a3c5a1d874",
"reference": "1ff038273949df7d6a455352659a878f3c89b29c", "reference": "582f9f9cd12a894a0901bef1652854a3c5a1d874",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-mbstring": "*", "ext-mbstring": "*",
"php": ">=5.3.0" "php": ">=7.1"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^4.8.35" "phpunit/phpunit": "^7.5|^8.5|^9.6"
}, },
"default-branch": true, "default-branch": true,
"type": "library", "type": "library",
@ -309,7 +310,7 @@
"issues": "https://github.com/erusev/parsedown/issues", "issues": "https://github.com/erusev/parsedown/issues",
"source": "https://github.com/erusev/parsedown/tree/master" "source": "https://github.com/erusev/parsedown/tree/master"
}, },
"time": "2024-03-12T05:27:45+00:00" "time": "2024-10-12T07:06:08+00:00"
}, },
{ {
"name": "ezyang/htmlpurifier", "name": "ezyang/htmlpurifier",
@ -317,12 +318,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git", "url": "https://github.com/ezyang/htmlpurifier.git",
"reference": "4828fdf45a93eeeacfcbcc855f96f9a7e6b4ed44" "reference": "cb56001e54359df7ae76dc522d08845dc741621b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/4828fdf45a93eeeacfcbcc855f96f9a7e6b4ed44", "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/cb56001e54359df7ae76dc522d08845dc741621b",
"reference": "4828fdf45a93eeeacfcbcc855f96f9a7e6b4ed44", "reference": "cb56001e54359df7ae76dc522d08845dc741621b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -369,9 +370,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/ezyang/htmlpurifier/issues", "issues": "https://github.com/ezyang/htmlpurifier/issues",
"source": "https://github.com/ezyang/htmlpurifier/tree/master" "source": "https://github.com/ezyang/htmlpurifier/tree/v4.18.0"
}, },
"time": "2024-03-13T03:41:45+00:00" "time": "2024-11-01T03:51:45+00:00"
}, },
{ {
"name": "guzzlehttp/guzzle", "name": "guzzlehttp/guzzle",
@ -678,12 +679,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/JamesHeinrich/getID3.git", "url": "https://github.com/JamesHeinrich/getID3.git",
"reference": "143af3325ee40e77c5d041d3f674c8bdd4762dc7" "reference": "eb358a0d4943c0cc23e783f1a72141f46153c13a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/JamesHeinrich/getID3/zipball/143af3325ee40e77c5d041d3f674c8bdd4762dc7", "url": "https://api.github.com/repos/JamesHeinrich/getID3/zipball/eb358a0d4943c0cc23e783f1a72141f46153c13a",
"reference": "143af3325ee40e77c5d041d3f674c8bdd4762dc7", "reference": "eb358a0d4943c0cc23e783f1a72141f46153c13a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -738,7 +739,7 @@
"issues": "https://github.com/JamesHeinrich/getID3/issues", "issues": "https://github.com/JamesHeinrich/getID3/issues",
"source": "https://github.com/JamesHeinrich/getID3/tree/master" "source": "https://github.com/JamesHeinrich/getID3/tree/master"
}, },
"time": "2024-03-26T12:26:30+00:00" "time": "2024-09-20T19:45:51+00:00"
}, },
{ {
"name": "league/uri", "name": "league/uri",
@ -1099,20 +1100,20 @@
}, },
{ {
"name": "psr/http-factory", "name": "psr/http-factory",
"version": "1.0.2", "version": "1.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/php-fig/http-factory.git", "url": "https://github.com/php-fig/http-factory.git",
"reference": "e616d01114759c4c489f93b099585439f795fe35" "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"reference": "e616d01114759c4c489f93b099585439f795fe35", "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.0.0", "php": ">=7.1",
"psr/http-message": "^1.0 || ^2.0" "psr/http-message": "^1.0 || ^2.0"
}, },
"type": "library", "type": "library",
@ -1136,7 +1137,7 @@
"homepage": "https://www.php-fig.org/" "homepage": "https://www.php-fig.org/"
} }
], ],
"description": "Common interfaces for PSR-7 HTTP message factories", "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
"keywords": [ "keywords": [
"factory", "factory",
"http", "http",
@ -1148,9 +1149,9 @@
"response" "response"
], ],
"support": { "support": {
"source": "https://github.com/php-fig/http-factory/tree/1.0.2" "source": "https://github.com/php-fig/http-factory"
}, },
"time": "2023-04-10T20:10:41+00:00" "time": "2024-04-15T12:06:14+00:00"
}, },
{ {
"name": "psr/http-message", "name": "psr/http-message",
@ -1318,21 +1319,22 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/scssphp/scssphp.git", "url": "https://github.com/scssphp/scssphp.git",
"reference": "5659341b5da400dafd5672171215f4534c06236b" "reference": "12d3cdf0fc2a73e9438ded5a5a82fb353b68d718"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/5659341b5da400dafd5672171215f4534c06236b", "url": "https://api.github.com/repos/scssphp/scssphp/zipball/12d3cdf0fc2a73e9438ded5a5a82fb353b68d718",
"reference": "5659341b5da400dafd5672171215f4534c06236b", "reference": "12d3cdf0fc2a73e9438ded5a5a82fb353b68d718",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-ctype": "*", "ext-ctype": "*",
"ext-json": "*", "ext-json": "*",
"league/uri": "^7.4@dev", "league/uri": "^7.4",
"league/uri-interfaces": "^7.3", "league/uri-interfaces": "^7.4",
"php": ">=8.1", "php": ">=8.1",
"symfony/filesystem": "^5.4 || ^6.0 || ^7.0" "symfony/filesystem": "^5.4 || ^6.0 || ^7.0",
"symfony/polyfill-mbstring": "^1.30"
}, },
"require-dev": { "require-dev": {
"bamarni/composer-bin-plugin": "^1.4", "bamarni/composer-bin-plugin": "^1.4",
@ -1347,8 +1349,7 @@
"zurb/foundation": "~6.7.0" "zurb/foundation": "~6.7.0"
}, },
"suggest": { "suggest": {
"ext-iconv": "Can be used as fallback when ext-mbstring is not available", "ext-mbstring": "For best performance, mbstring should be installed as it is faster than the polyfill"
"ext-mbstring": "For best performance, mbstring should be installed as it is faster than ext-iconv"
}, },
"default-branch": true, "default-branch": true,
"type": "library", "type": "library",
@ -1392,7 +1393,7 @@
"issues": "https://github.com/scssphp/scssphp/issues", "issues": "https://github.com/scssphp/scssphp/issues",
"source": "https://github.com/scssphp/scssphp/tree/main" "source": "https://github.com/scssphp/scssphp/tree/main"
}, },
"time": "2024-01-13T13:17:46+00:00" "time": "2024-10-24T15:03:30+00:00"
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
@ -1400,12 +1401,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/console.git", "url": "https://github.com/symfony/console.git",
"reference": "39f75d9d73d0c11952fdcecf4877b4d0f62a8f6e" "reference": "108d436c2af470858bdaba3257baab3a74172017"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/39f75d9d73d0c11952fdcecf4877b4d0f62a8f6e", "url": "https://api.github.com/repos/symfony/console/zipball/108d436c2af470858bdaba3257baab3a74172017",
"reference": "39f75d9d73d0c11952fdcecf4877b4d0f62a8f6e", "reference": "108d436c2af470858bdaba3257baab3a74172017",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1491,20 +1492,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-02-20T16:33:57+00:00" "time": "2024-10-08T07:27:17+00:00"
}, },
{ {
"name": "symfony/deprecation-contracts", "name": "symfony/deprecation-contracts",
"version": "v3.4.0", "version": "v3.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git", "url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1513,7 +1514,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "3.4-dev" "dev-main": "3.5-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/contracts", "name": "symfony/contracts",
@ -1542,7 +1543,7 @@
"description": "A generic function and convention to trigger deprecation notices", "description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
}, },
"funding": [ "funding": [
{ {
@ -1558,27 +1559,30 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-05-23T14:45:45+00:00" "time": "2024-04-18T09:32:20+00:00"
}, },
{ {
"name": "symfony/filesystem", "name": "symfony/filesystem",
"version": "v6.4.3", "version": "v7.2.0-BETA1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/filesystem.git", "url": "https://github.com/symfony/filesystem.git",
"reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb" "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb", "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb",
"reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb", "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.1", "php": ">=8.2",
"symfony/polyfill-ctype": "~1.8", "symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.8" "symfony/polyfill-mbstring": "~1.8"
}, },
"require-dev": {
"symfony/process": "^6.4|^7.0"
},
"type": "library", "type": "library",
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@ -1605,7 +1609,7 @@
"description": "Provides basic utilities for the filesystem", "description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/filesystem/tree/v6.4.3" "source": "https://github.com/symfony/filesystem/tree/v7.2.0-BETA1"
}, },
"funding": [ "funding": [
{ {
@ -1621,24 +1625,24 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-01-23T14:51:35+00:00" "time": "2024-10-25T15:15:23+00:00"
}, },
{ {
"name": "symfony/polyfill-ctype", "name": "symfony/polyfill-ctype",
"version": "v1.29.0", "version": "v1.31.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git", "url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.1" "php": ">=7.2"
}, },
"provide": { "provide": {
"ext-ctype": "*" "ext-ctype": "*"
@ -1684,7 +1688,7 @@
"portable" "portable"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
}, },
"funding": [ "funding": [
{ {
@ -1700,24 +1704,24 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-01-29T20:11:03+00:00" "time": "2024-09-09T11:45:10+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-grapheme", "name": "symfony/polyfill-intl-grapheme",
"version": "v1.29.0", "version": "v1.31.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
"reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.1" "php": ">=7.2"
}, },
"suggest": { "suggest": {
"ext-intl": "For best performance" "ext-intl": "For best performance"
@ -1762,7 +1766,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0"
}, },
"funding": [ "funding": [
{ {
@ -1778,26 +1782,25 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-01-29T20:11:03+00:00" "time": "2024-09-09T11:45:10+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-idn", "name": "symfony/polyfill-intl-idn",
"version": "v1.29.0", "version": "v1.31.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git", "url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "a287ed7475f85bf6f61890146edbc932c0fff919" "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a287ed7475f85bf6f61890146edbc932c0fff919", "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773",
"reference": "a287ed7475f85bf6f61890146edbc932c0fff919", "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.1", "php": ">=7.2",
"symfony/polyfill-intl-normalizer": "^1.10", "symfony/polyfill-intl-normalizer": "^1.10"
"symfony/polyfill-php72": "^1.10"
}, },
"suggest": { "suggest": {
"ext-intl": "For best performance" "ext-intl": "For best performance"
@ -1846,7 +1849,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.29.0" "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0"
}, },
"funding": [ "funding": [
{ {
@ -1862,24 +1865,24 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-01-29T20:11:03+00:00" "time": "2024-09-09T11:45:10+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-normalizer", "name": "symfony/polyfill-intl-normalizer",
"version": "v1.29.0", "version": "v1.31.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "bc45c394692b948b4d383a08d7753968bed9a83d" "reference": "3833d7255cc303546435cb650316bff708a1c75c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
"reference": "bc45c394692b948b4d383a08d7753968bed9a83d", "reference": "3833d7255cc303546435cb650316bff708a1c75c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.1" "php": ">=7.2"
}, },
"suggest": { "suggest": {
"ext-intl": "For best performance" "ext-intl": "For best performance"
@ -1927,7 +1930,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0"
}, },
"funding": [ "funding": [
{ {
@ -1943,24 +1946,24 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-01-29T20:11:03+00:00" "time": "2024-09-09T11:45:10+00:00"
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.29.0", "version": "v1.31.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git", "url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.1" "php": ">=7.2"
}, },
"provide": { "provide": {
"ext-mbstring": "*" "ext-mbstring": "*"
@ -2007,7 +2010,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
}, },
"funding": [ "funding": [
{ {
@ -2023,97 +2026,24 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-01-29T20:11:03+00:00" "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php72",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/861391a8da9a04cbad2d232ddd9e4893220d6e25",
"reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php72/tree/v1.29.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-01-29T20:11:03+00:00"
}, },
{ {
"name": "symfony/polyfill-php73", "name": "symfony/polyfill-php73",
"version": "v1.29.0", "version": "v1.31.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php73.git", "url": "https://github.com/symfony/polyfill-php73.git",
"reference": "21bd091060673a1177ae842c0ef8fe30893114d2" "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/21bd091060673a1177ae842c0ef8fe30893114d2", "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb",
"reference": "21bd091060673a1177ae842c0ef8fe30893114d2", "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.1" "php": ">=7.2"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@ -2156,7 +2086,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php73/tree/v1.29.0" "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0"
}, },
"funding": [ "funding": [
{ {
@ -2172,24 +2102,24 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-01-29T20:11:03+00:00" "time": "2024-09-09T11:45:10+00:00"
}, },
{ {
"name": "symfony/polyfill-php80", "name": "symfony/polyfill-php80",
"version": "v1.29.0", "version": "v1.31.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php80.git", "url": "https://github.com/symfony/polyfill-php80.git",
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.1" "php": ">=7.2"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@ -2236,7 +2166,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0"
}, },
"funding": [ "funding": [
{ {
@ -2252,25 +2182,26 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-01-29T20:11:03+00:00" "time": "2024-09-09T11:45:10+00:00"
}, },
{ {
"name": "symfony/service-contracts", "name": "symfony/service-contracts",
"version": "v3.4.1", "version": "v3.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/service-contracts.git", "url": "https://github.com/symfony/service-contracts.git",
"reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f",
"reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.1", "php": ">=8.1",
"psr/container": "^1.1|^2.0" "psr/container": "^1.1|^2.0",
"symfony/deprecation-contracts": "^2.5|^3"
}, },
"conflict": { "conflict": {
"ext-psr": "<1.1|>=2" "ext-psr": "<1.1|>=2"
@ -2278,7 +2209,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "3.4-dev" "dev-main": "3.5-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/contracts", "name": "symfony/contracts",
@ -2318,7 +2249,7 @@
"standards" "standards"
], ],
"support": { "support": {
"source": "https://github.com/symfony/service-contracts/tree/v3.4.1" "source": "https://github.com/symfony/service-contracts/tree/v3.5.0"
}, },
"funding": [ "funding": [
{ {
@ -2334,20 +2265,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-12-26T14:02:43+00:00" "time": "2024-04-18T09:32:20+00:00"
}, },
{ {
"name": "symfony/string", "name": "symfony/string",
"version": "v6.4.4", "version": "v6.4.13",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/string.git", "url": "https://github.com/symfony/string.git",
"reference": "4e465a95bdc32f49cf4c7f07f751b843bbd6dcd9" "reference": "38371c60c71c72b3d64d8d76f6b1bb81a2cc3627"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/4e465a95bdc32f49cf4c7f07f751b843bbd6dcd9", "url": "https://api.github.com/repos/symfony/string/zipball/38371c60c71c72b3d64d8d76f6b1bb81a2cc3627",
"reference": "4e465a95bdc32f49cf4c7f07f751b843bbd6dcd9", "reference": "38371c60c71c72b3d64d8d76f6b1bb81a2cc3627",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2404,7 +2335,7 @@
"utf8" "utf8"
], ],
"support": { "support": {
"source": "https://github.com/symfony/string/tree/v6.4.4" "source": "https://github.com/symfony/string/tree/v6.4.13"
}, },
"funding": [ "funding": [
{ {
@ -2420,7 +2351,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-02-01T13:16:41+00:00" "time": "2024-09-25T14:18:03+00:00"
}, },
{ {
"name": "vearutop/php-obscene-censor-rus", "name": "vearutop/php-obscene-censor-rus",
@ -2525,12 +2456,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/wapmorgan/Morphos.git", "url": "https://github.com/wapmorgan/Morphos.git",
"reference": "302b56636cf604ad07e20020076d8a4880554286" "reference": "d22876709756b538a52b30a50a3f37cd89efeb91"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/wapmorgan/Morphos/zipball/302b56636cf604ad07e20020076d8a4880554286", "url": "https://api.github.com/repos/wapmorgan/Morphos/zipball/d22876709756b538a52b30a50a3f37cd89efeb91",
"reference": "302b56636cf604ad07e20020076d8a4880554286", "reference": "d22876709756b538a52b30a50a3f37cd89efeb91",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2592,7 +2523,7 @@
"issues": "https://github.com/wapmorgan/Morphos/issues", "issues": "https://github.com/wapmorgan/Morphos/issues",
"source": "https://github.com/wapmorgan/Morphos/tree/master" "source": "https://github.com/wapmorgan/Morphos/tree/master"
}, },
"time": "2024-03-26T07:24:27+00:00" "time": "2024-09-26T12:41:09+00:00"
}, },
{ {
"name": "whichbrowser/parser", "name": "whichbrowser/parser",
@ -2600,12 +2531,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/WhichBrowser/Parser-PHP.git", "url": "https://github.com/WhichBrowser/Parser-PHP.git",
"reference": "1044880bc792dbce5948fbff22ae731c43c280d9" "reference": "581d614d686bfbec3529ad60562a5213ac5d8d72"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/WhichBrowser/Parser-PHP/zipball/1044880bc792dbce5948fbff22ae731c43c280d9", "url": "https://api.github.com/repos/WhichBrowser/Parser-PHP/zipball/581d614d686bfbec3529ad60562a5213ac5d8d72",
"reference": "1044880bc792dbce5948fbff22ae731c43c280d9", "reference": "581d614d686bfbec3529ad60562a5213ac5d8d72",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2629,8 +2560,7 @@
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"WhichBrowser\\": [ "WhichBrowser\\": [
"src/", "src/"
"tests/src/"
] ]
} }
}, },
@ -2655,28 +2585,28 @@
], ],
"support": { "support": {
"issues": "https://github.com/WhichBrowser/Parser-PHP/issues", "issues": "https://github.com/WhichBrowser/Parser-PHP/issues",
"source": "https://github.com/WhichBrowser/Parser-PHP/tree/v2.1.7" "source": "https://github.com/WhichBrowser/Parser-PHP/tree/v2.1.8"
}, },
"time": "2022-04-19T20:14:54+00:00" "time": "2024-04-17T12:47:41+00:00"
} }
], ],
"packages-dev": [], "packages-dev": [],
"aliases": [], "aliases": [],
"minimum-stability": "beta", "minimum-stability": "beta",
"stability-flags": { "stability-flags": {
"whichbrowser/parser": 20,
"james-heinrich/getid3": 20,
"rybakit/msgpack": 20,
"wapmorgan/binary-stream": 20,
"al/emoji-detector": 20, "al/emoji-detector": 20,
"ezyang/htmlpurifier": 20,
"scssphp/scssphp": 20,
"lfkeitel/phptotp": 20,
"vearutop/php-obscene-censor-rus": 20,
"erusev/parsedown": 20,
"bhaktaraz/php-rss-generator": 20, "bhaktaraz/php-rss-generator": 20,
"erusev/parsedown": 20,
"ezyang/htmlpurifier": 20,
"james-heinrich/getid3": 20,
"lfkeitel/phptotp": 20,
"rybakit/msgpack": 20,
"scssphp/scssphp": 20,
"symfony/console": 20, "symfony/console": 20,
"wapmorgan/morphos": 20 "vearutop/php-obscene-censor-rus": 20,
"wapmorgan/binary-stream": 20,
"wapmorgan/morphos": 20,
"whichbrowser/parser": 20
}, },
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
@ -2684,8 +2614,9 @@
"php": "~7.3||~8.1", "php": "~7.3||~8.1",
"ext-openssl": "*", "ext-openssl": "*",
"ext-simplexml": "*", "ext-simplexml": "*",
"ext-sodium": "*" "ext-sodium": "*",
"ext-iconv": "*"
}, },
"platform-dev": [], "platform-dev": {},
"plugin-api-version": "2.3.0" "plugin-api-version": "2.6.0"
} }

View file

@ -30,7 +30,7 @@ docker build -t ghcr.io/openvk/openvk/php:8.2-apache ../../.. -f base-php-apache
DB images: DB images:
``` ```
docker build -t ghcr.io/openvk/openvk/mariadb:10.9-primary ../../.. -f mariadb-primary.Dockerfile docker build -t ghcr.io/openvk/openvk/mariadb:10.9-primary ../../.. -f mariadb-primary.Dockerfile
docker build -t ghcr.io/openvk/openvk/mariadb:10.9-eventdb ../../.. --f mariadb-eventdb.Dockerfile docker build -t ghcr.io/openvk/openvk/mariadb:10.9-eventdb ../../.. -f mariadb-eventdb.Dockerfile
``` ```
OpenVK main image: OpenVK main image:
``` ```

View file

@ -1,5 +1,5 @@
ARG GITREPO=openvk/openvk ARG GITREPO=openvk/openvk
FROM ghcr.io/${GITREPO}/php:8.2-cli as builder FROM ghcr.io/${GITREPO}/php:8.2-cli AS builder
WORKDIR /opt WORKDIR /opt
@ -27,15 +27,15 @@ ADD composer.* .
RUN composer install RUN composer install
FROM docker.io/node:14 as nodejs FROM docker.io/node:20 AS nodejs
COPY --from=builder /opt/chandler /opt/chandler COPY --from=builder /opt/chandler /opt/chandler
WORKDIR /opt/chandler/extensions/available/openvk/Web/static/js WORKDIR /opt/chandler/extensions/available/openvk/Web/static/js
ADD Web/static/js/package.json Web/static/js/package-lock.json Web/static/js/yarn.lock ./ ADD Web/static/js/package.json Web/static/js/package-lock.json ./
RUN yarn install RUN npm ci
WORKDIR /opt/chandler/extensions/available/openvk WORKDIR /opt/chandler/extensions/available/openvk
@ -57,4 +57,6 @@ VOLUME [ "/opt/chandler/extensions/available/openvk/tmp/api-storage/audios" ]
VOLUME [ "/opt/chandler/extensions/available/openvk/tmp/api-storage/photos" ] VOLUME [ "/opt/chandler/extensions/available/openvk/tmp/api-storage/photos" ]
VOLUME [ "/opt/chandler/extensions/available/openvk/tmp/api-storage/videos" ] VOLUME [ "/opt/chandler/extensions/available/openvk/tmp/api-storage/videos" ]
USER www-data USER www-data
WORKDIR /opt/chandler/extensions/available/openvk

View file

@ -0,0 +1,2 @@
CREATE TABLE `ignored_sources` (`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT , `owner` BIGINT(20) UNSIGNED NOT NULL , `source` BIGINT(20) NOT NULL , PRIMARY KEY (`id`), INDEX (`owner`)) ENGINE = InnoDB;
ALTER TABLE `ignored_sources` ADD INDEX `owner_source` (`owner`, `source`);

View file

@ -0,0 +1,2 @@
ALTER TABLE `videos` ADD `length` SMALLINT(5) UNSIGNED NULL DEFAULT NULL AFTER `name`, ADD INDEX `length` (`length`);
ALTER TABLE `videos` ADD `height` SMALLINT(5) UNSIGNED NULL DEFAULT NULL AFTER `length`, ADD `width` SMALLINT(5) UNSIGNED NULL DEFAULT NULL AFTER `height`;

View file

@ -219,6 +219,7 @@
"my_news" = "My news"; "my_news" = "My news";
"all_news" = "All news"; "all_news" = "All news";
"posts_per_page" = "Number of posts per page"; "posts_per_page" = "Number of posts per page";
"show_ignored_sources" = "Show ignored sources";
"attachment" = "Attachment"; "attachment" = "Attachment";
"post_as_group" = "Post as group"; "post_as_group" = "Post as group";
@ -251,12 +252,24 @@
"edited_short" = "edited"; "edited_short" = "edited";
"feed_settings" = "Feed settings";
"ignored_sources" = "Ignored sources";
"ignore_user" = "Ignore user";
"unignore_user" = "Unignore user";
"ignore_club" = "Ignore club";
"unignore_club" = "Unignore club";
"no_ignores_count" = "No ignored sources.";
"stop_ignore" = "Unignore";
"start_from_page" = "Start from page";
"all_posts" = "All posts"; "all_posts" = "All posts";
"users_posts" = "Posts by $1"; "users_posts" = "Posts by $1";
"clubs_posts" = "Group's posts"; "clubs_posts" = "Group's posts";
"others_posts" = "Others posts"; "others_posts" = "Others posts";
"show_more" = "Show more"; "show_more" = "Show more";
"has_repost" = "Has repost";
/* Friends */ /* Friends */
@ -505,6 +518,7 @@
"click_to_go_to_album" = "Click here to go to album."; "click_to_go_to_album" = "Click here to go to album.";
"error_uploading_photo" = "Error when uploading photo"; "error_uploading_photo" = "Error when uploading photo";
"too_many_pictures" = "No more than 10 pictures"; "too_many_pictures" = "No more than 10 pictures";
"too_many_attachments" = "Too many attachments.";
"drag_files_here" = "Drag files here"; "drag_files_here" = "Drag files here";
"only_images_accepted" = "File \"$1\" is not an image"; "only_images_accepted" = "File \"$1\" is not an image";
@ -836,6 +850,9 @@
"no_videos" = "You don't have uploaded videos."; "no_videos" = "You don't have uploaded videos.";
"no_videos_results" = "No results."; "no_videos_results" = "No results.";
"video_file_upload" = "Upload file";
"video_youtube_upload" = "Add from YouTube";
"change_video" = "Change video"; "change_video" = "Change video";
"unknown_video" = "This video is not supported in your version of OpenVK."; "unknown_video" = "This video is not supported in your version of OpenVK.";
@ -1544,6 +1561,9 @@
"group_is_banned" = "Group was successfully banned"; "group_is_banned" = "Group was successfully banned";
"description_too_long" = "Description is too long."; "description_too_long" = "Description is too long.";
"invalid_club" = "This group does not exists.";
"invalid_user" = "This user does not exists.";
"ignored_sources_limit" = "Limit of ignored sources has exceed";
"invalid_audio" = "Invalid audio."; "invalid_audio" = "Invalid audio.";
"do_not_have_audio" = "You don't have this audio."; "do_not_have_audio" = "You don't have this audio.";
@ -1762,6 +1782,9 @@
"question_confirm" = "This action can't be undone. Do you really wanna do it?"; "question_confirm" = "This action can't be undone. Do you really wanna do it?";
"confirm_m" = "Confirm"; "confirm_m" = "Confirm";
"action_successfully" = "Success"; "action_successfully" = "Success";
"exit_noun" = "Exit";
"exit_confirmation" = "Are you sure want to exit?";
"apply" = "Apply";
/* User Alerts */ /* User Alerts */
@ -2066,7 +2089,7 @@
"showing_x_y" = "(showing $1—$2)"; "showing_x_y" = "(showing $1—$2)";
"no_results_by_this_query" = "Nothing was found by this query."; "no_results_by_this_query" = "Nothing was found by this query.";
"s_additional" = "Additional"; "s_additional" = "Additional";
"s_it_is_you" = "it is you"; "s_it_is_you" = "that's you";
/* BadBrowser */ /* BadBrowser */

View file

@ -13,7 +13,7 @@ list:
flag: "ua" flag: "ua"
name: "Ukrainian" name: "Ukrainian"
native_name: "Українcька" native_name: "Українcька"
author: "Aqukie (yaroslav.bielograd@ukr.net), Andrej Lenťaj, Maxim Hrabovi (dechioyo) and Kirill (mbsoft)" author: "Jaroslav (ovk.to/id6908), Andrej Lenťaj, Maxim Hrabovi (dechioyo) and Kirill (mbsoft)"
- code: "by" - code: "by"
flag: "by" flag: "by"
name: "Belarussian" name: "Belarussian"

View file

@ -204,6 +204,7 @@
"my_news" = "Мои новости"; "my_news" = "Мои новости";
"all_news" = "Все новости"; "all_news" = "Все новости";
"posts_per_page" = "Количество записей на странице"; "posts_per_page" = "Количество записей на странице";
"show_ignored_sources" = "Показывать игнорируемые источники";
"attachment" = "Вложение"; "attachment" = "Вложение";
"post_as_group" = "От имени сообщества"; "post_as_group" = "От имени сообщества";
"comment_as_group" = "От имени сообщества"; "comment_as_group" = "От имени сообщества";
@ -231,11 +232,23 @@
"post_is_ad" = "Этот пост был размещён за взятку."; "post_is_ad" = "Этот пост был размещён за взятку.";
"edited_short" = "ред."; "edited_short" = "ред.";
"feed_settings" = "Настройки ленты";
"ignored_sources" = "Игнорируемые источники";
"ignore_user" = "Игнорировать пользователя";
"unignore_user" = "Не игнорировать пользователя";
"ignore_club" = "Игнорировать группу";
"unignore_club" = "Не игнорировать группу";
"no_ignores_count" = "Игнорируемых источников нет.";
"stop_ignore" = "Не игнорировать";
"start_from_page" = "Начинать со страницы";
"all_posts" = "Все записи"; "all_posts" = "Все записи";
"users_posts" = "Записи $1"; "users_posts" = "Записи $1";
"clubs_posts" = "Записи сообщества"; "clubs_posts" = "Записи сообщества";
"others_posts" = "Чужие записи"; "others_posts" = "Чужие записи";
"show_more" = "Показать больше"; "show_more" = "Показать больше";
"has_repost" = "Содержит репост";
/* Friends */ /* Friends */
@ -488,9 +501,10 @@
"click_to_go_to_album" = "Нажмите, чтобы перейти к альбому."; "click_to_go_to_album" = "Нажмите, чтобы перейти к альбому.";
"error_uploading_photo" = "Не удалось загрузить фотографию"; "error_uploading_photo" = "Не удалось загрузить фотографию";
"too_many_pictures" = "Не больше 10 фотографий"; "too_many_pictures" = "Не больше 10 фотографий";
"too_many_attachments" = "Слишком много вложений.";
"drag_files_here" = "Перетащите файлы сюда"; "drag_files_here" = "Перетащите файлы сюда";
"only_images_accepted" = "Файл \"$1\" не является изображением"; "only_images_accepted" = "Файл \"$1\" не является изображением или видео.";
"max_filesize" = "Максимальный размер файла — $1 мегабайт"; "max_filesize" = "Максимальный размер файла — $1 мегабайт";
"uploading_photos_from_computer" = "Загрузка фотографий с Вашего компьютера"; "uploading_photos_from_computer" = "Загрузка фотографий с Вашего компьютера";
@ -795,6 +809,9 @@
"no_videos" = "У вас нет видео."; "no_videos" = "У вас нет видео.";
"no_videos_results" = "Нет результатов."; "no_videos_results" = "Нет результатов.";
"video_file_upload" = "Загрузить файл";
"video_youtube_upload" = "Добавить с YouTube";
/* Audios */ /* Audios */
"my" = "Моё"; "my" = "Моё";
@ -1445,6 +1462,11 @@
"group_owner_is_banned" = "Создатель сообщества успешно забанен."; "group_owner_is_banned" = "Создатель сообщества успешно забанен.";
"group_is_banned" = "Сообщество успешно забанено"; "group_is_banned" = "Сообщество успешно забанено";
"description_too_long" = "Описание слишком длинное."; "description_too_long" = "Описание слишком длинное.";
"invalid_club" = "Такой группы не существует.";
"invalid_user" = "Такого пользователя не существует.";
"ignored_sources_limit" = "Превышен лимит игнорируемых источников.";
"invalid_audio" = "Такой аудиозаписи не существует."; "invalid_audio" = "Такой аудиозаписи не существует.";
"do_not_have_audio" = "У вас нет этой аудиозаписи."; "do_not_have_audio" = "У вас нет этой аудиозаписи.";
"do_have_audio" = "У вас уже есть эта аудиозапись."; "do_have_audio" = "У вас уже есть эта аудиозапись.";
@ -1464,7 +1486,7 @@
"error_adding_source_regex" = "Ошибка добавления источника: некорректная ссылка."; "error_adding_source_regex" = "Ошибка добавления источника: некорректная ссылка.";
"error_adding_source_long" = "Ошибка добавления источника: слишком длинная ссылка."; "error_adding_source_long" = "Ошибка добавления источника: слишком длинная ссылка.";
"error_adding_source_sus" = "Ошибка добавления источника: подозрительная ссылка."; "error_adding_source_sus" = "Ошибка добавления источника: гиперссылка заблокирована.";
/* Admin actions */ /* Admin actions */
@ -1652,6 +1674,9 @@
"question_confirm" = "Это действие нельзя отменить. Вы действительно уверены в том что хотите сделать?"; "question_confirm" = "Это действие нельзя отменить. Вы действительно уверены в том что хотите сделать?";
"confirm_m" = "Подтвердить"; "confirm_m" = "Подтвердить";
"action_successfully" = "Операция успешна"; "action_successfully" = "Операция успешна";
"exit_noun" = "Выход";
"exit_confirmation" = "Уверены, что хотите выйти?";
"apply" = "Применить";
/* User alerts */ /* User alerts */
@ -2010,7 +2035,7 @@
/* Fullscreen player */ /* Fullscreen player */
"hide_player" = "Скрыть"; "hide_player" = "Свернуть";
"close_player" = "Закрыть"; "close_player" = "Закрыть";
"show_comments" = "Показать информацию"; "show_comments" = "Показать информацию";
"close_comments" = "Скрыть информацию"; "close_comments" = "Скрыть информацию";

File diff suppressed because it is too large Load diff

View file

@ -52,12 +52,15 @@ openvk:
strict: false strict: false
music: music:
exposeOriginalURLs: true exposeOriginalURLs: true
newsfeed:
ignoredSourcesLimit: 50
wall: wall:
christian: false christian: false
anonymousPosting: anonymousPosting:
enable: false enable: false
account: 100 account: 100
postSizes: postSizes:
maxAttachments: 10
maxSize: 60000 maxSize: 60000
processingLimit: 3000 processingLimit: 3000
emojiProcessingLimit: 1000 emojiProcessingLimit: 1000

View file

@ -1,7 +1,7 @@
.page_header { .page_header {
background-image: url('/themepack/midnight/0.0.3.0/resource/xheader.png') !important; background-image: url('/themepack/midnight/0.0.3.1/resource/xheader.png') !important;
} }
.page_custom_header { .page_custom_header {
background-image: url('/themepack/midnight/0.0.3.0/resource/xheader_custom.png') !important; background-image: url('/themepack/midnight/0.0.3.1/resource/xheader_custom.png') !important;
} }

View file

@ -186,8 +186,12 @@ hr {
.menu_divider, .menu_divider,
.ovk-diag-action, .ovk-diag-action,
.minilink .counter, .minilink .counter,
.topGrayBlock { .topGrayBlock,
background-color: #2c2640; .showMore,
.showMoreAudiosPlaylist,
#showMorePhotos,
#showMoreVideos {
background-color: #2c2640 !important;
} }
.bsdn_contextMenu { .bsdn_contextMenu {
@ -313,11 +317,11 @@ tr.v,
} }
.content_title_expanded { .content_title_expanded {
background-image: url("/themepack/midnight/0.0.3.0/resource/flex_arrow_open.png") !important; background-image: url("/themepack/midnight/0.0.3.1/resource/flex_arrow_open.png") !important;
} }
.content_title_unexpanded { .content_title_unexpanded {
background-image: url("/themepack/midnight/0.0.3.0/resource/flex_arrow_shut.gif") !important; background-image: url("/themepack/midnight/0.0.3.1/resource/flex_arrow_shut.gif") !important;
} }
.ovk-video>.preview, .ovk-video>.preview,
@ -344,17 +348,17 @@ tr.h {
.page_yellowheader { .page_yellowheader {
color: #c6d2e8; color: #c6d2e8;
background-image: url("/themepack/midnight/0.0.3.0/resource/header_purple.png") !important; background-image: url("/themepack/midnight/0.0.3.1/resource/header_purple.png") !important;
background-color: #231f34; background-color: #231f34;
border-color: #231f34; border-color: #231f34;
} }
.page_header { .page_header {
background-image: url("/themepack/midnight/0.0.3.0/resource/header.png") !important; background-image: url("/themepack/midnight/0.0.3.1/resource/header.png") !important;
} }
.page_custom_header { .page_custom_header {
background-image: url("/themepack/midnight/0.0.3.0/resource/header_custom.png") !important; background-image: url("/themepack/midnight/0.0.3.1/resource/header_custom.png") !important;
} }
.page_yellowheader span, .page_yellowheader span,
@ -392,11 +396,11 @@ select,
} }
input[type="checkbox"] { input[type="checkbox"] {
background-image: url("/themepack/midnight/0.0.3.0/resource/checkbox.png") !important; background-image: url("/themepack/midnight/0.0.3.1/resource/checkbox.png") !important;
} }
input[type="radio"] { input[type="radio"] {
background-image: url("/themepack/midnight/0.0.3.0/resource/radio.png") !important; background-image: url("/themepack/midnight/0.0.3.1/resource/radio.png") !important;
} }
.header_navigation .link, .header_navigation .header_divider_stick { .header_navigation .link, .header_navigation .header_divider_stick {
@ -404,20 +408,20 @@ input[type="radio"] {
} }
.heart { .heart {
background-image: url("/themepack/midnight/0.0.3.0/resource/like.gif") !important; background-image: url("/themepack/midnight/0.0.3.1/resource/like.gif") !important;
} }
.pinned-mark, .pinned-mark,
.post-author .pin { .post-author .pin {
background-image: url("/themepack/midnight/0.0.3.0/resource/pin.png") !important; background-image: url("/themepack/midnight/0.0.3.1/resource/pin.png") !important;
} }
.repost-icon { .repost-icon {
background-image: url("/themepack/midnight/0.0.3.0/resource/published.gif") !important; background-image: url("/themepack/midnight/0.0.3.1/resource/published.gif") !important;
} }
.post-author .delete { .post-author .delete {
background-image: url("/themepack/midnight/0.0.3.0/resource/input_clear.gif") !important; background-image: url("/themepack/midnight/0.0.3.1/resource/input_clear.gif") !important;
} }
.user-alert { .user-alert {
@ -450,7 +454,7 @@ input[type="radio"] {
} }
#backdropEditor { #backdropEditor {
background-image: url("/themepack/midnight/0.0.3.0/resource/backdrop-editor.gif") !important; background-image: url("/themepack/midnight/0.0.3.1/resource/backdrop-editor.gif") !important;
border-color: #473e66 !important; border-color: #473e66 !important;
} }
@ -588,11 +592,6 @@ ul {
cursor: pointer; cursor: pointer;
} }
.showMore,
.showMoreAudiosPlaylist {
background: #181826 !important;
}
/* Tour */ /* Tour */
.rightNav h1 { .rightNav h1 {
background: #000; background: #000;

View file

@ -1,5 +1,5 @@
id: midnight id: midnight
version: "0.0.3.0" version: "0.0.3.1"
openvk_version: 0 openvk_version: 0
enabled: 1 enabled: 1
metadata: metadata: