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);
55 if ((m_mediaName != "") || (m_clientName != "")) { m_closeable=true; }
56 m_checkCurrentWidget = true;
59 /* Set Defaults for check and spin for limits */
60 limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
61 limitSpinBox->setValue(mainWin->m_recordLimitVal);
62 daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
63 daysSpinBox->setValue(mainWin->m_daysLimitVal);
67 * The Meat of the class.
68 * This function will populate the QTableWidget, mp_tablewidget, with
69 * QTableWidgetItems representing the results of a query for what jobs exist on
70 * the media name passed from the constructor stored in m_mediaName.
72 void JobList::populateTable()
76 QBrush blackBrush(Qt::black);
78 if (!m_console->preventInUseConnect())
81 /* Can't do this in constructor because not neccesarily conected in constructor */
83 clientsComboBox->addItem("Any");
84 clientsComboBox->addItems(m_console->client_list);
85 int clientIndex = clientsComboBox->findText(m_clientName, Qt::MatchExactly);
86 if (clientIndex != -1)
87 clientsComboBox->setCurrentIndex(clientIndex);
89 QString query("SELECT VolumeName AS Media FROM Media ORDER BY Media");
90 if (mainWin->m_sqlDebug) {
91 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
93 QStringList results, volumeList;
94 if (m_console->sql_cmd(query, results)) {
96 QStringList fieldlist;
97 /* Iterate through the lines of results. */
98 foreach (QString resultline, results) {
99 fieldlist = resultline.split("\t");
100 volumeList.append(fieldlist[0]);
101 } /* foreach resultline */
102 } /* if results from query */
103 volumeComboBox->addItem("Any");
104 volumeComboBox->addItems(volumeList);
105 int volumeIndex = volumeComboBox->findText(m_mediaName, Qt::MatchExactly);
106 if (volumeIndex != -1) {
107 volumeComboBox->setCurrentIndex(volumeIndex);
109 jobComboBox->addItem("Any");
110 jobComboBox->addItems(m_console->job_list);
111 levelComboBox->addItem("Any");
112 levelComboBox->addItems( QStringList() << "F" << "D" << "I");
113 purgedComboBox->addItem("Any");
114 purgedComboBox->addItems( QStringList() << "0" << "1");
115 statusComboBox->addItem("Any");
116 fileSetComboBox->addItem("Any");
117 fileSetComboBox->addItems(m_console->fileset_list);
118 QString statusQuery("SELECT JobStatusLong FROM Status");
119 if (mainWin->m_sqlDebug) {
120 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
122 QStringList statusResults, statusLongList;
123 if (m_console->sql_cmd(statusQuery, statusResults)) {
125 QStringList fieldlist;
126 /* Iterate through the lines of results. */
127 foreach (QString resultline, statusResults) {
128 fieldlist = resultline.split("\t");
129 statusLongList.append(fieldlist[0]);
130 } /* foreach resultline */
131 } /* if results from statusquery */
132 statusComboBox->addItems(statusLongList);
137 int volumeIndex = volumeComboBox->currentIndex();
138 if (volumeIndex != -1)
139 m_mediaName = volumeComboBox->itemText(volumeIndex);
140 query += "SELECT DISTINCT Job.Jobid AS Id, Job.Name AS JobName, Client.Name AS Client,"
141 " Job.Starttime AS JobStart, Job.Type AS JobType,"
142 " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
143 " Job.JobBytes AS Bytes,"
144 " Job.JobStatus AS Status, Status.JobStatusLong AS StatusLong,"
145 " Job.PurgedFiles AS Purged, FileSet.FileSet"
147 " LEFT OUTER JOIN Client ON (Client.ClientId=Job.ClientId)"
148 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)"
149 " LEFT OUTER JOIN Status ON (Job.JobStatus=Status.JobStatus)"
150 " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
151 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
152 QStringList conditions;
153 if (m_mediaName != "Any") {
154 conditions.append("Media.VolumeName='" + m_mediaName + "'");
156 int clientIndex = clientsComboBox->currentIndex();
157 if (clientIndex != -1)
158 m_clientName = clientsComboBox->itemText(clientIndex);
159 if (m_clientName != "Any") {
160 conditions.append("Client.Name='" + m_clientName + "'");
162 int jobIndex = jobComboBox->currentIndex();
163 if ((jobIndex != -1) && (jobComboBox->itemText(jobIndex) != "Any")) {
164 conditions.append("Job.Name='" + jobComboBox->itemText(jobIndex) + "'");
166 int levelIndex = levelComboBox->currentIndex();
167 if ((levelIndex != -1) && (levelComboBox->itemText(levelIndex) != "Any")) {
168 conditions.append("Job.Level='" + levelComboBox->itemText(levelIndex) + "'");
170 int statusIndex = statusComboBox->currentIndex();
171 if ((statusIndex != -1) && (statusComboBox->itemText(statusIndex) != "Any")) {
172 conditions.append("Status.JobStatusLong='" + statusComboBox->itemText(statusIndex) + "'");
174 int purgedIndex = purgedComboBox->currentIndex();
175 if ((purgedIndex != -1) && (purgedComboBox->itemText(purgedIndex) != "Any")) {
176 conditions.append("Job.PurgedFiles='" + purgedComboBox->itemText(purgedIndex) + "'");
178 int fileSetIndex = fileSetComboBox->currentIndex();
179 if ((fileSetIndex != -1) && (fileSetComboBox->itemText(fileSetIndex) != "Any")) {
180 conditions.append("FileSet.FileSet='" + fileSetComboBox->itemText(fileSetIndex) + "'");
182 /* If Limit check box For limit by days is checked */
183 if (daysCheckBox->checkState() == Qt::Checked) {
184 QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
185 QString since = stamp.toString(Qt::ISODate);
186 conditions.append("Job.Starttime>'" + since + "'");
189 foreach (QString condition, conditions) {
191 query += " WHERE " + condition;
194 query += " AND " + condition;
198 query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
199 /* If Limit check box for limit records returned is checked */
200 if (limitCheckBox->checkState() == Qt::Checked) {
202 limit.setNum(limitSpinBox->value());
203 query += " LIMIT " + limit;
206 /* Set up the Header for the table */
207 QStringList headerlist = (QStringList()
208 << "Job Id" << "Job Name" << "Client" << "Job Starttime" << "Job Type"
209 << "Job Level" << "Job Files" << "Job Bytes" << "Job Status" << "Purged" << "File Set" );
210 m_purgedIndex = headerlist.indexOf("Purged");
211 m_typeIndex = headerlist.indexOf("Job Type");
212 statusIndex = headerlist.indexOf("Job Status");
214 /* Initialize the QTableWidget */
215 m_checkCurrentWidget = false;
216 mp_tableWidget->clear();
217 m_checkCurrentWidget = true;
218 mp_tableWidget->setColumnCount(headerlist.size());
219 mp_tableWidget->setHorizontalHeaderLabels(headerlist);
221 if (mainWin->m_sqlDebug) {
222 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
224 if (m_console->sql_cmd(query, results)) {
225 m_resultCount = results.count();
227 QTableWidgetItem* p_tableitem;
229 QStringList fieldlist;
230 mp_tableWidget->setRowCount(results.size());
233 /* Iterate through the record returned from the query */
234 foreach (resultline, results) {
235 fieldlist = resultline.split("\t");
237 bool statusIndexDone = false;
238 QString statusCode("");
239 /* Iterate through fields in the record */
240 foreach (field, fieldlist) {
241 field = field.trimmed(); /* strip leading & trailing spaces */
242 if ((column == statusIndex) && (!statusIndexDone)){
243 statusIndexDone = true;
246 p_tableitem = new QTableWidgetItem(field,1);
247 p_tableitem->setFlags(0);
248 p_tableitem->setForeground(blackBrush);
249 mp_tableWidget->setItem(row, column, p_tableitem);
250 if (column == statusIndex)
251 setStatusColor(p_tableitem, statusCode);
258 /* Resize the columns */
259 mp_tableWidget->resizeColumnsToContents();
260 mp_tableWidget->resizeRowsToContents();
261 mp_tableWidget->verticalHeader()->hide();
262 if ((m_mediaName != "Any") && (m_resultCount == 0)){
263 /* for context sensitive searches, let the user know if there were no
265 QMessageBox::warning(this, tr("Bat"),
266 tr("The Jobs query returned no results.\n"
267 "Press OK to continue?"), QMessageBox::Ok );
271 void JobList::setStatusColor(QTableWidgetItem *item, QString &field)
273 QString greenchars("TCR");
274 QString redchars("BEf");
275 QString yellowchars("eDAFSMmsjdctp");
276 if (greenchars.contains(field, Qt::CaseSensitive)) {
277 item->setBackground(Qt::green);
278 } else if (redchars.contains(field, Qt::CaseSensitive)) {
279 item->setBackground(Qt::red);
280 } else if (yellowchars.contains(field, Qt::CaseSensitive)){
281 item->setBackground(Qt::yellow);
286 * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
287 * The tree has been populated.
289 void JobList::PgSeltreeWidgetClicked()
298 * Virtual function override of pages function which is called when this page
299 * is visible on the stack
301 void JobList::currentStackItem()
305 m_contextActions.append(actionRefreshJobList);
311 * Virtual Function to return the name for the medialist tree widget
313 void JobList::treeWidgetName(QString &desc)
315 if ((m_mediaName == "") && (m_clientName == "")) {
319 if (m_mediaName != "" ) {
320 desc += "on Volume " + m_mediaName;
322 if (m_clientName != "" ) {
323 desc += "of Client " + m_clientName;
329 * This functions much line tableItemChanged for trees like the page selector,
330 * but I will do much less here
332 void JobList::tableItemChanged(QTableWidgetItem *currentItem, QTableWidgetItem * /*previousItem*/)
334 if (m_checkCurrentWidget) {
335 int row = currentItem->row();
336 QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
337 m_currentJob = jobitem->text();
338 jobitem = mp_tableWidget->item(row, m_purgedIndex);
339 QString purged = jobitem->text();
340 mp_tableWidget->removeAction(actionPurgeFiles);
342 mp_tableWidget->addAction(actionPurgeFiles);
344 jobitem = mp_tableWidget->item(row, m_typeIndex);
345 QString status = jobitem->text();
346 mp_tableWidget->removeAction(actionRestoreFromJob);
347 mp_tableWidget->removeAction(actionRestoreFromTime);
349 mp_tableWidget->addAction(actionRestoreFromJob);
350 mp_tableWidget->addAction(actionRestoreFromTime);
356 * Function to create connections for context sensitive menu for this and
359 void JobList::createConnections()
361 /* connect to the action specific to this pages class that shows up in the
362 * page selector tree */
363 connect(actionRefreshJobList, SIGNAL(triggered()), this,
364 SLOT(populateTable()));
365 connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
366 /* for the tableItemChanged to maintain m_currentJob */
367 connect(mp_tableWidget, SIGNAL(
368 currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
369 this, SLOT(tableItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
371 /* Do what is required for the local context sensitive menu */
374 /* setContextMenuPolicy is required */
375 mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
378 mp_tableWidget->addAction(actionRefreshJobList);
379 mp_tableWidget->addAction(actionLongListJob);
380 mp_tableWidget->addAction(actionListJobid);
381 mp_tableWidget->addAction(actionListFilesOnJob);
382 mp_tableWidget->addAction(actionListJobMedia);
383 mp_tableWidget->addAction(actionListVolumes);
384 mp_tableWidget->addAction(actionDeleteJob);
385 mp_tableWidget->addAction(actionPurgeFiles);
386 mp_tableWidget->addAction(actionRestoreFromJob);
387 mp_tableWidget->addAction(actionRestoreFromTime);
388 mp_tableWidget->addAction(actionShowLogForJob);
390 /* Make Connections */
391 connect(actionLongListJob, SIGNAL(triggered()), this,
392 SLOT(consoleLongListJob()));
393 connect(actionListJobid, SIGNAL(triggered()), this,
394 SLOT(consoleListJobid()));
395 connect(actionListFilesOnJob, SIGNAL(triggered()), this,
396 SLOT(consoleListFilesOnJob()));
397 connect(actionListJobMedia, SIGNAL(triggered()), this,
398 SLOT(consoleListJobMedia()));
399 connect(actionListVolumes, SIGNAL(triggered()), this,
400 SLOT(consoleListVolumes()));
401 connect(actionDeleteJob, SIGNAL(triggered()), this,
402 SLOT(consoleDeleteJob()));
403 connect(actionPurgeFiles, SIGNAL(triggered()), this,
404 SLOT(consolePurgeFiles()));
405 connect(actionRestoreFromJob, SIGNAL(triggered()), this,
406 SLOT(preRestoreFromJob()));
407 connect(actionRestoreFromTime, SIGNAL(triggered()), this,
408 SLOT(preRestoreFromTime()));
409 connect(actionShowLogForJob, SIGNAL(triggered()), this,
410 SLOT(showLogForJob()));
414 * Functions to respond to local context sensitive menu sending console commands
415 * If I could figure out how to make these one function passing a string, Yaaaaaa
417 void JobList::consoleLongListJob()
419 QString cmd("llist jobid=");
423 void JobList::consoleListJobid()
425 QString cmd("list jobid=");
429 void JobList::consoleListFilesOnJob()
431 QString cmd("list files jobid=");
435 void JobList::consoleListJobMedia()
437 QString cmd("list jobmedia jobid=");
441 void JobList::consoleListVolumes()
443 QString cmd("list volumes jobid=");
447 void JobList::consoleDeleteJob()
449 if (QMessageBox::warning(this, tr("Bat"),
450 tr("Are you sure you want to delete?? !!!.\n"
451 "This delete command is used to delete a Job record and all associated catalog"
452 " records that were created. This command operates only on the Catalog"
453 " database and has no effect on the actual data written to a Volume. This"
454 " command can be dangerous and we strongly recommend that you do not use"
455 " it unless you know what you are doing. The Job and all its associated"
456 " records (File and JobMedia) will be deleted from the catalog."
457 "Press OK to proceed with delete operation.?"),
458 QMessageBox::Ok | QMessageBox::Cancel)
459 == QMessageBox::Cancel) { return; }
461 QString cmd("delete job jobid=");
465 void JobList::consolePurgeFiles()
467 if (QMessageBox::warning(this, tr("Bat"),
468 tr("Are you sure you want to purge ?? !!!.\n"
469 "The Purge command will delete associated Catalog database records from Jobs and"
470 " Volumes without considering the retention period. Purge works only on the"
471 " Catalog database and does not affect data written to Volumes. This command can"
472 " be dangerous because you can delete catalog records associated with current"
473 " backups of files, and we recommend that you do not use it unless you know what"
475 "Press OK to proceed with the purge operation?"),
476 QMessageBox::Ok | QMessageBox::Cancel)
477 == QMessageBox::Cancel) { return; }
479 QString cmd("purge files jobid=");
485 * Subroutine to call preRestore to restore from a select job
487 void JobList::preRestoreFromJob()
489 new prerestorePage(m_currentJob, R_JOBIDLIST);
493 * Subroutine to call preRestore to restore from a select job
495 void JobList::preRestoreFromTime()
497 new prerestorePage(m_currentJob, R_JOBDATETIME);
501 * Subroutine to call class to show the log in the database from that job
503 void JobList::showLogForJob()
505 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
506 new JobLog(m_currentJob, pageSelectorTreeWidgetItem);