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