WIP. Reorganizing things to get settings work. First attempt on Database settings

This commit is contained in:
emeric
2014-07-22 12:57:29 +02:00
parent f1cea2404e
commit 938e96cc40
32 changed files with 752 additions and 96 deletions
+28 -23
View File
@@ -7,9 +7,6 @@ bin_PROGRAMS = lms
lms_SOURCES = \
$(top_srcdir)/main/main.cpp \
$(top_srcdir)/main/ServiceManager.cpp \
$(top_srcdir)/main/DatabaseUpdateService.cpp \
$(top_srcdir)/main/UserInterfaceService.cpp \
$(top_srcdir)/av/CodecContext.cpp \
$(top_srcdir)/av/Common.cpp \
$(top_srcdir)/av/Dictionary.cpp \
@@ -18,20 +15,6 @@ lms_SOURCES = \
$(top_srcdir)/av/Stream.cpp \
$(top_srcdir)/cover/CoverArt.cpp \
$(top_srcdir)/cover/CoverArtGrabber.cpp \
$(top_srcdir)/ui/LmsApplication.cpp \
$(top_srcdir)/ui/LmsHome.cpp \
$(top_srcdir)/ui/audio/AudioWidget.cpp \
$(top_srcdir)/ui/audio/AudioDatabaseWidget.cpp \
$(top_srcdir)/ui/audio/AudioMediaPlayerWidget.cpp \
$(top_srcdir)/ui/audio/SearchFilterWidget.cpp \
$(top_srcdir)/ui/audio/TableFilterWidget.cpp \
$(top_srcdir)/ui/audio/TrackWidget.cpp \
$(top_srcdir)/ui/common/SessionData.cpp \
$(top_srcdir)/ui/resource/AvConvTranscodeStreamResource.cpp \
$(top_srcdir)/ui/video/VideoWidget.cpp \
$(top_srcdir)/ui/video/VideoDatabaseWidget.cpp \
$(top_srcdir)/ui/video/VideoMediaPlayerWidget.cpp \
$(top_srcdir)/ui/video/VideoParametersDialog.cpp \
$(top_srcdir)/database/Artist.cpp \
$(top_srcdir)/database/Genre.cpp \
$(top_srcdir)/database/Release.cpp \
@@ -41,14 +24,11 @@ lms_SOURCES = \
$(top_srcdir)/database/SqlQuery.cpp \
$(top_srcdir)/database/Path.cpp \
$(top_srcdir)/database/Video.cpp \
$(top_srcdir)/database/User.cpp \
$(top_srcdir)/database-updater/DatabaseUpdater.cpp \
$(top_srcdir)/database-updater/Checksum.cpp \
$(top_srcdir)/transcode/AvConvTranscoder.cpp \
$(top_srcdir)/transcode/Format.cpp \
$(top_srcdir)/transcode/Parameters.cpp \
$(top_srcdir)/transcode/InputMediaFile.cpp \
$(top_srcdir)/metadata/AvFormat.cpp \
$(top_srcdir)/main/RemoteServerService.cpp \
$(top_srcdir)/metadata/Utils.cpp \
$(top_srcdir)/remote/messages/auth.pb.cc \
$(top_srcdir)/remote/messages/collection.pb.cc \
$(top_srcdir)/remote/messages/common.pb.cc \
@@ -61,7 +41,32 @@ lms_SOURCES = \
$(top_srcdir)/remote/server/MediaRequestHandler.cpp \
$(top_srcdir)/remote/server/RequestHandler.cpp \
$(top_srcdir)/remote/server/Server.cpp \
$(top_srcdir)/metadata/Utils.cpp
$(top_srcdir)/service/ServiceManager.cpp \
$(top_srcdir)/service/DatabaseUpdateService.cpp \
$(top_srcdir)/service/UserInterfaceService.cpp \
$(top_srcdir)/service/RemoteServerService.cpp \
$(top_srcdir)/transcode/AvConvTranscoder.cpp \
$(top_srcdir)/transcode/Format.cpp \
$(top_srcdir)/transcode/Parameters.cpp \
$(top_srcdir)/transcode/InputMediaFile.cpp \
$(top_srcdir)/ui/LmsApplication.cpp \
$(top_srcdir)/ui/LmsHome.cpp \
$(top_srcdir)/ui/auth/LmsAuth.cpp \
$(top_srcdir)/ui/audio/AudioWidget.cpp \
$(top_srcdir)/ui/audio/AudioDatabaseWidget.cpp \
$(top_srcdir)/ui/audio/AudioMediaPlayerWidget.cpp \
$(top_srcdir)/ui/audio/SearchFilterWidget.cpp \
$(top_srcdir)/ui/audio/TableFilterWidget.cpp \
$(top_srcdir)/ui/audio/TrackWidget.cpp \
$(top_srcdir)/ui/common/DirectoryValidator.cpp \
$(top_srcdir)/ui/common/SessionData.cpp \
$(top_srcdir)/ui/resource/AvConvTranscodeStreamResource.cpp \
$(top_srcdir)/ui/video/VideoWidget.cpp \
$(top_srcdir)/ui/video/VideoDatabaseWidget.cpp \
$(top_srcdir)/ui/video/VideoMediaPlayerWidget.cpp \
$(top_srcdir)/ui/video/VideoParametersDialog.cpp \
$(top_srcdir)/ui/settings/Settings.cpp \
$(top_srcdir)/ui/settings/SettingsDatabase.cpp
lms_CXXFLAGS=-std=c++11 -Wall -I$(top_srcdir)/boost/ -I$(top_srcdir)/ui -I$(top_srcdir)/remote
+7
View File
@@ -1,6 +1,12 @@
[ServiceManager]
- Rework the whole start/stop/try/cach/thread/interrupts things
- Rework the io_service thread pool thing
[Services]
- [UI] generate argc/argv from a config file (crypto, port info, db path)
- [Remote] use config file to get crypto, port info, db path
- consider using external web server instead of builtin httpd (new FCGI service?)
[Settings]
- [admin] Add/remove users
@@ -21,6 +27,7 @@
- Scaling: find something more "reliable" than GIL and its customs extensions (adobe work, io_new)?
[Database]
- Remove Audio/Video distinction
- When removing a track, make sure to remove genre/artist/release if last of it
- Use Inotify like system to watch modified/added files?
- Implement a video database cleanup
+4 -4
View File
@@ -67,10 +67,10 @@ AC_CHECK_LIB( [boost_thread],
,
[AC_MSG_ERROR([libboost_thread not found!])])
#AC_CHECK_LIB( [boost_date_time],
# [main],
# ,
# [AC_MSG_ERROR([libboost_date_time not found!])])
AC_CHECK_LIB( [boost_date_time],
[main],
,
[AC_MSG_ERROR([libboost_date_time not found!])])
#AC_CHECK_LIB( [boost_signals],
# [main],
+6 -13
View File
@@ -13,6 +13,12 @@ namespace DatabaseUpdater {
class Updater
{
public:
Updater(boost::filesystem::path db, MetaData::Parser& parser);
// Update database
void process();
private:
struct Stats
{
@@ -30,19 +36,6 @@ class Updater
Stats videoStats;
};
Updater(boost::filesystem::path db, MetaData::Parser& parser);
// Update database
// TODO add a callback to get notifed once finished
void process();
const Result& getResult(void) const { return _result; }
private:
// void refresh(const WatchedDirectory& directory);
// Video
void refreshVideoDirectory( const boost::filesystem::path& directory );
void processVideoFile( const boost::filesystem::path& file);
+25
View File
@@ -91,4 +91,29 @@ Handler::getUserDatabase()
return *_users;
}
User::pointer
Handler::getCurrentUser()
{
if (_login.loggedIn())
return getUser(_login.user());
else
return User::pointer();
}
User::pointer
Handler::getUser(const Wt::Auth::User& authUser)
{
Wt::Dbo::ptr<AuthInfo> authInfo = _users->find(authUser);
User::pointer user = authInfo->user();
if (!user) {
user = _session.add(new User());
authInfo.modify()->setUser(user);
}
return user;
}
} // namespace Database
+10
View File
@@ -1,3 +1,5 @@
#include <boost/foreach.hpp>
#include "MediaDirectory.hpp"
namespace Database {
@@ -27,6 +29,14 @@ MediaDirectory::create(Wt::Dbo::Session& session, boost::filesystem::path p, Typ
return session.add( new MediaDirectory( p, type ) );
}
void
MediaDirectory::eraseAll(Wt::Dbo::Session& session)
{
std::vector<MediaDirectory::pointer> dirs = getAll(session);
BOOST_FOREACH(MediaDirectory::pointer dir, dirs)
dir.remove();
}
std::vector<MediaDirectory::pointer>
MediaDirectory::getAll(Wt::Dbo::Session& session)
{
+15 -1
View File
@@ -24,15 +24,25 @@ class MediaDirectorySettings
static pointer get(Wt::Dbo::Session& session);
// write accessors
void setManualScanRequested(bool value) { _manualScanRequested = value;}
void setUpdatePeriod(boost::posix_time::time_duration dur) { _updatePeriod = dur;}
void setUpdateStartTime(boost::posix_time::time_duration dur) { _updateStartTime = dur;}
void setLastUpdate(boost::posix_time::ptime time) { _lastUpdate = time; }
void setLastScan(boost::posix_time::ptime time) { _lastScan = time; }
// Read accessors
bool getManualScanRequested(void) const { return _manualScanRequested; }
boost::posix_time::time_duration getUpdatePeriod(void) const { return _updatePeriod; }
boost::posix_time::time_duration getUpdateStartTime(void) const { return _updateStartTime; }
boost::posix_time::ptime getLastUpdated(void) const { return _lastUpdate; }
boost::posix_time::ptime getLastScan(void) const { return _lastScan; }
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _manualScanRequested, "manual_scan_requested");
Wt::Dbo::field(a, _updatePeriod, "update_period");
Wt::Dbo::field(a, _updateStartTime, "update_start_time");
Wt::Dbo::field(a, _lastUpdate, "last_update");
Wt::Dbo::field(a, _lastScan, "last_scan");
Wt::Dbo::hasMany(a, _mediaDirectories, Wt::Dbo::ManyToOne, "media_directory_settings");
@@ -40,7 +50,9 @@ class MediaDirectorySettings
private:
boost::posix_time::time_duration _updatePeriod; // TODO
bool _manualScanRequested; // Immadiate scan has been requested by user
boost::posix_time::time_duration _updatePeriod; // How long between updates
boost::posix_time::time_duration _updateStartTime; // Time of day to begin the update
boost::posix_time::ptime _lastUpdate; // last time the database has changed
boost::posix_time::ptime _lastScan; // last time the database has been scanned
Wt::Dbo::collection< Wt::Dbo::ptr<MediaDirectory> > _mediaDirectories; // list of media directories
@@ -65,6 +77,8 @@ class MediaDirectory
static pointer create(Wt::Dbo::Session& session, boost::filesystem::path p, Type type);
static std::vector<MediaDirectory::pointer> getAll(Wt::Dbo::Session& session);
static void eraseAll(Wt::Dbo::Session& session);
Type getType(void) const { return _type; }
boost::filesystem::path getPath(void) const { return boost::filesystem::path(_path); }
+22
View File
@@ -0,0 +1,22 @@
#include "User.hpp"
namespace Database {
User::User()
: _maxAudioBitrate(maxAudioBitrate),
_maxVideoBitrate(maxVideoBitrate),
_isAdmin(false),
_audioBitrate(defaultAudioBitrate),
_videoBitrate(defaultVideoBitrate)
{
}
} // namespace Database
+22 -1
View File
@@ -13,17 +13,38 @@ class User {
public:
User();
typedef Wt::Dbo::ptr<User> pointer;
bool isAdmin() const {return _isAdmin;}
template<class Action>
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, _videoBitrate, "video_bitrate");
}
private:
bool _isAdmin;
static const std::size_t maxAudioBitrate = 320000;
static const std::size_t maxVideoBitrate = 7500000;
static const std::size_t defaultAudioBitrate = 128000;
static const std::size_t defaultVideoBitrate = 1500000;
// Admin defined settings
int _maxAudioBitrate;
int _maxVideoBitrate;
bool _isAdmin;
// User defined settings
int _audioBitrate;
int _videoBitrate;
};
+6 -6
View File
@@ -7,10 +7,10 @@
#include "av/Common.hpp"
#include "database/DatabaseHandler.hpp"
#include "ServiceManager.hpp"
#include "DatabaseUpdateService.hpp"
#include "UserInterfaceService.hpp"
#include "RemoteServerService.hpp"
#include "service/ServiceManager.hpp"
#include "service/DatabaseUpdateService.hpp"
#include "service/UserInterfaceService.hpp"
#include "service/RemoteServerService.hpp"
#include "ui/LmsApplication.hpp"
@@ -21,7 +21,7 @@ int main(int argc, char* argv[])
try
{
ServiceManager serviceManager;
ServiceManager& serviceManager = ServiceManager::instance();
// TODO Retreive the database path in some config file
const boost::filesystem::path dbPath("test.db");
@@ -34,7 +34,7 @@ int main(int argc, char* argv[])
std::cout << "Starting services..." << std::endl;
// serviceManager.startService( std::make_shared<DatabaseUpdateService>( serviceManager.getIoService(), dbPath) );
serviceManager.startService( std::make_shared<DatabaseUpdateService>( serviceManager.getIoService(), dbPath) );
serviceManager.startService( std::make_shared<RemoteServerService>( serviceManager.getIoService(), remoteListenEndpoint, dbPath) );
serviceManager.startService( std::make_shared<UserInterfaceService>(argc, argv, dbPath) );
@@ -11,13 +11,16 @@ DatabaseUpdateService::DatabaseUpdateService(boost::asio::io_service& ioService,
void
DatabaseUpdateService::start(void)
{
_thread = boost::thread(boost::bind(&DatabaseUpdater::Updater::process, &_databaseUpdater));
// TODO
// Read database parameters and program a timer for the next scan
// _thread = boost::thread(boost::bind(&DatabaseUpdater::Updater::process, &_databaseUpdater));
}
void
DatabaseUpdateService::stop(void)
{
std::cout << "DatabaseUpdateService::stop, processing..." << std::endl;
// no effect if thread does not exist
_thread.interrupt();
_thread.join();
std::cout << "DatabaseUpdateService::stop, process done" << std::endl;
@@ -26,7 +29,16 @@ DatabaseUpdateService::stop(void)
void
DatabaseUpdateService::restart(void)
{
std::cout << "DatabaseUpdateService::restart, not implemented" << std::endl;
std::cout << "DatabaseUpdateService::restart" << std::endl;
stop();
start();
}
bool
DatabaseUpdateService::isScanning(void) const
{
// scanning is active only if a thread is running the updater
return _thread.get_id() != boost::thread::id();
}
@@ -13,12 +13,18 @@ class DatabaseUpdateService : public Service
{
public:
typedef std::shared_ptr<DatabaseUpdateService> pointer;
DatabaseUpdateService(boost::asio::io_service& ioService, const boost::filesystem::path& p);
// Service interface
void start(void);
void stop(void);
void restart(void);
// Specific interface
bool isScanning(void) const; //return if the service is currently scanning the db
private:
boost::thread _thread;
+1 -2
View File
@@ -1,6 +1,7 @@
#ifndef SERVICE_HPP
#define SERVICE_HPP
#include <boost/thread.hpp>
#include <memory>
#include <set>
@@ -21,8 +22,6 @@ class Service
virtual void stop(void) = 0;
virtual void restart(void) = 0;
private:
};
#endif
@@ -6,6 +6,13 @@
#include "ServiceManager.hpp"
ServiceManager&
ServiceManager::instance()
{
static ServiceManager instance;
return instance;
}
ServiceManager::ServiceManager()
: _signalSet(_ioService)
{
@@ -11,7 +11,7 @@ class ServiceManager
{
public:
ServiceManager();
static ServiceManager& instance();
~ServiceManager();
void stopService(Service::pointer service);
@@ -25,8 +25,16 @@ class ServiceManager
boost::asio::io_service& getIoService() {return _ioService;}
const boost::asio::io_service& getIoService() const {return _ioService;}
template <class T> typename T::pointer getService();
boost::mutex& mutex() { return _mutex;}
private:
ServiceManager();
ServiceManager(ServiceManager const&); // Don't Implement
void operator=(ServiceManager const&); // Don't implement
void restartServices(void);
void stopServices(void);
@@ -34,6 +42,8 @@ class ServiceManager
void handleSignal(boost::system::error_code error, int signo);
boost::mutex _mutex;
boost::asio::io_service _ioService;
// Listen for interesting signals
@@ -43,5 +53,18 @@ class ServiceManager
};
template <class T> typename T::pointer
ServiceManager::getService()
{
std::set<Service::pointer>::iterator it;
for (std::set<Service::pointer>::iterator it = _services.begin(); it != _services.end(); ++it)
{
if (typeid(*(*it)) == typeid(T)) {
return std::dynamic_pointer_cast<T>(*it);
}
}
return std::shared_ptr<T>();
}
#endif
+4
View File
@@ -17,6 +17,7 @@ remote_SOURCES = \
$(top_srcdir)/database/Track.cpp \
$(top_srcdir)/database/DatabaseHandler.cpp \
$(top_srcdir)/database/SqlQuery.cpp \
$(top_srcdir)/database/User.cpp \
$(top_srcdir)/database/Path.cpp \
$(top_srcdir)/database/Video.cpp
@@ -31,6 +32,7 @@ database_user_SOURCES = \
$(top_srcdir)/database/DatabaseHandler.cpp \
$(top_srcdir)/database/SqlQuery.cpp \
$(top_srcdir)/database/Path.cpp \
$(top_srcdir)/database/User.cpp \
$(top_srcdir)/database/Video.cpp
database_user_CXXFLAGS=-std=c++11 -Wall -Wextra -I$(top_srcdir)
@@ -45,6 +47,7 @@ database_integrity_SOURCES = \
$(top_srcdir)/database/DatabaseHandler.cpp \
$(top_srcdir)/database/SqlQuery.cpp \
$(top_srcdir)/database/Path.cpp \
$(top_srcdir)/database/User.cpp \
$(top_srcdir)/database/Video.cpp
database_integrity_CXXFLAGS=-std=c++11 -Wall -Wextra -I$(top_srcdir)
@@ -59,6 +62,7 @@ database_basics_SOURCES = \
$(top_srcdir)/database/MediaDirectory.cpp \
$(top_srcdir)/database/SqlQuery.cpp \
$(top_srcdir)/database/Path.cpp \
$(top_srcdir)/database/User.cpp \
$(top_srcdir)/database/Video.cpp
database_basics_CXXFLAGS=-std=c++11 -Wall -Wextra -I$(top_srcdir)
+3 -5
View File
@@ -1,5 +1,5 @@
#include <Wt/WBootstrapTheme>
#include "LmsAuth.hpp"
#include "auth/LmsAuth.hpp"
#include "LmsHome.hpp"
#include "LmsApplication.hpp"
@@ -35,12 +35,10 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, boost::filesystem::p
_sessionData.getDatabaseHandler().getLogin().changed().connect(this, &LmsApplication::handleAuthEvent);
LmsAuth *authWidget = new LmsAuth(Database::Handler::getAuthService(),
_sessionData.getDatabaseHandler().getUserDatabase(),
_sessionData.getDatabaseHandler().getLogin());
LmsAuth *authWidget = new LmsAuth(_sessionData.getDatabaseHandler());
authWidget->model()->addPasswordAuth(&Database::Handler::getPasswordService());
authWidget->setRegistrationEnabled(true);
authWidget->setRegistrationEnabled(false);
authWidget->processEnvironment();
-29
View File
@@ -1,29 +0,0 @@
#include <Wt/Auth/AuthService>
#include <Wt/Auth/AbstractUserDatabase>
#include <Wt/Auth/Login>
#include <Wt/Auth/AuthWidget>
#include <Wt/WContainerWidget>
namespace UserInterface {
class LmsAuth : public Wt::Auth::AuthWidget
{
public:
LmsAuth(const Wt::Auth::AuthService &baseAuth,
Wt::Auth::AbstractUserDatabase &users,
Wt::Auth::Login &login,
Wt::WContainerWidget *parent=0)
: Wt::Auth::AuthWidget(baseAuth, users, login, parent) {}
// LoggedInView is delegated to LmsHome
void createLoggedInView () { hide();}
void createLoginView () { show(); Wt::Auth::AuthWidget::createLoginView();}
private:
};
} // namespace UserInterface
+3 -2
View File
@@ -5,6 +5,8 @@
#include <Wt/WPopupMenuItem>
#include <Wt/Auth/Identity>
#include "settings/Settings.hpp"
#include "LmsHome.hpp"
namespace UserInterface {
@@ -31,6 +33,7 @@ LmsHome::LmsHome(SessionData& sessionData)
leftMenu->addItem("Audio", _audioWidget);
leftMenu->addItem("Video", _videoWidget);
leftMenu->addItem("Settings", new Settings::Settings(_sessionData));
// Setup a Right-aligned menu.
Wt::WMenu *rightMenu = new Wt::WMenu();
@@ -38,8 +41,6 @@ LmsHome::LmsHome(SessionData& sessionData)
navigation->addMenu(rightMenu, Wt::AlignRight);
Wt::WPopupMenu *popup = new Wt::WPopupMenu();
popup->addItem("Parameters");
popup->addSeparator();
popup->addItem("Logout");
popup->itemSelected().connect(this, &LmsHome::handleUserMenuSelected);
+27
View File
@@ -0,0 +1,27 @@
#include "LmsAuth.hpp"
namespace UserInterface {
LmsAuth::LmsAuth(Database::Handler& db)
: Wt::Auth::AuthWidget(db.getAuthService(),
db.getUserDatabase(),
db.getLogin())
{
}
void
LmsAuth::createLoggedInView()
{
hide();
}
void
LmsAuth::createLoginView()
{
show();
Wt::Auth::AuthWidget::createLoginView();
}
} // namespace UserInterface
+32
View File
@@ -0,0 +1,32 @@
#ifndef UI_AUTH_HPP
#define UI_AUTH_HPP
#include <Wt/Auth/AuthService>
#include <Wt/Auth/AbstractUserDatabase>
#include <Wt/Auth/Login>
#include <Wt/Auth/AuthWidget>
#include <Wt/WContainerWidget>
#include "database/DatabaseHandler.hpp"
namespace UserInterface {
class LmsAuth : public Wt::Auth::AuthWidget
{
public:
LmsAuth(Database::Handler& db);
// LoggedInView is delegated to LmsHome
void createLoggedInView () ;
void createLoginView ();
private:
};
} // namespace UserInterface
#endif
+29
View File
@@ -0,0 +1,29 @@
#include <boost/filesystem.hpp>
#include "DirectoryValidator.hpp"
namespace UserInterface {
DirectoryValidator::DirectoryValidator(bool mandatory, Wt::WObject *parent)
: Wt::WValidator(mandatory, parent)
{
}
Wt::WValidator::Result
DirectoryValidator::validate(const Wt::WString& input) const
{
boost::filesystem::path p(input.toUTF8());
boost::system::error_code ec;
bool res = boost::filesystem::is_directory(p, ec);
if (ec)
return Wt::WValidator::Result(Wt::WValidator::Invalid, ec.message());
else if (res)
return Wt::WValidator::Result(Wt::WValidator::Valid, "Valid");
else
return Wt::WValidator::Result(Wt::WValidator::Invalid, "Not a directory");
}
} // namespace UserInterface
+14
View File
@@ -0,0 +1,14 @@
#include <Wt/WValidator>
namespace UserInterface {
class DirectoryValidator : public Wt::WValidator
{
public:
DirectoryValidator(bool mandatory, Wt::WObject *parent = 0);
Wt::WValidator::Result validate(const Wt::WString& input) const;
};
} // namespace UserInterface
-7
View File
@@ -1,8 +1,6 @@
#ifndef UI_SESSION_DATA_HPP
#define UI_SESSION_DATA_HPP
#include <string>
#include <boost/filesystem.hpp>
#include "database/DatabaseHandler.hpp"
@@ -15,17 +13,12 @@ class SessionData
SessionData(boost::filesystem::path dbPath);
void setAuthenticatedUser(std::string user);
Database::Handler& getDatabaseHandler() { return _db;}
const Database::Handler& getDatabaseHandler() const { return _db;}
private:
Database::Handler _db;
std::string _authenticatedUser;
};
+45
View File
@@ -0,0 +1,45 @@
#include <Wt/WMenu>
#include <Wt/WStackedWidget>
#include <Wt/WTextArea>
#include "SettingsDatabase.hpp"
#include "Settings.hpp"
namespace UserInterface {
namespace Settings {
Settings::Settings(SessionData& sessionData, Wt::WContainerWidget* parent)
: Wt::WContainerWidget(parent),
_sessionData(sessionData)
{
// Create a stack where the contents will be located.
Wt::WStackedWidget *contents = new Wt::WStackedWidget();
Wt::WMenu *menu = new Wt::WMenu(contents, Wt::Vertical, this);
menu->setStyleClass("nav nav-pills nav-stacked");
menu->setWidth(150);
Wt::Dbo::Transaction transaction( sessionData.getDatabaseHandler().getSession());
::Database::User::pointer user = sessionData.getDatabaseHandler().getCurrentUser();
// Must be logged in here
assert(user);
if (user->isAdmin())
{
// TODO Special admin settings
menu->addItem("Database", new Database(sessionData));
menu->addItem("Users", new Wt::WTextArea("Users here!"));
}
// 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!"));
addWidget(contents);
}
} // namespace Settings
} // namespace UserInterface
+20
View File
@@ -0,0 +1,20 @@
#include <Wt/WContainerWidget>
#include "common/SessionData.hpp"
namespace UserInterface {
namespace Settings {
class Settings : public Wt::WContainerWidget
{
public:
Settings(SessionData& sessionData, Wt::WContainerWidget* parent = 0);
private:
SessionData& _sessionData;
};
} // namespace Settings
} // namespace UserInterface
+328
View File
@@ -0,0 +1,328 @@
#include <map>
#include <Wt/WTable>
#include <Wt/WGroupBox>
#include <Wt/WText>
#include <Wt/WLineEdit>
#include <Wt/WString>
#include <Wt/WPushButton>
#include <Wt/WCheckBox>
#include <Wt/WBreak>
#include <Wt/WMessageBox>
#include "database/MediaDirectory.hpp"
#include "service/ServiceManager.hpp"
#include "service/DatabaseUpdateService.hpp"
#include "common/DirectoryValidator.hpp"
#include "SettingsDatabase.hpp"
namespace UserInterface {
namespace Settings {
Database::Database(SessionData& sessionData, Wt::WContainerWidget *parent)
: Wt::WContainerWidget(parent),
_sessionData(sessionData)
{
// Media directories
{
Wt::WGroupBox *container = new Wt::WGroupBox("Media directories", this);
_table = new Wt::WTable(container);
_table->addStyleClass("table form-inline");
_table->toggleStyleClass("table-hover", true);
_table->toggleStyleClass("table-striped", true);
_table->setHeaderCount(1);
_table->elementAt(0, 0)->addWidget(new Wt::WText("Path"));
_table->elementAt(0, 1)->addWidget(new Wt::WText("Status"));
_table->elementAt(0, 2)->addWidget(new Wt::WText("Delete"));
{
Wt::WPushButton* btn = new Wt::WPushButton("Add");
container->addWidget(btn);
btn->clicked().connect(this, &Database::handleAddDirectory);
}
{
Wt::WPushButton* btn = new Wt::WPushButton("Delete");
container->addWidget(btn);
btn->clicked().connect(this, &Database::handleDelDirectory);
}
}
addWidget(new Wt::WBreak());
// Refresh settings
{
Wt::WGroupBox *container = new Wt::WGroupBox("Refresh settings", this);
Wt::WTable *table = new Wt::WTable(container);
table->addStyleClass("table form-inline");
table->setHeaderCount(1, Wt::Vertical);
std::size_t line = 0;
table->elementAt(line, 0)->addWidget(new Wt::WText("Update period"));
table->elementAt(line, 1)->addWidget(_updatePeriod = new Wt::WComboBox() );
// Populate combo
_updatePeriodModel = new Wt::WStringListModel(_updatePeriod);
_updatePeriodModel->addString("Never");
_updatePeriodModel->setData(0, 0, boost::posix_time::time_duration( boost::posix_time::hours(0) ), Wt::UserRole);
_updatePeriodModel->addString("Daily");
_updatePeriodModel->setData(1, 0, boost::posix_time::time_duration( boost::posix_time::hours(24) ), Wt::UserRole);
_updatePeriodModel->addString("Weekly");
_updatePeriodModel->setData(2, 0, boost::posix_time::time_duration( boost::posix_time::hours(24*7) ), Wt::UserRole);
_updatePeriodModel->addString("Monthly");
_updatePeriodModel->setData(3, 0, boost::posix_time::time_duration(boost::posix_time::hours(24*30) ), Wt::UserRole);
_updatePeriod->setModel(_updatePeriodModel);
line++;
table->elementAt(line, 0)->addWidget(new Wt::WText("Update start time"));
table->elementAt(line, 1)->addWidget(_updateStartTime = new Wt::WComboBox( ));
// populate combo
_updateStartTimeModel = new Wt::WStringListModel(_updateStartTime);
for (std::size_t i = 0; i < 24; ++i)
{
boost::posix_time::time_duration dur = boost::posix_time::hours(i);
boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%H:%M");
facet->time_duration_format("%H:%M");
std::ostringstream oss;
oss.imbue(std::locale(oss.getloc(), facet));
oss << dur;
_updateStartTimeModel->addString( oss.str() );
_updateStartTimeModel->setData(i, 0, dur, Wt::UserRole);
}
_updateStartTime->setModel(_updateStartTimeModel);
line++;
Wt::WPushButton* btn = new Wt::WPushButton("Scan now", container);
btn->clicked().connect(this, &Database::handleScanNow);
}
addWidget(new Wt::WBreak());
addWidget(new Wt::WBreak());
{
Wt::WPushButton* btn = new Wt::WPushButton("Apply", this);
btn->clicked().connect(this, &Database::handleApply);
}
{
Wt::WPushButton* btn = new Wt::WPushButton("Discard", this);
btn->clicked().connect(this, &Database::loadSettings);
}
loadSettings();
}
void
Database::addDirectory(const std::string& path)
{
int line = _table->rowCount();
Wt::WLineEdit* lineEdit = new Wt::WLineEdit( path );
_table->elementAt(line, 0)->addWidget( lineEdit );
DirectoryValidator* validator = new DirectoryValidator(true);
lineEdit->setValidator(validator);
std::string status;
if (!path.empty())
status = validator->validate( path ).message().toUTF8();
Wt::WText* statusText = new Wt::WText( status );
_table->elementAt(line, 1)->addWidget( statusText );
_table->elementAt(line, 2)->addWidget( new Wt::WCheckBox() );
lineEdit->blurred().connect(boost::bind(&Database::handleCheckDirectory, this, lineEdit, statusText));
}
void
Database::loadSettings(void)
{
Wt::Dbo::Transaction transaction(_sessionData.getDatabaseHandler().getSession());
// Get directories
std::vector<::Database::MediaDirectory::pointer> directories = ::Database::MediaDirectory::getAll(_sessionData.getDatabaseHandler().getSession());
// delete rows
assert(_table->rowCount() > 0 );
for (int i = _table->rowCount() - 1; i > 0; --i)
_table->deleteRow(i);
BOOST_FOREACH(::Database::MediaDirectory::pointer directory, directories)
addDirectory( directory->getPath().string() );
// Get refresh settings
::Database::MediaDirectorySettings::pointer settings = ::Database::MediaDirectorySettings::get(_sessionData.getDatabaseHandler().getSession());
{
int i;
for (i = 0; i < _updatePeriodModel->rowCount(); ++i)
{
if (boost::any_cast<boost::posix_time::time_duration>
(_updatePeriodModel->data(_updatePeriodModel->index(i, 0), Wt::UserRole))
>= settings->getUpdatePeriod())
break;
}
_updatePeriod->setCurrentIndex(i);
}
{
int i;
for (i = 0; i < _updateStartTimeModel->rowCount(); ++i)
{
if (boost::any_cast<boost::posix_time::time_duration>
(_updateStartTimeModel->data(_updateStartTimeModel->index(i, 0), Wt::UserRole))
>= settings->getUpdateStartTime())
break;
}
_updateStartTime->setCurrentIndex(i);
}
}
void
Database::handleAddDirectory(void)
{
// TODO check if the previous entry is empty too
addDirectory();
}
void
Database::handleDelDirectory()
{
assert(_table->rowCount() > 0 );
for (int i = _table->rowCount() - 1; i > 0; --i)
{
assert(_table->elementAt(i, 2)->count() == 1);
Wt::WCheckBox *checkBox = dynamic_cast<Wt::WCheckBox*>(_table->elementAt(i, 2)->widget(0));
if (checkBox->checkState() == Wt::CheckState::Checked)
_table->deleteRow(i);
}
}
void
Database::handleCheckDirectory(Wt::WLineEdit* edit, Wt::WText* status)
{
Wt::WValidator* validator = edit->validator();
if (validator)
status->setText( validator->validate( edit->text().toUTF8() ).message().toUTF8());
}
bool
Database::saveSettings(void)
{
Wt::Dbo::Transaction transaction(_sessionData.getDatabaseHandler().getSession());
assert(_table->rowCount() > 0 );
// Validate each directory in the table
for (int i = 1; i < _table->rowCount(); ++i)
{
assert(_table->elementAt(i, 0)->count() == 1);
Wt::WLineEdit* lineEdit = dynamic_cast<Wt::WLineEdit*>(_table->elementAt(i, 0)->widget(0));
if (!lineEdit->validate())
return false;;
}
// erase all directories and add from the table
::Database::MediaDirectory::eraseAll( _sessionData.getDatabaseHandler().getSession() );
for (int i = 1; i < _table->rowCount(); ++i)
{
Wt::WLineEdit* lineEdit = dynamic_cast<Wt::WLineEdit*>(_table->elementAt(i, 0)->widget(0));
::Database::MediaDirectory::create( _sessionData.getDatabaseHandler().getSession(), lineEdit->text().toUTF8(), ::Database::MediaDirectory::Audio);
}
::Database::MediaDirectorySettings::pointer settings = ::Database::MediaDirectorySettings::get(_sessionData.getDatabaseHandler().getSession() );
{
int row = _updateStartTime->currentIndex();
settings.modify()->setUpdateStartTime( boost::any_cast<boost::posix_time::time_duration>( _updateStartTimeModel->data(_updateStartTimeModel->index(row, 0), Wt::UserRole)));
}
{
int row = _updatePeriod->currentIndex();
settings.modify()->setUpdatePeriod( boost::any_cast<boost::posix_time::time_duration>( _updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::UserRole)));
}
return true;
}
void
Database::handleApply(void)
{
if (saveSettings())
{
boost::lock_guard<boost::mutex> serviceLock (ServiceManager::instance().mutex());
DatabaseUpdateService::pointer service = ServiceManager::instance().getService<DatabaseUpdateService>();
if (service)
service->restart();
// Show a popup to say it's fine!
Wt::WMessageBox *messageBox = new Wt::WMessageBox
("Status",
"New settings applied!",
Wt::Information, Wt::Ok);
messageBox->setModal(true);
messageBox->buttonClicked().connect(std::bind([=] ()
{
delete messageBox;
}
));
messageBox->show();
}
else
{
Wt::WMessageBox *messageBox = new Wt::WMessageBox
("Error",
"Cannot apply settings",
Wt::Critical, Wt::Ok);
messageBox->setModal(true);
messageBox->buttonClicked().connect(std::bind([=] ()
{
delete messageBox;
}
));
messageBox->show();
}
}
void
Database::handleScanNow(void)
{
}
} // namespace Settings
} // namespace UserInterface
+50
View File
@@ -0,0 +1,50 @@
#ifndef UI_SETTINGS_DB_HPP
#define UI_SETTINGS_DB_HPP
#include <Wt/WContainerWidget>
#include <Wt/WTable>
#include <Wt/WComboBox>
#include <Wt/WLineEdit>
#include <Wt/WStringListModel>
#include "common/SessionData.hpp"
namespace UserInterface {
namespace Settings {
class Database : public Wt::WContainerWidget
{
public:
Database(SessionData& sessionData, Wt::WContainerWidget *parent = 0);
private:
void handleApply();
void handleCheckDirectory(Wt::WLineEdit* edit, Wt::WText* status);
void handleAddDirectory();
void handleDelDirectory();
void handleScanNow(void);
void loadSettings(void); // load from db
bool saveSettings(void); // save into db
void addDirectory(const std::string& path = "");
SessionData& _sessionData;
Wt::WTable* _table;
Wt::WComboBox* _updatePeriod;
Wt::WStringListModel* _updatePeriodModel;
Wt::WComboBox* _updateStartTime;
Wt::WStringListModel* _updateStartTimeModel;
};
} // namespace Settings
} // namespace UserInterface
#endif