#include #include #include #include #include #include #include #include #include "logger/Logger.hpp" #include "common/Validators.hpp" #include "SettingsUserFormView.hpp" namespace UserInterface { namespace Settings { class UserFormModel : 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; static const Field AdminField; static const Field AudioBitrateLimitField; static const Field VideoBitrateLimitField; UserFormModel(SessionData& sessionData, std::string userId, Wt::WObject *parent = 0) : Wt::WFormModel(parent), _db(sessionData.getDatabaseHandler()), _userId(userId) { initializeModels(); addField(NameField); addField(EmailField); addField(PasswordField); addField(PasswordConfirmField); addField(AdminField); addField(AudioBitrateLimitField); addField(VideoBitrateLimitField); setValidator(NameField, createNameValidator()); setValidator(EmailField, createEmailValidator()); // If creating a user, passwords are mandatory if (_userId.empty()) { setValidator(PasswordField, new Wt::WValidator(true)); // mandatory setValidator(PasswordConfirmField, new Wt::WValidator(true)); // mandatory } setValidator(AudioBitrateLimitField, new Wt::WValidator(true)); // mandatory setValidator(VideoBitrateLimitField, new Wt::WValidator(true)); // mandatory // populate the model with initial data loadData(userId); } Wt::WAbstractItemModel *audioBitrateModel() { return _audioBitrateModel; } Wt::WAbstractItemModel *videoBitrateModel() { return _videoBitrateModel; } void loadData(std::string userId) { if (!userId.empty()) { 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 = _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()); } } } bool saveData() { try { 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); // Account authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(NameField)); authUser.setEmail(valueText(EmailField).toUTF8()); Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField)); // Access { boost::any v = value(AdminField); if (!v.empty() && boost::any_cast(v) == true) user.modify()->setAdmin( true ); else user.modify()->setAdmin( false ); } if (!isReadOnly(AudioBitrateLimitField)) user.modify()->setMaxAudioBitrate( Wt::asNumber(value(AudioBitrateLimitField)) * 1000); // in bps if (!isReadOnly(VideoBitrateLimitField)) user.modify()->setMaxVideoBitrate( Wt::asNumber(value(VideoBitrateLimitField)) * 1000); // in bps transaction.commit(); } else { // 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()) { LMS_LOG(MOD_UI, SEV_ERROR) << "user identity does not exist!"; return false; } else if(!user) { LMS_LOG(MOD_UI, SEV_ERROR) << "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)); // Access if (!isReadOnly(AdminField)) { boost::any v = value(AdminField); if (!v.empty() && boost::any_cast(v) == true) user.modify()->setAdmin( true ); else user.modify()->setAdmin( false ); } if (!isReadOnly(AudioBitrateLimitField)) user.modify()->setMaxAudioBitrate( Wt::asNumber(value(AudioBitrateLimitField)) * 1000); // in bps if (!isReadOnly(VideoBitrateLimitField)) user.modify()->setMaxVideoBitrate( Wt::asNumber(value(VideoBitrateLimitField)) * 1000); // in bps transaction.commit(); } } catch(Wt::Dbo::Exception& exception) { LMS_LOG(MOD_UI, SEV_ERROR) << "Dbo exception: " << exception.what(); return false; } return true; } bool validateField(Field field) { 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: void initializeModels() { // AUDIO _audioBitrateModel = new Wt::WStringListModel(); BOOST_FOREACH(std::size_t bitrate, Database::User::audioBitrates) _audioBitrateModel->addString( Wt::WString("{1}").arg( bitrate / 1000 ) ); // in kbps // VIDEO _videoBitrateModel = new Wt::WStringListModel(); BOOST_FOREACH(std::size_t bitrate, Database::User::videoBitrates) _videoBitrateModel->addString( Wt::WString("{1}").arg( bitrate / 1000 ) ); // in kbps } Database::Handler& _db; std::string _userId; Wt::WStringListModel* _audioBitrateModel; Wt::WStringListModel* _videoBitrateModel; }; const Wt::WFormModel::Field UserFormModel::NameField = "name"; const Wt::WFormModel::Field UserFormModel::EmailField = "email"; const Wt::WFormModel::Field UserFormModel::PasswordField = "password"; const Wt::WFormModel::Field UserFormModel::PasswordConfirmField = "password-confirm"; const Wt::WFormModel::Field UserFormModel::AdminField = "admin"; const Wt::WFormModel::Field UserFormModel::AudioBitrateLimitField = "audio-bitrate-limit"; const Wt::WFormModel::Field UserFormModel::VideoBitrateLimitField = "video-bitrate-limit"; UserFormView::UserFormView(SessionData& sessionData, std::string userId, Wt::WContainerWidget *parent) : Wt::WTemplateFormView(parent) { _model = new UserFormModel(sessionData, userId, this); setTemplateText(tr("userForm-template")); addFunction("id", &WTemplate::Functions::id); addFunction("block", &WTemplate::Functions::id); // Name setFormWidget(UserFormModel::NameField, new Wt::WLineEdit()); // Email setFormWidget(UserFormModel::EmailField, new Wt::WLineEdit()); // Password Wt::WLineEdit* passwordEdit = new Wt::WLineEdit(); setFormWidget(UserFormModel::PasswordField, passwordEdit ); passwordEdit->setEchoMode(Wt::WLineEdit::Password); // Password confirmation Wt::WLineEdit* passwordConfirmEdit = new Wt::WLineEdit(); setFormWidget(UserFormModel::PasswordConfirmField, passwordConfirmEdit); passwordConfirmEdit->setEchoMode(Wt::WLineEdit::Password); bindString("access", "Access"); // Admin Field setFormWidget(UserFormModel::AdminField, new Wt::WCheckBox()); // AudioBitrate Wt::WComboBox *audioBitrateCB = new Wt::WComboBox(); setFormWidget(UserFormModel::AudioBitrateLimitField, audioBitrateCB); audioBitrateCB->setStyleClass("span2"); audioBitrateCB->setModel(_model->audioBitrateModel()); // VideoBitrate Wt::WComboBox *videoBitrateCB = new Wt::WComboBox(); setFormWidget(UserFormModel::VideoBitrateLimitField, videoBitrateCB); videoBitrateCB->setStyleClass("span2"); videoBitrateCB->setModel(_model->videoBitrateModel()); // Title & Buttons Wt::WString title; if (userId.empty()) { title = Wt::WString("Create user"); } else { Database::Handler &db = sessionData.getDatabaseHandler(); Wt::Dbo::Transaction transaction (db.getSession()); Wt::Auth::User authUser = db.getUserDatabase().findWithId( userId ); Wt::WString userName; if (authUser.isValid()) userName = authUser.identity(Wt::Auth::Identity::LoginName); else ; // TODO display user deleted and close the widget title = Wt::WString("Edit user {1}").arg(userName); } bindString("title", title); Wt::WPushButton *saveButton = new Wt::WPushButton(); if (userId.empty()) { saveButton->setText("Create user"); saveButton->setStyleClass("btn-success"); } else { saveButton->setText("Save"); saveButton->setStyleClass("btn-primary"); } bindWidget("save-button", saveButton); saveButton->clicked().connect(this, &UserFormView::processSave); Wt::WPushButton *cancelButton = new Wt::WPushButton("Cancel"); bindWidget("cancel-button", cancelButton); cancelButton->clicked().connect(this, &UserFormView::processCancel); updateView(_model); } void UserFormView::processCancel() { // parent widget will delete this widget completed().emit(false); } void UserFormView::processSave() { updateModel(_model); if (_model->validate()) { // commit model into DB if (_model->saveData() ) { // parent widget will delete this widget completed().emit(true); } // else TODO display a nice error message } else { updateView(_model); } } } // namespace Settings } // namespace UserInterface