7#include "atticaprovider_p.h"
9#include "commentsmodel.h"
12#include "tagsfilterchecker.h"
15#include <KLocalizedString>
17#include <QDomDocument>
18#include <knewstuffcore_debug.h>
20#include <attica/accountbalance.h>
21#include <attica/config.h>
22#include <attica/content.h>
23#include <attica/downloaditem.h>
24#include <attica/listjob.h>
25#include <attica/person.h>
26#include <attica/provider.h>
27#include <attica/providermanager.h>
29#include "atticarequester_p.h"
35AtticaProvider::AtticaProvider(
const QStringList &categories,
const QString &additionalAgentInformation)
39 for (
const QString &category : categories) {
43 connect(&m_providerManager, &ProviderManager::providerAdded,
this, [
this, additionalAgentInformation](
const Attica::Provider &provider) {
44 providerLoaded(provider);
45 m_provider.setAdditionalAgentInformation(additionalAgentInformation);
47 connect(&m_providerManager, &ProviderManager::authenticationCredentialsMissing,
this, &AtticaProvider::onAuthenticationCredentialsMissing);
54 for (
const QString &category : categories) {
57 providerLoaded(provider);
58 m_provider.setAdditionalAgentInformation(additionalAgentInformation);
61QString AtticaProvider::id()
const
66void AtticaProvider::onAuthenticationCredentialsMissing(
const Attica::Provider &)
68 qCDebug(KNEWSTUFFCORE) <<
"Authentication missing!";
72bool AtticaProvider::setProviderXML(
const QDomElement &xmldata)
80 qCDebug(KNEWSTUFFCORE) <<
"setting provider xml" << doc.toString();
83 m_providerManager.addProviderFromXml(doc.toString());
85 if (!m_providerManager.providers().isEmpty()) {
86 qCDebug(KNEWSTUFFCORE) <<
"base url of attica provider:" << m_providerManager.providers().constLast().baseUrl().toString();
88 qCCritical(KNEWSTUFFCORE) <<
"Could not load provider.";
96 mCachedEntries = cachedEntries;
101 setName(provider.
name());
102 setIcon(provider.
icon());
103 qCDebug(KNEWSTUFFCORE) <<
"Added provider: " << provider.
name();
105 m_provider = provider;
110 connect(job, &BaseJob::finished,
this, &AtticaProvider::listOfCategoriesLoaded);
116 if (!jobSuccess(listJob)) {
120 qCDebug(KNEWSTUFFCORE) <<
"loading categories: " << mCategoryMap.keys();
123 const Category::List categoryList = job->itemList();
126 for (
const Category &category : categoryList) {
127 if (mCategoryMap.contains(
category.name())) {
128 qCDebug(KNEWSTUFFCORE) <<
"Adding category: " <<
category.name() <<
category.displayName();
130 if (mCategoryMap.contains(
category.name()) && !mCategoryMap.value(
category.name()).isValid()) {
131 mCategoryMap.replace(
category.name(), category);
133 mCategoryMap.insert(
category.name(), category);
136 CategoryMetadata categoryMetadata;
137 categoryMetadata.id =
category.id();
138 categoryMetadata.name =
category.name();
139 categoryMetadata.displayName =
category.displayName();
140 categoryMetadataList << categoryMetadata;
143 std::sort(categoryMetadataList.
begin(),
144 categoryMetadataList.
end(),
145 [](
const AtticaProvider::CategoryMetadata &i,
const AtticaProvider::CategoryMetadata &j) ->
bool {
146 const QString a(i.displayName.isEmpty() ? i.name : i.displayName);
147 const QString b(j.displayName.isEmpty() ? j.name : j.displayName);
149 return (QCollator().compare(a, b) < 0);
152 bool correct =
false;
153 for (
auto it = mCategoryMap.cbegin(), itEnd = mCategoryMap.cend(); it != itEnd; ++it) {
154 if (!it.value().isValid()) {
155 qCWarning(KNEWSTUFFCORE) <<
"Could not find category" << it.key();
163 Q_EMIT providerInitialized(
this);
164 Q_EMIT categoriesMetadataLoded(categoryMetadataList);
166 Q_EMIT signalErrorCode(KNSCore::ErrorCode::ConfigFileError,
i18n(
"All categories are missing"),
QVariant());
170bool AtticaProvider::isInitialized()
const
177 auto requester =
new AtticaRequester(request,
this,
this);
178 connect(requester, &AtticaRequester::entryDetailsLoaded,
this, &AtticaProvider::entryDetailsLoaded);
180 Q_EMIT loadingFinished(requester->request(), list);
182 connect(requester, &AtticaRequester::loadingFailed,
this, [
this, requester] {
183 Q_EMIT loadingFailed(requester->request());
191 connect(job, &BaseJob::finished,
this, [
this, entry] {
192 Q_EMIT entryDetailsLoaded(entry);
197void AtticaProvider::loadPayloadLink(
const KNSCore::Entry &entry,
int linkId)
202 if (desc.hasPrice()) {
205 connect(job, &BaseJob::finished,
this, &AtticaProvider::accountBalanceLoaded);
206 mDownloadLinkJobs[job] = qMakePair(entry, linkId);
209 qCDebug(KNEWSTUFFCORE) <<
"get account balance";
212 connect(job, &BaseJob::finished,
this, &AtticaProvider::downloadItemLoaded);
213 mDownloadLinkJobs[job] = qMakePair(entry, linkId);
216 qCDebug(KNEWSTUFFCORE) <<
" link for " << entry.uniqueId();
220void AtticaProvider::loadComments(
const Entry &entry,
int commentsPerPage,
int page)
222 ListJob<Attica::Comment> *job = m_provider.requestComments(Attica::Comment::ContentComment, entry.uniqueId(), QStringLiteral(
"0"), page, commentsPerPage);
223 connect(job, &BaseJob::finished,
this, &AtticaProvider::loadedComments);
231 qCDebug(KNEWSTUFFCORE) <<
"Appending comment with id" << comment.id() <<
", which has" << comment.childCount() <<
"children";
232 auto knsComment = std::make_shared<KNSCore::Comment>();
233 knsComment->id = comment.id();
234 knsComment->subject = comment.subject();
235 knsComment->text = comment.text();
236 knsComment->childCount = comment.childCount();
237 knsComment->username = comment.user();
238 knsComment->date = comment.date();
239 knsComment->score = comment.score();
240 knsComment->parent = parent;
241 knsComments << knsComment;
242 if (comment.childCount() > 0) {
243 qCDebug(KNEWSTUFFCORE) <<
"Getting more comments, as this one has children, and we currently have this number of comments:" << knsComments.
count();
244 knsComments << getCommentsList(comment.children(), knsComment);
245 qCDebug(KNEWSTUFFCORE) <<
"After getting the children, we now have the following number of comments:" << knsComments.
count();
253 if (!jobSuccess(baseJob)) {
258 Attica::Comment::List
comments = job->itemList();
261 Q_EMIT commentsLoaded(receivedComments);
264void AtticaProvider::loadPerson(
const QString &username)
266 if (m_provider.hasPersonService()) {
269 connect(job, &BaseJob::finished,
this, &AtticaProvider::loadedPerson);
276 if (!jobSuccess(baseJob)) {
283 auto author = std::make_shared<KNSCore::Author>();
286 author->setName(QStringLiteral(
"%1 %2").arg(person.firstName(), person.lastName()).trimmed());
287 author->setHomepage(person.homepage());
288 author->setProfilepage(person.extendedAttribute(QStringLiteral(
"profilepage")));
289 author->setAvatarUrl(person.avatarUrl());
290 author->setDescription(person.extendedAttribute(QStringLiteral(
"description")));
291 Q_EMIT personLoaded(author);
294void AtticaProvider::loadBasics()
297 connect(configJob, &BaseJob::finished,
this, &AtticaProvider::loadedConfig);
303 if (jobSuccess(baseJob)) {
306 setVersion(config.version());
307 setSupportsSsl(config.ssl());
308 setContactEmail(config.contact());
309 QString protocol{QStringLiteral(
"http")};
311 protocol = QStringLiteral(
"https");
316 setWebsite(
QUrl(config.website()));
321 setHost(
QUrl(config.host()));
330 if (!jobSuccess(baseJob)) {
337 QPair<Entry, int> pair = mDownloadLinkJobs.take(job);
338 Entry entry(pair.first);
339 Content content = mCachedContent.value(entry.uniqueId());
341 qCDebug(KNEWSTUFFCORE) <<
"Your balance is greater than the price." << content.
downloadUrlDescription(pair.second).priceAmount()
342 <<
" balance: " << item.balance();
344 question.setEntry(entry);
345 question.setQuestion(
i18nc(
"the price of a download item, parameter 1 is the currency, 2 is the price",
346 "This item costs %1 %2.\nDo you want to buy it?",
349 if (question.ask() == Question::YesResponse) {
351 connect(job, &BaseJob::finished,
this, &AtticaProvider::downloadItemLoaded);
352 mDownloadLinkJobs[job] = qMakePair(entry, pair.second);
358 qCDebug(KNEWSTUFFCORE) <<
"You don't have enough money on your account!" << content.
downloadUrlDescription(0).priceAmount()
359 <<
" balance: " << item.balance();
360 Q_EMIT signalInformation(
i18n(
"Your account balance is too low:\nYour balance: %1\nPrice: %2",
366void AtticaProvider::downloadItemLoaded(
BaseJob *baseJob)
368 if (!jobSuccess(baseJob)) {
375 Entry entry = mDownloadLinkJobs.take(job).first;
376 entry.setPayload(
QString(item.url().toString()));
377 Q_EMIT payloadLinkLoaded(entry);
380void AtticaProvider::vote(
const Entry &entry, uint rating)
382 PostJob *job = m_provider.voteForContent(entry.uniqueId(), rating);
383 connect(job, &BaseJob::finished,
this, &AtticaProvider::votingFinished);
389 if (!jobSuccess(job)) {
392 Q_EMIT signalInformation(
i18nc(
"voting for an item (good/bad)",
"Your vote was recorded."));
395void AtticaProvider::becomeFan(
const Entry &entry)
397 PostJob *job = m_provider.becomeFan(entry.uniqueId());
398 connect(job, &BaseJob::finished,
this, &AtticaProvider::becomeFanFinished);
404 if (!jobSuccess(job)) {
407 Q_EMIT signalInformation(
i18n(
"You are now a fan."));
412 if (job->metadata().error() == Attica::Metadata::NoError) {
415 qCDebug(KNEWSTUFFCORE) <<
"job error: " << job->metadata().error() <<
" status code: " << job->metadata().statusCode() << job->metadata().message();
417 if (job->metadata().error() == Attica::Metadata::NetworkError) {
418 if (job->metadata().statusCode() == 503) {
420 static const QByteArray retryAfterKey{
"Retry-After"};
422 if (headerPair.first == retryAfterKey) {
433 static const KFormat formatter;
434 Q_EMIT signalErrorCode(KNSCore::ErrorCode::TryAgainLaterError,
435 i18n(
"The service is currently undergoing maintenance and is expected to be back in %1.",
439 Q_EMIT signalErrorCode(KNSCore::ErrorCode::NetworkError,
440 i18n(
"Network error %1: %2", job->metadata().statusCode(), job->metadata().statusString()),
441 job->metadata().statusCode());
444 if (job->metadata().error() == Attica::Metadata::OcsError) {
445 if (job->metadata().statusCode() == 200) {
446 Q_EMIT signalErrorCode(KNSCore::ErrorCode::OcsError,
447 i18n(
"Too many requests to server. Please try again in a few minutes."),
448 job->metadata().statusCode());
449 }
else if (job->metadata().statusCode() == 405) {
450 Q_EMIT signalErrorCode(KNSCore::ErrorCode::OcsError,
451 i18n(
"The Open Collaboration Services instance %1 does not support the attempted function.",
name()),
452 job->metadata().statusCode());
454 Q_EMIT signalErrorCode(KNSCore::ErrorCode::OcsError,
455 i18n(
"Unknown Open Collaboration Service API error. (%1)", job->metadata().statusCode()),
456 job->metadata().statusCode());
460 if (
auto searchRequestVar = job->
property(
"searchRequest"); searchRequestVar.
isValid()) {
461 SearchRequest req = searchRequestVar.value<SearchRequest>();
462 Q_EMIT loadingFailed(req);
469#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
used to keep track of a search