add blog code.
This commit is contained in:
parent
969446d85f
commit
27ffee57be
@ -23,6 +23,7 @@ public:
|
||||
BUILD_SETTING(uint16_t, Port, 8081);
|
||||
BUILD_SETTING(uint32_t, Threads, std::thread::hardware_concurrency());
|
||||
BUILD_SETTING(std::string, DocumentRoot, ".");
|
||||
BUILD_SETTING(std::string, Sqlte3Path, "database.sqlite");
|
||||
BUILD_SETTING(std::string, HomeAssistantAccessToken, "");
|
||||
BUILD_SETTING(std::string, Live2dModelsRoot, "./live2d");
|
||||
|
||||
|
@ -61,7 +61,7 @@ int main(int argc, char const *argv[]) {
|
||||
auto application = Singleton<Application>::instance<Construct>("settings.ini");
|
||||
|
||||
auto database = Singleton<Database>::instance<Construct>();
|
||||
database->open("database.sqlite");
|
||||
database->open(application->getSqlte3Path());
|
||||
|
||||
if (!std::filesystem::exists(application->getDocumentRoot())) {
|
||||
LOG(fatal) << "document root: " << application->getDocumentRoot() << " is not exists...";
|
||||
|
11
WebApplication/BlogApplication.cpp
Normal file
11
WebApplication/BlogApplication.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "BlogApplication.h"
|
||||
#include "view/BlogView.h"
|
||||
#include <Wt/WContainerWidget.h>
|
||||
|
||||
static const char *FeedUrl = "/blog/feed/";
|
||||
|
||||
BlogApplication::BlogApplication(const Wt::WEnvironment &env, Wt::Dbo::SqlConnectionPool &blogDb)
|
||||
: Wt::WApplication(env) {
|
||||
root()->addWidget(std::make_unique<BlogView>("/", blogDb, FeedUrl));
|
||||
useStyleSheet("css/blogexample.css");
|
||||
}
|
11
WebApplication/BlogApplication.h
Normal file
11
WebApplication/BlogApplication.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __BLOGAPPLICATION_H__
|
||||
#define __BLOGAPPLICATION_H__
|
||||
|
||||
#include <Wt/WApplication.h>
|
||||
|
||||
class BlogApplication : public Wt::WApplication {
|
||||
public:
|
||||
BlogApplication(const Wt::WEnvironment &env, Wt::Dbo::SqlConnectionPool &blogDb);
|
||||
};
|
||||
|
||||
#endif // __BLOGAPPLICATION_H__
|
@ -1,13 +1,19 @@
|
||||
add_library(WebApplication
|
||||
WebApplication.h WebApplication.cpp
|
||||
BlogApplication.h BlogApplication.cpp
|
||||
Hello.h Hello.cpp
|
||||
Restful.h Restful.cpp
|
||||
Dialog.h Dialog.cpp
|
||||
Session.h Session.cpp
|
||||
User.h User.cpp
|
||||
model/BlogSession.h model/BlogSession.cpp
|
||||
model/BlogUserDatabase.h model/BlogUserDatabase.cpp
|
||||
model/User.h model/User.cpp
|
||||
view/BlogView.h view/BlogView.cpp
|
||||
view/EditUsers.h view/EditUsers.cpp
|
||||
)
|
||||
|
||||
target_include_directories(WebApplication
|
||||
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PRIVATE ${WT_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "Dialog.h"
|
||||
#include <Wt/WLineEdit.h>
|
||||
#include <Wt/WMessageBox.h>
|
||||
#include <Wt/WPushButton.h>
|
||||
|
||||
@ -22,6 +23,9 @@ Dialog::Dialog() {
|
||||
button = buttons->addWidget(std::make_unique<Wt::WPushButton>("Discard"));
|
||||
button->clicked().connect(this, &Dialog::messageBox4);
|
||||
|
||||
button = buttons->addWidget(std::make_unique<Wt::WPushButton>("Familiar"));
|
||||
button->clicked().connect(this, &Dialog::custom);
|
||||
|
||||
textdiv = addWidget(std::make_unique<Wt::WContainerWidget>());
|
||||
textdiv->setStyleClass("text");
|
||||
m_status = textdiv->addWidget(std::make_unique<Wt::WText>("Go ahead..."));
|
||||
@ -74,6 +78,27 @@ void Dialog::messageBox4() {
|
||||
Wt::TimingFunction::Linear, 250));
|
||||
}
|
||||
|
||||
void Dialog::custom() {
|
||||
Wt::WDialog dialog("个人信息");
|
||||
dialog.setClosable(true);
|
||||
dialog.setResizable(true);
|
||||
dialog.rejectWhenEscapePressed(true);
|
||||
|
||||
dialog.contents()->addWidget(std::make_unique<Wt::WText>("请输入你的名字: "));
|
||||
Wt::WLineEdit *edit = dialog.contents()->addWidget(std::make_unique<Wt::WLineEdit>());
|
||||
Wt::WPushButton *ok = dialog.footer()->addWidget(std::make_unique<Wt::WPushButton>("确认"));
|
||||
ok->setDefault(true);
|
||||
|
||||
edit->setFocus();
|
||||
ok->clicked().connect(&dialog, &Wt::WDialog::accept);
|
||||
|
||||
if (dialog.exec() == Wt::DialogCode::Accepted) {
|
||||
setStatus("欢迎, " + edit->text());
|
||||
} else {
|
||||
setStatus("Oh nevermind!");
|
||||
}
|
||||
}
|
||||
|
||||
void Dialog::messageBoxDone(Wt::StandardButton result) {
|
||||
switch (result) {
|
||||
case Wt::StandardButton::Ok:
|
||||
|
@ -13,6 +13,7 @@ protected:
|
||||
void messageBox2();
|
||||
void messageBox3();
|
||||
void messageBox4();
|
||||
void custom();
|
||||
void messageBoxDone(Wt::StandardButton result);
|
||||
|
||||
private:
|
||||
|
53
WebApplication/Restful.cpp
Normal file
53
WebApplication/Restful.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include "Restful.h"
|
||||
#include <Wt/Dbo/Impl.h>
|
||||
#include <Wt/Dbo/Json.h>
|
||||
#include <Wt/Dbo/backend/Sqlite3.h>
|
||||
#include <Wt/Http/Response.h>
|
||||
|
||||
DBO_INSTANTIATE_TEMPLATES(MyMessage)
|
||||
|
||||
DbStruct *m_dbStruct;
|
||||
|
||||
void JsonResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
|
||||
response.setMimeType("application/json");
|
||||
response.addHeader("Server", "Wt");
|
||||
|
||||
MyMessage message;
|
||||
message.message = "Hello, World!";
|
||||
|
||||
Wt::Dbo::JsonSerializer writer(response.out());
|
||||
writer.serialize(message);
|
||||
}
|
||||
|
||||
void PlaintextResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
|
||||
response.setMimeType("text/plain");
|
||||
response.addHeader("Server", "Wt");
|
||||
response.out() << "Hello, World!";
|
||||
}
|
||||
|
||||
DbResource::DbResource(const std::string &db) {
|
||||
if (!m_dbStruct) {
|
||||
m_dbStruct = new DbStruct(db);
|
||||
}
|
||||
}
|
||||
|
||||
void DbResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
|
||||
response.setMimeType("application/json");
|
||||
response.addHeader("Server", "Wt");
|
||||
|
||||
Wt::Dbo::Transaction transaction(m_dbStruct->session);
|
||||
Wt::Dbo::ptr<World> entry = m_dbStruct->session.load<World>(m_dbStruct->rand());
|
||||
|
||||
Wt::Dbo::JsonSerializer writer(response.out());
|
||||
writer.serialize(entry);
|
||||
}
|
||||
|
||||
int DbStruct::rand() {
|
||||
return distribution(rng);
|
||||
}
|
||||
|
||||
DbStruct::DbStruct(const std::string &db) : rng(clock()), distribution(1, 10000) {
|
||||
session.setConnection(std::make_unique<Wt::Dbo::backend::Sqlite3>(db));
|
||||
session.mapClass<World>("world");
|
||||
session.mapClass<Fortune>("fortune");
|
||||
}
|
63
WebApplication/Restful.h
Normal file
63
WebApplication/Restful.h
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef __RESTFUL_H__
|
||||
#define __RESTFUL_H__
|
||||
|
||||
#include <Wt/Dbo/Session.h>
|
||||
#include <Wt/Dbo/Types.h>
|
||||
#include <Wt/WResource.h>
|
||||
#include <random>
|
||||
|
||||
class MyMessage {
|
||||
public:
|
||||
std::string message;
|
||||
|
||||
template <class Action>
|
||||
void persist(Action &a) {
|
||||
Wt::Dbo::field(a, message, "message");
|
||||
}
|
||||
};
|
||||
|
||||
class World {
|
||||
public:
|
||||
int randomNumber;
|
||||
|
||||
template <class Action>
|
||||
void persist(Action &a) {
|
||||
Wt::Dbo::field(a, randomNumber, "randomnumber");
|
||||
}
|
||||
};
|
||||
|
||||
class Fortune {
|
||||
public:
|
||||
std::string message;
|
||||
|
||||
template <class Action>
|
||||
void persist(Action &a) {
|
||||
Wt::Dbo::field(a, message, "message");
|
||||
}
|
||||
};
|
||||
|
||||
struct DbStruct {
|
||||
DbStruct(const std::string &db);
|
||||
int rand();
|
||||
Wt::Dbo::Session session;
|
||||
std::default_random_engine rng;
|
||||
std::uniform_int_distribution<int> distribution;
|
||||
};
|
||||
|
||||
class JsonResource : public Wt::WResource {
|
||||
public:
|
||||
void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) final;
|
||||
};
|
||||
|
||||
class PlaintextResource : public Wt::WResource {
|
||||
public:
|
||||
void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) final;
|
||||
};
|
||||
|
||||
class DbResource : public Wt::WResource {
|
||||
public:
|
||||
DbResource(const std::string &db);
|
||||
void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) final;
|
||||
};
|
||||
|
||||
#endif // __RESTFUL_H__
|
@ -1,10 +1,11 @@
|
||||
#ifndef __SESSION_H__
|
||||
#define __SESSION_H__
|
||||
|
||||
#include "User.h"
|
||||
#include "model/User.h"
|
||||
#include <Wt/Auth/Login.h>
|
||||
#include <Wt/Dbo/Session.h>
|
||||
|
||||
using AuthInfo = Wt::Auth::Dbo::AuthInfo<User>;
|
||||
using UserDatabase = Wt::Auth::Dbo::UserDatabase<AuthInfo>;
|
||||
|
||||
class Session : public Wt::Dbo::Session {
|
||||
|
@ -1,4 +0,0 @@
|
||||
#include "User.h"
|
||||
#include <Wt/Dbo/Impl.h>
|
||||
|
||||
DBO_INSTANTIATE_TEMPLATES(User)
|
@ -1,22 +0,0 @@
|
||||
#ifndef __USER_H__
|
||||
#define __USER_H__
|
||||
|
||||
#include <Wt/Dbo/Types.h>
|
||||
#include <Wt/WGlobal.h>
|
||||
|
||||
class User;
|
||||
using AuthInfo = Wt::Auth::Dbo::AuthInfo<User>;
|
||||
|
||||
class User {
|
||||
public:
|
||||
template <class Action>
|
||||
void persist(Action &a) {
|
||||
// Wt::Dbo::field(a, name, "name");
|
||||
}
|
||||
// std::string name;
|
||||
// int age;
|
||||
};
|
||||
|
||||
DBO_EXTERN_TEMPLATES(User)
|
||||
|
||||
#endif // __USER_H__
|
@ -1,13 +1,22 @@
|
||||
#include "WebApplication.h"
|
||||
#include "BlogApplication.h"
|
||||
#include "BoostLog.h"
|
||||
#include "Hello.h"
|
||||
#include "Restful.h"
|
||||
#include "Session.h"
|
||||
#include "model/BlogSession.h"
|
||||
#include <Wt/Dbo/SqlConnectionPool.h>
|
||||
#include <Wt/WServer.h>
|
||||
|
||||
static std::unique_ptr<Wt::WApplication> createApplication(const Wt::WEnvironment &env) {
|
||||
return std::make_unique<Hello>(env, false);
|
||||
}
|
||||
|
||||
std::unique_ptr<Wt::WApplication> createBlogApplication(const Wt::WEnvironment &env,
|
||||
Wt::Dbo::SqlConnectionPool *blogDb) {
|
||||
return std::make_unique<BlogApplication>(env, *blogDb);
|
||||
}
|
||||
|
||||
static std::unique_ptr<Wt::WApplication> createWidgetSet(const Wt::WEnvironment &env) {
|
||||
return std::make_unique<Hello>(env, true);
|
||||
}
|
||||
@ -19,9 +28,16 @@ WebApplication::WebApplication() {
|
||||
args.push_back("--http-listen=127.0.0.1:8855");
|
||||
// --docroot=. --no-compression --http-listen 127.0.0.1:8855
|
||||
m_server = std::make_unique<Wt::WServer>("./build", args);
|
||||
m_server->addEntryPoint(Wt::EntryPointType::Application, createApplication);
|
||||
m_server->addEntryPoint(Wt::EntryPointType::Application, createApplication, "/hello");
|
||||
|
||||
auto blogDb = BlogSession::createConnectionPool(m_server->appRoot() + "blog.db");
|
||||
m_server->addEntryPoint(Wt::EntryPointType::Application,
|
||||
std::bind(&createBlogApplication, std::placeholders::_1, blogDb.get()), "/blog");
|
||||
m_server->addEntryPoint(Wt::EntryPointType::WidgetSet, createWidgetSet, "/gui/hello.js");
|
||||
Session::configureAuth();
|
||||
m_server->addResource(std::make_shared<JsonResource>(), "/json");
|
||||
m_server->addResource(std::make_shared<PlaintextResource>(), "/plaintext");
|
||||
m_server->addResource(std::make_shared<DbResource>("database.sqlite"), "/db");
|
||||
m_thread = std::thread(&WebApplication::run, this);
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
|
16
WebApplication/model/BlogSession.cpp
Normal file
16
WebApplication/model/BlogSession.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "BlogSession.h"
|
||||
#include <Wt/Dbo/FixedSqlConnectionPool.h>
|
||||
|
||||
BlogSession::BlogSession(Wt::Dbo::SqlConnectionPool &connectionPool)
|
||||
: m_connectionPool(connectionPool), m_users(*this) {
|
||||
}
|
||||
|
||||
std::unique_ptr<Wt::Dbo::SqlConnectionPool> BlogSession::createConnectionPool(const std::string &sqlite3) {
|
||||
auto connection = std::make_unique<Wt::Dbo::backend::Sqlite3>(sqlite3);
|
||||
|
||||
connection->setProperty("show-queries", "true");
|
||||
connection->setDateTimeStorage(Wt::Dbo::SqlDateTimeType::DateTime,
|
||||
Wt::Dbo::backend::DateTimeStorage::PseudoISO8601AsText);
|
||||
|
||||
return std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), 10);
|
||||
}
|
17
WebApplication/model/BlogSession.h
Normal file
17
WebApplication/model/BlogSession.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef __BLOGSESSION_H__
|
||||
#define __BLOGSESSION_H__
|
||||
|
||||
#include "BlogUserDatabase.h"
|
||||
#include <Wt/Dbo/Session.h>
|
||||
#include <Wt/Dbo/backend/Sqlite3.h>
|
||||
|
||||
class BlogSession : public Wt::Dbo::Session {
|
||||
public:
|
||||
BlogSession(Wt::Dbo::SqlConnectionPool &connectionPool);
|
||||
static std::unique_ptr<Wt::Dbo::SqlConnectionPool> createConnectionPool(const std::string &sqlite3);
|
||||
|
||||
private:
|
||||
Wt::Dbo::SqlConnectionPool &m_connectionPool;
|
||||
BlogUserDatabase m_users;
|
||||
};
|
||||
#endif // __BLOGSESSION_H__
|
124
WebApplication/model/BlogUserDatabase.cpp
Normal file
124
WebApplication/model/BlogUserDatabase.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
#include "User.h"
|
||||
|
||||
#include "BlogUserDatabase.h"
|
||||
|
||||
#include <Wt/Auth/Identity.h>
|
||||
|
||||
class TransactionImpl : public Wt::Auth::AbstractUserDatabase::Transaction, public Wt::Dbo::Transaction {
|
||||
public:
|
||||
TransactionImpl(Wt::Dbo::Session &session) : Wt::Dbo::Transaction(session) {
|
||||
}
|
||||
|
||||
virtual ~TransactionImpl() noexcept(false) {
|
||||
}
|
||||
|
||||
virtual void commit() {
|
||||
Wt::Dbo::Transaction::commit();
|
||||
}
|
||||
|
||||
virtual void rollback() {
|
||||
Wt::Dbo::Transaction::rollback();
|
||||
}
|
||||
};
|
||||
|
||||
class InvalidUser : public std::runtime_error {
|
||||
public:
|
||||
InvalidUser(const std::string &id) : std::runtime_error("Invalid user: " + id) {
|
||||
}
|
||||
};
|
||||
|
||||
BlogUserDatabase::BlogUserDatabase(Wt::Dbo::Session &session) : m_session(session) {
|
||||
}
|
||||
|
||||
BlogUserDatabase::~BlogUserDatabase() {
|
||||
}
|
||||
|
||||
BlogUserDatabase::Transaction *BlogUserDatabase::startTransaction() {
|
||||
return new TransactionImpl(m_session);
|
||||
}
|
||||
|
||||
Wt::Auth::User BlogUserDatabase::findWithId(const std::string &id) const {
|
||||
getUser(id);
|
||||
|
||||
if (m_user)
|
||||
return Wt::Auth::User(id, *this);
|
||||
else
|
||||
return Wt::Auth::User();
|
||||
}
|
||||
|
||||
Wt::Auth::User BlogUserDatabase::findWithIdentity(const std::string &provider, const Wt::WString &identity) const {
|
||||
Wt::Dbo::Transaction t(m_session);
|
||||
if (provider == Wt::Auth::Identity::LoginName) {
|
||||
if (!m_user || m_user->name != identity) m_user = m_session.find<User>().where("name = ?").bind(identity);
|
||||
} else
|
||||
m_user = m_session.find<User>()
|
||||
.where("oauth_id = ?")
|
||||
.bind(identity.toUTF8())
|
||||
.where("oauth_provider = ?")
|
||||
.bind(provider);
|
||||
t.commit();
|
||||
|
||||
if (m_user)
|
||||
return Wt::Auth::User(std::to_string(m_user.id()), *this);
|
||||
else
|
||||
return Wt::Auth::User();
|
||||
}
|
||||
|
||||
void BlogUserDatabase::addIdentity(const Wt::Auth::User &user, const std::string &provider,
|
||||
const Wt::WString &identity) {
|
||||
WithUser find(*this, user);
|
||||
|
||||
if (provider == Wt::Auth::Identity::LoginName)
|
||||
m_user.modify()->name = identity;
|
||||
else {
|
||||
m_user.modify()->oAuthProvider = provider;
|
||||
m_user.modify()->oAuthId = identity.toUTF8();
|
||||
}
|
||||
}
|
||||
|
||||
void BlogUserDatabase::removeIdentity(const Wt::Auth::User &user, const std::string &provider) {
|
||||
WithUser find(*this, user);
|
||||
|
||||
if (provider == Wt::Auth::Identity::LoginName)
|
||||
m_user.modify()->name = "";
|
||||
else if (provider == m_user->oAuthProvider) {
|
||||
m_user.modify()->oAuthProvider = std::string();
|
||||
m_user.modify()->oAuthId = std::string();
|
||||
}
|
||||
}
|
||||
|
||||
Wt::WString BlogUserDatabase::identity(const Wt::Auth::User &user, const std::string &provider) const {
|
||||
WithUser find(*this, user);
|
||||
|
||||
if (provider == Wt::Auth::Identity::LoginName)
|
||||
return m_user->name;
|
||||
else if (provider == m_user->oAuthProvider)
|
||||
return Wt::WString(m_user->oAuthId);
|
||||
else
|
||||
return Wt::WString::Empty;
|
||||
}
|
||||
|
||||
void BlogUserDatabase::setLastLoginAttempt(const Wt::Auth::User &user, const Wt::WDateTime &t) {
|
||||
WithUser find(*this, user);
|
||||
|
||||
m_user.modify()->lastLoginAttempt = t;
|
||||
}
|
||||
|
||||
BlogUserDatabase::WithUser::WithUser(const BlogUserDatabase &self, const Wt::Auth::User &user)
|
||||
: transaction(self.m_session) {
|
||||
self.getUser(user.id());
|
||||
|
||||
if (!self.m_user) throw InvalidUser(user.id());
|
||||
}
|
||||
|
||||
BlogUserDatabase::WithUser::~WithUser() {
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
void BlogUserDatabase::getUser(const std::string &id) const {
|
||||
if (!m_user || std::to_string(m_user.id()) != id) {
|
||||
Wt::Dbo::Transaction t(m_session);
|
||||
m_user = m_session.find<User>().where("id = ?").bind(User::stringToId(id));
|
||||
t.commit();
|
||||
}
|
||||
}
|
35
WebApplication/model/BlogUserDatabase.h
Normal file
35
WebApplication/model/BlogUserDatabase.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef __BLOGUSERDATABASE_H__
|
||||
#define __BLOGUSERDATABASE_H__
|
||||
|
||||
#include <Wt/Auth/AbstractUserDatabase.h>
|
||||
#include <Wt/Dbo/Transaction.h>
|
||||
#include <Wt/Dbo/Types.h>
|
||||
|
||||
class User;
|
||||
|
||||
class BlogUserDatabase : public Wt::Auth::AbstractUserDatabase {
|
||||
public:
|
||||
BlogUserDatabase(Wt::Dbo::Session &session);
|
||||
~BlogUserDatabase();
|
||||
Transaction *startTransaction() final;
|
||||
Wt::Auth::User findWithId(const std::string &id) const final;
|
||||
Wt::Auth::User findWithIdentity(const std::string &provider, const Wt::WString &identity) const final;
|
||||
void addIdentity(const Wt::Auth::User &user, const std::string &provider, const Wt::WString &identity) final;
|
||||
void removeIdentity(const Wt::Auth::User &user, const std::string &provider) final;
|
||||
Wt::WString identity(const Wt::Auth::User &user, const std::string &provider) const final;
|
||||
void setLastLoginAttempt(const Wt::Auth::User &user, const Wt::WDateTime &t) final;
|
||||
|
||||
protected:
|
||||
struct WithUser {
|
||||
WithUser(const BlogUserDatabase &self, const Wt::Auth::User &user);
|
||||
~WithUser();
|
||||
|
||||
Wt::Dbo::Transaction transaction;
|
||||
};
|
||||
void getUser(const std::string &id) const;
|
||||
|
||||
private:
|
||||
Wt::Dbo::Session &m_session;
|
||||
mutable Wt::Dbo::ptr<User> m_user;
|
||||
};
|
||||
#endif // __BLOGUSERDATABASE_H__
|
15
WebApplication/model/User.cpp
Normal file
15
WebApplication/model/User.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include "User.h"
|
||||
#include <Wt/Dbo/Impl.h>
|
||||
|
||||
DBO_INSTANTIATE_TEMPLATES(User)
|
||||
Wt::Dbo::dbo_traits<User>::IdType User::stringToId(const std::string &s) {
|
||||
std::size_t pos = std::string::npos;
|
||||
auto result = std::stoll(s, &pos);
|
||||
if (pos != s.size())
|
||||
return Wt::Dbo::dbo_traits<User>::invalidId();
|
||||
else
|
||||
return result;
|
||||
}
|
||||
|
||||
User::User() : role(Visitor), failedLoginAttempts(0) {
|
||||
}
|
38
WebApplication/model/User.h
Normal file
38
WebApplication/model/User.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef __USER_H__
|
||||
#define __USER_H__
|
||||
|
||||
#include <Wt/Dbo/Types.h>
|
||||
#include <Wt/Dbo/WtSqlTraits.h>
|
||||
#include <Wt/WDateTime.h>
|
||||
#include <Wt/WGlobal.h>
|
||||
#include <Wt/WString.h>
|
||||
|
||||
class User {
|
||||
public:
|
||||
enum Role {
|
||||
Visitor = 0,
|
||||
Admin = 1,
|
||||
};
|
||||
User();
|
||||
|
||||
static Wt::Dbo::dbo_traits<User>::IdType stringToId(const std::string &s);
|
||||
Wt::WString name;
|
||||
Role role;
|
||||
int failedLoginAttempts;
|
||||
Wt::WDateTime lastLoginAttempt;
|
||||
std::string oAuthId;
|
||||
std::string oAuthProvider;
|
||||
|
||||
template <class Action>
|
||||
void persist(Action &a) {
|
||||
Wt::Dbo::field(a, name, "name");
|
||||
Wt::Dbo::field(a, failedLoginAttempts, "failed_login_attempts");
|
||||
Wt::Dbo::field(a, lastLoginAttempt, "last_login_attempt");
|
||||
Wt::Dbo::field(a, oAuthId, "oauth_id");
|
||||
Wt::Dbo::field(a, oAuthProvider, "oauth_provider");
|
||||
}
|
||||
};
|
||||
|
||||
DBO_EXTERN_TEMPLATES(User)
|
||||
|
||||
#endif // __USER_H__
|
14
WebApplication/view/BlogLoginWidget.cpp
Normal file
14
WebApplication/view/BlogLoginWidget.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "BlogLoginWidget.h"
|
||||
#include "model/BlogSession.h"
|
||||
|
||||
BlogLoginWidget::BlogLoginWidget(BlogSession &session, const std::string &basePath) : AuthWidget(session.login()) {
|
||||
setInline(true);
|
||||
|
||||
auto model = std::make_unique<Wt::Auth::AuthModel>(session.passwordAuth()->baseAuth(), session.users());
|
||||
model->addPasswordAuth(session.passwordAuth());
|
||||
model->addOAuth(session.oAuth());
|
||||
|
||||
setModel(std::move(model));
|
||||
|
||||
setInternalBasePath(basePath + "login");
|
||||
}
|
12
WebApplication/view/BlogLoginWidget.h
Normal file
12
WebApplication/view/BlogLoginWidget.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __BLOGLOGINWIDGET_H__
|
||||
#define __BLOGLOGINWIDGET_H__
|
||||
|
||||
#include <Wt/Auth/AuthWidget.h>
|
||||
|
||||
class BlogSession;
|
||||
|
||||
class BlogLoginWidget : public Wt::Auth::AuthWidget {
|
||||
public:
|
||||
BlogLoginWidget(BlogSession &session, const std::string &basePath);
|
||||
};
|
||||
#endif // __BLOGLOGINWIDGET_H__
|
115
WebApplication/view/BlogView.cpp
Normal file
115
WebApplication/view/BlogView.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
#include "BlogView.h"
|
||||
#include "EditUsers.h"
|
||||
#include "model/BlogSession.h"
|
||||
#include <Wt/WApplication.h>
|
||||
#include <Wt/WContainerWidget.h>
|
||||
#include <Wt/WText.h>
|
||||
|
||||
class BlogImpl : public Wt::WContainerWidget {
|
||||
public:
|
||||
BlogImpl(const std::string &basePath, Wt::Dbo::SqlConnectionPool &connectionPool, const std::string &rssFeedUrl,
|
||||
BlogView *blogView)
|
||||
: m_basePath(basePath), m_rssFeedUrl(rssFeedUrl), m_session(connectionPool) {
|
||||
Wt::WApplication *app = Wt::WApplication::instance();
|
||||
|
||||
app->messageResourceBundle().use(Wt::WApplication::appRoot() + "blog");
|
||||
app->useStyleSheet("/css/blog.css");
|
||||
app->useStyleSheet("/css/asciidoc.css");
|
||||
app->internalPathChanged().connect(this, &BlogImpl::handlePathChange);
|
||||
|
||||
m_loginStatus = this->addWidget(std::make_unique<Wt::WTemplate>(tr("blog-login-status")));
|
||||
m_panel = this->addWidget(std::make_unique<Wt::WStackedWidget>());
|
||||
m_items = this->addWidget(std::make_unique<Wt::WContainerWidget>());
|
||||
|
||||
m_session.login().changed().connect(this, &BlogImpl::onUserChanged);
|
||||
|
||||
auto loginWidget = std::make_unique<BlogLoginWidget>(m_session, basePath);
|
||||
m_loginWidget = loginWidget.get();
|
||||
m_loginWidget->hide();
|
||||
|
||||
auto loginLink = std::make_unique<Wt::WText>(tr("login"));
|
||||
auto lPtr = loginLink.get();
|
||||
loginLink->setStyleClass("link");
|
||||
loginLink->clicked().connect(loginWidget_, &WWidget::show);
|
||||
loginLink->clicked().connect(lPtr, &WWidget::hide);
|
||||
|
||||
auto registerLink = std::make_unique<Wt::WText>(tr("Wt.Auth.register"));
|
||||
registerLink->setStyleClass("link");
|
||||
registerLink->clicked().connect(loginWidget_, &BlogLoginWidget::registerNewUser);
|
||||
|
||||
auto archiveLink =
|
||||
std::make_unique<Wt::WAnchor>(Wt::WLink(Wt::LinkType::InternalPath, basePath_ + "all"), tr("archive"));
|
||||
|
||||
loginStatus_->bindWidget("login", std::move(loginWidget));
|
||||
loginStatus_->bindWidget("login-link", std::move(loginLink));
|
||||
loginStatus_->bindWidget("register-link", std::move(registerLink));
|
||||
loginStatus_->bindString("feed-url", rssFeedUrl_);
|
||||
loginStatus_->bindWidget("archive-link", std::move(archiveLink));
|
||||
|
||||
onUserChanged();
|
||||
|
||||
loginWidget_->processEnvironment();
|
||||
}
|
||||
|
||||
protected:
|
||||
void handlePathChange(const std::string &) {
|
||||
Wt::WApplication *app = Wt::WApplication::instance();
|
||||
|
||||
if (app->internalPathMatches(basePath_)) {
|
||||
dbo::Transaction t(session_);
|
||||
|
||||
std::string path = app->internalPathNextPart(basePath_);
|
||||
|
||||
items_->clear();
|
||||
|
||||
if (users_) {
|
||||
users_ = 0;
|
||||
}
|
||||
|
||||
if (path.empty())
|
||||
showPosts(session_
|
||||
.find<Post>("where state = ? "
|
||||
"order by date desc "
|
||||
"limit 10")
|
||||
.bind(Post::Published),
|
||||
items_);
|
||||
|
||||
else if (path == "author") {
|
||||
std::string author = app->internalPathNextPart(basePath_ + path + '/');
|
||||
dbo::ptr<User> user = findUser(author);
|
||||
|
||||
if (user)
|
||||
showPosts(user);
|
||||
else
|
||||
showError(tr("blog-no-author").arg(author));
|
||||
} else if (path == "edituser") {
|
||||
editUser(app->internalPathNextPart(basePath_ + path + '/'));
|
||||
} else if (path == "all") {
|
||||
showArchive(items_);
|
||||
} else {
|
||||
std::string remainder = app->internalPath().substr(basePath_.length());
|
||||
showPostsByDateTopic(remainder, items_);
|
||||
}
|
||||
|
||||
t.commit();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_basePath, m_rssFeedUrl;
|
||||
BlogSession m_session; BlogLoginWidget *m_loginWidget=nullptr;
|
||||
Wt::WStackedWidget *m_panel = nullptr;
|
||||
Wt::WTemplate *m_authorPanel = nullptr;
|
||||
EditUsers *m_users = nullptr;
|
||||
EditUser *m_userEditor = nullptr;
|
||||
Wt::WTemplate *m_mustLoginWarning = nullptr;
|
||||
Wt::WTemplate *m_mustBeAdministratorWarning = nullptr;
|
||||
Wt::WTemplate *m_invalidUser = nullptr;
|
||||
Wt::WTemplate *m_loginStatus = nullptr;
|
||||
WContainerWidget *m_items = nullptr;
|
||||
};
|
||||
|
||||
BlogView::BlogView(const std::string &basePath, Wt::Dbo::SqlConnectionPool &db, const std::string &rssFeedUrl)
|
||||
: WCompositeWidget(), m_userChanged() {
|
||||
m_impl = setImplementation(std::make_unique<BlogImpl>(basePath, db, rssFeedUrl, this));
|
||||
}
|
16
WebApplication/view/BlogView.h
Normal file
16
WebApplication/view/BlogView.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef __BLOGVIEW_H__
|
||||
#define __BLOGVIEW_H__
|
||||
|
||||
#include <Wt/WCompositeWidget.h>
|
||||
|
||||
class BlogImpl;
|
||||
|
||||
class BlogView : public Wt::WCompositeWidget {
|
||||
public:
|
||||
BlogView(const std::string &basePath, Wt::Dbo::SqlConnectionPool &db, const std::string &rssFeedUrl);
|
||||
|
||||
private:
|
||||
BlogImpl *m_impl;
|
||||
Wt::Signal<Wt::WString> m_userChanged;
|
||||
};
|
||||
#endif // __BLOGVIEW_H__
|
40
WebApplication/view/EditUsers.cpp
Normal file
40
WebApplication/view/EditUsers.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include "EditUsers.h"
|
||||
#include <Wt/WLineEdit.h>
|
||||
#include <Wt/WPushButton.h>
|
||||
|
||||
EditUsers::EditUsers(Wt::Dbo::Session &aSession, const std::string &basePath)
|
||||
: m_session(aSession), m_basePath(basePath) {
|
||||
setStyleClass("user-editor");
|
||||
setTemplateText(tr("edit-users-list"));
|
||||
auto limitEdit = std::make_unique<Wt::WLineEdit>();
|
||||
auto goLimit = std::make_unique<Wt::WPushButton>(tr("go-limit"));
|
||||
goLimit->clicked().connect(this, &EditUsers::limitList);
|
||||
|
||||
m_limitEdit = bindWidget("limit-edit", std::move(limitEdit));
|
||||
bindWidget("limit-button", std::move(goLimit));
|
||||
limitList();
|
||||
}
|
||||
|
||||
void EditUsers::limitList() {
|
||||
auto listPtr = std::make_unique<Wt::WContainerWidget>();
|
||||
auto list = listPtr.get();
|
||||
bindWidget("user-list", std::move(listPtr));
|
||||
|
||||
typedef Wt::Dbo ::collection<Wt::Dbo ::ptr<User>> UserList;
|
||||
Wt::Dbo ::Transaction t(m_session);
|
||||
UserList users = m_session.find<User>().where("name like ?").bind("%" + m_limitEdit->text() + "%").orderBy("name");
|
||||
|
||||
for (auto user : users) {
|
||||
Wt::WText *t = list->addWidget(std::make_unique<Wt::WText>(user->name));
|
||||
t->setStyleClass("link");
|
||||
list->addWidget(std::make_unique<Wt::WBreak>());
|
||||
t->clicked().connect(std::bind(&EditUsers::onUserClicked, this, user.id()));
|
||||
}
|
||||
if (!users.size()) list->addWidget(std::make_unique<Wt::WText>(tr("no-users-found")));
|
||||
}
|
||||
|
||||
EditUser::EditUser(Wt::Dbo::Session &aSession) : WTemplate(tr("edit-user")), session_(aSession) {
|
||||
auto roleButton = std::make_unique<Wt::WPushButton>();
|
||||
roleButton_ = bindWidget("role-button", std::move(roleButton));
|
||||
roleButton_->clicked().connect(this, &EditUser::switchRole);
|
||||
}
|
35
WebApplication/view/EditUsers.h
Normal file
35
WebApplication/view/EditUsers.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef __EDITUSERS_H__
|
||||
#define __EDITUSERS_H__
|
||||
|
||||
#include "model/User.h"
|
||||
#include <Wt/Dbo/Types.h>
|
||||
#include <Wt/WTemplate.h>
|
||||
|
||||
class EditUsers : public Wt::WTemplate {
|
||||
public:
|
||||
EditUsers(Wt::Dbo::Session &aSession, const std::string &basePath);
|
||||
|
||||
private:
|
||||
void onUserClicked(Wt::Dbo::dbo_traits<User>::IdType id);
|
||||
void limitList();
|
||||
|
||||
Wt::Dbo::Session &m_session;
|
||||
std::string m_basePath;
|
||||
Wt::WLineEdit *m_limitEdit;
|
||||
};
|
||||
|
||||
class EditUser : public Wt::WTemplate {
|
||||
public:
|
||||
EditUser(Wt::Dbo::Session &aSession);
|
||||
void switchUser(Wt::Dbo::ptr<User> target);
|
||||
|
||||
private:
|
||||
void bindTemplate();
|
||||
void switchRole();
|
||||
|
||||
Wt::Dbo::Session &session_;
|
||||
Wt::Dbo::ptr<User> target_;
|
||||
Wt::WPushButton *roleButton_;
|
||||
};
|
||||
|
||||
#endif // __EDITUSERS_H__
|
Loading…
Reference in New Issue
Block a user