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, QTreeWidgetItem *parentTreeWidgetItem)
48 m_name = ""; /* treeWidgetName has a virtual override in this class */
49 m_mediaName = mediaName;
50 m_clientName = clientName;
52 pgInitialize(parentTreeWidgetItem);
53 QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
54 thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/emblem-system.svg")));
59 if ((m_mediaName != "") || (m_clientName != "") || (m_jobName != ""))
61 m_checkCurrentWidget = true;
64 /* Set Defaults for check and spin for limits */
65 limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
66 limitSpinBox->setValue(mainWin->m_recordLimitVal);
67 daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
68 daysSpinBox->setValue(mainWin->m_daysLimitVal);
73 * The Meat of the class.
74 * This function will populate the QTableWidget, mp_tablewidget, with
75 * QTableWidgetItems representing the results of a query for what jobs exist on
76 * the media name passed from the constructor stored in m_mediaName.
78 void JobList::populateTable()
82 QBrush blackBrush(Qt::black);
84 if (!m_console->preventInUseConnect())
87 /* Can't do this in constructor because not neccesarily conected in constructor */
89 clientsComboBox->addItem("Any");
90 clientsComboBox->addItems(m_console->client_list);
91 int clientIndex = clientsComboBox->findText(m_clientName, Qt::MatchExactly);
92 if (clientIndex != -1)
93 clientsComboBox->setCurrentIndex(clientIndex);
95 QString query("SELECT VolumeName AS Media FROM Media ORDER BY Media");
96 if (mainWin->m_sqlDebug) {
97 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
99 QStringList results, volumeList;
100 if (m_console->sql_cmd(query, results)) {
102 QStringList fieldlist;
103 /* Iterate through the lines of results. */
104 foreach (QString resultline, results) {
105 fieldlist = resultline.split("\t");
106 volumeList.append(fieldlist[0]);
107 } /* foreach resultline */
108 } /* if results from query */
109 volumeComboBox->addItem("Any");
110 volumeComboBox->addItems(volumeList);
111 int volumeIndex = volumeComboBox->findText(m_mediaName, Qt::MatchExactly);
112 if (volumeIndex != -1) {
113 volumeComboBox->setCurrentIndex(volumeIndex);
115 jobComboBox->addItem("Any");
116 jobComboBox->addItems(m_console->job_list);
117 int jobIndex = jobComboBox->findText(m_jobName, Qt::MatchExactly);
118 if (jobIndex != -1) {
119 jobComboBox->setCurrentIndex(jobIndex);
121 levelComboBox->addItem("Any");
122 levelComboBox->addItems( QStringList() << "F" << "D" << "I");
123 purgedComboBox->addItem("Any");
124 purgedComboBox->addItems( QStringList() << "0" << "1");
125 statusComboBox->addItem("Any");
126 fileSetComboBox->addItem("Any");
127 fileSetComboBox->addItems(m_console->fileset_list);
128 QString statusQuery("SELECT JobStatusLong FROM Status");
129 if (mainWin->m_sqlDebug) {
130 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
132 QStringList statusResults, statusLongList;
133 if (m_console->sql_cmd(statusQuery, statusResults)) {
135 QStringList fieldlist;
136 /* Iterate through the lines of results. */
137 foreach (QString resultline, statusResults) {
138 fieldlist = resultline.split("\t");
139 statusLongList.append(fieldlist[0]);
140 } /* foreach resultline */
141 } /* if results from statusquery */
142 statusComboBox->addItems(statusLongList);
147 int volumeIndex = volumeComboBox->currentIndex();
148 if (volumeIndex != -1)
149 m_mediaName = volumeComboBox->itemText(volumeIndex);
150 query += "SELECT DISTINCT Job.Jobid AS Id, Job.Name AS JobName, Client.Name AS Client,"
151 " Job.Starttime AS JobStart, Job.Type AS JobType,"
152 " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
153 " Job.JobBytes AS Bytes,"
154 " Job.JobStatus AS Status, Status.JobStatusLong AS StatusLong,"
155 " Job.PurgedFiles AS Purged, FileSet.FileSet"
157 " LEFT OUTER JOIN Client ON (Client.ClientId=Job.ClientId)"
158 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)"
159 " LEFT OUTER JOIN Status ON (Job.JobStatus=Status.JobStatus)"
160 " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
161 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
162 QStringList conditions;
163 if (m_mediaName != "Any") {
164 conditions.append("Media.VolumeName='" + m_mediaName + "'");
166 int clientIndex = clientsComboBox->currentIndex();
167 if (clientIndex != -1)
168 m_clientName = clientsComboBox->itemText(clientIndex);
169 if (m_clientName != "Any") {
170 conditions.append("Client.Name='" + m_clientName + "'");
172 int jobIndex = jobComboBox->currentIndex();
174 m_jobName = jobComboBox->itemText(jobIndex);
175 if ((jobIndex != -1) && (jobComboBox->itemText(jobIndex) != "Any")) {
176 conditions.append("Job.Name='" + jobComboBox->itemText(jobIndex) + "'");
178 int levelIndex = levelComboBox->currentIndex();
179 if ((levelIndex != -1) && (levelComboBox->itemText(levelIndex) != "Any")) {
180 conditions.append("Job.Level='" + levelComboBox->itemText(levelIndex) + "'");
182 int statusIndex = statusComboBox->currentIndex();
183 if ((statusIndex != -1) && (statusComboBox->itemText(statusIndex) != "Any")) {
184 conditions.append("Status.JobStatusLong='" + statusComboBox->itemText(statusIndex) + "'");
186 int purgedIndex = purgedComboBox->currentIndex();
187 if ((purgedIndex != -1) && (purgedComboBox->itemText(purgedIndex) != "Any")) {
188 conditions.append("Job.PurgedFiles='" + purgedComboBox->itemText(purgedIndex) + "'");
190 int fileSetIndex = fileSetComboBox->currentIndex();
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");
226 /* Initialize the QTableWidget */
227 m_checkCurrentWidget = false;
228 mp_tableWidget->clear();
229 m_checkCurrentWidget = true;
230 mp_tableWidget->setColumnCount(headerlist.size());
231 mp_tableWidget->setHorizontalHeaderLabels(headerlist);
233 if (mainWin->m_sqlDebug) {
234 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
236 if (m_console->sql_cmd(query, results)) {
237 m_resultCount = results.count();
239 QTableWidgetItem* p_tableitem;
241 QStringList fieldlist;
242 mp_tableWidget->setRowCount(results.size());
245 /* Iterate through the record returned from the query */
246 foreach (resultline, results) {
247 fieldlist = resultline.split("\t");
249 bool m_statusIndexDone = false;
250 QString statusCode("");
251 /* Iterate through fields in the record */
252 foreach (field, fieldlist) {
253 field = field.trimmed(); /* strip leading & trailing spaces */
254 if ((column == m_statusIndex) && (!m_statusIndexDone)){
255 m_statusIndexDone = true;
258 p_tableitem = new QTableWidgetItem(field,1);
259 p_tableitem->setFlags(0);
260 p_tableitem->setForeground(blackBrush);
261 mp_tableWidget->setItem(row, column, p_tableitem);
262 if (column == m_statusIndex)
263 setStatusColor(p_tableitem, statusCode);
270 /* Resize the columns */
271 mp_tableWidget->resizeColumnsToContents();
272 mp_tableWidget->resizeRowsToContents();
273 mp_tableWidget->verticalHeader()->hide();
274 if ((m_mediaName != "Any") && (m_resultCount == 0)){
275 /* for context sensitive searches, let the user know if there were no
277 QMessageBox::warning(this, tr("Bat"),
278 tr("The Jobs query returned no results.\n"
279 "Press OK to continue?"), QMessageBox::Ok );
283 void JobList::setStatusColor(QTableWidgetItem *item, QString &field)
285 QString greenchars("TCR");
286 QString redchars("BEf");
287 QString yellowchars("eDAFSMmsjdctp");
288 if (greenchars.contains(field, Qt::CaseSensitive)) {
289 item->setBackground(Qt::green);
290 } else if (redchars.contains(field, Qt::CaseSensitive)) {
291 item->setBackground(Qt::red);
292 } else if (yellowchars.contains(field, Qt::CaseSensitive)){
293 item->setBackground(Qt::yellow);
298 * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
299 * The tree has been populated.
301 void JobList::PgSeltreeWidgetClicked()
310 * Virtual function override of pages function which is called when this page
311 * is visible on the stack
313 void JobList::currentStackItem()
322 * Virtual Function to return the name for the medialist tree widget
324 void JobList::treeWidgetName(QString &desc)
326 if ((m_mediaName == "") && (m_clientName == "") && (m_jobName == "")) {
330 if (m_mediaName != "" ) {
331 desc += "of Volume " + m_mediaName;
333 if (m_clientName != "" ) {
334 desc += "of Client " + m_clientName;
336 if (m_jobName != "" ) {
337 desc += "of Job " + m_jobName;
343 * This functions much line tableItemChanged for trees like the page selector,
344 * but I will do much less here
346 void JobList::tableItemChanged(QTableWidgetItem *currentItem, QTableWidgetItem * /*previousItem*/)
348 if (m_checkCurrentWidget) {
349 int row = currentItem->row();
350 QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
351 m_currentJob = jobitem->text();
353 /* include purged action or not */
354 jobitem = mp_tableWidget->item(row, m_purgedIndex);
355 QString purged = jobitem->text();
356 mp_tableWidget->removeAction(actionPurgeFiles);
358 mp_tableWidget->addAction(actionPurgeFiles);
360 /* include restore from time and job action or not */
361 jobitem = mp_tableWidget->item(row, m_typeIndex);
362 QString type = jobitem->text();
363 mp_tableWidget->removeAction(actionRestoreFromJob);
364 mp_tableWidget->removeAction(actionRestoreFromTime);
366 mp_tableWidget->addAction(actionRestoreFromJob);
367 mp_tableWidget->addAction(actionRestoreFromTime);
369 /* include cancel action or not */
370 jobitem = mp_tableWidget->item(row, m_statusIndex);
371 QString status = jobitem->text();
372 mp_tableWidget->removeAction(actionCancelJob);
373 if (status == "Running") {
374 mp_tableWidget->addAction(actionCancelJob);
380 * Function to create connections for context sensitive menu for this and
383 void JobList::createConnections()
385 /* connect to the action specific to this pages class that shows up in the
386 * page selector tree */
387 connect(actionRefreshJobList, SIGNAL(triggered()), this,
388 SLOT(populateTable()));
389 connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
390 /* for the tableItemChanged to maintain m_currentJob */
391 connect(mp_tableWidget, SIGNAL(
392 currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
393 this, SLOT(tableItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
395 /* Do what is required for the local context sensitive menu */
398 /* setContextMenuPolicy is required */
399 mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
402 mp_tableWidget->addAction(actionRefreshJobList);
403 mp_tableWidget->addAction(actionListJobid);
404 mp_tableWidget->addAction(actionListFilesOnJob);
405 mp_tableWidget->addAction(actionListJobMedia);
406 mp_tableWidget->addAction(actionListVolumes);
407 mp_tableWidget->addAction(actionDeleteJob);
408 mp_tableWidget->addAction(actionPurgeFiles);
409 mp_tableWidget->addAction(actionRestoreFromJob);
410 mp_tableWidget->addAction(actionRestoreFromTime);
411 mp_tableWidget->addAction(actionShowLogForJob);
413 /* Make Connections */
414 connect(actionListJobid, SIGNAL(triggered()), this,
415 SLOT(consoleListJobid()));
416 connect(actionListFilesOnJob, SIGNAL(triggered()), this,
417 SLOT(consoleListFilesOnJob()));
418 connect(actionListJobMedia, SIGNAL(triggered()), this,
419 SLOT(consoleListJobMedia()));
420 connect(actionListVolumes, SIGNAL(triggered()), this,
421 SLOT(consoleListVolumes()));
422 connect(actionDeleteJob, SIGNAL(triggered()), this,
423 SLOT(consoleDeleteJob()));
424 connect(actionPurgeFiles, SIGNAL(triggered()), this,
425 SLOT(consolePurgeFiles()));
426 connect(actionRestoreFromJob, SIGNAL(triggered()), this,
427 SLOT(preRestoreFromJob()));
428 connect(actionRestoreFromTime, SIGNAL(triggered()), this,
429 SLOT(preRestoreFromTime()));
430 connect(actionShowLogForJob, SIGNAL(triggered()), this,
431 SLOT(showLogForJob()));
432 connect(actionCancelJob, SIGNAL(triggered()), this,
433 SLOT(consoleCancelJob()));
434 connect(actionListJobTotals, SIGNAL(triggered()), this,
435 SLOT(consoleListJobTotals()));
437 m_contextActions.append(actionRefreshJobList);
438 m_contextActions.append(actionListJobTotals);
442 * Functions to respond to local context sensitive menu sending console commands
443 * If I could figure out how to make these one function passing a string, Yaaaaaa
445 void JobList::consoleListJobid()
447 QString cmd("list jobid=");
449 if (mainWin->m_longList) { cmd.prepend("l"); }
452 void JobList::consoleListFilesOnJob()
454 QString cmd("list files jobid=");
456 if (mainWin->m_longList) { cmd.prepend("l"); }
459 void JobList::consoleListJobMedia()
461 QString cmd("list jobmedia jobid=");
463 if (mainWin->m_longList) { cmd.prepend("l"); }
466 void JobList::consoleListVolumes()
468 QString cmd("list volumes jobid=");
470 if (mainWin->m_longList) { cmd.prepend("l"); }
473 void JobList::consoleListJobTotals()
475 QString cmd("list jobtotals");
477 if (mainWin->m_longList) { cmd.prepend("l"); }
480 void JobList::consoleDeleteJob()
482 if (QMessageBox::warning(this, tr("Bat"),
483 tr("Are you sure you want to delete?? !!!.\n"
484 "This delete command is used to delete a Job record and all associated catalog"
485 " records that were created. This command operates only on the Catalog"
486 " database and has no effect on the actual data written to a Volume. This"
487 " command can be dangerous and we strongly recommend that you do not use"
488 " it unless you know what you are doing. The Job and all its associated"
489 " records (File and JobMedia) will be deleted from the catalog."
490 "Press OK to proceed with delete operation.?"),
491 QMessageBox::Ok | QMessageBox::Cancel)
492 == QMessageBox::Cancel) { return; }
494 QString cmd("delete job jobid=");
498 void JobList::consolePurgeFiles()
500 if (QMessageBox::warning(this, tr("Bat"),
501 tr("Are you sure you want to purge ?? !!!.\n"
502 "The Purge command will delete associated Catalog database records from Jobs and"
503 " Volumes without considering the retention period. Purge works only on the"
504 " Catalog database and does not affect data written to Volumes. This command can"
505 " be dangerous because you can delete catalog records associated with current"
506 " backups of files, and we recommend that you do not use it unless you know what"
508 "Press OK to proceed with the purge operation?"),
509 QMessageBox::Ok | QMessageBox::Cancel)
510 == QMessageBox::Cancel) { return; }
512 QString cmd("purge files jobid=");
518 * Subroutine to call preRestore to restore from a select job
520 void JobList::preRestoreFromJob()
522 new prerestorePage(m_currentJob, R_JOBIDLIST);
526 * Subroutine to call preRestore to restore from a select job
528 void JobList::preRestoreFromTime()
530 new prerestorePage(m_currentJob, R_JOBDATETIME);
534 * Subroutine to call class to show the log in the database from that job
536 void JobList::showLogForJob()
538 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
539 new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
543 * Cancel a running job
545 void JobList::consoleCancelJob()
547 QString cmd("cancel jobid=");