Adds initial intermediate qt6 porting state
This commit is contained in:
11247 files changed
+1891537
No files matched your search
+18
@@ -0,0 +1,18 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
# Created by Mike Wille, ESZS, 2024-02-20
|
||||
#
|
||||
|
||||
ADD_SUBDIRECTORY(Common2Core)
|
||||
ADD_SUBDIRECTORY(Common2)
|
||||
ADD_SUBDIRECTORY(Thp2Management)
|
||||
ADD_SUBDIRECTORY(Thp2UsbManagement)
|
||||
|
||||
ADD_INTERFACE_LIBRARY(Tpm2GuiPluginInterface
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tpm2_gui_plugin_interface.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
GuiPluginInterface
|
||||
)
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#
|
||||
# $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, 2024-01-11
|
||||
#
|
||||
|
||||
SET(src
|
||||
logger.cpp
|
||||
logger.h
|
||||
tpm_exported_scripts_model.cpp
|
||||
tpm_exported_scripts_model.h
|
||||
tpm_process_dialog.cpp
|
||||
tpm_process_dialog.h
|
||||
tpm_topics_model.cpp
|
||||
tpm_topics_model.h
|
||||
tpm_file_copy.cpp
|
||||
tpm_file_copy.h
|
||||
)
|
||||
|
||||
QT5_WRAP_UI(src
|
||||
tpm_process_dialog.ui
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY2(WS Thp2Common SHARED ${src})
|
||||
NGL_TARGET_COMPILE_DEFINITIONS(Thp2Common PRIVATE L=QStringLiteral)
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(Thp2Common
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ # export file
|
||||
)
|
||||
NGL_TARGET_LINK_LIBRARIES(Thp2Common
|
||||
PUBLIC
|
||||
Thp2CommonCore
|
||||
PRIVATE
|
||||
Qt5::Core
|
||||
Qt5::Concurrent
|
||||
NGLLogger
|
||||
LtsSession
|
||||
LtsUtilsCoreHeaderOnly
|
||||
TopicEditorBase # topic types
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(Thp2Common)
|
||||
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
|
||||
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
LOGGER_T getLogger(const char *name)
|
||||
{
|
||||
static const char ns[] = "TPM2.Common.";
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
|
||||
|
||||
|
||||
#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());
|
||||
}
|
||||
}
|
||||
+382
@@ -0,0 +1,382 @@
|
||||
|
||||
|
||||
|
||||
#include "tpm_exported_scripts_model.h"
|
||||
|
||||
#include <ngltools/ngltools_dtg.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QSaveFile>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QMutableVectorIterator>
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
ExportedScriptsModel::ExportedScriptsModel(QObject *parent)
|
||||
: LtsWidgets::LtsAbstractTableModel<ExportedScriptInformation>(
|
||||
{ tr("id"), tr("Exercise"), tr("Name"), tr("Local version"), tr("Server version"), tr("Location"), tr("Upload status")}, parent)
|
||||
, m_logger(getLogger("ExportedScriptsModel"))
|
||||
{}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ExportedScriptsModel::init(const QString &rootDirectoryToScan, bool checkForScriptsJsChanges) noexcept(false)
|
||||
{
|
||||
m_rootDirectoryToScan = rootDirectoryToScan;
|
||||
|
||||
beginResetModel();
|
||||
std::shared_ptr<int> _(nullptr, [this](int *) { endResetModel(); });
|
||||
m_vecData = ScriptsJsParser(m_rootDirectoryToScan).getExportedScriptInformations().values().toVector();
|
||||
|
||||
if (checkForScriptsJsChanges)
|
||||
{
|
||||
delete m_watcher;
|
||||
m_watcher = new QFileSystemWatcher({ scriptsJsLocation(m_rootDirectoryToScan) }, this);
|
||||
connect(m_watcher, &QFileSystemWatcher::fileChanged, this, [this](){try{reloadScriptsJs();}catch(...){}});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsModel::reloadScriptsJs() noexcept(false)
|
||||
{
|
||||
qWarning() << "RELOAD!!";
|
||||
|
||||
|
||||
const auto scriptsJsParser = ScriptsJsParser(m_rootDirectoryToScan);
|
||||
const auto esiMap = scriptsJsParser.getExportedScriptInformations();
|
||||
|
||||
|
||||
for (int row = 0; row < m_vecData.count(); row++)
|
||||
{
|
||||
auto currentEsi = getRowData(row);
|
||||
|
||||
if (!currentEsi.local)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (esiMap.contains(currentEsi.id()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (currentEsi.isUploaded())
|
||||
{
|
||||
|
||||
currentEsi.unsetLocal();
|
||||
setRowData(row, currentEsi);
|
||||
qWarning() << "RELOAD!! UPDATED TO NON-LOCAL!" << currentEsi.id();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
qWarning() << "RELOAD!! REMOVED!" << currentEsi.id();
|
||||
removeRow(row);
|
||||
}
|
||||
|
||||
|
||||
const auto esis = esiMap.values().toVector();
|
||||
for (const auto &localEsi : esis)
|
||||
{
|
||||
bool found = false;
|
||||
for (int row = 0; row < m_vecData.count(); row++)
|
||||
{
|
||||
auto currentEsi = getRowData(row);
|
||||
|
||||
if (currentEsi.id() != localEsi.id())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (localEsi.createdAt > currentEsi.createdAt && localEsi.createdAt > currentEsi.createdAtServer)
|
||||
{
|
||||
currentEsi.createdAt = localEsi.createdAt;
|
||||
currentEsi.exercise = localEsi.exercise;
|
||||
currentEsi.script = localEsi.script;
|
||||
|
||||
qWarning() << "RELOAD!! UPDATED DATA FROM SERVER!" << currentEsi.id();
|
||||
setRowData(row, currentEsi);
|
||||
}
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
qWarning() << "RELOAD!! ADDED!" << localEsi.id();
|
||||
insertRow(localEsi);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsModel::setServerContent(const QString &jsonObject)
|
||||
{
|
||||
if (jsonObject.isEmpty())
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
const QJsonObject scripts(JsonObject::fromJson(jsonObject.toUtf8()).object());
|
||||
|
||||
|
||||
for (int row = 0; row < m_vecData.count(); row++)
|
||||
{
|
||||
auto currentEsi = getRowData(row);
|
||||
|
||||
if (scripts.contains(currentEsi.id()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (currentEsi.local)
|
||||
{
|
||||
currentEsi.unsetUploaded();
|
||||
setRowData(row, currentEsi);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
removeRow(row);
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (const QJsonValue &val : scripts)
|
||||
{
|
||||
const auto serverEsi(ExportedScriptInformation::fromJson(val, false , ExportedScriptInformation::UploadState::ThpAlreadyUploaded));
|
||||
bool found = false;
|
||||
|
||||
for (int row = 0; row < m_vecData.count(); row++)
|
||||
{
|
||||
auto currentEsi = getRowData(row);
|
||||
|
||||
if (currentEsi.id() != serverEsi.id())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (serverEsi.createdAt > currentEsi.createdAt)
|
||||
{
|
||||
currentEsi.createdAt = serverEsi.createdAtServer;
|
||||
currentEsi.exercise = serverEsi.exercise;
|
||||
currentEsi.script = serverEsi.script;
|
||||
}
|
||||
|
||||
currentEsi.setUploaded(serverEsi.createdAtServer);
|
||||
|
||||
setRowData(row, currentEsi);
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
insertRow(serverEsi);
|
||||
}
|
||||
}
|
||||
catch(const std::runtime_error &err)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "something went wrong: " << err.what();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "something went wrong";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QVariant ExportedScriptsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!indexIsValid(index))
|
||||
return QVariant();
|
||||
|
||||
const ExportedScriptInformation &esi = m_vecData[index.row()];
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case Qt::BackgroundRole:
|
||||
{
|
||||
if ((index.column() == ColCreatedAt && esi.createdAtServer.isValid() && esi.createdAt < esi.createdAtServer) !=
|
||||
(index.column() == ColCreatedAtServer && esi.createdAt.isValid() && esi.createdAtServer < esi.createdAt))
|
||||
{
|
||||
return QBrush(QColor(255, 205, 205));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
switch (index.column())
|
||||
{
|
||||
case ColId:
|
||||
return esi.id();
|
||||
case ColExercise:
|
||||
return esi.exercise.name;
|
||||
case ColName:
|
||||
return esi.script.name;
|
||||
case ColCreatedAt:
|
||||
return esi.createdAt;
|
||||
case ColCreatedAtServer:
|
||||
return esi.createdAtServer;
|
||||
case ColLocation:
|
||||
{
|
||||
QStringList locations;
|
||||
if (esi.local)
|
||||
locations += tr("Local");
|
||||
if (esi.uploaded == ExportedScriptInformation::UploadState::ThpAlreadyUploaded)
|
||||
locations += tr("Server");
|
||||
|
||||
return locations.join(L(" & "));
|
||||
}
|
||||
case ColUploadStatus:
|
||||
{
|
||||
switch (esi.uploaded)
|
||||
{
|
||||
case ExportedScriptInformation::UploadState::ThpNotUploaded:
|
||||
return tr("Not uploaded");
|
||||
case ExportedScriptInformation::UploadState::ThpUnknownUploadState:
|
||||
return tr("Unknown upload status");
|
||||
case ExportedScriptInformation::UploadState::ThpAlreadyUploaded:
|
||||
return tr("Already uploaded");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::UserRole:
|
||||
{
|
||||
return esi.id();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsModel::addScript(const int exerciseId, const QString &exerciseName,
|
||||
const int scriptId, const QString &scriptName,
|
||||
const QDateTime &createdAt) noexcept(false)
|
||||
{
|
||||
insertRow(ExportedScriptInformation({ createdAt, { exerciseId, exerciseName }, { scriptId, scriptName, {} } }));
|
||||
writeScriptsJson();
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsModel::removeLocalScript(const QModelIndex &idx) noexcept(false)
|
||||
{
|
||||
auto esi = getRowData(idx.row());
|
||||
|
||||
if (!esi.local)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (esi.isUploaded())
|
||||
{
|
||||
esi.unsetLocal();
|
||||
setRowData(idx.row(), esi);
|
||||
}
|
||||
else
|
||||
{
|
||||
removeRow(idx.row());
|
||||
}
|
||||
|
||||
writeScriptsJson();
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsModel::removeLocalScript(const QString &scriptId) noexcept(false)
|
||||
{
|
||||
for (int i = 0; i < rowCount(); i++)
|
||||
{
|
||||
const auto idx = index(i, 0);
|
||||
if (data(idx, Qt::UserRole) == scriptId)
|
||||
{
|
||||
removeLocalScript(idx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
QVector<ExportedScriptInformation> ExportedScriptsModel::getLocalScripts() noexcept(false)
|
||||
{
|
||||
QVector<ExportedScriptInformation> ret;
|
||||
ret.reserve(m_vecData.size());
|
||||
|
||||
for (const auto &esi : m_vecData)
|
||||
{
|
||||
if (esi.local)
|
||||
{
|
||||
ret += esi;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsModel::writeScriptsJson() noexcept(false)
|
||||
{
|
||||
auto parser = ScriptsJsParser(m_rootDirectoryToScan);
|
||||
parser.setExportedScriptInformations(getLocalScripts());
|
||||
parser.save();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <logger.h>
|
||||
|
||||
#include <LtsWidgets/lts_abstract_table_model.h>
|
||||
#include <Common2/thp2common_export.h>
|
||||
#include <Common2Core/tpm_js_parser.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
|
||||
|
||||
class QFileSystemWatcher;
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
class THP2COMMON_EXPORT ExportedScriptsModel : public LtsWidgets::LtsAbstractTableModel<ExportedScriptInformation>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Columns
|
||||
{
|
||||
ColId,
|
||||
ColExercise,
|
||||
ColName,
|
||||
ColCreatedAt,
|
||||
ColCreatedAtServer,
|
||||
ColLocation,
|
||||
ColUploadStatus,
|
||||
};
|
||||
explicit ExportedScriptsModel(QObject *parent = nullptr);
|
||||
|
||||
void init(const QString &rootDirectoryToScan, bool checkForScriptsJsChanges = false) noexcept(false);
|
||||
void addScript(const int exerciseId, const QString &exerciseName, const int scriptId, const QString &scriptName, const QDateTime &createdAt) noexcept(false);
|
||||
void removeLocalScript(const QString &scriptId) noexcept(false);
|
||||
void removeLocalScript(const QModelIndex &idx) noexcept(false);
|
||||
void setServerContent(const QString &jsonObject);
|
||||
void setOC(bool isOC) { m_isOC = isOC; }
|
||||
void writeScriptsJson() noexcept(false);
|
||||
|
||||
protected:
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
private:
|
||||
void reloadScriptsJs() noexcept(false);
|
||||
QVector<ExportedScriptInformation> getLocalScripts() noexcept(false);
|
||||
|
||||
private:
|
||||
bool m_isOC { false };
|
||||
QString m_rootDirectoryToScan;
|
||||
QFileSystemWatcher *m_watcher { nullptr };
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
}
|
||||
Executable
+377
@@ -0,0 +1,377 @@
|
||||
|
||||
|
||||
|
||||
#include "tpm_file_copy.h"
|
||||
|
||||
#include <Common2/tpm_process_dialog.h>
|
||||
#include <Common2Core/tpm_tools.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QTemporaryFile>
|
||||
#include <QTimer>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QtConcurrent>
|
||||
#include <QDirIterator>
|
||||
#include <QFileDialog>
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#define _compiler_warning(x) _Pragma(#x)
|
||||
#define compiler_warning(x) _compiler_warning(GCC warning x)
|
||||
#define TMP_WARNING_LOG(x) compiler_warning("Temporary warning log active!") \
|
||||
WARNING_LOG(x)
|
||||
#else
|
||||
#define compiler_warning(x)
|
||||
#define TMP_WARNING_LOG(x) WARNING_LOG(x)
|
||||
#endif
|
||||
|
||||
|
||||
typedef std::runtime_error error;
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
CopyInfoList getFilesFromFolder(const QString &srcPath, const QString &dstPath, const QStringList &excludes) noexcept(false)
|
||||
{
|
||||
int fuse = 50000;
|
||||
QFileInfoList todoFiles({{srcPath}});
|
||||
CopyInfoList ret;
|
||||
|
||||
do
|
||||
{
|
||||
fuse--;
|
||||
if (fuse < 1)
|
||||
{
|
||||
throw error("fuse popped");
|
||||
}
|
||||
|
||||
const QString path(todoFiles.takeFirst().absoluteFilePath());
|
||||
|
||||
bool exclude = false;
|
||||
for (const auto &excludePath : excludes)
|
||||
{
|
||||
if ((srcPath + QDir::separator() + excludePath) == path)
|
||||
{
|
||||
qWarning() << "excluded" << path;
|
||||
exclude = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (exclude)
|
||||
continue;
|
||||
|
||||
QDir srcDir(path);
|
||||
if (!srcDir.exists())
|
||||
{
|
||||
throw error(L("getFilesFromFolder: path (%1) does not exist!").arg(srcPath).toStdString());
|
||||
}
|
||||
|
||||
|
||||
for (const auto &dirEntryInfo : srcDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot))
|
||||
{
|
||||
|
||||
todoFiles += dirEntryInfo;
|
||||
}
|
||||
|
||||
|
||||
for (const auto &fileEntryInfo : srcDir.entryInfoList(QDir::Files))
|
||||
{
|
||||
ret += { srcPath, dstPath, fileEntryInfo };
|
||||
}
|
||||
}
|
||||
while (!todoFiles.isEmpty());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void copyFiles(const CopyInfoList &fileList, LtsWidgets::LtsDialog *dialog) noexcept(false)
|
||||
{
|
||||
auto cp = new CopyProcess(fileList, dialog);
|
||||
dialog->setOverlay(cp->process());
|
||||
|
||||
bool success = false;
|
||||
QObject::connect(cp, &CopyProcess::finished, dialog, [&success](bool _success) { success = _success; });
|
||||
|
||||
awaitx(cp, &CopyProcess::finished);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw error("!success");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AsyncFileCopy::AsyncFileCopy(const CopyInfoList &fileList)
|
||||
{
|
||||
QFutureWatcher<bool> *fw = new QFutureWatcher<bool>(this);
|
||||
connect(fw, &QFutureWatcher<bool>::finished,
|
||||
this, [this, fw]()
|
||||
{
|
||||
emit finished(fw->result());
|
||||
});
|
||||
connect(fw, &QFutureWatcher<bool>::finished,
|
||||
this, &QObject::deleteLater);
|
||||
fw->setFuture(QtConcurrent::run(this, &AsyncFileCopy::copy, fileList));
|
||||
}
|
||||
|
||||
|
||||
#define cancelationPoint if (m_wasCanceled) { qWarning() << "AsyncFileCopy: canceled"; return false; } (void)m_wasCanceled
|
||||
|
||||
|
||||
bool AsyncFileCopy::copy(const CopyInfoList &fileList)
|
||||
{
|
||||
qint64 totalAll = 0, writtenAll = 0;
|
||||
int fileCount = 0;
|
||||
|
||||
for (const auto &fileInfo : fileList)
|
||||
{
|
||||
totalAll += fileInfo.fileInfo.size();
|
||||
fileCount++;
|
||||
}
|
||||
|
||||
if (fileCount < 1)
|
||||
{
|
||||
qWarning() << "AsyncFileCopy: fileCount < 1";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
emit started(fileCount, totalAll);
|
||||
|
||||
cancelationPoint;
|
||||
|
||||
int currentFile = 0;
|
||||
for (const auto ©Info : fileList)
|
||||
{
|
||||
cancelationPoint;
|
||||
|
||||
const QFileInfo &fileInfo = copyInfo.fileInfo;
|
||||
const QString &srcPath = copyInfo.srcPath;
|
||||
const QString &dstPath = copyInfo.dstPath;
|
||||
|
||||
currentFile++;
|
||||
const qint64 totalFile = fileInfo.size();
|
||||
qint64 writtenFile = 0;
|
||||
|
||||
QDir srcDir(srcPath);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const QString dstFullPath(dstPath + QDir::separator() + fileInfo.absolutePath().remove(srcDir.path()));
|
||||
|
||||
if (!QDir().mkpath(dstFullPath) )
|
||||
{
|
||||
qWarning() << "AsyncFileCopy: cannot create path";
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile srcFile(fileInfo.absoluteFilePath());
|
||||
emit newFile(currentFile, totalFile, fileInfo.fileName());
|
||||
|
||||
QFile dstFile(dstFullPath + QDir::separator() + fileInfo.fileName());
|
||||
|
||||
qWarning() << "AsyncFileCopy: copy file from" << srcFile.fileName() << "to" << dstFile.fileName();
|
||||
|
||||
if (!srcFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qWarning() << QStringLiteral("failed to open src '%1' (error: %2)").arg(srcFile.fileName(), srcFile.errorString());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dstFile.open(QIODevice::Unbuffered | QIODevice::WriteOnly | QIODevice::Truncate))
|
||||
{
|
||||
qWarning() << QStringLiteral("failed to open dst '%1' (error: %2)").arg(dstFile.fileName(), dstFile.errorString());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
qint64 fileSize = srcFile.size();
|
||||
while (1)
|
||||
{
|
||||
cancelationPoint;
|
||||
|
||||
|
||||
|
||||
emit status(tr("Reading..."));
|
||||
const auto data = srcFile.read(
|
||||
#ifdef SUPERSLOW
|
||||
54321
|
||||
#else
|
||||
s_chunkSize
|
||||
#endif
|
||||
);
|
||||
|
||||
cancelationPoint;
|
||||
|
||||
#ifdef SUPERSLOW
|
||||
usleep(100 * 1000);
|
||||
#endif
|
||||
|
||||
emit status(tr("Copying..."));
|
||||
const auto _written = dstFile.write(data);
|
||||
|
||||
|
||||
|
||||
#ifdef SUPERSLOW
|
||||
usleep(100 * 1000);
|
||||
#endif
|
||||
|
||||
if (_written < 0)
|
||||
{
|
||||
qWarning() << "AsyncFileCopy: unable to write to file" << dstFile.fileName() << ":" << dstFile.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
writtenFile += _written;
|
||||
writtenAll += _written;
|
||||
|
||||
|
||||
|
||||
|
||||
emit progress(writtenFile, writtenAll);
|
||||
|
||||
cancelationPoint;
|
||||
|
||||
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
|
||||
emit status(tr("Writing..."));
|
||||
|
||||
QElapsedTimer t;
|
||||
t.start();
|
||||
|
||||
fsync(dstFile.handle());
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
if (data.size() != _written)
|
||||
{
|
||||
qWarning() << "AsyncFileCopy: copy failed! read" << data.size() << "write" << _written << "size" << fileSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (writtenFile == fileSize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (totalAll == writtenAll)
|
||||
{
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "AsyncFileCopy: failed! total written bytes doesn't match all requested bytes - " << writtenAll << "/" << totalAll << "bytes";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
CopyProcess::CopyProcess(const CopyInfoList &fileList, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_process(new ProcessOverlay)
|
||||
{
|
||||
connect(m_process, &ProcessOverlay::signalCancel,
|
||||
this, [this]()
|
||||
{
|
||||
|
||||
m_wasCanceled = true;
|
||||
|
||||
});
|
||||
connect(m_process, &ProcessOverlay::signalCancel,
|
||||
this, &CopyProcess::signalCancel);
|
||||
|
||||
AsyncFileCopy *afc = AsyncFileCopy::createAsyncFileCopy(fileList);
|
||||
|
||||
connect(m_process, &ProcessOverlay::signalCancel,
|
||||
afc, &AsyncFileCopy::slotCancel);
|
||||
|
||||
connect(afc, &AsyncFileCopy::progress,
|
||||
this, [this](qint64 bytesCopiedFile, qint64 bytesCopiedAll)
|
||||
{
|
||||
m_process->setFileProgress(bytesCopiedFile, bytesCopiedAll);
|
||||
});
|
||||
|
||||
connect(afc, &AsyncFileCopy::newFile,
|
||||
this, [this](int currentFile, qint64 bytesTotalFile, const QString &filename)
|
||||
{
|
||||
m_process->setNewFile(currentFile, bytesTotalFile, filename);
|
||||
});
|
||||
|
||||
connect(afc, &AsyncFileCopy::started,
|
||||
this, [this](int fileCount, qint64 bytesTotalAll)
|
||||
{
|
||||
m_process->setStarted(fileCount, bytesTotalAll);
|
||||
});
|
||||
|
||||
connect(afc, &AsyncFileCopy::status,
|
||||
this, [this](const QString &status)
|
||||
{
|
||||
m_process->setStatus(status);
|
||||
});
|
||||
|
||||
connect(afc, &AsyncFileCopy::newAction,
|
||||
this, [this](const QString &newAction)
|
||||
{
|
||||
m_currentAction = newAction;
|
||||
});
|
||||
|
||||
connect(afc, &AsyncFileCopy::finished,
|
||||
this, &QObject::deleteLater);
|
||||
|
||||
connect(afc, &AsyncFileCopy::finished,
|
||||
this, &CopyProcess::finished);
|
||||
}
|
||||
|
||||
|
||||
CopyProcess::~CopyProcess()
|
||||
{
|
||||
|
||||
|
||||
m_process->deleteLater();
|
||||
}
|
||||
|
||||
|
||||
QWidget *CopyProcess::process()
|
||||
{
|
||||
return m_process;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Common2/thp2common_export.h>
|
||||
|
||||
#include <LtsWidgets/lts_dialog.h>
|
||||
|
||||
#include <QFileInfoList>
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
class ProcessOverlay;
|
||||
|
||||
|
||||
struct THP2COMMON_EXPORT CopyInfo
|
||||
{
|
||||
QString srcPath;
|
||||
QString dstPath;
|
||||
QFileInfo fileInfo;
|
||||
};
|
||||
using CopyInfoList = QList<CopyInfo>;
|
||||
|
||||
|
||||
THP2COMMON_EXPORT CopyInfoList getFilesFromFolder(const QString &srcPath, const QString &dstPath, const QStringList &excludes = {}) noexcept(false);
|
||||
THP2COMMON_EXPORT void copyFiles(const CopyInfoList &fileList, LtsWidgets::LtsDialog *dialog) noexcept(false);
|
||||
|
||||
|
||||
|
||||
class AsyncFileCopy : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static const int s_chunkSize = 1 * 1024 * 1024;
|
||||
|
||||
static AsyncFileCopy *createAsyncFileCopy(const CopyInfoList &fileList)
|
||||
{
|
||||
return new AsyncFileCopy(fileList);
|
||||
}
|
||||
|
||||
~AsyncFileCopy() = default;
|
||||
|
||||
private:
|
||||
AsyncFileCopy(const CopyInfoList &fileList);
|
||||
|
||||
private:
|
||||
bool copy(const CopyInfoList &fileList);
|
||||
bool m_wasCanceled { false };
|
||||
|
||||
public slots:
|
||||
void slotCancel()
|
||||
{
|
||||
m_wasCanceled = true;
|
||||
}
|
||||
|
||||
signals:
|
||||
void newAction(const QString &newAction);
|
||||
void status(const QString &status);
|
||||
void started(int fileCount, qint64 bytesTotalAll);
|
||||
void newFile(int currentFile, qint64 bytesTotalFile, const QString &filename);
|
||||
void progress(qint64 bytesCopiedFile, qint64 bytesCopiedAll);
|
||||
void finished(bool success);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class THP2COMMON_EXPORT CopyProcess : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CopyProcess(const CopyInfoList &fileList, QObject *parent = nullptr);
|
||||
~CopyProcess();
|
||||
|
||||
QWidget *process();
|
||||
|
||||
signals:
|
||||
void signalCancel();
|
||||
void finished(bool success);
|
||||
|
||||
private:
|
||||
ProcessOverlay *m_process;
|
||||
bool m_wasCanceled { false };
|
||||
QString m_currentAction;
|
||||
|
||||
};
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
|
||||
|
||||
|
||||
#include "tpm_process_dialog.h"
|
||||
|
||||
#include <ui_tpm_process_dialog.h>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
ProcessOverlay::ProcessOverlay(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_ui(new Ui::ProcessOverlay)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
connect(m_ui->pbCancel, &QPushButton::clicked,
|
||||
this, [this]()
|
||||
{
|
||||
m_ui->pbCancel->setEnabled(false);
|
||||
emit signalCancel();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
ProcessOverlay::~ProcessOverlay()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
|
||||
void ProcessOverlay::setCancelEnabled(bool enabled)
|
||||
{
|
||||
m_ui->pbCancel->setEnabled(enabled);
|
||||
}
|
||||
|
||||
|
||||
void ProcessOverlay::setStatus(const QString &text)
|
||||
{
|
||||
if (!text.isEmpty())
|
||||
m_ui->lblStatus->setText(text);
|
||||
}
|
||||
|
||||
|
||||
void ProcessOverlay::setFileProgress(qint64 bytesCopiedFile, qint64 bytesCopiedAll)
|
||||
{
|
||||
qWarning() << "setFileProgress" << "bytesCopiedFile" << bytesCopiedFile << "bytesCopiedAll" << bytesCopiedAll;
|
||||
|
||||
double progressBytes = (m_bytesTotalFile == 0) ? 0 : ((double)bytesCopiedFile / (double)m_bytesTotalFile);
|
||||
double progressTotal = (m_bytesTotalAll == 0) ? 0 : (double)bytesCopiedAll / (double)m_bytesTotalAll;
|
||||
qWarning() << "progressTotal" << progressTotal << "progressBytes" << progressBytes;
|
||||
|
||||
m_ui->pbarBytes->setValue(qRound(progressBytes * 100));
|
||||
m_ui->pbarProgress->setValue(qRound(progressTotal * 100));
|
||||
|
||||
const QLocale l;
|
||||
m_ui->lblBytes->setText((m_bytesTotalFile == 0) ? QStringLiteral(" - ") : QStringLiteral("%1 / %2 %3").arg(l.toString(bytesCopiedFile)).arg(l.toString(m_bytesTotalFile)).arg(tr("bytes")));
|
||||
m_ui->lblAllBytes->setText((m_bytesTotalAll == 0) ? QStringLiteral(" - ") : QStringLiteral("%1 / %2 %3").arg(l.toString(bytesCopiedAll)).arg(l.toString(m_bytesTotalAll)).arg(tr("bytes")));
|
||||
}
|
||||
|
||||
|
||||
void ProcessOverlay::setNewFile(int currentFile, qint64 bytesTotalFile, const QString &filename)
|
||||
{
|
||||
qWarning() << "setNewFile" << "currentFile" << currentFile << "bytesTotalFile" << bytesTotalFile << "filename" << filename;
|
||||
|
||||
m_bytesTotalFile = bytesTotalFile;
|
||||
m_currentFile = currentFile;
|
||||
|
||||
m_progressFiles = (m_fileCount == 0) ? 0 : (((double)m_currentFile - 1) / (double)m_fileCount);
|
||||
m_ui->pbarFiles->setValue(qRound(m_progressFiles * 100));
|
||||
|
||||
m_ui->lblFilename->setText(filename);
|
||||
|
||||
const QLocale l;
|
||||
m_ui->lblFiles->setText((m_currentFile == 0) ? QStringLiteral(" - ") : QStringLiteral("%1 / %2").arg(l.toString(m_currentFile)).arg(l.toString(m_fileCount)));
|
||||
}
|
||||
|
||||
|
||||
void ProcessOverlay::setStarted(int fileCount, qint64 bytesTotalAll)
|
||||
{
|
||||
qWarning() << "setStarted" << "fileCount" << fileCount << "bytesTotalAll" << bytesTotalAll;
|
||||
|
||||
m_fileCount = fileCount;
|
||||
m_bytesTotalAll = bytesTotalAll;
|
||||
|
||||
setFileProgress(0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
||||
#include <Common2/thp2common_export.h>
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class ProcessOverlay;
|
||||
}
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
class THP2COMMON_EXPORT ProcessOverlay : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProcessOverlay(QWidget *parent = nullptr);
|
||||
~ProcessOverlay();
|
||||
|
||||
void setStarted(int fileCount, qint64 bytesTotalAll);
|
||||
void setNewFile(int currentFile, qint64 bytesTotalFile, const QString &filename);
|
||||
void setStatus(const QString &text);
|
||||
void setFileProgress(qint64 bytesCopiedFile, qint64 bytesCopiedAll);
|
||||
|
||||
void setCancelEnabled(bool enabled = true);
|
||||
|
||||
signals:
|
||||
void signalCancel();
|
||||
|
||||
private:
|
||||
qint64 m_bytesTotalFile = 0;
|
||||
int m_currentFile = 0;
|
||||
int m_fileCount = 0;
|
||||
qint64 m_bytesTotalAll = 0;
|
||||
double m_progressFiles = 0;
|
||||
|
||||
Ui::ProcessOverlay *m_ui;
|
||||
bool m_decompressing { false };
|
||||
};
|
||||
}
|
||||
qt6-after/qt6-migrated-legatus/Lts/Plugins/TakeHomePackageManager/THP2/Common2/tpm_process_dialog.ui
Executable
+190
@@ -0,0 +1,190 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ProcessOverlay</class>
|
||||
<widget class="QWidget" name="ProcessOverlay">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>700</width>
|
||||
<height>250</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>700</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lblStatus">
|
||||
<property name="text">
|
||||
<string notr="true">Preparing ...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Filename</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="lblFilename">
|
||||
<property name="text">
|
||||
<string notr="true"> - </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Bytes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="lblBytes">
|
||||
<property name="text">
|
||||
<string notr="true"> - </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="pbarBytes">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Files</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="lblFiles">
|
||||
<property name="text">
|
||||
<string notr="true"> - </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="pbarFiles">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Progress</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="lblAllBytes">
|
||||
<property name="text">
|
||||
<string notr="true"> - </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="pbarProgress">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignRight">
|
||||
<widget class="QPushButton" name="pbCancel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
|
||||
|
||||
|
||||
#include "tpm_topics_model.h"
|
||||
|
||||
#include <TopicEditorBase/topic_editor_utils.h>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
TopicsModel::TopicsModel(QObject *parent)
|
||||
: LtsWidgets::LtsAbstractTableModel<Topic>(
|
||||
{ tr("id"), tr("Channel"), tr("Filter"), tr("Description"), tr("Type"), tr("Start [mm:ss]"), tr("Duration [mm:ss]")}, parent)
|
||||
, m_logger(getLogger("TopicsModel"))
|
||||
{}
|
||||
|
||||
|
||||
void TopicsModel::addTopic(const Topic &topic)
|
||||
{
|
||||
insertRow(topic);
|
||||
}
|
||||
|
||||
|
||||
QVariant TopicsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!indexIsValid(index))
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::UserRole)
|
||||
{
|
||||
const auto &topic = m_vecData[index.row()];
|
||||
return topic.id;
|
||||
}
|
||||
|
||||
if (role != Qt::DisplayRole)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto channelName = [](const int channel)
|
||||
{
|
||||
switch (channel)
|
||||
{
|
||||
case 1: return tr("Audio");
|
||||
case 2: return tr("Replay");
|
||||
case 3: return tr("Screen 1");
|
||||
case 4: return tr("Screen 2");
|
||||
default: { assert(false); return L("UNKNOWN"); }
|
||||
}
|
||||
};
|
||||
|
||||
const auto &topic = m_vecData[index.row()];
|
||||
|
||||
switch (index.column())
|
||||
{
|
||||
case ColId: return topic.id;
|
||||
case ColChannel: return channelName(topic.channel);
|
||||
case ColFilter: return (topic.filterSet ? QString::number(topic.filter) : L(" - "));
|
||||
case ColDescription: return (topic.description.isEmpty() ? L(" - ") : topic.description);
|
||||
case ColType: return TopicEditorBase::topicTypeTranslation((TopicEditorBase::TopicType)topic.type);
|
||||
case ColStart: return QTime(0, 0).addSecs(topic.start).toString(L("mm:ss"));
|
||||
case ColDuration: return QTime(0, 0).addSecs(topic.duration).toString(L("mm:ss"));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <logger.h>
|
||||
#include <Common2/thp2common_export.h>
|
||||
#include <Common2Core/tpm_js_parser.h>
|
||||
|
||||
#include <LtsWidgets/lts_abstract_table_model.h>
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
class THP2COMMON_EXPORT TopicsModel : public LtsWidgets::LtsAbstractTableModel<Topic>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Columns
|
||||
{
|
||||
ColId,
|
||||
ColChannel,
|
||||
ColFilter,
|
||||
ColDescription,
|
||||
ColType,
|
||||
ColStart,
|
||||
ColDuration,
|
||||
};
|
||||
|
||||
explicit TopicsModel(QObject *parent = nullptr);
|
||||
|
||||
void addTopic(const Topic &topic);
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
private:
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
}
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# $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, 2024-01-11
|
||||
#
|
||||
|
||||
SET(src
|
||||
#logger.cpp
|
||||
#logger.h
|
||||
tpm_js_parser.cpp
|
||||
tpm_js_parser.h
|
||||
tpm_tools.cpp
|
||||
tpm_tools.h
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY2(WS SV Thp2CommonCore SHARED ${src}) # SV because FLM Service (httq server) uses Thp2CommonCore, too
|
||||
NGL_TARGET_COMPILE_DEFINITIONS(Thp2CommonCore PRIVATE L=QStringLiteral)
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(Thp2CommonCore
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../ # export file
|
||||
)
|
||||
NGL_TARGET_LINK_LIBRARIES(Thp2CommonCore
|
||||
PRIVATE
|
||||
Qt5::Core
|
||||
#NGLLogger
|
||||
LtsUtilsCoreHeaderOnly
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(Thp2CommonCore)
|
||||
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
|
||||
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
LOGGER_T getLogger(const char *name)
|
||||
{
|
||||
static const char ns[] = "TPM2.CommonCore.";
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
|
||||
|
||||
|
||||
#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());
|
||||
}
|
||||
}
|
||||
qt6-after/qt6-migrated-legatus/Lts/Plugins/TakeHomePackageManager/THP2/Common2Core/tpm_js_parser.cpp
Executable
+537
@@ -0,0 +1,537 @@
|
||||
|
||||
|
||||
|
||||
#include "tpm_js_parser.h"
|
||||
|
||||
|
||||
#include <tpm_tools.h>
|
||||
|
||||
#include <LtsUtilsCore/json.h>
|
||||
|
||||
#include <QMapIterator>
|
||||
|
||||
#include <utility>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::string error(const QString &msg) { return msg.toStdString(); }
|
||||
using namespace std;
|
||||
#ifndef DLOG
|
||||
#define DLOG DEBUG_LOG(m_logger)
|
||||
#endif
|
||||
using namespace LtsUtils;
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
template<>
|
||||
JsonObject JsParser<JsonObject, QJsonObject>::parse(const QByteArray &data) noexcept(false)
|
||||
{
|
||||
return JsonObject::fromJsonSafe(_parse(data, true).toUtf8());
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
JsParser<JsonObject, QJsonObject>::JsParser(const QString &globalThisName, const QString &filePath, bool mustExist) noexcept(false)
|
||||
: m_globalThisName(globalThisName)
|
||||
, m_filePath(filePath)
|
||||
, m_json(load(mustExist))
|
||||
|
||||
{
|
||||
qWarning() << "OBJECT - m_globalThisName" << m_globalThisName << "m_filePath" << m_filePath << "m_json" << m_json.object().count();
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
QJsonDocument JsParser<JsonObject, QJsonObject>::document() const
|
||||
{
|
||||
return QJsonDocument(static_cast<const QJsonObject &>(m_json));
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
const QJsonObject &JsParser<JsonObject, QJsonObject>::get() const
|
||||
{
|
||||
return m_json;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
JsonArray JsParser<JsonArray, QJsonArray>::parse(const QByteArray &data) noexcept(false)
|
||||
{
|
||||
return JsonArray::fromJsonSafe(_parse(data, false).toUtf8());
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
JsParser<JsonArray, QJsonArray>::JsParser(const QString &globalThisName, const QString &filePath, bool mustExist) noexcept(false)
|
||||
: m_globalThisName(globalThisName)
|
||||
, m_filePath(filePath)
|
||||
, m_json(load(mustExist))
|
||||
|
||||
{
|
||||
qWarning() << "ARRAY - m_globalThisName" << m_globalThisName << "m_filePath" << m_filePath << "m_json" << m_json.size();
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
QJsonDocument JsParser<JsonArray, QJsonArray>::document() const
|
||||
{
|
||||
return QJsonDocument(m_json());
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
const QJsonArray &JsParser<JsonArray, QJsonArray>::get() const
|
||||
{
|
||||
return m_json();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename LtsJsonType, typename QJsonType>
|
||||
void JsParser<LtsJsonType, QJsonType>::save() noexcept(false)
|
||||
{
|
||||
if (!m_dataChanged)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto file(openSaveFile(m_filePath));
|
||||
file->write(L("globalThis.%1 = \n").arg(m_globalThisName).toUtf8());
|
||||
file->write(document().toJson(QJsonDocument::JsonFormat::Indented ));
|
||||
file->write(";");
|
||||
}
|
||||
|
||||
|
||||
template<typename LtsJsonType, typename QJsonType>
|
||||
QByteArray JsParser<LtsJsonType, QJsonType>:: _load(bool mustExist) const noexcept(false)
|
||||
{
|
||||
const auto data(readFile(m_filePath, mustExist));
|
||||
|
||||
qWarning() << "JsParser: file" << m_filePath << "######################## content:" << data;
|
||||
|
||||
if (data.isEmpty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
template<typename LtsJsonType, typename QJsonType>
|
||||
QString JsParser<LtsJsonType, QJsonType>::_parse(const QByteArray &data, bool isObject) noexcept(false)
|
||||
{
|
||||
const QString opener((isObject) ? L("{") : L("["));
|
||||
const QString closer((isObject) ? L("}") : L("]"));
|
||||
|
||||
const QRegExp regexp(L(R"RRR(\%1[\S\s]*\%2)RRR").arg(opener, closer));
|
||||
const QString topicsJsText(QString::fromUtf8(data));
|
||||
|
||||
|
||||
if (regexp.indexIn(topicsJsText) < 0)
|
||||
{
|
||||
|
||||
throw runtime_error(L("no %1 in js-file found").arg(isObject ? L("object") : L("array")).toStdString());
|
||||
}
|
||||
|
||||
|
||||
const QString jsonString(regexp.cap());
|
||||
|
||||
|
||||
qWarning() << "jsonString" << jsonString;
|
||||
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
|
||||
template<typename LtsJsonType, typename QJsonType>
|
||||
LtsJsonType JsParser<LtsJsonType, QJsonType>::load(bool mustExist) const noexcept(false)
|
||||
{
|
||||
auto data = _load(mustExist);
|
||||
|
||||
if (data.isEmpty())
|
||||
{
|
||||
if (mustExist)
|
||||
{
|
||||
throw runtime_error("js-file is missing content");
|
||||
}
|
||||
|
||||
qWarning() << ":(";
|
||||
return {};
|
||||
}
|
||||
|
||||
return parse(data);
|
||||
}
|
||||
|
||||
|
||||
template<typename LtsJsonType, typename QJsonType>
|
||||
void JsParser<LtsJsonType, QJsonType>::write(const QString &globalThisName, const QJsonType &json, const QString &filePath, bool mustExist) noexcept(false)
|
||||
{
|
||||
auto parser = JsParser<LtsJsonType, QJsonType>(globalThisName, filePath, mustExist);
|
||||
parser.set(json);
|
||||
parser.save();
|
||||
}
|
||||
template void JsParser<JsonObject, QJsonObject>::write(const QString &globalThisName, const QJsonObject &json, const QString &filePath, bool mustExist) noexcept(false);
|
||||
template void JsParser<JsonArray, QJsonArray>::write(const QString &globalThisName, const QJsonArray &json, const QString &filePath, bool mustExist) noexcept(false);
|
||||
|
||||
|
||||
template<typename LtsJsonType, typename QJsonType>
|
||||
void JsParser<LtsJsonType, QJsonType>::set(const QJsonType &json)
|
||||
{
|
||||
m_json = LtsJsonType(json);
|
||||
m_dataChanged = true;
|
||||
}
|
||||
template void JsParser<JsonObject, QJsonObject>::set(const QJsonObject &json);
|
||||
template void JsParser<JsonArray, QJsonArray>::set(const QJsonArray &json);
|
||||
|
||||
|
||||
template<typename LtsJsonType, typename QJsonType>
|
||||
QJsonType JsParser<LtsJsonType, QJsonType>::read(const QString &globalThisName, const QString &filePath, bool mustExist) noexcept(false)
|
||||
{
|
||||
return JsParser(globalThisName, filePath, mustExist).get();
|
||||
}
|
||||
template QJsonObject JsParser<JsonObject, QJsonObject>::read(const QString &globalThisName, const QString &filePath, bool mustExist) noexcept(false);
|
||||
template QJsonArray JsParser<JsonArray, QJsonArray>::read(const QString &globalThisName, const QString &filePath, bool mustExist) noexcept(false);
|
||||
|
||||
|
||||
|
||||
|
||||
ScriptsJsParser::ScriptsJsParser(const QString &root, bool mustExist) noexcept(false)
|
||||
: JsObjectParser(L("scriptsJs"), scriptsJsLocation(root), mustExist)
|
||||
{
|
||||
const QJsonObject obj(get());
|
||||
qWarning() << "ScriptsJsParser" << obj;
|
||||
|
||||
for (auto it = obj.constBegin(); it != obj.constEnd(); ++it)
|
||||
{
|
||||
const auto esi(ExportedScriptInformation::fromJson(it.value(), true , ExportedScriptInformation::UploadState::ThpUnknownUploadState));
|
||||
qWarning() << "adding" << esi.id();
|
||||
|
||||
|
||||
|
||||
|
||||
m_esis.insert(esi.id(), esi);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ScriptsJsParser::save() noexcept(false)
|
||||
{
|
||||
QJsonObject obj;
|
||||
|
||||
QMapIterator<QString, ExportedScriptInformation> it(m_esis);
|
||||
while (it.hasNext())
|
||||
{
|
||||
auto &esi = it.next().value();
|
||||
obj.insert(esi.id(), esi.toJson());
|
||||
}
|
||||
|
||||
set(obj);
|
||||
JsParser::save();
|
||||
}
|
||||
|
||||
|
||||
const QMap<QString, ExportedScriptInformation> &ScriptsJsParser::getExportedScriptInformations() const
|
||||
{
|
||||
return m_esis;
|
||||
}
|
||||
|
||||
|
||||
const ExportedScriptInformation &ScriptsJsParser::getExportedScriptInformation(const QString &scriptId) const noexcept(false)
|
||||
{
|
||||
auto it = m_esis.constFind(scriptId);
|
||||
|
||||
if (it == m_esis.constEnd())
|
||||
{
|
||||
throw L("getExportedScriptInformation: script with id '%1' not found").arg(scriptId);
|
||||
}
|
||||
|
||||
return it.value();
|
||||
}
|
||||
|
||||
|
||||
bool ScriptsJsParser::hasExportedScriptInformation(const QString &scriptId) const
|
||||
{
|
||||
return m_esis.contains(scriptId);
|
||||
}
|
||||
|
||||
|
||||
void ScriptsJsParser::setExportedScriptInformations(const QVector<ExportedScriptInformation> &esis) noexcept(false)
|
||||
{
|
||||
m_esis.clear();
|
||||
addExportedScriptInformations(esis);
|
||||
}
|
||||
|
||||
|
||||
void ScriptsJsParser::addExportedScriptInformations(const QVector<ExportedScriptInformation> &esis) noexcept(false)
|
||||
{
|
||||
for (const ExportedScriptInformation &esi : esis)
|
||||
addExportedScriptInformation(esi);
|
||||
}
|
||||
|
||||
|
||||
void ScriptsJsParser::addExportedScriptInformation(const ExportedScriptInformation &esi) noexcept(false)
|
||||
{
|
||||
m_esis.insert(esi.id(), esi);
|
||||
m_dataChanged = true;
|
||||
}
|
||||
|
||||
|
||||
void ScriptsJsParser::removeExportedScriptInformation(const QString &scriptId) noexcept(false)
|
||||
{
|
||||
if (m_esis.remove(scriptId) != 1)
|
||||
{
|
||||
throw L("removeExportedScriptInformation: unable to remove script with id '%1' because it was not found").arg(scriptId);
|
||||
}
|
||||
|
||||
m_dataChanged = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
QJsonObject dateTimeToJson(const QDateTime &dt)
|
||||
{
|
||||
return { { L("iso"), dt.toUTC().toString(Qt::DateFormat::ISODate) }, { L("secsSinceEpoch"), dt.toSecsSinceEpoch() } };
|
||||
}
|
||||
|
||||
|
||||
QDateTime jsonToDateTime(const QJsonValue &val) noexcept(false)
|
||||
{
|
||||
bool k = false;
|
||||
auto ret = QDateTime::fromSecsSinceEpoch(val.toObject()[L("secsSinceEpoch")].toVariant().toULongLong(&k));
|
||||
|
||||
if (!k)
|
||||
{
|
||||
throw std::runtime_error("jsonToDateTime: !k");
|
||||
}
|
||||
|
||||
if (!ret.isValid())
|
||||
{
|
||||
throw std::runtime_error("jsonToDateTime: !ret.isValid()");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QString ExportedScriptInformation::id() const
|
||||
{
|
||||
return L("%1.%2%3").arg(exercise.id).arg(script.id).arg(script.customName.isEmpty() ? QString() : L("-%1").arg(script.customName));
|
||||
}
|
||||
|
||||
|
||||
QJsonValue ExportedScriptInformation::toJson() const
|
||||
{
|
||||
QJsonObject ret({
|
||||
{ L("createdAt"), dateTimeToJson(createdAt) },
|
||||
{ L("exerciseId"), exercise.id },
|
||||
{ L("exerciseName"), exercise.name },
|
||||
{ L("scriptId"), script.id },
|
||||
{ L("scriptName"), script.name }
|
||||
});
|
||||
|
||||
if (!script.customName.isEmpty())
|
||||
{
|
||||
ret[L("scriptCustomName")] = script.customName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ExportedScriptInformation ExportedScriptInformation::fromJson(const QJsonValue &val,
|
||||
const bool local,
|
||||
const UploadState uploadState) noexcept(false)
|
||||
{
|
||||
const QJsonObject obj(val.toObject());
|
||||
|
||||
ExportedScriptInformation esi;
|
||||
|
||||
const auto createdAt = jsonToDateTime(obj[L("createdAt")]);
|
||||
|
||||
if (local)
|
||||
{
|
||||
esi.createdAt = createdAt;
|
||||
}
|
||||
|
||||
if (uploadState == UploadState::ThpAlreadyUploaded)
|
||||
{
|
||||
esi.createdAtServer = createdAt;
|
||||
}
|
||||
|
||||
esi.script.id = obj[L("scriptId")].toInt();
|
||||
esi.script.name = obj[L("scriptName")].toString();
|
||||
|
||||
auto scriptCustomNameValIt = obj.constFind(L("scriptCustomName"));
|
||||
if (scriptCustomNameValIt != obj.constEnd())
|
||||
{
|
||||
esi.script.customName = scriptCustomNameValIt->toString();
|
||||
}
|
||||
|
||||
esi.exercise.id = obj[L("exerciseId")].toInt();
|
||||
esi.exercise.name = obj[L("exerciseName")].toString();
|
||||
|
||||
if (esi.script.id < 1 || esi.script.name.isEmpty() || esi.exercise.id < 1 || esi.exercise.name.isEmpty())
|
||||
{
|
||||
throw std::runtime_error("one or more of (script id / script name / exc id / exc name) missing");
|
||||
}
|
||||
|
||||
esi.local = local;
|
||||
esi.uploaded = uploadState;
|
||||
|
||||
return esi;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Topic Topic::topicFromJson(const QJsonObject &obj) noexcept(false)
|
||||
{
|
||||
const JsonObject o(obj);
|
||||
|
||||
|
||||
const auto filterKey = JsonObject::Key(L("filter"));
|
||||
|
||||
Topic t;
|
||||
|
||||
t.id = o.value<int>(L("id"));
|
||||
t.channel = o.value<int>(L("channel"));
|
||||
t.description = o.value<QString>(L("description"));
|
||||
t.type = o.value<int>(L("type"));
|
||||
t.start = o.value<int>(L("start"));
|
||||
t.duration = o.value<int>(L("duration"));
|
||||
t.filename = o.valueOr<QString>(L("filename"), o.valueOr<QString>(L("src"), {}));
|
||||
t.filter = (o.isNull(filterKey) ? -1 : o.value<int>(filterKey));
|
||||
t.filterSet = o.contains<int>(filterKey);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
QVector<Topic> Topic::topicsFromJson(const QJsonArray &arr) noexcept(false)
|
||||
{
|
||||
QVector<Topic> ret;
|
||||
for (const auto &val : arr)
|
||||
{
|
||||
const QJsonObject obj(val.toObject());
|
||||
ret += topicFromJson(obj);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
Executable
+183
@@ -0,0 +1,183 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
#include <Common2Core/thp2commoncore_export.h>
|
||||
|
||||
|
||||
#include <LtsUtilsCore/json.h>
|
||||
#include <Common2Core/tpm_tools.h>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QFile>
|
||||
#include <QMap>
|
||||
|
||||
|
||||
using namespace LtsUtils;
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
template <typename LtsJsonType, typename QJsonType>
|
||||
class THP2COMMONCORE_EXPORT JsParser
|
||||
{
|
||||
public:
|
||||
explicit JsParser(const QString &globalThisName, const QString &filePath, bool mustExist = false) noexcept(false);
|
||||
|
||||
void save() noexcept(false);
|
||||
static QJsonType read(const QString &globalThisName, const QString &filePath, bool mustExist = false) noexcept(false);
|
||||
static void write(const QString &globalThisName, const QJsonType &json, const QString &filePath, bool mustExist = false) noexcept(false);
|
||||
static LtsJsonType parse(const QByteArray &data) noexcept(false);
|
||||
QJsonDocument document() const;
|
||||
const QJsonType &get() const;
|
||||
void set(const QJsonType &json);
|
||||
|
||||
protected:
|
||||
LtsJsonType load(bool mustExist) const noexcept(false);
|
||||
static QString _parse(const QByteArray &data, bool isObject) noexcept(false);
|
||||
QByteArray _load(bool mustExist) const noexcept(false);
|
||||
|
||||
const QString m_globalThisName;
|
||||
const QString m_filePath;
|
||||
LtsJsonType m_json;
|
||||
bool m_dataChanged { false };
|
||||
};
|
||||
|
||||
template class THP2COMMONCORE_EXPORT JsParser<JsonObject, QJsonObject>;
|
||||
using JsObjectParser = JsParser<JsonObject, QJsonObject>;
|
||||
|
||||
template class THP2COMMONCORE_EXPORT JsParser<JsonArray, QJsonArray>;
|
||||
using JsArrayParser = JsParser<JsonArray, QJsonArray>;
|
||||
|
||||
|
||||
|
||||
|
||||
struct THP2COMMONCORE_EXPORT ExportedScriptInformation
|
||||
{
|
||||
enum class UploadState
|
||||
{
|
||||
ThpUnknownUploadState,
|
||||
ThpNotUploaded,
|
||||
ThpAlreadyUploaded,
|
||||
};
|
||||
|
||||
|
||||
struct Exercise
|
||||
{
|
||||
int id;
|
||||
QString name;
|
||||
};
|
||||
|
||||
|
||||
struct Script
|
||||
{
|
||||
int id;
|
||||
QString name;
|
||||
QString customName;
|
||||
};
|
||||
|
||||
bool operator==(const ExportedScriptInformation &other) const
|
||||
{
|
||||
return (id() == other.id());
|
||||
}
|
||||
|
||||
QString id() const;
|
||||
|
||||
|
||||
QDateTime createdAt;
|
||||
Exercise exercise;
|
||||
Script script;
|
||||
|
||||
|
||||
|
||||
QDateTime createdAtServer = {};
|
||||
bool local = true;
|
||||
UploadState uploaded = UploadState::ThpUnknownUploadState;
|
||||
|
||||
|
||||
void setLocal(const QDateTime &_createdAt) { local = true; createdAt = _createdAt; }
|
||||
void unsetLocal() { local = false; createdAt = {}; }
|
||||
bool isUploaded() const { return (uploaded == UploadState::ThpAlreadyUploaded); }
|
||||
void setUploaded(const QDateTime &_createdAtServer) { uploaded = UploadState::ThpAlreadyUploaded; createdAtServer = _createdAtServer; }
|
||||
void unsetUploaded() { uploaded = UploadState::ThpNotUploaded; createdAtServer= {}; }
|
||||
QJsonValue toJson() const;
|
||||
static ExportedScriptInformation fromJson(const QJsonValue &val,
|
||||
const bool local = true,
|
||||
const UploadState uploadState = UploadState::ThpUnknownUploadState);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class THP2COMMONCORE_EXPORT ScriptsJsParser : public JsObjectParser
|
||||
{
|
||||
public:
|
||||
ScriptsJsParser(const QString &root, bool mustExist = false) noexcept(false);
|
||||
|
||||
void save() noexcept(false);
|
||||
const QMap<QString, ExportedScriptInformation> &getExportedScriptInformations() const;
|
||||
const ExportedScriptInformation &getExportedScriptInformation(const QString &scriptId) const noexcept(false);
|
||||
bool hasExportedScriptInformation(const QString &scriptId) const;
|
||||
void setExportedScriptInformations(const QVector<ExportedScriptInformation> &esis) noexcept(false);
|
||||
void addExportedScriptInformation(const ExportedScriptInformation &esi) noexcept(false);
|
||||
void addExportedScriptInformations(const QVector<ExportedScriptInformation> &esis) noexcept(false);
|
||||
void removeExportedScriptInformation(const QString &scriptId) noexcept(false);
|
||||
|
||||
private:
|
||||
QMap<QString , ExportedScriptInformation> m_esis;
|
||||
bool m_dataChanged { false };
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
struct THP2COMMONCORE_EXPORT Topic
|
||||
{
|
||||
int id;
|
||||
int channel;
|
||||
QString description;
|
||||
int type;
|
||||
int start;
|
||||
int duration;
|
||||
QString filename;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int filter;
|
||||
bool filterSet = false;
|
||||
|
||||
static Topic topicFromJson(const QJsonObject &obj) noexcept(false);
|
||||
static QVector<Topic> topicsFromJson(const QJsonArray &arr) noexcept(false);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
Executable
+152
@@ -0,0 +1,152 @@
|
||||
|
||||
|
||||
|
||||
#include "tpm_tools.h"
|
||||
#include "tpm_js_parser.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QSaveFile>
|
||||
#include <QDir>
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
SaveFile::SaveFile(SaveFile &&other)
|
||||
{
|
||||
m_saveFile.swap(other.m_saveFile);
|
||||
}
|
||||
|
||||
|
||||
SaveFile::SaveFile(const QString &filePath) noexcept(false)
|
||||
: m_saveFile(new QSaveFile(filePath))
|
||||
{}
|
||||
|
||||
|
||||
SaveFile::~SaveFile() noexcept(false)
|
||||
{
|
||||
if (!m_saveFile->isOpen())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_saveFile->commit())
|
||||
{
|
||||
throw std::runtime_error("Commit on temporary save file '" + m_saveFile->fileName().toStdString() + "' failed: " + m_saveFile->errorString().toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SaveFile SaveFile::open(const QString &filePath) noexcept(false)
|
||||
{
|
||||
SaveFile f(filePath);
|
||||
if (!f->open(QIODevice::OpenModeFlag::WriteOnly ))
|
||||
{
|
||||
throw runtime_error("Failed to create temporary save file '" + filePath.toStdString() + "': " + f->errorString().toStdString());
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
QString localThpDir() { return (QDir::homePath() + QDir::separator() + L("THP2")); }
|
||||
QString serverThpDir() { return (QDir::homePath() + QDir::separator() + L("THP2-Uploads")); }
|
||||
QString thpOverviewLocation() { return L("/shd/CTC/WS/share/Lts/THP-overview"); }
|
||||
QString scriptsLocation(const QString &root) { return root + QDir::separator() + L("scripts"); }
|
||||
QString scriptsJsLocation(const QString &root) { return scriptsLocation(root) + QDir::separator() + L("scripts.js"); }
|
||||
QString scriptLocation(const QString &root, const QString &scriptId) { return scriptsLocation(root) + QDir::separator() + scriptId; }
|
||||
QString scriptHtmlLocation(const QString &root, const QString &scriptId) { return scriptLocation(root, scriptId) + QDir::separator() + L("index.html"); }
|
||||
QString scriptArchiveLocation(const QString &root, const QString &scriptId) { return scriptLocation(root, scriptId) + L(".tar.bz2"); }
|
||||
QString topicsLocation(const QString &root, const QString &scriptId) { return scriptLocation(root, scriptId) + QDir::separator() + L("topics"); }
|
||||
QString topicsJsLocation(const QString &root, const QString &scriptId) { return topicsLocation(root, scriptId) + QDir::separator() + L("topics.js"); }
|
||||
QString topicLocation(const QString &root, const QString &scriptId, const QString &topicFilename) { return topicsLocation(root, scriptId) + QDir::separator() + topicFilename; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SaveFile openSaveFile(const QString &filePath) noexcept(false)
|
||||
{
|
||||
return SaveFile::open(filePath);
|
||||
}
|
||||
|
||||
|
||||
void writeSaveFile(const QString &filePath, const QByteArray &content) noexcept(false)
|
||||
{
|
||||
SaveFile f(openSaveFile(filePath));
|
||||
f->write(content);
|
||||
}
|
||||
|
||||
|
||||
QByteArray readFile(const QString &filename, bool mustExist) noexcept(false)
|
||||
{
|
||||
QFile f(filename);
|
||||
|
||||
if (!f.exists())
|
||||
{
|
||||
if (mustExist)
|
||||
{
|
||||
throw runtime_error("File '" + filename.toStdString() + "' doesn't exist");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!f.open(QIODevice::ReadOnly))
|
||||
{
|
||||
throw runtime_error("Unable to read file at " + filename.toStdString() + ": " + f.errorString().toStdString());
|
||||
}
|
||||
|
||||
return f.readAll();
|
||||
}
|
||||
|
||||
|
||||
void openFile(QFile &file, bool readOnly, bool existingOnly) noexcept(false)
|
||||
{
|
||||
QFlags<QIODevice::OpenModeFlag> flags(QIODevice::ReadWrite);
|
||||
if (readOnly)
|
||||
{
|
||||
flags.setFlag(QIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (!file.exists() && existingOnly)
|
||||
{
|
||||
throw runtime_error("Unable to open file '" + file.fileName().toStdString() + "' for " + (readOnly ? "reading" : "r/w") + ": <file does not exist!>");
|
||||
}
|
||||
|
||||
if (!file.open(flags))
|
||||
{
|
||||
throw runtime_error("Unable to open file '" + file.fileName().toStdString() + "' for " + (readOnly ? "reading" : "r/w") + ": " + file.errorString().toStdString());
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+167
@@ -0,0 +1,167 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
||||
#include <Common2Core/thp2commoncore_export.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QSaveFile>
|
||||
#include <QEventLoop>
|
||||
#include <QTimer>
|
||||
#include <QDateTime>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#define _compiler_warning(x) _Pragma(#x)
|
||||
#define compiler_warning(x) _compiler_warning(GCC warning x)
|
||||
#else
|
||||
#define compiler_warning(x)
|
||||
#endif
|
||||
|
||||
|
||||
class QFile;
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
class THP2COMMONCORE_EXPORT SaveFile
|
||||
{
|
||||
public:
|
||||
SaveFile() = delete;
|
||||
SaveFile(SaveFile &&other);
|
||||
~SaveFile() noexcept(false);
|
||||
|
||||
static SaveFile open(const QString &filePath) noexcept(false);
|
||||
|
||||
QSaveFile *get() { return m_saveFile.get(); }
|
||||
QSaveFile *operator->() { return m_saveFile.get(); }
|
||||
|
||||
private:
|
||||
explicit SaveFile(const QString &filePath) noexcept(false);
|
||||
|
||||
std::unique_ptr<QSaveFile> m_saveFile;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
THP2COMMONCORE_EXPORT void writeSaveFile(const QString &filePath, const QByteArray &content) noexcept(false);
|
||||
THP2COMMONCORE_EXPORT QByteArray readFile(const QString &filename, bool mustExist = false) noexcept(false);
|
||||
THP2COMMONCORE_EXPORT void openFile(QFile &file, bool readOnly = false, bool existingOnly = false) noexcept(false);
|
||||
THP2COMMONCORE_EXPORT void writeToFileOrRollback(QFile &file, const QByteArray &data) noexcept(false);
|
||||
THP2COMMONCORE_EXPORT void writeToFileOrRollback(QFile &file, const QString &string) noexcept(false);
|
||||
THP2COMMONCORE_EXPORT SaveFile openSaveFile(const QString &filePath) noexcept(false);
|
||||
|
||||
|
||||
|
||||
THP2COMMONCORE_EXPORT QString localThpDir();
|
||||
THP2COMMONCORE_EXPORT QString serverThpDir();
|
||||
THP2COMMONCORE_EXPORT QString thpOverviewLocation();
|
||||
THP2COMMONCORE_EXPORT QString scriptsLocation(const QString &root);
|
||||
THP2COMMONCORE_EXPORT QString scriptsJsLocation(const QString &root);
|
||||
THP2COMMONCORE_EXPORT QString scriptLocation(const QString &root, const QString &scriptId);
|
||||
THP2COMMONCORE_EXPORT QString scriptHtmlLocation(const QString &root, const QString &scriptId);
|
||||
THP2COMMONCORE_EXPORT QString scriptArchiveLocation(const QString &root, const QString &scriptId);
|
||||
THP2COMMONCORE_EXPORT QString topicsJsLocation(const QString &root, const QString &scriptId);
|
||||
THP2COMMONCORE_EXPORT QString topicsLocation(const QString &root, const QString &scriptId);
|
||||
THP2COMMONCORE_EXPORT QString topicLocation(const QString &root, const QString &scriptId, const QString &topicFilename);
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename Func1>
|
||||
void awaitx(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
|
||||
const int timeoutMs = 0)
|
||||
{
|
||||
QEventLoop x;
|
||||
QObject::connect(sender, signal, &x, &QEventLoop::quit);
|
||||
|
||||
std::unique_ptr<QTimer> t(new QTimer);
|
||||
bool timeout = false;
|
||||
if (timeoutMs > 0)
|
||||
{
|
||||
t->setInterval(timeoutMs);
|
||||
t->setSingleShot(true);
|
||||
QObject::connect(t.get(), &QTimer::timeout, &x, [&timeout](){ timeout = true; });
|
||||
QObject::connect(t.get(), &QTimer::timeout, &x, &QEventLoop::quit);
|
||||
}
|
||||
|
||||
x.exec();
|
||||
|
||||
if (timeout)
|
||||
{
|
||||
throw std::runtime_error("Timeout.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename Func1, typename Func2>
|
||||
void awaitWithCancelationSignalx(const typename QtPrivate::FunctionPointer<Func1>::Object *sender1, Func1 signal1,
|
||||
const typename QtPrivate::FunctionPointer<Func2>::Object *sender2, Func2 signal2,
|
||||
const int timeoutMs = 0)
|
||||
{
|
||||
QEventLoop x;
|
||||
bool success = false;
|
||||
QObject::connect(sender1, signal1, &x, [&success](){ success = true; });
|
||||
QObject::connect(sender1, signal1, &x, &QEventLoop::quit, Qt::QueuedConnection);
|
||||
|
||||
|
||||
|
||||
QObject::connect(sender2, signal2, &x, &QEventLoop::quit, Qt::QueuedConnection);
|
||||
|
||||
std::unique_ptr<QTimer> t(new QTimer);
|
||||
bool timeout = false;
|
||||
if (timeoutMs > 0)
|
||||
{
|
||||
t->setInterval(timeoutMs);
|
||||
t->setSingleShot(true);
|
||||
QObject::connect(t.get(), &QTimer::timeout, &x, [&timeout](){ timeout = true; });
|
||||
QObject::connect(t.get(), &QTimer::timeout, &x, &QEventLoop::quit, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
|
||||
x.exec();
|
||||
|
||||
|
||||
if (timeout && !success)
|
||||
{
|
||||
throw std::runtime_error("Timeout.");
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw std::runtime_error("Canceled.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
qt6-after/qt6-migrated-legatus/Lts/Plugins/TakeHomePackageManager/THP2/Thp2Management/CMakeLists.txt
Executable
+47
@@ -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)
|
||||
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
|
||||
|
||||
|
||||
#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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
|
||||
|
||||
|
||||
#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());
|
||||
}
|
||||
}
|
||||
+973
@@ -0,0 +1,973 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#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)
|
||||
#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)
|
||||
{
|
||||
|
||||
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,
|
||||
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);
|
||||
}
|
||||
catch(const std::runtime_error &err)
|
||||
{
|
||||
qWarning() << "## aborted" << err.what();
|
||||
compress->abort();
|
||||
compress->waitForFinished();
|
||||
compress->deleteLater();
|
||||
throw err;
|
||||
}
|
||||
|
||||
|
||||
const bool success = compress->waitForFinished();
|
||||
|
||||
compress->deleteLater();
|
||||
|
||||
|
||||
if (!success || !errorString.isEmpty())
|
||||
{
|
||||
throw error(L("compress failed: %1").arg(errorString));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
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)
|
||||
{
|
||||
|
||||
|
||||
|
||||
std::unique_ptr<NoticeDummy> dummy(new NoticeDummy);
|
||||
connect(r, &QNetworkReply::finished, dummy.get(), &NoticeDummy::signal, Qt::QueuedConnection);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
|
||||
|
||||
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));
|
||||
waitForAnswer(r, 200);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
const QString dstDir(esi.id());
|
||||
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(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)
|
||||
{
|
||||
|
||||
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).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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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,
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
CopyInfoList fileList;
|
||||
fileList += getFilesFromFolder(tmpDir.path() + QDir::separator() + scriptId, scriptLocationString);
|
||||
m_process->hide();
|
||||
dialog->unsetOverlay();
|
||||
m_process->hide();
|
||||
copyFiles(fileList, dialog);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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(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);
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Remove)->setEnabled(false);
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setEnabled(false);
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Import)->setEnabled(false);
|
||||
|
||||
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);
|
||||
m_buttonBox->connectButton(LtsWidgets::DialogButton::Show, this, &ExportedScriptsDialog::openScript);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_buttonBox->connectButton(LtsWidgets::DialogButton::Save, this, &ExportedScriptsDialog::uploadScript);
|
||||
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 );
|
||||
|
||||
|
||||
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 );
|
||||
|
||||
|
||||
connect(sock, &QWebSocket::textMessageReceived,
|
||||
this, [this](const QString &msg)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "new msg: '" << msg << "'";
|
||||
m_lastServerContent = msg;
|
||||
m_model->setServerContent(msg);
|
||||
onSelectionChanged();
|
||||
});
|
||||
|
||||
|
||||
initModel();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
ExportedScriptsDialog::~ExportedScriptsDialog()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
|
||||
bool ExportedScriptsDialog::isOCApp() const
|
||||
{
|
||||
|
||||
return (m_session->app() == LtsAppDescriptor::App::OC);
|
||||
}
|
||||
|
||||
|
||||
void ExportedScriptsDialog::onSelectionChanged()
|
||||
{
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Show)->setEnabled(false);
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setEnabled(false);
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Remove)->setEnabled(false);
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Import)->setEnabled(false);
|
||||
|
||||
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);
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setEnabled(enableDownload || enableUpload);
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Remove)->setEnabled(enableRemoveLocal);
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Import)->setEnabled(enableRemoveServer);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
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());
|
||||
m_ui->ltsDataView->setModel(m_model);
|
||||
|
||||
|
||||
#ifndef LTS_DEBUG
|
||||
m_ui->ltsDataView->hideColumn(ExportedScriptsModel::ColId);
|
||||
#endif
|
||||
|
||||
m_ui->ltsDataView->hideColumn(ExportedScriptsModel::Columns::ColUploadStatus);
|
||||
|
||||
m_model->setServerContent(m_lastServerContent);
|
||||
onSelectionChanged();
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
|
||||
}
|
||||
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());
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
m_model->writeScriptsJson();
|
||||
break;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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());
|
||||
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
|
||||
m_model->removeLocalScript(scriptId);
|
||||
onSelectionChanged();
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
|
||||
|
||||
#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;
|
||||
};
|
||||
}
|
||||
+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>
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
|
||||
|
||||
|
||||
#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.createWidgetFunc = [this](QWidget *)
|
||||
{
|
||||
return LtsWidgets::createThrowableDialog<ExportedScriptsDialog>(m_session, nullptr);
|
||||
};
|
||||
ret.append(plData);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
|
||||
|
||||
|
||||
#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:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#
|
||||
# $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, 2024-01-11
|
||||
#
|
||||
|
||||
SET(src
|
||||
tpm_usb_gui_plugin.cpp
|
||||
tpm_usb_gui_plugin.h
|
||||
tpm_usb_dialog.cpp
|
||||
tpm_usb_dialog.h
|
||||
logger.cpp
|
||||
logger.h
|
||||
)
|
||||
|
||||
QT5_WRAP_UI(src
|
||||
tpm_usb2_dialog.ui
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY2(WS Thp2UsbManagementGuiPlugin MODULE ${src})
|
||||
NGL_TARGET_COMPILE_DEFINITIONS(Thp2UsbManagementGuiPlugin PRIVATE L=QStringLiteral)
|
||||
NGL_TARGET_LINK_LIBRARIES(Thp2UsbManagementGuiPlugin
|
||||
Thp2Common
|
||||
Thp2CommonCore
|
||||
Tpm2GuiPluginInterface
|
||||
Qt5::Widgets
|
||||
Qt5::Network
|
||||
Qt5::Concurrent
|
||||
NGLLogger
|
||||
LtsWidgets
|
||||
LtsSession
|
||||
LtsDataView
|
||||
nglcompression
|
||||
nglsettings
|
||||
ngltools
|
||||
LtsSessionData
|
||||
|
||||
# for creating data.json
|
||||
#AsmDataPluginInterface
|
||||
#ExmDataPluginInterface
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(Thp2UsbManagementGuiPlugin)
|
||||
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
|
||||
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
LOGGER_T getLogger(const char *name)
|
||||
{
|
||||
static const char ns[] = "TPM2.USB.";
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
|
||||
|
||||
|
||||
#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());
|
||||
}
|
||||
}
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>USB2Dialog</class>
|
||||
<widget class="QWidget" name="USB2Dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1081</width>
|
||||
<height>541</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QGroupBox" name="verticalGroupBox">
|
||||
<property name="title">
|
||||
<string>Local</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>THPs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="tvLocal">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Topics</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="tvLocalTopics">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::MultiSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbCopy">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy selected THP + Topics to USB</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="verticalGroupBox_2">
|
||||
<property name="title">
|
||||
<string>On USB</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Path:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblPath">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"> - </string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbCHange">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Change path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbUnmount">
|
||||
<property name="text">
|
||||
<string>Unmount USB Device</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>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>THPs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="tvUSB">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbRemove">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove selected THPs from USB</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
+623
@@ -0,0 +1,623 @@
|
||||
|
||||
|
||||
|
||||
#include <ui_tpm_usb2_dialog.h>
|
||||
#include "tpm_usb_dialog.h"
|
||||
|
||||
#include <Common2/tpm_file_copy.h>
|
||||
#include <Common2/tpm_process_dialog.h>
|
||||
#include <Common2/tpm_exported_scripts_model.h>
|
||||
#include <Common2/tpm_topics_model.h>
|
||||
#include <Common2Core/tpm_js_parser.h>
|
||||
#include <nglcompression/nglcomp_compress.h>
|
||||
#include <nglsettings/nglsettings_connections.h>
|
||||
#include <nglsettings/nglsettings_settings.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 <ngltools/ngltools_filesystem.h>
|
||||
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QProcess>
|
||||
#include <QTemporaryFile>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
#include <QLocale>
|
||||
#include <QTimer>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QTreeView>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QVector>
|
||||
|
||||
#include <QtConcurrent>
|
||||
#include <QDirIterator>
|
||||
#include <QFileDialog>
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#define _compiler_warning(x) _Pragma(#x)
|
||||
#define compiler_warning(x) _compiler_warning(GCC warning x)
|
||||
#define TMP_WARNING_LOG(x) compiler_warning("Temporary warning log active!") \
|
||||
WARNING_LOG(x)
|
||||
#else
|
||||
#define compiler_warning(x)
|
||||
#define TMP_WARNING_LOG(x) WARNING_LOG(x)
|
||||
#endif
|
||||
|
||||
#ifndef ELOG
|
||||
#define ELOG ERROR_LOG(m_logger)
|
||||
#endif
|
||||
|
||||
|
||||
typedef std::runtime_error error;
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
USB2Dialog::USB2Dialog(LtsSession::LtsSession *session, QWidget *parent)
|
||||
: LtsWidgets::LtsDialog(parent)
|
||||
, m_localThpModel(new ExportedScriptsModel(this))
|
||||
, m_localTopicsModel(new TopicsModel(this))
|
||||
, m_usbThpModel(new ExportedScriptsModel(this))
|
||||
, m_ui(new Ui::USB2Dialog)
|
||||
, m_session(session)
|
||||
|
||||
|
||||
|
||||
, m_logger(getLogger("USB2Dialog"))
|
||||
{
|
||||
|
||||
m_ui->setupUi(this);
|
||||
|
||||
WARNING_LOG(m_logger)<<" USB2Dialog::USB2Dialog() START ";
|
||||
connect(m_ui->pbCHange, &QPushButton::clicked,
|
||||
this, &USB2Dialog::slotChangePath);
|
||||
|
||||
connect(m_ui->pbCopy, &QPushButton::clicked,
|
||||
this, &USB2Dialog::slotCopy);
|
||||
connect(m_ui->pbRemove, &QPushButton::clicked,
|
||||
this, &USB2Dialog::slotRemove);
|
||||
|
||||
|
||||
connect(m_ui->pbUnmount, &QPushButton::clicked, this, &USB2Dialog::slotUnmount);
|
||||
|
||||
reset();
|
||||
|
||||
#ifndef Q_OS_UNIX
|
||||
|
||||
m_ui->pbUnmount->hide();
|
||||
|
||||
#endif
|
||||
|
||||
m_ui->tvUSB->setModel(m_usbThpModel);
|
||||
#ifndef LTS_DEBUG
|
||||
m_ui->tvUSB->hideColumn(ExportedScriptsModel::ColId);
|
||||
#endif
|
||||
m_ui->tvUSB->hideColumn(ExportedScriptsModel::ColLocation);
|
||||
m_ui->tvUSB->hideColumn(ExportedScriptsModel::ColCreatedAtServer);
|
||||
m_ui->tvUSB->hideColumn(ExportedScriptsModel::ColUploadStatus);
|
||||
|
||||
|
||||
m_ui->tvLocal->setModel(m_localThpModel);
|
||||
#ifndef LTS_DEBUG
|
||||
m_ui->tvLocal->hideColumn(ExportedScriptsModel::ColId);
|
||||
#endif
|
||||
m_ui->tvLocal->hideColumn(ExportedScriptsModel::ColLocation);
|
||||
m_ui->tvLocal->hideColumn(ExportedScriptsModel::ColCreatedAtServer);
|
||||
m_ui->tvLocal->hideColumn(ExportedScriptsModel::ColUploadStatus);
|
||||
|
||||
m_ui->tvLocalTopics->setModel(m_localTopicsModel);
|
||||
|
||||
|
||||
connect(m_ui->tvLocal->selectionModel(), &QItemSelectionModel::selectionChanged,
|
||||
this, &USB2Dialog::slotSelectionChangedLocal);
|
||||
connect(m_ui->tvUSB->selectionModel(), &QItemSelectionModel::selectionChanged,
|
||||
this, &USB2Dialog::slotSelectionChangedUSB);
|
||||
|
||||
|
||||
try{
|
||||
initLocalModel();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
WARNING_LOG(m_logger)<<" data corrupted path: "<< localThpDir();
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Couldn't Load THP(s)"));
|
||||
}
|
||||
|
||||
setOnlineHelpFilename(QStringLiteral("Script_Editor-Export_Script_Administration"));
|
||||
|
||||
WARNING_LOG(m_logger)<<" USB2Dialog::USB2Dialog() END ";
|
||||
}
|
||||
|
||||
|
||||
USB2Dialog::~USB2Dialog()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
|
||||
void USB2Dialog::initUsbModel()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_usbThpModel->init(m_usbRoot);
|
||||
}
|
||||
catch (const error &e)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("The directory '%1' doesn't contain valid THP data.").arg(m_usbRoot));
|
||||
ELOG << "cannot initialize usb thp model from usb root: " << m_usbRoot << " - " << e.what();
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
|
||||
m_ui->lblPath->setText(m_usbRoot);
|
||||
m_ui->pbCopy->setEnabled(m_ui->tvLocal->selectionModel()->selectedRows().size() == 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void USB2Dialog::unmountUSB(const QString &device)
|
||||
{
|
||||
|
||||
QProcess process;
|
||||
|
||||
|
||||
|
||||
|
||||
process.start(QStringLiteral("bash"), QStringList() << QStringLiteral("-c") << QStringLiteral("lsblk -o NAME,MOUNTPOINT -r"));
|
||||
process.waitForFinished();
|
||||
process.setReadChannel(QProcess::StandardOutput);
|
||||
while (process.canReadLine())
|
||||
{
|
||||
QString outputLine = QString::fromUtf8(process.readLine());
|
||||
DEBUG_LOG(m_logger) << "OutputLine: " << outputLine;
|
||||
auto outputDevicePath = outputLine.split(QStringLiteral(" ")).last();
|
||||
DEBUG_LOG(m_logger) << "OutputDevicePath: " << outputDevicePath;
|
||||
|
||||
auto outputPathStripped = outputDevicePath.replace(QLatin1String("\\x20"), QLatin1String(" "));
|
||||
DEBUG_LOG(m_logger) << "OutputPathStripped: " << outputPathStripped;
|
||||
|
||||
auto outputDeviceName = outputPathStripped.split(QStringLiteral("/")).last();
|
||||
DEBUG_LOG(m_logger) << "OutputDeviceName: " << outputDeviceName;
|
||||
|
||||
if (outputDeviceName.trimmed().compare(device.trimmed()) == 0)
|
||||
{
|
||||
|
||||
|
||||
auto serialPort = outputLine.split(QStringLiteral(" ")).first();
|
||||
QString absPath = QStringLiteral("/dev/") + serialPort;
|
||||
DEBUG_LOG(m_logger) << "Serial Port: " << absPath;
|
||||
process.start(QStringLiteral("udisksctl"), QStringList() << QStringLiteral("unmount") << QStringLiteral("-b") << absPath);
|
||||
process.waitForFinished();
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Cant unmount USB device"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
template <typename Func1>
|
||||
void await(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, int timeoutMs = 0)
|
||||
{
|
||||
QEventLoop x;
|
||||
QObject::connect(sender, signal, &x, &QEventLoop::quit);
|
||||
|
||||
std::unique_ptr<QTimer> t(nullptr);
|
||||
bool timeout = false;
|
||||
if (timeoutMs > 0)
|
||||
{
|
||||
t = std::make_unique<QTimer>(new QTimer);
|
||||
t->setInterval(timeoutMs);
|
||||
t->setSingleShot(true);
|
||||
QObject::connect(t.get(), &QTimer::timeout, &x, [&timeout](){ timeout = true; });
|
||||
QObject::connect(t.get(), &QTimer::timeout, &x, &QEventLoop::quit);
|
||||
}
|
||||
|
||||
x.exec();
|
||||
|
||||
if (timeoutMs && t)
|
||||
{
|
||||
throw std::runtime_error("Timeout.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void USB2Dialog::reset()
|
||||
{
|
||||
m_ui->pbCopy->setEnabled(false);
|
||||
m_ui->pbRemove->setEnabled(false);
|
||||
m_usbRoot.clear();
|
||||
m_usbThpModel->clear();
|
||||
retranslate();
|
||||
}
|
||||
|
||||
|
||||
void USB2Dialog::slotChangePath()
|
||||
{
|
||||
QFileDialog *dlg = new QFileDialog(this);
|
||||
dlg->setFileMode(QFileDialog::Directory);
|
||||
dlg->setOption(QFileDialog::ShowDirsOnly);
|
||||
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dlg->open();
|
||||
|
||||
bool accepted = false;
|
||||
connect(dlg, &QFileDialog::finished, this, [&accepted](const int value){accepted = (value == 1);});
|
||||
await(dlg, &QFileDialog::finished);
|
||||
|
||||
if (!accepted)
|
||||
return;
|
||||
|
||||
const QStringList selected(dlg->selectedFiles());
|
||||
|
||||
if (selected.count() != 1)
|
||||
return;
|
||||
|
||||
reset();
|
||||
m_usbRoot = selected.first();
|
||||
|
||||
|
||||
|
||||
const QString scriptsJsonPath(scriptsJsLocation(m_usbRoot));
|
||||
qWarning() << "m_usbRoot" << m_usbRoot << "scriptsJsonPath" << scriptsJsonPath;
|
||||
if (QFile::exists(scriptsJsonPath))
|
||||
{
|
||||
qWarning() << "scripts.js exists!" << scriptsJsonPath;
|
||||
initUsbModel();
|
||||
return;
|
||||
}
|
||||
|
||||
qWarning() << "scripts.js doesn't exists";
|
||||
|
||||
auto res = LtsWidgets::LtsMessageDialog::showMessage(this, tr("The selected directory '%1' doesn't contain THPs yet. <br />Do you want to initialize the directory now?").arg(m_usbRoot),
|
||||
{}, LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::Cancel);
|
||||
if (res != LtsWidgets::Button::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!QDir().mkpath(scriptsLocation(m_usbRoot)))
|
||||
{
|
||||
throw error("mkpath for m_usbRoot/scripts failed");
|
||||
}
|
||||
|
||||
ScriptsJsParser(m_usbRoot, false).save();
|
||||
copyFiles(getFilesFromFolder(thpOverviewLocation(), m_usbRoot + QDir::separator()), this);
|
||||
}
|
||||
catch(const std::runtime_error &err)
|
||||
{
|
||||
ELOG << err.what();
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("The selected directory '%1' can't be written to correctly and the initialization was aborted.").arg(m_usbRoot));
|
||||
return;
|
||||
}
|
||||
|
||||
initUsbModel();
|
||||
}
|
||||
|
||||
|
||||
void USB2Dialog::slotUnmount()
|
||||
{
|
||||
|
||||
QString usbPath = QStringLiteral("/run/media/ctc_ws/");
|
||||
|
||||
|
||||
|
||||
|
||||
QDir usbDir(usbPath);
|
||||
usbDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
QStringList folderNames = usbDir.entryList();
|
||||
|
||||
for(const auto &folder : qAsConst(folderNames))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Folder: " << folder << " USB Root: " << m_usbRoot;
|
||||
|
||||
if(m_usbRoot.contains(folder))
|
||||
{
|
||||
unmountUSB(folder);
|
||||
return;
|
||||
}
|
||||
}
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Path is not inside of a USB device"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
void USB2Dialog::slotCopy()
|
||||
{
|
||||
if (m_usbRoot.isEmpty())
|
||||
return;
|
||||
|
||||
const auto selectedTopicIndices(m_ui->tvLocalTopics->selectionModel()->selectedRows());
|
||||
if (selectedTopicIndices.isEmpty())
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("To export a THP, please select at least one topic in the 'Topics' table."));
|
||||
return;
|
||||
}
|
||||
|
||||
const QModelIndexList mil(m_ui->tvLocal->selectionModel()->selectedRows());
|
||||
if (mil.size() != 1)
|
||||
return;
|
||||
|
||||
|
||||
const auto esi = m_localThpModel->getRowData(mil.first().row());
|
||||
const auto &scriptId = esi.id();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
removeScript(scriptId);
|
||||
|
||||
const QString srcPath(scriptLocation(localThpDir(), scriptId));
|
||||
const QString dstPath(scriptLocation(m_usbRoot, scriptId));
|
||||
CopyInfoList fileList;
|
||||
fileList += getFilesFromFolder(srcPath, dstPath, { L("topics") });
|
||||
|
||||
QSet<int> topicsToCopy;
|
||||
for (const auto &idx : selectedTopicIndices)
|
||||
{
|
||||
bool k = false;
|
||||
const int topicId = m_localTopicsModel->data(idx, Qt::UserRole).toInt(&k);
|
||||
if (!k)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
topicsToCopy += topicId;
|
||||
}
|
||||
qWarning() << "topicsToCopy" << topicsToCopy;
|
||||
|
||||
|
||||
|
||||
const QString topicsDestDirPath(topicsLocation(m_usbRoot, scriptId));
|
||||
const QJsonArray topicsArray(JsArrayParser::read(L("topicsJs"), topicsJsLocation(localThpDir(), scriptId) , true));
|
||||
const auto topics = Topic::topicsFromJson(topicsArray);
|
||||
|
||||
for (const auto &topic : topics)
|
||||
{
|
||||
qWarning() << "topic" << topic.id;
|
||||
|
||||
if (!topicsToCopy.contains(topic.id) || topic.filename.isEmpty() )
|
||||
{
|
||||
qWarning() << "SKIP FILE!";
|
||||
continue;
|
||||
}
|
||||
|
||||
const QFileInfo topicFile(topicLocation(localThpDir(), scriptId, topic.filename));
|
||||
fileList += { topicFile.absolutePath(), topicsDestDirPath, topicFile };
|
||||
qWarning() << "!!!!! copy topic" << topicFile.absoluteFilePath() << "to" << topicsDestDirPath;
|
||||
}
|
||||
|
||||
copyFiles(fileList, this);
|
||||
|
||||
|
||||
|
||||
QJsonArray finalTopicsArray;
|
||||
for (const auto &topicVal : topicsArray)
|
||||
{
|
||||
auto topic = Topic::topicFromJson(topicVal.toObject());
|
||||
|
||||
if (!topicsToCopy.contains(topic.id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
finalTopicsArray += topicVal;
|
||||
}
|
||||
|
||||
if (!QDir().mkpath(topicsLocation(m_usbRoot, scriptId)))
|
||||
{
|
||||
throw error("mkpath for directory 'topics' failed");
|
||||
}
|
||||
|
||||
JsArrayParser::write(L("topicsJs"), finalTopicsArray, topicsJsLocation(m_usbRoot, scriptId));
|
||||
|
||||
|
||||
|
||||
m_usbThpModel->addScript(esi.exercise.id, esi.exercise.name, esi.script.id, esi.script.name, esi.createdAt);
|
||||
}
|
||||
catch(const error &e)
|
||||
{
|
||||
ELOG << e.what();
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("The files could not be copied successfully!"));
|
||||
return;
|
||||
}
|
||||
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("The files were copied successfully"));
|
||||
}
|
||||
|
||||
|
||||
void USB2Dialog::removeScript(const QString &scriptId) noexcept(false)
|
||||
{
|
||||
if (scriptId.length() < 3)
|
||||
{
|
||||
ELOG << "scriptId is not in format 'n.n' - skipping for safety reasons - scriptId was: " << scriptId;
|
||||
throw error("scriptId.length() < 3");
|
||||
}
|
||||
|
||||
QDir dir(scriptLocation(m_usbRoot, 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 error("!dir.removeRecursively()");
|
||||
}
|
||||
|
||||
m_usbThpModel->removeLocalScript(scriptId);
|
||||
}
|
||||
|
||||
|
||||
void USB2Dialog::slotRemove()
|
||||
{
|
||||
|
||||
|
||||
if (m_usbRoot.isEmpty())
|
||||
return;
|
||||
|
||||
const QModelIndexList mil = m_ui->tvUSB->selectionModel()->selectedRows();
|
||||
if (mil.size() < 1)
|
||||
return;
|
||||
|
||||
DEBUG_LOG(m_logger) << "mil.size()" << mil.size();
|
||||
|
||||
QStringList scriptIdList;
|
||||
for (const QModelIndex &idx : mil)
|
||||
{
|
||||
const QString scriptId(idx.data(Qt::UserRole).toString());
|
||||
scriptIdList += scriptId;
|
||||
}
|
||||
|
||||
auto res = LtsWidgets::LtsMessageDialog::showMessage(this, tr("Do you really want to remove the following script(s)?<br>%1").arg(scriptIdList.join(L(", "))),
|
||||
{}, LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::Cancel);
|
||||
if (res != LtsWidgets::Button::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
for (const auto &scriptId : scriptIdList)
|
||||
{
|
||||
removeScript(scriptId);
|
||||
}
|
||||
}
|
||||
catch(const std::runtime_error &e)
|
||||
{
|
||||
ELOG << e.what();
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Failed to remove THP from from external medium."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void USB2Dialog::slotSelectionChangedUSB()
|
||||
{
|
||||
const QModelIndexList mil(m_ui->tvUSB->selectionModel()->selectedRows());
|
||||
bool enable = (mil.size() >= 1 && !m_usbRoot.isEmpty());
|
||||
m_ui->pbRemove->setEnabled(enable);
|
||||
}
|
||||
|
||||
|
||||
QByteArray readFile(const QString &filename) noexcept(false)
|
||||
{
|
||||
QFile f(filename);
|
||||
if (!f.open(QIODevice::ReadOnly))
|
||||
{
|
||||
throw error("Unable to read file at " + filename.toStdString() + ": " + f.errorString().toStdString());
|
||||
}
|
||||
|
||||
return f.readAll();
|
||||
}
|
||||
|
||||
|
||||
QJsonDocument jsonDocument(const QByteArray &data) noexcept(false)
|
||||
{
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc(QJsonDocument::fromJson(data, &err));
|
||||
|
||||
if (err.error != QJsonParseError::NoError)
|
||||
{
|
||||
throw error(L("json parse error: %1 at offset %2").arg(err.errorString()).arg(err.offset).toStdString());
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
|
||||
void USB2Dialog::slotSelectionChangedLocal()
|
||||
{
|
||||
m_ui->pbCopy->setEnabled(false);
|
||||
m_localTopicsModel->clear();
|
||||
const QModelIndexList mil(m_ui->tvLocal->selectionModel()->selectedRows());
|
||||
|
||||
if (mil.size() != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const QString scriptId(mil.first().data(Qt::UserRole).toString());
|
||||
|
||||
try
|
||||
{
|
||||
qWarning() << "############### scriptId" << scriptId;
|
||||
const QJsonArray topicsArray(JsArrayParser::read(L("topicsJs"), topicsJsLocation(localThpDir(), scriptId), true));
|
||||
const auto topics = Topic::topicsFromJson(topicsArray);
|
||||
|
||||
for (const auto &topic : topics)
|
||||
{
|
||||
|
||||
|
||||
|
||||
m_localTopicsModel->addTopic(topic);
|
||||
}
|
||||
}
|
||||
catch(const error &e)
|
||||
{
|
||||
ELOG << e.what();
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Unable to read topics for this THP. Please select another THP."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_ui->tvLocalTopics->selectAll();
|
||||
|
||||
m_ui->tvLocalTopics->resizeColumnToContents(TopicsModel::ColId);
|
||||
m_ui->tvLocalTopics->resizeColumnToContents(TopicsModel::ColChannel);
|
||||
m_ui->tvLocalTopics->resizeColumnToContents(TopicsModel::ColFilter);
|
||||
m_ui->tvLocalTopics->resizeColumnToContents(TopicsModel::ColDescription);
|
||||
m_ui->tvLocalTopics->resizeColumnToContents(TopicsModel::ColType);
|
||||
m_ui->tvLocalTopics->resizeColumnToContents(TopicsModel::ColStart);
|
||||
m_ui->tvLocalTopics->resizeColumnToContents(TopicsModel::ColDuration);
|
||||
|
||||
if (!m_usbRoot.isEmpty())
|
||||
{
|
||||
m_ui->pbCopy->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void USB2Dialog::retranslate()
|
||||
{
|
||||
translate(m_ui, tr("Export THP USB"));
|
||||
m_ui->lblPath->setText(m_usbRoot.isEmpty() ? tr("(none)") : m_usbRoot);
|
||||
}
|
||||
|
||||
|
||||
void USB2Dialog::initLocalModel() noexcept(false)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "(re)initModel !!";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
m_localThpModel->init(localThpDir(), true );
|
||||
|
||||
slotSelectionChangedLocal();
|
||||
}
|
||||
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <Common2/tpm_file_copy.h>
|
||||
#include <LtsWidgets/lts_dialog.h>
|
||||
|
||||
#include <QFileInfoList>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class USB2Dialog;
|
||||
}
|
||||
namespace LtsSession
|
||||
{
|
||||
class LtsSession;
|
||||
}
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
class ExportedScriptsModel;
|
||||
class TopicsModel;
|
||||
|
||||
class USB2Dialog : public LtsWidgets::LtsDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
|
||||
explicit USB2Dialog(LtsSession::LtsSession *session, QWidget *parent = nullptr);
|
||||
~USB2Dialog();
|
||||
|
||||
void retranslate() override;
|
||||
|
||||
static QString getContentFolder(const QString &rootFolder, int exerciseId);
|
||||
QString getContentFolder(const QString &rootFolder) const;
|
||||
|
||||
static QString exerciseDirectoryName(int exerciseId);
|
||||
static QString getScriptFolder(const QString &rootFolder, int exerciseId, int scriptId);
|
||||
|
||||
signals:
|
||||
void nextMap();
|
||||
|
||||
private slots:
|
||||
void slotSelectionChangedLocal();
|
||||
void slotSelectionChangedUSB();
|
||||
void slotCopy();
|
||||
void slotRemove();
|
||||
void slotChangePath();
|
||||
void slotUnmount();
|
||||
|
||||
private:
|
||||
void removeScript(const QString &scriptId);
|
||||
void reset();
|
||||
void initLocalModel() noexcept(false);
|
||||
void initUsbModel();
|
||||
bool checkIsInUSB(const QString &path);
|
||||
void unmountUSB(const QString &device);
|
||||
ExportedScriptsModel *m_localThpModel;
|
||||
TopicsModel *m_localTopicsModel;
|
||||
ExportedScriptsModel *m_usbThpModel;
|
||||
Ui::USB2Dialog *m_ui;
|
||||
LtsSession::LtsSession *m_session;
|
||||
|
||||
|
||||
QMap<QString, QFileInfoList> m_files;
|
||||
|
||||
QString m_usbRoot;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
|
||||
|
||||
|
||||
#include "tpm_usb_gui_plugin.h"
|
||||
#include "tpm_usb_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("Plugin"))
|
||||
{}
|
||||
|
||||
|
||||
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("THP2.USB.Dialog");
|
||||
plData.menuIcon = QIcon(QLatin1String("icon:export_documents.svg"));
|
||||
plData.menuText = tr("Export THP USB");
|
||||
|
||||
plData.createWidgetFunc = [this](QWidget *)
|
||||
{
|
||||
return LtsWidgets::createThrowableDialog<USB2Dialog>(m_session);
|
||||
};
|
||||
ret.append(plData);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
|
||||
|
||||
|
||||
#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.usb")
|
||||
Q_INTERFACES(GuiPluginBase::Plugin)
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
GuiPlugin() = default;
|
||||
~GuiPlugin() = default;
|
||||
|
||||
QString pluginName() override { return QLatin1String("Thp2UsbManager"); }
|
||||
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:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <QString>
|
||||
|
||||
|
||||
namespace TakeHomePackageManager
|
||||
{
|
||||
|
||||
class ForeignTpm2GuiInterface
|
||||
{
|
||||
public:
|
||||
ForeignTpm2GuiInterface() = default;
|
||||
virtual ~ForeignTpm2GuiInterface() = default;
|
||||
static QString pluginName() { return QStringLiteral("Tpm2GuiPlugin"); }
|
||||
};
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user