mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-04-08 17:15:08 +02:00
Merge branch 'master' into bugfix/downtimes-with-non-problem-state-time-caption-9266
This commit is contained in:
commit
01ec2cd590
@ -77,7 +77,21 @@ class DashletForm extends Form
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('Dashlet Title'),
|
||||
'description' => $this->translate('Enter a title for the dashlet.')
|
||||
'description' => $this->translate('Enter a title for the dashlet.'),
|
||||
'validators' => array(
|
||||
array(
|
||||
'Regex',
|
||||
false,
|
||||
array(
|
||||
'pattern' => '/^[^\\[\\]]+$/',
|
||||
'messages' => array(
|
||||
'regexNotMatch' => $this->translate(
|
||||
'The name cannot contain \'[\' or \']\'.'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
|
@ -359,11 +359,7 @@ class Config implements Countable, Iterator, Selectable
|
||||
*/
|
||||
protected function getIniWriter($filePath = null, $fileMode = null)
|
||||
{
|
||||
return new IniWriter(array(
|
||||
'config' => $this,
|
||||
'filename' => $filePath,
|
||||
'filemode' => $fileMode
|
||||
));
|
||||
return new IniWriter($this, $filePath, $fileMode);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -418,7 +414,6 @@ class Config implements Countable, Iterator, Selectable
|
||||
static::resolvePath('modules/' . $modulename . '/' . $configname . '.ini')
|
||||
);
|
||||
}
|
||||
|
||||
return $moduleConfigs[$configname];
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,9 @@ namespace Icinga\Application;
|
||||
|
||||
require_once dirname(__FILE__) . '/ApplicationBootstrap.php';
|
||||
|
||||
use Icinga\Web\Request;
|
||||
use Icinga\Web\Response;
|
||||
|
||||
/**
|
||||
* Use this if you want to make use of Icinga functionality in other web projects
|
||||
*
|
||||
@ -16,6 +19,40 @@ require_once dirname(__FILE__) . '/ApplicationBootstrap.php';
|
||||
*/
|
||||
class EmbeddedWeb extends ApplicationBootstrap
|
||||
{
|
||||
/**
|
||||
* Request object
|
||||
*
|
||||
* @var Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Response
|
||||
*
|
||||
* @var Response
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* Get the request
|
||||
*
|
||||
* @return Request
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Embedded bootstrap parts
|
||||
*
|
||||
@ -26,10 +63,34 @@ class EmbeddedWeb extends ApplicationBootstrap
|
||||
{
|
||||
return $this
|
||||
->setupZendAutoloader()
|
||||
->loadConfig()
|
||||
->setupErrorHandling()
|
||||
->loadConfig()
|
||||
->setupRequest()
|
||||
->setupResponse()
|
||||
->setupTimezone()
|
||||
->setupModuleManager()
|
||||
->loadEnabledModules();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the request
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function setupRequest()
|
||||
{
|
||||
$this->request = new Request();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the response
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function setupResponse()
|
||||
{
|
||||
$this->response = new Response();
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -162,6 +162,20 @@ class Module
|
||||
*/
|
||||
private $app;
|
||||
|
||||
/**
|
||||
* The CSS/LESS files this module provides
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cssFiles = array();
|
||||
|
||||
/**
|
||||
* The Javascript files this module provides
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $jsFiles = array();
|
||||
|
||||
/**
|
||||
* Routes to add to the route chain
|
||||
*
|
||||
@ -388,6 +402,19 @@ class Module
|
||||
return $manager->getModule($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an additional CSS/LESS file
|
||||
*
|
||||
* @param string $path The path to the file, relative to self::$cssdir
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function provideCssFile($path)
|
||||
{
|
||||
$this->cssFiles[] = $this->cssdir . DIRECTORY_SEPARATOR . $path;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if module provides css
|
||||
*
|
||||
@ -395,7 +422,12 @@ class Module
|
||||
*/
|
||||
public function hasCss()
|
||||
{
|
||||
return file_exists($this->getCssFilename());
|
||||
if (file_exists($this->getCssFilename())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->launchConfigScript();
|
||||
return !empty($this->cssFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -408,6 +440,32 @@ class Module
|
||||
return $this->cssdir . '/module.less';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the CSS/LESS files this module provides
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCssFiles()
|
||||
{
|
||||
$this->launchConfigScript();
|
||||
$files = $this->cssFiles;
|
||||
$files[] = $this->getCssFilename();
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an additional Javascript file
|
||||
*
|
||||
* @param string $path The path to the file, relative to self::$jsdir
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function provideJsFile($path)
|
||||
{
|
||||
$this->jsFiles[] = $this->jsdir . DIRECTORY_SEPARATOR . $path;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if module provides js
|
||||
*
|
||||
@ -415,7 +473,12 @@ class Module
|
||||
*/
|
||||
public function hasJs()
|
||||
{
|
||||
return file_exists($this->getJsFilename());
|
||||
if (file_exists($this->getJsFilename())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->launchConfigScript();
|
||||
return !empty($this->jsFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -428,6 +491,19 @@ class Module
|
||||
return $this->jsdir . '/module.js';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Javascript files this module provides
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJsFiles()
|
||||
{
|
||||
$this->launchConfigScript();
|
||||
$files = $this->jsFiles;
|
||||
$files[] = $this->getJsFilename();
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the module name
|
||||
*
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
namespace Icinga\Application;
|
||||
|
||||
require_once __DIR__ . '/ApplicationBootstrap.php';
|
||||
require_once __DIR__ . '/EmbeddedWeb.php';
|
||||
|
||||
use Zend_Controller_Action_HelperBroker;
|
||||
use Zend_Controller_Front;
|
||||
@ -11,14 +11,11 @@ use Zend_Controller_Router_Route;
|
||||
use Zend_Layout;
|
||||
use Zend_Paginator;
|
||||
use Zend_View_Helper_PaginationControl;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\User;
|
||||
use Icinga\Util\TimezoneDetect;
|
||||
use Icinga\Util\Translator;
|
||||
use Icinga\Web\Notification;
|
||||
use Icinga\Web\Request;
|
||||
use Icinga\Web\Response;
|
||||
use Icinga\Web\Session;
|
||||
use Icinga\Web\Session\Session as BaseSession;
|
||||
use Icinga\Web\View;
|
||||
@ -28,11 +25,11 @@ use Icinga\Web\View;
|
||||
*
|
||||
* Usage example:
|
||||
* <code>
|
||||
* use Icinga\Application\EmbeddedWeb;
|
||||
* EmbeddedWeb::start();
|
||||
* use Icinga\Application\Web;
|
||||
* Web::start();
|
||||
* </code>
|
||||
*/
|
||||
class Web extends ApplicationBootstrap
|
||||
class Web extends EmbeddedWeb
|
||||
{
|
||||
/**
|
||||
* View object
|
||||
@ -48,20 +45,6 @@ class Web extends ApplicationBootstrap
|
||||
*/
|
||||
private $frontController;
|
||||
|
||||
/**
|
||||
* Request object
|
||||
*
|
||||
* @var Request
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* Response
|
||||
*
|
||||
* @var Response
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* Session object
|
||||
*
|
||||
@ -145,26 +128,6 @@ class Web extends ApplicationBootstrap
|
||||
return $this->frontController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request
|
||||
*
|
||||
* @return Request
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for view
|
||||
*
|
||||
@ -211,7 +174,7 @@ class Web extends ApplicationBootstrap
|
||||
$auth = Auth::getInstance();
|
||||
if ($auth->isAuthenticated()) {
|
||||
$user = $auth->getUser();
|
||||
$this->request->setUser($user);
|
||||
$this->getRequest()->setUser($user);
|
||||
$this->user = $user;
|
||||
}
|
||||
return $this;
|
||||
@ -239,28 +202,6 @@ class Web extends ApplicationBootstrap
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the request
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
private function setupRequest()
|
||||
{
|
||||
$this->request = new Request();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the response
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function setupResponse()
|
||||
{
|
||||
$this->response = new Response();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate front controller
|
||||
*
|
||||
@ -269,7 +210,7 @@ class Web extends ApplicationBootstrap
|
||||
private function setupFrontController()
|
||||
{
|
||||
$this->frontController = Zend_Controller_Front::getInstance();
|
||||
$this->frontController->setRequest($this->request);
|
||||
$this->frontController->setRequest($this->getRequest());
|
||||
$this->frontController->setControllerDirectory($this->getApplicationDir('/controllers'));
|
||||
$this->frontController->setParams(
|
||||
array(
|
||||
|
@ -47,16 +47,6 @@ class DbQuery extends SimpleQuery
|
||||
*/
|
||||
protected $useSubqueryCount = false;
|
||||
|
||||
/**
|
||||
* Set the count maximum
|
||||
*
|
||||
* If the count maximum is set, count queries will not count more than that many rows. You should set this
|
||||
* property only for really heavy queries.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $maxCount;
|
||||
|
||||
/**
|
||||
* Count query result
|
||||
*
|
||||
@ -334,14 +324,15 @@ class DbQuery extends SimpleQuery
|
||||
// TODO: there may be situations where we should clone the "select"
|
||||
$count = $this->dbSelect();
|
||||
$this->applyFilterSql($count);
|
||||
if ($this->useSubqueryCount || $this->group) {
|
||||
$group = $this->getGroup();
|
||||
if ($this->useSubqueryCount || $group) {
|
||||
$count->columns($this->columns);
|
||||
if ($group) {
|
||||
$count->group($group);
|
||||
}
|
||||
$columns = array('cnt' => 'COUNT(*)');
|
||||
return $this->db->select()->from($count, $columns);
|
||||
}
|
||||
if ($this->maxCount !== null) {
|
||||
return $this->db->select()->from($count->limit($this->maxCount));
|
||||
}
|
||||
|
||||
$count->columns(array('cnt' => 'COUNT(*)'));
|
||||
return $count;
|
||||
|
@ -143,6 +143,16 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator
|
||||
return $this->ds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current position of this query's iterator
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getIteratorPosition()
|
||||
{
|
||||
return $this->iteratorPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start or rewind the iteration
|
||||
*/
|
||||
|
20
library/Icinga/File/Ini/Dom/Comment.php
Normal file
20
library/Icinga/File/Ini/Dom/Comment.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\File\Ini\Dom;
|
||||
|
||||
class Comment
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $content;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return ';' . $this->content;
|
||||
}
|
||||
}
|
97
library/Icinga/File/Ini/Dom/Directive.php
Normal file
97
library/Icinga/File/Ini/Dom/Directive.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\File\Ini\Dom;
|
||||
|
||||
class Directive
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $commentsPre;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $commentPost;
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct($key)
|
||||
{
|
||||
$this->key = trim($key);
|
||||
if (strlen($this->key) < 1) {
|
||||
throw new Exception(sprintf('Ini parser error: empty key.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getKey()
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->value = trim($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$str = '';
|
||||
if (! empty ($this->commentsPre)) {
|
||||
$comments = array();
|
||||
foreach ($this->commentsPre as $comment) {
|
||||
$comments[] = $comment->render();
|
||||
}
|
||||
$str = implode(PHP_EOL, $comments) . PHP_EOL;
|
||||
}
|
||||
$str .= sprintf('%s = "%s"', $this->sanitizeKey($this->key), $this->sanitizeValue($this->value));
|
||||
if (isset ($this->commentPost)) {
|
||||
$str .= ' ' . $this->commentPost->render();
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
protected function sanitizeKey($str)
|
||||
{
|
||||
return trim(str_replace(PHP_EOL, ' ', $str));
|
||||
}
|
||||
|
||||
protected function sanitizeValue($str)
|
||||
{
|
||||
$str = trim($str);
|
||||
$str = str_replace('\\', '\\\\', $str);
|
||||
$str = str_replace('"', '\\"', $str);
|
||||
return str_replace(PHP_EOL, ' ', $str);
|
||||
}
|
||||
}
|
82
library/Icinga/File/Ini/Dom/Document.php
Normal file
82
library/Icinga/File/Ini/Dom/Document.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\File\Ini\Dom;
|
||||
|
||||
class Document
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $sections = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $commentsDangling;
|
||||
|
||||
/**
|
||||
* @param Section $section
|
||||
*/
|
||||
public function addSection(Section $section)
|
||||
{
|
||||
$this->sections[$section->getName()] = $section;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasSection($name)
|
||||
{
|
||||
return isset($this->sections[trim($name)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return Section
|
||||
*/
|
||||
public function getSection($name)
|
||||
{
|
||||
return $this->sections[trim($name)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param Section $section
|
||||
*
|
||||
* @return Section
|
||||
*/
|
||||
public function setSection($name, Section $section)
|
||||
{
|
||||
return $this->sections[trim($name)] = $section;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function removeSection($name)
|
||||
{
|
||||
unset ($this->sections[trim($name)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$sections = array();
|
||||
foreach ($this->sections as $section) {
|
||||
$sections []= $section->render();
|
||||
}
|
||||
$str = implode(PHP_EOL, $sections);
|
||||
if (! empty($this->commentsDangling)) {
|
||||
foreach ($this->commentsDangling as $comment) {
|
||||
$str .= PHP_EOL . $comment->render();
|
||||
}
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
}
|
118
library/Icinga/File/Ini/Dom/Section.php
Normal file
118
library/Icinga/File/Ini/Dom/Section.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\File\Ini\Dom;
|
||||
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
|
||||
class Section
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $directives = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $commentsPre;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $commentPost;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function __construct($name)
|
||||
{
|
||||
$this->name = trim($name);
|
||||
if (strlen($this->name) < 1) {
|
||||
throw new ConfigurationError(sprintf('Ini file error: empty section identifier'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Directive $directive
|
||||
*/
|
||||
public function addDirective(Directive $directive)
|
||||
{
|
||||
$this->directives[$directive->getKey()] = $directive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*/
|
||||
public function removeDirective($key)
|
||||
{
|
||||
unset ($this->directives[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDirective($key)
|
||||
{
|
||||
return isset($this->directives[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key string
|
||||
*
|
||||
* @return Directive
|
||||
*/
|
||||
public function getDirective($key)
|
||||
{
|
||||
return $this->directives[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$dirs = '';
|
||||
$i = 0;
|
||||
foreach ($this->directives as $directive) {
|
||||
$dirs .= (($i++ > 0 && ! empty($directive->commentsPre)) ? PHP_EOL : '') . $directive->render() . PHP_EOL;
|
||||
}
|
||||
$cms = '';
|
||||
if (! empty($this->commentsPre)) {
|
||||
foreach ($this->commentsPre as $comment) {
|
||||
$comments[] = $comment->render();
|
||||
}
|
||||
$cms = implode(PHP_EOL, $comments) . PHP_EOL;
|
||||
}
|
||||
$post = '';
|
||||
if (isset($this->commentPost)) {
|
||||
$post = ' ' . $this->commentPost->render();
|
||||
}
|
||||
return $cms . sprintf('[%s]', $this->sanitize($this->name)) . $post . PHP_EOL . $dirs;
|
||||
}
|
||||
|
||||
protected function sanitize($str)
|
||||
{
|
||||
$str = trim($str);
|
||||
$str = str_replace('\\', '\\\\', $str);
|
||||
$str = str_replace('"', '\\"', $str);
|
||||
$str = str_replace(']', '\\]', $str);
|
||||
$str = str_replace(';', '\\;', $str);
|
||||
return str_replace(PHP_EOL, ' ', $str);
|
||||
}
|
||||
}
|
@ -1,625 +0,0 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\File\Ini;
|
||||
|
||||
/**
|
||||
* Edit the sections and keys of an ini in-place
|
||||
*/
|
||||
class IniEditor
|
||||
{
|
||||
/**
|
||||
* The text that is edited
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $text;
|
||||
|
||||
/**
|
||||
* The symbol that is used to separate keys
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $nestSeparator = '.';
|
||||
|
||||
/**
|
||||
* The indentation level of the comments
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $commentIndentation;
|
||||
|
||||
/**
|
||||
* The indentation level of the values
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $valueIndentation;
|
||||
|
||||
/**
|
||||
* The number of new lines between sections
|
||||
*
|
||||
* @var number
|
||||
*/
|
||||
private $sectionSeparators;
|
||||
|
||||
/**
|
||||
* Create a new IniEditor
|
||||
*
|
||||
* @param string $content The content of the ini as string
|
||||
* @param array $options Optional formatting options used when changing the ini file
|
||||
* * valueIndentation: The indentation level of the values
|
||||
* * commentIndentation: The indentation level of the comments
|
||||
* * sectionSeparators: The amount of newlines between sections
|
||||
*/
|
||||
public function __construct(
|
||||
$content,
|
||||
array $options = array()
|
||||
) {
|
||||
$this->text = explode(PHP_EOL, $content);
|
||||
$this->valueIndentation = array_key_exists('valueIndentation', $options)
|
||||
? $options['valueIndentation'] : 19;
|
||||
$this->commentIndentation = array_key_exists('commentIndentation', $options)
|
||||
? $options['commentIndentation'] : 43;
|
||||
$this->sectionSeparators = array_key_exists('sectionSeparators', $options)
|
||||
? $options['sectionSeparators'] : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the given key.
|
||||
*
|
||||
* @param array $key The key to set
|
||||
* @param string $value The value to set
|
||||
* @param array $section The section to insert to.
|
||||
*/
|
||||
public function set(array $key, $value, $section = null)
|
||||
{
|
||||
$line = $this->getKeyLine($key, $section);
|
||||
if ($line === -1) {
|
||||
$this->insert($key, $value, $section);
|
||||
} else {
|
||||
$content = $this->formatKeyValuePair($key, $value);
|
||||
$this->updateLine($line, $content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the value of the given array element
|
||||
*
|
||||
* @param array $key The key of the array value
|
||||
* @param array $section The section of the array.
|
||||
*/
|
||||
public function resetArrayElement(array $key, $section = null)
|
||||
{
|
||||
$line = $this->getArrayElement($key, $section);
|
||||
if ($line !== -1) {
|
||||
$this->deleteLine($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value for an array element
|
||||
*
|
||||
* @param array $key The key of the property
|
||||
* @param string $value The value of the property
|
||||
* @param array $section The section to use
|
||||
*/
|
||||
public function setArrayElement(array $key, $value, $section = null)
|
||||
{
|
||||
$line = $this->getArrayElement($key, $section);
|
||||
if ($line !== -1) {
|
||||
if (isset($section)) {
|
||||
$this->updateLine($line, $this->formatKeyValuePair($key, $value));
|
||||
} else {
|
||||
/*
|
||||
* Move into new section to avoid ambiguous configurations
|
||||
*/
|
||||
$section = $key[0];
|
||||
unset($key[0]);
|
||||
$this->deleteLine($line);
|
||||
$this->setSection($section);
|
||||
$this->insert($key, $value, $section);
|
||||
}
|
||||
} else {
|
||||
$this->insert($key, $value, $section);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the line of an array element
|
||||
*
|
||||
* @param array $key The key of the property.
|
||||
* @param mixed $section The section to use
|
||||
*
|
||||
* @return int The line of the array element.
|
||||
*/
|
||||
private function getArrayElement(array $key, $section = null)
|
||||
{
|
||||
$line = isset($section) ? $this->getSectionLine($section) + 1 : 0;
|
||||
$index = array_pop($key);
|
||||
$formatted = $this->formatKey($key);
|
||||
for (; $line < count($this->text); $line++) {
|
||||
$l = $this->text[$line];
|
||||
if ($this->isSectionDeclaration($l)) {
|
||||
return -1;
|
||||
}
|
||||
if (preg_match('/^\s*' . $formatted . '\[\]\s*=/', $l) === 1) {
|
||||
return $line;
|
||||
}
|
||||
if ($this->isPropertyDeclaration($l, array_merge($key, array($index)))) {
|
||||
return $line;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* When it exists, set the key back to null
|
||||
*
|
||||
* @param array $key The key to reset
|
||||
* @param array $section The section of the key
|
||||
*/
|
||||
public function reset(array $key, $section = null)
|
||||
{
|
||||
$line = $this->getKeyLine($key, $section);
|
||||
if ($line === -1) {
|
||||
return;
|
||||
}
|
||||
$this->deleteLine($line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the section if it does not exist and set the properties
|
||||
*
|
||||
* @param string $section The section name
|
||||
* @param array $extend The section that should be extended by this section
|
||||
*/
|
||||
public function setSection($section, $extend = null)
|
||||
{
|
||||
if (isset($extend)) {
|
||||
$decl = '[' . $section . ' : ' . $extend . ']';
|
||||
} else {
|
||||
$decl = '[' . $section . ']';
|
||||
}
|
||||
$line = $this->getSectionLine($section);
|
||||
if ($line !== -1) {
|
||||
$this->deleteLine($line);
|
||||
$this->insertAtLine($line, $decl);
|
||||
} else {
|
||||
$line = $this->getLastLine();
|
||||
$this->insertAtLine($line, $decl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the section order of the ini file
|
||||
*
|
||||
* @param array $order An array containing the section names in the new order
|
||||
* Example: array(0 => 'FirstSection', 1 => 'SecondSection')
|
||||
*/
|
||||
public function refreshSectionOrder(array $order)
|
||||
{
|
||||
$sections = $this->createSectionMap($this->text);
|
||||
/*
|
||||
* Move section-less properties to the start of the ordered text
|
||||
*/
|
||||
$orderedText = array();
|
||||
foreach ($sections['[section-less]'] as $line) {
|
||||
array_push($orderedText, $line);
|
||||
}
|
||||
/*
|
||||
* Reorder the sections
|
||||
*/
|
||||
$len = count($order);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
if (array_key_exists($i, $order)) {
|
||||
/*
|
||||
* Append the lines of the section to the end of the
|
||||
* ordered text
|
||||
*/
|
||||
foreach ($sections[$order[$i]] as $line) {
|
||||
array_push($orderedText, $line);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->text = $orderedText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a map of sections to lines of a given ini file
|
||||
*
|
||||
* @param array $text The text split up in lines
|
||||
*
|
||||
* @return array $sectionMap A map containing all sections as arrays of lines. The array of section-less
|
||||
* lines will be available using they key '[section-less]' which is no valid
|
||||
* section declaration because it contains brackets.
|
||||
*/
|
||||
private function createSectionMap($text)
|
||||
{
|
||||
$sections = array('[section-less]' => array());
|
||||
$section = '[section-less]';
|
||||
$len = count($text);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
if ($this->isSectionDeclaration($text[$i])) {
|
||||
$newSection = $this->getSectionFromDeclaration($this->text[$i]);
|
||||
$sections[$newSection] = array();
|
||||
|
||||
/*
|
||||
* Remove comments 'glued' to the new section from the old
|
||||
* section array and put them into the new section.
|
||||
*/
|
||||
$j = $i - 1;
|
||||
$comments = array();
|
||||
while ($j >= 0 && $this->isComment($this->text[$j])) {
|
||||
array_push($comments, array_pop($sections[$section]));
|
||||
$j--;
|
||||
}
|
||||
$comments = array_reverse($comments);
|
||||
foreach ($comments as $comment) {
|
||||
array_push($sections[$newSection], $comment);
|
||||
}
|
||||
|
||||
$section = $newSection;
|
||||
}
|
||||
array_push($sections[$section], $this->text[$i]);
|
||||
}
|
||||
return $sections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the section name from a section declaration
|
||||
*
|
||||
* @param String $declaration The section declaration
|
||||
*
|
||||
* @return string The section name
|
||||
*/
|
||||
private function getSectionFromDeclaration($declaration)
|
||||
{
|
||||
$tmp = preg_split('/(\[|\]|:)/', $declaration);
|
||||
return trim($tmp[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a section declaration
|
||||
*
|
||||
* @param string $section The section name
|
||||
*/
|
||||
public function removeSection($section)
|
||||
{
|
||||
$line = $this->getSectionLine($section);
|
||||
if ($line !== -1) {
|
||||
$this->deleteLine($line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the key at the end of the corresponding section
|
||||
*
|
||||
* @param array $key The key to insert
|
||||
* @param mixed $value The value to insert
|
||||
* @param array $section The key to insert
|
||||
*/
|
||||
private function insert(array $key, $value, $section = null)
|
||||
{
|
||||
$line = $this->getSectionEnd($section);
|
||||
$content = $this->formatKeyValuePair($key, $value);
|
||||
$this->insertAtLine($line, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the edited text
|
||||
*
|
||||
* @return string The edited text
|
||||
*/
|
||||
public function getText()
|
||||
{
|
||||
$this->normalizeSectionSpacing();
|
||||
|
||||
// trim leading and trailing whitespaces from generated file
|
||||
$txt = trim(implode(PHP_EOL, $this->text)) . PHP_EOL;
|
||||
|
||||
// replace linebreaks, unless they preceed a comment or a section
|
||||
return preg_replace("/\n[\n]*([^;\[])/", "\n$1", $txt);
|
||||
}
|
||||
|
||||
/**
|
||||
* normalize section spacing according to the current settings
|
||||
*/
|
||||
private function normalizeSectionSpacing()
|
||||
{
|
||||
$i = count($this->text) - 1;
|
||||
for (; $i > 0; $i--) {
|
||||
$line = $this->text[$i];
|
||||
if ($this->isSectionDeclaration($line) && $i > 0) {
|
||||
$i--;
|
||||
$line = $this->text[$i];
|
||||
// ignore comments that are glued to the section declaration
|
||||
while ($i > 0 && $this->isComment($line)) {
|
||||
$i--;
|
||||
$line = $this->text[$i];
|
||||
}
|
||||
// remove whitespaces between the sections
|
||||
while ($i > 0 && preg_match('/^\s*$/', $line) === 1) {
|
||||
$this->deleteLine($i);
|
||||
$i--;
|
||||
$line = $this->text[$i];
|
||||
}
|
||||
// refresh section separators
|
||||
if ($i !== 0 && $this->sectionSeparators > 0) {
|
||||
$this->insertAtLine($i + 1, str_repeat(PHP_EOL, $this->sectionSeparators - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the text at line $lineNr
|
||||
*
|
||||
* @param $lineNr The line nr the inserted line should have
|
||||
* @param $toInsert The text that will be inserted
|
||||
*/
|
||||
private function insertAtLine($lineNr, $toInsert)
|
||||
{
|
||||
$this->text = IniEditor::insertIntoArray($this->text, $lineNr, $toInsert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the line $lineNr
|
||||
*
|
||||
* @param int $lineNr The line number of the target line
|
||||
* @param string $content The new line content
|
||||
*/
|
||||
private function updateLine($lineNr, $content)
|
||||
{
|
||||
$comment = $this->getComment($this->text[$lineNr]);
|
||||
$comment = trim($comment);
|
||||
if (strlen($comment) > 0) {
|
||||
$comment = ' ; ' . $comment;
|
||||
$content = str_pad($content, $this->commentIndentation) . $comment;
|
||||
}
|
||||
$this->text[$lineNr] = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the comment from the given line
|
||||
*
|
||||
* @param $lineContent The content of the line
|
||||
*
|
||||
* @return string The extracted comment
|
||||
*/
|
||||
private function getComment($lineContent)
|
||||
{
|
||||
/*
|
||||
* Remove all content in double quotes that is not behind a semicolon, recognizing
|
||||
* escaped double quotes inside the string
|
||||
*/
|
||||
$cleaned = preg_replace('/^[^;"]*"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"/s', '', $lineContent);
|
||||
|
||||
$matches = explode(';', $cleaned, 2);
|
||||
return array_key_exists(1, $matches) ? $matches[1] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the line $lineNr
|
||||
*
|
||||
* @param $lineNr The lineNr starting at 0
|
||||
*/
|
||||
private function deleteLine($lineNr)
|
||||
{
|
||||
$this->text = $this->removeFromArray($this->text, $lineNr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a key-value pair to an INI file-entry
|
||||
*
|
||||
* @param array $key The key to format
|
||||
* @param string $value The value to format
|
||||
*
|
||||
* @return string The formatted key-value pair
|
||||
*/
|
||||
private function formatKeyValuePair(array $key, $value)
|
||||
{
|
||||
return str_pad($this->formatKey($key), $this->valueIndentation) . ' = ' . $this->formatValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a key to an INI key
|
||||
*
|
||||
* @param array $key the key array to format
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function formatKey(array $key)
|
||||
{
|
||||
foreach ($key as $i => $separator) {
|
||||
$key[$i] = $this->sanitize($separator);
|
||||
}
|
||||
return implode($this->nestSeparator, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first line after the given $section
|
||||
*
|
||||
* @param $section The name of the section
|
||||
*
|
||||
* @return int The line number of the section
|
||||
*/
|
||||
private function getSectionEnd($section = null)
|
||||
{
|
||||
$i = 0;
|
||||
$started = isset($section) ? false : true;
|
||||
foreach ($this->text as $line) {
|
||||
if ($started && $this->isSectionDeclaration($line)) {
|
||||
if ($i === 0) {
|
||||
return $i;
|
||||
}
|
||||
/*
|
||||
* ignore all comments 'glued' to the next section, to allow section
|
||||
* comments in front of sections
|
||||
*/
|
||||
while ($i > 0 && $this->isComment($this->text[$i - 1])) {
|
||||
$i--;
|
||||
}
|
||||
return $i;
|
||||
} elseif ($this->isSectionDeclaration($line, $section)) {
|
||||
$started = true;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
if (!$started) {
|
||||
return -1;
|
||||
}
|
||||
return $i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given line contains only a comment
|
||||
*/
|
||||
private function isComment($line)
|
||||
{
|
||||
return preg_match('/^\s*;/', $line) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the line contains the property declaration for a key
|
||||
*
|
||||
* @param string $lineContent The content of the line
|
||||
* @param array $key The key this declaration is supposed to have
|
||||
*
|
||||
* @return boolean True, when the lineContent is a property declaration
|
||||
*/
|
||||
private function isPropertyDeclaration($lineContent, array $key)
|
||||
{
|
||||
return preg_match(
|
||||
'/^\s*' . preg_quote($this->formatKey($key), '/') . '\s*=\s*/',
|
||||
$lineContent
|
||||
) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given line contains a section declaration
|
||||
*
|
||||
* @param $lineContent The content of the line
|
||||
* @param string $section The optional section name that will be assumed
|
||||
*
|
||||
* @return bool True, when the lineContent is a section declaration
|
||||
*/
|
||||
private function isSectionDeclaration($lineContent, $section = null)
|
||||
{
|
||||
if (isset($section)) {
|
||||
return preg_match('/^\s*\[\s*' . $section . '\s*[\]:]/', $lineContent) === 1;
|
||||
} else {
|
||||
return preg_match('/^\s*\[/', $lineContent) === 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the line where the section begins
|
||||
*
|
||||
* @param $section The section
|
||||
*
|
||||
* @return int The line number
|
||||
*/
|
||||
private function getSectionLine($section)
|
||||
{
|
||||
$i = 0;
|
||||
foreach ($this->text as $line) {
|
||||
if ($this->isSectionDeclaration($line, $section)) {
|
||||
return $i;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the line number where the given key occurs
|
||||
*
|
||||
* @param array $keys The key and its parents
|
||||
* @param $section The section of the key
|
||||
*
|
||||
* @return int The line number
|
||||
*/
|
||||
private function getKeyLine(array $keys, $section = null)
|
||||
{
|
||||
$key = implode($this->nestSeparator, $keys);
|
||||
$inSection = isset($section) ? false : true;
|
||||
$i = 0;
|
||||
foreach ($this->text as $line) {
|
||||
if ($inSection && $this->isSectionDeclaration($line)) {
|
||||
return -1;
|
||||
}
|
||||
if ($inSection && $this->isPropertyDeclaration($line, $keys)) {
|
||||
return $i;
|
||||
}
|
||||
if (!$inSection && $this->isSectionDeclaration($line, $section)) {
|
||||
$inSection = true;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last line number occurring in the text
|
||||
*
|
||||
* @return The line number of the last line
|
||||
*/
|
||||
private function getLastLine()
|
||||
{
|
||||
return count($this->text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new element into a specific position of an array
|
||||
*
|
||||
* @param $array The array to use
|
||||
* @param $pos The target position
|
||||
* @param $element The element to insert
|
||||
*
|
||||
* @return array The changed array
|
||||
*/
|
||||
private static function insertIntoArray($array, $pos, $element)
|
||||
{
|
||||
array_splice($array, $pos, 0, $element);
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an element from an array
|
||||
*
|
||||
* @param $array The array to use
|
||||
* @param $pos The position to remove
|
||||
*
|
||||
* @return array The altered array
|
||||
*/
|
||||
private function removeFromArray($array, $pos)
|
||||
{
|
||||
unset($array[$pos]);
|
||||
return array_values($array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a value for INI
|
||||
*
|
||||
* @param $value The value of the string
|
||||
*
|
||||
* @return string The formatted value
|
||||
*
|
||||
* @throws Zend_Config_Exception
|
||||
*/
|
||||
private function formatValue($value)
|
||||
{
|
||||
if (is_integer($value) || is_float($value)) {
|
||||
return $value;
|
||||
} elseif (is_bool($value)) {
|
||||
return ($value ? 'true' : 'false');
|
||||
}
|
||||
return '"' . str_replace('"', '\"', $this->sanitize($value)) . '"';
|
||||
}
|
||||
|
||||
private function sanitize($value)
|
||||
{
|
||||
return str_replace("\n", '', $value);
|
||||
}
|
||||
}
|
237
library/Icinga/File/Ini/IniParser.php
Normal file
237
library/Icinga/File/Ini/IniParser.php
Normal file
@ -0,0 +1,237 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\File\Ini;
|
||||
|
||||
use Icinga\File\Ini\Dom\Section;
|
||||
use Icinga\File\Ini\Dom\Comment;
|
||||
use Icinga\File\Ini\Dom\Document;
|
||||
use Icinga\File\Ini\Dom\Directive;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
|
||||
class IniParser
|
||||
{
|
||||
const LINE_START = 0;
|
||||
const SECTION = 1;
|
||||
const ESCAPE = 2;
|
||||
const DIRECTIVE_KEY = 4;
|
||||
const DIRECTIVE_VALUE_START = 5;
|
||||
const DIRECTIVE_VALUE = 6;
|
||||
const DIRECTIVE_VALUE_QUOTED = 7;
|
||||
const COMMENT = 8;
|
||||
const COMMENT_END = 9;
|
||||
const LINE_END = 10;
|
||||
|
||||
private static function throwParseError($message, $line)
|
||||
{
|
||||
throw new ConfigurationError(sprintf('Ini parser error: %s. (l. %d)', $message, $line));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the ini file contained in a string and return a mutable DOM that can be used
|
||||
* to change the content of an INI file.
|
||||
*
|
||||
* @param $str A string containing the whole ini file
|
||||
*
|
||||
* @return Document The mutable DOM object.
|
||||
* @throws ConfigurationError In case the file is not parseable
|
||||
*/
|
||||
public static function parseIni($str)
|
||||
{
|
||||
$doc = new Document();
|
||||
$sec = null;
|
||||
$dir = null;
|
||||
$coms = array();
|
||||
$state = self::LINE_START;
|
||||
$escaping = null;
|
||||
$token = '';
|
||||
$line = 0;
|
||||
|
||||
for ($i = 0; $i < strlen($str); $i++) {
|
||||
$s = $str[$i];
|
||||
switch ($state) {
|
||||
case self::LINE_START:
|
||||
if (ctype_space($s)) {
|
||||
continue;
|
||||
}
|
||||
switch ($s) {
|
||||
case '[':
|
||||
$state = self::SECTION;
|
||||
break;
|
||||
case ';':
|
||||
$state = self::COMMENT;
|
||||
break;
|
||||
default:
|
||||
$state = self::DIRECTIVE_KEY;
|
||||
$token = $s;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::ESCAPE:
|
||||
$token .= $s;
|
||||
$state = $escaping;
|
||||
$escaping = null;
|
||||
break;
|
||||
|
||||
case self::SECTION:
|
||||
if ($s === "\n") {
|
||||
self::throwParseError('Unterminated SECTION', $line);
|
||||
} elseif ($s === '\\') {
|
||||
$state = self::ESCAPE;
|
||||
$escaping = self::SECTION;
|
||||
} elseif ($s !== ']') {
|
||||
$token .= $s;
|
||||
} else {
|
||||
$sec = new Section($token);
|
||||
$sec->commentsPre = $coms;
|
||||
$doc->addSection($sec);
|
||||
$dir = null;
|
||||
$coms = array();
|
||||
|
||||
$state = self::LINE_END;
|
||||
$token = '';
|
||||
}
|
||||
break;
|
||||
|
||||
case self::DIRECTIVE_KEY:
|
||||
if ($s !== '=') {
|
||||
$token .= $s;
|
||||
} else {
|
||||
$dir = new Directive($token);
|
||||
$dir->commentsPre = $coms;
|
||||
|
||||
if (isset($sec)) {
|
||||
$sec->addDirective($dir);
|
||||
} else {
|
||||
Logger::warning(sprintf(
|
||||
'Ini parser warning: section-less directive "%s" ignored. (l. %d)',
|
||||
$token,
|
||||
$line
|
||||
));
|
||||
}
|
||||
|
||||
$coms = array();
|
||||
$state = self::DIRECTIVE_VALUE_START;
|
||||
$token = '';
|
||||
}
|
||||
break;
|
||||
|
||||
case self::DIRECTIVE_VALUE_START:
|
||||
if (ctype_space($s)) {
|
||||
continue;
|
||||
} elseif ($s === '"') {
|
||||
$state = self::DIRECTIVE_VALUE_QUOTED;
|
||||
} else {
|
||||
$state = self::DIRECTIVE_VALUE;
|
||||
$token = $s;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::DIRECTIVE_VALUE:
|
||||
/*
|
||||
Escaping non-quoted values is not supported by php_parse_ini, it might
|
||||
be reasonable to include in case we are switching completely our own
|
||||
parser implementation
|
||||
*/
|
||||
if ($s === "\n" || $s === ";") {
|
||||
$dir->setValue($token);
|
||||
$token = '';
|
||||
|
||||
if ($s === "\n") {
|
||||
$state = self::LINE_START;
|
||||
$line ++;
|
||||
} elseif ($s === ';') {
|
||||
$state = self::COMMENT;
|
||||
}
|
||||
} else {
|
||||
$token .= $s;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::DIRECTIVE_VALUE_QUOTED:
|
||||
if ($s === "\n") {
|
||||
self::throwParseError('Unterminated DIRECTIVE_VALUE_QUOTED', $line);
|
||||
} elseif ($s === '\\') {
|
||||
$state = self::ESCAPE;
|
||||
$escaping = self::DIRECTIVE_VALUE_QUOTED;
|
||||
} elseif ($s !== '"') {
|
||||
$token .= $s;
|
||||
} else {
|
||||
$dir->setValue($token);
|
||||
$token = '';
|
||||
$state = self::LINE_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::COMMENT:
|
||||
case self::COMMENT_END:
|
||||
if ($s !== "\n") {
|
||||
$token .= $s;
|
||||
} else {
|
||||
$com = new Comment();
|
||||
$com->content = $token;
|
||||
$token = '';
|
||||
|
||||
// Comments at the line end belong to the current line's directive or section. Comments
|
||||
// on empty lines belong to the next directive that shows up.
|
||||
if ($state === self::COMMENT_END) {
|
||||
if (isset($dir)) {
|
||||
$dir->commentPost = $com;
|
||||
} else {
|
||||
$sec->commentPost = $com;
|
||||
}
|
||||
} else {
|
||||
$coms[] = $com;
|
||||
}
|
||||
$state = self::LINE_START;
|
||||
$line ++;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::LINE_END:
|
||||
if ($s === "\n") {
|
||||
$state = self::LINE_START;
|
||||
$line ++;
|
||||
} elseif ($s === ';') {
|
||||
$state = self::COMMENT_END;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// process the last token
|
||||
switch ($state) {
|
||||
case self::COMMENT:
|
||||
case self::COMMENT_END:
|
||||
$com = new Comment();
|
||||
$com->content = $token;
|
||||
if ($state === self::COMMENT_END) {
|
||||
if (isset($dir)) {
|
||||
$dir->commentPost = $com;
|
||||
} else {
|
||||
$sec->commentPost = $com;
|
||||
}
|
||||
} else {
|
||||
$coms[] = $com;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::DIRECTIVE_VALUE:
|
||||
$dir->setValue($token);
|
||||
$sec->addDirective($dir);
|
||||
break;
|
||||
|
||||
case self::ESCAPE:
|
||||
case self::DIRECTIVE_VALUE_QUOTED:
|
||||
case self::DIRECTIVE_KEY:
|
||||
case self::SECTION:
|
||||
self::throwParseError('File ended in unterminated state ' . $state, $line);
|
||||
}
|
||||
if (! empty($coms)) {
|
||||
$doc->commentsDangling = $coms;
|
||||
}
|
||||
return $doc;
|
||||
}
|
||||
}
|
@ -3,16 +3,19 @@
|
||||
|
||||
namespace Icinga\File\Ini;
|
||||
|
||||
use Zend_Config;
|
||||
use Zend_Config_Ini;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\File\Ini\Dom\Directive;
|
||||
use Icinga\File\Ini\Dom\Document;
|
||||
use Icinga\File\Ini\Dom\Section;
|
||||
use Zend_Config_Exception;
|
||||
use Zend_Config_Writer_FileAbstract;
|
||||
use Icinga\Application\Config;
|
||||
|
||||
/**
|
||||
* A INI file adapter that respects the file structure and the comments of already existing ini files
|
||||
*/
|
||||
class IniWriter extends Zend_Config_Writer_FileAbstract
|
||||
class IniWriter
|
||||
{
|
||||
/**
|
||||
* Stores the options
|
||||
@ -21,151 +24,137 @@ class IniWriter extends Zend_Config_Writer_FileAbstract
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* The configuration object to write
|
||||
*
|
||||
* @var Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* The mode to set on new files
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public static $fileMode = 0660;
|
||||
protected $fileMode;
|
||||
|
||||
/**
|
||||
* The path to write to
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $filename;
|
||||
|
||||
/**
|
||||
* Create a new INI writer
|
||||
*
|
||||
* @param array $options Supports all options of Zend_Config_Writer and additional options:
|
||||
* * filemode: The mode to set on new files
|
||||
* * valueIndentation: The indentation level of the values
|
||||
* * commentIndentation: The indentation level of the comments
|
||||
* * sectionSeparators: The amount of newlines between sections
|
||||
* @param Config $config The configuration to write
|
||||
* @param string $filename The file name to write to
|
||||
* @param int $filemode Octal file persmissions
|
||||
*
|
||||
* @link http://framework.zend.com/apidoc/1.12/files/Config.Writer.html#\Zend_Config_Writer
|
||||
*/
|
||||
public function __construct(array $options = null)
|
||||
public function __construct(Config $config, $filename, $filemode = 0660, $options = array())
|
||||
{
|
||||
if (isset($options['config']) && $options['config'] instanceof Config) {
|
||||
// As this class inherits from Zend_Config_Writer_FileAbstract we must
|
||||
// not pass the config directly as it needs to be of type Zend_Config
|
||||
$options['config'] = new Zend_Config($options['config']->toArray(), true);
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
$this->filename = $filename;
|
||||
$this->fileMode = $filemode;
|
||||
$this->options = $options;
|
||||
parent::__construct($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Zend_Config into a config file string
|
||||
* Render the Zend_Config into a config filestring
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
if (file_exists($this->_filename)) {
|
||||
$oldconfig = new Zend_Config_Ini($this->_filename);
|
||||
$content = trim(file_get_contents($this->_filename));
|
||||
if (file_exists($this->filename)) {
|
||||
$oldconfig = Config::fromIni($this->filename);
|
||||
$content = trim(file_get_contents($this->filename));
|
||||
} else {
|
||||
$oldconfig = new Zend_Config(array());
|
||||
$oldconfig = Config::fromArray(array());
|
||||
$content = '';
|
||||
}
|
||||
|
||||
$newconfig = $this->_config;
|
||||
$editor = new IniEditor($content, $this->options);
|
||||
$this->diffConfigs($oldconfig, $newconfig, $editor);
|
||||
$this->updateSectionOrder($newconfig, $editor);
|
||||
return $editor->getText();
|
||||
$doc = IniParser::parseIni($content);
|
||||
$this->diffPropertyUpdates($this->config, $doc);
|
||||
$this->diffPropertyDeletions($oldconfig, $this->config, $doc);
|
||||
$doc = $this->updateSectionOrder($this->config, $doc);
|
||||
return $doc->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write configuration to file and set file mode in case it does not exist yet
|
||||
*
|
||||
* @param string $filename
|
||||
* @param Zend_Config $config
|
||||
* @param bool $exclusiveLock
|
||||
*
|
||||
* @throws Zend_Config_Exception
|
||||
*/
|
||||
public function write($filename = null, Zend_Config $config = null, $exclusiveLock = null)
|
||||
public function write($filename = null, $exclusiveLock = false)
|
||||
{
|
||||
$filePath = $filename !== null ? $filename : $this->_filename;
|
||||
$filePath = isset($filename) ? $filename : $this->filename;
|
||||
$setMode = false === file_exists($filePath);
|
||||
|
||||
parent::write($filename, $config, $exclusiveLock);
|
||||
if (file_put_contents($filePath, $this->render(), $exclusiveLock ? LOCK_EX : 0) === false) {
|
||||
throw new Zend_Config_Exception('Could not write to file "' . $filePath . '"');
|
||||
}
|
||||
|
||||
if ($setMode) {
|
||||
$mode = isset($this->options['filemode']) ? $this->options['filemode'] : static::$fileMode;
|
||||
if (is_int($mode) && false === @chmod($filePath, $mode)) {
|
||||
// file was newly created
|
||||
$mode = $this->fileMode;
|
||||
if (is_int($this->fileMode) && false === @chmod($filePath, $this->fileMode)) {
|
||||
throw new Zend_Config_Exception(sprintf('Failed to set file mode "%o" on file "%s"', $mode, $filePath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a property diff and apply the changes to the editor
|
||||
*
|
||||
* @param Zend_Config $oldconfig The config representing the state before the change
|
||||
* @param Zend_Config $newconfig The config representing the state after the change
|
||||
* @param IniEditor $editor The editor that should be used to edit the old config file
|
||||
* @param array $parents The parent keys that should be respected when editing the config
|
||||
*/
|
||||
protected function diffConfigs(
|
||||
Zend_Config $oldconfig,
|
||||
Zend_Config $newconfig,
|
||||
IniEditor $editor,
|
||||
array $parents = array()
|
||||
) {
|
||||
$this->diffPropertyUpdates($oldconfig, $newconfig, $editor, $parents);
|
||||
$this->diffPropertyDeletions($oldconfig, $newconfig, $editor, $parents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the order of the sections in the ini file to match the order of the new config
|
||||
*
|
||||
* @return Document A new document with the changed section order applied
|
||||
*/
|
||||
protected function updateSectionOrder(Zend_Config $newconfig, IniEditor $editor)
|
||||
protected function updateSectionOrder(Config $newconfig, Document $oldDoc)
|
||||
{
|
||||
$order = array();
|
||||
foreach ($newconfig as $key => $value) {
|
||||
if ($value instanceof Zend_Config) {
|
||||
array_push($order, $key);
|
||||
}
|
||||
$doc = new Document();
|
||||
$doc->commentsDangling = $oldDoc->commentsDangling;
|
||||
foreach ($newconfig->toArray() as $section => $directives) {
|
||||
$doc->addSection($oldDoc->getSection($section));
|
||||
}
|
||||
$editor->refreshSectionOrder($order);
|
||||
return $doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for created and updated properties and use the editor to create or update these entries
|
||||
*
|
||||
* @param Zend_Config $oldconfig The config representing the state before the change
|
||||
* @param Zend_Config $newconfig The config representing the state after the change
|
||||
* @param IniEditor $editor The editor that should be used to edit the old config file
|
||||
* @param array $parents The parent keys that should be respected when editing the config
|
||||
* @param Config $newconfig The config representing the state after the change
|
||||
* @param Document $doc
|
||||
*
|
||||
* @throws ProgrammingError
|
||||
*/
|
||||
protected function diffPropertyUpdates(
|
||||
Zend_Config $oldconfig,
|
||||
Zend_Config $newconfig,
|
||||
IniEditor $editor,
|
||||
array $parents = array()
|
||||
) {
|
||||
// The current section. This value is null when processing the section-less root element
|
||||
$section = empty($parents) ? null : $parents[0];
|
||||
// Iterate over all properties in the new configuration file and search for changes
|
||||
foreach ($newconfig as $key => $value) {
|
||||
$oldvalue = $oldconfig->get($key);
|
||||
$nextParents = array_merge($parents, array($key));
|
||||
$keyIdentifier = empty($parents) ? array($key) : array_slice($nextParents, 1, null, true);
|
||||
if ($value instanceof Zend_Config) {
|
||||
// The value is a nested Zend_Config, handle it recursively
|
||||
if ($section === null) {
|
||||
// Update the section declaration
|
||||
$extends = $newconfig->getExtends();
|
||||
$extend = array_key_exists($key, $extends) ? $extends[$key] : null;
|
||||
$editor->setSection($key, $extend);
|
||||
}
|
||||
if ($oldvalue === null) {
|
||||
$oldvalue = new Zend_Config(array());
|
||||
}
|
||||
$this->diffConfigs($oldvalue, $value, $editor, $nextParents);
|
||||
protected function diffPropertyUpdates(Config $newconfig, Document $doc)
|
||||
{
|
||||
foreach ($newconfig->toArray() as $section => $directives) {
|
||||
if (! is_array($directives)) {
|
||||
Logger::warning('Section-less property ' . (string)$directives . ' was ignored.');
|
||||
continue;
|
||||
}
|
||||
if (!$doc->hasSection($section)) {
|
||||
$domSection = new Section($section);
|
||||
$doc->addSection($domSection);
|
||||
} else {
|
||||
// The value is a plain value, use the editor to set it
|
||||
if (is_numeric($key)) {
|
||||
$editor->setArrayElement($keyIdentifier, $value, $section);
|
||||
$domSection = $doc->getSection($section);
|
||||
}
|
||||
foreach ($directives as $key => $value) {
|
||||
if ($value instanceof ConfigObject) {
|
||||
throw new ProgrammingError('Cannot diff recursive configs');
|
||||
}
|
||||
if ($domSection->hasDirective($key)) {
|
||||
$domSection->getDirective($key)->setValue($value);
|
||||
} else {
|
||||
$editor->set($keyIdentifier, $value, $section);
|
||||
$dir = new Directive($key);
|
||||
$dir->setValue($value);
|
||||
$domSection->addDirective($dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -174,68 +163,36 @@ class IniWriter extends Zend_Config_Writer_FileAbstract
|
||||
/**
|
||||
* Search for deleted properties and use the editor to delete these entries
|
||||
*
|
||||
* @param Zend_Config $oldconfig The config representing the state before the change
|
||||
* @param Zend_Config $newconfig The config representing the state after the change
|
||||
* @param IniEditor $editor The editor that should be used to edit the old config file
|
||||
* @param array $parents The parent keys that should be respected when editing the config
|
||||
* @param Config $oldconfig The config representing the state before the change
|
||||
* @param Config $newconfig The config representing the state after the change
|
||||
* @param Document $doc
|
||||
*
|
||||
* @throws ProgrammingError
|
||||
*/
|
||||
protected function diffPropertyDeletions(
|
||||
Zend_Config $oldconfig,
|
||||
Zend_Config $newconfig,
|
||||
IniEditor $editor,
|
||||
array $parents = array()
|
||||
) {
|
||||
// The current section. This value is null when processing the section-less root element
|
||||
$section = empty($parents) ? null : $parents[0];
|
||||
protected function diffPropertyDeletions(Config $oldconfig, Config $newconfig, Document $doc)
|
||||
{
|
||||
// Iterate over all properties in the old configuration file and remove those that don't
|
||||
// exist in the new config
|
||||
foreach ($oldconfig->toArray() as $section => $directives) {
|
||||
if (! is_array($directives)) {
|
||||
Logger::warning('Section-less property ' . (string)$directives . ' was ignored.');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Iterate over all properties in the old configuration file and search for deleted properties
|
||||
foreach ($oldconfig as $key => $value) {
|
||||
if ($newconfig->get($key) === null) {
|
||||
$nextParents = array_merge($parents, array($key));
|
||||
$keyIdentifier = empty($parents) ? array($key) : array_slice($nextParents, 1, null, true);
|
||||
foreach ($this->getPropertyIdentifiers($value, $keyIdentifier) as $propertyIdentifier) {
|
||||
$editor->reset($propertyIdentifier, $section);
|
||||
if ($newconfig->hasSection($section)) {
|
||||
$newSection = $newconfig->getSection($section);
|
||||
$oldDomSection = $doc->getSection($section);
|
||||
foreach ($directives as $key => $value) {
|
||||
if ($value instanceof ConfigObject) {
|
||||
throw new ProgrammingError('Cannot diff recursive configs');
|
||||
}
|
||||
if (null === $newSection->get($key) && $oldDomSection->hasDirective($key)) {
|
||||
$oldDomSection->removeDirective($key);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$doc->removeSection($section);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all possible combinations of property identifiers for the given value
|
||||
*
|
||||
* @param mixed $value The value to return all combinations for
|
||||
* @param array $key The root property identifier, if any
|
||||
*
|
||||
* @return array All property combinations that are possible
|
||||
*
|
||||
* @todo Cannot handle array properties yet (e.g. a.b[]='c')
|
||||
*/
|
||||
protected function getPropertyIdentifiers($value, array $key = null)
|
||||
{
|
||||
$combinations = array();
|
||||
$rootProperty = $key !== null ? $key : array();
|
||||
|
||||
if ($value instanceof Zend_Config) {
|
||||
foreach ($value as $subProperty => $subValue) {
|
||||
$combinations = array_merge(
|
||||
$combinations,
|
||||
$this->getPropertyIdentifiers($subValue, array_merge($rootProperty, array($subProperty)))
|
||||
);
|
||||
}
|
||||
} elseif (is_string($value)) {
|
||||
$combinations[] = $rootProperty;
|
||||
}
|
||||
|
||||
return $combinations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for filename
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFilename()
|
||||
{
|
||||
return $this->_filename;
|
||||
}
|
||||
}
|
||||
|
@ -605,6 +605,16 @@ class RepositoryQuery implements QueryInterface, SortRules, Iterator
|
||||
return $this->query->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current position of this query's iterator
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getIteratorPosition()
|
||||
{
|
||||
return $this->query->getIteratorPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start or rewind the iteration
|
||||
*/
|
||||
|
@ -42,17 +42,6 @@ class JavaScript
|
||||
'js/vendor/jquery.tipsy'
|
||||
);
|
||||
|
||||
public static function listModuleFiles()
|
||||
{
|
||||
$list = array();
|
||||
foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $name => $module) {
|
||||
if ($module->hasJs()) {
|
||||
$list[] = 'js/' . $name . '/module.js';
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
public static function sendMinified()
|
||||
{
|
||||
return self::send(true);
|
||||
@ -86,7 +75,11 @@ class JavaScript
|
||||
|
||||
foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $name => $module) {
|
||||
if ($module->hasJs()) {
|
||||
$jsFiles[] = $module->getJsFilename();
|
||||
foreach ($module->getJsFiles() as $path) {
|
||||
if (file_exists($path)) {
|
||||
$jsFiles[] = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$files = array_merge($vendorFiles, $jsFiles);
|
||||
@ -114,7 +107,7 @@ class JavaScript
|
||||
}
|
||||
|
||||
foreach ($jsFiles as $file) {
|
||||
$js .= file_get_contents($file);
|
||||
$js .= file_get_contents($file) . "\n\n\n";
|
||||
}
|
||||
|
||||
if ($minified) {
|
||||
|
@ -93,14 +93,21 @@ class LessCompiler
|
||||
public function addModule($name, $module)
|
||||
{
|
||||
if ($module->hasCss()) {
|
||||
$this->source .= "\n/* CSS: modules/$name/module.less */\n"
|
||||
$contents = array();
|
||||
foreach ($module->getCssFiles() as $path) {
|
||||
if (file_exists($path)) {
|
||||
$contents[] = "/* CSS: modules/$name/$path */\n" . file_get_contents($path);
|
||||
}
|
||||
}
|
||||
|
||||
$this->source .= ''
|
||||
. '.icinga-module.module-'
|
||||
. $name
|
||||
. " {\n"
|
||||
. file_get_contents($module->getCssFilename())
|
||||
. "}\n\n"
|
||||
;
|
||||
. join("\n\n", $contents)
|
||||
. "}\n\n";
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,11 @@ class StyleSheet
|
||||
$files = $lessFiles;
|
||||
foreach ($app->getModuleManager()->getLoadedModules() as $name => $module) {
|
||||
if ($module->hasCss()) {
|
||||
$files[] = $module->getCssFilename();
|
||||
foreach ($module->getCssFiles() as $path) {
|
||||
if (file_exists($path)) {
|
||||
$files[] = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Module\Doc\DocController;
|
||||
use Icinga\Module\Doc\Exception\DocException;
|
||||
use Icinga\File\Ini\Parser;
|
||||
|
||||
class Doc_ModuleController extends DocController
|
||||
{
|
||||
|
@ -576,7 +576,6 @@ class Monitoring_ListController extends Controller
|
||||
$this->view->history = $query;
|
||||
|
||||
$this->setupLimitControl();
|
||||
$this->setupPaginationControl($this->view->history);
|
||||
$this->setupSortControl(array(
|
||||
'timestamp' => $this->translate('Occurence')
|
||||
), $query);
|
||||
|
@ -17,6 +17,14 @@ function contactsLink($match, $view) {
|
||||
|
||||
$self = $this;
|
||||
|
||||
$url = $this->url();
|
||||
$limit = (int) $url->getParam('limit', 25);
|
||||
if (! $url->hasParam('page') || ($page = (int) $url->getParam('page')) < 1) {
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
$history->limit($limit * $page);
|
||||
|
||||
if (! $this->compact): ?>
|
||||
<div class="controls">
|
||||
<?= $this->tabs; ?>
|
||||
@ -24,14 +32,16 @@ if (! $this->compact): ?>
|
||||
<h1><?= $this->translate('This Host\'s Event History'); ?></h1>
|
||||
<?= $this->sortBox; ?>
|
||||
<?= $this->limiter; ?>
|
||||
<?= $this->paginator; ?>
|
||||
<a class="load-more-hint" href="#load-more">
|
||||
<?= $this->translate('Scroll to the bottom of this page to load additional events'); ?>
|
||||
</a>
|
||||
<?= $this->filterEditor; ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div class="content">
|
||||
<table data-base-target="_next" class="action objecthistory">
|
||||
<tbody>
|
||||
<?php foreach ($history as $event): ?>
|
||||
<?php foreach ($history->peekAhead() as $event): ?>
|
||||
<?php
|
||||
$stateClass = 'invalid';
|
||||
$msg = $this->escape($event->output);
|
||||
@ -102,6 +112,9 @@ if (! $this->compact): ?>
|
||||
?>
|
||||
<tr class="state <?= $stateClass; ?>">
|
||||
<td class="state">
|
||||
<?php if ($history->getIteratorPosition() % $limit === 0): ?>
|
||||
<a id="page-<?= $history->getIteratorPosition() / $limit + 1; ?>"></a>
|
||||
<?php endif ?>
|
||||
<strong><?= $this->escape($title); ?></strong>
|
||||
<br>
|
||||
<?= date('d.m. H:i', $event->timestamp); ?>
|
||||
@ -139,5 +152,17 @@ if (! $this->compact): ?>
|
||||
</table>
|
||||
<?php if (! $history->hasResult()): ?>
|
||||
<?= $this->translate('No history events found matching the filter'); ?>
|
||||
<?php elseif ($history->hasMore()): ?>
|
||||
<div class="load-more-container"><?= $this->qlink(
|
||||
$this->translate('Load More'),
|
||||
$url->setAnchor('page-' . ($page + 1)),
|
||||
array(
|
||||
'page' => $page + 1,
|
||||
),
|
||||
array(
|
||||
'id' => 'load-more',
|
||||
'class' => 'pull-right load-more button-like'
|
||||
)
|
||||
); ?></div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
|
@ -2,21 +2,29 @@
|
||||
use Icinga\Module\Monitoring\Object\Host;
|
||||
use Icinga\Module\Monitoring\Object\Service;
|
||||
|
||||
$history->peekAhead($this->compact);
|
||||
$url = $this->url();
|
||||
$limit = (int) $url->getParam('limit', 25);
|
||||
if (! $url->hasParam('page') || ($page = (int) $url->getParam('page')) < 1) {
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
$history->limit($limit * $page);
|
||||
|
||||
if (! $this->compact): ?>
|
||||
<div class="controls">
|
||||
<?= $this->tabs; ?>
|
||||
<?= $this->sortBox; ?>
|
||||
<?= $this->limiter; ?>
|
||||
<?= $this->paginator; ?>
|
||||
<a class="load-more-hint" href="#load-more">
|
||||
<?= $this->translate('Scroll to the bottom of this page to load additional events'); ?>
|
||||
</a>
|
||||
<?= $this->filterEditor; ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div class="content">
|
||||
<table data-base-target="_next" class="action">
|
||||
<tbody>
|
||||
<?php foreach ($history as $event): ?>
|
||||
<?php foreach ($history->peekAhead() as $event): ?>
|
||||
<?php
|
||||
$icon = 'help';
|
||||
$msg = $event->output;
|
||||
@ -71,6 +79,9 @@ if (! $this->compact): ?>
|
||||
?>
|
||||
<tr class="state <?= $stateName; ?>">
|
||||
<td class="state">
|
||||
<?php if ($history->getIteratorPosition() % $limit === 0): ?>
|
||||
<a id="page-<?= $history->getIteratorPosition() / $limit + 1; ?>"></a>
|
||||
<?php endif ?>
|
||||
<strong><?= $this->escape($title); ?></strong>
|
||||
<br>
|
||||
<?= $this->timeAgo($event->timestamp, $this->compact); ?>
|
||||
@ -95,14 +106,28 @@ if (! $this->compact): ?>
|
||||
<?php if (! $history->hasResult()): ?>
|
||||
<?= $this->translate('No history events found matching the filter'); ?>
|
||||
<?php elseif ($history->hasMore()): ?>
|
||||
<?php if ($this->compact): ?>
|
||||
<?= $this->qlink(
|
||||
$this->translate('Show More'),
|
||||
$this->url()->without(array('view', 'limit')),
|
||||
$url->without(array('view', 'limit')),
|
||||
null,
|
||||
array(
|
||||
'data-base-target' => '_next',
|
||||
'class' => 'pull-right show-more'
|
||||
)
|
||||
); ?>
|
||||
<?php else: ?>
|
||||
<div class="load-more-container"><?= $this->qlink(
|
||||
$this->translate('Load More'),
|
||||
$url->setAnchor('page-' . ($page + 1)),
|
||||
array(
|
||||
'page' => $page + 1,
|
||||
),
|
||||
array(
|
||||
'id' => 'load-more',
|
||||
'class' => 'pull-right load-more button-like'
|
||||
)
|
||||
); ?></div>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
|
@ -16,6 +16,14 @@ function contactsLink($match, $view) {
|
||||
|
||||
$self = $this;
|
||||
|
||||
$url = $this->url();
|
||||
$limit = (int) $url->getParam('limit', 25);
|
||||
if (! $url->hasParam('page') || ($page = (int) $url->getParam('page')) < 1) {
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
$history->limit($limit * $page);
|
||||
|
||||
if (! $this->compact): ?>
|
||||
<div class="controls">
|
||||
<?= $this->tabs; ?>
|
||||
@ -23,14 +31,16 @@ if (! $this->compact): ?>
|
||||
<h1><?= $this->translate('This Service\'s Event History'); ?></h1>
|
||||
<?= $this->sortBox; ?>
|
||||
<?= $this->limiter; ?>
|
||||
<?= $this->paginator; ?>
|
||||
<a class="load-more-hint" href="#load-more">
|
||||
<?= $this->translate('Scroll to the bottom of this page to load additional events'); ?>
|
||||
</a>
|
||||
<?= $this->filterEditor; ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div class="content">
|
||||
<table data-base-target="_next" class="action objecthistory">
|
||||
<tbody>
|
||||
<?php foreach ($history as $event): ?>
|
||||
<?php foreach ($history->peekAhead() as $event): ?>
|
||||
<?php
|
||||
$stateClass = 'invalid';
|
||||
$msg = $this->escape($event->output);
|
||||
@ -100,6 +110,9 @@ if (! $this->compact): ?>
|
||||
?>
|
||||
<tr class="state <?= $stateClass; ?>">
|
||||
<td class="state">
|
||||
<?php if ($history->getIteratorPosition() % $limit === 0): ?>
|
||||
<a id="page-<?= $history->getIteratorPosition() / $limit + 1; ?>"></a>
|
||||
<?php endif ?>
|
||||
<strong><?= $this->escape($title); ?></strong>
|
||||
<br>
|
||||
<?= date('d.m. H:i', $event->timestamp); ?>
|
||||
@ -121,5 +134,17 @@ if (! $this->compact): ?>
|
||||
</table>
|
||||
<?php if (! $history->hasResult()): ?>
|
||||
<?= $this->translate('No history events found matching the filter'); ?>
|
||||
<?php elseif ($history->hasMore()): ?>
|
||||
<div class="load-more-container"><?= $this->qlink(
|
||||
$this->translate('Load More'),
|
||||
$url->setAnchor('page-' . ($page + 1)),
|
||||
array(
|
||||
'page' => $page + 1,
|
||||
),
|
||||
array(
|
||||
'id' => 'load-more',
|
||||
'class' => 'pull-right load-more button-like'
|
||||
)
|
||||
); ?></div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
|
@ -115,7 +115,7 @@ class HostgroupsummaryQuery extends IdoQuery
|
||||
$this->subQueries[] = $services;
|
||||
$this->summaryQuery = $this->db->select()->union(array($hosts, $services), Zend_Db_Select::SQL_UNION_ALL);
|
||||
$this->select->from(array('statussummary' => $this->summaryQuery), array());
|
||||
$this->group(array('hostgroup_name', 'hostgroup_alias'));
|
||||
$this->group(array('statussummary.hostgroup_name', 'statussummary.hostgroup_alias'));
|
||||
$this->joinedVirtualTables['hoststatussummary'] = true;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ namespace Icinga\Module\Monitoring;
|
||||
use Exception;
|
||||
use Icinga\Module\Setup\Step;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Exception\IcingaException;
|
||||
|
||||
class BackendStep extends Step
|
||||
{
|
||||
@ -151,7 +152,7 @@ class BackendStep extends Step
|
||||
),
|
||||
Config::resolvePath('modules/monitoring/backends.ini')
|
||||
);
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->backendIniError->getMessage());
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->backendIniError));
|
||||
}
|
||||
|
||||
if ($this->resourcesIniError === false) {
|
||||
@ -164,7 +165,7 @@ class BackendStep extends Step
|
||||
mt('monitoring', 'Resource configuration could not be udpated: %s. An error occured:'),
|
||||
Config::resolvePath('resources.ini')
|
||||
);
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->resourcesIniError->getMessage());
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->resourcesIniError));
|
||||
}
|
||||
|
||||
return $report;
|
||||
|
@ -122,7 +122,6 @@ class LocalCommandFile implements CommandTransportInterface
|
||||
try {
|
||||
$file = new File($this->path, $this->openMode);
|
||||
$file->fwrite($commandString . "\n");
|
||||
$file->fflush();
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if ($e instanceof RuntimeException && ($pos = strrpos($message, ':')) !== false) {
|
||||
|
@ -69,6 +69,16 @@ abstract class DataView implements QueryInterface, SortRules, IteratorAggregate
|
||||
return $this->getQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current position of the result set's iterator
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getIteratorPosition()
|
||||
{
|
||||
return $this->query->getIteratorPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query name this data view relies on
|
||||
*
|
||||
|
@ -6,6 +6,7 @@ namespace Icinga\Module\Monitoring;
|
||||
use Exception;
|
||||
use Icinga\Module\Setup\Step;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Exception\IcingaException;
|
||||
|
||||
class InstanceStep extends Step
|
||||
{
|
||||
@ -98,7 +99,7 @@ class InstanceStep extends Step
|
||||
),
|
||||
Config::resolvePath('modules/monitoring/instances.ini')
|
||||
),
|
||||
sprintf(mt('setup', 'ERROR: %s'), $this->error->getMessage())
|
||||
sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->error))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ namespace Icinga\Module\Monitoring;
|
||||
use Exception;
|
||||
use Icinga\Module\Setup\Step;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Exception\IcingaException;
|
||||
|
||||
class SecurityStep extends Step
|
||||
{
|
||||
@ -76,7 +77,7 @@ class SecurityStep extends Step
|
||||
),
|
||||
Config::resolvePath('modules/monitoring/config.ini')
|
||||
),
|
||||
sprintf(mt('setup', 'ERROR: %s'), $this->error->getMessage())
|
||||
sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->error))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,6 @@ class DbResourcePage extends Form
|
||||
public function init()
|
||||
{
|
||||
$this->setTitle($this->translate('Database Resource', 'setup.page.title'));
|
||||
$this->addDescription($this->translate(
|
||||
'Now please configure your database resource. Note that the database itself does not need to'
|
||||
. ' exist at this time as it is going to be created once the wizard is about to be finished.'
|
||||
));
|
||||
$this->setValidatePartial(true);
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ use Exception;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Authentication\User\DbUserBackend;
|
||||
use Icinga\Module\Setup\Step;
|
||||
|
||||
@ -194,7 +195,7 @@ class AuthenticationStep extends Step
|
||||
mt('setup', 'Authentication configuration could not be written to: %s. An error occured:'),
|
||||
Config::resolvePath('authentication.ini')
|
||||
);
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->authIniError->getMessage());
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->authIniError));
|
||||
}
|
||||
|
||||
if ($this->dbError === false) {
|
||||
@ -207,7 +208,7 @@ class AuthenticationStep extends Step
|
||||
mt('setup', 'Unable to create account "%s". An error occured:'),
|
||||
$this->data['adminAccountData']['username']
|
||||
);
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->dbError->getMessage());
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->dbError));
|
||||
}
|
||||
|
||||
if ($this->permIniError === false) {
|
||||
@ -229,7 +230,7 @@ class AuthenticationStep extends Step
|
||||
),
|
||||
$this->data['adminAccountData']['groupname']
|
||||
);
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->permIniError->getMessage());
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->permIniError));
|
||||
}
|
||||
|
||||
return $report;
|
||||
|
@ -5,6 +5,7 @@ namespace Icinga\Module\Setup\Steps;
|
||||
|
||||
use Exception;
|
||||
use PDOException;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Module\Setup\Step;
|
||||
use Icinga\Module\Setup\Utils\DbTool;
|
||||
use Icinga\Module\Setup\Exception\SetupException;
|
||||
@ -253,7 +254,7 @@ class DatabaseStep extends Step
|
||||
} elseif ($this->error !== null) {
|
||||
$report = $this->messages;
|
||||
$report[] = mt('setup', 'Failed to fully setup the database. An error occured:');
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->error->getMessage());
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->error));
|
||||
return $report;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ namespace Icinga\Module\Setup\Steps;
|
||||
use Exception;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Module\Setup\Step;
|
||||
|
||||
class GeneralConfigStep extends Step
|
||||
@ -112,7 +113,7 @@ class GeneralConfigStep extends Step
|
||||
mt('setup', 'General configuration could not be written to: %s. An error occured:'),
|
||||
Config::resolvePath('config.ini')
|
||||
),
|
||||
sprintf(mt('setup', 'ERROR: %s'), $this->error->getMessage())
|
||||
sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->error))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ namespace Icinga\Module\Setup\Steps;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Module\Setup\Step;
|
||||
|
||||
class ResourceStep extends Step
|
||||
@ -143,7 +144,7 @@ class ResourceStep extends Step
|
||||
mt('setup', 'Resource configuration could not be written to: %s. An error occured:'),
|
||||
Config::resolvePath('resources.ini')
|
||||
),
|
||||
sprintf(mt('setup', 'ERROR: %s'), $this->error->getMessage())
|
||||
sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->error))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ namespace Icinga\Module\Setup\Utils;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Module\Setup\Step;
|
||||
|
||||
class EnableModuleStep extends Step
|
||||
@ -57,7 +58,7 @@ class EnableModuleStep extends Step
|
||||
foreach ($this->moduleNames as $moduleName) {
|
||||
if (isset($this->errors[$moduleName])) {
|
||||
$report[] = sprintf($failMessage, $moduleName);
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), $this->errors[$moduleName]->getMessage());
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->errors[$moduleName]));
|
||||
} else {
|
||||
$report[] = sprintf($okMessage, $moduleName);
|
||||
}
|
||||
|
@ -163,6 +163,16 @@ class WebWizard extends Wizard implements SetupWizard
|
||||
}
|
||||
/*} elseif ($page->getName() === 'setup_ldap_discovery_confirm') {
|
||||
$page->setResourceConfig($this->getPageData('setup_ldap_discovery'));*/
|
||||
} elseif ($page->getName() === 'setup_auth_db_resource') {
|
||||
$page->addDescription(mt(
|
||||
'setup',
|
||||
'Now please configure the database resource where to store users and user groups.'
|
||||
));
|
||||
$page->addDescription(mt(
|
||||
'setup',
|
||||
'Note that the database itself does not need to exist at this time as'
|
||||
. ' it is going to be created once the wizard is about to be finished.'
|
||||
));
|
||||
} elseif ($page->getName() === 'setup_usergroup_backend') {
|
||||
$page->setResourceConfig($this->getPageData('setup_ldap_resource'));
|
||||
$page->setBackendConfig($this->getPageData('setup_authentication_backend'));
|
||||
@ -187,6 +197,16 @@ class WebWizard extends Wizard implements SetupWizard
|
||||
$page->setSubjectTitle('Icinga Web 2');
|
||||
$page->setSummary($this->getSetup()->getSummary());
|
||||
} elseif ($page->getName() === 'setup_config_db_resource') {
|
||||
$page->addDescription(mt(
|
||||
'setup',
|
||||
'Now please configure the database resource where to store user preferences.'
|
||||
));
|
||||
$page->addDescription(mt(
|
||||
'setup',
|
||||
'Note that the database itself does not need to exist at this time as'
|
||||
. ' it is going to be created once the wizard is about to be finished.'
|
||||
));
|
||||
|
||||
$ldapData = $this->getPageData('setup_ldap_resource');
|
||||
if ($ldapData !== null && $request->getPost('name') === $ldapData['name']) {
|
||||
$page->error(
|
||||
@ -209,7 +229,10 @@ class WebWizard extends Wizard implements SetupWizard
|
||||
} elseif ($page->getName() === 'setup_general_config') {
|
||||
$authData = $this->getPageData('setup_authentication_type');
|
||||
if ($authData['type'] === 'db') {
|
||||
$page->create()->getElement('global_config_backend')->setValue('db');
|
||||
$page
|
||||
->create($this->getRequestData($page, $request))
|
||||
->getElement('global_config_backend')
|
||||
->setValue('db');
|
||||
$page->info(
|
||||
mt(
|
||||
'setup',
|
||||
|
@ -62,7 +62,18 @@ ul.pagination {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
a.show-more {
|
||||
a.show-more, a.load-more {
|
||||
display: block;
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
a.load-more-hint {
|
||||
display: inline-block;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
div.load-more-container {
|
||||
display: table;
|
||||
margin: 0 auto;
|
||||
margin-top: 0.5em;
|
||||
}
|
@ -322,21 +322,6 @@
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle anchor, i.e. focus the element which is referenced by the anchor
|
||||
*
|
||||
* @param {string} query jQuery selector
|
||||
*/
|
||||
handleAnchor: function(query) {
|
||||
var $element = $(query);
|
||||
if ($element.length > 0) {
|
||||
if (typeof $element.attr('tabindex') === 'undefined') {
|
||||
$element.attr('tabindex', -1);
|
||||
}
|
||||
$element.focus();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Someone clicked a link or tr[href]
|
||||
*/
|
||||
@ -404,7 +389,7 @@
|
||||
// This is an anchor only
|
||||
if (href.substr(0, 1) === '#' && href.length > 1
|
||||
&& href.substr(1, 1) !== '!') {
|
||||
self.handleAnchor(href);
|
||||
icinga.ui.focusElement(href.substr(1), $a.closest('.container'));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -434,7 +419,7 @@
|
||||
|
||||
formerUrl = $target.data('icingaUrl');
|
||||
if (typeof formerUrl !== 'undefined' && formerUrl.split(/#/)[0] === href.split(/#/)[0]) {
|
||||
icinga.ui.scrollContainerToAnchor($target, href.split(/#/)[1]);
|
||||
icinga.ui.focusElement(href.split(/#/)[1], $target);
|
||||
$target.data('icingaUrl', href);
|
||||
if (formerUrl !== href) {
|
||||
icinga.history.pushCurrentState();
|
||||
|
@ -560,7 +560,7 @@
|
||||
oldNotifications.appendTo($('#notifications'));
|
||||
}
|
||||
if (url.match(/#/)) {
|
||||
this.icinga.ui.scrollContainerToAnchor(req.$target, url.split(/#/)[1]);
|
||||
this.icinga.ui.focusElement(url.split(/#/)[1], req.$target);
|
||||
}
|
||||
if (newBody) {
|
||||
this.icinga.ui.fixDebugVisibility().triggerWindowResize();
|
||||
|
@ -122,6 +122,33 @@
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus the given element and scroll to its position
|
||||
*
|
||||
* @param {string} element The name or id of the element to focus
|
||||
* @param {object} $container The container containing the element
|
||||
*/
|
||||
focusElement: function(element, $container) {
|
||||
var $element = $('#' + element, $container);
|
||||
|
||||
if (! $element.length) {
|
||||
// The name attribute is actually deprecated, on anchor tags,
|
||||
// but we'll possibly handle links from another source
|
||||
// (module etc) so that's used as a fallback
|
||||
$element = $('[name="' + element.replace(/'/, '\\\'') + '"]', $container);
|
||||
}
|
||||
|
||||
if ($element.length) {
|
||||
if (typeof $element.attr('tabindex') === 'undefined') {
|
||||
$element.attr('tabindex', -1);
|
||||
}
|
||||
|
||||
$element.focus();
|
||||
$container.scrollTop(0);
|
||||
$container.scrollTop($element.first().position().top);
|
||||
}
|
||||
},
|
||||
|
||||
moveToLeft: function () {
|
||||
var col2 = this.cutContainer($('#col2'));
|
||||
var kill = this.cutContainer($('#col1'));
|
||||
@ -159,18 +186,6 @@
|
||||
$col.data('icingaModule', backup['data']['data-icinga-module']);
|
||||
},
|
||||
|
||||
scrollContainerToAnchor: function ($container, anchorName) {
|
||||
// TODO: Generic issue -> we probably should escape attribute value selectors!?
|
||||
var $anchor = $("a[name='" + anchorName.replace(/'/, '\\\'') + "']", $container);
|
||||
if ($anchor.length) {
|
||||
$container.scrollTop(0);
|
||||
$container.scrollTop($anchor.first().position().top);
|
||||
this.icinga.logger.debug('Scrolling ', $container, ' to ', anchorName);
|
||||
} else {
|
||||
this.icinga.logger.info('Anchor "' + anchorName + '" not found in ', $container);
|
||||
}
|
||||
},
|
||||
|
||||
triggerWindowResize: function () {
|
||||
this.onWindowResize({data: {self: this}});
|
||||
},
|
||||
|
63
test/php/library/Icinga/File/Ini/IniParserTest.php
Normal file
63
test/php/library/Icinga/File/Ini/IniParserTest.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Tests\Icinga\Config;
|
||||
|
||||
use Icinga\File\Ini\Dom\Document;
|
||||
use Icinga\File\Ini\IniWriter;
|
||||
use Icinga\Test\BaseTestCase;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\File\Ini\IniParser;
|
||||
|
||||
class IniParserTest extends BaseTestCase
|
||||
{
|
||||
protected $tempFile;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->tempFile = tempnam(sys_get_temp_dir(), 'icinga-ini-parser-test');
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
unlink($this->tempFile);
|
||||
}
|
||||
|
||||
public function testSectionNameEscaping()
|
||||
{
|
||||
$config = <<<'EOD'
|
||||
[title with \]bracket]
|
||||
key1 = "1"
|
||||
key2 = "2"
|
||||
|
||||
[title with \"quote]
|
||||
key1 = "1"
|
||||
key2 = "2"
|
||||
EOD;
|
||||
$doc = IniParser::parseIni($config);
|
||||
$this->assertTrue(
|
||||
$doc->hasSection('title with ]bracket'),
|
||||
'IniParser does not recognize escaped bracket in section'
|
||||
);
|
||||
$this->assertTrue(
|
||||
$doc->hasSection('title with "quote'),
|
||||
'IniParser does not recognize escaped quote in section'
|
||||
);
|
||||
}
|
||||
|
||||
public function testDirectiveValueEscaping()
|
||||
{
|
||||
$config = <<<'EOD'
|
||||
[section]
|
||||
key1 = "key with escaped \"quote"
|
||||
EOD;
|
||||
$doc = IniParser::parseIni($config);
|
||||
$this->assertEquals(
|
||||
'key with escaped "quote',
|
||||
$doc->getSection('section')->getDirective('key1')->getValue(),
|
||||
'IniParser does not recognize escaped bracket in section'
|
||||
);
|
||||
}
|
||||
}
|
@ -31,86 +31,28 @@ class IniWriterTest extends BaseTestCase
|
||||
public function testWhetherPointInSectionIsNotNormalized()
|
||||
{
|
||||
$writer = new IniWriter(
|
||||
array(
|
||||
'config' => Config::fromArray(
|
||||
array(
|
||||
'section' => array(
|
||||
'foo.bar' => 1337
|
||||
),
|
||||
'section.with.multiple.dots' => array(
|
||||
'some more' => array(
|
||||
'nested stuff' => 'With more values'
|
||||
)
|
||||
)
|
||||
)
|
||||
Config::fromArray(
|
||||
array(
|
||||
'section' => array(
|
||||
'foo.bar' => 1337
|
||||
),
|
||||
'filename' => $this->tempFile
|
||||
)
|
||||
'section.with.multiple.dots' => array(
|
||||
'some more.nested stuff' => 'With more values'
|
||||
)
|
||||
)
|
||||
),
|
||||
$this->tempFile
|
||||
);
|
||||
$writer->write();
|
||||
$config = Config::fromIni($this->tempFile)->toArray();
|
||||
$this->assertTrue(array_key_exists('section.with.multiple.dots', $config), 'Section names not normalized');
|
||||
}
|
||||
|
||||
public function testWhetherSimplePropertiesAreInsertedInEmptyFiles()
|
||||
{
|
||||
$this->markTestSkipped('Implementation has changed. Section-less properties are not supported anymore');
|
||||
$target = $this->writeConfigToTemporaryFile('');
|
||||
$config = Config::fromArray(array('key' => 'value'));
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertEquals('value', $newConfig->get('key'), 'IniWriter does not insert in empty files');
|
||||
}
|
||||
|
||||
public function testWhetherSimplePropertiesAreInsertedInExistingFiles()
|
||||
{
|
||||
$this->markTestSkipped('Implementation has changed. Section-less properties are not supported anymore');
|
||||
$target = $this->writeConfigToTemporaryFile('key1 = "1"');
|
||||
$config = Config::fromArray(array('key2' => '2'));
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertEquals('2', $newConfig->get('key2'), 'IniWriter does not insert in existing files');
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherSimplePropertiesAreInsertedInExistingFiles
|
||||
*/
|
||||
public function testWhetherSimplePropertiesAreUpdated()
|
||||
{
|
||||
$this->markTestSkipped('Implementation has changed. Section-less properties are not supported anymore');
|
||||
$target = $this->writeConfigToTemporaryFile('key = "value"');
|
||||
$config = Config::fromArray(array('key' => 'eulav'));
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertEquals('eulav', $newConfig->get('key'), 'IniWriter does not update simple properties');
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherSimplePropertiesAreInsertedInExistingFiles
|
||||
*/
|
||||
public function testWhetherSimplePropertiesAreDeleted()
|
||||
{
|
||||
$this->markTestSkipped('Implementation has changed. Section-less properties are not supported anymore');
|
||||
$target = $this->writeConfigToTemporaryFile('key = "value"');
|
||||
$config = new Config();
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertNull($newConfig->get('key'), 'IniWriter does not delete simple properties');
|
||||
}
|
||||
|
||||
public function testWhetherNestedPropertiesAreInserted()
|
||||
{
|
||||
$target = $this->writeConfigToTemporaryFile('');
|
||||
$config = Config::fromArray(array('a' => array('b' => 'c')));
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer = new IniWriter($config, $target);
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
@ -126,450 +68,54 @@ class IniWriterTest extends BaseTestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherNestedPropertiesAreInserted
|
||||
*/
|
||||
public function testWhetherNestedPropertiesAreUpdated()
|
||||
{
|
||||
$this->markTestSkipped('Implementation has changed. Section-less properties are not supported anymore');
|
||||
$target = $this->writeConfigToTemporaryFile('a.b = "c"');
|
||||
$config = Config::fromArray(array('a' => array('b' => 'cc')));
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertInstanceOf(
|
||||
get_class($newConfig),
|
||||
$newConfig->get('a'),
|
||||
'IniWriter does not update nested properties'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'cc',
|
||||
$newConfig->get('a')->get('b'),
|
||||
'IniWriter does not update nested properties'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherNestedPropertiesAreInserted
|
||||
*/
|
||||
public function testWhetherNestedPropertiesAreDeleted()
|
||||
{
|
||||
$this->markTestSkipped('Implementation has changed. Section-less properties are not supported anymore');
|
||||
$target = $this->writeConfigToTemporaryFile('a.b = "c"');
|
||||
$config = new Config();
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertNull(
|
||||
$newConfig->get('a'),
|
||||
'IniWriter does not delete nested properties'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherSimpleSectionPropertiesAreInserted()
|
||||
{
|
||||
$target = $this->writeConfigToTemporaryFile('');
|
||||
$config = Config::fromArray(array('section' => array('key' => 'value')));
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertInstanceOf(
|
||||
'Icinga\Data\ConfigObject',
|
||||
$newConfig->getSection('section'),
|
||||
'IniWriter does not insert sections'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'value',
|
||||
$newConfig->getSection('section')->get('key'),
|
||||
'IniWriter does not insert simple section properties'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherSimpleSectionPropertiesAreInserted
|
||||
*/
|
||||
public function testWhetherSimpleSectionPropertiesAreUpdated()
|
||||
{
|
||||
$target = $this->writeConfigToTemporaryFile(<<<'EOD'
|
||||
[section]
|
||||
key = "value"
|
||||
EOD
|
||||
);
|
||||
$config = Config::fromArray(array('section' => array('key' => 'eulav')));
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertEquals(
|
||||
'eulav',
|
||||
$newConfig->getSection('section')->get('key'),
|
||||
'IniWriter does not update simple section properties'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherSimpleSectionPropertiesAreInserted
|
||||
*/
|
||||
public function testWhetherSimpleSectionPropertiesAreDeleted()
|
||||
{
|
||||
$target = $this->writeConfigToTemporaryFile(<<<'EOD'
|
||||
[section]
|
||||
key = "value"
|
||||
EOD
|
||||
);
|
||||
$config = Config::fromArray(array('section' => array()));
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertNull(
|
||||
$newConfig->getSection('section')->get('key'),
|
||||
'IniWriter does not delete simple section properties'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherNestedSectionPropertiesAreInserted()
|
||||
{
|
||||
$this->markTestSkipped('Implementation has changed. Config::fromIni cannot handle nested properties anymore');
|
||||
$target = $this->writeConfigToTemporaryFile('');
|
||||
$config = Config::fromArray(array('section' => array('a' => array('b' => 'c'))));
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertInstanceOf(
|
||||
get_class($newConfig),
|
||||
$newConfig->get('section'),
|
||||
'IniWriter does not insert sections'
|
||||
);
|
||||
$this->assertInstanceOf(
|
||||
get_class($newConfig),
|
||||
$newConfig->get('section')->get('a'),
|
||||
'IniWriter does not insert nested section properties'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'c',
|
||||
$newConfig->get('section')->get('a')->get('b'),
|
||||
'IniWriter does not insert nested section properties'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherNestedSectionPropertiesAreInserted
|
||||
*/
|
||||
public function testWhetherNestedSectionPropertiesAreUpdated()
|
||||
{
|
||||
$target = $this->writeConfigToTemporaryFile(<<<'EOD'
|
||||
[section]
|
||||
a.b = "c"
|
||||
EOD
|
||||
);
|
||||
$config = Config::fromArray(array('section' => array('a' => array('b' => 'cc'))));
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertEquals(
|
||||
'cc',
|
||||
$newConfig->get('section')->get('a')->get('b'),
|
||||
'IniWriter does not update nested section properties'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherNestedSectionPropertiesAreInserted
|
||||
*/
|
||||
public function testWhetherNestedSectionPropertiesAreDeleted()
|
||||
{
|
||||
$target = $this->writeConfigToTemporaryFile(<<<'EOD'
|
||||
[section]
|
||||
a.b = "c"
|
||||
EOD
|
||||
);
|
||||
$config = Config::fromArray(array('section' => array()));
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertNull(
|
||||
$newConfig->get('section')->get('a'),
|
||||
'IniWriter does not delete nested section properties'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherSimplePropertiesOfExtendingSectionsAreInserted()
|
||||
{
|
||||
$this->markTestSkipped(
|
||||
'Implementation has changed. There is no "Extend" functionality anymore in our Config object'
|
||||
);
|
||||
$target = $this->writeConfigToTemporaryFile('');
|
||||
$config = Config::fromArray(
|
||||
array(
|
||||
'foo' => array('key1' => '1'),
|
||||
'bar' => array('key2' => '2')
|
||||
)
|
||||
);
|
||||
$config->setExtend('bar', 'foo');
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertInstanceOf(
|
||||
get_class($newConfig),
|
||||
$newConfig->get('foo'),
|
||||
'IniWriter does not insert extended sections'
|
||||
);
|
||||
$this->assertInstanceOf(
|
||||
get_class($newConfig),
|
||||
$newConfig->get('bar'),
|
||||
'IniWriter does not insert extending sections'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'2',
|
||||
$newConfig->get('bar')->get('key2'),
|
||||
'IniWriter does not insert simple properties into extending sections'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'1',
|
||||
$newConfig->get('foo')->get('key1'),
|
||||
'IniWriter does not properly define extending sections'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherSimplePropertiesOfExtendingSectionsAreInserted
|
||||
*/
|
||||
public function testWhetherSimplePropertiesOfExtendingSectionsAreUpdated()
|
||||
{
|
||||
$this->markTestSkipped(
|
||||
'Implementation has changed. There is no "Extend" functionality anymore in our Config object'
|
||||
);
|
||||
$target = $this->writeConfigToTemporaryFile(<<<'EOD'
|
||||
[foo]
|
||||
key1 = "1"
|
||||
|
||||
[bar : foo]
|
||||
key2 = "2"
|
||||
EOD
|
||||
);
|
||||
$config = Config::fromArray(
|
||||
array(
|
||||
'foo' => array('key1' => '1'),
|
||||
'bar' => array('key2' => '22')
|
||||
)
|
||||
);
|
||||
$config->setExtend('bar', 'foo');
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertEquals(
|
||||
'22',
|
||||
$newConfig->get('bar')->get('key2'),
|
||||
'IniWriter does not update simple properties of extending sections'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherSimplePropertiesOfExtendingSectionsAreInserted
|
||||
*/
|
||||
public function testWhetherSimplePropertiesOfExtendingSectionsAreDeleted()
|
||||
{
|
||||
$this->markTestSkipped(
|
||||
'Implementation has changed. There is no "Extend" functionality anymore in our Config object'
|
||||
);
|
||||
$target = $this->writeConfigToTemporaryFile(<<<'EOD'
|
||||
[foo]
|
||||
key1 = "1"
|
||||
|
||||
[bar : foo]
|
||||
key2 = "2"
|
||||
EOD
|
||||
);
|
||||
$config = Config::fromArray(
|
||||
array(
|
||||
'foo' => array('key1' => '1'),
|
||||
'bar' => array()
|
||||
)
|
||||
);
|
||||
$config->setExtend('bar', 'foo');
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertNull(
|
||||
$newConfig->get('bar')->get('key2'),
|
||||
'IniWriter does not delete simple properties of extending sections'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherNestedPropertiesOfExtendingSectionsAreInserted()
|
||||
{
|
||||
$this->markTestSkipped(
|
||||
'Implementation has changed. There is no "Extend" functionality anymore in our Config object'
|
||||
);
|
||||
$target = $this->writeConfigToTemporaryFile('');
|
||||
$config = Config::fromArray(
|
||||
array(
|
||||
'foo' => array('a' => array('b' => 'c')),
|
||||
'bar' => array('d' => array('e' => 'f'))
|
||||
)
|
||||
);
|
||||
$config->setExtend('bar', 'foo');
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertInstanceOf(
|
||||
get_class($newConfig),
|
||||
$newConfig->get('foo'),
|
||||
'IniWriter does not insert extended sections'
|
||||
);
|
||||
$this->assertInstanceOf(
|
||||
get_class($newConfig),
|
||||
$newConfig->get('bar'),
|
||||
'IniWriter does not insert extending sections'
|
||||
);
|
||||
$this->assertInstanceOf(
|
||||
get_class($newConfig),
|
||||
$newConfig->get('bar')->get('d'),
|
||||
'IniWriter does not insert nested properties into extending sections'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'f',
|
||||
$newConfig->get('bar')->get('d')->get('e'),
|
||||
'IniWriter does not insert nested properties into extending sections'
|
||||
);
|
||||
$this->assertEquals(
|
||||
'c',
|
||||
$newConfig->get('bar')->get('a')->get('b'),
|
||||
'IniWriter does not properly define extending sections with nested properties'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherNestedPropertiesOfExtendingSectionsAreInserted
|
||||
*/
|
||||
public function testWhetherNestedPropertiesOfExtendingSectionsAreUpdated()
|
||||
{
|
||||
$target = $this->writeConfigToTemporaryFile(<<<'EOD'
|
||||
[foo]
|
||||
a.b = "c"
|
||||
|
||||
[bar : foo]
|
||||
d.e = "f"
|
||||
EOD
|
||||
);
|
||||
$config = Config::fromArray(
|
||||
array(
|
||||
'foo' => array('a' => array('b' => 'c')),
|
||||
'bar' => array('d' => array('e' => 'ff'))
|
||||
)
|
||||
);
|
||||
$config->setExtend('bar', 'foo');
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertEquals(
|
||||
'ff',
|
||||
$newConfig->get('bar')->get('d')->get('e'),
|
||||
'IniWriter does not update nested properties of extending sections'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherNestedPropertiesOfExtendingSectionsAreInserted
|
||||
*/
|
||||
public function testWhetherNestedPropertiesOfExtendingSectionsAreDeleted()
|
||||
{
|
||||
$this->markTestSkipped(
|
||||
'Implementation has changed. There is no "Extend" functionality anymore in our Config object'
|
||||
);
|
||||
$target = $this->writeConfigToTemporaryFile(<<<'EOD'
|
||||
[foo]
|
||||
a.b = "c"
|
||||
|
||||
[bar : foo]
|
||||
d.e = "f"
|
||||
EOD
|
||||
);
|
||||
$config = Config::fromArray(
|
||||
array(
|
||||
'foo' => array('a' => array('b' => 'c')),
|
||||
'bar' => array()
|
||||
)
|
||||
);
|
||||
$config->setExtend('bar', 'foo');
|
||||
$writer = new IniWriter(array('config' => $config, 'filename' => $target));
|
||||
$writer->write();
|
||||
|
||||
$newConfig = Config::fromIni($target);
|
||||
$this->assertNull(
|
||||
$newConfig->get('bar')->get('d'),
|
||||
'IniWriter does not delete nested properties of extending sections'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherSectionOrderIsUpdated()
|
||||
{
|
||||
$config = <<<'EOD'
|
||||
[one]
|
||||
key1 = "1"
|
||||
key2 = "2"
|
||||
key1 = "1"
|
||||
key2 = "2"
|
||||
|
||||
[two]
|
||||
a.b = "c"
|
||||
d.e = "f"
|
||||
a.b = "c"
|
||||
d.e = "f"
|
||||
|
||||
[three]
|
||||
key = "value"
|
||||
foo.bar = "raboof"
|
||||
key = "value"
|
||||
foo.bar = "raboof"
|
||||
EOD;
|
||||
|
||||
$reverted = <<<'EOD'
|
||||
[three]
|
||||
key = "value"
|
||||
foo.bar = "raboof"
|
||||
key = "value"
|
||||
foo.bar = "raboof"
|
||||
|
||||
[two]
|
||||
a.b = "c"
|
||||
d.e = "f"
|
||||
a.b = "c"
|
||||
d.e = "f"
|
||||
|
||||
[one]
|
||||
key1 = "1"
|
||||
key2 = "2"
|
||||
key1 = "1"
|
||||
key2 = "2"
|
||||
EOD;
|
||||
$target = $this->writeConfigToTemporaryFile($config);
|
||||
$writer = new IniWriter(
|
||||
array(
|
||||
'config' => Config::fromArray(
|
||||
array(
|
||||
'three' => array(
|
||||
'foo' => array(
|
||||
'bar' => 'raboof'
|
||||
),
|
||||
'key' => 'value'
|
||||
),
|
||||
'two' => array(
|
||||
'd' => array(
|
||||
'e' => 'f'
|
||||
),
|
||||
'a' => array(
|
||||
'b' => 'c'
|
||||
)
|
||||
),
|
||||
'one' => array(
|
||||
'key2' => '2',
|
||||
'key1' => '1'
|
||||
)
|
||||
Config::fromArray(
|
||||
array(
|
||||
'three' => array(
|
||||
'foo.bar' => 'raboof',
|
||||
'key' => 'value'
|
||||
),
|
||||
'two' => array(
|
||||
'd.e' => 'f',
|
||||
'a.b' => 'c'
|
||||
),
|
||||
'one' => array(
|
||||
'key2' => '2',
|
||||
'key1' => '1'
|
||||
)
|
||||
),
|
||||
'filename' => $target
|
||||
)
|
||||
)
|
||||
),
|
||||
$target
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
@ -598,15 +144,13 @@ EOD;
|
||||
EOD;
|
||||
$target = $this->writeConfigToTemporaryFile($config);
|
||||
$writer = new IniWriter(
|
||||
array(
|
||||
'config' => Config::fromArray(
|
||||
array(
|
||||
'two' => array(),
|
||||
'one' => array()
|
||||
)
|
||||
),
|
||||
'filename' => $target
|
||||
)
|
||||
Config::fromArray(
|
||||
array(
|
||||
'two' => array(),
|
||||
'one' => array()
|
||||
)
|
||||
),
|
||||
$target
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
@ -621,14 +165,14 @@ EOD;
|
||||
{
|
||||
$config = <<<'EOD'
|
||||
; some interesting comment
|
||||
key = "value"
|
||||
; another interesting comment
|
||||
[blarg]
|
||||
key = "value"
|
||||
|
||||
; some dangling comment
|
||||
; boring comment
|
||||
EOD;
|
||||
$target = $this->writeConfigToTemporaryFile($config);
|
||||
$writer = new IniWriter(
|
||||
array('config' => Config::fromArray(array('key' => 'value')), 'filename' => $target)
|
||||
);
|
||||
$writer = new IniWriter(Config::fromArray(array('blarg' => array('key' => 'value'))), $target);
|
||||
|
||||
$this->assertEquals(
|
||||
trim($config),
|
||||
@ -640,26 +184,24 @@ EOD;
|
||||
public function testWhetherCommentsOnPropertyLinesArePreserved()
|
||||
{
|
||||
$config = <<<'EOD'
|
||||
foo = 1337 ; I know what a " and a ' is
|
||||
bar = 7331 ; I; tend; to; overact; !1!1!!11!111! ;
|
||||
key = "value" ; some comment for a small sized property
|
||||
xxl = "very loooooooooooooooooooooong" ; my value is very lo...
|
||||
[blarg]
|
||||
foo = "1337" ; I know what a " and a ' is
|
||||
bar = "7331" ; I; tend; to; overact; !1!1!!11!111! ;
|
||||
key = "value" ; some comment for a small sized property
|
||||
xxl = "very loooooooooooooooooooooong" ; my value is very lo...
|
||||
EOD;
|
||||
$target = $this->writeConfigToTemporaryFile($config);
|
||||
$writer = new IniWriter(
|
||||
array(
|
||||
'config' => Config::fromArray(
|
||||
array(
|
||||
'foo' => 1337,
|
||||
'bar' => 7331,
|
||||
'key' => 'value',
|
||||
'xxl' => 'very loooooooooooooooooooooong'
|
||||
)
|
||||
),
|
||||
'filename' => $target
|
||||
)
|
||||
Config::fromArray(
|
||||
array('blarg' => array(
|
||||
'foo' => 1337,
|
||||
'bar' => 7331,
|
||||
'key' => 'value',
|
||||
'xxl' => 'very loooooooooooooooooooooong'
|
||||
))
|
||||
),
|
||||
$target
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
trim($config),
|
||||
trim($writer->render()),
|
||||
@ -672,12 +214,10 @@ EOD;
|
||||
$config = <<<'EOD'
|
||||
[section]
|
||||
; some interesting comment, in a section
|
||||
key = "value"
|
||||
key = "value"
|
||||
EOD;
|
||||
$target = $this->writeConfigToTemporaryFile($config);
|
||||
$writer = new IniWriter(
|
||||
array('config' => Config::fromArray(array('section' => array('key' => 'value'))), 'filename' => $target)
|
||||
);
|
||||
$writer = new IniWriter(Config::fromArray(array('section' => array('key' => 'value'))), $target);
|
||||
|
||||
$this->assertEquals(
|
||||
trim($config),
|
||||
@ -690,26 +230,24 @@ EOD;
|
||||
{
|
||||
$config = <<<'EOD'
|
||||
[section]
|
||||
foo = 1337 ; I know what a " and a ' is
|
||||
bar = 7331 ; I; tend; to; overact; !1!1!!11!111! ;
|
||||
key = "value" ; some comment for a small sized property
|
||||
xxl = "very loooooooooooooooooooooong" ; my value is very lo...
|
||||
foo = "1337" ; I know what a " and a ' is
|
||||
bar = "7331" ; I; tend; to; overact; !1!1!!11!111! ;
|
||||
key = "value" ; some comment for a small sized property
|
||||
xxl = "very loooooooooooooooooooooong" ; my value is very lo...
|
||||
EOD;
|
||||
$target = $this->writeConfigToTemporaryFile($config);
|
||||
$writer = new IniWriter(
|
||||
array(
|
||||
'config' => Config::fromArray(
|
||||
array(
|
||||
'section' => array(
|
||||
'foo' => 1337,
|
||||
'bar' => 7331,
|
||||
'key' => 'value',
|
||||
'xxl' => 'very loooooooooooooooooooooong'
|
||||
)
|
||||
Config::fromArray(
|
||||
array(
|
||||
'section' => array(
|
||||
'foo' => 1337,
|
||||
'bar' => 7331,
|
||||
'key' => 'value',
|
||||
'xxl' => 'very loooooooooooooooooooooong'
|
||||
)
|
||||
),
|
||||
'filename' => $target
|
||||
)
|
||||
)
|
||||
),
|
||||
$target
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
@ -723,19 +261,17 @@ EOD;
|
||||
{
|
||||
$target = $this->writeConfigToTemporaryFile('');
|
||||
$writer = new IniWriter(
|
||||
array(
|
||||
'config' => Config::fromArray(
|
||||
array(
|
||||
'section' => array(
|
||||
'foo' => 'linebreak
|
||||
Config::fromArray(
|
||||
array(
|
||||
'section' => array(
|
||||
'foo' => 'linebreak
|
||||
in line',
|
||||
'linebreak
|
||||
'linebreak
|
||||
inkey' => 'blarg'
|
||||
)
|
||||
)
|
||||
),
|
||||
'filename' => $target
|
||||
)
|
||||
)
|
||||
),
|
||||
$target
|
||||
);
|
||||
|
||||
$rendered = $writer->render();
|
||||
@ -746,6 +282,114 @@ inkey' => 'blarg'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSectionNameEscaping()
|
||||
{
|
||||
$config = <<<'EOD'
|
||||
[section [brackets\]]
|
||||
foo = "bar"
|
||||
|
||||
[section \;comment]
|
||||
foo = "bar"
|
||||
|
||||
[section \"quotes\"]
|
||||
foo = "bar"
|
||||
|
||||
[section with \\]
|
||||
foo = "bar"
|
||||
|
||||
[section with newline]
|
||||
foo = "bar"
|
||||
EOD;
|
||||
$target = $this->writeConfigToTemporaryFile($config);
|
||||
$writer = new IniWriter(
|
||||
Config::fromArray(
|
||||
array(
|
||||
'section [brackets]' => array('foo' => 'bar'),
|
||||
'section ;comment' => array('foo' => 'bar'),
|
||||
'section "quotes"' => array('foo' => 'bar'),
|
||||
'section with \\' => array('foo' => 'bar'),
|
||||
'section with' . PHP_EOL . 'newline' => array('foo' => 'bar')
|
||||
)
|
||||
),
|
||||
$target
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
trim($config),
|
||||
trim($writer->render()),
|
||||
'IniWriter does not handle special chars in section names properly.'
|
||||
);
|
||||
}
|
||||
|
||||
public function testDirectiveValueEscaping()
|
||||
{
|
||||
$config = <<<'EOD'
|
||||
[section]
|
||||
key1 = "value with \"quotes\""
|
||||
key2 = "value with \\"
|
||||
key3 = "value with newline"
|
||||
|
||||
EOD;
|
||||
$target = $this->writeConfigToTemporaryFile($config);
|
||||
$writer = new IniWriter(
|
||||
Config::fromArray(
|
||||
array(
|
||||
'section' => array(
|
||||
'key1' => 'value with "quotes"',
|
||||
'key2' => 'value with \\',
|
||||
'key3' => 'value with' . PHP_EOL . 'newline'
|
||||
)
|
||||
)
|
||||
),
|
||||
$target
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
trim($config),
|
||||
trim($writer->render()),
|
||||
'IniWriter does not handle special chars in directives properly.'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSectionDeleted()
|
||||
{
|
||||
$config = <<<'EOD'
|
||||
[section 1]
|
||||
guarg = "1"
|
||||
|
||||
[section 2]
|
||||
foo = "1337"
|
||||
foo2 = "baz"
|
||||
foo3 = "nope"
|
||||
foo4 = "bar"
|
||||
|
||||
[section 3]
|
||||
guard = "2"
|
||||
EOD;
|
||||
$deleted = <<<'EOD'
|
||||
[section 1]
|
||||
guarg = "1"
|
||||
|
||||
[section 3]
|
||||
guard = "2"
|
||||
EOD;
|
||||
|
||||
$target = $this->writeConfigToTemporaryFile($config);
|
||||
$writer = new IniWriter(
|
||||
Config::fromArray(array(
|
||||
'section 1' => array('guarg' => 1),
|
||||
'section 3' => array('guard' => 2)
|
||||
)),
|
||||
$target
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
trim($deleted),
|
||||
trim($writer->render()),
|
||||
'IniWriter does not delete sections properly'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a INI-configuration string to a temporary file and return its path
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user