#include "gui.h" #include "progress.h" #include #include #include #include #include #include #include #include #include #include static QString STATE_COPYING("COPYING"); // Still busy copying original file, new chunks are only accepted in already copied range static QString STATE_WAITING_FOR_UPLOAD_DONE("WAITING_FOR_UPLOAD_DONE"); // Copying done, just waiting for new chunks until client signals that it's done static QString STATE_WAITING_FOR_COPYING_DONE("WAITING_FOR_COPYING_DONE"); // Copying not done, but dnbd3-fuse already told us it's done, wait for copying static QString STATE_UPLOAD_DONE("UPLOAD_DONE"); // dnbd3-fuse signaled merge request, upload is thus done and no new chunks are accepted static QString STATE_PROCESSING("PROCESSING"); // Hashing, renaming, creating DB entry static QString STATE_ERROR("ERROR"); static QString STATE_COMPLETELY_DONE("COMPLETELY_DONE"); Gui::Gui(const char *urlbase, const char *uuid, QWidget *parent) : QDialog(parent) , _nam(new QNetworkAccessManager(this)) , _urlStatus(QString(urlbase).append("status/").append(uuid)) , _urlAbort(QString(urlbase).append("abort/").append(uuid)) , _urlFinish(QString(urlbase).append("finish/").append(uuid)) , _denyInteraction(false) , _tmrStatus(new QTimer(this)) , _status(nullptr) { _nam->setAutoDeleteReplies(true); _nam->setTransferTimeout(5000); setupUi(); auto flags = windowFlags(); setWindowFlags(flags & ~(Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint)); _tmrStatus->start(2500); QObject::connect(_tmrStatus, &QTimer::timeout, [this]() { QNetworkReply *reply = _nam->get(QNetworkRequest(_urlStatus)); QObject::connect(reply, &QNetworkReply::finished, [reply, this]() { // JSON auto repData = reply->readAll(); auto doc = QJsonDocument::fromJson(repData); if (doc.isEmpty() || !doc.isObject()) { QString str = QString::fromUtf8(repData); int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (code == 404) { _remoteState = STATE_ERROR; _status->setText(tr("Sitzung dem Server nicht bekannt")); updateButtons(); } else if (str.length() > 0 && str.length() < 160) { _status->setText(QLatin1String("Server: ") + str); } else { _status->setText(tr("Fehler beim Statusabruf")); } return; } // Got JSON auto outer = doc.object(); QJsonValue state = outer[QLatin1String("state")]; if (state.isString()) { auto ns = state.toString(); if (ns != _remoteState) { _status->setText(ns); _remoteState = ns; updateButtons(); } } QJsonValue tasks = outer[QLatin1String("tasks")]; if (tasks.isArray()) { auto arr = tasks.toArray(); for (auto it : arr) { if (!it.isObject()) continue; auto o = it.toObject(); auto jt = o.value(QLatin1String("title")); if (!jt.isString()) continue; auto jp = o.value(QLatin1String("percent")); auto je = o.value(QLatin1String("error")); QString title = jt.toString(); QString err = je.isString() ? je.toString() : QLatin1String(""); int percent = err.isEmpty() && jp.isDouble() ? (int)jp.toDouble() : -1; Progress *item = _items.value(title); if (item == nullptr) { if (_items.size() > 10) continue; item = new Progress(title, this); _itemBox->addWidget(item); _items.insert(title, item); } item->setProgress(percent); item->setCaption(title + QLatin1String(" ") + err); } } }); }); } Gui::~Gui() { _nam->blockSignals(true); } void Gui::setupUi() { QVBoxLayout *outerVert = new QVBoxLayout(this); outerVert->setMargin(3); _itemBox = new QVBoxLayout; _itemBox->setObjectName("ItemLayout"); outerVert->addLayout(_itemBox); QLayout *buttons = new QHBoxLayout; buttons->setObjectName("ButtonLayout"); outerVert->addLayout(buttons); _status = new QLabel("-", this); _itemBox->addWidget(_status); _btnAbort = new QPushButton(tr("Abbrechen/Löschen"), this); buttons->addWidget(_btnAbort); QObject::connect(_btnAbort, &QPushButton::clicked, this, &Gui::pushedCancel); _btnOk = new QPushButton(tr("Änderungen permanent behalten"), this); buttons->addWidget(_btnOk); QObject::connect(_btnOk, &QPushButton::clicked, this, &Gui::pushedOk); } void Gui::updateButtons() { if (_remoteState == STATE_COMPLETELY_DONE || _remoteState == STATE_PROCESSING) { if (_remoteState == STATE_COMPLETELY_DONE) { _tmrStatus->stop(); } _btnAbort->setEnabled(false); _btnOk->setEnabled(true); _btnOk->setText(tr("Schließen")); } else if (_remoteState == STATE_UPLOAD_DONE) { _btnAbort->setEnabled(true); _btnOk->setEnabled(true); } else if (_remoteState == STATE_ERROR) { _btnAbort->setEnabled(true); _btnOk->setEnabled(false); _tmrStatus->stop(); _btnAbort->setText(tr("Schließen")); } else { _btnAbort->setEnabled(true); _btnOk->setEnabled(false); } } void Gui::pushedCancel(bool) { if (_remoteState == STATE_ERROR) { this->close(); return; } if (_denyInteraction) return; if (_remoteState != STATE_ERROR) { int ret = QMessageBox::question(this, tr("Bestätigung"), tr("Wollen Sie diesen Bearbeitungsvorgang wirklich abbrechen?")); if (ret == QMessageBox::No) return; } if (_remoteState == STATE_COMPLETELY_DONE) { _btnAbort->setEnabled(false); return; } _denyInteraction = true; QNetworkReply *reply = _nam->post(QNetworkRequest(_urlAbort), QByteArray()); connect(reply, &QNetworkReply::finished, [reply, this]() { QString msg = QString::fromUtf8(reply->readAll()); if (reply->error() != QNetworkReply::NoError) { QMessageBox::critical(this, tr("Fehler"), msg); _denyInteraction = false; } else { _btnAbort->setEnabled(false); _btnOk->setEnabled(false); _remoteState = STATE_ERROR; QMessageBox::information(this, tr("Ergebnis"), msg); QTimer::singleShot(2000, [this]() { this->close(); }); } }); } void Gui::pushedOk(bool) { if (_remoteState == STATE_COMPLETELY_DONE || _remoteState == STATE_PROCESSING) { this->close(); return; } if (_denyInteraction) return; if (_remoteState == STATE_UPLOAD_DONE) { int ret = QMessageBox::question(this, tr("Bestätigung"), tr("Die Änderungen an der VM werden dauerhaft beibehalten. Fortfahren?")); if (ret == QMessageBox::No) return; } _denyInteraction = true; QNetworkReply *reply = _nam->get(QNetworkRequest(_urlFinish)); connect(reply, &QNetworkReply::finished, [reply, this]() { QString msg = QString::fromUtf8(reply->readAll()); int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (reply->error() != QNetworkReply::NoError || (code < 200 || code >= 300)) { QMessageBox::critical(this, tr("Fehler"), msg); _denyInteraction = false; } else { _remoteState = STATE_PROCESSING; _status->setText(tr("Finalisiere...")); updateButtons(); } }); }