Added extra call to ask for applications details
[meego-garage:garage-client-services.git] / src / ocsapplication.cpp
1 #include <attica/content.h>
2 #include "ocsapplication.h"
3 #include "garagesettings.h"
4 #include "image.h"
5 #include "category.h"
6 #include "ocscatalog.h"
7 #include "utils.h"
8
9 using namespace MeeGoGarage;
10
11 /**
12     Job to retrieve review list from OCS content.
13   */
14 class OcsReviewListJob : public ReviewListJob
15 {
16 public:
17     OcsReviewListJob(Attica::Provider & atticaProvider, Catalog * catalog = 0, Application * application = 0, int pageNumber = 0, int itemsPerPage = 10) :
18             ReviewListJob(catalog, application, pageNumber, itemsPerPage),
19             m_atticaProvider(atticaProvider)
20     {
21     }
22
23 protected:
24     virtual bool doStart();
25     virtual void onJobOver();
26     void appendReviews(const Attica::Comment::List & commentList);
27
28     Attica::Provider & m_atticaProvider;
29     ReviewList m_reviewList;
30 };
31
32 bool OcsReviewListJob::doStart()
33 {
34     gcsDebug();
35
36     OcsCatalog * ocsCatalog = static_cast<OcsCatalog*>(m_catalog);
37     if (!ocsCatalog) {
38         return false;
39     }
40
41     OcsApplication * ocsApplication = static_cast<OcsApplication*>(m_application);
42     if (!ocsApplication) {
43         return false;
44     }
45
46     if (!m_atticaProvider.hasCommentService()) {
47         gcsWarning() << tr("Comment service is not available on server");
48         return false;
49     }
50
51     Attica::ListJob<Attica::Comment> * listJob = m_atticaProvider.requestComments(Attica::Comment::ContentComment, ocsApplication->id(), QString("0"), pageNumber(), itemsPerPage());
52     if (!listJob) {
53         return false;
54     }
55     connect(listJob, SIGNAL(finished(Attica::BaseJob*)), this, SLOT(onJobOver()));
56     listJob->start();
57
58     return true;
59 }
60
61 void OcsReviewListJob::appendReviews(const Attica::Comment::List & commentList)
62 {
63     gcsDebug();
64
65     Attica::Comment comment;
66     foreach(comment, commentList) {
67         Review review;
68         review.title = comment.subject();
69         review.text = comment.text();
70         review.datetime = comment.date();
71         review.user = comment.user();
72         review.rating = (int) comment.score() / 20.0f;
73         m_reviewList.append(review);
74         if (comment.childCount() > 0) {
75             appendReviews(comment.children());
76         }
77     }
78 }
79
80
81 void OcsReviewListJob::onJobOver()
82 {
83     gcsDebug();
84
85     Attica::ListJob<Attica::Comment> * listJob = static_cast<Attica::ListJob<Attica::Comment> *> (sender());
86     if (!listJob) {
87         return;
88     }
89
90     Attica::Comment::List commentList = listJob->itemList();    
91     qDebug() << "comments from Attica " << commentList.count();
92     appendReviews(commentList);
93     setResult(m_reviewList);
94     setState(SuccessfulState);
95 }
96
97 /*
98  OcsApplication
99  */
100 OcsApplication::OcsApplication (Catalog* catalog, Category *category, Attica::Content & content, QObject *parent) :
101     Application(catalog, category, parent),
102     m_content(content)
103 {
104     gcsDebug() << content.name() <<  "category=" << (category ? category->name() : "null");
105
106     setCategoryName(content.attribute("category"));
107     if (!category) {
108         category = catalog->findCategoryByName(categoryName());
109         if (category) {
110             m_category = category;
111             setParent(category);
112         }
113     }
114
115     updateFromContent(content);
116
117     m_reviewList.clear();
118     m_reviewListRequested = false;
119 }
120
121 void OcsApplication::updateFromContent(Attica::Content & content)
122 {
123     m_content = content;
124
125     setId(content.id());
126     m_applicationName = content.name(); //.trimmed();
127     // special handling of maemo server
128     if (!content.attribute("dc:title").isEmpty()) {
129         m_packageName = content.name();
130         m_applicationName = content.attribute("dc:title");
131         //content.attribute("dc:type") not used
132     }
133     m_averageRating = (content.rating() / 20.0f);
134     if (m_averageRating < 0.0f) {
135         m_averageRating = 0.0f;
136     } else if (m_averageRating > 5.0f) {
137         m_averageRating = 5.0f;
138     }
139     m_ratingCount = content.numberOfComments();
140     m_downloads = content.downloads();
141     m_publishDate = content.updated().toLocalTime();
142     if (!content.attribute("summary").isEmpty()) {
143         m_description = content.summary();
144         m_longDescription = content.description();
145     } else {
146         m_description = content.description();
147         m_longDescription = content.description();
148     }
149     m_createdDate = content.created().toLocalTime();
150     m_version = content.version();
151     m_licenseName = content.licenseName();
152     m_publisher = content.author();
153     m_changelog = content.changelog();
154     m_reviewCount = content.numberOfComments();
155
156     processDownloadDescriptions(content.downloadUrlDescriptions());
157     // fields from OCS not used
158     // detailpage -> long description ?
159     // depends
160     //QString smallPreviewPicture(const QString& number = QLatin1String("1")) const;
161
162     // extra fields required by Intel
163     //m_execFilename = content.attribute("execfilename");
164     //m_publisherURL = content.attribute("publisherurl");
165     //m_supportURL = content.attribute("supporturl");
166
167     // TODO reading user rating not available yet in OCS spec 1.6
168     //m_userRating = content.attribute("userrating").toInt();
169     m_userRating = m_averageRating;
170
171     // get icons
172     Attica::Icon icon;
173     Image * largestIcon = 0;
174     int largestIconSize = -1;
175     foreach(icon, content.icons()) {
176         //gcsDebug() << "icon" << icon.width() << icon.height() << icon.url();
177
178         Image::ImageType imageType;
179         if (icon.width() <= 48) {
180             imageType = Image::SmallIconImage;
181         } else if (icon.width() <= 64) {
182             imageType = Image::MediumIconImage;
183         } else {
184             imageType = Image::LargeIconImage;
185         }
186         if (!m_applicationIcon[imageType]) {
187             m_applicationIcon[imageType] = Image::NewImage(this, icon.url().toString(), imageType);
188             m_applicationIcon[imageType]->requestContent();
189
190             if (int(icon.width()) > largestIconSize) {
191                 largestIcon = m_applicationIcon[imageType];
192                 largestIconSize = icon.width();
193             }
194         }
195     }
196
197     // make sure there are no null icons
198     if (!m_applicationIcon[Image::SmallIconImage]) {
199         m_applicationIcon[Image::SmallIconImage] = largestIcon;
200     }
201     if (!m_applicationIcon[Image::MediumIconImage]) {
202         m_applicationIcon[Image::MediumIconImage] = largestIcon;
203     }
204     if (!m_applicationIcon[Image::LargeIconImage]) {
205         m_applicationIcon[Image::LargeIconImage] = largestIcon;
206     }
207
208     //gcsDebug() << "icon small " << m_applicationIcon[Image::SmallIconImage];
209     //gcsDebug() << "icon medium " << m_applicationIcon[Image::MediumIconImage];
210     //gcsDebug() << "icon large " << m_applicationIcon[Image::LargeIconImage];
211
212     // get screenshots
213     bool hasPictures = true;
214     int numPictures = 1;
215     while (hasPictures) {
216         QString picture = content.previewPicture(QString::number(numPictures));
217         if (!picture.isEmpty()) {
218             numPictures++;
219             gcsDebug() << "downloading image " << picture;
220             m_screenshotList.append( Image::NewImage(this, picture, Image::ScreenshotImage) );
221         } else {
222             hasPictures = false;
223         }
224     }
225
226     // get videos
227     bool hasVideos = true;
228     int numVideos = 1;
229     while (hasVideos) {
230         QString videoUrl = content.attribute("videourl" + QString::number(numVideos));
231         if (!videoUrl.isEmpty()) {
232             numVideos++;
233             m_videoUrlList.append( videoUrl );
234         } else {
235             hasVideos = false;
236         }
237     }
238
239     // todo: m_userRating
240     // todo: m_ratingCount
241     // todo: m_reviews: title, text, datetime, user, rating
242     m_userRating = 0;
243     m_ratingCount = 0;
244 }
245
246 bool OcsApplication::setUserRating(int rating)
247 {
248     gcsDebug();
249
250     OcsCatalog * ocsCatalog = static_cast<OcsCatalog*> (m_catalog);
251     if (!ocsCatalog->atticaProvider().hasContentService()) {
252         return false;
253     }
254
255     int ocsRating = rating * 20;
256     if (ocsRating < 0) {
257         ocsRating = 0;
258     } else if (ocsRating > 100) {
259         ocsRating = 100;
260     }
261     Attica::PostJob * postJob = ocsCatalog->atticaProvider().voteForContent(id(), uint(ocsRating));
262     if (!postJob) {
263         return false;
264     }
265
266     connect(postJob, SIGNAL(finished(Attica::BaseJob*)), SLOT(requestDetails()));
267     postJob->start();
268
269     return true;
270 }
271
272 ReviewListJob *OcsApplication::requestReviews(int pageNumber, int itemsPerPage)
273 {
274     gcsDebug() << "pageNumber=" << pageNumber << "itemsPerPage=" << itemsPerPage;
275
276     OcsCatalog * ocsCatalog = static_cast<OcsCatalog*> (m_catalog);
277     if (!ocsCatalog->atticaProvider().hasCommentService()) {
278         gcsDebug() << "No comments service";
279         return 0;
280     }
281
282     OcsReviewListJob * reviewListJob = new OcsReviewListJob(ocsCatalog->atticaProvider(), m_catalog, this, pageNumber, itemsPerPage);
283     if (!reviewListJob) {
284         return 0;
285     }
286
287     return reviewListJob;
288 }
289
290 void OcsApplication::requestDetails()
291 {
292     gcsDebug();
293
294     OcsCatalog * ocsCatalog = static_cast<OcsCatalog*> (m_catalog);
295     if (!ocsCatalog->atticaProvider().hasContentService()) {
296         return;
297     }
298
299     Attica::ItemJob<Attica::Content> * itemJob = ocsCatalog->atticaProvider().requestContent(m_content.id());
300     if (!itemJob) {
301         return;
302     }
303
304     connect(itemJob, SIGNAL(finished(Attica::BaseJob*)), SLOT(onRequestDetailsOver()));
305     itemJob->start();
306 }
307
308 void OcsApplication::onRequestDetailsOver()
309 {
310     Attica::ItemJob<Attica::Content> * itemJob = static_cast<Attica::ItemJob<Attica::Content> *> (sender());
311     if (!itemJob) {
312         return;
313     }
314
315     Attica::Content content = itemJob->result();
316     updateFromContent(content);
317
318     emit detailsUpdated();
319 }
320
321 bool OcsApplication::addReview(const Review & review)
322 {
323     gcsDebug();
324
325     OcsCatalog * ocsCatalog = static_cast<OcsCatalog*> (m_catalog);
326     if (!ocsCatalog->atticaProvider().hasCommentService()) {
327         return false;
328     }
329
330     if (!canReview()) {
331         return false;
332     }
333
334     int ocsRating = review.rating * 20;
335     if (ocsRating < 0) {
336         ocsRating = 0;
337     } else if (ocsRating > 100) {
338         ocsRating = 100;
339     }
340     Attica::PostJob * postJob = ocsCatalog->atticaProvider().addNewComment(Attica::Comment::ContentComment, id(), QString("0"), QString("0"), review.title, review.text);
341     if (!postJob) {
342         return false;
343     }
344
345     // save the rating for later in the job
346     postJob->setProperty("rating", QVariant(ocsRating));
347     connect(postJob, SIGNAL(finished(Attica::BaseJob*)), SLOT(postJobAddReviewFinished()));
348     postJob->start();
349
350     return true;
351 }
352
353 void OcsApplication::postJobAddReviewFinished()
354 {
355     gcsDebug();
356
357     OcsCatalog * ocsCatalog = static_cast<OcsCatalog*> (m_catalog);
358     if (!ocsCatalog->atticaProvider().hasCommentService()) {
359         resetReviewList();
360         reviewList();
361         return;
362     }
363
364     Attica::PostJob * postJob = static_cast<Attica::PostJob *> (sender());
365     if (!postJob) {
366         resetReviewList();
367         reviewList();
368         return;
369     }
370
371     int ocsRating = postJob->property("rating").value<int>();
372     if (ocsRating < 0 || ocsRating > 100) {
373         resetReviewList();
374         reviewList();
375         return;
376     }
377
378 #if 0
379     TODO add when server returns the id of the newly created comment - requested in OCS mailing list
380     QString atticaCommnentId;
381     Attica::PostJob * postJob2 = ocsCatalog->atticaProvider().voteForComment(atticaCommnentId, ocsRating);
382     if (!postJob) {
383         resetReviewList();
384         reviewList();
385         return;
386     }
387
388     connect(postJob2, SIGNAL(finished(Attica::BaseJob*)), SLOT(postVoteComment()));
389     postJob2->start();
390 #else
391     resetReviewList();
392     reviewList();
393 #endif
394 }
395
396 void OcsApplication::processDownloadDescriptions(const QList<Attica::DownloadDescription>& /*downloadDescriptions*/)
397 {
398 #if 0
399     Attica::DownloadDescription dd;
400     foreach(dd, downloadDescriptions) {
401         if (dd.type() != Attica::DownloadDescription::PackageDownload &&
402             !dd.packageName().isEmpty()) {
403             if (dd.size() > 0) {
404                 m_size = dd.size();
405             }
406             setPackageName(dd.packageName());
407             setRepository(dd.repository());
408             break;
409         }
410     }
411 #else
412     // for testing purposes
413     const char *packagesNames[] = { "ImageMagick", "OpenCV", "emacs", "gcompris", "gimp", "graphviz", "inkscape", "xpdf" };
414     static unsigned int num = 0;
415     QString packageName(packagesNames[num]);
416     num++;
417     if (num >= sizeof(packagesNames)/sizeof(packageName[0])) {
418         num = 0;
419     }
420     setPackageName(packageName);
421 #endif
422 }