2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
31 * Dirk Bartley, March 2007
35 #include <QAbstractEventDispatcher>
36 #include <QTableWidgetItem>
39 #include "joblog/joblog.h"
41 #include "jobgraphs/jobplot.h"
43 #include "util/fmtwidgetitem.h"
44 #include "util/comboutil.h"
47 * Constructor for the class
49 JobList::JobList(const QString &mediaName, const QString &clientName,
50 const QString &jobName, const QString &filesetName, QTreeWidgetItem *parentTreeWidgetItem)
53 m_name = ""; /* treeWidgetName has a virtual override in this class */
54 m_mediaName = mediaName;
55 m_clientName = clientName;
57 m_filesetName = filesetName;
58 m_filesetName = filesetName;
59 pgInitialize("", parentTreeWidgetItem);
60 QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
61 thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/emblem-system.png")));
66 if ((m_mediaName != "") || (m_clientName != "") || (m_jobName != "") || (m_filesetName != ""))
68 m_checkCurrentWidget = true;
71 /* Set Defaults for check and spin for limits */
72 limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
73 limitSpinBox->setValue(mainWin->m_recordLimitVal);
74 daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
75 daysSpinBox->setValue(mainWin->m_daysLimitVal);
78 QGridLayout *gridLayout = new QGridLayout(this);
79 gridLayout->setSpacing(6);
80 gridLayout->setMargin(9);
81 gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
83 m_splitter = new QSplitter(Qt::Vertical, this);
84 QScrollArea *area = new QScrollArea();
85 area->setObjectName(QString::fromUtf8("area"));
86 area->setWidget(frame);
87 area->setWidgetResizable(true);
88 m_splitter->addWidget(mp_tableWidget);
89 m_splitter->addWidget(area);
91 gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
96 * Write the m_splitter settings in the destructor
104 * The Meat of the class.
105 * This function will populate the QTableWidget, mp_tablewidget, with
106 * QTableWidgetItems representing the results of a query for what jobs exist on
107 * the media name passed from the constructor stored in m_mediaName.
109 void JobList::populateTable()
111 /* Can't do this in constructor because not neccesarily conected in constructor */
112 prepareFilterWidgets();
115 Freeze frz(*mp_tableWidget); /* disable updating*/
119 fillQueryString(query);
121 /* Set up the Header for the table */
122 QStringList headerlist = (QStringList()
123 << tr("Job Id") << tr("Job Name") << tr("Client") << tr("Job Starttime")
124 << tr("Job Type") << tr("Job Level") << tr("Job Files")
125 << tr("Job Bytes") << tr("Job Status") << tr("Purged") << tr("File Set"));
127 m_jobIdIndex = headerlist.indexOf(tr("Job Id"));
128 m_purgedIndex = headerlist.indexOf(tr("Purged"));
129 m_typeIndex = headerlist.indexOf(tr("Job Type"));
130 m_statusIndex = headerlist.indexOf(tr("Job Status"));
131 m_startIndex = headerlist.indexOf(tr("Job Starttime"));
132 m_filesIndex = headerlist.indexOf(tr("Job Files"));
133 m_bytesIndex = headerlist.indexOf(tr("Job Bytes"));
135 /* Initialize the QTableWidget */
136 m_checkCurrentWidget = false;
137 mp_tableWidget->clear();
138 m_checkCurrentWidget = true;
139 mp_tableWidget->setColumnCount(headerlist.size());
140 mp_tableWidget->setHorizontalHeaderLabels(headerlist);
141 mp_tableWidget->horizontalHeader()->setHighlightSections(false);
142 mp_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
143 mp_tableWidget->setSortingEnabled(false); /* rows move on insert if sorting enabled */
145 if (mainWin->m_sqlDebug) {
146 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
150 if (m_console->sql_cmd(query, results)) {
151 m_resultCount = results.count();
153 QStringList fieldlist;
154 mp_tableWidget->setRowCount(results.size());
157 /* Iterate through the record returned from the query */
159 foreach (resultline, results) {
160 fieldlist = resultline.split("\t");
161 if (fieldlist.size() < 12)
162 continue; /* some fields missing, ignore row */
164 TableItemFormatter jobitem(*mp_tableWidget, row);
166 /* Iterate through fields in the record */
167 QStringListIterator fld(fieldlist);
171 jobitem.setNumericFld(col++, fld.next());
174 jobitem.setTextFld(col++, fld.next());
177 jobitem.setTextFld(col++, fld.next());
180 jobitem.setTextFld(col++, fld.next(), true);
183 jobitem.setJobTypeFld(col++, fld.next());
186 jobitem.setJobLevelFld(col++, fld.next());
189 jobitem.setNumericFld(col++, fld.next());
192 jobitem.setBytesFld(col++, fld.next());
195 jobitem.setJobStatusFld(col++, fld.next());
198 jobitem.setBoolFld(col++, fld.next());
201 jobitem.setTextFld(col++, fld.next());
206 /* set default sorting */
207 mp_tableWidget->sortByColumn(m_jobIdIndex, Qt::DescendingOrder);
208 mp_tableWidget->setSortingEnabled(true);
210 /* Resize the columns */
211 mp_tableWidget->resizeColumnsToContents();
212 mp_tableWidget->resizeRowsToContents();
213 mp_tableWidget->verticalHeader()->hide();
214 if ((m_mediaName != tr("Any")) && (m_resultCount == 0)){
215 /* for context sensitive searches, let the user know if there were no
217 QMessageBox::warning(this, "Bat",
218 tr("The Jobs query returned no results.\n"
219 "Press OK to continue?"), QMessageBox::Ok );
223 void JobList::prepareFilterWidgets()
226 clientComboBox->addItem(tr("Any"));
227 clientComboBox->addItems(m_console->client_list);
228 comboSel(clientComboBox, m_clientName);
230 QStringList volumeList;
231 m_console->getVolumeList(volumeList);
232 volumeComboBox->addItem(tr("Any"));
233 volumeComboBox->addItems(volumeList);
234 comboSel(volumeComboBox, m_mediaName);
236 jobComboBox->addItem(tr("Any"));
237 jobComboBox->addItems(m_console->job_list);
238 comboSel(jobComboBox, m_jobName);
240 levelComboFill(levelComboBox);
242 boolComboFill(purgedComboBox);
244 fileSetComboBox->addItem(tr("Any"));
245 fileSetComboBox->addItems(m_console->fileset_list);
246 comboSel(fileSetComboBox, m_filesetName);
248 jobStatusComboFill(statusComboBox);
252 void JobList::fillQueryString(QString &query)
255 int volumeIndex = volumeComboBox->currentIndex();
256 if (volumeIndex != -1)
257 m_mediaName = volumeComboBox->itemText(volumeIndex);
258 QString distinct = "";
259 if (m_mediaName != tr("Any")) { distinct = "DISTINCT "; }
260 query += "SELECT " + distinct + "Job.Jobid AS Id, Job.Name AS JobName, "
261 " Client.Name AS Client,"
262 " Job.Starttime AS JobStart, Job.Type AS JobType,"
263 " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
264 " Job.JobBytes AS Bytes, Job.JobStatus AS Status,"
265 " Job.PurgedFiles AS Purged, FileSet.FileSet"
267 " JOIN Client ON (Client.ClientId=Job.ClientId)"
268 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId) ";
269 QStringList conditions;
270 if (m_mediaName != tr("Any")) {
271 query += " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId) "
272 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId) ";
273 conditions.append("Media.VolumeName='" + m_mediaName + "'");
276 comboCond(conditions, clientComboBox, "Client.Name");
277 comboCond(conditions, jobComboBox, "Job.Name");
278 levelComboCond(conditions, levelComboBox, "Job.Level");
279 jobStatusComboCond(conditions, statusComboBox, "Job.JobStatus");
280 boolComboCond(conditions, purgedComboBox, "Job.PurgedFiles");
281 comboCond(conditions, fileSetComboBox, "FileSet.FileSet");
283 /* If Limit check box For limit by days is checked */
284 if (daysCheckBox->checkState() == Qt::Checked) {
285 QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
286 QString since = stamp.toString(Qt::ISODate);
287 conditions.append("Job.Starttime>'" + since + "'");
290 foreach (QString condition, conditions) {
292 query += " WHERE " + condition;
295 query += " AND " + condition;
299 query += " ORDER BY Job.JobId DESC";
300 /* If Limit check box for limit records returned is checked */
301 if (limitCheckBox->checkState() == Qt::Checked) {
303 limit.setNum(limitSpinBox->value());
304 query += " LIMIT " + limit;
309 * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
310 * The tree has been populated.
312 void JobList::PgSeltreeWidgetClicked()
320 * Virtual function override of pages function which is called when this page
321 * is visible on the stack
323 void JobList::currentStackItem()
325 /* if (!m_populated) populate every time user comes back to this object */
330 * Virtual Function to return the name for the medialist tree widget
332 void JobList::treeWidgetName(QString &desc)
334 if (m_mediaName != "" ) {
335 desc = tr("JobList of Volume %1").arg(m_mediaName);
336 } else if (m_clientName != "" ) {
337 desc = tr("JobList of Client %1").arg(m_clientName);
338 } else if (m_jobName != "" ) {
339 desc = tr("JobList of Job %1").arg(m_jobName);
340 } else if (m_filesetName != "" ) {
341 desc = tr("JobList of fileset %1").arg(m_filesetName);
343 desc = tr("JobList");
348 * This functions much line tableItemChanged for trees like the page selector,
349 * but I will do much less here
351 void JobList::tableItemChanged(QTableWidgetItem *currentItem, QTableWidgetItem * /*previousItem*/)
353 if (m_checkCurrentWidget) {
354 int row = currentItem->row();
355 QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
356 m_currentJob = jobitem->text();
358 /* include purged action or not */
359 jobitem = mp_tableWidget->item(row, m_purgedIndex);
360 QString purged = jobitem->text();
361 mp_tableWidget->removeAction(actionPurgeFiles);
362 if (purged == tr("No") ) {
363 mp_tableWidget->addAction(actionPurgeFiles);
365 /* include restore from time and job action or not */
366 jobitem = mp_tableWidget->item(row, m_typeIndex);
367 QString type = jobitem->text();
368 mp_tableWidget->removeAction(actionRestoreFromJob);
369 mp_tableWidget->removeAction(actionRestoreFromTime);
370 if (type == tr("Backup")) {
371 mp_tableWidget->addAction(actionRestoreFromJob);
372 mp_tableWidget->addAction(actionRestoreFromTime);
374 /* include cancel action or not */
375 jobitem = mp_tableWidget->item(row, m_statusIndex);
376 QString status = jobitem->text();
377 mp_tableWidget->removeAction(actionCancelJob);
378 if (status == tr("Running") || status == tr("Created, not yet running")) {
379 mp_tableWidget->addAction(actionCancelJob);
385 * Function to create connections for context sensitive menu for this and
388 void JobList::createConnections()
390 /* connect to the action specific to this pages class that shows up in the
391 * page selector tree */
392 connect(actionRefreshJobList, SIGNAL(triggered()), this,
393 SLOT(populateTable()));
394 connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
396 connect(graphButton, SIGNAL(pressed()), this, SLOT(graphTable()));
398 graphButton->setEnabled(false);
399 graphButton->setVisible(false);
401 /* for the tableItemChanged to maintain m_currentJob */
402 connect(mp_tableWidget, SIGNAL(
403 currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
404 this, SLOT(tableItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
406 /* for the tableItemChanged to maintain a delete selection */
407 connect(mp_tableWidget, SIGNAL( itemSelectionChanged()),
408 this, SLOT(selectedJobsGet()) );
410 /* Do what is required for the local context sensitive menu */
413 /* setContextMenuPolicy is required */
414 mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
417 mp_tableWidget->addAction(actionRefreshJobList);
418 mp_tableWidget->addAction(actionListJobid);
419 mp_tableWidget->addAction(actionListFilesOnJob);
420 mp_tableWidget->addAction(actionListJobMedia);
421 mp_tableWidget->addAction(actionListVolumes);
422 mp_tableWidget->addAction(actionDeleteJob);
423 mp_tableWidget->addAction(actionPurgeFiles);
424 mp_tableWidget->addAction(actionRestoreFromJob);
425 mp_tableWidget->addAction(actionRestoreFromTime);
426 mp_tableWidget->addAction(actionShowLogForJob);
428 /* Make Connections */
429 connect(actionListJobid, SIGNAL(triggered()), this,
430 SLOT(consoleListJobid()));
431 connect(actionListFilesOnJob, SIGNAL(triggered()), this,
432 SLOT(consoleListFilesOnJob()));
433 connect(actionListJobMedia, SIGNAL(triggered()), this,
434 SLOT(consoleListJobMedia()));
435 connect(actionListVolumes, SIGNAL(triggered()), this,
436 SLOT(consoleListVolumes()));
437 connect(actionDeleteJob, SIGNAL(triggered()), this,
438 SLOT(consoleDeleteJob()));
439 connect(actionPurgeFiles, SIGNAL(triggered()), this,
440 SLOT(consolePurgeFiles()));
441 connect(actionRestoreFromJob, SIGNAL(triggered()), this,
442 SLOT(preRestoreFromJob()));
443 connect(actionRestoreFromTime, SIGNAL(triggered()), this,
444 SLOT(preRestoreFromTime()));
445 connect(actionShowLogForJob, SIGNAL(triggered()), this,
446 SLOT(showLogForJob()));
447 connect(actionCancelJob, SIGNAL(triggered()), this,
448 SLOT(consoleCancelJob()));
449 connect(actionListJobTotals, SIGNAL(triggered()), this,
450 SLOT(consoleListJobTotals()));
452 m_contextActions.append(actionRefreshJobList);
453 m_contextActions.append(actionListJobTotals);
457 * Functions to respond to local context sensitive menu sending console commands
458 * If I could figure out how to make these one function passing a string, Yaaaaaa
460 void JobList::consoleListJobid()
462 QString cmd("list jobid=");
464 if (mainWin->m_longList) { cmd.prepend("l"); }
467 void JobList::consoleListFilesOnJob()
469 QString cmd("list files jobid=");
471 if (mainWin->m_longList) { cmd.prepend("l"); }
474 void JobList::consoleListJobMedia()
476 QString cmd("list jobmedia jobid=");
478 if (mainWin->m_longList) { cmd.prepend("l"); }
481 void JobList::consoleListVolumes()
483 QString cmd("list volumes jobid=");
485 if (mainWin->m_longList) { cmd.prepend("l"); }
488 void JobList::consoleListJobTotals()
490 QString cmd("list jobtotals");
491 if (mainWin->m_longList) { cmd.prepend("l"); }
494 void JobList::consoleDeleteJob()
496 if (QMessageBox::warning(this, "Bat",
497 tr("Are you sure you want to delete?? !!!.\n"
498 "This delete command is used to delete a Job record and all associated catalog"
499 " records that were created. This command operates only on the Catalog"
500 " database and has no effect on the actual data written to a Volume. This"
501 " command can be dangerous and we strongly recommend that you do not use"
502 " it unless you know what you are doing. The Job and all its associated"
503 " records (File and JobMedia) will be deleted from the catalog."
504 "Press OK to proceed with delete operation.?"),
505 QMessageBox::Ok | QMessageBox::Cancel)
506 == QMessageBox::Cancel) { return; }
508 QString cmd("delete job jobid=");
509 cmd += m_selectedJobs;
512 void JobList::consolePurgeFiles()
514 if (QMessageBox::warning(this, "Bat",
515 tr("Are you sure you want to purge ?? !!!.\n"
516 "The Purge command will delete associated Catalog database records from Jobs and"
517 " Volumes without considering the retention period. Purge works only on the"
518 " Catalog database and does not affect data written to Volumes. This command can"
519 " be dangerous because you can delete catalog records associated with current"
520 " backups of files, and we recommend that you do not use it unless you know what"
522 "Press OK to proceed with the purge operation?"),
523 QMessageBox::Ok | QMessageBox::Cancel)
524 == QMessageBox::Cancel) { return; }
526 QString cmd("purge files jobid=");
532 * Subroutine to call preRestore to restore from a select job
534 void JobList::preRestoreFromJob()
536 new prerestorePage(m_currentJob, R_JOBIDLIST);
540 * Subroutine to call preRestore to restore from a select job
542 void JobList::preRestoreFromTime()
544 new prerestorePage(m_currentJob, R_JOBDATETIME);
548 * Subroutine to call class to show the log in the database from that job
550 void JobList::showLogForJob()
552 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
553 new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
557 * Cancel a running job
559 void JobList::consoleCancelJob()
561 QString cmd("cancel jobid=");
569 void JobList::graphTable()
573 pass.recordLimitCheck = limitCheckBox->checkState();
574 pass.daysLimitCheck = daysCheckBox->checkState();
575 pass.recordLimitSpin = limitSpinBox->value();
576 pass.daysLimitSpin = daysSpinBox->value();
577 pass.jobCombo = jobComboBox->currentText();
578 pass.clientCombo = clientComboBox->currentText();
579 pass.volumeCombo = volumeComboBox->currentText();
580 pass.fileSetCombo = fileSetComboBox->currentText();
581 pass.purgedCombo = purgedComboBox->currentText();
582 pass.levelCombo = levelComboBox->currentText();
583 pass.statusCombo = statusComboBox->currentText();
585 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
586 new JobPlot(pageSelectorTreeWidgetItem, pass);
591 * Save user settings associated with this page
593 void JobList::writeSettings()
595 QSettings settings(m_console->m_dir->name(), "bat");
596 settings.beginGroup(m_groupText);
597 settings.setValue(m_splitText, m_splitter->saveState());
602 * Read and restore user settings associated with this page
604 void JobList::readSettings()
606 m_groupText = "JobListPage";
607 m_splitText = "splitterSizes_1";
608 QSettings settings(m_console->m_dir->name(), "bat");
609 settings.beginGroup(m_groupText);
610 m_splitter->restoreState(settings.value(m_splitText).toByteArray());
615 * Function to fill m_selectedJobsCount and m_selectedJobs with selected values
617 void JobList::selectedJobsGet()
620 QList<QTableWidgetItem *> sitems = mp_tableWidget->selectedItems();
621 foreach (QTableWidgetItem *sitem, sitems) {
622 int row = sitem->row();
623 if (!rowList.contains(row)) {
630 foreach(int row, rowList) {
631 QTableWidgetItem * sitem = mp_tableWidget->item(row, m_jobIdIndex);
632 if (!first) m_selectedJobs.append(",");
634 m_selectedJobs.append(sitem->text());
636 m_selectedJobsCount = rowList.count();
637 if (m_selectedJobsCount > 1) {
638 QString text = QString( tr("Delete list of %1 Jobs")).arg(m_selectedJobsCount);
639 actionDeleteJob->setText(text);
641 actionDeleteJob->setText(tr("Delete Single Job"));