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.svg")));
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);
76 * The Meat of the class.
77 * This function will populate the QTableWidget, mp_tablewidget, with
78 * QTableWidgetItems representing the results of a query for what jobs exist on
79 * the media name passed from the constructor stored in m_mediaName.
81 void JobList::populateTable()
85 QBrush blackBrush(Qt::black);
87 if (!m_console->preventInUseConnect())
90 /* Can't do this in constructor because not neccesarily conected in constructor */
92 clientComboBox->addItem("Any");
93 clientComboBox->addItems(m_console->client_list);
94 int clientIndex = clientComboBox->findText(m_clientName, Qt::MatchExactly);
95 if (clientIndex != -1)
96 clientComboBox->setCurrentIndex(clientIndex);
98 QStringList volumeList;
99 m_console->getVolumeList(volumeList);
100 volumeComboBox->addItem("Any");
101 volumeComboBox->addItems(volumeList);
102 int volumeIndex = volumeComboBox->findText(m_mediaName, Qt::MatchExactly);
103 if (volumeIndex != -1) {
104 volumeComboBox->setCurrentIndex(volumeIndex);
106 jobComboBox->addItem("Any");
107 jobComboBox->addItems(m_console->job_list);
108 int jobIndex = jobComboBox->findText(m_jobName, Qt::MatchExactly);
109 if (jobIndex != -1) {
110 jobComboBox->setCurrentIndex(jobIndex);
112 levelComboBox->addItem("Any");
113 levelComboBox->addItems( QStringList() << "F" << "D" << "I");
114 purgedComboBox->addItem("Any");
115 purgedComboBox->addItems( QStringList() << "0" << "1");
116 fileSetComboBox->addItem("Any");
117 fileSetComboBox->addItems(m_console->fileset_list);
118 int filesetIndex = fileSetComboBox->findText(m_filesetName, Qt::MatchExactly);
119 if (filesetIndex != -1) {
120 fileSetComboBox->setCurrentIndex(filesetIndex);
122 QStringList statusLongList;
123 m_console->getStatusList(statusLongList);
124 statusComboBox->addItem("Any");
125 statusComboBox->addItems(statusLongList);
130 int volumeIndex = volumeComboBox->currentIndex();
131 if (volumeIndex != -1)
132 m_mediaName = volumeComboBox->itemText(volumeIndex);
133 query += "SELECT DISTINCT Job.Jobid AS Id, Job.Name AS JobName, Client.Name AS Client,"
134 " Job.Starttime AS JobStart, Job.Type AS JobType,"
135 " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
136 " Job.JobBytes AS Bytes,"
137 " Job.JobStatus AS Status, Status.JobStatusLong AS StatusLong,"
138 " Job.PurgedFiles AS Purged, FileSet.FileSet"
140 " LEFT OUTER JOIN Client ON (Client.ClientId=Job.ClientId)"
141 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)"
142 " LEFT OUTER JOIN Status ON (Job.JobStatus=Status.JobStatus)"
143 " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
144 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
145 QStringList conditions;
146 if (m_mediaName != "Any") {
147 conditions.append("Media.VolumeName='" + m_mediaName + "'");
149 int clientIndex = clientComboBox->currentIndex();
150 if (clientIndex != -1)
151 m_clientName = clientComboBox->itemText(clientIndex);
152 if (m_clientName != "Any") {
153 conditions.append("Client.Name='" + m_clientName + "'");
155 int jobIndex = jobComboBox->currentIndex();
157 m_jobName = jobComboBox->itemText(jobIndex);
158 if ((jobIndex != -1) && (jobComboBox->itemText(jobIndex) != "Any")) {
159 conditions.append("Job.Name='" + jobComboBox->itemText(jobIndex) + "'");
161 int levelIndex = levelComboBox->currentIndex();
162 if ((levelIndex != -1) && (levelComboBox->itemText(levelIndex) != "Any")) {
163 conditions.append("Job.Level='" + levelComboBox->itemText(levelIndex) + "'");
165 int statusIndex = statusComboBox->currentIndex();
166 if ((statusIndex != -1) && (statusComboBox->itemText(statusIndex) != "Any")) {
167 conditions.append("Status.JobStatusLong='" + statusComboBox->itemText(statusIndex) + "'");
169 int purgedIndex = purgedComboBox->currentIndex();
170 if ((purgedIndex != -1) && (purgedComboBox->itemText(purgedIndex) != "Any")) {
171 conditions.append("Job.PurgedFiles='" + purgedComboBox->itemText(purgedIndex) + "'");
173 int fileSetIndex = fileSetComboBox->currentIndex();
174 if (fileSetIndex != -1)
175 m_filesetName = fileSetComboBox->itemText(fileSetIndex);
176 if ((fileSetIndex != -1) && (fileSetComboBox->itemText(fileSetIndex) != "Any")) {
177 conditions.append("FileSet.FileSet='" + fileSetComboBox->itemText(fileSetIndex) + "'");
179 /* If Limit check box For limit by days is checked */
180 if (daysCheckBox->checkState() == Qt::Checked) {
181 QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
182 QString since = stamp.toString(Qt::ISODate);
183 conditions.append("Job.Starttime>'" + since + "'");
186 foreach (QString condition, conditions) {
188 query += " WHERE " + condition;
191 query += " AND " + condition;
195 query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
196 /* If Limit check box for limit records returned is checked */
197 if (limitCheckBox->checkState() == Qt::Checked) {
199 limit.setNum(limitSpinBox->value());
200 query += " LIMIT " + limit;
203 /* Set up the Header for the table */
204 QStringList headerlist = (QStringList()
205 << "Job Id" << "Job Name" << "Client" << "Job Starttime" << "Job Type"
206 << "Job Level" << "Job Files" << "Job Bytes" << "Job Status" << "Purged" << "File Set" );
207 m_purgedIndex = headerlist.indexOf("Purged");
208 m_typeIndex = headerlist.indexOf("Job Type");
209 m_statusIndex = headerlist.indexOf("Job Status");
210 m_startIndex = headerlist.indexOf("Job Starttime");
211 m_filesIndex = headerlist.indexOf("Job Files");
212 m_bytesIndex = headerlist.indexOf("Job Bytes");
214 /* Initialize the QTableWidget */
215 m_checkCurrentWidget = false;
216 mp_tableWidget->clear();
217 m_checkCurrentWidget = true;
218 mp_tableWidget->setColumnCount(headerlist.size());
219 mp_tableWidget->setHorizontalHeaderLabels(headerlist);
221 if (mainWin->m_sqlDebug) {
222 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
224 if (m_console->sql_cmd(query, results)) {
225 m_resultCount = results.count();
227 QTableWidgetItem* p_tableitem;
229 QStringList fieldlist;
230 mp_tableWidget->setRowCount(results.size());
233 /* Iterate through the record returned from the query */
234 foreach (resultline, results) {
235 fieldlist = resultline.split("\t");
237 bool m_statusIndexDone = false;
238 QString statusCode("");
239 /* Iterate through fields in the record */
240 foreach (field, fieldlist) {
241 field = field.trimmed(); /* strip leading & trailing spaces */
242 if ((column == m_statusIndex) && (!m_statusIndexDone)){
243 m_statusIndexDone = true;
246 p_tableitem = new QTableWidgetItem(field,1);
247 p_tableitem->setFlags(0);
248 p_tableitem->setForeground(blackBrush);
249 mp_tableWidget->setItem(row, column, p_tableitem);
250 if (column == m_statusIndex)
251 setStatusColor(p_tableitem, statusCode);
258 /* Resize the columns */
259 mp_tableWidget->resizeColumnsToContents();
260 mp_tableWidget->resizeRowsToContents();
261 mp_tableWidget->verticalHeader()->hide();
262 if ((m_mediaName != "Any") && (m_resultCount == 0)){
263 /* for context sensitive searches, let the user know if there were no
265 QMessageBox::warning(this, tr("Bat"),
266 tr("The Jobs query returned no results.\n"
267 "Press OK to continue?"), QMessageBox::Ok );
271 void JobList::setStatusColor(QTableWidgetItem *item, QString &field)
273 QString greenchars("TCR");
274 QString redchars("BEf");
275 QString yellowchars("eDAFSMmsjdctp");
276 if (greenchars.contains(field, Qt::CaseSensitive)) {
277 item->setBackground(Qt::green);
278 } else if (redchars.contains(field, Qt::CaseSensitive)) {
279 item->setBackground(Qt::red);
280 } else if (yellowchars.contains(field, Qt::CaseSensitive)){
281 item->setBackground(Qt::yellow);
286 * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
287 * The tree has been populated.
289 void JobList::PgSeltreeWidgetClicked()
298 * Virtual function override of pages function which is called when this page
299 * is visible on the stack
301 void JobList::currentStackItem()
310 * Virtual Function to return the name for the medialist tree widget
312 void JobList::treeWidgetName(QString &desc)
314 if ((m_mediaName == "") && (m_clientName == "") && (m_jobName == "") && (m_filesetName == "")) {
318 if (m_mediaName != "" ) {
319 desc += "of Volume " + m_mediaName;
321 if (m_clientName != "" ) {
322 desc += "of Client " + m_clientName;
324 if (m_jobName != "" ) {
325 desc += "of Job " + m_jobName;
327 if (m_filesetName != "" ) {
328 desc += "of fileset " + m_filesetName;
334 * This functions much line tableItemChanged for trees like the page selector,
335 * but I will do much less here
337 void JobList::tableItemChanged(QTableWidgetItem *currentItem, QTableWidgetItem * /*previousItem*/)
339 if (m_checkCurrentWidget) {
340 int row = currentItem->row();
341 QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
342 m_currentJob = jobitem->text();
344 /* include purged action or not */
345 jobitem = mp_tableWidget->item(row, m_purgedIndex);
346 QString purged = jobitem->text();
347 mp_tableWidget->removeAction(actionPurgeFiles);
349 mp_tableWidget->addAction(actionPurgeFiles);
351 /* include restore from time and job action or not */
352 jobitem = mp_tableWidget->item(row, m_typeIndex);
353 QString type = jobitem->text();
354 mp_tableWidget->removeAction(actionRestoreFromJob);
355 mp_tableWidget->removeAction(actionRestoreFromTime);
357 mp_tableWidget->addAction(actionRestoreFromJob);
358 mp_tableWidget->addAction(actionRestoreFromTime);
360 /* include cancel action or not */
361 jobitem = mp_tableWidget->item(row, m_statusIndex);
362 QString status = jobitem->text();
363 mp_tableWidget->removeAction(actionCancelJob);
364 if (status == "Running") {
365 mp_tableWidget->addAction(actionCancelJob);
371 * Function to create connections for context sensitive menu for this and
374 void JobList::createConnections()
376 /* connect to the action specific to this pages class that shows up in the
377 * page selector tree */
378 connect(actionRefreshJobList, SIGNAL(triggered()), this,
379 SLOT(populateTable()));
380 connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
381 connect(graphButton, SIGNAL(pressed()), this, SLOT(graphTable()));
382 /* for the tableItemChanged to maintain m_currentJob */
383 connect(mp_tableWidget, SIGNAL(
384 currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
385 this, SLOT(tableItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
387 /* Do what is required for the local context sensitive menu */
390 /* setContextMenuPolicy is required */
391 mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
394 mp_tableWidget->addAction(actionRefreshJobList);
395 mp_tableWidget->addAction(actionListJobid);
396 mp_tableWidget->addAction(actionListFilesOnJob);
397 mp_tableWidget->addAction(actionListJobMedia);
398 mp_tableWidget->addAction(actionListVolumes);
399 mp_tableWidget->addAction(actionDeleteJob);
400 mp_tableWidget->addAction(actionPurgeFiles);
401 mp_tableWidget->addAction(actionRestoreFromJob);
402 mp_tableWidget->addAction(actionRestoreFromTime);
403 mp_tableWidget->addAction(actionShowLogForJob);
405 /* Make Connections */
406 connect(actionListJobid, SIGNAL(triggered()), this,
407 SLOT(consoleListJobid()));
408 connect(actionListFilesOnJob, SIGNAL(triggered()), this,
409 SLOT(consoleListFilesOnJob()));
410 connect(actionListJobMedia, SIGNAL(triggered()), this,
411 SLOT(consoleListJobMedia()));
412 connect(actionListVolumes, SIGNAL(triggered()), this,
413 SLOT(consoleListVolumes()));
414 connect(actionDeleteJob, SIGNAL(triggered()), this,
415 SLOT(consoleDeleteJob()));
416 connect(actionPurgeFiles, SIGNAL(triggered()), this,
417 SLOT(consolePurgeFiles()));
418 connect(actionRestoreFromJob, SIGNAL(triggered()), this,
419 SLOT(preRestoreFromJob()));
420 connect(actionRestoreFromTime, SIGNAL(triggered()), this,
421 SLOT(preRestoreFromTime()));
422 connect(actionShowLogForJob, SIGNAL(triggered()), this,
423 SLOT(showLogForJob()));
424 connect(actionCancelJob, SIGNAL(triggered()), this,
425 SLOT(consoleCancelJob()));
426 connect(actionListJobTotals, SIGNAL(triggered()), this,
427 SLOT(consoleListJobTotals()));
429 m_contextActions.append(actionRefreshJobList);
430 m_contextActions.append(actionListJobTotals);
434 * Functions to respond to local context sensitive menu sending console commands
435 * If I could figure out how to make these one function passing a string, Yaaaaaa
437 void JobList::consoleListJobid()
439 QString cmd("list jobid=");
441 if (mainWin->m_longList) { cmd.prepend("l"); }
444 void JobList::consoleListFilesOnJob()
446 QString cmd("list files jobid=");
448 if (mainWin->m_longList) { cmd.prepend("l"); }
451 void JobList::consoleListJobMedia()
453 QString cmd("list jobmedia jobid=");
455 if (mainWin->m_longList) { cmd.prepend("l"); }
458 void JobList::consoleListVolumes()
460 QString cmd("list volumes jobid=");
462 if (mainWin->m_longList) { cmd.prepend("l"); }
465 void JobList::consoleListJobTotals()
467 QString cmd("list jobtotals");
469 if (mainWin->m_longList) { cmd.prepend("l"); }
472 void JobList::consoleDeleteJob()
474 if (QMessageBox::warning(this, tr("Bat"),
475 tr("Are you sure you want to delete?? !!!.\n"
476 "This delete command is used to delete a Job record and all associated catalog"
477 " records that were created. This command operates only on the Catalog"
478 " database and has no effect on the actual data written to a Volume. This"
479 " command can be dangerous and we strongly recommend that you do not use"
480 " it unless you know what you are doing. The Job and all its associated"
481 " records (File and JobMedia) will be deleted from the catalog."
482 "Press OK to proceed with delete operation.?"),
483 QMessageBox::Ok | QMessageBox::Cancel)
484 == QMessageBox::Cancel) { return; }
486 QString cmd("delete job jobid=");
490 void JobList::consolePurgeFiles()
492 if (QMessageBox::warning(this, tr("Bat"),
493 tr("Are you sure you want to purge ?? !!!.\n"
494 "The Purge command will delete associated Catalog database records from Jobs and"
495 " Volumes without considering the retention period. Purge works only on the"
496 " Catalog database and does not affect data written to Volumes. This command can"
497 " be dangerous because you can delete catalog records associated with current"
498 " backups of files, and we recommend that you do not use it unless you know what"
500 "Press OK to proceed with the purge operation?"),
501 QMessageBox::Ok | QMessageBox::Cancel)
502 == QMessageBox::Cancel) { return; }
504 QString cmd("purge files jobid=");
510 * Subroutine to call preRestore to restore from a select job
512 void JobList::preRestoreFromJob()
514 new prerestorePage(m_currentJob, R_JOBIDLIST);
518 * Subroutine to call preRestore to restore from a select job
520 void JobList::preRestoreFromTime()
522 new prerestorePage(m_currentJob, R_JOBDATETIME);
526 * Subroutine to call class to show the log in the database from that job
528 void JobList::showLogForJob()
530 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
531 new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
535 * Cancel a running job
537 void JobList::consoleCancelJob()
539 QString cmd("cancel jobid=");
547 void JobList::graphTable()
550 pass.recordLimitCheck = limitCheckBox->checkState();
551 pass.daysLimitCheck = daysCheckBox->checkState();
552 pass.recordLimitSpin = limitSpinBox->value();
553 pass.daysLimitSpin = daysSpinBox->value();
554 pass.jobCombo = jobComboBox->currentText();
555 pass.clientCombo = clientComboBox->currentText();
556 pass.volumeCombo = volumeComboBox->currentText();
557 pass.fileSetCombo = fileSetComboBox->currentText();
558 pass.purgedCombo = purgedComboBox->currentText();
559 pass.levelCombo = levelComboBox->currentText();
560 pass.statusCombo = statusComboBox->currentText();
562 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
563 new JobPlot(pageSelectorTreeWidgetItem, pass);