]> git.sur5r.net Git - bacula/bacula/commitdiff
baculum: Add dashboard panel
authorMarcin Haba <marcin.haba@bacula.pl>
Fri, 25 Dec 2015 19:51:24 +0000 (20:51 +0100)
committerMarcin Haba <marcin.haba@bacula.pl>
Fri, 25 Dec 2015 19:51:24 +0000 (20:51 +0100)
39 files changed:
gui/baculum/protected/Class/Bconsole.php
gui/baculum/protected/Class/Database.php
gui/baculum/protected/Class/JobManager.php
gui/baculum/protected/Class/Miscellaneous.php
gui/baculum/protected/JavaScript/graph.js
gui/baculum/protected/JavaScript/misc.js
gui/baculum/protected/JavaScript/panel-window.js
gui/baculum/protected/JavaScript/slide-window.js
gui/baculum/protected/JavaScript/statistics.js [new file with mode: 0644]
gui/baculum/protected/Lang/en/messages.mo
gui/baculum/protected/Lang/en/messages.po
gui/baculum/protected/Lang/pl/messages.mo
gui/baculum/protected/Lang/pl/messages.po
gui/baculum/protected/Layouts/Main.tpl
gui/baculum/protected/Pages/API/DbSize.php [new file with mode: 0644]
gui/baculum/protected/Pages/API/JobTotals.php [new file with mode: 0644]
gui/baculum/protected/Pages/Home.page
gui/baculum/protected/Pages/Home.php
gui/baculum/protected/Pages/Monitor.php
gui/baculum/protected/Portlets/TrayBar.tpl
gui/baculum/protected/application.xml
gui/baculum/themes/Baculum-v1/client.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/clients.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/dashboard.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/database.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/finished-jobs.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/graphs.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/jobrun.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/jobs.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/jobtotals.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/pools.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/quick-access.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/restore.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/setting.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/storages.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/style.css
gui/baculum/themes/Baculum-v1/tapes.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/volumes.png [new file with mode: 0644]
gui/baculum/themes/Baculum-v1/workspace.png [new file with mode: 0644]

index a3f27202bcf9bd387938dae0085d0586dfa05cfc..9dc27690df6c34fe47bc4016c90a85dff3dd0512 100644 (file)
@@ -91,6 +91,7 @@ class Bconsole extends TModule {
        }
 
        private function execCommand($director, array $command, $user) {
+               $cmd = '';
                if(!is_null($director) && $this->isValidDirector($director) === false) {
                        $output = array(BconsoleError::MSG_ERROR_INVALID_DIRECTOR, '');
                        $exitcode = BconsoleError::ERROR_INVALID_DIRECTOR;
index 0d6a9b356a342a5b4eee7155018c54865b64a04a..a2fdfd71a922e610e93bbb6a3bbc0c617404a797 100644 (file)
@@ -119,5 +119,38 @@ class Database extends TModule {
                $ret = (array_key_exists('versionid', $result) === true) ? $result['versionid'] : false;
                return $ret;
        }
+
+       public function getDatabaseSize() {
+               $configuration = $this->Application->getModule('configuration');
+               $db_params = $this->getDBParams();
+               $dbtype = $db_params['type'];
+               $dbname = $db_params['name'];
+
+               $db = new ActiveRecord();
+               $connection = $db->getDbConnection();
+               $connection->setActive(true);
+               $pdo = $connection->getPdoInstance();
+
+               $dbsize = 0;
+               if ($configuration->isPostgreSQLType($dbtype)) {
+                       $sql = "SELECT pg_database_size('$dbname') AS dbsize";
+                       $result = $pdo->query($sql);
+                       $dbsize = $result->fetch();
+               } else if ($configuration->isMySQLType($dbtype)) {
+                       $sql = "SELECT Sum(data_length + index_length) AS dbsize FROM information_schema.tables";
+                       $result = $pdo->query($sql);
+                       $dbsize = $result->fetch();
+               } else if ($configuration->isSQLiteType($dbtype)) {
+                       $sql = "PRAGMA page_count";
+                       $result = $pdo->query($sql);
+                       $page_count = $result->fetch();
+                       $sql = "PRAGMA page_size";
+                       $result = $pdo->query($sql);
+                       $page_size = $result->fetch();
+                       $dbsize = array('dbsize' => ($page_count['page_count'] * $page_size['page_size']));
+               }
+               $pdo = null;
+               return $dbsize['dbsize'];
+       }
 }
-?>
\ No newline at end of file
+?>
index d4d5fc4ae40ec8ce8104e4666d7e661d4c01c31f..cf53733d6b33eca9743999e6c8bfd67ce5761050 100644 (file)
@@ -71,5 +71,19 @@ class JobManager extends TModule {
                }
                return $jobids;
        }
+
+       public function getJobTotals() {
+               $jobtotals = array('bytes' => 0, 'files' => 0);
+               $connection = JobRecord::finder()->getDbConnection();
+               $connection->setActive(true);
+               $sql = "SELECT sum(JobFiles) AS files, sum(JobBytes) AS bytes FROM Job";
+               $pdo = $connection->getPdoInstance();
+               $result = $pdo->query($sql);
+               $ret = $result->fetch();
+               $jobtotals['bytes'] = $ret['bytes'];
+               $jobtotals['files'] = $ret['files'];
+               $pdo = null;
+               return $jobtotals;
+       }
 }
 ?>
index f1e33a03be2c07a6348615ffa50c296cbf364a3d..1767f1ebb3aceefe19ebc9e516d8a64dbef878a7 100644 (file)
@@ -75,6 +75,12 @@ class Miscellaneous extends TModule {
                'L' => array('value' => 'Commiting data', 'description' =>'Committing data (last despool)')
        );
 
+       private $jobStatesOK = array('T', 'D');
+       private $jobStatesWarning = array('W');
+       private $jobStatesError = array('E', 'e', 'f', 'I');
+       private $jobStatesCancel = array('A');
+       private $jobStatesRunning = array('C', 'R', 'B', 'F', 'S', 'm', 'M', 's', 'j', 'c', 'd','t', 'p', 'i', 'a', 'l', 'L');
+
        private $runningJobStates = array('C', 'R');
 
        /**
@@ -117,6 +123,34 @@ class Miscellaneous extends TModule {
 
        }
 
+       public function getJobStatesByType($type) {
+               $statesByType = array();
+               $states = array();
+               switch($type) {
+                       case 'ok':
+                               $states = $this->jobStatesOK;
+                               break;
+                       case 'warning':
+                               $states = $this->jobStatesWarning;
+                               break;
+                       case 'error':
+                               $states = $this->jobStatesError;
+                               break;
+                       case 'cancel':
+                               $states = $this->jobStatesCancel;
+                               break;
+                       case 'running':
+                               $states = $this->jobStatesRunning;
+                               break;
+               }
+
+               for ($i = 0; $i < count($states); $i++) {
+                       $statesByType[$states[$i]] = $this->getJobState($states[$i]);
+               }
+
+               return $statesByType;
+       }
+
        public function isValidJobLevel($jobLevel) {
                return array_key_exists($jobLevel, $this->getJobLevels());
        }
index c8c92c765caa2bd618746a614f63d6e8ad39a5bc..ed3f90949f8068a01feef08f4d54d1e5f01a5ce9 100644 (file)
@@ -411,6 +411,67 @@ var GraphClass = Class.create({
        }
 });
 
+var GraphPieClass = Class.create({
+       jobs: [],
+       container: null,
+       series: null,
+       pie: null,
+       graph_options: {
+               colors: ['#63c422', '#d70808', '#FFFF66', 'orange', 'blue'],
+               HtmlText: false,
+               fontColor: '#ffffff',
+               grid: {
+                       verticalLines : false,
+                       horizontalLines : false,
+                       outlineWidth: 0,
+               },
+               xaxis: { showLabels : false,},
+               yaxis: { showLabels : false },
+               pie: {
+                       show : true,
+                       explode : 5,
+                       labelFormatter: PieGraph.pie_label_formatter,
+                       shadowSize: 4,
+                       fillOpacity: 1,
+                       sizeRatio: 0.6
+               },
+               mouse: {
+                       track : true,
+                       trackFormatter: PieGraph.pie_track_formatter,
+                       relative: true
+               },
+               legend: {
+                       position : 'se',
+                       backgroundColor : '#D2E8FF',
+                       margin: 0
+               }
+       },
+       initialize: function(jobs, container_id) {
+               this.jobs = jobs;
+               this.container = document.getElementById(container_id);
+               this.series = this.prepare_series();
+               this.draw_grah();
+       },
+       prepare_series: function() {
+               var series = [];
+               var label, serie;
+               var job_types = Object.keys(this.jobs);
+               var jobs_count;
+               for (var i = 0; i < job_types.length; i++) {
+                       label = job_types[i];
+                       jobs_count = this.jobs[label].length;
+                       serie = {
+                               data: [[0, jobs_count]],
+                               label: label + ' (' + jobs_count.toString() + ')'
+                       }
+                       series.push(serie);
+               }
+               return series;
+       },
+       draw_grah: function() {
+               this.pie = Flotr.draw(this.container, this.series, this.graph_options);
+       }
+});
 
 var iso_date_to_timestamp = function(iso_date) {
        var date_split = iso_date.split(' ');
index 34f93eba0e3472748ddf346fd8f453bfb0b951a3..9500c0302601e1f9a179d3287886498fc83a5bf2 100644 (file)
@@ -1,5 +1,9 @@
 var Units = {
        get_decimal_size: function(size) {
+               if (size === null) {
+                       size = 0;
+               }
+
                size = parseInt(size, 10);
                var size_unit = 'B';
                var units = ['K', 'M', 'G', 'T', 'P'];
@@ -17,6 +21,35 @@ var Units = {
        }
 }
 
+var Strings = {
+       limits: {
+               label: 15
+       },
+       get_short_label: function(txt) {
+               var short_txt = txt;
+               var cut = ((this.limits.label - 2) / 2);
+               if (txt.length > this.limits.label) {
+                       short_txt = txt.substr(0, cut) + '..' + txt.substr(-cut);
+               }
+               return short_txt;
+       }
+}
+
+var PieGraph  = {
+       pie_label_formatter: function (total, value) {
+               var percents =  (100 * value / total).toFixed(1);
+               if (percents >= 1) {
+                       percents = percents.toString() + '%';
+               } else {
+                       percents = '';
+               }
+               return percents;
+       },
+       pie_track_formatter: function(e) {
+               return e.series.label;
+       }
+}
+
 var Formatters = {
        formatter: [
                {css_class: 'size', format_func: Units.get_decimal_size}
@@ -62,3 +95,133 @@ var Cookies = {
                return cookie_val;
        }
 }
+
+var Dashboard = {
+       stats: null,
+       txt: null,
+       pie: null,
+       noval: '-',
+       ids: {
+               clients: {
+                       no: 'clients_no',
+                       most: 'clients_most',
+                       jobs: 'clients_jobs'
+               },
+               jobs: {
+                       to_view: 'jobs_to_view',
+                       most: 'jobs_most',
+                       most_count: 'jobs_most_count'
+               },
+               jobtotals: {
+                       total_bytes: 'jobs_total_bytes',
+                       total_files: 'jobs_total_files'
+               },
+               database: {
+                       size: 'database_size'
+               },
+               pools: {
+                       no: 'pools_no',
+                       most: 'pools_most',
+                       jobs: 'pools_jobs'
+               },
+               pie_summary: 'jobs_summary_graph'
+       },
+       update_all: function(statistics, txt) {
+               this.stats = statistics;
+               this.txt = txt;
+               this.update_pie_jobstatus();
+               this.update_clients();
+               this.update_job_access();
+               this.update_jobs();
+               this.update_jobtotals();
+               this.update_database();
+               this.update_pools();
+       },
+       update_clients: function() {
+               var clients = this.stats.clients_occupancy;
+               var most_occuped_client = this.noval;
+               var occupancy = -1;
+               for (client in clients) {
+                       if (occupancy < clients[client]) {
+                               most_occuped_client = client;
+                               occupancy = clients[client];
+                       }
+               }
+
+               if (occupancy === -1) {
+                       occupancy = 0;
+               }
+
+               document.getElementById(this.ids.clients.no).textContent = Object.keys(this.stats.clients).length;
+               document.getElementById(this.ids.clients.most).setAttribute('title', most_occuped_client);
+               document.getElementById(this.ids.clients.most).textContent = Strings.get_short_label(most_occuped_client);
+               document.getElementById(this.ids.clients.jobs).textContent = occupancy;
+       },
+       update_job_access: function() {
+               var jobs_combobox= document.getElementById(this.ids.jobs.to_view);
+               jobs_combobox.innerHTML = '';
+               var last_jobs = this.stats.jobs.slice(0, 100);
+               for (var i = 0; i < last_jobs.length; i++) {
+                       var opt = document.createElement('OPTION');
+                       var txt = '[' + last_jobs[i].jobid + '] ' + last_jobs[i].name + ' (' + this.txt.level + ': ' + last_jobs[i].level + ' ' + this.txt.status + ': ' + last_jobs[i].jobstatus + ' ' + this.txt.starttime + ': ' + last_jobs[i].starttime + ')';
+                       var label = document.createTextNode(txt);
+                       opt.value = last_jobs[i].jobid;
+                       opt.appendChild(label);
+                       jobs_combobox.appendChild(opt);
+               }
+       },
+       update_jobs: function() {
+               var jobs = this.stats.jobs_occupancy;
+               var most_occuped_job = this.noval;
+               var occupancy = -1;
+               for (job in jobs) {
+                       if (occupancy < jobs[job]) {
+                               most_occuped_job = job;
+                               occupancy = jobs[job];
+                       }
+               }
+
+               if (occupancy === -1) {
+                       occupancy = 0;
+               }
+
+               document.getElementById(this.ids.jobs.most).setAttribute('title',most_occuped_job);
+               document.getElementById(this.ids.jobs.most).textContent = Strings.get_short_label(most_occuped_job);
+               document.getElementById(this.ids.jobs.most_count).textContent = occupancy;
+       },
+       update_jobtotals: function() {
+               document.getElementById(this.ids.jobtotals.total_bytes).textContent = Units.get_decimal_size(this.stats.jobtotals.bytes);
+               document.getElementById(this.ids.jobtotals.total_files).textContent = this.stats.jobtotals.files || 0;
+       },
+       update_database: function() {
+               document.getElementById(this.ids.database.size).textContent = Units.get_decimal_size(this.stats.dbsize);
+       },
+       update_pools: function() {
+               var pools = this.stats.pools_occupancy;
+               var most_occuped_pool = this.noval;
+               var occupancy = -1;
+               for (pool in pools) {
+                       if (occupancy < pools[pool]) {
+                               most_occuped_pool = pool;
+                               occupancy = pools[pool];
+                       }
+               }
+
+               if (occupancy === -1) {
+                       occupancy = 0;
+               }
+
+               document.getElementById(this.ids.pools.no).textContent = Object.keys(this.stats.pools).length;
+               document.getElementById(this.ids.pools.most).setAttribute('title', most_occuped_pool);
+               document.getElementById(this.ids.pools.most).textContent = Strings.get_short_label(most_occuped_pool);
+               document.getElementById(this.ids.pools.jobs).textContent = occupancy;
+       },
+       update_pie_jobstatus: function() {
+               if (PanelWindow.currentWindowId === 'dashboard') {
+                       if (this.pie != null) {
+                               this.pie.pie.destroy();
+                       }
+                       this.pie = new GraphPieClass(this.stats.jobs_summary, this.ids.pie_summary);
+               }
+       }
+}
index 77bd1a11ebc1f43b5eb8b0e10398fed6664a5e25..82881cb1dc90a2502c73bfef228eb2b3de601668 100644 (file)
@@ -1,18 +1,28 @@
 var PanelWindowClass = Class.create({
 
-       currentWindow: null,
-       windowElements: ['container', 'graphs'],
+       currentWindowId: null,
+       windowIds: ['dashboard', 'container', 'graphs'],
        onShow: null,
 
+       initialize: function() {
+               this.currentWindowId = this.windowIds[0];
+       },
+
        hideOthers: function() {
-                this.windowElements.each(function(element) {
-                        if(element != this.currentWindow) {
-                               Effect.toggle(element, 'slide', {duration: 0.3, afterFinish : function() {
-                                               $(element).hide();
-                                       }.bind(element)
+               var hide_panel_by_id = function(id) {
+                       var el = $(id);
+                       if(el.visible() === true && id != this.currentWindowId) {
+                               Effect.toggle(el, 'slide', {
+                                       duration: 0.3,
+                                       afterFinish: function() {
+                                               el.hide();
+                                       }.bind(el)
                                });
                        }
-                }.bind(this));
+               }
+               for (var i = 0, j = 1; i < this.windowIds.length; i++, j++) {
+                       hide_panel_by_id(this.windowIds[i]);
+               }
        },
 
        show: function(id) {
@@ -20,7 +30,7 @@ var PanelWindowClass = Class.create({
                        return;
                }
 
-               this.currentWindow = id;
+               this.currentWindowId = id;
                Effect.toggle(id, 'slide', {
                        duration: 0.3,
                        beforeStart: function() {
@@ -30,6 +40,7 @@ var PanelWindowClass = Class.create({
                                if (this.onShow) {
                                        this.onShow();
                                }
+                               setContentWidth();
                        }.bind(this)
                });
        }
index 202ee88845a8d38cb2912f0eb1653111e28442e8..557b6e826de76b064d4b055f40ae4f643f157a96 100644 (file)
@@ -596,6 +596,15 @@ var SlideWindowClass = Class.create({
        },
        setInitElementId: function(id) {
                this.initElementId = id;
+       },
+       quickJumpToElement: function(id, btn_id, panel_obj) {
+               this.setInitElementId(id);
+               panel_obj.show('container');
+               if (this.isWindowOpen() === true) {
+                       this.openConfigurationById(id);
+               } else {
+                       $(btn_id).click();
+               }
        }
 });
 
diff --git a/gui/baculum/protected/JavaScript/statistics.js b/gui/baculum/protected/JavaScript/statistics.js
new file mode 100644 (file)
index 0000000..b211691
--- /dev/null
@@ -0,0 +1,75 @@
+var Statistics = {
+       jobs: null,
+       clients: null,
+       pools: null,
+       jobtotals: null,
+       dbsize: null,
+       clients_occupancy: {},
+       pools_occupancy: {},
+       jobs_summary: [],
+       grab_statistics: function(data, jobstates) {
+               this.jobs = data.jobs;
+               this.clients = data.clients;
+               this.pools = data.pools;
+               this.jobtotals = data.jobtotals;
+               this.dbsize = data.dbsize;
+               var jobs_count = this.jobs.length;
+               var clients_occupancy = {};
+               var pools_occupancy = {};
+               var jobs_occupancy = {};
+               var jobs_summary = {
+                       ok: [],
+                       error: [],
+                       warning: [],
+                       cancel: [],
+                       running: []
+               };
+               var status_type;
+               for (var i = 0; i < jobs_count; i++) {
+                       if (typeof(clients_occupancy[this.jobs[i].clientid]) === 'undefined') {
+                               clients_occupancy[this.jobs[i].clientid] = 1;
+                       } else {
+                               clients_occupancy[this.jobs[i].clientid] += 1;
+                       }
+
+                       if (typeof(pools_occupancy[this.jobs[i].poolid]) === 'undefined') {
+                               pools_occupancy[this.jobs[i].poolid] = 1;
+                       } else {
+                               pools_occupancy[this.jobs[i].poolid] += 1;
+                       }
+
+                       if (typeof(jobs_occupancy[this.jobs[i].name]) === 'undefined') {
+                               jobs_occupancy[this.jobs[i].name] = 1;
+                       } else {
+                               jobs_occupancy[this.jobs[i].name] += 1;
+                       }
+                       if (jobstates.hasOwnProperty(this.jobs[i].jobstatus)) {
+                               status_type = jobstates[this.jobs[i].jobstatus].type;
+                               if (status_type == 'ok' && this.jobs[i].joberrors > 0) {
+                                       status_type = 'warning';
+                               }
+                               jobs_summary[status_type].push(this.jobs[i]);
+                       }
+               }
+               var clients_ids = Object.keys(clients_occupancy);
+               for (var i = 0; i < clients_ids.length; i++) {
+                       for (var j = 0; j < this.clients.length; j++) {
+                               if (clients_ids[i] == this.clients[j].clientid) {
+                                       this.clients_occupancy[this.clients[j].name] = clients_occupancy[clients_ids[i]];
+                               }
+                       }
+               }
+
+               var pools_ids = Object.keys(pools_occupancy);
+               for (var i = 0; i < pools_ids.length; i++) {
+                       for (var j = 0; j < this.pools.length; j++) {
+                               if (pools_ids[i] == this.pools[j].poolid) {
+                                       this.pools_occupancy[this.pools[j].name] = pools_occupancy[pools_ids[i]];
+                               }
+                       }
+               }
+
+               this.jobs_occupancy = jobs_occupancy;
+               this.jobs_summary = jobs_summary;
+       }
+}
index 14202632c6ef9dd69c127a1b5481ef74c382a50b..bf230e4dea11bf56352fd4a9cb92eadef0095287 100644 (file)
Binary files a/gui/baculum/protected/Lang/en/messages.mo and b/gui/baculum/protected/Lang/en/messages.mo differ
index 790af8a41f8f6130fd3e3df139a12aad032a576d..b4dcf0f6f406b6f25fa15cac60cf428ce54883dd 100644 (file)
@@ -1022,6 +1022,78 @@ msgstr "Job:"
 msgid "Media Type"
 msgstr "Media Type"
 
+msgid "Dashboard"
+msgstr "Dashboard"
+
 msgid "Go to started job after start:"
 msgstr "Go to started job after start:"
 
+msgid "Jobs status summary"
+msgstr "Jobs status summary"
+
+msgid "Quick job access"
+msgstr "Quick job access"
+
+msgid "Finished jobs"
+msgstr "Finished jobs"
+
+msgid "Most often used:"
+msgstr "Most often used:"
+
+msgid "Number of clients:"
+msgstr "Number of clients:"
+
+msgid "jobs"
+msgstr "jobs"
+
+msgid "Jobs count on most used:"
+msgstr "Jobs count on most used:"
+
+msgid "level"
+msgstr "level"
+
+msgid "status"
+msgstr "status"
+
+msgid "Jump to job (last 100):"
+msgstr "Jump to job (last 100):"
+
+msgid "View"
+msgstr "View"
+
+msgid "Total bytes:"
+msgstr "Total bytes:"
+
+msgid "Total files:"
+msgstr "Total files:"
+
+msgid "Database size:"
+msgstr "Database size:"
+
+msgid "Number of pools:"
+msgstr "Number of pools:"
+
+msgid "times"
+msgstr "times"
+
+msgid "Execution count most used:"
+msgstr "Execution count most used:"
+
+msgid "Job Totals"
+msgstr "Job Totals"
+
+msgid "Restore Wizard"
+msgstr "Restore Wizard"
+
+msgid "Perform Restore"
+msgstr "Perform Restore"
+
+msgid "Configuration Wizard"
+msgstr "Configuration Wizard"
+
+msgid "Baculum Settings"
+msgstr "Baculum Settings"
+
+msgid "start time"
+msgstr "start time"
+
index 953bc6e95a16a4efa61f341f6e1eb895e721d993..ba936762c4e5e1bd52f7b3c3fab154869bb01372 100644 (file)
Binary files a/gui/baculum/protected/Lang/pl/messages.mo and b/gui/baculum/protected/Lang/pl/messages.mo differ
index 8292ffd2b842e964402f313a4ac6c0054298c555..eb0f93f57cde5e970c91f22037d72aaa93a98cb0 100644 (file)
@@ -1023,6 +1023,78 @@ msgstr "Zadanie:"
 msgid "Media Type"
 msgstr "Media Type"
 
+msgid "Dashboard"
+msgstr "Start"
+
 msgid "Go to started job after start:"
 msgstr "Przejdź do uruchomionego zadania po starcie:"
 
+msgid "Jobs status summary"
+msgstr "Podsumowanie statusu zadaÅ„"
+
+msgid "Quick job access"
+msgstr "Quick job access"
+
+msgid "Finished jobs"
+msgstr "ZakoÅ„czone zadania"
+
+msgid "Most often used:"
+msgstr "Najczęściej używany:"
+
+msgid "Number of clients:"
+msgstr "Ilość klientów:"
+
+msgid "jobs"
+msgstr "zadaÅ„"
+
+msgid "Jobs count on most used:"
+msgstr "Ilość zadaÅ„ na najczęściej używanym:"
+
+msgid "level"
+msgstr "poziom"
+
+msgid "status"
+msgstr "status"
+
+msgid "Jump to job (last 100):"
+msgstr "Skocz do zadania (ostatnie 100):"
+
+msgid "View"
+msgstr "Zobacz"
+
+msgid "Total bytes:"
+msgstr "CaÅ‚kowita ilość danych:"
+
+msgid "Total files:"
+msgstr "CaÅ‚kowita ilość plików:"
+
+msgid "Database size:"
+msgstr "Rozmiar bazy danych:"
+
+msgid "Number of pools:"
+msgstr "Ilość puli wolumenów:"
+
+msgid "times"
+msgstr "razy"
+
+msgid "Execution count most used:"
+msgstr "Ilość wykonaÅ„ najczęściej używanego:"
+
+msgid "Job Totals"
+msgstr "Podsumowanie zadaÅ„"
+
+msgid "Restore Wizard"
+msgstr "Kreator przywracania danych"
+
+msgid "Perform Restore"
+msgstr "Wykonaj przywracanie danych"
+
+msgid "Configuration Wizard"
+msgstr "Kreator Konfiguracji"
+
+msgid "Baculum Settings"
+msgstr "Ustawienia Baculum"
+
+msgid "start time"
+msgstr "czas rozpoczÄ™cia"
+
index 74fbdaca87ee5684003fbe24491d834e74d7590f..ed3bc171cef4df6b3f2059cfaa3db379159486b6 100644 (file)
@@ -7,12 +7,13 @@
                        <com:TClientScript ScriptUrl=<%~ ../JavaScript/opentip.js %> />
                        <com:TClientScript ScriptUrl=<%~ ../JavaScript/excanvas.js %> />
                        <com:TClientScript ScriptUrl=<%~ ../JavaScript/tooltip.js %> />
+                       <com:TClientScript ScriptUrl=<%~ ../JavaScript/misc.js %> />
                        <com:TClientScript ScriptUrl=<%~ ../JavaScript/slide-window.js %> />
                        <com:TClientScript ScriptUrl=<%~ ../JavaScript/configuration-window.js %> />
                        <com:TClientScript ScriptUrl=<%~ ../JavaScript/panel-window.js %> />
                        <com:TClientScript ScriptUrl=<%~ ../JavaScript/flotr2.js %> />
                        <com:TClientScript ScriptUrl=<%~ ../JavaScript/graph.js %> />
-                       <com:TClientScript ScriptUrl=<%~ ../JavaScript/misc.js %> />
+                       <com:TClientScript ScriptUrl=<%~ ../JavaScript/statistics.js %> />
                        <com:TContentPlaceHolder ID="Main" />
                </com:TForm>
        </body>
diff --git a/gui/baculum/protected/Pages/API/DbSize.php b/gui/baculum/protected/Pages/API/DbSize.php
new file mode 100644 (file)
index 0000000..6d0c2e4
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2015 Marcin Haba
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+class DbSize extends BaculumAPI {
+       public function get() {
+               $dbsize = $this->getModule('db')->getDatabaseSize();
+               $this->output = $dbsize;
+               $this->error = JobError::ERROR_NO_ERRORS;
+       }
+}
+?>
diff --git a/gui/baculum/protected/Pages/API/JobTotals.php b/gui/baculum/protected/Pages/API/JobTotals.php
new file mode 100644 (file)
index 0000000..18e04ca
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum   - Bacula web interface
+ *
+ * Copyright (C) 2013-2015 Marcin Haba
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+class JobTotals extends BaculumAPI {
+       public function get() {
+               $jobtotals = $this->getModule('job')->getJobTotals();
+               $this->output = $jobtotals;
+               $this->error = JobError::ERROR_NO_ERRORS;
+       }
+}
+?>
index cadfe132f70b0f43c5c35ac45958bfec56ca74d8..068f060e006f55ae56964ab70c5a4bea0d056aae 100644 (file)
@@ -1,17 +1,82 @@
 <%@ MasterClass="Application.Layouts.Main" Theme="Baculum-v1"%>
 <com:TContent ID="Main">
        <div id="top">
-               <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/logo.png" alt="Baculum" />
+               <img id="logo" src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/logo.png" alt="Baculum" />
                <div id="directors"><com:TLabel ForControl="Director" Text="<%[ Director: ]%>" />
                        <com:TActiveDropDownList ID="Director" OnTextChanged="director" />
                </div>
                <div id="panel_switcher">
-                       <com:TActiveLinkButton ID="Workspace" Text="<%[ Workspace ]%>" Attributes.onclick="PanelWindow.show('container');" />
-                       <com:TActiveLinkButton ID="Graphs" Text="<%[ Graphs ]%>" Attributes.onclick="PanelWindow.show('graphs');" />
+                       <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/dashboard.png" alt="" onclick="$('<%=$this->Dashboard->ClientID%>').click()" />
+                       <com:TActiveLinkButton ID="Dashboard" Text="<%[ Dashboard ]%>" Attributes.onclick="PanelWindow.show('dashboard'); return false;" />
+                       <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/workspace.png" alt="" onclick="$('<%=$this->Workspace->ClientID%>').click()"/>
+                       <com:TActiveLinkButton ID="Workspace" Text="<%[ Workspace ]%>" Attributes.onclick="PanelWindow.show('container'); return false;" />
+                       <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/graphs.png" alt="" onclick="$('<%=$this->Graphs->ClientID%>').click()" />
+                       <com:TActiveLinkButton ID="Graphs" Text="<%[ Graphs ]%>" Attributes.onclick="PanelWindow.show('graphs'); return false;" />
                </div>
                <com:Application.Portlets.TrayBar ID="TrayBar" />
        </div>
-       <div id="container">
+       <div id="dashboard">
+               <div>
+                       <div class="dashboard_graph">
+                               <div id="jobs_summary_graph">
+                                       <script type="text/javascript">
+                                               var oJobsStates = <%=$this->jobs_states%>;
+                                       </script>
+                               </div>
+                               <span><%[ Jobs status summary ]%></span>
+                       </div>
+                       <div id="dashboard_icons">
+                               <fieldset class="dashboard_field">
+                                       <legend><%[ Clients ]%></legend>
+                                       <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/client.png" alt="" />
+                                       <p><span><%[ Number of clients: ]%></span><span id="clients_no"></span></p>
+                                       <p><span><%[ Most often used: ]%></span><span id="clients_most"></span></p>
+                                       <p><span><%[ Jobs count on most used: ]%></span><span id="clients_jobs"></span> <%[ jobs ]%></p>
+                               </fieldset>
+                               <fieldset class="dashboard_field">
+                                       <legend><%[ Quick job access ]%></legend>
+                                       <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/quick-access.png" alt="" />
+                                       <p><span><%[ Jump to job (last 100): ]%></span></p><select id="jobs_to_view"></select>
+                                       <p class="right"><a href="javascript:void(0)" onclick="SlideWindow.getObj('JobWindow').quickJumpToElement($('jobs_to_view').value, '<%=$this->JobBtn->ClientID%>', PanelWindow);"><%[ View ]%></a></p>
+                               </fieldset>
+                               <fieldset class="dashboard_field">
+                                       <legend><%[ Finished jobs ]%></legend>
+                                       <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/finished-jobs.png" alt="" />
+                                       <p><span><%[ Most often used: ]%></span><span id="jobs_most"></span></p>
+                                       <p><span><%[ Execution count most used: ]%></span><span id="jobs_most_count"></span> <%[ times ]%></p>
+                               </fieldset>
+                               <fieldset class="dashboard_field">
+                                       <legend><%[ Database ]%></legend>
+                                       <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/database.png" alt="" />
+                                       <p><span><%[ Database type: ]%></span><span><%=$this->dbtype%></span></p>
+                                       <p><span><%[ Database size: ]%></span><span id="database_size"></span></p>
+                               </fieldset>
+                               <fieldset class="dashboard_field">
+                                       <legend><%[ Pools ]%></legend>
+                                       <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/tapes.png" alt="" />
+                                       <p><span><%[ Number of pools: ]%></span><span id="pools_no"></span></p>
+                                       <p><span><%[ Most often used: ]%></span><span id="pools_most"></span></p>
+                                       <p><span><%[ Jobs count on most used: ]%></span><span id="pools_jobs"></span> <%[ jobs ]%></p>
+                               </fieldset>
+                               <fieldset class="dashboard_field">
+                                       <legend><%[ Job Totals ]%></legend>
+                                       <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/jobtotals.png" alt="" />
+                                       <p><span><%[ Total bytes: ]%></span><span id="jobs_total_bytes"></span></p>
+                                       <p><span><%[ Total files: ]%></span><span id="jobs_total_files"></span></p>
+                               </fieldset>
+                               <fieldset class="dashboard_field">
+                                       <legend><%[ Restore Wizard ]%></legend>
+                                       <a class="big" href="<%=$this->Service->constructUrl('RestoreWizard')%>" style="line-height: 73px; margin: 0 45px"><img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/restore.png" alt="" /><%[ Perform Restore ]%></a>
+                               </fieldset>
+                               <fieldset class="dashboard_field">
+                                       <legend><%[ Configuration Wizard ]%></legend>
+                                       <a class="big" href="<%=$this->Service->constructUrl('ConfigurationWizard')%>" style="line-height: 73px; margin: 0 45px"><img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/setting.png" alt="" /><%[ Baculum Settings ]%></a>
+                               </fieldset>
+                       </div>
+                               <div style="clear: both"></div>
+               </div>
+       </div>
+       <div id="container" style="display: none;">
                <div id="menu-left">
                        <com:TActiveButton ID="StorageBtn" CssClass="storage-btn">
                                <prop:Attributes.onmouseover>
                        var graph;
                        document.observe("dom:loaded", function() {
                                PanelWindow.onShow = function() {
-                                       if(typeof(graph) == "undefined") {
+                                       if (PanelWindow.currentWindowId === 'graphs' && typeof(graph) == "undefined") {
                                                graph = new GraphClass(jobs, graph_lang, 'graphs_content', 'legend', 'time_range', '<%=$this->DateFrom->ClientID%>', '<%=$this->DateTo->ClientID%>', '<%=$this->Clients->ClientID%>', '<%=$this->Jobs->ClientID%>');
+                                       } else if (PanelWindow.currentWindowId === 'dashboard') {
+                                               Dashboard.update_all(Statistics, TEXT);
                                        }
                                };
                        });
                                'SlideWindow.getObj("' . $this->initWindowId . 'Window").setInitElementId("' . $this->initElementId . '");'
                        : '')%>
                        <%=(!is_null($this->openWindow) ?
-                               '$("' . $this->openWindow . '").click(); window.history.pushState("", "", "/");'
+                               'PanelWindow.show("container");
+                               $("' . $this->openWindow . '").click();
+                               window.history.pushState("", "", "/");'
                        : '')%>
                });
+               var TEXT = {
+                       level: '<%[ level ]%>',
+                       status: '<%[ status ]%>',
+                       starttime: '<%[ start time ]%>'
+               }
        </script>
 </com:TContent>
index 666ceee4175377cc7233ae36e90693b49a1f349f..c78c00b0eb736299e18e7548a71fa4f1e5c80a46 100644 (file)
@@ -36,6 +36,10 @@ class Home extends BaculumPage
 
        public $initElementId = null;
 
+       public $jobs_states = null;
+
+       public $dbtype = null;
+
        public $windowIds = array('Storage', 'Client', 'Volume', 'Pool', 'Job', 'JobRun');
 
        public function onInit($param) {
@@ -63,12 +67,14 @@ class Home extends BaculumPage
 
                if(!$this->IsPostBack && !$this->IsCallBack) {
                        $directors = $this->getModule('api')->get(array('directors'))->output;
-                       if(!array_key_exists('director', $_SESSION)) {
+                       if(!array_key_exists('director', $_SESSION) || $directors[0] != $_SESSION['director']) {
                                $_SESSION['director'] = $directors[0];
                        }
                        $this->Director->dataSource = array_combine($directors, $directors);
                        $this->Director->SelectedValue = $_SESSION['director'];
                        $this->Director->dataBind();
+                       $this->dbtype = $this->getModule('configuration')->getDbNameByType($appConfig['db']['type']);
+                       $this->setJobsStates();
                        $this->setJobs();
                        $this->setClients();
                        $this->setWindowOpen();
@@ -104,6 +110,29 @@ class Home extends BaculumPage
                return json_encode($this->jobs);
        }
 
+       public function setJobsStates() {
+               $jobs_summary = array(
+                       'ok' => array(),
+                       'error' => array(),
+                       'warning' => array(),
+                       'cancel' => array(),
+                       'running' => array()
+               );
+               $job_types = $jobs_summary;
+               $job_states = array();
+
+               $misc = $this->getModule('misc');
+               foreach($job_types as $type => $arr) {
+                       $states = $misc->getJobStatesByType($type);
+                       foreach($states as $state => $desc) {
+                               $desc['type'] = $type;
+                               $jobs_states[$state] = $desc;
+                       }
+               }
+
+               $this->jobs_states = json_encode($jobs_states);
+       }
+
        public function setJobs() {
                $this->jobs = $this->getModule('api')->get(array('jobs'));
                $jobs = array('@' => Prado::localize('select job'));
@@ -122,7 +151,6 @@ class Home extends BaculumPage
                }
                $this->Clients->dataSource = $clients;
                $this->Clients->dataBind();
-
        }
 
        public function setWindowOpen() {
index cb72e951c3b56a1e3ec194dd802b68079cd6b48a..e96009f1157c8ee4da7a85d3c8a570c6d7ca5bef 100644 (file)
 class Monitor extends BaculumPage {
        public function onInit($param) {
                parent::onInit($param);
-               $_SESSION['monitor_data'] = array('jobs' => array(), 'running_jobs' => array(), 'terminated_jobs' => array());
+               $_SESSION['monitor_data'] = array(
+                       'jobs' => array(),
+                       'running_jobs' => array(),
+                       'terminated_jobs' => array(),
+                       'pools' => array(),
+                       'jobtotals' => array(),
+                       'dbsize' => 0
+               );
+
                $_SESSION['monitor_data']['jobs'] = $this->Application->getModule('api')->get(array('jobs'))->output;
+               $_SESSION['monitor_data']['clients'] = $this->getModule('api')->get(array('clients'))->output;
+               $_SESSION['monitor_data']['pools'] = $this->getModule('api')->get(array('pools'))->output;
+               $_SESSION['monitor_data']['jobtotals'] = $this->getModule('api')->get(array('jobs', 'totals'))->output;
+               $_SESSION['monitor_data']['dbsize'] = $this->getModule('api')->get(array('dbsize'))->output;
 
                $runningJobStates = $this->Application->getModule('misc')->getRunningJobStates();
 
index 87e9174d3458a9ba6c5535cfedc2d4e26ef5a92d..94292f0780ccec0684824ea2bfac162849962386 100644 (file)
@@ -9,6 +9,7 @@
        var timeout_handler;
        var last_callback_time = 0;
        var callback_time_offset = 0;
+       var oData;
        document.observe("dom:loaded", function() {
                oMonitor = function() {
                        return new Ajax.Request('<%=$this->Service->constructUrl("Monitor")%>', {
                                        if (timeout_handler) {
                                                clearTimeout(timeout_handler);
                                        }
-                                       var jobs = (response.responseText).evalJSON();
-                                       if (jobs.running_jobs.length > 0) {
+                                       oData = (response.responseText).evalJSON();
+                                       Statistics.grab_statistics(oData, oJobsStates);
+
+                                       if (PanelWindow.currentWindowId === 'dashboard') {
+                                               Dashboard.update_all(Statistics, TEXT);
+                                       }
+
+                                       if (oData.running_jobs.length > 0) {
                                                refreshInterval =  callback_time_offset + default_fast_refresh_interval;
                                        } else {
                                                refreshInterval = default_refresh_interval;
                                        }
                                        job_callback_func();
                                        status_callback_func();
-                                       $('running_jobs').update(jobs.running_jobs.length);
-                                       $('finished_jobs').update(jobs.terminated_jobs.length);
+                                       $('running_jobs').update(oData.running_jobs.length);
+                                       $('finished_jobs').update(oData.terminated_jobs.length);
                                        timeout_handler = setTimeout("oMonitor()", refreshInterval);
                                }
                        });
index d682bf82f7002dd28011443853782f5714acdf94..1f869f629b71e819d9d736b13b168ce1982ae05c 100644 (file)
@@ -68,6 +68,7 @@
                        <url ServiceParameter="API.JobsShow" pattern="jobs/show/" />
                        <url ServiceParameter="API.JobShow" pattern="jobs/show/{id}/" parameters.id="\d+" />
                        <url ServiceParameter="API.JobShow" pattern="jobs/show/name/{name}/" parameters.name=".+" />
+                       <url ServiceParameter="API.JobTotals" pattern="jobs/totals/" />
                        <url ServiceParameter="API.RestoreRun" pattern="jobs/restore/" />
                        <!-- END Jobs -->
                        <!-- START BVFS -->
@@ -89,6 +90,7 @@
                        <url ServiceParameter="API.FileSets" pattern="filesets/" />
                        <url ServiceParameter="API.FileSet" pattern="filesets/{id}/" parameters.id="\d+" />
                        <url ServiceParameter="API.ConsoleCommand" pattern="console/" />
+                       <url ServiceParameter="API.DbSize" pattern="dbsize/" />
                </module>
                <module id="globalization" class="TGlobalization">
                        <translation type="gettext" source="Application.Lang" marker="@@" autosave="false" cache="false" DefaultCulture="en" />
diff --git a/gui/baculum/themes/Baculum-v1/client.png b/gui/baculum/themes/Baculum-v1/client.png
new file mode 100644 (file)
index 0000000..573308c
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/client.png differ
diff --git a/gui/baculum/themes/Baculum-v1/clients.png b/gui/baculum/themes/Baculum-v1/clients.png
new file mode 100644 (file)
index 0000000..a5462b7
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/clients.png differ
diff --git a/gui/baculum/themes/Baculum-v1/dashboard.png b/gui/baculum/themes/Baculum-v1/dashboard.png
new file mode 100644 (file)
index 0000000..81e7f05
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/dashboard.png differ
diff --git a/gui/baculum/themes/Baculum-v1/database.png b/gui/baculum/themes/Baculum-v1/database.png
new file mode 100644 (file)
index 0000000..17e0826
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/database.png differ
diff --git a/gui/baculum/themes/Baculum-v1/finished-jobs.png b/gui/baculum/themes/Baculum-v1/finished-jobs.png
new file mode 100644 (file)
index 0000000..75a67ca
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/finished-jobs.png differ
diff --git a/gui/baculum/themes/Baculum-v1/graphs.png b/gui/baculum/themes/Baculum-v1/graphs.png
new file mode 100644 (file)
index 0000000..24fae86
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/graphs.png differ
diff --git a/gui/baculum/themes/Baculum-v1/jobrun.png b/gui/baculum/themes/Baculum-v1/jobrun.png
new file mode 100644 (file)
index 0000000..c8a5c36
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/jobrun.png differ
diff --git a/gui/baculum/themes/Baculum-v1/jobs.png b/gui/baculum/themes/Baculum-v1/jobs.png
new file mode 100644 (file)
index 0000000..024d0ca
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/jobs.png differ
diff --git a/gui/baculum/themes/Baculum-v1/jobtotals.png b/gui/baculum/themes/Baculum-v1/jobtotals.png
new file mode 100644 (file)
index 0000000..8dea50e
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/jobtotals.png differ
diff --git a/gui/baculum/themes/Baculum-v1/pools.png b/gui/baculum/themes/Baculum-v1/pools.png
new file mode 100644 (file)
index 0000000..fbaeb88
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/pools.png differ
diff --git a/gui/baculum/themes/Baculum-v1/quick-access.png b/gui/baculum/themes/Baculum-v1/quick-access.png
new file mode 100644 (file)
index 0000000..82d5426
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/quick-access.png differ
diff --git a/gui/baculum/themes/Baculum-v1/restore.png b/gui/baculum/themes/Baculum-v1/restore.png
new file mode 100644 (file)
index 0000000..a71dd6d
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/restore.png differ
diff --git a/gui/baculum/themes/Baculum-v1/setting.png b/gui/baculum/themes/Baculum-v1/setting.png
new file mode 100644 (file)
index 0000000..6ea0b94
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/setting.png differ
diff --git a/gui/baculum/themes/Baculum-v1/storages.png b/gui/baculum/themes/Baculum-v1/storages.png
new file mode 100644 (file)
index 0000000..3539d2e
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/storages.png differ
index 70dd6318b0f88191f6035f3cfb2b47f4c521a209..ebab362029824929480844c171bbff49ab338769 100644 (file)
@@ -56,19 +56,25 @@ textarea.textbox-auto {
        font-size: 11px;
 }
 
-a {
+a, a.big {
        font-size: 12px;
        color: white;
        padding: 3px 4px;
        text-decoration: none;
 }
 
-a:hover {
+a:hover, a.big {
        text-decoration: underline;
 }
+
+a.big {
+       font-size: 17px;
+       font-weight: bold;
+}
+
 #container {
        margin: 0 auto;
-       min-width: 975px;
+       min-width: 987px;
        max-width: 100%;
        border-right: 1px solid black;
        background-color: #585758;
@@ -76,7 +82,7 @@ a:hover {
 
 #top {
        margin: 0 auto;
-       min-width: 975px;
+       min-width: 987px;
        max-width: 100%;
        height: 51px;
        background-image: url('bls_top.png');
@@ -89,10 +95,14 @@ a:hover {
 }
 
 #top img {
-       padding: 11px 0 11px 35px;
+       padding: 11px 0 11px 5px;
        float: left;
 }
 
+#logo {
+       margin-left: 20px;
+}
+
 #menu-left {
        position: absolute;
        width: 74px;
@@ -125,18 +135,25 @@ a:hover {
 }
 
 #panel_switcher {
-       margin: 17px 20px 0 0;
+       margin: 2px 6px 0 0;
        font-weight: bold;
 }
 
+#panel_switcher img {
+       margin: 0;
+       padding-left: 12px;
+       float: none;
+       cursor: pointer;
+}
+
 #tray_bar {
-       margin: 2px 30px 2px 0;
+       margin: 2px 0;
 }
 
 #tray_bar img {
        float: none;
        margin: 0;
-       padding: auto 12px;
+       padding: auto 7px;
 }
 
 #tray_bar span{
@@ -243,7 +260,7 @@ div.actions_btn {
 
 #bottom {
        max-width: 100%;
-       min-width: 975px;
+       min-width: 987px;
        height: 11px;
        margin: 0 auto;
        background: transparent url('bls_bottom.png') repeat-x top left;
@@ -899,8 +916,7 @@ span.tab_active {
        background-color: rgb(163, 180, 197);
 }
 
-#graphs {
-       height: 600px;
+#graphs, #dashboard {
        min-width: 954px;
        max-width: 100%;
        padding: 10px;
@@ -930,6 +946,63 @@ span.tab_active {
        height: 425px;
 }
 
+.dashboard_graph {
+       height: 450px;
+       text-align: center;
+       font-size: 12px;
+       color: white;
+       font-weight: bold;
+       float: right;
+}
+
+#dashboard_icons  {
+       min-width: 500px;
+       min-height: 600px;
+}
+
+.dashboard_field {
+       max-width: 450px;
+       min-width: 320px;
+       height: 86px;
+       margin: 4px;
+       border-radius: 5px;
+       float: left;
+}
+
+.dashboard_field img {
+       float: left;
+}
+
+.dashboard_field p {
+       font-size: 10px;
+       margin-top: 5px;
+}
+
+.dashboard_field p span:nth-of-type(1) {
+       display: inline-block;
+       width: 122px;
+}
+
+.dashboard_field p span:nth-of-type(2) {
+       font-size: 12px;
+       padding-left: 5px;
+       font-weight: bold;
+}
+
+.dashboard_field a {
+       text-decoration: underline;
+}
+
+#jobs_summary_graph {
+       width: 380px;
+       height: 380px;
+}
+
+#jobs_to_view {
+    width: 240px;
+    margin: 0 2px 0 5px;
+}
+
 /* Overwrite date picker classes */
 .TDatePicker_default {
        z-index: 20;
diff --git a/gui/baculum/themes/Baculum-v1/tapes.png b/gui/baculum/themes/Baculum-v1/tapes.png
new file mode 100644 (file)
index 0000000..f4bea4b
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/tapes.png differ
diff --git a/gui/baculum/themes/Baculum-v1/volumes.png b/gui/baculum/themes/Baculum-v1/volumes.png
new file mode 100644 (file)
index 0000000..9721d1c
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/volumes.png differ
diff --git a/gui/baculum/themes/Baculum-v1/workspace.png b/gui/baculum/themes/Baculum-v1/workspace.png
new file mode 100644 (file)
index 0000000..7307500
Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/workspace.png differ