WIP. Switching to the WTemplateFormView/WFormModel for settings

This commit is contained in:
emeric
2014-07-23 12:58:57 +02:00
parent 3a5126f339
commit d4c0806f7a
20 changed files with 1533 additions and 1573 deletions
+2 -2
View File
@@ -2,7 +2,7 @@
#include <Wt/WStackedWidget>
#include <Wt/WTextArea>
#include "SettingsDatabase.hpp"
#include "SettingsDatabaseFormView.hpp"
#include "Settings.hpp"
@@ -30,7 +30,7 @@ _sessionData(sessionData)
if (user->isAdmin())
{
// TODO Special admin settings
menu->addItem("Database", new Database(sessionData));
menu->addItem("Database", new DatabaseFormView(sessionData));
menu->addItem("Users", new Wt::WTextArea("Users here!"));
}
-328
View File
@@ -1,328 +0,0 @@
#include <map>
#include <Wt/WTable>
#include <Wt/WGroupBox>
#include <Wt/WText>
#include <Wt/WLineEdit>
#include <Wt/WString>
#include <Wt/WPushButton>
#include <Wt/WCheckBox>
#include <Wt/WBreak>
#include <Wt/WMessageBox>
#include "database/MediaDirectory.hpp"
#include "service/ServiceManager.hpp"
#include "service/DatabaseUpdateService.hpp"
#include "common/DirectoryValidator.hpp"
#include "SettingsDatabase.hpp"
namespace UserInterface {
namespace Settings {
Database::Database(SessionData& sessionData, Wt::WContainerWidget *parent)
: Wt::WContainerWidget(parent),
_sessionData(sessionData)
{
// Media directories
{
Wt::WGroupBox *container = new Wt::WGroupBox("Media directories", this);
_table = new Wt::WTable(container);
_table->addStyleClass("table form-inline");
_table->toggleStyleClass("table-hover", true);
_table->toggleStyleClass("table-striped", true);
_table->setHeaderCount(1);
_table->elementAt(0, 0)->addWidget(new Wt::WText("Path"));
_table->elementAt(0, 1)->addWidget(new Wt::WText("Status"));
_table->elementAt(0, 2)->addWidget(new Wt::WText("Delete"));
{
Wt::WPushButton* btn = new Wt::WPushButton("Add");
container->addWidget(btn);
btn->clicked().connect(this, &Database::handleAddDirectory);
}
{
Wt::WPushButton* btn = new Wt::WPushButton("Delete");
container->addWidget(btn);
btn->clicked().connect(this, &Database::handleDelDirectory);
}
}
addWidget(new Wt::WBreak());
// Refresh settings
{
Wt::WGroupBox *container = new Wt::WGroupBox("Refresh settings", this);
Wt::WTable *table = new Wt::WTable(container);
table->addStyleClass("table form-inline");
table->setHeaderCount(1, Wt::Vertical);
std::size_t line = 0;
table->elementAt(line, 0)->addWidget(new Wt::WText("Update period"));
table->elementAt(line, 1)->addWidget(_updatePeriod = new Wt::WComboBox() );
// Populate combo
_updatePeriodModel = new Wt::WStringListModel(_updatePeriod);
_updatePeriodModel->addString("Never");
_updatePeriodModel->setData(0, 0, boost::posix_time::time_duration( boost::posix_time::hours(0) ), Wt::UserRole);
_updatePeriodModel->addString("Daily");
_updatePeriodModel->setData(1, 0, boost::posix_time::time_duration( boost::posix_time::hours(24) ), Wt::UserRole);
_updatePeriodModel->addString("Weekly");
_updatePeriodModel->setData(2, 0, boost::posix_time::time_duration( boost::posix_time::hours(24*7) ), Wt::UserRole);
_updatePeriodModel->addString("Monthly");
_updatePeriodModel->setData(3, 0, boost::posix_time::time_duration(boost::posix_time::hours(24*30) ), Wt::UserRole);
_updatePeriod->setModel(_updatePeriodModel);
line++;
table->elementAt(line, 0)->addWidget(new Wt::WText("Update start time"));
table->elementAt(line, 1)->addWidget(_updateStartTime = new Wt::WComboBox( ));
// populate combo
_updateStartTimeModel = new Wt::WStringListModel(_updateStartTime);
for (std::size_t i = 0; i < 24; ++i)
{
boost::posix_time::time_duration dur = boost::posix_time::hours(i);
boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%H:%M");
facet->time_duration_format("%H:%M");
std::ostringstream oss;
oss.imbue(std::locale(oss.getloc(), facet));
oss << dur;
_updateStartTimeModel->addString( oss.str() );
_updateStartTimeModel->setData(i, 0, dur, Wt::UserRole);
}
_updateStartTime->setModel(_updateStartTimeModel);
line++;
Wt::WPushButton* btn = new Wt::WPushButton("Scan now", container);
btn->clicked().connect(this, &Database::handleScanNow);
}
addWidget(new Wt::WBreak());
addWidget(new Wt::WBreak());
{
Wt::WPushButton* btn = new Wt::WPushButton("Apply", this);
btn->clicked().connect(this, &Database::handleApply);
}
{
Wt::WPushButton* btn = new Wt::WPushButton("Discard", this);
btn->clicked().connect(this, &Database::loadSettings);
}
loadSettings();
}
void
Database::addDirectory(const std::string& path)
{
int line = _table->rowCount();
Wt::WLineEdit* lineEdit = new Wt::WLineEdit( path );
_table->elementAt(line, 0)->addWidget( lineEdit );
DirectoryValidator* validator = new DirectoryValidator(true);
lineEdit->setValidator(validator);
std::string status;
if (!path.empty())
status = validator->validate( path ).message().toUTF8();
Wt::WText* statusText = new Wt::WText( status );
_table->elementAt(line, 1)->addWidget( statusText );
_table->elementAt(line, 2)->addWidget( new Wt::WCheckBox() );
lineEdit->blurred().connect(boost::bind(&Database::handleCheckDirectory, this, lineEdit, statusText));
}
void
Database::loadSettings(void)
{
Wt::Dbo::Transaction transaction(_sessionData.getDatabaseHandler().getSession());
// Get directories
std::vector<::Database::MediaDirectory::pointer> directories = ::Database::MediaDirectory::getAll(_sessionData.getDatabaseHandler().getSession());
// delete rows
assert(_table->rowCount() > 0 );
for (int i = _table->rowCount() - 1; i > 0; --i)
_table->deleteRow(i);
BOOST_FOREACH(::Database::MediaDirectory::pointer directory, directories)
addDirectory( directory->getPath().string() );
// Get refresh settings
::Database::MediaDirectorySettings::pointer settings = ::Database::MediaDirectorySettings::get(_sessionData.getDatabaseHandler().getSession());
{
int i;
for (i = 0; i < _updatePeriodModel->rowCount(); ++i)
{
if (boost::any_cast<boost::posix_time::time_duration>
(_updatePeriodModel->data(_updatePeriodModel->index(i, 0), Wt::UserRole))
>= settings->getUpdatePeriod())
break;
}
_updatePeriod->setCurrentIndex(i);
}
{
int i;
for (i = 0; i < _updateStartTimeModel->rowCount(); ++i)
{
if (boost::any_cast<boost::posix_time::time_duration>
(_updateStartTimeModel->data(_updateStartTimeModel->index(i, 0), Wt::UserRole))
>= settings->getUpdateStartTime())
break;
}
_updateStartTime->setCurrentIndex(i);
}
}
void
Database::handleAddDirectory(void)
{
// TODO check if the previous entry is empty too
addDirectory();
}
void
Database::handleDelDirectory()
{
assert(_table->rowCount() > 0 );
for (int i = _table->rowCount() - 1; i > 0; --i)
{
assert(_table->elementAt(i, 2)->count() == 1);
Wt::WCheckBox *checkBox = dynamic_cast<Wt::WCheckBox*>(_table->elementAt(i, 2)->widget(0));
if (checkBox->checkState() == Wt::CheckState::Checked)
_table->deleteRow(i);
}
}
void
Database::handleCheckDirectory(Wt::WLineEdit* edit, Wt::WText* status)
{
Wt::WValidator* validator = edit->validator();
if (validator)
status->setText( validator->validate( edit->text().toUTF8() ).message().toUTF8());
}
bool
Database::saveSettings(void)
{
Wt::Dbo::Transaction transaction(_sessionData.getDatabaseHandler().getSession());
assert(_table->rowCount() > 0 );
// Validate each directory in the table
for (int i = 1; i < _table->rowCount(); ++i)
{
assert(_table->elementAt(i, 0)->count() == 1);
Wt::WLineEdit* lineEdit = dynamic_cast<Wt::WLineEdit*>(_table->elementAt(i, 0)->widget(0));
if (!lineEdit->validate())
return false;;
}
// erase all directories and add from the table
::Database::MediaDirectory::eraseAll( _sessionData.getDatabaseHandler().getSession() );
for (int i = 1; i < _table->rowCount(); ++i)
{
Wt::WLineEdit* lineEdit = dynamic_cast<Wt::WLineEdit*>(_table->elementAt(i, 0)->widget(0));
::Database::MediaDirectory::create( _sessionData.getDatabaseHandler().getSession(), lineEdit->text().toUTF8(), ::Database::MediaDirectory::Audio);
}
::Database::MediaDirectorySettings::pointer settings = ::Database::MediaDirectorySettings::get(_sessionData.getDatabaseHandler().getSession() );
{
int row = _updateStartTime->currentIndex();
settings.modify()->setUpdateStartTime( boost::any_cast<boost::posix_time::time_duration>( _updateStartTimeModel->data(_updateStartTimeModel->index(row, 0), Wt::UserRole)));
}
{
int row = _updatePeriod->currentIndex();
settings.modify()->setUpdatePeriod( boost::any_cast<boost::posix_time::time_duration>( _updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::UserRole)));
}
return true;
}
void
Database::handleApply(void)
{
if (saveSettings())
{
boost::lock_guard<boost::mutex> serviceLock (ServiceManager::instance().mutex());
DatabaseUpdateService::pointer service = ServiceManager::instance().getService<DatabaseUpdateService>();
if (service)
service->restart();
// Show a popup to say it's fine!
Wt::WMessageBox *messageBox = new Wt::WMessageBox
("Status",
"New settings applied!",
Wt::Information, Wt::Ok);
messageBox->setModal(true);
messageBox->buttonClicked().connect(std::bind([=] ()
{
delete messageBox;
}
));
messageBox->show();
}
else
{
Wt::WMessageBox *messageBox = new Wt::WMessageBox
("Error",
"Cannot apply settings",
Wt::Critical, Wt::Ok);
messageBox->setModal(true);
messageBox->buttonClicked().connect(std::bind([=] ()
{
delete messageBox;
}
));
messageBox->show();
}
}
void
Database::handleScanNow(void)
{
}
} // namespace Settings
} // namespace UserInterface
-50
View File
@@ -1,50 +0,0 @@
#ifndef UI_SETTINGS_DB_HPP
#define UI_SETTINGS_DB_HPP
#include <Wt/WContainerWidget>
#include <Wt/WTable>
#include <Wt/WComboBox>
#include <Wt/WLineEdit>
#include <Wt/WStringListModel>
#include "common/SessionData.hpp"
namespace UserInterface {
namespace Settings {
class Database : public Wt::WContainerWidget
{
public:
Database(SessionData& sessionData, Wt::WContainerWidget *parent = 0);
private:
void handleApply();
void handleCheckDirectory(Wt::WLineEdit* edit, Wt::WText* status);
void handleAddDirectory();
void handleDelDirectory();
void handleScanNow(void);
void loadSettings(void); // load from db
bool saveSettings(void); // save into db
void addDirectory(const std::string& path = "");
SessionData& _sessionData;
Wt::WTable* _table;
Wt::WComboBox* _updatePeriod;
Wt::WStringListModel* _updatePeriodModel;
Wt::WComboBox* _updateStartTime;
Wt::WStringListModel* _updateStartTimeModel;
};
} // namespace Settings
} // namespace UserInterface
#endif
+343
View File
@@ -0,0 +1,343 @@
#include <Wt/WLineEdit>
#include <Wt/WString>
#include <Wt/WPushButton>
#include <Wt/WCheckBox>
#include <Wt/WComboBox>
#include <Wt/WBreak>
#include <Wt/WFormModel>
#include <Wt/WStringListModel>
#include "database/MediaDirectory.hpp"
#include "service/ServiceManager.hpp"
#include "service/DatabaseUpdateService.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 PathField;
static const Field UpdatePeriodField;
static const Field UpdateStartTimeField;
static const Field UpdateRequestImmediateField;
DatabaseFormModel(SessionData& sessionData, Wt::WObject *parent = 0)
: Wt::WFormModel(parent),
_sessionData(sessionData)
{
initializeModels();
addField(PathField);
addField(UpdatePeriodField);
addField(UpdateStartTimeField);
addField(UpdateRequestImmediateField);
setValidator(PathField, createPathValidator(PathField));
setValidator(UpdatePeriodField, createUpdatePeriodValidator(UpdatePeriodField));
setValidator(UpdateStartTimeField, createStartTimeValidator(UpdateStartTimeField));
setValidator(UpdateRequestImmediateField, createRequestImmediateFieldValidator(UpdateRequestImmediateField));
// 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 directories
std::vector<::Database::MediaDirectory::pointer> directories = ::Database::MediaDirectory::getAll(_sessionData.getDatabaseHandler().getSession());
BOOST_FOREACH(::Database::MediaDirectory::pointer directory, directories)
{
setValue(PathField, directory->getPath().string() );
// TODO, handle multiple directories
break;
}
// 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 ) );
setValue(UpdateRequestImmediateField, settings->getManualScanRequested() );
}
void saveData()
{
Wt::Dbo::Session& session( _sessionData.getDatabaseHandler().getSession());
Wt::Dbo::Transaction transaction(session);
::Database::MediaDirectory::eraseAll( session );
::Database::MediaDirectory::create(session, valueText(PathField).toUTF8(), ::Database::MediaDirectory::Audio);
::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 ) );
settings.modify()->setManualScanRequested( boost::any_cast<bool>(value(UpdateRequestImmediateField )) );
}
int getUpdatePeriodModelRow(Wt::WString value)
{
for (int i = 0; i < _updatePeriodModel->rowCount(); ++i)
{
if (updatePeriod(i) == value)
return i;
}
return -1;
}
int getUpdatePeriodModelRow(boost::posix_time::time_duration duration)
{
for (int i = 0; i < _updatePeriodModel->rowCount(); ++i)
{
if (updatePeriodDuration(i) == duration)
return i;
}
return -1;
}
boost::posix_time::time_duration updatePeriodDuration(int row) {
return boost::any_cast<boost::posix_time::time_duration>
(_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, boost::posix_time::time_duration( boost::posix_time::hours(0) ), Wt::UserRole);
_updatePeriodModel->addString("Daily");
_updatePeriodModel->setData(1, 0, boost::posix_time::time_duration( boost::posix_time::hours(24) ), Wt::UserRole);
_updatePeriodModel->addString("Weekly");
_updatePeriodModel->setData(2, 0, boost::posix_time::time_duration( boost::posix_time::hours(24*7) ), Wt::UserRole);
_updatePeriodModel->addString("Monthly");
_updatePeriodModel->setData(3, 0, boost::posix_time::time_duration(boost::posix_time::hours(24*30) ), Wt::UserRole);
_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 *createPathValidator(const std::string& field) {
DirectoryValidator* v = new DirectoryValidator();
v->setMandatory(true);
return v;
}
Wt::WValidator *createUpdatePeriodValidator(const std::string& field) {
Wt::WValidator* v = new Wt::WValidator();
v->setMandatory(true);
return v;
}
Wt::WValidator *createStartTimeValidator(const std::string& field) {
Wt::WValidator* v = new Wt::WValidator();
v->setMandatory(true);
return v;
}
Wt::WValidator *createRequestImmediateFieldValidator(const std::string& field) {
Wt::WValidator* v = new Wt::WValidator();
return v;
}
SessionData& _sessionData;
Wt::WStringListModel* _updatePeriodModel;
Wt::WStringListModel* _updateStartTimeModel;
};
const Wt::WFormModel::Field DatabaseFormModel::PathField = "path";
const Wt::WFormModel::Field DatabaseFormModel::UpdatePeriodField = "update-period";
const Wt::WFormModel::Field DatabaseFormModel::UpdateStartTimeField = "update-start-time";
const Wt::WFormModel::Field DatabaseFormModel::UpdateRequestImmediateField = "update-request-immediate-scan";
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);
// Path
setFormWidget(DatabaseFormModel::PathField, new Wt::WLineEdit());
// Update Period
Wt::WComboBox *updatePeriodCB = new Wt::WComboBox();
setFormWidget(DatabaseFormModel::UpdatePeriodField, updatePeriodCB);
updatePeriodCB->setModel(model->updatePeriodModel());
// Update Start Time
Wt::WComboBox *updateStartTimeCB = new Wt::WComboBox();
setFormWidget(DatabaseFormModel::UpdateStartTimeField, updateStartTimeCB);
updateStartTimeCB->setModel(model->updateStartTimeModel());
// Request Immediate scan
setFormWidget(DatabaseFormModel::UpdateRequestImmediateField, new Wt::WCheckBox());
// Title & Buttons
Wt::WString title = Wt::WString("Media directories settings");
bindString("title", title);
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);
applyInfo = new Wt::WText();
applyInfo->setInline(false);
applyInfo->hide();
bindWidget("apply-info", applyInfo);
saveButton->clicked().connect(this, &DatabaseFormView::processSave);
discardButton->clicked().connect(this, &DatabaseFormView::processDiscard);
updateView(model);
}
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();
// Restarting the update service
{
boost::lock_guard<boost::mutex> serviceLock (ServiceManager::instance().mutex());
DatabaseUpdateService::pointer service = ServiceManager::instance().getService<DatabaseUpdateService>();
if (service)
service->restart();
}
applyInfo->setText( Wt::WString::fromUTF8("New parameters successfully applied!"));
applyInfo->setStyleClass("alert alert-success");
// Udate the view: Delete any validation message in the view, etc.
updateView(model);
} else {
applyInfo->setText( Wt::WString::fromUTF8("Cannot apply new parameters!"));
applyInfo->setStyleClass("alert alert-danger");
updateView(model);
}
}
} // namespace Settings
} // namespace UserInterface
+34
View File
@@ -0,0 +1,34 @@
#ifndef UI_SETTINGS_DB_FORM_VIEW_HPP
#define UI_SETTINGS_DB_FORM_VIEW_HPP
#include <Wt/WContainerWidget>
#include <Wt/WTemplateFormView>
#include <Wt/WText>
#include "common/SessionData.hpp"
namespace UserInterface {
namespace Settings {
class DatabaseFormModel;
class DatabaseFormView : public Wt::WTemplateFormView
{
public:
DatabaseFormView(SessionData& sessionData, Wt::WContainerWidget *parent = 0);
private:
void processSave();
void processDiscard();
Wt::WText *applyInfo;
DatabaseFormModel *model;
};
} // namespace Settings
} // namespace UserInterface
#endif