Reworked the project layout
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/WBootstrapTheme>
|
||||
#include "auth/LmsAuth.hpp"
|
||||
|
||||
#include "LmsHome.hpp"
|
||||
#include "settings/SettingsFirstConnectionFormView.hpp"
|
||||
|
||||
#include "LmsApplication.hpp"
|
||||
|
||||
namespace skeletons {
|
||||
extern const char *AuthStrings_xml1;
|
||||
}
|
||||
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
Wt::WApplication*
|
||||
LmsApplication::create(const Wt::WEnvironment& env, boost::filesystem::path dbPath)
|
||||
{
|
||||
/*
|
||||
* You could read information from the environment to decide whether
|
||||
* the user has permission to start a new application
|
||||
*/
|
||||
return new LmsApplication(env, dbPath);
|
||||
}
|
||||
|
||||
/*
|
||||
* The env argument contains information about the new session, and
|
||||
* the initial request. It must be passed to the Wt::WApplication
|
||||
* constructor so it is typically also an argument for your custom
|
||||
* application constructor.
|
||||
*/
|
||||
LmsApplication::LmsApplication(const Wt::WEnvironment& env, boost::filesystem::path dbPath)
|
||||
: Wt::WApplication(env),
|
||||
_sessionData(dbPath),
|
||||
_home(nullptr)
|
||||
{
|
||||
|
||||
Wt::WBootstrapTheme *bootstrapTheme = new Wt::WBootstrapTheme(this);
|
||||
bootstrapTheme->setVersion(Wt::WBootstrapTheme::Version3);
|
||||
bootstrapTheme->setResponsive(true);
|
||||
setTheme(bootstrapTheme);
|
||||
|
||||
// Add a resource bundle
|
||||
messageResourceBundle().use(appRoot() + "templates");
|
||||
|
||||
setTitle("LMS"); // application title
|
||||
|
||||
bool firstConnection;
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(_sessionData.getDatabaseHandler().getSession());
|
||||
|
||||
firstConnection = (Database::User::getAll(_sessionData.getDatabaseHandler().getSession()).size() == 0);
|
||||
}
|
||||
|
||||
// If here is no account in the database, launch the first connection wizard
|
||||
if (firstConnection)
|
||||
{
|
||||
// Hack, use the auth widget builtin strings
|
||||
builtinLocalizedStrings().useBuiltin(skeletons::AuthStrings_xml1);
|
||||
|
||||
root()->addWidget( new Settings::FirstConnectionFormView(_sessionData));
|
||||
}
|
||||
else
|
||||
{
|
||||
_sessionData.getDatabaseHandler().getLogin().changed().connect(this, &LmsApplication::handleAuthEvent);
|
||||
|
||||
LmsAuth *authWidget = new LmsAuth(_sessionData.getDatabaseHandler());
|
||||
|
||||
authWidget->model()->addPasswordAuth(&Database::Handler::getPasswordService());
|
||||
authWidget->setRegistrationEnabled(false);
|
||||
|
||||
authWidget->processEnvironment();
|
||||
|
||||
root()->addWidget(authWidget);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
LmsApplication::handleAuthEvent(void)
|
||||
{
|
||||
if (_sessionData.getDatabaseHandler().getLogin().loggedIn())
|
||||
{
|
||||
if (_home == nullptr) {
|
||||
_home = new LmsHome(_sessionData, root() );
|
||||
}
|
||||
else
|
||||
std::cerr << "Already logged in??" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "user log out" << std::endl;
|
||||
if (_home != nullptr) {
|
||||
delete _home;
|
||||
_home = nullptr;
|
||||
|
||||
// Hack: quit/redirect in order to avoid 'signal not exposed' problems
|
||||
// TODO, investigate/remove?
|
||||
quit();
|
||||
redirect("/");
|
||||
|
||||
}
|
||||
else
|
||||
std::cerr << "Already logged out??" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMS_APPLICATION_HPP
|
||||
#define LMS_APPLICATION_HPP
|
||||
|
||||
#include <Wt/WApplication>
|
||||
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
#include "LmsHome.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
|
||||
class LmsApplication : public Wt::WApplication
|
||||
{
|
||||
public:
|
||||
|
||||
static Wt::WApplication *create(const Wt::WEnvironment& env, boost::filesystem::path dbPath);
|
||||
|
||||
LmsApplication(const Wt::WEnvironment& env, boost::filesystem::path dbPath);
|
||||
|
||||
private:
|
||||
|
||||
void handleAuthEvent(void);
|
||||
|
||||
SessionData _sessionData;
|
||||
|
||||
LmsHome* _home;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/WStackedWidget>
|
||||
#include <Wt/WMenu>
|
||||
#include <Wt/WNavigationBar>
|
||||
#include <Wt/WPopupMenu>
|
||||
#include <Wt/WPopupMenuItem>
|
||||
#include <Wt/Auth/Identity>
|
||||
|
||||
#include "settings/Settings.hpp"
|
||||
|
||||
#include "LmsHome.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
LmsHome::LmsHome(SessionData& sessionData, Wt::WContainerWidget* parent)
|
||||
: Wt::WContainerWidget(parent),
|
||||
_sessionData(sessionData)
|
||||
{
|
||||
const Wt::Auth::User& user = sessionData.getDatabaseHandler().getLogin().user();
|
||||
|
||||
// Create a navigation bar with a link to a web page.
|
||||
Wt::WNavigationBar *navigation = new Wt::WNavigationBar(this);
|
||||
navigation->setTitle("LMS");
|
||||
navigation->setResponsive(true);
|
||||
navigation->addStyleClass("main-nav");
|
||||
|
||||
Wt::WStackedWidget *contentsStack = new Wt::WStackedWidget(this);
|
||||
|
||||
// Setup a Left-aligned menu.
|
||||
Wt::WMenu *leftMenu = new Wt::WMenu(contentsStack);
|
||||
navigation->addMenu(leftMenu);
|
||||
|
||||
_audioWidget = new AudioWidget(_sessionData);
|
||||
_videoWidget = new VideoWidget(_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();
|
||||
|
||||
navigation->addMenu(rightMenu, Wt::AlignRight);
|
||||
|
||||
Wt::WPopupMenu *popup = new Wt::WPopupMenu();
|
||||
popup->addItem("Logout");
|
||||
|
||||
popup->itemSelected().connect(this, &LmsHome::handleUserMenuSelected);
|
||||
|
||||
Wt::WMenuItem *item = new Wt::WMenuItem( user.identity(Wt::Auth::Identity::LoginName) );
|
||||
item->setMenu(popup);
|
||||
rightMenu->addItem(item);
|
||||
|
||||
// Add a Search control.
|
||||
_searchEdit = new Wt::WLineEdit();
|
||||
_searchEdit->setEmptyText("Search...");
|
||||
|
||||
_searchEdit->enterPressed().connect(this, &LmsHome::handleSearch);
|
||||
|
||||
navigation->addSearch(_searchEdit, Wt::AlignLeft);
|
||||
|
||||
addWidget(contentsStack);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
LmsHome::handleUserMenuSelected( Wt::WMenuItem* item)
|
||||
{
|
||||
if (item && item->text() == "Logout") {
|
||||
_sessionData.getDatabaseHandler().getLogin().logout();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LmsHome::handleSearch(void)
|
||||
{
|
||||
// TODO Check currently selected menu item and search it
|
||||
_audioWidget->search( _searchEdit->text().toUTF8() );
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LMS_HOME_HPP
|
||||
#define LMS_HOME_HPP
|
||||
|
||||
#include <Wt/WContainerWidget>
|
||||
#include <Wt/WLineEdit>
|
||||
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
#include "audio/AudioWidget.hpp"
|
||||
#include "video/VideoWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class LmsHome : public Wt::WContainerWidget
|
||||
{
|
||||
public:
|
||||
|
||||
LmsHome(SessionData& sessionData, Wt::WContainerWidget* parent = 0);
|
||||
|
||||
private:
|
||||
|
||||
void handleSearch(void);
|
||||
void handleUserMenuSelected( Wt::WMenuItem* item );
|
||||
|
||||
SessionData& _sessionData;
|
||||
|
||||
Wt::WLineEdit* _searchEdit;
|
||||
AudioWidget* _audioWidget;
|
||||
VideoWidget* _videoWidget;
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<messages xmlns:if="Wt.WTemplate.conditions">
|
||||
<!--FORMS message blocks-->
|
||||
|
||||
<message id="firstConnectionForm-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>
|
||||
${password-info}
|
||||
</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}
|
||||
</div>
|
||||
</div>
|
||||
${apply-info}
|
||||
</div>
|
||||
</message>
|
||||
|
||||
|
||||
<message id="userForm-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>
|
||||
<legend>${access}</legend>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="${id:admin}">
|
||||
Admin
|
||||
</label>
|
||||
<div class="col-sm-5">
|
||||
${admin}
|
||||
</div>
|
||||
<div class="help-block col-sm-5">
|
||||
${admin-info}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="${id:audio-bitrate-limit}">
|
||||
Audio Bitrate Limit
|
||||
</label>
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
${audio-bitrate-limit}
|
||||
<span class="input-group-addon">kbps</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="help-block col-sm-5">
|
||||
${audio-bitrate-limit-info}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="${id:video-bitrate-limit}">
|
||||
Video Bitrate Limit
|
||||
</label>
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
${video-bitrate-limit}
|
||||
<span class="input-group-addon">kbps</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="help-block col-sm-5">
|
||||
${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}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</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="mediaDirectoryForm-template">
|
||||
<legend>${title}</legend>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="${id:path}">
|
||||
Path
|
||||
</label>
|
||||
<div class="col-sm-5">
|
||||
${path}
|
||||
</div>
|
||||
<div class="help-block col-sm-5">
|
||||
${path-info}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="${id:type}">
|
||||
Type
|
||||
</label>
|
||||
<div class="col-sm-5">
|
||||
${type}
|
||||
</div>
|
||||
<div class="help-block col-sm-5">
|
||||
${type-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">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="${id:update-period}">
|
||||
Update period
|
||||
</label>
|
||||
<div class="col-sm-5">
|
||||
${update-period}
|
||||
</div>
|
||||
<div class="help-block col-sm-5">
|
||||
${update-period-info}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="${id:update-start-time}">
|
||||
Update start time
|
||||
</label>
|
||||
<div class="col-sm-5">
|
||||
${update-start-time}
|
||||
</div>
|
||||
<div class="help-block col-sm-5">
|
||||
${update-start-time-info}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
${apply-button} ${discard-button} ${immediate-scan-button}
|
||||
</div>
|
||||
</div>
|
||||
${apply-info}
|
||||
</div>
|
||||
</message>
|
||||
</messages>
|
||||
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <Wt/WTable> // TODO
|
||||
#include <Wt/WBreak> // TODO
|
||||
|
||||
#include "AudioDatabaseWidget.hpp"
|
||||
|
||||
#include "TableFilterWidget.hpp"
|
||||
#include "SearchFilterWidget.hpp"
|
||||
#include "TrackWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
AudioDatabaseWidget::AudioDatabaseWidget( Database::Handler& db, Wt::WContainerWidget *parent)
|
||||
: Wt::WContainerWidget(parent),
|
||||
_refreshingFilters(false)
|
||||
{
|
||||
std::size_t idFilter (0);
|
||||
{
|
||||
SearchFilterWidget* search = new SearchFilterWidget(this);
|
||||
_filters.push_back( search );
|
||||
search->update().connect( boost::bind(&AudioDatabaseWidget::handleFilterUpdated, this, idFilter++) );
|
||||
}
|
||||
|
||||
Wt::WTable* table = new Wt::WTable(this);
|
||||
|
||||
{
|
||||
TableFilterWidget* filterTable = new TableFilterWidget(db, "genre", "name", table->elementAt(0,0));
|
||||
_filters.push_back( filterTable );
|
||||
filterTable->update().connect( boost::bind(&AudioDatabaseWidget::handleFilterUpdated, this, idFilter++) );
|
||||
}
|
||||
{
|
||||
TableFilterWidget* filterTable = new TableFilterWidget(db, "artist", "name", table->elementAt(0,1));
|
||||
_filters.push_back( filterTable );
|
||||
filterTable->update().connect( boost::bind(&AudioDatabaseWidget::handleFilterUpdated, this, idFilter++) );
|
||||
}
|
||||
{
|
||||
TableFilterWidget* filterTable = new TableFilterWidget(db, "release", "name", table->elementAt(0,2));
|
||||
_filters.push_back( filterTable );
|
||||
filterTable->update().connect( boost::bind(&AudioDatabaseWidget::handleFilterUpdated, this, idFilter++) );
|
||||
}
|
||||
|
||||
{
|
||||
TrackWidget* track = new TrackWidget(db, this);
|
||||
_filters.push_back( track );
|
||||
track->trackSelected().connect(this, &AudioDatabaseWidget::handleTrackSelected);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
AudioDatabaseWidget::search(const std::string& text)
|
||||
{
|
||||
SearchFilterWidget* searchWidget ( dynamic_cast<SearchFilterWidget*>(_filters.front() ) );
|
||||
|
||||
searchWidget->setText(text);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioDatabaseWidget::handleTrackSelected(boost::filesystem::path p)
|
||||
{
|
||||
_trackSelected.emit(p);
|
||||
}
|
||||
|
||||
void
|
||||
AudioDatabaseWidget::handleFilterUpdated(std::size_t idFilterUpdated)
|
||||
{
|
||||
// TODO disconnect from event!
|
||||
if (_refreshingFilters)
|
||||
return;
|
||||
|
||||
_refreshingFilters = true;
|
||||
|
||||
FilterWidget::Constraint currentConstraint;
|
||||
|
||||
currentConstraint.where.And( WhereClause( "track.artist_id = artist.id and track.release_id = release.id and track_genre.track_id = track.id and genre.id = track_genre.genre_id"));
|
||||
|
||||
for (std::size_t idFilter = 0; idFilter < _filters.size(); ++idFilter)
|
||||
{
|
||||
FilterWidget* filter = _filters.at(idFilter);
|
||||
|
||||
// Apply contraints created by previous filters
|
||||
if (idFilter > idFilterUpdated) {
|
||||
filter->refresh(currentConstraint);
|
||||
}
|
||||
|
||||
// Get constraints generated by this filter
|
||||
// (Note: adding accross successive calls)
|
||||
filter->getConstraint(currentConstraint);
|
||||
}
|
||||
|
||||
_refreshingFilters = false;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
AudioDatabaseWidget::selectNextTrack(void)
|
||||
{
|
||||
TrackWidget* trackWidget ( dynamic_cast<TrackWidget*>(_filters.back() ) );
|
||||
|
||||
trackWidget->selectNextTrack();
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_DB_WIDGET_HPP
|
||||
#define AUDIO_DB_WIDGET_HPP
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <Wt/WContainerWidget>
|
||||
#include <Wt/WSignal>
|
||||
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
#include "FilterWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class AudioDatabaseWidget : public Wt::WContainerWidget
|
||||
{
|
||||
public:
|
||||
AudioDatabaseWidget( Database::Handler& db, Wt::WContainerWidget *parent = 0);
|
||||
|
||||
void search(const std::string& text);
|
||||
|
||||
void selectNextTrack(void); // Will later emit the next selected track
|
||||
|
||||
// Signals
|
||||
Wt::Signal< boost::filesystem::path >& trackSelected() { return _trackSelected; }
|
||||
|
||||
private:
|
||||
|
||||
Wt::Signal< boost::filesystem::path > _trackSelected;
|
||||
|
||||
void handleTrackSelected(boost::filesystem::path p);
|
||||
void handleFilterUpdated(std::size_t idFilter);
|
||||
|
||||
std::vector<FilterWidget*> _filters; // Free Search, Genre, Artist, Release, etc.
|
||||
|
||||
bool _refreshingFilters;
|
||||
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <Wt/WMediaPlayer>
|
||||
#include <Wt/WProgressBar>
|
||||
|
||||
#include "AudioMediaPlayerWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
AudioMediaPlayerWidget::AudioMediaPlayerWidget( Wt::WContainerWidget *parent)
|
||||
: Wt::WContainerWidget(parent),
|
||||
_mediaResource(nullptr)
|
||||
{
|
||||
_mediaPlayer = new Wt::WMediaPlayer( Wt::WMediaPlayer::Audio, this );
|
||||
// _mediaPlayer->setAlternativeContent (new Wt::WText("You don't have HTML5 audio support!"));
|
||||
// _mediaPlayer->setOptions( Wt::WMediaPlayer::Autoplay );
|
||||
_mediaPlayer->addSource( Wt::WMediaPlayer::OGA, "" );
|
||||
|
||||
_mediaPlayer->ended().connect(this, &AudioMediaPlayerWidget::handleTrackEnded);
|
||||
|
||||
{
|
||||
Wt::WContainerWidget *container = new Wt::WContainerWidget(this);
|
||||
|
||||
_prevBtn = new Wt::WPushButton("<<", container );
|
||||
_playBtn = new Wt::WPushButton("Play", container );
|
||||
_pauseBtn = new Wt::WPushButton("Pause", container );
|
||||
_nextBtn = new Wt::WPushButton(">>", container );
|
||||
|
||||
_curTime = new Wt::WText(container);
|
||||
_timeSlider = new Wt::WSlider( container );
|
||||
_duration = new Wt::WText(container);
|
||||
|
||||
_volumeSlider = new Wt::WSlider( container );
|
||||
_volumeSlider->setRange(0,100);
|
||||
_volumeSlider->setValue(_mediaPlayer->volume() * 100);
|
||||
|
||||
_mediaPlayer->setControlsWidget( container );
|
||||
_mediaPlayer->setButton(Wt::WMediaPlayer::Play, _playBtn);
|
||||
_mediaPlayer->setButton(Wt::WMediaPlayer::Pause, _pauseBtn);
|
||||
|
||||
_mediaPlayer->setText( Wt::WMediaPlayer::CurrentTime, _curTime);
|
||||
_mediaPlayer->setText( Wt::WMediaPlayer::Duration, _duration);
|
||||
|
||||
_mediaPlayer->timeUpdated().connect(this, &AudioMediaPlayerWidget::handleTimeUpdated);
|
||||
}
|
||||
|
||||
_timeSlider->valueChanged().connect(this, &AudioMediaPlayerWidget::handlePlayOffset);
|
||||
_timeSlider->sliderMoved().connect(this, &AudioMediaPlayerWidget::handleSliderMoved);
|
||||
_timeSlider->setDisabled(true);
|
||||
|
||||
_volumeSlider->sliderMoved().connect(this, &AudioMediaPlayerWidget::handleVolumeSliderMoved);
|
||||
|
||||
_nextBtn->clicked().connect(this, &AudioMediaPlayerWidget::handlePlayNext);
|
||||
_prevBtn->clicked().connect(this, &AudioMediaPlayerWidget::handlePlayPrev);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioMediaPlayerWidget::loadPlayer(void)
|
||||
{
|
||||
_mediaPlayer->clearSources();
|
||||
|
||||
_mediaInternalLink.setResource( nullptr );
|
||||
if (_mediaResource)
|
||||
delete _mediaResource;
|
||||
|
||||
assert( _currentParameters );
|
||||
_mediaResource = new AvConvTranscodeStreamResource( *_currentParameters, this );
|
||||
_mediaInternalLink.setResource( _mediaResource );
|
||||
|
||||
_mediaPlayer->addSource( Wt::WMediaPlayer::OGA, _mediaInternalLink );
|
||||
}
|
||||
|
||||
void
|
||||
AudioMediaPlayerWidget::load(const Transcode::Parameters& parameters)
|
||||
{
|
||||
_timeSlider->setDisabled(false);
|
||||
|
||||
_currentParameters = std::make_shared<Transcode::Parameters>( parameters );
|
||||
|
||||
loadPlayer();
|
||||
|
||||
_timeSlider->setRange(0, parameters.getInputMediaFile().getDuration().total_seconds() );
|
||||
_timeSlider->setValue(0);
|
||||
|
||||
_duration->setText( boost::posix_time::to_simple_string( parameters.getInputMediaFile().getDuration() ));
|
||||
|
||||
_mediaPlayer->play();
|
||||
}
|
||||
|
||||
void
|
||||
AudioMediaPlayerWidget::handlePlayOffset(int offsetSecs)
|
||||
{
|
||||
if (!_currentParameters)
|
||||
return;
|
||||
|
||||
_currentParameters->setOffset( boost::posix_time::seconds(offsetSecs) );
|
||||
|
||||
loadPlayer();
|
||||
|
||||
_mediaPlayer->play();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioMediaPlayerWidget::handlePlayNext(void)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void
|
||||
AudioMediaPlayerWidget::handlePlayPrev(void)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioMediaPlayerWidget::handleTrackEnded(void)
|
||||
{
|
||||
_playbackEnded.emit();
|
||||
}
|
||||
|
||||
void
|
||||
AudioMediaPlayerWidget::handleValueChanged(double value)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void
|
||||
AudioMediaPlayerWidget::handleSliderMoved(int value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
AudioMediaPlayerWidget::handleTimeUpdated(void)
|
||||
{
|
||||
if (!_currentParameters)
|
||||
return;
|
||||
|
||||
boost::posix_time::time_duration currentTime ( boost::posix_time::seconds( _mediaPlayer->currentTime() + _currentParameters->getOffset().total_seconds()));
|
||||
|
||||
_timeSlider->setValue( currentTime.total_seconds() );
|
||||
_curTime->setText( boost::posix_time::to_simple_string( currentTime) );
|
||||
}
|
||||
|
||||
void
|
||||
AudioMediaPlayerWidget::handleVolumeSliderMoved(int value)
|
||||
{
|
||||
_mediaPlayer->setVolume( value / 100. );
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MEDIA_PLAYER_WIDGET_HPP
|
||||
#define __MEDIA_PLAYER_WIDGET_HPP
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <Wt/WSlider>
|
||||
#include <Wt/WPushButton>
|
||||
#include <Wt/WContainerWidget>
|
||||
#include <Wt/WMediaPlayer>
|
||||
#include <Wt/WLink>
|
||||
#include <Wt/WText>
|
||||
|
||||
#include "transcode/Parameters.hpp"
|
||||
#include "resource/AvConvTranscodeStreamResource.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class AudioMediaPlayerWidget : public Wt::WContainerWidget
|
||||
{
|
||||
public:
|
||||
|
||||
AudioMediaPlayerWidget( Wt::WContainerWidget *parent = 0);
|
||||
|
||||
void load(const Transcode::Parameters& parameters);
|
||||
|
||||
// Signal slot Next
|
||||
Wt::Signal<void>& playbackEnded() {return _playbackEnded;}
|
||||
// Signal Slot Previous
|
||||
// Signal slot Ended
|
||||
|
||||
private:
|
||||
|
||||
void handlePlayOffset(int offsetSecs);
|
||||
void handlePlayNext(void);
|
||||
void handlePlayPrev(void);
|
||||
void handleTrackEnded(void);
|
||||
|
||||
void handleValueChanged(double);
|
||||
void handleTimeUpdated(void);
|
||||
void handleSliderMoved(int value);
|
||||
|
||||
void handleVolumeSliderMoved(int value);
|
||||
|
||||
void loadPlayer(void);
|
||||
|
||||
// Signals
|
||||
Wt::Signal<void> _playbackEnded;
|
||||
|
||||
// Core
|
||||
Wt::WMediaPlayer* _mediaPlayer;
|
||||
AvConvTranscodeStreamResource* _mediaResource;
|
||||
Wt::WLink _mediaInternalLink;
|
||||
|
||||
// Controls
|
||||
std::shared_ptr<Transcode::Parameters> _currentParameters;
|
||||
Wt::WPushButton* _playBtn;
|
||||
Wt::WPushButton* _pauseBtn;
|
||||
Wt::WPushButton* _nextBtn;
|
||||
Wt::WPushButton* _prevBtn;
|
||||
Wt::WSlider* _timeSlider;
|
||||
Wt::WSlider* _volumeSlider;
|
||||
Wt::WText* _curTime;
|
||||
Wt::WText* _duration;
|
||||
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/WBreak>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#include "AudioWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
AudioWidget::AudioWidget(SessionData& sessionData, Wt::WContainerWidget* parent)
|
||||
: Wt::WContainerWidget(parent),
|
||||
_db(sessionData.getDatabaseHandler()),
|
||||
_audioDbWidget(nullptr),
|
||||
_mediaPlayer(nullptr),
|
||||
_imgResource(nullptr),
|
||||
_img(nullptr)
|
||||
{
|
||||
_audioDbWidget = new AudioDatabaseWidget(sessionData.getDatabaseHandler(), this);
|
||||
|
||||
_audioDbWidget->trackSelected().connect(this, &AudioWidget::playTrack);
|
||||
|
||||
_mediaPlayer = new AudioMediaPlayerWidget(this);
|
||||
_mediaPlayer->playbackEnded().connect(this, &AudioWidget::handleTrackEnded);
|
||||
this->addWidget(new Wt::WBreak());
|
||||
|
||||
// Image
|
||||
_imgResource = new Wt::WMemoryResource(this);
|
||||
_imgLink.setResource( _imgResource);
|
||||
_img = new Wt::WImage(_imgLink, this);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
AudioWidget::search(const std::string& searchText)
|
||||
{
|
||||
_audioDbWidget->search(searchText);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioWidget::playTrack(boost::filesystem::path p)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "play track '" << p << "'";
|
||||
try {
|
||||
|
||||
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
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Can't play video: user does not exists!";
|
||||
return; // TODO logout?
|
||||
}
|
||||
}
|
||||
|
||||
Transcode::InputMediaFile inputFile(p);
|
||||
|
||||
Transcode::Parameters parameters(inputFile, Transcode::Format::get(Transcode::Format::OGA));
|
||||
|
||||
parameters.setBitrate(Transcode::Stream::Audio, bitrate);
|
||||
|
||||
_mediaPlayer->load( parameters );
|
||||
|
||||
// Refresh cover
|
||||
{
|
||||
std::vector<CoverArt::CoverArt> covers = inputFile.getCovers();
|
||||
|
||||
if (!covers.empty())
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Cover found!";
|
||||
if (!covers.front().scale(256)) // TODO
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Cannot resize!";
|
||||
|
||||
//_imgResource->setMimeType(covers.front().getMimeType());
|
||||
_imgResource->setData(covers.front().getData());
|
||||
}
|
||||
else {
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "No cover found!";
|
||||
_imgResource->setData( std::vector<unsigned char>());
|
||||
}
|
||||
|
||||
_imgResource->setChanged();
|
||||
}
|
||||
}
|
||||
catch( std::exception &e)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Caught exception while loading '" << p << "': " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioWidget::handleTrackEnded(void)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Track playback ended!";
|
||||
_audioDbWidget->selectNextTrack();
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_WIDGET_HPP
|
||||
#define AUDIO_WIDGET_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <Wt/WLink>
|
||||
#include <Wt/WImage>
|
||||
#include <Wt/WContainerWidget>
|
||||
#include <Wt/WMemoryResource>
|
||||
|
||||
#include "audio/AudioMediaPlayerWidget.hpp"
|
||||
#include "audio/AudioDatabaseWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class AudioWidget : public Wt::WContainerWidget
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
AudioWidget(SessionData& sessionData, Wt::WContainerWidget* parent = 0);
|
||||
|
||||
void search(const std::string& searchText);
|
||||
|
||||
private:
|
||||
|
||||
void playTrack(boost::filesystem::path p);
|
||||
|
||||
void handleTrackEnded(void);
|
||||
|
||||
Database::Handler& _db;
|
||||
|
||||
AudioDatabaseWidget* _audioDbWidget;
|
||||
|
||||
AudioMediaPlayerWidget* _mediaPlayer;
|
||||
|
||||
// Image
|
||||
Wt::WMemoryResource *_imgResource;
|
||||
Wt::WLink _imgLink;
|
||||
Wt::WImage *_img;
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FILTER_WIDGET_HPP
|
||||
#define FILTER_WIDGET_HPP
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#include <Wt/WSignal>
|
||||
#include <Wt/WContainerWidget>
|
||||
|
||||
#include "database/SqlQuery.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class FilterWidget : public Wt::WContainerWidget {
|
||||
|
||||
public:
|
||||
|
||||
struct Constraint {
|
||||
WhereClause where; // WHERE SQL clause
|
||||
};
|
||||
|
||||
FilterWidget(Wt::WContainerWidget* parent = 0) : Wt::WContainerWidget(parent) {}
|
||||
virtual ~FilterWidget() {}
|
||||
|
||||
// Refresh filter Widget using constraints created by parent filters
|
||||
virtual void refresh(const Constraint& constraint) = 0;
|
||||
|
||||
// Update constraints for child filters
|
||||
virtual void getConstraint(Constraint& constraint) = 0;
|
||||
|
||||
// Emitted when a constraint has changed
|
||||
Wt::Signal<void>& update() { return _update; };
|
||||
|
||||
protected:
|
||||
|
||||
void emitUpdate() { _update.emit(); }
|
||||
|
||||
private:
|
||||
|
||||
Wt::Signal<void> _update;
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/WLabel>
|
||||
|
||||
#include "SearchFilterWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
SearchFilterWidget::SearchFilterWidget(Wt::WContainerWidget* parent)
|
||||
: FilterWidget( parent )
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
SearchFilterWidget::setText(const std::string& text)
|
||||
{
|
||||
_lastEmittedText = text;
|
||||
emitUpdate();
|
||||
}
|
||||
|
||||
// Get constraints created by this filter
|
||||
void
|
||||
SearchFilterWidget::getConstraint(Constraint& constraint)
|
||||
{
|
||||
// No active search means no constaint!
|
||||
if (!_lastEmittedText.empty()) {
|
||||
const std::string bindText ("%%" + _lastEmittedText + "%%");
|
||||
constraint.where.And( WhereClause("(track.name like ? or release.name like ? or artist.name like ?)").bind(bindText).bind(bindText).bind(bindText));
|
||||
}
|
||||
|
||||
// else no constraint!
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SEARCH_FILTER_WIDGET_PP
|
||||
#define SEARCH_FILTER_WIDGET_PP
|
||||
|
||||
#include <Wt/WLineEdit>
|
||||
|
||||
#include "FilterWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class SearchFilterWidget : public FilterWidget {
|
||||
public:
|
||||
SearchFilterWidget(Wt::WContainerWidget* parent = 0);
|
||||
|
||||
void setText(const std::string& text);
|
||||
|
||||
// Set constraint on this filter
|
||||
void refresh(const Constraint& constraint) {}
|
||||
|
||||
// Get constraints created by this filter
|
||||
void getConstraint(Constraint& constraint);
|
||||
|
||||
private:
|
||||
|
||||
void handleKeyWentUp(void);
|
||||
|
||||
std::string _lastEmittedText;
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#include "TableFilterWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
TableFilterWidget::TableFilterWidget(Database::Handler& db, std::string table, std::string field, Wt::WContainerWidget* parent)
|
||||
: FilterWidget( parent ),
|
||||
_db(db),
|
||||
_table(table),
|
||||
_field(field),
|
||||
_tableView(nullptr)
|
||||
{
|
||||
_queryModel.setQuery( _db.getSession().query< ResultType >("select " + _table + "." + _field + ",count(DISTINCT track.id),0 as ORDERBY from track,artist,release,genre,track_genre WHERE track.artist_id = artist.id and track.release_id = release.id and track_genre.track_id = track.id and genre.id = track_genre.genre_id GROUP BY " + _table + "." + _field + " UNION select '<All>',0,1 AS ORDERBY").orderBy("ORDERBY DESC," + _table + "." + _field));
|
||||
_queryModel.addColumn( _table + "." + _field, table);
|
||||
_queryModel.addColumn( "count(DISTINCT track.id)", "Tracks");
|
||||
|
||||
_tableView = new Wt::WTableView( this );
|
||||
_tableView->resize(250, 200); // TODO
|
||||
_tableView->setSelectionMode(Wt::ExtendedSelection);
|
||||
_tableView->setSortingEnabled(false);
|
||||
_tableView->setAlternatingRowColors(true);
|
||||
_tableView->setModel(&_queryModel);
|
||||
|
||||
_tableView->selectionChanged().connect(this, &TableFilterWidget::emitUpdate);
|
||||
|
||||
_queryModel.setBatchSize(100);
|
||||
}
|
||||
|
||||
// Set constraints on this filter
|
||||
void
|
||||
TableFilterWidget::refresh(const Constraint& constraint)
|
||||
{
|
||||
SqlQuery sqlQuery;
|
||||
|
||||
sqlQuery.select(_table + "." + _field + ",count(DISTINCT track.id),0 as ORDERBY");
|
||||
sqlQuery.from().And( FromClause("artist,release,track,genre,track_genre")) ;
|
||||
sqlQuery.where().And(constraint.where); // Add constraint made by other filters
|
||||
sqlQuery.groupBy().And( _table + "." + _field); // Add constraint made by other filters
|
||||
|
||||
SqlQuery AllSqlQuery;
|
||||
AllSqlQuery.select("'<All>',0,1 AS ORDERBY");
|
||||
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << _table << ", generated query = '" << sqlQuery.get() + " UNION " + AllSqlQuery.get() << "'";
|
||||
|
||||
Wt::Dbo::Query<ResultType> query = _db.getSession().query<ResultType>( sqlQuery.get() + " UNION " + AllSqlQuery.get() );
|
||||
|
||||
query.orderBy("ORDERBY DESC," + _table + "." + _field);
|
||||
|
||||
BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) {
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Binding value '" << bindArg << "'";
|
||||
query.bind(bindArg);
|
||||
}
|
||||
|
||||
_queryModel.setQuery( query, true /* Keep columns */);
|
||||
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Finish !";
|
||||
}
|
||||
|
||||
// Get constraint created by this filter
|
||||
void
|
||||
TableFilterWidget::getConstraint(Constraint& constraint)
|
||||
{
|
||||
Wt::WModelIndexSet indexSet = _tableView->selectedIndexes();
|
||||
|
||||
// WHERE statement
|
||||
WhereClause clause;
|
||||
|
||||
BOOST_FOREACH(Wt::WModelIndex index, indexSet) {
|
||||
|
||||
if (!index.isValid())
|
||||
continue;
|
||||
|
||||
ResultType result = _queryModel.resultRow( index.row() );
|
||||
|
||||
// Get the track part
|
||||
std::string name( result.get<0>() );
|
||||
bool isAll( result.get<2>() ) ;
|
||||
|
||||
if (isAll) {
|
||||
// no constraint, just return
|
||||
return;
|
||||
}
|
||||
|
||||
clause.Or(_table + "." + _field + " = ?").bind(name);
|
||||
}
|
||||
|
||||
// Adding our WHERE clause
|
||||
constraint.where.And( clause );
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TABLE_VIEW_FILTER_WIDGET_HPP
|
||||
#define TABLE_VIEW_FILTER_WIDGET_HPP
|
||||
|
||||
|
||||
#include <Wt/Dbo/QueryModel>
|
||||
#include <Wt/WTableView>
|
||||
|
||||
#include "FilterWidget.hpp"
|
||||
#include "database/DatabaseHandler.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class TableFilterWidget : public FilterWidget
|
||||
{
|
||||
|
||||
public:
|
||||
TableFilterWidget(Database::Handler& db, std::string table, std::string field, Wt::WContainerWidget* parent = 0);
|
||||
|
||||
// Set constraints on this filter
|
||||
virtual void refresh(const Constraint& constraint);
|
||||
|
||||
// Get constraints created by this filter
|
||||
virtual void getConstraint(Constraint& constraint);
|
||||
|
||||
protected:
|
||||
|
||||
Database::Handler& _db;
|
||||
const std::string _table;
|
||||
const std::string _field;
|
||||
|
||||
// Name, track count, special value that means 'all' if set to 1
|
||||
typedef boost::tuple<std::string, int, int> ResultType;
|
||||
Wt::Dbo::QueryModel< ResultType > _queryModel;
|
||||
|
||||
Wt::WTableView* _tableView;
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <Wt/WItemDelegate>
|
||||
#include <Wt/WBreak>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#include "TrackWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
TrackWidget::TrackWidget( Database::Handler& db, Wt::WContainerWidget* parent)
|
||||
: FilterWidget( parent ),
|
||||
_db(db),
|
||||
_tableView(nullptr),
|
||||
_trackStats(nullptr)
|
||||
{
|
||||
|
||||
// ----- TRACK -----
|
||||
_queryModel.setQuery(_db.getSession().query<ResultType>("select track,release,artist from track,release,artist where track.release_id = release.id and track.artist_id = artist.id" ).orderBy("artist.name,release.name,track.disc_number,track.track_number"));
|
||||
_queryModel.addColumn( "artist.name", "Artist" );
|
||||
_queryModel.addColumn( "release.name", "Album" );
|
||||
_queryModel.addColumn( "track.disc_number", "Disc #" );
|
||||
_queryModel.addColumn( "track.track_number", "Track #" );
|
||||
_queryModel.addColumn( "track.name", "Track" );
|
||||
_queryModel.addColumn( "track.duration", "Duration" );
|
||||
_queryModel.addColumn( "track.creation_time", "Date" );
|
||||
_queryModel.addColumn( "track.genre_list", "Genres" );
|
||||
|
||||
_queryModel.setBatchSize(1000);
|
||||
|
||||
_tableView = new Wt::WTableView( this );
|
||||
_tableView->resize(Wt::WLength::Auto, 400);
|
||||
|
||||
_tableView->setSortingEnabled(true);
|
||||
_tableView->setSelectionMode(Wt::SingleSelection);
|
||||
_tableView->setAlternatingRowColors(true);
|
||||
_tableView->setModel(&_queryModel);
|
||||
|
||||
{
|
||||
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
||||
delegate->setTextFormat("yyyy");
|
||||
_tableView->setItemDelegateForColumn(6, delegate);
|
||||
}
|
||||
{
|
||||
// TODO better handle 1 hour+ files!
|
||||
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
||||
delegate->setTextFormat("mm:ss");
|
||||
_tableView->setItemDelegateForColumn(5, delegate);
|
||||
}
|
||||
|
||||
// TODO other event!
|
||||
_tableView->selectionChanged().connect(this, &TrackWidget::handleTrackSelected);
|
||||
|
||||
new Wt::WBreak(this);
|
||||
_trackStats = new Wt::WText(this);
|
||||
|
||||
updateStats();
|
||||
}
|
||||
|
||||
void
|
||||
TrackWidget::updateStats(void)
|
||||
{
|
||||
std::ostringstream oss; oss << "Files :" << _tableView->model()->rowCount();
|
||||
// TODO from UTF-8 ?
|
||||
_trackStats->setText(oss.str());
|
||||
}
|
||||
|
||||
// Set constraints created by parent filters
|
||||
void
|
||||
TrackWidget::refresh(const Constraint& constraint)
|
||||
{
|
||||
|
||||
SqlQuery sqlQuery;
|
||||
|
||||
sqlQuery.select( "track,release,artist" );
|
||||
sqlQuery.from().And( FromClause("artist,release,track,genre,track_genre"));
|
||||
sqlQuery.where().And(constraint.where);
|
||||
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "TRACK REQ = '" << sqlQuery.get() << "'";
|
||||
|
||||
Wt::Dbo::Query<ResultType> query = _db.getSession().query<ResultType>( sqlQuery.get() );
|
||||
|
||||
query.groupBy("track").orderBy("artist.name,track.creation_time,release.name,track.disc_number,track.track_number");
|
||||
|
||||
BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) {
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Binding value '" << bindArg << "'";
|
||||
query.bind(bindArg);
|
||||
}
|
||||
|
||||
_queryModel.setQuery( query, true );
|
||||
|
||||
updateStats();
|
||||
}
|
||||
|
||||
void
|
||||
TrackWidget::handleTrackSelected(void)
|
||||
{
|
||||
Wt::WModelIndexSet indexSet = _tableView->selectedIndexes();
|
||||
if (!indexSet.empty()) {
|
||||
Wt::WModelIndex currentIndex( *indexSet.begin() );
|
||||
|
||||
// Check there are remainin tracks!
|
||||
if (currentIndex.isValid())
|
||||
{
|
||||
ResultType result = _queryModel.resultRow( currentIndex.row());
|
||||
|
||||
// Get the track part
|
||||
Wt::Dbo::ptr<Database::Track> track ( result.get<0>() );
|
||||
|
||||
_trackSelected.emit( track->getPath() );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackWidget::selectNextTrack(void)
|
||||
{
|
||||
Wt::WModelIndexSet indexSet = _tableView->selectedIndexes();
|
||||
if (!indexSet.empty()) {
|
||||
Wt::WModelIndex currentIndex( *indexSet.begin() );
|
||||
// Check there are remainin tracks!
|
||||
if (currentIndex.isValid() && _tableView->model()->rowCount() > currentIndex.row() + 1)
|
||||
{
|
||||
_tableView->select( _tableView->model()->index( currentIndex.row() + 1, currentIndex.column()));
|
||||
|
||||
ResultType result = _queryModel.resultRow( currentIndex.row() + 1 );
|
||||
|
||||
// Get the track part
|
||||
Wt::Dbo::ptr<Database::Track> track ( result.get<0>() );
|
||||
|
||||
_trackSelected.emit( track->getPath() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TRACK_WIDGET_HPP
|
||||
#define TRACK_WIDGET_HPP
|
||||
|
||||
#include <Wt/WTableView>
|
||||
#include <Wt/Dbo/QueryModel>
|
||||
|
||||
#include "database/DatabaseHandler.hpp"
|
||||
#include "database/AudioTypes.hpp"
|
||||
|
||||
#include "FilterWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class TrackWidget : public FilterWidget
|
||||
{
|
||||
public:
|
||||
|
||||
TrackWidget( Database::Handler& db, Wt::WContainerWidget* parent = 0);
|
||||
|
||||
// Set constraints created by parent filters
|
||||
void refresh(const Constraint& constraint);
|
||||
|
||||
// Create constraints for child filters (N/A)
|
||||
void getConstraint(Constraint& constraint) {}
|
||||
|
||||
void selectNextTrack(void); // Emit a trackSelected() if success
|
||||
|
||||
// Signals
|
||||
Wt::Signal< boost::filesystem::path >& trackSelected() { return _trackSelected; }
|
||||
|
||||
private:
|
||||
|
||||
void handleTrackSelected();
|
||||
|
||||
Wt::Signal< boost::filesystem::path > _trackSelected;
|
||||
|
||||
Database::Handler& _db;
|
||||
|
||||
typedef boost::tuple<Database::Track::pointer, Database::Release::pointer, Database::Artist::pointer> ResultType;
|
||||
Wt::Dbo::QueryModel< ResultType > _queryModel;
|
||||
Wt::WTableView* _tableView;
|
||||
|
||||
void updateStats(void);
|
||||
|
||||
Wt::WText* _trackStats;
|
||||
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "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
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "DirectoryValidator.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
DirectoryValidator::DirectoryValidator(Wt::WObject *parent)
|
||||
: Wt::WValidator(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Wt::WValidator::Result
|
||||
DirectoryValidator::validate(const Wt::WString& input) const
|
||||
{
|
||||
if (input.empty())
|
||||
return Wt::WValidator::validate(input);
|
||||
|
||||
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);
|
||||
else
|
||||
return Wt::WValidator::Result(Wt::WValidator::Invalid, "Not a directory");
|
||||
}
|
||||
|
||||
|
||||
} // namespace UserInterface
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/WValidator>
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class DirectoryValidator : public Wt::WValidator
|
||||
{
|
||||
public:
|
||||
DirectoryValidator(Wt::WObject *parent = 0);
|
||||
|
||||
Wt::WValidator::Result validate(const Wt::WString& input) const;
|
||||
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SessionData.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
SessionData::SessionData(boost::filesystem::path dbPath)
|
||||
: _db(dbPath)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UI_SESSION_DATA_HPP
|
||||
#define UI_SESSION_DATA_HPP
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "database/DatabaseHandler.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class SessionData
|
||||
{
|
||||
public:
|
||||
|
||||
SessionData(boost::filesystem::path dbPath);
|
||||
|
||||
Database::Handler& getDatabaseHandler() { return _db;}
|
||||
const Database::Handler& getDatabaseHandler() const { return _db;}
|
||||
|
||||
private:
|
||||
|
||||
Database::Handler _db;
|
||||
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/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
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <Wt/Http/Request>
|
||||
#include <Wt/Http/Response>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#include "AvConvTranscodeStreamResource.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
AvConvTranscodeStreamResource::AvConvTranscodeStreamResource(const Transcode::Parameters& parameters, Wt::WObject *parent)
|
||||
: Wt::WResource(parent),
|
||||
_parameters( parameters )
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "CONSTRUCTING RESOURCE";
|
||||
}
|
||||
|
||||
AvConvTranscodeStreamResource::~AvConvTranscodeStreamResource()
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "DESTRUCTING RESOURCE";
|
||||
beingDeleted();
|
||||
}
|
||||
|
||||
void
|
||||
AvConvTranscodeStreamResource::handleRequest(const Wt::Http::Request& request,
|
||||
Wt::Http::Response& response)
|
||||
{
|
||||
// see if this request is for a continuation:
|
||||
Wt::Http::ResponseContinuation *continuation = request.continuation();
|
||||
|
||||
std::shared_ptr<Transcode::AvConvTranscoder> transcoder;
|
||||
if (continuation)
|
||||
transcoder = boost::any_cast<std::shared_ptr<Transcode::AvConvTranscoder> >(continuation->data());
|
||||
|
||||
if (!transcoder)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Launching transcoder";
|
||||
transcoder = std::make_shared<Transcode::AvConvTranscoder>( _parameters);
|
||||
|
||||
response.setMimeType(_parameters.getOutputFormat().getMimeType());
|
||||
}
|
||||
|
||||
Transcode::AvConvTranscoder::data_type& data = transcoder->getOutputData();
|
||||
|
||||
while (!transcoder->isComplete() && data.size() < _bufferSize)
|
||||
transcoder->process();
|
||||
|
||||
// Give the client all the output data
|
||||
Transcode::AvConvTranscoder::data_type::const_iterator it = data.begin();
|
||||
bool copySuccess = true;
|
||||
std::size_t copiedSize = 0;
|
||||
for (Transcode::AvConvTranscoder::data_type::const_iterator it = data.begin(); it != data.end(); ++it)
|
||||
{
|
||||
if (!response.out().put(*it)) {
|
||||
copySuccess = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
copiedSize++;
|
||||
}
|
||||
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Wrote " << copiedSize << " bytes";
|
||||
|
||||
if (!copySuccess)
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "** Write failed!";
|
||||
|
||||
// Consume copied bytes
|
||||
data.erase(data.begin(), data.begin() + copiedSize);
|
||||
|
||||
if (copySuccess && !transcoder->isComplete()) {
|
||||
continuation = response.createContinuation();
|
||||
continuation->setData(transcoder);
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Continuation set!";
|
||||
}
|
||||
else
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "No more data!";
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AVCONV_TRANSCODE_STREAM_RESOURCE_HPP
|
||||
#define AVCONV_TRANSCODE_STREAM_RESOURCE_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include <Wt/WResource>
|
||||
|
||||
#include "transcode/AvConvTranscoder.hpp"
|
||||
#include "transcode/Parameters.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class AvConvTranscodeStreamResource : public Wt::WResource
|
||||
{
|
||||
public:
|
||||
AvConvTranscodeStreamResource(const Transcode::Parameters& parameters, Wt::WObject *parent = 0);
|
||||
~AvConvTranscodeStreamResource();
|
||||
|
||||
void handleRequest(const Wt::Http::Request& request,
|
||||
Wt::Http::Response& response);
|
||||
|
||||
private:
|
||||
Transcode::Parameters _parameters;
|
||||
std::shared_ptr<Transcode::AvConvTranscoder> _transcoder;
|
||||
|
||||
static const std::size_t _bufferSize = 8192*16;
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/WMenu>
|
||||
#include <Wt/WStackedWidget>
|
||||
#include <Wt/WTextArea>
|
||||
#include <Wt/WHBoxLayout>
|
||||
|
||||
#include "SettingsAudioFormView.hpp"
|
||||
#include "SettingsUserFormView.hpp"
|
||||
#include "SettingsAccountFormView.hpp"
|
||||
#include "SettingsDatabaseFormView.hpp"
|
||||
#include "SettingsMediaDirectories.hpp"
|
||||
#include "SettingsUsers.hpp"
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#include "service/ServiceManager.hpp"
|
||||
#include "service/DatabaseUpdateService.hpp"
|
||||
|
||||
#include "Settings.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
namespace Settings {
|
||||
|
||||
Settings::Settings(SessionData& sessionData, Wt::WContainerWidget* parent)
|
||||
: Wt::WContainerWidget(parent),
|
||||
_sessionData(sessionData)
|
||||
{
|
||||
Wt::WHBoxLayout* hLayout = new Wt::WHBoxLayout(this);
|
||||
|
||||
// Create a stack where the contents will be located.
|
||||
Wt::WStackedWidget *contents = new Wt::WStackedWidget();
|
||||
|
||||
contents->setStyleClass("contents");
|
||||
contents->setOverflow(WContainerWidget::OverflowHidden);
|
||||
|
||||
Wt::WMenu *menu = new Wt::WMenu(contents, Wt::Vertical);
|
||||
menu->setStyleClass("nav nav-pills nav-stacked submenu");
|
||||
menu->setWidth(150);
|
||||
|
||||
hLayout->addWidget(menu);
|
||||
hLayout->addWidget(contents, 1);
|
||||
|
||||
Wt::Dbo::Transaction transaction( sessionData.getDatabaseHandler().getSession());
|
||||
|
||||
::Database::User::pointer user = sessionData.getDatabaseHandler().getCurrentUser();
|
||||
|
||||
// Must be logged in here
|
||||
assert(user);
|
||||
|
||||
menu->addItem("Audio", new AudioFormView(sessionData, Database::User::getId(user)));
|
||||
if (user->isAdmin())
|
||||
{
|
||||
MediaDirectories* mediaDirectory = new MediaDirectories(sessionData);
|
||||
mediaDirectory->changed().connect(this, &Settings::handleDatabaseDirectoriesChanged);
|
||||
menu->addItem("Media Folders", mediaDirectory);
|
||||
|
||||
DatabaseFormView* databaseFormView = new DatabaseFormView(sessionData);
|
||||
databaseFormView->changed().connect(this, &Settings::restartDatabaseUpdateService);
|
||||
menu->addItem("Database Update", databaseFormView);
|
||||
|
||||
menu->addItem("Users", new Users(sessionData));
|
||||
}
|
||||
else
|
||||
{
|
||||
menu->addItem("Account", new AccountFormView(sessionData, Database::User::getId(user)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
Settings::handleDatabaseDirectoriesChanged()
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_NOTICE) << "Media directories have changed: requesting imediate scan";
|
||||
// On directory add or delete, request an immediate scan
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(_sessionData.getDatabaseHandler().getSession());
|
||||
Database::MediaDirectorySettings::get(_sessionData.getDatabaseHandler().getSession()).modify()->setManualScanRequested(true);
|
||||
}
|
||||
|
||||
restartDatabaseUpdateService();
|
||||
}
|
||||
|
||||
void
|
||||
Settings::restartDatabaseUpdateService()
|
||||
{
|
||||
// Restarting the update service
|
||||
boost::lock_guard<boost::mutex> serviceLock (Service::ServiceManager::instance().mutex());
|
||||
|
||||
Service::DatabaseUpdateService::pointer service = Service::ServiceManager::instance().getService<Service::DatabaseUpdateService>();
|
||||
if (service)
|
||||
service->restart();
|
||||
}
|
||||
|
||||
} // namespace Settings
|
||||
} // namespace UserInterface
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/WContainerWidget>
|
||||
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
namespace Settings {
|
||||
|
||||
class Settings : public Wt::WContainerWidget
|
||||
{
|
||||
public:
|
||||
Settings(SessionData& sessionData, Wt::WContainerWidget* parent = 0);
|
||||
|
||||
private:
|
||||
|
||||
void handleDatabaseDirectoriesChanged();
|
||||
|
||||
void restartDatabaseUpdateService(void);
|
||||
|
||||
SessionData& _sessionData;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Settings
|
||||
} // namespace UserInterface
|
||||
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Wt/WStringListModel>
|
||||
#include <Wt/WFormModel>
|
||||
#include <Wt/WLineEdit>
|
||||
#include <Wt/WCheckBox>
|
||||
#include <Wt/WComboBox>
|
||||
#include <Wt/WPushButton>
|
||||
#include <Wt/Auth/Identity>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#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)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Dbo exception: " << exception.what();
|
||||
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
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Wt/WStringListModel>
|
||||
#include <Wt/WFormModel>
|
||||
#include <Wt/WComboBox>
|
||||
#include <Wt/WPushButton>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#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)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Dbo exception: " << exception.what();
|
||||
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
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/WString>
|
||||
#include <Wt/WPushButton>
|
||||
#include <Wt/WCheckBox>
|
||||
#include <Wt/WComboBox>
|
||||
#include <Wt/WMessageBox>
|
||||
|
||||
#include <Wt/WFormModel>
|
||||
#include <Wt/WStringListModel>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
#include "database/MediaDirectory.hpp"
|
||||
|
||||
#include "common/DirectoryValidator.hpp"
|
||||
|
||||
#include "SettingsDatabaseFormView.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
namespace Settings {
|
||||
|
||||
class DatabaseFormModel : public Wt::WFormModel
|
||||
{
|
||||
public:
|
||||
// Associate each field with a unique string literal.
|
||||
static const Field UpdatePeriodField;
|
||||
static const Field UpdateStartTimeField;
|
||||
|
||||
DatabaseFormModel(SessionData& sessionData, Wt::WObject *parent = 0)
|
||||
: Wt::WFormModel(parent),
|
||||
_sessionData(sessionData)
|
||||
{
|
||||
initializeModels();
|
||||
|
||||
addField(UpdatePeriodField);
|
||||
addField(UpdateStartTimeField);
|
||||
|
||||
setValidator(UpdatePeriodField, createUpdatePeriodValidator());
|
||||
setValidator(UpdateStartTimeField, createStartTimeValidator());
|
||||
|
||||
// populate the model with initial data
|
||||
loadData();
|
||||
}
|
||||
|
||||
Wt::WAbstractItemModel *updatePeriodModel() { return _updatePeriodModel; }
|
||||
Wt::WAbstractItemModel *updateStartTimeModel() { return _updateStartTimeModel; }
|
||||
|
||||
void loadData()
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(_sessionData.getDatabaseHandler().getSession());
|
||||
|
||||
// Get refresh settings
|
||||
::Database::MediaDirectorySettings::pointer settings = ::Database::MediaDirectorySettings::get(_sessionData.getDatabaseHandler().getSession());
|
||||
|
||||
int periodRow = getUpdatePeriodModelRow( settings->getUpdatePeriod() );
|
||||
if (periodRow != -1)
|
||||
setValue(UpdatePeriodField, updatePeriod(periodRow));
|
||||
|
||||
int startTimeRow = getUpdateStartTimeModelRow( settings->getUpdateStartTime() );
|
||||
if (startTimeRow != -1)
|
||||
setValue(UpdateStartTimeField, updateStartTime( startTimeRow ) );
|
||||
|
||||
}
|
||||
|
||||
void saveData()
|
||||
{
|
||||
Wt::Dbo::Session& session( _sessionData.getDatabaseHandler().getSession());
|
||||
Wt::Dbo::Transaction transaction(session);
|
||||
|
||||
::Database::MediaDirectorySettings::pointer settings = ::Database::MediaDirectorySettings::get(_sessionData.getDatabaseHandler().getSession() );
|
||||
|
||||
int periodRow = getUpdatePeriodModelRow( boost::any_cast<Wt::WString>(value(UpdatePeriodField)));
|
||||
assert(periodRow != -1);
|
||||
settings.modify()->setUpdatePeriod( updatePeriodDuration( periodRow ) );
|
||||
|
||||
int startTimeRow = getUpdateStartTimeModelRow( boost::any_cast<Wt::WString>(value(UpdateStartTimeField)));
|
||||
assert(startTimeRow != -1);
|
||||
settings.modify()->setUpdateStartTime( updateStartTimeDuration( startTimeRow ) );
|
||||
|
||||
}
|
||||
|
||||
bool setImmediateScan(Wt::WString& error)
|
||||
{
|
||||
try {
|
||||
Wt::Dbo::Session& session( _sessionData.getDatabaseHandler().getSession());
|
||||
Wt::Dbo::Transaction transaction(session);
|
||||
|
||||
::Database::MediaDirectorySettings::pointer settings = ::Database::MediaDirectorySettings::get(_sessionData.getDatabaseHandler().getSession() );
|
||||
|
||||
settings.modify()->setManualScanRequested( true );
|
||||
}
|
||||
catch(Wt::Dbo::Exception& exception)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Dbo exception: " << exception.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int getUpdatePeriodModelRow(Wt::WString value)
|
||||
{
|
||||
for (int i = 0; i < _updatePeriodModel->rowCount(); ++i)
|
||||
{
|
||||
if (updatePeriod(i) == value)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int getUpdatePeriodModelRow(Database::MediaDirectorySettings::UpdatePeriod duration)
|
||||
{
|
||||
for (int i = 0; i < _updatePeriodModel->rowCount(); ++i)
|
||||
{
|
||||
if (updatePeriodDuration(i) == duration)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
Database::MediaDirectorySettings::UpdatePeriod updatePeriodDuration(int row) {
|
||||
return boost::any_cast<Database::MediaDirectorySettings::UpdatePeriod>
|
||||
(_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::UserRole));
|
||||
}
|
||||
|
||||
Wt::WString updatePeriod(int row) {
|
||||
return boost::any_cast<Wt::WString>
|
||||
(_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::DisplayRole));
|
||||
}
|
||||
|
||||
|
||||
int getUpdateStartTimeModelRow(Wt::WString value)
|
||||
{
|
||||
for (int i = 0; i < _updateStartTimeModel->rowCount(); ++i)
|
||||
{
|
||||
if (updateStartTime(i) == value)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int getUpdateStartTimeModelRow(boost::posix_time::time_duration duration)
|
||||
{
|
||||
for (int i = 0; i < _updateStartTimeModel->rowCount(); ++i)
|
||||
{
|
||||
if (updateStartTimeDuration(i) == duration)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
boost::posix_time::time_duration updateStartTimeDuration(int row) {
|
||||
return boost::any_cast<boost::posix_time::time_duration>
|
||||
(_updateStartTimeModel->data(_updateStartTimeModel->index(row, 0), Wt::UserRole));
|
||||
}
|
||||
|
||||
Wt::WString updateStartTime(int row) {
|
||||
return boost::any_cast<Wt::WString>
|
||||
(_updateStartTimeModel->data(_updateStartTimeModel->index(row, 0), Wt::DisplayRole));
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void initializeModels() {
|
||||
|
||||
_updatePeriodModel = new Wt::WStringListModel(this);
|
||||
|
||||
_updatePeriodModel->addString("Never");
|
||||
_updatePeriodModel->setData(0, 0, Database::MediaDirectorySettings::Never, Wt::UserRole);
|
||||
|
||||
_updatePeriodModel->addString("Daily");
|
||||
_updatePeriodModel->setData(1, 0, Database::MediaDirectorySettings::Daily, Wt::UserRole);
|
||||
|
||||
_updatePeriodModel->addString("Weekly");
|
||||
_updatePeriodModel->setData(2, 0, Database::MediaDirectorySettings::Weekly, Wt::UserRole);
|
||||
|
||||
_updatePeriodModel->addString("Monthly");
|
||||
_updatePeriodModel->setData(3, 0, Database::MediaDirectorySettings::Monthly, Wt::UserRole);
|
||||
|
||||
_updateStartTimeModel = new Wt::WStringListModel(this);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Wt::WValidator *createUpdatePeriodValidator() {
|
||||
Wt::WValidator* v = new Wt::WValidator();
|
||||
v->setMandatory(true);
|
||||
return v;
|
||||
}
|
||||
|
||||
Wt::WValidator *createStartTimeValidator() {
|
||||
Wt::WValidator* v = new Wt::WValidator();
|
||||
v->setMandatory(true);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
SessionData& _sessionData;
|
||||
Wt::WStringListModel* _updatePeriodModel;
|
||||
Wt::WStringListModel* _updateStartTimeModel;
|
||||
|
||||
};
|
||||
|
||||
const Wt::WFormModel::Field DatabaseFormModel::UpdatePeriodField = "update-period";
|
||||
const Wt::WFormModel::Field DatabaseFormModel::UpdateStartTimeField = "update-start-time";
|
||||
|
||||
|
||||
DatabaseFormView::DatabaseFormView(SessionData& sessionData, Wt::WContainerWidget *parent)
|
||||
: Wt::WTemplateFormView(parent)
|
||||
{
|
||||
_model = new DatabaseFormModel(sessionData, this);
|
||||
|
||||
setTemplateText(tr("databaseForm-template"));
|
||||
addFunction("id", &WTemplate::Functions::id);
|
||||
addFunction("block", &WTemplate::Functions::id);
|
||||
|
||||
_applyInfo = new Wt::WText();
|
||||
_applyInfo->setInline(false);
|
||||
_applyInfo->hide();
|
||||
bindWidget("apply-info", _applyInfo);
|
||||
|
||||
// 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);
|
||||
|
||||
// Title & Buttons
|
||||
bindString("title", "Media folder settings");
|
||||
|
||||
Wt::WPushButton *saveButton = new Wt::WPushButton("Apply");
|
||||
bindWidget("apply-button", saveButton);
|
||||
saveButton->setStyleClass("btn-primary");
|
||||
|
||||
Wt::WPushButton *discardButton = new Wt::WPushButton("Discard");
|
||||
bindWidget("discard-button", discardButton);
|
||||
|
||||
saveButton->clicked().connect(this, &DatabaseFormView::processSave);
|
||||
discardButton->clicked().connect(this, &DatabaseFormView::processDiscard);
|
||||
|
||||
Wt::WPushButton *immediateScanButton = new Wt::WPushButton("Immediate scan");
|
||||
immediateScanButton->setStyleClass("btn-warning");
|
||||
bindWidget("immediate-scan-button", immediateScanButton);
|
||||
immediateScanButton->clicked().connect(this, &DatabaseFormView::processImmediateScan);
|
||||
|
||||
updateView(_model);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
DatabaseFormView::processImmediateScan()
|
||||
{
|
||||
Wt::WString error;
|
||||
_applyInfo->show();
|
||||
|
||||
if (_model->setImmediateScan(error))
|
||||
{
|
||||
_applyInfo->setText( Wt::WString::fromUTF8("Media folder scan has been started!" ) );
|
||||
_applyInfo->setStyleClass("alert alert-warning");
|
||||
|
||||
_sigChanged.emit();
|
||||
}
|
||||
else
|
||||
{
|
||||
_applyInfo->setText( error );
|
||||
_applyInfo->setStyleClass("alert alert-danger");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DatabaseFormView::processDiscard()
|
||||
{
|
||||
_applyInfo->show();
|
||||
|
||||
_applyInfo->setText( Wt::WString::fromUTF8("Parameters reverted!"));
|
||||
_applyInfo->setStyleClass("alert alert-info");
|
||||
|
||||
_model->loadData();
|
||||
|
||||
_model->validate();
|
||||
updateView(_model);
|
||||
}
|
||||
|
||||
void
|
||||
DatabaseFormView::processSave()
|
||||
{
|
||||
updateModel(_model);
|
||||
|
||||
_applyInfo->show();
|
||||
|
||||
if (_model->validate()) {
|
||||
// Make the model to commit data into DB
|
||||
_model->saveData();
|
||||
|
||||
_sigChanged.emit();
|
||||
|
||||
_applyInfo->setText( Wt::WString::fromUTF8("New parameters successfully applied!"));
|
||||
_applyInfo->setStyleClass("alert alert-success");
|
||||
}
|
||||
else {
|
||||
_applyInfo->setText( Wt::WString::fromUTF8("Cannot apply new parameters!"));
|
||||
_applyInfo->setStyleClass("alert alert-danger");
|
||||
}
|
||||
// Udate the view: Delete any validation message in the view, etc.
|
||||
updateView(_model);
|
||||
}
|
||||
|
||||
|
||||
} // namespace Settings
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UI_SETTINGS_DB_FORM_VIEW_HPP
|
||||
#define UI_SETTINGS_DB_FORM_VIEW_HPP
|
||||
|
||||
#include <Wt/WContainerWidget>
|
||||
#include <Wt/WTemplateFormView>
|
||||
#include <Wt/WText>
|
||||
#include <Wt/WSignal>
|
||||
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
namespace Settings {
|
||||
|
||||
class DatabaseFormModel;
|
||||
|
||||
class DatabaseFormView : public Wt::WTemplateFormView
|
||||
{
|
||||
public:
|
||||
DatabaseFormView(SessionData& sessionData, Wt::WContainerWidget *parent = 0);
|
||||
|
||||
Wt::Signal<void>& changed() { return _sigChanged; }
|
||||
|
||||
private:
|
||||
|
||||
Wt::Signal<void> _sigChanged;
|
||||
|
||||
void processSave();
|
||||
void processDiscard();
|
||||
void processImmediateScan();
|
||||
|
||||
Wt::WText *_applyInfo;
|
||||
DatabaseFormModel *_model;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace Settings
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Wt/WFormModel>
|
||||
#include <Wt/WLineEdit>
|
||||
#include <Wt/WPushButton>
|
||||
#include <Wt/Auth/Identity>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#include "common/Validators.hpp"
|
||||
|
||||
#include "SettingsFirstConnectionFormView.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
namespace Settings {
|
||||
|
||||
class FirstConnectionFormModel : 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;
|
||||
|
||||
FirstConnectionFormModel(SessionData& sessionData, Wt::WObject *parent = 0)
|
||||
: Wt::WFormModel(parent),
|
||||
_db(sessionData.getDatabaseHandler())
|
||||
{
|
||||
addField(NameField);
|
||||
addField(EmailField);
|
||||
addField(PasswordField);
|
||||
addField(PasswordConfirmField);
|
||||
|
||||
setValidator(NameField, createNameValidator());
|
||||
setValidator(EmailField, createEmailValidator());
|
||||
|
||||
}
|
||||
|
||||
bool saveData(Wt::WString& error)
|
||||
{
|
||||
// DBO transaction active here
|
||||
try {
|
||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||
|
||||
// Check if a user already exist
|
||||
// If it's the case, just do nothing
|
||||
if (Database::User::getAll(_db.getSession()).size() > 0)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Admin user already created";
|
||||
error = Wt::WString("Admin user already created!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
user.modify()->setAdmin( true );
|
||||
|
||||
}
|
||||
catch(Wt::Dbo::Exception& exception)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Dbo exception: " << exception.what();
|
||||
error = Wt::WString(exception.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validateField(Field field)
|
||||
{
|
||||
// DBO transaction active here
|
||||
|
||||
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(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;
|
||||
};
|
||||
|
||||
const Wt::WFormModel::Field FirstConnectionFormModel::NameField = "name";
|
||||
const Wt::WFormModel::Field FirstConnectionFormModel::EmailField = "email";
|
||||
const Wt::WFormModel::Field FirstConnectionFormModel::PasswordField = "password";
|
||||
const Wt::WFormModel::Field FirstConnectionFormModel::PasswordConfirmField = "password-confirm";
|
||||
|
||||
FirstConnectionFormView::FirstConnectionFormView(SessionData& sessionData, Wt::WContainerWidget *parent)
|
||||
: Wt::WTemplateFormView(parent)
|
||||
{
|
||||
|
||||
_model = new FirstConnectionFormModel(sessionData, this);
|
||||
|
||||
setTemplateText(tr("firstConnectionForm-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(FirstConnectionFormModel::NameField, accountEdit);
|
||||
accountEdit->changed().connect(_applyInfo, &Wt::WWidget::hide);
|
||||
|
||||
// Email
|
||||
Wt::WLineEdit* emailEdit = new Wt::WLineEdit();
|
||||
setFormWidget(FirstConnectionFormModel::EmailField, emailEdit);
|
||||
emailEdit->changed().connect(_applyInfo, &Wt::WWidget::hide);
|
||||
|
||||
// Password
|
||||
Wt::WLineEdit* passwordEdit = new Wt::WLineEdit();
|
||||
setFormWidget(FirstConnectionFormModel::PasswordField, passwordEdit );
|
||||
passwordEdit->setEchoMode(Wt::WLineEdit::Password);
|
||||
passwordEdit->changed().connect(_applyInfo, &Wt::WWidget::hide);
|
||||
|
||||
// Password confirmation
|
||||
Wt::WLineEdit* passwordConfirmEdit = new Wt::WLineEdit();
|
||||
setFormWidget(FirstConnectionFormModel::PasswordConfirmField, passwordConfirmEdit);
|
||||
passwordConfirmEdit->setEchoMode(Wt::WLineEdit::Password);
|
||||
passwordConfirmEdit->changed().connect(_applyInfo, &Wt::WWidget::hide);
|
||||
|
||||
// Title & Buttons
|
||||
bindString("title", "Create Admin account");
|
||||
|
||||
_saveButton = new Wt::WPushButton("Create");
|
||||
_saveButton->setStyleClass("btn-primary");
|
||||
bindWidget("save-button", _saveButton);
|
||||
_saveButton->clicked().connect(this, &FirstConnectionFormView::processSave);
|
||||
|
||||
updateView(_model);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
FirstConnectionFormView::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! Please refresh this page in order to login"));
|
||||
_applyInfo->setStyleClass("alert alert-success");
|
||||
_saveButton->hide();
|
||||
}
|
||||
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
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UI_SETTINGS_FIRST_CONNECTION_FORM_VIEW_HPP
|
||||
#define UI_SETTINGS_FIRST_CONNECTION_FORM_VIEW_HPP
|
||||
|
||||
#include <Wt/WContainerWidget>
|
||||
#include <Wt/WTemplateFormView>
|
||||
#include <Wt/WText>
|
||||
#include <Wt/WPushButton>
|
||||
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
namespace Settings {
|
||||
|
||||
class FirstConnectionFormModel;
|
||||
|
||||
class FirstConnectionFormView : public Wt::WTemplateFormView
|
||||
{
|
||||
public:
|
||||
FirstConnectionFormView(SessionData& sessionData, Wt::WContainerWidget *parent = 0);
|
||||
|
||||
private:
|
||||
|
||||
void processSave();
|
||||
|
||||
Wt::WPushButton* _saveButton;
|
||||
FirstConnectionFormModel* _model;
|
||||
Wt::WText* _applyInfo;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace Settings
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/WGroupBox>
|
||||
#include <Wt/WText>
|
||||
#include <Wt/WPushButton>
|
||||
#include <Wt/WMessageBox>
|
||||
|
||||
#include "database/MediaDirectory.hpp"
|
||||
|
||||
#include "SettingsMediaDirectoryFormView.hpp"
|
||||
|
||||
#include "SettingsMediaDirectories.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
namespace Settings {
|
||||
|
||||
MediaDirectories::MediaDirectories(SessionData& sessionData, Wt::WContainerWidget *parent)
|
||||
: Wt::WContainerWidget(parent),
|
||||
_db(sessionData.getDatabaseHandler())
|
||||
{
|
||||
// Stack two widgets:
|
||||
_stack = new Wt::WStackedWidget(this);
|
||||
|
||||
// 1/ the media directory table view
|
||||
{
|
||||
Wt::WGroupBox *container = new Wt::WGroupBox("Media Folders", _stack);
|
||||
|
||||
_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("#"));
|
||||
_table->elementAt(0, 1)->addWidget(new Wt::WText("Path"));
|
||||
_table->elementAt(0, 2)->addWidget(new Wt::WText("Type"));
|
||||
|
||||
Wt::WPushButton* addBtn = new Wt::WPushButton("Add Folder");
|
||||
addBtn->setStyleClass("btn-success");
|
||||
container->addWidget( addBtn );
|
||||
addBtn->clicked().connect(boost::bind(&MediaDirectories::handleCreateMediaDirectory, this));
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void
|
||||
MediaDirectories::refresh(void)
|
||||
{
|
||||
|
||||
assert(_table->rowCount() > 0);
|
||||
for (int i = _table->rowCount() - 1; i > 0; --i)
|
||||
_table->deleteRow(i);
|
||||
|
||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||
|
||||
std::vector<Database::MediaDirectory::pointer> mediaDirectories = Database::MediaDirectory::getAll(_db.getSession());
|
||||
|
||||
std::size_t id = 1;
|
||||
BOOST_FOREACH(Database::MediaDirectory::pointer mediaDirectory, mediaDirectories)
|
||||
{
|
||||
_table->elementAt(id, 0)->addWidget(new Wt::WText( Wt::WString::fromUTF8("{1}").arg(id)));
|
||||
_table->elementAt(id, 1)->addWidget(new Wt::WText( Wt::WString::fromUTF8(mediaDirectory->getPath().string()) ));
|
||||
|
||||
Wt::WString dirType;
|
||||
switch(mediaDirectory->getType())
|
||||
{
|
||||
case Database::MediaDirectory::Video: dirType = "Video"; break;
|
||||
case Database::MediaDirectory::Audio: dirType = "Audio"; break;
|
||||
}
|
||||
|
||||
_table->elementAt(id, 2)->addWidget( new Wt::WText( dirType ));
|
||||
|
||||
Wt::WPushButton* delBtn = new Wt::WPushButton("Delete");
|
||||
delBtn->setStyleClass("btn-danger");
|
||||
_table->elementAt(id, 3)->addWidget(delBtn);
|
||||
delBtn->clicked().connect(boost::bind( &MediaDirectories::handleDelMediaDirectory, this, mediaDirectory->getPath(), mediaDirectory->getType() ) );
|
||||
|
||||
++id;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaDirectories::handleDelMediaDirectory(boost::filesystem::path p, Database::MediaDirectory::Type type)
|
||||
{
|
||||
Wt::WMessageBox *messageBox = new Wt::WMessageBox
|
||||
("Delete Folder",
|
||||
Wt::WString( "Deleting folder '{1}'?").arg(p.string()),
|
||||
Wt::Question, Wt::Yes | Wt::No);
|
||||
|
||||
messageBox->setModal(true);
|
||||
|
||||
messageBox->buttonClicked().connect(std::bind([=] () {
|
||||
if (messageBox->buttonResult() == Wt::Yes)
|
||||
{
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||
|
||||
// Delete the media diretory
|
||||
Database::MediaDirectory::pointer mediaDirectory = Database::MediaDirectory::get(_db.getSession(), p, type);
|
||||
if (mediaDirectory)
|
||||
mediaDirectory.remove();
|
||||
}
|
||||
|
||||
refresh();
|
||||
|
||||
// Emit something changed in the settings
|
||||
_sigChanged.emit();
|
||||
}
|
||||
|
||||
delete messageBox;
|
||||
}));
|
||||
|
||||
messageBox->show();
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
MediaDirectories::handleCreateMediaDirectory(void)
|
||||
{
|
||||
assert(_stack->count() == 1);
|
||||
|
||||
MediaDirectoryFormView* formView = new MediaDirectoryFormView(_db, _stack);
|
||||
formView->completed().connect(this, &MediaDirectories::handleMediaDirectoryFormCompleted);
|
||||
|
||||
_stack->setCurrentIndex(1);
|
||||
}
|
||||
|
||||
void
|
||||
MediaDirectories::handleMediaDirectoryFormCompleted(bool changed)
|
||||
{
|
||||
_stack->setCurrentIndex(0);
|
||||
|
||||
if (changed)
|
||||
{
|
||||
// Refresh the user table if a change has been made
|
||||
refresh();
|
||||
|
||||
// Emit something changed in the settings
|
||||
_sigChanged.emit();
|
||||
}
|
||||
|
||||
// Delete the form view
|
||||
delete _stack->widget(1);
|
||||
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
} // namespace Settings
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UI_SETTINGS_MEDIA_DIRECTORIES_HPP
|
||||
#define UI_SETTINGS_MEDIA_DIRECTORIES_HPP
|
||||
|
||||
#include <Wt/WStackedWidget>
|
||||
#include <Wt/WContainerWidget>
|
||||
#include <Wt/WTable>
|
||||
#include <Wt/WSignal>
|
||||
|
||||
#include "database/MediaDirectory.hpp"
|
||||
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
namespace Settings {
|
||||
|
||||
class MediaDirectories : public Wt::WContainerWidget
|
||||
{
|
||||
public:
|
||||
MediaDirectories(SessionData& sessioNData, Wt::WContainerWidget *parent = 0);
|
||||
|
||||
void refresh();
|
||||
|
||||
Wt::Signal<void>& changed() { return _sigChanged; }
|
||||
|
||||
private:
|
||||
|
||||
Wt::Signal<void> _sigChanged;
|
||||
|
||||
void handleMediaDirectoryFormCompleted(bool changed);
|
||||
|
||||
void handleDelMediaDirectory(boost::filesystem::path p, Database::MediaDirectory::Type type);
|
||||
void handleCreateMediaDirectory(void);
|
||||
|
||||
Database::Handler& _db;
|
||||
|
||||
Wt::WStackedWidget* _stack;
|
||||
Wt::WTable* _table;
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
} // namespace Settings
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Wt/WStringListModel>
|
||||
#include <Wt/WLineEdit>
|
||||
#include <Wt/WFormModel>
|
||||
#include <Wt/WComboBox>
|
||||
#include <Wt/WPushButton>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
#include "database/MediaDirectory.hpp"
|
||||
|
||||
#include "common/DirectoryValidator.hpp"
|
||||
|
||||
|
||||
#include "SettingsMediaDirectoryFormView.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
namespace Settings {
|
||||
|
||||
// TODO validate if directory already exists
|
||||
class MediaDirectoryFormModel : public Wt::WFormModel
|
||||
{
|
||||
public:
|
||||
|
||||
// Associate each field with a unique string literal.
|
||||
static const Field PathField;
|
||||
static const Field TypeField;
|
||||
|
||||
MediaDirectoryFormModel(Database::Handler& db, Wt::WObject *parent = 0)
|
||||
: Wt::WFormModel(parent),
|
||||
_db(db)
|
||||
{
|
||||
initializeModels();
|
||||
|
||||
addField(PathField);
|
||||
addField(TypeField);
|
||||
|
||||
DirectoryValidator* dirValidator = new DirectoryValidator();
|
||||
dirValidator->setMandatory(true);
|
||||
setValidator(PathField, dirValidator);
|
||||
setValidator(TypeField, new Wt::WValidator(true)); // mandatory
|
||||
|
||||
}
|
||||
|
||||
Wt::WAbstractItemModel *typeModel() { return _typeModel; }
|
||||
|
||||
bool saveData(Wt::WString& error)
|
||||
{
|
||||
try {
|
||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||
|
||||
Database::MediaDirectory::Type type
|
||||
= (valueText(TypeField) == "Audio") ? Database::MediaDirectory::Audio : Database::MediaDirectory::Video;
|
||||
|
||||
if (Database::MediaDirectory::get(_db.getSession(), valueText(PathField).toUTF8(), type))
|
||||
{
|
||||
error = "This Path/Type already exists!";
|
||||
return false;
|
||||
}
|
||||
|
||||
Database::MediaDirectory::create(_db.getSession(), valueText(PathField).toUTF8(), type);
|
||||
|
||||
}
|
||||
catch(Wt::Dbo::Exception& exception)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Dbo exception: " << exception.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void initializeModels()
|
||||
{
|
||||
_typeModel = new Wt::WStringListModel(this);
|
||||
_typeModel->addString("Audio");
|
||||
_typeModel->addString("Video");
|
||||
}
|
||||
|
||||
Database::Handler& _db;
|
||||
std::string _userId;
|
||||
Wt::WStringListModel* _typeModel;
|
||||
};
|
||||
|
||||
const Wt::WFormModel::Field MediaDirectoryFormModel::PathField = "path";
|
||||
const Wt::WFormModel::Field MediaDirectoryFormModel::TypeField = "type";
|
||||
|
||||
MediaDirectoryFormView::MediaDirectoryFormView(Database::Handler& db, Wt::WContainerWidget *parent)
|
||||
: Wt::WTemplateFormView(parent)
|
||||
{
|
||||
|
||||
_model = new MediaDirectoryFormModel(db, this);
|
||||
|
||||
setTemplateText(tr("mediaDirectoryForm-template"));
|
||||
addFunction("id", &WTemplate::Functions::id);
|
||||
addFunction("block", &WTemplate::Functions::id);
|
||||
|
||||
_applyInfo = new Wt::WText();
|
||||
_applyInfo->setInline(false);
|
||||
_applyInfo->hide();
|
||||
bindWidget("apply-info", _applyInfo);
|
||||
|
||||
// Path
|
||||
Wt::WLineEdit* pathEdit = new Wt::WLineEdit();
|
||||
setFormWidget(MediaDirectoryFormModel::PathField, pathEdit);
|
||||
pathEdit->changed().connect(_applyInfo, &Wt::WWidget::hide);
|
||||
|
||||
// Type
|
||||
Wt::WComboBox *typeCB = new Wt::WComboBox();
|
||||
setFormWidget(MediaDirectoryFormModel::TypeField, typeCB);
|
||||
typeCB->setModel(_model->typeModel());
|
||||
typeCB->changed().connect(_applyInfo, &Wt::WWidget::hide);
|
||||
|
||||
// Title & Buttons
|
||||
bindString("title", "Add Media Folder");
|
||||
|
||||
Wt::WPushButton *saveButton = new Wt::WPushButton("Add");
|
||||
saveButton->setStyleClass("btn-primary");
|
||||
bindWidget("save-button", saveButton);
|
||||
saveButton->clicked().connect(this, &MediaDirectoryFormView::processSave);
|
||||
|
||||
Wt::WPushButton *cancelButton = new Wt::WPushButton("Cancel");
|
||||
bindWidget("cancel-button", cancelButton);
|
||||
cancelButton->clicked().connect(this, &MediaDirectoryFormView::processCancel);
|
||||
|
||||
updateView(_model);
|
||||
}
|
||||
|
||||
void
|
||||
MediaDirectoryFormView::processCancel()
|
||||
{
|
||||
// parent widget will delete this widget
|
||||
completed().emit(false);
|
||||
}
|
||||
|
||||
void
|
||||
MediaDirectoryFormView::processSave()
|
||||
{
|
||||
updateModel(_model);
|
||||
|
||||
_applyInfo->show();
|
||||
if (_model->validate())
|
||||
{
|
||||
Wt::WString error;
|
||||
// commit model into DB
|
||||
if (_model->saveData(error) ) {
|
||||
completed().emit(true);
|
||||
return;
|
||||
}
|
||||
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
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UI_SETTINGS_MEDIA_DIR_FORM_VIEW_HPP
|
||||
#define UI_SETTINGS_MEDIA_DIR_FORM_VIEW_HPP
|
||||
|
||||
#include <Wt/WContainerWidget>
|
||||
#include <Wt/WTemplateFormView>
|
||||
#include <Wt/WText>
|
||||
#include <Wt/WSignal>
|
||||
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
namespace Settings {
|
||||
|
||||
class MediaDirectoryFormModel;
|
||||
|
||||
class MediaDirectoryFormView : public Wt::WTemplateFormView
|
||||
{
|
||||
public:
|
||||
MediaDirectoryFormView(Database::Handler& db, Wt::WContainerWidget *parent = 0);
|
||||
|
||||
// Signal emitted once the form is completed
|
||||
Wt::Signal<bool>& completed() { return _sigCompleted; }
|
||||
|
||||
private:
|
||||
void processCancel(); // reload from DB
|
||||
void processSave(); // commit into DB
|
||||
|
||||
Wt::Signal<bool> _sigCompleted;
|
||||
MediaDirectoryFormModel* _model;
|
||||
Wt::WText* _applyInfo;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Settings
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Wt/WStringListModel>
|
||||
#include <Wt/WFormModel>
|
||||
#include <Wt/WLineEdit>
|
||||
#include <Wt/WCheckBox>
|
||||
#include <Wt/WComboBox>
|
||||
#include <Wt/WPushButton>
|
||||
#include <Wt/Auth/Identity>
|
||||
|
||||
#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<bool>(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<bool>(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
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UI_SETTINGS_USER_FORM_VIEW_HPP
|
||||
#define UI_SETTINGS_USER_FORM_VIEW_HPP
|
||||
|
||||
#include <Wt/WContainerWidget>
|
||||
#include <Wt/WTemplateFormView>
|
||||
#include <Wt/WSignal>
|
||||
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
namespace Settings {
|
||||
|
||||
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
|
||||
Wt::Signal<bool>& completed() { return _sigCompleted; }
|
||||
|
||||
private:
|
||||
|
||||
Wt::Signal<bool> _sigCompleted;
|
||||
|
||||
void processSave();
|
||||
void processCancel();
|
||||
|
||||
UserFormModel* _model;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Settings
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <Wt/WGroupBox>
|
||||
#include <Wt/WText>
|
||||
#include <Wt/WPushButton>
|
||||
#include <Wt/WMessageBox>
|
||||
|
||||
#include <Wt/Auth/Identity>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#include "SettingsUserFormView.hpp"
|
||||
|
||||
#include "SettingsUsers.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
namespace Settings {
|
||||
|
||||
Users::Users(SessionData& sessionData, Wt::WContainerWidget *parent)
|
||||
: Wt::WContainerWidget(parent),
|
||||
_sessionData(sessionData)
|
||||
{
|
||||
// Stack two widgets:
|
||||
_stack = new Wt::WStackedWidget(this);
|
||||
|
||||
// 1/ the user table view
|
||||
{
|
||||
Wt::WGroupBox *container = new Wt::WGroupBox("Users", _stack);
|
||||
|
||||
_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("#"));
|
||||
_table->elementAt(0, 1)->addWidget(new Wt::WText("Name"));
|
||||
_table->elementAt(0, 2)->addWidget(new Wt::WText("e-Mail"));
|
||||
_table->elementAt(0, 3)->addWidget(new Wt::WText("Admin"));
|
||||
|
||||
Wt::WPushButton* addBtn = new Wt::WPushButton("Add User");
|
||||
addBtn->setStyleClass("btn-success");
|
||||
container->addWidget( addBtn );
|
||||
addBtn->clicked().connect(boost::bind(&Users::handleCreateUser, this, ""));
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void
|
||||
Users::refresh(void)
|
||||
{
|
||||
|
||||
assert(_table->rowCount() > 0);
|
||||
for (int i = _table->rowCount() - 1; i > 0; --i)
|
||||
_table->deleteRow(i);
|
||||
|
||||
Database::Handler& db = _sessionData.getDatabaseHandler();
|
||||
|
||||
Wt::Dbo::Transaction transaction(db.getSession());
|
||||
|
||||
const Wt::Auth::User& currentUser = db.getLogin().user();
|
||||
|
||||
std::vector<Database::User::pointer> users = Database::User::getAll(db.getSession());
|
||||
|
||||
std::size_t userIndex = 1;
|
||||
for (std::size_t i = 0; i < users.size(); ++i)
|
||||
{
|
||||
|
||||
std::string userId = Database::User::getId(users[i]);
|
||||
|
||||
Wt::Auth::User authUser;
|
||||
|
||||
// Hack try/catch here since it may fail!
|
||||
try {
|
||||
authUser = db.getUserDatabase().findWithId( userId );
|
||||
}
|
||||
catch(Wt::Dbo::Exception& e)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Caught exception when getting userId=" << userId << ": " << e.code();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!authUser.isValid()) {
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Users::refresh: skipping invalid userId = " << userId;
|
||||
continue;
|
||||
}
|
||||
|
||||
_table->elementAt(userIndex, 0)->addWidget(new Wt::WText( Wt::WString::fromUTF8("{1}").arg(userIndex)));
|
||||
_table->elementAt(userIndex, 1)->addWidget(new Wt::WText( authUser.identity(Wt::Auth::Identity::LoginName)) );
|
||||
|
||||
Wt::WText* email = new Wt::WText();
|
||||
if (!authUser.email().empty()) {
|
||||
email->setText(authUser.email());
|
||||
}
|
||||
else {
|
||||
email->setStyleClass("alert-danger");
|
||||
email->setText(authUser.unverifiedEmail());
|
||||
}
|
||||
_table->elementAt(userIndex, 2)->addWidget( email ) ;
|
||||
_table->elementAt(userIndex, 3)->addWidget(new Wt::WText( users[i]->isAdmin() ? "Yes" : "No" ));
|
||||
|
||||
Wt::WPushButton* editBtn = new Wt::WPushButton("Edit");
|
||||
_table->elementAt(userIndex, 4)->addWidget(editBtn);
|
||||
editBtn->clicked().connect(boost::bind( &Users::handleCreateUser, this, userId));
|
||||
|
||||
if (currentUser != authUser)
|
||||
{
|
||||
Wt::WPushButton* delBtn = new Wt::WPushButton("Delete");
|
||||
delBtn->setStyleClass("btn-danger");
|
||||
delBtn->setMargin(5, Wt::Left);
|
||||
_table->elementAt(userIndex, 4)->addWidget(delBtn);
|
||||
delBtn->clicked().connect(boost::bind( &Users::handleDelUser, this, authUser.identity(Wt::Auth::Identity::LoginName), userId));
|
||||
}
|
||||
|
||||
++userIndex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
Users::handleDelUser(Wt::WString loginNameIdentity, std::string id)
|
||||
{
|
||||
Wt::WMessageBox *messageBox = new Wt::WMessageBox
|
||||
("Delete User",
|
||||
Wt::WString( "Deleting user '{1}'?").arg(loginNameIdentity),
|
||||
Wt::Question, Wt::Yes | Wt::No);
|
||||
|
||||
messageBox->setModal(true);
|
||||
|
||||
messageBox->buttonClicked().connect(std::bind([=] () {
|
||||
if (messageBox->buttonResult() == Wt::Yes)
|
||||
{
|
||||
Database::Handler& db = _sessionData.getDatabaseHandler();
|
||||
|
||||
Wt::Dbo::Transaction transaction(db.getSession());
|
||||
|
||||
// Delete the user
|
||||
Wt::Auth::User authUser = db.getUserDatabase().findWithId( id );
|
||||
|
||||
db.getUserDatabase().deleteUser( authUser );
|
||||
|
||||
Database::User::pointer user = Database::User::getById(db.getSession(), id);
|
||||
if (user)
|
||||
user.remove();
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
delete messageBox;
|
||||
}));
|
||||
|
||||
messageBox->show();
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
Users::handleCreateUser(std::string id)
|
||||
{
|
||||
assert(_stack->count() == 1);
|
||||
|
||||
UserFormView* userFormView = new UserFormView(_sessionData, id, _stack);
|
||||
userFormView->completed().connect(this, &Users::handleUserFormCompleted);
|
||||
|
||||
_stack->setCurrentIndex(1);
|
||||
}
|
||||
|
||||
void
|
||||
Users::handleUserFormCompleted(bool changed)
|
||||
{
|
||||
_stack->setCurrentIndex(0);
|
||||
|
||||
// Refresh the user table if a change has been made
|
||||
if (changed)
|
||||
refresh();
|
||||
|
||||
// Delete the form view
|
||||
delete _stack->widget(1);
|
||||
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
} // namespace Settings
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UI_SETTINGS_USERS_HPP
|
||||
#define UI_SETTINGS_USERS_HPP
|
||||
|
||||
#include <Wt/WStackedWidget>
|
||||
#include <Wt/WContainerWidget>
|
||||
#include <Wt/WTable>
|
||||
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
namespace Settings {
|
||||
|
||||
class Users : public Wt::WContainerWidget
|
||||
{
|
||||
public:
|
||||
Users(SessionData& sessioNData, Wt::WContainerWidget *parent = 0);
|
||||
|
||||
void refresh();
|
||||
|
||||
private:
|
||||
|
||||
void handleUserFormCompleted(bool changed);
|
||||
|
||||
void handleDelUser(Wt::WString loginNameIdentity, std::string id);
|
||||
void handleCreateUser(std::string id); // set the id in order to edit the user
|
||||
|
||||
SessionData& _sessionData;
|
||||
|
||||
Wt::WStackedWidget* _stack;
|
||||
Wt::WTable* _table;
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
} // namespace Settings
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <Wt/WText>
|
||||
#include <Wt/WPushButton>
|
||||
|
||||
#include <Wt/WMediaPlayer>
|
||||
#include <Wt/WFileResource>
|
||||
|
||||
#include "transcode/Parameters.hpp"
|
||||
#include "database/VideoTypes.hpp"
|
||||
#include "database/MediaDirectory.hpp"
|
||||
|
||||
#include "VideoDatabaseWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
VideoDatabaseWidget::VideoDatabaseWidget(Database::Handler& db, Wt::WContainerWidget *parent)
|
||||
: Wt::WContainerWidget(parent),
|
||||
_db(db)
|
||||
{
|
||||
_table = new Wt::WTable( this );
|
||||
_table->setHeaderCount(1);
|
||||
|
||||
_table->addStyleClass("table form-inline");
|
||||
|
||||
_table->toggleStyleClass("table-hover", true);
|
||||
_table->toggleStyleClass("table-striped", true);
|
||||
|
||||
updateView( boost::filesystem::path(), 0);
|
||||
}
|
||||
|
||||
void
|
||||
VideoDatabaseWidget::addHeader(void)
|
||||
{
|
||||
_table->elementAt(0, 0)->addWidget(new Wt::WText("Name"));
|
||||
_table->elementAt(0, 1)->addWidget(new Wt::WText("Duration"));
|
||||
_table->elementAt(0, 2)->addWidget(new Wt::WText("Action"));
|
||||
}
|
||||
|
||||
void
|
||||
VideoDatabaseWidget::addDirectory(const std::string& name, boost::filesystem::path path, size_t depth)
|
||||
{
|
||||
|
||||
int row = _table->rowCount();
|
||||
|
||||
new Wt::WText(Wt::WString::fromUTF8( name ), _table->elementAt(row, 0));
|
||||
new Wt::WText(Wt::WString::fromUTF8( " " ), _table->elementAt(row, 1));
|
||||
|
||||
Wt::WPushButton* btn = new Wt::WPushButton(Wt::WString::fromUTF8( "Open"), _table->elementAt(row, 2));
|
||||
|
||||
btn->clicked().connect(std::bind([=] () {
|
||||
updateView(path, depth);
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoDatabaseWidget::addVideo(const std::string& name, const boost::posix_time::time_duration& duration, const boost::filesystem::path& path)
|
||||
{
|
||||
|
||||
int row = _table->rowCount();
|
||||
|
||||
new Wt::WText(Wt::WString::fromUTF8( name ), _table->elementAt(row, 0));
|
||||
new Wt::WText(Wt::WString::fromUTF8( boost::posix_time::to_simple_string( duration )), _table->elementAt(row, 1));
|
||||
|
||||
Wt::WPushButton* btn = new Wt::WPushButton(Wt::WString::fromUTF8( "Play"), _table->elementAt(row, 2));
|
||||
|
||||
btn->clicked().connect(std::bind([=] () {
|
||||
playVideo().emit(path);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoDatabaseWidget::updateView(boost::filesystem::path directory, size_t depth)
|
||||
{
|
||||
_table->clear();
|
||||
addHeader();
|
||||
|
||||
// If directory is not valid, add the root Media Directories
|
||||
if (depth == 0)
|
||||
{
|
||||
Wt::Dbo::Transaction transaction ( _db.getSession() );
|
||||
|
||||
std::vector<Database::MediaDirectory::pointer> dirs
|
||||
= Database::MediaDirectory::getByType(_db.getSession(), Database::MediaDirectory::Video);
|
||||
|
||||
BOOST_FOREACH(Database::MediaDirectory::pointer dir, dirs)
|
||||
{
|
||||
addDirectory( dir->getPath().filename().string(),
|
||||
dir->getPath(),
|
||||
depth + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addDirectory( "..", directory.parent_path(), depth - 1);
|
||||
|
||||
// Iterators over files in the directory
|
||||
// If directory, add a directory entry
|
||||
// If video, add a video entry
|
||||
std::vector<boost::filesystem::path> paths;
|
||||
std::copy(boost::filesystem::directory_iterator(directory), boost::filesystem::directory_iterator(), std::back_inserter(paths));
|
||||
|
||||
std::sort(paths.begin(), paths.end());
|
||||
|
||||
BOOST_FOREACH(const boost::filesystem::path& path, paths)
|
||||
{
|
||||
if (boost::filesystem::is_directory(path))
|
||||
addDirectory( path.filename().string(), path, depth + 1);
|
||||
else if (boost::filesystem::is_regular(path) )
|
||||
{
|
||||
Wt::Dbo::Transaction transaction ( _db.getSession() );
|
||||
|
||||
Database::Video::pointer video = Database::Video::getByPath( _db.getSession(), path);
|
||||
if (video)
|
||||
addVideo( video->getName(), video->getDuration(), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef VIDEO_DB_WIDGET_HPP
|
||||
#define VIDEO_DB_WIDGET_HPP
|
||||
|
||||
#include <Wt/WTable>
|
||||
#include <Wt/WContainerWidget>
|
||||
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class VideoDatabaseWidget : public Wt::WContainerWidget
|
||||
{
|
||||
public:
|
||||
VideoDatabaseWidget( Database::Handler& db, Wt::WContainerWidget *parent = 0);
|
||||
|
||||
// Signals
|
||||
Wt::Signal< boost::filesystem::path >& playVideo() { return _playVideo; }
|
||||
|
||||
private:
|
||||
|
||||
void addHeader(void);
|
||||
void addDirectory(const std::string& name, boost::filesystem::path path, size_t depth);
|
||||
void addVideo(const std::string& name, const boost::posix_time::time_duration& duration, const boost::filesystem::path& path);
|
||||
|
||||
void updateView(boost::filesystem::path directory, size_t depth);
|
||||
|
||||
Database::Handler& _db;
|
||||
|
||||
Wt::Signal< boost::filesystem::path > _playVideo;
|
||||
|
||||
Wt::WTable* _table;
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <Wt/WMediaPlayer>
|
||||
|
||||
#include "VideoParametersDialog.hpp"
|
||||
|
||||
#include "VideoMediaPlayerWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
Wt::WMediaPlayer::Encoding
|
||||
convert(Transcode::Format format)
|
||||
{
|
||||
switch( format.getEncoding() )
|
||||
{
|
||||
case Transcode::Format::OGA: return Wt::WMediaPlayer::OGA;
|
||||
case Transcode::Format::OGV: return Wt::WMediaPlayer::OGV;
|
||||
case Transcode::Format::MP3: return Wt::WMediaPlayer::MP3;
|
||||
case Transcode::Format::WEBMA: return Wt::WMediaPlayer::WEBMA;
|
||||
case Transcode::Format::WEBMV: return Wt::WMediaPlayer::WEBMV;
|
||||
case Transcode::Format::FLV: return Wt::WMediaPlayer::FLV;
|
||||
case Transcode::Format::M4A: return Wt::WMediaPlayer::M4A;
|
||||
case Transcode::Format::M4V: return Wt::WMediaPlayer::M4V;
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
|
||||
VideoMediaPlayerWidget::VideoMediaPlayerWidget( const Transcode::Parameters& parameters, Wt::WContainerWidget *parent)
|
||||
: Wt::WContainerWidget(parent),
|
||||
_mediaResource(nullptr),
|
||||
_currentParameters(parameters),
|
||||
_dialog(nullptr)
|
||||
{
|
||||
_mediaPlayer = new Wt::WMediaPlayer( Wt::WMediaPlayer::Video, this );
|
||||
// _mediaPlayer->setAlternativeContent (new Wt::WText("You don't have HTML5 audio support!"));
|
||||
// _mediaPlayer->setOptions( Wt::WMediaPlayer::Autoplay );
|
||||
// _mediaPlayer->addSource( Wt::WMediaPlayer::WEBMV, "" );
|
||||
|
||||
{
|
||||
Wt::WContainerWidget *container = new Wt::WContainerWidget(this);
|
||||
|
||||
_playBtn = new Wt::WPushButton("Play", container );
|
||||
_pauseBtn = new Wt::WPushButton("Pause", container );
|
||||
|
||||
_curTime = new Wt::WText(container);
|
||||
_timeSlider = new Wt::WSlider( container );
|
||||
_duration = new Wt::WText(container);
|
||||
|
||||
_volumeSlider = new Wt::WSlider( container );
|
||||
_volumeSlider->setRange(0,100);
|
||||
_volumeSlider->setValue(_mediaPlayer->volume() * 100);
|
||||
|
||||
_mediaPlayer->setControlsWidget( container );
|
||||
_mediaPlayer->setButton(Wt::WMediaPlayer::Play, _playBtn);
|
||||
_mediaPlayer->setButton(Wt::WMediaPlayer::Pause, _pauseBtn);
|
||||
|
||||
_mediaPlayer->setText( Wt::WMediaPlayer::CurrentTime, _curTime);
|
||||
_mediaPlayer->setText( Wt::WMediaPlayer::Duration, _duration);
|
||||
|
||||
_mediaPlayer->timeUpdated().connect(this, &VideoMediaPlayerWidget::handleTimeUpdated);
|
||||
|
||||
Wt::WPushButton* fullScreenButton = new Wt::WPushButton("Fullscreen", container);
|
||||
_mediaPlayer->setButton(Wt::WMediaPlayer::FullScreen, fullScreenButton);
|
||||
|
||||
Wt::WPushButton* restoreScreenButton = new Wt::WPushButton("Restore screen", container);
|
||||
_mediaPlayer->setButton(Wt::WMediaPlayer::RestoreScreen, restoreScreenButton);
|
||||
|
||||
_mediaPlayer->timeUpdated().connect(this, &VideoMediaPlayerWidget::handleTimeUpdated);
|
||||
|
||||
_timeSlider->valueChanged().connect(this, &VideoMediaPlayerWidget::handlePlayOffset);
|
||||
_timeSlider->sliderMoved().connect(this, &VideoMediaPlayerWidget::handleSliderMoved);
|
||||
|
||||
_volumeSlider->sliderMoved().connect(this, &VideoMediaPlayerWidget::handleVolumeSliderMoved);
|
||||
}
|
||||
|
||||
|
||||
Wt::WPushButton* closeButton = new Wt::WPushButton("Close", this);
|
||||
closeButton->clicked().connect( this, &VideoMediaPlayerWidget::handleClose );
|
||||
|
||||
Wt::WPushButton* parametersButton = new Wt::WPushButton("Parameters", this);
|
||||
parametersButton->clicked().connect( this, &VideoMediaPlayerWidget::handleParametersEdit );
|
||||
|
||||
load(parameters);
|
||||
}
|
||||
|
||||
void
|
||||
VideoMediaPlayerWidget::load(const Transcode::Parameters& parameters)
|
||||
{
|
||||
_mediaPlayer->clearSources();
|
||||
|
||||
_currentParameters = parameters;
|
||||
|
||||
_mediaInternalLink.setResource( nullptr );
|
||||
if (_mediaResource)
|
||||
delete _mediaResource;
|
||||
|
||||
_mediaResource = new AvConvTranscodeStreamResource( parameters, this );
|
||||
_mediaInternalLink.setResource( _mediaResource );
|
||||
|
||||
_mediaPlayer->addSource( convert(parameters.getOutputFormat()), _mediaInternalLink );
|
||||
|
||||
_timeSlider->setRange(0, parameters.getInputMediaFile().getDuration().total_seconds() );
|
||||
_timeSlider->setValue( parameters.getOffset().total_seconds() );
|
||||
|
||||
_duration->setText( boost::posix_time::to_simple_string( parameters.getInputMediaFile().getDuration() ));
|
||||
|
||||
_mediaPlayer->play();
|
||||
}
|
||||
|
||||
void
|
||||
VideoMediaPlayerWidget::handlePlayOffset(int offsetSecs)
|
||||
{
|
||||
std::cout << "Want to play at offset " << offsetSecs << std::endl;;
|
||||
_currentParameters.setOffset( boost::posix_time::seconds(offsetSecs) );
|
||||
load( _currentParameters );
|
||||
|
||||
_timeSlider->setValue( offsetSecs );
|
||||
}
|
||||
|
||||
void
|
||||
VideoMediaPlayerWidget::handleSliderMoved(int value)
|
||||
{
|
||||
std::cout << "Slider moved to " << value << std::endl;
|
||||
_curTime->setText( boost::posix_time::to_simple_string( boost::posix_time::seconds( value ) ) );
|
||||
}
|
||||
|
||||
void
|
||||
VideoMediaPlayerWidget::handleTimeUpdated(void)
|
||||
{
|
||||
std::cout << "Time updated to " << _mediaPlayer->currentTime() << std::endl;
|
||||
|
||||
if (_mediaPlayer->currentTime() > 0 && _mediaPlayer->currentTime() < _currentParameters.getInputMediaFile().getDuration().total_seconds())
|
||||
{
|
||||
boost::posix_time::time_duration currentTime ( boost::posix_time::seconds( _mediaPlayer->currentTime() + _currentParameters.getOffset().total_seconds()));
|
||||
|
||||
_timeSlider->setValue( currentTime.total_seconds() );
|
||||
_curTime->setText( boost::posix_time::to_simple_string( currentTime) );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VideoMediaPlayerWidget::handleVolumeSliderMoved(int value)
|
||||
{
|
||||
_mediaPlayer->setVolume( value / 100. );
|
||||
}
|
||||
|
||||
void
|
||||
VideoMediaPlayerWidget::handleClose(void)
|
||||
{
|
||||
_close.emit();
|
||||
}
|
||||
|
||||
void
|
||||
VideoMediaPlayerWidget::handleParametersEdit(void)
|
||||
{
|
||||
|
||||
_dialog = new VideoParametersDialog("Parameters");
|
||||
_dialog->load(_currentParameters);
|
||||
|
||||
_dialog->show();
|
||||
|
||||
_dialog->finished().connect(this, &VideoMediaPlayerWidget::handleParametersDone);
|
||||
}
|
||||
|
||||
void
|
||||
VideoMediaPlayerWidget::handleParametersDone(Wt::WDialog::DialogCode code)
|
||||
{
|
||||
assert(_dialog != nullptr);
|
||||
|
||||
if (code == Wt::WDialog::Accepted)
|
||||
{
|
||||
_dialog->save( _currentParameters );
|
||||
|
||||
// TODO SYNC current offset with player?
|
||||
// HACK use slider current value
|
||||
handlePlayOffset( _timeSlider->value());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __VIDEO_MEDIA_PLAYER_WIDGET_HPP
|
||||
#define __VIDEO_MEDIA_PLAYER_WIDGET_HPP
|
||||
|
||||
#include <Wt/WDialog>
|
||||
#include <Wt/WSlider>
|
||||
#include <Wt/WPushButton>
|
||||
#include <Wt/WContainerWidget>
|
||||
#include <Wt/WLink>
|
||||
|
||||
#include "VideoParametersDialog.hpp"
|
||||
|
||||
#include "transcode/Parameters.hpp"
|
||||
#include "resource/AvConvTranscodeStreamResource.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class VideoMediaPlayerWidget : public Wt::WContainerWidget
|
||||
{
|
||||
public:
|
||||
|
||||
VideoMediaPlayerWidget( const Transcode::Parameters& parameters, Wt::WContainerWidget *parent = 0);
|
||||
|
||||
|
||||
Wt::Signal<void>& close() { return _close; };
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Signals
|
||||
Wt::Signal<void> _close;
|
||||
|
||||
void load(const Transcode::Parameters& parameters);
|
||||
|
||||
// Player controls
|
||||
void handlePlayOffset(int offsetSecs);
|
||||
void handleTimeUpdated(void);
|
||||
void handleSliderMoved(int value);
|
||||
void handleFullscreen(void);
|
||||
void handleVolumeSliderMoved(int value);
|
||||
|
||||
void handleClose(void);
|
||||
|
||||
void handleParametersEdit(void);
|
||||
void handleParametersDone(Wt::WDialog::DialogCode);
|
||||
|
||||
// Core
|
||||
Wt::WMediaPlayer* _mediaPlayer;
|
||||
AvConvTranscodeStreamResource* _mediaResource;
|
||||
Wt::WLink _mediaInternalLink;
|
||||
|
||||
// Controls
|
||||
Transcode::Parameters _currentParameters;
|
||||
Wt::WPushButton* _playBtn;
|
||||
Wt::WPushButton* _pauseBtn;
|
||||
Wt::WSlider* _timeSlider;
|
||||
Wt::WSlider* _volumeSlider;
|
||||
Wt::WText* _curTime;
|
||||
Wt::WText* _duration;
|
||||
|
||||
VideoParametersDialog* _dialog;
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <Wt/WPushButton>
|
||||
#include <Wt/WLabel>
|
||||
#include <Wt/WTable>
|
||||
|
||||
#include "VideoParametersDialog.hpp"
|
||||
|
||||
using namespace Transcode;
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
static const std::list<Stream::Type> streamTypes = {Stream::Video, Stream::Audio, Stream::Subtitle};
|
||||
|
||||
VideoParametersDialog::VideoParametersDialog(const Wt::WString &windowTitle, Wt::WDialog* parent)
|
||||
: Wt::WDialog(windowTitle, parent)
|
||||
{
|
||||
|
||||
Wt::WTable* layout = new Wt::WTable(contents());
|
||||
int row = 0;
|
||||
|
||||
{
|
||||
Wt::WLabel* label = new Wt::WLabel("Format");
|
||||
_outputFormat = new Wt::WComboBox();
|
||||
label->setBuddy(_outputFormat);
|
||||
|
||||
layout->elementAt(row, 0)->addWidget(label);
|
||||
layout->elementAt(row, 1)->addWidget(_outputFormat);
|
||||
|
||||
_outputFormatModel = new Wt::WStringListModel(_outputFormat);
|
||||
|
||||
std::vector<Format> formats = Format::get( Format::Video );
|
||||
|
||||
for(std::size_t idFormat = 0; idFormat < formats.size(); ++idFormat)
|
||||
{
|
||||
_outputFormatModel->addString(formats[idFormat].getDesc());
|
||||
_outputFormatModel->setData(idFormat, 0, formats[idFormat].getEncoding(), Wt::UserRole);
|
||||
}
|
||||
|
||||
_outputFormat->setModel(_outputFormatModel);
|
||||
|
||||
row++;
|
||||
}
|
||||
|
||||
createStreamWidgets("Video", Stream::Video, layout);
|
||||
createStreamWidgets("Audio", Stream::Audio, layout);
|
||||
createStreamWidgets("Subtitles", Stream::Subtitle, layout);
|
||||
|
||||
Wt::WPushButton *ok = new Wt::WPushButton("Apply", contents());
|
||||
ok->clicked().connect(this, &Wt::WDialog::accept);
|
||||
|
||||
Wt::WPushButton *cancel = new Wt::WPushButton("Cancel", contents());
|
||||
cancel->clicked().connect(this, &Wt::WDialog::reject);
|
||||
}
|
||||
|
||||
void
|
||||
VideoParametersDialog::createStreamWidgets(const Wt::WString& labelString, Transcode::Stream::Type type, Wt::WTable* layout)
|
||||
{
|
||||
int row = layout->rowCount();
|
||||
|
||||
Wt::WLabel* label = new Wt::WLabel(labelString);
|
||||
Wt::WComboBox *combo = new Wt::WComboBox();
|
||||
label->setBuddy(combo);
|
||||
|
||||
layout->elementAt(row, 0)->addWidget(label);
|
||||
layout->elementAt(row, 1)->addWidget(combo);
|
||||
|
||||
Wt::WStringListModel* model = new Wt::WStringListModel(combo);
|
||||
|
||||
combo->setModel(model);
|
||||
|
||||
_streamSelection.insert( std::make_pair(type, std::make_pair(combo, model) ) );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoParametersDialog::handleApply()
|
||||
{
|
||||
_apply.emit();
|
||||
}
|
||||
|
||||
void
|
||||
VideoParametersDialog::addStreams(Wt::WStringListModel* model, const std::vector<Stream>& streams)
|
||||
{
|
||||
for (std::size_t idStream = 0; idStream < streams.size(); ++idStream)
|
||||
{
|
||||
const Stream& stream = streams[idStream];
|
||||
|
||||
std::ostringstream oss;
|
||||
if (!stream.getLanguage().empty())
|
||||
oss << "[" << stream.getLanguage() << "] ";
|
||||
|
||||
oss << stream.getDesc();
|
||||
|
||||
model->addString(oss.str());
|
||||
model->setData(idStream, 0, stream.getId(), Wt::UserRole);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VideoParametersDialog::selectStream(const Wt::WStringListModel* model, Stream::Id streamId, Wt::WComboBox* combo)
|
||||
{
|
||||
for (int idStream = 0; idStream < model->rowCount(); ++idStream)
|
||||
{
|
||||
Stream::Id id = boost::any_cast<Stream::Id>( model->data( model->index(idStream, 0), Wt::UserRole));
|
||||
if (id == streamId)
|
||||
{
|
||||
combo->setCurrentIndex(idStream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoParametersDialog::load(const Transcode::Parameters& parameters)
|
||||
{
|
||||
// Select proper encoding
|
||||
for (int row = 0; row < _outputFormat->count(); ++row)
|
||||
{
|
||||
Format::Encoding encoding = boost::any_cast<Format::Encoding>( _outputFormatModel->data(_outputFormatModel->index(row,0), Wt::UserRole));
|
||||
if (encoding == parameters.getOutputFormat().getEncoding()) {
|
||||
_outputFormat->setCurrentIndex(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current selected input streams
|
||||
Parameters::StreamMap streamMap = parameters.getInputStreams();;
|
||||
|
||||
// Populate the combox with the available streams
|
||||
// And then show the selected one
|
||||
BOOST_FOREACH(Stream::Type streamType, streamTypes)
|
||||
{
|
||||
Wt::WComboBox* combo = _streamSelection[streamType].first;
|
||||
Wt::WStringListModel* model = _streamSelection[streamType].second;
|
||||
|
||||
addStreams(model, parameters.getInputMediaFile().getStreams( streamType ) );
|
||||
|
||||
selectStream(model, streamMap[streamType], combo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
VideoParametersDialog::save(Transcode::Parameters& parameters)
|
||||
{
|
||||
std::cout << "Grabbing user input!" << std::endl;
|
||||
|
||||
// Get encoder used
|
||||
Format::Encoding encoding = boost::any_cast<Format::Encoding>( _outputFormatModel->data(_outputFormatModel->index(_outputFormat->currentIndex(), 0), Wt::UserRole));
|
||||
parameters.setOutputFormat( Format::get(encoding) );
|
||||
|
||||
// Get stream selected, if any
|
||||
BOOST_FOREACH(Stream::Type streamType, streamTypes)
|
||||
{
|
||||
Wt::WComboBox* combo = _streamSelection[streamType].first;
|
||||
Wt::WStringListModel* model = _streamSelection[streamType].second;
|
||||
|
||||
if (combo->currentIndex() >= 0)
|
||||
{
|
||||
Stream::Id streamId = boost::any_cast<Stream::Id>( model->data(model->index(combo->currentIndex(), 0), Wt::UserRole));
|
||||
|
||||
parameters.selectInputStream(streamType, streamId);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef VIDEO_PARAMETER_DIALOG
|
||||
#define VIDEO_PARAMETER_DIALOG
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <Wt/WSignal>
|
||||
#include <Wt/WDialog>
|
||||
#include <Wt/WComboBox>
|
||||
#include <Wt/WStringListModel>
|
||||
#include <Wt/WString>
|
||||
|
||||
#include "transcode/Parameters.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class VideoParametersDialog : public Wt::WDialog
|
||||
{
|
||||
public:
|
||||
// parameters to be edited
|
||||
VideoParametersDialog(const Wt::WString &windowTitle, Wt::WDialog* parent = 0);
|
||||
|
||||
// Populates widget contents using these paramaters
|
||||
void load(const Transcode::Parameters& parameters);
|
||||
|
||||
// Save widgets contents into these parameters
|
||||
void save(Transcode::Parameters& parameters);
|
||||
|
||||
// Signal to be emitted if parameters are changed
|
||||
Wt::Signal<void>& apply() { return _apply; }
|
||||
|
||||
private:
|
||||
|
||||
void handleApply(void);
|
||||
|
||||
// Stream handling
|
||||
void createStreamWidgets(const Wt::WString& label, Transcode::Stream::Type type, Wt::WTable* layout);
|
||||
void addStreams(Wt::WStringListModel* model, const std::vector<Transcode::Stream>& streams);
|
||||
void selectStream(const Wt::WStringListModel* model, Transcode::Stream::Id streamId, Wt::WComboBox* combo);
|
||||
|
||||
Wt::Signal<void> _apply;
|
||||
|
||||
Wt::WComboBox* _outputFormat;
|
||||
Wt::WStringListModel* _outputFormatModel;
|
||||
|
||||
std::map<Transcode::Stream::Type, std::pair<Wt::WComboBox*, Wt::WStringListModel* > > _streamSelection;
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Wt/WApplication>
|
||||
#include <Wt/WEnvironment>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
|
||||
#include "VideoWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
VideoWidget::VideoWidget(SessionData& sessionData, Wt::WContainerWidget* parent )
|
||||
: Wt::WContainerWidget(parent),
|
||||
_sessionData(sessionData)
|
||||
{
|
||||
|
||||
_videoDbWidget = new VideoDatabaseWidget(_sessionData.getDatabaseHandler(), this);
|
||||
|
||||
_videoDbWidget->playVideo().connect(this, &VideoWidget::playVideo);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoWidget::search(const std::string& searchText)
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
|
||||
void
|
||||
VideoWidget::backToList(void)
|
||||
{
|
||||
if (_mediaPlayer)
|
||||
delete _mediaPlayer;
|
||||
|
||||
_videoDbWidget->setHidden(false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VideoWidget::playVideo(boost::filesystem::path p)
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Want to play video " << p << "'" << std::endl;
|
||||
try {
|
||||
|
||||
std::size_t audioBitrate = 0;
|
||||
std::size_t videoBitrate = 0;
|
||||
|
||||
// Get user preferences
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(_sessionData.getDatabaseHandler().getSession());
|
||||
Database::User::pointer user = _sessionData.getDatabaseHandler().getCurrentUser();
|
||||
if (user)
|
||||
{
|
||||
audioBitrate = user->getMaxAudioBitrate();
|
||||
videoBitrate = user->getMaxVideoBitrate();
|
||||
}
|
||||
else
|
||||
{
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Can't play video: user does not exists!";
|
||||
return; // TODO logout?
|
||||
}
|
||||
}
|
||||
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Max bitrate set to " << videoBitrate << "/" << audioBitrate;
|
||||
|
||||
Transcode::InputMediaFile inputFile(p);
|
||||
|
||||
Transcode::Format::Encoding encoding;
|
||||
|
||||
if (Wt::WApplication::instance()->environment().agentIsChrome())
|
||||
encoding = Transcode::Format::WEBMV;
|
||||
else
|
||||
encoding = Transcode::Format::FLV;
|
||||
|
||||
Transcode::Parameters parameters(inputFile, Transcode::Format::get(encoding));
|
||||
|
||||
// TODO, make a quality button in order to choose...
|
||||
|
||||
parameters.setBitrate(Transcode::Stream::Audio, 0/*audioBitrate*/);
|
||||
parameters.setBitrate(Transcode::Stream::Video, 0/*videoBitrate*/);
|
||||
|
||||
_mediaPlayer = new VideoMediaPlayerWidget(parameters, this);
|
||||
_mediaPlayer->close().connect(this, &VideoWidget::backToList);
|
||||
|
||||
_videoDbWidget->setHidden(true);
|
||||
}
|
||||
catch( std::exception& e) {
|
||||
LMS_LOG(MOD_UI, SEV_ERROR) << "Caught exception while loading '" << p << "': " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef VIDEO_WIDGET_HPP
|
||||
#define VIDEO_WIDGET_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <Wt/WContainerWidget>
|
||||
|
||||
#include "common/SessionData.hpp"
|
||||
|
||||
#include "video/VideoMediaPlayerWidget.hpp"
|
||||
#include "video/VideoDatabaseWidget.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
class VideoWidget : public Wt::WContainerWidget
|
||||
{
|
||||
public:
|
||||
|
||||
VideoWidget(SessionData& sessionData, Wt::WContainerWidget* parent = 0);
|
||||
|
||||
void search(const std::string& searchText);
|
||||
|
||||
private:
|
||||
|
||||
void backToList(void);
|
||||
void playVideo(boost::filesystem::path p);
|
||||
|
||||
SessionData& _sessionData;
|
||||
|
||||
VideoDatabaseWidget* _videoDbWidget;
|
||||
VideoMediaPlayerWidget* _mediaPlayer;
|
||||
|
||||
};
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user