mirror of
https://github.com/openvk/openvk
synced 2024-11-15 11:39:13 +03:00
Merge branch 'master' into textarea-refactor
This commit is contained in:
commit
0fd5958443
198 changed files with 7905 additions and 4240 deletions
58
.github/workflows/build-base.yaml
vendored
Normal file
58
.github/workflows/build-base.yaml
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
name: Build base images
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
|
env:
|
||||||
|
BASE_IMAGE_NAME: php
|
||||||
|
BASE_IMAGE_VERSION: "8.1"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-cli:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
lfs: false
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Log into registry
|
||||||
|
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
||||||
|
|
||||||
|
- name: Build cli image
|
||||||
|
run: |
|
||||||
|
IMAGE_NAME=ghcr.io/${{ github.repository }}/$BASE_IMAGE_NAME:$BASE_IMAGE_VERSION-cli
|
||||||
|
|
||||||
|
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/base-php-cli.Dockerfile --build-arg VERSION=$BASE_IMAGE_VERSION
|
||||||
|
|
||||||
|
build-apache:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
lfs: false
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Log into registry
|
||||||
|
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
||||||
|
|
||||||
|
- name: Build apache image
|
||||||
|
run: |
|
||||||
|
IMAGE_NAME=ghcr.io/${{ github.repository }}/$BASE_IMAGE_NAME:$BASE_IMAGE_VERSION-apache
|
||||||
|
|
||||||
|
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/base-php-apache.Dockerfile --build-arg VERSION=$BASE_IMAGE_VERSION
|
64
.github/workflows/build.yaml
vendored
Normal file
64
.github/workflows/build.yaml
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
name: Build images
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
# Publish `master` as Docker `latest` image.
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
# Publish `v1.2.3` tags as releases.
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
|
||||||
|
env:
|
||||||
|
BASE_IMAGE_NAME: openvk
|
||||||
|
DB_IMAGE_NAME: mariadb
|
||||||
|
EVENT_IMAGE_NAME: mariadb
|
||||||
|
DB_VERSION: "10.9"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch: ['x86_64']
|
||||||
|
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
lfs: false
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Log into registry
|
||||||
|
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
||||||
|
|
||||||
|
- name: Build base image
|
||||||
|
run: |
|
||||||
|
IMAGE_ID=ghcr.io/${{ github.repository }}/$BASE_IMAGE_NAME
|
||||||
|
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
|
||||||
|
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
|
||||||
|
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
|
||||||
|
[ "$VERSION" == "master" ] && VERSION=latest
|
||||||
|
echo IMAGE_ID=$IMAGE_ID
|
||||||
|
echo VERSION=$VERSION
|
||||||
|
|
||||||
|
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_ID:$VERSION . --push -f install/automated/docker/openvk.Dockerfile --build-arg GITREPO=${{ github.repository }}
|
||||||
|
|
||||||
|
- name: Build MariaDB primary image
|
||||||
|
run: |
|
||||||
|
IMAGE_NAME=ghcr.io/${{ github.repository }}/$DB_IMAGE_NAME:$DB_VERSION-primary
|
||||||
|
|
||||||
|
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/mariadb-primary.Dockerfile --build-arg VERSION=$DB_VERSION
|
||||||
|
|
||||||
|
- name: Build MariaDB event image
|
||||||
|
run: |
|
||||||
|
IMAGE_NAME=ghcr.io/${{ github.repository }}/$EVENT_IMAGE_NAME:$DB_VERSION-eventdb
|
||||||
|
|
||||||
|
docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_NAME . --push -f install/automated/docker/mariadb-eventdb.Dockerfile --build-arg VERSION=$DB_VERSION
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,5 +1,6 @@
|
||||||
vendor
|
vendor
|
||||||
openvk.yml
|
openvk.yml
|
||||||
|
chandler.yml
|
||||||
update.pid
|
update.pid
|
||||||
update.pid.old
|
update.pid.old
|
||||||
Web/static/js/node_modules
|
Web/static/js/node_modules
|
||||||
|
@ -10,5 +11,6 @@ tmp/*
|
||||||
themepacks/*
|
themepacks/*
|
||||||
!themepacks/.gitkeep
|
!themepacks/.gitkeep
|
||||||
!themepacks/openvk_modern
|
!themepacks/openvk_modern
|
||||||
|
!themepacks/midnight
|
||||||
storage/*
|
storage/*
|
||||||
!storage/.gitkeep
|
!storage/.gitkeep
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
<?xml encoding="UTF-8"?>
|
<?xml encoding="UTF-8"?>
|
||||||
|
|
||||||
<!ELEMENT latte (tags,filters,variables,functions)>
|
<!ELEMENT latte (tags,filters,variables,functions)>
|
||||||
<!ATTLIST latte vendor #REQUIRED>
|
<!ATTLIST latte vendor #REQUIRED>
|
||||||
<!ATTLIST latte version #REQUIRED>
|
<!ATTLIST latte version #REQUIRED>
|
||||||
|
|
||||||
<!ELEMENT tags (tag)+>
|
<!ELEMENT tags (tag)+>
|
||||||
|
|
||||||
<!ELEMENT tag (arguments)?>
|
<!ELEMENT tag (arguments)?>
|
||||||
<!ATTLIST tag name CDATA #REQUIRED>
|
<!ATTLIST tag name CDATA #REQUIRED>
|
||||||
<!ATTLIST tag type (PAIR|UNPAIRED|UNPAIRED_ATTR|ATTR_ONLY|AUTO_EMPTY) #REQUIRED>
|
<!ATTLIST tag type (PAIR|UNPAIRED|UNPAIRED_ATTR|ATTR_ONLY|AUTO_EMPTY) #REQUIRED>
|
||||||
|
@ -13,32 +10,24 @@
|
||||||
<!ATTLIST tag arguments CDATA #IMPLIED>
|
<!ATTLIST tag arguments CDATA #IMPLIED>
|
||||||
<!ATTLIST tag deprecatedMessage CDATA #IMPLIED>
|
<!ATTLIST tag deprecatedMessage CDATA #IMPLIED>
|
||||||
<!ATTLIST tag multiLine (true|false) #IMPLIED>
|
<!ATTLIST tag multiLine (true|false) #IMPLIED>
|
||||||
|
|
||||||
<!ELEMENT arguments (argument)+>
|
<!ELEMENT arguments (argument)+>
|
||||||
|
|
||||||
<!ELEMENT argument EMPTY>
|
<!ELEMENT argument EMPTY>
|
||||||
<!ATTLIST argument name #REQUIRED>
|
<!ATTLIST argument name #REQUIRED>
|
||||||
<!ATTLIST argument types CDATA #REQUIRED>
|
<!ATTLIST argument types CDATA #REQUIRED>
|
||||||
<!ATTLIST argument repeatable (true|false) #IMPLIED>
|
<!ATTLIST argument repeatable (true|false) #IMPLIED>
|
||||||
<!ATTLIST argument required (true|false) #IMPLIED>
|
<!ATTLIST argument required (true|false) #IMPLIED>
|
||||||
<!ATTLIST argument validType #IMPLIED>
|
<!ATTLIST argument validType #IMPLIED>
|
||||||
|
|
||||||
<!ELEMENT filters (filter)+>
|
<!ELEMENT filters (filter)+>
|
||||||
|
|
||||||
<!ELEMENT filter EMPTY>
|
<!ELEMENT filter EMPTY>
|
||||||
<!ATTLIST filter name #REQUIRED>
|
<!ATTLIST filter name #REQUIRED>
|
||||||
<!ATTLIST filter description CDATA #IMPLIED>
|
<!ATTLIST filter description CDATA #IMPLIED>
|
||||||
<!ATTLIST filter arguments CDATA #IMPLIED>
|
<!ATTLIST filter arguments CDATA #IMPLIED>
|
||||||
<!ATTLIST filter insertColons #IMPLIED>
|
<!ATTLIST filter insertColons #IMPLIED>
|
||||||
|
|
||||||
<!ELEMENT variables (variable)+>
|
<!ELEMENT variables (variable)+>
|
||||||
|
|
||||||
<!ELEMENT variable EMPTY>
|
<!ELEMENT variable EMPTY>
|
||||||
<!ATTLIST variable name #REQUIRED>
|
<!ATTLIST variable name #REQUIRED>
|
||||||
<!ATTLIST variable type CDATA #REQUIRED>
|
<!ATTLIST variable type CDATA #REQUIRED>
|
||||||
|
|
||||||
<!ELEMENT functions (function)+>
|
<!ELEMENT functions (function)+>
|
||||||
|
|
||||||
<!ELEMENT function EMPTY>
|
<!ELEMENT function EMPTY>
|
||||||
<!ATTLIST function name #REQUIRED>
|
<!ATTLIST function name #REQUIRED>
|
||||||
<!ATTLIST function arguments CDATA #REQUIRED>
|
<!ATTLIST function arguments CDATA #REQUIRED>
|
||||||
|
|
82
Dockerfile
82
Dockerfile
|
@ -1,82 +0,0 @@
|
||||||
FROM fedora:33
|
|
||||||
|
|
||||||
#update and install httpd
|
|
||||||
RUN dnf -y update && dnf -y autoremove && dnf install -y httpd
|
|
||||||
|
|
||||||
#Let's install Remi repos for PHP 7.4:
|
|
||||||
RUN dnf -y install https://rpms.remirepo.net/fedora/remi-release-$(rpm -E %fedora).rpm
|
|
||||||
|
|
||||||
#Then enable modules that we need:
|
|
||||||
RUN dnf -y module enable php:remi-7.4 && \
|
|
||||||
dnf -y module enable nodejs:14
|
|
||||||
|
|
||||||
#And install dependencies:
|
|
||||||
RUN dnf -y --skip-broken install php php-cli php-common unzip php-zip php-yaml php-gd php-pdo_mysql nodejs git
|
|
||||||
|
|
||||||
#Don't forget about Yarn and Composer:
|
|
||||||
RUN npm i -g yarn && \
|
|
||||||
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \
|
|
||||||
php composer-setup.php --filename=composer2 --install-dir=/bin --snapshot && \
|
|
||||||
rm composer-setup.php
|
|
||||||
|
|
||||||
#We will use Mariadb for DB:
|
|
||||||
RUN dnf -y install mysql mysql-server && \
|
|
||||||
systemctl enable mariadb && \
|
|
||||||
echo 'skip-grant-tables' >> /etc/my.cnf
|
|
||||||
|
|
||||||
#Additionally, you can install ffmpeg for processing videos.
|
|
||||||
#You will need to use RPMFusion repo to install it:
|
|
||||||
RUN dnf -y install --nogpgcheck https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm && \
|
|
||||||
dnf -y install --nogpgcheck https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm
|
|
||||||
|
|
||||||
#Then install SDL2 and ffmpeg:
|
|
||||||
RUN dnf -y install --nogpgcheck SDL2 ffmpeg
|
|
||||||
|
|
||||||
#Install Chandler and OpenVk/Capcha-extention in /opt:
|
|
||||||
RUN cd /opt && \
|
|
||||||
git clone https://github.com/samukhin/chandler.git && \
|
|
||||||
cd chandler/ && \
|
|
||||||
composer2 install && \
|
|
||||||
mv chandler-example.yml chandler.yml && \
|
|
||||||
cd extensions/available/ && \
|
|
||||||
git clone https://github.com/samukhin/commitcaptcha.git && \
|
|
||||||
cd commitcaptcha/ && \
|
|
||||||
composer2 install && \
|
|
||||||
cd .. && \
|
|
||||||
git clone https://github.com/samukhin/openvk.git && \
|
|
||||||
cd openvk/ && \
|
|
||||||
composer2 install && \
|
|
||||||
cd Web/static/js && \
|
|
||||||
yarn install && \
|
|
||||||
cd ../../../ && \
|
|
||||||
mv openvk-example.yml openvk.yml && \
|
|
||||||
ln -s /opt/chandler/extensions/available/commitcaptcha/ /opt/chandler/extensions/enabled/commitcaptcha && \
|
|
||||||
ln -s /opt/chandler/extensions/available/openvk/ /opt/chandler/extensions/enabled/openvk
|
|
||||||
|
|
||||||
#Create database
|
|
||||||
RUN cp /opt/chandler/extensions/available/openvk/install/automated/common/create_db.service /etc/systemd/system/ && \
|
|
||||||
chmod 644 /etc/systemd/system/create_db.service && \
|
|
||||||
chmod 777 /opt/chandler/extensions/available/openvk/install/automated/common/autoexec && \
|
|
||||||
systemctl enable create_db
|
|
||||||
|
|
||||||
#Make the user apache owner of the chandler folder:
|
|
||||||
RUN cd /opt && \
|
|
||||||
chown -R apache: chandler/
|
|
||||||
|
|
||||||
#Now let's create config file /etc/httpd/conf.d/10-openvk.conf and
|
|
||||||
#Also enable rewrite_module by creating /etc/httpd/conf.modules.d/02-rewrite.conf
|
|
||||||
RUN cp /opt/chandler/extensions/available/openvk/install/automated/common/10-openvk.conf /etc/httpd/conf.d/ && \
|
|
||||||
cp /opt/chandler/extensions/available/openvk/install/automated/common/02-rewrite.conf /etc/httpd/conf.modules.d/
|
|
||||||
|
|
||||||
#Make directory for OpenVK logs and make the user apache owner of it:
|
|
||||||
RUN mkdir /var/log/openvk && \
|
|
||||||
chown apache: /var/log/openvk/
|
|
||||||
|
|
||||||
#And start Apache:
|
|
||||||
#RUN systemctl enable httpd
|
|
||||||
|
|
||||||
#For login
|
|
||||||
RUN dnf -y install passwd && passwd -d root
|
|
||||||
|
|
||||||
#Start systemd
|
|
||||||
CMD ["/sbin/init"]
|
|
|
@ -72,6 +72,9 @@ Once you are done, you can login as a system administrator on the network itself
|
||||||
|
|
||||||
💡Confused? Full installation walkthrough is available [here](https://docs.openvk.su/openvk_engine/centos8_installation/) (CentOS 8 [and](https://almalinux.org/) [family](https://yum.oracle.com/oracle-linux-isos.html)).
|
💡Confused? Full installation walkthrough is available [here](https://docs.openvk.su/openvk_engine/centos8_installation/) (CentOS 8 [and](https://almalinux.org/) [family](https://yum.oracle.com/oracle-linux-isos.html)).
|
||||||
|
|
||||||
|
### Looking for Docker or Kubernetes deployment?
|
||||||
|
See `install/automated/docker/README.md` and `install/automated/kubernetes/README.md` for Docker and Kubernetes deployment instructions.
|
||||||
|
|
||||||
### If my website uses OpenVK, should I release it's sources?
|
### If my website uses OpenVK, should I release it's sources?
|
||||||
|
|
||||||
It depends. You can keep the sources to yourself if you do not plan to distribute your website binaries. If your website software must be distributed, it can stay non-OSS provided the OpenVK is not used as a primary application and is not modified. If you modified OpenVK for your needs or your work is based on it and you're planning to redistribute this, then you should license it under terms of any LGPL-compatible license (like OSL, GPL, LGPL etc).
|
It depends. You can keep the sources to yourself if you do not plan to distribute your website binaries. If your website software must be distributed, it can stay non-OSS provided the OpenVK is not used as a primary application and is not modified. If you modified OpenVK for your needs or your work is based on it and you're planning to redistribute this, then you should license it under terms of any LGPL-compatible license (like OSL, GPL, LGPL etc).
|
||||||
|
|
|
@ -72,6 +72,9 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
|
||||||
|
|
||||||
💡Запутались? Полное руководство по установке доступно [здесь](https://docs.openvk.su/openvk_engine/centos8_installation/) (CentOS 8 [и](https://almalinux.org/ru/) [семейство](https://yum.oracle.com/oracle-linux-isos.html)).
|
💡Запутались? Полное руководство по установке доступно [здесь](https://docs.openvk.su/openvk_engine/centos8_installation/) (CentOS 8 [и](https://almalinux.org/ru/) [семейство](https://yum.oracle.com/oracle-linux-isos.html)).
|
||||||
|
|
||||||
|
# Установка в Docker/Kubernetes
|
||||||
|
Подробные иструкции можно найти в `install/automated/docker/README.md` и `install/automated/kubernetes/README.md` соответственно.
|
||||||
|
|
||||||
### Если мой сайт использует OpenVK, должен ли я публиковать его исходные тексты?
|
### Если мой сайт использует OpenVK, должен ли я публиковать его исходные тексты?
|
||||||
|
|
||||||
Это зависит от обстоятельств. Вы можете оставить исходные тексты при себе, если не планируете распространять бинарники вашего сайта. Если программное обеспечение вашего сайта должно распространяться, оно может оставаться не-OSS при условии, что OpenVK не используется в качестве основного приложения и не модифицируется. Если вы модифицировали OpenVK для своих нужд или ваша работа основана на нем и вы планируете ее распространять, то вы должны лицензировать ее на условиях любой совместимой с LGPL лицензии (например, OSL, GPL, LGPL и т.д.).
|
Это зависит от обстоятельств. Вы можете оставить исходные тексты при себе, если не планируете распространять бинарники вашего сайта. Если программное обеспечение вашего сайта должно распространяться, оно может оставаться не-OSS при условии, что OpenVK не используется в качестве основного приложения и не модифицируется. Если вы модифицировали OpenVK для своих нужд или ваша работа основана на нем и вы планируете ее распространять, то вы должны лицензировать ее на условиях любой совместимой с LGPL лицензии (например, OSL, GPL, LGPL и т.д.).
|
||||||
|
|
|
@ -55,6 +55,11 @@ class Apps implements Handler
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($amount < 0) {
|
||||||
|
$reject(552, "Payment amount is invalid");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$coinsLeft = $this->user->getCoins() - $amount;
|
$coinsLeft = $this->user->getCoins() - $amount;
|
||||||
if($coinsLeft < 0) {
|
if($coinsLeft < 0) {
|
||||||
$reject(41, "Not enough money");
|
$reject(41, "Not enough money");
|
||||||
|
|
50
ServiceAPI/Mentions.php
Normal file
50
ServiceAPI/Mentions.php
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\ServiceAPI;
|
||||||
|
use openvk\Web\Models\Entities\User;
|
||||||
|
use openvk\Web\Models\Repositories\{Users, Clubs};
|
||||||
|
|
||||||
|
class Mentions implements Handler
|
||||||
|
{
|
||||||
|
protected $user;
|
||||||
|
|
||||||
|
function __construct(?User $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolve(int $id, callable $resolve, callable $reject): void
|
||||||
|
{
|
||||||
|
if($id > 0) {
|
||||||
|
$user = (new Users)->get($id);
|
||||||
|
if(!$user) {
|
||||||
|
$reject("Not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolve([
|
||||||
|
"url" => $user->getURL(),
|
||||||
|
"name" => $user->getFullName(),
|
||||||
|
"ava" => $user->getAvatarURL("miniscule"),
|
||||||
|
"about" => $user->getStatus() ?? "",
|
||||||
|
"online" => ($user->isFemale() ? tr("was_online_f") : tr("was_online_m")) . " " . $user->getOnline(),
|
||||||
|
"verif" => $user->isVerified(),
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$club = (new Clubs)->get(abs($id));
|
||||||
|
if(!$club) {
|
||||||
|
$reject("Not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolve([
|
||||||
|
"url" => $club->getURL(),
|
||||||
|
"name" => $club->getName(),
|
||||||
|
"ava" => $club->getAvatarURL("miniscule"),
|
||||||
|
"about" => $club->getDescription() ?? "",
|
||||||
|
"online" => tr("participants", $club->getFollowersCount()),
|
||||||
|
"verif" => $club->isVerified(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
70
ServiceAPI/Polls.php
Normal file
70
ServiceAPI/Polls.php
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\ServiceAPI;
|
||||||
|
use Chandler\MVC\Routing\Router;
|
||||||
|
use openvk\Web\Models\Entities\User;
|
||||||
|
use openvk\Web\Models\Exceptions\{AlreadyVotedException, InvalidOptionException, PollLockedException};
|
||||||
|
use openvk\Web\Models\Repositories\Polls as PollRepo;
|
||||||
|
use UnexpectedValueException;
|
||||||
|
|
||||||
|
class Polls implements Handler
|
||||||
|
{
|
||||||
|
protected $user;
|
||||||
|
protected $polls;
|
||||||
|
|
||||||
|
function __construct(?User $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
$this->polls = new PollRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPollHtml(int $poll): string
|
||||||
|
{
|
||||||
|
return Router::i()->execute("/poll$poll", "SAPI");
|
||||||
|
}
|
||||||
|
|
||||||
|
function vote(int $pollId, string $options, callable $resolve, callable $reject): void
|
||||||
|
{
|
||||||
|
$poll = $this->polls->get($pollId);
|
||||||
|
if(!$poll) {
|
||||||
|
$reject("Poll not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$options = explode(",", $options);
|
||||||
|
$poll->vote($this->user, $options);
|
||||||
|
} catch(AlreadyVotedException $ex) {
|
||||||
|
$reject("Poll state changed: user has already voted.");
|
||||||
|
return;
|
||||||
|
} catch(PollLockedException $ex) {
|
||||||
|
$reject("Poll state changed: poll has ended.");
|
||||||
|
return;
|
||||||
|
} catch(InvalidOptionException $ex) {
|
||||||
|
$reject("Foreign options passed.");
|
||||||
|
return;
|
||||||
|
} catch(UnexpectedValueException $ex) {
|
||||||
|
$reject("Too much options passed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolve(["html" => $this->getPollHtml($pollId)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unvote(int $pollId, callable $resolve, callable $reject): void
|
||||||
|
{
|
||||||
|
$poll = $this->polls->get($pollId);
|
||||||
|
if(!$poll) {
|
||||||
|
$reject("Poll not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$poll->revokeVote($this->user);
|
||||||
|
} catch(PollLockedException $ex) {
|
||||||
|
$reject("Votes can't be revoked from this poll.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolve(["html" => $this->getPollHtml($pollId)]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\VKAPI\Handlers;
|
namespace openvk\VKAPI\Handlers;
|
||||||
|
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||||
|
|
||||||
final class Account extends VKAPIRequestHandler
|
final class Account extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
|
@ -13,7 +14,7 @@ final class Account extends VKAPIRequestHandler
|
||||||
"last_name" => $this->getUser()->getLastName(),
|
"last_name" => $this->getUser()->getLastName(),
|
||||||
"home_town" => $this->getUser()->getHometown(),
|
"home_town" => $this->getUser()->getHometown(),
|
||||||
"status" => $this->getUser()->getStatus(),
|
"status" => $this->getUser()->getStatus(),
|
||||||
"bdate" => $this->getUser()->getBirthday()->format('%e.%m.%Y'),
|
"bdate" => is_null($this->getUser()->getBirthday()) ? '01.01.1970' : $this->getUser()->getBirthday()->format('%e.%m.%Y'),
|
||||||
"bdate_visibility" => $this->getUser()->getBirthdayPrivacy(),
|
"bdate_visibility" => $this->getUser()->getBirthdayPrivacy(),
|
||||||
"phone" => "+420 ** *** 228", # TODO
|
"phone" => "+420 ** *** 228", # TODO
|
||||||
"relation" => $this->getUser()->getMaritalStatus(),
|
"relation" => $this->getUser()->getMaritalStatus(),
|
||||||
|
@ -74,4 +75,77 @@ final class Account extends VKAPIRequestHandler
|
||||||
|
|
||||||
# TODO: Filter
|
# TODO: Filter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveProfileInfo(string $first_name = "", string $last_name = "", string $screen_name = "", int $sex = -1, int $relation = -1, string $bdate = "", int $bdate_visibility = -1, string $home_town = "", string $status = ""): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
$user = $this->getUser();
|
||||||
|
|
||||||
|
$output = [
|
||||||
|
"changed" => 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
if(!empty($first_name) || !empty($last_name)) {
|
||||||
|
$output["name_request"] = [
|
||||||
|
"id" => random_int(1, 2048), # For compatibility with original VK API
|
||||||
|
"status" => "success",
|
||||||
|
"first_name" => !empty($first_name) ? $first_name : $user->getFirstName(),
|
||||||
|
"last_name" => !empty($last_name) ? $last_name : $user->getLastName(),
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
if(!empty($first_name))
|
||||||
|
$user->setFirst_name($first_name);
|
||||||
|
if(!empty($last_name))
|
||||||
|
$user->setLast_Name($last_name);
|
||||||
|
} catch (InvalidUserNameException $e) {
|
||||||
|
$output["name_request"]["status"] = "declined";
|
||||||
|
return (object) $output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($screen_name))
|
||||||
|
if (!$user->setShortCode($screen_name))
|
||||||
|
$this->fail(1260, "Invalid screen name");
|
||||||
|
|
||||||
|
# For compatibility with original VK API
|
||||||
|
if($sex > 0)
|
||||||
|
$user->setSex($sex == 1 ? 1 : 0);
|
||||||
|
|
||||||
|
if($relation > -1)
|
||||||
|
$user->setMarital_Status($relation);
|
||||||
|
|
||||||
|
if(!empty($bdate)) {
|
||||||
|
$birthday = strtotime($bdate);
|
||||||
|
if (!is_int($birthday))
|
||||||
|
$this->fail(100, "invalid value of bdate.");
|
||||||
|
|
||||||
|
$user->setBirthday($birthday);
|
||||||
|
}
|
||||||
|
|
||||||
|
# For compatibility with original VK API
|
||||||
|
switch($bdate_visibility) {
|
||||||
|
case 0:
|
||||||
|
$this->fail(946, "Hiding date of birth is not implemented.");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
$user->setBirthday_privacy(0);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
$user->setBirthday_privacy(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($home_town))
|
||||||
|
$user->setHometown($home_town);
|
||||||
|
|
||||||
|
if(!empty($status))
|
||||||
|
$user->setStatus($status);
|
||||||
|
|
||||||
|
if($sex > 0 || $relation > -1 || $bdate_visibility > 1 || !empty("$first_name$last_name$screen_name$bdate$home_town$status")) {
|
||||||
|
$output["changed"] = 1;
|
||||||
|
$user->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) $output;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,7 +107,7 @@ final class Friends extends VKAPIRequestHandler
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fail(15, "Access denied: No friend or friend request found.");
|
$this->fail(15, "Access denied: No friend or friend request found.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,15 +133,18 @@ final class Friends extends VKAPIRequestHandler
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRequests(string $fields = "", int $offset = 0, int $count = 100): object
|
function getRequests(string $fields = "", int $offset = 0, int $count = 100, int $extended = 0): object
|
||||||
{
|
{
|
||||||
|
if ($count >= 1000)
|
||||||
|
$this->fail(100, "One of the required parameters was not passed or is invalid.");
|
||||||
|
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
$i = 0;
|
$i = 0;
|
||||||
$offset++;
|
$offset++;
|
||||||
$followers = [];
|
$followers = [];
|
||||||
|
|
||||||
foreach($this->getUser()->getFollowers() as $follower) {
|
foreach($this->getUser()->getFollowers($offset, $count) as $follower) {
|
||||||
$followers[$i] = $follower->getId();
|
$followers[$i] = $follower->getId();
|
||||||
$i++;
|
$i++;
|
||||||
}
|
}
|
||||||
|
@ -149,8 +152,10 @@ final class Friends extends VKAPIRequestHandler
|
||||||
$response = $followers;
|
$response = $followers;
|
||||||
$usersApi = new Users($this->getUser());
|
$usersApi = new Users($this->getUser());
|
||||||
|
|
||||||
if(!is_null($fields))
|
if($extended == 1)
|
||||||
$response = $usersApi->get(implode(',', $followers), $fields, 0, $count); # FIXME
|
$response = $usersApi->get(implode(',', $followers), $fields, 0, $count);
|
||||||
|
else
|
||||||
|
$response = $usersApi->get(implode(',', $followers), "", 0, $count);
|
||||||
|
|
||||||
foreach($response as $user)
|
foreach($response as $user)
|
||||||
$user->user_id = $user->id;
|
$user->user_id = $user->id;
|
||||||
|
|
|
@ -10,7 +10,7 @@ final class Groups extends VKAPIRequestHandler
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
if($user_id == 0) {
|
if($user_id == 0) {
|
||||||
foreach($this->getUser()->getClubs($offset+1) as $club)
|
foreach($this->getUser()->getClubs($offset, false, $count, true) as $club)
|
||||||
$clbs[] = $club;
|
$clbs[] = $club;
|
||||||
$clbsCount = $this->getUser()->getClubCount();
|
$clbsCount = $this->getUser()->getClubCount();
|
||||||
} else {
|
} else {
|
||||||
|
@ -20,7 +20,7 @@ final class Groups extends VKAPIRequestHandler
|
||||||
if(is_null($user))
|
if(is_null($user))
|
||||||
$this->fail(15, "Access denied");
|
$this->fail(15, "Access denied");
|
||||||
|
|
||||||
foreach($user->getClubs($offset+1) as $club)
|
foreach($user->getClubs($offset, false, $count, true) as $club)
|
||||||
$clbs[] = $club;
|
$clbs[] = $club;
|
||||||
|
|
||||||
$clbsCount = $user->getClubCount();
|
$clbsCount = $user->getClubCount();
|
||||||
|
@ -33,17 +33,9 @@ final class Groups extends VKAPIRequestHandler
|
||||||
$ic = $count;
|
$ic = $count;
|
||||||
|
|
||||||
if(!empty($clbs)) {
|
if(!empty($clbs)) {
|
||||||
$clbs = array_slice($clbs, $offset * $count);
|
|
||||||
|
|
||||||
for($i=0; $i < $ic; $i++) {
|
for($i=0; $i < $ic; $i++) {
|
||||||
$usr = $clbs[$i];
|
$usr = $clbs[$i];
|
||||||
if(is_null($usr)) {
|
if(is_null($usr)) {
|
||||||
$rClubs[$i] = (object)[
|
|
||||||
"id" => $clbs[$i],
|
|
||||||
"name" => "DELETED",
|
|
||||||
"deactivated" => "deleted"
|
|
||||||
];
|
|
||||||
} else if($clbs[$i] == NULL) {
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$rClubs[$i] = (object) [
|
$rClubs[$i] = (object) [
|
||||||
|
@ -102,23 +94,32 @@ final class Groups extends VKAPIRequestHandler
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getById(string $group_ids = "", string $group_id = "", string $fields = ""): ?array
|
function getById(string $group_ids = "", string $group_id = "", string $fields = "", int $offset = 0, int $count = 500): ?array
|
||||||
{
|
{
|
||||||
|
/* Both offset and count SHOULD be used only in OpenVK code,
|
||||||
|
not in your app or script, since it's not oficially documented by VK */
|
||||||
|
|
||||||
$clubs = new ClubsRepo;
|
$clubs = new ClubsRepo;
|
||||||
|
|
||||||
if($group_ids == NULL && $group_id != NULL)
|
if(empty($group_ids) && !empty($group_id))
|
||||||
$group_ids = $group_id;
|
$group_ids = $group_id;
|
||||||
|
|
||||||
if($group_ids == NULL && $group_id == NULL)
|
if(empty($group_ids) && empty($group_id))
|
||||||
$this->fail(100, "One of the parameters specified was missing or invalid: group_ids is undefined");
|
$this->fail(100, "One of the parameters specified was missing or invalid: group_ids is undefined");
|
||||||
|
|
||||||
$clbs = explode(',', $group_ids);
|
$clbs = explode(',', $group_ids);
|
||||||
$response;
|
$response = array();
|
||||||
|
|
||||||
$ic = sizeof($clbs);
|
$ic = sizeof($clbs);
|
||||||
|
|
||||||
|
if(sizeof($clbs) > $count)
|
||||||
|
$ic = $count;
|
||||||
|
|
||||||
|
$clbs = array_slice($clbs, $offset * $count);
|
||||||
|
|
||||||
|
|
||||||
for($i=0; $i < $ic; $i++) {
|
for($i=0; $i < $ic; $i++) {
|
||||||
if($i > 500)
|
if($i > 500 || $clbs[$i] == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if($clbs[$i] < 0)
|
if($clbs[$i] < 0)
|
||||||
|
@ -142,6 +143,7 @@ final class Groups extends VKAPIRequestHandler
|
||||||
"screen_name" => $clb->getShortCode() ?? "club".$clb->getId(),
|
"screen_name" => $clb->getShortCode() ?? "club".$clb->getId(),
|
||||||
"is_closed" => false,
|
"is_closed" => false,
|
||||||
"type" => "group",
|
"type" => "group",
|
||||||
|
"is_member" => !is_null($this->getUser()) ? (int) $clb->getSubscriptionStatus($this->getUser()) : 0,
|
||||||
"can_access_closed" => true,
|
"can_access_closed" => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -204,10 +206,6 @@ final class Groups extends VKAPIRequestHandler
|
||||||
else
|
else
|
||||||
$response[$i]->can_post = $clb->canPost();
|
$response[$i]->can_post = $clb->canPost();
|
||||||
break;
|
break;
|
||||||
case "is_member":
|
|
||||||
if(!is_null($this->getUser()))
|
|
||||||
$response[$i]->is_member = (int) $clb->getSubscriptionStatus($this->getUser());
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,4 +213,52 @@ final class Groups extends VKAPIRequestHandler
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function search(string $q, int $offset = 0, int $count = 100)
|
||||||
|
{
|
||||||
|
$clubs = new ClubsRepo;
|
||||||
|
|
||||||
|
$array = [];
|
||||||
|
$find = $clubs->find($q);
|
||||||
|
|
||||||
|
foreach ($find as $group)
|
||||||
|
$array[] = $group->getId();
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"count" => $find->size(),
|
||||||
|
"items" => $this->getById(implode(',', $array), "", "is_admin,is_member,is_advertiser,photo_50,photo_100,photo_200", $offset, $count)
|
||||||
|
/*
|
||||||
|
* As there is no thing as "fields" by the original documentation
|
||||||
|
* i'll just bake this param by the example shown here: https://dev.vk.com/method/groups.search
|
||||||
|
*/
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function join(int $group_id)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$club = (new ClubsRepo)->get($group_id);
|
||||||
|
|
||||||
|
$isMember = !is_null($this->getUser()) ? (int) $club->getSubscriptionStatus($this->getUser()) : 0;
|
||||||
|
|
||||||
|
if($isMember == 0)
|
||||||
|
$club->toggleSubscription($this->getUser());
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function leave(int $group_id)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$club = (new ClubsRepo)->get($group_id);
|
||||||
|
|
||||||
|
$isMember = !is_null($this->getUser()) ? (int) $club->getSubscriptionStatus($this->getUser()) : 0;
|
||||||
|
|
||||||
|
if($isMember == 1)
|
||||||
|
$club->toggleSubscription($this->getUser());
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,6 +152,7 @@ final class Messages extends VKAPIRequestHandler
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
$convos = (new MSGRepo)->getCorrespondencies($this->getUser(), -1, $count, $offset);
|
$convos = (new MSGRepo)->getCorrespondencies($this->getUser(), -1, $count, $offset);
|
||||||
|
$convosCount = (new MSGRepo)->getCorrespondenciesCount($this->getUser());
|
||||||
$list = [];
|
$list = [];
|
||||||
|
|
||||||
$users = [];
|
$users = [];
|
||||||
|
@ -197,7 +198,6 @@ final class Messages extends VKAPIRequestHandler
|
||||||
$lastMessagePreview->emoji = true;
|
$lastMessagePreview->emoji = true;
|
||||||
|
|
||||||
if($extended == 1) {
|
if($extended == 1) {
|
||||||
$users[] = $lastMessage->getSender()->getId();
|
|
||||||
$users[] = $author;
|
$users[] = $author;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,16 +210,17 @@ final class Messages extends VKAPIRequestHandler
|
||||||
|
|
||||||
if($extended == 0){
|
if($extended == 0){
|
||||||
return (object) [
|
return (object) [
|
||||||
"count" => sizeof($list),
|
"count" => $convosCount,
|
||||||
"items" => $list,
|
"items" => $list,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
|
$users[] = $this->getUser()->getId();
|
||||||
$users = array_unique($users);
|
$users = array_unique($users);
|
||||||
|
|
||||||
return (object) [
|
return (object) [
|
||||||
"count" => sizeof($list),
|
"count" => $convosCount,
|
||||||
"items" => $list,
|
"items" => $list,
|
||||||
"profiles" => (!empty($users) ? (new APIUsers)->get(implode(',', $users), $fields, $offset, $count) : [])
|
"profiles" => (!empty($users) ? (new APIUsers)->get(implode(',', $users), $fields, 0, $count+1) : [])
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,6 +247,7 @@ final class Messages extends VKAPIRequestHandler
|
||||||
|
|
||||||
$user = (new USRRepo)->get((int) $peer);
|
$user = (new USRRepo)->get((int) $peer);
|
||||||
|
|
||||||
|
if($user) {
|
||||||
$dialogue = new Correspondence($this->getUser(), $user);
|
$dialogue = new Correspondence($this->getUser(), $user);
|
||||||
$iterator = $dialogue->getMessages(Correspondence::CAP_BEHAVIOUR_START_MESSAGE_ID, 0, 1, 0, false);
|
$iterator = $dialogue->getMessages(Correspondence::CAP_BEHAVIOUR_START_MESSAGE_ID, 0, 1, 0, false);
|
||||||
$msg = $iterator[0]->unwrap(); // шоб удобнее было
|
$msg = $iterator[0]->unwrap(); // шоб удобнее было
|
||||||
|
@ -273,6 +275,7 @@ final class Messages extends VKAPIRequestHandler
|
||||||
];
|
];
|
||||||
$userslist[] = $user->getId();
|
$userslist[] = $user->getId();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if($extended == 1) {
|
if($extended == 1) {
|
||||||
$userslist = array_unique($userslist);
|
$userslist = array_unique($userslist);
|
||||||
|
@ -283,7 +286,7 @@ final class Messages extends VKAPIRequestHandler
|
||||||
return (object) $output;
|
return (object) $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHistory(int $offset = 0, int $count = 20, int $user_id = -1, int $peer_id = -1, int $start_message_id = 0, int $rev = 0, int $extended = 0): object
|
function getHistory(int $offset = 0, int $count = 20, int $user_id = -1, int $peer_id = -1, int $start_message_id = 0, int $rev = 0, int $extended = 0, string $fields = ""): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
|
@ -315,10 +318,18 @@ final class Messages extends VKAPIRequestHandler
|
||||||
$results[] = $rMsg;
|
$results[] = $rMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (object) [
|
$output = [
|
||||||
"count" => sizeof($results),
|
"count" => sizeof($results),
|
||||||
"items" => $results,
|
"items" => $results,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ($extended == 1) {
|
||||||
|
$users[] = $this->getUser()->getId();
|
||||||
|
$users[] = $user_id;
|
||||||
|
$output["profiles"] = (!empty($users) ? (new APIUsers($this->getUser()))->get(implode(',', $users), $fields) : []);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLongPollHistory(int $ts = -1, int $preview_length = 0, int $events_limit = 1000, int $msgs_limit = 1000): object
|
function getLongPollHistory(int $ts = -1, int $preview_length = 0, int $events_limit = 1000, int $msgs_limit = 1000): object
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
namespace openvk\VKAPI\Handlers;
|
namespace openvk\VKAPI\Handlers;
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
||||||
|
use openvk\Web\Models\Entities\User;
|
||||||
use openvk\VKAPI\Handlers\Wall;
|
use openvk\VKAPI\Handlers\Wall;
|
||||||
|
|
||||||
final class Newsfeed extends VKAPIRequestHandler
|
final class Newsfeed extends VKAPIRequestHandler
|
||||||
|
@ -26,7 +27,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("created < (?)", empty($start_from) ? time()+1 : $start_from)
|
->where("id < (?)", empty($start_from) ? PHP_INT_MAX : $start_from)
|
||||||
->order("created DESC");
|
->order("created DESC");
|
||||||
|
|
||||||
$rposts = [];
|
$rposts = [];
|
||||||
|
@ -34,7 +35,33 @@ final class Newsfeed extends VKAPIRequestHandler
|
||||||
$rposts[] = (new PostsRepo)->get($post->id)->getPrettyId();
|
$rposts[] = (new PostsRepo)->get($post->id)->getPrettyId();
|
||||||
|
|
||||||
$response = (new Wall)->getById(implode(',', $rposts), $extended, $fields, $this->getUser());
|
$response = (new Wall)->getById(implode(',', $rposts), $extended, $fields, $this->getUser());
|
||||||
$response->next_from = end($response->items)->date;
|
$response->next_from = end(end($posts->page((int) ($offset + 1), $count))); // ну и костыли пиздец конечно)
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGlobal(string $fields = "", int $start_from = 0, int $offset = 0, int $count = 30, int $extended = 0)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$queryBase = "FROM `posts` LEFT JOIN `groups` ON GREATEST(`posts`.`wall`, 0) = 0 AND `groups`.`id` = ABS(`posts`.`wall`) WHERE (`groups`.`hide_from_global_feed` = 0 OR `groups`.`name` IS NULL) AND `posts`.`deleted` = 0";
|
||||||
|
|
||||||
|
if($this->getUser()->getNsfwTolerance() === User::NSFW_INTOLERANT)
|
||||||
|
$queryBase .= " AND `nsfw` = 0";
|
||||||
|
|
||||||
|
$start_from = empty($start_from) ? PHP_INT_MAX : $start_from;
|
||||||
|
$posts = DatabaseConnection::i()->getConnection()->query("SELECT `posts`.`id` " . $queryBase . " AND `posts`.`id` < " . $start_from . " ORDER BY `created` DESC LIMIT " . $count . " OFFSET " . $offset);
|
||||||
|
|
||||||
|
$rposts = [];
|
||||||
|
$ids = [];
|
||||||
|
foreach($posts as $post) {
|
||||||
|
$rposts[] = (new PostsRepo)->get($post->id)->getPrettyId();
|
||||||
|
$ids[] = $post->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = (new Wall)->getById(implode(',', $rposts), $extended, $fields, $this->getUser());
|
||||||
|
$response->next_from = end($ids);
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
105
VKAPI/Handlers/Polls.php
Executable file
105
VKAPI/Handlers/Polls.php
Executable file
|
@ -0,0 +1,105 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\VKAPI\Handlers;
|
||||||
|
use openvk\Web\Models\Entities\User;
|
||||||
|
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||||
|
use openvk\Web\Models\Entities\Poll;
|
||||||
|
use openvk\Web\Models\Exceptions\AlreadyVotedException;
|
||||||
|
use openvk\Web\Models\Exceptions\InvalidOptionException;
|
||||||
|
use openvk\Web\Models\Exceptions\PollLockedException;
|
||||||
|
use openvk\Web\Models\Repositories\Polls as PollsRepo;
|
||||||
|
|
||||||
|
final class Polls extends VKAPIRequestHandler
|
||||||
|
{
|
||||||
|
function getById(int $poll_id, bool $extended = false, string $fields = "sex,screen_name,photo_50,photo_100,online_info,online")
|
||||||
|
{
|
||||||
|
$poll = (new PollsRepo)->get($poll_id);
|
||||||
|
|
||||||
|
if (!$poll)
|
||||||
|
$this->fail(100, "One of the parameters specified was missing or invalid: poll_id is incorrect");
|
||||||
|
|
||||||
|
$users = array();
|
||||||
|
$answers = array();
|
||||||
|
foreach($poll->getResults()->options as $answer) {
|
||||||
|
$answers[] = (object)[
|
||||||
|
"id" => $answer->id,
|
||||||
|
"rate" => $answer->pct,
|
||||||
|
"text" => $answer->name,
|
||||||
|
"votes" => $answer->votes
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$userVote = array();
|
||||||
|
foreach($poll->getUserVote($this->getUser()) as $vote)
|
||||||
|
$userVote[] = $vote[0];
|
||||||
|
|
||||||
|
$response = [
|
||||||
|
"multiple" => $poll->isMultipleChoice(),
|
||||||
|
"end_date" => $poll->endsAt() == NULL ? 0 : $poll->endsAt()->timestamp(),
|
||||||
|
"closed" => $poll->hasEnded(),
|
||||||
|
"is_board" => false,
|
||||||
|
"can_edit" => false,
|
||||||
|
"can_vote" => $poll->canVote($this->getUser()),
|
||||||
|
"can_report" => false,
|
||||||
|
"can_share" => true,
|
||||||
|
"created" => 0,
|
||||||
|
"id" => $poll->getId(),
|
||||||
|
"owner_id" => $poll->getOwner()->getId(),
|
||||||
|
"question" => $poll->getTitle(),
|
||||||
|
"votes" => $poll->getVoterCount(),
|
||||||
|
"disable_unvote" => $poll->isRevotable(),
|
||||||
|
"anonymous" => $poll->isAnonymous(),
|
||||||
|
"answer_ids" => $userVote,
|
||||||
|
"answers" => $answers,
|
||||||
|
"author_id" => $poll->getOwner()->getId(),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($extended) {
|
||||||
|
$response["profiles"] = (new Users)->get(strval($poll->getOwner()->getId()), $fields, 0, 1);
|
||||||
|
/* Currently there is only one person that can be shown trough "Extended" param.
|
||||||
|
* As "friends" param will be implemented, "profiles" will show more users
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addVote(int $poll_id, string $answers_ids)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$poll = (new PollsRepo)->get($poll_id);
|
||||||
|
|
||||||
|
if(!$poll)
|
||||||
|
$this->fail(251, "Invalid poll id");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$poll->vote($this->getUser(), explode(",", $answers_ids));
|
||||||
|
return 0;
|
||||||
|
} catch(AlreadyVotedException $ex) {
|
||||||
|
return 0;
|
||||||
|
} catch(PollLockedException $ex) {
|
||||||
|
return 0;
|
||||||
|
} catch(InvalidOptionException $ex) {
|
||||||
|
$this->fail(8, "бдсм вибратор купить в киеве");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteVote(int $poll_id)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$poll = (new PollsRepo)->get($poll_id);
|
||||||
|
|
||||||
|
if(!$poll)
|
||||||
|
$this->fail(251, "Invalid poll id");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$poll->revokeVote($this->getUser());
|
||||||
|
return 0;
|
||||||
|
} catch(PollLockedException $ex) {
|
||||||
|
$this->fail(15, "Access denied: Poll is locked or isn't revotable");
|
||||||
|
} catch(InvalidOptionException $ex) {
|
||||||
|
$this->fail(8, "how.to. ook.bacon.in.microwova.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,10 +21,17 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$groups = [];
|
$groups = [];
|
||||||
$cnt = $posts->getPostCountOnUserWall($owner_id);
|
$cnt = $posts->getPostCountOnUserWall($owner_id);
|
||||||
|
|
||||||
|
if ($owner_id > 0)
|
||||||
$wallOnwer = (new UsersRepo)->get($owner_id);
|
$wallOnwer = (new UsersRepo)->get($owner_id);
|
||||||
|
else
|
||||||
|
$wallOnwer = (new ClubsRepo)->get($owner_id * -1);
|
||||||
|
|
||||||
if(!$wallOnwer || $wallOnwer->isDeleted() || $wallOnwer->isDeleted())
|
if ($owner_id > 0)
|
||||||
|
if(!$wallOnwer || $wallOnwer->isDeleted())
|
||||||
$this->fail(18, "User was deleted or banned");
|
$this->fail(18, "User was deleted or banned");
|
||||||
|
else
|
||||||
|
if(!$wallOnwer)
|
||||||
|
$this->fail(15, "Access denied: wall is disabled"); // Don't search for logic here pls
|
||||||
|
|
||||||
foreach($posts->getPostsFromUsersWall($owner_id, 1, $count, $offset) as $post) {
|
foreach($posts->getPostsFromUsersWall($owner_id, 1, $count, $offset) as $post) {
|
||||||
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
||||||
|
@ -37,12 +44,14 @@ final class Wall extends VKAPIRequestHandler
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$attachments[] = $this->getApiPhoto($attachment);
|
$attachments[] = $this->getApiPhoto($attachment);
|
||||||
|
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
||||||
|
$attachments[] = $this->getApiPoll($attachment, $this->getUser());
|
||||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||||
$repostAttachments = [];
|
$repostAttachments = [];
|
||||||
|
|
||||||
foreach($attachment->getChildren() as $repostAttachment) {
|
foreach($attachment->getChildren() as $repostAttachment) {
|
||||||
if($repostAttachment instanceof \openvk\Web\Models\Entities\Photo) {
|
if($repostAttachment instanceof \openvk\Web\Models\Entities\Photo) {
|
||||||
if($attachment->isDeleted())
|
if($repostAttachment->isDeleted())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$repostAttachments[] = $this->getApiPhoto($repostAttachment);
|
$repostAttachments[] = $this->getApiPhoto($repostAttachment);
|
||||||
|
@ -50,6 +59,11 @@ final class Wall extends VKAPIRequestHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($attachment->isPostedOnBehalfOfGroup())
|
||||||
|
$groups[] = $attachment->getOwner()->getId();
|
||||||
|
else
|
||||||
|
$profiles[] = $attachment->getOwner()->getId();
|
||||||
|
|
||||||
$repost[] = [
|
$repost[] = [
|
||||||
"id" => $attachment->getVirtualId(),
|
"id" => $attachment->getVirtualId(),
|
||||||
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
||||||
|
@ -178,6 +192,8 @@ final class Wall extends VKAPIRequestHandler
|
||||||
foreach($post->getChildren() as $attachment) {
|
foreach($post->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);
|
||||||
|
} else if($attachment instanceof \openvk\Web\Models\Entities\Poll) {
|
||||||
|
$attachments[] = $this->getApiPoll($attachment, $user);
|
||||||
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
} else if ($attachment instanceof \openvk\Web\Models\Entities\Post) {
|
||||||
$repostAttachments = [];
|
$repostAttachments = [];
|
||||||
|
|
||||||
|
@ -191,6 +207,11 @@ final class Wall extends VKAPIRequestHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($attachment->isPostedOnBehalfOfGroup())
|
||||||
|
$groups[] = $attachment->getOwner()->getId();
|
||||||
|
else
|
||||||
|
$profiles[] = $attachment->getOwner()->getId();
|
||||||
|
|
||||||
$repost[] = [
|
$repost[] = [
|
||||||
"id" => $attachment->getVirtualId(),
|
"id" => $attachment->getVirtualId(),
|
||||||
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
"owner_id" => $attachment->isPostedOnBehalfOfGroup() ? $attachment->getOwner()->getId() * -1 : $attachment->getOwner()->getId(),
|
||||||
|
@ -420,9 +441,14 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$profiles = [];
|
$profiles = [];
|
||||||
|
|
||||||
foreach($comments as $comment) {
|
foreach($comments as $comment) {
|
||||||
|
$owner = $comment->getOwner();
|
||||||
|
$oid = $owner->getId();
|
||||||
|
if($owner instanceof Club)
|
||||||
|
$oid *= -1;
|
||||||
|
|
||||||
$item = [
|
$item = [
|
||||||
"id" => $comment->getId(),
|
"id" => $comment->getId(),
|
||||||
"from_id" => $comment->getOwner()->getId(),
|
"from_id" => $oid,
|
||||||
"date" => $comment->getPublicationTime()->timestamp(),
|
"date" => $comment->getPublicationTime()->timestamp(),
|
||||||
"text" => $comment->getText(false),
|
"text" => $comment->getText(false),
|
||||||
"post_id" => $post->getVirtualId(),
|
"post_id" => $post->getVirtualId(),
|
||||||
|
@ -576,4 +602,44 @@ final class Wall extends VKAPIRequestHandler
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getApiPoll($attachment, $user) {
|
||||||
|
$answers = array();
|
||||||
|
foreach($attachment->getResults()->options as $answer) {
|
||||||
|
$answers[] = (object)[
|
||||||
|
"id" => $answer->id,
|
||||||
|
"rate" => $answer->pct,
|
||||||
|
"text" => $answer->name,
|
||||||
|
"votes" => $answer->votes
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$userVote = array();
|
||||||
|
foreach($attachment->getUserVote($user) as $vote)
|
||||||
|
$userVote[] = $vote[0];
|
||||||
|
|
||||||
|
return [
|
||||||
|
"type" => "poll",
|
||||||
|
"poll" => [
|
||||||
|
"multiple" => $attachment->isMultipleChoice(),
|
||||||
|
"end_date" => $attachment->endsAt() == NULL ? 0 : $attachment->endsAt()->timestamp(),
|
||||||
|
"closed" => $attachment->hasEnded(),
|
||||||
|
"is_board" => false,
|
||||||
|
"can_edit" => false,
|
||||||
|
"can_vote" => $attachment->canVote($user),
|
||||||
|
"can_report" => false,
|
||||||
|
"can_share" => true,
|
||||||
|
"created" => 0,
|
||||||
|
"id" => $attachment->getId(),
|
||||||
|
"owner_id" => $attachment->getOwner()->getId(),
|
||||||
|
"question" => $attachment->getTitle(),
|
||||||
|
"votes" => $attachment->getVoterCount(),
|
||||||
|
"disable_unvote" => $attachment->isRevotable(),
|
||||||
|
"anonymous" => $attachment->isAnonymous(),
|
||||||
|
"answer_ids" => $userVote,
|
||||||
|
"answers" => $answers,
|
||||||
|
"author_id" => $attachment->getOwner()->getId(),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
# VK API Compatability layer for OpenVK
|
# VK API Compatability layer for OpenVK
|
||||||
|
|
||||||
This directory contains VK api handlers, structures and relared
|
This directory contains VK API handlers, structures and relared
|
||||||
exceptions. It is still a work-in-progress functionality.
|
exceptions. It is still a work-in-progress functionality.
|
||||||
**Note**: requests to api are routed through
|
**Note**: requests to API are routed through
|
||||||
openvk.Web.Presenters.VKAPIPresenter, this dir contains only handlers.
|
openvk.Web.Presenters.VKAPIPresenter, this dir contains only handlers.
|
||||||
|
|
||||||
|
[Documentation for API clients](https://docs.openvk.su/openvk_engine/api/description/)
|
||||||
|
|
||||||
## Implementing API methods
|
## Implementing API methods
|
||||||
|
|
||||||
VK API methods have names like this: `example.test`. To implement a
|
VK API methods have names like this: `example.test`. To implement a
|
||||||
|
|
|
@ -27,14 +27,23 @@ class NewMessageEvent implements ILPEmitable
|
||||||
if($peer === $userId)
|
if($peer === $userId)
|
||||||
$peer = $msg->getRecipient()->getId();
|
$peer = $msg->getRecipient()->getId();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Source:
|
||||||
|
* https://github.com/danyadev/longpoll-doc
|
||||||
|
*/
|
||||||
|
|
||||||
return [
|
return [
|
||||||
4, # event type
|
4, # event type
|
||||||
|
$msg->getId(), # messageId
|
||||||
256, # checked for spam flag
|
256, # checked for spam flag
|
||||||
$peer, # TODO calculate peer correctly
|
$peer, # TODO calculate peer correctly
|
||||||
$msg->getSendTime()->timestamp(), # creation time in unix
|
$msg->getSendTime()->timestamp(), # creation time in unix
|
||||||
$msg->getText(), # text (formatted)
|
$msg->getText(), # text (formatted)
|
||||||
|
[], # empty additional info
|
||||||
[], # empty attachments
|
[], # empty attachments
|
||||||
$msg->getId() << 2, # id as random_id
|
$msg->getId() << 2, # id as random_id
|
||||||
|
$peer, # conversation id
|
||||||
|
0 # not edited yet
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
34
Web/Models/Entities/Alias.php
Normal file
34
Web/Models/Entities/Alias.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities;
|
||||||
|
|
||||||
|
use openvk\Web\Models\RowModel;
|
||||||
|
use openvk\Web\Models\Entities\{User, Club};
|
||||||
|
use openvk\Web\Models\Repositories\{Users, Clubs};
|
||||||
|
|
||||||
|
class Alias extends RowModel
|
||||||
|
{
|
||||||
|
protected $tableName = "aliases";
|
||||||
|
|
||||||
|
function getOwnerId(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->owner_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getType(): string
|
||||||
|
{
|
||||||
|
if ($this->getOwnerId() < 0)
|
||||||
|
return "club";
|
||||||
|
|
||||||
|
return "user";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUser(): ?User
|
||||||
|
{
|
||||||
|
return (new Users)->get($this->getOwnerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClub(): ?Club
|
||||||
|
{
|
||||||
|
return (new Clubs)->get($this->getOwnerId() * -1);
|
||||||
|
}
|
||||||
|
}
|
49
Web/Models/Entities/BannedLink.php
Normal file
49
Web/Models/Entities/BannedLink.php
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use openvk\Web\Models\RowModel;
|
||||||
|
use openvk\Web\Models\Entities\{User};
|
||||||
|
use openvk\Web\Models\Repositories\{Users};
|
||||||
|
use Nette\Database\Table\ActiveRow;
|
||||||
|
|
||||||
|
class BannedLink extends RowModel
|
||||||
|
{
|
||||||
|
protected $tableName = "links_banned";
|
||||||
|
private $overrideContentColumn = "reason";
|
||||||
|
|
||||||
|
function getId(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDomain(): string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReason(): string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->reason ?? tr("url_is_banned_default_reason");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInitiator(): ?User
|
||||||
|
{
|
||||||
|
return (new Users)->get($this->getRecord()->initiator);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getComment(): string
|
||||||
|
{
|
||||||
|
return OPENVK_ROOT_CONF["openvk"]["preferences"]["susLinks"]["showReason"]
|
||||||
|
? tr("url_is_banned_comment_r", OPENVK_ROOT_CONF["openvk"]["appearance"]["name"], $this->getReason())
|
||||||
|
: tr("url_is_banned_comment", OPENVK_ROOT_CONF["openvk"]["appearance"]["name"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRegexpRule(): string
|
||||||
|
{
|
||||||
|
return addslashes("/" . $this->getDomain() . $this->getRawRegexp() . "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRawRegexp(): string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->regexp_rule;
|
||||||
|
}
|
||||||
|
}
|
295
Web/Models/Entities/Poll.php
Normal file
295
Web/Models/Entities/Poll.php
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use openvk\Web\Models\Exceptions\TooMuchOptionsException;
|
||||||
|
use openvk\Web\Util\DateTime;
|
||||||
|
use \UnexpectedValueException;
|
||||||
|
use Nette\InvalidStateException;
|
||||||
|
use openvk\Web\Models\Repositories\Users;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use openvk\Web\Models\Exceptions\PollLockedException;
|
||||||
|
use openvk\Web\Models\Exceptions\AlreadyVotedException;
|
||||||
|
use openvk\Web\Models\Exceptions\InvalidOptionException;
|
||||||
|
|
||||||
|
class Poll extends Attachable
|
||||||
|
{
|
||||||
|
protected $tableName = "polls";
|
||||||
|
|
||||||
|
private $choicesToPersist = [];
|
||||||
|
|
||||||
|
function getTitle(): string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMetaDescription(): string
|
||||||
|
{
|
||||||
|
$props = [];
|
||||||
|
$props[] = tr($this->isAnonymous() ? "poll_anon" : "poll_public");
|
||||||
|
if($this->isMultipleChoice()) $props[] = tr("poll_multi");
|
||||||
|
if(!$this->isRevotable()) $props[] = tr("poll_lock");
|
||||||
|
if(!is_null($this->endsAt())) $props[] = tr("poll_until", $this->endsAt());
|
||||||
|
|
||||||
|
return implode(" • ", $props);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOwner(): User
|
||||||
|
{
|
||||||
|
return (new Users)->get($this->getRecord()->owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOptions(): array
|
||||||
|
{
|
||||||
|
$options = $this->getRecord()->related("poll_options.poll");
|
||||||
|
$res = [];
|
||||||
|
foreach($options as $opt)
|
||||||
|
$res[$opt->id] = $opt->name;
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserVote(User $user): ?array
|
||||||
|
{
|
||||||
|
$ctx = DatabaseConnection::i()->getContext();
|
||||||
|
$votedOpts = $ctx->table("poll_votes")
|
||||||
|
->where(["user" => $user->getId(), "poll" => $this->getId()]);
|
||||||
|
|
||||||
|
if($votedOpts->count() == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
$res = [];
|
||||||
|
foreach($votedOpts as $votedOpt) {
|
||||||
|
$option = $ctx->table("poll_options")->get($votedOpt->option);
|
||||||
|
$res[] = [$option->id, $option->name];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVoters(int $optionId, int $page = 1, ?int $perPage = NULL): array
|
||||||
|
{
|
||||||
|
$res = [];
|
||||||
|
$ctx = DatabaseConnection::i()->getContext();
|
||||||
|
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||||
|
$voters = $ctx->table("poll_votes")->where(["poll" => $this->getId(), "option" => $optionId]);
|
||||||
|
foreach($voters->page($page, $perPage) as $vote)
|
||||||
|
$res[] = (new Users)->get($vote->user);
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVoterCount(?int $optionId = NULL): int
|
||||||
|
{
|
||||||
|
$votes = DatabaseConnection::i()->getContext()->table("poll_votes");
|
||||||
|
if(!$optionId)
|
||||||
|
return $votes->select("COUNT(DISTINCT user) AS c")->where("poll", $this->getId())->fetch()->c;
|
||||||
|
|
||||||
|
return $votes->where(["poll" => $this->getId(), "option" => $optionId])->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResults(?User $user = NULL): object
|
||||||
|
{
|
||||||
|
$ctx = DatabaseConnection::i()->getContext();
|
||||||
|
$voted = NULL;
|
||||||
|
if(!is_null($user))
|
||||||
|
$voted = $this->getUserVote($user);
|
||||||
|
|
||||||
|
$result = (object) [];
|
||||||
|
$result->totalVotes = $this->getVoterCount();
|
||||||
|
|
||||||
|
$unsOptions = [];
|
||||||
|
foreach($this->getOptions() as $id => $title) {
|
||||||
|
$option = (object) [];
|
||||||
|
$option->id = $id;
|
||||||
|
$option->name = $title;
|
||||||
|
|
||||||
|
$option->votes = $this->getVoterCount($id);
|
||||||
|
$option->pct = $result->totalVotes == 0 ? 0 : min(100, floor(($option->votes / $result->totalVotes) * 100));
|
||||||
|
$option->voters = $this->getVoters($id, 1, 10);
|
||||||
|
if(!$user || !$voted)
|
||||||
|
$option->voted = NULL;
|
||||||
|
else
|
||||||
|
$option->voted = in_array([$id, $title], $voted);
|
||||||
|
|
||||||
|
$unsOptions[$id] = $option;
|
||||||
|
}
|
||||||
|
|
||||||
|
$optionsC = sizeof($unsOptions);
|
||||||
|
$sOptions = $unsOptions;
|
||||||
|
usort($sOptions, function($a, $b) { return $a->votes <=> $b->votes; });
|
||||||
|
for($i = 0; $i < $optionsC; $i++)
|
||||||
|
$unsOptions[$id]->rate = $optionsC - $i - 1;
|
||||||
|
|
||||||
|
$result->options = array_values($unsOptions);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAnonymous(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->is_anonymous;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMultipleChoice(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->allows_multiple;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRevotable(): bool
|
||||||
|
{
|
||||||
|
return (bool) $this->getRecord()->can_revote;
|
||||||
|
}
|
||||||
|
|
||||||
|
function endsAt(): ?DateTime
|
||||||
|
{
|
||||||
|
if(!$this->getRecord()->until)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return new DateTime($this->getRecord()->until);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasEnded(): bool
|
||||||
|
{
|
||||||
|
if($this->getRecord()->ended)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(!is_null($this->getRecord()->until))
|
||||||
|
return time() >= $this->getRecord()->until;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasVoted(User $user): bool
|
||||||
|
{
|
||||||
|
return !is_null($this->getUserVote($user));
|
||||||
|
}
|
||||||
|
|
||||||
|
function canVote(User $user): bool
|
||||||
|
{
|
||||||
|
return !$this->hasEnded() && !$this->hasVoted($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function vote(User $user, array $optionIds): void
|
||||||
|
{
|
||||||
|
if($this->hasEnded())
|
||||||
|
throw new PollLockedException;
|
||||||
|
|
||||||
|
if($this->hasVoted($user))
|
||||||
|
throw new AlreadyVotedException;
|
||||||
|
|
||||||
|
$optionIds = array_map(function($x) { return (int) $x; }, array_unique($optionIds));
|
||||||
|
$validOpts = array_keys($this->getOptions());
|
||||||
|
if(empty($optionIds) || (sizeof($optionIds) > 1 && !$this->isMultipleChoice()))
|
||||||
|
throw new UnexpectedValueException;
|
||||||
|
|
||||||
|
if(sizeof(array_diff($optionIds, $validOpts)) > 0)
|
||||||
|
throw new InvalidOptionException;
|
||||||
|
|
||||||
|
foreach($optionIds as $opt) {
|
||||||
|
DatabaseConnection::i()->getContext()->table("poll_votes")->insert([
|
||||||
|
"user" => $user->getId(),
|
||||||
|
"poll" => $this->getId(),
|
||||||
|
"option" => $opt,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function revokeVote(User $user): void
|
||||||
|
{
|
||||||
|
if(!$this->isRevotable())
|
||||||
|
throw new PollLockedException;
|
||||||
|
|
||||||
|
$this->getRecord()->related("poll_votes.poll")
|
||||||
|
->where("user", $user->getId())->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setOwner(User $owner): void
|
||||||
|
{
|
||||||
|
$this->stateChanges("owner", $owner->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
function setEndDate(int $timestamp): void
|
||||||
|
{
|
||||||
|
if(!is_null($this->getRecord()))
|
||||||
|
throw new PollLockedException;
|
||||||
|
|
||||||
|
$this->stateChanges("until", $timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setEnded(): void
|
||||||
|
{
|
||||||
|
$this->stateChanges("ended", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setOptions(array $options): void
|
||||||
|
{
|
||||||
|
if(!is_null($this->getRecord()))
|
||||||
|
throw new PollLockedException;
|
||||||
|
|
||||||
|
if(sizeof($options) > ovkGetQuirk("polls.max-opts"))
|
||||||
|
throw new TooMuchOptionsException;
|
||||||
|
|
||||||
|
$this->choicesToPersist = $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRevotability(bool $canReVote): void
|
||||||
|
{
|
||||||
|
if(!is_null($this->getRecord()))
|
||||||
|
throw new PollLockedException;
|
||||||
|
|
||||||
|
$this->stateChanges("can_revote", $canReVote);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAnonymity(bool $anonymous): void
|
||||||
|
{
|
||||||
|
$this->stateChanges("is_anonymous", $anonymous);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMultipleChoice(bool $mc): void
|
||||||
|
{
|
||||||
|
$this->stateChanges("allows_multiple", $mc);
|
||||||
|
}
|
||||||
|
|
||||||
|
function importXML(User $owner, string $xml): void
|
||||||
|
{
|
||||||
|
$xml = simplexml_load_string($xml);
|
||||||
|
$this->setOwner($owner);
|
||||||
|
$this->setTitle($xml["title"] ?? "Untitled");
|
||||||
|
$this->setMultipleChoice(($xml["multiple"] ?? "no") == "yes");
|
||||||
|
$this->setAnonymity(($xml["anonymous"] ?? "no") == "yes");
|
||||||
|
$this->setRevotability(($xml["locked"] ?? "no") == "no");
|
||||||
|
if(ctype_digit((string) ($xml["duration"] ?? "")))
|
||||||
|
$this->setEndDate(time() + ((86400 * (int) $xml["duration"])));
|
||||||
|
|
||||||
|
$options = [];
|
||||||
|
foreach($xml->options->option as $opt)
|
||||||
|
$options[] = (string) $opt;
|
||||||
|
|
||||||
|
if(empty($options))
|
||||||
|
throw new UnexpectedValueException;
|
||||||
|
|
||||||
|
$this->setOptions($options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function import(User $owner, string $xml): Poll
|
||||||
|
{
|
||||||
|
$poll = new Poll;
|
||||||
|
$poll->importXML($owner, $xml);
|
||||||
|
$poll->save();
|
||||||
|
|
||||||
|
return $poll;
|
||||||
|
}
|
||||||
|
|
||||||
|
function save(): void
|
||||||
|
{
|
||||||
|
if(empty($this->choicesToPersist))
|
||||||
|
throw new InvalidStateException;
|
||||||
|
|
||||||
|
parent::save();
|
||||||
|
foreach($this->choicesToPersist as $option) {
|
||||||
|
DatabaseConnection::i()->getContext()->table("poll_options")->insert([
|
||||||
|
"poll" => $this->getId(),
|
||||||
|
"name" => $option,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Entities;
|
namespace openvk\Web\Models\Entities;
|
||||||
use Chandler\Database\DatabaseConnection as DB;
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
use openvk\Web\Models\Repositories\Clubs;
|
use openvk\Web\Models\Repositories\{Clubs, Users};
|
||||||
use openvk\Web\Models\RowModel;
|
use openvk\Web\Models\RowModel;
|
||||||
use openvk\Web\Models\Entities\Notifications\LikeNotification;
|
use openvk\Web\Models\Entities\Notifications\LikeNotification;
|
||||||
|
|
||||||
|
@ -56,6 +56,15 @@ class Post extends Postable
|
||||||
return $this->getRecord()->wall;
|
return $this->getRecord()->wall;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWallOwner()
|
||||||
|
{
|
||||||
|
$w = $this->getRecord()->wall;
|
||||||
|
if($w < 0)
|
||||||
|
return (new Clubs)->get(abs($w));
|
||||||
|
|
||||||
|
return (new Users)->get($w);
|
||||||
|
}
|
||||||
|
|
||||||
function getRepostCount(): int
|
function getRepostCount(): int
|
||||||
{
|
{
|
||||||
return sizeof(
|
return sizeof(
|
||||||
|
@ -87,7 +96,7 @@ class Post extends Postable
|
||||||
|
|
||||||
function isDeactivationMessage(): bool
|
function isDeactivationMessage(): bool
|
||||||
{
|
{
|
||||||
return ($this->getRecord()->flags & 0b00100000) > 0;
|
return (($this->getRecord()->flags & 0b00100000) > 0) && ($this->getRecord()->owner > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isExplicit(): bool
|
function isExplicit(): bool
|
||||||
|
|
39
Web/Models/Entities/SupportAgent.php
Normal file
39
Web/Models/Entities/SupportAgent.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use openvk\Web\Models\Repositories\Users;
|
||||||
|
use openvk\Web\Models\RowModel;
|
||||||
|
|
||||||
|
class SupportAgent extends RowModel
|
||||||
|
{
|
||||||
|
protected $tableName = "support_names";
|
||||||
|
|
||||||
|
function getAgentId(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getName(): ?string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCanonicalName(): string
|
||||||
|
{
|
||||||
|
return $this->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAvatarURL(): ?string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isShowNumber(): int
|
||||||
|
{
|
||||||
|
return $this->getRecord()->numerate;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRealName(): string
|
||||||
|
{
|
||||||
|
return (new Users)->get($this->getAgentId())->getCanonicalName();
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,7 +42,7 @@ class TicketComment extends RowModel
|
||||||
|
|
||||||
$alias = $this->getSupportAlias();
|
$alias = $this->getSupportAlias();
|
||||||
if(!$alias)
|
if(!$alias)
|
||||||
return OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["supportName"] . " №" . $this->getAgentNumber();
|
return tr("helpdesk_agent") . " #" . $this->getAgentNumber();
|
||||||
|
|
||||||
$name = $alias->getName();
|
$name = $alias->getName();
|
||||||
if($alias->shouldAppendNumber())
|
if($alias->shouldAppendNumber())
|
||||||
|
|
|
@ -35,12 +35,12 @@ trait TRichText
|
||||||
"%(([A-z]++):\/\/(\S*?\.\S*?))([\s)\[\]{},\"\'<]|\.\s|$)%",
|
"%(([A-z]++):\/\/(\S*?\.\S*?))([\s)\[\]{},\"\'<]|\.\s|$)%",
|
||||||
(function (array $matches): string {
|
(function (array $matches): string {
|
||||||
$href = str_replace("#", "#", $matches[1]);
|
$href = str_replace("#", "#", $matches[1]);
|
||||||
$href = str_replace(";", ";", $matches[1]);
|
$href = rawurlencode(str_replace(";", ";", $matches[1]));
|
||||||
$link = str_replace("#", "#", $matches[3]);
|
$link = str_replace("#", "#", $matches[3]);
|
||||||
$link = str_replace(";", ";", $matches[3]);
|
$link = str_replace(";", ";", $matches[3]);
|
||||||
$rel = $this->isAd() ? "sponsored" : "ugc";
|
$rel = $this->isAd() ? "sponsored" : "ugc";
|
||||||
|
|
||||||
return "<a href='$href' rel='$rel' target='_blank'>$link</a>" . htmlentities($matches[4]);
|
return "<a href='/away.php?to=$href' rel='$rel' target='_blank'>$link</a>" . htmlentities($matches[4]);
|
||||||
}),
|
}),
|
||||||
$text
|
$text
|
||||||
);
|
);
|
||||||
|
|
|
@ -148,8 +148,9 @@ class User extends RowModel
|
||||||
function getFirstName(bool $pristine = false): string
|
function getFirstName(bool $pristine = false): string
|
||||||
{
|
{
|
||||||
$name = ($this->isDeleted() && !$this->isDeactivated() ? "DELETED" : mb_convert_case($this->getRecord()->first_name, MB_CASE_TITLE));
|
$name = ($this->isDeleted() && !$this->isDeactivated() ? "DELETED" : mb_convert_case($this->getRecord()->first_name, MB_CASE_TITLE));
|
||||||
if((($ts = tr("__transNames")) !== "@__transNames") && !$pristine)
|
$tsn = tr("__transNames");
|
||||||
return mb_convert_case(transliterator_transliterate($ts, $name), MB_CASE_TITLE);
|
if(( $tsn !== "@__transNames" && !empty($tsn) ) && !$pristine)
|
||||||
|
return mb_convert_case(transliterator_transliterate($tsn, $name), MB_CASE_TITLE);
|
||||||
else
|
else
|
||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
|
@ -157,8 +158,9 @@ class User extends RowModel
|
||||||
function getLastName(bool $pristine = false): string
|
function getLastName(bool $pristine = false): string
|
||||||
{
|
{
|
||||||
$name = ($this->isDeleted() && !$this->isDeactivated() ? "DELETED" : mb_convert_case($this->getRecord()->last_name, MB_CASE_TITLE));
|
$name = ($this->isDeleted() && !$this->isDeactivated() ? "DELETED" : mb_convert_case($this->getRecord()->last_name, MB_CASE_TITLE));
|
||||||
if((($ts = tr("__transNames")) !== "@__transNames") && !$pristine)
|
$tsn = tr("__transNames");
|
||||||
return mb_convert_case(transliterator_transliterate($ts, $name), MB_CASE_TITLE);
|
if(( $tsn !== "@__transNames" && !empty($tsn) ) && !$pristine)
|
||||||
|
return mb_convert_case(transliterator_transliterate($tsn, $name), MB_CASE_TITLE);
|
||||||
else
|
else
|
||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
|
@ -535,12 +537,15 @@ class User extends RowModel
|
||||||
return sizeof(DatabaseConnection::i()->getContext()->table("messages")->where(["recipient_id" => $this->getId(), "unread" => 1]));
|
return sizeof(DatabaseConnection::i()->getContext()->table("messages")->where(["recipient_id" => $this->getId(), "unread" => 1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getClubs(int $page = 1, bool $admin = false): \Traversable
|
function getClubs(int $page = 1, bool $admin = false, int $count = OPENVK_DEFAULT_PER_PAGE, bool $offset = false): \Traversable
|
||||||
{
|
{
|
||||||
|
if(!$offset)
|
||||||
|
$page = ($page - 1) * $count;
|
||||||
|
|
||||||
if($admin) {
|
if($admin) {
|
||||||
$id = $this->getId();
|
$id = $this->getId();
|
||||||
$query = "SELECT `id` FROM `groups` WHERE `owner` = ? UNION SELECT `club` as `id` FROM `group_coadmins` WHERE `user` = ?";
|
$query = "SELECT `id` FROM `groups` WHERE `owner` = ? UNION SELECT `club` as `id` FROM `group_coadmins` WHERE `user` = ?";
|
||||||
$query .= " LIMIT " . OPENVK_DEFAULT_PER_PAGE . " OFFSET " . ($page - 1) * OPENVK_DEFAULT_PER_PAGE;
|
$query .= " LIMIT " . $count . " OFFSET " . $page;
|
||||||
|
|
||||||
$sel = DatabaseConnection::i()->getConnection()->query($query, $id, $id);
|
$sel = DatabaseConnection::i()->getConnection()->query($query, $id, $id);
|
||||||
foreach($sel as $target) {
|
foreach($sel as $target) {
|
||||||
|
@ -550,7 +555,7 @@ class User extends RowModel
|
||||||
yield $target;
|
yield $target;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$sel = $this->getRecord()->related("subscriptions.follower")->page($page, OPENVK_DEFAULT_PER_PAGE);
|
$sel = $this->getRecord()->related("subscriptions.follower")->limit($count, $page);
|
||||||
foreach($sel->where("model", "openvk\\Web\\Models\\Entities\\Club") as $target) {
|
foreach($sel->where("model", "openvk\\Web\\Models\\Entities\\Club") as $target) {
|
||||||
$target = (new Clubs)->get($target->target);
|
$target = (new Clubs)->get($target->target);
|
||||||
if(!$target) continue;
|
if(!$target) continue;
|
||||||
|
@ -768,7 +773,7 @@ class User extends RowModel
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ban(string $reason, bool $deleteSubscriptions = true): void
|
function ban(string $reason, bool $deleteSubscriptions = true, ?int $unban_time = NULL): void
|
||||||
{
|
{
|
||||||
if($deleteSubscriptions) {
|
if($deleteSubscriptions) {
|
||||||
$subs = DatabaseConnection::i()->getContext()->table("subscriptions");
|
$subs = DatabaseConnection::i()->getContext()->table("subscriptions");
|
||||||
|
@ -782,6 +787,7 @@ class User extends RowModel
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->setBlock_Reason($reason);
|
$this->setBlock_Reason($reason);
|
||||||
|
$this->setUnblock_time($unban_time);
|
||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,6 +913,10 @@ class User extends RowModel
|
||||||
$pClub = DatabaseConnection::i()->getContext()->table("groups")->where("shortcode", $code)->fetch();
|
$pClub = DatabaseConnection::i()->getContext()->table("groups")->where("shortcode", $code)->fetch();
|
||||||
if(!is_null($pClub))
|
if(!is_null($pClub))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
$pAlias = DatabaseConnection::i()->getContext()->table("aliases")->where("shortcode", $code)->fetch();
|
||||||
|
if(!is_null($pAlias))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->stateChanges("shortcode", $code);
|
$this->stateChanges("shortcode", $code);
|
||||||
|
@ -1018,5 +1028,21 @@ class User extends RowModel
|
||||||
return (bool) $this->getRecord()->activated;
|
return (bool) $this->getRecord()->activated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getUnbanTime(): ?string
|
||||||
|
{
|
||||||
|
return !is_null($this->getRecord()->unblock_time) ? date('d.m.Y', $this->getRecord()->unblock_time) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function canUnbanThemself(): bool
|
||||||
|
{
|
||||||
|
if (!$this->isBanned())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ($this->getRecord()->unblock_time > time() || $this->getRecord()->unblock_time == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
use Traits\TSubscribable;
|
use Traits\TSubscribable;
|
||||||
}
|
}
|
||||||
|
|
7
Web/Models/Exceptions/AlreadyVotedException.php
Normal file
7
Web/Models/Exceptions/AlreadyVotedException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Exceptions;
|
||||||
|
|
||||||
|
final class AlreadyVotedException extends \RuntimeException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
7
Web/Models/Exceptions/InvalidOptionException.php
Normal file
7
Web/Models/Exceptions/InvalidOptionException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Exceptions;
|
||||||
|
|
||||||
|
final class InvalidOptionException extends \UnexpectedValueException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
8
Web/Models/Exceptions/PollLockedException.php
Normal file
8
Web/Models/Exceptions/PollLockedException.php
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Exceptions;
|
||||||
|
use Nette\InvalidStateException;
|
||||||
|
|
||||||
|
final class PollLockedException extends InvalidStateException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
7
Web/Models/Exceptions/TooMuchOptionsException.php
Normal file
7
Web/Models/Exceptions/TooMuchOptionsException.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Exceptions;
|
||||||
|
|
||||||
|
final class TooMuchOptionsException extends \UnexpectedValueException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
35
Web/Models/Repositories/Aliases.php
Normal file
35
Web/Models/Repositories/Aliases.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Repositories;
|
||||||
|
|
||||||
|
use openvk\Web\Models\Entities\Alias;
|
||||||
|
use Nette\Database\Table\ActiveRow;
|
||||||
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
|
use openvk\Web\Models\Entities\{Club, User};
|
||||||
|
use openvk\Web\Models\Repositories\{Clubs, Users};
|
||||||
|
|
||||||
|
class Aliases
|
||||||
|
{
|
||||||
|
private $context;
|
||||||
|
private $aliases;
|
||||||
|
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->context = DB::i()->getContext();
|
||||||
|
$this->aliases = $this->context->table("aliases");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function toAlias(?ActiveRow $ar): ?Alias
|
||||||
|
{
|
||||||
|
return is_null($ar) ? NULL : new Alias($ar);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(int $id): ?Alias
|
||||||
|
{
|
||||||
|
return $this->toAlias($this->aliases->get($id));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getByShortcode(string $shortcode): ?Alias
|
||||||
|
{
|
||||||
|
return $this->toAlias($this->aliases->where("shortcode", $shortcode)->fetch());
|
||||||
|
}
|
||||||
|
}
|
73
Web/Models/Repositories/BannedLinks.php
Normal file
73
Web/Models/Repositories/BannedLinks.php
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Repositories;
|
||||||
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
|
use Nette\Database\Table\{ActiveRow, Selection};
|
||||||
|
use openvk\Web\Models\Entities\BannedLink;
|
||||||
|
|
||||||
|
class BannedLinks
|
||||||
|
{
|
||||||
|
private $context;
|
||||||
|
private $bannedLinks;
|
||||||
|
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->context = DB::i()->getContext();
|
||||||
|
$this->bannedLinks = $this->context->table("links_banned");
|
||||||
|
}
|
||||||
|
|
||||||
|
function toBannedLink(?ActiveRow $ar): ?BannedLink
|
||||||
|
{
|
||||||
|
return is_null($ar) ? NULL : new BannedLink($ar);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(int $id): ?BannedLink
|
||||||
|
{
|
||||||
|
return $this->toBannedLink($this->bannedLinks->get($id));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getList(?int $page = 1): \Traversable
|
||||||
|
{
|
||||||
|
foreach($this->bannedLinks->order("id DESC")->page($page, OPENVK_DEFAULT_PER_PAGE) as $link)
|
||||||
|
yield new BannedLink($link);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCount(int $page = 1): int
|
||||||
|
{
|
||||||
|
return sizeof($this->bannedLinks->fetch());
|
||||||
|
}
|
||||||
|
|
||||||
|
function getByDomain(string $domain): ?Selection
|
||||||
|
{
|
||||||
|
return $this->bannedLinks->where("domain", $domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDomainBanned(string $domain): bool
|
||||||
|
{
|
||||||
|
return sizeof($this->bannedLinks->where(["link" => $domain, "regexp_rule" => ""])) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function genLinks($rules): \Traversable
|
||||||
|
{
|
||||||
|
foreach ($rules as $rule)
|
||||||
|
yield $this->get($rule->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function genEntries($links, $uri): \Traversable
|
||||||
|
{
|
||||||
|
foreach($links as $link)
|
||||||
|
if (preg_match($link->getRegexpRule(), $uri))
|
||||||
|
yield $link->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
function check(string $url): ?array
|
||||||
|
{
|
||||||
|
$uri = strstr(str_replace(["https://", "http://"], "", $url), "/", true);
|
||||||
|
$domain = str_replace("www.", "", $uri);
|
||||||
|
$rules = $this->getByDomain($domain);
|
||||||
|
|
||||||
|
if (is_null($rules))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return iterator_to_array($this->genEntries($this->genLinks($rules), $uri));
|
||||||
|
}
|
||||||
|
}
|
48
Web/Models/Repositories/ChandlerGroups.php
Normal file
48
Web/Models/Repositories/ChandlerGroups.php
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Repositories;
|
||||||
|
use Nette\Database\Table\ActiveRow;
|
||||||
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
|
use openvk\Web\Models\Entities\User;
|
||||||
|
use Chandler\Security\User as ChandlerUser;
|
||||||
|
|
||||||
|
class ChandlerGroups
|
||||||
|
{
|
||||||
|
private $context;
|
||||||
|
private $groups;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->context = DB::i()->getContext();
|
||||||
|
$this->groups = $this->context->table("ChandlerGroups");
|
||||||
|
$this->members = $this->context->table("ChandlerACLRelations");
|
||||||
|
$this->perms = $this->context->table("ChandlerACLGroupsPermissions");
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(string $UUID): ?ActiveRow
|
||||||
|
{
|
||||||
|
return $this->groups->where("id", $UUID)->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getList(): \Traversable
|
||||||
|
{
|
||||||
|
foreach($this->groups as $group) yield $group;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMembersById(string $UUID): \Traversable
|
||||||
|
{
|
||||||
|
foreach($this->members->where("group", $UUID) as $member)
|
||||||
|
yield (new Users)->getByChandlerUser(
|
||||||
|
new ChandlerUser($this->context->table("ChandlerUsers")->where("id", $member->user)->fetch())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUsersMemberships(string $UUID): \Traversable
|
||||||
|
{
|
||||||
|
foreach($this->members->where("user", $UUID) as $member) yield $member;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPermissionsById(string $UUID): \Traversable
|
||||||
|
{
|
||||||
|
foreach($this->perms->where("group", $UUID) as $perm) yield $perm;
|
||||||
|
}
|
||||||
|
}
|
39
Web/Models/Repositories/ChandlerUsers.php
Normal file
39
Web/Models/Repositories/ChandlerUsers.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Repositories;
|
||||||
|
use Nette\Database\Table\ActiveRow;
|
||||||
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
|
use openvk\Web\Models\Entities\User;
|
||||||
|
use Chandler\Security\User as ChandlerUser;
|
||||||
|
|
||||||
|
class ChandlerUsers
|
||||||
|
{
|
||||||
|
private $context;
|
||||||
|
private $users;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->context = DB::i()->getContext();
|
||||||
|
$this->users = $this->context->table("ChandlerUsers");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function toUser(?ActiveRow $ar): ?ChandlerUser
|
||||||
|
{
|
||||||
|
return is_null($ar) ? NULL : (new User($ar))->getChandlerUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(int $id): ?ChandlerUser
|
||||||
|
{
|
||||||
|
return (new Users)->get($id)->getChandlerUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getById(string $UUID): ?ChandlerUser
|
||||||
|
{
|
||||||
|
return new ChandlerUser($this->users->where("id", $UUID)->fetch());
|
||||||
|
}
|
||||||
|
|
||||||
|
function getList(int $page = 1): \Traversable
|
||||||
|
{
|
||||||
|
foreach($this->users as $user)
|
||||||
|
yield new ChandlerUser($user);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Repositories;
|
namespace openvk\Web\Models\Repositories;
|
||||||
use openvk\Web\Models\Entities\Club;
|
use openvk\Web\Models\Entities\Club;
|
||||||
|
use openvk\Web\Models\Repositories\Aliases;
|
||||||
use Nette\Database\Table\ActiveRow;
|
use Nette\Database\Table\ActiveRow;
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
|
||||||
|
@ -22,7 +23,17 @@ class Clubs
|
||||||
|
|
||||||
function getByShortURL(string $url): ?Club
|
function getByShortURL(string $url): ?Club
|
||||||
{
|
{
|
||||||
return $this->toClub($this->clubs->where("shortcode", $url)->fetch());
|
$shortcode = $this->toClub($this->clubs->where("shortcode", $url)->fetch());
|
||||||
|
|
||||||
|
if ($shortcode)
|
||||||
|
return $shortcode;
|
||||||
|
|
||||||
|
$alias = (new Aliases)->getByShortcode($url);
|
||||||
|
|
||||||
|
if (!$alias) return NULL;
|
||||||
|
if ($alias->getType() !== "club") return NULL;
|
||||||
|
|
||||||
|
return $alias->getClub();
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(int $id): ?Club
|
function get(int $id): ?Club
|
||||||
|
@ -45,6 +56,9 @@ class Clubs
|
||||||
|
|
||||||
function getPopularClubs(): \Traversable
|
function getPopularClubs(): \Traversable
|
||||||
{
|
{
|
||||||
|
// TODO rewrite
|
||||||
|
|
||||||
|
/*
|
||||||
$query = "SELECT ROW_NUMBER() OVER (ORDER BY `subscriptions` DESC) as `place`, `target` as `id`, COUNT(`follower`) as `subscriptions` FROM `subscriptions` WHERE `model` = \"openvk\\\Web\\\Models\\\Entities\\\Club\" GROUP BY `target` ORDER BY `subscriptions` DESC, `id` LIMIT 30;";
|
$query = "SELECT ROW_NUMBER() OVER (ORDER BY `subscriptions` DESC) as `place`, `target` as `id`, COUNT(`follower`) as `subscriptions` FROM `subscriptions` WHERE `model` = \"openvk\\\Web\\\Models\\\Entities\\\Club\" GROUP BY `target` ORDER BY `subscriptions` DESC, `id` LIMIT 30;";
|
||||||
$entries = DatabaseConnection::i()->getConnection()->query($query);
|
$entries = DatabaseConnection::i()->getConnection()->query($query);
|
||||||
|
|
||||||
|
@ -54,6 +68,7 @@ class Clubs
|
||||||
"club" => $this->get($entry["id"]),
|
"club" => $this->get($entry["id"]),
|
||||||
"subscriptions" => $entry["subscriptions"],
|
"subscriptions" => $entry["subscriptions"],
|
||||||
];
|
];
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
use \Nette\SmartObject;
|
use \Nette\SmartObject;
|
||||||
|
|
23
Web/Models/Repositories/Polls.php
Normal file
23
Web/Models/Repositories/Polls.php
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Repositories;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use openvk\Web\Models\Entities\Poll;
|
||||||
|
|
||||||
|
class Polls
|
||||||
|
{
|
||||||
|
private $polls;
|
||||||
|
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->polls = DatabaseConnection::i()->getContext()->table("polls");
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(int $id): ?Poll
|
||||||
|
{
|
||||||
|
$poll = $this->polls->get($id);
|
||||||
|
if(!$poll)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return new Poll($poll);
|
||||||
|
}
|
||||||
|
}
|
32
Web/Models/Repositories/SupportAgents.php
Normal file
32
Web/Models/Repositories/SupportAgents.php
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Models\Repositories;
|
||||||
|
use Nette\Database\Table\ActiveRow;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use openvk\Web\Models\Entities\{User, SupportAgent};
|
||||||
|
|
||||||
|
class SupportAgents
|
||||||
|
{
|
||||||
|
private $context;
|
||||||
|
private $tickets;
|
||||||
|
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->context = DatabaseConnection::i()->getContext();
|
||||||
|
$this->agents = $this->context->table("support_names");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function toAgent(?ActiveRow $ar)
|
||||||
|
{
|
||||||
|
return is_null($ar) ? NULL : new SupportAgent($ar);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(int $id): ?SupportAgent
|
||||||
|
{
|
||||||
|
return $this->toAgent($this->agents->where("agent", $id)->fetch());
|
||||||
|
}
|
||||||
|
|
||||||
|
function isExists(int $id): bool
|
||||||
|
{
|
||||||
|
return !is_null($this->get($id));
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,5 +28,12 @@ class TicketComments
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCountByAgent(int $agent_id, int $mark = NULL): int
|
||||||
|
{
|
||||||
|
$filter = ['user_id' => $agent_id, 'user_type' => 1];
|
||||||
|
$mark && $filter['mark'] = $mark;
|
||||||
|
return sizeof($this->comments->where($filter));
|
||||||
|
}
|
||||||
|
|
||||||
use \Nette\SmartObject;
|
use \Nette\SmartObject;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Repositories;
|
namespace openvk\Web\Models\Repositories;
|
||||||
use openvk\Web\Models\Entities\User;
|
use openvk\Web\Models\Entities\User;
|
||||||
|
use openvk\Web\Models\Repositories\Aliases;
|
||||||
use Nette\Database\Table\ActiveRow;
|
use Nette\Database\Table\ActiveRow;
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
use Chandler\Security\User as ChandlerUser;
|
use Chandler\Security\User as ChandlerUser;
|
||||||
|
@ -9,11 +10,13 @@ class Users
|
||||||
{
|
{
|
||||||
private $context;
|
private $context;
|
||||||
private $users;
|
private $users;
|
||||||
|
private $aliases;
|
||||||
|
|
||||||
function __construct()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->context = DatabaseConnection::i()->getContext();
|
$this->context = DatabaseConnection::i()->getContext();
|
||||||
$this->users = $this->context->table("profiles");
|
$this->users = $this->context->table("profiles");
|
||||||
|
$this->aliases = $this->context->table("aliases");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function toUser(?ActiveRow $ar): ?User
|
private function toUser(?ActiveRow $ar): ?User
|
||||||
|
@ -28,7 +31,17 @@ class Users
|
||||||
|
|
||||||
function getByShortURL(string $url): ?User
|
function getByShortURL(string $url): ?User
|
||||||
{
|
{
|
||||||
return $this->toUser($this->users->where("shortcode", $url)->fetch());
|
$shortcode = $this->toUser($this->users->where("shortcode", $url)->fetch());
|
||||||
|
|
||||||
|
if ($shortcode)
|
||||||
|
return $shortcode;
|
||||||
|
|
||||||
|
$alias = (new Aliases)->getByShortcode($url);
|
||||||
|
|
||||||
|
if (!$alias) return NULL;
|
||||||
|
if ($alias->getType() !== "user") return NULL;
|
||||||
|
|
||||||
|
return $alias->getUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getByChandlerUser(ChandlerUser $user): ?User
|
function getByChandlerUser(ChandlerUser $user): ?User
|
||||||
|
|
|
@ -10,6 +10,8 @@ SELECT DISTINCT id, class FROM
|
||||||
sender_id = ?
|
sender_id = ?
|
||||||
AND
|
AND
|
||||||
sender_type = ?
|
sender_type = ?
|
||||||
|
AND
|
||||||
|
deleted = 0
|
||||||
) UNION (
|
) UNION (
|
||||||
SELECT
|
SELECT
|
||||||
sender_id AS id,
|
sender_id AS id,
|
||||||
|
@ -20,6 +22,8 @@ SELECT DISTINCT id, class FROM
|
||||||
recipient_id = ?
|
recipient_id = ?
|
||||||
AND
|
AND
|
||||||
recipient_type = ?
|
recipient_type = ?
|
||||||
|
AND
|
||||||
|
deleted = 0
|
||||||
)
|
)
|
||||||
ORDER BY
|
ORDER BY
|
||||||
time
|
time
|
||||||
|
|
|
@ -64,7 +64,7 @@ final class AboutPresenter extends OpenVKPresenter
|
||||||
$this->template->usersStats = (new Users)->getStatistics();
|
$this->template->usersStats = (new Users)->getStatistics();
|
||||||
$this->template->clubsCount = (new Clubs)->getCount();
|
$this->template->clubsCount = (new Clubs)->getCount();
|
||||||
$this->template->postsCount = (new Posts)->getCount();
|
$this->template->postsCount = (new Posts)->getCount();
|
||||||
$this->template->popularClubs = iterator_to_array((new Clubs)->getPopularClubs());
|
$this->template->popularClubs = [];
|
||||||
$this->template->admins = iterator_to_array((new Users)->getInstanceAdmins());
|
$this->template->admins = iterator_to_array((new Users)->getInstanceAdmins());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +76,9 @@ final class AboutPresenter extends OpenVKPresenter
|
||||||
$this->assertNoCSRF();
|
$this->assertNoCSRF();
|
||||||
setLanguage($_GET['lg']);
|
setLanguage($_GET['lg']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!is_null($_GET['jReturnTo']))
|
||||||
|
$this->redirect(rawurldecode($_GET['jReturnTo']));
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderExportJSLanguage($lg = NULL): void
|
function renderExportJSLanguage($lg = NULL): void
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\{Voucher, Gift, GiftCategory, User};
|
use openvk\Web\Models\Entities\{Voucher, Gift, GiftCategory, User, BannedLink};
|
||||||
use openvk\Web\Models\Repositories\{Users, Clubs, Vouchers, Gifts};
|
use openvk\Web\Models\Repositories\{ChandlerGroups, ChandlerUsers, Users, Clubs, Vouchers, Gifts, BannedLinks};
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
|
||||||
final class AdminPresenter extends OpenVKPresenter
|
final class AdminPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
|
@ -9,13 +10,17 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
private $clubs;
|
private $clubs;
|
||||||
private $vouchers;
|
private $vouchers;
|
||||||
private $gifts;
|
private $gifts;
|
||||||
|
private $bannedLinks;
|
||||||
|
private $chandlerGroups;
|
||||||
|
|
||||||
function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts)
|
function __construct(Users $users, Clubs $clubs, Vouchers $vouchers, Gifts $gifts, BannedLinks $bannedLinks, ChandlerGroups $chandlerGroups)
|
||||||
{
|
{
|
||||||
$this->users = $users;
|
$this->users = $users;
|
||||||
$this->clubs = $clubs;
|
$this->clubs = $clubs;
|
||||||
$this->vouchers = $vouchers;
|
$this->vouchers = $vouchers;
|
||||||
$this->gifts = $gifts;
|
$this->gifts = $gifts;
|
||||||
|
$this->bannedLinks = $bannedLinks;
|
||||||
|
$this->chandlerGroups = $chandlerGroups;
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
@ -59,6 +64,8 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
|
|
||||||
$this->template->user = $user;
|
$this->template->user = $user;
|
||||||
|
$this->template->c_groups_list = (new ChandlerGroups)->getList();
|
||||||
|
$this->template->c_memberships = $this->chandlerGroups->getUsersMemberships($user->getChandlerGUID());
|
||||||
|
|
||||||
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||||
return;
|
return;
|
||||||
|
@ -75,8 +82,13 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
$user->changeEmail($this->postParam("email"));
|
$user->changeEmail($this->postParam("email"));
|
||||||
if($user->onlineStatus() != $this->postParam("online")) $user->setOnline(intval($this->postParam("online")));
|
if($user->onlineStatus() != $this->postParam("online")) $user->setOnline(intval($this->postParam("online")));
|
||||||
$user->setVerified(empty($this->postParam("verify") ? 0 : 1));
|
$user->setVerified(empty($this->postParam("verify") ? 0 : 1));
|
||||||
|
if($this->postParam("add-to-group")) {
|
||||||
|
$query = "INSERT INTO `ChandlerACLRelations` (`user`, `group`) VALUES ('" . $user->getChandlerGUID() . "', '" . $this->postParam("add-to-group") . "')";
|
||||||
|
DatabaseConnection::i()->getConnection()->query($query);
|
||||||
|
}
|
||||||
|
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,11 +352,13 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
$this->assertNoCSRF();
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
|
$unban_time = strtotime($this->queryParam("date")) ?: NULL;
|
||||||
|
|
||||||
$user = $this->users->get($id);
|
$user = $this->users->get($id);
|
||||||
if(!$user)
|
if(!$user)
|
||||||
exit(json_encode([ "error" => "User does not exist" ]));
|
exit(json_encode([ "error" => "User does not exist" ]));
|
||||||
|
|
||||||
$user->ban($this->queryParam("reason"));
|
$user->ban($this->queryParam("reason"), true, $unban_time);
|
||||||
exit(json_encode([ "success" => true, "reason" => $this->queryParam("reason") ]));
|
exit(json_encode([ "success" => true, "reason" => $this->queryParam("reason") ]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +370,8 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
if(!$user)
|
if(!$user)
|
||||||
exit(json_encode([ "error" => "User does not exist" ]));
|
exit(json_encode([ "error" => "User does not exist" ]));
|
||||||
|
|
||||||
$user->setBlock_Reason(null);
|
$user->setBlock_Reason(NULL);
|
||||||
|
$user->setUnblock_time(NULL);
|
||||||
$user->save();
|
$user->save();
|
||||||
exit(json_encode([ "success" => true ]));
|
exit(json_encode([ "success" => true ]));
|
||||||
}
|
}
|
||||||
|
@ -372,4 +387,164 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
$user->adminNotify("⚠️ " . $this->queryParam("message"));
|
$user->adminNotify("⚠️ " . $this->queryParam("message"));
|
||||||
exit(json_encode([ "message" => $this->queryParam("message") ]));
|
exit(json_encode([ "message" => $this->queryParam("message") ]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderBannedLinks(): void
|
||||||
|
{
|
||||||
|
$this->template->links = $this->bannedLinks->getList((int) $this->queryParam("p") ?: 1);
|
||||||
|
$this->template->users = new Users;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBannedLink(int $id): void
|
||||||
|
{
|
||||||
|
$this->template->form = (object) [];
|
||||||
|
|
||||||
|
if($id === 0) {
|
||||||
|
$this->template->form->id = 0;
|
||||||
|
$this->template->form->link = NULL;
|
||||||
|
$this->template->form->reason = NULL;
|
||||||
|
} else {
|
||||||
|
$link = (new BannedLinks)->get($id);
|
||||||
|
if(!$link)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$this->template->form->id = $link->getId();
|
||||||
|
$this->template->form->link = $link->getDomain();
|
||||||
|
$this->template->form->reason = $link->getReason();
|
||||||
|
$this->template->form->regexp = $link->getRawRegexp();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||||
|
return;
|
||||||
|
|
||||||
|
$link = (new BannedLinks)->get($id);
|
||||||
|
|
||||||
|
$new_domain = parse_url($this->postParam("link"))["host"];
|
||||||
|
$new_reason = $this->postParam("reason") ?: NULL;
|
||||||
|
|
||||||
|
$lid = $id;
|
||||||
|
|
||||||
|
if ($link) {
|
||||||
|
$link->setDomain($new_domain ?? $this->postParam("link"));
|
||||||
|
$link->setReason($new_reason);
|
||||||
|
$link->setRegexp_rule($this->postParam("regexp"));
|
||||||
|
$link->save();
|
||||||
|
} else {
|
||||||
|
if (!$new_domain)
|
||||||
|
$this->flashFail("err", tr("error"), tr("admin_banned_link_not_specified"));
|
||||||
|
|
||||||
|
$link = new BannedLink;
|
||||||
|
$link->setDomain($new_domain);
|
||||||
|
$link->setReason($new_reason);
|
||||||
|
$link->setRegexp_rule($this->postParam("regexp"));
|
||||||
|
$link->setInitiator($this->user->identity->getId());
|
||||||
|
$link->save();
|
||||||
|
|
||||||
|
$lid = $link->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->redirect("/admin/bannedLink/id" . $lid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderUnbanLink(int $id): void
|
||||||
|
{
|
||||||
|
$link = (new BannedLinks)->get($id);
|
||||||
|
|
||||||
|
if (!$link)
|
||||||
|
$this->flashFail("err", tr("error"), tr("admin_banned_link_not_found"));
|
||||||
|
|
||||||
|
$link->delete(false);
|
||||||
|
|
||||||
|
$this->redirect("/admin/bannedLinks");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderChandlerGroups(): void
|
||||||
|
{
|
||||||
|
$this->template->groups = (new ChandlerGroups)->getList();
|
||||||
|
|
||||||
|
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
||||||
|
return;
|
||||||
|
|
||||||
|
$req = "INSERT INTO `ChandlerGroups` (`name`) VALUES ('" . $this->postParam("name") . "')";
|
||||||
|
DatabaseConnection::i()->getConnection()->query($req);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderChandlerGroup(string $UUID): void
|
||||||
|
{
|
||||||
|
$DB = DatabaseConnection::i()->getConnection();
|
||||||
|
|
||||||
|
if(is_null($DB->query("SELECT * FROM `ChandlerGroups` WHERE `id` = '$UUID'")->fetch()))
|
||||||
|
$this->flashFail("err", tr("error"), tr("c_group_not_found"));
|
||||||
|
|
||||||
|
$this->template->group = (new ChandlerGroups)->get($UUID);
|
||||||
|
$this->template->mode = in_array(
|
||||||
|
$this->queryParam("act"),
|
||||||
|
[
|
||||||
|
"main",
|
||||||
|
"members",
|
||||||
|
"permissions",
|
||||||
|
"removeMember",
|
||||||
|
"removePermission",
|
||||||
|
"delete"
|
||||||
|
]) ? $this->queryParam("act") : "main";
|
||||||
|
$this->template->members = (new ChandlerGroups)->getMembersById($UUID);
|
||||||
|
$this->template->perms = (new ChandlerGroups)->getPermissionsById($UUID);
|
||||||
|
|
||||||
|
if($this->template->mode == "removeMember") {
|
||||||
|
$where = "`user` = '" . $this->queryParam("uid") . "' AND `group` = '$UUID'";
|
||||||
|
|
||||||
|
if(is_null($DB->query("SELECT * FROM `ChandlerACLRelations` WHERE " . $where)->fetch()))
|
||||||
|
$this->flashFail("err", tr("error"), tr("c_user_is_not_in_group"));
|
||||||
|
|
||||||
|
$DB->query("DELETE FROM `ChandlerACLRelations` WHERE " . $where);
|
||||||
|
$this->flashFail("succ", tr("changes_saved"), tr("c_user_removed_from_group"));
|
||||||
|
} elseif($this->template->mode == "removePermission") {
|
||||||
|
$where = "`model` = '" . trim(addslashes($this->queryParam("model"))) . "' AND `permission` = '". $this->queryParam("perm") ."' AND `group` = '$UUID'";
|
||||||
|
|
||||||
|
if(is_null($DB->query("SELECT * FROM `ChandlerACLGroupsPermissions WHERE $where`")))
|
||||||
|
$this->flashFail("err", tr("error"), tr("c_permission_not_found"));
|
||||||
|
|
||||||
|
$DB->query("DELETE FROM `ChandlerACLGroupsPermissions` WHERE $where");
|
||||||
|
$this->flashFail("succ", tr("changes_saved"), tr("c_permission_removed_from_group"));
|
||||||
|
} elseif($this->template->mode == "delete") {
|
||||||
|
$DB->query("DELETE FROM `ChandlerGroups` WHERE `id` = '$UUID'");
|
||||||
|
$DB->query("DELETE FROM `ChandlerACLGroupsPermissions` WHERE `group` = '$UUID'");
|
||||||
|
$DB->query("DELETE FROM `ChandlerACLRelations` WHERE `group` = '$UUID'");
|
||||||
|
|
||||||
|
$this->flashFail("succ", tr("changes_saved"), tr("c_group_removed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] !== "POST") return;
|
||||||
|
|
||||||
|
$req = "";
|
||||||
|
|
||||||
|
if($this->template->mode == "main")
|
||||||
|
if($this->postParam("delete"))
|
||||||
|
$req = "DELETE FROM `ChandlerGroups` WHERE `id`='$UUID'";
|
||||||
|
else
|
||||||
|
$req = "UPDATE `ChandlerGroups` SET `name`='". $this->postParam('name') ."' , `color`='". $this->postParam("color") ."' WHERE `id`='$UUID'";
|
||||||
|
|
||||||
|
if($this->template->mode == "members")
|
||||||
|
if($this->postParam("uid"))
|
||||||
|
if(!is_null($DB->query("SELECT * FROM `ChandlerACLRelations` WHERE `user` = '" . $this->postParam("uid") . "'")))
|
||||||
|
$this->flashFail("err", tr("error"), tr("c_user_is_already_in_group"));
|
||||||
|
|
||||||
|
$req = "INSERT INTO `ChandlerACLRelations` (`user`, `group`, `priority`) VALUES ('". $this->postParam("uid") ."', '$UUID', 32)";
|
||||||
|
|
||||||
|
if($this->template->mode == "permissions")
|
||||||
|
$req = "INSERT INTO `ChandlerACLGroupsPermissions` (`group`, `model`, `permission`, `context`) VALUES ('$UUID', '". trim(addslashes($this->postParam("model"))) ."', '". $this->postParam("permission") ."', 0)";
|
||||||
|
|
||||||
|
$DB->query($req);
|
||||||
|
$this->flashFail("succ", tr("changes_saved"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderChandlerUser(string $UUID): void
|
||||||
|
{
|
||||||
|
if(!$UUID) $this->notFound();
|
||||||
|
|
||||||
|
$c_user = (new ChandlerUsers())->getById($UUID);
|
||||||
|
$user = $this->users->getByChandlerUser($c_user);
|
||||||
|
if(!$user) $this->notFound();
|
||||||
|
|
||||||
|
$this->redirect("/admin/users/id" . $user->getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use openvk\Web\Models\Repositories\Applications;
|
||||||
final class AppsPresenter extends OpenVKPresenter
|
final class AppsPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
private $apps;
|
private $apps;
|
||||||
|
protected $presenterName = "apps";
|
||||||
function __construct(Applications $apps)
|
function __construct(Applications $apps)
|
||||||
{
|
{
|
||||||
$this->apps = $apps;
|
$this->apps = $apps;
|
||||||
|
|
|
@ -84,6 +84,9 @@ final class AuthPresenter extends OpenVKPresenter
|
||||||
if (strtotime($this->postParam("birthday")) > time())
|
if (strtotime($this->postParam("birthday")) > time())
|
||||||
$this->flashFail("err", tr("invalid_birth_date"), tr("invalid_birth_date_comment"));
|
$this->flashFail("err", tr("invalid_birth_date"), tr("invalid_birth_date_comment"));
|
||||||
|
|
||||||
|
if (!$this->postParam("confirmation"))
|
||||||
|
$this->flashFail("err", tr("error"), tr("checkbox_in_registration_unchecked"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$user = new User;
|
$user = new User;
|
||||||
$user->setFirst_Name($this->postParam("first_name"));
|
$user->setFirst_Name($this->postParam("first_name"));
|
||||||
|
@ -322,4 +325,39 @@ final class AuthPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
$this->redirect("/");
|
$this->redirect("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderUnbanThemself(): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
if(!$this->user->identity->canUnbanThemself())
|
||||||
|
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
|
|
||||||
|
$user = $this->users->get($this->user->id);
|
||||||
|
|
||||||
|
$user->setBlock_Reason(NULL);
|
||||||
|
$user->setUnblock_Time(NULL);
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$this->flashFail("succ", tr("banned_unban_title"), tr("banned_unban_description"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function will revoke all tokens, including API and Web tokens and except active one
|
||||||
|
*
|
||||||
|
* OF COURSE it requires CSRF
|
||||||
|
*/
|
||||||
|
function renderRevokeAllTokens(): void
|
||||||
|
{
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
|
// API tokens
|
||||||
|
$this->db->table("api_tokens")->where("user", $this->user->identity->getId())->delete();
|
||||||
|
// Web tokens
|
||||||
|
$this->db->table("ChandlerTokens")->where("user", $this->user->identity->getChandlerGUID())->where("token != ?", Session::i()->get("tok"))->delete();
|
||||||
|
$this->flashFail("succ", tr("information_-1"), tr("end_all_sessions_done"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,29 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
|
use openvk\Web\Models\Repositories\BannedLinks;
|
||||||
|
use openvk\Web\Models\Entities\BannedLink;
|
||||||
|
|
||||||
final class AwayPresenter extends OpenVKPresenter
|
final class AwayPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
function renderAway(): void
|
function renderAway(): void
|
||||||
{
|
{
|
||||||
|
$checkBanEntries = (new BannedLinks)->check($this->queryParam("to") . "/");
|
||||||
|
if (OPENVK_ROOT_CONF["openvk"]["preferences"]["susLinks"]["warnings"])
|
||||||
|
if (sizeof($checkBanEntries) > 0)
|
||||||
|
$this->pass("openvk!Away->view", $checkBanEntries[0]);
|
||||||
|
|
||||||
header("HTTP/1.0 302 Found");
|
header("HTTP/1.0 302 Found");
|
||||||
header("X-Robots-Tag: noindex, nofollow, noarchive");
|
header("X-Robots-Tag: noindex, nofollow, noarchive");
|
||||||
header("Location: " . $this->queryParam("to"));
|
header("Location: " . $this->queryParam("to"));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderView(int $lid) {
|
||||||
|
$this->template->link = (new BannedLinks)->get($lid);
|
||||||
|
|
||||||
|
if (!$this->template->link)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$this->template->to = $this->queryParam("to");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
13
Web/Presenters/BannedLinkPresenter.php
Normal file
13
Web/Presenters/BannedLinkPresenter.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Presenters;
|
||||||
|
|
||||||
|
use openvk\Web\Models\Entities\BannedLink;
|
||||||
|
use openvk\Web\Models\Repositories\BannedLinks;
|
||||||
|
|
||||||
|
final class BannedLinkPresenter extends OpenVKPresenter
|
||||||
|
{
|
||||||
|
function renderView(int $lid) {
|
||||||
|
$this->template->link = (new BannedLinks)->get($lid);
|
||||||
|
$this->template->to = $this->queryParam("to");
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ use openvk\Web\Models\Repositories\{Comments, Clubs};
|
||||||
|
|
||||||
final class CommentPresenter extends OpenVKPresenter
|
final class CommentPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
|
protected $presenterName = "comment";
|
||||||
private $models = [
|
private $models = [
|
||||||
"posts" => "openvk\\Web\\Models\\Repositories\\Posts",
|
"posts" => "openvk\\Web\\Models\\Repositories\\Posts",
|
||||||
"photos" => "openvk\\Web\\Models\\Repositories\\Photos",
|
"photos" => "openvk\\Web\\Models\\Repositories\\Photos",
|
||||||
|
|
|
@ -7,6 +7,7 @@ final class GiftsPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
private $gifts;
|
private $gifts;
|
||||||
private $users;
|
private $users;
|
||||||
|
protected $presenterName = "gifts";
|
||||||
|
|
||||||
function __construct(Gifts $gifts, Users $users)
|
function __construct(Gifts $gifts, Users $users)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,6 +8,7 @@ use Chandler\Security\Authenticator;
|
||||||
final class GroupPresenter extends OpenVKPresenter
|
final class GroupPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
private $clubs;
|
private $clubs;
|
||||||
|
protected $presenterName = "group";
|
||||||
|
|
||||||
function __construct(Clubs $clubs)
|
function __construct(Clubs $clubs)
|
||||||
{
|
{
|
||||||
|
|
|
@ -29,9 +29,10 @@ final class InternalAPIPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
function renderRoute(): void
|
function renderRoute(): void
|
||||||
{
|
{
|
||||||
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||||
|
header("HTTP/1.1 405 Method Not Allowed");
|
||||||
exit("ты дебил это точка апи");
|
exit("ты дебил это точка апи");
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
$input = (object) MessagePack::unpack(file_get_contents("php://input"));
|
$input = (object) MessagePack::unpack(file_get_contents("php://input"));
|
||||||
} catch (\Exception $ex) {
|
} catch (\Exception $ex) {
|
||||||
|
@ -71,9 +72,10 @@ final class InternalAPIPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTimezone() {
|
function renderTimezone() {
|
||||||
if($_SERVER["REQUEST_METHOD"] !== "POST")
|
if($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||||
|
header("HTTP/1.1 405 Method Not Allowed");
|
||||||
exit("ты дебил это метод апи");
|
exit("ты дебил это метод апи");
|
||||||
|
}
|
||||||
$sessionOffset = Session::i()->get("_timezoneOffset");
|
$sessionOffset = Session::i()->get("_timezoneOffset");
|
||||||
if(is_numeric($this->postParam("timezone", false))) {
|
if(is_numeric($this->postParam("timezone", false))) {
|
||||||
$postTZ = intval($this->postParam("timezone", false));
|
$postTZ = intval($this->postParam("timezone", false));
|
||||||
|
|
35
Web/Presenters/MaintenancePresenter.php
Normal file
35
Web/Presenters/MaintenancePresenter.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace openvk\Web\Presenters;
|
||||||
|
|
||||||
|
final class MaintenancePresenter extends OpenVKPresenter
|
||||||
|
{
|
||||||
|
protected $presenterName = "maintenance";
|
||||||
|
|
||||||
|
function renderSection(string $name): void
|
||||||
|
{
|
||||||
|
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["maintenanceMode"][$name])
|
||||||
|
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
|
|
||||||
|
$this->template->name = [
|
||||||
|
"photos" => tr("my_photos"),
|
||||||
|
"videos" => tr("my_videos"),
|
||||||
|
"messenger" => tr("my_messages"),
|
||||||
|
"user" => tr("users"),
|
||||||
|
"group" => tr("my_groups"),
|
||||||
|
"comment" => tr("comments"),
|
||||||
|
"gifts" => tr("gifts"),
|
||||||
|
"apps" => tr("apps"),
|
||||||
|
"notes" => tr("my_notes"),
|
||||||
|
"notification" => tr("my_feedback"),
|
||||||
|
"support" => tr("menu_support"),
|
||||||
|
"topics" => tr("topics")
|
||||||
|
][$name] ?? $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAll(): void
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,11 +9,13 @@ final class MessengerPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
private $messages;
|
private $messages;
|
||||||
private $signaler;
|
private $signaler;
|
||||||
|
protected $presenterName = "messenger";
|
||||||
|
|
||||||
function __construct(Messages $messages)
|
function __construct(Messages $messages)
|
||||||
{
|
{
|
||||||
$this->messages = $messages;
|
$this->messages = $messages;
|
||||||
$this->signaler = SignalManager::i();
|
$this->signaler = SignalManager::i();
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +96,13 @@ final class MessengerPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
$legacy = $this->queryParam("version") < 3;
|
$legacy = $this->queryParam("version") < 3;
|
||||||
|
|
||||||
|
$time = intval($this->queryParam("wait"));
|
||||||
|
|
||||||
|
if($time > 60)
|
||||||
|
$time = 60;
|
||||||
|
elseif($time == 0)
|
||||||
|
$time = 25; // default
|
||||||
|
|
||||||
$this->signaler->listen(function($event, $eId) use ($id) {
|
$this->signaler->listen(function($event, $eId) use ($id) {
|
||||||
exit(json_encode([
|
exit(json_encode([
|
||||||
"ts" => time(),
|
"ts" => time(),
|
||||||
|
@ -101,7 +110,7 @@ final class MessengerPresenter extends OpenVKPresenter
|
||||||
$event->getVKAPISummary($id),
|
$event->getVKAPISummary($id),
|
||||||
],
|
],
|
||||||
]));
|
]));
|
||||||
}, $id);
|
}, $id, $time);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderApiGetMessages(int $sel, int $lastMsg): void
|
function renderApiGetMessages(int $sel, int $lastMsg): void
|
||||||
|
|
|
@ -6,6 +6,7 @@ use openvk\Web\Models\Entities\Note;
|
||||||
final class NotesPresenter extends OpenVKPresenter
|
final class NotesPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
private $notes;
|
private $notes;
|
||||||
|
protected $presenterName = "notes";
|
||||||
|
|
||||||
function __construct(Notes $notes)
|
function __construct(Notes $notes)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,6 +3,8 @@ namespace openvk\Web\Presenters;
|
||||||
|
|
||||||
final class NotificationPresenter extends OpenVKPresenter
|
final class NotificationPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
|
protected $presenterName = "notification";
|
||||||
|
|
||||||
function renderFeed(): void
|
function renderFeed(): void
|
||||||
{
|
{
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
|
|
|
@ -17,6 +17,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
protected $deactivationTolerant = false;
|
protected $deactivationTolerant = false;
|
||||||
protected $errorTemplate = "@error";
|
protected $errorTemplate = "@error";
|
||||||
protected $user = NULL;
|
protected $user = NULL;
|
||||||
|
protected $presenterName;
|
||||||
|
|
||||||
private function calculateQueryString(array $data): string
|
private function calculateQueryString(array $data): string
|
||||||
{
|
{
|
||||||
|
@ -202,6 +203,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
|
|
||||||
$userValidated = 0;
|
$userValidated = 0;
|
||||||
$cacheTime = OPENVK_ROOT_CONF["openvk"]["preferences"]["nginxCacheTime"] ?? 0;
|
$cacheTime = OPENVK_ROOT_CONF["openvk"]["preferences"]["nginxCacheTime"] ?? 0;
|
||||||
|
|
||||||
if(!is_null($user)) {
|
if(!is_null($user)) {
|
||||||
$this->user = (object) [];
|
$this->user = (object) [];
|
||||||
$this->user->raw = $user;
|
$this->user->raw = $user;
|
||||||
|
@ -264,6 +266,16 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
header("X-Accel-Expires: $cacheTime");
|
header("X-Accel-Expires: $cacheTime");
|
||||||
setlocale(LC_TIME, ...(explode(";", tr("__locale"))));
|
setlocale(LC_TIME, ...(explode(";", tr("__locale"))));
|
||||||
|
|
||||||
|
if (!OPENVK_ROOT_CONF["openvk"]["preferences"]["maintenanceMode"]["all"]) {
|
||||||
|
if (OPENVK_ROOT_CONF["openvk"]["preferences"]["maintenanceMode"][$this->presenterName]) {
|
||||||
|
$this->pass("openvk!Maintenance->section", $this->presenterName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($this->presenterName != "maintenance") {
|
||||||
|
$this->redirect("/maintenances/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
parent::onStartup();
|
parent::onStartup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,8 +284,12 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
parent::onBeforeRender();
|
parent::onBeforeRender();
|
||||||
|
|
||||||
$whichbrowser = new WhichBrowser\Parser(getallheaders());
|
$whichbrowser = new WhichBrowser\Parser(getallheaders());
|
||||||
|
$featurephonetheme = OPENVK_ROOT_CONF["openvk"]["preferences"]["defaultFeaturePhoneTheme"];
|
||||||
$mobiletheme = OPENVK_ROOT_CONF["openvk"]["preferences"]["defaultMobileTheme"];
|
$mobiletheme = OPENVK_ROOT_CONF["openvk"]["preferences"]["defaultMobileTheme"];
|
||||||
if($mobiletheme && $whichbrowser->isType('mobile') && Session::i()->get("_tempTheme") == NULL)
|
|
||||||
|
if($featurephonetheme && $this->isOldThing($whichbrowser) && Session::i()->get("_tempTheme") == NULL) {
|
||||||
|
$this->setSessionTheme($featurephonetheme);
|
||||||
|
} elseif($mobiletheme && $whichbrowser->isType('mobile') && Session::i()->get("_tempTheme") == NULL)
|
||||||
$this->setSessionTheme($mobiletheme);
|
$this->setSessionTheme($mobiletheme);
|
||||||
|
|
||||||
$theme = NULL;
|
$theme = NULL;
|
||||||
|
@ -306,4 +322,33 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
header("Content-Length: $size");
|
header("Content-Length: $size");
|
||||||
exit($payload);
|
exit($payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function isOldThing($whichbrowser) {
|
||||||
|
if($whichbrowser->isOs('Series60') ||
|
||||||
|
$whichbrowser->isOs('Series40') ||
|
||||||
|
$whichbrowser->isOs('Series80') ||
|
||||||
|
$whichbrowser->isOs('Windows CE') ||
|
||||||
|
$whichbrowser->isOs('Windows Mobile') ||
|
||||||
|
$whichbrowser->isOs('Nokia Asha Platform') ||
|
||||||
|
$whichbrowser->isOs('UIQ') ||
|
||||||
|
$whichbrowser->isEngine('NetFront') || // PSP and other japanese portable systems
|
||||||
|
$whichbrowser->isOs('Android') ||
|
||||||
|
$whichbrowser->isOs('iOS') ||
|
||||||
|
$whichbrowser->isBrowser('Internet Explorer', '<=', '8')) {
|
||||||
|
// yeah, it's old, but ios and android are?
|
||||||
|
if($whichbrowser->isOs('iOS') && $whichbrowser->isOs('iOS', '<=', '9'))
|
||||||
|
return true;
|
||||||
|
elseif($whichbrowser->isOs('iOS') && $whichbrowser->isOs('iOS', '>', '9'))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if($whichbrowser->isOs('Android') && $whichbrowser->isOs('Android', '<=', '5'))
|
||||||
|
return true;
|
||||||
|
elseif($whichbrowser->isOs('Android') && $whichbrowser->isOs('Android', '>', '5'))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ final class PhotosPresenter extends OpenVKPresenter
|
||||||
private $users;
|
private $users;
|
||||||
private $photos;
|
private $photos;
|
||||||
private $albums;
|
private $albums;
|
||||||
|
protected $presenterName = "photos";
|
||||||
|
|
||||||
function __construct(Photos $photos, Albums $albums, Users $users)
|
function __construct(Photos $photos, Albums $albums, Users $users)
|
||||||
{
|
{
|
||||||
|
|
75
Web/Presenters/PollPresenter.php
Normal file
75
Web/Presenters/PollPresenter.php
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\Web\Presenters;
|
||||||
|
use openvk\Web\Models\Entities\Poll;
|
||||||
|
use openvk\Web\Models\Repositories\Polls;
|
||||||
|
|
||||||
|
final class PollPresenter extends OpenVKPresenter
|
||||||
|
{
|
||||||
|
private $polls;
|
||||||
|
|
||||||
|
function __construct(Polls $polls)
|
||||||
|
{
|
||||||
|
$this->polls = $polls;
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderView(int $id): void
|
||||||
|
{
|
||||||
|
$poll = $this->polls->get($id);
|
||||||
|
if(!$poll)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$this->template->id = $poll->getId();
|
||||||
|
$this->template->title = $poll->getTitle();
|
||||||
|
$this->template->isAnon = $poll->isAnonymous();
|
||||||
|
$this->template->multiple = $poll->isMultipleChoice();
|
||||||
|
$this->template->unlocked = $poll->isRevotable();
|
||||||
|
$this->template->until = $poll->endsAt();
|
||||||
|
$this->template->votes = $poll->getVoterCount();
|
||||||
|
$this->template->meta = $poll->getMetaDescription();
|
||||||
|
$this->template->ended = $ended = $poll->hasEnded();
|
||||||
|
if((is_null($this->user) || $poll->canVote($this->user->identity)) && !$ended) {
|
||||||
|
$this->template->options = $poll->getOptions();
|
||||||
|
|
||||||
|
$this->template->_template = "Poll/Poll.xml";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_null($this->user)) {
|
||||||
|
$this->template->voted = false;
|
||||||
|
$this->template->results = $poll->getResults();
|
||||||
|
} else {
|
||||||
|
$this->template->voted = $poll->hasVoted($this->user->identity);
|
||||||
|
$this->template->results = $poll->getResults($this->user->identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->_template = "Poll/PollResults.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderVoters(int $pollId): void
|
||||||
|
{
|
||||||
|
$poll = $this->polls->get($pollId);
|
||||||
|
if(!$poll)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
if($poll->isAnonymous())
|
||||||
|
$this->flashFail("err", tr("forbidden"), tr("poll_err_anonymous"));
|
||||||
|
|
||||||
|
$options = $poll->getOptions();
|
||||||
|
$option = (int) base_convert($this->queryParam("option"), 32, 10);
|
||||||
|
if(!in_array($option, array_keys($options)))
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$page = (int) ($this->queryParam("p") ?? 1);
|
||||||
|
$voters = $poll->getVoters($option, $page);
|
||||||
|
|
||||||
|
$this->template->pollId = $pollId;
|
||||||
|
$this->template->options = $options;
|
||||||
|
$this->template->option = [$option, $options[$option]];
|
||||||
|
$this->template->tabs = $options;
|
||||||
|
$this->template->iterator = $voters;
|
||||||
|
$this->template->count = $poll->getVoterCount($option);
|
||||||
|
$this->template->page = $page;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\{Ticket, TicketComment};
|
use openvk\Web\Models\Entities\{SupportAgent, Ticket, TicketComment};
|
||||||
use openvk\Web\Models\Repositories\{Tickets, Users, TicketComments};
|
use openvk\Web\Models\Repositories\{Tickets, Users, TicketComments, SupportAgents};
|
||||||
use openvk\Web\Util\Telegram;
|
use openvk\Web\Util\Telegram;
|
||||||
use Chandler\Session\Session;
|
use Chandler\Session\Session;
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
@ -11,6 +11,7 @@ final class SupportPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
protected $banTolerant = true;
|
protected $banTolerant = true;
|
||||||
protected $deactivationTolerant = true;
|
protected $deactivationTolerant = true;
|
||||||
|
protected $presenterName = "support";
|
||||||
|
|
||||||
private $tickets;
|
private $tickets;
|
||||||
private $comments;
|
private $comments;
|
||||||
|
@ -155,11 +156,12 @@ final class SupportPresenter extends OpenVKPresenter
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
} else {
|
} else {
|
||||||
if($ticket->getUserId() !== $this->user->id && $this->hasPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0))
|
if($ticket->getUserId() !== $this->user->id && $this->hasPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0))
|
||||||
$this->redirect("/support/tickets");
|
$_redirect = "/support/tickets";
|
||||||
else
|
else
|
||||||
$this->redirect("/support");
|
$_redirect = "/support?act=list";
|
||||||
|
|
||||||
$ticket->delete();
|
$ticket->delete();
|
||||||
|
$this->redirect($_redirect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,4 +342,58 @@ final class SupportPresenter extends OpenVKPresenter
|
||||||
$user->save();
|
$user->save();
|
||||||
$this->returnJson([ "success" => true ]);
|
$this->returnJson([ "success" => true ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderAgent(int $id): void
|
||||||
|
{
|
||||||
|
$this->assertPermission("openvk\Web\Models\Entities\TicketReply", "write", 0);
|
||||||
|
|
||||||
|
$support_names = new SupportAgents;
|
||||||
|
|
||||||
|
if(!$support_names->isExists($id))
|
||||||
|
$this->template->mode = "edit";
|
||||||
|
|
||||||
|
$this->template->agent_id = $id;
|
||||||
|
$this->template->mode = in_array($this->queryParam("act"), ["info", "edit"]) ? $this->queryParam("act") : "info";
|
||||||
|
$this->template->agent = $support_names->get($id) ?? NULL;
|
||||||
|
$this->template->counters = [
|
||||||
|
"all" => (new TicketComments)->getCountByAgent($id),
|
||||||
|
"good" => (new TicketComments)->getCountByAgent($id, 1),
|
||||||
|
"bad" => (new TicketComments)->getCountByAgent($id, 2)
|
||||||
|
];
|
||||||
|
|
||||||
|
if($id != $this->user->identity->getId())
|
||||||
|
if ($support_names->isExists($id))
|
||||||
|
$this->template->mode = "info";
|
||||||
|
else
|
||||||
|
$this->redirect("/support/agent" . $this->user->identity->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderEditAgent(int $id): void
|
||||||
|
{
|
||||||
|
$this->assertPermission("openvk\Web\Models\Entities\TicketReply", "write", 0);
|
||||||
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
|
$support_names = new SupportAgents;
|
||||||
|
$agent = $support_names->get($id);
|
||||||
|
|
||||||
|
if($agent)
|
||||||
|
if($agent->getAgentId() != $this->user->identity->getId()) $this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
|
|
||||||
|
if ($support_names->isExists($id)) {
|
||||||
|
$agent = $support_names->get($id);
|
||||||
|
$agent->setName($this->postParam("name") ?? tr("helpdesk_agent"));
|
||||||
|
$agent->setNumerate((int) $this->postParam("number") ?? NULL);
|
||||||
|
$agent->setIcon($this->postParam("avatar"));
|
||||||
|
$agent->save();
|
||||||
|
$this->flashFail("succ", "Успех", "Профиль отредактирован.");
|
||||||
|
} else {
|
||||||
|
$agent = new SupportAgent;
|
||||||
|
$agent->setAgent($this->user->identity->getId());
|
||||||
|
$agent->setName($this->postParam("name") ?? tr("helpdesk_agent"));
|
||||||
|
$agent->setNumerate((int) $this->postParam("number") ?? NULL);
|
||||||
|
$agent->setIcon($this->postParam("avatar"));
|
||||||
|
$agent->save();
|
||||||
|
$this->flashFail("succ", "Успех", "Профиль создан. Теперь пользователи видят Ваши псевдоним и аватарку вместо стандартных аватарки и номера.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ final class TopicsPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
private $topics;
|
private $topics;
|
||||||
private $clubs;
|
private $clubs;
|
||||||
|
protected $presenterName = "topics";
|
||||||
|
|
||||||
function __construct(Topics $topics, Clubs $clubs)
|
function __construct(Topics $topics, Clubs $clubs)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,8 +15,8 @@ use Nette\Database\UniqueConstraintViolationException;
|
||||||
final class UserPresenter extends OpenVKPresenter
|
final class UserPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
private $users;
|
private $users;
|
||||||
|
|
||||||
public $deactivationTolerant = false;
|
public $deactivationTolerant = false;
|
||||||
|
protected $presenterName = "user";
|
||||||
|
|
||||||
function __construct(Users $users)
|
function __construct(Users $users)
|
||||||
{
|
{
|
||||||
|
@ -29,7 +29,7 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
$user = $this->users->get($id);
|
$user = $this->users->get($id);
|
||||||
if(!$user || $user->isDeleted()) {
|
if(!$user || $user->isDeleted()) {
|
||||||
if($user->isDeactivated()) {
|
if(!is_null($user) && $user->isDeactivated()) {
|
||||||
$this->template->_template = "User/deactivated.xml";
|
$this->template->_template = "User/deactivated.xml";
|
||||||
|
|
||||||
$this->template->user = $user;
|
$this->template->user = $user;
|
||||||
|
@ -454,7 +454,7 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
$this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
|
$this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
|
||||||
}
|
}
|
||||||
$this->template->mode = in_array($this->queryParam("act"), [
|
$this->template->mode = in_array($this->queryParam("act"), [
|
||||||
"main", "privacy", "finance", "finance.top-up", "interface"
|
"main", "security", "privacy", "finance", "finance.top-up", "interface"
|
||||||
]) ? $this->queryParam("act")
|
]) ? $this->queryParam("act")
|
||||||
: "main";
|
: "main";
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ final class VideosPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
private $videos;
|
private $videos;
|
||||||
private $users;
|
private $users;
|
||||||
|
protected $presenterName = "videos";
|
||||||
|
|
||||||
function __construct(Videos $videos, Users $users)
|
function __construct(Videos $videos, Users $users)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\{Post, Photo, Video, Club, User};
|
use openvk\Web\Models\Exceptions\TooMuchOptionsException;
|
||||||
|
use openvk\Web\Models\Entities\{Poll, Post, Photo, Video, Club, User};
|
||||||
use openvk\Web\Models\Entities\Notifications\{RepostNotification, WallPostNotification};
|
use openvk\Web\Models\Entities\Notifications\{RepostNotification, WallPostNotification};
|
||||||
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums};
|
use openvk\Web\Models\Repositories\{Posts, Users, Clubs, Albums};
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
@ -44,9 +45,6 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
function renderWall(int $user, bool $embedded = false): void
|
function renderWall(int $user, bool $embedded = false): void
|
||||||
{
|
{
|
||||||
if(false)
|
|
||||||
exit(tr("forbidden") . ": " . (string) random_int(0, 255));
|
|
||||||
|
|
||||||
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
|
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
|
||||||
if(is_null($this->user)) {
|
if(is_null($this->user)) {
|
||||||
$canPost = false;
|
$canPost = false;
|
||||||
|
@ -66,6 +64,9 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
if ($embedded == true) $this->template->_template = "components/wall.xml";
|
if ($embedded == true) $this->template->_template = "components/wall.xml";
|
||||||
$this->template->oObj = $owner;
|
$this->template->oObj = $owner;
|
||||||
|
if($user < 0)
|
||||||
|
$this->template->club = $owner;
|
||||||
|
|
||||||
$this->template->owner = $user;
|
$this->template->owner = $user;
|
||||||
$this->template->canPost = $canPost;
|
$this->template->canPost = $canPost;
|
||||||
$this->template->count = $this->posts->getPostCountOnUserWall($user);
|
$this->template->count = $this->posts->getPostCountOnUserWall($user);
|
||||||
|
@ -88,9 +89,6 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
function renderRSS(int $user): void
|
function renderRSS(int $user): void
|
||||||
{
|
{
|
||||||
if(false)
|
|
||||||
exit(tr("forbidden") . ": " . (string) random_int(0, 255));
|
|
||||||
|
|
||||||
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
|
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
|
||||||
if(is_null($this->user)) {
|
if(is_null($this->user)) {
|
||||||
$canPost = false;
|
$canPost = false;
|
||||||
|
@ -113,14 +111,14 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$feed = new Feed();
|
$feed = new Feed();
|
||||||
|
|
||||||
$channel = new Channel();
|
$channel = new Channel();
|
||||||
$channel->title($post->getOwner()->getCanonicalName() . " — " . OPENVK_ROOT_CONF['openvk']['appearance']['name'])->url(ovk_scheme(true) . $_SERVER["SERVER_NAME"])->appendTo($feed);
|
$channel->title($owner->getCanonicalName() . " — " . OPENVK_ROOT_CONF['openvk']['appearance']['name'])->url(ovk_scheme(true) . $_SERVER["HTTP_HOST"])->appendTo($feed);
|
||||||
|
|
||||||
foreach($posts as $post) {
|
foreach($posts as $post) {
|
||||||
$item = new Item();
|
$item = new Item();
|
||||||
$item
|
$item
|
||||||
->title($post->getOwner()->getCanonicalName())
|
->title($post->getOwner()->getCanonicalName())
|
||||||
->description($post->getText())
|
->description($post->getText())
|
||||||
->url(ovk_scheme(true).$_SERVER["SERVER_NAME"]."/wall{$post->getPrettyId()}")
|
->url(ovk_scheme(true).$_SERVER["HTTP_HOST"]."/wall{$post->getPrettyId()}")
|
||||||
->pubDate($post->getPublicationTime()->timestamp())
|
->pubDate($post->getPublicationTime()->timestamp())
|
||||||
->appendTo($channel);
|
->appendTo($channel);
|
||||||
}
|
}
|
||||||
|
@ -259,16 +257,26 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album, $anon);
|
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album, $anon);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) {
|
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK)
|
||||||
$video = Video::fastMake($this->user->id, $this->postParam("text"), $_FILES["_vid_attachment"], $anon);
|
$video = Video::fastMake($this->user->id, $this->postParam("text"), $_FILES["_vid_attachment"], $anon);
|
||||||
}
|
|
||||||
} catch(\DomainException $ex) {
|
} catch(\DomainException $ex) {
|
||||||
$this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted"));
|
$this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted"));
|
||||||
} catch(ISE $ex) {
|
} catch(ISE $ex) {
|
||||||
$this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted_or_too_large"));
|
$this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted_or_too_large"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(empty($this->postParam("text")) && !$photo && !$video)
|
try {
|
||||||
|
$poll = NULL;
|
||||||
|
$xml = $this->postParam("poll");
|
||||||
|
if (!is_null($xml) && $xml != "none")
|
||||||
|
$poll = Poll::import($this->user->identity, $xml);
|
||||||
|
} catch(TooMuchOptionsException $e) {
|
||||||
|
$this->flashFail("err", tr("failed_to_publish_post"), tr("poll_err_to_much_options"));
|
||||||
|
} catch(\UnexpectedValueException $e) {
|
||||||
|
$this->flashFail("err", tr("failed_to_publish_post"), "Poll format invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty($this->postParam("text")) && !$photo && !$video && !$poll)
|
||||||
$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"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -291,6 +299,9 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
if(!is_null($video))
|
if(!is_null($video))
|
||||||
$post->attach($video);
|
$post->attach($video);
|
||||||
|
|
||||||
|
if(!is_null($poll))
|
||||||
|
$post->attach($poll);
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,16 @@
|
||||||
<p>
|
<p>
|
||||||
{tr("banned_1", htmlentities($thisUser->getCanonicalName()))|noescape}<br/>
|
{tr("banned_1", htmlentities($thisUser->getCanonicalName()))|noescape}<br/>
|
||||||
{tr("banned_2", htmlentities($thisUser->getBanReason()))|noescape}
|
{tr("banned_2", htmlentities($thisUser->getBanReason()))|noescape}
|
||||||
|
|
||||||
|
{if !$thisUser->getUnbanTime()}
|
||||||
|
{_banned_perm}
|
||||||
|
{else}
|
||||||
|
{tr("banned_until_time", $thisUser->getUnbanTime())|noescape}
|
||||||
|
{/if}
|
||||||
|
</p>
|
||||||
|
<p n:if="$thisUser->canUnbanThemself()">
|
||||||
|
<hr/>
|
||||||
|
<center><a class="button" href="/unban.php">{_banned_unban_myself}</a></center>
|
||||||
</p>
|
</p>
|
||||||
<hr/>
|
<hr/>
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -17,62 +17,19 @@
|
||||||
{script "js/l10n.js"}
|
{script "js/l10n.js"}
|
||||||
{script "js/openvk.cls.js"}
|
{script "js/openvk.cls.js"}
|
||||||
|
|
||||||
|
{css "js/node_modules/tippy.js/dist/backdrop.css"}
|
||||||
|
{css "js/node_modules/tippy.js/dist/border.css"}
|
||||||
|
{css "js/node_modules/tippy.js/dist/svg-arrow.css"}
|
||||||
|
{css "js/node_modules/tippy.js/themes/light.css"}
|
||||||
|
{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/handlebars/dist/handlebars.min.js"}
|
||||||
|
|
||||||
{if $isTimezoned == NULL}
|
{if $isTimezoned == NULL}
|
||||||
{script "js/timezone.js"}
|
{script "js/timezone.js"}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{ifset $thisUser}
|
{include "_includeCSS.xml"}
|
||||||
{if $thisUser->getNsfwTolerance() < 2}
|
|
||||||
{css "css/nsfw-posts.css"}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{if $theme !== NULL}
|
|
||||||
{if $theme->inheritDefault()}
|
|
||||||
{css "css/style.css"}
|
|
||||||
{css "css/dialog.css"}
|
|
||||||
{css "css/notifications.css"}
|
|
||||||
|
|
||||||
{if $isXmas}
|
|
||||||
{css "css/xmas.css"}
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/themepack/{$theme->getId()}/{$theme->getVersion()}/stylesheet/styles.css" />
|
|
||||||
|
|
||||||
{if $isXmas}
|
|
||||||
<link rel="stylesheet" href="/themepack/{$theme->getId()}/{$theme->getVersion()}/resource/xmas.css" />
|
|
||||||
{/if}
|
|
||||||
{else}
|
|
||||||
{css "css/style.css"}
|
|
||||||
{css "css/dialog.css"}
|
|
||||||
{css "css/notifications.css"}
|
|
||||||
|
|
||||||
{if $isXmas}
|
|
||||||
{css "css/xmas.css"}
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{if $thisUser->getStyleAvatar() == 1}
|
|
||||||
{css "css/avatar.1.css"}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{if $thisUser->getStyleAvatar() == 2}
|
|
||||||
{css "css/avatar.2.css"}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{if $thisUser->hasMicroblogEnabled() == 1}
|
|
||||||
{css "css/microblog.css"}
|
|
||||||
{/if}
|
|
||||||
{else}
|
|
||||||
{css "css/style.css"}
|
|
||||||
{css "css/dialog.css"}
|
|
||||||
{css "css/nsfw-posts.css"}
|
|
||||||
{css "css/notifications.css"}
|
|
||||||
|
|
||||||
{if $isXmas}
|
|
||||||
{css "css/xmas.css"}
|
|
||||||
{/if}
|
|
||||||
{/ifset}
|
|
||||||
|
|
||||||
{ifset headIncludes}
|
{ifset headIncludes}
|
||||||
{include headIncludes}
|
{include headIncludes}
|
||||||
|
@ -181,7 +138,7 @@
|
||||||
{var $menuLinksAvaiable = sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0 && $thisUser->getLeftMenuItemStatus('links')}
|
{var $menuLinksAvaiable = sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0 && $thisUser->getLeftMenuItemStatus('links')}
|
||||||
<div n:if="$canAccessAdminPanel || $canAccessHelpdesk || $menuLinksAvaiable" class="menu_divider"></div>
|
<div n:if="$canAccessAdminPanel || $canAccessHelpdesk || $menuLinksAvaiable" class="menu_divider"></div>
|
||||||
<a href="/admin" class="link" n:if="$canAccessAdminPanel" title="{_admin} [Alt+Shift+A]" accesskey="a">{_admin}</a>
|
<a href="/admin" class="link" n:if="$canAccessAdminPanel" title="{_admin} [Alt+Shift+A]" accesskey="a">{_admin}</a>
|
||||||
<a href="/support/tickets" class="link" n:if="$canAccessHelpdesk">Helpdesk
|
<a href="/support/tickets" class="link" n:if="$canAccessHelpdesk">{_helpdesk}
|
||||||
{if $helpdeskTicketNotAnsweredCount > 0}
|
{if $helpdeskTicketNotAnsweredCount > 0}
|
||||||
(<b>{$helpdeskTicketNotAnsweredCount}</b>)
|
(<b>{$helpdeskTicketNotAnsweredCount}</b>)
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -306,9 +263,17 @@
|
||||||
<a href="/blog" class="link">{_footer_blog}</a>
|
<a href="/blog" class="link">{_footer_blog}</a>
|
||||||
<a href="/support" class="link">{_footer_help}</a>
|
<a href="/support" class="link">{_footer_help}</a>
|
||||||
<a href="/dev" target="_blank" class="link">{_footer_developers}</a>
|
<a href="/dev" target="_blank" class="link">{_footer_developers}</a>
|
||||||
<a href="/language" class="link">{_footer_choose_language}</a>
|
|
||||||
<a href="/privacy" class="link">{_footer_privacy}</a>
|
<a href="/privacy" class="link">{_footer_privacy}</a>
|
||||||
</div>
|
</div>
|
||||||
|
<p>
|
||||||
|
{var $currentUrl = $_SERVER["REQUEST_URI"]}
|
||||||
|
{foreach array_slice(getLanguages(), 0, 3) as $language}
|
||||||
|
<a href="/language?lg={$language['code']}&hash={urlencode($csrfToken)}&jReturnTo={php echo rawurlencode($currentUrl)}" rel="nofollow" title="{$language['native_name']}" class="link">
|
||||||
|
<img src="/assets/packages/static/openvk/img/flags/{$language['flag']}.gif" alt="{$language['native_name']}">
|
||||||
|
</a>
|
||||||
|
{/foreach}
|
||||||
|
<a href="/language" class="link">all languages »</a>
|
||||||
|
</p>
|
||||||
<p>OpenVK <a href="/about:openvk">{php echo OPENVK_VERSION}</a> | PHP: {phpversion()} | DB: {$dbVersion}</p>
|
<p>OpenVK <a href="/about:openvk">{php echo OPENVK_VERSION}</a> | PHP: {phpversion()} | DB: {$dbVersion}</p>
|
||||||
<p n:ifcontent>
|
<p n:ifcontent>
|
||||||
{php echo OPENVK_ROOT_CONF["openvk"]["appearance"]["motd"]}
|
{php echo OPENVK_ROOT_CONF["openvk"]["appearance"]["motd"]}
|
||||||
|
@ -325,6 +290,8 @@
|
||||||
{script "js/scroll.js"}
|
{script "js/scroll.js"}
|
||||||
{script "js/al_wall.js"}
|
{script "js/al_wall.js"}
|
||||||
{script "js/al_api.js"}
|
{script "js/al_api.js"}
|
||||||
|
{script "js/al_mentions.js"}
|
||||||
|
{script "js/al_polls.js"}
|
||||||
|
|
||||||
{ifset $thisUser}
|
{ifset $thisUser}
|
||||||
{script "js/al_notifs.js"}
|
{script "js/al_notifs.js"}
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
{var $result = preg_match("/(.+)\((.+)\)/", $language['native_name'], $name)}
|
{var $result = preg_match("/(.+)\((.+)\)/", $language['native_name'], $name)}
|
||||||
|
|
||||||
<a href="language?lg={$language['code']}&hash={urlencode($csrfToken)}" class="link_new" rel="nofollow">
|
<a href="language?lg={$language['code']}&hash={urlencode($csrfToken)}" class="link_new" rel="nofollow">
|
||||||
<center><img src="/assets/packages/static/openvk/img/flags/{$language['flag']}.gif"></center>
|
<center><img src="/assets/packages/static/openvk/img/flags/{$language['flag']}.gif" alt="{$language['native_name']}"></center>
|
||||||
<br>
|
<br>
|
||||||
{if $result == 1}
|
{if $result == 1}
|
||||||
{$name[1]}
|
{$name[1]}
|
||||||
|
|
|
@ -396,8 +396,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, Alexander Kotov (l-lacker),
|
Nikita Volkov (sup_ban), Daniel Myslivets, Maxim Leshchenko (maksales / maksalees)
|
||||||
Alexey Assemblerov (BiosNod), Ilya Prokopenko (dsrev) and Maxim Leshchenko (maksales / maksalees)
|
and n1rwana
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -56,6 +56,17 @@
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/clubs">{_groups}</a>
|
<a href="/admin/clubs">{_groups}</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/admin/bannedLinks">{_admin_banned_links}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="aui-nav-heading">
|
||||||
|
<strong>Chandler</strong>
|
||||||
|
</div>
|
||||||
|
<ul class="aui-nav">
|
||||||
|
<li>
|
||||||
|
<a href="/admin/chandler/groups">{_c_groups}</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="aui-nav-heading">
|
<div class="aui-nav-heading">
|
||||||
<strong>{_admin_services}</strong>
|
<strong>{_admin_services}</strong>
|
||||||
|
|
48
Web/Presenters/templates/Admin/BannedLink.xml
Normal file
48
Web/Presenters/templates/Admin/BannedLink.xml
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_edit}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block heading}
|
||||||
|
{_edit} #{$form->id ?? "undefined"}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<div style="margin: 8px -8px;" class="aui-tabs horizontal-tabs">
|
||||||
|
<ul class="tabs-menu">
|
||||||
|
<li class="menu-item active-tab">
|
||||||
|
<a href="#info">{_admin_banned_link}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="tabs-pane active-pane" id="info">
|
||||||
|
<form class="aui" method="POST">
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="id">ID</label>
|
||||||
|
<input class="text long-field" type="number" id="id" name="id" disabled value="{$form->id}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="token">{_admin_banned_domain}</label>
|
||||||
|
<input class="text long-field" type="text" id="link" name="link" value="{$form->link}" />
|
||||||
|
<div class="description">{_admin_banned_link_description}</div>
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="token">{_admin_banned_link_regexp}</label>
|
||||||
|
<input class="text long-field" type="text" id="regexp" name="regexp" value="{$form->regexp}" />
|
||||||
|
<div class="description">{_admin_banned_link_regexp_description}</div>
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="coins">{_admin_banned_link_reason}</label>
|
||||||
|
<input class="text long-field" type="text" id="reason" name="reason" value="{$form->reason}" />
|
||||||
|
</div>
|
||||||
|
<div class="buttons-container">
|
||||||
|
<div class="buttons">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input class="aui-button aui-button-primary submit" type="submit" value="{_save}">
|
||||||
|
<a class="aui-button aui-button-secondary" href="/admin/bannedLink/id{$form->id}/unban">{_delete}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/block}
|
46
Web/Presenters/templates/Admin/BannedLinks.xml
Normal file
46
Web/Presenters/templates/Admin/BannedLinks.xml
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_admin_banned_links}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block heading}
|
||||||
|
<a style="float: right;" class="aui-button aui-button-primary" href="/admin/bannedLink/id0">
|
||||||
|
{_create}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{_admin_banned_links}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<table class="aui aui-table-list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>{_admin_banned_domain}</th>
|
||||||
|
<th>REGEXP</th>
|
||||||
|
<th>{_admin_banned_link_reason}</th>
|
||||||
|
<th>{_admin_banned_link_initiator}</th>
|
||||||
|
<th>{_admin_actions}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr n:foreach="$links as $link">
|
||||||
|
<td>{$link->getId()}</td>
|
||||||
|
<td>{$link->getDomain()}</td>
|
||||||
|
<td>{$link->getRegexpRule()}</td>
|
||||||
|
<td>{$link->getReason() ?? "-"}</td>
|
||||||
|
<td>{$link->getInitiator()->getCanonicalName()}</td>
|
||||||
|
<td>
|
||||||
|
<a class="aui-button aui-button-primary" href="/admin/bannedLink/id{$link->getId()}">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div align="right">
|
||||||
|
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||||
|
<a class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||||
|
</div>
|
||||||
|
{/block}
|
177
Web/Presenters/templates/Admin/ChandlerGroup.xml
Normal file
177
Web/Presenters/templates/Admin/ChandlerGroup.xml
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{$group->name}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block heading}
|
||||||
|
<a href="/admin/chandler/groups">{_c_groups}</a>
|
||||||
|
» {$group->name}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
{var $isMain = $mode === 'main'}
|
||||||
|
{var $isPermissions = $mode === 'permissions'}
|
||||||
|
{var $isMembers = $mode === 'members'}
|
||||||
|
|
||||||
|
{if $isMain}
|
||||||
|
<div class="aui-tabs horizontal-tabs">
|
||||||
|
<nav class="aui-navgroup aui-navgroup-horizontal">
|
||||||
|
<div class="aui-navgroup-inner">
|
||||||
|
<div class="aui-navgroup-primary">
|
||||||
|
<ul class="aui-nav">
|
||||||
|
<li class="aui-nav-selected"><a href="?act=main">{_admin_tab_main}</a></li>
|
||||||
|
<li><a href="?act=permissions">{_c_group_permissions}</a></li>
|
||||||
|
<li><a href="?act=members">{_c_group_members}</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<form class="aui" method="POST">
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="id">ID</label>
|
||||||
|
<input class="text medium-field" type="text" id="id" disabled value="{$group->id}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="first_name">{_name}</label>
|
||||||
|
<input class="text medium-field" type="text" id="name" name="name" value="{$group->name}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="first_name">{_c_color}</label>
|
||||||
|
<input class="text medium-field" type="text" id="color" name="color" value="{$group->color}" />
|
||||||
|
</div>
|
||||||
|
<div class="buttons-container">
|
||||||
|
<div class="buttons">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input class="button submit" type="submit" value="{_save}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{elseif $isMembers}
|
||||||
|
<div class="aui-tabs horizontal-tabs">
|
||||||
|
<nav class="aui-navgroup aui-navgroup-horizontal">
|
||||||
|
<div class="aui-navgroup-inner">
|
||||||
|
<div class="aui-navgroup-primary">
|
||||||
|
<ul class="aui-nav">
|
||||||
|
<li><a href="?act=main">{_admin_tab_main}</a></li>
|
||||||
|
<li><a href="?act=permissions">{_c_group_permissions}</a></li>
|
||||||
|
<li class="aui-nav-selected"><a href="?act=members">{_c_group_members}</a></li>
|
||||||
|
<li>
|
||||||
|
<form class="aui" method="POST" style="display: flex;">
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="uid">UID</label>
|
||||||
|
<input class="text" type="text" id="uid" name="uid" />
|
||||||
|
</div>
|
||||||
|
<div style="margin: 5px;">
|
||||||
|
<div class="buttons">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input class="button submit" type="submit" value="{_add}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<table class="aui aui-table-list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>UUID</th>
|
||||||
|
<th>{_admin_name}</th>
|
||||||
|
<th>{_gender}</th>
|
||||||
|
<th>{_admin_shortcode}</th>
|
||||||
|
<th>{_registration_date}</th>
|
||||||
|
<th>{_admin_actions}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr n:foreach="$members as $user">
|
||||||
|
<td>{$user->getId()}</td>
|
||||||
|
<td>{$user->getChandlerGUID()}</td>
|
||||||
|
<td>
|
||||||
|
<span class="aui-avatar aui-avatar-xsmall">
|
||||||
|
<span class="aui-avatar-inner">
|
||||||
|
<img src="{$user->getAvatarUrl('miniscule')}" alt="{$user->getCanonicalName()}" style="object-fit: cover;" role="presentation" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a>
|
||||||
|
|
||||||
|
<span n:if="$user->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">{_admin_banned}</span>
|
||||||
|
</td>
|
||||||
|
<td>{$user->isFemale() ? tr("female") : tr("male")}</td>
|
||||||
|
<td>{$user->getShortCode() ?? "(" . tr("none") . ")"}</td>
|
||||||
|
<td>{$user->getRegistrationTime()}</td>
|
||||||
|
<td>
|
||||||
|
<a class="aui-button aui-button-primary" href="/admin/chandler/groups/{$group->id}?act=removeMember&uid={$user->getChandlerGUID()}">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-delete">{_delete}</span>
|
||||||
|
</a>
|
||||||
|
<a class="aui-button aui-button-primary" href="/admin/users/id{$user->getId()}">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||||
|
</a>
|
||||||
|
{if $thisUser->getChandlerUser()->can("substitute")->model('openvk\Web\Models\Entities\User')->whichBelongsTo(0)}
|
||||||
|
<a class="aui-button" href="/setSID/{$user->getChandlerUser()->getId()}?hash={rawurlencode($csrfToken)}">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-sign-in">{_admin_loginas}</span>
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{elseif $isPermissions}
|
||||||
|
<div class="aui-tabs horizontal-tabs">
|
||||||
|
<nav class="aui-navgroup aui-navgroup-horizontal">
|
||||||
|
<div class="aui-navgroup-inner">
|
||||||
|
<div class="aui-navgroup-primary">
|
||||||
|
<ul class="aui-nav">
|
||||||
|
<li><a href="?act=main">{_admin_tab_main}</a></li>
|
||||||
|
<li class="aui-nav-selected"><a href="?act=permissions">{_c_group_permissions}</a></li>
|
||||||
|
<li><a href="?act=members">{_c_group_members}</a></li>
|
||||||
|
<li>
|
||||||
|
<form class="aui" method="POST" style="display: flex;">
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="model">{_c_model}</label>
|
||||||
|
<input class="text" type="text" id="model" name="model" />
|
||||||
|
<input class="text" type="text" id="permission" name="permission" />
|
||||||
|
<label for="action">{_c_permission}</label>
|
||||||
|
</div>
|
||||||
|
<div style="margin: 5px;">
|
||||||
|
<div class="buttons">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input class="button submit" type="submit" value="{_add}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<table class="aui aui-table-list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{_c_model}</th>
|
||||||
|
<th>{_c_permission}</th>
|
||||||
|
<th>{_admin_actions}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr n:foreach="$perms as $perm">
|
||||||
|
<td>{$perm->model}</td>
|
||||||
|
<td>{$perm->permission}</td>
|
||||||
|
<td>
|
||||||
|
<a class="aui-button aui-button-primary" href="/admin/chandler/groups/{$perm->group}?act=removePermission&model={$perm->model}&perm={$perm->permission}">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-delete">{_edit}</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/block}
|
59
Web/Presenters/templates/Admin/ChandlerGroups.xml
Normal file
59
Web/Presenters/templates/Admin/ChandlerGroups.xml
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_c_groups}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block heading}
|
||||||
|
{_c_groups}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<form class="aui" method="POST">
|
||||||
|
<div class="field-group" style="margin-left: -65px;">
|
||||||
|
<label for="uid">{_admin_title}</label>
|
||||||
|
<div style="display: flex;">
|
||||||
|
<input class="text" type="text" id="name" name="name" />
|
||||||
|
<div class="buttons" style="margin-left: 5px;">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input class="button submit" type="submit" value="{_add}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<table class="aui aui-table-list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>{_admin_title}</th>
|
||||||
|
<th>{_admin_actions}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr n:foreach="$groups as $group">
|
||||||
|
<td>
|
||||||
|
<a href="/admin/chandler/groups/{$group->id}">{$group->id}</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="aui-lozenge aui-lozenge-subtle" style="text-transform: none;">
|
||||||
|
{$group->name}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a class="aui-button aui-button-primary" href="/admin/chandler/groups/{$group->id}">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||||
|
</a>
|
||||||
|
<a class="aui-button aui-button-primary" href="/admin/chandler/groups/{$group->id}?act=permissions">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-book">{_c_permissions}</span>
|
||||||
|
</a>
|
||||||
|
<a class="aui-button aui-button-primary" href="/admin/chandler/groups/{$group->id}?act=members">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-group">{_members}</span>
|
||||||
|
</a>
|
||||||
|
<a class="aui-button aui-button-secondary" href="/admin/chandler/groups/{$group->id}?act=delete">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-delete">{_delete}</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{/block}
|
|
@ -68,6 +68,43 @@
|
||||||
<option value="2" {if $user->onlineStatus() == 2}selected{/if}>{_admin_user_online_deceased}</option>
|
<option value="2" {if $user->onlineStatus() == 2}selected{/if}>{_admin_user_online_deceased}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<hr/>
|
||||||
|
<h2>{_c_groups}</h2>
|
||||||
|
<div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="add-to-group">{_c_add_to_group}</label>
|
||||||
|
<select class="select" name="add-to-group">
|
||||||
|
<option n:foreach="$c_groups_list as $group" value="{$group->id}">
|
||||||
|
{$group->name}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<table class="aui aui-table-list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>{_admin_actions}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr n:foreach="$c_memberships as $membership">
|
||||||
|
<td>
|
||||||
|
<a href="/admin/chandler/groups/{$membership->group}?act=members">{$membership->group}</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
class="aui-icon aui-icon-small aui-iconfont-cross"
|
||||||
|
href="/admin/chandler/groups/{$membership->group}?act=removeMember&uid={$user->getChandlerGUID()}"
|
||||||
|
style="margin: 0 50%;"
|
||||||
|
>
|
||||||
|
{_c_remove_from_group}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="buttons-container">
|
<div class="buttons-container">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
|
<th>UUID</th>
|
||||||
<th>{_admin_name}</th>
|
<th>{_admin_name}</th>
|
||||||
<th>{_gender}</th>
|
<th>{_gender}</th>
|
||||||
<th>{_admin_shortcode}</th>
|
<th>{_admin_shortcode}</th>
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr n:foreach="$users as $user">
|
<tr n:foreach="$users as $user">
|
||||||
<td>{$user->getId()}</td>
|
<td>{$user->getId()}</td>
|
||||||
|
<td>{$user->getChandlerGUID()}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="aui-avatar aui-avatar-xsmall">
|
<span class="aui-avatar aui-avatar-xsmall">
|
||||||
<span class="aui-avatar-inner">
|
<span class="aui-avatar-inner">
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
<input class="text long-field" type="number" min="0" id="coins" name="coins" value="{$form->coins}" />
|
<input class="text long-field" type="number" min="0" id="coins" name="coins" value="{$form->coins}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="rating">{_admin_voucher_rating}</label>
|
<label for="rating">{_admin_voucher_rating_number}</label>
|
||||||
<input class="text long-field" type="number" min="0" id="rating" name="rating" value="{$form->rating}" />
|
<input class="text long-field" type="number" min="0" id="rating" name="rating" value="{$form->rating}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
|
|
|
@ -87,12 +87,15 @@
|
||||||
{captcha_template()|noescape}
|
{captcha_template()|noescape}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="checkbox" required="true" name="confirmation" /> {_checkbox_in_registration|noescape}
|
||||||
|
<br /><br />
|
||||||
<input type="submit" value="{_registration}" class="button" />
|
<input type="submit" value="{_registration}" class="button" />
|
||||||
<a href="/login">{_log_in}</a>
|
<a href="/login">{_log_in}</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
22
Web/Presenters/templates/Away/View.xml
Normal file
22
Web/Presenters/templates/Away/View.xml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block title}Переход по ссылке заблокирован{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
Предупреждение
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<div style="min-height: 120px;">
|
||||||
|
<img src="/assets/packages/static/openvk/img/oof.apng" width="110" height="110" style="margin-left: 20px;">
|
||||||
|
|
||||||
|
<div style="padding-left: 150px; margin-top: -100px;">
|
||||||
|
<h4 style="font-size: 14px; margin-bottom: 8px;">{_url_is_banned_title}</h4>
|
||||||
|
<span>
|
||||||
|
{$link->getComment()|noescape}
|
||||||
|
</span>
|
||||||
|
<br><br>
|
||||||
|
<a href="{$to}" class="button" target="_blank">{_url_is_banned_proceed}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/block}
|
20
Web/Presenters/templates/Maintenance/All.xml
Normal file
20
Web/Presenters/templates/Maintenance/All.xml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_global_maintenance}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
{_global_maintenance}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<div class="container_gray">
|
||||||
|
<center style="background: white;border: #DEDEDE solid 1px;">
|
||||||
|
<img src="/assets/packages/static/openvk/img/oof.apng" style="width: 20%;" />
|
||||||
|
<span style="color: #707070;margin: 10px 0;display: block;">
|
||||||
|
{_undergoing_global_maintenance}
|
||||||
|
</span>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
{/block}
|
20
Web/Presenters/templates/Maintenance/Section.xml
Normal file
20
Web/Presenters/templates/Maintenance/Section.xml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_section_maintenance}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
{_section_maintenance}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<div class="container_gray">
|
||||||
|
<center style="background: white;border: #DEDEDE solid 1px;">
|
||||||
|
<img src="/assets/packages/static/openvk/img/oof.apng" style="width: 20%;" />
|
||||||
|
<span style="color: #707070;margin: 10px 0;display: block;">
|
||||||
|
{tr("undergoing_section_maintenance", $name)|noescape}
|
||||||
|
</span>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
{/block}
|
|
@ -1,4 +1,4 @@
|
||||||
{extends "../@listView.xml"}
|
{extends "../@layout.xml"}
|
||||||
{var $sorting = false}
|
{var $sorting = false}
|
||||||
|
|
||||||
{block title}
|
{block title}
|
||||||
|
@ -10,29 +10,47 @@
|
||||||
{_feedback}
|
{_feedback}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block tabs}
|
{block content}
|
||||||
|
<div n:ifcontent class="tabs">
|
||||||
<div n:attr="id => ($mode === 'new' ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($mode === 'new' ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($mode === 'new' ? 'act_tab_a' : 'ki')" href="?act=new">{_unread}</a>
|
<a n:attr="id => ($mode === 'new' ? 'act_tab_a' : 'ki')" href="?act=new">{_unread}</a>
|
||||||
</div>
|
</div>
|
||||||
<div n:attr="id => ($mode === 'archived' ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($mode === 'archived' ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($mode === 'archived' ? 'act_tab_a' : 'ki')" href="?act=archived">{_archive}</a>
|
<a n:attr="id => ($mode === 'archived' ? 'act_tab_a' : 'ki')" href="?act=archived">{_archive}</a>
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
</div>
|
||||||
|
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
{if sizeof($data) > 0}
|
||||||
|
<table class="post post-divider" border="0" style="font-size: 11px;" n:foreach="$data as $dat">
|
||||||
{block link|strip|stripHtml}
|
<tbody>
|
||||||
javascript:void(0)
|
<tr>
|
||||||
{/block}
|
<td width="54" valign="top">
|
||||||
|
<a href="/sysop">
|
||||||
{block preview}
|
<img src="{$dat->getModel(1)->getAvatarUrl('miniscule')}" width=50 />
|
||||||
<img src="{$x->getModel(1)->getAvatarUrl('miniscule')}" width=64 />
|
</a>
|
||||||
{/block}
|
</td>
|
||||||
|
<td width="100%" valign="top">
|
||||||
{block name}
|
<div class="post-content">
|
||||||
<span style="color: #000; font-weight: 100;"></span>
|
<div class="text" style="line-height: 12pt;">
|
||||||
{/block}
|
{include $dat->getTemplatePath(), notification => $dat}
|
||||||
|
</div>
|
||||||
{block description}
|
</div>
|
||||||
{include $x->getTemplatePath(), notification => $x}
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{include "../components/paginator.xml", conf => (object) [
|
||||||
|
"page" => $page,
|
||||||
|
"count" => $count,
|
||||||
|
"amount" => sizeof($data),
|
||||||
|
"perPage" => $perPage ?? OPENVK_DEFAULT_PER_PAGE,
|
||||||
|
"atBottom" => true,
|
||||||
|
]}
|
||||||
|
{else}
|
||||||
|
{ifset customErrorMessage}
|
||||||
|
{include customErrorMessage}
|
||||||
|
{else}
|
||||||
|
{include "../components/nothing.xml"}
|
||||||
|
{/ifset}
|
||||||
|
{/if}
|
||||||
{/block}
|
{/block}
|
44
Web/Presenters/templates/Poll/Poll.xml
Normal file
44
Web/Presenters/templates/Poll/Poll.xml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{if !isset($parentModule) || substr($parentModule, 0, 21) === 'libchandler:absolute.'}
|
||||||
|
<link rel="shortcut icon" href="/assets/packages/static/openvk/img/icon.ico" />
|
||||||
|
<meta n:ifset="$csrfToken" name="csrf" value="{$csrfToken}" />
|
||||||
|
<script src="/language/{getLanguage()}.js" crossorigin="anonymous"></script>
|
||||||
|
{script "js/node_modules/jquery/dist/jquery.min.js"}
|
||||||
|
{script "js/node_modules/umbrellajs/umbrella.min.js"}
|
||||||
|
{script "js/node_modules/msgpack-lite/dist/msgpack.min.js"}
|
||||||
|
{script "js/messagebox.js"}
|
||||||
|
{script "js/l10n.js"}
|
||||||
|
{script "js/al_api.js"}
|
||||||
|
{script "js/al_polls.js"}
|
||||||
|
{include "../_includeCSS.xml"}
|
||||||
|
|
||||||
|
<style>body { margin: 8px; }</style>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="poll">
|
||||||
|
<h4>{$title}</h4>
|
||||||
|
<form onsubmit="pollFormSubmit(event, this)" data-multi="{$multiple ? '1' : '0'}" data-pid="{$id}">
|
||||||
|
<div class="poll-options">
|
||||||
|
<div n:foreach="$options as $oid => $option" class="poll-option">
|
||||||
|
<label>
|
||||||
|
{if $multiple}
|
||||||
|
<input n:attr="disabled => is_null($thisUser)" type="checkbox" name="option{$oid}" onclick="pollCheckBoxPressed(this)" />
|
||||||
|
{else}
|
||||||
|
<input n:attr="disabled => is_null($thisUser)" type="radio" value="{$oid}" name="vote" onclick="pollRadioPressed(this)" />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{$option}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{if $multiple}
|
||||||
|
<br/>
|
||||||
|
<input type="submit" class="button" value="{_cast_vote}" disabled="disabled" />
|
||||||
|
{/if}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="poll-meta">
|
||||||
|
{tr("poll_voter_count", $votes)|noescape}<br/>
|
||||||
|
<span class="nobold">{$meta}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
56
Web/Presenters/templates/Poll/PollResults.xml
Normal file
56
Web/Presenters/templates/Poll/PollResults.xml
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
{if !isset($parentModule) || substr($parentModule, 0, 21) === 'libchandler:absolute.'}
|
||||||
|
<link rel="shortcut icon" href="/assets/packages/static/openvk/img/icon.ico" />
|
||||||
|
<meta n:ifset="$csrfToken" name="csrf" value="{$csrfToken}" />
|
||||||
|
<script src="/language/{getLanguage()}.js" crossorigin="anonymous"></script>
|
||||||
|
{script "js/node_modules/jquery/dist/jquery.min.js"}
|
||||||
|
{script "js/node_modules/umbrellajs/umbrella.min.js"}
|
||||||
|
{script "js/node_modules/msgpack-lite/dist/msgpack.min.js"}
|
||||||
|
{script "js/messagebox.js"}
|
||||||
|
{script "js/l10n.js"}
|
||||||
|
{script "js/al_api.js"}
|
||||||
|
{script "js/al_polls.js"}
|
||||||
|
{include "../_includeCSS.xml"}
|
||||||
|
|
||||||
|
<style>body { margin: 8px; } .poll { border: 1px solid #e3e3e3; }</style>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="poll" data-id="{$id}">
|
||||||
|
<a n:if="$unlocked && $voted" href="javascript:pollRetractVote({$id})" class="poll-retract-vote">{_retract_vote}</a>
|
||||||
|
<h4>{$title}</h4>
|
||||||
|
<div class="poll-results">
|
||||||
|
<div n:foreach="$results->options as $option" class="poll-result">
|
||||||
|
{if $isAnon}
|
||||||
|
<a href="javascript:false">
|
||||||
|
{if $option->voted}
|
||||||
|
<b>{$option->name}</b>
|
||||||
|
{else}
|
||||||
|
{$option->name}
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
|
{else}
|
||||||
|
<a href="/poll{$id}/voters?option={base_convert($option->id, 10, 32)}">
|
||||||
|
{if $option->voted}
|
||||||
|
<b>{$option->name}</b>
|
||||||
|
{else}
|
||||||
|
{$option->name}
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="poll-result-barspace">
|
||||||
|
<div class="poll-result-bar">
|
||||||
|
<span class="poll-result-count">{$option->votes}</span>
|
||||||
|
<div class="poll-result-bar-sub" style="width: {$option->pct}%"> </div>
|
||||||
|
</div>
|
||||||
|
<div class="poll-result-pct">
|
||||||
|
<strong>{$option->pct}%</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="poll-meta">
|
||||||
|
{tr("poll_voter_count", $votes)|noescape}<br/>
|
||||||
|
<span class="nobold">{$meta}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
40
Web/Presenters/templates/Poll/Voters.xml
Normal file
40
Web/Presenters/templates/Poll/Voters.xml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
{extends "../@listView.xml"}
|
||||||
|
|
||||||
|
{block title}
|
||||||
|
{_poll_voters_list}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
{_poll_voters_list} »
|
||||||
|
{$option[1]}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block tabs}
|
||||||
|
<div n:foreach="$options as $optionId => $optionName" class="tab" id="{$optionId == $option[0] ? 'activetabs' : 'ki'}">
|
||||||
|
<a id="{$optionId == $option[0] ? 'act_tab_a' : ''}" href="/poll{$pollId}/voters?option={base_convert($optionId, 10, 32)}">{$optionName}</a>
|
||||||
|
</div>
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||||
|
|
||||||
|
{block link|strip|stripHtml}
|
||||||
|
{$x->getURL()}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block preview}
|
||||||
|
<img src="{$x->getAvatarUrl('miniscule')}" width="75" alt="Фотография пользователя" />
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block name}
|
||||||
|
{$x->getCanonicalName()}
|
||||||
|
<img n:if="$x->isVerified()"
|
||||||
|
class="name-checkmark"
|
||||||
|
src="/assets/packages/static/openvk/img/checkmark.png"
|
||||||
|
/>
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block description}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block actions}
|
||||||
|
{/block}
|
92
Web/Presenters/templates/Support/Agent.xml
Normal file
92
Web/Presenters/templates/Support/Agent.xml
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
{_helpdesk_agent_card}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
{var $isInfo = $mode === "info"}
|
||||||
|
{var $isEdit = $mode === "edit"}
|
||||||
|
|
||||||
|
{if $agent != NULL}
|
||||||
|
<div class="left_small_block">
|
||||||
|
<img src="{$agent->getAvatarURL()}" style="width:100%;" />
|
||||||
|
<div class="profile_links">
|
||||||
|
<div n:if="$agent_id == $thisUser->getId()" id="profile_link" style="width: 194px;">
|
||||||
|
<a href="?act=edit" class="link">{_edit_page}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right_big_block">
|
||||||
|
<div class="page_info">
|
||||||
|
<div class="accountInfo clearFix">
|
||||||
|
<a href="/id{$agent->getAgentId()}">
|
||||||
|
<div class="profileName" style="display: flex;">
|
||||||
|
<h2>{$agent->getCanonicalName()}</h2>
|
||||||
|
<span class="nobold"> ({$agent->getRealName()})</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; justify-content: space-between; padding: 10px; font-size: 12px;">
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<div>
|
||||||
|
<b style="color: green;">{$counters["good"]}</b>
|
||||||
|
</div>
|
||||||
|
<div>{_helpdesk_positive_answers}</div>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<div>
|
||||||
|
<b style="color: red;">{$counters["bad"]}</b>
|
||||||
|
</div>
|
||||||
|
<div>{_helpdesk_negative_answers}</div>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<div>
|
||||||
|
<b>{$counters["all"]}</b>
|
||||||
|
</div>
|
||||||
|
<div>{_helpdesk_all_answers}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{if $isEdit}
|
||||||
|
<h4>{_edit}</h4>
|
||||||
|
<br/>
|
||||||
|
<form method="post" action="/support/agent{$agent_id}/edit">
|
||||||
|
<label for="name">{_helpdesk_showing_name}</label>
|
||||||
|
<input name="name" type="text" value="{$agent->getCanonicalName()}" placeholder="{_helpdesk_agent} #777" />
|
||||||
|
<br/><br/>
|
||||||
|
<label for="number">{_helpdesk_show_number}?</label>
|
||||||
|
{$agent->isShowNumber()}
|
||||||
|
<select name="number">
|
||||||
|
<option value="1" n:attr="selected => $agent->isShowNumber() === 1 ? true : false">{_yes}</option>
|
||||||
|
<option value="0" n:attr="selected => $agent->isShowNumber() === 0 ? true : false">{_no}</option>
|
||||||
|
</select>
|
||||||
|
<br/><br/>
|
||||||
|
<label for="number">{_avatar}</label>
|
||||||
|
<input name="avatar" type="text" value="{$agent->getAvatarURL()}" placeholder="{_helpdesk_avatar_url}" />
|
||||||
|
<input type="hidden" value="{$csrfToken}" name="hash" />
|
||||||
|
<br/><br/>
|
||||||
|
<input type="submit" class="button" value="{_save}" />
|
||||||
|
</form>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{else}
|
||||||
|
<h4>Создать</h4>
|
||||||
|
<br/>
|
||||||
|
<form method="post" action="/support/agent{$agent_id}/edit">
|
||||||
|
<label for="name">{_helpdesk_showing_name}</label>
|
||||||
|
<input name="name" type="text" placeholder="{_helpdesk_agent} #777" />
|
||||||
|
<br/><br/>
|
||||||
|
<label for="number">{_helpdesk_show_number}?</span></label>
|
||||||
|
<select name="number">
|
||||||
|
<option value="1">{_yes}</option>
|
||||||
|
<option value="0">{_no}</option>
|
||||||
|
</select>
|
||||||
|
<br/><br/>
|
||||||
|
<label for="number">{_avatar}</label>
|
||||||
|
<input name="avatar" type="text" placeholder="{_helpdesk_avatar_url}" />
|
||||||
|
<input type="hidden" value="{$csrfToken}" name="hash" />
|
||||||
|
<input type="submit" class="button" value="{_save}" />
|
||||||
|
</form>
|
||||||
|
{/if}
|
||||||
|
{/block}
|
|
@ -66,7 +66,7 @@
|
||||||
</div>
|
</div>
|
||||||
{elseif ($comment->getUType() === 1)}
|
{elseif ($comment->getUType() === 1)}
|
||||||
<div class="post-author">
|
<div class="post-author">
|
||||||
<a><b>{$comment->getAuthorName()}</b></a>
|
<a n:attr="href => $thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0) ? '/support/agent' . $comment->getUser()->getId() : ''"><b>{$comment->getAuthorName()}</b></a>
|
||||||
{if $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
{if $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
||||||
<a href="{$comment->getUser()->getURL()}">
|
<a href="{$comment->getUser()->getURL()}">
|
||||||
<span class="nobold">
|
<span class="nobold">
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
<a n:attr="id => ($isNew ? 'act_tab_a' : 'ki')" href="/support?act=new">{_support_new}</a>
|
<a n:attr="id => ($isNew ? 'act_tab_a' : 'ki')" href="/support?act=new">{_support_new}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
{if $isNew}
|
{if $isNew}
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
<div n:attr="id => ($act === 'closed' ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($act === 'closed' ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($act === 'closed' ? 'act_tab_a' : 'ki')" href="?act=closed">{_support_closed}</a>
|
<a n:attr="id => ($act === 'closed' ? 'act_tab_a' : 'ki')" href="?act=closed">{_support_closed}</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab">
|
||||||
|
<a href="/support/agent{$thisUser->getId()}">Мой профиль</a>
|
||||||
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||||
|
|
|
@ -94,7 +94,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<select name="marialstatus">
|
<select name="marialstatus">
|
||||||
<option n:foreach="range(0, 8) as $i" n:attr="selected: ($user->getMaritalStatus() == $i)" value="{$i}">
|
<option n:foreach="range(0, 8) as $i" n:attr="selected => ($user->getMaritalStatus() == $i)" value="{$i}">
|
||||||
{if $user->isFemale()}
|
{if $user->isFemale()}
|
||||||
{var $str = "relationship_$i"}
|
{var $str = "relationship_$i"}
|
||||||
{if tr($str . "_fem") == ("@$str" . "_fem")}
|
{if tr($str . "_fem") == ("@$str" . "_fem")}
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
<div n:attr="id => ($act === 'online' ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($act === 'online' ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($act === 'online' ? 'act_tab_a' : 'ki')" href="?act=online">{_online}</a>
|
<a n:attr="id => ($act === 'online' ? 'act_tab_a' : 'ki')" href="?act=online">{_online}</a>
|
||||||
</div>
|
</div>
|
||||||
<div n:attr="id => ($act === 'incoming' || $act === 'outcoming' ? 'activetabs' : 'ki')" class="tab">
|
<div n:if="!is_null($thisUser) && $user->getId() === $thisUser->getId()" n:attr="id => ($act === 'incoming' || $act === 'outcoming' ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($act === 'incoming' || $act === 'outcoming' ? 'act_tab_a' : 'ki')" href="?act=incoming">{_req}</a>
|
<a n:attr="id => ($act === 'incoming' || $act === 'outcoming' ? 'act_tab_a' : 'ki')" href="?act=incoming">{_req}</a>
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -79,9 +79,9 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block actions}
|
{block actions}
|
||||||
|
<a href="{$x->getURL()}" class="profile_link">{_check_community}</a>
|
||||||
{if $x->canBeModifiedBy($thisUser ?? NULL)}
|
{if $x->canBeModifiedBy($thisUser ?? NULL)}
|
||||||
{var $clubPinned = $thisUser->isClubPinned($x)}
|
{var $clubPinned = $thisUser->isClubPinned($x)}
|
||||||
<a href="{$x->getURL()}" class="profile_link">{_check_community}</a>
|
|
||||||
<a href="/groups_pin?club={$x->getId()}&hash={rawurlencode($csrfToken)}" class="profile_link" n:if="$clubPinned || $thisUser->getPinnedClubCount() <= 10" id="_pinGroup" data-group-name="{$x->getName()}" data-group-url="{$x->getUrl()}">
|
<a href="/groups_pin?club={$x->getId()}&hash={rawurlencode($csrfToken)}" class="profile_link" n:if="$clubPinned || $thisUser->getPinnedClubCount() <= 10" id="_pinGroup" data-group-name="{$x->getName()}" data-group-url="{$x->getUrl()}">
|
||||||
{if $clubPinned}
|
{if $clubPinned}
|
||||||
{_remove_from_left_menu}
|
{_remove_from_left_menu}
|
||||||
|
@ -89,6 +89,15 @@
|
||||||
{_add_to_left_menu}
|
{_add_to_left_menu}
|
||||||
{/if}
|
{/if}
|
||||||
</a>
|
</a>
|
||||||
|
{/if}
|
||||||
|
{if $x->getSubscriptionStatus($thisUser) == false}
|
||||||
|
<form class="profile_link_form" action="/setSub/club" method="post">
|
||||||
|
<input type="hidden" name="act" value="add" />
|
||||||
|
<input type="hidden" name="id" value="{$x->getId()}" />
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" class="profile_link" value="{_join_community}" />
|
||||||
|
</form>
|
||||||
|
{else}
|
||||||
<form class="profile_link_form" action="/setSub/club" method="post">
|
<form class="profile_link_form" action="/setSub/club" method="post">
|
||||||
<input type="hidden" name="act" value="rem" />
|
<input type="hidden" name="act" value="rem" />
|
||||||
<input type="hidden" name="id" value="{$x->getId()}" />
|
<input type="hidden" name="id" value="{$x->getId()}" />
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
{block content}
|
{block content}
|
||||||
|
|
||||||
{var $isMain = $mode === 'main'}
|
{var $isMain = $mode === 'main'}
|
||||||
|
{var $isSecurity = $mode === 'security'}
|
||||||
{var $isPrivacy = $mode === 'privacy'}
|
{var $isPrivacy = $mode === 'privacy'}
|
||||||
{var $isFinance = $mode === 'finance'}
|
{var $isFinance = $mode === 'finance'}
|
||||||
{var $isFinanceTU = $mode === 'finance.top-up'}
|
{var $isFinanceTU = $mode === 'finance.top-up'}
|
||||||
|
@ -17,6 +18,9 @@
|
||||||
<div n:attr="id => ($isMain ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($isMain ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($isMain ? 'act_tab_a' : 'ki')" href="/settings">{_main}</a>
|
<a n:attr="id => ($isMain ? 'act_tab_a' : 'ki')" href="/settings">{_main}</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div n:attr="id => ($isSecurity ? 'activetabs' : 'ki')" class="tab">
|
||||||
|
<a n:attr="id => ($isSecurity ? 'act_tab_a' : 'ki')" href="/settings?act=security">{_security}</a>
|
||||||
|
</div>
|
||||||
<div n:attr="id => ($isPrivacy ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($isPrivacy ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($isPrivacy ? 'act_tab_a' : 'ki')" href="/settings?act=privacy">{_privacy}</a>
|
<a n:attr="id => ($isPrivacy ? 'act_tab_a' : 'ki')" href="/settings?act=privacy">{_privacy}</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,117 +36,6 @@
|
||||||
{if $isMain}
|
{if $isMain}
|
||||||
|
|
||||||
<form action="/settings?act=main" method="POST" enctype="multipart/form-data">
|
<form action="/settings?act=main" method="POST" enctype="multipart/form-data">
|
||||||
<h4>{_change_password}</h4>
|
|
||||||
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td width="120" valign="top">
|
|
||||||
<span class="nobold">{_old_password}</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="password" name="old_pass" style="width: 100%;" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td width="120" valign="top">
|
|
||||||
<span class="nobold">{_new_password}</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="password" name="new_pass" style="width: 100%;" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td width="120" valign="top">
|
|
||||||
<span class="nobold">{_repeat_password}</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="password" name="repeat_pass" style="width: 100%;" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr n:if="$user->is2faEnabled()">
|
|
||||||
<td width="120" valign="top">
|
|
||||||
<span class="nobold">{_"2fa_code"}</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="text" name="password_change_code" style="width: 100%;" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
|
||||||
<input type="submit" value="{_change_password}" class="button" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<br/>
|
|
||||||
<h4>{_two_factor_authentication}</h4>
|
|
||||||
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
|
||||||
<tbody>
|
|
||||||
{if $user->is2faEnabled()}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div class="accent-box">
|
|
||||||
{_two_factor_authentication_enabled}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="text-align: center;">
|
|
||||||
<a class="button" href="javascript:viewBackupCodes()">{_view_backup_codes}</a>
|
|
||||||
<a class="button" href="javascript:disableTwoFactorAuth()">{_disable}</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function viewBackupCodes() {
|
|
||||||
MessageBox("Просмотр резервных кодов", `
|
|
||||||
<form id="back-codes-view-form" method="post" action="/settings/2fa">
|
|
||||||
<label for="password">Пароль</label>
|
|
||||||
<input type="password" id="password" name="password" required />
|
|
||||||
<input type="hidden" name="hash" value={$csrfToken} />
|
|
||||||
</form>
|
|
||||||
`, ["Просмотреть", "Отменить"], [
|
|
||||||
() => {
|
|
||||||
document.querySelector("#back-codes-view-form").submit();
|
|
||||||
}, Function.noop
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function disableTwoFactorAuth() {
|
|
||||||
MessageBox("Отключить 2FA", `
|
|
||||||
<form id="two-factor-auth-disable-form" method="post" action="/settings/2fa/disable">
|
|
||||||
<label for="password">Пароль</label>
|
|
||||||
<input type="password" id="password" name="password" required />
|
|
||||||
<input type="hidden" name="hash" value={$csrfToken} />
|
|
||||||
</form>
|
|
||||||
`, ["Отключить", "Отменить"], [
|
|
||||||
() => {
|
|
||||||
document.querySelector("#two-factor-auth-disable-form").submit();
|
|
||||||
}, Function.noop
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{else}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div class="accent-box">
|
|
||||||
{_two_factor_authentication_disabled}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="text-align: center;">
|
|
||||||
<a class="button" href="/settings/2fa">{_connect}</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/if}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<br/>
|
|
||||||
<h4>{_your_email_address}</h4>
|
<h4>{_your_email_address}</h4>
|
||||||
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -226,6 +119,141 @@
|
||||||
{_you_can_also} <a onClick="showProfileDeactivateDialog({$csrfToken})">{_delete_your_page}</a>.
|
{_you_can_also} <a onClick="showProfileDeactivateDialog({$csrfToken})">{_delete_your_page}</a>.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{elseif $isSecurity}
|
||||||
|
<form action="/settings?act=main" method="POST" enctype="multipart/form-data">
|
||||||
|
<h4>{_change_password}</h4>
|
||||||
|
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top">
|
||||||
|
<span class="nobold">{_old_password}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="password" name="old_pass" style="width: 100%;" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top">
|
||||||
|
<span class="nobold">{_new_password}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="password" name="new_pass" style="width: 100%;" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top">
|
||||||
|
<span class="nobold">{_repeat_password}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="password" name="repeat_pass" style="width: 100%;" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr n:if="$user->is2faEnabled()">
|
||||||
|
<td width="120" valign="top">
|
||||||
|
<span class="nobold">{_"2fa_code"}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="password_change_code" style="width: 100%;" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" value="{_change_password}" class="button" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
</form>
|
||||||
|
<br/>
|
||||||
|
<h4>{_two_factor_authentication}</h4>
|
||||||
|
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||||
|
<tbody>
|
||||||
|
{if $user->is2faEnabled()}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="accent-box">
|
||||||
|
{_two_factor_authentication_enabled}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="text-align: center;">
|
||||||
|
<a class="button" href="javascript:viewBackupCodes()">{_view_backup_codes}</a>
|
||||||
|
<a class="button" href="javascript:disableTwoFactorAuth()">{_disable}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function viewBackupCodes() {
|
||||||
|
MessageBox("Просмотр резервных кодов", `
|
||||||
|
<form id="back-codes-view-form" method="post" action="/settings/2fa">
|
||||||
|
<label for="password">Пароль</label>
|
||||||
|
<input type="password" id="password" name="password" required />
|
||||||
|
<input type="hidden" name="hash" value={$csrfToken} />
|
||||||
|
</form>
|
||||||
|
`, ["Просмотреть", "Отменить"], [
|
||||||
|
() => {
|
||||||
|
document.querySelector("#back-codes-view-form").submit();
|
||||||
|
}, Function.noop
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableTwoFactorAuth() {
|
||||||
|
MessageBox("Отключить 2FA", `
|
||||||
|
<form id="two-factor-auth-disable-form" method="post" action="/settings/2fa/disable">
|
||||||
|
<label for="password">Пароль</label>
|
||||||
|
<input type="password" id="password" name="password" required />
|
||||||
|
<input type="hidden" name="hash" value={$csrfToken} />
|
||||||
|
</form>
|
||||||
|
`, ["Отключить", "Отменить"], [
|
||||||
|
() => {
|
||||||
|
document.querySelector("#two-factor-auth-disable-form").submit();
|
||||||
|
}, Function.noop
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{else}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="accent-box">
|
||||||
|
{_two_factor_authentication_disabled}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="text-align: center;">
|
||||||
|
<a class="button" href="/settings/2fa">{_connect}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h4>{_ui_settings_sessions}</h4>
|
||||||
|
<form action="/revokeAllTokens" method="POST">
|
||||||
|
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="accent-box">
|
||||||
|
{tr("end_all_sessions_description", OPENVK_ROOT_CONF['openvk']['appearance']['name'])}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="120" valign="top" style="text-align: center;">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" value="{_end_all_sessions}" class="button" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
|
||||||
{elseif $isPrivacy}
|
{elseif $isPrivacy}
|
||||||
|
|
||||||
<form action="/settings?act=privacy" method="POST" enctype="multipart/form-data">
|
<form action="/settings?act=privacy" method="POST" enctype="multipart/form-data">
|
||||||
|
|
|
@ -543,12 +543,14 @@
|
||||||
uBanMsgTxt = "Вы собираетесь забанить пользователя " + {$user->getCanonicalName()} + ".";
|
uBanMsgTxt = "Вы собираетесь забанить пользователя " + {$user->getCanonicalName()} + ".";
|
||||||
uBanMsgTxt += "<br/><b>Предупреждение</b>: Это действие удалит все подписки пользователя и отпишет всех от него.";
|
uBanMsgTxt += "<br/><b>Предупреждение</b>: Это действие удалит все подписки пользователя и отпишет всех от него.";
|
||||||
uBanMsgTxt += "<br/><br/><b>Причина бана</b>: <input type='text' id='uBanMsgInput' placeholder='придумайте что-нибудь крутое' />"
|
uBanMsgTxt += "<br/><br/><b>Причина бана</b>: <input type='text' id='uBanMsgInput' placeholder='придумайте что-нибудь крутое' />"
|
||||||
|
uBanMsgTxt += "<br/><br/><b>Заблокировать до</b>: <input type='date' id='uBanMsgDate' />";
|
||||||
|
|
||||||
MessageBox("Забанить " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [
|
MessageBox("Забанить " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [
|
||||||
(function() {
|
(function() {
|
||||||
res = document.querySelector("#uBanMsgInput").value;
|
res = document.querySelector("#uBanMsgInput").value;
|
||||||
|
date = document.querySelector("#uBanMsgDate").value;
|
||||||
xhr = new XMLHttpRequest();
|
xhr = new XMLHttpRequest();
|
||||||
xhr.open("GET", "/admin/ban/" + {$user->getId()} + "?reason=" + res + "&hash=" + {rawurlencode($csrfToken)}, true);
|
xhr.open("GET", "/admin/ban/" + {$user->getId()} + "?reason=" + res + "&date=" + date + "&hash=" + {rawurlencode($csrfToken)}, true);
|
||||||
xhr.onload = (function() {
|
xhr.onload = (function() {
|
||||||
if(xhr.responseText.indexOf("success") === -1)
|
if(xhr.responseText.indexOf("success") === -1)
|
||||||
MessageBox("Ошибка", "Не удалось забанить пользователя...", ["OK"], [Function.noop]);
|
MessageBox("Ошибка", "Не удалось забанить пользователя...", ["OK"], [Function.noop]);
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
<img src="/assets/packages/static/openvk/img/oof.apng" alt="Пользователь заблокирован." style="width: 20%;" />
|
<img src="/assets/packages/static/openvk/img/oof.apng" alt="Пользователь заблокирован." style="width: 20%;" />
|
||||||
<p>
|
<p>
|
||||||
{tr("user_banned", htmlentities($user->getFirstName()))|noescape}<br/>
|
{tr("user_banned", htmlentities($user->getFirstName()))|noescape}<br/>
|
||||||
{_user_banned_comment} <b>{$user->getBanReason()}</b>.
|
{_user_banned_comment} <b>{$user->getBanReason()}</b>.<br/>
|
||||||
|
Пользователь заблокирован до: <b>{$user->getUnbanTime()}</b>
|
||||||
</p>
|
</p>
|
||||||
{if isset($thisUser)}
|
{if isset($thisUser)}
|
||||||
<p n:if="$thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL) || $thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)">
|
<p n:if="$thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL) || $thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)">
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
<div style="width: 100%; min-height: 100px;">
|
<div style="width: 100%; min-height: 100px;">
|
||||||
<div style="float: left; min-height: 100px; width: 70%;">
|
<div style="float: left; min-height: 100px; width: 68%; margin-right: 2%;">
|
||||||
{include "../components/comments.xml",
|
{include "../components/comments.xml",
|
||||||
comments => $comments,
|
comments => $comments,
|
||||||
count => $cCount,
|
count => $cCount,
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div n:class="postFeedWrapper, $thisUser->hasMicroblogEnabled() ? postFeedWrapperMicroblog">
|
<div n:class="postFeedWrapper, $thisUser->hasMicroblogEnabled() ? postFeedWrapperMicroblog">
|
||||||
{include "../components/textArea.xml", route => "/wall" . $thisUser->getId() . "/makePost"}
|
{include "../components/textArea.xml", route => "/wall" . $thisUser->getId() . "/makePost", graffiti => true, polls => true}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{foreach $posts as $post}
|
{foreach $posts as $post}
|
||||||
|
|
52
Web/Presenters/templates/_includeCSS.xml
Normal file
52
Web/Presenters/templates/_includeCSS.xml
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
{ifset $thisUser}
|
||||||
|
{if $thisUser->getNsfwTolerance() < 2}
|
||||||
|
{css "css/nsfw-posts.css"}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{if $theme !== NULL}
|
||||||
|
{if $theme->inheritDefault()}
|
||||||
|
{css "css/style.css"}
|
||||||
|
{css "css/dialog.css"}
|
||||||
|
{css "css/notifications.css"}
|
||||||
|
|
||||||
|
{if $isXmas}
|
||||||
|
{css "css/xmas.css"}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/themepack/{$theme->getId()}/{$theme->getVersion()}/stylesheet/styles.css" />
|
||||||
|
|
||||||
|
{if $isXmas}
|
||||||
|
<link rel="stylesheet" href="/themepack/{$theme->getId()}/{$theme->getVersion()}/resource/xmas.css" />
|
||||||
|
{/if}
|
||||||
|
{else}
|
||||||
|
{css "css/style.css"}
|
||||||
|
{css "css/dialog.css"}
|
||||||
|
{css "css/notifications.css"}
|
||||||
|
|
||||||
|
{if $isXmas}
|
||||||
|
{css "css/xmas.css"}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{if $thisUser->getStyleAvatar() == 1}
|
||||||
|
{css "css/avatar.1.css"}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{if $thisUser->getStyleAvatar() == 2}
|
||||||
|
{css "css/avatar.2.css"}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{if $thisUser->hasMicroblogEnabled() == 1}
|
||||||
|
{css "css/microblog.css"}
|
||||||
|
{/if}
|
||||||
|
{else}
|
||||||
|
{css "css/style.css"}
|
||||||
|
{css "css/dialog.css"}
|
||||||
|
{css "css/nsfw-posts.css"}
|
||||||
|
{css "css/notifications.css"}
|
||||||
|
|
||||||
|
{if $isXmas}
|
||||||
|
{css "css/xmas.css"}
|
||||||
|
{/if}
|
||||||
|
{/ifset}
|
|
@ -11,6 +11,8 @@
|
||||||
{/if}
|
{/if}
|
||||||
{elseif $attachment instanceof \openvk\Web\Models\Entities\Video}
|
{elseif $attachment instanceof \openvk\Web\Models\Entities\Video}
|
||||||
<video class="media" src="{$attachment->getURL()}" controls="controls"></video>
|
<video class="media" src="{$attachment->getURL()}" controls="controls"></video>
|
||||||
|
{elseif $attachment instanceof \openvk\Web\Models\Entities\Poll}
|
||||||
|
{presenter "openvk!Poll->view", $attachment->getId()}
|
||||||
{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}
|
||||||
|
|
|
@ -2,5 +2,8 @@
|
||||||
{var $user = $notification->getModel(1)}
|
{var $user = $notification->getModel(1)}
|
||||||
|
|
||||||
<a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a>
|
<a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a>
|
||||||
{$notification->getDateTime()} {_nt_liked_yours}
|
{_nt_liked_yours}
|
||||||
<a href="/wall{$post->getPrettyId()}"><b>{_nt_post_nominative}</b></a> {_nt_from} {$post->getPublicationTime()}.
|
<a href="/wall{$post->getPrettyId()}"><b>{_nt_post_nominative}</b></a> {_nt_from} {$post->getPublicationTime()}.
|
||||||
|
<div class="nobold">
|
||||||
|
{$notification->getDateTime()}
|
||||||
|
</div>
|
||||||
|
|
|
@ -2,5 +2,8 @@
|
||||||
{var $user = $notification->getModel(1)}
|
{var $user = $notification->getModel(1)}
|
||||||
|
|
||||||
<a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a>
|
<a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a>
|
||||||
{$notification->getDateTime()} {_nt_shared_yours}
|
{_nt_shared_yours}
|
||||||
<a href="/wall{$post->getPrettyId()}"><b>{_nt_post_instrumental}</b></a> {_nt_from} {$post->getPublicationTime()}.
|
<a href="/wall{$post->getPrettyId()}"><b>{_nt_post_instrumental}</b></a> {_nt_from} {$post->getPublicationTime()}.
|
||||||
|
<div class="nobold">
|
||||||
|
{$notification->getDateTime()}
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
{var $user = $notification->getModel(1)}
|
{var $user = $notification->getModel(1)}
|
||||||
|
|
||||||
<a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a>
|
<a href="{$user->getURL()}"><b>{$user->getCanonicalName()}</b></a>
|
||||||
{$notification->getDateTime()} {_nt_commented_yours} {include under}: "{$notification->getData()}".
|
{_nt_commented_yours} {include under}: "{$notification->getData()}".
|
||||||
|
<div class="nobold">
|
||||||
|
{$notification->getDateTime()}
|
||||||
|
</div>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue