diff --git a/src/Graphics.cpp b/src/Graphics.cpp index ad3696d0c..f94858471 100644 --- a/src/Graphics.cpp +++ b/src/Graphics.cpp @@ -1190,9 +1190,14 @@ void Graphics::draw_icon(int x, int y, Icon icon) case IconPause: drawchar(x, y, 0x90, 255, 255, 255, 255); break; + case IconReport: + drawchar(x, y, 0xE3, 255, 255, 0, 255); + break; + case IconFavourite: + drawchar(x, y, 0xCC, 192, 160, 64, 255); + break; case IconVoteSort: case IconDateSort: - case IconFavourite: case IconFolder: case IconSearch: case IconDelete: diff --git a/src/Graphics.h b/src/Graphics.h index d2f97219f..34305d73b 100644 --- a/src/Graphics.h +++ b/src/Graphics.h @@ -72,7 +72,8 @@ enum Icon IconFavourite, IconFolder, IconSearch, - IconDelete + IconDelete, + IconReport }; class Graphics diff --git a/src/client/Client.cpp b/src/client/Client.cpp index b748005db..2d6567766 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -57,6 +57,14 @@ Client::Client(): authUser.SessionID = ((json::String)(configDocument["User"]["SessionID"])).Value(); authUser.SessionKey = ((json::String)(configDocument["User"]["SessionKey"])).Value(); authUser.Username = ((json::String)(configDocument["User"]["Username"])).Value(); + + std::string userElevation = ((json::String)(configDocument["User"]["Elevation"])).Value(); + if(userElevation == "Admin") + authUser.UserElevation = ElevationAdmin; + else if(userElevation == "Mod") + authUser.UserElevation = ElevationModerator; + else + authUser.UserElevation= ElevationNone; } catch (json::Exception &e) { @@ -117,6 +125,12 @@ Client::~Client() configDocument["User"]["SessionID"] = json::String(authUser.SessionID); configDocument["User"]["SessionKey"] = json::String(authUser.SessionKey); configDocument["User"]["Username"] = json::String(authUser.Username); + if(authUser.UserElevation == ElevationAdmin) + configDocument["User"]["Elevation"] = json::String("Admin"); + else if(authUser.UserElevation == ElevationModerator) + configDocument["User"]["Elevation"] = json::String("Mod"); + else + configDocument["User"]["Elevation"] = json::String("None"); } else { @@ -402,10 +416,18 @@ LoginStatus Client::Login(string username, string password, User & user) json::Number userIDTemp = objDocument["UserID"]; json::String sessionIDTemp = objDocument["SessionID"]; json::String sessionKeyTemp = objDocument["SessionKey"]; + json::String userElevationTemp = objDocument["Elevation"]; user.Username = username; user.ID = userIDTemp.Value(); user.SessionID = sessionIDTemp.Value(); user.SessionKey = sessionKeyTemp.Value(); + std::string userElevation = userElevationTemp.Value(); + if(userElevation == "Admin") + user.UserElevation = ElevationAdmin; + else if(userElevation == "Mod") + user.UserElevation = ElevationModerator; + else + user.UserElevation= ElevationNone; return LoginOkay; } else @@ -439,7 +461,175 @@ RequestStatus Client::DeleteSave(int saveID) std::stringstream urlStream; char * data = NULL; int dataStatus, dataLength; - urlStream << "http://" << SERVER << "/Browse/Delete.json?ID=" << saveID; + urlStream << "http://" << SERVER << "/Browse/Delete.json?ID=" << saveID << "&Mode=Delete&Key=" << authUser.SessionKey; + if(authUser.ID) + { + std::stringstream userIDStream; + userIDStream << authUser.ID; + data = http_auth_get((char *)urlStream.str().c_str(), (char *)(userIDStream.str().c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength); + } + else + { + lastError = "Not authenticated"; + return RequestFailure; + } + if(dataStatus == 200 && data) + { + try + { + std::istringstream dataStream(data); + json::Object objDocument; + json::Reader::Read(objDocument, dataStream); + + int status = ((json::Number)objDocument["Status"]).Value(); + + if(status!=1) + goto failure; + } + catch (json::Exception &e) + { + lastError = "Could not read response"; + goto failure; + } + } + else + { + lastError = http_ret_text(dataStatus); + goto failure; + } + if(data) + free(data); + return RequestOkay; +failure: + if(data) + free(data); + return RequestFailure; +} + +RequestStatus Client::FavouriteSave(int saveID, bool favourite) +{ + lastError = ""; + std::vector * tags = NULL; + std::stringstream urlStream; + char * data = NULL; + int dataStatus, dataLength; + urlStream << "http://" << SERVER << "/Browse/Favourite.json?ID=" << saveID << "&Key=" << authUser.SessionKey; + if(!favourite) + urlStream << "&Mode=Remove"; + if(authUser.ID) + { + std::stringstream userIDStream; + userIDStream << authUser.ID; + data = http_auth_get((char *)urlStream.str().c_str(), (char *)(userIDStream.str().c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength); + } + else + { + lastError = "Not authenticated"; + return RequestFailure; + } + if(dataStatus == 200 && data) + { + try + { + std::istringstream dataStream(data); + json::Object objDocument; + json::Reader::Read(objDocument, dataStream); + + int status = ((json::Number)objDocument["Status"]).Value(); + + if(status!=1) + { + lastError = ((json::String)objDocument["Error"]).Value(); + goto failure; + } + } + catch (json::Exception &e) + { + lastError = "Could not read response"; + goto failure; + } + } + else + { + lastError = http_ret_text(dataStatus); + goto failure; + } + if(data) + free(data); + return RequestOkay; +failure: + if(data) + free(data); + return RequestFailure; +} + +RequestStatus Client::ReportSave(int saveID, std::string message) +{ + lastError = ""; + std::vector * tags = NULL; + std::stringstream urlStream; + char * data = NULL; + int dataStatus, dataLength; + urlStream << "http://" << SERVER << "/Browse/Report.json?ID=" << saveID << "&Key=" << authUser.SessionKey; + if(authUser.ID) + { + std::stringstream userIDStream; + userIDStream << authUser.ID; + + char * postNames[] = { "Reason", NULL }; + char * postDatas[] = { (char*)(message.c_str()) }; + int postLengths[] = { message.length() }; + data = http_multipart_post((char *)urlStream.str().c_str(), postNames, postDatas, postLengths, (char *)(userIDStream.str().c_str()), NULL, (char *)(authUser.SessionID.c_str()), &dataStatus, &dataLength); + } + else + { + lastError = "Not authenticated"; + return RequestFailure; + } + if(dataStatus == 200 && data) + { + try + { + std::istringstream dataStream(data); + json::Object objDocument; + json::Reader::Read(objDocument, dataStream); + + int status = ((json::Number)objDocument["Status"]).Value(); + + if(status!=1) + { + lastError = ((json::String)objDocument["Error"]).Value(); + goto failure; + } + } + catch (json::Exception &e) + { + lastError = "Could not read response"; + goto failure; + } + } + else + { + lastError = http_ret_text(dataStatus); + goto failure; + } + if(data) + free(data); + return RequestOkay; +failure: + if(data) + free(data); + return RequestFailure; +} + +RequestStatus Client::UnpublishSave(int saveID) +{ + lastError = ""; + std::vector * tags = NULL; + std::stringstream urlStream; + char * data = NULL; + int dataStatus, dataLength; + urlStream << "http://" << SERVER << "/Browse/Delete.json?ID=" << saveID << "&Mode=Unpublish&Key=" << authUser.SessionKey; if(authUser.ID) { std::stringstream userIDStream; @@ -523,6 +713,7 @@ Save * Client::GetSave(int saveID, int saveDate) json::String tempDescription = objDocument["Description"]; json::Number tempDate = objDocument["Date"]; json::Boolean tempPublished = objDocument["Published"]; + json::Boolean tempFavourite = objDocument["Favourite"]; json::Array tagsArray = objDocument["Tags"]; vector tempTags; @@ -533,7 +724,7 @@ Save * Client::GetSave(int saveID, int saveDate) tempTags.push_back(tempTag.Value()); } - return new Save( + Save * tempSave = new Save( tempID.Value(), tempDate.Value(), tempScoreUp.Value(), @@ -545,6 +736,8 @@ Save * Client::GetSave(int saveID, int saveDate) tempPublished.Value(), tempTags ); + tempSave->Favourite = tempFavourite.Value(); + return tempSave; } catch (json::Exception &e) { @@ -597,8 +790,8 @@ Thumbnail * Client::GetPreview(int saveID, int saveDate) { free(data); } - return new Thumbnail(saveID, saveDate, (pixel *)malloc((128*128) * PIXELSIZE), ui::Point(128, 128)); } + return new Thumbnail(saveID, saveDate, (pixel *)malloc((128*128) * PIXELSIZE), ui::Point(128, 128)); } std::vector * Client::GetComments(int saveID, int start, int count) @@ -856,7 +1049,7 @@ std::vector * Client::RemoveTag(int saveID, string tag) std::stringstream urlStream; char * data = NULL; int dataStatus, dataLength; - urlStream << "http://" << SERVER << "/Browse/EditTag.json?Op=delete&ID=" << saveID << "&Tag=" << tag; + urlStream << "http://" << SERVER << "/Browse/EditTag.json?Op=delete&ID=" << saveID << "&Tag=" << tag << "&Key=" << authUser.SessionKey;; if(authUser.ID) { std::stringstream userIDStream; @@ -905,7 +1098,7 @@ std::vector * Client::AddTag(int saveID, string tag) std::stringstream urlStream; char * data = NULL; int dataStatus, dataLength; - urlStream << "http://" << SERVER << "/Browse/EditTag.json?Op=add&ID=" << saveID << "&Tag=" << tag; + urlStream << "http://" << SERVER << "/Browse/EditTag.json?Op=add&ID=" << saveID << "&Tag=" << tag << "&Key=" << authUser.SessionKey; if(authUser.ID) { std::stringstream userIDStream; diff --git a/src/client/Client.h b/src/client/Client.h index d0ea14b1a..2867f5350 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -68,6 +68,9 @@ public: Thumbnail * GetThumbnail(int saveID, int saveDate); Save * GetSave(int saveID, int saveDate); RequestStatus DeleteSave(int saveID); + RequestStatus ReportSave(int saveID, std::string message); + RequestStatus UnpublishSave(int saveID); + RequestStatus FavouriteSave(int saveID, bool favourite); void SetAuthUser(User user); User GetAuthUser(); std::vector * RemoveTag(int saveID, string tag); //TODO RequestStatus diff --git a/src/dialogues/TextPrompt.h b/src/dialogues/TextPrompt.h index efca128c5..8016d565d 100644 --- a/src/dialogues/TextPrompt.h +++ b/src/dialogues/TextPrompt.h @@ -16,7 +16,6 @@ class TextPrompt: public ui::Window { protected: ui::Textbox * textField; public: - //class CloseAction; friend class CloseAction; enum DialogueResult { ResultCancel, ResultOkay }; TextPrompt(std::string title, std::string message, bool multiline, TextDialogueCallback * callback_); diff --git a/src/preview/PreviewController.cpp b/src/preview/PreviewController.cpp index 558ccb4ad..a6e21c6b5 100644 --- a/src/preview/PreviewController.cpp +++ b/src/preview/PreviewController.cpp @@ -6,13 +6,17 @@ */ #include +#include "client/Client.h" #include "PreviewController.h" #include "PreviewView.h" #include "PreviewModel.h" +#include "PreviewModelException.h" +#include "dialogues/ErrorMessage.h" #include "Controller.h" PreviewController::PreviewController(int saveID, ControllerCallback * callback): - HasExited(false) + HasExited(false), + saveId(saveID) { previewModel = new PreviewModel(); previewView = new PreviewView(); @@ -26,7 +30,14 @@ PreviewController::PreviewController(int saveID, ControllerCallback * callback): void PreviewController::Update() { - previewModel->Update(); + try + { + previewModel->Update(); + } + catch (PreviewModelException & e) + { + new ErrorMessage("Error", e.what()); + } } Save * PreviewController::GetSave() @@ -44,6 +55,22 @@ void PreviewController::DoOpen() previewModel->SetDoOpen(true); } +void PreviewController::Report(std::string message) +{ + if(Client::Ref().ReportSave(saveId, message) == RequestOkay) + { + Exit(); + new ErrorMessage("Information", "Report submitted"); //TODO: InfoMessage + } + else + new ErrorMessage("Error", "Unable file report"); +} + +void PreviewController::FavouriteSave() +{ + previewModel->SetFavourite(true); +} + void PreviewController::OpenInBrowser() { if(previewModel->GetSave()) diff --git a/src/preview/PreviewController.h b/src/preview/PreviewController.h index 595a30ff3..e1b02953f 100644 --- a/src/preview/PreviewController.h +++ b/src/preview/PreviewController.h @@ -16,6 +16,7 @@ class PreviewModel; class PreviewView; class PreviewController { + int saveId; PreviewModel * previewModel; PreviewView * previewView; ControllerCallback * callback; @@ -25,10 +26,12 @@ public: void Exit(); void DoOpen(); void OpenInBrowser(); + void Report(std::string message); bool GetDoOpen(); Save * GetSave(); PreviewView * GetView() { return previewView; } void Update(); + void FavouriteSave(); virtual ~PreviewController(); }; diff --git a/src/preview/PreviewModel.cpp b/src/preview/PreviewModel.cpp index 371d917c6..71c4d4a0b 100644 --- a/src/preview/PreviewModel.cpp +++ b/src/preview/PreviewModel.cpp @@ -7,6 +7,7 @@ #include "PreviewModel.h" #include "client/Client.h" +#include "PreviewModelException.h" PreviewModel::PreviewModel(): save(NULL), @@ -60,6 +61,16 @@ void * PreviewModel::updateSaveCommentsT() return tempComments; } +void PreviewModel::SetFavourite(bool favourite) +{ + //if(save) + { + Client::Ref().FavouriteSave(save->id, favourite); + save->Favourite = favourite; + notifySaveChanged(); + } +} + void PreviewModel::UpdateSave(int saveID, int saveDate) { this->tSaveID = saveID; @@ -192,6 +203,8 @@ void PreviewModel::Update() updateSaveInfoWorking = false; pthread_join(updateSaveInfoThread, (void**)(&save)); notifySaveChanged(); + if(!save) + throw PreviewModelException("Unable to load save"); } } diff --git a/src/preview/PreviewModel.h b/src/preview/PreviewModel.h index 84ce83da8..91ca97218 100644 --- a/src/preview/PreviewModel.h +++ b/src/preview/PreviewModel.h @@ -56,6 +56,7 @@ public: std::vector * GetComments(); void AddObserver(PreviewView * observer); void UpdateSave(int saveID, int saveDate); + void SetFavourite(bool favourite); bool GetDoOpen(); void SetDoOpen(bool doOpen); void Update(); diff --git a/src/preview/PreviewModelException.h b/src/preview/PreviewModelException.h new file mode 100644 index 000000000..261d203e0 --- /dev/null +++ b/src/preview/PreviewModelException.h @@ -0,0 +1,26 @@ +/* + * PreviewModelException.h + * + * Created on: Apr 14, 2012 + * Author: Simon + */ + +#ifndef PREVIEWMODELEXCEPTION_H_ +#define PREVIEWMODELEXCEPTION_H_ + +#include +#include +using namespace std; + +struct PreviewModelException: public exception { + string message; +public: + PreviewModelException(string message_): message(message_) {} + const char * what() const throw() + { + return message.c_str(); + } + ~PreviewModelException() throw() {}; +}; + +#endif /* PREVIEWMODELEXCEPTION_H_ */ diff --git a/src/preview/PreviewView.cpp b/src/preview/PreviewView.cpp index aae792d3c..7df1f6b18 100644 --- a/src/preview/PreviewView.cpp +++ b/src/preview/PreviewView.cpp @@ -7,6 +7,7 @@ #include #include "PreviewView.h" +#include "dialogues/TextPrompt.h" #include "interface/Point.h" #include "interface/Window.h" #include "search/Thumbnail.h" @@ -32,6 +33,50 @@ PreviewView::PreviewView(): openButton->SetActionCallback(new OpenAction(this)); AddComponent(openButton); + class FavAction: public ui::ButtonAction + { + PreviewView * v; + public: + FavAction(PreviewView * v_){ v = v_; } + virtual void ActionCallback(ui::Button * sender) + { + v->c->FavouriteSave(); + } + }; + + favButton = new ui::Button(ui::Point(50, Size.Y-16), ui::Point(50, 16), "Fav."); + favButton->SetAlignment(AlignLeft, AlignMiddle); + favButton->SetIcon(IconFavourite); + favButton->SetActionCallback(new FavAction(this)); + AddComponent(favButton); + + class ReportPromptCallback: public TextDialogueCallback { + public: + PreviewView * v; + ReportPromptCallback(PreviewView * v_) { v = v_; } + virtual void TextCallback(TextPrompt::DialogueResult result, std::string resultText) { + if (result == TextPrompt::ResultOkay) + v->c->Report(resultText); + } + virtual ~ReportPromptCallback() { } + }; + + class ReportAction: public ui::ButtonAction + { + PreviewView * v; + public: + ReportAction(PreviewView * v_){ v = v_; } + virtual void ActionCallback(ui::Button * sender) + { + new TextPrompt("Report Save", "Reason for reporting", true, new ReportPromptCallback(v)); + } + }; + reportButton = new ui::Button(ui::Point(100, Size.Y-16), ui::Point(50, 16), "Report"); + reportButton->SetAlignment(AlignLeft, AlignMiddle); + reportButton->SetIcon(IconReport); + reportButton->SetActionCallback(new ReportAction(this)); + AddComponent(reportButton); + class BrowserOpenAction: public ui::ButtonAction { PreviewView * v; @@ -42,7 +87,8 @@ PreviewView::PreviewView(): v->c->OpenInBrowser(); } }; - browserOpenButton = new ui::Button(ui::Point((XRES/2)-90, Size.Y-16), ui::Point(90, 16), "Open in browser"); + + browserOpenButton = new ui::Button(ui::Point((XRES/2)-110, Size.Y-16), ui::Point(110, 16), "Open in browser"); browserOpenButton->SetAlignment(AlignLeft, AlignMiddle); browserOpenButton->SetIcon(IconOpen); browserOpenButton->SetActionCallback(new BrowserOpenAction(this)); @@ -114,6 +160,10 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender) saveNameLabel->SetText(save->name); authorDateLabel->SetText("\bgAuthor:\bw " + save->userName + " \bgDate:\bw "); saveDescriptionTextblock->SetText(save->Description); + if(save->Favourite) + favButton->Enabled = false; + else + favButton->Enabled = true; } else { @@ -122,6 +172,7 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender) saveNameLabel->SetText(""); authorDateLabel->SetText(""); saveDescriptionTextblock->SetText(""); + favButton->Enabled = false; } } diff --git a/src/preview/PreviewView.h b/src/preview/PreviewView.h index 11ed4bd88..367736a2f 100644 --- a/src/preview/PreviewView.h +++ b/src/preview/PreviewView.h @@ -24,6 +24,8 @@ class PreviewView: public ui::Window { Thumbnail * savePreview; ui::Button * openButton; ui::Button * browserOpenButton; + ui::Button * favButton; + ui::Button * reportButton; ui::Label * saveNameLabel; ui::Label * authorDateLabel; ui::Textblock * saveDescriptionTextblock; diff --git a/src/search/Save.h b/src/search/Save.h index 00387ae37..0b213e221 100644 --- a/src/search/Save.h +++ b/src/search/Save.h @@ -18,6 +18,7 @@ public: int votesUp, votesDown; unsigned char * data; int dataLength; + bool Favourite; Save(Save & save); diff --git a/src/search/SearchController.cpp b/src/search/SearchController.cpp index 90cd41b72..09fde713b 100644 --- a/src/search/SearchController.cpp +++ b/src/search/SearchController.cpp @@ -6,6 +6,7 @@ #include "SearchView.h" #include "interface/Panel.h" #include "dialogues/ConfirmPrompt.h" +#include "dialogues/ErrorMessage.h" #include "preview/PreviewController.h" #include "client/Client.h" #include "tasks/Task.h" @@ -206,15 +207,58 @@ void SearchController::removeSelectedC() void SearchController::UnpublishSelected() { + class UnpublishSelectedConfirmation: public ConfirmDialogueCallback { + public: + SearchController * c; + UnpublishSelectedConfirmation(SearchController * c_) { c = c_; } + virtual void ConfirmCallback(ConfirmPrompt::DialogueResult result) { + if (result == ConfirmPrompt::ResultOkay) + c->unpublishSelectedC(); + } + virtual ~UnpublishSelectedConfirmation() { } + }; + std::stringstream desc; + desc << "Are you sure you want to hide " << searchModel->GetSelected().size() << " save"; + if(searchModel->GetSelected().size()>1) + desc << "s"; + new ConfirmPrompt("Unpublish saves", desc.str(), new UnpublishSelectedConfirmation(this)); } void SearchController::unpublishSelectedC() { + class UnpublishSavesTask : public Task + { + std::vector saves; + public: + UnpublishSavesTask(std::vector saves_) { saves = saves_; } + virtual void doWork() + { + for(int i = 0; i < saves.size(); i++) + { + std::stringstream saveID; + saveID << "Hiding save [" << saves[i] << "] ..."; + notifyStatus(saveID.str()); + if(Client::Ref().UnpublishSave(saves[i])!=RequestOkay) + { + std::stringstream saveIDF; + saveIDF << "\boFailed to hide [" << saves[i] << "] ..."; + notifyStatus(saveIDF.str()); + usleep(500*1000); + } + usleep(100*1000); + notifyProgress((float(i+1)/float(saves.size())*100)); + } + } + }; + + std::vector selected = searchModel->GetSelected(); + new TaskWindow("Unpublishing saves", new UnpublishSavesTask(selected)); ClearSelection(); } void SearchController::FavouriteSelected() { + new ErrorMessage("Not impletemented", "Not ermplermerterd"); ClearSelection(); } diff --git a/src/search/SearchView.cpp b/src/search/SearchView.cpp index 4064658c1..5a1c6c7c6 100644 --- a/src/search/SearchView.cpp +++ b/src/search/SearchView.cpp @@ -163,6 +163,25 @@ SearchView::SearchView(): clearSelection->Visible = false; clearSelection->SetActionCallback(new ClearSelectionAction(this)); AddComponent(clearSelection); + + if(Client::Ref().GetAuthUser().ID) + { + favouriteSelected->Enabled = true; + if((Client::Ref().GetAuthUser().UserElevation == ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == ElevationModerator)) + { + unpublishSelected->Enabled = true; + removeSelected->Enabled = true; + } + else + { + unpublishSelected->Enabled = false; + removeSelected->Enabled = false; + } + } + else + { + favouriteSelected->Enabled = true; + } } void SearchView::doSearch() @@ -182,6 +201,16 @@ void SearchView::NotifySortChanged(SearchModel * sender) void SearchView::NotifyShowOwnChanged(SearchModel * sender) { ownButton->SetToggleState(sender->GetShowOwn()); + if(sender->GetShowOwn() || Client::Ref().GetAuthUser().UserElevation == ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == ElevationModerator) + { + unpublishSelected->Enabled = true; + removeSelected->Enabled = true; + } + else + { + unpublishSelected->Enabled = false; + removeSelected->Enabled = false; + } } void SearchView::NotifyPageChanged(SearchModel * sender) @@ -233,6 +262,7 @@ void SearchView::NotifySaveListChanged(SearchModel * sender) } if(!saves.size()) { + loadingSpinner->Visible = false; if(!errorLabel) { errorLabel = new ui::Label(ui::Point(((XRES+BARSIZE)/2)-100, ((YRES+MENUSIZE)/2)-6), ui::Point(200, 12), "Error");