Save work.
This commit is contained in:
parent
8f051db71b
commit
8ebb37fd32
@ -155,7 +155,7 @@ QNetworkReply::NetworkError NetworkFactory::downloadIcon(const QList<QString>& u
|
||||
const QString google_s2_with_url = QString("http://www.google.com/s2/favicons?domain=%1").arg(QUrl(url).host());
|
||||
QByteArray icon_data;
|
||||
|
||||
network_result = performNetworkOperation(google_s2_with_url, timeout, QByteArray(), QString(), icon_data,
|
||||
network_result = performNetworkOperation(google_s2_with_url, timeout, QByteArray(), icon_data,
|
||||
QNetworkAccessManager::GetOperation).first;
|
||||
|
||||
if (network_result == QNetworkReply::NoError) {
|
||||
@ -188,28 +188,24 @@ Downloader* NetworkFactory::performAsyncNetworkOperation(const QString& url, int
|
||||
}
|
||||
|
||||
NetworkResult NetworkFactory::performNetworkOperation(const QString& url, int timeout, const QByteArray& input_data,
|
||||
const QString& input_content_type, QByteArray& output,
|
||||
QNetworkAccessManager::Operation operation, bool protected_contents,
|
||||
const QString& username, const QString& password, bool set_basic_header) {
|
||||
QByteArray& output, QNetworkAccessManager::Operation operation,
|
||||
QList<QPair<QByteArray, QByteArray>> additional_headers,
|
||||
bool protected_contents,
|
||||
const QString& username, const QString& password) {
|
||||
Downloader downloader;
|
||||
QEventLoop loop;
|
||||
NetworkResult result;
|
||||
|
||||
if (!input_content_type.isEmpty()) {
|
||||
downloader.appendRawHeader("Content-Type", input_content_type.toLocal8Bit());
|
||||
}
|
||||
|
||||
if (set_basic_header) {
|
||||
QString basic_value = username + ":" + password;
|
||||
QString header_value = QString("Basic ") + QString(basic_value.toUtf8().toBase64());
|
||||
|
||||
downloader.appendRawHeader("Authorization", header_value.toLocal8Bit());
|
||||
}
|
||||
|
||||
// We need to quit event loop when the download finishes.
|
||||
QObject::connect(&downloader, &Downloader::completed, &loop, &QEventLoop::quit);
|
||||
|
||||
foreach (const auto& header, additional_headers) {
|
||||
downloader.appendRawHeader(header.first, header.second);
|
||||
}
|
||||
|
||||
downloader.manipulateData(url, operation, input_data, timeout, protected_contents, username, password);
|
||||
loop.exec();
|
||||
|
||||
output = downloader.lastOutputData();
|
||||
result.first = downloader.lastOutputError();
|
||||
result.second = downloader.lastContentType();
|
||||
|
@ -56,10 +56,13 @@ class NetworkFactory {
|
||||
const QString& username = QString(),
|
||||
const QString& password = QString());
|
||||
static NetworkResult performNetworkOperation(const QString& url, int timeout, const QByteArray& input_data,
|
||||
const QString& input_content_type, QByteArray& output,
|
||||
QByteArray& output,
|
||||
QNetworkAccessManager::Operation operation,
|
||||
bool protected_contents = false, const QString& username = QString(),
|
||||
const QString& password = QString(), bool set_basic_header = false);
|
||||
QList<QPair<QByteArray,
|
||||
QByteArray>> additional_headers = QList<QPair<QByteArray, QByteArray>>(),
|
||||
bool protected_contents = false,
|
||||
const QString& username = QString(),
|
||||
const QString& password = QString());
|
||||
};
|
||||
|
||||
#endif // NETWORKFACTORY_H
|
||||
|
@ -99,13 +99,17 @@ QNetworkReply::NetworkError OwnCloudNetworkFactory::lastError() const {
|
||||
|
||||
OwnCloudUserResponse OwnCloudNetworkFactory::userInfo() {
|
||||
QByteArray result_raw;
|
||||
|
||||
QList<QPair<QByteArray, QByteArray>> headers;
|
||||
headers << QPair<QByteArray, QByteArray>(HTTP_HEADERS_CONTENT_TYPE, OWNCLOUD_CONTENT_TYPE_JSON);
|
||||
headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword);
|
||||
|
||||
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_urlUser,
|
||||
qApp->settings()->value(GROUP(Feeds),
|
||||
SETTING(Feeds::UpdateTimeout)).toInt(),
|
||||
QByteArray(), QString(), result_raw,
|
||||
QByteArray(), result_raw,
|
||||
QNetworkAccessManager::GetOperation,
|
||||
true, m_authUsername, m_authPassword,
|
||||
true);
|
||||
headers);
|
||||
OwnCloudUserResponse user_response(QString::fromUtf8(result_raw));
|
||||
|
||||
if (network_reply.first != QNetworkReply::NoError) {
|
||||
@ -118,13 +122,17 @@ OwnCloudUserResponse OwnCloudNetworkFactory::userInfo() {
|
||||
|
||||
OwnCloudStatusResponse OwnCloudNetworkFactory::status() {
|
||||
QByteArray result_raw;
|
||||
|
||||
QList<QPair<QByteArray, QByteArray>> headers;
|
||||
headers << QPair<QByteArray, QByteArray>(HTTP_HEADERS_CONTENT_TYPE, OWNCLOUD_CONTENT_TYPE_JSON);
|
||||
headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword);
|
||||
|
||||
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_urlStatus,
|
||||
qApp->settings()->value(GROUP(Feeds),
|
||||
SETTING(Feeds::UpdateTimeout)).toInt(),
|
||||
QByteArray(), QString(), result_raw,
|
||||
QByteArray(), result_raw,
|
||||
QNetworkAccessManager::GetOperation,
|
||||
true, m_authUsername, m_authPassword,
|
||||
true);
|
||||
headers);
|
||||
OwnCloudStatusResponse status_response(QString::fromUtf8(result_raw));
|
||||
|
||||
if (network_reply.first != QNetworkReply::NoError) {
|
||||
|
@ -19,35 +19,30 @@
|
||||
#ifndef TTRSS_DEFINITIONS_H
|
||||
#define TTRSS_DEFINITIONS_H
|
||||
|
||||
#define MINIMAL_API_LEVEL 9
|
||||
#define CONTENT_TYPE "application/json; charset=utf-8"
|
||||
#define TTRSS_MINIMAL_API_LEVEL 9
|
||||
#define TTRSS_CONTENT_TYPE_JSON "application/json; charset=utf-8"
|
||||
|
||||
///
|
||||
/// Errors.
|
||||
///
|
||||
#define NOT_LOGGED_IN "NOT_LOGGED_IN" // Error when user needs to login before making an operation.
|
||||
#define UNKNOWN_METHOD "UNKNOWN_METHOD" // Given "op" is not recognized.
|
||||
#define INCORRECT_USAGE "INCORRECT_USAGE" // Given "op" was used with bad parameters.
|
||||
#define TTRSS_NOT_LOGGED_IN "NOT_LOGGED_IN" // Error when user needs to login before making an operation.
|
||||
#define TTRSS_UNKNOWN_METHOD "UNKNOWN_METHOD" // Given "op" is not recognized.
|
||||
#define TTRSS_INCORRECT_USAGE "INCORRECT_USAGE" // Given "op" was used with bad parameters.
|
||||
|
||||
// Limitations
|
||||
#define MAX_MESSAGES 200
|
||||
#define TTRSS_MAX_MESSAGES 200
|
||||
|
||||
// General return status codes.
|
||||
#define API_STATUS_OK 0
|
||||
#define API_STATUS_ERR 1
|
||||
#define STATUS_OK "OK"
|
||||
|
||||
#define CONTENT_NOT_LOADED -1
|
||||
#define TTRSS_API_STATUS_OK 0
|
||||
#define TTRSS_API_STATUS_ERR 1
|
||||
#define TTRSS_CONTENT_NOT_LOADED -1
|
||||
|
||||
// Login.
|
||||
#define API_DISABLED "API_DISABLED" // API is not enabled.
|
||||
#define LOGIN_ERROR "LOGIN_ERROR" // Incorrect password/username.
|
||||
|
||||
// Logout.
|
||||
#define LOGOUT_OK "OK"
|
||||
#define TTRSS_API_DISABLED "API_DISABLED" // API is not enabled.
|
||||
#define TTRSS_LOGIN_ERROR "LOGIN_ERROR" // Incorrect password/username.
|
||||
|
||||
// Get feed tree.
|
||||
#define GFT_TYPE_CATEGORY "category"
|
||||
#define TTRSS_GFT_TYPE_CATEGORY "category"
|
||||
|
||||
// Subscribe to feed.
|
||||
#define STF_UNKNOWN -1
|
||||
|
@ -35,7 +35,7 @@ FormEditTtRssAccount::FormEditTtRssAccount(QWidget* parent)
|
||||
m_ui->m_lblTestResult->label()->setWordWrap(true);
|
||||
m_ui->m_lblServerSideUpdateInformation->setText(tr("Leaving this option on causes that updates "
|
||||
"of feeds will be probably much slower and may time-out often."));
|
||||
m_ui->m_lblDescription->setText(tr("Note that at least API level %1 is required.").arg(MINIMAL_API_LEVEL));
|
||||
m_ui->m_lblDescription->setText(tr("Note that at least API level %1 is required.").arg(TTRSS_MINIMAL_API_LEVEL));
|
||||
m_ui->m_txtHttpUsername->lineEdit()->setPlaceholderText(tr("HTTP authentication username"));
|
||||
m_ui->m_txtHttpPassword->lineEdit()->setPlaceholderText(tr("HTTP authentication password"));
|
||||
m_ui->m_txtPassword->lineEdit()->setPlaceholderText(tr("Password for your TT-RSS account"));
|
||||
@ -130,12 +130,12 @@ void FormEditTtRssAccount::performTest() {
|
||||
if (result.hasError()) {
|
||||
QString error = result.error();
|
||||
|
||||
if (error == API_DISABLED) {
|
||||
if (error == TTRSS_API_DISABLED) {
|
||||
m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error,
|
||||
tr("API access on selected server is not enabled."),
|
||||
tr("API access on selected server is not enabled."));
|
||||
}
|
||||
else if (error == LOGIN_ERROR) {
|
||||
else if (error == TTRSS_LOGIN_ERROR) {
|
||||
m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error,
|
||||
tr("Entered credentials are incorrect."),
|
||||
tr("Entered credentials are incorrect."));
|
||||
@ -146,7 +146,7 @@ void FormEditTtRssAccount::performTest() {
|
||||
tr("Other error occurred, contact developers."));
|
||||
}
|
||||
}
|
||||
else if (result.apiLevel() < MINIMAL_API_LEVEL) {
|
||||
else if (result.apiLevel() < TTRSS_MINIMAL_API_LEVEL) {
|
||||
m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error,
|
||||
tr(
|
||||
"Selected Tiny Tiny RSS server is running unsupported version of API (%1). At least API level %2 is required.").arg(
|
||||
@ -156,7 +156,7 @@ void FormEditTtRssAccount::performTest() {
|
||||
apiLevel()),
|
||||
QString
|
||||
::
|
||||
number(MINIMAL_API_LEVEL)),
|
||||
number(TTRSS_MINIMAL_API_LEVEL)),
|
||||
tr("Selected Tiny Tiny RSS server is running unsupported version of API."));
|
||||
}
|
||||
else {
|
||||
@ -169,7 +169,7 @@ void FormEditTtRssAccount::performTest() {
|
||||
apiLevel()),
|
||||
QString
|
||||
::
|
||||
number(MINIMAL_API_LEVEL)),
|
||||
number(TTRSS_MINIMAL_API_LEVEL)),
|
||||
tr("Tiny Tiny RSS server is okay."));
|
||||
}
|
||||
}
|
||||
|
@ -95,13 +95,14 @@ TtRssLoginResponse TtRssNetworkFactory::login() {
|
||||
json["op"] = QSL("login");
|
||||
json["user"] = m_username;
|
||||
json["password"] = m_password;
|
||||
|
||||
QByteArray result_raw;
|
||||
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
|
||||
qApp->settings()->value(GROUP(Feeds),
|
||||
SETTING(
|
||||
Feeds::UpdateTimeout)).toInt(),
|
||||
QJsonDocument(json).toJson(QJsonDocument::Compact),
|
||||
CONTENT_TYPE,
|
||||
TTRSS_CONTENT_TYPE_JSON,
|
||||
result_raw,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
m_authIsUsed,
|
||||
@ -133,7 +134,7 @@ TtRssResponse TtRssNetworkFactory::logout() {
|
||||
SETTING(
|
||||
Feeds::UpdateTimeout)).toInt(),
|
||||
QJsonDocument(json).toJson(QJsonDocument::Compact),
|
||||
CONTENT_TYPE,
|
||||
TTRSS_CONTENT_TYPE_JSON,
|
||||
result_raw,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
m_authIsUsed,
|
||||
@ -168,7 +169,7 @@ TtRssGetFeedsCategoriesResponse TtRssNetworkFactory::getFeedsCategories() {
|
||||
QByteArray result_raw;
|
||||
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout,
|
||||
QJsonDocument(json).toJson(QJsonDocument::Compact),
|
||||
CONTENT_TYPE, result_raw,
|
||||
TTRSS_CONTENT_TYPE_JSON, result_raw,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
m_authIsUsed, m_authUsername, m_authPassword);
|
||||
TtRssGetFeedsCategoriesResponse result(QString::fromUtf8(result_raw));
|
||||
@ -178,7 +179,7 @@ TtRssGetFeedsCategoriesResponse TtRssNetworkFactory::getFeedsCategories() {
|
||||
login();
|
||||
json["sid"] = m_sessionId;
|
||||
network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact),
|
||||
CONTENT_TYPE, result_raw,
|
||||
TTRSS_CONTENT_TYPE_JSON, result_raw,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
m_authIsUsed, m_authUsername, m_authPassword);
|
||||
result = TtRssGetFeedsCategoriesResponse(QString::fromUtf8(result_raw));
|
||||
@ -210,7 +211,7 @@ TtRssGetHeadlinesResponse TtRssNetworkFactory::getHeadlines(int feed_id, int lim
|
||||
QByteArray result_raw;
|
||||
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout,
|
||||
QJsonDocument(json).toJson(QJsonDocument::Compact),
|
||||
CONTENT_TYPE, result_raw,
|
||||
TTRSS_CONTENT_TYPE_JSON, result_raw,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
m_authIsUsed, m_authUsername, m_authPassword);
|
||||
TtRssGetHeadlinesResponse result(QString::fromUtf8(result_raw));
|
||||
@ -220,7 +221,7 @@ TtRssGetHeadlinesResponse TtRssNetworkFactory::getHeadlines(int feed_id, int lim
|
||||
login();
|
||||
json["sid"] = m_sessionId;
|
||||
network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact),
|
||||
CONTENT_TYPE, result_raw,
|
||||
TTRSS_CONTENT_TYPE_JSON, result_raw,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
m_authIsUsed, m_authUsername, m_authPassword);
|
||||
result = TtRssGetHeadlinesResponse(QString::fromUtf8(result_raw));
|
||||
@ -250,7 +251,7 @@ TtRssUpdateArticleResponse TtRssNetworkFactory::updateArticles(const QStringList
|
||||
QByteArray result_raw;
|
||||
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout,
|
||||
QJsonDocument(json).toJson(QJsonDocument::Compact),
|
||||
CONTENT_TYPE, result_raw,
|
||||
TTRSS_CONTENT_TYPE_JSON, result_raw,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
m_authIsUsed, m_authUsername, m_authPassword);
|
||||
TtRssUpdateArticleResponse result(QString::fromUtf8(result_raw));
|
||||
@ -260,7 +261,7 @@ TtRssUpdateArticleResponse TtRssNetworkFactory::updateArticles(const QStringList
|
||||
login();
|
||||
json["sid"] = m_sessionId;
|
||||
network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact),
|
||||
CONTENT_TYPE, result_raw,
|
||||
TTRSS_CONTENT_TYPE_JSON, result_raw,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
m_authIsUsed, m_authUsername, m_authPassword);
|
||||
result = TtRssUpdateArticleResponse(QString::fromUtf8(result_raw));
|
||||
@ -293,7 +294,7 @@ TtRssSubscribeToFeedResponse TtRssNetworkFactory::subscribeToFeed(const QString&
|
||||
QByteArray result_raw;
|
||||
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout,
|
||||
QJsonDocument(json).toJson(QJsonDocument::Compact),
|
||||
CONTENT_TYPE, result_raw,
|
||||
TTRSS_CONTENT_TYPE_JSON, result_raw,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
m_authIsUsed, m_authUsername, m_authPassword);
|
||||
TtRssSubscribeToFeedResponse result(QString::fromUtf8(result_raw));
|
||||
@ -303,7 +304,7 @@ TtRssSubscribeToFeedResponse TtRssNetworkFactory::subscribeToFeed(const QString&
|
||||
login();
|
||||
json["sid"] = m_sessionId;
|
||||
network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact),
|
||||
CONTENT_TYPE, result_raw,
|
||||
TTRSS_CONTENT_TYPE_JSON, result_raw,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
m_authIsUsed, m_authUsername, m_authPassword);
|
||||
result = TtRssSubscribeToFeedResponse(QString::fromUtf8(result_raw));
|
||||
@ -328,7 +329,7 @@ TtRssUnsubscribeFeedResponse TtRssNetworkFactory::unsubscribeFeed(int feed_id) {
|
||||
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
|
||||
timeout,
|
||||
QJsonDocument(json).toJson(QJsonDocument::Compact),
|
||||
CONTENT_TYPE,
|
||||
TTRSS_CONTENT_TYPE_JSON,
|
||||
result_raw,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
m_authIsUsed,
|
||||
@ -341,7 +342,7 @@ TtRssUnsubscribeFeedResponse TtRssNetworkFactory::unsubscribeFeed(int feed_id) {
|
||||
login();
|
||||
json["sid"] = m_sessionId;
|
||||
network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact),
|
||||
CONTENT_TYPE, result_raw,
|
||||
TTRSS_CONTENT_TYPE_JSON, result_raw,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
m_authIsUsed, m_authUsername, m_authPassword);
|
||||
result = TtRssUnsubscribeFeedResponse(QString::fromUtf8(result_raw));
|
||||
@ -399,7 +400,7 @@ bool TtRssResponse::isLoaded() const {
|
||||
|
||||
int TtRssResponse::seq() const {
|
||||
if (!isLoaded()) {
|
||||
return CONTENT_NOT_LOADED;
|
||||
return TTRSS_CONTENT_NOT_LOADED;
|
||||
}
|
||||
else {
|
||||
return m_rawContent["seq"].toInt();
|
||||
@ -408,7 +409,7 @@ int TtRssResponse::seq() const {
|
||||
|
||||
int TtRssResponse::status() const {
|
||||
if (!isLoaded()) {
|
||||
return CONTENT_NOT_LOADED;
|
||||
return TTRSS_CONTENT_NOT_LOADED;
|
||||
}
|
||||
else {
|
||||
return m_rawContent["status"].toInt();
|
||||
@ -416,7 +417,7 @@ int TtRssResponse::status() const {
|
||||
}
|
||||
|
||||
bool TtRssResponse::isNotLoggedIn() const {
|
||||
return status() == API_STATUS_ERR && hasError() && error() == NOT_LOGGED_IN;
|
||||
return status() == TTRSS_API_STATUS_ERR && hasError() && error() == TTRSS_NOT_LOGGED_IN;
|
||||
}
|
||||
|
||||
QString TtRssResponse::toString() const {
|
||||
@ -429,7 +430,7 @@ TtRssLoginResponse::~TtRssLoginResponse() {}
|
||||
|
||||
int TtRssLoginResponse::apiLevel() const {
|
||||
if (!isLoaded()) {
|
||||
return CONTENT_NOT_LOADED;
|
||||
return TTRSS_CONTENT_NOT_LOADED;
|
||||
}
|
||||
else {
|
||||
return m_rawContent["content"].toObject()["api_level"].toInt();
|
||||
@ -474,7 +475,7 @@ RootItem* TtRssGetFeedsCategoriesResponse::feedsCategories(bool obtain_icons, QS
|
||||
base_address.chop(4);
|
||||
qDebug("TT-RSS: Chopped base address to '%s' to get feed icons.", qPrintable(base_address));
|
||||
|
||||
if (status() == API_STATUS_OK) {
|
||||
if (status() == TTRSS_API_STATUS_OK) {
|
||||
// We have data, construct object tree according to data.
|
||||
QJsonArray items_to_process = m_rawContent["content"].toObject()["categories"].toObject()["items"].toArray();
|
||||
|
||||
@ -489,7 +490,7 @@ RootItem* TtRssGetFeedsCategoriesResponse::feedsCategories(bool obtain_icons, QS
|
||||
RootItem* act_parent = pair.first;
|
||||
QJsonObject item = pair.second.toObject();
|
||||
int item_id = item["bare_id"].toInt();
|
||||
bool is_category = item.contains("type") && item["type"].toString() == GFT_TYPE_CATEGORY;
|
||||
bool is_category = item.contains("type") && item["type"].toString() == TTRSS_GFT_TYPE_CATEGORY;
|
||||
|
||||
if (item_id >= 0) {
|
||||
if (is_category) {
|
||||
|
@ -86,7 +86,7 @@ bool TtRssFeed::editItself(TtRssFeed* new_feed_data) {
|
||||
QList<Message> TtRssFeed::obtainNewMessages(bool* error_during_obtaining) {
|
||||
QList<Message> messages;
|
||||
int newly_added_messages = 0;
|
||||
int limit = MAX_MESSAGES;
|
||||
int limit = TTRSS_MAX_MESSAGES;
|
||||
int skip = 0;
|
||||
|
||||
do {
|
||||
|
@ -37,7 +37,7 @@ QString TtRssServiceEntryPoint::description() const {
|
||||
return QObject::tr("This service offers integration with Tiny Tiny RSS.\n\n"
|
||||
"Tiny Tiny RSS is an open source web-based news feed (RSS/Atom) reader and aggregator, "
|
||||
"designed to allow you to read news from any location, while feeling as close to a real "
|
||||
"desktop application as possible.\n\nAt least API level %1 is required.").arg(MINIMAL_API_LEVEL);
|
||||
"desktop application as possible.\n\nAt least API level %1 is required.").arg(TTRSS_MINIMAL_API_LEVEL);
|
||||
}
|
||||
|
||||
QString TtRssServiceEntryPoint::author() const {
|
||||
|
Loading…
x
Reference in New Issue
Block a user