7#include "atticaprovider_p.h"
9#include "commentsmodel.h"
12#include "tagsfilterchecker.h"
15#include <KLocalizedString>
17#include <QDomDocument>
19#include <knewstuffcore_debug.h>
21#include <attica/accountbalance.h>
22#include <attica/config.h>
23#include <attica/content.h>
24#include <attica/downloaditem.h>
25#include <attica/listjob.h>
26#include <attica/person.h>
27#include <attica/provider.h>
28#include <attica/providermanager.h>
30#include "atticarequester_p.h"
31#include "categorymetadata.h"
32#include "categorymetadata_p.h"
38AtticaProvider::AtticaProvider(
const QStringList &categories,
const QString &additionalAgentInformation)
42 for (
const QString &category : categories) {
46 connect(&m_providerManager, &ProviderManager::providerAdded,
this, [
this, additionalAgentInformation](
const Attica::Provider &provider) {
47 providerLoaded(provider);
48 m_provider.setAdditionalAgentInformation(additionalAgentInformation);
50 connect(&m_providerManager, &ProviderManager::authenticationCredentialsMissing,
this, &AtticaProvider::onAuthenticationCredentialsMissing);
57 for (
const QString &category : categories) {
60 providerLoaded(provider);
61 m_provider.setAdditionalAgentInformation(additionalAgentInformation);
64QString AtticaProvider::id()
const
69void AtticaProvider::onAuthenticationCredentialsMissing(
const Attica::Provider &)
71 qCDebug(KNEWSTUFFCORE) <<
"Authentication missing!";
75bool AtticaProvider::setProviderXML(
const QDomElement &xmldata)
83 qCDebug(KNEWSTUFFCORE) <<
"setting provider xml" << doc.toString();
86 m_providerManager.addProviderFromXml(doc.toString());
88 if (!m_providerManager.providers().isEmpty()) {
89 qCDebug(KNEWSTUFFCORE) <<
"base url of attica provider:" << m_providerManager.providers().constLast().baseUrl().toString();
91 qCCritical(KNEWSTUFFCORE) <<
"Could not load provider.";
99 mCachedEntries = cachedEntries;
104 m_name = provider.
name();
105 m_icon = provider.
icon();
106 qCDebug(KNEWSTUFFCORE) <<
"Added provider: " << provider.
name();
108 m_provider = provider;
113 connect(job, &BaseJob::finished,
this, &AtticaProvider::listOfCategoriesLoaded);
119 if (!jobSuccess(listJob)) {
123 qCDebug(KNEWSTUFFCORE) <<
"loading categories: " << mCategoryMap.keys();
126 const Category::List categoryList = job->itemList();
129 for (
const Category &category : categoryList) {
130 if (mCategoryMap.contains(
category.name())) {
131 qCDebug(KNEWSTUFFCORE) <<
"Adding category: " <<
category.name() <<
category.displayName();
133 if (mCategoryMap.contains(
category.name()) && !mCategoryMap.value(
category.name()).isValid()) {
134 mCategoryMap.replace(
category.name(), category);
136 mCategoryMap.insert(
category.name(), category);
139 categoryMetadataList << CategoryMetadata(
new CategoryMetadataPrivate{
142 .displayName =
category.displayName(),
146 std::sort(categoryMetadataList.
begin(), categoryMetadataList.
end(), [](
const auto &i,
const auto &j) ->
bool {
147 const QString a(i.displayName().isEmpty() ? i.name() : i.displayName());
148 const QString b(j.displayName().isEmpty() ? j.name() : j.displayName());
150 return (QCollator().compare(a, b) < 0);
153 bool correct =
false;
154 for (
auto it = mCategoryMap.cbegin(), itEnd = mCategoryMap.cend(); it != itEnd; ++it) {
155 if (!it.value().isValid()) {
156 qCWarning(KNEWSTUFFCORE) <<
"Could not find category" << it.key();
164 Q_EMIT providerInitialized(
this);
165 Q_EMIT categoriesMetadataLoaded(categoryMetadataList);
167 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ConfigFileError,
i18n(
"All categories are missing"),
QVariant());
171bool AtticaProvider::isInitialized()
const
178 auto requester =
new AtticaRequester(request,
this,
this);
179 connect(requester, &AtticaRequester::entryDetailsLoaded,
this, &AtticaProvider::entryDetailsLoaded);
181 Q_EMIT entriesLoaded(requester->request(), list);
183 connect(requester, &AtticaRequester::loadingDone,
this, [
this, requester] {
184 Q_EMIT loadingDone(requester->request());
186 connect(requester, &AtticaRequester::loadingFailed,
this, [
this, requester] {
187 Q_EMIT loadingFailed(requester->request());
195 connect(job, &BaseJob::finished,
this, [
this, entry] {
196 Q_EMIT entryDetailsLoaded(entry);
201void AtticaProvider::loadPayloadLink(
const KNSCore::Entry &entry,
int linkId)
206 if (desc.hasPrice()) {
209 connect(job, &BaseJob::finished,
this, &AtticaProvider::accountBalanceLoaded);
210 mDownloadLinkJobs[job] = qMakePair(entry, linkId);
213 qCDebug(KNEWSTUFFCORE) <<
"get account balance";
216 connect(job, &BaseJob::finished,
this, &AtticaProvider::downloadItemLoaded);
217 mDownloadLinkJobs[job] = qMakePair(entry, linkId);
220 qCDebug(KNEWSTUFFCORE) <<
" link for " << entry.uniqueId();
224void AtticaProvider::loadComments(
const Entry &entry,
int commentsPerPage,
int page)
226 ListJob<Attica::Comment> *job = m_provider.requestComments(Attica::Comment::ContentComment, entry.uniqueId(), QStringLiteral(
"0"), page, commentsPerPage);
227 connect(job, &BaseJob::finished,
this, &AtticaProvider::loadedComments);
235 qCDebug(KNEWSTUFFCORE) <<
"Appending comment with id" << comment.id() <<
", which has" << comment.childCount() <<
"children";
236 auto knsComment = std::make_shared<KNSCore::Comment>();
237 knsComment->id = comment.id();
238 knsComment->subject = comment.subject();
239 knsComment->text = comment.text();
240 knsComment->childCount = comment.childCount();
241 knsComment->username = comment.user();
242 knsComment->date = comment.date();
243 knsComment->score = comment.score();
244 knsComment->parent = parent;
245 knsComments << knsComment;
246 if (comment.childCount() > 0) {
247 qCDebug(KNEWSTUFFCORE) <<
"Getting more comments, as this one has children, and we currently have this number of comments:" << knsComments.
count();
248 knsComments << getCommentsList(comment.children(), knsComment);
249 qCDebug(KNEWSTUFFCORE) <<
"After getting the children, we now have the following number of comments:" << knsComments.
count();
257 if (!jobSuccess(baseJob)) {
262 Attica::Comment::List
comments = job->itemList();
265 Q_EMIT commentsLoaded(receivedComments);
268void AtticaProvider::loadPerson(
const QString &username)
270 if (m_provider.hasPersonService()) {
273 connect(job, &BaseJob::finished,
this, &AtticaProvider::loadedPerson);
280 if (!jobSuccess(baseJob)) {
287 auto author = std::make_shared<KNSCore::Author>();
290 author->setName(QStringLiteral(
"%1 %2").arg(person.firstName(), person.lastName()).trimmed());
291 author->setHomepage(person.homepage());
292 author->setProfilepage(person.extendedAttribute(QStringLiteral(
"profilepage")));
293 author->setAvatarUrl(person.avatarUrl());
294 author->setDescription(person.extendedAttribute(QStringLiteral(
"description")));
295 Q_EMIT personLoaded(author);
300 if (!jobSuccess(baseJob)) {
306 m_version = config.version();
307 m_supportsSsl = config.ssl();
308 m_contactEmail = config.contact();
309 const auto protocol = [&config] {
310 QString protocol{QStringLiteral(
"http")};
312 protocol = QStringLiteral(
"https");
316 m_website = [&config, &protocol] {
320 return QUrl(config.website());
324 m_host = [&config, &protocol] {
326 return QUrl(config.host());
331 Q_EMIT basicsLoaded();
336 if (!jobSuccess(baseJob)) {
343 QPair<Entry, int> pair = mDownloadLinkJobs.take(job);
344 Entry entry(pair.first);
345 Content content = mCachedContent.value(entry.uniqueId());
347 qCDebug(KNEWSTUFFCORE) <<
"Your balance is greater than the price." << content.
downloadUrlDescription(pair.second).priceAmount()
348 <<
" balance: " << item.balance();
350 question.setEntry(entry);
351 question.setQuestion(
i18nc(
"the price of a download item, parameter 1 is the currency, 2 is the price",
352 "This item costs %1 %2.\nDo you want to buy it?",
355 if (question.ask() == Question::YesResponse) {
357 connect(job, &BaseJob::finished,
this, &AtticaProvider::downloadItemLoaded);
358 mDownloadLinkJobs[job] = qMakePair(entry, pair.second);
364 qCDebug(KNEWSTUFFCORE) <<
"You don't have enough money on your account!" << content.
downloadUrlDescription(0).priceAmount()
365 <<
" balance: " << item.balance();
366 Q_EMIT signalInformation(
i18n(
"Your account balance is too low:\nYour balance: %1\nPrice: %2",
372void AtticaProvider::downloadItemLoaded(
BaseJob *baseJob)
374 if (!jobSuccess(baseJob)) {
381 Entry entry = mDownloadLinkJobs.take(job).first;
382 entry.setPayload(
QString(item.url().toString()));
383 Q_EMIT payloadLinkLoaded(entry);
386void AtticaProvider::vote(
const Entry &entry, uint rating)
388 PostJob *job = m_provider.voteForContent(entry.uniqueId(), rating);
389 connect(job, &BaseJob::finished,
this, &AtticaProvider::votingFinished);
395 if (!jobSuccess(job)) {
398 Q_EMIT signalInformation(
i18nc(
"voting for an item (good/bad)",
"Your vote was recorded."));
401void AtticaProvider::becomeFan(
const Entry &entry)
403 PostJob *job = m_provider.becomeFan(entry.uniqueId());
404 connect(job, &BaseJob::finished,
this, &AtticaProvider::becomeFanFinished);
410 if (!jobSuccess(job)) {
413 Q_EMIT signalInformation(
i18n(
"You are now a fan."));
418 if (job->metadata().error() == Attica::Metadata::NoError) {
421 qCDebug(KNEWSTUFFCORE) <<
"job error: " << job->metadata().error() <<
" status code: " << job->metadata().statusCode() << job->metadata().message();
423 if (job->metadata().error() == Attica::Metadata::NetworkError) {
424 if (job->metadata().statusCode() == 503) {
426 static const QByteArray retryAfterKey{
"Retry-After"};
428 if (headerPair.first == retryAfterKey) {
439 static const KFormat formatter;
440 Q_EMIT signalErrorCode(KNSCore::ErrorCode::TryAgainLaterError,
441 i18n(
"The service is currently undergoing maintenance and is expected to be back in %1.",
445 Q_EMIT signalErrorCode(KNSCore::ErrorCode::NetworkError,
446 i18n(
"Network error %1: %2", job->metadata().statusCode(), job->metadata().statusString()),
447 job->metadata().statusCode());
450 if (job->metadata().error() == Attica::Metadata::OcsError) {
451 if (job->metadata().statusCode() == 200) {
452 Q_EMIT signalErrorCode(KNSCore::ErrorCode::OcsError,
453 i18n(
"Too many requests to server. Please try again in a few minutes."),
454 job->metadata().statusCode());
455 }
else if (job->metadata().statusCode() == 405) {
456 Q_EMIT signalErrorCode(KNSCore::ErrorCode::OcsError,
457 i18n(
"The Open Collaboration Services instance %1 does not support the attempted function.",
name()),
458 job->metadata().statusCode());
460 Q_EMIT signalErrorCode(KNSCore::ErrorCode::OcsError,
461 i18n(
"Unknown Open Collaboration Service API error. (%1)", job->metadata().statusCode()),
462 job->metadata().statusCode());
466 if (
auto searchRequestVar = job->
property(
"searchRequest"); searchRequestVar.
isValid()) {
467 auto req = searchRequestVar.value<SearchRequest>();
468 Q_EMIT loadingFailed(req);
473void AtticaProvider::updateOnFirstBasicsGet()
479 connect(configJob, &BaseJob::finished,
this, &AtticaProvider::loadedConfig);
485QString AtticaProvider::name()
const
490QUrl AtticaProvider::icon()
const
495QString AtticaProvider::version()
497 updateOnFirstBasicsGet();
501QUrl AtticaProvider::website()
503 updateOnFirstBasicsGet();
507QUrl AtticaProvider::host()
509 updateOnFirstBasicsGet();
513QString AtticaProvider::contactEmail()
515 updateOnFirstBasicsGet();
516 return m_contactEmail;
519bool AtticaProvider::supportsSsl()
521 updateOnFirstBasicsGet();
522 return m_supportsSsl;
527#include "moc_atticaprovider_p.cpp"
DownloadDescription downloadUrlDescription(int number) const
void setAdditionalAgentInformation(const QString &additionalInformation)
KNewStuff data entry container.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QString name(StandardAction id)
Category category(StandardShortcut id)
KEDUVOCDOCUMENT_EXPORT QStringList comments(const QString &language=QString())
qint64 currentMSecsSinceEpoch()
qint64 toMSecsSinceEpoch() const const
QString tagName() const const
QDomNode cloneNode(bool deep) const const
qsizetype count() const const
QVariant property(const char *name) const const
bool setProperty(const char *name, QVariant &&value)
QString number(double n, char format, int precision)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QString host(ComponentFormattingOptions options) const const
bool isValid() const const
QDateTime toDateTime() const const
QString toString() const const