commit 1d26801a266d3b2d93632660df130cc884e9fd8d Author: niansa Date: Sun Aug 9 11:03:52 2020 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/QCommsy.pro b/QCommsy.pro new file mode 100644 index 0000000..fd4aa17 --- /dev/null +++ b/QCommsy.pro @@ -0,0 +1,39 @@ +QT += core gui network + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++17 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp + +HEADERS += \ + libcommsy.hpp \ + libcommsyauth.hpp + +FORMS += \ + failure.ui \ + info.ui \ + login.ui \ + offline.ui \ + overview.ui + +LIBS += -lcurlpp -lcurl +LIBS += -lfuse +LIBS += -lgumbo + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/failure.ui b/failure.ui new file mode 100644 index 0000000..3e86ba1 --- /dev/null +++ b/failure.ui @@ -0,0 +1,98 @@ + + + failureWindow + + + + 0 + 0 + 323 + 489 + + + + QCommsy + + + + + + + + 72 + + + + 🙈 + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Qt::AlignCenter + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Abmelden + + + + + + + nochmal versuchen + + + true + + + + + + + + + + + diff --git a/info.ui b/info.ui new file mode 100644 index 0000000..e324507 --- /dev/null +++ b/info.ui @@ -0,0 +1,66 @@ + + + infoWindow + + + + 0 + 0 + 323 + 489 + + + + QCommsy + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Okay + + + + + + + + + diff --git a/libcommsy.hpp b/libcommsy.hpp new file mode 100644 index 0000000..fb6999a --- /dev/null +++ b/libcommsy.hpp @@ -0,0 +1,389 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +using json = nlohmann::json; + +#include + +class invalidSIDError : public std::exception {}; +class invalidRoomError : public std::exception {}; +class invalidPostError : public std::exception {}; +class connectionFailError : public std::exception {}; +class parsingNoSuchIDError : public std::exception {}; +class parsingNoSuchTagError : public std::exception {}; +class descDownloadError : public std::exception {}; + + + +static std::string server_url; +static std::string server_sid; +static std::string room; + + + +std::string ltrim(const std::string& s) { + static const std::regex lws{"^[[:space:]]*", std::regex_constants::extended}; + return std::regex_replace(s, lws, ""); +} +std::string rtrim(const std::string& s) { + static const std::regex tws{"[[:space:]]*$", std::regex_constants::extended}; + return std::regex_replace(s, tws, ""); +} +std::string trim(const std::string& s) { + return ltrim(rtrim(s)); +} + +std::string get_filename(const std::string& path) { + return path.substr(path.find_last_of("/\\") + 1); +} + +std::string clean_spaces(const std::string& s) { + static const std::regex tws{"[ ]{2,}", std::regex_constants::extended}; + std::string newstr = std::regex_replace(s, tws, ""); + std::replace(newstr.begin(), newstr.end(), '\n', ' '); + newstr.erase(0, 4); + return newstr; +} + +std::vector merge_strvects(std::vector base, const std::vector &addition) { + base.insert(base.end(), addition.begin(), addition.end()); + return base; +} + +static long curlreq(std::stringstream &responsebuffer, std::string SID, std::string URL) { + // Initialise variables + curlpp::Cleanup cleaner; + curlpp::Easy request; + // Set the writer callback to enable cURL to write result in a memory area + request.setOpt(new curlpp::options::WriteStream(&responsebuffer)); + // Setting the URL to retrive. + request.setOpt(new curlpp::options::Url(URL)); + // Set SID cookie + std::list header; + header.push_back("Cookie: SID=" + SID); + request.setOpt(new curlpp::options::HttpHeader(header)); + // Perform request + request.perform(); + // Return result + return curlpp::infos::ResponseCode::get(request); +} + +void gumbo_search_by_attr(std::vector &elemvect, GumboNode* node, std::string attrname, std::string searchword, GumboTag expectedtag) { + if (node->type != GUMBO_NODE_ELEMENT) { + return; + } + GumboAttribute* hclass; + if (node->v.element.tag == expectedtag && + (hclass = gumbo_get_attribute(&node->v.element.attributes, attrname.c_str()))) { + if (hclass->value == searchword) { + elemvect.push_back(node); + } + } + GumboVector* children = &node->v.element.children; + for (unsigned int i = 0; i < children->length; ++i) { + gumbo_search_by_attr(elemvect, static_cast(children->data[i]), attrname, searchword, expectedtag); + } +} +void gumbo_search_by_class(std::vector &elemvect, GumboNode* node, std::string searchword, GumboTag expectedtag) { + return gumbo_search_by_attr(elemvect, node, "class", searchword, expectedtag); +} +GumboNode *gumbo_search_by_id(GumboNode* node, std::string searchword, GumboTag expectedtag) { + std::vector elemvect; + gumbo_search_by_attr(elemvect, node, "id", searchword, expectedtag); + // Use first node found + if (elemvect.size() > 0) { + return elemvect[0]; + } + // If no nodes were found, panic() + throw parsingNoSuchIDError(); + return new GumboNode; +} + +void gumbo_search_by_tag(std::vector &elemvect, GumboNode* node, GumboTag searchedtag) { + if (node->type != GUMBO_NODE_ELEMENT) { + return; + } + if (node->v.element.tag == searchedtag) { + elemvect.push_back(node); + } + GumboVector* children = &node->v.element.children; + for (unsigned int i = 0; i < children->length; ++i) { + gumbo_search_by_tag(elemvect, static_cast(children->data[i]), searchedtag); + } +} + +static std::string gumbo_cleantext(GumboNode* node) { + if (node->type == GUMBO_NODE_TEXT) { + return std::string(node->v.text.text); + } else if (node->type == GUMBO_NODE_ELEMENT && + node->v.element.tag != GUMBO_TAG_SCRIPT && + node->v.element.tag != GUMBO_TAG_STYLE) { + std::string contents = ""; + GumboVector* children = &node->v.element.children; + for (unsigned int i = 0; i < children->length; ++i) { + const std::string text = gumbo_cleantext(reinterpret_cast (children->data[i])); + if (i != 0 && !text.empty()) { + contents.append(" "); + } + contents.append(text); + } + return contents; + } else { + return ""; + } +} + +std::vector gumbo_get_attr(GumboNode *node, std::string attrkey, GumboTag expected_tag) { + std::vector attrvals; + GumboNode *childnode; + GumboVector* children = &node->v.element.children; + std::vector toappend; + // Check if current element is already the right one + if (node->v.element.tag == expected_tag) { + // Return this elements wanted attribute key + return {gumbo_get_attribute(&node->v.element.attributes, attrkey.c_str())->value}; + } + // Check if This is a node element + else if (node->type != GUMBO_NODE_ELEMENT) { + return {}; + } + // Iterate through child nodes + for (unsigned int it = 0; it < children->length; ++it) { + childnode = reinterpret_cast (children->data[it]); + if (childnode->v.element.tag == expected_tag) { // If node is the expected tag; use it + attrvals.push_back(gumbo_get_attribute(&childnode->v.element.attributes, attrkey.c_str())->value); + } else if (childnode->type == GUMBO_NODE_ELEMENT) { // Else; iterate through its child nodes + toappend = gumbo_get_attr(childnode, attrkey, expected_tag); + attrvals = merge_strvects(attrvals, toappend); + } + } + // Return the final result + return attrvals; +} + +std::string gumbo_find_text_by_tag(GumboNode *node, GumboTag searchtag) { + GumboNode *childnode; + GumboVector* children = &node->v.element.children; + // Iterate through childs + for (unsigned int it = 0; it < children->length; ++it) { + childnode = reinterpret_cast (children->data[it]); + if (childnode->v.element.tag == searchtag) { // If node is the expected tag; check content + return trim(gumbo_cleantext(childnode)); + } + } + throw parsingNoSuchTagError(); + return ""; +} + + +auto get_posts(GumboNode *node) { + std::vector posts; + gumbo_search_by_class(posts, node, "uk-comment", GUMBO_TAG_ARTICLE); + return posts; +} + +std::string get_post_name(GumboNode *node) { + std::vector titlenodes; + gumbo_search_by_class(titlenodes, node, "uk-comment-title", GUMBO_TAG_H4); + return trim(gumbo_cleantext(titlenodes[0])); +} + +std::string get_post_id(GumboNode *node) { + return gumbo_get_attr(node, "data-item-id", GUMBO_TAG_ARTICLE)[0]; +} + +std::string get_post_meta(GumboNode *node) { + std::vector metanodes; + gumbo_search_by_class(metanodes, node, "uk-comment-meta", GUMBO_TAG_DIV); + return clean_spaces(trim(gumbo_cleantext(metanodes[1]))); +} + +std::string get_post_url(GumboNode *node) { + std::vector titlenodes; + gumbo_search_by_class(titlenodes, node, "uk-comment-title", GUMBO_TAG_H4); + return gumbo_get_attr(titlenodes[0], "href", GUMBO_TAG_A)[0]; +} + +std::vector> get_post_files(GumboNode *node) { + std::vector metanodes; + std::vector fileurls; + std::vector filenames; + std::vector> filenameurlmap; + std::map tmpmap; + // Get meta nodes + gumbo_search_by_class(metanodes, node, "uk-comment-meta", GUMBO_TAG_DIV); + // Get URLs + fileurls = gumbo_get_attr(metanodes[2], "href", GUMBO_TAG_A); + // Get filenames + filenames = gumbo_get_attr(metanodes[2], "title", GUMBO_TAG_A); + // Generate map + auto urlit = fileurls.begin(); + auto nameit = filenames.begin(); + while (true) { + // Break if last item was reached + if (urlit == fileurls.end() or nameit == filenames.end()) { + break; + } + // Generate temporary map + tmpmap = {}; + tmpmap[*nameit] = *urlit; + // Append it to the result vector map + filenameurlmap.push_back(tmpmap); + // Get next item in both vectors + urlit++; nameit++; + } + return filenameurlmap; +} + +std::string get_post_desc(std::string post_url) { + std::string material_id; + std::stringstream httpcontent; + GumboOutput *post_document; + GumboNode *desc_node; + std::vector results; + // Get material ID + material_id = get_filename(post_url); + // Download post + long statuscode = curlreq(httpcontent, server_sid, post_url); + // Check statuscode + if (statuscode != 200) { + throw descDownloadError(); + } + // Parse post + post_document = gumbo_parse(httpcontent.str().c_str()); + // Get description element + desc_node = gumbo_search_by_id(post_document->root, "description" + material_id, GUMBO_TAG_DIV); + // Extract description + gumbo_search_by_tag(results, desc_node, GUMBO_TAG_P); + // Cencenate occurencies + std::string result_string; + for (auto it = results.begin(); it != results.end(); it++) { + result_string.append(trim(gumbo_cleantext(*it)) + "\n"); + } + // Return first occurence + return result_string; +} + + +struct commsyPost { + std::string name; + std::string id; + std::string description; + std::string meta; + std::string url; + std::map files; +}; + + +#define libCommsy_NAME "libcommsy" +#define libCommsy_VERSION "1.0" +class libCommsy { +public: + std::vector posts; + unsigned long numposts; + + bool postExists(unsigned long postID) { + return postID < numposts; + } + + commsyPost *getPost(unsigned long postID) { + // Check if post exists + if (not postExists(postID)) { + throw invalidPostError(); + } + // Return post pointer + return &posts[postID]; + } + + std::string *getDescription(unsigned long postID) { + // Get post + commsyPost *thispost = getPost(postID); + // Check if post description was downloaded already + if (thispost->description.empty()) { + // Download post + thispost->description = get_post_desc(server_url + thispost->url); + } + // Return it + return &thispost->description; + } + + libCommsy(std::string _server_url, std::string _server_sid, std::string _room) { + // Define required variables + server_url = _server_url; + server_sid = _server_sid; + room = _room; + std::string lastID; + std::stringstream httpcontent; + GumboOutput *document; + long statuscode; + numposts = 0; + while (1) { + // Check connection and download feed + try { + statuscode = curlreq(httpcontent, server_sid, server_url + "/room/" + room + "/feed/10/date?lastId=" + lastID); + } catch (std::exception&) { + throw connectionFailError(); + } + if (statuscode == 302) { + throw invalidSIDError(); + } else if (statuscode == 500) { + throw invalidRoomError(); + } else if (statuscode != 200) { + throw connectionFailError(); + } + // Do some stuff + document = gumbo_parse(httpcontent.str().c_str()); + httpcontent.str(std::string()); // Clear buffer just in case we need it later + // Get posts + auto gumboPosts = get_posts(document->root); + if (gumboPosts.size() == 0) { + // Stop fetching more data + break; + } + // Map posts and their corresponding URL to a number + for (auto it = gumboPosts.begin(); it != gumboPosts.end(); it++) { + // Create post struct + commsyPost thispost; + { + // Get posts name + thispost.name = get_post_name(*it); + // Get posts ID + thispost.id = get_post_id(*it); + // Get posts meta string + thispost.meta = get_post_meta(*it); + // Get posts URL + thispost.url = get_post_url(*it); + // Get posts files + auto files = get_post_files(*it); + for (const auto& filemap : files) { + thispost.files.insert(filemap.begin(), filemap.end()); + } + } + // Append to posts vector + posts.push_back(thispost); + // Increment post counter + numposts++; + // Get lastID + lastID = posts.back().id; + } + } + } +}; diff --git a/libcommsyauth.hpp b/libcommsyauth.hpp new file mode 100644 index 0000000..87354bc --- /dev/null +++ b/libcommsyauth.hpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define libCommsyAuth_NAME "libcommsyauth" +#define libCommsyAuth_VERSION "0.1-alpha" + +class authFailureError : public std::exception {}; + + +class libCommsyAuth : QObject { +public: + QNetworkReply *connect(const QUrl& serverBaseUrl) { + // Initialise variables + QNetworkAccessManager mNetMan(this); + QNetworkRequest mNetReq; + // Initialise request + //mNetReq->setTransferTimeout(15000); + mNetReq.setUrl(serverBaseUrl); + mNetReq.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + return mNetMan.get(mNetReq); + } + + QString getAuthUrl(const QNetworkReply *reply) { + return reply->url().toString(); + } + + QNetworkReply *sendAuth(const QString& authUrl, const QString& username, const QString& password) { + QNetworkAccessManager mNetMan(this); + QNetworkRequest mNetReq; + QUrlQuery mNetQuery; + // Set auth credentials + mNetQuery.addQueryItem("user_id", username); + mNetQuery.addQueryItem("password", password); + // Create request + mNetReq.setUrl(authUrl + "&mod=context&fct=login"); + mNetReq.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + // Perform the request + return mNetMan.post(mNetReq, mNetQuery.toString(QUrl::FullyEncoded).toUtf8()); + } + + QString getSID(QNetworkReply *reply) { + auto cookies = QNetworkCookie::parseCookies(reply->rawHeader("Set-Cookie")); + for (const auto& cookie : cookies) { + if (cookie.name() == "SID") { + return cookie.value(); + } + } + throw authFailureError(); + } +}; diff --git a/login.ui b/login.ui new file mode 100644 index 0000000..648481d --- /dev/null +++ b/login.ui @@ -0,0 +1,130 @@ + + + loginWindow + + + + 0 + 0 + 323 + 489 + + + + QCommsy + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 8 + + + + color: red; + + + + + + + + + + Qt::ImhNoAutoUppercase|Qt::ImhNoPredictiveText|Qt::ImhPreferLowercase|Qt::ImhUrlCharactersOnly + + + https://unterricht.sh.schulcommsy.de + + + Server Adresse + + + + + + + + + + Kennung + + + + + + + Qt::ImhDigitsOnly + + + + + + 7 + + + Raum + + + + + + + + + + QLineEdit::Password + + + SID + + + + + + + Daten speichern + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Anmelden + + + + + + + + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..fe64568 --- /dev/null +++ b/main.cpp @@ -0,0 +1,225 @@ +#include "ui_overview.h" +#include "ui_info.h" +#include "ui_login.h" +#include "ui_failure.h" +#include "ui_offline.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libcommsy.hpp" +#include "libcommsyauth.hpp" + +#define QCommsy_NAME "QCommsy" +#define QCommsy_VERSION "0.1-alpha" + +QSettings *settings; + + +class _UILoader { + libCommsy *connector = nullptr; + libCommsyAuth *auther; + bool cacheInvalidate = true; + +public: + _UILoader() { + auther = new libCommsyAuth(); + } + + void reconnect() { + // Destroy old connector + if (connector != nullptr) { + if (not cacheInvalidate) { + return; + } + delete connector; + connector = nullptr; + } + cacheInvalidate = false; + // Connect to commsy + connector = new libCommsy(settings->value("server_url").toString().toStdString(), settings->value("server_sid").toString().toStdString(), settings->value("server_room").toString().toStdString()); + } + + void logout() { + settings->remove("server_sid"); + } + + void overviewWindow(QMainWindow *w, bool catchInvalidRoomError = true); + void failureWindow(QMainWindow *w); + void loginWindow(QMainWindow *w, const QString& failure = ""); + void infoWindow(QMainWindow *w); + void offlineWindow(QMainWindow *w); +}; + +void _UILoader::infoWindow(QMainWindow *w) { + Ui::infoWindow *thisui = new Ui::infoWindow; + // Show initial UI + thisui->setupUi(w); + w->show(); + // Update version info + thisui->versionInfo->setText( + "App frontend: " QCommsy_NAME " version " QCommsy_VERSION "
" + "Authentication backend: " libCommsyAuth_NAME " version " libCommsyAuth_VERSION "
" + "Scrapping backend: " libCommsy_NAME " version " libCommsy_VERSION + ); + // Add button handlers + w->connect(thisui->backButton, &QPushButton::pressed, [this, w] () { + loginWindow(w); + }); +} + +void _UILoader::failureWindow(QMainWindow *w) { + Ui::failureWindow *thisui = new Ui::failureWindow; + QString reason; + // Show initial UI + thisui->setupUi(w); + w->show(); + // Get error message + reason = "Ein interner Fehler ist aufgetreten
" + "Dies ist sehr wahrscheinlich ein Programmfehler. Sollte das zu häufig passieren, kontaktiere bitte den Entwickler"; + // Show error message + thisui->descriptionText->setText("Verbindung fehlgeschlagen
" + reason); + // Add button handlers + w->connect(thisui->retryButton, &QPushButton::pressed, [this, w] () { + loginWindow(w); + }); + w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () { + logout(); + loginWindow(w); + }); +} +void _UILoader::offlineWindow(QMainWindow *w) { + Ui::offlineWindow *thisui = new Ui::offlineWindow; + QString reason; + // Show initial UI + thisui->setupUi(w); + w->show(); + // Add button handlers + w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () { + logout(); + loginWindow(w); + }); +} + +void _UILoader::loginWindow(QMainWindow *w, const QString& failure) { + Ui::loginWindow *thisui = new Ui::loginWindow; + // Check if logged in already + if (settings->contains("server_sid")) { + return overviewWindow(w); + } + // Show initial UI + thisui->setupUi(w); + w->show(); + // Restore login data if wanted + bool restoreForm = settings->value("server_save", false).toBool(); + if (restoreForm) { + thisui->saveBox->setCheckState(Qt::Checked); + //... TODO + } + // Set failure text + thisui->failureText->setText(failure); + // Add button handlers + w->connect(thisui->loginButton, &QPushButton::pressed, [this, thisui, w] () { + // Check input lines + if (thisui->adressLine->text().isEmpty() or thisui->usernameLine->text().isEmpty() or thisui->roomLine->text().isEmpty() or thisui->passwordLine->text().isEmpty()) { + return; + } + // Save basic server credentials + settings->setValue("server_save", thisui->saveBox->checkState() == Qt::Checked); + settings->setValue("server_url", thisui->adressLine->text()); + settings->setValue("server_room", thisui->roomLine->text()); + // Connect + QNetworkReply *netReply = auther->connect(thisui->adressLine->text()); + w->connect(netReply, &QNetworkReply::readyRead, [this, thisui, w, &netReply] () { // SIGSEGV + // Get SID + netReply = auther->sendAuth(auther->getAuthUrl(netReply), thisui->usernameLine->text(), thisui->passwordLine->text()); + w->connect(netReply, &QNetworkReply::readyRead, [this, thisui, w, netReply] () { + // Login + try { + settings->setValue("server_sid", auther->getSID(netReply)); + cacheInvalidate = true; + overviewWindow(w, false); + } catch (authFailureError&) { + thisui->failureText->setText("Die eingegebenen Anmeldedaten sind ungültig"); + } catch (invalidRoomError&) { + thisui->failureText->setText("Der angegebene Raum existiert nicht"); + } + }); + }); + }); +} + +void _UILoader::overviewWindow(QMainWindow *w, bool catchInvalidRoomError) { + Ui::overviewWindow *thisui = new Ui::overviewWindow; + // Connect if required + try { + reconnect(); + } catch (invalidSIDError&) { + logout(); + return loginWindow(w, "Die Sitzung ist abgelaufen - bitte erneut anmelden"); + } catch (invalidRoomError& exception) { + if (catchInvalidRoomError) { + logout(); + return loginWindow(w, "Der Raum existiert nicht mehr"); + } else { + throw exception; + } + } catch (connectionFailError&) { + return offlineWindow(w); + } catch (std::exception&) { + return failureWindow(w); + } + // Show initial UI + thisui->setupUi(w); + w->show(); + // Refresh view + thisui->postCounter->setText(QString::number(connector->numposts) + " Einträge"); + thisui->postList->clear(); + for (const auto& thispost : connector->posts) { + thisui->postList->addItem(QString::fromStdString(thispost.name)); + } + // Add button handlers + w->connect(thisui->postList, &QListWidget::activated, [this, thisui] (QModelIndex index) { + QDesktopServices::openUrl(QUrl(settings->value("server_url").toString() + QString::fromStdString(connector->getPost(index.row())->url))); + }); + w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () { + logout(); + loginWindow(w); + }); + w->connect(thisui->infoButton, &QPushButton::pressed, [this, w] () { + infoWindow(w); + }); +} + + + + +int main(int argc, char *argv[]) { + // Initalise settings storage + QCoreApplication::setOrganizationName("QCommsy"); + QCoreApplication::setOrganizationDomain("qcommsy.github.io"); + QCoreApplication::setApplicationName("QCommsy"); + settings = new QSettings(); + + //Initialise variables + QApplication a(argc, argv); + QMainWindow w; + _UILoader UILoader; + + // Set theme + settings->clear(); + a.setStyle(QStyleFactory::create("windows")); + + // Load initial window + UILoader.loginWindow(&w); + + // Run forever + return a.exec(); +} diff --git a/offline.ui b/offline.ui new file mode 100644 index 0000000..3b98621 --- /dev/null +++ b/offline.ui @@ -0,0 +1,84 @@ + + + offlineWindow + + + + 0 + 0 + 323 + 489 + + + + QCommsy + + + + + + + + 72 + + + + 📴 + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Entweder dein Handy oder der Server ist offline<br/></span>Wenn du dir ganz sicher bist, dass du eine funktionierende Internet Verbindung hast, ist sehr wahrscheinlich der Server wegen Wartungsarbeiten zurzeit offline.</p></body></html> + + + Qt::AlignCenter + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Abmelden + + + + + + + + + diff --git a/overview.ui b/overview.ui new file mode 100644 index 0000000..80e8b49 --- /dev/null +++ b/overview.ui @@ -0,0 +1,71 @@ + + + overviewWindow + + + + 0 + 0 + 323 + 489 + + + + QCommsy + + + + + + + QAbstractItemView::NoEditTriggers + + + + + + + + + QCommsy + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Abmelden + + + + + + + Info + + + true + + + + + + + + + + +