mirror of
https://github.com/openvk/openvk
synced 2024-11-14 19:19:14 +03:00
FAQ в Поддержке
This commit is contained in:
parent
a2384cc231
commit
047c0922fc
16 changed files with 780 additions and 2 deletions
44
Web/Models/Entities/FAQArticle.php
Normal file
44
Web/Models/Entities/FAQArticle.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use openvk\Web\Models\Repositories\FAQCategories;
|
||||
use openvk\Web\Models\RowModel;
|
||||
|
||||
class FAQArticle extends RowModel
|
||||
{
|
||||
protected $tableName = "faq_articles";
|
||||
|
||||
function getId(): int
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getTitle(): string
|
||||
{
|
||||
return $this->getRecord()->title;
|
||||
}
|
||||
|
||||
function getText(): string
|
||||
{
|
||||
return $this->getRecord()->text;
|
||||
}
|
||||
|
||||
function canSeeByUnloggedUsers(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->unlogged_can_see;
|
||||
}
|
||||
|
||||
function canSeeByUsers(): bool
|
||||
{
|
||||
return (bool) $this->getRecord()->users_can_see;
|
||||
}
|
||||
|
||||
function getCategory(): ?FAQCategory
|
||||
{
|
||||
return (new FAQCategories)->get($this->getRecord()->category);
|
||||
}
|
||||
|
||||
function getLanguage(): string
|
||||
{
|
||||
return $this->getRecord()->language;
|
||||
}
|
||||
}
|
50
Web/Models/Entities/FAQCategory.php
Normal file
50
Web/Models/Entities/FAQCategory.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Entities;
|
||||
use openvk\Web\Models\RowModel;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
||||
class FAQCategory extends RowModel
|
||||
{
|
||||
protected $tableName = "faq_categories";
|
||||
|
||||
function getId(): int
|
||||
{
|
||||
return $this->getRecord()->id;
|
||||
}
|
||||
|
||||
function getTitle(): string
|
||||
{
|
||||
return $this->getRecord()->title;
|
||||
}
|
||||
|
||||
function canSeeByUsers(): bool
|
||||
{
|
||||
return (bool) !$this->getRecord()->for_agents_only;
|
||||
}
|
||||
|
||||
function getIconBackgroundPosition(): int
|
||||
{
|
||||
return 28 * $this->getRecord()->icon;
|
||||
}
|
||||
|
||||
function getArticles(?int $limit = NULL, $isAgent): \Traversable
|
||||
{
|
||||
$filter = ["category" => $this->getId(), "deleted" => 0];
|
||||
if (!$isAgent) $filter["users_can_see"] = 1;
|
||||
|
||||
$articles = DatabaseConnection::i()->getContext()->table("faq_articles")->where($filter)->limit($limit);
|
||||
foreach ($articles as $article) {
|
||||
yield new FAQArticle($article);
|
||||
}
|
||||
}
|
||||
|
||||
function getIcon(): int
|
||||
{
|
||||
return $this->getRecord()->icon;
|
||||
}
|
||||
|
||||
function getLanguage(): string
|
||||
{
|
||||
return $this->getRecord()->language;
|
||||
}
|
||||
}
|
27
Web/Models/Repositories/FAQArticles.php
Normal file
27
Web/Models/Repositories/FAQArticles.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use openvk\Web\Models\Entities\FAQArticle;
|
||||
|
||||
class FAQArticles
|
||||
{
|
||||
private $context;
|
||||
private $articles;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DatabaseConnection::i()->getContext();
|
||||
$this->articles = $this->context->table("faq_articles");
|
||||
}
|
||||
|
||||
function toFAQArticle(?ActiveRow $ar): ?FAQArticle
|
||||
{
|
||||
return is_null($ar) ? NULL : new FAQArticle($ar);
|
||||
}
|
||||
|
||||
function get(int $id): ?FAQArticle
|
||||
{
|
||||
return $this->toFAQArticle($this->articles->get($id));
|
||||
}
|
||||
}
|
37
Web/Models/Repositories/FAQCategories.php
Normal file
37
Web/Models/Repositories/FAQCategories.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Models\Repositories;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
use Nette\Database\Table\ActiveRow;
|
||||
use openvk\Web\Models\Entities\FAQCategory;
|
||||
|
||||
class FAQCategories
|
||||
{
|
||||
private $context;
|
||||
private $categories;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->context = DatabaseConnection::i()->getContext();
|
||||
$this->categories = $this->context->table("faq_categories");
|
||||
}
|
||||
|
||||
function toFAQCategory(?ActiveRow $ar): ?FAQCategory
|
||||
{
|
||||
return is_null($ar) ? NULL : new FAQCategory($ar);
|
||||
}
|
||||
|
||||
function get(int $id): ?FAQCategory
|
||||
{
|
||||
return $this->toFAQCategory($this->categories->get($id));
|
||||
}
|
||||
|
||||
function getList(string $language, bool $includeForAgents = false): \Traversable
|
||||
{
|
||||
$filter = ["deleted" => 0, "language" => $language];
|
||||
if (!$includeForAgents) $filter["for_agents_only"] = 0;
|
||||
|
||||
foreach ($this->categories->where($filter) as $category) {
|
||||
yield new FAQCategory($category);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
namespace openvk\Web\Presenters;
|
||||
use openvk\Web\Models\Entities\{SupportAgent, Ticket, TicketComment};
|
||||
use openvk\Web\Models\Repositories\{Tickets, Users, TicketComments, SupportAgents};
|
||||
use openvk\Web\Models\Entities\{FAQArticle, FAQCategory, SupportAgent, Ticket, TicketComment};
|
||||
use openvk\Web\Models\Repositories\{FAQCategories, Tickets, Users, TicketComments, SupportAgents, FAQArticles};
|
||||
use openvk\Web\Util\Telegram;
|
||||
use Chandler\Session\Session;
|
||||
use Chandler\Database\DatabaseConnection;
|
||||
|
@ -28,8 +28,12 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
{
|
||||
$this->assertUserLoggedIn();
|
||||
$this->template->mode = in_array($this->queryParam("act"), ["faq", "new", "list"]) ? $this->queryParam("act") : "faq";
|
||||
$canEdit = $this->user->identity->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0);
|
||||
|
||||
if($this->template->mode === "faq") {
|
||||
$this->template->categories = (new FAQCategories)->getList($canEdit ? $this->queryParam("lang") ?? Session::i()->get("lang", "ru") : Session::i()->get("lang", "ru"), $canEdit);
|
||||
$this->template->canEditFAQ = $canEdit;
|
||||
|
||||
$lang = Session::i()->get("lang", "ru");
|
||||
$base = OPENVK_ROOT . "/data/knowledgebase/faq";
|
||||
if(file_exists("$base.$lang.md"))
|
||||
|
@ -104,6 +108,9 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
$this->flashFail("err", tr("error"), tr("you_have_not_entered_name_or_text"));
|
||||
}
|
||||
}
|
||||
|
||||
$this->template->languages = getLanguages();
|
||||
$this->template->activeLang = $this->queryParam("lang") ?? Session::i()->get("lang", "ru");
|
||||
}
|
||||
|
||||
function renderList(): void
|
||||
|
@ -396,4 +403,191 @@ final class SupportPresenter extends OpenVKPresenter
|
|||
$this->flashFail("succ", "Успех", "Профиль создан. Теперь пользователи видят Ваши псевдоним и аватарку вместо стандартных аватарки и номера.");
|
||||
}
|
||||
}
|
||||
|
||||
function renderFAQArticle(int $id): void
|
||||
{
|
||||
$article = (new FAQArticles)->get($id);
|
||||
if (!$article || $article->isDeleted())
|
||||
$this->notFound();
|
||||
|
||||
$category = $article->getCategory();
|
||||
|
||||
if ($category->isDeleted())
|
||||
$this->notFound();
|
||||
|
||||
if (!$article->canSeeByUnloggedUsers())
|
||||
$this->assertUserLoggedIn();
|
||||
|
||||
if (!$category->canSeeByUsers() || (!$article->canSeeByUsers() && !$article->canSeeByUnloggedUsers()))
|
||||
$this->assertPermission("openvk\Web\Models\Entities\TicketReply", "write", 0);
|
||||
|
||||
$canEdit = false;
|
||||
if ($this->user->identity)
|
||||
$canEdit = $this->user->identity->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0);
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$this->assertNoCSRF();
|
||||
if (!$canEdit) {
|
||||
$this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
|
||||
}
|
||||
|
||||
$cid = $this->postParam("category");
|
||||
if ($this->postParam("language") != $article->getLanguage()) {
|
||||
$categories = iterator_to_array((new FAQCategories)->getList($this->postParam("language")));
|
||||
if (sizeof($categories) > 0) {
|
||||
$cid = iterator_to_array((new FAQCategories)->getList($this->postParam("language")))[0]->getId();
|
||||
} else {
|
||||
$this->flashFail("err", tr("error"), tr("support_cant_change_lang_no_cats"));
|
||||
}
|
||||
}
|
||||
|
||||
$article->setTitle($this->postParam("title"));
|
||||
$article->setText($this->postParam("text"));
|
||||
$article->setUnlogged_Can_see(empty($this->postParam("unlogged_can_see") ? 0 : 1));
|
||||
$article->setUsers_Can_See(empty($this->postParam("users_can_see") ? 0 : 1));
|
||||
$article->setCategory($cid);
|
||||
$article->setLanguage($this->postParam("language"));
|
||||
$article->save();
|
||||
$this->flashFail("succ", tr("changes_saved"));
|
||||
} else {
|
||||
$this->template->mode = $canEdit ? in_array($this->queryParam("act"), ["view", "edit"]) ? $this->queryParam("act") : "view" : "view";
|
||||
$this->template->category = $category;
|
||||
$this->template->article = $article;
|
||||
$this->template->text = (new Parsedown())->text($article->getText());
|
||||
$this->template->canEditFAQ = $canEdit;
|
||||
$this->template->categories = (new FAQCategories)->getList($this->queryParam("lang") ?? $article->getLanguage(), TRUE);
|
||||
$this->template->languages = getLanguages();
|
||||
$this->template->activeLang = $this->queryParam("lang") ?? $article->getLanguage();
|
||||
}
|
||||
}
|
||||
|
||||
function renderFAQCategory(int $id): void
|
||||
{
|
||||
$category = (new FAQCategories)->get($id);
|
||||
|
||||
if (!$category)
|
||||
$this->notFound();
|
||||
|
||||
if (!$category->canSeeByUsers())
|
||||
$this->assertPermission("openvk\Web\Models\Entities\TicketReply", "write", 0);
|
||||
|
||||
$canEdit = $this->user->identity->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0);
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$this->assertNoCSRF();
|
||||
if (!$canEdit) {
|
||||
$this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
|
||||
}
|
||||
|
||||
if ($this->queryParam("mode") === "copy") {
|
||||
$orig_cat = $category;
|
||||
$category = new FAQCategory;
|
||||
}
|
||||
|
||||
$category->setTitle($this->postParam("title"));
|
||||
$category->setIcon($orig_cat->getIcon() ?? $this->postParam("icon"));
|
||||
$category->setFor_Agents_Only(empty($this->postParam("for_agents_only") ? 0 : 1));
|
||||
$category->setLanguage($this->postParam("language"));
|
||||
$category->save();
|
||||
|
||||
if ($this->queryParam("mode") === "copy" && !empty($this->postParam("copy_with_articles"))) {
|
||||
$articles = $orig_cat->getArticles(NULL, TRUE);
|
||||
foreach ($articles as $article) {
|
||||
$_article = new FAQArticle;
|
||||
$_article->setCategory($category->getId());
|
||||
$_article->setTitle($article->getTitle());
|
||||
$_article->setText($article->getText());
|
||||
$_article->setUsers_Can_See($article->canSeeByUsers());
|
||||
$_article->setUnlogged_Can_See($article->canSeeByUnloggedUsers());
|
||||
$_article->setLanguage($category->getLanguage());
|
||||
$_article->save();
|
||||
}
|
||||
}
|
||||
|
||||
$this->flashFail("succ", tr("changes_saved"));
|
||||
}
|
||||
|
||||
$this->template->mode = $canEdit ? in_array($this->queryParam("act"), ["view", "edit"]) ? $this->queryParam("act") : "view" : "view";
|
||||
$this->template->copyMode = $canEdit ? $this->queryParam("mode") === "copy" : FALSE;
|
||||
$this->template->category = $category;
|
||||
$this->template->canEditFAQ = $canEdit;
|
||||
$this->template->languages = getLanguages();
|
||||
}
|
||||
|
||||
function renderFAQNewArticle(): void
|
||||
{
|
||||
$this->assertPermission("openvk\Web\Models\Entities\TicketReply", "write", 0);
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
if (!$this->postParam("category"))
|
||||
$this->flashFail("err", tr("support_category_not_specified"));
|
||||
|
||||
$article = new FAQArticle;
|
||||
$article->setTitle($this->postParam("title"));
|
||||
$article->setText($this->postParam("text"));
|
||||
$article->setUnlogged_Can_see(empty($this->postParam("unlogged_can_see") ? 0 : 1));
|
||||
$article->setUsers_Can_See(empty($this->postParam("users_can_see") ? 0 : 1));
|
||||
$article->setCategory($this->postParam("category"));
|
||||
$article->setLanguage(Session::i()->get("lang", "ru"));
|
||||
$article->save();
|
||||
$this->redirect("/faq" . $article->getId());
|
||||
} else {
|
||||
$this->template->categories = (new FAQCategories)->getList($this->queryParam("lang") ?? Session::i()->get("lang", "ru"), TRUE);
|
||||
$this->template->category_id = $this->queryParam("cid");
|
||||
$this->template->activeLang = $this->queryParam("lang") ?? Session::i()->get("lang", "ru");
|
||||
$this->template->languages = getLanguages();
|
||||
}
|
||||
}
|
||||
|
||||
function renderFAQNewCategory(): void
|
||||
{
|
||||
$this->assertPermission("openvk\Web\Models\Entities\TicketReply", "write", 0);
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$category = new FAQCategory;
|
||||
$category->setTitle($this->postParam("title"));
|
||||
$category->setIcon($this->postParam("icon"));
|
||||
$category->setFor_Agents_Only(empty($this->postParam("for_agents_only") ? 0 : 1));
|
||||
$category->setLanguage($this->postParam("language"));
|
||||
$category->save();
|
||||
$this->redirect("/faqs" . $category->getId());
|
||||
}
|
||||
|
||||
$this->template->activeLang = $this->queryParam("lang") ?? Session::i()->get("lang", "ru");
|
||||
$this->template->languages = getLanguages();
|
||||
}
|
||||
|
||||
function renderFAQDeleteArticle(int $id): void
|
||||
{
|
||||
$this->assertNoCSRF();
|
||||
$this->assertPermission("openvk\Web\Models\Entities\TicketReply", "write", 0);
|
||||
|
||||
$article = (new FAQArticles)->get($id);
|
||||
if (!$article || $article->isDeleted())
|
||||
$this->notFound();
|
||||
|
||||
$cid = $article->getCategory()->getId();
|
||||
$article->delete();
|
||||
$this->redirect("/faqs" . $cid);
|
||||
}
|
||||
|
||||
function renderFAQDeleteCategory(int $id): void
|
||||
{
|
||||
$this->assertNoCSRF();
|
||||
$this->assertPermission("openvk\Web\Models\Entities\TicketReply", "write", 0);
|
||||
|
||||
$category = (new FAQCategories)->get($id);
|
||||
if (!$category || $category->isDeleted())
|
||||
$this->notFound();
|
||||
|
||||
if (!empty($this->postParam("delete_articles"))) {
|
||||
$articles = $category->getArticles(NULL, TRUE);
|
||||
foreach ($articles as $article) {
|
||||
$article->delete();
|
||||
}
|
||||
}
|
||||
|
||||
$category->delete();
|
||||
$this->redirect("/support");
|
||||
}
|
||||
}
|
||||
|
|
73
Web/Presenters/templates/Support/FAQArticle.xml
Normal file
73
Web/Presenters/templates/Support/FAQArticle.xml
Normal file
|
@ -0,0 +1,73 @@
|
|||
{extends "../@layout.xml"}
|
||||
|
||||
{block title}{$title}{/block}
|
||||
|
||||
{block header}
|
||||
<a href="/support">{_menu_help}</a>
|
||||
> <a href="/faqs{$category->getId()}">{$category->getTitle()}</a>
|
||||
> <a n:if="$mode === 'edit'" href="/faq{$article->getId()}">{$article->getTitle()}</a>
|
||||
<div style="display:inline;" n:if="$mode === 'view'">{_support_article} <a n:if="$canEditFAQ" href="/faq{$article->getId()}?act=edit">({_app_edit})</a></div>
|
||||
<div style="display:inline;" n:if="$mode === 'edit'">> {_support_edit_mode}</div>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
<script>
|
||||
function onLanguageSelectChanged(e) {
|
||||
window.location.href = "/faq{$article->getId()}?act=edit&lang=" + e.value;
|
||||
}
|
||||
</script>
|
||||
<div n:if="$mode === 'view'">
|
||||
<h4 style="padding:8px 0;">{$article->getTitle()}</h4>
|
||||
{$text|noescape}
|
||||
</div>
|
||||
<div n:if="$mode === 'edit'">
|
||||
<form action="/faq{$article->getId()}" method="post" style="margin:0;">
|
||||
<center>
|
||||
<input value="{$article->getTitle()}" type="text" name="title" style="width: 80%; resize: vertical;" placeholder="{_support_faq_title_placeholder}" /><br /><br />
|
||||
<textarea name="text" style="width: 80%; resize: vertical; height: 200px" placeholder="{_support_faq_text_placeholder}">{$article->getText()}</textarea><br /><br />
|
||||
<input n:attr="checked => $article->canSeeByUnloggedUsers()" type="checkbox" name="unlogged_can_see" value="1" /> {_support_unlogged_can_see}
|
||||
<input n:attr="checked => $article->canSeeByUsers()" type="checkbox" name="users_can_see" value="1" /> {_support_users_can_see}
|
||||
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_support_category}: </span>
|
||||
</td>
|
||||
<td>
|
||||
<select name="category">
|
||||
<option
|
||||
n:attr="selected => $article->getCategory()->getId() === $category->getId()"
|
||||
n:foreach="$categories as $category"
|
||||
value="{$category->getId()}"
|
||||
>[#{$category->getId()}] {$category->getTitle()}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_support_language}: </span>
|
||||
</td>
|
||||
<td>
|
||||
<select name="language" onchange="onLanguageSelectChanged(this)">
|
||||
<option
|
||||
n:attr="selected => $language['code'] === $activeLang"
|
||||
n:foreach="$languages as $language"
|
||||
value="{$language['code']}"
|
||||
>[{$language["code"]}] {$language["native_name"]}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" value="{_save}" class="button" style="margin-left: 70%;" /><br /><br />
|
||||
</center>
|
||||
</form>
|
||||
<h4 /><br />
|
||||
<form action="/al_helpdesk/{$article->getId()}/delete" method="post" style="margin:0;">
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" value="{_delete}" class="button" />
|
||||
</form>
|
||||
</div>
|
||||
{/block}
|
90
Web/Presenters/templates/Support/FAQCategory.xml
Normal file
90
Web/Presenters/templates/Support/FAQCategory.xml
Normal file
|
@ -0,0 +1,90 @@
|
|||
{extends "../@layout.xml"}
|
||||
|
||||
{block title}{$title}{/block}
|
||||
|
||||
{block header}
|
||||
<a href="/support">{_menu_help}</a>
|
||||
<div style="display:inline;" n:if="$mode === 'view'">> {_support_faq_category}</div>
|
||||
<div style="display:inline;" n:if="$mode === 'edit'">> <a href="/faqs{$category->getId()}">{$category->getTitle()}</a> > {_support_edit_mode}</div>
|
||||
{/block}
|
||||
|
||||
{block content}
|
||||
{var $articles = iterator_to_array($category->getArticles(NULL, $canEditFAQ))}
|
||||
<div n:if="$mode === 'view'">
|
||||
<h4 style="display:flex;line-height:28px;gap:8px;padding:8px;">
|
||||
<div style="width:28px;height:28px;border-radius:3px;background:url('/assets/packages/static/openvk/img/faq_icons.png');background-position:0 -{$category->getIconBackgroundPosition()}px;"/>
|
||||
{$category->getTitle()}
|
||||
<div n:if="$canEditFAQ">
|
||||
<a href="/faqs{$category->getId()}?act=edit">({_support_edit})</a>
|
||||
<a href="/faqs{$category->getId()}?act=edit&mode=copy">({_support_copy})</a>
|
||||
<a href="/new_faq?lang={$category->getLanguage()}&cid={$category->getId()}">(+{_support_add_article})</a>
|
||||
</div>
|
||||
</h4>
|
||||
<div style="margin:14px;" />
|
||||
<ul n:if="count($articles) > 0" n:foreach="$articles as $article" style="padding-inline-start:18px;">
|
||||
<li>
|
||||
<a href="/faq{$article->getId()}" style="display:block;">
|
||||
<h4 style="display:inherit;padding:8px;">{$article->getTitle()}</h4>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<center n:if="count($articles) === 0" style="padding: 30px;">
|
||||
<img src="/assets/packages/static/openvk/img/sad.png" height="45"/>
|
||||
<div style="margin-top: 8px;">{_support_empty}</div>
|
||||
</center>
|
||||
</div>
|
||||
<div n:if="$mode === 'edit'">
|
||||
<form method="post" style="margin:0;">
|
||||
<center>
|
||||
<input value="{$category->getTitle()}" type="text" name="title" style="width: 80%; resize: vertical;" placeholder="{_support_faq_title_placeholder}" /><br /><br />
|
||||
<input n:attr="checked => !$category->canSeeByUsers()" type="checkbox" name="for_agents_only" value="1" /> {_support_for_agents_only}
|
||||
<span n:if="$copyMode"><input type="checkbox" name="copy_with_articles" value="1" /> {_support_copy_articles}</span>
|
||||
<br/><br/>
|
||||
<div style="display:grid;grid-template-columns: repeat(11, auto);row-gap: 10px;">
|
||||
<div n:for="$i = 0; $i < 22; $i++" onclick="onIconClick({$i})">
|
||||
<div id="icon-{$i}" style="width:28px;height:28px;border-radius:3px;background:url('/assets/packages/static/openvk/img/faq_icons.png');background-position:0 -{28 * $i}px;cursor:pointer;" />
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_support_language}: </span>
|
||||
</td>
|
||||
<td>
|
||||
<select name="language">
|
||||
<option
|
||||
n:attr="selected => $language['code'] === $category->getLanguage()"
|
||||
n:foreach="$languages as $language"
|
||||
value="{$language['code']}"
|
||||
>[{$language["code"]}] {$language["native_name"]}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<input id="icon-number" type="hidden" name="icon" value="0" />
|
||||
<div style="display:none;" id="prev-icon">0</div>
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" value="{_save}" class="button" style="margin-left: 70%;" /><br /><br />
|
||||
</center>
|
||||
</form>
|
||||
<h4 /><br />
|
||||
<form action="/al_helpdesk_cat/{$category->getId()}/delete" method="post" style="margin:0;">
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" value="{_delete}" class="button" />
|
||||
<input type="checkbox" name="delete_articles" value="1" /> {_support_delete_articles}
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
const icon_number_elem = document.getElementById("icon-number");
|
||||
const prev_icon = document.getElementById("prev-icon");
|
||||
|
||||
function onIconClick(number) {
|
||||
icon_number_elem.value = number;
|
||||
document.getElementById("icon-" + prev_icon.innerHTML).style.border = "none";
|
||||
prev_icon.innerHTML = number;
|
||||
document.getElementById("icon-" + number).style.border = "3px solid red";
|
||||
}
|
||||
</script>
|
||||
{/block}
|
57
Web/Presenters/templates/Support/FAQNewArticle.xml
Normal file
57
Web/Presenters/templates/Support/FAQNewArticle.xml
Normal file
|
@ -0,0 +1,57 @@
|
|||
{extends "../@layout.xml"}
|
||||
|
||||
{block title}{_support_new_article}{/block}
|
||||
|
||||
{block header}<a href="/support?lang={$activeLang}">{_menu_help}</a> > {_support_create_article}{/block}
|
||||
|
||||
{block content}
|
||||
<script>
|
||||
function onLanguageSelectChanged(e) {
|
||||
window.location.href = "/new_faq?lang=" + e.value;
|
||||
}
|
||||
</script>
|
||||
<form action="/new_faq" method="post" style="margin:0;">
|
||||
<center>
|
||||
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_support_language}: </span>
|
||||
</td>
|
||||
<td>
|
||||
<select name="language" onchange="onLanguageSelectChanged(this)">
|
||||
<option
|
||||
n:attr="selected => $language['code'] == $activeLang"
|
||||
n:foreach="$languages as $language"
|
||||
value="{$language['code']}"
|
||||
>[{$language["code"]}] {$language["native_name"]}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type="text" name="title" style="width: 80%; resize: vertical;" placeholder="{_support_faq_title_placeholder}" /><br /><br />
|
||||
<textarea name="text" style="width: 80%; resize: vertical;" placeholder="{_support_faq_text_placeholder}"></textarea><br /><br />
|
||||
<input type="checkbox" name="unlogged_can_see" value="1" /> {_support_unlogged_can_see}
|
||||
<input type="checkbox" name="users_can_see" value="1" /> {_support_users_can_see}
|
||||
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_support_category}: </span>
|
||||
</td>
|
||||
<td>
|
||||
<select name="category">
|
||||
<option
|
||||
n:attr="selected => $category->getId() == $category_id"
|
||||
n:foreach="$categories as $category"
|
||||
value="{$category->getId()}"
|
||||
>[#{$category->getId()}] {$category->getTitle()}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{$test}
|
||||
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" value="{_create}" class="button" style="margin-left: 70%;" /><br /><br />
|
||||
</center>
|
||||
</form>
|
||||
{/block}
|
56
Web/Presenters/templates/Support/FAQNewCategory.xml
Normal file
56
Web/Presenters/templates/Support/FAQNewCategory.xml
Normal file
|
@ -0,0 +1,56 @@
|
|||
{extends "../@layout.xml"}
|
||||
|
||||
{block title}{_support_new_category}{/block}
|
||||
|
||||
{block header}<a href="/support?lang={$activeLang}">{_menu_help}</a> > {_support_create_category}{/block}
|
||||
|
||||
{block content}
|
||||
<form action="/new_faq_cat" method="post" style="margin:0;">
|
||||
<center>
|
||||
<table cellspacing="7" cellpadding="0" width="60%" border="0" align="center">
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_support_language}: </span>
|
||||
</td>
|
||||
<td>
|
||||
<select name="language">
|
||||
<option
|
||||
n:attr="selected => $language['code'] == $activeLang"
|
||||
n:foreach="$languages as $language"
|
||||
value="{$language['code']}"
|
||||
>[{$language["code"]}] {$language["native_name"]}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type="text" name="title" style="width: 80%; resize: vertical;" placeholder="{_support_faq_title_placeholder}" /><br /><br />
|
||||
<input type="checkbox" name="for_agents_only" value="1" /> {_support_for_agents_only}
|
||||
<br/><br/>
|
||||
</center>
|
||||
<b style="display:block;margin-bottom:8px;padding:0 16px;">Иконка</b>
|
||||
<center>
|
||||
<div style="display:grid;grid-template-columns: repeat(11, auto);row-gap: 10px">
|
||||
<div n:for="$i = 0; $i < 22; $i++" onclick="onIconClick({$i})">
|
||||
<div id="icon-{$i}" style="width:28px;height:28px;border-radius:3px;background:url('/assets/packages/static/openvk/img/faq_icons.png');background-position:0 -{28 * $i}px;cursor:pointer;" />
|
||||
</div>
|
||||
</div>
|
||||
<br/><br/>
|
||||
|
||||
<input id="icon-number" type="hidden" name="icon" value="0" />
|
||||
<div style="display:none;" id="prev-icon">0</div>
|
||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||
<input type="submit" value="{_create}" class="button" style="margin-left: 70%;" /><br /><br />
|
||||
</center>
|
||||
</form>
|
||||
<script>
|
||||
const icon_number_elem = document.getElementById("icon-number");
|
||||
const prev_icon = document.getElementById("prev-icon");
|
||||
|
||||
function onIconClick(number) {
|
||||
icon_number_elem.value = number;
|
||||
document.getElementById("icon-" + prev_icon.innerHTML).style.border = "none";
|
||||
prev_icon.innerHTML = number;
|
||||
document.getElementById("icon-" + number).style.border = "3px solid red";
|
||||
}
|
||||
</script>
|
||||
{/block}
|
|
@ -49,7 +49,57 @@
|
|||
{/if}
|
||||
|
||||
{if $isMain}
|
||||
<script>
|
||||
function onLanguageSelectChanged(e) {
|
||||
window.location.href = "/support?lang=" + e.value;
|
||||
}
|
||||
</script>
|
||||
<h4>{_support_faq}</h4><br />
|
||||
<div n:if="$canEditFAQ" style="display:flex;justify-content:space-between;">
|
||||
<div>
|
||||
{_create}
|
||||
<a href="/new_faq_cat?lang={$activeLang}">{_support_category_acc}</a>
|
||||
·
|
||||
<a href="/new_faq?lang={$activeLang}">{_support_article_acc}</a>
|
||||
</div>
|
||||
<table cellspacing="7" cellpadding="0" border="0">
|
||||
<tr>
|
||||
<td width="120" valign="top">
|
||||
<span class="nobold">{_support_language}: </span>
|
||||
</td>
|
||||
<td>
|
||||
<select name="language" onchange="onLanguageSelectChanged(this)">
|
||||
<option
|
||||
n:attr="selected => $language['code'] === $activeLang"
|
||||
n:foreach="$languages as $language"
|
||||
value="{$language['code']}"
|
||||
>[{$language["code"]}] {$language["native_name"]}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns: repeat(2, 1fr);row-gap:16px;">
|
||||
<div n:foreach="$categories as $category" href="/faqs{$category->getId()}">
|
||||
<h4 style="display:flex;line-height:28px;gap:8px;padding:8px;max-width:85%;">
|
||||
<div style="width:28px;height:28px;border-radius:3px;background:url('/assets/packages/static/openvk/img/faq_icons.png');background-position:0 -{$category->getIconBackgroundPosition()}px;"/>
|
||||
<a href="/faqs{$category->getId()}">{$category->getTitle()}</a>
|
||||
</h4>
|
||||
{var $articles = iterator_to_array($category->getArticles(3, $canEditFAQ))}
|
||||
<ul n:if="count($articles) > 0" style="padding-inline-start:18px;">
|
||||
<li n:foreach="$articles as $article">
|
||||
<a href="/faq{$article->getId()}" style="display:block;">
|
||||
<h4 style="display:inherit;padding:8px;border:none;">{$article->getTitle()}</h4>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<center n:if="count($articles) === 0" style="padding: 30px;">
|
||||
<img src="/assets/packages/static/openvk/img/sad.png" height="45"/>
|
||||
<div style="margin-top: 8px;">{_support_empty}</div>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<div n:foreach="$faq as $section" class="faq">
|
||||
<div id="faqhead">{$section[0]}</div>
|
||||
<div id="faqcontent">{$section[1]|noescape}</div>
|
||||
|
|
|
@ -43,6 +43,18 @@ routes:
|
|||
handler: "About->donate"
|
||||
- url: "/kb/{slug}"
|
||||
handler: "Support->knowledgeBaseArticle"
|
||||
- url: "/faq{num}"
|
||||
handler: "Support->FAQArticle"
|
||||
- url: "/faqs{num}"
|
||||
handler: "Support->FAQCategory"
|
||||
- url: "/new_faq"
|
||||
handler: "Support->FAQNewArticle"
|
||||
- url: "/new_faq_cat"
|
||||
handler: "Support->FAQNewCategory"
|
||||
- url: "/al_helpdesk/{num}/delete"
|
||||
handler: "Support->FAQDeleteArticle"
|
||||
- url: "/al_helpdesk_cat/{num}/delete"
|
||||
handler: "Support->FAQDeleteCategory"
|
||||
- url: "/about:{?!productName}"
|
||||
handler: "About->version"
|
||||
placeholders:
|
||||
|
|
BIN
Web/static/img/faq_icons.png
Normal file
BIN
Web/static/img/faq_icons.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5 KiB |
BIN
Web/static/img/sad.png
Normal file
BIN
Web/static/img/sad.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
37
install/sqls/00038-faq.sql
Normal file
37
install/sqls/00038-faq.sql
Normal file
|
@ -0,0 +1,37 @@
|
|||
CREATE TABLE `faq_categories`
|
||||
(
|
||||
`id` bigint(20) UNSIGNED NOT NULL,
|
||||
`title` tinytext NOT NULL,
|
||||
`for_agents_only` tinyint(1) DEFAULT NULL,
|
||||
`icon` int(11) NOT NULL,
|
||||
`language` varchar(255) NOT NULL,
|
||||
`deleted` tinyint(1) DEFAULT 0
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4;
|
||||
|
||||
ALTER TABLE `faq_categories`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
ALTER TABLE `faq_categories`
|
||||
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
|
||||
COMMIT;
|
||||
|
||||
CREATE TABLE `faq_articles`
|
||||
(
|
||||
`id` bigint(20) UNSIGNED NOT NULL,
|
||||
`category` bigint(20) UNSIGNED DEFAULT NULL,
|
||||
`title` mediumtext NOT NULL,
|
||||
`text` longtext NOT NULL,
|
||||
`users_can_see` tinyint(1) DEFAULT NULL,
|
||||
`unlogged_can_see` tinyint(1) DEFAULT NULL,
|
||||
`language` varchar(255) NOT NULL,
|
||||
`deleted` tinyint(1) DEFAULT 0
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4;
|
||||
|
||||
ALTER TABLE `faq_articles`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
ALTER TABLE `faq_articles`
|
||||
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
|
||||
COMMIT;
|
|
@ -913,6 +913,32 @@
|
|||
"banned_in_support_1" = "Sorry, <b>$1</b>, but now you can't create tickets.";
|
||||
"banned_in_support_2" = "And the reason for this is simple: <b>$1</b>. Unfortunately, this time we had to take away this opportunity from you forever.";
|
||||
|
||||
"support_article" = "Article";
|
||||
"support_edit_mode" = "Editing";
|
||||
"support_faq_title_placeholder" = "A long time ago,";
|
||||
"support_faq_text_placeholder" = "In a galaxy far, far away...";
|
||||
"support_unlogged_can_see" = "Unauthorized users can see";
|
||||
"support_users_can_see" = "Users can see";
|
||||
"support_category" = "Category";
|
||||
"support_faq_category" = "FAQ Category";
|
||||
"support_edit" = "edit";
|
||||
"support_add_article" = "article";
|
||||
"support_empty" = "There's nothing here yet";
|
||||
"support_for_agents_only" = "Only for agents";
|
||||
"support_icon_number" = "Icon number";
|
||||
"support_create_article" = "Create an article";
|
||||
"support_create_category" = "Create a category";
|
||||
"support_new_article" = "New article";
|
||||
"support_new_category" = "New category";
|
||||
"support_article_acc" = "an article";
|
||||
"support_category_acc" = "a category";
|
||||
"support_language" = "Language";
|
||||
"support_category_not_specified" = "You didn't specify a category";
|
||||
"support_cant_change_lang_no_cats" = "In this localization, not a single category has been created yet to which the article could be moved";
|
||||
"support_copy" = "copy";
|
||||
"support_delete_articles" = "Delete articles";
|
||||
"support_copy_articles" = "Copy with articles";
|
||||
|
||||
/* Invite */
|
||||
|
||||
"invite" = "Invite";
|
||||
|
|
|
@ -841,6 +841,31 @@
|
|||
"ticket_changed_comment" = "Изменения вступят силу через несколько секунд.";
|
||||
"banned_in_support_1" = "Извините, <b>$1</b>, но теперь вам нельзя создавать обращения.";
|
||||
"banned_in_support_2" = "А причина этому проста: <b>$1</b>. К сожалению, на этот раз нам пришлось отобрать у вас эту возможность навсегда.";
|
||||
"support_article" = "Статья";
|
||||
"support_edit_mode" = "Редактирование";
|
||||
"support_faq_title_placeholder" = "Давным-давно,";
|
||||
"support_faq_text_placeholder" = "В далёкой-далёкой галактике...";
|
||||
"support_unlogged_can_see" = "Могут видеть неавторизованные";
|
||||
"support_users_can_see" = "Могут видеть пользователи";
|
||||
"support_category" = "Категория";
|
||||
"support_faq_category" = "Категория FAQ";
|
||||
"support_edit" = "редактировать";
|
||||
"support_add_article" = "статья";
|
||||
"support_empty" = "Здесь пока ничего нет";
|
||||
"support_for_agents_only" = "Только для агентов";
|
||||
"support_icon_number" = "Номер иконки";
|
||||
"support_create_article" = "Создать статью";
|
||||
"support_create_category" = "Создать категорию";
|
||||
"support_new_article" = "Новая статья";
|
||||
"support_new_category" = "Новая категория";
|
||||
"support_article_acc" = "статью";
|
||||
"support_category_acc" = "категорию";
|
||||
"support_language" = "Язык";
|
||||
"support_category_not_specified" = "Вы не указали категорию";
|
||||
"support_cant_change_lang_no_cats" = "В этой локализации еще не создано ни одной категории, в которую можно было бы переместить статью";
|
||||
"support_copy" = "дублировать";
|
||||
"support_delete_articles" = "Удалить статьи";
|
||||
"support_copy_articles" = "Копировать со статьями";
|
||||
|
||||
/* Invite */
|
||||
|
||||
|
|
Loading…
Reference in a new issue