Commit e3f616d1 authored by akiraohgaki's avatar akiraohgaki Committed by GitHub
Browse files

Merge pull request #28 from xdgurl/development

Development
parents 7fac35ad 5e9572b2
unix:!android {
unix:!ios:!android {
isEmpty(PREFIX) {
PREFIX = /usr/local
}
SRCDIR = src
SRCDIR = $${PWD}/src
BINDIR = $${PREFIX}/bin
DATADIR = $${PREFIX}/share
......
# Maintainer: Akira Ohgaki <akiraohgaki@gmail.com>
pkgname='xdgurl'
pkgver='2.0.0'
pkgver='2.0.1'
pkgrel='1'
pkgdesc='An install helper program for desktop stuff.'
arch=('i686' 'x86_64')
......
......@@ -87,6 +87,7 @@ build_appimage() {
tar -xzvf "${BUILDDIR}/${PKGNAME}.tar.gz" -C "${BUILDDIR}"
cd "${BUILDDIR}/${PKGNAME}"
#qmake
/opt/qt57/bin/qmake
make
strip ./xdgurl
......
Summary: An install helper program for desktop stuff
Name: xdgurl
Version: 2.0.0
Version: 2.0.1
Release: 1%{?dist}
License: GPLv3+
Group: Applications/Internet
......@@ -37,6 +37,10 @@ make INSTALL_ROOT="%{buildroot}" install
rm -rf %{buildroot}
%changelog
* Mon Nov 14 2016 Akira Ohgaki <akiraohgaki@gmail.com> - 2.0.1-1
- Update library
- Fix download/installation process
* Fri Oct 28 2016 Akira Ohgaki <akiraohgaki@gmail.com> - 2.0.0-1
- Re-implemented xdgurl as Qt program
- Download progress bar
......
xdgurl (2.0.1-0ubuntu1) xenial; urgency=low
* Update library
* Fix download/installation process
* Package depends for xenial and later
-- Akira Ohgaki <akiraohgaki@gmail.com> Mon, 14 Nov 2016 01:40:41 +0000
xdgurl (2.0.0-0ubuntu1) xenial; urgency=low
* Re-implemented xdgurl as Qt program
......
......@@ -7,6 +7,6 @@ Standards-Version: 3.9.4
Package: xdgurl
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libqt5svg5 (>= 5.3.0), qtdeclarative5-qtquick2-plugin (>= 5.3.0), qtdeclarative5-window-plugin (>= 5.3.0), qtdeclarative5-controls-plugin (>= 5.3.0), qtdeclarative5-dialogs-plugin (>= 5.3.0)
Depends: ${shlibs:Depends}, ${misc:Depends}, libqt5svg5 (>= 5.3.0), qml-module-qtquick2 (>= 5.3.0) | qtdeclarative5-qtquick2-plugin (>= 5.3.0), qml-module-qtquick-window2 (>= 5.3.0) | qtdeclarative5-window-plugin (>= 5.3.0), qml-module-qtquick-controls (>= 5.3.0) | qtdeclarative5-controls-plugin (>= 5.3.0), qml-module-qtquick-dialogs (>= 5.3.0) | qtdeclarative5-dialogs-plugin (>= 5.3.0)
Description: An install helper program for desktop stuff
An install helper program for desktop stuff.
unix:!android {
QMAKE_LFLAGS += -Wl,-rpath=\\\$\$ORIGIN
QMAKE_LFLAGS += -Wl,-rpath=/usr/lib/$${TARGET}
QMAKE_RPATH =
}
QT += \
core \
gui \
qml \
quick \
svg
HEADERS += \
$${PWD}/handlers/xdgurl.h
SOURCES += \
$${PWD}/main.cpp \
$${PWD}/handlers/xdgurl.cpp
RESOURCES += \
$${PWD}/configs/configs.qrc \
$${PWD}/qml/qml.qrc
{
"id": "xdgurl",
"name": "xdgurl",
"version": "2.0.0",
"version": "2.0.1",
"organization": "xdgurl",
"domain": "com.xdgurl.xdgurl",
"icon": ":/desktop/xdgurl.svg",
......
#include "xdgurl.h"
#include <QUrl>
#include <QUrlQuery>
#include <QTemporaryFile>
#include <QNetworkReply>
#include <QDesktopServices>
#include "../../libs/utils/config.h"
#include "../../libs/utils/network.h"
#include "../../libs/utils/file.h"
#include "../../libs/utils/package.h"
#include "qtlibs/file.h"
#include "qtlibs/dir.h"
#include "qtlibs/networkresource.h"
#include "qtlibs/package.h"
namespace handlers {
XdgUrl::XdgUrl(const QString &xdgUrl, utils::Config *config, utils::Network *network, QObject *parent) :
QObject(parent), xdgUrl_(xdgUrl), config_(config), network_(network)
XdgUrl::XdgUrl(const QString &xdgUrl, const qtlibs::Config &config, QObject *parent)
: QObject(parent), xdgUrl_(xdgUrl), config_(config)
{
parse();
loadDestinations();
}
connect(network_, &utils::Network::finished, this, &handlers::XdgUrl::downloaded);
connect(network_, &utils::Network::downloadProgress, this, &handlers::XdgUrl::downloadProgress);
QString XdgUrl::xdgUrl() const
{
return xdgUrl_;
}
QJsonObject XdgUrl::metadata() const
{
return metadata_;
}
void XdgUrl::process()
......@@ -34,11 +38,15 @@ void XdgUrl::process()
QJsonObject result;
result["status"] = QString("error_validation");
result["message"] = QString("Invalid XDG-URL " + xdgUrl_);
emit error(result);
emit finishedWithError(result);
return;
}
network_->get(QUrl(metadata_["url"].toString()));
QString url = metadata_["url"].toString();
qtlibs::NetworkResource *resource = new qtlibs::NetworkResource(url, QUrl(url), true, this);
connect(resource, &qtlibs::NetworkResource::downloadProgress, this, &XdgUrl::downloadProgress);
connect(resource, &qtlibs::NetworkResource::finished, this, &XdgUrl::networkResourceFinished);
resource->get();
emit started();
}
......@@ -57,7 +65,6 @@ bool XdgUrl::isValid()
&& !filename.isEmpty()) {
return true;
}
return false;
}
......@@ -68,47 +75,21 @@ void XdgUrl::openDestination()
}
}
QString XdgUrl::xdgUrl() const
{
return xdgUrl_;
}
QJsonObject XdgUrl::metadata() const
{
return metadata_;
}
void XdgUrl::downloaded(QNetworkReply *reply)
void XdgUrl::networkResourceFinished(qtlibs::NetworkResource *resource)
{
if (reply->error() != QNetworkReply::NoError) {
if (resource->reply()->error() != QNetworkReply::NoError) {
QJsonObject result;
result["status"] = QString("error_network");
result["message"] = reply->errorString();
emit error(result);
return;
}
else if (reply->hasRawHeader("Location")) {
QString redirectUrl = QString(reply->rawHeader("Location"));
if (redirectUrl.startsWith("/")) {
redirectUrl = reply->url().authority() + redirectUrl;
}
network_->get(QUrl(redirectUrl));
return;
}
else if (reply->hasRawHeader("Refresh")) {
QString refreshUrl = QString(reply->rawHeader("Refresh")).split("url=").last();
if (refreshUrl.startsWith("/")) {
refreshUrl = reply->url().authority() + refreshUrl;
}
network_->get(QUrl(refreshUrl));
result["message"] = resource->reply()->errorString();
emit finishedWithError(result);
return;
}
if (metadata_["command"].toString() == "download") {
saveDownloadedFile(reply);
saveDownloadedFile(resource);
}
else if (metadata_["command"].toString() == "install") {
installDownloadedFile(reply);
installDownloadedFile(resource);
}
}
......@@ -150,8 +131,8 @@ void XdgUrl::parse()
void XdgUrl::loadDestinations()
{
QJsonObject configDestinations = config_->get("destinations");
QJsonObject configDestinationsAlias = config_->get("destinations_alias");
QJsonObject configDestinations = config_.get("destinations");
QJsonObject configDestinationsAlias = config_.get("destinations_alias");
foreach (const QString key, configDestinations.keys()) {
destinations_[key] = convertPathString(configDestinations[key].toString());
......@@ -170,42 +151,32 @@ QString XdgUrl::convertPathString(const QString &path)
QString newPath = path;
if (newPath.contains("$HOME")) {
newPath.replace("$HOME", utils::File::homePath());
newPath.replace("$HOME", qtlibs::Dir::homePath());
}
else if (newPath.contains("$XDG_DATA_HOME")) {
newPath.replace("$XDG_DATA_HOME", utils::File::genericDataPath());
newPath.replace("$XDG_DATA_HOME", qtlibs::Dir::genericDataPath());
}
else if (newPath.contains("$KDEHOME")) {
newPath.replace("$KDEHOME", utils::File::kdehomePath());
newPath.replace("$KDEHOME", qtlibs::Dir::kdehomePath());
}
return newPath;
}
void XdgUrl::saveDownloadedFile(QNetworkReply *reply)
void XdgUrl::saveDownloadedFile(qtlibs::NetworkResource *resource)
{
QJsonObject result;
QTemporaryFile temporaryFile;
if (!temporaryFile.open() || temporaryFile.write(reply->readAll()) == -1) {
result["status"] = QString("error_save");
result["message"] = temporaryFile.errorString();
emit error(result);
return;
}
QString type = metadata_["type"].toString();
QString destination = destinations_[type].toString();
QString path = destination + "/" + metadata_["filename"].toString();
utils::File::makeDir(destination);
utils::File::remove(path); // Remove previous downloaded file
qtlibs::Dir(destination).make();
if (!temporaryFile.copy(path)) {
if (!resource->saveData(path)) {
result["status"] = QString("error_save");
result["message"] = temporaryFile.errorString();
emit error(result);
result["message"] = QString("Failed to save data as " + path);
emit finishedWithError(result);
return;
}
......@@ -213,74 +184,79 @@ void XdgUrl::saveDownloadedFile(QNetworkReply *reply)
result["status"] = QString("success_download");
result["message"] = QString("The file has been stored into " + destination);
emit finished(result);
emit finishedWithSuccess(result);
}
void XdgUrl::installDownloadedFile(QNetworkReply *reply)
void XdgUrl::installDownloadedFile(qtlibs::NetworkResource *resource)
{
QJsonObject result;
QTemporaryFile temporaryFile;
QString tempPath = qtlibs::Dir::tempPath() + "/" + metadata_["filename"].toString();
if (!temporaryFile.open() || temporaryFile.write(reply->readAll()) == -1) {
if (!resource->saveData(tempPath)) {
result["status"] = QString("error_save");
result["message"] = temporaryFile.errorString();
emit error(result);
result["message"] = QString("Failed to save data as " + tempPath);
emit finishedWithError(result);
return;
}
qtlibs::Package package(tempPath);
qtlibs::File tempFile(tempPath);
QString type = metadata_["type"].toString();
QString destination = destinations_[type].toString();
QString path = destination + "/" + metadata_["filename"].toString();
utils::File::makeDir(destination);
utils::File::remove(path); // Remove previous downloaded file
qtlibs::Dir(destination).make();
if (type == "bin"
&& utils::Package::installProgram(temporaryFile.fileName(), path)) {
result["message"] = QString("The program has been installed into " + destination);
&& package.installAsProgram(path)) {
result["message"] = QString("The file has been installed into " + destination);
}
else if ((type == "plasma_plasmoids" || type == "plasma4_plasmoids" || type == "plasma5_plasmoids")
&& utils::Package::installPlasmapkg(temporaryFile.fileName(), "plasmoid")) {
&& package.installAsPlasmapkg("plasmoid")) {
result["message"] = QString("The plasmoid has been installed");
}
else if ((type == "plasma_look_and_feel" || type == "plasma5_look_and_feel")
&& utils::Package::installPlasmapkg(temporaryFile.fileName(), "lookandfeel")) {
&& package.installAsPlasmapkg("lookandfeel")) {
result["message"] = QString("The plasma look and feel has been installed");
}
else if ((type == "plasma_desktopthemes" || type == "plasma5_desktopthemes")
&& utils::Package::installPlasmapkg(temporaryFile.fileName(), "theme")) {
&& package.installAsPlasmapkg("theme")) {
result["message"] = QString("The plasma desktop theme has been installed");
}
else if (type == "kwin_effects"
&& utils::Package::installPlasmapkg(temporaryFile.fileName(), "kwineffect")) {
&& package.installAsPlasmapkg("kwineffect")) {
result["message"] = QString("The KWin effect has been installed");
}
else if (type == "kwin_scripts"
&& utils::Package::installPlasmapkg(temporaryFile.fileName(), "kwinscript")) {
&& package.installAsPlasmapkg("kwinscript")) {
result["message"] = QString("The KWin script has been installed");
}
else if (type == "kwin_tabbox"
&& utils::Package::installPlasmapkg(temporaryFile.fileName(), "windowswitcher")) {
&& package.installAsPlasmapkg("windowswitcher")) {
result["message"] = QString("The KWin window switcher has been installed");
}
else if (utils::Package::uncompressArchive(temporaryFile.fileName(), destination)) {
result["message"] = QString("The archive file has been uncompressed into " + destination);
else if (package.installAsArchive(destination)) {
result["message"] = QString("The archive file has been extracted into " + destination);
}
else if (temporaryFile.copy(path)) {
result["message"] = QString("The file has been stored into " + destination);
else if (package.installAsFile(path)) {
result["message"] = QString("The file has been installed into " + destination);
}
else {
tempFile.remove();
result["status"] = QString("error_install");
result["message"] = temporaryFile.errorString();
emit error(result);
result["message"] = QString("Failed to installation");
emit finishedWithError(result);
return;
}
tempFile.remove();
destination_ = destination;
result["status"] = QString("success_install");
emit finished(result);
emit finishedWithSuccess(result);
}
} // namespace handlers
......@@ -3,11 +3,10 @@
#include <QObject>
#include <QJsonObject>
class QNetworkReply;
#include "qtlibs/config.h"
namespace utils {
class Config;
class Network;
namespace qtlibs {
class NetworkResource;
}
namespace handlers {
......@@ -17,35 +16,34 @@ class XdgUrl : public QObject
Q_OBJECT
public:
explicit XdgUrl(const QString &xdgUrl, utils::Config *config, utils::Network *network, QObject *parent = 0);
explicit XdgUrl(const QString &xdgUrl, const qtlibs::Config &config, QObject *parent = 0);
signals:
void started();
void finished(const QJsonObject &result);
void error(const QJsonObject &result);
void downloadProgress(const qint64 &received, const qint64 &total);
void finishedWithSuccess(const QJsonObject &result);
void finishedWithError(const QJsonObject &result);
void downloadProgress(const qint64 &bytesReceived, const qint64 &bytesTotal);
public slots:
QString xdgUrl() const;
QJsonObject metadata() const;
void process();
bool isValid();
void openDestination();
QString xdgUrl() const;
QJsonObject metadata() const;
private slots:
void downloaded(QNetworkReply *reply);
void networkResourceFinished(qtlibs::NetworkResource *resource);
private:
void parse();
void loadDestinations();
QString convertPathString(const QString &path);
void saveDownloadedFile(QNetworkReply *reply);
void installDownloadedFile(QNetworkReply *reply);
void saveDownloadedFile(qtlibs::NetworkResource *resource);
void installDownloadedFile(qtlibs::NetworkResource *resource);
QString xdgUrl_;
utils::Config *config_;
utils::Network *network_;
qtlibs::Config config_;
QJsonObject metadata_;
QJsonObject destinations_;
QString destination_;
......
......@@ -10,8 +10,7 @@
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "../libs/utils/config.h"
#include "../libs/utils/network.h"
#include "qtlibs/config.h"
#include "handlers/xdgurl.h"
......@@ -22,10 +21,9 @@ int main(int argc, char *argv[])
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
utils::Config *config = new utils::Config(":/configs");
utils::Network *network = new utils::Network(true);
QJsonObject configApplication = config->get("application");
qtlibs::Config config(":/configs");
QJsonObject configApplication = config.get("application");
app.setApplicationName(configApplication["name"].toString());
app.setApplicationVersion(configApplication["version"].toString());
......@@ -52,7 +50,7 @@ int main(int argc, char *argv[])
// Setup QML
QQmlApplicationEngine qmlAppEngine;
QQmlContext *qmlContext = qmlAppEngine.rootContext();
qmlContext->setContextProperty("xdgUrlHandler", new handlers::XdgUrl(xdgUrl, config, network));
qmlContext->setContextProperty("xdgUrlHandler", new handlers::XdgUrl(xdgUrl, config, &qmlAppEngine));
qmlAppEngine.load(QUrl("qrc:/qml/main.qml"));
return app.exec();
......
......@@ -114,7 +114,7 @@ Window {
progressDialog.open();
});
xdgUrlHandler.finished.connect(function(result) {
xdgUrlHandler.finishedWithSuccess.connect(function(result) {
progressDialog.close();
infoDialog.text = primaryMessages[result.status];
infoDialog.informativeText = metadata.filename;
......@@ -122,7 +122,7 @@ Window {
infoDialog.open();
});
xdgUrlHandler.error.connect(function(result) {
xdgUrlHandler.finishedWithError.connect(function(result) {
progressDialog.close();
errorDialog.text = primaryMessages[result.status];
errorDialog.informativeText = metadata.filename;
......@@ -130,12 +130,12 @@ Window {
errorDialog.open();
});
xdgUrlHandler.downloadProgress.connect(function(received, total) {
xdgUrlHandler.downloadProgress.connect(function(bytesReceived, bytesTotal) {
progressDialog.primaryLabel.text = 'Downloading... ';
progressDialog.informativeLabel.text = metadata.filename;
progressDialog.progressBar.value = received / total;
progressDialog.progressLabel.text = Utility.convertByteToHumanReadable(received)
+ ' / ' + Utility.convertByteToHumanReadable(total)
progressDialog.progressBar.value = bytesReceived / bytesTotal;
progressDialog.progressLabel.text = Utility.convertByteToHumanReadable(bytesReceived)
+ ' / ' + Utility.convertByteToHumanReadable(bytesTotal)
});
if (xdgUrlHandler.isValid()) {
......
/**
* A library for Qt app
*
* LICENSE: The GNU Lesser General Public License, version 3.0
*
* @author Akira Ohgaki <akiraohgaki@gmail.com>
* @copyright Akira Ohgaki
* @license https://opensource.org/licenses/LGPL-3.0 The GNU Lesser General Public License, version 3.0
* @link https://github.com/akiraohgaki/qtlibs
*/
#include "config.h"
#include "file.h"
#include "dir.h"
#include "json.h"
namespace qtlibs {
Config::Config(const QString &configDirPath, QObject *parent)
: QObject(parent), configDirPath_(configDirPath)
{}
Config::Config(const Config &other)
{
this->setParent(other.parent());
setConfigDirPath(other.configDirPath());
}
Config &Config::operator =(const Config &other)
{
this->setParent(other.parent());
setConfigDirPath(other.configDirPath());
return *this;
}
QString Config::configDirPath() const
{
return configDirPath_;
}
void Config::setConfigDirPath(const QString &configDirPath)
{
configDirPath_ = configDirPath;
}
QJsonObject Config::get(const QString &name)
{
QString configFilePath = configDirPath() + "/" + name + ".json";
QByteArray json = qtlibs::File(configFilePath).readData();
if (json.isEmpty()) {
json = QString("{}").toUtf8(); // Blank JSON data as default
}
return qtlibs::Json(json).toObject();
}
bool Config::set(const QString &name, const QJsonObject &object)
{
QString configFilePath = configDirPath() + "/" + name + ".json";
QByteArray json = qtlibs::Json(object).toJson();
qtlibs::Dir(configDirPath()).make();
if (qtlibs::File(configFilePath).writeData(json)) {
return true;
}
return false;
}
} // namespace qtlibs
......@@ -6,7 +6,7 @@
* @author Akira Ohgaki <akiraohgaki@gmail.com>
* @copyright Akira Ohgaki
* @license https://opensource.org/licenses/LGPL-3.0 The GNU Lesser General Public License, version 3.0
* @link https://github.com/akiraohgaki/qt-libs
* @link https://github.com/akiraohgaki/qtlibs
*/
#pragma once
......@@ -14,21 +14,26 @@
#include <QObject>
#include <QJsonObject>
namespace utils {
namespace qtlibs {
class Config : public QObject
{