diff options
Diffstat (limited to 'meshmc/launcher/news')
| -rw-r--r-- | meshmc/launcher/news/NewsChecker.cpp | 158 | ||||
| -rw-r--r-- | meshmc/launcher/news/NewsChecker.h | 129 | ||||
| -rw-r--r-- | meshmc/launcher/news/NewsEntry.cpp | 100 | ||||
| -rw-r--r-- | meshmc/launcher/news/NewsEntry.h | 90 |
4 files changed, 477 insertions, 0 deletions
diff --git a/meshmc/launcher/news/NewsChecker.cpp b/meshmc/launcher/news/NewsChecker.cpp new file mode 100644 index 0000000000..3990a83d80 --- /dev/null +++ b/meshmc/launcher/news/NewsChecker.cpp @@ -0,0 +1,158 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NewsChecker.h" + +#include <QByteArray> +#include <QDomDocument> + +#include <QDebug> + +NewsChecker::NewsChecker(shared_qobject_ptr<QNetworkAccessManager> network, + const QString& feedUrl) +{ + m_network = network; + m_feedUrl = feedUrl; +} + +void NewsChecker::reloadNews() +{ + // Start a netjob to download the RSS feed and call rssDownloadFinished() + // when it's done. + if (isLoadingNews()) { + qDebug() + << "Ignored request to reload news. Currently reloading already."; + return; + } + + qDebug() << "Reloading news."; + + NetJob* job = new NetJob("News RSS Feed", m_network); + job->addNetAction(Net::Download::makeByteArray(m_feedUrl, &newsData)); + QObject::connect(job, &NetJob::succeeded, this, + &NewsChecker::rssDownloadFinished); + QObject::connect(job, &NetJob::failed, this, + &NewsChecker::rssDownloadFailed); + m_newsNetJob.reset(job); + job->start(); +} + +void NewsChecker::rssDownloadFinished() +{ + // Parse the XML file and process the RSS feed entries. + qDebug() << "Finished loading RSS feed."; + + m_newsNetJob.reset(); + QDomDocument doc; + { + // Stuff to store error info in. + QString errorMsg = "Unknown error."; + int errorLine = -1; + int errorCol = -1; + + // Parse the XML. + if (!doc.setContent(newsData, false, &errorMsg, &errorLine, + &errorCol)) { + QString fullErrorMsg = + QString("Error parsing RSS feed XML. %1 at %2:%3.") + .arg(errorMsg) + .arg(errorLine) + .arg(errorCol); + fail(fullErrorMsg); + newsData.clear(); + return; + } + newsData.clear(); + } + + // If the parsing succeeded, read it. + QDomNodeList items = doc.elementsByTagName("item"); + m_newsEntries.clear(); + for (int i = 0; i < items.length(); i++) { + QDomElement element = items.at(i).toElement(); + NewsEntryPtr entry; + entry.reset(new NewsEntry()); + QString errorMsg = "An unknown error occurred."; + if (NewsEntry::fromXmlElement(element, entry.get(), &errorMsg)) { + qDebug() << "Loaded news entry" << entry->title; + m_newsEntries.append(entry); + } else { + qWarning() << "Failed to load news entry at index" << i << ":" + << errorMsg; + } + } + + succeed(); +} + +void NewsChecker::rssDownloadFailed(QString reason) +{ + // Set an error message and fail. + fail(tr("Failed to load news RSS feed:\n%1").arg(reason)); +} + +QList<NewsEntryPtr> NewsChecker::getNewsEntries() const +{ + return m_newsEntries; +} + +bool NewsChecker::isLoadingNews() const +{ + return m_newsNetJob.get() != nullptr; +} + +QString NewsChecker::getLastLoadErrorMsg() const +{ + return m_lastLoadError; +} + +void NewsChecker::succeed() +{ + m_lastLoadError = ""; + qDebug() << "News loading succeeded."; + m_newsNetJob.reset(); + emit newsLoaded(); +} + +void NewsChecker::fail(const QString& errorMsg) +{ + m_lastLoadError = errorMsg; + qDebug() << "Failed to load news:" << errorMsg; + m_newsNetJob.reset(); + emit newsLoadingFailed(errorMsg); +} diff --git a/meshmc/launcher/news/NewsChecker.h b/meshmc/launcher/news/NewsChecker.h new file mode 100644 index 0000000000..ea31978ebe --- /dev/null +++ b/meshmc/launcher/news/NewsChecker.h @@ -0,0 +1,129 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <QObject> +#include <QString> +#include <QList> + +#include <net/NetJob.h> + +#include "NewsEntry.h" + +class NewsChecker : public QObject +{ + Q_OBJECT + public: + /*! + * Constructs a news reader to read from the given RSS feed URL. + */ + NewsChecker(shared_qobject_ptr<QNetworkAccessManager> network, + const QString& feedUrl); + + /*! + * Returns the error message for the last time the news was loaded. + * Empty string if the last load was successful. + */ + QString getLastLoadErrorMsg() const; + + /*! + * Returns true if the news has been loaded successfully. + */ + bool isNewsLoaded() const; + + //! True if the news is currently loading. If true, reloadNews() will do + //! nothing. + bool isLoadingNews() const; + + /*! + * Returns a list of news entries. + */ + QList<NewsEntryPtr> getNewsEntries() const; + + /*! + * Reloads the news from the website's RSS feed. + * If the news is already loading, this does nothing. + */ + void Q_SLOT reloadNews(); + + signals: + /*! + * Signal fired after the news has finished loading. + */ + void newsLoaded(); + + /*! + * Signal fired after the news fails to load. + */ + void newsLoadingFailed(QString errorMsg); + + protected slots: + void rssDownloadFinished(); + void rssDownloadFailed(QString reason); + + protected: /* data */ + //! The URL for the RSS feed to fetch. + QString m_feedUrl; + + //! List of news entries. + QList<NewsEntryPtr> m_newsEntries; + + //! The network job to use to load the news. + NetJob::Ptr m_newsNetJob; + + //! True if news has been loaded. + bool m_loadedNews; + + QByteArray newsData; + + /*! + * Gets the error message that was given last time the news was loaded. + * If the last news load succeeded, this will be an empty string. + */ + QString m_lastLoadError; + + shared_qobject_ptr<QNetworkAccessManager> m_network; + + protected slots: + /// Emits newsLoaded() and sets m_lastLoadError to empty string. + void succeed(); + + /// Emits newsLoadingFailed() and sets m_lastLoadError to the given message. + void fail(const QString& errorMsg); +}; diff --git a/meshmc/launcher/news/NewsEntry.cpp b/meshmc/launcher/news/NewsEntry.cpp new file mode 100644 index 0000000000..a7e7a9acbd --- /dev/null +++ b/meshmc/launcher/news/NewsEntry.cpp @@ -0,0 +1,100 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NewsEntry.h" + +#include <QDomNodeList> +#include <QVariant> + +NewsEntry::NewsEntry(QObject* parent) : QObject(parent) +{ + this->title = tr("Untitled"); + this->content = tr("No content."); + this->link = ""; + this->author = tr("Unknown Author"); + this->pubDate = QDateTime::currentDateTime(); +} + +NewsEntry::NewsEntry(const QString& title, const QString& content, + const QString& link, const QString& author, + const QDateTime& pubDate, QObject* parent) + : QObject(parent) +{ + this->title = title; + this->content = content; + this->link = link; + this->author = author; + this->pubDate = pubDate; +} + +/*! + * Gets the text content of the given child element as a QVariant. + */ +inline QString childValue(const QDomElement& element, const QString& childName, + QString defaultVal = "") +{ + QDomNodeList nodes = element.elementsByTagName(childName); + if (nodes.count() > 0) { + QDomElement element = nodes.at(0).toElement(); + return element.text(); + } else { + return defaultVal; + } +} + +bool NewsEntry::fromXmlElement(const QDomElement& element, NewsEntry* entry, + QString* errorMsg) +{ + QString title = childValue(element, "title", tr("Untitled")); + QString content = childValue(element, "description", tr("No content.")); + QString link = childValue(element, "link"); + QString author = childValue(element, "dc:creator", tr("Unknown Author")); + QString pubDateStr = childValue(element, "pubDate"); + + // FIXME: For now, we're just ignoring timezones. We assume that all time + // zones in the RSS feed are the same. + QString dateFormat("ddd, dd MMM yyyy hh:mm:ss"); + QDateTime pubDate = QDateTime::fromString(pubDateStr, dateFormat); + + entry->title = title; + entry->content = content; + entry->link = link; + entry->author = author; + entry->pubDate = pubDate; + return true; +} diff --git a/meshmc/launcher/news/NewsEntry.h b/meshmc/launcher/news/NewsEntry.h new file mode 100644 index 0000000000..40dad993bc --- /dev/null +++ b/meshmc/launcher/news/NewsEntry.h @@ -0,0 +1,90 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <QObject> +#include <QString> +#include <QDomElement> +#include <QDateTime> + +#include <memory> + +class NewsEntry : public QObject +{ + Q_OBJECT + + public: + /*! + * Constructs an empty news entry. + */ + explicit NewsEntry(QObject* parent = 0); + + /*! + * Constructs a new news entry. + * Note that content may contain HTML. + */ + NewsEntry(const QString& title, const QString& content, const QString& link, + const QString& author, const QDateTime& pubDate, + QObject* parent = 0); + + /*! + * Attempts to load information from the given XML element into the given + * news entry pointer. If this fails, the function will return false and + * store an error message in the errorMsg pointer. + */ + static bool fromXmlElement(const QDomElement& element, NewsEntry* entry, + QString* errorMsg = 0); + + //! The post title. + QString title; + + //! The post's content. May contain HTML. + QString content; + + //! URL to the post. + QString link; + + //! The post's author. + QString author; + + //! The date and time that this post was published. + QDateTime pubDate; +}; + +typedef std::shared_ptr<NewsEntry> NewsEntryPtr; |
