Merge branch 'feature/system-settings-4299'

fixes #4299
This commit is contained in:
Marius Hein 2013-06-27 12:46:34 +02:00
commit b585d7702e
11 changed files with 747 additions and 36 deletions

View File

@ -0,0 +1,39 @@
<?php
// @codingStandardsIgnoreStart
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
use \Icinga\Web\ActionController;
/**
* Class ConfigurationController
*/
class ConfigurationController extends ActionController
{
/**
* Index action
*/
public function indexAction()
{
$this->view->tabs = $this->createTabs();
}
/**
* @return \Icinga\Web\Widget
*/
protected function createTabs()
{
$tabs = $this->widget('tabs')->add(
'configuration',
array(
'title' => $this->translate('Overview'),
'url' => 'configuration/index',
)
);
return $tabs;
}
}
// @codingStandardsIgnoreEnd

View File

@ -0,0 +1,11 @@
<?= $this->tabs; ?>
<h2>Configuration</h2>
<p>
This is the configuration over page. Modules can register their handler to
provide own controllers for configuration.
</p>
<p>
Many thanks for your attention!
</p>

View File

@ -1,3 +1,3 @@
[menu] [menu]
Configuration = "/modules/overview" Configuration = "/configuration/index"

154
library/Icinga/Web/Url.php Normal file
View File

@ -0,0 +1,154 @@
<?php
namespace Icinga\Web;
use Icinga\Application\Icinga;
use Icinga\Exception\ProgrammingError;
class Url
{
protected $params = array();
protected $url;
protected $baseUrl;
public function __construct($url, $params = null)
{
if (($split = strpos($url, '?')) === false) {
$this->url = $url;
if (! empty($params)) {
$this->params = $params;
}
} else {
$this->url = substr($url, 0, $split);
parse_str(substr($url, $split + 1), $urlParams);
$this->params = $urlParams;
if (! empty($params)) {
$this->params += $params;
// TODO: Test += behavior!
}
}
}
public static function create($url, $params = null)
{
$u = new Url($url, $params);
return $u;
}
// For tests
public function setBaseUrl($baseUrl)
{
$this->baseUrl = $baseUrl;
return $this;
}
public static function current()
{
$app = Icinga::app();
$view = $app->getView()->view;
$request = $app->frontController()->getRequest();
$parts = array();
// TODO: getQuery!
$params = $request->getParams();
foreach (array('module', 'controller', 'action') as $param) {
if ($view->{$param . '_name'} !== 'default') {
$parts[] = $view->{$param . '_name'};
}
if (array_key_exists($param, $params)) {
unset($params[$param]);
}
}
$rel = implode('/', $parts);
$url = new Url($rel, $params);
return $url;
}
public function getScript()
{
return $this->url;
}
public function getRelative()
{
$params = $args = array();
foreach ($this->params as $name => $value) {
if (is_int($name)) {
$params[] = rawurlencode($value);
} else {
$args[] = rawurlencode($name) . '=' . rawurlencode($value);
}
}
$url = vsprintf($this->url, $params);
if (! empty($args)) {
$url .= '?' . implode('&', $args);
}
return $url;
}
public function addParams($params)
{
$this->params += $params;
return $this;
}
public function setParams($params)
{
$this->params = $params;
return $this;
}
public function set($key, $val)
{
$this->params[$key] = $val;
return $this;
}
public function hasParam($key)
{
return array_key_exists($key, $this->params);
}
public function getParam($key, $default = null)
{
if ($this->hasParam($key)) {
return $this->params[$key];
}
return $default;
}
public function getParams()
{
return $this->params;
}
public function without($keys)
{
if (! is_array($keys)) {
$keys = array($keys);
}
foreach ($keys as $key) {
if (array_key_exists($key, $this->params)) {
unset($this->params[$key]);
}
}
return $this;
}
public function __toString()
{
$url = $this->getRelative();
$base = is_null($this->baseUrl)
? Icinga::app()->getView()->view->baseUrl()
: $this->baseUrl;
if ($base === '') {
// Otherwise all URLs would be relative to wherever you are
$base = '/';
}
if (strlen($base) > 0 && strlen($url) > 0 && $url[0] !== '?') {
$base = rtrim($base, '/') . '/';
}
return $base . $url;
}
}

View File

@ -1,7 +1,8 @@
<?php <?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Web Widget abstract class
*/
namespace Icinga\Web\Widget; namespace Icinga\Web\Widget;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
@ -32,6 +33,8 @@ abstract class AbstractWidget
*/ */
protected static $view; protected static $view;
protected $module_name;
/** /**
* Fill $properties with default values for all your valid widget properties * Fill $properties with default values for all your valid widget properties
* *
@ -62,8 +65,11 @@ abstract class AbstractWidget
* *
* @param array $properties An optional properties array * @param array $properties An optional properties array
*/ */
final public function __construct($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) { foreach ($properties as $key => $val) {
$this->$key = $val; $this->$key = $val;
} }

View File

@ -0,0 +1,195 @@
<?php
namespace Icinga\Web\Widget;
use Icinga\Application\Icinga;
use Icinga\Application\Config;
use Icinga\Web\Widget;
use Icinga\Web\Widget\Dashboard\Pane;
use Icinga\Web\Url;
use Zend_Config as ZfConfig;
class Dashboard extends AbstractWidget
{
protected $config;
protected $configfile;
protected $panes = array();
protected $tabs;
protected $properties = array(
'url' => null,
'tabParam' => 'pane'
);
protected function init()
{
if ($this->url === null) {
$this->url = Url::current()->without($this->tabParam);
}
}
public function activate($name)
{
$this->tabs()->activate($name);
}
public function tabs()
{
if ($this->tabs === null) {
$this->tabs = Widget::create('tabs');
foreach ($this->panes as $key => $pane) {
$this->tabs->add($key, array(
'title' => $pane->getTitle(),
'url' => clone($this->url),
'urlParams' => array($this->tabParam => $key)
));
}
}
return $this->tabs;
}
public function isWritable()
{
return is_writable($this->configfile);
}
public function store()
{
if (! @file_put_contents($this->configfile, $this->toIni())) {
return false;
} else {
return $this;
}
}
public function readConfig(ZfConfig $config)
{
$this->configfile = Config::getInstance()->getConfigDir()
. '/dashboard.ini';
$this->config = $config;
$this->panes = array();
$this->loadConfigPanes();
return $this;
}
public function setComponentUrl($pane, $component, $url)
{
if ($component === null && strpos($pane, '.')) {
list($pane, $component) = preg_split('~\.~', $pane, 2);
}
$pane = $this->getPane($pane);
if ($pane->hasComponent($component)) {
$pane->getComponent($component)->setUrl($url);
} else {
$pane->addComponent($component, $url);
}
return $this;
}
public function removeComponent($pane, $component)
{
if ($component === null && strpos($pane, '.')) {
list($pane, $component) = preg_split('~\.~', $pane, 2);
}
$this->getPane($pane)->removeComponent($component);
return $this;
}
public function paneEnum()
{
$list = array();
foreach ($this->panes as $name => $pane) {
$list[$name] = $pane->getTitle();
}
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;
}
public function addPane(Pane $pane)
{
$this->panes[$pane->getName()] = $pane;
return $this;
}
public function getPane($name)
{
return $this->panes[$name];
}
public function renderAsHtml()
{
if (empty($this->panes)) {
return '';
}
return $this->tabs() . $this->getActivePane();
}
public function getActivePane()
{
$active = $this->tabs()->getActiveName();
if (! $active) {
if ($active = Url::current()->getParam($this->tabParam)) {
$this->activate($active);
} else {
reset($this->panes);
$active = key($this->panes);
$this->activate($active);
}
}
return $this->panes[$active];
}
public function toIni()
{
$ini = '';
foreach ($this->panes as $pane) {
$ini .= $pane->toIni();
}
return $ini;
}
protected function loadConfigPanes()
{
$items = $this->config->dashboard->toArray();
$app = Icinga::app();
foreach ($items as $key => $item) {
if (false === strstr($key, '.')) {
$pane = new Pane($key);
if (isset($item['title'])) {
$pane->setTitle($item['title']);
}
$this->addPane($pane);
} 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::create($base_url, $item)
);
}
}
}
}

View File

@ -0,0 +1,117 @@
<?php
namespace Icinga\Web\Widget\Dashboard;
use Icinga\Web\Url;
/**
* A dashboard pane component
*
* Needs a title and an URL
* // TODO: Rename to "Dashboardlet"
*
*/
class Component
{
protected $url;
protected $title;
public function __construct($title, $url)
{
$this->title = $title;
if ($url instanceof Url) {
$this->url = $url;
} else {
$this->url = Url::create($url);
}
}
/**
* Retrieve this components title
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Retrieve my url
*
* @return Url
*/
public function getUrl()
{
return $this->url;
}
/**
* Set this components URL
*
* @param string|Url $url Component URL
* @return self
*/
public function setUrl($url)
{
if ($url instanceof Url) {
$this->url = $url;
} else {
$this->url = Url::create($url);
}
return $this;
}
protected function iniPair($key, $val)
{
return sprintf(
"%s = %s\n",
$key,
$this->quoteIni($val)
);
}
protected function quoteIni($str)
{
return '"' . $str . '"';
}
public function toIni()
{
$ini = $this->iniPair('base_url', $this->url->getScript());
foreach ($this->url->getParams() as $key => $val) {
$ini .= $this->iniPair($key, $val);
}
return $ini;
}
/**
* Render this components HTML
*/
public function __toString()
{
$url = clone($this->url);
$url->addParams(array('view' => 'compact'));
if (isset($_GET['layout'])) {
$url->addParams(array('layout' => $_GET['layout']));
}
$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;
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace Icinga\Web\Widget\Dashboard;
use Icinga\Web\Url;
use Icinga\Exception\ConfigurationError;
class Pane
{
protected $name;
protected $title;
protected $components = array();
public function __construct($name)
{
$this->name = $name;
$this->title = $name;
}
public function getName()
{
return $this->name;
}
public function getTitle()
{
return $this->title;
}
public function setTitle($title)
{
$this->title = $title;
return $this;
}
public function hasComponent($title)
{
return array_key_exists($title, $this->components);
}
public function getComponent($title)
{
if ($this->hasComponent($title)) {
return $this->components[$title];
}
throw new ProgrammingError(sprintf(
'Trying to access invalid component: %s',
$title
));
}
public function removeComponent($title)
{
if ($this->hasComponent($title)) {
unset($this->components[$title]);
}
return $this;
}
public function getComponents()
{
return $this->components;
}
public function addComponent($component, $url = null)
{
if ($component instanceof Component) {
$this->components[$component->title] = $component;
} elseif (is_string($component) && $url !== null) {
$this->components[$component] = new Component($component, $url);
} else{
throw new ConfigurationError('You messed up your dashboard');
}
return $this;
}
protected function quoteIni($str)
{
return '"' . $str . '"';
}
public function toIni()
{
$ini = sprintf(
"[%s]\ntitle = %s\n",
$this->getName(),
$this->quoteIni($this->getTitle())
) . "\n";
foreach ($this->components as $title => $component) {
$ini .= sprintf(
"[%s.%s]\n",
$this->getName(),
$title
) . $component->toIni() . "\n";
}
return $ini;
}
public function __toString()
{
return implode('', $this->components);
}
}

View File

@ -1,22 +1,26 @@
<?php <?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Form
*/
namespace Icinga\Web\Widget; namespace Icinga\Web\Widget;
use Icinga\Exception\ProgrammingError;
use Icinga\Application\Icinga;
/** /**
* A form loader... * A form loader...
* *
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org> * @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
* @author 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 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
* @deprecated Because of HTML creation of PHP<
*/ */
class Form extends AbstractWidget class Form extends AbstractWidget
{ {
protected $form; protected $form;
protected $properties = array( protected $properties = array(
'name' => null 'name' => null,
'options' => null
); );
public function __call($func, $args) public function __call($func, $args)
@ -26,14 +30,48 @@ class Form extends AbstractWidget
protected function init() protected function init()
{ {
// Load form by name given in props? // Load form by name given in props:
$class = 'Icinga\\Web\\Form\\' . ucfirst($this->name) . 'Form'; $file = null;
$file = ICINGA_APPDIR $fparts = array();
. '/forms/authentication/' $cparts = array();
. ucfirst($this->name) foreach (preg_split('~/~', $this->name, -1, PREG_SPLIT_NO_EMPTY) as $part) {
. 'Form.php'; $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->moduleManager()->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); require_once($file);
$this->form = new $class; $this->form = new $class($this->options);
} }
public function renderAsHtml() public function renderAsHtml()
@ -41,3 +79,4 @@ class Form extends AbstractWidget
return (string) $this->form; return (string) $this->form;
} }
} }

View File

@ -1,7 +1,8 @@
<?php <?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Single tab
*/
namespace Icinga\Web\Widget; namespace Icinga\Web\Widget;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
@ -22,7 +23,6 @@ use Icinga\Exception\ProgrammingError;
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org> * @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
* @author 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 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
* @deprecated Because of HTML creation of PHP<
*/ */
class Tab extends AbstractWidget class Tab extends AbstractWidget
{ {
@ -39,16 +39,18 @@ class Tab extends AbstractWidget
* @var array * @var array
*/ */
protected $properties = array( protected $properties = array(
'name' => null, 'name' => null,
'title' => '', 'title' => '',
'url' => null, 'url' => null,
'urlParams' => array(), 'urlParams' => array(),
'icon' => null, 'icon' => null,
); );
/** /**
* Health check at initialization time * Health check at initialization time
* @throws \Icinga\Exception\ProgrammingError if tab name is missing *
* @throws Icinga\Exception\ProgrammingError if tab name is missing
*
* @return void * @return void
*/ */
protected function init() protected function init()
@ -72,7 +74,7 @@ class Tab extends AbstractWidget
*/ */
public function setActive($active = true) public function setActive($active = true)
{ {
$this->active = (bool)$active; $this->active = (bool) $active;
return $this; return $this;
} }
@ -97,15 +99,10 @@ class Tab extends AbstractWidget
$class = $this->isActive() ? ' class="active"' : ''; $class = $this->isActive() ? ' class="active"' : '';
$caption = $this->title; $caption = $this->title;
if ($this->icon !== null) { if ($this->icon !== null) {
$caption = $view->img( $caption = $view->img($this->icon, array(
$this->icon, 'width' => 16,
array( 'height' => 16
'width' => 16, )) . ' ' . $caption;
'height' => 16
)
)
. ' '
. $caption;
} }
if ($this->url !== null) { if ($this->url !== null) {
$tab = $view->qlink( $tab = $view->qlink(

View File

@ -1,10 +1,12 @@
<?php <?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Navigation tabs
*/
namespace Icinga\Web\Widget; namespace Icinga\Web\Widget;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
use Icinga\Web\Url;
/** /**
* Navigation tab widget * Navigation tab widget
@ -14,7 +16,6 @@ use Icinga\Exception\ProgrammingError;
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org> * @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
* @author 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 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
* @deprecated Because of HTML creation of PHP<
*/ */
class Tabs extends AbstractWidget class Tabs extends AbstractWidget
{ {
@ -39,6 +40,8 @@ class Tabs extends AbstractWidget
*/ */
protected $tab_class = 'nav-tabs'; protected $tab_class = 'nav-tabs';
protected $specialActions = false;
/** /**
* Activate the tab with the given name * Activate the tab with the given name
* *
@ -172,6 +175,12 @@ class Tabs extends AbstractWidget
return $this; return $this;
} }
public function enableSpecialActions()
{
$this->specialActions = true;
return $this;
}
/** /**
* This is where the tabs are going to be rendered * This is where the tabs are going to be rendered
* *
@ -189,6 +198,45 @@ class Tabs extends AbstractWidget
foreach ($this->tabs as $tab) { foreach ($this->tabs as $tab) {
$html .= $tab; $html .= $tab;
} }
$special = array();
$special[] = $this->view()->qlink(
'PDF',
Url::current(),
array('filetype' => 'pdf'),
array('target' => '_blank')
);
$special[] = $this->view()->qlink(
'Basket',
Url::create('basket/add'),
array('url' => Url::current()->getRelative())
);
$special[] = $this->view()->qlink(
'Dashboard',
Url::create('dashboard/addurl'),
array('url' => Url::current()->getRelative())
);
// @todo rework auth
// $auth = Auth::getInstance();
// if ($this->specialActions && ! empty($special) && $auth->isAuthenticated() && $auth->getUsername() === 'admin') {
if ($this->specialActions) {
$html .= '
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><b class="caret"></b></a>
<ul class="dropdown-menu">
';
foreach ($special as $shtml) {
$html .= '<li>' . $shtml . "</li>\n";
}
$html .= ' </ul>
</li>
';
}
$html .= "</ul>\n"; $html .= "</ul>\n";
return $html; return $html;
} }