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 QTreeWidgetItem *parentTreeWidgetItem)
48 m_name = ""; /* treeWidgetName has a virtual override in this class */
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);
71 * The Meat of the class.
72 * This function will populate the QTableWidget, mp_tablewidget, with
73 * QTableWidgetItems representing the results of a query for what jobs exist on
74 * the media name passed from the constructor stored in m_mediaName.
76 void JobList::populateTable()
80 QBrush blackBrush(Qt::black);
82 if (!m_console->preventInUseConnect())
85 /* Can't do this in constructor because not neccesarily conected in constructor */
87 clientsComboBox->addItem("Any");
88 clientsComboBox->addItems(m_console->client_list);
89 int clientIndex = clientsComboBox->findText(m_clientName, Qt::MatchExactly);
90 if (clientIndex != -1)
91 clientsComboBox->setCurrentIndex(clientIndex);
93 QString query("SELECT VolumeName AS Media FROM Media ORDER BY Media");
94 if (mainWin->m_sqlDebug) {
95 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
97 QStringList results, volumeList;
98 if (m_console->sql_cmd(query, results)) {
100 QStringList fieldlist;
101 /* Iterate through the lines of results. */
102 foreach (QString resultline, results) {
103 fieldlist = resultline.split("\t");
104 volumeList.append(fieldlist[0]);
105 } /* foreach resultline */
106 } /* if results from query */
107 volumeComboBox->addItem("Any");
108 volumeComboBox->addItems(volumeList);
109 int volumeIndex = volumeComboBox->findText(m_mediaName, Qt::MatchExactly);
110 if (volumeIndex != -1) {
111 volumeComboBox->setCurrentIndex(volumeIndex);
113 jobComboBox->addItem("Any");
114 jobComboBox->addItems(m_console->job_list);
115 levelComboBox->addItem("Any");
116 levelComboBox->addItems( QStringList() << "F" << "D" << "I");
117 purgedComboBox->addItem("Any");
118 purgedComboBox->addItems( QStringList() << "0" << "1");
119 statusComboBox->addItem("Any");
120 fileSetComboBox->addItem("Any");
121 fileSetComboBox->addItems(m_console->fileset_list);
122 QString statusQuery("SELECT JobStatusLong FROM Status");
123 if (mainWin->m_sqlDebug) {
124 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
126 QStringList statusResults, statusLongList;
127 if (m_console->sql_cmd(statusQuery, statusResults)) {
129 QStringList fieldlist;
130 /* Iterate through the lines of results. */
131 foreach (QString resultline, statusResults) {
132 fieldlist = resultline.split("\t");
133 statusLongList.append(fieldlist[0]);
134 } /* foreach resultline */
135 } /* if results from statusquery */
136 statusComboBox->addItems(statusLongList);
141 int volumeIndex = volumeComboBox->currentIndex();
142 if (volumeIndex != -1)
143 m_mediaName = volumeComboBox->itemText(volumeIndex);
144 query += "SELECT DISTINCT Job.Jobid AS Id, Job.Name AS JobName, Client.Name AS Client,"
145 " Job.Starttime AS JobStart, Job.Type AS JobType,"
146 " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
147 " Job.JobBytes AS Bytes,"
148 " Job.JobStatus AS Status, Status.JobStatusLong AS StatusLong,"
149 " Job.PurgedFiles AS Purged, FileSet.FileSet"
151 " LEFT OUTER JOIN Client ON (Client.ClientId=Job.ClientId)"
152 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)"
153 " LEFT OUTER JOIN Status ON (Job.JobStatus=Status.JobStatus)"
154 " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
155 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
156 QStringList conditions;
157 if (m_mediaName != "Any") {
158 conditions.append("Media.VolumeName='" + m_mediaName + "'");
160 int clientIndex = clientsComboBox->currentIndex();
161 if (clientIndex != -1)
162 m_clientName = clientsComboBox->itemText(clientIndex);
163 if (m_clientName != "Any") {
164 conditions.append("Client.Name='" + m_clientName + "'");
166 int jobIndex = jobComboBox->currentIndex();
167 if ((jobIndex != -1) && (jobComboBox->itemText(jobIndex) != "Any")) {
168 conditions.append("Job.Name='" + jobComboBox->itemText(jobIndex) + "'");
170 int levelIndex = levelComboBox->currentIndex();
171 if ((levelIndex != -1) && (levelComboBox->itemText(levelIndex) != "Any")) {
172 conditions.append("Job.Level='" + levelComboBox->itemText(levelIndex) + "'");
174 int statusIndex = statusComboBox->currentIndex();
175 if ((statusIndex != -1) && (statusComboBox->itemText(statusIndex) != "Any")) {
176 conditions.append("Status.JobStatusLong='" + statusComboBox->itemText(statusIndex) + "'");
178 int purgedIndex = purgedComboBox->currentIndex();
179 if ((purgedIndex != -1) && (purgedComboBox->itemText(purgedIndex) != "Any")) {
180 conditions.append("Job.PurgedFiles='" + purgedComboBox->itemText(purgedIndex) + "'");
182 int fileSetIndex = fileSetComboBox->currentIndex();
183 if ((fileSetIndex != -1) && (fileSetComboBox->itemText(fileSetIndex) != "Any")) {
184 conditions.append("FileSet.FileSet='" + fileSetComboBox->itemText(fileSetIndex) + "'");
186 /* If Limit check box For limit by days is checked */
187 if (daysCheckBox->checkState() == Qt::Checked) {
188 QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
189 QString since = stamp.toString(Qt::ISODate);
190 conditions.append("Job.Starttime>'" + since + "'");
193 foreach (QString condition, conditions) {
195 query += " WHERE " + condition;
198 query += " AND " + condition;
202 query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
203 /* If Limit check box for limit records returned is checked */
204 if (limitCheckBox->checkState() == Qt::Checked) {
206 limit.setNum(limitSpinBox->value());
207 query += " LIMIT " + limit;
210 /* Set up the Header for the table */
211 QStringList headerlist = (QStringList()
212 << "Job Id" << "Job Name" << "Client" << "Job Starttime" << "Job Type"
213 << "Job Level" << "Job Files" << "Job Bytes" << "Job Status" << "Purged" << "File Set" );
214 m_purgedIndex = headerlist.indexOf("Purged");
215 m_typeIndex = headerlist.indexOf("Job Type");
216 m_statusIndex = headerlist.indexOf("Job Status");
218 /* Initialize the QTableWidget */
219 m_checkCurrentWidget = false;
220 mp_tableWidget->clear();
221 m_checkCurrentWidget = true;
222 mp_tableWidget->setColumnCount(headerlist.size());
223 mp_tableWidget->setHorizontalHeaderLabels(headerlist);
225 if (mainWin->m_sqlDebug) {
226 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
228 if (m_console->sql_cmd(query, results)) {
229 m_resultCount = results.count();
231 QTableWidgetItem* p_tableitem;
233 QStringList fieldlist;
234 mp_tableWidget->setRowCount(results.size());
237 /* Iterate through the record returned from the query */
238 foreach (resultline, results) {
239 fieldlist = resultline.split("\t");
241 bool m_statusIndexDone = false;
242 QString statusCode("");
243 /* Iterate through fields in the record */
244 foreach (field, fieldlist) {
245 field = field.trimmed(); /* strip leading & trailing spaces */
246 if ((column == m_statusIndex) && (!m_statusIndexDone)){
247 m_statusIndexDone = true;
250 p_tableitem = new QTableWidgetItem(field,1);
251 p_tableitem->setFlags(0);
252 p_tableitem->setForeground(blackBrush);
253 mp_tableWidget->setItem(row, column, p_tableitem);
254 if (column == m_statusIndex)
255 setStatusColor(p_tableitem, statusCode);
262 /* Resize the columns */
263 mp_tableWidget->resizeColumnsToContents();
264 mp_tableWidget->resizeRowsToContents();
265 mp_tableWidget->verticalHeader()->hide();
266 if ((m_mediaName != "Any") && (m_resultCount == 0)){
267 /* for context sensitive searches, let the user know if there were no
269 QMessageBox::warning(this, tr("Bat"),
270 tr("The Jobs query returned no results.\n"
271 "Press OK to continue?"), QMessageBox::Ok );
275 void JobList::setStatusColor(QTableWidgetItem *item, QString &field)
277 QString greenchars("TCR");
278 QString redchars("BEf");
279 QString yellowchars("eDAFSMmsjdctp");
280 if (greenchars.contains(field, Qt::CaseSensitive)) {
281 item->setBackground(Qt::green);
282 } else if (redchars.contains(field, Qt::CaseSensitive)) {
283 item->setBackground(Qt::red);
284 } else if (yellowchars.contains(field, Qt::CaseSensitive)){
285 item->setBackground(Qt::yellow);
290 * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
291 * The tree has been populated.
293 void JobList::PgSeltreeWidgetClicked()
302 * Virtual function override of pages function which is called when this page
303 * is visible on the stack
305 void JobList::currentStackItem()
314 * Virtual Function to return the name for the medialist tree widget
316 void JobList::treeWidgetName(QString &desc)
318 if ((m_mediaName == "") && (m_clientName == "")) {
322 if (m_mediaName != "" ) {
323 desc += "of Volume " + m_mediaName;
325 if (m_clientName != "" ) {
326 desc += "of Client " + m_clientName;
332 * This functions much line tableItemChanged for trees like the page selector,
333 * but I will do much less here
335 void JobList::tableItemChanged(QTableWidgetItem *currentItem, QTableWidgetItem * /*previousItem*/)
337 if (m_checkCurrentWidget) {
338 int row = currentItem->row();
339 QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
340 m_currentJob = jobitem->text();
342 /* include purged action or not */
343 jobitem = mp_tableWidget->item(row, m_purgedIndex);
344 QString purged = jobitem->text();
345 mp_tableWidget->removeAction(actionPurgeFiles);
347 mp_tableWidget->addAction(actionPurgeFiles);
349 /* include restore from time and job action or not */
350 jobitem = mp_tableWidget->item(row, m_typeIndex);
351 QString type = jobitem->text();
352 mp_tableWidget->removeAction(actionRestoreFromJob);
353 mp_tableWidget->removeAction(actionRestoreFromTime);
355 mp_tableWidget->addAction(actionRestoreFromJob);
356 mp_tableWidget->addAction(actionRestoreFromTime);
358 /* include cancel action or not */
359 jobitem = mp_tableWidget->item(row, m_statusIndex);
360 QString status = jobitem->text();
361 mp_tableWidget->removeAction(actionCancelJob);
362 if (status == "Running") {
363 mp_tableWidget->addAction(actionCancelJob);
369 * Function to create connections for context sensitive menu for this and
372 void JobList::createConnections()
374 /* connect to the action specific to this pages class that shows up in the
375 * page selector tree */
376 connect(actionRefreshJobList, SIGNAL(triggered()), this,
377 SLOT(populateTable()));
378 connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
379 /* for the tableItemChanged to maintain m_currentJob */
380 connect(mp_tableWidget, SIGNAL(
381 currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
382 this, SLOT(tableItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
384 /* Do what is required for the local context sensitive menu */
387 /* setContextMenuPolicy is required */
388 mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
391 mp_tableWidget->addAction(actionRefreshJobList);
392 mp_tableWidget->addAction(actionListJobid);
393 mp_tableWidget->addAction(actionListFilesOnJob);
394 mp_tableWidget->addAction(actionListJobMedia);
395 mp_tableWidget->addAction(actionListVolumes);
396 mp_tableWidget->addAction(actionDeleteJob);
397 mp_tableWidget->addAction(actionPurgeFiles);
398 mp_tableWidget->addAction(actionRestoreFromJob);
399 mp_tableWidget->addAction(actionRestoreFromTime);
400 mp_tableWidget->addAction(actionShowLogForJob);
402 /* Make Connections */
403 connect(actionListJobid, SIGNAL(triggered()), this,
404 SLOT(consoleListJobid()));
405 connect(actionListFilesOnJob, SIGNAL(triggered()), this,
406 SLOT(consoleListFilesOnJob()));
407 connect(actionListJobMedia, SIGNAL(triggered()), this,
408 SLOT(consoleListJobMedia()));
409 connect(actionListVolumes, SIGNAL(triggered()), this,
410 SLOT(consoleListVolumes()));
411 connect(actionDeleteJob, SIGNAL(triggered()), this,
412 SLOT(consoleDeleteJob()));
413 connect(actionPurgeFiles, SIGNAL(triggered()), this,
414 SLOT(consolePurgeFiles()));
415 connect(actionRestoreFromJob, SIGNAL(triggered()), this,
416 SLOT(preRestoreFromJob()));
417 connect(actionRestoreFromTime, SIGNAL(triggered()), this,
418 SLOT(preRestoreFromTime()));
419 connect(actionShowLogForJob, SIGNAL(triggered()), this,
420 SLOT(showLogForJob()));
421 connect(actionCancelJob, SIGNAL(triggered()), this,
422 SLOT(consoleCancelJob()));
423 connect(actionListJobTotals, SIGNAL(triggered()), this,
424 SLOT(consoleListJobTotals()));
426 m_contextActions.append(actionRefreshJobList);
427 m_contextActions.append(actionListJobTotals);
431 * Functions to respond to local context sensitive menu sending console commands
432 * If I could figure out how to make these one function passing a string, Yaaaaaa
434 void JobList::consoleListJobid()
436 QString cmd("list jobid=");
438 if (mainWin->m_longList) { cmd.prepend("l"); }
441 void JobList::consoleListFilesOnJob()
443 QString cmd("list files jobid=");
445 if (mainWin->m_longList) { cmd.prepend("l"); }
448 void JobList::consoleListJobMedia()
450 QString cmd("list jobmedia jobid=");
452 if (mainWin->m_longList) { cmd.prepend("l"); }
455 void JobList::consoleListVolumes()
457 QString cmd("list volumes jobid=");
459 if (mainWin->m_longList) { cmd.prepend("l"); }
462 void JobList::consoleListJobTotals()
464 QString cmd("list jobtotals");
466 if (mainWin->m_longList) { cmd.prepend("l"); }
469 void JobList::consoleDeleteJob()
471 if (QMessageBox::warning(this, tr("Bat"),
472 tr("Are you sure you want to delete?? !!!.\n"
473 "This delete command is used to delete a Job record and all associated catalog"
474 " records that were created. This command operates only on the Catalog"
475 " database and has no effect on the actual data written to a Volume. This"
476 " command can be dangerous and we strongly recommend that you do not use"
477 " it unless you know what you are doing. The Job and all its associated"
478 " records (File and JobMedia) will be deleted from the catalog."
479 "Press OK to proceed with delete operation.?"),
480 QMessageBox::Ok | QMessageBox::Cancel)
481 == QMessageBox::Cancel) { return; }
483 QString cmd("delete job jobid=");
487 void JobList::consolePurgeFiles()
489 if (QMessageBox::warning(this, tr("Bat"),
490 tr("Are you sure you want to purge ?? !!!.\n"
491 "The Purge command will delete associated Catalog database records from Jobs and"
492 " Volumes without considering the retention period. Purge works only on the"
493 " Catalog database and does not affect data written to Volumes. This command can"
494 " be dangerous because you can delete catalog records associated with current"
495 " backups of files, and we recommend that you do not use it unless you know what"
497 "Press OK to proceed with the purge operation?"),
498 QMessageBox::Ok | QMessageBox::Cancel)
499 == QMessageBox::Cancel) { return; }
501 QString cmd("purge files jobid=");
507 * Subroutine to call preRestore to restore from a select job
509 void JobList::preRestoreFromJob()
511 new prerestorePage(m_currentJob, R_JOBIDLIST);
515 * Subroutine to call preRestore to restore from a select job
517 void JobList::preRestoreFromTime()
519 new prerestorePage(m_currentJob, R_JOBDATETIME);
523 * Subroutine to call class to show the log in the database from that job
525 void JobList::showLogForJob()
527 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
528 new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
532 * Cancel a running job
534 void JobList::consoleCancelJob()
536 QString cmd("cancel jobid=");