2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2009 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 int rcnt = mp_tableWidget->rowCount();
224 int ccnt = mp_tableWidget->columnCount();
225 for(int r=0; r < rcnt; r++) {
226 for(int c=0; c < ccnt; c++) {
227 QTableWidgetItem* item = mp_tableWidget->item(r, c);
229 item->setFlags(Qt::ItemFlags(item->flags() & (~Qt::ItemIsEditable)));
235 void JobList::prepareFilterWidgets()
238 clientComboBox->addItem(tr("Any"));
239 clientComboBox->addItems(m_console->client_list);
240 comboSel(clientComboBox, m_clientName);
242 QStringList volumeList;
243 getVolumeList(volumeList);
244 volumeComboBox->addItem(tr("Any"));
245 volumeComboBox->addItems(volumeList);
246 comboSel(volumeComboBox, m_mediaName);
248 jobComboBox->addItem(tr("Any"));
249 jobComboBox->addItems(m_console->job_list);
250 comboSel(jobComboBox, m_jobName);
252 levelComboFill(levelComboBox);
254 boolComboFill(purgedComboBox);
256 fileSetComboBox->addItem(tr("Any"));
257 fileSetComboBox->addItems(m_console->fileset_list);
258 comboSel(fileSetComboBox, m_filesetName);
260 jobStatusComboFill(statusComboBox);
264 void JobList::fillQueryString(QString &query)
267 int volumeIndex = volumeComboBox->currentIndex();
268 if (volumeIndex != -1)
269 m_mediaName = volumeComboBox->itemText(volumeIndex);
270 QString distinct = "";
271 if (m_mediaName != tr("Any")) { distinct = "DISTINCT "; }
272 query += "SELECT " + distinct + "Job.Jobid AS Id, Job.Name AS JobName, "
273 " Client.Name AS Client,"
274 " Job.Starttime AS JobStart, Job.Type AS JobType,"
275 " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
276 " Job.JobBytes AS Bytes, Job.JobStatus AS Status,"
277 " Job.PurgedFiles AS Purged, FileSet.FileSet"
279 " JOIN Client ON (Client.ClientId=Job.ClientId)"
280 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId) ";
281 QStringList conditions;
282 if (m_mediaName != tr("Any")) {
283 query += " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId) "
284 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId) ";
285 conditions.append("Media.VolumeName='" + m_mediaName + "'");
288 comboCond(conditions, clientComboBox, "Client.Name");
289 comboCond(conditions, jobComboBox, "Job.Name");
290 levelComboCond(conditions, levelComboBox, "Job.Level");
291 jobStatusComboCond(conditions, statusComboBox, "Job.JobStatus");
292 boolComboCond(conditions, purgedComboBox, "Job.PurgedFiles");
293 comboCond(conditions, fileSetComboBox, "FileSet.FileSet");
295 /* If Limit check box For limit by days is checked */
296 if (daysCheckBox->checkState() == Qt::Checked) {
297 QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
298 QString since = stamp.toString(Qt::ISODate);
299 conditions.append("Job.Starttime>'" + since + "'");
302 foreach (QString condition, conditions) {
304 query += " WHERE " + condition;
307 query += " AND " + condition;
311 query += " ORDER BY Job.JobId DESC";
312 /* If Limit check box for limit records returned is checked */
313 if (limitCheckBox->checkState() == Qt::Checked) {
315 limit.setNum(limitSpinBox->value());
316 query += " LIMIT " + limit;
321 * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
322 * The tree has been populated.
324 void JobList::PgSeltreeWidgetClicked()
332 * Virtual function override of pages function which is called when this page
333 * is visible on the stack
335 void JobList::currentStackItem()
337 /* if (!m_populated) populate every time user comes back to this object */
342 * Virtual Function to return the name for the medialist tree widget
344 void JobList::treeWidgetName(QString &desc)
346 if (m_mediaName != "" ) {
347 desc = tr("JobList of Volume %1").arg(m_mediaName);
348 } else if (m_clientName != "" ) {
349 desc = tr("JobList of Client %1").arg(m_clientName);
350 } else if (m_jobName != "" ) {
351 desc = tr("JobList of Job %1").arg(m_jobName);
352 } else if (m_filesetName != "" ) {
353 desc = tr("JobList of fileset %1").arg(m_filesetName);
355 desc = tr("JobList");
360 * This functions much line tableItemChanged for trees like the page selector,
361 * but I will do much less here
363 void JobList::tableItemChanged(QTableWidgetItem *currentItem, QTableWidgetItem * /*previousItem*/)
365 if (m_checkCurrentWidget) {
366 int row = currentItem->row();
367 QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
368 m_currentJob = jobitem->text();
370 /* include purged action or not */
371 jobitem = mp_tableWidget->item(row, m_purgedIndex);
372 QString purged = jobitem->text();
373 mp_tableWidget->removeAction(actionPurgeFiles);
374 if (purged == tr("No") ) {
375 mp_tableWidget->addAction(actionPurgeFiles);
377 /* include restore from time and job action or not */
378 jobitem = mp_tableWidget->item(row, m_typeIndex);
379 QString type = jobitem->text();
380 mp_tableWidget->removeAction(actionRestoreFromJob);
381 mp_tableWidget->removeAction(actionRestoreFromTime);
382 if (type == tr("Backup")) {
383 mp_tableWidget->addAction(actionRestoreFromJob);
384 mp_tableWidget->addAction(actionRestoreFromTime);
386 /* include cancel action or not */
387 jobitem = mp_tableWidget->item(row, m_statusIndex);
388 QString status = jobitem->text();
389 mp_tableWidget->removeAction(actionCancelJob);
390 if (status == tr("Running") || status == tr("Created, not yet running")) {
391 mp_tableWidget->addAction(actionCancelJob);
397 * Function to create connections for context sensitive menu for this and
400 void JobList::createConnections()
402 /* connect to the action specific to this pages class that shows up in the
403 * page selector tree */
404 connect(actionRefreshJobList, SIGNAL(triggered()), this,
405 SLOT(populateTable()));
406 connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
408 connect(graphButton, SIGNAL(pressed()), this, SLOT(graphTable()));
410 graphButton->setEnabled(false);
411 graphButton->setVisible(false);
413 /* for the tableItemChanged to maintain m_currentJob */
414 connect(mp_tableWidget, SIGNAL(
415 currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
416 this, SLOT(tableItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
418 /* for the tableItemChanged to maintain a delete selection */
419 connect(mp_tableWidget, SIGNAL( itemSelectionChanged()),
420 this, SLOT(selectedJobsGet()) );
422 /* Do what is required for the local context sensitive menu */
425 /* setContextMenuPolicy is required */
426 mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
429 mp_tableWidget->addAction(actionRefreshJobList);
430 mp_tableWidget->addAction(actionListJobid);
431 mp_tableWidget->addAction(actionListFilesOnJob);
432 mp_tableWidget->addAction(actionListJobMedia);
433 mp_tableWidget->addAction(actionListVolumes);
434 mp_tableWidget->addAction(actionDeleteJob);
435 mp_tableWidget->addAction(actionPurgeFiles);
436 mp_tableWidget->addAction(actionRestoreFromJob);
437 mp_tableWidget->addAction(actionRestoreFromTime);
438 mp_tableWidget->addAction(actionShowLogForJob);
440 /* Make Connections */
441 connect(actionListJobid, SIGNAL(triggered()), this,
442 SLOT(consoleListJobid()));
443 connect(actionListFilesOnJob, SIGNAL(triggered()), this,
444 SLOT(consoleListFilesOnJob()));
445 connect(actionListJobMedia, SIGNAL(triggered()), this,
446 SLOT(consoleListJobMedia()));
447 connect(actionListVolumes, SIGNAL(triggered()), this,
448 SLOT(consoleListVolumes()));
449 connect(actionDeleteJob, SIGNAL(triggered()), this,
450 SLOT(consoleDeleteJob()));
451 connect(actionPurgeFiles, SIGNAL(triggered()), this,
452 SLOT(consolePurgeFiles()));
453 connect(actionRestoreFromJob, SIGNAL(triggered()), this,
454 SLOT(preRestoreFromJob()));
455 connect(actionRestoreFromTime, SIGNAL(triggered()), this,
456 SLOT(preRestoreFromTime()));
457 connect(actionShowLogForJob, SIGNAL(triggered()), this,
458 SLOT(showLogForJob()));
459 connect(actionCancelJob, SIGNAL(triggered()), this,
460 SLOT(consoleCancelJob()));
461 connect(actionListJobTotals, SIGNAL(triggered()), this,
462 SLOT(consoleListJobTotals()));
464 m_contextActions.append(actionRefreshJobList);
465 m_contextActions.append(actionListJobTotals);
469 * Functions to respond to local context sensitive menu sending console commands
470 * If I could figure out how to make these one function passing a string, Yaaaaaa
472 void JobList::consoleListJobid()
474 QString cmd("list jobid=");
476 if (mainWin->m_longList) { cmd.prepend("l"); }
479 void JobList::consoleListFilesOnJob()
481 QString cmd("list files jobid=");
483 if (mainWin->m_longList) { cmd.prepend("l"); }
486 void JobList::consoleListJobMedia()
488 QString cmd("list jobmedia jobid=");
490 if (mainWin->m_longList) { cmd.prepend("l"); }
493 void JobList::consoleListVolumes()
495 QString cmd("list volumes jobid=");
497 if (mainWin->m_longList) { cmd.prepend("l"); }
500 void JobList::consoleListJobTotals()
502 QString cmd("list jobtotals");
503 if (mainWin->m_longList) { cmd.prepend("l"); }
506 void JobList::consoleDeleteJob()
508 if (QMessageBox::warning(this, "Bat",
509 tr("Are you sure you want to delete?? !!!.\n"
510 "This delete command is used to delete a Job record and all associated catalog"
511 " records that were created. This command operates only on the Catalog"
512 " database and has no effect on the actual data written to a Volume. This"
513 " command can be dangerous and we strongly recommend that you do not use"
514 " it unless you know what you are doing. The Job and all its associated"
515 " records (File and JobMedia) will be deleted from the catalog."
516 "Press OK to proceed with delete operation.?"),
517 QMessageBox::Ok | QMessageBox::Cancel)
518 == QMessageBox::Cancel) { return; }
520 QString cmd("delete job jobid=");
521 cmd += m_selectedJobs;
524 void JobList::consolePurgeFiles()
526 if (QMessageBox::warning(this, "Bat",
527 tr("Are you sure you want to purge ?? !!!.\n"
528 "The Purge command will delete associated Catalog database records from Jobs and"
529 " Volumes without considering the retention period. Purge works only on the"
530 " Catalog database and does not affect data written to Volumes. This command can"
531 " be dangerous because you can delete catalog records associated with current"
532 " backups of files, and we recommend that you do not use it unless you know what"
534 "Press OK to proceed with the purge operation?"),
535 QMessageBox::Ok | QMessageBox::Cancel)
536 == QMessageBox::Cancel) { return; }
538 QString cmd("purge files jobid=");
544 * Subroutine to call preRestore to restore from a select job
546 void JobList::preRestoreFromJob()
548 new prerestorePage(m_currentJob, R_JOBIDLIST);
552 * Subroutine to call preRestore to restore from a select job
554 void JobList::preRestoreFromTime()
556 new prerestorePage(m_currentJob, R_JOBDATETIME);
560 * Subroutine to call class to show the log in the database from that job
562 void JobList::showLogForJob()
564 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
565 new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
569 * Cancel a running job
571 void JobList::consoleCancelJob()
573 QString cmd("cancel jobid=");
581 void JobList::graphTable()
585 pass.recordLimitCheck = limitCheckBox->checkState();
586 pass.daysLimitCheck = daysCheckBox->checkState();
587 pass.recordLimitSpin = limitSpinBox->value();
588 pass.daysLimitSpin = daysSpinBox->value();
589 pass.jobCombo = jobComboBox->currentText();
590 pass.clientCombo = clientComboBox->currentText();
591 pass.volumeCombo = volumeComboBox->currentText();
592 pass.fileSetCombo = fileSetComboBox->currentText();
593 pass.purgedCombo = purgedComboBox->currentText();
594 pass.levelCombo = levelComboBox->currentText();
595 pass.statusCombo = statusComboBox->currentText();
597 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
598 new JobPlot(pageSelectorTreeWidgetItem, pass);
603 * Save user settings associated with this page
605 void JobList::writeSettings()
607 QSettings settings(m_console->m_dir->name(), "bat");
608 settings.beginGroup(m_groupText);
609 settings.setValue(m_splitText, m_splitter->saveState());
614 * Read and restore user settings associated with this page
616 void JobList::readSettings()
618 m_groupText = "JobListPage";
619 m_splitText = "splitterSizes_1";
620 QSettings settings(m_console->m_dir->name(), "bat");
621 settings.beginGroup(m_groupText);
622 m_splitter->restoreState(settings.value(m_splitText).toByteArray());
627 * Function to fill m_selectedJobsCount and m_selectedJobs with selected values
629 void JobList::selectedJobsGet()
632 QList<QTableWidgetItem *> sitems = mp_tableWidget->selectedItems();
633 foreach (QTableWidgetItem *sitem, sitems) {
634 int row = sitem->row();
635 if (!rowList.contains(row)) {
642 foreach(int row, rowList) {
643 QTableWidgetItem * sitem = mp_tableWidget->item(row, m_jobIdIndex);
644 if (!first) m_selectedJobs.append(",");
646 m_selectedJobs.append(sitem->text());
648 m_selectedJobsCount = rowList.count();
649 if (m_selectedJobsCount > 1) {
650 QString text = QString( tr("Delete list of %1 Jobs")).arg(m_selectedJobsCount);
651 actionDeleteJob->setText(text);
653 actionDeleteJob->setText(tr("Delete Single Job"));