diff --git a/Makefile.am b/Makefile.am index caa0718b..ddd7cc97 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,7 +24,8 @@ lms_approot_DATA = \ approot/playqueue.xml \ approot/releases.xml \ approot/release.xml \ - approot/settings.xml \ + approot/settings-database.xml \ + approot/settings-first-connection.xml \ approot/templates.xml \ approot/tracks.xml diff --git a/approot/messages.xml b/approot/messages.xml index 03322b49..ff60b3c9 100644 --- a/approot/messages.xml +++ b/approot/messages.xml @@ -21,6 +21,14 @@ Show more +Login +Password +Verify password +Create + +Administrator account +Account created. Please refresh to continue! + Settings Database Media root directory diff --git a/approot/settings.xml b/approot/settings-database.xml similarity index 100% rename from approot/settings.xml rename to approot/settings-database.xml diff --git a/approot/settings-first-connection.xml b/approot/settings-first-connection.xml new file mode 100644 index 00000000..c4fda6db --- /dev/null +++ b/approot/settings-first-connection.xml @@ -0,0 +1,57 @@ + + + + + + + + + ${tr:msg-settings-admin-account} + + + + + ${tr:msg-login} + + + ${admin-login} + + + ${admin-login-info} + + + + + + ${tr:msg-password} + + + ${password} + + + ${password-info} + + + + + + ${tr:msg-password-confirm} + + + ${password-confirm} + + + ${password-confirm-info} + + + + + + ${create-btn class="btn-primary"} + + + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index b958dfca..8aa3f025 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -33,13 +33,14 @@ lms_SOURCES = \ $(srcdir)/ui/ReleaseView.cpp \ $(srcdir)/ui/TracksView.cpp \ $(srcdir)/ui/auth/LmsAuth.cpp \ - $(srcdir)/ui/common/DirectoryValidator.cpp \ + $(srcdir)/ui/common/Validators.cpp \ $(srcdir)/ui/common/InputRange.cpp \ $(srcdir)/ui/common/LineEdit.cpp \ $(srcdir)/ui/resource/AvConvTranscodeStreamResource.cpp \ $(srcdir)/ui/resource/ImageResource.cpp \ $(srcdir)/ui/resource/TranscodeResource.cpp \ $(srcdir)/ui/settings/DatabaseView.cpp \ + $(srcdir)/ui/settings/FirstConnectionView.cpp \ $(srcdir)/utils/Config.cpp \ $(srcdir)/utils/Logger.cpp \ $(srcdir)/utils/Path.cpp \ diff --git a/src/database/DatabaseHandler.cpp b/src/database/DatabaseHandler.cpp index f47fa8a8..7334dbde 100644 --- a/src/database/DatabaseHandler.cpp +++ b/src/database/DatabaseHandler.cpp @@ -61,7 +61,7 @@ Handler::configureAuth(void) strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::OneCharClass, 4); strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::TwoCharClass, 4); strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::ThreeCharClass, 4 ); - strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::FourCharClass, 4 ); + strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::FourCharClass, 4 ); passwordService.setStrengthValidator(strengthValidator); } diff --git a/src/database/User.cpp b/src/database/User.cpp index 4024e2d4..bd1dfa2c 100644 --- a/src/database/User.cpp +++ b/src/database/User.cpp @@ -26,26 +26,9 @@ const std::vector User::audioBitrates = { 64000, - 96000, 128000, - 160000, 192000, - 224000, - 256000, 320000, - 512000 -}; - - -const std::vector -User::videoBitrates = -{ - 256000, - 512000, - 1024000, - 2048000, - 4096000, - 8192000 }; const std::vector @@ -57,20 +40,11 @@ User::audioEncodings = AudioEncoding::WEBMA, }; -const std::vector -User::videoEncodings = -{ - VideoEncoding::AUTO, -}; - User::User() : _maxAudioBitrate(maxAudioBitrate), - _maxVideoBitrate(maxVideoBitrate), _isAdmin(false), _audioBitrate(defaultAudioBitrate), _audioEncoding(AudioEncoding::AUTO), -_videoBitrate(defaultVideoBitrate), -_videoEncoding(VideoEncoding::AUTO), _curPlayingTrackPos(0) { @@ -108,24 +82,12 @@ User::setAudioBitrate(std::size_t bitrate) _audioBitrate = std::min(bitrate, std::min(static_cast(_maxAudioBitrate), audioBitrates.back())); } -void -User::setVideoBitrate(std::size_t bitrate) -{ - _videoBitrate = std::min(bitrate, std::min(static_cast(_maxVideoBitrate), videoBitrates.back())); -} - void User::setMaxAudioBitrate(std::size_t bitrate) { _maxAudioBitrate = std::min(bitrate, static_cast(_maxAudioBitrate)); } -void -User::setMaxVideoBitrate(std::size_t bitrate) -{ - _maxVideoBitrate = std::min(bitrate, static_cast(_maxVideoBitrate)); -} - std::size_t User::getAudioBitrate(void) const { @@ -135,15 +97,6 @@ User::getAudioBitrate(void) const return std::min(static_cast(_audioBitrate), audioBitrates.back()); } -std::size_t -User::getVideoBitrate(void) const -{ - if (!isAdmin()) - return std::min(static_cast(_videoBitrate), std::min(static_cast(_maxVideoBitrate), videoBitrates.back())); - else - return std::min(static_cast(_videoBitrate), videoBitrates.back()); -} - std::size_t User::getMaxAudioBitrate(void) const { @@ -153,15 +106,6 @@ User::getMaxAudioBitrate(void) const return audioBitrates.back(); } -std::size_t -User::getMaxVideoBitrate(void) const -{ - if (!isAdmin()) - return std::min(static_cast(_maxVideoBitrate), videoBitrates.back()); - else - return videoBitrates.back(); -} - } // namespace Database diff --git a/src/database/User.hpp b/src/database/User.hpp index e772f404..a422c369 100644 --- a/src/database/User.hpp +++ b/src/database/User.hpp @@ -41,29 +41,21 @@ enum class AudioEncoding WEBMA, }; -enum class VideoEncoding -{ - AUTO, -}; - class User { - public: + typedef Wt::Dbo::dbo_traits::IdType id_type; + typedef Wt::Dbo::ptr pointer; + static const std::size_t MinNameLength = 3; static const std::size_t MaxNameLength = 15; - // list of audio/video parameters + // list of audio parameters static const std::vector audioBitrates; static const std::vector audioEncodings; - static const std::vector videoBitrates; - static const std::vector videoEncodings; - User(); - typedef Wt::Dbo::ptr pointer; - // utility static pointer create(Wt::Dbo::Session& session); @@ -76,32 +68,23 @@ class User void setAdmin(bool admin) { _isAdmin = admin; } void setAudioBitrate(std::size_t bitrate); void setAudioEncoding(AudioEncoding encoding) { _audioEncoding = encoding; } - void setVideoBitrate(std::size_t bitrate); - void setVideoEncoding(VideoEncoding encoding) { _videoEncoding = encoding; } void setMaxAudioBitrate(std::size_t bitrate); - void setMaxVideoBitrate(std::size_t bitrate); void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; } // read bool isAdmin() const {return _isAdmin;} std::size_t getAudioBitrate() const; AudioEncoding getAudioEncoding() const { return _audioEncoding;} - std::size_t getVideoBitrate() const; - VideoEncoding getVideoEncoding() const { return _videoEncoding;} std::size_t getMaxAudioBitrate() const; - std::size_t getMaxVideoBitrate() const; std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; } template void persist(Action& a) { Wt::Dbo::field(a, _maxAudioBitrate, "max_audio_bitrate"); - Wt::Dbo::field(a, _maxVideoBitrate, "max_video_bitrate"); Wt::Dbo::field(a, _isAdmin, "admin"); Wt::Dbo::field(a, _audioBitrate, "audio_bitrate"); Wt::Dbo::field(a, _audioEncoding, "audio_encoding"); - Wt::Dbo::field(a, _videoBitrate, "video_bitrate"); - Wt::Dbo::field(a, _videoEncoding, "video_encoding"); // User's dynamic data Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos"); Wt::Dbo::hasMany(a, _playlists, Wt::Dbo::ManyToOne, "user"); @@ -110,21 +93,15 @@ class User private: static const std::size_t maxAudioBitrate = 320000; - static const std::size_t maxVideoBitrate = 2048000; - static const std::size_t defaultAudioBitrate = 128000; - static const std::size_t defaultVideoBitrate = 1024000; // Admin defined settings int _maxAudioBitrate; - int _maxVideoBitrate; bool _isAdmin; // User defined settings int _audioBitrate; AudioEncoding _audioEncoding; - int _videoBitrate; - VideoEncoding _videoEncoding; // User's dynamic data int _curPlayingTrackPos; // Current track position in queue diff --git a/src/ui/LmsApplication.cpp b/src/ui/LmsApplication.cpp index ab5a6ad7..84dc0c46 100644 --- a/src/ui/LmsApplication.cpp +++ b/src/ui/LmsApplication.cpp @@ -36,6 +36,7 @@ #include "PlayQueueView.hpp" #include "settings/DatabaseView.hpp" +#include "settings/FirstConnectionView.hpp" #include "LmsApplication.hpp" @@ -90,29 +91,26 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnecti messageResourceBundle().use(appRoot() + "playqueue"); messageResourceBundle().use(appRoot() + "release"); messageResourceBundle().use(appRoot() + "releases"); - messageResourceBundle().use(appRoot() + "settings"); + messageResourceBundle().use(appRoot() + "settings-database"); + messageResourceBundle().use(appRoot() + "settings-first-connection"); messageResourceBundle().use(appRoot() + "tracks"); messageResourceBundle().use(appRoot() + "templates"); setTitle("LMS"); + // If here is no account in the database, launch the first connection wizard bool firstConnection; { Wt::Dbo::Transaction transaction(DboSession()); firstConnection = (Database::User::getAll(DboSession()).size() == 0); - - // Create a fake user - // TODO settings - Database::User::create(DboSession()); } LMS_LOG(UI, DEBUG) << "Creating root widget. First connection = " << std::boolalpha << firstConnection; - // If here is no account in the database, launch the first connection wizard -// if (firstConnection) -// createFirstConnectionUI(); -// else + if (firstConnection) + createFirstConnectionUI(); + else createLmsUI(); } @@ -163,7 +161,7 @@ LmsApplication::createFirstConnectionUI() // Hack, use the auth widget builtin strings builtinLocalizedStrings().useBuiltin(skeletons::AuthStrings_xml1); -// root()->addWidget( new Settings::FirstConnectionFormView()); + root()->addWidget(new Settings::FirstConnectionView()); } void diff --git a/src/ui/common/DirectoryValidator.cpp b/src/ui/common/Validators.cpp similarity index 79% rename from src/ui/common/DirectoryValidator.cpp rename to src/ui/common/Validators.cpp index cf6c16fd..2d8b2170 100644 --- a/src/ui/common/DirectoryValidator.cpp +++ b/src/ui/common/Validators.cpp @@ -19,16 +19,31 @@ #include -#include "DirectoryValidator.hpp" +#include "Validators.hpp" namespace UserInterface { +Wt::WValidator* createNameValidator() +{ + Wt::WLengthValidator *v = new Wt::WLengthValidator(); + v->setMandatory(true); + v->setMinimumLength(::Database::User::MinNameLength); + v->setMaximumLength(::Database::User::MaxNameLength); + return v; +} + +Wt::WValidator* createMandatoryValidator() +{ + auto v = new Wt::WValidator(); + v->setMandatory(true); + return v; +} + DirectoryValidator::DirectoryValidator(Wt::WObject *parent) : Wt::WValidator(parent) { } - Wt::WValidator::Result DirectoryValidator::validate(const Wt::WString& input) const { diff --git a/src/ui/common/Validators.hpp b/src/ui/common/Validators.hpp index 76e1b3a5..45f5fd99 100644 --- a/src/ui/common/Validators.hpp +++ b/src/ui/common/Validators.hpp @@ -19,26 +19,23 @@ #pragma once -#include #include #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; -} +Wt::WValidator* createNameValidator(); +Wt::WValidator* createMandatoryValidator(); -static inline Wt::WValidator *createNameValidator() { - Wt::WLengthValidator *v = new Wt::WLengthValidator(); - v->setMandatory(true); - v->setMinimumLength(3); - v->setMaximumLength(::Database::User::MaxNameLength); - return v; -} +class DirectoryValidator : public Wt::WValidator +{ + public: + DirectoryValidator(Wt::WObject *parent = 0); + + Wt::WValidator::Result validate(const Wt::WString& input) const override; + +}; } // namespace UserInterface + diff --git a/src/ui/settings/DatabaseView.cpp b/src/ui/settings/DatabaseView.cpp index 05dad344..2a8940d0 100644 --- a/src/ui/settings/DatabaseView.cpp +++ b/src/ui/settings/DatabaseView.cpp @@ -26,7 +26,7 @@ #include #include -#include "common/DirectoryValidator.hpp" +#include "common/Validators.hpp" #include "database/MediaDirectory.hpp" #include "scanner/MediaScanner.hpp" #include "utils/Logger.hpp" @@ -214,12 +214,6 @@ class DatabaseModel : public Wt::WFormModel } - Wt::WValidator *createMandatoryValidator() - { - Wt::WValidator* v = new Wt::WValidator(); - v->setMandatory(true); - return v; - } Wt::WStringListModel* _updatePeriodModel; Wt::WStringListModel* _updateStartTimeModel; diff --git a/src/ui/settings/DatabaseView.hpp b/src/ui/settings/DatabaseView.hpp index ad82178c..ab930efb 100644 --- a/src/ui/settings/DatabaseView.hpp +++ b/src/ui/settings/DatabaseView.hpp @@ -21,8 +21,6 @@ #include #include -#include -#include namespace UserInterface { namespace Settings { @@ -34,17 +32,12 @@ class DatabaseView : public Wt::WTemplateFormView public: DatabaseView(Wt::WContainerWidget *parent = 0); - Wt::Signal& changed() { return _sigChanged; } - private: - Wt::Signal _sigChanged; - void processSave(); void processDiscard(); void processImmediateScan(); - Wt::WText *_applyInfo; DatabaseModel *_model; }; diff --git a/src/ui/settings/FirstConnectionView.cpp b/src/ui/settings/FirstConnectionView.cpp new file mode 100644 index 00000000..5b1dac97 --- /dev/null +++ b/src/ui/settings/FirstConnectionView.cpp @@ -0,0 +1,164 @@ +/* + * 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 . + */ + +#include +#include +#include +#include + +#include "utils/Logger.hpp" + +#include "common/Validators.hpp" +#include "LmsApplication.hpp" + +#include "FirstConnectionView.hpp" + +namespace UserInterface { +namespace Settings { + +class FirstConnectionModel : public Wt::WFormModel +{ + public: + + // Associate each field with a unique string literal. + static const Field AdminLoginField; + static const Field PasswordField; + static const Field PasswordConfirmField; + + FirstConnectionModel(Wt::WObject *parent = 0) + : Wt::WFormModel(parent) + { + addField(AdminLoginField); + addField(PasswordField); + addField(PasswordConfirmField); + + setValidator(AdminLoginField, createNameValidator()); + setValidator(PasswordField, createMandatoryValidator()); + setValidator(PasswordConfirmField, createMandatoryValidator()); + } + + void saveData() + { + Wt::Dbo::Transaction transaction(DboSession()); + + // Check if a user already exist + // If it's the case, just do nothing + if (!Database::User::getAll(DboSession()).empty()) + throw std::runtime_error("Admin user already created"); + + // Create user + Wt::Auth::User authUser = DbHandler().getUserDatabase().registerNew(); + Database::User::pointer user = DbHandler().getUser(authUser); + + // Account + authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(AdminLoginField)); + Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField)); + + user.modify()->setAdmin( true ); + } + + bool validateField(Field field) + { + Wt::WString error; + + 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(AdminLoginField), ""); + + 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 + { + 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); + } + +}; + +const Wt::WFormModel::Field FirstConnectionModel::AdminLoginField = "admin-login"; +const Wt::WFormModel::Field FirstConnectionModel::PasswordField = "password"; +const Wt::WFormModel::Field FirstConnectionModel::PasswordConfirmField = "password-confirm"; + +FirstConnectionView::FirstConnectionView(Wt::WContainerWidget *parent) +: Wt::WTemplateFormView(parent) +{ + auto model = new FirstConnectionModel(this); + + setTemplateText(Wt::WString::tr("template-settings-first-connection")); + addFunction("tr", &WTemplate::Functions::tr); + addFunction("id", &WTemplate::Functions::id); + + // AdminLogin + Wt::WLineEdit* accountEdit = new Wt::WLineEdit(); + setFormWidget(FirstConnectionModel::AdminLoginField, accountEdit); + + // Password + Wt::WLineEdit* passwordEdit = new Wt::WLineEdit(); + setFormWidget(FirstConnectionModel::PasswordField, passwordEdit ); + passwordEdit->setEchoMode(Wt::WLineEdit::Password); + + // Password confirmation + Wt::WLineEdit* passwordConfirmEdit = new Wt::WLineEdit(); + setFormWidget(FirstConnectionModel::PasswordConfirmField, passwordConfirmEdit); + passwordConfirmEdit->setEchoMode(Wt::WLineEdit::Password); + + auto saveButton = new Wt::WPushButton(Wt::WString::tr("msg-create")); + bindWidget("create-btn", saveButton); + saveButton->clicked().connect(std::bind([=] + { + updateModel(model); + + if (model->validate()) + { + model->saveData(); + notify(Wt::WString::tr("msg-settings-first-connection-done")); + saveButton->setEnabled(false); + } + + updateView(model); + })); + + updateView(model); +} + +} // namespace Settings +} // namespace UserInterface + diff --git a/src/ui/common/DirectoryValidator.hpp b/src/ui/settings/FirstConnectionView.hpp similarity index 72% rename from src/ui/common/DirectoryValidator.hpp rename to src/ui/settings/FirstConnectionView.hpp index 8ee161e2..2a9485e3 100644 --- a/src/ui/common/DirectoryValidator.hpp +++ b/src/ui/settings/FirstConnectionView.hpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Emeric Poupon + * Copyright (C) 2018 Emeric Poupon * * This file is part of LMS. * @@ -17,17 +17,23 @@ * along with LMS. If not, see . */ -#include +#pragma once + +#include +#include namespace UserInterface { +namespace Settings { -class DirectoryValidator : public Wt::WValidator +class FirstConnectionView : public Wt::WTemplateFormView { public: - DirectoryValidator(Wt::WObject *parent = 0); - - Wt::WValidator::Result validate(const Wt::WString& input) const; + FirstConnectionView(Wt::WContainerWidget *parent = 0); }; + +} // namespace Settings } // namespace UserInterface + +