Merge branch 'master' into feature-reports
1
.gitignore
vendored
|
@ -9,5 +9,6 @@ tmp/*
|
|||
!tmp/themepack_artifacts/.gitkeep
|
||||
themepacks/*
|
||||
!themepacks/.gitkeep
|
||||
!themepacks/openvk_modern
|
||||
storage/*
|
||||
!storage/.gitkeep
|
||||
|
|
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
|||
[submodule "Web/static/img/oxygen-icons"]
|
||||
path = Web/static/img/oxygen-icons
|
||||
url = https://github.com/KDE/oxygen-icons5.git
|
27
INSTALL.md
|
@ -1,27 +0,0 @@
|
|||
# OpenVK Installation Instructions
|
||||
|
||||
* * *
|
||||
|
||||
1. Install Composer, Node.js, Yarn and [Chandler](https://github.com/openvk/chandler)
|
||||
2. Install [commitcaptcha](https://github.com/openvk/commitcaptcha)/[chandler-recaptcha](https://github.com/openvk/chandler-recaptcha) and OpenVK as Chandler extensions and enable them like this:
|
||||
|
||||
```bash
|
||||
ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/extensions/enabled/
|
||||
ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/
|
||||
```
|
||||
|
||||
3. Import install/init-static-db.sql to same database you installed Chandler to
|
||||
4. Import install/init-event-db.sql to separate database
|
||||
5. Rename openvk-example.yml to openvk.yml and change options
|
||||
6. Run `composer install` in OpenVK directory
|
||||
7. Move to Web/static/js and execute `yarn install`
|
||||
8. Set `openvk` as your root app in chandler.yml
|
||||
|
||||
Once you are done, you can login as a system administrator on the network itself (no registration required):
|
||||
|
||||
- **Login**: `admin@localhost.localdomain6`
|
||||
- **Password**: `admin`
|
||||
|
||||
It is recommended to change the password before using the built-in account.
|
||||
|
||||
Full example installation instruction for CentOS 8 is also available [here](docs/centos8_install.md).
|
72
README.md
Normal file
|
@ -0,0 +1,72 @@
|
|||
# <img align="right" src="https://github.com/openvk/openvk/raw/master/Web/static/img/logo_shadow.png" alt="openvk" title="openvk" width="15%">OpenVK
|
||||
|
||||
**OpenVK** is an attempt to create a simple CMS that ~~cosplays~~ imitates old VK. Code provided here is not stable yet.
|
||||
|
||||
VKontakte belongs to Pavel Durov and VK Group.
|
||||
|
||||
To be honest, we don't even know whether it even works. However, this version is maintained and we will be happy to accept your bugreports [in our bug-tracker](https://github.com/openvk/openvk/projects/1). You should also be able to submit them using [ticketing system](https://openvk.su/support?act=new) (you will need an OVK account for this).
|
||||
|
||||
## When's the release?
|
||||
|
||||
Please use the master branch, as it has the most changes.
|
||||
|
||||
Updating the source code is done with this command: `git pull`
|
||||
|
||||
## Instances
|
||||
|
||||
* **[openvk.su](https://openvk.su/)**
|
||||
* [social.fetbuk.ru](http://social.fetbuk.ru/)
|
||||
|
||||
## Can I create my own OpenVK instance?
|
||||
|
||||
Yes! And you're very welcome to.
|
||||
However, OVK makes use of Chandler Application Server. This software requires extensions, that may not be provided by your hosting provider (namely, sodium and yaml. this extensions are available on most of ISPManager hostings).
|
||||
If you want, you can add your instance to the list above so that people can register there.
|
||||
|
||||
### Installation procedure
|
||||
|
||||
1. Install PHP 7, web-server, Composer, Node.js, Yarn and [Chandler](https://github.com/openvk/chandler)
|
||||
* PHP 8 has **not** yet been tested, so you should not expect it to work.
|
||||
2. Install [commitcaptcha](https://github.com/openvk/commitcaptcha) and OpenVK as Chandler extensions like this:
|
||||
```
|
||||
git clone https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk
|
||||
git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha
|
||||
```
|
||||
3. And enable them:
|
||||
```
|
||||
ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/extensions/enabled/
|
||||
ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/
|
||||
```
|
||||
4. Import `install/init-static-db.sql` to **same database** you installed Chandler to
|
||||
5. Import `install/init-event-db.sql` to **separate database**
|
||||
6. Copy `openvk-example.yml` to `openvk.yml` and change options
|
||||
7. Run `composer install` in OpenVK directory
|
||||
8. Move to `Web/static/js` and execute `yarn install`
|
||||
9. Set `openvk` as your root app in `chandler.yml`
|
||||
|
||||
Once you are done, you can login as a system administrator on the network itself (no registration required):
|
||||
* **Login**: `admin@localhost.localdomain6`
|
||||
* **Password**: `admin`
|
||||
* It is recommended to change the password before using the built-in account.
|
||||
|
||||
Full example installation instruction for CentOS 8 is also available [here](docs/centos8_install.md).
|
||||
|
||||
### If my website uses OpenVK, should I publish it's sources?
|
||||
|
||||
You are encouraged to do so. We don't enforce this though. You can keep your sources to yourself (unless you distribute your OpenVK distro to other people).
|
||||
You also not required to publish source texts of your themepacks and plugins.
|
||||
|
||||
## Where can I get assistance?
|
||||
|
||||
You may reach out to us via:
|
||||
* [Bug-tracker](https://github.com/openvk/openvk/projects/1)
|
||||
* [Ticketing system](https://openvk.su/support?act=new)
|
||||
* Telegram chat: Go to [our channel](https://t.me/openvkch) and open discussion in our channel menu.
|
||||
* [Reddit](https://www.reddit.com/r/openvk/)
|
||||
* [Discussions](https://github.com/openvk/openvk/discussions)
|
||||
|
||||
**Attention**: bug tracker and telegram chat are public places. And ticketing system is being served by volunteers. If you need to report something, that shouldn't be immediately disclosed to general public (for instance, vulnerability report), please use contact us directly at this email: **openvk [at] tutanota [dot] com**
|
||||
|
||||
<a href="https://codeberg.org/OpenVK/openvk">
|
||||
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">
|
||||
</a>
|
|
@ -1,72 +0,0 @@
|
|||
h1. <img align="right" src="https://github.com/openvk/openvk/raw/master/Web/static/img/logo_shadow.png" alt="openvk" title="openvk" width="15%">OpenVK
|
||||
|
||||
*OpenVK* is an attempt to create a simple CMS that -cosplays- imitates old VK. Code provided here is not stable yet.
|
||||
VKontakte belongs to Pavel Durov and VK Group.
|
||||
To be honest, we don't even know whether it even works. However, this version is maintained and we will be happy to accept your bugreports "in our bug-tracker":https://github.com/openvk/openvk/projects/1. You should also be able to submit them using "ticketing system":https://openvk.su/support?act=new (you will need an OVK account for this).
|
||||
|
||||
h2. When's the release?
|
||||
|
||||
Please use the master branch, as it has the most changes.
|
||||
|
||||
Updating the source code is done with this command: @git pull --recurse-submodules@
|
||||
|
||||
h2. Instances
|
||||
|
||||
* *"openvk.su":https://openvk.su/*
|
||||
* "social.fetbuk.ru":http://social.fetbuk.ru/
|
||||
|
||||
h2. Can I create my own OpenVK instance?
|
||||
|
||||
Yes! And you're very welcome to.
|
||||
However, OVK makes use of Chandler Application Server. This software requires extensions, that may not be provided by your hosting provider (namely, sodium and yaml. this extensions are available on most of ISPManager hostings).
|
||||
Simply put, we would recommend you to use latest CentOS (running on your own VDS/Dedicated) with latest PHP from EPEL. We used to have this configuration on our "main instance":https://openvk.su/ and thus will be able to provide quicker support for OVK running in this environment.
|
||||
If you want, you can add your instance to the list above so that people can register there.
|
||||
|
||||
h3. Installation procedure
|
||||
|
||||
"samukhin":https://github.com/samukhin is working on a Docker container that contains a microblog version of OpenVK, see "#76":https://github.com/openvk/openvk/pull/76 for details. (Experimental)
|
||||
|
||||
# Install PHP 7, web-server, Composer, Node.js, Yarn and "Chandler":https://github.com/openvk/chandler
|
||||
PHP 8 has *not* yet been tested, so you should not expect it to work.
|
||||
# Install "commitcaptcha":https://github.com/openvk/commitcaptcha and OpenVK as Chandler extensions like this:
|
||||
@git clone --recursive https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk@
|
||||
@git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha@
|
||||
# And enable them:
|
||||
@ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/extensions/enabled/@
|
||||
@ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/@
|
||||
# Import @install/init-static-db.sql@ to *same database* you installed Chandler to
|
||||
# Import @install/init-event-db.sql@ to *separate database*
|
||||
# Copy @openvk-example.yml@ to @openvk.yml@ and change options
|
||||
# Run @composer install@ in OpenVK directory
|
||||
# Move to @Web/static/js@ and execute @yarn install@
|
||||
# Set @openvk@ as your root app in @chandler.yml@
|
||||
|
||||
*Note*: If OVK submodules were not downloaded beforehand (i.e. @--recursive@ was not used during cloning), this command *must be* executed in the @openvk@ folder: @git submodule update --init@
|
||||
|
||||
Once you are done, you can login as a system administrator on the network itself (no registration required):
|
||||
* *Login*: admin@localhost.localdomain6
|
||||
* *Password*: admin
|
||||
It is recommended to change the password before using the built-in account.
|
||||
|
||||
h3. If my website uses OpenVK, should I publish it's sources?
|
||||
|
||||
You are encouraged to do so. We don't enforce this though. You can keep your sources to yourself (unless you distribute your OpenVK distro to other people).
|
||||
You also not required to publish source texts of your themepacks and plugins.
|
||||
|
||||
h2. Where can I get assistance?
|
||||
|
||||
You may reach out to us via:
|
||||
* "Bug-tracker":https://github.com/openvk/openvk/projects/1
|
||||
* "Ticketing system":https://openvk.su/support?act=new
|
||||
* Telegram chat: Go to "our channel":https://t.me/openvkch and open discussion in our channel menu.
|
||||
* "Reddit":https://www.reddit.com/r/openvk/
|
||||
* "Discussions":https://github.com/openvk/openvk/discussions
|
||||
|
||||
*Attention*: bug tracker and telegram chat are public places. And ticketing system is being served by volunteers. If you need to report something, that shouldn't be immediately disclosed to general public (for instance, vulnerability report), *please use contact us directly*:
|
||||
* *Head of OpenVK Security Commitee*: stingray@jill.pl or "@id155":https://t.me/id155
|
||||
* *Backend developer*: "@saddyteirusu":https://t.me/saddyteirusu
|
||||
|
||||
Codeberg repository clone:
|
||||
<a href="https://codeberg.org/OpenVK/openvk">
|
||||
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">
|
||||
</a>
|
|
@ -130,6 +130,16 @@ class Club extends RowModel
|
|||
return $this->getRecord()->administrators_list_display;
|
||||
}
|
||||
|
||||
function isEveryoneCanCreateTopics(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->everyone_can_create_topics;
|
||||
}
|
||||
|
||||
function isDisplayTopicsAboveWallEnabled(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->display_topics_above_wall;
|
||||
}
|
||||
|
||||
function getType(): int
|
||||
{
|
||||
return $this->getRecord()->type;
|
||||
|
|
|
@ -37,8 +37,19 @@ class Comment extends Post
|
|||
if($honourFlags && $this->isPostedOnBehalfOfGroup()) {
|
||||
if($this->getTarget() instanceof Post)
|
||||
return (new Clubs)->get(abs($this->getTarget()->getTargetWall()));
|
||||
|
||||
if($this->getTarget() instanceof Topic)
|
||||
return $this->getTarget()->getClub();
|
||||
}
|
||||
|
||||
return parent::getOwner($honourFlags, $real);
|
||||
}
|
||||
|
||||
function canBeDeletedBy(User $user): bool
|
||||
{
|
||||
return $this->getOwner()->getId() == $user->getId() ||
|
||||
$this->getTarget()->getOwner()->getId() == $user->getId() ||
|
||||
$this->getTarget() instanceof Post && $this->getTarget()->getTargetWall() < 0 && (new Clubs)->get(abs($this->getTarget()->getTargetWall()))->canBeModifiedBy($user) ||
|
||||
$this->getTarget() instanceof Topic && $this->getTarget()->canBeModifiedBy($user);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities\Notifications;
|
||||
use openvk\Web\Models\Entities\{User, Gift};
|
||||
|
||||
final class CoinsTransferNotification extends Notification
|
||||
{
|
||||
protected $actionCode = 9602;
|
||||
|
||||
function __construct(User $receiver, User $sender, int $value, string $message)
|
||||
{
|
||||
parent::__construct($receiver, $receiver, $sender, time(), $value . " " . $message);
|
||||
}
|
||||
}
|
|
@ -1,13 +1,8 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use openvk\Web\Util\DateTime;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use Chandler\Database\DatabaseConnection as DB;
|
||||
use Nette\InvalidStateException as ISE;
|
||||
use Nette\Database\Table\Selection;
|
||||
|
||||
class Ticket extends RowModel
|
||||
{
|
||||
|
@ -22,14 +17,7 @@ class Ticket extends RowModel
|
|||
|
||||
function getStatus(): string
|
||||
{
|
||||
if ($this->getRecord()->type === 0)
|
||||
{
|
||||
return tr("support_status_0");
|
||||
} elseif ($this->getRecord()->type === 1) {
|
||||
return tr("support_status_1");
|
||||
} elseif ($this->getRecord()->type === 2) {
|
||||
return tr("support_status_2");
|
||||
}
|
||||
return tr("support_status_" . $this->getRecord()->type);
|
||||
}
|
||||
|
||||
function getType(): int
|
||||
|
@ -58,17 +46,7 @@ class Ticket extends RowModel
|
|||
|
||||
function isDeleted(): bool
|
||||
{
|
||||
if ($this->getRecord()->deleted === 0)
|
||||
{
|
||||
return false;
|
||||
} elseif ($this->getRecord()->deleted === 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function authorId(): int
|
||||
{
|
||||
return $this->getRecord()->user_id;
|
||||
return (bool) $this->getRecord()->deleted;
|
||||
}
|
||||
|
||||
function getUser(): user
|
||||
|
@ -76,6 +54,11 @@ class Ticket extends RowModel
|
|||
return (new Users)->get($this->getRecord()->user_id);
|
||||
}
|
||||
|
||||
function getUserId(): int
|
||||
{
|
||||
return $this->getRecord()->user_id;
|
||||
}
|
||||
|
||||
function isAd(): bool /* Эх, костыли... */
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use openvk\Web\Util\DateTime;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Models\Repositories\{Users, SupportAliases};
|
||||
use Chandler\Database\DatabaseConnection as DB;
|
||||
use Nette\InvalidStateException as ISE;
|
||||
use Nette\Database\Table\Selection;
|
||||
use openvk\Web\Models\Repositories\{Users, SupportAliases, Tickets};
|
||||
|
||||
class TicketComment extends RowModel
|
||||
{
|
||||
|
@ -34,6 +29,11 @@ class TicketComment extends RowModel
|
|||
{
|
||||
return (new Users)->get($this->getRecord()->user_id);
|
||||
}
|
||||
|
||||
function getTicket(): Ticket
|
||||
{
|
||||
return (new Tickets)->get($this->getRecord()->ticket_id);
|
||||
}
|
||||
|
||||
function getAuthorName(): string
|
||||
{
|
||||
|
@ -113,5 +113,19 @@ class TicketComment extends RowModel
|
|||
return false; # Кооостыыыль!!!
|
||||
}
|
||||
|
||||
function getMark(): ?int
|
||||
{
|
||||
return $this->getRecord()->mark;
|
||||
}
|
||||
|
||||
function isLikedByUser(): ?bool
|
||||
{
|
||||
$mark = $this->getMark();
|
||||
if(is_null($mark))
|
||||
return NULL;
|
||||
else
|
||||
return $mark === 1;
|
||||
}
|
||||
|
||||
use Traits\TRichText;
|
||||
}
|
||||
|
|
86
Web/Models/Entities/Topic.php
Normal file
|
@ -0,0 +1,86 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use openvk\Web\Models\Repositories\Clubs;
|
||||
use openvk\Web\Util\DateTime;
|
||||
|
||||
class Topic extends Postable
|
||||
{
|
||||
protected $tableName = "topics";
|
||||
protected $upperNodeReferenceColumnName = "group";
|
||||
|
||||
/**
|
||||
* May return fake owner (group), if flags are [1, (*)]
|
||||
*
|
||||
* @param bool $honourFlags - check flags
|
||||
*/
|
||||
function getOwner(bool $honourFlags = true, bool $real = false): RowModel
|
||||
{
|
||||
if($honourFlags && $this->isPostedOnBehalfOfGroup())
|
||||
return $this->getClub();
|
||||
|
||||
return parent::getOwner($real);
|
||||
}
|
||||
|
||||
function getClub(): Club
|
||||
{
|
||||
return (new Clubs)->get($this->getRecord()->group);
|
||||
}
|
||||
|
||||
function getTitle(): string
|
||||
{
|
||||
return $this->getRecord()->title;
|
||||
}
|
||||
|
||||
function isClosed(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->closed;
|
||||
}
|
||||
|
||||
function isPinned(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->pinned;
|
||||
}
|
||||
|
||||
function getPrettyId(): string
|
||||
{
|
||||
return $this->getRecord()->group . "_" . $this->getVirtualId();
|
||||
}
|
||||
|
||||
function isPostedOnBehalfOfGroup(): bool
|
||||
{
|
||||
return ($this->getRecord()->flags & 0b10000000) > 0;
|
||||
}
|
||||
|
||||
function isDeleted(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->deleted;
|
||||
}
|
||||
|
||||
function canBeModifiedBy(User $user): bool
|
||||
{
|
||||
return $this->getOwner(false)->getId() === $user->getId() || $this->getClub()->canBeModifiedBy($user);
|
||||
}
|
||||
|
||||
function getLastComment(): ?Comment
|
||||
{
|
||||
$array = iterator_to_array($this->getLastComments(1));
|
||||
return isset($array[0]) ? $array[0] : NULL;
|
||||
}
|
||||
|
||||
function getUpdateTime(): DateTime
|
||||
{
|
||||
$lastComment = $this->getLastComment();
|
||||
if(!is_null($lastComment))
|
||||
return $lastComment->getPublicationTime();
|
||||
else
|
||||
return $this->getEditTime() ?? $this->getPublicationTime();
|
||||
}
|
||||
|
||||
function deleteTopic(): void
|
||||
{
|
||||
$this->setDeleted(1);
|
||||
$this->unwire();
|
||||
$this->save();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities\Traits;
|
||||
use Wkhooy\ObsceneCensorRus;
|
||||
|
||||
trait TRichText
|
||||
{
|
||||
|
@ -58,9 +59,9 @@ trait TRichText
|
|||
if($proc) {
|
||||
$rel = $this->isAd() ? "sponsored" : "ugc";
|
||||
$text = $this->formatLinks($text);
|
||||
$text = preg_replace("%@(id|club)([0-9]++) \(([\p{L} 0-9]+)\)%Xu", "[$1$2|$3]", $text);
|
||||
$text = preg_replace("%@(id|club)([0-9]++)%Xu", "[$1$2|@$1$2]", $text);
|
||||
$text = preg_replace("%\[(id|club)([0-9]++)\|([\p{L} 0-9@]+)\]%Xu", "<a href='/$1$2'>$3</a>", $text);
|
||||
$text = preg_replace("%@([A-Za-z0-9]++) \(([\p{L} 0-9]+)\)%Xu", "[$1|$2]", $text);
|
||||
$text = preg_replace("%([\n\r\s]|^)(@([A-Za-z0-9]++))%Xu", "$1[$3|@$3]", $text);
|
||||
$text = preg_replace("%\[([A-Za-z0-9]++)\|([\p{L} 0-9@]+)\]%Xu", "<a href='/$1'>$2</a>", $text);
|
||||
$text = preg_replace("%(#([\p{L}_-]++[0-9]*[\p{L}_-]*))%Xu", "<a href='/feed/hashtag/$2'>$1</a>", $text);
|
||||
$text = $this->formatEmojis($text);
|
||||
}
|
||||
|
@ -69,6 +70,9 @@ trait TRichText
|
|||
$text = nl2br($text);
|
||||
}
|
||||
|
||||
if(OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["christian"])
|
||||
ObsceneCensorRus::filterText($text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -317,14 +317,14 @@ class User extends RowModel
|
|||
return $this->getRecord()->notification_offset;
|
||||
}
|
||||
|
||||
function getBirthday(): ?int
|
||||
function getBirthday(): ?DateTime
|
||||
{
|
||||
return $this->getRecord()->birthday;
|
||||
return new DateTime($this->getRecord()->birthday);
|
||||
}
|
||||
|
||||
function getAge(): ?int
|
||||
{
|
||||
return (int)floor((time() - $this->getBirthday()) / mktime(0, 0, 0, 1, 1, 1971));
|
||||
return (int)floor((time() - $this->getBirthday()->timestamp()) / YEAR);
|
||||
}
|
||||
|
||||
function get2faSecret(): ?string
|
||||
|
@ -353,6 +353,7 @@ class User extends RowModel
|
|||
"notes",
|
||||
"groups",
|
||||
"news",
|
||||
"links",
|
||||
],
|
||||
])->get($id);
|
||||
}
|
||||
|
@ -539,6 +540,8 @@ class User extends RowModel
|
|||
$manager = $club->getManager($this);
|
||||
if(!is_null($manager))
|
||||
return $manager->isClubPinned();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getMeetings(int $page = 1): \Traversable
|
||||
|
@ -766,6 +769,7 @@ class User extends RowModel
|
|||
"notes",
|
||||
"groups",
|
||||
"news",
|
||||
"links",
|
||||
],
|
||||
])->set($id, (int) $status)->toInteger();
|
||||
|
||||
|
|
|
@ -48,6 +48,15 @@ class TicketComments
|
|||
// {
|
||||
// return $this->toTicket($this->tickets->get($id));
|
||||
// }
|
||||
|
||||
function get(int $id): ?TicketComment
|
||||
{
|
||||
$comment = $this->comments->get($id);;
|
||||
if (!is_null($comment))
|
||||
return new TicketComment($comment);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
use \Nette\SmartObject;
|
||||
}
|
||||
|
|
73
Web/Models/Repositories/Topics.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use openvk\Web\Models\Entities\Topic;
|
||||
use openvk\Web\Models\Entities\Club;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
class Topics
|
||||
{
|
||||
private $context;
|
||||
private $topics;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DatabaseConnection::i()->getContext();
|
||||
$this->topics = $this->context->table("topics");
|
||||
}
|
||||
|
||||
private function toTopic(?ActiveRow $ar): ?Topic
|
||||
{
|
||||
return is_null($ar) ? NULL : new Topic($ar);
|
||||
}
|
||||
|
||||
function get(int $id): ?Topic
|
||||
{
|
||||
return $this->toTopic($this->topics->get($id));
|
||||
}
|
||||
|
||||
function getTopicById(int $club, int $topic): ?Topic
|
||||
{
|
||||
return $this->toTopic($this->topics->where(["group" => $club, "virtual_id" => $topic, "deleted" => 0])->fetch());
|
||||
}
|
||||
|
||||
function getClubTopics(Club $club, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||
{
|
||||
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||
|
||||
// Get pinned topics first
|
||||
$query = "SELECT `id` FROM `topics` WHERE `pinned` = 1 AND `group` = ? AND `deleted` = 0 UNION SELECT `id` FROM `topics` WHERE `pinned` = 0 AND `group` = ? AND `deleted` = 0";
|
||||
$query .= " LIMIT " . $perPage . " OFFSET " . ($page - 1) * $perPage;
|
||||
|
||||
foreach(DatabaseConnection::i()->getConnection()->query($query, $club->getId(), $club->getId()) as $topic) {
|
||||
$topic = $this->get($topic->id);
|
||||
if(!$topic) continue;
|
||||
|
||||
yield $topic;
|
||||
}
|
||||
}
|
||||
|
||||
function getClubTopicsCount(Club $club): int
|
||||
{
|
||||
return sizeof($this->topics->where([
|
||||
"group" => $club->getId(),
|
||||
"deleted" => false
|
||||
]));
|
||||
}
|
||||
|
||||
function find(Club $club, string $query): \Traversable
|
||||
{
|
||||
return new Util\EntityStream("Topic", $this->topics->where("title LIKE ? AND group = ? AND deleted = 0", "%$query%", $club->getId()));
|
||||
}
|
||||
|
||||
function getLastTopics(Club $club, ?int $count = NULL): \Traversable
|
||||
{
|
||||
$topics = $this->topics->where([
|
||||
"group" => $club->getId(),
|
||||
"deleted" => false
|
||||
])->page(1, $count ?? OPENVK_DEFAULT_PER_PAGE)->order("created DESC");
|
||||
|
||||
foreach($topics as $topic)
|
||||
yield $this->toTopic($topic);
|
||||
}
|
||||
}
|
|
@ -52,6 +52,23 @@ class Users
|
|||
"online" => sizeof((clone $this->users)->where("online >= ?", time() - 900)),
|
||||
];
|
||||
}
|
||||
|
||||
function getByAddress(string $address): ?User
|
||||
{
|
||||
if(substr_compare($address, "/", -1) === 0)
|
||||
$address = substr($address, 0, iconv_strlen($address) - 1);
|
||||
|
||||
$serverUrl = ovk_scheme(true) . $_SERVER["SERVER_NAME"];
|
||||
if(strpos($address, $serverUrl . "/") === 0)
|
||||
$address = substr($address, iconv_strlen($serverUrl) + 1);
|
||||
|
||||
if(strpos($address, "id") === 0) {
|
||||
$user = $this->get((int) substr($address, 2));
|
||||
if($user) return $user;
|
||||
}
|
||||
|
||||
return $this->getByShortUrl($address);
|
||||
}
|
||||
|
||||
use \Nette\SmartObject;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use openvk\Web\Models\Entities\PasswordReset;
|
|||
use openvk\Web\Models\Repositories\IPs;
|
||||
use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\Repositories\Restores;
|
||||
use openvk\Web\Util\Validator;
|
||||
use Chandler\Session\Session;
|
||||
use Chandler\Security\User as ChandlerUser;
|
||||
use Chandler\Security\Authenticator;
|
||||
|
@ -32,17 +33,6 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
parent::__construct();
|
||||
}
|
||||
|
||||
private function emailValid(string $email): bool
|
||||
{
|
||||
if(empty($email)) return false;
|
||||
|
||||
$email = trim($email);
|
||||
[$user, $domain] = explode("@", $email);
|
||||
$domain = idn_to_ascii($domain) . ".";
|
||||
|
||||
return checkdnsrr($domain, "MX");
|
||||
}
|
||||
|
||||
private function ipValid(): bool
|
||||
{
|
||||
$ip = (new IPs)->get(CONNECTING_IP);
|
||||
|
@ -62,7 +52,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
if(!is_null($refLink = $this->queryParam("ref"))) {
|
||||
$pieces = explode(" ", $refLink, 2);
|
||||
if(sizeof($pieces) !== 2)
|
||||
$this->flashFail("err", "Пригласительная ссылка кривая", "Пригласительная ссылка недействительна.");
|
||||
$this->flashFail("err", tr("error"), tr("referral_link_invalid"));
|
||||
|
||||
[$ref, $hash] = $pieces;
|
||||
$ref = hexdec($ref);
|
||||
|
@ -70,10 +60,10 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
|
||||
$referer = (new Users)->get($ref);
|
||||
if(!$referer)
|
||||
$this->flashFail("err", "Пригласительная ссылка кривая", "Пригласительная ссылка недействительна.");
|
||||
$this->flashFail("err", tr("error"), tr("referral_link_invalid"));
|
||||
|
||||
if($referer->getRefLinkId() !== $refLink)
|
||||
$this->flashFail("err", "Пригласительная ссылка кривая", "Пригласительная ссылка недействительна.");
|
||||
$this->flashFail("err", tr("error"), tr("referral_link_invalid"));
|
||||
}
|
||||
|
||||
$this->template->referer = $referer;
|
||||
|
@ -82,20 +72,20 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$this->assertCaptchaCheckPassed();
|
||||
|
||||
if(!OPENVK_ROOT_CONF['openvk']['preferences']['registration']['enable'] && !$referer)
|
||||
$this->flashFail("err", "Подозрительная попытка регистрации", "Регистрация отключена системным администратором.");
|
||||
$this->flashFail("err", tr("failed_to_register"), tr("registration_disabled"));
|
||||
|
||||
if(!$this->ipValid())
|
||||
$this->flashFail("err", "Подозрительная попытка регистрации", "Вы пытались зарегистрироваться из подозрительного места.");
|
||||
$this->flashFail("err", tr("suspicious_registration_attempt"), tr("suspicious_registration_attempt_comment"));
|
||||
|
||||
if(!$this->emailValid($this->postParam("email")))
|
||||
$this->flashFail("err", "Неверный email адрес", "Email, который вы ввели, не является корректным.");
|
||||
if(!Validator::i()->emailValid($this->postParam("email")))
|
||||
$this->flashFail("err", tr("invalid_email_address"), tr("invalid_email_address_comment"));
|
||||
|
||||
if (strtotime($this->postParam("birthday")) > time())
|
||||
$this->flashFail("err", "Неверная дата рождения", "Дату рождения, которую вы ввели, не является корректным.");
|
||||
$this->flashFail("err", tr("invalid_birth_date"), tr("invalid_birth_date_comment"));
|
||||
|
||||
$chUser = ChandlerUser::create($this->postParam("email"), $this->postParam("password"));
|
||||
if(!$chUser)
|
||||
$this->flashFail("err", "Не удалось зарегистрироваться", "Пользователь с таким email уже существует.");
|
||||
$this->flashFail("err", tr("failed_to_register"), tr("user_already_exists"));
|
||||
|
||||
$user = new User;
|
||||
$user->setUser($chUser->getId());
|
||||
|
@ -131,10 +121,10 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
|
||||
$user = $this->db->table("ChandlerUsers")->where("login", $this->postParam("login"))->fetch();
|
||||
if(!$user)
|
||||
$this->flashFail("err", "Не удалось войти", "Неверное имя пользователя или пароль. <a href='/restore.pl'>Забыли пароль?</a>");
|
||||
$this->flashFail("err", tr("login_failed"), tr("invalid_username_or_password"));
|
||||
|
||||
if(!$this->authenticator->verifyCredentials($user->id, $this->postParam("password")))
|
||||
$this->flashFail("err", "Не удалось войти", "Неверное имя пользователя или пароль. <a href='/restore.pl'>Забыли пароль?</a>");
|
||||
$this->flashFail("err", tr("login_failed"), tr("invalid_username_or_password"));
|
||||
|
||||
$secret = $user->related("profiles.user")->fetch()["2fa_secret"];
|
||||
$code = $this->postParam("code");
|
||||
|
@ -148,7 +138,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
|
||||
$ovkUser = new User($user->related("profiles.user")->fetch());
|
||||
if(!($code === (new Totp)->GenerateToken(Base32::decode($secret)) || $ovkUser->use2faBackupCode((int) $code))) {
|
||||
$this->flash("err", "Не удалось войти", tr("incorrect_2fa_code"));
|
||||
$this->flash("err", tr("login_failed"), tr("incorrect_2fa_code"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -170,11 +160,11 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
if(!$this->db->table("ChandlerUsers")->where("id", $uuid))
|
||||
$this->flashFail("err", "Ошибка манипуляции токенами", "Пользователь не найден.");
|
||||
$this->flashFail("err", tr("token_manipulation_error"), tr("profile_not_found"));
|
||||
|
||||
$this->assertPermission('openvk\Web\Models\Entities\User', 'substitute', 0);
|
||||
Session::i()->set("_su", $uuid);
|
||||
$this->flash("succ", "Профиль изменён", "Ваш активный профиль был изменён.");
|
||||
$this->flash("succ", tr("profile_changed"), tr("profile_changed_comment"));
|
||||
$this->redirect("/", static::REDIRECT_TEMPORARY);
|
||||
exit;
|
||||
}
|
||||
|
@ -193,8 +183,9 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
{
|
||||
$request = $this->restores->getByToken(str_replace(" ", "+", $this->queryParam("key")));
|
||||
if(!$request || !$request->isStillValid()) {
|
||||
$this->flash("err", "Ошибка манипулирования токеном", "Токен недействителен или истёк");
|
||||
$this->flash("err", tr("token_manipulation_error"), tr("token_manipulation_error_comment"));
|
||||
$this->redirect("/");
|
||||
return;
|
||||
}
|
||||
|
||||
$this->template->is2faEnabled = $request->getUser()->is2faEnabled();
|
||||
|
@ -217,7 +208,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$this->authenticator->authenticate($user->getId());
|
||||
|
||||
$request->delete(false);
|
||||
$this->flash("succ", "Успешно", "Ваш пароль был успешно сброшен.");
|
||||
$this->flash("succ", tr("information_-1"), tr("password_successfully_reset"));
|
||||
$this->redirect("/settings");
|
||||
}
|
||||
}
|
||||
|
@ -231,16 +222,16 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$uRow = $this->db->table("ChandlerUsers")->where("login", $this->postParam("login"))->fetch();
|
||||
if(!$uRow) {
|
||||
#Privacy of users must be protected. We will not tell if email is bound to a user or not.
|
||||
$this->flashFail("succ", "Успешно", "Если вы зарегистрированы, вы получите инструкции на email.");
|
||||
$this->flashFail("succ", tr("information_-1"), tr("password_reset_email_sent"));
|
||||
}
|
||||
|
||||
$user = $this->users->getByChandlerUser(new ChandlerUser($uRow));
|
||||
if(!$user)
|
||||
$this->flashFail("err", "Ошибка", "Непредвиденная ошибка при сбросе пароля.");
|
||||
$this->flashFail("err", tr("error"), tr("password_reset_error"));
|
||||
|
||||
$request = $this->restores->getLatestByUser($user);
|
||||
if(!is_null($request) && $request->isNew())
|
||||
$this->flashFail("err", "Ошибка доступа", "Нельзя делать это так часто, извините.");
|
||||
$this->flashFail("err", tr("forbidden"), tr("password_reset_rate_limit_error"));
|
||||
|
||||
$resetObj = new PasswordReset;
|
||||
$resetObj->setProfile($user->getId());
|
||||
|
@ -253,7 +244,7 @@ final class AuthPresenter extends OpenVKPresenter
|
|||
$this->sendmail($uRow->login, "password-reset", $params); #Vulnerability possible
|
||||
|
||||
|
||||
$this->flashFail("succ", "Успешно", "Если вы зарегистрированы, вы получите инструкции на email.");
|
||||
$this->flashFail("succ", tr("information_-1"), tr("password_reset_email_sent"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{Comment, Photo, Video, User};
|
||||
use openvk\Web\Models\Entities\{Comment, Photo, Video, User, Topic, Post};
|
||||
use openvk\Web\Models\Entities\Notifications\CommentNotification;
|
||||
use openvk\Web\Models\Repositories\Comments;
|
||||
use openvk\Web\Models\Repositories\{Comments, Clubs};
|
||||
|
||||
final class CommentPresenter extends OpenVKPresenter
|
||||
{
|
||||
private $models = [
|
||||
"posts" => "openvk\\Web\\Models\\Repositories\\Posts",
|
||||
"photos" => "openvk\\Web\\Models\\Repositories\\Photos",
|
||||
"videos" => "openvk\\Web\\Models\\Repositories\\Videos",
|
||||
"notes" => "openvk\\Web\\Models\\Repositories\\Notes",
|
||||
"posts" => "openvk\\Web\\Models\\Repositories\\Posts",
|
||||
"photos" => "openvk\\Web\\Models\\Repositories\\Photos",
|
||||
"videos" => "openvk\\Web\\Models\\Repositories\\Videos",
|
||||
"notes" => "openvk\\Web\\Models\\Repositories\\Notes",
|
||||
"topics" => "openvk\\Web\\Models\\Repositories\\Topics",
|
||||
];
|
||||
|
||||
function renderLike(int $id): void
|
||||
|
@ -37,9 +38,17 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
$repo = new $repoClass;
|
||||
$entity = $repo->get($eId);
|
||||
if(!$entity) $this->notFound();
|
||||
|
||||
if($entity instanceof Topic && $entity->isClosed())
|
||||
$this->notFound();
|
||||
|
||||
if($entity instanceof Post && $entity->getTargetWall() < 0)
|
||||
$club = (new Clubs)->get(abs($entity->getTargetWall()));
|
||||
else if($entity instanceof Topic)
|
||||
$club = $entity->getClub();
|
||||
|
||||
$flags = 0;
|
||||
if($this->postParam("as_group") === "on")
|
||||
if($this->postParam("as_group") === "on" && !is_null($club) && $club->canBeModifiedBy($this->user->identity))
|
||||
$flags |= 0b10000000;
|
||||
|
||||
$photo = NULL;
|
||||
|
@ -106,9 +115,8 @@ final class CommentPresenter extends OpenVKPresenter
|
|||
|
||||
$comment = (new Comments)->get($id);
|
||||
if(!$comment) $this->notFound();
|
||||
if($comment->getOwner()->getId() !== $this->user->id)
|
||||
if($comment->getTarget()->getOwner()->getId() !== $this->user->id)
|
||||
$this->throwError(403, "Forbidden", "У вас недостаточно прав чтобы редактировать этот ресурс.");
|
||||
if(!$comment->canBeDeletedBy($this->user->identity))
|
||||
$this->throwError(403, "Forbidden", "У вас недостаточно прав чтобы редактировать этот ресурс.");
|
||||
|
||||
$comment->delete();
|
||||
$this->flashFail(
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{Club, Photo};
|
||||
use openvk\Web\Models\Entities\Notifications\ClubModeratorNotification;
|
||||
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers};
|
||||
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers, Topics};
|
||||
use Chandler\Security\Authenticator;
|
||||
|
||||
final class GroupPresenter extends OpenVKPresenter
|
||||
{
|
||||
|
@ -28,6 +29,8 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->template->club = $club;
|
||||
$this->template->albums = (new Albums)->getClubAlbums($club, 1, 3);
|
||||
$this->template->albumsCount = (new Albums)->getClubAlbumsCount($club);
|
||||
$this->template->topics = (new Topics)->getLastTopics($club, 3);
|
||||
$this->template->topicsCount = (new Topics)->getClubTopicsCount($club);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,6 +207,8 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$club->setShortcode(empty($this->postParam("shortcode")) ? NULL : $this->postParam("shortcode"));
|
||||
$club->setWall(empty($this->postParam("wall")) ? 0 : 1);
|
||||
$club->setAdministrators_List_Display(empty($this->postParam("administrators_list_display")) ? 0 : $this->postParam("administrators_list_display"));
|
||||
$club->setEveryone_Can_Create_Topics(empty($this->postParam("everyone_can_create_topics")) ? 0 : 1);
|
||||
$club->setDisplay_Topics_Above_Wall(empty($this->postParam("display_topics_above_wall")) ? 0 : 1);;
|
||||
|
||||
$website = $this->postParam("website") ?? "";
|
||||
if(empty($website))
|
||||
|
@ -269,4 +274,38 @@ final class GroupPresenter extends OpenVKPresenter
|
|||
$this->notFound();
|
||||
}
|
||||
}
|
||||
|
||||
function renderChangeOwner(int $id, int $newOwnerId): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] !== "POST")
|
||||
$this->redirect("/groups" . $this->user->id);
|
||||
|
||||
if(!Authenticator::verifyHash($this->postParam("password"), $this->user->identity->getChandlerUser()->getRaw()->passwordHash))
|
||||
$this->flashFail("err", tr("error"), tr("incorrect_password"));
|
||||
|
||||
$club = $this->clubs->get($id);
|
||||
$newOwner = (new Users)->get($newOwnerId);
|
||||
if($this->user->id !== $club->getOwner()->getId())
|
||||
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||
|
||||
$club->setOwner($newOwnerId);
|
||||
|
||||
$club->addManager($this->user->identity);
|
||||
$oldOwnerManager = $club->getManager($this->user->identity);
|
||||
$oldOwnerManager->setHidden($club->isOwnerHidden());
|
||||
$oldOwnerManager->setComment($club->getOwnerComment());
|
||||
$oldOwnerManager->save();
|
||||
|
||||
$newOwnerManager = $club->getManager($newOwner);
|
||||
$club->setOwner_Hidden($newOwnerManager->isHidden());
|
||||
$club->setOwner_Comment($newOwnerManager->getComment());
|
||||
$club->removeManager($newOwner);
|
||||
|
||||
$club->save();
|
||||
|
||||
$this->flashFail("succ", tr("information_-1"), tr("group_owner_setted", $newOwner->getCanonicalName(), $club->getName()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ final class NotesPresenter extends OpenVKPresenter
|
|||
{
|
||||
$user = (new Users)->get($owner);
|
||||
if(!$user) $this->notFound();
|
||||
if(!$user->getPrivacyPermission('notes.read', $this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
|
||||
$this->template->notes = $this->notes->getUserNotes($user, (int)($this->queryParam("p") ?? 1));
|
||||
$this->template->count = $this->notes->getUserNotesCount($user);
|
||||
|
@ -36,6 +38,8 @@ final class NotesPresenter extends OpenVKPresenter
|
|||
$note = $this->notes->getNoteById($owner, $note_id);
|
||||
if(!$note || $note->getOwner()->getId() !== $owner || $note->isDeleted())
|
||||
$this->notFound();
|
||||
if(!$note->getOwner()->getPrivacyPermission('notes.read', $this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
|
||||
$this->template->cCount = $note->getCommentsCount();
|
||||
$this->template->cPage = (int) ($this->queryParam("p") ?? 1);
|
||||
|
|
|
@ -34,10 +34,10 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
]));
|
||||
}
|
||||
|
||||
protected function setTempTheme(string $theme): void
|
||||
{
|
||||
Session::i()->set("_tempTheme", $theme);
|
||||
}
|
||||
protected function setTempTheme(string $theme): void
|
||||
{
|
||||
Session::i()->set("_tempTheme", $theme);
|
||||
}
|
||||
|
||||
protected function flashFail(string $type, string $title, ?string $message = NULL, ?int $code = NULL): void
|
||||
{
|
||||
|
@ -80,7 +80,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
$loginUrl .= "?jReturnTo=" . rawurlencode($currentUrl);
|
||||
}
|
||||
|
||||
$this->flash("err", "Недостаточно прав", "Чтобы просматривать эту страницу, нужно зайти на сайт.");
|
||||
$this->flash("err", tr("login_required_error"), tr("login_required_error_comment"));
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: $loginUrl");
|
||||
exit;
|
||||
|
@ -91,7 +91,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
{
|
||||
if(is_null($this->user)) {
|
||||
if($model !== "user") {
|
||||
$this->flash("info", "Недостаточно прав", "Чтобы просматривать эту страницу, нужно зайти на сайт.");
|
||||
$this->flash("info", tr("login_required_error"), tr("login_required_error_comment"));
|
||||
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: /login");
|
||||
|
@ -111,13 +111,13 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
if($throw)
|
||||
throw new SecurityPolicyViolationException("Permission error");
|
||||
else
|
||||
$this->flashFail("err", "Недостаточно прав", "У вас недостаточно прав чтобы выполнять это действие.");
|
||||
$this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
|
||||
}
|
||||
|
||||
protected function assertCaptchaCheckPassed(): void
|
||||
{
|
||||
if(!check_captcha())
|
||||
$this->flashFail("err", "Неправильно введены символы", "Пожалуйста, убедитесь, что вы правильно заполнили поле с капчей.");
|
||||
$this->flashFail("err", tr("captcha_error"), tr("captcha_error_comment"));
|
||||
}
|
||||
|
||||
protected function willExecuteWriteAction(): void
|
||||
|
@ -131,7 +131,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
exit("Хакеры? Интересно...");
|
||||
}
|
||||
|
||||
$this->flashFail("err", "Чумба, ты совсем ёбнутый?", "Сходи к мозгоправу, попей колёсики. В OpenVK нельзя вбрасывать щитпосты так часто. Код исключения: $res.");
|
||||
$this->flashFail("err", tr("rate_limit_error"), tr("rate_limit_error_comment", OPENVK_ROOT_CONF["openvk"]["appearance"]["name"], $res));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,6 +198,7 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
header("HTTP/1.1 403 Forbidden");
|
||||
$this->getTemplatingEngine()->render(__DIR__ . "/templates/@banned.xml", [
|
||||
"thisUser" => $this->user->identity,
|
||||
"csrfToken" => $GLOBALS["csrfToken"],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
@ -221,25 +222,23 @@ abstract class OpenVKPresenter extends SimplePresenter
|
|||
{
|
||||
parent::onBeforeRender();
|
||||
|
||||
if(!is_null($this->user)) {
|
||||
$theme = $this->user->identity->getTheme();
|
||||
if(!is_null($theme) && $theme->overridesTemplates()) {
|
||||
$this->template->_templatePath = $theme->getBaseDir() . "/tpl";
|
||||
}
|
||||
$theme = NULL;
|
||||
if(Session::i()->get("_tempTheme")) {
|
||||
$theme = Themepacks::i()[Session::i()->get("_tempTheme", "ovk")];
|
||||
Session::i()->set("_tempTheme", NULL);
|
||||
} else if($this->requestParam("themePreview")) {
|
||||
$theme = Themepacks::i()[$this->requestParam("themePreview")];
|
||||
} else if($this->user->identity !== null && $this->user->identity->getTheme()) {
|
||||
$theme = $this->user->identity->getTheme();
|
||||
}
|
||||
|
||||
$this->template->theme = $theme;
|
||||
if(!is_null($theme) && $theme->overridesTemplates())
|
||||
$this->template->_templatePath = $theme->getBaseDir() . "/tpl";
|
||||
|
||||
if(!is_null(Session::i()->get("_error"))) {
|
||||
$this->template->flashMessage = json_decode(Session::i()->get("_error"));
|
||||
Session::i()->set("_error", NULL);
|
||||
}
|
||||
|
||||
if(Session::i()->get("_tempTheme"))
|
||||
$this->template->theme = Themepacks::i()[Session::i()->get("_tempTheme", "ovk")];
|
||||
else if($this->requestParam("themePreview"))
|
||||
$this->template->theme = Themepacks::i()[$this->requestParam("themePreview")];
|
||||
else if($this->user->identity !== null && $this->user->identity->getTheme())
|
||||
$this->template->theme = $this->user->identity->getTheme();
|
||||
|
||||
// Знаю, каша ебаная, целестора рефактор всё равно сделает :)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if($owner > 0) {
|
||||
$user = $this->users->get($owner);
|
||||
if(!$user) $this->notFound();
|
||||
if (!$user->getPrivacyPermission('photos.read', $this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
$this->template->albums = $this->albums->getUserAlbums($user, $this->queryParam("p") ?? 1);
|
||||
$this->template->count = $this->albums->getUserAlbumsCount($user);
|
||||
$this->template->owner = $user;
|
||||
|
@ -129,13 +131,20 @@ final class PhotosPresenter extends OpenVKPresenter
|
|||
if($album->getPrettyId() !== $owner . "_" . $id || $album->isDeleted())
|
||||
$this->notFound();
|
||||
|
||||
if($owner > 0 /* bc we currently don't have perms for clubs */) {
|
||||
$ownerObject = (new Users)->get($owner);
|
||||
if(!$ownerObject->getPrivacyPermission('photos.read', $this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
}
|
||||
|
||||
$this->template->album = $album;
|
||||
$this->template->photos = iterator_to_array( $album->getPhotos( (int) ($this->queryParam("p") ?? 1) ) );
|
||||
$this->template->photos = iterator_to_array( $album->getPhotos( (int) ($this->queryParam("p") ?? 1), 20) );
|
||||
$this->template->paginatorConf = (object) [
|
||||
"count" => $album->getPhotosCount(),
|
||||
"page" => $this->queryParam("p") ?? 1,
|
||||
"amount" => sizeof($this->template->photos),
|
||||
"perPage" => OPENVK_DEFAULT_PER_PAGE,
|
||||
"perPage" => 20,
|
||||
"atBottom" => true
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -2,13 +2,9 @@
|
|||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\Ticket;
|
||||
use openvk\Web\Models\Repositories\Tickets;
|
||||
//
|
||||
use openvk\Web\Models\Entities\TicketComment;
|
||||
use openvk\Web\Models\Repositories\TicketComments;
|
||||
// use openvk\Web\Models\Repositories\Users;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use openvk\Web\Util\Telegram;
|
||||
use Chandler\Session\Session;
|
||||
use Netcarver\Textile;
|
||||
|
||||
|
@ -21,7 +17,7 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
|
||||
function __construct(Tickets $tickets, TicketComments $ticketComments)
|
||||
{
|
||||
$this->tickets = $tickets;
|
||||
$this->tickets = $tickets;
|
||||
$this->comments = $ticketComments;
|
||||
|
||||
parent::__construct();
|
||||
|
@ -29,22 +25,17 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
|
||||
function renderIndex(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->template->mode = in_array($this->queryParam("act"), ["faq", "new", "list"]) ? $this->queryParam("act") : "faq";
|
||||
|
||||
$tickets = $this->tickets->getTicketsByuId($this->user->id);
|
||||
if ($tickets) {
|
||||
$this->template->tickets = $tickets;
|
||||
}
|
||||
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST")
|
||||
{
|
||||
if(!empty($this->postParam("name")) && !empty($this->postParam("text")))
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->template->mode = in_array($this->queryParam("act"), ["faq", "new", "list"]) ? $this->queryParam("act") : "faq";
|
||||
|
||||
$tickets = $this->tickets->getTicketsByuId($this->user->id);
|
||||
if($tickets)
|
||||
$this->template->tickets = $tickets;
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!empty($this->postParam("name")) && !empty($this->postParam("text"))) {
|
||||
$this->assertNoCSRF();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
|
||||
$ticket = new Ticket;
|
||||
$ticket->setType(0);
|
||||
$ticket->setUser_id($this->user->id);
|
||||
|
@ -52,12 +43,23 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
$ticket->setText($this->postParam("text"));
|
||||
$ticket->setcreated(time());
|
||||
$ticket->save();
|
||||
|
||||
$helpdeskChat = OPENVK_ROOT_CONF["openvk"]["credentials"]["telegram"]["helpdeskChat"];
|
||||
if($helpdeskChat) {
|
||||
$serverUrl = ovk_scheme(true) . $_SERVER["SERVER_NAME"];
|
||||
$ticketText = ovk_proc_strtr($this->postParam("text"), 1500);
|
||||
$telegramText = "<b>📬 Новый тикет!</b>\n\n";
|
||||
$telegramText .= "<a href='$serverUrl/support/reply/{$ticket->getId()}'>{$ticket->getName()}</a>\n";
|
||||
$telegramText .= "$ticketText\n\n";
|
||||
$telegramText .= "Автор: <a href='$serverUrl{$ticket->getUser()->getURL()}'>{$ticket->getUser()->getCanonicalName()}</a> ({$ticket->getUser()->getRegistrationIP()})\n";
|
||||
Telegram::send($helpdeskChat, $telegramText);
|
||||
}
|
||||
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: /support/view/" . $ticket->getId());
|
||||
} else {
|
||||
$this->flashFail("err", "Ошибка", "Вы не ввели имя или текст ");
|
||||
$this->flashFail("err", tr("error"), tr("you_have_not_entered_name_or_text"));
|
||||
}
|
||||
// $this->template->test = 'cool post';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,48 +90,49 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
function renderView(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$ticket = $this->tickets->get($id);
|
||||
$ticketComments1 = $this->comments->getCommentsById($id);
|
||||
if(!$ticket || $ticket->isDeleted() != 0 || $ticket->authorId() !== $this->user->id) {
|
||||
$ticket = $this->tickets->get($id);
|
||||
$ticketComments = $this->comments->getCommentsById($id);
|
||||
if(!$ticket || $ticket->isDeleted() != 0 || $ticket->getUserId() !== $this->user->id) {
|
||||
$this->notFound();
|
||||
} else {
|
||||
$this->template->ticket = $ticket;
|
||||
$this->template->comments = $ticketComments1;
|
||||
$this->template->id = $id;
|
||||
$this->template->ticket = $ticket;
|
||||
$this->template->comments = $ticketComments;
|
||||
$this->template->id = $id;
|
||||
}
|
||||
}
|
||||
|
||||
function renderDelete(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
if (!empty($id)) {
|
||||
$ticket = $this->tickets->get($id);
|
||||
if (!$ticket || $ticket->isDeleted() != 0 || $ticket->authorId() !== $this->user->id)
|
||||
{
|
||||
$this->notFound();
|
||||
} else {
|
||||
$ticket->delete();
|
||||
header("HTTP/1.1 302 Found");
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(!empty($id)) {
|
||||
$ticket = $this->tickets->get($id);
|
||||
if(!$ticket || $ticket->isDeleted() != 0 || $ticket->getUserId() !== $this->user->id && !$this->hasPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0)) {
|
||||
$this->notFound();
|
||||
} else {
|
||||
header("HTTP/1.1 302 Found");
|
||||
if($ticket->getUserId() !== $this->user->id && $this->hasPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0))
|
||||
header("Location: /support/tickets");
|
||||
else
|
||||
header("Location: /support");
|
||||
}
|
||||
$ticket->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderMakeComment(int $id): void
|
||||
{
|
||||
$ticket = $this->tickets->get($id);
|
||||
|
||||
if($ticket->isDeleted() === 1 || $ticket->getType() === 2 || $ticket->authorId() !== $this->user->id) {
|
||||
if($ticket->isDeleted() === 1 || $ticket->getType() === 2 || $ticket->getUserId() !== $this->user->id) {
|
||||
header("HTTP/1.1 403 Forbidden");
|
||||
header("Location: /support/view/" . $id);
|
||||
exit;
|
||||
}
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST")
|
||||
{
|
||||
if(!empty($this->postParam("text")))
|
||||
{
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if(!empty($this->postParam("text"))) {
|
||||
$ticket->setType(0);
|
||||
$ticket->save();
|
||||
|
||||
|
@ -147,7 +150,7 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
header("HTTP/1.1 302 Found");
|
||||
header("Location: /support/view/" . $id);
|
||||
} else {
|
||||
$this->flashFail("err", "Ошибка", "Вы не ввели текст");
|
||||
$this->flashFail("err", tr("error"), tr("you_have_not_entered_text"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,10 +159,14 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
{
|
||||
$this->assertPermission('openvk\Web\Models\Entities\TicketReply', 'write', 0);
|
||||
$ticket = $this->tickets->get($id);
|
||||
|
||||
if(!$ticket || $ticket->isDeleted() != 0)
|
||||
$this->notFound();
|
||||
|
||||
$ticketComments = $this->comments->getCommentsById($id);
|
||||
$this->template->ticket = $ticket;
|
||||
$this->template->comments = $ticketComments;
|
||||
$this->template->id = $id;
|
||||
$this->template->ticket = $ticket;
|
||||
$this->template->comments = $ticketComments;
|
||||
$this->template->id = $id;
|
||||
}
|
||||
|
||||
function renderAnswerTicketReply(int $id): void
|
||||
|
@ -168,12 +175,10 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
|
||||
$ticket = $this->tickets->get($id);
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST")
|
||||
{
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if(!empty($this->postParam("text")) && !empty($this->postParam("status")))
|
||||
{
|
||||
if(!empty($this->postParam("text")) && !empty($this->postParam("status"))) {
|
||||
$ticket->setType($this->postParam("status"));
|
||||
$ticket->save();
|
||||
|
||||
|
@ -185,14 +190,13 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
$comment->setTicket_id($id);
|
||||
$comment->setCreated(time());
|
||||
$comment->save();
|
||||
} elseif (empty($this->postParam("text"))) {
|
||||
} elseif(empty($this->postParam("text"))) {
|
||||
$ticket->setType($this->postParam("status"));
|
||||
$ticket->save();
|
||||
}
|
||||
|
||||
$this->flashFail("succ", "Тикет изменён", "Изменения вступят силу через несколько секунд.");
|
||||
$this->flashFail("succ", tr("ticket_changed"), tr("ticket_changed_comment"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function renderKnowledgeBaseArticle(string $name): void
|
||||
|
@ -220,4 +224,24 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
$this->template->heading = $heading;
|
||||
$this->template->content = $parser->parse($content);
|
||||
}
|
||||
}
|
||||
|
||||
function renderRateAnswer(int $id, int $mark): void
|
||||
{
|
||||
$this->willExecuteWriteAction();
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertNoCSRF();
|
||||
|
||||
$comment = $this->comments->get($id);
|
||||
|
||||
if($this->user->id !== $comment->getTicket()->getUser()->getId())
|
||||
exit(header("HTTP/1.1 403 Forbidden"));
|
||||
|
||||
if($mark !== 1 && $mark !== 2)
|
||||
exit(header("HTTP/1.1 400 Bad Request"));
|
||||
|
||||
$comment->setMark($mark);
|
||||
$comment->save();
|
||||
|
||||
exit(header("HTTP/1.1 200 OK"));
|
||||
}
|
||||
}
|
194
Web/Presenters/TopicsPresenter.php
Normal file
|
@ -0,0 +1,194 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{Topic, Club, Comment, Photo, Video};
|
||||
use openvk\Web\Models\Repositories\{Topics, Clubs};
|
||||
|
||||
final class TopicsPresenter extends OpenVKPresenter
|
||||
{
|
||||
private $topics;
|
||||
private $clubs;
|
||||
|
||||
function __construct(Topics $topics, Clubs $clubs)
|
||||
{
|
||||
$this->topics = $topics;
|
||||
$this->clubs = $clubs;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
function renderBoard(int $id): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$club = $this->clubs->get($id);
|
||||
if(!$club)
|
||||
$this->notFound();
|
||||
|
||||
$this->template->club = $club;
|
||||
$page = (int) ($this->queryParam("p") ?? 1);
|
||||
|
||||
$query = $this->queryParam("query");
|
||||
if($query) {
|
||||
$results = $this->topics->find($club, $query);
|
||||
$this->template->topics = $results->page($page);
|
||||
$this->template->count = $results->size();
|
||||
} else {
|
||||
$this->template->topics = $this->topics->getClubTopics($club, $page);
|
||||
$this->template->count = $this->topics->getClubTopicsCount($club);
|
||||
}
|
||||
|
||||
$this->template->paginatorConf = (object) [
|
||||
"count" => $this->template->count,
|
||||
"page" => $page,
|
||||
"amount" => NULL,
|
||||
"perPage" => OPENVK_DEFAULT_PER_PAGE,
|
||||
];
|
||||
}
|
||||
|
||||
function renderTopic(int $clubId, int $topicId): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$topic = $this->topics->getTopicById($clubId, $topicId);
|
||||
if(!$topic)
|
||||
$this->notFound();
|
||||
|
||||
$this->template->topic = $topic;
|
||||
$this->template->club = $topic->getClub();
|
||||
$this->template->count = $topic->getCommentsCount();
|
||||
$this->template->page = (int) ($this->queryParam("p") ?? 1);
|
||||
$this->template->comments = iterator_to_array($topic->getComments($this->template->page));
|
||||
}
|
||||
|
||||
function renderCreate(int $clubId): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$club = $this->clubs->get($clubId);
|
||||
if(!$club)
|
||||
$this->notFound();
|
||||
|
||||
if(!$club->isEveryoneCanCreateTopics() && !$club->canBeModifiedBy($this->user->identity))
|
||||
$this->notFound();
|
||||
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$this->willExecuteWriteAction();
|
||||
$title = $this->postParam("title");
|
||||
|
||||
if(!$title)
|
||||
$this->flashFail("err", tr("failed_to_create_topic"), tr("no_title_specified"));
|
||||
|
||||
$flags = 0;
|
||||
if($this->postParam("as_group") === "on" && $club->canBeModifiedBy($this->user->identity))
|
||||
$flags |= 0b10000000;
|
||||
|
||||
$topic = new Topic;
|
||||
$topic->setGroup($club->getId());
|
||||
$topic->setOwner($this->user->id);
|
||||
$topic->setTitle(ovk_proc_strtr($title, 127));
|
||||
$topic->setCreated(time());
|
||||
$topic->setFlags($flags);
|
||||
$topic->save();
|
||||
|
||||
// TODO move to trait
|
||||
try {
|
||||
$photo = NULL;
|
||||
$video = NULL;
|
||||
if($_FILES["_pic_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||
$album = NULL;
|
||||
if($wall > 0 && $wall === $this->user->id)
|
||||
$album = (new Albums)->getUserWallAlbum($wallOwner);
|
||||
|
||||
$photo = Photo::fastMake($this->user->id, $this->postParam("text"), $_FILES["_pic_attachment"], $album);
|
||||
}
|
||||
|
||||
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||
$video = Video::fastMake($this->user->id, $this->postParam("text"), $_FILES["_vid_attachment"]);
|
||||
}
|
||||
} catch(ISE $ex) {
|
||||
$this->flash("err", "Не удалось опубликовать комментарий", "Файл медиаконтента повреждён или слишком велик.");
|
||||
$this->redirect("/topic" . $topic->getPrettyId(), static::REDIRECT_TEMPORARY);
|
||||
}
|
||||
|
||||
if(!empty($this->postParam("text")) || $photo || $video) {
|
||||
try {
|
||||
$comment = new Comment;
|
||||
$comment->setOwner($this->user->id);
|
||||
$comment->setModel(get_class($topic));
|
||||
$comment->setTarget($topic->getId());
|
||||
$comment->setContent($this->postParam("text"));
|
||||
$comment->setCreated(time());
|
||||
$comment->setFlags($flags);
|
||||
$comment->save();
|
||||
} catch (\LengthException $ex) {
|
||||
$this->flash("err", "Не удалось опубликовать комментарий", "Комментарий слишком большой.");
|
||||
$this->redirect("/topic" . $topic->getPrettyId(), static::REDIRECT_TEMPORARY);
|
||||
}
|
||||
|
||||
if(!is_null($photo))
|
||||
$comment->attach($photo);
|
||||
|
||||
if(!is_null($video))
|
||||
$comment->attach($video);
|
||||
}
|
||||
|
||||
$this->redirect("/topic" . $topic->getPrettyId(), static::REDIRECT_TEMPORARY);
|
||||
}
|
||||
|
||||
$this->template->club = $club;
|
||||
$this->template->graffiti = (bool) ovkGetQuirk("comments.allow-graffiti");
|
||||
}
|
||||
|
||||
function renderEdit(int $clubId, int $topicId): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
$topic = $this->topics->getTopicById($clubId, $topicId);
|
||||
if(!$topic)
|
||||
$this->notFound();
|
||||
|
||||
if(!$topic->canBeModifiedBy($this->user->identity))
|
||||
$this->notFound();
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$this->willExecuteWriteAction();
|
||||
$title = $this->postParam("title");
|
||||
|
||||
if(!$title)
|
||||
$this->flashFail("err", tr("failed_to_change_topic"), tr("no_title_specified"));
|
||||
|
||||
$topic->setTitle(ovk_proc_strtr($title, 127));
|
||||
$topic->setClosed(empty($this->postParam("close")) ? 0 : 1);
|
||||
|
||||
if($topic->getClub()->canBeModifiedBy($this->user->identity))
|
||||
$topic->setPinned(empty($this->postParam("pin")) ? 0 : 1);
|
||||
|
||||
$topic->save();
|
||||
|
||||
$this->flash("succ", tr("changes_saved"), tr("topic_changes_saved_comment"));
|
||||
$this->redirect("/topic" . $topic->getPrettyId(), static::REDIRECT_TEMPORARY);
|
||||
}
|
||||
|
||||
$this->template->topic = $topic;
|
||||
$this->template->club = $topic->getClub();
|
||||
}
|
||||
|
||||
function renderDelete(int $clubId, int $topicId): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->assertNoCSRF();
|
||||
|
||||
$topic = $this->topics->getTopicById($clubId, $topicId);
|
||||
if(!$topic)
|
||||
$this->notFound();
|
||||
|
||||
if(!$topic->canBeModifiedBy($this->user->identity))
|
||||
$this->notFound();
|
||||
|
||||
$this->willExecuteWriteAction();
|
||||
$topic->deleteTopic();
|
||||
|
||||
$this->redirect("/board" . $topic->getClub()->getId(), static::REDIRECT_TEMPORARY);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@ use openvk\Web\Models\Repositories\Albums;
|
|||
use openvk\Web\Models\Repositories\Videos;
|
||||
use openvk\Web\Models\Repositories\Notes;
|
||||
use openvk\Web\Models\Repositories\Vouchers;
|
||||
use openvk\Web\Util\Validator;
|
||||
use openvk\Web\Models\Entities\Notifications\CoinsTransferNotification;
|
||||
use Chandler\Security\Authenticator;
|
||||
use lfkeitel\phptotp\{Base32, Totp};
|
||||
use chillerlan\QRCode\{QRCode, QROptions};
|
||||
|
@ -53,6 +55,8 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$page = abs($this->queryParam("p") ?? 1);
|
||||
if(!$user)
|
||||
$this->notFound();
|
||||
elseif (!$user->getPrivacyPermission('friends.read', $this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
else
|
||||
$this->template->user = $user;
|
||||
|
||||
|
@ -77,11 +81,13 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$this->assertUserLoggedIn();
|
||||
|
||||
$user = $this->users->get($id);
|
||||
if(!$user) {
|
||||
if(!$user)
|
||||
$this->notFound();
|
||||
} else {
|
||||
elseif (!$user->getPrivacyPermission('groups.read', $this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
else {
|
||||
$this->template->user = $user;
|
||||
$this->template->page = $this->queryParam("p") ?? 1;
|
||||
$this->template->page = (int) ($this->queryParam("p") ?? 1);
|
||||
$this->template->admin = $this->queryParam("act") == "managed";
|
||||
}
|
||||
}
|
||||
|
@ -127,87 +133,99 @@ final class UserPresenter extends OpenVKPresenter
|
|||
if(!$id)
|
||||
$this->notFound();
|
||||
|
||||
$user = $this->users->get($id);
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if($_GET['act'] === "main" || $_GET['act'] == NULL) {
|
||||
$user->setFirst_Name(empty($this->postParam("first_name")) ? $user->getFirstName() : $this->postParam("first_name"));
|
||||
$user->setLast_Name(empty($this->postParam("last_name")) ? "" : $this->postParam("last_name"));
|
||||
$user->setPseudo(empty($this->postParam("pseudo")) ? NULL : $this->postParam("pseudo"));
|
||||
$user->setStatus(empty($this->postParam("status")) ? NULL : $this->postParam("status"));
|
||||
if (strtotime($this->postParam("birthday")) < time())
|
||||
$user->setBirthday(strtotime($this->postParam("birthday")));
|
||||
$user = $this->users->get($id);
|
||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
if($_GET['act'] === "main" || $_GET['act'] == NULL) {
|
||||
$user->setFirst_Name(empty($this->postParam("first_name")) ? $user->getFirstName() : $this->postParam("first_name"));
|
||||
$user->setLast_Name(empty($this->postParam("last_name")) ? "" : $this->postParam("last_name"));
|
||||
$user->setPseudo(empty($this->postParam("pseudo")) ? NULL : $this->postParam("pseudo"));
|
||||
$user->setStatus(empty($this->postParam("status")) ? NULL : $this->postParam("status"));
|
||||
if (strtotime($this->postParam("birthday")) < time())
|
||||
$user->setBirthday(strtotime($this->postParam("birthday")));
|
||||
|
||||
if ($this->postParam("marialstatus") <= 8 && $this->postParam("marialstatus") >= 0)
|
||||
$user->setMarital_Status($this->postParam("marialstatus"));
|
||||
if ($this->postParam("marialstatus") <= 8 && $this->postParam("marialstatus") >= 0)
|
||||
$user->setMarital_Status($this->postParam("marialstatus"));
|
||||
|
||||
if ($this->postParam("politViews") <= 9 && $this->postParam("politViews") >= 0)
|
||||
$user->setPolit_Views($this->postParam("politViews"));
|
||||
|
||||
if ($this->postParam("gender") <= 1 && $this->postParam("gender") >= 0)
|
||||
$user->setSex($this->postParam("gender"));
|
||||
|
||||
if(!empty($this->postParam("phone")) && $this->postParam("phone") !== $user->getPhone()) {
|
||||
if(!OPENVK_ROOT_CONF["openvk"]["credentials"]["smsc"]["enable"])
|
||||
$this->flashFail("err", tr("error_segmentation"), "котлетки");
|
||||
|
||||
if ($this->postParam("politViews") <= 9 && $this->postParam("politViews") >= 0)
|
||||
$user->setPolit_Views($this->postParam("politViews"));
|
||||
$code = $user->setPhoneWithVerification($this->postParam("phone"));
|
||||
|
||||
if ($this->postParam("gender") <= 1 && $this->postParam("gender") >= 0)
|
||||
$user->setSex($this->postParam("gender"));
|
||||
|
||||
if(!empty($this->postParam("phone")) && $this->postParam("phone") !== $user->getPhone()) {
|
||||
if(!OPENVK_ROOT_CONF["openvk"]["credentials"]["smsc"]["enable"])
|
||||
$this->flashFail("err", tr("error_segmentation"), "котлетки");
|
||||
|
||||
$code = $user->setPhoneWithVerification($this->postParam("phone"));
|
||||
|
||||
if(!Sms::send($this->postParam("phone"), "OPENVK - Your verification code is: $code"))
|
||||
$this->flashFail("err", tr("error_segmentation"), "котлетки: Remote err!");
|
||||
}
|
||||
} elseif($_GET['act'] === "contacts") {
|
||||
if(!Sms::send($this->postParam("phone"), "OPENVK - Your verification code is: $code"))
|
||||
$this->flashFail("err", tr("error_segmentation"), "котлетки: Remote err!");
|
||||
}
|
||||
} elseif($_GET['act'] === "contacts") {
|
||||
if(empty($this->postParam("email_contact")) || Validator::i()->emailValid($this->postParam("email_contact")))
|
||||
$user->setEmail_Contact(empty($this->postParam("email_contact")) ? NULL : $this->postParam("email_contact"));
|
||||
$user->setTelegram(empty($this->postParam("telegram")) ? NULL : ltrim($this->postParam("telegram"), "@"));
|
||||
$user->setCity(empty($this->postParam("city")) ? NULL : $this->postParam("city"));
|
||||
$user->setAddress(empty($this->postParam("address")) ? NULL : $this->postParam("address"));
|
||||
|
||||
$website = $this->postParam("website") ?? "";
|
||||
if(empty($website))
|
||||
$user->setWebsite(NULL);
|
||||
else
|
||||
$user->setWebsite((!parse_url($website, PHP_URL_SCHEME) ? "https://" : "") . $website);
|
||||
} elseif($_GET['act'] === "interests") {
|
||||
$user->setInterests(empty($this->postParam("interests")) ? NULL : ovk_proc_strtr($this->postParam("interests"), 300));
|
||||
$user->setFav_Music(empty($this->postParam("fav_music")) ? NULL : ovk_proc_strtr($this->postParam("fav_music"), 300));
|
||||
$user->setFav_Films(empty($this->postParam("fav_films")) ? NULL : ovk_proc_strtr($this->postParam("fav_films"), 300));
|
||||
$user->setFav_Shows(empty($this->postParam("fav_shows")) ? NULL : ovk_proc_strtr($this->postParam("fav_shows"), 300));
|
||||
$user->setFav_Books(empty($this->postParam("fav_books")) ? NULL : ovk_proc_strtr($this->postParam("fav_books"), 300));
|
||||
$user->setFav_Quote(empty($this->postParam("fav_quote")) ? NULL : ovk_proc_strtr($this->postParam("fav_quote"), 300));
|
||||
$user->setAbout(empty($this->postParam("about")) ? NULL : ovk_proc_strtr($this->postParam("about"), 300));
|
||||
} elseif($_GET['act'] === "status") {
|
||||
if(mb_strlen($this->postParam("status")) > 255) {
|
||||
$statusLength = (string) mb_strlen($this->postParam("status"));
|
||||
$this->flashFail("err", "Ошибка", "Статус слишком длинный ($statusLength символов вместо 255 символов)");
|
||||
}
|
||||
else
|
||||
$this->flashFail("err", tr("invalid_email_address"), tr("invalid_email_address_comment"));
|
||||
|
||||
$user->setStatus(empty($this->postParam("status")) ? NULL : $this->postParam("status"));
|
||||
$user->save();
|
||||
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: /id" . $user->getId());
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$user->save();
|
||||
} catch(\PDOException $ex) {
|
||||
if($ex->getCode() == 23000)
|
||||
$this->flashFail("err", tr("error"), tr("error_shorturl"));
|
||||
$telegram = $this->postParam("telegram");
|
||||
if(empty($telegram) || Validator::i()->telegramValid($telegram))
|
||||
if(strpos($telegram, "t.me/") === 0)
|
||||
$user->setTelegram(empty($telegram) ? NULL : substr($telegram, 5));
|
||||
else
|
||||
throw $ex;
|
||||
}
|
||||
$user->setTelegram(empty($telegram) ? NULL : ltrim($telegram, "@"));
|
||||
else
|
||||
$this->flashFail("err", tr("invalid_telegram_name"), tr("invalid_telegram_name_comment"));
|
||||
|
||||
$user->setCity(empty($this->postParam("city")) ? NULL : $this->postParam("city"));
|
||||
$user->setAddress(empty($this->postParam("address")) ? NULL : $this->postParam("address"));
|
||||
|
||||
$this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
|
||||
$website = $this->postParam("website") ?? "";
|
||||
if(empty($website))
|
||||
$user->setWebsite(NULL);
|
||||
else
|
||||
$user->setWebsite((!parse_url($website, PHP_URL_SCHEME) ? "https://" : "") . $website);
|
||||
} elseif($_GET['act'] === "interests") {
|
||||
$user->setInterests(empty($this->postParam("interests")) ? NULL : ovk_proc_strtr($this->postParam("interests"), 300));
|
||||
$user->setFav_Music(empty($this->postParam("fav_music")) ? NULL : ovk_proc_strtr($this->postParam("fav_music"), 300));
|
||||
$user->setFav_Films(empty($this->postParam("fav_films")) ? NULL : ovk_proc_strtr($this->postParam("fav_films"), 300));
|
||||
$user->setFav_Shows(empty($this->postParam("fav_shows")) ? NULL : ovk_proc_strtr($this->postParam("fav_shows"), 300));
|
||||
$user->setFav_Books(empty($this->postParam("fav_books")) ? NULL : ovk_proc_strtr($this->postParam("fav_books"), 300));
|
||||
$user->setFav_Quote(empty($this->postParam("fav_quote")) ? NULL : ovk_proc_strtr($this->postParam("fav_quote"), 300));
|
||||
$user->setAbout(empty($this->postParam("about")) ? NULL : ovk_proc_strtr($this->postParam("about"), 300));
|
||||
} elseif($_GET['act'] === "status") {
|
||||
if(mb_strlen($this->postParam("status")) > 255) {
|
||||
$statusLength = (string) mb_strlen($this->postParam("status"));
|
||||
$this->flashFail("err", "Ошибка", "Статус слишком длинный ($statusLength символов вместо 255 символов)");
|
||||
}
|
||||
|
||||
$user->setStatus(empty($this->postParam("status")) ? NULL : $this->postParam("status"));
|
||||
$user->save();
|
||||
|
||||
header("HTTP/1.1 302 Found");
|
||||
header("Location: /id" . $user->getId());
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->template->mode = in_array($this->queryParam("act"), [
|
||||
"main", "contacts", "interests", "avatar"
|
||||
]) ? $this->queryParam("act")
|
||||
: "main";
|
||||
try {
|
||||
$user->save();
|
||||
} catch(\PDOException $ex) {
|
||||
if($ex->getCode() == 23000)
|
||||
$this->flashFail("err", tr("error"), tr("error_shorturl"));
|
||||
else
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
$this->template->user = $user;
|
||||
$this->flash("succ", tr("changes_saved"), tr("changes_saved_comment"));
|
||||
}
|
||||
|
||||
$this->template->mode = in_array($this->queryParam("act"), [
|
||||
"main", "contacts", "interests", "avatar"
|
||||
]) ? $this->queryParam("act")
|
||||
: "main";
|
||||
|
||||
$this->template->user = $user;
|
||||
}
|
||||
|
||||
function renderVerifyPhone(): void
|
||||
|
@ -358,6 +376,7 @@ final class UserPresenter extends OpenVKPresenter
|
|||
"menu_notatoj" => "notes",
|
||||
"menu_grupoj" => "groups",
|
||||
"menu_novajoj" => "news",
|
||||
"menu_ligiloj" => "links",
|
||||
];
|
||||
foreach($settings as $checkbox => $setting)
|
||||
$user->setLeftMenuItemStatus($setting, $this->checkbox($checkbox));
|
||||
|
@ -447,4 +466,42 @@ final class UserPresenter extends OpenVKPresenter
|
|||
$this->user->identity->save();
|
||||
$this->flashFail("succ", tr("information_-1"), tr("two_factor_authentication_disabled_message"));
|
||||
}
|
||||
|
||||
function renderCoinsTransfer(): void
|
||||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->willExecuteWriteAction();
|
||||
|
||||
$receiverAddress = $this->postParam("receiver");
|
||||
$value = (int) $this->postParam("value");
|
||||
$message = $this->postParam("message");
|
||||
|
||||
if(!$receiverAddress || !$value)
|
||||
$this->flashFail("err", tr("failed_to_tranfer_points"), tr("not_all_information_has_been_entered"));
|
||||
|
||||
if($value < 0)
|
||||
$this->flashFail("err", tr("failed_to_tranfer_points"), tr("negative_transfer_value"));
|
||||
|
||||
if(iconv_strlen($message) > 255)
|
||||
$this->flashFail("err", tr("failed_to_tranfer_points"), tr("message_is_too_long"));
|
||||
|
||||
$receiver = $this->users->getByAddress($receiverAddress);
|
||||
if(!$receiver)
|
||||
$this->flashFail("err", tr("failed_to_tranfer_points"), tr("receiver_not_found"));
|
||||
|
||||
if($this->user->identity->getCoins() < $value)
|
||||
$this->flashFail("err", tr("failed_to_tranfer_points"), tr("you_dont_have_enough_points"));
|
||||
|
||||
if($this->user->id !== $receiver->getId()) {
|
||||
$this->user->identity->setCoins($this->user->identity->getCoins() - $value);
|
||||
$this->user->identity->save();
|
||||
|
||||
$receiver->setCoins($receiver->getCoins() + $value);
|
||||
$receiver->save();
|
||||
|
||||
(new CoinsTransferNotification($receiver, $this->user->identity, $value, $message))->emit();
|
||||
}
|
||||
|
||||
$this->flashFail("succ", tr("information_-1"), tr("points_transfer_successful", tr("points_amount", $value), $receiver->getURL(), htmlentities($receiver->getCanonicalName())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ final class VideosPresenter extends OpenVKPresenter
|
|||
{
|
||||
$user = $this->users->get($id);
|
||||
if(!$user) $this->notFound();
|
||||
if(!$user->getPrivacyPermission('videos.read', $this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
|
||||
$this->template->user = $user;
|
||||
$this->template->videos = $this->videos->getByUser($user, (int) ($this->queryParam("p") ?? 1));
|
||||
|
@ -38,6 +40,8 @@ final class VideosPresenter extends OpenVKPresenter
|
|||
{
|
||||
$user = $this->users->get($owner);
|
||||
if(!$user) $this->notFound();
|
||||
if(!$user->getPrivacyPermission('videos.read', $this->user->identity ?? NULL))
|
||||
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
|
||||
|
||||
if($this->videos->getByOwnerAndVID($owner, $vId)->isDeleted()) $this->notFound();
|
||||
|
||||
|
|
|
@ -197,7 +197,7 @@ final class WallPresenter extends OpenVKPresenter
|
|||
}
|
||||
|
||||
$flags = 0;
|
||||
if($this->postParam("as_group") === "on")
|
||||
if($this->postParam("as_group") === "on" && $wallOwner instanceof Club && $wallOwner->canBeModifiedBy($this->user->identity))
|
||||
$flags |= 0b10000000;
|
||||
if($this->postParam("force_sign") === "on")
|
||||
$flags |= 0b01000000;
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
{extends "@layout.xml"}
|
||||
{block title}Вам бан{/block}
|
||||
{block title}{_"banned_title"}{/block}
|
||||
|
||||
{block header}
|
||||
Вы были верискокнуты
|
||||
{_"banned_header"}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<center>
|
||||
<img src="/assets/packages/static/openvk/img/oof.apng" alt="Пользователь заблокирован." style="width: 20%;" />
|
||||
<img src="/assets/packages/static/openvk/img/oof.apng" alt="{_'banned_alt'}" style="width: 20%;" />
|
||||
</center>
|
||||
<p>
|
||||
Извините, <b>{$thisUser->getCanonicalName()}</b>, но вы были верискокнуты.<br/>
|
||||
А причина этому проста: <b>{$thisUser->getBanReason()}</b>. К сожалению, на этот раз
|
||||
нам пришлось заблокировать вас навсегда.
|
||||
{tr("banned_1", htmlentities($thisUser->getCanonicalName()))|noescape}<br/>
|
||||
{tr("banned_2", htmlentities($thisUser->getBanReason()))|noescape}
|
||||
</p>
|
||||
<hr/>
|
||||
<p>
|
||||
Вы всё ещё можете <a href="/support?act=new">написать в службу поддержки</a>, если считаете что произошла ошибка
|
||||
или <a href="/logout">выйти</a>.
|
||||
{tr("banned_3", urlencode($csrfToken))|noescape}
|
||||
</p>
|
||||
{/block}
|
||||
{/block}
|
||||
|
|
|
@ -96,10 +96,12 @@
|
|||
<a href="/search?type=groups">{_"header_groups"}</a>
|
||||
</div>
|
||||
<div class="link">
|
||||
<a href="/donate">{_"header_donate"}</a>
|
||||
<a href="/search">{_"header_search"}</a>
|
||||
</div>
|
||||
<div class="link">
|
||||
<a href="/search">{_"header_search"}</a>
|
||||
<a href="/invite">
|
||||
{_"header_invite"}
|
||||
</a>
|
||||
</div>
|
||||
<div class="link">
|
||||
<a href="/support">
|
||||
|
@ -166,24 +168,25 @@
|
|||
<a href="/settings" class="link">{_"my_settings"}</a>
|
||||
{var canAccessAdminPanel = $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
||||
{var canAccessHelpdesk = $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
||||
{var menuLinksAvaiable = sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0}
|
||||
{var menuLinksAvaiable = sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0 && $thisUser->getLeftMenuItemStatus('links')}
|
||||
<div n:if="$canAccessAdminPanel || $canAccessHelpdesk || $menuLinksAvaiable" class="menu_divider"></div>
|
||||
{if $canAccessAdminPanel}
|
||||
<a href="/admin" class="link">Админ-панель</a>
|
||||
{/if}
|
||||
{if $canAccessHelpdesk}
|
||||
<a href="/support/tickets" class="link">Helpdesk
|
||||
{if $helpdeskTicketAnsweredCount > 0}
|
||||
{if $helpdeskTicketNotAnsweredCount > 0}
|
||||
(<b>{$helpdeskTicketNotAnsweredCount}</b>)
|
||||
{/if}
|
||||
</a>
|
||||
<a href="/admin/reports" class="link">Reports</a>
|
||||
{/if}
|
||||
<a
|
||||
n:if="$thisUser->getLeftMenuItemStatus('links')"
|
||||
n:foreach="OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links'] as $menuItem"
|
||||
href="{$menuItem['url']}"
|
||||
target="_blank"
|
||||
class="link">{$menuItem["name"]}</a>
|
||||
class="link">{strpos($menuItem["name"], "@") === 0 ? tr(substr($menuItem["name"], 1)) : $menuItem["name"]}</a>
|
||||
<div id="_groupListPinnedGroups">
|
||||
<div n:if="$thisUser->getPinnedClubCount() > 0" class="menu_divider"></div>
|
||||
<a
|
||||
|
@ -202,8 +205,8 @@
|
|||
style="max-width: 100%; margin-top: 50px;" />
|
||||
</a>
|
||||
{else}
|
||||
<a href="/support" class="link">Поддержка</a>
|
||||
<a href="/logout" class="link">Выйти</a>
|
||||
<a href="/support" class="link">{_"menu_support"}</a>
|
||||
<a href="/logout?hash={urlencode($csrfToken)}" class="link">{_"menu_logout"}</a>
|
||||
{/if}
|
||||
{else}
|
||||
<form id="fastLogin" action="/login" method="POST" enctype="multipart/form-data">
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
|
||||
{include description, x => $dat}
|
||||
</td>
|
||||
<td n:ifset="actions" valign="top" class="action_links" style="width: 150px">
|
||||
{include actions, x => $dat}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}Пригласить{/block}
|
||||
{block title}{_invite}{/block}
|
||||
|
||||
{block header}
|
||||
Пригласить
|
||||
{_invite}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
Вы можете пригласить своих друзей или знакомых в сеть с помощью индивидуальной ссылки:<br><br>
|
||||
{_"you_can_invite"}<br><br>
|
||||
<center>
|
||||
<input type="text" readonly value="https://{$_SERVER["HTTP_HOST"]}/reg?ref={rawurlencode($thisUser->getRefLinkId())}" size="50" />
|
||||
</center>
|
||||
<p>Приложите эту ссылку к вашему сообщению. Пользователь зарегистрируется, и он сразу появится у вас в друзьях.</p>
|
||||
<p>{_"you_can_invite_2"}</p>
|
||||
{/block}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{extends "../@layout.xml"}
|
||||
|
||||
{block title}Об OpenVK{/block}
|
||||
{block title}{_about_openvk}{/block}
|
||||
|
||||
{block header}
|
||||
Об OpenVK
|
||||
{_about_openvk}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
|
@ -343,7 +343,7 @@
|
|||
<tbody>
|
||||
<tr class="h">
|
||||
<th>Name</th>
|
||||
<th>Status</th>
|
||||
<th style="width: 50px;">Status</th>
|
||||
<th>Version</th>
|
||||
<th>Description</th>
|
||||
<th>Author</th>
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
{extends "../@layout.xml"}
|
||||
|
||||
{block title}
|
||||
Восстановление доступа
|
||||
{_"access_recovery"}
|
||||
{/block}
|
||||
|
||||
{block header}
|
||||
Восстановить доступ к странице
|
||||
{_"page_access_recovery"}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<p>
|
||||
Введите ваш новый пароль. Все текущие сеансы будут приостановлены и токены доступа будут аннулированы.
|
||||
{_"access_recovery_info_2"}
|
||||
</p>
|
||||
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
<label for="password">Новый пароль: </label>
|
||||
<label for="password">{_"new_password"}: </label>
|
||||
<input id="password" type="password" name="password" required />
|
||||
<br/><br/>
|
||||
{if $is2faEnabled}
|
||||
<label for="code">Код двухфакторной аутентификации: </label>
|
||||
<label for="code">{_"2fa_code_2"}: </label>
|
||||
<input id="code" type="text" name="code" required />
|
||||
<br/><br/>
|
||||
{/if}
|
||||
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" value="Сбросить пароль" class="button" style="float: right;" />
|
||||
<input type="submit" value="{_'reset_password'}" class="button" style="float: right;" />
|
||||
</form>
|
||||
{/block}
|
||||
|
|
|
@ -7,20 +7,20 @@
|
|||
|
||||
{block headIncludes}
|
||||
{if !$referer}
|
||||
<meta name="description" content="Зарегистрируйтесь в OpenVK прямо сейчас!" />
|
||||
<meta name="description" content="{tr('register_meta_desc', OPENVK_ROOT_CONF['openvk']['appearance']['name'])}" />
|
||||
{else}
|
||||
<meta property="og:title" content="{$referer->getFullName()} приглашает вас в OpenVK!" />
|
||||
<meta property="og:title" content="{tr('register_referer_meta_title', $referer->getFullName(), OPENVK_ROOT_CONF['openvk']['appearance']['name'])}" />
|
||||
<meta property="og:image" content="{$referer->getAvatarUrl()}" />
|
||||
|
||||
<meta name="description"
|
||||
content="Присоединяйтесь к {$referer->getFullName()} и множеству других пользователей в OpenVK!" />
|
||||
content="{tr('register_referer_meta_desc', $referer->getFullName(), OPENVK_ROOT_CONF['openvk']['appearance']['name'])}" />
|
||||
{/if}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
{if OPENVK_ROOT_CONF['openvk']['preferences']['registration']['enable'] || $referer}
|
||||
<p n:if="!is_null($referer)" align="center">
|
||||
<strong>{$referer->getFullName()}</strong> приглашает вас в OpenVK!
|
||||
{tr("invites_you_to", $referer->getFullName(), OPENVK_ROOT_CONF['openvk']['appearance']['name'])|noescape}
|
||||
</p>
|
||||
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
|
@ -102,9 +102,9 @@
|
|||
</form>
|
||||
{else}
|
||||
<center>
|
||||
<img src="/assets/packages/static/openvk/img/oof.apng" alt="Регистрация закрыта." style="width: 20%;" />
|
||||
<img src="/assets/packages/static/openvk/img/oof.apng" alt="{_'registration_closed'}" style="width: 20%;" />
|
||||
<p>
|
||||
Регистрация отключена системным администратором. При возможности попросите приглашение у вашего знакомого, если он зарегистрирован на этом сайте.
|
||||
{_"registration_disabled_info"}
|
||||
{if OPENVK_ROOT_CONF['openvk']['preferences']['registration']['reason']}
|
||||
<br/><br/><b>{php echo OPENVK_ROOT_CONF['openvk']['preferences']['registration']['reason']}</b>
|
||||
{/if}
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
{extends "../@layout.xml"}
|
||||
|
||||
{block title}
|
||||
Восстановление доступа
|
||||
{_"access_recovery"}
|
||||
{/block}
|
||||
|
||||
{block header}
|
||||
Восстановить доступ к странице
|
||||
{_"page_access_recovery"}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<p>
|
||||
Забыли пароль? Не волнуйтесь, введите ваши данные и мы отправим вам email с инструкциями по восстановлению аккаунта.
|
||||
{_"access_recovery_info"}
|
||||
</p>
|
||||
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
<label for="login">Логин: </label>
|
||||
<label for="login">{_"log_in"}: </label>
|
||||
<input id="login" type="text" name="login" required />
|
||||
<br/><br/>
|
||||
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" value="Сбросить пароль" class="button" style="float: right;" />
|
||||
<input type="submit" value="{_'reset_password'}" class="button" style="float: right;" />
|
||||
</form>
|
||||
{/block}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
{/block}
|
||||
|
||||
{block description}
|
||||
<table class="ugc-table" n:if="$hideInfo ? !$x->anon : true">
|
||||
<table class="ugc-table" n:if="$hideInfo ? (!$x->anon || $x->sender->getId() === $thisUser->getId()) : true">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="nobold">{_sender}: </span></td>
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
<input type="file" name="ava" accept="image/*" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_wall}: </span>
|
||||
</td>
|
||||
|
@ -77,6 +77,15 @@
|
|||
<input type="checkbox" name="wall" value="1" {if $club->canPost()}checked{/if}/> {_group_allow_post_for_everyone}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_discussions}: </span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" name="everyone_can_create_topics" value="1" n:attr="checked => $club->isEveryoneCanCreateTopics()" /> {_everyone_can_create_topics}<br>
|
||||
<input type="checkbox" name="display_topics_above_wall" value="1" n:attr="checked => $club->isDisplayTopicsAboveWallEnabled()" /> {_display_list_of_topics_above_wall}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_group_administrators_list}: </span>
|
||||
|
|
|
@ -14,10 +14,6 @@
|
|||
<a n:if="$onlyShowManagers" href="/club{$club->getId()}/followers" style="float: right;">{_only_administrators}</a>
|
||||
{/block}
|
||||
|
||||
{block actions}
|
||||
|
||||
{/block}
|
||||
|
||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||
|
||||
{block tabs}
|
||||
|
@ -81,39 +77,67 @@
|
|||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
<tr n:if="$club->canBeModifiedBy($thisUser ?? NULL)">
|
||||
<td width="120" valign="top"><span class="nobold">{_actions}: </span></td>
|
||||
<td>
|
||||
<a href="/club{$club->getId()}/setAdmin.jsp?user={$user->getId()}&hash={rawurlencode($csrfToken)}" n:if="$club->getOwner()->getId() !== $user->getId()">
|
||||
{if $manager}
|
||||
{_devote}
|
||||
{else}
|
||||
{_promote_to_admin}
|
||||
{/if}
|
||||
</a>
|
||||
{if $manager}
|
||||
|
|
||||
<a href="javascript:setClubAdminComment('{$club->getId()}', '{$manager->getUserId()}', '{rawurlencode($csrfToken)}')">
|
||||
{_set_comment}
|
||||
</a>
|
||||
{/if}
|
||||
<a n:if="$club->getOwner()->getId() === $user->getId()" href="javascript:setClubAdminComment('{$club->getId()}', '{$club->getOwner()->getId()}', '{rawurlencode($csrfToken)}')">
|
||||
{_set_comment}
|
||||
</a>
|
||||
{if $manager}
|
||||
|
|
||||
<a href="/club{$club->getId()}/setAdmin.jsp?user={$user->getId()}&hidden={(int) !$manager->isHidden()}&hash={rawurlencode($csrfToken)}">
|
||||
{if $manager->isHidden()}{_hidden_yes}{else}{_hidden_no}{/if}
|
||||
</a>
|
||||
{/if}
|
||||
{if $club->getOwner()->getId() == $user->getId()}
|
||||
|
|
||||
<a href="/club{$club->getId()}/setAdmin.jsp?user={$user->getId()}&hidden={(int) !$club->isOwnerHidden()}&hash={rawurlencode($csrfToken)}">
|
||||
{if $club->isOwnerHidden()}{_hidden_yes}{else}{_hidden_no}{/if}
|
||||
</a>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script n:if="$club->getOwner()->getId() != $user->getId() && $manager && $thisUser->getId() == $club->getOwner()->getId()">
|
||||
console.log("gayshit");
|
||||
console.log("сам такой");
|
||||
function changeOwner(club, newOwner) {
|
||||
const action = "/groups/" + club + "/setNewOwner/" + newOwner;
|
||||
|
||||
MessageBox({_group_changeowner_modal_title}, `
|
||||
{tr("group_changeowner_modal_text", htmlentities($user->getFullName()))|noescape}
|
||||
<br/><br/>
|
||||
<form id="transfer-owner-permissions-form" method="post">
|
||||
<label for="password">{_password|noescape}</label>
|
||||
<input type="password" id="password" name="password" required />
|
||||
<input type="hidden" name="hash" value={$csrfToken} />
|
||||
</form>
|
||||
`, [{_transfer}, {_cancel}], [
|
||||
() => {
|
||||
$("#transfer-owner-permissions-form").attr("action", action);
|
||||
document.querySelector("#transfer-owner-permissions-form").submit();
|
||||
}, Function.noop
|
||||
]);
|
||||
}
|
||||
</script>
|
||||
{/block}
|
||||
|
||||
{block actions}
|
||||
{var user = $x instanceof $Manager ? $x->getUser() : $x}
|
||||
{var manager = $x instanceof $Manager ? $x : $club->getManager($user, !$club->canBeModifiedBy($thisUser))}
|
||||
{if $club->canBeModifiedBy($thisUser ?? NULL)}
|
||||
<a class="profile_link" href="/club{$club->getId()}/setAdmin.jsp?user={$user->getId()}&hash={rawurlencode($csrfToken)}" n:if="$club->getOwner()->getId() !== $user->getId()">
|
||||
{if $manager}
|
||||
{_devote}
|
||||
{else}
|
||||
{_promote_to_admin}
|
||||
{/if}
|
||||
</a>
|
||||
{if $club->getOwner()->getId() != $user->getId() && $manager && $thisUser->getId() == $club->getOwner()->getId()}
|
||||
<a class="profile_link" href="javascript:changeOwner({$club->getId()}, {$user->getId()})">
|
||||
{_promote_to_owner}
|
||||
</a>
|
||||
{/if}
|
||||
{if $manager}
|
||||
<a class="profile_link" href="javascript:setClubAdminComment('{$club->getId()}', '{$manager->getUserId()}', '{rawurlencode($csrfToken)}')">
|
||||
{_set_comment}
|
||||
</a>
|
||||
{/if}
|
||||
<a class="profile_link" n:if="$club->getOwner()->getId() === $user->getId()" href="javascript:setClubAdminComment('{$club->getId()}', '{$club->getOwner()->getId()}', '{rawurlencode($csrfToken)}')">
|
||||
{_set_comment}
|
||||
</a>
|
||||
{if $manager}
|
||||
<a class="profile_link" href="/club{$club->getId()}/setAdmin.jsp?user={$user->getId()}&hidden={(int) !$manager->isHidden()}&hash={rawurlencode($csrfToken)}">
|
||||
{if $manager->isHidden()}{_hidden_yes}{else}{_hidden_no}{/if}
|
||||
</a>
|
||||
{/if}
|
||||
{if $club->getOwner()->getId() == $user->getId()}
|
||||
<a class="profile_link" href="/club{$club->getId()}/setAdmin.jsp?user={$user->getId()}&hidden={(int) !$club->isOwnerHidden()}&hash={rawurlencode($csrfToken)}">
|
||||
{if $club->isOwnerHidden()}{_hidden_yes}{else}{_hidden_no}{/if}
|
||||
</a>
|
||||
{/if}
|
||||
{/if}
|
||||
{/block}
|
|
@ -41,45 +41,54 @@
|
|||
</table>
|
||||
</div>
|
||||
<div n:if="$club->getFollowersCount() > 0">
|
||||
{var followersCount = $club->getFollowersCount()}
|
||||
|
||||
<div class="content_title_expanded" onclick="hidePanel(this, {$followersCount});">
|
||||
{_participants}
|
||||
</div>
|
||||
<div>
|
||||
<div class="content_subtitle">
|
||||
{tr("participants", $followersCount)}
|
||||
<div style="float:right;">
|
||||
<a href="/club{$club->getId()}/followers">{_all_title}</a>
|
||||
</div>
|
||||
{var followersCount = $club->getFollowersCount()}
|
||||
|
||||
<div class="content_title_expanded" onclick="hidePanel(this, {$followersCount});">
|
||||
{_participants}
|
||||
</div>
|
||||
<div>
|
||||
<div class="content_subtitle">
|
||||
{tr("participants", $followersCount)}
|
||||
<div style="float:right;">
|
||||
<a href="/club{$club->getId()}/followers">{_all_title}</a>
|
||||
</div>
|
||||
<div style="padding-left: 5px;">
|
||||
<table
|
||||
n:foreach="$club->getFollowers(1) as $follower"
|
||||
n:class="User"
|
||||
style="text-align:center;display:inline-block;width:62px"
|
||||
cellspacing=4>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{$follower->getURL()}">
|
||||
<img src="{$follower->getAvatarUrl()}" width="50" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{$follower->getURL()}">{$follower->getFirstName()}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="padding-left: 5px;" class="content_list long">
|
||||
<div class="cl_element" n:foreach="$club->getFollowers(1) as $follower">
|
||||
<div class="cl_avatar">
|
||||
<a href="{$follower->getURL()}">
|
||||
<img class="ava" src="{$follower->getAvatarUrl()}" />
|
||||
</a>
|
||||
</div>
|
||||
<a href="{$follower->getURL()}" class="cl_name">
|
||||
<text class="cl_fname">{$follower->getFirstName()}</text>
|
||||
<text class="cl_lname">{$follower->getLastName()}</text>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div n:if="($topicsCount > 0 || $club->isEveryoneCanCreateTopics() || ($thisUser && $club->canBeModifiedBy($thisUser))) && $club->isDisplayTopicsAboveWallEnabled()">
|
||||
<div class="content_title_expanded" onclick="hidePanel(this, {$topicsCount});">
|
||||
{_discussions}
|
||||
</div>
|
||||
<div>
|
||||
<div class="content_subtitle">
|
||||
{tr("topics", $topicsCount)}
|
||||
<div style="float: right;">
|
||||
<a href="/board{$club->getId()}">{_"all_title"}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div n:foreach="$topics as $topic" class="topic-list-item" style="padding: 8px;">
|
||||
<b><a href="/topic{$topic->getPrettyId()}">{$topic->getTitle()}</a></b><br>
|
||||
<span class="nobold">{tr("updated_at", $topic->getUpdateTime())}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{presenter "openvk!Wall->wallEmbedded", -$club->getId()}
|
||||
|
||||
</div>
|
||||
<div class="right_small_block">
|
||||
<a href="{$club->getAvatarLink()|nocheck}">
|
||||
|
@ -197,12 +206,31 @@
|
|||
</div>
|
||||
<div>
|
||||
<b><a href="/album{$album->getPrettyId()}">{$album->getName()}</a></b><br>
|
||||
<span class="nobold">Обновлён {$album->getEditTime() ?? $album->getCreationTime()}</span>
|
||||
<span class="nobold">{tr("updated_at", $album->getEditTime() ?? $album->getCreationTime())}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div n:if="($topicsCount > 0 || $club->isEveryoneCanCreateTopics() || ($thisUser && $club->canBeModifiedBy($thisUser))) && !$club->isDisplayTopicsAboveWallEnabled()">
|
||||
<div class="content_title_expanded" onclick="hidePanel(this, {$topicsCount});">
|
||||
{_discussions}
|
||||
</div>
|
||||
<div>
|
||||
<div class="content_subtitle">
|
||||
{tr("topics", $topicsCount)}
|
||||
<div style="float: right;">
|
||||
<a href="/board{$club->getId()}">{_"all_title"}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div n:foreach="$topics as $topic" class="topic-list-item">
|
||||
<b><a href="/topic{$topic->getPrettyId()}">{$topic->getTitle()}</a></b><br>
|
||||
<span class="nobold">{tr("updated_at", $topic->getUpdateTime())}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/block}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<div n:if="($online = $correspondent->getOnline()->timestamp()) < time() + 2678400" style="float: right;">
|
||||
{var diff = date_diff(date_create(), date_create('@' . $online))}
|
||||
{if 5 >= $diff->i}
|
||||
<span><b>Online</b></span>
|
||||
<span><b>{_online}</b></span>
|
||||
{else}
|
||||
<span>{$correspondent->isFemale() ? "заходила" : "заходил"} {$correspondent->getOnline()}</span>
|
||||
{/if}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}{_my_messages}{/block}
|
||||
|
||||
{block header}{/block}
|
||||
{block header}{_my_messages}{/block}
|
||||
|
||||
{block content}
|
||||
<div class="tabs">
|
||||
|
|
|
@ -19,13 +19,6 @@
|
|||
</div>
|
||||
{/block}
|
||||
|
||||
{block actions}
|
||||
<div class="tile">
|
||||
<a class="profile_link" href="?">{_unreaded}</a>
|
||||
<a class="profile_link" href="?act=archived">{_archive}</a>
|
||||
</div>
|
||||
{/block}
|
||||
|
||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||
|
||||
{block link|strip|stripHtml}
|
||||
|
|
|
@ -27,23 +27,24 @@
|
|||
|
|
||||
<a href="/album{$album->getPrettyId()}/edit">{_"edit_album"}</a>
|
||||
{/if}
|
||||
<br/>
|
||||
<br/><br/>
|
||||
{if $album->getPhotosCount() > 0}
|
||||
{foreach $photos as $photo}
|
||||
{php if($photo->isDeleted()) continue; }
|
||||
<div class="album-photo" style="display: inline-table;">
|
||||
<a
|
||||
n:if="!is_null($thisUser) && $album->canBeModifiedBy($thisUser)"
|
||||
href="/album{$album->getPrettyId()}/remove_photo.pl/{$photo->getId()}" class="album-photo--delete">
|
||||
×
|
||||
</a>
|
||||
|
||||
<a href="/photo{$photo->getPrettyId()}?from=album{$album->getId()}">
|
||||
<img class="album-photo--image" src="{$photo->getURL()}" alt="{$photo->getDescription()}" style="width:unset;max-height:unset;max-width: 188px;" />
|
||||
</a>
|
||||
</div>
|
||||
{/foreach}
|
||||
|
||||
<div class="container_gray album-flex">
|
||||
{foreach $photos as $photo}
|
||||
{php if($photo->isDeleted()) continue; }
|
||||
<div class="album-photo">
|
||||
<a
|
||||
n:if="!is_null($thisUser) && $album->canBeModifiedBy($thisUser)"
|
||||
href="/album{$album->getPrettyId()}/remove_photo.pl/{$photo->getId()}" class="album-photo--delete">
|
||||
×
|
||||
</a>
|
||||
|
||||
<a href="/photo{$photo->getPrettyId()}?from=album{$album->getId()}">
|
||||
<img class="album-photo--image" src="{$photo->getURL()}" alt="{$photo->getDescription()}" />
|
||||
</a>
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
{include "../components/paginator.xml", conf => $paginatorConf}
|
||||
{else}
|
||||
{include "../components/nothing.xml"}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
{/block}
|
||||
|
||||
{block header}
|
||||
OpenVK »
|
||||
{=OPENVK_ROOT_CONF["openvk"]["appearance"]["name"]} »
|
||||
{if $type === "users"}
|
||||
{tr("search_for_people")}
|
||||
{else}
|
||||
|
@ -17,20 +17,31 @@
|
|||
{/if}
|
||||
{/block}
|
||||
|
||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||
|
||||
{block tabs}
|
||||
<form style="margin-left: 12px;">
|
||||
<input name="type" type="hidden" value="{$_GET['type'] ?? 'users'}" />
|
||||
<input name="query" type="text" placeholder="{_"header_search"}" value="{$_GET['query'] ?? ''}" style="width: 90%" />
|
||||
<input type="submit" class="button" value="{_"search_button"}" style="width: 9%" />
|
||||
<div {if $type === "users"}id="activetabs"{/if} class="tab">
|
||||
<a {if $type === "users"}id="act_tab_a"{/if} href="/search?type=users">
|
||||
{_users}
|
||||
</a>
|
||||
</div>
|
||||
<div {if $type === "groups"}id="activetabs"{/if} class="tab">
|
||||
<a {if $type === "groups"}id="act_tab_a"{/if} href="/search?type=groups">
|
||||
{_groups}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form class="header_search_inputbt">
|
||||
<input name="type" type="hidden" value="{$type ?? 'users'}" />
|
||||
<input name="query" class="header_search_input" placeholder="{_search_placeholder}" value="{$_GET['query'] ?? ''}" />
|
||||
<button class="button_search">{_search_button}</button>
|
||||
</form>
|
||||
|
||||
<p style="margin-left: 15px;">
|
||||
|
||||
<p style="margin-left: 15px; margin-top: 0;">
|
||||
<b>{tr("results", $count)}</b>
|
||||
</p>
|
||||
{/block}
|
||||
|
||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||
|
||||
{block link|strip|stripHtml}
|
||||
{$x->getURL()}
|
||||
{/block}
|
||||
|
|
|
@ -73,7 +73,12 @@
|
|||
{if $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
||||
<a href="{$comment->getUser()->getURL()}">
|
||||
<span class="nobold">
|
||||
({$comment->getUser()->getFirstName()} {iconv_substr($comment->getUser()->getLastName(), 0, 1)}.)
|
||||
{var lastName = $comment->getUser()->getLastName()}
|
||||
{if empty(trim($lastName))}
|
||||
({$comment->getUser()->getFirstName()})
|
||||
{else}
|
||||
({$comment->getUser()->getFirstName()} {iconv_substr($lastName, 0, 1)}.)
|
||||
{/if}
|
||||
</span>
|
||||
</a>
|
||||
{/if}
|
||||
|
@ -104,6 +109,18 @@
|
|||
<a href="/support/comment/{$comment->getId()}/delete">{_delete}</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{if $comment->getUType() === 1 && !is_null($comment->isLikedByUser())}
|
||||
<div class="post-menu">
|
||||
<strong>
|
||||
{if $comment->isLikedByUser()}
|
||||
{_support_good_answer_agent}
|
||||
{else}
|
||||
{_support_bad_answer_agent}
|
||||
{/if}
|
||||
</strong>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -6,6 +6,28 @@
|
|||
{/block}
|
||||
|
||||
{block content}
|
||||
<script>
|
||||
function markAnswer(id, mark) {
|
||||
let url = "/support/comment/" + id + "/rate/" + mark + "?hash=" + {urlencode($csrfToken)};
|
||||
$.ajax(url, {
|
||||
error: errorHandler,
|
||||
success: success(id, mark)
|
||||
});
|
||||
}
|
||||
|
||||
function success(id, mark) {
|
||||
if(mark == 1)
|
||||
document.getElementById("markText-" + id).innerHTML = {_support_good_answer_user};
|
||||
else
|
||||
document.getElementById("markText-" + id).innerHTML = {_support_bad_answer_user};
|
||||
|
||||
document.getElementById("markLinks-" + id).remove();
|
||||
}
|
||||
|
||||
function errorHandler(id, mark) {
|
||||
document.getElementById("markText-" + id).innerHTML = {_error};
|
||||
}
|
||||
</script>
|
||||
{if $ticket->isDeleted() == 0 }
|
||||
<div class="post-author">
|
||||
<a href="#" style="font-size:13px;">
|
||||
|
@ -90,11 +112,34 @@
|
|||
{$comment->getText()|noescape}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{if $comment->getUType() === 0}
|
||||
<div class="post-menu">
|
||||
<a href="/support/comment/{$comment->getId()}/delete">{_delete}</a>
|
||||
</div>
|
||||
<div class="post-menu">
|
||||
<a href="/support/comment/{$comment->getId()}/delete">{_delete}</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{if $comment->getUType() === 1}
|
||||
<div class="post-menu">
|
||||
{var isLikedByUser = $comment->isLikedByUser()}
|
||||
<strong id="markText-{$comment->getId()}">
|
||||
{if !is_null($isLikedByUser)}
|
||||
{if $comment->isLikedByUser()}
|
||||
{_support_good_answer_user}
|
||||
{else}
|
||||
{_support_bad_answer_user}
|
||||
{/if}
|
||||
{/if}
|
||||
</strong>
|
||||
<div id="markLinks-{$comment->getId()}">
|
||||
{if is_null($isLikedByUser)}
|
||||
<a onClick="markAnswer({$comment->getId()}, 1)">{_support_rate_good_answer}</a>
|
||||
|
|
||||
<a onClick="markAnswer({$comment->getId()}, 2)">{_support_rate_bad_answer}</a>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
61
Web/Presenters/templates/Topics/Board.xml
Normal file
|
@ -0,0 +1,61 @@
|
|||
{extends "../@listView.xml"}
|
||||
{var iterator = iterator_to_array($topics)}
|
||||
{var page = $paginatorConf->page}
|
||||
|
||||
{block title}{_discussions} {$club->getCanonicalName()}{/block}
|
||||
|
||||
{block header}
|
||||
<a href="{$club->getURL()}">{$club->getCanonicalName()}</a> » {_discussions}
|
||||
|
||||
<div n:if="$club->isEveryoneCanCreateTopics() || $club->canBeModifiedBy($thisUser)" style="float: right;">
|
||||
<a href="/board{$club->getId()}/create">{_create_topic}</a>
|
||||
</div>
|
||||
{/block}
|
||||
|
||||
{block tabs}
|
||||
<form style="margin-left: 12px;">
|
||||
<input name="query" class="header_search_input" placeholder="{_"header_search"}" value="{$_GET['query'] ?? ''}" style="width: 90%" />
|
||||
<input type="submit" class="button" value="{_"search_button"}" style="width: 7%" />
|
||||
</form>
|
||||
|
||||
<p style="margin-left: 15px;">
|
||||
<b>{tr("results", $count)}</b>
|
||||
</p>
|
||||
{/block}
|
||||
|
||||
{block actions}
|
||||
|
||||
{/block}
|
||||
|
||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||
|
||||
{block link|strip|stripHtml}
|
||||
/topic{$x->getPrettyId()}
|
||||
{/block}
|
||||
|
||||
{block preview}
|
||||
|
||||
{/block}
|
||||
|
||||
{block name}
|
||||
{$x->getTitle()}
|
||||
<div n:if="$x->isPinned()" class="pinned-mark"></div>
|
||||
{/block}
|
||||
|
||||
{block description}
|
||||
<div style="float: left;">
|
||||
{tr("messages", $x->getCommentsCount())}
|
||||
</div>
|
||||
{var lastComment = $x->getLastComment()}
|
||||
<div n:if="$lastComment" class="avatar-list-item" style="float: right;">
|
||||
<div class="avatar">
|
||||
<a href="{$lastComment->getOwner()->getURL()}">
|
||||
<img class="ava" src="{$lastComment->getOwner()->getAvatarUrl()}" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="info">
|
||||
<a href="{$lastComment->getOwner()->getURL()}" class="title">{$lastComment->getOwner()->getCanonicalName()}</a>
|
||||
<div class="subtitle">{_replied} {$lastComment->getPublicationTime()}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
96
Web/Presenters/templates/Topics/Create.xml
Normal file
|
@ -0,0 +1,96 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}{_new_topic}{/block}
|
||||
|
||||
{block header}
|
||||
<a href="{$club->getURL()}">{$club->getCanonicalName()}</a>
|
||||
»
|
||||
<a href="/board{$club->getId()}">{_discussions}</a>
|
||||
»
|
||||
{_new_topic}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
<table cellspacing="7" cellpadding="0" width="80%" border="0" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_title}</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="title" style="width: 100%;" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_text}</span>
|
||||
</td>
|
||||
<td>
|
||||
<textarea id="wall-post-input1" name="text" style="width: 100%; resize: none;"></textarea>
|
||||
<div n:if="$club->canBeModifiedBy($thisUser)" class="post-opts">
|
||||
<label>
|
||||
<input type="checkbox" name="as_group" onchange="onWallAsGroupClick(this)" /> {_post_as_group}
|
||||
</label>
|
||||
</div>
|
||||
<div id="post-buttons1">
|
||||
<div class="post-upload">
|
||||
{_attachment}: <span>(unknown)</span>
|
||||
</div>
|
||||
<input type="file" class="postFileSel" id="postFilePic" name="_pic_attachment" accept="image/*" style="display: none;" />
|
||||
<input type="file" class="postFileSel" id="postFileVid" name="_vid_attachment" accept="video/*" style="display: none;" />
|
||||
<br/>
|
||||
<div style="float: right; display: flex; flex-direction: column;">
|
||||
<a href="javascript:void(u('#post-buttons1 #wallAttachmentMenu').toggleClass('hidden'));">
|
||||
{_attach}
|
||||
</a>
|
||||
|
||||
<div id="wallAttachmentMenu" class="hidden">
|
||||
<a href="javascript:void(document.querySelector('#post-buttons1 input[name=_pic_attachment]').click());">
|
||||
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-x-egon.png" />
|
||||
{_attach_photo}
|
||||
</a>
|
||||
<a href="javascript:void(document.querySelector('#post-buttons1 input[name=_vid_attachment]').click());">
|
||||
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-vnd.rn-realmedia.png" />
|
||||
{_attach_video}
|
||||
</a>
|
||||
<a n:if="$graffiti ?? false" href="javascript:initGraffiti(1);">
|
||||
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/actions/draw-brush.png" />
|
||||
{_draw_graffiti}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" value="{_create_topic}" class="button" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$(document).ready(() => {
|
||||
u("#post-buttons1 .postFileSel").on("change", function() {
|
||||
handleUpload.bind(this, 1)();
|
||||
});
|
||||
|
||||
setupWallPostInputHandlers(1);
|
||||
});
|
||||
</script>
|
||||
|
||||
{if $graffiti}
|
||||
{script "js/node_modules/react/dist/react-with-addons.min.js"}
|
||||
{script "js/node_modules/react-dom/dist/react-dom.min.js"}
|
||||
{script "js/vnd_literallycanvas.js"}
|
||||
{css "js/node_modules/literallycanvas/lib/css/literallycanvas.css"}
|
||||
{/if}
|
||||
{/block}
|
55
Web/Presenters/templates/Topics/Edit.xml
Normal file
|
@ -0,0 +1,55 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}{_edit_topic} "{$topic->getTitle()}"{/block}
|
||||
|
||||
{block header}
|
||||
<a href="{$club->getURL()}">{$club->getCanonicalName()}</a>
|
||||
»
|
||||
<a href="/board{$club->getId()}">{_discussions}</a>
|
||||
»
|
||||
{_edit_topic}
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<div class="container_gray">
|
||||
<b>{$topic->getTitle()}</b>
|
||||
<br />
|
||||
<a href="{$topic->getOwner()->getURL()}">{$topic->getOwner()->getCanonicalName()}</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" enctype="multipart/form-data" style="margin-top: 20px;">
|
||||
<table cellspacing="7" cellpadding="0" width="80%" border="0" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_title}</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="title" style="width: 100%;" value="{$topic->getTitle()}" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_topic_settings}</span>
|
||||
</td>
|
||||
<td>
|
||||
{if $topic->getClub()->canBeModifiedBy($thisUser)}
|
||||
<input type="checkbox" name="pin" n:attr="checked => $topic->isPinned()" /> {_pin_topic}<br />
|
||||
{/if}
|
||||
<input type="checkbox" name="close" n:attr="checked => $topic->isClosed()" /> {_close_topic}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="button" href="/topic{$topic->getPrettyId()}/delete?hash={urlencode($csrfToken)}">{_delete_topic}</a>
|
||||
</td>
|
||||
<td>
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" value="{_save}" class="button" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
</form>
|
||||
{/block}
|
29
Web/Presenters/templates/Topics/Topic.xml
Normal file
|
@ -0,0 +1,29 @@
|
|||
{extends "../@layout.xml"}
|
||||
{block title}{_view_topic} "{$topic->getTitle()}"{/block}
|
||||
|
||||
{block header}
|
||||
<a href="{$club->getURL()}">{$club->getCanonicalName()}</a>
|
||||
»
|
||||
<a href="/board{$club->getId()}">{_discussions}</a>
|
||||
»
|
||||
{_view_topic}
|
||||
|
||||
<div style="float: right;" n:if="$topic->canBeModifiedBy($thisUser)">
|
||||
<a href="/topic{$club->getId()}_{$topic->getVirtualId()}/edit">{_edit_topic_action}</a>
|
||||
</div>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<div class="container_gray">
|
||||
<b>{$topic->getTitle()}</b>
|
||||
<br />
|
||||
<a href="{$topic->getOwner()->getURL()}">{$topic->getOwner()->getCanonicalName()}</a>
|
||||
<div class="nobold" style="float: right;">
|
||||
{_created} {$topic->getPublicationTime()}
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 20px;">
|
||||
<h4>{tr("topic_messages_count", $count)}</h4>
|
||||
{include "../components/comments.xml", comments => $comments, count => $count, page => $page, model => "topics", club => $club, readOnly => $topic->isClosed(), showTitle => false, parent => $topic}
|
||||
</div>
|
||||
{/block}
|
|
@ -135,7 +135,7 @@
|
|||
<span class="nobold">{_"birth_date"}: </span>
|
||||
</td>
|
||||
<td>
|
||||
<input max={date('Y-m-d')} name="birthday" value={gmdate("Y-m-d", $user->getBirthday())} type="date"/>
|
||||
<input max={date('Y-m-d')} name="birthday" value={$user->getBirthday()->format('%Y-%m-%d')} type="date"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
|
@ -79,3 +79,39 @@
|
|||
</tbody>
|
||||
</table>
|
||||
{/block}
|
||||
|
||||
{block actions}
|
||||
{if $x->getId() !== $thisUser->getId()}
|
||||
{var subStatus = $x->getSubscriptionStatus($thisUser)}
|
||||
{if $subStatus === 0}
|
||||
<form action="/setSub/user" method="post" class="profile_link_form">
|
||||
<input type="hidden" name="act" value="add" />
|
||||
<input type="hidden" name="id" value="{$x->getId()}" />
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" class="profile_link" value="{_"friends_add"}" />
|
||||
</form>
|
||||
{elseif $subStatus === 1}
|
||||
<form action="/setSub/user" method="post" class="profile_link_form">
|
||||
<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="{_"friends_accept"}" />
|
||||
</form>
|
||||
{elseif $subStatus === 2}
|
||||
<form action="/setSub/user" method="post" class="profile_link_form">
|
||||
<input type="hidden" name="act" value="rem" />
|
||||
<input type="hidden" name="id" value="{$x->getId()}" />
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" class="profile_link" value="{_"friends_reject"}" />
|
||||
</form>
|
||||
{elseif $subStatus === 3}
|
||||
<a href="/im?sel={$x->getId()}" class="profile_link">{_"send_message"}</a>
|
||||
<form action="/setSub/user" method="post" class="profile_link_form">
|
||||
<input type="hidden" name="act" value="rem" />
|
||||
<input type="hidden" name="id" value="{$x->getId()}" />
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" class="profile_link" value="{_"friends_delete"}" />
|
||||
</form>
|
||||
{/if}
|
||||
{/if}
|
||||
{/block}
|
||||
|
|
|
@ -49,23 +49,17 @@
|
|||
|
||||
{block description}
|
||||
{$x->getDescription()}
|
||||
{if $x->canBeModifiedBy($thisUser ?? NULL)}
|
||||
{var clubPinned = $thisUser->isClubPinned($x)}
|
||||
<table n:if="$clubPinned || $thisUser->getPinnedClubCount() <= 10">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="120" valign="top"><span class="nobold">{_actions}: </span></td>
|
||||
<td>
|
||||
<a href="/groups_pin?club={$x->getId()}&hash={rawurlencode($csrfToken)}" id="_pinGroup" data-group-name="{$x->getName()}" data-group-url="{$x->getUrl()}">
|
||||
{if $clubPinned}
|
||||
{_remove_from_left_menu}
|
||||
{else}
|
||||
{_add_to_left_menu}
|
||||
{/if}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{/block}
|
||||
|
||||
{block actions}
|
||||
{var clubPinned = $thisUser->isClubPinned($x)}
|
||||
{if $x->canBeModifiedBy($thisUser ?? NULL) && ($clubPinned || $thisUser->getPinnedClubCount() <= 10)}
|
||||
<a class="profile_link" href="/groups_pin?club={$x->getId()}&hash={rawurlencode($csrfToken)}" id="_pinGroup" data-group-name="{$x->getName()}" data-group-url="{$x->getUrl()}">
|
||||
{if $clubPinned}
|
||||
{_remove_from_left_menu}
|
||||
{else}
|
||||
{_add_to_left_menu}
|
||||
{/if}
|
||||
</a>
|
||||
{/if}
|
||||
{/block}
|
||||
|
|
|
@ -211,7 +211,7 @@
|
|||
<option value="3" {if $user->getPrivacySetting('page.read') == 3}selected{/if}>{_privacy_value_anybody_dative}</option>
|
||||
<option value="2" {if $user->getPrivacySetting('page.read') == 2}selected{/if}>{_privacy_value_users}</option>
|
||||
<option value="1" {if $user->getPrivacySetting('page.read') == 1}selected{/if}>{_privacy_value_friends_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('page.read') == 0}selected{/if}>{_privacy_value_only_me_and_super_capite_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('page.read') == 0}selected{/if}>{_privacy_value_only_me_dative}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -224,7 +224,7 @@
|
|||
<option value="3" {if $user->getPrivacySetting('page.info.read') == 3}selected{/if}>{_privacy_value_anybody_dative}</option>
|
||||
<option value="2" {if $user->getPrivacySetting('page.info.read') == 2}selected{/if}>{_privacy_value_users}</option>
|
||||
<option value="1" {if $user->getPrivacySetting('page.info.read') == 1}selected{/if}>{_privacy_value_friends_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('page.info.read') == 0}selected{/if}>{_privacy_value_only_me_and_super_capite_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('page.info.read') == 0}selected{/if}>{_privacy_value_only_me_dative}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -237,7 +237,7 @@
|
|||
<option value="3" {if $user->getPrivacySetting('groups.read') == 3}selected{/if}>{_privacy_value_anybody_dative}</option>
|
||||
<option value="2" {if $user->getPrivacySetting('groups.read') == 2}selected{/if}>{_privacy_value_users}</option>
|
||||
<option value="1" {if $user->getPrivacySetting('groups.read') == 1}selected{/if}>{_privacy_value_friends_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('groups.read') == 0}selected{/if}>{_privacy_value_only_me_and_super_capite_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('groups.read') == 0}selected{/if}>{_privacy_value_only_me_dative}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -250,7 +250,7 @@
|
|||
<option value="3" {if $user->getPrivacySetting('photos.read') == 3}selected{/if}>{_privacy_value_anybody_dative}</option>
|
||||
<option value="2" {if $user->getPrivacySetting('photos.read') == 2}selected{/if}>{_privacy_value_users}</option>
|
||||
<option value="1" {if $user->getPrivacySetting('photos.read') == 1}selected{/if}>{_privacy_value_friends_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('photos.read') == 0}selected{/if}>{_privacy_value_only_me_and_super_capite_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('photos.read') == 0}selected{/if}>{_privacy_value_only_me_dative}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -263,7 +263,7 @@
|
|||
<option value="3" {if $user->getPrivacySetting('videos.read') == 3}selected{/if}>{_privacy_value_anybody_dative}</option>
|
||||
<option value="2" {if $user->getPrivacySetting('videos.read') == 2}selected{/if}>{_privacy_value_users}</option>
|
||||
<option value="1" {if $user->getPrivacySetting('videos.read') == 1}selected{/if}>{_privacy_value_friends_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('videos.read') == 0}selected{/if}>{_privacy_value_only_me_and_super_capite_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('videos.read') == 0}selected{/if}>{_privacy_value_only_me_dative}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -276,7 +276,7 @@
|
|||
<option value="3" {if $user->getPrivacySetting('notes.read') == 3}selected{/if}>{_privacy_value_anybody_dative}</option>
|
||||
<option value="2" {if $user->getPrivacySetting('notes.read') == 2}selected{/if}>{_privacy_value_users}</option>
|
||||
<option value="1" {if $user->getPrivacySetting('notes.read') == 1}selected{/if}>{_privacy_value_friends_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('notes.read') == 0}selected{/if}>{_privacy_value_only_me_and_super_capite_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('notes.read') == 0}selected{/if}>{_privacy_value_only_me_dative}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -289,7 +289,7 @@
|
|||
<option value="3" {if $user->getPrivacySetting('friends.read') == 3}selected{/if}>{_privacy_value_anybody_dative}</option>
|
||||
<option value="2" {if $user->getPrivacySetting('friends.read') == 2}selected{/if}>{_privacy_value_users}</option>
|
||||
<option value="1" {if $user->getPrivacySetting('friends.read') == 1}selected{/if}>{_privacy_value_friends_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('friends.read') == 0}selected{/if}>{_privacy_value_only_me_and_super_capite_dative}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('friends.read') == 0}selected{/if}>{_privacy_value_only_me_dative}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -300,7 +300,7 @@
|
|||
<td>
|
||||
<select name="friends.add" style="width: 164px;">
|
||||
<option value="3" {if $user->getPrivacySetting('friends.add') == 2}selected{/if}>{_privacy_value_anybody}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('friends.add') == 0}selected{/if}>{_privacy_value_super_capite}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('friends.add') == 0}selected{/if}>{_privacy_value_nobody}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -312,7 +312,7 @@
|
|||
<select name="wall.write" style="width: 164px;">
|
||||
<option value="2" {if $user->getPrivacySetting('wall.write') == 2}selected{/if}>{_privacy_value_anybody}</option>
|
||||
<option value="1" {if $user->getPrivacySetting('wall.write') == 1}selected{/if}>{_privacy_value_friends}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('wall.write') == 0}selected{/if}>{_privacy_value_only_me_and_super_capite}</option>
|
||||
<option value="0" {if $user->getPrivacySetting('wall.write') == 0}selected{/if}>{_privacy_value_only_me}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -332,6 +332,7 @@
|
|||
|
||||
<div style="width: 75%; display: inline-block;">
|
||||
{presenter "openvk!Support->knowledgeBaseArticle", "points"}
|
||||
<center>{tr("also_you_can_transfer_points", $thisUser->getCoins(), rawurlencode($csrfToken))|noescape}</center>
|
||||
</div>
|
||||
<div style="width: 22%; float: right;">
|
||||
<p style="margin: 0; font-size: medium; text-align: center;">
|
||||
|
@ -505,6 +506,16 @@
|
|||
<td>
|
||||
<span class="nobold">{_my_feed}</span>
|
||||
</td>
|
||||
</tr><tr n:if="sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0">
|
||||
<td width="120" valign="top" align="right" align="right">
|
||||
<input
|
||||
n:attr="checked => $user->getLeftMenuItemStatus('links')"
|
||||
type="checkbox"
|
||||
name="menu_ligiloj" />
|
||||
</td>
|
||||
<td>
|
||||
<span class="nobold">{_additional_links}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
{block title}{$user->getCanonicalName()}{/block}
|
||||
|
||||
{block headIncludes}
|
||||
{if $user->getPrivacyPermission('page.read', $thisUser ?? NULL)}
|
||||
<!-- openGraph -->
|
||||
<meta property="og:title" content="{$user->getCanonicalName()}" />
|
||||
<meta property="og:url" content="http://{$_SERVER['HTTP_HOST']}{$user->getURL()}" />
|
||||
|
@ -22,6 +23,9 @@
|
|||
"url": {('http://') . $_SERVER['HTTP_HOST'] . $user->getURL()}
|
||||
}
|
||||
</script>
|
||||
{else}
|
||||
<meta name="robots" content="noindex, noarchive">
|
||||
{/if}
|
||||
{/block}
|
||||
|
||||
{block header}
|
||||
|
@ -87,25 +91,25 @@
|
|||
</a>
|
||||
{/if}
|
||||
|
||||
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce']" href="/gifts?act=pick&user={$user->getId()}" class="profile_link">{_send_gift}</a>
|
||||
|
||||
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && $user->getGiftCount() == 0" href="/gifts?act=pick&user={$user->getId()}" class="profile_link">{_send_gift}</a>
|
||||
|
||||
{var subStatus = $user->getSubscriptionStatus($thisUser)}
|
||||
{if $subStatus === 0}
|
||||
<form action="/setSub/user" method="post">
|
||||
<form action="/setSub/user" method="post" class="profile_link_form">
|
||||
<input type="hidden" name="act" value="add" />
|
||||
<input type="hidden" name="id" value="{$user->getId()}" />
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" class="profile_link" value="{_"friends_add"}" />
|
||||
</form>
|
||||
{elseif $subStatus === 1}
|
||||
<form action="/setSub/user" method="post">
|
||||
<form action="/setSub/user" method="post" class="profile_link_form">
|
||||
<input type="hidden" name="act" value="add" />
|
||||
<input type="hidden" name="id" value="{$user->getId()}" />
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" class="profile_link" value="{_"friends_accept"}" />
|
||||
</form>
|
||||
{elseif $subStatus === 2}
|
||||
<form action="/setSub/user" method="post">
|
||||
<form action="/setSub/user" method="post" class="profile_link_form">
|
||||
<input type="hidden" name="act" value="rem" />
|
||||
<input type="hidden" name="id" value="{$user->getId()}" />
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
|
@ -113,7 +117,7 @@
|
|||
</form>
|
||||
{elseif $subStatus === 3}
|
||||
<a href="/im?sel={$user->getId()}" class="profile_link">{_"send_message"}</a>
|
||||
<form action="/setSub/user" method="post">
|
||||
<form action="/setSub/user" method="post" class="profile_link_form">
|
||||
<input type="hidden" name="act" value="rem" />
|
||||
<input type="hidden" name="id" value="{$user->getId()}" />
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
|
@ -121,6 +125,7 @@
|
|||
</form>
|
||||
{/if}
|
||||
{/if}
|
||||
<a n:if="$user->getFollowersCount() > 0" href="/friends{$user->getId()}?act=incoming" class="profile_link">{tr("followers", $user->getFollowersCount())}</a>
|
||||
</div>
|
||||
<div n:if="isset($thisUser) && !$thisUser->prefersNotToSeeRating()" class="profile-hints">
|
||||
{var completeness = $user->getProfileCompletenessReport()}
|
||||
|
@ -146,39 +151,15 @@
|
|||
</a>
|
||||
<a n:if="in_array('telegram', $completeness->unfilled)" href="/edit?act=contacts">
|
||||
<img src="/assets/packages/static/openvk/img/icon2.gif" />
|
||||
Telegram (+10%)
|
||||
Telegram (+15%)
|
||||
</a>
|
||||
<a n:if="in_array('status', $completeness->unfilled)" href="/edit">
|
||||
<img src="/assets/packages/static/openvk/img/icon3.gif" />
|
||||
{_status} (+10%)
|
||||
{_status} (+15%)
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
<br />
|
||||
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && ($giftCount = $user->getGiftCount()) > 0">
|
||||
<div class="content_title_expanded" onclick="hidePanel(this, {$giftCount});">
|
||||
{_gifts}
|
||||
</div>
|
||||
<div>
|
||||
<div class="content_subtitle">
|
||||
{tr("gifts", $giftCount)}
|
||||
<div style="float:right;">
|
||||
<a href="/gifts{$user->getId()}">{_all_title}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ovk-avView">
|
||||
<div class="ovk-avView--el" n:foreach="$user->getGifts(1, 3) as $giftDescriptor">
|
||||
{var hideInfo = !is_null($thisUser) ? ($giftDescriptor->anon ? $thisUser->getId() !== $user->getId() : false) : false}
|
||||
|
||||
<a href="{$hideInfo ? 'javascript:false' : $giftDescriptor->sender->getURL()}">
|
||||
<img class="ava"
|
||||
src="{$giftDescriptor->gift->getImage(2)}"
|
||||
alt="{$hideInfo ? tr('gift') : ($giftDescriptor->caption ?? tr('gift'))}" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div n:if="$user->getFriendsCount() > 0 && $user->getPrivacyPermission('friends.read', $thisUser ?? NULL)">
|
||||
{var friendCount = $user->getFriendsCount()}
|
||||
|
||||
|
@ -192,37 +173,17 @@
|
|||
<a href="/friends{$user->getId()}">{_"all_title"}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ovk-avView">
|
||||
<div class="ovk-avView--el" n:foreach="$user->getFriends(1) as $friend">
|
||||
<a href="{$friend->getURL()}">
|
||||
<img class="ava" src="{$friend->getAvatarUrl()}" />
|
||||
</a>
|
||||
<br/>
|
||||
<a href="{$friend->getURL()}">{$friend->getFirstName()}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div n:if="$user->getFollowersCount() > 0">
|
||||
{var followersCount = $user->getFollowersCount()}
|
||||
|
||||
<div class="content_title_expanded" onclick="hidePanel(this, {$followersCount});">
|
||||
{_followers}
|
||||
</div>
|
||||
<div>
|
||||
<div class="content_subtitle">
|
||||
{tr("followers", $followersCount)}
|
||||
<div style="float:right;">
|
||||
<a href="/friends{$user->getId()}?act=incoming">{_"all_title"}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ovk-avView">
|
||||
<div class="ovk-avView--el" n:foreach="$user->getFollowers(1) as $follower">
|
||||
<a href="{$follower->getURL()}">
|
||||
<img class="ava" src="{$follower->getAvatarUrl()}" />
|
||||
</a>
|
||||
<br/>
|
||||
<a href="{$follower->getURL()}">{$follower->getFirstName()}</a>
|
||||
<div class="content_list">
|
||||
<div class="cl_element" n:foreach="$user->getFriends(1) as $friend">
|
||||
<div class="cl_avatar">
|
||||
<a href="{$friend->getURL()}">
|
||||
<img class="ava" src="{$friend->getAvatarUrl()}" />
|
||||
</a>
|
||||
</div>
|
||||
<a href="{$friend->getURL()}" class="cl_name">
|
||||
<text class="cl_fname">{$friend->getFirstName()}</text>
|
||||
<text class="cl_lname">{$friend->getLastName()}</text>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -398,10 +359,10 @@
|
|||
<td class="label"><span class="nobold">{_"politViews"}:</span></td>
|
||||
<td class="data">{var $pviews = $user->getPoliticalViews()}{_"politViews_$pviews"}</td>
|
||||
</tr>
|
||||
{if $user->getBirthday() > 0}
|
||||
{if $user->getBirthday()->timestamp() > 0}
|
||||
<tr>
|
||||
<td class="label"><span class="nobold">{_"birth_date"}:</span></td>
|
||||
<td class="data">{date('d F Y',$user->getBirthday())},
|
||||
<td class="data">{$user->getBirthday()->format('%e %B %Y')},
|
||||
{tr("years", $user->getAge())}</td>
|
||||
</tr>
|
||||
{/if}
|
||||
|
@ -504,6 +465,35 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && ($giftCount = $user->getGiftCount()) > 0">
|
||||
<div class="content_title_expanded" onclick="hidePanel(this, {$giftCount});">
|
||||
{_gifts}
|
||||
</div>
|
||||
<div>
|
||||
<div class="content_subtitle">
|
||||
{tr("gifts", $giftCount)}
|
||||
<div style="float:right;">
|
||||
{if OPENVK_ROOT_CONF['openvk']['preferences']['commerce']}
|
||||
<a href="/gifts?act=pick&user={$user->getId()}">{_send_gift}</a> |
|
||||
{/if}
|
||||
<a href="/gifts{$user->getId()}">{_all_title}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content_list long">
|
||||
<div class="cl_element" style="width: 25%;" n:foreach="$user->getGifts(1, 4) as $giftDescriptor">
|
||||
{var hideInfo = !is_null($thisUser) ? ($giftDescriptor->anon ? $thisUser->getId() !== $user->getId() : false) : false}
|
||||
<div class="cl_avatar">
|
||||
<a href="{$hideInfo ? 'javascript:false' : $giftDescriptor->sender->getURL()}">
|
||||
<img style="width: 70px; max-height: 70px;"
|
||||
src="{$giftDescriptor->gift->getImage(2)}"
|
||||
alt="{$hideInfo ? tr('gift') : ($giftDescriptor->caption ?? tr('gift'))}" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{presenter "openvk!Wall->wallEmbedded", $user->getId()}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<center>
|
||||
<img src="/assets/packages/static/openvk/img/oof.apng" alt="Пользователь заблокирован." style="width: 20%;" />
|
||||
<p>
|
||||
К сожалению, нам пришлось заблокировать страницу пользователя <b>{$user->getFirstName()}</b>.<br/>
|
||||
Комментарий модератора: <b>{$user->getBanReason()}</b>.
|
||||
{tr("user_banned", htmlentities($user->getFirstName()))|noescape}<br/>
|
||||
{_"user_banned_comment"} <b>{$user->getBanReason()}</b>.
|
||||
</p>
|
||||
</center>
|
||||
|
|
|
@ -46,4 +46,8 @@
|
|||
});
|
||||
</script>
|
||||
</center>
|
||||
|
||||
{if isset($thisUser) && $thisUser->hasMicroblogEnabled()}
|
||||
{script "js/al_comments.js"}
|
||||
{/if}
|
||||
{/block}
|
||||
|
|
|
@ -16,4 +16,8 @@
|
|||
{/foreach}
|
||||
{include "../components/paginator.xml", conf => $paginatorConf}
|
||||
</center>
|
||||
|
||||
{if isset($thisUser) && $thisUser->hasMicroblogEnabled()}
|
||||
{script "js/al_comments.js"}
|
||||
{/if}
|
||||
{/block}
|
||||
|
|
|
@ -13,24 +13,28 @@
|
|||
{/block}
|
||||
|
||||
{block content}
|
||||
<div class="content_divider">
|
||||
<div>
|
||||
<div n:if="$canPost" class="content_subtitle">
|
||||
{include "../components/textArea.xml", route => "/wall$owner/makePost"}
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
{if sizeof($posts) > 0}
|
||||
{foreach $posts as $post}
|
||||
<a name="postGarter={$post->getId()}"></a>
|
||||
|
||||
{include "../components/post.xml", post => $post, commentSection => true}
|
||||
{/foreach}
|
||||
{include "../components/paginator.xml", conf => $paginatorConf}
|
||||
{else}
|
||||
{_no_posts_abstract}
|
||||
{/if}
|
||||
<div class="content_divider">
|
||||
<div>
|
||||
<div n:if="$canPost" class="content_subtitle">
|
||||
{include "../components/textArea.xml", route => "/wall$owner/makePost"}
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
{if sizeof($posts) > 0}
|
||||
{foreach $posts as $post}
|
||||
<a name="postGarter={$post->getId()}"></a>
|
||||
|
||||
{include "../components/post.xml", post => $post, commentSection => true}
|
||||
{/foreach}
|
||||
{include "../components/paginator.xml", conf => $paginatorConf}
|
||||
{else}
|
||||
{_no_posts_abstract}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{if isset($thisUser) && $thisUser->hasMicroblogEnabled()}
|
||||
{script "js/al_comments.js"}
|
||||
{/if}
|
||||
{/block}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
{var author = $comment->getOwner()}
|
||||
{var $Club = openvk\Web\Models\Entities\Club::class}
|
||||
{var postId = $comment->getTarget() instanceof \openvk\Web\Models\Entities\Post ? $comment->getTarget()->getId() : NULL}
|
||||
|
||||
<a name="cid={$comment->getId()}"></a>
|
||||
<table border="0" style="font-size: 11px;" class="post comment" id="_comment{$comment->getId()}" data-comment-id="{$comment->getId()}" data-owner-id="{$author->getId()}" data-from-group="{$comment->getOwner() instanceof $Club}">
|
||||
<table border="0" style="font-size: 11px;" class="post comment" id="_comment{$comment->getId()}" data-comment-id="{$comment->getId()}" data-owner-id="{$author->getId()}" data-from-group="{$comment->getOwner() instanceof $Club}" n:attr="data-post-id => $postId">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="30" valign="top">
|
||||
|
@ -29,12 +30,10 @@
|
|||
</div>
|
||||
<div n:if="isset($thisUser) &&! ($compact ?? false)" class="post-menu">
|
||||
<a href="#_comment{$comment->getId()}" class="date">{$comment->getPublicationTime()}</a> |
|
||||
{var canDelete = $comment->getOwner()->getId() == $thisUser->getId()}
|
||||
{var canDelete = $canDelete || $comment->getTarget()->getOwner()->getId() == $thisUser->getId()}
|
||||
{if $canDelete}
|
||||
{if $comment->canBeDeletedBy($thisUser)}
|
||||
<a href="/comment{$comment->getId()}/delete">{_"delete"}</a> |
|
||||
{/if}
|
||||
<a class="comment-reply">Ответить</a>
|
||||
<a class="comment-reply">{_"reply"}</a>
|
||||
<div style="float: right; font-size: .7rem;">
|
||||
<a class="post-like-button" href="/comment{$comment->getId()}/like?hash={rawurlencode($csrfToken)}">
|
||||
<div class="heart" style="{if $comment->hasLikeFrom($thisUser)}opacity: 1;{else}opacity: 0.4;{/if}"></div>
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
<h4>{_"comments"} ({$count})</h4>
|
||||
<h4 n:if="$showTitle ?? true">{_"comments"} ({$count})</h4>
|
||||
|
||||
<div n:ifset="$thisUser">
|
||||
{var commentsURL = "/al_comments.pl/create/$model/" . $parent->getId()}
|
||||
{var club = $parent instanceof \openvk\Web\Models\Entities\Post && $parent->getTargetWall() < 0 ? (new openvk\Web\Models\Repositories\Clubs)->get(abs($parent->getTargetWall())) : NULL}
|
||||
{include "textArea.xml", route => $commentsURL, postOpts => false, graffiti => (bool) ovkGetQuirk("comments.allow-graffiti"), club => $club}
|
||||
{var club = $parent instanceof \openvk\Web\Models\Entities\Post && $parent->getTargetWall() < 0 ? (new openvk\Web\Models\Repositories\Clubs)->get(abs($parent->getTargetWall())) : $club}
|
||||
{if !$readOnly}
|
||||
{include "textArea.xml", route => $commentsURL, postOpts => false, graffiti => (bool) ovkGetQuirk("comments.allow-graffiti"), club => $club}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{if sizeof($comments) > 0}
|
||||
{foreach $comments as $comment}
|
||||
{include "comment.xml", comment => $comment}
|
||||
{/foreach}
|
||||
{include "paginator.xml", conf => (object) ["page" => $page, "count" => $count, "amount" => sizeof($comments), "perPage" => 10]}
|
||||
<div style="margin-top: 11px;">
|
||||
{include "paginator.xml", conf => (object) ["page" => $page, "count" => $count, "amount" => sizeof($comments), "perPage" => 10]}
|
||||
</div>
|
||||
{else}
|
||||
<!-- {if $model === "photos"}
|
||||
<p>Будьте первым, кто оставит комментарий к этой фотографии</p>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{extends "@default.xml"}
|
||||
{var post = $notification->getModel(0)}
|
||||
|
||||
{block under}
|
||||
{_nt_yours_adjective} <a href="/topic{$post->getPrettyId()}">{_nt_topic_instrumental}</a>
|
||||
{/block}
|
|
@ -0,0 +1,8 @@
|
|||
{var sender = $notification->getModel(1)}
|
||||
{var value = (int) explode(" ", $notification->getData(), 2)[0]}
|
||||
{var message = explode(" ", $notification->getData(), 2)[1]}
|
||||
|
||||
<a href="{$sender->getURL()}"><b>{$sender->getCanonicalName()}</b></a> {_transferred_to_you} {tr("points_amount", $value)}.
|
||||
{if !empty($message)}
|
||||
{_message}: "{$message}".
|
||||
{/if}
|
|
@ -10,7 +10,8 @@
|
|||
<td width="54" valign="top">
|
||||
<img
|
||||
src="{$author->getAvatarURL()}"
|
||||
width="{ifset $compact}25{else}50{/ifset}" />
|
||||
width="{ifset $compact}25{else}50{/ifset}"
|
||||
{ifset $compact}class="cCompactAvatars"{/ifset} />
|
||||
{if !$post->isPostedOnBehalfOfGroup() && !$compact}
|
||||
<span n:if="$author->isOnline()" class="post-online">
|
||||
{_online}
|
||||
|
@ -25,6 +26,22 @@
|
|||
</b>
|
||||
</a>
|
||||
{if $author->isVerified()}<img class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png">{/if}
|
||||
{if ($onWallOf ?? false) &&!$post->isPostedOnBehalfOfGroup() && $post->getOwnerPost() !== $post->getTargetWall()}
|
||||
{var wallId = $post->getTargetWall()}
|
||||
{var wallURL = $wallId > -1 ? "/id$wallId" : "/club" . abs($wallId)}
|
||||
на
|
||||
<a href="{$wallURL}">
|
||||
<b>
|
||||
{if isset($thisUser) && $thisUser->getId() === $wallId}
|
||||
вашей
|
||||
{/if}
|
||||
стене
|
||||
{if $wallId < 0}
|
||||
группы
|
||||
{/if}
|
||||
</b>
|
||||
</a>
|
||||
{/if}
|
||||
{ifset $compact}<br>
|
||||
<a href="/wall{$post->getPrettyId()}" class="date">
|
||||
{$post->getPublicationTime()}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</div>
|
||||
<div id="post-buttons{$textAreaId}" style="display: none;">
|
||||
<div class="post-upload">
|
||||
Вложение: <span>(unknown)</span>
|
||||
{_attachment}: <span>(unknown)</span>
|
||||
</div>
|
||||
<div n:if="$postOpts ?? true" class="post-opts">
|
||||
{var anonEnabled = OPENVK_ROOT_CONF['openvk']['preferences']['wall']['anonymousPosting']['enable']}
|
||||
|
@ -35,7 +35,7 @@
|
|||
{/if}
|
||||
|
||||
<label n:if="$anonEnabled" id="octoberAnonOpt">
|
||||
<input type="checkbox" name="anon" /> Анонимно
|
||||
<input type="checkbox" name="anon" /> {_"as_anonymous"}
|
||||
</label>
|
||||
|
||||
<label>
|
||||
|
@ -55,7 +55,7 @@
|
|||
<input type="submit" value="{_'write'}" class="button" />
|
||||
<div style="float: right; display: flex; flex-direction: column;">
|
||||
<a href="javascript:void(u('#post-buttons{$textAreaId} #wallAttachmentMenu').toggleClass('hidden'));">
|
||||
Прикрепить
|
||||
{_attach}
|
||||
</a>
|
||||
|
||||
<div id="wallAttachmentMenu" class="hidden">
|
||||
|
@ -65,11 +65,11 @@
|
|||
</a>
|
||||
<a href="javascript:void(document.querySelector('#post-buttons{$textAreaId} input[name=_vid_attachment]').click());">
|
||||
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-vnd.rn-realmedia.png" />
|
||||
Прикрепить видео
|
||||
{_attach_video}
|
||||
</a>
|
||||
<a n:if="$graffiti ?? false" href="javascript:initGraffiti({$textAreaId});">
|
||||
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/actions/draw-brush.png" />
|
||||
Нарисовать граффити
|
||||
{_draw_graffiti}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -25,3 +25,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{if isset($thisUser) && $thisUser->hasMicroblogEnabled()}
|
||||
{script "js/al_comments.js"}
|
||||
{/if}
|
||||
|
|
34
Web/Util/Telegram.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Util;
|
||||
use GuzzleHttp\Client as GuzzleClient;
|
||||
use GuzzleHttp\Exception\ClientException as GuzzleClientException;
|
||||
|
||||
class Telegram
|
||||
{
|
||||
static function send(string $to, string $text, bool $webPagePreview = false): bool
|
||||
{
|
||||
$conf = (object) OPENVK_ROOT_CONF["openvk"]["credentials"]["telegram"];
|
||||
if(!$conf->enable)
|
||||
return false;
|
||||
|
||||
try {
|
||||
(new GuzzleClient)->request(
|
||||
"POST",
|
||||
"https://api.telegram.org/bot{$conf->token}/sendMessage",
|
||||
[
|
||||
"form_params" => [
|
||||
"chat_id" => $to,
|
||||
"text" => $text,
|
||||
"disable_web_page_preview" => $webPagePreview ? "true" : "false",
|
||||
"parse_mode" => "HTML",
|
||||
]
|
||||
]
|
||||
);
|
||||
} catch (GuzzleClientException $ex) {
|
||||
trigger_error("Could not send Telegram message to $to: {$ex->getMessage()}", E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
26
Web/Util/Validator.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Util;
|
||||
use Chandler\Patterns\TSimpleSingleton;
|
||||
|
||||
class Validator
|
||||
{
|
||||
function emailValid(string $email): bool
|
||||
{
|
||||
if(empty($email)) return false;
|
||||
|
||||
$email = trim($email);
|
||||
[$user, $domain] = explode("@", $email);
|
||||
if(is_null($domain)) return false;
|
||||
if(iconv_strlen($user) > 64) return false;
|
||||
$domain = idn_to_ascii($domain) . ".";
|
||||
|
||||
return checkdnsrr($domain, "MX");
|
||||
}
|
||||
|
||||
function telegramValid(string $telegram): bool
|
||||
{
|
||||
return (bool) preg_match("/^(?:t.me\/|@)?([a-zA-Z0-9_]{0,32})$/", $telegram);
|
||||
}
|
||||
|
||||
use TSimpleSingleton;
|
||||
}
|
|
@ -20,6 +20,7 @@ services:
|
|||
- openvk\Web\Presenters\AdminPresenter
|
||||
- openvk\Web\Presenters\GiftsPresenter
|
||||
- openvk\Web\Presenters\MessengerPresenter
|
||||
- openvk\Web\Presenters\TopicsPresenter
|
||||
- openvk\Web\Presenters\ThemepacksPresenter
|
||||
- openvk\Web\Presenters\VKAPIPresenter
|
||||
- openvk\Web\Models\Repositories\Users
|
||||
|
@ -38,4 +39,5 @@ services:
|
|||
- openvk\Web\Models\Repositories\IPs
|
||||
- openvk\Web\Models\Repositories\Vouchers
|
||||
- openvk\Web\Models\Repositories\Gifts
|
||||
- openvk\Web\Models\Repositories\Topics
|
||||
- openvk\Web\Models\Repositories\ContentSearchRepository
|
||||
|
|
|
@ -17,6 +17,8 @@ routes:
|
|||
handler: "Support->AnswerTicket"
|
||||
- url: "/support/view/{num}"
|
||||
handler: "Support->view"
|
||||
- url: "/support/comment/{num}/rate/{num}"
|
||||
handler: "Support->rateAnswer"
|
||||
- url: "/al_comments.pl/create/support/{num}"
|
||||
handler: "Support->makeComment"
|
||||
- url: "/al_comments.pl/create/support/reply/{num}"
|
||||
|
@ -57,6 +59,8 @@ routes:
|
|||
handler: "User->twoFactorAuthSettings"
|
||||
- url: "/settings/2fa/disable"
|
||||
handler: "User->disableTwoFactorAuth"
|
||||
- url: "/coins_transfer"
|
||||
handler: "User->coinsTransfer"
|
||||
- url: "/id{num}"
|
||||
handler: "User->view"
|
||||
- url: "/friends{num}"
|
||||
|
@ -73,6 +77,8 @@ routes:
|
|||
handler: "Group->attend"
|
||||
- url: "/al_comments.pl/create/{text}/{num}"
|
||||
handler: "Comment->makeComment"
|
||||
- url: "/groups/{num}/setNewOwner/{num}"
|
||||
handler: "Group->changeOwner"
|
||||
- url: "/comment{num}/like"
|
||||
handler: "Comment->like"
|
||||
- url: "/comment{num}/delete"
|
||||
|
@ -163,6 +169,16 @@ routes:
|
|||
handler: "User->pinClub"
|
||||
- url: "/groups_create"
|
||||
handler: "Group->create"
|
||||
- url: "/board{num}"
|
||||
handler: "Topics->board"
|
||||
- url: "/board{num}/create"
|
||||
handler: "Topics->create"
|
||||
- url: "/topic{num}_{num}"
|
||||
handler: "Topics->topic"
|
||||
- url: "/topic{num}_{num}/edit"
|
||||
handler: "Topics->edit"
|
||||
- url: "/topic{num}_{num}/delete"
|
||||
handler: "Topics->delete"
|
||||
- url: "/audios{num}"
|
||||
handler: "Audios->app"
|
||||
- url: "/audios{num}.json"
|
||||
|
|
|
@ -19,7 +19,7 @@ span {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nobold {
|
||||
.nobold, nobold {
|
||||
font-weight: normal;
|
||||
color: gray;
|
||||
}
|
||||
|
@ -257,23 +257,29 @@ a {
|
|||
|
||||
.album-photo {
|
||||
position: relative;
|
||||
background-color: darkgrey;
|
||||
margin: 4pt;
|
||||
width: calc(33% - 10pt);
|
||||
height: 82px;
|
||||
width: 25%;
|
||||
max-height: 140px;
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
vertical-align: text-top;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.album-photo img {
|
||||
width: 100%;
|
||||
max-height: 82px;
|
||||
width: unset;
|
||||
max-height: 120px !important;
|
||||
max-width: 83%;
|
||||
vertical-align: top;
|
||||
border: 1px #ccc solid;
|
||||
padding: 8px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.album-photo > .album-photo--delete {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 5px;
|
||||
margin: 4px;
|
||||
color: #fff;
|
||||
|
@ -289,6 +295,11 @@ a {
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
.album-flex {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.name-checkmark {
|
||||
margin-left: 2pt;
|
||||
}
|
||||
|
@ -307,6 +318,10 @@ a {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.profile_link_form {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#profile_links {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
@ -315,6 +330,24 @@ a {
|
|||
background: #ECECEC;
|
||||
}
|
||||
|
||||
.action_links > .profile_link, .action_links > .profile_link_form > .profile_link {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.profile_link.disable > a, .profile_link.disable {
|
||||
cursor: not-allowed;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.profile_link.loading > a::after, .profile_link.loading::after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
background-image: url('/assets/packages/static/openvk/img/loading_mini.gif');
|
||||
width: 30px;
|
||||
height: 7px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.page_footer {
|
||||
margin-left: 95px;
|
||||
padding-top: 5px;
|
||||
|
@ -683,6 +716,44 @@ span {
|
|||
max-height: 63px;
|
||||
}
|
||||
|
||||
.content_list {
|
||||
display: flex;
|
||||
width: 200px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.content_list.long {
|
||||
width: 397px;
|
||||
}
|
||||
|
||||
.content_list .cl_element {
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
.content_list.long .cl_element {
|
||||
width: 16.5%;
|
||||
}
|
||||
|
||||
.content_list .cl_element .cl_avatar {
|
||||
padding: 7px 7px 0 7px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content_list .cl_element .cl_name {
|
||||
padding: 0 3px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.content_list .cl_element .cl_name .cl_lname {
|
||||
font-size: 7pt;
|
||||
}
|
||||
|
||||
.ava {
|
||||
width: 45px;
|
||||
}
|
||||
|
||||
table.User {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
@ -696,6 +767,10 @@ table.User {
|
|||
margin-bottom: -12px;
|
||||
}
|
||||
|
||||
.container_gray.bottom {
|
||||
border-bottom: #ebebeb solid 1px;
|
||||
}
|
||||
|
||||
#auth .container_gray {
|
||||
margin-left: -10px;
|
||||
margin-bottom: -10px;
|
||||
|
@ -847,7 +922,7 @@ table.User {
|
|||
}
|
||||
|
||||
.messenger-app--messages---message.unread {
|
||||
background-color: #dcdcdc;
|
||||
background-color: #ededed;
|
||||
padding: 5px;
|
||||
margin: -5px;
|
||||
padding-bottom: 1.2rem;
|
||||
|
@ -1598,3 +1673,98 @@ body.scrolled .toTop:hover {
|
|||
margin: 5px;
|
||||
border: 1px solid #C0CAD5;
|
||||
}
|
||||
|
||||
.header_search {
|
||||
background: #f7f7f7;
|
||||
width: 607px;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #e1e1e1;
|
||||
}
|
||||
|
||||
.header_search_inputbt {
|
||||
padding: 10px;
|
||||
border-top: 1px solid #ebebeb;
|
||||
display: flex;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.header_search_input {
|
||||
border: 1px solid #C0CAD5;
|
||||
padding: 3px;
|
||||
padding-left: 19px;
|
||||
font-size: 11px;
|
||||
font-family: tahoma, verdana, arial, sans-serif;
|
||||
width: 549px;
|
||||
background: #fff url('/assets/packages/static/openvk/img/search_icon.png') no-repeat;
|
||||
background-position-x: 4px;
|
||||
background-position-y: 4px;
|
||||
}
|
||||
|
||||
.button_search {
|
||||
border-radius: 2px;
|
||||
border: #595959;
|
||||
font-size: 11px;
|
||||
outline: none;
|
||||
white-space: nowrap;
|
||||
background: #595959;
|
||||
background-position: 0px -16px;
|
||||
color: #fff;
|
||||
padding: 4px 8px 4px;
|
||||
text-shadow: 0 1px 0 #686868;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
margin-left: 10px;
|
||||
width: 7.5%;
|
||||
}
|
||||
|
||||
.content_search {
|
||||
width: 607px;
|
||||
}
|
||||
|
||||
.content_search_list {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.content_search_list:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.content_search_list_ava img{
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
.content_search_list_ava{
|
||||
width: 85px;
|
||||
}
|
||||
|
||||
.content_search_list_name_h4 {
|
||||
color: #2b587a;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.content_search_list_span {
|
||||
color: #7b7b7b;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.pinned-mark {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
overflow: auto;
|
||||
background: url("/assets/packages/static/openvk/img/pin.png") no-repeat 0px 0px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.topic-list-item {
|
||||
border-bottom: #e6e6e6 solid 1px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.messagebox-content-header {
|
||||
background: #F7F7F7;
|
||||
margin: -20px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
|
BIN
Web/static/img/flags/ru_old.gif
Normal file
After Width: | Height: | Size: 560 B |
BIN
Web/static/img/loading_mini.gif
Executable file
After Width: | Height: | Size: 246 B |
|
@ -1 +0,0 @@
|
|||
Subproject commit 9d4eb793307e7af10c39c9da3b46646d97ad9dc2
|
|
@ -0,0 +1 @@
|
|||
system-run.png
|
|
@ -0,0 +1 @@
|
|||
run-build.png
|
|
@ -0,0 +1 @@
|
|||
appointment-new.png
|
BIN
Web/static/img/oxygen-icons/16x16/actions/acrobat.png
Normal file
After Width: | Height: | Size: 615 B |
|
@ -0,0 +1 @@
|
|||
folder-new.png
|
|
@ -0,0 +1 @@
|
|||
fork.png
|
1
Web/static/img/oxygen-icons/16x16/actions/actor.png
Normal file
|
@ -0,0 +1 @@
|
|||
im-user.png
|
|
@ -0,0 +1 @@
|
|||
flag-red.png
|
BIN
Web/static/img/oxygen-icons/16x16/actions/address-book-new.png
Normal file
After Width: | Height: | Size: 797 B |
|
@ -0,0 +1 @@
|
|||
address-book-new.png
|
|
@ -0,0 +1 @@
|
|||
address-book-new.png
|
|
@ -0,0 +1 @@
|
|||
go-home.png
|
|
@ -0,0 +1 @@
|
|||
folder-new.png
|
|
@ -0,0 +1 @@
|
|||
document-import.png
|
|
@ -0,0 +1 @@
|
|||
folder-new.png
|
|
@ -0,0 +1 @@
|
|||
configure.png
|
|
@ -0,0 +1 @@
|
|||
edit-delete.png
|
After Width: | Height: | Size: 384 B |
After Width: | Height: | Size: 441 B |
After Width: | Height: | Size: 454 B |
After Width: | Height: | Size: 456 B |
After Width: | Height: | Size: 444 B |
After Width: | Height: | Size: 457 B |
After Width: | Height: | Size: 418 B |
After Width: | Height: | Size: 445 B |
After Width: | Height: | Size: 399 B |