]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/joblist/joblist.cpp
When using "Restore from job" option with a job that was a restore
[bacula/bacula] / bacula / src / qt-console / joblist / joblist.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
5
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.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  *   Version $Id: joblist.h 4230 2007-02-21 20:07:37Z kerns $
30  *
31  *   Dirk Bartley, March 2007
32  */
33  
34 #include <QAbstractEventDispatcher>
35 #include <QTableWidgetItem>
36 #include "bat.h"
37 #include "joblist.h"
38 #include "restore.h"
39 #include "joblog/joblog.h"
40
41 /*
42  * Constructor for the class
43  */
44 JobList::JobList(QString &mediaName, QString &clientname,
45          QTreeWidgetItem *parentTreeWidgetItem)
46 {
47    setupUi(this);
48    m_name = "Clients";
49    m_mediaName = mediaName;
50    m_clientName = clientname;
51    pgInitialize(parentTreeWidgetItem);
52    m_resultCount = 0;
53    m_populated = false;
54    m_closeable = false;
55    if ((m_mediaName != "") || (m_clientName != "")) { m_closeable=true; }
56    m_checkCurrentWidget = true;
57    createConnections();
58
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);
64 }
65
66 /*
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.
71  */
72 void JobList::populateTable()
73 {
74    QStringList results;
75    QString resultline;
76    QBrush blackBrush(Qt::black);
77
78    if (!m_console->preventInUseConnect())
79        return;
80
81    /* Can't do this in constructor because not neccesarily conected in constructor */
82    if (!m_populated) {
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);
88
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)) {
93          QString field;
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);
106       }
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       QString statusQuery("SELECT JobStatusLong FROM Status");
115       QStringList statusResults, statusLongList;
116       if (m_console->sql_cmd(statusQuery, statusResults)) {
117          QString field;
118          QStringList fieldlist;
119          /* Iterate through the lines of results. */
120          foreach (QString resultline, statusResults) {
121             fieldlist = resultline.split("\t");
122             statusLongList.append(fieldlist[0]);
123          } /* foreach resultline */
124       } /* if results from statusquery */
125       statusComboBox->addItems(statusLongList);
126    }
127
128    /* Set up query */
129    QString query("");
130    int volumeIndex = volumeComboBox->currentIndex();
131    if (volumeIndex != -1)
132       m_mediaName = volumeComboBox->itemText(volumeIndex);
133    query += "SELECT DISTINCT Job.Jobid AS Id, Job.Name AS JobName, Client.Name AS Client,"
134             " Job.Starttime AS JobStart, Job.Type AS JobType,"
135             " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
136             " Job.JobBytes AS Bytes,"
137             " Job.JobStatus AS Status, Status.JobStatusLong AS StatusLong,"
138             " Job.PurgedFiles AS Purged"
139             " FROM Job,Client,Status";
140    if (m_mediaName != "Any") {
141       query += ",JobMedia,Media";
142    }
143    query += " WHERE Client.ClientId=Job.ClientId AND Job.JobStatus=Status.JobStatus";
144    if (m_mediaName != "Any") {
145       query += " AND JobMedia.JobId=Job.JobId AND JobMedia.MediaId=Media.MediaId"
146                " AND Media.VolumeName='" + m_mediaName + "'";
147    }
148    int clientIndex = clientsComboBox->currentIndex();
149    if (clientIndex != -1)
150       m_clientName = clientsComboBox->itemText(clientIndex);
151    if (m_clientName != "Any") {
152       query += " AND Client.Name='" + m_clientName + "'";
153    }
154    int jobIndex = jobComboBox->currentIndex();
155    if ((jobIndex != -1) && (jobComboBox->itemText(jobIndex) != "Any")) {
156       query += " AND Job.Name='" + jobComboBox->itemText(jobIndex) + "'";
157    }
158    int levelIndex = levelComboBox->currentIndex();
159    if ((levelIndex != -1) && (levelComboBox->itemText(levelIndex) != "Any")) {
160       query += " AND Job.Level='" + levelComboBox->itemText(levelIndex) + "'";
161    }
162    int statusIndex = statusComboBox->currentIndex();
163    if ((statusIndex != -1) && (statusComboBox->itemText(statusIndex) != "Any")) {
164       query += " AND Status.JobStatusLong='" + statusComboBox->itemText(statusIndex) + "'";
165    }
166    int purgedIndex = purgedComboBox->currentIndex();
167    if ((purgedIndex != -1) && (purgedComboBox->itemText(purgedIndex) != "Any")) {
168       query += " AND Job.PurgedFiles='" + purgedComboBox->itemText(purgedIndex) + "'";
169    }
170    /* If Limit check box For limit by days is checked  */
171    if (daysCheckBox->checkState() == Qt::Checked) {
172       QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
173       QString since = stamp.toString(Qt::ISODate);
174       query += " AND Job.Starttime>'" + since + "'";
175    }
176    /* Descending */
177    query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
178    /* If Limit check box for limit records returned is checked  */
179    if (limitCheckBox->checkState() == Qt::Checked) {
180       QString limit;
181       limit.setNum(limitSpinBox->value());
182       query += " LIMIT " + limit;
183    }
184
185    /* Set up the Header for the table */
186    QStringList headerlist = (QStringList()
187       << "Job Id" << "Job Name" << "Client" << "Job Starttime" << "Job Type" 
188       << "Job Level" << "Job Files" << "Job Bytes" << "Job Status"  << "Purged" );
189    m_purgedIndex = headerlist.indexOf("Purged");
190    m_typeIndex = headerlist.indexOf("Job Type");
191    statusIndex = headerlist.indexOf("Job Status");
192
193    /* Initialize the QTableWidget */
194    m_checkCurrentWidget = false;
195    mp_tableWidget->clear();
196    m_checkCurrentWidget = true;
197    mp_tableWidget->setColumnCount(headerlist.size());
198    mp_tableWidget->setHorizontalHeaderLabels(headerlist);
199
200    /*  This could be a user preference debug message?? */
201    //printf("Query cmd : %s\n",query.toUtf8().data());
202    if (m_console->sql_cmd(query, results)) {
203       m_resultCount = results.count();
204
205       QTableWidgetItem* p_tableitem;
206       QString field;
207       QStringList fieldlist;
208       mp_tableWidget->setRowCount(results.size());
209
210       int row = 0;
211       /* Iterate through the record returned from the query */
212       foreach (resultline, results) {
213          fieldlist = resultline.split("\t");
214          int column = 0;
215          bool statusIndexDone = false;
216          QString statusCode("");
217          /* Iterate through fields in the record */
218          foreach (field, fieldlist) {
219             field = field.trimmed();  /* strip leading & trailing spaces */
220             if ((column == statusIndex) && (!statusIndexDone)){
221                statusIndexDone = true;
222                statusCode = field;
223             } else {
224                p_tableitem = new QTableWidgetItem(field,1);
225                p_tableitem->setFlags(0);
226                p_tableitem->setForeground(blackBrush);
227                mp_tableWidget->setItem(row, column, p_tableitem);
228                if (column == statusIndex)
229                   setStatusColor(p_tableitem, statusCode);
230                column++;
231             }
232          }
233          row++;
234       }
235    } 
236    /* Resize the columns */
237    mp_tableWidget->resizeColumnsToContents();
238    mp_tableWidget->resizeRowsToContents();
239    mp_tableWidget->verticalHeader()->hide();
240    if ((m_mediaName != "Any") && (m_resultCount == 0)){
241       /* for context sensitive searches, let the user know if there were no
242        * results */
243       QMessageBox::warning(this, tr("Bat"),
244           tr("The Jobs query returned no results.\n"
245          "Press OK to continue?"), QMessageBox::Ok );
246    }
247 }
248
249 void JobList::setStatusColor(QTableWidgetItem *item, QString &field)
250 {
251    QString greenchars("TCR");
252    QString redchars("BEf");
253    QString yellowchars("eDAFSMmsjdctp");
254    if (greenchars.contains(field, Qt::CaseSensitive)) {
255       item->setBackground(Qt::green);
256    } else if (redchars.contains(field, Qt::CaseSensitive)) {
257       item->setBackground(Qt::red);
258    } else if (yellowchars.contains(field, Qt::CaseSensitive)){ 
259       item->setBackground(Qt::yellow);
260    }
261 }
262
263 /*
264  * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
265  * The tree has been populated.
266  */
267 void JobList::PgSeltreeWidgetClicked()
268 {
269    populateTable();
270    if (!m_populated) {
271       m_populated=true;
272    }
273 }
274
275 /*
276  *  Virtual function override of pages function which is called when this page
277  *  is visible on the stack
278  */
279 void JobList::currentStackItem()
280 {
281    if (!m_populated) {
282       populateTable();
283       m_contextActions.append(actionRefreshJobList);
284       m_populated=true;
285    }
286 }
287
288 /*
289  * Virtual Function to return the name for the medialist tree widget
290  */
291 void JobList::treeWidgetName(QString &desc)
292 {
293    if ((m_mediaName == "") && (m_clientName == "")) {
294       desc = "Jobs";
295    } else {
296       desc = "Jobs ";
297       if (m_mediaName != "" ) {
298          desc += "on Volume " + m_mediaName;
299       }
300       if (m_clientName != "" ) {
301          desc += "of Client " + m_clientName;
302       }
303    }
304 }
305
306 /*
307  * This functions much line tableItemChanged for trees like the page selector,
308  * but I will do much less here
309  */
310 void JobList::tableItemChanged(QTableWidgetItem *currentItem, QTableWidgetItem * /*previousItem*/)
311 {
312    if (m_checkCurrentWidget) {
313       int row = currentItem->row();
314       QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
315       m_currentJob = jobitem->text();
316       jobitem = mp_tableWidget->item(row, m_purgedIndex);
317       QString purged = jobitem->text();
318       mp_tableWidget->removeAction(actionPurgeFiles);
319       if (purged == "0") {
320          mp_tableWidget->addAction(actionPurgeFiles);
321       }
322       jobitem = mp_tableWidget->item(row, m_typeIndex);
323       QString status = jobitem->text();
324       mp_tableWidget->removeAction(actionRestoreFromJob);
325       mp_tableWidget->removeAction(actionRestoreFromTime);
326       if (status == "B") {
327          mp_tableWidget->addAction(actionRestoreFromJob);
328          mp_tableWidget->addAction(actionRestoreFromTime);
329       }
330    }
331 }
332
333 /*
334  * Function to create connections for context sensitive menu for this and
335  * the page selector
336  */
337 void JobList::createConnections()
338 {
339    /* connect to the action specific to this pages class that shows up in the 
340     * page selector tree */
341    connect(actionRefreshJobList, SIGNAL(triggered()), this,
342                 SLOT(populateTable()));
343    connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
344    /* for the tableItemChanged to maintain m_currentJob */
345    connect(mp_tableWidget, SIGNAL(
346            currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
347            this, SLOT(tableItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
348
349    /* Do what is required for the local context sensitive menu */
350
351
352    /* setContextMenuPolicy is required */
353    mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
354
355    /* Add Actions */
356    mp_tableWidget->addAction(actionRefreshJobList);
357    mp_tableWidget->addAction(actionLongListJob);
358    mp_tableWidget->addAction(actionListJobid);
359    mp_tableWidget->addAction(actionListFilesOnJob);
360    mp_tableWidget->addAction(actionListJobMedia);
361    mp_tableWidget->addAction(actionListVolumes);
362    mp_tableWidget->addAction(actionDeleteJob);
363    mp_tableWidget->addAction(actionPurgeFiles);
364    mp_tableWidget->addAction(actionRestoreFromJob);
365    mp_tableWidget->addAction(actionRestoreFromTime);
366    mp_tableWidget->addAction(actionShowLogForJob);
367
368    /* Make Connections */
369    connect(actionLongListJob, SIGNAL(triggered()), this,
370                 SLOT(consoleLongListJob()));
371    connect(actionListJobid, SIGNAL(triggered()), this,
372                 SLOT(consoleListJobid()));
373    connect(actionListFilesOnJob, SIGNAL(triggered()), this,
374                 SLOT(consoleListFilesOnJob()));
375    connect(actionListJobMedia, SIGNAL(triggered()), this,
376                 SLOT(consoleListJobMedia()));
377    connect(actionListVolumes, SIGNAL(triggered()), this,
378                 SLOT(consoleListVolumes()));
379    connect(actionDeleteJob, SIGNAL(triggered()), this,
380                 SLOT(consoleDeleteJob()));
381    connect(actionPurgeFiles, SIGNAL(triggered()), this,
382                 SLOT(consolePurgeFiles()));
383    connect(actionRestoreFromJob, SIGNAL(triggered()), this,
384                 SLOT(preRestoreFromJob()));
385    connect(actionRestoreFromTime, SIGNAL(triggered()), this,
386                 SLOT(preRestoreFromTime()));
387    connect(actionShowLogForJob, SIGNAL(triggered()), this,
388                 SLOT(showLogForJob()));
389 }
390
391 /*
392  * Functions to respond to local context sensitive menu sending console commands
393  * If I could figure out how to make these one function passing a string, Yaaaaaa
394  */
395 void JobList::consoleLongListJob()
396 {
397    QString cmd("llist jobid=");
398    cmd += m_currentJob;
399    consoleCommand(cmd);
400 }
401 void JobList::consoleListJobid()
402 {
403    QString cmd("list jobid=");
404    cmd += m_currentJob;
405    consoleCommand(cmd);
406 }
407 void JobList::consoleListFilesOnJob()
408 {
409    QString cmd("list files jobid=");
410    cmd += m_currentJob;
411    consoleCommand(cmd);
412 }
413 void JobList::consoleListJobMedia()
414 {
415    QString cmd("list jobmedia jobid=");
416    cmd += m_currentJob;
417    consoleCommand(cmd);
418 }
419 void JobList::consoleListVolumes()
420 {
421    QString cmd("list volumes jobid=");
422    cmd += m_currentJob;
423    consoleCommand(cmd);
424 }
425 void JobList::consoleDeleteJob()
426 {
427    if (QMessageBox::warning(this, tr("Bat"),
428       tr("Are you sure you want to delete??  !!!.\n"
429 "This delete command is used to delete a Job record and all associated catalog"
430 " records that were created. This command operates only on the Catalog"
431 " database and has no effect on the actual data written to a Volume. This"
432 " command can be dangerous and we strongly recommend that you do not use"
433 " it unless you know what you are doing.  The Job and all its associated"
434 " records (File and JobMedia) will be deleted from the catalog."
435       "Press OK to proceed with delete operation.?"),
436       QMessageBox::Ok | QMessageBox::Cancel)
437       == QMessageBox::Cancel) { return; }
438
439    QString cmd("delete job jobid=");
440    cmd += m_currentJob;
441    consoleCommand(cmd);
442 }
443 void JobList::consolePurgeFiles()
444 {
445    if (QMessageBox::warning(this, tr("Bat"),
446       tr("Are you sure you want to purge ??  !!!.\n"
447 "The Purge command will delete associated Catalog database records from Jobs and"
448 " Volumes without considering the retention period. Purge  works only on the"
449 " Catalog database and does not affect data written to Volumes. This command can"
450 " be dangerous because you can delete catalog records associated with current"
451 " backups of files, and we recommend that you do not use it unless you know what"
452 " you are doing.\n"
453       "Press OK to proceed with the purge operation?"),
454       QMessageBox::Ok | QMessageBox::Cancel)
455       == QMessageBox::Cancel) { return; }
456
457    QString cmd("purge files jobid=");
458    cmd += m_currentJob;
459    consoleCommand(cmd);
460 }
461
462 /*
463  * Subroutine to call preRestore to restore from a select job
464  */
465 void JobList::preRestoreFromJob()
466 {
467    new prerestorePage(m_currentJob, R_JOBIDLIST);
468 }
469
470 /*
471  * Subroutine to call preRestore to restore from a select job
472  */
473 void JobList::preRestoreFromTime()
474 {
475    new prerestorePage(m_currentJob, R_JOBDATETIME);
476 }
477
478 /*
479  * Subroutine to call class to show the log in the database from that job
480  */
481 void JobList::showLogForJob()
482 {
483    QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
484    new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
485 }