Adding User edit/create view
This commit is contained in:
@@ -22,6 +22,7 @@ lms_js_DATA = \
|
||||
|
||||
lms_approot_DATA = \
|
||||
approot/admin-database.xml \
|
||||
approot/admin-user.xml \
|
||||
approot/admin-users.xml \
|
||||
approot/admin-initwizard.xml \
|
||||
approot/artist.xml \
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<messages xmlns:if="Wt.WTemplate.conditions">
|
||||
|
||||
<message id="Lms.Admin.User.template">
|
||||
<div class="page-header">
|
||||
<h2>${title}</h2>
|
||||
</div>
|
||||
<div class="form-horizontal">
|
||||
${<if-has-login>}
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="${id:login}">
|
||||
${tr:Lms.login}
|
||||
</label>
|
||||
<div class="col-sm-5">
|
||||
${login}
|
||||
</div>
|
||||
<div class="help-block col-sm-5">
|
||||
${login-info}
|
||||
</div>
|
||||
</div>
|
||||
${</if-has-login>}
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="${id:password}">
|
||||
${tr:Lms.password}
|
||||
</label>
|
||||
<div class="col-sm-5">
|
||||
${password}
|
||||
</div>
|
||||
<div class="help-block col-sm-5">
|
||||
${password-info}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="${id:audio-bitrate-limit}">
|
||||
Audio Bitrate Limit
|
||||
</label>
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
${audio-bitrate-limit}
|
||||
<span class="input-group-addon">kbps</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="help-block col-sm-5">
|
||||
${audio-bitrate-limit-info}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
${save-btn class="btn-primary"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</message>
|
||||
|
||||
</messages>
|
||||
@@ -15,6 +15,7 @@
|
||||
<message id="Lms.password-confirm">Confirm password</message>
|
||||
<message id="Lms.passwords-dont-match">Passwords don't match</message>
|
||||
<message id="Lms.quit-confirm">Are you sure?</message>
|
||||
<message id="Lms.save">Save</message>
|
||||
|
||||
<!--Administration-->
|
||||
<message id="Lms.Admin.Database.daily">Daily</message>
|
||||
@@ -36,6 +37,11 @@
|
||||
<message id="Lms.Admin.Users.edit">Edit</message>
|
||||
<message id="Lms.Admin.Users.users">Users</message>
|
||||
|
||||
<message id="Lms.Admin.User.user-create">New user</message>
|
||||
<message id="Lms.Admin.User.user-created">New user created!</message>
|
||||
<message id="Lms.Admin.User.user-edit">Edit user {1}</message>
|
||||
<message id="Lms.Admin.User.user-updated">User updated!</message>
|
||||
|
||||
<!--Wizard-->
|
||||
<message id="Lms.Admin.InitWizard.done">Administration account created. Please refresh to continue!</message>
|
||||
<message id="Lms.Admin.InitWizard.header">Create administrator account</message>
|
||||
|
||||
@@ -29,6 +29,7 @@ lms_SOURCES = \
|
||||
$(srcdir)/ui/SettingsView.cpp \
|
||||
$(srcdir)/ui/admin/DatabaseSettingsView.cpp \
|
||||
$(srcdir)/ui/admin/InitWizardView.cpp \
|
||||
$(srcdir)/ui/admin/UserView.cpp \
|
||||
$(srcdir)/ui/admin/UsersView.cpp \
|
||||
$(srcdir)/ui/common/InputRange.cpp \
|
||||
$(srcdir)/ui/common/LineEdit.cpp \
|
||||
|
||||
@@ -164,7 +164,6 @@ Handler::getCurrentUser()
|
||||
User::pointer
|
||||
Handler::getUser(const Wt::Auth::User& authUser)
|
||||
{
|
||||
|
||||
if (!authUser.isValid()) {
|
||||
LMS_LOG(DB, ERROR) << "Handler::getUser: invalid authUser";
|
||||
return User::pointer();
|
||||
@@ -172,13 +171,21 @@ Handler::getUser(const Wt::Auth::User& authUser)
|
||||
|
||||
Wt::Dbo::ptr<AuthInfo> authInfo = _users->find(authUser);
|
||||
|
||||
User::pointer user = authInfo->user();
|
||||
return authInfo->user();
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
user = _session.add(new User());
|
||||
authInfo.modify()->setUser(user);
|
||||
User::pointer
|
||||
Handler::createUser(const Wt::Auth::User& authUser)
|
||||
{
|
||||
if (!authUser.isValid()) {
|
||||
LMS_LOG(DB, ERROR) << "Handler::getUser: invalid authUser";
|
||||
return User::pointer();
|
||||
}
|
||||
|
||||
User::pointer user = _session.add(new User());
|
||||
Wt::Dbo::ptr<AuthInfo> authInfo = _users->find(authUser);
|
||||
authInfo.modify()->setUser(user);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,8 @@ class Handler
|
||||
Wt::Dbo::Session& getSession() { return _session; }
|
||||
|
||||
Wt::Dbo::ptr<User> getCurrentUser(); // get the current user, may return empty
|
||||
Wt::Dbo::ptr<User> getUser(const Wt::Auth::User& authUser); // Get or create the given user
|
||||
Wt::Dbo::ptr<User> getUser(const Wt::Auth::User& authUser);
|
||||
Wt::Dbo::ptr<User> createUser(const Wt::Auth::User& authUser);
|
||||
|
||||
Wt::Auth::AbstractUserDatabase& getUserDatabase();
|
||||
Wt::Auth::Login& getLogin() { return _login; }
|
||||
|
||||
+7
-11
@@ -32,7 +32,7 @@ User::audioBitrates =
|
||||
};
|
||||
|
||||
User::User()
|
||||
: _maxAudioBitrate(maxAudioBitrate),
|
||||
: _maxAudioBitrate(audioBitrates.back()),
|
||||
_isAdmin(false),
|
||||
_audioBitrate(defaultAudioBitrate),
|
||||
_audioEncoding(AudioEncoding::AUTO),
|
||||
@@ -63,31 +63,27 @@ User::getById(Wt::Dbo::Session& session, id_type id)
|
||||
void
|
||||
User::setAudioBitrate(std::size_t bitrate)
|
||||
{
|
||||
_audioBitrate = std::min(bitrate, std::min(static_cast<std::size_t>(_maxAudioBitrate), audioBitrates.back()));
|
||||
_audioBitrate = std::min(bitrate, static_cast<std::size_t>(_maxAudioBitrate));
|
||||
}
|
||||
|
||||
void
|
||||
User::setMaxAudioBitrate(std::size_t bitrate)
|
||||
{
|
||||
_maxAudioBitrate = std::min(bitrate, static_cast<std::size_t>(_maxAudioBitrate));
|
||||
_maxAudioBitrate = std::min(bitrate, audioBitrates.back());
|
||||
if (_audioBitrate > _maxAudioBitrate)
|
||||
_audioBitrate = _maxAudioBitrate;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
User::getAudioBitrate(void) const
|
||||
{
|
||||
if (!isAdmin())
|
||||
return std::min(static_cast<std::size_t>(_audioBitrate), std::min(static_cast<std::size_t>(_maxAudioBitrate), audioBitrates.back()));
|
||||
else
|
||||
return std::min(static_cast<std::size_t>(_audioBitrate), audioBitrates.back());
|
||||
return _audioBitrate;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
User::getMaxAudioBitrate(void) const
|
||||
{
|
||||
if (!isAdmin())
|
||||
return std::min(static_cast<std::size_t>(_maxAudioBitrate), audioBitrates.back());
|
||||
else
|
||||
return audioBitrates.back();
|
||||
return _maxAudioBitrate;
|
||||
}
|
||||
|
||||
} // namespace Database
|
||||
|
||||
@@ -41,7 +41,7 @@ enum class AudioEncoding
|
||||
WEBMA,
|
||||
};
|
||||
|
||||
class User
|
||||
class User : public Wt::Dbo::Dbo<User>
|
||||
{
|
||||
public:
|
||||
typedef Wt::Dbo::dbo_traits<User>::IdType id_type;
|
||||
@@ -70,9 +70,9 @@ class User
|
||||
void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; }
|
||||
|
||||
// read
|
||||
bool isAdmin() const {return _isAdmin;}
|
||||
bool isAdmin() const { return _isAdmin; }
|
||||
std::size_t getAudioBitrate() const;
|
||||
AudioEncoding getAudioEncoding() const { return _audioEncoding;}
|
||||
AudioEncoding getAudioEncoding() const { return _audioEncoding; }
|
||||
std::size_t getMaxAudioBitrate() const;
|
||||
std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; }
|
||||
|
||||
@@ -90,7 +90,6 @@ class User
|
||||
|
||||
private:
|
||||
|
||||
static const std::size_t maxAudioBitrate = 320000;
|
||||
static const std::size_t defaultAudioBitrate = 128000;
|
||||
|
||||
// Admin defined settings
|
||||
|
||||
@@ -30,8 +30,6 @@
|
||||
#include "feature/FeatureExtractor.hpp"
|
||||
|
||||
#include "scanner/MediaScanner.hpp"
|
||||
//#include "database/updater/DatabaseFeatureExtractor.hpp"
|
||||
//#include "database/updater/DatabaseHighLevelCluster.hpp"
|
||||
|
||||
#include "ui/LmsApplication.hpp"
|
||||
|
||||
|
||||
+13
-14
@@ -57,20 +57,8 @@ Auth::Auth(Wt::WContainerWidget *parent)
|
||||
|
||||
auto loginBtn = new Wt::WPushButton(Wt::WString::tr("Lms.login"));
|
||||
bindWidget("login-btn", loginBtn);
|
||||
loginBtn->clicked().connect(std::bind([=]
|
||||
{
|
||||
updateModel(_model);
|
||||
|
||||
if (_model->validate())
|
||||
_model->login(DbHandler().getLogin());
|
||||
else
|
||||
updateView(_model);
|
||||
}));
|
||||
|
||||
password->enterPressed().connect(std::bind([=]
|
||||
{
|
||||
loginBtn->clicked().emit(Wt::WMouseEvent());
|
||||
}));
|
||||
loginBtn->clicked().connect(this, &Auth::processAuth);
|
||||
password->enterPressed().connect(this, &Auth::processAuth);
|
||||
|
||||
DbHandler().getLogin().changed().connect(std::bind([=]
|
||||
{
|
||||
@@ -84,6 +72,17 @@ Auth::Auth(Wt::WContainerWidget *parent)
|
||||
updateView(_model);
|
||||
}
|
||||
|
||||
void
|
||||
Auth::processAuth()
|
||||
{
|
||||
updateModel(_model);
|
||||
|
||||
if (_model->validate())
|
||||
_model->login(DbHandler().getLogin());
|
||||
else
|
||||
updateView(_model);
|
||||
}
|
||||
|
||||
void
|
||||
Auth::logout()
|
||||
{
|
||||
|
||||
@@ -34,6 +34,8 @@ class Auth : public Wt::WTemplateFormView
|
||||
void logout();
|
||||
|
||||
private:
|
||||
void processAuth();
|
||||
|
||||
Wt::Auth::AuthModel* _model;
|
||||
};
|
||||
|
||||
|
||||
+39
-10
@@ -39,6 +39,7 @@
|
||||
|
||||
#include "admin/InitWizardView.hpp"
|
||||
#include "admin/DatabaseSettingsView.hpp"
|
||||
#include "admin/UserView.hpp"
|
||||
#include "admin/UsersView.hpp"
|
||||
|
||||
#include "resource/ImageResource.hpp"
|
||||
@@ -87,6 +88,7 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnecti
|
||||
|
||||
// Add a resource bundle
|
||||
messageResourceBundle().use(appRoot() + "admin-database");
|
||||
messageResourceBundle().use(appRoot() + "admin-user");
|
||||
messageResourceBundle().use(appRoot() + "admin-users");
|
||||
messageResourceBundle().use(appRoot() + "admin-wizard");
|
||||
messageResourceBundle().use(appRoot() + "artist");
|
||||
@@ -103,6 +105,9 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnecti
|
||||
messageResourceBundle().use(appRoot() + "tracks");
|
||||
messageResourceBundle().use(appRoot() + "templates");
|
||||
|
||||
// Require js here to avoid async problems
|
||||
require("/js/mediaplayer.js");
|
||||
|
||||
setTitle("LMS");
|
||||
|
||||
// If here is no account in the database, launch the first connection wizard
|
||||
@@ -121,7 +126,18 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnecti
|
||||
}
|
||||
else
|
||||
{
|
||||
DbHandler().getLogin().changed().connect(this, &LmsApplication::handleAuthEvent);
|
||||
DbHandler().getLogin().changed().connect(std::bind([=]
|
||||
{
|
||||
try
|
||||
{
|
||||
handleAuthEvent();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
LMS_LOG(UI, ERROR) << "Error while handling auth event: " << e.what();
|
||||
throw std::runtime_error("Internal error");
|
||||
}
|
||||
}));
|
||||
|
||||
_auth = new Auth();
|
||||
root()->addWidget(_auth);
|
||||
@@ -199,6 +215,7 @@ LmsApplication::goHome()
|
||||
void
|
||||
LmsApplication::quit()
|
||||
{
|
||||
setConfirmCloseMessage("");
|
||||
WApplication::quit("");
|
||||
redirect("/");
|
||||
}
|
||||
@@ -211,6 +228,7 @@ enum IdxRoot
|
||||
IdxSettings,
|
||||
IdxAdminDatabase,
|
||||
IdxAdminUsers,
|
||||
IdxAdminUser,
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -233,6 +251,7 @@ handlePathChange(Wt::WStackedWidget* stack, bool isAdmin)
|
||||
{ "/settings", IdxSettings, false },
|
||||
{ "/admin/database", IdxAdminDatabase, true },
|
||||
{ "/admin/users", IdxAdminUsers, true },
|
||||
{ "/admin/user", IdxAdminUser, true },
|
||||
};
|
||||
|
||||
LMS_LOG(UI, DEBUG) << "Internal path changed to '" << wApp->internalPath() << "'";
|
||||
@@ -270,8 +289,6 @@ LmsApplication::handleAuthEvent(void)
|
||||
_isAdmin = CurrentUser()->isAdmin();
|
||||
}
|
||||
|
||||
require("/js/mediaplayer.js");
|
||||
|
||||
_imageResource = new ImageResource(_db, root());
|
||||
_transcodeResource = new TranscodeResource(_db, root());
|
||||
|
||||
@@ -368,6 +385,9 @@ LmsApplication::handleAuthEvent(void)
|
||||
|
||||
auto users = new UsersView();
|
||||
mainStack->addWidget(users);
|
||||
|
||||
auto user = new UserView();
|
||||
mainStack->addWidget(user);
|
||||
}
|
||||
|
||||
explore->tracksAdd.connect(std::bind([=] (std::vector<Database::Track::pointer> tracks)
|
||||
@@ -411,7 +431,7 @@ LmsApplication::handleAuthEvent(void)
|
||||
{
|
||||
Wt::WServer::instance()->post(sessionId, [=]
|
||||
{
|
||||
notify(Wt::WString::tr("Lms.Admin.Database.scan-complete")
|
||||
notifyMsg(Wt::WString::tr("Lms.Admin.Database.scan-complete")
|
||||
.arg(stats.nbFiles())
|
||||
.arg(stats.additions)
|
||||
.arg(stats.deletions)
|
||||
@@ -431,17 +451,26 @@ LmsApplication::handleAuthEvent(void)
|
||||
}
|
||||
|
||||
void
|
||||
LmsApplication::notify(const Wt::WString& message)
|
||||
LmsApplication::notify(const Wt::WEvent& event)
|
||||
{
|
||||
try
|
||||
{
|
||||
WApplication::notify(event);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
LMS_LOG(UI, ERROR) << "Caught exception: " << e.what();
|
||||
throw std::runtime_error("Internal error");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LmsApplication::notifyMsg(const Wt::WString& message)
|
||||
{
|
||||
LMS_LOG(UI, INFO) << "Notifying message '" << message.toUTF8() << "'";
|
||||
root()->addWidget(new Wt::WText(message));
|
||||
}
|
||||
|
||||
void notify(const Wt::WString& message)
|
||||
{
|
||||
LmsApplication::instance()->notify(message);
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ class LmsApplication : public Wt::WApplication
|
||||
// Utils
|
||||
void goHome();
|
||||
void quit();
|
||||
void notify(const Wt::WString& message);
|
||||
void notifyMsg(const Wt::WString& message);
|
||||
|
||||
static Wt::WAnchor* createArtistAnchor(Database::Artist::pointer artist, bool addText = true);
|
||||
static Wt::WAnchor* createReleaseAnchor(Database::Release::pointer release, bool addText = true);
|
||||
@@ -59,6 +59,7 @@ class LmsApplication : public Wt::WApplication
|
||||
LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner);
|
||||
|
||||
void handleAuthEvent(void);
|
||||
void notify(const Wt::WEvent& event) override;
|
||||
|
||||
Database::Handler _db;
|
||||
Auth* _auth;
|
||||
|
||||
+12
-3
@@ -156,13 +156,13 @@ class SettingsModel : public Wt::WFormModel
|
||||
std::size_t bitrate(int row)
|
||||
{
|
||||
return boost::any_cast<std::size_t>
|
||||
(_bitrateModel->data(_encodingModel->index(row, 0), Wt::UserRole));
|
||||
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::UserRole));
|
||||
}
|
||||
|
||||
Wt::WString bitrateString(int row)
|
||||
{
|
||||
return boost::any_cast<Wt::WString>
|
||||
(_bitrateModel->data(_encodingModel->index(row, 0), Wt::DisplayRole));
|
||||
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::DisplayRole));
|
||||
}
|
||||
|
||||
boost::optional<int> getEncodingRow(Wt::WString value)
|
||||
@@ -207,9 +207,18 @@ class SettingsModel : public Wt::WFormModel
|
||||
|
||||
_bitrateModel = new Wt::WStringListModel(this);
|
||||
|
||||
std::size_t maxAudioBitrate;
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(DboSession());
|
||||
maxAudioBitrate = CurrentUser()->getMaxAudioBitrate();
|
||||
}
|
||||
|
||||
std::size_t id = 0;
|
||||
for (auto bitrate : Database::User::audioBitrates)
|
||||
{
|
||||
if (bitrate > maxAudioBitrate)
|
||||
break;
|
||||
|
||||
_bitrateModel->addString( Wt::WString::fromUTF8(std::to_string(bitrate / 1000)) );
|
||||
_bitrateModel->setData( id++, 0, bitrate, Wt::UserRole);
|
||||
}
|
||||
@@ -282,7 +291,7 @@ SettingsView::SettingsView(Wt::WContainerWidget *parent)
|
||||
if (model->validate())
|
||||
{
|
||||
model->saveData();
|
||||
LmsApp->notify(Wt::WString::tr("Lms.Settings.settings-saved"));
|
||||
LmsApp->notifyMsg(Wt::WString::tr("Lms.Settings.settings-saved"));
|
||||
}
|
||||
|
||||
// Udate the view: Delete any validation message in the view, etc.
|
||||
|
||||
@@ -267,7 +267,7 @@ DatabaseSettingsView::DatabaseSettingsView(Wt::WContainerWidget *parent)
|
||||
model->saveData();
|
||||
|
||||
LmsApp->getMediaScanner().reschedule();
|
||||
LmsApp->notify(Wt::WString::tr("Lms.Admin.Database.settings-saved"));
|
||||
LmsApp->notifyMsg(Wt::WString::tr("Lms.Admin.Database.settings-saved"));
|
||||
}
|
||||
|
||||
// Udate the view: Delete any validation message in the view, etc.
|
||||
@@ -284,7 +284,7 @@ DatabaseSettingsView::DatabaseSettingsView(Wt::WContainerWidget *parent)
|
||||
immScanBtn->clicked().connect(std::bind([=] ()
|
||||
{
|
||||
LmsApp->getMediaScanner().scheduleImmediateScan();
|
||||
LmsApp->notify(Wt::WString::tr("Lms.Admin.Database.scan-launched"));
|
||||
LmsApp->notifyMsg(Wt::WString::tr("Lms.Admin.Database.scan-launched"));
|
||||
}));
|
||||
|
||||
updateView(model);
|
||||
|
||||
@@ -148,7 +148,7 @@ InitWizardView::InitWizardView(Wt::WContainerWidget *parent)
|
||||
if (model->validate())
|
||||
{
|
||||
model->saveData();
|
||||
LmsApp->notify(Wt::WString::tr("Lms.Admin.InitWizard.done"));
|
||||
LmsApp->notifyMsg(Wt::WString::tr("Lms.Admin.InitWizard.done"));
|
||||
saveButton->setEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/WApplication>
|
||||
#include <Wt/WCheckBox>
|
||||
#include <Wt/WComboBox>
|
||||
#include <Wt/WLineEdit>
|
||||
#include <Wt/WPushButton>
|
||||
#include <Wt/WTemplateFormView>
|
||||
|
||||
#include <Wt/WFormModel>
|
||||
#include <Wt/WStringListModel>
|
||||
|
||||
#include "common/Validators.hpp"
|
||||
#include "utils/Utils.hpp"
|
||||
#include "utils/Logger.hpp"
|
||||
|
||||
#include "LmsApplication.hpp"
|
||||
#include "UserView.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
|
||||
class UserModel : public Wt::WFormModel
|
||||
{
|
||||
public:
|
||||
static const Field LoginField;
|
||||
static const Field PasswordField;
|
||||
static const Field BitrateLimitField;
|
||||
|
||||
UserModel(boost::optional<Database::User::id_type> userId, Wt::WObject *parent = 0)
|
||||
: Wt::WFormModel(parent),
|
||||
_userId(userId)
|
||||
{
|
||||
if (!_userId)
|
||||
{
|
||||
addField(LoginField);
|
||||
setValidator(LoginField, createNameValidator());
|
||||
}
|
||||
|
||||
addField(PasswordField);
|
||||
addField(BitrateLimitField);
|
||||
|
||||
|
||||
if (!_userId)
|
||||
setValidator(PasswordField, createMandatoryValidator());
|
||||
|
||||
initializeModels();
|
||||
|
||||
// populate the model with initial data
|
||||
loadData();
|
||||
}
|
||||
|
||||
void loadData()
|
||||
{
|
||||
if (!_userId)
|
||||
return;
|
||||
|
||||
Wt::Dbo::Transaction transaction(DboSession());
|
||||
|
||||
Wt::Auth::User authUser = DbHandler().getUserDatabase().findWithId( std::to_string(*_userId) );
|
||||
Database::User::pointer user = DbHandler().getUser(authUser);
|
||||
|
||||
if (user == CurrentUser())
|
||||
throw std::runtime_error("Cannot edit ourselves");
|
||||
|
||||
auto bitrate = getBitrateLimitRow(user->getMaxAudioBitrate());
|
||||
if (bitrate)
|
||||
setValue(BitrateLimitField, bitrateLimitString(*bitrate));
|
||||
}
|
||||
|
||||
void saveData()
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(DboSession());
|
||||
|
||||
if (_userId)
|
||||
{
|
||||
// Update user
|
||||
Wt::Auth::User authUser = DbHandler().getUserDatabase().findWithId( std::to_string(*_userId) );
|
||||
Database::User::pointer user = DbHandler().getUser( authUser );
|
||||
|
||||
// Account
|
||||
if (!valueText(PasswordField).empty())
|
||||
Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField));
|
||||
|
||||
auto bitrateLimitRow = getBitrateLimitRow(Wt::asString(value(BitrateLimitField)));
|
||||
user.modify()->setMaxAudioBitrate(bitrateLimit(*bitrateLimitRow));
|
||||
LMS_LOG(UI, DEBUG) << "Max audio bitrate set to " << bitrateLimit(*bitrateLimitRow);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create user
|
||||
Wt::Auth::User authUser = DbHandler().getUserDatabase().registerNew();
|
||||
Database::User::pointer user = DbHandler().createUser(authUser);
|
||||
|
||||
// Account
|
||||
authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(LoginField));
|
||||
Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField));
|
||||
|
||||
auto bitrateLimitRow = getBitrateLimitRow(Wt::asString(value(BitrateLimitField)));
|
||||
user.modify()->setMaxAudioBitrate(bitrateLimit(*bitrateLimitRow));
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<int> getBitrateLimitRow(Wt::WString value)
|
||||
{
|
||||
for (int i = 0; i < _bitrateModel->rowCount(); ++i)
|
||||
{
|
||||
if (bitrateLimitString(i) == value)
|
||||
return i;
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
boost::optional<int> getBitrateLimitRow(std::size_t value)
|
||||
{
|
||||
for (int i = 0; i < _bitrateModel->rowCount(); ++i)
|
||||
{
|
||||
if (bitrateLimit(i) == value)
|
||||
return i;
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
std::size_t bitrateLimit(int row)
|
||||
{
|
||||
return boost::any_cast<std::size_t>
|
||||
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::UserRole));
|
||||
}
|
||||
|
||||
Wt::WString bitrateLimitString(int row)
|
||||
{
|
||||
return boost::any_cast<Wt::WString>
|
||||
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::DisplayRole));
|
||||
}
|
||||
|
||||
Wt::WAbstractItemModel *bitrateModel() { return _bitrateModel; }
|
||||
|
||||
private:
|
||||
|
||||
void initializeModels()
|
||||
{
|
||||
_bitrateModel = new Wt::WStringListModel(this);
|
||||
|
||||
std::size_t id = 0;
|
||||
for (auto bitrate : Database::User::audioBitrates)
|
||||
{
|
||||
_bitrateModel->addString( Wt::WString::fromUTF8(std::to_string(bitrate / 1000)) );
|
||||
_bitrateModel->setData( id++, 0, bitrate, Wt::UserRole);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Wt::WStringListModel* _bitrateModel;
|
||||
boost::optional<Database::User::id_type> _userId;
|
||||
};
|
||||
|
||||
const Wt::WFormModel::Field UserModel::LoginField = "login";
|
||||
const Wt::WFormModel::Field UserModel::PasswordField = "password";
|
||||
const Wt::WFormModel::Field UserModel::BitrateLimitField = "audio-bitrate-limit";
|
||||
|
||||
UserView::UserView(Wt::WContainerWidget* parent)
|
||||
: Wt::WContainerWidget(parent)
|
||||
{
|
||||
wApp->internalPathChanged().connect(std::bind([=]
|
||||
{
|
||||
refresh();
|
||||
}));
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void
|
||||
UserView::refresh()
|
||||
{
|
||||
if (!wApp->internalPathMatches("/admin/user"))
|
||||
return;
|
||||
|
||||
auto userId = readLong(wApp->internalPathNextPart("/admin/user/"));
|
||||
|
||||
clear();
|
||||
|
||||
auto t = new Wt::WTemplateFormView(Wt::WString::tr("Lms.Admin.User.template"), this);
|
||||
|
||||
t->addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||
t->addFunction("id", &Wt::WTemplate::Functions::id);
|
||||
|
||||
auto model = new UserModel(userId ? boost::make_optional<Database::User::id_type>(*userId) : boost::none, this);
|
||||
|
||||
if (userId)
|
||||
{
|
||||
auto authUser = DbHandler().getUserDatabase().findWithId( std::to_string(*userId) );
|
||||
auto name = authUser.identity(Wt::Auth::Identity::LoginName);
|
||||
t->bindString("title", Wt::WString::tr("Lms.Admin.User.user-edit").arg(name), Wt::PlainText);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Login
|
||||
t->setCondition("if-has-login", true);
|
||||
t->setFormWidget(UserModel::LoginField, new Wt::WLineEdit());
|
||||
t->bindString("title", Wt::WString::tr("Lms.Admin.User.user-create"));
|
||||
}
|
||||
|
||||
|
||||
// Password
|
||||
Wt::WLineEdit* passwordEdit = new Wt::WLineEdit();
|
||||
t->setFormWidget(UserModel::PasswordField, passwordEdit );
|
||||
passwordEdit->setEchoMode(Wt::WLineEdit::Password);
|
||||
|
||||
// AudioBitrate
|
||||
Wt::WComboBox *bitrateCB = new Wt::WComboBox();
|
||||
t->setFormWidget(UserModel::BitrateLimitField, bitrateCB);
|
||||
bitrateCB->setModel(model->bitrateModel());
|
||||
|
||||
auto saveBtn = new Wt::WPushButton(Wt::WString::tr(userId ? "Lms.save" : "Lms.create"));
|
||||
t->bindWidget("save-btn", saveBtn);
|
||||
saveBtn->clicked().connect(std::bind([=]
|
||||
{
|
||||
t->updateModel(model);
|
||||
|
||||
if (model->validate())
|
||||
{
|
||||
model->saveData();
|
||||
LmsApp->notifyMsg(Wt::WString::tr(userId ? "Lms.Admin.User.user-updated" : "Lms.Admin.User.user-created"));
|
||||
LmsApp->setInternalPath("/admin/users", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
t->updateView(model);
|
||||
}
|
||||
}));
|
||||
|
||||
t->updateView(model);
|
||||
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Wt/WContainerWidget>
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class UserView : public Wt::WContainerWidget
|
||||
{
|
||||
public:
|
||||
UserView(Wt::WContainerWidget *parent = 0);
|
||||
|
||||
private:
|
||||
void refresh();
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
+37
-10
@@ -31,7 +31,6 @@ namespace UserInterface {
|
||||
UsersView::UsersView(Wt::WContainerWidget *parent)
|
||||
: Wt::WContainerWidget(parent)
|
||||
{
|
||||
LMS_LOG(UI, DEBUG) << "TEST";
|
||||
auto t = new Wt::WTemplate(Wt::WString::tr("Lms.Admin.Users.template"), this);
|
||||
t->addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||
|
||||
@@ -41,12 +40,25 @@ UsersView::UsersView(Wt::WContainerWidget *parent)
|
||||
auto addBtn = new Wt::WPushButton(Wt::WString::tr("Lms.Admin.Users.add"));
|
||||
t->bindWidget("add-btn", addBtn);
|
||||
|
||||
addBtn->clicked().connect(std::bind([=]
|
||||
{
|
||||
LmsApp->setInternalPath("/admin/user", true);
|
||||
}));
|
||||
|
||||
wApp->internalPathChanged().connect(std::bind([=]
|
||||
{
|
||||
refresh();
|
||||
}));
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void
|
||||
UsersView::refresh()
|
||||
{
|
||||
if (!wApp->internalPathMatches("/admin/users"))
|
||||
return;
|
||||
|
||||
_container->clear();
|
||||
|
||||
Wt::Dbo::Transaction transaction(DboSession());
|
||||
@@ -54,9 +66,10 @@ UsersView::refresh()
|
||||
auto users = Database::User::getAll(DboSession());
|
||||
for (auto user : users)
|
||||
{
|
||||
auto userId = std::to_string(user.id());
|
||||
auto entry = new Wt::WTemplate(Wt::WString::tr("Lms.Admin.Users.template.entry"), _container);
|
||||
|
||||
Wt::Auth::User authUser = DbHandler().getUserDatabase().findWithId(std::to_string(user.id()));
|
||||
Wt::Auth::User authUser = DbHandler().getUserDatabase().findWithId(userId);
|
||||
|
||||
if (!authUser.isValid()) {
|
||||
LMS_LOG(UI, ERROR) << "Skipping invalid userId = " << user.id();
|
||||
@@ -66,15 +79,29 @@ UsersView::refresh()
|
||||
entry->bindString("name", authUser.identity(Wt::Auth::Identity::LoginName), Wt::PlainText);
|
||||
|
||||
// Don't edit ourself this way
|
||||
if (CurrentUser() != user)
|
||||
{
|
||||
entry->setCondition("if-edit", true);
|
||||
auto editBtn = new Wt::WPushButton(Wt::WString::tr("Lms.Admin.Users.edit"));
|
||||
entry->bindWidget("edit-btn", editBtn);
|
||||
if (CurrentUser() == user)
|
||||
continue;
|
||||
|
||||
auto delBtn = new Wt::WPushButton(Wt::WString::tr("Lms.Admin.Users.del"));
|
||||
entry->bindWidget("del-btn", delBtn);
|
||||
}
|
||||
entry->setCondition("if-edit", true);
|
||||
auto editBtn = new Wt::WPushButton(Wt::WString::tr("Lms.Admin.Users.edit"));
|
||||
entry->bindWidget("edit-btn", editBtn);
|
||||
editBtn->clicked().connect(std::bind([=]
|
||||
{
|
||||
LmsApp->setInternalPath("/admin/user/" + std::to_string(user.id()), true);
|
||||
}));
|
||||
|
||||
auto delBtn = new Wt::WPushButton(Wt::WString::tr("Lms.Admin.Users.del"));
|
||||
entry->bindWidget("del-btn", delBtn);
|
||||
delBtn->clicked().connect(std::bind([=]
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(DboSession());
|
||||
|
||||
auto authUser = DbHandler().getUserDatabase().findWithId(userId);
|
||||
auto user = DbHandler().getUser(authUser);
|
||||
DbHandler().getUserDatabase().deleteUser( authUser );
|
||||
user.remove();
|
||||
_container->removeWidget(entry);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,19 @@
|
||||
#include "Utils.hpp"
|
||||
|
||||
|
||||
boost::optional<long>
|
||||
readLong(const std::string& str)
|
||||
{
|
||||
try
|
||||
{
|
||||
return std::stol(str);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
|
||||
bool readAsPosixTime(const std::string& str, boost::posix_time::ptime& time)
|
||||
{
|
||||
const std::locale formats[] = {
|
||||
|
||||
@@ -24,8 +24,13 @@
|
||||
#include <list>
|
||||
|
||||
#include <boost/locale.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
|
||||
|
||||
boost::optional<long>
|
||||
readLong(const std::string& str);
|
||||
|
||||
bool
|
||||
readAsPosixTime(const std::string& str, boost::posix_time::ptime& time);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user