2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 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 John Walker.
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
34 #include <QAbstractEventDispatcher>
35 #include <QTableWidgetItem>
39 #include "joblog/joblog.h"
40 #include "jobgraphs/jobplot.h"
43 * Constructor for the class
45 JobList::JobList(const QString &mediaName, const QString &clientName,
46 const QString &jobName, const QString &filesetName, QTreeWidgetItem *parentTreeWidgetItem)
49 m_name = ""; /* treeWidgetName has a virtual override in this class */
50 m_mediaName = mediaName;
51 m_clientName = clientName;
53 m_filesetName = filesetName;
54 m_filesetName = filesetName;
55 pgInitialize(parentTreeWidgetItem);
56 QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
57 thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/emblem-system.png")));
62 if ((m_mediaName != "") || (m_clientName != "") || (m_jobName != "") || (m_filesetName != ""))
64 m_checkCurrentWidget = true;
67 /* Set Defaults for check and spin for limits */
68 limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
69 limitSpinBox->setValue(mainWin->m_recordLimitVal);
70 daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
71 daysSpinBox->setValue(mainWin->m_daysLimitVal);
74 QGridLayout *m_gridLayout = new QGridLayout(this);
75 m_gridLayout->setSpacing(6);
76 m_gridLayout->setMargin(9);
77 m_gridLayout->setObjectName(QString::fromUtf8("m_gridLayout"));
79 QSplitter *splitter_2 = new QSplitter(Qt::Vertical, this);
80 QScrollArea *area = new QScrollArea();
81 area->setObjectName(QString::fromUtf8("area"));
82 area->setWidget(frame);
83 area->setWidgetResizable(true);
84 splitter_2->addWidget(mp_tableWidget);
85 splitter_2->addWidget(area);
87 m_gridLayout->addWidget(splitter_2, 0, 0, 1, 1);
91 * The Meat of the class.
92 * This function will populate the QTableWidget, mp_tablewidget, with
93 * QTableWidgetItems representing the results of a query for what jobs exist on
94 * the media name passed from the constructor stored in m_mediaName.
96 void JobList::populateTable()
100 QBrush blackBrush(Qt::black);
102 if (!m_console->preventInUseConnect())
105 /* Can't do this in constructor because not neccesarily conected in constructor */
107 clientComboBox->addItem("Any");
108 clientComboBox->addItems(m_console->client_list);
109 int clientIndex = clientComboBox->findText(m_clientName, Qt::MatchExactly);
110 if (clientIndex != -1)
111 clientComboBox->setCurrentIndex(clientIndex);
113 QStringList volumeList;
114 m_console->getVolumeList(volumeList);
115 volumeComboBox->addItem("Any");
116 volumeComboBox->addItems(volumeList);
117 int volumeIndex = volumeComboBox->findText(m_mediaName, Qt::MatchExactly);
118 if (volumeIndex != -1) {
119 volumeComboBox->setCurrentIndex(volumeIndex);
121 jobComboBox->addItem("Any");
122 jobComboBox->addItems(m_console->job_list);
123 int jobIndex = jobComboBox->findText(m_jobName, Qt::MatchExactly);
124 if (jobIndex != -1) {
125 jobComboBox->setCurrentIndex(jobIndex);
127 levelComboBox->addItem("Any");
128 levelComboBox->addItems( QStringList() << "F" << "D" << "I");
129 purgedComboBox->addItem("Any");
130 purgedComboBox->addItems( QStringList() << "0" << "1");
131 fileSetComboBox->addItem("Any");
132 fileSetComboBox->addItems(m_console->fileset_list);
133 int filesetIndex = fileSetComboBox->findText(m_filesetName, Qt::MatchExactly);
134 if (filesetIndex != -1) {
135 fileSetComboBox->setCurrentIndex(filesetIndex);
137 QStringList statusLongList;
138 m_console->getStatusList(statusLongList);
139 statusComboBox->addItem("Any");
140 statusComboBox->addItems(statusLongList);
145 int volumeIndex = volumeComboBox->currentIndex();
146 if (volumeIndex != -1)
147 m_mediaName = volumeComboBox->itemText(volumeIndex);
148 query += "SELECT DISTINCT Job.Jobid AS Id, Job.Name AS JobName, Client.Name AS Client,"
149 " Job.Starttime AS JobStart, Job.Type AS JobType,"
150 " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
151 " Job.JobBytes AS Bytes,"
152 " Job.JobStatus AS Status, Status.JobStatusLong AS StatusLong,"
153 " Job.PurgedFiles AS Purged, FileSet.FileSet"
155 " LEFT OUTER JOIN Client ON (Client.ClientId=Job.ClientId)"
156 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)"
157 " LEFT OUTER JOIN Status ON (Job.JobStatus=Status.JobStatus)"
158 " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
159 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
160 QStringList conditions;
161 if (m_mediaName != "Any") {
162 conditions.append("Media.VolumeName='" + m_mediaName + "'");
164 int clientIndex = clientComboBox->currentIndex();
165 if (clientIndex != -1)
166 m_clientName = clientComboBox->itemText(clientIndex);
167 if (m_clientName != "Any") {
168 conditions.append("Client.Name='" + m_clientName + "'");
170 int jobIndex = jobComboBox->currentIndex();
172 m_jobName = jobComboBox->itemText(jobIndex);
173 if ((jobIndex != -1) && (jobComboBox->itemText(jobIndex) != "Any")) {
174 conditions.append("Job.Name='" + jobComboBox->itemText(jobIndex) + "'");
176 int levelIndex = levelComboBox->currentIndex();
177 if ((levelIndex != -1) && (levelComboBox->itemText(levelIndex) != "Any")) {
178 conditions.append("Job.Level='" + levelComboBox->itemText(levelIndex) + "'");
180 int statusIndex = statusComboBox->currentIndex();
181 if ((statusIndex != -1) && (statusComboBox->itemText(statusIndex) != "Any")) {
182 conditions.append("Status.JobStatusLong='" + statusComboBox->itemText(statusIndex) + "'");
184 int purgedIndex = purgedComboBox->currentIndex();
185 if ((purgedIndex != -1) && (purgedComboBox->itemText(purgedIndex) != "Any")) {
186 conditions.append("Job.PurgedFiles='" + purgedComboBox->itemText(purgedIndex) + "'");
188 int fileSetIndex = fileSetComboBox->currentIndex();
189 if (fileSetIndex != -1)
190 m_filesetName = fileSetComboBox->itemText(fileSetIndex);
191 if ((fileSetIndex != -1) && (fileSetComboBox->itemText(fileSetIndex) != "Any")) {
192 conditions.append("FileSet.FileSet='" + fileSetComboBox->itemText(fileSetIndex) + "'");
194 /* If Limit check box For limit by days is checked */
195 if (daysCheckBox->checkState() == Qt::Checked) {
196 QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
197 QString since = stamp.toString(Qt::ISODate);
198 conditions.append("Job.Starttime>'" + since + "'");
201 foreach (QString condition, conditions) {
203 query += " WHERE " + condition;
206 query += " AND " + condition;
210 query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
211 /* If Limit check box for limit records returned is checked */
212 if (limitCheckBox->checkState() == Qt::Checked) {
214 limit.setNum(limitSpinBox->value());
215 query += " LIMIT " + limit;
218 /* Set up the Header for the table */
219 QStringList headerlist = (QStringList()
220 << "Job Id" << "Job Name" << "Client" << "Job Starttime" << "Job Type"
221 << "Job Level" << "Job Files" << "Job Bytes" << "Job Status" << "Purged" << "File Set" );
222 m_purgedIndex = headerlist.indexOf("Purged");
223 m_typeIndex = headerlist.indexOf("Job Type");
224 m_statusIndex = headerlist.indexOf("Job Status");
225 m_startIndex = headerlist.indexOf("Job Starttime");
226 m_filesIndex = headerlist.indexOf("Job Files");
227 m_bytesIndex = headerlist.indexOf("Job Bytes");
229 /* Initialize the QTableWidget */
230 m_checkCurrentWidget = false;
231 mp_tableWidget->clear();
232 m_checkCurrentWidget = true;
233 mp_tableWidget->setColumnCount(headerlist.size());
234 mp_tableWidget->setHorizontalHeaderLabels(headerlist);
236 if (mainWin->m_sqlDebug) {
237 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
239 if (m_console->sql_cmd(query, results)) {
240 m_resultCount = results.count();
242 QTableWidgetItem* p_tableitem;
244 QStringList fieldlist;
245 mp_tableWidget->setRowCount(results.size());
248 /* Iterate through the record returned from the query */
249 foreach (resultline, results) {
250 fieldlist = resultline.split("\t");
252 bool m_statusIndexDone = false;
253 QString statusCode("");
254 /* Iterate through fields in the record */
255 foreach (field, fieldlist) {
256 field = field.trimmed(); /* strip leading & trailing spaces */
257 if ((column == m_statusIndex) && (!m_statusIndexDone)){
258 m_statusIndexDone = true;
261 p_tableitem = new QTableWidgetItem(field,1);
262 p_tableitem->setFlags(0);
263 p_tableitem->setForeground(blackBrush);
264 mp_tableWidget->setItem(row, column, p_tableitem);
265 if (column == m_statusIndex)
266 setStatusColor(p_tableitem, statusCode);
273 /* Resize the columns */
274 mp_tableWidget->resizeColumnsToContents();
275 mp_tableWidget->resizeRowsToContents();
276 mp_tableWidget->verticalHeader()->hide();
277 if ((m_mediaName != "Any") && (m_resultCount == 0)){
278 /* for context sensitive searches, let the user know if there were no
280 QMessageBox::warning(this, tr("Bat"),
281 tr("The Jobs query returned no results.\n"
282 "Press OK to continue?"), QMessageBox::Ok );
286 void JobList::setStatusColor(QTableWidgetItem *item, QString &field)
288 QString greenchars("TCR");
289 QString redchars("BEf");
290 QString yellowchars("eDAFSMmsjdctp");
291 if (greenchars.contains(field, Qt::CaseSensitive)) {
292 item->setBackground(Qt::green);
293 } else if (redchars.contains(field, Qt::CaseSensitive)) {
294 item->setBackground(Qt::red);
295 } else if (yellowchars.contains(field, Qt::CaseSensitive)){
296 item->setBackground(Qt::yellow);
301 * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
302 * The tree has been populated.
304 void JobList::PgSeltreeWidgetClicked()
313 * Virtual function override of pages function which is called when this page
314 * is visible on the stack
316 void JobList::currentStackItem()
325 * Virtual Function to return the name for the medialist tree widget
327 void JobList::treeWidgetName(QString &desc)
329 if ((m_mediaName == "") && (m_clientName == "") && (m_jobName == "") && (m_filesetName == "")) {
333 if (m_mediaName != "" ) {
334 desc += "of Volume " + m_mediaName;
336 if (m_clientName != "" ) {
337 desc += "of Client " + m_clientName;
339 if (m_jobName != "" ) {
340 desc += "of Job " + m_jobName;
342 if (m_filesetName != "" ) {
343 desc += "of fileset " + m_filesetName;
349 * This functions much line tableItemChanged for trees like the page selector,
350 * but I will do much less here
352 void JobList::tableItemChanged(QTableWidgetItem *currentItem, QTableWidgetItem * /*previousItem*/)
354 if (m_checkCurrentWidget) {
355 int row = currentItem->row();
356 QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
357 m_currentJob = jobitem->text();
359 /* include purged action or not */
360 jobitem = mp_tableWidget->item(row, m_purgedIndex);
361 QString purged = jobitem->text();
362 mp_tableWidget->removeAction(actionPurgeFiles);
364 mp_tableWidget->addAction(actionPurgeFiles);
366 /* include restore from time and job action or not */
367 jobitem = mp_tableWidget->item(row, m_typeIndex);
368 QString type = jobitem->text();
369 mp_tableWidget->removeAction(actionRestoreFromJob);
370 mp_tableWidget->removeAction(actionRestoreFromTime);
372 mp_tableWidget->addAction(actionRestoreFromJob);
373 mp_tableWidget->addAction(actionRestoreFromTime);
375 /* include cancel action or not */
376 jobitem = mp_tableWidget->item(row, m_statusIndex);
377 QString status = jobitem->text();
378 mp_tableWidget->removeAction(actionCancelJob);
379 if (status == "Running") {
380 mp_tableWidget->addAction(actionCancelJob);
386 * Function to create connections for context sensitive menu for this and
389 void JobList::createConnections()
391 /* connect to the action specific to this pages class that shows up in the
392 * page selector tree */
393 connect(actionRefreshJobList, SIGNAL(triggered()), this,
394 SLOT(populateTable()));
395 connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
396 connect(graphButton, SIGNAL(pressed()), this, SLOT(graphTable()));
397 /* for the tableItemChanged to maintain m_currentJob */
398 connect(mp_tableWidget, SIGNAL(
399 currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
400 this, SLOT(tableItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
402 /* Do what is required for the local context sensitive menu */
405 /* setContextMenuPolicy is required */
406 mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
409 mp_tableWidget->addAction(actionRefreshJobList);
410 mp_tableWidget->addAction(actionListJobid);
411 mp_tableWidget->addAction(actionListFilesOnJob);
412 mp_tableWidget->addAction(actionListJobMedia);
413 mp_tableWidget->addAction(actionListVolumes);
414 mp_tableWidget->addAction(actionDeleteJob);
415 mp_tableWidget->addAction(actionPurgeFiles);
416 mp_tableWidget->addAction(actionRestoreFromJob);
417 mp_tableWidget->addAction(actionRestoreFromTime);
418 mp_tableWidget->addAction(actionShowLogForJob);
420 /* Make Connections */
421 connect(actionListJobid, SIGNAL(triggered()), this,
422 SLOT(consoleListJobid()));
423 connect(actionListFilesOnJob, SIGNAL(triggered()), this,
424 SLOT(consoleListFilesOnJob()));
425 connect(actionListJobMedia, SIGNAL(triggered()), this,
426 SLOT(consoleListJobMedia()));
427 connect(actionListVolumes, SIGNAL(triggered()), this,
428 SLOT(consoleListVolumes()));
429 connect(actionDeleteJob, SIGNAL(triggered()), this,
430 SLOT(consoleDeleteJob()));
431 connect(actionPurgeFiles, SIGNAL(triggered()), this,
432 SLOT(consolePurgeFiles()));
433 connect(actionRestoreFromJob, SIGNAL(triggered()), this,
434 SLOT(preRestoreFromJob()));
435 connect(actionRestoreFromTime, SIGNAL(triggered()), this,
436 SLOT(preRestoreFromTime()));
437 connect(actionShowLogForJob, SIGNAL(triggered()), this,
438 SLOT(showLogForJob()));
439 connect(actionCancelJob, SIGNAL(triggered()), this,
440 SLOT(consoleCancelJob()));
441 connect(actionListJobTotals, SIGNAL(triggered()), this,
442 SLOT(consoleListJobTotals()));
444 m_contextActions.append(actionRefreshJobList);
445 m_contextActions.append(actionListJobTotals);
449 * Functions to respond to local context sensitive menu sending console commands
450 * If I could figure out how to make these one function passing a string, Yaaaaaa
452 void JobList::consoleListJobid()
454 QString cmd("list jobid=");
456 if (mainWin->m_longList) { cmd.prepend("l"); }
459 void JobList::consoleListFilesOnJob()
461 QString cmd("list files jobid=");
463 if (mainWin->m_longList) { cmd.prepend("l"); }
466 void JobList::consoleListJobMedia()
468 QString cmd("list jobmedia jobid=");
470 if (mainWin->m_longList) { cmd.prepend("l"); }
473 void JobList::consoleListVolumes()
475 QString cmd("list volumes jobid=");
477 if (mainWin->m_longList) { cmd.prepend("l"); }
480 void JobList::consoleListJobTotals()
482 QString cmd("list jobtotals");
484 if (mainWin->m_longList) { cmd.prepend("l"); }
487 void JobList::consoleDeleteJob()
489 if (QMessageBox::warning(this, tr("Bat"),
490 tr("Are you sure you want to delete?? !!!.\n"
491 "This delete command is used to delete a Job record and all associated catalog"
492 " records that were created. This command operates only on the Catalog"
493 " database and has no effect on the actual data written to a Volume. This"
494 " command can be dangerous and we strongly recommend that you do not use"
495 " it unless you know what you are doing. The Job and all its associated"
496 " records (File and JobMedia) will be deleted from the catalog."
497 "Press OK to proceed with delete operation.?"),
498 QMessageBox::Ok | QMessageBox::Cancel)
499 == QMessageBox::Cancel) { return; }
501 QString cmd("delete job jobid=");
505 void JobList::consolePurgeFiles()
507 if (QMessageBox::warning(this, tr("Bat"),
508 tr("Are you sure you want to purge ?? !!!.\n"
509 "The Purge command will delete associated Catalog database records from Jobs and"
510 " Volumes without considering the retention period. Purge works only on the"
511 " Catalog database and does not affect data written to Volumes. This command can"
512 " be dangerous because you can delete catalog records associated with current"
513 " backups of files, and we recommend that you do not use it unless you know what"
515 "Press OK to proceed with the purge operation?"),
516 QMessageBox::Ok | QMessageBox::Cancel)
517 == QMessageBox::Cancel) { return; }
519 QString cmd("purge files jobid=");
525 * Subroutine to call preRestore to restore from a select job
527 void JobList::preRestoreFromJob()
529 new prerestorePage(m_currentJob, R_JOBIDLIST);
533 * Subroutine to call preRestore to restore from a select job
535 void JobList::preRestoreFromTime()
537 new prerestorePage(m_currentJob, R_JOBDATETIME);
541 * Subroutine to call class to show the log in the database from that job
543 void JobList::showLogForJob()
545 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
546 new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
550 * Cancel a running job
552 void JobList::consoleCancelJob()
554 QString cmd("cancel jobid=");
562 void JobList::graphTable()
565 pass.recordLimitCheck = limitCheckBox->checkState();
566 pass.daysLimitCheck = daysCheckBox->checkState();
567 pass.recordLimitSpin = limitSpinBox->value();
568 pass.daysLimitSpin = daysSpinBox->value();
569 pass.jobCombo = jobComboBox->currentText();
570 pass.clientCombo = clientComboBox->currentText();
571 pass.volumeCombo = volumeComboBox->currentText();
572 pass.fileSetCombo = fileSetComboBox->currentText();
573 pass.purgedCombo = purgedComboBox->currentText();
574 pass.levelCombo = levelComboBox->currentText();
575 pass.statusCombo = statusComboBox->currentText();
577 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
578 new JobPlot(pageSelectorTreeWidgetItem, pass);