Added database settings view

This commit is contained in:
emeric
2018-03-03 17:23:28 +01:00
parent 4b20f713f3
commit 529055d432
11 changed files with 506 additions and 31 deletions
+21 -4
View File
@@ -22,6 +22,7 @@
#include <Wt/WNavigationBar>
#include <Wt/WStackedWidget>
#include <Wt/WMenu>
#include <Wt/WPopupMenu>
#include <Wt/WText>
#include "config/config.h"
@@ -34,6 +35,8 @@
#include "MediaPlayer.hpp"
#include "PlaylistView.hpp"
#include "settings/DatabaseView.hpp"
#include "LmsApplication.hpp"
namespace skeletons {
@@ -86,6 +89,7 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnecti
messageResourceBundle().use(appRoot() + "playlist");
messageResourceBundle().use(appRoot() + "release");
messageResourceBundle().use(appRoot() + "releases");
messageResourceBundle().use(appRoot() + "settings");
messageResourceBundle().use(appRoot() + "tracks");
messageResourceBundle().use(appRoot() + "templates");
@@ -179,7 +183,7 @@ enum IdxRoot
IdxHome = 0,
IdxExplore,
IdxPlaylist,
IdxSettings,
IdxSettingsDatabase,
};
@@ -196,7 +200,7 @@ handlePathChange(Wt::WStackedWidget* stack)
{ "/release", IdxExplore },
{ "/tracks", IdxExplore },
{ "/playlist", IdxPlaylist },
{ "/settings", IdxSettings },
{ "/settings/database", IdxSettingsDatabase },
};
LMS_LOG(UI, DEBUG) << "Internal path changed to '" << wApp->internalPath() << "'";
@@ -237,7 +241,6 @@ LmsApplication::handleAuthEvent(void)
auto navbar = new Wt::WNavigationBar();
navbar->setTitle("LMS", Wt::WLink(Wt::WLink::InternalPath, "/home"));
navbar->setResponsive(true);
// navbar->setStyleClass("main-nav");
main->bindWidget("navbar-top", navbar);
@@ -262,6 +265,18 @@ LmsApplication::handleAuthEvent(void)
menuItem->setLink(Wt::WLink(Wt::WLink::InternalPath, "/playlist"));
menuItem->setSelectable(false);
}
{
auto menuItem = menu->insertItem(4, Wt::WString::tr("msg-settings"));
menuItem->setSelectable(false);
Wt::WPopupMenu *settings = new Wt::WPopupMenu();
auto dbSettings = settings->insertItem(0, Wt::WString::tr("msg-settings-database"));
dbSettings->setLink(Wt::WLink(Wt::WLink::InternalPath, "/settings/database"));
dbSettings->setSelectable(false);
menuItem->setMenu(settings);
}
navbar->addMenu(menu);
// Contents
@@ -275,7 +290,9 @@ LmsApplication::handleAuthEvent(void)
auto playlist = new Playlist();
mainStack->addWidget(playlist);
mainStack->addWidget(new Wt::WText("SETTINGS"));
auto databaseSettings = new Settings::DatabaseView();
mainStack->addWidget(databaseSettings);
explore->tracksAdd.connect(std::bind([=] (std::vector<Database::Track::pointer> tracks)
{
+3 -2
View File
@@ -38,13 +38,14 @@ DirectoryValidator::validate(const Wt::WString& input) const
boost::filesystem::path p(input.toUTF8());
boost::system::error_code ec;
// TODO check rights
bool res = boost::filesystem::is_directory(p, ec);
if (ec)
return Wt::WValidator::Result(Wt::WValidator::Invalid, ec.message());
return Wt::WValidator::Result(Wt::WValidator::Invalid, ec.message()); // TODO translate common errors
else if (res)
return Wt::WValidator::Result(Wt::WValidator::Valid);
else
return Wt::WValidator::Result(Wt::WValidator::Invalid, "Not a directory");
return Wt::WValidator::Result(Wt::WValidator::Invalid, Wt::WString::tr("msg-error-not-a-directory"));
}
+298
View File
@@ -0,0 +1,298 @@
/*
* Copyright (C) 2018 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WString>
#include <Wt/WPushButton>
#include <Wt/WComboBox>
#include <Wt/WMessageBox>
#include <Wt/WLineEdit>
#include <Wt/WFormModel>
#include <Wt/WStringListModel>
#include "common/DirectoryValidator.hpp"
#include "database/MediaDirectory.hpp"
#include "scanner/MediaScanner.hpp"
#include "utils/Logger.hpp"
#include "LmsApplication.hpp"
#include "DatabaseView.hpp"
namespace UserInterface {
namespace Settings {
using namespace Database;
class DatabaseModel : public Wt::WFormModel
{
public:
// Associate each field with a unique string literal.
static const Field MediaDirectoryField;
static const Field UpdatePeriodField;
static const Field UpdateStartTimeField;
DatabaseModel(Wt::WObject *parent = 0)
: Wt::WFormModel(parent)
{
initializeModels();
addField(MediaDirectoryField);
addField(UpdatePeriodField);
addField(UpdateStartTimeField);
DirectoryValidator* dirValidator = new DirectoryValidator();
dirValidator->setMandatory(true);
setValidator(MediaDirectoryField, dirValidator);
setValidator(UpdatePeriodField, createMandatoryValidator());
setValidator(UpdateStartTimeField, createMandatoryValidator());
// populate the model with initial data
loadData();
}
Wt::WAbstractItemModel *updatePeriodModel() { return _updatePeriodModel; }
Wt::WAbstractItemModel *updateStartTimeModel() { return _updateStartTimeModel; }
void loadData()
{
using namespace Database;
Wt::Dbo::Transaction transaction(DboSession());
std::vector<MediaDirectory::pointer> mediaDirectories = MediaDirectory::getAll(DboSession());
if (!mediaDirectories.empty())
setValue(MediaDirectoryField, mediaDirectories.front()->getPath().string());
auto periodRow = getUpdatePeriodModelRow( Scanner::getUpdatePeriod(DboSession()) );
if (periodRow)
setValue(UpdatePeriodField, updatePeriodString(*periodRow));
auto startTimeRow = getUpdateStartTimeModelRow( Scanner::getUpdateStartTime(DboSession()) );
if (startTimeRow)
setValue(UpdateStartTimeField, updateStartTimeString(*startTimeRow) );
}
void saveData()
{
Wt::Dbo::Transaction transaction(DboSession());
MediaDirectory::eraseAll(DboSession());
MediaDirectory::create(DboSession(), boost::any_cast<Wt::WString>(value(MediaDirectoryField)).toUTF8());
auto updatePeriodRow = getUpdatePeriodModelRow( boost::any_cast<Wt::WString>(value(UpdatePeriodField)));
assert(updatePeriodRow);
Scanner::setUpdatePeriod(DboSession(), updatePeriod(*updatePeriodRow));
auto startTimeRow = getUpdateStartTimeModelRow( boost::any_cast<Wt::WString>(value(UpdateStartTimeField)));
assert(startTimeRow);
Scanner::setUpdateStartTime(DboSession(), updateStartTime(*startTimeRow));
}
boost::optional<int> getUpdatePeriodModelRow(Wt::WString value)
{
for (int i = 0; i < _updatePeriodModel->rowCount(); ++i)
{
if (updatePeriodString(i) == value)
return i;
}
return boost::none;
}
boost::optional<int> getUpdatePeriodModelRow(Scanner::UpdatePeriod period)
{
for (int i = 0; i < _updatePeriodModel->rowCount(); ++i)
{
if (updatePeriod(i) == period)
return i;
}
return boost::none;
}
Scanner::UpdatePeriod updatePeriod(int row)
{
return boost::any_cast<Scanner::UpdatePeriod>
(_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::UserRole));
}
Wt::WString updatePeriodString(int row)
{
return boost::any_cast<Wt::WString>
(_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::DisplayRole));
}
boost::optional<int> getUpdateStartTimeModelRow(Wt::WString value)
{
for (int i = 0; i < _updateStartTimeModel->rowCount(); ++i)
{
if (updateStartTimeString(i) == value)
return i;
}
return boost::none;
}
boost::optional<int> getUpdateStartTimeModelRow(boost::posix_time::time_duration startTime)
{
for (int i = 0; i < _updateStartTimeModel->rowCount(); ++i)
{
if (updateStartTime(i) == startTime)
return i;
}
return boost::none;
}
boost::posix_time::time_duration updateStartTime(int row)
{
return boost::any_cast<boost::posix_time::time_duration>
(_updateStartTimeModel->data(_updateStartTimeModel->index(row, 0), Wt::UserRole));
}
Wt::WString updateStartTimeString(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(Wt::WString::tr("msg-update-period-never"));
_updatePeriodModel->setData(0, 0, Scanner::UpdatePeriod::Never, Wt::UserRole);
_updatePeriodModel->addString(Wt::WString::tr("msg-update-period-daily"));
_updatePeriodModel->setData(1, 0, Scanner::UpdatePeriod::Daily, Wt::UserRole);
_updatePeriodModel->addString(Wt::WString::tr("msg-update-period-weekly"));
_updatePeriodModel->setData(2, 0, Scanner::UpdatePeriod::Weekly, Wt::UserRole);
_updatePeriodModel->addString(Wt::WString::tr("msg-update-period-monthly"));
_updatePeriodModel->setData(3, 0, Scanner::UpdatePeriod::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 *createMandatoryValidator()
{
Wt::WValidator* v = new Wt::WValidator();
v->setMandatory(true);
return v;
}
Wt::WStringListModel* _updatePeriodModel;
Wt::WStringListModel* _updateStartTimeModel;
};
const Wt::WFormModel::Field DatabaseModel::MediaDirectoryField = "media-directory";
const Wt::WFormModel::Field DatabaseModel::UpdatePeriodField = "update-period";
const Wt::WFormModel::Field DatabaseModel::UpdateStartTimeField = "update-start-time";
DatabaseView::DatabaseView(Wt::WContainerWidget *parent)
: Wt::WTemplateFormView(parent)
{
_model = new DatabaseModel(this);
setTemplateText(tr("template-settings-database"));
addFunction("tr", &WTemplate::Functions::tr);
addFunction("id", &WTemplate::Functions::id);
// Media Directory
Wt::WLineEdit *mediaDirectoryEdit = new Wt::WLineEdit();
setFormWidget(DatabaseModel::MediaDirectoryField, mediaDirectoryEdit);
// Update Period
Wt::WComboBox *updatePeriodCB = new Wt::WComboBox();
setFormWidget(DatabaseModel::UpdatePeriodField, updatePeriodCB);
updatePeriodCB->setModel(_model->updatePeriodModel());
// Update Start Time
Wt::WComboBox *updateStartTimeCB = new Wt::WComboBox();
setFormWidget(DatabaseModel::UpdateStartTimeField, updateStartTimeCB);
updateStartTimeCB->setModel(_model->updateStartTimeModel());
// Buttons
Wt::WPushButton *saveBtn = new Wt::WPushButton(Wt::WString::tr("msg-btn-apply"));
bindWidget("apply-btn", saveBtn);
saveBtn->setStyleClass("btn-primary");
Wt::WPushButton *discardBtn = new Wt::WPushButton(Wt::WString::tr("msg-btn-discard"));
bindWidget("discard-btn", discardBtn);
saveBtn->clicked().connect(this, &DatabaseView::processSave);
discardBtn->clicked().connect(this, &DatabaseView::processDiscard);
updateView(_model);
}
void
DatabaseView::processDiscard()
{
_model->loadData();
_model->validate();
updateView(_model);
}
void
DatabaseView::processSave()
{
updateModel(_model);
if (_model->validate()) {
_model->saveData();
// _sigChanged.emit();
}
// Udate the view: Delete any validation message in the view, etc.
updateView(_model);
}
} // namespace Settings
} // namespace UserInterface
+54
View File
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2018 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WTemplateFormView>
#include <Wt/WText>
#include <Wt/WSignal>
namespace UserInterface {
namespace Settings {
class DatabaseModel;
class DatabaseView : public Wt::WTemplateFormView
{
public:
DatabaseView(Wt::WContainerWidget *parent = 0);
Wt::Signal<void>& changed() { return _sigChanged; }
private:
Wt::Signal<void> _sigChanged;
void processSave();
void processDiscard();
void processImmediateScan();
Wt::WText *_applyInfo;
DatabaseModel *_model;
};
} // namespace Settings
} // namespace UserInterface