renamed debug class to logger
This commit is contained in:
parent
77e51b40b8
commit
3b2948d4dd
13 changed files with 56 additions and 55 deletions
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
|
|
||||||
#include "core/servercontroller.h"
|
#include "core/servercontroller.h"
|
||||||
#include "debug.h"
|
#include "logger.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include <QQuickStyle>
|
#include <QQuickStyle>
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ void AmneziaApplication::init()
|
||||||
QCoreApplication::exit(-1);
|
QCoreApplication::exit(-1);
|
||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
m_engine->rootContext()->setContextProperty("Debug", &Debug::Instance());
|
m_engine->rootContext()->setContextProperty("Debug", &Logger::Instance());
|
||||||
m_uiLogic->registerPagesLogic();
|
m_uiLogic->registerPagesLogic();
|
||||||
|
|
||||||
#if defined(Q_OS_IOS)
|
#if defined(Q_OS_IOS)
|
||||||
|
@ -113,7 +113,7 @@ void AmneziaApplication::init()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_settings->isSaveLogs()) {
|
if (m_settings->isSaveLogs()) {
|
||||||
if (!Debug::init()) {
|
if (!Logger::init()) {
|
||||||
qWarning() << "Initialization of debug subsystem failed";
|
qWarning() << "Initialization of debug subsystem failed";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,7 @@ bool AmneziaApplication::parseCommands()
|
||||||
m_parser.process(*this);
|
m_parser.process(*this);
|
||||||
|
|
||||||
if (m_parser.isSet(c_cleanup)) {
|
if (m_parser.isSet(c_cleanup)) {
|
||||||
Debug::cleanUp();
|
Logger::cleanUp();
|
||||||
QTimer::singleShot(100, this, [this]{
|
QTimer::singleShot(100, this, [this]{
|
||||||
quit();
|
quit();
|
||||||
});
|
});
|
||||||
|
|
|
@ -43,8 +43,8 @@ HEADERS += \
|
||||||
core/scripts_registry.h \
|
core/scripts_registry.h \
|
||||||
core/server_defs.h \
|
core/server_defs.h \
|
||||||
core/servercontroller.h \
|
core/servercontroller.h \
|
||||||
debug.h \
|
|
||||||
defines.h \
|
defines.h \
|
||||||
|
logger.h \
|
||||||
managementserver.h \
|
managementserver.h \
|
||||||
platforms/ios/MobileUtils.h \
|
platforms/ios/MobileUtils.h \
|
||||||
platforms/linux/leakdetector.h \
|
platforms/linux/leakdetector.h \
|
||||||
|
@ -102,7 +102,7 @@ SOURCES += \
|
||||||
core/scripts_registry.cpp \
|
core/scripts_registry.cpp \
|
||||||
core/server_defs.cpp \
|
core/server_defs.cpp \
|
||||||
core/servercontroller.cpp \
|
core/servercontroller.cpp \
|
||||||
debug.cpp \
|
logger.cpp \
|
||||||
main.cpp \
|
main.cpp \
|
||||||
managementserver.cpp \
|
managementserver.cpp \
|
||||||
platforms/ios/MobileUtils.cpp \
|
platforms/ios/MobileUtils.cpp \
|
||||||
|
|
|
@ -79,7 +79,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug().noquote() << "EXEC" << lineToExec;
|
qDebug().noquote() << "EXEC" << lineToExec;
|
||||||
Debug::appendSshLog("Run command:" + lineToExec);
|
Logger::appendSshLog("Run command:" + lineToExec);
|
||||||
|
|
||||||
QSharedPointer<SshRemoteProcess> proc = client->createRemoteProcess(lineToExec.toUtf8());
|
QSharedPointer<SshRemoteProcess> proc = client->createRemoteProcess(lineToExec.toUtf8());
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||||
QString s = proc->readAllStandardOutput();
|
QString s = proc->readAllStandardOutput();
|
||||||
|
|
||||||
if (s != "." && !s.isEmpty()) {
|
if (s != "." && !s.isEmpty()) {
|
||||||
Debug::appendSshLog("Output: " + s);
|
Logger::appendSshLog("Output: " + s);
|
||||||
qDebug().noquote() << "stdout" << s;
|
qDebug().noquote() << "stdout" << s;
|
||||||
}
|
}
|
||||||
if (cbReadStdOut) cbReadStdOut(s, proc);
|
if (cbReadStdOut) cbReadStdOut(s, proc);
|
||||||
|
@ -114,7 +114,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||||
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, &wait, [proc, cbReadStdErr](){
|
QObject::connect(proc.data(), &SshRemoteProcess::readyReadStandardError, &wait, [proc, cbReadStdErr](){
|
||||||
QString s = proc->readAllStandardError();
|
QString s = proc->readAllStandardError();
|
||||||
if (s != "." && !s.isEmpty()) {
|
if (s != "." && !s.isEmpty()) {
|
||||||
Debug::appendSshLog("Output: " + s);
|
Logger::appendSshLog("Output: " + s);
|
||||||
qDebug().noquote() << "stderr" << s;
|
qDebug().noquote() << "stderr" << s;
|
||||||
}
|
}
|
||||||
if (cbReadStdErr) cbReadStdErr(s, proc);
|
if (cbReadStdErr) cbReadStdErr(s, proc);
|
||||||
|
@ -140,7 +140,7 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
|
||||||
const std::function<void (const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdErr)
|
const std::function<void (const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdErr)
|
||||||
{
|
{
|
||||||
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
|
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
|
||||||
Debug::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + ":\n" + script);
|
Logger::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + ":\n" + script);
|
||||||
|
|
||||||
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
|
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
|
||||||
if (e) return e;
|
if (e) return e;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include "sshconnection.h"
|
#include "sshconnection.h"
|
||||||
#include "sshremoteprocess.h"
|
#include "sshremoteprocess.h"
|
||||||
#include "debug.h"
|
#include "logger.h"
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
@ -7,7 +9,6 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "debug.h"
|
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
@ -15,9 +16,9 @@
|
||||||
#include <core/ipcclient.h>
|
#include <core/ipcclient.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QFile Debug::m_file;
|
QFile Logger::m_file;
|
||||||
QTextStream Debug::m_textStream;
|
QTextStream Logger::m_textStream;
|
||||||
QString Debug::m_logFileName = QString("%1.log").arg(APPLICATION_NAME);
|
QString Logger::m_logFileName = QString("%1.log").arg(APPLICATION_NAME);
|
||||||
|
|
||||||
void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
|
void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
|
||||||
{
|
{
|
||||||
|
@ -30,32 +31,32 @@ void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug::m_textStream << qFormatLogMessage(type, context, msg) << Qt::endl << Qt::flush;
|
Logger::m_textStream << qFormatLogMessage(type, context, msg) << Qt::endl << Qt::flush;
|
||||||
Debug::appendAllLog(qFormatLogMessage(type, context, msg));
|
Logger::appendAllLog(qFormatLogMessage(type, context, msg));
|
||||||
|
|
||||||
std::cout << qFormatLogMessage(type, context, msg).toStdString() << std::endl << std::flush;
|
std::cout << qFormatLogMessage(type, context, msg).toStdString() << std::endl << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug &Debug::Instance()
|
Logger &Logger::Instance()
|
||||||
{
|
{
|
||||||
static Debug s;
|
static Logger s;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debug::appendSshLog(const QString &log)
|
void Logger::appendSshLog(const QString &log)
|
||||||
{
|
{
|
||||||
QString dt = QDateTime::currentDateTime().toString();
|
QString dt = QDateTime::currentDateTime().toString();
|
||||||
Instance().m_sshLog.append(dt + ": " + log + "\n");
|
Instance().m_sshLog.append(dt + ": " + log + "\n");
|
||||||
emit Instance().sshLogChanged(Instance().sshLog());
|
emit Instance().sshLogChanged(Instance().sshLog());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debug::appendAllLog(const QString &log)
|
void Logger::appendAllLog(const QString &log)
|
||||||
{
|
{
|
||||||
Instance().m_allLog.append(log + "\n");
|
Instance().m_allLog.append(log + "\n");
|
||||||
emit Instance().allLogChanged(Instance().allLog());
|
emit Instance().allLogChanged(Instance().allLog());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Debug::init()
|
bool Logger::init()
|
||||||
{
|
{
|
||||||
qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} %{type} %{message}");
|
qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} %{type} %{message}");
|
||||||
|
|
||||||
|
@ -80,7 +81,7 @@ bool Debug::init()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debug::deInit()
|
void Logger::deInit()
|
||||||
{
|
{
|
||||||
qInstallMessageHandler(0);
|
qInstallMessageHandler(0);
|
||||||
qSetMessagePattern("%{message}");
|
qSetMessagePattern("%{message}");
|
||||||
|
@ -88,17 +89,17 @@ void Debug::deInit()
|
||||||
m_file.close();
|
m_file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Debug::userLogsDir()
|
QString Logger::userLogsDir()
|
||||||
{
|
{
|
||||||
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/log";
|
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/log";
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Debug::userLogsFilePath()
|
QString Logger::userLogsFilePath()
|
||||||
{
|
{
|
||||||
return userLogsDir() + QDir::separator() + m_logFileName;
|
return userLogsDir() + QDir::separator() + m_logFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Debug::getLogFile()
|
QString Logger::getLogFile()
|
||||||
{
|
{
|
||||||
m_file.flush();
|
m_file.flush();
|
||||||
QFile file(userLogsFilePath());
|
QFile file(userLogsFilePath());
|
||||||
|
@ -107,7 +108,7 @@ QString Debug::getLogFile()
|
||||||
return file.readAll();
|
return file.readAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Debug::openLogsFolder()
|
bool Logger::openLogsFolder()
|
||||||
{
|
{
|
||||||
QString path = userLogsDir();
|
QString path = userLogsDir();
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
@ -120,7 +121,7 @@ bool Debug::openLogsFolder()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Debug::openServiceLogsFolder()
|
bool Logger::openServiceLogsFolder()
|
||||||
{
|
{
|
||||||
QString path = Utils::systemLogPath();
|
QString path = Utils::systemLogPath();
|
||||||
path = "file:///" + path;
|
path = "file:///" + path;
|
||||||
|
@ -128,12 +129,12 @@ bool Debug::openServiceLogsFolder()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Debug::appLogFileNamePath()
|
QString Logger::appLogFileNamePath()
|
||||||
{
|
{
|
||||||
return m_file.fileName();
|
return m_file.fileName();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debug::clearLogs()
|
void Logger::clearLogs()
|
||||||
{
|
{
|
||||||
bool isLogActive = m_file.isOpen();
|
bool isLogActive = m_file.isOpen();
|
||||||
m_file.close();
|
m_file.close();
|
||||||
|
@ -149,7 +150,7 @@ void Debug::clearLogs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debug::clearServiceLogs()
|
void Logger::clearServiceLogs()
|
||||||
{
|
{
|
||||||
#ifdef AMNEZIA_DESKTOP
|
#ifdef AMNEZIA_DESKTOP
|
||||||
IpcClient *m_IpcClient = new IpcClient;
|
IpcClient *m_IpcClient = new IpcClient;
|
||||||
|
@ -171,7 +172,7 @@ void Debug::clearServiceLogs()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debug::cleanUp()
|
void Logger::cleanUp()
|
||||||
{
|
{
|
||||||
clearLogs();
|
clearLogs();
|
||||||
QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
|
QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef DEBUG_H
|
#ifndef LOGGER_H
|
||||||
#define DEBUG_H
|
#define LOGGER_H
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
@ -9,14 +9,14 @@
|
||||||
|
|
||||||
#include "ui/property_helper.h"
|
#include "ui/property_helper.h"
|
||||||
|
|
||||||
class Debug : public QObject
|
class Logger : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
AUTO_PROPERTY(QString, sshLog)
|
AUTO_PROPERTY(QString, sshLog)
|
||||||
AUTO_PROPERTY(QString, allLog)
|
AUTO_PROPERTY(QString, allLog)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static Debug& Instance();
|
static Logger& Instance();
|
||||||
|
|
||||||
static void appendSshLog(const QString &log);
|
static void appendSshLog(const QString &log);
|
||||||
static void appendAllLog(const QString &log);
|
static void appendAllLog(const QString &log);
|
||||||
|
@ -35,9 +35,9 @@ public:
|
||||||
static QString getLogFile();
|
static QString getLogFile();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Debug() {}
|
Logger() {}
|
||||||
Debug(Debug const &) = delete;
|
Logger(Logger const &) = delete;
|
||||||
Debug& operator= (Debug const&) = delete;
|
Logger& operator= (Logger const&) = delete;
|
||||||
|
|
||||||
static QString userLogsDir();
|
static QString userLogsDir();
|
||||||
|
|
||||||
|
@ -48,4 +48,4 @@ private:
|
||||||
friend void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
|
friend void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DEBUG_H
|
#endif // LOGGER_H
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
#include "debug.h"
|
#include "logger.h"
|
||||||
#include "ikev2_vpn_protocol_windows.h"
|
#include "ikev2_vpn_protocol_windows.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <QTcpServer>
|
#include <QTcpServer>
|
||||||
#include <QRandomGenerator>
|
#include <QRandomGenerator>
|
||||||
|
|
||||||
#include "debug.h"
|
#include "logger.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#include "openvpnprotocol.h"
|
#include "openvpnprotocol.h"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "shadowsocksvpnprotocol.h"
|
#include "shadowsocksvpnprotocol.h"
|
||||||
|
|
||||||
#include "debug.h"
|
#include "logger.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
#include "debug.h"
|
#include "logger.h"
|
||||||
#include "wireguardprotocol.h"
|
#include "wireguardprotocol.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
#include "debug.h"
|
#include "logger.h"
|
||||||
|
|
||||||
const char Settings::cloudFlareNs1[] = "1.1.1.1";
|
const char Settings::cloudFlareNs1[] = "1.1.1.1";
|
||||||
const char Settings::cloudFlareNs2[] = "1.0.0.1";
|
const char Settings::cloudFlareNs2[] = "1.0.0.1";
|
||||||
|
@ -211,9 +211,9 @@ void Settings::setSaveLogs(bool enabled)
|
||||||
{
|
{
|
||||||
m_settings.setValue("Conf/saveLogs", enabled);
|
m_settings.setValue("Conf/saveLogs", enabled);
|
||||||
if (!isSaveLogs()) {
|
if (!isSaveLogs()) {
|
||||||
Debug::deInit();
|
Logger::deInit();
|
||||||
} else {
|
} else {
|
||||||
if (!Debug::init()) {
|
if (!Logger::init()) {
|
||||||
qWarning() << "Initialization of debug subsystem failed";
|
qWarning() << "Initialization of debug subsystem failed";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "AppSettingsLogic.h"
|
#include "AppSettingsLogic.h"
|
||||||
|
|
||||||
#include "debug.h"
|
#include "logger.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "ui/qautostart.h"
|
#include "ui/qautostart.h"
|
||||||
#include "ui/uilogic.h"
|
#include "ui/uilogic.h"
|
||||||
|
@ -62,18 +62,18 @@ void AppSettingsLogic::onCheckBoxSaveLogsCheckedToggled(bool checked)
|
||||||
|
|
||||||
void AppSettingsLogic::onPushButtonOpenLogsClicked()
|
void AppSettingsLogic::onPushButtonOpenLogsClicked()
|
||||||
{
|
{
|
||||||
Debug::openLogsFolder();
|
Logger::openLogsFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppSettingsLogic::onPushButtonExportLogsClicked()
|
void AppSettingsLogic::onPushButtonExportLogsClicked()
|
||||||
{
|
{
|
||||||
uiLogic()->saveTextFile(tr("Save log"), "AmneziaVPN.log", ".log", Debug::getLogFile());
|
uiLogic()->saveTextFile(tr("Save log"), "AmneziaVPN.log", ".log", Logger::getLogFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppSettingsLogic::onPushButtonClearLogsClicked()
|
void AppSettingsLogic::onPushButtonClearLogsClicked()
|
||||||
{
|
{
|
||||||
Debug::clearLogs();
|
Logger::clearLogs();
|
||||||
Debug::clearServiceLogs();
|
Logger::clearServiceLogs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppSettingsLogic::onPushButtonBackupAppConfigClicked()
|
void AppSettingsLogic::onPushButtonBackupAppConfigClicked()
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
#include "ui/qautostart.h"
|
#include "ui/qautostart.h"
|
||||||
|
|
||||||
#include "debug.h"
|
#include "logger.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "uilogic.h"
|
#include "uilogic.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
@ -190,9 +190,9 @@ void UiLogic::keyPressEvent(Qt::Key key)
|
||||||
case Qt::Key_AsciiTilde:
|
case Qt::Key_AsciiTilde:
|
||||||
case Qt::Key_QuoteLeft: emit toggleLogPanel();
|
case Qt::Key_QuoteLeft: emit toggleLogPanel();
|
||||||
break;
|
break;
|
||||||
case Qt::Key_L: Debug::openLogsFolder();
|
case Qt::Key_L: Logger::openLogsFolder();
|
||||||
break;
|
break;
|
||||||
case Qt::Key_K: Debug::openServiceLogsFolder();
|
case Qt::Key_K: Logger::openServiceLogsFolder();
|
||||||
break;
|
break;
|
||||||
#ifdef QT_DEBUG
|
#ifdef QT_DEBUG
|
||||||
case Qt::Key_Q:
|
case Qt::Key_Q:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue