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 <QRegExp>
6 #include <QUrl>
7 #include <QVector>
8 #include <utility>
9 
10 // MythTV headers
11 #include "storagegroupeditor.h"
12 #include "mythcorecontext.h"
13 #include "mythdb.h"
14 #include "mythlogging.h"
15 #include "mythdate.h"
16 #include "mythuifilebrowser.h"
17 
18 #define LOC QString("SGE(%1): ").arg(m_groupname)
19 
20 /****************************************************************************/
21 
23  m_group(std::move(group))
24 {
25  SetLabel();
26 }
27 
29 {
30  QString dispGroup = m_group;
31 
32  if (m_group == "Default")
33  dispGroup = tr("Default", "Default storage group");
34  else if (StorageGroup::kSpecialGroups.contains(m_group))
35  dispGroup = QCoreApplication::translate("(StorageGroups)",
36  m_group.toLatin1().constData());
37 
39  {
40  setLabel(tr("'%1' Storage Group Directories").arg(dispGroup));
41  }
42  else
43  {
44  setLabel(tr("Local '%1' Storage Group Directories").arg(dispGroup));
45  }
46 }
47 
49 {
50  QStringList actions;
51  bool handled =
52  GetMythMainWindow()->TranslateKeyPress("Global", e, actions);
53  for (int i = 0; i < actions.size() && !handled; i++)
54  {
55  QString action = actions[i];
56 
57  if (action == "DELETE")
58  {
59  handled = true;
61  }
62  }
63  if (handled)
64  return handled;
66 }
67 
69 {
70  return true;
71 }
72 
74 {
75  bool is_master_host = gCoreContext->IsMasterHost();
76 
77  QString dispGroup = m_group;
78  if (m_group == "Default")
79  dispGroup = tr("Default", "Default storage group");
80  else if (StorageGroup::kSpecialGroups.contains(m_group))
81  dispGroup = QCoreApplication::translate("(StorageGroups)",
82  m_group.toLatin1().constData());
83 
84  QString message = tr("Delete '%1' Storage Group?").arg(dispGroup);
85  if (is_master_host)
86  {
87  if (m_group == "Default")
88  {
89  message = tr("Delete '%1' Storage Group?\n(from remote hosts)").arg(dispGroup);
90  }
91  else
92  {
93  message = tr("Delete '%1' Storage Group?\n(from all hosts)").arg(dispGroup);
94  }
95  }
96 
97  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
98  MythConfirmationDialog *confirmDelete =
99  new MythConfirmationDialog(popupStack, message, true);
100  if (confirmDelete->Create())
101  {
102  connect(confirmDelete, SIGNAL(haveResult(bool)),
103  SLOT(DoDeleteSlot(bool)));
104  popupStack->AddScreen(confirmDelete);
105  }
106  else
107  delete confirmDelete;
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  const QString &group) :
145  SimpleDBStorage(_user, "storagegroup", "dirname"),
146  m_id(id),
147  m_group(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  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  MythConfirmationDialog *confirmDelete =
206  new MythConfirmationDialog(popupStack, message, true);
207 
208  if (confirmDelete->Create())
209  {
210  connect(confirmDelete, SIGNAL(haveResult(bool)),
211  SLOT(DoDeleteSlot(bool)));
212  popupStack->AddScreen(confirmDelete);
213  }
214  else
215  delete confirmDelete;
216 }
217 
219 {
220  if (doDelete)
221  {
222  MSqlQuery query(MSqlQuery::InitCon());
223  query.prepare("DELETE FROM storagegroup "
224  "WHERE groupname = :NAME "
225  "AND dirname = :DIRNAME "
226  "AND hostname = :HOSTNAME;");
227  query.bindValue(":NAME", m_group);
228  query.bindValue(":DIRNAME", getValue());
229  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
230  if (query.exec())
231  getParent()->removeChild(this);
232  else
233  MythDB::DBError("StorageGroupEditor::DoDeleteSlot", query);
234  }
235 }
236 
238 {
239  clearSettings();
240 
241  ButtonStandardSetting *button =
242  new ButtonStandardSetting(tr("(Add New Directory)"));
243  connect(button, SIGNAL(clicked()), SLOT(ShowFileBrowser()));
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  MythUIFileBrowser *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  delete settingdialog;
296 }
297 
299 {
300  if (event->type() == DialogCompletionEvent::kEventType)
301  {
303  QString resultid = dce->GetId();
304 
305  if (resultid == "editsetting")
306  {
307  QString dirname = dce->GetResultText();
308 
309  if (dirname.isEmpty())
310  return;
311 
312  if (!dirname.endsWith("/"))
313  dirname.append("/");
314 
315  MSqlQuery query(MSqlQuery::InitCon());
316  query.prepare("INSERT INTO storagegroup (groupname, hostname, dirname) "
317  "VALUES (:NAME, :HOSTNAME, :DIRNAME);");
318  query.bindValue(":NAME", m_group);
319  query.bindValue(":DIRNAME", dirname);
320  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
321  if (!query.exec())
322  MythDB::DBError("StorageGroupEditor::customEvent", query);
323  else
324  {
325  SetLabel();
326  StandardSetting *directory =
327  new StorageGroupDirSetting(query.lastInsertId().toInt(),
328  m_group);
329  directory->setValue(dirname);
330  addChild(directory);
331  emit settingsChanged(this);
332  }
333  }
334  }
335 }
336 
337 
338 /****************************************************************************/
339 
341 {
342  if (gCoreContext->IsMasterHost())
343  setLabel(tr("Storage Groups (directories for new recordings)"));
344  else
345  setLabel(tr("Local Storage Groups (directories for new recordings)"));
346 }
347 
348 void StorageGroupListEditor::AddSelection(const QString &label,
349  const QString &value)
350 {
351  StorageGroupEditor *button = new StorageGroupEditor(value);
352  button->setLabel(label);
353  addChild(button);
354 }
355 
357 {
358  QStringList names;
359  QStringList masterNames;
360  bool createAddDefaultButton = false;
361  QVector< bool > createAddSpecialGroupButton( StorageGroup::kSpecialGroups.size() );
362  bool isMaster = gCoreContext->IsMasterHost();
363 
364  MSqlQuery query(MSqlQuery::InitCon());
365  query.prepare("SELECT distinct groupname "
366  "FROM storagegroup "
367  "WHERE hostname = :HOSTNAME "
368  "ORDER BY groupname;");
369  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
370  if (!query.exec())
371  MythDB::DBError("StorageGroup::Load getting local group names",
372  query);
373  else
374  {
375  while (query.next())
376  names << query.value(0).toString();
377  }
378 
379  query.prepare("SELECT distinct groupname "
380  "FROM storagegroup "
381  "ORDER BY groupname;");
382  if (!query.exec())
383  MythDB::DBError("StorageGroup::Load getting all group names",
384  query);
385  else
386  {
387  while (query.next())
388  masterNames << query.value(0).toString();
389  }
390 
391  clearSettings();
392 
393  if (isMaster || names.contains("Default"))
394  {
395  AddSelection(tr("Default", "Default storage group"),
396  "Default");
397  }
398  else
399  createAddDefaultButton = true;
400 
401  int curGroup = 0;
402  QString groupName;
403  while (curGroup < StorageGroup::kSpecialGroups.size())
404  {
405  groupName = StorageGroup::kSpecialGroups[curGroup];
406  if (names.contains(groupName))
407  {
408  addChild(new StorageGroupEditor(groupName));
409  createAddSpecialGroupButton[curGroup] = false;
410  }
411  else
412  createAddSpecialGroupButton[curGroup] = true;
413  curGroup++;
414  }
415 
416  int curName = 0;
417  while (curName < names.size())
418  {
419  if ((names[curName] != "Default") &&
420  (!StorageGroup::kSpecialGroups.contains(names[curName])))
421  addChild(new StorageGroupEditor(names[curName]));
422  curName++;
423  }
424 
425  if (createAddDefaultButton)
426  {
427  AddSelection(tr("(Create default group)"), "Default");
428  }
429 
430  curGroup = 0;
431  while (curGroup < StorageGroup::kSpecialGroups.size())
432  {
433  groupName = StorageGroup::kSpecialGroups[curGroup];
434  if (createAddSpecialGroupButton[curGroup])
435  AddSelection(tr("(Create %1 group)")
436  .arg(QCoreApplication::translate("(StorageGroups)",
437  groupName.toLatin1().constData())),
438  groupName);
439  curGroup++;
440  }
441 
442  if (isMaster)
443  {
444  ButtonStandardSetting *newGroup =
445  new ButtonStandardSetting(tr("(Create new group)"));
446  connect(newGroup, SIGNAL(clicked()), SLOT(ShowNewGroupDialog()));
447  addChild(newGroup);
448  }
449  else
450  {
451  curName = 0;
452  while (curName < masterNames.size())
453  {
454  if ((masterNames[curName] != "Default") &&
455  (!StorageGroup::kSpecialGroups.contains(masterNames[curName])) &&
456  (!names.contains(masterNames[curName])))
457  AddSelection(tr("(Create %1 group)")
458  .arg(masterNames[curName]),
459  masterNames[curName]);
460  curName++;
461  }
462  }
463 
465 }
466 
467 
469 {
470  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
471  MythTextInputDialog *settingdialog =
472  new MythTextInputDialog(popupStack,
473  tr("Enter the name of the new storage group"));
474 
475  if (settingdialog->Create())
476  {
477  connect(settingdialog, SIGNAL(haveResult(QString)),
478  SLOT(CreateNewGroup(QString)));
479  popupStack->AddScreen(settingdialog);
480  }
481  else
482  {
483  delete settingdialog;
484  }
485 }
486 
488 {
490  button->setLabel(name + tr(" Storage Group Directories"));
491  button->Load();
492  addChild(button);
493  emit settingsChanged(this);
494 }
495 
496 /* vim: set expandtab tabstop=4 shiftwidth=4: */
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:782
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:863
bool keyPressEvent(QKeyEvent *event) override
virtual bool keyPressEvent(QKeyEvent *)
Dialog asking for user confirmation.
virtual void clearSettings()
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
QString GetWhereClause(MSqlBindings &bindings) const override
StandardSetting * getParent() const
void settingsChanged(StandardSetting *selectedSetting=nullptr)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
virtual void Load(void)
void AddSelection(const QString &label, const QString &value)
void SetReturnEvent(QObject *retobject, const QString &resultid)
bool Create(void) override
StorageGroupDirStorage(StorageUser *_user, int id, const QString &group)
MythScreenStack * GetStack(const QString &stackname)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static const QStringList kSpecialGroups
Definition: storagegroup.h:46
virtual QString GetDBValue(void) const =0
static Type kEventType
Definition: mythdialogbox.h:50
virtual QString getValue(void) const
QVariant value(int i) const
Definition: mythdbcon.h:198
bool Create(void) override
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
QVariant lastInsertId()
Return the id of the last inserted row.
Definition: mythdbcon.cpp:887
void DoDeleteSlot(bool doDelete)
virtual void setLabel(QString str)
StorageUser * m_user
Definition: mythstorage.h:46
bool isActive(void) const
Definition: mythdbcon.h:204
void SetTypeFilter(QDir::Filters filter)
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
StorageGroupDirSetting(int id, const QString &group)
const char * name
Definition: ParseText.cpp:328
void CreateNewGroup(const QString &name)
bool IsMasterHost(void)
is this the same host as the master
virtual void addChild(StandardSetting *child)
MythMainWindow * GetMythMainWindow(void)
Dialog prompting the user to enter a text string.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:807
void DoDeleteSlot(bool doDelete)
bool Create(void) override
void SetTypeFilter(QDir::Filters filter)
void Load(void) override
bool canDelete(void) override
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:98
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
void Load(void) override
StorageGroupEditor(QString group)
QString GetHostName(void)
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:37
virtual void setValue(const QString &newValue)
QString GetSetClause(MSqlBindings &bindings) const override
void customEvent(QEvent *event) override
bool keyPressEvent(QKeyEvent *event) override
virtual void removeChild(StandardSetting *child)