diff --git a/translations/updaterplugin_de.ts b/translations/updaterplugin_de.ts new file mode 100644 index 0000000..0fd56e0 --- /dev/null +++ b/translations/updaterplugin_de.ts @@ -0,0 +1,28 @@ + + + + + UpdaterDialog + + + + New update available! + Neues Update verfügbar! + + + + There is a new release available to download! + Ein neues Release steht zum Download bereit! + + + + Dont show today anymore + Heute nicht mehr anzeigen + + + + Could not open default webbrowser! + Konnte Standard-Browser nicht öffnen! + + + diff --git a/translations/updaterplugin_en.ts b/translations/updaterplugin_en.ts new file mode 100644 index 0000000..fd1099e --- /dev/null +++ b/translations/updaterplugin_en.ts @@ -0,0 +1,28 @@ + + + + + UpdaterDialog + + + + New update available! + + + + + There is a new release available to download! + + + + + Dont show today anymore + + + + + Could not open default webbrowser! + + + + diff --git a/updaterdialog.cpp b/updaterdialog.cpp new file mode 100644 index 0000000..0e3c6f4 --- /dev/null +++ b/updaterdialog.cpp @@ -0,0 +1,108 @@ +#include "updaterdialog.h" +#include "ui_updaterdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mainwindow.h" +#include "zeiterfassungsettings.h" +#include "zeiterfassungapi.h" + +#include "updatersettings.h" + +UpdaterDialog::UpdaterDialog(MainWindow &mainWindow) : + ZeiterfassungDialog(&mainWindow), + ui(new Ui::UpdaterDialog), + m_mainWindow(mainWindow) +{ + ui->setupUi(this); + + setAttribute(Qt::WA_DeleteOnClose); + + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &UpdaterDialog::acceptedSlot); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &UpdaterDialog::rejectedSlot); + + m_reply = m_mainWindow.erfassung().manager()->get(QNetworkRequest(UpdaterSettings(mainWindow.settings()).url())); + connect(m_reply, &QNetworkReply::finished, this, &UpdaterDialog::finished); +} + +UpdaterDialog::~UpdaterDialog() +{ + delete ui; +} + +void UpdaterDialog::acceptedSlot() +{ + if(ui->checkBoxDontShow->isChecked()) + UpdaterSettings(m_mainWindow.settings()).setLastUpdateCheck(QDate::currentDate()); + + if(!QDesktopServices::openUrl(m_url)) + QMessageBox::warning(this, tr("Could not open default webbrowser!"), tr("Could not open default webbrowser!")); + + accept(); +} + +void UpdaterDialog::rejectedSlot() +{ + if(ui->checkBoxDontShow->isChecked()) + UpdaterSettings(m_mainWindow.settings()).setLastUpdateCheck(QDate::currentDate()); + + reject(); +} + +void UpdaterDialog::finished() +{ + if(m_reply->error() != QNetworkReply::NoError) + { + qWarning() << "request error" << m_reply->error() << m_reply->errorString(); + return; + } + + QJsonParseError error; + auto document = QJsonDocument::fromJson(m_reply->readAll(), &error); + + if(error.error != QJsonParseError::NoError) + { + qWarning() << "parse error" << error.error << error.errorString(); + return; + } + + if(!document.isArray()) + { + qWarning() << "document is not an array!"; + return; + } + + auto appVersion = QVersionNumber::fromString(QCoreApplication::applicationVersion()); + + auto array = document.array(); + + for(const auto &releaseVal : array) + { + auto releaseObj = releaseVal.toObject(); + auto version = QVersionNumber::fromString(releaseObj.value(QStringLiteral("tag_name")).toString()); + + if(appVersion < version) + { + m_url = QUrl(releaseObj.value(QStringLiteral("html_url")).toString()); + ui->labelDescription->setText(releaseObj.value(QStringLiteral("body")).toString()); + + show(); + return; + } + } + + UpdaterSettings(m_mainWindow.settings()).setLastUpdateCheck(QDate::currentDate()); + + deleteLater(); +} diff --git a/updaterdialog.h b/updaterdialog.h new file mode 100644 index 0000000..2ea7560 --- /dev/null +++ b/updaterdialog.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include "zeiterfassungdialog.h" + +class QNetworkReply; + +namespace Ui { class UpdaterDialog; } +class MainWindow; + +class UpdaterDialog : public ZeiterfassungDialog +{ + Q_OBJECT + +public: + explicit UpdaterDialog(MainWindow &parent); + ~UpdaterDialog(); + +private Q_SLOTS: + void acceptedSlot(); + void rejectedSlot(); + void finished(); + +private: + Ui::UpdaterDialog *ui; + MainWindow &m_mainWindow; + QNetworkReply *m_reply; + + QUrl m_url; +}; diff --git a/updaterdialog.ui b/updaterdialog.ui new file mode 100644 index 0000000..8d8f8d3 --- /dev/null +++ b/updaterdialog.ui @@ -0,0 +1,90 @@ + + + UpdaterDialog + + + + 0 + 0 + 447 + 280 + + + + New update available! + + + + + + + 20 + + + + New update available! + + + + + + + There is a new release available to download! + + + + + + + QFrame::WinPanel + + + QFrame::Sunken + + + TextLabel + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Dont show today anymore + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + diff --git a/updaterplugin.cpp b/updaterplugin.cpp new file mode 100644 index 0000000..5654138 --- /dev/null +++ b/updaterplugin.cpp @@ -0,0 +1,46 @@ +#include "updaterplugin.h" + +#include +#include +#include +#include + +#include "mainwindow.h" +#include "zeiterfassungsettings.h" +#include "zeiterfassungapi.h" + +#include "updatersettings.h" +#include "updaterdialog.h" +#include "updatersettingswidget.h" + +UpdaterPlugin::UpdaterPlugin(QObject *parent) : + ZeiterfassungPlugin(parent) +{ + qDebug() << "called"; + + static auto dir = QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(QStringLiteral("translations")); + + if(m_translator.load(QLocale(), QStringLiteral("updaterplugin"), QStringLiteral("_"), dir)) + { + if(!QCoreApplication::installTranslator(&m_translator)) + { + qWarning() << "could not install translation updaterplugin"; + } + } + else + { + qWarning() << "could not load translation updaterplugin"; + } +} + +void UpdaterPlugin::attachTo(MainWindow &mainWindow) +{ + auto lastUpdateCheck = UpdaterSettings(mainWindow.settings()).lastUpdateCheck(); + if(lastUpdateCheck.isNull() || lastUpdateCheck < QDate::currentDate()) + new UpdaterDialog(mainWindow); +} + +SettingsWidget *UpdaterPlugin::settingsWidget(ZeiterfassungSettings &settings, QWidget *parent) const +{ + return new UpdaterSettingsWidget(settings, parent); +} diff --git a/updaterplugin.h b/updaterplugin.h new file mode 100644 index 0000000..e98d448 --- /dev/null +++ b/updaterplugin.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include "zeiterfassungplugin.h" + +class MainWindow; + +class Q_DECL_EXPORT UpdaterPlugin : public ZeiterfassungPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "dbsoftware.zeiterfassung.plugin/1.0" FILE "updaterplugin.json") + Q_INTERFACES(ZeiterfassungPlugin) + +public: + explicit UpdaterPlugin(QObject *parent = Q_NULLPTR); + + // ZeiterfassungPlugin interface + void attachTo(MainWindow &mainWindow) Q_DECL_OVERRIDE; + + virtual SettingsWidget *settingsWidget(ZeiterfassungSettings &settings, QWidget *parent = Q_NULLPTR) const Q_DECL_OVERRIDE; + +private: + QTranslator m_translator; +}; diff --git a/updaterplugin.json b/updaterplugin.json new file mode 100644 index 0000000..e69de29 diff --git a/updaterplugin.pro b/updaterplugin.pro new file mode 100644 index 0000000..ed617ce --- /dev/null +++ b/updaterplugin.pro @@ -0,0 +1,26 @@ +QT += core network gui widgets + +DBLIBS += zeiterfassungcore zeiterfassunggui + +TARGET = updaterplugin + +HEADERS += updaterdialog.h \ + updaterplugin.h \ + updatersettings.h \ + updatersettingswidget.h + +SOURCES += updaterdialog.cpp \ + updaterplugin.cpp \ + updatersettings.cpp \ + updatersettingswidget.cpp + +FORMS += updaterdialog.ui + +RESOURCES += + +TRANSLATIONS += translations/updaterplugin_en.ts \ + translations/updaterplugin_de.ts + +OTHER_FILES += updaterplugin.json + +include(../plugin.pri) diff --git a/updatersettings.cpp b/updatersettings.cpp new file mode 100644 index 0000000..9d439e0 --- /dev/null +++ b/updatersettings.cpp @@ -0,0 +1,72 @@ +#include "updatersettings.h" + +#include "zeiterfassungsettings.h" + +const QString UpdaterSettings::m_url("UpdaterPlugin/url"); +const QString UpdaterSettings::m_lastUpdateCheck("UpdaterPlugin/lastUpdateCheck"); +const QUrl UpdaterSettings::m_defaultUrl(QStringLiteral("https://api.github.com/repos/0xFEEDC0DE64/QtZeiterfassung/releases")); + +UpdaterSettings::UpdaterSettings(ZeiterfassungSettings &settings, QObject *parent) : + QObject(parent), + m_settings(settings) +{ + +} + +QUrl UpdaterSettings::url() const +{ + return m_settings.value(m_url, m_defaultUrl).toUrl(); +} + +bool UpdaterSettings::setUrl(const QUrl &url) +{ + if(this->url() == url) + return true; + + if(url == m_defaultUrl) + m_settings.remove(m_url); + else + m_settings.setValue(m_url, url); + + m_settings.sync(); + + const auto success = m_settings.status() == QSettings::NoError; + if(success) + Q_EMIT urlChanged(url); + else + { + Q_EMIT m_settings.saveErrorOccured(); + Q_EMIT saveErrorOccured(); + } + + return success; +} + +QDate UpdaterSettings::lastUpdateCheck() const +{ + return m_settings.value(m_lastUpdateCheck).toDate(); +} + +bool UpdaterSettings::setLastUpdateCheck(const QDate &lastUpdateCheck) +{ + if(this->lastUpdateCheck() == lastUpdateCheck) + return true; + + if(!lastUpdateCheck.isValid()) + m_settings.remove(m_lastUpdateCheck); + else + m_settings.setValue(m_lastUpdateCheck, lastUpdateCheck); + + m_settings.sync(); + + const auto success = m_settings.status() == QSettings::NoError; + if(success) + Q_EMIT lastUpdateCheckChanged(lastUpdateCheck); + else + { + Q_EMIT m_settings.saveErrorOccured(); + Q_EMIT saveErrorOccured(); + } + + return success; +} diff --git a/updatersettings.h b/updatersettings.h new file mode 100644 index 0000000..f6998eb --- /dev/null +++ b/updatersettings.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +class ZeiterfassungSettings; + +class UpdaterSettings : public QObject +{ + Q_OBJECT + Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged) + Q_PROPERTY(QDate lastUpdateCheck READ lastUpdateCheck WRITE setLastUpdateCheck NOTIFY lastUpdateCheckChanged) + +public: + explicit UpdaterSettings(ZeiterfassungSettings &settings, QObject *parent = Q_NULLPTR); + + QUrl url() const; + bool setUrl(const QUrl &url); + + QDate lastUpdateCheck() const; + bool setLastUpdateCheck(const QDate &lastUpdateCheck); + +Q_SIGNALS: + void saveErrorOccured(); + + void urlChanged(const QUrl &url); + void lastUpdateCheckChanged(const QDate &lastUpdateCheck); + +private: + ZeiterfassungSettings &m_settings; + + static const QString m_url; + static const QString m_lastUpdateCheck; + static const QUrl m_defaultUrl; +}; diff --git a/updatersettingswidget.cpp b/updatersettingswidget.cpp new file mode 100644 index 0000000..883bcc1 --- /dev/null +++ b/updatersettingswidget.cpp @@ -0,0 +1,32 @@ +#include "updatersettingswidget.h" + +#include +#include + +UpdaterSettingsWidget::UpdaterSettingsWidget(ZeiterfassungSettings &settings, QWidget *parent) : + SettingsWidget(parent), + m_settings(settings) +{ + auto layout = new QFormLayout(this); + layout->setMargin(0); + + m_lineEdit = new QLineEdit(m_settings.url().toString(), this); + layout->addRow(tr("Updater url:"), m_lineEdit); + + setLayout(layout); +} + +bool UpdaterSettingsWidget::isValid(QString &message) const +{ + auto valid = QUrl::fromUserInput(m_lineEdit->text()).isValid(); + + if(!valid) + message = tr("The updater url is invalid!"); + + return valid; +} + +bool UpdaterSettingsWidget::apply() +{ + return m_settings.setUrl(QUrl(m_lineEdit->text())); +} diff --git a/updatersettingswidget.h b/updatersettingswidget.h new file mode 100644 index 0000000..a3568d2 --- /dev/null +++ b/updatersettingswidget.h @@ -0,0 +1,25 @@ +#pragma once + +#include "settingswidget.h" + +#include "updatersettings.h" + +class QLineEdit; + +class UpdaterSettingsWidget : public SettingsWidget +{ + Q_OBJECT + +public: + explicit UpdaterSettingsWidget(ZeiterfassungSettings &settings, QWidget *parent = Q_NULLPTR); + + virtual bool isValid(QString &message) const Q_DECL_OVERRIDE; + +public Q_SLOTS: + virtual bool apply() Q_DECL_OVERRIDE; + +private: + UpdaterSettings m_settings; + + QLineEdit *m_lineEdit; +};