Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
@@ -0,0 +1,47 @@
|
||||
#
|
||||
# Exported scripts management for uploading, downloading and removing exported scripts
|
||||
#
|
||||
# $Id: CMakeLists.txt 51804 2020-07-31 04:47:28Z fritsche_h $
|
||||
# $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/CMakeLists.txt $
|
||||
# $Author: fritsche_h $
|
||||
#
|
||||
# Created by Mike Wille, SLTE, 2020-03-23
|
||||
#
|
||||
|
||||
SET(src
|
||||
tpm2_gui_plugin.cpp
|
||||
tpm2_gui_plugin.h
|
||||
tpm2_exported_scripts_dialog.cpp
|
||||
tpm2_exported_scripts_dialog.h
|
||||
logger.cpp
|
||||
logger.h
|
||||
)
|
||||
|
||||
QT5_WRAP_UI(src
|
||||
tpm2_exported_scripts_dialog.ui
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY2(WS Thp2ManagementGuiPlugin MODULE ${src})
|
||||
NGL_TARGET_COMPILE_DEFINITIONS(Thp2ManagementGuiPlugin PRIVATE L=QStringLiteral)
|
||||
NGL_TARGET_LINK_LIBRARIES(Thp2ManagementGuiPlugin
|
||||
Thp2Common
|
||||
Thp2CommonCore
|
||||
Tpm2GuiPluginInterface
|
||||
Qt5::Widgets
|
||||
Qt5::Network
|
||||
Qt5::WebSockets # get live changes from the server
|
||||
NGLLogger
|
||||
LtsWidgets
|
||||
LtsSession
|
||||
LtsDataView
|
||||
nglcompression # tar
|
||||
nglsettings # windoze / OC thp server hostname
|
||||
ngltools # NGL::Connections::eConnection and findProjectConfigFile
|
||||
#AsmDataPluginInterface
|
||||
#ExmDataPluginInterface
|
||||
LtsSessionData
|
||||
#httq
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(Thp2ManagementGuiPlugin)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Logging for THP2
|
||||
* $Id: logger.cpp 46225 2019-03-25 13:59:56Z rudolphus_g $
|
||||
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/logger.cpp $
|
||||
* $Author: rudolphus_g $
|
||||
*
|
||||
* Created by Mike Wille, ESZS, 2024-02-20
|
||||
*/
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
LOGGER_T getLogger(const char *name)
|
||||
{
|
||||
static const char ns[] = "TPM2.";
|
||||
std::string loggerName;
|
||||
const size_t len = strlen(name);
|
||||
loggerName.reserve(sizeof(ns) + len);
|
||||
loggerName.append(ns, sizeof(ns) - 1);
|
||||
loggerName.append(name, len);
|
||||
|
||||
try
|
||||
{
|
||||
return NGL::NGLLoggerBootstrap::getNGLRootLogger().
|
||||
getNGLLogger(loggerName);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
fprintf(stderr, "Error retrieving logger: %s", ex.what());
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TakeHomePackageManager
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Logging for THP2
|
||||
* $Id: logger.h 46225 2019-03-25 13:59:56Z rudolphus_g $
|
||||
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/logger.h $
|
||||
* $Author: rudolphus_g $
|
||||
*
|
||||
* Created by Mike Wille, ESZS, 2024-02-20
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
typedef NGL::INGLLogger &LOGGER_T;
|
||||
|
||||
LOGGER_T getLogger(const char *name);
|
||||
inline LOGGER_T getLogger(const QString &loggerName)
|
||||
{
|
||||
return getLogger(loggerName.toUtf8().constData());
|
||||
}
|
||||
} // namespace TakeHomePackageManager
|
||||
+989
@@ -0,0 +1,989 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Show the exported scripts and allow them to upload them to the server
|
||||
* $Id: tpm_exported_scripts_dialog.cpp 53111 2021-03-22 08:21:29Z wille_m $
|
||||
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_exported_scripts_dialog.cpp $
|
||||
* $Author: wille_m $
|
||||
*
|
||||
* Created by Mike Wille, ESZS, 2024-02-20
|
||||
*/
|
||||
|
||||
/* TODO: FIXME: adjust data.json when uploading file, as data.json contains ALL
|
||||
* scripts, but we only upload ONE script at a time - so remove all scripts but
|
||||
* the one to be uploaded from data.json (but don't touch the main data.json!!)
|
||||
*/
|
||||
|
||||
#include "ui_tpm2_exported_scripts_dialog.h"
|
||||
#include "tpm2_exported_scripts_dialog.h"
|
||||
|
||||
#include <Common2/tpm_process_dialog.h>
|
||||
#include <Common2/tpm_file_copy.h>
|
||||
#include <Common2Core/tpm_tools.h>
|
||||
|
||||
#include <nglcompression/nglcomp_compress.h>
|
||||
#include <nglsettings/nglsettings_connections.h>
|
||||
#include <nglsettings/nglsettings_settings.h>
|
||||
#include <ngltools/ngltools_filesystem.h>
|
||||
#include <LtsWidgets/lts_message_dialog.h>
|
||||
#include <LtsWidgets/lts_global_wait_dialog.h>
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <LtsSession/lts_session_data.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QProcess>
|
||||
#include <QTemporaryFile>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
#include <QWebSocket>
|
||||
#include <QLocale>
|
||||
#include <QTimer>
|
||||
#include <QTemporaryDir>
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
#ifndef ELOG
|
||||
#define ELOG ERROR_LOG(m_logger) // polyfill
|
||||
#endif
|
||||
|
||||
using err = std::runtime_error;
|
||||
err error(const QString &msg) { return err(msg.toStdString()); }
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
Compressor::Compressor()
|
||||
: QObject(nullptr)
|
||||
{}
|
||||
|
||||
|
||||
void Compressor::go(ProcessOverlay *processOverlay, QIODevice &out, const QString &sourceDir, const QString &destRootDir) noexcept(false)
|
||||
{
|
||||
//std::unique_ptr<NGL::Compress> compress(new NGL::Compress(nullptr));
|
||||
auto *compress = new NGL::Compress(nullptr);
|
||||
|
||||
bool firstTime = true;
|
||||
connect(compress, &NGL::Compress::compressInfoEx,
|
||||
this, [processOverlay, &firstTime](qint64 totalFiles, qint64 curFile, qint64 fileSize, qint64 curPos, const QString &curName, const qint64 bytesTotal)
|
||||
{
|
||||
if (firstTime)
|
||||
{
|
||||
firstTime = false;
|
||||
processOverlay->setStarted(totalFiles, bytesTotal);
|
||||
processOverlay->setNewFile(curFile, fileSize, curName);
|
||||
processOverlay->setStatus(tr("Compressing files ..."));
|
||||
}
|
||||
|
||||
processOverlay->setFileProgress(curPos, fileSize);
|
||||
});
|
||||
|
||||
QString errorString;
|
||||
connect(compress, &NGL::Compress::error, // finished is ALWAYS called by nglcompress! delete on finished!
|
||||
this, [&errorString](const QString &errStr)
|
||||
{
|
||||
qWarning() << "## COMPRESS ERROR" << errStr;
|
||||
errorString = errStr;
|
||||
});
|
||||
|
||||
connect(this, &Compressor::abort,
|
||||
compress, [&errorString]()
|
||||
{
|
||||
errorString = L("Aborted.");
|
||||
});
|
||||
|
||||
|
||||
compress->compress(&out, sourceDir, destRootDir);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
awaitWithCancelationSignalx(compress, &NGL::Compress::finished, this, &Compressor::abort); // NOTE: finished is always called - even ofter abort!
|
||||
}
|
||||
catch(const std::runtime_error &err)
|
||||
{
|
||||
qWarning() << "## aborted" << err.what();
|
||||
compress->abort();
|
||||
compress->waitForFinished();
|
||||
compress->deleteLater();
|
||||
throw err;
|
||||
}
|
||||
|
||||
|
||||
const bool success = compress->waitForFinished();//success();
|
||||
|
||||
compress->deleteLater();
|
||||
|
||||
|
||||
if (!success || !errorString.isEmpty())
|
||||
{
|
||||
throw error(L("compress failed: %1").arg(errorString));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*/
|
||||
* every process needs to deleteLater itself
|
||||
* every process must listen to signalTimeout & signalCancel (+ call deleteLater in them!)
|
||||
* only connect to signalDone & signalError when using a request
|
||||
* disconnect signalTimeout & signalCancel before you do another step
|
||||
* a QNetworkReply will always call either signalDone or signalError - call deleteLater in both of them!
|
||||
* aborting a QNetworkReply finally calls signalError, so call deleteLater at this point
|
||||
* nglcompress will always call finished, it doesn't matter if it failed or not - call deleteLater at this point
|
||||
/*/
|
||||
HttpProcess::HttpProcess(LtsWidgets::LtsDialog *dialog, const QString &statusMessage)
|
||||
: QObject()
|
||||
, m_logger(getLogger("ExportedScriptsDialog.HttpProcess"))
|
||||
, m_process(new ProcessOverlay)
|
||||
, m_nam(new QNetworkAccessManager)
|
||||
{
|
||||
connect(m_process, &ProcessOverlay::signalCancel,
|
||||
this, [this]()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "m_wasCanceled!!";
|
||||
m_process->setCancelEnabled(false);
|
||||
emit signalCancel();
|
||||
});
|
||||
|
||||
dialog->setOverlay(m_process);
|
||||
m_process->setStatus(statusMessage);
|
||||
}
|
||||
|
||||
|
||||
HttpProcess::~HttpProcess()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "~HttpProcess";
|
||||
m_process->deleteLater();
|
||||
m_nam->deleteLater();
|
||||
}
|
||||
|
||||
|
||||
QUrl ExportedScriptsDialog::forgeUrl(const QString &path, const QString &protocol)
|
||||
{
|
||||
#ifdef WIN32
|
||||
const bool isTabOC = true;
|
||||
#else // unix
|
||||
const bool isTabOC = false;
|
||||
#endif
|
||||
|
||||
const QString hostname(isTabOC
|
||||
? NGL::Settings(NGL::findProjectConfigFile()).getString(L("TabOC"), L("remoteServerHostname"), L("exctds01"))
|
||||
: NGL::Connections::hostName(NGL::Connections::eConnection::FLM_THP_SERVER));
|
||||
|
||||
const QUrl url(QStringLiteral("%1://%2:%3%4").arg(protocol, hostname).arg(NGL::Connections::port(NGL::Connections::eConnection::FLM_THP_SERVER)).arg(path));
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
QNetworkRequest ExportedScriptsDialog::forgeRequest(const QString &path, const QUrlQuery &q, const QString &contentType)
|
||||
{
|
||||
auto url = forgeUrl(path);
|
||||
url.setQuery(q);
|
||||
|
||||
QNetworkRequest r;
|
||||
if (!contentType.isEmpty())
|
||||
r.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, contentType);
|
||||
r.setUrl(url);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
int getResponseStatus(QNetworkReply *r) noexcept(false)
|
||||
{
|
||||
bool k = false;
|
||||
const int status = r->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&k);
|
||||
if (!k)
|
||||
{
|
||||
throw err("Missing http status in http response");
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void HttpProcess::waitForAnswer(QNetworkReply *r, const int expectedResponseStatus) noexcept(false)
|
||||
{
|
||||
if (!r->isFinished() && r->bytesAvailable() < 1)
|
||||
{
|
||||
// either r errors and emits finished, or the user cancels, which emits abort ... awaitWithCancelationSignal can only listen to ONE signal... that's the 'best' solution that came to my mind without getting into this for too long.... :'(
|
||||
// wait for finished, readyRead or cancel
|
||||
|
||||
std::unique_ptr<NoticeDummy> dummy(new NoticeDummy);
|
||||
connect(r, &QNetworkReply::finished, dummy.get(), &NoticeDummy::signal, Qt::QueuedConnection); // if called before readyRead => error
|
||||
connect(r, &QNetworkReply::readyRead, dummy.get(), &NoticeDummy::signal, Qt::QueuedConnection);
|
||||
|
||||
awaitWithCancelationSignalx(dummy.get(), &NoticeDummy::signal, this, &HttpProcess::signalCancel);
|
||||
}
|
||||
|
||||
|
||||
if (r->error() != QNetworkReply::NetworkError::NoError)
|
||||
{
|
||||
r->deleteLater();
|
||||
throw error(L("network error: %1").arg(r->errorString()));
|
||||
}
|
||||
|
||||
|
||||
const int status = getResponseStatus(r);
|
||||
if (status == expectedResponseStatus)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// not 201 (created) - must be an error message from the server
|
||||
r->deleteLater();
|
||||
const QByteArray data(r->readAll());
|
||||
|
||||
ELOG << "server error: data = '" << data << "' http status: " << r->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
|
||||
QJsonParseError parseError;
|
||||
const QJsonDocument doc(QJsonDocument::fromJson(data, &parseError));
|
||||
|
||||
if (parseError.error != QJsonParseError::NoError)
|
||||
{
|
||||
ELOG << "didn't receive a valid json from server at " << parseError.offset << ": " << parseError.errorString() << " - message was: '" << data << "'";
|
||||
throw err("request failed and error msg from server was malformed");
|
||||
}
|
||||
|
||||
const QString errorMsg(doc.object()[QStringLiteral("error")].toString());
|
||||
|
||||
if (errorMsg.isEmpty())
|
||||
ELOG << "server sent empty error";
|
||||
else
|
||||
ELOG << "server sent error: " << errorMsg;
|
||||
|
||||
//new LtsWidgets::LtsMessageDialogNew(qobject_cast<QWidget *>(this->parent()), tr("The server failed to process the operation"));
|
||||
throw error(L("request failed - server said: %1 (http status = %2)").arg(errorMsg).arg(r->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()));
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply *HttpProcess::post(const QString &path, QIODevice *data, const QUrlQuery &q, const QString &contentType, const QMap<QString, QString> &additionalHeaders) noexcept(false)
|
||||
{
|
||||
auto req = ExportedScriptsDialog::forgeRequest(path, q, contentType);
|
||||
|
||||
QMapIterator<QString, QString> it(additionalHeaders);
|
||||
while (it.hasNext())
|
||||
{
|
||||
it.next();
|
||||
auto key = it.key();
|
||||
auto val = it.value();
|
||||
req.setRawHeader(key.toUtf8(), val.toUtf8());
|
||||
}
|
||||
|
||||
QNetworkReply *r = m_nam->post(req, data);
|
||||
|
||||
bool firstTime = true;
|
||||
connect(r, &QNetworkReply::uploadProgress,
|
||||
this, [this, &firstTime, path](const qint64 bytes, const qint64 bytesTotal)
|
||||
{
|
||||
if (firstTime)
|
||||
{
|
||||
firstTime = false;
|
||||
m_process->setStarted(1, bytesTotal);
|
||||
m_process->setNewFile(1, bytesTotal, path);
|
||||
m_process->setStatus(tr("Uploading ..."));
|
||||
}
|
||||
|
||||
m_process->setFileProgress(bytes, bytesTotal);
|
||||
});
|
||||
|
||||
waitForAnswer(r, 201);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply *HttpProcess::get(const QString &path, const QUrlQuery &q) noexcept(false)
|
||||
{
|
||||
QNetworkReply *r = m_nam->get(ExportedScriptsDialog::forgeRequest(path, q));
|
||||
|
||||
|
||||
bool firstTime = true;
|
||||
connect(r, &QNetworkReply::downloadProgress,
|
||||
this, [this, &firstTime, path](const qint64 bytes, const qint64 bytesTotal)
|
||||
{
|
||||
if (firstTime)
|
||||
{
|
||||
firstTime = false;
|
||||
m_process->setStarted(1, bytesTotal);
|
||||
m_process->setNewFile(1, bytesTotal, path);
|
||||
m_process->setStatus(tr("Downloading ..."));
|
||||
}
|
||||
|
||||
m_process->setFileProgress(bytes, bytesTotal);
|
||||
});
|
||||
|
||||
|
||||
/// wait for first read - to check if it's a 200 or an error message
|
||||
waitForAnswer(r, 200);
|
||||
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply *HttpProcess::deleteResource(const QString &path, const QUrlQuery &q) noexcept(false)
|
||||
{
|
||||
QNetworkReply *r = m_nam->deleteResource(ExportedScriptsDialog::forgeRequest(path, q)); // hahah, because 'delete' is a reserved keyword in c++
|
||||
waitForAnswer(r, 200);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// UploadProcess
|
||||
UploadProcess::UploadProcess(LtsWidgets::LtsDialog *dialog)
|
||||
: HttpProcess(dialog, tr("Preparing Script upload ..."))
|
||||
{}
|
||||
|
||||
|
||||
void UploadProcess::upload(LtsWidgets::LtsDialog *dialog, const QString &rootFolder, const ExportedScriptInformation &esi) noexcept(false)
|
||||
{
|
||||
UploadProcess up(dialog);
|
||||
up.go(rootFolder, esi);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<QTemporaryFile> createTempFile() noexcept(false)
|
||||
{
|
||||
std::unique_ptr<QTemporaryFile> tempFile(new QTemporaryFile);
|
||||
if (!tempFile->open())
|
||||
{
|
||||
const QString msg(L("Cannot open temporary file: %1").arg(tempFile->errorString()));
|
||||
throw error(msg);
|
||||
}
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
|
||||
void UploadProcess::go(const QString &rootFolder, const ExportedScriptInformation &esi) noexcept(false)
|
||||
{
|
||||
// No error & done here - as not a request, but only nglcompress
|
||||
const QString dstDir(esi.id()); // this is the destination directory where bzip2 is going to extract it to (that is, when the user later downloads the thp on his machine) - this can be a relative path!
|
||||
const QString srcDir(scriptLocation(rootFolder, esi.id()));
|
||||
|
||||
|
||||
auto tempFile(createTempFile());
|
||||
|
||||
Compressor compressor;
|
||||
connect(this, &HttpProcess::signalCancel, &compressor, &Compressor::abort);
|
||||
compressor.go(m_process, *tempFile.get(), srcDir, dstDir);
|
||||
|
||||
DEBUG_LOG(m_logger) << "posting file of size " << tempFile->size();
|
||||
tempFile->seek(0);
|
||||
|
||||
QNetworkReply *rep = post(L("/thp2"), tempFile.get(), {}, L("application/octet-stream"), { { L("x-thp-info"), QString::fromUtf8(QJsonDocument(esi.toJson().toObject()).toJson(QJsonDocument::JsonFormat::Compact)) } });
|
||||
rep->deleteLater();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// DownloadProcess
|
||||
DownloadProcess::DownloadProcess(LtsWidgets::LtsDialog *dialog)
|
||||
: HttpProcess(dialog, tr("Preparing Script download ..."))
|
||||
{}
|
||||
|
||||
|
||||
void DownloadProcess::download(LtsWidgets::LtsDialog *dialog, const QString &rootFolder, const QString &scriptId) noexcept(false)
|
||||
{
|
||||
DownloadProcess down(dialog);
|
||||
down.go(dialog, rootFolder, scriptId);
|
||||
}
|
||||
|
||||
|
||||
void DownloadProcess::go(LtsWidgets::LtsDialog *dialog, const QString &rootFolder, const QString &scriptId) noexcept(false)
|
||||
{
|
||||
/// download file
|
||||
auto tempFile = createTempFile();
|
||||
QNetworkReply *rep = get(L("/thp2"), QUrlQuery({{L("scriptId"), scriptId}}));
|
||||
const auto thpInfo(rep->rawHeader(L("x-thp-info").toUtf8()));
|
||||
|
||||
if (thpInfo.isEmpty())
|
||||
{
|
||||
throw err("x-thp-info header missing");
|
||||
}
|
||||
|
||||
const auto esi = ExportedScriptInformation::fromJson(JsonObject::fromJson(thpInfo/*.toUtf8()*/).object());
|
||||
|
||||
|
||||
connect(rep, &QNetworkReply::readyRead,
|
||||
this, [rep, &tempFile]()
|
||||
{
|
||||
tempFile->write(rep->readAll());
|
||||
});
|
||||
|
||||
if (rep->bytesAvailable() > 0)
|
||||
{
|
||||
tempFile->write(rep->readAll());
|
||||
}
|
||||
|
||||
|
||||
if (!rep->isFinished())
|
||||
{
|
||||
try
|
||||
{
|
||||
awaitWithCancelationSignalx(rep, &QNetworkReply::finished, this, &HttpProcess::signalCancel);
|
||||
}
|
||||
catch(const err &err)
|
||||
{
|
||||
rep->deleteLater();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// decompress file
|
||||
m_process->setStatus(tr("Preparing Script decompression ..."));
|
||||
|
||||
std::unique_ptr<NGL::Compress> compress(new NGL::Compress(nullptr));
|
||||
|
||||
tempFile->seek(0);
|
||||
|
||||
|
||||
QTemporaryDir tmpDir;
|
||||
if (!tmpDir.isValid())
|
||||
{
|
||||
throw error(L("Temp dir creation failed: %1").arg(tmpDir.errorString()));
|
||||
}
|
||||
|
||||
bool firstTime = true;
|
||||
connect(compress.get(), &NGL::Compress::uncompressInfo,
|
||||
this, [this, &firstTime](qint64 size, qint64 pos, const QString &file)
|
||||
{
|
||||
if (firstTime)
|
||||
{
|
||||
firstTime = false;
|
||||
m_process->setStarted(1, size);
|
||||
m_process->setNewFile(1, size, file);
|
||||
m_process->setStatus(tr("Decompressing ..."));
|
||||
}
|
||||
|
||||
m_process->setFileProgress(pos, size);
|
||||
});
|
||||
|
||||
QString errorString;
|
||||
connect(compress.get(), &NGL::Compress::error, // finished is ALWAYS called by nglcompress! delete on finished!
|
||||
this, [&errorString](const QString &errStr)
|
||||
{
|
||||
qWarning() << "## COMPRESS ERROR" << errStr;
|
||||
errorString = errStr;
|
||||
});
|
||||
|
||||
compress->uncompress(tempFile.get(), tmpDir.path());
|
||||
|
||||
try
|
||||
{
|
||||
awaitWithCancelationSignalx(compress.get(), &NGL::Compress::finished, this, &HttpProcess::signalCancel);
|
||||
}
|
||||
catch(const std::runtime_error &err)
|
||||
{
|
||||
qWarning() << "## abort" << err.what();
|
||||
compress->abort();
|
||||
compress->waitForFinished();
|
||||
throw err;
|
||||
}
|
||||
|
||||
|
||||
const bool success = compress->waitForFinished();
|
||||
|
||||
if (!success || !errorString.isEmpty())
|
||||
{
|
||||
throw error(L("uncompress failed: %1").arg(errorString));
|
||||
}
|
||||
|
||||
|
||||
const auto scriptLocationString(scriptLocation(rootFolder, scriptId));
|
||||
if (!QDir(scriptLocationString).removeRecursively())
|
||||
{
|
||||
throw error(L("failed to remove existing script directory at %1").arg(scriptLocationString));
|
||||
}
|
||||
|
||||
|
||||
// is cross-device move in production - so copy
|
||||
CopyInfoList fileList;
|
||||
fileList += getFilesFromFolder(tmpDir.path() + QDir::separator() + scriptId, scriptLocationString); // copy script's topics
|
||||
m_process->hide();
|
||||
dialog->unsetOverlay();
|
||||
m_process->hide(); // as copyFiles does a setOverlay, too, we need to get rid of the currently existing loading overlay now
|
||||
copyFiles(fileList, dialog); // ... and wait ...
|
||||
}
|
||||
|
||||
|
||||
|
||||
// RemoveProcess
|
||||
RemoveProcess::RemoveProcess(LtsWidgets::LtsDialog *dialog)
|
||||
: HttpProcess(dialog, tr("Requesting Script deletion on server ..."))
|
||||
{}
|
||||
|
||||
|
||||
void RemoveProcess::remove(LtsWidgets::LtsDialog *dialog, const QString &scriptId) noexcept(false)
|
||||
{
|
||||
RemoveProcess rm(dialog);
|
||||
rm.go(scriptId);
|
||||
}
|
||||
|
||||
|
||||
void RemoveProcess::go(const QString &scriptId) noexcept(false)
|
||||
{
|
||||
deleteResource(L("/thp2"), {{ L("scriptId"), scriptId }});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ExportedScriptsDialog
|
||||
ExportedScriptsDialog::ExportedScriptsDialog(LtsSession::LtsSession *session, QWidget *parent) noexcept(false)
|
||||
: LtsWidgets::LtsDialog(parent)
|
||||
, m_model(new ExportedScriptsModel(this))
|
||||
, m_ui(new Ui::ExportedScriptsDialog2)
|
||||
, m_session(session)
|
||||
, m_logger(getLogger("ExportedScriptsDialog2"))
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_buttonBox->connectButton(LtsWidgets::DialogButton::Remove, this, &ExportedScriptsDialog::removeScriptLocal);
|
||||
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Show)->setEnabled(false); // Show presentation Button
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Remove)->setEnabled(false); // local remove Button
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setEnabled(false); // download from Server
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Import)->setEnabled(false); // Remove from Server Button
|
||||
|
||||
connect(m_ui->ltsDataView, &LtsDataView::LtsDataView::signalSelectionChanged, this, &ExportedScriptsDialog::onSelectionChanged);
|
||||
|
||||
|
||||
setOnlineHelpFilename(QStringLiteral("Script_Editor-Export_Script_Administration"));
|
||||
|
||||
|
||||
if (isOCApp())
|
||||
{
|
||||
m_buttonBox->connectButton(LtsWidgets::DialogButton::Save, this, &ExportedScriptsDialog::downloadScript); // download from Server
|
||||
m_buttonBox->connectButton(LtsWidgets::DialogButton::Show, this, &ExportedScriptsDialog::openScript);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_buttonBox->connectButton(LtsWidgets::DialogButton::Save, this, &ExportedScriptsDialog::uploadScript); // upload to Server
|
||||
m_buttonBox->connectButton(LtsWidgets::DialogButton::Import, this, &ExportedScriptsDialog::removeScriptFromServer);
|
||||
}
|
||||
|
||||
|
||||
QWebSocket *sock = new QWebSocket();
|
||||
sock->setParent(this);
|
||||
|
||||
auto openFn = [this, sock]()
|
||||
{
|
||||
setWsConnectionStatus(WsConnectionStatus::Connecting);
|
||||
sock->open(forgeUrl(L("/thp2"), L("ws")));
|
||||
};
|
||||
|
||||
openFn();
|
||||
|
||||
|
||||
connect(sock, &QWebSocket::connected,
|
||||
this, [this]()
|
||||
{
|
||||
setWsConnectionStatus(WsConnectionStatus::Connected);
|
||||
});
|
||||
|
||||
|
||||
connect(sock, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error),
|
||||
this, [this, sock]()
|
||||
{
|
||||
setWsConnectionStatus(WsConnectionStatus::Error);
|
||||
ELOG << "ws error: " << sock->errorString();
|
||||
}, Qt::QueuedConnection /* prevent crash */);
|
||||
|
||||
|
||||
connect(sock, &QWebSocket::disconnected,
|
||||
this, [this, sock, openFn]()
|
||||
{
|
||||
WARNING_LOG(m_logger) << "disconnected from ws... reconnecting in 3...";
|
||||
setWsConnectionStatus(WsConnectionStatus::Disconnected);
|
||||
|
||||
QTimer *t = new QTimer(sock);
|
||||
t->setInterval(3000);
|
||||
t->setSingleShot(true);
|
||||
|
||||
connect(t, &QTimer::timeout,
|
||||
sock, [t, openFn]()
|
||||
{
|
||||
openFn();
|
||||
t->deleteLater();
|
||||
});
|
||||
|
||||
t->start();
|
||||
}, Qt::QueuedConnection /* prevent crash */);
|
||||
|
||||
|
||||
connect(sock, &QWebSocket::textMessageReceived,
|
||||
this, [this](const QString &msg)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "new msg: '" << msg << "'";
|
||||
m_lastServerContent = msg;
|
||||
m_model->setServerContent(msg);
|
||||
onSelectionChanged(); // aka. enaDisa
|
||||
});
|
||||
|
||||
|
||||
initModel(); // throws!
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
ExportedScriptsDialog::~ExportedScriptsDialog()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
|
||||
bool ExportedScriptsDialog::isOCApp() const
|
||||
{
|
||||
//return true;
|
||||
return (m_session->app() == LtsAppDescriptor::App::OC);
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsDialog::onSelectionChanged()
|
||||
{
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Show)->setEnabled(false); // Show presentation Button
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setEnabled(false); // download from / upload to Server
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Remove)->setEnabled(false); // local remove Button
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Import)->setEnabled(false); // Remove from Server Button
|
||||
|
||||
QTimer::singleShot(500, this, &ExportedScriptsDialog::_onSelectionChanged); // -.-
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsDialog::_onSelectionChanged()
|
||||
{
|
||||
const QModelIndexList mil(m_ui->ltsDataView->selectedRows());
|
||||
|
||||
const bool isOneSelected = (mil.size() == 1);
|
||||
|
||||
bool enableShow = false;
|
||||
bool enableRemoveLocal = false;
|
||||
bool enableDownload = false;
|
||||
bool enableUpload = false;
|
||||
bool enableRemoveServer = false;
|
||||
|
||||
if (isOneSelected)
|
||||
{
|
||||
const ExportedScriptInformation esi = m_model->getRowData(mil.first().row());
|
||||
|
||||
const bool isOC = isOCApp();
|
||||
const bool onLocal = esi.local;
|
||||
const bool onServer = (esi.uploaded == ExportedScriptInformation::UploadState::ThpAlreadyUploaded);
|
||||
|
||||
enableShow = isOC && onLocal;
|
||||
enableDownload = isOC && onServer;
|
||||
enableUpload = !isOC && onLocal;
|
||||
enableRemoveLocal = onLocal;
|
||||
enableRemoveServer = onServer;
|
||||
}
|
||||
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Show)->setEnabled(enableShow); // Show presentation Button
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setEnabled(enableDownload || enableUpload); // download from / upload to Server
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Remove)->setEnabled(enableRemoveLocal); // local remove Button
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Import)->setEnabled(enableRemoveServer); // Remove from Server Button
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsDialog::retranslate()
|
||||
{
|
||||
translate(m_ui, tr("Manage exported scripts"));
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Remove)->setText(tr("Remove local"));
|
||||
|
||||
Q_ASSERT(m_session != nullptr);
|
||||
|
||||
if (isOCApp())
|
||||
{
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setText(tr("Download from Server"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setText(tr("Upload to Server"));
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Import)->setText(tr("Remove from Server"));
|
||||
}
|
||||
|
||||
setWsConnectionStatusLabel();
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsDialog::setWsConnectionStatus(WsConnectionStatus status)
|
||||
{
|
||||
m_wsStatus = status;
|
||||
setWsConnectionStatusLabel();
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsDialog::setWsConnectionStatusLabel()
|
||||
{
|
||||
QString str;
|
||||
QColor color;
|
||||
|
||||
switch (m_wsStatus)
|
||||
{
|
||||
case WsConnectionStatus::NotConnected:
|
||||
{
|
||||
str = tr("Not connected");
|
||||
color = QColor::fromRgb(100, 100, 100);
|
||||
break;
|
||||
}
|
||||
|
||||
case WsConnectionStatus::Disconnected:
|
||||
{
|
||||
str = tr("Disconnected");
|
||||
color = QColor::fromRgb(200, 55, 0);
|
||||
//color = QColor::fromRgb(100, 100, 100);
|
||||
break;
|
||||
}
|
||||
|
||||
case WsConnectionStatus::Error:
|
||||
{
|
||||
str = tr("Connection error");
|
||||
color = QColor::fromRgb(200, 55, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case WsConnectionStatus::Connecting:
|
||||
{
|
||||
str = tr("Connecting ...");
|
||||
color = QColor::fromRgb(100, 100, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case WsConnectionStatus::Connected:
|
||||
{
|
||||
str = tr("Connected");
|
||||
color = QColor::fromRgb(50, 200, 50);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_ui->lblWsStatus->setText(QStringLiteral("<font color='%2'>%1</font>").arg(str, color.name()));
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsDialog::initModel() noexcept(false)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "(re)initModel !!";
|
||||
|
||||
const QString rootDirectory(localThpDir());
|
||||
|
||||
QDir().mkpath(rootDirectory);
|
||||
try{
|
||||
m_model->init(rootDirectory, true);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Couldn't Load THP(s)"));
|
||||
WARNING_LOG(m_logger)<<" data corrupted path: "<< localThpDir();
|
||||
}
|
||||
|
||||
m_model->setOC(isOCApp()); // important to call *before* setting model
|
||||
m_ui->ltsDataView->setModel(m_model);
|
||||
|
||||
// 'hide' is important to call *after* setting model
|
||||
#ifndef LTS_DEBUG
|
||||
m_ui->ltsDataView->hideColumn(ExportedScriptsModel::ColId);
|
||||
#endif
|
||||
|
||||
m_ui->ltsDataView->hideColumn(ExportedScriptsModel::Columns::ColUploadStatus);
|
||||
|
||||
m_model->setServerContent(m_lastServerContent);
|
||||
onSelectionChanged(); // aka. enaDisa
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsDialog::uploadScript()
|
||||
{
|
||||
const QModelIndexList mil = m_ui->ltsDataView->selectedRows();
|
||||
if (mil.size() != 1)
|
||||
return;
|
||||
const ExportedScriptInformation esi = m_model->getRowData(mil.first().row());
|
||||
|
||||
if (!esi.local)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
UploadProcess::upload(this, localThpDir(), std::move(esi));
|
||||
// NOTE: no need to call setServerContent as the server will broadcast new content after the upload
|
||||
}
|
||||
catch(const std::runtime_error &err)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Uploaded script FAILED: " << err.what();
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Uploading script failed: %1").arg(QString::fromUtf8(err.what())));
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "Uploaded script successfully";
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Uploaded script successfully"));
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsDialog::downloadScript()
|
||||
{
|
||||
const QModelIndexList mil = m_ui->ltsDataView->selectedRows();
|
||||
if (mil.size() != 1)
|
||||
return;
|
||||
const QModelIndex mi = mil.first();
|
||||
ExportedScriptInformation esi = m_model->getRowData(mi.row());
|
||||
|
||||
if (esi.uploaded != ExportedScriptInformation::UploadState::ThpAlreadyUploaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
DownloadProcess::download(this, localThpDir(), esi.id());
|
||||
|
||||
// modify esi in model
|
||||
for (int row = 0; row < m_model->rowCount(); row++)
|
||||
{
|
||||
const auto rowEsi(m_model->getRowData(row));
|
||||
if (rowEsi.id() != esi.id())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
esi.setLocal(rowEsi.createdAtServer);
|
||||
|
||||
if (!m_model->setRowData(row, esi))
|
||||
{
|
||||
qWarning() << "failed... ignored...";
|
||||
break; // can only match 1 time
|
||||
}
|
||||
|
||||
m_model->writeScriptsJson(); // throws
|
||||
break; // can only match 1 time
|
||||
}
|
||||
}
|
||||
catch(const std::runtime_error &err)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Downloading script FAILED: " << err.what();
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Downloading script failed: %1").arg(QString::fromUtf8(err.what())));
|
||||
return;
|
||||
}
|
||||
|
||||
//m_model->setServerContent(m_lastServerContent);
|
||||
//onSelectionChanged(); // aka. enaDisa
|
||||
|
||||
DEBUG_LOG(m_logger) << "Downloaded script successfully";
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Downloaded script successfully"));
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsDialog::removeScriptFromServer()
|
||||
{
|
||||
const QModelIndexList mil = m_ui->ltsDataView->selectedRows();
|
||||
if (mil.size() != 1)
|
||||
return;
|
||||
|
||||
const ExportedScriptInformation esi = m_model->getRowData(mil.first().row());
|
||||
|
||||
if (esi.uploaded != ExportedScriptInformation::UploadState::ThpAlreadyUploaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
RemoveProcess::remove(this, esi.id());
|
||||
// NOTE: server will broadcast new content, so do nothing here
|
||||
}
|
||||
catch (const std::runtime_error &err)
|
||||
{
|
||||
ERROR_LOG(m_logger) << err.what();
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Failed to remove script from server!"));
|
||||
return;
|
||||
}
|
||||
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Script was removed from server successfully"));
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsDialog::removeScriptLocal()
|
||||
{
|
||||
const QModelIndexList mil = m_ui->ltsDataView->selectedRows();
|
||||
if (mil.size() != 1)
|
||||
return;
|
||||
|
||||
const ExportedScriptInformation esi = m_model->getRowData(mil.first().row());
|
||||
|
||||
if (!esi.local)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
const auto &esi = m_model->getRowData(mil.first().row());
|
||||
const auto scriptId = esi.id();
|
||||
|
||||
if (scriptId.length() < 3)
|
||||
{
|
||||
ELOG << "scriptId is not in format 'n.n' - skipping for safety reasons - scriptId was: " << scriptId;
|
||||
throw err("scriptId.length() < 3");
|
||||
}
|
||||
|
||||
|
||||
// rm from scripts.js and model
|
||||
m_model->removeLocalScript(scriptId); // NOTE: doesn't remove from model if not local or available on server, so no need to call setServerContent
|
||||
onSelectionChanged();
|
||||
|
||||
|
||||
// rm from fs
|
||||
QDir dir(scriptLocation(localThpDir(), scriptId));
|
||||
if (!dir.exists())
|
||||
{
|
||||
ELOG << "script dir doesn't exist - ignoring - path was: " << dir.path();
|
||||
}
|
||||
else if (!dir.removeRecursively())
|
||||
{
|
||||
ELOG << "failed to remove script dir - path was: " << dir.path();
|
||||
throw err("!dir.removeRecursively()");
|
||||
}
|
||||
}
|
||||
catch (const std::runtime_error &err)
|
||||
{
|
||||
ERROR_LOG(m_logger) << err.what();
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Failed to remove script locally!"));
|
||||
return;
|
||||
}
|
||||
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Script was locally removed successfully."));
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsDialog::openScript()
|
||||
{
|
||||
const QModelIndexList mil = m_ui->ltsDataView->selectedRows();
|
||||
if (mil.size() != 1)
|
||||
return;
|
||||
const ExportedScriptInformation esi = m_model->getRowData(mil.first().row());
|
||||
|
||||
if (!esi.local)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto path(QDir(scriptHtmlLocation(localThpDir(), esi.id())).path());
|
||||
const auto url(QUrl::fromLocalFile(path));
|
||||
const bool openUrlResult = QDesktopServices::openUrl(url);
|
||||
DEBUG_LOG(m_logger) << "path: " << path << " - url: " << url.toString() << " - success? " << openUrlResult;
|
||||
}
|
||||
|
||||
} // namespace TakeHomePackageManager
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Show the exported scripts and allow them to upload them to the server
|
||||
* $Id: tpm_exported_scripts_dialog.h 51804 2020-07-31 04:47:28Z fritsche_h $
|
||||
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_exported_scripts_dialog.h $
|
||||
* $Author: fritsche_h $
|
||||
*
|
||||
* Created by Mike Wille, ESZS, 2024-02-20
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <Common2/tpm_exported_scripts_model.h>
|
||||
#include <LtsWidgets/lts_dialog.h>
|
||||
|
||||
#include <QUrlQuery>
|
||||
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class ExportedScriptsDialog2;
|
||||
}
|
||||
namespace NGL
|
||||
{
|
||||
class Compress;
|
||||
}
|
||||
namespace LtsSession
|
||||
{
|
||||
class LtsSession;
|
||||
}
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class QNetworkRequest;
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
class ProcessOverlay;
|
||||
|
||||
|
||||
class Compressor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Compressor();
|
||||
void go(ProcessOverlay *processOverlay, QIODevice &out, const QString &sourceDir, const QString &destRootDir) noexcept(false);
|
||||
|
||||
signals:
|
||||
void abort();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class NoticeDummy : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using QObject::QObject;
|
||||
|
||||
signals:
|
||||
void signal();
|
||||
};
|
||||
|
||||
|
||||
|
||||
class HttpProcess : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HttpProcess(LtsWidgets::LtsDialog *dialog, const QString &statusMessage);
|
||||
virtual ~HttpProcess();
|
||||
|
||||
signals:
|
||||
void signalCancel();
|
||||
|
||||
protected:
|
||||
void waitForAnswer(QNetworkReply *r, const int expectedResponseStatus) noexcept(false);
|
||||
|
||||
QNetworkReply *post(const QString &path, QIODevice *data, const QUrlQuery &q = QUrlQuery(), const QString &contentType = QStringLiteral("application/octet-stream"), const QMap<QString, QString> &additionalHeaders = {}) noexcept(false);
|
||||
QNetworkReply *get(const QString &path, const QUrlQuery &q = QUrlQuery()) noexcept(false);
|
||||
QNetworkReply *deleteResource(const QString &path, const QUrlQuery &q = QUrlQuery()) noexcept(false);
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
ProcessOverlay *m_process;
|
||||
|
||||
private:
|
||||
QNetworkAccessManager *m_nam;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class UploadProcess : public HttpProcess
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static void upload(LtsWidgets::LtsDialog *dialog, const QString &rootFolder, const ExportedScriptInformation &esi) noexcept(false);
|
||||
|
||||
private:
|
||||
UploadProcess(LtsWidgets::LtsDialog *dialog);
|
||||
|
||||
protected:
|
||||
void go(const QString &rootFolder, const ExportedScriptInformation &esi) noexcept(false);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class DownloadProcess : public HttpProcess
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static void download(LtsWidgets::LtsDialog *dialog, const QString &rootFolder, const QString &scriptId) noexcept(false);
|
||||
|
||||
private:
|
||||
DownloadProcess(LtsWidgets::LtsDialog *dialog);
|
||||
|
||||
protected:
|
||||
void go(LtsWidgets::LtsDialog *dialog, const QString &rootFolder, const QString &scriptId) noexcept(false);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class RemoveProcess : public HttpProcess
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static void remove(LtsWidgets::LtsDialog *dialog, const QString &scriptId) noexcept(false);
|
||||
|
||||
private:
|
||||
RemoveProcess(LtsWidgets::LtsDialog *dialog);
|
||||
|
||||
protected:
|
||||
void go(const QString &scriptId) noexcept(false);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ExportedScriptsDialog : public LtsWidgets::LtsDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ExportedScriptsDialog(LtsSession::LtsSession *session, QWidget *parent = nullptr) noexcept(false);
|
||||
~ExportedScriptsDialog();
|
||||
|
||||
void retranslate() override;
|
||||
static QUrl forgeUrl(const QString &path, const QString &protocol = L("http"));
|
||||
static QNetworkRequest forgeRequest(const QString &path, const QUrlQuery &q, const QString &contentType = QString());
|
||||
|
||||
private slots:
|
||||
void uploadScript();
|
||||
void downloadScript();
|
||||
void removeScriptFromServer();
|
||||
void removeScriptLocal();
|
||||
void openScript();
|
||||
void onSelectionChanged();
|
||||
void _onSelectionChanged();
|
||||
|
||||
private:
|
||||
enum class WsConnectionStatus
|
||||
{
|
||||
NotConnected,
|
||||
Connecting,
|
||||
Disconnected,
|
||||
Error,
|
||||
Connected,
|
||||
};
|
||||
|
||||
void initModel() noexcept(false);
|
||||
void setWsConnectionStatus(WsConnectionStatus status);
|
||||
void setWsConnectionStatusLabel();
|
||||
bool isOCApp() const;
|
||||
|
||||
private:
|
||||
ExportedScriptsModel *m_model;
|
||||
Ui::ExportedScriptsDialog2 *m_ui;
|
||||
LtsSession::LtsSession *m_session;
|
||||
QString m_lastServerContent;
|
||||
WsConnectionStatus m_wsStatus { WsConnectionStatus::NotConnected };
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
} // namespace TakeHomePackageManager
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ExportedScriptsDialog2</class>
|
||||
<widget class="QWidget" name="ExportedScriptsDialog2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>834</width>
|
||||
<height>491</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>The following Scripts are already exported. You can remove them from your local hard disk or upload them to the server here.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Server connection status:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblWsStatus">
|
||||
<property name="text">
|
||||
<string notr="true">-</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="LtsDataView::LtsDataView" name="ltsDataView" native="true">
|
||||
<property name="showColumnCount" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolButtonsVisible" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LtsDataView::LtsDataView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>LtsDataView/lts_data_view.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* GUI Plugin for THP2
|
||||
* $Id: tpm_gui_plugin.cpp 48572 2019-10-02 11:47:04Z rudolphus_g $
|
||||
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_gui_plugin.cpp $
|
||||
* $Author: rudolphus_g $
|
||||
*
|
||||
* Created by Mike Wille, ESZS, 2024-02-20
|
||||
*/
|
||||
|
||||
#include "tpm2_gui_plugin.h"
|
||||
#include "tpm2_exported_scripts_dialog.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
std::shared_ptr<GuiPluginBase::Interface> GuiPlugin::createGuiInterface(LtsSession::LtsSession *session)
|
||||
{
|
||||
return std::make_shared<InterfaceImpl>(session);
|
||||
}
|
||||
|
||||
|
||||
InterfaceImpl::InterfaceImpl(LtsSession::LtsSession *session)
|
||||
: GuiPluginBase::Interface(session, getLogger("Plugins.Gui.Tpm"))
|
||||
{}
|
||||
|
||||
|
||||
bool InterfaceImpl::fetchInterfaces()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
QString InterfaceImpl::menuGroup() const
|
||||
{
|
||||
return tr("Presentation");
|
||||
}
|
||||
|
||||
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
|
||||
{
|
||||
QVector<LtsWidgets::PluginData> ret;
|
||||
LtsWidgets::PluginData plData;
|
||||
plData.menuGroup = menuGroup();
|
||||
if (m_session->app() == LtsAppDescriptor::App::Scripts)
|
||||
plData.exercisePart = LtsWidgets::ExercisePart::eSpecial;
|
||||
else
|
||||
plData.exercisePart = LtsWidgets::ExercisePart::eAfterAction;
|
||||
plData.identifier = QLatin1String("PresentationManagement.ScriptDialog.ManageExportedScripts");
|
||||
plData.menuIcon = QIcon(QLatin1String("icon:export_documents.svg"));
|
||||
plData.menuText = tr("Manage Exported Scripts");
|
||||
// plData.operatorRight = OperatorRights::ScriptEditorEdit;
|
||||
plData.createWidgetFunc = [this](QWidget *)
|
||||
{
|
||||
return LtsWidgets::createThrowableDialog<ExportedScriptsDialog>(m_session, nullptr);
|
||||
};
|
||||
ret.append(plData);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace TakeHomePackageManager
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* GUI Plugin for THP2
|
||||
* $Id: tpm_gui_plugin.h 49807 2020-02-06 09:53:16Z ehrlicher $
|
||||
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_gui_plugin.h $
|
||||
* $Author: ehrlicher $
|
||||
*
|
||||
* Created by Mike Wille, ESZS, 2024-02-20
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <THP2/tpm2_gui_plugin_interface.h>
|
||||
|
||||
#include "logger.h"
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <PluginInterfaces/gui_plugin_interface.h>
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
|
||||
{
|
||||
Q_PLUGIN_METADATA(IID "gui:3:tpm2.man")
|
||||
Q_INTERFACES(GuiPluginBase::Plugin)
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
GuiPlugin() = default;
|
||||
~GuiPlugin() = default;
|
||||
|
||||
QString pluginName() override { return QLatin1String("TakeHomePackageManager2"); }
|
||||
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
|
||||
bool availableInPartition(IDL::PartitionType partition) const override
|
||||
{
|
||||
return partition == IDL::PartitionType::Exercise;
|
||||
}
|
||||
};
|
||||
|
||||
class InterfaceImpl final : public QObject, public GuiPluginBase::Interface, public ForeignTpm2GuiInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit InterfaceImpl(LtsSession::LtsSession *session);
|
||||
|
||||
bool fetchInterfaces() override;
|
||||
QString menuGroup() const override;
|
||||
QVector<LtsWidgets::PluginData> getActions() override;
|
||||
bool availableInPartition(IDL::PartitionType partition) const override { return partition == IDL::PartitionType::Exercise; }
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
} // namespace TakeHomePackageManager
|
||||
Reference in new issue
Block a user