Merge branch 'master' into bugfix/slow-unhandled-service-summary-query-in-host-overview-9864
This commit is contained in:
commit
3f0d073f43
|
@ -447,6 +447,7 @@ class Module
|
|||
*/
|
||||
public function getCssFiles()
|
||||
{
|
||||
$this->launchConfigScript();
|
||||
$files = $this->cssFiles;
|
||||
$files[] = $this->getCssFilename();
|
||||
return $files;
|
||||
|
@ -497,6 +498,7 @@ class Module
|
|||
*/
|
||||
public function getJsFiles()
|
||||
{
|
||||
$this->launchConfigScript();
|
||||
$files = $this->jsFiles;
|
||||
$files[] = $this->getJsFilename();
|
||||
return $files;
|
||||
|
|
|
@ -173,8 +173,10 @@ class ConfigObject extends ArrayDatasource implements Iterator, ArrayAccess
|
|||
/**
|
||||
* Add a new property or section
|
||||
*
|
||||
* @param string $key The name of the new property or section
|
||||
* @param mixed $value The value to set for the new property or section
|
||||
* @param string $key The name of the new property or section
|
||||
* @param mixed $value The value to set for the new property or section
|
||||
*
|
||||
* @throws ProgrammingError If the key is null
|
||||
*/
|
||||
public function offsetSet($key, $value)
|
||||
{
|
||||
|
@ -254,7 +256,7 @@ class ConfigObject extends ArrayDatasource implements Iterator, ArrayAccess
|
|||
/**
|
||||
* Merge the given data with this config
|
||||
*
|
||||
* @param array|Config $data An array or a config
|
||||
* @param array|ConfigObject $data An array or a config
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
|
|
|
@ -3,14 +3,31 @@
|
|||
|
||||
namespace Icinga\File\Ini\Dom;
|
||||
|
||||
/**
|
||||
* A single comment-line in an INI file
|
||||
*/
|
||||
class Comment
|
||||
{
|
||||
/**
|
||||
* The comment text
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $content;
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* Set the text content of this comment
|
||||
*
|
||||
* @param $content
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render this comment into INI markup
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
|
|
|
@ -3,42 +3,57 @@
|
|||
|
||||
namespace Icinga\File\Ini\Dom;
|
||||
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
|
||||
/**
|
||||
* A key value pair in a Section
|
||||
*/
|
||||
class Directive
|
||||
{
|
||||
/**
|
||||
* The value of this configuration directive
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* The immutable name of this configuration directive
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $commentsPre;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $commentPost;
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* Comments added one line before this directive
|
||||
*
|
||||
* @throws Exception
|
||||
* @var Comment[] The comment lines
|
||||
*/
|
||||
protected $commentsPre = null;
|
||||
|
||||
/**
|
||||
* Comment added at the end of the same line
|
||||
*
|
||||
* @var Comment
|
||||
*/
|
||||
protected $commentPost = null;
|
||||
|
||||
/**
|
||||
* @param string $key The name of this configuration directive
|
||||
*
|
||||
* @throws ConfigurationError
|
||||
*/
|
||||
public function __construct($key)
|
||||
{
|
||||
$this->key = trim($key);
|
||||
if (strlen($this->key) < 1) {
|
||||
throw new Exception(sprintf('Ini parser error: empty key.'));
|
||||
throw new ConfigurationError(sprintf('Ini error: empty directive key.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of this directive
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getKey()
|
||||
|
@ -47,7 +62,9 @@ class Directive
|
|||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* Return the value of this configuration directive
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
|
@ -55,7 +72,9 @@ class Directive
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* Set the value of this configuration directive
|
||||
*
|
||||
* @param string $value
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
|
@ -63,7 +82,39 @@ class Directive
|
|||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* Set the comments to be rendered on the line before this directive
|
||||
*
|
||||
* @param Comment[] $comments
|
||||
*/
|
||||
public function setCommentsPre(array $comments)
|
||||
{
|
||||
$this->commentsPre = $comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the comments to be rendered on the line before this directive
|
||||
*
|
||||
* @return Comment[]
|
||||
*/
|
||||
public function getCommentsPre()
|
||||
{
|
||||
return $this->commentsPre;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the comment rendered on the same line of this directive
|
||||
*
|
||||
* @param Comment $comment
|
||||
*/
|
||||
public function setCommentPost(Comment $comment)
|
||||
{
|
||||
$this->commentPost = $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render this configuration directive into INI markup
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
|
@ -82,16 +133,35 @@ class Directive
|
|||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assure that the given identifier contains no newlines and pending or trailing whitespaces
|
||||
*
|
||||
* @param $str The string to sanitize
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function sanitizeKey($str)
|
||||
{
|
||||
return trim(str_replace(PHP_EOL, ' ', $str));
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape the significant characters in directive values, normalize line breaks and assure that
|
||||
* the character contains no linebreaks
|
||||
*
|
||||
* @param $str The string to sanitize
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
protected function sanitizeValue($str)
|
||||
{
|
||||
$str = trim($str);
|
||||
$str = str_replace('\\', '\\\\', $str);
|
||||
$str = str_replace('"', '\\"', $str);
|
||||
return str_replace(PHP_EOL, ' ', $str);
|
||||
|
||||
// line breaks in the value should always match the current system EOL sequence
|
||||
// to assure editable configuration files
|
||||
$str = preg_replace("/(\r\n)|(\n)/", PHP_EOL, $str);
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,16 +6,22 @@ namespace Icinga\File\Ini\Dom;
|
|||
class Document
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
* The sections of this INI file
|
||||
*
|
||||
* @var Section[]
|
||||
*/
|
||||
protected $sections = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* The comemnts at file end that belong to no particular section
|
||||
*
|
||||
* @var Comment[]
|
||||
*/
|
||||
public $commentsDangling;
|
||||
protected $commentsDangling;
|
||||
|
||||
/**
|
||||
* Append a section to the end of this INI file
|
||||
*
|
||||
* @param Section $section
|
||||
*/
|
||||
public function addSection(Section $section)
|
||||
|
@ -24,6 +30,8 @@ class Document
|
|||
}
|
||||
|
||||
/**
|
||||
* Return whether this INI file has the section with the given key
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
|
@ -34,6 +42,8 @@ class Document
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the section with the given name
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return Section
|
||||
|
@ -44,6 +54,8 @@ class Document
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the section with the given name
|
||||
*
|
||||
* @param string $name
|
||||
* @param Section $section
|
||||
*
|
||||
|
@ -55,6 +67,8 @@ class Document
|
|||
}
|
||||
|
||||
/**
|
||||
* Remove the section with the given name
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function removeSection($name)
|
||||
|
@ -63,6 +77,28 @@ class Document
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the dangling comments at file end that belong to no particular directive
|
||||
*
|
||||
* @param Comment[] $comments
|
||||
*/
|
||||
public function setCommentsDangling(array $comments)
|
||||
{
|
||||
$this->commentsDangling = $comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dangling comments at file end that belong to no particular directive
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCommentsDangling()
|
||||
{
|
||||
return $this->commentsDangling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render this document into the corresponding INI markup
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
|
|
|
@ -5,30 +5,43 @@ namespace Icinga\File\Ini\Dom;
|
|||
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
|
||||
/**
|
||||
* A section in an INI file
|
||||
*/
|
||||
class Section
|
||||
{
|
||||
/**
|
||||
* The immutable name of this section
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* All configuration directives of this section
|
||||
*
|
||||
* @var Directive[]
|
||||
*/
|
||||
protected $directives = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* Comments added one line before this section
|
||||
*
|
||||
* @var Comment[]
|
||||
*/
|
||||
public $commentsPre;
|
||||
protected $commentsPre;
|
||||
|
||||
/**
|
||||
* Comment added at the end of the same line
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $commentPost;
|
||||
protected $commentPost;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $name The immutable name of this section
|
||||
*
|
||||
* @throws ConfigurationError When the section name is empty
|
||||
*/
|
||||
public function __construct($name)
|
||||
{
|
||||
|
@ -39,7 +52,9 @@ class Section
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Directive $directive
|
||||
* Append a directive to the end of this section
|
||||
*
|
||||
* @param Directive $directive The directive to append
|
||||
*/
|
||||
public function addDirective(Directive $directive)
|
||||
{
|
||||
|
@ -47,7 +62,9 @@ class Section
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* Remove the directive with the given name
|
||||
*
|
||||
* @param string $key They name of the directive to remove
|
||||
*/
|
||||
public function removeDirective($key)
|
||||
{
|
||||
|
@ -55,7 +72,9 @@ class Section
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* Return whether this section has a directive with the given key
|
||||
*
|
||||
* @param string $key The name of the directive
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
|
@ -65,6 +84,8 @@ class Section
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the directive with the given key
|
||||
*
|
||||
* @param $key string
|
||||
*
|
||||
* @return Directive
|
||||
|
@ -75,7 +96,9 @@ class Section
|
|||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* Return the name of this section
|
||||
*
|
||||
* @return string The name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
|
@ -83,6 +106,28 @@ class Section
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the comments to be rendered on the line before this section
|
||||
*
|
||||
* @param Comment[] $comments
|
||||
*/
|
||||
public function setCommentsPre(array $comments)
|
||||
{
|
||||
$this->commentsPre = $comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the comment rendered on the same line of this section
|
||||
*
|
||||
* @param Comment $comment
|
||||
*/
|
||||
public function setCommentPost(Comment $comment)
|
||||
{
|
||||
$this->commentPost = $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render this section into INI markup
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
|
@ -90,7 +135,9 @@ class Section
|
|||
$dirs = '';
|
||||
$i = 0;
|
||||
foreach ($this->directives as $directive) {
|
||||
$dirs .= (($i++ > 0 && ! empty($directive->commentsPre)) ? PHP_EOL : '') . $directive->render() . PHP_EOL;
|
||||
$comments = $directive->getCommentsPre();
|
||||
$dirs .= (($i++ > 0 && ! empty($comments)) ? PHP_EOL : '')
|
||||
. $directive->render() . PHP_EOL;
|
||||
}
|
||||
$cms = '';
|
||||
if (! empty($this->commentsPre)) {
|
||||
|
@ -106,6 +153,13 @@ class Section
|
|||
return $cms . sprintf('[%s]', $this->sanitize($this->name)) . $post . PHP_EOL . $dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape the significant characters in sections and normalize line breaks
|
||||
*
|
||||
* @param $str The string to sanitize
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function sanitize($str)
|
||||
{
|
||||
$str = trim($str);
|
||||
|
|
|
@ -23,6 +23,14 @@ class IniParser
|
|||
const COMMENT_END = 9;
|
||||
const LINE_END = 10;
|
||||
|
||||
/**
|
||||
* Cancel the parsing with an error
|
||||
*
|
||||
* @param $message The error description
|
||||
* @param $line The line in which the error occured
|
||||
*
|
||||
* @throws ConfigurationError
|
||||
*/
|
||||
private static function throwParseError($message, $line)
|
||||
{
|
||||
throw new ConfigurationError(sprintf('Ini parser error: %s. (l. %d)', $message, $line));
|
||||
|
@ -85,7 +93,7 @@ class IniParser
|
|||
$token .= $s;
|
||||
} else {
|
||||
$sec = new Section($token);
|
||||
$sec->commentsPre = $coms;
|
||||
$sec->setCommentsPre($coms);
|
||||
$doc->addSection($sec);
|
||||
$dir = null;
|
||||
$coms = array();
|
||||
|
@ -100,8 +108,7 @@ class IniParser
|
|||
$token .= $s;
|
||||
} else {
|
||||
$dir = new Directive($token);
|
||||
$dir->commentsPre = $coms;
|
||||
|
||||
$dir->setCommentsPre($coms);
|
||||
if (isset($sec)) {
|
||||
$sec->addDirective($dir);
|
||||
} else {
|
||||
|
@ -151,9 +158,7 @@ class IniParser
|
|||
break;
|
||||
|
||||
case self::DIRECTIVE_VALUE_QUOTED:
|
||||
if ($s === "\n") {
|
||||
self::throwParseError('Unterminated DIRECTIVE_VALUE_QUOTED', $line);
|
||||
} elseif ($s === '\\') {
|
||||
if ($s === '\\') {
|
||||
$state = self::ESCAPE;
|
||||
$escaping = self::DIRECTIVE_VALUE_QUOTED;
|
||||
} elseif ($s !== '"') {
|
||||
|
@ -171,16 +176,16 @@ class IniParser
|
|||
$token .= $s;
|
||||
} else {
|
||||
$com = new Comment();
|
||||
$com->content = $token;
|
||||
$com->setContent($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;
|
||||
$dir->setCommentPost($com);
|
||||
} else {
|
||||
$sec->commentPost = $com;
|
||||
$sec->setCommentPost($com);
|
||||
}
|
||||
} else {
|
||||
$coms[] = $com;
|
||||
|
@ -206,12 +211,12 @@ class IniParser
|
|||
case self::COMMENT:
|
||||
case self::COMMENT_END:
|
||||
$com = new Comment();
|
||||
$com->content = $token;
|
||||
$com->setContent($token);
|
||||
if ($state === self::COMMENT_END) {
|
||||
if (isset($dir)) {
|
||||
$dir->commentPost = $com;
|
||||
$dir->setCommentPost($com);
|
||||
} else {
|
||||
$sec->commentPost = $com;
|
||||
$sec->setCommentPost($com);
|
||||
}
|
||||
} else {
|
||||
$coms[] = $com;
|
||||
|
@ -230,7 +235,7 @@ class IniParser
|
|||
self::throwParseError('File ended in unterminated state ' . $state, $line);
|
||||
}
|
||||
if (! empty($coms)) {
|
||||
$doc->commentsDangling = $coms;
|
||||
$doc->setCommentsDangling($coms);
|
||||
}
|
||||
return $doc;
|
||||
}
|
||||
|
|
|
@ -117,7 +117,10 @@ class IniWriter
|
|||
protected function updateSectionOrder(Config $newconfig, Document $oldDoc)
|
||||
{
|
||||
$doc = new Document();
|
||||
$doc->commentsDangling = $oldDoc->commentsDangling;
|
||||
$dangling = $oldDoc->getCommentsDangling();
|
||||
if (isset($dangling)) {
|
||||
$doc->setCommentsDangling($dangling);
|
||||
}
|
||||
foreach ($newconfig->toArray() as $section => $directives) {
|
||||
$doc->addSection($oldDoc->getSection($section));
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ class Menu implements RecursiveIterator
|
|||
/**
|
||||
* The url of this menu
|
||||
*
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
|
@ -404,21 +404,20 @@ class Menu implements RecursiveIterator
|
|||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
if ($url instanceof Url) {
|
||||
$this->url = $url;
|
||||
} else {
|
||||
$this->url = Url::fromPath($url);
|
||||
}
|
||||
$this->url = $url;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the url of this menu
|
||||
*
|
||||
* @return Url
|
||||
* @return Url|null
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
if ($this->url !== null && ! $this->url instanceof Url) {
|
||||
$this->url = Url::fromPath($this->url);
|
||||
}
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class Dashlet extends UserWidget
|
|||
/**
|
||||
* The url of this Dashlet
|
||||
*
|
||||
* @var \Icinga\Web\Url
|
||||
* @var Url|null
|
||||
*/
|
||||
private $url;
|
||||
|
||||
|
@ -74,16 +74,13 @@ EOD;
|
|||
{
|
||||
$this->title = $title;
|
||||
$this->pane = $pane;
|
||||
if ($url instanceof Url) {
|
||||
$this->url = $url;
|
||||
} elseif ($url) {
|
||||
$this->url = Url::fromPath($url);
|
||||
} else {
|
||||
if (! $url) {
|
||||
throw new IcingaException(
|
||||
'Cannot create dashboard dashlet "%s" without valid URL',
|
||||
$title
|
||||
);
|
||||
}
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,10 +104,13 @@ EOD;
|
|||
/**
|
||||
* Retrieve the dashlets url
|
||||
*
|
||||
* @return Url
|
||||
* @return Url|null
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
if ($this->url !== null && ! $this->url instanceof Url) {
|
||||
$this->url = Url::fromPath($this->url);
|
||||
}
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
|
@ -123,11 +123,7 @@ EOD;
|
|||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
if ($url instanceof Url) {
|
||||
$this->url = $url;
|
||||
} else {
|
||||
$this->url = Url::fromPath($url);
|
||||
}
|
||||
$this->url = $url;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -159,7 +155,7 @@ EOD;
|
|||
public function toArray()
|
||||
{
|
||||
$array = array(
|
||||
'url' => $this->url->getRelativeUrl(),
|
||||
'url' => $this->getUrl()->getRelativeUrl(),
|
||||
'title' => $this->getTitle()
|
||||
);
|
||||
if ($this->getDisabled() === true) {
|
||||
|
@ -178,9 +174,9 @@ EOD;
|
|||
}
|
||||
|
||||
$view = $this->view();
|
||||
$url = clone($this->url);
|
||||
$url = $this->getUrl();
|
||||
$url->setParam('view', 'compact');
|
||||
$iframeUrl = clone($url);
|
||||
$iframeUrl = clone $url;
|
||||
$iframeUrl->setParam('isIframe');
|
||||
|
||||
$searchTokens = array(
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
namespace Icinga\Module\Doc;
|
||||
|
||||
use CachingIterator;
|
||||
use LogicException;
|
||||
use SplStack;
|
||||
use Icinga\Data\Tree\SimpleTree;
|
||||
|
@ -15,6 +16,20 @@ use Icinga\Module\Doc\Exception\DocException;
|
|||
*/
|
||||
class DocParser
|
||||
{
|
||||
/**
|
||||
* Internal identifier for Atx-style headers
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const HEADER_ATX = 1;
|
||||
|
||||
/**
|
||||
* Internal identifier for Setext-style headers
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const HEADER_SETEXT = 2;
|
||||
|
||||
/**
|
||||
* Path to the documentation
|
||||
*
|
||||
|
@ -70,11 +85,11 @@ class DocParser
|
|||
* Extract atx- or setext-style headers from the given lines
|
||||
*
|
||||
* @param string $line
|
||||
* @param string $lastLine
|
||||
* @param string $nextLine
|
||||
*
|
||||
* @return array|null An array containing the header and the header level or null if there's nothing to extract
|
||||
*/
|
||||
protected function extractHeader($line, $lastLine)
|
||||
protected function extractHeader($line, $nextLine)
|
||||
{
|
||||
if (! $line) {
|
||||
return null;
|
||||
|
@ -90,13 +105,14 @@ class DocParser
|
|||
if (! $header) {
|
||||
return null;
|
||||
}
|
||||
$headerStyle = static::HEADER_ATX;
|
||||
} elseif (
|
||||
$line
|
||||
&& ($line[0] === '=' || $line[0] === '-')
|
||||
&& preg_match('/^[=-]+\s*$/', $line, $match) === 1
|
||||
$nextLine
|
||||
&& ($nextLine[0] === '=' || $nextLine[0] === '-')
|
||||
&& preg_match('/^[=-]+\s*$/', $nextLine, $match) === 1
|
||||
) {
|
||||
// Setext
|
||||
$header = trim($lastLine);
|
||||
$header = trim($line);
|
||||
if (! $header) {
|
||||
return null;
|
||||
}
|
||||
|
@ -105,6 +121,7 @@ class DocParser
|
|||
} else {
|
||||
$level = 2;
|
||||
}
|
||||
$headerStyle = static::HEADER_SETEXT;
|
||||
}
|
||||
if ($header === null) {
|
||||
return null;
|
||||
|
@ -117,7 +134,7 @@ class DocParser
|
|||
} else {
|
||||
$id = null;
|
||||
}
|
||||
return array($header, $id, $level);
|
||||
return array($header, $id, $level, $headerStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,15 +145,18 @@ class DocParser
|
|||
public function getDocTree()
|
||||
{
|
||||
$tree = new SimpleTree();
|
||||
$stack = new SplStack();
|
||||
foreach ($this->docIterator as $fileInfo) {
|
||||
/** @var $fileInfo \SplFileInfo */
|
||||
$file = $fileInfo->openFile();
|
||||
$lastLine = null;
|
||||
foreach ($file as $line) {
|
||||
$header = $this->extractHeader($line, $lastLine);
|
||||
$stack = new SplStack();
|
||||
$cachingIterator = new CachingIterator($file, CachingIterator::TOSTRING_USE_CURRENT);
|
||||
for ($cachingIterator->rewind(); $line = $cachingIterator->valid(); $cachingIterator->next()) {
|
||||
$fileIterator = $cachingIterator->getInnerIterator();
|
||||
$line = $cachingIterator->current();
|
||||
$header = $this->extractHeader($line, $fileIterator->valid() ? $fileIterator->current() : null);
|
||||
if ($header !== null) {
|
||||
list($title, $id, $level) = $header;
|
||||
list($title, $id, $level, $headerStyle) = $header;
|
||||
while (! $stack->isEmpty() && $stack->top()->getLevel() >= $level) {
|
||||
$stack->pop();
|
||||
}
|
||||
|
@ -169,14 +189,29 @@ class DocParser
|
|||
$tree->addChild($section, $stack->top());
|
||||
}
|
||||
$stack->push($section);
|
||||
if ($headerStyle === static::HEADER_SETEXT) {
|
||||
$cachingIterator->next();
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if ($stack->isEmpty()) {
|
||||
throw new LogicException('Heading required');
|
||||
$title = ucfirst($file->getBasename('.' . pathinfo($file->getFilename(), PATHINFO_EXTENSION)));
|
||||
$id = $title;
|
||||
if ($tree->getNode($id) !== null) {
|
||||
$id = uniqid($id);
|
||||
}
|
||||
$section = new DocSection();
|
||||
$section
|
||||
->setId($id)
|
||||
->setTitle($title)
|
||||
->setLevel(1)
|
||||
->setNoFollow(true);
|
||||
$section->setChapter($section);
|
||||
$tree->addChild($section);
|
||||
$stack->push($section);
|
||||
}
|
||||
$stack->top()->appendContent($line);
|
||||
}
|
||||
// Save last line for setext-style headers
|
||||
$lastLine = $line;
|
||||
}
|
||||
}
|
||||
return $tree;
|
||||
|
|
|
@ -88,6 +88,14 @@ class DocSection extends TreeNode
|
|||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
return parent::setId(str_replace(' ', '-', (string) $id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the header level
|
||||
*
|
||||
|
|
|
@ -35,9 +35,15 @@ if (! $this->compact): ?>
|
|||
?>
|
||||
<tr class="state <?= $stateName; ?><?= $downtime->is_in_effect ? ' handled' : ''; ?>">
|
||||
<td class="state">
|
||||
<?php if ($downtime->start <= time() && ! $downtime->is_in_effect): ?>
|
||||
<strong><?= $this->translate('Ends'); ?></strong>
|
||||
<br>
|
||||
<?= $this->timeUntil($downtime->is_flexible ? $downtime->scheduled_end : $downtime->end, $this->compact) ?>
|
||||
<?php else: ?>
|
||||
<strong><?= $downtime->is_in_effect ? $this->translate('Expires') : $this->translate('Starts'); ?></strong>
|
||||
<br>
|
||||
<?= $this->timeUntil($downtime->is_in_effect ? $downtime->end : $downtime->start, $this->compact) ?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($isService): ?>
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
<table class="action">
|
||||
<tr class="state <?= $stateName; ?><?= $downtime->is_in_effect ? ' handled' : ''; ?>">
|
||||
<td class="state">
|
||||
<strong><?= $downtime->is_in_effect ? $this->translate('Expires') : $this->translate('Starts'); ?></strong>
|
||||
<br>
|
||||
<?= $this->timeUntil($downtime->is_in_effect ? $downtime->end : $downtime->start, $this->compact) ?>
|
||||
<?php if ($downtime->start <= time() && ! $downtime->is_in_effect): ?>
|
||||
<strong><?= $this->translate('Ends'); ?></strong>
|
||||
<br>
|
||||
<?= $this->timeUntil($downtime->is_flexible ? $downtime->scheduled_end : $downtime->end, $this->compact) ?>
|
||||
<?php else: ?>
|
||||
<strong><?= $downtime->is_in_effect ? $this->translate('Expires') : $this->translate('Starts'); ?></strong>
|
||||
<br>
|
||||
<?= $this->timeUntil($downtime->is_in_effect ? $downtime->end : $downtime->start, $this->compact) ?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<small>
|
||||
|
|
|
@ -6,9 +6,15 @@
|
|||
} ?>
|
||||
<tr class="state <?= $downtime->stateText ?>">
|
||||
<td class="state">
|
||||
<?php if ($downtime->start <= time() && ! $downtime->is_in_effect): ?>
|
||||
<strong><?= $this->translate('Ends'); ?></strong>
|
||||
<br>
|
||||
<?= $this->timeUntil($downtime->is_flexible ? $downtime->scheduled_end : $downtime->end, $this->compact) ?>
|
||||
<?php else: ?>
|
||||
<strong><?= $downtime->is_in_effect ? $this->translate('Expires') : $this->translate('Starts'); ?></strong>
|
||||
<br>
|
||||
<?= $this->timeUntil($downtime->is_in_effect ? $downtime->end : $downtime->start, $this->compact) ?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="name oneline">
|
||||
<?php if ($downtime->isService): ?>
|
||||
|
|
|
@ -53,7 +53,12 @@ if (empty($object->comments) && ! $addLink) {
|
|||
$this->timeUntil($downtime->end)
|
||||
);
|
||||
} else {
|
||||
if ((bool) $downtime->is_fixed) {
|
||||
if ($downtime->start <= time()) {
|
||||
$state = sprintf(
|
||||
$this->translate('ends %s', 'Last format parameter represents the end time'),
|
||||
$this->timeUntil($downtime->is_flexible ? $downtime->scheduled_end : $downtime->end)
|
||||
);
|
||||
} elseif ((bool) $downtime->is_fixed) {
|
||||
$state = sprintf(
|
||||
$this->translate('scheduled %s', 'Last format parameter represents the time scheduled'),
|
||||
$this->timeUntil($downtime->start)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -279,6 +279,7 @@ abstract class MonitoredObject implements Filterable
|
|||
'author_name' => 'downtime_author_name',
|
||||
'start' => 'downtime_start',
|
||||
'scheduled_start' => 'downtime_scheduled_start',
|
||||
'scheduled_end' => 'downtime_scheduled_end',
|
||||
'end' => 'downtime_end',
|
||||
'duration' => 'downtime_duration',
|
||||
'is_flexible' => 'downtime_is_flexible',
|
||||
|
|
|
@ -335,10 +335,6 @@ table.action td.state, table.objectstate td.state {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
table.action td.timesince {
|
||||
width: 3.5em;
|
||||
}
|
||||
|
||||
|
||||
/* State row behaviour */
|
||||
|
||||
|
@ -1117,10 +1113,6 @@ table.groupview {
|
|||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
span.timesince {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
&.ok {
|
||||
border-color: @colorOk;
|
||||
}
|
||||
|
|
|
@ -112,38 +112,69 @@
|
|||
|
||||
/**
|
||||
* Load a given module by name
|
||||
*
|
||||
* @param {string} name
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
loadModule: function (name) {
|
||||
|
||||
if (this.hasModule(name)) {
|
||||
if (this.isLoadedModule(name)) {
|
||||
this.logger.error('Cannot load module ' + name + ' twice');
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.modules[name] = new Icinga.Module(this, name);
|
||||
if (! this.hasModule(name)) {
|
||||
this.logger.error('Cannot find module ' + name);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.modules[name] = new Icinga.Module(
|
||||
this,
|
||||
name,
|
||||
Icinga.availableModules[name]
|
||||
);
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether a module matching the given name exists
|
||||
* Whether a module matching the given name exists or is loaded
|
||||
*
|
||||
* @param {string} name
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasModule: function (name) {
|
||||
return 'undefined' !== typeof this.modules[name] ||
|
||||
return this.isLoadedModule(name) ||
|
||||
'undefined' !== typeof Icinga.availableModules[name];
|
||||
},
|
||||
|
||||
/**
|
||||
* Return whether the given module is loaded
|
||||
*
|
||||
* @param {string} name The name of the module
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isLoadedModule: function (name) {
|
||||
return 'undefined' !== typeof this.modules[name];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a module by name
|
||||
*
|
||||
* @param {string} name
|
||||
*
|
||||
* @return {object}
|
||||
*/
|
||||
module: function (name) {
|
||||
|
||||
if ('undefined' === typeof this.modules[name]) {
|
||||
if ('undefined' !== typeof Icinga.availableModules[name]) {
|
||||
this.modules[name] = new Icinga.Module(
|
||||
this,
|
||||
name,
|
||||
Icinga.availableModules[name]
|
||||
);
|
||||
}
|
||||
if (this.hasModule(name) && !this.isLoadedModule(name)) {
|
||||
this.modules[name] = new Icinga.Module(
|
||||
this,
|
||||
name,
|
||||
Icinga.availableModules[name]
|
||||
);
|
||||
}
|
||||
|
||||
return this.modules[name];
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
this.icinga = icinga;
|
||||
|
||||
this.searchValue = '';
|
||||
this.initializeModules = true;
|
||||
};
|
||||
|
||||
Icinga.Events.prototype = {
|
||||
|
@ -30,39 +31,63 @@
|
|||
},
|
||||
|
||||
// TODO: What's this?
|
||||
applyHandlers: function (evt) {
|
||||
var el = $(evt.target), self = evt.data.self;
|
||||
applyHandlers: function (event) {
|
||||
var $target = $(event.target);
|
||||
var self = event.data.self;
|
||||
var icinga = self.icinga;
|
||||
|
||||
$('.dashboard > div', el).each(function(idx, el) {
|
||||
var url = $(el).data('icingaUrl');
|
||||
if (typeof url === 'undefined') return;
|
||||
icinga.loader.loadUrl(url, $(el)).autorefresh = true;
|
||||
});
|
||||
|
||||
$('td.state span.timesince').attr('title', null);
|
||||
|
||||
var moduleName = el.data('icingaModule');
|
||||
if (moduleName) {
|
||||
if (icinga.hasModule(moduleName)) {
|
||||
var module = icinga.module(moduleName);
|
||||
// NOT YET, the applyOnloadDings: module.applyEventHandlers(mod);
|
||||
if (self.initializeModules) {
|
||||
var loaded = false;
|
||||
var moduleName = $target.data('icingaModule');
|
||||
if (moduleName) {
|
||||
if (icinga.hasModule(moduleName) && !icinga.isLoadedModule(moduleName)) {
|
||||
loaded |= icinga.loadModule(moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
$('.icinga-module', $target).each(function(idx, mod) {
|
||||
moduleName = $(mod).data('icingaModule');
|
||||
if (icinga.hasModule(moduleName) && !icinga.isLoadedModule(moduleName)) {
|
||||
loaded |= icinga.loadModule(moduleName);
|
||||
}
|
||||
});
|
||||
|
||||
if (loaded) {
|
||||
// Modules may register their own handler for the 'renderend' event
|
||||
// so we need to ensure that it is called the first time they are
|
||||
// initialized
|
||||
event.stopImmediatePropagation();
|
||||
self.initializeModules = false;
|
||||
|
||||
var $container = $target.closest('.container');
|
||||
if (! $container.length) {
|
||||
// The page obviously got loaded for the first time,
|
||||
// so we'll trigger the event for all containers
|
||||
$container = $('.container');
|
||||
}
|
||||
|
||||
$container.trigger('rendered');
|
||||
|
||||
// But since we're listening on this event by ourself, we'll have
|
||||
// to abort our own processing as we'll process it twice otherwise
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
self.initializeModules = true;
|
||||
}
|
||||
|
||||
$('.icinga-module', el).each(function(idx, mod) {
|
||||
var $mod = $(mod);
|
||||
moduleName = $mod.data('icingaModule');
|
||||
if (icinga.hasModule(moduleName)) {
|
||||
var module = icinga.module(moduleName);
|
||||
// NOT YET, the applyOnloadDings: module.applyEventHandlers(mod);
|
||||
$('.dashboard > div', $target).each(function(idx, el) {
|
||||
var $element = $(el);
|
||||
var $url = $element.data('icingaUrl');
|
||||
if (typeof $url !== 'undefined') {
|
||||
icinga.loader.loadUrl($url, $element).autorefresh = true;
|
||||
}
|
||||
});
|
||||
|
||||
var searchField = $('#menu input.search', el);
|
||||
var $searchField = $('#menu input.search', $target);
|
||||
// Remember initial search field value if any
|
||||
if (searchField.length && searchField.val().length) {
|
||||
self.searchValue = searchField.val();
|
||||
if ($searchField.length && $searchField.val().length) {
|
||||
self.searchValue = $searchField.val();
|
||||
}
|
||||
|
||||
if (icinga.ui.isOneColLayout()) {
|
||||
|
@ -76,13 +101,14 @@
|
|||
* Global default event handlers
|
||||
*/
|
||||
applyGlobalDefaults: function () {
|
||||
// Apply element-specific behavior whenever the layout is rendered
|
||||
// Note: It is important that this is the first handler for this event!
|
||||
$(document).on('rendered', { self: this }, this.applyHandlers);
|
||||
|
||||
$.each(self.icinga.behaviors, function (name, behavior) {
|
||||
behavior.bind($(document));
|
||||
});
|
||||
|
||||
// Apply element-specific behavior whenever the layout is rendered
|
||||
$(document).on('rendered', { self: this }, this.applyHandlers);
|
||||
|
||||
// We catch resize events
|
||||
$(window).on('resize', { self: this.icinga.ui }, this.icinga.ui.onWindowResize);
|
||||
|
||||
|
|
|
@ -60,4 +60,19 @@ EOD;
|
|||
'IniParser does not recognize escaped bracket in section'
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultilineValues()
|
||||
{
|
||||
$config = <<<'EOD'
|
||||
[section]
|
||||
key1 = "with some
|
||||
newline in the value"
|
||||
EOD;
|
||||
$doc = IniParser::parseIni($config);
|
||||
$this->assertEquals(
|
||||
2,
|
||||
count(explode("\n", $doc->getSection('section')->getDirective('key1')->getValue())),
|
||||
'IniParser does not recognize multi-line values'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -257,7 +257,7 @@ EOD;
|
|||
);
|
||||
}
|
||||
|
||||
public function testWhetherLinebreaksAreRemoved()
|
||||
public function testWhetherLinebreaksAreProcessed()
|
||||
{
|
||||
$target = $this->writeConfigToTemporaryFile('');
|
||||
$writer = new IniWriter(
|
||||
|
@ -277,7 +277,7 @@ inkey' => 'blarg'
|
|||
$rendered = $writer->render();
|
||||
$this->assertEquals(
|
||||
count(explode("\n", $rendered)),
|
||||
4,
|
||||
5,
|
||||
'generated config should not contain more than three line breaks'
|
||||
);
|
||||
}
|
||||
|
@ -327,7 +327,6 @@ EOD;
|
|||
[section]
|
||||
key1 = "value with \"quotes\""
|
||||
key2 = "value with \\"
|
||||
key3 = "value with newline"
|
||||
|
||||
EOD;
|
||||
$target = $this->writeConfigToTemporaryFile($config);
|
||||
|
@ -336,8 +335,7 @@ EOD;
|
|||
array(
|
||||
'section' => array(
|
||||
'key1' => 'value with "quotes"',
|
||||
'key2' => 'value with \\',
|
||||
'key3' => 'value with' . PHP_EOL . 'newline'
|
||||
'key2' => 'value with \\'
|
||||
)
|
||||
)
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue