diff --git a/app/Bootstrap.cpp b/app/Bootstrap.cpp index 52e9cfa..d0769ba 100644 --- a/app/Bootstrap.cpp +++ b/app/Bootstrap.cpp @@ -35,4 +35,4 @@ void main() clearScreen(); toast(string(e.what()), string("error")); } -} +} \ No newline at end of file diff --git a/app/Console.cpp b/app/Console.cpp index 529fee3..eb3ffaf 100644 --- a/app/Console.cpp +++ b/app/Console.cpp @@ -152,13 +152,11 @@ void Console::handleView() this->theController = Controller(this->currentView.getViewName(), buffer.str(), View::getViewExtension()); if (!Controller::getErrorBag().empty()) { - toast(string("There were some issues:"), string("error")); - printString("\n"); - printVector(Controller::getErrorBag()); - - // Empty the bag and provide a retry step. - Controller::pushError(string("")); - this->provideRetry(); + this->handleErrors(); + } + else if (Controller::hasRedirectTo != "") + { + this->handleRedirect(); } buffer.clear(); @@ -170,7 +168,30 @@ void Console::handleView() } } -// Prepare the next view and also cache the current one. +void Console::handleErrors() +{ + toast(string("There were some issues:"), string("error")); + printString("\n"); + printVector(Controller::getErrorBag()); + + Controller::pushError(string("")); + + this->provideRetry(); +} + +void Console::handleRedirect() +{ + toast(string("Redirecting..."), string("notification")); + printString("\n"); + sleepAndFlushInput(this->delay); + + string redirectTo = Controller::hasRedirectTo; + Controller::hasRedirectTo = ""; + + this->renderNextView(redirectTo); +} + +// Prepare the next view and cache the current one. void Console::renderNextView() { string nextView = this->currentView.getAvailableOptions().find(this->getLastInput())->second; @@ -183,13 +204,14 @@ void Console::renderNextView() this->handleView(); } -// Render a custom view without caching the previous one as it breaks the normal flow. +// Render a custom view without caching the previous one. void Console::renderNextView(string &viewName) { map nextOptions = View::getViewsOptions().find(viewName)->second; - this->previousViews.erase(this->previousViews.begin(), this->previousViews.end()); + //this->previousViews.erase(this->previousViews.end() - 1, this->previousViews.end()); this->currentView = View(viewName, nextOptions); + this->lastInput = '\0'; clearScreen(); this->handleView(); diff --git a/app/Console.h b/app/Console.h index a7b6ed5..dc3ec1d 100644 --- a/app/Console.h +++ b/app/Console.h @@ -34,6 +34,9 @@ class Console { void renderPreviousView(); void handleView(); + + void handleErrors(); + void handleRedirect(); public: static void initTerminal(); diff --git a/app/Controller.cpp b/app/Controller.cpp index 12e5896..6d7b159 100644 --- a/app/Controller.cpp +++ b/app/Controller.cpp @@ -8,6 +8,7 @@ string Controller::userOutputString = "@output-"; string Controller::actionString = "@action-"; vector Controller::errorBag = {}; +string Controller::hasRedirectTo = ""; void Controller::justShow() { diff --git a/app/Controller.h b/app/Controller.h index d73630f..c01d8a7 100644 --- a/app/Controller.h +++ b/app/Controller.h @@ -30,6 +30,7 @@ class Controller { public: static void pushError(string &); static vector getErrorBag(); + static string hasRedirectTo; Controller(); Controller(char *, string &, string &); diff --git a/app/QuestionModel.cpp b/app/QuestionModel.cpp index e56577a..075aa03 100644 --- a/app/QuestionModel.cpp +++ b/app/QuestionModel.cpp @@ -23,6 +23,23 @@ Question QuestionModel::setAfterId(int id) return this->question; } +vector QuestionModel::retrieveAll() +{ + vector questions; + + this->io.seekg(0, this->io.beg); + + while (this->io.read(reinterpret_cast(&this->question), sizeof(Question))) + { + if (string(this->question.deleted_at) == "") + { + questions.push_back(this->question); + } + } + + return questions; +} + bool QuestionModel::questionTitleExists(string &title) { Question question; @@ -87,6 +104,16 @@ void QuestionModel::setAttributes(map &cleanInputs) { strcpy(this->question.body, it->second.c_str()); } + else if (it->first == "userId") + { + this->question.user_id = atoi(it->second.c_str()); + } + else if (it->first == "category") + { + this->question.category_id = 1; + + // TODO: get the id from the static map defined in the category model. + } } // If there's a new user, assign created_at with the current date. @@ -94,6 +121,7 @@ void QuestionModel::setAttributes(map &cleanInputs) { time_t t = time(nullptr); strftime(this->question.created_at, sizeof(this->question.created_at), "%c", localtime(&t)); + this->question.active = false; } this->question.id = ++this->lastId; @@ -112,14 +140,16 @@ void QuestionModel::dumpFile() while (db.read(reinterpret_cast(&question), sizeof(Question))) { - dump << question.id << endl - << question.user_id << endl - << question.category_id << endl - << question.title << endl - << question.body << endl - << question.created_at << endl - << question.deleted_at << endl - << question.hasAnswer << endl; + dump << "Id: " << question.id << endl + << "User ID: " << question.user_id << endl + << "Category ID: " << question.category_id << endl + << "Votes: " << question.votes << endl + << "Title: " << question.title << endl + << "Body: " << question.body << endl + << "Created at: " << question.created_at << endl + << "Deleted at: " << question.deleted_at << endl + << "Has answer: " << question.hasAnswer << endl + << "Active: " << question.active << endl << endl; } db.close(); diff --git a/app/QuestionModel.h b/app/QuestionModel.h index 4f1f9f4..5685e3f 100644 --- a/app/QuestionModel.h +++ b/app/QuestionModel.h @@ -42,6 +42,7 @@ class QuestionModel : public ModelInterface { Question setAfterUserId(int); Question setAfterId(int); + vector retrieveAll(); bool questionTitleExists(string &); void markAnswered(int); diff --git a/app/QuestionRepository.cpp b/app/QuestionRepository.cpp index 87fe002..6060676 100644 --- a/app/QuestionRepository.cpp +++ b/app/QuestionRepository.cpp @@ -19,12 +19,15 @@ void QuestionRepository::defineValidation() this->ValidationRules = { { "title", "^.*$" }, { "body", "^.*$" }, - { "keyword", "^.*$" } + { "category", "^.*$" }, + { "keyword", "^.*$" }, + { "action", "(?!.*\\W+)(?!.*[A-Z]+)[a-z]+$" } }; this->ValidationErrors = { { "title", "..." }, { "body", "..." }, + { "category", "..." }, { "keyword", "..." } }; @@ -40,16 +43,49 @@ void QuestionRepository::receiveCleanInput(map &cleanInput) if (action == "search") { string keyword = cleanInput.find("keyword")->second; + bool foundAnyRelated = false; - toast(string("Search!"), string("notice")); + vector results = this->model.retrieveAll(); + vector::iterator it = results.begin(); + + while (it != results.end()) + { + if (string((*it).title).find(keyword) != string::npos || string((*it).body).find(keyword) != string::npos) + { + foundAnyRelated = true; + ++it; + } + else + { + it = results.erase(it); + } + } + + if (!foundAnyRelated) + { + Controller::hasRedirectTo = "create.view"; + + toast(string("No related questions found!"), string("notification")); + printString("\n"); + } + else + { + Controller::hasRedirectTo = "search-results.view"; + + toast(string("Found ") + to_string(results.size()) + string(" similar questions."), string("notification")); + printString("\n"); + } } else if (action == "create") { - toast(string("Create!"), string("notice")); + this->model.save(); + + toast(string("Your question was created!"), string("notification")); + printString("\n"); } else { - Controller::pushError(string("Please provide a valid action!")); + Controller::pushError(string("Please provide a valid action in your view!")); } } @@ -76,10 +112,19 @@ void QuestionRepository::validateItems(map &truncatedInput) { toast("\n\n" + string(e.what()) + "\n", string("error")); } + + if (it->first == "category" && !isInVector(vector({ "something", "tada", "c++" }), it->second)) + { + Controller::pushError(string("Please provide a valid category.")); + } } if (Controller::getErrorBag().empty()) { + if (truncatedInput.find("action")->second == "create") + { + truncatedInput["userId"] = to_string(this->users.setActive().id); + } this->receiveCleanInput(truncatedInput); } } diff --git a/app/QuestionRepository.h b/app/QuestionRepository.h index 110cdd4..04bd294 100644 --- a/app/QuestionRepository.h +++ b/app/QuestionRepository.h @@ -4,6 +4,7 @@ #include "RepositoryInterface.h" #include "QuestionModel.h" +#include "UserModel.h" using namespace std; @@ -12,6 +13,7 @@ class QuestionRepository : public RepositoryInterface { static string alias; QuestionModel model; + UserModel users; void defineValidation(); void receiveCleanInput(map &); diff --git a/app/UserModel.cpp b/app/UserModel.cpp index a5ba351..7a59eaa 100644 --- a/app/UserModel.cpp +++ b/app/UserModel.cpp @@ -133,16 +133,16 @@ void UserModel::dumpFile() while (db.read(reinterpret_cast(&user), sizeof(User))) { - dump << user.id << endl - << user.full_name << endl - << user.email << endl - << user.username << endl - << user.password << endl - << user.created_at << endl - << user.deleted_at << endl - << user.role << endl - << user.active << endl - << user.banned << endl << endl; + dump << "Id: " << user.id << endl + << "Full name: " << user.full_name << endl + << "Email: " << user.email << endl + << "Username: " << user.username << endl + << "Password: " << user.password << endl + << "Created at: " << user.created_at << endl + << "Delete at: " << user.deleted_at << endl + << "Role: " << user.role << endl + << "Active: " << user.active << endl + << "Banned: " << user.banned << endl << endl; } db.close(); diff --git a/app/View.cpp b/app/View.cpp index b651726..993debd 100644 --- a/app/View.cpp +++ b/app/View.cpp @@ -54,27 +54,35 @@ void View::loadViewsOptions() helpView = "help.view", dashboardView = "dashboard.view", logoutView = "logout.view", - findOrAsk = "search-or-ask.view"; + findOrAskView = "find-or-ask.view", + createView = "create.view", + searchResultsView = "search-results.view"; + + // Create non-linkable views, e.g. search results View::ViewsOptions = { - { homeView, { { '1', "login.view" }, { '2', "signup.view" }, { '3', "browse-index.view" }, - { '4', "faq.view" }, { '5', "help.view" }, { 'q', "quit" } } }, + { homeView, { { '1', loginView }, { '2', signupView }, { '3', browseIndexView }, + { '4', faqView }, { '5', helpView }, { 'q', "" } } }, + + { browseIndexView, { { 'q', "" }, { 'b', "" } } }, + + { loginView, { { 'c', dashboardView } } }, + + { signupView, { { 'c', dashboardView } } }, + + { dashboardView, { { '1', findOrAskView }, { '4', logoutView } } }, - { browseIndexView, { { 'q', "quit" }, { 'b', "back" } } }, + { logoutView, { { 'y', "" }, { 'b', "" } } }, - { loginView, { { 'c', "dashboard.view" } } }, + { faqView, { { 'q', "" }, { 'b', "" } } }, - { signupView, { { 'c', "dashboard.view" } } }, + { helpView, { { 'q', "" }, { 'b', "" } } }, - { dashboardView, { { '1', "find-or-ask.view" }, { '4', "logout.view" } } }, + { findOrAskView, { { 'b', "" } } }, - { logoutView, { { 'y', "confirm" }, { 'b', "back" } } }, - - { faqView, { { 'q', "quit" }, { 'b', "back" } } }, + { searchResultsView, { { '1', createView }, { '3', dashboardView } } }, - { helpView, { { 'q', "quit" }, { 'b', "back" } } }, - - { findOrAsk, { { 'c', "search-results.view" } } } + { createView, { { '1', dashboardView } } } }; } diff --git a/views/questions/create.view b/views/questions/create.view index bcd85e9..3766c23 100644 --- a/views/questions/create.view +++ b/views/questions/create.view @@ -4,11 +4,14 @@ _\ \ || (_| | (__| @input-question-keyword -@action-seach + +Title: @input-question-title +Description: @input-question-body +Category: @input-question-category +@action-create =========================================================== # (c) 2016 Cristian-Adrian Frasineanu | GNU GPLv3 licence # -=========================================================== +=========================================================== \ No newline at end of file diff --git a/views/questions/find-or-ask.view b/views/questions/find-or-ask.view index bcd85e9..5a9620a 100644 --- a/views/questions/find-or-ask.view +++ b/views/questions/find-or-ask.view @@ -4,11 +4,12 @@ _\ \ || (_| | (__| @input-question-keyword -@action-seach +@action-search =========================================================== # (c) 2016 Cristian-Adrian Frasineanu | GNU GPLv3 licence # -=========================================================== +=========================================================== \ No newline at end of file diff --git a/views/questions/search-results.view b/views/questions/search-results.view index 26a7861..392557a 100644 --- a/views/questions/search-results.view +++ b/views/questions/search-results.view @@ -6,12 +6,12 @@ _\ \ || (_| | (__|