diff --git a/application/controllers/AuthenticationController.php b/application/controllers/AuthenticationController.php index e0a3e6b8d..d500f23e6 100644 --- a/application/controllers/AuthenticationController.php +++ b/application/controllers/AuthenticationController.php @@ -84,6 +84,8 @@ class AuthenticationController extends ActionController } } + + /** * Action handle logout */ diff --git a/application/controllers/DashboardController.php b/application/controllers/DashboardController.php new file mode 100644 index 000000000..b8ce87acd --- /dev/null +++ b/application/controllers/DashboardController.php @@ -0,0 +1,86 @@ +readConfig(IcingaConfig::app($config)); + return $dashboard; + } + + public function removecomponentAction() + { + $pane = $this->_getParam('pane'); + $dashboard = $this->getDashboard(); + $dashboard->removeComponent( + $pane, + $this->_getParam('component') + )->store(); + + // When the pane doesn't exist anymore, display the default pane + if ($dashboard->isEmptyPane($pane)) { + $this->redirectNow(Url::fromPath('dashboard')); + return; + } + $this->redirectNow(Url::fromPath('dashboard', array('pane' => $pane))); + } + + 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'), '/') + ); + $this->persistDashboard($dashboard); + $this->redirectNow( + Url::fromPath( + 'dashboard', + array( + 'pane' => $form->getValue('pane') + ) + ) + ); + } + } + + private function persistDashboard(Dashboard $dashboard) + { + $dashboard->store(); + } + + public function indexAction() + { + $dashboard = $this->getDashboard(); + + if ($this->_getParam('dashboard')) { + $dashboardName = $this->_getParam('dashboard'); + $dashboard->activate($dashboardName); + } + $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; + } +} + diff --git a/application/controllers/StaticController.php b/application/controllers/StaticController.php index 7e1ebb5ae..bb86e60ef 100644 --- a/application/controllers/StaticController.php +++ b/application/controllers/StaticController.php @@ -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; } } diff --git a/application/forms/dashboard/AddurlForm.php b/application/forms/dashboard/AddurlForm.php new file mode 100644 index 000000000..37cad2e9f --- /dev/null +++ b/application/forms/dashboard/AddurlForm.php @@ -0,0 +1,102 @@ + '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')); + } + + 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"); + } +} + diff --git a/config/dashboard/dashboard.ini b/config/dashboard/dashboard.ini new file mode 100644 index 000000000..15f8994be --- /dev/null +++ b/config/dashboard/dashboard.ini @@ -0,0 +1,11 @@ +[test] +title = "test" +[test.test] +url = "test" + +[test.test2] +url = "test2" + +[test.dsgdgs] +url = "dsdsgdsg" + diff --git a/config/menu.ini b/config/menu.ini index 21e230bcc..b4e42e7b5 100755 --- a/config/menu.ini +++ b/config/menu.ini @@ -1,3 +1,4 @@ [menu] +Dashboard = "/dashboard/index" Configuration = "/configuration/index" diff --git a/doc/form.md b/doc/form.md index 0150ad431..befbdf0d7 100644 --- a/doc/form.md +++ b/doc/form.md @@ -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()* diff --git a/library/Icinga/Config/Config.php b/library/Icinga/Config/Config.php index 74dc64be9..a9f80c12d 100755 --- a/library/Icinga/Config/Config.php +++ b/library/Icinga/Config/Config.php @@ -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; + } + + } diff --git a/library/Icinga/Util/Dimension.php b/library/Icinga/Util/Dimension.php new file mode 100644 index 000000000..f1371e46e --- /dev/null +++ b/library/Icinga/Util/Dimension.php @@ -0,0 +1,114 @@ +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; + } + + /** + * Returns true when the value is > 0 + * + * @return bool + */ + public function isDefined() + { + return $this->value > 0; + } + + /** + * Returns the underlying value without unit information + * + * @return int + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns this value with it's according unit as a string + * + * @return string + */ + public function __toString() + { + if (!$this->isDefined()) { + return ""; + } + return $this->value.$this->unit; + } + + public static function fromString($string) + { + $matches = array(); + if (!preg_match_all('/^ *([0-9]+)(px|pt|em|\%) */i', $string, $matches)) { + throw new InvalidArgumentException($string.' is not a valid dimension'); + } + return new Dimension(intval($matches[1]), $matches[2]); + } +} \ No newline at end of file diff --git a/library/Icinga/Web/ActionController.php b/library/Icinga/Web/ActionController.php index e7c39bf42..0b24384da 100755 --- a/library/Icinga/Web/ActionController.php +++ b/library/Icinga/Web/ActionController.php @@ -237,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); } diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 901910352..253bde3a7 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -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 diff --git a/library/Icinga/Web/Widget/Dashboard.php b/library/Icinga/Web/Widget/Dashboard.php index 15d2fba49..3217194fd 100644 --- a/library/Icinga/Web/Widget/Dashboard.php +++ b/library/Icinga/Web/Widget/Dashboard.php @@ -3,28 +3,30 @@ namespace Icinga\Web\Widget; use Icinga\Application\Icinga; -use Icinga\Config\Config; +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; class Dashboard implements Widget { /** - * @var Config + * @var IcingaConfig; */ - protected $config; - protected $configfile; - protected $panes = array(); - protected $tabs; + private $config; + private $configfile; + private $panes = array(); + private $tabs; - protected $properties = array( - 'url' => null, - 'tabParam' => 'pane' - ); + private $url = null; + private $tabParam = 'pane'; - protected function init() + + public function __construct() { if ($this->url === null) { $this->url = Url::fromRequest()->getUrlWithout($this->tabParam); @@ -33,14 +35,15 @@ class Dashboard implements Widget public function activate($name) { - $this->tabs()->activate($name); + $this->getTabs()->activate($name); } - public function tabs() + public function getTabs() { if ($this->tabs === null) { $this->tabs = new Tabs(); foreach ($this->panes as $key => $pane) { + $this->tabs->add($key, array( 'title' => $pane->getTitle(), 'url' => clone($this->url), @@ -57,29 +60,57 @@ class Dashboard implements Widget return is_writable($this->configfile); } - public function store() + public function store($file = null) { + if ($file === null) { + $file = IcingaConfig::app('dashboard/dashboard')->getConfigFile(); + } + $this->configfile = $file; + if (!$this->isWritable()) { + Logger::error("Tried to persist dashboard to %s, but path is not writeable", $this->configfile); + throw new ConfigurationError('Can\'t persist dashboard'); + } + if (! @file_put_contents($this->configfile, $this->toIni())) { - return false; + $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", $this->configfile, $error); + throw new ConfigurationError('Can\'t persist dashboard'); } else { return $this; } + } - public function readConfig(ZfConfig $config) + public function readConfig(IcingaConfig $config) { - $this->configfile = Icinga::app('dashboard')->getApplicationDir("dashboard"); $this->config = $config; $this->panes = array(); $this->loadConfigPanes(); return $this; } + public function createPane($title) + { + $pane = new Pane($title); + $pane->setTitle($title); + $this->addPane($pane); + + } + 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); @@ -89,16 +120,30 @@ class Dashboard implements Widget return $this; } + public function isEmptyPane($pane) + { + $paneObj = $this->getPane($pane); + if ($paneObj === null) { + return true; + } + $cmps = $paneObj->getComponents(); + return !empty($cmps); + } + 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() + public function getPaneKeyTitleArray() { $list = array(); foreach ($this->panes as $name => $pane) { @@ -127,6 +172,8 @@ class Dashboard implements Widget public function getPane($name) { + if (!isset($this->panes[$name])) + return null; return $this->panes[$name]; } @@ -136,22 +183,31 @@ class Dashboard implements Widget return ''; } - return $this->tabs() . $this->getActivePane(); + return $this->getActivePane()->render($view); + } + + private function setDefaultPane() + { + reset($this->panes); + $active = key($this->panes); + $this->activate($active); + return $active; } public function getActivePane() { - $active = $this->tabs()->getActiveName(); + $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]; } @@ -166,31 +222,20 @@ class Dashboard implements Widget protected function loadConfigPanes() { - $items = $this->config->keys(); + $items = $this->config; $app = Icinga::app(); - foreach ($items as $key => $item) { + 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)); } } + + } } diff --git a/library/Icinga/Web/Widget/Dashboard/Component.php b/library/Icinga/Web/Widget/Dashboard/Component.php index 9c43f36c9..3690e29f7 100644 --- a/library/Icinga/Web/Widget/Dashboard/Component.php +++ b/library/Icinga/Web/Widget/Dashboard/Component.php @@ -2,23 +2,48 @@ 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" * */ -class Component +class Component implements Widget { - protected $url; - protected $title; + private $url; + private $title; + private $width; + private $height; - public function __construct($title, $url) + /** + * @var Pane + */ + private $pane; + + private $template =<<<'EOD' + +
+ {TITLE} + X + +
+ +
+
+EOD; + + + public function __construct($title, $url, Pane $pane) { $this->title = $title; + $this->pane = $pane; if ($url instanceof Url) { $this->url = $url; } else { @@ -26,6 +51,16 @@ class Component } } + public function setWidth(Dimension $width = null) + { + $this->width = $width; + } + + public function setHeight(Dimension $height = null) + { + $this->height = $height; + } + /** * Retrieve this components title * @@ -78,39 +113,74 @@ class Component public function toIni() { - $ini = $this->iniPair('base_url', $this->url->getScript()); + $ini = $this->iniPair('url', $this->url->getRelativeUrl()); foreach ($this->url->getParams() as $key => $val) { $ini .= $this->iniPair($key, $val); } + if ($this->height !== null) { + $ini .= 'height: '.((string) $this->height).'\n'; + } + if ($this->width !== null) { + $ini .= 'width: '.((string) $this->width).'\n'; + } return $ini; } - /** - * Render this components HTML - */ - 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() + ) + ); - $htm = '
' - . "\n" - . '

' - . htmlspecialchars($this->title) - . "

\n" - . '' - . "\n
\n"; - return $htm; + + $html = str_replace("{URL}", $url->getAbsoluteUrl(), $this->template); + $html = str_replace("{REMOVE_URL}", $removeUrl, $html); + $html = str_replace("{STYLE}", $this->getBoxSizeAsCSS(), $html); + $html = str_replace("{TITLE}", $view->escape($this->getTitle()), $html); + return $html; + } + + private function getBoxSizeAsCSS() + { + $style = ""; + if ($this->height) { + $style .= 'height:'.(string) $this->height.';'; + } + if ($this->width) { + $style .= 'width:'.(string) $this->width.';'; + } + } + + 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"]); + } + + 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; } } diff --git a/library/Icinga/Web/Widget/Dashboard/Pane.php b/library/Icinga/Web/Widget/Dashboard/Pane.php index c57aaf5d2..6749da12a 100644 --- a/library/Icinga/Web/Widget/Dashboard/Pane.php +++ b/library/Icinga/Web/Widget/Dashboard/Pane.php @@ -4,8 +4,10 @@ namespace Icinga\Web\Widget\Dashboard; use Icinga\Web\Url; use Icinga\Exception\ConfigurationError; +use Icinga\Web\Widget\Widget; +use Zend_Config; -class Pane +class Pane implements Widget { protected $name; protected $title; @@ -61,13 +63,22 @@ class Pane { return $this->components; } - + + public function render(\Zend_View_Abstract $view) + { + $html = PHP_EOL; + foreach ($this->getComponents() as $component) { + $html .= PHP_EOL.$component->render($view); + } + return $html; + } + 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); + $this->components[$component] = new Component($component, $url, $this); } else{ throw new ConfigurationError('You messed up your dashboard'); } @@ -81,24 +92,28 @@ class Pane 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->quoteIni($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() + 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; } } diff --git a/library/Icinga/Web/Widget/Form.php b/library/Icinga/Web/Widget/Form.php deleted file mode 100644 index 9ee4bb827..000000000 --- a/library/Icinga/Web/Widget/Form.php +++ /dev/null @@ -1,78 +0,0 @@ - - * @author Icinga-Web Team - * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License - */ -class Form -{ - 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 render() - { - return (string) $this->form; - } -} diff --git a/library/Icinga/Web/Widget/Tab.php b/library/Icinga/Web/Widget/Tab.php index 501e358de..0e1fc0f76 100644 --- a/library/Icinga/Web/Widget/Tab.php +++ b/library/Icinga/Web/Widget/Tab.php @@ -41,6 +41,7 @@ class Tab implements Widget private $url = null; private $urlParams = array(); private $icon = null; + private $iconCls = null; /** @@ -59,6 +60,16 @@ class Tab implements Widget return $this->icon; } + public function setIconCls($iconCls) + { + $this->iconCls = $iconCls; + } + + public function getIconCls() + { + return $this->iconCls; + } + /** * @param mixed $name */ @@ -107,6 +118,22 @@ class Tab implements Widget return $this->url; } + /** + * @param mixed $url + */ + public function setUrlParams(array $urlParams) + { + $this->urlParams = $urlParams; + } + + /** + * @return mixed + */ + public function getUrlParams() + { + return $this->urlParams; + } + public function __construct(array $properties = array()) { foreach ($properties as $name=>$value) { @@ -115,18 +142,6 @@ class Tab implements Widget $this->$setter($value); } } - - } - - /** - * Health check at initialization time - * - * @throws Icinga\Exception\ProgrammingError if tab name is missing - * - * @return void - */ - protected function init() - { if ($this->name === null) { throw new ProgrammingError('Cannot create a nameless tab'); } @@ -172,6 +187,8 @@ class Tab implements Widget 'width' => 16, 'height' => 16 )) . ' ' . $caption; + } else if ($this->iconCls !== null) { + $caption = ' ' . $caption; } if ($this->url !== null) { $tab = $view->qlink( diff --git a/library/Icinga/Web/Widget/Tabs.php b/library/Icinga/Web/Widget/Tabs.php index af3fa1f21..69b1b2065 100644 --- a/library/Icinga/Web/Widget/Tabs.php +++ b/library/Icinga/Web/Widget/Tabs.php @@ -197,6 +197,7 @@ class Tabs implements Countable, Widget $html .= $tab->render($view); } + // @TODO: Remove special part from tabs (Bug #4512) $special = array(); $special[] = $view->qlink( $view->img('img/classic/application-pdf.png') . ' PDF', diff --git a/library/vendor/tcpdf/examples/example_059.php b/library/vendor/tcpdf/examples/example_059.php index 6e458d5b7..57870bd3f 100755 --- a/library/vendor/tcpdf/examples/example_059.php +++ b/library/vendor/tcpdf/examples/example_059.php @@ -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] = '
#TOC_DESCRIPTION##TOC_PAGE_NUMBER#
'; $bookmark_templates[1] = '
 #TOC_DESCRIPTION##TOC_PAGE_NUMBER#
'; $bookmark_templates[2] = '
 #TOC_DESCRIPTION##TOC_PAGE_NUMBER#
'; -// 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 diff --git a/library/vendor/tcpdf/tcpdf.php b/library/vendor/tcpdf/tcpdf.php index fccf6b117..c113071a5 100644 --- a/library/vendor/tcpdf/tcpdf.php +++ b/library/vendor/tcpdf/tcpdf.php @@ -76,7 +76,7 @@ // dullus for text Justification. // Bob Vincent (pillarsdotnet@users.sourceforge.net) for
  • value attribute. // Patrick Benny for text stretch suggestion on Cell(). -// Johannes Güntert for JavaScript support. +// Johannes G�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�l Mar�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: or a string containing some or all of the following characters (in any order): 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�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: or a string containing some or all of the following characters (in any order): 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�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�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. * 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�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. * 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�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. * 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�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�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�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�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�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�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�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�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�zier curveto foreach ($params as $ck => $cp) { $params[$ck] = $cp; if (($ck % 2) != 0) { diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 6996fc7eb..46d5cd6ac 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -10,6 +10,7 @@ use Icinga\Web\Widget\Tabs; class Monitoring_ListController extends ModuleActionController { protected $backend; + private $compactView = null; public function init() { @@ -23,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( @@ -192,6 +194,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 '
    '
                     . htmlspecialchars(wordwrap($query->getQuery()->dump()))
    diff --git a/modules/monitoring/application/views/scripts/list/hosts-compact.phtml b/modules/monitoring/application/views/scripts/list/hosts-compact.phtml
    index 644eb9aee..ff0a1bcdd 100644
    --- a/modules/monitoring/application/views/scripts/list/hosts-compact.phtml
    +++ b/modules/monitoring/application/views/scripts/list/hosts-compact.phtml
    @@ -1,64 +1,110 @@
    +
     count();
    -if (! $count) {
    -    echo '- no host is matching this filter -';
    -    return;
    -}
    -
    -$hosts->paginate();
    -
    -?>
    - 
    -  
    -   
    -   
    -  
    - 
    - 
    -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');
     ?>
    -  
    -   
    -   
    -  
    -
    - 
    +
    +paginationControl($hosts, null, null, array('preserve' => $this->preserve)) ?>
    +
    +
    StateHost
    qlink( - $this->timeSince($host->host_last_state_change), - 'monitoring/show/history', - array('host' => $host->host_name), - array('quote' => false)) ?> $alt) - echo $this->img('img/classic/' . $icon, array( - 'class' => 'icon', - 'title' => $alt - )); -?> -qlink( - $host->host_name, - 'monitoring/show/host', - array('host' => $host->host_name), - array('class' => 'row-action') - ) ?>
    + + + + + + + + + + + + + + + + + + + + +
    StatusHostOutput
    +
    start(); ?> + host_icon_image) : ?> + + + end(); ?>
    +
    +
    start(); ?> + host_handled && $host->host_state > 0): ?> + + + + + host_acknowledged && !$host->host_in_downtime): ?> + + + + + host_is_flapping): ?> + + + + + host_notifications_enabled): ?> + + + + + host_in_downtime): ?> + + + + + end(); ?>
    +
    +
    + qlink( + "".ucfirst($viewHelper->monitoringState($host, 'host'))."". + '
    since '. + $this->timeSince($host->host_last_state_change), + 'monitoring/show/history', array( + 'host' => $host->host_name + ), + array('quote' => false) + );?> + host_state_type == 0): ?> + + + + +
    +
    + host_last_comment !== null): ?> + + + + + qlink( + "".$host->host_name."
    ". + "".$host->host_address."", + 'monitoring/show/host', array( + 'host' => $host->host_name + ), array( + 'class' => 'row-action', + 'quote' => false + ) + ); ?> + + + host_action_url != ""): ?> + Action + + + host_notes_url != ""): ?> + Notes + +
    + escape(substr(strip_tags($host->host_output), 0, 500)); ?> +
    diff --git a/modules/monitoring/test/php/testlib/datasource/strategies/StatusdatInsertionStrategy.php b/modules/monitoring/test/php/testlib/datasource/strategies/StatusdatInsertionStrategy.php index 840e83ed1..2d1d1ec6f 100644 --- a/modules/monitoring/test/php/testlib/datasource/strategies/StatusdatInsertionStrategy.php +++ b/modules/monitoring/test/php/testlib/datasource/strategies/StatusdatInsertionStrategy.php @@ -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 * */ diff --git a/public/js/icinga/container.js b/public/js/icinga/container.js index ce9c4031b..ec1dc21bf 100755 --- a/public/js/icinga/container.js +++ b/public/js/icinga/container.js @@ -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) {