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