KNewStuff
atticaprovider.cpp
Go to the documentation of this file.
00001 /* 00002 Copyright (c) 2009-2010 Frederik Gladhorn <gladhorn@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Lesser General Public 00006 License as published by the Free Software Foundation; either 00007 version 2.1 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Lesser General Public License for more details. 00013 00014 You should have received a copy of the GNU Lesser General Public 00015 License along with this library. If not, see <http://www.gnu.org/licenses/>. 00016 */ 00017 00018 #include "atticaprovider.h" 00019 00020 #include <kdebug.h> 00021 #include <klocale.h> 00022 #include <kio/job.h> 00023 #include <kmessagebox.h> 00024 00025 #include <attica/providermanager.h> 00026 #include <attica/provider.h> 00027 #include <attica/listjob.h> 00028 #include <attica/content.h> 00029 #include <attica/downloaditem.h> 00030 #include <attica/accountbalance.h> 00031 #include <attica/person.h> 00032 00033 using namespace Attica; 00034 00035 namespace KNS3 00036 { 00037 00038 AtticaProvider::AtticaProvider(const QStringList& categories) 00039 : mEntryJob(0) 00040 , mInitialized(false) 00041 { 00042 // init categories map with invalid categories 00043 foreach (const QString& category, categories) 00044 mCategoryMap.insert(category, Attica::Category()); 00045 00046 connect(&m_providerManager, SIGNAL(providerAdded(const Attica::Provider&)), SLOT(providerLoaded(const Attica::Provider&))); 00047 connect(&m_providerManager, SIGNAL(authenticationCredentialsMissing(const Provider&)), 00048 SLOT(authenticationCredentialsMissing(const Provider&))); 00049 } 00050 00051 AtticaProvider::AtticaProvider(const Attica::Provider& provider, const QStringList& categories) 00052 : mEntryJob(0) 00053 , mInitialized(false) 00054 { 00055 // init categories map with invalid categories 00056 foreach (const QString& category, categories) { 00057 mCategoryMap.insert(category, Attica::Category()); 00058 } 00059 providerLoaded(provider); 00060 } 00061 00062 QString AtticaProvider::id() const 00063 { 00064 return m_provider.baseUrl().toString(); 00065 } 00066 00067 void AtticaProvider::authenticationCredentialsMissing(const KNS3::Provider& ) 00068 { 00069 kDebug() << "Authentication missing!"; 00070 // FIXME Show autentication dialog 00071 } 00072 00073 bool AtticaProvider::setProviderXML(const QDomElement & xmldata) 00074 { 00075 if (xmldata.tagName() != "provider") 00076 return false; 00077 00078 // FIXME this is quite ugly, repackaging the xml into a string 00079 QDomDocument doc("temp"); 00080 kDebug(550) << "setting provider xml" << doc.toString(); 00081 00082 doc.appendChild(xmldata.cloneNode(true)); 00083 m_providerManager.addProviderFromXml(doc.toString()); 00084 00085 if (!m_providerManager.providers().isEmpty()) { 00086 kDebug() << "base url of attica provider:" << m_providerManager.providers().last().baseUrl().toString(); 00087 } else { 00088 kError() << "Could not load provider."; 00089 return false; 00090 } 00091 return true; 00092 } 00093 00094 void AtticaProvider::setCachedEntries(const KNS3::EntryInternal::List& cachedEntries) 00095 { 00096 mCachedEntries = cachedEntries; 00097 } 00098 00099 void AtticaProvider::providerLoaded(const Attica::Provider& provider) 00100 { 00101 mName = provider.name(); 00102 kDebug() << "Added provider: " << provider.name(); 00103 00104 m_provider = provider; 00105 00106 Attica::ListJob<Attica::Category>* job = m_provider.requestCategories(); 00107 connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(listOfCategoriesLoaded(Attica::BaseJob*))); 00108 job->start(); 00109 } 00110 00111 void AtticaProvider::listOfCategoriesLoaded(Attica::BaseJob* listJob) 00112 { 00113 if (!jobSuccess(listJob)) return; 00114 00115 kDebug() << "loading categories: " << mCategoryMap.keys(); 00116 00117 Attica::ListJob<Attica::Category>* job = static_cast<Attica::ListJob<Attica::Category>*>(listJob); 00118 Category::List categoryList = job->itemList(); 00119 00120 foreach(const Category& category, categoryList) { 00121 if (mCategoryMap.contains(category.name())) { 00122 kDebug() << "Adding category: " << category.name(); 00123 mCategoryMap[category.name()] = category; 00124 } 00125 } 00126 mInitialized = true; 00127 emit providerInitialized(this); 00128 } 00129 00130 bool AtticaProvider::isInitialized() const 00131 { 00132 return mInitialized; 00133 } 00134 00135 void AtticaProvider::loadEntries(const KNS3::Provider::SearchRequest& request) 00136 { 00137 if (mEntryJob) { 00138 mEntryJob->abort(); 00139 mEntryJob = 0; 00140 } 00141 00142 mCurrentRequest = request; 00143 if (request.sortMode == Installed) { 00144 if (request.page == 0) { 00145 emit loadingFinished(request, installedEntries()); 00146 } else { 00147 emit loadingFinished(request, EntryInternal::List()); 00148 } 00149 return; 00150 } 00151 00152 if (request.sortMode == Updates) { 00153 checkForUpdates(); 00154 return; 00155 } 00156 00157 Attica::Provider::SortMode sorting = atticaSortMode(request.sortMode); 00158 Attica::Category::List categoriesToSearch; 00159 00160 if (request.categories.isEmpty()) { 00161 // search in all categories 00162 categoriesToSearch = mCategoryMap.values(); 00163 } else { 00164 foreach (const QString& categoryName, request.categories) { 00165 categoriesToSearch.append(mCategoryMap.value(categoryName)); 00166 } 00167 } 00168 00169 ListJob<Content>* job = m_provider.searchContents(categoriesToSearch, request.searchTerm, sorting, request.page, request.pageSize); 00170 connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(categoryContentsLoaded(Attica::BaseJob*))); 00171 00172 mEntryJob = job; 00173 job->start(); 00174 } 00175 00176 void AtticaProvider::checkForUpdates() 00177 { 00178 foreach (const EntryInternal& e, mCachedEntries) { 00179 ItemJob<Content>* job = m_provider.requestContent(e.uniqueId()); 00180 connect(job, SIGNAL(finished(Attica::BaseJob*)), this, SLOT(detailsLoaded(Attica::BaseJob*))); 00181 m_updateJobs.insert(job); 00182 job->start(); 00183 kDebug() << "Checking for update: " << e.name(); 00184 } 00185 } 00186 00187 void AtticaProvider::loadEntryDetails(const KNS3::EntryInternal& entry) 00188 { 00189 ItemJob<Content>* job = m_provider.requestContent(entry.uniqueId()); 00190 connect(job, SIGNAL(finished(Attica::BaseJob*)), this, SLOT(detailsLoaded(Attica::BaseJob*))); 00191 job->start(); 00192 } 00193 00194 void AtticaProvider::detailsLoaded(BaseJob* job) 00195 { 00196 if (jobSuccess(job)) { 00197 ItemJob<Content>* contentJob = static_cast<ItemJob<Content>*>(job); 00198 Content content = contentJob->result(); 00199 EntryInternal entry = entryFromAtticaContent(content); 00200 emit entryDetailsLoaded(entry); 00201 kDebug() << "check update finished: " << entry.name(); 00202 } 00203 00204 if (m_updateJobs.remove(job) && m_updateJobs.isEmpty()) { 00205 kDebug() << "check update finished."; 00206 QList<EntryInternal> updatable; 00207 foreach(const EntryInternal& entry, mCachedEntries) { 00208 if (entry.status() == Entry::Updateable) { 00209 updatable.append(entry); 00210 } 00211 } 00212 emit loadingFinished(mCurrentRequest, updatable); 00213 } 00214 } 00215 00216 void AtticaProvider::categoryContentsLoaded(BaseJob* job) 00217 { 00218 if (!jobSuccess(job)) return; 00219 00220 ListJob<Content>* listJob = static_cast<ListJob<Content>*>(job); 00221 Content::List contents = listJob->itemList(); 00222 00223 EntryInternal::List entries; 00224 Q_FOREACH(const Content &content, contents) { 00225 mCachedContent.insert(content.id(), content); 00226 entries.append(entryFromAtticaContent(content)); 00227 } 00228 00229 kDebug() << "loaded: " << mCurrentRequest.hashForRequest() << " count: " << entries.size(); 00230 emit loadingFinished(mCurrentRequest, entries); 00231 mEntryJob = 0; 00232 } 00233 00234 Attica::Provider::SortMode AtticaProvider::atticaSortMode(const SortMode& sortMode) 00235 { 00236 if (sortMode == Newest) { 00237 return Attica::Provider::Newest; 00238 } 00239 if (sortMode == Alphabetical) { 00240 return Attica::Provider::Alphabetical; 00241 } 00242 if (sortMode == Downloads) { 00243 return Attica::Provider::Downloads; 00244 } 00245 return Attica::Provider::Rating; 00246 } 00247 00248 void AtticaProvider::loadPayloadLink(const KNS3::EntryInternal& entry, int linkId) 00249 { 00250 Attica::Content content = mCachedContent.value(entry.uniqueId()); 00251 DownloadDescription desc = content.downloadUrlDescription(linkId); 00252 00253 if (desc.hasPrice()) { 00254 // Ask for balance, then show information... 00255 ItemJob<AccountBalance>* job = m_provider.requestAccountBalance(); 00256 connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(accountBalanceLoaded(Attica::BaseJob*))); 00257 mDownloadLinkJobs[job] = qMakePair(entry, linkId); 00258 job->start(); 00259 00260 kDebug() << "get account balance"; 00261 } else { 00262 ItemJob<DownloadItem>* job = m_provider.downloadLink(entry.uniqueId(), QString::number(linkId)); 00263 connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(downloadItemLoaded(Attica::BaseJob*))); 00264 mDownloadLinkJobs[job] = qMakePair(entry, linkId); 00265 job->start(); 00266 00267 kDebug() << " link for " << entry.uniqueId(); 00268 } 00269 } 00270 00271 void AtticaProvider::accountBalanceLoaded(Attica::BaseJob* baseJob) 00272 { 00273 if (!jobSuccess(baseJob)) return; 00274 00275 ItemJob<AccountBalance>* job = static_cast<ItemJob<AccountBalance>*>(baseJob); 00276 AccountBalance item = job->result(); 00277 00278 QPair<EntryInternal, int> pair = mDownloadLinkJobs.take(job); 00279 EntryInternal entry(pair.first); 00280 Content content = mCachedContent.value(entry.uniqueId()); 00281 if (content.downloadUrlDescription(pair.second).priceAmount() < item.balance()) { 00282 kDebug() << "Your balance is greather than the price." 00283 << content.downloadUrlDescription(pair.second).priceAmount() << " balance: " << item.balance(); 00284 if (KMessageBox::questionYesNo(0, 00285 i18nc("the price of a download item, parameter 1 is the currency, 2 is the price", 00286 "This items costs %1 %2.\nDo you want to buy it?", 00287 item.currency(), content.downloadUrlDescription(pair.second).priceAmount() 00288 )) == KMessageBox::Yes) { 00289 ItemJob<DownloadItem>* job = m_provider.downloadLink(entry.uniqueId(), QString::number(pair.second)); 00290 connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(downloadItemLoaded(Attica::BaseJob*))); 00291 connect(job, SIGNAL(jobStarted(QNetworkReply*)), SLOT(atticaJobStarted(QNetworkReply*))); 00292 mDownloadLinkJobs[job] = qMakePair(entry, pair.second); 00293 job->start(); 00294 } else { 00295 return; 00296 } 00297 } else { 00298 kDebug() << "You don't have enough money on your account!" 00299 << content.downloadUrlDescription(0).priceAmount() << " balance: " << item.balance(); 00300 KMessageBox::information(0, i18n("Your account balance is too low:\nYour balance: %1\nPrice: %2", 00301 item.balance(),content.downloadUrlDescription(0).priceAmount())); 00302 } 00303 } 00304 00305 void AtticaProvider::downloadItemLoaded(BaseJob* baseJob) 00306 { 00307 if (!jobSuccess(baseJob)) return; 00308 00309 ItemJob<DownloadItem>* job = static_cast<ItemJob<DownloadItem>*>(baseJob); 00310 DownloadItem item = job->result(); 00311 00312 EntryInternal entry = mDownloadLinkJobs.take(job).first; 00313 entry.setPayload(QString(item.url().toString())); 00314 emit payloadLinkLoaded(entry); 00315 } 00316 00317 EntryInternal::List AtticaProvider::installedEntries() const 00318 { 00319 EntryInternal::List entries; 00320 foreach (const EntryInternal& entry, mCachedEntries) { 00321 if (entry.status() == Entry::Installed || entry.status() == Entry::Updateable) { 00322 entries.append(entry); 00323 } 00324 } 00325 return entries; 00326 } 00327 00328 void AtticaProvider::vote(const EntryInternal& entry, uint rating) 00329 { 00330 PostJob * job = m_provider.voteForContent(entry.uniqueId(), rating); 00331 connect(job, SIGNAL(finished(Attica::BaseJob*)), this, SLOT(votingFinished(Attica::BaseJob*))); 00332 connect(job, SIGNAL(jobStarted(QNetworkReply*)), SLOT(atticaJobStarted(QNetworkReply*))); 00333 job->start(); 00334 } 00335 00336 void AtticaProvider::votingFinished(Attica::BaseJob* job) 00337 { 00338 if (!jobSuccess(job)) return; 00339 emit signalInformation(i18nc("voting for an item (good/bad)", "Your vote was recorded.")); 00340 } 00341 00342 void AtticaProvider::becomeFan(const EntryInternal& entry) 00343 { 00344 PostJob * job = m_provider.becomeFan(entry.uniqueId()); 00345 connect(job, SIGNAL(finished(Attica::BaseJob*)), this, SLOT(becomeFanFinished(Attica::BaseJob*))); 00346 connect(job, SIGNAL(jobStarted(QNetworkReply*)), SLOT(atticaJobStarted(QNetworkReply*))); 00347 job->start(); 00348 } 00349 00350 void AtticaProvider::becomeFanFinished(Attica::BaseJob* job) 00351 { 00352 if (!jobSuccess(job)) return; 00353 emit signalInformation(i18n("You are now a fan.")); 00354 } 00355 00356 bool AtticaProvider::jobSuccess(Attica::BaseJob* job) const 00357 { 00358 if (job->metadata().error() == Attica::Metadata::NoError) { 00359 return true; 00360 } 00361 kDebug() << "job error: " << job->metadata().error() << " status code: " << job->metadata().statusCode() << job->metadata().message(); 00362 00363 if (job->metadata().error() == Attica::Metadata::NetworkError) { 00364 emit signalError(i18n("Network error. (%1)", job->metadata().statusCode())); 00365 } 00366 if (job->metadata().error() == Attica::Metadata::OcsError) { 00367 if (job->metadata().statusCode() == 200) { 00368 emit signalError(i18n("Too many requests to server. Please try again in a few minutes.")); 00369 } else { 00370 emit signalError(i18n("Unknown Open Collaboration Service API error. (%1)", job->metadata().statusCode())); 00371 } 00372 } 00373 return false; 00374 } 00375 00376 EntryInternal AtticaProvider::entryFromAtticaContent(const Attica::Content& content) 00377 { 00378 EntryInternal entry; 00379 00380 entry.setProviderId(id()); 00381 entry.setUniqueId(content.id()); 00382 entry.setStatus(KNS3::Entry::Downloadable); 00383 entry.setVersion(content.version()); 00384 entry.setReleaseDate(content.updated().date()); 00385 00386 int index = mCachedEntries.indexOf(entry); 00387 if (index >= 0) { 00388 EntryInternal &cacheEntry = mCachedEntries[index]; 00389 // check if updateable 00390 if (((cacheEntry.status() == Entry::Installed) || (cacheEntry.status() == Entry::Updateable)) && 00391 ((cacheEntry.version() != entry.version()) || (cacheEntry.releaseDate() != entry.releaseDate()))) { 00392 cacheEntry.setStatus(Entry::Updateable); 00393 cacheEntry.setUpdateVersion(entry.version()); 00394 cacheEntry.setUpdateReleaseDate(entry.releaseDate()); 00395 } 00396 entry = cacheEntry; 00397 } else { 00398 mCachedEntries.append(entry); 00399 } 00400 00401 entry.setName(content.name()); 00402 entry.setHomepage(content.detailpage()); 00403 entry.setRating(content.rating()); 00404 entry.setDownloadCount(content.downloads()); 00405 entry.setNumberFans(content.attribute("fans").toInt()); 00406 entry.setDonationLink(content.attribute("donationpage")); 00407 entry.setKnowledgebaseLink(content.attribute("knowledgebasepage")); 00408 entry.setNumberKnowledgebaseEntries(content.attribute("knowledgebaseentries").toInt()); 00409 00410 entry.setPreviewUrl(content.smallPreviewPicture("1"), EntryInternal::PreviewSmall1); 00411 entry.setPreviewUrl(content.smallPreviewPicture("2"), EntryInternal::PreviewSmall2); 00412 entry.setPreviewUrl(content.smallPreviewPicture("3"), EntryInternal::PreviewSmall3); 00413 00414 entry.setPreviewUrl(content.previewPicture("1"), EntryInternal::PreviewBig1); 00415 entry.setPreviewUrl(content.previewPicture("2"), EntryInternal::PreviewBig2); 00416 entry.setPreviewUrl(content.previewPicture("3"), EntryInternal::PreviewBig3); 00417 00418 entry.setLicense(content.license()); 00419 Author author; 00420 author.setName(content.author()); 00421 author.setHomepage(content.attribute("profilepage")); 00422 entry.setAuthor(author); 00423 00424 entry.setSource(KNS3::EntryInternal::Online); 00425 entry.setSummary(content.description()); 00426 entry.setChangelog(content.changelog()); 00427 00428 entry.clearDownloadLinkInformation(); 00429 QList<Attica::DownloadDescription> descs = content.downloadUrlDescriptions(); 00430 foreach (Attica::DownloadDescription desc, descs) { 00431 EntryInternal::DownloadLinkInformation info; 00432 info.name = desc.name(); 00433 info.priceAmount = desc.priceAmount(); 00434 info.distributionType = desc.distributionType(); 00435 info.descriptionLink = desc.link(); 00436 info.id = desc.id(); 00437 info.isDownloadtypeLink = desc.isDownloadtypLink(); 00438 entry.appendDownloadLinkInformation(info); 00439 } 00440 00441 return entry; 00442 } 00443 00444 } // namespace 00445 00446 00447 #include "atticaprovider.moc"
KDE 4.6 API Reference