MythTV  master
storagegroupeditor.cpp
Go to the documentation of this file.
1 // Qt headers
2 #include <QCoreApplication>
3 #include <QDir>
4 #include <QFile>
5 #include <QUrl>
6 #include <QVector>
7 #include <utility>
8 
9 // MythTV headers
11 #include "libmythbase/mythdate.h"
12 #include "libmythbase/mythdb.h"
14 
15 #include "storagegroupeditor.h"
16 
17 #define LOC QString("SGE(%1): ").arg(m_groupname)
18 
19 /****************************************************************************/
20 
22  m_group(std::move(group))
23 {
24  SetLabel();
25 }
26 
28 {
29  QString dispGroup = m_group;
30 
31  if (m_group == "Default")
32  dispGroup = tr("Default", "Default storage group");
33  else if (StorageGroup::kSpecialGroups.contains(m_group))
34  dispGroup = QCoreApplication::translate("(StorageGroups)",
35  m_group.toLatin1().constData());
36 
38  {
39  setLabel(tr("'%1' Storage Group Directories").arg(dispGroup));
40  }
41  else
42  {
43  setLabel(tr("Local '%1' Storage Group Directories").arg(dispGroup));
44  }
45 }
46 
48 {
49  QStringList actions;
50  bool handled =
51  GetMythMainWindow()->TranslateKeyPress("Global", e, actions);
52  for (int i = 0; i < actions.size() && !handled; i++)
53  {
54  const QString& action = actions[i];
55 
56  if (action == "DELETE")
57  {
58  handled = true;
60  }
61  }
62  if (handled)
63  return handled;
65 }
66 
68 {
69  return true;
70 }
71 
73 {
74  bool is_master_host = gCoreContext->IsMasterHost();
75 
76  QString dispGroup = m_group;
77  if (m_group == "Default")
78  dispGroup = tr("Default", "Default storage group");
79  else if (StorageGroup::kSpecialGroups.contains(m_group))
80  dispGroup = QCoreApplication::translate("(StorageGroups)",
81  m_group.toLatin1().constData());
82 
83  QString message = tr("Delete '%1' Storage Group?").arg(dispGroup);
84  if (is_master_host)
85  {
86  if (m_group == "Default")
87  {
88  message = tr("Delete '%1' Storage Group?\n(from remote hosts)").arg(dispGroup);
89  }
90  else
91  {
92  message = tr("Delete '%1' Storage Group?\n(from all hosts)").arg(dispGroup);
93  }
94  }
95 
96  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
97  auto *confirmDelete = new MythConfirmationDialog(popupStack, message, true);
98  if (confirmDelete->Create())
99  {
100  connect(confirmDelete, &MythConfirmationDialog::haveResult,
102  popupStack->AddScreen(confirmDelete);
103  }
104  else
105  {
106  delete confirmDelete;
107  }
108 }
109 
111 {
112  if (doDelete)
113  {
114  bool is_master_host = gCoreContext->IsMasterHost();
115  MSqlQuery query(MSqlQuery::InitCon());
116  QString sql = "DELETE FROM storagegroup "
117  "WHERE groupname = :NAME";
118  if (is_master_host)
119  {
120  // From the master host, delete the group completely (versus just
121  // local directory list) unless it's the Default group, then just
122  // delete remote overrides of the Default group
123  if (m_group == "Default")
124  sql.append(" AND hostname != :HOSTNAME");
125  }
126  else
127  {
128  // For non-master hosts, delete only the local override of the
129  // group directory list
130  sql.append(" AND hostname = :HOSTNAME");
131  }
132  sql.append(';');
133  query.prepare(sql);
134  query.bindValue(":NAME", m_group);
135  if (!is_master_host || (m_group == "Default"))
136  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
137  if (!query.exec())
138  MythDB::DBError("StorageGroupListEditor::doDelete", query);
139  }
140 }
141 
143  int id,
144  QString group) :
145  SimpleDBStorage(_user, "storagegroup", "dirname"),
146  m_id(id),
147  m_group(std::move(group))
148 {
149 }
150 
152 {
153  QString dirnameTag(":SETDIRNAME");
154 
155  QString query("dirname = " + dirnameTag);
156 
157  bindings.insert(dirnameTag, m_user->GetDBValue());
158 
159  return query;
160 }
161 
163 {
164  QString hostnameTag(":WHEREHOST");
165  QString idTag(":WHEREID");
166 
167  QString query("hostname = " + hostnameTag + " AND id = " + idTag);
168 
169  bindings.insert(hostnameTag, gCoreContext->GetHostName());
170  bindings.insert(idTag, m_id);
171 
172  return query;
173 }
174 
175 StorageGroupDirSetting::StorageGroupDirSetting(int id, const QString &group) :
176  MythUIFileBrowserSetting(new StorageGroupDirStorage(this, id, group)),
177  m_id(id), m_group(group)
178 {
179  SetTypeFilter(QDir::AllDirs | QDir::Drives);
180 }
181 
183 {
184  QStringList actions;
185  bool handled =
186  GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
187  for (int i = 0; i < actions.size() && !handled; i++)
188  {
189  const QString& action = actions[i];
190 
191  if (action == "DELETE")
192  {
193  handled = true;
195  }
196  }
197  return handled;
198 }
199 
201 {
202  QString message =
203  tr("Remove '%1'\nDirectory From Storage Group?").arg(getValue());
204  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
205  auto *confirmDelete = new MythConfirmationDialog(popupStack, message, true);
206 
207  if (confirmDelete->Create())
208  {
209  connect(confirmDelete, &MythConfirmationDialog::haveResult,
211  popupStack->AddScreen(confirmDelete);
212  }
213  else
214  {
215  delete confirmDelete;
216  }
217 }
218 
220 {
221  if (doDelete)
222  {
223  MSqlQuery query(MSqlQuery::InitCon());
224  query.prepare("DELETE FROM storagegroup "
225  "WHERE groupname = :NAME "
226  "AND dirname = :DIRNAME "
227  "AND hostname = :HOSTNAME;");
228  query.bindValue(":NAME", m_group);
229  query.bindValue(":DIRNAME", getValue());
230  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
231  if (query.exec())
232  getParent()->removeChild(this);
233  else
234  MythDB::DBError("StorageGroupEditor::DoDeleteSlot", query);
235  }
236 }
237 
239 {
240  clearSettings();
241 
242  auto *button = new ButtonStandardSetting(tr("(Add New Directory)"));
244  addChild(button);
245 
246  MSqlQuery query(MSqlQuery::InitCon());
247  query.prepare("SELECT dirname, id FROM storagegroup "
248  "WHERE groupname = :NAME AND hostname = :HOSTNAME "
249  "ORDER BY id;");
250  query.bindValue(":NAME", m_group);
251  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
252  if (!query.exec() || !query.isActive())
253  MythDB::DBError("StorageGroupEditor::Load", query);
254  else
255  {
256  bool first = true;
257  QString dirname;
258  while (query.next())
259  {
260  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
261  * uses QString::fromAscii() for toString(). Explicitly convert the
262  * value using QString::fromUtf8() to prevent corruption. */
263  dirname = QString::fromUtf8(query.value(0)
264  .toByteArray().constData());
265  if (first)
266  {
267  first = false;
268  }
269  addChild(new StorageGroupDirSetting(query.value(1).toInt(),
270  m_group));
271  }
272 
273  if (!first)
274  {
275  SetLabel();
276  }
277  }
278 
280 }
281 
283 {
284  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
285 
286  auto *settingdialog = new MythUIFileBrowser(popupStack, "");
287  settingdialog->SetTypeFilter(QDir::AllDirs | QDir::Drives);
288 
289  if (settingdialog->Create())
290  {
291  settingdialog->SetReturnEvent(this, "editsetting");
292  popupStack->AddScreen(settingdialog);
293  }
294  else
295  {
296  delete settingdialog;
297  }
298 }
299 
301 {
302  if (event->type() == DialogCompletionEvent::kEventType)
303  {
304  auto *dce = dynamic_cast<DialogCompletionEvent*>(event);
305  if (dce == nullptr)
306  return;
307  QString resultid = dce->GetId();
308 
309  if (resultid == "editsetting")
310  {
311  QString dirname = dce->GetResultText();
312 
313  if (dirname.isEmpty())
314  return;
315 
316  if (!dirname.endsWith("/"))
317  dirname.append("/");
318 
319  MSqlQuery query(MSqlQuery::InitCon());
320  query.prepare("INSERT INTO storagegroup (groupname, hostname, dirname) "
321  "VALUES (:NAME, :HOSTNAME, :DIRNAME);");
322  query.bindValue(":NAME", m_group);
323  query.bindValue(":DIRNAME", dirname);
324  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
325  if (!query.exec())
326  MythDB::DBError("StorageGroupEditor::customEvent", query);
327  else
328  {
329  SetLabel();
330  StandardSetting *directory =
331  new StorageGroupDirSetting(query.lastInsertId().toInt(),
332  m_group);
333  directory->setValue(dirname);
334  addChild(directory);
335  emit settingsChanged(this);
336  }
337  }
338  }
339 }
340 
341 
342 /****************************************************************************/
343 
345 {
346  if (gCoreContext->IsMasterHost())
347  setLabel(tr("Storage Groups (directories for new recordings)"));
348  else
349  setLabel(tr("Local Storage Groups (directories for new recordings)"));
350 }
351 
352 void StorageGroupListEditor::AddSelection(const QString &label,
353  const QString &value)
354 {
355  auto *button = new StorageGroupEditor(value);
356  button->setLabel(label);
357  addChild(button);
358 }
359 
361 {
362  QStringList names;
363  QStringList masterNames;
364  bool createAddDefaultButton = false;
365  QVector< bool > createAddSpecialGroupButton( StorageGroup::kSpecialGroups.size() );
366  bool isMaster = gCoreContext->IsMasterHost();
367 
368  MSqlQuery query(MSqlQuery::InitCon());
369  query.prepare("SELECT distinct groupname "
370  "FROM storagegroup "
371  "WHERE hostname = :HOSTNAME "
372  "ORDER BY groupname;");
373  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
374  if (!query.exec())
375  {
376  MythDB::DBError("StorageGroup::Load getting local group names",
377  query);
378  }
379  else
380  {
381  while (query.next())
382  names << query.value(0).toString();
383  }
384 
385  query.prepare("SELECT distinct groupname "
386  "FROM storagegroup "
387  "ORDER BY groupname;");
388  if (!query.exec())
389  {
390  MythDB::DBError("StorageGroup::Load getting all group names",
391  query);
392  }
393  else
394  {
395  while (query.next())
396  masterNames << query.value(0).toString();
397  }
398 
399  clearSettings();
400 
401  if (isMaster || names.contains("Default"))
402  {
403  AddSelection(tr("Default", "Default storage group"),
404  "Default");
405  }
406  else
407  {
408  createAddDefaultButton = true;
409  }
410 
411  int curGroup = 0;
412  QString groupName;
413  while (curGroup < StorageGroup::kSpecialGroups.size())
414  {
415  groupName = StorageGroup::kSpecialGroups[curGroup];
416  if (names.contains(groupName))
417  {
418  addChild(new StorageGroupEditor(groupName));
419  createAddSpecialGroupButton[curGroup] = false;
420  }
421  else
422  {
423  createAddSpecialGroupButton[curGroup] = true;
424  }
425  curGroup++;
426  }
427 
428  int curName = 0;
429  while (curName < names.size())
430  {
431  if ((names[curName] != "Default") &&
432  (!StorageGroup::kSpecialGroups.contains(names[curName])))
433  addChild(new StorageGroupEditor(names[curName]));
434  curName++;
435  }
436 
437  if (createAddDefaultButton)
438  {
439  AddSelection(tr("(Create default group)"), "Default");
440  }
441 
442  curGroup = 0;
443  while (curGroup < StorageGroup::kSpecialGroups.size())
444  {
445  groupName = StorageGroup::kSpecialGroups[curGroup];
446  if (createAddSpecialGroupButton[curGroup])
447  {
448  AddSelection(tr("(Create %1 group)")
449  .arg(QCoreApplication::translate("(StorageGroups)",
450  groupName.toLatin1().constData())),
451  groupName);
452  }
453  curGroup++;
454  }
455 
456  if (isMaster)
457  {
458  auto *newGroup = new ButtonStandardSetting(tr("(Create new group)"));
460  addChild(newGroup);
461  }
462  else
463  {
464  curName = 0;
465  while (curName < masterNames.size())
466  {
467  if ((masterNames[curName] != "Default") &&
468  (!StorageGroup::kSpecialGroups.contains(masterNames[curName])) &&
469  (!names.contains(masterNames[curName])))
470  {
471  AddSelection(tr("(Create %1 group)")
472  .arg(masterNames[curName]),
473  masterNames[curName]);
474  }
475  curName++;
476  }
477  }
478 
480 }
481 
482 
484 {
485  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
486  auto *settingdialog = new MythTextInputDialog(popupStack,
487  tr("Enter the name of the new storage group"));
488 
489  if (settingdialog->Create())
490  {
491  connect(settingdialog, &MythTextInputDialog::haveResult,
493  popupStack->AddScreen(settingdialog);
494  }
495  else
496  {
497  delete settingdialog;
498  }
499 }
500 
501 void StorageGroupListEditor::CreateNewGroup(const QString& name)
502 {
503  auto *button = new StorageGroupEditor(name);
504  button->setLabel(name + tr(" Storage Group Directories"));
505  button->Load();
506  addChild(button);
507  emit settingsChanged(this);
508 }
509 
510 /* vim: set expandtab tabstop=4 shiftwidth=4: */
MSqlBindings
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:100
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:215
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:127
StorageGroupEditor
Definition: storagegroupeditor.h:8
StandardSetting::setValue
virtual void setValue(const QString &newValue)
Definition: standardsettings.cpp:169
DialogCompletionEvent::GetId
QString GetId()
Definition: mythdialogbox.h:51
StorageGroupListEditor::Load
void Load(void) override
Definition: storagegroupeditor.cpp:360
storagegroupeditor.h
mythdb.h
DBStorage::m_user
StorageUser * m_user
Definition: mythstorage.h:50
StorageGroupDirSetting::StorageGroupDirSetting
StorageGroupDirSetting(int id, const QString &group)
Definition: storagegroupeditor.cpp:175
StorageGroupDirStorage::StorageGroupDirStorage
StorageGroupDirStorage(StorageUser *_user, int id, QString group)
Definition: storagegroupeditor.cpp:142
ButtonStandardSetting
Definition: standardsettings.h:450
MSqlQuery::lastInsertId
QVariant lastInsertId()
Return the id of the last inserted row.
Definition: mythdbcon.cpp:935
MythConfirmationDialog::haveResult
void haveResult(bool)
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:204
MythScreenStack
Definition: mythscreenstack.h:16
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
StorageGroupDirStorage::m_id
int m_id
Definition: storagegroupeditor.h:53
StandardSetting::clearSettings
virtual void clearSettings()
Definition: standardsettings.cpp:159
StorageUser::GetDBValue
virtual QString GetDBValue(void) const =0
StorageGroupDirStorage::GetWhereClause
QString GetWhereClause(MSqlBindings &bindings) const override
Definition: storagegroupeditor.cpp:162
StorageGroupDirSetting::DoDeleteSlot
void DoDeleteSlot(bool doDelete)
Definition: storagegroupeditor.cpp:219
StorageGroupEditor::canDelete
bool canDelete(void) override
Definition: storagegroupeditor.cpp:67
SimpleDBStorage
Definition: mythstorage.h:55
MythTextInputDialog::haveResult
void haveResult(QString)
StorageGroupListEditor::AddSelection
void AddSelection(const QString &label, const QString &value)
Definition: storagegroupeditor.cpp:352
ButtonStandardSetting::clicked
void clicked()
StandardSetting::settingsChanged
void settingsChanged(StandardSetting *selectedSetting=nullptr)
StorageGroupDirStorage
Definition: storagegroupeditor.h:43
StandardSetting::addChild
virtual void addChild(StandardSetting *child)
Definition: standardsettings.cpp:70
mythdate.h
StorageGroupEditor::StorageGroupEditor
StorageGroupEditor(QString group)
Definition: storagegroupeditor.cpp:21
MythMainWindow::TranslateKeyPress
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
Definition: mythmainwindow.cpp:1111
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
StandardSetting::getParent
StandardSetting * getParent() const
Definition: standardsettings.h:49
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
StorageGroupEditor::ShowDeleteDialog
void ShowDeleteDialog()
Definition: storagegroupeditor.cpp:72
StorageGroupDirSetting::m_group
QString m_group
Definition: storagegroupeditor.h:73
StandardSetting::Load
virtual void Load(void)
Definition: standardsettings.cpp:213
StorageGroupEditor::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Definition: storagegroupeditor.cpp:47
mythuifilebrowser.h
StandardSetting::getValue
virtual QString getValue(void) const
Definition: standardsettings.h:52
StandardSetting::keyPressEvent
virtual bool keyPressEvent(QKeyEvent *event)
Definition: standardsettings.cpp:85
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:57
MythUIFileBrowser
Definition: mythuifilebrowser.h:76
StorageGroupDirSetting::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Definition: storagegroupeditor.cpp:182
MythUIFileBrowserSetting::SetTypeFilter
void SetTypeFilter(QDir::Filters filter)
Definition: standardsettings.h:195
StorageGroupEditor::DoDeleteSlot
void DoDeleteSlot(bool doDelete)
Definition: storagegroupeditor.cpp:110
StandardSetting::setLabel
virtual void setLabel(QString str)
Definition: standardsettings.h:34
MythUIFileBrowserSetting
Definition: standardsettings.h:187
MythConfirmationDialog
Dialog asking for user confirmation. Ok and optional Cancel button.
Definition: mythdialogbox.h:271
mythcorecontext.h
StorageGroupEditor::Load
void Load(void) override
Definition: storagegroupeditor.cpp:238
StorageGroupEditor::m_group
QString m_group
Definition: storagegroupeditor.h:26
StandardSetting::removeChild
virtual void removeChild(StandardSetting *child)
Definition: standardsettings.cpp:79
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
StorageGroupDirSetting::ShowDeleteDialog
void ShowDeleteDialog()
Definition: storagegroupeditor.cpp:200
DialogCompletionEvent
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:40
DialogCompletionEvent::kEventType
static const Type kEventType
Definition: mythdialogbox.h:56
StorageGroup::kSpecialGroups
static const QStringList kSpecialGroups
Definition: storagegroup.h:46
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:104
build_compdb.action
action
Definition: build_compdb.py:9
StorageGroupEditor::customEvent
void customEvent(QEvent *event) override
Definition: storagegroupeditor.cpp:300
StorageGroupDirStorage::GetSetClause
QString GetSetClause(MSqlBindings &bindings) const override
Definition: storagegroupeditor.cpp:151
StorageGroupEditor::SetLabel
void SetLabel(void)
Definition: storagegroupeditor.cpp:27
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:322
StandardSetting
Definition: standardsettings.h:29
StorageGroupDirSetting
Definition: storagegroupeditor.h:57
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:844
MythCoreContext::IsMasterHost
bool IsMasterHost(void)
is this the same host as the master
Definition: mythcorecontext.cpp:665
StorageGroupListEditor::CreateNewGroup
void CreateNewGroup(const QString &name)
Definition: storagegroupeditor.cpp:501
MythTextInputDialog
Dialog prompting the user to enter a text string.
Definition: mythdialogbox.h:313
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:52
StorageGroupListEditor::ShowNewGroupDialog
void ShowNewGroupDialog(void) const
Definition: storagegroupeditor.cpp:483
StorageGroupListEditor::StorageGroupListEditor
StorageGroupListEditor(void)
Definition: storagegroupeditor.cpp:344
StorageGroupEditor::ShowFileBrowser
void ShowFileBrowser(void)
Definition: storagegroupeditor.cpp:282
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
StorageUser
Definition: mythstorage.h:15