WIP. Added Account and Audio forms

This commit is contained in:
emeric
2014-08-01 00:05:44 +02:00
parent 1f41f7f481
commit 1cdb69e4eb
18 changed files with 830 additions and 160 deletions
+3
View File
@@ -66,7 +66,10 @@ lms_SOURCES = \
$(top_srcdir)/ui/video/VideoMediaPlayerWidget.cpp \
$(top_srcdir)/ui/video/VideoParametersDialog.cpp \
$(top_srcdir)/ui/settings/Settings.cpp \
$(top_srcdir)/ui/settings/SettingsAccountFormView.cpp \
$(top_srcdir)/ui/settings/SettingsAudioFormView.cpp \
$(top_srcdir)/ui/settings/SettingsDatabaseFormView.cpp \
$(top_srcdir)/ui/settings/SettingsUserFormView.cpp \
$(top_srcdir)/ui/settings/SettingsUsers.cpp
+8 -1
View File
@@ -32,7 +32,14 @@ Handler::configureAuth(void)
verifier->addHashFunction(new Wt::Auth::BCryptHashFunction(8));
passwordService.setVerifier(verifier);
passwordService.setAttemptThrottlingEnabled(true);
passwordService.setStrengthValidator(new Wt::Auth::PasswordStrengthValidator());
Wt::Auth::PasswordStrengthValidator* strengthValidator = new Wt::Auth::PasswordStrengthValidator();
// Reduce some constraints...
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::TwoCharClass, 11);
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::ThreeCharClass, 8 );
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::FourCharClass, 6 );
passwordService.setStrengthValidator(strengthValidator);
}
const Wt::Auth::AuthService&
+94
View File
@@ -4,6 +4,33 @@
namespace Database {
// must be ordered
const std::vector<std::size_t>
User::audioBitrates =
{
64000,
96000,
128000,
160000,
192000,
224000,
256000,
320000,
512000
};
const std::vector<std::size_t>
User::videoBitrates =
{
256000,
512000,
1024000,
2048000,
4096000,
8192000
};
User::User()
: _maxAudioBitrate(maxAudioBitrate),
_maxVideoBitrate(maxVideoBitrate),
@@ -27,6 +54,73 @@ User::getById(Wt::Dbo::Session& session, std::string id)
return session.find<User>().where("id = ?").bind( id );
}
std::string
User::getId( pointer user)
{
std::ostringstream oss; oss << user.id();
return oss.str();
}
void
User::setAudioBitrate(std::size_t bitrate)
{
_audioBitrate = std::min(bitrate, std::min(static_cast<std::size_t>(_maxAudioBitrate), audioBitrates.back()));
}
void
User::setVideoBitrate(std::size_t bitrate)
{
_videoBitrate = std::min(bitrate, std::min(static_cast<std::size_t>(_maxVideoBitrate), videoBitrates.back()));
}
void
User::setMaxAudioBitrate(std::size_t bitrate)
{
_maxAudioBitrate = std::min(bitrate, static_cast<std::size_t>(_maxAudioBitrate));
}
void
User::setMaxVideoBitrate(std::size_t bitrate)
{
_maxVideoBitrate = std::min(bitrate, static_cast<std::size_t>(_maxVideoBitrate));
}
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());
}
std::size_t
User::getVideoBitrate(void) const
{
if (!isAdmin())
return std::min(static_cast<std::size_t>(_videoBitrate), std::min(static_cast<std::size_t>(_maxVideoBitrate), videoBitrates.back()));
else
return std::min(static_cast<std::size_t>(_videoBitrate), videoBitrates.back());
}
std::size_t
User::getMaxAudioBitrate(void) const
{
if (!isAdmin())
return std::min(static_cast<std::size_t>(_maxAudioBitrate), audioBitrates.back());
else
return audioBitrates.back();
}
std::size_t
User::getMaxVideoBitrate(void) const
{
if (!isAdmin())
return std::min(static_cast<std::size_t>(_maxVideoBitrate), videoBitrates.back());
else
return videoBitrates.back();
}
} // namespace Database
+15 -6
View File
@@ -14,6 +14,10 @@ class User {
public:
static const std::size_t MaxNameLength = 15;
// list of commonly used bitrates
static const std::vector<std::size_t> audioBitrates;
static const std::vector<std::size_t> videoBitrates;
User();
typedef Wt::Dbo::ptr<User> pointer;
@@ -21,16 +25,21 @@ class User {
// accessors
static pointer getById(Wt::Dbo::Session& session, std::string id);
static std::vector<pointer> getAll(Wt::Dbo::Session& session);
static std::string getId(pointer user);
// write
void setAdmin(bool admin) { _isAdmin = admin; }
void setMaxAudioBitrate(std::size_t bitrate) { _maxAudioBitrate = bitrate; }
void setMaxVideoBitrate(std::size_t bitrate) { _maxVideoBitrate = bitrate; }
void setAudioBitrate(std::size_t bitrate);
void setVideoBitrate(std::size_t bitrate);
void setMaxAudioBitrate(std::size_t bitrate);
void setMaxVideoBitrate(std::size_t bitrate);
// read
bool isAdmin() const {return _isAdmin;}
std::size_t getMaxAudioBitrate() const { return _maxAudioBitrate; }
std::size_t getMaxVideoBitrate() const { return _maxVideoBitrate; }
std::size_t getAudioBitrate() const;
std::size_t getVideoBitrate() const;
std::size_t getMaxAudioBitrate() const;
std::size_t getMaxVideoBitrate() const;
template<class Action>
void persist(Action& a)
@@ -45,10 +54,10 @@ class User {
private:
static const std::size_t maxAudioBitrate = 320000;
static const std::size_t maxVideoBitrate = 7500000;
static const std::size_t maxVideoBitrate = 2048000;
static const std::size_t defaultAudioBitrate = 128000;
static const std::size_t defaultVideoBitrate = 1500000;
static const std::size_t defaultVideoBitrate = 1024000;
// Admin defined settings
int _maxAudioBitrate;
+85 -2
View File
@@ -50,7 +50,7 @@
</div>
</div>
</div>
<legend>Access</legend>
<legend>${access}</legend>
<div class="form-horizontal">
<div class="form-group">
<label class="control-label col-sm-2" for="${id:admin}">
@@ -91,6 +91,8 @@
${video-bitrate-limit-info}
</div>
</div>
</div>
<div class="form-horizontal">
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
${save-button} ${cancel-button}
@@ -100,6 +102,88 @@
</message>
<message id="userAccountForm-template">
<legend>${title}</legend>
<div class="form-horizontal">
<div class="form-group">
<label class="control-label col-sm-2" for="${id:name}">
Name
</label>
<div class="col-sm-5">
${name}
</div>
<div class="help-block col-sm-5">
${name-info}
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="${id:email}">
e-Mail
</label>
<div class="col-sm-5">
${email}
</div>
<div class="help-block col-sm-5">
${email-info}
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="${id:password}">
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:password-confirm}">
Confirm password
</label>
<div class="col-sm-5">
${password-confirm}
</div>
<div class="help-block col-sm-5">
${password-confirm-info}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
${save-button} ${cancel-button}
</div>
</div>
${apply-info}
</div>
</message>
<message id="audioForm-template">
<legend>${title}</legend>
<div class="form-horizontal">
<div class="form-group">
<label class="control-label col-sm-2" for="${id:bitrate}">
Audio bitrate
</label>
<div class="col-sm-5">
<div class="input-group">
${bitrate}
<span class="input-group-addon">kbps</span>
</div>
</div>
<div class="help-block col-sm-5">
${bitrate-info}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
${save-button} ${cancel-button}
</div>
</div>
${apply-info}
</div>
</message>
<message id="databaseForm-template">
<legend>${title}</legend>
<div class="form-horizontal">
@@ -150,7 +234,6 @@
${update-request-immediate-scan-info}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
${apply-button} ${discard-button}
+14 -3
View File
@@ -6,7 +6,7 @@ namespace UserInterface {
AudioWidget::AudioWidget(SessionData& sessionData, Wt::WContainerWidget* parent)
: Wt::WContainerWidget(parent),
_sessionData(sessionData),
_db(sessionData.getDatabaseHandler()),
_audioDbWidget(nullptr),
_mediaPlayer(nullptr),
_imgResource(nullptr),
@@ -39,11 +39,22 @@ AudioWidget::playTrack(boost::filesystem::path p)
{
std::cout << "play track '" << p << "'" << std::endl;
try {
// TODO get user's encoding preference
std::size_t bitrate = 0;
// Get user preferences
{
Wt::Dbo::Transaction transaction(_db.getSession());
Database::User::pointer user = _db.getCurrentUser();
if (user)
bitrate = user->getAudioBitrate();
else
return; // TODO logout?
}
Transcode::InputMediaFile inputFile(p);
Transcode::Parameters parameters(inputFile, Transcode::Format::get(Transcode::Format::OGA), 128000);
Transcode::Parameters parameters(inputFile, Transcode::Format::get(Transcode::Format::OGA), bitrate);
_mediaPlayer->load( parameters );
+1 -1
View File
@@ -28,7 +28,7 @@ class AudioWidget : public Wt::WContainerWidget
void handleTrackEnded(void);
SessionData& _sessionData;
Database::Handler& _db;
AudioDatabaseWidget* _audioDbWidget;
-10
View File
@@ -1,10 +0,0 @@
#include <Wt/WRegExpValidator>
namespace UserInterface {
static inline Wt::WValidator *createEmailValidator()
{
return new Wt::WRegExpValidator("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}");
}
} // namespace UserInterface
+23
View File
@@ -0,0 +1,23 @@
#include <Wt/WRegExpValidator>
#include <Wt/WLengthValidator>
#include "database/User.hpp"
namespace UserInterface {
static inline Wt::WValidator *createEmailValidator()
{
Wt::WValidator *res = new Wt::WRegExpValidator("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}");
res->setMandatory(true);
return res;
}
static inline Wt::WValidator *createNameValidator() {
Wt::WLengthValidator *v = new Wt::WLengthValidator();
v->setMandatory(true);
v->setMinimumLength(3);
v->setMaximumLength(::Database::User::MaxNameLength);
return v;
}
} // namespace UserInterface
+8 -5
View File
@@ -2,6 +2,9 @@
#include <Wt/WStackedWidget>
#include <Wt/WTextArea>
#include "SettingsAudioFormView.hpp"
#include "SettingsUserFormView.hpp"
#include "SettingsAccountFormView.hpp"
#include "SettingsDatabaseFormView.hpp"
#include "SettingsUsers.hpp"
@@ -28,16 +31,16 @@ _sessionData(sessionData)
// Must be logged in here
assert(user);
menu->addItem("Audio", new AudioFormView(sessionData, Database::User::getId(user)));
if (user->isAdmin())
{
// TODO Special admin settings
menu->addItem("Database", new DatabaseFormView(sessionData));
menu->addItem("Users", new Users(sessionData));
}
// User specifics settings
menu->addItem("Transcoding", new Wt::WTextArea("User's transcoding settings here!"));
menu->addItem("Personal", new Wt::WTextArea("User's password + mail here!"));
else
{
menu->addItem("Account", new AccountFormView(sessionData, Database::User::getId(user)));
}
addWidget(contents);
}
+266
View File
@@ -0,0 +1,266 @@
#include <vector>
#include <Wt/WStringListModel>
#include <Wt/WFormModel>
#include <Wt/WLineEdit>
#include <Wt/WCheckBox>
#include <Wt/WComboBox>
#include <Wt/WPushButton>
#include <Wt/Auth/Identity>
#include "common/Validators.hpp"
#include "SettingsAccountFormView.hpp"
namespace UserInterface {
namespace Settings {
class AccountFormModel : public Wt::WFormModel
{
public:
// Associate each field with a unique string literal.
static const Field NameField;
static const Field EmailField;
static const Field PasswordField;
static const Field PasswordConfirmField;
AccountFormModel(SessionData& sessionData, std::string userId, Wt::WObject *parent = 0)
: Wt::WFormModel(parent),
_db(sessionData.getDatabaseHandler()),
_userId(userId)
{
addField(NameField);
addField(EmailField);
addField(PasswordField);
addField(PasswordConfirmField);
setValidator(NameField, createNameValidator());
setValidator(EmailField, createEmailValidator());
// populate the model with initial data
loadData();
}
void loadData()
{
Wt::Dbo::Transaction transaction(_db.getSession());
Wt::Auth::User authUser = _db.getUserDatabase().findWithId( _userId );
Database::User::pointer user = _db.getUser(authUser);
if (user && authUser.isValid())
{
setValue(NameField, authUser.identity(Wt::Auth::Identity::LoginName));
if (!authUser.email().empty())
setValue(EmailField, authUser.email());
else
setValue(EmailField, authUser.unverifiedEmail());
}
setValue(PasswordField, Wt::WString());
setValue(PasswordConfirmField, Wt::WString());
}
bool saveData(Wt::WString& error)
{
// DBO transaction active here
try {
Wt::Dbo::Transaction transaction(_db.getSession());
// Update user
Wt::Auth::User authUser = _db.getUserDatabase().findWithId(_userId);
Database::User::pointer user = _db.getUser( authUser );
// user may have been deleted by someone else
if (!authUser.isValid()) {
error = Wt::WString("User identity does not exist");
return false;
}
else if(!user)
{
error = Wt::WString("User not found");
return false;
}
// Account
authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(NameField));
authUser.setEmail(valueText(EmailField).toUTF8());
// Password
if (!valueText(PasswordField).empty())
Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField));
setValue(PasswordField, Wt::WString());
setValue(PasswordConfirmField, Wt::WString());
}
catch(Wt::Dbo::Exception& exception)
{
std::cerr << "Dbo exception: " << exception.what() << std::endl;
return false;
}
return true;
}
bool validateField(Field field)
{
// DBO transaction active here
Wt::WString error;
if (field == NameField)
{
Wt::Dbo::Transaction transaction(_db.getSession());
// Must be unique since used as LoginIdentity
Wt::Auth::User user = _db.getUserDatabase().findWithIdentity(Wt::Auth::Identity::LoginName, valueText(field));
if (user.isValid() && user.id() != _userId)
error = "Already exists";
else
return Wt::WFormModel::validateField(field);
}
else if (field == PasswordField)
{
// Password is mandatory if we create the user
if (!valueText(PasswordField).empty())
{
// Evaluate the strength of the password
Wt::Auth::AbstractPasswordService::StrengthValidatorResult res
= Database::Handler::getPasswordService().strengthValidator()->evaluateStrength(valueText(PasswordField),
valueText(NameField),
valueText(EmailField).toUTF8());
if (!res.isValid())
error = res.message();
}
else
return Wt::WFormModel::validateField(field);
}
else if (field == PasswordConfirmField)
{
if (validation(PasswordField).state() == Wt::WValidator::Valid)
{
if (valueText(PasswordField) != valueText(PasswordConfirmField))
error = Wt::WString::tr("Wt.Auth.passwords-dont-match");
}
}
else
{
// Apply validators
return Wt::WFormModel::validateField(field);
}
setValidation(field, Wt::WValidator::Result( error.empty() ? Wt::WValidator::Valid : Wt::WValidator::Invalid, error));
return validation(field).state() == Wt::WValidator::Valid;
}
private:
Database::Handler& _db;
std::string _userId;
};
const Wt::WFormModel::Field AccountFormModel::NameField = "name";
const Wt::WFormModel::Field AccountFormModel::EmailField = "email";
const Wt::WFormModel::Field AccountFormModel::PasswordField = "password";
const Wt::WFormModel::Field AccountFormModel::PasswordConfirmField = "password-confirm";
AccountFormView::AccountFormView(SessionData& sessionData, std::string userId, Wt::WContainerWidget *parent)
: Wt::WTemplateFormView(parent)
{
_model = new AccountFormModel(sessionData, userId, this);
setTemplateText(tr("userAccountForm-template"));
addFunction("id", &WTemplate::Functions::id);
addFunction("block", &WTemplate::Functions::id);
_applyInfo = new Wt::WText();
_applyInfo->setInline(false);
_applyInfo->hide();
bindWidget("apply-info", _applyInfo);
// Name
Wt::WLineEdit* accountEdit = new Wt::WLineEdit();
setFormWidget(AccountFormModel::NameField, accountEdit);
accountEdit->changed().connect(_applyInfo, &Wt::WWidget::hide);
// Email
Wt::WLineEdit* emailEdit = new Wt::WLineEdit();
setFormWidget(AccountFormModel::EmailField, emailEdit);
emailEdit->changed().connect(_applyInfo, &Wt::WWidget::hide);
// Password
Wt::WLineEdit* passwordEdit = new Wt::WLineEdit();
setFormWidget(AccountFormModel::PasswordField, passwordEdit );
passwordEdit->setEchoMode(Wt::WLineEdit::Password);
passwordEdit->changed().connect(_applyInfo, &Wt::WWidget::hide);
// Password confirmation
Wt::WLineEdit* passwordConfirmEdit = new Wt::WLineEdit();
setFormWidget(AccountFormModel::PasswordConfirmField, passwordConfirmEdit);
passwordConfirmEdit->setEchoMode(Wt::WLineEdit::Password);
passwordConfirmEdit->changed().connect(_applyInfo, &Wt::WWidget::hide);
// Title & Buttons
bindString("title", "Account settings");
Wt::WPushButton *saveButton = new Wt::WPushButton("Apply");
saveButton->setStyleClass("btn-primary");
bindWidget("save-button", saveButton);
saveButton->clicked().connect(this, &AccountFormView::processSave);
Wt::WPushButton *cancelButton = new Wt::WPushButton("Discard");
bindWidget("cancel-button", cancelButton);
cancelButton->clicked().connect(this, &AccountFormView::processCancel);
updateView(_model);
}
void
AccountFormView::processCancel()
{
_applyInfo->show();
_applyInfo->setText( Wt::WString::fromUTF8("Parameters reverted!"));
_applyInfo->setStyleClass("alert alert-info");
_model->loadData();
_model->validate();
updateView(_model);
}
void
AccountFormView::processSave()
{
updateModel(_model);
_applyInfo->show();
if (_model->validate())
{
Wt::WString error;
// commit model into DB
if (_model->saveData(error) )
{
_applyInfo->setText( Wt::WString::fromUTF8("New parameters successfully applied!"));
_applyInfo->setStyleClass("alert alert-success");
}
else
{
_applyInfo->setText( Wt::WString::fromUTF8("Cannot apply new parameters: ") + error);
_applyInfo->setStyleClass("alert alert-danger");
}
}
else
{
_applyInfo->setText( Wt::WString::fromUTF8("Cannot apply new parameters!"));
_applyInfo->setStyleClass("alert alert-danger");
}
updateView(_model);
}
} // namespace Settings
} // namespace UserInterface
+32
View File
@@ -0,0 +1,32 @@
#ifndef UI_SETTINGS_ACCOUNT_FORM_VIEW_HPP
#define UI_SETTINGS_ACCOUNT_FORM_VIEW_HPP
#include <Wt/WContainerWidget>
#include <Wt/WTemplateFormView>
#include <Wt/WText>
#include "common/SessionData.hpp"
namespace UserInterface {
namespace Settings {
class AccountFormModel;
class AccountFormView : public Wt::WTemplateFormView
{
public:
AccountFormView(SessionData& sessionData, std::string userId, Wt::WContainerWidget *parent = 0);
private:
void processCancel(); // reload from DB
void processSave(); // commit into DB
AccountFormModel* _model;
Wt::WText* _applyInfo;
};
} // namespace Settings
} // namespace UserInterface
#endif
+178
View File
@@ -0,0 +1,178 @@
#include <vector>
#include <Wt/WStringListModel>
#include <Wt/WFormModel>
#include <Wt/WComboBox>
#include <Wt/WPushButton>
#include "common/Validators.hpp"
#include "SettingsAudioFormView.hpp"
namespace UserInterface {
namespace Settings {
class AudioFormModel : public Wt::WFormModel
{
public:
// Associate each field with a unique string literal.
static const Field BitrateField;
AudioFormModel(SessionData& sessionData, std::string userId, Wt::WObject *parent = 0)
: Wt::WFormModel(parent),
_db(sessionData.getDatabaseHandler()),
_userId(userId)
{
initializeModels();
addField(BitrateField);
setValidator(BitrateField, new Wt::WValidator(true)); // mandatory
// populate the model with initial data
loadData();
}
Wt::WAbstractItemModel *bitrateModel() { return _bitrateModel; }
void loadData()
{
Wt::Dbo::Transaction transaction(_db.getSession());
Database::User::pointer user = Database::User::getById( _db.getSession(), _userId);
if (user)
setValue(BitrateField, std::min(user->getMaxAudioBitrate(), user->getAudioBitrate()) / 1000); // in kps
else
setValue(BitrateField, Wt::WString());
}
bool saveData(Wt::WString& error)
{
// DBO transaction active here
try {
Wt::Dbo::Transaction transaction(_db.getSession());
Database::User::pointer user = Database::User::getById( _db.getSession(), _userId);
// user may have been deleted by someone else
if (user)
{
user.modify()->setAudioBitrate( Wt::asNumber(value(BitrateField)) * 1000); // in kbps
}
}
catch(Wt::Dbo::Exception& exception)
{
std::cerr << "Dbo exception: " << exception.what() << std::endl;
return false;
}
return true;
}
private:
void initializeModels()
{
Wt::Dbo::Transaction transaction(_db.getSession());
Database::User::pointer user = Database::User::getById(_db.getSession(), _userId);
_bitrateModel = new Wt::WStringListModel();
BOOST_FOREACH(std::size_t bitrate, Database::User::audioBitrates)
{
if (user && bitrate <= user->getMaxAudioBitrate())
_bitrateModel->addString( Wt::WString("{1}").arg( bitrate / 1000 ) ); // in kbps
}
}
Database::Handler& _db;
std::string _userId;
Wt::WStringListModel* _bitrateModel;
};
const Wt::WFormModel::Field AudioFormModel::BitrateField = "bitrate";
AudioFormView::AudioFormView(SessionData& sessionData, std::string userId, Wt::WContainerWidget *parent)
: Wt::WTemplateFormView(parent)
{
_model = new AudioFormModel(sessionData, userId, this);
setTemplateText(tr("audioForm-template"));
addFunction("id", &WTemplate::Functions::id);
addFunction("block", &WTemplate::Functions::id);
_applyInfo = new Wt::WText();
_applyInfo->setInline(false);
_applyInfo->hide();
bindWidget("apply-info", _applyInfo);
// Bitrate
Wt::WComboBox *bitrateCB = new Wt::WComboBox();
setFormWidget(AudioFormModel::BitrateField, bitrateCB);
bitrateCB->setStyleClass("span2");
bitrateCB->setModel(_model->bitrateModel());
bitrateCB->changed().connect(_applyInfo, &Wt::WWidget::hide);
// Title & Buttons
bindString("title", "Audio settings");
Wt::WPushButton *saveButton = new Wt::WPushButton("Apply");
saveButton->setStyleClass("btn-primary");
bindWidget("save-button", saveButton);
saveButton->clicked().connect(this, &AudioFormView::processSave);
Wt::WPushButton *cancelButton = new Wt::WPushButton("Discard");
bindWidget("cancel-button", cancelButton);
cancelButton->clicked().connect(this, &AudioFormView::processCancel);
updateView(_model);
}
void
AudioFormView::processCancel()
{
_applyInfo->show();
_applyInfo->setText( Wt::WString::fromUTF8("Parameters reverted!"));
_applyInfo->setStyleClass("alert alert-info");
_model->loadData();
_model->validate();
updateView(_model);
}
void
AudioFormView::processSave()
{
updateModel(_model);
_applyInfo->show();
if (_model->validate())
{
Wt::WString error;
// commit model into DB
if (_model->saveData(error) )
{
_applyInfo->setText( Wt::WString::fromUTF8("New parameters successfully applied!"));
_applyInfo->setStyleClass("alert alert-success");
}
else
{
_applyInfo->setText( Wt::WString::fromUTF8("Cannot apply new parameters: ") + error);
_applyInfo->setStyleClass("alert alert-danger");
}
}
else
{
_applyInfo->setText( Wt::WString::fromUTF8("Cannot apply new parameters!"));
_applyInfo->setStyleClass("alert alert-danger");
}
updateView(_model);
}
} // namespace Settings
} // namespace UserInterface
+32
View File
@@ -0,0 +1,32 @@
#ifndef UI_SETTINGS_AUDIO_FORM_VIEW_HPP
#define UI_SETTINGS_AUDIO_ACCOUNT_FORM_VIEW_HPP
#include <Wt/WContainerWidget>
#include <Wt/WTemplateFormView>
#include <Wt/WText>
#include "common/SessionData.hpp"
namespace UserInterface {
namespace Settings {
class AudioFormModel;
class AudioFormView : public Wt::WTemplateFormView
{
public:
AudioFormView(SessionData& sessionData, std::string userId, Wt::WContainerWidget *parent = 0);
private:
void processCancel(); // reload from DB
void processSave(); // commit into DB
AudioFormModel* _model;
Wt::WText* _applyInfo;
};
} // namespace Settings
} // namespace UserInterface
#endif
+18 -15
View File
@@ -250,25 +250,35 @@ DatabaseFormView::DatabaseFormView(SessionData& sessionData, Wt::WContainerWidge
addFunction("id", &WTemplate::Functions::id);
addFunction("block", &WTemplate::Functions::id);
applyInfo = new Wt::WText();
applyInfo->setInline(false);
applyInfo->hide();
bindWidget("apply-info", applyInfo);
// Path
setFormWidget(DatabaseFormModel::PathField, new Wt::WLineEdit());
Wt::WLineEdit *pathEdit = new Wt::WLineEdit();
setFormWidget(DatabaseFormModel::PathField, pathEdit);
pathEdit->changed().connect(applyInfo, &Wt::WWidget::hide);
// Update Period
Wt::WComboBox *updatePeriodCB = new Wt::WComboBox();
setFormWidget(DatabaseFormModel::UpdatePeriodField, updatePeriodCB);
updatePeriodCB->setModel(model->updatePeriodModel());
updatePeriodCB->changed().connect(applyInfo, &Wt::WWidget::hide);
// Update Start Time
Wt::WComboBox *updateStartTimeCB = new Wt::WComboBox();
setFormWidget(DatabaseFormModel::UpdateStartTimeField, updateStartTimeCB);
updateStartTimeCB->setModel(model->updateStartTimeModel());
updateStartTimeCB->changed().connect(applyInfo, &Wt::WWidget::hide);
// Request Immediate scan
setFormWidget(DatabaseFormModel::UpdateRequestImmediateField, new Wt::WCheckBox());
Wt::WCheckBox *immScan = new Wt::WCheckBox();
setFormWidget(DatabaseFormModel::UpdateRequestImmediateField, immScan);
immScan->changed().connect(applyInfo, &Wt::WWidget::hide);
// Title & Buttons
Wt::WString title = Wt::WString("Media directories settings");
bindString("title", title);
bindString("title", "Media directories settings");
Wt::WPushButton *saveButton = new Wt::WPushButton("Apply");
bindWidget("apply-button", saveButton);
@@ -277,11 +287,6 @@ DatabaseFormView::DatabaseFormView(SessionData& sessionData, Wt::WContainerWidge
Wt::WPushButton *discardButton = new Wt::WPushButton("Discard");
bindWidget("discard-button", discardButton);
applyInfo = new Wt::WText();
applyInfo->setInline(false);
applyInfo->hide();
bindWidget("apply-info", applyInfo);
saveButton->clicked().connect(this, &DatabaseFormView::processSave);
discardButton->clicked().connect(this, &DatabaseFormView::processDiscard);
@@ -325,15 +330,13 @@ DatabaseFormView::processSave()
applyInfo->setText( Wt::WString::fromUTF8("New parameters successfully applied!"));
applyInfo->setStyleClass("alert alert-success");
// Udate the view: Delete any validation message in the view, etc.
updateView(model);
} else {
}
else {
applyInfo->setText( Wt::WString::fromUTF8("Cannot apply new parameters!"));
applyInfo->setStyleClass("alert alert-danger");
updateView(model);
}
// Udate the view: Delete any validation message in the view, etc.
updateView(model);
}
+51 -110
View File
@@ -2,20 +2,17 @@
#include <Wt/WStringListModel>
#include <Wt/WFormModel>
#include <Wt/WLengthValidator>
#include <Wt/WLineEdit>
#include <Wt/WCheckBox>
#include <Wt/WComboBox>
#include <Wt/WPushButton>
#include <Wt/Auth/Identity>
#include <Wt/Auth/PasswordStrengthValidator>
#include <Wt/Auth/AbstractPasswordService>
#include <Wt/Auth/PasswordVerifier>
#include "common/EmailValidator.hpp"
#include "common/Validators.hpp"
#include "SettingsUserFormView.hpp"
namespace UserInterface {
namespace Settings {
@@ -34,7 +31,7 @@ class UserFormModel : public Wt::WFormModel
UserFormModel(SessionData& sessionData, std::string userId, Wt::WObject *parent = 0)
: Wt::WFormModel(parent),
_sessionData(sessionData),
_db(sessionData.getDatabaseHandler()),
_userId(userId)
{
initializeModels();
@@ -48,9 +45,7 @@ class UserFormModel : public Wt::WFormModel
addField(VideoBitrateLimitField);
setValidator(NameField, createNameValidator());
Wt::WValidator *emailValidator = createEmailValidator();
emailValidator->setMandatory(true);
setValidator(EmailField, emailValidator);
setValidator(EmailField, createEmailValidator());
// If creating a user, passwords are mandatory
if (_userId.empty())
{
@@ -72,67 +67,60 @@ class UserFormModel : public Wt::WFormModel
if (!userId.empty())
{
Database::Handler& db = _sessionData.getDatabaseHandler();
Wt::Dbo::Transaction transaction(_db.getSession());
Wt::Dbo::Transaction transaction(db.getSession());
Wt::Auth::User authUser = db.getUserDatabase().findWithId( userId );
Database::User::pointer user = db.getUser(authUser);
Wt::Auth::User currentUser = _sessionData.getDatabaseHandler().getLogin().user();
if (user && user->isAdmin())
{
setValue(AdminField, true);
// We can cannot remove admin rights to ourselves
if (currentUser == authUser)
setReadOnly(AdminField, true);
// if the user is admin, no need to limit it
setReadOnly(AudioBitrateLimitField, true);
setValidator(AudioBitrateLimitField, nullptr);
setReadOnly(VideoBitrateLimitField, true);
setValidator(VideoBitrateLimitField, nullptr);
}
Wt::Auth::User authUser = _db.getUserDatabase().findWithId( userId );
Database::User::pointer user = _db.getUser(authUser);
Wt::Auth::User currentUser = _db.getLogin().user();
if (user && authUser.isValid())
{
if (user->isAdmin())
{
setValue(AdminField, true);
// We can cannot remove admin rights to ourselves
if (currentUser == authUser)
setReadOnly(AdminField, true);
// if the user is admin, no need to limit it
setReadOnly(AudioBitrateLimitField, true);
setValidator(AudioBitrateLimitField, nullptr);
setReadOnly(VideoBitrateLimitField, true);
setValidator(VideoBitrateLimitField, nullptr);
}
else
{
setValue(AudioBitrateLimitField, user->getMaxAudioBitrate() / 1000); // in kbps
setValue(VideoBitrateLimitField, user->getMaxVideoBitrate() / 1000); // in kbps
}
setValue(NameField, authUser.identity(Wt::Auth::Identity::LoginName));
if (!authUser.email().empty())
setValue(EmailField, authUser.email());
else
setValue(EmailField, authUser.unverifiedEmail());
}
if (user)
{
setValue(AudioBitrateLimitField, user->getMaxAudioBitrate() / 1000); // in kbps
setValue(VideoBitrateLimitField, user->getMaxVideoBitrate() / 1000); // in kbps
}
}
}
bool saveData()
{
// DBO transaction active here
try {
Database::Handler& db = _sessionData.getDatabaseHandler();
Wt::Dbo::Transaction transaction(db.getSession());
Wt::Dbo::Transaction transaction(_db.getSession());
if (_userId.empty())
{
// Create user
Wt::Auth::User authUser = db.getUserDatabase().registerNew();
Database::User::pointer user = db.getUser(authUser);
Wt::Auth::User authUser = _db.getUserDatabase().registerNew();
Database::User::pointer user = _db.getUser(authUser);
// Account
authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(NameField));
authUser.setEmail(valueText(EmailField).toUTF8());
db.getPasswordService().updatePassword(authUser, valueText(PasswordField));
Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField));
// Access
{
@@ -153,10 +141,9 @@ class UserFormModel : public Wt::WFormModel
}
else
{
// Update user
Wt::Auth::User authUser = db.getUserDatabase().findWithId(_userId);
Database::User::pointer user = db.getUser( authUser );
Wt::Auth::User authUser = _db.getUserDatabase().findWithId(_userId);
Database::User::pointer user = _db.getUser( authUser );
// user may have been deleted by someone else
if (!authUser.isValid()) {
@@ -175,7 +162,7 @@ class UserFormModel : public Wt::WFormModel
// Password
if (!valueText(PasswordField).empty())
db.getPasswordService().updatePassword(authUser, valueText(PasswordField));
Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField));
// Access
if (!isReadOnly(AdminField))
@@ -207,15 +194,13 @@ class UserFormModel : public Wt::WFormModel
bool validateField(Field field)
{
// DBO transaction active here
Wt::WString error;
if (field == NameField)
{
Wt::Dbo::Transaction transaction(_sessionData.getDatabaseHandler().getSession());
Wt::Dbo::Transaction transaction(_db.getSession());
// Must be unique since used as LoginIdentity
Wt::Auth::User user = _sessionData.getDatabaseHandler().getUserDatabase().findWithIdentity(Wt::Auth::Identity::LoginName, valueText(field));
Wt::Auth::User user = _db.getUserDatabase().findWithIdentity(Wt::Auth::Identity::LoginName, valueText(field));
if (user.isValid() && user.id() != _userId)
error = "Already exists";
else
@@ -227,17 +212,10 @@ class UserFormModel : public Wt::WFormModel
if (!valueText(PasswordField).empty())
{
// Evaluate the strength of the password
Wt::Auth::PasswordStrengthValidator validator;
// Reduce some constraints...
validator.setMinimumLength( Wt::Auth::PasswordStrengthValidator::TwoCharClass, 11);
validator.setMinimumLength( Wt::Auth::PasswordStrengthValidator::ThreeCharClass, 8 );
validator.setMinimumLength( Wt::Auth::PasswordStrengthValidator::FourCharClass, 6 );
Wt::Auth::AbstractPasswordService::StrengthValidatorResult res
= validator.evaluateStrength(valueText(PasswordField),
valueText(NameField),
valueText(EmailField).toUTF8());
= Database::Handler::getPasswordService().strengthValidator()->evaluateStrength(valueText(PasswordField),
valueText(NameField),
valueText(EmailField).toUTF8());
if (!res.isValid())
error = res.message();
@@ -267,58 +245,21 @@ class UserFormModel : public Wt::WFormModel
private:
static Wt::WValidator *createNameValidator() {
Wt::WLengthValidator *v = new Wt::WLengthValidator();
v->setMandatory(true);
v->setMinimumLength(3);
v->setMaximumLength(::Database::User::MaxNameLength);
return v;
}
void initializeModels()
{
// AUDIO
// TODO move defaults somewhere else?
static const std::vector<std::size_t>
audioBitrateLimits =
{
64,
96,
128,
160,
192,
224,
256,
320,
512
};
_audioBitrateModel = new Wt::WStringListModel();
for (std::size_t i = 0; i < audioBitrateLimits.size(); ++i)
_audioBitrateModel->addString( Wt::WString("{1}").arg( audioBitrateLimits[i] ) );
BOOST_FOREACH(std::size_t bitrate, Database::User::audioBitrates)
_audioBitrateModel->addString( Wt::WString("{1}").arg( bitrate / 1000 ) ); // in kbps
// VIDEO
// TODO move defaults somewhere else?
static const std::vector<std::size_t>
videoBitrateLimits =
{
256,
512,
1024,
2048,
4096,
8192
};
_videoBitrateModel = new Wt::WStringListModel();
for (std::size_t i = 0; i < videoBitrateLimits.size(); ++i)
_videoBitrateModel->addString( Wt::WString("{1}").arg( videoBitrateLimits[i] ) );
BOOST_FOREACH(std::size_t bitrate, Database::User::videoBitrates)
_videoBitrateModel->addString( Wt::WString("{1}").arg( bitrate / 1000 ) ); // in kbps
}
SessionData& _sessionData;
Database::Handler& _db;
std::string _userId;
Wt::WStringListModel* _audioBitrateModel;
Wt::WStringListModel* _videoBitrateModel;
@@ -334,8 +275,7 @@ const Wt::WFormModel::Field UserFormModel::VideoBitrateLimitField = "video-bitra
UserFormView::UserFormView(SessionData& sessionData, std::string userId, Wt::WContainerWidget *parent)
: Wt::WTemplateFormView(parent),
_sessionData(sessionData)
: Wt::WTemplateFormView(parent)
{
_model = new UserFormModel(sessionData, userId, this);
@@ -360,9 +300,10 @@ UserFormView::UserFormView(SessionData& sessionData, std::string userId, Wt::WCo
setFormWidget(UserFormModel::PasswordConfirmField, passwordConfirmEdit);
passwordConfirmEdit->setEchoMode(Wt::WLineEdit::Password);
bindString("access", "Access");
// Admin Field
Wt::WCheckBox *admin = new Wt::WCheckBox();
setFormWidget(UserFormModel::AdminField, admin);
setFormWidget(UserFormModel::AdminField, new Wt::WCheckBox());
// AudioBitrate
Wt::WComboBox *audioBitrateCB = new Wt::WComboBox();
@@ -418,7 +359,7 @@ UserFormView::UserFormView(SessionData& sessionData, std::string userId, Wt::WCo
}
void
void
UserFormView::processCancel()
{
// parent widget will delete this widget
+1 -2
View File
@@ -15,6 +15,7 @@ class UserFormModel;
class UserFormView : public Wt::WTemplateFormView
{
public:
UserFormView(SessionData& sessionData, std::string userId, Wt::WContainerWidget *parent = 0);
// Signal emitted once the form is completed
@@ -24,8 +25,6 @@ class UserFormView : public Wt::WTemplateFormView
Wt::Signal<bool> _sigCompleted;
SessionData& _sessionData;
void processSave();
void processCancel();
+1 -5
View File
@@ -67,11 +67,7 @@ Users::refresh(void)
for (std::size_t i = 0; i < users.size(); ++i)
{
std::string userId;
{
std::ostringstream oss; oss << users[i].id();
userId = oss.str();
}
std::string userId = Database::User::getId(users[i]);
Wt::Auth::User authUser;