remove blog example source.
This commit is contained in:
parent
f12ecefe14
commit
a9640bbda5
@ -79,8 +79,7 @@ int main(int argc, char const *argv[]) {
|
|||||||
corpContext->start();
|
corpContext->start();
|
||||||
auto live2d = std::make_shared<Live2dBackend>();
|
auto live2d = std::make_shared<Live2dBackend>();
|
||||||
|
|
||||||
LOG(info) << "hardware_concurrency: " << std::thread::hardware_concurrency()
|
LOG(info) << "hardware_concurrency: " << std::thread::hardware_concurrency() << ",threads: " << application->getThreads();
|
||||||
<< ",threads: " << application->getThreads();
|
|
||||||
LOG(info) << "working directory: " << prefix.generic_string();
|
LOG(info) << "working directory: " << prefix.generic_string();
|
||||||
LOG(info) << "server: " << application->getServer() << ",port: " << application->getPort();
|
LOG(info) << "server: " << application->getServer() << ",port: " << application->getPort();
|
||||||
LOG(info) << "document root: " << application->getDocumentRoot();
|
LOG(info) << "document root: " << application->getDocumentRoot();
|
||||||
@ -102,7 +101,7 @@ int main(int argc, char const *argv[]) {
|
|||||||
|
|
||||||
auto udpServer = std::make_shared<UdpServer>(application->ioContext());
|
auto udpServer = std::make_shared<UdpServer>(application->ioContext());
|
||||||
auto mediaServer = std::make_shared<MediaServer>(554, false);
|
auto mediaServer = std::make_shared<MediaServer>(554, false);
|
||||||
auto webApp = std::make_unique<WebApplication>();
|
auto webApp = Singleton<WebApplication>::instance<Construct>();
|
||||||
|
|
||||||
using namespace boost::asio::ip;
|
using namespace boost::asio::ip;
|
||||||
auto proxyAddress = make_address(application->getServer());
|
auto proxyAddress = make_address(application->getServer());
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
#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) {
|
|
||||||
messageResourceBundle().use(Wt::WApplication::appRoot() + "blog");
|
|
||||||
useStyleSheet("/css/blog.css");
|
|
||||||
useStyleSheet("/css/asciidoc.css");
|
|
||||||
|
|
||||||
|
|
||||||
root()->addWidget(std::make_unique<BlogView>("/", blogDb, FeedUrl));
|
|
||||||
useStyleSheet("css/blogexample.css");
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
#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,23 +1,10 @@
|
|||||||
add_library(WebApplication
|
add_library(WebApplication
|
||||||
WebApplication.h WebApplication.cpp
|
WebApplication.h WebApplication.cpp
|
||||||
BlogApplication.h BlogApplication.cpp
|
User.h User.cpp
|
||||||
Hello.h Hello.cpp
|
Hello.h Hello.cpp
|
||||||
Restful.h Restful.cpp
|
Restful.h Restful.cpp
|
||||||
Dialog.h Dialog.cpp
|
Dialog.h Dialog.cpp
|
||||||
Session.h Session.cpp
|
Session.h Session.cpp
|
||||||
model/asciidoc.h model/asciidoc.cpp
|
|
||||||
model/BlogSession.h model/BlogSession.cpp
|
|
||||||
model/BlogUserDatabase.h model/BlogUserDatabase.cpp
|
|
||||||
model/Comment.h model/Comment.cpp
|
|
||||||
model/Post.h model/Post.cpp
|
|
||||||
model/Tag.h model/Tag.cpp
|
|
||||||
model/Token.h model/Token.cpp
|
|
||||||
model/User.h model/User.cpp
|
|
||||||
view/BlogLoginWidget.h view/BlogLoginWidget.cpp
|
|
||||||
view/BlogView.h view/BlogView.cpp
|
|
||||||
view/CommentView.h view/CommentView.cpp
|
|
||||||
view/EditUsers.h view/EditUsers.cpp
|
|
||||||
view/PostView.h view/PostView.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(WebApplication
|
target_include_directories(WebApplication
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "BoostLog.h"
|
#include "BoostLog.h"
|
||||||
#include "Dialog.h"
|
#include "Dialog.h"
|
||||||
#include "Session.h"
|
#include "Session.h"
|
||||||
|
#include "WebApplication.h"
|
||||||
#include <Wt/Auth/AuthService.h>
|
#include <Wt/Auth/AuthService.h>
|
||||||
#include <Wt/Auth/AuthWidget.h>
|
#include <Wt/Auth/AuthWidget.h>
|
||||||
#include <Wt/Auth/Identity.h>
|
#include <Wt/Auth/Identity.h>
|
||||||
@ -57,8 +58,9 @@ Hello::Hello(const Wt::WEnvironment &env, bool embedded) : Wt::WApplication(env)
|
|||||||
b->clicked().connect(this, &Hello::greet);
|
b->clicked().connect(this, &Hello::greet);
|
||||||
m_nameEdit->enterPressed().connect(this, &Hello::greet);
|
m_nameEdit->enterPressed().connect(this, &Hello::greet);
|
||||||
|
|
||||||
auto authWidget = std::make_unique<Wt::Auth::AuthWidget>(Session::auth(), m_session->users(), m_session->login());
|
auto app = Amass::Singleton<WebApplication>::instance();
|
||||||
authWidget->model()->addPasswordAuth(&Session::passwordAuth());
|
auto authWidget = std::make_unique<Wt::Auth::AuthWidget>(app->authService(), m_session->users(), m_session->login());
|
||||||
|
authWidget->model()->addPasswordAuth(&app->passwordService());
|
||||||
authWidget->setRegistrationEnabled(true);
|
authWidget->setRegistrationEnabled(true);
|
||||||
authWidget->processEnvironment();
|
authWidget->processEnvironment();
|
||||||
top->addWidget(std::move(authWidget));
|
top->addWidget(std::move(authWidget));
|
||||||
|
@ -10,10 +10,6 @@
|
|||||||
#include <Wt/Auth/PasswordVerifier.h>
|
#include <Wt/Auth/PasswordVerifier.h>
|
||||||
#include <Wt/Dbo/backend/Sqlite3.h>
|
#include <Wt/Dbo/backend/Sqlite3.h>
|
||||||
|
|
||||||
Wt::Auth::AuthService myAuthService;
|
|
||||||
Wt::Auth::PasswordService myPasswordService(myAuthService);
|
|
||||||
std::vector<std::unique_ptr<Wt::Auth::OAuthService>> myOAuthServices;
|
|
||||||
|
|
||||||
Session::Session(const std::string &sqliteDb) {
|
Session::Session(const std::string &sqliteDb) {
|
||||||
auto connection = std::make_unique<Wt::Dbo::backend::Sqlite3>(sqliteDb);
|
auto connection = std::make_unique<Wt::Dbo::backend::Sqlite3>(sqliteDb);
|
||||||
|
|
||||||
@ -46,35 +42,3 @@ Wt::Auth::AbstractUserDatabase &Session::users() {
|
|||||||
Wt::Auth::Login &Session::login() {
|
Wt::Auth::Login &Session::login() {
|
||||||
return m_login;
|
return m_login;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::configureAuth() {
|
|
||||||
myAuthService.setAuthTokensEnabled(true, "logincookie");
|
|
||||||
// myAuthService.setEmailVerificationEnabled(true);
|
|
||||||
// myAuthService.setEmailVerificationRequired(true);
|
|
||||||
|
|
||||||
auto verifier = std::make_unique<Wt::Auth::PasswordVerifier>();
|
|
||||||
verifier->addHashFunction(std::make_unique<Wt::Auth::BCryptHashFunction>(7));
|
|
||||||
myPasswordService.setVerifier(std::move(verifier));
|
|
||||||
myPasswordService.setPasswordThrottle(std::make_unique<Wt::Auth::AuthThrottle>());
|
|
||||||
myPasswordService.setStrengthValidator(std::make_unique<Wt::Auth::PasswordStrengthValidator>());
|
|
||||||
|
|
||||||
if (Wt::Auth::GoogleService::configured()) {
|
|
||||||
myOAuthServices.push_back(std::make_unique<Wt::Auth::GoogleService>(myAuthService));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Wt::Auth::FacebookService::configured()) {
|
|
||||||
myOAuthServices.push_back(std::make_unique<Wt::Auth::FacebookService>(myAuthService));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &oAuthService : myOAuthServices) {
|
|
||||||
oAuthService->generateRedirectEndpoint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Wt::Auth::AuthService &Session::auth() {
|
|
||||||
return myAuthService;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Wt::Auth::PasswordService &Session::passwordAuth() {
|
|
||||||
return myPasswordService;
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
#ifndef __SESSION_H__
|
#ifndef __SESSION_H__
|
||||||
#define __SESSION_H__
|
#define __SESSION_H__
|
||||||
|
|
||||||
#include "model/User.h"
|
#include "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 {
|
||||||
@ -14,9 +13,6 @@ public:
|
|||||||
~Session();
|
~Session();
|
||||||
Wt::Auth::AbstractUserDatabase &users();
|
Wt::Auth::AbstractUserDatabase &users();
|
||||||
Wt::Auth::Login &login();
|
Wt::Auth::Login &login();
|
||||||
static void configureAuth();
|
|
||||||
static const Wt::Auth::AuthService &auth();
|
|
||||||
static const Wt::Auth::PasswordService &passwordAuth();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<UserDatabase> m_users;
|
std::unique_ptr<UserDatabase> m_users;
|
||||||
|
5
WebApplication/User.cpp
Normal file
5
WebApplication/User.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#include "User.h"
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
|
#include <Wt/Auth/Dbo/AuthInfo.h>
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(User)
|
17
WebApplication/User.h
Normal file
17
WebApplication/User.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef __USER_H__
|
||||||
|
#define __USER_H__
|
||||||
|
|
||||||
|
#include <Wt/Auth/Dbo/AuthInfo.h>
|
||||||
|
|
||||||
|
class User;
|
||||||
|
using AuthInfo = Wt::Auth::Dbo::AuthInfo<User>;
|
||||||
|
|
||||||
|
class User {
|
||||||
|
public:
|
||||||
|
template <class Action>
|
||||||
|
void persist(Action &a) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
DBO_EXTERN_TEMPLATES(User);
|
||||||
|
|
||||||
|
#endif // __USER_H__
|
@ -1,10 +1,13 @@
|
|||||||
#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 "Restful.h"
|
||||||
#include "Session.h"
|
#include "Session.h"
|
||||||
#include "model/BlogSession.h"
|
#include <Wt/Auth/AuthService.h>
|
||||||
|
#include <Wt/Auth/HashFunction.h>
|
||||||
|
#include <Wt/Auth/PasswordService.h>
|
||||||
|
#include <Wt/Auth/PasswordStrengthValidator.h>
|
||||||
|
#include <Wt/Auth/PasswordVerifier.h>
|
||||||
#include <Wt/Dbo/SqlConnectionPool.h>
|
#include <Wt/Dbo/SqlConnectionPool.h>
|
||||||
#include <Wt/WServer.h>
|
#include <Wt/WServer.h>
|
||||||
|
|
||||||
@ -12,11 +15,6 @@ static std::unique_ptr<Wt::WApplication> createApplication(const Wt::WEnvironmen
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@ -28,16 +26,10 @@ WebApplication::WebApplication() {
|
|||||||
args.push_back("--docroot=./build");
|
args.push_back("--docroot=./build");
|
||||||
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
|
||||||
|
initializeAuthenticationService();
|
||||||
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, "/hello");
|
m_server->addEntryPoint(Wt::EntryPointType::Application, createApplication, "/hello");
|
||||||
|
|
||||||
BlogSession::configureAuth();
|
|
||||||
m_blogSqlConnectionPool = BlogSession::createConnectionPool(m_server->appRoot() + "database.sqlite");
|
|
||||||
m_server->addEntryPoint(Wt::EntryPointType::Application,
|
|
||||||
std::bind(&createBlogApplication, std::placeholders::_1, m_blogSqlConnectionPool.get()),
|
|
||||||
"/blog");
|
|
||||||
m_server->addEntryPoint(Wt::EntryPointType::WidgetSet, createWidgetSet, "/gui/hello.js");
|
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<JsonResource>(), "/json");
|
||||||
m_server->addResource(std::make_shared<PlaintextResource>(), "/plaintext");
|
m_server->addResource(std::make_shared<PlaintextResource>(), "/plaintext");
|
||||||
m_server->addResource(std::make_shared<DbResource>("database.sqlite"), "/db");
|
m_server->addResource(std::make_shared<DbResource>("database.sqlite"), "/db");
|
||||||
@ -49,6 +41,24 @@ WebApplication::WebApplication() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WebApplication::~WebApplication() {
|
WebApplication::~WebApplication() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebApplication::initializeAuthenticationService() {
|
||||||
|
m_authService = std::make_unique<Wt::Auth::AuthService>();
|
||||||
|
m_authService->setAuthTokensEnabled(true, "logincookie");
|
||||||
|
m_passwordService = std::make_unique<Wt::Auth::PasswordService>(*m_authService);
|
||||||
|
|
||||||
|
auto verifier = std::make_unique<Wt::Auth::PasswordVerifier>();
|
||||||
|
verifier->addHashFunction(std::make_unique<Wt::Auth::BCryptHashFunction>(7));
|
||||||
|
m_passwordService->setVerifier(std::move(verifier));
|
||||||
|
m_passwordService->setPasswordThrottle(std::make_unique<Wt::Auth::AuthThrottle>());
|
||||||
|
m_passwordService->setStrengthValidator(std::make_unique<Wt::Auth::PasswordStrengthValidator>());
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wt::Auth::AuthService &WebApplication::authService() {
|
||||||
|
return *m_authService;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wt::Auth::PasswordService &WebApplication::passwordService() {
|
||||||
|
return *m_passwordService;
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#define __WEBAPPLICATION_H__
|
#define __WEBAPPLICATION_H__
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include "Singleton.h"
|
||||||
|
|
||||||
namespace Wt {
|
namespace Wt {
|
||||||
class WServer;
|
class WServer;
|
||||||
@ -10,16 +10,32 @@ class WServer;
|
|||||||
namespace Dbo {
|
namespace Dbo {
|
||||||
class SqlConnectionPool;
|
class SqlConnectionPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Auth {
|
||||||
|
class AuthService;
|
||||||
|
class PasswordService;
|
||||||
|
} // namespace Auth
|
||||||
|
|
||||||
}; // namespace Wt
|
}; // namespace Wt
|
||||||
|
|
||||||
class WebApplication {
|
class WebApplication {
|
||||||
|
friend class Amass::Singleton<WebApplication>;
|
||||||
public:
|
public:
|
||||||
WebApplication();
|
|
||||||
~WebApplication();
|
~WebApplication();
|
||||||
|
|
||||||
|
void initializeAuthenticationService();
|
||||||
|
const Wt::Auth::AuthService &authService();
|
||||||
|
const Wt::Auth::PasswordService &passwordService();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
WebApplication();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Wt::WServer> m_server;
|
std::unique_ptr<Wt::WServer> m_server;
|
||||||
std::unique_ptr<Wt::Dbo::SqlConnectionPool> m_blogSqlConnectionPool;
|
std::unique_ptr<Wt::Dbo::SqlConnectionPool> m_blogSqlConnectionPool;
|
||||||
|
|
||||||
|
std::unique_ptr<Wt::Auth::AuthService> m_authService;
|
||||||
|
std::unique_ptr<Wt::Auth::PasswordService> m_passwordService;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __WEBAPPLICATION_H__
|
#endif // __WEBAPPLICATION_H__
|
@ -1,296 +0,0 @@
|
|||||||
.asciidoc em {
|
|
||||||
font-style: italic;
|
|
||||||
color: #111111;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc strong {
|
|
||||||
font-weight: bold;
|
|
||||||
color: #111111;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc tt {
|
|
||||||
color: #111111;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc h1, .asciidoc h2, .asciidoc h3, .asciidoc h4, .asciidoc h5, .asciidoc h6 {
|
|
||||||
color: #111111;
|
|
||||||
font-family: sans-serif;
|
|
||||||
margin-top: 1.2em;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc h1, .asciidoc h2, .asciidoc h3 {
|
|
||||||
border-bottom: 2px solid silver;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc h2 {
|
|
||||||
padding-top: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc h3 {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc h3 + * {
|
|
||||||
clear: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.sectionbody {
|
|
||||||
font-family: serif;
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc hr {
|
|
||||||
border: 1px solid silver;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc p {
|
|
||||||
margin-top: 0.5em;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc ul, .asciidoc ol, .asciidoc li > p {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc pre {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
line-height: 14px;
|
|
||||||
font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.tableblock, .asciidoc div.imageblock, .asciidoc div.exampleblock,
|
|
||||||
.asciidoc div.verseblock, .asciidoc div.quoteblock, .asciidoc div.literalblock,
|
|
||||||
.asciidoc div.listingblock, .asciidoc div.sidebarblock,
|
|
||||||
.asciidoc div.admonitionblock {
|
|
||||||
margin-top: 1.5em;
|
|
||||||
margin-bottom: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.admonitionblock {
|
|
||||||
margin-top: 2.5em;
|
|
||||||
margin-bottom: 2.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.content { /* Block element content. */
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Block element titles. */
|
|
||||||
.asciidoc div.title, .asciidoc caption.title, .asciidoc div.sidebar-title {
|
|
||||||
color: #111111;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: left;
|
|
||||||
margin-top: 1.0em;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
.asciidoc div.title + * {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc td div.title:first-child {
|
|
||||||
margin-top: 0.0em;
|
|
||||||
}
|
|
||||||
.asciidoc div.content div.title:first-child {
|
|
||||||
margin-top: 0.0em;
|
|
||||||
}
|
|
||||||
.asciidoc div.content + div.title {
|
|
||||||
margin-top: 0.0em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.sidebarblock > div.sidebar-content {
|
|
||||||
background: #ffffee;
|
|
||||||
border: 1px solid silver;
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.listingblock > div.content {
|
|
||||||
border: 1px solid silver;
|
|
||||||
background: #f4f4f4;
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.quoteblock {
|
|
||||||
padding-left: 2.0em;
|
|
||||||
margin-right: 10%;
|
|
||||||
}
|
|
||||||
.asciidoc div.quoteblock > div.attribution {
|
|
||||||
padding-top: 0.5em;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.verseblock {
|
|
||||||
padding-left: 2.0em;
|
|
||||||
margin-right: 10%;
|
|
||||||
}
|
|
||||||
.asciidoc div.verseblock > div.content {
|
|
||||||
white-space: pre;
|
|
||||||
}
|
|
||||||
.asciidoc div.verseblock > div.attribution {
|
|
||||||
padding-top: 0.75em;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
|
|
||||||
.asciidoc div.verseblock + div.attribution {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.admonitionblock .icon {
|
|
||||||
vertical-align: top;
|
|
||||||
font-size: 1.1em;
|
|
||||||
font-weight: bold;
|
|
||||||
text-decoration: underline;
|
|
||||||
color: #111111;
|
|
||||||
padding-right: 0.5em;
|
|
||||||
}
|
|
||||||
.asciidoc div.admonitionblock td.content {
|
|
||||||
padding-left: 0.5em;
|
|
||||||
border-left: 2px solid silver;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.exampleblock > div.content {
|
|
||||||
border-left: 2px solid silver;
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.imageblock div.content { padding-left: 0; }
|
|
||||||
.asciidoc span.image img { border-style: none; }
|
|
||||||
.asciidoc a.image:visited { color: white; }
|
|
||||||
|
|
||||||
.asciidoc dl {
|
|
||||||
margin-top: 0.8em;
|
|
||||||
margin-bottom: 0.8em;
|
|
||||||
}
|
|
||||||
.asciidoc dt {
|
|
||||||
margin-top: 0.5em;
|
|
||||||
margin-bottom: 0;
|
|
||||||
font-style: normal;
|
|
||||||
color: #111111;
|
|
||||||
}
|
|
||||||
.asciidoc dd > *:first-child {
|
|
||||||
margin-top: 0.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc ul, ol {
|
|
||||||
list-style-position: outside;
|
|
||||||
}
|
|
||||||
.asciidoc ol.arabic {
|
|
||||||
list-style-type: decimal;
|
|
||||||
}
|
|
||||||
.asciidoc ol.loweralpha {
|
|
||||||
list-style-type: lower-alpha;
|
|
||||||
}
|
|
||||||
.asciidoc ol.upperalpha {
|
|
||||||
list-style-type: upper-alpha;
|
|
||||||
}
|
|
||||||
.asciidoc ol.lowerroman {
|
|
||||||
list-style-type: lower-roman;
|
|
||||||
}
|
|
||||||
.asciidoc ol.upperroman {
|
|
||||||
list-style-type: upper-roman;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.compact ul, .asciidoc div.compact ol,
|
|
||||||
.asciidoc div.compact p, .asciidoc div.compact p,
|
|
||||||
.asciidoc div.compact div, .asciidoc div.compact div {
|
|
||||||
margin-top: 0.1em;
|
|
||||||
margin-bottom: 0.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.tableblock > table {
|
|
||||||
border: 3px solid #527bbd;
|
|
||||||
}
|
|
||||||
.asciidoc thead {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.asciidoc tfoot {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.asciidoc td > div.verse {
|
|
||||||
white-space: pre;
|
|
||||||
}
|
|
||||||
.asciidoc p.table {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
/* Because the table frame attribute is overriden by CSS in most browsers. */
|
|
||||||
.asciidoc div.tableblock > table[frame="void"] {
|
|
||||||
border-style: none;
|
|
||||||
}
|
|
||||||
.asciidoc div.tableblock > table[frame="hsides"] {
|
|
||||||
border-left-style: none;
|
|
||||||
border-right-style: none;
|
|
||||||
}
|
|
||||||
.asciidoc div.tableblock > table[frame="vsides"] {
|
|
||||||
border-top-style: none;
|
|
||||||
border-bottom-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.asciidoc div.hdlist {
|
|
||||||
margin-top: 0.8em;
|
|
||||||
margin-bottom: 0.8em;
|
|
||||||
}
|
|
||||||
.asciidoc div.hdlist tr {
|
|
||||||
padding-bottom: 15px;
|
|
||||||
}
|
|
||||||
.asciidoc dt.hdlist1.strong, .asciidoc td.hdlist1.strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.asciidoc td.hdlist1 {
|
|
||||||
vertical-align: top;
|
|
||||||
font-style: normal;
|
|
||||||
padding-right: 0.8em;
|
|
||||||
color: #111111;
|
|
||||||
}
|
|
||||||
.asciidoc td.hdlist2 {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
.asciidoc div.hdlist.compact tr {
|
|
||||||
margin: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc .comment {
|
|
||||||
background: yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
div#footer-badges { display: none; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div#toctitle {
|
|
||||||
color: #111111;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 1.1em;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-top: 1.0em;
|
|
||||||
margin-bottom: 0.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.toclevel1,
|
|
||||||
.asciidoc div.toclevel2,
|
|
||||||
.asciidoc div.toclevel3,
|
|
||||||
.asciidoc div.toclevel4 {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.toclevel2 {
|
|
||||||
margin-left: 2em;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.toclevel3 {
|
|
||||||
margin-left: 4em;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc div.toclevel4 {
|
|
||||||
margin-left: 6em;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
.login-box {
|
|
||||||
margin-bottom: -10px;
|
|
||||||
height: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-menu {
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-menu {
|
|
||||||
float: right;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Wt-auth-logged-in {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
text-decoration: underline;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invalid {
|
|
||||||
background-color: #EE9999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-icon {
|
|
||||||
float: left;
|
|
||||||
margin-top: 3px;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
background-image: url(comment.png);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-edit-icon {
|
|
||||||
float: left;
|
|
||||||
margin-top: 3px;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
background-image: url(comment_edit.png);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-info {
|
|
||||||
color: rgb(136,136,136);
|
|
||||||
}
|
|
||||||
|
|
||||||
.poster {
|
|
||||||
color: rgb(51, 102, 53);
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.author-panel {
|
|
||||||
margin-top: 10px;
|
|
||||||
border: 1px #528B12 dashed;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-editor {
|
|
||||||
margin-top: 10px;
|
|
||||||
border: 1px #528B12 dashed;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-panel {
|
|
||||||
margin-top: 10px;
|
|
||||||
border: 1px #528B12 dashed;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-body {
|
|
||||||
overflow: hidden; /* trick that makes alignment work properly */
|
|
||||||
margin-left: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-body .vspace {
|
|
||||||
margin: 10px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-body pre {
|
|
||||||
line-height: 140%;
|
|
||||||
border: 1px solid silver;
|
|
||||||
background: #f4f4f4;
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-edit {
|
|
||||||
width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-edit textarea {
|
|
||||||
width: 396px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-links {
|
|
||||||
margin-bottom: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blogpost-edit {
|
|
||||||
width: 500px;
|
|
||||||
margin: 10x;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blogpost-edit div {
|
|
||||||
margin: 5px 0px;
|
|
||||||
}
|
|
||||||
.blogpost-edit input {
|
|
||||||
width: 450px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blogpost-edit textarea {
|
|
||||||
width: 496px;
|
|
||||||
height: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.archive-month-title {
|
|
||||||
color: #528B12;
|
|
||||||
display: block;
|
|
||||||
font-size: 1.3em;
|
|
||||||
line-height: 1.8;
|
|
||||||
margin-top: 15px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.asciidoc .subtitle {
|
|
||||||
font-size: 1.4em;
|
|
||||||
}
|
|
@ -1,258 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
|
||||||
<messages xmlns:if="Wt.WTemplate.conditions">
|
|
||||||
|
|
||||||
<message id="blog-login">
|
|
||||||
${user-name} ${password} ${remember-me} Remember me
|
|
||||||
${login}${<if:oauth>}, or use ${icons}${</if:oauth>}
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-login-status">
|
|
||||||
<div class="login-box">
|
|
||||||
<div class="login-menu">
|
|
||||||
${login} ${login-link} | ${register-link}
|
|
||||||
<a href="${feed-url}">
|
|
||||||
<img src="/css/rss.png" alt="Rss Feed"
|
|
||||||
style="margin-left: 6px; vertical-align: top;"/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="user-menu">
|
|
||||||
${archive-link} ${profile-link} ${author-panel-link} ${userlist-link}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="edit-users-list">
|
|
||||||
<h4>Registered users</h4>
|
|
||||||
${user-list}
|
|
||||||
Limit list to user names containing : ${limit-edit} ${limit-button}
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="edit-user">
|
|
||||||
<div class="profile-panel">
|
|
||||||
<h4>Edit user ${username}</h4>
|
|
||||||
${role-button}
|
|
||||||
</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="edit-one-user">
|
|
||||||
<h4>Editing user ${user}</h4>
|
|
||||||
${save-button} ${cancel-button}
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-invaliduser">
|
|
||||||
<div class="profile-panel">
|
|
||||||
<h4>This user id is invalid</h4>
|
|
||||||
</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-mustlogin">
|
|
||||||
<div class="profile-panel">
|
|
||||||
<h4>You need to log in to access this function</h4>
|
|
||||||
</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-mustbeadministrator">
|
|
||||||
<div class="profile-panel">
|
|
||||||
<h4>You need to be administrator to access this function</h4>
|
|
||||||
</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-register">
|
|
||||||
<h4>Register</h4>
|
|
||||||
<p>
|
|
||||||
To register as a user to post comments with your own login,
|
|
||||||
please fill out the following form.
|
|
||||||
</p>
|
|
||||||
<table style="margin: 5px auto;">
|
|
||||||
<tr>
|
|
||||||
<td>Login:</td>
|
|
||||||
<td>${name}</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Password:</td>
|
|
||||||
<td>${passwd}</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Repeat password:</td>
|
|
||||||
<td>${passwd2}</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" style="text-align: center">
|
|
||||||
${ok-button}
|
|
||||||
${cancel-button}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" class="login-error">
|
|
||||||
${error}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-userslist">
|
|
||||||
<div class="profile-panel">
|
|
||||||
<h4>Registered users</h4>
|
|
||||||
Search for ${searchstring} ${search-button}<br/>
|
|
||||||
</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-profile">
|
|
||||||
<div class="profile-panel">
|
|
||||||
<h4>Profile panel for <span class="poster">${user}</span></h4>
|
|
||||||
<table style="margin: 5px auto;">
|
|
||||||
<tr>
|
|
||||||
<td>New password:</td>
|
|
||||||
<td>${passwd}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Repeat password:</td>
|
|
||||||
<td>${passwd2}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" style="text-align: center">
|
|
||||||
${ok-button}
|
|
||||||
${cancel-button}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" style="text-align: center; color: red;" class="login-error">
|
|
||||||
${error}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-author-panel">
|
|
||||||
<div class="author-panel">
|
|
||||||
<h4>Author panel for <span class="poster">${user}</span></h4>
|
|
||||||
Statistics:
|
|
||||||
<ul>
|
|
||||||
<li>${unpublished-count} unpublished post(s)</li>
|
|
||||||
<li>${published-count} published post(s)</li>
|
|
||||||
</ul>
|
|
||||||
${new-post}
|
|
||||||
${unpublished-posts}
|
|
||||||
</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-post">
|
|
||||||
<h4>${title}</h4>
|
|
||||||
<div id="${anchor}">
|
|
||||||
<div>by ${author} on ${date}</div>
|
|
||||||
<div class="asciidoc">
|
|
||||||
${brief+body}
|
|
||||||
</div>
|
|
||||||
<div style="margin-top: 10px;">${comment-count}</div>
|
|
||||||
<div>
|
|
||||||
${publish} ${edit} ${delete}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="${anchor}/comments">
|
|
||||||
${comments}
|
|
||||||
</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-post-brief">
|
|
||||||
<h4>${title}</h4>
|
|
||||||
<div>by ${author} on ${date}</div>
|
|
||||||
<div class="asciidoc">
|
|
||||||
${brief}
|
|
||||||
</div>
|
|
||||||
<div>${read-more}</div>
|
|
||||||
<div style="margin-top: 10px">
|
|
||||||
${publish} ${edit} ${delete}
|
|
||||||
</div>
|
|
||||||
<div style="margin-top: 10px;">${comment-count}</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-post-edit">
|
|
||||||
<h4></h4>
|
|
||||||
<div class="blogpost-edit">
|
|
||||||
<div>Title: ${title-edit}</div>
|
|
||||||
<div>${brief-edit}</div>
|
|
||||||
<div>${body-edit}</div>
|
|
||||||
<div>
|
|
||||||
${save} ${cancel}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-comment">
|
|
||||||
<div class="comment-icon" />
|
|
||||||
<div class="comment-body">
|
|
||||||
<div class="comment-info">
|
|
||||||
<span class="poster">${author}</span>
|
|
||||||
${date} ${collapse-expand}
|
|
||||||
</div>
|
|
||||||
${contents}
|
|
||||||
<div class="comment-links">
|
|
||||||
${reply} ${edit} ${delete}
|
|
||||||
</div>
|
|
||||||
${children}
|
|
||||||
</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-root-comment">
|
|
||||||
<div class="comment-links">
|
|
||||||
${reply}
|
|
||||||
</div>
|
|
||||||
${children}
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-edit-comment">
|
|
||||||
<div class="comment-edit-icon" />
|
|
||||||
<div class="comment-body comment-edit">
|
|
||||||
<div>${area}</div>
|
|
||||||
<div style="float: right"><i>plain text</i> or (<code>...</code>)</div>
|
|
||||||
${save}
|
|
||||||
${cancel}
|
|
||||||
</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-no-post">
|
|
||||||
<h4>No posts found</h4>
|
|
||||||
Sorry, no blog posts found that match your selection.
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="blog-no-author">
|
|
||||||
<h4>No author</h4>
|
|
||||||
Sorry, {1} is not a registered blog author.
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="archive-title">
|
|
||||||
<h4>Archive</h4>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<message id="login">Login</message>
|
|
||||||
<message id="login-tooshort">Login too short (must be at least {1} characters)</message>
|
|
||||||
<message id="passwd-mismatch">Passwords don't match.</message>
|
|
||||||
<message id="register">Register</message>
|
|
||||||
<message id="logout">Logout</message>
|
|
||||||
<message id="archive">Archive</message>
|
|
||||||
<message id="profile">Profile</message>
|
|
||||||
<message id="author-post">Authoring panel</message>
|
|
||||||
<message id="edit-users">Edit users</message>
|
|
||||||
<message id="comment-add">Add comment</message>
|
|
||||||
<message id="comment-reply">Reply</message>
|
|
||||||
<message id="comment-edit">Edit</message>
|
|
||||||
<message id="comment-delete">Delete</message>
|
|
||||||
<message id="comment-deleted"><i>[[Comment deleted]]</i></message>
|
|
||||||
<message id="blog-read-more">Read the rest of this post >></message>
|
|
||||||
<message id="new-post">New post</message>
|
|
||||||
<message id="publish">Publish</message>
|
|
||||||
<message id="retract">Retract</message>
|
|
||||||
<message id="delete">Delete</message>
|
|
||||||
<message id="edit">Edit</message>
|
|
||||||
<message id="save">Save</message>
|
|
||||||
<message id="cancel">Cancel</message>
|
|
||||||
<message id="go-limit">Search</message>
|
|
||||||
<message id="demote-admin">Demote this administrator to a regular visitor</message>
|
|
||||||
<message id="promote-user">Promote this user to administrator</message>
|
|
||||||
<message id="no-users-found">No users found</message>
|
|
||||||
</messages>
|
|
@ -1,52 +0,0 @@
|
|||||||
body {
|
|
||||||
color: #333333;
|
|
||||||
font-family: arial,sans-serif;
|
|
||||||
font-size: 80%;
|
|
||||||
line-height:1.5em;
|
|
||||||
background-color:#FFF;
|
|
||||||
min-width:750px;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: underline;
|
|
||||||
color: #528B12; /*#70BD1A;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
color: #528B12; /*#70BD1A;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
a.blank {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 1.3em;
|
|
||||||
line-height: 1.8;
|
|
||||||
border-top : 1px solid #528B12;
|
|
||||||
padding-bottom:10px;
|
|
||||||
margin-top:15px;
|
|
||||||
color: #528B12;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 1.1em;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin-bottom : 15px;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
p.intro {
|
|
||||||
font-size: 1.3em;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1.4;
|
|
||||||
padding-bottom : 15px;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
|||||||
#include "BlogSession.h"
|
|
||||||
#include "BoostLog.h"
|
|
||||||
#include "Post.h"
|
|
||||||
#include "Token.h"
|
|
||||||
#include "User.h"
|
|
||||||
#include "asciidoc.h"
|
|
||||||
#include <Wt/Auth/AuthService.h>
|
|
||||||
#include <Wt/Auth/GoogleService.h>
|
|
||||||
#include <Wt/Auth/HashFunction.h>
|
|
||||||
#include <Wt/Auth/PasswordService.h>
|
|
||||||
#include <Wt/Auth/PasswordStrengthValidator.h>
|
|
||||||
#include <Wt/Auth/PasswordVerifier.h>
|
|
||||||
#include <Wt/Dbo/FixedSqlConnectionPool.h>
|
|
||||||
|
|
||||||
const std::string ADMIN_USERNAME = "admin";
|
|
||||||
const std::string ADMIN_PASSWORD = "admin";
|
|
||||||
|
|
||||||
class BlogOAuth : public std::vector<const Wt::Auth::OAuthService *> {
|
|
||||||
public:
|
|
||||||
~BlogOAuth() {
|
|
||||||
for (unsigned i = 0; i < size(); ++i) delete (*this)[i];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Wt::Auth::AuthService blogAuth;
|
|
||||||
Wt::Auth::PasswordService blogPasswords(blogAuth);
|
|
||||||
BlogOAuth blogOAuth;
|
|
||||||
|
|
||||||
BlogSession::BlogSession(Wt::Dbo::SqlConnectionPool &connectionPool)
|
|
||||||
: m_connectionPool(connectionPool), m_users(*this) {
|
|
||||||
setConnectionPool(m_connectionPool);
|
|
||||||
mapClass<Comment>("comment");
|
|
||||||
mapClass<Post>("post");
|
|
||||||
mapClass<Tag>("tag");
|
|
||||||
mapClass<Token>("token");
|
|
||||||
mapClass<User>("user");
|
|
||||||
|
|
||||||
try {
|
|
||||||
Wt::Dbo::Transaction t(*this);
|
|
||||||
createTables();
|
|
||||||
Wt::Dbo::ptr<User> admin = add(std::make_unique<User>());
|
|
||||||
User *a = admin.modify();
|
|
||||||
a->name = ADMIN_USERNAME;
|
|
||||||
a->role = User::Admin;
|
|
||||||
|
|
||||||
Wt::Auth::User authAdmin = m_users.findWithIdentity(Wt::Auth::Identity::LoginName, a->name);
|
|
||||||
blogPasswords.updatePassword(authAdmin, ADMIN_PASSWORD);
|
|
||||||
|
|
||||||
Wt::Dbo::ptr<Post> post = add(std::make_unique<Post>());
|
|
||||||
Post *p = post.modify();
|
|
||||||
|
|
||||||
p->state = Post::Published;
|
|
||||||
p->author = admin;
|
|
||||||
p->title = "Welcome!";
|
|
||||||
p->briefSrc = "Welcome to your own blog.";
|
|
||||||
p->bodySrc = "We have created for you an " + ADMIN_USERNAME + " user with password " + ADMIN_PASSWORD;
|
|
||||||
p->briefHtml = asciidoc(p->briefSrc);
|
|
||||||
p->bodyHtml = asciidoc(p->bodySrc);
|
|
||||||
p->date = Wt::WDateTime::currentDateTime();
|
|
||||||
|
|
||||||
Wt::Dbo::ptr<Comment> rootComment = add(std::make_unique<Comment>());
|
|
||||||
rootComment.modify()->post = post;
|
|
||||||
|
|
||||||
t.commit();
|
|
||||||
|
|
||||||
LOG(info) << "Created database, and user " << ADMIN_USERNAME << " / " << ADMIN_PASSWORD << std::endl;
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
LOG(error) << e.what() << std::endl;
|
|
||||||
LOG(info) << "Using existing database";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlogSession::configureAuth() {
|
|
||||||
blogAuth.setAuthTokensEnabled(true, "bloglogin");
|
|
||||||
|
|
||||||
std::unique_ptr<Wt::Auth::PasswordVerifier> verifier = std::make_unique<Wt::Auth::PasswordVerifier>();
|
|
||||||
verifier->addHashFunction(std::make_unique<Wt::Auth::BCryptHashFunction>(7));
|
|
||||||
#ifdef WT_WITH_SSL
|
|
||||||
verifier->addHashFunction(std::make_unique<Wt::Auth::SHA1HashFunction>());
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_CRYPT
|
|
||||||
verifier->addHashFunction(std::make_unique<UnixCryptHashFunction>());
|
|
||||||
#endif
|
|
||||||
blogPasswords.setVerifier(std::move(verifier));
|
|
||||||
blogPasswords.setPasswordThrottle(std::make_unique<Wt::Auth::AuthThrottle>());
|
|
||||||
blogPasswords.setStrengthValidator(std::make_unique<Wt::Auth::PasswordStrengthValidator>());
|
|
||||||
|
|
||||||
if (Wt::Auth::GoogleService::configured()) blogOAuth.push_back(new Wt::Auth::GoogleService(blogAuth));
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
Wt::Dbo::ptr<User> BlogSession::user() const {
|
|
||||||
if (m_login.loggedIn())
|
|
||||||
return m_users.find(m_login.user());
|
|
||||||
else
|
|
||||||
return Wt::Dbo::ptr<User>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Wt::Signal<Wt::Dbo::ptr<Comment>> &BlogSession::commentsChanged() {
|
|
||||||
return m_commentsChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
Wt::Auth::PasswordService *BlogSession::passwordAuth() const {
|
|
||||||
return &blogPasswords;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<const Wt::Auth::OAuthService *> &BlogSession::oAuth() const {
|
|
||||||
return blogOAuth;
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
#ifndef __BLOGSESSION_H__
|
|
||||||
#define __BLOGSESSION_H__
|
|
||||||
|
|
||||||
#include "BlogUserDatabase.h"
|
|
||||||
#include <Wt/Auth/Login.h>
|
|
||||||
#include <Wt/Auth/OAuthService.h>
|
|
||||||
#include <Wt/Dbo/Session.h>
|
|
||||||
#include <Wt/Dbo/backend/Sqlite3.h>
|
|
||||||
|
|
||||||
class Comment;
|
|
||||||
|
|
||||||
class BlogSession : public Wt::Dbo::Session {
|
|
||||||
public:
|
|
||||||
BlogSession(Wt::Dbo::SqlConnectionPool &connectionPool);
|
|
||||||
static void configureAuth();
|
|
||||||
|
|
||||||
static std::unique_ptr<Wt::Dbo::SqlConnectionPool> createConnectionPool(const std::string &sqlite3);
|
|
||||||
Wt::Auth::Login &login() {
|
|
||||||
return m_login;
|
|
||||||
}
|
|
||||||
Wt::Dbo::ptr<User> user() const;
|
|
||||||
Wt::Signal<Wt::Dbo::ptr<Comment>> &commentsChanged();
|
|
||||||
BlogUserDatabase &users() {
|
|
||||||
return m_users;
|
|
||||||
}
|
|
||||||
Wt::Auth::PasswordService *passwordAuth() const;
|
|
||||||
const std::vector<const Wt::Auth::OAuthService *> &oAuth() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Wt::Dbo::SqlConnectionPool &m_connectionPool;
|
|
||||||
BlogUserDatabase m_users;
|
|
||||||
Wt::Auth::Login m_login;
|
|
||||||
Wt::Signal<Wt::Dbo::ptr<Comment>> m_commentsChanged;
|
|
||||||
};
|
|
||||||
#endif // __BLOGSESSION_H__
|
|
@ -1,140 +0,0 @@
|
|||||||
#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() {
|
|
||||||
}
|
|
||||||
|
|
||||||
Wt::Dbo::ptr<User> BlogUserDatabase::find(const Wt::Auth::User &user) const {
|
|
||||||
getUser(user.id());
|
|
||||||
return m_user;
|
|
||||||
}
|
|
||||||
|
|
||||||
Wt::Auth::User BlogUserDatabase::find(Wt::Dbo::ptr<User> user) const {
|
|
||||||
m_user = user;
|
|
||||||
return Wt::Auth::User(std::to_string(m_user.id()), *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Wt::Auth::PasswordHash BlogUserDatabase::password(const Wt::Auth::User &user) const {
|
|
||||||
WithUser find(*this, user);
|
|
||||||
return Wt::Auth::PasswordHash(m_user->passwordMethod, m_user->passwordSalt, m_user->password);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlogUserDatabase::setPassword(const Wt::Auth::User &user, const Wt::Auth::PasswordHash &password) {
|
|
||||||
WithUser find(*this, user);
|
|
||||||
m_user.modify()->password = password.value();
|
|
||||||
m_user.modify()->passwordMethod = password.function();
|
|
||||||
m_user.modify()->passwordSalt = password.salt();
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
#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();
|
|
||||||
Wt::Dbo::ptr<User> find(const Wt::Auth::User &user) const;
|
|
||||||
Wt::Auth::User find(Wt::Dbo::ptr<User> user) const;
|
|
||||||
|
|
||||||
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;
|
|
||||||
Wt::Auth::PasswordHash password(const Wt::Auth::User &user) const final;
|
|
||||||
void setPassword(const Wt::Auth::User &user, const Wt::Auth::PasswordHash &password) 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__
|
|
@ -1,76 +0,0 @@
|
|||||||
#include "Comment.h"
|
|
||||||
#include "Post.h"
|
|
||||||
#include "Tag.h"
|
|
||||||
#include "User.h"
|
|
||||||
#include <Wt/Dbo/Impl.h>
|
|
||||||
#include <Wt/WWebWidget.h>
|
|
||||||
|
|
||||||
DBO_INSTANTIATE_TEMPLATES(Comment)
|
|
||||||
|
|
||||||
static std::string &replace(std::string &s, const std::string &k, const std::string &r) {
|
|
||||||
std::string::size_type p = 0;
|
|
||||||
|
|
||||||
while ((p = s.find(k, p)) != std::string::npos) {
|
|
||||||
s.replace(p, k.length(), r);
|
|
||||||
p += r.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Comment::setText(const Wt::WString &text) {
|
|
||||||
textSrc_ = text;
|
|
||||||
|
|
||||||
std::string html = Wt::WWebWidget::escapeText(text, true).toUTF8();
|
|
||||||
|
|
||||||
std::string::size_type b = 0;
|
|
||||||
|
|
||||||
// Replace <code>...</code> with <pre>...</pre>
|
|
||||||
// This is kind of very ad-hoc!
|
|
||||||
|
|
||||||
while ((b = html.find("<code>", b)) != std::string::npos) {
|
|
||||||
std::string::size_type e = html.find("</code>", b);
|
|
||||||
if (e == std::string::npos)
|
|
||||||
break;
|
|
||||||
else {
|
|
||||||
if (b > 6 && html.substr(b - 6, 6) == "<br />") {
|
|
||||||
html.erase(b - 6, 6);
|
|
||||||
b -= 6;
|
|
||||||
e -= 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.replace(b, 12, "<pre>");
|
|
||||||
e -= 7;
|
|
||||||
|
|
||||||
if (html.substr(b + 5, 6) == "<br />") {
|
|
||||||
html.erase(b + 5, 6);
|
|
||||||
e -= 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (html.substr(e - 6, 6) == "<br />") {
|
|
||||||
html.erase(e - 6, 6);
|
|
||||||
e -= 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.replace(e, 13, "</pre>");
|
|
||||||
e += 6;
|
|
||||||
|
|
||||||
if (e + 6 <= html.length() && html.substr(e, 6) == "<br />") {
|
|
||||||
html.erase(e, 6);
|
|
||||||
e -= 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
b = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We would also want to replace <br /><br /> (empty line) with
|
|
||||||
// <div class="vspace"></div>
|
|
||||||
replace(html, "<br /><br />", "<div class=\"vspace\"></div>");
|
|
||||||
|
|
||||||
textHtml_ = Wt::WString(html);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Comment::setDeleted() {
|
|
||||||
textHtml_ = Wt::WString::tr("comment-deleted");
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
#ifndef __COMMENT_H__
|
|
||||||
#define __COMMENT_H__
|
|
||||||
|
|
||||||
#include <Wt/Dbo/Types.h>
|
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
|
||||||
#include <Wt/WDateTime.h>
|
|
||||||
|
|
||||||
class Comment;
|
|
||||||
using Comments = Wt::Dbo::collection<Wt::Dbo::ptr<Comment>>;
|
|
||||||
class Post;
|
|
||||||
class User;
|
|
||||||
|
|
||||||
class Comment {
|
|
||||||
public:
|
|
||||||
Wt::Dbo::ptr<User> author;
|
|
||||||
Wt::Dbo::ptr<Post> post;
|
|
||||||
Wt::Dbo::ptr<Comment> parent;
|
|
||||||
|
|
||||||
Wt::WDateTime date;
|
|
||||||
|
|
||||||
void setText(const Wt::WString &text);
|
|
||||||
void setDeleted();
|
|
||||||
|
|
||||||
const Wt::WString &textSrc() const {
|
|
||||||
return textSrc_;
|
|
||||||
}
|
|
||||||
const Wt::WString &textHtml() const {
|
|
||||||
return textHtml_;
|
|
||||||
}
|
|
||||||
|
|
||||||
Comments children;
|
|
||||||
|
|
||||||
template <class Action>
|
|
||||||
void persist(Action &a) {
|
|
||||||
Wt::Dbo::field(a, date, "date");
|
|
||||||
Wt::Dbo::field(a, textSrc_, "text_source");
|
|
||||||
Wt::Dbo::field(a, textHtml_, "text_html");
|
|
||||||
|
|
||||||
Wt::Dbo::belongsTo(a, post, "post", Wt::Dbo::OnDeleteCascade);
|
|
||||||
Wt::Dbo::belongsTo(a, author, "author");
|
|
||||||
Wt::Dbo::belongsTo(a, parent, "parent", Wt::Dbo::OnDeleteCascade);
|
|
||||||
|
|
||||||
Wt::Dbo::hasMany(a, children, Wt::Dbo::ManyToOne, "parent");
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Wt::WString textSrc_;
|
|
||||||
Wt::WString textHtml_;
|
|
||||||
};
|
|
||||||
#endif // __COMMENT_H__
|
|
@ -1,36 +0,0 @@
|
|||||||
#include "Post.h"
|
|
||||||
#include "User.h"
|
|
||||||
#include <Wt/Dbo/Impl.h>
|
|
||||||
|
|
||||||
DBO_INSTANTIATE_TEMPLATES(Post)
|
|
||||||
|
|
||||||
std::string Post::permaLink() const {
|
|
||||||
return date.toString("yyyy/MM/dd/'" + titleToUrl() + '\'').toUTF8();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Post::commentCount() const {
|
|
||||||
int count = (int)comments.size() - 1;
|
|
||||||
if (count == 1)
|
|
||||||
return "1 comment";
|
|
||||||
else
|
|
||||||
return std::to_string(count) + " comments";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Post::titleToUrl() const {
|
|
||||||
std::string result = title.narrow();
|
|
||||||
for (unsigned i = 0; i < result.length(); ++i) {
|
|
||||||
if (!isalnum(result[i]))
|
|
||||||
result[i] = '_';
|
|
||||||
else
|
|
||||||
result[i] = tolower(result[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Wt::Dbo::ptr<Comment> Post::rootComment() const {
|
|
||||||
if (session())
|
|
||||||
return session()->find<Comment>().where("post_id = ?").bind(id()).where("parent_id is null");
|
|
||||||
else
|
|
||||||
return Wt::Dbo::ptr<Comment>();
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
#ifndef __POST_H__
|
|
||||||
#define __POST_H__
|
|
||||||
|
|
||||||
#include "Comment.h"
|
|
||||||
#include "Tag.h"
|
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
|
||||||
#include <Wt/WDateTime.h>
|
|
||||||
#include <Wt/WString.h>
|
|
||||||
|
|
||||||
class User;
|
|
||||||
|
|
||||||
typedef Wt::Dbo::collection<Wt::Dbo::ptr<Comment>> Comments;
|
|
||||||
typedef Wt::Dbo::collection<Wt::Dbo::ptr<Tag>> Tags;
|
|
||||||
|
|
||||||
class Post : public Wt::Dbo::Dbo<Post> {
|
|
||||||
public:
|
|
||||||
enum State {
|
|
||||||
Unpublished = 0,
|
|
||||||
Published = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string permaLink() const;
|
|
||||||
std::string commentCount() const;
|
|
||||||
std::string titleToUrl() const;
|
|
||||||
Wt::Dbo::ptr<Comment> rootComment() const;
|
|
||||||
|
|
||||||
template <class Action>
|
|
||||||
void persist(Action &a) {
|
|
||||||
Wt::Dbo::field(a, state, "state");
|
|
||||||
Wt::Dbo::field(a, date, "date");
|
|
||||||
Wt::Dbo::field(a, title, "title");
|
|
||||||
Wt::Dbo::field(a, briefSrc, "brief_src");
|
|
||||||
Wt::Dbo::field(a, briefHtml, "brief_html");
|
|
||||||
Wt::Dbo::field(a, bodySrc, "body_src");
|
|
||||||
Wt::Dbo::field(a, bodyHtml, "body_html");
|
|
||||||
|
|
||||||
Wt::Dbo::belongsTo(a, author, "author");
|
|
||||||
|
|
||||||
Wt::Dbo::hasMany(a, comments, Wt::Dbo::ManyToOne, "post");
|
|
||||||
Wt::Dbo::hasMany(a, tags, Wt::Dbo::ManyToMany, "post_tag");
|
|
||||||
}
|
|
||||||
|
|
||||||
Wt::Dbo::ptr<User> author;
|
|
||||||
State state;
|
|
||||||
|
|
||||||
Wt::WDateTime date;
|
|
||||||
Wt::WString title;
|
|
||||||
Wt::WString briefSrc;
|
|
||||||
Wt::WString briefHtml;
|
|
||||||
Wt::WString bodySrc;
|
|
||||||
Wt::WString bodyHtml;
|
|
||||||
|
|
||||||
Comments comments;
|
|
||||||
Tags tags;
|
|
||||||
};
|
|
||||||
DBO_EXTERN_TEMPLATES(Post)
|
|
||||||
|
|
||||||
#endif // __POST_H__
|
|
@ -1,7 +0,0 @@
|
|||||||
#include "Tag.h"
|
|
||||||
#include "Comment.h"
|
|
||||||
#include "Post.h"
|
|
||||||
#include "User.h"
|
|
||||||
#include <Wt/Dbo/Impl.h>
|
|
||||||
|
|
||||||
DBO_INSTANTIATE_TEMPLATES(Tag)
|
|
@ -1,26 +0,0 @@
|
|||||||
#ifndef __TAG_H__
|
|
||||||
#define __TAG_H__
|
|
||||||
|
|
||||||
#include <Wt/Dbo/Types.h>
|
|
||||||
|
|
||||||
class Post;
|
|
||||||
using Posts= Wt::Dbo::collection<Wt::Dbo::ptr<Post>> ;
|
|
||||||
|
|
||||||
class Tag {
|
|
||||||
public:
|
|
||||||
Tag() = default;
|
|
||||||
Tag(const std::string &aName) : name(aName) {
|
|
||||||
}
|
|
||||||
template <class Action>
|
|
||||||
void persist(Action &a) {
|
|
||||||
Wt::Dbo::field(a, name, "name");
|
|
||||||
|
|
||||||
Wt::Dbo::hasMany(a, posts, Wt::Dbo::ManyToMany, "post_tag");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
Posts posts;
|
|
||||||
};
|
|
||||||
|
|
||||||
DBO_EXTERN_TEMPLATES(Tag)
|
|
||||||
#endif // __TAG_H__
|
|
@ -1,11 +0,0 @@
|
|||||||
#include "Token.h"
|
|
||||||
#include "User.h"
|
|
||||||
#include <Wt/Dbo/Impl.h>
|
|
||||||
|
|
||||||
DBO_INSTANTIATE_TEMPLATES(Token)
|
|
||||||
|
|
||||||
Token::Token() {
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Token(const std::string &v, const Wt::WDateTime &e) : value(v), expires(e) {
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
#ifndef __TOKENS_H__
|
|
||||||
#define __TOKENS_H__
|
|
||||||
|
|
||||||
#include <Wt/Dbo/Types.h>
|
|
||||||
#include <Wt/WDateTime.h>
|
|
||||||
|
|
||||||
class User;
|
|
||||||
|
|
||||||
class Token : public Wt::Dbo::Dbo<Token> {
|
|
||||||
public:
|
|
||||||
Token();
|
|
||||||
Token(const std::string &value, const Wt::WDateTime &expires);
|
|
||||||
|
|
||||||
Wt::Dbo::ptr<User> user;
|
|
||||||
|
|
||||||
std::string value;
|
|
||||||
Wt::WDateTime expires;
|
|
||||||
|
|
||||||
template <class Action>
|
|
||||||
void persist(Action &a) {
|
|
||||||
Wt::Dbo::field(a, value, "value");
|
|
||||||
Wt::Dbo::field(a, expires, "expires");
|
|
||||||
|
|
||||||
Wt::Dbo::belongsTo(a, user, "user");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DBO_EXTERN_TEMPLATES(Token)
|
|
||||||
|
|
||||||
#endif // __TOKENS_H__
|
|
@ -1,23 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Posts User::latestPosts(int count) const {
|
|
||||||
return posts.find().where("state = ?").bind(Post::Published).orderBy("date desc").limit(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
Posts User::allPosts(Post::State state) const {
|
|
||||||
return posts.find().where("state = ?").bind(state).orderBy("date desc");
|
|
||||||
}
|
|
||||||
|
|
||||||
User::User() : role(Visitor), failedLoginAttempts(0) {
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
#ifndef __USER_H__
|
|
||||||
#define __USER_H__
|
|
||||||
|
|
||||||
#include "Post.h"
|
|
||||||
#include "Token.h"
|
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
|
||||||
#include <Wt/WDateTime.h>
|
|
||||||
#include <Wt/WGlobal.h>
|
|
||||||
#include <Wt/WString.h>
|
|
||||||
|
|
||||||
using Tokens = Wt::Dbo::collection<Wt::Dbo::ptr<Token>>;
|
|
||||||
|
|
||||||
class User {
|
|
||||||
public:
|
|
||||||
enum Role {
|
|
||||||
Visitor = 0,
|
|
||||||
Admin = 1,
|
|
||||||
};
|
|
||||||
User();
|
|
||||||
|
|
||||||
static Wt::Dbo::dbo_traits<User>::IdType stringToId(const std::string &s);
|
|
||||||
Posts latestPosts(int count = 10) const;
|
|
||||||
Posts allPosts(Post::State state) const;
|
|
||||||
Wt::WString name;
|
|
||||||
Role role;
|
|
||||||
|
|
||||||
std::string password;
|
|
||||||
std::string passwordMethod;
|
|
||||||
std::string passwordSalt;
|
|
||||||
int failedLoginAttempts;
|
|
||||||
Wt::WDateTime lastLoginAttempt;
|
|
||||||
|
|
||||||
std::string oAuthId;
|
|
||||||
std::string oAuthProvider;
|
|
||||||
|
|
||||||
Tokens authTokens;
|
|
||||||
Comments comments;
|
|
||||||
Posts posts;
|
|
||||||
|
|
||||||
template <class Action>
|
|
||||||
void persist(Action &a) {
|
|
||||||
Wt::Dbo::field(a, name, "name");
|
|
||||||
Wt::Dbo::field(a, password, "password");
|
|
||||||
Wt::Dbo::field(a, passwordMethod, "password_method");
|
|
||||||
Wt::Dbo::field(a, passwordSalt, "password_salt");
|
|
||||||
Wt::Dbo::field(a, role, "role");
|
|
||||||
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");
|
|
||||||
|
|
||||||
Wt::Dbo::hasMany(a, comments, Wt::Dbo::ManyToOne, "author");
|
|
||||||
Wt::Dbo::hasMany(a, posts, Wt::Dbo::ManyToOne, "author");
|
|
||||||
Wt::Dbo::hasMany(a, authTokens, Wt::Dbo::ManyToOne, "user");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DBO_EXTERN_TEMPLATES(User)
|
|
||||||
|
|
||||||
#endif // __USER_H__
|
|
@ -1,94 +0,0 @@
|
|||||||
#include "asciidoc.h"
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "Wt/WString.h"
|
|
||||||
|
|
||||||
#ifndef WT_WIN32
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
std::string tempFileName()
|
|
||||||
{
|
|
||||||
#ifndef WT_WIN32
|
|
||||||
char spool[20];
|
|
||||||
strcpy(spool, "/tmp/wtXXXXXX");
|
|
||||||
|
|
||||||
int i = mkstemp(spool);
|
|
||||||
close(i);
|
|
||||||
#else
|
|
||||||
char spool[2 * L_tmpnam];
|
|
||||||
tmpnam(spool);
|
|
||||||
#endif
|
|
||||||
return std::string(spool);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string readFileToString(const std::string& fileName)
|
|
||||||
{
|
|
||||||
std::fstream file(fileName.c_str(), std::ios::in | std::ios::binary | std::ios::ate);
|
|
||||||
int length = file.tellg();
|
|
||||||
file.seekg(0, std::ios::beg);
|
|
||||||
|
|
||||||
std::unique_ptr<char[]> buf(new char[length]);
|
|
||||||
file.read(buf.get(), length);
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
return std::string(buf.get(), length);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Wt::WString asciidoc(const Wt::WString& src)
|
|
||||||
{
|
|
||||||
std::string srcFileName = tempFileName();
|
|
||||||
std::string htmlFileName = tempFileName();
|
|
||||||
|
|
||||||
{
|
|
||||||
std::ofstream srcFile(srcFileName.c_str(), std::ios::out);
|
|
||||||
std::string ssrc = src.toUTF8();
|
|
||||||
srcFile.write(ssrc.c_str(), (std::streamsize)ssrc.length());
|
|
||||||
srcFile.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(ASCIIDOCTOR_EXECUTABLE)
|
|
||||||
#define xstr(s) str(s)
|
|
||||||
#define str(s) #s
|
|
||||||
std::string cmd = xstr(ASCIIDOCTOR_EXECUTABLE);
|
|
||||||
#else
|
|
||||||
std::string cmd = "asciidoctor";
|
|
||||||
#endif
|
|
||||||
std::string command = cmd + " -a htmlsyntax=xml -o " + htmlFileName + " -s " + srcFileName;
|
|
||||||
|
|
||||||
#ifndef WT_WIN32
|
|
||||||
/*
|
|
||||||
* So, asciidoc apparently sends a SIGINT which is caught by its parent
|
|
||||||
* process.. So we have to temporarily ignore it.
|
|
||||||
*/
|
|
||||||
struct sigaction newAction, oldAction;
|
|
||||||
newAction.sa_handler = SIG_IGN;
|
|
||||||
newAction.sa_flags = 0;
|
|
||||||
sigemptyset(&newAction.sa_mask);
|
|
||||||
sigaction(SIGINT, &newAction, &oldAction);
|
|
||||||
#endif
|
|
||||||
bool ok = system(command.c_str()) == 0;
|
|
||||||
#ifndef WT_WIN32
|
|
||||||
sigaction(SIGINT, &oldAction, 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Wt::WString result;
|
|
||||||
|
|
||||||
if (ok) {
|
|
||||||
result = Wt::WString(readFileToString(htmlFileName));
|
|
||||||
} else
|
|
||||||
result = Wt::WString("<i>Could not execute asciidoc</i>");
|
|
||||||
|
|
||||||
unlink(srcFileName.c_str());
|
|
||||||
unlink(htmlFileName.c_str());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
#ifndef __ASCIIDOC_H__
|
|
||||||
#define __ASCIIDOC_H__
|
|
||||||
|
|
||||||
#include <Wt/WString.h>
|
|
||||||
|
|
||||||
Wt::WString asciidoc(const Wt::WString &src);
|
|
||||||
#endif // __ASCIIDOC_H__
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB |
@ -1,40 +0,0 @@
|
|||||||
#include "BlogLoginWidget.h"
|
|
||||||
#include "model/BlogSession.h"
|
|
||||||
#include <Wt/Auth/PasswordService.h>
|
|
||||||
#include <Wt/WLineEdit.h>
|
|
||||||
#include <Wt/WText.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");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlogLoginWidget::createLoginView() {
|
|
||||||
AuthWidget::createLoginView();
|
|
||||||
|
|
||||||
setTemplateText(tr("blog-login"));
|
|
||||||
|
|
||||||
Wt::WLineEdit *userName = resolve<Wt::WLineEdit *>("user-name");
|
|
||||||
userName->setPlaceholderText("login");
|
|
||||||
userName->setToolTip("login");
|
|
||||||
|
|
||||||
Wt::WLineEdit *password = resolve<Wt::WLineEdit *>("password");
|
|
||||||
password->setPlaceholderText("password");
|
|
||||||
password->setToolTip("password");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlogLoginWidget::createLoggedInView() {
|
|
||||||
AuthWidget::createLoggedInView();
|
|
||||||
|
|
||||||
auto logout = std::make_unique<Wt::WText>(tr("logout"));
|
|
||||||
logout->setStyleClass("link");
|
|
||||||
logout->clicked().connect(&login(), &Wt::Auth::Login::logout);
|
|
||||||
bindWidget("logout", std::move(logout));
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
#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);
|
|
||||||
void createLoginView() final;
|
|
||||||
void createLoggedInView() final;
|
|
||||||
};
|
|
||||||
#endif // __BLOGLOGINWIDGET_H__
|
|
@ -1,392 +0,0 @@
|
|||||||
#include "BlogView.h"
|
|
||||||
#include "BlogLoginWidget.h"
|
|
||||||
#include "EditUsers.h"
|
|
||||||
#include "PostView.h"
|
|
||||||
#include "model/BlogSession.h"
|
|
||||||
#include "model/Post.h"
|
|
||||||
#include "model/Tag.h"
|
|
||||||
#include <Wt/WApplication.h>
|
|
||||||
#include <Wt/WContainerWidget.h>
|
|
||||||
#include <Wt/WPushButton.h>
|
|
||||||
#include <Wt/WStackedWidget.h>
|
|
||||||
#include <Wt/WText.h>
|
|
||||||
#include <boost/algorithm/string.hpp>
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
|
||||||
|
|
||||||
static int try_stoi(const std::string &v) {
|
|
||||||
std::size_t pos;
|
|
||||||
auto result = std::stoi(v, &pos);
|
|
||||||
if (pos != v.length()) throw std::invalid_argument("stoi() of " + v + " failed");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
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::instance()->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(m_loginWidget, &BlogLoginWidget::show);
|
|
||||||
loginLink->clicked().connect(lPtr, &WWidget::hide);
|
|
||||||
|
|
||||||
auto registerLink = std::make_unique<Wt::WText>(tr("Wt.Auth.register"));
|
|
||||||
registerLink->setStyleClass("link");
|
|
||||||
registerLink->clicked().connect(m_loginWidget, &BlogLoginWidget::registerNewUser);
|
|
||||||
|
|
||||||
auto archiveLink =
|
|
||||||
std::make_unique<Wt::WAnchor>(Wt::WLink(Wt::LinkType::InternalPath, m_basePath + "all"), tr("archive"));
|
|
||||||
|
|
||||||
m_loginStatus->bindWidget("login", std::move(loginWidget));
|
|
||||||
m_loginStatus->bindWidget("login-link", std::move(loginLink));
|
|
||||||
m_loginStatus->bindWidget("register-link", std::move(registerLink));
|
|
||||||
m_loginStatus->bindString("feed-url", m_rssFeedUrl);
|
|
||||||
m_loginStatus->bindWidget("archive-link", std::move(archiveLink));
|
|
||||||
|
|
||||||
onUserChanged();
|
|
||||||
|
|
||||||
m_loginWidget->processEnvironment();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void handlePathChange(const std::string &) {
|
|
||||||
Wt::WApplication *app = Wt::WApplication::instance();
|
|
||||||
|
|
||||||
if (app->internalPathMatches(m_basePath)) {
|
|
||||||
Wt::Dbo::Transaction t(m_session);
|
|
||||||
|
|
||||||
std::string path = app->internalPathNextPart(m_basePath);
|
|
||||||
|
|
||||||
m_items->clear();
|
|
||||||
|
|
||||||
if (m_users) {
|
|
||||||
m_users = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.empty())
|
|
||||||
showPosts(m_session
|
|
||||||
.find<Post>("where state = ? "
|
|
||||||
"order by date desc "
|
|
||||||
"limit 10")
|
|
||||||
.bind(Post::Published),
|
|
||||||
m_items);
|
|
||||||
|
|
||||||
else if (path == "author") {
|
|
||||||
std::string author = app->internalPathNextPart(m_basePath + path + '/');
|
|
||||||
Wt::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(m_basePath + path + '/'));
|
|
||||||
} else if (path == "all") {
|
|
||||||
showArchive(m_items);
|
|
||||||
} else {
|
|
||||||
std::string remainder = app->internalPath().substr(m_basePath.length());
|
|
||||||
showPostsByDateTopic(remainder, m_items);
|
|
||||||
}
|
|
||||||
|
|
||||||
t.commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void showArchive(WContainerWidget *parent) {
|
|
||||||
static const char *dateFormat = "MMMM yyyy";
|
|
||||||
|
|
||||||
parent->addWidget(std::make_unique<Wt::WText>(tr("archive-title")));
|
|
||||||
|
|
||||||
Posts posts = m_session.find<Post>("order by date desc");
|
|
||||||
|
|
||||||
Wt::WDateTime formerDate;
|
|
||||||
for (auto post : posts) {
|
|
||||||
if (post->state != Post::Published) continue;
|
|
||||||
|
|
||||||
if (formerDate.isNull() || yearMonthDiffer(formerDate, post->date)) {
|
|
||||||
Wt::WText *title =
|
|
||||||
parent->addWidget(std::make_unique<Wt::WText>(post->date.date().toString(dateFormat)));
|
|
||||||
title->setStyleClass("archive-month-title");
|
|
||||||
}
|
|
||||||
|
|
||||||
Wt::WAnchor *a = parent->addWidget(std::make_unique<Wt::WAnchor>(
|
|
||||||
Wt::WLink(Wt::LinkType::InternalPath, m_basePath + post->permaLink()), post->title));
|
|
||||||
a->setInline(false);
|
|
||||||
|
|
||||||
formerDate = post->date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool yearMonthDiffer(const Wt::WDateTime &dt1, const Wt::WDateTime &dt2) {
|
|
||||||
return dt1.date().year() != dt2.date().year() || dt1.date().month() != dt2.date().month();
|
|
||||||
}
|
|
||||||
|
|
||||||
Wt::Dbo::ptr<User> findUser(const std::string &name) {
|
|
||||||
return m_session.find<User>("where name = ?").bind(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkLoggedIn() {
|
|
||||||
if (m_session.user()) return true;
|
|
||||||
m_panel->show();
|
|
||||||
if (!m_mustLoginWarning) {
|
|
||||||
m_mustLoginWarning = m_panel->addWidget(std::make_unique<Wt::WTemplate>(tr("blog-mustlogin")));
|
|
||||||
}
|
|
||||||
m_panel->setCurrentWidget(m_mustLoginWarning);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkAdministrator() {
|
|
||||||
if (m_session.user() && (m_session.user()->role == User::Admin)) return true;
|
|
||||||
m_panel->show();
|
|
||||||
if (!m_mustBeAdministratorWarning) {
|
|
||||||
m_mustBeAdministratorWarning =
|
|
||||||
m_panel->addWidget(std::make_unique<Wt::WTemplate>(tr("blog-mustbeadministrator")));
|
|
||||||
}
|
|
||||||
m_panel->setCurrentWidget(m_mustBeAdministratorWarning);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void editUser(const std::string &ids) {
|
|
||||||
if (!checkLoggedIn()) return;
|
|
||||||
if (!checkAdministrator()) return;
|
|
||||||
Wt::Dbo::dbo_traits<User>::IdType id = User::stringToId(ids);
|
|
||||||
|
|
||||||
m_panel->show();
|
|
||||||
try {
|
|
||||||
Wt::Dbo::Transaction t(m_session);
|
|
||||||
Wt::Dbo::ptr<User> target(m_session.load<User>(id));
|
|
||||||
if (!m_userEditor) {
|
|
||||||
m_userEditor = m_panel->addWidget(std::make_unique<EditUser>(m_session));
|
|
||||||
}
|
|
||||||
m_userEditor->switchUser(target);
|
|
||||||
m_panel->setCurrentWidget(m_userEditor);
|
|
||||||
} catch (Wt::Dbo::ObjectNotFoundException &) {
|
|
||||||
if (!m_invalidUser) {
|
|
||||||
m_invalidUser = m_panel->addWidget(std::make_unique<Wt::WTemplate>(tr("blog-invaliduser")));
|
|
||||||
}
|
|
||||||
m_panel->setCurrentWidget(m_invalidUser);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void showPostsByDateTopic(const std::string &path, WContainerWidget *parent) {
|
|
||||||
std::vector<std::string> parts;
|
|
||||||
boost::split(parts, path, boost::is_any_of("/"));
|
|
||||||
|
|
||||||
Wt::WDate lower, upper;
|
|
||||||
try {
|
|
||||||
int year = try_stoi(parts[0]);
|
|
||||||
|
|
||||||
if (parts.size() > 1) {
|
|
||||||
int month = try_stoi(parts[1]);
|
|
||||||
|
|
||||||
if (parts.size() > 2) {
|
|
||||||
int day = try_stoi(parts[2]);
|
|
||||||
|
|
||||||
lower.setDate(year, month, day);
|
|
||||||
upper = lower.addDays(1);
|
|
||||||
} else {
|
|
||||||
lower.setDate(year, month, 1);
|
|
||||||
upper = lower.addMonths(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lower.setDate(year, 1, 1);
|
|
||||||
upper = lower.addYears(1);
|
|
||||||
}
|
|
||||||
} catch (std::invalid_argument &) {
|
|
||||||
showError(tr("blog-no-post"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Posts posts = m_session
|
|
||||||
.find<Post>("where date >= ? "
|
|
||||||
"and date < ? "
|
|
||||||
"and (state = ? or author_id = ?)")
|
|
||||||
.bind(Wt::WDateTime(lower))
|
|
||||||
.bind(Wt::WDateTime(upper))
|
|
||||||
.bind(Post::Published)
|
|
||||||
.bind(m_session.user().id());
|
|
||||||
|
|
||||||
if (parts.size() > 3) {
|
|
||||||
std::string title = parts[3];
|
|
||||||
|
|
||||||
for (auto post : posts)
|
|
||||||
if (post->titleToUrl() == title) {
|
|
||||||
showPost(post, PostView::Detail, parent);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
showError(tr("blog-no-post"));
|
|
||||||
} else {
|
|
||||||
showPosts(posts, parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void showPosts(Wt::Dbo::ptr<User> user) {
|
|
||||||
showPosts(user->latestPosts(), m_items);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showPosts(const Posts &posts, WContainerWidget *parent) {
|
|
||||||
for (auto post : posts) showPost(post, PostView::Brief, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onUserChanged() {
|
|
||||||
if (m_session.login().loggedIn())
|
|
||||||
loggedIn();
|
|
||||||
else
|
|
||||||
loggedOut();
|
|
||||||
}
|
|
||||||
|
|
||||||
void editUsers() {
|
|
||||||
m_panel->show();
|
|
||||||
|
|
||||||
if (!m_users) {
|
|
||||||
m_users = m_panel->addWidget(std::make_unique<EditUsers>(m_session, m_basePath));
|
|
||||||
bindPanelTemplates();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_panel->setCurrentWidget(m_users);
|
|
||||||
}
|
|
||||||
BlogSession &session() {
|
|
||||||
return m_session;
|
|
||||||
}
|
|
||||||
void loggedIn() {
|
|
||||||
Wt::WApplication::instance()->changeSessionId();
|
|
||||||
|
|
||||||
refresh();
|
|
||||||
|
|
||||||
m_loginStatus->resolveWidget("login")->show();
|
|
||||||
m_loginStatus->resolveWidget("login-link")->hide();
|
|
||||||
m_loginStatus->resolveWidget("register-link")->hide();
|
|
||||||
|
|
||||||
auto profileLink = std::make_unique<Wt::WText>(tr("profile"));
|
|
||||||
profileLink->setStyleClass("link");
|
|
||||||
profileLink->clicked().connect(this, &BlogImpl::editProfile);
|
|
||||||
|
|
||||||
Wt::Dbo::ptr<User> user = session().user();
|
|
||||||
|
|
||||||
if (user->role == User::Admin) {
|
|
||||||
auto editUsersLink = std::make_unique<Wt::WText>(tr("edit-users"));
|
|
||||||
editUsersLink->setStyleClass("link");
|
|
||||||
editUsersLink->clicked().connect(this, &BlogImpl::editUsers);
|
|
||||||
m_loginStatus->bindWidget("userlist-link", std::move(editUsersLink));
|
|
||||||
|
|
||||||
auto authorPanelLink = std::make_unique<Wt::WText>(tr("author-post"));
|
|
||||||
authorPanelLink->setStyleClass("link");
|
|
||||||
authorPanelLink->clicked().connect(this, &BlogImpl::authorPanel);
|
|
||||||
m_loginStatus->bindWidget("author-panel-link", std::move(authorPanelLink));
|
|
||||||
} else {
|
|
||||||
m_loginStatus->bindEmpty("userlist-link");
|
|
||||||
m_loginStatus->bindEmpty("author-panel-link");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_loginStatus->bindWidget("profile-link", std::move(profileLink));
|
|
||||||
|
|
||||||
bindPanelTemplates();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loggedOut() {
|
|
||||||
m_loginStatus->bindEmpty("profile-link");
|
|
||||||
m_loginStatus->bindEmpty("author-panel-link");
|
|
||||||
m_loginStatus->bindEmpty("userlist-link");
|
|
||||||
|
|
||||||
m_loginStatus->resolveWidget("login")->hide();
|
|
||||||
m_loginStatus->resolveWidget("login-link")->show();
|
|
||||||
m_loginStatus->resolveWidget("register-link")->show();
|
|
||||||
|
|
||||||
refresh();
|
|
||||||
m_panel->hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
void editProfile() {
|
|
||||||
m_loginWidget->letUpdatePassword(m_session.login().user(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showError(const Wt::WString &msg) {
|
|
||||||
m_items->addWidget(std::make_unique<Wt::WText>(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
void authorPanel() {
|
|
||||||
m_panel->show();
|
|
||||||
if (!m_authorPanel) {
|
|
||||||
m_authorPanel = m_panel->addWidget(std::make_unique<Wt::WTemplate>(tr("blog-author-panel")));
|
|
||||||
bindPanelTemplates();
|
|
||||||
}
|
|
||||||
m_panel->setCurrentWidget(m_authorPanel);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showPost(const Wt::Dbo::ptr<Post> post, PostView::RenderType type, Wt::WContainerWidget *parent) {
|
|
||||||
parent->addWidget(std::make_unique<PostView>(m_session, m_basePath, post, type));
|
|
||||||
}
|
|
||||||
|
|
||||||
void newPost() {
|
|
||||||
Wt::Dbo::Transaction t(m_session);
|
|
||||||
|
|
||||||
authorPanel();
|
|
||||||
WContainerWidget *unpublishedPosts = m_authorPanel->resolve<WContainerWidget *>("unpublished-posts");
|
|
||||||
|
|
||||||
Wt::Dbo::ptr<Post> post(std::make_unique<Post>());
|
|
||||||
|
|
||||||
Post *p = post.modify();
|
|
||||||
p->state = Post::Unpublished;
|
|
||||||
p->author = m_session.user();
|
|
||||||
p->title = "Title";
|
|
||||||
p->briefSrc = "Brief ...";
|
|
||||||
p->bodySrc = "Body ...";
|
|
||||||
|
|
||||||
showPost(post, PostView::Edit, unpublishedPosts);
|
|
||||||
|
|
||||||
t.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void bindPanelTemplates() {
|
|
||||||
if (!m_session.user()) return;
|
|
||||||
|
|
||||||
Wt::Dbo::Transaction t(m_session);
|
|
||||||
|
|
||||||
if (m_authorPanel) {
|
|
||||||
auto newPost = std::make_unique<Wt::WPushButton>(tr("new-post"));
|
|
||||||
newPost->clicked().connect(this, &BlogImpl::newPost);
|
|
||||||
auto unpublishedPosts = std::make_unique<Wt::WContainerWidget>();
|
|
||||||
showPosts(m_session.user()->allPosts(Post::Unpublished), unpublishedPosts.get());
|
|
||||||
|
|
||||||
m_authorPanel->bindString("user", m_session.user()->name);
|
|
||||||
m_authorPanel->bindInt("unpublished-count", (int)m_session.user()->allPosts(Post::Unpublished).size());
|
|
||||||
m_authorPanel->bindInt("published-count", (int)m_session.user()->allPosts(Post::Published).size());
|
|
||||||
m_authorPanel->bindWidget("new-post", std::move(newPost));
|
|
||||||
m_authorPanel->bindWidget("unpublished-posts", std::move(unpublishedPosts));
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#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__
|
|
@ -1,146 +0,0 @@
|
|||||||
#include "CommentView.h"
|
|
||||||
#include "model/BlogSession.h"
|
|
||||||
#include "model/Comment.h"
|
|
||||||
#include "model/User.h"
|
|
||||||
#include <Wt/WPushButton.h>
|
|
||||||
#include <Wt/WTextArea.h>
|
|
||||||
|
|
||||||
CommentView::CommentView(BlogSession &session, long long parentId) : session_(session) {
|
|
||||||
Wt::Dbo::ptr<Comment> parent = session_.load<Comment>(parentId);
|
|
||||||
|
|
||||||
comment_ = std::make_unique<Comment>();
|
|
||||||
comment_.modify()->parent = parent;
|
|
||||||
comment_.modify()->post = parent->post;
|
|
||||||
|
|
||||||
edit();
|
|
||||||
}
|
|
||||||
|
|
||||||
CommentView::CommentView(BlogSession &session, Wt::Dbo::ptr<Comment> comment) : session_(session), comment_(comment) {
|
|
||||||
comment_ = comment;
|
|
||||||
renderView();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommentView::cancel() {
|
|
||||||
if (isNew())
|
|
||||||
removeFromParent();
|
|
||||||
else {
|
|
||||||
Wt::Dbo::Transaction t(session_);
|
|
||||||
renderView();
|
|
||||||
t.commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommentView::renderView() {
|
|
||||||
clear();
|
|
||||||
|
|
||||||
bool isRootComment = !comment_->parent;
|
|
||||||
setTemplateText(isRootComment ? tr("blog-root-comment") : tr("blog-comment"));
|
|
||||||
|
|
||||||
bindString("collapse-expand", Wt::WString::Empty); // NYI
|
|
||||||
|
|
||||||
auto replyText = std::make_unique<Wt::WText>(isRootComment ? tr("comment-add") : tr("comment-reply"));
|
|
||||||
replyText->setStyleClass("link");
|
|
||||||
replyText->clicked().connect(this, &CommentView::reply);
|
|
||||||
bindWidget("reply", std::move(replyText));
|
|
||||||
|
|
||||||
bool mayEdit = session_.user() && (comment_->author == session_.user() || session_.user()->role == User::Admin);
|
|
||||||
|
|
||||||
if (mayEdit) {
|
|
||||||
auto editText = std::make_unique<Wt::WText>(tr("comment-edit"));
|
|
||||||
editText->setStyleClass("link");
|
|
||||||
editText->clicked().connect(this, &CommentView::edit);
|
|
||||||
bindWidget("edit", std::move(editText));
|
|
||||||
} else
|
|
||||||
bindString("edit", Wt::WString::Empty);
|
|
||||||
|
|
||||||
bool mayDelete =
|
|
||||||
(session_.user() && session_.user() == comment_->author) || session_.user() == comment_->post->author;
|
|
||||||
|
|
||||||
if (mayDelete) {
|
|
||||||
auto deleteText = std::make_unique<Wt::WText>(tr("comment-delete"));
|
|
||||||
deleteText->setStyleClass("link");
|
|
||||||
deleteText->clicked().connect(this, &CommentView::rm);
|
|
||||||
bindWidget("delete", std::move(deleteText));
|
|
||||||
} else
|
|
||||||
bindString("delete", Wt::WString::Empty);
|
|
||||||
|
|
||||||
typedef std::vector<Wt::Dbo::ptr<Comment>> CommentVector;
|
|
||||||
CommentVector comments;
|
|
||||||
{
|
|
||||||
Wt::Dbo::collection<Wt::Dbo::ptr<Comment>> cmts = comment_->children.find().orderBy("date");
|
|
||||||
comments.insert(comments.end(), cmts.begin(), cmts.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto children = std::make_unique<Wt::WContainerWidget>();
|
|
||||||
for (int i = (int)comments.size() - 1; i >= 0; --i)
|
|
||||||
children->addWidget(std::make_unique<CommentView>(session_, comments[i]));
|
|
||||||
|
|
||||||
bindWidget("children", std::move(children));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CommentView::isNew() const {
|
|
||||||
return comment_.id() == -1;
|
|
||||||
}
|
|
||||||
void CommentView::rm() {
|
|
||||||
Wt::Dbo::Transaction t(session_);
|
|
||||||
|
|
||||||
comment_.modify()->setDeleted();
|
|
||||||
renderView();
|
|
||||||
|
|
||||||
t.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommentView::reply() {
|
|
||||||
Wt::Dbo::Transaction t(session_);
|
|
||||||
|
|
||||||
Wt::WContainerWidget *c = resolve<Wt::WContainerWidget *>("children");
|
|
||||||
c->insertWidget(0, std::make_unique<CommentView>(session_, comment_.id()));
|
|
||||||
|
|
||||||
t.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommentView::save() {
|
|
||||||
Wt::Dbo::Transaction t(session_);
|
|
||||||
|
|
||||||
bool isNew = comment_.id() == -1;
|
|
||||||
|
|
||||||
Comment *comment = comment_.modify();
|
|
||||||
|
|
||||||
comment->setText(editArea_->text());
|
|
||||||
|
|
||||||
if (isNew) {
|
|
||||||
session_.add(comment_);
|
|
||||||
comment->date = Wt::WDateTime::currentDateTime();
|
|
||||||
comment->author = session_.user();
|
|
||||||
session_.commentsChanged().emit(comment_);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderView();
|
|
||||||
|
|
||||||
t.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommentView::edit() {
|
|
||||||
clear();
|
|
||||||
|
|
||||||
Wt::Dbo::Transaction t(session_);
|
|
||||||
|
|
||||||
setTemplateText(tr("blog-edit-comment"));
|
|
||||||
|
|
||||||
auto editArea = std::make_unique<Wt::WTextArea>();
|
|
||||||
editArea_ = editArea.get();
|
|
||||||
editArea_->setText(comment_->textSrc());
|
|
||||||
editArea_->setFocus();
|
|
||||||
|
|
||||||
auto save = std::make_unique<Wt::WPushButton>(tr("save"));
|
|
||||||
save->clicked().connect(this, &CommentView::save);
|
|
||||||
|
|
||||||
auto cancel = std::make_unique<Wt::WPushButton>(tr("cancel"));
|
|
||||||
cancel->clicked().connect(this, &CommentView::cancel);
|
|
||||||
|
|
||||||
bindWidget("area", std::move(editArea));
|
|
||||||
bindWidget("save", std::move(save));
|
|
||||||
bindWidget("cancel", std::move(cancel));
|
|
||||||
|
|
||||||
t.commit();
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
#ifndef __COMMENTVIEW_H__
|
|
||||||
#define __COMMENTVIEW_H__
|
|
||||||
|
|
||||||
#include <Wt/Dbo/ptr.h>
|
|
||||||
#include <Wt/WTemplate.h>
|
|
||||||
|
|
||||||
class BlogSession;
|
|
||||||
class Comment;
|
|
||||||
|
|
||||||
class CommentView : public Wt::WTemplate {
|
|
||||||
public:
|
|
||||||
CommentView(BlogSession &session, long long parentId);
|
|
||||||
CommentView(BlogSession& session, Wt::Dbo::ptr<Comment> comment);
|
|
||||||
protected:
|
|
||||||
void edit();
|
|
||||||
void save();
|
|
||||||
void cancel();
|
|
||||||
void renderView();
|
|
||||||
bool isNew() const;
|
|
||||||
void reply();
|
|
||||||
void rm();
|
|
||||||
private:
|
|
||||||
BlogSession &session_;
|
|
||||||
Wt::Dbo::ptr<Comment> comment_;
|
|
||||||
Wt::WTextArea *editArea_;
|
|
||||||
};
|
|
||||||
#endif // __COMMENTVIEW_H__
|
|
@ -1,70 +0,0 @@
|
|||||||
#include "EditUsers.h"
|
|
||||||
#include <Wt/WApplication.h>
|
|
||||||
#include <Wt/WBreak.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::onUserClicked(Wt::Dbo::dbo_traits<User>::IdType id) {
|
|
||||||
Wt::WApplication::instance()->setInternalPath(m_basePath + "edituser/" + std::to_string(id), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditUser::switchUser(Wt::Dbo::ptr<User> target) {
|
|
||||||
target_ = target;
|
|
||||||
bindTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditUser::bindTemplate() {
|
|
||||||
bindString("username", target_->name);
|
|
||||||
if (target_->role == User::Admin)
|
|
||||||
roleButton_->setText(tr("demote-admin"));
|
|
||||||
else
|
|
||||||
roleButton_->setText(tr("promote-user"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditUser::switchRole() {
|
|
||||||
Wt::Dbo::Transaction t(session_);
|
|
||||||
target_.reread();
|
|
||||||
if (target_->role == User::Admin)
|
|
||||||
target_.modify()->role = User::Visitor;
|
|
||||||
else
|
|
||||||
target_.modify()->role = User::Admin;
|
|
||||||
t.commit();
|
|
||||||
bindTemplate();
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
#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__
|
|
@ -1,188 +0,0 @@
|
|||||||
#include "PostView.h"
|
|
||||||
#include "CommentView.h"
|
|
||||||
#include "model/BlogSession.h"
|
|
||||||
#include "model/User.h"
|
|
||||||
#include "model/asciidoc.h"
|
|
||||||
#include <Wt/WAnchor.h>
|
|
||||||
#include <Wt/WLineEdit.h>
|
|
||||||
#include <Wt/WLink.h>
|
|
||||||
#include <Wt/WPushButton.h>
|
|
||||||
#include <Wt/WText.h>
|
|
||||||
#include <Wt/WTextArea.h>
|
|
||||||
|
|
||||||
PostView::PostView(BlogSession &session, const std::string &basePath, Wt::Dbo::ptr<Post> post, RenderType type)
|
|
||||||
: session_(session), basePath_(basePath), post_(post) {
|
|
||||||
viewType_ = Brief;
|
|
||||||
render(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostView::render(RenderType type) {
|
|
||||||
if (type != Edit) viewType_ = type;
|
|
||||||
|
|
||||||
clear();
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case Detail: {
|
|
||||||
setTemplateText(tr("blog-post"));
|
|
||||||
|
|
||||||
session_.commentsChanged().connect(this, &PostView::updateCommentCount);
|
|
||||||
|
|
||||||
commentCount_ = bindWidget("comment-count", std::make_unique<Wt::WText>(post_->commentCount()));
|
|
||||||
bindWidget("comments", std::make_unique<CommentView>(session_, post_->rootComment()));
|
|
||||||
bindString("anchor", basePath_ + post_->permaLink());
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Brief: {
|
|
||||||
setTemplateText(tr("blog-post-brief"));
|
|
||||||
|
|
||||||
auto titleAnchor = std::make_unique<Wt::WAnchor>(
|
|
||||||
Wt::WLink(Wt::LinkType::InternalPath, basePath_ + post_->permaLink()), post_->title);
|
|
||||||
bindWidget("title", std::move(titleAnchor));
|
|
||||||
|
|
||||||
if (!post_->briefSrc.empty()) {
|
|
||||||
auto moreAnchor = std::make_unique<Wt::WAnchor>(
|
|
||||||
Wt::WLink(Wt::LinkType::InternalPath, basePath_ + post_->permaLink() + "/more"), tr("blog-read-more"));
|
|
||||||
bindWidget("read-more", std::move(moreAnchor));
|
|
||||||
} else {
|
|
||||||
bindString("read-more", Wt::WString::Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto commentsAnchor = std::make_unique<Wt::WAnchor>(
|
|
||||||
Wt::WLink(Wt::LinkType::InternalPath, basePath_ + post_->permaLink() + "/comments"));
|
|
||||||
commentCount_ = commentsAnchor->addWidget(std::make_unique<Wt::WText>("(" + post_->commentCount() + ")"));
|
|
||||||
bindWidget("comment-count", std::move(commentsAnchor));
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Edit: {
|
|
||||||
setTemplateText(tr("blog-post-edit"));
|
|
||||||
|
|
||||||
titleEdit_ = bindWidget("title-edit", std::make_unique<Wt::WLineEdit>(post_->title));
|
|
||||||
briefEdit_ = bindWidget("brief-edit", std::make_unique<Wt::WTextArea>(post_->briefSrc));
|
|
||||||
bodyEdit_ = bindWidget("body-edit", std::make_unique<Wt::WTextArea>(post_->bodySrc));
|
|
||||||
|
|
||||||
auto saveButton = bindWidget("save", std::make_unique<Wt::WPushButton>(tr("save")));
|
|
||||||
auto cancelButton = bindWidget("cancel", std::make_unique<Wt::WPushButton>(tr("cancel")));
|
|
||||||
|
|
||||||
saveButton->clicked().connect(this, &PostView::saveEdit);
|
|
||||||
cancelButton->clicked().connect(this, &PostView::showView);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == Detail || type == Brief) {
|
|
||||||
if (session_.user() == post_->author) {
|
|
||||||
std::unique_ptr<Wt::WPushButton> publishButton;
|
|
||||||
if (post_->state != Post::Published) {
|
|
||||||
publishButton = std::make_unique<Wt::WPushButton>(tr("publish"));
|
|
||||||
publishButton->clicked().connect(this, &PostView::publish);
|
|
||||||
} else {
|
|
||||||
publishButton = std::make_unique<Wt::WPushButton>(tr("retract"));
|
|
||||||
publishButton->clicked().connect(this, &PostView::retract);
|
|
||||||
}
|
|
||||||
bindWidget("publish", std::move(publishButton));
|
|
||||||
|
|
||||||
auto editButton(std::make_unique<Wt::WPushButton>(tr("edit")));
|
|
||||||
editButton->clicked().connect(this, &PostView::showEdit);
|
|
||||||
bindWidget("edit", std::move(editButton));
|
|
||||||
|
|
||||||
auto deleteButton(std::make_unique<Wt::WPushButton>(tr("delete")));
|
|
||||||
deleteButton->clicked().connect(this, &PostView::rm);
|
|
||||||
bindWidget("delete", std::move(deleteButton));
|
|
||||||
} else {
|
|
||||||
bindString("publish", Wt::WString::Empty);
|
|
||||||
bindString("edit", Wt::WString::Empty);
|
|
||||||
bindString("delete", Wt::WString::Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto postAnchor = std::make_unique<Wt::WAnchor>(
|
|
||||||
Wt::WLink(Wt::LinkType::InternalPath, basePath_ + "author/" + post_->author->name.toUTF8()),
|
|
||||||
post_->author->name);
|
|
||||||
bindWidget("author", std::move(postAnchor));
|
|
||||||
}
|
|
||||||
void PostView::publish() {
|
|
||||||
setState(Post::Published);
|
|
||||||
}
|
|
||||||
void PostView::showEdit() {
|
|
||||||
Wt::Dbo::Transaction t(session_);
|
|
||||||
|
|
||||||
render(Edit);
|
|
||||||
|
|
||||||
t.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostView::rm() {
|
|
||||||
Wt::Dbo::Transaction t(session_);
|
|
||||||
post_.remove();
|
|
||||||
t.commit();
|
|
||||||
|
|
||||||
this->removeFromParent();
|
|
||||||
}
|
|
||||||
void PostView::retract() {
|
|
||||||
setState(Post::Unpublished);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostView::setState(Post::State state) {
|
|
||||||
Wt::Dbo::Transaction t(session_);
|
|
||||||
|
|
||||||
post_.modify()->state = state;
|
|
||||||
if (state == Post::Published) post_.modify()->date = Wt::WDateTime::currentDateTime();
|
|
||||||
|
|
||||||
render(viewType_);
|
|
||||||
|
|
||||||
t.commit();
|
|
||||||
}
|
|
||||||
void PostView::saveEdit() {
|
|
||||||
Wt::Dbo::Transaction t(session_);
|
|
||||||
|
|
||||||
bool newPost = post_.id() == -1;
|
|
||||||
|
|
||||||
Post *post = post_.modify();
|
|
||||||
|
|
||||||
post->title = titleEdit_->text();
|
|
||||||
post->briefSrc = briefEdit_->text();
|
|
||||||
post->bodySrc = bodyEdit_->text();
|
|
||||||
|
|
||||||
post->briefHtml = asciidoc(post->briefSrc);
|
|
||||||
post->bodyHtml = asciidoc(post->bodySrc);
|
|
||||||
|
|
||||||
if (newPost) {
|
|
||||||
session_.add(post_);
|
|
||||||
|
|
||||||
post->date = Wt::WDateTime::currentDateTime();
|
|
||||||
post->state = Post::Unpublished;
|
|
||||||
post->author = session_.user();
|
|
||||||
|
|
||||||
Wt::Dbo::ptr<Comment> rootComment = session_.add(std::make_unique<Comment>());
|
|
||||||
rootComment.modify()->post = post_;
|
|
||||||
}
|
|
||||||
|
|
||||||
session_.flush();
|
|
||||||
|
|
||||||
render(viewType_);
|
|
||||||
|
|
||||||
t.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostView::showView() {
|
|
||||||
if (post_.id() == -1)
|
|
||||||
this->removeFromParent();
|
|
||||||
else {
|
|
||||||
Wt::Dbo::Transaction t(session_);
|
|
||||||
render(viewType_);
|
|
||||||
t.commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void PostView::updateCommentCount(Wt::Dbo::ptr<Comment> comment) {
|
|
||||||
if (comment->post == post_) {
|
|
||||||
std::string count = comment->post->commentCount();
|
|
||||||
|
|
||||||
if (commentCount_->text().toUTF8()[0] == '(')
|
|
||||||
commentCount_->setText("(" + count + ")");
|
|
||||||
else
|
|
||||||
commentCount_->setText(count);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
#ifndef __POSTVIEW_H__
|
|
||||||
#define __POSTVIEW_H__
|
|
||||||
|
|
||||||
#include "model/Post.h"
|
|
||||||
#include <Wt/WTemplate.h>
|
|
||||||
|
|
||||||
class BlogSession;
|
|
||||||
|
|
||||||
class PostView : public Wt::WTemplate {
|
|
||||||
public:
|
|
||||||
enum RenderType {
|
|
||||||
Brief,
|
|
||||||
Detail,
|
|
||||||
Edit,
|
|
||||||
};
|
|
||||||
|
|
||||||
PostView(BlogSession &session, const std::string &basePath, Wt::Dbo::ptr<Post> post, RenderType type);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void render(RenderType type);
|
|
||||||
void updateCommentCount(Wt::Dbo::ptr<Comment> comment);
|
|
||||||
void saveEdit();
|
|
||||||
void showView();
|
|
||||||
void publish();
|
|
||||||
void retract();
|
|
||||||
void setState(Post::State state);
|
|
||||||
void showEdit();
|
|
||||||
void rm();
|
|
||||||
|
|
||||||
private:
|
|
||||||
BlogSession &session_;
|
|
||||||
std::string basePath_;
|
|
||||||
Wt::Dbo::ptr<Post> post_;
|
|
||||||
|
|
||||||
RenderType viewType_;
|
|
||||||
Wt::WText *commentCount_;
|
|
||||||
Wt::WLineEdit *titleEdit_;
|
|
||||||
Wt::WTextArea *briefEdit_, *bodyEdit_;
|
|
||||||
};
|
|
||||||
#endif // __POSTVIEW_H__
|
|
Loading…
Reference in New Issue
Block a user