Merge branch 'feature/dashboard-view-4192'

resolves #4192
This commit is contained in:
Marius Hein 2013-08-09 10:37:31 +02:00
commit 19b98c84a2
52 changed files with 2104 additions and 935 deletions

View File

@ -84,6 +84,8 @@ class AuthenticationController extends ActionController
}
}
/**
* Action handle logout
*/

View File

@ -28,7 +28,7 @@
use Icinga\Application\Benchmark;
use Icinga\Authentication\Manager;
use Icinga\Web\ActionController;
use Icinga\Web\Hook\Configuration\ConfigurationTab;
use Icinga\Web\Widget\Tabs;
use Icinga\Web\Hook\Configuration\ConfigurationTabBuilder;
/**
@ -48,11 +48,10 @@ class ConfigurationController extends ActionController
public function indexAction()
{
$tabBuilder = new ConfigurationTabBuilder(
$this->widget('tabs')
new Tabs()
);
$tabBuilder->build();
$this->view->tabs = $tabBuilder->getTabs();
}
}

View File

@ -0,0 +1,141 @@
<?php
// @codingStandardsIgnoreStart
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
use Icinga\Web\ActionController;
use Icinga\Web\Url;
use Icinga\Application\Icinga;
use Icinga\Web\Widget\Dashboard;
use Icinga\Config\Config as IcingaConfig;
use Icinga\Form\Dashboard\AddUrlForm;
use Icinga\Exception\ConfigurationError;
/**
* Handle creation, removal and displaying of dashboards, panes and components
*
* @see Icinga\Web\Widget\Dashboard for more information about dashboards
*/
class DashboardController extends ActionController
{
/**
* Retrieve a dashboard from the provided config
*
* @param string $config The config to read the dashboard from, or 'dashboard/dashboard' if none is given
*
* @return Dashboard
*/
private function getDashboard($config = 'dashboard/dashboard')
{
$dashboard = new Dashboard();
$dashboard->readConfig(IcingaConfig::app($config));
return $dashboard;
}
/**
* Remove a component from the pane identified by the 'pane' parameter
*
*/
public function removecomponentAction()
{
$pane = $this->_getParam('pane');
$dashboard = $this->getDashboard();
try {
$dashboard->removeComponent(
$pane,
$this->_getParam('component')
)->store();
$this->redirectNow(Url::fromPath('dashboard', array('pane' => $pane)));
} catch (ConfigurationError $exc ) {
$this->_helper->viewRenderer('show_configuration');
$this->view->exceptionMessage = $exc->getMessage();
$this->view->iniConfigurationString = $dashboard->toIni();
}
}
/**
* Display the form for adding new components or add the new component if submitted
*
*/
public function addurlAction()
{
$form = new AddUrlForm();
$form->setRequest($this->_request);
$this->view->form = $form;
if ($form->isSubmittedAndValid()) {
$dashboard = $this->getDashboard();
$dashboard->setComponentUrl(
$form->getValue('pane'),
$form->getValue('component'),
ltrim($form->getValue('url'), '/')
);
try {
$dashboard->store();
$this->redirectNow(
Url::fromPath('dashboard', array('pane' => $form->getValue('pane')))
);
} catch (ConfigurationError $exc) {
$this->_helper->viewRenderer('show_configuration');
$this->view->exceptionMessage = $exc->getMessage();
$this->view->iniConfigurationString = $dashboard->toIni();
}
}
}
/**
* Display the dashboard with the pane set in the 'pane' request parameter
*
* If no pane is submitted or the submitted one doesn't exist, the default pane is
* displayed (normally the first one)
*
*/
public function indexAction()
{
$dashboard = $this->getDashboard();
if ($this->_getParam('pane')) {
$pane = $this->_getParam('pane');
$dashboard->activate($pane);
}
$this->view->tabs = $dashboard->getTabs();
$this->view->tabs->add(
'Add',
array(
'title' => 'Add Url',
'iconCls' => 'plus',
'url' => Url::fromPath('dashboard/addurl')
)
);
$this->view->dashboard = $dashboard;
}
}
// @codingStandardsIgnoreEnd

View File

@ -33,8 +33,7 @@
use Icinga\Web\ActionController;
use Icinga\Application\Icinga;
use Icinga\Web\Hook\Configuration\ConfigurationTabBuilder;
use Icinga\Application\Modules\Manager as ModuleManager;
use Zend_Controller_Action as ZfActionController;
use Icinga\Web\Widget\Tabs;
/**
* Handle module depending frontend actions
@ -60,9 +59,7 @@ class ModulesController extends ActionController
*/
public function indexAction()
{
$tabBuilder = new ConfigurationTabBuilder(
$this->widget('tabs')
);
$tabBuilder = new ConfigurationTabBuilder(new Tabs());
$tabBuilder->build();

View File

@ -72,8 +72,9 @@ class StaticController extends ActionController
public function imgAction()
{
$module = $this->_getParam('moduleName');
$module = $this->_getParam('module_name');
$file = $this->_getParam('file');
$basedir = Icinga::app()->getModuleManager()->getModule($module)->getBaseDir();
$filePath = $basedir . '/public/img/' . $file;
@ -102,7 +103,7 @@ class StaticController extends ActionController
) . ' GMT');
readfile($filePath);
$this->_viewRenderer->setNoRender();
return;
}
public function javascriptAction()
@ -110,17 +111,16 @@ class StaticController extends ActionController
$module = $this->_getParam('module_name');
$file = $this->_getParam('file');
if (!Icinga::app()->getModuleManager()->hasEnabled($module)) {
echo "/** Module not enabled **/";
return;
}
$basedir = Icinga::app()->getModuleManager()->getModule($module)->getBaseDir();
$filePath = $basedir . '/public/js/' . $file;
if (!file_exists($filePath)) {
throw new ActionException(
sprintf(
'%s does not exist',
$filePath
),
404
);
echo "/** Module has no js files **/";
return;
}
$hash = md5_file($filePath);
$response = $this->getResponse();
@ -143,7 +143,8 @@ class StaticController extends ActionController
} else {
readfile($filePath);
}
$this->_viewRenderer->setNoRender();
return;
}
}

View File

@ -0,0 +1,136 @@
<?php
namespace Icinga\Form\Dashboard;
use Icinga\Config\Config as IcingaConfig;
use Icinga\Web\Form;
use Icinga\Web\Widget\Dashboard;
use Zend_Form_Element_Text;
use Zend_Form_Element_Submit;
use Zend_Form_Element_Hidden;
use Zend_Form_Element_Select;
/**
* Form to add an url a dashboard pane
*
*/
class AddUrlForm extends Form
{
/**
* Add a selection box for different panes to the form
*
* @param Dashboard $dashboard The dashboard to retrieve the panes from
*/
private function addPaneSelectionBox(Dashboard $dashboard)
{
$selectPane = new Zend_Form_Element_Select(
'pane',
array(
'label' => 'Dashboard',
'required' => true,
'style' => 'display:inline-block',
'multiOptions' => $dashboard->getPaneKeyTitleArray()
)
);
$newDashboardBtn = new Zend_Form_Element_Submit(
'create_new_pane',
array(
'label' => '+',
'required' => false,
'style' => 'display:inline-block'
)
);
$newDashboardBtn->removeDecorator('DtDdWrapper');
$selectPane->removeDecorator('DtDdWrapper');
$selectPane->removeDecorator('htmlTag');
$this->addElement($selectPane);
$this->addElement($newDashboardBtn);
$this->enableAutoSubmit(array('create_new_pane'));
}
/**
* Add a textfield for creating a new pane to this form
*
*/
private function addNewPaneTextField()
{
$txtCreatePane = new Zend_Form_Element_Text(
'pane',
array(
'label' => 'New dashboard title',
'required' => true,
'style' => 'display:inline-block'
)
);
// Marks this field as a new pane (and prevents the checkbox being displayed when validation errors occur)
$markAsNewPane = new Zend_Form_Element_Hidden(
'create_new_pane',
array(
'required' => true,
'value' => 1
)
);
$cancelDashboardBtn = new Zend_Form_Element_Submit(
'use_existing_dashboard',
array(
'label' => 'X',
'required' => false,
'style' => 'display:inline-block'
)
);
$cancelDashboardBtn->removeDecorator('DtDdWrapper');
$txtCreatePane->removeDecorator('DtDdWrapper');
$txtCreatePane->removeDecorator('htmlTag');
$this->addElement($txtCreatePane);
$this->addElement($cancelDashboardBtn);
$this->addElement($markAsNewPane);
}
/**
* Add elements to this form (used by extending classes)
*
*/
protected function create()
{
$dashboard = new Dashboard();
$dashboard->readConfig(IcingaConfig::app('dashboard/dashboard'));
$this->addElement(
'text',
'url',
array(
'label' => 'Url',
'required' => true,
)
);
$elems = $dashboard->getPaneKeyTitleArray();
if (empty($elems) || // show textfield instead of combobox when no pane is available
($this->getRequest()->getPost('create_new_pane', '0') && // or when a new pane should be created (+ button)
!$this->getRequest()->getPost('use_existing_dashboard', '0')) // and the user didn't click the 'use existing' button
) {
$this->addNewPaneTextField();
} else {
$this->addPaneSelectionBox($dashboard);
}
$this->addElement(
'text',
'component',
array(
'label' => 'Title',
'required' => true,
)
);
$this->setSubmitLabel("Add to dashboard");
}
}

View File

@ -1,4 +1,5 @@
<?= $this->tabs; ?>
<?= $this->tabs->render($this); ?>
<h3>Configuration</h3>
<p>

View File

@ -0,0 +1,3 @@
<h3>Add dashboard URL</h3>
<?= $this->form->render($this); ?>

View File

@ -0,0 +1,3 @@
<?= $this->tabs->render($this); ?>
<?= $this->dashboard->render($this); ?>

View File

@ -0,0 +1,29 @@
<br/>
<div class="alert alert-error">
<h4><i class="icon-warning-sign"> </i>Saving dashboard failed</h4>
<br/>
<p>
Your dashboard couldn't be stored (error: "<?= $this->exceptionMessage; ?>"). This could have one or more
of the following reasons:
</p>
<ul>
<li>You don't have permissions to write to the dashboard file</li>
<li>Something went wrong while writing the file</li>
<li>There's an application error preventing you from persisting the configuration</li>
</ul>
</div>
<p>
Details can be seen in your application log (if you don't have access to this file, call your administrator in this case).
<br/>
In case you can access the configuration file (config/dashboard/dashboard.ini) by yourself, you can open it and
insert the config manually:
</p>
<p>
<pre>
<code>
<?= $this->escape($this->iniConfigurationString); ?>
</code>
</pre>
</p>

View File

@ -3,7 +3,7 @@ $this->modules->limit(10);
$modules = $this->modules->paginate();
?>
<?= $this->tabs; ?>
<?= $this->tabs->render($this); ?>
<h3>Installed Modules</h3>
<?= $this->paginationControl($modules, null, null, array(
'preserve' => $this->preserve

View File

View File

@ -1,3 +1,4 @@
[menu]
Dashboard = "/dashboard/index"
Configuration = "/configuration/index"

43
doc/dashboard.md Normal file
View File

@ -0,0 +1,43 @@
# The dashboard
The icingaweb dashboard allows you to display different views on one page. You can create customized overviews over
the objects you're interested in and can add and remove elements.
## Dashboard, Panes and Components
![Dashboard structure][dashboards1]
* The building blocks of dashboards are components - those represent a single URL and display it's content (often in
a more condensed layout)
* Different components can be added to a pane and will be shown there. All panes are shown as tabs on top of the dashboard,
whereas the title is used for the text in the tab
* The dashboard itself is just the view containing the panes
## Configuration files
By default, the config/dashboard/dashboard.ini is used for storing dashboards in the following format:
[PaneName] ; Define a new Pane
title = "PaneTitle" ; The title of the pane as displayed in the tabls
[PaneName.Component1] ; Define a new component 'Component 1' underneat the pane
url = "/url/for/component1" ; the url that will be displayed, with view=compact as URL parameter appended
height = "500px" ; optional height setting
width = "400px" ; optional width setting
[test.My hosts] ; Another component, here with host
url = "monitoring/list/hosts" ; the url of the component
; Notice the missing height/width definition
[test.My services] ; And another pane
url = "monitoring/list/services" ; With service url
[test2] ; Define a second pane
title = "test2" ; with the title
[test2.test] ; Add a component to the second pane
url = "/monitoring/show/host/host1" ; ...and define it's url
[dashboards1]: res/Dashboard.png

View File

@ -41,6 +41,10 @@ some fields in the form) the form is repopulated but not validated at this time.
in this case, but no errors are added to the created form.
In order to be able to use isSubmittedAndValid, you have to define a submitbutton in the form.
This is done with the *setSubmitLabel(string)* function, with the first parameter being the
label set to the submit button.
#### Pre validation
To handle dependend fields you can just override *preValid()* or *postValid()*

273
doc/res/Dashboard.graphml Normal file
View File

@ -0,0 +1,273 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yFiles for Java 2.10-->
<key for="graphml" id="d0" yfiles.type="resources"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
<key for="port" id="d3" yfiles.type="portuserdata"/>
<key attr.name="url" attr.type="string" for="node" id="d4"/>
<key attr.name="description" attr.type="string" for="node" id="d5"/>
<key for="node" id="d6" yfiles.type="nodegraphics"/>
<key attr.name="Description" attr.type="string" for="graph" id="d7"/>
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
<graph edgedefault="directed" id="G">
<data key="d7"/>
<node id="n0">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="313.0" width="488.0" x="171.0" y="186.0"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="67.193359375" x="210.4033203125" y="147.43359375">Dashboard<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n1">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="288.0" width="488.0" x="171.0" y="211.0"/>
<y:Fill color="#808080" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="242.0" y="142.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n2">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="203.0" width="167.0" x="195.0" y="237.0"/>
<y:Fill color="#00CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="81.5" y="99.5">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n3">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="94.0" width="158.0" x="374.0" y="237.0"/>
<y:Fill color="#00CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="77.0" y="45.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n4">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="94.0" width="158.0" x="374.0" y="346.0"/>
<y:Fill color="#00CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="77.0" y="45.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n5">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="14.0" width="73.0" x="183.0" y="197.0"/>
<y:Fill color="#808080" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="34.5" y="5.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n6">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="14.0" width="73.0" x="271.0" y="197.0"/>
<y:Fill color="#808080" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="34.5" y="5.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n7">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="123.0" x="171.0" y="115.0"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="37.509765625" x="42.7451171875" y="5.93359375">Panes<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n8">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="187.0" x="472.0" y="115.0"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="67.193359375" x="59.9033203125" y="5.93359375">Dashboard<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n9">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="129.0" x="171.0" y="529.0"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="77.986328125" x="25.5068359375" y="5.93359375">Components<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
<edge id="e0" source="n8" target="n0">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e1" source="n5" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e2" source="n9" target="n2">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e3" source="n9" target="n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e4" source="n9" target="n4">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e5" source="n6" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d0">
<y:Resources/>
</data>
</graphml>

BIN
doc/res/Dashboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -45,7 +45,7 @@ class Config extends Zend_Config_Ini
* The INI file this configuration has been loaded from
* @var string
*/
protected $configFile;
private $configFile;
/**
* Application config instances per file
@ -135,4 +135,11 @@ class Config extends Zend_Config_Ini
return array_keys($this->$name->toArray());
}
}
public function getConfigFile()
{
return $this->configFile;
}
}

View File

@ -0,0 +1,148 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Util;
class Dimension
{
/**
* Defines this dimension as nr of pixels
*/
const UNIT_PX = "px";
/**
* Defines this dimension as width of 'M' in current font
*/
const UNIT_EM = "em";
/**
* Defines this dimension as a percentage value
*/
const UNIT_PERCENT = "%";
/**
* Defines this dimension in points
*/
const UNIT_PT = "pt";
/**
* The current set value for this dimension
*
* @var int
*/
private $value = 0;
/**
* The unit to interpret the value with
*
* @var string
*/
private $unit = self::UNIT_PX;
/**
* Create a new Dimension object with the given size and unit
*
* @param int $value The new value
* @param string $unit The unit to use (default: px)
*/
public function __construct($value, $unit = self::UNIT_PX)
{
$this->setValue($value, $unit);
}
/**
* Change the value and unit of this dimension
*
* @param int $value The new value
* @param string $unit The unit to use (default: px)
*/
public function setValue($value, $unit = self::UNIT_PX)
{
$this->value = intval($value);
$this->unit = $unit;
}
/**
* Return true when the value is > 0
*
* @return bool
*/
public function isDefined()
{
return $this->value > 0;
}
/**
* Return the underlying value without unit information
*
* @return int
*/
public function getValue()
{
return $this->value;
}
/**
* Return the unit used for the value
*
* @return string
*/
public function getUnit()
{
return $this->unit;
}
/**
* Return this value with it's according unit as a string
*
* @return string
*/
public function __toString()
{
if (!$this->isDefined()) {
return "";
}
return $this->value.$this->unit;
}
/**
* Create a new Dimension object from a string containing the numeric value and the dimension (e.g. 200px, 20%)
*
* @param $string The string to parse
*
* @return Dimension
*/
public static function fromString($string)
{
$matches = array();
if (!preg_match_all('/^ *([0-9]+)(px|pt|em|\%) */i', $string, $matches)) {
return new Dimension(0);
}
return new Dimension(intval($matches[1][0]), $matches[2][0]);
}
}

View File

@ -153,19 +153,6 @@ class ActionController extends ZfController
return t($string);
}
/**
* Helper function creating a new widget
*
* @param string $name The widget name
* @param string $properties Optional widget properties
*
* @return Widget\AbstractWidget
*/
public function widget($name, $properties = array())
{
return Widget::create($name, $properties);
}
/**
* Whether the current user has the given permission
*
@ -250,7 +237,7 @@ class ActionController extends ZfController
public function redirectNow($url, array $params = array())
{
if ($url instanceof Url) {
$url = $url->getRelative();
$url = $url->getRelativeUrl();
}
$this->_helper->Redirector->gotoUrlAndExit($url);
}

View File

@ -121,6 +121,16 @@ class Url
return $this;
}
/**
* Return the baseUrl set for this Url
*
* @return string
*/
public function getBaseUrl()
{
return $this->baseUrl;
}
/**
* Set the relative path of this url, without query parameters
*
@ -302,6 +312,8 @@ class Url
return $url;
}
/**
* Alias for @see Url::getAbsoluteUrl()
* @return mixed

View File

@ -1,73 +0,0 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Icinga 2 Web - Head for multiple monitoring frontends
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Web;
use Icinga\Exception\ProgrammingError;
/**
* Web widgets make things easier for you!
*
* This class provides nothing but a static factory method for widget creation.
* Usually it will not be used directly as there are widget()-helpers available
* in your action controllers and view scripts.
*
* Usage example:
* <code>
* $tabs = Widget::create('tabs');
* </code>
*
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
* @author Icinga-Web Team <info@icinga.org>
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
*/
class Widget
{
/**
* Create a new widget
*
* @param string $name Widget name
* @param array $options Widget constructor options
*
* @return Icinga\Web\Widget\AbstractWidget
*/
public static function create($name, $options = array(), $module_name = null)
{
$class = 'Icinga\\Web\\Widget\\' . ucfirst($name);
if (! class_exists($class)) {
throw new ProgrammingError(
sprintf(
'There is no such widget: %s',
$name
)
);
}
$widget = new $class($options, $module_name);
return $widget;
}
}

View File

@ -1,167 +0,0 @@
<?php
/**
* Web Widget abstract class
*/
namespace Icinga\Web\Widget;
use Icinga\Exception\ProgrammingError;
use Zend_Controller_Action_HelperBroker as ZfActionHelper;
/**
* Web widgets MUST extend this class
*
* AbstractWidget implements getters and setters for widget options stored in
* the protected options array. If you want to allow options for your own
* widget, you have to set a default value (may be null) for each single option
* in this array.
*
* Please have a look at the available widgets in this folder to get a better
* idea on what they should look like.
*
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
* @author Icinga-Web Team <info@icinga.org>
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
*/
abstract class AbstractWidget
{
/**
* If you are going to access the current view with the view() function,
* it's instance is stored here for performance reasons.
*
* @var Zend_View_Abstract
*/
protected static $view;
protected $module_name;
/**
* Fill $properties with default values for all your valid widget properties
*
* @var array
*/
protected $properties = array();
/**
* You MUST extend this function. This is where all your HTML voodoo happens
*
* @return string
*/
abstract public function renderAsHtml();
/**
* You are not allowed to override the constructor, but you can put
* initialization stuff in your init() function
*
* @return void
*/
protected function init()
{
}
/**
* We are not allowing you to override the constructor unless someone
* presents a very good reason for doing so
*
* @param array $properties An optional properties array
*/
final public function __construct($properties = array(), $module_name = null)
{
if ($module_name !== null) {
$this->module_name = $module_name;
}
foreach ($properties as $key => $val) {
$this->$key = $val;
}
$this->init();
}
/**
* Getter for widget properties
*
* @param string $key The option you're interested in
*
* @throws ProgrammingError for unknown property name
*
* @return mixed
*/
public function __get($key)
{
if (array_key_exists($key, $this->properties)) {
return $this->properties[$key];
}
throw new ProgrammingError(
sprintf(
'Trying to get invalid "%s" property for %s',
$key,
get_class($this)
)
);
}
/**
* Setter for widget properties
*
* @param string $key The option you want to set
* @param string $val The new value going to be assigned to this option
*
* @throws ProgrammingError for unknown property name
*
* @return mixed
*/
public function __set($key, $val)
{
if (array_key_exists($key, $this->properties)) {
$this->properties[$key] = $val;
return;
}
throw new ProgrammingError(
sprintf(
'Trying to set invalid "%s" property in %s. Allowed are: %s',
$key,
get_class($this),
empty($this->properties)
? 'none'
: implode(', ', array_keys($this->properties))
)
);
}
/**
* Access the current view
*
* Will instantiate a new one if none exists
* // TODO: App->getView
*
* @return Zend_View_Abstract
*/
protected function view()
{
if (self::$view === null) {
$renderer = ZfActionHelper::getStaticHelper(
'viewRenderer'
);
if (null === $renderer->view) {
$renderer->initView();
}
self::$view = $renderer->view;
}
return self::$view;
}
/**
* Cast this widget to a string. Will call your renderAsHtml() function
*
* @return string
*/
public function __toString()
{
return $this->renderAsHtml();
}
}

View File

@ -1,83 +1,209 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Web\Widget;
use Icinga\Application\Icinga;
use Icinga\Config\Config;
use Icinga\Web\Widget;
use Icinga\Config\Config as IcingaConfig;
use Icinga\Application\Logger;
use Icinga\Exception\ConfigurationError;
use Icinga\Web\Widget\Widget;
use Icinga\Web\Widget\Dashboard\Pane;
use Icinga\Web\Widget\Dashboard\Component as DashboardComponent;
use Icinga\Web\Url;
use Zend_Config as ZfConfig;
use Zend_View_Abstract;
class Dashboard extends AbstractWidget
/**
* Dashboards display multiple views on a single page
*
* The terminology is as follows:
* - Component: A single view showing a specific url
* - Pane: Aggregates one or more components on one page, displays it's title as a tab
* - Dashboard: Shows all panes
*
*/
class Dashboard implements Widget
{
protected $config;
protected $configfile;
protected $panes = array();
protected $tabs;
/**
* The configuration containing information about this dashboard
*
* @var IcingaConfig;
*/
private $config;
protected $properties = array(
'url' => null,
'tabParam' => 'pane'
);
/**
* An array containing all panes of this dashboard
*
* @var array
*/
private $panes = array();
protected function init()
{
if ($this->url === null) {
$this->url = Url::fromRequest()->without($this->tabParam);
}
}
/**
* The @see Icinga\Web\Widget\Tabs object for displaying displayable panes
*
* @var Tabs
*/
private $tabs;
/**
* The parameter that will be added to identify panes
*
* @var string
*/
private $tabParam = 'pane';
/**
* Set the given tab name as active.
*
* @param string $name The tab name to activate
*
*/
public function activate($name)
{
$this->tabs()->activate($name);
$this->getTabs()->activate($name);
}
public function tabs()
/**
* Return the tab object used to navigate through this dashboard
*
* @return Tabs
*/
public function getTabs()
{
$url = Url::fromRequest()->getUrlWithout($this->tabParam);
if ($this->tabs === null) {
$this->tabs = Widget::create('tabs');
$this->tabs = new Tabs();
foreach ($this->panes as $key => $pane) {
$this->tabs->add($key, array(
'title' => $pane->getTitle(),
'url' => clone($this->url),
'urlParams' => array($this->tabParam => $key)
));
$this->tabs->add(
$key,
array(
'title' => $pane->getTitle(),
'url' => clone($url),
'urlParams' => array($this->tabParam => $key)
)
);
}
}
return $this->tabs;
}
public function isWritable()
/**
* Store the current dashboard with all it's panes and components to the given file (or the default one if none is
* given)
*
*
* @param string $file The filename to store this dashboard as an ini
*
* @return $this
* @throws \Icinga\Exception\ConfigurationError If persisting fails, details are written to the log
*
*/
public function store($file = null)
{
return is_writable($this->configfile);
}
if ($file === null) {
$file = IcingaConfig::app('dashboard/dashboard')->getConfigFile();
}
public function store()
{
if (! @file_put_contents($this->configfile, $this->toIni())) {
return false;
if (!is_writable($file)) {
Logger::error('Tried to persist dashboard to %s, but path is not writeable', $file);
throw new ConfigurationError('Can\'t persist dashboard');
}
// make sure empty dashboards don't cause errors
$iniString = trim($this->toIni());
if (!$iniString) {
$iniString = ' ';
}
if (!@file_put_contents($file, $iniString)) {
$error = error_get_last();
if ($error == null) {
$error = 'Unknown error';
} else {
$error = $error['message'];
}
Logger::error('Tried to persist dashboard to %s, but got error: %s', $file, $error);
throw new ConfigurationError('Can\'t persist dashboard');
} else {
return $this;
}
}
public function readConfig(ZfConfig $config)
/**
* Populate this dashboard via the given configuration file
*
* @param IcingaConfig $config The configuration file to populate this dashboard with
*
* @return self
*/
public function readConfig(IcingaConfig $config)
{
$this->configfile = Config::getInstance()->getConfigDir()
. '/dashboard.ini';
$this->config = $config;
$this->panes = array();
$this->loadConfigPanes();
return $this;
}
/**
* Creates a new empty pane with the given title
*
* @param string $title
*
* @return self
*/
public function createPane($title)
{
$pane = new Pane($title);
$pane->setTitle($title);
$this->addPane($pane);
return $this;
}
/**
* Update or adds a new component with the given url to a pane
*
* @TODO: Should only allow component objects to be added directly as soon as we store more information
*
* @param string $pane The pane to add the component to
* @param Component|string $component The component to add or the title of the newly created component
* @param $url The url to use for the component
*
* @return self
*/
public function setComponentUrl($pane, $component, $url)
{
if ($component === null && strpos($pane, '.')) {
list($pane, $component) = preg_split('~\.~', $pane, 2);
}
if (!isset($this->panes[$pane])) {
$this->createPane($pane);
}
$pane = $this->getPane($pane);
if ($pane->hasComponent($component)) {
$pane->getComponent($component)->setUrl($url);
@ -87,16 +213,51 @@ class Dashboard extends AbstractWidget
return $this;
}
/**
* Return true if a pane doesn't exist or doesn't have any components in it
*
* @param string $pane The name of the pane to check for emptyness
*
* @return bool
*/
public function isEmptyPane($pane)
{
$paneObj = $this->getPane($pane);
if ($paneObj === null) {
return true;
}
$cmps = $paneObj->getComponents();
return !empty($cmps);
}
/**
* Remove a component $component from the given pane
*
* @param string $pane The pane to remove the component from
* @param Component|string $component The component to remove or it's name
*
* @return self
*/
public function removeComponent($pane, $component)
{
if ($component === null && strpos($pane, '.')) {
list($pane, $component) = preg_split('~\.~', $pane, 2);
}
$this->getPane($pane)->removeComponent($component);
$pane = $this->getPane($pane);
if ($pane !== null) {
$pane->removeComponent($component);
}
return $this;
}
public function paneEnum()
/**
* Return an array with pane name=>title format used for comboboxes
*
* @return array
*/
public function getPaneKeyTitleArray()
{
$list = array();
foreach ($this->panes as $name => $pane) {
@ -105,54 +266,85 @@ class Dashboard extends AbstractWidget
return $list;
}
public function getComponentEnum()
{
$list = array();
foreach ($this->panes as $name => $pane) {
foreach ($pane->getComponents() as $component) {
$list[$name . '.' . $component->getTitle()] =
$pane->getTitle() . ': ' . $component->getTitle();
}
}
return $list;
}
/**
* Add a pane object to this dashboard
*
* @param Pane $pane The pane to add
*
* @return self
*/
public function addPane(Pane $pane)
{
$this->panes[$pane->getName()] = $pane;
return $this;
}
/**
* Return the pane with the provided name or null if it doesn't exit
*
* @param string $name The name of the pane to return
*
* @return null|Pane The pane or null if no pane with the given name exists
*/
public function getPane($name)
{
if (!isset($this->panes[$name])) {
return null;
}
return $this->panes[$name];
}
public function renderAsHtml()
/**
* @see Icinga\Web\Widget::render
*/
public function render(Zend_View_Abstract $view)
{
if (empty($this->panes)) {
return '';
}
return $this->tabs() . $this->getActivePane();
return $this->determineActivePane()->render($view);
}
public function getActivePane()
/**
* Activates the default pane of this dashboard and returns it's name
*
* @return mixed
*/
private function setDefaultPane()
{
$active = $this->tabs()->getActiveName();
if (! $active) {
reset($this->panes);
$active = key($this->panes);
$this->activate($active);
return $active;
}
/**
* Determine the active pane either by the selected tab or the current request
*
* @return Pane The currently active pane
*/
public function determineActivePane()
{
$active = $this->getTabs()->getActiveName();
if (!$active) {
if ($active = Url::fromRequest()->getParam($this->tabParam)) {
$this->activate($active);
if ($this->isEmptyPane($active)) {
$active = $this->setDefaultPane();
} else {
$this->activate($active);
}
} else {
reset($this->panes);
$active = key($this->panes);
$this->activate($active);
$active = $this->setDefaultPane();
}
}
return $this->panes[$active];
}
/**
* Return the ini string describing this dashboard
*
* @return string
*/
public function toIni()
{
$ini = '';
@ -161,33 +353,22 @@ class Dashboard extends AbstractWidget
}
return $ini;
}
protected function loadConfigPanes()
/**
* Load all config panes from @see Dashboard::$config
*
*/
private function loadConfigPanes()
{
$items = $this->config->dashboard->toArray();
$app = Icinga::app();
foreach ($items as $key => $item) {
$items = $this->config;
foreach ($items->keys() as $key) {
$item = $this->config->get($key, false);
if (false === strstr($key, '.')) {
$pane = new Pane($key);
if (isset($item['title'])) {
$pane->setTitle($item['title']);
}
$this->addPane($pane);
$this->addPane(Pane::fromIni($key, $item));
} else {
list($dashboard, $title) = preg_split('~\.~', $key, 2);
$base_url = $item['base_url'];
$module = substr($base_url, 0, strpos($base_url, '/'));
$whitelist = array();
if (! $app->hasModule($module)) {
continue;
}
unset($item['base_url']);
$this->getPane($dashboard)->addComponent(
$title,
Url::fromPath($base_url, $item)
);
list($paneName, $title) = explode('.', $key, 2);
$pane = $this->getPane($paneName);
$pane->addComponent(DashboardComponent::fromIni($title, $item, $pane));
}
}
}

View File

@ -1,24 +1,109 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Web\Widget\Dashboard;
use Icinga\Util\Dimension;
use Icinga\Web\Url;
use Icinga\Web\Widget\Widget;
use Zend_Config;
/**
* A dashboard pane component
*
* Needs a title and an URL
* // TODO: Rename to "Dashboardlet"
* This is the element displaying a specific view in icinga2web
*
*/
class Component
class Component implements Widget
{
protected $url;
protected $title;
/**
* The url of this Component
*
* @var \Icinga\Web\Url
*/
private $url;
public function __construct($title, $url)
/**
* The title being displayed on top of the component
* @var
*/
private $title;
/**
* The width of the component, if set
*
* @var Dimension|null
*/
private $width = null;
/**
* The height of the component, if set
*
* @var Dimension|null
*/
private $height = null;
/**
* The pane containing this component, needed for the 'remove button'
* @var Pane
*/
private $pane;
/**
* The template string used for rendering this widget
*
* @var string
*/
private $template =<<<'EOD'
<div class="icinga-container dashboard" icingatitle="{TITLE}" style="{DIMENSION}">
<a href="{URL}"> {TITLE}</a>
<a class="btn btn-danger btn-mini pull-right" href="{REMOVE_URL}" style="color:black">X</a>
<div class="icinga-container" icingaurl="{URL}">
<noscript>
<iframe src="{URL}" style="height:100%; width:99%" frameborder="no"></iframe>
</noscript>
</div>
</div>
EOD;
/**
* Create a new component displaying the given url in the provided pane
*
* @param string $title The title to use for this component
* @param Url|string $url The url this component uses for displaying information
* @param Pane $pane The pane this Component will be added to
*/
public function __construct($title, $url, Pane $pane)
{
$this->title = $title;
$this->pane = $pane;
if ($url instanceof Url) {
$this->url = $url;
} else {
@ -27,7 +112,27 @@ class Component
}
/**
* Retrieve this components title
* Set the with for this component or use the default width if null is provided
*
* @param Dimension|null $width The width to use or null to use the default width
*/
public function setWidth(Dimension $width = null)
{
$this->width = $width;
}
/**
* Set the with for this component or use the default height if null is provided
*
* @param Dimension|null $height The height to use or null to use the default height
*/
public function setHeight(Dimension $height = null)
{
$this->height = $height;
}
/**
* Retrieve the components title
*
* @return string
*/
@ -37,7 +142,7 @@ class Component
}
/**
* Retrieve my url
* Retrieve the components url
*
* @return Url
*/
@ -47,9 +152,10 @@ class Component
}
/**
* Set this components URL
* Set the components URL
*
* @param string|Url $url The url to use, either as an Url object or as a path
*
* @param string|Url $url Component URL
* @return self
*/
public function setUrl($url)
@ -62,55 +168,96 @@ class Component
return $this;
}
protected function iniPair($key, $val)
{
return sprintf(
"%s = %s\n",
$key,
$this->quoteIni($val)
);
}
protected function quoteIni($str)
{
return '"' . $str . '"';
}
/**
* Return this component in a suitable format and encoding for ini files
*
* @return string
*/
public function toIni()
{
$ini = $this->iniPair('base_url', $this->url->getScript());
$ini = 'url = "' . $this->url->getRelativeUrl() . '"' . PHP_EOL;
foreach ($this->url->getParams() as $key => $val) {
$ini .= $this->iniPair($key, $val);
$ini .= $key.' = "' . $val . '"' . PHP_EOL;
}
if ($this->height !== null) {
$ini .= 'height = "' . ((string) $this->height) . '"' . PHP_EOL;
}
if ($this->width !== null) {
$ini .= 'width = "' . ((string) $this->width) . '"' . PHP_EOL;
}
return $ini;
}
/**
* Render this components HTML
* @see Widget::render()
*/
public function __toString()
public function render(\Zend_View_Abstract $view)
{
$url = clone($this->url);
$url = clone($this->url);
$url->addParams(array('view' => 'compact'));
if (isset($_GET['layout'])) {
$url->addParams(array('layout' => $_GET['layout']));
$removeUrl = Url::fromPath(
'/dashboard/removecomponent',
array(
'pane' => $this->pane->getName(),
'component' => $this->getTitle()
)
);
$html = str_replace('{URL}', $url->getAbsoluteUrl(), $this->template);
$html = str_replace('{REMOVE_URL}', $removeUrl, $html);
$html = str_replace('{DIMENSION}', $this->getBoxSizeAsCSS(), $html);
$html = str_replace('{TITLE}', $view->escape($this->getTitle()), $html);
return $html;
}
/**
* Return the height and width deifnition (if given) in CSS format
*
* @return string
*/
private function getBoxSizeAsCSS()
{
$style = '';
if ($this->height) {
$style .= 'height:' . (string) $this->height . ';';
}
if ($this->width) {
$style .= 'width:' . (string) $this->width . ';';
}
return $style;
}
/**
* Create a @see Component instance from the given Zend config, using the provided title
*
* @param $title The title for this component
* @param Zend_Config $config The configuration defining url, parameters, height, width, etc.
* @param Pane $pane The pane this component belongs to
*
* @return Component A newly created Component for use in the Dashboard
*/
public static function fromIni($title, Zend_Config $config, Pane $pane)
{
$height = null;
$width = null;
$url = $config->get('url');
$parameters = $config->toArray();
unset($parameters['url']); // otherwise there's an url = parameter in the Url
if (isset($parameters['height'])) {
$height = Dimension::fromString($parameters['height']);
unset($parameters['height']);
}
$htm = '<div class="icinga-container dashboard" icingaurl="'
. $url
. '" icingatitle="'
. htmlspecialchars($this->title)
. '">'
. "\n"
. '<h1><a href="'
. $this->url
. '">'
. htmlspecialchars($this->title)
. "</a></h1>\n"
. '<noscript><iframe src="'
. $url->addParams(array('layout' => 'embedded', 'iframe' => 'true'))
. '" style="height:100%; width:99%" frameborder="no"></iframe></noscript>'
. "\n</div>\n";
return $htm;
if (isset($parameters['width'])) {
$width = Dimension::fromString($parameters['width']);
unset($parameters['width']);
}
$cmp = new Component($title, Url::fromPath($url, $parameters), $pane);
$cmp->setHeight($height);
$cmp->setWidth($width);
return $cmp;
}
}

View File

@ -1,54 +1,145 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Web\Widget\Dashboard;
use Icinga\Web\Url;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\ProgrammingError;
use Icinga\Web\Widget\Widget;
use Zend_Config;
use Zend_View_Abstract;
class Pane
/**
* A pane, displaying different Dashboard components
*
*/
class Pane implements Widget
{
protected $name;
protected $title;
protected $components = array();
/**
* The name of this pane, as defined in the ini file
*
* @var string
*/
private $name;
/**
* The title of this pane, as displayed in the dashboard tabs
* @TODO: Currently the same as $name, evaluate if distinguishing is needed
*
* @var string
*/
private $title;
/**
* An array of @see Components that are displayed in this pane
*
* @var array
*/
private $components = array();
/**
* Create a new pane
*
* @param $name The pane to create
*/
public function __construct($name)
{
$this->name = $name;
$this->title = $name;
}
/**
* Returns the name of this pane
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Returns the title of this pane
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Overwrite the title of this pane
*
* @param string $title The new title to use for this pane
*
* @return self
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Return true if a component with the given title exists in this pane
*
* @param string $title The title of the component to check for existence
*
* @return bool
*/
public function hasComponent($title)
{
return array_key_exists($title, $this->components);
}
/**
* Return a component with the given name if existing
*
* @param string $title The title of the component to return
*
* @return Component The component with the given title
* @throws ProgrammingError If the component doesn't exist
*/
public function getComponent($title)
{
if ($this->hasComponent($title)) {
return $this->components[$title];
}
throw new ProgrammingError(sprintf(
'Trying to access invalid component: %s',
$title
));
throw new ProgrammingError(sprintf('Trying to access invalid component: %s', $title));
}
/**
* Removes the component with the given title if it exists in this pane
*
* @param string $title The pane
* @return Pane $this
*/
public function removeComponent($title)
{
if ($this->hasComponent($title)) {
@ -57,48 +148,86 @@ class Pane
return $this;
}
/**
* Return all components added at this pane
*
* @return array
*/
public function getComponents()
{
return $this->components;
}
/**
* @see Widget::render
*/
public function render(Zend_View_Abstract $view)
{
$html = PHP_EOL;
foreach ($this->components as $component) {
$html .= PHP_EOL.$component->render($view);
}
return $html;
}
/**
* Add a component to this pane, optionally creating it if $component is a string
*
* @param string|Component $component The component object or title
* (if a new component will be created)
* @param string|null $url An Url to be used when component is a string
*
* @return self
* @throws \Icinga\Exception\ConfigurationError
*/
public function addComponent($component, $url = null)
{
if ($component instanceof Component) {
$this->components[$component->title] = $component;
$this->components[$component->getTitle()] = $component;
} elseif (is_string($component) && $url !== null) {
$this->components[$component] = new Component($component, $url);
} else{
throw new ConfigurationError('You messed up your dashboard');
$this->components[$component] = new Component($component, $url, $this);
} else {
throw new ConfigurationError('Invalid component added: ' . $component);
}
return $this;
}
protected function quoteIni($str)
{
return '"' . $str . '"';
}
/**
* Return the ini representation of this pane as a string
*
* @return string
*/
public function toIni()
{
$ini = sprintf(
"[%s]\ntitle = %s\n",
$this->getName(),
$this->quoteIni($this->getTitle())
) . "\n";
if (empty($this->components)) {
return '';
}
$ini = '[' . $this->getName() . ']' . PHP_EOL.
'title = "' . $this->getTitle() . '"' . PHP_EOL;
foreach ($this->components as $title => $component) {
$ini .= sprintf(
"[%s.%s]\n",
$this->getName(),
$title
) . $component->toIni() . "\n";
// component header
$ini .= '[' . $this->getName() . '.' . $title . ']' . PHP_EOL;
// component content
$ini .= $component->toIni() . PHP_EOL;
}
return $ini;
}
public function __toString()
/**
* Create a new pane with the title $title from the given configuration
*
* @param $title The title for this pane
* @param Zend_Config $config The configuration to use for setup
*
* @return Pane
*/
public static function fromIni($title, Zend_Config $config)
{
return implode('', $this->components);
$pane = new Pane($title);
if ($config->get('title', false)) {
$pane->setTitle($config->get('title'));
}
return $pane;
}
}

View File

@ -1,81 +0,0 @@
<?php
/**
* Form
*/
namespace Icinga\Web\Widget;
use Icinga\Exception\ProgrammingError;
use Icinga\Application\Icinga;
/**
* A form loader...
*
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
* @author Icinga-Web Team <info@icinga.org>
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
*/
class Form extends AbstractWidget
{
protected $form;
protected $properties = array(
'name' => null,
'options' => null
);
public function __call($func, $args)
{
return call_user_func_array(array($this->form, $func), $args);
}
protected function init()
{
// Load form by name given in props:
$file = null;
$fparts = array();
$cparts = array();
foreach (preg_split('~/~', $this->name, -1, PREG_SPLIT_NO_EMPTY) as $part) {
$fparts[] = $part;
$cparts[] = ucfirst($part);
}
array_push($fparts, ucfirst(array_pop($fparts)));
$app = Icinga::app();
$module_name = $this->view()->module_name;
if ($module_name === 'default') {
$module_name = null;
}
if ($module_name !== null) {
$fname = $app->getModuleManager()->getModule($module_name)->getBaseDir()
. '/application/forms/'
. implode('/', $fparts)
. 'Form.php';
if (file_exists($fname)) {
$file = $fname;
array_unshift($cparts, ucfirst($module_name));
}
}
if ($file === null) {
$fname = $app->getApplicationDir('forms/')
. implode('/', $fparts)
. 'Form.php';
if (file_exists($fname)) {
$file = $fname;
} else {
throw new ProgrammingError(sprintf(
'Unable to load your form: %s',
$this->name
));
}
}
$class = 'Icinga\\Web\\Form\\' . implode('_', $cparts) . 'Form';
require_once($file);
$this->form = new $class($this->options);
}
public function renderAsHtml()
{
return (string) $this->form;
}
}

View File

@ -1,11 +1,35 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Single tab
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Web\Widget;
use Icinga\Exception\ProgrammingError;
use Zend_View_Abstract;
/**
* A single tab, usually used through the tabs widget
@ -24,41 +48,141 @@ use Icinga\Exception\ProgrammingError;
* @author Icinga-Web Team <info@icinga.org>
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
*/
class Tab extends AbstractWidget
class Tab implements Widget
{
/**
* Whether this tab is currently active
*
* @var bool
*/
protected $active = false;
private $active = false;
/**
* Default values for widget properties
*
* @var array
*/
protected $properties = array(
'name' => null,
'title' => '',
'url' => null,
'urlParams' => array(),
'icon' => null,
);
private $name = null;
/**
* Health check at initialization time
* The title displayed for this tab
*
* @throws Icinga\Exception\ProgrammingError if tab name is missing
*
* @return void
* @var string
*/
protected function init()
private $title = '';
/**
* The Url this tab points to
*
* @var string|null
*/
private $url = null;
/**
* The parameters for this tab's Url
*
* @var array
*/
private $urlParams = array();
/**
* The icon image to use for this tab or null if none
*
* @var string|null
*/
private $icon = null;
/**
* The icon class to use if $icon is null
*
* @var string|null
*/
private $iconCls = null;
/**
* Sets an icon image for this tab
*
* @param string $icon The url of the image to use
*/
public function setIcon($icon)
{
$this->icon = $icon;
}
/**
* Set's an icon class that will be used in an <i> tag if no icon image is set
*
* @param string $iconCls The CSS class of the icon to use
*/
public function setIconCls($iconCls)
{
$this->iconCls = $iconCls;
}
/**
* @param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @return mixed
*/
public function getName()
{
return $this->name;
}
/**
* @param mixed $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* Set the Url this tab points to
*
* @param string $url The Url to use for this tab
*/
public function setUrl($url)
{
$this->url = $url;
}
/**
* Set the parameters to be set for this tabs Url
*
* @param array $url The Url parameters to set
*/
public function setUrlParams(array $urlParams)
{
$this->urlParams = $urlParams;
}
/**
* Create a new Tab with the given properties
*
* Allowed properties are all properties for which a setter exists
*
* @param array $properties An array of properties
*/
public function __construct(array $properties = array())
{
foreach ($properties as $name => $value) {
$setter = 'set' . ucfirst($name);
if (method_exists($this, $setter)) {
$this->$setter($value);
}
}
if ($this->name === null) {
throw new ProgrammingError(
'Cannot create a nameless tab'
);
throw new ProgrammingError('Cannot create a nameless tab');
}
}
@ -78,31 +202,24 @@ class Tab extends AbstractWidget
return $this;
}
/**
* Whether this tab is currently active
*
* @return bool
*/
public function isActive()
{
return $this->active;
}
/**
* This is where the list item HTML is created
*
* @return string
* @see Widget::render()
*/
public function renderAsHtml()
public function render(Zend_View_Abstract $view)
{
$view = $this->view();
$class = $this->isActive() ? ' class="active"' : '';
$class = $this->active ? ' class="active"' : '';
$caption = $this->title;
if ($this->icon !== null) {
$caption = $view->img($this->icon, array(
'width' => 16,
'height' => 16
)) . ' ' . $caption;
$caption = $view->img(
$this->icon,
array(
'width' => 16,
'height' => 16
)
) . ' ' . $caption;
} elseif ($this->iconCls !== null) {
$caption = '<i class="icon-' . $this->iconCls . '"></i> ' . $caption;
}
if ($this->url !== null) {
$tab = $view->qlink(
@ -114,6 +231,7 @@ class Tab extends AbstractWidget
} else {
$tab = $caption;
}
return "<li $class>$tab</li>\n";
return '<li ' . $class . '>' . $tab . '</li>' . PHP_EOL;
}
}

View File

@ -1,8 +1,31 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Navigation tabs
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Web\Widget;
use Icinga\Exception\ProgrammingError;
@ -13,36 +36,37 @@ use Countable;
/**
* Navigation tab widget
*
* Useful if you want to create navigation tabs
*
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
* @author Icinga-Web Team <info@icinga.org>
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
*/
class Tabs extends AbstractWidget implements Countable
class Tabs implements Countable, Widget
{
/**
* This is where single tabs added to this container will be stored
*
* @var array
*/
protected $tabs = array();
private $tabs = array();
/**
* The name of the currently activated tab
*
* @var string
*/
protected $active;
private $active;
/**
* Class name(s) going to be assigned to the &lt;ul&gt; element
*
* @var string
*/
protected $tab_class = 'nav-tabs';
private $tab_class = 'nav-tabs';
protected $specialActions = false;
/**
* Array when special actions (dropdown) are enabled
* @TODO: Remove special part from tabs (Bug #4512)
*
* @var bool|array
*/
private $specialActions = false;
/**
* Activate the tab with the given name
@ -77,6 +101,11 @@ class Tabs extends AbstractWidget implements Countable
);
}
/**
* Return the name of the active tab
*
* @return string
*/
public function getActiveName()
{
return $this->active;
@ -118,7 +147,7 @@ class Tabs extends AbstractWidget implements Countable
*/
public function get($name)
{
if (! $this->has($name)) {
if (!$this->has($name)) {
throw new ProgrammingError(
sprintf(
'There is no such tab: %s',
@ -177,6 +206,13 @@ class Tabs extends AbstractWidget implements Countable
return $this;
}
/**
* Enable special actions (dropdown with format, basket and dashboard)
*
* @TODO: Remove special part from tabs (Bug #4512)
*
* @return $this
*/
public function enableSpecialActions()
{
$this->specialActions = true;
@ -184,52 +220,49 @@ class Tabs extends AbstractWidget implements Countable
}
/**
* This is where the tabs are going to be rendered
*
* @return string
* @see Widget::render
*/
public function renderAsHtml()
public function render(\Zend_View_Abstract $view)
{
$view = $this->view();
if (empty($this->tabs)) {
return '';
}
$html = '<ul class="nav ' . $this->tab_class . '">' . "\n";
$html = '<ul class="nav ' . $this->tab_class . '">' . PHP_EOL;
foreach ($this->tabs as $tab) {
$html .= $tab;
$html .= $tab->render($view);
}
// @TODO: Remove special part from tabs (Bug #4512)
$special = array();
$special[] = $this->view()->qlink(
$this->view()->img('img/classic/application-pdf.png') . ' PDF',
$special[] = $view->qlink(
$view->img('img/classic/application-pdf.png') . ' PDF',
Url::fromRequest(),
array('filetype' => 'pdf'),
array('target' => '_blank', 'quote' => false)
);
$special[] = $this->view()->qlink(
$this->view()->img('img/classic/application-csv.png') . ' CSV',
$special[] = $view->qlink(
$view->img('img/classic/application-csv.png') . ' CSV',
Url::fromRequest(),
array('format' => 'csv'),
array('target' => '_blank', 'quote' => false)
);
$special[] = $this->view()->qlink(
$this->view()->img('img/classic/application-json.png') . ' JSON',
$special[] = $view->qlink(
$view->img('img/classic/application-json.png') . ' JSON',
Url::fromRequest(),
array('format' => 'json', 'quote' => false),
array('target' => '_blank', 'quote' => false)
);
$special[] = $this->view()->qlink(
$this->view()->img('img/classic/basket.png') . ' URL Basket',
$special[] = $view->qlink(
$view->img('img/classic/basket.png') . ' URL Basket',
Url::fromPath('basket/add'),
array('url' => Url::fromRequest()->getRelativeUrl()),
array('quote' => false)
);
$special[] = $this->view()->qlink(
$this->view()->img('img/classic/dashboard.png') . ' Dashboard',
$special[] = $view->qlink(
$view->img('img/classic/dashboard.png') . ' Dashboard',
Url::fromPath('dashboard/addurl'),
array('url' => Url::fromRequest()->getRelativeUrl()),
array('quote' => false)
@ -255,11 +288,23 @@ class Tabs extends AbstractWidget implements Countable
return $html;
}
/**
* Return the number of tabs
*
* @see Countable
*
* @return int
*/
public function count()
{
return count($this->tabs);
}
/**
* Return all tabs contained in this tab panel
*
* @return array
*/
public function getTabs()
{
return $this->tabs;

View File

@ -0,0 +1,49 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Web\Widget;
use Icinga\Web\View;
use Zend_View_Abstract;
/**
* Abstract class for reusable view elements that can be
* rendered to a view
*
*/
interface Widget
{
/**
* Renders this widget via the given view and returns the
* HTML as a string
*
* @param \Zend_View_Abstract $view
* @return string
*/
public function render(Zend_View_Abstract $view);
}

View File

@ -5,7 +5,7 @@
// Last Update : 2011-04-15
//
// Description : Example 059 for TCPDF class
// Table Of Content using HTML templates.
// Table Of Content using HTML Templates.
//
// Author: Nicola Asuni
//
@ -19,7 +19,7 @@
/**
* Creates an example PDF TEST document using TCPDF
* @package com.tecnick.tcpdf
* @abstract TCPDF - Example: Table Of Content using HTML templates.
* @abstract TCPDF - Example: Table Of Content using HTML Templates.
* @author Nicola Asuni
* @since 2010-05-06
*/
@ -153,7 +153,7 @@ $bookmark_templates = array();
/*
* The key of the $bookmark_templates array represent the bookmark level (from 0 to n).
* The following templates will be replaced with proper content:
* The following Templates will be replaced with proper content:
* #TOC_DESCRIPTION# this will be replaced with the bookmark description;
* #TOC_PAGE_NUMBER# this will be replaced with page number.
*
@ -166,7 +166,7 @@ $bookmark_templates = array();
$bookmark_templates[0] = '<table border="0" cellpadding="0" cellspacing="0" style="background-color:#EEFAFF"><tr><td width="155mm"><span style="font-family:times;font-weight:bold;font-size:12pt;color:black;">#TOC_DESCRIPTION#</span></td><td width="25mm"><span style="font-family:courier;font-weight:bold;font-size:12pt;color:black;" align="right">#TOC_PAGE_NUMBER#</span></td></tr></table>';
$bookmark_templates[1] = '<table border="0" cellpadding="0" cellspacing="0"><tr><td width="5mm">&nbsp;</td><td width="150mm"><span style="font-family:times;font-size:11pt;color:green;">#TOC_DESCRIPTION#</span></td><td width="25mm"><span style="font-family:courier;font-weight:bold;font-size:11pt;color:green;" align="right">#TOC_PAGE_NUMBER#</span></td></tr></table>';
$bookmark_templates[2] = '<table border="0" cellpadding="0" cellspacing="0"><tr><td width="10mm">&nbsp;</td><td width="145mm"><span style="font-family:times;font-size:10pt;color:#666666;"><i>#TOC_DESCRIPTION#</i></span></td><td width="25mm"><span style="font-family:courier;font-weight:bold;font-size:10pt;color:#666666;" align="right">#TOC_PAGE_NUMBER#</span></td></tr></table>';
// add other bookmark level templates here ...
// add other bookmark level Templates here ...
// add table of content at page 1
// (check the example n. 45 for a text-only TOC

View File

@ -76,7 +76,7 @@
// dullus for text Justification.
// Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
// Patrick Benny for text stretch suggestion on Cell().
// Johannes Güntert for JavaScript support.
// Johannes G<EFBFBD>ntert for JavaScript support.
// Denis Van Nuffelen for Dynamic Form.
// Jacek Czekaj for multibyte justification
// Anthony Ferrara for the reintroduction of legacy image methods.
@ -87,7 +87,7 @@
// Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
// Moritz Wagner and Andreas Wurmser for graphic functions.
// Andrew Whitehead for core fonts support.
// Esteban Joël Marín for OpenType font conversion.
// Esteban Jo<EFBFBD>l Mar<61>n for OpenType font conversion.
// Teus Hagen for several suggestions and fixes.
// Yukihiro Nakadaira for CID-0 CJK fonts fixes.
// Kosmas Papachristos for some CSS improvements.
@ -647,7 +647,7 @@ class TCPDF {
protected $footer_font;
/**
* Language templates.
* Language Templates.
* @protected
*/
protected $l;
@ -6123,7 +6123,7 @@ class TCPDF {
* @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
* @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
* @return float Return the minimal height needed for multicell method for printing the $txt param.
* @author Alexander Escalona Fernández, Nicola Asuni
* @author Alexander Escalona Fern<EFBFBD>ndez, Nicola Asuni
* @public
* @since 4.5.011
*/
@ -6230,7 +6230,7 @@ class TCPDF {
* @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
* @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
* @return float Return the minimal height needed for multicell method for printing the $txt param.
* @author Nicola Asuni, Alexander Escalona Fernández
* @author Nicola Asuni, Alexander Escalona Fern<EFBFBD>ndez
* @public
*/
public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
@ -11442,7 +11442,7 @@ class TCPDF {
}
/**
* Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x2, y2) as the Bézier control points.
* Append a cubic B<EFBFBD>zier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x2, y2) as the B<EFBFBD>zier control points.
* The new current point shall be (x3, y3).
* @param $x1 (float) Abscissa of control point 1.
* @param $y1 (float) Ordinate of control point 1.
@ -11460,7 +11460,7 @@ class TCPDF {
}
/**
* Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using the current point and (x2, y2) as the Bézier control points.
* Append a cubic B<EFBFBD>zier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using the current point and (x2, y2) as the B<EFBFBD>zier control points.
* The new current point shall be (x3, y3).
* @param $x2 (float) Abscissa of control point 2.
* @param $y2 (float) Ordinate of control point 2.
@ -11476,7 +11476,7 @@ class TCPDF {
}
/**
* Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x3, y3) as the Bézier control points.
* Append a cubic B<EFBFBD>zier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x3, y3) as the B<EFBFBD>zier control points.
* The new current point shall be (x3, y3).
* @param $x1 (float) Abscissa of control point 1.
* @param $y1 (float) Ordinate of control point 1.
@ -12271,7 +12271,7 @@ class TCPDF {
/**
* Insert Named Destinations.
* @protected
* @author Johannes Güntert, Nicola Asuni
* @author Johannes G<EFBFBD>ntert, Nicola Asuni
* @since 5.9.098 (2011-06-23)
*/
protected function _putdests() {
@ -12499,7 +12499,7 @@ class TCPDF {
* Adds a javascript
* @param $script (string) Javascript code
* @public
* @author Johannes Güntert, Nicola Asuni
* @author Johannes G<EFBFBD>ntert, Nicola Asuni
* @since 2.1.002 (2008-02-12)
*/
public function IncludeJS($script) {
@ -12528,7 +12528,7 @@ class TCPDF {
/**
* Create a javascript PDF string.
* @protected
* @author Johannes Güntert, Nicola Asuni
* @author Johannes G<EFBFBD>ntert, Nicola Asuni
* @since 2.1.002 (2008-02-12)
*/
protected function _putjavascript() {
@ -13414,7 +13414,7 @@ class TCPDF {
* @param $private_key (mixed) private key (string or filename prefixed with 'file://')
* @param $private_key_password (string) password
* @param $extracerts (string) specifies the name of a file containing a bunch of extra certificates to include in the signature which can for example be used to help the recipient to verify the certificate that you used.
* @param $cert_type (int) The access permissions granted for this document. Valid values shall be: 1 = No changes to the document shall be permitted; any change to the document shall invalidate the signature; 2 = Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature; 3 = Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature.
* @param $cert_type (int) The access permissions granted for this document. Valid values shall be: 1 = No changes to the document shall be permitted; any change to the document shall invalidate the signature; 2 = Permitted changes shall be filling in forms, instantiating page Templates, and signing; other changes shall invalidate the signature; 3 = Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature.
* @param $info (array) array of option information: Name, Location, Reason, ContactInfo.
* @public
* @author Nicola Asuni
@ -14174,7 +14174,7 @@ class TCPDF {
* @param $col1 (array) first color (Grayscale, RGB or CMYK components).
* @param $col2 (array) second color (Grayscale, RGB or CMYK components).
* @param $coords (array) array of the form (x1, y1, x2, y2) which defines the gradient vector (see linear_gradient_coords.jpg). The default value is from left to right (x1=0, y1=0, x2=1, y2=0).
* @author Andreas Würmser, Nicola Asuni
* @author Andreas W<EFBFBD>rmser, Nicola Asuni
* @since 3.1.000 (2008-06-09)
* @public
*/
@ -14192,7 +14192,7 @@ class TCPDF {
* @param $col1 (array) first color (Grayscale, RGB or CMYK components).
* @param $col2 (array) second color (Grayscale, RGB or CMYK components).
* @param $coords (array) array of the form (fx, fy, cx, cy, r) where (fx, fy) is the starting point of the gradient with color1, (cx, cy) is the center of the circle with color2, and r is the radius of the circle (see radial_gradient_coords.jpg). (fx, fy) should be inside the circle, otherwise some areas will not be defined.
* @author Andreas Würmser, Nicola Asuni
* @author Andreas W<EFBFBD>rmser, Nicola Asuni
* @since 3.1.000 (2008-06-09)
* @public
*/
@ -14215,7 +14215,7 @@ class TCPDF {
* @param $coords_min (array) minimum value used by the coordinates. If a coordinate's value is smaller than this it will be cut to coords_min. default: 0
* @param $coords_max (array) maximum value used by the coordinates. If a coordinate's value is greater than this it will be cut to coords_max. default: 1
* @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
* @author Andreas Würmser, Nicola Asuni
* @author Andreas W<EFBFBD>rmser, Nicola Asuni
* @since 3.1.000 (2008-06-09)
* @public
*/
@ -14307,7 +14307,7 @@ class TCPDF {
* @param $y (float) ordinate of the top left corner of the rectangle.
* @param $w (float) width of the rectangle.
* @param $h (float) height of the rectangle.
* @author Andreas Würmser, Nicola Asuni
* @author Andreas W<EFBFBD>rmser, Nicola Asuni
* @since 3.1.000 (2008-06-09)
* @protected
*/
@ -21301,13 +21301,13 @@ if (! $size) $size = 10;
}
/**
* Output a Table Of Content Index (TOC) using HTML templates.
* Output a Table Of Content Index (TOC) using HTML Templates.
* This method must be called after all Bookmarks were set.
* Before calling this method you have to open the page using the addTOCPage() method.
* After calling this method you have to call endTOCPage() to close the TOC page.
* @param $page (int) page number where this TOC should be inserted (leave empty for current page).
* @param $toc_name (string) name to use for TOC bookmark.
* @param $templates (array) array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number.
* @param $templates (array) array of html Templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number.
* @param $correct_align (boolean) if true correct the number alignment (numbers must be in monospaced font like courier and right aligned on LTR, or left aligned on RTL)
* @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
* @param $color (array) RGB color array for title (values from 0 to 255).
@ -21352,7 +21352,7 @@ if (! $size) $size = 10;
}
$maxpage = max($maxpage, $outline['p']);
}
// replace templates with current values
// replace Templates with current values
$row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
$row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
// add link to page
@ -23300,7 +23300,7 @@ if (! $size) $size = 10;
}
break;
}
case 'Q': { // quadratic Bézier curveto
case 'Q': { // quadratic B<EFBFBD>zier curveto
foreach ($params as $ck => $cp) {
$params[$ck] = $cp;
if ((($ck + 1) % 4) == 0) {
@ -23326,7 +23326,7 @@ if (! $size) $size = 10;
}
break;
}
case 'T': { // shorthand/smooth quadratic Bézier curveto
case 'T': { // shorthand/smooth quadratic B<EFBFBD>zier curveto
foreach ($params as $ck => $cp) {
$params[$ck] = $cp;
if (($ck % 2) != 0) {

View File

@ -5,10 +5,12 @@ use Icinga\Web\Hook;
use Icinga\File\Csv;
use Monitoring\Backend;
use Icinga\Application\Benchmark;
use Icinga\Web\Widget\Tabs;
class Monitoring_ListController extends ModuleActionController
{
protected $backend;
private $compactView = null;
public function init()
{
@ -22,6 +24,7 @@ class Monitoring_ListController extends ModuleActionController
public function hostsAction()
{
Benchmark::measure("hostsAction::query()");
$this->compactView = "hosts-compact";
$this->view->hosts = $this->query(
'status',
array(
@ -57,6 +60,8 @@ class Monitoring_ListController extends ModuleActionController
$state_column = 'service_hard_state';
$state_change_column = 'service_last_hard_state_change';
}
$this->compactView = "services-compact";
$this->view->services = $this->query('status', array(
'host_name',
@ -191,6 +196,10 @@ class Monitoring_ListController extends ModuleActionController
protected function handleFormatRequest($query)
{
if ($this->compactView !== null && ($this->_getParam('view', false) === 'compact')) {
$this->_helper->viewRenderer($this->compactView);
}
if ($this->_getParam('format') === 'sql') {
echo '<pre>'
. htmlspecialchars(wordwrap($query->getQuery()->dump()))
@ -213,7 +222,7 @@ class Monitoring_ListController extends ModuleActionController
protected function getTabs()
{
$tabs = $this->widget('tabs');
$tabs = new Tabs();
$tabs->add('services', array(
'title' => 'All services',
'icon' => 'img/classic/service.png',

View File

@ -33,6 +33,7 @@ use Icinga\Web\Hook;
use Monitoring\Object\Host;
use Monitoring\Object\Service;
use Icinga\Application\Benchmark;
use Icinga\Web\Widget\Tabs;
/**
* Class Monitoring_ShowController
*
@ -425,7 +426,7 @@ class Monitoring_ShowController extends ModuleActionController
protected function createTabs()
{
$object = $this->view->object;
$tabs = $this->widget('tabs');
$tabs = new Tabs();
if (!$this->view->host) {
return $tabs;
}

View File

@ -2,6 +2,7 @@
use Icinga\Web\ModuleActionController;
use Icinga\Backend;
use Icinga\Web\Widget\Tabs;
class Monitoring_SummaryController extends ModuleActionController
{
@ -18,7 +19,7 @@ class Monitoring_SummaryController extends ModuleActionController
protected function getTabs()
{
$tabs = $this->widget('tabs');
$tabs = new Tabs();
$tabs->add('hostgroup', array(
'title' => 'Hostgroups',
'url' => 'monitoring/summary/group',

View File

@ -1,4 +1,5 @@
<?= $this->tabs ?>
<?= $this->tabs->render($this); ?>
<pre>
<?= $this->escape(print_r($this->contactgroups->fetchAll(), 1)) ?>
</pre>

View File

@ -1,4 +1,5 @@
<?= $this->tabs ?>
<?= $this->tabs->render($this); ?>
<pre>
<?= $this->escape(print_r($this->contacts->fetchAll(), 1)) ?>
</pre>

View File

@ -1,4 +1,4 @@
<?= $this->tabs ?>
<?= $this->tabs->render($this); ?>
<?php
/**

View File

@ -1,4 +1,5 @@
<?= $this->tabs ?>
<?= $this->tabs->render($this); ?>
<pre>
<?= $this->escape(print_r($this->hostgroups->fetchAll(), 1)) ?>
</pre>

View File

@ -1,64 +1,109 @@
<?php
$count = $hosts->count();
if (! $count) {
echo '- no host is matching this filter -';
return;
}
$hosts->paginate();
?><table class="hosts action">
<thead>
<tr>
<th style="width: 6em;" >State</th>
<th >Host</th>
</tr>
</thead>
<tbody>
<?php foreach ($hosts->fetchAll() as $host):
$icons = array();
if ($host->host_acknowledged) {
$icons['ack.gif'] = 'Problem has been acknowledged';
}
if ($host->host_in_downtime) {
$icons['downtime.gif'] = 'Host is in a scheduled downtime';
}
$state_classes = array($this->monitoringState($host, 'host'));
if ($host->host_acknowledged || $host->host_in_downtime) {
$state_classes[] = 'handled';
}
if ($host->host_last_state_change > (time() - 600)) {
$state_classes[] = 'new';
}
$state_title = strtoupper($this->monitoringState($host, 'host'))
. ' since '
. date('Y-m-d H:i:s', $host->host_last_state_change);
$hosts = $this->hosts->paginate();
$viewHelper = $this->getHelper('MonitoringState');
$trimArea = $this->getHelper('Trim');
?>
<tr class="<?= implode(' ', $state_classes) ?>">
<td style="width: 20%;" class="state" title="<?= $state_title ?>"><?= $this->qlink(
$this->timeSince($host->host_last_state_change),
'monitoring/show/history',
array('host' => $host->host_name),
array('quote' => false)) ?></td>
<td style="width: 80%;"><?
foreach ($icons as $icon => $alt)
echo $this->img('img/classic/' . $icon, array(
'class' => 'icon',
'title' => $alt
));
?>
<?= $this->qlink(
$host->host_name,
'monitoring/show/host',
array('host' => $host->host_name),
array('class' => 'row-action')
) ?></td>
</tr>
<? endforeach ?>
</tbody>
<?= $this->paginationControl($hosts, null, null, array('preserve' => $this->preserve)) ?>
<table class="statustable action hosts">
<thead>
<tr>
<th colspan="3">Status</th>
<th>Host</th>
<th>Output</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach($hosts as $host): ?>
<tr class="<?= implode(' ', $viewHelper->getStateFlags($host, 'host')); ?>">
<td class="icons indicator">
<div class="img-box"><?php $trimArea->start(); ?>
<?php if ($host->host_icon_image) : ?>
<img src="<?= $host->host_icon_image; ?>"/>
<?php endif; ?>
<?php $trimArea->end(); ?></div>
</td>
<td class="icons indicator">
<div class="icon-box"><?php $trimArea->start(); ?>
<?php if (!$host->host_handled && $host->host_state > 0): ?>
<a href="#" title="<?= 'Unhandled host' ?>">
<i class="icon-warning-sign"></i>
</a>
<?php endif; ?>
<?php if ($host->host_acknowledged && !$host->host_in_downtime): ?>
<a href="#" title="<?= 'Acknowledged' ?>">
<i class="icon-ok-sign"></i>
</a>
<?php endif; ?>
<?php if ($host->host_is_flapping): ?>
<a href="#" title="<?= 'Flapping' ?>">
<i class="icon-random"></i>
</a>
<?php endif; ?>
<?php if (!$host->host_notifications_enabled): ?>
<a href="#" title="<?= 'Notifications disabled' ?>">
<i class="icon-volume-off"></i>
</a>
<?php endif; ?>
<?php if ($host->host_in_downtime): ?>
<a href="#" title="<?= 'In downtime' ?>">
<i class="icon-wrench"></i>
</a>
<?php endif; ?>
<?php $trimArea->end(); ?></div>
</td>
<td class="indicator state" title="<?= $viewHelper->getStateTitle($host, 'host'); ?>">
<div class="statetext">
<?= $this->qlink(
"<b>".ucfirst($viewHelper->monitoringState($host, 'host')).'</b>'.
'<div class="nowrap"> since&nbsp;'.
$this->timeSince($host->host_last_state_change),
'monitoring/show/history', array(
'host' => $host->host_name
),
array('quote' => false)
);?>
<?php if ($host->host_state_type == 0): ?>
<a href="#" title="<?= 'Soft state' ?>">
<i class="icon-gears"></i>
</a>
<?php endif; ?>
</div>
</td>
<td class="hostname">
<?php if ($host->host_last_comment !== null): ?>
<a href="#" title="<?= 'Comments' ?>">
<i class="icon-comment"> </i>
</a>
<?php endif; ?>
<?= $this->qlink(
'<b>'.$host->host_name.'</b><br/>'.
'<i>'.$host->host_address.'</i>',
'monitoring/show/host', array(
'host' => $host->host_name
), array(
'class' => 'row-action',
'quote' => false
)
); ?>
<?php if ($host->host_action_url != ''): ?>
<a href="<?= $host->host_action_url; ?>">Action</a>
<?php endif; ?>
<?php if ($host->host_notes_url != ''): ?>
<a href="<?= $host->host_notes_url; ?>">Notes</a>
<?php endif; ?>
</td>
<td class="expand-full">
<?= $this->escape(substr(strip_tags($host->host_output), 0, 500)); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>

View File

@ -1,4 +1,4 @@
<?= $this->tabs ?>
<?= $this->tabs->render($this); ?>
<?php
$hosts = $this->hosts->paginate();

View File

@ -1,4 +1,5 @@
<?= $this->tabs ?>
<?= $this->tabs->render($this); ?>
<pre>
<?= $this->escape(print_r($this->servicegroups->fetchAll(), 1)) ?>
</pre>

View File

@ -1,102 +1,160 @@
<?php
$paginator = $services->paginate();
$count = $services->count();
if (! $count) {
echo '- no object is matching this filter -';
return;
}
$services->paginate();
?><table class="services action">
<thead>
<tr>
<th style="width: 6em;" >State</th>
<th >Service</th>
</tr>
</thead>
<tbody>
<?php foreach ($services->fetchAll() as $service):
$icons = array();
if ($service->service_acknowledged) {
$icons['ack.gif'] = 'Problem has been acknowledged';
}
if ($service->service_in_downtime) {
$icons['downtime.gif'] = 'Service is in a scheduled downtime';
}
if ($service->host_problems) {
$icons['server.png'] = 'This services host has a problem';
}
$state_classes = array($this->monitoringState($service, 'service'));
if ($service->service_handled || $service->service_acknowledged || ! empty($service->service_downtimes_with_info)) {
$state_classes[] = 'handled';
}
if ($service->service_last_state_change > (time() - 600)) {
$state_classes[] = 'new';
}
$state_title = date('Y-m-d H:i:s', $service->service_last_state_change);
$params = array(
'host' => $service->host_name,
'service' => $service->service_description
);
if (isset($this->preserve['backend'])) {
$params['backend'] = $this->preserve['backend'];
}
$viewHelper = $this->getHelper('MonitoringState');
$trimArea = $this->getHelper('Trim');
?>
<tr class="<?= implode(' ', $state_classes) ?>">
<td class="state" title="<?= $state_title ?>"><?=
$this->qlink(
$this->timeSince($service->service_last_state_change),
'monitoring/show/history',
$params,
array('quote' => false)
) ?></td>
<td><?php
foreach ($icons as $icon => $alt) {
echo $this->img('img/classic/' . $icon, array(
'class' => 'icon',
'title' => $alt
));
}
echo $this->qlink(
$service->host_name,
'monitoring/show/host',
$params
) . ': ' . $this->qlink(
$service->service_description,
'monitoring/show/service',
$params,
array('class' => 'row-action')
) ?></td>
</tr>
<?php endforeach ?>
</tbody>
<?php if (empty($services)): ?>
<div class="alert alert-info fullpage_infobox">
Sorry, no services found for this search
</div>
<?php return; endif ?>
<form method="get" action="<?= $this->qUrl(
'monitoring/list/services?' . http_build_query($this->services->getAppliedFilter()->toParams()),
array()
);
?>">
Sort by <?= $this->formSelect(
'sort',
$this->sort,
array('class' => 'autosubmit'),
array(
'service_severity' => 'Severity',
'service_last_state_change' => 'Last state change',
'service_last_time_unknown' => 'Last UNKNOWN',
'service_last_time_critical' => 'Last CRITICAL',
'service_last_time_warning' => 'Last WARNING',
'service_last_time_ok' => 'Last OK',
'service_description' => 'Service',
)
) ?>
<input type="search" name="filter" placeholder="Type to filter" />
<button class="btn btn-small"><i class="icon-refresh"></i></button>
</form>
<?= $this->paginationControl($paginator, null, null, array('preserve' => $this->preserve)) ?>
<table class="statustable action services">
<thead>
<tr>
<th colspan="3">Status</th>
<th>Service</th>
<th>Output</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($services->fetchAll() as $service): ?>
<tr class="<?= implode(' ', $viewHelper->getStateFlags($service, 'service')); ?>">
<td class="icons indicator">
<div class="img-box"><?php $trimArea->start(); ?>
<?php if ($service->service_icon_image) : ?>
<img src="<?= $service->service_icon_image; ?>"/>
<?php endif; ?>
<?php $trimArea->end(); ?></div>
</td>
<td class="icons indicator">
<div class="icon-box"><?php $trimArea->start(); ?>
<?php if (!$service->service_handled && $service->service_state > 0): ?>
<a href="#" title="<?= 'Unhandled service' ?>">
<i class="icon-warning-sign"></i>
</a>
<?php endif; ?>
<?php if ($service->service_acknowledged && !$service->service_in_downtime): ?>
<a href="#" title="<?= 'Acknowledged' ?>">
<i class="icon-ok-sign"></i>
</a>
<?php endif; ?>
<?php if ($service->service_is_flapping): ?>
<a href="#" title="<?= 'Flapping' ?>">
<i class="icon-random"></i>
</a>
<?php endif; ?>
<?php if (!$service->service_notifications_enabled): ?>
<a href="#" title="<?= 'Notifications disabled' ?>">
<i class="icon-volume-off"></i>
</a>
<?php endif; ?>
<?php if ($service->service_in_downtime): ?>
<a href="#" title="<?= 'In downtime' ?>">
<i class="icon-wrench"></i>
</a>
<?php endif; ?>
<?php $trimArea->end(); ?></div>
</td>
<td class="indicator state" title="<?= $viewHelper->getStateTitle($service, 'service'); ?>">
<div class="statetext">
<?= $this->qlink(
"<b>".ucfirst($viewHelper->monitoringState($service, 'service'))."</b>".
'<div class="nowrap"> since&nbsp;'.
$this->timeSince($service->service_last_state_change),
'monitoring/show/history', array(
'host' => $service->host_name,
'service' => $service->service_description
),
array('quote' => false)
);?>
<?php if ($service->service_state_type == 0): ?>
<a href="#" title="<?= 'Soft state' ?>">
<i class="icon-gears"></i>
</a>
<?php endif; ?>
</div>
</td>
<td class="servicename">
<?php if ($service->service_last_comment !== null): ?>
<a href="#" title="<?= 'Comments' ?>">
<i class="icon-comment"> </i>
</a>
<?php endif; ?>
<?= $this->qlink(
"<b>".$service->service_display_name."</b><br/>",
'monitoring/show/service', array(
'host' => $service->host_name,
'service' => $service->service_description
), array(
'class' => 'row-action',
'quote' => false
)
); ?>
<?= $this->qlink(
$service->host_name,
'monitoring/show/host', array(
'host' => $service->host_name
), array(
'class' => 'row-action',
'quote' => false
)
); ?>
<?php if ($service->service_action_url != ""): ?>
<a href="<?= $service->service_action_url; ?>">Action</a>
<?php endif; ?>
<?php if ($service->service_notes_url != ""): ?>
<a href="<?= $service->service_notes_url; ?>">Notes</a>
<?php endif; ?>
<?php if ($service->service_state_type == 0): ?>
<a href="#" title="<?= 'Soft state' ?>">
<i class="icon-gears"></i>
</a>
<?php endif; ?>
</td>
<td class="expand-full">
<?= $this->escape(substr(strip_tags($service->service_output), 0, 10000)); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php
$limit = $services->getLimit();
$count = $services->count();
if ($limit < $count) {
$more = sprintf(
$this->translate(
'Showing %d of %s, click here for more',
$limit,
$count
)
);
} else {
$more = $this->translate(
sprintf(
'Showing %d objects, click here for details',
$count
)
);
}
?><p><?= $this->qlink($more,$this->url()->without('page', 'limit', 'view')) ?></p>

View File

@ -1,4 +1,4 @@
<?= $this->tabs ?>
<?= $this->tabs->render($this); ?>
<?php
$paginator = $services->paginate();

View File

@ -1,6 +1,6 @@
<? $showService = $object->service_description && $tabs->getActiveName() !== 'host' ?>
<? if (! $compact): ?>
<?= $tabs ?>
<?= $tabs->render($this) ?>
<? endif ?>
<table style="margin-bottom: 1em">
<tr class="<?= $this->monitoringState($object, 'host') ?><?= $object->host_handled ? ' handled' : '' ?>">

View File

@ -13,7 +13,8 @@ if ($this->tabs->getActiveName() === 'history') {
}
if (!$this->compact) {
echo $this->tabs;
echo $this->tabs->render($this);
}
?>
<div class="information-container">

View File

@ -18,7 +18,7 @@ require_once(dirname(__FILE__).'/../schemes/StatusdatTemplates.php');
* to according objects.cache and status.dat files which then can be read
* by the Statusdat parser and used in tests.
*
* The templates for insertion can be found under schemes/objectsCacheTemplates.php
* The Templates for insertion can be found under schemes/objectsCacheTemplates.php
* and schemes/StatusdatTempaltes.php
*
*/

View File

@ -71,12 +71,13 @@
};
this.loadAsyncContainers = function(root) {
$('.icinga-container[icinga-url]',root).each(function() {
$('.icinga-container[icingaurl]',root).each(function() {
var el = $(this);
var url = el.attr('icinga-url');
var url = el.attr('icingaurl');
el.attr('loaded',true);
async.loadToTarget(el,url);
});
log.debug("Loading async");
};
this.initializeContainers = function(root) {

View File

@ -0,0 +1,58 @@
<?php
namespace Tests\Icinga\Util;
require_once("../../library/Icinga/Util/Dimension.php");
use Icinga\Util\Dimension;
/**
* Tests for the dimension class
*
*/
class DimensionTest extends \PHPUnit_Framework_TestCase {
/**
* Test Dimension creation from string
*
*/
public function testStringFactoryWithValidInput()
{
$d = Dimension::fromString("200px");
$this->assertEquals(200, $d->getValue(), "Asserting the numeric value of px input to be correctly parsed");
$this->assertEquals(Dimension::UNIT_PX, $d->getUnit(), "Asserting the unit of px input to be correctly parsed");
$d = Dimension::fromString("40%");
$this->assertEquals(40, $d->getValue(), "Asserting the numeric value of % input to be correctly parsed");
$this->assertEquals(Dimension::UNIT_PERCENT, $d->getUnit(), "Asserting the unit of % input to be correctly parsed");
$d = Dimension::fromString("4044em");
$this->assertEquals(4044, $d->getValue(), "Asserting the numeric value of em input to be correctly parsed");
$this->assertEquals(Dimension::UNIT_EM, $d->getUnit(), "Asserting the unit of em input to be correctly parsed");
$d = Dimension::fromString("010pt");
$this->assertEquals(10, $d->getValue(), "Asserting the numeric value of pt input to be correctly parsed");
$this->assertEquals(Dimension::UNIT_PT, $d->getUnit(), "Asserting the unit of pt input to be correctly parsed");
}
/**
* Test string creation from Dimension
*
*/
public function testStringCreation()
{
$d = new Dimension(1000, Dimension::UNIT_PX);
$this->assertEquals("1000px", (string) $d, "Asserting value-unit string creation to be correct");
$d = new Dimension(40.5, Dimension::UNIT_PT);
$this->assertEquals("40pt", (string) $d, "Asserting float values being truncated by now");
}
/**
*
*/
public function testInvalidDimensions()
{
$d = new Dimension(-20, Dimension::UNIT_PX);
$this->assertFalse($d->isDefined(), "Asserting a negative dimension to be considered invalid");
}
}

View File

@ -6,7 +6,7 @@ require_once '../../library/Icinga/Web/Hook/Configuration/ConfigurationTabInterf
require_once '../../library/Icinga/Web/Hook/Configuration/ConfigurationTab.php';
require_once '../../library/Icinga/Web/Hook/Configuration/ConfigurationTabBuilder.php';
require_once '../../library/Icinga/Web/Hook.php';
require_once '../../library/Icinga/Web/Widget/AbstractWidget.php';
require_once '../../library/Icinga/Web/Widget/Widget.php';
require_once '../../library/Icinga/Web/Widget/Tabs.php';
require_once '../../library/Icinga/Web/Widget/Tab.php';
require_once '../../library/Icinga/Exception/ProgrammingError.php';

View File

@ -1,113 +0,0 @@
<?php
/**
* Created by JetBrains PhpStorm.
* User: mhein
* Date: 6/10/13
* Time: 1:31 PM
* To change this template use File | Settings | File Templates.
*/
namespace Tests\Icinga\Web\Widget;
require_once '../../library/Icinga/Exception/ProgrammingError.php';
require_once '../../library/Icinga/Web/Widget.php';
require_once '../../library/Icinga/Web/Widget/AbstractWidget.php';
require_once 'Zend/View.php';
require_once 'Zend/Controller/Action/HelperBroker.php';
use Icinga\Web\Widget\AbstractWidget;
/**
* Class TestWidget
* @package Tests\Icinga\Web\Widget
* @property boolean $test1
* @property boolean $test2
*/
class TestWidget extends AbstractWidget
{
protected $properties = array(
'test1' => true,
'test2' => false
);
public function renderAsHtml()
{
return "ok123123";
}
}
class TestWidget2 extends AbstractWidget
{
protected function init()
{
$this->view();
}
public function getView()
{
return $this->view();
}
public function renderAsHtml()
{
return "ok123123";
}
}
class AbstractWidgetTest extends \PHPUnit_Framework_TestCase
{
public function testAbstractImplementation1()
{
$widget = new TestWidget(
array(
'test1' => false,
'test2' => true
)
);
$this->assertTrue($widget->test2);
$this->assertFalse($widget->test1);
$this->assertEquals('ok123123', (string)$widget);
$this->assertEquals('ok123123', $widget->renderAsHtml());
}
/**
* @expectedException Icinga\Exception\ProgrammingError
* @expectedExceptionMessage Trying to set invalid "test3" property in Tests\Icinga\Web\Widget\TestWidget. Allowed are: test1, test2
*/
public function testSetFail1()
{
$widget = new TestWidget();
$widget->test3 = true;
}
/**
* @expectedException Icinga\Exception\ProgrammingError
* @expectedExceptionMessage Trying to set invalid "unknown" property in Tests\Icinga\Web\Widget\TestWidget2. Allowed are: none
*/
public function testSetFail2()
{
$widget = new TestWidget2();
$widget->unknown = true;
}
/**
* @expectedException Icinga\Exception\ProgrammingError
* @expectedExceptionMessage Trying to get invalid "test3" property for Tests\Icinga\Web\Widget\TestWidget
*/
public function testGetFail()
{
$widget = new TestWidget();
$target = $widget->test3;
}
public function testView1()
{
$widget = new TestWidget2();
$view = $widget->getView();
$this->assertInstanceOf('Zend_View', $view);
}
}

View File

@ -1,31 +0,0 @@
<?php
namespace Tests\Icinga\Web;
use Icinga\Web\Widget;
require_once '../../library/Icinga/Web/Widget.php';
require_once '../../library/Icinga/Web/Widget/AbstractWidget.php';
require_once '../../library/Icinga/Web/Widget/Tab.php';
require_once '../../library/Icinga/Exception/ProgrammingError.php';
class WidgetTest extends \PHPUnit_Framework_TestCase
{
public function testCreate1()
{
$widgetCreator = new Widget();
$widget = $widgetCreator->create('tab', array('name' => 'TEST'));
$this->assertInstanceOf('Icinga\Web\Widget\Tab', $widget);
}
/**
* @expectedException Icinga\Exception\ProgrammingError
* @expectedExceptionMessage There is no such widget: DOES_NOT_EXIST
*/
public function testFail1()
{
$widgetCreator = new Widget();
$widget = $widgetCreator->create('DOES_NOT_EXIST');
}
}