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.png")));
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()
308 m_contextActions.append(actionRefreshJobList);
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 += "on 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(actionLongListJob);
393 mp_tableWidget->addAction(actionListJobid);
394 mp_tableWidget->addAction(actionListFilesOnJob);
395 mp_tableWidget->addAction(actionListJobMedia);
396 mp_tableWidget->addAction(actionListVolumes);
397 mp_tableWidget->addAction(actionDeleteJob);
398 mp_tableWidget->addAction(actionPurgeFiles);
399 mp_tableWidget->addAction(actionRestoreFromJob);
400 mp_tableWidget->addAction(actionRestoreFromTime);
401 mp_tableWidget->addAction(actionShowLogForJob);
403 /* Make Connections */
404 connect(actionLongListJob, SIGNAL(triggered()), this,
405 SLOT(consoleLongListJob()));
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()));
429 * Functions to respond to local context sensitive menu sending console commands
430 * If I could figure out how to make these one function passing a string, Yaaaaaa
432 void JobList::consoleLongListJob()
434 QString cmd("llist jobid=");
438 void JobList::consoleListJobid()
440 QString cmd("list jobid=");
444 void JobList::consoleListFilesOnJob()
446 QString cmd("list files jobid=");
450 void JobList::consoleListJobMedia()
452 QString cmd("list jobmedia jobid=");
456 void JobList::consoleListVolumes()
458 QString cmd("list volumes jobid=");
462 void JobList::consoleDeleteJob()
464 if (QMessageBox::warning(this, tr("Bat"),
465 tr("Are you sure you want to delete?? !!!.\n"
466 "This delete command is used to delete a Job record and all associated catalog"
467 " records that were created. This command operates only on the Catalog"
468 " database and has no effect on the actual data written to a Volume. This"
469 " command can be dangerous and we strongly recommend that you do not use"
470 " it unless you know what you are doing. The Job and all its associated"
471 " records (File and JobMedia) will be deleted from the catalog."
472 "Press OK to proceed with delete operation.?"),
473 QMessageBox::Ok | QMessageBox::Cancel)
474 == QMessageBox::Cancel) { return; }
476 QString cmd("delete job jobid=");
480 void JobList::consolePurgeFiles()
482 if (QMessageBox::warning(this, tr("Bat"),
483 tr("Are you sure you want to purge ?? !!!.\n"
484 "The Purge command will delete associated Catalog database records from Jobs and"
485 " Volumes without considering the retention period. Purge works only on the"
486 " Catalog database and does not affect data written to Volumes. This command can"
487 " be dangerous because you can delete catalog records associated with current"
488 " backups of files, and we recommend that you do not use it unless you know what"
490 "Press OK to proceed with the purge operation?"),
491 QMessageBox::Ok | QMessageBox::Cancel)
492 == QMessageBox::Cancel) { return; }
494 QString cmd("purge files jobid=");
500 * Subroutine to call preRestore to restore from a select job
502 void JobList::preRestoreFromJob()
504 new prerestorePage(m_currentJob, R_JOBIDLIST);
508 * Subroutine to call preRestore to restore from a select job
510 void JobList::preRestoreFromTime()
512 new prerestorePage(m_currentJob, R_JOBDATETIME);
516 * Subroutine to call class to show the log in the database from that job
518 void JobList::showLogForJob()
520 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
521 new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
525 * Cancel a running job
527 void JobList::consoleCancelJob()
529 QString cmd("cancel jobid=");