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(Qt::Checked);
61 limitSpinBox->setValue(150);
62 daysCheckBox->setCheckState(Qt::Unchecked);
63 daysSpinBox->setValue(30);
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 /* Not m_console->volume_list will query database */
90 QString query("SELECT VolumeName AS Media FROM Media ORDER BY Media");
91 QStringList results, volumeList;
92 if (m_console->sql_cmd(query, results)) {
94 QStringList fieldlist;
95 /* Iterate through the lines of results. */
96 foreach (QString resultline, results) {
97 fieldlist = resultline.split("\t");
98 volumeList.append(fieldlist[0]);
99 } /* foreach resultline */
100 } /* if results from query */
101 volumeComboBox->addItem("Any");
102 volumeComboBox->addItems(volumeList);
103 int volumeIndex = volumeComboBox->findText(m_mediaName, Qt::MatchExactly);
104 if (volumeIndex != -1) {
105 volumeComboBox->setCurrentIndex(volumeIndex);
107 jobComboBox->addItem("Any");
108 jobComboBox->addItems(m_console->job_list);
109 levelComboBox->addItem("Any");
110 levelComboBox->addItems( QStringList() << "F" << "D" << "I");
111 purgedComboBox->addItem("Any");
112 purgedComboBox->addItems( QStringList() << "0" << "1");
113 statusComboBox->addItem("Any");
114 fileSetComboBox->addItem("Any");
115 fileSetComboBox->addItems(m_console->fileset_list);
116 QString statusQuery("SELECT JobStatusLong FROM Status");
117 QStringList statusResults, statusLongList;
118 if (m_console->sql_cmd(statusQuery, statusResults)) {
120 QStringList fieldlist;
121 /* Iterate through the lines of results. */
122 foreach (QString resultline, statusResults) {
123 fieldlist = resultline.split("\t");
124 statusLongList.append(fieldlist[0]);
125 } /* foreach resultline */
126 } /* if results from statusquery */
127 statusComboBox->addItems(statusLongList);
132 int volumeIndex = volumeComboBox->currentIndex();
133 if (volumeIndex != -1)
134 m_mediaName = volumeComboBox->itemText(volumeIndex);
135 query += "SELECT DISTINCT Job.Jobid AS Id, Job.Name AS JobName, Client.Name AS Client,"
136 " Job.Starttime AS JobStart, Job.Type AS JobType,"
137 " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
138 " Job.JobBytes AS Bytes,"
139 " Job.JobStatus AS Status, Status.JobStatusLong AS StatusLong,"
140 " Job.PurgedFiles AS Purged, FileSet.FileSet"
142 " LEFT OUTER JOIN Client ON (Client.ClientId=Job.ClientId)"
143 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)"
144 " LEFT OUTER JOIN Status ON (Job.JobStatus=Status.JobStatus)"
145 " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
146 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
147 QStringList conditions;
148 if (m_mediaName != "Any") {
149 conditions.append("Media.VolumeName='" + m_mediaName + "'");
151 int clientIndex = clientsComboBox->currentIndex();
152 if (clientIndex != -1)
153 m_clientName = clientsComboBox->itemText(clientIndex);
154 if (m_clientName != "Any") {
155 conditions.append("Client.Name='" + m_clientName + "'");
157 int jobIndex = jobComboBox->currentIndex();
158 if ((jobIndex != -1) && (jobComboBox->itemText(jobIndex) != "Any")) {
159 conditions.append("Job.Name='" + jobComboBox->itemText(jobIndex) + "'");
161 int levelIndex = levelComboBox->currentIndex();
162 if ((levelIndex != -1) && (levelComboBox->itemText(levelIndex) != "Any")) {
163 conditions.append("Job.Level='" + levelComboBox->itemText(levelIndex) + "'");
165 int statusIndex = statusComboBox->currentIndex();
166 if ((statusIndex != -1) && (statusComboBox->itemText(statusIndex) != "Any")) {
167 conditions.append("Status.JobStatusLong='" + statusComboBox->itemText(statusIndex) + "'");
169 int purgedIndex = purgedComboBox->currentIndex();
170 if ((purgedIndex != -1) && (purgedComboBox->itemText(purgedIndex) != "Any")) {
171 conditions.append("Job.PurgedFiles='" + purgedComboBox->itemText(purgedIndex) + "'");
173 int fileSetIndex = fileSetComboBox->currentIndex();
174 if ((fileSetIndex != -1) && (fileSetComboBox->itemText(fileSetIndex) != "Any")) {
175 conditions.append("FileSet.FileSet='" + fileSetComboBox->itemText(fileSetIndex) + "'");
177 /* If Limit check box For limit by days is checked */
178 if (daysCheckBox->checkState() == Qt::Checked) {
179 QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
180 QString since = stamp.toString(Qt::ISODate);
181 conditions.append("Job.Starttime>'" + since + "'");
184 foreach (QString condition, conditions) {
186 query += " WHERE " + condition;
189 query += " AND " + condition;
193 query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
194 /* If Limit check box for limit records returned is checked */
195 if (limitCheckBox->checkState() == Qt::Checked) {
197 limit.setNum(limitSpinBox->value());
198 query += " LIMIT " + limit;
201 /* Set up the Header for the table */
202 QStringList headerlist = (QStringList()
203 << "Job Id" << "Job Name" << "Client" << "Job Starttime" << "Job Type"
204 << "Job Level" << "Job Files" << "Job Bytes" << "Job Status" << "Purged" << "File Set" );
205 m_purgedIndex = headerlist.indexOf("Purged");
206 m_typeIndex = headerlist.indexOf("Job Type");
207 statusIndex = headerlist.indexOf("Job Status");
209 /* Initialize the QTableWidget */
210 m_checkCurrentWidget = false;
211 mp_tableWidget->clear();
212 m_checkCurrentWidget = true;
213 mp_tableWidget->setColumnCount(headerlist.size());
214 mp_tableWidget->setHorizontalHeaderLabels(headerlist);
216 /* This could be a user preference debug message?? */
217 printf("Query cmd : %s\n",query.toUtf8().data());
218 if (m_console->sql_cmd(query, results)) {
219 m_resultCount = results.count();
221 QTableWidgetItem* p_tableitem;
223 QStringList fieldlist;
224 mp_tableWidget->setRowCount(results.size());
227 /* Iterate through the record returned from the query */
228 foreach (resultline, results) {
229 fieldlist = resultline.split("\t");
231 bool statusIndexDone = false;
232 QString statusCode("");
233 /* Iterate through fields in the record */
234 foreach (field, fieldlist) {
235 field = field.trimmed(); /* strip leading & trailing spaces */
236 if ((column == statusIndex) && (!statusIndexDone)){
237 statusIndexDone = true;
240 p_tableitem = new QTableWidgetItem(field,1);
241 p_tableitem->setFlags(0);
242 p_tableitem->setForeground(blackBrush);
243 mp_tableWidget->setItem(row, column, p_tableitem);
244 if (column == statusIndex)
245 setStatusColor(p_tableitem, statusCode);
252 /* Resize the columns */
253 mp_tableWidget->resizeColumnsToContents();
254 mp_tableWidget->resizeRowsToContents();
255 mp_tableWidget->verticalHeader()->hide();
256 if ((m_mediaName != "Any") && (m_resultCount == 0)){
257 /* for context sensitive searches, let the user know if there were no
259 QMessageBox::warning(this, tr("Bat"),
260 tr("The Jobs query returned no results.\n"
261 "Press OK to continue?"), QMessageBox::Ok );
265 void JobList::setStatusColor(QTableWidgetItem *item, QString &field)
267 QString greenchars("TCR");
268 QString redchars("BEf");
269 QString yellowchars("eDAFSMmsjdctp");
270 if (greenchars.contains(field, Qt::CaseSensitive)) {
271 item->setBackground(Qt::green);
272 } else if (redchars.contains(field, Qt::CaseSensitive)) {
273 item->setBackground(Qt::red);
274 } else if (yellowchars.contains(field, Qt::CaseSensitive)){
275 item->setBackground(Qt::yellow);
280 * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
281 * The tree has been populated.
283 void JobList::PgSeltreeWidgetClicked()
292 * Virtual function override of pages function which is called when this page
293 * is visible on the stack
295 void JobList::currentStackItem()
299 m_contextActions.append(actionRefreshJobList);
305 * Virtual Function to return the name for the medialist tree widget
307 void JobList::treeWidgetName(QString &desc)
309 if ((m_mediaName == "") && (m_clientName == "")) {
313 if (m_mediaName != "" ) {
314 desc += "on Volume " + m_mediaName;
316 if (m_clientName != "" ) {
317 desc += "of Client " + m_clientName;
323 * This functions much line tableItemChanged for trees like the page selector,
324 * but I will do much less here
326 void JobList::tableItemChanged(QTableWidgetItem *currentItem, QTableWidgetItem * /*previousItem*/)
328 if (m_checkCurrentWidget) {
329 int row = currentItem->row();
330 QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
331 m_currentJob = jobitem->text();
332 jobitem = mp_tableWidget->item(row, m_purgedIndex);
333 QString purged = jobitem->text();
334 mp_tableWidget->removeAction(actionPurgeFiles);
336 mp_tableWidget->addAction(actionPurgeFiles);
338 jobitem = mp_tableWidget->item(row, m_typeIndex);
339 QString status = jobitem->text();
340 mp_tableWidget->removeAction(actionRestoreFromJob);
341 mp_tableWidget->removeAction(actionRestoreFromTime);
343 mp_tableWidget->addAction(actionRestoreFromJob);
344 mp_tableWidget->addAction(actionRestoreFromTime);
350 * Function to create connections for context sensitive menu for this and
353 void JobList::createConnections()
355 /* connect to the action specific to this pages class that shows up in the
356 * page selector tree */
357 connect(actionRefreshJobList, SIGNAL(triggered()), this,
358 SLOT(populateTable()));
359 connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
360 /* for the tableItemChanged to maintain m_currentJob */
361 connect(mp_tableWidget, SIGNAL(
362 currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
363 this, SLOT(tableItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
365 /* Do what is required for the local context sensitive menu */
368 /* setContextMenuPolicy is required */
369 mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
372 mp_tableWidget->addAction(actionRefreshJobList);
373 mp_tableWidget->addAction(actionLongListJob);
374 mp_tableWidget->addAction(actionListJobid);
375 mp_tableWidget->addAction(actionListFilesOnJob);
376 mp_tableWidget->addAction(actionListJobMedia);
377 mp_tableWidget->addAction(actionListVolumes);
378 mp_tableWidget->addAction(actionDeleteJob);
379 mp_tableWidget->addAction(actionPurgeFiles);
380 mp_tableWidget->addAction(actionRestoreFromJob);
381 mp_tableWidget->addAction(actionRestoreFromTime);
382 mp_tableWidget->addAction(actionShowLogForJob);
384 /* Make Connections */
385 connect(actionLongListJob, SIGNAL(triggered()), this,
386 SLOT(consoleLongListJob()));
387 connect(actionListJobid, SIGNAL(triggered()), this,
388 SLOT(consoleListJobid()));
389 connect(actionListFilesOnJob, SIGNAL(triggered()), this,
390 SLOT(consoleListFilesOnJob()));
391 connect(actionListJobMedia, SIGNAL(triggered()), this,
392 SLOT(consoleListJobMedia()));
393 connect(actionListVolumes, SIGNAL(triggered()), this,
394 SLOT(consoleListVolumes()));
395 connect(actionDeleteJob, SIGNAL(triggered()), this,
396 SLOT(consoleDeleteJob()));
397 connect(actionPurgeFiles, SIGNAL(triggered()), this,
398 SLOT(consolePurgeFiles()));
399 connect(actionRestoreFromJob, SIGNAL(triggered()), this,
400 SLOT(preRestoreFromJob()));
401 connect(actionRestoreFromTime, SIGNAL(triggered()), this,
402 SLOT(preRestoreFromTime()));
403 connect(actionShowLogForJob, SIGNAL(triggered()), this,
404 SLOT(showLogForJob()));
408 * Functions to respond to local context sensitive menu sending console commands
409 * If I could figure out how to make these one function passing a string, Yaaaaaa
411 void JobList::consoleLongListJob()
413 QString cmd("llist jobid=");
417 void JobList::consoleListJobid()
419 QString cmd("list jobid=");
423 void JobList::consoleListFilesOnJob()
425 QString cmd("list files jobid=");
429 void JobList::consoleListJobMedia()
431 QString cmd("list jobmedia jobid=");
435 void JobList::consoleListVolumes()
437 QString cmd("list volumes jobid=");
441 void JobList::consoleDeleteJob()
443 if (QMessageBox::warning(this, tr("Bat"),
444 tr("Are you sure you want to delete?? !!!.\n"
445 "This delete command is used to delete a Job record and all associated catalog"
446 " records that were created. This command operates only on the Catalog"
447 " database and has no effect on the actual data written to a Volume. This"
448 " command can be dangerous and we strongly recommend that you do not use"
449 " it unless you know what you are doing. The Job and all its associated"
450 " records (File and JobMedia) will be deleted from the catalog."
451 "Press OK to proceed with delete operation.?"),
452 QMessageBox::Ok | QMessageBox::Cancel)
453 == QMessageBox::Cancel) { return; }
455 QString cmd("delete job jobid=");
459 void JobList::consolePurgeFiles()
461 if (QMessageBox::warning(this, tr("Bat"),
462 tr("Are you sure you want to purge ?? !!!.\n"
463 "The Purge command will delete associated Catalog database records from Jobs and"
464 " Volumes without considering the retention period. Purge works only on the"
465 " Catalog database and does not affect data written to Volumes. This command can"
466 " be dangerous because you can delete catalog records associated with current"
467 " backups of files, and we recommend that you do not use it unless you know what"
469 "Press OK to proceed with the purge operation?"),
470 QMessageBox::Ok | QMessageBox::Cancel)
471 == QMessageBox::Cancel) { return; }
473 QString cmd("purge files jobid=");
479 * Subroutine to call preRestore to restore from a select job
481 void JobList::preRestoreFromJob()
483 new prerestorePage(m_currentJob, R_JOBIDLIST);
487 * Subroutine to call preRestore to restore from a select job
489 void JobList::preRestoreFromTime()
491 new prerestorePage(m_currentJob, R_JOBDATETIME);
495 * Subroutine to call class to show the log in the database from that job
497 void JobList::showLogForJob()
499 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
500 new JobLog(m_currentJob, pageSelectorTreeWidgetItem);