1 /* 2 * Copyright 2016-2024, Andrew Lindesay <apl@lindesay.co.nz>. 3 * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>. 4 * All rights reserved. Distributed under the terms of the MIT License. 5 * 6 * Note that this file included code earlier from `Model.cpp` and 7 * copyrights have been latterly been carried across in 2024. 8 */ 9 10 11 #include "PopulatePkgUserRatingsFromServerProcess.h" 12 13 #include <Autolock.h> 14 #include <Catalog.h> 15 16 #include "Logger.h" 17 #include "ServerHelper.h" 18 19 20 #undef B_TRANSLATION_CONTEXT 21 #define B_TRANSLATION_CONTEXT "PopulatePkgUserRatingsFromServerProcess" 22 23 24 PopulatePkgUserRatingsFromServerProcess::PopulatePkgUserRatingsFromServerProcess( 25 PackageInfoRef packageInfo, Model* model) 26 : 27 fModel(model), 28 fPackageInfo(packageInfo) 29 { 30 } 31 32 33 PopulatePkgUserRatingsFromServerProcess::~PopulatePkgUserRatingsFromServerProcess() 34 { 35 } 36 37 38 const char* 39 PopulatePkgUserRatingsFromServerProcess::Name() const 40 { 41 return "PopulatePkgUserRatingsFromServerProcess"; 42 } 43 44 45 const char* 46 PopulatePkgUserRatingsFromServerProcess::Description() const 47 { 48 return B_TRANSLATE("Fetching user ratings for package"); 49 } 50 51 52 status_t 53 PopulatePkgUserRatingsFromServerProcess::RunInternal() 54 { 55 // TODO; use API spec to code generation techniques instead of this manually written client. 56 57 // Retrieve info from web-app 58 BMessage info; 59 60 BString packageName; 61 BString webAppRepositoryCode; 62 BString webAppRepositorySourceCode; 63 64 { 65 BAutolock locker(&fLock); 66 packageName = fPackageInfo->Name(); 67 const DepotInfo* depot = fModel->DepotForName(fPackageInfo->DepotName()); 68 69 if (depot != NULL) { 70 webAppRepositoryCode = depot->WebAppRepositoryCode(); 71 webAppRepositorySourceCode = depot->WebAppRepositorySourceCode(); 72 } 73 } 74 75 status_t status = fModel->GetWebAppInterface()->RetrieveUserRatingsForPackageForDisplay( 76 packageName, webAppRepositoryCode, webAppRepositorySourceCode, 0, 77 PACKAGE_INFO_MAX_USER_RATINGS, info); 78 79 if (status == B_OK) { 80 // Parse message 81 BMessage result; 82 BMessage items; 83 84 status = info.FindMessage("result", &result); 85 86 if (status == B_OK) { 87 88 if (result.FindMessage("items", &items) == B_OK) { 89 90 // TODO; later make the PackageInfo immutable to avoid the need for locking here. 91 92 BAutolock locker(&fLock); 93 fPackageInfo->ClearUserRatings(); 94 95 int32 index = 0; 96 while (true) { 97 BString name; 98 name << index++; 99 100 BMessage item; 101 if (items.FindMessage(name, &item) != B_OK) 102 break; 103 104 BString code; 105 if (item.FindString("code", &code) != B_OK) { 106 HDERROR("corrupt user rating at index %" B_PRIi32, index); 107 continue; 108 } 109 110 BString user; 111 BMessage userInfo; 112 if (item.FindMessage("user", &userInfo) != B_OK 113 || userInfo.FindString("nickname", &user) != B_OK) { 114 HDERROR("ignored user rating [%s] without a user nickname", code.String()); 115 continue; 116 } 117 118 // Extract basic info, all items are optional 119 BString languageCode; 120 BString comment; 121 double rating; 122 item.FindString("naturalLanguageCode", &languageCode); 123 item.FindString("comment", &comment); 124 if (item.FindDouble("rating", &rating) != B_OK) 125 rating = -1; 126 if (comment.Length() == 0 && rating == -1) { 127 HDERROR("rating [%s] has no comment or rating so will" 128 " be ignored", 129 code.String()); 130 continue; 131 } 132 133 // For which version of the package was the rating? 134 BString major = "?"; 135 BString minor = "?"; 136 BString micro = ""; 137 double revision = -1; 138 BString architectureCode = ""; 139 BMessage version; 140 if (item.FindMessage("pkgVersion", &version) == B_OK) { 141 version.FindString("major", &major); 142 version.FindString("minor", &minor); 143 version.FindString("micro", µ); 144 version.FindDouble("revision", &revision); 145 version.FindString("architectureCode", &architectureCode); 146 } 147 BString versionString = major; 148 versionString << "."; 149 versionString << minor; 150 if (!micro.IsEmpty()) { 151 versionString << "."; 152 versionString << micro; 153 } 154 if (revision > 0) { 155 versionString << "-"; 156 versionString << (int)revision; 157 } 158 159 if (!architectureCode.IsEmpty()) { 160 versionString << " " << STR_MDASH << " "; 161 versionString << architectureCode; 162 } 163 164 double createTimestamp; 165 item.FindDouble("createTimestamp", &createTimestamp); 166 167 // Add the rating to the PackageInfo 168 UserRatingRef userRating( 169 new UserRating(UserInfo(user), rating, comment, languageCode, 170 // note that language identifiers are "code" in HDS and "id" in Haiku 171 versionString, (uint64) createTimestamp), 172 true); 173 fPackageInfo->AddUserRating(userRating); 174 HDDEBUG("rating [%s] retrieved from server", code.String()); 175 } 176 177 fPackageInfo->SetDidPopulateUserRatings(); 178 179 HDDEBUG("did retrieve %" B_PRIi32 " user ratings for [%s]", index - 1, 180 packageName.String()); 181 } 182 } else { 183 int32 errorCode = WebAppInterface::ErrorCodeFromResponse(info); 184 185 if (errorCode != ERROR_CODE_NONE) 186 ServerHelper::NotifyServerJsonRpcError(info); 187 } 188 } else { 189 ServerHelper::NotifyTransportError(status); 190 } 191 192 return status; 193 } 194