forked from SimplesIP/pabx-app
19 changed files with 560 additions and 78 deletions
@ -0,0 +1,109 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace app\controllers; |
||||||
|
|
||||||
|
use app\models\Meet; |
||||||
|
use app\models\MeetPeople; |
||||||
|
use app\models\Queue; |
||||||
|
use app\traits\MeetRequest; |
||||||
|
use app\traits\Validate; |
||||||
|
use Slim\Routing\RouteCollectorProxy; |
||||||
|
use Psr\Http\Message\ResponseInterface as Response; |
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request; |
||||||
|
use Exception; |
||||||
|
|
||||||
|
class MeetController |
||||||
|
{ |
||||||
|
use Validate, MeetRequest; |
||||||
|
static function route() |
||||||
|
{ |
||||||
|
return function (RouteCollectorProxy $group) { |
||||||
|
$group->post('/joinmeet', [self::class, 'joinMeet']); |
||||||
|
$group->post('/participantleft', [self::class, 'participantLeft']); |
||||||
|
$group->post('/conferenceleft', [self::class, 'conferenceLeft']); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
function joinMeet(Request $request, Response $response, array $args) |
||||||
|
{ |
||||||
|
try { |
||||||
|
$this->serverRequest(); |
||||||
|
$body = json_decode($request->getBody()->getContents(), true); |
||||||
|
|
||||||
|
$data = []; |
||||||
|
$dados['org_id'] = $body['org_id']; |
||||||
|
$dados['meet_id'] = $body['data']['roomName']; |
||||||
|
$dados['nome'] = $body['data']['roomName']; |
||||||
|
$dados['token'] = md5($body['data']['roomName']); |
||||||
|
$dados['org_id'] = 1; |
||||||
|
|
||||||
|
$people = []; |
||||||
|
$people['nome'] = $body['data']['displayName']; |
||||||
|
$people['meet_id'] = $body['data']['roomName']; |
||||||
|
$people['people_id'] = $body['data']['id']; |
||||||
|
|
||||||
|
$m = Meet::find(['meet_id' => $body['data']['roomName']]); |
||||||
|
$meet = null; |
||||||
|
if (!$m) { |
||||||
|
$meet = Meet::create($dados); |
||||||
|
} |
||||||
|
$p = MeetPeople::create($people); |
||||||
|
|
||||||
|
if ($data) { |
||||||
|
$response->getBody()->write(json_encode(['status' => true, 'data' => ['meet' => ['created' => $meet ?? 'OK'], 'user' => ['join' => $p ?? 'yes']]])); |
||||||
|
} else { |
||||||
|
$response->getBody()->write(json_encode(['status' => false, 'data' => $data])); |
||||||
|
} |
||||||
|
} catch (Exception $e) { |
||||||
|
$response->getBody()->write(json_encode(['status' => false, 'data' => ["message" => "Nao foi possivel realizar a consulta! " . $e->getMessage()]])); |
||||||
|
} |
||||||
|
return $response; |
||||||
|
} |
||||||
|
|
||||||
|
function conferenceLeft(Request $request, Response $response, array $args) |
||||||
|
{ |
||||||
|
try { |
||||||
|
$this->serverRequest(); |
||||||
|
$body = json_decode($request->getBody()->getContents(), true); |
||||||
|
|
||||||
|
$mp = Meet::find(['nome' => $body['roomName']]); |
||||||
|
$meet = null; |
||||||
|
if ($mp) { |
||||||
|
$meet = Meet::delete(['id' => $mp->id]); |
||||||
|
MeetPeople::delete(['meet_id' => $body['roomName']]); |
||||||
|
} |
||||||
|
|
||||||
|
if ($meet) { |
||||||
|
$response->getBody()->write(json_encode(['status' => true, 'data' => ['meet' => ['deleted' => $meet ?? 'OK']]])); |
||||||
|
} else { |
||||||
|
$response->getBody()->write(json_encode(['status' => false, 'data' => ['message' => 'Nao foi possivel exluir participant']])); |
||||||
|
} |
||||||
|
} catch (Exception $e) { |
||||||
|
$response->getBody()->write(json_encode(['status' => false, 'data' => ["message" => "Nao foi possivel realizar a consulta! " . $e->getMessage()]])); |
||||||
|
} |
||||||
|
return $response; |
||||||
|
} |
||||||
|
|
||||||
|
function participantLeft(Request $request, Response $response, array $args) |
||||||
|
{ |
||||||
|
try { |
||||||
|
$this->serverRequest(); |
||||||
|
$body = json_decode($request->getBody()->getContents(), true); |
||||||
|
|
||||||
|
$mp = MeetPeople::find(['people_id' => $body['id']]); |
||||||
|
$meet = null; |
||||||
|
if ($mp) { |
||||||
|
$meet = MeetPeople::delete(['people_id' => $body['id']]); |
||||||
|
} |
||||||
|
|
||||||
|
if ($meet) { |
||||||
|
$response->getBody()->write(json_encode(['status' => true, 'data' => ['meet' => ['created' => $meet ?? 'OK'], 'user' => ['join' => $p ?? 'yes']]])); |
||||||
|
} else { |
||||||
|
$response->getBody()->write(json_encode(['status' => false, 'data' => ['message' => 'Nao foi possivel exluir participant']])); |
||||||
|
} |
||||||
|
} catch (Exception $e) { |
||||||
|
$response->getBody()->write(json_encode(['status' => false, 'data' => ["message" => "Nao foi possivel realizar a consulta! " . $e->getMessage()]])); |
||||||
|
} |
||||||
|
return $response; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace app\models; |
||||||
|
|
||||||
|
use app\core\Repository; |
||||||
|
|
||||||
|
class Meet extends Repository |
||||||
|
{ |
||||||
|
protected static $table = 'pbx_meet'; |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace app\models; |
||||||
|
|
||||||
|
use app\core\Repository; |
||||||
|
|
||||||
|
class MeetPeople extends Repository |
||||||
|
{ |
||||||
|
protected static $table = 'pbx_meet_people'; |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace app\traits; |
||||||
|
|
||||||
|
use Exception; |
||||||
|
|
||||||
|
trait MeetRequest |
||||||
|
{ |
||||||
|
public function serverRequest() |
||||||
|
{ |
||||||
|
if ( |
||||||
|
!isset($_SERVER['HTTP_ORIGIN']) && |
||||||
|
(strpos($_SERVER['HTTP_ORIGIN'], 'localhost') === false || strpos($_SERVER['HTTP_ORIGIN'], '127.0.0.1') === false) |
||||||
|
) { |
||||||
|
throw new Exception('Request nao permitida!'); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,124 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
require_once("util/constantes.php"); |
||||||
|
$tpLayout = 1; |
||||||
|
$token = isset($_GET["t"]) ? $_GET["t"] : null; |
||||||
|
$error = false; |
||||||
|
try { |
||||||
|
$org_id = GetOrganizacao(); |
||||||
|
if ($token) { |
||||||
|
$inf = getInfoMeet($dbcon, $token); |
||||||
|
$nome = $inf['meet_id']; |
||||||
|
if ($inf) { |
||||||
|
setVisitas($inf['meet_id']); |
||||||
|
} else { |
||||||
|
header('location: index.php'); |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (Exception $e) { |
||||||
|
$jsStartup[] = $msg = sprintf("alert('%s');", $e->getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
function getInfoMeet($dbcon, $token) |
||||||
|
{ |
||||||
|
$query = "SELECT * FROM pbx_meet WHERE token = '$token';"; |
||||||
|
$result = pg_query($dbcon, $query); |
||||||
|
if (!$result) { |
||||||
|
throw new Exception("Não foi possível buscar a informação do reunião!"); |
||||||
|
} |
||||||
|
$dados = pg_fetch_assoc($result); |
||||||
|
return $dados; |
||||||
|
} |
||||||
|
|
||||||
|
function setVisitas($nome) |
||||||
|
{ |
||||||
|
$nomeUser = $_SESSION['SSnomeUser']; |
||||||
|
$mat = GetMatricula() ?? 'Unknow'; |
||||||
|
$name = rawurlencode($nome); |
||||||
|
|
||||||
|
$sql = "SELECT people_id FROM pbx_meet_people WHERE people_id = '$mat' AND meet_id = '$name';"; |
||||||
|
$res = pg_query($sql); |
||||||
|
$resp = pg_fetch_assoc($res); |
||||||
|
|
||||||
|
if (!$resp) { |
||||||
|
$query = "INSERT INTO pbx_meet_people (meet_id, nome, people_id) VALUES('$name', '$nomeUser', '$mat');"; |
||||||
|
pg_query($query); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$smarty->assign("desabilitaLayout", 1); |
||||||
|
|
||||||
|
?> |
||||||
|
|
||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
|
||||||
|
<head> |
||||||
|
<title>Simples IP - Teleconferência</title> |
||||||
|
<script src="../../scriptApl/jitsi_api.js"></script> |
||||||
|
<style> |
||||||
|
body { |
||||||
|
margin: 0; |
||||||
|
top: 0; |
||||||
|
bottom: 0; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
</style> |
||||||
|
</head> |
||||||
|
|
||||||
|
<body> |
||||||
|
<?php if ($nome) { ?> |
||||||
|
<iframe src="<?= "https://192.168.115.88:8443/$nome" ?>" style="border-style: none;width: 100%; height: 100vh;">
|
||||||
|
<p>Your browser does not support iframes.</p> |
||||||
|
</iframe> |
||||||
|
<?php } ?> |
||||||
|
|
||||||
|
<?php if (!$nome && !$token) { ?> |
||||||
|
<div id="jitsi-container" style="height: 100vh; width: 100%;"></div> |
||||||
|
<script> |
||||||
|
const domain = localStorage.getItem('meet'); |
||||||
|
const nameMeet = localStorage.getItem('nameMeet'); |
||||||
|
const options = { |
||||||
|
roomName: nameMeet, |
||||||
|
width: '100%', |
||||||
|
height: '100%', |
||||||
|
parentNode: document.querySelector('#jitsi-container') |
||||||
|
}; |
||||||
|
|
||||||
|
const api = new JitsiMeetExternalAPI(domain, options); |
||||||
|
api.addEventListener('videoConferenceJoined', (event) => { |
||||||
|
let dados = { |
||||||
|
action: 'ConferenceJoined', |
||||||
|
data: event |
||||||
|
} |
||||||
|
requestWebhook(dados, 'joinmeet'); |
||||||
|
}); |
||||||
|
|
||||||
|
api.addEventListener('videoConferenceLeft', (event) => { |
||||||
|
requestWebhook(event, 'conferenceleft'); |
||||||
|
}); |
||||||
|
|
||||||
|
function requestWebhook(meet, endpoint) { |
||||||
|
const webhookUrl = 'http://localhost:8080/api/v2/meet/' + endpoint; |
||||||
|
fetch(webhookUrl, { |
||||||
|
method: 'POST', |
||||||
|
headers: { |
||||||
|
'Content-Type': 'application/json' |
||||||
|
}, |
||||||
|
body: JSON.stringify(meet) |
||||||
|
}).then(response => { |
||||||
|
if (!response.ok) { |
||||||
|
throw new Error('Erro ao enviar webhook: ' + response.statusText); |
||||||
|
} |
||||||
|
return response.json(); |
||||||
|
}).then(data => { |
||||||
|
console.log('Webhook enviado com sucesso:', data); |
||||||
|
}).catch(error => { |
||||||
|
console.error('Erro ao enviar webhook:', error); |
||||||
|
}); |
||||||
|
} |
||||||
|
</script> |
||||||
|
<?php } ?> |
||||||
|
</body> |
||||||
|
|
||||||
|
</html> |
@ -0,0 +1,69 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
$param = ''; |
||||||
|
$erro = ''; |
||||||
|
$janW = 500; |
||||||
|
$janH = 285; |
||||||
|
$tpl = 'cadastros/meeting/meetings.tpl'; |
||||||
|
$paramPesquisa = isset($_REQUEST['paramPesquisa']) ? $_REQUEST['paramPesquisa'] : ''; |
||||||
|
$filtro = isset($_REQUEST["paramPesquisa"]) ? trim($_REQUEST["paramPesquisa"]) : ''; |
||||||
|
|
||||||
|
$statusAtivo = isset($_REQUEST["status"]) ? $_REQUEST["status"] : 1; |
||||||
|
$imgNovo = "<img src=\"images/novo.gif\" width=\"16\" height=\"16\" align=\"absmiddle\" border=\"0\" title=\"Cria nova meet\">"; |
||||||
|
$imgNovo = "<a href=\"javaScript:NovaJanela('index.php?idProg=373', 'cadMeet', '$janW', '$janH', 'resizable=NO,scrollbars=NO');\">$imgNovo NOVA REUNIÃO</a>"; |
||||||
|
$id = isset($_GET["id"]) ? $_GET["id"] : ''; |
||||||
|
$idDelete = isset($_GET["idDelete"]) ? $_GET["idDelete"] : null; |
||||||
|
|
||||||
|
//Complete para buscar parâmetros (no caso multiempresas terá somente 1 que é nome) |
||||||
|
$compl = ''; |
||||||
|
if (isset($_REQUEST['paramPesquisa']) && $_REQUEST['paramPesquisa']) { |
||||||
|
$compl = PreparaLike($filtro, true); |
||||||
|
$compl = sprintf("AND (a.nome ILIKE %s )", $compl); |
||||||
|
} |
||||||
|
|
||||||
|
$statusAtv = $statusAtivo ? "AND status = 1" : "AND status = 0"; |
||||||
|
$query = "SELECT a.id, a.token, a.meet_id, a.nome, a.status, a.data_reg, count(b.id) as visitas |
||||||
|
FROM pbx_meet a |
||||||
|
INNER JOIN pbx_meet_people b ON a.meet_id = b.meet_id |
||||||
|
WHERE 1 = 1 $compl "; |
||||||
|
$query .= " GROUP BY a.id, a.meet_id, a.nome, a.status, a.data_reg"; |
||||||
|
$params = "¶mPesquisa={$filtro}&status=$statusAtivo"; |
||||||
|
$links = PaginaDados($idProg, $query, $params, $regPagina, $offSet, $pagMostra, true); |
||||||
|
|
||||||
|
$result = pg_query($dbcon, $query); |
||||||
|
$linha = ""; |
||||||
|
while ($dados = @pg_fetch_array($result)) { |
||||||
|
$id = $dados["id"]; |
||||||
|
$nome = mb_convert_encoding(urldecode($dados["nome"]), 'ISO-8859-1', 'UTF-8'); |
||||||
|
$nome = strtoupper(RemoveAcentos($nome)); |
||||||
|
//$invite = "<a href=\"javaScript:NovaJanelaFull('index.php?idProg=373&meet={$dados["nome"]}', 'meet', '$janW', '$janH', 'resizable=NO,scrollbars=NO');\">ENTRAR NA REUNIÃO</a>"; |
||||||
|
$invite = "<a href=\"javaScript:NovaJanelaFull('index.php?idProg=373&t={$dados["token"]}&action=meeting', 'meet', '$janW', '$janH', 'resizable=NO,scrollbars=NO');\">ENTRAR NA REUNIÃO</a>"; |
||||||
|
$visitas = $dados["visitas"]; |
||||||
|
$status = GetStatusModulo($dados['status'], true); |
||||||
|
$dataReg = date('d/m/Y H:i:s', strtotime($dados['data_reg'])); |
||||||
|
|
||||||
|
$delete = $dados['status'] == 1 ? "<a href='index.php?idProg=369&idDelete=$id'>{$imgDelete}</a>" : $imgDelete; |
||||||
|
$linha .= "<tr> |
||||||
|
<td align=\"center\">$id</td> |
||||||
|
<td align=\"left\" nowrap>$nome</td> |
||||||
|
<td width=\"20\" align=\"center\">$visitas</td> |
||||||
|
<td width=\"20\" align=\"center\">$status</td> |
||||||
|
<td width=\"20\" align=\"center\">$dataReg</td> |
||||||
|
<td width=\"20\" align=\"center\" style=\"border-right: 0\">$invite</td> |
||||||
|
</tr>"; |
||||||
|
} |
||||||
|
|
||||||
|
$smarty->assign('erro', $erro); |
||||||
|
$smarty->assign("linhas", $linha); |
||||||
|
$smarty->assign("paramPesquisa", $paramPesquisa); |
||||||
|
$smarty->assign("imgNovo", $imgNovo); |
||||||
|
$smarty->assign("imgLcFixo", $imgLcFixo); |
||||||
|
$smarty->assign("links", $links); |
||||||
|
$smarty->assign("pagMostra", $pagMostra); |
||||||
|
$smarty->assign("totalReg", $totalReg); |
||||||
|
$smarty->assign("status", $statusAtivo); |
||||||
|
$smarty->assign("pgn", $_REQUEST["pgn"]); |
||||||
|
$smarty->assign("bloco", $_REQUEST["bloco"]); |
||||||
|
$smarty->assign("pg", $_REQUEST["pg"]); |
||||||
|
|
||||||
|
GetTemplate($smarty, $tpl); |
Before Width: | Height: | Size: 17 KiB |
File diff suppressed because one or more lines are too long
@ -0,0 +1,64 @@ |
|||||||
|
{capture name="display"} |
||||||
|
<input name="pesquisa" type="hidden" value="1" /> |
||||||
|
<input name="pgn" type="hidden" value="{$pgn}" /> |
||||||
|
<input name="bloco" type="hidden" value="{$bloco}" /> |
||||||
|
<input name="pg" type="hidden" value="{$pg}" /> |
||||||
|
<table width="99%" align="center" border="0" cellspacing="0" cellpadding="2" > |
||||||
|
<tr> |
||||||
|
<td> |
||||||
|
<table class="filtro" border="0" cellpadding="2" cellspacing="0"> |
||||||
|
<tr> |
||||||
|
<td>Palavra Chave</td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td> |
||||||
|
<input name="paramPesquisa" type="text" id="paramPesquisa" size="20" value="{$paramPesquisa}"/> |
||||||
|
</td> |
||||||
|
<td> |
||||||
|
<input name="btConsulta" type="submit" id="btConsulta" value="Consultar"> |
||||||
|
</td> |
||||||
|
<td align="right" nowrap="nowrap" width="90%"> |
||||||
|
{$imgLcFixo} |
||||||
|
</td> |
||||||
|
<td align="right" nowrap="nowrap" width="90%"> |
||||||
|
{$imgNovo} |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td> |
||||||
|
<table width="100%" class="grid" border="0" cellspacing="0" cellpadding="2"> |
||||||
|
<tr> |
||||||
|
<th width="10" align="center">Id</th> |
||||||
|
<th align="left" nowrap width='50%'>Nome</th> |
||||||
|
<th align="center">Visitas</th> |
||||||
|
<th align="center">Status</th> |
||||||
|
<th align="center">Criada</th> |
||||||
|
<th align="center">Entrar</th> |
||||||
|
</tr> |
||||||
|
{$linhas} |
||||||
|
<tr> |
||||||
|
<th align="center" colspan="10"> |
||||||
|
<table width="100%" class="grid" border="0" cellspacing="0" cellpadding="2"> |
||||||
|
<tr> |
||||||
|
<th align="center" style="width: 90%; border:0;">{$links}</th> |
||||||
|
<th align="right" style="width: 10%; border:0;">{$pagMostra}/{$totalReg}</th> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</th> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
{if $erro} |
||||||
|
<table> |
||||||
|
<tr> |
||||||
|
<td>{$erro}</td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
{/if} |
||||||
|
|
||||||
|
{/capture} |
@ -1,38 +1,50 @@ |
|||||||
{capture name="subDisplay"} |
{capture name="subDisplay"} |
||||||
<table width="100%" class="formCad" border="0" cellspacing="0" cellpadding="2"> |
<table width="100%" class="formCad" border="0" cellspacing="0" cellpadding="2"> |
||||||
<tr> |
<tr> |
||||||
<th align="left">Dominio WS</th> |
<th>WhatsApp</th> |
||||||
<td> |
</tr> |
||||||
<input name="prm_sk_host_chat" type="text" id="prm_sk_host_chat" value="{$prm_sk_host_chat}" size="50" maxlength="255"> |
<tr> |
||||||
</td> |
<td> |
||||||
</tr> |
<table width="100%" class="formCad" border="0" cellspacing="0" cellpadding="2"> |
||||||
|
<tr> |
||||||
|
<th align="left">Dominio WS</th> |
||||||
|
<td> |
||||||
|
<input name="prm_sk_host_chat" type="text" id="prm_sk_host_chat" value="{$prm_sk_host_chat}" size="30" maxlength="255"> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<th align="left">Servidor API</th> |
||||||
|
<td> |
||||||
|
<input name="prm_chat_api" type="text" id="prm_chat_api" value="{$prm_chat_api}" size="30" maxlength="255" /> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
|
||||||
<tr> |
<tr> |
||||||
<th align="left">Servidor API</th> |
<td><hr></td> |
||||||
<td> |
</tr> |
||||||
<input name="prm_chat_api" type="text" id="prm_chat_api" value="{$prm_chat_api}" size="50" maxlength="255" /> |
|
||||||
</td> |
|
||||||
</tr> |
|
||||||
|
|
||||||
<tr> |
|
||||||
<th align="left">Api Supervisor</th> |
|
||||||
<td> |
|
||||||
<input name="prm_chat_api_supervisor" type="text" id="prm_chat_api_supervisor" value="{$prm_chat_api_supervisor}" size="50" maxlength="255"> |
|
||||||
</td> |
|
||||||
</tr> |
|
||||||
|
|
||||||
<tr> |
|
||||||
<th align="left">Atendimento Simultâneo</th> |
|
||||||
<td> |
|
||||||
<input name="prm_media_simultaneo" type="text" id="prm_media_simultaneo" value="{$prm_media_simultaneo}" maxlength="2"> |
|
||||||
</td> |
|
||||||
</tr> |
|
||||||
|
|
||||||
<tr> |
<tr> |
||||||
<td> </td> |
<th>Vídeo Conferência</th> |
||||||
<td> |
</tr> |
||||||
<input name="btGravar" type="submit" id="btGravar" value="Gravar"> |
<tr> |
||||||
</td> |
<td> |
||||||
</tr> |
<table width="100%" class="formCad" border="0" cellspacing="0" cellpadding="2"> |
||||||
</table> |
<tr> |
||||||
|
<th align="left">URL Jitsi</th> |
||||||
|
<td> |
||||||
|
<input name="prm_url_meet" type="text" id="prm_url_meet" value="{$prm_url_meet}" size="30" maxlength="250"> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<td> </td> |
||||||
|
<td> |
||||||
|
<input name="btGravar" type="submit" id="btGravar" value="Salvar"> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
{/capture} |
{/capture} |
Loading…
Reference in new issue