add blog code.
Some checks failed
Deploy / PullDocker (push) Successful in 4s
Deploy / Build (push) Failing after 1m34s
Deploy Docker Images / Docusaurus build and Server deploy (push) Successful in 14s

This commit is contained in:
luocai 2024-11-01 19:05:20 +08:00
parent 969446d85f
commit 27ffee57be
25 changed files with 672 additions and 33 deletions

View File

@ -23,6 +23,7 @@ public:
BUILD_SETTING(uint16_t, Port, 8081); BUILD_SETTING(uint16_t, Port, 8081);
BUILD_SETTING(uint32_t, Threads, std::thread::hardware_concurrency()); BUILD_SETTING(uint32_t, Threads, std::thread::hardware_concurrency());
BUILD_SETTING(std::string, DocumentRoot, "."); BUILD_SETTING(std::string, DocumentRoot, ".");
BUILD_SETTING(std::string, Sqlte3Path, "database.sqlite");
BUILD_SETTING(std::string, HomeAssistantAccessToken, ""); BUILD_SETTING(std::string, HomeAssistantAccessToken, "");
BUILD_SETTING(std::string, Live2dModelsRoot, "./live2d"); BUILD_SETTING(std::string, Live2dModelsRoot, "./live2d");

View File

@ -61,7 +61,7 @@ int main(int argc, char const *argv[]) {
auto application = Singleton<Application>::instance<Construct>("settings.ini"); auto application = Singleton<Application>::instance<Construct>("settings.ini");
auto database = Singleton<Database>::instance<Construct>(); auto database = Singleton<Database>::instance<Construct>();
database->open("database.sqlite"); database->open(application->getSqlte3Path());
if (!std::filesystem::exists(application->getDocumentRoot())) { if (!std::filesystem::exists(application->getDocumentRoot())) {
LOG(fatal) << "document root: " << application->getDocumentRoot() << " is not exists..."; LOG(fatal) << "document root: " << application->getDocumentRoot() << " is not exists...";

View 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");
}

View 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__

View File

@ -1,13 +1,19 @@
add_library(WebApplication add_library(WebApplication
WebApplication.h WebApplication.cpp WebApplication.h WebApplication.cpp
BlogApplication.h BlogApplication.cpp
Hello.h Hello.cpp Hello.h Hello.cpp
Restful.h Restful.cpp
Dialog.h Dialog.cpp Dialog.h Dialog.cpp
Session.h Session.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 target_include_directories(WebApplication
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${WT_INCLUDE_DIR} PRIVATE ${WT_INCLUDE_DIR}
) )

View File

@ -1,4 +1,5 @@
#include "Dialog.h" #include "Dialog.h"
#include <Wt/WLineEdit.h>
#include <Wt/WMessageBox.h> #include <Wt/WMessageBox.h>
#include <Wt/WPushButton.h> #include <Wt/WPushButton.h>
@ -22,6 +23,9 @@ Dialog::Dialog() {
button = buttons->addWidget(std::make_unique<Wt::WPushButton>("Discard")); button = buttons->addWidget(std::make_unique<Wt::WPushButton>("Discard"));
button->clicked().connect(this, &Dialog::messageBox4); 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 = addWidget(std::make_unique<Wt::WContainerWidget>());
textdiv->setStyleClass("text"); textdiv->setStyleClass("text");
m_status = textdiv->addWidget(std::make_unique<Wt::WText>("Go ahead...")); m_status = textdiv->addWidget(std::make_unique<Wt::WText>("Go ahead..."));
@ -74,6 +78,27 @@ void Dialog::messageBox4() {
Wt::TimingFunction::Linear, 250)); 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) { void Dialog::messageBoxDone(Wt::StandardButton result) {
switch (result) { switch (result) {
case Wt::StandardButton::Ok: case Wt::StandardButton::Ok:

View File

@ -13,6 +13,7 @@ protected:
void messageBox2(); void messageBox2();
void messageBox3(); void messageBox3();
void messageBox4(); void messageBox4();
void custom();
void messageBoxDone(Wt::StandardButton result); void messageBoxDone(Wt::StandardButton result);
private: private:

View 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
View 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__

View File

@ -1,10 +1,11 @@
#ifndef __SESSION_H__ #ifndef __SESSION_H__
#define __SESSION_H__ #define __SESSION_H__
#include "User.h" #include "model/User.h"
#include <Wt/Auth/Login.h> #include <Wt/Auth/Login.h>
#include <Wt/Dbo/Session.h> #include <Wt/Dbo/Session.h>
using AuthInfo = Wt::Auth::Dbo::AuthInfo<User>;
using UserDatabase = Wt::Auth::Dbo::UserDatabase<AuthInfo>; using UserDatabase = Wt::Auth::Dbo::UserDatabase<AuthInfo>;
class Session : public Wt::Dbo::Session { class Session : public Wt::Dbo::Session {

View File

@ -1,4 +0,0 @@
#include "User.h"
#include <Wt/Dbo/Impl.h>
DBO_INSTANTIATE_TEMPLATES(User)

View File

@ -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__

View File

@ -1,13 +1,22 @@
#include "WebApplication.h" #include "WebApplication.h"
#include "BlogApplication.h"
#include "BoostLog.h" #include "BoostLog.h"
#include "Hello.h" #include "Hello.h"
#include "Restful.h"
#include "Session.h" #include "Session.h"
#include "model/BlogSession.h"
#include <Wt/Dbo/SqlConnectionPool.h>
#include <Wt/WServer.h> #include <Wt/WServer.h>
static std::unique_ptr<Wt::WApplication> createApplication(const Wt::WEnvironment &env) { static std::unique_ptr<Wt::WApplication> createApplication(const Wt::WEnvironment &env) {
return std::make_unique<Hello>(env, false); 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) { static std::unique_ptr<Wt::WApplication> createWidgetSet(const Wt::WEnvironment &env) {
return std::make_unique<Hello>(env, true); return std::make_unique<Hello>(env, true);
} }
@ -19,9 +28,16 @@ WebApplication::WebApplication() {
args.push_back("--http-listen=127.0.0.1:8855"); args.push_back("--http-listen=127.0.0.1:8855");
// --docroot=. --no-compression --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 = 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"); m_server->addEntryPoint(Wt::EntryPointType::WidgetSet, createWidgetSet, "/gui/hello.js");
Session::configureAuth(); 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); m_thread = std::thread(&WebApplication::run, this);
} catch (const std::exception &e) { } catch (const std::exception &e) {

View 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);
}

View 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__

View 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();
}
}

View 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__

View 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) {
}

View 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__

View 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");
}

View 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__

View 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));
}

View 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__

View 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);
}

View 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__