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(QString &mediaName, QString &clientname,
45 QTreeWidgetItem *parentTreeWidgetItem)
49 m_mediaName = mediaName;
50 m_clientName = clientname;
51 pgInitialize(parentTreeWidgetItem);
52 QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
53 thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/emblem-system.svg")));
58 if ((m_mediaName != "") || (m_clientName != "")) { m_closeable=true; }
59 m_checkCurrentWidget = true;
62 /* Set Defaults for check and spin for limits */
63 limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
64 limitSpinBox->setValue(mainWin->m_recordLimitVal);
65 daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
66 daysSpinBox->setValue(mainWin->m_daysLimitVal);
70 * The Meat of the class.
71 * This function will populate the QTableWidget, mp_tablewidget, with
72 * QTableWidgetItems representing the results of a query for what jobs exist on
73 * the media name passed from the constructor stored in m_mediaName.
75 void JobList::populateTable()
79 QBrush blackBrush(Qt::black);
81 if (!m_console->preventInUseConnect())
84 /* Can't do this in constructor because not neccesarily conected in constructor */
86 clientsComboBox->addItem("Any");
87 clientsComboBox->addItems(m_console->client_list);
88 int clientIndex = clientsComboBox->findText(m_clientName, Qt::MatchExactly);
89 if (clientIndex != -1)
90 clientsComboBox->setCurrentIndex(clientIndex);
92 QString query("SELECT VolumeName AS Media FROM Media ORDER BY Media");
93 if (mainWin->m_sqlDebug) {
94 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
96 QStringList results, volumeList;
97 if (m_console->sql_cmd(query, results)) {
99 QStringList fieldlist;
100 /* Iterate through the lines of results. */
101 foreach (QString resultline, results) {
102 fieldlist = resultline.split("\t");
103 volumeList.append(fieldlist[0]);
104 } /* foreach resultline */
105 } /* if results from query */
106 volumeComboBox->addItem("Any");
107 volumeComboBox->addItems(volumeList);
108 int volumeIndex = volumeComboBox->findText(m_mediaName, Qt::MatchExactly);
109 if (volumeIndex != -1) {
110 volumeComboBox->setCurrentIndex(volumeIndex);
112 jobComboBox->addItem("Any");
113 jobComboBox->addItems(m_console->job_list);
114 levelComboBox->addItem("Any");
115 levelComboBox->addItems( QStringList() << "F" << "D" << "I");
116 purgedComboBox->addItem("Any");
117 purgedComboBox->addItems( QStringList() << "0" << "1");
118 statusComboBox->addItem("Any");
119 fileSetComboBox->addItem("Any");
120 fileSetComboBox->addItems(m_console->fileset_list);
121 QString statusQuery("SELECT JobStatusLong FROM Status");
122 if (mainWin->m_sqlDebug) {
123 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
125 QStringList statusResults, statusLongList;
126 if (m_console->sql_cmd(statusQuery, statusResults)) {
128 QStringList fieldlist;
129 /* Iterate through the lines of results. */
130 foreach (QString resultline, statusResults) {
131 fieldlist = resultline.split("\t");
132 statusLongList.append(fieldlist[0]);
133 } /* foreach resultline */
134 } /* if results from statusquery */
135 statusComboBox->addItems(statusLongList);
140 int volumeIndex = volumeComboBox->currentIndex();
141 if (volumeIndex != -1)
142 m_mediaName = volumeComboBox->itemText(volumeIndex);
143 query += "SELECT DISTINCT Job.Jobid AS Id, Job.Name AS JobName, Client.Name AS Client,"
144 " Job.Starttime AS JobStart, Job.Type AS JobType,"
145 " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
146 " Job.JobBytes AS Bytes,"
147 " Job.JobStatus AS Status, Status.JobStatusLong AS StatusLong,"
148 " Job.PurgedFiles AS Purged, FileSet.FileSet"
150 " LEFT OUTER JOIN Client ON (Client.ClientId=Job.ClientId)"
151 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)"
152 " LEFT OUTER JOIN Status ON (Job.JobStatus=Status.JobStatus)"
153 " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
154 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
155 QStringList conditions;
156 if (m_mediaName != "Any") {
157 conditions.append("Media.VolumeName='" + m_mediaName + "'");
159 int clientIndex = clientsComboBox->currentIndex();
160 if (clientIndex != -1)
161 m_clientName = clientsComboBox->itemText(clientIndex);
162 if (m_clientName != "Any") {
163 conditions.append("Client.Name='" + m_clientName + "'");
165 int jobIndex = jobComboBox->currentIndex();
166 if ((jobIndex != -1) && (jobComboBox->itemText(jobIndex) != "Any")) {
167 conditions.append("Job.Name='" + jobComboBox->itemText(jobIndex) + "'");
169 int levelIndex = levelComboBox->currentIndex();
170 if ((levelIndex != -1) && (levelComboBox->itemText(levelIndex) != "Any")) {
171 conditions.append("Job.Level='" + levelComboBox->itemText(levelIndex) + "'");
173 int statusIndex = statusComboBox->currentIndex();
174 if ((statusIndex != -1) && (statusComboBox->itemText(statusIndex) != "Any")) {
175 conditions.append("Status.JobStatusLong='" + statusComboBox->itemText(statusIndex) + "'");
177 int purgedIndex = purgedComboBox->currentIndex();
178 if ((purgedIndex != -1) && (purgedComboBox->itemText(purgedIndex) != "Any")) {
179 conditions.append("Job.PurgedFiles='" + purgedComboBox->itemText(purgedIndex) + "'");
181 int fileSetIndex = fileSetComboBox->currentIndex();
182 if ((fileSetIndex != -1) && (fileSetComboBox->itemText(fileSetIndex) != "Any")) {
183 conditions.append("FileSet.FileSet='" + fileSetComboBox->itemText(fileSetIndex) + "'");
185 /* If Limit check box For limit by days is checked */
186 if (daysCheckBox->checkState() == Qt::Checked) {
187 QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
188 QString since = stamp.toString(Qt::ISODate);
189 conditions.append("Job.Starttime>'" + since + "'");
192 foreach (QString condition, conditions) {
194 query += " WHERE " + condition;
197 query += " AND " + condition;
201 query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
202 /* If Limit check box for limit records returned is checked */
203 if (limitCheckBox->checkState() == Qt::Checked) {
205 limit.setNum(limitSpinBox->value());
206 query += " LIMIT " + limit;
209 /* Set up the Header for the table */
210 QStringList headerlist = (QStringList()
211 << "Job Id" << "Job Name" << "Client" << "Job Starttime" << "Job Type"
212 << "Job Level" << "Job Files" << "Job Bytes" << "Job Status" << "Purged" << "File Set" );
213 m_purgedIndex = headerlist.indexOf("Purged");
214 m_typeIndex = headerlist.indexOf("Job Type");
215 m_statusIndex = headerlist.indexOf("Job Status");
217 /* Initialize the QTableWidget */
218 m_checkCurrentWidget = false;
219 mp_tableWidget->clear();
220 m_checkCurrentWidget = true;
221 mp_tableWidget->setColumnCount(headerlist.size());
222 mp_tableWidget->setHorizontalHeaderLabels(headerlist);
224 if (mainWin->m_sqlDebug) {
225 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
227 if (m_console->sql_cmd(query, results)) {
228 m_resultCount = results.count();
230 QTableWidgetItem* p_tableitem;
232 QStringList fieldlist;
233 mp_tableWidget->setRowCount(results.size());
236 /* Iterate through the record returned from the query */
237 foreach (resultline, results) {
238 fieldlist = resultline.split("\t");
240 bool m_statusIndexDone = false;
241 QString statusCode("");
242 /* Iterate through fields in the record */
243 foreach (field, fieldlist) {
244 field = field.trimmed(); /* strip leading & trailing spaces */
245 if ((column == m_statusIndex) && (!m_statusIndexDone)){
246 m_statusIndexDone = true;
249 p_tableitem = new QTableWidgetItem(field,1);
250 p_tableitem->setFlags(0);
251 p_tableitem->setForeground(blackBrush);
252 mp_tableWidget->setItem(row, column, p_tableitem);
253 if (column == m_statusIndex)
254 setStatusColor(p_tableitem, statusCode);
261 /* Resize the columns */
262 mp_tableWidget->resizeColumnsToContents();
263 mp_tableWidget->resizeRowsToContents();
264 mp_tableWidget->verticalHeader()->hide();
265 if ((m_mediaName != "Any") && (m_resultCount == 0)){
266 /* for context sensitive searches, let the user know if there were no
268 QMessageBox::warning(this, tr("Bat"),
269 tr("The Jobs query returned no results.\n"
270 "Press OK to continue?"), QMessageBox::Ok );
274 void JobList::setStatusColor(QTableWidgetItem *item, QString &field)
276 QString greenchars("TCR");
277 QString redchars("BEf");
278 QString yellowchars("eDAFSMmsjdctp");
279 if (greenchars.contains(field, Qt::CaseSensitive)) {
280 item->setBackground(Qt::green);
281 } else if (redchars.contains(field, Qt::CaseSensitive)) {
282 item->setBackground(Qt::red);
283 } else if (yellowchars.contains(field, Qt::CaseSensitive)){
284 item->setBackground(Qt::yellow);
289 * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
290 * The tree has been populated.
292 void JobList::PgSeltreeWidgetClicked()
301 * Virtual function override of pages function which is called when this page
302 * is visible on the stack
304 void JobList::currentStackItem()
313 * Virtual Function to return the name for the medialist tree widget
315 void JobList::treeWidgetName(QString &desc)
317 if ((m_mediaName == "") && (m_clientName == "")) {
321 if (m_mediaName != "" ) {
322 desc += "on Volume " + m_mediaName;
324 if (m_clientName != "" ) {
325 desc += "of Client " + m_clientName;
331 * This functions much line tableItemChanged for trees like the page selector,
332 * but I will do much less here
334 void JobList::tableItemChanged(QTableWidgetItem *currentItem, QTableWidgetItem * /*previousItem*/)
336 if (m_checkCurrentWidget) {
337 int row = currentItem->row();
338 QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
339 m_currentJob = jobitem->text();
341 /* include purged action or not */
342 jobitem = mp_tableWidget->item(row, m_purgedIndex);
343 QString purged = jobitem->text();
344 mp_tableWidget->removeAction(actionPurgeFiles);
346 mp_tableWidget->addAction(actionPurgeFiles);
348 /* include restore from time and job action or not */
349 jobitem = mp_tableWidget->item(row, m_typeIndex);
350 QString type = jobitem->text();
351 mp_tableWidget->removeAction(actionRestoreFromJob);
352 mp_tableWidget->removeAction(actionRestoreFromTime);
354 mp_tableWidget->addAction(actionRestoreFromJob);
355 mp_tableWidget->addAction(actionRestoreFromTime);
357 /* include cancel action or not */
358 jobitem = mp_tableWidget->item(row, m_statusIndex);
359 QString status = jobitem->text();
360 mp_tableWidget->removeAction(actionCancelJob);
361 if (status == "Running") {
362 mp_tableWidget->addAction(actionCancelJob);
368 * Function to create connections for context sensitive menu for this and
371 void JobList::createConnections()
373 /* connect to the action specific to this pages class that shows up in the
374 * page selector tree */
375 connect(actionRefreshJobList, SIGNAL(triggered()), this,
376 SLOT(populateTable()));
377 connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
378 /* for the tableItemChanged to maintain m_currentJob */
379 connect(mp_tableWidget, SIGNAL(
380 currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
381 this, SLOT(tableItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
383 /* Do what is required for the local context sensitive menu */
386 /* setContextMenuPolicy is required */
387 mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
390 mp_tableWidget->addAction(actionRefreshJobList);
391 mp_tableWidget->addAction(actionListJobid);
392 mp_tableWidget->addAction(actionListFilesOnJob);
393 mp_tableWidget->addAction(actionListJobMedia);
394 mp_tableWidget->addAction(actionListVolumes);
395 mp_tableWidget->addAction(actionDeleteJob);
396 mp_tableWidget->addAction(actionPurgeFiles);
397 mp_tableWidget->addAction(actionRestoreFromJob);
398 mp_tableWidget->addAction(actionRestoreFromTime);
399 mp_tableWidget->addAction(actionShowLogForJob);
401 /* Make Connections */
402 connect(actionListJobid, SIGNAL(triggered()), this,
403 SLOT(consoleListJobid()));
404 connect(actionListFilesOnJob, SIGNAL(triggered()), this,
405 SLOT(consoleListFilesOnJob()));
406 connect(actionListJobMedia, SIGNAL(triggered()), this,
407 SLOT(consoleListJobMedia()));
408 connect(actionListVolumes, SIGNAL(triggered()), this,
409 SLOT(consoleListVolumes()));
410 connect(actionDeleteJob, SIGNAL(triggered()), this,
411 SLOT(consoleDeleteJob()));
412 connect(actionPurgeFiles, SIGNAL(triggered()), this,
413 SLOT(consolePurgeFiles()));
414 connect(actionRestoreFromJob, SIGNAL(triggered()), this,
415 SLOT(preRestoreFromJob()));
416 connect(actionRestoreFromTime, SIGNAL(triggered()), this,
417 SLOT(preRestoreFromTime()));
418 connect(actionShowLogForJob, SIGNAL(triggered()), this,
419 SLOT(showLogForJob()));
420 connect(actionCancelJob, SIGNAL(triggered()), this,
421 SLOT(consoleCancelJob()));
422 connect(actionListJobTotals, SIGNAL(triggered()), this,
423 SLOT(consoleListJobTotals()));
425 m_contextActions.append(actionRefreshJobList);
426 m_contextActions.append(actionListJobTotals);
430 * Functions to respond to local context sensitive menu sending console commands
431 * If I could figure out how to make these one function passing a string, Yaaaaaa
433 void JobList::consoleListJobid()
435 QString cmd("list jobid=");
437 if (mainWin->m_longList) { cmd.prepend("l"); }
440 void JobList::consoleListFilesOnJob()
442 QString cmd("list files jobid=");
444 if (mainWin->m_longList) { cmd.prepend("l"); }
447 void JobList::consoleListJobMedia()
449 QString cmd("list jobmedia jobid=");
451 if (mainWin->m_longList) { cmd.prepend("l"); }
454 void JobList::consoleListVolumes()
456 QString cmd("list volumes jobid=");
458 if (mainWin->m_longList) { cmd.prepend("l"); }
461 void JobList::consoleListJobTotals()
463 QString cmd("list jobtotals");
465 if (mainWin->m_longList) { cmd.prepend("l"); }
468 void JobList::consoleDeleteJob()
470 if (QMessageBox::warning(this, tr("Bat"),
471 tr("Are you sure you want to delete?? !!!.\n"
472 "This delete command is used to delete a Job record and all associated catalog"
473 " records that were created. This command operates only on the Catalog"
474 " database and has no effect on the actual data written to a Volume. This"
475 " command can be dangerous and we strongly recommend that you do not use"
476 " it unless you know what you are doing. The Job and all its associated"
477 " records (File and JobMedia) will be deleted from the catalog."
478 "Press OK to proceed with delete operation.?"),
479 QMessageBox::Ok | QMessageBox::Cancel)
480 == QMessageBox::Cancel) { return; }
482 QString cmd("delete job jobid=");
486 void JobList::consolePurgeFiles()
488 if (QMessageBox::warning(this, tr("Bat"),
489 tr("Are you sure you want to purge ?? !!!.\n"
490 "The Purge command will delete associated Catalog database records from Jobs and"
491 " Volumes without considering the retention period. Purge works only on the"
492 " Catalog database and does not affect data written to Volumes. This command can"
493 " be dangerous because you can delete catalog records associated with current"
494 " backups of files, and we recommend that you do not use it unless you know what"
496 "Press OK to proceed with the purge operation?"),
497 QMessageBox::Ok | QMessageBox::Cancel)
498 == QMessageBox::Cancel) { return; }
500 QString cmd("purge files jobid=");
506 * Subroutine to call preRestore to restore from a select job
508 void JobList::preRestoreFromJob()
510 new prerestorePage(m_currentJob, R_JOBIDLIST);
514 * Subroutine to call preRestore to restore from a select job
516 void JobList::preRestoreFromTime()
518 new prerestorePage(m_currentJob, R_JOBDATETIME);
522 * Subroutine to call class to show the log in the database from that job
524 void JobList::showLogForJob()
526 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
527 new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
531 * Cancel a running job
533 void JobList::consoleCancelJob()
535 QString cmd("cancel jobid=");