logic: Added update checker connecting to online API

* connects to https://www.sourcetrail.com/api/v1/versions/latest
* sends information about OS, platform, license type, current version and a unique user token
* Automatic update check is enabled via Startscreen and performed once every 24 hours on window focus
* Update check can be forced by clicking on "check for new version" on start screen
* renamed TimePoint to TimeStamp

fortune cookie message = In Träumen und im Leben ist nichts unmöglich.
This commit is contained in:
Eberhard Graether
2017-08-08 14:18:14 +02:00
parent db1c3b3f40
commit ff65a3f285
67 changed files with 680 additions and 251 deletions
+5 -4
View File
@@ -242,9 +242,10 @@ std::string QtBookmark::getDateString() const
{
std::string result = "n/a";
TimePoint creationDate = m_bookmark->getTimeStamp();
TimeStamp creationDate = m_bookmark->getTimeStamp();
TimeStamp now = TimeStamp::now();
float delta = TimePoint::now() - creationDate;
size_t delta = now.deltaS(creationDate);
if (delta < 3600.0f) // less than an hour ago
{
@@ -254,11 +255,11 @@ std::string QtBookmark::getDateString() const
{
result = std::to_string(int(delta / 3600.0f)) + " hours ago";
}
else if (creationDate.isSameDay(TimePoint::now())) // today
else if (creationDate.isSameDay(now)) // today
{
result = "today";
}
else if (creationDate.deltaDays(TimePoint::now()) == 1) // yesterday
else if (creationDate.deltaDays(now) == 1) // yesterday
{
result = "yesterday";
}
+1 -1
View File
@@ -114,7 +114,7 @@ QtCodeFile::~QtCodeFile()
{
}
void QtCodeFile::setModificationTime(const TimePoint modificationTime)
void QtCodeFile::setModificationTime(const TimeStamp modificationTime)
{
m_title->setModificationTime(modificationTime);
}
+2 -2
View File
@@ -18,7 +18,7 @@ class QtCodeFileTitleButton;
class QtCodeNavigator;
class QtCodeSnippet;
class QVBoxLayout;
class TimePoint;
class TimeStamp;
class QtCodeFile
: public QFrame
@@ -29,7 +29,7 @@ public:
QtCodeFile(const FilePath& filePath, QtCodeNavigator* navigator);
virtual ~QtCodeFile();
void setModificationTime(const TimePoint modificationTime);
void setModificationTime(const TimeStamp modificationTime);
const FilePath& getFilePath() const;
std::string getFileName() const;
+1 -1
View File
@@ -70,7 +70,7 @@ QtCodeFile* QtCodeFileList::getFile(const FilePath filePath)
return file;
}
void QtCodeFileList::addFile(const FilePath& filePath, bool isWholeFile, int refCount, TimePoint modificationTime, bool isComplete)
void QtCodeFileList::addFile(const FilePath& filePath, bool isWholeFile, int refCount, TimeStamp modificationTime, bool isComplete)
{
QtCodeFile* file = getFile(filePath);
file->setWholeFile(isWholeFile, refCount);
+1 -1
View File
@@ -28,7 +28,7 @@ public:
void clear();
QtCodeFile* getFile(const FilePath filePath);
void addFile(const FilePath& filePath, bool isWholeFile, int refCount, TimePoint modificationTime, bool isComplete);
void addFile(const FilePath& filePath, bool isWholeFile, int refCount, TimeStamp modificationTime, bool isComplete);
// QtCodeNaviatebale implementation
virtual QScrollArea* getScrollArea();
+2 -2
View File
@@ -7,7 +7,7 @@
#include <QFrame>
#include "utility/file/FilePath.h"
#include "utility/TimePoint.h"
#include "utility/TimeStamp.h"
#include "qt/element/QtCodeNavigateable.h"
@@ -52,7 +52,7 @@ private:
struct FileData
{
FilePath filePath;
TimePoint modificationTime;
TimeStamp modificationTime;
bool isComplete;
std::string title;
@@ -47,7 +47,7 @@ void QtCodeFileTitleButton::setFilePath(const FilePath& filePath)
));
}
void QtCodeFileTitleButton::setModificationTime(const TimePoint modificationTime)
void QtCodeFileTitleButton::setModificationTime(const TimeStamp modificationTime)
{
if (modificationTime.isValid())
{
@@ -4,7 +4,7 @@
#include <QPushButton>
#include "utility/file/FilePath.h"
#include "utility/TimePoint.h"
#include "utility/TimeStamp.h"
class QtCodeFileTitleButton
: public QPushButton
@@ -16,7 +16,7 @@ public:
virtual ~QtCodeFileTitleButton();
void setFilePath(const FilePath& filePath);
void setModificationTime(const TimePoint modificationTime);
void setModificationTime(const TimeStamp modificationTime);
void setIsComplete(bool isComplete);
void setProject(const std::string& name);
@@ -30,7 +30,7 @@ private slots:
private:
FilePath m_filePath;
TimePoint m_modificationTime;
TimeStamp m_modificationTime;
bool m_isComplete;
};
+1 -1
View File
@@ -155,7 +155,7 @@ void QtCodeNavigator::addCodeSnippet(const CodeSnippetParams& params)
}
}
void QtCodeNavigator::addFile(std::shared_ptr<SourceLocationFile> locationFile, int refCount, TimePoint modificationTime)
void QtCodeNavigator::addFile(std::shared_ptr<SourceLocationFile> locationFile, int refCount, TimeStamp modificationTime)
{
bool firstFile = m_references.size() == 0;
+1 -1
View File
@@ -35,7 +35,7 @@ public:
virtual ~QtCodeNavigator();
void addCodeSnippet(const CodeSnippetParams& params);
void addFile(std::shared_ptr<SourceLocationFile> locationFile, int refCount, TimePoint modificationTime);
void addFile(std::shared_ptr<SourceLocationFile> locationFile, int refCount, TimeStamp modificationTime);
void addedFiles();
+4 -4
View File
@@ -56,7 +56,7 @@ void QtProgressBar::paintEvent(QPaintEvent* event)
void QtProgressBar::start()
{
m_timePoint = TimePoint::now();
m_TimeStamp = TimeStamp::now();
m_timer->start(25);
}
@@ -68,15 +68,15 @@ void QtProgressBar::stop()
void QtProgressBar::animate()
{
TimePoint t = TimePoint::now();
size_t dt = t.deltaMS(m_timePoint);
TimeStamp t = TimeStamp::now();
size_t dt = t.deltaMS(m_TimeStamp);
if (dt < 5)
{
return;
}
m_timePoint = t;
m_TimeStamp = t;
m_count++;
update();
+2 -2
View File
@@ -4,7 +4,7 @@
#include <QWidget>
#include "qt/utility/QtDeviceScaledPixmap.h"
#include "utility/TimePoint.h"
#include "utility/TimeStamp.h"
class QTimer;
@@ -34,7 +34,7 @@ private:
size_t m_count;
QTimer* m_timer;
TimePoint m_timePoint;
TimeStamp m_TimeStamp;
QtDeviceScaledPixmap m_pixmap;
};
+8 -2
View File
@@ -1,6 +1,7 @@
#include "QtNetworkFactory.h"
#include "QtIDECommunicationController.h"
#include "QtUpdateChecker.h"
QtNetworkFactory::QtNetworkFactory()
{
@@ -11,6 +12,11 @@ QtNetworkFactory::~QtNetworkFactory()
}
std::shared_ptr<IDECommunicationController> QtNetworkFactory::createIDECommunicationController(StorageAccess* storageAccess) const
{
{
return std::make_shared<QtIDECommunicationController>(nullptr, storageAccess);
}
}
std::shared_ptr<UpdateChecker> QtNetworkFactory::createUpdateChecker() const
{
return std::make_shared<QtUpdateChecker>();
}
+7 -4
View File
@@ -1,15 +1,18 @@
#ifndef QT_NETWORK_FACTORY_H
#define QT_NETWORK_FACTORY_H
#include "component/controller/NetworkFactory.h"
#include "component/NetworkFactory.h"
class QtNetworkFactory : public NetworkFactory
class QtNetworkFactory
: public NetworkFactory
{
public:
QtNetworkFactory();
virtual ~QtNetworkFactory();
virtual std::shared_ptr<IDECommunicationController> createIDECommunicationController(StorageAccess* storageAccess) const;
virtual std::shared_ptr<IDECommunicationController>
createIDECommunicationController(StorageAccess* storageAccess) const override;
virtual std::shared_ptr<UpdateChecker> createUpdateChecker() const override;
};
#endif // QT_NETWORK_FACTORY_H
#endif // QT_NETWORK_FACTORY_H
+40
View File
@@ -0,0 +1,40 @@
#include "qt/network/QtRequest.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include "utility/logging/logging.h"
QtRequest::QtRequest()
{
m_networkManager = new QNetworkAccessManager(this);
QObject::connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(finished(QNetworkReply*)));
}
void QtRequest::sendRequest(QString url)
{
LOG_INFO_STREAM(<< "send HTTP request: " << url.toStdString());
QNetworkRequest request;
request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
request.setUrl(QUrl(url));
m_networkManager->get(request);
}
void QtRequest::finished(QNetworkReply *reply)
{
QVariant statusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
QVariant redirectionTargetUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (reply->error() != QNetworkReply::NoError)
{
LOG_ERROR_STREAM(<< "An error occured during http request. ERRORCODE: " << reply->error());
}
QByteArray bytes = reply->readAll();
LOG_INFO_STREAM(<< "received HTTP reply: " << bytes.toStdString());
delete reply;
emit receivedData(bytes);
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef QT_REQUEST_H
#define QT_REQUEST_H
#include <QByteArray>
#include <QObject>
class QNetworkAccessManager;
class QNetworkReply;
class QtRequest
: public QObject
{
Q_OBJECT
public:
QtRequest();
void sendRequest(QString url);
signals:
void receivedData(QByteArray bytes);
private slots:
void finished(QNetworkReply* reply);
private:
QNetworkAccessManager* m_networkManager;
};
#endif // QT_REQUEST_H
+148
View File
@@ -0,0 +1,148 @@
#include "qt/network/QtUpdateChecker.h"
#include <QDesktopServices>
#include <QMessageBox>
#include <QJsonDocument>
#include <QJsonObject>
#include <QUrl>
#include "LicenseChecker.h"
#include "qt/network/QtRequest.h"
#include "settings/ApplicationSettings.h"
#include "utility/Version.h"
#include "utility/utility.h"
#include "utility/utilityUuid.h"
void QtUpdateChecker::check(bool force)
{
ApplicationSettings* appSettings = ApplicationSettings::getInstance().get();
if (!force && TimeStamp::now().deltaHours(appSettings->getLastUpdateCheck()) < 24)
{
return;
}
appSettings->setLastUpdateCheck(TimeStamp::now());
appSettings->save();
QString urlString = "https://www.sourcetrail.com/api/v1/versions/latest";
// OS
std::string osString;
#if defined(Q_OS_WIN)
osString = "windows";
#elif defined(Q_OS_MACOS)
osString = "macOS";
#elif defined(Q_OS_LINUX)
osString = "linux";
#endif
if (!osString.size())
{
return;
}
urlString += ("?os=" + osString).c_str();
// architecture
std::string platformString = (utility::getApplicationArchitectureType() == APPLICATION_ARCHITECTURE_X86_64 ? "64" : "32");
urlString += ("&platform=" + platformString + "bit").c_str();
// version
urlString += ("&version=" + Version::getApplicationVersion().toDisplayString()).c_str();
// license
std::string licenseString;
switch (LicenseChecker::getInstance()->getCurrentLicenseType())
{
case MessageEnteredLicense::LICENSE_NONE:
licenseString = "trial";
break;
case MessageEnteredLicense::LICENSE_TEST:
licenseString = "test";
break;
case MessageEnteredLicense::LICENSE_NON_COMMERCIAL:
licenseString = "private";
break;
case MessageEnteredLicense::LICENSE_COMMERCIAL:
licenseString = "commercial";
break;
}
urlString += ("&license=" + licenseString).c_str();
// user token
std::string token = appSettings->getUserToken();
if (!token.size())
{
token = utility::getUuidString();
appSettings->setUserToken(token);
appSettings->save();
}
urlString += ("&token=" + token).c_str();
// send request
QtRequest* request = new QtRequest();
QObject::connect(request, &QtRequest::receivedData,
[force, request](QByteArray bytes)
{
do
{
QJsonDocument doc = QJsonDocument::fromJson(bytes);
if (!doc.isObject())
{
LOG_ERROR_STREAM(<< "Update response couldn't be parsed as JSON");
break;
}
QString version = doc.object().find("version")->toString();
QString url = doc.object().find("url")->toString();
Version updateVersion = Version::fromString(version.toStdString());
if (!updateVersion.isValid())
{
LOG_ERROR_STREAM(<< "update version string is not valid: " << version.toStdString());
break;
}
if (updateVersion > Version::getApplicationVersion())
{
QMessageBox msgBox;
msgBox.setText("Update Check");
msgBox.setInformativeText(
"Sourcetrial " + version + " is available for download: <a href=\"" + url + "\">" + url + "</a>");
msgBox.addButton("Close", QMessageBox::ButtonRole::NoRole);
QPushButton* but = msgBox.addButton("Download", QMessageBox::ButtonRole::YesRole);
msgBox.setDefaultButton(but);
if (msgBox.exec() == 1)
{
QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));
}
}
else if (force)
{
QMessageBox msgBox;
msgBox.setText("Update Check");
msgBox.setInformativeText(
("Sourcetrial " + Version::getApplicationVersion().toDisplayString() + " is still up-to-date.").c_str());
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
}
}
while (false);
request->deleteLater();
}
);
request->sendRequest(urlString);
}
void QtUpdateChecker::checkUpdate()
{
check();
}
+15
View File
@@ -0,0 +1,15 @@
#ifndef QT_UPDATE_CHECKER_H
#define QT_UPDATE_CHECKER_H
#include "UpdateChecker.h"
class QtUpdateChecker
: public UpdateChecker
{
public:
static void check(bool force = false);
virtual void checkUpdate() override;
};
#endif // QT_UPDATE_CHECKER_H
+12 -9
View File
@@ -17,6 +17,7 @@
#include "component/view/View.h"
#include "data/bookmark/Bookmark.h"
#include "LicenseChecker.h"
#include "qt/network/QtUpdateChecker.h"
#include "qt/utility/QtContextMenu.h"
#include "qt/utility/utilityQt.h"
#include "qt/view/QtViewWidgetWrapper.h"
@@ -51,6 +52,7 @@
#include "utility/UserPaths.h"
#include "utility/utilityString.h"
QtViewToggle::QtViewToggle(View* view, QWidget *parent)
: QWidget(parent)
, m_view(view)
@@ -104,7 +106,7 @@ QtMainWindow::QtMainWindow()
, m_showDockWidgetTitleBars(true)
, m_windowStack(this)
{
setObjectName("QtMainWindow");
setObjectName("QtMainWindow");
setCentralWidget(nullptr);
setDockNestingEnabled(true);
@@ -112,13 +114,13 @@ QtMainWindow::QtMainWindow()
setWindowFlags(Qt::Widget);
#ifdef __linux__
if (std::getenv("SOURCETRAIL_VIA_SCRIPT") == nullptr)
{
QMessageBox::warning(this, "Run Sourcetrail via Script", "Please run Sourcetrail via Sourcetrail.sh");
}
if (std::getenv("SOURCETRAIL_VIA_SCRIPT") == nullptr)
{
QMessageBox::warning(this, "Run Sourcetrail via Script", "Please run Sourcetrail via Sourcetrail.sh");
}
#endif
QApplication* app = dynamic_cast<QApplication*>(QCoreApplication::instance());
QApplication* app = dynamic_cast<QApplication*>(QCoreApplication::instance());
app->installEventFilter(new MouseReleaseFilter(this));
app->setStyleSheet(utility::getStyleSheet(ResourcePaths::getGuiPath().concat(FilePath("main.css"))).c_str());
@@ -358,6 +360,7 @@ void QtMainWindow::keyPressEvent(QKeyEvent* event)
break;
case Qt::Key_Space:
QtUpdateChecker::check();
PRINT_TRACES();
break;
}
@@ -376,13 +379,13 @@ void QtMainWindow::closeEvent(QCloseEvent* event)
{
log->setEnabled(false);
}
MessageWindowClosed().dispatch();
MessageWindowClosed().dispatch();
}
void QtMainWindow::resizeEvent(QResizeEvent *event)
{
m_windowStack.centerSubWindows();
QMainWindow::resizeEvent(event);
m_windowStack.centerSubWindows();
QMainWindow::resizeEvent(event);
}
void QtMainWindow::about()
+43 -18
View File
@@ -1,21 +1,23 @@
#include "qt/window/QtStartScreen.h"
#include <QCheckBox>
#include <QLabel>
#include <QString>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QMessageBox>
#include "settings/ApplicationSettings.h"
#include "settings/ProjectSettings.h"
#include "utility/AppPath.h"
#include "utility/messaging/type/MessageLoadProject.h"
#include "utility/ResourcePaths.h"
#include "qt/utility/utilityQt.h"
#include "utility/Version.h"
#include "License.h"
#include "PublicKey.h"
#include "utility/AppPath.h"
#include "qt/network/QtUpdateChecker.h"
#include "qt/utility/utilityQt.h"
#include "settings/ApplicationSettings.h"
#include "settings/ProjectSettings.h"
QtRecentProjectButton::QtRecentProjectButton(QWidget* parent)
: QPushButton(parent)
@@ -158,12 +160,44 @@ void QtStartScreen::setupStartScreen()
QVBoxLayout* col = new QVBoxLayout();
layout->addLayout(col, 2);
QLabel* versionLabel = new QLabel(("Version " + Version::getApplicationVersion().toDisplayString()).c_str(), this);
versionLabel->setObjectName("versionLabel");
col->addWidget(versionLabel);
QPushButton* updateButton = new QPushButton("check for new version", this);
updateButton->setObjectName("updateButton");
updateButton->setCursor(Qt::PointingHandCursor);
connect(updateButton, &QPushButton::clicked,
[]()
{
QtUpdateChecker::check(true);
}
);
col->addWidget(updateButton);
QCheckBox* updateCheckbox = new QCheckBox("automatic update check");
updateCheckbox->setObjectName("updateCheckbox");
updateCheckbox->setChecked(ApplicationSettings::getInstance()->getAutomaticUpdateCheck());
connect(updateCheckbox, &QCheckBox::stateChanged,
[updateCheckbox]()
{
ApplicationSettings::getInstance()->setAutomaticUpdateCheck(updateCheckbox->isChecked());
ApplicationSettings::getInstance()->save();
if (updateCheckbox->isChecked())
{
QtUpdateChecker::check();
}
}
);
col->addWidget(updateCheckbox);
if (!licenseValid)
{
col->addSpacing(4);
col->addSpacing(20);
QLabel* welcomeLabel = new QLabel(
"Welcome to the trial version of <b>Sourcetrail</b>!<br /><br />"
"<b>Welcome to the trial version of Sourcetrail!</b><br />"
"Explore our preindexed projects to experience Sourcetrail's unique user interface. "
"More projects are available for download <a href=\"http://sourcetrail.com/downloads#extra\" style=\"color: #007AC2;\">here</a>.<br /><br />"
"If you want to use Sourcetrail on your own source code please "
@@ -175,6 +209,7 @@ void QtStartScreen::setupStartScreen()
welcomeLabel->setAlignment(Qt::AlignTop);
col->addWidget(welcomeLabel, 0, Qt::AlignHCenter | Qt::AlignTop);
col->addStrut(260);
col->addStretch();
QPushButton* openProjectButton = new QPushButton("Open Project", this);
@@ -201,17 +236,7 @@ void QtStartScreen::setupStartScreen()
}
else
{
QLabel* versionLabel = new QLabel(("Version " + Version::getApplicationVersion().toDisplayString()).c_str(), this);
versionLabel->setObjectName("versionLabel");
col->addWidget(versionLabel);
QLabel* updateLabel = new QLabel(
"<a href=\"https://sourcetrail.com/downloads\" style=\"color: #007AC2;\">check for new version</a>", this);
updateLabel->setOpenExternalLinks(true);
updateLabel->setObjectName("updateLabel");
col->addWidget(updateLabel);
col->addSpacing(20);
col->addSpacing(30);
std::string licenseString = license.getLicenseInfo();