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 plus additions
11 that are listed in the file LICENSE.
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.
29 * Version $Id: joblist.h 4230 2007-02-21 20:07:37Z kerns $
31 * Dirk Bartley, March 2007
34 #include <QAbstractEventDispatcher>
35 #include <QTableWidgetItem>
39 #include "joblog/joblog.h"
42 * Constructor for the class
44 JobList::JobList(const QString &mediaName, const QString &clientName,
45 const QString &jobName, const QString &filesetName, QTreeWidgetItem *parentTreeWidgetItem)
48 m_name = ""; /* treeWidgetName has a virtual override in this class */
49 m_mediaName = mediaName;
50 m_clientName = clientName;
52 m_filesetName = filesetName;
53 m_filesetName = filesetName;
54 pgInitialize(parentTreeWidgetItem);
55 QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
56 thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/emblem-system.svg")));
61 if ((m_mediaName != "") || (m_clientName != "") || (m_jobName != "") || (m_filesetName != ""))
63 m_checkCurrentWidget = true;
66 /* Set Defaults for check and spin for limits */
67 limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
68 limitSpinBox->setValue(mainWin->m_recordLimitVal);
69 daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
70 daysSpinBox->setValue(mainWin->m_daysLimitVal);
75 * The Meat of the class.
76 * This function will populate the QTableWidget, mp_tablewidget, with
77 * QTableWidgetItems representing the results of a query for what jobs exist on
78 * the media name passed from the constructor stored in m_mediaName.
80 void JobList::populateTable()
84 QBrush blackBrush(Qt::black);
86 if (!m_console->preventInUseConnect())
89 /* Can't do this in constructor because not neccesarily conected in constructor */
91 clientsComboBox->addItem("Any");
92 clientsComboBox->addItems(m_console->client_list);
93 int clientIndex = clientsComboBox->findText(m_clientName, Qt::MatchExactly);
94 if (clientIndex != -1)
95 clientsComboBox->setCurrentIndex(clientIndex);
97 QString query("SELECT VolumeName AS Media FROM Media ORDER BY Media");
98 if (mainWin->m_sqlDebug) {
99 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
101 QStringList results, volumeList;
102 if (m_console->sql_cmd(query, results)) {
104 QStringList fieldlist;
105 /* Iterate through the lines of results. */
106 foreach (QString resultline, results) {
107 fieldlist = resultline.split("\t");
108 volumeList.append(fieldlist[0]);
109 } /* foreach resultline */
110 } /* if results from query */
111 volumeComboBox->addItem("Any");
112 volumeComboBox->addItems(volumeList);
113 int volumeIndex = volumeComboBox->findText(m_mediaName, Qt::MatchExactly);
114 if (volumeIndex != -1) {
115 volumeComboBox->setCurrentIndex(volumeIndex);
117 jobComboBox->addItem("Any");
118 jobComboBox->addItems(m_console->job_list);
119 int jobIndex = jobComboBox->findText(m_jobName, Qt::MatchExactly);
120 if (jobIndex != -1) {
121 jobComboBox->setCurrentIndex(jobIndex);
123 levelComboBox->addItem("Any");
124 levelComboBox->addItems( QStringList() << "F" << "D" << "I");
125 purgedComboBox->addItem("Any");
126 purgedComboBox->addItems( QStringList() << "0" << "1");
127 statusComboBox->addItem("Any");
128 fileSetComboBox->addItem("Any");
129 fileSetComboBox->addItems(m_console->fileset_list);
130 int filesetIndex = fileSetComboBox->findText(m_filesetName, Qt::MatchExactly);
131 if (filesetIndex != -1) {
132 fileSetComboBox->setCurrentIndex(filesetIndex);
134 QString statusQuery("SELECT JobStatusLong FROM Status");
135 if (mainWin->m_sqlDebug) {
136 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
138 QStringList statusResults, statusLongList;
139 if (m_console->sql_cmd(statusQuery, statusResults)) {
141 QStringList fieldlist;
142 /* Iterate through the lines of results. */
143 foreach (QString resultline, statusResults) {
144 fieldlist = resultline.split("\t");
145 statusLongList.append(fieldlist[0]);
146 } /* foreach resultline */
147 } /* if results from statusquery */
148 statusComboBox->addItems(statusLongList);
153 int volumeIndex = volumeComboBox->currentIndex();
154 if (volumeIndex != -1)
155 m_mediaName = volumeComboBox->itemText(volumeIndex);
156 query += "SELECT DISTINCT Job.Jobid AS Id, Job.Name AS JobName, Client.Name AS Client,"
157 " Job.Starttime AS JobStart, Job.Type AS JobType,"
158 " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
159 " Job.JobBytes AS Bytes,"
160 " Job.JobStatus AS Status, Status.JobStatusLong AS StatusLong,"
161 " Job.PurgedFiles AS Purged, FileSet.FileSet"
163 " LEFT OUTER JOIN Client ON (Client.ClientId=Job.ClientId)"
164 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)"
165 " LEFT OUTER JOIN Status ON (Job.JobStatus=Status.JobStatus)"
166 " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
167 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
168 QStringList conditions;
169 if (m_mediaName != "Any") {
170 conditions.append("Media.VolumeName='" + m_mediaName + "'");
172 int clientIndex = clientsComboBox->currentIndex();
173 if (clientIndex != -1)
174 m_clientName = clientsComboBox->itemText(clientIndex);
175 if (m_clientName != "Any") {
176 conditions.append("Client.Name='" + m_clientName + "'");
178 int jobIndex = jobComboBox->currentIndex();
180 m_jobName = jobComboBox->itemText(jobIndex);
181 if ((jobIndex != -1) && (jobComboBox->itemText(jobIndex) != "Any")) {
182 conditions.append("Job.Name='" + jobComboBox->itemText(jobIndex) + "'");
184 int levelIndex = levelComboBox->currentIndex();
185 if ((levelIndex != -1) && (levelComboBox->itemText(levelIndex) != "Any")) {
186 conditions.append("Job.Level='" + levelComboBox->itemText(levelIndex) + "'");
188 int statusIndex = statusComboBox->currentIndex();
189 if ((statusIndex != -1) && (statusComboBox->itemText(statusIndex) != "Any")) {
190 conditions.append("Status.JobStatusLong='" + statusComboBox->itemText(statusIndex) + "'");
192 int purgedIndex = purgedComboBox->currentIndex();
193 if ((purgedIndex != -1) && (purgedComboBox->itemText(purgedIndex) != "Any")) {
194 conditions.append("Job.PurgedFiles='" + purgedComboBox->itemText(purgedIndex) + "'");
196 int fileSetIndex = fileSetComboBox->currentIndex();
197 if (fileSetIndex != -1)
198 m_filesetName = fileSetComboBox->itemText(fileSetIndex);
199 if ((fileSetIndex != -1) && (fileSetComboBox->itemText(fileSetIndex) != "Any")) {
200 conditions.append("FileSet.FileSet='" + fileSetComboBox->itemText(fileSetIndex) + "'");
202 /* If Limit check box For limit by days is checked */
203 if (daysCheckBox->checkState() == Qt::Checked) {
204 QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
205 QString since = stamp.toString(Qt::ISODate);
206 conditions.append("Job.Starttime>'" + since + "'");
209 foreach (QString condition, conditions) {
211 query += " WHERE " + condition;
214 query += " AND " + condition;
218 query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
219 /* If Limit check box for limit records returned is checked */
220 if (limitCheckBox->checkState() == Qt::Checked) {
222 limit.setNum(limitSpinBox->value());
223 query += " LIMIT " + limit;
226 /* Set up the Header for the table */
227 QStringList headerlist = (QStringList()
228 << "Job Id" << "Job Name" << "Client" << "Job Starttime" << "Job Type"
229 << "Job Level" << "Job Files" << "Job Bytes" << "Job Status" << "Purged" << "File Set" );
230 m_purgedIndex = headerlist.indexOf("Purged");
231 m_typeIndex = headerlist.indexOf("Job Type");
232 m_statusIndex = headerlist.indexOf("Job Status");
234 /* Initialize the QTableWidget */
235 m_checkCurrentWidget = false;
236 mp_tableWidget->clear();
237 m_checkCurrentWidget = true;
238 mp_tableWidget->setColumnCount(headerlist.size());
239 mp_tableWidget->setHorizontalHeaderLabels(headerlist);
241 if (mainWin->m_sqlDebug) {
242 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
244 if (m_console->sql_cmd(query, results)) {
245 m_resultCount = results.count();
247 QTableWidgetItem* p_tableitem;
249 QStringList fieldlist;
250 mp_tableWidget->setRowCount(results.size());
253 /* Iterate through the record returned from the query */
254 foreach (resultline, results) {
255 fieldlist = resultline.split("\t");
257 bool m_statusIndexDone = false;
258 QString statusCode("");
259 /* Iterate through fields in the record */
260 foreach (field, fieldlist) {
261 field = field.trimmed(); /* strip leading & trailing spaces */
262 if ((column == m_statusIndex) && (!m_statusIndexDone)){
263 m_statusIndexDone = true;
266 p_tableitem = new QTableWidgetItem(field,1);
267 p_tableitem->setFlags(0);
268 p_tableitem->setForeground(blackBrush);
269 mp_tableWidget->setItem(row, column, p_tableitem);
270 if (column == m_statusIndex)
271 setStatusColor(p_tableitem, statusCode);
278 /* Resize the columns */
279 mp_tableWidget->resizeColumnsToContents();
280 mp_tableWidget->resizeRowsToContents();
281 mp_tableWidget->verticalHeader()->hide();
282 if ((m_mediaName != "Any") && (m_resultCount == 0)){
283 /* for context sensitive searches, let the user know if there were no
285 QMessageBox::warning(this, tr("Bat"),
286 tr("The Jobs query returned no results.\n"
287 "Press OK to continue?"), QMessageBox::Ok );
291 void JobList::setStatusColor(QTableWidgetItem *item, QString &field)
293 QString greenchars("TCR");
294 QString redchars("BEf");
295 QString yellowchars("eDAFSMmsjdctp");
296 if (greenchars.contains(field, Qt::CaseSensitive)) {
297 item->setBackground(Qt::green);
298 } else if (redchars.contains(field, Qt::CaseSensitive)) {
299 item->setBackground(Qt::red);
300 } else if (yellowchars.contains(field, Qt::CaseSensitive)){
301 item->setBackground(Qt::yellow);
306 * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
307 * The tree has been populated.
309 void JobList::PgSeltreeWidgetClicked()
318 * Virtual function override of pages function which is called when this page
319 * is visible on the stack
321 void JobList::currentStackItem()
330 * Virtual Function to return the name for the medialist tree widget
332 void JobList::treeWidgetName(QString &desc)
334 if ((m_mediaName == "") && (m_clientName == "") && (m_jobName == "") && (m_filesetName == "")) {
338 if (m_mediaName != "" ) {
339 desc += "of Volume " + m_mediaName;
341 if (m_clientName != "" ) {
342 desc += "of Client " + m_clientName;
344 if (m_jobName != "" ) {
345 desc += "of Job " + m_jobName;
347 if (m_filesetName != "" ) {
348 desc += "of fileset " + m_filesetName;
354 * This functions much line tableItemChanged for trees like the page selector,
355 * but I will do much less here
357 void JobList::tableItemChanged(QTableWidgetItem *currentItem, QTableWidgetItem * /*previousItem*/)
359 if (m_checkCurrentWidget) {
360 int row = currentItem->row();
361 QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
362 m_currentJob = jobitem->text();
364 /* include purged action or not */
365 jobitem = mp_tableWidget->item(row, m_purgedIndex);
366 QString purged = jobitem->text();
367 mp_tableWidget->removeAction(actionPurgeFiles);
369 mp_tableWidget->addAction(actionPurgeFiles);
371 /* include restore from time and job action or not */
372 jobitem = mp_tableWidget->item(row, m_typeIndex);
373 QString type = jobitem->text();
374 mp_tableWidget->removeAction(actionRestoreFromJob);
375 mp_tableWidget->removeAction(actionRestoreFromTime);
377 mp_tableWidget->addAction(actionRestoreFromJob);
378 mp_tableWidget->addAction(actionRestoreFromTime);
380 /* include cancel action or not */
381 jobitem = mp_tableWidget->item(row, m_statusIndex);
382 QString status = jobitem->text();
383 mp_tableWidget->removeAction(actionCancelJob);
384 if (status == "Running") {
385 mp_tableWidget->addAction(actionCancelJob);
391 * Function to create connections for context sensitive menu for this and
394 void JobList::createConnections()
396 /* connect to the action specific to this pages class that shows up in the
397 * page selector tree */
398 connect(actionRefreshJobList, SIGNAL(triggered()), this,
399 SLOT(populateTable()));
400 connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
401 /* for the tableItemChanged to maintain m_currentJob */
402 connect(mp_tableWidget, SIGNAL(
403 currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
404 this, SLOT(tableItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
406 /* Do what is required for the local context sensitive menu */
409 /* setContextMenuPolicy is required */
410 mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
413 mp_tableWidget->addAction(actionRefreshJobList);
414 mp_tableWidget->addAction(actionListJobid);
415 mp_tableWidget->addAction(actionListFilesOnJob);
416 mp_tableWidget->addAction(actionListJobMedia);
417 mp_tableWidget->addAction(actionListVolumes);
418 mp_tableWidget->addAction(actionDeleteJob);
419 mp_tableWidget->addAction(actionPurgeFiles);
420 mp_tableWidget->addAction(actionRestoreFromJob);
421 mp_tableWidget->addAction(actionRestoreFromTime);
422 mp_tableWidget->addAction(actionShowLogForJob);
424 /* Make Connections */
425 connect(actionListJobid, SIGNAL(triggered()), this,
426 SLOT(consoleListJobid()));
427 connect(actionListFilesOnJob, SIGNAL(triggered()), this,
428 SLOT(consoleListFilesOnJob()));
429 connect(actionListJobMedia, SIGNAL(triggered()), this,
430 SLOT(consoleListJobMedia()));
431 connect(actionListVolumes, SIGNAL(triggered()), this,
432 SLOT(consoleListVolumes()));
433 connect(actionDeleteJob, SIGNAL(triggered()), this,
434 SLOT(consoleDeleteJob()));
435 connect(actionPurgeFiles, SIGNAL(triggered()), this,
436 SLOT(consolePurgeFiles()));
437 connect(actionRestoreFromJob, SIGNAL(triggered()), this,
438 SLOT(preRestoreFromJob()));
439 connect(actionRestoreFromTime, SIGNAL(triggered()), this,
440 SLOT(preRestoreFromTime()));
441 connect(actionShowLogForJob, SIGNAL(triggered()), this,
442 SLOT(showLogForJob()));
443 connect(actionCancelJob, SIGNAL(triggered()), this,
444 SLOT(consoleCancelJob()));
445 connect(actionListJobTotals, SIGNAL(triggered()), this,
446 SLOT(consoleListJobTotals()));
448 m_contextActions.append(actionRefreshJobList);
449 m_contextActions.append(actionListJobTotals);
453 * Functions to respond to local context sensitive menu sending console commands
454 * If I could figure out how to make these one function passing a string, Yaaaaaa
456 void JobList::consoleListJobid()
458 QString cmd("list jobid=");
460 if (mainWin->m_longList) { cmd.prepend("l"); }
463 void JobList::consoleListFilesOnJob()
465 QString cmd("list files jobid=");
467 if (mainWin->m_longList) { cmd.prepend("l"); }
470 void JobList::consoleListJobMedia()
472 QString cmd("list jobmedia jobid=");
474 if (mainWin->m_longList) { cmd.prepend("l"); }
477 void JobList::consoleListVolumes()
479 QString cmd("list volumes jobid=");
481 if (mainWin->m_longList) { cmd.prepend("l"); }
484 void JobList::consoleListJobTotals()
486 QString cmd("list jobtotals");
488 if (mainWin->m_longList) { cmd.prepend("l"); }
491 void JobList::consoleDeleteJob()
493 if (QMessageBox::warning(this, tr("Bat"),
494 tr("Are you sure you want to delete?? !!!.\n"
495 "This delete command is used to delete a Job record and all associated catalog"
496 " records that were created. This command operates only on the Catalog"
497 " database and has no effect on the actual data written to a Volume. This"
498 " command can be dangerous and we strongly recommend that you do not use"
499 " it unless you know what you are doing. The Job and all its associated"
500 " records (File and JobMedia) will be deleted from the catalog."
501 "Press OK to proceed with delete operation.?"),
502 QMessageBox::Ok | QMessageBox::Cancel)
503 == QMessageBox::Cancel) { return; }
505 QString cmd("delete job jobid=");
509 void JobList::consolePurgeFiles()
511 if (QMessageBox::warning(this, tr("Bat"),
512 tr("Are you sure you want to purge ?? !!!.\n"
513 "The Purge command will delete associated Catalog database records from Jobs and"
514 " Volumes without considering the retention period. Purge works only on the"
515 " Catalog database and does not affect data written to Volumes. This command can"
516 " be dangerous because you can delete catalog records associated with current"
517 " backups of files, and we recommend that you do not use it unless you know what"
519 "Press OK to proceed with the purge operation?"),
520 QMessageBox::Ok | QMessageBox::Cancel)
521 == QMessageBox::Cancel) { return; }
523 QString cmd("purge files jobid=");
529 * Subroutine to call preRestore to restore from a select job
531 void JobList::preRestoreFromJob()
533 new prerestorePage(m_currentJob, R_JOBIDLIST);
537 * Subroutine to call preRestore to restore from a select job
539 void JobList::preRestoreFromTime()
541 new prerestorePage(m_currentJob, R_JOBDATETIME);
545 * Subroutine to call class to show the log in the database from that job
547 void JobList::showLogForJob()
549 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
550 new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
554 * Cancel a running job
556 void JobList::consoleCancelJob()
558 QString cmd("cancel jobid=");