Merge branch 'master' of github.com:openvk/openvk

This commit is contained in:
veselcraft 2022-01-30 13:05:11 +03:00
commit 8a373faba0
No known key found for this signature in database
GPG key ID: AED66BC1AC628A4E
26 changed files with 277 additions and 464 deletions

View file

@ -31,17 +31,23 @@ If you want, you can add your instance to the list above so that people can regi
### Installation procedure ### Installation procedure
1. Install PHP 7.4, web-server, Composer, Node.js, Yarn and [Chandler](https://github.com/openvk/chandler) 1. Install PHP 7.4, web-server, Composer, Node.js, Yarn and [Chandler](https://github.com/openvk/chandler)
* PHP 8 has **not** yet been tested, so you should not expect it to work.
* 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: 2. Install [commitcaptcha](https://github.com/openvk/commitcaptcha) and OpenVK as Chandler extensions like this:
```
```bash
git clone https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk git clone https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk
git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha
``` ```
3. And enable them: 3. And enable them:
```
```bash
ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/extensions/enabled/ 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/ 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 4. Import `install/init-static-db.sql` to **same database** you installed Chandler to
5. Import `install/init-event-db.sql` to **separate database** 5. Import `install/init-event-db.sql` to **separate database**
6. Copy `openvk-example.yml` to `openvk.yml` and change options 6. Copy `openvk-example.yml` to `openvk.yml` and change options
@ -50,11 +56,12 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
9. Set `openvk` as your root app in `chandler.yml` 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): Once you are done, you can login as a system administrator on the network itself (no registration required):
* **Login**: `admin@localhost.localdomain6` * **Login**: `admin@localhost.localdomain6`
* **Password**: `admin` * **Password**: `admin`
* It is recommended to change the password before using the built-in account. * 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). Full example installation instruction for CentOS 8 is also available [here](https://docs.openvk.su/openvk_engine/centos8_installation/).
### If my website uses OpenVK, should I publish it's sources? ### If my website uses OpenVK, should I publish it's sources?
@ -65,6 +72,7 @@ You also not required to publish source texts of your themepacks and plugins.
## Where can I get assistance? ## Where can I get assistance?
You may reach out to us via: You may reach out to us via:
* [Bug-tracker](https://github.com/openvk/openvk/projects/1) * [Bug-tracker](https://github.com/openvk/openvk/projects/1)
* [Ticketing system](https://openvk.su/support?act=new) * [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. * Telegram chat: Go to [our channel](https://t.me/openvkch) and open discussion in our channel menu.

View file

@ -31,17 +31,23 @@ _[English](README.md)_
### Процедура установки ### Процедура установки
1. Установите PHP 7.4, веб-сервер, Composer, Node.js, Yarn и [Chandler](https://github.com/openvk/chandler) 1. Установите PHP 7.4, веб-сервер, Composer, Node.js, Yarn и [Chandler](https://github.com/openvk/chandler)
* PHP 8 еще **не** тестировался, поэтому не стоит ожидать, что он будет работать.
* PHP 8 еще **не** тестировался, поэтому не стоит ожидать, что он будет работать.
2. Установите [commitcaptcha](https://github.com/openvk/commitcaptcha) и OpenVK в качестве расширений Chandler следующим образом: 2. Установите [commitcaptcha](https://github.com/openvk/commitcaptcha) и OpenVK в качестве расширений Chandler следующим образом:
```
```bash
git clone https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk git clone https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk
git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha
``` ```
3. И включите их: 3. И включите их:
```
```bash
ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/extensions/enabled/ 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/ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/
``` ```
4. Импортируйте `install/init-static-db.sql` в **ту же базу данных**, в которую вы установили Chandler 4. Импортируйте `install/init-static-db.sql` в **ту же базу данных**, в которую вы установили Chandler
5. Импортируйте `install/init-event-db.sql` в **отдельную базу данных** 5. Импортируйте `install/init-event-db.sql` в **отдельную базу данных**
6. Скопируйте `openvk-example.yml` в `openvk.yml` и измените параметры 6. Скопируйте `openvk-example.yml` в `openvk.yml` и измените параметры
@ -50,11 +56,12 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
9. Установите `openvk` в качестве корневого приложения в файле `chandler.yml` 9. Установите `openvk` в качестве корневого приложения в файле `chandler.yml`
После этого вы можете войти как системный администратор в саму сеть (регистрация не требуется): После этого вы можете войти как системный администратор в саму сеть (регистрация не требуется):
* **Логин**: `admin@localhost.localdomain6` * **Логин**: `admin@localhost.localdomain6`
* **Пароль**: `admin` * **Пароль**: `admin`
* Перед использованием встроенной учетной записи рекомендуется сменить пароль. * Перед использованием встроенной учетной записи рекомендуется сменить пароль.
Полный пример инструкции по установке CentOS 8 также доступен [здесь](docs/centos8_install.md). Полный пример инструкции по установке CentOS 8 также доступен [здесь](https://docs.openvk.su/openvk_engine/centos8_installation/).
### Если мой сайт использует OpenVK, должен ли я публиковать его исходные тексты? ### Если мой сайт использует OpenVK, должен ли я публиковать его исходные тексты?
@ -65,6 +72,7 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
## Где я могу получить помощь? ## Где я могу получить помощь?
Вы можете связаться с нами через: Вы можете связаться с нами через:
* [Баг-трекер](https://github.com/openvk/openvk/projects/1) * [Баг-трекер](https://github.com/openvk/openvk/projects/1)
* [Помощь в OVK](https://openvk.su/support?act=new) * [Помощь в OVK](https://openvk.su/support?act=new)
* Telegram-чат: Перейдите на [наш канал](https://t.me/openvkch) и откройте обсуждение в меню нашего канала. * Telegram-чат: Перейдите на [наш канал](https://t.me/openvkch) и откройте обсуждение в меню нашего канала.

View file

@ -94,4 +94,9 @@ class Note extends Postable
return $cached; return $cached;
} }
function getSource(): string
{
return $this->getRecord()->source;
}
} }

View file

@ -127,5 +127,10 @@ class TicketComment extends RowModel
return $mark === 1; return $mark === 1;
} }
function isDeleted(): bool
{
return (bool) $this->getRecord()->deleted;
}
use Traits\TRichText; use Traits\TRichText;
} }

View file

@ -4,7 +4,6 @@ namespace openvk\Web\Models\Repositories;
// use openvk\Web\Models\Entities\User; // use openvk\Web\Models\Entities\User;
// use openvk\Web\Models\Repositories\Users; // use openvk\Web\Models\Repositories\Users;
use openvk\Web\Models\Entities\TicketComment; use openvk\Web\Models\Entities\TicketComment;
use Nette\Database\Table\ActiveRow;
use Chandler\Database\DatabaseConnection; use Chandler\Database\DatabaseConnection;
class TicketComments class TicketComments
@ -20,7 +19,7 @@ class TicketComments
function getCommentsById(int $ticket_id): \Traversable function getCommentsById(int $ticket_id): \Traversable
{ {
foreach($this->comments->where(['ticket_id' => $ticket_id]) as $comment) yield new TicketComment($comment); foreach($this->comments->where(['ticket_id' => $ticket_id, 'deleted' => 0]) as $comment) yield new TicketComment($comment);
} }
// private function toTicket(?ActiveRow $ar): ?Ticket // private function toTicket(?ActiveRow $ar): ?Ticket

View file

@ -39,7 +39,7 @@ class Users
function find(string $query): Util\EntityStream function find(string $query): Util\EntityStream
{ {
$query = "%$query%"; $query = "%$query%";
$result = $this->users->where("CONCAT_WS(' ', first_name, last_name) LIKE ?", $query); $result = $this->users->where("CONCAT_WS(' ', first_name, last_name) LIKE ?", $query)->where("deleted", 0);
return new Util\EntityStream("User", $result); return new Util\EntityStream("User", $result);
} }

View file

@ -63,7 +63,7 @@ final class AboutPresenter extends OpenVKPresenter
$this->template->usersStats = (new Users)->getStatistics(); $this->template->usersStats = (new Users)->getStatistics();
$this->template->clubsCount = (new Clubs)->getCount(); $this->template->clubsCount = (new Clubs)->getCount();
$this->template->postsCount = (new Posts)->getCount(); $this->template->postsCount = (new Posts)->getCount();
$this->template->popularClubs = (new Clubs)->getPopularClubs(); $this->template->popularClubs = iterator_to_array((new Clubs)->getPopularClubs());
$this->template->admins = iterator_to_array((new Users)->getInstanceAdmins()); $this->template->admins = iterator_to_array((new Users)->getInstanceAdmins());
} }

View file

@ -126,6 +126,10 @@ final class AuthPresenter extends OpenVKPresenter
if(!$this->authenticator->verifyCredentials($user->id, $this->postParam("password"))) if(!$this->authenticator->verifyCredentials($user->id, $this->postParam("password")))
$this->flashFail("err", tr("login_failed"), tr("invalid_username_or_password")); $this->flashFail("err", tr("login_failed"), tr("invalid_username_or_password"));
$ovkUser = new User($user->related("profiles.user")->fetch());
if($ovkUser->isDeleted())
$this->flashFail("err", tr("login_failed"), tr("invalid_username_or_password"));
$secret = $user->related("profiles.user")->fetch()["2fa_secret"]; $secret = $user->related("profiles.user")->fetch()["2fa_secret"];
$code = $this->postParam("code"); $code = $this->postParam("code");
if(!is_null($secret)) { if(!is_null($secret)) {
@ -136,7 +140,6 @@ final class AuthPresenter extends OpenVKPresenter
if(is_null($code)) if(is_null($code))
return; return;
$ovkUser = new User($user->related("profiles.user")->fetch());
if(!($code === (new Totp)->GenerateToken(Base32::decode($secret)) || $ovkUser->use2faBackupCode((int) $code))) { if(!($code === (new Totp)->GenerateToken(Base32::decode($secret)) || $ovkUser->use2faBackupCode((int) $code))) {
$this->flash("err", tr("login_failed"), tr("incorrect_2fa_code")); $this->flash("err", tr("login_failed"), tr("incorrect_2fa_code"));
return; return;
@ -229,7 +232,7 @@ final class AuthPresenter extends OpenVKPresenter
} }
$user = $this->users->getByChandlerUser(new ChandlerUser($uRow)); $user = $this->users->getByChandlerUser(new ChandlerUser($uRow));
if(!$user) if(!$user || $user->isDeleted())
$this->flashFail("err", tr("error"), tr("password_reset_error")); $this->flashFail("err", tr("error"), tr("password_reset_error"));
$request = $this->restores->getLatestByUser($user); $request = $this->restores->getLatestByUser($user);

View file

@ -207,6 +207,7 @@ final class GroupPresenter extends OpenVKPresenter
$club->setShortcode(empty($this->postParam("shortcode")) ? NULL : $this->postParam("shortcode")); $club->setShortcode(empty($this->postParam("shortcode")) ? NULL : $this->postParam("shortcode"));
$club->setWall(empty($this->postParam("wall")) ? 0 : 1); $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->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); $club->setDisplay_Topics_Above_Wall(empty($this->postParam("display_topics_above_wall")) ? 0 : 1);
$club->setHide_From_Global_Feed(empty($this->postParam("hide_from_global_feed")) ? 0 : 1); $club->setHide_From_Global_Feed(empty($this->postParam("hide_from_global_feed")) ? 0 : 1);

View file

@ -67,6 +67,35 @@ final class NotesPresenter extends OpenVKPresenter
$note->setCreated(time()); $note->setCreated(time());
$note->setName($this->postParam("name")); $note->setName($this->postParam("name"));
$note->setSource($this->postParam("html")); $note->setSource($this->postParam("html"));
$note->setEdited(time());
$note->save();
$this->redirect("/note" . $this->user->id . "_" . $note->getVirtualId());
}
}
function renderEdit(int $owner, int $note_id): void
{
$this->assertUserLoggedIn();
$this->willExecuteWriteAction();
$note = $this->notes->getNoteById($owner, $note_id);
if(!$note || $note->getOwner()->getId() !== $owner || $note->isDeleted())
$this->notFound();
if(is_null($this->user) || !$note->canBeModifiedBy($this->user->identity))
$this->flashFail("err", "Ошибка доступа", "Недостаточно прав для модификации данного ресурса.");
$this->template->note = $note;
if($_SERVER["REQUEST_METHOD"] === "POST") {
if(empty($this->postParam("name"))) {
$this->flashFail("err", tr("error"), tr("error_segmentation"));
}
$note->setName($this->postParam("name"));
$note->setSource($this->postParam("html"));
$note->setCached_Content(NULL);
$note->setEdited(time());
$note->save(); $note->save();
$this->redirect("/note" . $this->user->id . "_" . $note->getVirtualId()); $this->redirect("/note" . $this->user->id . "_" . $note->getVirtualId());

View file

@ -211,11 +211,19 @@ abstract class OpenVKPresenter extends SimplePresenter
$this->template->thisUser = $this->user->identity; $this->template->thisUser = $this->user->identity;
$this->template->userTainted = $user->isTainted(); $this->template->userTainted = $user->isTainted();
if($this->user->identity->isDeleted()) {
Authenticator::i()->logout();
Session::i()->set("_su", NULL);
$this->flashFail("err", tr("error"), tr("profile_not_found"));
$this->redirect("/", static::REDIRECT_TEMPORARY);
}
if($this->user->identity->isBanned() && !$this->banTolerant) { if($this->user->identity->isBanned() && !$this->banTolerant) {
header("HTTP/1.1 403 Forbidden"); header("HTTP/1.1 403 Forbidden");
$this->getTemplatingEngine()->render(__DIR__ . "/templates/@banned.xml", [ $this->getTemplatingEngine()->render(__DIR__ . "/templates/@banned.xml", [
"thisUser" => $this->user->identity, "thisUser" => $this->user->identity,
"csrfToken" => $GLOBALS["csrfToken"], "csrfToken" => $GLOBALS["csrfToken"],
"isTimezoned" => Session::i()->get("_timezoneOffset"),
]); ]);
exit; exit;
} }

View file

@ -226,6 +226,29 @@ final class SupportPresenter extends OpenVKPresenter
$this->template->content = $parser->text($content); $this->template->content = $parser->text($content);
} }
function renderDeleteComment(int $id): void
{
$this->assertUserLoggedIn();
$this->assertNoCSRF();
$comment = $this->comments->get($id);
if(is_null($comment))
$this->notFound();
$ticket = $comment->getTicket();
if($ticket->isDeleted())
$this->notFound();
if(!($ticket->getUserId() === $this->user->id && $comment->getUType() === 0))
$this->assertPermission("openvk\Web\Models\Entities\TicketReply", "write", 0);
$this->willExecuteWriteAction();
$comment->delete();
$this->flashFail("succ", tr("ticket_changed"), tr("ticket_changed_comment"));
}
function renderRateAnswer(int $id, int $mark): void function renderRateAnswer(int $id, int $mark): void
{ {
$this->willExecuteWriteAction(); $this->willExecuteWriteAction();

View file

@ -190,7 +190,11 @@
<img src="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['src']}" alt="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['caption']}" class="psa-poster" style="max-width: 100%; margin-top: 50px;" /> <img src="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['src']}" alt="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['caption']}" class="psa-poster" style="max-width: 100%; margin-top: 50px;" />
</a> </a>
{else} {else}
<a href="/support" class="link">{_menu_support}</a> <a href="/support" class="link">{_menu_support}
{if $ticketAnsweredCount > 0}
(<b>{$ticketAnsweredCount}</b>)
{/if}
</a>
<a href="/logout?hash={urlencode($csrfToken)}" class="link">{_menu_logout}</a> <a href="/logout?hash={urlencode($csrfToken)}" class="link">{_menu_logout}</a>
{/if} {/if}
{else} {else}

View file

@ -42,6 +42,7 @@
</tbody> </tbody>
</table> </table>
{if sizeof($popularClubs) !== 0}
<h4>{_most_popular_groups}</h4> <h4>{_most_popular_groups}</h4>
<ol> <ol>
<li n:foreach="$popularClubs as $entry" style="margin-top: 5px;"> <li n:foreach="$popularClubs as $entry" style="margin-top: 5px;">
@ -51,6 +52,7 @@
</div> </div>
</li> </li>
</ol> </ol>
{/if}
<h4>{_rules}</h4> <h4>{_rules}</h4>
<div style="margin-top: 16px;"> <div style="margin-top: 16px;">

View file

@ -0,0 +1,49 @@
{extends "../@layout.xml"}
{block title}{_edit_note}{/block}
{block header}
{var author = $note->getOwner()}
<a href="{$author->getURL()}">{$author->getCanonicalName()}</a>
»
<a href="/notes{$author->getId()}">{_notes}</a>
»
<a href="/note{$author->getId()}_{$note->getVirtualId()}">{$note->getName()}</a>
{/block}
{block content}
<form id="noteFactory" method="POST">
<input type="text" name="name" placeholder="{_name_note}" style="width:603px;" value="{$note->getName()}" />
<br/><br/>
<textarea name="html" style="display:none;"></textarea>
<div id="editor" style="width:600px;height:300px;border:1px solid grey"></div>
<p><i><a href="/kb/notes">Кое-что</a> из (X)HTML поддерживается.</i></p>
<input type="hidden" name="hash" value="{$csrfToken}" />
<button class="button">{_save}</button>
&nbsp;
<a href="/note{$note->getOwner()->getId()}_{$note->getVirtualId()}" class="button">{_cancel}</a>
</form>
{script "js/node_modules/monaco-editor/min/vs/loader.js"}
{script "js/node_modules/requirejs/bin/r.js"}
<script>
require.config({
paths: {
'vs': '/assets/packages/static/openvk/js/node_modules/monaco-editor/min/vs'
}
});
require(['vs/editor/editor.main'], function() {
window._editor = monaco.editor.create(document.getElementById('editor'), {
value: {$note->getSource()},
lineNumbers: "off",
language: "html"
});
});
document.querySelector("#noteFactory").addEventListener("submit", function() {
document.querySelector("textarea").value = window._editor.getValue();
});
</script>
{/block}

View file

@ -35,7 +35,7 @@
} }
</style> </style>
<article id="userContent" style="min-height: 300pt;margin: 10px;"> <article id="userContent" style="margin: 10px 10px 0;">
<div class="note_header"> <div class="note_header">
<div class="note_title"> <div class="note_title">
<div class="note_title"> <div class="note_title">
@ -44,27 +44,35 @@
</div> </div>
<div class="byline"> <div class="byline">
<span><a href="{$author->getURL()}">{$author->getCanonicalName()}</a></span> {$note->getPublicationTime()} <span><a href="{$author->getURL()}">{$author->getCanonicalName()}</a></span> {$note->getPublicationTime()}
<span n:if="$note->getEditTime() > $note->getPublicationTime()">({_edited} {$note->getEditTime()})</span>
</div> </div>
</div> </div>
<div style="margin-left: 6px;"> <div style="margin-left: 6px; width: 535px;">
{$note->getText()|noescape} {$note->getText()|noescape}
</div> </div>
</article> </article>
<div style="width: 100%; min-height: 100px;"> <div class="note_footer" style="margin: 10px 10px 0;">
<div style="float: left; min-height: 100px; width: 70%;"> <div class="comments_count">
{if sizeof($comments) > 0}
{_comments} ({$note->getCommentsCount()})
{else}
{_no_comments}
{/if}
<span n:if="isset($thisUser) && $thisUser->getId() === $note->getOwner()->getId()">&nbsp;|&nbsp;
<a id="_noteDelete" href="/note{$note->getOwner()->getId()}_{$note->getId()}/delete">{_delete}</a>
&nbsp;|&nbsp;
<a href="/note{$note->getOwner()->getId()}_{$note->getVirtualId()}/edit">{_edit}</a>
</span>
</div>
</div>
<div style="margin: 6px 10px 10px;border-top: 1px solid #ddd;">
{include "../components/comments.xml", {include "../components/comments.xml",
comments => $comments, comments => $comments,
count => $cCount, count => $cCount,
page => $cPage, page => $cPage,
model => "notes", model => "notes",
parent => $note} parent => $note,
</div> showTitle => false}
<div style="float: right; min-height: 100px; width: 30%;">
<h4>{_actions}</h4>
<div n:if="isset($thisUser) && $thisUser->getId() === $note->getOwner()->getId()">
<a id="_noteDelete" href="/note{$note->getOwner()->getId()}_{$note->getId()}/delete" class="profile_link" style="display:block;width:96%;">{_delete}</a>
</div>
</div>
</div> </div>
{/block} {/block}

View file

@ -28,11 +28,9 @@
{var cover = $x->getCoverPhoto()} {var cover = $x->getCoverPhoto()}
{var preview = is_null($cover) ? "/assets/packages/static/openvk/img/camera_200.png" : $cover->getURL()} {var preview = is_null($cover) ? "/assets/packages/static/openvk/img/camera_200.png" : $cover->getURL()}
<center style="height: 54pt; width: 140px;">
<a href="/album{$x->getPrettyId()}"> <a href="/album{$x->getPrettyId()}">
<img src="{$preview}" alt="{$x->getName()}" style="max-height: 100%; max-width: 100%;" /> <img src="{$preview}" alt="{$x->getName()}" style="height: 130px; width: 170px; object-fit: cover" />
</a> </a>
</center>
{/block} {/block}
{block name} {block name}
@ -40,8 +38,8 @@
{/block} {/block}
{block description} {block description}
<span>{$x->getDescription() ?? $x->getName()}</span><br/> <span>{$x->getDescription() ?? $x->getName()}</span><br />
<span style="color: grey;">{$x->getPhotosCount()} фотографий</span><br/> <span style="color: grey;">{$x->getPhotosCount()} фотографий</span><br />
<span style="color: grey;">Создан {$x->getCreationTime()}</span><br/> <span style="color: grey;">Обновлен {$x->getEditTime() ?? $x->getCreationTime()}</span><br />
<span style="color: grey;">Изменён {$x->getEditTime() ?? $x->getCreationTime()}</span> <span style="color: grey;">Создан {$x->getCreationTime()}</span><br />
{/block} {/block}

View file

@ -101,11 +101,9 @@
{/if} {/if}
</div> </div>
{if $comment->getUType() === 0}
<div class="post-menu"> <div class="post-menu">
<a href="/support/comment/{$comment->getId()}/delete">{_delete}</a> <a href="/support/comment/{$comment->getId()}/delete?hash={urlencode($csrfToken)}">{_delete}</a>
</div> </div>
{/if}
{if $comment->getUType() === 1 && !is_null($comment->isLikedByUser())} {if $comment->getUType() === 1 && !is_null($comment->isLikedByUser())}
<div class="post-menu"> <div class="post-menu">

View file

@ -103,7 +103,7 @@
{if $comment->getUType() === 0} {if $comment->getUType() === 0}
<div class="post-menu"> <div class="post-menu">
<a href="/support/comment/{$comment->getId()}/delete">{_delete}</a> <a href="/support/comment/{$comment->getId()}/delete?hash={urlencode($csrfToken)}">{_delete}</a>
</div> </div>
{/if} {/if}

View file

@ -25,6 +25,8 @@ routes:
handler: "Support->makeComment" handler: "Support->makeComment"
- url: "/al_comments/create/support/reply/{num}" - url: "/al_comments/create/support/reply/{num}"
handler: "Support->AnswerTicketReply" handler: "Support->AnswerTicketReply"
- url: "/support/comment/{num}/delete"
handler: "Support->deleteComment"
- url: "/al_comments/create/{text}/{num}" - url: "/al_comments/create/{text}/{num}"
handler: "Comment->makeComment" handler: "Comment->makeComment"
- url: "/support/delete/{num}" - url: "/support/delete/{num}"
@ -223,6 +225,8 @@ routes:
handler: "Notes->view" handler: "Notes->view"
- url: "/notes/create" - url: "/notes/create"
handler: "Notes->create" handler: "Notes->create"
- url: "/note{num}_{num}/edit"
handler: "Notes->edit"
- url: "/note{num}_{num}/delete" - url: "/note{num}_{num}/delete"
handler: "Notes->delete" handler: "Notes->delete"
- url: "/invite" - url: "/invite"

View file

@ -214,7 +214,7 @@ a {
} }
.page_yellowheader a { .page_yellowheader a {
color: #C8BF85; color: #696029;
} }
.page_content { .page_content {
@ -1581,11 +1581,10 @@ body.scrolled .toTop:hover {
} }
.postFeedWrapper { .postFeedWrapper {
margin: -10px;
width: 611px;
padding: 4px 8px; padding: 4px 8px;
background-color: rgb(240, 240, 240); background-color: rgb(240, 240, 240);
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
border-top: 1px solid #ccc;
} }
.user-alert { .user-alert {
@ -1786,38 +1785,49 @@ body.scrolled .toTop:hover {
.hover-box:hover { .hover-box:hover {
background-color: #C0CAD5; background-color: #C0CAD5;
} }
.summaryBar { .summaryBar {
border-bottom: 1px solid #DAE2E8; border-bottom: 1px solid #DAE2E8;
clear: both; clear: both;
padding: 0 10px; padding: 11px 10px;
padding-top: 11px;
padding-top: 11px;
color: black; color: black;
font-weight: normal; font-weight: normal;
line-height: normal; line-height: normal;
margin-left: -12px; margin-left: -12px;
margin-right: -12px; margin-right: -12px;
} }
.summaryBar .summary { .summaryBar .summary {
color: #45688E; color: #45688E;
font-weight: bold; font-weight: bold;
padding-top: 3px; padding-top: 3px;
padding-bottom: 4px; padding-bottom: 4px;
display: inline-block;
} }
.note_header { .note_header {
background: #f7f7f7; background: #f7f7f7;
border-bottom: solid 1px #DAE1E8; border-bottom: solid 1px #DAE1E8;
border-top: solid 1px #45688E; border-top: solid 1px #45688E;
padding: 4px 6px 5px 6px; padding: 4px 6px 5px 6px;
} }
.note_header .note_title { .note_header .note_title {
color: #45688E; color: #45688E;
font-size: 13px; font-size: 13px;
font-weight: bold; font-weight: bold;
line-height: 15px; line-height: 15px;
margin: 0px; margin: 0;
padding: 0px 0px 1px 0px; padding: 0 0 1px 0;
} }
.page_yellowheader a {
color: #696029; .note_footer {
border-top: 1px solid #ddd;
clear: both;
margin-top: 10px;
padding: 0px 2px 0px 6px;
}
.comments_count {
padding: 5px 0px 0px 0px;
} }

View file

@ -1,357 +0,0 @@
# Installing OpenVK
Based on [@rem-pai](https://github.com/rem-pai)'s way to install OpenVK modified using my experience.
!!WARNING!!
CentOS 8 is reaching it's end-of-life soon. There are other supported similar distributions like Rocky Linux or AlmaLinux.
## SELinux
🖥Run the command:
```bash
sestatus
```
If it says `SELinux status: enabled` then SELinux will disturb us. Let's disable it.
_Note: I know that it's not most secured solution but I don't know any proper way that will work._
📝Edit file `/etc/sysconfig/selinux` and change the line `SELinux=enforcing` to `SELinux=disabled`, then 🔌reboot your machine. `sestatus` should tell `SELinux status: disabled` right now.
## Dependencies
🖥Let's install EPEL and Remi repos for PHP 7.4:
```bash
dnf -y install epel-release
dnf -y install https://rpms.remirepo.net/enterprise/remi-release-8.rpm
```
🖥Then enable modules that we need:
```bash
dnf -y module enable php:remi-7.4
dnf -y module enable nodejs:14
```
🖥And install dependencies:
```bash
dnf -y install php php-cli php-common unzip php-zip php-yaml php-gd php-pdo_mysql nodejs git
```
🖥Don't forget about Yarn and Composer:
```bash
npm i -g yarn
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php --filename=composer2 --install-dir=/bin --snapshot
rm composer-setup.php
```
### Database
🖥We will use Percona Server for DB:
```bash
dnf -y install https://repo.percona.com/yum/percona-release-latest.noarch.rpm
percona-release setup -y ps80
dnf -y install percona-server-server percona-toolkit
systemctl start mysql
```
🖥And then look up for temporary password:
```bash
cat /var/log/mysqld.log | grep password
```
It should look like this:
2021-01-11T12:56:09.203991Z 6 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: >b?Q.fDXJ4fk
🖥Then run `mysql_secure_installation`, set new password and answer like this:
Change the password for root ? ((Press y|Y for Yes, any other key for No) : n
Remove anonymous users? (Press y|Y for Yes, any other key for No) : y
Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y
Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y
Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
### ffmpeg
Additionally, you can install ffmpeg for processing videos.
🖥You will need to use RPMFusion repo to install it:
```bash
dnf -y localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpm
dnf -y install --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpm
```
🖥Then install SDL2 and ffmpeg:
```bash
dnf -y install http://rpmfind.net/linux/epel/7/x86_64/Packages/s/SDL2-2.0.10-1.el7.x86_64.rpm
dnf -y install ffmpeg
```
## Chandler and OpenVK installation
🖥Install Chandler in `/opt`:
```bash
cd /opt
git clone https://github.com/openvk/chandler.git
cd chandler/
composer2 install
```
🖥You will need a secret key. You can generate it using:
```bash
cat /dev/random | tr -dc 'a-z0-9' | fold -w 128 | head -n 1
```
📝Now edit config file `chandler-example.yml` like this:
```yaml
chandler:
debug: true
websiteUrl: null
rootApp: "openvk"
preferences:
appendExtension: "xhtml"
adminUrl: "/chandlerd"
exposeChandler: true
database:
dsn: "mysql:unix_socket=/var/lib/mysql/mysql.sock;dbname=db"
user: "root"
password: "DATABASE_PASSWORD"
security:
secret: "SECRET_KEY_HERE"
csrfProtection: "permissive"
sessionDuration: 14
```
🖥And rename it to `chandler.yml`:
```bash
mv chandler-example.yml chandler.yml
```
🖥Now let's install CommitCaptcha extension. It is mandatory for OpenVK.
```bash
cd extensions/available/
git clone https://github.com/openvk/commitcaptcha.git
cd commitcaptcha/
composer2 install
```
🖥And now download OpenVK:
```bash
cd ..
git clone https://github.com/openvk/openvk.git
cd openvk/
composer2 install
cd Web/static/js
yarn install
```
📝Now edit config file `openvk-example.yml` like this:
```yaml
openvk:
debug: true
appearance:
name: "OpenVK"
motd: "Yet another OpenVK instance"
preferences:
femaleGenderPriority: true
uploads:
disableLargeUploads: false
mode: "basic"
shortcodes:
forbiddenNames:
- "index.php"
security:
requireEmail: false
requirePhone: false
forcePhoneVerification: false
forceEmailVerification: false
enableSu: true
rateLimits:
actions: 5
time: 20
maxViolations: 50
maxViolationsAge: 120
autoban: true
support:
supportName: "Moderator"
adminAccount: 1 # Change this ok
messages:
strict: false
wall:
postSizes:
maxSize: 60000
processingLimit: 3000
emojiProcessingLimit: 1000
menu:
links:
adPoster:
enable: false
src: "https://example.org/ad_poster.jpeg"
caption: "Ad caption"
link: "https://example.org/product.aspx?id=10&from=ovk"
bellsAndWhistles:
fartscroll: false
testLabel: false
credentials:
smsc:
enable: false
client: ""
secret: ""
eventDB:
enable: true # Better enable this
database:
dsn: "mysql:unix_socket=/var/lib/mysql/mysql.sock;dbname=openvk_eventdb"
user: "root"
password: "DATABASE_PASSWORD"
```
Please note `eventDB` section because it's better to enable event database.
🖥And rename it to `openvk.yml`:
```bash
mv openvk-example.yml openvk.yml
```
🖥Then enable CommitCaptcha and OpenVK for Chandler:
```bash
ln -s /opt/chandler/extensions/available/commitcaptcha/ /opt/chandler/extensions/enabled/commitcaptcha
ln -s /opt/chandler/extensions/available/openvk/ /opt/chandler/extensions/enabled/openvk
```
### DB configuration
_Note: it's better to create another user for SQL but I won't cover that._
🖥Enter MySQL shell:
```bash
mysql -p
```
🖥And create main and event databases:
```sql
CREATE DATABASE openvk;
CREATE DATABASE openvk_eventdb;
exit
```
🖥Go to `/opt/chandler`:
```bash
cd /opt/chandler
```
📝We need to import Chandler database but for some reason it's not ready for use so we need to edit dump `install/init-db.sql`:
1\. Remove ` PAGE_CHECKSUM=1` everywhere.
2\. Replace `Aria` with `InnoDB` everywhere.
🖥Now database dump can be imported:
```bash
mysql -p'DATABASE_PASSWORD' openvk < install/init-db.sql
```
🖥Go to `extensions/available/openvk/`:
```bash
cd extensions/available/openvk/
```
📝We also need to import OpenVK database. Unless you use MariaDB (we are using Percona here) you should edit `install/init-static-db.sql`:
1\. Replace `utf8mb4_unicode_nopad_ci` with `utf8mb4_unicode_520_ci` everywhere.
🖥Now database dump can be imported:
```bash
mysql -p'DATABASE_PASSWORD' openvk < install/init-static-db.sql
```
🖥Also import event database:
```bash
mysql -p'DATABASE_PASSWORD' openvk_eventdb < install/init-event-db.sql
```
### Webserver configuration
Apache is already installed so we will use it.
🖥Make the user `apache` owner of the `chandler` folder:
```bash
cd /opt
chown -R apache: chandler/
```
📝Now let's create config file `/etc/httpd/conf.d/10-openvk.conf`:
```apache
<VirtualHost *:80>
ServerName openvk.local
DocumentRoot /opt/chandler/htdocs
<Directory /opt/chandler/htdocs>
AllowOverride All
Require all granted
</Directory>
ErrorLog /var/log/openvk/error.log
CustomLog /var/log/openvk/access.log combinedio
</VirtualHost>
```
📝Also enable rewrite_module by creating `/etc/httpd/conf.modules.d/02-rewrite.conf`:
```apache
LoadModule rewrite_module modules/mod_rewrite.so
```
🖥Make directory for OpenVK logs and make the user `apache` owner of it:
```bash
mkdir /var/log/openvk
chown apache: /var/log/openvk/
```
🖥Make the firewall exception for port 80:
```bash
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --reload
```
🖥And start Apache:
```bash
systemctl start httpd
```
OpenVK should work right now!
Also you can raise 2MB the file limit through editing `/etc/php.ini`. And it's also better to install PHPMyAdmin but I won't cover that.

View file

@ -1 +1,3 @@
--- if you haven't used helpdesk before nov 25, 2021 - you will not need it.
UPDATE `tickets_comments` SET `text`=REGEXP_REPLACE(`text`, "(?:Здравствуйте, [^!]*!<br><\/br>|<br><\/br>С уважением,<br\/> Команда поддержки OpenVK.)", "") WHERE 1=1; UPDATE `tickets_comments` SET `text`=REGEXP_REPLACE(`text`, "(?:Здравствуйте, [^!]*!<br><\/br>|<br><\/br>С уважением,<br\/> Команда поддержки OpenVK.)", "") WHERE 1=1;

View file

@ -1,5 +1,5 @@
# Locales for OpenVK # Locales for OpenVK
So, there is a locales contained here for [OpenVK](../../../openvk). So, there is a locales contained here for [OpenVK](../../../).
## Contributing ## Contributing

View file

@ -295,8 +295,11 @@
"name_note" = "Title"; "name_note" = "Title";
"text_note" = "Content"; "text_note" = "Content";
"create_note" = "Create note"; "create_note" = "Create note";
"edit_note" = "Edit note";
"actions" = "Actions"; "actions" = "Actions";
"edited" = "Edited";
"notes_zero" = "No notes"; "notes_zero" = "No notes";
"notes_one" = "$1 note"; "notes_one" = "$1 note";
"notes_other" = "$1 notes"; "notes_other" = "$1 notes";

View file

@ -313,8 +313,11 @@
"name_note" = "Название"; "name_note" = "Название";
"text_note" = "Содержание"; "text_note" = "Содержание";
"create_note" = "Создать заметку"; "create_note" = "Создать заметку";
"edit_note" = "Редактировать заметку";
"actions" = "Действия"; "actions" = "Действия";
"edited" = "Отредактировано";
"notes_zero" = "Ни одной заметки"; "notes_zero" = "Ни одной заметки";
"notes_one" = "Одна заметка"; "notes_one" = "Одна заметка";
"notes_few" = "$1 заметки"; "notes_few" = "$1 заметки";