From 7ecbefee795678505df2512cb6da9a2f9781659d Mon Sep 17 00:00:00 2001 From: Markus Frosch Date: Wed, 14 Oct 2015 16:34:22 +0200 Subject: [PATCH 001/337] Handle E_RECOVERABLE_ERROR with our error handler refs #10361 --- library/Icinga/Application/ApplicationBootstrap.php | 1 + 1 file changed, 1 insertion(+) diff --git a/library/Icinga/Application/ApplicationBootstrap.php b/library/Icinga/Application/ApplicationBootstrap.php index c46aa3a27..1e7bc8edb 100644 --- a/library/Icinga/Application/ApplicationBootstrap.php +++ b/library/Icinga/Application/ApplicationBootstrap.php @@ -493,6 +493,7 @@ abstract class ApplicationBootstrap case E_NOTICE: case E_WARNING: case E_STRICT: + case E_RECOVERABLE_ERROR: throw new ErrorException($errstr, 0, $errno, $errfile, $errline); } return false; // Continue with the normal error handler From c94228308afe1dd6a52c0013f28353af9de76e5e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 19 May 2016 11:37:12 +0200 Subject: [PATCH 002/337] PluginOutput: preserve character after comma refs #11728 --- modules/monitoring/application/views/helpers/PluginOutput.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php index ba3013a3a..42b26baef 100644 --- a/modules/monitoring/application/views/helpers/PluginOutput.php +++ b/modules/monitoring/application/views/helpers/PluginOutput.php @@ -54,7 +54,7 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract // Help browsers to break words in plugin output $output = trim($output); // Add space after comma where missing - $output = preg_replace('/,[^\s]/', ', ', $output); + $output = preg_replace('/,(?=[^\s])/', ', ', $output); // Add zero width space after ')', ']', ':', '.', '_' and '-' if not surrounded by whitespaces $output = preg_replace('/([^\s])([\\)\\]:._-])([^\s])/', '$1$2​$3', $output); // Add zero width space before '(' and '[' if not surrounded by whitespaces From 06755ba7789fc8f95d5eb89d664cee6d69072e32 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 19 May 2016 13:45:15 +0200 Subject: [PATCH 003/337] Add regression test for bug #11728 refs #11728 --- .../test/php/regression/Bug11728Test.php | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 modules/monitoring/test/php/regression/Bug11728Test.php diff --git a/modules/monitoring/test/php/regression/Bug11728Test.php b/modules/monitoring/test/php/regression/Bug11728Test.php new file mode 100644 index 000000000..90b4cbe10 --- /dev/null +++ b/modules/monitoring/test/php/regression/Bug11728Test.php @@ -0,0 +1,25 @@ +assertTrue(strpos($helper->pluginOutput('A,BC', true), 'BC') !== false); + } +} From 10d2b9c51d8660ca732490976b2f628a40cecd21 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 19 May 2016 18:38:36 +0200 Subject: [PATCH 004/337] Don't destroy links by adding zero width space characters to plugin output refs #11796 --- .../views/helpers/PluginOutput.php | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php index ba3013a3a..b78417db0 100644 --- a/modules/monitoring/application/views/helpers/PluginOutput.php +++ b/modules/monitoring/application/views/helpers/PluginOutput.php @@ -27,6 +27,16 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract '@@@@@@', ); + /** + * @var string + */ + protected $zeroWidthSpace; + + public function __construct() + { + $this->zeroWidthSpace = html_entity_decode('​', ENT_NOQUOTES, 'UTF-8'); + } + public function pluginOutput($output, $raw = false) { if (empty($output)) { @@ -50,15 +60,11 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract ); $isHtml = false; } - $output = $this->fixLinks($output); + $output = $this->fixLinksAndWrapping($output); // Help browsers to break words in plugin output $output = trim($output); // Add space after comma where missing $output = preg_replace('/,[^\s]/', ', ', $output); - // Add zero width space after ')', ']', ':', '.', '_' and '-' if not surrounded by whitespaces - $output = preg_replace('/([^\s])([\\)\\]:._-])([^\s])/', '$1$2​$3', $output); - // Add zero width space before '(' and '[' if not surrounded by whitespaces - $output = preg_replace('/([^\s])([([])([^\s])/', '$1​$2$3', $output); if (! $raw) { if ($isHtml) { @@ -70,13 +76,14 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract return $output; } - protected function fixLinks($html) + protected function fixLinksAndWrapping($html) { $ret = array(); $dom = new DOMDocument; $dom->loadXML('
' . $html . '
', LIBXML_NOERROR | LIBXML_NOWARNING); $dom->preserveWhiteSpace = false; + $links = $dom->getElementsByTagName('a'); foreach ($links as $tag) { @@ -93,6 +100,9 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract } //$ret[$tag->getAttribute('href')] = $tag->childNodes->item(0)->nodeValue; } + + $this->fixWrappingRecursive($dom); + return substr($dom->saveHTML(), 5, -7); } @@ -119,4 +129,23 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract } return self::$purifier; } + + /** + * Add zero width space to all text in the DOM to make wrapping easier for the user agent + * + * @param DOMNode $node + */ + protected function fixWrappingRecursive(DOMNode $node) + { + if ($node instanceof DOMText) { + // Add zero width space after ')', ']', ':', '.', '_' and '-' if not surrounded by whitespaces + $data = preg_replace('/([^\s])([\\)\\]:._-])([^\s])/', '$1$2' . $this->zeroWidthSpace . '$3', $node->data); + // Add zero width space before '(' and '[' if not surrounded by whitespaces + $node->data = preg_replace('/([^\s])([([])([^\s])/', '$1' . $this->zeroWidthSpace . '$2$3', $data); + } elseif ($node->childNodes !== null) { + foreach ($node->childNodes as $childNode) { + $this->fixWrappingRecursive($childNode); + } + } + } } From 53d8278877a804cdc5a6fa5fe3ed26d831220505 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 20 May 2016 10:06:22 +0200 Subject: [PATCH 005/337] Add regression test for bug #11796 --- .../test/php/regression/Bug11796Test.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 modules/monitoring/test/php/regression/Bug11796Test.php diff --git a/modules/monitoring/test/php/regression/Bug11796Test.php b/modules/monitoring/test/php/regression/Bug11796Test.php new file mode 100644 index 000000000..f024334b7 --- /dev/null +++ b/modules/monitoring/test/php/regression/Bug11796Test.php @@ -0,0 +1,27 @@ +assertTrue( + strpos($helper->pluginOutput('
EXAMPLE.COM', true), 'example.com') !== false + ); + } +} From ac96d16af0a2b8d1098699b395439fdb74f7fca1 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 20 May 2016 12:48:50 +0200 Subject: [PATCH 006/337] PluginOutput: add doc refs #11796 --- .../views/helpers/PluginOutput.php | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php index b78417db0..7bb1cc462 100644 --- a/modules/monitoring/application/views/helpers/PluginOutput.php +++ b/modules/monitoring/application/views/helpers/PluginOutput.php @@ -1,10 +1,23 @@ zeroWidthSpace = html_entity_decode('​', ENT_NOQUOTES, 'UTF-8'); } + /** + * Render plugin output + * + * @param string $output + * @param bool $raw + * + * @return string + */ public function pluginOutput($output, $raw = false) { if (empty($output)) { @@ -76,6 +109,14 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract return $output; } + /** + * Replace classic Icinga CGI links with Icinga Web 2 links and + * add zero width space to make wrapping easier for the user agent + * + * @param string $html + * + * @return string + */ protected function fixLinksAndWrapping($html) { @@ -106,6 +147,11 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract return substr($dom->saveHTML(), 5, -7); } + /** + * Initialize and return self::$purifier + * + * @return HTMLPurifier + */ protected function getPurifier() { if (self::$purifier === null) { From 7cfdbfccdb2349ceaa70cfe2d523745396afd87d Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 20 May 2016 13:11:09 +0200 Subject: [PATCH 007/337] PluginOutput: fix links only if there is any HTML refs #11796 --- .../views/helpers/PluginOutput.php | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php index 7bb1cc462..b1b9a46ab 100644 --- a/modules/monitoring/application/views/helpers/PluginOutput.php +++ b/modules/monitoring/application/views/helpers/PluginOutput.php @@ -52,6 +52,13 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract */ protected $zeroWidthSpace; + /** + * The encoded character ​ + * + * @var string + */ + protected $zeroWidthSpaceEnt = '​'; + /** * Create a new Zend_View_Helper_PluginOutput */ @@ -59,7 +66,7 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract { // This is actually not required as the value is constant, // but as its (visual) length is 0, it's likely to be mixed up with the empty string. - $this->zeroWidthSpace = html_entity_decode('​', ENT_NOQUOTES, 'UTF-8'); + $this->zeroWidthSpace = html_entity_decode($this->zeroWidthSpaceEnt, ENT_NOQUOTES, 'UTF-8'); } /** @@ -84,16 +91,23 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract $this->getPurifier()->purify($output) ); $isHtml = true; + $useDom = true; } else { // Plaintext + $count = 0; $output = preg_replace( self::$txtPatterns, self::$txtReplacements, - $this->view->escape($output) + $this->view->escape($output), + -1, + $count ); $isHtml = false; + $useDom = (bool) $count; } - $output = $this->fixLinksAndWrapping($output); + + $output = $useDom ? $this->fixLinksAndWrapping($output) : $this->fixWrapping($output, $this->zeroWidthSpaceEnt); + // Help browsers to break words in plugin output $output = trim($output); // Add space after comma where missing @@ -184,14 +198,27 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract protected function fixWrappingRecursive(DOMNode $node) { if ($node instanceof DOMText) { - // Add zero width space after ')', ']', ':', '.', '_' and '-' if not surrounded by whitespaces - $data = preg_replace('/([^\s])([\\)\\]:._-])([^\s])/', '$1$2' . $this->zeroWidthSpace . '$3', $node->data); - // Add zero width space before '(' and '[' if not surrounded by whitespaces - $node->data = preg_replace('/([^\s])([([])([^\s])/', '$1' . $this->zeroWidthSpace . '$2$3', $data); + $node->data = $this->fixWrapping($node->data, $this->zeroWidthSpace); } elseif ($node->childNodes !== null) { foreach ($node->childNodes as $childNode) { $this->fixWrappingRecursive($childNode); } } } + + /** + * Add zero width space to make wrapping easier for the user agent + * + * @param string $output + * @param string $zeroWidthSpace + * + * @return string + */ + protected function fixWrapping($output, $zeroWidthSpace) + { + // Add zero width space after ')', ']', ':', '.', '_' and '-' if not surrounded by whitespaces + $output = preg_replace('/([^\s])([\\)\\]:._-])([^\s])/', '$1$2' . $zeroWidthSpace . '$3', $output); + // Add zero width space before '(' and '[' if not surrounded by whitespaces + return preg_replace('/([^\s])([([])([^\s])/', '$1' . $zeroWidthSpace . '$2$3', $output); + } } From af2cee094229e83d5fc69d669101d28a7809cdd3 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 20 May 2016 14:41:02 +0200 Subject: [PATCH 008/337] Make host and service severities independend of whether the state is soft or hard refs #11729 --- .../Monitoring/Backend/Ido/Query/HoststatusQuery.php | 5 ----- .../Backend/Ido/Query/ServicestatusQuery.php | 10 ---------- 2 files changed, 15 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HoststatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HoststatusQuery.php index ea466bf37..dd73dd3e5 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HoststatusQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HoststatusQuery.php @@ -148,11 +148,6 @@ class HoststatusQuery extends IdoQuery ELSE 4 END END - END - + - CASE WHEN hs.state_type = 1 - THEN 8 - ELSE 0 END', 'host_state' => 'CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL THEN 99 ELSE hs.current_state END', 'host_state_type' => 'hs.state_type', diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicestatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicestatusQuery.php index 99c37613d..3aadb1047 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicestatusQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicestatusQuery.php @@ -131,11 +131,6 @@ class ServicestatusQuery extends IdoQuery ELSE 4 END END - END - + - CASE WHEN hs.state_type = 1 - THEN 8 - ELSE 0 END', 'host_state' => 'CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL THEN 99 ELSE hs.current_state END', 'host_state_type' => 'hs.state_type', @@ -271,11 +266,6 @@ class ServicestatusQuery extends IdoQuery END END END - END - + - CASE WHEN ss.state_type = 1 - THEN 8 - ELSE 0 END', 'service_state' => 'CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL THEN 99 ELSE ss.current_state END', 'service_state_type' => 'ss.state_type', From 50037d8ec9516ee55a847f5f22c77426ccd5fbc0 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 31 May 2016 14:47:25 +0200 Subject: [PATCH 009/337] Tabs: fix wrong parameter for array_splice() refs #11850 --- library/Icinga/Web/Widget/Tabs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Widget/Tabs.php b/library/Icinga/Web/Widget/Tabs.php index 2f8094197..0c4c17ded 100644 --- a/library/Icinga/Web/Widget/Tabs.php +++ b/library/Icinga/Web/Widget/Tabs.php @@ -249,7 +249,7 @@ EOT; if ($this->has($name)) { unset($this->tabs[$name]); if (($dropdownIndex = array_search($name, $this->dropdownTabs, true)) !== false) { - array_splice($this->dropdownTabs, $dropdownIndex, 2); + array_splice($this->dropdownTabs, $dropdownIndex, 1); } } From ecfc8b0f0a8fd8c69d4331405c6bb6b491286f17 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 31 May 2016 14:56:42 +0200 Subject: [PATCH 010/337] Don't provide the "Add to menu" action for command forms refs #11850 --- .../Monitoring/Web/Controller/MonitoredObjectController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php b/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php index f7a0b8179..f4936fd99 100644 --- a/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php +++ b/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php @@ -129,6 +129,7 @@ abstract class MonitoredObjectController extends Controller $this->view->form = $form; $this->view->object = $this->object; $this->view->tabs->remove('dashboard'); + $this->view->tabs->remove('menu-entry'); $this->_helper->viewRenderer('partials/command/object-command-form', null, true); return $form; } From 3fed5e920f61c24146a85842c322d9025ffeadf0 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 31 May 2016 17:50:49 +0200 Subject: [PATCH 011/337] list/contactgroups: show empty usergroups, too refs #11744 --- .../library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php index af665cb35..f45c21e01 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php @@ -87,7 +87,7 @@ class ContactgroupQuery extends IdoQuery array('cgm' => $this->prefix . 'contactgroup_members'), 'cgm.contactgroup_id = cg.contactgroup_id', array() - )->join( + )->joinLeft( array('co' => $this->prefix . 'objects'), 'co.object_id = cgm.contact_object_id AND co.is_active = 1 AND co.objecttype_id = 10', array() From b6a76581b01c41bd21694dcafa8d3017de411de1 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 1 Jun 2016 13:12:48 +0200 Subject: [PATCH 012/337] Host/show: display host alias if it differs from host_name and display_name fixes #11348 --- .../views/scripts/partials/object/host-header.phtml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/monitoring/application/views/scripts/partials/object/host-header.phtml b/modules/monitoring/application/views/scripts/partials/object/host-header.phtml index 8bc7b6c64..a77a95d58 100644 --- a/modules/monitoring/application/views/scripts/partials/object/host-header.phtml +++ b/modules/monitoring/application/views/scripts/partials/object/host-header.phtml @@ -18,6 +18,11 @@ use Icinga\Module\Monitoring\Object\Host; escape($object->host_display_name) ?> host_display_name !== $object->host_name): ?> (escape($object->host_name) ?>) + + host_alias !== $object->host_display_name && $object->host_alias !== $object->host_name): ?> +
+ escape($this->translate('Alias', 'host') . ': ' . $object->host_alias) ?> +
render('partials/host/statusicons.phtml') ?> host_address6 && $object->host_address6 !== $object->host_name): ?> From 656359a108150994c35a7df2cb72c96e4d499231 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 1 Jun 2016 13:37:02 +0200 Subject: [PATCH 013/337] CSS: Remove border from extrapolated circles in the timeline Looks cleaner to me --- modules/monitoring/public/css/module.less | 6 ------ 1 file changed, 6 deletions(-) diff --git a/modules/monitoring/public/css/module.less b/modules/monitoring/public/css/module.less index af2458b8b..159df5579 100644 --- a/modules/monitoring/public/css/module.less +++ b/modules/monitoring/public/css/module.less @@ -395,12 +395,6 @@ div.timeline { position: absolute; top: 50%; - &.extrapolated { - border-width: 2px; - border-style: dotted; - border-radius: 100%; - } - a.inner-circle { display: block; position: absolute; From ba5fe61fa921058d2e05f89c214d57036717555a Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 1 Jun 2016 14:40:55 +0200 Subject: [PATCH 014/337] monitoring: Make timeline colors accessible fixes #11871 --- .../controllers/TimelineController.php | 25 ++++---- .../views/scripts/timeline/index.phtml | 17 +++--- .../library/Monitoring/Timeline/TimeEntry.php | 33 +++++----- .../library/Monitoring/Timeline/TimeLine.php | 4 +- modules/monitoring/public/css/module.less | 55 +++++++++++++++++ public/css/themes/high-contrast.less | 61 ++++++++++++++++++- 6 files changed, 154 insertions(+), 41 deletions(-) diff --git a/modules/monitoring/application/controllers/TimelineController.php b/modules/monitoring/application/controllers/TimelineController.php index d97224564..c0aa3ff61 100644 --- a/modules/monitoring/application/controllers/TimelineController.php +++ b/modules/monitoring/application/controllers/TimelineController.php @@ -46,34 +46,33 @@ class TimelineController extends Controller ), array( 'notify' => array( + 'class' => 'timeline-notification', 'detailUrl' => $detailUrl, - 'label' => mt('monitoring', 'Notifications'), - 'color' => '#3a71ea' + 'label' => mt('monitoring', 'Notifications') ), 'hard_state' => array( + 'class' => 'timeline-hard-state', 'detailUrl' => $detailUrl, - 'label' => mt('monitoring', 'Hard state changes'), - 'color' => '#ff7000' + 'label' => mt('monitoring', 'Hard state changes') ), 'comment' => array( + 'class' => 'timeline-comment', 'detailUrl' => $detailUrl, - 'label' => mt('monitoring', 'Comments'), - 'color' => '#79bdba' + 'label' => mt('monitoring', 'Comments') ), 'ack' => array( - 'detailUrl' => $detailUrl, - 'label' => mt('monitoring', 'Acknowledgements'), - 'color' => '#a2721d' + 'class' => 'timeline-ack', + 'label' => mt('monitoring', 'Acknowledgements') ), 'dt_start' => array( + 'class' => 'timeline-downtime-start', 'detailUrl' => $detailUrl, - 'label' => mt('monitoring', 'Started downtimes'), - 'color' => '#8e8e8e' + 'label' => mt('monitoring', 'Started downtimes') ), 'dt_end' => array( + 'class' => 'timeline-downtime-end', 'detailUrl' => $detailUrl, - 'label' => mt('monitoring', 'Ended downtimes'), - 'color' => '#d5d6ad' + 'label' => mt('monitoring', 'Ended downtimes') ) ) ); diff --git a/modules/monitoring/application/views/scripts/timeline/index.phtml b/modules/monitoring/application/views/scripts/timeline/index.phtml index 891a7f2b5..af3b406cc 100644 --- a/modules/monitoring/application/views/scripts/timeline/index.phtml +++ b/modules/monitoring/application/views/scripts/timeline/index.phtml @@ -13,9 +13,9 @@ if (! $beingExtended && !$this->compact): ?>

translate('Legend'); ?>

- - - + + +
@@ -84,11 +84,9 @@ $extrapolatedCircleWidth = $timeline->getExtrapolatedCircleWidth($timeInfo[1][$g ?>
-
getClass() ?>" style="getColor(), $extrapolatedCircleWidth ); ?>"> @@ -114,11 +112,10 @@ $extrapolatedCircleWidth = $timeline->getExtrapolatedCircleWidth($timeInfo[1][$g strtolower($labelAndColor['label']), $titleTime ), - 'class' => 'inner-circle', + 'class' => 'inner-circle ' . $timeInfo[1][$groupName]->getClass(), 'style' => sprintf( - 'width: %3$s; height: %3$s; background-color: %2$s; margin-top: -%1$Fem; margin-left: -%1$Fem;', + 'width: %2$s; height: %2$s; margin-top: -%1$Fem; margin-left: -%1$Fem;', (float) substr($circleWidth, 0, -2) / 2, - $timeInfo[1][$groupName]->getColor(), (string) $circleWidth ) ) diff --git a/modules/monitoring/library/Monitoring/Timeline/TimeEntry.php b/modules/monitoring/library/Monitoring/Timeline/TimeEntry.php index 4c721b808..ee313b3c8 100644 --- a/modules/monitoring/library/Monitoring/Timeline/TimeEntry.php +++ b/modules/monitoring/library/Monitoring/Timeline/TimeEntry.php @@ -55,11 +55,11 @@ class TimeEntry protected $label; /** - * The color of this group + * The CSS class of the entry * * @var string */ - protected $color; + protected $class; /** * Return a new TimeEntry object with the given attributes being set @@ -209,22 +209,25 @@ class TimeEntry } /** - * Set this group's color - * - * @param string $color The color to set. (The css name or hex-code) - */ - public function setColor($color) - { - $this->color = $color; - } - - /** - * Get the color of this group + * Get the CSS class * * @return string */ - public function getColor() + public function getClass() { - return $this->color; + return $this->class; + } + + /** + * Set the CSS class + * + * @param string $class + * + * @return $this + */ + public function setClass($class) + { + $this->class = $class; + return $this; } } diff --git a/modules/monitoring/library/Monitoring/Timeline/TimeLine.php b/modules/monitoring/library/Monitoring/Timeline/TimeLine.php index 08e5b7a48..5ed48a43c 100644 --- a/modules/monitoring/library/Monitoring/Timeline/TimeLine.php +++ b/modules/monitoring/library/Monitoring/Timeline/TimeLine.php @@ -200,7 +200,7 @@ class TimeLine implements IteratorAggregate } /** - * Return all known group types (identifiers) with their respective labels and colors as array + * Return all known group types (identifiers) with their respective labels and classess as array * * @return array */ @@ -208,8 +208,8 @@ class TimeLine implements IteratorAggregate { $groupInfo = array(); foreach ($this->identifiers as $name => $attributes) { + $groupInfo[$name]['class'] = $attributes['class']; $groupInfo[$name]['label'] = $attributes['label']; - $groupInfo[$name]['color'] = $attributes['color']; } return $groupInfo; diff --git a/modules/monitoring/public/css/module.less b/modules/monitoring/public/css/module.less index 159df5579..fa04397f9 100644 --- a/modules/monitoring/public/css/module.less +++ b/modules/monitoring/public/css/module.less @@ -417,6 +417,61 @@ div.timeline { } } +@timeline-notification-color: #3a71ea; +@timeline-hard-state-color: #ff7000; +@timeline-comment-color: #79bdba; +@timeline-ack-color: #a2721d; +@timeline-downtime-start-color: #8e8e8e; +@timeline-downtime-end-color: #d5d6ad; + +.timeline-notification { + background-color: @timeline-notification-color; + + &.extrapolated { + background-color: lighten(@timeline-notification-color, 20%); + } +} + +.timeline-hard-state { + background-color: @timeline-hard-state-color; + + &.extrapolated { + background-color: lighten(@timeline-hard-state-color, 20%); + } +} + +.timeline-comment { + background-color: @timeline-comment-color; + + &.extrapolated { + background-color: lighten(@timeline-comment-color, 20%); + } +} + +.timeline-ack { + background-color: @timeline-ack-color; + + &.extrapolated { + background-color: lighten(@timeline-ack-color, 20%); + } +} + +.timeline-downtime-start { + background-color: @timeline-downtime-start-color; + + &.extrapolated { + background-color: lighten(@timeline-downtime-start-color, 20%); + } +} + +.timeline-downtime-end { + background-color: @timeline-downtime-end-color; + + &.extrapolated { + background-color: lighten(@timeline-downtime-end-color, 20%); + } +} + /* End of monitoring timeline styles */ /* Object features */ diff --git a/public/css/themes/high-contrast.less b/public/css/themes/high-contrast.less index d5db9795c..c9057c08d 100644 --- a/public/css/themes/high-contrast.less +++ b/public/css/themes/high-contrast.less @@ -16,7 +16,6 @@ @text-color: #191919; @text-color-light: #555555; - .badge { background-color: @text-color-light; } @@ -64,3 +63,63 @@ color: @text-color; text-decoration: underline; } + +.icinga-module.module-monitoring { + @timeline-notification-color: #1650CF; + @timeline-hard-state-color: #A24600; + @timeline-comment-color: #346964; + @timeline-ack-color: #855D18; + @timeline-downtime-start-color: #515151; + @timeline-downtime-end-color: #5e5e2f; + + // Unfortunately it does not suffice to only override the timeline colors here, because our less compiler seems to + // have the related style block in module.less already evaluated + + .timeline-notification { + background-color: @timeline-notification-color; + + &.extrapolated { + background-color: lighten(@timeline-notification-color, 20%); + } + } + + .timeline-hard-state { + background-color: @timeline-hard-state-color; + + &.extrapolated { + background-color: lighten(@timeline-hard-state-color, 20%); + } + } + + .timeline-comment { + background-color: @timeline-comment-color; + + &.extrapolated { + background-color: lighten(@timeline-comment-color, 20%); + } + } + + .timeline-ack { + background-color: @timeline-ack-color; + + &.extrapolated { + background-color: lighten(@timeline-ack-color, 20%); + } + } + + .timeline-downtime-start { + background-color: @timeline-downtime-start-color; + + &.extrapolated { + background-color: lighten(@timeline-downtime-start-color, 20%); + } + } + + .timeline-downtime-end { + background-color: @timeline-downtime-end-color; + + &.extrapolated { + background-color: lighten(@timeline-downtime-end-color, 20%); + } + } +} From 188703cf955eb19db248632dc678011582bccbdb Mon Sep 17 00:00:00 2001 From: "hailthemelody@rm-laptop04" Date: Tue, 31 May 2016 00:37:53 -0400 Subject: [PATCH 015/337] fix various typos Signed-off-by: Johannes Meyer --- application/locale/de_DE/LC_MESSAGES/icinga.po | 2 +- application/locale/it_IT/LC_MESSAGES/icinga.po | 2 +- application/locale/ru_RU/LC_MESSAGES/icinga.po | 2 +- library/Icinga/Web/Form.php | 2 +- library/Icinga/Web/Form/Decorator/Autosubmit.php | 2 +- .../application/locale/de_DE/LC_MESSAGES/monitoring.po | 2 +- .../application/locale/it_IT/LC_MESSAGES/monitoring.po | 2 +- .../application/locale/pt_BR/LC_MESSAGES/monitoring.po | 2 +- .../application/locale/ru_RU/LC_MESSAGES/monitoring.po | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.po b/application/locale/de_DE/LC_MESSAGES/icinga.po index b086aa2c3..d5f427e11 100644 --- a/application/locale/de_DE/LC_MESSAGES/icinga.po +++ b/application/locale/de_DE/LC_MESSAGES/icinga.po @@ -2752,7 +2752,7 @@ msgstr "" #: /vagrant/library/Icinga/Web/Form.php:951 #: /vagrant/library/Icinga/Web/Form/Decorator/Autosubmit.php:100 msgid "" -"Upon its value has changed, this field issues an automatic update of this " +"Upon its value changing, this field issues an automatic update of this " "page." msgstr "" "Dieses Feld löst eine automatische Aktualisierung dieser Seite aus, wenn " diff --git a/application/locale/it_IT/LC_MESSAGES/icinga.po b/application/locale/it_IT/LC_MESSAGES/icinga.po index de61e1f51..1550ef138 100644 --- a/application/locale/it_IT/LC_MESSAGES/icinga.po +++ b/application/locale/it_IT/LC_MESSAGES/icinga.po @@ -2696,7 +2696,7 @@ msgstr "" #: /usr/share/php/Icinga/Web/Form.php:951 #: /usr/share/php/Icinga/Web/Form/Decorator/Autosubmit.php:100 msgid "" -"Upon its value has changed, this field issues an automatic update of this " +"Upon its value changing, this field issues an automatic update of this " "page." msgstr "Cambiando questo valore la pagina verrà ricaricata automaticamente." diff --git a/application/locale/ru_RU/LC_MESSAGES/icinga.po b/application/locale/ru_RU/LC_MESSAGES/icinga.po index bf4698b02..e27f63e9c 100644 --- a/application/locale/ru_RU/LC_MESSAGES/icinga.po +++ b/application/locale/ru_RU/LC_MESSAGES/icinga.po @@ -2581,7 +2581,7 @@ msgstr "" #: /vagrant/library/Icinga/Web/Form.php:951 #: /vagrant/library/Icinga/Web/Form/Decorator/Autosubmit.php:100 msgid "" -"Upon its value has changed, this field issues an automatic update of this " +"Upon its value changing, this field issues an automatic update of this " "page." msgstr "В случае изменения значения поля страница будет обновлена" diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index cfe730984..bea07488f 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -948,7 +948,7 @@ class Form extends Zend_Form if ($this->getUseFormAutosubmit()) { $warningId = 'autosubmit_warning_' . $el->getId(); $warningText = $this->getView()->escape($this->translate( - 'Upon its value has changed, this field issues an automatic update of this page.' + 'Upon its value changing, this field issues an automatic update of this page.' )); $autosubmitDecorator = $this->_getDecorator('Callback', array( 'placement' => 'PREPEND', diff --git a/library/Icinga/Web/Form/Decorator/Autosubmit.php b/library/Icinga/Web/Form/Decorator/Autosubmit.php index f3ce02d53..81a8e5753 100644 --- a/library/Icinga/Web/Form/Decorator/Autosubmit.php +++ b/library/Icinga/Web/Form/Decorator/Autosubmit.php @@ -97,7 +97,7 @@ class Autosubmit extends Zend_Form_Decorator_Abstract $isForm = $this->getElement() instanceof Form; $warning = $isForm ? t('Upon any of this form\'s fields were changed, this page is being updated automatically.') - : t('Upon its value has changed, this field issues an automatic update of this page.'); + : t('Upon its value changing, this field issues an automatic update of this page.'); $content .= $this->getView()->icon('cw', $warning, array( 'aria-hidden' => $isForm ? 'false' : 'true', 'class' => 'spinner autosubmit-info' diff --git a/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.po b/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.po index 79a04cf40..60d02e456 100644 --- a/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.po +++ b/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.po @@ -1919,7 +1919,7 @@ msgstr "" #: /vagrant/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:45 msgid "" "If you work with other administrators, you may find it useful to share " -"information about the the host or service that is having problems. Make sure " +"information about the host or service that is having problems. Make sure " "you enter a brief description of what you are doing." msgstr "" "Wenn Sie mit anderen Administratoren zusammenarbeiten, werden Sie es " diff --git a/modules/monitoring/application/locale/it_IT/LC_MESSAGES/monitoring.po b/modules/monitoring/application/locale/it_IT/LC_MESSAGES/monitoring.po index ec0f3e5f6..8f752efba 100644 --- a/modules/monitoring/application/locale/it_IT/LC_MESSAGES/monitoring.po +++ b/modules/monitoring/application/locale/it_IT/LC_MESSAGES/monitoring.po @@ -1872,7 +1872,7 @@ msgstr "" #: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:45 msgid "" "If you work with other administrators, you may find it useful to share " -"information about the the host or service that is having problems. Make sure " +"information about the host or service that is having problems. Make sure " "you enter a brief description of what you are doing." msgstr "" "Se lavori con altri amministratori potresti trovare utile condividere le " diff --git a/modules/monitoring/application/locale/pt_BR/LC_MESSAGES/monitoring.po b/modules/monitoring/application/locale/pt_BR/LC_MESSAGES/monitoring.po index 9edbc328c..2f6b97aa4 100644 --- a/modules/monitoring/application/locale/pt_BR/LC_MESSAGES/monitoring.po +++ b/modules/monitoring/application/locale/pt_BR/LC_MESSAGES/monitoring.po @@ -1553,7 +1553,7 @@ msgstr "" #: /usr/local/icingaweb/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:73 msgid "" "If you work with other administrators, you may find it useful to share " -"information about the the host or service that is having problems. Make sure " +"information about the host or service that is having problems. Make sure " "you enter a brief description of what you are doing." msgstr "" "Se você trabalha com outros administradores, você pode achar isso útil para " diff --git a/modules/monitoring/application/locale/ru_RU/LC_MESSAGES/monitoring.po b/modules/monitoring/application/locale/ru_RU/LC_MESSAGES/monitoring.po index bcaf5fb6d..655f0adce 100644 --- a/modules/monitoring/application/locale/ru_RU/LC_MESSAGES/monitoring.po +++ b/modules/monitoring/application/locale/ru_RU/LC_MESSAGES/monitoring.po @@ -1819,7 +1819,7 @@ msgstr "" #: /vagrant/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:45 msgid "" "If you work with other administrators, you may find it useful to share " -"information about the the host or service that is having problems. Make sure " +"information about the host or service that is having problems. Make sure " "you enter a brief description of what you are doing." msgstr "" From 4a78fa6a660d5e80d237dfdfac718662cb98d8ba Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 2 Jun 2016 10:43:34 +0200 Subject: [PATCH 016/337] List views: handle clicks on mailto: and http:// links as expected refs #11267 --- public/js/icinga/events.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 829ede335..743b7a95e 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -420,8 +420,8 @@ var linkTarget = $a.attr('target'); var $target; var formerUrl; - var remote = /^(?:[a-z]+:)\/\//; - if (href.match(/^(mailto|javascript|data):/)) { + if (href.match(/^(?:(?:mailto|javascript|data):|[a-z]+:\/\/)/)) { + event.stopPropagation(); return true; } @@ -439,10 +439,6 @@ } } - // Let remote links pass through - if (href.match(remote)) { - return true; - } // window.open is used as return true; didn't work reliable if (linkTarget === '_blank' || linkTarget === '_self') { window.open(href, linkTarget); From f75925b550b49eea431bed4f47b7a0bb3a837961 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 2 Jun 2016 12:38:17 +0200 Subject: [PATCH 017/337] PluginOutput: preserve refs #11267 --- modules/monitoring/application/views/helpers/PluginOutput.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php index ba3013a3a..997536ef0 100644 --- a/modules/monitoring/application/views/helpers/PluginOutput.php +++ b/modules/monitoring/application/views/helpers/PluginOutput.php @@ -105,7 +105,8 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract $config = HTMLPurifier_Config::createDefault(); $config->set('Core.EscapeNonASCIICharacters', true); - $config->set('HTML.Allowed', 'p,br,b,a[href],i,table,tr,td[colspan],div,*[class]'); + $config->set('HTML.Allowed', 'p,br,b,a[href|target],i,table,tr,td[colspan],div,*[class]'); + $config->set('Attr.AllowedFrameTargets', array('_blank')); // This avoids permission problems: // $config->set('Core.DefinitionCache', null); $config->set('Cache.DefinitionImpl', null); From 3978d70127199879ead6c80da8451b99322f17ff Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 2 Jun 2016 15:46:18 +0200 Subject: [PATCH 018/337] Add screenreader compatibility note to the login page --- .../locale/de_DE/LC_MESSAGES/icinga.mo | Bin 59318 -> 60895 bytes .../locale/de_DE/LC_MESSAGES/icinga.po | 175 ++++++++++-------- .../views/scripts/authentication/login.phtml | 7 + 3 files changed, 105 insertions(+), 77 deletions(-) diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.mo b/application/locale/de_DE/LC_MESSAGES/icinga.mo index 83e648c9e926f7038b13657524fb87b65fc77b9d..22ba80c2af004204fefad8cac3d4c4177ebf497a 100644 GIT binary patch delta 14116 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 diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.po b/application/locale/de_DE/LC_MESSAGES/icinga.po index d5f427e11..e455fa85e 100644 --- a/application/locale/de_DE/LC_MESSAGES/icinga.po +++ b/application/locale/de_DE/LC_MESSAGES/icinga.po @@ -7,16 +7,16 @@ msgid "" msgstr "" "Project-Id-Version: Icinga Web 2 (None)\n" "Report-Msgid-Bugs-To: dev@icinga.org\n" -"POT-Creation-Date: 2016-02-29 14:39+0000\n" -"PO-Revision-Date: 2016-02-29 22:16+0100\n" -"Last-Translator: Thomas Gelf \n" +"POT-Creation-Date: 2016-06-02 14:11+0200\n" +"PO-Revision-Date: 2016-06-02 15:45+0200\n" +"Last-Translator: Eric Lippmann \n" "Language: de_DE\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "Language-Team: \n" -"X-Generator: Poedit 1.8.7\n" +"X-Generator: Poedit 1.8.8\n" #: /vagrant/library/Icinga/Web/Form/Validator/InArray.php:16 #, php-format @@ -143,9 +143,8 @@ msgid "Add" msgstr "Hinzufügen" #: /vagrant/library/Icinga/Web/Widget/Tabextension/DashboardSettings.php:25 -#, fuzzy msgid "Add Dashlet" -msgstr "Dashlet bearbeiten" +msgstr "Dashlet hinzufügen" #: /vagrant/application/controllers/DashboardController.php:76 msgid "Add Dashlet To Dashboard" @@ -164,11 +163,11 @@ msgstr "Zum Dashboard hinzufügen" msgid "Add To Menu" msgstr "Zum Menü hinzufügen" -#: /vagrant/application/views/scripts/user/list.phtml:34 +#: /vagrant/application/views/scripts/user/list.phtml:32 msgid "Add a New User" msgstr "Neuen Benutzer anlegen" -#: /vagrant/application/views/scripts/group/list.phtml:34 +#: /vagrant/application/views/scripts/group/list.phtml:32 msgid "Add a New User Group" msgstr "Neue Gruppe anlegen" @@ -180,13 +179,13 @@ msgstr "Neue Gruppe anlegen" msgid "Add a new user" msgstr "Neuen Benutzer anlegen" -#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:409 +#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:410 msgid "Add another filter" -msgstr "Weiteren Filter hinzufügen..." +msgstr "Weiteren Filter hinzufügen" #: /vagrant/library/Icinga/Web/Widget/FilterWidget.php:80 msgid "Add filter..." -msgstr "Filter hinzufügen..." +msgstr "Filter hinzufügen" #: /vagrant/application/forms/Config/UserGroup/AddMemberForm.php:125 #, php-format @@ -249,7 +248,7 @@ msgstr "" "Verbindung. Leer lassen, um keine Filter zu verwenden." #: /vagrant/library/Icinga/Application/Web.php:318 -#: /vagrant/library/Icinga/Web/Menu.php:273 +#: /vagrant/library/Icinga/Web/Menu.php:274 msgid "Application" msgstr "Anwendung" @@ -268,7 +267,7 @@ msgstr "Anwenden" #: /vagrant/application/controllers/ConfigController.php:50 #: /vagrant/library/Icinga/Application/Web.php:324 -#: /vagrant/library/Icinga/Web/Menu.php:278 +#: /vagrant/library/Icinga/Web/Menu.php:279 msgid "Authentication" msgstr "Authentifizierung" @@ -365,7 +364,7 @@ msgstr "Abbrechen" msgid "Cancel this membership" msgstr "Diese Mitgliedschaft beenden" -#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:435 +#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:436 msgid "Cancel this operation" msgstr "Diese Operation abbrechen" @@ -446,7 +445,7 @@ msgstr "" msgid "Click to remove this part of your filter" msgstr "Klicken, um diesen Teil des Filters zu löschen" -#: /vagrant/library/Icinga/Repository/Repository.php:1022 +#: /vagrant/library/Icinga/Repository/Repository.php:1023 #, php-format msgid "Column \"%s\" cannot be queried" msgstr "Spalte “%s” kann nicht abgefragt werden" @@ -472,7 +471,7 @@ msgid "Comma-separated list of users that are assigned to the role" msgstr "Kommaseparierte Liste von Nutzern, die dieser Rolle zugewiesen werden" #: /vagrant/library/Icinga/Application/Web.php:312 -#: /vagrant/library/Icinga/Web/Menu.php:268 +#: /vagrant/library/Icinga/Web/Menu.php:269 msgid "Configuration" msgstr "Konfiguration" @@ -486,9 +485,8 @@ msgstr "" "2" #: /vagrant/application/controllers/ConfigController.php:49 -#, fuzzy msgid "Configure the user and group backends" -msgstr "Neues Backend für Benutzergruppen erstellen" +msgstr "Konfiguration der Benutzer- und Gruppen-Backends" #: /vagrant/application/controllers/ConfigController.php:43 msgid "Configure which resources are being utilized by Icinga Web 2" @@ -652,6 +650,7 @@ msgstr "" "Momentan ist kein Dashlet verfügbar. Das könnte durch die Aktivierung von " "einigen der verfügbaren %s behoben werden." +#: /vagrant/application/controllers/DashboardController.php:257 #: /vagrant/application/forms/Dashboard/DashletForm.php:120 #: /vagrant/library/Icinga/Application/Web.php:290 #: /vagrant/library/Icinga/Web/Menu.php:243 @@ -712,10 +711,9 @@ msgid "Debug" msgstr "Debug" #: /vagrant/application/forms/Config/General/ThemingConfigForm.php:42 -#, fuzzy msgctxt "Form element label" msgid "Default Theme" -msgstr "Standardsprache" +msgstr "Standard-Theme" #: /vagrant/application/views/scripts/config/module.phtml:46 msgid "Dependencies" @@ -742,6 +740,14 @@ msgstr "Automatische Aktualisierung deaktivieren" msgid "Disable the %s module" msgstr "Modul %s deaktivieren" +#: /vagrant/application/forms/Dashboard/PaneForm.php:42 +msgid "Disable the dashboard" +msgstr "Dashboard deaktivieren" + +#: /vagrant/application/forms/Dashboard/PaneForm.php:43 +msgid "Disabled" +msgstr "Deaktiviert" + #: /vagrant/application/forms/Config/UserBackend/LdapBackendForm.php:88 msgctxt "A button to discover LDAP capabilities" msgid "Discover" @@ -756,11 +762,10 @@ msgid "Edit Dashlet" msgstr "Dashlet bearbeiten" #: /vagrant/application/views/scripts/user/show.phtml:16 -#, fuzzy msgid "Edit User" -msgstr "Nutzer %s bearbeiten" +msgstr "Nutzer bearbeiten" -#: /vagrant/application/views/scripts/dashboard/settings.phtml:53 +#: /vagrant/application/views/scripts/dashboard/settings.phtml:54 #, php-format msgid "Edit dashlet %s" msgstr "Dashlet %s bearbeiten" @@ -831,7 +836,6 @@ msgid "Enter a title for the dashlet." msgstr "Titel für das Dashlet eingeben" #: /vagrant/application/forms/Dashboard/DashletForm.php:111 -#, fuzzy msgid "Enter a title for the new dashboard" msgstr "Titel für das Dashboard eingeben" @@ -844,7 +848,6 @@ msgstr "" "URLs mit Filtern möglich." #: /vagrant/application/controllers/ErrorController.php:109 -#, fuzzy msgid "Error" msgstr "Fehler" @@ -981,17 +984,17 @@ msgstr "Dateipfad" msgid "Filter Pattern" msgstr "Muster" -#: /vagrant/library/Icinga/Repository/Repository.php:1066 +#: /vagrant/library/Icinga/Repository/Repository.php:1067 #, php-format msgid "Filter column \"%s\" not found" msgstr "Filterspalte “%s” nicht gefunden" -#: /vagrant/library/Icinga/Repository/Repository.php:1070 +#: /vagrant/library/Icinga/Repository/Repository.php:1071 #, php-format msgid "Filter column \"%s\" not found in table \"%s\"" msgstr "Filterspalte “%s” in Tabelle “%s” nicht gefunden" -#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:744 +#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:745 #: /vagrant/library/Icinga/Web/Widget/FilterWidget.php:89 msgid "Filter this list" msgstr "Diese Liste filtern" @@ -1110,7 +1113,7 @@ msgstr "Ich bin bereit zur Suche. Warte auf Eingabe" msgid "Icinga Documentation" msgstr "Icinga Dokumentation" -#: /vagrant/application/views/scripts/authentication/login.phtml:11 +#: /vagrant/application/views/scripts/authentication/login.phtml:18 msgid "Icinga Web 2 Documentation" msgstr "Icinga Web 2 Dokumentation" @@ -1118,7 +1121,7 @@ msgstr "Icinga Web 2 Dokumentation" msgid "Icinga Web 2 Login" msgstr "Icinga Web 2 Anmeldung" -#: /vagrant/application/views/scripts/authentication/login.phtml:12 +#: /vagrant/application/views/scripts/authentication/login.phtml:19 msgid "Icinga Web 2 Setup-Wizard" msgstr "Icinga Web 2 Einrichtungsassistent" @@ -1127,18 +1130,17 @@ msgid "Icinga Wiki" msgstr "Icinga Wiki" #: /vagrant/application/views/scripts/about/index.phtml:55 -#: /vagrant/application/views/scripts/authentication/login.phtml:37 +#: /vagrant/application/views/scripts/authentication/login.phtml:44 msgid "Icinga on Facebook" msgstr "Icinga auf Facebook" #: /vagrant/application/views/scripts/about/index.phtml:64 -#: /vagrant/application/views/scripts/authentication/login.phtml:46 -#, fuzzy +#: /vagrant/application/views/scripts/authentication/login.phtml:53 msgid "Icinga on Google+" -msgstr "Icinga auf Facebook" +msgstr "Icinga auf Google+" #: /vagrant/application/views/scripts/about/index.phtml:46 -#: /vagrant/application/views/scripts/authentication/login.phtml:27 +#: /vagrant/application/views/scripts/authentication/login.phtml:34 msgid "Icinga on Twitter" msgstr "Icinga auf Twitter" @@ -1188,7 +1190,7 @@ msgstr "Ungültiger Backendtyp “%s” angegeben" msgid "Invalid resource type \"%s\" provided" msgstr "Ungültiger Ressourcentyp “%s” angegeben" -#: /vagrant/application/views/scripts/authentication/login.phtml:7 +#: /vagrant/application/views/scripts/authentication/login.phtml:14 #, php-format msgid "" "It appears that you did not configure Icinga Web 2 yet so it's not possible " @@ -1322,7 +1324,7 @@ msgid "Login" msgstr "Anmelden" #: /vagrant/library/Icinga/Application/Web.php:355 -#: /vagrant/library/Icinga/Web/Menu.php:313 +#: /vagrant/library/Icinga/Web/Menu.php:314 msgid "Logout" msgstr "Abmelden" @@ -1356,7 +1358,7 @@ msgstr "Menüeintrag" msgid "Method Not Allowed" msgstr "Nicht zulässig" -#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:746 +#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:747 #: /vagrant/library/Icinga/Web/Widget/FilterWidget.php:94 msgid "Modify this filter" msgstr "Diesen Filter bearbeiten" @@ -1396,7 +1398,7 @@ msgstr "Pfad zu den Modulen" #: /vagrant/application/controllers/ConfigController.php:96 #: /vagrant/library/Icinga/Application/Web.php:336 -#: /vagrant/library/Icinga/Web/Menu.php:298 +#: /vagrant/library/Icinga/Web/Menu.php:299 msgid "Modules" msgstr "Module" @@ -1473,9 +1475,8 @@ msgid "New Dashboard Title" msgstr "Neuen Dashboardtitel vergeben" #: /vagrant/application/controllers/DashboardController.php:42 -#, fuzzy msgid "New Dashlet" -msgstr "Dashlet aktualisieren" +msgstr "Neues Dashlet" #: /vagrant/application/controllers/NavigationController.php:244 msgid "New Navigation Item" @@ -1550,15 +1551,15 @@ msgstr "" "Keine Authentifizierungsmethode verfügbar. Wurde bei der Installation von " "Icinga Web 2 eine authentication.ini erstellt?" -#: /vagrant/application/views/scripts/group/list.phtml:24 +#: /vagrant/application/views/scripts/group/list.phtml:22 msgid "No backend found which is able to list user groups" msgstr "Kein Backend gefunden, um Benutzergruppen aufzulisten" -#: /vagrant/application/views/scripts/user/list.phtml:24 +#: /vagrant/application/views/scripts/user/list.phtml:22 msgid "No backend found which is able to list users" msgstr "Kein Backend gefunden, um Benutzer aufzulisten" -#: /vagrant/application/views/scripts/dashboard/settings.phtml:41 +#: /vagrant/application/views/scripts/dashboard/settings.phtml:42 msgid "No dashlets added to dashboard" msgstr "Dem Dashboard wurden keine Dashlets hinzugefügt" @@ -1577,13 +1578,13 @@ msgstr "" msgid "No roles found." msgstr "Es wurden keine Rollen gefunden" -#: /vagrant/application/views/scripts/group/list.phtml:46 +#: /vagrant/application/views/scripts/group/list.phtml:44 msgid "No user groups found matching the filter" msgstr "" "Es wurden keine Benutzergruppen gefunden, welche den Filterkriterien " "entsprechen" -#: /vagrant/application/views/scripts/user/list.phtml:46 +#: /vagrant/application/views/scripts/user/list.phtml:44 msgid "No users found matching the filter" msgstr "Es wurde kein Benutzer gefunden, der den Filterkriterien entspricht" @@ -1716,7 +1717,7 @@ msgstr "Port" #: /vagrant/application/controllers/NavigationController.php:134 #: /vagrant/application/controllers/PreferenceController.php:33 #: /vagrant/library/Icinga/Application/Web.php:350 -#: /vagrant/library/Icinga/Web/Menu.php:308 +#: /vagrant/library/Icinga/Web/Menu.php:309 msgid "Preferences" msgstr "Einstellungen" @@ -1768,12 +1769,12 @@ msgid "Push to fill in the chosen connection's default settings." msgstr "" "Klicken Sie hier, um die gewählten Verbindungseinstellungen auszufüllen." -#: /vagrant/library/Icinga/Repository/Repository.php:1018 +#: /vagrant/library/Icinga/Repository/Repository.php:1019 #, php-format msgid "Query column \"%s\" not found" msgstr "Abfragespalte “%s” wurde nicht gefunden" -#: /vagrant/library/Icinga/Repository/Repository.php:1026 +#: /vagrant/library/Icinga/Repository/Repository.php:1027 #, php-format msgid "Query column \"%s\" not found in table \"%s\"" msgstr "Abfragespalte “%s” wurde in Tabelle “%s” nicht gefunden" @@ -1782,9 +1783,9 @@ msgstr "Abfragespalte “%s” wurde in Tabelle “%s” nicht gefunden" msgid "Ready to search" msgstr "Bereit zur Suche" -#: /vagrant/application/views/scripts/group/list.phtml:55 +#: /vagrant/application/views/scripts/group/list.phtml:52 #: /vagrant/application/views/scripts/group/show.phtml:77 -#: /vagrant/application/views/scripts/user/list.phtml:55 +#: /vagrant/application/views/scripts/user/list.phtml:53 msgid "Remove" msgstr "Entfernen" @@ -1793,9 +1794,8 @@ msgid "Remove Dashboard" msgstr "Dashboard entfernen" #: /vagrant/application/controllers/DashboardController.php:158 -#, fuzzy msgid "Remove Dashlet" -msgstr "Dashboard entfernen" +msgstr "Dashlet entfernen" #: /vagrant/application/controllers/DashboardController.php:192 msgid "Remove Dashlet From Dashboard" @@ -1830,7 +1830,7 @@ msgstr "Benutzergruppe entfernen" msgid "Remove User Group Backend" msgstr "Backend für Benutzergruppen entfernen" -#: /vagrant/application/views/scripts/dashboard/settings.phtml:71 +#: /vagrant/application/views/scripts/dashboard/settings.phtml:72 #, php-format msgid "Remove dashlet %s from pane %s" msgstr "Dashlet %s aus Dashboard %s entfernen" @@ -1845,7 +1845,7 @@ msgstr "Gruppe %s entfernen" msgid "Remove navigation item %s" msgstr "Navigationselement %s entfernen" -#: /vagrant/application/views/scripts/dashboard/settings.phtml:32 +#: /vagrant/application/views/scripts/dashboard/settings.phtml:33 #, php-format msgid "Remove pane %s" msgstr "Dashboard %s entfernen" @@ -1873,11 +1873,11 @@ msgstr "Diesen Filter entfernen" msgid "Remove this member" msgstr "Dieses Mitglied entfernen" -#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:396 +#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:397 msgid "Remove this part of your filter" msgstr "Klicken, um diesen Teil des Filters zu löschen" -#: /vagrant/application/views/scripts/user/list.phtml:84 +#: /vagrant/application/views/scripts/user/list.phtml:82 #, php-format msgid "Remove user %s" msgstr "Benutzer %s entfernen" @@ -1892,7 +1892,7 @@ msgstr "Benutzer %s entfernen" msgid "Remove user backend %s" msgstr "Benutzerbackend %s entfernen" -#: /vagrant/application/views/scripts/group/list.phtml:88 +#: /vagrant/application/views/scripts/group/list.phtml:86 #, php-format msgid "Remove user group %s" msgstr "Benutzergruppe %s entfernen" @@ -1998,7 +1998,7 @@ msgstr "Rolle aktualisiert" #: /vagrant/application/controllers/GroupController.php:344 #: /vagrant/application/controllers/RoleController.php:153 #: /vagrant/application/controllers/UserController.php:310 -#: /vagrant/library/Icinga/Web/Menu.php:283 +#: /vagrant/library/Icinga/Web/Menu.php:284 msgid "Roles" msgstr "Rollen" @@ -2053,7 +2053,7 @@ msgstr "Domains durchsuchen" msgid "Search this domain for records of available servers." msgstr "Diese Domain nach verfügbaren Servern durchsuchen." -#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:740 +#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:741 msgid "Search..." msgstr "Suche..." @@ -2062,9 +2062,8 @@ msgid "Searching" msgstr "Suche" #: /vagrant/application/forms/Dashboard/DashletForm.php:122 -#, fuzzy msgid "Select a dashboard you want to add the dashlet to" -msgstr "Wählen Sie ein Dashboard aus, dem Sie das Dashlet hinzufügen wollen." +msgstr "Wählen Sie das Dashboard aus, dem Sie das Dashlet hinzufügen wollen" #: /vagrant/application/forms/Config/User/CreateMembershipForm.php:81 #, php-format @@ -2117,7 +2116,7 @@ msgstr "Freigegeben" msgid "Shared Navigation" msgstr "Freigegebene Navigation" -#: /vagrant/library/Icinga/Web/Widget/Dashboard.php:238 +#: /vagrant/library/Icinga/Web/Widget/Dashboard.php:235 #, php-format msgctxt "dashboard.pane.tooltip" msgid "Show %s" @@ -2133,13 +2132,13 @@ msgstr "Suche anzeigen" msgid "Show Stacktraces" msgstr "Stacktrace anzeigen" -#: /vagrant/application/views/scripts/dashboard/settings.phtml:61 +#: /vagrant/application/views/scripts/dashboard/settings.phtml:62 #, php-format msgid "Show dashlet %s" msgstr "Dashlet %s anzeigen" #: /vagrant/application/views/scripts/group/show.phtml:93 -#: /vagrant/application/views/scripts/user/list.phtml:70 +#: /vagrant/application/views/scripts/user/list.phtml:68 #, php-format msgid "Show detailed information about %s" msgstr "Zeige detaillierte Informationen über %s" @@ -2149,7 +2148,7 @@ msgstr "Zeige detaillierte Informationen über %s" msgid "Show detailed information for group %s" msgstr "Zeige detaillierte Informationen über die Gruppe %s" -#: /vagrant/application/views/scripts/group/list.phtml:72 +#: /vagrant/application/views/scripts/group/list.phtml:69 #, php-format msgid "Show detailed information for user group %s" msgstr "Zeige detaillierte Informationen über die Benutzergruppe %s" @@ -2206,7 +2205,7 @@ msgstr "Sortieren nach" msgid "State" msgstr "Status" -#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:422 +#: /vagrant/library/Icinga/Web/Widget/FilterEditor.php:423 msgid "Strip this filter" msgstr "Diesen Filter bearbeiten" @@ -2224,7 +2223,7 @@ msgid "Target" msgstr "Ziel" #: /vagrant/application/views/scripts/about/index.phtml:29 -#: /vagrant/application/views/scripts/authentication/login.phtml:19 +#: /vagrant/application/views/scripts/authentication/login.phtml:26 msgid "The Icinga Project" msgstr "Das Icinga Projekt" @@ -2296,7 +2295,7 @@ msgstr "" #: /vagrant/application/forms/Config/General/ThemingConfigForm.php:40 msgctxt "Form element description" msgid "The default theme" -msgstr "Standardtheme" +msgstr "Standard-Theme" #: /vagrant/application/views/scripts/showConfiguration.phtml:5 #, php-format @@ -2600,7 +2599,7 @@ msgstr "Der für die Authentifizierung zu benutzende Benutzername." #: /vagrant/application/forms/PreferenceForm.php:170 msgctxt "Form element label" msgid "Theme" -msgstr "" +msgstr "Theme" #: /vagrant/application/views/scripts/navigation/shared.phtml:15 msgid "There are currently no navigation items being shared" @@ -2635,6 +2634,7 @@ msgid "This module has no dependencies" msgstr "Dieses Modul hat keine Abhängigkeiten" #: /vagrant/library/Icinga/Application/Modules/Module.php:721 +#: /vagrant/library/Icinga/Application/Modules/ModuleInfo.php:13 msgid "This module has no description" msgstr "Dieses Modul hat keine Beschreibung" @@ -2651,6 +2651,14 @@ msgid "Tick this box to share this item with others" msgstr "" "Markieren Sie dieses Kästchen, um dieses Element mit Anderen zu teilen." +#: /vagrant/application/forms/Dashboard/PaneForm.php:34 +msgid "Title" +msgstr "Titel" + +#: /vagrant/application/forms/Dashboard/PaneForm.php:33 +msgid "Title of the dashboard" +msgstr "Titel für das Dashboard" + #: /vagrant/application/forms/Config/Resource/SshResourceForm.php:91 msgid "To modify the private key you must recreate this resource." msgstr "" @@ -2708,6 +2716,10 @@ msgstr "Freigabe aufheben" msgid "Unshare this navigation item" msgstr "Freigabe dieses Navigationselementes aufheben" +#: /vagrant/application/forms/Dashboard/PaneForm.php:21 +msgid "Update Dashboard" +msgstr "Dashboard aktualisieren" + #: /vagrant/application/controllers/DashboardController.php:86 #: /vagrant/application/controllers/DashboardController.php:92 msgid "Update Dashlet" @@ -2752,8 +2764,7 @@ msgstr "" #: /vagrant/library/Icinga/Web/Form.php:951 #: /vagrant/library/Icinga/Web/Form/Decorator/Autosubmit.php:100 msgid "" -"Upon its value changing, this field issues an automatic update of this " -"page." +"Upon its value changing, this field issues an automatic update of this page." msgstr "" "Dieses Feld löst eine automatische Aktualisierung dieser Seite aus, wenn " "sein Inhalt geändert wird." @@ -2821,7 +2832,7 @@ msgstr "Backends für Benutzer" #: /vagrant/application/controllers/GroupController.php:69 #: /vagrant/application/controllers/UserController.php:101 -#: /vagrant/application/views/scripts/group/list.phtml:53 +#: /vagrant/application/views/scripts/group/list.phtml:50 #: /vagrant/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:115 #: /vagrant/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:120 #: /vagrant/library/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php:465 @@ -2833,7 +2844,6 @@ msgid "User Group Backend" msgstr "Backend für Benutzergruppen" #: /vagrant/application/views/scripts/config/userbackend/reorder.phtml:19 -#, fuzzy msgid "User Group Backends" msgstr "Backend für Benutzergruppen" @@ -2905,7 +2915,7 @@ msgstr "" "soll. Bitte beachten Sie, dass für diesen Benutzer ein schlüsselbasierter " "Zugriff möglich sein muss." -#: /vagrant/library/Icinga/Web/Menu.php:293 +#: /vagrant/library/Icinga/Web/Menu.php:294 msgid "Usergroups" msgstr "Benutzergruppen" @@ -2915,7 +2925,7 @@ msgstr "Benutzergruppen" #: /vagrant/application/forms/Config/Resource/DbResourceForm.php:127 #: /vagrant/application/forms/Config/User/UserForm.php:33 #: /vagrant/application/views/scripts/group/show.phtml:75 -#: /vagrant/application/views/scripts/user/list.phtml:53 +#: /vagrant/application/views/scripts/user/list.phtml:51 #: /vagrant/library/Icinga/Authentication/User/DbUserBackend.php:115 #: /vagrant/library/Icinga/Authentication/User/DbUserBackend.php:118 #: /vagrant/library/Icinga/Authentication/User/LdapUserBackend.php:255 @@ -2932,7 +2942,7 @@ msgstr "Benutzername" #: /vagrant/application/forms/Navigation/NavigationConfigForm.php:613 #: /vagrant/application/forms/Security/RoleForm.php:117 #: /vagrant/application/views/scripts/role/list.phtml:23 -#: /vagrant/library/Icinga/Web/Menu.php:288 +#: /vagrant/library/Icinga/Web/Menu.php:289 msgid "Users" msgstr "Benutzer" @@ -2967,6 +2977,17 @@ msgctxt "app.config.logging.level" msgid "Warning" msgstr "Warnung" +#: /vagrant/application/views/scripts/authentication/login.phtml:5 +msgid "" +"Welcome to Icinga Web 2. For users of the screen reader Jaws full and " +"expectant compliant accessibility is possible only with use of the Firefox " +"browser. VoiceOver on Mac OS X is tested on Chrome, Safari and Firefox." +msgstr "" +"Willkommen zu Icinga Web 2. Für Nutzer des Screenreader JAWS ist die " +"vollständige und erwartungskonforme Zugänglichkeit nur mit Nutzung des " +"Browsers Firefox möglich. VoiceOver unter Mac OS X ist getestet mit Chrome, " +"Safari und Firefox." + #: /vagrant/application/views/scripts/dashboard/index.phtml:12 msgid "Welcome to Icinga Web!" msgstr "Willkommen zu Icinga Web 2!" diff --git a/application/views/scripts/authentication/login.phtml b/application/views/scripts/authentication/login.phtml index 3446b4de3..0e11fa3b6 100644 --- a/application/views/scripts/authentication/login.phtml +++ b/application/views/scripts/authentication/login.phtml @@ -1,5 +1,12 @@

+
+ translate( + 'Welcome to Icinga Web 2. For users of the screen reader Jaws full and expectant compliant' + . ' accessibility is possible only with use of the Firefox browser. VoiceOver on Mac OS X is tested on' + . ' Chrome, Safari and Firefox.' + ) ?> +

Date: Thu, 2 Jun 2016 17:23:38 +0200 Subject: [PATCH 019/337] Make delete action in the comment and dowtime list view accessible fixes #11883 --- .../scripts/partials/comment/comment-detail.phtml | 10 +++++++++- .../scripts/partials/downtime/downtime-header.phtml | 9 ++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml b/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml index 6d27a14a4..079fd5880 100644 --- a/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml +++ b/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml @@ -43,6 +43,10 @@ $this->formatTime($comment->expiration) )) : '' ?> setAttrib('class', $deleteButton->getAttrib('class') . ' remove-action'); @@ -52,10 +56,14 @@ 'comment_is_service' => isset($comment->service_description) ) ); + $deleteButton->getElement('btn_submit') + ->setAttrib('aria-label', $this->translate('Delete comment')) + ->setAttrib('id', $buttonId) + ->setAttrib('aria-describedby', $buttonId . ' ' . $textId); echo $deleteButton; } ?>

-

+

id=""> nl2br($this->escapeComment($comment->comment)) ?>

diff --git a/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml b/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml index 96130db7a..eb9f26ac3 100644 --- a/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml +++ b/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml @@ -53,6 +53,9 @@ setAttrib('class', $deleteButton->getAttrib('class') . ' remove-action'); @@ -62,11 +65,15 @@ 'downtime_is_service' => isset($downtime->service_description) ) ); + $deleteButton->getElement('btn_submit') + ->setAttrib('aria-label', $this->translate('Delete downtime')) + ->setAttrib('id', $buttonId) + ->setAttrib('aria-describedby', $buttonId . ' ' . $textId); echo $deleteButton; } ?>
-

+

id=""> nl2br($this->escapeComment($downtime->comment)) ?>

From fee45a24386a60619b582fdc1b494a05d547b8db Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 2 Jun 2016 17:27:32 +0200 Subject: [PATCH 020/337] Database: Never ignore asterisk filters fixes #11885 --- library/Icinga/Data/Db/DbQuery.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/library/Icinga/Data/Db/DbQuery.php b/library/Icinga/Data/Db/DbQuery.php index 72c9b7743..48607b66d 100644 --- a/library/Icinga/Data/Db/DbQuery.php +++ b/library/Icinga/Data/Db/DbQuery.php @@ -4,6 +4,7 @@ namespace Icinga\Data\Db; use Exception; +use Zend_Db_Expr; use Zend_Db_Select; use Icinga\Application\Logger; use Icinga\Data\Filter\FilterAnd; @@ -306,19 +307,13 @@ class DbQuery extends SimpleQuery throw new QueryException('Unable to render array expressions with operators other than equal or not equal'); } elseif ($sign === '=' && strpos($expression, '*') !== false) { if ($expression === '*') { - // We'll ignore such filters as it prevents index usage and because "*" means anything, anything means - // all whereas all means that whether we use a filter to match anything or no filter at all makes no - // difference, except for performance reasons... - return ''; + return new Zend_Db_Expr('TRUE'); } return $col . ' LIKE ' . $this->escapeForSql($this->escapeWildcards($expression)); } elseif ($sign === '!=' && strpos($expression, '*') !== false) { if ($expression === '*') { - // We'll ignore such filters as it prevents index usage and because "*" means nothing, so whether we're - // using a real column with a valid comparison here or just an expression which cannot be evaluated to - // true makes no difference, except for performance reasons... - return $this->escapeForSql(0); + return new Zend_Db_Expr('FALSE'); } return $col . ' NOT LIKE ' . $this->escapeForSql($this->escapeWildcards($expression)); From de61e0c703f611f337feb91696ea80f8a9b97ada Mon Sep 17 00:00:00 2001 From: Florian Strohmaier Date: Mon, 23 May 2016 11:16:27 +0200 Subject: [PATCH 021/337] CSS: Remove border-radius on focussed input on login screen --- public/css/icinga/login.less | 1 + 1 file changed, 1 insertion(+) diff --git a/public/css/icinga/login.less b/public/css/icinga/login.less index 9a91a87ef..1da2690ed 100644 --- a/public/css/icinga/login.less +++ b/public/css/icinga/login.less @@ -55,6 +55,7 @@ .rounded-corners(3px); background-color: @body-bg-color; border: none; + border-radius: 0; color: @icinga-blue; padding-bottom: 3px; } From 3a00923116ef4f4ccda130403228f8b49067fd18 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 26 May 2016 12:01:25 +0200 Subject: [PATCH 022/337] RemoteCommandFile: Replace exec with proc_open --- .../Command/Transport/RemoteCommandFile.php | 206 +++++++++++++++++- 1 file changed, 196 insertions(+), 10 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php b/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php index 4ac365001..ab0e78ce8 100644 --- a/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php +++ b/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php @@ -73,6 +73,20 @@ class RemoteCommandFile implements CommandTransportInterface */ protected $renderer; + /** + * SSH subprocess pipes + * + * @var array + */ + protected $sshPipes; + + /** + * SSH subprocess + * + * @var resource + */ + protected $sshProcess; + /** * Create a new remote command file command transport */ @@ -274,26 +288,198 @@ class RemoteCommandFile implements CommandTransportInterface $this->port, $this->path ); - $ssh = sprintf('ssh -o BatchMode=yes -p %u', $this->port); + return $this->sendCommandString($commandString); + } + + /** + * Get the SSH command + * + * @return string + */ + protected function sshCommand() + { + $cmd = sprintf( + 'exec ssh -o BatchMode=yes -p %u', + $this->port + ); // -o BatchMode=yes for disabling interactive authentication methods + if (isset($this->user)) { - $ssh .= sprintf(' -l %s', escapeshellarg($this->user)); + $cmd .= ' -l ' . escapeshellarg($this->user); } + if (isset($this->privateKey)) { - $ssh .= sprintf(' -o StrictHostKeyChecking=no -i %s', escapeshellarg($this->privateKey)); + // TODO: StrictHostKeyChecking=no for compat only, must be removed + $cmd .= ' -o StrictHostKeyChecking=no' + . ' -i ' . escapeshellarg($this->privateKey); } - $ssh .= sprintf( - ' %s "echo %s > %s" 2>&1', // Redirect stderr to stdout + + $cmd .= sprintf( + ' %s "cat > %s"', escapeshellarg($this->host), - escapeshellarg($commandString), escapeshellarg($this->path) ); - exec($ssh, $output, $status); - if ($status !== 0) { + + return $cmd; + } + + /** + * Send the command over SSH + * + * @param string $commandString + * + * @throws CommandTransportException + */ + protected function sendCommandString($commandString) + { + if ($this->isSshAlive()) { + $ret = fwrite($this->sshPipes[0], $commandString . "\n"); + if ($ret === false) { + $this->throwSshFailure('Cannot write to the remote command pipe'); + } elseif ($ret !== strlen($commandString) + 1) { + $this->throwSshFailure( + 'Failed to write the whole command to the remote command pipe' + ); + } + fclose($this->sshPipes[0]); + $readBuffer = $read = array($this->sshPipes[2]); + $write = null; + $except = null; + $stderr = ''; + while ( + false !== (stream_select($readBuffer, $write, $exceptBuffer, 0, 20000)) + && $this->getSshProcessStatus('running') + ) { + if (! empty($readBuffer)) { + $stderr .= stream_get_contents($readBuffer[0]); + } + // Reset buffer + $readBuffer = $read; + } + if (! empty($stderr)) { + throw new CommandTransportException('Can\'t send external Icinga command: %s', trim($stderr)); + } + } else { + $this->throwSshFailure(); + } + } + + /** + * Get the pipes of the SSH subprocess + * + * @return array + */ + protected function getSshPipes() + { + if ($this->sshPipes === null) { + $this->forkSsh(); + } + + return $this->sshPipes; + } + + /** + * Get the SSH subprocess + * + * @return resource + */ + protected function getSshProcess() + { + if ($this->sshProcess === null) { + $this->forkSsh(); + } + + return $this->sshProcess; + } + + /** + * Get the status of the SSH subprocess + * + * @param string $what + * + * @return mixed + */ + protected function getSshProcessStatus($what = null) + { + $status = proc_get_status($this->getSshProcess()); + if ($what === null) { + return $status; + } else { + return $status[$what]; + } + } + + /** + * Get whether the SSH subprocess is alive + * + * @return bool + */ + protected function isSshAlive() + { + return $this->getSshProcessStatus('running'); + } + + /** + * Fork SSH subprocess + * + * @throws CommandTransportException If fork fails + */ + protected function forkSsh() + { + $descriptors = array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w') + ); + + $this->sshProcess = proc_open($this->sshCommand(), $descriptors, $this->sshPipes); + + if (! is_resource($this->sshProcess)) { throw new CommandTransportException( - 'Can\'t send external Icinga command: %s', - implode(' ', $output) + 'Can\'t send external Icinga command, failed to fork SSH' ); } } + + /** + * Read from STDERR + * + * @return string + */ + protected function readStderr() + { + return stream_get_contents($this->sshPipes[2]); + } + + /** + * Throw SSH failure + * + * @param string $msg + * + * @throws CommandTransportException + */ + protected function throwSshFailure($msg = 'Can\'t send external Icinga command') + { + throw new CommandTransportException( + '%s: %s', + $msg, + $this->readStderr() . var_export($this->getSshProcessStatus(), true) + ); + } + + /** + * Close SSH pipes and SSH subprocess + */ + public function __destruct() + { + if (is_resource($this->sshProcess)) { + if (is_resource($this->sshPipes[0])) { + fclose($this->sshPipes[0]); + } + fclose($this->sshPipes[1]); + fclose($this->sshPipes[2]); + + proc_close($this->sshProcess); + } + } } From 54b43efb33b53d10b2406265a3101c1e263bd0ff Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 6 Jun 2016 16:21:10 +0200 Subject: [PATCH 023/337] Fix exception if module.info contains newlines that are not part of the module's description fixes #11831 --- library/Icinga/Application/Modules/Module.php | 2 + test/php/regression/Bug11831Test.php | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 test/php/regression/Bug11831Test.php diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index bfe941d70..f62684471 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -671,6 +671,8 @@ class Module $metadata->description .= $line; continue; } + } elseif (empty($line)) { + continue; } list($key, $val) = preg_split('/:\s+/', $line, 2); diff --git a/test/php/regression/Bug11831Test.php b/test/php/regression/Bug11831Test.php new file mode 100644 index 000000000..9264c46f9 --- /dev/null +++ b/test/php/regression/Bug11831Test.php @@ -0,0 +1,37 @@ +getProperty('metadataFile'); + $prop->setAccessible(true); + $prop->setValue($module, stream_get_meta_data($moduleInfoFile)['uri']); + $this->assertEquals($module->getVersion(), '1.0.0'); + fclose($moduleInfoFile); + } +} From c7b4ef00e2a6bebdc6f7a904f5c4e28ef1ca70b9 Mon Sep 17 00:00:00 2001 From: Florian Strohmaier Date: Mon, 6 Jun 2016 11:13:00 +0200 Subject: [PATCH 024/337] Replace multiple touch icon images with universal image refs #11910 --- application/layouts/scripts/layout.phtml | 1 + public/img/touch-icon.png | Bin 0 -> 4777 bytes 2 files changed, 1 insertion(+) create mode 100644 public/img/touch-icon.png diff --git a/application/layouts/scripts/layout.phtml b/application/layouts/scripts/layout.phtml index 85ba5f4c5..75238d458 100644 --- a/application/layouts/scripts/layout.phtml +++ b/application/layouts/scripts/layout.phtml @@ -47,6 +47,7 @@ $innerLayoutScript = $this->layout()->innerLayout . '.phtml'; +

diff --git a/public/img/touch-icon.png b/public/img/touch-icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..c5d5fdd0e3eca8a59faeee7f188334bfa14a2ff6
GIT binary patch
literal 4777
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

literal 0
HcmV?d00001


From 9f5e1d4785008e1fe933f026413ab2d83602b431 Mon Sep 17 00:00:00 2001
From: Florian Strohmaier 
Date: Mon, 6 Jun 2016 11:16:22 +0200
Subject: [PATCH 025/337] Remove duplicate meta tag for viewport scale

refs #11910
---
 application/layouts/scripts/layout.phtml | 2 --
 1 file changed, 2 deletions(-)

diff --git a/application/layouts/scripts/layout.phtml b/application/layouts/scripts/layout.phtml
index 75238d458..d21c56f52 100644
--- a/application/layouts/scripts/layout.phtml
+++ b/application/layouts/scripts/layout.phtml
@@ -24,12 +24,10 @@ $innerLayoutScript = $this->layout()->innerLayout . '.phtml';
  
 
   
-  
   
   
   
   <?= $this->title ? $this->escape($this->title) : 'Icinga Web' ?>
-  
   
 
   

From c59bd73e0621a70381f3b4791aeb1c94c353d529 Mon Sep 17 00:00:00 2001
From: Florian Strohmaier 
Date: Mon, 6 Jun 2016 11:17:36 +0200
Subject: [PATCH 026/337] Add meta tags for using Web 2 as mobile web app
 bookmark

refs #11910
---
 application/layouts/scripts/layout.phtml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/application/layouts/scripts/layout.phtml b/application/layouts/scripts/layout.phtml
index d21c56f52..b50f101e4 100644
--- a/application/layouts/scripts/layout.phtml
+++ b/application/layouts/scripts/layout.phtml
@@ -29,6 +29,9 @@ $innerLayoutScript = $this->layout()->innerLayout . '.phtml';
   
   <?= $this->title ? $this->escape($this->title) : 'Icinga Web' ?>
   
+  
+  
+  
 
   
 

From 825499662d39906e2ad63011c243fec4fe8d7733 Mon Sep 17 00:00:00 2001
From: Florian Strohmaier 
Date: Tue, 24 May 2016 14:24:16 +0200
Subject: [PATCH 027/337] Menu: Reduce left padding for submenu hover flyout

---
 public/css/icinga/menu.less | 1 +
 1 file changed, 1 insertion(+)

diff --git a/public/css/icinga/menu.less b/public/css/icinga/menu.less
index 61e3e0bb9..6da93bb8d 100644
--- a/public/css/icinga/menu.less
+++ b/public/css/icinga/menu.less
@@ -138,6 +138,7 @@
 
     > .nav-item {
       display: block;
+      margin-left: 0.5em;
 
       > a:hover {
         color: @text-color-inverted;

From d4b9b6d303a15f72e6f75b87a558df9519ccec60 Mon Sep 17 00:00:00 2001
From: Eric Lippmann 
Date: Tue, 7 Jun 2016 15:15:01 +0200
Subject: [PATCH 028/337] Bump version to 2.3.3

---
 ChangeLog                              | 22 ++++++++++++++++++++--
 VERSION                                |  2 +-
 icingaweb2.spec                        |  2 +-
 library/Icinga/Application/Version.php |  2 +-
 modules/doc/module.info                |  2 +-
 modules/monitoring/module.info         |  2 +-
 modules/setup/module.info              |  2 +-
 modules/test/module.info               |  2 +-
 modules/translation/module.info        |  2 +-
 9 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 8954889e5..8559f954a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,25 @@
 # Icinga Web 2 Changelog
 
+## What's New
+
+### What's New in Version 2.3.3
+
+#### Bugfixes
+
+* Bug 11267: Links in plugin output don't behave as expected
+* Bug 11348: Host aliases are not shown in detail area
+* Bug 11728: First non whitespace character after comma stripped from plugin output
+* Bug 11729: Sort by severity depends on state type
+* Bug 11737: Zero width space characters destroy state highlighting in plugin output
+* Bug 11796: Zero width space characters may destroy links in plugin output
+* Bug 11831: module.info parsing fails in case it contains newlines that are not part of the module's description
+* Bug 11850: "Add to menu" tab unnecessarily appears in command forms
+* Bug 11871: Colors used in the timeline are not accessible
+* Bug 11883: Delete action on comments and downtimes in list views not accessible because they lack context
+* Bug 11885: Database: Asterisk filters ignored when combined w/ other filters
+* Bug 11910: Web 2 lacks mobile meta tags
+* Fix remote code execution via remote command transport
+
 ### What's New in Version 2.3.2
 
 #### Feature
@@ -14,8 +34,6 @@
 * Bug 10848: Can't change items per page if filter is in modify state
 * Bug 11392: Can't configure monitoring backend via the web interface when no monitoring backend was configured
 
-## What's New
-
 ### What's New in Version 2.3.1
 
 #### Bugfixes
diff --git a/VERSION b/VERSION
index f706a60d8..fac9fc04e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v2.3.2
+v2.3.3
diff --git a/icingaweb2.spec b/icingaweb2.spec
index dbb8b6174..488053262 100644
--- a/icingaweb2.spec
+++ b/icingaweb2.spec
@@ -3,7 +3,7 @@
 %define revision 1
 
 Name:           icingaweb2
-Version:        2.3.2
+Version:        2.3.3
 Release:        %{revision}%{?dist}
 Summary:        Icinga Web 2
 Group:          Applications/System
diff --git a/library/Icinga/Application/Version.php b/library/Icinga/Application/Version.php
index 8e2863d96..493bb27b8 100644
--- a/library/Icinga/Application/Version.php
+++ b/library/Icinga/Application/Version.php
@@ -8,7 +8,7 @@ namespace Icinga\Application;
  */
 class Version
 {
-    const VERSION = '2.3.2';
+    const VERSION = '2.3.3';
 
     /**
      * Get the version of this instance of Icinga Web 2
diff --git a/modules/doc/module.info b/modules/doc/module.info
index e69a16605..8ae0d9949 100644
--- a/modules/doc/module.info
+++ b/modules/doc/module.info
@@ -1,4 +1,4 @@
 Module: doc
-Version: 2.3.2
+Version: 2.3.3
 Description: Documentation module
  Extracts, shows and exports documentation for Icinga Web 2 and its modules.
diff --git a/modules/monitoring/module.info b/modules/monitoring/module.info
index f7e69d53a..4406178b5 100644
--- a/modules/monitoring/module.info
+++ b/modules/monitoring/module.info
@@ -1,5 +1,5 @@
 Module: monitoring
-Version: 2.3.2
+Version: 2.3.3
 Description: Icinga monitoring module
  This is the core module for most Icingaweb users. It provides an
  abstraction layer for various Icinga data backends.
diff --git a/modules/setup/module.info b/modules/setup/module.info
index 7ca7e6958..fd0a115c3 100644
--- a/modules/setup/module.info
+++ b/modules/setup/module.info
@@ -1,5 +1,5 @@
 Module: setup
-Version: 2.3.2
+Version: 2.3.3
 Description: Setup module
  Web based wizard for setting up Icinga Web 2 and its modules.
  This includes the data backends (e.g. relational database, LDAP),
diff --git a/modules/test/module.info b/modules/test/module.info
index 03ad30875..dcfd55539 100644
--- a/modules/test/module.info
+++ b/modules/test/module.info
@@ -1,5 +1,5 @@
 Module: test
-Version: 2.3.2
+Version: 2.3.3
 Description: Translation module
  This module allows developers to run (unit) tests against Icinga Web 2 and
  any of its modules. Usually you do not need to enable this.
diff --git a/modules/translation/module.info b/modules/translation/module.info
index 5ae61f7d5..cfc148507 100644
--- a/modules/translation/module.info
+++ b/modules/translation/module.info
@@ -1,5 +1,5 @@
 Module: translation
-Version: 2.3.2
+Version: 2.3.3
 Description: Translation module
  This module allows developers and translators to translate Icinga Web 2 and
  its modules for multiple languages. You do not need this module to run an

From 1bbe1b36205bff70718abe78b7c5e400e862f37f Mon Sep 17 00:00:00 2001
From: Eric Lippmann 
Date: Thu, 9 Jun 2016 16:43:06 +0200
Subject: [PATCH 029/337] RemoteCommandFile: Don't close stdin to support
 transmitting multiple commands

---
 .../Command/Transport/RemoteCommandFile.php   | 24 ++-----------------
 1 file changed, 2 insertions(+), 22 deletions(-)

diff --git a/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php b/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php
index ab0e78ce8..5426bb90a 100644
--- a/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php
+++ b/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php
@@ -341,24 +341,6 @@ class RemoteCommandFile implements CommandTransportInterface
                     'Failed to write the whole command to the remote command pipe'
                 );
             }
-            fclose($this->sshPipes[0]);
-            $readBuffer = $read = array($this->sshPipes[2]);
-            $write = null;
-            $except = null;
-            $stderr = '';
-            while (
-                false !== (stream_select($readBuffer, $write, $exceptBuffer, 0, 20000))
-                && $this->getSshProcessStatus('running')
-            ) {
-                if (! empty($readBuffer)) {
-                    $stderr .= stream_get_contents($readBuffer[0]);
-                }
-                // Reset buffer
-                $readBuffer = $read;
-            }
-            if (! empty($stderr)) {
-                throw new CommandTransportException('Can\'t send external Icinga command: %s', trim($stderr));
-            }
         } else {
             $this->throwSshFailure();
         }
@@ -436,7 +418,7 @@ class RemoteCommandFile implements CommandTransportInterface
 
         if (! is_resource($this->sshProcess)) {
             throw new CommandTransportException(
-                'Can\'t send external Icinga command, failed to fork SSH'
+                'Can\'t send external Icinga command: Failed to fork SSH'
             );
         }
     }
@@ -473,9 +455,7 @@ class RemoteCommandFile implements CommandTransportInterface
     public function __destruct()
     {
         if (is_resource($this->sshProcess)) {
-            if (is_resource($this->sshPipes[0])) {
-                fclose($this->sshPipes[0]);
-            }
+            fclose($this->sshPipes[0]);
             fclose($this->sshPipes[1]);
             fclose($this->sshPipes[2]);
 

From f127611969b2a0c0d562794d088cf5009b96e3b0 Mon Sep 17 00:00:00 2001
From: Michael Friedrich 
Date: Wed, 22 Jun 2016 13:30:44 +0200
Subject: [PATCH 030/337] Fix contact service filters for 'Downtime'

refs #12019
---
 .../library/Monitoring/Backend/Ido/Query/AllcontactsQuery.php   | 2 +-
 .../library/Monitoring/Backend/Ido/Query/ContactQuery.php       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/AllcontactsQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/AllcontactsQuery.php
index ae0276ddc..09779b66e 100644
--- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/AllcontactsQuery.php
+++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/AllcontactsQuery.php
@@ -27,7 +27,7 @@ class AllcontactsQuery extends IdoQuery
             'contact_notify_service_critical'  => 'c.notify_service_critical',
             'contact_notify_service_unknown'   => 'c.notify_service_unknown',
             'contact_notify_service_flapping'  => 'c.notify_service_flapping',
-            'contact_notify_service_downtime'  => 'c.notify_service_recovery',
+            'contact_notify_service_downtime'  => 'c.notify_service_downtime',
             'contact_notify_host_recovery'     => 'c.notify_host_recovery',
             'contact_notify_host_down'         => 'c.notify_host_down',
             'contact_notify_host_unreachable'  => 'c.notify_host_unreachable',
diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactQuery.php
index c71975b8a..196c50cd2 100644
--- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactQuery.php
+++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactQuery.php
@@ -48,7 +48,7 @@ class ContactQuery extends IdoQuery
             'contact_notify_service_critical'   => 'c.notify_service_critical',
             'contact_notify_service_unknown'    => 'c.notify_service_unknown',
             'contact_notify_service_flapping'   => 'c.notify_service_flapping',
-            'contact_notify_service_downtime'   => 'c.notify_service_recovery',
+            'contact_notify_service_downtime'   => 'c.notify_service_downtime',
             'contact_notify_host_recovery'      => 'c.notify_host_recovery',
             'contact_notify_host_down'          => 'c.notify_host_down',
             'contact_notify_host_unreachable'   => 'c.notify_host_unreachable',

From fc5ed3feb1ccfac97cdf66f7418bb87a1939522b Mon Sep 17 00:00:00 2001
From: Eric Lippmann 
Date: Thu, 23 Jun 2016 14:09:55 +0200
Subject: [PATCH 031/337] Bump version to 2.3.4

---
 ChangeLog                              | 2 +-
 VERSION                                | 2 +-
 icingaweb2.spec                        | 2 +-
 library/Icinga/Application/Version.php | 2 +-
 modules/doc/module.info                | 2 +-
 modules/monitoring/module.info         | 2 +-
 modules/setup/module.info              | 2 +-
 modules/test/module.info               | 2 +-
 modules/translation/module.info        | 2 +-
 9 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 8559f954a..132c6e0f6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,7 @@
 
 ## What's New
 
-### What's New in Version 2.3.3
+### What's New in Version 2.3.4/2.3.3
 
 #### Bugfixes
 
diff --git a/VERSION b/VERSION
index fac9fc04e..20dd63223 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v2.3.3
+v2.3.4
diff --git a/icingaweb2.spec b/icingaweb2.spec
index 488053262..8a660b957 100644
--- a/icingaweb2.spec
+++ b/icingaweb2.spec
@@ -3,7 +3,7 @@
 %define revision 1
 
 Name:           icingaweb2
-Version:        2.3.3
+Version:        2.3.4
 Release:        %{revision}%{?dist}
 Summary:        Icinga Web 2
 Group:          Applications/System
diff --git a/library/Icinga/Application/Version.php b/library/Icinga/Application/Version.php
index 493bb27b8..ee4dc6505 100644
--- a/library/Icinga/Application/Version.php
+++ b/library/Icinga/Application/Version.php
@@ -8,7 +8,7 @@ namespace Icinga\Application;
  */
 class Version
 {
-    const VERSION = '2.3.3';
+    const VERSION = '2.3.4';
 
     /**
      * Get the version of this instance of Icinga Web 2
diff --git a/modules/doc/module.info b/modules/doc/module.info
index 8ae0d9949..10d96a7cf 100644
--- a/modules/doc/module.info
+++ b/modules/doc/module.info
@@ -1,4 +1,4 @@
 Module: doc
-Version: 2.3.3
+Version: 2.3.4
 Description: Documentation module
  Extracts, shows and exports documentation for Icinga Web 2 and its modules.
diff --git a/modules/monitoring/module.info b/modules/monitoring/module.info
index 4406178b5..1d43bee2a 100644
--- a/modules/monitoring/module.info
+++ b/modules/monitoring/module.info
@@ -1,5 +1,5 @@
 Module: monitoring
-Version: 2.3.3
+Version: 2.3.4
 Description: Icinga monitoring module
  This is the core module for most Icingaweb users. It provides an
  abstraction layer for various Icinga data backends.
diff --git a/modules/setup/module.info b/modules/setup/module.info
index fd0a115c3..10729ab94 100644
--- a/modules/setup/module.info
+++ b/modules/setup/module.info
@@ -1,5 +1,5 @@
 Module: setup
-Version: 2.3.3
+Version: 2.3.4
 Description: Setup module
  Web based wizard for setting up Icinga Web 2 and its modules.
  This includes the data backends (e.g. relational database, LDAP),
diff --git a/modules/test/module.info b/modules/test/module.info
index dcfd55539..609fef5fc 100644
--- a/modules/test/module.info
+++ b/modules/test/module.info
@@ -1,5 +1,5 @@
 Module: test
-Version: 2.3.3
+Version: 2.3.4
 Description: Translation module
  This module allows developers to run (unit) tests against Icinga Web 2 and
  any of its modules. Usually you do not need to enable this.
diff --git a/modules/translation/module.info b/modules/translation/module.info
index cfc148507..817194a66 100644
--- a/modules/translation/module.info
+++ b/modules/translation/module.info
@@ -1,5 +1,5 @@
 Module: translation
-Version: 2.3.3
+Version: 2.3.4
 Description: Translation module
  This module allows developers and translators to translate Icinga Web 2 and
  its modules for multiple languages. You do not need this module to run an

From cefdc496efbb52022ed86158e7706063c84ff83a Mon Sep 17 00:00:00 2001
From: Eric Lippmann 
Date: Mon, 27 Jun 2016 10:41:16 +0200
Subject: [PATCH 032/337] Forms: Remember submit from click event before

This helps Safari to determine the correct clicked or pressed submit button in case the form has more than one.
---
 public/js/icinga/events.js | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js
index 743b7a95e..aeca60db4 100644
--- a/public/js/icinga/events.js
+++ b/public/js/icinga/events.js
@@ -129,6 +129,7 @@
             $(document).on('click', 'a', { self: this }, this.linkClicked);
             $(document).on('click', 'tr[href]', { self: this }, this.linkClicked);
 
+            $(document).on('click', 'input[type="submit"], button[type="submit"]', this.rememberSubmitButton);
             // We catch all form submit events
             $(document).on('submit', 'form', { self: this }, this.submitForm);
 
@@ -197,6 +198,12 @@
             return self.autoSubmitForm(event);
         },
 
+        rememberSubmitButton: function(e) {
+            var $button = $(this);
+            var $form = $button.closest('form');
+            $form.data('submitButton', $button);
+        },
+
         autoSubmitForm: function (event) {
             return event.data.self.submitForm(event, true);
         },
@@ -217,6 +224,14 @@
             var $target;
             var data;
 
+            var $rememberedSubmittButton = $form.data('submitButton');
+            if (typeof $rememberedSubmittButton != 'undefined') {
+                if ($form.has($rememberedSubmittButton)) {
+                    $button = $rememberedSubmittButton;
+                }
+                $form.removeData('submitButton');
+            }
+
             if ($button.length === 0) {
                 var $el;
 

From dfa77b2b2f8fde6209021fd7ddd610b010b1676a Mon Sep 17 00:00:00 2001
From: Markus Frosch 
Date: Tue, 28 Jun 2016 16:16:29 +0200
Subject: [PATCH 033/337] IniRepository: Avoid using iterator_to_array with
 ArrayDatasource

While iterating ConfigObject/ArrayDatasource will not rewrite the section header into a row value as keycolumn.

In addition we now properly use the filter.

refs #12065
---
 library/Icinga/Repository/IniRepository.php | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php
index f19763138..18ae80f47 100644
--- a/library/Icinga/Repository/IniRepository.php
+++ b/library/Icinga/Repository/IniRepository.php
@@ -98,11 +98,11 @@ abstract class IniRepository extends Repository implements Extensible, Updatable
         }
 
         $newSection = null;
-        foreach (iterator_to_array($this->ds) as $section => $config) {
-            if ($filter !== null && !$filter->matches($config)) {
-                continue;
-            }
 
+        $query = $this->ds->select();
+        $query->addFilter($filter);
+
+        foreach ($query as $section => $config) {
             if ($newSection !== null) {
                 throw new StatementException(
                     t('Cannot update. Column "%s" holds a section\'s name which must be unique'),
@@ -150,12 +150,13 @@ abstract class IniRepository extends Repository implements Extensible, Updatable
             $filter = $this->requireFilter($target, $filter);
         }
 
-        foreach (iterator_to_array($this->ds) as $section => $config) {
-            if ($filter === null || $filter->matches($config)) {
-                $this->ds->removeSection($section);
-            }
-        }
+        $query = $this->ds->select();
+        $query->addFilter($filter);
 
+        foreach ($query as $section => $config) {
+            $this->ds->removeSection($section);
+        }
+        
         try {
             $this->ds->saveIni();
         } catch (Exception $e) {

From e9681de388a6b3abb7b57323aef331b90bae9494 Mon Sep 17 00:00:00 2001
From: Markus Frosch 
Date: Thu, 30 Jun 2016 13:16:05 +0200
Subject: [PATCH 034/337] RepositoryQuery: Avoid cloning sub-objects when they
 are null

fixes #12078
---
 library/Icinga/Repository/RepositoryQuery.php | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/library/Icinga/Repository/RepositoryQuery.php b/library/Icinga/Repository/RepositoryQuery.php
index f028b3e3d..b1cd09a82 100644
--- a/library/Icinga/Repository/RepositoryQuery.php
+++ b/library/Icinga/Repository/RepositoryQuery.php
@@ -69,8 +69,12 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera
      */
     public function __clone()
     {
-        $this->query = clone $this->query;
-        $this->iterator = clone $this->iterator;
+        if ($this->query !== null) {
+            $this->query = clone $this->query;
+        }
+        if ($this->iterator !== null) {
+            $this->iterator = clone $this->iterator;
+        }
     }
 
     /**

From 4774db16ef375a75b3c338a2ecc43a16ffddb900 Mon Sep 17 00:00:00 2001
From: Johannes Meyer 
Date: Mon, 11 Jul 2016 09:07:01 +0200
Subject: [PATCH 035/337] View: Add methods setHelperFunction and
 dropHelperFunction

---
 library/Icinga/Web/View.php | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/library/Icinga/Web/View.php b/library/Icinga/Web/View.php
index 8a86d4de6..ce1ea0150 100644
--- a/library/Icinga/Web/View.php
+++ b/library/Icinga/Web/View.php
@@ -119,6 +119,33 @@ class View extends Zend_View_Abstract
         return $this;
     }
 
+    /**
+     * Set or overwrite a helper function
+     *
+     * @param   string  $name
+     * @param   Closure $function
+     *
+     * @return  $this
+     */
+    public function setHelperFunction($name, Closure $function)
+    {
+        $this->helperFunctions[$name] = $function;
+        return $this;
+    }
+
+    /**
+     * Drop a helper function
+     *
+     * @param   string  $name
+     *
+     * @return  $this
+     */
+    public function dropHelperFunction($name)
+    {
+        unset($this->helperFunctions[$name]);
+        return $this;
+    }
+
     /**
      * Call a helper function
      *

From 42991b07560b85d2d8aa3c819bb1d8bf6c125b49 Mon Sep 17 00:00:00 2001
From: Eric Lippmann 
Date: Wed, 13 Jul 2016 10:24:30 +0200
Subject: [PATCH 036/337] Ensure correct URL for the refresh container button

Shifted params no longer appear in the URL which is retrieved from the request object.
We now use Url::fromRequest() instead.
---
 library/Icinga/Web/Widget/Tabs.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/Icinga/Web/Widget/Tabs.php b/library/Icinga/Web/Widget/Tabs.php
index 0c4c17ded..69a8fd396 100644
--- a/library/Icinga/Web/Widget/Tabs.php
+++ b/library/Icinga/Web/Widget/Tabs.php
@@ -315,7 +315,7 @@ EOT;
 
     private function renderRefreshTab()
     {
-        $url = Icinga::app()->getRequest()->getUrl();
+        $url = Url::fromRequest();
         $tab = $this->get($this->getActiveName());
 
         if ($tab !== null) {

From 733696864e832d73c1ac383aa48fcd8c0b341462 Mon Sep 17 00:00:00 2001
From: Eric Lippmann 
Date: Thu, 21 Jul 2016 10:56:32 +0200
Subject: [PATCH 037/337] Wrap zero width space in hidden span

This should fix copy and paste issues but still help browsers to nicely break lines in plugin output.

refs #12134
---
 .../monitoring/application/views/helpers/PluginOutput.php  | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php
index 215ad5ad8..404c9159c 100644
--- a/modules/monitoring/application/views/helpers/PluginOutput.php
+++ b/modules/monitoring/application/views/helpers/PluginOutput.php
@@ -66,7 +66,12 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract
     {
         // This is actually not required as the value is constant,
         // but as its (visual) length is 0, it's likely to be mixed up with the empty string.
-        $this->zeroWidthSpace = html_entity_decode($this->zeroWidthSpaceEnt, ENT_NOQUOTES, 'UTF-8');
+        $this->zeroWidthSpace = ''
+            . html_entity_decode($this->zeroWidthSpaceEnt, ENT_NOQUOTES, 'UTF-8')
+            . '';
+        $this->zeroWidthSpaceEnt = ''
+            . $this->zeroWidthSpaceEnt
+            . '';
     }
 
     /**

From e6ec6e05b28e7091a69bad89faf8adf5f91b555d Mon Sep 17 00:00:00 2001
From: Eric Lippmann 
Date: Thu, 21 Jul 2016 12:31:10 +0200
Subject: [PATCH 038/337] Rename Preferences to My Account

refs #10616
---
 application/controllers/AccountController.php | 56 ++++++++++++++++
 .../controllers/NavigationController.php      |  8 +--
 .../controllers/PreferenceController.php      | 65 -------------------
 .../{preference => account}/index.phtml       |  0
 library/Icinga/Application/Web.php            |  6 +-
 5 files changed, 63 insertions(+), 72 deletions(-)
 create mode 100644 application/controllers/AccountController.php
 delete mode 100644 application/controllers/PreferenceController.php
 rename application/views/scripts/{preference => account}/index.phtml (100%)

diff --git a/application/controllers/AccountController.php b/application/controllers/AccountController.php
new file mode 100644
index 000000000..378848b14
--- /dev/null
+++ b/application/controllers/AccountController.php
@@ -0,0 +1,56 @@
+getTabs()
+            ->add('account', array(
+                'title' => $this->translate('Update your account'),
+                'label' => $this->translate('My Account'),
+                'url'   => 'account'
+            ))
+            ->add('navigation', array(
+                'title' => $this->translate('List and configure your own navigation items'),
+                'label' => $this->translate('Navigation'),
+                'url'   => 'navigation'
+            ));
+    }
+
+    /**
+     * My account
+     */
+    public function indexAction()
+    {
+        $config = Config::app()->getSection('global');
+        $user = $this->Auth()->getUser();
+
+        $form = new PreferenceForm();
+        $form->setPreferences($user->getPreferences());
+        if ($config->get('config_backend', 'ini') !== 'none') {
+            $form->setStore(PreferencesStore::create(new ConfigObject(array(
+                'store'     => $config->get('config_backend', 'ini'),
+                'resource'  => $config->config_resource
+            )), $user));
+        }
+        $form->handleRequest();
+
+        $this->view->form = $form;
+        $this->getTabs()->activate('account');
+    }
+}
diff --git a/application/controllers/NavigationController.php b/application/controllers/NavigationController.php
index 51b5dae1c..221ff39f4 100644
--- a/application/controllers/NavigationController.php
+++ b/application/controllers/NavigationController.php
@@ -128,11 +128,11 @@ class NavigationController extends Controller
 
         $this->getTabs()
         ->add(
-            'preferences',
+            'account',
             array(
-                'title' => $this->translate('Adjust the preferences of Icinga Web 2 according to your needs'),
-                'label' => $this->translate('Preferences'),
-                'url'   => 'preference'
+                'title' => $this->translate('Update your account'),
+                'label' => $this->translate('My Account'),
+                'url'   => 'account'
             )
         )
         ->add(
diff --git a/application/controllers/PreferenceController.php b/application/controllers/PreferenceController.php
deleted file mode 100644
index bb4655c27..000000000
--- a/application/controllers/PreferenceController.php
+++ /dev/null
@@ -1,65 +0,0 @@
- new Tab(array(
-                'title' => t('Adjust the preferences of Icinga Web 2 according to your needs'),
-                'label' => t('Preferences'),
-                'url'   => 'preference'
-            )),
-            'navigation' => new Tab(array(
-                'title' => t('List and configure your own navigation items'),
-                'label' => t('Navigation'),
-                'url'   => 'navigation'
-            ))
-        );
-    }
-
-    /**
-     * Show form to adjust user preferences
-     */
-    public function indexAction()
-    {
-        $config = Config::app()->getSection('global');
-        $user = $this->getRequest()->getUser();
-
-        $form = new PreferenceForm();
-        $form->setPreferences($user->getPreferences());
-        if ($config->get('config_backend', 'ini') !== 'none') {
-            $form->setStore(PreferencesStore::create(new ConfigObject(array(
-                'store'     => $config->get('config_backend', 'ini'),
-                'resource'  => $config->config_resource
-            )), $user));
-        }
-        $form->handleRequest();
-
-        $this->view->form = $form;
-        $this->getTabs()->activate('preferences');
-    }
-}
diff --git a/application/views/scripts/preference/index.phtml b/application/views/scripts/account/index.phtml
similarity index 100%
rename from application/views/scripts/preference/index.phtml
rename to application/views/scripts/account/index.phtml
diff --git a/library/Icinga/Application/Web.php b/library/Icinga/Application/Web.php
index 7af9b9b8f..df5cbf729 100644
--- a/library/Icinga/Application/Web.php
+++ b/library/Icinga/Application/Web.php
@@ -346,10 +346,10 @@ class Web extends EmbeddedWeb
                     'icon'      => 'user',
                     'priority'  => 900,
                     'children'  => array(
-                        'preferences' => array(
-                            'label'     => t('Preferences'),
+                        'account' => array(
+                            'label'     => t('My Account'),
                             'priority'  => 100,
-                            'url'       => 'preference'
+                            'url'       => 'account'
                         ),
                         'logout' => array(
                             'label'         => t('Logout'),

From e62d94209f9f4016d2eac5b9afa3ef949624756d Mon Sep 17 00:00:00 2001
From: Eric Lippmann 
Date: Thu, 21 Jul 2016 17:38:19 +0200
Subject: [PATCH 039/337] Allow users to change their password if backend is db

refs #10616
---
 application/controllers/AccountController.php |  17 +++
 .../forms/Account/ChangePasswordForm.php      | 123 ++++++++++++++++++
 application/views/scripts/account/index.phtml |  11 +-
 library/Icinga/Authentication/AuthChain.php   |   2 +
 4 files changed, 150 insertions(+), 3 deletions(-)
 create mode 100644 application/forms/Account/ChangePasswordForm.php

diff --git a/application/controllers/AccountController.php b/application/controllers/AccountController.php
index 378848b14..25bc97756 100644
--- a/application/controllers/AccountController.php
+++ b/application/controllers/AccountController.php
@@ -4,7 +4,10 @@
 namespace Icinga\Controllers;
 
 use Icinga\Application\Config;
+use Icinga\Authentication\User\UserBackend;
 use Icinga\Data\ConfigObject;
+use Icinga\Exception\ConfigurationError;
+use Icinga\Forms\Account\ChangePasswordForm;
 use Icinga\Forms\PreferenceForm;
 use Icinga\User\Preferences\PreferencesStore;
 use Icinga\Web\Controller;
@@ -39,6 +42,20 @@ class AccountController extends Controller
     {
         $config = Config::app()->getSection('global');
         $user = $this->Auth()->getUser();
+        if ($user->getAdditional('backend_type') === 'db') {
+            try {
+                $userBackend = UserBackend::create($user->getAdditional('backend_name'));
+            } catch (ConfigurationError $e) {
+                $userBackend = null;
+            }
+            if ($userBackend !== null) {
+                $changePasswordForm = new ChangePasswordForm();
+                $changePasswordForm
+                    ->setBackend($userBackend)
+                    ->handleRequest();
+                $this->view->changePasswordForm = $changePasswordForm;
+            }
+        }
 
         $form = new PreferenceForm();
         $form->setPreferences($user->getPreferences());
diff --git a/application/forms/Account/ChangePasswordForm.php b/application/forms/Account/ChangePasswordForm.php
new file mode 100644
index 000000000..60c58604b
--- /dev/null
+++ b/application/forms/Account/ChangePasswordForm.php
@@ -0,0 +1,123 @@
+setSubmitLabel($this->translate('Update Account'));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createElements(array $formData)
+    {
+        $this->addElement(
+            'password',
+            'old_password',
+            array(
+                'label'         => $this->translate('Old Password'),
+                'required'      => true
+            )
+        );
+        $this->addElement(
+            'password',
+            'new_password',
+            array(
+                'label'         => $this->translate('New Password'),
+                'required'      => true
+            )
+        );
+        $this->addElement(
+            'password',
+            'new_password_confirmation',
+            array(
+                'label'         => $this->translate('Confirm New Password'),
+                'required'      => true,
+                'validators'        => array(
+                    array('identical', false, array('new_password'))
+                )
+            )
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function onSuccess()
+    {
+        $backend = $this->getBackend();
+        $backend->update(
+            $backend->getBaseTable(),
+            array('password' => $this->getElement('new_password')->getValue()),
+            Filter::where('user_name', $this->Auth()->getUser()->getUsername())
+        );
+        Notification::success($this->translate('Account updated'));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isValid($formData)
+    {
+        $valid = parent::isValid($formData);
+        if (! $valid) {
+            return false;
+        }
+
+        $oldPasswordEl = $this->getElement('old_password');
+
+        if (! $this->backend->authenticate($this->Auth()->getUser(), $oldPasswordEl->getValue())) {
+            $oldPasswordEl->addError($this->translate('Old password is invalid'));
+            $this->markAsError();
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Get the user backend
+     *
+     * @return  DbUserBackend
+     */
+    public function getBackend()
+    {
+        return $this->backend;
+    }
+
+    /**
+     * Set the user backend
+     *
+     * @param   DbUserBackend $backend
+     *
+     * @return  $this
+     */
+    public function setBackend(DbUserBackend $backend)
+    {
+        $this->backend = $backend;
+        return $this;
+    }
+}
diff --git a/application/views/scripts/account/index.phtml b/application/views/scripts/account/index.phtml
index 4ca5e6821..efc2bcbf6 100644
--- a/application/views/scripts/account/index.phtml
+++ b/application/views/scripts/account/index.phtml
@@ -1,6 +1,11 @@
 
- +
- -
\ No newline at end of file + +

translate('Account') ?>

+ + +

translate('Preferences') ?>

+ +
diff --git a/library/Icinga/Authentication/AuthChain.php b/library/Icinga/Authentication/AuthChain.php index c2cf8b9da..396e947a1 100644 --- a/library/Icinga/Authentication/AuthChain.php +++ b/library/Icinga/Authentication/AuthChain.php @@ -118,6 +118,8 @@ class AuthChain implements Authenticatable, Iterator continue; } if ($authenticated) { + $user->setAdditional('backend_name', $backend->getName()); + $user->setAdditional('backend_type', $this->config->current()->get('backend')); return true; } } From 2b50f6ff1073e9ad4a47ee291cc3a5bca8f6e58a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 3 Aug 2016 13:03:08 +0000 Subject: [PATCH 040/337] FilterExpression: render boolean true as such fixes #12299 --- library/Icinga/Data/Filter/FilterExpression.php | 13 +++++++++++++ test/php/library/Icinga/Data/Filter/FilterTest.php | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/library/Icinga/Data/Filter/FilterExpression.php b/library/Icinga/Data/Filter/FilterExpression.php index ad6105cdf..87cd73392 100644 --- a/library/Icinga/Data/Filter/FilterExpression.php +++ b/library/Icinga/Data/Filter/FilterExpression.php @@ -107,6 +107,10 @@ class FilterExpression extends Filter public function __toString() { + if ($this->isBooleanTrue()) { + return $this->column; + } + $expression = is_array($this->expression) ? '( ' . implode(' | ', $this->expression) . ' )' : $this->expression; @@ -121,6 +125,10 @@ class FilterExpression extends Filter public function toQueryString() { + if ($this->isBooleanTrue()) { + return $this->column; + } + $expression = is_array($this->expression) ? '(' . implode('|', array_map('rawurlencode', $this->expression)) . ')' : rawurlencode($this->expression); @@ -128,6 +136,11 @@ class FilterExpression extends Filter return $this->column . $this->sign . $expression; } + protected function isBooleanTrue() + { + return $this->sign === '=' && $this->expression === true; + } + /** * If $var is a scalar, do the same as strtolower() would do. * If $var is an array, map $this->strtolowerRecursive() to its elements. diff --git a/test/php/library/Icinga/Data/Filter/FilterTest.php b/test/php/library/Icinga/Data/Filter/FilterTest.php index 8b9336081..ef64b5a21 100644 --- a/test/php/library/Icinga/Data/Filter/FilterTest.php +++ b/test/php/library/Icinga/Data/Filter/FilterTest.php @@ -198,6 +198,20 @@ class FilterTest extends BaseTestCase $this->assertNotEquals((string) $c, (string) $d); } + public function testBooleanExpressionIsRenderedCorrectly() + { + $filter = Filter::fromQueryString('a&!b'); + $this->assertEquals( + $filter->toQueryString(), + 'a&!b' + ); + $this->assertEquals( + (string) $filter, + // TODO: I'd prefer to see 'a & !b' here: + 'a & (! b)' + ); + } + public function testLeadingAndTrailingWhitespaces() { $columnWithWhitespaces = Filter::where(' host ', 'localhost'); From c899456d93c722550e20bc8f6c437f0d0f780e35 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 4 Aug 2016 16:11:43 +0200 Subject: [PATCH 041/337] Fix copyright year in ChangePasswordForm refs #10616 --- application/forms/Account/ChangePasswordForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/forms/Account/ChangePasswordForm.php b/application/forms/Account/ChangePasswordForm.php index 60c58604b..5bca11cc8 100644 --- a/application/forms/Account/ChangePasswordForm.php +++ b/application/forms/Account/ChangePasswordForm.php @@ -1,5 +1,5 @@ Date: Thu, 18 Aug 2016 13:35:52 +0200 Subject: [PATCH 042/337] Repository: Allow for some more fine-grained control Filter columns, search columns and sort rules can now be returned dependent on a query's current target --- library/Icinga/Repository/Repository.php | 57 +++++++++++++------ library/Icinga/Repository/RepositoryQuery.php | 6 +- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/library/Icinga/Repository/Repository.php b/library/Icinga/Repository/Repository.php index df6a8934c..2fd8ace2e 100644 --- a/library/Icinga/Repository/Repository.php +++ b/library/Icinga/Repository/Repository.php @@ -323,25 +323,30 @@ abstract class Repository implements Selectable * * Calls $this->initializeBlacklistedQueryColumns() in case $this->blacklistedQueryColumns is null. * + * @param string $table + * * @return array */ - public function getBlacklistedQueryColumns() + public function getBlacklistedQueryColumns($table = null) { if ($this->blacklistedQueryColumns === null) { - $this->blacklistedQueryColumns = $this->initializeBlacklistedQueryColumns(); + $this->blacklistedQueryColumns = $this->initializeBlacklistedQueryColumns($table); } return $this->blacklistedQueryColumns; } /** - * Overwrite this in your repository implementation in case you - * need to initialize the blacklisted query columns lazily + * Overwrite this in your repository implementation in case you need to initialize the + * blacklisted query columns lazily or dependent on a query's current base table + * + * @param string $table * * @return array */ protected function initializeBlacklistedQueryColumns() { + // $table is not part of the signature due to PHP strict standards return array(); } @@ -350,24 +355,30 @@ abstract class Repository implements Selectable * * Calls $this->initializeFilterColumns() in case $this->filterColumns is null. * + * @param string $table + * * @return array */ - public function getFilterColumns() + public function getFilterColumns($table = null) { if ($this->filterColumns === null) { - $this->filterColumns = $this->initializeFilterColumns(); + $this->filterColumns = $this->initializeFilterColumns($table); } return $this->filterColumns; } /** - * Overwrite this in your repository implementation in case you need to initialize the filter columns lazily + * Overwrite this in your repository implementation in case you need to initialize + * the filter columns lazily or dependent on a query's current base table + * + * @param string $table * * @return array */ protected function initializeFilterColumns() { + // $table is not part of the signature due to PHP strict standards return array(); } @@ -376,24 +387,30 @@ abstract class Repository implements Selectable * * Calls $this->initializeSearchColumns() in case $this->searchColumns is null. * + * @param string $table + * * @return array */ - public function getSearchColumns() + public function getSearchColumns($table = null) { if ($this->searchColumns === null) { - $this->searchColumns = $this->initializeSearchColumns(); + $this->searchColumns = $this->initializeSearchColumns($table); } return $this->searchColumns; } /** - * Overwrite this in your repository implementation in case you need to initialize the search columns lazily + * Overwrite this in your repository implementation in case you need to initialize + * the search columns lazily or dependent on a query's current base table + * + * @param string $table * * @return array */ protected function initializeSearchColumns() { + // $table is not part of the signature due to PHP strict standards return array(); } @@ -402,24 +419,30 @@ abstract class Repository implements Selectable * * Calls $this->initializeSortRules() in case $this->sortRules is null. * + * @param string $table + * * @return array */ - public function getSortRules() + public function getSortRules($table = null) { if ($this->sortRules === null) { - $this->sortRules = $this->initializeSortRules(); + $this->sortRules = $this->initializeSortRules($table); } return $this->sortRules; } /** - * Overwrite this in your repository implementation in case you need to initialize the sort rules lazily + * Overwrite this in your repository implementation in case you need to initialize + * the sort rules lazily or dependent on a query's current base table + * + * @param string $table * * @return array */ protected function initializeSortRules() { + // $table is not part of the signature due to PHP strict standards return array(); } @@ -900,7 +923,7 @@ abstract class Repository implements Selectable throw new ProgrammingError('Table name "%s" not found', $table); } - $blacklist = $this->getBlacklistedQueryColumns(); + $blacklist = $this->getBlacklistedQueryColumns($table); $columns = array(); foreach ($queryColumns[$table] as $alias => $column) { $name = is_string($alias) ? $alias : $column; @@ -994,7 +1017,7 @@ abstract class Repository implements Selectable return false; } - return !in_array($alias, $this->getBlacklistedQueryColumns()) + return !in_array($alias, $this->getBlacklistedQueryColumns($table)) && $this->validateQueryColumnAssociation($table, $name); } @@ -1019,7 +1042,7 @@ abstract class Repository implements Selectable throw new QueryException(t('Query column "%s" not found'), $name); } - if (in_array($alias, $this->getBlacklistedQueryColumns())) { + if (in_array($alias, $this->getBlacklistedQueryColumns($table))) { throw new QueryException(t('Column "%s" cannot be queried'), $name); } @@ -1107,7 +1130,7 @@ abstract class Repository implements Selectable throw new StatementException('Statement column "%s" not found', $name); } - if (in_array($alias, $this->getBlacklistedQueryColumns())) { + if (in_array($alias, $this->getBlacklistedQueryColumns($table))) { throw new StatementException('Column "%s" cannot be referenced in a statement', $name); } diff --git a/library/Icinga/Repository/RepositoryQuery.php b/library/Icinga/Repository/RepositoryQuery.php index b1cd09a82..586403873 100644 --- a/library/Icinga/Repository/RepositoryQuery.php +++ b/library/Icinga/Repository/RepositoryQuery.php @@ -204,7 +204,7 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera */ public function getFilterColumns() { - return $this->repository->getFilterColumns(); + return $this->repository->getFilterColumns($this->target); } /** @@ -214,7 +214,7 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera */ public function getSearchColumns() { - return $this->repository->getSearchColumns(); + return $this->repository->getSearchColumns($this->target); } /** @@ -294,7 +294,7 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera */ public function getSortRules() { - return $this->repository->getSortRules(); + return $this->repository->getSortRules($this->target); } /** From bd4f21df1190833d01c1e956f4b34c7f8462e5f8 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 18 Aug 2016 15:30:47 +0200 Subject: [PATCH 043/337] Repository: Fix column caches --- library/Icinga/Repository/Repository.php | 86 +++++++++++++++++++++--- 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/library/Icinga/Repository/Repository.php b/library/Icinga/Repository/Repository.php index 2fd8ace2e..a211a253f 100644 --- a/library/Icinga/Repository/Repository.php +++ b/library/Icinga/Repository/Repository.php @@ -90,6 +90,13 @@ abstract class Repository implements Selectable */ protected $blacklistedQueryColumns; + /** + * Whether the blacklisted query columns are in the legacy format + * + * @var bool + */ + protected $legacyBlacklistedQueryColumns; + /** * The filter columns being provided * @@ -105,6 +112,13 @@ abstract class Repository implements Selectable */ protected $filterColumns; + /** + * Whether the provided filter columns are in the legacy format + * + * @var bool + */ + protected $legacyFilterColumns; + /** * The search columns (or aliases) being provided * @@ -112,6 +126,13 @@ abstract class Repository implements Selectable */ protected $searchColumns; + /** + * Whether the provided search columns are in the legacy format + * + * @var bool + */ + protected $legacySearchColumns; + /** * The sort rules to be applied on a query * @@ -140,6 +161,13 @@ abstract class Repository implements Selectable */ protected $sortRules; + /** + * Whether the provided sort rules are in the legacy format + * + * @var bool + */ + protected $legacySortRules; + /** * The value conversion rules to apply on a query or statement * @@ -330,10 +358,21 @@ abstract class Repository implements Selectable public function getBlacklistedQueryColumns($table = null) { if ($this->blacklistedQueryColumns === null) { - $this->blacklistedQueryColumns = $this->initializeBlacklistedQueryColumns($table); + $this->legacyBlacklistedQueryColumns = false; + + $blacklistedQueryColumns = $this->initializeBlacklistedQueryColumns($table); + if (is_int(key($blacklistedQueryColumns))) { + $this->blacklistedQueryColumns[$table] = $blacklistedQueryColumns; + } else { + $this->blacklistedQueryColumns = $blacklistedQueryColumns; + } + } elseif ($this->legacyBlacklistedQueryColumns === null) { + $this->legacyBlacklistedQueryColumns = is_int(key($this->blacklistedQueryColumns)); } - return $this->blacklistedQueryColumns; + return $this->legacyBlacklistedQueryColumns + ? $this->blacklistedQueryColumns + : $this->blacklistedQueryColumns[$table]; } /** @@ -362,10 +401,21 @@ abstract class Repository implements Selectable public function getFilterColumns($table = null) { if ($this->filterColumns === null) { - $this->filterColumns = $this->initializeFilterColumns($table); + $this->legacyFilterColumns = false; + + $filterColumns = $this->initializeFilterColumns($table); + $foundTables = array_intersect_key($this->getQueryColumns(), $filterColumns); + if (empty($foundTables)) { + $this->filterColumns[$table] = $filterColumns; + } else { + $this->filterColumns = $filterColumns; + } + } elseif ($this->legacyFilterColumns === null) { + $foundTables = array_intersect_key($this->getQueryColumns(), $this->filterColumns); + $this->legacyFilterColumns = empty($foundTables); } - return $this->filterColumns; + return $this->legacyFilterColumns ? $this->filterColumns : $this->filterColumns[$table]; } /** @@ -394,10 +444,19 @@ abstract class Repository implements Selectable public function getSearchColumns($table = null) { if ($this->searchColumns === null) { - $this->searchColumns = $this->initializeSearchColumns($table); + $this->legacySearchColumns = false; + + $searchColumns = $this->initializeSearchColumns($table); + if (is_int(key($searchColumns))) { + $this->searchColumns[$table] = $searchColumns; + } else { + $this->searchColumns = $searchColumns; + } + } elseif ($this->legacySearchColumns === null) { + $this->legacySearchColumns = is_int(key($this->searchColumns)); } - return $this->searchColumns; + return $this->legacySearchColumns ? $this->searchColumns : $this->searchColumns[$table]; } /** @@ -426,10 +485,21 @@ abstract class Repository implements Selectable public function getSortRules($table = null) { if ($this->sortRules === null) { - $this->sortRules = $this->initializeSortRules($table); + $this->legacySortRules = false; + + $sortRules = $this->initializeSortRules($table); + $foundTables = array_intersect_key($this->getQueryColumns(), $sortRules); + if (empty($foundTables)) { + $this->sortRules[$table] = $sortRules; + } else { + $this->sortRules = $sortRules; + } + } elseif ($this->legacySortRules === null) { + $foundTables = array_intersect_key($this->getQueryColumns(), $this->sortRules); + $this->legacyFilterColumns = empty($foundTables); } - return $this->sortRules; + return $this->legacySortRules ? $this->sortRules : $this->sortRules[$table]; } /** From 124fb848a09733900c880c2650c434fa62f090c0 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 18 Aug 2016 15:40:20 +0200 Subject: [PATCH 044/337] =?UTF-8?q?Repository:=20Fix=20column=20caches?= =?UTF-8?q?=C2=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library/Icinga/Repository/Repository.php | 34 +++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/library/Icinga/Repository/Repository.php b/library/Icinga/Repository/Repository.php index a211a253f..379614f22 100644 --- a/library/Icinga/Repository/Repository.php +++ b/library/Icinga/Repository/Repository.php @@ -370,9 +370,13 @@ abstract class Repository implements Selectable $this->legacyBlacklistedQueryColumns = is_int(key($this->blacklistedQueryColumns)); } - return $this->legacyBlacklistedQueryColumns - ? $this->blacklistedQueryColumns - : $this->blacklistedQueryColumns[$table]; + if ($this->legacyBlacklistedQueryColumns) { + return $this->blacklistedQueryColumns; + } elseif (! isset($this->blacklistedQueryColumns[$table])) { + $this->blacklistedQueryColumns[$table] = $this->initializeBlacklistedQueryColumns($table); + } + + return $this->blacklistedQueryColumns[$table]; } /** @@ -415,7 +419,13 @@ abstract class Repository implements Selectable $this->legacyFilterColumns = empty($foundTables); } - return $this->legacyFilterColumns ? $this->filterColumns : $this->filterColumns[$table]; + if ($this->legacyFilterColumns) { + return $this->filterColumns; + } elseif (! isset($this->filterColumns[$table])) { + $this->filterColumns[$table] = $this->initializeFilterColumns($table); + } + + return $this->filterColumns[$table]; } /** @@ -456,7 +466,13 @@ abstract class Repository implements Selectable $this->legacySearchColumns = is_int(key($this->searchColumns)); } - return $this->legacySearchColumns ? $this->searchColumns : $this->searchColumns[$table]; + if ($this->legacySearchColumns) { + return $this->searchColumns; + } elseif (! isset($this->searchColumns[$table])) { + $this->searchColumns[$table] = $this->initializeSearchColumns($table); + } + + return $this->searchColumns[$table]; } /** @@ -499,7 +515,13 @@ abstract class Repository implements Selectable $this->legacyFilterColumns = empty($foundTables); } - return $this->legacySortRules ? $this->sortRules : $this->sortRules[$table]; + if ($this->legacySortRules) { + return $this->sortRules; + } elseif (! isset($this->sortRules[$table])) { + $this->sortRules[$table] = $this->initializeSortRules($table); + } + + return $this->sortRules[$table]; } /** From 1993ae2ed2ee8ec2bc53a57dec78d733bb921b9d Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 25 Aug 2016 08:54:04 +0200 Subject: [PATCH 045/337] js: Fix that the menu is not reloaded in case no search is available fixes #12541 --- public/js/icinga/behavior/form.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/public/js/icinga/behavior/form.js b/public/js/icinga/behavior/form.js index 750f02223..eae75168d 100644 --- a/public/js/icinga/behavior/form.js +++ b/public/js/icinga/behavior/form.js @@ -60,10 +60,9 @@ if ($search[0] === document.activeElement) { return null; } - var search = $container.find('#search').val(); - if (search.length) { + if ($search.length) { var $content = $('
').append(content); - $content.find('#search').attr('value', search).addClass('active'); + $content.find('#search').attr('value', $search.val()).addClass('active'); return $content.html(); } return content; From 0e307c6482f85f0fdbd277579620d114815959a4 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 12:57:50 +0200 Subject: [PATCH 046/337] IDO: Provide comment name column The Icinga 2 API requires the comment's name when removing the comment. refs #11398 --- .../library/Monitoring/Backend/Ido/Query/CommentQuery.php | 4 ++++ .../library/Monitoring/Backend/Ido/Query/HostcommentQuery.php | 4 ++++ .../Monitoring/Backend/Ido/Query/ServicecommentQuery.php | 4 ++++ modules/monitoring/library/Monitoring/DataView/Comment.php | 1 + .../monitoring/library/Monitoring/DataView/Hostcomment.php | 1 + .../monitoring/library/Monitoring/DataView/Servicecomment.php | 1 + 6 files changed, 15 insertions(+) diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php index 2fd3fe15f..1d723a0c1 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php @@ -23,6 +23,7 @@ class CommentQuery extends IdoQuery 'comment_expiration' => 'c.comment_expiration', 'comment_internal_id' => 'c.comment_internal_id', 'comment_is_persistent' => 'c.comment_is_persistent', + 'comment_name' => 'c.comment_name', 'comment_timestamp' => 'c.comment_timestamp', 'comment_type' => 'c.comment_type', 'instance_name' => 'c.instance_name', @@ -85,6 +86,9 @@ class CommentQuery extends IdoQuery */ protected function joinBaseTables() { + if (version_compare($this->getIdoVersion(), '1.14.0', '<')) { + $this->columnMap['comments']['comment_name'] = '(NULL)'; + } $this->commentQuery = $this->db->select(); $this->select->from( array('c' => $this->commentQuery), diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostcommentQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostcommentQuery.php index 056eda400..30cd414ed 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostcommentQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostcommentQuery.php @@ -34,6 +34,7 @@ class HostcommentQuery extends IdoQuery 'comment_expiration' => 'CASE c.expires WHEN 1 THEN UNIX_TIMESTAMP(c.expiration_time) ELSE NULL END', 'comment_internal_id' => 'c.internal_comment_id', 'comment_is_persistent' => 'c.is_persistent', + 'comment_name' => 'c.name', 'comment_timestamp' => 'UNIX_TIMESTAMP(c.comment_time)', 'comment_type' => "CASE c.entry_type WHEN 1 THEN 'comment' WHEN 2 THEN 'downtime' WHEN 3 THEN 'flapping' WHEN 4 THEN 'ack' END", 'host' => 'ho.name1 COLLATE latin1_general_ci', @@ -72,6 +73,9 @@ class HostcommentQuery extends IdoQuery */ protected function joinBaseTables() { + if (version_compare($this->getIdoVersion(), '1.14.0', '<')) { + $this->columnMap['comments']['comment_name'] = '(NULL)'; + } $this->select->from( array('c' => $this->prefix . 'comments'), array() diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicecommentQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicecommentQuery.php index bebc6b7fd..2225058fd 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicecommentQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicecommentQuery.php @@ -34,6 +34,7 @@ class ServicecommentQuery extends IdoQuery 'comment_expiration' => 'CASE c.expires WHEN 1 THEN UNIX_TIMESTAMP(c.expiration_time) ELSE NULL END', 'comment_internal_id' => 'c.internal_comment_id', 'comment_is_persistent' => 'c.is_persistent', + 'comment_name' => 'c.name', 'comment_timestamp' => 'UNIX_TIMESTAMP(c.comment_time)', 'comment_type' => "CASE c.entry_type WHEN 1 THEN 'comment' WHEN 2 THEN 'downtime' WHEN 3 THEN 'flapping' WHEN 4 THEN 'ack' END", 'host' => 'so.name1 COLLATE latin1_general_ci', @@ -77,6 +78,9 @@ class ServicecommentQuery extends IdoQuery */ protected function joinBaseTables() { + if (version_compare($this->getIdoVersion(), '1.14.0', '<')) { + $this->columnMap['comments']['comment_name'] = '(NULL)'; + } $this->select->from( array('c' => $this->prefix . 'comments'), array() diff --git a/modules/monitoring/library/Monitoring/DataView/Comment.php b/modules/monitoring/library/Monitoring/DataView/Comment.php index 56facbec6..3a035bc79 100644 --- a/modules/monitoring/library/Monitoring/DataView/Comment.php +++ b/modules/monitoring/library/Monitoring/DataView/Comment.php @@ -19,6 +19,7 @@ class Comment extends DataView 'comment_expiration', 'comment_internal_id', 'comment_is_persistent', + 'comment_name', 'comment_timestamp', 'comment_type', 'host_display_name', diff --git a/modules/monitoring/library/Monitoring/DataView/Hostcomment.php b/modules/monitoring/library/Monitoring/DataView/Hostcomment.php index 92020abe5..74fc2efef 100644 --- a/modules/monitoring/library/Monitoring/DataView/Hostcomment.php +++ b/modules/monitoring/library/Monitoring/DataView/Hostcomment.php @@ -20,6 +20,7 @@ class Hostcomment extends DataView 'comment_expiration', 'comment_internal_id', 'comment_is_persistent', + 'comment_name', 'comment_timestamp', 'comment_type', 'host_display_name', diff --git a/modules/monitoring/library/Monitoring/DataView/Servicecomment.php b/modules/monitoring/library/Monitoring/DataView/Servicecomment.php index b404955d7..78c133386 100644 --- a/modules/monitoring/library/Monitoring/DataView/Servicecomment.php +++ b/modules/monitoring/library/Monitoring/DataView/Servicecomment.php @@ -20,6 +20,7 @@ class Servicecomment extends DataView 'comment_expiration', 'comment_internal_id', 'comment_is_persistent', + 'comment_name', 'comment_timestamp', 'comment_type', 'host_display_name', From c55c5a9e64e56c8b6075663620eac3d75b53f867 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 12:59:39 +0200 Subject: [PATCH 047/337] IDO: Provide downtime name column The Icinga 2 API requires the downtimes's name when removing the downtime. refs #11398 --- .../library/Monitoring/Backend/Ido/Query/DowntimeQuery.php | 4 ++++ .../Monitoring/Backend/Ido/Query/HostdowntimeQuery.php | 4 ++++ .../Monitoring/Backend/Ido/Query/ServicedowntimeQuery.php | 4 ++++ modules/monitoring/library/Monitoring/DataView/Downtime.php | 1 + .../monitoring/library/Monitoring/DataView/Hostdowntime.php | 1 + .../library/Monitoring/DataView/Servicedowntime.php | 1 + 6 files changed, 15 insertions(+) diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php index ae0c08d9c..2c85c1547 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/DowntimeQuery.php @@ -27,6 +27,7 @@ class DowntimeQuery extends IdoQuery 'downtime_is_fixed' => 'd.downtime_is_fixed', 'downtime_is_flexible' => 'd.downtime_is_flexible', 'downtime_is_in_effect' => 'd.downtime_is_in_effect', + 'downtime_name' => 'd.downtime_name', 'downtime_scheduled_end' => 'd.downtime_scheduled_end', 'downtime_scheduled_start' => 'd.downtime_scheduled_start', 'downtime_start' => 'd.downtime_start', @@ -90,6 +91,9 @@ class DowntimeQuery extends IdoQuery */ protected function joinBaseTables() { + if (version_compare($this->getIdoVersion(), '1.14.0', '<')) { + $this->columnMap['downtimes']['downtime_name'] = '(NULL)'; + } $this->downtimeQuery = $this->db->select(); $this->select->from( array('d' => $this->downtimeQuery), diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostdowntimeQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostdowntimeQuery.php index b3be3420b..3bea210de 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostdowntimeQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostdowntimeQuery.php @@ -38,6 +38,7 @@ class HostdowntimeQuery extends IdoQuery 'downtime_is_fixed' => 'sd.is_fixed', 'downtime_is_flexible' => 'CASE WHEN sd.is_fixed = 0 THEN 1 ELSE 0 END', 'downtime_is_in_effect' => 'sd.is_in_effect', + 'downtime_name' => 'sd.name', 'downtime_scheduled_end' => 'UNIX_TIMESTAMP(sd.scheduled_end_time)', 'downtime_scheduled_start' => 'UNIX_TIMESTAMP(sd.scheduled_start_time)', 'downtime_start' => 'UNIX_TIMESTAMP(CASE WHEN UNIX_TIMESTAMP(sd.trigger_time) > 0 then sd.trigger_time ELSE sd.scheduled_start_time END)', @@ -78,6 +79,9 @@ class HostdowntimeQuery extends IdoQuery */ protected function joinBaseTables() { + if (version_compare($this->getIdoVersion(), '1.14.0', '<')) { + $this->columnMap['downtimes']['downtime_name'] = '(NULL)'; + } $this->select->from( array('sd' => $this->prefix . 'scheduleddowntime'), array() diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicedowntimeQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicedowntimeQuery.php index c0b0fca2a..2c370bea9 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicedowntimeQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicedowntimeQuery.php @@ -38,6 +38,7 @@ class ServicedowntimeQuery extends IdoQuery 'downtime_is_fixed' => 'sd.is_fixed', 'downtime_is_flexible' => 'CASE WHEN sd.is_fixed = 0 THEN 1 ELSE 0 END', 'downtime_is_in_effect' => 'sd.is_in_effect', + 'downtime_name' => 'sd.name', 'downtime_scheduled_end' => 'UNIX_TIMESTAMP(sd.scheduled_end_time)', 'downtime_scheduled_start' => 'UNIX_TIMESTAMP(sd.scheduled_start_time)', 'downtime_start' => 'UNIX_TIMESTAMP(CASE WHEN UNIX_TIMESTAMP(sd.trigger_time) > 0 then sd.trigger_time ELSE sd.scheduled_start_time END)', @@ -83,6 +84,9 @@ class ServicedowntimeQuery extends IdoQuery */ protected function joinBaseTables() { + if (version_compare($this->getIdoVersion(), '1.14.0', '<')) { + $this->columnMap['downtimes']['downtime_name'] = '(NULL)'; + } $this->select->from( array('sd' => $this->prefix . 'scheduleddowntime'), array() diff --git a/modules/monitoring/library/Monitoring/DataView/Downtime.php b/modules/monitoring/library/Monitoring/DataView/Downtime.php index 8e14a13ee..ca42e2da5 100644 --- a/modules/monitoring/library/Monitoring/DataView/Downtime.php +++ b/modules/monitoring/library/Monitoring/DataView/Downtime.php @@ -23,6 +23,7 @@ class Downtime extends DataView 'downtime_is_fixed', 'downtime_is_flexible', 'downtime_is_in_effect', + 'downtime_name', 'downtime_scheduled_end', 'downtime_scheduled_start', 'downtime_start', diff --git a/modules/monitoring/library/Monitoring/DataView/Hostdowntime.php b/modules/monitoring/library/Monitoring/DataView/Hostdowntime.php index 8782b33fe..f5e4e8005 100644 --- a/modules/monitoring/library/Monitoring/DataView/Hostdowntime.php +++ b/modules/monitoring/library/Monitoring/DataView/Hostdowntime.php @@ -24,6 +24,7 @@ class Hostdowntime extends DataView 'downtime_is_fixed', 'downtime_is_flexible', 'downtime_is_in_effect', + 'downtime_name', 'downtime_scheduled_end', 'downtime_scheduled_start', 'downtime_start', diff --git a/modules/monitoring/library/Monitoring/DataView/Servicedowntime.php b/modules/monitoring/library/Monitoring/DataView/Servicedowntime.php index baed8c195..43d895ede 100644 --- a/modules/monitoring/library/Monitoring/DataView/Servicedowntime.php +++ b/modules/monitoring/library/Monitoring/DataView/Servicedowntime.php @@ -21,6 +21,7 @@ class Servicedowntime extends DataView 'downtime_is_fixed', 'downtime_is_flexible', 'downtime_is_in_effect', + 'downtime_name', 'downtime_scheduled_end', 'downtime_scheduled_start', 'downtime_start', From b8df909ad5555390fae9050b9447e6eca0697ac4 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 13:00:39 +0200 Subject: [PATCH 048/337] Add name property to the delete comment command refs #11398 --- .../Command/Object/DeleteCommentCommand.php | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/modules/monitoring/library/Monitoring/Command/Object/DeleteCommentCommand.php b/modules/monitoring/library/Monitoring/Command/Object/DeleteCommentCommand.php index 9ba9e0d85..4d791064f 100644 --- a/modules/monitoring/library/Monitoring/Command/Object/DeleteCommentCommand.php +++ b/modules/monitoring/library/Monitoring/Command/Object/DeleteCommentCommand.php @@ -17,6 +17,15 @@ class DeleteCommentCommand extends IcingaCommand */ protected $commentId; + /** + * Name of the comment (Icinga 2.4+) + * + * Required for removing the comment via Icinga 2's API. + * + * @var string + */ + protected $commentName; + /** * Whether the command affects a service comment * @@ -47,6 +56,33 @@ class DeleteCommentCommand extends IcingaCommand return $this; } + /** + * Get the name of the comment (Icinga 2.4+) + * + * Required for removing the comment via Icinga 2's API. + * + * @return string + */ + public function getCommentName() + { + return $this->commentName; + } + + /** + * Set the name of the comment (Icinga 2.4+) + * + * Required for removing the comment via Icinga 2's API. + * + * @param string $commentName + * + * @return $this + */ + public function setCommentName($commentName) + { + $this->commentName = $commentName; + return $this; + } + /** * Get whether the command affects a service comment * From fa113e023b658470c13756bd463d8b64d29db95b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 31 Aug 2016 15:11:55 +0200 Subject: [PATCH 049/337] dashboard/new-dashlet: don't allow external URLs refs #11920 --- application/forms/Dashboard/DashletForm.php | 3 +- .../Form/Validator/InternalUrlValidator.php | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 library/Icinga/Web/Form/Validator/InternalUrlValidator.php diff --git a/application/forms/Dashboard/DashletForm.php b/application/forms/Dashboard/DashletForm.php index f3df2c216..07d5c327e 100644 --- a/application/forms/Dashboard/DashletForm.php +++ b/application/forms/Dashboard/DashletForm.php @@ -3,6 +3,7 @@ namespace Icinga\Forms\Dashboard; +use Icinga\Web\Form\Validator\InternalUrlValidator; use Icinga\Web\Widget\Dashboard; use Icinga\Web\Form; use Icinga\Web\Form\Validator\UrlValidator; @@ -70,7 +71,7 @@ class DashletForm extends Form 'description' => $this->translate( 'Enter url being loaded in the dashlet. You can paste the full URL, including filters.' ), - 'validators' => array(new UrlValidator()) + 'validators' => array(new UrlValidator(), new InternalUrlValidator()) ) ); $this->addElement( diff --git a/library/Icinga/Web/Form/Validator/InternalUrlValidator.php b/library/Icinga/Web/Form/Validator/InternalUrlValidator.php new file mode 100644 index 000000000..a0230c782 --- /dev/null +++ b/library/Icinga/Web/Form/Validator/InternalUrlValidator.php @@ -0,0 +1,37 @@ +isExternal(); + if ($isExternal) { + $this->_error('IS_EXTERNAL'); + } + return ! $isExternal; + } + + /** + * {@inheritdoc} + */ + protected function _error($messageKey, $value = null) + { + if ($messageKey === 'IS_EXTERNAL') { + $this->_messages[$messageKey] = t('The url must not be external.'); + } else { + parent::_error($messageKey, $value); + } + } +} From 62f2f92ae8517b60bcfbcdbe58a94a9b72b9cc1e Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 13:01:19 +0200 Subject: [PATCH 050/337] Add name property to the delete downtime command refs #11398 --- .../Command/Object/DeleteDowntimeCommand.php | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/modules/monitoring/library/Monitoring/Command/Object/DeleteDowntimeCommand.php b/modules/monitoring/library/Monitoring/Command/Object/DeleteDowntimeCommand.php index ded5cc5e2..63f4bbce3 100644 --- a/modules/monitoring/library/Monitoring/Command/Object/DeleteDowntimeCommand.php +++ b/modules/monitoring/library/Monitoring/Command/Object/DeleteDowntimeCommand.php @@ -17,6 +17,15 @@ class DeleteDowntimeCommand extends IcingaCommand */ protected $downtimeId; + /** + * Name of the downtime (Icinga 2.4+) + * + * Required for removing the downtime via Icinga 2's API. + * + * @var string + */ + protected $downtimeName; + /** * Whether the command affects a service downtime * @@ -47,6 +56,33 @@ class DeleteDowntimeCommand extends IcingaCommand return $this; } + /** + * Get the name of the downtime (Icinga 2.4+) + * + * Required for removing the downtime via Icinga 2's API. + * + * @return string + */ + public function getDowntimeName() + { + return $this->downtimeName; + } + + /** + * Set the name of the downtime (Icinga 2.4+) + * + * Required for removing the downtime via Icinga 2's API. + * + * @param string $downtimeName + * + * @return $this + */ + public function setDowntimeName($downtimeName) + { + $this->downtimeName = $downtimeName; + return $this; + } + /** * Get whether the command affects a service * From 59dceb472905cd587f690f9aebdac172810adbff Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 13:01:57 +0200 Subject: [PATCH 051/337] Add name to the delete comment command form refs #11398 --- .../Object/DeleteCommentCommandForm.php | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php index e526d60e7..3b1cd5927 100644 --- a/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php @@ -20,42 +20,6 @@ class DeleteCommentCommandForm extends CommandForm $this->setAttrib('class', 'inline'); } - /** - * {@inheritdoc} - */ - public function createElements(array $formData = array()) - { - $this->addElements( - array( - array( - 'hidden', - 'comment_id', - array( - 'required' => true, - 'validators' => array('NotEmpty'), - 'decorators' => array('ViewHelper') - ) - ), - array( - 'hidden', - 'comment_is_service', - array( - 'filters' => array('Boolean'), - 'decorators' => array('ViewHelper') - ) - ), - array( - 'hidden', - 'redirect', - array( - 'decorators' => array('ViewHelper') - ) - ) - ) - ); - return $this; - } - /** * {@inheritdoc} */ @@ -80,14 +44,59 @@ class DeleteCommentCommandForm extends CommandForm return $this; } + /** + * {@inheritdoc} + */ + public function createElements(array $formData = array()) + { + $this->addElements( + array( + array( + 'hidden', + 'comment_id', + array( + 'required' => true, + 'validators' => array('NotEmpty'), + 'decorators' => array('ViewHelper') + ) + ), + array( + 'hidden', + 'comment_is_service', + array( + 'filters' => array('Boolean'), + 'decorators' => array('ViewHelper') + ) + ), + array( + 'hidden', + 'comment_name', + array( + 'decorators' => array('ViewHelper') + ) + ), + array( + 'hidden', + 'redirect', + array( + 'decorators' => array('ViewHelper') + ) + ) + ) + ); + return $this; + } + /** * {@inheritdoc} */ public function onSuccess() { $cmd = new DeleteCommentCommand(); - $cmd->setIsService($this->getElement('comment_is_service')->getValue()) - ->setCommentId($this->getElement('comment_id')->getValue()); + $cmd + ->setCommentId($this->getElement('comment_id')->getValue()) + ->setCommentName($this->getElement('comment_name')->getValue()) + ->setIsService($this->getElement('comment_is_service')->getValue()); $this->getTransport($this->request)->send($cmd); $redirect = $this->getElement('redirect')->getValue(); if (! empty($redirect)) { From 69db59a38eb613a622ef0dae065f63bf64c2cc9e Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 13:02:16 +0200 Subject: [PATCH 052/337] Add name to the delete downtime command form refs #11398 --- .../Command/Object/DeleteDowntimeCommandForm.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php index 15c0e8ebd..be2109eec 100644 --- a/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php @@ -68,6 +68,13 @@ class DeleteDowntimeCommandForm extends CommandForm 'filters' => array('Boolean') ) ), + array( + 'hidden', + 'downtime_name', + array( + 'decorators' => array('ViewHelper') + ) + ), array( 'hidden', 'redirect', @@ -86,8 +93,10 @@ class DeleteDowntimeCommandForm extends CommandForm public function onSuccess() { $cmd = new DeleteDowntimeCommand(); - $cmd->setDowntimeId($this->getElement('downtime_id')->getValue()); - $cmd->setIsService($this->getElement('downtime_is_service')->getValue()); + $cmd + ->setDowntimeId($this->getElement('downtime_id')->getValue()) + ->setDowntimeName($this->getElement('downtime_name')->getValue()) + ->setIsService($this->getElement('downtime_is_service')->getValue()); $this->getTransport($this->request)->send($cmd); $redirect = $this->getElement('redirect')->getValue(); From 39f225a6276bdb763f3581fe8fc7b2433a584551 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 13:04:11 +0200 Subject: [PATCH 053/337] Select comment name in the comment list view refs #11398 --- modules/monitoring/application/controllers/ListController.php | 1 + .../views/scripts/partials/comment/comment-detail.phtml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 8e493ed44..22630caab 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -422,6 +422,7 @@ class ListController extends Controller 'type' => 'comment_type', 'persistent' => 'comment_is_persistent', 'expiration' => 'comment_expiration', + 'name' => 'comment_name', 'host_name', 'service_description', 'host_display_name', diff --git a/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml b/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml index 079fd5880..2ed932916 100644 --- a/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml +++ b/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml @@ -53,7 +53,8 @@ $deleteButton->populate( array( 'comment_id' => $comment->id, - 'comment_is_service' => isset($comment->service_description) + 'comment_is_service' => isset($comment->service_description), + 'comment_name' => $comment->name ) ); $deleteButton->getElement('btn_submit') From 25a17b0bc94a49b3666b6fe55d025bb127def88a Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 13:46:09 +0200 Subject: [PATCH 054/337] Select comment name in the comment detail view refs #11398 --- .../monitoring/application/controllers/CommentController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/monitoring/application/controllers/CommentController.php b/modules/monitoring/application/controllers/CommentController.php index 122927531..b5d5e6720 100644 --- a/modules/monitoring/application/controllers/CommentController.php +++ b/modules/monitoring/application/controllers/CommentController.php @@ -37,6 +37,7 @@ class CommentController extends Controller 'type' => 'comment_type', 'persistent' => 'comment_is_persistent', 'expiration' => 'comment_expiration', + 'name' => 'comment_name', 'host_name', 'service_description', 'host_display_name', @@ -73,6 +74,7 @@ class CommentController extends Controller ->populate(array( 'comment_id' => $this->comment->id, 'comment_is_service' => isset($this->comment->service_description), + 'comment_name' => $this->comment->name, 'redirect' => $listUrl )) ->handleRequest(); From b0be1e56ddeb4e87c6ff36a89e5e16179874433d Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 13:47:31 +0200 Subject: [PATCH 055/337] Select comment name in the multi select detail view refs #11398 --- .../monitoring/application/controllers/CommentsController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/monitoring/application/controllers/CommentsController.php b/modules/monitoring/application/controllers/CommentsController.php index ac118c477..9375636dc 100644 --- a/modules/monitoring/application/controllers/CommentsController.php +++ b/modules/monitoring/application/controllers/CommentsController.php @@ -46,6 +46,7 @@ class CommentsController extends Controller 'type' => 'comment_type', 'persistent' => 'comment_is_persistent', 'expiration' => 'comment_expiration', + 'name' => 'comment_name', 'host_name', 'service_description', 'host_display_name', From 573752168314592517d944a1d61d8e46bb64652e Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 13:47:56 +0200 Subject: [PATCH 056/337] Add comment name to the delete comments command form refs #11398 --- .../forms/Command/Object/DeleteCommentsCommandForm.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php index 2b5b9a79e..71886639e 100644 --- a/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php @@ -72,6 +72,7 @@ class DeleteCommentsCommandForm extends CommandForm $cmd = new DeleteCommentCommand(); $cmd ->setCommentId($comment->id) + ->setCommentName($comment->name) ->setIsService(isset($comment->service_description)); $this->getTransport($this->request)->send($cmd); } From 57406245cae51860e1a3ed20273d609c1e4cb456 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 14:05:36 +0200 Subject: [PATCH 057/337] Select downtime name in the downtime list view refs #11398 --- .../monitoring/application/controllers/ListController.php | 1 + .../views/scripts/partials/downtime/downtime-header.phtml | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 22630caab..5499cfdf9 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -229,6 +229,7 @@ class ListController extends Controller 'is_fixed' => 'downtime_is_fixed', 'is_in_effect' => 'downtime_is_in_effect', 'entry_time' => 'downtime_entry_time', + 'name' => 'downtime_name', 'host_state', 'service_state', 'host_name', diff --git a/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml b/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml index eb9f26ac3..bfe88a761 100644 --- a/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml +++ b/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml @@ -61,8 +61,9 @@ $deleteButton->setAttrib('class', $deleteButton->getAttrib('class') . ' remove-action'); $deleteButton->populate( array( - 'downtime_id' => $downtime->id, - 'downtime_is_service' => isset($downtime->service_description) + 'downtime_id' => $downtime->id, + 'downtime_is_service' => isset($downtime->service_description), + 'downtime_name' => $downtime->name ) ); $deleteButton->getElement('btn_submit') From c982c96e164f23308c6cd7957de210349a7ad712 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 14:07:15 +0200 Subject: [PATCH 058/337] Select downtime name in the downtime detail view refs #11398 --- .../monitoring/application/controllers/DowntimeController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/monitoring/application/controllers/DowntimeController.php b/modules/monitoring/application/controllers/DowntimeController.php index 6e8a19809..e19ce38c0 100644 --- a/modules/monitoring/application/controllers/DowntimeController.php +++ b/modules/monitoring/application/controllers/DowntimeController.php @@ -44,6 +44,7 @@ class DowntimeController extends Controller 'is_fixed' => 'downtime_is_fixed', 'is_in_effect' => 'downtime_is_in_effect', 'entry_time' => 'downtime_entry_time', + 'name' => 'downtime_name', 'host_state', 'service_state', 'host_name', @@ -91,6 +92,7 @@ class DowntimeController extends Controller ->populate(array( 'downtime_id' => $this->downtime->id, 'downtime_is_service' => $isService, + 'downtime_name' => $this->downtime->name, 'redirect' => Url::fromPath('monitoring/list/downtimes'), )) ->handleRequest(); From 9c766c02413cb95918e6128a2875cad965f9464a Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 14:08:05 +0200 Subject: [PATCH 059/337] Select downtime name in the multi select detail view refs #11398 --- .../monitoring/application/controllers/DowntimesController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/monitoring/application/controllers/DowntimesController.php b/modules/monitoring/application/controllers/DowntimesController.php index fb15c98de..0b33aa339 100644 --- a/modules/monitoring/application/controllers/DowntimesController.php +++ b/modules/monitoring/application/controllers/DowntimesController.php @@ -51,6 +51,7 @@ class DowntimesController extends Controller 'is_fixed' => 'downtime_is_fixed', 'is_in_effect' => 'downtime_is_in_effect', 'entry_time' => 'downtime_entry_time', + 'name' => 'downtime_name', 'host_state', 'service_state', 'host_name', From 4fda29c1f0a297666fd846bf4f82bd2faab850a1 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 31 Aug 2016 14:08:29 +0200 Subject: [PATCH 060/337] Add downtime name to the delete downtimes command form refs #11398 --- .../forms/Command/Object/DeleteDowntimesCommandForm.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php index 52590d145..1e0399c88 100644 --- a/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php @@ -72,6 +72,7 @@ class DeleteDowntimesCommandForm extends CommandForm $delDowntime = new DeleteDowntimeCommand(); $delDowntime ->setDowntimeId($downtime->id) + ->setDowntimeName($downtime->name) ->setIsService(isset($downtime->service_description)); $this->getTransport($this->request)->send($delDowntime); } From 13085776d89c58369b029dfcf1affe8eebbd1caa Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 31 Aug 2016 15:46:22 +0200 Subject: [PATCH 061/337] Performance data: handle negative values fixes #11915 --- modules/monitoring/library/Monitoring/Plugin/Perfdata.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/library/Monitoring/Plugin/Perfdata.php b/modules/monitoring/library/Monitoring/Plugin/Perfdata.php index 0c9f09d90..9008a27ab 100644 --- a/modules/monitoring/library/Monitoring/Plugin/Perfdata.php +++ b/modules/monitoring/library/Monitoring/Plugin/Perfdata.php @@ -282,7 +282,7 @@ class Perfdata $parts = explode(';', $this->perfdataValue); $matches = array(); - if (preg_match('@^(\d+(\.\d+)?)([a-zA-Z%]{1,2})$@', $parts[0], $matches)) { + if (preg_match('@^(-?\d+(\.\d+)?)([a-zA-Z%]{1,2})$@', $parts[0], $matches)) { $this->unit = strtolower($matches[3]); $this->value = self::convert($matches[1], $this->unit); } else { From b240e0e6bb74205a2ba0b4379c6981c3588f5c81 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Sep 2016 10:14:14 +0200 Subject: [PATCH 062/337] Implement Version::getGitHead() refs #11664 --- library/Icinga/Application/Version.php | 54 ++++++++++++++++++-------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/library/Icinga/Application/Version.php b/library/Icinga/Application/Version.php index ee4dc6505..0565c3bf5 100644 --- a/library/Icinga/Application/Version.php +++ b/library/Icinga/Application/Version.php @@ -25,24 +25,46 @@ class Version } } - $gitDir = Icinga::app()->getBaseDir('.git'); - $gitHead = @file_get_contents($gitDir . DIRECTORY_SEPARATOR . 'HEAD'); - if (false !== $gitHead) { - $matches = array(); - if (@preg_match('/(?[0-9a-f]+)$/ms', $gitCommitID, $matches)) { - return array_merge($version, $matches); - } - } + $gitCommitId = static::getGitHead(Icinga::app()->getBaseDir()); + if ($gitCommitId !== false) { + $version['gitCommitID'] = $gitCommitId; } return $version; } + + /** + * Get the hexadecimal ID of the HEAD commit of the Git repository in $repoDir + * + * @param string $repoDir + * @param bool $bare Whether the repository has been created with + * $ git init|clone --bare + * + * @return string|bool false if not available + */ + public static function getGitHead($repoDir, $bare = false) + { + if (! $bare) { + $repoDir .= DIRECTORY_SEPARATOR . '.git'; + } + + $gitHead = @ file_get_contents($repoDir . DIRECTORY_SEPARATOR . 'HEAD'); + if ($gitHead !== false) { + $matches = array(); + if (preg_match('/(? Date: Thu, 1 Sep 2016 10:26:55 +0200 Subject: [PATCH 063/337] config/module: show Git HEAD commit's ID if available refs #11664 --- application/controllers/ConfigController.php | 2 ++ application/views/scripts/config/module.phtml | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index c1d75b711..c65298377 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -4,6 +4,7 @@ namespace Icinga\Controllers; use Exception; +use Icinga\Application\Version; use InvalidArgumentException; use Icinga\Application\Config; use Icinga\Application\Icinga; @@ -122,6 +123,7 @@ class ConfigController extends Controller $this->view->module = $module; $this->view->tabs = $module->getConfigTabs()->activate('info'); + $this->view->moduleGitCommitId = Version::getGitHead($module->getBaseDir()); } else { $this->view->module = false; $this->view->tabs = null; diff --git a/application/views/scripts/config/module.phtml b/application/views/scripts/config/module.phtml index 798b179ad..a201a0aab 100644 --- a/application/views/scripts/config/module.phtml +++ b/application/views/scripts/config/module.phtml @@ -37,7 +37,14 @@ escape($this->translate('Version')) ?> - escape($module->getVersion()) ?> + escape($module->getVersion()) ?> + + + + escape($this->translate('Git commit')) ?> + escape($moduleGitCommitId) ?> + + escape($this->translate('Description')) ?> escape($module->getDescription())) ?> From 0f4fa2d492fa3e48de1f649f0e74677fcba16b58 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 10:44:59 +0200 Subject: [PATCH 064/337] Add IcingaApiCommand class refs #11398 --- .../Monitoring/Command/IcingaApiCommand.php | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 modules/monitoring/library/Monitoring/Command/IcingaApiCommand.php diff --git a/modules/monitoring/library/Monitoring/Command/IcingaApiCommand.php b/modules/monitoring/library/Monitoring/Command/IcingaApiCommand.php new file mode 100644 index 000000000..b14f96bc4 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/IcingaApiCommand.php @@ -0,0 +1,86 @@ +setEndpoint($endpoint) + ->setData($data); + return $command; + } + + /** + * Get the command data + * + * @return array + */ + public function getData() + { + return $this->data; + } + + /** + * Set the command data + * + * @param array $data + * + * @return $this + */ + public function setData($data) + { + $this->data = $data; + + return $this; + } + + /** + * Get the name of the endpoint + * + * @return string + */ + public function getEndpoint() + { + return $this->endpoint; + } + + /** + * Set the name of the endpoint + * + * @param string $endpoint + * + * @return $this + */ + public function setEndpoint($endpoint) + { + $this->endpoint = $endpoint; + + return $this; + } +} From ad658b049c506e49fba879f2c19cb55fc96a6012 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 10:45:28 +0200 Subject: [PATCH 065/337] Add IcingaApiCommandRenderer class refs #11398 --- .../Renderer/IcingaApiCommandRenderer.php | 276 ++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php diff --git a/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php b/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php new file mode 100644 index 000000000..cb346bca5 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php @@ -0,0 +1,276 @@ +app; + } + + /** + * Set the name of the Icinga application object + * + * @param string $app + * + * @return $this + */ + public function setApp($app) + { + $this->app = $app; + + return $this; + } + + /** + * Apply filter to query data + * + * @param array $data + * @param MonitoredObject $object + * + * @return array + */ + protected function applyFilter(array &$data, MonitoredObject $object) + { + if ($object->getType() === $object::TYPE_HOST) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + $data['host'] = $object->getName(); + } else { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $data['service'] = sprintf('%s!%s', $object->getHost()->getName(), $object->getName()); + } + } + + /** + * Render a command + * + * @param IcingaCommand $command + * + * @return IcingaApiCommand + */ + public function render(IcingaCommand $command) + { + $renderMethod = 'render' . $command->getName(); + if (! method_exists($this, $renderMethod)) { + die($renderMethod); + } + return $this->$renderMethod($command); + } + + public function renderAddComment(AddCommentCommand $command) + { + $endpoint = 'actions/add-comment'; + $data = array( + 'author' => $command->getAuthor(), + 'comment' => $command->getComment() + ); + $this->applyFilter($data, $command->getObject()); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderSendCustomNotification(SendCustomNotificationCommand $command) + { + $endpoint = 'actions/send-custom-notification'; + $data = array( + 'author' => $command->getAuthor(), + 'comment' => $command->getComment(), + 'force' => $command->getForced() + ); + $this->applyFilter($data, $command->getObject()); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderProcessCheckResult(ProcessCheckResultCommand $command) + { + $endpoint = 'actions/process-check-result'; + $data = array( + 'exit_status' => $command->getStatus(), + 'plugin_output' => $command->getOutput(), + 'performance_data' => $command->getPerformanceData() + ); + $this->applyFilter($data, $command->getObject()); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderScheduleCheck(ScheduleServiceCheckCommand $command) + { + $endpoint = 'actions/reschedule-check'; + $data = array( + 'next_check' => $command->getCheckTime(), + 'force_check' => $command->getForced() + ); + $this->applyFilter($data, $command->getObject()); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderScheduleDowntime(ScheduleServiceDowntimeCommand $command) + { + $endpoint = 'actions/schedule-downtime'; + $data = array( + 'author' => $command->getAuthor(), + 'comment' => $command->getComment(), + 'start_time' => $command->getStart(), + 'end_time' => $command->getEnd(), + 'duration' => $command->getDuration(), + 'fixed' => $command->getFixed(), + 'trigger_name' => $command->getTriggerId() + ); + $this->applyFilter($data, $command->getObject()); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderAcknowledgeProblem(AcknowledgeProblemCommand $command) + { + $endpoint = 'actions/acknowledge-problem'; + $data = array( + 'author' => $command->getAuthor(), + 'comment' => $command->getComment(), + 'expiry' => $command->getExpireTime(), + 'sticky' => $command->getSticky(), + 'notify' => $command->getNotify() + ); + $this->applyFilter($data, $command->getObject()); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderToggleObjectFeature(ToggleObjectFeatureCommand $command) + { + if ($command->getEnabled() === true) { + $enabled = true; + } else { + $enabled = false; + } + switch ($command->getFeature()) { + case ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS: + $attr = 'enable_active_checks'; + break; + case ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS: + $attr = 'enable_passive_checks'; + break; + case ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS: + $attr = 'enable_notifications'; + break; + case ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER: + $attr = 'enable_event_handler'; + break; + case ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION: + $attr = 'enable_flapping'; + break; + default: + throw new InvalidArgumentException($command->getFeature()); + } + $endpoint = 'objects/'; + $object = $command->getObject(); + if ($object->getType() === ToggleObjectFeatureCommand::TYPE_HOST) { + /** @var \Icinga\Module\Monitoring\Object\Host $object */ + $endpoint .= 'hosts'; + } else { + /** @var \Icinga\Module\Monitoring\Object\Service $object */ + $endpoint .= 'services'; + } + $data = array( + 'attrs' => array( + $attr => $enabled + ) + ); + $this->applyFilter($data, $object); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderDeleteComment(DeleteCommentCommand $command) + { + $endpoint = 'actions/remove-comment'; + $data = array( + 'comment' => $command->getCommentName() + ); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderDeleteDowntime(DeleteDowntimeCommand $command) + { + $endpoint = 'actions/remove-downtime'; + $data = array( + 'downtime' => $command->getDowntimeName() + ); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderRemoveAcknowledgement(RemoveAcknowledgementCommand $command) + { + $endpoint = 'actions/remove-acknowledgement'; + $data = array(); + $this->applyFilter($data, $command->getObject()); + return IcingaApiCommand::create($endpoint, $data); + } + + public function renderToggleInstanceFeature(ToggleInstanceFeatureCommand $command) + { + $endpoint = 'objects/icingaapplications/' . $this->getApp(); + if ($command->getEnabled() === true) { + $enabled = true; + } else { + $enabled = false; + } + switch ($command->getFeature()) { + case ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS: + $attr = 'enable_host_checks'; + break; + case ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS: + $attr = 'enable_service_checks'; + break; + case ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS: + $attr = 'enable_event_handlers'; + break; + case ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION: + $attr = 'enable_flapping'; + break; + case ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS: + $attr = 'enable_notifications'; + break; + case ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA: + $attr = 'enable_perfdata'; + break; + default: + throw new InvalidArgumentException($command->getFeature()); + } + $data = array( + 'attrs' => array( + $attr => $enabled + ) + ); + return IcingaApiCommand::create($endpoint, $data); + } +} From cfc0f60042d60869a986e3e1e4cb0ce031e227a0 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 10:47:06 +0200 Subject: [PATCH 066/337] Add RestRequest class refs #11398 --- .../Monitoring/Web/Rest/RestRequest.php | 269 ++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php diff --git a/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php b/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php new file mode 100644 index 000000000..7506f6418 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php @@ -0,0 +1,269 @@ +uri = $uri; + $request->method = 'POST'; + return $request; + } + + /** + * Send content type JSON + * + * @return $this + */ + public function sendJson() + { + $this->contentType = 'application/json'; + + return $this; + } + + /** + * Set basic auth credentials + * + * @param string $username + * @param string $password + * + * @return $this + */ + public function authenticateWith($username, $password) + { + $this->hasBasicAuth = true; + $this->username = $username; + $this->password = $password; + + return $this; + } + + /** + * Set request payload + * + * @param mixed $payload + * + * @return $this + */ + public function setPayload($payload) + { + $this->payload = $payload; + + return $this; + } + + /** + * Disable strict SSL + * + * @return $this + */ + public function noStrictSsl() + { + $this->strictSsl = false; + + return $this; + } + + /** + * Serialize payload according to content type + * + * @param mixed $payload + * @param string $contentType + * + * @return string + */ + public function serializePayload($payload, $contentType) + { + switch ($contentType) { + case 'application/json': + $payload = json_encode($payload); + break; + } + + return $payload; + } + + /** + * Send the request + * + * @return mixed + * + * @throws Exception + */ + public function send() + { + $defaults = array( + 'host' => 'localhost', + 'path' => '/' + ); + + $url = array_merge($defaults, parse_url($this->uri)); + + if (isset($url['port'])) { + $url['host'] .= sprintf(':%u', $url['port']); + } + + if (isset($url['query'])) { + $url['path'] .= sprintf('?%s', $url['query']); + } + + $headers = array( + "{$this->method} {$url['path']} HTTP/1.1", + "Host: {$url['host']}", + "Content-Type: {$this->contentType}", + 'Accept: application/json', + // Bypass "Expect: 100-continue" timeouts + 'Expect:' + ); + + $ch = curl_init(); + + $options = array( + CURLOPT_URL => $this->uri, + CURLOPT_TIMEOUT => $this->timeout, + // Ignore proxy settings + CURLOPT_PROXY => '', + CURLOPT_CUSTOMREQUEST => $this->method, + CURLOPT_RETURNTRANSFER => true + ); + + if ($this->strictSsl) { + $options[CURLOPT_SSL_VERIFYHOST] = 2; + $options[CURLOPT_SSL_VERIFYPEER] = true; + } else { + $options[CURLOPT_SSL_VERIFYHOST] = false; + $options[CURLOPT_SSL_VERIFYPEER] = false; + } + + if ($this->hasBasicAuth) { + $options[CURLOPT_USERPWD] = sprintf('%s:%s', $this->username, $this->password); + } + + if (! empty($this->payload)) { + $payload = $this->serializePayload($this->payload, $this->contentType); + $options[CURLOPT_POSTFIELDS] = $payload; + } + + $options[CURLOPT_HTTPHEADER] = $headers; + + curl_setopt_array($ch, $options); + + $result = curl_exec($ch); + + if ($result === false) { + throw new Exception(curl_error($ch)); + } + + curl_close($ch); + + $response = @json_decode($result, true); + + if ($response === null) { + if (version_compare(PHP_VERSION, '5.5.0', '>=')) { + throw new Exception(json_last_error_msg()); + } else { + switch (json_last_error()) { + case JSON_ERROR_DEPTH: + $msg = 'The maximum stack depth has been exceeded'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Control character error, possibly incorrectly encoded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Invalid or malformed JSON'; + break; + case JSON_ERROR_SYNTAX: + $msg = 'Syntax error'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $msg = 'An error occured when parsing a JSON string'; + } + throw new Exception($msg); + } + } + + return $response; + } +} From db41024c894f297f5ef96a5f32ac8560c371ac8e Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 10:47:27 +0200 Subject: [PATCH 067/337] Add ApiCommandTransport class refs #11398 --- .../Command/Transport/ApiCommandTransport.php | 227 ++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php diff --git a/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php b/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php new file mode 100644 index 000000000..1058e9178 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php @@ -0,0 +1,227 @@ +renderer = new IcingaApiCommandRenderer(); + } + + /** + * Set the name of the Icinga application object + * + * @param string $app + * + * @return $this + */ + public function setApp($app) + { + $this->renderer->setApp($app); + + return $this; + } + + /** + * Get the API host + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Set the API host + * + * @param string $host + * + * @return $this + */ + public function setHost($host) + { + $this->host = $host; + + return $this; + } + + /** + * Get the API password + * + * @return string + */ + public function getPassword() + { + return $this->password; + } + + /** + * Set the API password + * + * @param string $password + * + * @return $this + */ + public function setPassword($password) + { + $this->password = $password; + + return $this; + } + + /** + * Get the API port + * + * @return int + */ + public function getPort() + { + return $this->port; + } + + /** + * Set the API port + * + * @param int $port + * + * @return $this + */ + public function setPort($port) + { + $this->port = (int) $port; + + return $this; + } + + /** + * Get the API username + * + * @return string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Set the API username + * + * @param string $username + * + * @return $this + */ + public function setUsername($username) + { + $this->username = $username; + + return $this; + } + + /** + * Get URI for endpoint + * + * @param string $endpoint + * + * @return string + */ + protected function getUriFor($endpoint) + { + return sprintf('https://%s:%u/v1/%s', $this->getHost(), $this->getPort(), $endpoint); + } + + /** + * Send the Icinga command over the Icinga 2 API + * + * @param IcingaCommand $command + * @param int|null $now + * + * @throws CommandTransportException + */ + public function send(IcingaCommand $command, $now = null) + { + $command = $this->renderer->render($command); + Logger::debug( + 'Sending Icinga command "%s" to the API "%s:%u"', + $command->getEndpoint(), + $this->getHost(), + $this->getPort() + ); + $response = RestRequest::post($this->getUriFor($command->getEndpoint())) + ->authenticateWith($this->getUsername(), $this->getPassword()) + ->sendJson() + ->noStrictSsl() + ->setPayload($command->getData()) + ->send(); + if (isset($response['error'])) { + throw new CommandTransportException( + 'Can\'t send external Icinga command: %u %s', + $response['error'], + $response['status'] + ); + } + $result = array_pop($response['results']); + + if ($result['code'] < 200 || $result['code'] >= 300) { + throw new CommandTransportException( + 'Can\'t send external Icinga command: %u %s', + $result['code'], + $result['status'] + ); + } + } +} From 9b310ca1ca5eedd115f4411c9c487ee3d7b460bd Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 10:47:52 +0200 Subject: [PATCH 068/337] Add ApiTransportForm class refs #11398 --- .../Config/Transport/ApiTransportForm.php | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 modules/monitoring/application/forms/Config/Transport/ApiTransportForm.php diff --git a/modules/monitoring/application/forms/Config/Transport/ApiTransportForm.php b/modules/monitoring/application/forms/Config/Transport/ApiTransportForm.php new file mode 100644 index 000000000..3a54ef2e9 --- /dev/null +++ b/modules/monitoring/application/forms/Config/Transport/ApiTransportForm.php @@ -0,0 +1,73 @@ +setName('form_config_command_transport_api'); + } + + /** + * {@inheritdoc} + */ + public function createElements(array $formData = array()) + { + $this->addElements(array( + array( + 'text', + 'host', + array( + 'required' => true, + 'label' => $this->translate('Host'), + 'description' => $this->translate( + 'Hostname or address of the remote Icinga instance' + ) + ) + ), + array( + 'number', + 'port', + array( + 'required' => true, + 'label' => $this->translate('Port'), + 'description' => $this->translate('SSH port to connect to on the remote Icinga instance'), + 'value' => 5665 + ) + ), + array( + 'text', + 'username', + array( + 'required' => true, + 'label' => $this->translate('API Username'), + 'description' => $this->translate( + 'User to log in as on the remote Icinga instance. Please note that key-based SSH login must be' + . ' possible for this user' + ) + ) + ), + array( + 'password', + 'password', + array( + 'required' => true, + 'label' => $this->translate('API Password'), + 'description' => $this->translate( + 'User to log in as on the remote Icinga instance. Please note that key-based SSH login must be' + . ' possible for this user' + ) + ) + ) + )); + + return $this; + } +} From 3ccbf371615fa0adf87ca3c8258e57ec6f53b18f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 10:48:36 +0200 Subject: [PATCH 069/337] Provide API transport in the transport config form refs #11398 --- .../application/forms/Config/TransportConfigForm.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/forms/Config/TransportConfigForm.php b/modules/monitoring/application/forms/Config/TransportConfigForm.php index 4f14bfd5e..0e5ad4f49 100644 --- a/modules/monitoring/application/forms/Config/TransportConfigForm.php +++ b/modules/monitoring/application/forms/Config/TransportConfigForm.php @@ -7,8 +7,10 @@ use InvalidArgumentException; use Icinga\Exception\IcingaException; use Icinga\Exception\NotFoundError; use Icinga\Forms\ConfigForm; +use Icinga\Module\Monitoring\Command\Transport\ApiCommandTransport; use Icinga\Module\Monitoring\Command\Transport\LocalCommandFile; use Icinga\Module\Monitoring\Command\Transport\RemoteCommandFile; +use Icinga\Module\Monitoring\Forms\Config\Transport\ApiTransportForm; use Icinga\Module\Monitoring\Forms\Config\Transport\LocalTransportForm; use Icinga\Module\Monitoring\Forms\Config\Transport\RemoteTransportForm; @@ -68,7 +70,7 @@ class TransportConfigForm extends ConfigForm * * @param string $type The transport type for which to return a form * - * @return Form + * @return \Icinga\Web\Form * * @throws InvalidArgumentException In case the given transport type is invalid */ @@ -79,6 +81,8 @@ class TransportConfigForm extends ConfigForm return new LocalTransportForm(); case RemoteCommandFile::TRANSPORT; return new RemoteTransportForm(); + case ApiCommandTransport::TRANSPORT: + return new ApiTransportForm(); default: throw new InvalidArgumentException( sprintf($this->translate('Invalid command transport type "%s" given'), $type) @@ -223,7 +227,8 @@ class TransportConfigForm extends ConfigForm $transportTypes = array( LocalCommandFile::TRANSPORT => $this->translate('Local Command File'), - RemoteCommandFile::TRANSPORT => $this->translate('Remote Command File') + RemoteCommandFile::TRANSPORT => $this->translate('Remote Command File'), + ApiCommandTransport::TRANSPORT => $this->translate('Icinga 2 API') ); $transportType = isset($formData['transport']) ? $formData['transport'] : null; From 440a3f5fdd8872d0ee00fce9336de2b01425c5a9 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 10:49:08 +0200 Subject: [PATCH 070/337] Respect API transport in the command transport factory refs #11398 --- .../Monitoring/Command/Transport/CommandTransport.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php b/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php index 81fb54939..512de2f28 100644 --- a/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php +++ b/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php @@ -65,6 +65,9 @@ class CommandTransport implements CommandTransportInterface case RemoteCommandFile::TRANSPORT: $transport = new RemoteCommandFile(); break; + case ApiCommandTransport::TRANSPORT: + $transport = new ApiCommandTransport(); + break; case LocalCommandFile::TRANSPORT: case '': // Casting null to string is the empty string $transport = new LocalCommandFile(); @@ -74,12 +77,13 @@ class CommandTransport implements CommandTransportInterface mt( 'monitoring', 'Cannot create command transport "%s". Invalid transport' - . ' defined in "%s". Use one of "%s" or "%s".' + . ' defined in "%s". Use one of "%s", "%s" or "%s".' ), $config->transport, static::getConfig()->getConfigFile(), LocalCommandFile::TRANSPORT, - RemoteCommandFile::TRANSPORT + RemoteCommandFile::TRANSPORT, + ApiCommandTransport::TRANSPORT ); } From 64b3811ab0523456e9d5053a6da44c916afd68a1 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 10:49:56 +0200 Subject: [PATCH 071/337] Display transport type from config in the config overview refs #11398 --- modules/monitoring/application/views/scripts/config/index.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/views/scripts/config/index.phtml b/modules/monitoring/application/views/scripts/config/index.phtml index 091aaf70f..79a8ccd3c 100644 --- a/modules/monitoring/application/views/scripts/config/index.phtml +++ b/modules/monitoring/application/views/scripts/config/index.phtml @@ -92,7 +92,7 @@ ); ?> (translate('Type: %s'), - $config->host !== null ? $this->translate('Remote') : $this->translate('Local') + ucfirst($config->get('transport', 'local')) ) ?>) From 18d20849241d8a0f8b24d524fe9b2d50a439b7e6 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 10:51:04 +0200 Subject: [PATCH 072/337] Disable broadcast option for send custom notification if backend is Icinga 2 --- .../Object/SendCustomNotificationCommandForm.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php b/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php index 6bc691f26..06e1b35ee 100644 --- a/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php @@ -59,8 +59,11 @@ class SendCustomNotificationCommandForm extends ObjectsCommandForm . ' whether or not notifications are enabled.' ) ) - ), - array( + ) + )); + + if (! $this->getBackend()->isIcinga2()) { + $this->addElement( 'checkbox', 'broadcast', array( @@ -70,8 +73,9 @@ class SendCustomNotificationCommandForm extends ObjectsCommandForm 'If you check this option, the notification is sent out to all normal and escalated contacts.' ) ) - ) - )); + ); + } + return $this; } From c559055b9d8cd29f0dbe293499ca5bc2f8253c83 Mon Sep 17 00:00:00 2001 From: Ken Jungclaus Date: Mon, 15 Aug 2016 14:12:36 +0200 Subject: [PATCH 073/337] Fix translation for colon Colon is no comma... gave me a short headache. :) Signed-off-by: Eric Lippmann --- application/locale/de_DE/LC_MESSAGES/icinga.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.po b/application/locale/de_DE/LC_MESSAGES/icinga.po index e455fa85e..88c5b08b7 100644 --- a/application/locale/de_DE/LC_MESSAGES/icinga.po +++ b/application/locale/de_DE/LC_MESSAGES/icinga.po @@ -520,7 +520,7 @@ msgid "" "modules." msgstr "" "Enthält die Verzeichnisse, die nach verfügbaren Modulen durchsucht werden " -"(kommasepariert). Module, die nicht in diesen Verzeichnissen vorhanden sind, " +"(getrennt durch Doppelpunkte). Module, die nicht in diesen Verzeichnissen vorhanden sind, " "können trotzdem in den Modulordner gesymlinkt werden. Diese werden " "allerdings nicht in der der deaktivierten Module angezeigt." From 92373504059c99206462571bb6cb138af12000f3 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 11:29:45 +0200 Subject: [PATCH 074/337] Compile translation for colon --- .../locale/de_DE/LC_MESSAGES/icinga.mo | Bin 60895 -> 60908 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.mo b/application/locale/de_DE/LC_MESSAGES/icinga.mo index 22ba80c2af004204fefad8cac3d4c4177ebf497a..ae880d0444f01d506d18283868966799939961aa 100644 GIT binary patch delta 4143 zcmXZf3shBA9>?*mh{i*F5Q0hq0;b60A`s#d#7xr)b;ideTGk{vgMdbgrY%pD)>;~= znfV}1!pXORq(+JqPC9CiZ?e!abU;g8(`1@DHTC_mPpjo;|Mxj}KmPl_@70S9Ue_DE zDt-xaqFWg=Y?U!S#+ba-#=MC|7>-_RY=0DXBaX)oI0Ad(1PsBK@qS!^(YOU;@dUns zzhWjXEVa)M;wa+lrN(I)VBmXpfN>bbgIv@AOHdQ-LqELedJVOZpYadaZmlu1aRO>1 zC$S4QU?~2Gov_0?V>)95>Ur`y$Cw^89;ZV)$i?>PU?7&baT)p(e}tNNpBsOPONhV5 z-uTz|jS0fHQ4?%JExZ;Vz@w-Ie1}@lFAfcTAYi>+Koo`(r(rN=qZU$#G59)ap_SMj z4`Mi8!d~cAX2*-c1maBWh%+z@i!lPrFbACjG}_YW^MT#D2Nh>v3Jyhma31P|r8od< zFaz&kJf@Z#SuY{K!Fyup~AxET9m#@||w*fFzdXn+kEho@0z+Ui4N(s2xW z<1$n#SEA0i+;uy)CEkOoo&Bf@E~7FRy3xMxK^^S?deK8YN6M5JTi&4e*9`?j4)WYgf8M%Zi!rSPm;j`5a9E$vJ zBKTtx9z!i4W}8iIKkP&NCyd2n?AFSd9jMeE-fnNlSLjE41+}1?sPBn@9rkk%VkGg{ z9pqmrokNGJc@65C>_(mC8TZAzsD(c8k=27q#6wU;I@^u6qZZbP8YiH_W-=B#5I>DR zSb$pKoC@-PAB_cWM+quk?fL;Km0R&~tV3T6sI*1Y6?Jr@(I1OY6TODo@p9BrZ9`>Z zr`vxFwZUeGMh_Z(+zowG#h}jUQEZD7Q8n>rOvO@EF`Y(Dd=FI<;Z=5F(KwXYgB~ox zLAcS4uV6B9$jA1%lSLztj`^sRy^Za$0#!WwQ9EhG&Ug=%k@man8AhRwZa6CSQ`~qN zYMkwujC)a;`2jVq+1;{1$3)TyAdW%pBn<~+2EK%^U<%&Dc=YVCE1!&y5iiBj*nrAd z>?igMD+$#<28ZAVoQhXb8y#Iu0qXu2&`>cPz#u&C+Jqg6|A#6X|4;2#au-yM#G@um zMQtF%H49ZEliYYJK1^JME3g{h#Zi0t9nt-7rlFme@3Se}ggV>ZsIxndX&72#Q#c-V zWY6PBoQu=&Flxss`)y`k$G*fTQRDoI+E99}-OxyMG|-bYI$<7$;e6EnT!(sIhdR4@ zR1Gv^7~aJY?E0B4%6_PYPe6UnLA|#eyI_^;5!5)1pOJq(_=%2S^!uE_FcK3m9d#u6 zuCHMz@oH4j?M5y16l%iT7=ylbc84k0k9ZI!Vxj8>)REQKIX2}@bZDhFQ9BAeV9ziX zQ;1V>0_NjHJcPD-1sQ2G;+y`q>JKczpScBS8 zBWmTh&>J88!uH2u67gVEG0sA7oR33s0glBZI2XeX+V|FA1#vl!M`!FIyP~bATTqQU ziZ4(LxZwI<)OWx4KkR)@z=6b5P?;#h2&_bH;54eHenyS!bJ!kP5Ng~+Br}c~N<*oC z0+o_H`@qaY9mT7t%#^zQmDq;(6I6<8@F_fjBQXAmy)AQ4N467{3IC(^N9|e6Cf<)d zb^p75$=59%1JQ#EPy_CA4LoMA&kLAFe2j~lZJSK*VG zc*0)CMc7ijIGOp)DHzh#vyNkg%jnnFX4H;K z&Kolmzr_JK?t74+(pOk@F*SLblm@~ zExK+vnK%i(a5>(GD=`<>;~n&CwD-ErCHpfTfU5d9jKe{wOy#3?z7WH))a~Er&~FEu_h{b+f(iVW^@@zzEDn?f7NaEtp7r4mGjw6#9pZF|x#!IN9 zy@jd)pBr`q5va_@C}w_>MnfNb7=3XR`d~I{!YQZ}PeV;u;<^I0u(hbnl%vMkhC0G( z)OaUQnQFjR*j<0{Uk+BCkKR+*eQM6k!kmJFnc-7r6+ZWu@JFW4n30n=V^+bmnK@Nu It1`y@4iskbCjbBd delta 4130 zcmXZf2~d|s7{~D?1-S%81wxP;5m7nR)I3lpvl7fS&63cH3K=w!JesMO(yTEvn+(m{ z!ZSj|v@|vHCX1}lI7}?d6Vonh8jrp|_jMdT`|SI^`#$^Z?*9+XjV+oQTU1={;DmY@ zGi<3bKE{}w_l#M9c^HM)Q2imxj0wYN48;BzgCj5$r(%129piB&CgXm54X;S_tjtkkS0bW79Xbbw`cdn;UiCn}{_#e*15i4!N2QZkp2E*}J zY>RiSGA0Pyqpo*e@-FPwj6Tgpo@fJ7!9N!}T3KMa{YGXR! ztEd+&MR~#C8z+;U~AOxd`lQ9G{QHkVY63#{?T8bTUCr06MOu%cX z@gmk5(*x774L*sHScK8I7<19tLBoqi`1^L|v8Xr|dto2cgHuosF2sIVfdlapcE_X- zj7h=qn2GZ-3+r$!b}pp~_!9QP)OEL)*fEo7Xn-Zy4G*E#^e-HM!`2()iE~kia; zJX8W3(HARGCEkNdum<&f1191HOu(Q`c2oC7t^Ly&!28WC8u9oVD)Wyp70+TT4Bc#v z!j{B|sD!#Cki5v{WBGl%43uACSDzR!*MUJC3;RSTmxK2X@2YzS_|4n;-Ou#Bs z0ug1lwDH)5_aD;bG_#G}v$C!rF|ub}?k zG-kRT#i)3J>$|8@uEGa!JNlx>R=bJ(QA;-z{V@;qq8X?e&qFQMYE&iGx&2kB3D!F_ zI?=d^`lO2BjA)Jep%-SM_Qb<@7cN9?rbDO~Uq&rmyKOeHP`rmY7E>?}2jMa|Zor{V=yAD}kRR@6*tF$gcCDq^sBzX{Pb^1O z<_v1wKamMKCU}QEE)l4iB;#OA#o0I=d*Nm5jZ&wn&C*)9(e+lP#)@3Oh?`Svg2M@gqrCxjKvDnjA~JtpGQv& zJ81i(FrBz3>I3Up^u%d+56;9fxCdXrphNb)H?e}a1jnLt?_rzKD%2?`LoLOps05C= z{(@bI|3#hWm?Qiih~rR|Sd7tFikiS7)SkMC8uvPC$-JxWxN%5j9Mgw}N{$KG; zL`M%y!I`K5H@bR#ZI91WsAE`)1Mn2~#_$?r-oVjVfK90Dnco<*6yLx{Fz%Q=jxXZv z&5Jp_-yEcoh4J6oFPS3LOsZUczq7|`45|_@pkBNP@4~~FgtssalfF0RA=NwFd9>6s5IMAHwK;Ha~mshIFA0o{=U8fl~^-|VDCD+*|Kmj@nYlv znG@I@->bLZdPgvh_zY?S-VJubof@dWHcJ5=%IIy>jmPi|{*CLgzR}L8_$0q{5Py&T zaQKgQ#&a-*cn4~&&!H0Pf6D$r^AaWy`!(6knTF30k8N`74ENIENk^;GcGI=S9K%D{ z0_UMO&d1644*rWb(Fgze$$pJ{{A_o96n3LO4OOX$sF}~gC|v0FZ*gd}qvJ3JVj@l#Hn2TlD0)u|B&j;gB;v^h^Q_x@cp=Q1l%25~;)htVShL=lVD5ya%1Nn=S^Ui8E0%p6a?1Q;CnFUVH=NG3K02V7S#WFVg74 zg-Yy-mv9?~oVPDJike9yY6;F`DE^LZ(D#D9uOqf1?uu719d*oNFY*T|4#GR|N7P<9 zjoo$r+g{>hla37h0xMCKC~CHqn2(;sYtaigpdXf_UbxG>z7PG0k6;iUM=kAn)E>Bw znn3%@wz3iE&HGI<4S(!~zBmMZFcbB{ai|hMj(TCS>+7h*7NG_%L5;H-wS;A;@%E!C pRf8VbQ9t;Xi`!1de^MBbKeeD>()8TtCKcx878Y$QUpnyN{{bWF=ZpXV From ddd42e044aa8c197e57acd91d00a23c462c1c0bd Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 11:29:14 +0200 Subject: [PATCH 075/337] Fix images in the documentation of the translation module --- .../{public/img/doc => doc/img}/poedit_001.png | Bin .../{public/img/doc => doc/img}/poedit_002.png | Bin .../{public/img/doc => doc/img}/poedit_003.png | Bin .../{public/img/doc => doc/img}/poedit_004.png | Bin .../{public/img/doc => doc/img}/poedit_005.png | Bin modules/translation/doc/translation.md | 10 +++++----- 6 files changed, 5 insertions(+), 5 deletions(-) rename modules/translation/{public/img/doc => doc/img}/poedit_001.png (100%) rename modules/translation/{public/img/doc => doc/img}/poedit_002.png (100%) rename modules/translation/{public/img/doc => doc/img}/poedit_003.png (100%) rename modules/translation/{public/img/doc => doc/img}/poedit_004.png (100%) rename modules/translation/{public/img/doc => doc/img}/poedit_005.png (100%) diff --git a/modules/translation/public/img/doc/poedit_001.png b/modules/translation/doc/img/poedit_001.png similarity index 100% rename from modules/translation/public/img/doc/poedit_001.png rename to modules/translation/doc/img/poedit_001.png diff --git a/modules/translation/public/img/doc/poedit_002.png b/modules/translation/doc/img/poedit_002.png similarity index 100% rename from modules/translation/public/img/doc/poedit_002.png rename to modules/translation/doc/img/poedit_002.png diff --git a/modules/translation/public/img/doc/poedit_003.png b/modules/translation/doc/img/poedit_003.png similarity index 100% rename from modules/translation/public/img/doc/poedit_003.png rename to modules/translation/doc/img/poedit_003.png diff --git a/modules/translation/public/img/doc/poedit_004.png b/modules/translation/doc/img/poedit_004.png similarity index 100% rename from modules/translation/public/img/doc/poedit_004.png rename to modules/translation/doc/img/poedit_004.png diff --git a/modules/translation/public/img/doc/poedit_005.png b/modules/translation/doc/img/poedit_005.png similarity index 100% rename from modules/translation/public/img/doc/poedit_005.png rename to modules/translation/doc/img/poedit_005.png diff --git a/modules/translation/doc/translation.md b/modules/translation/doc/translation.md index 1a893f72c..fc2fbd841 100644 --- a/modules/translation/doc/translation.md +++ b/modules/translation/doc/translation.md @@ -93,15 +93,15 @@ configuration under the Preferences. __Personalize__: Please provide your Name and E-Mail under Identity. -![Personalize](img/translation/doc/poedit_001.png) +![Personalize](img/poedit_001.png) __Editor__: Under the Behavior the Automatically compile .mo files on save, should be disabled. -![Editor](/img/translation/doc/poedit_002.png) +![Editor](img/poedit_002.png) __Translations Memory__: Under the Database please add your languages, for which are you writing translations. -![Translations Memory](img/translation/doc/poedit_003.png) +![Translations Memory](img/poedit_003.png) When you are done, just save your new settings. @@ -113,12 +113,12 @@ translation strings for the core application. Each module names its translation module called __yourmodule__ the .po translation file will be named `yourmodule.po`. -![Full list of strings](img/translation/doc/poedit_004.png) +![Full list of strings](img/poedit_004.png) Now you can make changes and when there is no translation available, Poedit would mark it with a blue color, as shown below. -![Untranslated strings](img/translation/doc/poedit_005.png) +![Untranslated strings](img/poedit_005.png) And when you want to test your changes, please read more about under the chapter [Testing Translations](Testing Translations). From f05d22a39da51caec01f19a078791d0f3393761f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 11:30:58 +0200 Subject: [PATCH 076/337] Fix image in the documentation of the monitoring module --- .../doc/{public/img/doc => doc/img}/markdown.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/doc/{public/img/doc => doc/img}/markdown.png (100%) diff --git a/modules/doc/public/img/doc/markdown.png b/modules/doc/doc/img/markdown.png similarity index 100% rename from modules/doc/public/img/doc/markdown.png rename to modules/doc/doc/img/markdown.png From 8e40d3cc259f566a8f5f4e750bb46273a54ad36d Mon Sep 17 00:00:00 2001 From: Chris Reeves Date: Wed, 29 Jun 2016 14:29:35 +0100 Subject: [PATCH 077/337] doc: document 'encryption' directive for LDAP Signed-off-by: Eric Lippmann --- doc/04-Resources.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/04-Resources.md b/doc/04-Resources.md index 6b43ca3d3..72021c38a 100644 --- a/doc/04-Resources.md +++ b/doc/04-Resources.md @@ -68,6 +68,7 @@ Directive | Description **root_dn** | Root object of the tree, e.g. "ou=people,dc=icinga,dc=org" **bind_dn** | The user to use when connecting to the server. **bind_pw** | The password to use when connecting to the server. +**encryption** | Type of encryption to use: `none` (default), `starttls`, `ldaps`. **Example:** From cbf55ffbf1bb35d7269db073c26641e0c82ec08f Mon Sep 17 00:00:00 2001 From: Heike Jurzik Date: Fri, 8 Jul 2016 16:39:24 +0200 Subject: [PATCH 078/337] Fix some syntax bugs in markdown files Signed-off-by: Eric Lippmann --- doc/02-Installation.md | 30 ++++++++++-------------- doc/03-Configuration.md | 18 +++++++------- doc/04-Resources.md | 50 ++++++++++++++++++++++----------------- doc/06-Security.md | 52 ++++++++++++++++++++--------------------- doc/99-Vagrant.md | 14 +++++++---- 5 files changed, 84 insertions(+), 80 deletions(-) diff --git a/doc/02-Installation.md b/doc/02-Installation.md index 156387838..ae37dbbc5 100644 --- a/doc/02-Installation.md +++ b/doc/02-Installation.md @@ -37,7 +37,7 @@ Below is a list of official package repositories for installing Icinga Web 2 for Distribution | Repository ------------------------|--------------------------- -Debian | [debmon](http://debmon.org/packages/debmon-wheezy/icingaweb2), [Icinga Repository](http://packages.icinga.org/debian/) +Debian | [debmon](http://debmon.org/packages/debmon-jessie/icingaweb2), [Icinga Repository](http://packages.icinga.org/debian/) Ubuntu | [Icinga Repository](http://packages.icinga.org/ubuntu/) RHEL/CentOS | [Icinga Repository](http://packages.icinga.org/epel/) openSUSE | [Icinga Repository](http://packages.icinga.org/openSUSE/) @@ -57,7 +57,7 @@ Below is a list with examples for various distributions. **Debian (debmon)**: ``` wget -O - http://debmon.org/debmon/repo.key 2>/dev/null | apt-key add - -echo 'deb http://debmon.org/debmon debmon-wheezy main' >/etc/apt/sources.list.d/debmon.list +echo 'deb http://debmon.org/debmon debmon-jessie main' >/etc/apt/sources.list.d/debmon.list apt-get update ``` @@ -111,12 +111,6 @@ The packages for RHEL/CentOS depend on other packages which are distributed as p > Please note that installing Icinga Web 2 on **RHEL/CentOS 5** is not supported due to EOL versions of PHP and > PostgreSQL. -####
Debian wheezy Notes - -The packages for Debian wheezy depend on other packages which are distributed as part of the -[wheezy-backports](http://backports.debian.org/) repository. Please make sure to enable this repository by following -[these instructions](http://backports.debian.org/Instructions/). - ### Installing Icinga Web 2 You can install Icinga Web 2 by using your distribution's package manager to install the `icingaweb2` package. @@ -126,7 +120,6 @@ Below is a list with examples for various distributions. The additional package ``` apt-get install icingaweb2 ``` -For Debian wheezy please read the [package repositories notes](#package-repositories-wheezy-notes). **RHEL, CentOS and Fedora**: ``` @@ -449,24 +442,24 @@ path = "/var/run/icinga2/cmd/icinga2.cmd" Finally visit Icinga Web 2 in your browser to login as `icingaadmin` user: `/icingaweb2`. -# Upgrading Icinga Web 2 +## Upgrading Icinga Web 2 -## Upgrading to Icinga Web 2 2.3.x +### Upgrading to Icinga Web 2 2.3.x * Icinga Web 2 version 2.3.x does not introduce any backward incompatible change. -## Upgrading to Icinga Web 2 2.2.0 +### Upgrading to Icinga Web 2 2.2.0 * The menu entry `Authorization` beneath `Config` has been renamed to `Authentication`. The role, user backend and user group backend configuration which was previously found beneath `Authentication` has been moved to `Application`. -## Upgrading to Icinga Web 2 2.1.x +### Upgrading to Icinga Web 2 2.1.x * Since Icinga Web 2 version 2.1.3 LDAP user group backends respect the configuration option `group_filter`. Users who changed the configuration manually and used the option `filter` instead have to change it back to `group_filter`. -## Upgrading to Icinga Web 2 2.0.0 +### Upgrading to Icinga Web 2 2.0.0 * Icinga Web 2 installations from package on RHEL/CentOS 7 now depend on `php-ZendFramework` which is available through the [EPEL repository](http://fedoraproject.org/wiki/EPEL). Before, Zend was installed as Icinga Web 2 vendor library @@ -488,7 +481,7 @@ Finally visit Icinga Web 2 in your browser to login as `icingaadmin` user: `/ici **<config-dir>/preferences/<username>/config.ini**. The content of the file remains unchanged. -## Upgrading to Icinga Web 2 Release Candidate 1 +### Upgrading to Icinga Web 2 Release Candidate 1 The first release candidate of Icinga Web 2 introduces the following non-backward compatible changes: @@ -507,12 +500,12 @@ The first release candidate of Icinga Web 2 introduces the following non-backwar predefined subset of filter columns. Please see the module's security related documentation for more details. -## Upgrading to Icinga Web 2 Beta 3 +### Upgrading to Icinga Web 2 Beta 3 Because Icinga Web 2 Beta 3 does not introduce any backward incompatible change you don't have to change your configuration files after upgrading to Icinga Web 2 Beta 3. -## Upgrading to Icinga Web 2 Beta 2 +### Upgrading to Icinga Web 2 Beta 2 Icinga Web 2 Beta 2 introduces access control based on roles for secured actions. If you've already set up Icinga Web 2, you are required to create the file **roles.ini** beneath Icinga Web 2's configuration directory with the following @@ -526,5 +519,6 @@ permissions = "*" After please log out from Icinga Web 2 and log in again for having all permissions granted. If you delegated authentication to your web server using the `autologin` backend, you have to switch to the `external` -authentication backend to be able to log in again. The new name better reflects what’s going on. A similar change +authentication backend to be able to log in again. The new name better reflects +what's going on. A similar change affects environments that opted for not storing preferences, your new backend is `none`. diff --git a/doc/03-Configuration.md b/doc/03-Configuration.md index 4df9ad2e6..5ded2eaff 100644 --- a/doc/03-Configuration.md +++ b/doc/03-Configuration.md @@ -1,15 +1,15 @@ # Configuration -## Overview +## Overview Apart from its web configuration capabilities, the local configuration is stored in `/etc/icingaweb2` by default (depending on your config setup). -File/Directory | Description ---------------------------------------------------------- -config.ini | General configuration (logging, preferences) -[resources.ini](04-Ressources.md) | Global resources (Icinga Web 2 database for preferences and authentication, Icinga IDO database) -roles.ini | User specific roles (e.g. `administrators`) and permissions -[authentication.ini](05-Authentication.md) | Authentication backends (e.g. database) -enabledModules | Contains symlinks to enabled modules -modules | Directory for module specific configuration +| **File/Directory** | **Description/Purpose** | +| :------------- | :------------------- | +| **config.ini** | general configuration (logging, preferences, etc.) +| [**resources.ini**](04-Ressources.md) | global resources (Icinga Web 2 database for preferences and authentication, Icinga IDO database) +| **roles.ini** | user specific roles (e.g. `administrators`) and permissions +| [**authentication.ini**](05-Authentication.md) | authentication backends (e.g. database) +| **enabledModules** | contains symlinks to enabled modules +| **modules** | directory for module specific configuration diff --git a/doc/04-Resources.md b/doc/04-Resources.md index 72021c38a..a464cfe91 100644 --- a/doc/04-Resources.md +++ b/doc/04-Resources.md @@ -1,12 +1,12 @@ # Resources -The INI configuration file **config/resources.ini** contains information about data sources that can be referenced in other +The configuration file `config/resources.ini` contains information about data sources that can be referenced in other configuration files. This allows you to manage all data sources at one central place, avoiding the need to edit several different files, when the information about a data source changes. ## Configuration -Each section in **config/resources.ini** represents a data source with the section name being the identifier used to +Each section in `config/resources.ini` represents a data source with the section name being the identifier used to reference this specific data source. Depending on the data source type, the sections define different directives. The available data source types are *db*, *ldap*, *ssh* and *livestatus* which will described in detail in the following paragraphs. @@ -17,7 +17,7 @@ A Database resource defines a connection to a SQL databases which can contain us to handle authentication and authorization, monitoring data or user preferences. Directive | Description -----------------|------------ +:---------------|:------------ **type** | `db` **db** | Database management system. In most cases `mysql` or `pgsql`. **host** | Connect to the database server on the given host. For using unix domain sockets, specify `localhost` for MySQL and the path to the unix domain socket directory for PostgreSQL. @@ -26,7 +26,7 @@ Directive | Description **password** | The password to use when connecting to the server. **dbname** | The database to use. -**Example:** +#### Example ```` [icingaweb-mysql-tcp] @@ -54,32 +54,34 @@ port = 5432 username = icingaweb password = icingaweb dbname = icingaweb -``` + +```` ### LDAP A LDAP resource represents a tree in a LDAP directory. LDAP is usually used for authentication and authorization. Directive | Description -----------------|------------ +:---------------|:------------ **type** | `ldap` **hostname** | Connect to the LDAP server on the given host. **port** | Port number to use for the connection. -**root_dn** | Root object of the tree, e.g. "ou=people,dc=icinga,dc=org" +**root_dn** | Root object of the tree, e.g. `ou=people,dc=icinga,dc=org` **bind_dn** | The user to use when connecting to the server. **bind_pw** | The password to use when connecting to the server. **encryption** | Type of encryption to use: `none` (default), `starttls`, `ldaps`. -**Example:** +#### Example ```` [ad] -type = ldap -hostname = localhost -port = 389 -root_dn = "ou=people,dc=icinga,dc=org" -bind_dn = "cn=admin,ou=people,dc=icinga,dc=org" -bind_pw = admin` +type = ldap +hostname = localhost +port = 389 +root_dn = "ou=people,dc=icinga,dc=org" +bind_dn = "cn=admin,ou=people,dc=icinga,dc=org" +bind_pw = admin + ```` ### SSH @@ -88,18 +90,20 @@ A SSH resource contains the information about the user and the private key locat ssh authentication. Directive | Description ---------------------|------------ +:--------------------|:------------ **type** | `ssh` **user** | The username to use when connecting to the server. **private_key** | The path to the private key of the user. -**Example:** +#### Example ```` + [ssh] -type = "ssh" -user = "ssh-user" -private_key = "/etc/icingaweb2/ssh/ssh-user" +type = "ssh" +user = "ssh-user" +private_key = "/etc/icingaweb2/ssh/ssh-user" + ```` ### Livestatus @@ -107,14 +111,16 @@ private_key = "/etc/icingaweb2/ssh/ssh-user" A Livestatus resource represents the location of a Livestatus socket which is used for fetching monitoring data. Directive | Description -----------------|------------ +:---------------|:------------ **type** | `livestatus` -**socket** | Location of the Livestatus socket. Either a path to a local Livestatus socket or a path to a remote Livestatus socket in the format `tcp://:`. +**socket** | location of the livestatus socket (either a path to a local livestatus socket or a path to a remote livestatus socket in the format `tcp://:`) -**Example:** +#### Example ```` + [livestatus] type = livestatus socket = /var/run/icinga2/cmd/livestatus + ```` diff --git a/doc/06-Security.md b/doc/06-Security.md index 65c908342..9c4dc13f4 100644 --- a/doc/06-Security.md +++ b/doc/06-Security.md @@ -11,14 +11,14 @@ environment they are in charge of. This chapter will describe how to do the security configuration of Icinga Web 2 and how to apply permissions and restrictions to users or groups of users. -## Basics +## Basics Icinga Web 2 access control is done by defining **roles** that associate permissions and restrictions with **users** and **groups**. There are two general kinds of things to which access can be managed: actions and objects. -### Actions +### Actions Actions are all the things an Icinga Web 2 user can do, like changing a certain configuration, changing permissions or sending a command to the Icinga instance through the Icinga command pipe. @@ -28,7 +28,7 @@ A permission is a simple list of identifiers of actions a user is allowed to do. Permissions are described in greater detail in the section [Permissions](#permissions). -### Objects +### Objects There are all kinds of different objects in Icinga Web 2: Hosts, Services, Notifications, Downtimes and Events. @@ -37,7 +37,7 @@ By default, a user can **see everything**, but it is possible to **explicitly re Restrictions are complex filter queries that describe what objects should be displayed to a user. Restrictions are described in greater detail in the section [Restrictions](#restrictions). -### Users +### Users Anyone who can **login** to Icinga Web 2 is considered a user and can be referenced to by the **user name** used during login. @@ -55,13 +55,13 @@ an **authentication backend**. For extended information on setting up authentica backend to fetch users and groups from, which must be configured separately.
-#### Managing Users +#### Managing Users When using a [Database as authentication backend](05-Authentication.md#authentication-configuration-db-authentication), it is possible to create, add and delete users directly in the frontend. This configuration can be found at **Configuration > Authentication > Users **. -### Groups +### Groups If there is a big amount of users to manage, it would be tedious to specify each user separately when regularly referring to the same group of users. Because of that, it is possible to group users. @@ -72,13 +72,13 @@ Like users, groups are identified solely by their **name** that is provided by please read the chapter [Authentication](05-Authentication.md#authentication). -#### Managing Groups +#### Managing Groups When using a [Database as an authentication backend](05-Authentication.md#authentication-configuration-db-authentication), it is possible to manage groups and group memberships directly in the frontend. This configuration can be found at **Configuration > Authentication > Groups **. -## Roles +## Roles A role defines a set of **permissions** and **restrictions** and assigns those to **users** and **groups**. For example, a role **admins** could define that certain @@ -91,7 +91,7 @@ and restrictions of the user itself and all the groups the user is member of. Pe be simply added up, while restrictions follow a slighty more complex pattern, that is described in the section [Stacking Filters](#stacking-filters). -### Configuration +### Configuration Roles can be changed either through the icingaweb2 interface, by navigation to the page **Configuration > Authentication > Roles**, or through editing the @@ -101,7 +101,7 @@ configuration file: /etc/icingaweb2/roles.ini -#### Introducing Example +#### Introducing Example To get you a quick start, here is an example of what a role definition could look like: @@ -126,11 +126,11 @@ attributes can be defined for each role in a default Icinga Web 2 installation: Directive | Description ----------------------------|----------------------------------------------------------------------------- - users | A comma-separated list of user **user names** that are affected by this role - groups | A comma-separated list of **group names** that are affected by this role - permissions | A comma-separated list of **permissions** granted by this role - monitoring/filter/objects | A **filter expression** that restricts the access to services and hosts +:--------------------------|:---------------- + **users** | a comma-separated list of user **user names** that are affected by this role + **groups** | a comma-separated list of **group names** that are affected by this role + **permissions** | a comma-separated list of **permissions** granted by this role + **monitoring/filter/objects** | a **filter expression** that restricts the access to services and hosts @@ -154,17 +154,17 @@ a module permission in the format `module/` for each installed modul When multiple roles assign permissions to the same user (either directly or indirectly through a group) all permissions are added together to get the users actual permission set. -### Global Permissions +### Global Permissions -Name | Permits ---------------------------|-------------------------------------------------------- -* | Allow everything, including module-specific permissions -config/* | Allow all configuration actions -config/modules | Allow enabling or disabling modules -module/<moduleName> | Allow access to module <moduleName> +Name | Permits +:-----------|:------------ +**\*** | allow everything, including module-specific permissions +**config/\*** | allow all configuration actions +**config/modules** | allow enabling or disabling modules +**module/<moduleName>** | allow access to module <moduleName> -### Monitoring Module Permissions +### Monitoring Module Permissions The built-in monitoring module defines an additional set of permissions, that is described in detail in the monitoring module documentation. @@ -183,7 +183,7 @@ in a default installation, is the `monitoring/filter/objects` directive, defined that can be used to apply filter to hosts and services. This directive was previously mentioned in the section [Syntax](#syntax). -### Filter Expressions +### Filter Expressions Filters operate on columns. A complete list of all available filter columns on hosts and services can be found in the monitoring module documentation. @@ -235,7 +235,7 @@ expression: As a result, a user is be able to see hosts that are matched by **ANY** of the filter expressions. The following examples will show the usefulness of this behavior: -#### Example 1: Negation +#### Example 1: Negation [winadmin] groups = "windows-admins" @@ -251,7 +251,7 @@ Will only match hosts and services whose host name does **not** contain **win** Notice that because of the behavior of two stacking filters, a user that is member of **windows-admins** and **web-admins**, will now be able to see both, Windows and non-Windows hosts and services. -#### Example 2: Hostgroups +#### Example 2: Hostgroups [unix-server] groups = "unix-admins" diff --git a/doc/99-Vagrant.md b/doc/99-Vagrant.md index d94702aee..7fdad8727 100644 --- a/doc/99-Vagrant.md +++ b/doc/99-Vagrant.md @@ -1,6 +1,10 @@ -# Vagrant +# Vagrant -## Requirements +This chapter shows how to set up and use our [Icinga Vagrant +boxes](https://github.com/icinga/icinga-vagrant) that we've created for +development, tests and demo cases. + +## Requirements * Vagrant >= version 1.5 * VirtualBox or Parallels Desktop @@ -13,7 +17,7 @@ Parallels requires the additional provider plugin $ vagrant plugin install vagrant-parallels -## General +## General The Icinga Web 2 project ships with a Vagrant virtual machine that integrates the source code with various services and example data in a controlled @@ -31,7 +35,7 @@ vagrant up After you should be able to browse [localhost:8080/icingaweb2](http://localhost:8080/icingaweb2). -## Log into Icinga Web 2 +## Log into Icinga Web 2 Both LDAP and a MySQL are configured as authentication backend. Please use one of the following login credentials: @@ -47,7 +51,7 @@ Both LDAP and a MySQL are configured as authentication backend. Please use one o -## Testing the Source Code +## Testing the Source Code All software required to run tests is installed in the virtual machine. In order to run all tests you have to execute the following command: From 2d32f4a3bea39ee02cc654c9e0808dcd7e575fa2 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 14:18:29 +0200 Subject: [PATCH 079/337] Fix markdown table syntax --- doc/02-Installation.md | 20 +++--- doc/03-Configuration.md | 16 ++--- doc/04-Resources.md | 83 ++++++++++--------------- doc/05-Authentication.md | 30 ++++----- doc/06-Security.md | 27 ++++---- doc/07-Preferences.md | 8 +-- modules/monitoring/doc/configuration.md | 12 ++-- modules/monitoring/doc/security.md | 6 +- 8 files changed, 91 insertions(+), 111 deletions(-) diff --git a/doc/02-Installation.md b/doc/02-Installation.md index ae37dbbc5..e301afa4a 100644 --- a/doc/02-Installation.md +++ b/doc/02-Installation.md @@ -35,16 +35,16 @@ pagespeed Disallow "*/icingaweb2/*"; Below is a list of official package repositories for installing Icinga Web 2 for various operating systems. -Distribution | Repository -------------------------|--------------------------- -Debian | [debmon](http://debmon.org/packages/debmon-jessie/icingaweb2), [Icinga Repository](http://packages.icinga.org/debian/) -Ubuntu | [Icinga Repository](http://packages.icinga.org/ubuntu/) -RHEL/CentOS | [Icinga Repository](http://packages.icinga.org/epel/) -openSUSE | [Icinga Repository](http://packages.icinga.org/openSUSE/) -SLES | [Icinga Repository](http://packages.icinga.org/SUSE/) -Gentoo | - -FreeBSD | - -ArchLinux | [Upstream](https://aur.archlinux.org/packages/icingaweb2) +| Distribution | Repository | +| ------------- | ---------- | +| Debian | [debmon](http://debmon.org/packages/debmon-jessie/icingaweb2), [Icinga Repository](http://packages.icinga.org/debian/) | +| Ubuntu | [Icinga Repository](http://packages.icinga.org/ubuntu/) | +| RHEL/CentOS | [Icinga Repository](http://packages.icinga.org/epel/) | +| openSUSE | [Icinga Repository](http://packages.icinga.org/openSUSE/) | +| SLES | [Icinga Repository](http://packages.icinga.org/SUSE/) | +| Gentoo | - | +| FreeBSD | - | +| ArchLinux | [Upstream](https://aur.archlinux.org/packages/icingaweb2) | Packages for distributions other than the ones listed above may also be available. Please contact your distribution packagers. diff --git a/doc/03-Configuration.md b/doc/03-Configuration.md index 5ded2eaff..879c11545 100644 --- a/doc/03-Configuration.md +++ b/doc/03-Configuration.md @@ -5,11 +5,11 @@ Apart from its web configuration capabilities, the local configuration is stored in `/etc/icingaweb2` by default (depending on your config setup). -| **File/Directory** | **Description/Purpose** | -| :------------- | :------------------- | -| **config.ini** | general configuration (logging, preferences, etc.) -| [**resources.ini**](04-Ressources.md) | global resources (Icinga Web 2 database for preferences and authentication, Icinga IDO database) -| **roles.ini** | user specific roles (e.g. `administrators`) and permissions -| [**authentication.ini**](05-Authentication.md) | authentication backends (e.g. database) -| **enabledModules** | contains symlinks to enabled modules -| **modules** | directory for module specific configuration +| File/Directory | Description/Purpose | +| ------------------------------------------------- | ------------------- | +| **config.ini** | general configuration (logging, preferences, etc.) | +| [**resources.ini**](04-Ressources.md) | global resources (Icinga Web 2 database for preferences and authentication, Icinga IDO database) | +| **roles.ini** | user specific roles (e.g. `administrators`) and permissions | +| [**authentication.ini**](05-Authentication.md) | authentication backends (e.g. database) | +| **enabledModules** | contains symlinks to enabled modules | +| **modules** | directory for module specific configuration | diff --git a/doc/04-Resources.md b/doc/04-Resources.md index a464cfe91..3fb0fe802 100644 --- a/doc/04-Resources.md +++ b/doc/04-Resources.md @@ -16,15 +16,15 @@ paragraphs. A Database resource defines a connection to a SQL databases which can contain users and groups to handle authentication and authorization, monitoring data or user preferences. -Directive | Description -:---------------|:------------ -**type** | `db` -**db** | Database management system. In most cases `mysql` or `pgsql`. -**host** | Connect to the database server on the given host. For using unix domain sockets, specify `localhost` for MySQL and the path to the unix domain socket directory for PostgreSQL. -**port** | Port number to use. Mandatory for connections to a PostgreSQL database. -**username** | The username to use when connecting to the server. -**password** | The password to use when connecting to the server. -**dbname** | The database to use. +| Directive | Description | +| ------------- | ----------- | +| **type** | `db` | +| **db** | Database management system. In most cases `mysql` or `pgsql`. | +| **host** | Connect to the database server on the given host. For using unix domain sockets, specify `localhost` for MySQL and the path to the unix domain socket directory for PostgreSQL. | +| **port** | Port number to use. Mandatory for connections to a PostgreSQL database. | +| **username** | The username to use when connecting to the server. | +| **password** | The password to use when connecting to the server. | +| **dbname** | The database to use. | #### Example @@ -61,26 +61,26 @@ dbname = icingaweb A LDAP resource represents a tree in a LDAP directory. LDAP is usually used for authentication and authorization. -Directive | Description -:---------------|:------------ -**type** | `ldap` -**hostname** | Connect to the LDAP server on the given host. -**port** | Port number to use for the connection. -**root_dn** | Root object of the tree, e.g. `ou=people,dc=icinga,dc=org` -**bind_dn** | The user to use when connecting to the server. -**bind_pw** | The password to use when connecting to the server. -**encryption** | Type of encryption to use: `none` (default), `starttls`, `ldaps`. +| Directive | Description | +| ----------------- | ----------- | +| **type** | `ldap` | +| **hostname** | Connect to the LDAP server on the given host. | +| **port** | Port number to use for the connection. | +| **root_dn** | Root object of the tree, e.g. `ou=people,dc=icinga,dc=org` | +| **bind_dn** | The user to use when connecting to the server. | +| **bind_pw** | The password to use when connecting to the server. | +| **encryption** | Type of encryption to use: `none` (default), `starttls`, `ldaps`. | #### Example ```` [ad] -type = ldap -hostname = localhost -port = 389 -root_dn = "ou=people,dc=icinga,dc=org" -bind_dn = "cn=admin,ou=people,dc=icinga,dc=org" -bind_pw = admin +type = ldap +hostname = localhost +port = 389 +root_dn = "ou=people,dc=icinga,dc=org" +bind_dn = "cn=admin,ou=people,dc=icinga,dc=org" +bind_pw = admin ```` @@ -89,38 +89,19 @@ bind_pw = admin A SSH resource contains the information about the user and the private key location, which can be used for the key-based ssh authentication. -Directive | Description -:--------------------|:------------ -**type** | `ssh` -**user** | The username to use when connecting to the server. -**private_key** | The path to the private key of the user. +| Directive | Description | +| ----------------- | ----------- | +| **type** | `ssh` | +| **user** | The username to use when connecting to the server. | +| **private_key** | The path to the private key of the user. | #### Example ```` [ssh] -type = "ssh" -user = "ssh-user" -private_key = "/etc/icingaweb2/ssh/ssh-user" - -```` - -### Livestatus - -A Livestatus resource represents the location of a Livestatus socket which is used for fetching monitoring data. - -Directive | Description -:---------------|:------------ -**type** | `livestatus` -**socket** | location of the livestatus socket (either a path to a local livestatus socket or a path to a remote livestatus socket in the format `tcp://:`) - -#### Example - -```` - -[livestatus] -type = livestatus -socket = /var/run/icinga2/cmd/livestatus +type = "ssh" +user = "ssh-user" +private_key = "/etc/icingaweb2/ssh/ssh-user" ```` diff --git a/doc/05-Authentication.md b/doc/05-Authentication.md index c30c593ff..8bf10970f 100644 --- a/doc/05-Authentication.md +++ b/doc/05-Authentication.md @@ -68,13 +68,13 @@ Active Directory or LDAP configuration method. ### LDAP -Directive | Description -------------------------|------------ -**backend** | `ldap` -**resource** | The name of the LDAP resource defined in [resources.ini](04-Resources.md#resources). -**user_class** | LDAP user class. -**user_name_attribute** | LDAP attribute which contains the username. -**filter** | LDAP search filter. +| Directive | Description | +| ------------------------- | ----------- | +| **backend** | `ldap` | +| **resource** | The name of the LDAP resource defined in [resources.ini](04-Resources.md#resources). | +| **user_class** | LDAP user class. | +| **user_name_attribute** | LDAP attribute which contains the username. | +| **filter** | LDAP search filter. | **Example:** @@ -93,10 +93,10 @@ with Icinga Web 2 (e.g. an alias) no matter what the primary user id might actua ### Active Directory -Directive | Description -------------------------|------------ -**backend** | `msldap` -**resource** | The name of the LDAP resource defined in [resources.ini](04-Resources.md#resources). +| Directive | Description | +| ------------- | ----------- | +| **backend** | `msldap` | +| **resource** | The name of the LDAP resource defined in [resources.ini](04-Resources.md#resources). | **Example:** @@ -112,10 +112,10 @@ If you want to authenticate against a MySQL or a PostgreSQL database, you have t [database resource](04-Resources.md#resources-configuration-database) which will be referenced as data source for the database authentication method. -Directive | Description -------------------------|------------ -**backend** | `db` -**resource** | The name of the database resource defined in [resources.ini](04-Resources.md#resources). +| Directive | Description | +| ------------------------| ----------- | +| **backend** | `db` | +| **resource** | The name of the database resource defined in [resources.ini](04-Resources.md#resources). | **Example:** diff --git a/doc/06-Security.md b/doc/06-Security.md index 9c4dc13f4..7c0fd40f4 100644 --- a/doc/06-Security.md +++ b/doc/06-Security.md @@ -97,8 +97,7 @@ Roles can be changed either through the icingaweb2 interface, by navigation to the page **Configuration > Authentication > Roles**, or through editing the configuration file: - - /etc/icingaweb2/roles.ini + /etc/icingaweb2/roles.ini #### Introducing Example @@ -125,12 +124,12 @@ Each role is defined as a section, with the name of the role as section name. Th attributes can be defined for each role in a default Icinga Web 2 installation: - Directive | Description -:--------------------------|:---------------- - **users** | a comma-separated list of user **user names** that are affected by this role - **groups** | a comma-separated list of **group names** that are affected by this role - **permissions** | a comma-separated list of **permissions** granted by this role - **monitoring/filter/objects** | a **filter expression** that restricts the access to services and hosts +| Directive | Description | +| ----------------------------- | ----------- | +| **users** | a comma-separated list of user **user names** that are affected by this role | +| **groups** | a comma-separated list of **group names** that are affected by this role | +| **permissions** | a comma-separated list of **permissions** granted by this role | +| **monitoring/filter/objects** | a **filter expression** that restricts the access to services and hosts | @@ -156,12 +155,12 @@ through a group) all permissions are added together to get the users actual perm ### Global Permissions -Name | Permits -:-----------|:------------ -**\*** | allow everything, including module-specific permissions -**config/\*** | allow all configuration actions -**config/modules** | allow enabling or disabling modules -**module/<moduleName>** | allow access to module <moduleName> +| Name | Permits | +| ----------------------------- | ------------ | +| **\*** | allow everything, including module-specific permissions | +| **config/\*** | allow all configuration actions | +| **config/modules** | allow enabling or disabling modules | +| **module/<moduleName>** | allow access to module <moduleName> | ### Monitoring Module Permissions diff --git a/doc/07-Preferences.md b/doc/07-Preferences.md index e89a507bf..198969378 100644 --- a/doc/07-Preferences.md +++ b/doc/07-Preferences.md @@ -30,10 +30,10 @@ In order to be more flexible in distributed setups you can store preferences in For storing preferences in a database, you have to define a [database resource](04-Resources.md#resources-configuration-database) which will be referenced as resource for the preferences storage. -Directive | Description -------------------------|------------ -**type** | `db` -**resource** | The name of the database resource defined in [resources.ini](04-Resources.md#resources). +| Directive | Description | +| ------------- | ----------- | +| **type** | `db` | +| **resource** | The name of the database resource defined in [resources.ini](04-Resources.md#resources). | **Example:** diff --git a/modules/monitoring/doc/configuration.md b/modules/monitoring/doc/configuration.md index 5c3a23aa4..da63d8bbd 100644 --- a/modules/monitoring/doc/configuration.md +++ b/modules/monitoring/doc/configuration.md @@ -5,12 +5,12 @@ Apart from its web configuration capabilities, the local configuration is stored in `/etc/icingaweb2` by default (depending on your config setup). - Location | File | Description - ------------------------------|-----------------------|--------------------------- - modules/monitoring | Directory | `monitoring` module specific configuration - modules/monitoring | config.ini | Security settings (e.g. protected custom vars) for the `monitoring` module - modules/monitoring | backends.ini | Backend type and resources (e.g. Icinga IDO DB) - modules/monitoring | [commandtransports.ini](commandtransports.md#commandtransports) | Command transports for specific Icinga instances +| Location | File | Description | +| --------------------- | ----------------------------------------------------------------- | ----------- | +| modules/monitoring | Directory | `monitoring` module specific configuration | +| modules/monitoring | config.ini | Security settings (e.g. protected custom vars) for the `monitoring` module | +| modules/monitoring | backends.ini | Backend type and resources (e.g. Icinga IDO DB) | +| modules/monitoring | [commandtransports.ini](commandtransports.md#commandtransports) | Command transports for specific Icinga instances | diff --git a/modules/monitoring/doc/security.md b/modules/monitoring/doc/security.md index cce3727a8..74ca69519 100644 --- a/modules/monitoring/doc/security.md +++ b/modules/monitoring/doc/security.md @@ -13,7 +13,7 @@ commands when using the monitoring module. | Name | Permits | -|---------------------------------------------|-----------------------------------------------------------------------------| +| ------------------------------------------- | --------------------------------------------------------------------------- | | monitoring/command/* | Allow all commands | | monitoring/command/schedule-check | Allow scheduling host and service checks' | | monitoring/command/acknowledge-problem | Allow acknowledging host and service problems | @@ -33,7 +33,7 @@ The monitoring module allows filtering objects: | Keys | Restricts | -|----------------------------|-----------------------------------------------| +| ---------------------------|---------------------------------------------- | | monitoring/filter/objects | Applies a filter to all hosts and services | @@ -48,7 +48,7 @@ The following filter column names are available in filter expressions: | Column | -|--------------------------------------------------------------| +| ------------------------------------------------------------ | | instance_name | | host_name | | hostgroup_name | From c322077aed1268d98205f7055b11ad9c5c837738 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 1 Sep 2016 14:31:39 +0200 Subject: [PATCH 080/337] Fix code block related syntax issues in markdown files --- doc/02-Installation.md | 3 +- doc/04-Resources.md | 15 +++---- doc/07-Preferences.md | 2 +- doc/99-Vagrant.md | 8 ++-- modules/monitoring/doc/commandtransports.md | 50 ++++++++++----------- 5 files changed, 37 insertions(+), 41 deletions(-) diff --git a/doc/02-Installation.md b/doc/02-Installation.md index e301afa4a..5a3b5dc97 100644 --- a/doc/02-Installation.md +++ b/doc/02-Installation.md @@ -108,8 +108,7 @@ The packages for RHEL/CentOS depend on other packages which are distributed as p [EPEL repository](http://fedoraproject.org/wiki/EPEL). Please make sure to enable this repository by following [these instructions](http://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F). -> Please note that installing Icinga Web 2 on **RHEL/CentOS 5** is not supported due to EOL versions of PHP and -> PostgreSQL. +> Please note that installing Icinga Web 2 on **RHEL/CentOS 5** is not supported due to EOL versions of PHP and PostgreSQL. ### Installing Icinga Web 2 diff --git a/doc/04-Resources.md b/doc/04-Resources.md index 3fb0fe802..10c668997 100644 --- a/doc/04-Resources.md +++ b/doc/04-Resources.md @@ -28,7 +28,7 @@ to handle authentication and authorization, monitoring data or user preferences. #### Example -```` +``` [icingaweb-mysql-tcp] type = db db = mysql @@ -54,8 +54,7 @@ port = 5432 username = icingaweb password = icingaweb dbname = icingaweb - -```` +``` ### LDAP @@ -73,7 +72,7 @@ A LDAP resource represents a tree in a LDAP directory. LDAP is usually used for #### Example -```` +``` [ad] type = ldap hostname = localhost @@ -81,8 +80,7 @@ port = 389 root_dn = "ou=people,dc=icinga,dc=org" bind_dn = "cn=admin,ou=people,dc=icinga,dc=org" bind_pw = admin - -```` +``` ### SSH @@ -97,11 +95,10 @@ ssh authentication. #### Example -```` +``` [ssh] type = "ssh" user = "ssh-user" private_key = "/etc/icingaweb2/ssh/ssh-user" - -```` +``` diff --git a/doc/07-Preferences.md b/doc/07-Preferences.md index 198969378..8348fd886 100644 --- a/doc/07-Preferences.md +++ b/doc/07-Preferences.md @@ -22,7 +22,7 @@ For storing preferences in INI files you have to add the following section to th ``` [preferences] type = ini -```` +``` ### Store Preferences in a Database diff --git a/doc/99-Vagrant.md b/doc/99-Vagrant.md index 7fdad8727..b0d4a28c4 100644 --- a/doc/99-Vagrant.md +++ b/doc/99-Vagrant.md @@ -25,9 +25,9 @@ environment. This enables developers and users to test Livestatus, MySQL and PostgreSQL backends as well as the LDAP authentication. All you have to do is install Vagrant and run: -```` +``` vagrant up -```` +``` > **Note:** The first boot of the vm takes a fairly long time because > you'll download a plain CentOS base box and Vagrant will automatically @@ -56,6 +56,6 @@ Both LDAP and a MySQL are configured as authentication backend. Please use one o All software required to run tests is installed in the virtual machine. In order to run all tests you have to execute the following command: -```` +``` vagrant ssh -c "icingacli test php unit" -```` +``` diff --git a/modules/monitoring/doc/commandtransports.md b/modules/monitoring/doc/commandtransports.md index 680f34ca0..238643f5c 100644 --- a/modules/monitoring/doc/commandtransports.md +++ b/modules/monitoring/doc/commandtransports.md @@ -22,11 +22,11 @@ the order of sections in the commandtransports.ini. A local Icinga instance requires the following directives: -```` +``` [icinga2] -transport = local -path = /var/run/icinga2/cmd/icinga2.cmd -```` +transport = local +path = /var/run/icinga2/cmd/icinga2.cmd +``` When sending commands to the Icinga instance, Icinga Web 2 opens the file found on the local filesystem underneath 'path' and writes the external command to it. @@ -36,14 +36,14 @@ on the local filesystem underneath 'path' and writes the external command to it. A command pipe on a remote host's filesystem can be accessed by configuring a SSH based command transport and requires the following directives: -```` +``` [icinga2] -transport = remote -path = /var/run/icinga2/cmd/icinga2.cmd -host = example.tld -;port = 22 ; Optional. The default is 22 -user = icinga -```` +transport = remote +path = /var/run/icinga2/cmd/icinga2.cmd +host = example.tld +user = icinga +;port = 22 ; Optional. The default is 22 +``` To make this example work, you'll need to permit your web-server's user public-key based access to the defined remote host so that Icinga Web 2 can @@ -56,23 +56,23 @@ key file on the local filesystem that is used to access the remote host. To accomplish this, a new resource is required that is defined in your transport's configuration instead of a user: -```` +``` [icinga2] -transport = remote -path = /var/run/icinga2/cmd/icinga2.cmd -host = example.tld -;port = 22 ; Optional. The default is 22 -resource = example.tld-icinga2 -```` +transport = remote +path = /var/run/icinga2/cmd/icinga2.cmd +host = example.tld +resource = example.tld-icinga2 +;port = 22 ; Optional. The default is 22 +``` The resource's configuration needs to be put into the resources.ini file: -```` +``` [example.tld-icinga2] -type = ssh -user = icinga +type = ssh +user = icinga private_key = /etc/icingaweb2/ssh/icinga -```` +``` ## Configuring transports for different Icinga instances @@ -81,7 +81,7 @@ define which transport belongs to which Icinga instance by providing the directive 'instance'. This directive should contain the name of the Icinga instance you want to assign to the transport: -```` +``` [icinga1] ... instance = icinga1 @@ -89,8 +89,8 @@ instance = icinga1 [icinga2] ... instance = icinga2 -```` +``` Associating a transport to a specific Icinga instance causes this transport to be used to send commands to the linked instance only. Transports without a -linked Icinga instance are utilized to send commands to all instances. \ No newline at end of file +linked Icinga instance are utilized to send commands to all instances. From f72411c6285a539ec234b5aaec5755ce614b41fb Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Sep 2016 15:09:50 +0200 Subject: [PATCH 081/337] Avoid local variable name `self' in icinga.js refs #10703 --- public/js/icinga.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/js/icinga.js b/public/js/icinga.js index b52c19afc..6c5035912 100644 --- a/public/js/icinga.js +++ b/public/js/icinga.js @@ -69,10 +69,10 @@ */ this.modules = {}; - var self = this; + var _this = this; $(document).ready(function () { - self.initialize(); - self = null; + _this.initialize(); + _this = null; }); }; @@ -94,9 +94,9 @@ this.loader = new Icinga.Loader(this); this.events = new Icinga.Events(this); this.history = new Icinga.History(this); - var self = this; + var _this = this; $.each(Icinga.Behaviors, function(name, Behavior) { - self.behaviors[name.toLowerCase()] = new Behavior(self); + _this.behaviors[name.toLowerCase()] = new Behavior(_this); }); this.timezone.initialize(); From 311fd36326a83849fd49f8e71ff30a66606c4a46 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Sep 2016 15:21:45 +0200 Subject: [PATCH 082/337] Avoid local variable name `self' in actiontable.js refs #10703 --- public/js/icinga/behavior/actiontable.js | 46 ++++++++++++------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/public/js/icinga/behavior/actiontable.js b/public/js/icinga/behavior/actiontable.js index 8d8424999..155e549a0 100644 --- a/public/js/icinga/behavior/actiontable.js +++ b/public/js/icinga/behavior/actiontable.js @@ -185,12 +185,12 @@ filter.addClass('active'); return; } - var self = this; + var _this = this; this.rowActions() .filter( function (i, el) { - var params = self.getRowData($(el)); - if (self.icinga.utils.objectKeys(params).length !== self.icinga.utils.objectKeys(filter).length) { + var params = _this.getRowData($(el)); + if (_this.icinga.utils.objectKeys(params).length !== _this.icinga.utils.objectKeys(filter).length) { return false; } var equal = true; @@ -274,21 +274,21 @@ * @returns {String} The filter string */ toQuery: function() { - var self = this; + var _this = this; var selections = this.selections(); var queries = []; var utils = this.icinga.utils; if (selections.length === 1) { return $(selections[0]).attr('href'); - } else if (selections.length > 1 && self.hasMultiselection()) { + } else if (selections.length > 1 && _this.hasMultiselection()) { selections.each(function (i, el) { var parts = []; - $.each(self.getRowData($(el)), function(key, value) { + $.each(_this.getRowData($(el)), function(key, value) { parts.push(utils.fixedEncodeURIComponent(key) + '=' + utils.fixedEncodeURIComponent(value)); }); queries.push('(' + parts.join('&') + ')'); }); - return self.getMultiselectionUrl() + '?(' + queries.join('|') + ')'; + return _this.getMultiselectionUrl() + '?(' + queries.join('|') + ')'; } else { return ''; } @@ -304,9 +304,9 @@ var query = parseSelectionQuery(hash); if (query.length > 1 && this.hasMultiselectionUrl(this.icinga.utils.parseUrl(hash).path)) { // select all rows with matching filters - var self = this; + var _this = this; $.each(query, function(i, selection) { - self.select(selection); + _this.select(selection); }); } if (query.length > 1) { @@ -351,10 +351,10 @@ * Handle clicks on table rows and update selection and history */ ActionTable.prototype.onRowClicked = function (event) { - var self = event.data.self; + var _this = event.data.self; var $target = $(event.target); var $tr = $target.closest('tr'); - var table = new Selection($tr.closest('table.action, table.table-row-selectable')[0], self.icinga); + var table = new Selection($tr.closest('table.action, table.table-row-selectable')[0], _this.icinga); // some rows may contain form actions that trigger a different action, pass those through if (!$target.hasClass('rowaction') && $target.closest('form').length && @@ -390,18 +390,18 @@ var count = table.selections().length; if (count > 0) { var query = table.toQuery(); - self.icinga.loader.loadUrl(query, self.icinga.events.getLinkTargetFor($tr)); + _this.icinga.loader.loadUrl(query, _this.icinga.events.getLinkTargetFor($tr)); state += '#!' + query; } else { - if (self.icinga.events.getLinkTargetFor($tr).attr('id') === 'col2') { - self.icinga.ui.layout1col(); + if (_this.icinga.events.getLinkTargetFor($tr).attr('id') === 'col2') { + _this.icinga.ui.layout1col(); } } - self.icinga.history.pushUrl(state); + _this.icinga.history.pushUrl(state); // redraw all table selections - self.tables().each(function () { - new Selection(this, self.icinga).refresh(); + _this.tables().each(function () { + new Selection(this, _this.icinga).refresh(); }); // update selection info @@ -414,7 +414,7 @@ */ ActionTable.prototype.onRendered = function(evt) { var container = evt.target; - var self = evt.data.self; + var _this = evt.data.self; // initialize all rows with the correct row action $('table.action tr, table.table-row-selectable tr', container).each(function(idx, el) { @@ -442,19 +442,19 @@ }); // draw all active selections that have disappeared on reload - self.tables().each(function(i, el) { - new Selection(el, self.icinga).refresh(); + _this.tables().each(function(i, el) { + new Selection(el, _this.icinga).refresh(); }); // update displayed selection counter - var table = new Selection(self.tables(container).first()); + var table = new Selection(_this.tables(container).first()); $(container).find('.selection-info-count').text(table.selections().size()); }; ActionTable.prototype.clearAll = function () { - var self = this; + var _this = this; this.tables().each(function () { - new Selection(this, self.icinga).clear(); + new Selection(this, _this.icinga).clear(); }); }; From 0059622bba987663515f7fe034b7a7a1dcade549 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Sep 2016 15:42:54 +0200 Subject: [PATCH 083/337] Avoid local variable name `self' in form.js refs #10703 --- public/js/icinga/behavior/form.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/js/icinga/behavior/form.js b/public/js/icinga/behavior/form.js index eae75168d..0acb3d3c6 100644 --- a/public/js/icinga/behavior/form.js +++ b/public/js/icinga/behavior/form.js @@ -71,15 +71,15 @@ var origFocus = document.activeElement; var containerId = $container.attr('id'); var icinga = this.icinga; - var self = this.icinga.behaviors.form; + var _this = this.icinga.behaviors.form; var changed = false; $container.find('form').each(function () { - var form = self.uniqueFormName(this); + var form = _this.uniqueFormName(this); if (autorefresh) { // check if an element in this container was changed $(this).find('input').each(function () { var name = this.name; - if (self.inputs[form] && self.inputs[form][name]) { + if (_this.inputs[form] && _this.inputs[form][name]) { icinga.logger.debug( 'form input: ' + form + '.' + name + ' was changed and aborts reload...' ); @@ -88,7 +88,7 @@ }); } else { // user-triggered reload, forget all changes to forms in this container - self.inputs[form] = null; + _this.inputs[form] = null; } }); if (changed) { From 0d7f8148dedea4abab88a5304b38c2c62fc020cc Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Sep 2016 15:49:39 +0200 Subject: [PATCH 084/337] Avoid local variable name `self' in navigation.js refs #10703 --- public/js/icinga/behavior/navigation.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/public/js/icinga/behavior/navigation.js b/public/js/icinga/behavior/navigation.js index 539639685..2da6bc87d 100644 --- a/public/js/icinga/behavior/navigation.js +++ b/public/js/icinga/behavior/navigation.js @@ -108,14 +108,14 @@ var $a = $(this); var href = $a.attr('href'); var $li; - var self = event.data.self; - var icinga = self.icinga; + var _this = event.data.self; + var icinga = _this.icinga; - self.hovered = null; + _this.hovered = null; if (href.match(/#/)) { // ...it may be a menu section without a dedicated link. // Switch the active menu item: - self.setActive($a); + _this.setActive($a); $li = $a.closest('li'); if ($li.hasClass('hover')) { $li.removeClass('hover'); @@ -128,7 +128,7 @@ return; } } else { - self.setActive($(event.target)); + _this.setActive($(event.target)); } // update target url of the menu container to the clicked link var $menu = $('#menu'); @@ -276,9 +276,9 @@ var $li = $(this), delay = 800, - self = event.data.self; + _this = event.data.self; - self.hovered = null; + _this.hovered = null; if ($li.hasClass('active')) { $li.siblings().removeClass('hover'); return; @@ -315,14 +315,14 @@ $sibling.removeClass('hover'); } }); - self.hoverElement($li); + _this.hoverElement($li); }, delay); }; Navigation.prototype.leaveSidebar = function (event) { var $sidebar = $(this), $li = $sidebar.find('li.hover'), - self = event.data.self; + _this = event.data.self; if (! $li.length) { $('#layout').removeClass('hoveredmenu'); return; @@ -337,7 +337,7 @@ $li.removeClass('hover'); $('#layout').removeClass('hoveredmenu'); }, 500); - self.hovered = null; + _this.hovered = null; }; Navigation.prototype.hoverElement = function ($li) { @@ -356,7 +356,7 @@ Navigation.prototype.dropdownLeave = function (event) { var $li = $(this), - self = event.data.self; + _this = event.data.self; setTimeout(function () { // TODO: make this behave well together with keyboard navigation try { @@ -365,7 +365,7 @@ } } catch(e) { /* Bypass because if IE8 */ } }, 300); - self.hovered = null; + _this.hovered = null; }; Icinga.Behaviors.Navigation = Navigation; From 060a82fa4a4a09075c28609a6ae45febe6d4fe11 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Sep 2016 15:58:32 +0200 Subject: [PATCH 085/337] Avoid local variable name `self' in tooltip.js and tristate.js refs #10703 --- public/js/icinga/behavior/tooltip.js | 4 ++-- public/js/icinga/behavior/tristate.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/js/icinga/behavior/tooltip.js b/public/js/icinga/behavior/tooltip.js index 98cc1ff59..abc1d4952 100644 --- a/public/js/icinga/behavior/tooltip.js +++ b/public/js/icinga/behavior/tooltip.js @@ -21,7 +21,7 @@ }; Tooltip.prototype.onRendered = function(evt) { - var self = evt.data.self, icinga = evt.data.icinga, el = evt.target; + var _this = evt.data.self, icinga = evt.data.icinga, el = evt.target; $('[title]', el).each(function () { var $el = $(this); @@ -63,7 +63,7 @@ return; } var title = $(this).find('.tipsy-inner').html(); - var atMouse = document.elementFromPoint(self.mouseX, self.mouseY); + var atMouse = document.elementFromPoint(_this.mouseX, _this.mouseY); var nearestTip = $(atMouse).closest('[original-title="' + title + '"]')[0]; if (nearestTip) { var tipsy = $.data(nearestTip, 'tipsy'); diff --git a/public/js/icinga/behavior/tristate.js b/public/js/icinga/behavior/tristate.js index 88c07102d..77dad14e1 100644 --- a/public/js/icinga/behavior/tristate.js +++ b/public/js/icinga/behavior/tristate.js @@ -13,7 +13,7 @@ Tristate.prototype = new Icinga.EventListener(); Tristate.prototype.clickTriState = function (event) { - var self = event.data.self; + var _this = event.data.self; var $tristate = $(this); var triState = parseInt($tristate.data('icinga-tristate'), 10); @@ -42,7 +42,7 @@ } else { $tristate.parent().find('b.tristate-changed').css('visibility', 'hidden'); } - self.icinga.ui.setTriState(value.toString(), $tristate); + _this.icinga.ui.setTriState(value.toString(), $tristate); }; Icinga.Behaviors.Tristate = Tristate; From 4e2c626ae0e5602e38bed47baf6531cbf00ca812 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Sep 2016 16:02:13 +0200 Subject: [PATCH 086/337] Avoid local variable name `self' in eventlistener.js refs #10703 --- public/js/icinga/eventlistener.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/js/icinga/eventlistener.js b/public/js/icinga/eventlistener.js index a7415e2f8..786441fa5 100644 --- a/public/js/icinga/eventlistener.js +++ b/public/js/icinga/eventlistener.js @@ -41,14 +41,14 @@ * 'on' to register listeners */ EventListener.prototype.bind = function (emitter) { - var self = this; + var _this = this; $.each(this.handlers, function(i, handler) { - self.icinga.logger.debug('bind: ' + handler.evt + '(' + handler.cond + ')'); + _this.icinga.logger.debug('bind: ' + handler.evt + '(' + handler.cond + ')'); emitter.on( handler.evt, handler.cond, { self: handler.scope || emitter, - icinga: self.icinga + icinga: _this.icinga }, handler.fn ); }); @@ -61,9 +61,9 @@ * 'off' to un-register listeners. */ EventListener.prototype.unbind = function (emitter) { - var self = this; + var _this = this; $.each(this.handlers, function(i, handler) { - self.icinga.logger.debug('unbind: ' + handler.evt + '(' + handler.cond + ')'); + _this.icinga.logger.debug('unbind: ' + handler.evt + '(' + handler.cond + ')'); emitter.off(handler.evt, handler.cond, handler.fn); }); }; From 3c43d38171a1967c48919153e4e2dfad9870ab85 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Sep 2016 16:13:38 +0200 Subject: [PATCH 087/337] Avoid local variable name `self' in events.js refs #10703 --- public/js/icinga/events.js | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index aeca60db4..85dbdd87d 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -33,8 +33,8 @@ // TODO: What's this? applyHandlers: function (event) { var $target = $(event.target); - var self = event.data.self; - var icinga = self.icinga; + var _this = event.data.self; + var icinga = _this.icinga; if (! icinga) { // Attempt to catch a rare error, race condition, whatever @@ -42,7 +42,7 @@ return; } - if (self.initializeModules) { + if (_this.initializeModules) { var loaded = false; var moduleName = $target.data('icingaModule'); if (moduleName) { @@ -63,7 +63,7 @@ // so we need to ensure that it is called the first time they are // initialized event.stopImmediatePropagation(); - self.initializeModules = false; + _this.initializeModules = false; var $container = $target.closest('.container'); if (! $container.length) { @@ -79,7 +79,7 @@ return false; } } else { - self.initializeModules = true; + _this.initializeModules = true; } $('.dashboard > div', $target).each(function(idx, el) { @@ -93,7 +93,7 @@ var $searchField = $('#menu input.search', $target); // Remember initial search field value if any if ($searchField.length && $searchField.val().length) { - self.searchValue = $searchField.val(); + _this.searchValue = $searchField.val(); } }, @@ -190,12 +190,12 @@ }, autoSubmitSearch: function(event) { - var self = event.data.self; - if ($('#menu input.search').val() === self.searchValue) { + var _this = event.data.self; + if ($('#menu input.search').val() === _this.searchValue) { return; } - self.searchValue = $('#menu input.search').val(); - return self.autoSubmitForm(event); + _this.searchValue = $('#menu input.search').val(); + return _this.autoSubmitForm(event); }, rememberSubmitButton: function(e) { @@ -212,8 +212,8 @@ * */ submitForm: function (event, autosubmit) { - var self = event.data.self; - var icinga = self.icinga; + var _this = event.data.self; + var icinga = _this.icinga; // .closest is not required unless subelements to trigger this var $form = $(event.currentTarget).closest('form'); var url = $form.attr('action'); @@ -277,9 +277,9 @@ $button.addClass('active'); } - $target = self.getLinkTargetFor($button); + $target = _this.getLinkTargetFor($button); } else { - $target = self.getLinkTargetFor($form); + $target = _this.getLinkTargetFor($form); } if (! url) { @@ -427,8 +427,8 @@ * Someone clicked a link or tr[href] */ linkClicked: function (event) { - var self = event.data.self; - var icinga = self.icinga; + var _this = event.data.self; + var icinga = _this.icinga; var $a = $(this); var $eventTarget = $(event.target); var href = $a.attr('href'); @@ -512,7 +512,7 @@ } return false; } - $target = self.getLinkTargetFor($a); + $target = _this.getLinkTargetFor($a); formerUrl = $target.data('icingaUrl'); if (typeof formerUrl !== 'undefined' && formerUrl.split(/#/)[0] === href.split(/#/)[0]) { @@ -524,7 +524,7 @@ return false; } } else { - $target = self.getLinkTargetFor($a); + $target = _this.getLinkTargetFor($a); } // Load link URL From e726f10e6817adcfec080a62de7d83907a1f5988 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Sep 2016 16:15:10 +0200 Subject: [PATCH 088/337] Avoid local variable name `self' in history.js and timer.js refs #10703 --- public/js/icinga/history.js | 8 ++++---- public/js/icinga/timer.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/public/js/icinga/history.js b/public/js/icinga/history.js index a0ebffa32..d0f6f6612 100644 --- a/public/js/icinga/history.js +++ b/public/js/icinga/history.js @@ -144,8 +144,8 @@ */ onHistoryChange: function (event) { - var self = event.data.self, - icinga = self.icinga; + var _this = event.data.self, + icinga = _this.icinga; icinga.logger.debug('Got a history change'); @@ -157,9 +157,9 @@ } // keep the last pushed url in sync with history changes - self.lastPushUrl = location.href; + _this.lastPushUrl = location.href; - self.applyLocationBar(); + _this.applyLocationBar(); // notify behaviors of the state change $.each(this.icinga.behaviors, function (i, behavior) { diff --git a/public/js/icinga/timer.js b/public/js/icinga/timer.js index 153e5325f..e16c5fe1d 100644 --- a/public/js/icinga/timer.js +++ b/public/js/icinga/timer.js @@ -49,8 +49,8 @@ * The initialization function starts our ticker */ initialize: function () { - var self = this; - this.ticker = setInterval(function () { self.tick(); }, this.interval); + var _this = this; + this.ticker = setInterval(function () { _this.tick(); }, this.interval); }, /** From 56c10ffdd591c20ae900a085c1cbea73706e87df Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Sep 2016 16:28:57 +0200 Subject: [PATCH 089/337] Avoid local variable name `self' in loader.js refs #10703 --- public/js/icinga/loader.js | 52 +++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index 2ff4d7f6f..d4b84cbb1 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -114,13 +114,13 @@ contentType = false; } - var self = this; + var _this = this; var req = $.ajax({ type : method, url : url, data : data, headers: headers, - context: self, + context: _this, contentType: contentType, processData: ! isFormData }); @@ -153,9 +153,9 @@ * @param {object} $target The target container */ submitFormToIframe: function ($form, action, $target) { - var self = this; + var _this = this; - $form.prop('action', self.icinga.utils.addUrlParams(action, { + $form.prop('action', _this.icinga.utils.addUrlParams(action, { '_frameUpload': true })); $form.prop('target', 'fileupload-frame-target'); @@ -165,10 +165,10 @@ var $redirectMeta = $contents.find('meta[name="redirectUrl"]'); if ($redirectMeta.length) { - self.redirectToUrl($redirectMeta.attr('content'), $target); + _this.redirectToUrl($redirectMeta.attr('content'), $target); } else { // Fetch the frame's new content and paste it into the target - self.renderContentToContainer( + _this.renderContentToContainer( $contents.find('body').html(), $target, 'replace' @@ -208,16 +208,16 @@ }, autorefresh: function () { - var self = this; - if (self.autorefreshEnabled !== true) { + var _this = this; + if (_this.autorefreshEnabled !== true) { return; } $('.container').filter(this.filterAutorefreshingContainers).each(function (idx, el) { var $el = $(el); var id = $el.attr('id'); - if (typeof self.requests[id] !== 'undefined') { - self.icinga.logger.debug('No refresh, request pending for ', id); + if (typeof _this.requests[id] !== 'undefined') { + _this.icinga.logger.debug('No refresh, request pending for ', id); return; } @@ -225,12 +225,12 @@ var lastUpdate = $el.data('lastUpdate'); if (typeof interval === 'undefined' || ! interval) { - self.icinga.logger.info('No interval, setting default', id); + _this.icinga.logger.info('No interval, setting default', id); interval = 10; } if (typeof lastUpdate === 'undefined' || ! lastUpdate) { - self.icinga.logger.info('No lastUpdate, setting one', id); + _this.icinga.logger.info('No lastUpdate, setting one', id); $el.data('lastUpdate',(new Date()).getTime()); return; } @@ -248,12 +248,12 @@ return; } - if (self.loadUrl($el.data('icingaUrl'), $el, undefined, undefined, undefined, true) === false) { - self.icinga.logger.debug( + if (_this.loadUrl($el.data('icingaUrl'), $el, undefined, undefined, undefined, true) === false) { + _this.icinga.logger.debug( 'NOT autorefreshing ' + id + ', even if ' + interval + ' ms passed. Request pending?' ); } else { - self.icinga.logger.debug( + _this.icinga.logger.debug( 'Autorefreshing ' + id + ' ' + interval + ' ms passed' ); } @@ -277,12 +277,12 @@ processNotificationHeader: function(req) { var header = req.getResponseHeader('X-Icinga-Notification'); - var self = this; + var _this = this; if (! header) return false; var list = header.split('&'); $.each(list, function(idx, el) { var parts = decodeURIComponent(el).split(' '); - self.createNotice(parts.shift(), parts.join(' ')); + _this.createNotice(parts.shift(), parts.join(' ')); }); return true; }, @@ -406,15 +406,15 @@ // TODO: this is just a prototype, disabled for now return; - var self = this; + var _this = this; $('img.icon', $container).each(function(idx, img) { var src = $(img).attr('src'); - if (typeof self.iconCache[src] !== 'undefined') { + if (typeof _this.iconCache[src] !== 'undefined') { return; } var cache = new Image(); cache.src = src - self.iconCache[src] = cache; + _this.iconCache[src] = cache; }); }, @@ -422,7 +422,7 @@ * Handle successful XHR response */ onResponse: function (data, textStatus, req) { - var self = this; + var _this = this; if (this.failureNotice !== null) { if (! this.failureNotice.hasClass('fading-out')) { this.failureNotice.remove(); @@ -540,7 +540,7 @@ var title = $('h1', $el).first(); $('h1', targets[i]).first().replaceWith(title); - self.loadUrl(url, targets[i]); + _this.loadUrl(url, targets[i]); i++; }); rendered = true; @@ -569,7 +569,7 @@ if (newBody) { this.icinga.ui.fixDebugVisibility().triggerWindowResize(); } - self.cacheLoadedIcons(req.$target); + _this.cacheLoadedIcons(req.$target); }, /** @@ -735,7 +735,7 @@ renderContentToContainer: function (content, $container, action, autorefresh, forceFocus) { // Container update happens here var scrollPos = false; - var self = this; + var _this = this; var containerId = $container.attr('id'); var activeElementPath = false; @@ -772,7 +772,7 @@ $container.trigger('beforerender'); var discard = false; - $.each(self.icinga.behaviors, function(name, behavior) { + $.each(_this.icinga.behaviors, function(name, behavior) { if (behavior.renderHook) { var changed = behavior.renderHook(content, $container, action, autorefresh); if (!changed) { @@ -798,7 +798,7 @@ // }); $('.container', $container).each(function() { - self.stopPendingRequestsFor($(this)); + _this.stopPendingRequestsFor($(this)); }); if (false && From 4f52beb32a125c65780a043e3c3b2ff1e647d9ca Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Sep 2016 16:30:01 +0200 Subject: [PATCH 090/337] Avoid local variable name `self' in module.js refs #10703 --- public/js/icinga/module.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/js/icinga/module.js b/public/js/icinga/module.js index 83700eb39..2c2368e5b 100644 --- a/public/js/icinga/module.js +++ b/public/js/icinga/module.js @@ -80,16 +80,16 @@ }, applyHandlers: function () { - var self = this; + var _this = this; $.each(this.registeredHandlers, function (key, on) { - self.bindEventHandler( + _this.bindEventHandler( on.event, on.filter, on.handler ); }); - self = null; + _this = null; return this; }, @@ -98,10 +98,10 @@ * Effectively bind the given event handler */ bindEventHandler: function (event, filter, handler) { - var self = this; + var _this = this; this.icinga.logger.debug('Bound ' + filter + ' .' + event + '()'); this.handlers.push([event, filter, handler]); - $(document).on(event, filter, handler.bind(self.object)); + $(document).on(event, filter, handler.bind(_this.object)); }, /** From cf5e26c56c15f4e9bb1b79082c8591e4c2941769 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 1 Sep 2016 16:32:31 +0200 Subject: [PATCH 091/337] Avoid local variable name `self' in ui.js refs #10703 --- public/js/icinga/ui.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/public/js/icinga/ui.js b/public/js/icinga/ui.js index af60a3ce9..feaababd7 100644 --- a/public/js/icinga/ui.js +++ b/public/js/icinga/ui.js @@ -211,16 +211,16 @@ * Our window got resized, let's fix our UI */ onWindowResize: function (event) { - var self = event.data.self; + var _this = event.data.self; - if (self.layoutHasBeenChanged()) { - self.icinga.logger.info( + if (_this.layoutHasBeenChanged()) { + _this.icinga.logger.info( 'Layout change detected, switching to', - self.currentLayout + _this.currentLayout ); } - self.fixControls(); - self.refreshDebug(); + _this.fixControls(); + _this.refreshDebug(); }, /** @@ -458,7 +458,6 @@ * Initialize all TriStateCheckboxes in the given html */ initializeTriStates: function ($html) { - var self = this; $('div.tristate', $html).each(function(index, item) { var $target = $(item); From c09c96677c293ab5fcacc2fdc1ac1a20cfa03dc7 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 5 Sep 2016 11:00:53 +0200 Subject: [PATCH 092/337] Update 02-Installation.md --- doc/02-Installation.md | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/doc/02-Installation.md b/doc/02-Installation.md index 5a3b5dc97..5ce49f7ac 100644 --- a/doc/02-Installation.md +++ b/doc/02-Installation.md @@ -37,7 +37,7 @@ Below is a list of official package repositories for installing Icinga Web 2 for | Distribution | Repository | | ------------- | ---------- | -| Debian | [debmon](http://debmon.org/packages/debmon-jessie/icingaweb2), [Icinga Repository](http://packages.icinga.org/debian/) | +| Debian | [Icinga Repository](http://packages.icinga.org/debian/) | | Ubuntu | [Icinga Repository](http://packages.icinga.org/ubuntu/) | | RHEL/CentOS | [Icinga Repository](http://packages.icinga.org/epel/) | | openSUSE | [Icinga Repository](http://packages.icinga.org/openSUSE/) | @@ -52,23 +52,29 @@ Please contact your distribution packagers. ### Setting up Package Repositories You need to add the Icinga repository to your package management configuration for installing Icinga Web 2. -Below is a list with examples for various distributions. +If you've already configured your OS to use the Icinga repository for installing Icinga 2, you may skip this step. +Below is a list with **examples** for various distributions. -**Debian (debmon)**: -``` -wget -O - http://debmon.org/debmon/repo.key 2>/dev/null | apt-key add - -echo 'deb http://debmon.org/debmon debmon-jessie main' >/etc/apt/sources.list.d/debmon.list -apt-get update -``` - -**Ubuntu Trusty**: +**Debian Jessie**: ``` wget -O - http://packages.icinga.org/icinga.key | apt-key add - -add-apt-repository 'deb http://packages.icinga.org/ubuntu icinga-trusty main' +echo 'deb http://packages.icinga.org/debian icinga-jessie main' >/etc/apt/sources.list.d/icinga.list apt-get update ``` -For other Ubuntu versions just replace trusty with your distribution\'s code name. +> INFO +> +> For other Debian versions just replace jessie with your distribution's code name. + +**Ubuntu Xenial**: +``` +wget -O - http://packages.icinga.org/icinga.key | apt-key add - +add-apt-repository 'deb http://packages.icinga.org/ubuntu icinga-xenial main' +apt-get update +``` +> INFO +> +> For other Ubuntu versions just replace xenial with your distribution's code name. **RHEL and CentOS**: ``` From a5da4afb5cf008e1e6b2e97a2095b26bfec93e36 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 5 Sep 2016 11:26:19 +0200 Subject: [PATCH 093/337] doc: Mention FreeBSD and Gentoo packages --- doc/02-Installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/02-Installation.md b/doc/02-Installation.md index 5ce49f7ac..0f81c6e92 100644 --- a/doc/02-Installation.md +++ b/doc/02-Installation.md @@ -42,8 +42,8 @@ Below is a list of official package repositories for installing Icinga Web 2 for | RHEL/CentOS | [Icinga Repository](http://packages.icinga.org/epel/) | | openSUSE | [Icinga Repository](http://packages.icinga.org/openSUSE/) | | SLES | [Icinga Repository](http://packages.icinga.org/SUSE/) | -| Gentoo | - | -| FreeBSD | - | +| Gentoo | [Upstream](https://packages.gentoo.org/packages/www-apps/icingaweb2) | +| FreeBSD | [Upstream](http://portsmon.freebsd.org/portoverview.py?category=net-mgmt&portname=icingaweb2) | | ArchLinux | [Upstream](https://aur.archlinux.org/packages/icingaweb2) | Packages for distributions other than the ones listed above may also be available. From 9402c1ffa60e5296c7a95571044569e5113f917e Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 8 Sep 2016 09:23:49 +0200 Subject: [PATCH 094/337] Add debug info to commands sent over Icinga 2's API --- .../Monitoring/Web/Rest/RestRequest.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php b/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php index 7506f6418..2cf25ea0a 100644 --- a/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php +++ b/modules/monitoring/library/Monitoring/Web/Rest/RestRequest.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Monitoring\Web\Rest; use Exception; +use Icinga\Application\Logger; /** * REST Request @@ -206,27 +207,46 @@ class RestRequest CURLOPT_RETURNTRANSFER => true ); + // Record cURL command line for debugging + $curlCmd = array('curl', '-s', '-X', $this->method, '-H', escapeshellarg('Accept: application/json')); + if ($this->strictSsl) { $options[CURLOPT_SSL_VERIFYHOST] = 2; $options[CURLOPT_SSL_VERIFYPEER] = true; } else { $options[CURLOPT_SSL_VERIFYHOST] = false; $options[CURLOPT_SSL_VERIFYPEER] = false; + $curlCmd[] = '-k'; } if ($this->hasBasicAuth) { $options[CURLOPT_USERPWD] = sprintf('%s:%s', $this->username, $this->password); + $curlCmd[] = sprintf('-u %s:%s', escapeshellarg($this->username), escapeshellarg($this->password)); } if (! empty($this->payload)) { $payload = $this->serializePayload($this->payload, $this->contentType); $options[CURLOPT_POSTFIELDS] = $payload; + $curlCmd[] = sprintf('-d %s', escapeshellarg($payload)); } $options[CURLOPT_HTTPHEADER] = $headers; + $stream = null; + if (Logger::getInstance()->getLevel() === Logger::DEBUG) { + $stream = fopen('php://temp', 'w'); + $options[CURLOPT_VERBOSE] = true; + $options[CURLOPT_STDERR] = $stream; + } + curl_setopt_array($ch, $options); + Logger::debug( + 'Executing %s %s', + implode(' ', $curlCmd), + escapeshellarg($this->uri) + ); + $result = curl_exec($ch); if ($result === false) { @@ -235,6 +255,12 @@ class RestRequest curl_close($ch); + if (is_resource($stream)) { + rewind($stream); + Logger::debug(stream_get_contents($stream)); + fclose($stream); + } + $response = @json_decode($result, true); if ($response === null) { From 01bee4641d82c0a1b2fc699876c2932da72f233e Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 8 Sep 2016 15:03:45 +0200 Subject: [PATCH 095/337] Fix missing comment and downtime name in the object detail views --- .../Monitoring/Object/MonitoredObject.php | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php index 437268eb8..3214769e6 100644 --- a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php +++ b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php @@ -332,6 +332,7 @@ abstract class MonitoredObject implements Filterable 'comment' => 'comment_data', 'expiration' => 'comment_expiration', 'id' => 'comment_internal_id', + 'name' => 'comment_name', 'persistent' => 'comment_is_persistent', 'timestamp' => 'comment_timestamp', 'type' => 'comment_type' @@ -584,19 +585,20 @@ abstract class MonitoredObject implements Filterable public function fetchDowntimes() { $downtimes = $this->backend->select()->from('downtime', array( - 'id' => 'downtime_internal_id', - 'objecttype' => 'object_type', - 'comment' => 'downtime_comment', 'author_name' => 'downtime_author_name', - 'start' => 'downtime_start', - 'scheduled_start' => 'downtime_scheduled_start', - 'scheduled_end' => 'downtime_scheduled_end', - 'end' => 'downtime_end', + 'comment' => 'downtime_comment', 'duration' => 'downtime_duration', - 'is_flexible' => 'downtime_is_flexible', + 'end' => 'downtime_end', + 'entry_time' => 'downtime_entry_time', + 'id' => 'downtime_internal_id', 'is_fixed' => 'downtime_is_fixed', + 'is_flexible' => 'downtime_is_flexible', 'is_in_effect' => 'downtime_is_in_effect', - 'entry_time' => 'downtime_entry_time' + 'name' => 'downtime_name', + 'objecttype' => 'object_type', + 'scheduled_end' => 'downtime_scheduled_end', + 'scheduled_start' => 'downtime_scheduled_start', + 'start' => 'downtime_start' )) ->where('object_type', $this->type) ->order('downtime_is_in_effect', 'DESC') From 067de6b7f46c8d186d103bf803507debb63d9ce8 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 8 Sep 2016 15:15:53 +0200 Subject: [PATCH 096/337] Set comment and downtime name in the detail views --- .../application/views/scripts/show/components/comments.phtml | 3 ++- .../application/views/scripts/show/components/downtime.phtml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/views/scripts/show/components/comments.phtml b/modules/monitoring/application/views/scripts/show/components/comments.phtml index 671c363d1..3d5904ef4 100644 --- a/modules/monitoring/application/views/scripts/show/components/comments.phtml +++ b/modules/monitoring/application/views/scripts/show/components/comments.phtml @@ -60,7 +60,8 @@ if (empty($object->comments) && ! $addLink) { $deleteButton->populate( array( 'comment_id' => $comment->id, - 'comment_is_service' => isset($comment->service_description) + 'comment_is_service' => isset($comment->service_description), + 'comment_name' => $comment->name ) ); echo $deleteButton; diff --git a/modules/monitoring/application/views/scripts/show/components/downtime.phtml b/modules/monitoring/application/views/scripts/show/components/downtime.phtml index 5655da4c4..ded3b9248 100644 --- a/modules/monitoring/application/views/scripts/show/components/downtime.phtml +++ b/modules/monitoring/application/views/scripts/show/components/downtime.phtml @@ -89,7 +89,8 @@ if (empty($object->comments) && ! $addLink) { $deleteButton->populate( array( 'downtime_id' => $downtime->id, - 'downtime_is_service' => $object->getType() === $object::TYPE_SERVICE + 'downtime_is_service' => $object->getType() === $object::TYPE_SERVICE, + 'downtime_name' => $downtime->name ) ); echo $deleteButton; From 33506b29652e71a5008c53f14d02e9dde761883f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 8 Sep 2016 15:49:39 +0200 Subject: [PATCH 097/337] Fix send custom notification command form if backend is Icinga 2 --- .../Command/Object/SendCustomNotificationCommandForm.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php b/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php index 06e1b35ee..0d04477f1 100644 --- a/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php @@ -91,8 +91,10 @@ class SendCustomNotificationCommandForm extends ObjectsCommandForm ->setObject($object) ->setComment($this->getElement('comment')->getValue()) ->setAuthor($this->request->getUser()->getUsername()) - ->setForced($this->getElement('forced')->isChecked()) - ->setBroadcast($this->getElement('broadcast')->isChecked()); + ->setForced($this->getElement('forced')->isChecked()); + if (($broadcast = $this->getElement('broadcast')) !== null) { + $notification->setBroadcast($broadcast->isChecked()); + } $this->getTransport($this->request)->send($notification); } Notification::success($this->translatePlural( From fc086adf21c4b878eb8ea449c8f87ee52c87e2dd Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 8 Sep 2016 16:10:01 +0200 Subject: [PATCH 098/337] Document how to use the Icinga 2 API for transmitting external commands --- modules/monitoring/doc/commandtransports.md | 76 ++++++++++++++------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/modules/monitoring/doc/commandtransports.md b/modules/monitoring/doc/commandtransports.md index 238643f5c..ba9a3be54 100644 --- a/modules/monitoring/doc/commandtransports.md +++ b/modules/monitoring/doc/commandtransports.md @@ -1,24 +1,56 @@ -# The commandtransports.ini configuration file +# External Command Transport Configuration -## Abstract +## Introduction -The commandtransports.ini defines how Icinga Web 2 accesses the command pipe of -your Icinga instance in order to submit external commands. Depending on the -config path (default: /etc/icingaweb2) of your Icinga Web 2 installation you can -find it under ./modules/monitoring/commandtransports.ini. +The `commandtransports.ini` defines how Icinga Web 2 transports commands to your Icinga instance in order to submit +external commands. By default, this file is located at `/etc/icingaweb2/modules/monitoring/commandtransports.ini`. -## Syntax +You can define multiple command transports in the `commandtransports.ini`. Every transport starts with a section header +containing its name, followed by the config directives for this transport in the standard INI-format. -You can define multiple command transports in the commandtransports.ini. Every -transport starts with a section header containing its name, followed by the -config directives for this transport in the standard INI-format. +Icinga Web 2 will try one transport after another to send a command until the command is successfully sent. +If [configured](#commandtransports-multiple-instances), Icinga Web 2 will take different instances into account. +The order in which Icinga Web 2 processes the configured transports is defined by the order of sections in +`commandtransports.ini`. -Icinga Web 2 will try one transport after another to send a command, depending -on the respective Icinga instance, until the command is successfully sent. The -order in which Icinga Web 2 processes the configured transports is defined by -the order of sections in the commandtransports.ini. +## Use the Icinga 2 API -## Using a local command pipe +If you're running Icinga 2 it's best to use the Icinga 2 API for transmitting external commands. + +First, you have to enable the `api` feature on the Icinga 2 host where you want to send the commands to: + +``` +icinga2 feature enable api +``` + +Next, you have to create an ApiUser object for authenticating against the Icinga 2 API. This configuration also applies +to the host where you want to send the commands to. We recommend to create/edit the file +`/etc/icinga2/conf.d/api-users.conf`: + +``` +object ApiUser "web2" { + password = "bea11beb7b810ea9ce6ea" // Change this! + permissions = [ "actions/*", "objects/modify/hosts", "objects/modify/services", "objects/modify/icingaapplications" ] +} +``` + +The permissions `actions/*`, `objects/modify/hosts`, `objects/modify/services`, `objects/modify/icingaapplications` are +mandatory in order to submit all external commands from within Icinga Web 2. + +**Restart Icinga 2** for the changes to take effect. + +After that, you have to set up Icinga Web 2's `commandtransport.ini` to use the Icinga 2 API: + +``` +[icinga2] +transport = "api" +host = "127.0.0.1" // Icinga 2 host +port = "5665" +username = "web2" +password = "bea11beb7b810ea9ce6ea" // Change that! +``` + +## Use a Local Command Pipe A local Icinga instance requires the following directives: @@ -31,7 +63,7 @@ path = /var/run/icinga2/cmd/icinga2.cmd When sending commands to the Icinga instance, Icinga Web 2 opens the file found on the local filesystem underneath 'path' and writes the external command to it. -## Using SSH for accessing a remote command pipe +## Use SSH For a Remote Command Pipe A command pipe on a remote host's filesystem can be accessed by configuring a SSH based command transport and requires the following directives: @@ -74,11 +106,10 @@ user = icinga private_key = /etc/icingaweb2/ssh/icinga ``` -## Configuring transports for different Icinga instances +## Configure Transports for Different Icinga Instances -If there are multiple but different Icinga instances writing to your IDO you can -define which transport belongs to which Icinga instance by providing the -directive 'instance'. This directive should contain the name of the Icinga +If there are multiple but different Icinga instances writing to your IDO, you can define which transport belongs to +which Icinga instance by providing the directive `instance`. This directive should contain the name of the Icinga instance you want to assign to the transport: ``` @@ -91,6 +122,5 @@ instance = icinga1 instance = icinga2 ``` -Associating a transport to a specific Icinga instance causes this transport to -be used to send commands to the linked instance only. Transports without a -linked Icinga instance are utilized to send commands to all instances. +Associating a transport to a specific Icinga instance causes this transport to be used to send commands to the linked +instance only. Transports without a linked Icinga instance are used to send commands to all instances. From 7e47a2965ce7ed1a26d3273b202aec2bcd0a65b5 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 9 Sep 2016 13:18:00 +0200 Subject: [PATCH 099/337] JS: Fix usage of global variable self used to reference icinga in loader.js::onComplete() refs #10703 --- public/js/icinga/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index d4b84cbb1..73df66c79 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -586,7 +586,7 @@ var url = req.url; if (req.$target[0].id === 'col1') { - self.icinga.behaviors.navigation.trySetActiveByUrl(url); + this.icinga.behaviors.navigation.trySetActiveByUrl(url); } var $forms = $('[action="' + this.icinga.utils.parseUrl(url).path + '"]'); From 118c435bc58086680ead7d1f569e14db13b88450 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 9 Sep 2016 13:20:55 +0200 Subject: [PATCH 100/337] JS: Fix usage of global variable self used to reference icinga in events.js refs #10703 --- public/js/icinga/events.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 85dbdd87d..340d652b1 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -105,7 +105,7 @@ // 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) { + $.each(this.icinga.behaviors, function (name, behavior) { behavior.bind($(document)); }); @@ -577,11 +577,11 @@ } else if (targetId === '_main') { targetId = 'col1'; $target = $('#' + targetId); - self.icinga.ui.layout1col(); + this.icinga.ui.layout1col(); } else { $target = $('#' + targetId); if (! $target.length) { - self.icinga.logger.warn('Link target "#' + targetId + '" does not exist in DOM.'); + this.icinga.logger.warn('Link target "#' + targetId + '" does not exist in DOM.'); } } @@ -596,7 +596,7 @@ }, unbindGlobalHandlers: function () { - $.each(self.icinga.behaviors, function (name, behavior) { + $.each(this.icinga.behaviors, function (name, behavior) { behavior.unbind($(document)); }); $(window).off('resize', this.onWindowResize); From 576640e661f205f673662af68b24033df8a91e78 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 9 Sep 2016 14:19:32 +0200 Subject: [PATCH 101/337] Simplify Version::getGitHead() refs #11664 --- library/Icinga/Application/Version.php | 32 ++++++++++---------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/library/Icinga/Application/Version.php b/library/Icinga/Application/Version.php index 0565c3bf5..264fe5065 100644 --- a/library/Icinga/Application/Version.php +++ b/library/Icinga/Application/Version.php @@ -34,35 +34,27 @@ class Version } /** - * Get the hexadecimal ID of the HEAD commit of the Git repository in $repoDir + * Get the current commit of the Git repository in the given path * - * @param string $repoDir - * @param bool $bare Whether the repository has been created with - * $ git init|clone --bare + * @param string $repo Path to the Git repository + * @param bool $bare Whether the Git repository is bare * - * @return string|bool false if not available + * @return string|bool False if not available */ - public static function getGitHead($repoDir, $bare = false) + public static function getGitHead($repo, $bare = false) { if (! $bare) { - $repoDir .= DIRECTORY_SEPARATOR . '.git'; + $repo .= '/.git'; } - $gitHead = @ file_get_contents($repoDir . DIRECTORY_SEPARATOR . 'HEAD'); - if ($gitHead !== false) { - $matches = array(); - if (preg_match('/(? Date: Fri, 9 Sep 2016 15:20:45 +0200 Subject: [PATCH 102/337] Optimize imports in DashletForm refs #11920 --- application/forms/Dashboard/DashletForm.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/forms/Dashboard/DashletForm.php b/application/forms/Dashboard/DashletForm.php index 07d5c327e..9b23d9517 100644 --- a/application/forms/Dashboard/DashletForm.php +++ b/application/forms/Dashboard/DashletForm.php @@ -3,11 +3,11 @@ namespace Icinga\Forms\Dashboard; -use Icinga\Web\Form\Validator\InternalUrlValidator; -use Icinga\Web\Widget\Dashboard; use Icinga\Web\Form; +use Icinga\Web\Form\Validator\InternalUrlValidator; use Icinga\Web\Form\Validator\UrlValidator; use Icinga\Web\Url; +use Icinga\Web\Widget\Dashboard; use Icinga\Web\Widget\Dashboard\Dashlet; /** From 1f980f92f2bafc36ba7817ec7a2459839a8b1157 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 9 Sep 2016 15:21:06 +0200 Subject: [PATCH 103/337] Optimize imports in InternalUrlValidator refs #11920 --- library/Icinga/Web/Form/Validator/InternalUrlValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Form/Validator/InternalUrlValidator.php b/library/Icinga/Web/Form/Validator/InternalUrlValidator.php index a0230c782..8876f562b 100644 --- a/library/Icinga/Web/Form/Validator/InternalUrlValidator.php +++ b/library/Icinga/Web/Form/Validator/InternalUrlValidator.php @@ -3,8 +3,8 @@ namespace Icinga\Web\Form\Validator; -use Icinga\Web\Url; use Zend_Validate_Abstract; +use Icinga\Web\Url; /** * Validator that checks whether a textfield doesn't contain an external URL From ef7be98e0c29eeeb4b35933adb8e98edae0c8c66 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 9 Sep 2016 15:22:24 +0200 Subject: [PATCH 104/337] Simplify InternalUrlValidator::isValid() refs #11920 --- .../Icinga/Web/Form/Validator/InternalUrlValidator.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Web/Form/Validator/InternalUrlValidator.php b/library/Icinga/Web/Form/Validator/InternalUrlValidator.php index 8876f562b..07726824a 100644 --- a/library/Icinga/Web/Form/Validator/InternalUrlValidator.php +++ b/library/Icinga/Web/Form/Validator/InternalUrlValidator.php @@ -16,11 +16,13 @@ class InternalUrlValidator extends Zend_Validate_Abstract */ public function isValid($value) { - $isExternal = Url::fromPath($value)->isExternal(); - if ($isExternal) { + if (Url::fromPath($value)->isExternal()) { $this->_error('IS_EXTERNAL'); + + return false; } - return ! $isExternal; + + return true; } /** From c8b1693fdc20309e1e6302673e8f2a5fdf5edc47 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 31 Aug 2016 13:19:30 +0200 Subject: [PATCH 105/337] Fix Controller::assertPermission() allowing everything for unauthenticated requests fixes #12108 --- library/Icinga/Web/Controller/ActionController.php | 2 +- library/Icinga/Web/Controller/ModuleActionController.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Controller/ActionController.php b/library/Icinga/Web/Controller/ActionController.php index 0703e4e98..655a85aca 100644 --- a/library/Icinga/Web/Controller/ActionController.php +++ b/library/Icinga/Web/Controller/ActionController.php @@ -179,7 +179,7 @@ class ActionController extends Zend_Controller_Action */ public function assertPermission($permission) { - if ($this->requiresAuthentication && ! $this->Auth()->hasPermission($permission)) { + if (! $this->Auth()->hasPermission($permission)) { throw new SecurityException('No permission for %s', $permission); } } diff --git a/library/Icinga/Web/Controller/ModuleActionController.php b/library/Icinga/Web/Controller/ModuleActionController.php index 1ae32e10c..38826a84c 100644 --- a/library/Icinga/Web/Controller/ModuleActionController.php +++ b/library/Icinga/Web/Controller/ModuleActionController.php @@ -26,7 +26,8 @@ class ModuleActionController extends ActionController protected function prepareInit() { $this->moduleInit(); - if ($this->getFrontController()->getDefaultModule() !== $this->getModuleName()) { + if ($this->requiresLogin() + && $this->getFrontController()->getDefaultModule() !== $this->getModuleName()) { $this->assertPermission(Manager::MODULE_PERMISSION_NS . $this->getModuleName()); } } From 1ed2ebc1916d6d83a62cf649de0f5b981c27fc4b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 12 Sep 2016 15:52:07 +0200 Subject: [PATCH 106/337] IDO: Add config option to use the customvariables table for fetching custom vars Icinga 1.x has the option to not dump the icinga_customvariablestatus table: dump_customvar_status=0 With this setting applied, Web 2 will never show custom variables because Web 2 relies on the customvariablestatus table. This commit introduces a config option to use the customvariables table instead: /etc/icingaweb2/modules/monitoring/config.ini [ido] use_customvar_status_table = 0 --- .../Monitoring/Backend/Ido/Query/CustomvarQuery.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/CustomvarQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CustomvarQuery.php index 9ae95b204..c97cc916a 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/CustomvarQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CustomvarQuery.php @@ -3,6 +3,8 @@ namespace Icinga\Module\Monitoring\Backend\Ido\Query; +use Icinga\Application\Config; + class CustomvarQuery extends IdoQuery { protected $columnMap = array( @@ -44,8 +46,14 @@ class CustomvarQuery extends IdoQuery $this->columnMap['customvariablestatus']['is_json'] = '(0)'; } + if (! (bool) Config::module('monitoring')->get('ido', 'use_customvar_status_table', true)) { + $table = 'customvariables'; + } else { + $table = 'customvariablestatus'; + } + $this->select->from( - array('cvs' => $this->prefix . 'customvariablestatus'), + array('cvs' => $this->prefix . $table), array() )->join( array('cvo' => $this->prefix . 'objects'), From e0d59ded2d5ec05380237a5b7cb10105d1132f1b Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 13 Sep 2016 13:55:47 +0200 Subject: [PATCH 107/337] vagrant: Add missing icinga2 constants fixes #12717 --- .puppet/profiles/icinga2_dev/files/constants.conf | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.puppet/profiles/icinga2_dev/files/constants.conf b/.puppet/profiles/icinga2_dev/files/constants.conf index 9e6ed51af..f5425b062 100644 --- a/.puppet/profiles/icinga2_dev/files/constants.conf +++ b/.puppet/profiles/icinga2_dev/files/constants.conf @@ -6,6 +6,16 @@ /* The directory which contains the plugins from the Monitoring Plugins project. */ const PluginDir = "/usr/lib64/nagios/plugins" +/* The directory which contains the Manubulon plugins. + * Check the documentation, chapter "SNMP Manubulon Plugin Check Commands", for details. + */ +const ManubulonPluginDir = "/usr/lib64/nagios/plugins" + +/* The directory which you use to store additional plugins which ITL provides user contributed command definitions for. + * Check the documentation, chapter "Plugins Contribution", for details. + */ +const PluginContribDir = "/usr/lib64/nagios/plugins" + /* Our local instance name. By default this is the server's hostname as returned by `hostname --fqdn`. * This should be the common name from the API certificate. */ From fd6ae75803bc9b0499415c0889744cb9ff9a2342 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 14 Sep 2016 16:18:08 +0200 Subject: [PATCH 108/337] IDO: Improve notification view performance --- .../controllers/ListController.php | 12 +- .../views/scripts/list/notifications.phtml | 2 +- modules/monitoring/configuration.php | 2 +- .../Backend/Ido/Query/EventhistoryQuery.php | 3 +- .../Ido/Query/HostnotificationQuery.php | 95 +++------ .../Backend/Ido/Query/NotificationQuery.php | 99 ++------- .../Ido/Query/NotificationhistoryQuery.php | 192 +++++++++++------- .../Ido/Query/ServicenotificationQuery.php | 95 +++------ .../Monitoring/DataView/Notification.php | 41 +--- 9 files changed, 206 insertions(+), 335 deletions(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 5499cfdf9..787a184f5 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -277,13 +277,13 @@ class ListController extends Controller $this->setAutorefreshInterval(15); $notifications = $this->backend->select()->from('notification', array( - 'host_name', - 'service_description', - 'notification_output', - 'notification_contact_name', - 'notification_start_time', - 'notification_state', 'host_display_name', + 'host_name', + 'notification_contact_name', + 'notification_output', + 'notification_state', + 'notification_timestamp', + 'service_description', 'service_display_name' )); $this->applyRestriction('monitoring/filter/objects', $notifications); diff --git a/modules/monitoring/application/views/scripts/list/notifications.phtml b/modules/monitoring/application/views/scripts/list/notifications.phtml index 8316665d1..dc34a8c88 100644 --- a/modules/monitoring/application/views/scripts/list/notifications.phtml +++ b/modules/monitoring/application/views/scripts/list/notifications.phtml @@ -35,7 +35,7 @@ if (! $this->compact): ?>
- timeAgo($notification->notification_start_time, $this->compact) ?> + timeAgo($notification->notification_timestamp, $this->compact) ?>
diff --git a/modules/monitoring/configuration.php b/modules/monitoring/configuration.php index 01a6380ad..c2b385c35 100644 --- a/modules/monitoring/configuration.php +++ b/modules/monitoring/configuration.php @@ -223,7 +223,7 @@ $section->add(N_('Event Overview'), array( )); $section->add(N_('Notifications'), array( 'priority' => 30, - 'url' => 'monitoring/list/notifications', + 'url' => 'monitoring/list/notifications?notification_timestamp>=-7%20days', )); $section->add(N_('Timeline'), array( 'priority' => 40, diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/EventhistoryQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/EventhistoryQuery.php index 542a45b9d..c8ee0b8a3 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/EventhistoryQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/EventhistoryQuery.php @@ -56,7 +56,6 @@ class EventhistoryQuery extends IdoQuery 'type', 'state', 'object_type', - 'object_id', 'host_name', 'service_description', 'host_display_name', @@ -68,7 +67,7 @@ class EventhistoryQuery extends IdoQuery $this->createSubQuery('Downtimeendhistory', $columns), $this->createSubQuery('Commenthistory', $columns), $this->createSubQuery('Commentdeletionhistory', $columns), - $this->createSubQuery('Notification', $columns) + $this->createSubQuery('Notificationhistory', $columns) ); $sub = $this->db->select()->union($this->subQueries, Zend_Db_Select::SQL_UNION_ALL); $this->select->from(array('eh' => $sub), array()); diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostnotificationQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostnotificationQuery.php index 0ee815df8..de4d37807 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostnotificationQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostnotificationQuery.php @@ -17,51 +17,34 @@ class HostnotificationQuery extends IdoQuery * {@inheritdoc} */ protected $columnMap = array( + 'contactnotifications' => array( + 'notification_contact_name' => 'co.name1' + ), + 'hostgroups' => array( + 'hostgroup_name' => 'hgo.name1' + ), + 'hosts' => array( + 'host_display_name' => 'h.display_name COLLATE latin1_general_ci' + ), + 'history' => array( + 'output' => null, + 'state' => 'hn.state', + 'timestamp' => 'UNIX_TIMESTAMP(hn.start_time)', + ), 'instances' => array( 'instance_name' => 'i.instance_name' ), 'notifications' => array( - 'notification_output' => 'hn.output', - 'notification_start_time' => 'UNIX_TIMESTAMP(hn.start_time)', - 'notification_state' => 'hn.state', - 'notification_object_id' => 'hn.object_id', - 'host' => 'ho.name1 COLLATE latin1_general_ci', 'host_name' => 'ho.name1', + 'notification_output' => 'hn.output', + 'notification_state' => 'hn.state', + 'notification_timestamp' => 'UNIX_TIMESTAMP(hn.start_time)', 'object_type' => '(\'host\')' ), - 'history' => array( - 'type' => "('notify')", - 'timestamp' => 'UNIX_TIMESTAMP(hn.start_time)', - 'object_id' => 'hn.object_id', - 'state' => 'hn.state', - 'output' => null - ), - 'contactnotifications' => array( - 'contact' => 'cno.name1 COLLATE latin1_general_ci', - 'notification_contact_name' => 'cno.name1', - 'contact_object_id' => 'cno.object_id' - ), - 'acknowledgements' => array( - 'acknowledgement_entry_time' => 'UNIX_TIMESTAMP(a.entry_time)', - 'acknowledgement_author_name' => 'a.author_name', - 'acknowledgement_comment_data' => 'a.comment_data' - ), - 'hostgroups' => array( - 'hostgroup' => 'hgo.name1 COLLATE latin1_general_ci', - 'hostgroup_alias' => 'hg.alias COLLATE latin1_general_ci', - 'hostgroup_name' => 'hgo.name1' - ), - 'hosts' => array( - 'host_alias' => 'h.alias', - 'host_display_name' => 'h.display_name COLLATE latin1_general_ci' - ), 'servicegroups' => array( - 'servicegroup' => 'sgo.name1 COLLATE latin1_general_ci', - 'servicegroup_name' => 'sgo.name1', - 'servicegroup_alias' => 'sg.alias COLLATE latin1_general_ci' + 'servicegroup_name' => 'sgo.name1' ), 'services' => array( - 'service' => 'so.name2 COLLATE latin1_general_ci', 'service_description' => 'so.name2', 'service_display_name' => 's.display_name COLLATE latin1_general_ci', 'service_host_name' => 'so.name1' @@ -88,26 +71,14 @@ class HostnotificationQuery extends IdoQuery switch ($this->ds->getDbType()) { case 'mysql': $concattedContacts = "GROUP_CONCAT(" - . "DISTINCT cno.name1 ORDER BY cno.name1 SEPARATOR ', '" + . "DISTINCT co.name1 ORDER BY co.name1 SEPARATOR ', '" . ") COLLATE latin1_general_ci"; break; case 'pgsql': // TODO: Find a way to order the contact alias list: - $concattedContacts = "ARRAY_TO_STRING(ARRAY_AGG(DISTINCT cno.name1), ', ')"; - break; - case 'oracle': - // TODO: This is only valid for Oracle >= 11g Release 2 - $concattedContacts = "LISTAGG(cno.name1, ', ') WITHIN GROUP (ORDER BY cno.name1)"; - // Alternatives: - // - // RTRIM(XMLAGG(XMLELEMENT(e, column_name, ',').EXTRACT('//text()')), - // - // not supported and not documented but works since 10.1, - // however it is NOT always present: - // WM_CONCAT(c.alias) + $concattedContacts = "ARRAY_TO_STRING(ARRAY_AGG(DISTINCT co.name1), ', ')"; break; } - $this->columnMap['history']['output'] = "('[' || $concattedContacts || '] ' || hn.output)"; $this->select->from( @@ -140,20 +111,8 @@ class HostnotificationQuery extends IdoQuery array() ); $this->select->joinLeft( - array('cno' => $this->prefix . 'objects'), - 'cno.object_id = cn.contact_object_id', - array() - ); - } - - /** - * Join acknowledgements - */ - protected function joinAcknowledgements() - { - $this->select->joinLeft( - array('a' => $this->prefix . 'acknowledgements'), - 'a.object_id = hn.object_id', + array('co' => $this->prefix . 'objects'), + 'co.object_id = cn.contact_object_id AND co.is_active = 1 AND co.objecttype_id = 10', array() ); } @@ -244,6 +203,8 @@ class HostnotificationQuery extends IdoQuery */ public function getGroup() { + $group = array(); + if ( $this->hasJoinedVirtualTable('history') || $this->hasJoinedVirtualTable('services') @@ -251,10 +212,10 @@ class HostnotificationQuery extends IdoQuery ) { $group = array('hn.notification_id', 'ho.object_id'); if ($this->hasJoinedVirtualTable('contactnotifications') && !$this->hasJoinedVirtualTable('history')) { - $group[] = 'cno.object_id'; + $group[] = 'co.object_id'; } } elseif ($this->hasJoinedVirtualTable('contactnotifications')) { - $group = array('hn.notification_id', 'cno.object_id', 'ho.object_id'); + $group = array('hn.notification_id', 'co.object_id', 'ho.object_id'); } if (! empty($group)) { @@ -262,10 +223,6 @@ class HostnotificationQuery extends IdoQuery $group[] = 'h.host_id'; } - if ($this->hasJoinedVirtualTable('acknowledgements')) { - $group[] = 'a.acknowledgement_id'; - } - if ($this->hasJoinedVirtualTable('instances')) { $group[] = 'i.instance_id'; } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php index 9950fdbcd..c9515140b 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php @@ -12,29 +12,22 @@ use Icinga\Data\Filter\Filter; */ class NotificationQuery extends IdoQuery { + /** + * {@inheritdoc} + */ + protected $allowCustomVars = true; + /** * {@inheritdoc} */ protected $columnMap = array( 'notifications' => array( - 'notification_state' => 'n.notification_state', - 'notification_start_time' => 'n.notification_start_time', + 'instance_name' => 'n.instance_name', 'notification_contact_name' => 'n.notification_contact_name', - 'notification_output' => 'n.notification_output', 'notification_object_id' => 'n.notification_object_id', - 'contact_object_id' => 'n.contact_object_id', - 'acknowledgement_entry_time' => 'n.acknowledgement_entry_time', - 'acknowledgement_author_name' => 'n.acknowledgement_author_name', - 'acknowledgement_comment_data' => 'n.acknowledgement_comment_data', - 'object_type' => 'n.object_type', - 'instance_name' => 'n.instance_name' - ), - 'history' => array( - 'type' => 'n.type', - 'timestamp' => 'n.timestamp', - 'object_id' => 'n.object_id', - 'state' => 'n.state', - 'output' => 'n.output' + 'notification_output' => 'n.notification_output', + 'notification_state' => 'n.notification_state', + 'notification_timestamp' => 'n.notification_timestamp' ), 'hosts' => array( 'host_display_name' => 'n.host_display_name', @@ -61,13 +54,6 @@ class NotificationQuery extends IdoQuery */ protected $subQueries = array(); - /** - * Whether to additionally select all history columns - * - * @var bool - */ - protected $fetchHistoryColumns = false; - /** * {@inheritdoc} */ @@ -81,33 +67,20 @@ class NotificationQuery extends IdoQuery $this->joinedVirtualTables['notifications'] = true; } - /** - * Join history related columns and tables - */ - protected function joinHistory() - { - // TODO: Ensure that one is selecting the history columns first... - $this->fetchHistoryColumns = true; - $this->requireVirtualTable('hosts'); - $this->requireVirtualTable('services'); - } - /** * Join hosts */ protected function joinHosts() { - $columns = array_keys($this->columnMap['hosts']); + $columns = $this->desiredColumns; + $columns = array_combine($columns, $columns); foreach ($this->columnMap['services'] as $column => $_) { - $columns[$column] = new Zend_Db_Expr('NULL'); - } - if ($this->fetchHistoryColumns) { - $columns = array_merge($columns, array_keys($this->columnMap['history'])); - $columns[] = 'object_type'; - } else { - $columns = array_merge($columns, array_keys($this->columnMap['notifications'])); + if (isset($columns[$column])) { + $columns[$column] = new Zend_Db_Expr('NULL'); + } } $hosts = $this->createSubQuery('hostnotification', $columns); + $hosts->setIsSubQuery(true); $this->subQueries[] = $hosts; $this->notificationQuery->union(array($hosts), Zend_Db_Select::SQL_UNION_ALL); } @@ -117,14 +90,8 @@ class NotificationQuery extends IdoQuery */ protected function joinServices() { - $columns = array_keys($this->columnMap['hosts'] + $this->columnMap['services']); - if ($this->fetchHistoryColumns) { - $columns = array_merge($columns, array_keys($this->columnMap['history'])); - $columns[] = 'object_type'; - } else { - $columns = array_merge($columns, array_keys($this->columnMap['notifications'])); - } - $services = $this->createSubQuery('servicenotification', $columns); + $services = $this->createSubQuery('servicenotification', $this->desiredColumns); + $services->setIsSubQuery(true); $this->subQueries[] = $services; $this->notificationQuery->union(array($services), Zend_Db_Select::SQL_UNION_ALL); } @@ -132,15 +99,12 @@ class NotificationQuery extends IdoQuery /** * {@inheritdoc} */ - public function allowsCustomVars() + public function addFilter(Filter $filter) { - foreach ($this->subQueries as $query) { - if (! $query->allowsCustomVars()) { - return false; - } + foreach ($this->subQueries as $sub) { + $sub->applyFilter(clone $filter); } - - return true; + return $this; } /** @@ -165,25 +129,4 @@ class NotificationQuery extends IdoQuery } return $this; } - - /** - * {@inheritdoc} - */ - public function addFilter(Filter $filter) - { - foreach ($this->subQueries as $sub) { - $sub->applyFilter(clone $filter); - } - return $this; - } - - /** - * {@inheritdoc} - */ - public function columns(array $columns) - { - parent::columns($columns); - $this->requireVirtualTable('hosts'); - $this->requireVirtualTable('services'); - } } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationhistoryQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationhistoryQuery.php index dad75d5e0..fd83e571f 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationhistoryQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationhistoryQuery.php @@ -3,94 +3,134 @@ namespace Icinga\Module\Monitoring\Backend\Ido\Query; +use Zend_Db_Expr; +use Zend_Db_Select; +use Icinga\Data\Filter\Filter; + +/** + * Query for host and service notification history + */ class NotificationhistoryQuery extends IdoQuery { + /** + * {@inheritdoc} + */ + protected $allowCustomVars = true; + + /** + * {@inheritdoc} + */ protected $columnMap = array( 'history' => array( - 'state_time' => 'n.start_time', - 'timestamp' => 'UNIX_TIMESTAMP(n.start_time)', - 'raw_timestamp' => 'n.start_time', - 'object_id' => 'n.object_id', - 'type' => "('notify')", - 'state' => 'n.state', - 'state_type' => '(NULL)', - 'output' => null, - 'attempt' => '(NULL)', - 'max_attempts' => '(NULL)', - - 'host' => 'o.name1 COLLATE latin1_general_ci', - 'service' => 'o.name2 COLLATE latin1_general_ci', - 'host_name' => 'o.name1', - 'service_description' => 'o.name2', - 'object_type' => "CASE WHEN o.objecttype_id = 1 THEN 'host' ELSE 'service' END" + 'object_type' => 'n.object_type', + 'output' => 'n.output', + 'state' => 'n.state', + 'timestamp' => 'n.timestamp', + 'type' => "('notify')" + ), + 'hosts' => array( + 'host_display_name' => 'n.host_display_name', + 'host_name' => 'n.host_name' + ), + 'services' => array( + 'service_description' => 'n.service_description', + 'service_display_name' => 'n.service_display_name', + 'service_host_name' => 'n.service_host_name' ) ); - public function whereToSql($col, $sign, $expression) - { - if ($col === 'UNIX_TIMESTAMP(n.start_time)') { - return 'n.start_time ' . $sign . ' ' . $this->timestampForSql($this->valueToTimestamp($expression)); - } else { - return parent::whereToSql($col, $sign, $expression); - } - } + /** + * The union + * + * @var Zend_Db_Select + */ + protected $notificationQuery; + /** + * Subqueries used for the notification query + * + * @var IdoQuery[] + */ + protected $subQueries = array(); + + /** + * {@inheritdoc} + */ protected function joinBaseTables() { - switch ($this->ds->getDbType()) { - case 'mysql': - $concattedContacts = "GROUP_CONCAT(co.name1 ORDER BY co.name1 SEPARATOR ', ') COLLATE latin1_general_ci"; - break; - case 'pgsql': - // TODO: Find a way to order the contact alias list: - $concattedContacts = "ARRAY_TO_STRING(ARRAY_AGG(co.name1), ', ')"; - break; - case 'oracle': - // TODO: This is only valid for Oracle >= 11g Release 2 - $concattedContacts = "LISTAGG(co.name1, ', ') WITHIN GROUP (ORDER BY co.name1)"; - // Alternatives: - // - // RTRIM(XMLAGG(XMLELEMENT(e, column_name, ',').EXTRACT('//text()')), - // - // not supported and not documented but works since 10.1, - // however it is NOT always present: - // WM_CONCAT(c.alias) - break; - } - - $this->columnMap['history']['output'] = "('[' || $concattedContacts || '] ' || n.output)"; - + $this->notificationQuery = $this->db->select(); $this->select->from( - array('o' => $this->prefix . 'objects'), + array('n' => $this->notificationQuery), array() - )->join( - array('n' => $this->prefix . 'notifications'), - 'o.' . $this->object_id . ' = n.' . $this->object_id . ' AND o.is_active = 1', - array() - )->join( - array('cn' => $this->prefix . 'contactnotifications'), - 'cn.notification_id = n.notification_id', - array() - )->joinLeft( - array('co' => $this->prefix . 'objects'), - 'cn.contact_object_id = co.object_id', - array() - )->joinLeft( - array('c' => $this->prefix . 'contacts'), - 'co.object_id = c.contact_object_id', - array() - )->group('cn.notification_id'); - - // TODO: hmmmm... - if ($this->ds->getDbType() === 'pgsql') { - $this->select->group('n.object_id') - ->group('n.start_time') - ->group('n.output') - ->group('n.state') - ->group('o.objecttype_id'); - } - - $this->joinedVirtualTables = array('history' => true); + ); + $this->joinedVirtualTables['history'] = true; } + /** + * Join hosts + */ + protected function joinHosts() + { + $columns = $this->desiredColumns; + $columns = array_combine($columns, $columns); + foreach ($this->columnMap['services'] as $column => $_) { + if (isset($columns[$column])) { + $columns[$column] = new Zend_Db_Expr('NULL'); + } + } + if (isset($columns['type'])) { + unset($columns['type']); + } + $hosts = $this->createSubQuery('hostnotification', $columns); + $this->subQueries[] = $hosts; + $this->notificationQuery->union(array($hosts), Zend_Db_Select::SQL_UNION_ALL); + } + + /** + * Join services + */ + protected function joinServices() + { + $columns = array_flip($this->desiredColumns); + if (isset($columns['type'])) { + unset($columns['type']); + } + $services = $this->createSubQuery('servicenotification', array_flip($columns)); + $this->subQueries[] = $services; + $this->notificationQuery->union(array($services), Zend_Db_Select::SQL_UNION_ALL); + } + + /** + * {@inheritdoc} + */ + public function addFilter(Filter $filter) + { + foreach ($this->subQueries as $sub) { + $sub->applyFilter(clone $filter); + } + return $this; + } + + /** + * {@inheritdoc} + */ + public function order($columnOrAlias, $dir = null) + { + foreach ($this->subQueries as $sub) { + $sub->requireColumn($columnOrAlias); + } + return parent::order($columnOrAlias, $dir); + } + + /** + * {@inheritdoc} + */ + public function where($condition, $value = null) + { + $this->requireColumn($condition); + foreach ($this->subQueries as $sub) { + $sub->where($condition, $value); + } + return $this; + } } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicenotificationQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicenotificationQuery.php index 0e7954746..4d7baa7ab 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicenotificationQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicenotificationQuery.php @@ -17,52 +17,34 @@ class ServicenotificationQuery extends IdoQuery * {@inheritdoc} */ protected $columnMap = array( + 'contactnotifications' => array( + 'notification_contact_name' => 'co.name1' + ), + 'history' => array( + 'output' => null, + 'state' => 'sn.state', + 'timestamp' => 'UNIX_TIMESTAMP(sn.start_time)', + ), + 'hostgroups' => array( + 'hostgroup_name' => 'hgo.name1' + ), + 'hosts' => array( + 'host_display_name' => 'h.display_name COLLATE latin1_general_ci' + ), 'instances' => array( 'instance_name' => 'i.instance_name' ), 'notifications' => array( - 'notification_output' => 'sn.output', - 'notification_start_time' => 'UNIX_TIMESTAMP(sn.start_time)', - 'notification_state' => 'sn.state', - 'notification_object_id' => 'sn.object_id', - 'host' => 'so.name1 COLLATE latin1_general_ci', 'host_name' => 'so.name1', + 'notification_output' => 'sn.output', + 'notification_state' => 'sn.state', + 'notification_timestamp' => 'UNIX_TIMESTAMP(sn.start_time)', 'object_type' => '(\'service\')', - 'service' => 'so.name2 COLLATE latin1_general_ci', 'service_description' => 'so.name2', - 'service_host' => 'so.name1 COLLATE latin1_general_ci', 'service_host_name' => 'so.name1' ), - 'history' => array( - 'type' => "('notify')", - 'timestamp' => 'UNIX_TIMESTAMP(sn.start_time)', - 'object_id' => 'sn.object_id', - 'state' => 'sn.state', - 'output' => null - ), - 'contactnotifications' => array( - 'contact' => 'cno.name1 COLLATE latin1_general_ci', - 'notification_contact_name' => 'cno.name1', - 'contact_object_id' => 'cno.object_id' - ), - 'acknowledgements' => array( - 'acknowledgement_entry_time' => 'UNIX_TIMESTAMP(a.entry_time)', - 'acknowledgement_author_name' => 'a.author_name', - 'acknowledgement_comment_data' => 'a.comment_data' - ), - 'hostgroups' => array( - 'hostgroup' => 'hgo.name1 COLLATE latin1_general_ci', - 'hostgroup_alias' => 'hg.alias COLLATE latin1_general_ci', - 'hostgroup_name' => 'hgo.name1' - ), - 'hosts' => array( - 'host_alias' => 'h.alias', - 'host_display_name' => 'h.display_name COLLATE latin1_general_ci' - ), 'servicegroups' => array( - 'servicegroup' => 'sgo.name1 COLLATE latin1_general_ci', - 'servicegroup_name' => 'sgo.name1', - 'servicegroup_alias' => 'sg.alias COLLATE latin1_general_ci' + 'servicegroup_name' => 'sgo.name1' ), 'services' => array( 'service_display_name' => 's.display_name COLLATE latin1_general_ci' @@ -89,26 +71,14 @@ class ServicenotificationQuery extends IdoQuery switch ($this->ds->getDbType()) { case 'mysql': $concattedContacts = "GROUP_CONCAT(" - . "DISTINCT cno.name1 ORDER BY cno.name1 SEPARATOR ', '" + . "DISTINCT co.name1 ORDER BY co.name1 SEPARATOR ', '" . ") COLLATE latin1_general_ci"; break; case 'pgsql': // TODO: Find a way to order the contact alias list: - $concattedContacts = "ARRAY_TO_STRING(ARRAY_AGG(DISTINCT cno.name1), ', ')"; - break; - case 'oracle': - // TODO: This is only valid for Oracle >= 11g Release 2 - $concattedContacts = "LISTAGG(cno.name1, ', ') WITHIN GROUP (ORDER BY cno.name1)"; - // Alternatives: - // - // RTRIM(XMLAGG(XMLELEMENT(e, column_name, ',').EXTRACT('//text()')), - // - // not supported and not documented but works since 10.1, - // however it is NOT always present: - // WM_CONCAT(c.alias) + $concattedContacts = "ARRAY_TO_STRING(ARRAY_AGG(DISTINCT co.name1), ', ')"; break; } - $this->columnMap['history']['output'] = "('[' || $concattedContacts || '] ' || sn.output)"; $this->select->from( @@ -141,20 +111,8 @@ class ServicenotificationQuery extends IdoQuery array() ); $this->select->joinLeft( - array('cno' => $this->prefix . 'objects'), - 'cno.object_id = cn.contact_object_id', - array() - ); - } - - /** - * Join acknowledgements - */ - protected function joinAcknowledgements() - { - $this->select->joinLeft( - array('a' => $this->prefix . 'acknowledgements'), - 'a.object_id = sn.object_id', + array('co' => $this->prefix . 'objects'), + 'co.object_id = cn.contact_object_id AND co.is_active = 1 AND co.objecttype_id = 10', array() ); } @@ -243,6 +201,7 @@ class ServicenotificationQuery extends IdoQuery public function getGroup() { $group = array(); + if ( $this->hasJoinedVirtualTable('history') || $this->hasJoinedVirtualTable('hostgroups') @@ -250,10 +209,10 @@ class ServicenotificationQuery extends IdoQuery ) { $group = array('sn.notification_id', 'so.object_id'); if ($this->hasJoinedVirtualTable('contactnotifications') && !$this->hasJoinedVirtualTable('history')) { - $group[] = 'cno.object_id'; + $group[] = 'co.object_id'; } } elseif ($this->hasJoinedVirtualTable('contactnotifications')) { - $group = array('sn.notification_id', 'cno.object_id', 'so.object_id'); + $group = array('sn.notification_id', 'co.object_id', 'so.object_id'); } if (! empty($group)) { @@ -265,10 +224,6 @@ class ServicenotificationQuery extends IdoQuery $group[] = 's.service_id'; } - if ($this->hasJoinedVirtualTable('acknowledgements')) { - $group[] = 'a.acknowledgement_id'; - } - if ($this->hasJoinedVirtualTable('instances')) { $group[] = 'i.instance_id'; } diff --git a/modules/monitoring/library/Monitoring/DataView/Notification.php b/modules/monitoring/library/Monitoring/DataView/Notification.php index febf9e343..b9a85cd2c 100644 --- a/modules/monitoring/library/Monitoring/DataView/Notification.php +++ b/modules/monitoring/library/Monitoring/DataView/Notification.php @@ -11,18 +11,12 @@ class Notification extends DataView public function getColumns() { return array( - 'instance_name', - 'notification_state', - 'notification_start_time', - 'notification_contact_name', - 'notification_output', - 'notification_object_id', - 'contact_object_id', - 'acknowledgement_entry_time', - 'acknowledgement_author_name', - 'acknowledgement_comment_data', 'host_display_name', 'host_name', + 'notification_contact_name', + 'notification_output', + 'notification_state', + 'notification_timestamp', 'object_type', 'service_description', 'service_display_name', @@ -36,23 +30,8 @@ class Notification extends DataView public function getSortRules() { return array( - 'notification_start_time' => array( - 'order' => self::SORT_DESC, - 'title' => 'Notification Start' - ), - 'host_display_name' => array( - 'columns' => array( - 'host_display_name', - 'service_display_name' - ), - 'order' => self::SORT_ASC - ), - 'service_display_name' => array( - 'columns' => array( - 'service_display_name', - 'host_display_name' - ), - 'order' => self::SORT_ASC + 'notification_timestamp' => array( + 'order' => self::SORT_DESC ) ); } @@ -63,11 +42,9 @@ class Notification extends DataView public function getStaticFilterColumns() { return array( - 'contact', - 'host', 'host_alias', - 'hostgroup', 'hostgroup_alias', 'hostgroup_name', - 'service', - 'servicegroup', 'servicegroup_alias', 'servicegroup_name' + 'hostgroup_name', + 'instance_name', + 'servicegroup_name' ); } From c547f4c17f20bae02a12848c12b59804e6c73f7a Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 15 Sep 2016 14:07:19 +0200 Subject: [PATCH 109/337] Fix error in the contact detail view introduced by changes to the notification queries --- .../monitoring/application/controllers/ListController.php | 2 +- .../monitoring/application/controllers/ShowController.php | 5 ++--- .../Monitoring/Backend/Ido/Query/NotificationQuery.php | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 787a184f5..cfbff2169 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -292,7 +292,7 @@ class ListController extends Controller $this->setupPaginationControl($notifications); $this->setupLimitControl(); $this->setupSortControl(array( - 'notification_start_time' => $this->translate('Notification Start') + 'notification_timestamp' => $this->translate('Notification Start') ), $notifications); $this->view->notifications = $notifications; diff --git a/modules/monitoring/application/controllers/ShowController.php b/modules/monitoring/application/controllers/ShowController.php index bfd5167ff..2c7bc6039 100644 --- a/modules/monitoring/application/controllers/ShowController.php +++ b/modules/monitoring/application/controllers/ShowController.php @@ -29,7 +29,6 @@ class ShowController extends Controller 'contact_alias', 'contact_email', 'contact_pager', - 'contact_object_id', 'contact_notify_service_timeperiod', 'contact_notify_service_recovery', 'contact_notify_service_warning', @@ -61,13 +60,13 @@ class ShowController extends Controller 'service_description', 'notification_output', 'notification_contact_name', - 'notification_start_time', + 'notification_timestamp', 'notification_state', 'host_display_name', 'service_display_name' )); - $notifications->where('contact_object_id', $contact->contact_object_id); + $notifications->where('notification_contact_name', $contactName); $this->applyRestriction('monitoring/filter/objects', $notifications); $this->view->notifications = $notifications; $this->setupLimitControl(); diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php index c9515140b..8729cf25c 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/NotificationQuery.php @@ -24,7 +24,6 @@ class NotificationQuery extends IdoQuery 'notifications' => array( 'instance_name' => 'n.instance_name', 'notification_contact_name' => 'n.notification_contact_name', - 'notification_object_id' => 'n.notification_object_id', 'notification_output' => 'n.notification_output', 'notification_state' => 'n.notification_state', 'notification_timestamp' => 'n.notification_timestamp' From 5b875f37494678fe08dea4157507027527e9aa61 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 21 Sep 2016 15:58:59 +0200 Subject: [PATCH 110/337] JsonResponse: Allow to output the "data" key in case of an error As stated by the JSend specification this key is optional for error responses. --- library/Icinga/Web/Response/JsonResponse.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Web/Response/JsonResponse.php b/library/Icinga/Web/Response/JsonResponse.php index 9f2cd95cf..8a9ba7d33 100644 --- a/library/Icinga/Web/Response/JsonResponse.php +++ b/library/Icinga/Web/Response/JsonResponse.php @@ -121,7 +121,7 @@ class JsonResponse extends Response */ public function getFailData() { - return $this->failData; + return (! is_array($this->failData) || empty($this->failData)) ? null : $this->failData; } /** @@ -173,9 +173,11 @@ class JsonResponse extends Response switch ($this->status) { case static::STATUS_ERROR: $body['message'] = $this->getErrorMessage(); - break; case static::STATUS_FAIL: - $body['data'] = $this->getFailData(); + $failData = $this->getFailData(); + if ($failData !== null || $this->status === static::STATUS_FAIL) { + $body['data'] = $failData; + } break; case static::STATUS_SUCCESS: $body['data'] = $this->getSuccessData(); From 7ed3acbbd806794291794430d082f0416dc367c9 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 26 Sep 2016 11:00:42 +0200 Subject: [PATCH 111/337] Module: Check a menu item's permission and don't overwrite its label This allows now totally different values for an item's name and label and fixes the bug that permissions declared in the configuration.php were not evaluated for menu items. fixes #11431 --- library/Icinga/Application/Modules/Module.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index f62684471..de40266e1 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -362,25 +362,28 @@ class Module public function getMenu() { $this->launchConfigScript(); - return $this->createMenu($this->menuItems); + return Navigation::fromArray($this->createMenu($this->menuItems)); } /** - * Create and return a new navigation for the given menu items + * Create and return an array structure for the given menu items * * @param MenuItemContainer[] $items * - * @return Navigation + * @return array */ private function createMenu(array $items) { - $navigation = new Navigation(); + $navigation = array(); foreach ($items as $item) { /** @var MenuItemContainer $item */ - $navigationItem = $navigation->createItem($item->getName(), $item->getProperties()); - $navigationItem->setChildren($this->createMenu($item->getChildren())); - $navigationItem->setLabel($this->translate($item->getName())); - $navigation->addItem($navigationItem); + $properties = $item->getProperties(); + $properties['children'] = $this->createMenu($item->getChildren()); + if (! isset($properties['label'])) { + $properties['label'] = $this->translate($item->getName()); + } + + $navigation[$item->getName()] = $properties; } return $navigation; From b3a3db6382b1c6be7059bdc7ec5a87790226f930 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 27 Sep 2016 11:08:34 +0200 Subject: [PATCH 112/337] Make permission application/log configurable refs #12655 --- application/forms/Security/RoleForm.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/application/forms/Security/RoleForm.php b/application/forms/Security/RoleForm.php index eba9502f5..1873123c2 100644 --- a/application/forms/Security/RoleForm.php +++ b/application/forms/Security/RoleForm.php @@ -42,6 +42,8 @@ class RoleForm extends ConfigForm 'application/stacktraces' => $this->translate( 'Allow to adjust in the preferences whether to show stacktraces' ) . ' (application/stacktraces)', + 'application/log' => $this->translate('Allow to view the application log') + . ' (application/log)', 'config/*' => $this->translate('Allow config access') . ' (config/*)' ); From 5576e00a015d3b028aac2dad753d93545b562906 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 27 Sep 2016 11:22:18 +0200 Subject: [PATCH 113/337] Fix missing translation refs #12655 --- .../locale/de_DE/LC_MESSAGES/icinga.mo | Bin 60908 -> 60998 bytes .../locale/de_DE/LC_MESSAGES/icinga.po | 10 +++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.mo b/application/locale/de_DE/LC_MESSAGES/icinga.mo index ae880d0444f01d506d18283868966799939961aa..3413b7cbff069bdec8a4239867b2db1b767e0dea 100644 GIT binary patch delta 13107 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 KBYED&KK}#C=N6U# delta 13025 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 diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.po b/application/locale/de_DE/LC_MESSAGES/icinga.po index 88c5b08b7..d099a821b 100644 --- a/application/locale/de_DE/LC_MESSAGES/icinga.po +++ b/application/locale/de_DE/LC_MESSAGES/icinga.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: Icinga Web 2 (None)\n" "Report-Msgid-Bugs-To: dev@icinga.org\n" -"POT-Creation-Date: 2016-06-02 14:11+0200\n" -"PO-Revision-Date: 2016-06-02 15:45+0200\n" +"POT-Creation-Date: 2016-09-27 09:09+0000\n" +"PO-Revision-Date: 2016-09-27 11:15+0200\n" "Last-Translator: Eric Lippmann \n" "Language: de_DE\n" "MIME-Version: 1.0\n" @@ -16,7 +16,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "Language-Team: \n" -"X-Generator: Poedit 1.8.8\n" +"X-Generator: Poedit 1.8.9\n" #: /vagrant/library/Icinga/Web/Form/Validator/InArray.php:16 #, php-format @@ -230,6 +230,10 @@ msgstr "Erlaubt die Einstellung zur Anzeige von Stacktraces" msgid "Allow to share navigation items" msgstr "Erlaubt das Teilen von Navigationselemten" +#: /vagrant/application/forms/Security/RoleForm.php:45 +msgid "Allow to view the application log" +msgstr "Erlaubt das Einsehen des Anwendungslogs" + #: /vagrant/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:135 msgid "" "An additional filter to use when looking up groups using the specified " From 4d16656100d74c81f6bf3783343d28643b4c7939 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 27 Sep 2016 13:26:20 +0200 Subject: [PATCH 114/337] Preserve status code and headers in JSON responses refs #12583 --- library/Icinga/Web/Response.php | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Response.php b/library/Icinga/Web/Response.php index 5fa5bc0bd..7e0b89693 100644 --- a/library/Icinga/Web/Response.php +++ b/library/Icinga/Web/Response.php @@ -210,9 +210,11 @@ class Response extends Zend_Controller_Response_Http * * @return JsonResponse */ - public static function json() + public function json() { - return new JsonResponse(); + $response = new JsonResponse(); + $response->setMetaDataFrom($this); + return $response; } /** @@ -292,4 +294,20 @@ class Response extends Zend_Controller_Response_Http } return parent::sendHeaders(); } + + /** + * Copies non-body-related response data from $response + * + * @param Response $response + * + * @return $this + */ + protected function setMetaDataFrom(self $response) + { + $this->_headers = $response->_headers; + $this->_headersRaw = $response->_headersRaw; + $this->_httpResponseCode = $response->_httpResponseCode; + $this->headersSentThrowsException = $response->headersSentThrowsException; + return $this; + } } From c83659eff78438f1450f074322ffdaf2a852571b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 29 Sep 2016 16:15:35 +0200 Subject: [PATCH 115/337] Sometimes you just herp the derp so hard it herpderps --- library/Icinga/Web/Controller/ModuleActionController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Controller/ModuleActionController.php b/library/Icinga/Web/Controller/ModuleActionController.php index 38826a84c..2cb5f62bc 100644 --- a/library/Icinga/Web/Controller/ModuleActionController.php +++ b/library/Icinga/Web/Controller/ModuleActionController.php @@ -26,7 +26,7 @@ class ModuleActionController extends ActionController protected function prepareInit() { $this->moduleInit(); - if ($this->requiresLogin() + if (($this->Auth()->isAuthenticated() || $this->requiresLogin()) && $this->getFrontController()->getDefaultModule() !== $this->getModuleName()) { $this->assertPermission(Manager::MODULE_PERMISSION_NS . $this->getModuleName()); } From 246ad7b4d3ca42199e3990848c580dcdc309807f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 5 Oct 2016 09:57:00 +0200 Subject: [PATCH 116/337] Remove ChartController fixes #10019 --- .../controllers/ChartController.php | 405 ------------------ .../views/scripts/chart/hostgroup.phtml | 9 - .../views/scripts/chart/servicegroup.phtml | 8 - .../views/scripts/chart/test.phtml | 5 - 4 files changed, 427 deletions(-) delete mode 100644 modules/monitoring/application/controllers/ChartController.php delete mode 100644 modules/monitoring/application/views/scripts/chart/hostgroup.phtml delete mode 100644 modules/monitoring/application/views/scripts/chart/servicegroup.phtml delete mode 100644 modules/monitoring/application/views/scripts/chart/test.phtml diff --git a/modules/monitoring/application/controllers/ChartController.php b/modules/monitoring/application/controllers/ChartController.php deleted file mode 100644 index a92a8bcdb..000000000 --- a/modules/monitoring/application/controllers/ChartController.php +++ /dev/null @@ -1,405 +0,0 @@ -alignTopLeft(); - $chart->setAxisLabel('X axis label', 'Y axis label') - ->setYAxis(new LogarithmicUnit()); - - for ($i = -15; $i < 15; $i++) { - $data1[] = array($i, -$i * rand(1, 10) * pow(2, rand(1, 2))); - } - for ($i = -15; $i < 15; $i++) { - $data2[] = array($i, 1000 + $i * rand(1, 35) * pow(2, rand(1, 2))); - } - for ($i = -15; $i < 15; $i++) { - $data3[] = array($i, $i * rand(1, 100) * pow(2, rand(1, 10)) - 1000); - } - - $chart->drawLines( - array( - 'label' => 'Random 1', - 'color' => '#F56', - 'data' => $data1, - 'showPoints' => true - ) - ); - $chart->drawLines( - array( - 'label' => 'Random 2', - 'color' => '#fa4', - 'data' => $data2, - 'showPoints' => true - ) - ); - $chart->drawLines( - array( - 'label' => 'Random 3', - 'color' => '#4b7', - 'data' => $data3, - 'showPoints' => true - ) - ); - return $chart; - } - - private function drawLogChart2() - { - $chart = new GridChart(); - $chart->alignTopLeft(); - $chart->setAxisLabel('X axis label', 'Y axis label') - ->setYAxis(new LogarithmicUnit()); - - for ($i = -10; $i < 10; $i++) { - $sign = $i > 0 ? 1 : - ($i < 0 ? -1 : 0); - $data[] = array($i, $sign * pow(10, abs($i))); - } - $chart->drawLines( - array( - 'label' => 'f(x): sign(x) * 10^|x|', - 'color' => '#F56', - 'data' => $data, - 'showPoints' => true - ) - ); - return $chart; - } - private function drawLogChart3() - { - $chart = new GridChart(); - $chart->alignTopLeft(); - $chart->setAxisLabel('X axis label', 'Y axis label') - ->setYAxis(new LogarithmicUnit()); - - for ($i = -2; $i < 3; $i++) { - $sign = $i > 0 ? 1 : - ($i < 0 ? -1 : 0); - $data[] = array($i, $sign * pow(10, abs($i))); - } - $chart->drawLines( - array( - 'label' => 'f(x): sign(x) * 10^|x|', - 'color' => '#F56', - 'data' => $data, - 'showPoints' => true - ) - ); - return $chart; - } - - public function testAction() - { - $this->chart = new GridChart(); - $this->chart->alignTopLeft(); - $this->chart->setAxisLabel('X axis label', 'Y axis label')->setXAxis(new StaticAxis()); - $data1 = array(); - $data2 = array(); - $data3 = array(); - for ($i = 0; $i < 50; $i++) { - $data3[] = array('Label ' . $i, rand(0, 30)); - } - - /* - $this->chart->drawLines( - array( - 'label' => 'Nr of outtakes', - 'color' => '#F56', - 'width' => '5', - - 'data' => $data - ), array( - 'label' => 'Some line', - 'color' => '#fa4', - 'width' => '4', - - 'data' => $data3, - 'showPoints' => true - ) - ); -*/ - $this->chart->drawBars( - array( - 'label' => 'A big amount of data', - 'color' => '#4b7', - 'data' => $data3, - 'showPoints' => true - ) - ); -/* - $this->chart->drawLines( - array( - 'label' => 'Nr of outtakes', - 'color' => 'yellow', - 'width' => '5', - 'data' => $data2 - ) - ); -*/ - $this->view->svgs = array(); - $this->view->svgs[] = $this->drawLogChart1(); - $this->view->svgs[] = $this->drawLogChart2(); - $this->view->svgs[] = $this->drawLogChart3(); - $this->view->svgs[] = $this->chart; - } - - public function hostgroupAction() - { - $query = $this->backend->select()->from( - 'hostgroupsummary', - array( - 'hostgroup', - 'hosts_up', - 'hosts_unreachable_handled', - 'hosts_unreachable_unhandled', - 'hosts_down_handled', - 'hosts_down_unhandled', - 'hosts_pending', - 'services_ok', - 'services_unknown_handled', - 'services_unknown_unhandled', - 'services_critical_handled', - 'services_critical_unhandled', - 'services_warning_handled', - 'services_warning_unhandled', - 'services_pending' - ) - )->order('hostgroup')->getQuery()->fetchAll(); - $this->view->height = intval($this->getParam('height', 500)); - $this->view->width = intval($this->getParam('width', 500)); - if (count($query) === 1) { - $this->drawHostGroupPie($query[0]); - } else { - $this->drawHostGroupChart($query); - } - } - - public function servicegroupAction() - { - $query = $this->backend->select()->from( - 'servicegroupsummary', - array( - 'servicegroup', - 'services_ok', - 'services_unknown_handled', - 'services_unknown_unhandled', - 'services_critical_handled', - 'services_critical_unhandled', - 'services_warning_handled', - 'services_warning_unhandled', - 'services_pending' - ) - )->order('servicegroup')->getQuery()->fetchAll(); - $this->view->height = intval($this->getParam('height', 500)); - $this->view->width = intval($this->getParam('width', 500)); - - - if (count($query) === 1) { - $this->drawServiceGroupPie($query[0]); - } else { - $this->drawServiceGroupChart($query); - } - } - - private function drawServiceGroupChart($query) - { - $okBars = array(); - $warningBars = array(); - $critBars = array(); - $unknownBars = array(); - foreach ($query as $servicegroup) { - $okBars[] = array($servicegroup->servicegroup, $servicegroup->services_ok); - $warningBars[] = array($servicegroup->servicegroup, $servicegroup->services_warning_unhandled); - $critBars[] = array($servicegroup->servicegroup, $servicegroup->services_critical_unhandled); - $unknownBars[] = array($servicegroup->servicegroup, $servicegroup->services_unknown_unhandled); - } - $this->view->chart = new GridChart(); - $this->view->chart->title = $this->translate('Service Group Chart'); - $this->view->chart->description = $this->translate('Contains service states for each service group.'); - - $this->view->chart->alignTopLeft(); - $this->view->chart->setAxisLabel('', $this->translate('Services')) - ->setXAxis(new StaticAxis()) - ->setAxisMin(null, 0); - - $tooltip = $this->translate('{title}:
{value} of {sum} services are {label}'); - $this->view->chart->drawBars( - array( - 'label' => $this->translate('Ok'), - 'color' => '#44bb77', - 'stack' => 'stack1', - 'data' => $okBars, - 'tooltip' => $tooltip - ), - array( - 'label' => $this->translate('Warning'), - 'color' => '#ffaa44', - 'stack' => 'stack1', - 'data' => $warningBars, - 'tooltip' => $tooltip - ), - array( - 'label' => $this->translate('Critical'), - 'color' => '#ff5566', - 'stack' => 'stack1', - 'data' => $critBars, - 'tooltip' => $tooltip - ), - array( - 'label' => $this->translate('Unknown'), - 'color' => '#dd66ff', - 'stack' => 'stack1', - 'data' => $unknownBars, - 'tooltip' => $tooltip - ) - ); - } - - private function drawHostGroupChart($query) - { - $upBars = array(); - $downBars = array(); - $unreachableBars = array(); - for ($i = 0; $i < 50; $i++) { - $upBars[] = array( - (string)$i, rand(1, 200), rand(1, 200) - ); - $downBars[] = array( - (string)$i, rand(1, 200), rand(1, 200) - ); - $unreachableBars[] = array( - (string)$i, rand(1, 200), rand(1, 200) - ); - } - $tooltip = $this->translate('{title}:
{value} of {sum} hosts are {label}'); - $this->view->chart = new GridChart(); - $this->view->chart->title = $this->translate('Host Group Chart'); - $this->view->chart->description = $this->translate('Contains host states of each service group.'); - - $this->view->chart->alignTopLeft(); - $this->view->chart->setAxisLabel('', $this->translate('Hosts')) - ->setXAxis(new StaticAxis()) - ->setAxisMin(null, 0); - $this->view->chart->drawBars( - array( - 'label' => $this->translate('Up'), - 'color' => '#44bb77', - 'stack' => 'stack1', - 'data' => $upBars, - 'tooltip' => $tooltip - ), - array( - 'label' => $this->translate('Down'), - 'color' => '#ff5566', - 'stack' => 'stack1', - 'data' => $downBars, - 'tooltip' => $tooltip - ), - array( - 'label' => $this->translate('Unreachable'), - 'color' => '#dd66ff', - 'stack' => 'stack1', - 'data' => $unreachableBars, - 'tooltip' => $tooltip - ) - ); - } - - private function drawServiceGroupPie($query) - { - $this->view->chart = new PieChart(); - $this->view->chart->alignTopLeft(); - $this->view->chart->drawPie(array( - 'data' => array( - (int) $query->services_ok, - (int) $query->services_warning_unhandled, - (int) $query->services_warning_handled, - (int) $query->services_critical_unhandled, - (int) $query->services_critical_handled, - (int) $query->services_unknown_unhandled, - (int) $query->services_unknown_handled, - (int) $query->services_pending - ), - 'colors' => array('#44bb77', '#ff4444', '#ff0000', '#ffff00', '#ffff33', '#E066FF', '#f099FF', '#fefefe'), - 'labels'=> array( - $query->services_ok . ' Up Services', - $query->services_warning_handled . $this->translate(' Warning Services (Handled)'), - $query->services_warning_unhandled . $this->translate(' Warning Services (Unhandled)'), - $query->services_critical_handled . $this->translate(' Down Services (Handled)'), - $query->services_critical_unhandled . $this->translate(' Down Services (Unhandled)'), - $query->services_unknown_handled . $this->translate(' Unreachable Services (Handled)'), - $query->services_unknown_unhandled . $this->translate(' Unreachable Services (Unhandled)'), - $query->services_pending . $this->translate(' Pending Services') - ) - )); - } - - private function drawHostGroupPie($query) - { - $this->view->chart = new PieChart(); - $this->view->chart->alignTopLeft(); - $this->view->chart->drawPie(array( - 'data' => array( - (int) $query->hosts_up, - (int) $query->hosts_down_handled, - (int) $query->hosts_down_unhandled, - (int) $query->hosts_unreachable_handled, - (int) $query->hosts_unreachable_unhandled, - (int) $query->hosts_pending - ), - 'colors' => array( - '#44bb77', // 'Ok' - '#ff4444', // 'Warning' - '#ff0000', // 'WarningHandled' - '#E066FF', - '#f099FF', - '#fefefe' - ), - 'labels'=> array( - (int) $query->hosts_up . $this->translate(' Up Hosts'), - (int) $query->hosts_down_handled . $this->translate(' Down Hosts (Handled)'), - (int) $query->hosts_down_unhandled . $this->translate(' Down Hosts (Unhandled)'), - (int) $query->hosts_unreachable_handled . $this->translate(' Unreachable Hosts (Handled)'), - (int) $query->hosts_unreachable_unhandled . $this->translate(' Unreachable Hosts (Unhandled)'), - (int) $query->hosts_pending . $this->translate(' Pending Hosts') - ) - ), array( - 'data' => array( - (int) $query->services_ok, - (int) $query->services_warning_unhandled, - (int) $query->services_warning_handled, - (int) $query->services_critical_unhandled, - (int) $query->services_critical_handled, - (int) $query->services_unknown_unhandled, - (int) $query->services_unknown_handled, - (int) $query->services_pending - ), - 'colors' => array('#44bb77', '#ff4444', '#ff0000', '#ffff00', '#ffff33', '#E066FF', '#f099FF', '#fefefe'), - 'labels'=> array( - $query->services_ok . $this->translate(' Up Services'), - $query->services_warning_handled . $this->translate(' Warning Services (Handled)'), - $query->services_warning_unhandled . $this->translate(' Warning Services (Unhandled)'), - $query->services_critical_handled . $this->translate(' Down Services (Handled)'), - $query->services_critical_unhandled . $this->translate(' Down Services (Unhandled)'), - $query->services_unknown_handled . $this->translate(' Unreachable Services (Handled)'), - $query->services_unknown_unhandled . $this->translate(' Unreachable Services (Unhandled)'), - $query->services_pending . $this->translate(' Pending Services') - ) - )); - } -} diff --git a/modules/monitoring/application/views/scripts/chart/hostgroup.phtml b/modules/monitoring/application/views/scripts/chart/hostgroup.phtml deleted file mode 100644 index 22a8a8c05..000000000 --- a/modules/monitoring/application/views/scripts/chart/hostgroup.phtml +++ /dev/null @@ -1,9 +0,0 @@ - -compact) { ?> -
- render(); ?> - render() : ''; ?> -
- - render(); ?> - diff --git a/modules/monitoring/application/views/scripts/chart/servicegroup.phtml b/modules/monitoring/application/views/scripts/chart/servicegroup.phtml deleted file mode 100644 index f24efe5ba..000000000 --- a/modules/monitoring/application/views/scripts/chart/servicegroup.phtml +++ /dev/null @@ -1,8 +0,0 @@ - -compact) { ?> -
- render(); ?> -
- - render(); ?> - diff --git a/modules/monitoring/application/views/scripts/chart/test.phtml b/modules/monitoring/application/views/scripts/chart/test.phtml deleted file mode 100644 index 214f44d33..000000000 --- a/modules/monitoring/application/views/scripts/chart/test.phtml +++ /dev/null @@ -1,5 +0,0 @@ - -
- render() ?> -
- \ No newline at end of file From 213804401cd494a124ada7dc30893e9062072a8d Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 7 Oct 2016 14:44:08 +0200 Subject: [PATCH 117/337] Fix Web 2's remove-downtime API actions not selecting the downtime name refs #11398 --- .../application/controllers/ActionsController.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/controllers/ActionsController.php b/modules/monitoring/application/controllers/ActionsController.php index 06bbc8e61..1ec5fe102 100644 --- a/modules/monitoring/application/controllers/ActionsController.php +++ b/modules/monitoring/application/controllers/ActionsController.php @@ -61,7 +61,7 @@ class Monitoring_ActionsController extends Controller $filter = $this->getFilterOrExitIfEmpty(); $downtimes = $this->backend ->select() - ->from('downtime', array('host_name', 'id' => 'downtime_internal_id')) + ->from('downtime', array('host_name', 'id' => 'downtime_internal_id', 'name' => 'downtime_name')) ->where('object_type', 'host') ->applyFilter($this->getRestriction('monitoring/filter/objects')) ->applyFilter($filter); @@ -110,7 +110,10 @@ class Monitoring_ActionsController extends Controller $filter = $this->getFilterOrExitIfEmpty(); $downtimes = $this->backend ->select() - ->from('downtime', array('host_name', 'service_description', 'id' => 'downtime_internal_id')) + ->from( + 'downtime', + array('host_name', 'service_description', 'id' => 'downtime_internal_id', 'name' => 'downtime_name') + ) ->where('object_type', 'service') ->applyFilter($this->getRestriction('monitoring/filter/objects')) ->applyFilter($filter); From 14cb499c1b8ee19ea1ef66aa1f964e3b1de04e61 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 7 Oct 2016 14:48:34 +0200 Subject: [PATCH 118/337] Change default for sticky option of acknowledgements from true to false resolves #12873 --- .../forms/Command/Object/AcknowledgeProblemCommandForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php b/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php index cd5b866b7..32fb522e2 100644 --- a/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php @@ -109,7 +109,7 @@ class AcknowledgeProblemCommandForm extends ObjectsCommandForm 'sticky', array( 'label' => $this->translate('Sticky Acknowledgement'), - 'value' => true, + 'value' => false, 'description' => $this->translate( 'If you want the acknowledgement to remain until the host or service recovers even if the host' . ' or service changes state, check this option.' From 529ba8ed8ae0752fa4623f3a46c492dc974982e0 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 11 Oct 2016 09:53:21 +0200 Subject: [PATCH 119/337] Use the query's iterator instead fetchAll() on CSV export fixes #12723 --- library/Icinga/File/Csv.php | 8 +++++--- test/php/library/Icinga/File/CsvTest.php | 20 ++++++++------------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/library/Icinga/File/Csv.php b/library/Icinga/File/Csv.php index 448327806..f1008d611 100644 --- a/library/Icinga/File/Csv.php +++ b/library/Icinga/File/Csv.php @@ -3,15 +3,17 @@ namespace Icinga\File; +use Traversable; + class Csv { protected $query; protected function __construct() {} - public static function fromQuery($query) + public static function fromQuery(Traversable $query) { - $csv = new Csv(); + $csv = new static(); $csv->query = $query; return $csv; } @@ -26,7 +28,7 @@ class Csv { $first = true; $csv = ''; - foreach ($this->query->fetchAll() as $row) { + foreach ($this->query as $row) { if ($first) { $csv .= implode(',', array_keys((array) $row)) . "\r\n"; $first = false; diff --git a/test/php/library/Icinga/File/CsvTest.php b/test/php/library/Icinga/File/CsvTest.php index 6eaf6bad0..048e3b073 100644 --- a/test/php/library/Icinga/File/CsvTest.php +++ b/test/php/library/Icinga/File/CsvTest.php @@ -3,7 +3,7 @@ namespace Tests\Icinga\File; -use Mockery; +use Icinga\Data\DataArray\ArrayDatasource; use Icinga\File\Csv; use Icinga\Test\BaseTestCase; @@ -11,19 +11,15 @@ class CsvTest extends BaseTestCase { public function testWhetherValidCsvIsRendered() { - $queryMock = Mockery::mock( - 'Icinga\Data\SimpleQuery', - array( - 'fetchAll' => array( - array('col1' => 'val1', 'col2' => 'val2', 'col3' => 'val3', 'col4' => 'val4'), - array('col1' => 'val5', 'col2' => 'val6', 'col3' => 'val7', 'col4' => 'val8') - ) - ) - ); - $csv = Csv::fromQuery($queryMock); + $data = new ArrayDatasource(array( + array('col1' => 'val1', 'col2' => 'val2', 'col3' => 'val3', 'col4' => 'val4'), + array('col1' => 'val5', 'col2' => 'val6', 'col3' => 'val7', 'col4' => 'val8') + )); + + $csv = Csv::fromQuery($data->select()); $this->assertEquals( - join( + implode( "\r\n", array( 'col1,col2,col3,col4', From bf3cfda209d9ee442f675eedd2f85a3c1da23952 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 11 Oct 2016 10:45:15 +0200 Subject: [PATCH 120/337] Response: rename setMetaDataFrom() to copyMetaDataFrom() refs #12583 --- library/Icinga/Web/Response.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Response.php b/library/Icinga/Web/Response.php index 7e0b89693..af3e1e27e 100644 --- a/library/Icinga/Web/Response.php +++ b/library/Icinga/Web/Response.php @@ -213,7 +213,7 @@ class Response extends Zend_Controller_Response_Http public function json() { $response = new JsonResponse(); - $response->setMetaDataFrom($this); + $response->copyMetaDataFrom($this); return $response; } @@ -302,7 +302,7 @@ class Response extends Zend_Controller_Response_Http * * @return $this */ - protected function setMetaDataFrom(self $response) + protected function copyMetaDataFrom(self $response) { $this->_headers = $response->_headers; $this->_headersRaw = $response->_headersRaw; From 4b0e90cf23d17e30628cb47857e83bc19af6e368 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 11 Oct 2016 15:20:20 +0200 Subject: [PATCH 121/337] Disable zero width spaces for the moment refs #12774 --- modules/monitoring/application/views/helpers/PluginOutput.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php index 404c9159c..4381e5b14 100644 --- a/modules/monitoring/application/views/helpers/PluginOutput.php +++ b/modules/monitoring/application/views/helpers/PluginOutput.php @@ -221,6 +221,8 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract */ protected function fixWrapping($output, $zeroWidthSpace) { + // TODO(el): Disabled until we find a bulletproof implementation + return $output; // Add zero width space after ')', ']', ':', '.', '_' and '-' if not surrounded by whitespaces $output = preg_replace('/([^\s])([\\)\\]:._-])([^\s])/', '$1$2' . $zeroWidthSpace . '$3', $output); // Add zero width space before '(' and '[' if not surrounded by whitespaces From 9bb798c18b33bd31259722d95a2e98b11ec6006d Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 12 Oct 2016 11:06:04 +0200 Subject: [PATCH 122/337] IniWriter: don't persist a section key if the value is null refs #11743 --- library/Icinga/File/Ini/IniWriter.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/Icinga/File/Ini/IniWriter.php b/library/Icinga/File/Ini/IniWriter.php index 83410599f..f00040e39 100644 --- a/library/Icinga/File/Ini/IniWriter.php +++ b/library/Icinga/File/Ini/IniWriter.php @@ -149,6 +149,10 @@ class IniWriter $domSection = $doc->getSection($section); } foreach ($directives as $key => $value) { + if ($value === null) { + continue; + } + if ($value instanceof ConfigObject) { throw new ProgrammingError('Cannot diff recursive configs'); } From 014e7c136a2aaf60201726185c4d2f7655f9b0ad Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 12 Oct 2016 11:07:21 +0200 Subject: [PATCH 123/337] Revert "UserGroupBackendForm: Do not persist null values, really" This reverts commit 975edbe548ecacabee22fb871e21dd2b8312dc19. refs #11743 --- .../forms/Config/UserGroup/UserGroupBackendForm.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/application/forms/Config/UserGroup/UserGroupBackendForm.php b/application/forms/Config/UserGroup/UserGroupBackendForm.php index 84c1cb4f8..42e76e347 100644 --- a/application/forms/Config/UserGroup/UserGroupBackendForm.php +++ b/application/forms/Config/UserGroup/UserGroupBackendForm.php @@ -127,14 +127,7 @@ class UserGroupBackendForm extends ConfigForm unset($data['name']); } - $backendConfig->merge($data); - foreach ($backendConfig->toArray() as $k => $v) { - if ($v === null) { - unset($backendConfig->$k); - } - } - - $this->config->setSection($name, $backendConfig); + $this->config->setSection($name, $backendConfig->merge($data)); return $this; } From d98732f663e8b81427c2e1a6c4fd27d694ca6653 Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Wed, 12 Oct 2016 12:17:29 +0200 Subject: [PATCH 124/337] ErrorController: Don't log exceptions other than HTTP 500 fixes #12760 --- application/controllers/ErrorController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/application/controllers/ErrorController.php b/application/controllers/ErrorController.php index 86ee26554..101d48585 100644 --- a/application/controllers/ErrorController.php +++ b/application/controllers/ErrorController.php @@ -32,8 +32,6 @@ class ErrorController extends ActionController $error = $this->_getParam('error_handler'); $exception = $error->exception; /** @var \Exception $exception */ - Logger::error($exception); - Logger::error('Stacktrace: %s', $exception->getTraceAsString()); if (! ($isAuthenticated = $this->Auth()->isAuthenticated())) { $this->innerLayout = 'guest-error'; @@ -83,6 +81,7 @@ class ErrorController extends ActionController break; default: $this->getResponse()->setHttpResponseCode(500); + Logger::error($exception); break; } $this->view->message = $exception->getMessage(); From 58b2e6c00f78a86199ed453c8804722665b543b3 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 12 Oct 2016 13:41:42 +0200 Subject: [PATCH 125/337] Add unit tests for the fixed IniWriter implementation refs #11743 --- .../library/Icinga/File/Ini/IniWriterTest.php | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/php/library/Icinga/File/Ini/IniWriterTest.php b/test/php/library/Icinga/File/Ini/IniWriterTest.php index 3148779ef..e4bfd2716 100644 --- a/test/php/library/Icinga/File/Ini/IniWriterTest.php +++ b/test/php/library/Icinga/File/Ini/IniWriterTest.php @@ -400,4 +400,53 @@ EOD; file_put_contents($this->tempFile, $config); return $this->tempFile; } + + public function testWhetherNullValuesGetPersisted() + { + $config = Config::fromArray(array()); + $section = $config->getSection('garbage'); + $section->foobar = null; + $config->setSection('garbage', $section); + + $iniWriter = new IniWriter($config, '/dev/null'); + $this->assertEquals( + 0, + preg_match('/foobar/', $iniWriter->render()), + 'IniWriter persists section keys with null values' + ); + } + + public function testWhetherEmptyValuesGetPersisted() + { + $config = Config::fromArray(array()); + $section = $config->getSection('garbage'); + $section->foobar = ''; + $config->setSection('garbage', $section); + + $iniWriter = new IniWriter($config, '/dev/null'); + $this->assertEquals( + 1, + preg_match('/foobar/', $iniWriter->render()), + 'IniWriter doesn\'t persist section keys with empty values' + ); + } + + public function testExplicitRemove() + { + $filename = tempnam(sys_get_temp_dir(), 'iw2'); + $config = Config::fromArray(array('garbage' => array('foobar' => 'lolcat'))); + $iniWriter = new IniWriter($config, $filename); + $iniWriter->write(); + + $section = $config->getSection('garbage'); + $section->foobar = null; + $iniWriter = new IniWriter($config, $filename); + $this->assertEquals( + 0, + preg_match('/foobar/', $iniWriter->render()), + 'IniWriter doesn\'t remove section keys with null values' + ); + + unlink($filename); + } } From c5c10adf75fd0ce75156ced6ad8f06398517f0ce Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Thu, 13 Oct 2016 08:54:30 +0200 Subject: [PATCH 126/337] MonitoredObject: Delete fallback for protected custom variables fixes #12848 --- .../library/Monitoring/Object/MonitoredObject.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php index 3214769e6..b57c4c6d4 100644 --- a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php +++ b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php @@ -446,7 +446,7 @@ abstract class MonitoredObject implements Filterable } $blacklist = array(); - $blacklistPattern = '/^(.*pw.*|.*pass.*|community)$/i'; + $blacklistPattern = ''; if (($blacklistConfig = Config::module('monitoring')->get('security', 'protected_customvars', '')) !== '') { foreach (explode(',', $blacklistConfig) as $customvar) { @@ -469,7 +469,10 @@ abstract class MonitoredObject implements Filterable $this->customvars = $customvars; $this->hideBlacklistedProperties(); - $this->customvars = $this->obfuscateCustomVars($this->customvars, $blacklistPattern); + + if ($blacklistPattern) { + $this->customvars = $this->obfuscateCustomVars($this->customvars, $blacklistPattern); + } return $this; } From c868789472bd74423c63b048c4367956722c61a4 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Thu, 13 Oct 2016 10:52:54 +0200 Subject: [PATCH 127/337] Url: Add alternative attributes for baseUrl refs #12133 --- library/Icinga/Web/Url.php | 151 +++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index b61fd990f..3fec8e385 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -52,6 +52,41 @@ class Url */ protected $baseUrl = ''; + /** + * The host of the Url + * + * @var string + */ + protected $host; + + /** + * The port auf the Url + * + * @var string + */ + protected $port; + + /** + * The scheme of the Url + * + * @var string + */ + protected $scheme; + + /** + * The username passed with the Url + * + * @var string + */ + protected $username; + + /** + * The password passed with the Url + * + * @var string + */ + protected $password; + protected function __construct() { $this->params = UrlParams::fromQueryString(''); // TODO: ::create() @@ -228,6 +263,76 @@ class Url return $this; } + + /** + * Overwrite the host + * + * @param string $host New host of this Url + * + * @return $this + */ + public function setHost($host) + { + $this->host = $host; + return $this; + } + + /** + * Return the host set for this Url + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Overwrite the port + * + * @param string $port New port of this Url + * + * @return $this + */ + public function setPort($port) + { + $this->port = $port; + return $this; + } + + /** + * Return the port set for this url + * + * @return string + */ + public function getPort() + { + return $this->port; + } + + /** + * Overwrite the scheme + * + * @param string $scheme The scheme used for this url + * + * @return $this + */ + public function setScheme($scheme) + { + $this->scheme = $scheme; + return $this; + } + + /** + * Return the scheme set for this url + * + * @return string + */ + public function getScheme() + { + return $this->scheme; + } + /** * Overwrite the baseUrl * @@ -299,6 +404,52 @@ class Url return $this->external; } + /** + * Set the username passed with the url + * + * @param string $username The username to set + * + * @return $this + */ + public function setUsername($username) + { + $this->username = $username; + return $this; + } + + /** + * Return the username passed with the url + * + * @return string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Set the username passed with the url + * + * @param string $password The password to set + * + * @return $this + */ + public function setPassword($password) + { + $this->password = $password; + return $this; + } + + /** + * Return the password passed with the url + * + * @return string + */ + public function getPassword() + { + return $this->password; + } + /** * Return the relative url * From f557b89aea1a41cec89ddbdf67d8fa1e77115b8e Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Thu, 13 Oct 2016 11:01:39 +0200 Subject: [PATCH 128/337] Url: Add support for username and password fixes #12133 --- library/Icinga/Web/Url.php | 93 ++++++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 28 deletions(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 3fec8e385..eee57d82c 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -183,33 +183,16 @@ class Url || (isset($urlParts['host']) && $urlParts['host'] !== $request->getServer('SERVER_NAME')) || (isset($urlParts['port']) && $urlParts['port'] != $request->getServer('SERVER_PORT'))) ) { - $baseUrl = $urlParts['scheme'] . '://' . $urlParts['host'] . (isset($urlParts['port']) - ? (':' . $urlParts['port']) - : ''); $urlObject->setIsExternal(); - } else { - $baseUrl = ''; } if (isset($urlParts['path'])) { $urlPath = $urlParts['path']; if ($urlPath && $urlPath[0] === '/') { - if ($baseUrl) { - $urlPath = substr($urlPath, 1); - } else { - $requestBaseUrl = $request->getBaseUrl(); - if ($requestBaseUrl && $requestBaseUrl !== '/' && strpos($urlPath, $requestBaseUrl) === 0) { - $urlPath = substr($urlPath, strlen($requestBaseUrl) + 1); - $baseUrl = $requestBaseUrl; - } - } - } elseif (! $baseUrl) { - $baseUrl = $request->getBaseUrl(); + $urlPath = substr($urlPath, 1); } $urlObject->setPath($urlPath); - } elseif (! $baseUrl) { - $baseUrl = $request->getBaseUrl(); } // TODO: This has been used by former filter implementation, remove it: @@ -221,7 +204,26 @@ class Url $urlObject->setAnchor($urlParts['fragment']); } - $urlObject->setBaseUrl($baseUrl); + if (isset($urlParts['host'])) { + $urlObject->setHost($urlParts['host']); + } + + if (isset($urlParts['port'])) { + $urlObject->setPort($urlParts['port']); + } + + if (isset($urlParts['scheme'])) { + $urlObject->setScheme($urlParts['scheme']); + } + + if (isset($urlParts['user'])) { + $urlObject->setUsername($urlParts['user']); + } + + if (isset($urlParts['pass'])) { + $urlObject->setPassword($urlParts['pass']); + } + $urlObject->setParams($params); return $urlObject; } @@ -336,24 +338,46 @@ class Url /** * Overwrite the baseUrl * - * @param string $baseUrl The url path to use as the Url Base + * @deprecated Please create a new url from scratch instead * - * @return $this + * @param string $baseUrl The url path to use as the Url Base + * + * @return $this */ public function setBaseUrl($baseUrl) { - $this->baseUrl = rtrim($baseUrl, '/ '); + $urlParts = parse_url($baseUrl); + if (isset($urlParts["host"])) { + $this->setHost($urlParts["host"]); + } + if (isset($urlParts["port"])) { + $this->setPort($urlParts["port"]); + } + if (isset($urlParts["scheme"])) { + $this->setScheme($urlParts["scheme"]); + } + if (isset($urlParts["user"])) { + $this->setUsername($urlParts["user"]); + } + if (isset($urlParts["pass"])) { + $this->setPassword($urlParts["pass"]); + } return $this; } /** - * Return the baseUrl set for this url + * Return the baseUrl for this url + * + * @deprecated * * @return string */ public function getBaseUrl() { - return $this->baseUrl; + if (!$this->isExternal()) { + return ''; + } + return $this->getScheme() . '://' . $this->getHost() . ($this->getPort() ? (':' . $this->getPort()) : ''); } /** @@ -508,12 +532,25 @@ class Url return $path; } - $baseUrl = $this->getBaseUrl(); - if (! $baseUrl) { - $baseUrl = '/'; + if (!$this->isExternal()) { + return '/icingaweb2/' . $path; } - return $baseUrl . ($baseUrl !== '/' && $path ? '/' : '') . $path; + $urlString = ''; + if ($this->getScheme()) { + $urlString = $urlString . $this->getScheme() . '://'; + } + if ($this->getUsername() && $this->getPassword()) { + $urlString = $urlString . $this->getUsername() . ':' . $this->getPassword() . "@"; + } + if ($this->getHost()) { + $urlString = $urlString . $this->getHost(); + } + if ($this->getPort()) { + $urlString = $urlString . $this->getPort(); + } + + return $urlString . '/' . $path; } /** From 0fd5e8e4e892adfc2ebb73f5d5636dcb38f53ee4 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 13 Oct 2016 12:06:23 +0200 Subject: [PATCH 129/337] ErrorController: Log the full stacktrace in case of unhandled exceptions --- application/controllers/ErrorController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/controllers/ErrorController.php b/application/controllers/ErrorController.php index 101d48585..b0010385d 100644 --- a/application/controllers/ErrorController.php +++ b/application/controllers/ErrorController.php @@ -81,7 +81,7 @@ class ErrorController extends ActionController break; default: $this->getResponse()->setHttpResponseCode(500); - Logger::error($exception); + Logger::error("%s\n%s", $exception, $exception->getTraceAsString()); break; } $this->view->message = $exception->getMessage(); From e4695b6937e30940558e69693f6fbd36dcdb52cf Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 13 Oct 2016 12:07:36 +0200 Subject: [PATCH 130/337] Doc: Mention PHP cURL extension requirement for using the Icinga 2 API refs #11398 --- doc/02-Installation.md | 1 + modules/monitoring/doc/commandtransports.md | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/02-Installation.md b/doc/02-Installation.md index 0f81c6e92..789c2e128 100644 --- a/doc/02-Installation.md +++ b/doc/02-Installation.md @@ -15,6 +15,7 @@ thoroughly. * Icinga 1.x w/ IDO; Icinga 2.x w/ IDO feature enabled * The IDO table prefix must be icinga_ which is the default * MySQL or PostgreSQL PHP libraries +* cURL PHP library when using the Icinga 2 API for transmitting external commands ### PageSpeed Module Incompatibility diff --git a/modules/monitoring/doc/commandtransports.md b/modules/monitoring/doc/commandtransports.md index ba9a3be54..dd29e98dc 100644 --- a/modules/monitoring/doc/commandtransports.md +++ b/modules/monitoring/doc/commandtransports.md @@ -17,7 +17,9 @@ The order in which Icinga Web 2 processes the configured transports is defined b If you're running Icinga 2 it's best to use the Icinga 2 API for transmitting external commands. -First, you have to enable the `api` feature on the Icinga 2 host where you want to send the commands to: +First, please make sure that your server running Icinga Web 2 has the `PHP cURL` extension installed and enabled. + +Second, you have to enable the `api` feature on the Icinga 2 host where you want to send the commands to: ``` icinga2 feature enable api From 2e24833d7cb474ec9614f44496bcf3e589f386af Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Fri, 14 Oct 2016 08:06:05 +0200 Subject: [PATCH 131/337] LessCompiler: Remove error message when switching to Icinga theme fixes #12660 --- library/Icinga/Web/LessCompiler.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Web/LessCompiler.php b/library/Icinga/Web/LessCompiler.php index 7ec34a143..0419e1518 100644 --- a/library/Icinga/Web/LessCompiler.php +++ b/library/Icinga/Web/LessCompiler.php @@ -122,7 +122,9 @@ class LessCompiler if (is_file($theme) && is_readable($theme)) { $this->theme = $theme; } else { - Logger::error('Can\t load theme %s. Make sure that the theme exists and is readable', $theme); + if ($theme !== '/vagrant/public/css/themes/Icinga.less') { + Logger::error('Can\t load theme %s. Make sure that the theme exists and is readable', $theme); + } } return $this; } From 8294471253e58331662759a609b3da730e9f311c Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Fri, 14 Oct 2016 09:39:24 +0200 Subject: [PATCH 132/337] Revert "LessCompiler: Remove error message when switching to Icinga theme" This reverts commit 2e24833d7cb474ec9614f44496bcf3e589f386af. --- library/Icinga/Web/LessCompiler.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/library/Icinga/Web/LessCompiler.php b/library/Icinga/Web/LessCompiler.php index 0419e1518..7ec34a143 100644 --- a/library/Icinga/Web/LessCompiler.php +++ b/library/Icinga/Web/LessCompiler.php @@ -122,9 +122,7 @@ class LessCompiler if (is_file($theme) && is_readable($theme)) { $this->theme = $theme; } else { - if ($theme !== '/vagrant/public/css/themes/Icinga.less') { - Logger::error('Can\t load theme %s. Make sure that the theme exists and is readable', $theme); - } + Logger::error('Can\t load theme %s. Make sure that the theme exists and is readable', $theme); } return $this; } From 0cd2d98f71fba8b086ab27941632005b9eef181e Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Fri, 14 Oct 2016 13:24:17 +0200 Subject: [PATCH 133/337] StyleSheet: Don't try to load the icinga default theme refs #12660 --- library/Icinga/Web/StyleSheet.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/StyleSheet.php b/library/Icinga/Web/StyleSheet.php index 8a7ffa3e5..549e11c5c 100644 --- a/library/Icinga/Web/StyleSheet.php +++ b/library/Icinga/Web/StyleSheet.php @@ -127,7 +127,7 @@ class StyleSheet } } - if ($theme) { + if ($theme && $theme !== self::DEFAULT_THEME) { if (($pos = strpos($theme, '/')) !== false) { $moduleName = substr($theme, 0, $pos); $theme = substr($theme, $pos + 1); From 3e2f3c8467c4c010211bc54389d0ef442627eb4e Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Fri, 14 Oct 2016 15:01:03 +0200 Subject: [PATCH 134/337] Url: Add basePath attribute refs #12133 --- library/Icinga/Web/Url.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index eee57d82c..43de21f11 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -45,6 +45,13 @@ class Url */ protected $path = ''; + /** + * The basePath of this Url + * + * @var string + */ + protected $basePath; + /** * The baseUrl that will be appended to @see Url::$path * @@ -266,6 +273,29 @@ class Url } + /** + * Set the basePath for this url + * + * @param string $basePath New basePath of this url + * + * @return $this + */ + public function setBasePath($basePath) + { + $this->basePath = $basePath; + return $this; + } + + /** + * Return the basePath set for this url + * + * @return string + */ + public function getBasePath() + { + return $this->basePath; + } + /** * Overwrite the host * From ef359aa686a5b3f44374824dc1eb6d203e9ba7f1 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Fri, 14 Oct 2016 15:04:03 +0200 Subject: [PATCH 135/337] Url: Improve documentation refs #12133 --- library/Icinga/Web/Url.php | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 43de21f11..548de4331 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -206,27 +206,21 @@ class Url if (isset($urlParts['query'])) { $params = UrlParams::fromQueryString($urlParts['query'])->mergeValues($params); } - if (isset($urlParts['fragment'])) { $urlObject->setAnchor($urlParts['fragment']); } - if (isset($urlParts['host'])) { $urlObject->setHost($urlParts['host']); } - if (isset($urlParts['port'])) { $urlObject->setPort($urlParts['port']); } - if (isset($urlParts['scheme'])) { $urlObject->setScheme($urlParts['scheme']); } - if (isset($urlParts['user'])) { $urlObject->setUsername($urlParts['user']); } - if (isset($urlParts['pass'])) { $urlObject->setPassword($urlParts['pass']); } @@ -310,7 +304,7 @@ class Url } /** - * Return the host set for this Url + * Return the host set for this url * * @return string */ @@ -320,9 +314,9 @@ class Url } /** - * Overwrite the port + * Set the port for this url * - * @param string $port New port of this Url + * @param string $port New port of this url * * @return $this */ @@ -343,7 +337,7 @@ class Url } /** - * Overwrite the scheme + * Set the scheme for this url * * @param string $scheme The scheme used for this url * @@ -366,11 +360,11 @@ class Url } /** - * Overwrite the baseUrl + * Set the baseUrl for this url * * @deprecated Please create a new url from scratch instead * - * @param string $baseUrl The url path to use as the Url Base + * @param string $baseUrl The url path to use as the url base * * @return $this */ @@ -392,6 +386,7 @@ class Url if (isset($urlParts["pass"])) { $this->setPassword($urlParts["pass"]); } + return $this; } @@ -407,6 +402,7 @@ class Url if (!$this->isExternal()) { return ''; } + return $this->getScheme() . '://' . $this->getHost() . ($this->getPort() ? (':' . $this->getPort()) : ''); } From 9439dfa8f3109ba40d740c4d9810ffdcf11b3f1c Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Fri, 14 Oct 2016 15:05:16 +0200 Subject: [PATCH 136/337] Url: Fix path handling for internal urls refs #12133 --- library/Icinga/Web/Url.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 548de4331..3b25d7786 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -191,6 +191,8 @@ class Url || (isset($urlParts['port']) && $urlParts['port'] != $request->getServer('SERVER_PORT'))) ) { $urlObject->setIsExternal(); + } else { + $urlObject->setBasePath($request->getBaseUrl()); } if (isset($urlParts['path'])) { @@ -558,8 +560,12 @@ class Url return $path; } + $basePath = $this->getBasePath(); + if (!$basePath) { + $basePath = '/'; + } if (!$this->isExternal()) { - return '/icingaweb2/' . $path; + return $basePath . ($basePath !== '/' && $path ? '/' : '') . $path; } $urlString = ''; From 7cf6b860b5cfa5e0d717b31a45c18fb182637543 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Fri, 14 Oct 2016 16:12:47 +0200 Subject: [PATCH 137/337] Response: Add contentType attribute refs #12161 --- library/Icinga/Web/Response.php | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/library/Icinga/Web/Response.php b/library/Icinga/Web/Response.php index af3e1e27e..71536c239 100644 --- a/library/Icinga/Web/Response.php +++ b/library/Icinga/Web/Response.php @@ -54,6 +54,13 @@ class Response extends Zend_Controller_Response_Http */ protected $rerenderLayout = false; + /** + * Content type of this response + * + * @var string + */ + protected $contentType = 'text/html'; + /** * Get the auto-refresh interval * @@ -205,6 +212,30 @@ class Response extends Zend_Controller_Response_Http return $this; } + /** + * Set the content type of this response + * + * @param string $contentType + * + * @return $this + */ + public function setContentType($contentType) + { + $this->contentType = $contentType; + return $this; + } + + /** + * Get the content type of this response + * + * @return string + */ + public function getContentType() + { + return $this->contentType; + } + + /** * Entry point for HTTP responses in JSON format * From af5c578adfd1497a98e202ca24300d30ad4fe2ff Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Fri, 14 Oct 2016 16:16:13 +0200 Subject: [PATCH 138/337] Response: Set header Content-Type for every response by default refs #12161 --- library/Icinga/Web/Response.php | 2 ++ library/Icinga/Web/Response/JsonResponse.php | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Web/Response.php b/library/Icinga/Web/Response.php index 71536c239..11773a594 100644 --- a/library/Icinga/Web/Response.php +++ b/library/Icinga/Web/Response.php @@ -275,6 +275,8 @@ class Response extends Zend_Controller_Response_Http $this->setRedirect($redirectUrl->getAbsoluteUrl()); } } + + $this->setHeader("Content-Type", $this->getContentType(), true); } /** diff --git a/library/Icinga/Web/Response/JsonResponse.php b/library/Icinga/Web/Response/JsonResponse.php index 8a9ba7d33..21614149f 100644 --- a/library/Icinga/Web/Response/JsonResponse.php +++ b/library/Icinga/Web/Response/JsonResponse.php @@ -191,7 +191,7 @@ class JsonResponse extends Response */ public function sendHeaders() { - $this->setHeader('Content-Type', 'application/json', true); + $this->setContentType('application/json'); parent::sendHeaders(); } From 4df9696ed97e5b2cfe0451ff3d8c6a902620b99e Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Mon, 17 Oct 2016 08:29:51 +0200 Subject: [PATCH 139/337] StyleSheet: Modify send method to use setContentType of Response class refs #12161 --- library/Icinga/Web/StyleSheet.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/StyleSheet.php b/library/Icinga/Web/StyleSheet.php index 549e11c5c..e74f3c955 100644 --- a/library/Icinga/Web/StyleSheet.php +++ b/library/Icinga/Web/StyleSheet.php @@ -197,7 +197,7 @@ class StyleSheet $response ->setHeader('Cache-Control', 'public', true) ->setHeader('ETag', $etag, true) - ->setHeader('Content-Type', 'text/css', true); + ->setContentType('text/css'); $cacheFile = 'icinga-' . $etag . ($minified ? '.min' : '') . '.css'; $cache = FileCache::instance(); From 9ec471d34965198d2cfd5f2b9a6bbb76883023e0 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Thu, 13 Oct 2016 15:53:46 +0200 Subject: [PATCH 140/337] DataView: Apply object restrictions to dynamic filter columns fixes #9993 --- .../library/Monitoring/DataView/DataView.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/modules/monitoring/library/Monitoring/DataView/DataView.php b/modules/monitoring/library/Monitoring/DataView/DataView.php index e71469804..1f63c78a8 100644 --- a/modules/monitoring/library/Monitoring/DataView/DataView.php +++ b/modules/monitoring/library/Monitoring/DataView/DataView.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Monitoring\DataView; use IteratorAggregate; use Icinga\Application\Hook; +use Icinga\Authentication\Auth; use Icinga\Data\ConnectionInterface; use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\FilterMatch; @@ -264,12 +265,24 @@ abstract class DataView implements QueryInterface, SortRules, FilterColumns, Ite return $columns; } + $restriction = Filter::matchAny(); + $restrictions = Auth::getInstance()->getRestrictions('monitoring/filter/objects'); + foreach ($restrictions as $filter) { + if ($filter === '*') { + $restriction = Filter::matchAny(); + break; + } + $restriction->addFilter(Filter::fromQueryString($filter)); + } + $query = MonitoringBackend::instance() ->select() ->from('customvar', array('varname', 'object_type')) ->where('is_json', 0) ->where('object_type_id', array(1, 2)) + ->applyFilter($restriction) ->getQuery()->group(array('varname', 'object_type')); + foreach ($query as $row) { if ($row->object_type === 'host') { $label = t('Host') . ' ' . ucwords(str_replace('_', ' ', $row->varname)); From 5c2711872ae0e2f0ff5828da031fc36f2934ed6d Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Fri, 14 Oct 2016 15:16:39 +0200 Subject: [PATCH 141/337] StatehistoryForm: Remove autoupdate on clicking the select-boxes refs #7755 --- .../application/forms/StatehistoryForm.php | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/modules/monitoring/application/forms/StatehistoryForm.php b/modules/monitoring/application/forms/StatehistoryForm.php index 200830567..3a7c10df5 100644 --- a/modules/monitoring/application/forms/StatehistoryForm.php +++ b/modules/monitoring/application/forms/StatehistoryForm.php @@ -71,8 +71,7 @@ class StatehistoryForm extends Form strtotime('midnight 8 months ago') => $this->translate('8 Months'), strtotime('midnight 12 months ago') => $this->translate('1 Year'), strtotime('midnight 24 months ago') => $this->translate('2 Years') - ), - 'class' => 'autosubmit' + ) ) ); $this->addElement( @@ -83,8 +82,7 @@ class StatehistoryForm extends Form 'value' => $this->getRequest()->getParam('to', time()), 'multiOptions' => array( time() => $this->translate('Today') - ), - 'class' => 'autosubmit' + ) ) ); @@ -98,8 +96,7 @@ class StatehistoryForm extends Form 'multiOptions' => array( 'services' => $this->translate('Services'), 'hosts' => $this->translate('Hosts') - ), - 'class' => 'autosubmit' + ) ) ); if ($objectType === 'services') { @@ -118,8 +115,7 @@ class StatehistoryForm extends Form 'cnt_warning_hard' => $this->translate('Warning'), 'cnt_unknown_hard' => $this->translate('Unknown'), 'cnt_ok' => $this->translate('Ok') - ), - 'class' => 'autosubmit' + ) ) ); } else { @@ -137,8 +133,7 @@ class StatehistoryForm extends Form 'cnt_up' => $this->translate('Up'), 'cnt_down_hard' => $this->translate('Down'), 'cnt_unreachable_hard' => $this->translate('Unreachable') - ), - 'class' => 'autosubmit' + ) ) ); } From 2439460371df13c2caf5d6c27f2a081e2c0eee4e Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Mon, 17 Oct 2016 10:16:03 +0200 Subject: [PATCH 142/337] Url: Fix incorrect line indentation in method getAbsoluteUrl refs #12133 --- library/Icinga/Web/Url.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 3b25d7786..ba77b3b03 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -562,7 +562,7 @@ class Url $basePath = $this->getBasePath(); if (!$basePath) { - $basePath = '/'; + $basePath = '/'; } if (!$this->isExternal()) { return $basePath . ($basePath !== '/' && $path ? '/' : '') . $path; From c57a6362908b117b46bb1e3699940d7ff4837d81 Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Mon, 17 Oct 2016 10:28:50 +0200 Subject: [PATCH 143/337] MonitoredObject: Fix incorrect line indentation in method fetchCustomvars --- .../monitoring/library/Monitoring/Object/MonitoredObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php index b57c4c6d4..6fb73112f 100644 --- a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php +++ b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php @@ -446,7 +446,7 @@ abstract class MonitoredObject implements Filterable } $blacklist = array(); - $blacklistPattern = ''; + $blacklistPattern = ''; if (($blacklistConfig = Config::module('monitoring')->get('security', 'protected_customvars', '')) !== '') { foreach (explode(',', $blacklistConfig) as $customvar) { From a0a203c875a5031c5a487d93d11848d8ae87f21b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 17 Oct 2016 12:44:05 +0200 Subject: [PATCH 144/337] Make timeline working again refs #12889 --- .../Monitoring/Backend/Ido/Query/HostnotificationQuery.php | 1 + .../Monitoring/Backend/Ido/Query/ServicenotificationQuery.php | 1 + 2 files changed, 2 insertions(+) diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostnotificationQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostnotificationQuery.php index de4d37807..9b7d3e5c8 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostnotificationQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostnotificationQuery.php @@ -30,6 +30,7 @@ class HostnotificationQuery extends IdoQuery 'output' => null, 'state' => 'hn.state', 'timestamp' => 'UNIX_TIMESTAMP(hn.start_time)', + 'type' => '(\'notify\')' ), 'instances' => array( 'instance_name' => 'i.instance_name' diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicenotificationQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicenotificationQuery.php index 4d7baa7ab..d990bb0fb 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicenotificationQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicenotificationQuery.php @@ -24,6 +24,7 @@ class ServicenotificationQuery extends IdoQuery 'output' => null, 'state' => 'sn.state', 'timestamp' => 'UNIX_TIMESTAMP(sn.start_time)', + 'type' => '(\'notify\')' ), 'hostgroups' => array( 'hostgroup_name' => 'hgo.name1' From 99866bfdbea99667ff7cf5bf27dfbbbb7efd5d05 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 17 Oct 2016 13:51:58 +0200 Subject: [PATCH 145/337] IniWriterTest: make recently added tests more expressive refs #11743 --- .../library/Icinga/File/Ini/IniWriterTest.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/php/library/Icinga/File/Ini/IniWriterTest.php b/test/php/library/Icinga/File/Ini/IniWriterTest.php index e4bfd2716..9e38aa9bd 100644 --- a/test/php/library/Icinga/File/Ini/IniWriterTest.php +++ b/test/php/library/Icinga/File/Ini/IniWriterTest.php @@ -409,9 +409,9 @@ EOD; $config->setSection('garbage', $section); $iniWriter = new IniWriter($config, '/dev/null'); - $this->assertEquals( - 0, - preg_match('/foobar/', $iniWriter->render()), + $this->assertNotContains( + 'foobar', + $iniWriter->render(), 'IniWriter persists section keys with null values' ); } @@ -424,9 +424,9 @@ EOD; $config->setSection('garbage', $section); $iniWriter = new IniWriter($config, '/dev/null'); - $this->assertEquals( - 1, - preg_match('/foobar/', $iniWriter->render()), + $this->assertContains( + 'foobar', + $iniWriter->render(), 'IniWriter doesn\'t persist section keys with empty values' ); } @@ -441,9 +441,9 @@ EOD; $section = $config->getSection('garbage'); $section->foobar = null; $iniWriter = new IniWriter($config, $filename); - $this->assertEquals( - 0, - preg_match('/foobar/', $iniWriter->render()), + $this->assertNotContains( + 'foobar', + $iniWriter->render(), 'IniWriter doesn\'t remove section keys with null values' ); From d0b93b064c72ecfc8bbffbfe12d7b8fe20f69db1 Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Mon, 17 Oct 2016 14:46:58 +0200 Subject: [PATCH 146/337] UrlTest: Test whether getAbsoluteUrl returns the given username and password refs #12133 --- test/php/library/Icinga/Web/UrlTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/php/library/Icinga/Web/UrlTest.php b/test/php/library/Icinga/Web/UrlTest.php index 0481d723b..e4648ba0d 100644 --- a/test/php/library/Icinga/Web/UrlTest.php +++ b/test/php/library/Icinga/Web/UrlTest.php @@ -9,6 +9,15 @@ use Icinga\Test\BaseTestCase; class UrlTest extends BaseTestCase { + public function testWhetherGetAbsoluteUrlReturnsTestUrl(){ + $url = Url::fromPath('http://testusername:testpassword@testsite.com/path/to/my/url.html'); + $this->assertEquals( + 'http://testusername:testpassword@testsite.com/path/to/my/url.html', + $url->getAbsoluteUrl(), + 'Url::fromPath does not reassemble the correct url' + ); + } + public function testWhetherFromRequestWorksWithoutARequest() { $this->getRequestMock()->shouldReceive('getBaseUrl')->andReturn('/path/to') From 7df6e6cb631f8731ecc9d4c45342926ef218f667 Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Mon, 17 Oct 2016 14:57:30 +0200 Subject: [PATCH 147/337] UrlTest: Fix method name and PSR issue refs #12133 --- test/php/library/Icinga/Web/UrlTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/php/library/Icinga/Web/UrlTest.php b/test/php/library/Icinga/Web/UrlTest.php index e4648ba0d..56ac1eefc 100644 --- a/test/php/library/Icinga/Web/UrlTest.php +++ b/test/php/library/Icinga/Web/UrlTest.php @@ -9,7 +9,8 @@ use Icinga\Test\BaseTestCase; class UrlTest extends BaseTestCase { - public function testWhetherGetAbsoluteUrlReturnsTestUrl(){ + public function testWhetherGetAbsoluteUrlReturnsTheGivenUsernameAndPassword() + { $url = Url::fromPath('http://testusername:testpassword@testsite.com/path/to/my/url.html'); $this->assertEquals( 'http://testusername:testpassword@testsite.com/path/to/my/url.html', From e2f6c81bfa41fe78cd9c3f8db3aeb09cfb2cc0d1 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 17 Oct 2016 15:00:43 +0200 Subject: [PATCH 148/337] Remove obsolete workarounds refs #11743 --- application/forms/Config/UserBackendConfigForm.php | 6 ------ application/forms/Navigation/NavigationConfigForm.php | 5 ----- .../application/forms/Config/BackendConfigForm.php | 6 ------ .../application/forms/Config/TransportConfigForm.php | 6 ------ 4 files changed, 23 deletions(-) diff --git a/application/forms/Config/UserBackendConfigForm.php b/application/forms/Config/UserBackendConfigForm.php index 421313820..f7d4d1572 100644 --- a/application/forms/Config/UserBackendConfigForm.php +++ b/application/forms/Config/UserBackendConfigForm.php @@ -200,12 +200,6 @@ class UserBackendConfigForm extends ConfigForm } $backendConfig->merge($data); - foreach ($backendConfig->toArray() as $k => $v) { - if ($v === null) { - unset($backendConfig->$k); - } - } - $this->config->setSection($name, $backendConfig); return $this; } diff --git a/application/forms/Navigation/NavigationConfigForm.php b/application/forms/Navigation/NavigationConfigForm.php index 158875c87..9bfc3a02a 100644 --- a/application/forms/Navigation/NavigationConfigForm.php +++ b/application/forms/Navigation/NavigationConfigForm.php @@ -426,11 +426,6 @@ class NavigationConfigForm extends ConfigForm } $itemConfig->merge($data); - foreach ($itemConfig->toArray() as $k => $v) { - if ($v === null) { - unset($itemConfig->$k); - } - } if ($shared) { // Share all descendant children diff --git a/modules/monitoring/application/forms/Config/BackendConfigForm.php b/modules/monitoring/application/forms/Config/BackendConfigForm.php index 78cb049de..e7fdba67c 100644 --- a/modules/monitoring/application/forms/Config/BackendConfigForm.php +++ b/modules/monitoring/application/forms/Config/BackendConfigForm.php @@ -147,12 +147,6 @@ class BackendConfigForm extends ConfigForm } $backendConfig->merge($data); - foreach ($backendConfig->toArray() as $k => $v) { - if ($v === null) { - unset($backendConfig->$k); - } - } - $this->config->setSection($name, $backendConfig); return $this; } diff --git a/modules/monitoring/application/forms/Config/TransportConfigForm.php b/modules/monitoring/application/forms/Config/TransportConfigForm.php index 0e5ad4f49..79ac1741b 100644 --- a/modules/monitoring/application/forms/Config/TransportConfigForm.php +++ b/modules/monitoring/application/forms/Config/TransportConfigForm.php @@ -167,12 +167,6 @@ class TransportConfigForm extends ConfigForm } $transportConfig->merge($data); - foreach ($transportConfig->toArray() as $k => $v) { - if ($v === null) { - unset($transportConfig->$k); - } - } - $this->config->setSection($name, $transportConfig); return $this; } From c3276d4341bbbde86f6a0f52dbc8535a7cdb2ff5 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Mon, 17 Oct 2016 16:03:33 +0200 Subject: [PATCH 149/337] Response: Remove empty line after method getContentType refs #12161 --- library/Icinga/Web/Response.php | 1 - 1 file changed, 1 deletion(-) diff --git a/library/Icinga/Web/Response.php b/library/Icinga/Web/Response.php index 11773a594..cdb377efd 100644 --- a/library/Icinga/Web/Response.php +++ b/library/Icinga/Web/Response.php @@ -235,7 +235,6 @@ class Response extends Zend_Controller_Response_Http return $this->contentType; } - /** * Entry point for HTTP responses in JSON format * From 29c221418b4049aab1d20c316806a5440b312fae Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 17 Oct 2016 16:19:26 +0200 Subject: [PATCH 150/337] External authentication: respect REDIRECT_REMOTE_USER as well refs #12164 --- .../Authentication/User/ExternalBackend.php | 20 ++++++++++--------- .../application/forms/AdminAccountPage.php | 5 +++-- .../application/forms/AuthenticationPage.php | 4 +++- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/library/Icinga/Authentication/User/ExternalBackend.php b/library/Icinga/Authentication/User/ExternalBackend.php index 3baf1c8e0..7e9f7baba 100644 --- a/library/Icinga/Authentication/User/ExternalBackend.php +++ b/library/Icinga/Authentication/User/ExternalBackend.php @@ -55,18 +55,20 @@ class ExternalBackend implements UserBackendInterface /** * Get the remote user from environment or $_SERVER, if any * - * @param string $variable The name variable where to read the user from + * @param string|null $variable The name variable where to read the user from * * @return string|null */ public static function getRemoteUser($variable = 'REMOTE_USER') { - $username = getenv($variable); - if ($username !== false) { - return $username; - } - if (array_key_exists($variable, $_SERVER)) { - return $_SERVER[$variable]; + foreach (($variable === null ? array('REMOTE_USER', 'REDIRECT_REMOTE_USER') : array($variable)) as $variable) { + $username = getenv($variable); + if ($username !== false) { + return $username; + } + if (array_key_exists($variable, $_SERVER)) { + return $_SERVER[$variable]; + } } return null; } @@ -77,9 +79,9 @@ class ExternalBackend implements UserBackendInterface */ public function authenticate(User $user, $password = null) { - $username = static::getRemoteUser(); + $username = static::getRemoteUser(null); if ($username !== null) { - $user->setExternalUserInformation($username, 'REMOTE_USER'); + $user->setExternalUserInformation($username, null); if ($this->stripUsernameRegexp) { $stripped = preg_replace($this->stripUsernameRegexp, '', $username); diff --git a/modules/setup/application/forms/AdminAccountPage.php b/modules/setup/application/forms/AdminAccountPage.php index 439a3beb2..6e8fe26dc 100644 --- a/modules/setup/application/forms/AdminAccountPage.php +++ b/modules/setup/application/forms/AdminAccountPage.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Setup\Forms; use Exception; use Icinga\Application\Config; +use Icinga\Authentication\User\ExternalBackend; use Icinga\Authentication\User\UserBackend; use Icinga\Authentication\User\DbUserBackend; use Icinga\Authentication\User\LdapUserBackend; @@ -269,8 +270,8 @@ class AdminAccountPage extends Form */ protected function getUsername() { - $name = getenv('REMOTE_USER'); - if ($name === false) { + $name = ExternalBackend::getRemoteUser(null); + if ($name === null) { return ''; } diff --git a/modules/setup/application/forms/AuthenticationPage.php b/modules/setup/application/forms/AuthenticationPage.php index 132f9377b..d90b52a09 100644 --- a/modules/setup/application/forms/AuthenticationPage.php +++ b/modules/setup/application/forms/AuthenticationPage.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Setup\Forms; +use Icinga\Authentication\User\ExternalBackend; use Icinga\Web\Form; use Icinga\Application\Platform; @@ -30,7 +31,8 @@ class AuthenticationPage extends Form */ public function createElements(array $formData) { - if (isset($formData['type']) && $formData['type'] === 'external' && getenv('REMOTE_USER') === false) { + if (isset($formData['type']) && $formData['type'] === 'external' + && ExternalBackend::getRemoteUser(null) === null) { $this->info( $this->translate( 'You\'re currently not authenticated using any of the web server\'s authentication ' From 703561f8749acc064ffdbf5c90b599790e5efef6 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Mon, 17 Oct 2016 16:33:51 +0200 Subject: [PATCH 151/337] JsonResponse: Set content type at initialization and not at sendHeaders refs #12161 --- library/Icinga/Web/Response/JsonResponse.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/library/Icinga/Web/Response/JsonResponse.php b/library/Icinga/Web/Response/JsonResponse.php index 21614149f..47a2ccd74 100644 --- a/library/Icinga/Web/Response/JsonResponse.php +++ b/library/Icinga/Web/Response/JsonResponse.php @@ -67,6 +67,13 @@ class JsonResponse extends Response */ protected $successData; + /** + * Content type of this response + * + * @var string + */ + protected $contentType = 'application/json'; + /** * Get the JSON encoding options * @@ -186,15 +193,6 @@ class JsonResponse extends Response echo json_encode($body, $this->getEncodingOptions()); } - /** - * {@inheritdoc} - */ - public function sendHeaders() - { - $this->setContentType('application/json'); - parent::sendHeaders(); - } - /** * {@inheritdoc} */ From c927c3244261ca36dd341652e1791c069a16f01a Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Mon, 17 Oct 2016 16:34:07 +0200 Subject: [PATCH 152/337] Response: Only set header Content-Type in method prepare if not already set refs #12161 --- library/Icinga/Web/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Response.php b/library/Icinga/Web/Response.php index cdb377efd..d96972de6 100644 --- a/library/Icinga/Web/Response.php +++ b/library/Icinga/Web/Response.php @@ -275,7 +275,7 @@ class Response extends Zend_Controller_Response_Http } } - $this->setHeader("Content-Type", $this->getContentType(), true); + $this->setHeader('Content-Type', $this->getContentType(), false); } /** From ce951295d33ccf8b18d62c2347bce8345b8caac2 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 17 Oct 2016 18:46:00 +0200 Subject: [PATCH 153/337] ExternalBackend: make the variable a webserver assigns a username to configurable refs #12164 --- .../Icinga/Authentication/User/ExternalBackend.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Authentication/User/ExternalBackend.php b/library/Icinga/Authentication/User/ExternalBackend.php index 7e9f7baba..d3631dcee 100644 --- a/library/Icinga/Authentication/User/ExternalBackend.php +++ b/library/Icinga/Authentication/User/ExternalBackend.php @@ -11,6 +11,13 @@ use Icinga\User; */ class ExternalBackend implements UserBackendInterface { + /** + * The configuration of this backend + * + * @var ConfigObject + */ + protected $config; + /** * The name of this backend * @@ -32,6 +39,7 @@ class ExternalBackend implements UserBackendInterface */ public function __construct(ConfigObject $config) { + $this->config = $config; $this->stripUsernameRegexp = $config->get('strip_username_regexp'); } @@ -79,9 +87,10 @@ class ExternalBackend implements UserBackendInterface */ public function authenticate(User $user, $password = null) { - $username = static::getRemoteUser(null); + $usernameEnvvar = $this->config->username_envvar; + $username = static::getRemoteUser($usernameEnvvar); if ($username !== null) { - $user->setExternalUserInformation($username, null); + $user->setExternalUserInformation($username, $usernameEnvvar); if ($this->stripUsernameRegexp) { $stripped = preg_replace($this->stripUsernameRegexp, '', $username); From f95c7893530418dc4fce9b18d257308a9dba9750 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 09:58:33 +0200 Subject: [PATCH 154/337] Response: Add method getHeader refs #12161 --- library/Icinga/Web/Response.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/library/Icinga/Web/Response.php b/library/Icinga/Web/Response.php index d96972de6..2a90a97b1 100644 --- a/library/Icinga/Web/Response.php +++ b/library/Icinga/Web/Response.php @@ -153,6 +153,31 @@ class Response extends Zend_Controller_Response_Http return $this; } + /** + * Get an array of every header value with a specified name + * + * @param string $name + * @param bool $lastOnly If this is true, the last value will be returned as a string. + * + * @return null|array|string + */ + public function getHeader($name, $lastOnly = false) + { + $result = ($lastOnly ? null : array()); + $headers = $this->getHeaders(); + foreach ($headers as $header) { + if ($header['name'] === $name) { + if ($lastOnly) { + $result = $header['value']; + } else { + $result[] = $header['value']; + } + } + } + + return $result; + } + /** * Get the request * From e5c736aab4b707eba9288cf5e12ffda937bbc32b Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 10:13:58 +0200 Subject: [PATCH 155/337] Response: Use method getHeader to check if Content-Type is set already refs #12161 --- library/Icinga/Web/Response.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Web/Response.php b/library/Icinga/Web/Response.php index 2a90a97b1..ac2573f8d 100644 --- a/library/Icinga/Web/Response.php +++ b/library/Icinga/Web/Response.php @@ -300,7 +300,9 @@ class Response extends Zend_Controller_Response_Http } } - $this->setHeader('Content-Type', $this->getContentType(), false); + if (!$this->getHeader('Content-Type', true)) { + $this->setHeader('Content-Type', $this->getContentType()); + } } /** From ab01d2f91532c77133e0d62f1177d149872821a4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 18 Oct 2016 10:17:21 +0200 Subject: [PATCH 156/337] ExternalBackend: don't reference more than necessary from the config refs #12164 --- .../Authentication/User/ExternalBackend.php | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/library/Icinga/Authentication/User/ExternalBackend.php b/library/Icinga/Authentication/User/ExternalBackend.php index d3631dcee..38cc27b61 100644 --- a/library/Icinga/Authentication/User/ExternalBackend.php +++ b/library/Icinga/Authentication/User/ExternalBackend.php @@ -11,13 +11,6 @@ use Icinga\User; */ class ExternalBackend implements UserBackendInterface { - /** - * The configuration of this backend - * - * @var ConfigObject - */ - protected $config; - /** * The name of this backend * @@ -32,6 +25,13 @@ class ExternalBackend implements UserBackendInterface */ protected $stripUsernameRegexp; + /** + * The name variable where to read the user from + * + * @var string|null + */ + protected $usernameEnvvar; + /** * Create new authentication backend of type "external" * @@ -39,8 +39,8 @@ class ExternalBackend implements UserBackendInterface */ public function __construct(ConfigObject $config) { - $this->config = $config; $this->stripUsernameRegexp = $config->get('strip_username_regexp'); + $this->usernameEnvvar = $config->get('username_envvar'); } /** @@ -87,10 +87,9 @@ class ExternalBackend implements UserBackendInterface */ public function authenticate(User $user, $password = null) { - $usernameEnvvar = $this->config->username_envvar; - $username = static::getRemoteUser($usernameEnvvar); + $username = static::getRemoteUser($this->usernameEnvvar); if ($username !== null) { - $user->setExternalUserInformation($username, $usernameEnvvar); + $user->setExternalUserInformation($username, $this->usernameEnvvar); if ($this->stripUsernameRegexp) { $stripped = preg_replace($this->stripUsernameRegexp, '', $username); From 4d6160d9872e1fca21ba7aba9d78027c22c30c04 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 18 Oct 2016 10:22:06 +0200 Subject: [PATCH 157/337] ExternalBackend::getRemoteUser(): restore previous default behavior refs #12164 --- .../Authentication/User/ExternalBackend.php | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/library/Icinga/Authentication/User/ExternalBackend.php b/library/Icinga/Authentication/User/ExternalBackend.php index 38cc27b61..44cd85eb5 100644 --- a/library/Icinga/Authentication/User/ExternalBackend.php +++ b/library/Icinga/Authentication/User/ExternalBackend.php @@ -67,16 +67,18 @@ class ExternalBackend implements UserBackendInterface * * @return string|null */ - public static function getRemoteUser($variable = 'REMOTE_USER') + public static function getRemoteUser($variable = null) { - foreach (($variable === null ? array('REMOTE_USER', 'REDIRECT_REMOTE_USER') : array($variable)) as $variable) { - $username = getenv($variable); - if ($username !== false) { - return $username; - } - if (array_key_exists($variable, $_SERVER)) { - return $_SERVER[$variable]; - } + if ($variable === null) { + $variable = 'REMOTE_USER'; + } + + $username = getenv($variable); + if ($username !== false) { + return $username; + } + if (array_key_exists($variable, $_SERVER)) { + return $_SERVER[$variable]; } return null; } From 888ac98007a74dd8b45f0a61b329d0d2928812a8 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 11:07:24 +0200 Subject: [PATCH 158/337] Response: Improve documentation for method getHeader refs #12161 --- library/Icinga/Web/Response.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/Icinga/Web/Response.php b/library/Icinga/Web/Response.php index ac2573f8d..a42f48fde 100644 --- a/library/Icinga/Web/Response.php +++ b/library/Icinga/Web/Response.php @@ -154,12 +154,12 @@ class Response extends Zend_Controller_Response_Http } /** - * Get an array of every header value with a specified name + * Get an array of all header values for the given name * - * @param string $name - * @param bool $lastOnly If this is true, the last value will be returned as a string. + * @param string $name The name of the header + * @param bool $lastOnly If this is true, the last value will be returned as a string * - * @return null|array|string + * @return null|array|string */ public function getHeader($name, $lastOnly = false) { @@ -300,7 +300,7 @@ class Response extends Zend_Controller_Response_Http } } - if (!$this->getHeader('Content-Type', true)) { + if (! $this->getHeader('Content-Type', true)) { $this->setHeader('Content-Type', $this->getContentType()); } } From f6f90822d658c7156b2abd569eda89bbeb811b16 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 12:25:11 +0200 Subject: [PATCH 159/337] Url: Set basePath correctly in methods fromRequest and fromPath refs #12133 --- library/Icinga/Web/Url.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index ba77b3b03..28290e2f7 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -133,7 +133,7 @@ class Url $urlParams->set($k, $v); } $url->setParams($urlParams); - $url->setBaseUrl($request->getBaseUrl()); + $url->setBasePath($request->getBaseUrl()); return $url; } @@ -191,17 +191,27 @@ class Url || (isset($urlParts['port']) && $urlParts['port'] != $request->getServer('SERVER_PORT'))) ) { $urlObject->setIsExternal(); - } else { - $urlObject->setBasePath($request->getBaseUrl()); } if (isset($urlParts['path'])) { $urlPath = $urlParts['path']; if ($urlPath && $urlPath[0] === '/') { - $urlPath = substr($urlPath, 1); + if ($urlObject->isExternal()) { + $urlPath = substr($urlPath, 1); + } else { + $requestBaseUrl = $request->getBaseUrl(); + if ($requestBaseUrl && $requestBaseUrl !== '/' && strpos($urlPath, $requestBaseUrl) === 0) { + $urlPath = substr($urlPath, strlen($requestBaseUrl) + 1); + $urlObject->setBasePath($requestBaseUrl); + } + } + } elseif (!$urlObject->isExternal()) { + $urlObject->setBasePath($request->getBaseUrl()); } $urlObject->setPath($urlPath); + } elseif (!$urlObject->isExternal()) { + $urlObject->setBasePath($request->getBaseUrl()); } // TODO: This has been used by former filter implementation, remove it: From 4dd9c05882814be1821735e98757d46783446dd8 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 12:26:59 +0200 Subject: [PATCH 160/337] Url: Return basePath in method getBaseUrl if url is internal refs #12133 --- library/Icinga/Web/Url.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 28290e2f7..46678524f 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -412,7 +412,7 @@ class Url public function getBaseUrl() { if (!$this->isExternal()) { - return ''; + return $this->getBasePath(); } return $this->getScheme() . '://' . $this->getHost() . ($this->getPort() ? (':' . $this->getPort()) : ''); From 790d83cb72177cf3ff9495e16d81feb2c8268bf7 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 18 Oct 2016 13:44:01 +0200 Subject: [PATCH 161/337] ExternalBackendForm: make the variable a webserver assigns a username to configurable refs #12164 --- .../forms/Config/UserBackend/ExternalBackendForm.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/application/forms/Config/UserBackend/ExternalBackendForm.php b/application/forms/Config/UserBackend/ExternalBackendForm.php index f4a463911..39c46d927 100644 --- a/application/forms/Config/UserBackend/ExternalBackendForm.php +++ b/application/forms/Config/UserBackend/ExternalBackendForm.php @@ -55,6 +55,18 @@ class ExternalBackendForm extends Form 'validators' => array($callbackValidator) ) ); + $this->addElement( + 'text', + 'username_envvar', + array( + 'label' => $this->translate('Username Environment Variable'), + 'description' => $this->translate( + 'The environment variable the webserver assigns the authenticated user\'s name to.' + ), + 'required' => true, + 'value' => 'REMOTE_USER' + ) + ); $this->addElement( 'hidden', 'backend', From 011bce0909e66c3b9aed34d6807b02028ad91b67 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 14:12:38 +0200 Subject: [PATCH 162/337] UrlTest: Use getBasePath instead of getBaseUrl refs #12133 --- test/php/library/Icinga/Web/UrlTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/php/library/Icinga/Web/UrlTest.php b/test/php/library/Icinga/Web/UrlTest.php index 56ac1eefc..c5cd839bb 100644 --- a/test/php/library/Icinga/Web/UrlTest.php +++ b/test/php/library/Icinga/Web/UrlTest.php @@ -93,8 +93,8 @@ class UrlTest extends BaseTestCase $this->assertEquals( '', - $url->getBaseUrl(), - 'Url::fromPath does not recognize the correct base url' + $url->getBasePath(), + 'Url::fromPath does not recognize the correct base path' ); $this->assertEquals( '/my/test/url.html', From be4a31c8e19db2bc51932d8d6c69b31f1ae7fb32 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 18 Oct 2016 15:05:57 +0200 Subject: [PATCH 163/337] ExternalBackendForm: suggest REDIRECT_REMOTE_USER as username variable if set refs #12164 --- .../forms/Config/UserBackend/ExternalBackendForm.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/application/forms/Config/UserBackend/ExternalBackendForm.php b/application/forms/Config/UserBackend/ExternalBackendForm.php index 39c46d927..34519e63b 100644 --- a/application/forms/Config/UserBackend/ExternalBackendForm.php +++ b/application/forms/Config/UserBackend/ExternalBackendForm.php @@ -3,6 +3,7 @@ namespace Icinga\Forms\Config\UserBackend; +use Icinga\Authentication\User\ExternalBackend; use Zend_Validate_Callback; use Icinga\Web\Form; @@ -55,6 +56,12 @@ class ExternalBackendForm extends Form 'validators' => array($callbackValidator) ) ); + + foreach (array('REDIRECT_REMOTE_USER', 'REMOTE_USER') as $envvar) { + if (ExternalBackend::getRemoteUser($envvar) !== null) { + break; + } + } $this->addElement( 'text', 'username_envvar', @@ -64,9 +71,10 @@ class ExternalBackendForm extends Form 'The environment variable the webserver assigns the authenticated user\'s name to.' ), 'required' => true, - 'value' => 'REMOTE_USER' + 'value' => $envvar ) ); + $this->addElement( 'hidden', 'backend', From d6ac6c8374a4268f746fbcea04f61503c855eb7d Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 18 Oct 2016 15:19:13 +0200 Subject: [PATCH 164/337] setup/AuthenticationPage: don't show the warning about external backend configuration if REDIRECT_REMOTE_USER is set refs #12164 --- .../UserBackend/ExternalBackendForm.php | 2 +- .../Authentication/User/ExternalBackend.php | 9 ++++++ .../application/forms/AuthenticationPage.php | 28 ++++++++++++------- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/application/forms/Config/UserBackend/ExternalBackendForm.php b/application/forms/Config/UserBackend/ExternalBackendForm.php index 34519e63b..e21ed426f 100644 --- a/application/forms/Config/UserBackend/ExternalBackendForm.php +++ b/application/forms/Config/UserBackend/ExternalBackendForm.php @@ -57,7 +57,7 @@ class ExternalBackendForm extends Form ) ); - foreach (array('REDIRECT_REMOTE_USER', 'REMOTE_USER') as $envvar) { + foreach (ExternalBackend::getRemoteUserEnvvars() as $envvar) { if (ExternalBackend::getRemoteUser($envvar) !== null) { break; } diff --git a/library/Icinga/Authentication/User/ExternalBackend.php b/library/Icinga/Authentication/User/ExternalBackend.php index 44cd85eb5..98a180788 100644 --- a/library/Icinga/Authentication/User/ExternalBackend.php +++ b/library/Icinga/Authentication/User/ExternalBackend.php @@ -83,6 +83,15 @@ class ExternalBackend implements UserBackendInterface return null; } + /** + * Get possible variables where to read the user from + * + * @return string[] + */ + public static function getRemoteUserEnvvars() + { + return array('REDIRECT_REMOTE_USER', 'REMOTE_USER'); + } /** * {@inheritdoc} diff --git a/modules/setup/application/forms/AuthenticationPage.php b/modules/setup/application/forms/AuthenticationPage.php index d90b52a09..8fb397edc 100644 --- a/modules/setup/application/forms/AuthenticationPage.php +++ b/modules/setup/application/forms/AuthenticationPage.php @@ -31,16 +31,24 @@ class AuthenticationPage extends Form */ public function createElements(array $formData) { - if (isset($formData['type']) && $formData['type'] === 'external' - && ExternalBackend::getRemoteUser(null) === null) { - $this->info( - $this->translate( - 'You\'re currently not authenticated using any of the web server\'s authentication ' - . 'mechanisms. Make sure you\'ll configure such, otherwise you\'ll not be able to ' - . 'log into Icinga Web 2.' - ), - false - ); + if (isset($formData['type']) && $formData['type'] === 'external') { + $hasRemoteUser = false; + foreach (ExternalBackend::getRemoteUserEnvvars() as $envvar) { + if (ExternalBackend::getRemoteUser($envvar) !== null) { + $hasRemoteUser = true; + break; + } + } + if (! $hasRemoteUser) { + $this->info( + $this->translate( + 'You\'re currently not authenticated using any of the web server\'s authentication ' + . 'mechanisms. Make sure you\'ll configure such, otherwise you\'ll not be able to ' + . 'log into Icinga Web 2.' + ), + false + ); + } } $backendTypes = array(); From f8501aa80d413cb36abc2afec677f14e47a81c05 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 18 Oct 2016 15:38:03 +0200 Subject: [PATCH 165/337] setup/AdminAccountPage: respect previously configured envvar when suggesting admin username refs #12164 --- modules/setup/application/forms/AdminAccountPage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/setup/application/forms/AdminAccountPage.php b/modules/setup/application/forms/AdminAccountPage.php index 6e8fe26dc..b636063c9 100644 --- a/modules/setup/application/forms/AdminAccountPage.php +++ b/modules/setup/application/forms/AdminAccountPage.php @@ -270,7 +270,7 @@ class AdminAccountPage extends Form */ protected function getUsername() { - $name = ExternalBackend::getRemoteUser(null); + $name = ExternalBackend::getRemoteUser($this->backendConfig['username_envvar']); if ($name === null) { return ''; } From 4e64c0207890c19a8342cac4175fd2df8144e5d9 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 16:01:23 +0200 Subject: [PATCH 166/337] Url: Remove attribute baseUrl refs #12133 --- library/Icinga/Web/Url.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 46678524f..47aeee4a1 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -52,13 +52,6 @@ class Url */ protected $basePath; - /** - * The baseUrl that will be appended to @see Url::$path - * - * @var string - */ - protected $baseUrl = ''; - /** * The host of the Url * From dda73112263f9e7327fd6c4a9458614d6bc37f47 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 16:03:31 +0200 Subject: [PATCH 167/337] Url: Improve documentation refs #12133 --- library/Icinga/Web/Url.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 47aeee4a1..4749b4444 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -53,35 +53,35 @@ class Url protected $basePath; /** - * The host of the Url + * The host of this Url * * @var string */ protected $host; /** - * The port auf the Url + * The port of this Url * * @var string */ protected $port; /** - * The scheme of the Url + * The scheme of this Url * * @var string */ protected $scheme; /** - * The username passed with the Url + * The username passed with this Url * * @var string */ protected $username; /** - * The password passed with the Url + * The password passed with this Url * * @var string */ @@ -460,7 +460,7 @@ class Url } /** - * Set the username passed with the url + * Set the username passed with this url * * @param string $username The username to set * @@ -473,7 +473,7 @@ class Url } /** - * Return the username passed with the url + * Return the username passed with this url * * @return string */ @@ -483,7 +483,7 @@ class Url } /** - * Set the username passed with the url + * Set the username passed with this url * * @param string $password The password to set * @@ -496,7 +496,7 @@ class Url } /** - * Return the password passed with the url + * Return the password passed with this url * * @return string */ From 1c5535146069ae249d07483b14a5d6865f3f405d Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 16:07:13 +0200 Subject: [PATCH 168/337] Url: Remove "/" in front of path if the url has a user parameter refs #12133 --- library/Icinga/Web/Url.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 4749b4444..cefbdf5c4 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -189,7 +189,7 @@ class Url if (isset($urlParts['path'])) { $urlPath = $urlParts['path']; if ($urlPath && $urlPath[0] === '/') { - if ($urlObject->isExternal()) { + if ($urlObject->isExternal() || isset($urlParts['user'])) { $urlPath = substr($urlPath, 1); } else { $requestBaseUrl = $request->getBaseUrl(); @@ -198,12 +198,12 @@ class Url $urlObject->setBasePath($requestBaseUrl); } } - } elseif (!$urlObject->isExternal()) { + } elseif (! $urlObject->isExternal()) { $urlObject->setBasePath($request->getBaseUrl()); } $urlObject->setPath($urlPath); - } elseif (!$urlObject->isExternal()) { + } elseif (! $urlObject->isExternal()) { $urlObject->setBasePath($request->getBaseUrl()); } From b4aca4737b83cb13d8ebd4a1f77ca14315fd3631 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 16:08:34 +0200 Subject: [PATCH 169/337] Url: Cut of "/" and spaces at the end of basePath in setBasePath refs #12133 --- library/Icinga/Web/Url.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index cefbdf5c4..742002b5b 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -281,7 +281,7 @@ class Url */ public function setBasePath($basePath) { - $this->basePath = $basePath; + $this->basePath = rtrim($basePath, '/ '); return $this; } From 08719b0522fa4b84698114750b34fa91b1496d0e Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 16:13:37 +0200 Subject: [PATCH 170/337] Url: Do not try to set attributes in method fromPath if not needed refs #12133 --- library/Icinga/Web/Url.php | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 742002b5b..19310e622 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -214,20 +214,23 @@ class Url if (isset($urlParts['fragment'])) { $urlObject->setAnchor($urlParts['fragment']); } - if (isset($urlParts['host'])) { - $urlObject->setHost($urlParts['host']); - } - if (isset($urlParts['port'])) { - $urlObject->setPort($urlParts['port']); - } - if (isset($urlParts['scheme'])) { - $urlObject->setScheme($urlParts['scheme']); - } - if (isset($urlParts['user'])) { - $urlObject->setUsername($urlParts['user']); - } - if (isset($urlParts['pass'])) { - $urlObject->setPassword($urlParts['pass']); + + if (isset($urlParts['user']) || $urlObject->isExternal()) { + if (isset($urlParts['user'])) { + $urlObject->setUsername($urlParts['user']); + } + if (isset($urlParts['host'])) { + $urlObject->setHost($urlParts['host']); + } + if (isset($urlParts['port'])) { + $urlObject->setPort($urlParts['port']); + } + if (isset($urlParts['scheme'])) { + $urlObject->setScheme($urlParts['scheme']); + } + if (isset($urlParts['pass'])) { + $urlObject->setPassword($urlParts['pass']); + } } $urlObject->setParams($params); From 9fcc39e0bad22cde23019bb7652f83843cdbb3dc Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 16:15:14 +0200 Subject: [PATCH 171/337] Url: Add use of method setBasePath in method setBaseUrl refs #12133 --- library/Icinga/Web/Url.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 19310e622..4128a2322 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -385,14 +385,17 @@ class Url if (isset($urlParts["port"])) { $this->setPort($urlParts["port"]); } - if (isset($urlParts["scheme"])) { - $this->setScheme($urlParts["scheme"]); + if (isset($urlParts['scheme'])) { + $this->setScheme($urlParts['scheme']); } - if (isset($urlParts["user"])) { - $this->setUsername($urlParts["user"]); + if (isset($urlParts['user'])) { + $this->setUsername($urlParts['user']); } - if (isset($urlParts["pass"])) { - $this->setPassword($urlParts["pass"]); + if (isset($urlParts['pass'])) { + $this->setPassword($urlParts['pass']); + } + if (isset($urlParts['path'])) { + $this->setBasePath($urlParts['path']); } return $this; From a952a400ca84cef24157aff83dfc94e059352f53 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 16:16:13 +0200 Subject: [PATCH 172/337] Url: Add username and password to method getBaseUrl refs #12133 --- library/Icinga/Web/Url.php | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 4128a2322..3bc975695 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -410,11 +410,27 @@ class Url */ public function getBaseUrl() { - if (!$this->isExternal()) { + if (! $this->isExternal()) { return $this->getBasePath(); } - return $this->getScheme() . '://' . $this->getHost() . ($this->getPort() ? (':' . $this->getPort()) : ''); + $urlString = ''; + if ($this->getScheme()) { + $urlString .= $this->getScheme() . '://'; + } + if ($this->getPassword()) { + $urlString .= $this->getUsername() . ':' . $this->getPassword() . '@'; + } elseif ($this->getUsername()) { + $urlString .= $this->getUsername() . '@'; + } + if ($this->getHost()) { + $urlString .= $this->getHost(); + } + if ($this->getPort()) { + $urlString .= ':' . $this->getPort(); + } + + return $urlString; } /** From 3b6b0b8d4b92acff29d3d9d68471a68db56fd3a6 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 18 Oct 2016 16:19:24 +0200 Subject: [PATCH 173/337] Url: Build full urlString instead of path if username is set refs #12133 --- library/Icinga/Web/Url.php | 39 ++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 3bc975695..4898864ad 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -586,28 +586,31 @@ class Url } $basePath = $this->getBasePath(); - if (!$basePath) { + if (! $basePath) { $basePath = '/'; } - if (!$this->isExternal()) { + + if ($this->getUsername() || $this->isExternal()) { + $urlString = ''; + if ($this->getScheme()) { + $urlString .= $this->getScheme() . '://'; + } + if ($this->getPassword()) { + $urlString .= $this->getUsername() . ':' . $this->getPassword() . '@'; + } elseif ($this->getUsername()) { + $urlString .= $this->getUsername() . '@'; + } + if ($this->getHost()) { + $urlString .= $this->getHost(); + } + if ($this->getPort()) { + $urlString .= ':' . $this->getPort(); + } + + return $urlString . '/' . $path; + } else { return $basePath . ($basePath !== '/' && $path ? '/' : '') . $path; } - - $urlString = ''; - if ($this->getScheme()) { - $urlString = $urlString . $this->getScheme() . '://'; - } - if ($this->getUsername() && $this->getPassword()) { - $urlString = $urlString . $this->getUsername() . ':' . $this->getPassword() . "@"; - } - if ($this->getHost()) { - $urlString = $urlString . $this->getHost(); - } - if ($this->getPort()) { - $urlString = $urlString . $this->getPort(); - } - - return $urlString . '/' . $path; } /** From 00880710edccd5bb1a5c6372627e63ca26ace1c3 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 18 Oct 2016 18:06:51 +0200 Subject: [PATCH 174/337] Navigation::fromArray(): pass $name only as string to addItem() fixes #12923 --- library/Icinga/Web/Navigation/Navigation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Navigation/Navigation.php b/library/Icinga/Web/Navigation/Navigation.php index 9ecce98bc..3371d6c00 100644 --- a/library/Icinga/Web/Navigation/Navigation.php +++ b/library/Icinga/Web/Navigation/Navigation.php @@ -565,7 +565,7 @@ class Navigation implements ArrayAccess, Countable, IteratorAggregate { $navigation = new static(); foreach ($array as $name => $properties) { - $navigation->addItem($name, $properties); + $navigation->addItem((string) $name, $properties); } return $navigation; From 13827c79e49e1caf74f35a3b6a0d9e3363cff916 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Wed, 19 Oct 2016 09:35:40 +0200 Subject: [PATCH 175/337] Url: Improve documentation of method setHost refs #12133 --- library/Icinga/Web/Url.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 4898864ad..143b8cfb8 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -274,7 +274,6 @@ class Url return $this; } - /** * Set the basePath for this url * @@ -299,7 +298,7 @@ class Url } /** - * Overwrite the host + * Set the host for this url * * @param string $host New host of this Url * From 2fd1a957a8fb29f85462b0ed901893bf01b6aaef Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Wed, 19 Oct 2016 09:40:08 +0200 Subject: [PATCH 176/337] Url: Add basePath to baseUrl in method getBaseUrl refs #12133 --- library/Icinga/Web/Url.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 143b8cfb8..3f70bd3c1 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -428,6 +428,9 @@ class Url if ($this->getPort()) { $urlString .= ':' . $this->getPort(); } + if ($this->getBasePath()) { + $urlString .= $this->getBasePath(); + } return $urlString; } From 1ff3149636d1551bd8c4d351963d61cf58a52d75 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Wed, 19 Oct 2016 09:41:03 +0200 Subject: [PATCH 177/337] Url: Add basePath to urlString if url is external or has a username refs #12133 --- library/Icinga/Web/Url.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index 3f70bd3c1..d2cbf1dd0 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -609,7 +609,7 @@ class Url $urlString .= ':' . $this->getPort(); } - return $urlString . '/' . $path; + return $urlString . $basePath . ($basePath !== '/' && $path ? '/' : '') . $path; } else { return $basePath . ($basePath !== '/' && $path ? '/' : '') . $path; } From a84e0c4d84a81ae48720a9f51957a29ad7681883 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Wed, 19 Oct 2016 13:12:55 +0200 Subject: [PATCH 178/337] UrlTest: Add additional test cases refs #12133 --- test/php/library/Icinga/Web/UrlTest.php | 62 +++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/test/php/library/Icinga/Web/UrlTest.php b/test/php/library/Icinga/Web/UrlTest.php index c5cd839bb..4975199e6 100644 --- a/test/php/library/Icinga/Web/UrlTest.php +++ b/test/php/library/Icinga/Web/UrlTest.php @@ -9,6 +9,68 @@ use Icinga\Test\BaseTestCase; class UrlTest extends BaseTestCase { + public function testWhetherFromPathCutsOfTheFirstCharOfThePathIfUrlIsInternalAndHasAUsernameInIt() + { + $this->getRequestMock()->shouldReceive('getServer')->with("SERVER_NAME")->andReturn('localhost') + ->shouldReceive('getServer')->with("SERVER_PORT")->andReturn('8080'); + + $url = Url::fromPath('http://testusername:testpassword@localhost:8080/path/to/my/url.html'); + $this->assertEquals( + 'path/to/my/url.html', + $url->getPath(), + 'Url::fromPath does not cut of the first char of path if the url is internal and has a username in it' + ); + } + + public function testWhetherGetAbsoluteUrlReturnsTheBasePathIfUrlIsInternalAndHasAUsernameInIt() + { + $this->getRequestMock()->shouldReceive('getServer')->with("SERVER_NAME")->andReturn('localhost') + ->shouldReceive('getServer')->with("SERVER_PORT")->andReturn('8080'); + + $url = Url::fromPath('http://testusername:testpassword@localhost:8080/path/to/my/url.html'); + $this->assertEquals( + 'http://testusername:testpassword@localhost:8080/path/to/my/url.html', + $url->getAbsoluteUrl(), + 'Url::getAbsoluteUrl does not reassemble the correct basePath' + ); + } + + public function testWhetherGetAbsoluteUrlReturnsTheBasePathIfUrlIsInternalAndHasNoUsernameInIt() + { + $url = Url::fromPath('/path/to/my/url.html'); + $this->assertEquals( + '/path/to/my/url.html', + $url->getAbsoluteUrl(), + 'Url::getAbsoluteUrl does not reassemble the correct basePath' + ); + } + + public function testWhetherGetAbsoluteUrlReturnsTheBasePathIfUrlIsExternalAndHasAUsernameInIt() + { + $this->getRequestMock()->shouldReceive('getServer')->with("SERVER_NAME")->andReturn('localhost') + ->shouldReceive('getServer')->with("SERVER_PORT")->andReturn('8080'); + + $url = Url::fromPath('http://testusername:testpassword@testhost/path/to/my/url.html'); + $this->assertEquals( + 'http://testusername:testpassword@testhost/path/to/my/url.html', + $url->getAbsoluteUrl(), + 'Url::getAbsoluteUrl does not reassemble the correct basePath' + ); + } + + public function testWhetherGetAbsoluteUrlReturnsTheBasePathIfUrlIsExternalAndHasNoUsernameInIt() + { + $this->getRequestMock()->shouldReceive('getServer')->with("SERVER_NAME")->andReturn('localhost') + ->shouldReceive('getServer')->with("SERVER_PORT")->andReturn('8080'); + + $url = Url::fromPath('http://testhost/path/to/my/url.html'); + $this->assertEquals( + 'http://testhost/path/to/my/url.html', + $url->getAbsoluteUrl(), + 'Url::getAbsoluteUrl does not reassemble the correct basePath' + ); + } + public function testWhetherGetAbsoluteUrlReturnsTheGivenUsernameAndPassword() { $url = Url::fromPath('http://testusername:testpassword@testsite.com/path/to/my/url.html'); From 647c0b43549f80626e00bda18e0066ead0e849fd Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 19 Oct 2016 14:09:06 +0200 Subject: [PATCH 179/337] ResourceConfigForm: if the resource being used as config backend gets renamed, update the global config refs #9804 --- .../forms/Config/ResourceConfigForm.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/application/forms/Config/ResourceConfigForm.php b/application/forms/Config/ResourceConfigForm.php index 1ce12aaec..dfa885295 100644 --- a/application/forms/Config/ResourceConfigForm.php +++ b/application/forms/Config/ResourceConfigForm.php @@ -3,6 +3,7 @@ namespace Icinga\Forms\Config; +use Icinga\Application\Config; use InvalidArgumentException; use Icinga\Application\Platform; use Icinga\Exception\ConfigurationError; @@ -21,6 +22,13 @@ use Icinga\Web\Notification; class ResourceConfigForm extends ConfigForm { + /** + * If the global config must be updated because a resource has been changed, this is the updated global config + * + * @var Config|null + */ + protected $updatedAppConfig = null; + /** * Initialize this form */ @@ -104,6 +112,16 @@ class ResourceConfigForm extends ConfigForm $this->config->removeSection($name); unset($values['name']); $this->config->setSection($newName, $resourceConfig->merge($values)); + + if ($newName !== $name) { + $appConfig = Config::app(); + $section = $appConfig->getSection('global'); + if ($section->config_resource === $name) { + $section->config_resource = $newName; + $this->updatedAppConfig = $appConfig->setSection('global', $section); + } + } + return $resourceConfig; } @@ -376,4 +394,15 @@ class ResourceConfigForm extends ConfigForm return $this; } + + /** + * {@inheritDoc} + */ + protected function writeConfig(Config $config) + { + parent::writeConfig($config); + if ($this->updatedAppConfig !== null) { + $this->updatedAppConfig->saveIni(); + } + } } From a387f0524fe85a98d70570345d58221868b193d5 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 19 Oct 2016 15:29:51 +0200 Subject: [PATCH 180/337] Add log writer PhpWriter refs #11652 --- .../Application/Logger/Writer/PhpWriter.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 library/Icinga/Application/Logger/Writer/PhpWriter.php diff --git a/library/Icinga/Application/Logger/Writer/PhpWriter.php b/library/Icinga/Application/Logger/Writer/PhpWriter.php new file mode 100644 index 000000000..8e9061280 --- /dev/null +++ b/library/Icinga/Application/Logger/Writer/PhpWriter.php @@ -0,0 +1,22 @@ + Date: Wed, 19 Oct 2016 15:34:57 +0200 Subject: [PATCH 181/337] LoggingConfigForm: make the webserver log option available refs #11652 --- application/forms/Config/General/LoggingConfigForm.php | 1 + 1 file changed, 1 insertion(+) diff --git a/application/forms/Config/General/LoggingConfigForm.php b/application/forms/Config/General/LoggingConfigForm.php index 35cd37a89..1ca6bf8dd 100644 --- a/application/forms/Config/General/LoggingConfigForm.php +++ b/application/forms/Config/General/LoggingConfigForm.php @@ -37,6 +37,7 @@ class LoggingConfigForm extends Form 'label' => $this->translate('Logging Type'), 'description' => $this->translate('The type of logging to utilize.'), 'multiOptions' => array( + 'php' => $this->translate('Webserver Log', 'app.config.logging.type'), 'syslog' => 'Syslog', 'file' => $this->translate('File', 'app.config.logging.type'), 'none' => $this->translate('None', 'app.config.logging.type') From 8a06e686bbbf9f90d958d03a281a38dd8e8ea0a8 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Thu, 20 Oct 2016 11:29:35 +0200 Subject: [PATCH 182/337] NavigationItemForm: Update translation to fit updated Url class refs #12890 --- .../forms/Navigation/NavigationItemForm.php | 2 +- .../locale/de_DE/LC_MESSAGES/icinga.mo | Bin 60998 -> 61126 bytes .../locale/de_DE/LC_MESSAGES/icinga.po | 12 +++++------- .../locale/it_IT/LC_MESSAGES/icinga.mo | Bin 56908 -> 56530 bytes .../locale/it_IT/LC_MESSAGES/icinga.po | 10 ---------- .../locale/ru_RU/LC_MESSAGES/icinga.po | 10 ---------- 6 files changed, 6 insertions(+), 28 deletions(-) diff --git a/application/forms/Navigation/NavigationItemForm.php b/application/forms/Navigation/NavigationItemForm.php index 61fd7428f..d46b3b3cd 100644 --- a/application/forms/Navigation/NavigationItemForm.php +++ b/application/forms/Navigation/NavigationItemForm.php @@ -54,7 +54,7 @@ class NavigationItemForm extends Form 'label' => $this->translate('Url'), 'description' => $this->translate( 'The url of this navigation item. Leave blank if you only want the' - . ' name being displayed. For external urls, make sure to prepend' + . ' name being displayed. For internal urls with password and username in them and for all external urls, make sure to prepend' . ' an appropriate protocol identifier (e.g. http://example.tld)' ) ) diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.mo b/application/locale/de_DE/LC_MESSAGES/icinga.mo index 3413b7cbff069bdec8a4239867b2db1b767e0dea..50d8eadf2bd493613a1d8b0a7dd010b0fb8fa8fd 100644 GIT binary patch delta 5997 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 delta 10699 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 WNmISksw&63JC0WFUV5}N^nU?ieOMm= diff --git a/application/locale/it_IT/LC_MESSAGES/icinga.po b/application/locale/it_IT/LC_MESSAGES/icinga.po index 1550ef138..6746860cd 100644 --- a/application/locale/it_IT/LC_MESSAGES/icinga.po +++ b/application/locale/it_IT/LC_MESSAGES/icinga.po @@ -2517,16 +2517,6 @@ msgstr "Tipo del backend gruppo utenti" msgid "The unique name of this resource" msgstr "Nome univoco per questa risorsa" -#: /usr/share/icingaweb2/application/forms/Navigation/NavigationItemForm.php:56 -msgid "" -"The url of this navigation item. Leave blank if you only want the name being " -"displayed. For external urls, make sure to prepend an appropriate protocol " -"identifier (e.g. http://example.tld)" -msgstr "" -"La url di questo oggetto di navigazione. Lasciare vuoto se si vuole solo " -"mostrare il nome. Per url esterne ricordare di anteporre l'appropriato " -"identificatore di protocollo (ex. http://esempio.tld)" - #: /usr/share/icingaweb2/application/forms/Navigation/DashletForm.php:29 msgid "" "The url to load in the dashlet. For external urls, make sure to prepend an " diff --git a/application/locale/ru_RU/LC_MESSAGES/icinga.po b/application/locale/ru_RU/LC_MESSAGES/icinga.po index e27f63e9c..c43e1cdd2 100644 --- a/application/locale/ru_RU/LC_MESSAGES/icinga.po +++ b/application/locale/ru_RU/LC_MESSAGES/icinga.po @@ -2417,16 +2417,6 @@ msgstr "" msgid "The unique name of this resource" msgstr "Уникальное название ресурса" -#: /vagrant/application/forms/Navigation/NavigationItemForm.php:56 -msgid "" -"The url of this navigation item. Leave blank if you only want the name being " -"displayed. For external urls, make sure to prepend an appropriate protocol " -"identifier (e.g. http://example.tld)" -msgstr "" -"URL адрес элемента навигации. Оставьте пустым, если необходимо только " -"отображение имени. Для внешних адресов убедитесь, что в начале ссылке указан " -"корректный протокол (например, http://example.tld)" - #: /vagrant/application/forms/Navigation/DashletForm.php:29 msgid "" "The url to load in the dashlet. For external urls, make sure to prepend an " From 7d94c1fddbce7fa9aa31ae08b87e4d25bec191cb Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 20 Oct 2016 15:40:33 +0200 Subject: [PATCH 183/337] DbQuery: handle strange NULL comparision results refs #12852 --- library/Icinga/Data/Db/DbQuery.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Data/Db/DbQuery.php b/library/Icinga/Data/Db/DbQuery.php index 48607b66d..966e094a4 100644 --- a/library/Icinga/Data/Db/DbQuery.php +++ b/library/Icinga/Data/Db/DbQuery.php @@ -301,7 +301,7 @@ class DbQuery extends SimpleQuery if ($sign === '=') { return $col . ' IN (' . $this->escapeForSql($expression) . ')'; } elseif ($sign === '!=') { - return $col . ' NOT IN (' . $this->escapeForSql($expression) . ')'; + return $col . ' NOT IN (' . $this->escapeForSql($expression) . ') OR ' . $col . ' IS NULL'; } throw new QueryException('Unable to render array expressions with operators other than equal or not equal'); @@ -316,9 +316,12 @@ class DbQuery extends SimpleQuery return new Zend_Db_Expr('FALSE'); } - return $col . ' NOT LIKE ' . $this->escapeForSql($this->escapeWildcards($expression)); + return $col . ' NOT LIKE ' . $this->escapeForSql($this->escapeWildcards($expression)) + . ' OR ' . $col . ' IS NULL'; } else { - return $col . ' ' . $sign . ' ' . $this->escapeForSql($expression); + return $col . ' ' . $sign . ' ' . $this->escapeForSql($expression) . ( + $sign === '!=' ? ' OR ' . $col . ' IS NULL' : '' + ); } } From 4d2e6d2d8709da9168af824c36458dd5857b08a8 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 20 Oct 2016 18:28:19 +0200 Subject: [PATCH 184/337] Make hooks respect module permissions refs #12396 --- library/Icinga/Application/Modules/Module.php | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index de40266e1..555127228 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -4,6 +4,7 @@ namespace Icinga\Application\Modules; use Exception; +use Icinga\Authentication\Auth; use Zend_Controller_Router_Route; use Zend_Controller_Router_Route_Abstract; use Zend_Controller_Router_Route_Regex; @@ -1282,19 +1283,22 @@ class Module */ protected function provideHook($name, $implementation = null, $key = null) { - if ($implementation === null) { - $implementation = $name; + if (Auth::getInstance()->hasPermission('module/' . $this->name)) { + if ($implementation === null) { + $implementation = $name; + } + + if (strpos($implementation, '\\') === false) { + $class = $this->getNamespace() + . '\\ProvidedHook\\' + . $this->slashesToNamespace($implementation); + } else { + $class = $implementation; + } + + Hook::register($name, $class, $class); } - if (strpos($implementation, '\\') === false) { - $class = $this->getNamespace() - . '\\ProvidedHook\\' - . $this->slashesToNamespace($implementation); - } else { - $class = $implementation; - } - - Hook::register($name, $class, $class); return $this; } From 319f648b32af7f2b76075326bec0efa662daf847 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 21 Oct 2016 11:48:39 +0200 Subject: [PATCH 185/337] LdapUserGroupBackendForm: purge user_* settings refs #10401 --- .../Config/UserGroup/LdapUserGroupBackendForm.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php index 5affd86ac..8a56237ba 100644 --- a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php +++ b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php @@ -92,6 +92,8 @@ class LdapUserGroupBackendForm extends Form $this->createGroupConfigElements($defaults, $groupConfigDisabled); if (count($userBackends) === 1 || (isset($formData['user_backend']) && $formData['user_backend'] === 'none')) { $this->createUserConfigElements($defaults, $userConfigDisabled); + } else { + $this->createHiddenUserConfigElements(); } $this->addElement( @@ -278,6 +280,17 @@ class LdapUserGroupBackendForm extends Form ); } + /** + * Create and add all elements to this form required for the user configuration as hidden + */ + protected function createHiddenUserConfigElements() + { + $this->addElement('hidden', 'user_class', array('disabled' => true)); + $this->addElement('hidden', 'user_filter', array('disabled' => true)); + $this->addElement('hidden', 'user_name_attribute', array('disabled' => true)); + $this->addElement('hidden', 'user_base_dn', array('disabled' => true)); + } + /** * Return the names of all configured LDAP resources * From 52e6293b9602ccc1f45cf090cd4d9144b43416ac Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 21 Oct 2016 17:09:22 +0200 Subject: [PATCH 186/337] Allow to configure the Syslog facility refs #11214 --- .../Config/General/LoggingConfigForm.php | 61 ++++++++++++++----- .../Logger/Writer/SyslogWriter.php | 32 +++++++++- 2 files changed, 75 insertions(+), 18 deletions(-) diff --git a/application/forms/Config/General/LoggingConfigForm.php b/application/forms/Config/General/LoggingConfigForm.php index 35cd37a89..cf2228f7c 100644 --- a/application/forms/Config/General/LoggingConfigForm.php +++ b/application/forms/Config/General/LoggingConfigForm.php @@ -4,6 +4,7 @@ namespace Icinga\Forms\Config\General; use Icinga\Application\Logger; +use Icinga\Application\Platform; use Icinga\Web\Form; /** @@ -90,22 +91,50 @@ class LoggingConfigForm extends Form ) ) ); - /* - * Note(el): Since we provide only one possible value for the syslog facility, I opt against exposing - * this configuration. - */ -// $this->addElement( -// 'select', -// 'logging_facility', -// array( -// 'required' => true, -// 'label' => $this->translate('Facility'), -// 'description' => $this->translate('The syslog facility to utilize.'), -// 'multiOptions' => array( -// 'user' => 'LOG_USER' -// ) -// ) -// ); + + if (Platform::isWindows()) { + /* @see https://secure.php.net/manual/en/function.openlog.php */ + $this->addElement( + 'hidden', + 'logging_facility', + array( + 'value' => 'user', + 'disabled' => true + ) + ); + } else { + $this->addElement( + 'select', + 'logging_facility', + array( + 'required' => true, + 'label' => $this->translate('Facility'), + 'description' => $this->translate('The syslog facility to utilize.'), + 'value' => 'user', + 'multiOptions' => array( + 'auth' => 'LOG_AUTH', + 'authpriv' => 'LOG_AUTHPRIV', + 'cron' => 'LOG_CRON', + 'daemon' => 'LOG_DAEMON', + 'kern' => 'LOG_KERN', + 'local0' => 'LOG_LOCAL0', + 'local1' => 'LOG_LOCAL1', + 'local2' => 'LOG_LOCAL2', + 'local3' => 'LOG_LOCAL3', + 'local4' => 'LOG_LOCAL4', + 'local5' => 'LOG_LOCAL5', + 'local6' => 'LOG_LOCAL6', + 'local7' => 'LOG_LOCAL7', + 'lpr' => 'LOG_LPR', + 'mail' => 'LOG_MAIL', + 'news' => 'LOG_NEWS', + 'syslog' => 'LOG_SYSLOG', + 'user' => 'LOG_USER', + 'uucp' => 'LOG_UUCP' + ) + ) + ); + } } elseif (isset($formData['logging_log']) && $formData['logging_log'] === 'file') { $this->addElement( 'text', diff --git a/library/Icinga/Application/Logger/Writer/SyslogWriter.php b/library/Icinga/Application/Logger/Writer/SyslogWriter.php index b1387078c..f26a07412 100644 --- a/library/Icinga/Application/Logger/Writer/SyslogWriter.php +++ b/library/Icinga/Application/Logger/Writer/SyslogWriter.php @@ -6,6 +6,7 @@ namespace Icinga\Application\Logger\Writer; use Icinga\Data\ConfigObject; use Icinga\Application\Logger; use Icinga\Application\Logger\LogWriter; +use Icinga\Exception\ConfigurationError; /** * Log to the syslog service @@ -32,7 +33,25 @@ class SyslogWriter extends LogWriter * @var array */ public static $facilities = array( - 'user' => LOG_USER + 'auth' => LOG_AUTH, + 'authpriv' => LOG_AUTHPRIV, + 'cron' => LOG_CRON, + 'daemon' => LOG_DAEMON, + 'kern' => LOG_KERN, + 'local0' => LOG_LOCAL0, + 'local1' => LOG_LOCAL1, + 'local2' => LOG_LOCAL2, + 'local3' => LOG_LOCAL3, + 'local4' => LOG_LOCAL4, + 'local5' => LOG_LOCAL5, + 'local6' => LOG_LOCAL6, + 'local7' => LOG_LOCAL7, + 'lpr' => LOG_LPR, + 'mail' => LOG_MAIL, + 'news' => LOG_NEWS, + 'syslog' => LOG_SYSLOG, + 'user' => LOG_USER, + 'uucp' => LOG_UUCP ); /** @@ -55,7 +74,16 @@ class SyslogWriter extends LogWriter public function __construct(ConfigObject $config) { $this->ident = $config->get('application', 'icingaweb2'); - $this->facility = static::$facilities['user']; + + $configuredFacility = $config->get('facility', 'user'); + if (! isset(static::$facilities[$configuredFacility])) { + throw new ConfigurationError( + 'Invalid logging facility: "%s" (expected one of: %s)', + $configuredFacility, + implode(', ', array_keys(static::$facilities)) + ); + } + $this->facility = static::$facilities[$configuredFacility]; } /** From 00af3d61b7728a5bb4f8e1525bd76341bfd94ffa Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 24 Oct 2016 10:43:27 +0200 Subject: [PATCH 187/337] LoggingConfigForm: provide less Syslog facilities refs #11214 --- .../forms/Config/General/LoggingConfigForm.php | 14 ++------------ .../Application/Logger/Writer/SyslogWriter.php | 14 ++------------ 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/application/forms/Config/General/LoggingConfigForm.php b/application/forms/Config/General/LoggingConfigForm.php index cf2228f7c..428e5a75e 100644 --- a/application/forms/Config/General/LoggingConfigForm.php +++ b/application/forms/Config/General/LoggingConfigForm.php @@ -112,11 +112,7 @@ class LoggingConfigForm extends Form 'description' => $this->translate('The syslog facility to utilize.'), 'value' => 'user', 'multiOptions' => array( - 'auth' => 'LOG_AUTH', - 'authpriv' => 'LOG_AUTHPRIV', - 'cron' => 'LOG_CRON', - 'daemon' => 'LOG_DAEMON', - 'kern' => 'LOG_KERN', + 'user' => 'LOG_USER', 'local0' => 'LOG_LOCAL0', 'local1' => 'LOG_LOCAL1', 'local2' => 'LOG_LOCAL2', @@ -124,13 +120,7 @@ class LoggingConfigForm extends Form 'local4' => 'LOG_LOCAL4', 'local5' => 'LOG_LOCAL5', 'local6' => 'LOG_LOCAL6', - 'local7' => 'LOG_LOCAL7', - 'lpr' => 'LOG_LPR', - 'mail' => 'LOG_MAIL', - 'news' => 'LOG_NEWS', - 'syslog' => 'LOG_SYSLOG', - 'user' => 'LOG_USER', - 'uucp' => 'LOG_UUCP' + 'local7' => 'LOG_LOCAL7' ) ) ); diff --git a/library/Icinga/Application/Logger/Writer/SyslogWriter.php b/library/Icinga/Application/Logger/Writer/SyslogWriter.php index f26a07412..93efc2a19 100644 --- a/library/Icinga/Application/Logger/Writer/SyslogWriter.php +++ b/library/Icinga/Application/Logger/Writer/SyslogWriter.php @@ -33,11 +33,7 @@ class SyslogWriter extends LogWriter * @var array */ public static $facilities = array( - 'auth' => LOG_AUTH, - 'authpriv' => LOG_AUTHPRIV, - 'cron' => LOG_CRON, - 'daemon' => LOG_DAEMON, - 'kern' => LOG_KERN, + 'user' => LOG_USER, 'local0' => LOG_LOCAL0, 'local1' => LOG_LOCAL1, 'local2' => LOG_LOCAL2, @@ -45,13 +41,7 @@ class SyslogWriter extends LogWriter 'local4' => LOG_LOCAL4, 'local5' => LOG_LOCAL5, 'local6' => LOG_LOCAL6, - 'local7' => LOG_LOCAL7, - 'lpr' => LOG_LPR, - 'mail' => LOG_MAIL, - 'news' => LOG_NEWS, - 'syslog' => LOG_SYSLOG, - 'user' => LOG_USER, - 'uucp' => LOG_UUCP + 'local7' => LOG_LOCAL7 ); /** From 893daf3a7bf98203c6b7c3f0a96e10c4d2601a0e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 24 Oct 2016 10:55:15 +0200 Subject: [PATCH 188/337] Store available Syslog facilities non-redundandly refs #11214 --- .../forms/Config/General/LoggingConfigForm.php | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/application/forms/Config/General/LoggingConfigForm.php b/application/forms/Config/General/LoggingConfigForm.php index 428e5a75e..5f1b27a4d 100644 --- a/application/forms/Config/General/LoggingConfigForm.php +++ b/application/forms/Config/General/LoggingConfigForm.php @@ -4,6 +4,7 @@ namespace Icinga\Forms\Config\General; use Icinga\Application\Logger; +use Icinga\Application\Logger\Writer\SyslogWriter; use Icinga\Application\Platform; use Icinga\Web\Form; @@ -103,6 +104,7 @@ class LoggingConfigForm extends Form ) ); } else { + $facilities = array_keys(SyslogWriter::$facilities); $this->addElement( 'select', 'logging_facility', @@ -111,17 +113,7 @@ class LoggingConfigForm extends Form 'label' => $this->translate('Facility'), 'description' => $this->translate('The syslog facility to utilize.'), 'value' => 'user', - 'multiOptions' => array( - 'user' => 'LOG_USER', - 'local0' => 'LOG_LOCAL0', - 'local1' => 'LOG_LOCAL1', - 'local2' => 'LOG_LOCAL2', - 'local3' => 'LOG_LOCAL3', - 'local4' => 'LOG_LOCAL4', - 'local5' => 'LOG_LOCAL5', - 'local6' => 'LOG_LOCAL6', - 'local7' => 'LOG_LOCAL7' - ) + 'multiOptions' => array_combine($facilities, $facilities) ) ); } From 366064e21fd44302abae13a3684c25d90c0b905e Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Tue, 25 Oct 2016 13:37:31 +0200 Subject: [PATCH 189/337] NavigationItemForm: Improve translation refs #12890 --- .../forms/Navigation/NavigationItemForm.php | 6 +++--- .../locale/de_DE/LC_MESSAGES/icinga.mo | Bin 61126 -> 61085 bytes .../locale/de_DE/LC_MESSAGES/icinga.po | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/application/forms/Navigation/NavigationItemForm.php b/application/forms/Navigation/NavigationItemForm.php index d46b3b3cd..4f2df85cc 100644 --- a/application/forms/Navigation/NavigationItemForm.php +++ b/application/forms/Navigation/NavigationItemForm.php @@ -53,9 +53,9 @@ class NavigationItemForm extends Form 'allowEmpty' => true, 'label' => $this->translate('Url'), 'description' => $this->translate( - 'The url of this navigation item. Leave blank if you only want the' - . ' name being displayed. For internal urls with password and username in them and for all external urls, make sure to prepend' - . ' an appropriate protocol identifier (e.g. http://example.tld)' + 'The url of this navigation item. Leave blank if only the name should be displayed.' + . ' For urls with username and password and for all external urls,' + . ' make sure to prepend an appropriate protocol identifier (e.g. http://example.tld)' ) ) ); diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.mo b/application/locale/de_DE/LC_MESSAGES/icinga.mo index 50d8eadf2bd493613a1d8b0a7dd010b0fb8fa8fd..d6f05c01e5045a61cade2e5c9b7c4e5fb8ea89f0 100644 GIT binary patch delta 5927 zcmXZg3w+PjAII_UuEu6;E@RgG%qAN)HkTQ5w<0uJ3N3&B`(^XrM!WpSq~DYhsjx_H zDO$o`(~{f%N-iOyLWRk#-2N30B`W-1@165_czB-A`CiUBpYu7t-;cdDKFe!-D%Usg zoOc}O^1mHtCLStroUS-xisL+uv+;iX3p-=$sg4tckK<6BhFx$!X5u|;iJ7I26N%ID z5nP7t@f!N!ebXGLDYl;Gc$^d(DRlT^F$Q2Mj=<;fMLdpmF!LG5X^w7G?7;|}g$;2v z2ID5w0K2gPp2R@BYW)Kn5&KN{I8F$Su<7Q-u{e^rKMuhq=z|xrK3>ISyp8dg_^g@u zIMl?R!f-6ZI9!SvXfMX%2@FDC#?|{mJT&UjXo;a1hk76tHRItJfteVFrPu-&U>n?E zJ%+7_|F%Y!nYa%s_2aFtp|-Rdi_mj{h9)p-hDm)EYLD_zDJ-RO&17JKSQg z&wtKw$f~mjm9d!T9j6_}Vh?;2vv5AH!kgHH@tq}99u21wBXO_wM~o)+eZg^D7>mlp zVCw|Dk9a2Pxp%BPt!GfhdJi?>C<;l&p+6>K7~?zL?S*6i!jovSt74@?YMxCY+s9Ji(x)PPiD)hteFc43pN5yu9hE{kN8)LJ%ChmaR zng>v|kcqm#$oi7)Uxq5mD%8M-QK>(NTA1G}j?)bjkfb;huoeFL3i)qCBji<6T*;V3 z`~d2~X{b!Ri^|j?oPk4MGbul8J#MW*t?+{NiuD>Qb2m}P&Y5S9eegWqst+8O?TAI~ zSr62Vb8WmDHL+u;J*+$5q`n6R5%;&Iqf(oR!I*<}a0+VRGVF)1qPFTQ4~HKwD_LdSUwGcW-? z>uI#3aSAgqV3GOE&cuPl8?Aq!R*?FJ<2;NHVMknw@purM;9vM8hAcK!J{fDb5M#X9 zLey5=c#}nHSMShp(J^O6A*d8B471vDcgF(y8 z^`WQ<7owiug^lqPMysl>)6ff=E;j@9N2Ty7YXNE^&*E@=6-)38Y9)`aFsEZYMi5WM z5L|@Xk~Q}Fc8nzc8a09ISYK7`|CTvcjZrCXiz=$FsDb<2_z`@K_#fC7uc1=je5IK{ z0&3!Ecpr{HO<*!=LeHb_Ux=E(I`p_`d`cq>&tfCIjd56?3et_q*aC;53v*D%b`I)= z8!-Vt$ARavoTkwg9`Wr?!>Nm81vA7jmcOM{)>2yhej}sn^wQ~%>){w zG7ye>VNa}wX{ZzrNA2}^)bsh+7N=tytU#^!5NhxLMBNv()|`qKsEK>}(&$8^6zkx6 z>qqEIT#d@ePHcytqiW&?hGW1wQ+%y4iZ}%WF%y-M98?ibx6VVocNOx-I+Y2KH-j_>M~rjK!vyh!5c)tcMFxMYIxQ@LLSRJE(!`QR-T8IBKhsP?_j%`!i4r%)>}r zfcl`?h_&DUU(!%jpFCmqXwRV+Pn8q6I+i1aWi(rJJ=iJKQeJXb|!wy z#z(OcvEL?>vF4k|zxJ{l9V(uIsFh@6D9%7-6lJjjmk{@Pt51T}?F{hC!%-Gf_2= zhur9tVKZEbZE!1U;%89L`R_FMg=16V6zg!*d$Mgj4VBpi*j(p-EsX>^c4HI#&06m> zb4*;QqDw^;;V9IA(=iU`qgGgriMSU#;caWXU1rNNQN^B%T2LuAV|-@`4eenScEKGu z3V*{fIBd7c$Xe8W+px9M%(@7vpgBKfYGR;dKPuO=ApK18S1@T&>s(?QvVGq zgE#h*e_ilCV2Z0QDm784{$%vU-l!B0z)XA$2jM2vX}O2mvhH7)OqAmj#Fub14m@Z+ zh*shQ#JjK~26_&e7xuI+!QNcBg?e%Fm*$5_0d^&>#Fz0R=3(JsbNwtXCT@9zAEdYi z+hNF8ws>(Y@hBXITk#?E1bl5)l3`teFe1!{t1Q!_5qxL|&_PedQ$Bd8Y~#r}8+Q?dPz<_F0{Y(rdv(YOM2e0QOW z@fLQ$=%364NB_kBYbKNF2*z2ci7c{Kq0ajeRMDNmRv38Ethj@940fV_HfrGQ7^C}9 z6Y#%e?19O|lTj0T|B{D~T^h&e&_J^vxob36ZNk!t%W=|eOKQ4?#e{^FY=As6iVy~BB5b=u` zish)SsX*=ZHq-*XMrHP-jjy1dyMgs^l=F)zszB6$O;9P0Kn>W-nueO#Fw~1PP%p|v zZQ(@J3umA*HMi>fn6bX(peiNq7jN$yUR7}&ejSuHDL;3rt0>#;${&~Kb`@q%D$dPv zjd#1UatbHsj+^Svig%?=DsUAS(LQI3SD_QMXrAC{Nkcg`)Dzbrkh8L%7>l0KiHj< o@2-8BJ3q@^P@YiJG-!gmu*jX8Tja_qEGi#T6H&GElwauo0ivxTIRF3v delta 5929 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 diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.po b/application/locale/de_DE/LC_MESSAGES/icinga.po index 8addee6ef..f99ff565c 100644 --- a/application/locale/de_DE/LC_MESSAGES/icinga.po +++ b/application/locale/de_DE/LC_MESSAGES/icinga.po @@ -2564,12 +2564,12 @@ msgstr "Der eindeutige Name dieser Ressource" #: /vagrant/application/forms/Navigation/NavigationItemForm.php:56 msgid "" -"The url of this navigation item. Leave blank if you only want the name being displayed. For internal urls with password and username in " -"them and for all external urls, make sure to prepend an appropriate protocol identifier (e.g. http://example.tld)" +"The url of this navigation item. Leave blank if only the name should be displayed. For urls with username and password and " +"for all external urls, make sure to prepend an appropriate protocol identifier (e.g. http://example.tld)" msgstr "" -"Die URL dieses Navigationselementes. Dies kann leer gelassen werden, wenn nur der Name angezeigt werden soll. Stellen Sie für alle " -"internen URLs mit Passwort und Benutzername sowie alle externen URLs sicher, dass Sie einen passenden Protokollnamen voran stellen (z." -"B. http://example.tld)" +"Die URL dieses Navigationselementes. Dies kann leer gelassen werden, wenn nur der Name angezeigt werden soll. Stelle für URLs " +"mit Benutzername und Passwort sowie alle externen URLs sicher, dass eine passender Protokollnamen voran gestellt ist (z.B. " +"http://example.tld)" #: /vagrant/application/forms/Navigation/DashletForm.php:29 msgid "" From 5212b6bab9349b60810217b25dc304f011f9c471 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 26 Oct 2016 10:27:30 +0200 Subject: [PATCH 190/337] IniRepository: don't persist the key column as regular column --- library/Icinga/Repository/IniRepository.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index 18ae80f47..8cc961c8b 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -118,6 +118,9 @@ abstract class IniRepository extends Repository implements Extensible, Updatable } } + // This is necessary as the query result set contains the key column. + unset($config->$keyColumn); + if ($newSection) { if ($this->ds->hasSection($newSection)) { throw new StatementException(t('Cannot update. Section "%s" does already exist'), $newSection); From f6448605298666b1ca53888b11cb62adc2945d6f Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 27 Oct 2016 15:09:00 +0200 Subject: [PATCH 191/337] IniRepository: Fix that method update and delete fail without filter --- library/Icinga/Repository/IniRepository.php | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index 8cc961c8b..8ff1936bf 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -93,15 +93,12 @@ abstract class IniRepository extends Repository implements Extensible, Updatable ); } + $query = $this->ds->select(); if ($filter !== null) { - $filter = $this->requireFilter($target, $filter); + $query->addFilter($this->requireFilter($target, $filter)); } $newSection = null; - - $query = $this->ds->select(); - $query->addFilter($filter); - foreach ($query as $section => $config) { if ($newSection !== null) { throw new StatementException( @@ -149,17 +146,15 @@ abstract class IniRepository extends Repository implements Extensible, Updatable */ public function delete($target, Filter $filter = null) { - if ($filter !== null) { - $filter = $this->requireFilter($target, $filter); - } - $query = $this->ds->select(); - $query->addFilter($filter); + if ($filter !== null) { + $query->addFilter($this->requireFilter($target, $filter)); + } foreach ($query as $section => $config) { $this->ds->removeSection($section); } - + try { $this->ds->saveIni(); } catch (Exception $e) { From 957ad9361f359d86308f3b6fb8e7326dac70e0d7 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 27 Oct 2016 15:11:21 +0200 Subject: [PATCH 192/337] IniRepository: Add support for triggers --- library/Icinga/Repository/IniRepository.php | 148 +++++++++++++++++++- 1 file changed, 143 insertions(+), 5 deletions(-) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index 8ff1936bf..04b25c5cb 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -5,6 +5,7 @@ namespace Icinga\Repository; use Exception; use Icinga\Application\Config; +use Icinga\Data\ConfigObject; use Icinga\Data\Extensible; use Icinga\Data\Filter\Filter; use Icinga\Data\Updatable; @@ -18,6 +19,7 @@ use Icinga\Exception\StatementException; * Additionally provided features: *
    *
  • Insert, update and delete capabilities
  • + *
  • Triggers for inserts, updates and deletions
  • *
*/ abstract class IniRepository extends Repository implements Extensible, Updatable, Reducible @@ -29,6 +31,19 @@ abstract class IniRepository extends Repository implements Extensible, Updatable */ protected $ds; + /** + * The tables for which triggers are available when inserting, updating or deleting rows + * + * This may be initialized by concrete repository implementations and describes for which table names triggers + * are available. The repository attempts to find a method depending on the type of event and table for which + * to run the trigger. The name of such a method is expected to be declared using lowerCamelCase. + * (e.g. group_membership will be translated to onUpdateGroupMembership and groupmembership will be translated + * to onUpdateGroupmembership) The available events are onInsert, onUpdate and onDelete. + * + * @var array + */ + protected $triggers; + /** * Create a new INI repository object * @@ -45,6 +60,119 @@ abstract class IniRepository extends Repository implements Extensible, Updatable } } + /** + * Return the tables for which triggers are available when inserting, updating or deleting rows + * + * Calls $this->initializeTriggers() in case $this->triggers is null. + * + * @return array + */ + public function getTriggers() + { + if ($this->triggers === null) { + $this->triggers = $this->initializeTriggers(); + } + + return $this->triggers; + } + + /** + * Overwrite this in your repository implementation in case you need to initialize the triggers lazily + * + * @return array + */ + protected function initializeTriggers() + { + return array(); + } + + /** + * Run a trigger for the given table and row which is about to be inserted + * + * @param string $table + * @param ConfigObject $new + * + * @return ConfigObject + */ + public function onInsert($table, ConfigObject $new) + { + $trigger = $this->getTrigger($table, 'onInsert'); + if ($trigger !== null) { + $row = $this->$trigger($new); + if ($row !== null) { + $new = $row; + } + } + + return $new; + } + + /** + * Run a trigger for the given table and row which is about to be updated + * + * @param string $table + * @param ConfigObject $old + * @param ConfigObject $new + * + * @return ConfigObject + */ + public function onUpdate($table, ConfigObject $old, ConfigObject $new) + { + $trigger = $this->getTrigger($table, 'onUpdate'); + if ($trigger !== null) { + $row = $this->$trigger($old, $new); + if ($row !== null) { + $new = $row; + } + } + + return $new; + } + + /** + * Run a trigger for the given table and row which has been deleted + * + * @param string $table + * @param ConfigObject $old + * + * @return ConfigObject + */ + public function onDelete($table, ConfigObject $old) + { + $trigger = $this->getTrigger($table, 'onDelete'); + if ($trigger !== null) { + $this->$trigger($old); + } + } + + /** + * Return the name of the trigger method for the given table and event-type + * + * @param string $table The table name for which to return a trigger method + * @param string $event The name of the event type + * + * @return string + * + * @throws ProgrammingError In case the table is registered as having triggers but not any trigger is found + */ + protected function getTrigger($table, $event) + { + if (! in_array($table, $this->getTriggers())) { + return; + } + + $identifier = join('', array_map('ucfirst', explode('_', $table))); + if (! method_exists($this, $event . $identifier)) { + throw new ProgrammingError( + 'Cannot find any trigger for table "%s". Add a trigger or remove the table from %s::$triggers', + $table, + get_class($this) + ); + } + + return $event . $identifier; + } + /** * Insert the given data for the given target * @@ -64,7 +192,7 @@ abstract class IniRepository extends Repository implements Extensible, Updatable throw new StatementException(t('Cannot insert. Section "%s" does already exist'), $section); } - $this->ds->setSection($section, $newData); + $this->ds->setSection($section, $this->onInsert($target, new ConfigObject($newData))); try { $this->ds->saveIni(); @@ -98,6 +226,7 @@ abstract class IniRepository extends Repository implements Extensible, Updatable $query->addFilter($this->requireFilter($target, $filter)); } + /** @var ConfigObject $config */ $newSection = null; foreach ($query as $section => $config) { if ($newSection !== null) { @@ -107,25 +236,32 @@ abstract class IniRepository extends Repository implements Extensible, Updatable ); } + $newConfig = clone $config; foreach ($newData as $column => $value) { if ($column === $keyColumn) { $newSection = $value; } else { - $config->$column = $value; + $newConfig->$column = $value; } } // This is necessary as the query result set contains the key column. - unset($config->$keyColumn); + unset($newConfig->$keyColumn); if ($newSection) { if ($this->ds->hasSection($newSection)) { throw new StatementException(t('Cannot update. Section "%s" does already exist'), $newSection); } - $this->ds->removeSection($section)->setSection($newSection, $config); + $this->ds->removeSection($section)->setSection( + $newSection, + $this->onUpdate($target, $config, $newConfig) + ); } else { - $this->ds->setSection($section, $config); + $this->ds->setSection( + $section, + $this->onUpdate($target, $config, $newConfig) + ); } } @@ -151,8 +287,10 @@ abstract class IniRepository extends Repository implements Extensible, Updatable $query->addFilter($this->requireFilter($target, $filter)); } + /** @var ConfigObject $config */ foreach ($query as $section => $config) { $this->ds->removeSection($section); + $this->onDelete($target, $config); } try { From 3338040952e9e00dfa1173adc452e6daa2cece27 Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Wed, 19 Oct 2016 16:25:40 +0200 Subject: [PATCH 193/337] actiontable.js: Update row counter when detail area is closed refs #10691 --- public/js/icinga/behavior/actiontable.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/js/icinga/behavior/actiontable.js b/public/js/icinga/behavior/actiontable.js index 155e549a0..a7fc06a36 100644 --- a/public/js/icinga/behavior/actiontable.js +++ b/public/js/icinga/behavior/actiontable.js @@ -456,6 +456,7 @@ this.tables().each(function () { new Selection(this, _this.icinga).clear(); }); + $('.selection-info-count').text('0'); }; Icinga.Behaviors.ActionTable = ActionTable; From 6492f3c105a0fc97db91e6933f94b9c7001ff228 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Fri, 28 Oct 2016 10:36:13 +0200 Subject: [PATCH 194/337] NavigationItemForm: Validate that urls with credentials contain a protocol refs #12890 --- .../forms/Navigation/NavigationItemForm.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/application/forms/Navigation/NavigationItemForm.php b/application/forms/Navigation/NavigationItemForm.php index 4f2df85cc..44f16488c 100644 --- a/application/forms/Navigation/NavigationItemForm.php +++ b/application/forms/Navigation/NavigationItemForm.php @@ -56,6 +56,24 @@ class NavigationItemForm extends Form 'The url of this navigation item. Leave blank if only the name should be displayed.' . ' For urls with username and password and for all external urls,' . ' make sure to prepend an appropriate protocol identifier (e.g. http://example.tld)' + ), + 'validators' => array( + array( + 'Callback', + false, + array( + 'callback' => function($url) { + // Matches if the given url contains obviously + // a username but not any protocol identifier + return !preg_match('#^((?=[^/@]).)+@.*$#', $url); + }, + 'messages' => array( + 'callbackValue' => $this->translate( + 'Missing protocol identifier' + ) + ) + ) + ) ) ) ); From d207dcbd03bb70d885127c085f025e4e7bfef2fa Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 28 Oct 2016 11:47:45 +0200 Subject: [PATCH 195/337] Url: Throw ProgrammingError when trying to use Url::from* on the CLI refs #7051 --- library/Icinga/Web/Url.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Url.php b/library/Icinga/Web/Url.php index d2cbf1dd0..8ad89af40 100644 --- a/library/Icinga/Web/Url.php +++ b/library/Icinga/Web/Url.php @@ -4,7 +4,6 @@ namespace Icinga\Web; use Icinga\Application\Icinga; -use Icinga\Cli\FakeRequest; use Icinga\Exception\ProgrammingError; use Icinga\Data\Filter\Filter; @@ -139,7 +138,9 @@ class Url { $app = Icinga::app(); if ($app->isCli()) { - return new FakeRequest(); + throw new ProgrammingError( + 'Url::fromRequest and Url::fromPath are currently not supported for CLI operations' + ); } else { return $app->getRequest(); } From 82c7a5105069e6b2c61e55942937b7bac5380700 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 31 Oct 2016 08:35:45 +0100 Subject: [PATCH 196/337] IniRepository: Don't require a key column before the onInsert trigger fixes #13005 --- library/Icinga/Repository/IniRepository.php | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index 04b25c5cb..b292dc2bc 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -186,13 +186,14 @@ abstract class IniRepository extends Repository implements Extensible, Updatable public function insert($target, array $data) { $newData = $this->requireStatementColumns($target, $data); - $section = $this->extractSectionName($newData); + $config = $this->onInsert($target, new ConfigObject($newData)); + $section = $this->extractSectionName($config); if ($this->ds->hasSection($section)) { throw new StatementException(t('Cannot insert. Section "%s" does already exist'), $section); } - $this->ds->setSection($section, $this->onInsert($target, new ConfigObject($newData))); + $this->ds->setSection($section, $config); try { $this->ds->saveIni(); @@ -301,23 +302,25 @@ abstract class IniRepository extends Repository implements Extensible, Updatable } /** - * Extract and return the section name off of the given $data + * Extract and return the section name off of the given $config * - * @param array $data + * @param array|ConfigObject $config * * @return string * * @throws ProgrammingError In case no valid section name is available */ - protected function extractSectionName(array & $data) + protected function extractSectionName( & $config) { $keyColumn = $this->ds->getConfigObject()->getKeyColumn(); - if (! isset($data[$keyColumn])) { - throw new ProgrammingError('$data does not provide a value for key column "%s"', $keyColumn); + if (! is_array($config) && !$config instanceof ConfigObject) { + throw new ProgrammingError('$config is neither an array nor a ConfigObject'); + } elseif (! isset($config[$keyColumn])) { + throw new ProgrammingError('$config does not provide a value for key column "%s"', $keyColumn); } - $section = $data[$keyColumn]; - unset($data[$keyColumn]); + $section = $config[$keyColumn]; + unset($config[$keyColumn]); return $section; } } From 4a791e6204d1ae56a894e8dd6a1a88541dca0234 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 2 Nov 2016 13:25:37 +0100 Subject: [PATCH 197/337] IniRepository: Don't fail if there is no trigger to be found --- library/Icinga/Repository/IniRepository.php | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index b292dc2bc..7413971a1 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -152,8 +152,6 @@ abstract class IniRepository extends Repository implements Extensible, Updatable * @param string $event The name of the event type * * @return string - * - * @throws ProgrammingError In case the table is registered as having triggers but not any trigger is found */ protected function getTrigger($table, $event) { @@ -162,15 +160,9 @@ abstract class IniRepository extends Repository implements Extensible, Updatable } $identifier = join('', array_map('ucfirst', explode('_', $table))); - if (! method_exists($this, $event . $identifier)) { - throw new ProgrammingError( - 'Cannot find any trigger for table "%s". Add a trigger or remove the table from %s::$triggers', - $table, - get_class($this) - ); + if (method_exists($this, $event . $identifier)) { + return $event . $identifier; } - - return $event . $identifier; } /** From ca8e4ea924be3c56b8fe195031868d4755d49ce4 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 3 Nov 2016 12:01:03 +0100 Subject: [PATCH 198/337] Conform to code style guidelines refs #11652 --- library/Icinga/Application/Logger/Writer/PhpWriter.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Application/Logger/Writer/PhpWriter.php b/library/Icinga/Application/Logger/Writer/PhpWriter.php index 8e9061280..98419823b 100644 --- a/library/Icinga/Application/Logger/Writer/PhpWriter.php +++ b/library/Icinga/Application/Logger/Writer/PhpWriter.php @@ -16,7 +16,8 @@ class PhpWriter extends LogWriter /** * {@inheritdoc} */ - public function log($severity, $message) { + public function log($severity, $message) + { error_log(Logger::$levels[$severity] . ' - ' . str_replace("\n", ' ', $message)); } } From a24999ff7fcdd50f0d4c3601255c885f7be96a24 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 3 Nov 2016 12:16:48 +0100 Subject: [PATCH 199/337] PhpWriter: throw an exception on logging failure refs #11652 --- library/Icinga/Application/Logger/Writer/PhpWriter.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Application/Logger/Writer/PhpWriter.php b/library/Icinga/Application/Logger/Writer/PhpWriter.php index 98419823b..51c2fea77 100644 --- a/library/Icinga/Application/Logger/Writer/PhpWriter.php +++ b/library/Icinga/Application/Logger/Writer/PhpWriter.php @@ -5,6 +5,7 @@ namespace Icinga\Application\Logger\Writer; use Icinga\Application\Logger; use Icinga\Application\Logger\LogWriter; +use Icinga\Exception\NotWritableError; /** * Log to the webserver log, a file or syslog @@ -18,6 +19,8 @@ class PhpWriter extends LogWriter */ public function log($severity, $message) { - error_log(Logger::$levels[$severity] . ' - ' . str_replace("\n", ' ', $message)); + if (! error_log(Logger::$levels[$severity] . ' - ' . str_replace("\n", ' ', $message))) { + throw new NotWritableError('Could not log to ' . (ini_get('error_log') ?: 'SAPI')); + } } } From 8952434393ab59a30bc6c61f3d3f9e4ee293ff3c Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 3 Nov 2016 12:35:40 +0100 Subject: [PATCH 200/337] PhpWriter: prefix messages with the app name as in Syslog refs #11652 --- .../Config/General/LoggingConfigForm.php | 4 ++-- .../Application/Logger/Writer/PhpWriter.php | 21 ++++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/application/forms/Config/General/LoggingConfigForm.php b/application/forms/Config/General/LoggingConfigForm.php index 1ca6bf8dd..6a5cb9238 100644 --- a/application/forms/Config/General/LoggingConfigForm.php +++ b/application/forms/Config/General/LoggingConfigForm.php @@ -63,7 +63,7 @@ class LoggingConfigForm extends Form ); } - if (false === isset($formData['logging_log']) || $formData['logging_log'] === 'syslog') { + if (false === isset($formData['logging_log']) || in_array($formData['logging_log'], array('syslog', 'php'))) { $this->addElement( 'text', 'logging_application', @@ -71,7 +71,7 @@ class LoggingConfigForm extends Form 'required' => true, 'label' => $this->translate('Application Prefix'), 'description' => $this->translate( - 'The name of the application by which to prefix syslog messages.' + 'The name of the application by which to prefix log messages.' ), 'requirement' => $this->translate('The application prefix must not contain whitespace.'), 'value' => 'icingaweb2', diff --git a/library/Icinga/Application/Logger/Writer/PhpWriter.php b/library/Icinga/Application/Logger/Writer/PhpWriter.php index 51c2fea77..913ea07de 100644 --- a/library/Icinga/Application/Logger/Writer/PhpWriter.php +++ b/library/Icinga/Application/Logger/Writer/PhpWriter.php @@ -5,6 +5,7 @@ namespace Icinga\Application\Logger\Writer; use Icinga\Application\Logger; use Icinga\Application\Logger\LogWriter; +use Icinga\Data\ConfigObject; use Icinga\Exception\NotWritableError; /** @@ -14,12 +15,30 @@ use Icinga\Exception\NotWritableError; */ class PhpWriter extends LogWriter { + /** + * Prefix to prepend to each message + * + * @var string + */ + protected $ident; + + /** + * {@inheritDoc} + */ + public function __construct(ConfigObject $config) + { + parent::__construct($config); + $this->ident = $config->get('application', 'icingaweb2'); + } + /** * {@inheritdoc} */ public function log($severity, $message) { - if (! error_log(Logger::$levels[$severity] . ' - ' . str_replace("\n", ' ', $message))) { + if (! error_log( + $this->ident . ': ' . Logger::$levels[$severity] . ' - ' . str_replace("\n", ' ', $message) + )) { throw new NotWritableError('Could not log to ' . (ini_get('error_log') ?: 'SAPI')); } } From 4689f224b5cfbcfd8c69d4149095b0c9f5cde5ef Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 3 Nov 2016 12:51:13 +0100 Subject: [PATCH 201/337] Fix missing translation refs #11652 --- .../locale/de_DE/LC_MESSAGES/icinga.mo | Bin 60998 -> 61067 bytes .../locale/de_DE/LC_MESSAGES/icinga.po | 5 +++++ 2 files changed, 5 insertions(+) diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.mo b/application/locale/de_DE/LC_MESSAGES/icinga.mo index 3413b7cbff069bdec8a4239867b2db1b767e0dea..fff6fd30c554c4786172c9983231c64d477ae0c3 100644 GIT binary patch delta 10016 zcmX}y34Bf0+Q;#IL?$vxh>(a735g(yq=v*yXd(k5lu%V^XiY^C%Bd1#2YSpyX^UHW zl{UC;%~PseMC-P+HMBL|qSI~Pw!G^7{jK$R??-*tv(Daot@W&D?GyRj%CEf^UGj2Y z4z0e-;eSJ_IZhN-4|ALZFUPqPr(VZdoa#7&=%hJLC`4i_Y>90g$L$QI@faP;u@|03{y5?3j^m9vs2lrW1dc!-oPo^KnS(WP zzWsbD1`%(=5Zq@!KW+OjVr}|wVKC!659|kl%t8Z2VIa0b4cGybu?RcjJe-J?SP$E? zj4&)j4LA~YT^TAfd$18+zy){@=i{8VWSa4vz;-+Y+aQ0O3H;R^|Bg!a&)5N@xkxK5 z#$X(Yn($OqrWT=Az67;<8?2S6t-XVd(LcjXC<)!w>Bytui~TSF2U|y?GVy}_e721j zq4szcCg2flj(4mPnQR|%cdUbhu_Zo_+WQTtiC1Nk|128c*$Wa`rfzrwHSjom2WMbs zY(!qW;Bf4Z8&MOvhYc{c69tJ~P??;8Jlgq(?f(_^+~m%VlZzcYyG^QIp+gsbh)V5` zn1>HhsmxB!*hf$bf`}43C@d!7KdNd|u zLtKnga0gN!PBRKYH$H_d(wT|M(0iYfGQ>f=$L}lUzmf
faUt87(BN*R#j)tmw4tB;$q>dcl910GzQ4ctXDzclX6?pY_oOF!Fcq~L+Hyx|v zJE+XOi~e{BHPMr(TKEjzDz0y7B;rHV8!R!`T$q7A#NAPEz#Qy{FJd58;R3vjx_=}` zCI&sIH|I`lj;GNV|HK;T$~RjWkWc>A5k-em+7z|27O0i>LhacIRJE6(u3v;2U^!~b zwqp#QM@{%QR0jUn>MAhL_d!i40QKCq1>|1?=F*|P8j0GQ=~xq&+5Wds6WD33L=A8O zH{c~K#+iLg=DtGJ&NXa|UVTjs#bYfOTZ#en7rV)4FpW{D0n1PmDMt;s5|xRqsEJjg z_VlX#{0|Hvu2E<*5{Y{CrlGbd$NB^&6PKd4Y9%J2`zQ^~@F&!ZC5-oK94258=A#}o z6gA_~s4Xb7pU**Uod=crRj37Qz%;Bt?fos(fIndjHYj@Z9JkYvhHiM=T8w(oIINA6 zF$@=>igi8K!F~4gPf?k>i5lp?*a+{V-iQqbn62o5y1y8;b!AvX=YJ-RR5})50`9|j zyn))oz=5Ve2~})Gs0>U;O>8b|0;_Gj8I`GzP{-|KYZYp%E}}B~J%;Q2|4BnD3VFgj zI38OPr=U_l4C~=o)Xe9j&hx8S4Od_fT#X~}Dr!MJ2APykMJ;3#YJtbF8UBE7t*Gu` zQ-qnQ4|=0&;sqRsE3qE>QQ}$H7`5l4P%9aSD&m=_%)Ex0=ql7!zKP1jCe#+akILBY zV)CyWkI|B3s2N}r>OosE8h7Jk_-|AO1D-S)%s^fD zIQGQ;I1JZ4>88SI{6$AQ%%e9F=c7`!5jDXxsFhqs?csIQ%Kr!TZRq`!Id-wA>)WFS z%(fPxYGpX;SWiY}biSL0X8tzn!H4Y!pP~l1gE}4eZ5%Y*4AcPCpJwfe4T%S#?wf|1 z$N~(sG8wya!dhRj94Ify(4h z*c}t8$Zj|eHDCqmxD zhoiP|5-RoO*cg{#Jnq5fcm*|~nj`J^e?1zyFcr0;{-_IvqaHNg`Vwlu#i$3ZKwmtH z-gpW%k#nd${|lV}W~p_MO09iIc3g}-4B%zW1PGHRkHP}ki? zy=WRgXZE^3wjy4QE$}32<$qu@HXLm-Sb#H$-J?1G)QR&K9eUvKG3K9Et56dP9BcNr zi*+b=r++%~c6JV-CKNHwaXMj!bvml3_oAx)E9{RyVG(AHH$VTc8_)h1&~clNOw4@V z`~#v4sW)dA@|JY!PcSQa0kx8~sEM4$moRi9t3eMYVAv#+;trTj?8ac+g=u&k+v7cb zu!ctZ3ueXVFoM{7vROei)+X+VT46toK@Y~_P8(mqeZ+s_9Nay{WHf`usWQFwrHX^=m^?k|238>WPSf`@)bQ4lW z&S}&Hy3R7G&qZxf5h{bl)`_UhE=0Fdzlz2Me9K-i?PXq@#7j^q3z}_SIH8z9+!06M zG<*lIU>%%I<;hhTjoYnfunF;9jKR=ylZn>lEh*t zI{yI+Oa?k&3h~>hV|5-iLH~uOMv_olR)l)aY>dJ8P^ax*ZW{hHsx30-I{($Ebvt#8;5He)3H z`%wd5M6K)&CSdTZkA8pWv_q2OJdcg=PmIUN*G!Rh#n!}qQ1_LiGO-;=zT5eN#wikEbUOb65ZjH&rJ+KW<7L4x% zc}&q|B45?cAnbxiF%-R*nh8YUTf|K;3oEez1DBa^!=c!UcsDl38(4^;%gw916nhZw zwYpZ2f32V=jeP8n$+!ty;^$Zgy;t%+$4E@WY4~UlG0x>UU!b<)!79h0JRINErplM1 z`gdV{Jcz;gC90ObUrqiaXaue?RT__R#2J``PoawIHSCPxYt82aP!pbky8krR#v7

b@4Wap%;adh;>odb;U?@51ijRDp<+9MD!$(_7n^M~zk;2N zMa0MPLyX#FelvO&wa0#&&Hd@vf_MaKOW(i_cn!U=@!w1)o1wNi?U8O~LBofRUZ~>9 z$F?{LmAY-H8xEsZcnTlGi#P^D-ZdGUiO&%)MZG7SEyiHf1fo$HXoCKjg*6!8DWIVg z4@T|vXw(CyVl$kF@wfxE;xABpUwx~&E*#^C<4_aN!4#Z>zPQ_Z0IL&Mq9$|--OXv7 zqoJaBfb}u-JyU!wurYCWtcj(lj7&ro;XLbV)PuJnf1D5bs~Cs=-ArIFDzis10dHXv zhHhj3>$~{o+h$Tb=zViMMq*7an2MUv9Mm~qkGgRW#^QBUO8vK+YHp4?B|T9CmY}X% zh??;G*2CDE`22SAuPXK1VLCdZ2AF_)(0WuV_hT^ri2nExH9`Lm%$H3aR2*&NL~Cmd zr#};OupidI^{5)!>ZYN+`vwEhmlD@NwNWc>g4(MNs7!RT{X`^xbKGFlmd5r(zmzvGHXLBM#nW zG8T(EC0$UplaE@+1PsMRsEoXcY>nI5MMH0*i>TE9X5+@Y&4W6k2JDT>%q-M{m!MX- z6N7LsY9S}F3!cR?to0AG#fz{d@nO_Le!^_VcN*<6U!$W@58RIp@tEzuhTVwM|H%o! zGE{ARgN67IwfBV|nfJgj>tqa}{}oitti!taH}u1U=%e$0f`(Rb)_NJW6?bj?2X-a) z-D`gPm5s}Zui`$O!e3f>+J2L|x4BkX-+3)DiqfzsKd2)>D3+DFVONkr9L&m(R# z)2Hdsfb%dB*PvEdiK$qHDd=_7n2y@AQdG51My+TLYC#)OTeu(7@dQ4Bf8k&pc+6yE zr<;Z@Jcf^Iz%VbdH+|`Jx7_g}T0>?Qe-%XXB2KY*=? z-JjA>HTqPV3xcr+aUE2355{S@19e^Vk7;8X7U6Z&gfdT&q2fsW#JtP9qE;{hRaEm)$7?le&o-kTd<1>)b5!atp)&Ho_6M9aTM>rJ zOcLt)u2>x($55UBJQ_pjcnTlKgQ(-;f6DAxH&iCpVKM%KgE0SJ=0&s>dlH|L}qdCpcZK1uvE z=HXGy!O&05LWWx3#W?zZL1iN1GxO!s65Va+D4~&v>o65hU|+nC{V?x5-&puIF2t_? ze)O}Oa~iXV13ovI>4&;*2X4TEFU|n-I@Jtza{1#rsgTQu8Y_p;oBt#^QJA!Ob}3YqOx}OXfEv&trS~Z(Jh( zT5+SxX7377dtHv2(3kiMMqM#4s4b`gtFRP*#a>u^)tsJ97(x6VK8eTB3!8jnPERv@ zmN*6fz_o4~{xp_-Yre>I}GUejv%lc;W%r1ZQFzzJn^p3z&}8ZbUPfYR>H(qS1(s+o%->-ZW-l3h^k^z-urL51}S-!y0+Z{Df10n$QBQ zz+I?;M*YVuqzwIt=c2agbqvw@f0u?X{3rV2C-@_t!$>^*JtqS%VKtn7+tkWTRFQ7S z=kYQg#)3O06QOraCYqr){hiPUdtgoMjlPWU473*v!vNxEFc2qVD9%D{(Nff&zl&PQ zAykS#vGE1e{oh~>`~m&(Pt?F(KbTC{KrJ*0)740$p&4eP9^4i6pd8d*4n#e09ERXD z&&9Y5SFxu~`~X)U&zSgkT>U+@nx1kMcrG>l%$4K$pjju^6P}-&wQ?1D8YRrGmR-%W zIU(9r! z&I6i$L-|OXhX+rdDYQPjs!yN2_lW+oV!!S%_ z8Nt{CHQ->>b)!+4*?`e_0L$?*&c%uC$TZ_ScWHFM`1X#&FXu`A=z(vcQhg3PqdymE zrF}5~2csrD3YDoTsFhDgRqr+HHdJX(;)D4As0l@MB>%N&bfHljyJLOqV;zjjM6vyR zf{mx3YFv&9_z5QANo$=>juT4U8bh!Trs5N*+P{XHcvUCzpF`uQy&#-r>V}6=0}scw zI2N<9A$jeJ`S=JfMor{0Mq+Rl`vuccnH-Be+F5V=&!e6jmF+nFFg4q4QuQ(&x^N9D zwP!FFuc1=eg;6!Y2>bt z8Y8h8PQyvK8rc?3V>Utr4nP*^j6*%(Bh&--pzg0m-G9x-0X-e39&v)THEM5UVQZOx9pcwer(iki`j4z%pq_IOm5Jk6 zivL26q1!3yWmfc@bq?x*E3px-Lp@+8YNCfw6RAcGbOCkU4b<`Td&pcLflY}MZ2S=F z+&_X^;1~?n`JZJ!SdOuLuo0EIqo@a*#u&Vd+H}zmo8vYKHGwj$gY!_QU@x%RkE(wnDL#VG_bWiQ$iD{coM&n^7*(4R^ut-Ue-UZ|?^w5? z1~`DL@mnmyaRW@|4x#qWF? zsEKVu)%39a{1*%){tJ~6ze4lsjYXBHqxE4-BOZh*)m%(QcO?zY@GR=Z;>{M1!!Y#6 zEYyQ?Q8O+=m0+~}d?G5fGf=57M=jtrY>jJCwXZ=9cow6u!NB{^aXYCrbVGYT5qKE2pv*^2%15CVvIMojE!YB2qdS&H z?V)B9CZj&+gxV9uI3DL>7~aAh3}UOR=0i{`8IIb-<4~EIikfISs+6yyGO+|zqJN+= zwyuc$>&8#$(2cuMEBh8(;*U5LYY$`9I2)JX->3nWJZ2uW0=2?**ap8sW$+FvgGs~9 zb?vbiaZh|47Yrx={b~G0M|lA|8mPgOra#u&7Ms!E8+G3p z)Wl0s*O#MC(`t;st*A}vK0-q`ow;6}{B3)mBr#~7bSt>`n< zbth0SnxGO>>z>$>xB^pfJ8I>>U>XKIV=|bHQ;Ekv!}({QIKR=M2j-78f3+${P3SJF zw&~-Hx!8la1bHhvn^6<0GoCLhOtO}sHuVRnU4IB4!Lv9J+dON2{$KDc_0OZ@1Rb3) z`8o3!h|$PCbKXVXl1{zn%}R<f|cr z;AJ z#CSZ0n!p`Y=G^I}CKZLKnXWPw~&m2T;x<4=!?_dWEo??z? zF4iF~zigtuXDjj}o$y(R}^?)o4$03-A6Hx=LLe;nu>*FcZ z121DeyoEvN&9imAzT-9FM2x^Ts25cMHsQBZ!XG*&bF3RNnz-8fr!{PvNqtA_C{#_C zAlt~;+?9aU8PfOh={c9wwpJ40AeC zaRl)gT#Mgh2u@(<$#QIptE@XQmiQD#q1P;viI`dBUn$9^BNB(9Zk%FWVf`GnTWe4g zzHRkpNBI)_p;8)z`aBUcFauS>V(TP?~D@EaaxI*;1Nu~o2XOLtin8}FGdkhM4hrFSQihuX=Kr;M$J5Ao-q=2 zoZ?WM>2d1>R4Qkq4}O4t_%Uj;?Le*Y2Ydjp*w~qGN)wLS3u&n9-MwsMsQq9(HsOQW zsDam@R<;$jvKnlQUJJ~(Vh4;S-i^)iJZf|Ky=q=);i&s^F&-x)Nq1J`bTy+Env}1x zzGvNxTH&YG9o9Xl)E&eIcocQ+&!Jv8H*9d}99+2Ad}$rWLB#P(%-60PlZZEAA)dejOj~NczGqs$MlB%l z4f5ZgMpGI}%{WZORTzTb;bV9nTVwZSwuTtzqK2qa>|4&H@i0c=uob3%Duxr!#Q}hq|#%#Z6is0S=X-MObuXfB zs6ef7F}A@qI2M0GWvtf*zQS=B`r|=s4Qc`xQ5m?3dS1YL=4(0(0~z0mrJ)&Sq8`u{ zTVNhG$5Pa;UX7~#x2WsRU>yF2n)n0nn=hq)Sew{worATA7osM#81F7t?6*ZxLsB=CFb>no5!HuYt9>FH~U(_iH+++q!MO{~bn(!oR1-2qy ziQ1$;Y$E^asJ+t$f+~%0zuse>^Is+1M0|Q7@{Qs1m)6zPJ^&C%(kC zcoVgkQYy{Bg{acGU!tLzxv?i!U>Y9AEOc!#aaU|j{Jf3VV=!?IDr3K*N?C8K**lF< z6YhvXScuBV)2Px+MM~#(*3eLGcbX383hF_%x0wM$P?_nC+LVu?R#=AqI1{xJ4|c_6 zSc*SmCKi5b-V+t5g?)~_@iOM={AYe<9ylAD@xcPj!;i2#-ovM{+vjGb?_(kH0n}cI z+-}|j3DztOr2i4ro*9Xa@LANJnTuM$BJ^i`XPFwf9#x8MHr|EZh!5iu4E};|JbVwo z#BBc1%I~5w=C#ArHV{?1HrO6tL}hR@s$^A|i^tGiLL+9US@A+tYR+O3CjHYqXb@^e zZ=hDR0rj9yup#clW>|x&?Jd;j5mjd3G}Io*MqO8cjd4O1`PZ(TPlt{}CF;hXP#0ds zP;9Wv7>jyPM;qs&Qd^8+ScWa}4Gh8k)>Eicas&0g2;6NZn!MX>2F#-)kq<_oR=5x| za5<*qH`aToQl;%NDbGSpv>$3kV^JlXjhVOzhv0r3iqU&bM#@mvEpXG&1D9FXqgJ>L zwMh=5CUh886M zjoRI@`}hkYmZC2FFK))WI1o4PHxu$XU`|0Wsuayo6KHGgg9*gLu{D-sC)|l_O1E>H zMl>CM2h9pnP@8H1>Ucecs@ZtdgXf_yu0o}L9V#RHY=1SX6em%cxoP|TzBZ>N2$kVR zI85h1k;cPx%talSBdD6yKV&j75{rnxz(=w1H|9k&0ecZI#WXyP!!Y1m<5|J9fRDjweD^YuGYYqADO5-*ioGhpFPv%|i{A|AUT4F5y zy-_O|k6Q68jKO2r1#h9QYkPwK{)0uh4zo|11zp66#2rqVA3`^w7JT^>`B&{CPn%lj zq9(K!=i>!zj?bSl11?8>7wo`?Fz&25J>#%3@kAVs3(y;{VjaAWWAHD$g~jL0v3|_` zFY`S<3LEml98AIGs8sDot^5o|;Z55g$})qAQ!x~~VGItz2rR<{T!G5iUM$Ao^JZeD zsQcYzG`iDRgq^V(>+8Y`X5~>BOWX@}exJc4d>b?IThs)-|6?W+hW^B9sEK4*i!gw= z6ce!=qjmnb(9nvHTWei3Z>|i~z#}kDH=ri4$$B2!5r_R^CX|nr#8XiNWn3}~>4qvn ze^iMEV<0|<_y7N&S7_9x<8{1-D^SO*;xaoG*P$2oxMKE7FH9w#jL+eE+=XGknoOKP zmE=15V4bVxh2@Wa#388hqS41qBY}oe-V%eb6RI}-QG4Ji)C#7cQajVe9@KqrVLf~g z>*5yl#hplsov%;>UbJ3AP3%vM(}VBQ(1Uz`GcylHJunfKs!Y$NxQ?!&p19_NTzQ_d z=4)Mrp6K{PuKu2@@kd>~J-b?DxdwY&2`ya(p7ewnB+Iicp{eT;Pjy1PYe41Qgg!3U z5YNL&--q<$voaSyLGaW)_$I#}^`s=9aSik|NGWjTRz8u^$mJU3DNUW^>RWj|b*Rgg zU)e8hzsuFf6Ou8>RaChk<7|GVXVzFR|Frfg8EvD|(>tVRCZ%O~{OA7VYF}BeeDsLQ J=~wHy{s#s^{M`Tm diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.po b/application/locale/de_DE/LC_MESSAGES/icinga.po index d099a821b..935c5f8a3 100644 --- a/application/locale/de_DE/LC_MESSAGES/icinga.po +++ b/application/locale/de_DE/LC_MESSAGES/icinga.po @@ -2981,6 +2981,11 @@ msgctxt "app.config.logging.level" msgid "Warning" msgstr "Warnung" +#: /vagrant/application/forms/Config/General/LoggingConfigForm.php:40 +msgctxt "app.config.logging.type" +msgid "Webserver Log" +msgstr "Webserver-Log" + #: /vagrant/application/views/scripts/authentication/login.phtml:5 msgid "" "Welcome to Icinga Web 2. For users of the screen reader Jaws full and " From a85d259b0afc3a1fb25f27fb12b2b972fe7d5843 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 3 Nov 2016 14:06:02 +0100 Subject: [PATCH 202/337] PhpWriter: replace newlines in the message only if PHP logs to Syslog refs #11652 --- library/Icinga/Application/Logger/Writer/PhpWriter.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Application/Logger/Writer/PhpWriter.php b/library/Icinga/Application/Logger/Writer/PhpWriter.php index 913ea07de..310cd7678 100644 --- a/library/Icinga/Application/Logger/Writer/PhpWriter.php +++ b/library/Icinga/Application/Logger/Writer/PhpWriter.php @@ -36,9 +36,9 @@ class PhpWriter extends LogWriter */ public function log($severity, $message) { - if (! error_log( - $this->ident . ': ' . Logger::$levels[$severity] . ' - ' . str_replace("\n", ' ', $message) - )) { + if (! error_log($this->ident . ': ' . Logger::$levels[$severity] . ' - ' . ( + ini_get('error_log') === 'syslog' ? str_replace("\n", ' ', $message) : $message + ))) { throw new NotWritableError('Could not log to ' . (ini_get('error_log') ?: 'SAPI')); } } From da2940a8110b5b2109777f94a79dd3656ed7e38d Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 3 Nov 2016 14:31:47 +0100 Subject: [PATCH 203/337] NavigationItemForm: Don't bypass Url's own optimisations when saving urls refs #12133 --- application/forms/Navigation/NavigationItemForm.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/application/forms/Navigation/NavigationItemForm.php b/application/forms/Navigation/NavigationItemForm.php index 44f16488c..1bd7eb97f 100644 --- a/application/forms/Navigation/NavigationItemForm.php +++ b/application/forms/Navigation/NavigationItemForm.php @@ -99,8 +99,10 @@ class NavigationItemForm extends Form $values = parent::getValues($suppressArrayNotation); if (isset($values['url']) && $values['url']) { $url = Url::fromPath($values['url']); - if (! $url->isExternal() && ($relativePath = $url->getRelativeUrl())) { - $values['url'] = $relativePath; + if ($url->getBasePath() === $this->getRequest()->getBasePath()) { + $values['url'] = $url->getRelativeUrl(); + } else { + $values['url'] = $url->getAbsoluteUrl(); } } From e655699917a473c464215798d3316741c4fd2bcd Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 3 Nov 2016 14:35:18 +0100 Subject: [PATCH 204/337] Repository: add support for per-table datasources refs #13034 --- library/Icinga/Repository/Repository.php | 6 ++++-- library/Icinga/Repository/RepositoryQuery.php | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/library/Icinga/Repository/Repository.php b/library/Icinga/Repository/Repository.php index 379614f22..5e2b61b14 100644 --- a/library/Icinga/Repository/Repository.php +++ b/library/Icinga/Repository/Repository.php @@ -263,11 +263,13 @@ abstract class Repository implements Selectable } /** - * Return the datasource being used + * Return the datasource being used for the table $table + * + * @param string $table * * @return Selectable */ - public function getDataSource() + public function getDataSource($table = null) { return $this->ds; } diff --git a/library/Icinga/Repository/RepositoryQuery.php b/library/Icinga/Repository/RepositoryQuery.php index 586403873..4f42fa7c2 100644 --- a/library/Icinga/Repository/RepositoryQuery.php +++ b/library/Icinga/Repository/RepositoryQuery.php @@ -109,7 +109,7 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera */ public function from($target, array $columns = null) { - $this->query = $this->repository->getDataSource()->select(); + $this->query = $this->repository->getDataSource($target)->select(); $this->query->from($this->repository->requireTable($target, $this)); $this->query->columns($this->prepareQueryColumns($target, $columns)); $this->target = $target; @@ -716,7 +716,7 @@ class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Itera if ($this->query instanceof Traversable) { $iterator = $this->query; } else { - $iterator = $this->repository->getDataSource()->query($this->query); + $iterator = $this->repository->getDataSource($this->target)->query($this->query); } if ($iterator instanceof IteratorAggregate) { From fb3f3ed788daec80a36e2fc8081647f449d67b5c Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 3 Nov 2016 15:06:37 +0100 Subject: [PATCH 205/337] IniRepository: add support for per-table datasources refs #13034 --- library/Icinga/Repository/IniRepository.php | 45 ++++++++++++--------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index 7413971a1..1df35d7e1 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -25,11 +25,11 @@ use Icinga\Exception\StatementException; abstract class IniRepository extends Repository implements Extensible, Updatable, Reducible { /** - * The datasource being used + * Per-table datasources * - * @var Config + * @var Config[string] */ - protected $ds; + protected $datasources = array(); /** * The tables for which triggers are available when inserting, updating or deleting rows @@ -179,16 +179,16 @@ abstract class IniRepository extends Repository implements Extensible, Updatable { $newData = $this->requireStatementColumns($target, $data); $config = $this->onInsert($target, new ConfigObject($newData)); - $section = $this->extractSectionName($config); + $section = $this->extractSectionName($config, $target); - if ($this->ds->hasSection($section)) { + if ($this->getDataSource($target)->hasSection($section)) { throw new StatementException(t('Cannot insert. Section "%s" does already exist'), $section); } - $this->ds->setSection($section, $config); + $this->getDataSource($target)->setSection($section, $config); try { - $this->ds->saveIni(); + $this->getDataSource($target)->saveIni(); } catch (Exception $e) { throw new StatementException(t('Failed to insert. An error occurred: %s'), $e->getMessage()); } @@ -206,7 +206,7 @@ abstract class IniRepository extends Repository implements Extensible, Updatable public function update($target, array $data, Filter $filter = null) { $newData = $this->requireStatementColumns($target, $data); - $keyColumn = $this->ds->getConfigObject()->getKeyColumn(); + $keyColumn = $this->getDataSource($target)->getConfigObject()->getKeyColumn(); if ($filter === null && isset($newData[$keyColumn])) { throw new StatementException( t('Cannot update. Column "%s" holds a section\'s name which must be unique'), @@ -214,7 +214,7 @@ abstract class IniRepository extends Repository implements Extensible, Updatable ); } - $query = $this->ds->select(); + $query = $this->getDataSource($target)->select(); if ($filter !== null) { $query->addFilter($this->requireFilter($target, $filter)); } @@ -242,16 +242,16 @@ abstract class IniRepository extends Repository implements Extensible, Updatable unset($newConfig->$keyColumn); if ($newSection) { - if ($this->ds->hasSection($newSection)) { + if ($this->getDataSource($target)->hasSection($newSection)) { throw new StatementException(t('Cannot update. Section "%s" does already exist'), $newSection); } - $this->ds->removeSection($section)->setSection( + $this->getDataSource($target)->removeSection($section)->setSection( $newSection, $this->onUpdate($target, $config, $newConfig) ); } else { - $this->ds->setSection( + $this->getDataSource($target)->setSection( $section, $this->onUpdate($target, $config, $newConfig) ); @@ -259,7 +259,7 @@ abstract class IniRepository extends Repository implements Extensible, Updatable } try { - $this->ds->saveIni(); + $this->getDataSource($target)->saveIni(); } catch (Exception $e) { throw new StatementException(t('Failed to update. An error occurred: %s'), $e->getMessage()); } @@ -275,19 +275,19 @@ abstract class IniRepository extends Repository implements Extensible, Updatable */ public function delete($target, Filter $filter = null) { - $query = $this->ds->select(); + $query = $this->getDataSource($target)->select(); if ($filter !== null) { $query->addFilter($this->requireFilter($target, $filter)); } /** @var ConfigObject $config */ foreach ($query as $section => $config) { - $this->ds->removeSection($section); + $this->getDataSource($target)->removeSection($section); $this->onDelete($target, $config); } try { - $this->ds->saveIni(); + $this->getDataSource($target)->saveIni(); } catch (Exception $e) { throw new StatementException(t('Failed to delete. An error occurred: %s'), $e->getMessage()); } @@ -297,14 +297,15 @@ abstract class IniRepository extends Repository implements Extensible, Updatable * Extract and return the section name off of the given $config * * @param array|ConfigObject $config + * @param string $target The table whose datasource to get the key column from * * @return string * * @throws ProgrammingError In case no valid section name is available */ - protected function extractSectionName( & $config) + protected function extractSectionName( & $config, $target) { - $keyColumn = $this->ds->getConfigObject()->getKeyColumn(); + $keyColumn = $this->getDataSource($target)->getConfigObject()->getKeyColumn(); if (! is_array($config) && !$config instanceof ConfigObject) { throw new ProgrammingError('$config is neither an array nor a ConfigObject'); } elseif (! isset($config[$keyColumn])) { @@ -315,4 +316,12 @@ abstract class IniRepository extends Repository implements Extensible, Updatable unset($config[$keyColumn]); return $section; } + + /** + * {@inheritDoc} + */ + public function getDataSource($table = null) + { + return isset($this->datasources[$table]) ? $this->datasources[$table] : parent::getDataSource($table); + } } From e5b24eb467c3956c63635afc3b93d13f0003a629 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 3 Nov 2016 16:00:20 +0100 Subject: [PATCH 206/337] IniRepository: fix doc and method order refs #13034 --- library/Icinga/Repository/IniRepository.php | 24 +++++++++++++-------- library/Icinga/Repository/Repository.php | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index 1df35d7e1..ec9b4b1cf 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -27,7 +27,13 @@ abstract class IniRepository extends Repository implements Extensible, Updatable /** * Per-table datasources * - * @var Config[string] + * + * array( + * $table => $datasource + * ) + * + * + * @var Config[] */ protected $datasources = array(); @@ -60,6 +66,14 @@ abstract class IniRepository extends Repository implements Extensible, Updatable } } + /** + * {@inheritDoc} + */ + public function getDataSource($table = null) + { + return isset($this->datasources[$table]) ? $this->datasources[$table] : parent::getDataSource($table); + } + /** * Return the tables for which triggers are available when inserting, updating or deleting rows * @@ -316,12 +330,4 @@ abstract class IniRepository extends Repository implements Extensible, Updatable unset($config[$keyColumn]); return $section; } - - /** - * {@inheritDoc} - */ - public function getDataSource($table = null) - { - return isset($this->datasources[$table]) ? $this->datasources[$table] : parent::getDataSource($table); - } } diff --git a/library/Icinga/Repository/Repository.php b/library/Icinga/Repository/Repository.php index 5e2b61b14..1d111b4d7 100644 --- a/library/Icinga/Repository/Repository.php +++ b/library/Icinga/Repository/Repository.php @@ -263,7 +263,7 @@ abstract class Repository implements Selectable } /** - * Return the datasource being used for the table $table + * Return the datasource being used for the given table * * @param string $table * From 38602379be8ed8de7f367c0fa4b258c5593baacf Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 3 Nov 2016 16:12:09 +0100 Subject: [PATCH 207/337] Repository: allow not to provide a datasource for all tables refs #13034 --- library/Icinga/Repository/Repository.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Repository/Repository.php b/library/Icinga/Repository/Repository.php index 1d111b4d7..73721c003 100644 --- a/library/Icinga/Repository/Repository.php +++ b/library/Icinga/Repository/Repository.php @@ -214,9 +214,10 @@ abstract class Repository implements Selectable /** * Create a new repository object * - * @param Selectable $ds The datasource to use + * @param Selectable|null $ds The datasource to use. + * Only pass null if you have overridden {@link getDataSource()}! */ - public function __construct(Selectable $ds) + public function __construct(Selectable $ds = null) { $this->ds = $ds; $this->aliasTableMap = array(); @@ -271,6 +272,9 @@ abstract class Repository implements Selectable */ public function getDataSource($table = null) { + if ($this->ds === null) { + throw new ProgrammingError('No data source available'); + } return $this->ds; } From e50dd10193627ee512c636dc78f6a958eac56ed6 Mon Sep 17 00:00:00 2001 From: Munzir Taha Date: Sun, 12 Jun 2016 00:20:30 +0300 Subject: [PATCH 208/337] Add Arabic translation Signed-off-by: Eric Lippmann --- .../locale/ar_SA/LC_MESSAGES/icinga.po | 3015 ++++++++++++ .../locale/ar_SA/LC_MESSAGES/monitoring.po | 4346 +++++++++++++++++ 2 files changed, 7361 insertions(+) create mode 100644 application/locale/ar_SA/LC_MESSAGES/icinga.po create mode 100644 modules/monitoring/application/locale/ar_SA/LC_MESSAGES/monitoring.po diff --git a/application/locale/ar_SA/LC_MESSAGES/icinga.po b/application/locale/ar_SA/LC_MESSAGES/icinga.po new file mode 100644 index 000000000..68d0b6a30 --- /dev/null +++ b/application/locale/ar_SA/LC_MESSAGES/icinga.po @@ -0,0 +1,3015 @@ +# Icinga Web 2 - Head for multiple monitoring backends. +# Copyright (C) 2016 Icinga Development Team +# This file is distributed under the same license as Icinga Web 2. +# +# Munzir Taha , 2016. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: dev@icinga.org\n" +"POT-Creation-Date: 2016-05-23 20:16+0300\n" +"PO-Revision-Date: 2016-06-02 10:02+0300\n" +"Last-Translator: Munzir Taha \n" +"Language-Team: Arabic \n" +"Language: ar\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 &&" +" n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" +"X-Generator: Lokalize 2.0\n" + +#: /usr/share/php/Icinga/Web/Form/Validator/InArray.php:16 +#, php-format +msgid "\"%s\" is not in the list of allowed values." +msgstr "\"%s\" ليست ضمن قائمة القيم المتاحة." + +#: /usr/share/php/Icinga/Web/Form/Validator/InArray.php:19 +#, php-format +msgid "" +"\"%s\" is not in the list of allowed values. Did you mean one of the " +"following?: %s" +msgstr "\"%s\" ليست ضمن قائمة القيم المتاحة. هل تعني إحدى؟: %s" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/ExternalBackendForm.php:42 +#: /usr/share/icingaweb2/application/forms/Config/Resource/FileResourceForm.php:50 +msgid "\"%value%\" is not a valid regular expression." +msgstr "\"%value%\" ليست خياراً صالحاً." + +#: /usr/share/php/Icinga/Web/Form/ErrorLabeller.php:48 +#, php-format +msgid "%s (%%value%%) has a false MIME type of \"%%type%%\"" +msgstr "%s (%%value%%) لها نوع MIME خاطئ هو \"%%type%%\"" + +#: /usr/share/php/Icinga/Web/Form/ErrorLabeller.php:51 +#, php-format +msgid "%s (%%value%%) has no MIME type" +msgstr "%s (%%value%%) ليس لها نوع MIME" + +#: /usr/share/php/Icinga/Date/DateFormatter.php:171 +#, php-format +msgctxt "An event that happened the given time interval ago" +msgid "%s ago" +msgstr "قبل %s" + +#: /usr/share/php/Icinga/Web/Form/ErrorLabeller.php:53 +#, php-format +msgctxt "config.path" +msgid "%s does not exist" +msgstr "%s غير موجود" + +#: /usr/share/php/Icinga/Web/Form/ErrorLabeller.php:54 +#, php-format +msgctxt "config.path" +msgid "%s is not readable" +msgstr "%s غير مقروءة" + +#: /usr/share/php/Icinga/Web/Form/ErrorLabeller.php:52 +#, php-format +msgctxt "config.path" +msgid "%s is not writable" +msgstr "%s غير قابلة للكتابة" + +#: /usr/share/php/Icinga/Web/Form/ErrorLabeller.php:46 +#, php-format +msgid "%s is required and must not be empty" +msgstr "%s مطلوبة ويجب ألا تكون خالية" + +#: /usr/share/php/Icinga/Web/Form/ErrorLabeller.php:56 +#, php-format +msgid "%s not in the expected format: %%value%%" +msgstr "%s ليست على النسق المتوقع: %%value%%" + +#: /usr/share/icingaweb2/application/views/scripts/pivottablePagination.phtml:9 +#, php-format +msgid "%s: %d to %d of %d (on the %s-axis)" +msgstr "%s: %d إلى %d من %d (على محور-%s)" + +#: /usr/share/icingaweb2/application/views/scripts/joystickPagination.phtml:9 +#, php-format +msgctxt "pagination.joystick" +msgid "%s: Show %s %u to %u out of %u" +msgstr "%s: عرض %s %u إلى %u من %u" + +#: /usr/share/php/Icinga/Web/Form/Element/Number.php:138 +#, php-format +msgid "'%s' is not a valid number" +msgstr "'%s' ليس رقما صالحا" + +#: /usr/share/php/Icinga/Authentication/User/DbUserBackend.php:115 +#: /usr/share/php/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:114 +#: /usr/share/php/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:115 +msgid "(Case insensitive)" +msgstr "(غير حساس لحالة الأحرف)" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:334 +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:419 +#, php-format +msgid "A navigation item with the name \"%s\" does already exist" +msgstr "عنصر تنقل باسم \"%s\" موجود مسبقا" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:166 +#, php-format +msgid "A user backend with the name \"%s\" does already exist" +msgstr "خدمة مستخدم باسم \"%s\" موجودة مسبقا" + +#: /usr/share/icingaweb2/application/controllers/AboutController.php:19 +#: /usr/share/php/Icinga/Web/Menu.php:257 +#: /usr/share/php/Icinga/Application/Web.php:305 +msgid "About" +msgstr "حول" + +#: /usr/share/icingaweb2/application/controllers/AboutController.php:20 +msgid "About Icinga Web 2" +msgstr "حول إسنجا وب 2" + +#: /usr/share/icingaweb2/application/layouts/scripts/parts/navigation.phtml:12 +msgid "Accessibility Skip Links" +msgstr "Accessibility Skip Links" + +#: /usr/share/icingaweb2/application/views/scripts/user/show.phtml:33 +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:24 +#: /usr/share/icingaweb2/application/controllers/UserController.php:70 +#: /usr/share/php/Icinga/Authentication/User/LdapUserBackend.php:256 +#: /usr/share/php/Icinga/Authentication/User/DbUserBackend.php:119 +msgid "Active" +msgstr "نَشِط" + +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:46 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/AddMemberForm.php:126 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:29 +msgid "Add" +msgstr "إضافة" + +#: /usr/share/php/Icinga/Web/Widget/Tabextension/DashboardSettings.php:25 +msgid "Add Dashlet" +msgstr "إضافة لوحة مصغرة" + +#: /usr/share/icingaweb2/application/controllers/DashboardController.php:76 +msgid "Add Dashlet To Dashboard" +msgstr "إضافة لوحة مصغرة للوحة المعلومات" + +#: /usr/share/icingaweb2/application/views/scripts/group/show.phtml:54 +msgid "Add New Member" +msgstr "إضافة عضو جديد" + +#: /usr/share/icingaweb2/application/forms/Dashboard/DashletForm.php:29 +#: /usr/share/php/Icinga/Web/Widget/Tabextension/DashboardAction.php:27 +msgid "Add To Dashboard" +msgstr "إضافة إلى لوحة المعلومات" + +#: /usr/share/php/Icinga/Web/Widget/Tabextension/MenuAction.php:27 +msgid "Add To Menu" +msgstr "إضافة إلى القائمة" + +#: /usr/share/icingaweb2/application/views/scripts/user/list.phtml:32 +msgid "Add a New User" +msgstr "إضافة مستخدم جديد" + +#: /usr/share/icingaweb2/application/views/scripts/group/list.phtml:32 +msgid "Add a New User Group" +msgstr "إضافة مجموعة مستخدمين جديدة" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:28 +msgid "Add a new group" +msgstr "إضافة مجموعة جديدة" + +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:45 +msgid "Add a new user" +msgstr "إضافة مستخدم جديد" + +#: /usr/share/php/Icinga/Web/Widget/FilterEditor.php:410 +msgid "Add another filter" +msgstr "إضافة مرشِّح جديد" + +#: /usr/share/php/Icinga/Web/Widget/FilterWidget.php:80 +msgid "Add filter..." +msgstr "إضافة مرشح ..." + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/AddMemberForm.php:125 +#, php-format +msgid "Add members for group %s" +msgstr "إضافة أعضاء للمجموعة %s" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:37 +msgid "Adjust the general configuration of Icinga Web 2" +msgstr "ضبط الإعدادات العاملة ل إسنجا وب 2" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:133 +#: /usr/share/icingaweb2/application/controllers/PreferenceController.php:32 +msgid "Adjust the preferences of Icinga Web 2 according to your needs" +msgstr "ضبط إعدادات إسنجا وب 2 حسب حاجتك" + +#: /usr/share/icingaweb2/application/forms/Authentication/LoginForm.php:105 +msgid "" +"All configured authentication methods failed. Please check the system log or " +"Icinga Web 2 log for more information." +msgstr "" +"فشلت جميع طرق التحقق. رجاء قم بفحص سجلات النظام أو سجلات إسنجا وب 2 للمزيد" +" من المعلومات." + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:68 +#, php-format +msgid "Allow access to module %s" +msgstr "السمحا بالوصول للوحدة %s" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:45 +msgid "Allow config access" +msgstr "السماح بالوصول للإعدادات" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:39 +msgid "Allow everything" +msgstr "السماح بكل شيء" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:43 +msgid "Allow to adjust in the preferences whether to show stacktraces" +msgstr "السماح بضبط الإعدادات لتسمح بعرض تتبعات المكدس (stacktraces)" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:40 +msgid "Allow to share navigation items" +msgstr "السماح بمشاركة عناصر التنقل" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:135 +msgid "" +"An additional filter to use when looking up groups using the specified " +"connection. Leave empty to not to use any additional filter rules." +msgstr "" +"مرشح إضافة لاستخدامه عند البحث عن المجموعات باستخدام الاتصال المحدد. دعه" +" فارغا حتى لا تستخدم أي قوانين مرشحات إضافية." + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:228 +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:153 +msgid "" +"An additional filter to use when looking up users using the specified " +"connection. Leave empty to not to use any additional filter rules." +msgstr "" +"مرشح إضافة لاستخدامه عند البحث عن المستخدمين باستخدام الاتصال المحدد. دعه" +" فارغا حتى لا تستخدم أي قوانين مرشحات إضافية." + +#: /usr/share/php/Icinga/Web/Menu.php:274 +#: /usr/share/php/Icinga/Application/Web.php:318 +msgid "Application" +msgstr "التطبيق" + +#: /usr/share/php/Icinga/Web/Menu.php:262 +#: /usr/share/php/Icinga/Application/Web.php:366 +msgid "Application Log" +msgstr "سجل التطبيق" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:71 +msgid "Application Prefix" +msgstr "بادئة التطبيق" + +#: /usr/share/php/Icinga/Web/Form/Decorator/Autosubmit.php:122 +msgid "Apply" +msgstr "تطبيق" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:50 +#: /usr/share/php/Icinga/Web/Menu.php:279 +#: /usr/share/php/Icinga/Application/Web.php:324 +msgid "Authentication" +msgstr "المصادقة" + +#: /usr/share/php/Icinga/Web/Controller/AuthBackendController.php:69 +#, php-format +msgid "Authentication backend \"%s\" is not %s" +msgstr "خدمة المصادقة \"%s\" ليست %s" + +#: /usr/share/php/Icinga/Web/Controller/AuthBackendController.php:62 +#, php-format +msgid "Authentication backend \"%s\" not found" +msgstr "خدمة المصادقة \"%s\" لا توجد" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackendReorderForm.php:65 +msgid "Authentication order updated" +msgstr "حُدِّث ترتيب المصادقة" + +#: /usr/share/icingaweb2/application/forms/AutoRefreshForm.php:44 +msgid "Auto refresh successfully disabled" +msgstr "عُطِّل التحديث التلقائي" + +#: /usr/share/icingaweb2/application/forms/AutoRefreshForm.php:41 +msgid "Auto refresh successfully enabled" +msgstr "فُعِّل التحديث التلقائي" + +#: /usr/share/php/Icinga/Web/Wizard.php:626 +#: /usr/share/php/Icinga/Web/Wizard.php:650 +msgid "Back" +msgstr "خلف" + +#: /usr/share/icingaweb2/application/views/scripts/config/userbackend/reorder.phtml:35 +#: /usr/share/icingaweb2/application/views/scripts/form/reorder-authbackend.phtml:4 +msgid "Backend" +msgstr "خلفية" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:39 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/DbUserGroupBackendForm.php:34 +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:57 +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/DbBackendForm.php:53 +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/ExternalBackendForm.php:32 +msgid "Backend Name" +msgstr "اسم الخلفية" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:295 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupBackendForm.php:180 +msgid "Backend Type" +msgstr "نوع الخلفية" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/AddMemberForm.php:104 +msgid "Backend Users" +msgstr "مستخدمي الخلفية" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:99 +msgid "Bind DN" +msgstr "ربط الاسم المميز" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:111 +msgid "Bind Password" +msgstr "كلمة مرور Bind" + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:181 +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:187 +#, php-format +msgctxt "preferences.form" +msgid "Browser (%s)" +msgstr "متصفح (%s)" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:214 +#, php-format +msgid "Can't add role '%s'. Role already exists" +msgstr "لا يمكن إضافة الدور '%s'. الدور موجود مسبقا" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:173 +#, php-format +msgid "Can't load role '%s'. Role does not exist" +msgstr "لا يمكن تحميل الدور '%s'. الدور غير موجود" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:240 +#, php-format +msgid "Can't remove role '%s'. Role does not exist" +msgstr "لا يمكن إزالة الدور '%s'. الدور غير موجود" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:273 +#, php-format +msgid "Can't update role '%s'. Role does not exist" +msgstr "لا يمكن تحديث الدور '%s'. الدور غير موجود" + +#: /usr/share/icingaweb2/application/views/scripts/user/show.phtml:79 +msgctxt "group.membership" +msgid "Cancel" +msgstr "إلغاء" + +#: /usr/share/icingaweb2/application/controllers/UserController.php:148 +msgid "Cancel this membership" +msgstr "إلغاء العضوية" + +#: /usr/share/php/Icinga/Web/Widget/FilterEditor.php:436 +msgid "Cancel this operation" +msgstr "إلغاء العملية" + +#: /usr/share/php/Icinga/Repository/IniRepository.php:64 +#, php-format +msgid "Cannot insert. Section \"%s\" does already exist" +msgstr "لا يمكن الإدراج. المقطع \"%s\" موجود مسبقا" + +#: /usr/share/php/Icinga/Application/Config.php:328 +#, php-format +msgid "Cannot read config file \"%s\". Permission denied" +msgstr "لا يمكن قراءة ملف الإعدادات \"%s\". الصلاحيات ممنوعة" + +#: /usr/share/php/Icinga/Web/Widget/FilterEditor.php:283 +msgid "Cannot search here" +msgstr "لا يمكن البحث هنا" + +#: /usr/share/php/Icinga/Repository/IniRepository.php:91 +#: /usr/share/php/Icinga/Repository/IniRepository.php:108 +#, php-format +msgid "" +"Cannot update. Column \"%s\" holds a section's name which must be unique" +msgstr "تعذر التحديث. العمود \"%s\" يحوي اسم المقطع الذي يجب أن يكون وحيدا" + +#: /usr/share/php/Icinga/Repository/IniRepository.php:123 +#, php-format +msgid "Cannot update. Section \"%s\" does already exist" +msgstr "تعذر التحديث. المقطع \"%s\" موجود مسبقا" + +#: /usr/share/php/Icinga/Web/Widget/SortBox.php:251 +msgid "Change sort direction" +msgstr "تغيير اتجاه الترتيب" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:146 +msgid "Character Set" +msgstr "مجموعة المحارف" + +#: /usr/share/icingaweb2/application/forms/Config/General/ThemingConfigForm.php:53 +msgctxt "Form element description" +msgid "" +"Check this box for disallowing users to change the theme. If a default theme " +"is set, it will be used nonetheless" +msgstr "" +"اختر هذا المربع لمنع المستخدمين من تغيير السمة. إذا تم تعيين سمة افتراضية،" +" سيتم استخدامها في جميع الحالات" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:154 +msgid "" +"Check this box for persistent database connections. Persistent connections " +"are not closed at the end of a request, but are cached and re-used. This is " +"experimental" +msgstr "" +"حدد هذا المربع لاتصالات قاعدة البيانات الثابتة. لا يتم إغلاق الاتصالات " +"المستمرة في نهاية الطلب، ولكنها تخزن في ذاكرة مؤقتة ويعاد " +"استخدامها. وهذه خاصية تجريبية" + +#: /usr/share/icingaweb2/application/forms/Dashboard/DashletForm.php:101 +msgid "Check this box if you want to add the dashlet to a new dashboard" +msgstr "حدد هذا المربع إذا كنت ترغب في إضافة لوحة مصغرة للوحة المعلومات" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:222 +msgid "Check this box to enforce changes without connectivity validation" +msgstr "حدد هذا المربع لفرض تغييرات دون تحقق من صحة الاتصال" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:383 +msgid "" +"Check this box to enforce changes without validating that authentication is " +"possible." +msgstr "حدد هذا المربع لفرض تغييرات دون تحقق من إمكانية المصادقة." + +#: /usr/share/php/Icinga/Web/Widget/FilterWidget.php:68 +msgid "Click to remove this part of your filter" +msgstr "انقر لإزالة هذا الجزء من المرشح" + +#: /usr/share/php/Icinga/Repository/Repository.php:1023 +#, php-format +msgid "Column \"%s\" cannot be queried" +msgstr "العمود \"%s\" لا يمكن الاستعلام عنه" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:625 +msgid "Comma separated list of group names to share this item with" +msgstr "قائمة أسماء مجموعة مفصولة بفواصل لمشاركة هذا البند معها" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:615 +msgid "Comma separated list of usernames to share this item with" +msgstr "قائمة أسماء مستخدمين مفصولة بفواصل لمشاركة هذا البند معها" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:126 +msgid "Comma-separated list of groups that are assigned to the role" +msgstr "قائمة مجموعات مفصولة بفواصل تم إسناد هذا الدور لها" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:118 +msgid "Comma-separated list of users that are assigned to the role" +msgstr "قائمة مستخدمين مفصولة بفواصل تم إسناد هذا الدور لها" + +#: /usr/share/php/Icinga/Web/Menu.php:269 +#: /usr/share/php/Icinga/Application/Web.php:312 +msgid "Configuration" +msgstr "إعدادات" + +#: /usr/share/icingaweb2/application/controllers/UserController.php:312 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:346 +#: /usr/share/icingaweb2/application/controllers/RoleController.php:155 +msgid "" +"Configure roles to permit or restrict users and groups accessing Icinga Web 2" +msgstr "" +"إعداد الأدوار للسماح أو منع المستخدمين والمجموعات من الوصول إلى إسنجا وب 2" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:49 +msgid "Configure the user and group backends" +msgstr "إعداد خلفيات المستخدم والمجموعة" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:43 +msgid "Configure which resources are being utilized by Icinga Web 2" +msgstr "إعداد الموارد التي تستخدمها إسنجا وب 2" + +#: /usr/share/icingaweb2/application/forms/ConfirmRemovalForm.php:19 +msgid "Confirm Removal" +msgstr "تأكيد الإزالة" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LivestatusResourceForm.php:79 +msgid "" +"Connectivity validation failed, connection to the given resource not " +"possible." +msgstr "فشل التحقق من الاتصال، الاتصال بالمورد المعطى غير ممكن." + +#: /usr/share/php/Icinga/Chart/GridChart.php:90 +msgid "Contains data in a bar or line chart." +msgstr "يحتوي على البيانات في شريط أو خط رسم بياني." + +#: /usr/share/php/Icinga/Chart/PieChart.php:56 +msgid "Contains data in a pie chart." +msgstr "يحتوي على البيانات في مخطط دائري." + +#: /usr/share/icingaweb2/application/forms/Config/General/ApplicationConfigForm.php:54 +msgid "" +"Contains the directories that will be searched for available modules, " +"separated by colons. Modules that don't exist in these directories can still " +"be symlinked in the module folder, but won't show up in the list of disabled " +"modules." +msgstr "" +"يحتوي على المجلدات التي سيتم البحث فيها عن الوحدات المتاحة، " +"مفصولة بنقطتين. الوحدات التي لا وجود لها في هذه المجلدات يظل " +"بالإمكان ربطها من مجلد الوحدة، لكنها لن تظهر في قائمة الوحدات " +"المعطلة." + +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:25 +msgid "Copyright" +msgstr "حقوق النسخ" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:82 +msgid "" +"Could not find any valid user backend resources. Please configure a resource " +"for authentication first." +msgstr "" +"لا يمكن العثور على أي موارد لخلفية المستخدم صالحة. يرجى إعداد مورد " +"للمصادقة أولا." + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/error.phtml:2 +msgid "Could not persist dashboard" +msgstr "لا يمكن للوحة المعلومات أن تستمر" + +#: /usr/share/icingaweb2/application/forms/Config/User/CreateMembershipForm.php:89 +msgid "Create" +msgstr "إنشاء" + +#: /usr/share/icingaweb2/application/views/scripts/user/show.phtml:57 +msgid "Create New Membership" +msgstr "إنشاء عضوية حديدة" + +#: /usr/share/icingaweb2/application/controllers/RoleController.php:59 +msgid "Create Role" +msgstr "إنشاء دور" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/index.phtml:12 +msgid "Create a New Navigation Item" +msgstr "إنشاء عنصر تنقل جديد" + +#: /usr/share/icingaweb2/application/views/scripts/config/resource.phtml:6 +msgid "Create a New Resource" +msgstr "إنشاء مورد جديد" + +#: /usr/share/icingaweb2/application/views/scripts/role/list.phtml:6 +msgid "Create a New Role" +msgstr "إنشاء دور جديد" + +#: /usr/share/icingaweb2/application/views/scripts/config/userbackend/reorder.phtml:7 +msgid "Create a New User Backend" +msgstr "إنشاء خلفية مستخدم جديد" + +#: /usr/share/icingaweb2/application/views/scripts/config/userbackend/reorder.phtml:21 +msgid "Create a New User Group Backend" +msgstr "إنشاء خلفية مجموعة مستخدمين جديدة" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:198 +msgid "" +"Create a new backend for authenticating your users. This backend will be " +"added at the end of your authentication order." +msgstr "" +"إنشاء خلفية جديدة لمصادقة المستخدمين. ستضاف هذه الخلفية " +"في نهاية ترتيب المصادقة الخاصة بك." + +#: /usr/share/icingaweb2/application/controllers/UsergroupbackendController.php:42 +msgid "Create a new backend to associate users and groups with." +msgstr "إنشاء خلفية جديدة لربطها بالمستخدمين والمجموعات." + +#: /usr/share/icingaweb2/application/views/scripts/navigation/index.phtml:19 +msgid "Create a new navigation item" +msgstr "إنشاء عنصر تنقل جديد" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:216 +msgid "Create a new navigation item, such as a menu entry or dashlet." +msgstr "إنشاء عنصر تنقل جديد، مثل مدخل قائمة أو لوحة مصغرة." + +#: /usr/share/icingaweb2/application/views/scripts/config/resource.phtml:13 +msgid "Create a new resource" +msgstr "إنشاء مورد جديد" + +#: /usr/share/icingaweb2/application/views/scripts/role/list.phtml:13 +msgid "Create a new role" +msgstr "إنشاء دور جديد" + +#: /usr/share/icingaweb2/application/views/scripts/config/userbackend/reorder.phtml:14 +msgid "Create a new user backend" +msgstr "إنشاء خلفية مستخدم جديد" + +#: /usr/share/icingaweb2/application/views/scripts/config/userbackend/reorder.phtml:28 +msgid "Create a new user group backend" +msgstr "إنشاء خلفية مجموعة مستخدمين جديدة" + +#: /usr/share/icingaweb2/application/forms/Config/User/CreateMembershipForm.php:88 +#, php-format +msgid "Create memberships for %s" +msgstr "إنشاء عضوية لأجل %s" + +#: /usr/share/php/Icinga/Authentication/User/LdapUserBackend.php:257 +#: /usr/share/php/Icinga/Authentication/User/DbUserBackend.php:120 +#: /usr/share/php/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php:466 +#: /usr/share/php/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:122 +msgid "Created At" +msgstr "أُنشئ في" + +#: /usr/share/icingaweb2/application/views/scripts/group/show.phtml:33 +#: /usr/share/icingaweb2/application/views/scripts/user/show.phtml:36 +#: /usr/share/icingaweb2/application/controllers/UserController.php:71 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:70 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:106 +msgid "Created at" +msgstr "أنشئ في" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationItemForm.php:44 +msgid "Current Column" +msgstr "العمود الحالي" + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/index.phtml:16 +msgid "" +"Currently there is no dashlet available. Please contact the administrator." +msgstr "لا يوجد حاليا لوحة مصغرة متاحة. يُرجى الاتصال بالمسئول." + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/index.phtml:20 +#, php-format +msgid "" +"Currently there is no dashlet available. This might change once you enabled " +"some of the available %s." +msgstr "" +"لا يوجد حاليا لوحة مصغرة متاحة. يمكن لهذا أن يتغير بمجرد " +"تفعيلك لبعض الـ %s المتوفرة." + +#: /usr/share/icingaweb2/application/forms/Dashboard/DashletForm.php:120 +#: /usr/share/icingaweb2/application/controllers/DashboardController.php:257 +#: /usr/share/php/Icinga/Web/Menu.php:243 +#: /usr/share/php/Icinga/Application/Web.php:290 +msgid "Dashboard" +msgstr "لوحة المعلومات" + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/settings.phtml:5 +msgid "Dashboard Settings" +msgstr "إعدادات لوحة المعلومات" + +#: /usr/share/icingaweb2/application/controllers/DashboardController.php:219 +msgid "Dashboard has been removed" +msgstr "تم إزالة لوحة المعلومات" + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/settings.phtml:11 +msgid "Dashlet Name" +msgstr "اسم اللوحة المصغرة" + +#: /usr/share/icingaweb2/application/forms/Dashboard/DashletForm.php:81 +msgid "Dashlet Title" +msgstr "عنوان اللوحة المصغرة" + +#: /usr/share/icingaweb2/application/controllers/DashboardController.php:73 +msgid "Dashlet created" +msgstr "أنشئت لوحة مصغرة" + +#: /usr/share/icingaweb2/application/controllers/DashboardController.php:183 +msgid "Dashlet has been removed from" +msgstr "أُزيلت اللوحة المصغرة من" + +#: /usr/share/icingaweb2/application/controllers/DashboardController.php:140 +msgid "Dashlet updated" +msgstr "حُدثت اللوحة المصغرة" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:267 +#: /usr/share/icingaweb2/application/forms/Config/General/ApplicationConfigForm.php:70 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupBackendForm.php:163 +msgid "Database" +msgstr "قاعدة البيانات" + +#: /usr/share/icingaweb2/application/forms/Config/General/ApplicationConfigForm.php:90 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/DbUserGroupBackendForm.php:47 +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/DbBackendForm.php:64 +msgid "Database Connection" +msgstr "اتصال قاعدة البيانات" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:118 +msgid "Database Name" +msgstr "اسم قاعدة البيانات" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:86 +msgid "Database Type" +msgstr "نوع قاعدة البيانات" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:59 +msgctxt "app.config.logging.level" +msgid "Debug" +msgstr "فحص الأخطاء" + +#: /usr/share/icingaweb2/application/forms/Config/General/ThemingConfigForm.php:42 +msgctxt "Form element label" +msgid "Default Theme" +msgstr "السمة الافتراضية" + +#: /usr/share/icingaweb2/application/views/scripts/config/module.phtml:46 +msgid "Dependencies" +msgstr "الاعتمادات" + +#: /usr/share/icingaweb2/application/views/scripts/config/module.phtml:42 +msgid "Description" +msgstr "الوصف" + +#: /usr/share/icingaweb2/application/views/scripts/showConfiguration.phtml:19 +msgid "" +"Details can be found in the application log. (If you don't have access to " +"this log, call your administrator in this case)" +msgstr "" +"يمكن الاطلاع على التفاصيل في سجل التطبيق. (إذا لم يكن لديك " +"وصولا إلى هذا السجل، اتصل بالمسؤول في هذه الحالة)" + +#: /usr/share/icingaweb2/application/forms/AutoRefreshForm.php:65 +msgid "Disable auto refresh" +msgstr "تعطيل التحديث التلقائي" + +#: /usr/share/icingaweb2/application/views/scripts/config/module.phtml:26 +#, php-format +msgid "Disable the %s module" +msgstr "تعطيل وحدة %s" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:88 +msgctxt "A button to discover LDAP capabilities" +msgid "Discover" +msgstr "اكتشاف" + +#: /usr/share/icingaweb2/application/forms/Config/General/ApplicationConfigForm.php:71 +msgid "Don't Store Preferences" +msgstr "عدم تخزين التفضيلات" + +#: /usr/share/icingaweb2/application/controllers/DashboardController.php:143 +msgid "Edit Dashlet" +msgstr "تحرير لوحة صغيرة" + +#: /usr/share/icingaweb2/application/views/scripts/user/show.phtml:16 +msgid "Edit User" +msgstr "تحرير مستخدم" + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/settings.phtml:54 +#, php-format +msgid "Edit dashlet %s" +msgstr "تحرير لوحة صغيرة %s" + +#: /usr/share/icingaweb2/application/views/scripts/group/show.phtml:18 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:31 +#, php-format +msgid "Edit group %s" +msgstr "تحرير محموعة %s" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/index.phtml:47 +#, php-format +msgid "Edit navigation item %s" +msgstr "تحرير عنصر تنقل %s" + +#: /usr/share/icingaweb2/application/views/scripts/config/resource.phtml:56 +#, php-format +msgid "Edit resource %s" +msgstr "تحرير مورد %s" + +#: /usr/share/icingaweb2/application/views/scripts/role/list.phtml:36 +#, php-format +msgid "Edit role %s" +msgstr "تحرير دور %s" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/shared.phtml:37 +#, php-format +msgid "Edit shared navigation item %s" +msgstr "تحرير عنصر تنقل مشترك %s" + +#: /usr/share/icingaweb2/application/views/scripts/user/show.phtml:25 +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:67 +#, php-format +msgid "Edit user %s" +msgstr "تحرير مستخدم %s" + +#: /usr/share/icingaweb2/application/views/scripts/form/reorder-authbackend.phtml:25 +#, php-format +msgid "Edit user backend %s" +msgstr "تحرير خلفية مستخدم %s" + +#: /usr/share/icingaweb2/application/views/scripts/config/userbackend/reorder.phtml:51 +#, php-format +msgid "Edit user group backend %s" +msgstr "تحرير خلفية مجموعة مستخدم %s" + +#: /usr/share/icingaweb2/application/forms/AutoRefreshForm.php:63 +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:243 +msgid "Enable auto refresh" +msgstr "تفعيل التحديث التلقائي" + +#: /usr/share/icingaweb2/application/views/scripts/config/module.phtml:34 +#, php-format +msgid "Enable the %s module" +msgstr "تفعيل الوحدة %s" + +#: /usr/share/icingaweb2/application/controllers/ErrorController.php:55 +#, php-format +msgid "Enabling the \"%s\" module might help!" +msgstr "قد يساعدك تفعيل الوحدة \"%s!" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:71 +msgid "Encryption" +msgstr "التشفير" + +#: /usr/share/icingaweb2/application/forms/Dashboard/DashletForm.php:82 +msgid "Enter a title for the dashlet." +msgstr "أدخل عنوانا للوحة المصغرة." + +#: /usr/share/icingaweb2/application/forms/Dashboard/DashletForm.php:111 +msgid "Enter a title for the new dashboard" +msgstr "أدخل عنوانا للوحة المعلومات الجديدة" + +#: /usr/share/icingaweb2/application/forms/Dashboard/DashletForm.php:71 +msgid "" +"Enter url being loaded in the dashlet. You can paste the full URL, including " +"filters." +msgstr "" +"أدخل الرابط الذي تم تحميله في اللوحة المصغرة. يمكنك لصق عنوان URL الكامل، بما" +" في ذلك " +"المرشحات." + +#: /usr/share/icingaweb2/application/controllers/ErrorController.php:109 +msgid "Error" +msgstr "خطأ" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:56 +msgctxt "app.config.logging.level" +msgid "Error" +msgstr "خطأ" + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/error.phtml:11 +msgid "Error details" +msgstr "تفاصيل الخطأ" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:281 +msgid "External" +msgstr "خارجي" + +#: /usr/share/icingaweb2/application/forms/Config/User/CreateMembershipForm.php:128 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/AddMemberForm.php:163 +#, php-format +msgid "Failed to add \"%s\" as group member for \"%s\"" +msgstr "فَشِل في إضافة \"%s\" كعضو مجموعة عن \"%s\"" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:91 +msgid "Failed to add group" +msgstr "فشل في إضافة مجموعة" + +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:141 +msgid "Failed to add user" +msgstr "فشل في إضافة مستخدم" + +#: /usr/share/php/Icinga/Repository/IniRepository.php:162 +#, php-format +msgid "Failed to delete. An error occurred: %s" +msgstr "فشل في الحذف. حدث خطأ: %s" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:112 +#, php-format +msgid "Failed to discover the chosen LDAP connection: %s" +msgstr "فشل في اكتشاف اتصال LDAP المختار: %s" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:107 +#, php-format +msgid "Failed to edit group \"%s\"" +msgstr "فشل في تحرير مجموعة \"%s\"" + +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:157 +#, php-format +msgid "Failed to edit user \"%s\"" +msgstr "فشل في تحرير مستخدم \"%s\"" + +#: /usr/share/icingaweb2/application/forms/Config/User/CreateMembershipForm.php:183 +#, php-format +msgid "Failed to fetch any groups from backend %s. Please check your log" +msgstr "فشل في إحضار أي مجموعات للخلفية %s. يُرجى فحص ملف السجل الخاص بك" + +#: /usr/share/icingaweb2/application/controllers/GroupController.php:304 +#, php-format +msgid "Failed to fetch any users from backend %s. Please check your log" +msgstr "فشل في إحضار أي مستخدمين للخلفية %s. يُرجى فحص ملف السجل الخاص بك" + +#: /usr/share/icingaweb2/application/controllers/UserController.php:270 +#, php-format +msgid "Failed to fetch memberships from backend %s. Please check your log" +msgstr "فشل في إحضار أي عضوية للخلفية %s. يُرجى فحص ملف السجل الخاص بك" + +#: /usr/share/php/Icinga/Web/Navigation/Navigation.php:547 +#, php-format +msgid "" +"Failed to fully parse navigation configuration. Ensure that all referenced " +"parents are existing navigation items: %s" +msgstr "" +"فشل في إجراء تحليل كامل لإعداد التنقل. تأكد من أن جميع الآباء المشار " +"لهم هم عناصر تنقل موجودة: %s" + +#: /usr/share/php/Icinga/Repository/IniRepository.php:72 +#, php-format +msgid "Failed to insert. An error occurred: %s" +msgstr "فشل في إدراج. حدث خطأ: %s" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:123 +#, php-format +msgid "Failed to remove group \"%s\"" +msgstr "فَشِل في حَذف مجموعة \"%s\"" + +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:173 +#, php-format +msgid "Failed to remove user \"%s\"" +msgstr "فَشِل في حَذف مستخدم \"%s\"" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:317 +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:422 +#, php-format +msgid "Failed to successfully validate the configuration: %s" +msgstr "فَشِل في التحقق من الإعداد بنجاح: %s" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:386 +#, php-format +msgid "Failed to unshare navigation item \"%s\"" +msgstr "فشل في إلغاء مشاركة عنصر التنقل \"%s\"" + +#: /usr/share/php/Icinga/Repository/IniRepository.php:135 +#, php-format +msgid "Failed to update. An error occurred: %s" +msgstr "فشل في التحديث. حديث خطأ: %s" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:235 +msgid "File" +msgstr "ملف" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:41 +msgctxt "app.config.logging.type" +msgid "File" +msgstr "ملف" + +#: /usr/share/icingaweb2/application/forms/Config/General/ApplicationConfigForm.php:69 +msgid "File System (INI Files)" +msgstr "نظام الملفات (INI Files)" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:115 +msgid "File path" +msgstr "مسار الملف" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/FileResourceForm.php:41 +msgid "Filepath" +msgstr "مسار الملف" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/ExternalBackendForm.php:49 +msgid "Filter Pattern" +msgstr "نمط المرشح" + +#: /usr/share/php/Icinga/Repository/Repository.php:1067 +#, php-format +msgid "Filter column \"%s\" not found" +msgstr "لم يتم العثور على عمود المرشح \"%s\"" + +#: /usr/share/php/Icinga/Repository/Repository.php:1071 +#, php-format +msgid "Filter column \"%s\" not found in table \"%s\"" +msgstr "لم يتم العثور على عمود المرشح \"%s\" في الجدول \"%s\"" + +#: /usr/share/php/Icinga/Web/Widget/FilterWidget.php:89 +#: /usr/share/php/Icinga/Web/Widget/FilterEditor.php:745 +msgid "Filter this list" +msgstr "تصفية هذه القائمة" + +#: /usr/share/php/Icinga/Web/Widget/FilterWidget.php:93 +msgid "Filtered" +msgstr "مُرشَّح" + +#: /usr/share/php/Icinga/Web/Wizard.php:662 +msgid "Finish" +msgstr "نهاية" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:68 +msgid "For using unix domain sockets, specify localhost" +msgstr "لاستخدام مآخذ نطاق يونيكس، اختر localhost" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:64 +msgid "" +"For using unix domain sockets, specify the path to the unix domain socket " +"directory" +msgstr "لاستخدام مآخذ نطاق يونيكس، اختر مسار مجلد مأخد نطاق يونيكس" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:221 +msgid "Force Changes" +msgstr "فرض التغييرات" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:38 +msgid "General" +msgstr "عام" + +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:18 +msgid "Git commit" +msgstr "Git commit" + +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:22 +msgid "Git commit date" +msgstr "تاريخ Git commit" + +#: /usr/share/php/Icinga/Chart/GridChart.php:89 +msgid "Grid Chart" +msgstr "مخطط الشبكة" + +#: /usr/share/icingaweb2/application/views/scripts/user/show.phtml:78 +#: /usr/share/icingaweb2/application/controllers/UserController.php:109 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:326 +msgid "Group" +msgstr "مجموعة" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:105 +#, php-format +msgid "Group \"%s\" has been edited" +msgstr "تم تحرير المجموعة \"%s\"" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:121 +#, php-format +msgid "Group \"%s\" has been removed" +msgstr "تمت إزالة المجموعة \"%s\"" + +#: /usr/share/icingaweb2/application/controllers/GroupController.php:92 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:180 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:202 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:229 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:284 +#, php-format +msgid "Group \"%s\" not found" +msgstr "عُثر على المجموعة \"%s\"" + +#: /usr/share/icingaweb2/application/views/scripts/user/show.phtml:45 +msgid "Group Memberships" +msgstr "عضوية المجموعة" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:23 +msgid "Group Name" +msgstr "اسم المجموعة" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:89 +msgid "Group added successfully" +msgstr "تم إضافة المجموعة بنجاح" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/AddMemberForm.php:175 +#, php-format +msgid "Group member \"%s\" added successfully" +msgstr "تم إضافة عضو المجموعة \"%s\" بنجاح" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/AddMemberForm.php:177 +msgid "Group members added successfully" +msgstr "تم إضافة أعضاء للمجموعة بنجاح" + +#: /usr/share/icingaweb2/application/views/scripts/role/list.phtml:24 +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:623 +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:125 +#: /usr/share/icingaweb2/application/forms/Config/User/CreateMembershipForm.php:79 +msgid "Groups" +msgstr "مجموعات" + +#: /usr/share/icingaweb2/application/views/scripts/search/hint.phtml:3 +msgid "Hint" +msgstr "تلميح" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:96 +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:47 +msgid "Host" +msgstr "مضيف" + +#: /usr/share/icingaweb2/application/views/scripts/pivottablePagination.phtml:28 +msgid "Hosts" +msgstr "مضيفون" + +#: /usr/share/icingaweb2/application/views/scripts/search/hint.phtml:2 +msgid "I'm ready to search, waiting for your input" +msgstr "أنا مستعد للبحث، في انتظار مدخلاتكم" + +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:106 +msgid "Icinga Documentation" +msgstr "مستندات مساعدة إسنجا" + +#: /usr/share/icingaweb2/application/views/scripts/authentication/login.phtml:11 +msgid "Icinga Web 2 Documentation" +msgstr "مستندات مساعدة إسنجا وب 2" + +#: /usr/share/icingaweb2/application/controllers/AuthenticationController.php:53 +msgid "Icinga Web 2 Login" +msgstr "دخول إسنجا وب 2" + +#: /usr/share/icingaweb2/application/views/scripts/authentication/login.phtml:12 +msgid "Icinga Web 2 Setup-Wizard" +msgstr "معالج تثبيت إسنجا وب 2" + +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:96 +msgid "Icinga Wiki" +msgstr "ويكي إسنجا" + +#: /usr/share/icingaweb2/application/views/scripts/authentication/login.phtml:37 +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:55 +msgid "Icinga on Facebook" +msgstr "إسنجا على فيسبوك" + +#: /usr/share/icingaweb2/application/views/scripts/authentication/login.phtml:46 +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:64 +msgid "Icinga on Google+" +msgstr "إسنجا على قوقل+" + +#: /usr/share/icingaweb2/application/views/scripts/authentication/login.phtml:27 +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:46 +msgid "Icinga on Twitter" +msgstr "إسنجا على تويتر" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationItemForm.php:68 +msgid "Icon" +msgstr "أيقونة" + +#: /usr/share/icingaweb2/application/views/scripts/authentication/logout.phtml:15 +msgid "" +"If this message does not disappear, it might be necessary to quit the " +"current session manually by clearing the cache, or by closing the current " +"browser session." +msgstr "" +"إذا لم تختفي هذه الرسالة، فقد يكون من اللازم إنهاء " +"الجلسة الحالية يدويا بتنظيف الذاكرة المخبأة أو بإغلاق " +"جلسة المتصفح الحالية." + +#: /usr/share/icingaweb2/application/views/scripts/showConfiguration.phtml:21 +msgid "" +"In case you can access the file by yourself, you can open it and insert the " +"config manually:" +msgstr "" +"في حال كان بإمكانك الوصول إلى الملف بنفسك، يمكنك فتحه وإدراج " +"الإعداد يدويا:" + +#: /usr/share/icingaweb2/application/views/scripts/user/show.phtml:33 +msgid "Inactive" +msgstr "غير نشط" + +#: /usr/share/icingaweb2/application/forms/Authentication/LoginForm.php:117 +msgid "Incorrect username or password" +msgstr "اسم المستخدم أو كلمة المرور غير صحيح" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:58 +msgctxt "app.config.logging.level" +msgid "Information" +msgstr "معلومات" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:119 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupBackendForm.php:52 +#, php-format +msgid "Invalid backend type \"%s\" provided" +msgstr "نوع الخلفية المعطى \"%s\" غير صالح" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:54 +#, php-format +msgid "Invalid resource type \"%s\" provided" +msgstr "نوع المورد المعطى \"%s\" غير صالح" + +#: /usr/share/icingaweb2/application/views/scripts/authentication/login.phtml:7 +#, php-format +msgid "" +"It appears that you did not configure Icinga Web 2 yet so it's not possible " +"to log in without any defined authentication method. Please define a " +"authentication method by following the instructions in the %1$sdocumentation" +"%3$s or by using our %2$sweb-based setup-wizard%3$s." +msgstr "" +"يبدو أنك لم تقوم بإعداد إسنجا الويب 2 بعد لذلك فأنه من غير الممكن " +"الدخول دون أي أسلوب مصادقة معرف. يرجى تعريف أسلوب " +"مصادقة من خلال اتباع التعليمات في %1$sdocumentation " +"%3$s أو عن طريق استخدام معالج التثبيت%3$s المبني على الوب الخاص بنا%2$" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:210 +msgid "LDAP Base DN" +msgstr "اسم LDAP المميز الأساسي " + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:53 +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:68 +msgid "LDAP Connection" +msgstr "اتصال LDAP" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:151 +msgid "LDAP Filter" +msgstr "مرشح LDAP" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:190 +msgid "LDAP Group Base DN" +msgstr "اسم مجموعة LDAP المميز الأساسي" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:133 +msgid "LDAP Group Filter" +msgstr "مرشح مجموعة LDAP" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:180 +msgid "LDAP Group Member Attribute" +msgstr "سمة عضو مجموعة LDAP" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:166 +msgid "LDAP Group Name Attribute" +msgstr "سمة اسم مجموعة LDAP" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:122 +msgid "LDAP Group Object Class" +msgstr "فئة كائن مجموعة LDAP" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:271 +msgid "LDAP User Base DN" +msgstr "اسم مستخدم LDAP المميز الأساسي " + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:226 +msgid "LDAP User Filter" +msgstr "مرشح مستخدم LDAP" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:259 +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:189 +msgid "LDAP User Name Attribute" +msgstr "سمة اسم مستخدم LDAP" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:215 +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:139 +msgid "LDAP User Object Class" +msgstr "فئة كائن مستخدم LDAP" + +#: /usr/share/php/Icinga/Authentication/User/LdapUserBackend.php:258 +#: /usr/share/php/Icinga/Authentication/User/DbUserBackend.php:121 +#: /usr/share/php/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php:467 +#: /usr/share/php/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:123 +msgid "Last Modified" +msgstr "آخر تعديل" + +#: /usr/share/icingaweb2/application/views/scripts/group/show.phtml:37 +#: /usr/share/icingaweb2/application/views/scripts/user/show.phtml:40 +#: /usr/share/icingaweb2/application/controllers/UserController.php:72 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:71 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:107 +msgid "Last modified" +msgstr "آخر تعديل" + +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:62 +msgid "Leave empty for not updating the user's password" +msgstr "دعها فارغة لعدم تحديث كلمة مرور المستخدم" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:192 +msgid "List and configure shared navigation items" +msgstr "سرد وإعداد عناصر التنقل المشتركة" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:142 +#: /usr/share/icingaweb2/application/controllers/PreferenceController.php:37 +msgid "List and configure your own navigation items" +msgstr "سرد وإعداد عناصر التنقل الخاصة بك" + +#: /usr/share/icingaweb2/application/controllers/UserController.php:329 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:363 +#: /usr/share/icingaweb2/application/controllers/RoleController.php:172 +msgid "List groups of user group backends" +msgstr "سرد مجموعات لخلفيات مجموعة المستخدمين" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:97 +msgid "List intalled modules" +msgstr "سرد الوحدات المثبتة" + +#: /usr/share/icingaweb2/application/controllers/UserController.php:321 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:355 +#: /usr/share/icingaweb2/application/controllers/RoleController.php:164 +msgid "List users of authentication backends" +msgstr "سرد المستخدمين لخلفيات المصادقة" + +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:110 +msgid "Loaded modules" +msgstr "الوحدات المحملة" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:53 +msgid "Logging Level" +msgstr "مستوى التسجيل" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:37 +msgid "Logging Type" +msgstr "نوع التسجيل" + +#: /usr/share/icingaweb2/application/forms/Authentication/LoginForm.php:30 +msgid "Logging in" +msgstr "الدخول" + +#: /usr/share/icingaweb2/application/views/scripts/authentication/logout.phtml:12 +msgid "Logging out..." +msgstr "تسخيل الخروج..." + +#: /usr/share/icingaweb2/application/views/scripts/authentication/logout.phtml:22 +#: /usr/share/icingaweb2/application/forms/Authentication/LoginForm.php:29 +msgid "Login" +msgstr "دخول" + +#: /usr/share/php/Icinga/Web/Menu.php:314 +#: /usr/share/php/Icinga/Application/Web.php:355 +msgid "Logout" +msgstr "خروج" + +#: /usr/share/icingaweb2/application/views/scripts/authentication/logout.phtml:44 +msgid "Logout successful!" +msgstr "خروج ناجح" + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/error.phtml:7 +msgid "Make sure that the webserver can write to this file." +msgstr "تأكد من أن خادم الوب يمكنه الكتابة على هذا الملف." + +#: /usr/share/icingaweb2/application/views/scripts/group/show.phtml:42 +msgid "Members" +msgstr "أعضاء" + +#: /usr/share/icingaweb2/application/forms/Config/User/CreateMembershipForm.php:141 +#, php-format +msgid "Membership for group %s created successfully" +msgstr "عضوية المجموعة %s أنشئت بنجاح" + +#: /usr/share/icingaweb2/application/forms/Config/User/CreateMembershipForm.php:144 +msgid "Memberships created successfully" +msgstr "أُنشئت العضوية بنجاح" + +#: /usr/share/php/Icinga/Web/Navigation/Navigation.php:465 +msgid "Menu Entry" +msgstr "مدخل قائمة" + +#: /usr/share/php/Icinga/Web/Controller/ActionController.php:249 +msgid "Method Not Allowed" +msgstr "الطّريقة غير مسموح بها" + +#: /usr/share/php/Icinga/Web/Widget/FilterWidget.php:94 +#: /usr/share/php/Icinga/Web/Widget/FilterEditor.php:747 +msgid "Modify this filter" +msgstr "تعديل هذا المرشح" + +#: /usr/share/icingaweb2/application/views/scripts/config/modules.phtml:11 +msgid "Module" +msgstr "وحدة" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:161 +#, php-format +msgid "Module \"%s\" disabled" +msgstr "عُطّلت الوحدة \"%s\"" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:141 +#, php-format +msgid "Module \"%s\" enabled" +msgstr "فُعّلت الوحدة \"%s\"" + +#: /usr/share/icingaweb2/application/views/scripts/config/modules.phtml:23 +#, php-format +msgid "Module %s has failed to load" +msgstr "فشل تحميل الوحدة %s" + +#: /usr/share/icingaweb2/application/views/scripts/config/modules.phtml:21 +#, php-format +msgid "Module %s is disabled" +msgstr "عُطّلت الوحدة %s" + +#: /usr/share/icingaweb2/application/views/scripts/config/modules.phtml:19 +#, php-format +msgid "Module %s is enabled" +msgstr "فُعّلت الوحدة %s" + +#: /usr/share/icingaweb2/application/forms/Config/General/ApplicationConfigForm.php:50 +msgid "Module Path" +msgstr "مسار الوحدة" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:96 +#: /usr/share/php/Icinga/Web/Menu.php:299 +#: /usr/share/php/Icinga/Application/Web.php:336 +msgid "Modules" +msgstr "الوحدات" + +#: /usr/share/icingaweb2/application/views/scripts/form/reorder-authbackend.phtml:58 +msgid "Move down in authentication order" +msgstr "تحرك للأسفل في ترتيب المصادقة" + +#: /usr/share/icingaweb2/application/views/scripts/form/reorder-authbackend.phtml:46 +msgid "Move up in authentication order" +msgstr "تحرك للأعلى في ترتيب المصادقة" + +#: /usr/share/icingaweb2/application/views/scripts/form/reorder-authbackend.phtml:60 +#, php-format +msgid "Move user backend %s downwards" +msgstr "تحريك خلفية المستخدم %s للأسفل" + +#: /usr/share/icingaweb2/application/views/scripts/form/reorder-authbackend.phtml:48 +#, php-format +msgid "Move user backend %s upwards" +msgstr "تحريك خلفية المستخدم %s للأعلى" + +#: /usr/share/icingaweb2/application/views/scripts/config/module.phtml:16 +#: /usr/share/icingaweb2/application/views/scripts/role/list.phtml:22 +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:114 +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:582 +msgid "Name" +msgstr "الاسم" + +#: /usr/share/icingaweb2/application/views/scripts/pivottablePagination.phtml:16 +#: /usr/share/icingaweb2/application/views/scripts/navigation/index.phtml:29 +#: /usr/share/icingaweb2/application/views/scripts/layout/menu.phtml:16 +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:143 +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:151 +#: /usr/share/icingaweb2/application/controllers/PreferenceController.php:38 +msgid "Navigation" +msgstr "التنقل" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:327 +#, php-format +msgid "Navigation Item \"%s\" not found. No action required" +msgstr "لم يُعثر على عنصر التنقل \"%s\". ليس مطلوبا أي إجراء" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:339 +#, php-format +msgid "Navigation Item \"%s\" successfully removed" +msgstr "أُزيل عنصر التنقل \"%s\" بنجاح" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:379 +#, php-format +msgid "Navigation item \"%s\" has been unshared" +msgstr "إزيلت مشاركة عنصر التنقل \"%s\"" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:301 +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:412 +#, php-format +msgid "Navigation item \"%s\" not found" +msgstr "لم يُعثر على عنصر التنقل \"%s\"" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:290 +#, php-format +msgid "Navigation item \"%s\" successfully updated" +msgstr "تم تحديث عنصر التنقل \"%s\" بنجاح" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:236 +msgid "Navigation item successfully created" +msgstr "أُنشئ عنصر التنقل بنجاح" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationItemForm.php:42 +msgid "New Column" +msgstr "عمود جديد" + +#: /usr/share/icingaweb2/application/forms/Dashboard/DashletForm.php:110 +msgid "New Dashboard Title" +msgstr "عنوان لوحة معلومات جديد" + +#: /usr/share/icingaweb2/application/controllers/DashboardController.php:42 +msgid "New Dashlet" +msgstr "لوحة مصغرة جديدة" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:244 +msgid "New Navigation Item" +msgstr "عنصر تنقل جديد" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:326 +msgid "New Resource" +msgstr "مورد جديد" + +#: /usr/share/icingaweb2/application/controllers/RoleController.php:63 +msgid "New Role" +msgstr "دور جديد" + +#: /usr/share/icingaweb2/application/controllers/UserController.php:166 +msgid "New User" +msgstr "مستخدم جديد" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:231 +msgid "New User Backend" +msgstr "خلفية مستخدم جديد" + +#: /usr/share/icingaweb2/application/controllers/GroupController.php:159 +msgid "New User Group" +msgstr "مجموعة مستخدم جديدة" + +#: /usr/share/icingaweb2/application/controllers/UsergroupbackendController.php:61 +msgid "New User Group Backend" +msgstr "خلفية مجموعة مستخدم جديدة" + +#: /usr/share/icingaweb2/application/controllers/GroupController.php:232 +msgid "New User Group Member" +msgstr "عضو مجموعة مستخدم جديد" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationItemForm.php:41 +msgid "New Window" +msgstr "نافذة جديدة" + +#: /usr/share/icingaweb2/application/forms/ConfigForm.php:74 +msgid "New configuration has successfully been stored" +msgstr "تم تخزين إعداد جديد بنجاح" + +#: /usr/share/icingaweb2/application/forms/Dashboard/DashletForm.php:100 +msgid "New dashboard" +msgstr "لوحة معلومات جديدة" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:98 +msgid "New resource name missing" +msgstr "اسم مورد جديد مفقود" + +#: /usr/share/php/Icinga/Web/Wizard.php:614 +#: /usr/share/php/Icinga/Web/Wizard.php:638 +msgid "Next" +msgstr "التالي" + +#: /usr/share/icingaweb2/application/views/scripts/mixedPagination.phtml:77 +msgid "Next page" +msgstr "الصفحة التالية" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/index.phtml:57 +msgid "No" +msgstr "لا" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:297 +msgid "No LDAP resources available. Please configure an LDAP resource first." +msgstr "لا يوجد موارد LDAP متوفرة. يُرجى إعداد مورد LDAP أولا." + +#: /usr/share/icingaweb2/application/forms/Authentication/LoginForm.php:99 +msgid "" +"No authentication methods available. Did you create authentication.ini when " +"setting up Icinga Web 2?" +msgstr "" +"لا يُوجد طرق مصادقة متوفرة. هل قمت بإنشاء authentication.ini " +"عند إعداد إسنجا وب 2؟" + +#: /usr/share/icingaweb2/application/views/scripts/group/list.phtml:22 +msgid "No backend found which is able to list user groups" +msgstr "لم يُعثر على خلفية قادرة على سرد مجموعات المستخدم" + +#: /usr/share/icingaweb2/application/views/scripts/user/list.phtml:22 +msgid "No backend found which is able to list users" +msgstr "لم يُعثر على خلفية قادرة على سرد المستخدمين" + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/settings.phtml:42 +msgid "No dashlets added to dashboard" +msgstr "لم تُضاف لوحات صغيرة للوحة المعلومات" + +#: /usr/share/icingaweb2/application/views/scripts/group/show.phtml:68 +msgid "No group member found matching the filter" +msgstr "لم يُعثر على عضو مجموعة يوافق المرشح" + +#: /usr/share/icingaweb2/application/views/scripts/user/show.phtml:71 +msgid "No memberships found matching the filter" +msgstr "لم يُعثر على عضوية توافق المرشح" + +#: /usr/share/icingaweb2/application/views/scripts/role/list.phtml:17 +msgid "No roles found." +msgstr "لم يُعثر على أدوار." + +#: /usr/share/icingaweb2/application/views/scripts/group/list.phtml:44 +msgid "No user groups found matching the filter" +msgstr "لم يُعثر على مجموعة مستخدمين توافق المرشح" + +#: /usr/share/icingaweb2/application/views/scripts/user/list.phtml:44 +msgid "No users found matching the filter" +msgstr "لم يُعثر على مستخدمين يوافقون المرشح" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:674 +msgctxt "No parent for a navigation item" +msgid "None" +msgstr "بلا" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:42 +msgctxt "app.config.logging.type" +msgid "None" +msgstr "بلا" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:77 +msgctxt "resource.ldap.encryption" +msgid "None" +msgstr "بلا" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:67 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:69 +msgctxt "usergroupbackend.ldap.user_backend" +msgid "None" +msgstr "بلا" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:63 +msgid "" +"Note that all users that are currently a member of this group will have " +"their membership cleared automatically." +msgstr "" +"لاحظ أن جميع المستخدمين الذين هم حاليا أعضاء في هذه المجموعة ستحذف " +"عضويتهم تلقائيا." + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:96 +msgid "Old resource name missing" +msgstr "اسم مورد قديم مفقود" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:91 +msgid "Only the root and its child nodes will be accessible on this resource." +msgstr "فقط الجذر والعقد التابعة له سوف تكون متاحة في هذا المورد." + +#: /usr/share/icingaweb2/application/views/scripts/navigation/shared.phtml:21 +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:200 +msgid "Owner" +msgstr "المالك" + +#: /usr/share/php/Icinga/Web/Widget/SearchDashboard.php:91 +msgid "Page not found" +msgstr "الصفحة غير موجودة" + +#: /usr/share/icingaweb2/application/controllers/ErrorController.php:51 +msgid "Page not found." +msgstr "الصفحة غير موجودة." + +#: /usr/share/icingaweb2/application/views/scripts/mixedPagination.phtml:7 +msgid "Pagination" +msgstr "تعدد الصفحات" + +#: /usr/share/icingaweb2/application/forms/Navigation/DashletForm.php:18 +msgid "Pane" +msgstr "جزء" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:668 +#: /usr/share/php/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:121 +msgid "Parent" +msgstr "أب" + +#: /usr/share/icingaweb2/application/forms/Authentication/LoginForm.php:52 +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:41 +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:63 +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:137 +msgid "Password" +msgstr "كلمة مرور" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/FileResourceForm.php:58 +msgid "Pattern" +msgstr "نمط" + +#: /usr/share/icingaweb2/application/views/scripts/config/module.phtml:58 +msgid "Permissions" +msgstr "أذونات" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:133 +msgid "Permissions Set" +msgstr "طقم الأذونات" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:157 +msgid "Persistent" +msgstr "ثابت" + +#: /usr/share/php/Icinga/Chart/PieChart.php:55 +msgid "Pie Chart" +msgstr "مخطط دائري" + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/error.phtml:4 +msgid "Please copy the following dashboard snippet to " +msgstr "يرجى نسخ مقتطف لوحة المعلومات التالية ل" + +#: /usr/share/icingaweb2/application/forms/Authentication/LoginForm.php:112 +msgid "" +"Please note that not all authentication methods were available. Check the " +"system log or Icinga Web 2 log for more information." +msgstr "" +"يرجى ملاحظة أن ليس كل أساليب المصادقة متاحة. تحقق من سجل " +"النظام أو سجل إسنجا الويب 2 لمزيد من المعلومات." + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/AddMemberForm.php:143 +msgid "" +"Please provide at least one username, either by choosing one in the list or " +"by manually typing one in the text box below" +msgstr "" +"يرجى تقديم اسم مستخدم واحد على الأقل، إما عن طريق اختيار واحد من القائمة أو " +"عن طريق كتابة واحد يدويا في مربع النص أدناه" + +#: /usr/share/icingaweb2/application/views/scripts/search/hint.phtml:4 +msgid "" +"Please use the asterisk (*) as a placeholder for wildcard searches. For " +"convenience I'll always add a wildcard in front and after your search string." +msgstr "" +"الرجاء استخدام علامة النجمة (*) كعنصر نائب لعمليات بحث أحرف البدل. تسهيلا " +"للعمل سأقوم دائما بإضافة أحرف البدل في المقدمة وبعد سلسلة البحث الخاصة بك." + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:107 +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:60 +msgid "Port" +msgstr "منفذ" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:134 +#: /usr/share/icingaweb2/application/controllers/PreferenceController.php:33 +#: /usr/share/php/Icinga/Web/Menu.php:309 +#: /usr/share/php/Icinga/Application/Web.php:350 +msgid "Preferences" +msgstr "تفضيلات" + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:123 +msgid "Preferences successfully saved" +msgstr "حُفظت الإعدادات بنجاح" + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:125 +msgid "Preferences successfully saved for the current session" +msgstr "حُفظت الإعدادات بنجاح للجلسة الحالية" + +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:25 +msgid "Prevents the user from logging in if unchecked" +msgstr "منع المستخدم من تسجيل الدخول إذا لم يلغ الاختيار" + +#: /usr/share/icingaweb2/application/views/scripts/mixedPagination.phtml:27 +msgid "Previous page" +msgstr "الصفحة السابقة" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/SshResourceForm.php:71 +#: /usr/share/icingaweb2/application/forms/Config/Resource/SshResourceForm.php:84 +msgid "Private Key" +msgstr "المفتاح الخاص" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/AddMemberForm.php:120 +msgid "Provide one or more usernames separated by comma to add as group member" +msgstr "تقديم اسم مستخدم واحد أو أكثر مفصولة بفواصل للإضافة كعضو مجموعة" + +#: /usr/share/php/Icinga/Web/Form/Decorator/Autosubmit.php:120 +msgid "" +"Push this button to update the form to reflect the change that was made in " +"the field on the left" +msgstr "" +"ضغط هذا الزر لتحديث النموذج لعكس التغيير الذي تم إجراؤه في " +"الحقل على اليسار" + +#: /usr/share/php/Icinga/Web/Form/Decorator/Autosubmit.php:119 +msgid "" +"Push this button to update the form to reflect the changes that were made " +"below" +msgstr "ضغط هذا الزر لتحديث النموذج لعكس التغييرات التي تم إجراؤها أدناه" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:90 +msgid "Push to fill in the chosen connection's default settings." +msgstr "الضغط لتعبئة الإعدادات الافتراضية للاتصال الذي تم اختياره." + +#: /usr/share/php/Icinga/Repository/Repository.php:1019 +#, php-format +msgid "Query column \"%s\" not found" +msgstr "لم يتم العثور على عمود الاستعلام \"%s\"" + +#: /usr/share/php/Icinga/Repository/Repository.php:1027 +#, php-format +msgid "Query column \"%s\" not found in table \"%s\"" +msgstr "لم يتم العثور على عمود الاستعلام \"%s\" في الجدول \"%s\"" + +#: /usr/share/php/Icinga/Web/Widget/SearchDashboard.php:61 +msgid "Ready to search" +msgstr "مستعد للبحث" + +#: /usr/share/icingaweb2/application/views/scripts/group/show.phtml:77 +#: /usr/share/icingaweb2/application/views/scripts/group/list.phtml:52 +#: /usr/share/icingaweb2/application/views/scripts/user/list.phtml:53 +msgid "Remove" +msgstr "إزالة" + +#: /usr/share/icingaweb2/application/controllers/DashboardController.php:228 +msgid "Remove Dashboard" +msgstr "إزالة لوحة المعلومات" + +#: /usr/share/icingaweb2/application/controllers/DashboardController.php:158 +msgid "Remove Dashlet" +msgstr "إزالة لوحة مصغرة" + +#: /usr/share/icingaweb2/application/controllers/DashboardController.php:192 +msgid "Remove Dashlet From Dashboard" +msgstr "إزالة لوحة مصغرة من لوحة المعلومات" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:347 +msgid "Remove Navigation Item" +msgstr "إزالة عنصر تنقل" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:365 +msgid "Remove Resource" +msgstr "إزالة مورد" + +#: /usr/share/icingaweb2/application/controllers/RoleController.php:137 +#: /usr/share/icingaweb2/application/controllers/RoleController.php:140 +msgid "Remove Role" +msgstr "إزالة دور" + +#: /usr/share/icingaweb2/application/controllers/UserController.php:210 +msgid "Remove User" +msgstr "إزالة مستخدم" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:306 +msgid "Remove User Backend" +msgstr "إزالة خلفية مستخدم" + +#: /usr/share/icingaweb2/application/controllers/GroupController.php:205 +msgid "Remove User Group" +msgstr "إزالة مجموعة مستخدم" + +#: /usr/share/icingaweb2/application/controllers/UsergroupbackendController.php:133 +msgid "Remove User Group Backend" +msgstr "إزالة خلفية مجموعة مستخدم" + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/settings.phtml:72 +#, php-format +msgid "Remove dashlet %s from pane %s" +msgstr "إزالة لوحة مصغرة %s من الجزء %s" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:61 +#, php-format +msgid "Remove group %s?" +msgstr "إزالة المجموعة %s؟" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/index.phtml:70 +#, php-format +msgid "Remove navigation item %s" +msgstr "إزالة عنصر التنقل %s" + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/settings.phtml:33 +#, php-format +msgid "Remove pane %s" +msgstr "إزالة الجزء %s" + +#: /usr/share/icingaweb2/application/views/scripts/config/resource.phtml:68 +#, php-format +msgid "Remove resource %s" +msgstr "إزالة المورد %s" + +#: /usr/share/icingaweb2/application/views/scripts/role/list.phtml:49 +#, php-format +msgid "Remove role %s" +msgstr "إزالة الدور %s" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/SshResourceForm.php:89 +#, php-format +msgid "Remove the %s resource" +msgstr "إزالة المورد %s" + +#: /usr/share/php/Icinga/Web/Widget/FilterWidget.php:98 +msgid "Remove this filter" +msgstr "إزالة هذا المرشح" + +#: /usr/share/icingaweb2/application/controllers/GroupController.php:141 +msgid "Remove this member" +msgstr "إزالة هذا العضو" + +#: /usr/share/php/Icinga/Web/Widget/FilterEditor.php:397 +msgid "Remove this part of your filter" +msgstr "إزالة هذا الجزء من مرشحك" + +#: /usr/share/icingaweb2/application/views/scripts/user/list.phtml:82 +#, php-format +msgid "Remove user %s" +msgstr "إزالة مستخدم %s" + +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:115 +#, php-format +msgid "Remove user %s?" +msgstr "إزالة مستخدم %s؟" + +#: /usr/share/icingaweb2/application/views/scripts/form/reorder-authbackend.phtml:37 +#, php-format +msgid "Remove user backend %s" +msgstr "إزالة خلفية مستخدم %s" + +#: /usr/share/icingaweb2/application/views/scripts/group/list.phtml:86 +#, php-format +msgid "Remove user group %s" +msgstr "إزالة مجموعة مستخدم %s" + +#: /usr/share/icingaweb2/application/views/scripts/config/userbackend/reorder.phtml:63 +#, php-format +msgid "Remove user group backend %s" +msgstr "إزالة خلفية مجموعة مستخدم %s" + +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:76 +msgid "Report a bug" +msgstr "إبلاغ عن مشكلة" + +#: /usr/share/php/Icinga/Web/UrlParams.php:65 +#: /usr/share/php/Icinga/Web/UrlParams.php:160 +#: /usr/share/php/Icinga/Cli/Params.php:183 +#: /usr/share/php/Icinga/Cli/Params.php:285 +#, php-format +msgid "Required parameter '%s' missing" +msgstr "المعامل المطلوب '%s' مفقود" + +#: /usr/share/icingaweb2/application/views/scripts/config/resource.phtml:19 +msgid "Resource" +msgstr "مَوْرد" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:170 +#, php-format +msgid "Resource \"%s\" has been successfully changed" +msgstr "تم تغيير المورد \"%s\" بنجاح" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:167 +#, php-format +msgid "Resource \"%s\" has been successfully created" +msgstr "أُنشئ المورد \"%s\" بنجاح" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:382 +#, php-format +msgid "Resource \"%s\" has been successfully removed" +msgstr "أُزيل المورد \"%s\" بنجاح" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:76 +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:38 +#: /usr/share/icingaweb2/application/forms/Config/Resource/LivestatusResourceForm.php:35 +#: /usr/share/icingaweb2/application/forms/Config/Resource/FileResourceForm.php:32 +#: /usr/share/icingaweb2/application/forms/Config/Resource/SshResourceForm.php:36 +msgid "Resource Name" +msgstr "اسم المورد" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:252 +msgid "Resource Type" +msgstr "نوع المورد" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:75 +msgid "Resource already exists" +msgstr "المورد موجود مسبقا" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:73 +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:122 +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:196 +msgid "Resource name missing" +msgstr "اسم المورد مفقود" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:44 +msgid "Resources" +msgstr "الموارد" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:330 +msgid "Resources are entities that provide data to Icinga Web 2." +msgstr "الموارد هي كيانات توفر البيانات لإسنجا وب 2." + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:59 +msgid "Restrict which groups this role can share items and information with" +msgstr "تقييد المجموعات التي يمكن لهذا الدور أن يشارك معها العناصر والمعلومات" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:53 +msgid "Restrict which users this role can share items and information with" +msgstr "" +"تقييد المستخدمين الذين يمكن لهذا الدور أن يشارك معهم العناصر والمعلومات" + +#: /usr/share/icingaweb2/application/views/scripts/config/module.phtml:68 +msgid "Restrictions" +msgstr "تقييدات" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:108 +msgid "Role Name" +msgstr "اسم الدور" + +#: /usr/share/icingaweb2/application/controllers/RoleController.php:52 +msgid "Role created" +msgstr "أُنشئ الدور" + +#: /usr/share/icingaweb2/application/controllers/RoleController.php:130 +msgid "Role removed" +msgstr "أُزيل الدور" + +#: /usr/share/icingaweb2/application/controllers/RoleController.php:96 +msgid "Role updated" +msgstr "حُدّث الدور" + +#: /usr/share/icingaweb2/application/controllers/UserController.php:310 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:344 +#: /usr/share/icingaweb2/application/controllers/RoleController.php:153 +#: /usr/share/php/Icinga/Web/Menu.php:284 +msgid "Roles" +msgstr "أدوار" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:89 +msgid "Root DN" +msgstr "الاسم المميز الأصل" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:243 +msgid "SQL Database" +msgstr "قاعدة بيانات" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:237 +msgid "SSH Identity" +msgstr "هوية SSH" + +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:68 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:32 +msgid "Save" +msgstr "حفظ" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:85 +#: /usr/share/icingaweb2/application/forms/Config/GeneralConfigForm.php:22 +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:30 +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:46 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupBackendForm.php:29 +msgid "Save Changes" +msgstr "حفظ التغييرات" + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:266 +msgid "Save for the current Session" +msgstr "حفظ للجلسة الحالية" + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:255 +msgid "Save to the Preferences" +msgstr "حفظ للتفضيلات" + +#: /usr/share/icingaweb2/application/views/scripts/showConfiguration.phtml:2 +msgid "Saving Configuration Failed" +msgstr "فشل حفظ الإعدادات" + +#: /usr/share/icingaweb2/application/views/scripts/layout/menu.phtml:11 +#: /usr/share/php/Icinga/Web/Widget/SearchDashboard.php:33 +#: /usr/share/php/Icinga/Web/Widget/SearchDashboard.php:50 +msgid "Search" +msgstr "بحث" + +#: /usr/share/icingaweb2/application/forms/LdapDiscoveryForm.php:27 +msgid "Search Domain" +msgstr "بحث النطاق" + +#: /usr/share/icingaweb2/application/forms/LdapDiscoveryForm.php:28 +msgid "Search this domain for records of available servers." +msgstr "بحث هذا النطاق عن سجلات الخوادم الموجودة." + +#: /usr/share/php/Icinga/Web/Widget/FilterEditor.php:741 +msgid "Search..." +msgstr "بحث..." + +#: /usr/share/php/Icinga/Web/Widget/SearchDashboard.php:75 +msgid "Searching" +msgstr "جار البحث" + +#: /usr/share/icingaweb2/application/forms/Dashboard/DashletForm.php:122 +msgid "Select a dashboard you want to add the dashlet to" +msgstr "اختر لوحة المعلومات التي تريد إضافة اللوحة الصغيرة لها" + +#: /usr/share/icingaweb2/application/forms/Config/User/CreateMembershipForm.php:81 +#, php-format +msgid "Select one or more groups where to add %s as member" +msgstr "اختر مجموعة أو أكثر حيث تضاف %s كعضو" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/AddMemberForm.php:106 +msgid "" +"Select one or more users (fetched from your user backends) to add as group " +"member" +msgstr "" +"اختيار مستخدم واحد أو أكثر (جلبت من خلفيات المستخدم الخاص بك) لإضافته كعضو في " +"المجموعة" + +#: /usr/share/icingaweb2/application/views/scripts/pivottablePagination.phtml:35 +msgid "Services" +msgstr "خدمات" + +#: /usr/share/icingaweb2/application/forms/Config/General/ApplicationConfigForm.php:40 +msgid "" +"Set whether to show an exception's stacktrace by default. This can also be " +"set in a user's preferences with the appropriate permission." +msgstr "" +"اختيار هل يظهر تتبع مكدس الاستثناء بشكل افتراضي. يمكن أيضا " +"تعيينه في تفضيلات المستخدم مع الإذن المناسب." + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:224 +msgid "Set whether to show an exception's stacktrace." +msgstr "اختيار هل يظهر تتبع مكدس الاستثناء." + +#: /usr/share/php/Icinga/Web/Widget/Tabextension/DashboardSettings.php:34 +msgid "Settings" +msgstr "إعدادات" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/index.phtml:31 +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:602 +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:150 +msgid "Shared" +msgstr "مشترك" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/shared.phtml:19 +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:193 +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:201 +#: /usr/share/php/Icinga/Application/Web.php:330 +msgid "Shared Navigation" +msgstr "التنقل المشترك" + +#: /usr/share/php/Icinga/Web/Widget/Dashboard.php:235 +#, php-format +msgctxt "dashboard.pane.tooltip" +msgid "Show %s" +msgstr "عرض %s" + +#: /usr/share/php/Icinga/Web/Widget/SearchDashboard.php:32 +msgctxt "dashboard.pane.tooltip" +msgid "Show Search" +msgstr "عرض البحث" + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:223 +#: /usr/share/icingaweb2/application/forms/Config/General/ApplicationConfigForm.php:38 +msgid "Show Stacktraces" +msgstr "عرض تتبعات المكدس" + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/settings.phtml:62 +#, php-format +msgid "Show dashlet %s" +msgstr "عرض لوحة صغيرة %s" + +#: /usr/share/icingaweb2/application/views/scripts/group/show.phtml:93 +#: /usr/share/icingaweb2/application/views/scripts/user/list.phtml:68 +#, php-format +msgid "Show detailed information about %s" +msgstr "عرض معلومات تفصيلية عن %s" + +#: /usr/share/icingaweb2/application/views/scripts/user/show.phtml:91 +#, php-format +msgid "Show detailed information for group %s" +msgstr "عرض معلومات تفصيلية عن المجموعة %s" + +#: /usr/share/icingaweb2/application/views/scripts/group/list.phtml:69 +#, php-format +msgid "Show detailed information for user group %s" +msgstr "عرض معلومات تفصيلية عن مجموعة المستخدمين %s" + +#: /usr/share/icingaweb2/application/controllers/GroupController.php:325 +#, php-format +msgid "Show group %s" +msgstr "عرض المجموعة %s" + +#: /usr/share/icingaweb2/application/views/scripts/mixedPagination.phtml:11 +#: /usr/share/icingaweb2/application/views/scripts/mixedPagination.phtml:44 +#: /usr/share/icingaweb2/application/views/scripts/mixedPagination.phtml:61 +#, php-format +msgid "Show rows %u to %u of %u" +msgstr "عرض الصفوف %u إلى %u من %u" + +#: /usr/share/icingaweb2/application/views/scripts/config/modules.phtml:32 +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:127 +#, php-format +msgid "Show the overview of the %s module" +msgstr "عرض ملخص عام للوحدة %s" + +#: /usr/share/icingaweb2/application/controllers/UserController.php:291 +#, php-format +msgid "Show user %s" +msgstr "عرض المستخدم %s" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationItemForm.php:43 +msgid "Single Column" +msgstr "عمود واحد" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:381 +msgid "Skip Validation" +msgstr "تخطي التحقق" + +#: /usr/share/icingaweb2/application/layouts/scripts/parts/navigation.phtml:15 +msgid "Skip to Content" +msgstr "تخطي للمحتوى" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LivestatusResourceForm.php:44 +msgid "Socket" +msgstr "مقبس" + +#: /usr/share/icingaweb2/application/views/scripts/showConfiguration.phtml:14 +msgid "Something went wrong while writing the file" +msgstr "حدث خطأ أثناء كتابة الملف" + +#: /usr/share/php/Icinga/Web/Widget/SortBox.php:196 +msgid "Sort by" +msgstr "فرز بواسطة" + +#: /usr/share/icingaweb2/application/views/scripts/config/module.phtml:20 +#: /usr/share/icingaweb2/application/views/scripts/user/show.phtml:32 +msgid "State" +msgstr "حالة" + +#: /usr/share/php/Icinga/Web/Widget/FilterEditor.php:423 +msgid "Strip this filter" +msgstr "تجريد هذا المرشح" + +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:86 +msgid "Support / Mailinglists" +msgstr "الدعم / القوائم البريدية" + +#: /usr/share/php/Icinga/Web/Menu.php:249 +#: /usr/share/php/Icinga/Application/Web.php:296 +msgid "System" +msgstr "النظام" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationItemForm.php:38 +msgid "Target" +msgstr "الهدف" + +#: /usr/share/icingaweb2/application/views/scripts/authentication/login.phtml:19 +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:29 +msgid "The Icinga Project" +msgstr "مشروع إسنجا" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:70 +msgid "The LDAP connection to use for authenticating with this provider." +msgstr "اتصال LDAP المستخدم للمصادقة مع هذا المزود" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:54 +msgid "The LDAP connection to use for this backend." +msgstr "اتصال LDAP المستخدم لهذه الخلفية" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:75 +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:85 +msgid "The application prefix must not contain whitespace." +msgstr "يتحتم على بادئة البرنامج ألا تحتوى على مسافات" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:181 +msgid "The attribute name used for storing a group's members." +msgstr "اسم السمة المستخدمة لتخزين أعضاء المجموعة." + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:168 +msgid "The attribute name used for storing a group's name on the LDAP server." +msgstr "اسم السمة المستخدمة لتخزين أسم المجموعة على خادم LDAP." + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:261 +msgid "The attribute name used for storing a user's name on the LDAP server." +msgstr "اسم السمة المستخدمة لتخزين أسم المستخدم على خادم LDAP." + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:191 +msgid "The attribute name used for storing the user name on the LDAP server." +msgstr "اسم السمة المستخدمة لتخزين أسم المستخدم على خادم LDAP." + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:145 +msgid "The character set for the database" +msgstr "طقم محارف قاعدة البيانات" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/FileResourceForm.php:60 +msgid "The column pattern must be a valid regular expression." +msgstr "يجب أن يكون نمط العمود تعبيرا عاديا صالحا." + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:324 +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:429 +msgid "The configuration has been successfully validated." +msgstr "تم التحقق بنجاح من الإعدادات" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/DbBackendForm.php:66 +msgid "The database connection to use for authenticating with this provider" +msgstr "اتصال قاعدة البيانات المستخدم للمصادقة مع هذا المزود" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/DbUserGroupBackendForm.php:48 +msgid "The database connection to use for this backend" +msgstr "اتصال قاعدة البيانات المستخدم لهذه الخلفية" + +#: /usr/share/icingaweb2/application/forms/Config/General/ThemingConfigForm.php:40 +msgctxt "Form element description" +msgid "The default theme" +msgstr "السمة الافتراضية" + +#: /usr/share/icingaweb2/application/views/scripts/showConfiguration.phtml:5 +#, php-format +msgid "The file %s couldn't be stored. (Error: \"%s\")" +msgstr "لا يمكن تخزين الملف %s. (خطأ: \"%s\")" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/FileResourceForm.php:42 +msgid "The filename to fetch information from" +msgstr "اسم الملف لجلب المعلومات منه" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:174 +msgid "The filter is invalid. Please check your syntax." +msgstr "المرشح غير صالح. يُرجى فحص البنية" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:151 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:244 +msgid "The filter must not be wrapped in parantheses." +msgstr "يجب ألا يكون المرشح بين قوسين." + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:139 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:232 +msgid "" +"The filter needs to be expressed as standard LDAP expression, without outer " +"parentheses. (e.g. &(foo=bar)(bar=foo) or foo=bar)" +msgstr "" +"يحتاج المرشح أن يعبر عنه كتعبير LDAP قياسي، دون الأقواس " +"الخارجية. (مثلا: &(foo=bar)(bar=foo) or foo=bar)" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:157 +msgid "" +"The filter needs to be expressed as standard LDAP expression. (e.g. " +"&(foo=bar)(bar=foo) or foo=bar)" +msgstr "" +"يحتاج المرشح أن يعبر عنه كتعبير LDAP قياسي، " +"(مثلا: &(foo=bar)(bar=foo) or foo=bar)" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/ExternalBackendForm.php:54 +msgid "The filter pattern must be a valid regular expression." +msgstr "يجب أن يكون نمط المرشح تعبيرا عاديا صالحا." + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/ExternalBackendForm.php:51 +msgid "" +"The filter to use to strip specific parts off from usernames. Leave empty if " +"you do not want to strip off anything." +msgstr "" +"الفلتر المستخدم لتجريد أجزاء معينة من من أسماء المستخدمين. دعه فارغا إذا " +"كنت لا تريد تجريد أي شيء." + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:116 +msgid "The full path to the log file to write messages to." +msgstr "المسار الكامل لملف السجل لكتابة الرسائل إليه." + +#: /usr/share/icingaweb2/application/forms/Config/Resource/SshResourceForm.php:62 +msgid "The given SSH key is invalid" +msgstr "مفتاح SSH المعطى غير صالح" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:97 +msgid "The hostname of the database" +msgstr "اسم مضيف قاعدة البيانات" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:49 +msgid "The hostname or address of the LDAP server to use for authentication" +msgstr "اسم المضيف أو العنوان لخادم LDAP لاستخدامه للمصادقة" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationItemForm.php:70 +msgid "" +"The icon of this navigation item. Leave blank if you do not want a icon " +"being displayed" +msgstr "" +"أيقونة عنصر التنقل هذا. دعها فارغة إن لم ترد " +"عرض الأيقونة" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:54 +msgid "The maximum logging level to emit." +msgstr "مستوى التسجيل الأقصى للبث" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:73 +msgid "The name of the application by which to prefix syslog messages." +msgstr "اسم التطبيق الذي يمكن من خلاله وضع بادئة لرسائل .syslog" + +#: /usr/share/icingaweb2/application/forms/Navigation/DashletForm.php:19 +msgid "The name of the dashboard pane in which to display this dashlet" +msgstr "اسم جزء لوحة المعلومات حيث تعرض هذه اللوحة الصغيرة" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:119 +msgid "The name of the database to use" +msgstr "اسم قاعدة البيانات للاستخدام" + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:109 +msgid "The name of the role" +msgstr "اسم الدور" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/DbBackendForm.php:55 +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/ExternalBackendForm.php:34 +msgid "" +"The name of this authentication provider that is used to differentiate it " +"from others" +msgstr "" +"اسم مزود المصادقة لاستخدامه في التفرقة " +"بينه وبين الآخرين" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:59 +msgid "" +"The name of this authentication provider that is used to differentiate it " +"from others." +msgstr "" +"اسم مزود المصادقة لاستخدامه في التفرقة " +"بينه وبين الآخرين." + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:584 +msgid "" +"The name of this navigation item that is used to differentiate it from others" +msgstr "اسم عنصر التنقل لاستخدامه في التفرقة بينه وبين الآخرين" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:41 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/DbUserGroupBackendForm.php:36 +msgid "" +"The name of this user group backend that is used to differentiate it from " +"others" +msgstr "" +"اسم خلفية مجموعة المستخدم لاستخدامه في التفرقة بينه وبين " +"الآخرين" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:123 +msgid "The object class used for storing groups on the LDAP server." +msgstr "فئة الكائن المستخدمة لتخزين المجموعات على خادم LDAP." + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:216 +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:140 +msgid "The object class used for storing users on the LDAP server." +msgstr "فئة الكائن المستخدمة لتخزين المستخدمين على خادم LDAP." + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:670 +msgid "" +"The parent item to assign this navigation item to. Select \"None\" to make " +"this a main navigation item" +msgstr "" +"العنصر الأصل لتعيين بند التنقل هذا إليه. اختر \"بلا\" لجعل " +"بند التنقل هذا رئيسيا" + +#: /usr/share/icingaweb2/application/forms/Navigation/MenuItemForm.php:27 +msgid "" +"The parent menu to assign this menu entry to. Select \"None\" to make this a " +"main menu entry" +msgstr "" +"القائمة الأم لتعيين مدخل القائمة هذا إليه. اختر \"بلا\" لجعل " +"مدخل القائمة هذا رئيسيا" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:138 +msgid "The password to use for authentication" +msgstr "كلمة المرور لاستخدامها في المصادقة" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:112 +msgid "The password to use for querying the ldap server" +msgstr "كلمة المرور لاستخدامها في الاستعلام عن خادم ldap" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LivestatusResourceForm.php:45 +msgid "The path to your livestatus socket used for querying monitoring data" +msgstr "" +"المسار إلى مقبس livestatus الخاصة بك لاستخدامه في الاستعلام عن بيانات المراقبة" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:192 +msgid "" +"The path where groups can be found on the LDAP server. Leave empty to select " +"all users available using the specified connection." +msgstr "" +"المسار حيث يمكن العثور على مجموعات على خادم LDAP. اتركه فارغا لتحديد " +"جميع المستخدمين المتوفرين باستخدام الاتصال المحدد." + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:273 +#: /usr/share/icingaweb2/application/forms/Config/UserBackend/LdapBackendForm.php:212 +msgid "" +"The path where users can be found on the LDAP server. Leave empty to select " +"all users available using the specified connection." +msgstr "" +"المسار حيث يمكن العثور على مستخدمين على خادم LDAP. اتركه فارغا لتحديد " +"جميع المستخدمين المتوفرين باستخدام الاتصال المحدد." + +#: /usr/share/icingaweb2/application/forms/Config/Resource/FileResourceForm.php:59 +msgid "The pattern by which to identify columns." +msgstr "النمط الذي يمكن من خلاله تحديد الأعمدة." + +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:135 +msgid "The permissions to grant. You may select more than one permission" +msgstr "الأذونات التي بالإمكان منحها. بإمكانك اختيار أكثر من إذن واحد" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:61 +msgid "The port of the LDAP server to use for authentication" +msgstr "منفذ خادم LDAP لاستخدامه للمصادقة" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:106 +msgid "The port to use" +msgstr "المنفذ المستخدم" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/SshResourceForm.php:136 +#, php-format +msgid "The private key for the user \"%s\" is already exists." +msgstr "المفتاح الخاص للمستخدم \"%s\" موجود مسبقا." + +#: /usr/share/icingaweb2/application/forms/Config/Resource/SshResourceForm.php:72 +msgid "The private key which will be used for the SSH connections" +msgstr "المفتاح الخاص الذي سيستخدم لاتصالات SSH" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:398 +#, php-format +msgid "" +"The resource \"%s\" is currently utilized for authentication by user backend " +"\"%s\". Removing the resource can result in noone being able to log in any " +"longer." +msgstr "" +"المورد \"%s\" يستخدم حاليا للمصادقة من قبل خلفية المستخدم \"%s\". " +"إزالة المورد يؤدي إلى عدم قدرة أي أحد على الدخول لفترة أطول." + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationItemForm.php:39 +msgid "The target where to open this navigation item's url" +msgstr "الهدف حيث سيفتح رابط عنصر التنقل هذا" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:87 +msgid "The type of SQL database" +msgstr "نوع قاعدة بيانات SQL" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:38 +msgid "The type of logging to utilize." +msgstr "نوع التسجيل المراد استخدامه." + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:253 +msgid "The type of resource" +msgstr "نوع المورد" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:297 +msgid "The type of the resource to use for this authenticaton provider" +msgstr "نوع المورد المراد استخدامه لمزود المصادقة هذا" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:648 +msgid "The type of this navigation item" +msgstr "نوع عنصر التنقل هذا" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupBackendForm.php:181 +msgid "The type of this user group backend" +msgstr "نوع خلفية مجموعة هذا المستخدم" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:77 +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:39 +#: /usr/share/icingaweb2/application/forms/Config/Resource/LivestatusResourceForm.php:36 +#: /usr/share/icingaweb2/application/forms/Config/Resource/FileResourceForm.php:33 +#: /usr/share/icingaweb2/application/forms/Config/Resource/SshResourceForm.php:37 +msgid "The unique name of this resource" +msgstr "الاسم الوحيد لهذا المورد" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationItemForm.php:56 +msgid "" +"The url of this navigation item. Leave blank if you only want the name being " +"displayed. For external urls, make sure to prepend an appropriate protocol " +"identifier (e.g. http://example.tld)" +msgstr "" +"عنوان الموقع في بند التنقل هذا. اتركه فارغا إذا كنت تريد فقط للاسم أن يُعرض. " +"لعناوين المواقع الخارجية، تأكد من إلحاقها بمعرف بروتوكول مناسب " +"(مثل: http: //example.tld)" + +#: /usr/share/icingaweb2/application/forms/Navigation/DashletForm.php:29 +msgid "" +"The url to load in the dashlet. For external urls, make sure to prepend an " +"appropriate protocol identifier (e.g. http://example.tld)" +msgstr "" +"عنوان الموقع لتحميله في اللوحة الصغيرة. لعناوين المواقع الخارجية، تأكد من" +" إلحاقها " +"بمعرف بروتوكول مناسب (مثل: http://example.tld)" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:78 +msgid "The user backend to link with this user group backend." +msgstr "خلفية المستخدم لربطها مع خلفة مجموعة المستخدم." + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:101 +msgid "" +"The user dn to use for querying the ldap server. Leave the dn and password " +"empty for attempting an anonymous bind" +msgstr "" +"الاسم المميز للمستخدم لاستخدامه في استعلام خادم ldap. اترك الاسم المميز وكلمة" +" المرور " +"فارغين لمحاولة الربط المجهول" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:128 +msgid "The user name to use for authentication" +msgstr "اسم المستخدم لاستخدامه في المصادقة" + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:170 +msgctxt "Form element label" +msgid "Theme" +msgstr "السمة" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/shared.phtml:15 +msgid "There are currently no navigation items being shared" +msgstr "لا يوجد حاليا عناصر تنقل يجري مشاركتها" + +#: /usr/share/icingaweb2/application/views/scripts/config/module.phtml:6 +msgid "There is no such module installed." +msgstr "لا توحد مثل هذه الوحدة مثبتة." + +#: /usr/share/icingaweb2/application/views/scripts/showConfiguration.phtml:15 +msgid "" +"There's an application error preventing you from persisting the configuration" +msgstr "هناك خطأ في التطبيق يمنعك من تثبيت الإعدادات" + +#: /usr/share/icingaweb2/application/views/scripts/showConfiguration.phtml:10 +msgid "This could have one or more of the following reasons:" +msgstr "هذا قد يكون بسبب واحد أو أكثر من الأسباب التالية:" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/shared.phtml:49 +#, php-format +msgid "" +"This is a child of the navigation item %1$s. You can only unshare this item " +"by unsharing %1$s" +msgstr "" +"هذا تابع لعنصر التنقل %1$s. يمكنك فقط إلغاء مشارك هذا العنصر " +"بإلغاء مشاركة %1$s" + +#: /usr/share/icingaweb2/application/views/scripts/config/module.phtml:50 +msgid "This module has no dependencies" +msgstr "هذه الوحدة ليس لها اعتمادات" + +#: /usr/share/php/Icinga/Application/Modules/Module.php:721 +msgid "This module has no description" +msgstr "هذه الوحدة ليس لها وصف" + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:244 +msgid "" +"This option allows you to enable or to disable the global page content auto " +"refresh" +msgstr "" +"هذا الخيار يسمح لك بتفعيل أو تعطيل التحديث " +"التلقائي لمحتوى الصفحة الكامل" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:603 +msgid "Tick this box to share this item with others" +msgstr "ضع علامة في هذا المربع لمشاركة هذا البند مع الآخرين" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/SshResourceForm.php:91 +msgid "To modify the private key you must recreate this resource." +msgstr "لتعديل المفتاح الخاص يجب عليك إعادة إنشاء هذا المورد" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/index.phtml:30 +#: /usr/share/icingaweb2/application/views/scripts/navigation/shared.phtml:20 +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:647 +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:149 +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:199 +msgid "Type" +msgstr "النوع" + +#: /usr/share/icingaweb2/application/views/scripts/config/devtools.phtml:5 +msgid "UI Debug" +msgstr "واجهة مستخدم تصحيح الأخطاء" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:486 +#, php-format +msgid "" +"Unable to delete navigation item \"%s\". There are other items dependent " +"from it: %s" +msgstr "" +"غير قادر على حذف عنصر التنقل \"%s\". هناك عناصر أخرى " +"تابعة له: %s" + +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:526 +#, php-format +msgid "" +"Unable to unshare navigation item \"%s\". It is dependent from item \"%s\". " +"Dependent items can only be unshared by unsharing their parent" +msgstr "" +"غير قادر على إلغاء مشاركة عنصر التنقل \"%s\". فهو تابع للعنصر \"%s\". " +"العناصر التابعة يمكن إلغاء مشاركتها فقط بإلغاء مشاركة أصلها" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/index.phtml:54 +#: /usr/share/icingaweb2/application/views/scripts/navigation/shared.phtml:42 +msgid "Unknown" +msgstr "مجهول" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:100 +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:124 +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:198 +msgid "Unknown resource provided" +msgstr "تم تقديم مورد مجهول" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/shared.phtml:22 +msgid "Unshare" +msgstr "إلغاء مشاركة" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:182 +msgid "Unshare this navigation item" +msgstr "إلغاء مشاركة عنصر تنقل" + +#: /usr/share/icingaweb2/application/controllers/DashboardController.php:86 +#: /usr/share/icingaweb2/application/controllers/DashboardController.php:92 +msgid "Update Dashlet" +msgstr "تحديث لوحة مصغرة" + +#: /usr/share/icingaweb2/application/controllers/NavigationController.php:304 +msgid "Update Navigation Item" +msgstr "تحديث عنصر تنقل" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:346 +msgid "Update Resource" +msgstr "تحديث مورد" + +#: /usr/share/icingaweb2/application/controllers/RoleController.php:76 +#: /usr/share/icingaweb2/application/controllers/RoleController.php:103 +msgid "Update Role" +msgstr "تحديث دور" + +#: /usr/share/icingaweb2/application/controllers/UserController.php:188 +msgid "Update User" +msgstr "تحديث مستخدم" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:274 +msgid "Update User Backend" +msgstr "تحديث خلفية مستخدم" + +#: /usr/share/icingaweb2/application/controllers/GroupController.php:183 +msgid "Update User Group" +msgstr "تحديث مجموعة مستخدم" + +#: /usr/share/icingaweb2/application/controllers/UsergroupbackendController.php:102 +msgid "Update User Group Backend" +msgstr "تحديث خلفية مجموعة مستخدم" + +#: /usr/share/php/Icinga/Web/Form/Decorator/Autosubmit.php:99 +msgid "" +"Upon any of this form's fields were changed, this page is being updated " +"automatically." +msgstr "" +" عند تغيير أي من حقول هذا النموذج، فإن هذه الصفحة يتم تحديثها " +"تلقائيا." + +#: /usr/share/php/Icinga/Web/Form.php:951 +#: /usr/share/php/Icinga/Web/Form/Decorator/Autosubmit.php:100 +msgid "" +"Upon its value has changed, this field issues an automatic update of this " +"page." +msgstr "" +"عند تغيير قيمتها، فإن هذا الحقل يصدر تحديث تلقائي لهذه " +"الصفحة." + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/settings.phtml:14 +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationItemForm.php:54 +#: /usr/share/icingaweb2/application/forms/Navigation/DashletForm.php:27 +#: /usr/share/icingaweb2/application/forms/Dashboard/DashletForm.php:69 +msgid "Url" +msgstr "رابط" + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:234 +msgid "Use benchmark" +msgstr "استخدام مؤشر أداء" + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:198 +msgid "Use the following language to display texts and messages" +msgstr "استخدم اللغة التالية لعرض النصوص والرسائل" + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:210 +msgid "Use the following timezone for dates and times" +msgstr "استخدم المنطقة الزمنية التالية لعرض التواريخ والأزمنة" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/SshResourceForm.php:45 +#: /usr/share/icingaweb2/application/controllers/UserController.php:292 +msgid "User" +msgstr "المستخدم" + +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:155 +#, php-format +msgid "User \"%s\" has been edited" +msgstr "تم تحرير المستخدم \"%s\"" + +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:171 +#, php-format +msgid "User \"%s\" has been removed" +msgstr "تمت إزالة المستخدم \"%s\"" + +#: /usr/share/icingaweb2/application/controllers/GroupController.php:257 +#, php-format +msgid "User \"%s\" has been removed from group \"%s\"" +msgstr "تمت إزالة المستخدم \"%s\" من المجموعة \"%s\"" + +#: /usr/share/icingaweb2/application/controllers/UserController.php:94 +#: /usr/share/icingaweb2/application/controllers/UserController.php:185 +#: /usr/share/icingaweb2/application/controllers/UserController.php:207 +#: /usr/share/icingaweb2/application/controllers/UserController.php:223 +#, php-format +msgid "User \"%s\" not found" +msgstr "لم يُعثر على المستخدم \"%s\"" + +#: /usr/share/icingaweb2/application/forms/Config/User/CreateMembershipForm.php:98 +#, php-format +msgid "User %s is already a member of all groups" +msgstr "المستخدم \"%s\" هو عضو في كل المجموعات مسبقا" + +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php:77 +#: /usr/share/icingaweb2/application/controllers/UserController.php:47 +msgid "User Backend" +msgstr "خلفية المستخدم" + +#: /usr/share/icingaweb2/application/views/scripts/config/userbackend/reorder.phtml:5 +msgid "User Backends" +msgstr "خلفيات المستخدم" + +#: /usr/share/icingaweb2/application/views/scripts/group/list.phtml:50 +#: /usr/share/icingaweb2/application/controllers/UserController.php:101 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:69 +#: /usr/share/php/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php:465 +#: /usr/share/php/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:115 +#: /usr/share/php/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:120 +msgid "User Group" +msgstr "مجموعة المستخدم" + +#: /usr/share/icingaweb2/application/controllers/GroupController.php:47 +msgid "User Group Backend" +msgstr "خلفية مجموعة المستخدم" + +#: /usr/share/icingaweb2/application/views/scripts/config/userbackend/reorder.phtml:19 +msgid "User Group Backends" +msgstr "خلفيات مجموعة المستخدم" + +#: /usr/share/icingaweb2/application/controllers/UserController.php:330 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:364 +#: /usr/share/icingaweb2/application/controllers/RoleController.php:173 +msgid "User Groups" +msgstr "مجموعات المستخدم" + +#: /usr/share/icingaweb2/application/forms/Config/General/ApplicationConfigForm.php:67 +msgid "User Preference Storage Type" +msgstr "نوع تخزين تفضيلات المستخدم" + +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:139 +msgid "User added successfully" +msgstr "أُضيف المستخدم بنجاح" + +#: /usr/share/icingaweb2/application/forms/Config/UserBackendReorderForm.php:70 +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:271 +#, php-format +msgid "User backend \"%s\" not found" +msgstr "لم يُعثر على خلفية المستخدم \"%s\"" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:298 +#, php-format +msgid "User backend \"%s\" successfully removed" +msgstr "أُزيلت خلفية المستخدم \"%s\" بنجاح" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:259 +#, php-format +msgid "User backend \"%s\" successfully updated" +msgstr "حُدثت خلفية المستخدم \"%s\" بنجاح" + +#: /usr/share/icingaweb2/application/controllers/ConfigController.php:223 +msgid "User backend successfully created" +msgstr "أُنشئت خلفية المستخدم بنجاح" + +#: /usr/share/php/Icinga/Web/Controller/AuthBackendController.php:127 +#, php-format +msgid "User group backend \"%s\" is not %s" +msgstr "خلفية مجموعة المستخدم \"%s\" ليست %s" + +#: /usr/share/icingaweb2/application/controllers/UsergroupbackendController.php:99 +#: /usr/share/php/Icinga/Web/Controller/AuthBackendController.php:120 +#, php-format +msgid "User group backend \"%s\" not found" +msgstr "لم يُعثر على خلفية المجموعة \"%s\"" + +#: /usr/share/icingaweb2/application/controllers/UsergroupbackendController.php:125 +#, php-format +msgid "User group backend \"%s\" successfully removed" +msgstr "أُزيلت خلفية المجموعة \"%s\" بنجاح" + +#: /usr/share/icingaweb2/application/controllers/UsergroupbackendController.php:88 +#, php-format +msgid "User group backend \"%s\" successfully updated" +msgstr "حُدثت خلفية المجموعة \"%s\" بنجاح" + +#: /usr/share/icingaweb2/application/controllers/UsergroupbackendController.php:53 +msgid "User group backend successfully created" +msgstr "أُنشئت خلفية المجموعة بنجاح" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/SshResourceForm.php:47 +msgid "" +"User to log in as on the remote Icinga instance. Please note that key-based " +"SSH login must be possible for this user" +msgstr "" +"الاسم المستخدم لتسجيل الدخول به لحالة إسنجا البعيدة. يرجى ملاحظة أن " +"دخول SSH القائم على مفتاح يجب أن يكون متاحا لهذا المستخدم" + +#: /usr/share/php/Icinga/Web/Menu.php:294 +msgid "Usergroups" +msgstr "مجموعات المستخدمين" + +#: /usr/share/icingaweb2/application/views/scripts/group/show.phtml:75 +#: /usr/share/icingaweb2/application/views/scripts/user/list.phtml:51 +#: /usr/share/icingaweb2/application/forms/Authentication/LoginForm.php:43 +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:33 +#: /usr/share/icingaweb2/application/forms/Config/Resource/DbResourceForm.php:127 +#: /usr/share/icingaweb2/application/controllers/UserController.php:69 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:105 +#: /usr/share/php/Icinga/Authentication/User/LdapUserBackend.php:255 +#: /usr/share/php/Icinga/Authentication/User/DbUserBackend.php:115 +#: /usr/share/php/Icinga/Authentication/User/DbUserBackend.php:118 +#: /usr/share/php/Icinga/Authentication/UserGroup/LdapUserGroupBackend.php:464 +#: /usr/share/php/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:114 +#: /usr/share/php/Icinga/Authentication/UserGroup/DbUserGroupBackend.php:118 +msgid "Username" +msgstr "اسم المستخدم" + +#: /usr/share/icingaweb2/application/views/scripts/role/list.phtml:23 +#: /usr/share/icingaweb2/application/forms/Navigation/NavigationConfigForm.php:613 +#: /usr/share/icingaweb2/application/forms/Security/RoleForm.php:117 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/AddMemberForm.php:118 +#: /usr/share/icingaweb2/application/controllers/UserController.php:322 +#: /usr/share/icingaweb2/application/controllers/GroupController.php:356 +#: /usr/share/icingaweb2/application/controllers/RoleController.php:165 +#: /usr/share/php/Icinga/Web/Menu.php:289 +msgid "Users" +msgstr "المستخدمون" + +#: /usr/share/icingaweb2/application/forms/Config/General/ThemingConfigForm.php:57 +msgctxt "Form element label" +msgid "Users Can't Change Theme" +msgstr "المستخدمون لا يستطيعون تغيير السمة" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:348 +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:453 +msgid "Validate Configuration" +msgstr "تحقق من الإعدادات" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:349 +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:454 +msgid "Validation In Progress" +msgstr "جار التحقق" + +#: /usr/share/icingaweb2/application/forms/Config/ResourceConfigForm.php:306 +#: /usr/share/icingaweb2/application/forms/Config/UserBackendConfigForm.php:411 +msgid "Validation Log" +msgstr "سجل التحقق" + +#: /usr/share/icingaweb2/application/views/scripts/config/module.phtml:39 +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:14 +#: /usr/share/icingaweb2/application/views/scripts/about/index.phtml:115 +msgid "Version" +msgstr "إصدار" + +#: /usr/share/icingaweb2/application/forms/Config/General/LoggingConfigForm.php:57 +msgctxt "app.config.logging.level" +msgid "Warning" +msgstr "تحذير" + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/index.phtml:12 +msgid "Welcome to Icinga Web!" +msgstr "أهلاً بكم في نظام إسنجا وب!" + +#: /usr/share/icingaweb2/application/forms/Config/Resource/LdapResourceForm.php:73 +msgid "" +"Whether to encrypt communication. Choose STARTTLS or LDAPS for encrypted " +"communication or none for unencrypted communication" +msgstr "" +"سواء لتشفير الاتصالات أو عدمه. اختر STARTTLS أو LDAPS للاتصال " +"المشفر أو \"بلا\" للاتصال غير المشفر" + +#: /usr/share/icingaweb2/application/views/scripts/joystickPagination.phtml:70 +#: /usr/share/icingaweb2/application/views/scripts/joystickPagination.phtml:96 +msgctxt "pagination.joystick" +msgid "X-Axis" +msgstr "محور-س" + +#: /usr/share/icingaweb2/application/views/scripts/joystickPagination.phtml:42 +#: /usr/share/icingaweb2/application/views/scripts/joystickPagination.phtml:125 +msgctxt "pagination.joystick" +msgid "Y-Axis" +msgstr "محور-ص" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/index.phtml:57 +#: /usr/share/icingaweb2/application/forms/Config/User/UserForm.php:116 +#: /usr/share/icingaweb2/application/forms/Config/UserGroup/UserGroupForm.php:66 +msgid "Yes" +msgstr "نعم" + +#: /usr/share/icingaweb2/application/views/scripts/navigation/index.phtml:23 +msgid "You did not create any navigation item yet." +msgstr "لم تقم بإنشاء أ عنصر تنقل بعد." + +#: /usr/share/icingaweb2/application/views/scripts/showConfiguration.phtml:13 +msgid "You don't have file-system permissions to write to the file" +msgstr "ليس لديك أذونات في نظام الملفات للكتابة في الملف" + +#: /usr/share/icingaweb2/application/controllers/UserController.php:229 +msgid "" +"You'll need to configure at least one user group backend first that allows " +"to create new memberships" +msgstr "" +"ستحتاج لإعداد خلفية مجموعة مستخدم واحدة على الأقل أولا تسمح " +"بإنشاء عضوية جديدة" + +#: /usr/share/icingaweb2/application/forms/Authentication/LoginForm.php:138 +msgid "" +"You're currently not authenticated using any of the web server's " +"authentication mechanisms. Make sure you'll configure such, otherwise you'll " +"not be able to login." +msgstr "" +"لا يمكنك حاليا المصادقة باستخدام أي من آليات مصادقة " +"خادم الوب. تأكد من أنك سوف تقوم بإعداد واحدة، " +"وإلا فسوف لن تكون قادرا على الدخول." + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:197 +msgid "Your Current Language" +msgstr "لغتك الحالية" + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:209 +msgid "Your Current Timezone" +msgstr "منطقتك الزمنية الحالية" + +#: /usr/share/php/Icinga/Date/DateFormatter.php:176 +#, php-format +msgctxt "An event happened at the given time" +msgid "at %s" +msgstr "عند %s" + +#: /usr/share/php/Icinga/Date/DateFormatter.php:250 +#, php-format +msgctxt "An event will happen at the given time" +msgid "at %s" +msgstr "عند %s" + +#: /usr/share/icingaweb2/application/forms/PreferenceForm.php:164 +#: /usr/share/icingaweb2/application/forms/Config/General/ThemingConfigForm.php:34 +msgid "default" +msgstr "افتراضي" + +#: /usr/share/icingaweb2/application/views/scripts/config/module.phtml:23 +msgid "disable" +msgstr "تَعطيل" + +#: /usr/share/icingaweb2/application/views/scripts/config/module.phtml:31 +msgid "enable" +msgstr "تفعيل" + +#: /usr/share/php/Icinga/Date/DateFormatter.php:199 +#, php-format +msgctxt "A status is lasting for the given time interval" +msgid "for %s" +msgstr "لـ %s" + +#: /usr/share/icingaweb2/application/views/scripts/joystickPagination.phtml:43 +#: /usr/share/icingaweb2/application/views/scripts/joystickPagination.phtml:126 +msgctxt "pagination.joystick" +msgid "hosts" +msgstr "مضيفون" + +#: /usr/share/php/Icinga/Date/DateFormatter.php:245 +#, php-format +msgctxt "An event will happen after the given time interval has elapsed" +msgid "in %s" +msgstr "في %s" + +#: /usr/share/icingaweb2/application/views/scripts/dashboard/index.phtml:21 +msgid "modules" +msgstr "وحدات" + +#: /usr/share/php/Icinga/Date/DateFormatter.php:165 +#, php-format +msgctxt "An event happened on the given date or date and time" +msgid "on %s" +msgstr "عند %s" + +#: /usr/share/php/Icinga/Date/DateFormatter.php:239 +#, php-format +msgctxt "An event will happen on the given date or date and time" +msgid "on %s" +msgstr "عند %s" + +#: /usr/share/icingaweb2/application/views/scripts/joystickPagination.phtml:71 +#: /usr/share/icingaweb2/application/views/scripts/joystickPagination.phtml:97 +msgctxt "pagination.joystick" +msgid "services" +msgstr "خدمات" + +#: /usr/share/php/Icinga/Date/DateFormatter.php:209 +#, php-format +msgctxt "A status is lasting since the given time, date or date and time" +msgid "since %s" +msgstr "منذ %s" + +#: /usr/share/icingaweb2/application/views/scripts/config/devtools.phtml:5 +msgid "toggle" +msgstr "تبديل" + + diff --git a/modules/monitoring/application/locale/ar_SA/LC_MESSAGES/monitoring.po b/modules/monitoring/application/locale/ar_SA/LC_MESSAGES/monitoring.po new file mode 100644 index 000000000..15e202b76 --- /dev/null +++ b/modules/monitoring/application/locale/ar_SA/LC_MESSAGES/monitoring.po @@ -0,0 +1,4346 @@ +# Icinga Web 2 - Head for multiple monitoring backends. +# Copyright (C) 2016 Icinga Development Team +# This file is distributed under the same license as Monitoring Module. +# +# Munzir Taha , 2016. +msgid "" +msgstr "" +"Project-Id-Version: Monitoring Module (2.3.2)\n" +"Report-Msgid-Bugs-To: dev@icinga.org\n" +"POT-Creation-Date: 2016-05-25 05:05+0300\n" +"PO-Revision-Date: 2016-06-02 10:09+0300\n" +"Last-Translator: Munzir Taha \n" +"Language: ar_SA\n" +"Language-Team: English \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 2.0\n" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:375 +msgid " Down Hosts (Handled)" +msgstr "أجهزة مضيفة متوقفة (عولجت)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:376 +msgid " Down Hosts (Unhandled)" +msgstr "أجهزة مضيفة متوقفة ( لم تعالج)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:343 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:397 +msgid " Down Services (Handled)" +msgstr "خدمات متوقفة (عُولجت)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:344 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:398 +msgid " Down Services (Unhandled)" +msgstr "خدمات متوقفة (لم تُعالج)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:379 +msgid " Pending Hosts" +msgstr "أجهزة مضيفة معلقة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:347 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:401 +msgid " Pending Services" +msgstr "خدمات معلقة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:377 +msgid " Unreachable Hosts (Handled)" +msgstr "أجهزة مضيفة لا يمكن الوصول إليها (عُولجت)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:378 +msgid " Unreachable Hosts (Unhandled)" +msgstr "أجهزة مضيفة لا يمكن الوصول إليها (لم تُعالج)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:345 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:399 +msgid " Unreachable Services (Handled)" +msgstr "حدمات لا يمكن الوصول إليها (عولجت)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:346 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:400 +msgid " Unreachable Services (Unhandled)" +msgstr "حدمات لا يمكن الوصول إليها (لم تُعالج)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:374 +msgid " Up Hosts" +msgstr "أجهزة مضيفة قيد التشغيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:394 +msgid " Up Services" +msgstr "خدمات قيد التشغيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:341 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:395 +msgid " Warning Services (Handled)" +msgstr "خدمات تحذيرية (عولجت)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:342 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:396 +msgid " Warning Services (Unhandled)" +msgstr "خدمات تحذيرية (لم تعالج)" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:65 +#, php-format +msgctxt "Last format parameter represents the time running" +msgid "%1$s has been up and running with PID %2$d %3$s" +msgstr "%1$s ما زالت قيد التشغيل ب PID %2$d %3$s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:35 +msgctxt "date.verbose" +msgid "%A, %B %e, %Y" +msgstr "%A, %B %e, %Y" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:75 +#, php-format +msgid "%d Active" +msgid_plural "%d Active" +msgstr[0] "%d نشطة" +msgstr[1] "%d نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:55 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:111 +#, php-format +msgid "%d Disabled" +msgid_plural "%d Disabled" +msgstr[0] "%d متعطلة" +msgstr[1] "%d متعطلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:37 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:93 +#, php-format +msgid "%d Passive" +msgid_plural "%d Passive" +msgstr[0] "%d سلبية" +msgstr[1] "%d سلبية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:38 +#, php-format +msgid "%d hosts down on %s" +msgstr "%d أجهزة مضيفة متوقفة على %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:16 +#, php-format +msgid "%d hosts ok on %s" +msgstr "%d أجهزة مضيفة سليمة على %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:21 +#, php-format +msgid "%d hosts unreachable on %s" +msgstr "%d أجهزة مضيفة لا يمكن الوصول إليها على %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:97 +#, php-format +msgid "%d more ..." +msgstr "%d المزيد ..." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:54 +#, php-format +msgid "%d notifications have been sent for this issue." +msgstr "%d من الإخطارات أرسلت بخصوص هذه المشكلة." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:118 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:117 +#, php-format +msgid "%d scheduled downtime" +msgid_plural "%d scheduled downtimes" +msgstr[0] "%d زمن توقف مجدول" +msgstr[1] "%d من أزمنة التوقف المجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:26 +#, php-format +msgid "%d services critical on %s" +msgstr "%d من الخدمات الضرورية على %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:48 +#, php-format +msgid "%d services ok on %s" +msgstr "%d من الخدمات سليمة على %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:43 +#, php-format +msgid "%d services unknown on %s" +msgstr "%d من الخدمات مجهولة على %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:32 +#, php-format +msgid "%d services warning on %s" +msgstr "%d من الخدمات التحذيرية على %s" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:136 +#, php-format +msgid "%d unhandled hosts down" +msgstr "%d أجهزة مضيفة لم تُعالج" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:27 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:27 +#, php-format +msgid "%d unhandled problems" +msgstr "%d مشاكل لم تُعالج" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:148 +#, php-format +msgid "%d unhandled services critical" +msgstr "%d من الخدمات غير المعالجة ضرورية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:46 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:44 +#, php-format +msgid "%s acknowledgement" +msgid_plural "%s acknowledgements" +msgstr[0] "%s إشعار" +msgstr[1] "%s من الإشعارات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:80 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:79 +#, php-format +msgid "%s comment" +msgid_plural "%s comments" +msgstr[0] "%s تعليق" +msgstr[1] "%s من التعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/not-running.phtml:7 +#, php-format +msgid "%s is currently not up and running" +msgstr "%s حاليا لا تعمل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/components/selectioninfo.phtml:12 +#, php-format +msgctxt "Multi-selection count" +msgid "%s row(s) selected" +msgstr "%s من الصفوف تم اختيارها" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:19 +#, php-format +msgid "%u Active" +msgid_plural "%u Active" +msgstr[0] "%u نشطة" +msgstr[1] "%u نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/components/hostssummary.phtml:12 +#, php-format +msgid "%u Host" +msgid_plural "%u Hosts" +msgstr[0] "%u مضيف" +msgstr[1] "%u أجهزة مضيفة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/problem_hosts.phtml:11 +#, php-format +msgid "%u Host DOWN" +msgid_plural "%u Hosts DOWN" +msgstr[0] "%u مضيف متوقف" +msgstr[1] "%u أجهزة مضيفة متوقفة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:20 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:141 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:220 +#, php-format +msgid "%u Host Disabled" +msgid_plural "%u Hosts Disabled" +msgstr[0] "%u مضيف معطل" +msgstr[1] "%u من الأجهزة المضيفة معطلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:50 +#, php-format +msgid "%u Host Flapping" +msgid_plural "%u Hosts Flapping" +msgstr[0] "%u مضيف مذبذب" +msgstr[1] "%u أجهزة مضيفة مذبذبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/ok_hosts.phtml:34 +#, php-format +msgid "%u Host PENDING" +msgid_plural "%u Hosts PENDING" +msgstr[0] "%u مضيف معلق" +msgstr[1] "%u أجهزة مضيفة معلقة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/problem_hosts.phtml:30 +#, php-format +msgid "%u Host UNREACHABLE" +msgid_plural "%u Hosts UNREACHABLE" +msgstr[0] "%u مضيف لا يمكن الوصول إليه" +msgstr[1] "%u من الأجهزة المضيفة لا يمكن الوصول إليها" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/ok_hosts.phtml:16 +#, php-format +msgid "%u Host UP" +msgid_plural "%u Hosts UP" +msgstr[0] "%u مضيف سليم" +msgstr[1] "%u أجهزة مضيفة سليمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/components/servicesummary.phtml:13 +#, php-format +msgid "%u Service" +msgid_plural "%u Services" +msgstr[0] "%u خدمة" +msgstr[1] "%u من الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:75 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:175 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:254 +#, php-format +msgid "%u Service Disabled" +msgid_plural "%u Services Disabled" +msgstr[0] "%u خدمة متعطلة" +msgstr[1] "%u من الخدمات متعطلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:105 +#, php-format +msgid "%u Service Flapping" +msgid_plural "%u Services Flapping" +msgstr[0] "%u خدمة مذبذبة" +msgstr[1] "%u من الخدمات مذبذبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:80 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:179 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:278 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:325 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:372 +#, php-format +msgid "%u is not checked at all" +msgid_plural "%u are not checked at all" +msgstr[0] "%u لم يتم فحصها مطلقا" +msgstr[1] "%u لم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:53 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:152 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:251 +#, php-format +msgid "%u is passively checked" +msgid_plural "%u are passively checked" +msgstr[0] "%u تم فحصها سلبيا" +msgstr[1] "%u تم فحصها سلبيا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hosts.phtml:62 +#, php-format +msgid "%u unhandled service" +msgid_plural "%u unhandled services" +msgstr[0] "%u خدمة لم تُعالج" +msgstr[1] "%u من الخدمات لم تُعالج" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/DataView/DataView.php:235 +msgid "(Case insensitive)" +msgstr "(غير حساسة لحالة الأحرف)" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:72 +msgid "1 Year" +msgstr "سنة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:73 +msgid "2 Years" +msgstr "سنتان" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:69 +msgid "3 Months" +msgstr "3 أشهر" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:103 +msgid "4 Hours" +msgstr "4 ساعات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:70 +msgid "4 Months" +msgstr "4 أشهر" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:71 +msgid "8 Months" +msgstr "8 أشهر" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:448 +msgid "{title}: {value}m max. reaction time" +msgstr "{title}: {value}m أقصى زمن رد فعل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:438 +msgid "{title}: {value}m min. reaction time" +msgstr "{title}: {value}m أدنى زمن رد فعل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:289 +msgid "{title}:
{value} of {sum} hosts are {label}" +msgstr "{title}:
{value} من {sum} أجهزة مضيفة {label}" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:240 +msgid "{title}:
{value} of {sum} services are {label}" +msgstr "{title}:
{value} من {sum} خدمات {label}" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:129 +#, php-format +msgid "A command transport with the name \"%s\" does already exist" +msgstr "ناقل أمر باسم \"%s\" موجود مسبقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:46 +msgid "" +"A comment, as entered by the author, associated with the scheduled downtime" +msgstr "تعليق مدخل من قبل المؤلف له ارتباط بزمن التوقف المجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:77 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:212 +msgid "A downtime must not be in the past" +msgstr "زمن التوقف لا يجب أن يكون في الماضي" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:113 +#, php-format +msgid "A monitoring backend with the name \"%s\" does already exist" +msgstr " خلفية المراقبة المسماة \"%s\" موجودة مسبقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:49 +#, php-format +msgid "A notification has been sent for this issue %s." +msgstr "أُرسل إشعار بخصوص هذه المشكلة %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:67 +msgid "ACKNOWLEDGED" +msgstr "معروف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:72 +msgid "ACKNOWLEDGEMENT REMOVED" +msgstr "تمت إزالة إشعار المعرفة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:30 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:29 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:77 +msgid "Acknowledge" +msgstr "يعرف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:118 +msgid "Acknowledge Host Problem" +msgstr "تعرف على مشكلة مضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:175 +msgid "Acknowledge Host Problems" +msgstr "تعرف على مشاكل الأجهزة المضيفة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:76 +msgid "Acknowledge Service Problem" +msgstr "تعرف على مشكلة خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:178 +msgid "Acknowledge Service Problems" +msgstr "تعرف على مشاكل خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:34 +msgid "Acknowledge problem" +msgid_plural "Acknowledge problems" +msgstr[0] "تعرف مشكلة" +msgstr[1] "تعرف مشاكل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:85 +msgid "" +"Acknowledge this problem, suppress all future notifications for it and tag " +"it as being handled" +msgstr "" +"تعرف على هذه المشكلة، واخمد كل الإشعارات المستقبلية لها وضع علامة أنه تم" +" معالجتها" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml:9 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml:9 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:15 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:16 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:12 +msgid "Acknowledged" +msgstr "معروف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:335 +msgid "Acknowledged Problem Hosts" +msgstr "مضيفون بمشاكل معروفة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:331 +msgid "Acknowledged Problem Services" +msgstr "خدمات بمشاكل معروفة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:20 +msgid "Acknowledgement" +msgstr "إشعار استلام" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:35 +#, php-format +msgid "" +"Acknowledgement remains until the %1$s recovers even if the %1$s changes " +"state" +msgstr "ستبقى الإشعارات حتى تتعافي %1$s حتى ولو غيرت %1$s حالتها" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:65 +msgid "Acknowledgements" +msgstr "إشعارات استلام" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:302 +msgid "Acknowledgements Active For At Least Three Days" +msgstr "الإشعارات النشطة لثلاثة أيام على الأقل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:157 +msgid "Acknowledging problem.." +msgid_plural "Acknowledging problems.." +msgstr[0] "اعتراف بمشكلة.." +msgstr[1] "اعتراف بمشاكل.." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/actions.phtml:41 +msgid "Actions" +msgstr "أفعال" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml:24 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml:24 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:29 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:25 +msgid "Active And Passive Checks Disabled" +msgstr "تم تعطيل الفحوصات النشطة والسالبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:38 +msgid "Active Checks" +msgstr "الفحوصات النشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml:22 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml:22 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:31 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:27 +msgid "Active Checks Disabled" +msgstr "تم تعطيل الفحوصات النشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:54 +msgid "Active Endpoint" +msgstr "نقطة نهاية نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:90 +msgid "Active Host Checks" +msgstr "فحوصات المضيف النشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:99 +msgid "Active Service Checks" +msgstr "فحوصات الخدمة نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:109 +msgid "Active checks" +msgstr "فحوصات نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:145 +msgid "Actual end time" +msgstr "زمن النهاية الفعلي " + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:140 +msgid "Actual start time" +msgstr "زمن البدء الفعلي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:130 +msgid "Add Host Comment" +msgstr "إضافة تعليق مضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:163 +msgid "Add Host Comments" +msgstr "إضافة تعليقات مضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:88 +msgid "Add Service Comment" +msgstr "إضافة تعليق خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:166 +msgid "Add Service Comments" +msgstr "إضافة تعليقات خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/comments.phtml:14 +msgid "Add a new comment to this host" +msgstr "إضافة تعليق جديد لهذا المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/comments.phtml:26 +msgid "Add a new comment to this service" +msgstr "إضافة تعليق جديد لهذه الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/comments.phtml:7 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/comments.phtml:19 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:28 +msgid "Add comment" +msgid_plural "Add comments" +msgstr[0] "إضافة تعليق" +msgstr[1] "إضافة تعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:62 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:61 +msgid "Add comments" +msgstr "إضافة تعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:85 +msgid "Adding comment.." +msgid_plural "Adding comments.." +msgstr[0] "إضافة تعليق.." +msgstr[1] "إضافة تعليقات.." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:90 +msgid "Address" +msgstr "عنوان" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:241 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:54 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:58 +msgid "Alert Summary" +msgstr "ملخص التحذيرات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:11 +msgid "Alert summary" +msgstr "ملخص التحذيرات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contactgroups.phtml:24 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:326 +msgid "Alias" +msgstr "اسم مستعار" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:39 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:160 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:239 +msgid "All Hosts Enabled" +msgstr "كل المضيفون نشطاء" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:32 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php:26 +msgid "All Services" +msgstr "كل الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:94 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:194 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:273 +msgid "All Services Enabled" +msgstr "كل الخدمات مفعلة" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php:178 +msgid "All backends are disabled" +msgstr "كل الخلفيات متعطلة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:16 +msgid "Allow acknowledging host and service problems" +msgstr "السماح بالتعرف على مشاكل الخدمة والمضيف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:24 +msgid "Allow adding and deleting host and service comments" +msgstr "السماح بإضافة وحذف تعليقات الخدمة والمضيف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:8 +msgid "Allow all commands" +msgstr "السماح بكل الأوامر" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:28 +msgid "Allow commenting on hosts and services" +msgstr "السماح بالتعليق على الخدمات والمضيفين" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:32 +msgid "Allow deleting host and service comments" +msgstr "السماح بحذف تعليقات على المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:44 +msgid "Allow deleting host and service downtimes" +msgstr "السماح بحذف أزمنة توقف المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:60 +msgid "" +"Allow processing commands for toggling active checks on host and service " +"objects" +msgstr "" +"السماح بمعالجة الأوامر لتبديل الفحوصات النشطة على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:72 +msgid "" +"Allow processing commands for toggling event handlers on host and service " +"objects" +msgstr "" +"السماح بمعالجة الأوامر لتبديل الفحوصات النشطة على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:52 +msgid "" +"Allow processing commands for toggling features on an instance-wide basis" +msgstr "" +"السماح بمعالجة الأوامر لتبديل الفحوصات النشطة على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:56 +msgid "" +"Allow processing commands for toggling features on host and service objects" +msgstr "السماح بمعالجة الأوامر لتبديل المميزات على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:76 +msgid "" +"Allow processing commands for toggling flap detection on host and service " +"objects" +msgstr "السماح بمعالجة الأوامر لتبديل فحص التقلب على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:68 +msgid "" +"Allow processing commands for toggling notifications on host and service " +"objects" +msgstr "السماح بمعالجة الأوامر لتبديل التنبيهات على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:64 +msgid "" +"Allow processing commands for toggling passive checks on host and service " +"objects" +msgstr "" +"السماح بمعالجة الأوامر لتبديل الفحوصات السالبة على كائنات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:48 +msgid "Allow processing host and service check results" +msgstr "السماح بمعالجة نتائج فحص المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:20 +msgid "Allow removing problem acknowledgements" +msgstr "السماح بإزالة إشعارات المشكلة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:36 +msgid "Allow scheduling and deleting host and service downtimes" +msgstr "السماح بجدولة وحذف أزمنة توقف المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:12 +msgid "Allow scheduling host and service checks" +msgstr "السماح بجدولة فحوصات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:40 +msgid "Allow scheduling host and service downtimes" +msgstr "السماح بجدولة زمن توقف المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:80 +msgid "Allow sending custom notifications for hosts and services" +msgstr "السماح بإرسال تنبيهات مخصصة للأجهزة المضيفة والخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:20 +msgid "Apply" +msgstr "تطبيق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:39 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:42 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:249 +msgid "Author" +msgstr "المؤلف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:33 +msgid "Average" +msgstr "المتوسط" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:434 +msgid "Avg (min)" +msgstr "المتوسط (الأدنى)" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:74 +#, php-format +msgid "Backend %s is not running" +msgstr "الخلفية %s لا تعمل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/BackendPage.php:28 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:193 +msgid "Backend Name" +msgstr "اسم الخلفية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/BackendPage.php:44 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:221 +msgid "Backend Type" +msgstr "نوع الخلفية" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:94 +msgid "Backends" +msgstr "الخلفيات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:67 +msgid "Broadcast" +msgstr "بث" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:59 +msgid "COMMENT" +msgstr "تعليق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:63 +msgid "COMMENT DELETED" +msgstr "حذف التعليق" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Service.php:196 +msgid "CRITICAL" +msgstr "ضروري" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:58 +msgctxt "icinga.state" +msgid "CRITICAL" +msgstr "ضروري" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php:214 +msgid "Can't send external Icinga Command. Remote user is missing" +msgstr "لا يمكن إرسال أمر \"إسنجا\" خارجي. مستخدم بعيد مفقود" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Command/Transport/RemoteCommandFile.php:219 +msgid "" +"Can't send external Icinga Command. The private key for the remote user is " +"missing" +msgstr "" +"لا يمكن إرسال أمر \"إسنجا\" خارجي. المفتاح الخاص بالمستخدم البعيد مفقود" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Controller.php:97 +#, php-format +msgid "" +"Cannot apply restriction %s using the filter %s. You can only use the " +"following columns: %s" +msgstr "" +"لا يمكن تطبيق تقييد %s باستخدام المرشح %s. يمكنك استخدام الأعمدة التالية: %s" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php:76 +#, php-format +msgid "" +"Cannot create command transport \"%s\". Invalid transport defined in \"%s\". " +"Use one of \"%s\" or \"%s\"." +msgstr "" +"لا يمكن إنشاء ناقل أمر \"%s\". نقل غير صالح معرف في \"%s\". استخدم واحدا من \"" +"%s\" أو \"%s\"." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:350 +msgid "" +"Cannot find the IDO schema. Please verify that the given database contains " +"the schema and that the configured user has access to it." +msgstr "" +"تعذر العثور على مخطط IDO. الرجاء التأكد أن قاعدة البيانات المعطاة تحتوي على" +" المخطط وأن المستخدم المعد له صلاحية عليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:158 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:160 +msgid "Check Execution" +msgstr "فحص التنفيذ" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:113 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:140 +msgid "Check Performance" +msgstr "فحص الأداء" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checksource.phtml:4 +msgid "Check Source" +msgstr "فحص المصدر" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:51 +msgid "Check Time" +msgstr "فحص الزمن" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checktimeperiod.phtml:4 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checktimeperiod.phtml:15 +msgid "Check Timeperiod" +msgstr "فحص المدة الزمنية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:66 +msgid "Check attempts" +msgstr "فحص المحاولات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/detail-content.phtml:32 +msgid "Check execution" +msgstr "فحص التنفيذ" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:75 +msgid "Check execution time" +msgstr "فحص زمن التنفيذ" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:82 +msgid "Check latency" +msgstr "فحص التأخير" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php:42 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php:45 +msgid "Check now" +msgstr "الفحص الآن" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:12 +msgid "Check result is late" +msgstr "فحص كون النتيجة متأخرة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/LivestatusResourcePage.php:78 +msgid "" +"Check this to not to validate connectivity with the given Livestatus socket" +msgstr "حدد هذا الخيار لعدم التحقق من صحة الاتصال مع مقبس Livestatus معين" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:329 +msgid "Check this to not to validate the IDO schema of the chosen resource." +msgstr "حدد هذا الخيار لعدم التحقق من صحة المخطط IDO للمورد الذي تم اختياره." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:185 +msgid "Check this to not to validate the configuration" +msgstr "حدد هذا الخيار لعدم التحقق من الإعداد" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:114 +msgid "Checks" +msgstr "فحوصات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:45 +msgid "Child Hosts" +msgstr "مضيفون تابعون" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/SecurityConfigForm.php:58 +msgid "" +"Comma separated case insensitive list of protected custom variables. Use * " +"as a placeholder for zero or more wildcard characters. Existance of those " +"custom variables will be shown, but their values will be masked." +msgstr "" +"قائمة مفصولة بفواصل لمتغيرات مخصصة ومحمية غير حساسة لحالة الأحرف. " +"استخدم * كعلامة نائبة عن أي عدد من المحارف. وسيظهر ما إذا كانت تلك " +"المتغيرات المخصصة موجودة لكن قيمها ستحجب." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:163 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:165 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/command.phtml:12 +msgid "Command" +msgstr "أمر" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:177 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/LocalTransportForm.php:30 +msgid "Command File" +msgstr "أمر ملف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/TransportPage.php:14 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:43 +msgctxt "setup.page.title" +msgid "Command Transport" +msgstr "ناقل الأمر" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:62 +msgid "Command Transports" +msgstr "نواقل الأمر" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:240 +#, php-format +msgid "Command transport \"%s\" not found" +msgstr "ناقل الأمر \"%s\" غير موجود" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/CommandForm.php:65 +#, php-format +msgid "Command transport \"%s\" not found." +msgstr "ناقل الأمر \"%s\" غير موجود." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:189 +#, php-format +msgid "Command transport \"%s\" successfully removed" +msgstr "تم إزالة ناقل الأمر \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:229 +#, php-format +msgid "Command transport \"%s\" successfully updated" +msgstr "تم تحديث ناقل الأمر \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:98 +#, php-format +msgid "" +"Command transport configuration could not be written to: %s. An error " +"occured:" +msgstr "إعداد ناقل الأمر لا يمكن كتابته على: %s. حدث خطأ:" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:90 +#, php-format +msgid "Command transport configuration has been successfully created: %s" +msgstr "تم إنشاء إعداد ناقل الأمر: %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:268 +msgid "Command transport successfully created" +msgstr "تم إنشاء ناقل الأمر بنجاح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:163 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:70 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtimes/show.phtml:9 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:51 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comments/show.phtml:9 +msgid "Commands" +msgstr "أوامر" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:47 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:42 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:43 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:65 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:49 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentController.php:55 +msgid "Comment" +msgstr "تعليق" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:437 +msgid "Comment Timestamp" +msgstr "زمن التعليق" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:440 +msgid "Comment Type" +msgstr "نوع التعليق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:12 +msgid "Comment detail information" +msgstr "معلومات التعليق التفصيلية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentController.php:48 +msgid "Comment not found" +msgstr "تعليق غير موجود" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:16 +msgid "Comment was caused by a downtime" +msgstr "تعليق بسبب زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:6 +msgid "Comment was caused by a flapping host or service" +msgstr "تعليق بسبب خدمة أو مضيف مذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:21 +msgid "Comment was caused by an acknowledgement" +msgstr "تعليق بسبب إشعار" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:11 +msgid "Comment was created by an user" +msgstr "تعليق منشأ من قبل مستخدم" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:200 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:59 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:58 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/comments.phtml:37 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/EventOverviewForm.php:64 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentsController.php:62 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:60 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:410 +msgid "Comments" +msgstr "تعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php:196 +#, php-format +msgid "Configuration for backend %s is disabled" +msgstr "إعداد الخلفية %s معطل" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:98 +msgid "" +"Configure how to protect your monitoring environment against prying eyes" +msgstr "إعداد كيفية حماية بيئة المراقبة ضد أعين المتطفلين" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:93 +msgid "Configure how to retrieve monitoring information" +msgstr "إعداد كيفية استعادة معلومات المراقبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentsController.php:95 +#, php-format +msgid "Confirm removal of %d comments." +msgstr "تأكيد إزالة %d من التعليقات." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimesController.php:99 +#, php-format +msgid "Confirm removal of %d downtimes." +msgstr "إعداد إزالة %d من أزمنة التوقف." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contactgroups.phtml:23 +msgid "Contact Group " +msgstr "مجموعة الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:381 +msgid "Contact Groups" +msgstr "مجموعات الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:6 +msgid "Contact details" +msgstr "تفاصيل الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:397 +msgid "Contactgroup Alias" +msgstr "اسم مجموعة الاتصال المستعار" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:396 +msgid "Contactgroup Name" +msgstr "اسم مجموعة الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:196 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/contacts.phtml:35 +msgid "Contactgroups" +msgstr "مجموعات الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:192 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/contacts.phtml:17 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:307 +msgid "Contacts" +msgstr "جهات الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:292 +msgid "Contains host states of each service group." +msgstr "يحتوي على حالات المضيف لكل مجموعة خدمة." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:233 +msgid "Contains service states for each service group." +msgstr "يحتوي حالات الخدمة لكل مجموعة خدمة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:47 +msgid "Could not find any valid SSH resources" +msgstr "تعذر العثور على موارد SSH صالحة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:65 +msgid "" +"Could not find any valid monitoring backend resources. Please configure a " +"database resource first." +msgstr "" +"تعذر العثور على موارد مراقبة خلفية صالحة. الرجاء إعداد " +"مورد لقاعدة البيانات أولا." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:254 +msgid "Create New Command Transport" +msgstr "إنشاء ناقل أمر جديد" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:93 +msgid "Create New Monitoring Backend" +msgstr "إنشاء خلفية مراقبة جديدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:64 +msgid "Create a New Command Transport" +msgstr "إنشاء ناقل أمر جديد" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:9 +msgid "Create a New Monitoring Backend" +msgstr "إنشاء خلفية مراقبة جديدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:70 +msgid "Create a new command transport" +msgstr "إنشاء ناقل أمر جديد" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:15 +msgid "Create a new monitoring backend" +msgstr "إنشاء خلفية مراقبة جديدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:52 +msgid "Created" +msgstr "أُنشيء" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:41 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:117 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:257 +msgid "Critical" +msgstr "ضروري" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:160 +msgid "Current Downtimes" +msgstr "أزمنة التوقف الحالية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:174 +msgid "Current Host State" +msgstr "حالة المضيفين الحالية" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:258 +msgid "Current Incidents" +msgstr "الحوادث الحالية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:169 +msgid "Current Service State" +msgstr "حالة الخدمة الحالية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:88 +msgid "Current State" +msgstr "الحالة الحالية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/detail-content.phtml:43 +msgid "Custom Variables" +msgstr "متغيرات مخصصة" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Host.php:185 +msgid "DOWN" +msgstr "متوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:124 +msgctxt "icinga.state" +msgid "DOWN" +msgstr "متوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:83 +msgid "DOWNTIME DELETED" +msgstr "حُذف زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:108 +msgid "DOWNTIME END" +msgstr "نهاية زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:103 +msgid "DOWNTIME START" +msgstr "بداية زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:105 +msgid "Database Name" +msgstr "اسم قاعدة البيانات" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:84 +msgid "Database Resource" +msgstr "مورد قاعدة بيانات" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:93 +msgid "Database Type" +msgstr "نوع قاعدة البيانات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:42 +msgid "Date and time this downtime was entered" +msgstr "تاريخ ووقت إدخال زمن التوقف هذا" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:642 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:645 +msgid "Day" +msgstr "يوم" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:463 +msgid "Defect Chart" +msgstr "رسم بياني للعيوب" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:484 +msgid "Defects" +msgstr "العيوب" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:43 +msgid "Define what should be done with the child hosts of the hosts." +msgstr "عرف ما يتحتم عمله مع المضيفين التابعين." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php:76 +msgid "Delete this comment" +msgstr "احذف هذا التعليق" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php:40 +msgid "Delete this downtime" +msgstr "حذف زمن التوقف هذا" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php:96 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php:83 +msgid "Deleting comment.." +msgid_plural "Deleting comments.." +msgstr[0] "حذف تعليق.." +msgstr[1] "حذف تعليقات.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php:97 +msgid "Deleting downtime." +msgstr "حذف زمن التوقف." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php:83 +msgid "Deleting downtime.." +msgid_plural "Deleting downtimes.." +msgstr[0] "حذف زمن التوقف.." +msgstr[1] "حذف أزمنة التوقف.." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:10 +msgid "Details" +msgstr "تفاصيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php:24 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HealthController.php:172 +msgid "Disable Notifications" +msgstr "تعطيل التنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:185 +msgid "Disable This Backend" +msgstr "تعطيل هذه الخلفية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:69 +msgid "Disable notifications for a specific time on a program-wide basis" +msgstr "تعطيل التنبيهات لوقت محدد على أساس البرنامج بأكمله" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:71 +msgid "Disable temporarily" +msgstr "تعطيل مؤقت" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:327 +msgid "Disabled Host Checks" +msgstr "تعطيل فحوصات المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:319 +msgid "Disabled Host Notifications" +msgstr "تنبيهات المضيف المعطلة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:323 +msgid "Disabled Service Checks" +msgstr "فحوصات الخدمة المعطلة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:315 +msgid "Disabled Service Notifications" +msgstr "تنبيهات الخدمة المعطلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:130 +msgid "Disabling active checks.." +msgstr "تعطيل الفحوصات النشطة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:222 +msgid "Disabling active host checks.." +msgstr "تعطيل فحوصات المضيف النشطة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:226 +msgid "Disabling active service checks.." +msgstr "تعطيل فحوصات الخدمة النشطة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:146 +msgid "Disabling event handler.." +msgstr "تعطيل معالج الحدث.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:230 +msgid "Disabling event handlers.." +msgstr "تعطيل معالجات الحدث.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:150 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:234 +msgid "Disabling flap detection.." +msgstr "تعطيل فحص التذبذب.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php:61 +msgid "Disabling host and service notifications.." +msgstr "تعطيل تنبيهات المضيف والخدمة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:142 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:238 +msgid "Disabling notifications.." +msgstr "تعطيل التنبيهات.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:242 +msgid "Disabling obsessing over hosts.." +msgstr "تعطيل التوجس على المضيفين.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:246 +msgid "Disabling obsessing over services.." +msgstr "تعطيل التوجس على الخدمات.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:138 +msgid "Disabling obsessing.." +msgstr "تعطيل التوجس.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:134 +msgid "Disabling passive checks.." +msgstr "تعطيل الفحوصات السلبية.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:250 +msgid "Disabling passive host checks.." +msgstr "تعطيل فحوصات المضيف السلبية.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:254 +msgid "Disabling passive service checks.." +msgstr "تعطيل فحوصات الخدمة السلبية.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:258 +msgid "Disabling performance data.." +msgstr "تعطيل بيانات الأداء.." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentController.php:56 +msgid "Display detailed information about a comment." +msgstr "عرض معلومات مفصلة عن تعليق." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimeController.php:66 +msgid "Display detailed information about a downtime." +msgstr "عرض معلومات مفصلة عن توقف." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentsController.php:64 +msgid "Display detailed information about multiple comments." +msgstr "عرض معلومات تفصيلية عن تعليقات متعددة." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimesController.php:70 +msgid "Display detailed information about multiple downtimes." +msgstr "عرض معلومات تفصيلية عن توقفات متعددة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Navigation/ActionForm.php:26 +msgid "" +"Display this action only for objects matching this filter. Leave it blank if " +"you want this action being displayed regardless of the object" +msgstr "" +"عرض هذا الفعل فقط للكائنات الموافقة لهذا المرشح. اتركه فارغا " +"إذا كنت تريد لهذا الفعل أن يعرض بغض النظر عن الكائن" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:47 +msgid "Do nothing with child hosts" +msgstr "لا تفعل شيئا بالمضيفين التابعين" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:138 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:307 +msgid "Down" +msgstr "متوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:15 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimeController.php:65 +msgid "Downtime" +msgstr "زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimeController.php:57 +msgid "Downtime not found" +msgstr "زمن التوقف غير موجود" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:204 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:96 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:95 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:41 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/EventOverviewForm.php:54 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimesController.php:69 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:212 +msgid "Downtimes" +msgstr "أزمنة التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:306 +msgid "Downtimes Active For More Than Three Days" +msgstr "أزمنة التوقف النشطة لأكثر من ثلاثة أيام" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:53 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:135 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:254 +msgid "Duration" +msgstr "المدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:3 +msgctxt "Downtime status" +msgid "ENDS" +msgstr "نهايات" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:155 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:168 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:80 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:102 +#, php-format +msgid "ERROR: %s" +msgstr "خطأ: %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:6 +msgctxt "Downtime status" +msgid "EXPIRES" +msgstr "انتهاء صلاحية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:210 +#, php-format +msgid "Edit Command Transport %s" +msgstr "تحرير ناقل الأمر %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:51 +#, php-format +msgid "Edit Monitoring Backend %s" +msgstr "تحرير خلفية المراقبة %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:90 +#, php-format +msgid "Edit command transport %s" +msgstr "تحرير ناقل الأمر %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:35 +#, php-format +msgid "Edit monitoring backend %s" +msgstr "تحرير خلفية المراقبة %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:24 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:21 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:42 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:327 +msgid "Email" +msgstr "بريد إلكتروني" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:129 +msgid "Enabling active checks.." +msgstr "تفعيل فحوصات نشطة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:221 +msgid "Enabling active host checks.." +msgstr "تفعيل فحوصات مضيف نشطة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:225 +msgid "Enabling active service checks.." +msgstr "تفعيل فحوصات الخدمة النشطة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:145 +msgid "Enabling event handler.." +msgstr "تفعيل معالج حدث.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:229 +msgid "Enabling event handlers.." +msgstr "تفعيل معالجات حدث.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:149 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:233 +msgid "Enabling flap detection.." +msgstr "تفعيل كشف التذبذب.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:141 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:237 +msgid "Enabling notifications.." +msgstr "تفعيل التنبيهات.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:241 +msgid "Enabling obsessing over hosts.." +msgstr "تعطيل التوجس على المضيفين.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:245 +msgid "Enabling obsessing over services.." +msgstr "تعطيل التوجس على الخدمات.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:137 +msgid "Enabling obsessing.." +msgstr "تعطيل التوجس.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:133 +msgid "Enabling passive checks.." +msgstr "تفعيل فحوصات سلبية.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:249 +msgid "Enabling passive host checks.." +msgstr "تفعيل فحوصات مضيف سلبية.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:253 +msgid "Enabling passive service checks.." +msgstr "تفعيل فحوصات الخدمة السلبية.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:257 +msgid "Enabling performance data.." +msgstr "تفعيل بيانات الأداء.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:88 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:251 +msgid "End Time" +msgstr "زمن الانتهاء" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:75 +msgid "Ended downtimes" +msgstr "أزمنة التوقف المنتهية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:159 +msgid "" +"Enter here the duration of the downtime. The downtime will be automatically " +"deleted after this time expired." +msgstr "" +"أدخل وقت التوقف هنا. سيتم حذف زمن التوقف تلقائيا " +"بعد انقضاء هذا الوقت." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:90 +msgid "" +"Enter the expire date and time for this acknowledgement here. Icinga will " +"delete the acknowledgement after this time expired." +msgstr "" +"أدخل تاريخ الانتهاء ووقت هذا الإشعار هنا. \"إسنجا\" سيحذف " +"هذا الإشعار بعد انتهاء هذا الوقت." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:43 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:248 +msgid "Entry Time" +msgstr "زمن الدخول" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:216 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:336 +msgid "Event Grid" +msgstr "شبكة الحدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:54 +msgid "Event Handler" +msgstr "معالج الحدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:208 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:210 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:108 +msgid "Event Handlers" +msgstr "معالجات الحدث" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:220 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:546 +msgid "Event Overview" +msgstr "نظرة عامة على الحدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:116 +msgid "Execution time" +msgstr "وقت التنفيذ" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:441 +msgid "Expiration" +msgstr "انتهاء الصلاحية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:87 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php:43 +msgid "Expire Time" +msgstr "وقت انتهاء الصلاحية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:57 +msgid "Expires" +msgstr "انتهاء صلاحية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:27 +#, php-format +msgid "Expires %s" +msgstr "انتهاء صلاحية %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:87 +msgid "FLAPPING" +msgstr "تذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:91 +msgid "FLAPPING STOPPED" +msgstr "توقف التذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php:138 +msgid "" +"Failed to send external Icinga command. No transport has been configured for " +"this instance. Please contact your Icinga Web administrator." +msgstr "" +"فشل في إرسال أمر \"إسنجا\" الخارجي. لم يتم إعداد نقل " +"لهذه الحالة. يرجى الاتصال بمدير الوب الخاص بإسنجا" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:143 +#, php-format +msgid "Failed to successfully validate the configuration: %s" +msgstr "فشل التحقق من صحة الإعداد بنجاح: %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:79 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:200 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:202 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/flags.phtml:2 +msgid "Feature Commands" +msgstr "أوامر ميزة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Navigation/ActionForm.php:24 +msgid "Filter" +msgstr "مرشح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:60 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:107 +msgid "Fixed" +msgstr "ثابت" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:66 +msgid "Fixed downtimes have a static start and end time." +msgstr "أزمنة التوقف الثابتة لها بداية ونهاية ثابتة." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:8 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:10 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:58 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:117 +msgid "Flap Detection" +msgstr "كشف التذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:5 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:19 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:15 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/EventOverviewForm.php:84 +msgid "Flapping" +msgstr "التذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:60 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:108 +msgid "Flexible" +msgstr "مرن" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:157 +msgid "Flexible Duration" +msgstr "مدة مرنة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:63 +msgid "" +"Flexible downtimes have a hard start and end time, but also an additional " +"restriction on the duration in which the host or service may actually be " +"down." +msgstr "" +"أزمنة التوقف المرنة لديها زمن بداية ونهاية صعب، ولكن أيضا لها تقييد إضافي " +"على المدة التي يكون فيها المضيف أو الخدمة متوقفا." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:62 +msgid "Force Check" +msgstr "فحص مفروض" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:55 +msgid "Forced" +msgstr "مفروض" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:66 +msgid "From" +msgstr "من" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:48 +msgid "Global Host Event Handler" +msgstr "معالج حدث المضيف العالمي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:42 +msgid "Global Service Event Handler" +msgstr "معالج حدث الخدمة العالمي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:31 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:130 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:229 +msgid "Handled" +msgstr "مُعالج" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:55 +msgid "Hard state changes" +msgstr "تغييرات حالة صعبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:325 +msgid "Healing Chart" +msgstr "رسم الصحة البياني" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:89 +msgid "Hide the properties of monitored objects that match the filter" +msgstr "إخفاء خصائص الكائنات المراقبة التي توافق المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:212 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:69 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:229 +msgid "History" +msgstr "المحفوظات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:15 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:30 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:29 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:31 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml:20 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/notifications.phtml:53 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:139 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:246 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:438 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:97 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:185 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/DataView/DataView.php:275 +msgid "Host" +msgstr "مضيف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:115 +msgid "Host Action" +msgstr "فعل المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:176 +msgid "Host Address" +msgstr "عنوان المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:61 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:121 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:148 +msgid "Host Checks" +msgstr "فحوصات المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:25 +msgid "Host Group" +msgstr "مجموعة المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:291 +msgid "Host Group Chart" +msgstr "رسم بياني لمجموعة المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:534 +msgid "Host Group Name" +msgstr "اسم مجموعة المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:501 +msgid "Host Groups" +msgstr "مجموعات المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:132 +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:268 +msgid "Host Problems" +msgstr "مشاكل المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:35 +msgid "Host Problems:" +msgstr "مشاكل المضيف:" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:173 +msgid "Host Severity" +msgstr "خطورة المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:26 +msgid "Host States" +msgstr "حالات المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:3 +msgid "Host and Service Checks" +msgstr "فحوصات المضيف والخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/disable-notifications.phtml:10 +msgid "Host and service notifications are already disabled." +msgstr "تنبيهات المضيف والخدمة تم تعطيلها مسبقا." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:33 +msgid "Host not found" +msgstr "المضيف غير موجود" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:109 +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:188 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/hostgroups.phtml:16 +msgid "Hostgroups" +msgstr "مجموعات المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:89 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:175 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:598 +msgid "Hostname" +msgstr "اسم المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:141 +msgid "Hostname or address of the remote Icinga instance" +msgstr "اسم المضيف وعنوان حالة \"إسنجا\" البعيدة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:107 +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:176 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:9 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:39 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:100 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:295 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:59 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:48 +msgid "Hosts" +msgstr "المضيفون" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:639 +msgid "Hour" +msgstr "ساعة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:137 +msgid "Hours" +msgstr "ساعات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/service-header.phtml:30 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/host-header.phtml:29 +msgid "IPv4 address" +msgstr "عنوان Ipv4" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/service-header.phtml:25 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/host-header.phtml:24 +msgid "IPv6 address" +msgstr "عنوان IPv6" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:45 +msgid "" +"Icinga Web 2 will protect your monitoring environment against prying eyes " +"using the configuration specified below:" +msgstr "" +"واجهة وب إسنجا 2 ستحمي بيئة المراقبة الخاصة بك ضد أعين " +"المتطفلين باستخدام الإعداد المحدد أدناه:" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:77 +#, php-format +msgid "" +"Icinga Web 2 will retrieve information from your monitoring environment " +"using a backend called \"%s\" and the specified resource below:" +msgstr "" +"واجهة وب إسنجا 2 ستسترد المعلومات من بيئة المراقبة الخاصة بك " +"باستخدام الخلفية المسماة \"%s\" والمورد المحدد أدناه:" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:76 +#, php-format +msgid "" +"Icinga Web 2 will use the named pipe located at \"%s\" to send commands to " +"your monitoring instance." +msgstr "" +"واجهة وب إسنجا 2 ستستخدم الأنبوب المسمى الواقع على \"%s\" لإرسال أوامر " +"إلى مثال المراقبة الخاصة بك." + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:49 +#, php-format +msgid "" +"Icinga Web 2 will use the named pipe located on a remote machine at \"%s\" " +"to send commands to your monitoring instance by using the connection details " +"listed below:" +msgstr "" +"واجهة وب إسنجا 2 ستستخدم \"الأنبوب المسمى\" الواقع على الجهاز البعيد \"%s\"" +" لإرسال أوامر " +"إلى مثال المراقبة الخاصة بك باستخدام تفاصيل الاتصال المدونة أدناه:" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:74 +msgid "If the acknowledgement should expire, check this option." +msgstr "إذا كان الإشعار ستنتهي صلاحيته فحدد هذا الخيار." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:58 +msgid "" +"If you check this option, the notification is sent out regardless of time " +"restrictions and whether or not notifications are enabled." +msgstr "" +"إذا قمت بتحديد هذا الخيار، فإن التنبيه سيرسل بغض النظر عن تقييدات " +"الوقت وكون التنبيهات مفعلة أم لا." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:70 +msgid "" +"If you check this option, the notification is sent out to all normal and " +"escalated contacts." +msgstr "" +"إذا قمت بتحديد هذا الخيار، فإن التنبيه سيرسل لكل جهات " +"الاتصال العادية والمصعّدة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:126 +msgid "" +"If you do not want an acknowledgement notification to be sent out to the " +"appropriate contacts, uncheck this option." +msgstr "" +"إذا كنت تريد لتنبيه الإشعار أن يرسل لجهة الاتصال المناسبة " +"فقم بإلغاء هذا الخيار." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:101 +msgid "" +"If you select the fixed option, the downtime will be in effect between the " +"start and end times you specify whereas a flexible downtime starts when the " +"host or service enters a problem state sometime between the start and end " +"times you specified and lasts as long as the duration time you enter. The " +"duration fields do not apply for fixed downtimes." +msgstr "" +"إذا قمت بتحديد الخيار الثابت، فإن زمن التوقف يكون ساري المفعول بين" +"أزمنة البداية والنهاية التي تحددها في حين يبدأ التوقف المرن عندما يدخل" +"مضيف أو خدمة إلى حالة مشكلة في وقت ما بين زمن البداية والنهاية " +"الذين حددتهما ويستمر طيلة المدة الزمنية التي تدخلها. حقول الزمن " +"لا تنطبق على أزمنة التوقف الثابتة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:64 +msgid "" +"If you select this option, Icinga will force a check regardless of both what " +"time the scheduled check occurs and whether or not checks are enabled." +msgstr "" +"إذا قمت بتحديد هذا الخيار، فإن إسنجا ستفرض اختبارا بغض النظر عن" +"زمن حدوث الاختبار المجدول وعما إذا كانت الاختبارات مفعلة." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:175 +msgid "" +"If you still have any environments or views referring to this transport, you " +"won't be able to send commands anymore after deletion." +msgstr "" +"إذا كان لديك أي بيئات أو عروض تشير إلى هذا النقل، فلن " +"لن تكون قادرا على إرسال الأوامر مرة أخرى بعد الحذف." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:58 +msgid "" +"If you uncheck this option, the comment will automatically be deleted the " +"next time Icinga is restarted." +msgstr "" +"إذا قمت بإلغاء تحديد هذا الخيار، سيتم تلقائيا حذف التعليق" +"في المرة القادمة التي سيعاد فيها تشغيل إسنجا." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:114 +msgid "" +"If you want the acknowledgement to remain until the host or service recovers " +"even if the host or service changes state, check this option." +msgstr "" +"ذا كنت ترغب في بقاء الإشعار حتى يتعافى المضيف أو الخدمة" +"حتى إذا تغيرت حالة المضيف أو الخدمة، فاختر هذا الخيار." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:44 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:45 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:67 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:51 +msgid "" +"If you work with other administrators, you may find it useful to share " +"information about the the host or service that is having problems. Make sure " +"you enter a brief description of what you are doing." +msgstr "" +"إذا كنت تعمل مع المسؤولين الآخرين، قد تجد أنه من المفيد تبادل " +"المعلومات حول المضيف أو الخدمة التي تواجه مشاكل. تأكد " +"من إدخال وصفا موجزا لما تقوم به." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:63 +msgid "" +"If you would like the comment to remain even when the acknowledgement is " +"removed, check this option." +msgstr "" +"إذا كنت ترغب في بقاء التعليق حتى عندما يُزال الإقرار " +"فاختر هذا الخيار." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml:17 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml:17 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:25 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:21 +msgid "In Downtime" +msgstr "في زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:151 +msgid "In effect" +msgstr "ساري المفعول" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:131 +msgid "" +"Indicates the number of seconds that the scheduled downtime should last. " +"This is usually only needed if this is a flexible downtime, which can start " +"at a variable time, but lasts for the specified duration" +msgstr "" +"يشير إلى عدد الثواني التي يستمرها زمن التوقف المجدول. عادة " +"ما تكون هناك حاجة إليها إذا كان هذا هو وقت التوقف المرن، " +"الذي يمكن أن يبدأ في وقت متغير، ولكن يستمر للمدة المحددة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/command.phtml:7 +msgid "Instance" +msgstr "حالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:203 +msgid "Instance Link" +msgstr "رابط الحالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:84 +#, php-format +msgid "Invalid command transport type \"%s\" given" +msgstr "نوع ناقل الأمر المعطى \"%s\" غير صالح " + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Navigation/ActionForm.php:61 +#, php-format +msgid "Invalid filter provided. You can only use the following columns: %s" +msgstr "تم تزويد مرشح غير صالح. تستطيع استخدام الأعمدة التالية فقط: %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:245 +msgid "Is In Effect" +msgstr "ساري المفعول" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checksource.phtml:11 +msgid "Is reachable" +msgstr "يمكن الوصول إليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/WelcomePage.php:43 +msgid "" +"It offers various status and reporting views with powerful filter " +"capabilities that allow you to keep track of the most important events in " +"your monitoring environment." +msgstr "" +"ويقدم الوضع عروض مختلفة للحالة والتقارير مع قدرات تصفية قوية " +"تسمح لك بتتبع أهم الأحداث في بيئة المراقبة الخاصة بك." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:36 +msgid "Label" +msgstr "عنوان" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:91 +msgid "Last Check" +msgstr "آخر فحص" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:32 +msgid "Last External Command Check" +msgstr "آخر فحص أمر خارجي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:177 +msgid "Last Host Check" +msgstr "آخر فحص لمضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:36 +msgid "Last Log File Rotation" +msgstr "آخر تدوير لملف السحل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:171 +msgid "Last Service Check" +msgstr "آخر فحص لخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:92 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:172 +msgid "Last State Change" +msgstr "آخر تغيير حالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:28 +msgid "Last Status Update" +msgstr "آخر تحديث للحالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:7 +msgid "Last check" +msgstr "آخر فحص" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:7 +msgid "Last update" +msgstr "آخر تحديث" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:294 +msgid "Late Host Check Results" +msgstr "آخر نتائج لفحص المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:298 +msgid "Late Service Check Results" +msgstr "آخر نتائج لفحص الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:115 +msgid "Latency" +msgstr "تأخير" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:15 +msgid "Legend" +msgstr "الدليل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notes.phtml:23 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notes.phtml:24 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/actions.phtml:17 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/actions.phtml:18 +msgid "Link opens in new window" +msgstr "يفتح الرابط في نافذة جديدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hosts.phtml:75 +#, php-format +msgid "List %s unhandled service problem on host %s" +msgid_plural "List %s unhandled service problems on host %s" +msgstr[0] "سرد مشكلة الخدمة غير المعالجة %s على المضيف %s" +msgstr[1] "سرد مشاكل الخدمة غير المعالجة %s على المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:112 +#, php-format +msgctxt "timeline.link.title" +msgid "List %u %s registered %s" +msgstr "سرد %u %s مسجلة %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:26 +#, php-format +msgid "List %u actively checked host" +msgid_plural "List %u actively checked hosts" +msgstr[0] "سرد %u مضيف مفحوص بطريقة نشطة" +msgstr[1] "سرد %u من المضيفين المفحوصين بطريقة نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:82 +#, php-format +msgid "List %u actively checked service" +msgid_plural "List %u actively checked services" +msgstr[0] "سرد الخدمة %u المفحوصة بطريقة نشطة" +msgstr[1] "سرد الخدمات %u المفحوصة بطريقة نشطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/components/hostssummary.phtml:17 +#, php-format +msgid "List %u host" +msgid_plural "List all %u hosts" +msgstr[0] "سرد %u مضيف" +msgstr[1] "سرد %u من المضيفين" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:29 +#, php-format +msgid "List %u host for which flap detection has been disabled" +msgid_plural "List %u hosts for which flap detection has been disabled" +msgstr[0] "سرد %u مضيف عطل فيه كشف التذبذب" +msgstr[1] "سرد %u من المضيفين الذين عطل فيهم كشف التذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:150 +#, php-format +msgid "List %u host for which notifications are suppressed" +msgid_plural "List %u hosts for which notifications are suppressed" +msgstr[0] "سرد %u مضيف حيث أُخمدت التنبيهات" +msgstr[1] "سرد %u من المضيفين حيث أُخمدت التنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:59 +#, php-format +msgid "List %u host that is currently flapping" +msgid_plural "List %u hosts which are currently flapping" +msgstr[0] "سرد %u مضيف يتذبذب حاليا" +msgstr[1] "سرد %u مضيف يتذبذب حاليا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/problem_hosts.phtml:18 +#, php-format +msgid "List %u host that is currently in state DOWN" +msgid_plural "List %u hosts which are currently in state DOWN" +msgstr[0] "سرد %u مضيف حاليا في حالة \"متوقف\"" +msgstr[1] "سرد %u من المضيفين حاليا في حالة \"متوقف\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/ok_hosts.phtml:41 +#, php-format +msgid "List %u host that is currently in state PENDING" +msgid_plural "List %u hosts which are currently in state PENDING" +msgstr[0] "سرد %u مضيف حاليا في حالة \"معلق\"" +msgstr[1] "سرد %u من المضيفين حاليا في حالة \"معلق\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/problem_hosts.phtml:40 +#, php-format +msgid "List %u host that is currently in state UNREACHABLE" +msgid_plural "List %u hosts which are currently in state UNREACHABLE" +msgstr[0] "سرد %u مضيف حاليا في حالة \"لا يمكن الوصول\"" +msgstr[1] "سرد %u من المضيفين حاليا في حالة \"لا يمكن الوصول\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/ok_hosts.phtml:23 +#, php-format +msgid "List %u host that is currently in state UP" +msgid_plural "List %u hosts which are currently in state UP" +msgstr[0] "سرد %u مضيف حاليا في حالة \"سليم\"" +msgstr[1] "سرد %u من المضيفين حاليا في حالة \"سليمة\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:62 +#, php-format +msgid "List %u host that is not being checked at all" +msgid_plural "List %u hosts which are not being checked at all" +msgstr[0] "سرد %u مضيف لم يتم فحصه مطلقا" +msgstr[1] "سرد %u من المضيفين لم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:229 +#, php-format +msgid "List %u host that is not processing any event handlers" +msgid_plural "List %u hosts which are not processing any event handlers" +msgstr[0] "سرد %u مضيف لا تقوم بمعالجة أي معالجات حدث" +msgstr[1] "سرد %u من المضيفين لا تقوم بمعالجة أي معالجات حدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:44 +#, php-format +msgid "List %u passively checked host" +msgid_plural "List %u passively checked hosts" +msgstr[0] "سرد %u مضيف مفحوص بطريقة سالبة" +msgstr[1] "سرد %u من المضيفين المفحوصين بطريقة سالبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:100 +#, php-format +msgid "List %u passively checked service" +msgid_plural "List %u passively checked services" +msgstr[0] "سرد %u خدمة مفحوصة بطريقة سالبة" +msgstr[1] "سرد %u من الخدمات المفحوصة بطريقة سالبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/components/servicesummary.phtml:20 +#, php-format +msgid "List %u service" +msgid_plural "List all %u services" +msgstr[0] "سرد %u خدمة" +msgstr[1] "سرد %u من الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:84 +#, php-format +msgid "List %u service for which flap detection has been disabled" +msgid_plural "List %u services for which flap detection has been disabled" +msgstr[0] "سرد %u خدمة عطل فيه كشف التذبذب" +msgstr[1] "سرد %u من الخدمات التي عطل فيها كشف التذبذب" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:184 +#, php-format +msgid "List %u service for which notifications are suppressed" +msgid_plural "List %u services for which notifications are suppressed" +msgstr[0] "سرد %u خدمة حيث أُخمدت التنبيهات" +msgstr[1] "سرد %u من الخدمات حيث أُخمدت التنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:114 +#, php-format +msgid "List %u service that is currently flapping" +msgid_plural "List %u services which are currently flapping" +msgstr[0] "سرد %u خدمة مذبذبة حاليا" +msgstr[1] "سرد %u من الخدمات مذبذبة حاليا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:20 +#, php-format +msgid "List %u service that is currently in state CRITICAL" +msgid_plural "List %u services which are currently in state CRITICAL" +msgstr[0] "سرد %u خدمة حاليا في حالة \"ضرورية\"" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"ضرورية\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:41 +#, php-format +msgid "List %u service that is currently in state CRITICAL (Handled)" +msgid_plural "List %u services which are currently in state CRITICAL (Handled)" +msgstr[0] "سرد %u خدمة حاليا في حالة \"ضرورية\" (مُعالجة)" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"ضرورية\" (مُعالجة)" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:95 +#, php-format +msgid "" +"List %u service that is currently in state CRITICAL and not checked at all" +msgid_plural "" +"List %u services which are currently in state CRITICAL and not checked at all" +msgstr[0] "سرد %u خدمة حاليا في حالة \"ضرورية\" ولم يتم فحصها مطلقا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"ضرورية\" ولم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:68 +#, php-format +msgid "" +"List %u service that is currently in state CRITICAL and passively checked" +msgid_plural "" +"List %u services which are currently in state CRITICAL and passively checked" +msgstr[0] "سرد %u خدمة حاليا في حالة \"ضرورية\" وتم فحصها سلبيا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"ضرورية\" وتم فحصها سلبيا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:314 +#, php-format +msgid "List %u service that is currently in state OK" +msgid_plural "List %u services which are currently in state OK" +msgstr[0] "سرد %u خدمة حاليا في حالة \"سليمة\"" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"سليمة\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:340 +#, php-format +msgid "List %u service that is currently in state OK and not checked at all" +msgid_plural "" +"List %u services which are currently in state OK and not checked at all" +msgstr[0] "سرد %u خدمة حاليا في حالة \"سليمة\" ولم يتم فحصها مطلقا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"سليمة\" ولم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:361 +#, php-format +msgid "List %u service that is currently in state PENDING" +msgid_plural "List %u services which are currently in state PENDING" +msgstr[0] "سرد %u خدمة حاليا في حالة \"معلقة\"" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"معلقة\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:387 +#, php-format +msgid "" +"List %u service that is currently in state PENDING and not checked at all" +msgid_plural "" +"List %u services which are currently in state PENDING and not checked at all" +msgstr[0] "سرد %u خدمة حاليا في حالة \"معلقة\" ولم يتم فحصها مطلقا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"معلقة\" ولم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:218 +#, php-format +msgid "List %u service that is currently in state UNKNOWN" +msgid_plural "List %u services which are currently in state UNKNOWN" +msgstr[0] "سرد %u خدمة حاليا في حالة \"مجهولة\"" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"مجهولة\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:239 +#, php-format +msgid "List %u service that is currently in state UNKNOWN (Handled)" +msgid_plural "List %u services which are currently in state UNKNOWN (Handled)" +msgstr[0] "سرد %u خدمة حاليا في حالة \"مجهولة\" (معالجة)" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"مجهولة\" (مُعالجة)" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:293 +#, php-format +msgid "" +"List %u service that is currently in state UNKNOWN and not checked at all" +msgid_plural "" +"List %u services which are currently in state UNKNOWN and not checked at all" +msgstr[0] "سرد %u خدمة حاليا في حالة \"مجهولة\" ولم يتم فحصها مطلقا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"مجهولة\" ولم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:266 +#, php-format +msgid "" +"List %u service that is currently in state UNKNOWN and passively checked" +msgid_plural "" +"List %u services which are currently in state UNKNOWN and passively checked" +msgstr[0] "سرد %u خدمة حاليا في حالة \"مجهولة\" وتم فحصها سلبيا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"مجهولة\" وتم فحصها سلبيا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:119 +#, php-format +msgid "List %u service that is currently in state WARNING" +msgid_plural "List %u services which are currently in state WARNING" +msgstr[0] "سرد %u خدمة حاليا في حالة \"تحذير\"" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"تحذير\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:140 +#, php-format +msgid "List %u service that is currently in state WARNING (Handled)" +msgid_plural "List %u services which are currently in state WARNING (Handled)" +msgstr[0] "سرد %u خدمة حاليا في حالة \"تحذير\" (مُعالجة)" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"تحذير\" (مُعالجة)" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:194 +#, php-format +msgid "" +"List %u service that is currently in state WARNING and not checked at all" +msgid_plural "" +"List %u services which are currently in state WARNING and not checked at all" +msgstr[0] "سرد %u خدمة حاليا في حالة \"تحذير\" ولم يتم فحصها مطلقا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"تحذير\" ولم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/parts/servicestatesummarybyhoststate.phtml:167 +#, php-format +msgid "" +"List %u service that is currently in state WARNING and passively checked" +msgid_plural "" +"List %u services which are currently in state WARNING and passively checked" +msgstr[0] "سرد %u خدمة حاليا في حالة \"تحذير\" وتم فحصها سلبيا" +msgstr[1] "سرد %u من الخدمات حاليا في حالة \"تحذير\" وتم فحصها سلبيا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:118 +#, php-format +msgid "List %u service that is not being checked at all" +msgid_plural "List %u services which are not being checked at all" +msgstr[0] "سرد %u خدمة لم يتم فحصها مطلقا" +msgstr[1] "سرد %u من الخدمات التي لم يتم فحصها مطلقا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:263 +#, php-format +msgid "List %u service that is not processing any event handlers" +msgid_plural "List %u services which are not processing any event handlers" +msgstr[0] "سرد %u خدمة لا تقوم بمعالجة أي معالجات حدث" +msgstr[1] "سرد %u من الخدمات التي لا تقوم بمعالجة أي معالجات حدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comments-header.phtml:23 +#, php-format +msgid "List all %d comments" +msgstr "سرد جميع التعليقات %d" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtimes-header.phtml:31 +#, php-format +msgid "List all %d downtimes" +msgstr "سرد جميع أزمنة التوقف %d" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/objects-header.phtml:32 +#, php-format +msgid "List all %d hosts" +msgstr "سرد جميع المضيفين %d" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/objects-header.phtml:36 +#, php-format +msgid "List all %d services" +msgstr "سرد جميع الخدمات %d" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:74 +#, php-format +msgctxt "timeline.link.title" +msgid "List all event records registered %s" +msgstr "سرد جميع سجلات الأحداث المسجلة %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/hostgroups.phtml:11 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:43 +#, php-format +msgid "List all hosts in the group \"%s\"" +msgstr "سرد جميع المضيفين في مجموعة \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:43 +msgid "List all hosts, for which flap detection is enabled entirely" +msgstr "سرد جميع المضيفين الذين فُعل لهم كشف التذبذب بصورة كاملة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:164 +msgid "List all hosts, for which notifications are enabled entirely" +msgstr "سرد جميع المضيفين الذين فُعلت لهم التنبيهات بصورة كاملة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:243 +msgid "List all hosts, which are processing event handlers entirely" +msgstr "سرد جميع المضيفين الذين يقومون بمعالجة معالجات الحدث بصورة كاملة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegrid.phtml:56 +#, php-format +msgid "List all reported services on host %s" +msgstr "سرد جميع الخدمات المذكورة على المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/servicegroups.phtml:11 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegroups.phtml:38 +#, php-format +msgid "List all services in the group \"%s\"" +msgstr "سرد جميع الخدمات في مجموعة \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:141 +#, php-format +msgid "List all services of all hosts in host group \"%s\"" +msgstr "سرد جميع الخدمات لكل المضيفين في مجموعة المضيف \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:209 +#, php-format +msgid "List all services on host %s" +msgstr "سرد جميع الخدمات على المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegrid.phtml:36 +#, php-format +msgid "List all services with the name \"%s\" on all reported hosts" +msgstr "سرد جميع الخدمات باسم \"%s\" على جميع المضيفين المذكورين" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:98 +msgid "List all services, for which flap detection is enabled entirely" +msgstr "سرد جميع الخدمات اللاتي فُعّل لها كشف التذبذب بصورة كاملة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:198 +msgid "List all services, for which notifications are enabled entirely" +msgstr "سرد جميع الخدمات اللاتي فُعّلت لها التنبيهات بصورة كاملة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:277 +msgid "List all services, which are processing event handlers entirely" +msgstr "سرد جميع الخدمات اللاتي تُعالج معالجات الحدث بصورة كاملة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:411 +msgid "List comments" +msgstr "سرد التعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:382 +msgid "List contact groups" +msgstr "سرد مجموعات جهة الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:308 +msgid "List contacts" +msgstr "سرد جهات الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/contacts.phtml:29 +#, php-format +msgid "List contacts in contact-group \"%s\"" +msgstr "سرد جهات الاتصال في مجموعة الاتصال \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:213 +msgid "List downtimes" +msgstr "سرد أزمنة التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:547 +msgid "List event records" +msgstr "سرد سجلات الحدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:502 +msgid "List host groups" +msgstr "سرد مجموعات المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:49 +msgid "List hosts" +msgstr "سرد المضيفين" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:273 +msgid "List notifications" +msgstr "سرد التنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:462 +msgid "List service groups" +msgstr "سرد مجموعات الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:124 +msgid "List services" +msgstr "سرد الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:119 +msgid "Livestatus Resource" +msgstr "مورد Livestatus" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:183 +msgid "Load More" +msgstr "تحميل أكثر" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegrid.phtml:93 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegrid.phtml:114 +msgid "Load more" +msgstr "تحميل أكثر" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:95 +msgid "Local" +msgstr "محلي" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:225 +msgid "Local Command File" +msgstr "ملف أمر محلي" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:89 +msgid "Make use of the ssh identity resource" +msgstr "استخدم مورد الهوية ssh" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:39 +msgid "Max" +msgstr "أقصى" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:444 +msgid "Max (min)" +msgstr "أقصى (أدنى)" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:38 +msgid "Min" +msgstr "أدنى" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:147 +msgid "Minutes" +msgstr "دقائق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:21 +msgid "Monitoring Backend" +msgstr "مراقبة الخلفية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/BackendPage.php:14 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:73 +msgctxt "setup.page.title" +msgid "Monitoring Backend" +msgstr "مراقبة الخلفية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:7 +msgid "Monitoring Backends" +msgstr "مراقبة الخلفيات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:3 +msgid "Monitoring Features" +msgstr "مراقبة المميزات" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:249 +msgid "Monitoring Health" +msgstr "مراقبة الصحة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:21 +msgctxt "setup.page.title" +msgid "Monitoring IDO Resource" +msgstr "مراقبة مورد IDO" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/LivestatusResourcePage.php:14 +msgctxt "setup.page.title" +msgid "Monitoring Livestatus Resource" +msgstr "مراقبة مورد Livestatus" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/SecurityPage.php:14 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:42 +msgctxt "setup.page.title" +msgid "Monitoring Security" +msgstr "مراقبة الأمان" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:79 +#, php-format +msgid "Monitoring backend \"%s\" not found" +msgstr "مراقبة الخلفية \"%s\" غير موجود" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:149 +#, php-format +msgid "Monitoring backend \"%s\" successfully removed" +msgstr "تمت إزالة مراقبة الخلفية \"%s\" بنجاح" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:68 +#, php-format +msgid "Monitoring backend \"%s\" successfully updated" +msgstr "تم تحديث مراقبة الخلفية \"%s\" بنجاح" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Navigation/Renderer/BackendAvailabilityNavigationItemRenderer.php:66 +#, php-format +msgid "Monitoring backend %s is not running" +msgstr "الخلفية %s لا تعمل" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:151 +#, php-format +msgid "" +"Monitoring backend configuration could not be written to: %s. An error " +"occured:" +msgstr "إعداد خلفية المراقبة لم يُتمكن من كتابته إلى: %s. حدث خطأ:" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:144 +#, php-format +msgid "Monitoring backend configuration has been successfully written to: %s" +msgstr "إعداد خلفية المراقبة تمت كتابته إلى: %s بنجاح" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:116 +msgid "Monitoring backend successfully created" +msgstr "تم إنشاء خلفية المراقبة بنجاح" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:76 +#, php-format +msgid "" +"Monitoring security configuration could not be written to: %s. An error " +"occured:" +msgstr "إعداد مراقبة الأمان لم يُتمكن من كتابته إلى: %s. حدث خطأ:" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:68 +#, php-format +msgid "Monitoring security configuration has been successfully created: %s" +msgstr "إعداد مراقبة الأمان تم إنشاؤه بنجاح: %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:648 +msgid "Month" +msgstr "شهر" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:313 +msgid "Muted" +msgstr "صامت" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:21 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:39 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:45 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:51 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:57 +msgid "N/A" +msgstr "غير متوفر" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:49 +msgid "NOTIFICATION" +msgstr "تنبيه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:20 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:325 +msgid "Name" +msgstr "اسم" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/SecurityConfigForm.php:31 +msgid "New security configuration has successfully been stored" +msgstr "تم تخزين إعداد الأمان الجديد بنجاح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:22 +msgid "Next check" +msgstr "الفحص التالي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:22 +msgid "Next update" +msgstr "التحديث التالي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:155 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:48 +msgid "No" +msgstr "لا" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php:176 +msgid "No backend has been configured" +msgstr "لم يتمّ تهيئة أيّة خلفية" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Command/Transport/CommandTransport.php:41 +#, php-format +msgid "No command transports have been configured in \"%s\"." +msgstr "لم يتمّ تهيئة أيّ أمر نواقل في: \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/comments.phtml:15 +msgid "No comments found matching the filter" +msgstr "لا توجد تعليقات توافق المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php:189 +#, php-format +msgid "No configuration for backend %s" +msgstr "لا يوجد إعداد للخلفية %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contactgroups.phtml:16 +msgid "No contact groups found matching the filter" +msgstr "لا توجد مجموعات توافق المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:14 +msgid "No contacts found matching the filter" +msgstr "لا توجد جهات اتصال توافق المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/downtimes.phtml:19 +msgid "No downtimes found matching the filter." +msgstr "لا يوجد أزمنة توقف توافق المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:31 +msgid "No historical events found matching the filter." +msgstr "لا توجد أحداث تاريخية توافق المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:18 +msgid "No host groups found matching the filter." +msgstr "لا توجد مجموعات مضيف توافق المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:18 +msgid "No hosts found matching the filter" +msgstr "لا يوجد مضيفين يوافقون المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hosts.phtml:19 +msgid "No hosts found matching the filter." +msgstr "لا يوجد مضيفين يوافقون المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:62 +msgid "No notification has been sent for this issue." +msgstr "لم تُرسَل أي تنبيهات لهذه المشكلة." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/notifications.phtml:18 +msgid "No notifications found matching the filter." +msgstr "لا يوجد تنبيهات توافق المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:69 +msgid "No notifications have been sent for this contact" +msgstr "لم تُرسَل أي تنبيهات لجهة الاتصال هذه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegroups.phtml:16 +msgid "No service groups found matching the filter." +msgstr "لا توجد مجموعات خدمة توافق المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:20 +msgid "No services found matching the filter" +msgstr "لا توجد خدمات توافق المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/services.phtml:20 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegrid.phtml:14 +msgid "No services found matching the filter." +msgstr "لا توجد خدمات توافق المرشح." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/eventgrid.phtml:77 +msgid "No state changes in the selected time period." +msgstr "ليس هناك تغييرات حالة في الفترة الزمنية المحددة." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:12 +msgid "No such contact" +msgstr "جهة الاتصال غير موجودة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:198 +msgctxt "command transport instance association" +msgid "None" +msgstr "لا شيء" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:56 +msgctxt "monitoring.protected_customvars" +msgid "None" +msgstr "لا شيء" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:57 +msgid "Not acknowledged" +msgstr "غير معترف به" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checksource.phtml:13 +msgid "Not reachable" +msgstr "لا يمكن الوصول إليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/notifications.phtml:68 +msgid "Not sent out to any contact" +msgstr "لم يتم إرساله لأي جهة اتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notes.phtml:43 +msgid "Notes" +msgstr "ملاحظات" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:294 +msgid "Notification Start" +msgstr "بداية التنبيه" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:224 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:129 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/monitoringfeatures.phtml:131 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:137 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:141 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/detail-content.phtml:24 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:138 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:143 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:2 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/EventOverviewForm.php:74 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:50 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:126 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:329 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:424 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:467 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:474 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:50 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:272 +msgid "Notifications" +msgstr "تنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml:13 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml:13 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:22 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:18 +msgid "Notifications Disabled" +msgstr "التنبيهات معطلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:15 +msgid "Notifications and Problems" +msgstr "التنبيهات والمشاكل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:326 +msgid "Notifications and average reaction time per hour." +msgstr "التنبيهات ومتوسط زمن رد الفعل في الساعة." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:464 +msgid "Notifications and defects per hour" +msgstr "التنبيهات والعيوب في الساعة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:58 +msgid "Notifications sent to this contact" +msgstr "التنبيهات المرسلة لجهة الاتصال" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:78 +#, php-format +msgid "Notifications will be re-enabled in %s" +msgstr "سيعاد تفعيل التنبيهات في %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/disable-notifications.phtml:13 +#, php-format +msgid "Notifications will be re-enabled in %s." +msgstr "سيعاد تفعيل التنبيهات في %s." + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Service.php:190 +msgid "OK" +msgstr "سليم" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:56 +msgctxt "icinga.state" +msgid "OK" +msgstr "سليم" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:96 +msgid "Object type" +msgstr "نوع الكائن" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:46 +msgid "Obsessing" +msgstr "توجس" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:149 +msgid "Obsessing Over Hosts" +msgstr "توجس على المضيفين" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:158 +msgid "Obsessing Over Services" +msgstr "توجس على الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:568 +msgid "Occurence" +msgstr "حدوث" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:120 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:243 +msgid "Ok" +msgstr "سليم" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:535 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:104 +msgid "One day" +msgstr "يوم واحد" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:537 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:106 +msgid "One month" +msgstr "شهر واحد" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:536 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:105 +msgid "One week" +msgstr "أسبوع واحد" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:538 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:107 +msgid "One year" +msgstr "سنة واحدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:68 +msgid "Output" +msgstr "مخرج" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:292 +msgid "Overdue" +msgstr "متأخر" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:168 +msgid "Overview" +msgstr "نظرة عامة" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Host.php:191 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Service.php:202 +msgid "PENDING" +msgstr "معلق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:34 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:22 +msgid "Pager" +msgstr "جهاز إخطار" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:328 +msgid "Pager Address / Number" +msgstr "عنوان / رقم جهاز الإخطار" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:141 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:42 +msgid "Passive Checks" +msgstr "اختبارات سلبية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:167 +msgid "Passive Host Checks" +msgstr "فحوصات مضيف سلبية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:176 +msgid "Passive Service Checks" +msgstr "فحوصات الخدمة السلبية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:136 +msgid "Passive checks" +msgstr "فحوصات سلبية" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:113 +msgid "Password" +msgstr "كلمة مرور" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:179 +msgid "Path to the Icinga command file on the remote Icinga instance" +msgstr "مسار ملف أمر إسنجا على حالة إسنجا البعيدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/LocalTransportForm.php:32 +msgid "Path to the local Icinga command file" +msgstr "مسار ملف أمر إسنجا المحلي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:88 +msgid "Per Host" +msgstr "لكل مضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:77 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/ToggleInstanceFeaturesCommandForm.php:187 +msgid "Performance Data" +msgstr "بيانات الأداء" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/perfdata.phtml:2 +msgid "Performance data" +msgstr "بيانات الأداء" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:47 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:55 +msgid "Persistent" +msgstr "دائم" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:61 +msgid "Persistent Comment" +msgstr "تعليق دائم" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/BackendPage.php:16 +msgid "" +"Please configure below how Icinga Web 2 should retrieve monitoring " +"information." +msgstr "الرجاء إعداد كيفية استعادة معلومات المراقبة من قبل إسنجا وب 2" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/TransportPage.php:16 +msgid "" +"Please define below how you want to send commands to your monitoring " +"instance." +msgstr "الرجاء تعريف كيف تريد إرسال أوامر لحالة المراقبة خاصتك." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:23 +msgid "" +"Please fill out the connection details below to access the IDO database of " +"your monitoring environment." +msgstr "الرجاء تعبئة تفاصيل الاتصال أدناه للوصول لقاعدة بيانات بيئة المراقبة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/LivestatusResourcePage.php:16 +msgid "" +"Please fill out the connection details below to access the Livestatus socket " +"interface for your monitoring environment." +msgstr "" +"الرجاء تعبئة تفاصيل الاتصال أدناه للوصول لواجهة مقبس Livestatus لبيئة" +" المراقبة خاصتك." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/output.phtml:1 +msgid "Plugin Output" +msgstr "مخرج التوصيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:150 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:101 +msgid "Port" +msgstr "المنفذ" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/components/selectioninfo.phtml:3 +msgctxt "Multi-selection help" +msgid "" +"Press and hold the Ctrl key while clicking on rows to select multiple rows " +"or press and hold the Shift key to select a range of rows" +msgstr "" +"اضغط باستمرار على مفتاح Ctrl أثناء الضغط على صفوف لاختيار صفوف متعددة " +"أو الضغط باستمرار مع مفتاح Shift لاختيار مدى من الصفوف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:20 +msgid "Problem Handling" +msgstr "معالجة المشكلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/detail-content.phtml:5 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:22 +msgid "Problem handling" +msgstr "معالجة المشكلة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:124 +msgid "Problems" +msgstr "المشاكل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:14 +msgid "Process Info" +msgstr "معلومات العملية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HealthController.php:33 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HealthController.php:55 +msgid "Process Information" +msgstr "معلومات العملية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:166 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:168 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/command.phtml:21 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/command.phtml:33 +msgid "Process check result" +msgstr "معالجة نتيجة الفحص" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:107 +msgid "Processing check result.." +msgid_plural "Processing check results.." +msgstr[0] "معالجة نتيجة الفحص.." +msgstr[1] "معالجة نتائج الفحص.." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:24 +msgid "Program Start Time" +msgstr "زمن بداية البرنامج" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/info.phtml:18 +msgid "Program Version" +msgstr "إصدار البرنامج" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/SecurityConfigForm.php:56 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/SecurityStep.php:53 +msgid "Protected Custom Variables" +msgstr "متغيرات مخصصة محمية" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:264 +msgid "Recently Recovered Services" +msgstr "خدمات تعافت مؤخرا" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:95 +msgid "Remote" +msgstr "بعيد" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:226 +msgid "Remote Command File" +msgstr "ملف أمر بعيد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:59 +msgid "Remote Host" +msgstr "مضيف بعيد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:63 +msgid "Remote SSH Port" +msgstr "منفذ SSH بعيد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/TransportStep.php:67 +msgid "Remote SSH User" +msgstr "مستخدم SSH بعيد" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteDowntimesCommandForm.php:63 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/DeleteCommentsCommandForm.php:63 +msgid "Remove" +msgid_plural "Remove All" +msgstr[0] "إزالة" +msgstr[1] "إزالة الكل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comments/show.phtml:11 +#, php-format +msgid "Remove %d comments" +msgstr "إزالة %d من التعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:172 +#, php-format +msgid "Remove Command Transport %s" +msgstr "إزالة ناقل أمر %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ConfigController.php:139 +#, php-format +msgid "Remove Monitoring Backend %s" +msgstr "إزالة خلفية مراقبة %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtimes/show.phtml:11 +#, php-format +msgid "Remove all %d scheduled downtimes" +msgstr "إزالة كل أزمنة المجدولة %d" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/CommentsController.php:93 +msgid "Remove all Comments" +msgstr "إزالة كل التعليقات" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/DowntimesController.php:97 +msgid "Remove all Downtimes" +msgstr "إزالة كل أزمنة التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comments/show.phtml:16 +msgid "Remove all selected comments" +msgstr "إزالة كل التعليقات المحددة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtimes/show.phtml:16 +msgid "Remove all selected downtimes" +msgstr "إزالة كل أزمنة التوقف المحددة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:107 +#, php-format +msgid "Remove command transport %s" +msgstr "إزالة ناقل الأمر %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:52 +#, php-format +msgid "Remove monitoring backend %s" +msgstr "إزالة خلفية المراقبة %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php:73 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php:92 +msgid "Remove problem acknowledgement" +msgid_plural "Remove problem acknowledgements" +msgstr[0] "إزالة إشعار مشكلة" +msgstr[1] "إزالة إشعارات مشكلة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/RemoveAcknowledgementCommandForm.php:114 +msgid "Removing problem acknowledgement.." +msgid_plural "Removing problem acknowledgements.." +msgstr[0] "إزالة إشعار مشكلة.." +msgstr[1] "إزالة إشعارات مشكلة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:540 +msgid "Report interval" +msgstr "فترة التقرير" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:236 +msgid "Reporting" +msgstr "التقارير" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:188 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:190 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:34 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:48 +msgid "Reschedule" +msgstr "إعادة الجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:142 +msgid "Reschedule Host Check" +msgstr "إعادة جدولة فحص المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:187 +msgid "Reschedule Host Checks" +msgstr "إعادة جدولة فحص المضيفين" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:100 +msgid "Reschedule Service Check" +msgstr "إعادة جدولة فحص الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:190 +msgid "Reschedule Service Checks" +msgstr "إعادة جدولة فحوصات الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:236 +msgid "Resource" +msgstr "مورد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:89 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:124 +msgid "Resource Name" +msgstr "اسم المورد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:165 +#, php-format +msgid "Resource configuration could not be udpated: %s. An error occured:" +msgstr "إعداد المورد لا يُمكن تحديثه: %s. حدث خطأ:" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:160 +#, php-format +msgid "Resource configuration has been successfully updated: %s" +msgstr "إعداد المورد تم تحديثه بنجاح: %s" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:85 +msgid "Restrict views to the Icinga objects that match the filter" +msgstr "تقييد العروض بكائنات إسنجا التي توافق المرشح" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:60 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:86 +msgid "Runtime Variables" +msgstr "متغيرات وقت التشغيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:77 +msgid "SCHEDULED DOWNTIME" +msgstr "زمن توقف مجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:108 +msgid "SSH Identity" +msgstr "هوية SSH" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:151 +msgid "SSH port to connect to on the remote Icinga instance" +msgstr "منفذ SSH المُتصل به على حالة إسنجا البعيدة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:6 +msgctxt "Downtime status" +msgid "STARTS" +msgstr "يبدأ" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:40 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:42 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/SecurityConfigForm.php:20 +msgid "Save Changes" +msgstr "حفظ التغييرات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:179 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:181 +msgid "Schedule Check" +msgstr "فحص مجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:154 +msgid "Schedule Host Downtime" +msgstr "زمن توقف مضيف مجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:199 +msgid "Schedule Host Downtimes" +msgstr "أزمنة توقف مضيف مجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:112 +msgid "Schedule Service Downtime" +msgstr "زمن توقف خدمة مجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:202 +msgid "Schedule Service Downtimes" +msgstr "أزمنة توقف خدمة مجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:15 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:29 +msgid "" +"Schedule a downtime to suppress all problem notifications within a specific " +"period of time" +msgstr "جدولة زمن توقف لإخماد كل تنبيهات المشكلة ضمن نطاق زمني محدد" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:34 +msgid "Schedule check" +msgid_plural "Schedule checks" +msgstr[0] "جدولة فحص" +msgstr[1] "جدولة فحوصات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php:28 +msgid "Schedule check for all services on the hosts and the hosts themselves." +msgstr "جدولة فحص لكل خدمات المضيفين والمضيفين أنفسهم." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:7 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:21 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:47 +msgid "Schedule downtime" +msgid_plural "Schedule downtimes" +msgstr[0] "زمن توقف مجدول" +msgstr[1] "أزمنة توقف مجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:30 +msgid "" +"Schedule downtime for all services on the hosts and the hosts themselves." +msgstr "جدولة فحص لكل خدمات المضيفين والمضيفين أنفسهم." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:100 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:99 +msgid "Schedule downtimes" +msgstr "جدولة أزمنة توقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:49 +msgid "Schedule non-triggered downtime for all child hosts" +msgstr "جدولة زمن توقف لا يمكن تشغيله لكل المضيفين التابعين" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:42 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:56 +msgid "Schedule the next active check at a different time than the current one" +msgstr "جدول الفحص التالي النشط في زمن مختلف عن الزمن الحالي" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php:44 +msgid "Schedule the next active check to run immediately" +msgstr "جدول الفحص التالي النشط ليعمل حالا" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:48 +msgid "Schedule triggered downtime for all child hosts" +msgstr "جدولة زمن توقف يمكن تشغيله لكل المضيفين التابعين" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:72 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:100 +msgid "Scheduled" +msgstr "مجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:253 +msgid "Scheduled End" +msgstr "نهاية الجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:252 +msgid "Scheduled Start" +msgstr "بداية الجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:127 +msgid "Scheduled end" +msgstr "نهاية الجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:120 +msgid "Scheduled start" +msgstr "بداية الجدولة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php:74 +msgid "Scheduling check.." +msgid_plural "Scheduling checks.." +msgstr[0] "جدولة الفحص.." +msgstr[1] "جدولة الفحص.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php:51 +msgid "Scheduling host check.." +msgid_plural "Scheduling host checks.." +msgstr[0] "جدولة فحص المضيف.." +msgstr[1] "جدولة فحص المضيفين.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:111 +msgid "Scheduling host downtime.." +msgid_plural "Scheduling host downtimes.." +msgstr[0] "جدولة زمن توقف المضيف.." +msgstr[1] "جدولة أزمنة توقف المضيف.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:100 +msgid "Scheduling service check.." +msgid_plural "Scheduling service checks.." +msgstr[0] "جدولة فحص خدمة.." +msgstr[1] "جدولة فحوصات خدمة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:223 +msgid "Scheduling service downtime.." +msgid_plural "Scheduling service downtimes.." +msgstr[0] "جدولة زمن توقف خدمة.." +msgstr[1] "جدولة أزمنة توقف خدمة.." + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:99 +msgid "Security" +msgstr "أمان" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:223 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:178 +msgid "Send Custom Host Notification" +msgstr "إرسال تنبيهات مضيف مخصصة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:136 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:226 +msgid "Send Custom Service Notification" +msgstr "إرسال تنبيهات خدمة مخصصة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:123 +msgid "Send Notification" +msgstr "إرسال تنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:18 +msgid "Send a custom notification to contacts responsible for this host" +msgstr "إرسال تنبيه مخصص لجهات الاتصال المسئولة عن هذا المضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:33 +msgid "Send a custom notification to contacts responsible for this service" +msgstr "إرسال تنبيه مخصص لجهات الاتصال المسئولة عن هذا الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:26 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:44 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:45 +#, php-format +msgid "Send a mail to %s" +msgstr "إرسال بريد إلى %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:29 +msgid "Send custom notification" +msgid_plural "Send custom notifications" +msgstr[0] "إرسال تنبيه مخصص" +msgstr[1] "إرسال تنبيهات مخصصة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:10 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:25 +msgid "Send notification" +msgstr "إرسال تنبيه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/hosts/show.phtml:144 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/services/show.phtml:146 +msgid "Send notifications" +msgstr "إرسال تنبيهات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:95 +msgid "Sending custom notification.." +msgid_plural "Sending custom notifications.." +msgstr[0] "إرسال تنبيه مخصص.." +msgstr[1] "إرسال تنبيهات مخصصة.." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/notifications.phtml:60 +#, php-format +msgid "Sent to %s" +msgstr "إرسال إلى %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:15 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:26 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:17 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:19 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:13 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml:3 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/service-header.phtml:48 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/notifications.phtml:44 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:247 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:439 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:199 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/DataView/DataView.php:278 +msgid "Service" +msgstr "خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:116 +msgid "Service Action" +msgstr "فعل خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:87 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:127 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:152 +msgid "Service Checks" +msgstr "فحوصات الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:156 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:574 +msgid "Service Grid" +msgstr "شبكة الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegroups.phtml:23 +msgid "Service Group" +msgstr "مجموعة خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:232 +msgid "Service Group Chart" +msgstr "مخطط مجموعة خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:487 +msgid "Service Group Name" +msgstr "اسم مجموعة خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:461 +msgid "Service Groups" +msgstr "مجموعات الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:170 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:599 +msgid "Service Name" +msgstr "اسم الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:144 +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:260 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/ok_hosts.phtml:78 +msgid "Service Problems" +msgstr "مشاكل الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:20 +msgid "Service Problems:" +msgstr "مشاكل الخدمة:" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:168 +msgid "Service Severity" +msgstr "خطورة الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:28 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegroups.phtml:24 +msgid "Service States" +msgstr "حالات الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:36 +msgid "Service not found" +msgstr "الخدمة غير موجودة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:110 +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:184 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/servicegroups.phtml:17 +msgid "Servicegroups" +msgstr "مجموعات خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:108 +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:180 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/tactical/components/hostservicechecks.phtml:10 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/contact.phtml:44 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:99 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:236 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:66 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:123 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:212 +msgid "Services" +msgstr "الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:53 +msgid "Set the date and time when the check should be scheduled." +msgstr "ضبط التاريخ والوقت الذي ينبغي جدولة الفحص فيه." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:89 +msgid "Set the end date and time for the downtime." +msgstr "ضبط التاريخ والوقت لزمن التوقف." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php:44 +msgid "Set the expire time." +msgstr "ضبط وقت انتهاء الصلاحية." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:79 +msgid "Set the start date and time for the downtime." +msgstr "ضبط تاريخ البداية والوقت لزمن التوقف." + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:114 +msgctxt "setup.summary.btn.finish" +msgid "Setup the monitoring module for Icinga Web 2" +msgstr "ضبط وحدة المراقبة لـ إسنجا وب 2" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:87 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:486 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:533 +msgid "Severity" +msgstr "الخطورة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/show-more.phtml:6 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:173 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/notifications.phtml:82 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hostgroups.phtml:270 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/comments.phtml:45 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegroups.phtml:166 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hosts.phtml:99 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/services.phtml:104 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/downtimes.phtml:48 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:73 +msgid "Show More" +msgstr "عرض المزيد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:227 +#, php-format +msgid "Show all event records of host %s" +msgstr "عرض كل سجلات الحدث للمضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:223 +#, php-format +msgid "Show all event records of service %s on host %s" +msgstr "عرض كل سجلات الحدث للخدمة %s على المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TacticalController.php:20 +msgid "" +"Show an overview of all hosts and services, their current states and " +"monitoring feature utilisation" +msgstr "" +"عرض نظرة عامة عن كل الخدمات والمضيفين، وحالاتهم الحالية " +"واستخدام ميزة المراقبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:12 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/contacts.phtml:11 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contactgroups.phtml:39 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/contacts.phtml:35 +#, php-format +msgid "Show detailed information about %s" +msgstr "عرض معلومات تفصيلية عن %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:137 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hosts.phtml:52 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/services.phtml:67 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Link.php:37 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:182 +#, php-format +msgid "Show detailed information for host %s" +msgstr "عرض معلومات تفصيلية عن المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:151 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/services.phtml:78 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/servicegrid.phtml:80 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Link.php:63 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php:195 +#, php-format +msgid "Show detailed information for service %s on host %s" +msgstr "عرض معلومات تفصيلية عن الخدمة %s على المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml:27 +#, php-format +msgid "Show detailed information for this comment about host %s" +msgstr "عرض معلومات تفصيلية لهذا التعليق عن المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml:13 +#, php-format +msgid "Show detailed information for this comment about service %s on host %s" +msgstr "عرض معلومات تفصيلية لهذا التعليق عن الخدمة %s على المضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:35 +#, php-format +msgid "Show detailed information for this downtime scheduled for host %s" +msgstr "عرض معلومات تفصيلية عن زمن التوقف المجدول هذا للمضيف %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:22 +#, php-format +msgid "" +"Show detailed information for this downtime scheduled for service %s on host " +"%s" +msgstr "" +"عرض معلومات تفصيلية عن زمن التوقف المجدول هذا للخدمة %s على المضيف" +"%s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HealthController.php:30 +msgid "" +"Show information about the current monitoring instance's process and it's " +"performance as well as available features" +msgstr "" +"أظهر معلومات عن عملية حالة المراقبة الحالية " +"وأدائها فضلا عن الميزات المتوفرة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:51 +msgid "" +"Show recent alerts and visualize notifications and problems based on their " +"amount and chronological distribution" +msgstr "" +"عرض التنبيهات الأخيرة وتصور الإنذارات والمشاكل على أساس كميتها وتوزيعها الزمني" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:255 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:127 +msgid "Show resource configuration" +msgstr "عرض إعداد المورد" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HealthController.php:41 +msgid "Show statistics about the monitored objects" +msgstr "عرض إحصائيات عن الكائنات المراقبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:61 +#, php-format +msgid "Show summarized information for %u hosts" +msgstr "عرض معلومات مختصرة عن مضيفين %u" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:68 +#, php-format +msgid "Show summarized information for %u services" +msgstr "عرض معلومات مختصرة عن خدمات %u" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:336 +msgid "Show the Event Grid" +msgstr "عرض شبكة الحدث" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:574 +msgid "Show the Service Grid" +msgstr "عرض شبكة الخدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:254 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:126 +#, php-format +msgid "Show the configuration of the %s resource" +msgstr "عرض إعداد المورد %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:24 +msgid "Show the number of historical event records grouped by time and type" +msgstr "عرض عدد سجلات الأحداث التاريخية والتي تم تجميعها حسب الوقت والنوع" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/LivestatusResourcePage.php:76 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:184 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:327 +msgid "Skip Validation" +msgstr "تخطي التحقق" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:128 +msgid "Socket" +msgstr "مقبس" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/service-header.phtml:13 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/service-header.phtml:42 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/object/host-header.phtml:12 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/hosts.phtml:38 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/list/services.phtml:51 +msgctxt "Soft state" +msgid "Soft" +msgstr "ناعم" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:111 +msgctxt "setup.welcome.btn.next" +msgid "Start" +msgstr "بداية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:78 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:250 +msgid "Start Time" +msgstr "زمن البداية" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:70 +msgid "Started downtimes" +msgstr "بداية أزمنة التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:114 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:134 +msgid "State" +msgstr "حالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/EventOverviewForm.php:44 +msgid "State Changes" +msgstr "تغييرات الحالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HealthController.php:43 +msgid "Stats" +msgstr "إحصائيات" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:53 +msgid "Status" +msgstr "الحالة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:111 +msgid "Sticky Acknowledgement" +msgstr "إشعار مثبت" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:31 +msgid "Submit Passive Check Result" +msgid_plural "Submit Passive Check Results" +msgstr[0] "تسليم نتيجة الفحص السلبي" +msgstr[1] "تسليم نتائج الفحص السلبي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostController.php:166 +msgid "Submit Passive Host Check Result" +msgstr "تسليم نتيجة فحص المضيف السلبي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/HostsController.php:211 +msgid "Submit Passive Host Check Results" +msgstr "تسليم نتائج فحص المضيف السلبي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServiceController.php:124 +msgid "Submit Passive Service Check Result" +msgstr "تسليم نتيجة فحص الخدمة السلبي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ServicesController.php:214 +msgid "Submit Passive Service Check Results" +msgstr "تسليم نتائج فحص الخدمة السلبي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/command.phtml:17 +#, php-format +msgid "Submit a one time or so called passive result for the %s check" +msgstr "تسليم لمرة واحدة أو ما يسمى بالنتيجة السلبية للفحص %s" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:248 +msgid "System" +msgstr "نظام" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:172 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TacticalController.php:23 +msgid "Tactical Overview" +msgstr "نظرة عامة تكتيكية" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:192 +msgid "" +"The Zend database adapter for MySQL is required to access a MySQL database." +msgstr "مطلوب محول قاعدة بيانات زند لـ MySQL للوصول إلى قاعدة بيانات MySQL." + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:212 +msgid "" +"The Zend database adapter for PostgreSQL is required to access a PostgreSQL " +"database." +msgstr "" +"مطلوب محول قاعدة بيانات زند لـ PostgreSQL للوصول إلى قاعدة بيانات PostgreSQL." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:102 +msgid "The configuration has been successfully validated." +msgstr "تم التحقق من الإعداد بنجاح." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/BackendPage.php:46 +msgid "The data source used for retrieving monitoring information" +msgstr "مصدر البيانات المستخدم لاسترجاع معلومات المراقبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:143 +msgid "The date/time the scheduled downtime actually ended" +msgstr "تاريخ ووقت انتهاء زمن التوقف المجدول الفعلي" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:123 +msgid "" +"The date/time the scheduled downtime is supposed to end. If this is a " +"flexible (non-fixed) downtime, this refers to the last possible time that " +"the downtime can start" +msgstr "" +"التاريخ/الوقت الذي يفترض انتهاء زمن التوقف المجدول فيه. إذا كان " +"زمنا مرنا (غير ثابت)، فإنه يشير إلى آخر وقت من الممكن أن يبدأ " +"فيه زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:116 +msgid "" +"The date/time the scheduled downtime is supposed to start. If this is a " +"flexible (non-fixed) downtime, this refers to the earliest possible time " +"that the downtime can start" +msgstr "" +"التاريخ/الوقت الذي يفترض ابتداء زمن التوقف فيه. إذا كان زمنا " +"مرنا (غير ثابت)، فإنه يشير إلى أول وقت من الممكن أن يبدأ " +"فيه زمن التوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php:69 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:204 +msgid "The end time must be greater than the start time" +msgstr "زمن النهاية يجب أن يكون أكبر من زمن البداية" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/DataView/DataView.php:448 +#, php-format +msgid "The filter column \"%s\" is not allowed here." +msgstr "لا يُسمح هنا بعمود المرشح \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:66 +msgid "The given resource name is already in use." +msgstr "اسم المورد المعطى مستخدم سابقا." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/BackendPage.php:29 +msgid "The identifier of this backend" +msgstr "رقم التعريف لهذه الخلفية" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/notifications.phtml:57 +#, php-format +msgid "The last one was sent %s." +msgstr "تم إرسال آخر %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:205 +msgid "" +"The name of the Icinga instance this transport should exclusively transfer " +"commands to." +msgstr "اسم حالة \"إسنجا\" التي يتحتم على هذا النقل حصريا نقل الأوامر إليها." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:38 +msgid "The name of the person who scheduled this downtime" +msgstr "اسم الشخص الذي جدول زمن التوقف هذا" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:219 +msgid "" +"The name of this command transport that is used to differentiate it from " +"others" +msgstr "اسم ناقل الأمر هذا الذي يستخدم للتفرقة بينه وبين الآخرين" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:195 +msgid "" +"The name of this monitoring backend that is used to differentiate it from " +"others" +msgstr "اسم خلفية المراقبة هذه التي تستخدم للتفرقة بينها وبين الآخرين" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:79 +msgid "" +"The performance data of this check result. Leave empty if this check result " +"has no performance data" +msgstr "" +"بيانات الأداء لنتيجة الفحص هذه. دعها فارغة إذا لم يكن لنتيجة الفحص هذه " +"بيانات أداء" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:69 +msgid "The plugin output of this check result" +msgstr "مخرج التوصيل لنتيجة الفحص هذه" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:237 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:109 +msgid "The resource to use" +msgstr "المورد المراد استخدامه" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/DataView/DataView.php:357 +#, php-format +msgid "The sort column \"%s\" is not allowed in \"%s\"." +msgstr "عمود الفرز \"%s\" لا يُسمح به في \"%s\"." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:54 +msgid "The state this check result should report" +msgstr "حالة نتيجة الفحص التي يجب أن تكون في التقرير" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:223 +msgid "The type of data source used for retrieving monitoring information" +msgstr "نوع مصدر البيانات المستخدم لاسترجاع معلومات المراقبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:381 +msgid "" +"There is currently more than one icinga instance writing to the IDO. You'll " +"see all objects from all instances without any differentation. If this is " +"not desired, consider setting up a separate IDO for each instance." +msgstr "" +"يوجد حاليا أكثر من حالة icinga تقوم بالكتابة على IDO. سترى " +"كل الكائنات من جميع الحالات دون أي اختلاف. إن لم يكن هذا " +"هو المطلوب، فانظر في إنشاء IDO منفصلة لكل حالة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/BackendConfigForm.php:375 +msgid "" +"There is currently no icinga instance writing to the IDO. Make sure that a " +"icinga instance is configured and able to write to the IDO." +msgstr "" +"لا يوجد حاليا أي حالة إسنجا تقوم بالكتابة على IDO. تأكد من " +"أن حالة إسنجا تم إعدادها وقادرة على الكتابة على IDO." + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Backend/MonitoringBackend.php:91 +#, php-format +msgid "There is no \"%s\" monitoring backend" +msgstr "لا يوحد خلفية مراقبة \"%s\"" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:22 +msgid "" +"This command is used to acknowledge host or service problems. When a problem " +"is acknowledged, future notifications about problems are temporarily " +"disabled until the host or service recovers." +msgstr "" +"يستخدم هذا الأمر للإشعار بمشاكل المضيف أو الخدمة. عندما يتم الإشعار " +"بمشكلة، فإن الإخطارات المستقبلية حول المشاكل سيتم تعطيلها مؤقتا " +"حتى يتعافى المضيف أو الخدمة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php:19 +msgid "This command is used to add host or service comments." +msgstr "يستخدم هذا الأمر لإضافة تعليقات على المضيف أو الخدمة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Instance/DisableNotificationsExpireCommandForm.php:26 +msgid "" +"This command is used to disable host and service notifications for a " +"specific time." +msgstr "يستخدم هذا الأمر لتعطيل التنبيهات على المضيف والخدمة لوقت محدد." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:33 +msgid "" +"This command is used to schedule host and service downtimes. During the " +"specified downtime, Icinga will not send notifications out about the hosts " +"and services. When the scheduled downtime expires, Icinga will send out " +"notifications for the hosts and services as it normally would. Scheduled " +"downtimes are preserved across program shutdowns and restarts." +msgstr "" +"يستخدم هذا الأمر لجدولة أزمنة التوقف للمضيف والخدمة. خلال فترة التوقف " +"المحددة لن يرسل إسنجا أي إخطارات عن المضيفين والخدمات. عند انتهاء" +"صلاحية زمن التوقف المجدول، ستقوم إسنجا بإرسال إخطارات للمضيفين " +"والخدمات كالمعتاد. يتم الاحتفاظ بأزمنة التوقف المجدول خلال إغلاق " +"البرنامج وإعادة تشغيله." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceCheckCommandForm.php:23 +msgid "" +"This command is used to schedule the next check of hosts or services. Icinga " +"will re-queue the hosts or services to be checked at the time you specify." +msgstr "" +"يستخدم هذا الأمر لجدولة الفحص التالي للمضيفين والخدمات. ستقوم إسنجا " +"بإعادة قائمة الانتظار للمضيفين والخدمات لتُفحص في الوقت الذي تحدده." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:20 +msgid "" +"This command is used to send custom notifications about hosts or services." +msgstr "يستخدم هذا الأمر لإرسال تنبيهات مخصصة حول المضيفين والخدمات." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:20 +msgid "This command is used to submit passive host or service check results." +msgstr "يستخدم هذا الأمر لتسليم نتائج فحص المضيف أو الخدمة السلبي." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:63 +msgid "This comment does not expire." +msgstr "هذا التعليق لا تنتهي صلاحيته." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml:41 +#, php-format +msgid "This comment expires on %s at %s" +msgstr "هذا التعليق تنتهي صلاحيته في %s في %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:60 +#, php-format +msgid "This comment expires on %s at %s." +msgstr "هذا التعليق تنتهي صلاحيته في %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:47 +msgid "This downtime is flexible" +msgstr "زمن التوقف هذا مرن" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:51 +msgid "This downtime is in effect" +msgstr "زمن التوقف هذا ساري المفعول" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:106 +#, php-format +msgid "" +"This fixed host downtime has been scheduled to start on %s at %s and to end " +"on %s at %s." +msgstr "" +"زمن توقف المضيف الثابت هذا تمت جدولته ليبدأ في %s في %s " +"وينتهي في %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:96 +#, php-format +msgid "" +"This fixed host downtime was started on %s at %s and expires on %s at %s." +msgstr "" +"زمن توقف المضيف الثابت هذا بدأ في %s في %s " +"وينتهي في %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:105 +#, php-format +msgid "" +"This fixed service downtime has been scheduled to start on %s at %s and to " +"end on %s at %s." +msgstr "" +"زمن توقف الخدمة الثابت هذه تمت جدولته ليبدأ في %s في %s " +"وينتهي في %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:95 +#, php-format +msgid "" +"This fixed service downtime was started on %s at %s and expires on %s at %s." +msgstr "زمن توقف الخدمة الثابت هذا بدأ في %s في %s وينتهي في %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:85 +#, php-format +msgid "" +"This flexible host downtime has been scheduled to start between %s - %s and " +"to last for %s." +msgstr "" +"زمن توقف المضيف المرن هذا تمت جدولته ليبدأ بين %s - %s " +"ويستمر إلى %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:74 +#, php-format +msgid "" +"This flexible host downtime was started on %s at %s and lasts for %s until " +"%s at %s." +msgstr "" +"زمن توقف المضيف المرن هذا بدأ في s في %s ويستمر حتى %s " +"إلى %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:84 +#, php-format +msgid "" +"This flexible service downtime has been scheduled to start between %s - %s " +"and to last for %s." +msgstr "" +"زمن توقف الخدمة المرن هذا تمت جدولته ليبدأ بين %s - %s " +"ويستمر إلى %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:73 +#, php-format +msgid "" +"This flexible service downtime was started on %s at %s and lasts for %s " +"until %s at %s." +msgstr "" +"زمن توقف الخدمة المرن هذا بدأ في s في %s ويستمر حتى %s " +"إلى %s في %s." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/WelcomePage.php:33 +msgid "This is the core module for Icinga Web 2." +msgstr "هذه هي الوحدة الأساسية في إسنجا وب 2." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/event-history.phtml:54 +msgid "This notification was not sent out to any contact." +msgstr "لم يتم إرسال هذا التنبيه لأي جهة اتصال." + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:22 +msgid "Time to Reaction (Ack, Recover)" +msgstr "وقت رد الفعل (إشعار، استرداد)" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:109 +msgid "TimeLine interval" +msgstr "فترة الجدول الزمني" + +#: /usr/share/icingaweb2/modules/monitoring/configuration.php:228 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:25 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/TimelineController.php:29 +msgid "Timeline" +msgstr "الجدول الزمني" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:82 +msgid "To" +msgstr "إلى" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:183 +msgid "" +"To access the IDO stored in a MySQL database the PDO-MySQL module for PHP is " +"required." +msgstr "" +"للوصول إلى الـ IDO المخزنة في قاعدة بيانات \"ماي سيكول\" فإن وحدة PDO-MySQL" +" للـ PHP " +"مطلوبة." + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:203 +msgid "" +"To access the IDO stored in a PostgreSQL database the PDO-PostgreSQL module " +"for PHP is required." +msgstr "" +"للوصول إلى الـ IDO المخزنة في قاعدة بيانات PostgreSQL فإن وحدة PDO-PostgreSQL " +"للـ PHP مطلوبة." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/SecurityPage.php:16 +msgid "" +"To protect your monitoring environment against prying eyes please fill out " +"the settings below." +msgstr "" +"لحماية بيئة المراقبة الخاصة بك ضد أعين المتطفلين الرجاء تعبئة " +"الإعدادات أدناه." + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:85 +msgid "Today" +msgstr "اليوم" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:56 +msgid "Top 5 Recent Alerts" +msgstr "آخر أعلى 5 إنذارات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:68 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:94 +msgid "Total" +msgstr "الكلي" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:535 +msgid "Total Hosts" +msgstr "إجمالي المضيفين" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:488 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ListController.php:536 +msgid "Total Services" +msgstr "إجمالي الخدمات" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:76 +msgid "Transport" +msgstr "النقل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:217 +msgid "Transport Name" +msgstr "اسم النقل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/TransportConfigForm.php:241 +msgid "Transport Type" +msgstr "نوع النقل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:29 +msgid "Trend" +msgstr "اتجاه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:39 +msgid "Trend for the last 24h" +msgstr "الاتجاه في آخر 24 ساعة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ScheduleServiceDowntimeCommandForm.php:99 +msgid "Type" +msgstr "نوع" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:39 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/config/index.phtml:94 +#, php-format +msgid "Type: %s" +msgstr "نوع: %s" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Service.php:199 +msgid "UNKNOWN" +msgstr "مجهول" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:59 +msgctxt "icinga.state" +msgid "UNKNOWN" +msgstr "مجهول" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Host.php:188 +msgid "UNREACHABLE" +msgstr "لا يمكن الوصول إليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:128 +msgctxt "icinga.state" +msgid "UNREACHABLE" +msgstr "لا يمكن الوصول للوجهة" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Host.php:182 +msgid "UP" +msgstr "قيد التشغيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:123 +msgctxt "icinga.state" +msgid "UP" +msgstr "قيد التشغيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/service/statusicons.phtml:5 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/host/statusicons.phtml:5 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/HostFlags.php:13 +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/ServiceFlags.php:9 +msgid "Unhandled" +msgstr "غير مُعالج" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/health/stats.phtml:14 +msgid "Unhandled Problems:" +msgstr "مشاكل غير معالجة:" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:119 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:264 +msgid "Unknown" +msgstr "مجهول" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:139 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:314 +msgid "Unreachable" +msgstr "لا يمكن الوصول إليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:137 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:300 +msgid "Up" +msgstr "قيد التشغيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php:72 +msgid "Use Expire Time" +msgstr "استخدام وقت انتهاء الصلاحية" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:88 +msgid "Use SSH Identity" +msgstr "استخدام هوية SSH" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:40 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:43 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:163 +msgid "User" +msgstr "مستخدم" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-description.phtml:10 +msgid "User Comment" +msgstr "تعليق مستخدم" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Config/Transport/RemoteTransportForm.php:165 +msgid "" +"User to log in as on the remote Icinga instance. Please note that key-based " +"SSH login must be possible for this user" +msgstr "" +"المستخدم الذي يمكن الدخول بواسطته لحالة إسنجا البعيدة. يرجى ملاحظة أن " +"دخول SSH المبني على المفاتيح يجب أن يكون متاحا لهذا المستخدم" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/BackendStep.php:109 +msgid "Username" +msgstr "اسم المستخدم" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:124 +msgid "Validate Configuration" +msgstr "التحقق من الإعداد" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:125 +msgid "Validation In Progress" +msgstr "جار التحقق" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/IdoResourcePage.php:131 +msgid "Validation Log" +msgstr "سجل التحقق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:37 +msgid "Value" +msgstr "قيمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:623 +msgid "Value for interval not valid" +msgstr "قيمة الفترة ليست صالحة" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/Service.php:193 +msgid "WARNING" +msgstr "تحذير" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ProcessCheckResultCommandForm.php:57 +msgctxt "icinga.state" +msgid "WARNING" +msgstr "تحذير" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/helpers/Perfdata.php:40 +#: /usr/share/icingaweb2/modules/monitoring/application/forms/StatehistoryForm.php:118 +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/ChartController.php:250 +msgid "Warning" +msgstr "تحذير" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Setup/WelcomePage.php:21 +msgid "Welcome to the configuration of the monitoring module for Icinga Web 2!" +msgstr "مرحبا بك في إعداد وحدة المراقبة لـ إسنجا وب 2!" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:155 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/comment/show.phtml:48 +msgid "Yes" +msgstr "نعم" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:189 +msgid "Zend database adapter for MySQL" +msgstr "محول زند لقاعدة بيانات ماي سيكول" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:209 +msgid "Zend database adapter for PostgreSQL" +msgstr "محول زند لقاعدة بيانات بوستجرى إس كيو إل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml:22 +msgid "acknowledged" +msgstr "معترف به" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:60 +#, php-format +msgctxt "timeline.link.title.datetime.twice" +msgid "between %s and %s" +msgstr "بين %s و%s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:42 +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/comment/comment-detail.phtml:34 +msgid "by" +msgstr "بواسطة" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:80 +msgid "changed" +msgstr "غُيِّر" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/comments.phtml:51 +msgid "commented" +msgstr "عُلِّق" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:79 +msgid "created" +msgstr "أُنشيء" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:84 +msgid "disable" +msgstr "تعطيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:194 +msgid "down" +msgstr "متوقف" + +#: /usr/share/icingaweb2/modules/monitoring/application/forms/Command/Object/ToggleObjectFeaturesCommandForm.php:85 +msgid "enable" +msgstr "تفعيل" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:60 +#, php-format +msgctxt "Last format parameter represents the end time" +msgid "ends %s" +msgstr "انتهاء %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:28 +#, php-format +msgid "expected %s" +msgstr "متوقع %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:54 +#, php-format +msgctxt "Last format parameter represents the downtime expire time" +msgid "expires %s" +msgstr "تنتهي صلاحيته %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:69 +msgid "hard state" +msgstr "حالة صعبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/downtime/show.phtml:138 +msgid "he date/time the scheduled downtime was actually started" +msgstr "التاريخ والوقت الذي ابتدأ فيه فعليا زمن التوقف المجدول" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/partials/downtime/downtime-header.phtml:29 +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/MonitoredObject.php:735 +msgid "host" +msgstr "مضيف" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:48 +#, php-format +msgctxt "timeline.link.title.month.and.year" +msgid "in %s" +msgstr "في %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:54 +#, php-format +msgctxt "timeline.link.title.year" +msgid "in %s" +msgstr "في %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:37 +msgid "in the last hour" +msgstr "في الساعة الأخيرة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:41 +#, php-format +msgctxt "timeline.link.title.week.and.year" +msgid "in week %s of %s" +msgstr "في الاسبوع %s من %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checksource.phtml:9 +msgid "is not reachable" +msgstr "لا يمكن الوصول إليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checksource.phtml:9 +msgid "is reachable" +msgstr "يمكن الوصول إليه" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/alertsummary/index.phtml:35 +msgid "notifications per hour" +msgstr "التنبيهات في الساعة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/timeline/index.phtml:35 +#, php-format +msgctxt "timeline.link.title.time" +msgid "on %s" +msgstr "في %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:65 +#, php-format +msgctxt "Last format parameter represents the time scheduled" +msgid "scheduled %s" +msgstr "مجدول في %s" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/downtime.phtml:70 +#, php-format +msgctxt "Last format parameter represents the time scheduled" +msgid "scheduled flexible %s" +msgstr "مجدول بصورة مرنة في %s" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/Object/MonitoredObject.php:738 +msgid "service" +msgstr "خدمة" + +#: /usr/share/icingaweb2/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml:69 +msgid "soft state" +msgstr "حالة ناعمة" + +#: /usr/share/icingaweb2/modules/monitoring/library/Monitoring/MonitoringWizard.php:53 +msgctxt "setup.summary.subject" +msgid "the monitoring module" +msgstr "وحدة المراقبة" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:192 +msgid "unchanged" +msgstr "دون تغيير" + +#: /usr/share/icingaweb2/modules/monitoring/application/controllers/AlertsummaryController.php:196 +msgid "up" +msgstr "قيد التشغيل" + + From 8da05e0bf837f4a0e6c8120649f952d9b25de2df Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 3 Nov 2016 16:15:34 +0100 Subject: [PATCH 209/337] Compile Arabic translation --- application/locale/ar_SA/LC_MESSAGES/icinga.mo | Bin 0 -> 66172 bytes .../locale/ar_SA/LC_MESSAGES/monitoring.mo | Bin 0 -> 96154 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 application/locale/ar_SA/LC_MESSAGES/icinga.mo create mode 100644 modules/monitoring/application/locale/ar_SA/LC_MESSAGES/monitoring.mo diff --git a/application/locale/ar_SA/LC_MESSAGES/icinga.mo b/application/locale/ar_SA/LC_MESSAGES/icinga.mo new file mode 100644 index 0000000000000000000000000000000000000000..c65cf3d7fa12c35eae8460c73a708f015432c8f6 GIT binary patch literal 66172 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 literal 0 HcmV?d00001 From 647e404c8f6d8bf309adbbf28c89eeb345baf0aa Mon Sep 17 00:00:00 2001 From: Bernhard Friedreich Date: Thu, 15 Sep 2016 17:49:27 +0200 Subject: [PATCH 210/337] Allow table header in pluginoutput resolves #12125 Signed-off-by: Eric Lippmann --- modules/monitoring/application/views/helpers/PluginOutput.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php index 4381e5b14..457938ab9 100644 --- a/modules/monitoring/application/views/helpers/PluginOutput.php +++ b/modules/monitoring/application/views/helpers/PluginOutput.php @@ -179,7 +179,7 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract $config = HTMLPurifier_Config::createDefault(); $config->set('Core.EscapeNonASCIICharacters', true); - $config->set('HTML.Allowed', 'p,br,b,a[href|target],i,table,tr,td[colspan],div,*[class]'); + $config->set('HTML.Allowed', 'p,br,b,a[href|target],i,table,tr,th,td[colspan],div,*[class]'); $config->set('Attr.AllowedFrameTargets', array('_blank')); // This avoids permission problems: // $config->set('Core.DefinitionCache', null); From 313cb3233db072adcb085da3960bd70bc83b1765 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 3 Nov 2016 16:24:46 +0100 Subject: [PATCH 211/337] Allow colspan for th in plugin output refs #12125 --- modules/monitoring/application/views/helpers/PluginOutput.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php index 457938ab9..fc3ec8e30 100644 --- a/modules/monitoring/application/views/helpers/PluginOutput.php +++ b/modules/monitoring/application/views/helpers/PluginOutput.php @@ -179,7 +179,7 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract $config = HTMLPurifier_Config::createDefault(); $config->set('Core.EscapeNonASCIICharacters', true); - $config->set('HTML.Allowed', 'p,br,b,a[href|target],i,table,tr,th,td[colspan],div,*[class]'); + $config->set('HTML.Allowed', 'p,br,b,a[href|target],i,table,tr,th[colspan],td[colspan],div,*[class]'); $config->set('Attr.AllowedFrameTargets', array('_blank')); // This avoids permission problems: // $config->set('Core.DefinitionCache', null); From cee86d1a8c3a291c9fea4ce32e04eb7d09f09102 Mon Sep 17 00:00:00 2001 From: Ian Shearin Date: Thu, 15 Sep 2016 11:36:29 -0700 Subject: [PATCH 212/337] Fix links in about doc Without this change, the links in the about doc are broken. Signed-off-by: Eric Lippmann --- doc/01-About.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/01-About.md b/doc/01-About.md index 48b4041ac..e2c9d4932 100644 --- a/doc/01-About.md +++ b/doc/01-About.md @@ -19,20 +19,20 @@ Most such actions (like rescheduling a check) can be done with just a single cli Icinga Web 2 can be installed easily from packages from the official package repositories. Setting it up is also easy with the web based setup wizard. -See [here](installation#installation) for more information about the installation. +See [here](02-Installation.md#installation) for more information about the installation. ## Configuration Icinga Web 2 can be configured via the user interface and .ini files. -See [here](configuration#configuration) for more information about the configuration. +See [here](03-Configuration.md#configuration) for more information about the configuration. ## Authentication With Icinga Web 2 you can authenticate against relational databases, LDAP and more. These authentication methods can be easily configured (via the corresponding .ini file). -See [here](authentication#authentication) for more information about +See [here](05-Authentication.md#authentication) for more information about the different authentication methods available and how to configure them. ## Authorization @@ -41,7 +41,7 @@ In Icinga Web 2 there are permissions and restrictions to allow and deny (respec roles to view or to do certain things. These roles can be assigned to users and groups. -See [here](security#security) for more information about authorization +See [here](06-Security.md#security) for more information about authorization and how to configure roles. ## User preferences @@ -50,7 +50,7 @@ Besides the global configuration each user has individual configuration options like the interface's language or the current timezone. They can be stored either in a database or in .ini files. -See [here](preferences#preferences) for more information about a user's preferences +See [here](07-Preferences.md#preferences) for more information about a user's preferences and how to configure their storage type. ## Documentation From 7e8beda5b3e7102de62105350552770b585a59ce Mon Sep 17 00:00:00 2001 From: Sander Ferdinand Date: Fri, 23 Sep 2016 13:20:55 +0200 Subject: [PATCH 213/337] Fix small typo in the README for RPM packages Signed-off-by: Eric Lippmann --- packages/RPM.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/RPM.md b/packages/RPM.md index 9c8e4a548..7a343bb5d 100644 --- a/packages/RPM.md +++ b/packages/RPM.md @@ -63,7 +63,7 @@ Decide whether to use MySQL or PostgreSQL. postgres=# CREATE DATABASE icingaweb; postgres=# \q -Add the `cingaweb` user for trusted authentication to your `pg_hba.conf` file +Add the `icingaweb` user for trusted authentication to your `pg_hba.conf` file in `/var/lib/pgsql/data/pg_hba.conf` and restart the PostgreSQL server. local icingaweb icingaweb trust From bd7851d15c4412eefda411f524969e123cce8c49 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 3 Nov 2016 18:05:05 +0100 Subject: [PATCH 214/337] IniRepository: improve initialization of per-table datasources refs #13034 --- library/Icinga/Repository/IniRepository.php | 79 ++++++++++++++++++--- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index ec9b4b1cf..e17bbaa42 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -25,17 +25,22 @@ use Icinga\Exception\StatementException; abstract class IniRepository extends Repository implements Extensible, Updatable, Reducible { /** - * Per-table datasources + * Per-table configs * + * Example: * * array( - * $table => $datasource + * 'event-type' => array( + * 'module' => 'elasticsearch', + * 'path' => 'event-types', + * 'keyColumn' => 'name' + * ) * ) * * - * @var Config[] + * @var array */ - protected $datasources = array(); + protected $configs = null; /** * The tables for which triggers are available when inserting, updating or deleting rows @@ -53,15 +58,15 @@ abstract class IniRepository extends Repository implements Extensible, Updatable /** * Create a new INI repository object * - * @param Config $ds The data source to use + * @param Config|null $ds The data source to use * * @throws ProgrammingError In case the given data source does not provide a valid key column */ - public function __construct(Config $ds) + public function __construct(Config $ds = null) { parent::__construct($ds); // First! Due to init(). - if (! $ds->getConfigObject()->getKeyColumn()) { + if (! ($ds === null || $ds->getConfigObject()->getKeyColumn())) { throw new ProgrammingError('INI repositories require their data source to provide a valid key column'); } } @@ -71,7 +76,56 @@ abstract class IniRepository extends Repository implements Extensible, Updatable */ public function getDataSource($table = null) { - return isset($this->datasources[$table]) ? $this->datasources[$table] : parent::getDataSource($table); + if ($this->ds === null) { + if ($table === null) { + $table = $this->getBaseTable(); + } + + if ($this->configs === null) { + $this->configs = $this->initializeConfigs(); + if ($this->configs === null) { + throw new ProgrammingError('Per-table configs missing'); + } + } + + if (! isset($this->configs[$table])) { + throw new ProgrammingError('Config for table "%s" missing', $table); + } + + $config = $this->configs[$table]; + if ($config instanceof Config) { + if (! $config->getConfigObject()->getKeyColumn()) { + throw new ProgrammingError( + 'INI repositories require their data source to provide a valid key column' + ); + } + } elseif (is_array($config)) { + if (! isset($config['path'])) { + throw new ProgrammingError('Path to config for table "%s" missing', $table); + } + if (! isset($config['keyColumn'])) { + throw new ProgrammingError( + 'INI repositories require their data source to provide a valid key column' + ); + } + + $newConfig = isset($config['module']) + ? Config::module($config['module'], $config['path']) + : Config::app($config['path']); + $newConfig->getConfigObject()->setKeyColumn($config['keyColumn']); + $this->configs[$table] = $config = $newConfig; + } else { + throw new ProgrammingError( + 'Config for table "%s" is a %s, expected either Icinga\Application\Config or an associative array', + $table, + is_object($config) ? get_class($config) : gettype($config) + ); + } + + return $config; + } else { + return parent::getDataSource($table); + } } /** @@ -100,6 +154,15 @@ abstract class IniRepository extends Repository implements Extensible, Updatable return array(); } + /** + * Overwrite this in your INI repository implementation in case you need to initialize the configs lazily + * + * @return array + */ + protected function initializeConfigs() + { + } + /** * Run a trigger for the given table and row which is about to be inserted * From 7331904851ec6bbb5d32a40ead3bfed632f10d1e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 3 Nov 2016 18:28:44 +0100 Subject: [PATCH 215/337] Don't show the link for resource removal if the resource is utilized for configuration refs #9804 --- application/controllers/ConfigController.php | 2 ++ .../views/scripts/config/resource.phtml | 24 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index c65298377..03750fe24 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -315,6 +315,8 @@ class ConfigController extends Controller { $this->assertPermission('config/application/resources'); $this->view->resources = Config::app('resources', true); + $configResource = Config::app()->get('global', 'config_resource'); + $this->view->unremovableResources = $configResource === null ? array() : array($configResource); $this->createApplicationTabs()->activate('resource'); } diff --git a/application/views/scripts/config/resource.phtml b/application/views/scripts/config/resource.phtml index ec9c0c7ea..82f7af0cf 100644 --- a/application/views/scripts/config/resource.phtml +++ b/application/views/scripts/config/resource.phtml @@ -58,16 +58,20 @@ ) ?> - qlink( - '', - 'config/removeresource', - array('resource' => $name), - array( - 'class' => 'action-link', - 'icon' => 'cancel', - 'title' => sprintf($this->translate('Remove resource %s'), $name) - ) - ) ?> + qlink( + '', + 'config/removeresource', + array('resource' => $name), + array( + 'class' => 'action-link', + 'icon' => 'cancel', + 'title' => sprintf($this->translate('Remove resource %s'), $name) + ) + ); + } + ?> From 9c13eedd00a609949a838e25d41f0ede21ee27ac Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 3 Nov 2016 18:48:01 +0100 Subject: [PATCH 216/337] LdapUserGroupBackendForm::createHiddenUserConfigElements(): improve doc refs #10401 --- application/forms/Config/UserGroup/LdapUserGroupBackendForm.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php index 8a56237ba..357dbaca9 100644 --- a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php +++ b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php @@ -282,6 +282,8 @@ class LdapUserGroupBackendForm extends Form /** * Create and add all elements to this form required for the user configuration as hidden + * + * This is required to purge already present options when changing the type form LDAP to MS AD. */ protected function createHiddenUserConfigElements() { From df4702679230ddb711d6f96071ac9ec2f837c622 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 4 Nov 2016 09:56:53 +0100 Subject: [PATCH 217/337] Repository: Fix documentation of method getDataSource refs #13034 --- library/Icinga/Repository/Repository.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Repository/Repository.php b/library/Icinga/Repository/Repository.php index 73721c003..541719058 100644 --- a/library/Icinga/Repository/Repository.php +++ b/library/Icinga/Repository/Repository.php @@ -269,12 +269,18 @@ abstract class Repository implements Selectable * @param string $table * * @return Selectable + * + * @throws ProgrammingError In case no datasource is available */ public function getDataSource($table = null) { if ($this->ds === null) { - throw new ProgrammingError('No data source available'); + throw new ProgrammingError( + 'No data source available. It is required to either pass it' + . ' at initialization time or by overriding this method.' + ); } + return $this->ds; } From ca689bc9441170908ee464bc9b6213fe0cb3c26f Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 4 Nov 2016 09:57:56 +0100 Subject: [PATCH 218/337] IniRepository: Improve documentation of attribute $configs refs #13034 --- library/Icinga/Repository/IniRepository.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index e17bbaa42..08d721326 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -20,27 +20,28 @@ use Icinga\Exception\StatementException; *

    *
  • Insert, update and delete capabilities
  • *
  • Triggers for inserts, updates and deletions
  • + *
  • Lazy initialization of table specific configs
  • *
*/ abstract class IniRepository extends Repository implements Extensible, Updatable, Reducible { /** - * Per-table configs + * The configuration files used as table specific datasources * - * Example: + * This must be initialized by concrete repository implementations, in the following format * * array( - * 'event-type' => array( - * 'module' => 'elasticsearch', - * 'path' => 'event-types', - * 'keyColumn' => 'name' + * 'table_name' => array( + * 'config' => 'name_of_the_ini_file_without_extension', + * 'keyColumn' => 'the_name_of_the_column_to_use_as_key_column', + * ['module' => 'the_name_of_the_module_if_any'] * ) * ) * * * @var array */ - protected $configs = null; + protected $configs; /** * The tables for which triggers are available when inserting, updating or deleting rows From ac3e182f0d4a2420ede171aca53c9273d344b5e8 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 4 Nov 2016 09:59:37 +0100 Subject: [PATCH 219/337] IniRepository: Make key column validation in the constructor more readable refs #13034 --- library/Icinga/Repository/IniRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index 08d721326..da1a57c45 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -67,7 +67,7 @@ abstract class IniRepository extends Repository implements Extensible, Updatable { parent::__construct($ds); // First! Due to init(). - if (! ($ds === null || $ds->getConfigObject()->getKeyColumn())) { + if ($ds !== null && !$ds->getConfigObject()->getKeyColumn()) { throw new ProgrammingError('INI repositories require their data source to provide a valid key column'); } } From 3c5fe76cc0addeaa098ffeb4b0239fb46b838847 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 4 Nov 2016 10:04:42 +0100 Subject: [PATCH 220/337] IniRepository: Directly pass the key column to method extractSectionName refs #13034 --- library/Icinga/Repository/IniRepository.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index da1a57c45..725c6df55 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -257,7 +257,7 @@ abstract class IniRepository extends Repository implements Extensible, Updatable { $newData = $this->requireStatementColumns($target, $data); $config = $this->onInsert($target, new ConfigObject($newData)); - $section = $this->extractSectionName($config, $target); + $section = $this->extractSectionName($config, $this->getDataSource($target)->getConfigObject()->getKeyColumn()); if ($this->getDataSource($target)->hasSection($section)) { throw new StatementException(t('Cannot insert. Section "%s" does already exist'), $section); @@ -375,15 +375,14 @@ abstract class IniRepository extends Repository implements Extensible, Updatable * Extract and return the section name off of the given $config * * @param array|ConfigObject $config - * @param string $target The table whose datasource to get the key column from + * @param string $keyColumn * * @return string * * @throws ProgrammingError In case no valid section name is available */ - protected function extractSectionName( & $config, $target) + protected function extractSectionName( & $config, $keyColumn) { - $keyColumn = $this->getDataSource($target)->getConfigObject()->getKeyColumn(); if (! is_array($config) && !$config instanceof ConfigObject) { throw new ProgrammingError('$config is neither an array nor a ConfigObject'); } elseif (! isset($config[$keyColumn])) { From a0db5bea879308e19b1f13283fffd6146528a8e6 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 4 Nov 2016 10:15:56 +0100 Subject: [PATCH 221/337] IniRepository: Don't call getDataSource over and over again refs #13034 --- library/Icinga/Repository/IniRepository.php | 34 ++++++++++++--------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index 725c6df55..74fe72154 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -255,18 +255,20 @@ abstract class IniRepository extends Repository implements Extensible, Updatable */ public function insert($target, array $data) { + $ds = $this->getDataSource($target); $newData = $this->requireStatementColumns($target, $data); - $config = $this->onInsert($target, new ConfigObject($newData)); - $section = $this->extractSectionName($config, $this->getDataSource($target)->getConfigObject()->getKeyColumn()); - if ($this->getDataSource($target)->hasSection($section)) { + $config = $this->onInsert($target, new ConfigObject($newData)); + $section = $this->extractSectionName($config, $ds->getConfigObject()->getKeyColumn()); + + if ($ds->hasSection($section)) { throw new StatementException(t('Cannot insert. Section "%s" does already exist'), $section); } - $this->getDataSource($target)->setSection($section, $config); + $ds->setSection($section, $config); try { - $this->getDataSource($target)->saveIni(); + $ds->saveIni(); } catch (Exception $e) { throw new StatementException(t('Failed to insert. An error occurred: %s'), $e->getMessage()); } @@ -283,8 +285,10 @@ abstract class IniRepository extends Repository implements Extensible, Updatable */ public function update($target, array $data, Filter $filter = null) { + $ds = $this->getDataSource($target); $newData = $this->requireStatementColumns($target, $data); - $keyColumn = $this->getDataSource($target)->getConfigObject()->getKeyColumn(); + + $keyColumn = $ds->getConfigObject()->getKeyColumn(); if ($filter === null && isset($newData[$keyColumn])) { throw new StatementException( t('Cannot update. Column "%s" holds a section\'s name which must be unique'), @@ -292,7 +296,7 @@ abstract class IniRepository extends Repository implements Extensible, Updatable ); } - $query = $this->getDataSource($target)->select(); + $query = $ds->select(); if ($filter !== null) { $query->addFilter($this->requireFilter($target, $filter)); } @@ -320,16 +324,16 @@ abstract class IniRepository extends Repository implements Extensible, Updatable unset($newConfig->$keyColumn); if ($newSection) { - if ($this->getDataSource($target)->hasSection($newSection)) { + if ($ds->hasSection($newSection)) { throw new StatementException(t('Cannot update. Section "%s" does already exist'), $newSection); } - $this->getDataSource($target)->removeSection($section)->setSection( + $ds->removeSection($section)->setSection( $newSection, $this->onUpdate($target, $config, $newConfig) ); } else { - $this->getDataSource($target)->setSection( + $ds->setSection( $section, $this->onUpdate($target, $config, $newConfig) ); @@ -337,7 +341,7 @@ abstract class IniRepository extends Repository implements Extensible, Updatable } try { - $this->getDataSource($target)->saveIni(); + $ds->saveIni(); } catch (Exception $e) { throw new StatementException(t('Failed to update. An error occurred: %s'), $e->getMessage()); } @@ -353,19 +357,21 @@ abstract class IniRepository extends Repository implements Extensible, Updatable */ public function delete($target, Filter $filter = null) { - $query = $this->getDataSource($target)->select(); + $ds = $this->getDataSource($target); + + $query = $ds->select(); if ($filter !== null) { $query->addFilter($this->requireFilter($target, $filter)); } /** @var ConfigObject $config */ foreach ($query as $section => $config) { - $this->getDataSource($target)->removeSection($section); + $ds->removeSection($section); $this->onDelete($target, $config); } try { - $this->getDataSource($target)->saveIni(); + $ds->saveIni(); } catch (Exception $e) { throw new StatementException(t('Failed to delete. An error occurred: %s'), $e->getMessage()); } From c44e164de92052f5cd56bec697e993522d83359d Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 4 Nov 2016 10:16:45 +0100 Subject: [PATCH 222/337] IniRepository: Introduce method createConfig refs #13034 --- library/Icinga/Repository/IniRepository.php | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index 74fe72154..200513049 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -377,6 +377,34 @@ abstract class IniRepository extends Repository implements Extensible, Updatable } } + /** + * Create and return a Config for the given meta and table + * + * @param array $meta + * @param string $table + * + * @return Config + * + * @throws ProgrammingError In case the given meta is invalid + */ + protected function createConfig(array $meta, $table) + { + if (! isset($meta['name'])) { + throw new ProgrammingError('Config file name missing for table "%s"', $table); + } elseif (! isset($meta['keyColumn'])) { + throw new ProgrammingError('Config key column name missing for table "%s"', $table); + } + + if (isset($meta['module'])) { + $config = Config::module($meta['module'], $meta['name']); + } else { + $config = Config::app($meta['name']); + } + + $config->getConfigObject()->setKeyColumn($meta['keyColumn']); + return $config; + } + /** * Extract and return the section name off of the given $config * From ad79e675508b6e39ddac45414f7dc770eac008de Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 4 Nov 2016 10:17:37 +0100 Subject: [PATCH 223/337] IniRepository: Reduce complexity of method getDataSource refs #13034 --- library/Icinga/Repository/IniRepository.php | 102 +++++++++----------- 1 file changed, 45 insertions(+), 57 deletions(-) diff --git a/library/Icinga/Repository/IniRepository.php b/library/Icinga/Repository/IniRepository.php index 200513049..3c30a5e8d 100644 --- a/library/Icinga/Repository/IniRepository.php +++ b/library/Icinga/Repository/IniRepository.php @@ -74,59 +74,56 @@ abstract class IniRepository extends Repository implements Extensible, Updatable /** * {@inheritDoc} + * + * @return Config */ public function getDataSource($table = null) { - if ($this->ds === null) { - if ($table === null) { - $table = $this->getBaseTable(); - } - - if ($this->configs === null) { - $this->configs = $this->initializeConfigs(); - if ($this->configs === null) { - throw new ProgrammingError('Per-table configs missing'); - } - } - - if (! isset($this->configs[$table])) { - throw new ProgrammingError('Config for table "%s" missing', $table); - } - - $config = $this->configs[$table]; - if ($config instanceof Config) { - if (! $config->getConfigObject()->getKeyColumn()) { - throw new ProgrammingError( - 'INI repositories require their data source to provide a valid key column' - ); - } - } elseif (is_array($config)) { - if (! isset($config['path'])) { - throw new ProgrammingError('Path to config for table "%s" missing', $table); - } - if (! isset($config['keyColumn'])) { - throw new ProgrammingError( - 'INI repositories require their data source to provide a valid key column' - ); - } - - $newConfig = isset($config['module']) - ? Config::module($config['module'], $config['path']) - : Config::app($config['path']); - $newConfig->getConfigObject()->setKeyColumn($config['keyColumn']); - $this->configs[$table] = $config = $newConfig; - } else { - throw new ProgrammingError( - 'Config for table "%s" is a %s, expected either Icinga\Application\Config or an associative array', - $table, - is_object($config) ? get_class($config) : gettype($config) - ); - } - - return $config; - } else { + if ($this->ds !== null) { return parent::getDataSource($table); } + + $table = $table ?: $this->getBaseTable(); + $configs = $this->getConfigs(); + if (! isset($configs[$table])) { + throw new ProgrammingError('Config for table "%s" missing', $table); + } elseif (! $configs[$table] instanceof Config) { + $configs[$table] = $this->createConfig($configs[$table], $table); + } + + if (! $configs[$table]->getConfigObject()->getKeyColumn()) { + throw new ProgrammingError( + 'INI repositories require their data source to provide a valid key column' + ); + } + + return $configs[$table]; + } + + /** + * Return the configuration files used as table specific datasources + * + * Calls $this->initializeConfigs() in case $this->configs is null. + * + * @return array + */ + public function getConfigs() + { + if ($this->configs === null) { + $this->configs = $this->initializeConfigs(); + } + + return $this->configs; + } + + /** + * Overwrite this in your repository implementation in case you need to initialize the configs lazily + * + * @return array + */ + protected function initializeConfigs() + { + return array(); } /** @@ -155,15 +152,6 @@ abstract class IniRepository extends Repository implements Extensible, Updatable return array(); } - /** - * Overwrite this in your INI repository implementation in case you need to initialize the configs lazily - * - * @return array - */ - protected function initializeConfigs() - { - } - /** * Run a trigger for the given table and row which is about to be inserted * From 3189b5b12d8cd7190004dfba6e0179f6ca35de1c Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 4 Nov 2016 10:30:36 +0100 Subject: [PATCH 224/337] Make GeneralConfigStep::getSummary() working with all logging types refs #11652 --- .../library/Setup/Steps/GeneralConfigStep.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/setup/library/Setup/Steps/GeneralConfigStep.php b/modules/setup/library/Setup/Steps/GeneralConfigStep.php index 002bd965b..95597f5c3 100644 --- a/modules/setup/library/Setup/Steps/GeneralConfigStep.php +++ b/modules/setup/library/Setup/Steps/GeneralConfigStep.php @@ -70,12 +70,24 @@ class GeneralConfigStep extends Step $loggingHtml = '

' . mt('setup', 'Logging will be disabled.') . '

'; } else { $level = $this->data['generalConfig']['logging_level']; + switch ($type) { + case 'syslog': + $typeText = t('Syslog', 'app.config.logging.type'); + break; + case 'php': + $typeText = t('Webserver Log', 'app.config.logging.type'); + break; + case 'file': + $typeText = t('File', 'app.config.logging.type'); + break; + } + $loggingHtml = '' . '' . '' . '' . '' - . '' + . '' . '' . '' . '' @@ -88,7 +100,7 @@ class GeneralConfigStep extends Step )) . '' . '' . '' - . ($type === 'syslog' ? ( + . ($type !== 'file' ? ( '' . '' ) : ( From 1045d9138925d5428c2c2ca833bf53fb6f018410 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 4 Nov 2016 17:19:35 +0100 Subject: [PATCH 225/337] LoggingConfigForm: only display the Syslog facility option if logging to Syslog --- .../Config/General/LoggingConfigForm.php | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/application/forms/Config/General/LoggingConfigForm.php b/application/forms/Config/General/LoggingConfigForm.php index 46588779c..e1b0e130f 100644 --- a/application/forms/Config/General/LoggingConfigForm.php +++ b/application/forms/Config/General/LoggingConfigForm.php @@ -94,29 +94,31 @@ class LoggingConfigForm extends Form ) ); - if (Platform::isWindows()) { - /* @see https://secure.php.net/manual/en/function.openlog.php */ - $this->addElement( - 'hidden', - 'logging_facility', - array( - 'value' => 'user', - 'disabled' => true - ) - ); - } else { - $facilities = array_keys(SyslogWriter::$facilities); - $this->addElement( - 'select', - 'logging_facility', - array( - 'required' => true, - 'label' => $this->translate('Facility'), - 'description' => $this->translate('The syslog facility to utilize.'), - 'value' => 'user', - 'multiOptions' => array_combine($facilities, $facilities) - ) - ); + if (! isset($formData['logging_log']) || $formData['logging_log'] === 'syslog') { + if (Platform::isWindows()) { + /* @see https://secure.php.net/manual/en/function.openlog.php */ + $this->addElement( + 'hidden', + 'logging_facility', + array( + 'value' => 'user', + 'disabled' => true + ) + ); + } else { + $facilities = array_keys(SyslogWriter::$facilities); + $this->addElement( + 'select', + 'logging_facility', + array( + 'required' => true, + 'label' => $this->translate('Facility'), + 'description' => $this->translate('The syslog facility to utilize.'), + 'value' => 'user', + 'multiOptions' => array_combine($facilities, $facilities) + ) + ); + } } } elseif (isset($formData['logging_log']) && $formData['logging_log'] === 'file') { $this->addElement( From d9330486e9fe36a179d2d0a1e01e608fecff0558 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 4 Nov 2016 17:27:36 +0100 Subject: [PATCH 226/337] Replace ExternalBackend::getRemoteUserEnvvars() with an attribute refs #12164 --- .../Config/UserBackend/ExternalBackendForm.php | 2 +- .../Authentication/User/ExternalBackend.php | 17 +++++++---------- .../application/forms/AuthenticationPage.php | 2 +- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/application/forms/Config/UserBackend/ExternalBackendForm.php b/application/forms/Config/UserBackend/ExternalBackendForm.php index e21ed426f..61303b0f3 100644 --- a/application/forms/Config/UserBackend/ExternalBackendForm.php +++ b/application/forms/Config/UserBackend/ExternalBackendForm.php @@ -57,7 +57,7 @@ class ExternalBackendForm extends Form ) ); - foreach (ExternalBackend::getRemoteUserEnvvars() as $envvar) { + foreach (ExternalBackend::$remoteUserEnvvars as $envvar) { if (ExternalBackend::getRemoteUser($envvar) !== null) { break; } diff --git a/library/Icinga/Authentication/User/ExternalBackend.php b/library/Icinga/Authentication/User/ExternalBackend.php index 98a180788..65ec1029a 100644 --- a/library/Icinga/Authentication/User/ExternalBackend.php +++ b/library/Icinga/Authentication/User/ExternalBackend.php @@ -11,6 +11,13 @@ use Icinga\User; */ class ExternalBackend implements UserBackendInterface { + /** + * Possible variables where to read the user from + * + * @var string[] + */ + public static $remoteUserEnvvars = array('REDIRECT_REMOTE_USER', 'REMOTE_USER'); + /** * The name of this backend * @@ -83,16 +90,6 @@ class ExternalBackend implements UserBackendInterface return null; } - /** - * Get possible variables where to read the user from - * - * @return string[] - */ - public static function getRemoteUserEnvvars() - { - return array('REDIRECT_REMOTE_USER', 'REMOTE_USER'); - } - /** * {@inheritdoc} */ diff --git a/modules/setup/application/forms/AuthenticationPage.php b/modules/setup/application/forms/AuthenticationPage.php index 8fb397edc..97356cab8 100644 --- a/modules/setup/application/forms/AuthenticationPage.php +++ b/modules/setup/application/forms/AuthenticationPage.php @@ -33,7 +33,7 @@ class AuthenticationPage extends Form { if (isset($formData['type']) && $formData['type'] === 'external') { $hasRemoteUser = false; - foreach (ExternalBackend::getRemoteUserEnvvars() as $envvar) { + foreach (ExternalBackend::$remoteUserEnvvars as $envvar) { if (ExternalBackend::getRemoteUser($envvar) !== null) { $hasRemoteUser = true; break; From 846f8ec3d8a59155a187d54e45c668ca75057213 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 4 Nov 2016 17:31:52 +0100 Subject: [PATCH 227/337] ExternalBackendForm: set default username envvar explicitly refs #12164 --- .../forms/Config/UserBackend/ExternalBackendForm.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/application/forms/Config/UserBackend/ExternalBackendForm.php b/application/forms/Config/UserBackend/ExternalBackendForm.php index 61303b0f3..f178f53a2 100644 --- a/application/forms/Config/UserBackend/ExternalBackendForm.php +++ b/application/forms/Config/UserBackend/ExternalBackendForm.php @@ -57,11 +57,17 @@ class ExternalBackendForm extends Form ) ); + $hasRemoteUser = false; foreach (ExternalBackend::$remoteUserEnvvars as $envvar) { if (ExternalBackend::getRemoteUser($envvar) !== null) { + $hasRemoteUser = true; break; } } + if (! $hasRemoteUser) { + $envvar = 'REMOTE_USER'; + } + $this->addElement( 'text', 'username_envvar', From 2b060d9bd42bba8a4cb89bb81d7455643e973fb1 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 7 Nov 2016 10:40:38 +0100 Subject: [PATCH 228/337] Challenge API requests only if the controller requires auth fixes #12580 --- library/Icinga/Application/Web.php | 4 +++ library/Icinga/Authentication/Auth.php | 33 +++++++++++-------- .../Web/Controller/ActionController.php | 16 ++++++--- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/library/Icinga/Application/Web.php b/library/Icinga/Application/Web.php index df5cbf729..1d8097af1 100644 --- a/library/Icinga/Application/Web.php +++ b/library/Icinga/Application/Web.php @@ -411,6 +411,10 @@ class Web extends EmbeddedWeb private function setupUser() { $auth = Auth::getInstance(); + $auth->authenticate(); + if (! $this->request->isXmlHttpRequest() && $this->request->isApiRequest() && ! $auth->isAuthenticated()) { + $auth->authHttp(); + } if ($auth->isAuthenticated()) { $user = $auth->getUser(); $this->getRequest()->setUser($user); diff --git a/library/Icinga/Authentication/Auth.php b/library/Icinga/Authentication/Auth.php index 814f1366e..498495997 100644 --- a/library/Icinga/Authentication/Auth.php +++ b/library/Icinga/Authentication/Auth.php @@ -79,23 +79,31 @@ class Auth } /** - * Whether the user is authenticated - * - * @param bool $ignoreSession True to prevent session authentication + * Authenticate the user * * @return bool */ - public function isAuthenticated($ignoreSession = false) + public function authenticate() { - if ($this->user === null && ! $ignoreSession) { + if ($this->user === null) { $this->authenticateFromSession(); } if ($this->user === null && ! $this->authExternal()) { - return $this->authHttp(); + return false; } return true; } + /** + * Get whether the user is authenticated + * + * @return bool + */ + public function isAuthenticated() + { + return $this->user !== null; + } + public function setAuthenticated(User $user, $persist = true) { $username = $user->getUsername(); @@ -275,15 +283,12 @@ class Auth * * @return bool */ - protected function authHttp() + public function authHttp() { $request = $this->getRequest(); - if ($request->isXmlHttpRequest() || ! $request->isApiRequest()) { - return false; - } $header = $request->getHeader('Authorization'); if (empty($header)) { - $this->challengeHttp(); + return false; } list($scheme) = explode(' ', $header, 2); if ($scheme !== 'Basic') { @@ -294,7 +299,7 @@ class Auth $credentials = array_filter(explode(':', $credentials, 2)); if (count($credentials) !== 2) { // Deny empty username and/or password - $this->challengeHttp(); + return false; } $user = new User($credentials[0]); $password = $credentials[1]; @@ -303,7 +308,7 @@ class Auth $user->setIsHttpUser(true); return true; } else { - $this->challengeHttp(); + return false; } } @@ -312,7 +317,7 @@ class Auth * * Sends the response w/ the 401 Unauthorized status code and WWW-Authenticate header. */ - protected function challengeHttp() + public function challengeHttp() { $response = $this->getResponse(); $response->setHttpResponseCode(401); diff --git a/library/Icinga/Web/Controller/ActionController.php b/library/Icinga/Web/Controller/ActionController.php index 655a85aca..c6030b079 100644 --- a/library/Icinga/Web/Controller/ActionController.php +++ b/library/Icinga/Web/Controller/ActionController.php @@ -99,6 +99,8 @@ class ActionController extends Zend_Controller_Action Zend_Controller_Response_Abstract $response, array $invokeArgs = array() ) { + /** @var \Icinga\Web\Request $request */ + /** @var \Icinga\Web\Response $response */ $this->params = UrlParams::fromQueryString(); $this->setRequest($request) @@ -124,7 +126,11 @@ class ActionController extends Zend_Controller_Action $this->_helper->layout()->disableLayout(); } + // $auth->authenticate($request, $response, $this->requiresLogin()); if ($this->requiresLogin()) { + if (! $request->isXmlHttpRequest() && $request->isApiRequest()) { + Auth::getInstance()->challengeHttp(); + } $this->redirectToLogin(Url::fromRequest()); } @@ -255,8 +261,9 @@ class ActionController extends Zend_Controller_Action /** * Return restriction information for an eventually authenticated user * - * @param string $name Permission name - * @return Array + * @param string $name Restriction name + * + * @return array */ public function getRestrictions($name) { @@ -268,15 +275,14 @@ class ActionController extends Zend_Controller_Action * user is currently not authenticated * * @return bool - * @see requiresAuthentication */ protected function requiresLogin() { - if (!$this->requiresAuthentication) { + if (! $this->requiresAuthentication) { return false; } - return !$this->Auth()->isAuthenticated(); + return ! $this->Auth()->isAuthenticated(); } /** From 93474972bf8594673c19d02b8bada40cf7c5db6c Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 8 Nov 2016 15:27:29 +0100 Subject: [PATCH 229/337] Enable propagate host downtime commands for Icinga 2 This only works when using the latest Icinga 2 snapshot packages. Before releasing Web 2.4.0, we have to include a check to only offer these commands when using Icinga 2 2.6.0+ refs #10774 --- .../ScheduleHostDowntimeCommandForm.php | 34 +++++++++---------- .../Renderer/IcingaApiCommandRenderer.php | 7 ++++ 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php b/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php index 003f3f0aa..d1f43972a 100644 --- a/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php @@ -34,24 +34,22 @@ class ScheduleHostDowntimeCommandForm extends ScheduleServiceDowntimeCommandForm ) ); - if (! $this->getBackend()->isIcinga2()) { - $this->addElement( - 'select', - 'child_hosts', - array( - 'description' => $this->translate( - 'Define what should be done with the child hosts of the hosts.' - ), - 'label' => $this->translate('Child Hosts'), - 'multiOptions' => array( - 0 => $this->translate('Do nothing with child hosts'), - 1 => $this->translate('Schedule triggered downtime for all child hosts'), - 2 => $this->translate('Schedule non-triggered downtime for all child hosts') - ), - 'value' => 0 - ) - ); - } + $this->addElement( + 'select', + 'child_hosts', + array( + 'description' => $this->translate( + 'Define what should be done with the child hosts of the hosts.' + ), + 'label' => $this->translate('Child Hosts'), + 'multiOptions' => array( + 0 => $this->translate('Do nothing with child hosts'), + 1 => $this->translate('Schedule triggered downtime for all child hosts'), + 2 => $this->translate('Schedule non-triggered downtime for all child hosts') + ), + 'value' => 0 + ) + ); return $this; } diff --git a/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php b/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php index cb346bca5..7f6b2956f 100644 --- a/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php +++ b/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php @@ -10,6 +10,7 @@ use Icinga\Module\Monitoring\Command\Object\AddCommentCommand; use Icinga\Module\Monitoring\Command\Object\DeleteCommentCommand; use Icinga\Module\Monitoring\Command\Object\DeleteDowntimeCommand; use Icinga\Module\Monitoring\Command\Object\ProcessCheckResultCommand; +use Icinga\Module\Monitoring\Command\Object\PropagateHostDowntimeCommand; use Icinga\Module\Monitoring\Command\Object\RemoveAcknowledgementCommand; use Icinga\Module\Monitoring\Command\Object\ScheduleServiceCheckCommand; use Icinga\Module\Monitoring\Command\Object\ScheduleServiceDowntimeCommand; @@ -148,6 +149,12 @@ class IcingaApiCommandRenderer implements IcingaCommandRendererInterface 'fixed' => $command->getFixed(), 'trigger_name' => $command->getTriggerId() ); + if ($command->getObject()->getType() === $command::TYPE_HOST + && $command instanceof PropagateHostDowntimeCommand + ) { + /** @var \Icinga\Module\Monitoring\Command\Object\PropagateHostDowntimeCommand $command */ + $data['child_options'] = $command->getTriggered() ? 1 : 2; + } $this->applyFilter($data, $command->getObject()); return IcingaApiCommand::create($endpoint, $data); } From 0ef81a2e0b50fb55248c350be6479b4d40f3e833 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 9 Nov 2016 11:38:04 +0100 Subject: [PATCH 230/337] Response: Use a constant for the default content type There is already setHeader() to adjust this dynamically. refs #12161 --- library/Icinga/Web/Response.php | 39 ++++---------------- library/Icinga/Web/Response/JsonResponse.php | 12 +++--- library/Icinga/Web/StyleSheet.php | 2 +- 3 files changed, 14 insertions(+), 39 deletions(-) diff --git a/library/Icinga/Web/Response.php b/library/Icinga/Web/Response.php index a42f48fde..d58ca4e1a 100644 --- a/library/Icinga/Web/Response.php +++ b/library/Icinga/Web/Response.php @@ -12,6 +12,13 @@ use Icinga\Web\Response\JsonResponse; */ class Response extends Zend_Controller_Response_Http { + /** + * The default content type being used for responses + * + * @var string + */ + const DEFAULT_CONTENT_TYPE = 'text/html'; + /** * Auto-refresh interval * @@ -54,13 +61,6 @@ class Response extends Zend_Controller_Response_Http */ protected $rerenderLayout = false; - /** - * Content type of this response - * - * @var string - */ - protected $contentType = 'text/html'; - /** * Get the auto-refresh interval * @@ -237,29 +237,6 @@ class Response extends Zend_Controller_Response_Http return $this; } - /** - * Set the content type of this response - * - * @param string $contentType - * - * @return $this - */ - public function setContentType($contentType) - { - $this->contentType = $contentType; - return $this; - } - - /** - * Get the content type of this response - * - * @return string - */ - public function getContentType() - { - return $this->contentType; - } - /** * Entry point for HTTP responses in JSON format * @@ -301,7 +278,7 @@ class Response extends Zend_Controller_Response_Http } if (! $this->getHeader('Content-Type', true)) { - $this->setHeader('Content-Type', $this->getContentType()); + $this->setHeader('Content-Type', static::DEFAULT_CONTENT_TYPE); } } diff --git a/library/Icinga/Web/Response/JsonResponse.php b/library/Icinga/Web/Response/JsonResponse.php index 47a2ccd74..744456c82 100644 --- a/library/Icinga/Web/Response/JsonResponse.php +++ b/library/Icinga/Web/Response/JsonResponse.php @@ -11,6 +11,11 @@ use Icinga\Web\Response; */ class JsonResponse extends Response { + /** + * {@inheritdoc} + */ + const DEFAULT_CONTENT_TYPE = 'application/json'; + /** * Status identifier for failed API calls due to an error on the server * @@ -67,13 +72,6 @@ class JsonResponse extends Response */ protected $successData; - /** - * Content type of this response - * - * @var string - */ - protected $contentType = 'application/json'; - /** * Get the JSON encoding options * diff --git a/library/Icinga/Web/StyleSheet.php b/library/Icinga/Web/StyleSheet.php index e74f3c955..549e11c5c 100644 --- a/library/Icinga/Web/StyleSheet.php +++ b/library/Icinga/Web/StyleSheet.php @@ -197,7 +197,7 @@ class StyleSheet $response ->setHeader('Cache-Control', 'public', true) ->setHeader('ETag', $etag, true) - ->setContentType('text/css'); + ->setHeader('Content-Type', 'text/css', true); $cacheFile = 'icinga-' . $etag . ($minified ? '.min' : '') . '.css'; $cache = FileCache::instance(); From 14363e52f42c2fb4d8afa3ebf4024fcf74a2910e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 4 Nov 2016 17:15:06 +0100 Subject: [PATCH 231/337] Revert "Merge branch 'feature/emit-log-messages-in-the-webserver-s-log-11652'" This reverts commit f27e8c059d1d34925e4ecd33fb7aa70004abbbc3. refs #11652 --- .../Config/General/LoggingConfigForm.php | 5 +- .../locale/de_DE/LC_MESSAGES/icinga.mo | Bin 61148 -> 61085 bytes .../locale/de_DE/LC_MESSAGES/icinga.po | 5 -- .../Application/Logger/Writer/PhpWriter.php | 45 ------------------ .../library/Setup/Steps/GeneralConfigStep.php | 16 +------ 5 files changed, 4 insertions(+), 67 deletions(-) delete mode 100644 library/Icinga/Application/Logger/Writer/PhpWriter.php diff --git a/application/forms/Config/General/LoggingConfigForm.php b/application/forms/Config/General/LoggingConfigForm.php index e1b0e130f..75b82a330 100644 --- a/application/forms/Config/General/LoggingConfigForm.php +++ b/application/forms/Config/General/LoggingConfigForm.php @@ -39,7 +39,6 @@ class LoggingConfigForm extends Form 'label' => $this->translate('Logging Type'), 'description' => $this->translate('The type of logging to utilize.'), 'multiOptions' => array( - 'php' => $this->translate('Webserver Log', 'app.config.logging.type'), 'syslog' => 'Syslog', 'file' => $this->translate('File', 'app.config.logging.type'), 'none' => $this->translate('None', 'app.config.logging.type') @@ -65,7 +64,7 @@ class LoggingConfigForm extends Form ); } - if (false === isset($formData['logging_log']) || in_array($formData['logging_log'], array('syslog', 'php'))) { + if (false === isset($formData['logging_log']) || $formData['logging_log'] === 'syslog') { $this->addElement( 'text', 'logging_application', @@ -73,7 +72,7 @@ class LoggingConfigForm extends Form 'required' => true, 'label' => $this->translate('Application Prefix'), 'description' => $this->translate( - 'The name of the application by which to prefix log messages.' + 'The name of the application by which to prefix syslog messages.' ), 'requirement' => $this->translate('The application prefix must not contain whitespace.'), 'value' => 'icingaweb2', diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.mo b/application/locale/de_DE/LC_MESSAGES/icinga.mo index c538f9dc0c0feef93dcbfb8121a1f2dcbb4d6aaf..d6f05c01e5045a61cade2e5c9b7c4e5fb8ea89f0 100644 GIT binary patch delta 10004 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

-w5>Ag{Ymsf=%#iOvan2VvMEDc6(Dzt|-oKiO-bZcZ5c=Xt)DiuJHT3-7rJ)s7yJCJ@t%o;=BQXqvukwD!IIM(U zqMq}wQAO%pGheaB_V;dT@1hj7>vhJ zXM7P=3wKdF3Hr^XxE?BwLEYa9t6?XsioH=24@PBrIQn2QwpOErhE})~HIN52(2JRP>FV?`rp3_n7UD=*m(GR-@dZt8gaSibVH9hPa;HhZ(jVr^mt63-4P|qLD;$4G1 z5i#>C^{(W3Ii{g2%X2WMscTT#^_UEoYnUgs#n-j^kQhkF{t{^)wx z^G|H1tAE+RmUUdNM?AT4vs``4evTXNa%GlvNcha<>f`ZhHOn=kthkj=X4IIeQ{%?v z=RKM`A#QU1gbBHM6XK>9PR*(EaL(9iIR!Iw3c~y3PbiyTl3%GqV#|c&@WhmMiAgOI z5)%@t^%*m5ddtBDWAdg=9y2|^pj~)+LGHNlKDkq;P8pMz7oL_w7&qC5o}@*GT#04( T7rih(;Qz;Nsj;8G?&ta+VrnMS diff --git a/application/locale/de_DE/LC_MESSAGES/icinga.po b/application/locale/de_DE/LC_MESSAGES/icinga.po index 37cf95ea2..f99ff565c 100644 --- a/application/locale/de_DE/LC_MESSAGES/icinga.po +++ b/application/locale/de_DE/LC_MESSAGES/icinga.po @@ -2979,11 +2979,6 @@ msgctxt "app.config.logging.level" msgid "Warning" msgstr "Warnung" -#: /vagrant/application/forms/Config/General/LoggingConfigForm.php:40 -msgctxt "app.config.logging.type" -msgid "Webserver Log" -msgstr "Webserver-Log" - #: /vagrant/application/views/scripts/authentication/login.phtml:5 msgid "" "Welcome to Icinga Web 2. For users of the screen reader Jaws full and " diff --git a/library/Icinga/Application/Logger/Writer/PhpWriter.php b/library/Icinga/Application/Logger/Writer/PhpWriter.php deleted file mode 100644 index 310cd7678..000000000 --- a/library/Icinga/Application/Logger/Writer/PhpWriter.php +++ /dev/null @@ -1,45 +0,0 @@ -ident = $config->get('application', 'icingaweb2'); - } - - /** - * {@inheritdoc} - */ - public function log($severity, $message) - { - if (! error_log($this->ident . ': ' . Logger::$levels[$severity] . ' - ' . ( - ini_get('error_log') === 'syslog' ? str_replace("\n", ' ', $message) : $message - ))) { - throw new NotWritableError('Could not log to ' . (ini_get('error_log') ?: 'SAPI')); - } - } -} diff --git a/modules/setup/library/Setup/Steps/GeneralConfigStep.php b/modules/setup/library/Setup/Steps/GeneralConfigStep.php index 95597f5c3..002bd965b 100644 --- a/modules/setup/library/Setup/Steps/GeneralConfigStep.php +++ b/modules/setup/library/Setup/Steps/GeneralConfigStep.php @@ -70,24 +70,12 @@ class GeneralConfigStep extends Step $loggingHtml = '

' . mt('setup', 'Logging will be disabled.') . '

'; } else { $level = $this->data['generalConfig']['logging_level']; - switch ($type) { - case 'syslog': - $typeText = t('Syslog', 'app.config.logging.type'); - break; - case 'php': - $typeText = t('Webserver Log', 'app.config.logging.type'); - break; - case 'file': - $typeText = t('File', 'app.config.logging.type'); - break; - } - $loggingHtml = '' . '
' . t('Type', 'app.config.logging') . '' . ($type === 'syslog' ? 'Syslog' : t('File', 'app.config.logging.type')) . '' . $typeText . '
' . t('Level', 'app.config.logging') . '
' . t('Application Prefix') . '' . $this->data['generalConfig']['logging_application'] . '
' . '' . '' . '' - . '' + . '' . '' . '' . '' @@ -100,7 +88,7 @@ class GeneralConfigStep extends Step )) . '' . '' . '' - . ($type !== 'file' ? ( + . ($type === 'syslog' ? ( '' . '' ) : ( From 0152a5941dae39903a9ad21258fe2bc0b4f21d07 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 10 Nov 2016 13:10:31 +0100 Subject: [PATCH 232/337] DbQuery: Fix wrong operator precedence when rendering unequal filters fixes #13107 refs #12852 --- library/Icinga/Data/Db/DbQuery.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/library/Icinga/Data/Db/DbQuery.php b/library/Icinga/Data/Db/DbQuery.php index 966e094a4..78605b7ad 100644 --- a/library/Icinga/Data/Db/DbQuery.php +++ b/library/Icinga/Data/Db/DbQuery.php @@ -301,7 +301,7 @@ class DbQuery extends SimpleQuery if ($sign === '=') { return $col . ' IN (' . $this->escapeForSql($expression) . ')'; } elseif ($sign === '!=') { - return $col . ' NOT IN (' . $this->escapeForSql($expression) . ') OR ' . $col . ' IS NULL'; + return sprintf('(%1$s NOT IN (%2$s) OR %1$s IS NULL)', $col, $this->escapeForSql($expression)); } throw new QueryException('Unable to render array expressions with operators other than equal or not equal'); @@ -316,12 +316,15 @@ class DbQuery extends SimpleQuery return new Zend_Db_Expr('FALSE'); } - return $col . ' NOT LIKE ' . $this->escapeForSql($this->escapeWildcards($expression)) - . ' OR ' . $col . ' IS NULL'; - } else { - return $col . ' ' . $sign . ' ' . $this->escapeForSql($expression) . ( - $sign === '!=' ? ' OR ' . $col . ' IS NULL' : '' + return sprintf( + '(%1$s NOT LIKE %2$s OR %1$s IS NULL)', + $col, + $this->escapeForSql($this->escapeWildcards($expression)) ); + } elseif ($sign === '!=') { + return sprintf('(%1$s %2$s %3$s OR %1$s IS NULL)', $col, $sign, $this->escapeForSql($expression)); + } else { + return sprintf('%s %s %s', $col, $sign, $this->escapeForSql($expression)); } } From c1d7d7005b74e7852168bea7e7ea7c3122c7a310 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 10 Nov 2016 14:59:18 +0100 Subject: [PATCH 233/337] Update dompdf to version 0.7.0 and let the Web 2 rpm depend on it --- icingaweb2.spec | 4 +- library/Icinga/File/Pdf.php | 33 +- library/vendor/dompdf/SOURCE | 17 +- library/vendor/dompdf/autoload.inc.php | 28 + library/vendor/dompdf/dompdf.php | 279 - .../dompdf/dompdf_config.custom.inc.php | 41 - library/vendor/dompdf/dompdf_config.inc.php | 395 - .../include/absolute_positioner.cls.php | 125 - .../dompdf/include/abstract_renderer.cls.php | 759 -- .../include/attribute_translator.cls.php | 592 -- .../vendor/dompdf/include/autoload.inc.php | 86 - .../include/block_frame_decorator.cls.php | 234 - .../include/block_frame_reflower.cls.php | 805 -- .../dompdf/include/block_positioner.cls.php | 57 - .../dompdf/include/block_renderer.cls.php | 230 - .../include/cached_pdf_decorator.cls.php | 164 - library/vendor/dompdf/include/canvas.cls.php | 385 - .../dompdf/include/canvas_factory.cls.php | 63 - library/vendor/dompdf/include/cellmap.cls.php | 790 -- .../dompdf/include/cpdf_adapter.cls.php | 877 -- .../vendor/dompdf/include/css_color.cls.php | 287 - library/vendor/dompdf/include/dompdf.cls.php | 1101 --- .../dompdf/include/dompdf_exception.cls.php | 26 - .../include/dompdf_image_exception.cls.php | 26 - .../dompdf/include/fixed_positioner.cls.php | 88 - .../dompdf/include/font_metrics.cls.php | 382 - library/vendor/dompdf/include/frame.cls.php | 1191 --- .../dompdf/include/frame_decorator.cls.php | 717 -- .../dompdf/include/frame_factory.cls.php | 252 - .../dompdf/include/frame_reflower.cls.php | 458 - .../vendor/dompdf/include/frame_tree.cls.php | 241 - .../vendor/dompdf/include/functions.inc.php | 1036 --- .../vendor/dompdf/include/gd_adapter.cls.php | 840 -- .../vendor/dompdf/include/image_cache.cls.php | 185 - .../include/image_frame_decorator.cls.php | 80 - .../include/image_frame_reflower.cls.php | 186 - .../dompdf/include/image_renderer.cls.php | 119 - .../include/inline_frame_decorator.cls.php | 74 - .../include/inline_frame_reflower.cls.php | 66 - .../dompdf/include/inline_positioner.cls.php | 70 - .../dompdf/include/inline_renderer.cls.php | 190 - .../include/javascript_embedder.cls.php | 37 - .../vendor/dompdf/include/line_box.cls.php | 252 - .../list_bullet_frame_decorator.cls.php | 65 - .../list_bullet_frame_reflower.cls.php | 33 - .../list_bullet_image_frame_decorator.cls.php | 143 - .../include/list_bullet_positioner.cls.php | 73 - .../include/list_bullet_renderer.cls.php | 236 - .../include/null_frame_decorator.cls.php | 26 - .../include/null_frame_reflower.cls.php | 21 - .../dompdf/include/null_positioner.cls.php | 23 - .../vendor/dompdf/include/page_cache.cls.php | 126 - .../include/page_frame_decorator.cls.php | 592 -- .../include/page_frame_reflower.cls.php | 186 - .../dompdf/include/pdflib_adapter.cls.php | 1085 --- .../dompdf/include/php_evaluator.cls.php | 48 - .../vendor/dompdf/include/positioner.cls.php | 51 - .../vendor/dompdf/include/renderer.cls.php | 290 - library/vendor/dompdf/include/style.cls.php | 2435 ----- .../vendor/dompdf/include/stylesheet.cls.php | 1418 --- .../table_cell_frame_decorator.cls.php | 102 - .../include/table_cell_frame_reflower.cls.php | 119 - .../include/table_cell_positioner.cls.php | 28 - .../include/table_cell_renderer.cls.php | 155 - .../include/table_frame_decorator.cls.php | 334 - .../include/table_frame_reflower.cls.php | 578 -- .../include/table_row_frame_decorator.cls.php | 48 - .../include/table_row_frame_reflower.cls.php | 61 - .../table_row_group_frame_decorator.cls.php | 66 - .../table_row_group_frame_reflower.cls.php | 59 - .../include/table_row_group_renderer.cls.php | 40 - .../include/table_row_positioner.cls.php | 35 - .../dompdf/include/tcpdf_adapter.cls.php | 628 -- .../include/text_frame_decorator.cls.php | 173 - .../include/text_frame_reflower.cls.php | 441 - .../dompdf/include/text_renderer.cls.php | 152 - library/vendor/dompdf/lib/Cpdf.php | 5122 +++++++++++ library/vendor/dompdf/lib/class.pdf.php | 4617 ---------- .../dompdf/lib/fonts/DejaVuSans-Bold.ttf | Bin 634184 -> 693876 bytes .../dompdf/lib/fonts/DejaVuSans-Bold.ufm | 7863 ++++++++-------- .../lib/fonts/DejaVuSans-BoldOblique.ttf | Bin 574636 -> 632168 bytes .../lib/fonts/DejaVuSans-BoldOblique.ufm | 6838 +++++++------- .../lib/fonts/DejaVuSans-ExtraLight.ttf | Bin 338436 -> 0 bytes .../lib/fonts/DejaVuSans-ExtraLight.ufm | 7580 --------------- .../dompdf/lib/fonts/DejaVuSans-Oblique.ttf | Bin 574456 -> 632416 bytes .../dompdf/lib/fonts/DejaVuSans-Oblique.ufm | 7049 +++++++------- .../vendor/dompdf/lib/fonts/DejaVuSans.ttf | Bin 683528 -> 741536 bytes .../vendor/dompdf/lib/fonts/DejaVuSans.ufm | 8125 +++++++++-------- .../lib/fonts/DejaVuSansCondensed-Bold.ttf | Bin 595416 -> 0 bytes .../lib/fonts/DejaVuSansCondensed-Bold.ufm | 5734 ------------ .../fonts/DejaVuSansCondensed-BoldOblique.ttf | Bin 543704 -> 0 bytes .../fonts/DejaVuSansCondensed-BoldOblique.ufm | 5434 ----------- .../lib/fonts/DejaVuSansCondensed-Oblique.ttf | Bin 539024 -> 0 bytes .../lib/fonts/DejaVuSansCondensed-Oblique.ufm | 5073 ---------- .../dompdf/lib/fonts/DejaVuSansCondensed.ttf | Bin 605792 -> 0 bytes .../dompdf/lib/fonts/DejaVuSansCondensed.ufm | 6454 ------------- .../dompdf/lib/fonts/DejaVuSansMono-Bold.ttf | Bin 303644 -> 318392 bytes .../dompdf/lib/fonts/DejaVuSansMono-Bold.ufm | 5035 +++++----- .../lib/fonts/DejaVuSansMono-BoldOblique.ttf | Bin 224936 -> 239876 bytes .../lib/fonts/DejaVuSansMono-BoldOblique.ufm | 3883 ++++---- .../lib/fonts/DejaVuSansMono-Oblique.ttf | Bin 231000 -> 245948 bytes .../lib/fonts/DejaVuSansMono-Oblique.ufm | 4141 +++++---- .../dompdf/lib/fonts/DejaVuSansMono.ttf | Bin 323288 -> 335068 bytes .../dompdf/lib/fonts/DejaVuSansMono.ufm | 5289 +++++------ .../dompdf/lib/fonts/DejaVuSerif-Bold.ttf | Bin 315052 -> 345364 bytes .../dompdf/lib/fonts/DejaVuSerif-Bold.ufm | 5432 +++++------ .../lib/fonts/DejaVuSerif-BoldItalic.ttf | Bin 302940 -> 336884 bytes .../lib/fonts/DejaVuSerif-BoldItalic.ufm | 5392 +++++------ .../dompdf/lib/fonts/DejaVuSerif-Italic.ttf | Bin 310604 -> 343388 bytes .../dompdf/lib/fonts/DejaVuSerif-Italic.ufm | 5650 ++++++------ .../vendor/dompdf/lib/fonts/DejaVuSerif.ttf | Bin 337444 -> 367260 bytes .../vendor/dompdf/lib/fonts/DejaVuSerif.ufm | 5716 ++++++------ .../lib/fonts/DejaVuSerifCondensed-Bold.ttf | Bin 290556 -> 0 bytes .../lib/fonts/DejaVuSerifCondensed-Bold.ufm | 3636 -------- .../fonts/DejaVuSerifCondensed-BoldItalic.ttf | Bin 302120 -> 0 bytes .../fonts/DejaVuSerifCondensed-BoldItalic.ufm | 3515 ------- .../lib/fonts/DejaVuSerifCondensed-Italic.ttf | Bin 310024 -> 0 bytes .../lib/fonts/DejaVuSerifCondensed-Italic.ufm | 3634 -------- .../dompdf/lib/fonts/DejaVuSerifCondensed.ttf | Bin 304328 -> 0 bytes .../dompdf/lib/fonts/DejaVuSerifCondensed.ufm | 3762 -------- .../fonts/dompdf_font_family_cache.dist.php | 207 +- .../vendor/dompdf/lib/php-font-lib/LICENSE | 456 + .../FontLib}/AdobeFontMetrics.php | 0 .../{classes => src/FontLib}/Autoloader.php | 0 .../{classes => src/FontLib}/BinaryStream.php | 0 .../{classes => src/FontLib}/EOT/File.php | 0 .../{classes => src/FontLib}/EOT/Header.php | 0 .../{classes => src/FontLib}/EncodingMap.php | 0 .../{classes => src/FontLib}/Font.php | 0 .../FontLib}/Glyph/Outline.php | 0 .../FontLib}/Glyph/OutlineComponent.php | 0 .../FontLib}/Glyph/OutlineComposite.php | 0 .../FontLib}/Glyph/OutlineSimple.php | 0 .../{classes => src/FontLib}/Header.php | 0 .../FontLib}/OpenType/File.php | 0 .../FontLib}/OpenType/TableDirectoryEntry.php | 0 .../FontLib}/Table/DirectoryEntry.php | 0 .../{classes => src/FontLib}/Table/Table.php | 0 .../FontLib}/Table/Type/cmap.php | 0 .../FontLib}/Table/Type/glyf.php | 0 .../FontLib}/Table/Type/head.php | 0 .../FontLib}/Table/Type/hhea.php | 0 .../FontLib}/Table/Type/hmtx.php | 0 .../FontLib}/Table/Type/kern.php | 0 .../FontLib}/Table/Type/loca.php | 0 .../FontLib}/Table/Type/maxp.php | 0 .../FontLib}/Table/Type/name.php | 0 .../FontLib}/Table/Type/nameRecord.php | 0 .../FontLib}/Table/Type/os2.php | 0 .../FontLib}/Table/Type/post.php | 0 .../FontLib}/TrueType/Collection.php | 0 .../FontLib}/TrueType/File.php | 0 .../FontLib}/TrueType/Header.php | 0 .../FontLib}/TrueType/TableDirectoryEntry.php | 0 .../{classes => src/FontLib}/WOFF/File.php | 0 .../{classes => src/FontLib}/WOFF/Header.php | 0 .../FontLib}/WOFF/TableDirectoryEntry.php | 0 .../lib/php-svg-lib/src/Svg/DefaultStyle.php | 28 + .../lib/php-svg-lib/src/Svg/Document.php | 353 + .../dompdf/lib/php-svg-lib/src/Svg/Style.php | 485 + .../lib/php-svg-lib/src/Svg/Surface/CPdf.php | 4768 ++++++++++ .../src/Svg/Surface/SurfaceCpdf.php | 425 + .../src/Svg/Surface/SurfaceGmagick.php | 303 + .../src/Svg/Surface/SurfaceInterface.php | 88 + .../src/Svg/Surface/SurfacePDFLib.php | 417 + .../php-svg-lib/src/Svg/Tag/AbstractTag.php | 153 + .../lib/php-svg-lib/src/Svg/Tag/Anchor.php | 14 + .../lib/php-svg-lib/src/Svg/Tag/Circle.php | 31 + .../lib/php-svg-lib/src/Svg/Tag/Ellipse.php | 37 + .../lib/php-svg-lib/src/Svg/Tag/Group.php | 36 + .../lib/php-svg-lib/src/Svg/Tag/Image.php | 62 + .../lib/php-svg-lib/src/Svg/Tag/Line.php | 38 + .../src/Svg/Tag/LinearGradient.php | 18 + .../lib/php-svg-lib/src/Svg/Tag/Path.php | 529 ++ .../lib/php-svg-lib/src/Svg/Tag/Polygon.php | 33 + .../lib/php-svg-lib/src/Svg/Tag/Polyline.php | 31 + .../src/Svg/Tag/RadialGradient.php | 17 + .../lib/php-svg-lib/src/Svg/Tag/Rect.php | 45 + .../lib/php-svg-lib/src/Svg/Tag/Shape.php | 58 + .../lib/php-svg-lib/src/Svg/Tag/Stop.php | 17 + .../lib/php-svg-lib/src/Svg/Tag/Text.php | 56 + .../dompdf/lib/php-svg-lib/src/autoload.php | 17 + library/vendor/dompdf/src/Adapter/CPDF.php | 954 ++ library/vendor/dompdf/src/Adapter/GD.php | 1055 +++ library/vendor/dompdf/src/Adapter/PDFLib.php | 1168 +++ library/vendor/dompdf/src/Autoloader.php | 42 + library/vendor/dompdf/src/Canvas.php | 388 + library/vendor/dompdf/src/CanvasFactory.php | 59 + library/vendor/dompdf/src/Cellmap.php | 912 ++ .../dompdf/src/Css/AttributeTranslator.php | 618 ++ library/vendor/dompdf/src/Css/Color.php | 286 + library/vendor/dompdf/src/Css/Style.php | 2582 ++++++ library/vendor/dompdf/src/Css/Stylesheet.php | 1475 +++ library/vendor/dompdf/src/Dompdf.php | 1480 +++ library/vendor/dompdf/src/Exception.php | 30 + .../dompdf/src/Exception/ImageException.php | 31 + library/vendor/dompdf/src/FontMetrics.php | 603 ++ library/vendor/dompdf/src/Frame.php | 1109 +++ library/vendor/dompdf/src/Frame/Factory.php | 262 + library/vendor/dompdf/src/Frame/FrameList.php | 34 + .../dompdf/src/Frame/FrameListIterator.php | 91 + library/vendor/dompdf/src/Frame/FrameTree.php | 300 + .../dompdf/src/Frame/FrameTreeIterator.php | 96 + .../vendor/dompdf/src/Frame/FrameTreeList.php | 35 + .../FrameDecorator/AbstractFrameDecorator.php | 808 ++ .../dompdf/src/FrameDecorator/Block.php | 252 + .../dompdf/src/FrameDecorator/Image.php | 90 + .../dompdf/src/FrameDecorator/Inline.php | 92 + .../dompdf/src/FrameDecorator/ListBullet.php | 75 + .../src/FrameDecorator/ListBulletImage.php | 155 + .../src/FrameDecorator/NullFrameDecorator.php | 31 + .../vendor/dompdf/src/FrameDecorator/Page.php | 658 ++ .../dompdf/src/FrameDecorator/Table.php | 370 + .../dompdf/src/FrameDecorator/TableCell.php | 117 + .../dompdf/src/FrameDecorator/TableRow.php | 52 + .../src/FrameDecorator/TableRowGroup.php | 72 + .../vendor/dompdf/src/FrameDecorator/Text.php | 182 + .../FrameReflower/AbstractFrameReflower.php | 460 + .../vendor/dompdf/src/FrameReflower/Block.php | 793 ++ .../vendor/dompdf/src/FrameReflower/Image.php | 195 + .../dompdf/src/FrameReflower/Inline.php | 76 + .../dompdf/src/FrameReflower/ListBullet.php | 41 + .../src/FrameReflower/NullFrameReflower.php | 32 + .../vendor/dompdf/src/FrameReflower/Page.php | 199 + .../vendor/dompdf/src/FrameReflower/Table.php | 587 ++ .../dompdf/src/FrameReflower/TableCell.php | 120 + .../dompdf/src/FrameReflower/TableRow.php | 68 + .../src/FrameReflower/TableRowGroup.php | 65 + .../vendor/dompdf/src/FrameReflower/Text.php | 487 + library/vendor/dompdf/src/Helpers.php | 733 ++ library/vendor/dompdf/src/Image/Cache.php | 186 + .../vendor/dompdf/src/JavascriptEmbedder.php | 43 + library/vendor/dompdf/src/LineBox.php | 260 + library/vendor/dompdf/src/Options.php | 974 ++ library/vendor/dompdf/src/PhpEvaluator.php | 55 + .../vendor/dompdf/src/Positioner/Absolute.php | 123 + .../src/Positioner/AbstractPositioner.php | 52 + .../vendor/dompdf/src/Positioner/Block.php | 63 + .../vendor/dompdf/src/Positioner/Fixed.php | 95 + .../vendor/dompdf/src/Positioner/Inline.php | 82 + .../dompdf/src/Positioner/ListBullet.php | 81 + .../dompdf/src/Positioner/NullPositioner.php | 31 + .../dompdf/src/Positioner/TableCell.php | 37 + .../vendor/dompdf/src/Positioner/TableRow.php | 43 + library/vendor/dompdf/src/Renderer.php | 296 + .../dompdf/src/Renderer/AbstractRenderer.php | 768 ++ library/vendor/dompdf/src/Renderer/Block.php | 240 + library/vendor/dompdf/src/Renderer/Image.php | 125 + library/vendor/dompdf/src/Renderer/Inline.php | 198 + .../vendor/dompdf/src/Renderer/ListBullet.php | 248 + .../vendor/dompdf/src/Renderer/TableCell.php | 160 + .../dompdf/src/Renderer/TableRowGroup.php | 44 + library/vendor/dompdf/src/Renderer/Text.php | 158 + 253 files changed, 74842 insertions(+), 107374 deletions(-) create mode 100644 library/vendor/dompdf/autoload.inc.php delete mode 100644 library/vendor/dompdf/dompdf.php delete mode 100644 library/vendor/dompdf/dompdf_config.custom.inc.php delete mode 100644 library/vendor/dompdf/dompdf_config.inc.php delete mode 100644 library/vendor/dompdf/include/absolute_positioner.cls.php delete mode 100644 library/vendor/dompdf/include/abstract_renderer.cls.php delete mode 100644 library/vendor/dompdf/include/attribute_translator.cls.php delete mode 100644 library/vendor/dompdf/include/autoload.inc.php delete mode 100644 library/vendor/dompdf/include/block_frame_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/block_frame_reflower.cls.php delete mode 100644 library/vendor/dompdf/include/block_positioner.cls.php delete mode 100644 library/vendor/dompdf/include/block_renderer.cls.php delete mode 100644 library/vendor/dompdf/include/cached_pdf_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/canvas.cls.php delete mode 100644 library/vendor/dompdf/include/canvas_factory.cls.php delete mode 100644 library/vendor/dompdf/include/cellmap.cls.php delete mode 100644 library/vendor/dompdf/include/cpdf_adapter.cls.php delete mode 100644 library/vendor/dompdf/include/css_color.cls.php delete mode 100644 library/vendor/dompdf/include/dompdf.cls.php delete mode 100644 library/vendor/dompdf/include/dompdf_exception.cls.php delete mode 100644 library/vendor/dompdf/include/dompdf_image_exception.cls.php delete mode 100644 library/vendor/dompdf/include/fixed_positioner.cls.php delete mode 100644 library/vendor/dompdf/include/font_metrics.cls.php delete mode 100644 library/vendor/dompdf/include/frame.cls.php delete mode 100644 library/vendor/dompdf/include/frame_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/frame_factory.cls.php delete mode 100644 library/vendor/dompdf/include/frame_reflower.cls.php delete mode 100644 library/vendor/dompdf/include/frame_tree.cls.php delete mode 100644 library/vendor/dompdf/include/functions.inc.php delete mode 100644 library/vendor/dompdf/include/gd_adapter.cls.php delete mode 100644 library/vendor/dompdf/include/image_cache.cls.php delete mode 100644 library/vendor/dompdf/include/image_frame_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/image_frame_reflower.cls.php delete mode 100644 library/vendor/dompdf/include/image_renderer.cls.php delete mode 100644 library/vendor/dompdf/include/inline_frame_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/inline_frame_reflower.cls.php delete mode 100644 library/vendor/dompdf/include/inline_positioner.cls.php delete mode 100644 library/vendor/dompdf/include/inline_renderer.cls.php delete mode 100644 library/vendor/dompdf/include/javascript_embedder.cls.php delete mode 100644 library/vendor/dompdf/include/line_box.cls.php delete mode 100644 library/vendor/dompdf/include/list_bullet_frame_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/list_bullet_frame_reflower.cls.php delete mode 100644 library/vendor/dompdf/include/list_bullet_image_frame_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/list_bullet_positioner.cls.php delete mode 100644 library/vendor/dompdf/include/list_bullet_renderer.cls.php delete mode 100644 library/vendor/dompdf/include/null_frame_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/null_frame_reflower.cls.php delete mode 100644 library/vendor/dompdf/include/null_positioner.cls.php delete mode 100644 library/vendor/dompdf/include/page_cache.cls.php delete mode 100644 library/vendor/dompdf/include/page_frame_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/page_frame_reflower.cls.php delete mode 100644 library/vendor/dompdf/include/pdflib_adapter.cls.php delete mode 100644 library/vendor/dompdf/include/php_evaluator.cls.php delete mode 100644 library/vendor/dompdf/include/positioner.cls.php delete mode 100644 library/vendor/dompdf/include/renderer.cls.php delete mode 100644 library/vendor/dompdf/include/style.cls.php delete mode 100644 library/vendor/dompdf/include/stylesheet.cls.php delete mode 100644 library/vendor/dompdf/include/table_cell_frame_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/table_cell_frame_reflower.cls.php delete mode 100644 library/vendor/dompdf/include/table_cell_positioner.cls.php delete mode 100644 library/vendor/dompdf/include/table_cell_renderer.cls.php delete mode 100644 library/vendor/dompdf/include/table_frame_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/table_frame_reflower.cls.php delete mode 100644 library/vendor/dompdf/include/table_row_frame_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/table_row_frame_reflower.cls.php delete mode 100644 library/vendor/dompdf/include/table_row_group_frame_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/table_row_group_frame_reflower.cls.php delete mode 100644 library/vendor/dompdf/include/table_row_group_renderer.cls.php delete mode 100644 library/vendor/dompdf/include/table_row_positioner.cls.php delete mode 100644 library/vendor/dompdf/include/tcpdf_adapter.cls.php delete mode 100644 library/vendor/dompdf/include/text_frame_decorator.cls.php delete mode 100644 library/vendor/dompdf/include/text_frame_reflower.cls.php delete mode 100644 library/vendor/dompdf/include/text_renderer.cls.php create mode 100644 library/vendor/dompdf/lib/Cpdf.php delete mode 100644 library/vendor/dompdf/lib/class.pdf.php delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSans-ExtraLight.ttf delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSans-ExtraLight.ufm delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSansCondensed-Bold.ttf delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSansCondensed-Bold.ufm delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSansCondensed-BoldOblique.ttf delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSansCondensed-BoldOblique.ufm delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSansCondensed-Oblique.ttf delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSansCondensed-Oblique.ufm delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSansCondensed.ttf delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSansCondensed.ufm delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSerifCondensed-Bold.ttf delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSerifCondensed-Bold.ufm delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSerifCondensed-BoldItalic.ttf delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSerifCondensed-BoldItalic.ufm delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSerifCondensed-Italic.ttf delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSerifCondensed-Italic.ufm delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSerifCondensed.ttf delete mode 100644 library/vendor/dompdf/lib/fonts/DejaVuSerifCondensed.ufm create mode 100644 library/vendor/dompdf/lib/php-font-lib/LICENSE rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/AdobeFontMetrics.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Autoloader.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/BinaryStream.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/EOT/File.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/EOT/Header.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/EncodingMap.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Font.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Glyph/Outline.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Glyph/OutlineComponent.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Glyph/OutlineComposite.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Glyph/OutlineSimple.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Header.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/OpenType/File.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/OpenType/TableDirectoryEntry.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/DirectoryEntry.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/Table.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/Type/cmap.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/Type/glyf.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/Type/head.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/Type/hhea.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/Type/hmtx.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/Type/kern.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/Type/loca.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/Type/maxp.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/Type/name.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/Type/nameRecord.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/Type/os2.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/Table/Type/post.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/TrueType/Collection.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/TrueType/File.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/TrueType/Header.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/TrueType/TableDirectoryEntry.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/WOFF/File.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/WOFF/Header.php (100%) rename library/vendor/dompdf/lib/php-font-lib/{classes => src/FontLib}/WOFF/TableDirectoryEntry.php (100%) create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/DefaultStyle.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Document.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Style.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/CPdf.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceCpdf.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceGmagick.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfaceInterface.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Surface/SurfacePDFLib.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/AbstractTag.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Anchor.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Circle.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Ellipse.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Group.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Image.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Line.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/LinearGradient.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Path.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polygon.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Polyline.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/RadialGradient.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Rect.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Shape.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Stop.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/Svg/Tag/Text.php create mode 100644 library/vendor/dompdf/lib/php-svg-lib/src/autoload.php create mode 100644 library/vendor/dompdf/src/Adapter/CPDF.php create mode 100644 library/vendor/dompdf/src/Adapter/GD.php create mode 100644 library/vendor/dompdf/src/Adapter/PDFLib.php create mode 100644 library/vendor/dompdf/src/Autoloader.php create mode 100644 library/vendor/dompdf/src/Canvas.php create mode 100644 library/vendor/dompdf/src/CanvasFactory.php create mode 100644 library/vendor/dompdf/src/Cellmap.php create mode 100644 library/vendor/dompdf/src/Css/AttributeTranslator.php create mode 100644 library/vendor/dompdf/src/Css/Color.php create mode 100644 library/vendor/dompdf/src/Css/Style.php create mode 100644 library/vendor/dompdf/src/Css/Stylesheet.php create mode 100644 library/vendor/dompdf/src/Dompdf.php create mode 100644 library/vendor/dompdf/src/Exception.php create mode 100644 library/vendor/dompdf/src/Exception/ImageException.php create mode 100644 library/vendor/dompdf/src/FontMetrics.php create mode 100644 library/vendor/dompdf/src/Frame.php create mode 100644 library/vendor/dompdf/src/Frame/Factory.php create mode 100644 library/vendor/dompdf/src/Frame/FrameList.php create mode 100644 library/vendor/dompdf/src/Frame/FrameListIterator.php create mode 100644 library/vendor/dompdf/src/Frame/FrameTree.php create mode 100644 library/vendor/dompdf/src/Frame/FrameTreeIterator.php create mode 100644 library/vendor/dompdf/src/Frame/FrameTreeList.php create mode 100644 library/vendor/dompdf/src/FrameDecorator/AbstractFrameDecorator.php create mode 100644 library/vendor/dompdf/src/FrameDecorator/Block.php create mode 100644 library/vendor/dompdf/src/FrameDecorator/Image.php create mode 100644 library/vendor/dompdf/src/FrameDecorator/Inline.php create mode 100644 library/vendor/dompdf/src/FrameDecorator/ListBullet.php create mode 100644 library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php create mode 100644 library/vendor/dompdf/src/FrameDecorator/NullFrameDecorator.php create mode 100644 library/vendor/dompdf/src/FrameDecorator/Page.php create mode 100644 library/vendor/dompdf/src/FrameDecorator/Table.php create mode 100644 library/vendor/dompdf/src/FrameDecorator/TableCell.php create mode 100644 library/vendor/dompdf/src/FrameDecorator/TableRow.php create mode 100644 library/vendor/dompdf/src/FrameDecorator/TableRowGroup.php create mode 100644 library/vendor/dompdf/src/FrameDecorator/Text.php create mode 100644 library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php create mode 100644 library/vendor/dompdf/src/FrameReflower/Block.php create mode 100644 library/vendor/dompdf/src/FrameReflower/Image.php create mode 100644 library/vendor/dompdf/src/FrameReflower/Inline.php create mode 100644 library/vendor/dompdf/src/FrameReflower/ListBullet.php create mode 100644 library/vendor/dompdf/src/FrameReflower/NullFrameReflower.php create mode 100644 library/vendor/dompdf/src/FrameReflower/Page.php create mode 100644 library/vendor/dompdf/src/FrameReflower/Table.php create mode 100644 library/vendor/dompdf/src/FrameReflower/TableCell.php create mode 100644 library/vendor/dompdf/src/FrameReflower/TableRow.php create mode 100644 library/vendor/dompdf/src/FrameReflower/TableRowGroup.php create mode 100644 library/vendor/dompdf/src/FrameReflower/Text.php create mode 100644 library/vendor/dompdf/src/Helpers.php create mode 100644 library/vendor/dompdf/src/Image/Cache.php create mode 100644 library/vendor/dompdf/src/JavascriptEmbedder.php create mode 100644 library/vendor/dompdf/src/LineBox.php create mode 100644 library/vendor/dompdf/src/Options.php create mode 100644 library/vendor/dompdf/src/PhpEvaluator.php create mode 100644 library/vendor/dompdf/src/Positioner/Absolute.php create mode 100644 library/vendor/dompdf/src/Positioner/AbstractPositioner.php create mode 100644 library/vendor/dompdf/src/Positioner/Block.php create mode 100644 library/vendor/dompdf/src/Positioner/Fixed.php create mode 100644 library/vendor/dompdf/src/Positioner/Inline.php create mode 100644 library/vendor/dompdf/src/Positioner/ListBullet.php create mode 100644 library/vendor/dompdf/src/Positioner/NullPositioner.php create mode 100644 library/vendor/dompdf/src/Positioner/TableCell.php create mode 100644 library/vendor/dompdf/src/Positioner/TableRow.php create mode 100644 library/vendor/dompdf/src/Renderer.php create mode 100644 library/vendor/dompdf/src/Renderer/AbstractRenderer.php create mode 100644 library/vendor/dompdf/src/Renderer/Block.php create mode 100644 library/vendor/dompdf/src/Renderer/Image.php create mode 100644 library/vendor/dompdf/src/Renderer/Inline.php create mode 100644 library/vendor/dompdf/src/Renderer/ListBullet.php create mode 100644 library/vendor/dompdf/src/Renderer/TableCell.php create mode 100644 library/vendor/dompdf/src/Renderer/TableRowGroup.php create mode 100644 library/vendor/dompdf/src/Renderer/Text.php diff --git a/icingaweb2.spec b/icingaweb2.spec index 8a660b957..8cf242c50 100644 --- a/icingaweb2.spec +++ b/icingaweb2.spec @@ -41,7 +41,7 @@ Requires: apache2-mod_php5 %{?suse_version:Requires(pre): pwdutils} Requires: %{name}-common = %{version}-%{release} Requires: php-Icinga = %{version}-%{release} -Requires: %{name}-vendor-dompdf +Requires: %{name}-vendor-dompdf = 0.7.0-1%{?dist} Requires: %{name}-vendor-HTMLPurifier Requires: %{name}-vendor-JShrink Requires: %{name}-vendor-lessphp @@ -106,7 +106,7 @@ Icinga CLI %package vendor-dompdf -Version: 0.6.2 +Version: 0.7.0 Release: 1%{?dist} Summary: Icinga Web 2 vendor library dompdf Group: Development/Libraries diff --git a/library/Icinga/File/Pdf.php b/library/Icinga/File/Pdf.php index 51a100734..7bb370c2b 100644 --- a/library/Icinga/File/Pdf.php +++ b/library/Icinga/File/Pdf.php @@ -3,28 +3,16 @@ namespace Icinga\File; -use DOMPDF; -use DOMDocument; -use DOMXPath; -use Font_Metrics; +use Dompdf\Dompdf; +use Dompdf\Options; use Icinga\Application\Icinga; -use Icinga\Web\StyleSheet; -use Icinga\Web\Url; use Icinga\Exception\ProgrammingError; +use Icinga\Web\Url; -require_once 'dompdf/dompdf_config.inc.php'; -require_once 'dompdf/include/autoload.inc.php'; +require_once 'dompdf/autoload.inc.php'; -class Pdf extends DOMPDF +class Pdf { - public $paginateTable = false; - - public function __construct() - { - $this->set_paper('A4', 'portrait'); - parent::__construct(); - } - protected function assertNoHeadersSent() { if (headers_sent()) { @@ -50,12 +38,15 @@ class Pdf extends DOMPDF $html = $layout->render(); $imgDir = Url::fromPath('img'); $html = preg_replace('~src="' . $imgDir . '/~', 'src="' . Icinga::app()->getBootstrapDirectory() . '/img/', $html); - $this->load_html($html); - $this->render(); + $options = new Options(); + $options->set('defaultPaperSize', 'A4'); + $dompdf = new Dompdf($options); + $dompdf->loadHtml($html); + $dompdf->render(); $request = $controller->getRequest(); - $this->stream( + $dompdf->stream( sprintf( - '%s-%s-%d.pdf', + '%s-%s-%d', $request->getControllerName(), $request->getActionName(), time() diff --git a/library/vendor/dompdf/SOURCE b/library/vendor/dompdf/SOURCE index babe1b191..3eb54e1fd 100644 --- a/library/vendor/dompdf/SOURCE +++ b/library/vendor/dompdf/SOURCE @@ -1,9 +1,16 @@ -curl https://codeload.github.com/dompdf/dompdf/tar.gz/v0.6.2 -o dompdf-0.6.2.tar.gz -tar xzf dompdf-0.6.2.tar.gz --strip-components 1 --wildcards dompdf-0.6.2/{include/\*.php,lib,dompdf\*.php,LICENSE.LGPL} -rm dompdf-0.6.2.tar.gz +GLOBIGNORE=$0; rm -rf * + +curl https://codeload.github.com/dompdf/dompdf/tar.gz/v0.7.0 -o dompdf-0.7.0.tar.gz +tar xzf dompdf-0.7.0.tar.gz --strip-components 1 dompdf-0.7.0/{lib,src,autoload.inc.php,LICENSE.LGPL} +rm dompdf-0.7.0.tar.gz mv LICENSE.LGPL LICENSE curl https://codeload.github.com/PhenX/php-font-lib/tar.gz/0.4 -o php-font-lib-0.4.tar.gz -mkdir -p lib/php-font-lib/classes -tar xzf php-font-lib-0.4.tar.gz --strip-components 3 -C lib/php-font-lib/classes php-font-lib-0.4/src/FontLib +mkdir -p lib/php-font-lib +tar xzf php-font-lib-0.4.tar.gz --strip-components 1 -C lib/php-font-lib php-font-lib-0.4/{src,LICENSE} rm php-font-lib-0.4.tar.gz + +curl https://codeload.github.com/PhenX/php-svg-lib/tar.gz/v0.1 -o php-svg-lib-0.1.tar.gz +mkdir -p lib/php-svg-lib +tar xzf php-svg-lib-0.1.tar.gz --strip-components 1 -C lib/php-svg-lib php-svg-lib-0.1/src +rm php-svg-lib-0.1.tar.gz diff --git a/library/vendor/dompdf/autoload.inc.php b/library/vendor/dompdf/autoload.inc.php new file mode 100644 index 000000000..c5c32f193 --- /dev/null +++ b/library/vendor/dompdf/autoload.inc.php @@ -0,0 +1,28 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +/** + * Dompdf autoload function + * + * If you have an existing autoload function, add a call to this function + * from your existing __autoload() implementation. + * + * @param string $class + */ + +require_once __DIR__ . '/lib/html5lib/Parser.php'; +require_once __DIR__ . '/lib/php-font-lib/src/FontLib/Autoloader.php'; +require_once __DIR__ . '/lib/php-svg-lib/src/autoload.php'; + +/* + * New PHP 5.3.0 namespaced autoloader + */ +require_once __DIR__ . '/src/Autoloader.php'; + +Dompdf\Autoloader::register(); diff --git a/library/vendor/dompdf/dompdf.php b/library/vendor/dompdf/dompdf.php deleted file mode 100644 index dc3551b73..000000000 --- a/library/vendor/dompdf/dompdf.php +++ /dev/null @@ -1,279 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Display command line usage - */ -function dompdf_usage() { - $default_paper_size = DOMPDF_DEFAULT_PAPER_SIZE; - - echo <<set_option('enable_php', false); - - if ( isset($_GET["input_file"]) ) - $file = rawurldecode($_GET["input_file"]); - else - throw new DOMPDF_Exception("An input file is required (i.e. input_file _GET variable)."); - - if ( isset($_GET["paper"]) ) - $paper = rawurldecode($_GET["paper"]); - else - $paper = DOMPDF_DEFAULT_PAPER_SIZE; - - if ( isset($_GET["orientation"]) ) - $orientation = rawurldecode($_GET["orientation"]); - else - $orientation = "portrait"; - - if ( isset($_GET["base_path"]) ) { - $base_path = rawurldecode($_GET["base_path"]); - $file = $base_path . $file; # Set the input file - } - - if ( isset($_GET["options"]) ) { - $options = $_GET["options"]; - } - - $file_parts = explode_url($file); - - $outfile = "dompdf_out.pdf"; # Don't allow them to set the output file - $save_file = false; # Don't save the file - - break; -} - -if ( $file === "-" ) { - $str = ""; - while ( !feof(STDIN) ) - $str .= fread(STDIN, 4096); - - $dompdf->load_html($str); - -} else - $dompdf->load_html_file($file); - -if ( isset($base_path) ) { - $dompdf->set_base_path($base_path); -} - -$dompdf->set_paper($paper, $orientation); - -$dompdf->render(); - -if ( $_dompdf_show_warnings ) { - global $_dompdf_warnings; - foreach ($_dompdf_warnings as $msg) - echo $msg . "\n"; - echo $dompdf->get_canvas()->get_cpdf()->messages; - flush(); -} - -if ( $save_file ) { -// if ( !is_writable($outfile) ) -// throw new DOMPDF_Exception("'$outfile' is not writable."); - if ( strtolower(DOMPDF_PDF_BACKEND) === "gd" ) - $outfile = str_replace(".pdf", ".png", $outfile); - - list($proto, $host, $path, $file) = explode_url($outfile); - if ( $proto != "" ) // i.e. not file:// - $outfile = $file; // just save it locally, FIXME? could save it like wget: ./host/basepath/file - - $outfile = realpath(dirname($outfile)) . DIRECTORY_SEPARATOR . basename($outfile); - - if ( strpos($outfile, DOMPDF_CHROOT) !== 0 ) - throw new DOMPDF_Exception("Permission denied."); - - file_put_contents($outfile, $dompdf->output( array("compress" => 0) )); - exit(0); -} - -if ( !headers_sent() ) { - $dompdf->stream($outfile, $options); -} diff --git a/library/vendor/dompdf/dompdf_config.custom.inc.php b/library/vendor/dompdf/dompdf_config.custom.inc.php deleted file mode 100644 index e9c0899d5..000000000 --- a/library/vendor/dompdf/dompdf_config.custom.inc.php +++ /dev/null @@ -1,41 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @author Brian Sweeney - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -if ( class_exists( 'DOMPDF' , false ) ) { return; } - -PHP_VERSION >= 5.0 or die("DOMPDF requires PHP 5.0+"); - -/** - * The root of your DOMPDF installation - */ -define("DOMPDF_DIR", str_replace(DIRECTORY_SEPARATOR, '/', realpath(dirname(__FILE__)))); - -/** - * The location of the DOMPDF include directory - */ -define("DOMPDF_INC_DIR", DOMPDF_DIR . "/include"); - -/** - * The location of the DOMPDF lib directory - */ -define("DOMPDF_LIB_DIR", DOMPDF_DIR . "/lib"); - -/** - * Some installations don't have $_SERVER['DOCUMENT_ROOT'] - * http://fyneworks.blogspot.com/2007/08/php-documentroot-in-iis-windows-servers.html - */ -if( !isset($_SERVER['DOCUMENT_ROOT']) ) { - $path = ""; - - if ( isset($_SERVER['SCRIPT_FILENAME']) ) - $path = $_SERVER['SCRIPT_FILENAME']; - elseif ( isset($_SERVER['PATH_TRANSLATED']) ) - $path = str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']); - - $_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr($path, 0, 0-strlen($_SERVER['PHP_SELF']))); -} - -/** Include the custom config file if it exists */ -if ( file_exists(DOMPDF_DIR . "/dompdf_config.custom.inc.php") ){ - require_once(DOMPDF_DIR . "/dompdf_config.custom.inc.php"); -} - -//FIXME: Some function definitions rely on the constants defined by DOMPDF. However, might this location prove problematic? -require_once(DOMPDF_INC_DIR . "/functions.inc.php"); - -/** - * Username and password used by the configuration utility in www/ - */ -def("DOMPDF_ADMIN_USERNAME", "user"); -def("DOMPDF_ADMIN_PASSWORD", "password"); - -/** - * The location of the DOMPDF font directory - * - * The location of the directory where DOMPDF will store fonts and font metrics - * Note: This directory must exist and be writable by the webserver process. - * *Please note the trailing slash.* - * - * Notes regarding fonts: - * Additional .afm font metrics can be added by executing load_font.php from command line. - * - * Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must - * be embedded in the pdf file or the PDF may not display correctly. This can significantly - * increase file size unless font subsetting is enabled. Before embedding a font please - * review your rights under the font license. - * - * Any font specification in the source HTML is translated to the closest font available - * in the font directory. - * - * The pdf standard "Base 14 fonts" are: - * Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique, - * Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique, - * Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic, - * Symbol, ZapfDingbats. - */ -def("DOMPDF_FONT_DIR", DOMPDF_DIR . "/lib/fonts/"); - -/** - * The location of the DOMPDF font cache directory - * - * This directory contains the cached font metrics for the fonts used by DOMPDF. - * This directory can be the same as DOMPDF_FONT_DIR - * - * Note: This directory must exist and be writable by the webserver process. - */ -def("DOMPDF_FONT_CACHE", DOMPDF_FONT_DIR); - -/** - * The location of a temporary directory. - * - * The directory specified must be writeable by the webserver process. - * The temporary directory is required to download remote images and when - * using the PFDLib back end. - */ -def("DOMPDF_TEMP_DIR", sys_get_temp_dir()); - -/** - * ==== IMPORTANT ==== - * - * dompdf's "chroot": Prevents dompdf from accessing system files or other - * files on the webserver. All local files opened by dompdf must be in a - * subdirectory of this directory. DO NOT set it to '/' since this could - * allow an attacker to use dompdf to read any files on the server. This - * should be an absolute path. - * This is only checked on command line call by dompdf.php, but not by - * direct class use like: - * $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output(); - */ -def("DOMPDF_CHROOT", realpath(DOMPDF_DIR)); - -/** - * Whether to use Unicode fonts or not. - * - * When set to true the PDF backend must be set to "CPDF" and fonts must be - * loaded via load_font.php. - * - * When enabled, dompdf can support all Unicode glyphs. Any glyphs used in a - * document must be present in your fonts, however. - */ -def("DOMPDF_UNICODE_ENABLED", true); - -/** - * Whether to enable font subsetting or not. - */ -def("DOMPDF_ENABLE_FONTSUBSETTING", false); - -/** - * The PDF rendering backend to use - * - * Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and - * 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will - * fall back on CPDF. 'GD' renders PDFs to graphic files. {@link - * Canvas_Factory} ultimately determines which rendering class to instantiate - * based on this setting. - * - * Both PDFLib & CPDF rendering backends provide sufficient rendering - * capabilities for dompdf, however additional features (e.g. object, - * image and font support, etc.) differ between backends. Please see - * {@link PDFLib_Adapter} for more information on the PDFLib backend - * and {@link CPDF_Adapter} and lib/class.pdf.php for more information - * on CPDF. Also see the documentation for each backend at the links - * below. - * - * The GD rendering backend is a little different than PDFLib and - * CPDF. Several features of CPDF and PDFLib are not supported or do - * not make any sense when creating image files. For example, - * multiple pages are not supported, nor are PDF 'objects'. Have a - * look at {@link GD_Adapter} for more information. GD support is - * experimental, so use it at your own risk. - * - * @link http://www.pdflib.com - * @link http://www.ros.co.nz/pdf - * @link http://www.php.net/image - */ -def("DOMPDF_PDF_BACKEND", "CPDF"); - -/** - * PDFlib license key - * - * If you are using a licensed, commercial version of PDFlib, specify - * your license key here. If you are using PDFlib-Lite or are evaluating - * the commercial version of PDFlib, comment out this setting. - * - * @link http://www.pdflib.com - * - * If pdflib present in web server and auto or selected explicitely above, - * a real license code must exist! - */ -//def("DOMPDF_PDFLIB_LICENSE", "your license key here"); - -/** - * html target media view which should be rendered into pdf. - * List of types and parsing rules for future extensions: - * http://www.w3.org/TR/REC-html40/types.html - * screen, tty, tv, projection, handheld, print, braille, aural, all - * Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3. - * Note, even though the generated pdf file is intended for print output, - * the desired content might be different (e.g. screen or projection view of html file). - * Therefore allow specification of content here. - */ -def("DOMPDF_DEFAULT_MEDIA_TYPE", "screen"); - -/** - * The default paper size. - * - * North America standard is "letter"; other countries generally "a4" - * - * @see CPDF_Adapter::PAPER_SIZES for valid sizes - */ -def("DOMPDF_DEFAULT_PAPER_SIZE", "letter"); - -/** - * The default font family - * - * Used if no suitable fonts can be found. This must exist in the font folder. - * @var string - */ -def("DOMPDF_DEFAULT_FONT", "serif"); - -/** - * Image DPI setting - * - * This setting determines the default DPI setting for images and fonts. The - * DPI may be overridden for inline images by explictly setting the - * image's width & height style attributes (i.e. if the image's native - * width is 600 pixels and you specify the image's width as 72 points, - * the image will have a DPI of 600 in the rendered PDF. The DPI of - * background images can not be overridden and is controlled entirely - * via this parameter. - * - * For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI). - * If a size in html is given as px (or without unit as image size), - * this tells the corresponding size in pt at 72 DPI. - * This adjusts the relative sizes to be similar to the rendering of the - * html page in a reference browser. - * - * In pdf, always 1 pt = 1/72 inch - * - * Rendering resolution of various browsers in px per inch: - * Windows Firefox and Internet Explorer: - * SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:? - * Linux Firefox: - * about:config *resolution: Default:96 - * (xorg screen dimension in mm and Desktop font dpi settings are ignored) - * - * Take care about extra font/image zoom factor of browser. - * - * In images, size in pixel attribute, img css style, are overriding - * the real image dimension in px for rendering. - * - * @var int - */ -def("DOMPDF_DPI", 96); - -/** - * Enable inline PHP - * - * If this setting is set to true then DOMPDF will automatically evaluate - * inline PHP contained within tags. - * - * Attention! - * Enabling this for documents you do not trust (e.g. arbitrary remote html - * pages) is a security risk. Inline scripts are run with the same level of - * system access available to dompdf. Set this option to false (recommended) - * if you wish to process untrusted documents. - * - * @var bool - */ -def("DOMPDF_ENABLE_PHP", false); - -/** - * Enable inline Javascript - * - * If this setting is set to true then DOMPDF will automatically insert - * JavaScript code contained within tags. - * - * @var bool - */ -def("DOMPDF_ENABLE_JAVASCRIPT", true); - -/** - * Enable remote file access - * - * If this setting is set to true, DOMPDF will access remote sites for - * images and CSS files as required. - * This is required for part of test case www/test/image_variants.html through www/examples.php - * - * Attention! - * This can be a security risk, in particular in combination with DOMPDF_ENABLE_PHP and - * allowing remote access to dompdf.php or on allowing remote html code to be passed to - * $dompdf = new DOMPDF(); $dompdf->load_html(...); - * This allows anonymous users to download legally doubtful internet content which on - * tracing back appears to being downloaded by your server, or allows malicious php code - * in remote html pages to be executed by your server with your account privileges. - * - * @var bool - */ -def("DOMPDF_ENABLE_REMOTE", false); - -/** - * The debug output log - * @var string - */ -def("DOMPDF_LOG_OUTPUT_FILE", DOMPDF_FONT_DIR."log.htm"); - -/** - * A ratio applied to the fonts height to be more like browsers' line height - */ -def("DOMPDF_FONT_HEIGHT_RATIO", 1.1); - -/** - * Enable CSS float - * - * Allows people to disabled CSS float support - * @var bool - */ -def("DOMPDF_ENABLE_CSS_FLOAT", false); - -/** - * Enable the built in DOMPDF autoloader - * - * @var bool - */ -def("DOMPDF_ENABLE_AUTOLOAD", true); - -/** - * Prepend the DOMPDF autoload function to the spl_autoload stack - * - * @var bool - */ -def("DOMPDF_AUTOLOAD_PREPEND", false); - -/** - * Use the more-than-experimental HTML5 Lib parser - */ -def("DOMPDF_ENABLE_HTML5PARSER", false); -require_once(DOMPDF_LIB_DIR . "/html5lib/Parser.php"); - -// ### End of user-configurable options ### - -/** - * Load autoloader - */ -if (DOMPDF_ENABLE_AUTOLOAD) { - require_once(DOMPDF_INC_DIR . "/autoload.inc.php"); - require_once(DOMPDF_LIB_DIR . "/php-font-lib/classes/Font.php"); -} - -/** - * Ensure that PHP is working with text internally using UTF8 character encoding. - */ -mb_internal_encoding('UTF-8'); - -/** - * Global array of warnings generated by DomDocument parser and - * stylesheet class - * - * @var array - */ -global $_dompdf_warnings; -$_dompdf_warnings = array(); - -/** - * If true, $_dompdf_warnings is dumped on script termination when using - * dompdf/dompdf.php or after rendering when using the DOMPDF class. - * When using the class, setting this value to true will prevent you from - * streaming the PDF. - * - * @var bool - */ -global $_dompdf_show_warnings; -$_dompdf_show_warnings = false; - -/** - * If true, the entire tree is dumped to stdout in dompdf.cls.php. - * Setting this value to true will prevent you from streaming the PDF. - * - * @var bool - */ -global $_dompdf_debug; -$_dompdf_debug = false; - -/** - * Array of enabled debug message types - * - * @var array - */ -global $_DOMPDF_DEBUG_TYPES; -$_DOMPDF_DEBUG_TYPES = array(); //array("page-break" => 1); - -/* Optionally enable different classes of debug output before the pdf content. - * Visible if displaying pdf as text, - * E.g. on repeated display of same pdf in browser when pdf is not taken out of - * the browser cache and the premature output prevents setting of the mime type. - */ -def('DEBUGPNG', false); -def('DEBUGKEEPTEMP', false); -def('DEBUGCSS', false); - -/* Layout debugging. Will display rectangles around different block levels. - * Visible in the PDF itself. - */ -def('DEBUG_LAYOUT', false); -def('DEBUG_LAYOUT_LINES', true); -def('DEBUG_LAYOUT_BLOCKS', true); -def('DEBUG_LAYOUT_INLINE', true); -def('DEBUG_LAYOUT_PADDINGBOX', true); diff --git a/library/vendor/dompdf/include/absolute_positioner.cls.php b/library/vendor/dompdf/include/absolute_positioner.cls.php deleted file mode 100644 index 050db49f7..000000000 --- a/library/vendor/dompdf/include/absolute_positioner.cls.php +++ /dev/null @@ -1,125 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Positions absolutely positioned frames - */ -class Absolute_Positioner extends Positioner { - - function __construct(Frame_Decorator $frame) { parent::__construct($frame); } - - function position() { - - $frame = $this->_frame; - $style = $frame->get_style(); - - $p = $frame->find_positionned_parent(); - - list($x, $y, $w, $h) = $frame->get_containing_block(); - - $top = $style->length_in_pt($style->top, $h); - $right = $style->length_in_pt($style->right, $w); - $bottom = $style->length_in_pt($style->bottom, $h); - $left = $style->length_in_pt($style->left, $w); - - if ( $p && !($left === "auto" && $right === "auto") ) { - // Get the parent's padding box (see http://www.w3.org/TR/CSS21/visuren.html#propdef-top) - list($x, $y, $w, $h) = $p->get_padding_box(); - } - - list($width, $height) = array($frame->get_margin_width(), $frame->get_margin_height()); - - $orig_style = $this->_frame->get_original_style(); - $orig_width = $orig_style->width; - $orig_height = $orig_style->height; - - /**************************** - - Width auto: - ____________| left=auto | left=fixed | - right=auto | A | B | - right=fixed | C | D | - - Width fixed: - ____________| left=auto | left=fixed | - right=auto | E | F | - right=fixed | G | H | - - *****************************/ - - if ( $left === "auto" ) { - if ( $right === "auto" ) { - // A or E - Keep the frame at the same position - $x = $x + $frame->find_block_parent()->get_current_line_box()->w; - } - else { - if ( $orig_width === "auto" ) { - // C - $x += $w - $width - $right; - } - else { - // G - $x += $w - $width - $right; - } - } - } - else { - if ( $right === "auto" ) { - // B or F - $x += $left; - } - else { - if ( $orig_width === "auto" ) { - // D - TODO change width - $x += $left; - } - else { - // H - Everything is fixed: left + width win - $x += $left; - } - } - } - - // The same vertically - if ( $top === "auto" ) { - if ( $bottom === "auto" ) { - // A or E - Keep the frame at the same position - $y = $frame->find_block_parent()->get_current_line_box()->y; - } - else { - if ( $orig_height === "auto" ) { - // C - $y += $h - $height - $bottom; - } - else { - // G - $y += $h - $height - $bottom; - } - } - } - else { - if ( $bottom === "auto" ) { - // B or F - $y += $top; - } - else { - if ( $orig_height === "auto" ) { - // D - TODO change height - $y += $top; - } - else { - // H - Everything is fixed: top + height win - $y += $top; - } - } - } - - $frame->set_position($x, $y); - - } -} \ No newline at end of file diff --git a/library/vendor/dompdf/include/abstract_renderer.cls.php b/library/vendor/dompdf/include/abstract_renderer.cls.php deleted file mode 100644 index dcd801306..000000000 --- a/library/vendor/dompdf/include/abstract_renderer.cls.php +++ /dev/null @@ -1,759 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Base renderer class - * - * @access private - * @package dompdf - */ -abstract class Abstract_Renderer { - - /** - * Rendering backend - * - * @var Canvas - */ - protected $_canvas; - - /** - * Current dompdf instance - * - * @var DOMPDF - */ - protected $_dompdf; - - /** - * Class constructor - * - * @param DOMPDF $dompdf The current dompdf instance - */ - function __construct(DOMPDF $dompdf) { - $this->_dompdf = $dompdf; - $this->_canvas = $dompdf->get_canvas(); - } - - /** - * Render a frame. - * - * Specialized in child classes - * - * @param Frame $frame The frame to render - */ - abstract function render(Frame $frame); - - //........................................................................ - - /** - * Render a background image over a rectangular area - * - * @param string $url The background image to load - * @param float $x The left edge of the rectangular area - * @param float $y The top edge of the rectangular area - * @param float $width The width of the rectangular area - * @param float $height The height of the rectangular area - * @param Style $style The associated Style object - * - * @throws Exception - */ - protected function _background_image($url, $x, $y, $width, $height, $style) { - if ( !function_exists("imagecreatetruecolor") ) { - throw new Exception("The PHP GD extension is required, but is not installed."); - } - - $sheet = $style->get_stylesheet(); - - // Skip degenerate cases - if ( $width == 0 || $height == 0 ) { - return; - } - - $box_width = $width; - $box_height = $height; - - //debugpng - if (DEBUGPNG) print '[_background_image '.$url.']'; - - list($img, $type, /*$msg*/) = Image_Cache::resolve_url( - $url, - $sheet->get_protocol(), - $sheet->get_host(), - $sheet->get_base_path(), - $this->_dompdf - ); - - // Bail if the image is no good - if ( Image_Cache::is_broken($img) ) { - return; - } - - //Try to optimize away reading and composing of same background multiple times - //Postponing read with imagecreatefrom ...() - //final composition parameters and name not known yet - //Therefore read dimension directly from file, instead of creating gd object first. - //$img_w = imagesx($src); $img_h = imagesy($src); - - list($img_w, $img_h) = dompdf_getimagesize($img, $this->_dompdf->get_http_context()); - if (!isset($img_w) || $img_w == 0 || !isset($img_h) || $img_h == 0) { - return; - } - - $repeat = $style->background_repeat; - $dpi = $this->_dompdf->get_option("dpi"); - - //Increase background resolution and dependent box size according to image resolution to be placed in - //Then image can be copied in without resize - $bg_width = round((float)($width * $dpi) / 72); - $bg_height = round((float)($height * $dpi) / 72); - - //Need %bg_x, $bg_y as background pos, where img starts, converted to pixel - - list($bg_x, $bg_y) = $style->background_position; - - if ( is_percent($bg_x) ) { - // The point $bg_x % from the left edge of the image is placed - // $bg_x % from the left edge of the background rectangle - $p = ((float)$bg_x)/100.0; - $x1 = $p * $img_w; - $x2 = $p * $bg_width; - - $bg_x = $x2 - $x1; - } - else { - $bg_x = (float)($style->length_in_pt($bg_x)*$dpi) / 72; - } - - $bg_x = round($bg_x + $style->length_in_pt($style->border_left_width)*$dpi / 72); - - if ( is_percent($bg_y) ) { - // The point $bg_y % from the left edge of the image is placed - // $bg_y % from the left edge of the background rectangle - $p = ((float)$bg_y)/100.0; - $y1 = $p * $img_h; - $y2 = $p * $bg_height; - - $bg_y = $y2 - $y1; - } - else { - $bg_y = (float)($style->length_in_pt($bg_y)*$dpi) / 72; - } - - $bg_y = round($bg_y + $style->length_in_pt($style->border_top_width)*$dpi / 72); - - //clip background to the image area on partial repeat. Nothing to do if img off area - //On repeat, normalize start position to the tile at immediate left/top or 0/0 of area - //On no repeat with positive offset: move size/start to have offset==0 - //Handle x/y Dimensions separately - - if ( $repeat !== "repeat" && $repeat !== "repeat-x" ) { - //No repeat x - if ($bg_x < 0) { - $bg_width = $img_w + $bg_x; - } - else { - $x += ($bg_x * 72)/$dpi; - $bg_width = $bg_width - $bg_x; - if ($bg_width > $img_w) { - $bg_width = $img_w; - } - $bg_x = 0; - } - - if ($bg_width <= 0) { - return; - } - - $width = (float)($bg_width * 72)/$dpi; - } - else { - //repeat x - if ($bg_x < 0) { - $bg_x = - ((-$bg_x) % $img_w); - } - else { - $bg_x = $bg_x % $img_w; - if ($bg_x > 0) { - $bg_x -= $img_w; - } - } - } - - if ( $repeat !== "repeat" && $repeat !== "repeat-y" ) { - //no repeat y - if ($bg_y < 0) { - $bg_height = $img_h + $bg_y; - } - else { - $y += ($bg_y * 72)/$dpi; - $bg_height = $bg_height - $bg_y; - if ($bg_height > $img_h) { - $bg_height = $img_h; - } - $bg_y = 0; - } - if ($bg_height <= 0) { - return; - } - $height = (float)($bg_height * 72)/$dpi; - } - else { - //repeat y - if ($bg_y < 0) { - $bg_y = - ((-$bg_y) % $img_h); - } - else { - $bg_y = $bg_y % $img_h; - if ($bg_y > 0) { - $bg_y -= $img_h; - } - } - } - - //Optimization, if repeat has no effect - if ( $repeat === "repeat" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height ) { - $repeat = "repeat-x"; - } - - if ( $repeat === "repeat" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width ) { - $repeat = "repeat-y"; - } - - if ( ($repeat === "repeat-x" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width) || - ($repeat === "repeat-y" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height) ) { - $repeat = "no-repeat"; - } - - //Use filename as indicator only - //different names for different variants to have different copies in the pdf - //This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color) - //Note: Here, bg_* are the start values, not end values after going through the tile loops! - - $filedummy = $img; - - $is_png = false; - $filedummy .= '_'.$bg_width.'_'.$bg_height.'_'.$bg_x.'_'.$bg_y.'_'.$repeat; - - //Optimization to avoid multiple times rendering the same image. - //If check functions are existing and identical image already cached, - //then skip creation of duplicate, because it is not needed by addImagePng - if ( $this->_canvas instanceof CPDF_Adapter && - $this->_canvas->get_cpdf()->image_iscached($filedummy) ) { - $bg = null; - } - - else { - - // Create a new image to fit over the background rectangle - $bg = imagecreatetruecolor($bg_width, $bg_height); - - switch (strtolower($type)) { - case IMAGETYPE_PNG: - $is_png = true; - imagesavealpha($bg, true); - imagealphablending($bg, false); - $src = imagecreatefrompng($img); - break; - - case IMAGETYPE_JPEG: - $src = imagecreatefromjpeg($img); - break; - - case IMAGETYPE_GIF: - $src = imagecreatefromgif($img); - break; - - case IMAGETYPE_BMP: - $src = imagecreatefrombmp($img); - break; - - default: - return; // Unsupported image type - } - - if ( $src == null ) { - return; - } - - //Background color if box is not relevant here - //Non transparent image: box clipped to real size. Background non relevant. - //Transparent image: The image controls the transparency and lets shine through whatever background. - //However on transparent image preset the composed image with the transparency color, - //to keep the transparency when copying over the non transparent parts of the tiles. - $ti = imagecolortransparent($src); - - if ( $ti >= 0 ) { - $tc = imagecolorsforindex($src, $ti); - $ti = imagecolorallocate($bg, $tc['red'], $tc['green'], $tc['blue']); - imagefill($bg, 0, 0, $ti); - imagecolortransparent($bg, $ti); - } - - //This has only an effect for the non repeatable dimension. - //compute start of src and dest coordinates of the single copy - if ( $bg_x < 0 ) { - $dst_x = 0; - $src_x = -$bg_x; - } - else { - $src_x = 0; - $dst_x = $bg_x; - } - - if ( $bg_y < 0 ) { - $dst_y = 0; - $src_y = -$bg_y; - } - else { - $src_y = 0; - $dst_y = $bg_y; - } - - //For historical reasons exchange meanings of variables: - //start_* will be the start values, while bg_* will be the temporary start values in the loops - $start_x = $bg_x; - $start_y = $bg_y; - - // Copy regions from the source image to the background - if ( $repeat === "no-repeat" ) { - - // Simply place the image on the background - imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h); - - } - else if ( $repeat === "repeat-x" ) { - - for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) { - if ( $bg_x < 0 ) { - $dst_x = 0; - $src_x = -$bg_x; - $w = $img_w + $bg_x; - } - else { - $dst_x = $bg_x; - $src_x = 0; - $w = $img_w; - } - imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h); - } - - } - else if ( $repeat === "repeat-y" ) { - - for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) { - if ( $bg_y < 0 ) { - $dst_y = 0; - $src_y = -$bg_y; - $h = $img_h + $bg_y; - } - else { - $dst_y = $bg_y; - $src_y = 0; - $h = $img_h; - } - imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h); - - } - - } - else if ( $repeat === "repeat" ) { - - for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) { - for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) { - - if ( $bg_x < 0 ) { - $dst_x = 0; - $src_x = -$bg_x; - $w = $img_w + $bg_x; - } - else { - $dst_x = $bg_x; - $src_x = 0; - $w = $img_w; - } - - if ( $bg_y < 0 ) { - $dst_y = 0; - $src_y = -$bg_y; - $h = $img_h + $bg_y; - } - else { - $dst_y = $bg_y; - $src_y = 0; - $h = $img_h; - } - imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h); - } - } - } - - else { - print 'Unknown repeat!'; - } - - imagedestroy($src); - - } /* End optimize away creation of duplicates */ - - $this->_canvas->clipping_rectangle($x, $y, $box_width, $box_height); - - //img: image url string - //img_w, img_h: original image size in px - //width, height: box size in pt - //bg_width, bg_height: box size in px - //x, y: left/top edge of box on page in pt - //start_x, start_y: placement of image relative to pattern - //$repeat: repeat mode - //$bg: GD object of result image - //$src: GD object of original image - //When using cpdf and optimization to direct png creation from gd object is available, - //don't create temp file, but place gd object directly into the pdf - if ( !$is_png && $this->_canvas instanceof CPDF_Adapter ) { - // Note: CPDF_Adapter image converts y position - $this->_canvas->get_cpdf()->addImagePng($filedummy, $x, $this->_canvas->get_height() - $y - $height, $width, $height, $bg); - } - - else { - $tmp_dir = $this->_dompdf->get_option("temp_dir"); - $tmp_name = tempnam($tmp_dir, "bg_dompdf_img_"); - @unlink($tmp_name); - $tmp_file = "$tmp_name.png"; - - //debugpng - if (DEBUGPNG) print '[_background_image '.$tmp_file.']'; - - imagepng($bg, $tmp_file); - $this->_canvas->image($tmp_file, $x, $y, $width, $height); - imagedestroy($bg); - - //debugpng - if (DEBUGPNG) print '[_background_image unlink '.$tmp_file.']'; - - if (!DEBUGKEEPTEMP) { - unlink($tmp_file); - } - } - - $this->_canvas->clipping_end(); - } - - protected function _get_dash_pattern($style, $width) { - $pattern = array(); - - switch ($style) { - default: - /*case "solid": - case "double": - case "groove": - case "inset": - case "outset": - case "ridge":*/ - case "none": break; - - case "dotted": - if ( $width <= 1 ) - $pattern = array($width, $width*2); - else - $pattern = array($width); - break; - - case "dashed": - $pattern = array(3 * $width); - break; - } - - return $pattern; - } - - protected function _border_none($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { - return; - } - - protected function _border_hidden($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { - return; - } - - // Border rendering functions - - protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { - $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dotted", $r1, $r2); - } - - - protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { - $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dashed", $r1, $r2); - } - - - protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { - // TODO: Solve rendering where one corner is beveled (radius == 0), one corner isn't. - if ( $corner_style !== "bevel" || $r1 > 0 || $r2 > 0 ) { - // do it the simple way - $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "solid", $r1, $r2); - return; - } - - list($top, $right, $bottom, $left) = $widths; - - // All this polygon business is for beveled corners... - switch ($side) { - case "top": - $points = array($x, $y, - $x + $length, $y, - $x + $length - $right, $y + $top, - $x + $left, $y + $top); - $this->_canvas->polygon($points, $color, null, null, true); - break; - - case "bottom": - $points = array($x, $y, - $x + $length, $y, - $x + $length - $right, $y - $bottom, - $x + $left, $y - $bottom); - $this->_canvas->polygon($points, $color, null, null, true); - break; - - case "left": - $points = array($x, $y, - $x, $y + $length, - $x + $left, $y + $length - $bottom, - $x + $left, $y + $top); - $this->_canvas->polygon($points, $color, null, null, true); - break; - - case "right": - $points = array($x, $y, - $x, $y + $length, - $x - $right, $y + $length - $bottom, - $x - $right, $y + $top); - $this->_canvas->polygon($points, $color, null, null, true); - break; - - default: - return; - } - } - - protected function _apply_ratio($side, $ratio, $top, $right, $bottom, $left, &$x, &$y, &$length, &$r1, &$r2) { - switch ($side) { - - case "top": - $r1 -= $left * $ratio; - $r2 -= $right * $ratio; - $x += $left * $ratio; - $y += $top * $ratio; - $length -= $left * $ratio + $right * $ratio; - break; - - case "bottom": - $r1 -= $right * $ratio; - $r2 -= $left * $ratio; - $x += $left * $ratio; - $y -= $bottom * $ratio; - $length -= $left * $ratio + $right * $ratio; - break; - - case "left": - $r1 -= $top * $ratio; - $r2 -= $bottom * $ratio; - $x += $left * $ratio; - $y += $top * $ratio; - $length -= $top * $ratio + $bottom * $ratio; - break; - - case "right": - $r1 -= $bottom * $ratio; - $r2 -= $top * $ratio; - $x -= $right * $ratio; - $y += $top * $ratio; - $length -= $top * $ratio + $bottom * $ratio; - break; - - default: - return; - - } - } - - protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { - list($top, $right, $bottom, $left) = $widths; - - $third_widths = array($top / 3, $right / 3, $bottom / 3, $left / 3); - - // draw the outer border - $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2); - - $this->_apply_ratio($side, 2/3, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); - - $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2); - } - - protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { - list($top, $right, $bottom, $left) = $widths; - - $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2); - - $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); - - $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); - - $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); - - } - - protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { - list($top, $right, $bottom, $left) = $widths; - - $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2); - - $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); - - $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); - - $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); - - } - - protected function _tint($c) { - if ( !is_numeric($c) ) - return $c; - - return min(1, $c + 0.16); - } - - protected function _shade($c) { - if ( !is_numeric($c) ) - return $c; - - return max(0, $c - 0.33); - } - - protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { - switch ($side) { - case "top": - case "left": - $shade = array_map(array($this, "_shade"), $color); - $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2); - break; - - case "bottom": - case "right": - $tint = array_map(array($this, "_tint"), $color); - $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2); - break; - - default: - return; - } - } - - protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) { - switch ($side) { - case "top": - case "left": - $tint = array_map(array($this, "_tint"), $color); - $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2); - break; - - case "bottom": - case "right": - $shade = array_map(array($this, "_shade"), $color); - $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2); - break; - - default: - return; - } - } - // Draws a solid, dotted, or dashed line, observing the border radius - protected function _border_line($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $pattern_name, $r1 = 0, $r2 = 0) { - list($top, $right, $bottom, $left) = $widths; - - $width = $$side; - $pattern = $this->_get_dash_pattern($pattern_name, $width); - - $half_width = $width/2; - $r1 -= $half_width; - $r2 -= $half_width; - $adjust = $r1/80; - $length -= $width; - - switch ($side) { - case "top": - $x += $half_width; - $y += $half_width; - - if ( $r1 > 0 ) { - $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 90-$adjust, 135+$adjust, $color, $width, $pattern); - } - - $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern); - - if ( $r2 > 0 ) { - $this->_canvas->arc($x + $length - $r2, $y + $r2, $r2, $r2, 45-$adjust, 90+$adjust, $color, $width, $pattern); - } - break; - - case "bottom": - $x += $half_width; - $y -= $half_width; - - if ( $r1 > 0 ) { - $this->_canvas->arc($x + $r1, $y - $r1, $r1, $r1, 225-$adjust, 270+$adjust, $color, $width, $pattern); - } - - $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern); - - if ( $r2 > 0 ) { - $this->_canvas->arc($x + $length - $r2, $y - $r2, $r2, $r2, 270-$adjust, 315+$adjust, $color, $width, $pattern); - } - break; - - case "left": - $y += $half_width; - $x += $half_width; - - if ( $r1 > 0 ) { - $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 135-$adjust, 180+$adjust, $color, $width, $pattern); - } - - $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern); - - if ( $r2 > 0 ) { - $this->_canvas->arc($x + $r2, $y + $length - $r2, $r2, $r2, 180-$adjust, 225+$adjust, $color, $width, $pattern); - } - break; - - case "right": - $y += $half_width; - $x -= $half_width; - - if ( $r1 > 0 ) { - $this->_canvas->arc($x - $r1, $y + $r1, $r1, $r1, 0-$adjust, 45+$adjust, $color, $width, $pattern); - } - - $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern); - - if ( $r2 > 0 ) { - $this->_canvas->arc($x - $r2, $y + $length - $r2, $r2, $r2, 315-$adjust, 360+$adjust, $color, $width, $pattern); - } - break; - } - } - - protected function _set_opacity($opacity) { - if ( is_numeric($opacity) && $opacity <= 1.0 && $opacity >= 0.0 ) { - $this->_canvas->set_opacity( $opacity ); - } - } - - protected function _debug_layout($box, $color = "red", $style = array()) { - $this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], CSS_Color::parse($color), 0.1, $style); - } -} diff --git a/library/vendor/dompdf/include/attribute_translator.cls.php b/library/vendor/dompdf/include/attribute_translator.cls.php deleted file mode 100644 index 68da668c1..000000000 --- a/library/vendor/dompdf/include/attribute_translator.cls.php +++ /dev/null @@ -1,592 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Translates HTML 4.0 attributes into CSS rules - * - * @package dompdf - */ -class Attribute_Translator { - static $_style_attr = "_html_style_attribute"; - - // Munged data originally from - // http://www.w3.org/TR/REC-html40/index/attributes.html - // http://www.cs.tut.fi/~jkorpela/html2css.html - static private $__ATTRIBUTE_LOOKUP = array( - //'caption' => array ( 'align' => '', ), - 'img' => array( - 'align' => array( - 'bottom' => 'vertical-align: baseline;', - 'middle' => 'vertical-align: middle;', - 'top' => 'vertical-align: top;', - 'left' => 'float: left;', - 'right' => 'float: right;' - ), - 'border' => 'border: %0.2F px solid;', - 'height' => 'height: %s px;', - 'hspace' => 'padding-left: %1$0.2F px; padding-right: %1$0.2F px;', - 'vspace' => 'padding-top: %1$0.2F px; padding-bottom: %1$0.2F px;', - 'width' => 'width: %s px;', - ), - 'table' => array( - 'align' => array( - 'left' => 'margin-left: 0; margin-right: auto;', - 'center' => 'margin-left: auto; margin-right: auto;', - 'right' => 'margin-left: auto; margin-right: 0;' - ), - 'bgcolor' => 'background-color: %s;', - 'border' => '!set_table_border', - 'cellpadding' => '!set_table_cellpadding',//'border-spacing: %0.2F; border-collapse: separate;', - 'cellspacing' => '!set_table_cellspacing', - 'frame' => array( - 'void' => 'border-style: none;', - 'above' => 'border-top-style: solid;', - 'below' => 'border-bottom-style: solid;', - 'hsides' => 'border-left-style: solid; border-right-style: solid;', - 'vsides' => 'border-top-style: solid; border-bottom-style: solid;', - 'lhs' => 'border-left-style: solid;', - 'rhs' => 'border-right-style: solid;', - 'box' => 'border-style: solid;', - 'border' => 'border-style: solid;' - ), - 'rules' => '!set_table_rules', - 'width' => 'width: %s;', - ), - 'hr' => array( - 'align' => '!set_hr_align', // Need to grab width to set 'left' & 'right' correctly - 'noshade' => 'border-style: solid;', - 'size' => '!set_hr_size', //'border-width: %0.2F px;', - 'width' => 'width: %s;', - ), - 'div' => array( - 'align' => 'text-align: %s;', - ), - 'h1' => array( - 'align' => 'text-align: %s;', - ), - 'h2' => array( - 'align' => 'text-align: %s;', - ), - 'h3' => array( - 'align' => 'text-align: %s;', - ), - 'h4' => array( - 'align' => 'text-align: %s;', - ), - 'h5' => array( - 'align' => 'text-align: %s;', - ), - 'h6' => array( - 'align' => 'text-align: %s;', - ), - 'p' => array( - 'align' => 'text-align: %s;', - ), -// 'col' => array( -// 'align' => '', -// 'valign' => '', -// ), -// 'colgroup' => array( -// 'align' => '', -// 'valign' => '', -// ), - 'tbody' => array( - 'align' => '!set_table_row_align', - 'valign' => '!set_table_row_valign', - ), - 'td' => array( - 'align' => 'text-align: %s;', - 'bgcolor' => '!set_background_color', - 'height' => 'height: %s;', - 'nowrap' => 'white-space: nowrap;', - 'valign' => 'vertical-align: %s;', - 'width' => 'width: %s;', - ), - 'tfoot' => array( - 'align' => '!set_table_row_align', - 'valign' => '!set_table_row_valign', - ), - 'th' => array( - 'align' => 'text-align: %s;', - 'bgcolor' => '!set_background_color', - 'height' => 'height: %s;', - 'nowrap' => 'white-space: nowrap;', - 'valign' => 'vertical-align: %s;', - 'width' => 'width: %s;', - ), - 'thead' => array( - 'align' => '!set_table_row_align', - 'valign' => '!set_table_row_valign', - ), - 'tr' => array( - 'align' => '!set_table_row_align', - 'bgcolor' => '!set_table_row_bgcolor', - 'valign' => '!set_table_row_valign', - ), - 'body' => array( - 'background' => 'background-image: url(%s);', - 'bgcolor' => '!set_background_color', - 'link' => '!set_body_link', - 'text' => '!set_color', - ), - 'br' => array( - 'clear' => 'clear: %s;', - ), - 'basefont' => array( - 'color' => '!set_color', - 'face' => 'font-family: %s;', - 'size' => '!set_basefont_size', - ), - 'font' => array( - 'color' => '!set_color', - 'face' => 'font-family: %s;', - 'size' => '!set_font_size', - ), - 'dir' => array( - 'compact' => 'margin: 0.5em 0;', - ), - 'dl' => array( - 'compact' => 'margin: 0.5em 0;', - ), - 'menu' => array( - 'compact' => 'margin: 0.5em 0;', - ), - 'ol' => array( - 'compact' => 'margin: 0.5em 0;', - 'start' => 'counter-reset: -dompdf-default-counter %d;', - 'type' => 'list-style-type: %s;', - ), - 'ul' => array( - 'compact' => 'margin: 0.5em 0;', - 'type' => 'list-style-type: %s;', - ), - 'li' => array( - 'type' => 'list-style-type: %s;', - 'value' => 'counter-reset: -dompdf-default-counter %d;', - ), - 'pre' => array( - 'width' => 'width: %s;', - ), - ); - - static protected $_last_basefont_size = 3; - static protected $_font_size_lookup = array( - // For basefont support - -3 => "4pt", - -2 => "5pt", - -1 => "6pt", - 0 => "7pt", - - 1 => "8pt", - 2 => "10pt", - 3 => "12pt", - 4 => "14pt", - 5 => "18pt", - 6 => "24pt", - 7 => "34pt", - - // For basefont support - 8 => "48pt", - 9 => "44pt", - 10 => "52pt", - 11 => "60pt", - ); - - /** - * @param Frame $frame - */ - static function translate_attributes(Frame $frame) { - $node = $frame->get_node(); - $tag = $node->nodeName; - - if ( !isset(self::$__ATTRIBUTE_LOOKUP[$tag]) ) { - return; - } - - $valid_attrs = self::$__ATTRIBUTE_LOOKUP[$tag]; - $attrs = $node->attributes; - $style = rtrim($node->getAttribute(self::$_style_attr), "; "); - if ( $style != "" ) { - $style .= ";"; - } - - foreach ($attrs as $attr => $attr_node ) { - if ( !isset($valid_attrs[$attr]) ) { - continue; - } - - $value = $attr_node->value; - - $target = $valid_attrs[$attr]; - - // Look up $value in $target, if $target is an array: - if ( is_array($target) ) { - if ( isset($target[$value]) ) { - $style .= " " . self::_resolve_target($node, $target[$value], $value); - } - } - else { - // otherwise use target directly - $style .= " " . self::_resolve_target($node, $target, $value); - } - } - - if ( !is_null($style) ) { - $style = ltrim($style); - $node->setAttribute(self::$_style_attr, $style); - } - - } - - /** - * @param DOMNode $node - * @param string $target - * @param string $value - * - * @return string - */ - static protected function _resolve_target(DOMNode $node, $target, $value) { - if ( $target[0] === "!" ) { - // Function call - $func = "_" . mb_substr($target, 1); - return self::$func($node, $value); - } - - return $value ? sprintf($target, $value) : ""; - } - - /** - * @param DOMElement $node - * @param string $new_style - */ - static function append_style(DOMElement $node, $new_style) { - $style = rtrim($node->getAttribute(self::$_style_attr), ";"); - $style .= $new_style; - $style = ltrim($style, ";"); - $node->setAttribute(self::$_style_attr, $style); - } - - /** - * @param DOMNode $node - * - * @return DOMNodeList|DOMElement[] - */ - static protected function get_cell_list(DOMNode $node) { - $xpath = new DOMXpath($node->ownerDocument); - - switch($node->nodeName) { - default: - case "table": - $query = "tr/td | thead/tr/td | tbody/tr/td | tfoot/tr/td | tr/th | thead/tr/th | tbody/tr/th | tfoot/tr/th"; - break; - - case "tbody": - case "tfoot": - case "thead": - $query = "tr/td | tr/th"; - break; - - case "tr": - $query = "td | th"; - break; - } - - return $xpath->query($query, $node); - } - - /** - * @param string $value - * - * @return string - */ - static protected function _get_valid_color($value) { - if ( preg_match('/^#?([0-9A-F]{6})$/i', $value, $matches) ) { - $value = "#$matches[1]"; - } - - return $value; - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return string - */ - static protected function _set_color(DOMElement $node, $value) { - $value = self::_get_valid_color($value); - return "color: $value;"; - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return string - */ - static protected function _set_background_color(DOMElement $node, $value) { - $value = self::_get_valid_color($value); - return "background-color: $value;"; - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return null - */ - static protected function _set_table_cellpadding(DOMElement $node, $value) { - $cell_list = self::get_cell_list($node); - - foreach ($cell_list as $cell) { - self::append_style($cell, "; padding: {$value}px;"); - } - - return null; - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return string - */ - static protected function _set_table_border(DOMElement $node, $value) { - $cell_list = self::get_cell_list($node); - - foreach ($cell_list as $cell) { - $style = rtrim($cell->getAttribute(self::$_style_attr)); - $style .= "; border-width: " . ($value > 0 ? 1 : 0) . "pt; border-style: inset;"; - $style = ltrim($style, ";"); - $cell->setAttribute(self::$_style_attr, $style); - } - - $style = rtrim($node->getAttribute(self::$_style_attr), ";"); - $style .= "; border-width: $value" . "px; "; - return ltrim($style, "; "); - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return string - */ - static protected function _set_table_cellspacing(DOMElement $node, $value) { - $style = rtrim($node->getAttribute(self::$_style_attr), ";"); - - if ( $value == 0 ) { - $style .= "; border-collapse: collapse;"; - } - else { - $style .= "; border-spacing: {$value}px; border-collapse: separate;"; - } - - return ltrim($style, ";"); - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return null|string - */ - static protected function _set_table_rules(DOMElement $node, $value) { - $new_style = "; border-collapse: collapse;"; - - switch ($value) { - case "none": - $new_style .= "border-style: none;"; - break; - - case "groups": - // FIXME: unsupported - return null; - - case "rows": - $new_style .= "border-style: solid none solid none; border-width: 1px; "; - break; - - case "cols": - $new_style .= "border-style: none solid none solid; border-width: 1px; "; - break; - - case "all": - $new_style .= "border-style: solid; border-width: 1px; "; - break; - - default: - // Invalid value - return null; - } - - $cell_list = self::get_cell_list($node); - - foreach ($cell_list as $cell) { - $style = $cell->getAttribute(self::$_style_attr); - $style .= $new_style; - $cell->setAttribute(self::$_style_attr, $style); - } - - $style = rtrim($node->getAttribute(self::$_style_attr), ";"); - $style .= "; border-collapse: collapse; "; - - return ltrim($style, "; "); - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return string - */ - static protected function _set_hr_size(DOMElement $node, $value) { - $style = rtrim($node->getAttribute(self::$_style_attr), ";"); - $style .= "; border-width: ".max(0, $value-2)."; "; - return ltrim($style, "; "); - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return null|string - */ - static protected function _set_hr_align(DOMElement $node, $value) { - $style = rtrim($node->getAttribute(self::$_style_attr),";"); - $width = $node->getAttribute("width"); - - if ( $width == "" ) { - $width = "100%"; - } - - $remainder = 100 - (double)rtrim($width, "% "); - - switch ($value) { - case "left": - $style .= "; margin-right: $remainder %;"; - break; - - case "right": - $style .= "; margin-left: $remainder %;"; - break; - - case "center": - $style .= "; margin-left: auto; margin-right: auto;"; - break; - - default: - return null; - } - - return ltrim($style, "; "); - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return null - */ - static protected function _set_table_row_align(DOMElement $node, $value) { - $cell_list = self::get_cell_list($node); - - foreach ($cell_list as $cell) { - self::append_style($cell, "; text-align: $value;"); - } - - return null; - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return null - */ - static protected function _set_table_row_valign(DOMElement $node, $value) { - $cell_list = self::get_cell_list($node); - - foreach ($cell_list as $cell) { - self::append_style($cell, "; vertical-align: $value;"); - } - - return null; - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return null - */ - static protected function _set_table_row_bgcolor(DOMElement $node, $value) { - $cell_list = self::get_cell_list($node); - $value = self::_get_valid_color($value); - - foreach ($cell_list as $cell) { - self::append_style($cell, "; background-color: $value;"); - } - - return null; - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return null - */ - static protected function _set_body_link(DOMElement $node, $value) { - $a_list = $node->getElementsByTagName("a"); - $value = self::_get_valid_color($value); - - foreach ($a_list as $a) { - self::append_style($a, "; color: $value;"); - } - - return null; - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return null - */ - static protected function _set_basefont_size(DOMElement $node, $value) { - // FIXME: ? we don't actually set the font size of anything here, just - // the base size for later modification by tags. - self::$_last_basefont_size = $value; - return null; - } - - /** - * @param DOMElement $node - * @param string $value - * - * @return string - */ - static protected function _set_font_size(DOMElement $node, $value) { - $style = $node->getAttribute(self::$_style_attr); - - if ( $value[0] === "-" || $value[0] === "+" ) { - $value = self::$_last_basefont_size + (int)$value; - } - - if ( isset(self::$_font_size_lookup[$value]) ) { - $style .= "; font-size: " . self::$_font_size_lookup[$value] . ";"; - } - else { - $style .= "; font-size: $value;"; - } - - return ltrim($style, "; "); - } -} diff --git a/library/vendor/dompdf/include/autoload.inc.php b/library/vendor/dompdf/include/autoload.inc.php deleted file mode 100644 index 509d3e3bc..000000000 --- a/library/vendor/dompdf/include/autoload.inc.php +++ /dev/null @@ -1,86 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * DOMPDF autoload function - * - * If you have an existing autoload function, add a call to this function - * from your existing __autoload() implementation. - * - * @param string $class - */ -function DOMPDF_autoload($class) { - $filename = DOMPDF_INC_DIR . "/" . mb_strtolower($class) . ".cls.php"; - - if ( is_file($filename) ) { - include_once $filename; - } -} - -// If SPL autoload functions are available (PHP >= 5.1.2) -if ( function_exists("spl_autoload_register") ) { - $autoload = "DOMPDF_autoload"; - $funcs = spl_autoload_functions(); - - // No functions currently in the stack. - if ( !DOMPDF_AUTOLOAD_PREPEND || $funcs === false ) { - spl_autoload_register($autoload); - } - - // If PHP >= 5.3 the $prepend argument is available - else if ( PHP_VERSION_ID >= 50300 ) { - spl_autoload_register($autoload, true, true); - } - - else { - // Unregister existing autoloaders... - $compat = (PHP_VERSION_ID <= 50102 && PHP_VERSION_ID >= 50100); - - foreach ($funcs as $func) { - if (is_array($func)) { - // :TRICKY: There are some compatibility issues and some - // places where we need to error out - $reflector = new ReflectionMethod($func[0], $func[1]); - if (!$reflector->isStatic()) { - throw new Exception('This function is not compatible with non-static object methods due to PHP Bug #44144.'); - } - - // Suprisingly, spl_autoload_register supports the - // Class::staticMethod callback format, although call_user_func doesn't - if ($compat) $func = implode('::', $func); - } - - spl_autoload_unregister($func); - } - - // Register the new one, thus putting it at the front of the stack... - spl_autoload_register($autoload); - - // Now, go back and re-register all of our old ones. - foreach ($funcs as $func) { - spl_autoload_register($func); - } - - // Be polite and ensure that userland autoload gets retained - if ( function_exists("__autoload") ) { - spl_autoload_register("__autoload"); - } - } -} - -else if ( !function_exists("__autoload") ) { - /** - * Default __autoload() function - * - * @param string $class - */ - function __autoload($class) { - DOMPDF_autoload($class); - } -} diff --git a/library/vendor/dompdf/include/block_frame_decorator.cls.php b/library/vendor/dompdf/include/block_frame_decorator.cls.php deleted file mode 100644 index 407635c0e..000000000 --- a/library/vendor/dompdf/include/block_frame_decorator.cls.php +++ /dev/null @@ -1,234 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Decorates frames for block layout - * - * @access private - * @package dompdf - */ -class Block_Frame_Decorator extends Frame_Decorator { - /** - * Current line index - * - * @var int - */ - protected $_cl; - - /** - * The block's line boxes - * - * @var Line_Box[] - */ - protected $_line_boxes; - - function __construct(Frame $frame, DOMPDF $dompdf) { - parent::__construct($frame, $dompdf); - - $this->_line_boxes = array(new Line_Box($this)); - $this->_cl = 0; - } - - function reset() { - parent::reset(); - - $this->_line_boxes = array(new Line_Box($this)); - $this->_cl = 0; - } - - /** - * @return Line_Box - */ - function get_current_line_box() { - return $this->_line_boxes[$this->_cl]; - } - - /** - * @return integer - */ - function get_current_line_number() { - return $this->_cl; - } - - /** - * @return Line_Box[] - */ - function get_line_boxes() { - return $this->_line_boxes; - } - - /** - * @param integer $i - */ - function clear_line($i) { - if ( isset($this->_line_boxes[$i]) ) { - unset($this->_line_boxes[$i]); - } - } - - /** - * @param Frame $frame - */ - function add_frame_to_line(Frame $frame) { - if ( !$frame->is_in_flow() ) { - return; - } - - $style = $frame->get_style(); - - $frame->set_containing_line($this->_line_boxes[$this->_cl]); - - /* - // Adds a new line after a block, only if certain conditions are met - if ((($frame instanceof Inline_Frame_Decorator && $frame->get_node()->nodeName !== "br") || - $frame instanceof Text_Frame_Decorator && trim($frame->get_text())) && - ($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" && - $this->_line_boxes[$this->_cl]->w > 0 )) { - - $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame ); - $this->add_line(); - - // Add each child of the inline frame to the line individually - foreach ($frame->get_children() as $child) - $this->add_frame_to_line( $child ); - } - else*/ - - // Handle inline frames (which are effectively wrappers) - if ( $frame instanceof Inline_Frame_Decorator ) { - - // Handle line breaks - if ( $frame->get_node()->nodeName === "br" ) { - $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame ); - $this->add_line(true); - } - - return; - } - - // Trim leading text if this is an empty line. Kinda a hack to put it here, - // but what can you do... - if ( $this->get_current_line_box()->w == 0 && - $frame->is_text_node() && - !$frame->is_pre() ) { - - $frame->set_text( ltrim($frame->get_text()) ); - $frame->recalculate_width(); - } - - $w = $frame->get_margin_width(); - - if ( $w == 0 ) { - return; - } - - // Debugging code: - /* - pre_r("\n

Adding frame to line:

"); - - // pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")"); - // pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")"); - if ( $frame->is_text_node() ) - pre_r('"'.$frame->get_node()->nodeValue.'"'); - - pre_r("Line width: " . $this->_line_boxes[$this->_cl]->w); - pre_r("Frame: " . get_class($frame)); - pre_r("Frame width: " . $w); - pre_r("Frame height: " . $frame->get_margin_height()); - pre_r("Containing block width: " . $this->get_containing_block("w")); - */ - // End debugging - - $line = $this->_line_boxes[$this->_cl]; - if ( $line->left + $line->w + $line->right + $w > $this->get_containing_block("w")) { - $this->add_line(); - } - - $frame->position(); - - $current_line = $this->_line_boxes[$this->_cl]; - $current_line->add_frame($frame); - - if ( $frame->is_text_node() ) { - $current_line->wc += count(preg_split("/\s+/", trim($frame->get_text()))); - } - - $this->increase_line_width($w); - - $this->maximize_line_height($frame->get_margin_height(), $frame); - } - - function remove_frames_from_line(Frame $frame) { - // Search backwards through the lines for $frame - $i = $this->_cl; - $j = null; - - while ($i >= 0) { - if ( ($j = in_array($frame, $this->_line_boxes[$i]->get_frames(), true)) !== false ) { - break; - } - - $i--; - } - - if ( $j === false ) { - return; - } - - // Remove $frame and all frames that follow - while ($j < count($this->_line_boxes[$i]->get_frames())) { - $frames = $this->_line_boxes[$i]->get_frames(); - $f = $frames[$j]; - $frames[$j] = null; - unset($frames[$j]); - $j++; - $this->_line_boxes[$i]->w -= $f->get_margin_width(); - } - - // Recalculate the height of the line - $h = 0; - foreach ($this->_line_boxes[$i]->get_frames() as $f) { - $h = max( $h, $f->get_margin_height() ); - } - - $this->_line_boxes[$i]->h = $h; - - // Remove all lines that follow - while ($this->_cl > $i) { - $this->_line_boxes[ $this->_cl ] = null; - unset($this->_line_boxes[ $this->_cl ]); - $this->_cl--; - } - } - - function increase_line_width($w) { - $this->_line_boxes[ $this->_cl ]->w += $w; - } - - function maximize_line_height($val, Frame $frame) { - if ( $val > $this->_line_boxes[ $this->_cl ]->h ) { - $this->_line_boxes[ $this->_cl ]->tallest_frame = $frame; - $this->_line_boxes[ $this->_cl ]->h = $val; - } - } - - function add_line($br = false) { - -// if ( $this->_line_boxes[$this->_cl]["h"] == 0 ) //count($this->_line_boxes[$i]["frames"]) == 0 || -// return; - - $this->_line_boxes[$this->_cl]->br = $br; - $y = $this->_line_boxes[$this->_cl]->y + $this->_line_boxes[$this->_cl]->h; - - $new_line = new Line_Box($this, $y); - - $this->_line_boxes[ ++$this->_cl ] = $new_line; - } - - //........................................................................ -} diff --git a/library/vendor/dompdf/include/block_frame_reflower.cls.php b/library/vendor/dompdf/include/block_frame_reflower.cls.php deleted file mode 100644 index bbbdba96d..000000000 --- a/library/vendor/dompdf/include/block_frame_reflower.cls.php +++ /dev/null @@ -1,805 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Reflows block frames - * - * @access private - * @package dompdf - */ -class Block_Frame_Reflower extends Frame_Reflower { - // Minimum line width to justify, as fraction of available width - const MIN_JUSTIFY_WIDTH = 0.80; - - /** - * @var Block_Frame_Decorator - */ - protected $_frame; - - function __construct(Block_Frame_Decorator $frame) { parent::__construct($frame); } - - /** - * Calculate the ideal used value for the width property as per: - * http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins - * - * @param float $width - * @return array - */ - protected function _calculate_width($width) { - $frame = $this->_frame; - $style = $frame->get_style(); - $w = $frame->get_containing_block("w"); - - if ( $style->position === "fixed" ) { - $w = $frame->get_parent()->get_containing_block("w"); - } - - $rm = $style->length_in_pt($style->margin_right, $w); - $lm = $style->length_in_pt($style->margin_left, $w); - - $left = $style->length_in_pt($style->left, $w); - $right = $style->length_in_pt($style->right, $w); - - // Handle 'auto' values - $dims = array($style->border_left_width, - $style->border_right_width, - $style->padding_left, - $style->padding_right, - $width !== "auto" ? $width : 0, - $rm !== "auto" ? $rm : 0, - $lm !== "auto" ? $lm : 0); - - // absolutely positioned boxes take the 'left' and 'right' properties into account - if ( $frame->is_absolute() ) { - $absolute = true; - $dims[] = $left !== "auto" ? $left : 0; - $dims[] = $right !== "auto" ? $right : 0; - } - else { - $absolute = false; - } - - $sum = $style->length_in_pt($dims, $w); - - // Compare to the containing block - $diff = $w - $sum; - - if ( $diff > 0 ) { - - if ( $absolute ) { - - // resolve auto properties: see - // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width - - if ( $width === "auto" && $left === "auto" && $right === "auto" ) { - - if ( $lm === "auto" ) $lm = 0; - if ( $rm === "auto" ) $rm = 0; - - // Technically, the width should be "shrink-to-fit" i.e. based on the - // preferred width of the content... a little too costly here as a - // special case. Just get the width to take up the slack: - $left = 0; - $right = 0; - $width = $diff; - } - else if ( $width === "auto" ) { - - if ( $lm === "auto" ) $lm = 0; - if ( $rm === "auto" ) $rm = 0; - if ( $left === "auto" ) $left = 0; - if ( $right === "auto" ) $right = 0; - - $width = $diff; - } - else if ( $left === "auto" ) { - - if ( $lm === "auto" ) $lm = 0; - if ( $rm === "auto" ) $rm = 0; - if ( $right === "auto" ) $right = 0; - - $left = $diff; - } - else if ( $right === "auto" ) { - - if ( $lm === "auto" ) $lm = 0; - if ( $rm === "auto" ) $rm = 0; - - $right = $diff; - } - - } - else { - - // Find auto properties and get them to take up the slack - if ( $width === "auto" ) { - $width = $diff; - } - else if ( $lm === "auto" && $rm === "auto" ) { - $lm = $rm = round($diff / 2); - } - else if ( $lm === "auto" ) { - $lm = $diff; - } - else if ( $rm === "auto" ) { - $rm = $diff; - } - } - - } - else if ($diff < 0) { - - // We are over constrained--set margin-right to the difference - $rm = $diff; - - } - - return array( - "width" => $width, - "margin_left" => $lm, - "margin_right" => $rm, - "left" => $left, - "right" => $right, - ); - } - - /** - * Call the above function, but resolve max/min widths - * - * @throws DOMPDF_Exception - * @return array - */ - protected function _calculate_restricted_width() { - $frame = $this->_frame; - $style = $frame->get_style(); - $cb = $frame->get_containing_block(); - - if ( $style->position === "fixed" ) { - $cb = $frame->get_root()->get_containing_block(); - } - - //if ( $style->position === "absolute" ) - // $cb = $frame->find_positionned_parent()->get_containing_block(); - - if ( !isset($cb["w"]) ) { - throw new DOMPDF_Exception("Box property calculation requires containing block width"); - } - - // Treat width 100% as auto - if ( $style->width === "100%" ) { - $width = "auto"; - } - else { - $width = $style->length_in_pt($style->width, $cb["w"]); - } - - extract($this->_calculate_width($width)); - - // Handle min/max width - $min_width = $style->length_in_pt($style->min_width, $cb["w"]); - $max_width = $style->length_in_pt($style->max_width, $cb["w"]); - - if ( $max_width !== "none" && $min_width > $max_width ) { - list($max_width, $min_width) = array($min_width, $max_width); - } - - if ( $max_width !== "none" && $width > $max_width ) { - extract($this->_calculate_width($max_width)); - } - - if ( $width < $min_width ) { - extract($this->_calculate_width($min_width)); - } - - return array($width, $margin_left, $margin_right, $left, $right); - } - - /** - * Determine the unrestricted height of content within the block - * not by adding each line's height, but by getting the last line's position. - * This because lines could have been pushed lower by a clearing element. - * - * @return float - */ - protected function _calculate_content_height() { - $lines = $this->_frame->get_line_boxes(); - $height = 0; - - foreach ($lines as $line) { - $height += $line->h; - } - - /* - $first_line = reset($lines); - $last_line = end($lines); - $height2 = $last_line->y + $last_line->h - $first_line->y; - */ - - return $height; - } - - /** - * Determine the frame's restricted height - * - * @return array - */ - protected function _calculate_restricted_height() { - $frame = $this->_frame; - $style = $frame->get_style(); - $content_height = $this->_calculate_content_height(); - $cb = $frame->get_containing_block(); - - $height = $style->length_in_pt($style->height, $cb["h"]); - - $top = $style->length_in_pt($style->top, $cb["h"]); - $bottom = $style->length_in_pt($style->bottom, $cb["h"]); - - $margin_top = $style->length_in_pt($style->margin_top, $cb["h"]); - $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]); - - if ( $frame->is_absolute() ) { - - // see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height - - $dims = array($top !== "auto" ? $top : 0, - $style->margin_top !== "auto" ? $style->margin_top : 0, - $style->padding_top, - $style->border_top_width, - $height !== "auto" ? $height : 0, - $style->border_bottom_width, - $style->padding_bottom, - $style->margin_bottom !== "auto" ? $style->margin_bottom : 0, - $bottom !== "auto" ? $bottom : 0); - - $sum = $style->length_in_pt($dims, $cb["h"]); - - $diff = $cb["h"] - $sum; - - if ( $diff > 0 ) { - - if ( $height === "auto" && $top === "auto" && $bottom === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $height = $diff; - } - else if ( $height === "auto" && $top === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $height = $content_height; - $top = $diff - $content_height; - } - else if ( $height === "auto" && $bottom === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $height = $content_height; - $bottom = $diff - $content_height; - } - else if ( $top === "auto" && $bottom === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $bottom = $diff; - } - else if ( $top === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $top = $diff; - } - else if ( $height === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $height = $diff; - } - else if ( $bottom === "auto" ) { - - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - - $bottom = $diff; - } - else { - - if ( $style->overflow === "visible" ) { - // set all autos to zero - if ( $margin_top === "auto" ) $margin_top = 0; - if ( $margin_bottom === "auto" ) $margin_bottom = 0; - if ( $top === "auto" ) $top = 0; - if ( $bottom === "auto" ) $bottom = 0; - if ( $height === "auto" ) $height = $content_height; - } - - // FIXME: overflow hidden - } - - } - - } - else { - - // Expand the height if overflow is visible - if ( $height === "auto" && $content_height > $height /* && $style->overflow === "visible" */) { - $height = $content_height; - } - - // FIXME: this should probably be moved to a seperate function as per - // _calculate_restricted_width - - // Only handle min/max height if the height is independent of the frame's content - if ( !($style->overflow === "visible" || - ($style->overflow === "hidden" && $height === "auto")) ) { - - $min_height = $style->min_height; - $max_height = $style->max_height; - - if ( isset($cb["h"]) ) { - $min_height = $style->length_in_pt($min_height, $cb["h"]); - $max_height = $style->length_in_pt($max_height, $cb["h"]); - - } - else if ( isset($cb["w"]) ) { - - if ( mb_strpos($min_height, "%") !== false ) { - $min_height = 0; - } - else { - $min_height = $style->length_in_pt($min_height, $cb["w"]); - } - - if ( mb_strpos($max_height, "%") !== false ) { - $max_height = "none"; - } - else { - $max_height = $style->length_in_pt($max_height, $cb["w"]); - } - } - - if ( $max_height !== "none" && $min_height > $max_height ) { - // Swap 'em - list($max_height, $min_height) = array($min_height, $max_height); - } - - if ( $max_height !== "none" && $height > $max_height ) { - $height = $max_height; - } - - if ( $height < $min_height ) { - $height = $min_height; - } - } - - } - - return array($height, $margin_top, $margin_bottom, $top, $bottom); - - } - - /** - * Adjust the justification of each of our lines. - * http://www.w3.org/TR/CSS21/text.html#propdef-text-align - */ - protected function _text_align() { - $style = $this->_frame->get_style(); - $w = $this->_frame->get_containing_block("w"); - $width = $style->length_in_pt($style->width, $w); - - switch ($style->text_align) { - default: - case "left": - foreach ($this->_frame->get_line_boxes() as $line) { - if ( !$line->left ) { - continue; - } - - foreach($line->get_frames() as $frame) { - if ( $frame instanceof Block_Frame_Decorator) { - continue; - } - $frame->set_position( $frame->get_position("x") + $line->left ); - } - } - return; - - case "right": - foreach ($this->_frame->get_line_boxes() as $line) { - // Move each child over by $dx - $dx = $width - $line->w - $line->right; - - foreach($line->get_frames() as $frame) { - // Block frames are not aligned by text-align - if ($frame instanceof Block_Frame_Decorator) { - continue; - } - - $frame->set_position( $frame->get_position("x") + $dx ); - } - } - break; - - - case "justify": - // We justify all lines except the last one - $lines = $this->_frame->get_line_boxes(); // needs to be a variable (strict standards) - array_pop($lines); - - foreach($lines as $i => $line) { - if ( $line->br ) { - unset($lines[$i]); - } - } - - // One space character's width. Will be used to get a more accurate spacing - $space_width = Font_Metrics::get_text_width(" ", $style->font_family, $style->font_size); - - foreach ($lines as $line) { - if ( $line->left ) { - foreach ( $line->get_frames() as $frame ) { - if ( !$frame instanceof Text_Frame_Decorator ) { - continue; - } - - $frame->set_position( $frame->get_position("x") + $line->left ); - } - } - - // Only set the spacing if the line is long enough. This is really - // just an aesthetic choice ;) - //if ( $line["left"] + $line["w"] + $line["right"] > self::MIN_JUSTIFY_WIDTH * $width ) { - - // Set the spacing for each child - if ( $line->wc > 1 ) { - $spacing = ($width - ($line->left + $line->w + $line->right) + $space_width) / ($line->wc - 1); - } - else { - $spacing = 0; - } - - $dx = 0; - foreach($line->get_frames() as $frame) { - if ( !$frame instanceof Text_Frame_Decorator ) { - continue; - } - - $text = $frame->get_text(); - $spaces = mb_substr_count($text, " "); - - $char_spacing = $style->length_in_pt($style->letter_spacing); - $_spacing = $spacing + $char_spacing; - - $frame->set_position( $frame->get_position("x") + $dx ); - $frame->set_text_spacing($_spacing); - - $dx += $spaces * $_spacing; - } - - // The line (should) now occupy the entire width - $line->w = $width; - - //} - } - break; - - case "center": - case "centre": - foreach ($this->_frame->get_line_boxes() as $line) { - // Centre each line by moving each frame in the line by: - $dx = ($width + $line->left - $line->w - $line->right ) / 2; - - foreach ($line->get_frames() as $frame) { - // Block frames are not aligned by text-align - if ($frame instanceof Block_Frame_Decorator) { - continue; - } - - $frame->set_position( $frame->get_position("x") + $dx ); - } - } - break; - } - } - - /** - * Align inline children vertically. - * Aligns each child vertically after each line is reflowed - */ - function vertical_align() { - - $canvas = null; - - foreach ( $this->_frame->get_line_boxes() as $line ) { - - $height = $line->h; - - foreach ( $line->get_frames() as $frame ) { - $style = $frame->get_style(); - - if ( $style->display !== "inline" ) { - continue; - } - - $align = $frame->get_parent()->get_style()->vertical_align; - - if ( !isset($canvas) ) { - $canvas = $frame->get_root()->get_dompdf()->get_canvas(); - } - - $baseline = $canvas->get_font_baseline($style->font_family, $style->font_size); - $y_offset = 0; - - switch ($align) { - case "baseline": - $y_offset = $height*0.8 - $baseline; // The 0.8 ratio is arbitrary until we find it's meaning - break; - - case "middle": - $y_offset = ($height*0.8 - $baseline) / 2; - break; - - case "sub": - $y_offset = 0.3 * $height; - break; - - case "super": - $y_offset = -0.2 * $height; - break; - - case "text-top": - case "top": // Not strictly accurate, but good enough for now - break; - - case "text-bottom": - case "bottom": - $y_offset = $height*0.8 - $baseline; - break; - } - - if ( $y_offset ) { - $frame->move(0, $y_offset); - } - } - } - } - - /** - * @param Frame $child - */ - function process_clear(Frame $child){ - $enable_css_float = $this->get_dompdf()->get_option("enable_css_float"); - if ( !$enable_css_float ) { - return; - } - - $child_style = $child->get_style(); - $root = $this->_frame->get_root(); - - // Handle "clear" - if ( $child_style->clear !== "none" ) { - $lowest_y = $root->get_lowest_float_offset($child); - - // If a float is still applying, we handle it - if ( $lowest_y ) { - if ( $child->is_in_flow() ) { - $line_box = $this->_frame->get_current_line_box(); - $line_box->y = $lowest_y + $child->get_margin_height(); - $line_box->left = 0; - $line_box->right = 0; - } - - $child->move(0, $lowest_y - $child->get_position("y")); - } - } - } - - /** - * @param Frame $child - * @param float $cb_x - * @param float $cb_w - */ - function process_float(Frame $child, $cb_x, $cb_w){ - $enable_css_float = $this->_frame->get_dompdf()->get_option("enable_css_float"); - if ( !$enable_css_float ) { - return; - } - - $child_style = $child->get_style(); - $root = $this->_frame->get_root(); - - // Handle "float" - if ( $child_style->float !== "none" ) { - $root->add_floating_frame($child); - - // Remove next frame's beginning whitespace - $next = $child->get_next_sibling(); - if ( $next && $next instanceof Text_Frame_Decorator) { - $next->set_text(ltrim($next->get_text())); - } - - $line_box = $this->_frame->get_current_line_box(); - list($old_x, $old_y) = $child->get_position(); - - $float_x = $cb_x; - $float_y = $old_y; - $float_w = $child->get_margin_width(); - - if ( $child_style->clear === "none" ) { - switch( $child_style->float ) { - case "left": - $float_x += $line_box->left; - break; - case "right": - $float_x += ($cb_w - $line_box->right - $float_w); - break; - } - } - else { - if ( $child_style->float === "right" ) { - $float_x += ($cb_w - $float_w); - } - } - - if ( $cb_w < $float_x + $float_w - $old_x ) { - // TODO handle when floating elements don't fit - } - - $line_box->get_float_offsets(); - - if ( $child->_float_next_line ) { - $float_y += $line_box->h; - } - - $child->set_position($float_x, $float_y); - $child->move($float_x - $old_x, $float_y - $old_y, true); - } - } - - /** - * @param Frame_Decorator $block - */ - function reflow(Block_Frame_Decorator $block = null) { - - // Check if a page break is forced - $page = $this->_frame->get_root(); - $page->check_forced_page_break($this->_frame); - - // Bail if the page is full - if ( $page->is_full() ) { - return; - } - - // Generated content - $this->_set_content(); - - // Collapse margins if required - $this->_collapse_margins(); - - $style = $this->_frame->get_style(); - $cb = $this->_frame->get_containing_block(); - - if ( $style->position === "fixed" ) { - $cb = $this->_frame->get_root()->get_containing_block(); - } - - // Determine the constraints imposed by this frame: calculate the width - // of the content area: - list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width(); - - // Store the calculated properties - $style->width = $w . "pt"; - $style->margin_left = $left_margin."pt"; - $style->margin_right = $right_margin."pt"; - $style->left = $left ."pt"; - $style->right = $right . "pt"; - - // Update the position - $this->_frame->position(); - list($x, $y) = $this->_frame->get_position(); - - // Adjust the first line based on the text-indent property - $indent = $style->length_in_pt($style->text_indent, $cb["w"]); - $this->_frame->increase_line_width($indent); - - // Determine the content edge - $top = $style->length_in_pt(array($style->margin_top, - $style->padding_top, - $style->border_top_width), $cb["h"]); - - $bottom = $style->length_in_pt(array($style->border_bottom_width, - $style->margin_bottom, - $style->padding_bottom), $cb["h"]); - - $cb_x = $x + $left_margin + $style->length_in_pt(array($style->border_left_width, - $style->padding_left), $cb["w"]); - - $cb_y = $y + $top; - - $cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y; - - // Set the y position of the first line in this block - $line_box = $this->_frame->get_current_line_box(); - $line_box->y = $cb_y; - $line_box->get_float_offsets(); - - // Set the containing blocks and reflow each child - foreach ( $this->_frame->get_children() as $child ) { - - // Bail out if the page is full - if ( $page->is_full() ) { - break; - } - - $child->set_containing_block($cb_x, $cb_y, $w, $cb_h); - - $this->process_clear($child); - - $child->reflow($this->_frame); - - // Don't add the child to the line if a page break has occurred - if ( $page->check_page_break($child) ) { - break; - } - - $this->process_float($child, $cb_x, $w); - } - - // Determine our height - list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height(); - $style->height = $height; - $style->margin_top = $margin_top; - $style->margin_bottom = $margin_bottom; - $style->top = $top; - $style->bottom = $bottom; - - $needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto")); - - // Absolute positioning measurement - if ( $needs_reposition ) { - $orig_style = $this->_frame->get_original_style(); - if ( $orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto") ) { - $width = 0; - foreach ($this->_frame->get_line_boxes() as $line) { - $width = max($line->w, $width); - } - $style->width = $width; - } - - $style->left = $orig_style->left; - $style->right = $orig_style->right; - } - - $this->_text_align(); - $this->vertical_align(); - - // Absolute positioning - if ( $needs_reposition ) { - list($x, $y) = $this->_frame->get_position(); - $this->_frame->position(); - list($new_x, $new_y) = $this->_frame->get_position(); - $this->_frame->move($new_x-$x, $new_y-$y, true); - } - - if ( $block && $this->_frame->is_in_flow() ) { - $block->add_frame_to_line($this->_frame); - - // May be inline-block - if ( $style->display === "block" ) { - $block->add_line(); - } - } - } -} diff --git a/library/vendor/dompdf/include/block_positioner.cls.php b/library/vendor/dompdf/include/block_positioner.cls.php deleted file mode 100644 index d7e1c3fbc..000000000 --- a/library/vendor/dompdf/include/block_positioner.cls.php +++ /dev/null @@ -1,57 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Positions block frames - * - * @access private - * @package dompdf - */ -class Block_Positioner extends Positioner { - - - function __construct(Frame_Decorator $frame) { parent::__construct($frame); } - - //........................................................................ - - function position() { - $frame = $this->_frame; - $style = $frame->get_style(); - $cb = $frame->get_containing_block(); - $p = $frame->find_block_parent(); - - if ( $p ) { - $float = $style->float; - - $enable_css_float = $frame->get_dompdf()->get_option("enable_css_float"); - if ( !$enable_css_float || !$float || $float === "none" ) { - $p->add_line(true); - } - $y = $p->get_current_line_box()->y; - - } - else { - $y = $cb["y"]; - } - - $x = $cb["x"]; - - // Relative positionning - if ( $style->position === "relative" ) { - $top = $style->length_in_pt($style->top, $cb["h"]); - //$right = $style->length_in_pt($style->right, $cb["w"]); - //$bottom = $style->length_in_pt($style->bottom, $cb["h"]); - $left = $style->length_in_pt($style->left, $cb["w"]); - - $x += $left; - $y += $top; - } - - $frame->set_position($x, $y); - } -} diff --git a/library/vendor/dompdf/include/block_renderer.cls.php b/library/vendor/dompdf/include/block_renderer.cls.php deleted file mode 100644 index ef42c93d5..000000000 --- a/library/vendor/dompdf/include/block_renderer.cls.php +++ /dev/null @@ -1,230 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Renders block frames - * - * @access private - * @package dompdf - */ -class Block_Renderer extends Abstract_Renderer { - - //........................................................................ - - function render(Frame $frame) { - $style = $frame->get_style(); - $node = $frame->get_node(); - - list($x, $y, $w, $h) = $frame->get_border_box(); - - $this->_set_opacity( $frame->get_opacity( $style->opacity ) ); - - if ( $node->nodeName === "body" ) { - $h = $frame->get_containing_block("h") - $style->length_in_pt(array( - $style->margin_top, - $style->border_top_width, - $style->border_bottom_width, - $style->margin_bottom), - $style->width); - } - - // Handle anchors & links - if ( $node->nodeName === "a" && $href = $node->getAttribute("href") ) { - $this->_canvas->add_link($href, $x, $y, $w, $h); - } - - // Draw our background, border and content - list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); - - if ( $tl + $tr + $br + $bl > 0 ) { - $this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl ); - } - - if ( ($bg = $style->background_color) !== "transparent" ) { - $this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg ); - } - - if ( ($url = $style->background_image) && $url !== "none" ) { - $this->_background_image($url, $x, $y, $w, $h, $style); - } - - if ( $tl + $tr + $br + $bl > 0 ) { - $this->_canvas->clipping_end(); - } - - $border_box = array($x, $y, $w, $h); - $this->_render_border($frame, $border_box); - $this->_render_outline($frame, $border_box); - - if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) { - $this->_debug_layout($frame->get_border_box(), "red"); - if (DEBUG_LAYOUT_PADDINGBOX) { - $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5)); - } - } - - if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES && $frame->get_decorator()) { - foreach ($frame->get_decorator()->get_line_boxes() as $line) { - $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange"); - } - } - } - - protected function _render_border(Frame_Decorator $frame, $border_box = null, $corner_style = "bevel") { - $style = $frame->get_style(); - $bp = $style->get_border_properties(); - - if ( empty($border_box) ) { - $border_box = $frame->get_border_box(); - } - - // find the radius - $radius = $style->get_computed_border_radius($border_box[2], $border_box[3]); // w, h - - // Short-cut: If all the borders are "solid" with the same color and style, and no radius, we'd better draw a rectangle - if ( - in_array($bp["top"]["style"], array("solid", "dashed", "dotted")) && - $bp["top"] == $bp["right"] && - $bp["right"] == $bp["bottom"] && - $bp["bottom"] == $bp["left"] && - array_sum($radius) == 0 - ) { - $props = $bp["top"]; - if ( $props["color"] === "transparent" || $props["width"] <= 0 ) return; - - list($x, $y, $w, $h) = $border_box; - $width = $style->length_in_pt($props["width"]); - $pattern = $this->_get_dash_pattern($props["style"], $width); - $this->_canvas->rectangle($x + $width / 2, $y + $width / 2, $w - $width, $h - $width, $props["color"], $width, $pattern); - return; - } - - // Do it the long way - $widths = array($style->length_in_pt($bp["top"]["width"]), - $style->length_in_pt($bp["right"]["width"]), - $style->length_in_pt($bp["bottom"]["width"]), - $style->length_in_pt($bp["left"]["width"])); - - foreach ($bp as $side => $props) { - list($x, $y, $w, $h) = $border_box; - $length = 0; - $r1 = 0; - $r2 = 0; - - if ( !$props["style"] || - $props["style"] === "none" || - $props["width"] <= 0 || - $props["color"] == "transparent" ) - continue; - - switch($side) { - case "top": - $length = $w; - $r1 = $radius["top-left"]; - $r2 = $radius["top-right"]; - break; - - case "bottom": - $length = $w; - $y += $h; - $r1 = $radius["bottom-left"]; - $r2 = $radius["bottom-right"]; - break; - - case "left": - $length = $h; - $r1 = $radius["top-left"]; - $r2 = $radius["bottom-left"]; - break; - - case "right": - $length = $h; - $x += $w; - $r1 = $radius["top-right"]; - $r2 = $radius["bottom-right"]; - break; - default: - break; - } - $method = "_border_" . $props["style"]; - - // draw rounded corners - $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style, $r1, $r2); - } - } - - protected function _render_outline(Frame_Decorator $frame, $border_box = null, $corner_style = "bevel") { - $style = $frame->get_style(); - - $props = array( - "width" => $style->outline_width, - "style" => $style->outline_style, - "color" => $style->outline_color, - ); - - if ( !$props["style"] || $props["style"] === "none" || $props["width"] <= 0 ) - return; - - if ( empty($border_box) ) { - $border_box = $frame->get_border_box(); - } - - $offset = $style->length_in_pt($props["width"]); - $pattern = $this->_get_dash_pattern($props["style"], $offset); - - // If the outline style is "solid" we'd better draw a rectangle - if ( in_array($props["style"], array("solid", "dashed", "dotted")) ) { - $border_box[0] -= $offset / 2; - $border_box[1] -= $offset / 2; - $border_box[2] += $offset; - $border_box[3] += $offset; - - list($x, $y, $w, $h) = $border_box; - $this->_canvas->rectangle($x, $y, $w, $h, $props["color"], $offset, $pattern); - return; - } - - $border_box[0] -= $offset; - $border_box[1] -= $offset; - $border_box[2] += $offset * 2; - $border_box[3] += $offset * 2; - - $method = "_border_" . $props["style"]; - $widths = array_fill(0, 4, $props["width"]); - $sides = array("top", "right", "left", "bottom"); - $length = 0; - - foreach ($sides as $side) { - list($x, $y, $w, $h) = $border_box; - - switch($side) { - case "top": - $length = $w; - break; - - case "bottom": - $length = $w; - $y += $h; - break; - - case "left": - $length = $h; - break; - - case "right": - $length = $h; - $x += $w; - break; - default: - break; - } - - $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style); - } - } -} diff --git a/library/vendor/dompdf/include/cached_pdf_decorator.cls.php b/library/vendor/dompdf/include/cached_pdf_decorator.cls.php deleted file mode 100644 index 519e572e8..000000000 --- a/library/vendor/dompdf/include/cached_pdf_decorator.cls.php +++ /dev/null @@ -1,164 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Caching canvas implementation - * - * Each rendered page is serialized and stored in the {@link Page_Cache}. - * This is useful for static forms/pages that do not need to be re-rendered - * all the time. - * - * This class decorates normal CPDF_Adapters. It is currently completely - * experimental. - * - * @access private - * @package dompdf - */ -class Cached_PDF_Decorator extends CPDF_Adapter implements Canvas { - /** - * @var CPDF_Adapter - */ - protected $_pdf; - protected $_cache_id; - protected $_current_page_id; - protected $_fonts; // fonts used in this document - - function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) { - $this->_fonts = array(); - } - - /** - * Must be called after constructor - * - * @param int $cache_id - * @param CPDF_Adapter $pdf - */ - function init($cache_id, CPDF_Adapter $pdf) { - $this->_cache_id = $cache_id; - $this->_pdf = $pdf; - $this->_current_page_id = $this->_pdf->open_object(); - } - - //........................................................................ - - function get_cpdf() { return $this->_pdf->get_cpdf(); } - - function open_object() { $this->_pdf->open_object(); } - function reopen_object($object) { $this->_pdf->reopen_object($object); } - - function close_object() { $this->_pdf->close_object(); } - - function add_object($object, $where = 'all') { $this->_pdf->add_object($object, $where); } - - function serialize_object($id) { $this->_pdf->serialize_object($id); } - - function reopen_serialized_object($obj) { $this->_pdf->reopen_serialized_object($obj); } - - //........................................................................ - - function get_width() { return $this->_pdf->get_width(); } - function get_height() { return $this->_pdf->get_height(); } - function get_page_number() { return $this->_pdf->get_page_number(); } - function get_page_count() { return $this->_pdf->get_page_count(); } - - function set_page_number($num) { $this->_pdf->set_page_number($num); } - function set_page_count($count) { $this->_pdf->set_page_count($count); } - - function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) { - $this->_pdf->line($x1, $y1, $x2, $y2, $color, $width, $style); - } - - function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) { - $this->_pdf->rectangle($x1, $y1, $w, $h, $color, $width, $style); - } - - function filled_rectangle($x1, $y1, $w, $h, $color) { - $this->_pdf->filled_rectangle($x1, $y1, $w, $h, $color); - } - - function polygon($points, $color, $width = null, $style = array(), $fill = false) { - $this->_pdf->polygon($points, $color, $width, $style, $fill); - } - - function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) { - $this->_pdf->circle($x, $y, $r1, $color, $width, $style, $fill); - } - - function image($img_url, $x, $y, $w, $h, $resolution = "normal") { - $this->_pdf->image($img_url, $x, $y, $w, $h, $resolution); - } - - function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) { - $this->_fonts[$font] = true; - $this->_pdf->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle); - } - - function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) { - - // We want to remove this from cached pages since it may not be correct - $this->_pdf->close_object(); - $this->_pdf->page_text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle); - $this->_pdf->reopen_object($this->_current_page_id); - } - - function page_script($script, $type = 'text/php') { - - // We want to remove this from cached pages since it may not be correct - $this->_pdf->close_object(); - $this->_pdf->page_script($script, $type); - $this->_pdf->reopen_object($this->_current_page_id); - } - - function new_page() { - $this->_pdf->close_object(); - - // Add the object to the current page - $this->_pdf->add_object($this->_current_page_id, "add"); - $this->_pdf->new_page(); - - Page_Cache::store_page($this->_cache_id, - $this->_pdf->get_page_number() - 1, - $this->_pdf->serialize_object($this->_current_page_id)); - - $this->_current_page_id = $this->_pdf->open_object(); - return $this->_current_page_id; - } - - function stream($filename, $options = null) { - // Store the last page in the page cache - if ( !is_null($this->_current_page_id) ) { - $this->_pdf->close_object(); - $this->_pdf->add_object($this->_current_page_id, "add"); - Page_Cache::store_page($this->_cache_id, - $this->_pdf->get_page_number(), - $this->_pdf->serialize_object($this->_current_page_id)); - Page_Cache::store_fonts($this->_cache_id, $this->_fonts); - $this->_current_page_id = null; - } - - $this->_pdf->stream($filename); - - } - - function output($options = null) { - // Store the last page in the page cache - if ( !is_null($this->_current_page_id) ) { - $this->_pdf->close_object(); - $this->_pdf->add_object($this->_current_page_id, "add"); - Page_Cache::store_page($this->_cache_id, - $this->_pdf->get_page_number(), - $this->_pdf->serialize_object($this->_current_page_id)); - $this->_current_page_id = null; - } - - return $this->_pdf->output(); - } - - function get_messages() { return $this->_pdf->get_messages(); } - -} diff --git a/library/vendor/dompdf/include/canvas.cls.php b/library/vendor/dompdf/include/canvas.cls.php deleted file mode 100644 index 0158df6b1..000000000 --- a/library/vendor/dompdf/include/canvas.cls.php +++ /dev/null @@ -1,385 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Main rendering interface - * - * Currently {@link CPDF_Adapter}, {@link PDFLib_Adapter}, {@link TCPDF_Adapter}, and {@link GD_Adapter} - * implement this interface. - * - * Implementations should measure x and y increasing to the left and down, - * respectively, with the origin in the top left corner. Implementations - * are free to use a unit other than points for length, but I can't - * guarantee that the results will look any good. - * - * @package dompdf - */ -interface Canvas { - function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf); - - /** - * @return DOMPDF - */ - function get_dompdf(); - - /** - * Returns the current page number - * - * @return int - */ - function get_page_number(); - - /** - * Returns the total number of pages - * - * @return int - */ - function get_page_count(); - - /** - * Sets the total number of pages - * - * @param int $count - */ - function set_page_count($count); - - /** - * Draws a line from x1,y1 to x2,y2 - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the format of the - * $style parameter (aka dash). - * - * @param float $x1 - * @param float $y1 - * @param float $x2 - * @param float $y2 - * @param array $color - * @param float $width - * @param array $style - */ - function line($x1, $y1, $x2, $y2, $color, $width, $style = null); - - /** - * Draws a rectangle at x1,y1 with width w and height h - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - * @param float $width - * @param array $style - */ - function rectangle($x1, $y1, $w, $h, $color, $width, $style = null); - - /** - * Draws a filled rectangle at x1,y1 with width w and height h - * - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - */ - function filled_rectangle($x1, $y1, $w, $h, $color); - - /** - * Starts a clipping rectangle at x1,y1 with width w and height h - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - */ - function clipping_rectangle($x1, $y1, $w, $h); - - /** - * Starts a rounded clipping rectangle at x1,y1 with width w and height h - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param float $tl - * @param float $tr - * @param float $br - * @param float $bl - * - * @return - */ - function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl); - - /** - * Ends the last clipping shape - */ - function clipping_end(); - - /** - * Save current state - */ - function save(); - - /** - * Restore last state - */ - function restore(); - - /** - * Rotate - */ - function rotate($angle, $x, $y); - - /** - * Skew - */ - function skew($angle_x, $angle_y, $x, $y); - - /** - * Scale - */ - function scale($s_x, $s_y, $x, $y); - - /** - * Translate - */ - function translate($t_x, $t_y); - - /** - * Transform - */ - function transform($a, $b, $c, $d, $e, $f); - - /** - * Draws a polygon - * - * The polygon is formed by joining all the points stored in the $points - * array. $points has the following structure: - * - * array(0 => x1, - * 1 => y1, - * 2 => x2, - * 3 => y2, - * ... - * ); - * - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param array $points - * @param array $color - * @param float $width - * @param array $style - * @param bool $fill Fills the polygon if true - */ - function polygon($points, $color, $width = null, $style = null, $fill = false); - - /** - * Draws a circle at $x,$y with radius $r - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param float $x - * @param float $y - * @param float $r - * @param array $color - * @param float $width - * @param array $style - * @param bool $fill Fills the circle if true - */ - function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false); - - /** - * Add an image to the pdf. - * - * The image is placed at the specified x and y coordinates with the - * given width and height. - * - * @param string $img_url the path to the image - * @param float $x x position - * @param float $y y position - * @param int $w width (in pixels) - * @param int $h height (in pixels) - * @param string $resolution The resolution of the image - */ - function image($img_url, $x, $y, $w, $h, $resolution = "normal"); - - /** - * Add an arc to the PDF - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x X coordinate of the arc - * @param float $y Y coordinate of the arc - * @param float $r1 Radius 1 - * @param float $r2 Radius 2 - * @param float $astart Start angle in degrees - * @param float $aend End angle in degrees - * @param array $color Color - * @param float $width - * @param array $style - * - * @return void - */ - function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array()); - - /** - * Writes text at the specified x and y coordinates - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x - * @param float $y - * @param string $text the text to write - * @param string $font the font file to use - * @param float $size the font size, in points - * @param array $color - * @param float $word_space word spacing adjustment - * @param float $char_space char spacing adjustment - * @param float $angle angle - * - * @return void - */ - function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0); - - /** - * Add a named destination (similar to ... in html) - * - * @param string $anchorname The name of the named destination - */ - function add_named_dest($anchorname); - - /** - * Add a link to the pdf - * - * @param string $url The url to link to - * @param float $x The x position of the link - * @param float $y The y position of the link - * @param float $width The width of the link - * @param float $height The height of the link - * - * @return void - */ - function add_link($url, $x, $y, $width, $height); - - /** - * Add meta information to the pdf - * - * @param string $name Label of the value (Creator, Producer, etc.) - * @param string $value The text to set - */ - function add_info($name, $value); - - /** - * Calculates text size, in points - * - * @param string $text the text to be sized - * @param string $font the desired font - * @param float $size the desired font size - * @param float $word_spacing word spacing, if any - * @param float $char_spacing - * - * @return float - */ - function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0); - - /** - * Calculates font height, in points - * - * @param string $font - * @param float $size - * - * @return float - */ - function get_font_height($font, $size); - - /** - * Calculates font baseline, in points - * - * @param string $font - * @param float $size - * - * @return float - */ - function get_font_baseline($font, $size); - - /** - * Returns the font x-height, in points - * - * @param string $font - * @param float $size - * - * @return float - */ - //function get_font_x_height($font, $size); - - /** - * Sets the opacity - * - * @param float $opacity - * @param string $mode - */ - function set_opacity($opacity, $mode = "Normal"); - - /** - * Sets the default view - * - * @param string $view - * 'XYZ' left, top, zoom - * 'Fit' - * 'FitH' top - * 'FitV' left - * 'FitR' left,bottom,right - * 'FitB' - * 'FitBH' top - * 'FitBV' left - * @param array $options - * - * @return void - */ - function set_default_view($view, $options = array()); - - /** - * @param string $script - * - * @return void - */ - function javascript($script); - - /** - * Starts a new page - * - * Subsequent drawing operations will appear on the new page. - */ - function new_page(); - - /** - * Streams the PDF directly to the browser - * - * @param string $filename the name of the PDF file - * @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0 - */ - function stream($filename, $options = null); - - /** - * Returns the PDF as a string - * - * @param array $options associative array: 'compress' => 1 or 0 - * @return string - */ - function output($options = null); -} diff --git a/library/vendor/dompdf/include/canvas_factory.cls.php b/library/vendor/dompdf/include/canvas_factory.cls.php deleted file mode 100644 index ef634e6b0..000000000 --- a/library/vendor/dompdf/include/canvas_factory.cls.php +++ /dev/null @@ -1,63 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Create canvas instances - * - * The canvas factory creates canvas instances based on the - * availability of rendering backends and config options. - * - * @package dompdf - */ -class Canvas_Factory { - - /** - * Constructor is private: this is a static class - */ - private function __construct() { } - - /** - * @param DOMPDF $dompdf - * @param string|array $paper - * @param string $orientation - * @param string $class - * - * @return Canvas - */ - static function get_instance(DOMPDF $dompdf, $paper = null, $orientation = null, $class = null) { - - $backend = strtolower(DOMPDF_PDF_BACKEND); - - if ( isset($class) && class_exists($class, false) ) { - $class .= "_Adapter"; - } - - else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "pdflib" ) && - class_exists("PDFLib", false) ) { - $class = "PDFLib_Adapter"; - } - - // FIXME The TCPDF adapter is not ready yet - //else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "cpdf") ) - // $class = "CPDF_Adapter"; - - else if ( $backend === "tcpdf" ) { - $class = "TCPDF_Adapter"; - } - - else if ( $backend === "gd" ) { - $class = "GD_Adapter"; - } - - else { - $class = "CPDF_Adapter"; - } - - return new $class($paper, $orientation, $dompdf); - } -} diff --git a/library/vendor/dompdf/include/cellmap.cls.php b/library/vendor/dompdf/include/cellmap.cls.php deleted file mode 100644 index 0982849b3..000000000 --- a/library/vendor/dompdf/include/cellmap.cls.php +++ /dev/null @@ -1,790 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Maps table cells to the table grid. - * - * This class resolves borders in tables with collapsed borders and helps - * place row & column spanned table cells. - * - * @access private - * @package dompdf - */ -class Cellmap { - - /** - * Border style weight lookup for collapsed border resolution. - * - * @var array - */ - static protected $_BORDER_STYLE_SCORE = array( - "inset" => 1, - "groove" => 2, - "outset" => 3, - "ridge" => 4, - "dotted" => 5, - "dashed" => 6, - "solid" => 7, - "double" => 8, - "hidden" => 9, - "none" => 0, - ); - - /** - * The table object this cellmap is attached to. - * - * @var Table_Frame_Decorator - */ - protected $_table; - - /** - * The total number of rows in the table - * - * @var int - */ - protected $_num_rows; - - /** - * The total number of columns in the table - * - * @var int - */ - protected $_num_cols; - - /** - * 2D array mapping to frames - * - * @var Frame[][] - */ - protected $_cells; - - /** - * 1D array of column dimensions - * - * @var array - */ - protected $_columns; - - /** - * 1D array of row dimensions - * - * @var array - */ - protected $_rows; - - /** - * 2D array of border specs - * - * @var array - */ - protected $_borders; - - /** - * 1D Array mapping frames to (multiple) pairs, keyed on frame_id. - * - * @var Frame[] - */ - protected $_frames; - - /** - * Current column when adding cells, 0-based - * - * @var int - */ - private $__col; - - /** - * Current row when adding cells, 0-based - * - * @var int - */ - private $__row; - - /** - * Tells wether the columns' width can be modified - * - * @var bool - */ - private $_columns_locked = false; - - /** - * Tells wether the table has table-layout:fixed - * - * @var bool - */ - private $_fixed_layout = false; - - //........................................................................ - - function __construct(Table_Frame_Decorator $table) { - $this->_table = $table; - $this->reset(); - } - - function __destruct() { - clear_object($this); - } - //........................................................................ - - function reset() { - $this->_num_rows = 0; - $this->_num_cols = 0; - - $this->_cells = array(); - $this->_frames = array(); - - if ( !$this->_columns_locked ) { - $this->_columns = array(); - } - - $this->_rows = array(); - - $this->_borders = array(); - - $this->__col = $this->__row = 0; - } - - //........................................................................ - - function lock_columns() { - $this->_columns_locked = true; - } - - function is_columns_locked() { - return $this->_columns_locked; - } - - function set_layout_fixed($fixed) { - $this->_fixed_layout = $fixed; - } - - function is_layout_fixed() { - return $this->_fixed_layout; - } - - function get_num_rows() { return $this->_num_rows; } - function get_num_cols() { return $this->_num_cols; } - - function &get_columns() { - return $this->_columns; - } - - function set_columns($columns) { - $this->_columns = $columns; - } - - function &get_column($i) { - if ( !isset($this->_columns[$i]) ) { - $this->_columns[$i] = array( - "x" => 0, - "min-width" => 0, - "max-width" => 0, - "used-width" => null, - "absolute" => 0, - "percent" => 0, - "auto" => true, - ); - } - - return $this->_columns[$i]; - } - - function &get_rows() { - return $this->_rows; - } - - function &get_row($j) { - if ( !isset($this->_rows[$j]) ) { - $this->_rows[$j] = array( - "y" => 0, - "first-column" => 0, - "height" => null, - ); - } - - return $this->_rows[$j]; - } - - function get_border($i, $j, $h_v, $prop = null) { - if ( !isset($this->_borders[$i][$j][$h_v]) ) { - $this->_borders[$i][$j][$h_v] = array( - "width" => 0, - "style" => "solid", - "color" => "black", - ); - } - - if ( isset($prop) ) { - return $this->_borders[$i][$j][$h_v][$prop]; - } - - return $this->_borders[$i][$j][$h_v]; - } - - function get_border_properties($i, $j) { - return array( - "top" => $this->get_border($i, $j, "horizontal"), - "right" => $this->get_border($i, $j+1, "vertical"), - "bottom" => $this->get_border($i+1, $j, "horizontal"), - "left" => $this->get_border($i, $j, "vertical"), - ); - } - - //........................................................................ - - function get_spanned_cells(Frame $frame) { - $key = $frame->get_id(); - - if ( !isset($this->_frames[$key]) ) { - throw new DOMPDF_Exception("Frame not found in cellmap"); - } - - return $this->_frames[$key]; - - } - - function frame_exists_in_cellmap(Frame $frame) { - $key = $frame->get_id(); - return isset($this->_frames[$key]); - } - - function get_frame_position(Frame $frame) { - global $_dompdf_warnings; - - $key = $frame->get_id(); - - if ( !isset($this->_frames[$key]) ) { - throw new DOMPDF_Exception("Frame not found in cellmap"); - } - - $col = $this->_frames[$key]["columns"][0]; - $row = $this->_frames[$key]["rows"][0]; - - if ( !isset($this->_columns[$col])) { - $_dompdf_warnings[] = "Frame not found in columns array. Check your table layout for missing or extra TDs."; - $x = 0; - } - else { - $x = $this->_columns[$col]["x"]; - } - - if ( !isset($this->_rows[$row])) { - $_dompdf_warnings[] = "Frame not found in row array. Check your table layout for missing or extra TDs."; - $y = 0; - } - else { - $y = $this->_rows[$row]["y"]; - } - - return array($x, $y, "x" => $x, "y" => $y); - } - - function get_frame_width(Frame $frame) { - $key = $frame->get_id(); - - if ( !isset($this->_frames[$key]) ) { - throw new DOMPDF_Exception("Frame not found in cellmap"); - } - - $cols = $this->_frames[$key]["columns"]; - $w = 0; - foreach ($cols as $i) { - $w += $this->_columns[$i]["used-width"]; - } - - return $w; - } - - function get_frame_height(Frame $frame) { - $key = $frame->get_id(); - - if ( !isset($this->_frames[$key]) ) { - throw new DOMPDF_Exception("Frame not found in cellmap"); - } - - $rows = $this->_frames[$key]["rows"]; - $h = 0; - foreach ($rows as $i) { - if ( !isset($this->_rows[$i]) ) { - throw new Exception("The row #$i could not be found, please file an issue in the tracker with the HTML code"); - } - - $h += $this->_rows[$i]["height"]; - } - - return $h; - } - - - //........................................................................ - - function set_column_width($j, $width) { - if ( $this->_columns_locked ) { - return; - } - - $col =& $this->get_column($j); - $col["used-width"] = $width; - $next_col =& $this->get_column($j+1); - $next_col["x"] = $next_col["x"] + $width; - } - - function set_row_height($i, $height) { - $row =& $this->get_row($i); - - if ( $row["height"] !== null && $height <= $row["height"] ) { - return; - } - - $row["height"] = $height; - $next_row =& $this->get_row($i+1); - $next_row["y"] = $row["y"] + $height; - - } - - //........................................................................ - - - protected function _resolve_border($i, $j, $h_v, $border_spec) { - $n_width = $border_spec["width"]; - $n_style = $border_spec["style"]; - - if ( !isset($this->_borders[$i][$j][$h_v]) ) { - $this->_borders[$i][$j][$h_v] = $border_spec; - return $this->_borders[$i][$j][$h_v]["width"]; - } - - $border = &$this->_borders[$i][$j][$h_v]; - - $o_width = $border["width"]; - $o_style = $border["style"]; - - if ( ($n_style === "hidden" || - $n_width > $o_width || - $o_style === "none") - - or - - ($o_width == $n_width && - in_array($n_style, self::$_BORDER_STYLE_SCORE) && - self::$_BORDER_STYLE_SCORE[ $n_style ] > self::$_BORDER_STYLE_SCORE[ $o_style ]) ) { - $border = $border_spec; - } - - return $border["width"]; - } - - //........................................................................ - - function add_frame(Frame $frame) { - - $style = $frame->get_style(); - $display = $style->display; - - $collapse = $this->_table->get_style()->border_collapse == "collapse"; - - // Recursively add the frames within tables, table-row-groups and table-rows - if ( $display === "table-row" || - $display === "table" || - $display === "inline-table" || - in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) { - - $start_row = $this->__row; - foreach ( $frame->get_children() as $child ) { - $this->add_frame( $child ); - } - - if ( $display === "table-row" ) { - $this->add_row(); - } - - $num_rows = $this->__row - $start_row - 1; - $key = $frame->get_id(); - - // Row groups always span across the entire table - $this->_frames[$key]["columns"] = range(0,max(0,$this->_num_cols-1)); - $this->_frames[$key]["rows"] = range($start_row, max(0, $this->__row - 1)); - $this->_frames[$key]["frame"] = $frame; - - if ( $display !== "table-row" && $collapse ) { - - $bp = $style->get_border_properties(); - - // Resolve the borders - for ( $i = 0; $i < $num_rows+1; $i++) { - $this->_resolve_border($start_row + $i, 0, "vertical", $bp["left"]); - $this->_resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]); - } - - for ( $j = 0; $j < $this->_num_cols; $j++) { - $this->_resolve_border($start_row, $j, "horizontal", $bp["top"]); - $this->_resolve_border($this->__row, $j, "horizontal", $bp["bottom"]); - } - } - - - return; - } - - $node = $frame->get_node(); - - // Determine where this cell is going - $colspan = $node->getAttribute("colspan"); - $rowspan = $node->getAttribute("rowspan"); - - if ( !$colspan ) { - $colspan = 1; - $node->setAttribute("colspan",1); - } - - if ( !$rowspan ) { - $rowspan = 1; - $node->setAttribute("rowspan",1); - } - $key = $frame->get_id(); - - $bp = $style->get_border_properties(); - - - // Add the frame to the cellmap - $max_left = $max_right = 0; - - // Find the next available column (fix by Ciro Mondueri) - $ac = $this->__col; - while ( isset($this->_cells[$this->__row][$ac]) ) { - $ac++; - } - - $this->__col = $ac; - - // Rows: - for ( $i = 0; $i < $rowspan; $i++ ) { - $row = $this->__row + $i; - - $this->_frames[$key]["rows"][] = $row; - - for ( $j = 0; $j < $colspan; $j++) { - $this->_cells[$row][$this->__col + $j] = $frame; - } - - if ( $collapse ) { - // Resolve vertical borders - $max_left = max($max_left, $this->_resolve_border($row, $this->__col, "vertical", $bp["left"])); - $max_right = max($max_right, $this->_resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"])); - } - } - - $max_top = $max_bottom = 0; - - // Columns: - for ( $j = 0; $j < $colspan; $j++ ) { - $col = $this->__col + $j; - $this->_frames[$key]["columns"][] = $col; - - if ( $collapse ) { - // Resolve horizontal borders - $max_top = max($max_top, $this->_resolve_border($this->__row, $col, "horizontal", $bp["top"])); - $max_bottom = max($max_bottom, $this->_resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"])); - } - } - - $this->_frames[$key]["frame"] = $frame; - - // Handle seperated border model - if ( !$collapse ) { - list($h, $v) = $this->_table->get_style()->border_spacing; - - // Border spacing is effectively a margin between cells - $v = $style->length_in_pt($v) / 2; - $h = $style->length_in_pt($h) / 2; - $style->margin = "$v $h"; - - // The additional 1/2 width gets added to the table proper - } - else { - // Drop the frame's actual border - $style->border_left_width = $max_left / 2; - $style->border_right_width = $max_right / 2; - $style->border_top_width = $max_top / 2; - $style->border_bottom_width = $max_bottom / 2; - $style->margin = "none"; - } - - if ( !$this->_columns_locked ) { - // Resolve the frame's width - if ( $this->_fixed_layout ) { - list($frame_min, $frame_max) = array(0, 10e-10); - } - else { - list($frame_min, $frame_max) = $frame->get_min_max_width(); - } - - $width = $style->width; - - $val = null; - if ( is_percent($width) ) { - $var = "percent"; - $val = (float)rtrim($width, "% ") / $colspan; - } - else if ( $width !== "auto" ) { - $var = "absolute"; - $val = $style->length_in_pt($frame_min) / $colspan; - } - - $min = 0; - $max = 0; - for ( $cs = 0; $cs < $colspan; $cs++ ) { - - // Resolve the frame's width(s) with other cells - $col =& $this->get_column( $this->__col + $cs ); - - // Note: $var is either 'percent' or 'absolute'. We compare the - // requested percentage or absolute values with the existing widths - // and adjust accordingly. - if ( isset($var) && $val > $col[$var] ) { - $col[$var] = $val; - $col["auto"] = false; - } - - $min += $col["min-width"]; - $max += $col["max-width"]; - } - - if ( $frame_min > $min ) { - // The frame needs more space. Expand each sub-column - // FIXME try to avoid putting this dummy value when table-layout:fixed - $inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_min - $min) / $colspan); - for ($c = 0; $c < $colspan; $c++) { - $col =& $this->get_column($this->__col + $c); - $col["min-width"] += $inc; - } - } - - if ( $frame_max > $max ) { - // FIXME try to avoid putting this dummy value when table-layout:fixed - $inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_max - $max) / $colspan); - for ($c = 0; $c < $colspan; $c++) { - $col =& $this->get_column($this->__col + $c); - $col["max-width"] += $inc; - } - } - } - - $this->__col += $colspan; - if ( $this->__col > $this->_num_cols ) - $this->_num_cols = $this->__col; - - } - - //........................................................................ - - function add_row() { - - $this->__row++; - $this->_num_rows++; - - // Find the next available column - $i = 0; - while ( isset($this->_cells[$this->__row][$i]) ) { - $i++; - } - - $this->__col = $i; - - } - - //........................................................................ - - /** - * Remove a row from the cellmap. - * - * @param Frame - */ - function remove_row(Frame $row) { - - $key = $row->get_id(); - if ( !isset($this->_frames[$key]) ) { - return; // Presumably this row has alredy been removed - } - - $this->_row = $this->_num_rows--; - - $rows = $this->_frames[$key]["rows"]; - $columns = $this->_frames[$key]["columns"]; - - // Remove all frames from this row - foreach ( $rows as $r ) { - foreach ( $columns as $c ) { - if ( isset($this->_cells[$r][$c]) ) { - $id = $this->_cells[$r][$c]->get_id(); - - $this->_frames[$id] = null; - unset($this->_frames[$id]); - - $this->_cells[$r][$c] = null; - unset($this->_cells[$r][$c]); - } - } - - $this->_rows[$r] = null; - unset($this->_rows[$r]); - } - - $this->_frames[$key] = null; - unset($this->_frames[$key]); - - } - - /** - * Remove a row group from the cellmap. - * - * @param Frame $group The group to remove - */ - function remove_row_group(Frame $group) { - - $key = $group->get_id(); - if ( !isset($this->_frames[$key]) ) { - return; // Presumably this row has alredy been removed - } - - $iter = $group->get_first_child(); - while ($iter) { - $this->remove_row($iter); - $iter = $iter->get_next_sibling(); - } - - $this->_frames[$key] = null; - unset($this->_frames[$key]); - } - - /** - * Update a row group after rows have been removed - * - * @param Frame $group The group to update - * @param Frame $last_row The last row in the row group - */ - function update_row_group(Frame $group, Frame $last_row) { - - $g_key = $group->get_id(); - $r_key = $last_row->get_id(); - - $r_rows = $this->_frames[$r_key]["rows"]; - $this->_frames[$g_key]["rows"] = range( $this->_frames[$g_key]["rows"][0], end($r_rows) ); - - } - - //........................................................................ - - function assign_x_positions() { - // Pre-condition: widths must be resolved and assigned to columns and - // column[0]["x"] must be set. - - if ( $this->_columns_locked ) { - return; - } - - $x = $this->_columns[0]["x"]; - foreach ( array_keys($this->_columns) as $j ) { - $this->_columns[$j]["x"] = $x; - $x += $this->_columns[$j]["used-width"]; - } - - } - - function assign_frame_heights() { - // Pre-condition: widths and heights of each column & row must be - // calcluated - - foreach ( $this->_frames as $arr ) { - $frame = $arr["frame"]; - - $h = 0; - foreach( $arr["rows"] as $row ) { - if ( !isset($this->_rows[$row]) ) { - // The row has been removed because of a page split, so skip it. - continue; - } - - $h += $this->_rows[$row]["height"]; - } - - if ( $frame instanceof Table_Cell_Frame_Decorator ) { - $frame->set_cell_height($h); - } - else { - $frame->get_style()->height = $h; - } - } - - } - - //........................................................................ - - /** - * Re-adjust frame height if the table height is larger than its content - */ - function set_frame_heights($table_height, $content_height) { - - - // Distribute the increased height proportionally amongst each row - foreach ( $this->_frames as $arr ) { - $frame = $arr["frame"]; - - $h = 0; - foreach ($arr["rows"] as $row ) { - if ( !isset($this->_rows[$row]) ) { - continue; - } - - $h += $this->_rows[$row]["height"]; - } - - if ( $content_height > 0 ) { - $new_height = ($h / $content_height) * $table_height; - } - else { - $new_height = 0; - } - - if ( $frame instanceof Table_Cell_Frame_Decorator ) { - $frame->set_cell_height($new_height); - } - else { - $frame->get_style()->height = $new_height; - } - } - - } - - //........................................................................ - - // Used for debugging: - function __toString() { - $str = ""; - $str .= "Columns:
"; - $str .= pre_r($this->_columns, true); - $str .= "Rows:
"; - $str .= pre_r($this->_rows, true); - - $str .= "Frames:
"; - $arr = array(); - foreach ( $this->_frames as $key => $val ) { - $arr[$key] = array("columns" => $val["columns"], "rows" => $val["rows"]); - } - - $str .= pre_r($arr, true); - - if ( php_sapi_name() == "cli" ) { - $str = strip_tags(str_replace(array("
","",""), - array("\n",chr(27)."[01;33m", chr(27)."[0m"), - $str)); - } - - return $str; - } -} diff --git a/library/vendor/dompdf/include/cpdf_adapter.cls.php b/library/vendor/dompdf/include/cpdf_adapter.cls.php deleted file mode 100644 index 041e1f375..000000000 --- a/library/vendor/dompdf/include/cpdf_adapter.cls.php +++ /dev/null @@ -1,877 +0,0 @@ - - * @author Orion Richardson - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -// FIXME: Need to sanity check inputs to this class -require_once(DOMPDF_LIB_DIR . "/class.pdf.php"); - -/** - * PDF rendering interface - * - * CPDF_Adapter provides a simple stateless interface to the stateful one - * provided by the Cpdf class. - * - * Unless otherwise mentioned, all dimensions are in points (1/72 in). The - * coordinate origin is in the top left corner, and y values increase - * downwards. - * - * See {@link http://www.ros.co.nz/pdf/} for more complete documentation - * on the underlying {@link Cpdf} class. - * - * @package dompdf - */ -class CPDF_Adapter implements Canvas { - - /** - * Dimensions of paper sizes in points - * - * @var array; - */ - static $PAPER_SIZES = array( - "4a0" => array(0,0,4767.87,6740.79), - "2a0" => array(0,0,3370.39,4767.87), - "a0" => array(0,0,2383.94,3370.39), - "a1" => array(0,0,1683.78,2383.94), - "a2" => array(0,0,1190.55,1683.78), - "a3" => array(0,0,841.89,1190.55), - "a4" => array(0,0,595.28,841.89), - "a5" => array(0,0,419.53,595.28), - "a6" => array(0,0,297.64,419.53), - "a7" => array(0,0,209.76,297.64), - "a8" => array(0,0,147.40,209.76), - "a9" => array(0,0,104.88,147.40), - "a10" => array(0,0,73.70,104.88), - "b0" => array(0,0,2834.65,4008.19), - "b1" => array(0,0,2004.09,2834.65), - "b2" => array(0,0,1417.32,2004.09), - "b3" => array(0,0,1000.63,1417.32), - "b4" => array(0,0,708.66,1000.63), - "b5" => array(0,0,498.90,708.66), - "b6" => array(0,0,354.33,498.90), - "b7" => array(0,0,249.45,354.33), - "b8" => array(0,0,175.75,249.45), - "b9" => array(0,0,124.72,175.75), - "b10" => array(0,0,87.87,124.72), - "c0" => array(0,0,2599.37,3676.54), - "c1" => array(0,0,1836.85,2599.37), - "c2" => array(0,0,1298.27,1836.85), - "c3" => array(0,0,918.43,1298.27), - "c4" => array(0,0,649.13,918.43), - "c5" => array(0,0,459.21,649.13), - "c6" => array(0,0,323.15,459.21), - "c7" => array(0,0,229.61,323.15), - "c8" => array(0,0,161.57,229.61), - "c9" => array(0,0,113.39,161.57), - "c10" => array(0,0,79.37,113.39), - "ra0" => array(0,0,2437.80,3458.27), - "ra1" => array(0,0,1729.13,2437.80), - "ra2" => array(0,0,1218.90,1729.13), - "ra3" => array(0,0,864.57,1218.90), - "ra4" => array(0,0,609.45,864.57), - "sra0" => array(0,0,2551.18,3628.35), - "sra1" => array(0,0,1814.17,2551.18), - "sra2" => array(0,0,1275.59,1814.17), - "sra3" => array(0,0,907.09,1275.59), - "sra4" => array(0,0,637.80,907.09), - "letter" => array(0,0,612.00,792.00), - "legal" => array(0,0,612.00,1008.00), - "ledger" => array(0,0,1224.00, 792.00), - "tabloid" => array(0,0,792.00, 1224.00), - "executive" => array(0,0,521.86,756.00), - "folio" => array(0,0,612.00,936.00), - "commercial #10 envelope" => array(0,0,684,297), - "catalog #10 1/2 envelope" => array(0,0,648,864), - "8.5x11" => array(0,0,612.00,792.00), - "8.5x14" => array(0,0,612.00,1008.0), - "11x17" => array(0,0,792.00, 1224.00), - ); - - /** - * The DOMPDF object - * - * @var DOMPDF - */ - private $_dompdf; - - /** - * Instance of Cpdf class - * - * @var Cpdf - */ - private $_pdf; - - /** - * PDF width, in points - * - * @var float - */ - private $_width; - - /** - * PDF height, in points - * - * @var float; - */ - private $_height; - - /** - * Current page number - * - * @var int - */ - private $_page_number; - - /** - * Total number of pages - * - * @var int - */ - private $_page_count; - - /** - * Text to display on every page - * - * @var array - */ - private $_page_text; - - /** - * Array of pages for accesing after rendering is initially complete - * - * @var array - */ - private $_pages; - - /** - * Array of temporary cached images to be deleted when processing is complete - * - * @var array - */ - private $_image_cache; - - /** - * Class constructor - * - * @param mixed $paper The size of paper to use in this PDF ({@link CPDF_Adapter::$PAPER_SIZES}) - * @param string $orientation The orientation of the document (either 'landscape' or 'portrait') - * @param DOMPDF $dompdf The DOMPDF instance - */ - function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) { - if ( is_array($paper) ) { - $size = $paper; - } - else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) ) { - $size = self::$PAPER_SIZES[mb_strtolower($paper)]; - } - else { - $size = self::$PAPER_SIZES["letter"]; - } - - if ( mb_strtolower($orientation) === "landscape" ) { - list($size[2], $size[3]) = array($size[3], $size[2]); - } - - $this->_dompdf = $dompdf; - - $this->_pdf = new Cpdf( - $size, - $dompdf->get_option("enable_unicode"), - $dompdf->get_option("font_cache"), - $dompdf->get_option("temp_dir") - ); - - $this->_pdf->addInfo("Creator", "DOMPDF"); - $time = substr_replace(date('YmdHisO'), '\'', -2, 0).'\''; - $this->_pdf->addInfo("CreationDate", "D:$time"); - $this->_pdf->addInfo("ModDate", "D:$time"); - - $this->_width = $size[2] - $size[0]; - $this->_height= $size[3] - $size[1]; - - $this->_page_number = $this->_page_count = 1; - $this->_page_text = array(); - - $this->_pages = array($this->_pdf->getFirstPageId()); - - $this->_image_cache = array(); - } - - function get_dompdf(){ - return $this->_dompdf; - } - - /** - * Class destructor - * - * Deletes all temporary image files - */ - function __destruct() { - foreach ($this->_image_cache as $img) { - // The file might be already deleted by 3rd party tmp cleaner, - // the file might not have been created at all - // (if image outputting commands failed) - // or because the destructor was called twice accidentally. - if (!file_exists($img)) { - continue; - } - - if (DEBUGPNG) print '[__destruct unlink '.$img.']'; - if (!DEBUGKEEPTEMP) unlink($img); - } - } - - /** - * Returns the Cpdf instance - * - * @return Cpdf - */ - function get_cpdf() { - return $this->_pdf; - } - - /** - * Add meta information to the PDF - * - * @param string $label label of the value (Creator, Producer, etc.) - * @param string $value the text to set - */ - function add_info($label, $value) { - $this->_pdf->addInfo($label, $value); - } - - /** - * Opens a new 'object' - * - * While an object is open, all drawing actions are recored in the object, - * as opposed to being drawn on the current page. Objects can be added - * later to a specific page or to several pages. - * - * The return value is an integer ID for the new object. - * - * @see CPDF_Adapter::close_object() - * @see CPDF_Adapter::add_object() - * - * @return int - */ - function open_object() { - $ret = $this->_pdf->openObject(); - $this->_pdf->saveState(); - return $ret; - } - - /** - * Reopens an existing 'object' - * - * @see CPDF_Adapter::open_object() - * @param int $object the ID of a previously opened object - */ - function reopen_object($object) { - $this->_pdf->reopenObject($object); - $this->_pdf->saveState(); - } - - /** - * Closes the current 'object' - * - * @see CPDF_Adapter::open_object() - */ - function close_object() { - $this->_pdf->restoreState(); - $this->_pdf->closeObject(); - } - - /** - * Adds a specified 'object' to the document - * - * $object int specifying an object created with {@link - * CPDF_Adapter::open_object()}. $where can be one of: - * - 'add' add to current page only - * - 'all' add to every page from the current one onwards - * - 'odd' add to all odd numbered pages from now on - * - 'even' add to all even numbered pages from now on - * - 'next' add the object to the next page only - * - 'nextodd' add to all odd numbered pages from the next one - * - 'nexteven' add to all even numbered pages from the next one - * - * @see Cpdf::addObject() - * - * @param int $object - * @param string $where - */ - function add_object($object, $where = 'all') { - $this->_pdf->addObject($object, $where); - } - - /** - * Stops the specified 'object' from appearing in the document. - * - * The object will stop being displayed on the page following the current - * one. - * - * @param int $object - */ - function stop_object($object) { - $this->_pdf->stopObject($object); - } - - /** - * @access private - */ - function serialize_object($id) { - // Serialize the pdf object's current state for retrieval later - return $this->_pdf->serializeObject($id); - } - - /** - * @access private - */ - function reopen_serialized_object($obj) { - return $this->_pdf->restoreSerializedObject($obj); - } - - //........................................................................ - - /** - * Returns the PDF's width in points - * @return float - */ - function get_width() { return $this->_width; } - - /** - * Returns the PDF's height in points - * @return float - */ - function get_height() { return $this->_height; } - - /** - * Returns the current page number - * @return int - */ - function get_page_number() { return $this->_page_number; } - - /** - * Returns the total number of pages in the document - * @return int - */ - function get_page_count() { return $this->_page_count; } - - /** - * Sets the current page number - * - * @param int $num - */ - function set_page_number($num) { $this->_page_number = $num; } - - /** - * Sets the page count - * - * @param int $count - */ - function set_page_count($count) { $this->_page_count = $count; } - - /** - * Sets the stroke color - * - * See {@link Style::set_color()} for the format of the color array. - * @param array $color - */ - protected function _set_stroke_color($color) { - $this->_pdf->setStrokeColor($color); - } - - /** - * Sets the fill colour - * - * See {@link Style::set_color()} for the format of the colour array. - * @param array $color - */ - protected function _set_fill_color($color) { - $this->_pdf->setColor($color); - } - - /** - * Sets line transparency - * @see Cpdf::setLineTransparency() - * - * Valid blend modes are (case-sensitive): - * - * Normal, Multiply, Screen, Overlay, Darken, Lighten, - * ColorDodge, ColorBurn, HardLight, SoftLight, Difference, - * Exclusion - * - * @param string $mode the blending mode to use - * @param float $opacity 0.0 fully transparent, 1.0 fully opaque - */ - protected function _set_line_transparency($mode, $opacity) { - $this->_pdf->setLineTransparency($mode, $opacity); - } - - /** - * Sets fill transparency - * @see Cpdf::setFillTransparency() - * - * Valid blend modes are (case-sensitive): - * - * Normal, Multiply, Screen, Overlay, Darken, Lighten, - * ColorDogde, ColorBurn, HardLight, SoftLight, Difference, - * Exclusion - * - * @param string $mode the blending mode to use - * @param float $opacity 0.0 fully transparent, 1.0 fully opaque - */ - protected function _set_fill_transparency($mode, $opacity) { - $this->_pdf->setFillTransparency($mode, $opacity); - } - - /** - * Sets the line style - * - * @see Cpdf::setLineStyle() - * - * @param float $width - * @param string $cap - * @param string $join - * @param array $dash - */ - protected function _set_line_style($width, $cap, $join, $dash) { - $this->_pdf->setLineStyle($width, $cap, $join, $dash); - } - - /** - * Sets the opacity - * - * @param $opacity - * @param $mode - */ - function set_opacity($opacity, $mode = "Normal") { - $this->_set_line_transparency($mode, $opacity); - $this->_set_fill_transparency($mode, $opacity); - } - - function set_default_view($view, $options = array()) { - array_unshift($options, $view); - call_user_func_array(array($this->_pdf, "openHere"), $options); - } - - /** - * Remaps y coords from 4th to 1st quadrant - * - * @param float $y - * @return float - */ - protected function y($y) { - return $this->_height - $y; - } - - // Canvas implementation - function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) { - $this->_set_stroke_color($color); - $this->_set_line_style($width, "butt", "", $style); - - $this->_pdf->line($x1, $this->y($y1), - $x2, $this->y($y2)); - } - - function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array()) { - $this->_set_stroke_color($color); - $this->_set_line_style($width, "butt", "", $style); - - $this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false); - } - - //........................................................................ - - /** - * Convert a GIF or BMP image to a PNG image - * - * @param string $image_url - * @param integer $type - * - * @throws DOMPDF_Exception - * @return string The url of the newly converted image - */ - protected function _convert_gif_bmp_to_png($image_url, $type) { - $image_type = Image_Cache::type_to_ext($type); - $func_name = "imagecreatefrom$image_type"; - - if ( !function_exists($func_name) ) { - throw new DOMPDF_Exception("Function $func_name() not found. Cannot convert $image_type image: $image_url. Please install the image PHP extension."); - } - - set_error_handler("record_warnings"); - $im = $func_name($image_url); - - if ( $im ) { - imageinterlace($im, false); - - $tmp_dir = $this->_dompdf->get_option("temp_dir"); - $tmp_name = tempnam($tmp_dir, "{$image_type}dompdf_img_"); - @unlink($tmp_name); - $filename = "$tmp_name.png"; - $this->_image_cache[] = $filename; - - imagepng($im, $filename); - imagedestroy($im); - } - else { - $filename = Image_Cache::$broken_image; - } - - restore_error_handler(); - - return $filename; - } - - function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) { - $this->_set_stroke_color($color); - $this->_set_line_style($width, "butt", "", $style); - $this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h); - } - - function filled_rectangle($x1, $y1, $w, $h, $color) { - $this->_set_fill_color($color); - $this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h); - } - - function clipping_rectangle($x1, $y1, $w, $h) { - $this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h); - } - - function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) { - $this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL); - } - - function clipping_end() { - $this->_pdf->clippingEnd(); - } - - function save() { - $this->_pdf->saveState(); - } - - function restore() { - $this->_pdf->restoreState(); - } - - function rotate($angle, $x, $y) { - $this->_pdf->rotate($angle, $x, $y); - } - - function skew($angle_x, $angle_y, $x, $y) { - $this->_pdf->skew($angle_x, $angle_y, $x, $y); - } - - function scale($s_x, $s_y, $x, $y) { - $this->_pdf->scale($s_x, $s_y, $x, $y); - } - - function translate($t_x, $t_y) { - $this->_pdf->translate($t_x, $t_y); - } - - function transform($a, $b, $c, $d, $e, $f) { - $this->_pdf->transform(array($a, $b, $c, $d, $e, $f)); - } - - function polygon($points, $color, $width = null, $style = array(), $fill = false) { - $this->_set_fill_color($color); - $this->_set_stroke_color($color); - - // Adjust y values - for ( $i = 1; $i < count($points); $i += 2) { - $points[$i] = $this->y($points[$i]); - } - - $this->_pdf->polygon($points, count($points) / 2, $fill); - } - - function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) { - $this->_set_fill_color($color); - $this->_set_stroke_color($color); - - if ( !$fill && isset($width) ) { - $this->_set_line_style($width, "round", "round", $style); - } - - $this->_pdf->ellipse($x, $this->y($y), $r1, 0, 0, 8, 0, 360, 1, $fill); - } - - function image($img, $x, $y, $w, $h, $resolution = "normal") { - list($width, $height, $type) = dompdf_getimagesize($img, $this->_dompdf->get_http_context()); - - $debug_png = $this->_dompdf->get_option("debug_png"); - - if ($debug_png) print "[image:$img|$width|$height|$type]"; - - switch ($type) { - case IMAGETYPE_JPEG: - if ($debug_png) print '!!!jpg!!!'; - $this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h); - break; - - case IMAGETYPE_GIF: - case IMAGETYPE_BMP: - if ($debug_png) print '!!!bmp or gif!!!'; - // @todo use cache for BMP and GIF - $img = $this->_convert_gif_bmp_to_png($img, $type); - - case IMAGETYPE_PNG: - if ($debug_png) print '!!!png!!!'; - - $this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h); - break; - - default: - if ($debug_png) print '!!!unknown!!!'; - } - } - - function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) { - $pdf = $this->_pdf; - - $pdf->setColor($color); - - $font .= ".afm"; - $pdf->selectFont($font); - - //Font_Metrics::get_font_height($font, $size) == - //$this->get_font_height($font, $size) == - //$this->_pdf->selectFont($font),$this->_pdf->getFontHeight($size) - //- FontBBoxheight+FontHeightOffset, scaled to $size, in pt - //$this->_pdf->getFontDescender($size) - //- Descender scaled to size - // - //$this->_pdf->fonts[$this->_pdf->currentFont] sizes: - //['FontBBox'][0] left, ['FontBBox'][1] bottom, ['FontBBox'][2] right, ['FontBBox'][3] top - //Maximum extent of all glyphs of the font from the baseline point - //['Ascender'] maximum height above baseline except accents - //['Descender'] maximum depth below baseline, negative number means below baseline - //['FontHeightOffset'] manual enhancement of .afm files to trim windows fonts. currently not used. - //Values are in 1/1000 pt for a font size of 1 pt - // - //['FontBBox'][1] should be close to ['Descender'] - //['FontBBox'][3] should be close to ['Ascender']+Accents - //in practice, FontBBox values are a little bigger - // - //The text position is referenced to the baseline, not to the lower corner of the FontBBox, - //for what the left,top corner is given. - //FontBBox spans also the background box for the text. - //If the lower corner would be used as reference point, the Descents of the glyphs would - //hang over the background box border. - //Therefore compensate only the extent above the Baseline. - // - //print '
['.$font.','.$size.','.$pdf->getFontHeight($size).','.$pdf->getFontDescender($size).','.$pdf->fonts[$pdf->currentFont]['FontBBox'][3].','.$pdf->fonts[$pdf->currentFont]['FontBBox'][1].','.$pdf->fonts[$pdf->currentFont]['FontHeightOffset'].','.$pdf->fonts[$pdf->currentFont]['Ascender'].','.$pdf->fonts[$pdf->currentFont]['Descender'].']
'; - // - //$pdf->addText($x, $this->y($y) - ($pdf->fonts[$pdf->currentFont]['FontBBox'][3]*$size)/1000, $size, $text, $angle, $word_space, $char_space); - $pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space); - } - - //........................................................................ - - function javascript($code) { - $this->_pdf->addJavascript($code); - } - - //........................................................................ - - /** - * Add a named destination (similar to ... in html) - * - * @param string $anchorname The name of the named destination - */ - function add_named_dest($anchorname) { - $this->_pdf->addDestination($anchorname, "Fit"); - } - - //........................................................................ - - /** - * Add a link to the pdf - * - * @param string $url The url to link to - * @param float $x The x position of the link - * @param float $y The y position of the link - * @param float $width The width of the link - * @param float $height The height of the link - */ - function add_link($url, $x, $y, $width, $height) { - - $y = $this->y($y) - $height; - - if ( strpos($url, '#') === 0 ) { - // Local link - $name = substr($url,1); - if ( $name ) { - $this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height); - } - - } - else { - $this->_pdf->addLink(rawurldecode($url), $x, $y, $x + $width, $y + $height); - } - } - - function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0) { - $this->_pdf->selectFont($font); - - $unicode = $this->_dompdf->get_option("enable_unicode"); - if (!$unicode) { - $text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8'); - } - - return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing); - } - - function register_string_subset($font, $string) { - $this->_pdf->registerText($font, $string); - } - - function get_font_height($font, $size) { - $this->_pdf->selectFont($font); - - $ratio = $this->_dompdf->get_option("font_height_ratio"); - return $this->_pdf->getFontHeight($size) * $ratio; - } - - /*function get_font_x_height($font, $size) { - $this->_pdf->selectFont($font); - $ratio = $this->_dompdf->get_option("font_height_ratio"); - return $this->_pdf->getFontXHeight($size) * $ratio; - }*/ - - function get_font_baseline($font, $size) { - $ratio = $this->_dompdf->get_option("font_height_ratio"); - return $this->get_font_height($font, $size) / $ratio; - } - - /** - * Writes text at the specified x and y coordinates on every page - * - * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced - * with their current values. - * - * See {@link Style::munge_color()} for the format of the colour array. - * - * @param float $x - * @param float $y - * @param string $text the text to write - * @param string $font the font file to use - * @param float $size the font size, in points - * @param array $color - * @param float $word_space word spacing adjustment - * @param float $char_space char spacing adjustment - * @param float $angle angle to write the text at, measured CW starting from the x-axis - */ - function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) { - $_t = "text"; - $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle"); - } - - /** - * Processes a script on every page - * - * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available. - * - * This function can be used to add page numbers to all pages - * after the first one, for example. - * - * @param string $code the script code - * @param string $type the language type for script - */ - function page_script($code, $type = "text/php") { - $_t = "script"; - $this->_page_text[] = compact("_t", "code", "type"); - } - - function new_page() { - $this->_page_number++; - $this->_page_count++; - - $ret = $this->_pdf->newPage(); - $this->_pages[] = $ret; - return $ret; - } - - /** - * Add text to each page after rendering is complete - */ - protected function _add_page_text() { - - if ( !count($this->_page_text) ) { - return; - } - - $page_number = 1; - $eval = null; - - foreach ($this->_pages as $pid) { - $this->reopen_object($pid); - - foreach ($this->_page_text as $pt) { - extract($pt); - - switch ($_t) { - case "text": - $text = str_replace(array("{PAGE_NUM}","{PAGE_COUNT}"), - array($page_number, $this->_page_count), $text); - $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle); - break; - - case "script": - if ( !$eval ) { - $eval = new PHP_Evaluator($this); - } - $eval->evaluate($code, array('PAGE_NUM' => $page_number, 'PAGE_COUNT' => $this->_page_count)); - break; - } - } - - $this->close_object(); - $page_number++; - } - } - - /** - * Streams the PDF directly to the browser - * - * @param string $filename the name of the PDF file - * @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0 - */ - function stream($filename, $options = null) { - // Add page text - $this->_add_page_text(); - - $options["Content-Disposition"] = $filename; - $this->_pdf->stream($options); - } - - /** - * Returns the PDF as a string - * - * @param array $options Output options - * @return string - */ - function output($options = null) { - $this->_add_page_text(); - - $debug = isset($options["compress"]) && $options["compress"] != 1; - - return $this->_pdf->output($debug); - } - - /** - * Returns logging messages generated by the Cpdf class - * - * @return string - */ - function get_messages() { - return $this->_pdf->messages; - } -} diff --git a/library/vendor/dompdf/include/css_color.cls.php b/library/vendor/dompdf/include/css_color.cls.php deleted file mode 100644 index 481751db5..000000000 --- a/library/vendor/dompdf/include/css_color.cls.php +++ /dev/null @@ -1,287 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -class CSS_Color { - static $cssColorNames = array( - "aliceblue" => "F0F8FF", - "antiquewhite" => "FAEBD7", - "aqua" => "00FFFF", - "aquamarine" => "7FFFD4", - "azure" => "F0FFFF", - "beige" => "F5F5DC", - "bisque" => "FFE4C4", - "black" => "000000", - "blanchedalmond" => "FFEBCD", - "blue" => "0000FF", - "blueviolet" => "8A2BE2", - "brown" => "A52A2A", - "burlywood" => "DEB887", - "cadetblue" => "5F9EA0", - "chartreuse" => "7FFF00", - "chocolate" => "D2691E", - "coral" => "FF7F50", - "cornflowerblue" => "6495ED", - "cornsilk" => "FFF8DC", - "crimson" => "DC143C", - "cyan" => "00FFFF", - "darkblue" => "00008B", - "darkcyan" => "008B8B", - "darkgoldenrod" => "B8860B", - "darkgray" => "A9A9A9", - "darkgreen" => "006400", - "darkgrey" => "A9A9A9", - "darkkhaki" => "BDB76B", - "darkmagenta" => "8B008B", - "darkolivegreen" => "556B2F", - "darkorange" => "FF8C00", - "darkorchid" => "9932CC", - "darkred" => "8B0000", - "darksalmon" => "E9967A", - "darkseagreen" => "8FBC8F", - "darkslateblue" => "483D8B", - "darkslategray" => "2F4F4F", - "darkslategrey" => "2F4F4F", - "darkturquoise" => "00CED1", - "darkviolet" => "9400D3", - "deeppink" => "FF1493", - "deepskyblue" => "00BFFF", - "dimgray" => "696969", - "dimgrey" => "696969", - "dodgerblue" => "1E90FF", - "firebrick" => "B22222", - "floralwhite" => "FFFAF0", - "forestgreen" => "228B22", - "fuchsia" => "FF00FF", - "gainsboro" => "DCDCDC", - "ghostwhite" => "F8F8FF", - "gold" => "FFD700", - "goldenrod" => "DAA520", - "gray" => "808080", - "green" => "008000", - "greenyellow" => "ADFF2F", - "grey" => "808080", - "honeydew" => "F0FFF0", - "hotpink" => "FF69B4", - "indianred" => "CD5C5C", - "indigo" => "4B0082", - "ivory" => "FFFFF0", - "khaki" => "F0E68C", - "lavender" => "E6E6FA", - "lavenderblush" => "FFF0F5", - "lawngreen" => "7CFC00", - "lemonchiffon" => "FFFACD", - "lightblue" => "ADD8E6", - "lightcoral" => "F08080", - "lightcyan" => "E0FFFF", - "lightgoldenrodyellow" => "FAFAD2", - "lightgray" => "D3D3D3", - "lightgreen" => "90EE90", - "lightgrey" => "D3D3D3", - "lightpink" => "FFB6C1", - "lightsalmon" => "FFA07A", - "lightseagreen" => "20B2AA", - "lightskyblue" => "87CEFA", - "lightslategray" => "778899", - "lightslategrey" => "778899", - "lightsteelblue" => "B0C4DE", - "lightyellow" => "FFFFE0", - "lime" => "00FF00", - "limegreen" => "32CD32", - "linen" => "FAF0E6", - "magenta" => "FF00FF", - "maroon" => "800000", - "mediumaquamarine" => "66CDAA", - "mediumblue" => "0000CD", - "mediumorchid" => "BA55D3", - "mediumpurple" => "9370DB", - "mediumseagreen" => "3CB371", - "mediumslateblue" => "7B68EE", - "mediumspringgreen" => "00FA9A", - "mediumturquoise" => "48D1CC", - "mediumvioletred" => "C71585", - "midnightblue" => "191970", - "mintcream" => "F5FFFA", - "mistyrose" => "FFE4E1", - "moccasin" => "FFE4B5", - "navajowhite" => "FFDEAD", - "navy" => "000080", - "oldlace" => "FDF5E6", - "olive" => "808000", - "olivedrab" => "6B8E23", - "orange" => "FFA500", - "orangered" => "FF4500", - "orchid" => "DA70D6", - "palegoldenrod" => "EEE8AA", - "palegreen" => "98FB98", - "paleturquoise" => "AFEEEE", - "palevioletred" => "DB7093", - "papayawhip" => "FFEFD5", - "peachpuff" => "FFDAB9", - "peru" => "CD853F", - "pink" => "FFC0CB", - "plum" => "DDA0DD", - "powderblue" => "B0E0E6", - "purple" => "800080", - "red" => "FF0000", - "rosybrown" => "BC8F8F", - "royalblue" => "4169E1", - "saddlebrown" => "8B4513", - "salmon" => "FA8072", - "sandybrown" => "F4A460", - "seagreen" => "2E8B57", - "seashell" => "FFF5EE", - "sienna" => "A0522D", - "silver" => "C0C0C0", - "skyblue" => "87CEEB", - "slateblue" => "6A5ACD", - "slategray" => "708090", - "slategrey" => "708090", - "snow" => "FFFAFA", - "springgreen" => "00FF7F", - "steelblue" => "4682B4", - "tan" => "D2B48C", - "teal" => "008080", - "thistle" => "D8BFD8", - "tomato" => "FF6347", - "turquoise" => "40E0D0", - "violet" => "EE82EE", - "wheat" => "F5DEB3", - "white" => "FFFFFF", - "whitesmoke" => "F5F5F5", - "yellow" => "FFFF00", - "yellowgreen" => "9ACD32", - ); - - static function parse($color) { - if ( is_array($color) ) { - // Assume the array has the right format... - // FIXME: should/could verify this. - return $color; - } - - static $cache = array(); - - $color = strtolower($color); - - if ( isset($cache[$color]) ) { - return $cache[$color]; - } - - if ( in_array($color, array("transparent", "inherit")) ) { - return $cache[$color] = $color; - } - - if ( isset(self::$cssColorNames[$color]) ) { - return $cache[$color] = self::getArray(self::$cssColorNames[$color]); - } - - $length = mb_strlen($color); - - // #rgb format - if ( $length == 4 && $color[0] === "#" ) { - return $cache[$color] = self::getArray($color[1].$color[1].$color[2].$color[2].$color[3].$color[3]); - } - - // #rrggbb format - else if ( $length == 7 && $color[0] === "#" ) { - return $cache[$color] = self::getArray(mb_substr($color, 1, 6)); - } - - // rgb( r,g,b ) / rgbaa( r,g,b,α ) format - else if ( mb_strpos($color, "rgb") !== false ) { - $i = mb_strpos($color, "("); - $j = mb_strpos($color, ")"); - - // Bad color value - if ( $i === false || $j === false ) { - return null; - } - - $triplet = explode(",", mb_substr($color, $i+1, $j-$i-1)); - - // alpha transparency - // FIXME: not currently using transparency - $alpha = 1; - if ( count( $triplet ) == 4 ) { - $alpha = (float) ( trim( array_pop( $triplet ) ) ); - // bad value, set to fully opaque - if ( $alpha > 1 || $alpha < 0 ) { - $alpha = 1; - } - } - - if ( count($triplet) != 3 ) { - return null; - } - - foreach (array_keys($triplet) as $c) { - $triplet[$c] = trim($triplet[$c]); - - if ( $triplet[$c][mb_strlen($triplet[$c]) - 1] === "%" ) { - $triplet[$c] = round($triplet[$c] * 2.55); - } - } - - return $cache[$color] = self::getArray(vsprintf("%02X%02X%02X", $triplet)); - - } - - // cmyk( c,m,y,k ) format - // http://www.w3.org/TR/css3-gcpm/#cmyk-colors - else if ( mb_strpos($color, "cmyk") !== false ) { - $i = mb_strpos($color, "("); - $j = mb_strpos($color, ")"); - - // Bad color value - if ( $i === false || $j === false ) { - return null; - } - - $values = explode(",", mb_substr($color, $i+1, $j-$i-1)); - - if ( count($values) != 4 ) { - return null; - } - - foreach ($values as &$c) { - $c = floatval(trim($c)); - if ($c > 1.0) $c = 1.0; - if ($c < 0.0) $c = 0.0; - } - - return $cache[$color] = self::getArray($values); - } - - return null; - } - - static function getArray($color) { - $c = array(null, null, null, null, "hex" => null); - - if (is_array($color)) { - $c = $color; - $c["c"] = $c[0]; - $c["m"] = $c[1]; - $c["y"] = $c[2]; - $c["k"] = $c[3]; - $c["hex"] = "cmyk($c[0],$c[1],$c[2],$c[3])"; - } - else { - $c[0] = hexdec(mb_substr($color, 0, 2)) / 0xff; - $c[1] = hexdec(mb_substr($color, 2, 2)) / 0xff; - $c[2] = hexdec(mb_substr($color, 4, 2)) / 0xff; - $c["r"] = $c[0]; - $c["g"] = $c[1]; - $c["b"] = $c[2]; - $c["hex"] = "#$color"; - } - - return $c; - } -} diff --git a/library/vendor/dompdf/include/dompdf.cls.php b/library/vendor/dompdf/include/dompdf.cls.php deleted file mode 100644 index b798d486e..000000000 --- a/library/vendor/dompdf/include/dompdf.cls.php +++ /dev/null @@ -1,1101 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * DOMPDF - PHP5 HTML to PDF renderer - * - * DOMPDF loads HTML and does its best to render it as a PDF. It gets its - * name from the new DomDocument PHP5 extension. Source HTML is first - * parsed by a DomDocument object. DOMPDF takes the resulting DOM tree and - * attaches a {@link Frame} object to each node. {@link Frame} objects store - * positioning and layout information and each has a reference to a {@link - * Style} object. - * - * Style information is loaded and parsed (see {@link Stylesheet}) and is - * applied to the frames in the tree by using XPath. CSS selectors are - * converted into XPath queries, and the computed {@link Style} objects are - * applied to the {@link Frame}s. - * - * {@link Frame}s are then decorated (in the design pattern sense of the - * word) based on their CSS display property ({@link - * http://www.w3.org/TR/CSS21/visuren.html#propdef-display}). - * Frame_Decorators augment the basic {@link Frame} class by adding - * additional properties and methods specific to the particular type of - * {@link Frame}. For example, in the CSS layout model, block frames - * (display: block;) contain line boxes that are usually filled with text or - * other inline frames. The Block_Frame_Decorator therefore adds a $lines - * property as well as methods to add {@link Frame}s to lines and to add - * additional lines. {@link Frame}s also are attached to specific - * Positioner and {@link Frame_Reflower} objects that contain the - * positioining and layout algorithm for a specific type of frame, - * respectively. This is an application of the Strategy pattern. - * - * Layout, or reflow, proceeds recursively (post-order) starting at the root - * of the document. Space constraints (containing block width & height) are - * pushed down, and resolved positions and sizes bubble up. Thus, every - * {@link Frame} in the document tree is traversed once (except for tables - * which use a two-pass layout algorithm). If you are interested in the - * details, see the reflow() method of the Reflower classes. - * - * Rendering is relatively straightforward once layout is complete. {@link - * Frame}s are rendered using an adapted {@link Cpdf} class, originally - * written by Wayne Munro, http://www.ros.co.nz/pdf/. (Some performance - * related changes have been made to the original {@link Cpdf} class, and - * the {@link CPDF_Adapter} class provides a simple, stateless interface to - * PDF generation.) PDFLib support has now also been added, via the {@link - * PDFLib_Adapter}. - * - * - * @package dompdf - */ -class DOMPDF { - - /** - * DomDocument representing the HTML document - * - * @var DOMDocument - */ - protected $_xml; - - /** - * Frame_Tree derived from the DOM tree - * - * @var Frame_Tree - */ - protected $_tree; - - /** - * Stylesheet for the document - * - * @var Stylesheet - */ - protected $_css; - - /** - * Actual PDF renderer - * - * @var Canvas - */ - protected $_pdf; - - /** - * Desired paper size ('letter', 'legal', 'A4', etc.) - * - * @var string - */ - protected $_paper_size; - - /** - * Paper orientation ('portrait' or 'landscape') - * - * @var string - */ - protected $_paper_orientation; - - /** - * Callbacks on new page and new element - * - * @var array - */ - protected $_callbacks; - - /** - * Experimental caching capability - * - * @var string - */ - private $_cache_id; - - /** - * Base hostname - * - * Used for relative paths/urls - * @var string - */ - protected $_base_host; - - /** - * Absolute base path - * - * Used for relative paths/urls - * @var string - */ - protected $_base_path; - - /** - * Protcol used to request file (file://, http://, etc) - * - * @var string - */ - protected $_protocol; - - /** - * HTTP context created with stream_context_create() - * Will be used for file_get_contents - * - * @var resource - */ - protected $_http_context; - - /** - * Timestamp of the script start time - * - * @var int - */ - private $_start_time = null; - - /** - * The system's locale - * - * @var string - */ - private $_system_locale = null; - - /** - * Tells if the system's locale is the C standard one - * - * @var bool - */ - private $_locale_standard = false; - - /** - * The default view of the PDF in the viewer - * - * @var string - */ - private $_default_view = "Fit"; - - /** - * The default view options of the PDF in the viewer - * - * @var array - */ - private $_default_view_options = array(); - - /** - * Tells wether the DOM document is in quirksmode (experimental) - * - * @var bool - */ - private $_quirksmode = false; - - /** - * Protocol whitelist - * - * Protocols and PHP wrappers allowed in URLs. Full support is not - * guarantee for the protocols/wrappers contained in this array. - * - * @var array - */ - private $_allowed_protocols = array(null, "", "file://", "http://", "https://"); - - /** - * Local file extension whitelist - * - * File extensions supported by dompdf for local files. - * - * @var array - */ - private $_allowed_local_file_extensions = array("htm", "html"); - - /** - * The list of built-in fonts - * - * @var array - */ - public static $native_fonts = array( - "courier", "courier-bold", "courier-oblique", "courier-boldoblique", - "helvetica", "helvetica-bold", "helvetica-oblique", "helvetica-boldoblique", - "times-roman", "times-bold", "times-italic", "times-bolditalic", - "symbol", "zapfdinbats" - ); - - private $_options = array( - // Directories - "temp_dir" => DOMPDF_TEMP_DIR, - "font_dir" => DOMPDF_FONT_DIR, - "font_cache" => DOMPDF_FONT_CACHE, - "chroot" => DOMPDF_CHROOT, - "log_output_file" => DOMPDF_LOG_OUTPUT_FILE, - - // Rendering - "default_media_type" => DOMPDF_DEFAULT_MEDIA_TYPE, - "default_paper_size" => DOMPDF_DEFAULT_PAPER_SIZE, - "default_font" => DOMPDF_DEFAULT_FONT, - "dpi" => DOMPDF_DPI, - "font_height_ratio" => DOMPDF_FONT_HEIGHT_RATIO, - - // Features - "enable_unicode" => DOMPDF_UNICODE_ENABLED, - "enable_php" => DOMPDF_ENABLE_PHP, - "enable_remote" => DOMPDF_ENABLE_REMOTE, - "enable_css_float" => DOMPDF_ENABLE_CSS_FLOAT, - "enable_javascript" => DOMPDF_ENABLE_JAVASCRIPT, - "enable_html5_parser" => DOMPDF_ENABLE_HTML5PARSER, - "enable_font_subsetting" => DOMPDF_ENABLE_FONTSUBSETTING, - - // Debug - "debug_png" => DEBUGPNG, - "debug_keep_temp" => DEBUGKEEPTEMP, - "debug_css" => DEBUGCSS, - "debug_layout" => DEBUG_LAYOUT, - "debug_layout_lines" => DEBUG_LAYOUT_LINES, - "debug_layout_blocks" => DEBUG_LAYOUT_BLOCKS, - "debug_layout_inline" => DEBUG_LAYOUT_INLINE, - "debug_layout_padding_box" => DEBUG_LAYOUT_PADDINGBOX, - - // Admin - "admin_username" => DOMPDF_ADMIN_USERNAME, - "admin_password" => DOMPDF_ADMIN_PASSWORD, - ); - - /** - * Class constructor - */ - function __construct() { - $this->_locale_standard = sprintf('%.1f', 1.0) == '1.0'; - - $this->save_locale(); - - $this->_messages = array(); - $this->_css = new Stylesheet($this); - $this->_pdf = null; - $this->_paper_size = DOMPDF_DEFAULT_PAPER_SIZE; - $this->_paper_orientation = "portrait"; - $this->_base_protocol = ""; - $this->_base_host = ""; - $this->_base_path = ""; - $this->_http_context = null; - $this->_callbacks = array(); - $this->_cache_id = null; - - $this->restore_locale(); - } - - /** - * Class destructor - */ - function __destruct() { - clear_object($this); - } - - /** - * Get the dompdf option value - * - * @param string $key - * - * @return mixed - * @throws DOMPDF_Exception - */ - function get_option($key) { - if ( !array_key_exists($key, $this->_options) ) { - throw new DOMPDF_Exception("Option '$key' doesn't exist"); - } - - return $this->_options[$key]; - } - - /** - * @param string $key - * @param mixed $value - * - * @throws DOMPDF_Exception - */ - function set_option($key, $value) { - if ( !array_key_exists($key, $this->_options) ) { - throw new DOMPDF_Exception("Option '$key' doesn't exist"); - } - - $this->_options[$key] = $value; - } - - /** - * @param array $options - */ - function set_options(array $options) { - foreach ($options as $key => $value) { - $this->set_option($key, $value); - } - } - - /** - * Save the system's locale configuration and - * set the right value for numeric formatting - */ - private function save_locale() { - if ( $this->_locale_standard ) { - return; - } - - $this->_system_locale = setlocale(LC_NUMERIC, "0"); - setlocale(LC_NUMERIC, "C"); - } - - /** - * Restore the system's locale configuration - */ - private function restore_locale() { - if ( $this->_locale_standard ) { - return; - } - - setlocale(LC_NUMERIC, $this->_system_locale); - } - - /** - * Returns the underlying {@link Frame_Tree} object - * - * @return Frame_Tree - */ - function get_tree() { - return $this->_tree; - } - - /** - * Sets the protocol to use - * FIXME validate these - * - * @param string $proto - */ - function set_protocol($proto) { - $this->_protocol = $proto; - } - - /** - * Sets the base hostname - * - * @param string $host - */ - function set_host($host) { - $this->_base_host = $host; - } - - /** - * Sets the base path - * - * @param string $path - */ - function set_base_path($path) { - $this->_base_path = $path; - } - - /** - * Sets the HTTP context - * - * @param resource $http_context - */ - function set_http_context($http_context) { - $this->_http_context = $http_context; - } - - /** - * Sets the default view - * - * @param string $default_view The default document view - * @param array $options The view's options - */ - function set_default_view($default_view, $options) { - $this->_default_view = $default_view; - $this->_default_view_options = $options; - } - - /** - * Returns the protocol in use - * - * @return string - */ - function get_protocol() { - return $this->_protocol; - } - - /** - * Returns the base hostname - * - * @return string - */ - function get_host() { - return $this->_base_host; - } - - /** - * Returns the base path - * - * @return string - */ - function get_base_path() { - return $this->_base_path; - } - - /** - * Returns the HTTP context - * - * @return resource - */ - function get_http_context() { - return $this->_http_context; - } - - /** - * Return the underlying Canvas instance (e.g. CPDF_Adapter, GD_Adapter) - * - * @return Canvas - */ - function get_canvas() { - return $this->_pdf; - } - - /** - * Returns the callbacks array - * - * @return array - */ - function get_callbacks() { - return $this->_callbacks; - } - - /** - * Returns the stylesheet - * - * @return Stylesheet - */ - function get_css() { - return $this->_css; - } - - /** - * @return DOMDocument - */ - function get_dom() { - return $this->_xml; - } - - /** - * Loads an HTML file - * Parse errors are stored in the global array _dompdf_warnings. - * - * @param string $file a filename or url to load - * - * @throws DOMPDF_Exception - */ - function load_html_file($file) { - $this->save_locale(); - - // Store parsing warnings as messages (this is to prevent output to the - // browser if the html is ugly and the dom extension complains, - // preventing the pdf from being streamed.) - if ( !$this->_protocol && !$this->_base_host && !$this->_base_path ) { - list($this->_protocol, $this->_base_host, $this->_base_path) = explode_url($file); - } - - if ( !in_array($this->_protocol, $this->_allowed_protocols) ) { - throw new DOMPDF_Exception("Permission denied on $file. The communication protocol is not supported."); - } - - if ( !$this->get_option("enable_remote") && ($this->_protocol != "" && $this->_protocol !== "file://" ) ) { - throw new DOMPDF_Exception("Remote file requested, but DOMPDF_ENABLE_REMOTE is false."); - } - - if ($this->_protocol == "" || $this->_protocol === "file://") { - - // Get the full path to $file, returns false if the file doesn't exist - $realfile = realpath($file); - - $chroot = $this->get_option("chroot"); - if ( strpos($realfile, $chroot) !== 0 ) { - throw new DOMPDF_Exception("Permission denied on $file. The file could not be found under the directory specified by DOMPDF_CHROOT."); - } - - $ext = pathinfo($realfile, PATHINFO_EXTENSION); - if (!in_array($ext, $this->_allowed_local_file_extensions)) { - throw new DOMPDF_Exception("Permission denied on $file."); - } - - if ( !$realfile ) { - throw new DOMPDF_Exception("File '$file' not found."); - } - - $file = $realfile; - } - - $contents = file_get_contents($file, null, $this->_http_context); - $encoding = null; - - // See http://the-stickman.com/web-development/php/getting-http-response-headers-when-using-file_get_contents/ - if ( isset($http_response_header) ) { - foreach($http_response_header as $_header) { - if ( preg_match("@Content-Type:\s*[\w/]+;\s*?charset=([^\s]+)@i", $_header, $matches) ) { - $encoding = strtoupper($matches[1]); - break; - } - } - } - - $this->restore_locale(); - - $this->load_html($contents, $encoding); - } - - /** - * Loads an HTML string - * Parse errors are stored in the global array _dompdf_warnings. - * @todo use the $encoding variable - * - * @param string $str HTML text to load - * @param string $encoding Not used yet - */ - function load_html($str, $encoding = null) { - $this->save_locale(); - - // FIXME: Determine character encoding, switch to UTF8, update meta tag. Need better http/file stream encoding detection, currently relies on text or meta tag. - mb_detect_order('auto'); - - if (mb_detect_encoding($str) !== 'UTF-8') { - $metatags = array( - '@]*charset\s*=\s*["\']?\s*([^"\' ]+)@i', - ); - - foreach($metatags as $metatag) { - if (preg_match($metatag, $str, $matches)) break; - } - - if (mb_detect_encoding($str) == '') { - if (isset($matches[1])) { - $encoding = strtoupper($matches[1]); - } - else { - $encoding = 'UTF-8'; - } - } - else { - if ( isset($matches[1]) ) { - $encoding = strtoupper($matches[1]); - } - else { - $encoding = 'auto'; - } - } - - if ( $encoding !== 'UTF-8' ) { - $str = mb_convert_encoding($str, 'UTF-8', $encoding); - } - - if ( isset($matches[1]) ) { - $str = preg_replace('/charset=([^\s"]+)/i', 'charset=UTF-8', $str); - } - else { - $str = str_replace('', '', $str); - } - } - else { - $encoding = 'UTF-8'; - } - - // remove BOM mark from UTF-8, it's treated as document text by DOMDocument - // FIXME: roll this into the encoding detection using UTF-8/16/32 BOM (http://us2.php.net/manual/en/function.mb-detect-encoding.php#91051)? - if ( substr($str, 0, 3) == chr(0xEF).chr(0xBB).chr(0xBF) ) { - $str = substr($str, 3); - } - - // if the document contains non utf-8 with a utf-8 meta tag chars and was - // detected as utf-8 by mbstring, problems could happen. - // http://devzone.zend.com/article/8855 - if ( $encoding !== 'UTF-8' ) { - $re = '/]*)((?:charset=[^"\' ]+)([^>]*)|(?:charset=["\'][^"\' ]+["\']))([^>]*)>/i'; - $str = preg_replace($re, '', $str); - } - - // Store parsing warnings as messages - set_error_handler("record_warnings"); - - // @todo Take the quirksmode into account - // http://hsivonen.iki.fi/doctype/ - // https://developer.mozilla.org/en/mozilla's_quirks_mode - $quirksmode = false; - - if ( $this->get_option("enable_html5_parser") ) { - $tokenizer = new HTML5_Tokenizer($str); - $tokenizer->parse(); - $doc = $tokenizer->save(); - - // Remove #text children nodes in nodes that shouldn't have - $tag_names = array("html", "table", "tbody", "thead", "tfoot", "tr"); - foreach($tag_names as $tag_name) { - $nodes = $doc->getElementsByTagName($tag_name); - - foreach($nodes as $node) { - self::remove_text_nodes($node); - } - } - - $quirksmode = ($tokenizer->getTree()->getQuirksMode() > HTML5_TreeBuilder::NO_QUIRKS); - } - else { - // loadHTML assumes ISO-8859-1 unless otherwise specified, but there are - // bugs in how DOMDocument determines the actual encoding. Converting to - // HTML-ENTITIES prior to import appears to resolve the issue. - // http://devzone.zend.com/1538/php-dom-xml-extension-encoding-processing/ (see #4) - // http://stackoverflow.com/a/11310258/264628 - $doc = new DOMDocument(); - $doc->preserveWhiteSpace = true; - $doc->loadHTML( mb_convert_encoding( $str , 'HTML-ENTITIES' , 'UTF-8' ) ); - - // If some text is before the doctype, we are in quirksmode - if ( preg_match("/^(.+) - if ( !$doc->doctype->publicId && !$doc->doctype->systemId ) { - $quirksmode = false; - } - - // not XHTML - if ( !preg_match("/xhtml/i", $doc->doctype->publicId) ) { - $quirksmode = true; - } - } - } - - $this->_xml = $doc; - $this->_quirksmode = $quirksmode; - - $this->_tree = new Frame_Tree($this->_xml); - - restore_error_handler(); - - $this->restore_locale(); - } - - static function remove_text_nodes(DOMNode $node) { - $children = array(); - for ($i = 0; $i < $node->childNodes->length; $i++) { - $child = $node->childNodes->item($i); - if ( $child->nodeName === "#text" ) { - $children[] = $child; - } - } - - foreach($children as $child) { - $node->removeChild($child); - } - } - - /** - * Builds the {@link Frame_Tree}, loads any CSS and applies the styles to - * the {@link Frame_Tree} - */ - protected function _process_html() { - $this->_tree->build_tree(); - - $this->_css->load_css_file(Stylesheet::DEFAULT_STYLESHEET, Stylesheet::ORIG_UA); - - $acceptedmedia = Stylesheet::$ACCEPTED_GENERIC_MEDIA_TYPES; - $acceptedmedia[] = $this->get_option("default_media_type"); - - // - $base_nodes = $this->_xml->getElementsByTagName("base"); - if ( $base_nodes->length && ($href = $base_nodes->item(0)->getAttribute("href")) ) { - list($this->_protocol, $this->_base_host, $this->_base_path) = explode_url($href); - } - - // Set the base path of the Stylesheet to that of the file being processed - $this->_css->set_protocol($this->_protocol); - $this->_css->set_host($this->_base_host); - $this->_css->set_base_path($this->_base_path); - - // Get all the stylesheets so that they are processed in document order - $xpath = new DOMXPath($this->_xml); - $stylesheets = $xpath->query("//*[name() = 'link' or name() = 'style']"); - - foreach($stylesheets as $tag) { - switch (strtolower($tag->nodeName)) { - // load tags - case "link": - if ( mb_strtolower(stripos($tag->getAttribute("rel"), "stylesheet") !== false) || // may be "appendix stylesheet" - mb_strtolower($tag->getAttribute("type")) === "text/css" ) { - //Check if the css file is for an accepted media type - //media not given then always valid - $formedialist = preg_split("/[\s\n,]/", $tag->getAttribute("media"),-1, PREG_SPLIT_NO_EMPTY); - if ( count($formedialist) > 0 ) { - $accept = false; - foreach ( $formedialist as $type ) { - if ( in_array(mb_strtolower(trim($type)), $acceptedmedia) ) { - $accept = true; - break; - } - } - - if (!$accept) { - //found at least one mediatype, but none of the accepted ones - //Skip this css file. - continue; - } - } - - $url = $tag->getAttribute("href"); - $url = build_url($this->_protocol, $this->_base_host, $this->_base_path, $url); - - $this->_css->load_css_file($url, Stylesheet::ORIG_AUTHOR); - } - break; - - // load - $child = $child->nextSibling; - } - } - else { - $css = $tag->nodeValue; - } - - $this->_css->load_css($css); - break; - } - } - } - - /** - * Sets the paper size & orientation - * - * @param string $size 'letter', 'legal', 'A4', etc. {@link CPDF_Adapter::$PAPER_SIZES} - * @param string $orientation 'portrait' or 'landscape' - */ - function set_paper($size, $orientation = "portrait") { - $this->_paper_size = $size; - $this->_paper_orientation = $orientation; - } - - /** - * Enable experimental caching capability - * @access private - */ - function enable_caching($cache_id) { - $this->_cache_id = $cache_id; - } - - /** - * Sets callbacks for events like rendering of pages and elements. - * The callbacks array contains arrays with 'event' set to 'begin_page', - * 'end_page', 'begin_frame', or 'end_frame' and 'f' set to a function or - * object plus method to be called. - * - * The function 'f' must take an array as argument, which contains info - * about the event. - * - * @param array $callbacks the set of callbacks to set - */ - function set_callbacks($callbacks) { - if (is_array($callbacks)) { - $this->_callbacks = array(); - foreach ($callbacks as $c) { - if (is_array($c) && isset($c['event']) && isset($c['f'])) { - $event = $c['event']; - $f = $c['f']; - if (is_callable($f) && is_string($event)) { - $this->_callbacks[$event][] = $f; - } - } - } - } - } - - /** - * Get the quirks mode - * - * @return boolean true if quirks mode is active - */ - function get_quirksmode(){ - return $this->_quirksmode; - } - - function parse_default_view($value) { - $valid = array("XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV"); - - $options = preg_split("/\s*,\s*/", trim($value)); - $default_view = array_shift($options); - - if ( !in_array($default_view, $valid) ) { - return false; - } - - $this->set_default_view($default_view, $options); - return true; - } - - /** - * Renders the HTML to PDF - */ - function render() { - $this->save_locale(); - - $log_output_file = $this->get_option("log_output_file"); - if ( $log_output_file ) { - if ( !file_exists($log_output_file) && is_writable(dirname($log_output_file)) ) { - touch($log_output_file); - } - - $this->_start_time = microtime(true); - ob_start(); - } - - //enable_mem_profile(); - - $this->_process_html(); - - $this->_css->apply_styles($this->_tree); - - // @page style rules : size, margins - $page_styles = $this->_css->get_page_styles(); - - $base_page_style = $page_styles["base"]; - unset($page_styles["base"]); - - foreach($page_styles as $_page_style) { - $_page_style->inherit($base_page_style); - } - - if ( is_array($base_page_style->size) ) { - $this->set_paper(array(0, 0, $base_page_style->size[0], $base_page_style->size[1])); - } - - $this->_pdf = Canvas_Factory::get_instance($this, $this->_paper_size, $this->_paper_orientation); - Font_Metrics::init($this->_pdf); - - if ( $this->get_option("enable_font_subsetting") && $this->_pdf instanceof CPDF_Adapter ) { - foreach ($this->_tree->get_frames() as $frame) { - $style = $frame->get_style(); - $node = $frame->get_node(); - - // Handle text nodes - if ( $node->nodeName === "#text" ) { - $this->_pdf->register_string_subset($style->font_family, $node->nodeValue); - continue; - } - - // Handle generated content (list items) - if ( $style->display === "list-item" ) { - $chars = List_Bullet_Renderer::get_counter_chars($style->list_style_type); - $this->_pdf->register_string_subset($style->font_family, $chars); - continue; - } - - // Handle other generated content (pseudo elements) - // FIXME: This only captures the text of the stylesheet declaration, - // not the actual generated content, and forces all possible counter - // values. See notes in issue #750. - if ( $frame->get_node()->nodeName == "dompdf_generated" ) { - // all possible counter values - $chars = List_Bullet_Renderer::get_counter_chars('decimal'); - $this->_pdf->register_string_subset($style->font_family, $chars); - $chars = List_Bullet_Renderer::get_counter_chars('upper-alpha'); - $this->_pdf->register_string_subset($style->font_family, $chars); - $chars = List_Bullet_Renderer::get_counter_chars('lower-alpha'); - $this->_pdf->register_string_subset($style->font_family, $chars); - $chars = List_Bullet_Renderer::get_counter_chars('lower-greek'); - $this->_pdf->register_string_subset($style->font_family, $chars); - // the text of the stylesheet declaration - $this->_pdf->register_string_subset($style->font_family, $style->content); - continue; - } - } - } - - $root = null; - - foreach ($this->_tree->get_frames() as $frame) { - // Set up the root frame - if ( is_null($root) ) { - $root = Frame_Factory::decorate_root( $this->_tree->get_root(), $this ); - continue; - } - - // Create the appropriate decorators, reflowers & positioners. - Frame_Factory::decorate_frame($frame, $this, $root); - } - - // Add meta information - $title = $this->_xml->getElementsByTagName("title"); - if ( $title->length ) { - $this->_pdf->add_info("Title", trim($title->item(0)->nodeValue)); - } - - $metas = $this->_xml->getElementsByTagName("meta"); - $labels = array( - "author" => "Author", - "keywords" => "Keywords", - "description" => "Subject", - ); - foreach($metas as $meta) { - $name = mb_strtolower($meta->getAttribute("name")); - $value = trim($meta->getAttribute("content")); - - if ( isset($labels[$name]) ) { - $this->_pdf->add_info($labels[$name], $value); - continue; - } - - if ( $name === "dompdf.view" && $this->parse_default_view($value) ) { - $this->_pdf->set_default_view($this->_default_view, $this->_default_view_options); - } - } - - $root->set_containing_block(0, 0, $this->_pdf->get_width(), $this->_pdf->get_height()); - $root->set_renderer(new Renderer($this)); - - // This is where the magic happens: - $root->reflow(); - - // Clean up cached images - Image_Cache::clear(); - - global $_dompdf_warnings, $_dompdf_show_warnings; - if ( $_dompdf_show_warnings ) { - echo 'DOMPDF Warnings
';
-      foreach ($_dompdf_warnings as $msg) {
-        echo $msg . "\n";
-      }
-      echo $this->get_canvas()->get_cpdf()->messages;
-      echo '
'; - flush(); - } - - $this->restore_locale(); - } - - /** - * Add meta information to the PDF after rendering - */ - function add_info($label, $value) { - if ( !is_null($this->_pdf) ) { - $this->_pdf->add_info($label, $value); - } - } - - /** - * Writes the output buffer in the log file - * - * @return void - */ - private function write_log() { - $log_output_file = $this->get_option("log_output_file"); - if ( !$log_output_file || !is_writable($log_output_file) ) { - return; - } - - $frames = Frame::$ID_COUNTER; - $memory = DOMPDF_memory_usage() / 1024; - $time = (microtime(true) - $this->_start_time) * 1000; - - $out = sprintf( - "%6d". - "%10.2f KB". - "%10.2f ms". - " ". - ($this->_quirksmode ? " ON" : "OFF"). - "
", $frames, $memory, $time); - - $out .= ob_get_clean(); - - $log_output_file = $this->get_option("log_output_file"); - file_put_contents($log_output_file, $out); - } - - /** - * Streams the PDF to the client - * - * The file will open a download dialog by default. The options - * parameter controls the output. Accepted options are: - * - * 'Accept-Ranges' => 1 or 0 - if this is not set to 1, then this - * header is not included, off by default this header seems to - * have caused some problems despite the fact that it is supposed - * to solve them, so I am leaving it off by default. - * - * 'compress' = > 1 or 0 - apply content stream compression, this is - * on (1) by default - * - * 'Attachment' => 1 or 0 - if 1, force the browser to open a - * download dialog, on (1) by default - * - * @param string $filename the name of the streamed file - * @param array $options header options (see above) - */ - function stream($filename, $options = null) { - $this->save_locale(); - - $this->write_log(); - - if ( !is_null($this->_pdf) ) { - $this->_pdf->stream($filename, $options); - } - - $this->restore_locale(); - } - - /** - * Returns the PDF as a string - * - * The file will open a download dialog by default. The options - * parameter controls the output. Accepted options are: - * - * - * 'compress' = > 1 or 0 - apply content stream compression, this is - * on (1) by default - * - * - * @param array $options options (see above) - * - * @return string - */ - function output($options = null) { - $this->save_locale(); - - $this->write_log(); - - if ( is_null($this->_pdf) ) { - return null; - } - - $output = $this->_pdf->output( $options ); - - $this->restore_locale(); - - return $output; - } - - /** - * Returns the underlying HTML document as a string - * - * @return string - */ - function output_html() { - return $this->_xml->saveHTML(); - } -} diff --git a/library/vendor/dompdf/include/dompdf_exception.cls.php b/library/vendor/dompdf/include/dompdf_exception.cls.php deleted file mode 100644 index ca47fa036..000000000 --- a/library/vendor/dompdf/include/dompdf_exception.cls.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Standard exception thrown by DOMPDF classes - * - * @package dompdf - */ -class DOMPDF_Exception extends Exception { - - /** - * Class constructor - * - * @param string $message Error message - * @param int $code Error code - */ - function __construct($message = null, $code = 0) { - parent::__construct($message, $code); - } - -} diff --git a/library/vendor/dompdf/include/dompdf_image_exception.cls.php b/library/vendor/dompdf/include/dompdf_image_exception.cls.php deleted file mode 100644 index 8fdecec52..000000000 --- a/library/vendor/dompdf/include/dompdf_image_exception.cls.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Image exception thrown by DOMPDF - * - * @package dompdf - */ -class DOMPDF_Image_Exception extends DOMPDF_Exception { - - /** - * Class constructor - * - * @param string $message Error message - * @param int $code Error code - */ - function __construct($message = null, $code = 0) { - parent::__construct($message, $code); - } - -} diff --git a/library/vendor/dompdf/include/fixed_positioner.cls.php b/library/vendor/dompdf/include/fixed_positioner.cls.php deleted file mode 100644 index 31a2a079c..000000000 --- a/library/vendor/dompdf/include/fixed_positioner.cls.php +++ /dev/null @@ -1,88 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Positions fixely positioned frames - */ -class Fixed_Positioner extends Positioner { - - function __construct(Frame_Decorator $frame) { parent::__construct($frame); } - - function position() { - - $frame = $this->_frame; - $style = $frame->get_original_style(); - $root = $frame->get_root(); - $initialcb = $root->get_containing_block(); - $initialcb_style = $root->get_style(); - - $p = $frame->find_block_parent(); - if ( $p ) { - $p->add_line(); - } - - // Compute the margins of the @page style - $margin_top = $initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]); - $margin_right = $initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]); - $margin_bottom = $initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]); - $margin_left = $initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]); - - // The needed computed style of the element - $height = $style->length_in_pt($style->height, $initialcb["h"]); - $width = $style->length_in_pt($style->width, $initialcb["w"]); - - $top = $style->length_in_pt($style->top, $initialcb["h"]); - $right = $style->length_in_pt($style->right, $initialcb["w"]); - $bottom = $style->length_in_pt($style->bottom, $initialcb["h"]); - $left = $style->length_in_pt($style->left, $initialcb["w"]); - - $y = $margin_top; - if ( isset($top) ) { - $y = $top + $margin_top; - if ( $top === "auto" ) { - $y = $margin_top; - if ( isset($bottom) && $bottom !== "auto" ) { - $y = $initialcb["h"] - $bottom - $margin_bottom; - $margin_height = $this->_frame->get_margin_height(); - if ( $margin_height !== "auto" ) { - $y -= $margin_height; - } - else { - $y -= $height; - } - } - } - } - - $x = $margin_left; - if ( isset($left) ) { - $x = $left + $margin_left; - if ( $left === "auto" ) { - $x = $margin_left; - if ( isset($right) && $right !== "auto" ) { - $x = $initialcb["w"] - $right - $margin_right; - $margin_width = $this->_frame->get_margin_width(); - if ( $margin_width !== "auto" ) { - $x -= $margin_width; - } - else { - $x -= $width; - } - } - } - } - - $frame->set_position($x, $y); - - $children = $frame->get_children(); - foreach($children as $child) { - $child->set_position($x, $y); - } - } -} \ No newline at end of file diff --git a/library/vendor/dompdf/include/font_metrics.cls.php b/library/vendor/dompdf/include/font_metrics.cls.php deleted file mode 100644 index 2d15d6714..000000000 --- a/library/vendor/dompdf/include/font_metrics.cls.php +++ /dev/null @@ -1,382 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -require_once DOMPDF_LIB_DIR . "/class.pdf.php"; - -/** - * Name of the font cache file - * - * This file must be writable by the webserver process only to update it - * with save_font_families() after adding the .afm file references of a new font family - * with Font_Metrics::save_font_families(). - * This is typically done only from command line with load_font.php on converting - * ttf fonts to ufm with php-font-lib. - * - * Declared here because PHP5 prevents constants from being declared with expressions - */ -define('__DOMPDF_FONT_CACHE_FILE', DOMPDF_FONT_DIR . "dompdf_font_family_cache.php"); - -/** - * The font metrics class - * - * This class provides information about fonts and text. It can resolve - * font names into actual installed font files, as well as determine the - * size of text in a particular font and size. - * - * @static - * @package dompdf - */ -class Font_Metrics { - - /** - * @see __DOMPDF_FONT_CACHE_FILE - */ - const CACHE_FILE = __DOMPDF_FONT_CACHE_FILE; - - /** - * Underlying {@link Canvas} object to perform text size calculations - * - * @var Canvas - */ - static protected $_pdf = null; - - /** - * Array of font family names to font files - * - * Usually cached by the {@link load_font.php} script - * - * @var array - */ - static protected $_font_lookup = array(); - - - /** - * Class initialization - * - */ - static function init(Canvas $canvas = null) { - if (!self::$_pdf) { - if (!$canvas) { - $canvas = Canvas_Factory::get_instance(new DOMPDF()); - } - - self::$_pdf = $canvas; - } - } - - /** - * Calculates text size, in points - * - * @param string $text the text to be sized - * @param string $font the desired font - * @param float $size the desired font size - * @param float $word_spacing - * @param float $char_spacing - * - * @internal param float $spacing word spacing, if any - * @return float - */ - static function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) { - //return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing); - - // @todo Make sure this cache is efficient before enabling it - static $cache = array(); - - if ( $text === "" ) { - return 0; - } - - // Don't cache long strings - $use_cache = !isset($text[50]); // Faster than strlen - - $key = "$font/$size/$word_spacing/$char_spacing"; - - if ( $use_cache && isset($cache[$key][$text]) ) { - return $cache[$key]["$text"]; - } - - $width = self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing); - - if ( $use_cache ) { - $cache[$key][$text] = $width; - } - - return $width; - } - - /** - * Calculates font height - * - * @param string $font - * @param float $size - * @return float - */ - static function get_font_height($font, $size) { - return self::$_pdf->get_font_height($font, $size); - } - - /** - * Resolves a font family & subtype into an actual font file - * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If - * the particular font family has no suitable font file, the default font - * ({@link DOMPDF_DEFAULT_FONT}) is used. The font file returned - * is the absolute pathname to the font file on the system. - * - * @param string $family_raw - * @param string $subtype_raw - * - * @return string - */ - static function get_font($family_raw, $subtype_raw = "normal") { - static $cache = array(); - - if ( isset($cache[$family_raw][$subtype_raw]) ) { - return $cache[$family_raw][$subtype_raw]; - } - - /* Allow calling for various fonts in search path. Therefore not immediately - * return replacement on non match. - * Only when called with NULL try replacement. - * When this is also missing there is really trouble. - * If only the subtype fails, nevertheless return failure. - * Only on checking the fallback font, check various subtypes on same font. - */ - - $subtype = strtolower($subtype_raw); - - if ( $family_raw ) { - $family = str_replace( array("'", '"'), "", strtolower($family_raw)); - - if ( isset(self::$_font_lookup[$family][$subtype]) ) { - return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype]; - } - - return null; - } - - $family = "serif"; - - if ( isset(self::$_font_lookup[$family][$subtype]) ) { - return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype]; - } - - if ( !isset(self::$_font_lookup[$family]) ) { - return null; - } - - $family = self::$_font_lookup[$family]; - - foreach ( $family as $sub => $font ) { - if (strpos($subtype, $sub) !== false) { - return $cache[$family_raw][$subtype_raw] = $font; - } - } - - if ($subtype !== "normal") { - foreach ( $family as $sub => $font ) { - if ($sub !== "normal") { - return $cache[$family_raw][$subtype_raw] = $font; - } - } - } - - $subtype = "normal"; - - if ( isset($family[$subtype]) ) { - return $cache[$family_raw][$subtype_raw] = $family[$subtype]; - } - - return null; - } - - static function get_family($family) { - $family = str_replace( array("'", '"'), "", mb_strtolower($family)); - - if ( isset(self::$_font_lookup[$family]) ) { - return self::$_font_lookup[$family]; - } - - return null; - } - - /** - * Saves the stored font family cache - * - * The name and location of the cache file are determined by {@link - * Font_Metrics::CACHE_FILE}. This file should be writable by the - * webserver process. - * - * @see Font_Metrics::load_font_families() - */ - static function save_font_families() { - // replace the path to the DOMPDF font directories with the corresponding constants (allows for more portability) - $cache_data = sprintf(" $variants) { - $cache_data .= sprintf(" '%s' => array(%s", addslashes($family), PHP_EOL); - foreach ($variants as $variant => $path) { - $path = sprintf("'%s'", $path); - $path = str_replace('\'' . DOMPDF_FONT_DIR , 'DOMPDF_FONT_DIR . \'' , $path); - $path = str_replace('\'' . DOMPDF_DIR , 'DOMPDF_DIR . \'' , $path); - $cache_data .= sprintf(" '%s' => %s,%s", $variant, $path, PHP_EOL); - } - $cache_data .= sprintf(" ),%s", PHP_EOL); - } - $cache_data .= ") ?>"; - file_put_contents(self::CACHE_FILE, $cache_data); - } - - /** - * Loads the stored font family cache - * - * @see save_font_families() - */ - static function load_font_families() { - $dist_fonts = require_once DOMPDF_DIR . "/lib/fonts/dompdf_font_family_cache.dist.php"; - - // FIXME: temporary step for font cache created before the font cache fix - if ( is_readable( DOMPDF_FONT_DIR . "dompdf_font_family_cache" ) ) { - $old_fonts = require_once DOMPDF_FONT_DIR . "dompdf_font_family_cache"; - // If the font family cache is still in the old format - if ( $old_fonts === 1 ) { - $cache_data = file_get_contents(DOMPDF_FONT_DIR . "dompdf_font_family_cache"); - file_put_contents(DOMPDF_FONT_DIR . "dompdf_font_family_cache", "<"."?php return $cache_data ?".">"); - $old_fonts = require_once DOMPDF_FONT_DIR . "dompdf_font_family_cache"; - } - $dist_fonts += $old_fonts; - } - - if ( !is_readable(self::CACHE_FILE) ) { - self::$_font_lookup = $dist_fonts; - return; - } - - $cache_data = require_once self::CACHE_FILE; - - // If the font family cache is still in the old format - if ( self::$_font_lookup === 1 ) { - $cache_data = file_get_contents(self::CACHE_FILE); - file_put_contents(self::CACHE_FILE, "<"."?php return $cache_data ?".">"); - $cache_data = require_once self::CACHE_FILE; - } - - self::$_font_lookup = array(); - foreach ($cache_data as $key => $value) { - self::$_font_lookup[stripslashes($key)] = $value; - } - - // Merge provided fonts - self::$_font_lookup += $dist_fonts; - } - - static function get_type($type) { - if (preg_match("/bold/i", $type)) { - if (preg_match("/italic|oblique/i", $type)) { - $type = "bold_italic"; - } - else { - $type = "bold"; - } - } - elseif (preg_match("/italic|oblique/i", $type)) { - $type = "italic"; - } - else { - $type = "normal"; - } - - return $type; - } - - static function install_fonts($files) { - $names = array(); - - foreach($files as $file) { - $font = Font::load($file); - $records = $font->getData("name", "records"); - $type = self::get_type($records[2]); - $names[mb_strtolower($records[1])][$type] = $file; - } - - return $names; - } - - static function get_system_fonts() { - $files = glob("/usr/share/fonts/truetype/*.ttf") + - glob("/usr/share/fonts/truetype/*/*.ttf") + - glob("/usr/share/fonts/truetype/*/*/*.ttf") + - glob("C:\\Windows\\fonts\\*.ttf") + - glob("C:\\WinNT\\fonts\\*.ttf") + - glob("/mnt/c_drive/WINDOWS/Fonts/"); - - return self::install_fonts($files); - } - - /** - * Returns the current font lookup table - * - * @return array - */ - static function get_font_families() { - return self::$_font_lookup; - } - - static function set_font_family($fontname, $entry) { - self::$_font_lookup[mb_strtolower($fontname)] = $entry; - } - - static function register_font($style, $remote_file, $context = null) { - $fontname = mb_strtolower($style["family"]); - $families = Font_Metrics::get_font_families(); - - $entry = array(); - if ( isset($families[$fontname]) ) { - $entry = $families[$fontname]; - } - - $local_file = DOMPDF_FONT_DIR . md5($remote_file); - $local_temp_file = DOMPDF_TEMP_DIR . "/" . md5($remote_file); - $cache_entry = $local_file; - $local_file .= ".ttf"; - - $style_string = Font_Metrics::get_type("{$style['weight']} {$style['style']}"); - - if ( !isset($entry[$style_string]) ) { - $entry[$style_string] = $cache_entry; - - // Download the remote file - file_put_contents($local_temp_file, file_get_contents($remote_file, null, $context)); - - $font = Font::load($local_temp_file); - - if (!$font) { - unlink($local_temp_file); - return false; - } - - $font->parse(); - $font->saveAdobeFontMetrics("$cache_entry.ufm"); - - unlink($local_temp_file); - - if ( !file_exists("$cache_entry.ufm") ) { - return false; - } - - // Save the changes - file_put_contents($local_file, file_get_contents($remote_file, null, $context)); - Font_Metrics::set_font_family($fontname, $entry); - Font_Metrics::save_font_families(); - } - - return true; - } -} - -Font_Metrics::load_font_families(); diff --git a/library/vendor/dompdf/include/frame.cls.php b/library/vendor/dompdf/include/frame.cls.php deleted file mode 100644 index bc2f26d40..000000000 --- a/library/vendor/dompdf/include/frame.cls.php +++ /dev/null @@ -1,1191 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * The main Frame class - * - * This class represents a single HTML element. This class stores - * positioning information as well as containing block location and - * dimensions. Style information for the element is stored in a {@link - * Style} object. Tree structure is maintained via the parent & children - * links. - * - * @access protected - * @package dompdf - */ -class Frame { - - /** - * The DOMElement or DOMText object this frame represents - * - * @var DOMElement|DOMText - */ - protected $_node; - - /** - * Unique identifier for this frame. Used to reference this frame - * via the node. - * - * @var string - */ - protected $_id; - - /** - * Unique id counter - */ - static /*protected*/ $ID_COUNTER = 0; - - /** - * This frame's calculated style - * - * @var Style - */ - protected $_style; - - /** - * This frame's original style. Needed for cases where frames are - * split across pages. - * - * @var Style - */ - protected $_original_style; - - /** - * This frame's parent in the document tree. - * - * @var Frame - */ - protected $_parent; - - /** - * This frame's children - * - * @var Frame[] - */ - protected $_frame_list; - - /** - * This frame's first child. All children are handled as a - * doubly-linked list. - * - * @var Frame - */ - protected $_first_child; - - /** - * This frame's last child. - * - * @var Frame - */ - protected $_last_child; - - /** - * This frame's previous sibling in the document tree. - * - * @var Frame - */ - protected $_prev_sibling; - - /** - * This frame's next sibling in the document tree. - * - * @var Frame - */ - protected $_next_sibling; - - /** - * This frame's containing block (used in layout): array(x, y, w, h) - * - * @var float[] - */ - protected $_containing_block; - - /** - * Position on the page of the top-left corner of the margin box of - * this frame: array(x,y) - * - * @var float[] - */ - protected $_position; - - /** - * Absolute opacity of this frame - * - * @var float - */ - protected $_opacity; - - /** - * This frame's decorator - * - * @var Frame_Decorator - */ - protected $_decorator; - - /** - * This frame's containing line box - * - * @var Line_Box - */ - protected $_containing_line; - - protected $_is_cache = array(); - - /** - * Tells wether the frame was already pushed to the next page - * - * @var bool - */ - public $_already_pushed = false; - - public $_float_next_line = false; - - /** - * Tells wether the frame was split - * - * @var bool - */ - public $_splitted; - - static $_ws_state = self::WS_SPACE; - - const WS_TEXT = 1; - const WS_SPACE = 2; - - /** - * Class destructor - */ - function __destruct() { - clear_object($this); - } - - /** - * Class constructor - * - * @param DOMNode $node the DOMNode this frame represents - */ - function __construct(DOMNode $node) { - $this->_node = $node; - - $this->_parent = null; - $this->_first_child = null; - $this->_last_child = null; - $this->_prev_sibling = $this->_next_sibling = null; - - $this->_style = null; - $this->_original_style = null; - - $this->_containing_block = array( - "x" => null, - "y" => null, - "w" => null, - "h" => null, - ); - - $this->_containing_block[0] =& $this->_containing_block["x"]; - $this->_containing_block[1] =& $this->_containing_block["y"]; - $this->_containing_block[2] =& $this->_containing_block["w"]; - $this->_containing_block[3] =& $this->_containing_block["h"]; - - $this->_position = array( - "x" => null, - "y" => null, - ); - - $this->_position[0] =& $this->_position["x"]; - $this->_position[1] =& $this->_position["y"]; - - $this->_opacity = 1.0; - $this->_decorator = null; - - $this->set_id( self::$ID_COUNTER++ ); - } - - // WIP : preprocessing to remove all the unused whitespace - protected function ws_trim(){ - if ( $this->ws_keep() ) { - return; - } - - switch(self::$_ws_state) { - case self::WS_SPACE: - $node = $this->_node; - - if ( $node->nodeName === "#text" ) { - $node->nodeValue = preg_replace("/[ \t\r\n\f]+/u", " ", $node->nodeValue); - - // starts with a whitespace - if ( isset($node->nodeValue[0]) && $node->nodeValue[0] === " " ) { - $node->nodeValue = ltrim($node->nodeValue); - } - - // if not empty - if ( $node->nodeValue !== "" ) { - // change the current state (text) - self::$_ws_state = self::WS_TEXT; - - // ends with a whitespace - if ( preg_match("/[ \t\r\n\f]+$/u", $node->nodeValue) ) { - $node->nodeValue = ltrim($node->nodeValue); - } - } - } - break; - - case self::WS_TEXT: - } - } - - protected function ws_keep(){ - $whitespace = $this->get_style()->white_space; - return in_array($whitespace, array("pre", "pre-wrap", "pre-line")); - } - - protected function ws_is_text(){ - $node = $this->get_node(); - - if ($node->nodeName === "img") { - return true; - } - - if ( !$this->is_in_flow() ) { - return false; - } - - if ($this->is_text_node()) { - return trim($node->nodeValue) !== ""; - } - - return true; - } - - /** - * "Destructor": forcibly free all references held by this frame - * - * @param bool $recursive if true, call dispose on all children - */ - function dispose($recursive = false) { - - if ( $recursive ) { - while ( $child = $this->_first_child ) { - $child->dispose(true); - } - } - - // Remove this frame from the tree - if ( $this->_prev_sibling ) { - $this->_prev_sibling->_next_sibling = $this->_next_sibling; - } - - if ( $this->_next_sibling ) { - $this->_next_sibling->_prev_sibling = $this->_prev_sibling; - } - - if ( $this->_parent && $this->_parent->_first_child === $this ) { - $this->_parent->_first_child = $this->_next_sibling; - } - - if ( $this->_parent && $this->_parent->_last_child === $this ) { - $this->_parent->_last_child = $this->_prev_sibling; - } - - if ( $this->_parent ) { - $this->_parent->get_node()->removeChild($this->_node); - } - - $this->_style->dispose(); - $this->_style = null; - unset($this->_style); - - $this->_original_style->dispose(); - $this->_original_style = null; - unset($this->_original_style); - - } - - // Re-initialize the frame - function reset() { - $this->_position["x"] = null; - $this->_position["y"] = null; - - $this->_containing_block["x"] = null; - $this->_containing_block["y"] = null; - $this->_containing_block["w"] = null; - $this->_containing_block["h"] = null; - - $this->_style = null; - unset($this->_style); - $this->_style = clone $this->_original_style; - } - - //........................................................................ - - /** - * @return DOMElement|DOMText - */ - function get_node() { - return $this->_node; - } - - /** - * @return string - */ - function get_id() { - return $this->_id; - } - - /** - * @return Style - */ - function get_style() { - return $this->_style; - } - - /** - * @return Style - */ - function get_original_style() { - return $this->_original_style; - } - - /** - * @return Frame - */ - function get_parent() { - return $this->_parent; - } - - /** - * @return Frame_Decorator - */ - function get_decorator() { - return $this->_decorator; - } - - /** - * @return Frame - */ - function get_first_child() { - return $this->_first_child; - } - - /** - * @return Frame - */ - function get_last_child() { - return $this->_last_child; - } - - /** - * @return Frame - */ - function get_prev_sibling() { - return $this->_prev_sibling; - } - - /** - * @return Frame - */ - function get_next_sibling() { - return $this->_next_sibling; - } - - /** - * @return FrameList|Frame[] - */ - function get_children() { - if ( isset($this->_frame_list) ) { - return $this->_frame_list; - } - - $this->_frame_list = new FrameList($this); - return $this->_frame_list; - } - - // Layout property accessors - - /** - * Containing block dimensions - * - * @param $i string The key of the wanted containing block's dimension (x, y, x, h) - * - * @return float[]|float - */ - function get_containing_block($i = null) { - if ( isset($i) ) { - return $this->_containing_block[$i]; - } - return $this->_containing_block; - } - - /** - * Block position - * - * @param $i string The key of the wanted position value (x, y) - * - * @return array|float - */ - function get_position($i = null) { - if ( isset($i) ) { - return $this->_position[$i]; - } - return $this->_position; - } - - //........................................................................ - - /** - * Return the height of the margin box of the frame, in pt. Meaningless - * unless the height has been calculated properly. - * - * @return float - */ - function get_margin_height() { - $style = $this->_style; - - return $style->length_in_pt(array( - $style->height, - $style->margin_top, - $style->margin_bottom, - $style->border_top_width, - $style->border_bottom_width, - $style->padding_top, - $style->padding_bottom - ), $this->_containing_block["h"]); - } - - /** - * Return the width of the margin box of the frame, in pt. Meaningless - * unless the width has been calculated properly. - * - * @return float - */ - function get_margin_width() { - $style = $this->_style; - - return $style->length_in_pt(array( - $style->width, - $style->margin_left, - $style->margin_right, - $style->border_left_width, - $style->border_right_width, - $style->padding_left, - $style->padding_right - ), $this->_containing_block["w"]); - } - - function get_break_margins(){ - $style = $this->_style; - - return $style->length_in_pt(array( - //$style->height, - $style->margin_top, - $style->margin_bottom, - $style->border_top_width, - $style->border_bottom_width, - $style->padding_top, - $style->padding_bottom - ), $this->_containing_block["h"]); - } - - /** - * Return the padding box (x,y,w,h) of the frame - * - * @return array - */ - function get_padding_box() { - $style = $this->_style; - $cb = $this->_containing_block; - - $x = $this->_position["x"] + - $style->length_in_pt(array($style->margin_left, - $style->border_left_width), - $cb["w"]); - - $y = $this->_position["y"] + - $style->length_in_pt(array($style->margin_top, - $style->border_top_width), - $cb["h"]); - - $w = $style->length_in_pt(array($style->padding_left, - $style->width, - $style->padding_right), - $cb["w"]); - - $h = $style->length_in_pt(array($style->padding_top, - $style->height, - $style->padding_bottom), - $cb["h"]); - - return array(0 => $x, "x" => $x, - 1 => $y, "y" => $y, - 2 => $w, "w" => $w, - 3 => $h, "h" => $h); - } - - /** - * Return the border box of the frame - * - * @return array - */ - function get_border_box() { - $style = $this->_style; - $cb = $this->_containing_block; - - $x = $this->_position["x"] + $style->length_in_pt($style->margin_left, $cb["w"]); - - $y = $this->_position["y"] + $style->length_in_pt($style->margin_top, $cb["h"]); - - $w = $style->length_in_pt(array($style->border_left_width, - $style->padding_left, - $style->width, - $style->padding_right, - $style->border_right_width), - $cb["w"]); - - $h = $style->length_in_pt(array($style->border_top_width, - $style->padding_top, - $style->height, - $style->padding_bottom, - $style->border_bottom_width), - $cb["h"]); - - return array(0 => $x, "x" => $x, - 1 => $y, "y" => $y, - 2 => $w, "w" => $w, - 3 => $h, "h" => $h); - } - - function get_opacity($opacity = null) { - if ( $opacity !== null ) { - $this->set_opacity($opacity); - } - return $this->_opacity; - } - - /** - * @return Line_Box - */ - function &get_containing_line() { - return $this->_containing_line; - } - - //........................................................................ - - // Set methods - function set_id($id) { - $this->_id = $id; - - // We can only set attributes of DOMElement objects (nodeType == 1). - // Since these are the only objects that we can assign CSS rules to, - // this shortcoming is okay. - if ( $this->_node->nodeType == XML_ELEMENT_NODE ) { - $this->_node->setAttribute("frame_id", $id); - } - } - - function set_style(Style $style) { - if ( is_null($this->_style) ) { - $this->_original_style = clone $style; - } - - //$style->set_frame($this); - $this->_style = $style; - } - - function set_decorator(Frame_Decorator $decorator) { - $this->_decorator = $decorator; - } - - function set_containing_block($x = null, $y = null, $w = null, $h = null) { - if ( is_array($x) ){ - foreach($x as $key => $val){ - $$key = $val; - } - } - - if (is_numeric($x)) { - $this->_containing_block["x"] = $x; - } - - if (is_numeric($y)) { - $this->_containing_block["y"] = $y; - } - - if (is_numeric($w)) { - $this->_containing_block["w"] = $w; - } - - if (is_numeric($h)) { - $this->_containing_block["h"] = $h; - } - } - - function set_position($x = null, $y = null) { - if ( is_array($x) ) { - list($x, $y) = array($x["x"], $x["y"]); - } - - if ( is_numeric($x) ) { - $this->_position["x"] = $x; - } - - if ( is_numeric($y) ) { - $this->_position["y"] = $y; - } - } - - function set_opacity($opacity) { - $parent = $this->get_parent(); - $base_opacity = (($parent && $parent->_opacity !== null) ? $parent->_opacity : 1.0); - $this->_opacity = $base_opacity * $opacity; - } - - function set_containing_line(Line_Box $line) { - $this->_containing_line = $line; - } - - //........................................................................ - - /** - * Tells if the frame is a text node - * @return bool - */ - function is_text_node() { - if ( isset($this->_is_cache["text_node"]) ) { - return $this->_is_cache["text_node"]; - } - - return $this->_is_cache["text_node"] = ($this->get_node()->nodeName === "#text"); - } - - function is_positionned() { - if ( isset($this->_is_cache["positionned"]) ) { - return $this->_is_cache["positionned"]; - } - - $position = $this->get_style()->position; - - return $this->_is_cache["positionned"] = in_array($position, Style::$POSITIONNED_TYPES); - } - - function is_absolute() { - if ( isset($this->_is_cache["absolute"]) ) { - return $this->_is_cache["absolute"]; - } - - $position = $this->get_style()->position; - - return $this->_is_cache["absolute"] = ($position === "absolute" || $position === "fixed"); - } - - function is_block() { - if ( isset($this->_is_cache["block"]) ) { - return $this->_is_cache["block"]; - } - - return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::$BLOCK_TYPES); - } - - function is_in_flow() { - if ( isset($this->_is_cache["in_flow"]) ) { - return $this->_is_cache["in_flow"]; - } - - $enable_css_float = $this->get_style()->get_stylesheet()->get_dompdf()->get_option("enable_css_float"); - return $this->_is_cache["in_flow"] = !($enable_css_float && $this->get_style()->float !== "none" || $this->is_absolute()); - } - - function is_pre(){ - if ( isset($this->_is_cache["pre"]) ) { - return $this->_is_cache["pre"]; - } - - $white_space = $this->get_style()->white_space; - - return $this->_is_cache["pre"] = in_array($white_space, array("pre", "pre-wrap")); - } - - function is_table(){ - if ( isset($this->_is_cache["table"]) ) { - return $this->_is_cache["table"]; - } - - $display = $this->get_style()->display; - - return $this->_is_cache["table"] = in_array($display, Style::$TABLE_TYPES); - } - - - /** - * Inserts a new child at the beginning of the Frame - * - * @param $child Frame The new Frame to insert - * @param $update_node boolean Whether or not to update the DOM - */ - function prepend_child(Frame $child, $update_node = true) { - if ( $update_node ) { - $this->_node->insertBefore($child->_node, $this->_first_child ? $this->_first_child->_node : null); - } - - // Remove the child from its parent - if ( $child->_parent ) { - $child->_parent->remove_child($child, false); - } - - $child->_parent = $this; - $child->_prev_sibling = null; - - // Handle the first child - if ( !$this->_first_child ) { - $this->_first_child = $child; - $this->_last_child = $child; - $child->_next_sibling = null; - } - else { - $this->_first_child->_prev_sibling = $child; - $child->_next_sibling = $this->_first_child; - $this->_first_child = $child; - } - } - - /** - * Inserts a new child at the end of the Frame - * - * @param $child Frame The new Frame to insert - * @param $update_node boolean Whether or not to update the DOM - */ - function append_child(Frame $child, $update_node = true) { - if ( $update_node ) { - $this->_node->appendChild($child->_node); - } - - // Remove the child from its parent - if ( $child->_parent ) { - $child->_parent->remove_child($child, false); - } - - $child->_parent = $this; - $child->_next_sibling = null; - - // Handle the first child - if ( !$this->_last_child ) { - $this->_first_child = $child; - $this->_last_child = $child; - $child->_prev_sibling = null; - } - else { - $this->_last_child->_next_sibling = $child; - $child->_prev_sibling = $this->_last_child; - $this->_last_child = $child; - } - } - - /** - * Inserts a new child immediately before the specified frame - * - * @param $new_child Frame The new Frame to insert - * @param $ref Frame The Frame after the new Frame - * @param $update_node boolean Whether or not to update the DOM - * - * @throws DOMPDF_Exception - */ - function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) { - if ( $ref === $this->_first_child ) { - $this->prepend_child($new_child, $update_node); - return; - } - - if ( is_null($ref) ) { - $this->append_child($new_child, $update_node); - return; - } - - if ( $ref->_parent !== $this ) { - throw new DOMPDF_Exception("Reference child is not a child of this node."); - } - - // Update the node - if ( $update_node ) { - $this->_node->insertBefore($new_child->_node, $ref->_node); - } - - // Remove the child from its parent - if ( $new_child->_parent ) { - $new_child->_parent->remove_child($new_child, false); - } - - $new_child->_parent = $this; - $new_child->_next_sibling = $ref; - $new_child->_prev_sibling = $ref->_prev_sibling; - - if ( $ref->_prev_sibling ) { - $ref->_prev_sibling->_next_sibling = $new_child; - } - - $ref->_prev_sibling = $new_child; - } - - /** - * Inserts a new child immediately after the specified frame - * - * @param $new_child Frame The new Frame to insert - * @param $ref Frame The Frame before the new Frame - * @param $update_node boolean Whether or not to update the DOM - * - * @throws DOMPDF_Exception - */ - function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) { - if ( $ref === $this->_last_child ) { - $this->append_child($new_child, $update_node); - return; - } - - if ( is_null($ref) ) { - $this->prepend_child($new_child, $update_node); - return; - } - - if ( $ref->_parent !== $this ) { - throw new DOMPDF_Exception("Reference child is not a child of this node."); - } - - // Update the node - if ( $update_node ) { - if ( $ref->_next_sibling ) { - $next_node = $ref->_next_sibling->_node; - $this->_node->insertBefore($new_child->_node, $next_node); - } - else { - $new_child->_node = $this->_node->appendChild($new_child->_node); - } - } - - // Remove the child from its parent - if ( $new_child->_parent ) { - $new_child->_parent->remove_child($new_child, false); - } - - $new_child->_parent = $this; - $new_child->_prev_sibling = $ref; - $new_child->_next_sibling = $ref->_next_sibling; - - if ( $ref->_next_sibling ) { - $ref->_next_sibling->_prev_sibling = $new_child; - } - - $ref->_next_sibling = $new_child; - } - - - /** - * Remove a child frame - * - * @param Frame $child - * @param boolean $update_node Whether or not to remove the DOM node - * - * @throws DOMPDF_Exception - * @return Frame The removed child frame - */ - function remove_child(Frame $child, $update_node = true) { - if ( $child->_parent !== $this ) { - throw new DOMPDF_Exception("Child not found in this frame"); - } - - if ( $update_node ) { - $this->_node->removeChild($child->_node); - } - - if ( $child === $this->_first_child ) { - $this->_first_child = $child->_next_sibling; - } - - if ( $child === $this->_last_child ) { - $this->_last_child = $child->_prev_sibling; - } - - if ( $child->_prev_sibling ) { - $child->_prev_sibling->_next_sibling = $child->_next_sibling; - } - - if ( $child->_next_sibling ) { - $child->_next_sibling->_prev_sibling = $child->_prev_sibling; - } - - $child->_next_sibling = null; - $child->_prev_sibling = null; - $child->_parent = null; - return $child; - } - - //........................................................................ - - // Debugging function: - function __toString() { - // Skip empty text frames -// if ( $this->is_text_node() && -// preg_replace("/\s/", "", $this->_node->data) === "" ) -// return ""; - - - $str = "" . $this->_node->nodeName . ":
"; - //$str .= spl_object_hash($this->_node) . "
"; - $str .= "Id: " .$this->get_id() . "
"; - $str .= "Class: " .get_class($this) . "
"; - - if ( $this->is_text_node() ) { - $tmp = htmlspecialchars($this->_node->nodeValue); - $str .= "
'" .  mb_substr($tmp,0,70) .
-        (mb_strlen($tmp) > 70 ? "..." : "") . "'
"; - } - elseif ( $css_class = $this->_node->getAttribute("class") ) { - $str .= "CSS class: '$css_class'
"; - } - - if ( $this->_parent ) { - $str .= "\nParent:" . $this->_parent->_node->nodeName . - " (" . spl_object_hash($this->_parent->_node) . ") " . - "
"; - } - - if ( $this->_prev_sibling ) { - $str .= "Prev: " . $this->_prev_sibling->_node->nodeName . - " (" . spl_object_hash($this->_prev_sibling->_node) . ") " . - "
"; - } - - if ( $this->_next_sibling ) { - $str .= "Next: " . $this->_next_sibling->_node->nodeName . - " (" . spl_object_hash($this->_next_sibling->_node) . ") " . - "
"; - } - - $d = $this->get_decorator(); - while ($d && $d != $d->get_decorator()) { - $str .= "Decorator: " . get_class($d) . "
"; - $d = $d->get_decorator(); - } - - $str .= "Position: " . pre_r($this->_position, true); - $str .= "\nContaining block: " . pre_r($this->_containing_block, true); - $str .= "\nMargin width: " . pre_r($this->get_margin_width(), true); - $str .= "\nMargin height: " . pre_r($this->get_margin_height(), true); - - $str .= "\nStyle:
". $this->_style->__toString() . "
"; - - if ( $this->_decorator instanceof Block_Frame_Decorator ) { - $str .= "Lines:
";
-      foreach ($this->_decorator->get_line_boxes() as $line) {
-        foreach ($line->get_frames() as $frame) {
-          if ($frame instanceof Text_Frame_Decorator) {
-            $str .= "\ntext: ";
-            $str .= "'". htmlspecialchars($frame->get_text()) ."'";
-          }
-          else {
-            $str .= "\nBlock: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")";
-          }
-        }
-
-        $str .=
-          "\ny => " . $line->y . "\n" .
-          "w => " . $line->w . "\n" .
-          "h => " . $line->h . "\n" .
-          "left => " . $line->left . "\n" .
-          "right => " . $line->right . "\n";
-      }
-      $str .= "
"; - } - - $str .= "\n"; - if ( php_sapi_name() === "cli" ) { - $str = strip_tags(str_replace(array("
","",""), - array("\n","",""), - $str)); - } - - return $str; - } -} - -//------------------------------------------------------------------------ - -/** - * Linked-list IteratorAggregate - * - * @access private - * @package dompdf - */ -class FrameList implements IteratorAggregate { - protected $_frame; - - function __construct($frame) { $this->_frame = $frame; } - function getIterator() { return new FrameListIterator($this->_frame); } -} - -/** - * Linked-list Iterator - * - * Returns children in order and allows for list to change during iteration, - * provided the changes occur to or after the current element - * - * @access private - * @package dompdf - */ -class FrameListIterator implements Iterator { - - /** - * @var Frame - */ - protected $_parent; - - /** - * @var Frame - */ - protected $_cur; - - /** - * @var int - */ - protected $_num; - - function __construct(Frame $frame) { - $this->_parent = $frame; - $this->_cur = $frame->get_first_child(); - $this->_num = 0; - } - - function rewind() { - $this->_cur = $this->_parent->get_first_child(); - $this->_num = 0; - } - - /** - * @return bool - */ - function valid() { - return isset($this->_cur);// && ($this->_cur->get_prev_sibling() === $this->_prev); - } - - function key() { return $this->_num; } - - /** - * @return Frame - */ - function current() { return $this->_cur; } - - /** - * @return Frame - */ - function next() { - - $ret = $this->_cur; - if ( !$ret ) { - return null; - } - - $this->_cur = $this->_cur->get_next_sibling(); - $this->_num++; - return $ret; - } -} - -//------------------------------------------------------------------------ - -/** - * Pre-order IteratorAggregate - * - * @access private - * @package dompdf - */ -class FrameTreeList implements IteratorAggregate { - /** - * @var Frame - */ - protected $_root; - - function __construct(Frame $root) { $this->_root = $root; } - - /** - * @return FrameTreeIterator - */ - function getIterator() { return new FrameTreeIterator($this->_root); } -} - -/** - * Pre-order Iterator - * - * Returns frames in preorder traversal order (parent then children) - * - * @access private - * @package dompdf - */ -class FrameTreeIterator implements Iterator { - /** - * @var Frame - */ - protected $_root; - protected $_stack = array(); - - /** - * @var int - */ - protected $_num; - - function __construct(Frame $root) { - $this->_stack[] = $this->_root = $root; - $this->_num = 0; - } - - function rewind() { - $this->_stack = array($this->_root); - $this->_num = 0; - } - - /** - * @return bool - */ - function valid() { - return count($this->_stack) > 0; - } - - /** - * @return int - */ - function key() { - return $this->_num; - } - - /** - * @return Frame - */ - function current() { - return end($this->_stack); - } - - /** - * @return Frame - */ - function next() { - $b = end($this->_stack); - - // Pop last element - unset($this->_stack[ key($this->_stack) ]); - $this->_num++; - - // Push all children onto the stack in reverse order - if ( $c = $b->get_last_child() ) { - $this->_stack[] = $c; - while ( $c = $c->get_prev_sibling() ) { - $this->_stack[] = $c; - } - } - - return $b; - } -} - diff --git a/library/vendor/dompdf/include/frame_decorator.cls.php b/library/vendor/dompdf/include/frame_decorator.cls.php deleted file mode 100644 index 987e32ee1..000000000 --- a/library/vendor/dompdf/include/frame_decorator.cls.php +++ /dev/null @@ -1,717 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Base Frame_Decorator class - * - * @access private - * @package dompdf - */ -abstract class Frame_Decorator extends Frame { - const DEFAULT_COUNTER = "-dompdf-default-counter"; - - public $_counters = array(); // array([id] => counter_value) (for generated content) - - /** - * The root node of the DOM tree - * - * @var Frame - */ - protected $_root; - - /** - * The decorated frame - * - * @var Frame - */ - protected $_frame; - - /** - * Positioner object used to position this frame (Strategy pattern) - * - * @var Positioner - */ - protected $_positioner; - - /** - * Reflower object used to calculate frame dimensions (Strategy pattern) - * - * @var Frame_Reflower - */ - protected $_reflower; - - /** - * Reference to the current dompdf instance - * - * @var DOMPDF - */ - protected $_dompdf; - - /** - * First block parent - * - * @var Block_Frame_Decorator - */ - private $_block_parent; - - /** - * First positionned parent (position: relative | absolute | fixed) - * - * @var Frame_Decorator - */ - private $_positionned_parent; - - /** - * Class constructor - * - * @param Frame $frame The decoration target - * @param DOMPDF $dompdf The DOMPDF object - */ - function __construct(Frame $frame, DOMPDF $dompdf) { - $this->_frame = $frame; - $this->_root = null; - $this->_dompdf = $dompdf; - $frame->set_decorator($this); - } - - /** - * "Destructor": foribly free all references held by this object - * - * @param bool $recursive if true, call dispose on all children - */ - function dispose($recursive = false) { - if ( $recursive ) { - while ( $child = $this->get_first_child() ) { - $child->dispose(true); - } - } - - $this->_root = null; - unset($this->_root); - - $this->_frame->dispose(true); - $this->_frame = null; - unset($this->_frame); - - $this->_positioner = null; - unset($this->_positioner); - - $this->_reflower = null; - unset($this->_reflower); - } - - /** - * Return a copy of this frame with $node as its node - * - * @param DOMNode $node - * - * @return Frame - */ - function copy(DOMNode $node) { - $frame = new Frame($node); - $frame->set_style(clone $this->_frame->get_original_style()); - - return Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root); - } - - /** - * Create a deep copy: copy this node and all children - * - * @return Frame - */ - function deep_copy() { - $frame = new Frame($this->get_node()->cloneNode()); - $frame->set_style(clone $this->_frame->get_original_style()); - - $deco = Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root); - - foreach ($this->get_children() as $child) { - $deco->append_child($child->deep_copy()); - } - - return $deco; - } - - /** - * Delegate calls to decorated frame object - */ - function reset() { - $this->_frame->reset(); - - $this->_counters = array(); - - // Reset all children - foreach ($this->get_children() as $child) { - $child->reset(); - } - } - - // Getters ----------- - function get_id() { - return $this->_frame->get_id(); - } - - /** - * @return Frame - */ - function get_frame() { - return $this->_frame; - } - - /** - * @return DOMElement|DOMText - */ - function get_node() { - return $this->_frame->get_node(); - } - - /** - * @return Style - */ - function get_style() { - return $this->_frame->get_style(); - } - - /** - * @return Style - */ - function get_original_style() { - return $this->_frame->get_original_style(); - } - - /** - * @param integer $i - * - * @return array|float - */ - function get_containing_block($i = null) { - return $this->_frame->get_containing_block($i); - } - - /** - * @param integer $i - * - * @return array|float - */ - function get_position($i = null) { - return $this->_frame->get_position($i); - } - - /** - * @return DOMPDF - */ - function get_dompdf() { - return $this->_dompdf; - } - - /** - * @return float - */ - function get_margin_height() { - return $this->_frame->get_margin_height(); - } - - /** - * @return float - */ - function get_margin_width() { - return $this->_frame->get_margin_width(); - } - - /** - * @return array - */ - function get_padding_box() { - return $this->_frame->get_padding_box(); - } - - /** - * @return array - */ - function get_border_box() { - return $this->_frame->get_border_box(); - } - - /** - * @param integer $id - */ - function set_id($id) { - $this->_frame->set_id($id); - } - - /** - * @param Style $style - */ - function set_style(Style $style) { - $this->_frame->set_style($style); - } - - /** - * @param float $x - * @param float $y - * @param float $w - * @param float $h - */ - function set_containing_block($x = null, $y = null, $w = null, $h = null) { - $this->_frame->set_containing_block($x, $y, $w, $h); - } - - /** - * @param float $x - * @param float $y - */ - function set_position($x = null, $y = null) { - $this->_frame->set_position($x, $y); - } - - /** - * @return string - */ - function __toString() { - return $this->_frame->__toString(); - } - - /** - * @param Frame $child - * @param bool $update_node - */ - function prepend_child(Frame $child, $update_node = true) { - while ( $child instanceof Frame_Decorator ) { - $child = $child->_frame; - } - - $this->_frame->prepend_child($child, $update_node); - } - - /** - * @param Frame $child - * @param bool $update_node - */ - function append_child(Frame $child, $update_node = true) { - while ( $child instanceof Frame_Decorator ) { - $child = $child->_frame; - } - - $this->_frame->append_child($child, $update_node); - } - - /** - * @param Frame $new_child - * @param Frame $ref - * @param bool $update_node - */ - function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) { - while ( $new_child instanceof Frame_Decorator ) { - $new_child = $new_child->_frame; - } - - if ( $ref instanceof Frame_Decorator ) { - $ref = $ref->_frame; - } - - $this->_frame->insert_child_before($new_child, $ref, $update_node); - } - - /** - * @param Frame $new_child - * @param Frame $ref - * @param bool $update_node - */ - function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) { - while ( $new_child instanceof Frame_Decorator ) { - $new_child = $new_child->_frame; - } - - while ( $ref instanceof Frame_Decorator ) { - $ref = $ref->_frame; - } - - $this->_frame->insert_child_after($new_child, $ref, $update_node); - } - - /** - * @param Frame $child - * @param bool $update_node - * - * @return Frame - */ - function remove_child(Frame $child, $update_node = true) { - while ( $child instanceof Frame_Decorator ) { - $child = $child->_frame; - } - - return $this->_frame->remove_child($child, $update_node); - } - - /** - * @return Frame_Decorator - */ - function get_parent() { - $p = $this->_frame->get_parent(); - if ( $p && $deco = $p->get_decorator() ) { - while ( $tmp = $deco->get_decorator() ) { - $deco = $tmp; - } - - return $deco; - } - else if ( $p ) { - return $p; - } - - return null; - } - - /** - * @return Frame_Decorator - */ - function get_first_child() { - $c = $this->_frame->get_first_child(); - if ( $c && $deco = $c->get_decorator() ) { - while ( $tmp = $deco->get_decorator() ) { - $deco = $tmp; - } - - return $deco; - } - else if ( $c ) { - return $c; - } - - return null; - } - - /** - * @return Frame_Decorator - */ - function get_last_child() { - $c = $this->_frame->get_last_child(); - if ( $c && $deco = $c->get_decorator() ) { - while ( $tmp = $deco->get_decorator() ) { - $deco = $tmp; - } - - return $deco; - } - else if ( $c ) { - return $c; - } - - return null; - } - - /** - * @return Frame_Decorator - */ - function get_prev_sibling() { - $s = $this->_frame->get_prev_sibling(); - if ( $s && $deco = $s->get_decorator() ) { - while ( $tmp = $deco->get_decorator() ) { - $deco = $tmp; - } - return $deco; - } - else if ( $s ) { - return $s; - } - - return null; - } - - /** - * @return Frame_Decorator - */ - function get_next_sibling() { - $s = $this->_frame->get_next_sibling(); - if ( $s && $deco = $s->get_decorator() ) { - while ( $tmp = $deco->get_decorator() ) { - $deco = $tmp; - } - - return $deco; - } - else if ( $s ) { - return $s; - } - - return null; - } - - /** - * @return FrameTreeList - */ - function get_subtree() { - return new FrameTreeList($this); - } - - function set_positioner(Positioner $posn) { - $this->_positioner = $posn; - if ( $this->_frame instanceof Frame_Decorator ) { - $this->_frame->set_positioner($posn); - } - } - - function set_reflower(Frame_Reflower $reflower) { - $this->_reflower = $reflower; - if ( $this->_frame instanceof Frame_Decorator ) { - $this->_frame->set_reflower( $reflower ); - } - } - - /** - * @return Frame_Reflower - */ - function get_reflower() { - return $this->_reflower; - } - - /** - * @param Frame $root - */ - function set_root(Frame $root) { - $this->_root = $root; - - if ( $this->_frame instanceof Frame_Decorator ) { - $this->_frame->set_root($root); - } - } - - /** - * @return Page_Frame_Decorator - */ - function get_root() { - return $this->_root; - } - - /** - * @return Block_Frame_Decorator - */ - function find_block_parent() { - // Find our nearest block level parent - $p = $this->get_parent(); - - while ( $p ) { - if ( $p->is_block() ) { - break; - } - - $p = $p->get_parent(); - } - - return $this->_block_parent = $p; - } - - /** - * @return Frame_Decorator - */ - function find_positionned_parent() { - // Find our nearest relative positionned parent - $p = $this->get_parent(); - while ( $p ) { - if ( $p->is_positionned() ) { - break; - } - - $p = $p->get_parent(); - } - - if ( !$p ) { - $p = $this->_root->get_first_child(); // - } - - return $this->_positionned_parent = $p; - } - - /** - * split this frame at $child. - * The current frame is cloned and $child and all children following - * $child are added to the clone. The clone is then passed to the - * current frame's parent->split() method. - * - * @param Frame $child - * @param boolean $force_pagebreak - * - * @throws DOMPDF_Exception - * @return void - */ - function split(Frame $child = null, $force_pagebreak = false) { - // decrement any counters that were incremented on the current node, unless that node is the body - $style = $this->_frame->get_style(); - if ( $this->_frame->get_node()->nodeName !== "body" && $style->counter_increment && ($decrement = $style->counter_increment) !== "none" ) { - $this->decrement_counters($decrement); - } - - if ( is_null( $child ) ) { - // check for counter increment on :before content (always a child of the selected element @link Frame_Reflower::_set_content) - // this can push the current node to the next page before counter rules have bubbled up (but only if - // it's been rendered, thus the position check) - if ( !$this->is_text_node() && $this->get_node()->hasAttribute("dompdf_before_frame_id") ) { - foreach($this->_frame->get_children() as $child) { - if ( $this->get_node()->getAttribute("dompdf_before_frame_id") == $child->get_id() && $child->get_position('x') !== NULL ) { - $style = $child->get_style(); - if ( $style->counter_increment && ($decrement = $style->counter_increment) !== "none" ) { - $this->decrement_counters($decrement); - } - } - } - } - $this->get_parent()->split($this, $force_pagebreak); - return; - } - - if ( $child->get_parent() !== $this ) { - throw new DOMPDF_Exception("Unable to split: frame is not a child of this one."); - } - - $node = $this->_frame->get_node(); - - $split = $this->copy( $node->cloneNode() ); - $split->reset(); - $split->get_original_style()->text_indent = 0; - $split->_splitted = true; - - // The body's properties must be kept - if ( $node->nodeName !== "body" ) { - // Style reset on the first and second parts - $style = $this->_frame->get_style(); - $style->margin_bottom = 0; - $style->padding_bottom = 0; - $style->border_bottom = 0; - - // second - $orig_style = $split->get_original_style(); - $orig_style->text_indent = 0; - $orig_style->margin_top = 0; - $orig_style->padding_top = 0; - $orig_style->border_top = 0; - } - - $this->get_parent()->insert_child_after($split, $this); - - // Add $frame and all following siblings to the new split node - $iter = $child; - while ($iter) { - $frame = $iter; - $iter = $iter->get_next_sibling(); - $frame->reset(); - $split->append_child($frame); - } - - $this->get_parent()->split($split, $force_pagebreak); - - // If this node resets a counter save the current value to use when rendering on the next page - if ( $style->counter_reset && ( $reset = $style->counter_reset ) !== "none" ) { - $vars = preg_split( '/\s+/' , trim( $reset ) , 2 ); - $split->_counters[ '__' . $vars[0] ] = $this->lookup_counter_frame( $vars[0] )->_counters[$vars[0]]; - } - } - - function reset_counter($id = self::DEFAULT_COUNTER, $value = 0) { - $this->get_parent()->_counters[$id] = intval($value); - } - - function decrement_counters($counters) { - foreach($counters as $id => $increment) { - $this->increment_counter($id, intval($increment) * -1); - } - } - - function increment_counters($counters) { - foreach($counters as $id => $increment) { - $this->increment_counter($id, intval($increment)); - } - } - - function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1) { - $counter_frame = $this->lookup_counter_frame($id); - - if ( $counter_frame ) { - if ( !isset($counter_frame->_counters[$id]) ) { - $counter_frame->_counters[$id] = 0; - } - - $counter_frame->_counters[$id] += $increment; - } - } - - function lookup_counter_frame($id = self::DEFAULT_COUNTER) { - $f = $this->get_parent(); - - while( $f ) { - if( isset($f->_counters[$id]) ) { - return $f; - } - $fp = $f->get_parent(); - - if ( !$fp ) { - return $f; - } - - $f = $fp; - } - } - - // TODO: What version is the best : this one or the one in List_Bullet_Renderer ? - function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal") { - $type = mb_strtolower($type); - - if ( !isset($this->_counters[$id]) ) { - $this->_counters[$id] = 0; - } - - $value = $this->_counters[$id]; - - switch ($type) { - default: - case "decimal": - return $value; - - case "decimal-leading-zero": - return str_pad($value, 2, "0"); - - case "lower-roman": - return dec2roman($value); - - case "upper-roman": - return mb_strtoupper(dec2roman($value)); - - case "lower-latin": - case "lower-alpha": - return chr( ($value % 26) + ord('a') - 1); - - case "upper-latin": - case "upper-alpha": - return chr( ($value % 26) + ord('A') - 1); - - case "lower-greek": - return unichr($value + 944); - - case "upper-greek": - return unichr($value + 912); - } - } - - final function position() { - $this->_positioner->position(); - } - - final function move($offset_x, $offset_y, $ignore_self = false) { - $this->_positioner->move($offset_x, $offset_y, $ignore_self); - } - - final function reflow(Block_Frame_Decorator $block = null) { - // Uncomment this to see the frames before they're laid out, instead of - // during rendering. - //echo $this->_frame; flush(); - $this->_reflower->reflow($block); - } - - final function get_min_max_width() { - return $this->_reflower->get_min_max_width(); - } -} diff --git a/library/vendor/dompdf/include/frame_factory.cls.php b/library/vendor/dompdf/include/frame_factory.cls.php deleted file mode 100644 index 70813d2e3..000000000 --- a/library/vendor/dompdf/include/frame_factory.cls.php +++ /dev/null @@ -1,252 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Contains frame decorating logic - * - * This class is responsible for assigning the correct {@link Frame_Decorator}, - * {@link Positioner}, and {@link Frame_Reflower} objects to {@link Frame} - * objects. This is determined primarily by the Frame's display type, but - * also by the Frame's node's type (e.g. DomElement vs. #text) - * - * @access private - * @package dompdf - */ -class Frame_Factory { - - /** - * Decorate the root Frame - * - * @param $root Frame The frame to decorate - * @param $dompdf DOMPDF The dompdf instance - * @return Page_Frame_Decorator - */ - static function decorate_root(Frame $root, DOMPDF $dompdf) { - $frame = new Page_Frame_Decorator($root, $dompdf); - $frame->set_reflower( new Page_Frame_Reflower($frame) ); - $root->set_decorator($frame); - return $frame; - } - - /** - * Decorate a Frame - * - * @param Frame $frame The frame to decorate - * @param DOMPDF $dompdf The dompdf instance - * @param Frame $root The frame to decorate - * - * @throws DOMPDF_Exception - * @return Frame_Decorator - * FIXME: this is admittedly a little smelly... - */ - static function decorate_frame(Frame $frame, DOMPDF $dompdf, Frame $root = null) { - if ( is_null($dompdf) ) { - throw new DOMPDF_Exception("The DOMPDF argument is required"); - } - - $style = $frame->get_style(); - - // Floating (and more generally out-of-flow) elements are blocks - // http://coding.smashingmagazine.com/2007/05/01/css-float-theory-things-you-should-know/ - if ( !$frame->is_in_flow() && in_array($style->display, Style::$INLINE_TYPES)) { - $style->display = "block"; - } - - $display = $style->display; - - switch ($display) { - - case "block": - $positioner = "Block"; - $decorator = "Block"; - $reflower = "Block"; - break; - - case "inline-block": - $positioner = "Inline"; - $decorator = "Block"; - $reflower = "Block"; - break; - - case "inline": - $positioner = "Inline"; - if ( $frame->is_text_node() ) { - $decorator = "Text"; - $reflower = "Text"; - } - else { - $enable_css_float = $dompdf->get_option("enable_css_float"); - if ( $enable_css_float && $style->float !== "none" ) { - $decorator = "Block"; - $reflower = "Block"; - } - else { - $decorator = "Inline"; - $reflower = "Inline"; - } - } - break; - - case "table": - $positioner = "Block"; - $decorator = "Table"; - $reflower = "Table"; - break; - - case "inline-table": - $positioner = "Inline"; - $decorator = "Table"; - $reflower = "Table"; - break; - - case "table-row-group": - case "table-header-group": - case "table-footer-group": - $positioner = "Null"; - $decorator = "Table_Row_Group"; - $reflower = "Table_Row_Group"; - break; - - case "table-row": - $positioner = "Null"; - $decorator = "Table_Row"; - $reflower = "Table_Row"; - break; - - case "table-cell": - $positioner = "Table_Cell"; - $decorator = "Table_Cell"; - $reflower = "Table_Cell"; - break; - - case "list-item": - $positioner = "Block"; - $decorator = "Block"; - $reflower = "Block"; - break; - - case "-dompdf-list-bullet": - if ( $style->list_style_position === "inside" ) { - $positioner = "Inline"; - } - else { - $positioner = "List_Bullet"; - } - - if ( $style->list_style_image !== "none" ) { - $decorator = "List_Bullet_Image"; - } - else { - $decorator = "List_Bullet"; - } - - $reflower = "List_Bullet"; - break; - - case "-dompdf-image": - $positioner = "Inline"; - $decorator = "Image"; - $reflower = "Image"; - break; - - case "-dompdf-br": - $positioner = "Inline"; - $decorator = "Inline"; - $reflower = "Inline"; - break; - - default: - // FIXME: should throw some sort of warning or something? - case "none": - if ( $style->_dompdf_keep !== "yes" ) { - // Remove the node and the frame - $frame->get_parent()->remove_child($frame); - return; - } - - $positioner = "Null"; - $decorator = "Null"; - $reflower = "Null"; - break; - } - - // Handle CSS position - $position = $style->position; - - if ( $position === "absolute" ) { - $positioner = "Absolute"; - } - else if ( $position === "fixed" ) { - $positioner = "Fixed"; - } - - $node = $frame->get_node(); - - // Handle nodeName - if ( $node->nodeName === "img" ) { - $style->display = "-dompdf-image"; - $decorator = "Image"; - $reflower = "Image"; - } - - $positioner .= "_Positioner"; - $decorator .= "_Frame_Decorator"; - $reflower .= "_Frame_Reflower"; - - $deco = new $decorator($frame, $dompdf); - - $deco->set_positioner( new $positioner($deco) ); - $deco->set_reflower( new $reflower($deco) ); - - if ( $root ) { - $deco->set_root($root); - } - - if ( $display === "list-item" ) { - // Insert a list-bullet frame - $xml = $dompdf->get_dom(); - $bullet_node = $xml->createElement("bullet"); // arbitrary choice - $b_f = new Frame($bullet_node); - - $node = $frame->get_node(); - $parent_node = $node->parentNode; - - if ( $parent_node ) { - if ( !$parent_node->hasAttribute("dompdf-children-count") ) { - $xpath = new DOMXPath($xml); - $count = $xpath->query("li", $parent_node)->length; - $parent_node->setAttribute("dompdf-children-count", $count); - } - - if ( is_numeric($node->getAttribute("value")) ) { - $index = intval($node->getAttribute("value")); - } - else { - if ( !$parent_node->hasAttribute("dompdf-counter") ) { - $index = ($parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1); - } - else { - $index = $parent_node->getAttribute("dompdf-counter")+1; - } - } - - $parent_node->setAttribute("dompdf-counter", $index); - $bullet_node->setAttribute("dompdf-counter", $index); - } - - $new_style = $dompdf->get_css()->create_style(); - $new_style->display = "-dompdf-list-bullet"; - $new_style->inherit($style); - $b_f->set_style($new_style); - - $deco->prepend_child( Frame_Factory::decorate_frame($b_f, $dompdf, $root) ); - } - - return $deco; - } -} diff --git a/library/vendor/dompdf/include/frame_reflower.cls.php b/library/vendor/dompdf/include/frame_reflower.cls.php deleted file mode 100644 index 576039e1f..000000000 --- a/library/vendor/dompdf/include/frame_reflower.cls.php +++ /dev/null @@ -1,458 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Base reflower class - * - * Reflower objects are responsible for determining the width and height of - * individual frames. They also create line and page breaks as necessary. - * - * @access private - * @package dompdf - */ -abstract class Frame_Reflower { - - /** - * Frame for this reflower - * - * @var Frame - */ - protected $_frame; - - /** - * Cached min/max size - * - * @var array - */ - protected $_min_max_cache; - - function __construct(Frame $frame) { - $this->_frame = $frame; - $this->_min_max_cache = null; - } - - function dispose() { - clear_object($this); - } - - /** - * @return DOMPDF - */ - function get_dompdf() { - return $this->_frame->get_dompdf(); - } - - /** - * Collapse frames margins - * http://www.w3.org/TR/CSS2/box.html#collapsing-margins - */ - protected function _collapse_margins() { - $frame = $this->_frame; - $cb = $frame->get_containing_block(); - $style = $frame->get_style(); - - if ( !$frame->is_in_flow() ) { - return; - } - - $t = $style->length_in_pt($style->margin_top, $cb["h"]); - $b = $style->length_in_pt($style->margin_bottom, $cb["h"]); - - // Handle 'auto' values - if ( $t === "auto" ) { - $style->margin_top = "0pt"; - $t = 0; - } - - if ( $b === "auto" ) { - $style->margin_bottom = "0pt"; - $b = 0; - } - - // Collapse vertical margins: - $n = $frame->get_next_sibling(); - if ( $n && !$n->is_block() ) { - while ( $n = $n->get_next_sibling() ) { - if ( $n->is_block() ) { - break; - } - - if ( !$n->get_first_child() ) { - $n = null; - break; - } - } - } - - if ( $n ) { - $n_style = $n->get_style(); - $b = max($b, $n_style->length_in_pt($n_style->margin_top, $cb["h"])); - $n_style->margin_top = "0pt"; - $style->margin_bottom = $b."pt"; - } - - // Collapse our first child's margin - /*$f = $this->_frame->get_first_child(); - if ( $f && !$f->is_block() ) { - while ( $f = $f->get_next_sibling() ) { - if ( $f->is_block() ) { - break; - } - - if ( !$f->get_first_child() ) { - $f = null; - break; - } - } - } - - // Margin are collapsed only between block elements - if ( $f ) { - $f_style = $f->get_style(); - $t = max($t, $f_style->length_in_pt($f_style->margin_top, $cb["h"])); - $style->margin_top = $t."pt"; - $f_style->margin_bottom = "0pt"; - }*/ - } - - //........................................................................ - - abstract function reflow(Block_Frame_Decorator $block = null); - - //........................................................................ - - // Required for table layout: Returns an array(0 => min, 1 => max, "min" - // => min, "max" => max) of the minimum and maximum widths of this frame. - // This provides a basic implementation. Child classes should override - // this if necessary. - function get_min_max_width() { - if ( !is_null($this->_min_max_cache) ) { - return $this->_min_max_cache; - } - - $style = $this->_frame->get_style(); - - // Account for margins & padding - $dims = array($style->padding_left, - $style->padding_right, - $style->border_left_width, - $style->border_right_width, - $style->margin_left, - $style->margin_right); - - $cb_w = $this->_frame->get_containing_block("w"); - $delta = $style->length_in_pt($dims, $cb_w); - - // Handle degenerate case - if ( !$this->_frame->get_first_child() ) { - return $this->_min_max_cache = array( - $delta, $delta, - "min" => $delta, - "max" => $delta, - ); - } - - $low = array(); - $high = array(); - - for ( $iter = $this->_frame->get_children()->getIterator(); - $iter->valid(); - $iter->next() ) { - - $inline_min = 0; - $inline_max = 0; - - // Add all adjacent inline widths together to calculate max width - while ( $iter->valid() && in_array( $iter->current()->get_style()->display, Style::$INLINE_TYPES ) ) { - - $child = $iter->current(); - - $minmax = $child->get_min_max_width(); - - if ( in_array( $iter->current()->get_style()->white_space, array("pre", "nowrap") ) ) { - $inline_min += $minmax["min"]; - } - else { - $low[] = $minmax["min"]; - } - - $inline_max += $minmax["max"]; - $iter->next(); - - } - - if ( $inline_max > 0 ) $high[] = $inline_max; - if ( $inline_min > 0 ) $low[] = $inline_min; - - if ( $iter->valid() ) { - list($low[], $high[]) = $iter->current()->get_min_max_width(); - continue; - } - - } - $min = count($low) ? max($low) : 0; - $max = count($high) ? max($high) : 0; - - // Use specified width if it is greater than the minimum defined by the - // content. If the width is a percentage ignore it for now. - $width = $style->width; - if ( $width !== "auto" && !is_percent($width) ) { - $width = $style->length_in_pt($width, $cb_w); - if ( $min < $width ) $min = $width; - if ( $max < $width ) $max = $width; - } - - $min += $delta; - $max += $delta; - return $this->_min_max_cache = array($min, $max, "min"=>$min, "max"=>$max); - } - - /** - * Parses a CSS string containing quotes and escaped hex characters - * - * @param $string string The CSS string to parse - * @param $single_trim - * @return string - */ - protected function _parse_string($string, $single_trim = false) { - if ( $single_trim ) { - $string = preg_replace('/^[\"\']/', "", $string); - $string = preg_replace('/[\"\']$/', "", $string); - } - else { - $string = trim($string, "'\""); - } - - $string = str_replace(array("\\\n",'\\"',"\\'"), - array("",'"',"'"), $string); - - // Convert escaped hex characters into ascii characters (e.g. \A => newline) - $string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/", - create_function('$matches', - 'return unichr(hexdec($matches[1]));'), - $string); - return $string; - } - - /** - * Parses a CSS "quotes" property - * - * @return array|null An array of pairs of quotes - */ - protected function _parse_quotes() { - - // Matches quote types - $re = '/(\'[^\']*\')|(\"[^\"]*\")/'; - - $quotes = $this->_frame->get_style()->quotes; - - // split on spaces, except within quotes - if ( !preg_match_all($re, "$quotes", $matches, PREG_SET_ORDER) ) { - return null; - } - - $quotes_array = array(); - foreach($matches as &$_quote){ - $quotes_array[] = $this->_parse_string($_quote[0], true); - } - - if ( empty($quotes_array) ) { - $quotes_array = array('"', '"'); - } - - return array_chunk($quotes_array, 2); - } - - /** - * Parses the CSS "content" property - * - * @return string|null The resulting string - */ - protected function _parse_content() { - - // Matches generated content - $re = "/\n". - "\s(counters?\\([^)]*\\))|\n". - "\A(counters?\\([^)]*\\))|\n". - "\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?_frame->get_style()->content; - - $quotes = $this->_parse_quotes(); - - // split on spaces, except within quotes - if ( !preg_match_all($re, $content, $matches, PREG_SET_ORDER) ) { - return null; - } - - $text = ""; - - foreach ($matches as $match) { - - if ( isset($match[2]) && $match[2] !== "" ) { - $match[1] = $match[2]; - } - - if ( isset($match[6]) && $match[6] !== "" ) { - $match[4] = $match[6]; - } - - if ( isset($match[8]) && $match[8] !== "" ) { - $match[7] = $match[8]; - } - - if ( isset($match[1]) && $match[1] !== "" ) { - - // counters?(...) - $match[1] = mb_strtolower(trim($match[1])); - - // Handle counter() references: - // http://www.w3.org/TR/CSS21/generate.html#content - - $i = mb_strpos($match[1], ")"); - if ( $i === false ) { - continue; - } - - preg_match( '/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]+)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i' , $match[1] , $args ); - $counter_id = $args[3]; - if ( strtolower( $args[1] ) == 'counter' ) { - // counter(name [,style]) - if ( isset( $args[5] ) ) { - $type = trim( $args[5] ); - } - else { - $type = null; - } - $p = $this->_frame->lookup_counter_frame( $counter_id ); - - $text .= $p->counter_value($counter_id, $type); - - } - else if ( strtolower( $args[1] ) == 'counters' ) { - // counters(name, string [,style]) - if ( isset($args[5]) ) { - $string = $this->_parse_string( $args[5] ); - } - else { - $string = ""; - } - - if ( isset( $args[7] ) ) { - $type = trim( $args[7] ); - } - else { - $type = null; - } - - $p = $this->_frame->lookup_counter_frame($counter_id); - $tmp = array(); - while ($p) { - // We only want to use the counter values when they actually increment the counter - if ( array_key_exists( $counter_id , $p->_counters ) ) { - array_unshift( $tmp , $p->counter_value($counter_id, $type) ); - } - $p = $p->lookup_counter_frame($counter_id); - - } - $text .= implode( $string , $tmp ); - - } - else { - // countertops? - continue; - } - - } - else if ( isset($match[4]) && $match[4] !== "" ) { - // String match - $text .= $this->_parse_string($match[4]); - } - else if ( isset($match[7]) && $match[7] !== "" ) { - // Directive match - - if ( $match[7] === "open-quote" ) { - // FIXME: do something here - $text .= $quotes[0][0]; - } - else if ( $match[7] === "close-quote" ) { - // FIXME: do something else here - $text .= $quotes[0][1]; - } - else if ( $match[7] === "no-open-quote" ) { - // FIXME: - } - else if ( $match[7] === "no-close-quote" ) { - // FIXME: - } - else if ( mb_strpos($match[7],"attr(") === 0 ) { - - $i = mb_strpos($match[7],")"); - if ( $i === false ) { - continue; - } - - $attr = mb_substr($match[7], 5, $i - 5); - if ( $attr == "" ) { - continue; - } - - $text .= $this->_frame->get_parent()->get_node()->getAttribute($attr); - } - else { - continue; - } - } - } - - return $text; - } - - /** - * Sets the generated content of a generated frame - */ - protected function _set_content(){ - $frame = $this->_frame; - $style = $frame->get_style(); - - // if the element was pushed to a new page use the saved counter value, otherwise use the CSS reset value - if ( $style->counter_reset && ($reset = $style->counter_reset) !== "none" ) { - $vars = preg_split('/\s+/', trim($reset), 2); - $frame->reset_counter( $vars[0] , ( isset($frame->_counters['__'.$vars[0]]) ? $frame->_counters['__'.$vars[0]] : ( isset($vars[1]) ? $vars[1] : 0 ) ) ); - } - - if ( $style->counter_increment && ($increment = $style->counter_increment) !== "none" ) { - $frame->increment_counters($increment); - } - - if ( $style->content && !$frame->get_first_child() && $frame->get_node()->nodeName === "dompdf_generated" ) { - $content = $this->_parse_content(); - // add generated content to the font subset - // FIXME: This is currently too late because the font subset has already been generated. - // See notes in issue #750. - if ( $frame->get_dompdf()->get_option("enable_font_subsetting") && $frame->get_dompdf()->get_canvas() instanceof CPDF_Adapter ) { - $frame->get_dompdf()->get_canvas()->register_string_subset($style->font_family, $content); - } - - $node = $frame->get_node()->ownerDocument->createTextNode($content); - - $new_style = $style->get_stylesheet()->create_style(); - $new_style->inherit($style); - - $new_frame = new Frame($node); - $new_frame->set_style($new_style); - - Frame_Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root()); - $frame->append_child($new_frame); - } - } -} diff --git a/library/vendor/dompdf/include/frame_tree.cls.php b/library/vendor/dompdf/include/frame_tree.cls.php deleted file mode 100644 index 257ca097e..000000000 --- a/library/vendor/dompdf/include/frame_tree.cls.php +++ /dev/null @@ -1,241 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Represents an entire document as a tree of frames - * - * The Frame_Tree consists of {@link Frame} objects each tied to specific - * DOMNode objects in a specific DomDocument. The Frame_Tree has the same - * structure as the DomDocument, but adds additional capabalities for - * styling and layout. - * - * @package dompdf - * @access protected - */ -class Frame_Tree { - - /** - * Tags to ignore while parsing the tree - * - * @var array - */ - static protected $_HIDDEN_TAGS = array("area", "base", "basefont", "head", "style", - "meta", "title", "colgroup", - "noembed", "noscript", "param", "#comment"); - /** - * The main DomDocument - * - * @see http://ca2.php.net/manual/en/ref.dom.php - * @var DomDocument - */ - protected $_dom; - - /** - * The root node of the FrameTree. - * - * @var Frame - */ - protected $_root; - - /** - * Subtrees of absolutely positioned elements - * - * @var array of Frames - */ - protected $_absolute_frames; - - /** - * A mapping of {@link Frame} objects to DOMNode objects - * - * @var array - */ - protected $_registry; - - - /** - * Class constructor - * - * @param DomDocument $dom the main DomDocument object representing the current html document - */ - function __construct(DomDocument $dom) { - $this->_dom = $dom; - $this->_root = null; - $this->_registry = array(); - } - - function __destruct() { - clear_object($this); - } - - /** - * Returns the DomDocument object representing the curent html document - * - * @return DOMDocument - */ - function get_dom() { - return $this->_dom; - } - - /** - * Returns the root frame of the tree - * - * @return Page_Frame_Decorator - */ - function get_root() { - return $this->_root; - } - - /** - * Returns a specific frame given its id - * - * @param string $id - * @return Frame - */ - function get_frame($id) { - return isset($this->_registry[$id]) ? $this->_registry[$id] : null; - } - - /** - * Returns a post-order iterator for all frames in the tree - * - * @return FrameTreeList|Frame[] - */ - function get_frames() { - return new FrameTreeList($this->_root); - } - - /** - * Builds the tree - */ - function build_tree() { - $html = $this->_dom->getElementsByTagName("html")->item(0); - if ( is_null($html) ) { - $html = $this->_dom->firstChild; - } - - if ( is_null($html) ) { - throw new DOMPDF_Exception("Requested HTML document contains no data."); - } - - $this->fix_tables(); - - $this->_root = $this->_build_tree_r($html); - } - - /** - * Adds missing TBODYs around TR - */ - protected function fix_tables(){ - $xp = new DOMXPath($this->_dom); - - // Move table caption before the table - // FIXME find a better way to deal with it... - $captions = $xp->query("//table/caption"); - foreach($captions as $caption) { - $table = $caption->parentNode; - $table->parentNode->insertBefore($caption, $table); - } - - $rows = $xp->query("//table/tr"); - foreach($rows as $row) { - $tbody = $this->_dom->createElement("tbody"); - $tbody = $row->parentNode->insertBefore($tbody, $row); - $tbody->appendChild($row); - } - } - - /** - * Recursively adds {@link Frame} objects to the tree - * - * Recursively build a tree of Frame objects based on a dom tree. - * No layout information is calculated at this time, although the - * tree may be adjusted (i.e. nodes and frames for generated content - * and images may be created). - * - * @param DOMNode $node the current DOMNode being considered - * @return Frame - */ - protected function _build_tree_r(DOMNode $node) { - - $frame = new Frame($node); - $id = $frame->get_id(); - $this->_registry[ $id ] = $frame; - - if ( !$node->hasChildNodes() ) { - return $frame; - } - - // Fixes 'cannot access undefined property for object with - // overloaded access', fix by Stefan radulian - // - //foreach ($node->childNodes as $child) { - - // Store the children in an array so that the tree can be modified - $children = array(); - for ($i = 0; $i < $node->childNodes->length; $i++) { - $children[] = $node->childNodes->item($i); - } - - foreach ($children as $child) { - $node_name = mb_strtolower($child->nodeName); - - // Skip non-displaying nodes - if ( in_array($node_name, self::$_HIDDEN_TAGS) ) { - if ( $node_name !== "head" && $node_name !== "style" ) { - $child->parentNode->removeChild($child); - } - - continue; - } - - // Skip empty text nodes - if ( $node_name === "#text" && $child->nodeValue == "" ) { - $child->parentNode->removeChild($child); - continue; - } - - // Skip empty image nodes - if ( $node_name === "img" && $child->getAttribute("src") == "" ) { - $child->parentNode->removeChild($child); - continue; - } - - $frame->append_child($this->_build_tree_r($child), false); - } - - return $frame; - } - - public function insert_node(DOMNode $node, DOMNode $new_node, $pos) { - if ( $pos === "after" || !$node->firstChild ) { - $node->appendChild($new_node); - } - else { - $node->insertBefore($new_node, $node->firstChild); - } - - $this->_build_tree_r($new_node); - - $frame_id = $new_node->getAttribute("frame_id"); - $frame = $this->get_frame($frame_id); - - $parent_id = $node->getAttribute("frame_id"); - $parent = $this->get_frame($parent_id); - - if ( $parent ) { - if ( $pos === "before" ) { - $parent->prepend_child($frame, false); - } - else { - $parent->append_child($frame, false); - } - } - - return $frame_id; - } -} diff --git a/library/vendor/dompdf/include/functions.inc.php b/library/vendor/dompdf/include/functions.inc.php deleted file mode 100644 index 265244aaf..000000000 --- a/library/vendor/dompdf/include/functions.inc.php +++ /dev/null @@ -1,1036 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -if ( !defined('PHP_VERSION_ID') ) { - $version = explode('.', PHP_VERSION); - define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2])); -} - -/** - * Defined a constant if not already defined - * - * @param string $name The constant name - * @param mixed $value The value - */ -function def($name, $value = true) { - if ( !defined($name) ) { - define($name, $value); - } -} - -if ( !function_exists("pre_r") ) { -/** - * print_r wrapper for html/cli output - * - * Wraps print_r() output in < pre > tags if the current sapi is not 'cli'. - * Returns the output string instead of displaying it if $return is true. - * - * @param mixed $mixed variable or expression to display - * @param bool $return - * - * @return string - */ -function pre_r($mixed, $return = false) { - if ( $return ) { - return "
" . print_r($mixed, true) . "
"; - } - - if ( php_sapi_name() !== "cli" ) { - echo "
";
-  }
-  
-  print_r($mixed);
-
-  if ( php_sapi_name() !== "cli" ) {
-    echo "
"; - } - else { - echo "\n"; - } - - flush(); - -} -} - -if ( !function_exists("pre_var_dump") ) { -/** - * var_dump wrapper for html/cli output - * - * Wraps var_dump() output in < pre > tags if the current sapi is not 'cli'. - * - * @param mixed $mixed variable or expression to display. - */ -function pre_var_dump($mixed) { - if ( php_sapi_name() !== "cli" ) { - echo "
";
-  }
-    
-  var_dump($mixed);
-  
-  if ( php_sapi_name() !== "cli" ) {
-    echo "
"; - } -} -} - -if ( !function_exists("d") ) { -/** - * generic debug function - * - * Takes everything and does its best to give a good debug output - * - * @param mixed $mixed variable or expression to display. - */ -function d($mixed) { - if ( php_sapi_name() !== "cli" ) { - echo "
";
-  }
-    
-  // line
-  if ( $mixed instanceof Line_Box ) {
-    echo $mixed;
-  }
-  
-  // other
-  else {
-    var_export($mixed);
-  }
-  
-  if ( php_sapi_name() !== "cli" ) {
-    echo "
"; - } -} -} - -/** - * builds a full url given a protocol, hostname, base path and url - * - * @param string $protocol - * @param string $host - * @param string $base_path - * @param string $url - * @return string - * - * Initially the trailing slash of $base_path was optional, and conditionally appended. - * However on dynamically created sites, where the page is given as url parameter, - * the base path might not end with an url. - * Therefore do not append a slash, and **require** the $base_url to ending in a slash - * when needed. - * Vice versa, on using the local file system path of a file, make sure that the slash - * is appended (o.k. also for Windows) - */ -function build_url($protocol, $host, $base_path, $url) { - $protocol = mb_strtolower($protocol); - if (strlen($url) == 0) { - //return $protocol . $host . rtrim($base_path, "/\\") . "/"; - return $protocol . $host . $base_path; - } - // Is the url already fully qualified or a Data URI? - if (mb_strpos($url, "://") !== false || mb_strpos($url, "data:") === 0) { - return $url; - } - $ret = $protocol; - if (!in_array(mb_strtolower($protocol), array("http://", "https://", "ftp://", "ftps://"))) { - //On Windows local file, an abs path can begin also with a '\' or a drive letter and colon - //drive: followed by a relative path would be a drive specific default folder. - //not known in php app code, treat as abs path - //($url[1] !== ':' || ($url[2]!=='\\' && $url[2]!=='/')) - if ($url[0] !== '/' && (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' || ($url[0] !== '\\' && $url[1] !== ':'))) { - // For rel path and local acess we ignore the host, and run the path through realpath() - $ret .= realpath($base_path) . '/'; - } - $ret .= $url; - $ret = preg_replace('/\?(.*)$/', "", $ret); - return $ret; - } - // Protocol relative urls (e.g. "//example.org/style.css") - if (strpos($url, '//') === 0) { - $ret .= substr($url, 2); - //remote urls with backslash in html/css are not really correct, but lets be genereous - } elseif ($url[0] === '/' || $url[0] === '\\') { - // Absolute path - $ret .= $host . $url; - } else { - // Relative path - //$base_path = $base_path !== "" ? rtrim($base_path, "/\\") . "/" : ""; - $ret .= $host . $base_path . $url; - } - return $ret; -} - - -/** - * parse a full url or pathname and return an array(protocol, host, path, - * file + query + fragment) - * - * @param string $url - * @return array - */ -function explode_url($url) { - $protocol = ""; - $host = ""; - $path = ""; - $file = ""; - - $arr = parse_url($url); - if ( isset($arr["scheme"])) { - $arr["scheme"] == mb_strtolower($arr["scheme"]); - } - - // Exclude windows drive letters... - if ( isset($arr["scheme"]) && $arr["scheme"] !== "file" && strlen($arr["scheme"]) > 1 ) { - $protocol = $arr["scheme"] . "://"; - - if ( isset($arr["user"]) ) { - $host .= $arr["user"]; - - if ( isset($arr["pass"]) ) { - $host .= ":" . $arr["pass"]; - } - - $host .= "@"; - } - - if ( isset($arr["host"]) ) { - $host .= $arr["host"]; - } - - if ( isset($arr["port"]) ) { - $host .= ":" . $arr["port"]; - } - - if ( isset($arr["path"]) && $arr["path"] !== "" ) { - // Do we have a trailing slash? - if ( $arr["path"][ mb_strlen($arr["path"]) - 1 ] === "/" ) { - $path = $arr["path"]; - $file = ""; - } - else { - $path = rtrim(dirname($arr["path"]), '/\\') . "/"; - $file = basename($arr["path"]); - } - } - - if ( isset($arr["query"]) ) { - $file .= "?" . $arr["query"]; - } - - if ( isset($arr["fragment"]) ) { - $file .= "#" . $arr["fragment"]; - } - - } - else { - - $i = mb_stripos($url, "file://"); - if ( $i !== false ) { - $url = mb_substr($url, $i + 7); - } - - $protocol = ""; // "file://"; ? why doesn't this work... It's because of - // network filenames like //COMPU/SHARENAME - - $host = ""; // localhost, really - $file = basename($url); - - $path = dirname($url); - - // Check that the path exists - if ( $path !== false ) { - $path .= '/'; - - } - else { - // generate a url to access the file if no real path found. - $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://'; - - $host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : php_uname("n"); - - if ( substr($arr["path"], 0, 1) === '/' ) { - $path = dirname($arr["path"]); - } - else { - $path = '/' . rtrim(dirname($_SERVER["SCRIPT_NAME"]), '/') . '/' . $arr["path"]; - } - } - } - - $ret = array($protocol, $host, $path, $file, - "protocol" => $protocol, - "host" => $host, - "path" => $path, - "file" => $file); - return $ret; -} - -/** - * Converts decimal numbers to roman numerals - * - * @param int $num - * - * @throws DOMPDF_Exception - * @return string - */ -function dec2roman($num) { - - static $ones = array("", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"); - static $tens = array("", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"); - static $hund = array("", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"); - static $thou = array("", "m", "mm", "mmm"); - - if ( !is_numeric($num) ) { - throw new DOMPDF_Exception("dec2roman() requires a numeric argument."); - } - - if ( $num > 4000 || $num < 0 ) { - return "(out of range)"; - } - - $num = strrev((string)$num); - - $ret = ""; - switch (mb_strlen($num)) { - case 4: $ret .= $thou[$num[3]]; - case 3: $ret .= $hund[$num[2]]; - case 2: $ret .= $tens[$num[1]]; - case 1: $ret .= $ones[$num[0]]; - default: break; - } - - return $ret; -} - -/** - * Determines whether $value is a percentage or not - * - * @param float $value - * - * @return bool - */ -function is_percent($value) { - return false !== mb_strpos($value, "%"); -} - -/** - * Parses a data URI scheme - * http://en.wikipedia.org/wiki/Data_URI_scheme - * - * @param string $data_uri The data URI to parse - * - * @return array The result with charset, mime type and decoded data - */ -function parse_data_uri($data_uri) { - if (!preg_match('/^data:(?P[a-z0-9\/+-.]+)(;charset=(?P[a-z0-9-])+)?(?P;base64)?\,(?P.*)?/i', $data_uri, $match)) { - return false; - } - - $match['data'] = rawurldecode($match['data']); - $result = array( - 'charset' => $match['charset'] ? $match['charset'] : 'US-ASCII', - 'mime' => $match['mime'] ? $match['mime'] : 'text/plain', - 'data' => $match['base64'] ? base64_decode($match['data']) : $match['data'], - ); - - return $result; -} - -/** - * mb_string compatibility - */ -if (!extension_loaded('mbstring')) { - def('MB_OVERLOAD_MAIL', 1); - def('MB_OVERLOAD_STRING', 2); - def('MB_OVERLOAD_REGEX', 4); - def('MB_CASE_UPPER', 0); - def('MB_CASE_LOWER', 1); - def('MB_CASE_TITLE', 2); - - if (!function_exists('mb_convert_encoding')) { - function mb_convert_encoding($data, $to_encoding, $from_encoding = 'UTF-8') { - if (str_replace('-', '', strtolower($to_encoding)) === 'utf8') { - return utf8_encode($data); - } - - return utf8_decode($data); - } - } - - if (!function_exists('mb_detect_encoding')) { - function mb_detect_encoding($data, $encoding_list = array('iso-8859-1'), $strict = false) { - return 'iso-8859-1'; - } - } - - if (!function_exists('mb_detect_order')) { - function mb_detect_order($encoding_list = array('iso-8859-1')) { - return 'iso-8859-1'; - } - } - - if (!function_exists('mb_internal_encoding')) { - function mb_internal_encoding($encoding = null) { - if (isset($encoding)) { - return true; - } - - return 'iso-8859-1'; - } - } - - if (!function_exists('mb_strlen')) { - function mb_strlen($str, $encoding = 'iso-8859-1') { - switch (str_replace('-', '', strtolower($encoding))) { - case "utf8": return strlen(utf8_encode($str)); - case "8bit": return strlen($str); - default: return strlen(utf8_decode($str)); - } - } - } - - if (!function_exists('mb_strpos')) { - function mb_strpos($haystack, $needle, $offset = 0) { - return strpos($haystack, $needle, $offset); - } - } - - if (!function_exists('mb_stripos')) { - function mb_stripos($haystack, $needle, $offset = 0) { - return stripos($haystack, $needle, $offset); - } - } - - if (!function_exists('mb_strrpos')) { - function mb_strrpos($haystack, $needle, $offset = 0) { - return strrpos($haystack, $needle, $offset); - } - } - - if (!function_exists('mb_strtolower')) { - function mb_strtolower( $str ) { - return strtolower($str); - } - } - - if (!function_exists('mb_strtoupper')) { - function mb_strtoupper( $str ) { - return strtoupper($str); - } - } - - if (!function_exists('mb_substr')) { - function mb_substr($string, $start, $length = null, $encoding = 'iso-8859-1') { - if ( is_null($length) ) { - return substr($string, $start); - } - - return substr($string, $start, $length); - } - } - - if (!function_exists('mb_substr_count')) { - function mb_substr_count($haystack, $needle, $encoding = 'iso-8859-1') { - return substr_count($haystack, $needle); - } - } - - if (!function_exists('mb_encode_numericentity')) { - function mb_encode_numericentity($str, $convmap, $encoding) { - return htmlspecialchars($str); - } - } - - if (!function_exists('mb_convert_case')) { - function mb_convert_case($str, $mode = MB_CASE_UPPER, $encoding = array()) { - switch($mode) { - case MB_CASE_UPPER: return mb_strtoupper($str); - case MB_CASE_LOWER: return mb_strtolower($str); - case MB_CASE_TITLE: return ucwords(mb_strtolower($str)); - default: return $str; - } - } - } - - if (!function_exists('mb_list_encodings')) { - function mb_list_encodings() { - return array( - "ISO-8859-1", - "UTF-8", - "8bit", - ); - } - } -} - -/** - * Decoder for RLE8 compression in windows bitmaps - * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp - * - * @param string $str Data to decode - * @param integer $width Image width - * - * @return string - */ -function rle8_decode ($str, $width){ - $lineWidth = $width + (3 - ($width-1) % 4); - $out = ''; - $cnt = strlen($str); - - for ($i = 0; $i <$cnt; $i++) { - $o = ord($str[$i]); - switch ($o){ - case 0: # ESCAPE - $i++; - switch (ord($str[$i])){ - case 0: # NEW LINE - $padCnt = $lineWidth - strlen($out)%$lineWidth; - if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line - break; - case 1: # END OF FILE - $padCnt = $lineWidth - strlen($out)%$lineWidth; - if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line - break 3; - case 2: # DELTA - $i += 2; - break; - default: # ABSOLUTE MODE - $num = ord($str[$i]); - for ($j = 0; $j < $num; $j++) - $out .= $str[++$i]; - if ($num % 2) $i++; - } - break; - default: - $out .= str_repeat($str[++$i], $o); - } - } - return $out; -} - -/** - * Decoder for RLE4 compression in windows bitmaps - * see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp - * - * @param string $str Data to decode - * @param integer $width Image width - * - * @return string - */ -function rle4_decode ($str, $width) { - $w = floor($width/2) + ($width % 2); - $lineWidth = $w + (3 - ( ($width-1) / 2) % 4); - $pixels = array(); - $cnt = strlen($str); - $c = 0; - - for ($i = 0; $i < $cnt; $i++) { - $o = ord($str[$i]); - switch ($o) { - case 0: # ESCAPE - $i++; - switch (ord($str[$i])){ - case 0: # NEW LINE - while (count($pixels)%$lineWidth != 0) { - $pixels[] = 0; - } - break; - case 1: # END OF FILE - while (count($pixels)%$lineWidth != 0) { - $pixels[] = 0; - } - break 3; - case 2: # DELTA - $i += 2; - break; - default: # ABSOLUTE MODE - $num = ord($str[$i]); - for ($j = 0; $j < $num; $j++) { - if ($j%2 == 0) { - $c = ord($str[++$i]); - $pixels[] = ($c & 240)>>4; - } - else { - $pixels[] = $c & 15; - } - } - - if ($num % 2 == 0) { - $i++; - } - } - break; - default: - $c = ord($str[++$i]); - for ($j = 0; $j < $o; $j++) { - $pixels[] = ($j%2==0 ? ($c & 240)>>4 : $c & 15); - } - } - } - - $out = ''; - if (count($pixels)%2) { - $pixels[] = 0; - } - - $cnt = count($pixels)/2; - - for ($i = 0; $i < $cnt; $i++) { - $out .= chr(16*$pixels[2*$i] + $pixels[2*$i+1]); - } - - return $out; -} - -if ( !function_exists("imagecreatefrombmp") ) { - -/** - * Credit goes to mgutt - * http://www.programmierer-forum.de/function-imagecreatefrombmp-welche-variante-laeuft-t143137.htm - * Modified by Fabien Menager to support RGB555 BMP format - */ -function imagecreatefrombmp($filename) { - if (!function_exists("imagecreatetruecolor")) { - trigger_error("The PHP GD extension is required, but is not installed.", E_ERROR); - return false; - } - - // version 1.00 - if (!($fh = fopen($filename, 'rb'))) { - trigger_error('imagecreatefrombmp: Can not open ' . $filename, E_USER_WARNING); - return false; - } - - $bytes_read = 0; - - // read file header - $meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14)); - - // check for bitmap - if ($meta['type'] != 19778) { - trigger_error('imagecreatefrombmp: ' . $filename . ' is not a bitmap!', E_USER_WARNING); - return false; - } - - // read image header - $meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40)); - $bytes_read += 40; - - // read additional bitfield header - if ($meta['compression'] == 3) { - $meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12)); - $bytes_read += 12; - } - - // set bytes and padding - $meta['bytes'] = $meta['bits'] / 8; - $meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4)- floor($meta['width'] * $meta['bytes'] / 4))); - if ($meta['decal'] == 4) { - $meta['decal'] = 0; - } - - // obtain imagesize - if ($meta['imagesize'] < 1) { - $meta['imagesize'] = $meta['filesize'] - $meta['offset']; - // in rare cases filesize is equal to offset so we need to read physical size - if ($meta['imagesize'] < 1) { - $meta['imagesize'] = @filesize($filename) - $meta['offset']; - if ($meta['imagesize'] < 1) { - trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $filename . '!', E_USER_WARNING); - return false; - } - } - } - - // calculate colors - $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors']; - - // read color palette - $palette = array(); - if ($meta['bits'] < 16) { - $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4)); - // in rare cases the color value is signed - if ($palette[1] < 0) { - foreach ($palette as $i => $color) { - $palette[$i] = $color + 16777216; - } - } - } - - // ignore extra bitmap headers - if ($meta['headersize'] > $bytes_read) { - fread($fh, $meta['headersize'] - $bytes_read); - } - - // create gd image - $im = imagecreatetruecolor($meta['width'], $meta['height']); - $data = fread($fh, $meta['imagesize']); - - // uncompress data - switch ($meta['compression']) { - case 1: $data = rle8_decode($data, $meta['width']); break; - case 2: $data = rle4_decode($data, $meta['width']); break; - } - - $p = 0; - $vide = chr(0); - $y = $meta['height'] - 1; - $error = 'imagecreatefrombmp: ' . $filename . ' has not enough data!'; - - // loop through the image data beginning with the lower left corner - while ($y >= 0) { - $x = 0; - while ($x < $meta['width']) { - switch ($meta['bits']) { - case 32: - case 24: - if (!($part = substr($data, $p, 3 /*$meta['bytes']*/))) { - trigger_error($error, E_USER_WARNING); - return $im; - } - $color = unpack('V', $part . $vide); - break; - case 16: - if (!($part = substr($data, $p, 2 /*$meta['bytes']*/))) { - trigger_error($error, E_USER_WARNING); - return $im; - } - $color = unpack('v', $part); - - if (empty($meta['rMask']) || $meta['rMask'] != 0xf800) { - $color[1] = (($color[1] & 0x7c00) >> 7) * 65536 + (($color[1] & 0x03e0) >> 2) * 256 + (($color[1] & 0x001f) << 3); // 555 - } - else { - $color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3); // 565 - } - break; - case 8: - $color = unpack('n', $vide . substr($data, $p, 1)); - $color[1] = $palette[ $color[1] + 1 ]; - break; - case 4: - $color = unpack('n', $vide . substr($data, floor($p), 1)); - $color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F; - $color[1] = $palette[ $color[1] + 1 ]; - break; - case 1: - $color = unpack('n', $vide . substr($data, floor($p), 1)); - switch (($p * 8) % 8) { - case 0: $color[1] = $color[1] >> 7; break; - case 1: $color[1] = ($color[1] & 0x40) >> 6; break; - case 2: $color[1] = ($color[1] & 0x20) >> 5; break; - case 3: $color[1] = ($color[1] & 0x10) >> 4; break; - case 4: $color[1] = ($color[1] & 0x8 ) >> 3; break; - case 5: $color[1] = ($color[1] & 0x4 ) >> 2; break; - case 6: $color[1] = ($color[1] & 0x2 ) >> 1; break; - case 7: $color[1] = ($color[1] & 0x1 ); break; - } - $color[1] = $palette[ $color[1] + 1 ]; - break; - default: - trigger_error('imagecreatefrombmp: ' . $filename . ' has ' . $meta['bits'] . ' bits and this is not supported!', E_USER_WARNING); - return false; - } - imagesetpixel($im, $x, $y, $color[1]); - $x++; - $p += $meta['bytes']; - } - $y--; - $p += $meta['decal']; - } - fclose($fh); - return $im; -} -} - -/** - * getimagesize doesn't give a good size for 32bit BMP image v5 - * - * @param string $filename - * @return array The same format as getimagesize($filename) - */ -function dompdf_getimagesize($filename, $context = null) { - static $cache = array(); - - if ( isset($cache[$filename]) ) { - return $cache[$filename]; - } - - list($width, $height, $type) = getimagesize($filename); - - if ( $width == null || $height == null ) { - $data = file_get_contents($filename, null, $context, 0, 26); - - if ( substr($data, 0, 2) === "BM" ) { - $meta = unpack('vtype/Vfilesize/Vreserved/Voffset/Vheadersize/Vwidth/Vheight', $data); - $width = (int)$meta['width']; - $height = (int)$meta['height']; - $type = IMAGETYPE_BMP; - } - } - - return $cache[$filename] = array($width, $height, $type); -} - -/** - * Converts a CMYK color to RGB - * - * @param float|float[] $c - * @param float $m - * @param float $y - * @param float $k - * - * @return float[] - */ -function cmyk_to_rgb($c, $m = null, $y = null, $k = null) { - if (is_array($c)) { - list($c, $m, $y, $k) = $c; - } - - $c *= 255; - $m *= 255; - $y *= 255; - $k *= 255; - - $r = (1 - round(2.55 * ($c+$k))) ; - $g = (1 - round(2.55 * ($m+$k))) ; - $b = (1 - round(2.55 * ($y+$k))) ; - - if ($r < 0) $r = 0; - if ($g < 0) $g = 0; - if ($b < 0) $b = 0; - - return array( - $r, $g, $b, - "r" => $r, "g" => $g, "b" => $b - ); -} - -function unichr($c) { - if ($c <= 0x7F) { - return chr($c); - } - else if ($c <= 0x7FF) { - return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F); - } - else if ($c <= 0xFFFF) { - return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F) - . chr(0x80 | $c & 0x3F); - } - else if ($c <= 0x10FFFF) { - return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F) - . chr(0x80 | $c >> 6 & 0x3F) - . chr(0x80 | $c & 0x3F); - } - return false; -} - -if ( !function_exists("date_default_timezone_get") ) { - function date_default_timezone_get() { - return ""; - } - - function date_default_timezone_set($timezone_identifier) { - return true; - } -} - -/** - * Stores warnings in an array for display later - * This function allows warnings generated by the DomDocument parser - * and CSS loader ({@link Stylesheet}) to be captured and displayed - * later. Without this function, errors are displayed immediately and - * PDF streaming is impossible. - * @see http://www.php.net/manual/en/function.set-error_handler.php - * - * @param int $errno - * @param string $errstr - * @param string $errfile - * @param string $errline - * - * @throws DOMPDF_Exception - */ -function record_warnings($errno, $errstr, $errfile, $errline) { - - // Not a warning or notice - if ( !($errno & (E_WARNING | E_NOTICE | E_USER_NOTICE | E_USER_WARNING )) ) { - throw new DOMPDF_Exception($errstr . " $errno"); - } - - global $_dompdf_warnings; - global $_dompdf_show_warnings; - - if ( $_dompdf_show_warnings ) { - echo $errstr . "\n"; - } - - $_dompdf_warnings[] = $errstr; -} - -/** - * Print a useful backtrace - */ -function bt() { - if ( php_sapi_name() !== "cli") { - echo "
";
-  }
-    
-  $bt = debug_backtrace();
-
-  array_shift($bt); // remove actual bt() call
-  echo "\n";
-
-  $i = 0;
-  foreach ($bt as $call) {
-    $file = basename($call["file"]) . " (" . $call["line"] . ")";
-    if ( isset($call["class"]) ) {
-      $func = $call["class"] . "->" . $call["function"] . "()";
-    }
-    else {
-      $func = $call["function"] . "()";
-    }
-
-    echo "#" . str_pad($i, 2, " ", STR_PAD_RIGHT) . ": " . str_pad($file.":", 42) . " $func\n";
-    $i++;
-  }
-  echo "\n";
-  
-  if ( php_sapi_name() !== "cli") {
-    echo "
"; - } -} - -/** - * Print debug messages - * - * @param string $type The type of debug messages to print - * @param string $msg The message to show - */ -function dompdf_debug($type, $msg) { - global $_DOMPDF_DEBUG_TYPES, $_dompdf_show_warnings, $_dompdf_debug; - if ( isset($_DOMPDF_DEBUG_TYPES[$type]) && ($_dompdf_show_warnings || $_dompdf_debug) ) { - $arr = debug_backtrace(); - - echo basename($arr[0]["file"]) . " (" . $arr[0]["line"] ."): " . $arr[1]["function"] . ": "; - pre_r($msg); - } -} - -if ( !function_exists("print_memusage") ) { -/** - * Dump memory usage - */ -function print_memusage() { - global $memusage; - echo "Memory Usage\n"; - $prev = 0; - $initial = reset($memusage); - echo str_pad("Initial:", 40) . $initial . "\n\n"; - - foreach ($memusage as $key=>$mem) { - $mem -= $initial; - echo str_pad("$key:" , 40); - echo str_pad("$mem", 12) . "(diff: " . ($mem - $prev) . ")\n"; - $prev = $mem; - } - - echo "\n" . str_pad("Total:", 40) . memory_get_usage() . "\n"; -} -} - -if ( !function_exists("enable_mem_profile") ) { -/** - * Initialize memory profiling code - */ -function enable_mem_profile() { - global $memusage; - $memusage = array("Startup" => memory_get_usage()); - register_shutdown_function("print_memusage"); -} -} - -if ( !function_exists("mark_memusage") ) { -/** - * Record the current memory usage - * - * @param string $location a meaningful location - */ -function mark_memusage($location) { - global $memusage; - if ( isset($memusage) ) { - $memusage[$location] = memory_get_usage(); - } -} -} - -if ( !function_exists('sys_get_temp_dir')) { -/** - * Find the current system temporary directory - * - * @link http://us.php.net/manual/en/function.sys-get-temp-dir.php#85261 - */ -function sys_get_temp_dir() { - if (!empty($_ENV['TMP'])) { - return realpath($_ENV['TMP']); - } - - if (!empty($_ENV['TMPDIR'])) { - return realpath( $_ENV['TMPDIR']); - } - - if (!empty($_ENV['TEMP'])) { - return realpath( $_ENV['TEMP']); - } - - $tempfile=tempnam(uniqid(rand(), true), ''); - if (file_exists($tempfile)) { - unlink($tempfile); - return realpath(dirname($tempfile)); - } -} -} - -if ( function_exists("memory_get_peak_usage") ) { - function DOMPDF_memory_usage(){ - return memory_get_peak_usage(true); - } -} -else if ( function_exists("memory_get_usage") ) { - function DOMPDF_memory_usage(){ - return memory_get_usage(true); - } -} -else { - function DOMPDF_memory_usage(){ - return "N/A"; - } -} - - -/** - * Affect null to the unused objects - * @param mixed $object - */ -if ( PHP_VERSION_ID < 50300 ) { - function clear_object(&$object) { - if ( is_object($object) ) { - foreach ($object as &$value) { - clear_object($value); - } - } - - $object = null; - unset($object); - } -} -else { - function clear_object(&$object) { - // void - } -} diff --git a/library/vendor/dompdf/include/gd_adapter.cls.php b/library/vendor/dompdf/include/gd_adapter.cls.php deleted file mode 100644 index 1eab04cc4..000000000 --- a/library/vendor/dompdf/include/gd_adapter.cls.php +++ /dev/null @@ -1,840 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Image rendering interface - * - * Renders to an image format supported by GD (jpeg, gif, png, xpm). - * Not super-useful day-to-day but handy nonetheless - * - * @package dompdf - */ -class GD_Adapter implements Canvas { - /** - * @var DOMPDF - */ - private $_dompdf; - - /** - * Resource handle for the image - * - * @var resource - */ - private $_img; - - /** - * Image width in pixels - * - * @var int - */ - private $_width; - - /** - * Image height in pixels - * - * @var int - */ - private $_height; - - /** - * Current page number - * - * @var int - */ - private $_page_number; - - /** - * Total number of pages - * - * @var int - */ - private $_page_count; - - /** - * Image antialias factor - * - * @var float - */ - private $_aa_factor; - - /** - * Allocated colors - * - * @var array - */ - private $_colors; - - /** - * Background color - * - * @var int - */ - private $_bg_color; - - /** - * Class constructor - * - * @param mixed $size The size of image to create: array(x1,y1,x2,y2) or "letter", "legal", etc. - * @param string $orientation The orientation of the document (either 'landscape' or 'portrait') - * @param DOMPDF $dompdf - * @param float $aa_factor Anti-aliasing factor, 1 for no AA - * @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1 - */ - function __construct($size, $orientation = "portrait", DOMPDF $dompdf, $aa_factor = 1.0, $bg_color = array(1,1,1,0) ) { - - if ( !is_array($size) ) { - $size = strtolower($size); - - if ( isset(CPDF_Adapter::$PAPER_SIZES[$size]) ) { - $size = CPDF_Adapter::$PAPER_SIZES[$size]; - } - else { - $size = CPDF_Adapter::$PAPER_SIZES["letter"]; - } - } - - if ( strtolower($orientation) === "landscape" ) { - list($size[2],$size[3]) = array($size[3],$size[2]); - } - - $this->_dompdf = $dompdf; - - if ( $aa_factor < 1 ) { - $aa_factor = 1; - } - - $this->_aa_factor = $aa_factor; - - $size[2] *= $aa_factor; - $size[3] *= $aa_factor; - - $this->_width = $size[2] - $size[0]; - $this->_height = $size[3] - $size[1]; - - $this->_img = imagecreatetruecolor($this->_width, $this->_height); - - if ( is_null($bg_color) || !is_array($bg_color) ) { - // Pure white bg - $bg_color = array(1,1,1,0); - } - - $this->_bg_color = $this->_allocate_color($bg_color); - imagealphablending($this->_img, true); - imagesavealpha($this->_img, true); - imagefill($this->_img, 0, 0, $this->_bg_color); - - } - - function get_dompdf(){ - return $this->_dompdf; - } - - /** - * Return the GF image resource - * - * @return resource - */ - function get_image() { return $this->_img; } - - /** - * Return the image's width in pixels - * - * @return float - */ - function get_width() { return $this->_width / $this->_aa_factor; } - - /** - * Return the image's height in pixels - * - * @return float - */ - function get_height() { return $this->_height / $this->_aa_factor; } - - /** - * Returns the current page number - * @return int - */ - function get_page_number() { return $this->_page_number; } - - /** - * Returns the total number of pages in the document - * @return int - */ - function get_page_count() { return $this->_page_count; } - - /** - * Sets the current page number - * - * @param int $num - */ - function set_page_number($num) { $this->_page_number = $num; } - - /** - * Sets the page count - * - * @param int $count - */ - function set_page_count($count) { $this->_page_count = $count; } - - /** - * Sets the opacity - * - * @param $opacity - * @param $mode - */ - function set_opacity($opacity, $mode = "Normal") { - // FIXME - } - - /** - * Allocate a new color. Allocate with GD as needed and store - * previously allocated colors in $this->_colors. - * - * @param array $color The new current color - * @return int The allocated color - */ - private function _allocate_color($color) { - - if ( isset($color["c"]) ) { - $color = cmyk_to_rgb($color); - } - - // Full opacity if no alpha set - if ( !isset($color[3]) ) - $color[3] = 0; - - list($r,$g,$b,$a) = $color; - - $r *= 255; - $g *= 255; - $b *= 255; - $a *= 127; - - // Clip values - $r = $r > 255 ? 255 : $r; - $g = $g > 255 ? 255 : $g; - $b = $b > 255 ? 255 : $b; - $a = $a > 127 ? 127 : $a; - - $r = $r < 0 ? 0 : $r; - $g = $g < 0 ? 0 : $g; - $b = $b < 0 ? 0 : $b; - $a = $a < 0 ? 0 : $a; - - $key = sprintf("#%02X%02X%02X%02X", $r, $g, $b, $a); - - if ( isset($this->_colors[$key]) ) - return $this->_colors[$key]; - - if ( $a != 0 ) - $this->_colors[$key] = imagecolorallocatealpha($this->_img, $r, $g, $b, $a); - else - $this->_colors[$key] = imagecolorallocate($this->_img, $r, $g, $b); - - return $this->_colors[$key]; - - } - - /** - * Draws a line from x1,y1 to x2,y2 - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the format of the - * $style parameter (aka dash). - * - * @param float $x1 - * @param float $y1 - * @param float $x2 - * @param float $y2 - * @param array $color - * @param float $width - * @param array $style - */ - function line($x1, $y1, $x2, $y2, $color, $width, $style = null) { - - // Scale by the AA factor - $x1 *= $this->_aa_factor; - $y1 *= $this->_aa_factor; - $x2 *= $this->_aa_factor; - $y2 *= $this->_aa_factor; - $width *= $this->_aa_factor; - - $c = $this->_allocate_color($color); - - // Convert the style array if required - if ( !is_null($style) ) { - $gd_style = array(); - - if ( count($style) == 1 ) { - for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) { - $gd_style[] = $c; - } - - for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) { - $gd_style[] = $this->_bg_color; - } - - } else { - - $i = 0; - foreach ($style as $length) { - - if ( $i % 2 == 0 ) { - // 'On' pattern - for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) - $gd_style[] = $c; - - } else { - // Off pattern - for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) - $gd_style[] = $this->_bg_color; - - } - $i++; - } - } - - imagesetstyle($this->_img, $gd_style); - $c = IMG_COLOR_STYLED; - } - - imagesetthickness($this->_img, $width); - - imageline($this->_img, $x1, $y1, $x2, $y2, $c); - - } - - function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array()) { - // @todo - } - - /** - * Draws a rectangle at x1,y1 with width w and height h - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - * @param float $width - * @param array $style - */ - function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) { - - // Scale by the AA factor - $x1 *= $this->_aa_factor; - $y1 *= $this->_aa_factor; - $w *= $this->_aa_factor; - $h *= $this->_aa_factor; - - $c = $this->_allocate_color($color); - - // Convert the style array if required - if ( !is_null($style) ) { - $gd_style = array(); - - foreach ($style as $length) { - for ($i = 0; $i < $length; $i++) { - $gd_style[] = $c; - } - } - - imagesetstyle($this->_img, $gd_style); - $c = IMG_COLOR_STYLED; - } - - imagesetthickness($this->_img, $width); - - imagerectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c); - - } - - /** - * Draws a filled rectangle at x1,y1 with width w and height h - * - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - * @param array $color - */ - function filled_rectangle($x1, $y1, $w, $h, $color) { - - // Scale by the AA factor - $x1 *= $this->_aa_factor; - $y1 *= $this->_aa_factor; - $w *= $this->_aa_factor; - $h *= $this->_aa_factor; - - $c = $this->_allocate_color($color); - - imagefilledrectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c); - - } - - /** - * Starts a clipping rectangle at x1,y1 with width w and height h - * - * @param float $x1 - * @param float $y1 - * @param float $w - * @param float $h - */ - function clipping_rectangle($x1, $y1, $w, $h) { - // @todo - } - - function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) { - // @todo - } - - /** - * Ends the last clipping shape - */ - function clipping_end() { - // @todo - } - - function save() { - // @todo - } - - function restore() { - // @todo - } - - function rotate($angle, $x, $y) { - // @todo - } - - function skew($angle_x, $angle_y, $x, $y) { - // @todo - } - - function scale($s_x, $s_y, $x, $y) { - // @todo - } - - function translate($t_x, $t_y) { - // @todo - } - - function transform($a, $b, $c, $d, $e, $f) { - // @todo - } - - /** - * Draws a polygon - * - * The polygon is formed by joining all the points stored in the $points - * array. $points has the following structure: - * - * array(0 => x1, - * 1 => y1, - * 2 => x2, - * 3 => y2, - * ... - * ); - * - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param array $points - * @param array $color - * @param float $width - * @param array $style - * @param bool $fill Fills the polygon if true - */ - function polygon($points, $color, $width = null, $style = null, $fill = false) { - - // Scale each point by the AA factor - foreach (array_keys($points) as $i) - $points[$i] *= $this->_aa_factor; - - $c = $this->_allocate_color($color); - - // Convert the style array if required - if ( !is_null($style) && !$fill ) { - $gd_style = array(); - - foreach ($style as $length) { - for ($i = 0; $i < $length; $i++) { - $gd_style[] = $c; - } - } - - imagesetstyle($this->_img, $gd_style); - $c = IMG_COLOR_STYLED; - } - - imagesetthickness($this->_img, $width); - - if ( $fill ) - imagefilledpolygon($this->_img, $points, count($points) / 2, $c); - else - imagepolygon($this->_img, $points, count($points) / 2, $c); - - } - - /** - * Draws a circle at $x,$y with radius $r - * - * See {@link Style::munge_color()} for the format of the color array. - * See {@link Cpdf::setLineStyle()} for a description of the $style - * parameter (aka dash) - * - * @param float $x - * @param float $y - * @param float $r - * @param array $color - * @param float $width - * @param array $style - * @param bool $fill Fills the circle if true - */ - function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) { - - // Scale by the AA factor - $x *= $this->_aa_factor; - $y *= $this->_aa_factor; - $r *= $this->_aa_factor; - - $c = $this->_allocate_color($color); - - // Convert the style array if required - if ( !is_null($style) && !$fill ) { - $gd_style = array(); - - foreach ($style as $length) { - for ($i = 0; $i < $length; $i++) { - $gd_style[] = $c; - } - } - - imagesetstyle($this->_img, $gd_style); - $c = IMG_COLOR_STYLED; - } - - imagesetthickness($this->_img, $width); - - if ( $fill ) - imagefilledellipse($this->_img, $x, $y, $r, $r, $c); - else - imageellipse($this->_img, $x, $y, $r, $r, $c); - - } - - /** - * Add an image to the pdf. - * The image is placed at the specified x and y coordinates with the - * given width and height. - * - * @param string $img_url the path to the image - * @param float $x x position - * @param float $y y position - * @param int $w width (in pixels) - * @param int $h height (in pixels) - * @param string $resolution - * - * @return void - * @internal param string $img_type the type (e.g. extension) of the image - */ - function image($img_url, $x, $y, $w, $h, $resolution = "normal") { - $img_type = Image_Cache::detect_type($img_url, $this->_dompdf->get_http_context()); - $img_ext = Image_Cache::type_to_ext($img_type); - - if ( !$img_ext ) { - return; - } - - $func = "imagecreatefrom$img_ext"; - $src = @$func($img_url); - - if ( !$src ) { - return; // Probably should add to $_dompdf_errors or whatever here - } - - // Scale by the AA factor - $x *= $this->_aa_factor; - $y *= $this->_aa_factor; - - $w *= $this->_aa_factor; - $h *= $this->_aa_factor; - - $img_w = imagesx($src); - $img_h = imagesy($src); - - imagecopyresampled($this->_img, $src, $x, $y, 0, 0, $w, $h, $img_w, $img_h); - - } - - /** - * Writes text at the specified x and y coordinates - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x - * @param float $y - * @param string $text the text to write - * @param string $font the font file to use - * @param float $size the font size, in points - * @param array $color - * @param float $word_spacing word spacing adjustment - * @param float $char_spacing - * @param float $angle Text angle - * - * @return void - */ - function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0) { - - // Scale by the AA factor - $x *= $this->_aa_factor; - $y *= $this->_aa_factor; - $size *= $this->_aa_factor; - - $h = $this->get_font_height($font, $size); - $c = $this->_allocate_color($color); - - $text = mb_encode_numericentity($text, array(0x0080, 0xff, 0, 0xff), 'UTF-8'); - - $font = $this->get_ttf_file($font); - - // FIXME: word spacing - @imagettftext($this->_img, $size, $angle, $x, $y + $h, $c, $font, $text); - - } - - function javascript($code) { - // Not implemented - } - - /** - * Add a named destination (similar to ... in html) - * - * @param string $anchorname The name of the named destination - */ - function add_named_dest($anchorname) { - // Not implemented - } - - /** - * Add a link to the pdf - * - * @param string $url The url to link to - * @param float $x The x position of the link - * @param float $y The y position of the link - * @param float $width The width of the link - * @param float $height The height of the link - */ - function add_link($url, $x, $y, $width, $height) { - // Not implemented - } - - /** - * Add meta information to the PDF - * - * @param string $label label of the value (Creator, Producer, etc.) - * @param string $value the text to set - */ - function add_info($label, $value) { - // N/A - } - - function set_default_view($view, $options = array()) { - // N/A - } - - /** - * Calculates text size, in points - * - * @param string $text the text to be sized - * @param string $font the desired font - * @param float $size the desired font size - * @param float $word_spacing word spacing, if any - * @param float $char_spacing char spacing, if any - * - * @return float - */ - function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) { - $font = $this->get_ttf_file($font); - - $text = mb_encode_numericentity($text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8'); - - // FIXME: word spacing - list($x1,,$x2) = @imagettfbbox($size, 0, $font, $text); - return $x2 - $x1; - } - - function get_ttf_file($font) { - if ( strpos($font, '.ttf') === false ) - $font .= ".ttf"; - - /*$filename = substr(strtolower(basename($font)), 0, -4); - - if ( in_array($filename, DOMPDF::$native_fonts) ) { - return "arial.ttf"; - }*/ - - return $font; - } - - /** - * Calculates font height, in points - * - * @param string $font - * @param float $size - * @return float - */ - function get_font_height($font, $size) { - $font = $this->get_ttf_file($font); - $ratio = $this->_dompdf->get_option("font_height_ratio"); - - // FIXME: word spacing - list(,$y2,,,,$y1) = imagettfbbox($size, 0, $font, "MXjpqytfhl"); // Test string with ascenders, descenders and caps - return ($y2 - $y1) * $ratio; - } - - function get_font_baseline($font, $size) { - $ratio = $this->_dompdf->get_option("font_height_ratio"); - return $this->get_font_height($font, $size) / $ratio; - } - - /** - * Starts a new page - * - * Subsequent drawing operations will appear on the new page. - */ - function new_page() { - $this->_page_number++; - $this->_page_count++; - } - - function open_object(){ - // N/A - } - - function close_object(){ - // N/A - } - - function add_object(){ - // N/A - } - - function page_text(){ - // N/A - } - - /** - * Streams the image directly to the browser - * - * @param string $filename the name of the image file (ignored) - * @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only) - */ - function stream($filename, $options = null) { - - // Perform any antialiasing - if ( $this->_aa_factor != 1 ) { - $dst_w = $this->_width / $this->_aa_factor; - $dst_h = $this->_height / $this->_aa_factor; - $dst = imagecreatetruecolor($dst_w, $dst_h); - imagecopyresampled($dst, $this->_img, 0, 0, 0, 0, - $dst_w, $dst_h, - $this->_width, $this->_height); - } else { - $dst = $this->_img; - } - - if ( !isset($options["type"]) ) - $options["type"] = "png"; - - $type = strtolower($options["type"]); - - header("Cache-Control: private"); - - switch ($type) { - - case "jpg": - case "jpeg": - if ( !isset($options["quality"]) ) - $options["quality"] = 75; - - header("Content-type: image/jpeg"); - imagejpeg($dst, '', $options["quality"]); - break; - - case "png": - default: - header("Content-type: image/png"); - imagepng($dst); - break; - } - - if ( $this->_aa_factor != 1 ) - imagedestroy($dst); - } - - /** - * Returns the PNG as a string - * - * @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only) - * @return string - */ - function output($options = null) { - - if ( $this->_aa_factor != 1 ) { - $dst_w = $this->_width / $this->_aa_factor; - $dst_h = $this->_height / $this->_aa_factor; - $dst = imagecreatetruecolor($dst_w, $dst_h); - imagecopyresampled($dst, $this->_img, 0, 0, 0, 0, - $dst_w, $dst_h, - $this->_width, $this->_height); - } else { - $dst = $this->_img; - } - - if ( !isset($options["type"]) ) - $options["type"] = "png"; - - $type = $options["type"]; - - ob_start(); - - switch ($type) { - - case "jpg": - case "jpeg": - if ( !isset($options["quality"]) ) - $options["quality"] = 75; - - imagejpeg($dst, '', $options["quality"]); - break; - - case "png": - default: - imagepng($dst); - break; - } - - $image = ob_get_clean(); - - if ( $this->_aa_factor != 1 ) - imagedestroy($dst); - - return $image; - } - - -} diff --git a/library/vendor/dompdf/include/image_cache.cls.php b/library/vendor/dompdf/include/image_cache.cls.php deleted file mode 100644 index e7175c4dc..000000000 --- a/library/vendor/dompdf/include/image_cache.cls.php +++ /dev/null @@ -1,185 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Static class that resolves image urls and downloads and caches - * remote images if required. - * - * @access private - * @package dompdf - */ -class Image_Cache { - - /** - * Array of downloaded images. Cached so that identical images are - * not needlessly downloaded. - * - * @var array - */ - static protected $_cache = array(); - - /** - * The url to the "broken image" used when images can't be loade - * - * @var string - */ - public static $broken_image; - - /** - * Resolve and fetch an image for use. - * - * @param string $url The url of the image - * @param string $protocol Default protocol if none specified in $url - * @param string $host Default host if none specified in $url - * @param string $base_path Default path if none specified in $url - * @param DOMPDF $dompdf The DOMPDF instance - * - * @throws DOMPDF_Image_Exception - * @return array An array with two elements: The local path to the image and the image extension - */ - static function resolve_url($url, $protocol, $host, $base_path, DOMPDF $dompdf) { - $protocol = mb_strtolower($protocol); - $parsed_url = explode_url($url); - $message = null; - - $remote = ($protocol && $protocol !== "file://") || ($parsed_url['protocol'] != ""); - - $data_uri = strpos($parsed_url['protocol'], "data:") === 0; - $full_url = null; - $enable_remote = $dompdf->get_option("enable_remote"); - - try { - - // Remote not allowed and is not DataURI - if ( !$enable_remote && $remote && !$data_uri ) { - throw new DOMPDF_Image_Exception("DOMPDF_ENABLE_REMOTE is set to FALSE"); - } - - // Remote allowed or DataURI - else if ( $enable_remote && $remote || $data_uri ) { - // Download remote files to a temporary directory - $full_url = build_url($protocol, $host, $base_path, $url); - - // From cache - if ( isset(self::$_cache[$full_url]) ) { - $resolved_url = self::$_cache[$full_url]; - } - - // From remote - else { - $tmp_dir = $dompdf->get_option("temp_dir"); - $resolved_url = tempnam($tmp_dir, "ca_dompdf_img_"); - $image = ""; - - if ($data_uri) { - if ($parsed_data_uri = parse_data_uri($url)) { - $image = $parsed_data_uri['data']; - } - } - else { - set_error_handler("record_warnings"); - $image = file_get_contents($full_url, null, $dompdf->get_http_context()); - restore_error_handler(); - } - - // Image not found or invalid - if ( strlen($image) == 0 ) { - $msg = ($data_uri ? "Data-URI could not be parsed" : "Image not found"); - throw new DOMPDF_Image_Exception($msg); - } - - // Image found, put in cache and process - else { - //e.g. fetch.php?media=url.jpg&cache=1 - //- Image file name might be one of the dynamic parts of the url, don't strip off! - //- a remote url does not need to have a file extension at all - //- local cached file does not have a matching file extension - //Therefore get image type from the content - file_put_contents($resolved_url, $image); - } - } - } - - // Not remote, local image - else { - $resolved_url = build_url($protocol, $host, $base_path, $url); - } - - // Check if the local file is readable - if ( !is_readable($resolved_url) || !filesize($resolved_url) ) { - throw new DOMPDF_Image_Exception("Image not readable or empty"); - } - - // Check is the file is an image - else { - list($width, $height, $type) = dompdf_getimagesize($resolved_url, $dompdf->get_http_context()); - - // Known image type - if ( $width && $height && in_array($type, array(IMAGETYPE_GIF, IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_BMP)) ) { - //Don't put replacement image into cache - otherwise it will be deleted on cache cleanup. - //Only execute on successful caching of remote image. - if ( $enable_remote && $remote || $data_uri ) { - self::$_cache[$full_url] = $resolved_url; - } - } - - // Unknown image type - else { - throw new DOMPDF_Image_Exception("Image type unknown"); - } - } - } - catch(DOMPDF_Image_Exception $e) { - $resolved_url = self::$broken_image; - $type = IMAGETYPE_PNG; - $message = "Image not found or type unknown"; - $_dompdf_warnings[] = $e->getMessage()." :: $url"; - } - - return array($resolved_url, $type, $message); - } - - /** - * Unlink all cached images (i.e. temporary images either downloaded - * or converted) - */ - static function clear() { - if ( empty(self::$_cache) || DEBUGKEEPTEMP ) return; - - foreach ( self::$_cache as $file ) { - if (DEBUGPNG) print "[clear unlink $file]"; - unlink($file); - } - - self::$_cache = array(); - } - - static function detect_type($file, $context = null) { - list(, , $type) = dompdf_getimagesize($file, $context); - return $type; - } - - static function type_to_ext($type) { - $image_types = array( - IMAGETYPE_GIF => "gif", - IMAGETYPE_PNG => "png", - IMAGETYPE_JPEG => "jpeg", - IMAGETYPE_BMP => "bmp", - ); - - return (isset($image_types[$type]) ? $image_types[$type] : null); - } - - static function is_broken($url) { - return $url === self::$broken_image; - } -} - -Image_Cache::$broken_image = DOMPDF_LIB_DIR . "/res/broken_image.png"; diff --git a/library/vendor/dompdf/include/image_frame_decorator.cls.php b/library/vendor/dompdf/include/image_frame_decorator.cls.php deleted file mode 100644 index b5a7983aa..000000000 --- a/library/vendor/dompdf/include/image_frame_decorator.cls.php +++ /dev/null @@ -1,80 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Decorates frames for image layout and rendering - * - * @access private - * @package dompdf - */ -class Image_Frame_Decorator extends Frame_Decorator { - - /** - * The path to the image file (note that remote images are - * downloaded locally to DOMPDF_TEMP_DIR). - * - * @var string - */ - protected $_image_url; - - /** - * The image's file error message - * - * @var string - */ - protected $_image_msg; - - /** - * Class constructor - * - * @param Frame $frame the frame to decorate - * @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls) - */ - function __construct(Frame $frame, DOMPDF $dompdf) { - parent::__construct($frame, $dompdf); - $url = $frame->get_node()->getAttribute("src"); - - $debug_png = $dompdf->get_option("debug_png"); - if ($debug_png) print '[__construct '.$url.']'; - - list($this->_image_url, /*$type*/, $this->_image_msg) = Image_Cache::resolve_url( - $url, - $dompdf->get_protocol(), - $dompdf->get_host(), - $dompdf->get_base_path(), - $dompdf - ); - - if ( Image_Cache::is_broken($this->_image_url) && - $alt = $frame->get_node()->getAttribute("alt") ) { - $style = $frame->get_style(); - $style->width = (4/3)*Font_Metrics::get_text_width($alt, $style->font_family, $style->font_size, $style->word_spacing); - $style->height = Font_Metrics::get_font_height($style->font_family, $style->font_size); - } - } - - /** - * Return the image's url - * - * @return string The url of this image - */ - function get_image_url() { - return $this->_image_url; - } - - /** - * Return the image's error message - * - * @return string The image's error message - */ - function get_image_msg() { - return $this->_image_msg; - } - -} diff --git a/library/vendor/dompdf/include/image_frame_reflower.cls.php b/library/vendor/dompdf/include/image_frame_reflower.cls.php deleted file mode 100644 index c938bd075..000000000 --- a/library/vendor/dompdf/include/image_frame_reflower.cls.php +++ /dev/null @@ -1,186 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Image reflower class - * - * @access private - * @package dompdf - */ -class Image_Frame_Reflower extends Frame_Reflower { - - function __construct(Image_Frame_Decorator $frame) { - parent::__construct($frame); - } - - function reflow(Block_Frame_Decorator $block = null) { - $this->_frame->position(); - - //FLOAT - //$frame = $this->_frame; - //$page = $frame->get_root(); - - //$enable_css_float = $this->get_dompdf()->get_option("enable_css_float"); - //if ($enable_css_float && $frame->get_style()->float !== "none" ) { - // $page->add_floating_frame($this); - //} - // Set the frame's width - $this->get_min_max_width(); - - if ( $block ) { - $block->add_frame_to_line($this->_frame); - } - } - - function get_min_max_width() { - if (DEBUGPNG) { - // Determine the image's size. Time consuming. Only when really needed? - list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url(), $this->get_dompdf()->get_http_context()); - print "get_min_max_width() ". - $this->_frame->get_style()->width.' '. - $this->_frame->get_style()->height.';'. - $this->_frame->get_parent()->get_style()->width." ". - $this->_frame->get_parent()->get_style()->height.";". - $this->_frame->get_parent()->get_parent()->get_style()->width.' '. - $this->_frame->get_parent()->get_parent()->get_style()->height.';'. - $img_width. ' '. - $img_height.'|' ; - } - - $style = $this->_frame->get_style(); - - $width_forced = true; - $height_forced = true; - - //own style auto or invalid value: use natural size in px - //own style value: ignore suffix text including unit, use given number as px - //own style %: walk up parent chain until found available space in pt; fill available space - // - //special ignored unit: e.g. 10ex: e treated as exponent; x ignored; 10e completely invalid ->like auto - - $width = ($style->width > 0 ? $style->width : 0); - if ( is_percent($width) ) { - $t = 0.0; - for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) { - $f_style = $f->get_style(); - $t = $f_style->length_in_pt($f_style->width); - if ($t != 0) { - break; - } - } - $width = ((float)rtrim($width,"%") * $t)/100; //maybe 0 - } elseif ( !mb_strpos($width, 'pt') ) { - // Don't set image original size if "%" branch was 0 or size not given. - // Otherwise aspect changed on %/auto combination for width/height - // Resample according to px per inch - // See also List_Bullet_Image_Frame_Decorator::__construct - $width = $style->length_in_pt($width); - } - - $height = ($style->height > 0 ? $style->height : 0); - if ( is_percent($height) ) { - $t = 0.0; - for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) { - $f_style = $f->get_style(); - $t = $f_style->length_in_pt($f_style->height); - if ($t != 0) { - break; - } - } - $height = ((float)rtrim($height,"%") * $t)/100; //maybe 0 - } elseif ( !mb_strpos($height, 'pt') ) { - // Don't set image original size if "%" branch was 0 or size not given. - // Otherwise aspect changed on %/auto combination for width/height - // Resample according to px per inch - // See also List_Bullet_Image_Frame_Decorator::__construct - $height = $style->length_in_pt($height); - } - - if ($width == 0 || $height == 0) { - // Determine the image's size. Time consuming. Only when really needed! - list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url(), $this->get_dompdf()->get_http_context()); - - // don't treat 0 as error. Can be downscaled or can be catched elsewhere if image not readable. - // Resample according to px per inch - // See also List_Bullet_Image_Frame_Decorator::__construct - if ($width == 0 && $height == 0) { - $dpi = $this->_frame->get_dompdf()->get_option("dpi"); - $width = (float)($img_width * 72) / $dpi; - $height = (float)($img_height * 72) / $dpi; - $width_forced = false; - $height_forced = false; - } elseif ($height == 0 && $width != 0) { - $height_forced = false; - $height = ($width / $img_width) * $img_height; //keep aspect ratio - } elseif ($width == 0 && $height != 0) { - $width_forced = false; - $width = ($height / $img_height) * $img_width; //keep aspect ratio - } - } - - // Handle min/max width/height - if ( $style->min_width !== "none" || - $style->max_width !== "none" || - $style->min_height !== "none" || - $style->max_height !== "none" ) { - - list(/*$x*/, /*$y*/, $w, $h) = $this->_frame->get_containing_block(); - - $min_width = $style->length_in_pt($style->min_width, $w); - $max_width = $style->length_in_pt($style->max_width, $w); - $min_height = $style->length_in_pt($style->min_height, $h); - $max_height = $style->length_in_pt($style->max_height, $h); - - if ( $max_width !== "none" && $width > $max_width ) { - if ( !$height_forced ) { - $height *= $max_width / $width; - } - - $width = $max_width; - } - - if ( $min_width !== "none" && $width < $min_width ) { - if ( !$height_forced ) { - $height *= $min_width / $width; - } - - $width = $min_width; - } - - if ( $max_height !== "none" && $height > $max_height ) { - if ( !$width_forced ) { - $width *= $max_height / $height; - } - - $height = $max_height; - } - - if ( $min_height !== "none" && $height < $min_height ) { - if ( !$width_forced ) { - $width *= $min_height / $height; - } - - $height = $min_height; - } - } - - if (DEBUGPNG) print $width.' '.$height.';'; - - $style->width = $width . "pt"; - $style->height = $height . "pt"; - - $style->min_width = "none"; - $style->max_width = "none"; - $style->min_height = "none"; - $style->max_height = "none"; - - return array( $width, $width, "min" => $width, "max" => $width); - - } -} diff --git a/library/vendor/dompdf/include/image_renderer.cls.php b/library/vendor/dompdf/include/image_renderer.cls.php deleted file mode 100644 index 561b70153..000000000 --- a/library/vendor/dompdf/include/image_renderer.cls.php +++ /dev/null @@ -1,119 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Image renderer - * - * @access private - * @package dompdf - */ -class Image_Renderer extends Block_Renderer { - - function render(Frame $frame) { - // Render background & borders - $style = $frame->get_style(); - $cb = $frame->get_containing_block(); - list($x, $y, $w, $h) = $frame->get_border_box(); - - $this->_set_opacity( $frame->get_opacity( $style->opacity ) ); - - list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); - - $has_border_radius = $tl + $tr + $br + $bl > 0; - - if ( $has_border_radius ) { - $this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl ); - } - - if ( ($bg = $style->background_color) !== "transparent" ) { - $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg); - } - - if ( ($url = $style->background_image) && $url !== "none" ) { - $this->_background_image($url, $x, $y, $w, $h, $style); - } - - if ( $has_border_radius ) { - $this->_canvas->clipping_end(); - } - - $this->_render_border($frame); - $this->_render_outline($frame); - - list($x, $y) = $frame->get_padding_box(); - - $x += $style->length_in_pt($style->padding_left, $cb["w"]); - $y += $style->length_in_pt($style->padding_top, $cb["h"]); - - $w = $style->length_in_pt($style->width, $cb["w"]); - $h = $style->length_in_pt($style->height, $cb["h"]); - - if ( $has_border_radius ) { - list($wt, $wr, $wb, $wl) = array( - $style->border_top_width, - $style->border_right_width, - $style->border_bottom_width, - $style->border_left_width, - ); - - // we have to get the "inner" radius - if ( $tl > 0 ) { - $tl -= ($wt + $wl) / 2; - } - if ( $tr > 0 ) { - $tr -= ($wt + $wr) / 2; - } - if ( $br > 0 ) { - $br -= ($wb + $wr) / 2; - } - if ( $bl > 0 ) { - $bl -= ($wb + $wl) / 2; - } - - $this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl ); - } - - $src = $frame->get_image_url(); - $alt = null; - - if ( Image_Cache::is_broken($src) && - $alt = $frame->get_node()->getAttribute("alt") ) { - $font = $style->font_family; - $size = $style->font_size; - $spacing = $style->word_spacing; - $this->_canvas->text($x, $y, $alt, - $font, $size, - $style->color, $spacing); - } - else { - $this->_canvas->image( $src, $x, $y, $w, $h, $style->image_resolution); - } - - if ( $has_border_radius ) { - $this->_canvas->clipping_end(); - } - - if ( $msg = $frame->get_image_msg() ) { - $parts = preg_split("/\s*\n\s*/", $msg); - $height = 10; - $_y = $alt ? $y+$h-count($parts)*$height : $y; - - foreach($parts as $i => $_part) { - $this->_canvas->text($x, $_y + $i*$height, $_part, "times", $height*0.8, array(0.5, 0.5, 0.5)); - } - } - - if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) { - $this->_debug_layout($frame->get_border_box(), "blue"); - if (DEBUG_LAYOUT_PADDINGBOX) { - $this->_debug_layout($frame->get_padding_box(), "blue", array(0.5, 0.5)); - } - } - } -} diff --git a/library/vendor/dompdf/include/inline_frame_decorator.cls.php b/library/vendor/dompdf/include/inline_frame_decorator.cls.php deleted file mode 100644 index ce79bab08..000000000 --- a/library/vendor/dompdf/include/inline_frame_decorator.cls.php +++ /dev/null @@ -1,74 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Decorates frames for inline layout - * - * @access private - * @package dompdf - */ -class Inline_Frame_Decorator extends Frame_Decorator { - - function __construct(Frame $frame, DOMPDF $dompdf) { parent::__construct($frame, $dompdf); } - - function split(Frame $frame = null, $force_pagebreak = false) { - - if ( is_null($frame) ) { - $this->get_parent()->split($this, $force_pagebreak); - return; - } - - if ( $frame->get_parent() !== $this ) - throw new DOMPDF_Exception("Unable to split: frame is not a child of this one."); - - $split = $this->copy( $this->_frame->get_node()->cloneNode() ); - $this->get_parent()->insert_child_after($split, $this); - - // Unset the current node's right style properties - $style = $this->_frame->get_style(); - $style->margin_right = 0; - $style->padding_right = 0; - $style->border_right_width = 0; - - // Unset the split node's left style properties since we don't want them - // to propagate - $style = $split->get_style(); - $style->margin_left = 0; - $style->padding_left = 0; - $style->border_left_width = 0; - - //On continuation of inline element on next line, - //don't repeat non-vertically repeatble background images - //See e.g. in testcase image_variants, long desriptions - if ( ($url = $style->background_image) && $url !== "none" - && ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-y" - ) { - $style->background_image = "none"; - } - - // Add $frame and all following siblings to the new split node - $iter = $frame; - while ($iter) { - $frame = $iter; - $iter = $iter->get_next_sibling(); - $frame->reset(); - $split->append_child($frame); - } - - $page_breaks = array("always", "left", "right"); - $frame_style = $frame->get_style(); - if( $force_pagebreak || - in_array($frame_style->page_break_before, $page_breaks) || - in_array($frame_style->page_break_after, $page_breaks) ) { - - $this->get_parent()->split($split, true); - } - } - -} diff --git a/library/vendor/dompdf/include/inline_frame_reflower.cls.php b/library/vendor/dompdf/include/inline_frame_reflower.cls.php deleted file mode 100644 index 049b8e586..000000000 --- a/library/vendor/dompdf/include/inline_frame_reflower.cls.php +++ /dev/null @@ -1,66 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Reflows inline frames - * - * @access private - * @package dompdf - */ -class Inline_Frame_Reflower extends Frame_Reflower { - - function __construct(Frame $frame) { parent::__construct($frame); } - - //........................................................................ - - function reflow(Block_Frame_Decorator $block = null) { - $frame = $this->_frame; - - // Check if a page break is forced - $page = $frame->get_root(); - $page->check_forced_page_break($frame); - - if ( $page->is_full() ) - return; - - $style = $frame->get_style(); - - // Generated content - $this->_set_content(); - - $frame->position(); - - $cb = $frame->get_containing_block(); - - // Add our margin, padding & border to the first and last children - if ( ($f = $frame->get_first_child()) && $f instanceof Text_Frame_Decorator ) { - $f_style = $f->get_style(); - $f_style->margin_left = $style->margin_left; - $f_style->padding_left = $style->padding_left; - $f_style->border_left = $style->border_left; - } - - if ( ($l = $frame->get_last_child()) && $l instanceof Text_Frame_Decorator ) { - $l_style = $l->get_style(); - $l_style->margin_right = $style->margin_right; - $l_style->padding_right = $style->padding_right; - $l_style->border_right = $style->border_right; - } - - if ( $block ) { - $block->add_frame_to_line($this->_frame); - } - - // Set the containing blocks and reflow each child. The containing - // block is not changed by line boxes. - foreach ( $frame->get_children() as $child ) { - $child->set_containing_block($cb); - $child->reflow($block); - } - } -} diff --git a/library/vendor/dompdf/include/inline_positioner.cls.php b/library/vendor/dompdf/include/inline_positioner.cls.php deleted file mode 100644 index 1694ce8e2..000000000 --- a/library/vendor/dompdf/include/inline_positioner.cls.php +++ /dev/null @@ -1,70 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Positions inline frames - * - * @access private - * @package dompdf - */ -class Inline_Positioner extends Positioner { - - function __construct(Frame_Decorator $frame) { parent::__construct($frame); } - - //........................................................................ - - function position() { - /** - * Find our nearest block level parent and access its lines property. - * @var Block_Frame_Decorator - */ - $p = $this->_frame->find_block_parent(); - - // Debugging code: - -// pre_r("\nPositioning:"); -// pre_r("Me: " . $this->_frame->get_node()->nodeName . " (" . spl_object_hash($this->_frame->get_node()) . ")"); -// pre_r("Parent: " . $p->get_node()->nodeName . " (" . spl_object_hash($p->get_node()) . ")"); - - // End debugging - - if ( !$p ) - throw new DOMPDF_Exception("No block-level parent found. Not good."); - - $f = $this->_frame; - - $cb = $f->get_containing_block(); - $line = $p->get_current_line_box(); - - // Skip the page break if in a fixed position element - $is_fixed = false; - while($f = $f->get_parent()) { - if($f->get_style()->position === "fixed") { - $is_fixed = true; - break; - } - } - - $f = $this->_frame; - - if ( !$is_fixed && $f->get_parent() && - $f->get_parent() instanceof Inline_Frame_Decorator && - $f->is_text_node() ) { - - $min_max = $f->get_reflower()->get_min_max_width(); - - // If the frame doesn't fit in the current line, a line break occurs - if ( $min_max["min"] > ($cb["w"] - $line->left - $line->w - $line->right) ) { - $p->add_line(); - } - } - - $f->set_position($cb["x"] + $line->w, $line->y); - - } -} diff --git a/library/vendor/dompdf/include/inline_renderer.cls.php b/library/vendor/dompdf/include/inline_renderer.cls.php deleted file mode 100644 index 7a8ff51c3..000000000 --- a/library/vendor/dompdf/include/inline_renderer.cls.php +++ /dev/null @@ -1,190 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Renders inline frames - * - * @access private - * @package dompdf - */ -class Inline_Renderer extends Abstract_Renderer { - - //........................................................................ - - function render(Frame $frame) { - $style = $frame->get_style(); - - if ( !$frame->get_first_child() ) - return; // No children, no service - - // Draw the left border if applicable - $bp = $style->get_border_properties(); - $widths = array($style->length_in_pt($bp["top"]["width"]), - $style->length_in_pt($bp["right"]["width"]), - $style->length_in_pt($bp["bottom"]["width"]), - $style->length_in_pt($bp["left"]["width"])); - - // Draw the background & border behind each child. To do this we need - // to figure out just how much space each child takes: - list($x, $y) = $frame->get_first_child()->get_position(); - $w = null; - $h = 0; -// $x += $widths[3]; -// $y += $widths[0]; - - $this->_set_opacity( $frame->get_opacity( $style->opacity ) ); - - $first_row = true; - - foreach ($frame->get_children() as $child) { - list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box(); - - if ( !is_null($w) && $child_x < $x + $w ) { - //This branch seems to be supposed to being called on the first part - //of an inline html element, and the part after the if clause for the - //parts after a line break. - //But because $w initially mostly is 0, and gets updated only on the next - //round, this seem to be never executed and the common close always. - - // The next child is on another line. Draw the background & - // borders on this line. - - // Background: - if ( ($bg = $style->background_color) !== "transparent" ) - $this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg); - - if ( ($url = $style->background_image) && $url !== "none" ) { - $this->_background_image($url, $x, $y, $w, $h, $style); - } - - // If this is the first row, draw the left border - if ( $first_row ) { - - if ( $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $bp["left"]["width"] > 0 ) { - $method = "_border_" . $bp["left"]["style"]; - $this->$method($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left"); - } - $first_row = false; - } - - // Draw the top & bottom borders - if ( $bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $bp["top"]["width"] > 0 ) { - $method = "_border_" . $bp["top"]["style"]; - $this->$method($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top"); - } - - if ( $bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $bp["bottom"]["width"] > 0 ) { - $method = "_border_" . $bp["bottom"]["style"]; - $this->$method($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom"); - } - - // Handle anchors & links - $link_node = null; - if ( $frame->get_node()->nodeName === "a" ) { - $link_node = $frame->get_node(); - } - else if ( $frame->get_parent()->get_node()->nodeName === "a" ){ - $link_node = $frame->get_parent()->get_node(); - } - - if ( $link_node && $href = $link_node->getAttribute("href") ) { - $this->_canvas->add_link($href, $x, $y, $w, $h); - } - - $x = $child_x; - $y = $child_y; - $w = $child_w; - $h = $child_h; - continue; - } - - if ( is_null($w) ) - $w = $child_w; - else - $w += $child_w; - - $h = max($h, $child_h); - - if (DEBUG_LAYOUT && DEBUG_LAYOUT_INLINE) { - $this->_debug_layout($child->get_border_box(), "blue"); - if (DEBUG_LAYOUT_PADDINGBOX) { - $this->_debug_layout($child->get_padding_box(), "blue", array(0.5, 0.5)); - } - } - } - - - // Handle the last child - if ( ($bg = $style->background_color) !== "transparent" ) - $this->_canvas->filled_rectangle( $x + $widths[3], $y + $widths[0], $w, $h, $bg); - - //On continuation lines (after line break) of inline elements, the style got copied. - //But a non repeatable background image should not be repeated on the next line. - //But removing the background image above has never an effect, and removing it below - //removes it always, even on the initial line. - //Need to handle it elsewhere, e.g. on certain ...clone()... usages. - // Repeat not given: default is Style::__construct - // ... && (!($repeat = $style->background_repeat) || $repeat === "repeat" ... - //different position? $this->_background_image($url, $x, $y, $w, $h, $style); - if ( ($url = $style->background_image) && $url !== "none" ) - $this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style); - - // Add the border widths - $w += $widths[1] + $widths[3]; - $h += $widths[0] + $widths[2]; - - // make sure the border and background start inside the left margin - $left_margin = $style->length_in_pt($style->margin_left); - $x += $left_margin; - - // If this is the first row, draw the left border too - if ( $first_row && $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $widths[3] > 0 ) { - $method = "_border_" . $bp["left"]["style"]; - $this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left"); - } - - // Draw the top & bottom borders - if ( $bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $widths[0] > 0 ) { - $method = "_border_" . $bp["top"]["style"]; - $this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top"); - } - - if ( $bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $widths[2] > 0 ) { - $method = "_border_" . $bp["bottom"]["style"]; - $this->$method($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom"); - } - - // pre_var_dump(get_class($frame->get_next_sibling())); - // $last_row = get_class($frame->get_next_sibling()) !== 'Inline_Frame_Decorator'; - // Draw the right border if this is the last row - if ( $bp["right"]["style"] !== "none" && $bp["right"]["color"] !== "transparent" && $widths[1] > 0 ) { - $method = "_border_" . $bp["right"]["style"]; - $this->$method($x + $w, $y, $h, $bp["right"]["color"], $widths, "right"); - } - - // Only two levels of links frames - $link_node = null; - if ( $frame->get_node()->nodeName === "a" ) { - $link_node = $frame->get_node(); - - if ( ($name = $link_node->getAttribute("name")) || ($name = $link_node->getAttribute("id")) ) { - $this->_canvas->add_named_dest($name); - } - } - - if ( $frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a" ){ - $link_node = $frame->get_parent()->get_node(); - } - - // Handle anchors & links - if ( $link_node ) { - if ( $href = $link_node->getAttribute("href") ) - $this->_canvas->add_link($href, $x, $y, $w, $h); - } - } -} diff --git a/library/vendor/dompdf/include/javascript_embedder.cls.php b/library/vendor/dompdf/include/javascript_embedder.cls.php deleted file mode 100644 index 92c244b2d..000000000 --- a/library/vendor/dompdf/include/javascript_embedder.cls.php +++ /dev/null @@ -1,37 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Embeds Javascript into the PDF document - * - * @access private - * @package dompdf - */ -class Javascript_Embedder { - - /** - * @var DOMPDF - */ - protected $_dompdf; - - function __construct(DOMPDF $dompdf) { - $this->_dompdf = $dompdf; - } - - function insert($script) { - $this->_dompdf->get_canvas()->javascript($script); - } - - function render(Frame $frame) { - if ( !$this->_dompdf->get_option("enable_javascript") ) { - return; - } - - $this->insert($frame->get_node()->nodeValue); - } -} diff --git a/library/vendor/dompdf/include/line_box.cls.php b/library/vendor/dompdf/include/line_box.cls.php deleted file mode 100644 index 352359729..000000000 --- a/library/vendor/dompdf/include/line_box.cls.php +++ /dev/null @@ -1,252 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * The line box class - * - * This class represents a line box - * http://www.w3.org/TR/CSS2/visuren.html#line-box - * - * @access protected - * @package dompdf - */ -class Line_Box { - - /** - * @var Block_Frame_Decorator - */ - protected $_block_frame; - - /** - * @var Frame[] - */ - protected $_frames = array(); - - /** - * @var integer - */ - public $wc = 0; - - /** - * @var float - */ - public $y = null; - - /** - * @var float - */ - public $w = 0.0; - - /** - * @var float - */ - public $h = 0.0; - - /** - * @var float - */ - public $left = 0.0; - - /** - * @var float - */ - public $right = 0.0; - - /** - * @var Frame - */ - public $tallest_frame = null; - - /** - * @var bool[] - */ - public $floating_blocks = array(); - - /** - * @var bool - */ - public $br = false; - - /** - * Class constructor - * - * @param Block_Frame_Decorator $frame the Block_Frame_Decorator containing this line - */ - function __construct(Block_Frame_Decorator $frame, $y = 0) { - $this->_block_frame = $frame; - $this->_frames = array(); - $this->y = $y; - - $this->get_float_offsets(); - } - - /** - * Returns the floating elements inside the first floating parent - * - * @param Page_Frame_Decorator $root - * - * @return Frame[] - */ - function get_floats_inside(Page_Frame_Decorator $root) { - $floating_frames = $root->get_floating_frames(); - - if ( count($floating_frames) == 0 ) { - return $floating_frames; - } - - // Find nearest floating element - $p = $this->_block_frame; - while( $p->get_style()->float === "none" ) { - $parent = $p->get_parent(); - - if ( !$parent ) { - break; - } - - $p = $parent; - } - - if ( $p == $root ) { - return $floating_frames; - } - - $parent = $p; - - $childs = array(); - - foreach ($floating_frames as $_floating) { - $p = $_floating->get_parent(); - - while (($p = $p->get_parent()) && $p !== $parent); - - if ( $p ) { - $childs[] = $p; - } - } - - return $childs; - } - - function get_float_offsets() { - $enable_css_float = $this->_block_frame->get_dompdf()->get_option("enable_css_float"); - if ( !$enable_css_float ) { - return; - } - - static $anti_infinite_loop = 500; // FIXME smelly hack - - $reflower = $this->_block_frame->get_reflower(); - - if ( !$reflower ) { - return; - } - - $cb_w = null; - - $block = $this->_block_frame; - $root = $block->get_root(); - - if ( !$root ) { - return; - } - - $floating_frames = $this->get_floats_inside($root); - - foreach ( $floating_frames as $child_key => $floating_frame ) { - $id = $floating_frame->get_id(); - - if ( isset($this->floating_blocks[$id]) ) { - continue; - } - - $floating_style = $floating_frame->get_style(); - $float = $floating_style->float; - - $floating_width = $floating_frame->get_margin_width(); - - if (!$cb_w) { - $cb_w = $floating_frame->get_containing_block("w"); - } - - $line_w = $this->get_width(); - - if ( !$floating_frame->_float_next_line && ($cb_w <= $line_w + $floating_width) && ($cb_w > $line_w) ) { - $floating_frame->_float_next_line = true; - continue; - } - - // If the child is still shifted by the floating element - if ( $anti_infinite_loop-- > 0 && - $floating_frame->get_position("y") + $floating_frame->get_margin_height() > $this->y && - $block->get_position("x") + $block->get_margin_width() > $floating_frame->get_position("x") - ) { - if ( $float === "left" ) - $this->left += $floating_width; - else - $this->right += $floating_width; - - $this->floating_blocks[$id] = true; - } - - // else, the floating element won't shift anymore - else { - $root->remove_floating_frame($child_key); - } - } - } - - /** - * @return float - */ - function get_width(){ - return $this->left + $this->w + $this->right; - } - - /** - * @return Block_Frame_Decorator - */ - function get_block_frame() { - return $this->_block_frame; - } - - /** - * @return Frame[] - */ - function &get_frames() { - return $this->_frames; - } - - /** - * @param Frame $frame - */ - function add_frame(Frame $frame) { - $this->_frames[] = $frame; - } - - function __toString(){ - $props = array("wc", "y", "w", "h", "left", "right", "br"); - $s = ""; - foreach($props as $prop) { - $s .= "$prop: ".$this->$prop."\n"; - } - $s .= count($this->_frames)." frames\n"; - return $s; - } - /*function __get($prop) { - if (!isset($this->{"_$prop"})) return; - return $this->{"_$prop"}; - }*/ -} - -/* -class LineBoxList implements Iterator { - private $_p = 0; - private $_lines = array(); - -} -*/ diff --git a/library/vendor/dompdf/include/list_bullet_frame_decorator.cls.php b/library/vendor/dompdf/include/list_bullet_frame_decorator.cls.php deleted file mode 100644 index a661dc050..000000000 --- a/library/vendor/dompdf/include/list_bullet_frame_decorator.cls.php +++ /dev/null @@ -1,65 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Decorates frames for list bullet rendering - * - * @access private - * @package dompdf - */ -class List_Bullet_Frame_Decorator extends Frame_Decorator { - - const BULLET_PADDING = 1; // Distance from bullet to text in pt - // As fraction of font size (including descent). See also DECO_THICKNESS. - const BULLET_THICKNESS = 0.04; // Thickness of bullet outline. Screen: 0.08, print: better less, e.g. 0.04 - const BULLET_DESCENT = 0.3; //descent of font below baseline. Todo: Guessed for now. - const BULLET_SIZE = 0.35; // bullet diameter. For now 0.5 of font_size without descent. - - static $BULLET_TYPES = array("disc", "circle", "square"); - - //........................................................................ - - function __construct(Frame $frame, DOMPDF $dompdf) { - parent::__construct($frame, $dompdf); - } - - function get_margin_width() { - $style = $this->_frame->get_style(); - - // Small hack to prevent extra indenting of list text on list_style_position === "inside" - // and on suppressed bullet - if ( $style->list_style_position === "outside" || - $style->list_style_type === "none" ) { - return 0; - } - - return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING; - } - - //hits only on "inset" lists items, to increase height of box - function get_margin_height() { - $style = $this->_frame->get_style(); - - if ( $style->list_style_type === "none" ) { - return 0; - } - - return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING; - } - - function get_width() { - return $this->get_margin_height(); - } - - function get_height() { - return $this->get_margin_height(); - } - - //........................................................................ -} diff --git a/library/vendor/dompdf/include/list_bullet_frame_reflower.cls.php b/library/vendor/dompdf/include/list_bullet_frame_reflower.cls.php deleted file mode 100644 index 283056f00..000000000 --- a/library/vendor/dompdf/include/list_bullet_frame_reflower.cls.php +++ /dev/null @@ -1,33 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Reflows list bullets - * - * @access private - * @package dompdf - */ -class List_Bullet_Frame_Reflower extends Frame_Reflower { - - function __construct(Frame_Decorator $frame) { parent::__construct($frame); } - - //........................................................................ - - function reflow(Block_Frame_Decorator $block = null) { - $style = $this->_frame->get_style(); - - $style->width = $this->_frame->get_width(); - $this->_frame->position(); - - if ( $style->list_style_position === "inside" ) { - $p = $this->_frame->find_block_parent(); - $p->add_frame_to_line($this->_frame); - } - - } -} diff --git a/library/vendor/dompdf/include/list_bullet_image_frame_decorator.cls.php b/library/vendor/dompdf/include/list_bullet_image_frame_decorator.cls.php deleted file mode 100644 index 6cfb546cb..000000000 --- a/library/vendor/dompdf/include/list_bullet_image_frame_decorator.cls.php +++ /dev/null @@ -1,143 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Decorates frames for list bullets with custom images - * - * @access private - * @package dompdf - */ -class List_Bullet_Image_Frame_Decorator extends Frame_Decorator { - - /** - * The underlying image frame - * - * @var Image_Frame_Decorator - */ - protected $_img; - - /** - * The image's width in pixels - * - * @var int - */ - protected $_width; - - /** - * The image's height in pixels - * - * @var int - */ - protected $_height; - - /** - * Class constructor - * - * @param Frame $frame the bullet frame to decorate - * @param DOMPDF $dompdf the document's dompdf object - */ - function __construct(Frame $frame, DOMPDF $dompdf) { - $style = $frame->get_style(); - $url = $style->list_style_image; - $frame->get_node()->setAttribute("src", $url); - $this->_img = new Image_Frame_Decorator($frame, $dompdf); - parent::__construct($this->_img, $dompdf); - list($width, $height) = dompdf_getimagesize($this->_img->get_image_url(), $dompdf->get_http_context()); - - // Resample the bullet image to be consistent with 'auto' sized images - // See also Image_Frame_Reflower::get_min_max_width - // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary. - $dpi = $this->_dompdf->get_option("dpi"); - $this->_width = ((float)rtrim($width, "px") * 72) / $dpi; - $this->_height = ((float)rtrim($height, "px") * 72) / $dpi; - - //If an image is taller as the containing block/box, the box should be extended. - //Neighbour elements are overwriting the overlapping image areas. - //Todo: Where can the box size be extended? - //Code below has no effect. - //See block_frame_reflower _calculate_restricted_height - //See generated_frame_reflower, Dompdf:render() "list-item", "-dompdf-list-bullet"S. - //Leave for now - //if ($style->min_height < $this->_height ) { - // $style->min_height = $this->_height; - //} - //$style->height = "auto"; - } - - /** - * Return the bullet's width - * - * @return int - */ - function get_width() { - //ignore image width, use same width as on predefined bullet List_Bullet_Frame_Decorator - //for proper alignment of bullet image and text. Allow image to not fitting on left border. - //This controls the distance between bullet image and text - //return $this->_width; - return $this->_frame->get_style()->get_font_size()*List_Bullet_Frame_Decorator::BULLET_SIZE + - 2 * List_Bullet_Frame_Decorator::BULLET_PADDING; - } - - /** - * Return the bullet's height - * - * @return int - */ - function get_height() { - //based on image height - return $this->_height; - } - - /** - * Override get_margin_width - * - * @return int - */ - function get_margin_width() { - //ignore image width, use same width as on predefined bullet List_Bullet_Frame_Decorator - //for proper alignment of bullet image and text. Allow image to not fitting on left border. - //This controls the extra indentation of text to make room for the bullet image. - //Here use actual image size, not predefined bullet size - //return $this->_frame->get_style()->get_font_size()*List_Bullet_Frame_Decorator::BULLET_SIZE + - // 2 * List_Bullet_Frame_Decorator::BULLET_PADDING; - - // Small hack to prevent indenting of list text - // Image Might not exist, then position like on list_bullet_frame_decorator fallback to none. - if ( $this->_frame->get_style()->list_style_position === "outside" || - $this->_width == 0) - return 0; - //This aligns the "inside" image position with the text. - //The text starts to the right of the image. - //Between the image and the text there is an added margin of image width. - //Where this comes from is unknown. - //The corresponding List_Bullet_Frame_Decorator sets a smaller margin. bullet size? - return $this->_width + 2 * List_Bullet_Frame_Decorator::BULLET_PADDING; - } - - /** - * Override get_margin_height() - * - * @return int - */ - function get_margin_height() { - //Hits only on "inset" lists items, to increase height of box - //based on image height - return $this->_height + 2 * List_Bullet_Frame_Decorator::BULLET_PADDING; - } - - /** - * Return image url - * - * @return string - */ - function get_image_url() { - return $this->_img->get_image_url(); - } - -} diff --git a/library/vendor/dompdf/include/list_bullet_positioner.cls.php b/library/vendor/dompdf/include/list_bullet_positioner.cls.php deleted file mode 100644 index 7e89ae471..000000000 --- a/library/vendor/dompdf/include/list_bullet_positioner.cls.php +++ /dev/null @@ -1,73 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Positions list bullets - * - * @access private - * @package dompdf - */ -class List_Bullet_Positioner extends Positioner { - - function __construct(Frame_Decorator $frame) { parent::__construct($frame); } - - //........................................................................ - - function position() { - - // Bullets & friends are positioned an absolute distance to the left of - // the content edge of their parent element - $cb = $this->_frame->get_containing_block(); - - // Note: this differs from most frames in that we must position - // ourselves after determining our width - $x = $cb["x"] - $this->_frame->get_width(); - - $p = $this->_frame->find_block_parent(); - - $y = $p->get_current_line_box()->y; - - // This is a bit of a hack... - $n = $this->_frame->get_next_sibling(); - if ( $n ) { - $style = $n->get_style(); - $line_height = $style->length_in_pt($style->line_height, $style->get_font_size()); - $offset = $style->length_in_pt($line_height, $n->get_containing_block("h")) - $this->_frame->get_height(); - $y += $offset / 2; - } - - // Now the position is the left top of the block which should be marked with the bullet. - // We tried to find out the y of the start of the first text character within the block. - // But the top margin/padding does not fit, neither from this nor from the next sibling - // The "bit of a hack" above does not work also. - - // Instead let's position the bullet vertically centered to the block which should be marked. - // But for get_next_sibling() the get_containing_block is all zero, and for find_block_parent() - // the get_containing_block is paper width and the entire list as height. - - // if ($p) { - // //$cb = $n->get_containing_block(); - // $cb = $p->get_containing_block(); - // $y += $cb["h"]/2; - // print 'cb:'.$cb["x"].':'.$cb["y"].':'.$cb["w"].':'.$cb["h"].':'; - // } - - // Todo: - // For now give up on the above. Use Guesswork with font y-pos in the middle of the line spacing - - /*$style = $p->get_style(); - $font_size = $style->get_font_size(); - $line_height = $style->length_in_pt($style->line_height, $font_size); - $y += ($line_height - $font_size) / 2; */ - - //Position is x-end y-top of character position of the bullet. - $this->_frame->set_position($x, $y); - - } -} diff --git a/library/vendor/dompdf/include/list_bullet_renderer.cls.php b/library/vendor/dompdf/include/list_bullet_renderer.cls.php deleted file mode 100644 index be4cde2bd..000000000 --- a/library/vendor/dompdf/include/list_bullet_renderer.cls.php +++ /dev/null @@ -1,236 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Renders list bullets - * - * @access private - * @package dompdf - */ -class List_Bullet_Renderer extends Abstract_Renderer { - static function get_counter_chars($type) { - static $cache = array(); - - if ( isset($cache[$type]) ) { - return $cache[$type]; - } - - $uppercase = false; - $text = ""; - - switch ($type) { - case "decimal-leading-zero": - case "decimal": - case "1": - return "0123456789"; - - case "upper-alpha": - case "upper-latin": - case "A": - $uppercase = true; - case "lower-alpha": - case "lower-latin": - case "a": - $text = "abcdefghijklmnopqrstuvwxyz"; - break; - - case "upper-roman": - case "I": - $uppercase = true; - case "lower-roman": - case "i": - $text = "ivxlcdm"; - break; - - case "lower-greek": - for($i = 0; $i < 24; $i++) { - $text .= unichr($i+944); - } - break; - } - - if ( $uppercase ) { - $text = strtoupper($text); - } - - return $cache[$type] = "$text."; - } - - /** - * @param integer $n - * @param string $type - * @param integer $pad - * - * @return string - */ - private function make_counter($n, $type, $pad = null){ - $n = intval($n); - $text = ""; - $uppercase = false; - - switch ($type) { - case "decimal-leading-zero": - case "decimal": - case "1": - if ($pad) - $text = str_pad($n, $pad, "0", STR_PAD_LEFT); - else - $text = $n; - break; - - case "upper-alpha": - case "upper-latin": - case "A": - $uppercase = true; - case "lower-alpha": - case "lower-latin": - case "a": - $text = chr( ($n % 26) + ord('a') - 1); - break; - - case "upper-roman": - case "I": - $uppercase = true; - case "lower-roman": - case "i": - $text = dec2roman($n); - break; - - case "lower-greek": - $text = unichr($n + 944); - break; - } - - if ( $uppercase ) { - $text = strtoupper($text); - } - - return "$text."; - } - - function render(Frame $frame) { - $style = $frame->get_style(); - $font_size = $style->get_font_size(); - $line_height = $style->length_in_pt($style->line_height, $frame->get_containing_block("w")); - - $this->_set_opacity( $frame->get_opacity( $style->opacity ) ); - - $li = $frame->get_parent(); - - // Don't render bullets twice if if was split - if ($li->_splitted) { - return; - } - - // Handle list-style-image - // If list style image is requested but missing, fall back to predefined types - if ( $style->list_style_image !== "none" && - !Image_Cache::is_broken($img = $frame->get_image_url())) { - - list($x,$y) = $frame->get_position(); - - //For expected size and aspect, instead of box size, use image natural size scaled to DPI. - // Resample the bullet image to be consistent with 'auto' sized images - // See also Image_Frame_Reflower::get_min_max_width - // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary. - //$w = $frame->get_width(); - //$h = $frame->get_height(); - list($width, $height) = dompdf_getimagesize($img, $this->_dompdf->get_http_context()); - $dpi = $this->_dompdf->get_option("dpi"); - $w = ((float)rtrim($width, "px") * 72) / $dpi; - $h = ((float)rtrim($height, "px") * 72) / $dpi; - - $x -= $w; - $y -= ($line_height - $font_size)/2; //Reverse hinting of list_bullet_positioner - - $this->_canvas->image( $img, $x, $y, $w, $h); - - } else { - - $bullet_style = $style->list_style_type; - - $fill = false; - - switch ($bullet_style) { - - default: - case "disc": - $fill = true; - - case "circle": - list($x,$y) = $frame->get_position(); - $r = ($font_size*(List_Bullet_Frame_Decorator::BULLET_SIZE /*-List_Bullet_Frame_Decorator::BULLET_THICKNESS*/ ))/2; - $x -= $font_size*(List_Bullet_Frame_Decorator::BULLET_SIZE/2); - $y += ($font_size*(1-List_Bullet_Frame_Decorator::BULLET_DESCENT))/2; - $o = $font_size*List_Bullet_Frame_Decorator::BULLET_THICKNESS; - $this->_canvas->circle($x, $y, $r, $style->color, $o, null, $fill); - break; - - case "square": - list($x, $y) = $frame->get_position(); - $w = $font_size*List_Bullet_Frame_Decorator::BULLET_SIZE; - $x -= $w; - $y += ($font_size*(1-List_Bullet_Frame_Decorator::BULLET_DESCENT-List_Bullet_Frame_Decorator::BULLET_SIZE))/2; - $this->_canvas->filled_rectangle($x, $y, $w, $w, $style->color); - break; - - case "decimal-leading-zero": - case "decimal": - case "lower-alpha": - case "lower-latin": - case "lower-roman": - case "lower-greek": - case "upper-alpha": - case "upper-latin": - case "upper-roman": - case "1": // HTML 4.0 compatibility - case "a": - case "i": - case "A": - case "I": - $pad = null; - if ( $bullet_style === "decimal-leading-zero" ) { - $pad = strlen($li->get_parent()->get_node()->getAttribute("dompdf-children-count")); - } - - $node = $frame->get_node(); - - if ( !$node->hasAttribute("dompdf-counter") ) { - return; - } - - $index = $node->getAttribute("dompdf-counter"); - $text = $this->make_counter($index, $bullet_style, $pad); - - if ( trim($text) == "" ) { - return; - } - - $spacing = 0; - $font_family = $style->font_family; - - $line = $li->get_containing_line(); - list($x, $y) = array($frame->get_position("x"), $line->y); - - $x -= Font_Metrics::get_text_width($text, $font_family, $font_size, $spacing); - - // Take line-height into account - $line_height = $style->line_height; - $y += ($line_height - $font_size) / 4; // FIXME I thought it should be 2, but 4 gives better results - - $this->_canvas->text($x, $y, $text, - $font_family, $font_size, - $style->color, $spacing); - - case "none": - break; - } - } - } -} diff --git a/library/vendor/dompdf/include/null_frame_decorator.cls.php b/library/vendor/dompdf/include/null_frame_decorator.cls.php deleted file mode 100644 index 15f806d0d..000000000 --- a/library/vendor/dompdf/include/null_frame_decorator.cls.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Dummy decorator - * - * @access private - * @package dompdf - */ -class Null_Frame_Decorator extends Frame_Decorator { - - function __construct(Frame $frame, DOMPDF $dompdf) { - parent::__construct($frame, $dompdf); - $style = $this->_frame->get_style(); - $style->width = 0; - $style->height = 0; - $style->margin = 0; - $style->padding = 0; - } - -} diff --git a/library/vendor/dompdf/include/null_frame_reflower.cls.php b/library/vendor/dompdf/include/null_frame_reflower.cls.php deleted file mode 100644 index 389f1479d..000000000 --- a/library/vendor/dompdf/include/null_frame_reflower.cls.php +++ /dev/null @@ -1,21 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Dummy reflower - * - * @access private - * @package dompdf - */ -class Null_Frame_Reflower extends Frame_Reflower { - - function __construct(Frame $frame) { parent::__construct($frame); } - - function reflow(Block_Frame_Decorator $block = null) { return; } - -} diff --git a/library/vendor/dompdf/include/null_positioner.cls.php b/library/vendor/dompdf/include/null_positioner.cls.php deleted file mode 100644 index 97d4986dc..000000000 --- a/library/vendor/dompdf/include/null_positioner.cls.php +++ /dev/null @@ -1,23 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Dummy positioner - * - * @access private - * @package dompdf - */ -class Null_Positioner extends Positioner { - - function __construct(Frame_Decorator $frame) { - parent::__construct($frame); - } - - function position() { return; } - -} diff --git a/library/vendor/dompdf/include/page_cache.cls.php b/library/vendor/dompdf/include/page_cache.cls.php deleted file mode 100644 index 652da160a..000000000 --- a/library/vendor/dompdf/include/page_cache.cls.php +++ /dev/null @@ -1,126 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Caches individual rendered PDF pages - * - * Not totally implemented yet. Use at your own risk ;) - * - * @access private - * @package dompdf - * @static - */ -class Page_Cache { - - const DB_USER = "dompdf_page_cache"; - const DB_PASS = "some meaningful password"; - const DB_NAME = "dompdf_page_cache"; - - static private $__connection = null; - - static function init() { - if ( is_null(self::$__connection) ) { - $con_str = "host=" . DB_HOST . - " dbname=" . self::DB_NAME . - " user=" . self::DB_USER . - " password=" . self::DB_PASS; - - if ( !self::$__connection = pg_connect($con_str) ) - throw new Exception("Database connection failed."); - } - } - - function __construct() { throw new Exception("Can not create instance of Page_Class. Class is static."); } - - private static function __query($sql) { - if ( !($res = pg_query(self::$__connection, $sql)) ) - throw new Exception(pg_last_error(self::$__connection)); - return $res; - } - - static function store_page($id, $page_num, $data) { - $where = "WHERE id='" . pg_escape_string($id) . "' AND ". - "page_num=". pg_escape_string($page_num); - - $res = self::__query("SELECT timestamp FROM page_cache ". $where); - - $row = pg_fetch_assoc($res); - - if ( $row ) - self::__query("UPDATE page_cache SET data='" . pg_escape_string($data) . "' " . $where); - else - self::__query("INSERT INTO page_cache (id, page_num, data) VALUES ('" . pg_escape_string($id) . "', ". - pg_escape_string($page_num) . ", ". - "'". pg_escape_string($data) . "')"); - - } - - static function store_fonts($id, $fonts) { - self::__query("BEGIN"); - // Update the font information - self::__query("DELETE FROM page_fonts WHERE id='" . pg_escape_string($id) . "'"); - - foreach (array_keys($fonts) as $font) - self::__query("INSERT INTO page_fonts (id, font_name) VALUES ('" . - pg_escape_string($id) . "', '" . pg_escape_string($font) . "')"); - self::__query("COMMIT"); - } - -// static function retrieve_page($id, $page_num) { - -// $res = self::__query("SELECT data FROM page_cache WHERE id='" . pg_escape_string($id) . "' AND ". -// "page_num=". pg_escape_string($page_num)); - -// $row = pg_fetch_assoc($res); - -// return pg_unescape_bytea($row["data"]); - -// } - - static function get_page_timestamp($id, $page_num) { - $res = self::__query("SELECT timestamp FROM page_cache WHERE id='" . pg_escape_string($id) . "' AND ". - "page_num=". pg_escape_string($page_num)); - - $row = pg_fetch_assoc($res); - - return $row["timestamp"]; - - } - - // Adds the cached document referenced by $id to the provided pdf - static function insert_cached_document(CPDF_Adapter $pdf, $id, $new_page = true) { - $res = self::__query("SELECT font_name FROM page_fonts WHERE id='" . pg_escape_string($id) . "'"); - - // Ensure that the fonts needed by the cached document are loaded into - // the pdf - while ($row = pg_fetch_assoc($res)) - $pdf->get_cpdf()->selectFont($row["font_name"]); - - $res = self::__query("SELECT data FROM page_cache WHERE id='" . pg_escape_string($id) . "'"); - - if ( $new_page ) - $pdf->new_page(); - - $first = true; - while ($row = pg_fetch_assoc($res)) { - - if ( !$first ) - $pdf->new_page(); - else - $first = false; - - $page = $pdf->reopen_serialized_object($row["data"]); - //$pdf->close_object(); - $pdf->add_object($page, "add"); - - } - - } -} - -Page_Cache::init(); diff --git a/library/vendor/dompdf/include/page_frame_decorator.cls.php b/library/vendor/dompdf/include/page_frame_decorator.cls.php deleted file mode 100644 index f089d0075..000000000 --- a/library/vendor/dompdf/include/page_frame_decorator.cls.php +++ /dev/null @@ -1,592 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Decorates frames for page layout - * - * @access private - * @package dompdf - */ -class Page_Frame_Decorator extends Frame_Decorator { - - /** - * y value of bottom page margin - * - * @var float - */ - protected $_bottom_page_margin; - - /** - * Flag indicating page is full. - * - * @var bool - */ - protected $_page_full; - - /** - * Number of tables currently being reflowed - * - * @var int - */ - protected $_in_table; - - /** - * The pdf renderer - * - * @var Renderer - */ - protected $_renderer; - - /** - * This page's floating frames - * - * @var array - */ - protected $_floating_frames = array(); - - //........................................................................ - - /** - * Class constructor - * - * @param Frame $frame the frame to decorate - * @param DOMPDF $dompdf - */ - function __construct(Frame $frame, DOMPDF $dompdf) { - parent::__construct($frame, $dompdf); - $this->_page_full = false; - $this->_in_table = 0; - $this->_bottom_page_margin = null; - } - - /** - * Set the renderer used for this pdf - * - * @param Renderer $renderer the renderer to use - */ - function set_renderer($renderer) { - $this->_renderer = $renderer; - } - - /** - * Return the renderer used for this pdf - * - * @return Renderer - */ - function get_renderer() { - return $this->_renderer; - } - - /** - * Set the frame's containing block. Overridden to set $this->_bottom_page_margin. - * - * @param float $x - * @param float $y - * @param float $w - * @param float $h - */ - function set_containing_block($x = null, $y = null, $w = null, $h = null) { - parent::set_containing_block($x,$y,$w,$h); - //$w = $this->get_containing_block("w"); - if ( isset($h) ) - $this->_bottom_page_margin = $h; // - $this->_frame->get_style()->length_in_pt($this->_frame->get_style()->margin_bottom, $w); - } - - /** - * Returns true if the page is full and is no longer accepting frames. - * - * @return bool - */ - function is_full() { - return $this->_page_full; - } - - /** - * Start a new page by resetting the full flag. - */ - function next_page() { - $this->_floating_frames = array(); - $this->_renderer->new_page(); - $this->_page_full = false; - } - - /** - * Indicate to the page that a table is currently being reflowed. - */ - function table_reflow_start() { - $this->_in_table++; - } - - /** - * Indicate to the page that table reflow is finished. - */ - function table_reflow_end() { - $this->_in_table--; - } - - /** - * Return whether we are currently in a nested table or not - * - * @return bool - */ - function in_nested_table() { - return $this->_in_table > 1; - } - - /** - * Check if a forced page break is required before $frame. This uses the - * frame's page_break_before property as well as the preceeding frame's - * page_break_after property. - * - * @link http://www.w3.org/TR/CSS21/page.html#forced - * - * @param Frame $frame the frame to check - * @return bool true if a page break occured - */ - function check_forced_page_break(Frame $frame) { - - // Skip check if page is already split - if ( $this->_page_full ) - return null; - - $block_types = array("block", "list-item", "table", "inline"); - $page_breaks = array("always", "left", "right"); - - $style = $frame->get_style(); - - if ( !in_array($style->display, $block_types) ) - return false; - - // Find the previous block-level sibling - $prev = $frame->get_prev_sibling(); - - while ( $prev && !in_array($prev->get_style()->display, $block_types) ) - $prev = $prev->get_prev_sibling(); - - - if ( in_array($style->page_break_before, $page_breaks) ) { - - // Prevent cascading splits - $frame->split(null, true); - // We have to grab the style again here because split() resets - // $frame->style to the frame's orignal style. - $frame->get_style()->page_break_before = "auto"; - $this->_page_full = true; - - return true; - } - - if ( $prev && in_array($prev->get_style()->page_break_after, $page_breaks) ) { - // Prevent cascading splits - $frame->split(null, true); - $prev->get_style()->page_break_after = "auto"; - $this->_page_full = true; - return true; - } - - if( $prev && $prev->get_last_child() && $frame->get_node()->nodeName != "body" ) { - $prev_last_child = $prev->get_last_child(); - if ( in_array($prev_last_child->get_style()->page_break_after, $page_breaks) ) { - $frame->split(null, true); - $prev_last_child->get_style()->page_break_after = "auto"; - $this->_page_full = true; - return true; - } - } - - - return false; - } - - /** - * Determine if a page break is allowed before $frame - * http://www.w3.org/TR/CSS21/page.html#allowed-page-breaks - * - * In the normal flow, page breaks can occur at the following places: - * - * 1. In the vertical margin between block boxes. When a page - * break occurs here, the used values of the relevant - * 'margin-top' and 'margin-bottom' properties are set to '0'. - * 2. Between line boxes inside a block box. - * - * These breaks are subject to the following rules: - * - * * Rule A: Breaking at (1) is allowed only if the - * 'page-break-after' and 'page-break-before' properties of - * all the elements generating boxes that meet at this margin - * allow it, which is when at least one of them has the value - * 'always', 'left', or 'right', or when all of them are - * 'auto'. - * - * * Rule B: However, if all of them are 'auto' and the - * nearest common ancestor of all the elements has a - * 'page-break-inside' value of 'avoid', then breaking here is - * not allowed. - * - * * Rule C: Breaking at (2) is allowed only if the number of - * line boxes between the break and the start of the enclosing - * block box is the value of 'orphans' or more, and the number - * of line boxes between the break and the end of the box is - * the value of 'widows' or more. - * - * * Rule D: In addition, breaking at (2) is allowed only if - * the 'page-break-inside' property is 'auto'. - * - * If the above doesn't provide enough break points to keep - * content from overflowing the page boxes, then rules B and D are - * dropped in order to find additional breakpoints. - * - * If that still does not lead to sufficient break points, rules A - * and C are dropped as well, to find still more break points. - * - * We will also allow breaks between table rows. However, when - * splitting a table, the table headers should carry over to the - * next page (but they don't yet). - * - * @param Frame $frame the frame to check - * @return bool true if a break is allowed, false otherwise - */ - protected function _page_break_allowed(Frame $frame) { - - $block_types = array("block", "list-item", "table", "-dompdf-image"); - dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName. ")"); - $display = $frame->get_style()->display; - - // Block Frames (1): - if ( in_array($display, $block_types) ) { - - // Avoid breaks within table-cells - if ( $this->_in_table ) { - dompdf_debug("page-break", "In table: " . $this->_in_table); - return false; - } - - // Rules A & B - - if ( $frame->get_style()->page_break_before === "avoid" ) { - dompdf_debug("page-break", "before: avoid"); - return false; - } - - // Find the preceeding block-level sibling - $prev = $frame->get_prev_sibling(); - while ( $prev && !in_array($prev->get_style()->display, $block_types) ) - $prev = $prev->get_prev_sibling(); - - // Does the previous element allow a page break after? - if ( $prev && $prev->get_style()->page_break_after === "avoid" ) { - dompdf_debug("page-break", "after: avoid"); - return false; - } - - // If both $prev & $frame have the same parent, check the parent's - // page_break_inside property. - $parent = $frame->get_parent(); - if ( $prev && $parent && $parent->get_style()->page_break_inside === "avoid" ) { - dompdf_debug("page-break", "parent inside: avoid"); - return false; - } - - // To prevent cascading page breaks when a top-level element has - // page-break-inside: avoid, ensure that at least one frame is - // on the page before splitting. - if ( $parent->get_node()->nodeName === "body" && !$prev ) { - // We are the body's first child - dompdf_debug("page-break", "Body's first child."); - return false; - } - - // If the frame is the first block-level frame, use the value from - // $frame's parent instead. - if ( !$prev && $parent ) - return $this->_page_break_allowed( $parent ); - - dompdf_debug("page-break", "block: break allowed"); - return true; - - } - - // Inline frames (2): - else if ( in_array($display, Style::$INLINE_TYPES) ) { - - // Avoid breaks within table-cells - if ( $this->_in_table ) { - dompdf_debug("page-break", "In table: " . $this->_in_table); - return false; - } - - // Rule C - $block_parent = $frame->find_block_parent(); - if ( count($block_parent->get_line_boxes() ) < $frame->get_style()->orphans ) { - dompdf_debug("page-break", "orphans"); - return false; - } - - // FIXME: Checking widows is tricky without having laid out the - // remaining line boxes. Just ignore it for now... - - // Rule D - $p = $block_parent; - while ($p) { - if ( $p->get_style()->page_break_inside === "avoid" ) { - dompdf_debug("page-break", "parent->inside: avoid"); - return false; - } - $p = $p->find_block_parent(); - } - - // To prevent cascading page breaks when a top-level element has - // page-break-inside: avoid, ensure that at least one frame with - // some content is on the page before splitting. - $prev = $frame->get_prev_sibling(); - while ( $prev && ($prev->is_text_node() && trim($prev->get_node()->nodeValue) == "") ) - $prev = $prev->get_prev_sibling(); - - if ( $block_parent->get_node()->nodeName === "body" && !$prev ) { - // We are the body's first child - dompdf_debug("page-break", "Body's first child."); - return false; - } - - // Skip breaks on empty text nodes - if ( $frame->is_text_node() && - $frame->get_node()->nodeValue == "" ) - return false; - - dompdf_debug("page-break", "inline: break allowed"); - return true; - - // Table-rows - } else if ( $display === "table-row" ) { - - // Simply check if the parent table's page_break_inside property is - // not 'avoid' - $p = Table_Frame_Decorator::find_parent_table($frame); - - while ($p) { - if ( $p->get_style()->page_break_inside === "avoid" ) { - dompdf_debug("page-break", "parent->inside: avoid"); - return false; - } - $p = $p->find_block_parent(); - } - - // Avoid breaking after the first row of a table - if ( $p && $p->get_first_child() === $frame) { - dompdf_debug("page-break", "table: first-row"); - return false; - } - - // If this is a nested table, prevent the page from breaking - if ( $this->_in_table > 1 ) { - dompdf_debug("page-break", "table: nested table"); - return false; - } - - dompdf_debug("page-break","table-row/row-groups: break allowed"); - return true; - - } else if ( in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) { - - // Disallow breaks at row-groups: only split at row boundaries - return false; - - } else { - - dompdf_debug("page-break", "? " . $frame->get_style()->display . ""); - return false; - } - - } - - /** - * Check if $frame will fit on the page. If the frame does not fit, - * the frame tree is modified so that a page break occurs in the - * correct location. - * - * @param Frame $frame the frame to check - * @return Frame the frame following the page break - */ - function check_page_break(Frame $frame) { - // Do not split if we have already or if the frame was already - // pushed to the next page (prevents infinite loops) - if ( $this->_page_full || $frame->_already_pushed ) { - return false; - } - - // If the frame is absolute of fixed it shouldn't break - $p = $frame; - do { - if ( $p->is_absolute() ) - return false; - } while ( $p = $p->get_parent() ); - - $margin_height = $frame->get_margin_height(); - - // FIXME If the row is taller than the page and - // if it the first of the page, we don't break - if ( $frame->get_style()->display === "table-row" && - !$frame->get_prev_sibling() && - $margin_height > $this->get_margin_height() ) - return false; - - // Determine the frame's maximum y value - $max_y = $frame->get_position("y") + $margin_height; - - // If a split is to occur here, then the bottom margins & paddings of all - // parents of $frame must fit on the page as well: - $p = $frame->get_parent(); - while ( $p ) { - $style = $p->get_style(); - $max_y += $style->length_in_pt(array($style->margin_bottom, - $style->padding_bottom, - $style->border_bottom_width)); - $p = $p->get_parent(); - } - - - // Check if $frame flows off the page - if ( $max_y <= $this->_bottom_page_margin ) - // no: do nothing - return false; - - dompdf_debug("page-break", "check_page_break"); - dompdf_debug("page-break", "in_table: " . $this->_in_table); - - // yes: determine page break location - $iter = $frame; - $flg = false; - - $in_table = $this->_in_table; - - dompdf_debug("page-break","Starting search"); - while ( $iter ) { - // echo "\nbacktrack: " .$iter->get_node()->nodeName ." ".spl_object_hash($iter->get_node()). ""; - if ( $iter === $this ) { - dompdf_debug("page-break", "reached root."); - // We've reached the root in our search. Just split at $frame. - break; - } - - if ( $this->_page_break_allowed($iter) ) { - dompdf_debug("page-break","break allowed, splitting."); - $iter->split(null, true); - $this->_page_full = true; - $this->_in_table = $in_table; - $frame->_already_pushed = true; - return true; - } - - if ( !$flg && $next = $iter->get_last_child() ) { - dompdf_debug("page-break", "following last child."); - - if ( $next->is_table() ) - $this->_in_table++; - - $iter = $next; - continue; - } - - if ( $next = $iter->get_prev_sibling() ) { - dompdf_debug("page-break", "following prev sibling."); - - if ( $next->is_table() && !$iter->is_table() ) - $this->_in_table++; - - else if ( !$next->is_table() && $iter->is_table() ) - $this->_in_table--; - - $iter = $next; - $flg = false; - continue; - } - - if ( $next = $iter->get_parent() ) { - dompdf_debug("page-break", "following parent."); - - if ( $iter->is_table() ) - $this->_in_table--; - - $iter = $next; - $flg = true; - continue; - } - - break; - } - - $this->_in_table = $in_table; - - // No valid page break found. Just break at $frame. - dompdf_debug("page-break", "no valid break found, just splitting."); - - // If we are in a table, backtrack to the nearest top-level table row - if ( $this->_in_table ) { - $iter = $frame; - while ($iter && $iter->get_style()->display !== "table-row") - $iter = $iter->get_parent(); - - $iter->split(null, true); - } else { - $frame->split(null, true); - } - - $this->_page_full = true; - $frame->_already_pushed = true; - return true; - } - - //........................................................................ - - function split(Frame $frame = null, $force_pagebreak = false) { - // Do nothing - } - - /** - * Add a floating frame - * - * @param Frame $frame - * - * @return void - */ - function add_floating_frame(Frame $frame) { - array_unshift($this->_floating_frames, $frame); - } - - /** - * @return Frame[] - */ - function get_floating_frames() { - return $this->_floating_frames; - } - - public function remove_floating_frame($key) { - unset($this->_floating_frames[$key]); - } - - public function get_lowest_float_offset(Frame $child) { - $style = $child->get_style(); - $side = $style->clear; - $float = $style->float; - - $y = 0; - - foreach($this->_floating_frames as $key => $frame) { - if ( $side === "both" || $frame->get_style()->float === $side ) { - $y = max($y, $frame->get_position("y") + $frame->get_margin_height()); - - if ( $float !== "none" ) { - $this->remove_floating_frame($key); - } - } - } - - return $y; - } - -} diff --git a/library/vendor/dompdf/include/page_frame_reflower.cls.php b/library/vendor/dompdf/include/page_frame_reflower.cls.php deleted file mode 100644 index 4223f4e85..000000000 --- a/library/vendor/dompdf/include/page_frame_reflower.cls.php +++ /dev/null @@ -1,186 +0,0 @@ - - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Reflows pages - * - * @access private - * @package dompdf - */ -class Page_Frame_Reflower extends Frame_Reflower { - - /** - * Cache of the callbacks array - * - * @var array - */ - private $_callbacks; - - /** - * Cache of the canvas - * - * @var Canvas - */ - private $_canvas; - - function __construct(Page_Frame_Decorator $frame) { parent::__construct($frame); } - - function apply_page_style(Frame $frame, $page_number){ - $style = $frame->get_style(); - $page_styles = $style->get_stylesheet()->get_page_styles(); - - // http://www.w3.org/TR/CSS21/page.html#page-selectors - if ( count($page_styles) > 1 ) { - $odd = $page_number % 2 == 1; - $first = $page_number == 1; - - $style = clone $page_styles["base"]; - - // FIXME RTL - if ( $odd && isset($page_styles[":right"]) ) { - $style->merge($page_styles[":right"]); - } - - if ( $odd && isset($page_styles[":odd"]) ) { - $style->merge($page_styles[":odd"]); - } - - // FIXME RTL - if ( !$odd && isset($page_styles[":left"]) ) { - $style->merge($page_styles[":left"]); - } - - if ( !$odd && isset($page_styles[":even"]) ) { - $style->merge($page_styles[":even"]); - } - - if ( $first && isset($page_styles[":first"]) ) { - $style->merge($page_styles[":first"]); - } - - $frame->set_style($style); - } - } - - //........................................................................ - - /** - * Paged layout: - * http://www.w3.org/TR/CSS21/page.html - */ - function reflow(Block_Frame_Decorator $block = null) { - $fixed_children = array(); - $prev_child = null; - $child = $this->_frame->get_first_child(); - $current_page = 0; - - while ($child) { - $this->apply_page_style($this->_frame, $current_page + 1); - - $style = $this->_frame->get_style(); - - // Pages are only concerned with margins - $cb = $this->_frame->get_containing_block(); - $left = $style->length_in_pt($style->margin_left, $cb["w"]); - $right = $style->length_in_pt($style->margin_right, $cb["w"]); - $top = $style->length_in_pt($style->margin_top, $cb["h"]); - $bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]); - - $content_x = $cb["x"] + $left; - $content_y = $cb["y"] + $top; - $content_width = $cb["w"] - $left - $right; - $content_height = $cb["h"] - $top - $bottom; - - // Only if it's the first page, we save the nodes with a fixed position - if ($current_page == 0) { - $children = $child->get_children(); - foreach ($children as $onechild) { - if ($onechild->get_style()->position === "fixed") { - $fixed_children[] = $onechild->deep_copy(); - } - } - $fixed_children = array_reverse($fixed_children); - } - - $child->set_containing_block($content_x, $content_y, $content_width, $content_height); - - // Check for begin reflow callback - $this->_check_callbacks("begin_page_reflow", $child); - - //Insert a copy of each node which have a fixed position - if ($current_page >= 1) { - foreach ($fixed_children as $fixed_child) { - $child->insert_child_before($fixed_child->deep_copy(), $child->get_first_child()); - } - } - - $child->reflow(); - $next_child = $child->get_next_sibling(); - - // Check for begin render callback - $this->_check_callbacks("begin_page_render", $child); - - // Render the page - $this->_frame->get_renderer()->render($child); - - // Check for end render callback - $this->_check_callbacks("end_page_render", $child); - - if ( $next_child ) { - $this->_frame->next_page(); - } - - // Wait to dispose of all frames on the previous page - // so callback will have access to them - if ( $prev_child ) { - $prev_child->dispose(true); - } - $prev_child = $child; - $child = $next_child; - $current_page++; - } - - // Dispose of previous page if it still exists - if ( $prev_child ) { - $prev_child->dispose(true); - } - } - - //........................................................................ - - /** - * Check for callbacks that need to be performed when a given event - * gets triggered on a page - * - * @param string $event the type of event - * @param Frame $frame the frame that event is triggered on - */ - protected function _check_callbacks($event, $frame) { - if (!isset($this->_callbacks)) { - $dompdf = $this->_frame->get_dompdf(); - $this->_callbacks = $dompdf->get_callbacks(); - $this->_canvas = $dompdf->get_canvas(); - } - - if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) { - $info = array(0 => $this->_canvas, "canvas" => $this->_canvas, - 1 => $frame, "frame" => $frame); - $fs = $this->_callbacks[$event]; - foreach ($fs as $f) { - if (is_callable($f)) { - if (is_array($f)) { - $f[0]->$f[1]($info); - } else { - $f($info); - } - } - } - } - } -} diff --git a/library/vendor/dompdf/include/pdflib_adapter.cls.php b/library/vendor/dompdf/include/pdflib_adapter.cls.php deleted file mode 100644 index 2417d5f52..000000000 --- a/library/vendor/dompdf/include/pdflib_adapter.cls.php +++ /dev/null @@ -1,1085 +0,0 @@ - - * @author Helmut Tischer - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * PDF rendering interface - * - * PDFLib_Adapter provides a simple, stateless interface to the one - * provided by PDFLib. - * - * Unless otherwise mentioned, all dimensions are in points (1/72 in). - * The coordinate origin is in the top left corner and y values - * increase downwards. - * - * See {@link http://www.pdflib.com/} for more complete documentation - * on the underlying PDFlib functions. - * - * @package dompdf - */ -class PDFLib_Adapter implements Canvas { - - /** - * Dimensions of paper sizes in points - * - * @var array; - */ - static public $PAPER_SIZES = array(); // Set to CPDF_Adapter::$PAPER_SIZES below. - - /** - * Whether to create PDFs in memory or on disk - * - * @var bool - */ - static $IN_MEMORY = true; - - /** - * @var DOMPDF - */ - private $_dompdf; - - /** - * Instance of PDFLib class - * - * @var PDFlib - */ - private $_pdf; - - /** - * Name of temporary file used for PDFs created on disk - * - * @var string - */ - private $_file; - - /** - * PDF width, in points - * - * @var float - */ - private $_width; - - /** - * PDF height, in points - * - * @var float - */ - private $_height; - - /** - * Last fill color used - * - * @var array - */ - private $_last_fill_color; - - /** - * Last stroke color used - * - * @var array - */ - private $_last_stroke_color; - - /** - * Cache of image handles - * - * @var array - */ - private $_imgs; - - /** - * Cache of font handles - * - * @var array - */ - private $_fonts; - - /** - * List of objects (templates) to add to multiple pages - * - * @var array - */ - private $_objs; - - /** - * Current page number - * - * @var int - */ - private $_page_number; - - /** - * Total number of pages - * - * @var int - */ - private $_page_count; - - /** - * Text to display on every page - * - * @var array - */ - private $_page_text; - - /** - * Array of pages for accesing after rendering is initially complete - * - * @var array - */ - private $_pages; - - /** - * Class constructor - * - * @param mixed $paper The size of paper to use either a string (see {@link CPDF_Adapter::$PAPER_SIZES}) or - * an array(xmin,ymin,xmax,ymax) - * @param string $orientation The orientation of the document (either 'landscape' or 'portrait') - * @param DOMPDF $dompdf - */ - function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) { - if ( is_array($paper) ) { - $size = $paper; - } - else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) ) { - $size = self::$PAPER_SIZES[mb_strtolower($paper)]; - } - else { - $size = self::$PAPER_SIZES["letter"]; - } - - if ( mb_strtolower($orientation) === "landscape" ) { - list($size[2], $size[3]) = array($size[3], $size[2]); - } - - $this->_width = $size[2] - $size[0]; - $this->_height= $size[3] - $size[1]; - - $this->_dompdf = $dompdf; - - $this->_pdf = new PDFLib(); - - if ( defined("DOMPDF_PDFLIB_LICENSE") ) - $this->_pdf->set_parameter( "license", DOMPDF_PDFLIB_LICENSE); - - $this->_pdf->set_parameter("textformat", "utf8"); - $this->_pdf->set_parameter("fontwarning", "false"); - - $this->_pdf->set_info("Creator", "DOMPDF"); - - // Silence pedantic warnings about missing TZ settings - $tz = @date_default_timezone_get(); - date_default_timezone_set("UTC"); - $this->_pdf->set_info("Date", date("Y-m-d")); - date_default_timezone_set($tz); - - if ( self::$IN_MEMORY ) - $this->_pdf->begin_document("",""); - else { - $tmp_dir = $this->_dompdf->get_options("temp_dir"); - $tmp_name = tempnam($tmp_dir, "libdompdf_pdf_"); - @unlink($tmp_name); - $this->_file = "$tmp_name.pdf"; - $this->_pdf->begin_document($this->_file,""); - } - - $this->_pdf->begin_page_ext($this->_width, $this->_height, ""); - - $this->_page_number = $this->_page_count = 1; - $this->_page_text = array(); - - $this->_imgs = array(); - $this->_fonts = array(); - $this->_objs = array(); - - // Set up font paths - $families = Font_Metrics::get_font_families(); - foreach ($families as $files) { - foreach ($files as $file) { - $face = basename($file); - $afm = null; - - // Prefer ttfs to afms - if ( file_exists("$file.ttf") ) { - $outline = "$file.ttf"; - - } else if ( file_exists("$file.TTF") ) { - $outline = "$file.TTF"; - - } else if ( file_exists("$file.pfb") ) { - $outline = "$file.pfb"; - - if ( file_exists("$file.afm") ) { - $afm = "$file.afm"; - } - - } else if ( file_exists("$file.PFB") ) { - $outline = "$file.PFB"; - if ( file_exists("$file.AFM") ) { - $afm = "$file.AFM"; - } - } else { - continue; - } - - $this->_pdf->set_parameter("FontOutline", "\{$face\}=\{$outline\}"); - - if ( !is_null($afm) ) { - $this->_pdf->set_parameter("FontAFM", "\{$face\}=\{$afm\}"); - } - } - } - } - - function get_dompdf(){ - return $this->_dompdf; - } - - /** - * Close the pdf - */ - protected function _close() { - $this->_place_objects(); - - // Close all pages - $this->_pdf->suspend_page(""); - for ($p = 1; $p <= $this->_page_count; $p++) { - $this->_pdf->resume_page("pagenumber=$p"); - $this->_pdf->end_page_ext(""); - } - - $this->_pdf->end_document(""); - } - - - /** - * Returns the PDFLib instance - * - * @return PDFLib - */ - function get_pdflib() { - return $this->_pdf; - } - - /** - * Add meta information to the PDF - * - * @param string $label label of the value (Creator, Producter, etc.) - * @param string $value the text to set - */ - function add_info($label, $value) { - $this->_pdf->set_info($label, $value); - } - - /** - * Opens a new 'object' (template in PDFLib-speak) - * - * While an object is open, all drawing actions are recorded to the - * object instead of being drawn on the current page. Objects can - * be added later to a specific page or to several pages. - * - * The return value is an integer ID for the new object. - * - * @see PDFLib_Adapter::close_object() - * @see PDFLib_Adapter::add_object() - * - * @return int - */ - function open_object() { - $this->_pdf->suspend_page(""); - $ret = $this->_pdf->begin_template($this->_width, $this->_height); - $this->_pdf->save(); - $this->_objs[$ret] = array("start_page" => $this->_page_number); - return $ret; - } - - /** - * Reopen an existing object (NOT IMPLEMENTED) - * PDFLib does not seem to support reopening templates. - * - * @param int $object the ID of a previously opened object - * - * @throws DOMPDF_Exception - * @return void - */ - function reopen_object($object) { - throw new DOMPDF_Exception("PDFLib does not support reopening objects."); - } - - /** - * Close the current template - * - * @see PDFLib_Adapter::open_object() - */ - function close_object() { - $this->_pdf->restore(); - $this->_pdf->end_template(); - $this->_pdf->resume_page("pagenumber=".$this->_page_number); - } - - /** - * Adds the specified object to the document - * - * $where can be one of: - * - 'add' add to current page only - * - 'all' add to every page from the current one onwards - * - 'odd' add to all odd numbered pages from now on - * - 'even' add to all even numbered pages from now on - * - 'next' add the object to the next page only - * - 'nextodd' add to all odd numbered pages from the next one - * - 'nexteven' add to all even numbered pages from the next one - * - * @param int $object the object handle returned by open_object() - * @param string $where - */ - function add_object($object, $where = 'all') { - - if ( mb_strpos($where, "next") !== false ) { - $this->_objs[$object]["start_page"]++; - $where = str_replace("next", "", $where); - if ( $where == "" ) - $where = "add"; - } - - $this->_objs[$object]["where"] = $where; - } - - /** - * Stops the specified template from appearing in the document. - * - * The object will stop being displayed on the page following the - * current one. - * - * @param int $object - */ - function stop_object($object) { - - if ( !isset($this->_objs[$object]) ) - return; - - $start = $this->_objs[$object]["start_page"]; - $where = $this->_objs[$object]["where"]; - - // Place the object on this page if required - if ( $this->_page_number >= $start && - (($this->_page_number % 2 == 0 && $where === "even") || - ($this->_page_number % 2 == 1 && $where === "odd") || - ($where === "all")) ) { - $this->_pdf->fit_image($object, 0, 0, ""); - } - - $this->_objs[$object] = null; - unset($this->_objs[$object]); - } - - /** - * Add all active objects to the current page - */ - protected function _place_objects() { - - foreach ( $this->_objs as $obj => $props ) { - $start = $props["start_page"]; - $where = $props["where"]; - - // Place the object on this page if required - if ( $this->_page_number >= $start && - (($this->_page_number % 2 == 0 && $where === "even") || - ($this->_page_number % 2 == 1 && $where === "odd") || - ($where === "all")) ) { - $this->_pdf->fit_image($obj,0,0,""); - } - } - - } - - function get_width() { return $this->_width; } - - function get_height() { return $this->_height; } - - function get_page_number() { return $this->_page_number; } - - function get_page_count() { return $this->_page_count; } - - function set_page_number($num) { $this->_page_number = (int)$num; } - - function set_page_count($count) { $this->_page_count = (int)$count; } - - - /** - * Sets the line style - * - * @param float $width - * @param $cap - * @param string $join - * @param array $dash - * - * @return void - */ - protected function _set_line_style($width, $cap, $join, $dash) { - - if ( count($dash) == 1 ) - $dash[] = $dash[0]; - - if ( count($dash) > 1 ) - $this->_pdf->setdashpattern("dasharray={" . implode(" ", $dash) . "}"); - else - $this->_pdf->setdash(0,0); - - switch ( $join ) { - case "miter": - $this->_pdf->setlinejoin(0); - break; - - case "round": - $this->_pdf->setlinejoin(1); - break; - - case "bevel": - $this->_pdf->setlinejoin(2); - break; - - default: - break; - } - - switch ( $cap ) { - case "butt": - $this->_pdf->setlinecap(0); - break; - - case "round": - $this->_pdf->setlinecap(1); - break; - - case "square": - $this->_pdf->setlinecap(2); - break; - - default: - break; - } - - $this->_pdf->setlinewidth($width); - - } - - /** - * Sets the line color - * - * @param array $color array(r,g,b) - */ - protected function _set_stroke_color($color) { - if($this->_last_stroke_color == $color) - return; - - $this->_last_stroke_color = $color; - - if (isset($color[3])) { - $type = "cmyk"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]); - } - elseif (isset($color[2])) { - $type = "rgb"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null); - } - else { - $type = "gray"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null); - } - - $this->_pdf->setcolor("stroke", $type, $c1, $c2, $c3, $c4); - } - - /** - * Sets the fill color - * - * @param array $color array(r,g,b) - */ - protected function _set_fill_color($color) { - if($this->_last_fill_color == $color) - return; - - $this->_last_fill_color = $color; - - if (isset($color[3])) { - $type = "cmyk"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]); - } - elseif (isset($color[2])) { - $type = "rgb"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null); - } - else { - $type = "gray"; - list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null); - } - - $this->_pdf->setcolor("fill", $type, $c1, $c2, $c3, $c4); - } - - /** - * Sets the opacity - * - * @param $opacity - * @param $mode - */ - function set_opacity($opacity, $mode = "Normal") { - if ( $mode === "Normal" ) { - $gstate = $this->_pdf->create_gstate("opacityfill=$opacity opacitystroke=$opacity"); - $this->_pdf->set_gstate($gstate); - } - } - - function set_default_view($view, $options = array()) { - // TODO - // http://www.pdflib.com/fileadmin/pdflib/pdf/manuals/PDFlib-8.0.2-API-reference.pdf - /** - * fitheight Fit the page height to the window, with the x coordinate left at the left edge of the window. - * fitrect Fit the rectangle specified by left, bottom, right, and top to the window. - * fitvisible Fit the visible contents of the page (the ArtBox) to the window. - * fitvisibleheight Fit the visible contents of the page to the window with the x coordinate left at the left edge of the window. - * fitvisiblewidth Fit the visible contents of the page to the window with the y coordinate top at the top edge of the window. - * fitwidth Fit the page width to the window, with the y coordinate top at the top edge of the window. - * fitwindow Fit the complete page to the window. - * fixed - */ - //$this->_pdf->set_parameter("openaction", $view); - } - - /** - * Loads a specific font and stores the corresponding descriptor. - * - * @param string $font - * @param string $encoding - * @param string $options - * - * @return int the font descriptor for the font - */ - protected function _load_font($font, $encoding = null, $options = "") { - - // Check if the font is a native PDF font - // Embed non-native fonts - $test = strtolower(basename($font)); - if ( in_array($test, DOMPDF::$native_fonts) ) { - $font = basename($font); - - } else { - // Embed non-native fonts - $options .= " embedding=true"; - } - - if ( is_null($encoding) ) { - - // Unicode encoding is only available for the commerical - // version of PDFlib and not PDFlib-Lite - if ( defined("DOMPDF_PDFLIB_LICENSE") ) - $encoding = "unicode"; - else - $encoding = "auto"; - - } - - $key = "$font:$encoding:$options"; - - if ( isset($this->_fonts[$key]) ) - return $this->_fonts[$key]; - - else { - - $this->_fonts[$key] = $this->_pdf->load_font($font, $encoding, $options); - return $this->_fonts[$key]; - - } - - } - - /** - * Remaps y coords from 4th to 1st quadrant - * - * @param float $y - * @return float - */ - protected function y($y) { return $this->_height - $y; } - - //........................................................................ - - /** - * @param float $x1 - * @param float $y1 - * @param float $x2 - * @param float $y2 - * @param array $color - * @param float $width - * @param array $style - */ - function line($x1, $y1, $x2, $y2, $color, $width, $style = null) { - $this->_set_line_style($width, "butt", "", $style); - $this->_set_stroke_color($color); - - $y1 = $this->y($y1); - $y2 = $this->y($y2); - - $this->_pdf->moveto($x1, $y1); - $this->_pdf->lineto($x2, $y2); - $this->_pdf->stroke(); - } - - function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array()) { - $this->_set_line_style($width, "butt", "", $style); - $this->_set_stroke_color($color); - - $y1 = $this->y($y1); - - $this->_pdf->arc($x1, $y1, $r1, $astart, $aend); - $this->_pdf->stroke(); - } - - //........................................................................ - - function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) { - $this->_set_stroke_color($color); - $this->_set_line_style($width, "butt", "", $style); - - $y1 = $this->y($y1) - $h; - - $this->_pdf->rect($x1, $y1, $w, $h); - $this->_pdf->stroke(); - } - - //........................................................................ - - function filled_rectangle($x1, $y1, $w, $h, $color) { - $this->_set_fill_color($color); - - $y1 = $this->y($y1) - $h; - - $this->_pdf->rect(floatval($x1), floatval($y1), floatval($w), floatval($h)); - $this->_pdf->fill(); - } - - function clipping_rectangle($x1, $y1, $w, $h) { - $this->_pdf->save(); - - $y1 = $this->y($y1) - $h; - - $this->_pdf->rect(floatval($x1), floatval($y1), floatval($w), floatval($h)); - $this->_pdf->clip(); - } - - function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) { - // @todo - $this->clipping_rectangle($x1, $y1, $w, $h); - } - - function clipping_end() { - $this->_pdf->restore(); - } - - function save() { - $this->_pdf->save(); - } - - function restore() { - $this->_pdf->restore(); - } - - function rotate($angle, $x, $y) { - $pdf = $this->_pdf; - $pdf->translate($x, $this->_height-$y); - $pdf->rotate(-$angle); - $pdf->translate(-$x, -$this->_height+$y); - } - - function skew($angle_x, $angle_y, $x, $y) { - $pdf = $this->_pdf; - $pdf->translate($x, $this->_height-$y); - $pdf->skew($angle_y, $angle_x); // Needs to be inverted - $pdf->translate(-$x, -$this->_height+$y); - } - - function scale($s_x, $s_y, $x, $y) { - $pdf = $this->_pdf; - $pdf->translate($x, $this->_height-$y); - $pdf->scale($s_x, $s_y); - $pdf->translate(-$x, -$this->_height+$y); - } - - function translate($t_x, $t_y) { - $this->_pdf->translate($t_x, -$t_y); - } - - function transform($a, $b, $c, $d, $e, $f) { - $this->_pdf->concat($a, $b, $c, $d, $e, $f); - } - - //........................................................................ - - function polygon($points, $color, $width = null, $style = null, $fill = false) { - - $this->_set_fill_color($color); - $this->_set_stroke_color($color); - - if ( !$fill && isset($width) ) - $this->_set_line_style($width, "square", "miter", $style); - - $y = $this->y(array_pop($points)); - $x = array_pop($points); - $this->_pdf->moveto($x,$y); - - while (count($points) > 1) { - $y = $this->y(array_pop($points)); - $x = array_pop($points); - $this->_pdf->lineto($x,$y); - } - - if ( $fill ) - $this->_pdf->fill(); - else - $this->_pdf->closepath_stroke(); - } - - //........................................................................ - - function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) { - - $this->_set_fill_color($color); - $this->_set_stroke_color($color); - - if ( !$fill && isset($width) ) - $this->_set_line_style($width, "round", "round", $style); - - $y = $this->y($y); - - $this->_pdf->circle($x, $y, $r); - - if ( $fill ) - $this->_pdf->fill(); - else - $this->_pdf->stroke(); - - } - - //........................................................................ - - function image($img_url, $x, $y, $w, $h, $resolution = "normal") { - $w = (int)$w; - $h = (int)$h; - - $img_type = Image_Cache::detect_type($img_url, $this->_dompdf->get_http_context()); - $img_ext = Image_Cache::type_to_ext($img_type); - - if ( !isset($this->_imgs[$img_url]) ) { - $this->_imgs[$img_url] = $this->_pdf->load_image($img_ext, $img_url, ""); - } - - $img = $this->_imgs[$img_url]; - - $y = $this->y($y) - $h; - $this->_pdf->fit_image($img, $x, $y, 'boxsize={'."$w $h".'} fitmethod=entire'); - } - - //........................................................................ - - function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_spacing = 0, $char_spacing = 0, $angle = 0) { - $fh = $this->_load_font($font); - - $this->_pdf->setfont($fh, $size); - $this->_set_fill_color($color); - - $y = $this->y($y) - Font_Metrics::get_font_height($font, $size); - - $word_spacing = (float)$word_spacing; - $char_spacing = (float)$char_spacing; - $angle = -(float)$angle; - - $this->_pdf->fit_textline($text, $x, $y, "rotate=$angle wordspacing=$word_spacing charspacing=$char_spacing "); - - } - - //........................................................................ - - function javascript($code) { - if ( defined("DOMPDF_PDFLIB_LICENSE") ) { - $this->_pdf->create_action("JavaScript", $code); - } - } - - //........................................................................ - - /** - * Add a named destination (similar to ... in html) - * - * @param string $anchorname The name of the named destination - */ - function add_named_dest($anchorname) { - $this->_pdf->add_nameddest($anchorname,""); - } - - //........................................................................ - - /** - * Add a link to the pdf - * - * @param string $url The url to link to - * @param float $x The x position of the link - * @param float $y The y position of the link - * @param float $width The width of the link - * @param float $height The height of the link - */ - function add_link($url, $x, $y, $width, $height) { - - $y = $this->y($y) - $height; - if ( strpos($url, '#') === 0 ) { - // Local link - $name = substr($url,1); - if ( $name ) - $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} destname=". substr($url,1) . " linewidth=0"); - } else { - - list($proto, $host, $path, $file) = explode_url($url); - - if ( $proto == "" || $proto === "file://" ) - return; // Local links are not allowed - $url = build_url($proto, $host, $path, $file); - $url = '{' . rawurldecode($url) . '}'; - - $action = $this->_pdf->create_action("URI", "url=" . $url); - $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} action={activate=$action} linewidth=0"); - } - } - - //........................................................................ - - function get_text_width($text, $font, $size, $word_spacing = 0, $letter_spacing = 0) { - $fh = $this->_load_font($font); - - // Determine the additional width due to extra spacing - $num_spaces = mb_substr_count($text," "); - $delta = $word_spacing * $num_spaces; - - if ( $letter_spacing ) { - $num_chars = mb_strlen($text); - $delta += ($num_chars - $num_spaces) * $letter_spacing; - } - - return $this->_pdf->stringwidth($text, $fh, $size) + $delta; - } - - //........................................................................ - - function get_font_height($font, $size) { - - $fh = $this->_load_font($font); - - $this->_pdf->setfont($fh, $size); - - $asc = $this->_pdf->get_value("ascender", $fh); - $desc = $this->_pdf->get_value("descender", $fh); - - // $desc is usually < 0, - $ratio = $this->_dompdf->get_option("font_height_ratio"); - return $size * ($asc - $desc) * $ratio; - } - - function get_font_baseline($font, $size) { - $ratio = $this->_dompdf->get_option("font_height_ratio"); - return $this->get_font_height($font, $size) / $ratio * 1.1; - } - - //........................................................................ - - /** - * Writes text at the specified x and y coordinates on every page - * - * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced - * with their current values. - * - * See {@link Style::munge_color()} for the format of the color array. - * - * @param float $x - * @param float $y - * @param string $text the text to write - * @param string $font the font file to use - * @param float $size the font size, in points - * @param array $color - * @param float $word_space word spacing adjustment - * @param float $char_space char spacing adjustment - * @param float $angle angle to write the text at, measured CW starting from the x-axis - */ - function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) { - $_t = "text"; - $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle"); - } - - //........................................................................ - - /** - * Processes a script on every page - * - * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available. - * - * This function can be used to add page numbers to all pages - * after the first one, for example. - * - * @param string $code the script code - * @param string $type the language type for script - */ - function page_script($code, $type = "text/php") { - $_t = "script"; - $this->_page_text[] = compact("_t", "code", "type"); - } - - //........................................................................ - - function new_page() { - - // Add objects to the current page - $this->_place_objects(); - - $this->_pdf->suspend_page(""); - $this->_pdf->begin_page_ext($this->_width, $this->_height, ""); - $this->_page_number = ++$this->_page_count; - - } - - //........................................................................ - - /** - * Add text to each page after rendering is complete - */ - protected function _add_page_text() { - - if ( !count($this->_page_text) ) - return; - - $this->_pdf->suspend_page(""); - - for ($p = 1; $p <= $this->_page_count; $p++) { - $this->_pdf->resume_page("pagenumber=$p"); - - foreach ($this->_page_text as $pt) { - extract($pt); - - switch ($_t) { - - case "text": - $text = str_replace(array("{PAGE_NUM}","{PAGE_COUNT}"), - array($p, $this->_page_count), $text); - $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle); - break; - - case "script": - if (!$eval) { - $eval = new PHP_Evaluator($this); - } - $eval->evaluate($code, array('PAGE_NUM' => $p, 'PAGE_COUNT' => $this->_page_count)); - break; - } - } - - $this->_pdf->suspend_page(""); - } - - $this->_pdf->resume_page("pagenumber=".$this->_page_number); - } - - //........................................................................ - - function stream($filename, $options = null) { - - // Add page text - $this->_add_page_text(); - - if ( isset($options["compress"]) && $options["compress"] != 1 ) - $this->_pdf->set_value("compress", 0); - else - $this->_pdf->set_value("compress", 6); - - $this->_close(); - - $data = ""; - - if ( self::$IN_MEMORY ) { - $data = $this->_pdf->get_buffer(); - //$size = strlen($data); - } else { - //$size = filesize($this->_file); - } - - - $filename = str_replace(array("\n","'"),"", $filename); - $attach = (isset($options["Attachment"]) && $options["Attachment"]) ? "attachment" : "inline"; - - header("Cache-Control: private"); - header("Content-type: application/pdf"); - header("Content-Disposition: $attach; filename=\"$filename\""); - - //header("Content-length: " . $size); - - if ( self::$IN_MEMORY ) - echo $data; - - else { - - // Chunked readfile() - $chunk = (1 << 21); // 2 MB - $fh = fopen($this->_file, "rb"); - if ( !$fh ) - throw new DOMPDF_Exception("Unable to load temporary PDF file: " . $this->_file); - - while ( !feof($fh) ) - echo fread($fh,$chunk); - fclose($fh); - - //debugpng - if (DEBUGPNG) print '[pdflib stream unlink '.$this->_file.']'; - if (!DEBUGKEEPTEMP) - - unlink($this->_file); - $this->_file = null; - unset($this->_file); - } - - flush(); - } - - //........................................................................ - - function output($options = null) { - - // Add page text - $this->_add_page_text(); - - if ( isset($options["compress"]) && $options["compress"] != 1 ) - $this->_pdf->set_value("compress", 0); - else - $this->_pdf->set_value("compress", 6); - - $this->_close(); - - if ( self::$IN_MEMORY ) - $data = $this->_pdf->get_buffer(); - - else { - $data = file_get_contents($this->_file); - - //debugpng - if (DEBUGPNG) print '[pdflib output unlink '.$this->_file.']'; - if (!DEBUGKEEPTEMP) - - unlink($this->_file); - $this->_file = null; - unset($this->_file); - } - - return $data; - } -} - -// Workaround for idiotic limitation on statics... -PDFLib_Adapter::$PAPER_SIZES = CPDF_Adapter::$PAPER_SIZES; diff --git a/library/vendor/dompdf/include/php_evaluator.cls.php b/library/vendor/dompdf/include/php_evaluator.cls.php deleted file mode 100644 index 38896675d..000000000 --- a/library/vendor/dompdf/include/php_evaluator.cls.php +++ /dev/null @@ -1,48 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Executes inline PHP code during the rendering process - * - * @access private - * @package dompdf - */ -class PHP_Evaluator { - - /** - * @var Canvas - */ - protected $_canvas; - - function __construct(Canvas $canvas) { - $this->_canvas = $canvas; - } - - function evaluate($code, $vars = array()) { - if ( !$this->_canvas->get_dompdf()->get_option("enable_php") ) { - return; - } - - // Set up some variables for the inline code - $pdf = $this->_canvas; - $PAGE_NUM = $pdf->get_page_number(); - $PAGE_COUNT = $pdf->get_page_count(); - - // Override those variables if passed in - foreach ($vars as $k => $v) { - $$k = $v; - } - - //$code = html_entity_decode($code); // @todo uncomment this when tested - eval($code); - } - - function render(Frame $frame) { - $this->evaluate($frame->get_node()->nodeValue); - } -} diff --git a/library/vendor/dompdf/include/positioner.cls.php b/library/vendor/dompdf/include/positioner.cls.php deleted file mode 100644 index 6a8b9edc1..000000000 --- a/library/vendor/dompdf/include/positioner.cls.php +++ /dev/null @@ -1,51 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Base Positioner class - * - * Defines postioner interface - * - * @access private - * @package dompdf - */ -abstract class Positioner { - - /** - * @var Frame_Decorator - */ - protected $_frame; - - //........................................................................ - - function __construct(Frame_Decorator $frame) { - $this->_frame = $frame; - } - - /** - * Class destructor - */ - function __destruct() { - clear_object($this); - } - //........................................................................ - - abstract function position(); - - function move($offset_x, $offset_y, $ignore_self = false) { - list($x, $y) = $this->_frame->get_position(); - - if ( !$ignore_self ) { - $this->_frame->set_position($x + $offset_x, $y + $offset_y); - } - - foreach($this->_frame->get_children() as $child) { - $child->move($offset_x, $offset_y); - } - } -} diff --git a/library/vendor/dompdf/include/renderer.cls.php b/library/vendor/dompdf/include/renderer.cls.php deleted file mode 100644 index ceff4775c..000000000 --- a/library/vendor/dompdf/include/renderer.cls.php +++ /dev/null @@ -1,290 +0,0 @@ - - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Concrete renderer - * - * Instantiates several specific renderers in order to render any given - * frame. - * - * @access private - * @package dompdf - */ -class Renderer extends Abstract_Renderer { - - /** - * Array of renderers for specific frame types - * - * @var Abstract_Renderer[] - */ - protected $_renderers; - - /** - * Cache of the callbacks array - * - * @var array - */ - private $_callbacks; - - /** - * Class destructor - */ - function __destruct() { - clear_object($this); - } - - /** - * Advance the canvas to the next page - */ - function new_page() { - $this->_canvas->new_page(); - } - - /** - * Render frames recursively - * - * @param Frame $frame the frame to render - */ - function render(Frame $frame) { - global $_dompdf_debug; - - if ( $_dompdf_debug ) { - echo $frame; - flush(); - } - - $style = $frame->get_style(); - - if ( in_array($style->visibility, array("hidden", "collapse")) ) { - return; - } - - $display = $style->display; - - // Starts the CSS transformation - if ( $style->transform && is_array($style->transform) ) { - $this->_canvas->save(); - list($x, $y) = $frame->get_padding_box(); - $origin = $style->transform_origin; - - foreach($style->transform as $transform) { - list($function, $values) = $transform; - if ( $function === "matrix" ) { - $function = "transform"; - } - - $values = array_map("floatval", $values); - $values[] = $x + $style->length_in_pt($origin[0], $style->width); - $values[] = $y + $style->length_in_pt($origin[1], $style->height); - - call_user_func_array(array($this->_canvas, $function), $values); - } - } - - switch ($display) { - - case "block": - case "list-item": - case "inline-block": - case "table": - case "inline-table": - $this->_render_frame("block", $frame); - break; - - case "inline": - if ( $frame->is_text_node() ) - $this->_render_frame("text", $frame); - else - $this->_render_frame("inline", $frame); - break; - - case "table-cell": - $this->_render_frame("table-cell", $frame); - break; - - case "table-row-group": - case "table-header-group": - case "table-footer-group": - $this->_render_frame("table-row-group", $frame); - break; - - case "-dompdf-list-bullet": - $this->_render_frame("list-bullet", $frame); - break; - - case "-dompdf-image": - $this->_render_frame("image", $frame); - break; - - case "none": - $node = $frame->get_node(); - - if ( $node->nodeName === "script" ) { - if ( $node->getAttribute("type") === "text/php" || - $node->getAttribute("language") === "php" ) { - // Evaluate embedded php scripts - $this->_render_frame("php", $frame); - } - - elseif ( $node->getAttribute("type") === "text/javascript" || - $node->getAttribute("language") === "javascript" ) { - // Insert JavaScript - $this->_render_frame("javascript", $frame); - } - } - - // Don't render children, so skip to next iter - return; - - default: - break; - - } - - // Starts the overflow: hidden box - if ( $style->overflow === "hidden" ) { - list($x, $y, $w, $h) = $frame->get_padding_box(); - - // get border radii - $style = $frame->get_style(); - list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); - - if ( $tl + $tr + $br + $bl > 0 ) { - $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); - } - else { - $this->_canvas->clipping_rectangle($x, $y, $w, $h); - } - } - - $stack = array(); - - foreach ($frame->get_children() as $child) { - // < 0 : nagative z-index - // = 0 : no z-index, no stacking context - // = 1 : stacking context without z-index - // > 1 : z-index - $child_style = $child->get_style(); - $child_z_index = $child_style->z_index; - $z_index = 0; - - if ( $child_z_index !== "auto" ) { - $z_index = intval($child_z_index) + 1; - } - elseif ( $child_style->float !== "none" || $child->is_positionned()) { - $z_index = 1; - } - - $stack[$z_index][] = $child; - } - - ksort($stack); - - foreach ($stack as $by_index) { - foreach($by_index as $child) { - $this->render($child); - } - } - - // Ends the overflow: hidden box - if ( $style->overflow === "hidden" ) { - $this->_canvas->clipping_end(); - } - - if ( $style->transform && is_array($style->transform) ) { - $this->_canvas->restore(); - } - - // Check for end frame callback - $this->_check_callbacks("end_frame", $frame); - } - - /** - * Check for callbacks that need to be performed when a given event - * gets triggered on a frame - * - * @param string $event the type of event - * @param Frame $frame the frame that event is triggered on - */ - protected function _check_callbacks($event, $frame) { - if (!isset($this->_callbacks)) { - $this->_callbacks = $this->_dompdf->get_callbacks(); - } - - if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) { - $info = array(0 => $this->_canvas, "canvas" => $this->_canvas, - 1 => $frame, "frame" => $frame); - $fs = $this->_callbacks[$event]; - foreach ($fs as $f) { - if (is_callable($f)) { - if (is_array($f)) { - $f[0]->$f[1]($info); - } else { - $f($info); - } - } - } - } - } - - /** - * Render a single frame - * - * Creates Renderer objects on demand - * - * @param string $type type of renderer to use - * @param Frame $frame the frame to render - */ - protected function _render_frame($type, $frame) { - - if ( !isset($this->_renderers[$type]) ) { - - switch ($type) { - case "block": - $this->_renderers[$type] = new Block_Renderer($this->_dompdf); - break; - - case "inline": - $this->_renderers[$type] = new Inline_Renderer($this->_dompdf); - break; - - case "text": - $this->_renderers[$type] = new Text_Renderer($this->_dompdf); - break; - - case "image": - $this->_renderers[$type] = new Image_Renderer($this->_dompdf); - break; - - case "table-cell": - $this->_renderers[$type] = new Table_Cell_Renderer($this->_dompdf); - break; - - case "table-row-group": - $this->_renderers[$type] = new Table_Row_Group_Renderer($this->_dompdf); - break; - - case "list-bullet": - $this->_renderers[$type] = new List_Bullet_Renderer($this->_dompdf); - break; - - case "php": - $this->_renderers[$type] = new PHP_Evaluator($this->_canvas); - break; - - case "javascript": - $this->_renderers[$type] = new Javascript_Embedder($this->_dompdf); - break; - - } - } - - $this->_renderers[$type]->render($frame); - - } -} diff --git a/library/vendor/dompdf/include/style.cls.php b/library/vendor/dompdf/include/style.cls.php deleted file mode 100644 index d08fb4baa..000000000 --- a/library/vendor/dompdf/include/style.cls.php +++ /dev/null @@ -1,2435 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * Represents CSS properties. - * - * The Style class is responsible for handling and storing CSS properties. - * It includes methods to resolve colors and lengths, as well as getters & - * setters for many CSS properites. - * - * Actual CSS parsing is performed in the {@link Stylesheet} class. - * - * @package dompdf - */ -class Style { - - const CSS_IDENTIFIER = "-?[_a-zA-Z]+[_a-zA-Z0-9-]*"; - const CSS_INTEGER = "-?\d+"; - - /** - * Default font size, in points. - * - * @var float - */ - static $default_font_size = 12; - - /** - * Default line height, as a fraction of the font size. - * - * @var float - */ - static $default_line_height = 1.2; - - /** - * Default "absolute" font sizes relative to the default font-size - * http://www.w3.org/TR/css3-fonts/#font-size-the-font-size-property - * @var array - */ - static $font_size_keywords = array( - "xx-small" => 0.6, // 3/5 - "x-small" => 0.75, // 3/4 - "small" => 0.889, // 8/9 - "medium" => 1, // 1 - "large" => 1.2, // 6/5 - "x-large" => 1.5, // 3/2 - "xx-large" => 2.0, // 2/1 - ); - - /** - * List of all inline types. Should really be a constant. - * - * @var array - */ - static $INLINE_TYPES = array("inline"); - - /** - * List of all block types. Should really be a constant. - * - * @var array - */ - static $BLOCK_TYPES = array("block", "inline-block", "table-cell", "list-item"); - - /** - * List of all positionned types. Should really be a constant. - * - * @var array - */ - static $POSITIONNED_TYPES = array("relative", "absolute", "fixed"); - - /** - * List of all table types. Should really be a constant. - * - * @var array; - */ - static $TABLE_TYPES = array("table", "inline-table"); - - /** - * List of valid border styles. Should also really be a constant. - * - * @var array - */ - static $BORDER_STYLES = array("none", "hidden", "dotted", "dashed", "solid", - "double", "groove", "ridge", "inset", "outset"); - - /** - * Default style values. - * - * @link http://www.w3.org/TR/CSS21/propidx.html - * - * @var array - */ - static protected $_defaults = null; - - /** - * List of inherited properties - * - * @link http://www.w3.org/TR/CSS21/propidx.html - * - * @var array - */ - static protected $_inherited = null; - - /** - * Caches method_exists result - * - * @var array - */ - static protected $_methods_cache = array(); - - /** - * The stylesheet this style belongs to - * - * @see Stylesheet - * @var Stylesheet - */ - protected $_stylesheet; // stylesheet this style is attached to - - /** - * Main array of all CSS properties & values - * - * @var array - */ - protected $_props; - - /* var instead of protected would allow access outside of class */ - protected $_important_props; - - /** - * Cached property values - * - * @var array - */ - protected $_prop_cache; - - /** - * Font size of parent element in document tree. Used for relative font - * size resolution. - * - * @var float - */ - protected $_parent_font_size; // Font size of parent element - - protected $_font_family; - - /** - * @var Frame - */ - protected $_frame; - - /** - * The origin of the style - * - * @var int - */ - protected $_origin = Stylesheet::ORIG_AUTHOR; - - // private members - /** - * True once the font size is resolved absolutely - * - * @var bool - */ - private $__font_size_calculated; // Cache flag - - /** - * The computed border radius - */ - private $_computed_border_radius = null; - - /** - * @var bool - */ - public $_has_border_radius = false; - - /** - * Class constructor - * - * @param Stylesheet $stylesheet the stylesheet this Style is associated with. - * @param int $origin - */ - function __construct(Stylesheet $stylesheet, $origin = Stylesheet::ORIG_AUTHOR) { - $this->_props = array(); - $this->_important_props = array(); - $this->_stylesheet = $stylesheet; - $this->_origin = $origin; - $this->_parent_font_size = null; - $this->__font_size_calculated = false; - - if ( !isset(self::$_defaults) ) { - - // Shorthand - $d =& self::$_defaults; - - // All CSS 2.1 properties, and their default values - $d["azimuth"] = "center"; - $d["background_attachment"] = "scroll"; - $d["background_color"] = "transparent"; - $d["background_image"] = "none"; - $d["background_image_resolution"] = "normal"; - $d["_dompdf_background_image_resolution"] = $d["background_image_resolution"]; - $d["background_position"] = "0% 0%"; - $d["background_repeat"] = "repeat"; - $d["background"] = ""; - $d["border_collapse"] = "separate"; - $d["border_color"] = ""; - $d["border_spacing"] = "0"; - $d["border_style"] = ""; - $d["border_top"] = ""; - $d["border_right"] = ""; - $d["border_bottom"] = ""; - $d["border_left"] = ""; - $d["border_top_color"] = ""; - $d["border_right_color"] = ""; - $d["border_bottom_color"] = ""; - $d["border_left_color"] = ""; - $d["border_top_style"] = "none"; - $d["border_right_style"] = "none"; - $d["border_bottom_style"] = "none"; - $d["border_left_style"] = "none"; - $d["border_top_width"] = "medium"; - $d["border_right_width"] = "medium"; - $d["border_bottom_width"] = "medium"; - $d["border_left_width"] = "medium"; - $d["border_width"] = "medium"; - $d["border_bottom_left_radius"] = ""; - $d["border_bottom_right_radius"] = ""; - $d["border_top_left_radius"] = ""; - $d["border_top_right_radius"] = ""; - $d["border_radius"] = ""; - $d["border"] = ""; - $d["bottom"] = "auto"; - $d["caption_side"] = "top"; - $d["clear"] = "none"; - $d["clip"] = "auto"; - $d["color"] = "#000000"; - $d["content"] = "normal"; - $d["counter_increment"] = "none"; - $d["counter_reset"] = "none"; - $d["cue_after"] = "none"; - $d["cue_before"] = "none"; - $d["cue"] = ""; - $d["cursor"] = "auto"; - $d["direction"] = "ltr"; - $d["display"] = "inline"; - $d["elevation"] = "level"; - $d["empty_cells"] = "show"; - $d["float"] = "none"; - $d["font_family"] = $stylesheet->get_dompdf()->get_option("default_font"); - $d["font_size"] = "medium"; - $d["font_style"] = "normal"; - $d["font_variant"] = "normal"; - $d["font_weight"] = "normal"; - $d["font"] = ""; - $d["height"] = "auto"; - $d["image_resolution"] = "normal"; - $d["_dompdf_image_resolution"] = $d["image_resolution"]; - $d["_dompdf_keep"] = ""; - $d["left"] = "auto"; - $d["letter_spacing"] = "normal"; - $d["line_height"] = "normal"; - $d["list_style_image"] = "none"; - $d["list_style_position"] = "outside"; - $d["list_style_type"] = "disc"; - $d["list_style"] = ""; - $d["margin_right"] = "0"; - $d["margin_left"] = "0"; - $d["margin_top"] = "0"; - $d["margin_bottom"] = "0"; - $d["margin"] = ""; - $d["max_height"] = "none"; - $d["max_width"] = "none"; - $d["min_height"] = "0"; - $d["min_width"] = "0"; - $d["opacity"] = "1.0"; // CSS3 - $d["orphans"] = "2"; - $d["outline_color"] = ""; // "invert" special color is not supported - $d["outline_style"] = "none"; - $d["outline_width"] = "medium"; - $d["outline"] = ""; - $d["overflow"] = "visible"; - $d["padding_top"] = "0"; - $d["padding_right"] = "0"; - $d["padding_bottom"] = "0"; - $d["padding_left"] = "0"; - $d["padding"] = ""; - $d["page_break_after"] = "auto"; - $d["page_break_before"] = "auto"; - $d["page_break_inside"] = "auto"; - $d["pause_after"] = "0"; - $d["pause_before"] = "0"; - $d["pause"] = ""; - $d["pitch_range"] = "50"; - $d["pitch"] = "medium"; - $d["play_during"] = "auto"; - $d["position"] = "static"; - $d["quotes"] = ""; - $d["richness"] = "50"; - $d["right"] = "auto"; - $d["size"] = "auto"; // @page - $d["speak_header"] = "once"; - $d["speak_numeral"] = "continuous"; - $d["speak_punctuation"] = "none"; - $d["speak"] = "normal"; - $d["speech_rate"] = "medium"; - $d["stress"] = "50"; - $d["table_layout"] = "auto"; - $d["text_align"] = "left"; - $d["text_decoration"] = "none"; - $d["text_indent"] = "0"; - $d["text_transform"] = "none"; - $d["top"] = "auto"; - $d["transform"] = "none"; // CSS3 - $d["transform_origin"] = "50% 50%"; // CSS3 - $d["_webkit_transform"] = $d["transform"]; // CSS3 - $d["_webkit_transform_origin"] = $d["transform_origin"]; // CSS3 - $d["unicode_bidi"] = "normal"; - $d["vertical_align"] = "baseline"; - $d["visibility"] = "visible"; - $d["voice_family"] = ""; - $d["volume"] = "medium"; - $d["white_space"] = "normal"; - $d["word_wrap"] = "normal"; - $d["widows"] = "2"; - $d["width"] = "auto"; - $d["word_spacing"] = "normal"; - $d["z_index"] = "auto"; - - // for @font-face - $d["src"] = ""; - $d["unicode_range"] = ""; - - // Properties that inherit by default - self::$_inherited = array( - "azimuth", - "background_image_resolution", - "border_collapse", - "border_spacing", - "caption_side", - "color", - "cursor", - "direction", - "elevation", - "empty_cells", - "font_family", - "font_size", - "font_style", - "font_variant", - "font_weight", - "font", - "image_resolution", - "letter_spacing", - "line_height", - "list_style_image", - "list_style_position", - "list_style_type", - "list_style", - "orphans", - "page_break_inside", - "pitch_range", - "pitch", - "quotes", - "richness", - "speak_header", - "speak_numeral", - "speak_punctuation", - "speak", - "speech_rate", - "stress", - "text_align", - "text_indent", - "text_transform", - "visibility", - "voice_family", - "volume", - "white_space", - "word_wrap", - "widows", - "word_spacing", - ); - } - } - - /** - * "Destructor": forcibly free all references held by this object - */ - function dispose() { - clear_object($this); - } - - function set_frame(Frame $frame) { - $this->_frame = $frame; - } - - function get_frame() { - return $this->_frame; - } - - function set_origin($origin) { - $this->_origin = $origin; - } - - function get_origin() { - return $this->_origin; - } - - /** - * returns the {@link Stylesheet} this Style is associated with. - * - * @return Stylesheet - */ - function get_stylesheet() { return $this->_stylesheet; } - - /** - * Converts any CSS length value into an absolute length in points. - * - * length_in_pt() takes a single length (e.g. '1em') or an array of - * lengths and returns an absolute length. If an array is passed, then - * the return value is the sum of all elements. - * - * If a reference size is not provided, the default font size is used - * ({@link Style::$default_font_size}). - * - * @param float|array $length the length or array of lengths to resolve - * @param float $ref_size an absolute reference size to resolve percentage lengths - * @return float - */ - function length_in_pt($length, $ref_size = null) { - static $cache = array(); - - if ( !is_array($length) ) { - $length = array($length); - } - - if ( !isset($ref_size) ) { - $ref_size = self::$default_font_size; - } - - $key = implode("@", $length)."/$ref_size"; - - if ( isset($cache[$key]) ) { - return $cache[$key]; - } - - $ret = 0; - foreach ($length as $l) { - - if ( $l === "auto" ) { - return "auto"; - } - - if ( $l === "none" ) { - return "none"; - } - - // Assume numeric values are already in points - if ( is_numeric($l) ) { - $ret += $l; - continue; - } - - if ( $l === "normal" ) { - $ret += $ref_size; - continue; - } - - // Border lengths - if ( $l === "thin" ) { - $ret += 0.5; - continue; - } - - if ( $l === "medium" ) { - $ret += 1.5; - continue; - } - - if ( $l === "thick" ) { - $ret += 2.5; - continue; - } - - if ( ($i = mb_strpos($l, "px")) !== false ) { - $dpi = $this->_stylesheet->get_dompdf()->get_option("dpi"); - $ret += ( mb_substr($l, 0, $i) * 72 ) / $dpi; - continue; - } - - if ( ($i = mb_strpos($l, "pt")) !== false ) { - $ret += (float)mb_substr($l, 0, $i); - continue; - } - - if ( ($i = mb_strpos($l, "%")) !== false ) { - $ret += (float)mb_substr($l, 0, $i)/100 * $ref_size; - continue; - } - - if ( ($i = mb_strpos($l, "rem")) !== false ) { - $ret += (float)mb_substr($l, 0, $i) * $this->_stylesheet->get_dompdf()->get_tree()->get_root()->get_style()->font_size; - continue; - } - - if ( ($i = mb_strpos($l, "em")) !== false ) { - $ret += (float)mb_substr($l, 0, $i) * $this->__get("font_size"); - continue; - } - - if ( ($i = mb_strpos($l, "cm")) !== false ) { - $ret += mb_substr($l, 0, $i) * 72 / 2.54; - continue; - } - - if ( ($i = mb_strpos($l, "mm")) !== false ) { - $ret += mb_substr($l, 0, $i) * 72 / 25.4; - continue; - } - - // FIXME: em:ex ratio? - if ( ($i = mb_strpos($l, "ex")) !== false ) { - $ret += mb_substr($l, 0, $i) * $this->__get("font_size") / 2; - continue; - } - - if ( ($i = mb_strpos($l, "in")) !== false ) { - $ret += (float)mb_substr($l, 0, $i) * 72; - continue; - } - - if ( ($i = mb_strpos($l, "pc")) !== false ) { - $ret += (float)mb_substr($l, 0, $i) * 12; - continue; - } - - // Bogus value - $ret += $ref_size; - } - - return $cache[$key] = $ret; - } - - - /** - * Set inherited properties in this style using values in $parent - * - * @param Style $parent - * - * @return Style - */ - function inherit(Style $parent) { - - // Set parent font size - $this->_parent_font_size = $parent->get_font_size(); - - foreach (self::$_inherited as $prop) { - //inherit the !important property also. - //if local property is also !important, don't inherit. - if ( isset($parent->_props[$prop]) && - ( !isset($this->_props[$prop]) || - ( isset($parent->_important_props[$prop]) && !isset($this->_important_props[$prop]) ) - ) - ) { - if ( isset($parent->_important_props[$prop]) ) { - $this->_important_props[$prop] = true; - } - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - $this->_props[$prop] = $parent->_props[$prop]; - } - } - - foreach ($this->_props as $prop => $value) { - if ( $value === "inherit" ) { - if ( isset($parent->_important_props[$prop]) ) { - $this->_important_props[$prop] = true; - } - //do not assign direct, but - //implicite assignment through __set, redirect to specialized, get value with __get - //This is for computing defaults if the parent setting is also missing. - //Therefore do not directly assign the value without __set - //set _important_props before that to be able to propagate. - //see __set and __get, on all assignments clear cache! - //$this->_prop_cache[$prop] = null; - //$this->_props[$prop] = $parent->_props[$prop]; - //props_set for more obvious explicite assignment not implemented, because - //too many implicite uses. - // $this->props_set($prop, $parent->$prop); - $this->__set($prop, $parent->__get($prop)); - } - } - - return $this; - } - - /** - * Override properties in this style with those in $style - * - * @param Style $style - */ - function merge(Style $style) { - //treat the !important attribute - //if old rule has !important attribute, override with new rule only if - //the new rule is also !important - foreach($style->_props as $prop => $val ) { - if (isset($style->_important_props[$prop])) { - $this->_important_props[$prop] = true; - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - $this->_props[$prop] = $val; - } - else if ( !isset($this->_important_props[$prop]) ) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - $this->_props[$prop] = $val; - } - } - - if ( isset($style->_props["font_size"]) ) { - $this->__font_size_calculated = false; - } - } - - /** - * Returns an array(r, g, b, "r"=> r, "g"=>g, "b"=>b, "hex"=>"#rrggbb") - * based on the provided CSS color value. - * - * @param string $color - * @return array - */ - function munge_color($color) { - return CSS_Color::parse($color); - } - - /* direct access to _important_props array from outside would work only when declared as - * 'var $_important_props;' instead of 'protected $_important_props;' - * Don't call _set/__get on missing attribute. Therefore need a special access. - * Assume that __set will be also called when this is called, so do not check validity again. - * Only created, if !important exists -> always set true. - */ - function important_set($prop) { - $prop = str_replace("-", "_", $prop); - $this->_important_props[$prop] = true; - } - - function important_get($prop) { - return isset($this->_important_props[$prop]); - } - - /** - * PHP5 overloaded setter - * - * This function along with {@link Style::__get()} permit a user of the - * Style class to access any (CSS) property using the following syntax: - * - * Style->margin_top = "1em"; - * echo (Style->margin_top); - * - * - * __set() automatically calls the provided set function, if one exists, - * otherwise it sets the property directly. Typically, __set() is not - * called directly from outside of this class. - * - * On each modification clear cache to return accurate setting. - * Also affects direct settings not using __set - * For easier finding all assignments, attempted to allowing only explicite assignment: - * Very many uses, e.g. frame_reflower.cls.php -> for now leave as it is - * function __set($prop, $val) { - * throw new DOMPDF_Exception("Implicite replacement of assignment by __set. Not good."); - * } - * function props_set($prop, $val) { ... } - * - * @param string $prop the property to set - * @param mixed $val the value of the property - * - */ - function __set($prop, $val) { - $prop = str_replace("-", "_", $prop); - $this->_prop_cache[$prop] = null; - - if ( !isset(self::$_defaults[$prop]) ) { - global $_dompdf_warnings; - $_dompdf_warnings[] = "'$prop' is not a valid CSS2 property."; - return; - } - - if ( $prop !== "content" && is_string($val) && strlen($val) > 5 && mb_strpos($val, "url") === false ) { - $val = mb_strtolower(trim(str_replace(array("\n", "\t"), array(" "), $val))); - $val = preg_replace("/([0-9]+) (pt|px|pc|em|ex|in|cm|mm|%)/S", "\\1\\2", $val); - } - - $method = "set_$prop"; - - if ( !isset(self::$_methods_cache[$method]) ) { - self::$_methods_cache[$method] = method_exists($this, $method); - } - - if ( self::$_methods_cache[$method] ) { - $this->$method($val); - } - else { - $this->_props[$prop] = $val; - } - } - - /** - * PHP5 overloaded getter - * Along with {@link Style::__set()} __get() provides access to all CSS - * properties directly. Typically __get() is not called directly outside - * of this class. - * On each modification clear cache to return accurate setting. - * Also affects direct settings not using __set - * - * @param string $prop - * - * @throws DOMPDF_Exception - * @return mixed - */ - function __get($prop) { - if ( !isset(self::$_defaults[$prop]) ) { - throw new DOMPDF_Exception("'$prop' is not a valid CSS2 property."); - } - - if ( isset($this->_prop_cache[$prop]) && $this->_prop_cache[$prop] != null ) { - return $this->_prop_cache[$prop]; - } - - $method = "get_$prop"; - - // Fall back on defaults if property is not set - if ( !isset($this->_props[$prop]) ) { - $this->_props[$prop] = self::$_defaults[$prop]; - } - - if ( !isset(self::$_methods_cache[$method]) ) { - self::$_methods_cache[$method] = method_exists($this, $method); - } - - if ( self::$_methods_cache[$method] ) { - return $this->_prop_cache[$prop] = $this->$method(); - } - - return $this->_prop_cache[$prop] = $this->_props[$prop]; - } - - function get_font_family_raw(){ - return trim($this->_props["font_family"], " \t\n\r\x0B\"'"); - } - - /** - * Getter for the 'font-family' CSS property. - * Uses the {@link Font_Metrics} class to resolve the font family into an - * actual font file. - * - * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-family - * @throws DOMPDF_Exception - * - * @return string - */ - function get_font_family() { - if ( isset($this->_font_family) ) { - return $this->_font_family; - } - - $DEBUGCSS=DEBUGCSS; //=DEBUGCSS; Allow override of global setting for ad hoc debug - - // Select the appropriate font. First determine the subtype, then check - // the specified font-families for a candidate. - - // Resolve font-weight - $weight = $this->__get("font_weight"); - - if ( is_numeric($weight) ) { - if ( $weight < 600 ) { - $weight = "normal"; - } - else { - $weight = "bold"; - } - } - else if ( $weight === "bold" || $weight === "bolder" ) { - $weight = "bold"; - } - else { - $weight = "normal"; - } - - // Resolve font-style - $font_style = $this->__get("font_style"); - - if ( $weight === "bold" && ($font_style === "italic" || $font_style === "oblique") ) { - $subtype = "bold_italic"; - } - else if ( $weight === "bold" && $font_style !== "italic" && $font_style !== "oblique" ) { - $subtype = "bold"; - } - else if ( $weight !== "bold" && ($font_style === "italic" || $font_style === "oblique") ) { - $subtype = "italic"; - } - else { - $subtype = "normal"; - } - - // Resolve the font family - if ( $DEBUGCSS ) { - print "
[get_font_family:";
-      print '('.$this->_props["font_family"].'.'.$font_style.'.'.$this->__get("font_weight").'.'.$weight.'.'.$subtype.')';
-    }
-    
-    $families = preg_split("/\s*,\s*/", $this->_props["font_family"]);
-
-    $font = null;
-    foreach($families as $family) {
-      //remove leading and trailing string delimiters, e.g. on font names with spaces;
-      //remove leading and trailing whitespace
-      $family = trim($family, " \t\n\r\x0B\"'");
-      if ( $DEBUGCSS ) {
-        print '('.$family.')';
-      }
-      $font = Font_Metrics::get_font($family, $subtype);
-
-      if ( $font ) {
-        if ($DEBUGCSS) print '('.$font.")get_font_family]\n
"; - return $this->_font_family = $font; - } - } - - $family = null; - if ( $DEBUGCSS ) { - print '(default)'; - } - $font = Font_Metrics::get_font($family, $subtype); - - if ( $font ) { - if ( $DEBUGCSS ) print '('.$font.")get_font_family]\n"; - return$this->_font_family = $font; - } - - throw new DOMPDF_Exception("Unable to find a suitable font replacement for: '" . $this->_props["font_family"] ."'"); - - } - - /** - * Returns the resolved font size, in points - * - * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size - * @return float - */ - function get_font_size() { - - if ( $this->__font_size_calculated ) { - return $this->_props["font_size"]; - } - - if ( !isset($this->_props["font_size"]) ) { - $fs = self::$_defaults["font_size"]; - } - else { - $fs = $this->_props["font_size"]; - } - - if ( !isset($this->_parent_font_size) ) { - $this->_parent_font_size = self::$default_font_size; - } - - switch ((string)$fs) { - case "xx-small": - case "x-small": - case "small": - case "medium": - case "large": - case "x-large": - case "xx-large": - $fs = self::$default_font_size * self::$font_size_keywords[$fs]; - break; - - case "smaller": - $fs = 8/9 * $this->_parent_font_size; - break; - - case "larger": - $fs = 6/5 * $this->_parent_font_size; - break; - - default: - break; - } - - // Ensure relative sizes resolve to something - if ( ($i = mb_strpos($fs, "em")) !== false ) { - $fs = mb_substr($fs, 0, $i) * $this->_parent_font_size; - } - else if ( ($i = mb_strpos($fs, "ex")) !== false ) { - $fs = mb_substr($fs, 0, $i) * $this->_parent_font_size; - } - else { - $fs = $this->length_in_pt($fs); - } - - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["font_size"] = null; - $this->_props["font_size"] = $fs; - $this->__font_size_calculated = true; - return $this->_props["font_size"]; - - } - - /** - * @link http://www.w3.org/TR/CSS21/text.html#propdef-word-spacing - * @return float - */ - function get_word_spacing() { - if ( $this->_props["word_spacing"] === "normal" ) { - return 0; - } - - return $this->_props["word_spacing"]; - } - - /** - * @link http://www.w3.org/TR/CSS21/text.html#propdef-letter-spacing - * @return float - */ - function get_letter_spacing() { - if ( $this->_props["letter_spacing"] === "normal" ) { - return 0; - } - - return $this->_props["letter_spacing"]; - } - - /** - * @link http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height - * @return float - */ - function get_line_height() { - $line_height = $this->_props["line_height"]; - - if ( $line_height === "normal" ) { - return self::$default_line_height * $this->get_font_size(); - } - - if ( is_numeric($line_height) ) { - return $this->length_in_pt( $line_height . "em", $this->get_font_size()); - } - - return $this->length_in_pt( $line_height, $this->_parent_font_size ); - } - - /** - * Returns the color as an array - * - * The array has the following format: - * array(r,g,b, "r" => r, "g" => g, "b" => b, "hex" => "#rrggbb") - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color - * @return array - */ - function get_color() { - return $this->munge_color( $this->_props["color"] ); - } - - /** - * Returns the background color as an array - * - * The returned array has the same format as {@link Style::get_color()} - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color - * @return array - */ - function get_background_color() { - return $this->munge_color( $this->_props["background_color"] ); - } - - /** - * Returns the background position as an array - * - * The returned array has the following format: - * array(x,y, "x" => x, "y" => y) - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position - * @return array - */ - function get_background_position() { - $tmp = explode(" ", $this->_props["background_position"]); - - switch ($tmp[0]) { - case "left": - $x = "0%"; - break; - - case "right": - $x = "100%"; - break; - - case "top": - $y = "0%"; - break; - - case "bottom": - $y = "100%"; - break; - - case "center": - $x = "50%"; - $y = "50%"; - break; - - default: - $x = $tmp[0]; - break; - } - - if ( isset($tmp[1]) ) { - - switch ($tmp[1]) { - case "left": - $x = "0%"; - break; - - case "right": - $x = "100%"; - break; - - case "top": - $y = "0%"; - break; - - case "bottom": - $y = "100%"; - break; - - case "center": - if ( $tmp[0] === "left" || $tmp[0] === "right" || $tmp[0] === "center" ) { - $y = "50%"; - } - else { - $x = "50%"; - } - break; - - default: - $y = $tmp[1]; - break; - } - - } - else { - $y = "50%"; - } - - if ( !isset($x) ) { - $x = "0%"; - } - - if ( !isset($y) ) { - $y = "0%"; - } - - return array( - 0 => $x, "x" => $x, - 1 => $y, "y" => $y, - ); - } - - - /** - * Returns the background as it is currently stored - * - * (currently anyway only for completeness. - * not used for further processing) - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment - * @return string - */ - function get_background_attachment() { - return $this->_props["background_attachment"]; - } - - - /** - * Returns the background_repeat as it is currently stored - * - * (currently anyway only for completeness. - * not used for further processing) - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat - * @return string - */ - function get_background_repeat() { - return $this->_props["background_repeat"]; - } - - - /** - * Returns the background as it is currently stored - * - * (currently anyway only for completeness. - * not used for further processing, but the individual get_background_xxx) - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background - * @return string - */ - function get_background() { - return $this->_props["background"]; - } - - - /**#@+ - * Returns the border color as an array - * - * See {@link Style::get_color()} - * - * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties - * @return array - */ - function get_border_top_color() { - if ( $this->_props["border_top_color"] === "" ) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_top_color"] = null; - $this->_props["border_top_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["border_top_color"]); - } - - function get_border_right_color() { - if ( $this->_props["border_right_color"] === "" ) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_right_color"] = null; - $this->_props["border_right_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["border_right_color"]); - } - - function get_border_bottom_color() { - if ( $this->_props["border_bottom_color"] === "" ) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_bottom_color"] = null; - $this->_props["border_bottom_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["border_bottom_color"]); - } - - function get_border_left_color() { - if ( $this->_props["border_left_color"] === "" ) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_left_color"] = null; - $this->_props["border_left_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["border_left_color"]); - } - - /**#@-*/ - - /**#@+ - * Returns the border width, as it is currently stored - * - * @link http://www.w3.org/TR/CSS21/box.html#border-width-properties - * @return float|string - */ - function get_border_top_width() { - $style = $this->__get("border_top_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_top_width"]) : 0; - } - - function get_border_right_width() { - $style = $this->__get("border_right_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_right_width"]) : 0; - } - - function get_border_bottom_width() { - $style = $this->__get("border_bottom_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_bottom_width"]) : 0; - } - - function get_border_left_width() { - $style = $this->__get("border_left_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_left_width"]) : 0; - } - /**#@-*/ - - /** - * Return an array of all border properties. - * - * The returned array has the following structure: - * - * array("top" => array("width" => [border-width], - * "style" => [border-style], - * "color" => [border-color (array)]), - * "bottom" ... ) - * - * - * @return array - */ - function get_border_properties() { - return array( - "top" => array( - "width" => $this->__get("border_top_width"), - "style" => $this->__get("border_top_style"), - "color" => $this->__get("border_top_color"), - ), - "bottom" => array( - "width" => $this->__get("border_bottom_width"), - "style" => $this->__get("border_bottom_style"), - "color" => $this->__get("border_bottom_color"), - ), - "right" => array( - "width" => $this->__get("border_right_width"), - "style" => $this->__get("border_right_style"), - "color" => $this->__get("border_right_color"), - ), - "left" => array( - "width" => $this->__get("border_left_width"), - "style" => $this->__get("border_left_style"), - "color" => $this->__get("border_left_color"), - ), - ); - } - - /** - * Return a single border property - * - * @param string $side - * - * @return mixed - */ - protected function _get_border($side) { - $color = $this->__get("border_" . $side . "_color"); - - return $this->__get("border_" . $side . "_width") . " " . - $this->__get("border_" . $side . "_style") . " " . $color["hex"]; - } - - /**#@+ - * Return full border properties as a string - * - * Border properties are returned just as specified in CSS: - *
[width] [style] [color]
- * e.g. "1px solid blue" - * - * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties - * @return string - */ - function get_border_top() { - return $this->_get_border("top"); - } - - function get_border_right() { - return $this->_get_border("right"); - } - - function get_border_bottom() { - return $this->_get_border("bottom"); - } - - function get_border_left() { - return $this->_get_border("left"); - } - /**#@-*/ - - function get_computed_border_radius($w, $h) { - if ( !empty($this->_computed_border_radius) ) { - return $this->_computed_border_radius; - } - - $rTL = $this->__get("border_top_left_radius"); - $rTR = $this->__get("border_top_right_radius"); - $rBL = $this->__get("border_bottom_left_radius"); - $rBR = $this->__get("border_bottom_right_radius"); - - if ( $rTL + $rTR + $rBL + $rBR == 0 ) { - return $this->_computed_border_radius = array( - 0, 0, 0, 0, - "top-left" => 0, - "top-right" => 0, - "bottom-right" => 0, - "bottom-left" => 0, - ); - } - - $t = $this->__get("border_top_width"); - $r = $this->__get("border_right_width"); - $b = $this->__get("border_bottom_width"); - $l = $this->__get("border_left_width"); - - $rTL = min($rTL, $h - $rBL - $t/2 - $b/2, $w - $rTR - $l/2 - $r/2); - $rTR = min($rTR, $h - $rBR - $t/2 - $b/2, $w - $rTL - $l/2 - $r/2); - $rBL = min($rBL, $h - $rTL - $t/2 - $b/2, $w - $rBR - $l/2 - $r/2); - $rBR = min($rBR, $h - $rTR - $t/2 - $b/2, $w - $rBL - $l/2 - $r/2); - - return $this->_computed_border_radius = array( - $rTL, $rTR, $rBR, $rBL, - "top-left" => $rTL, - "top-right" => $rTR, - "bottom-right" => $rBR, - "bottom-left" => $rBL, - ); - } - /**#@-*/ - - - /** - * Returns the outline color as an array - * - * See {@link Style::get_color()} - * - * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties - * @return array - */ - function get_outline_color() { - if ( $this->_props["outline_color"] === "" ) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["outline_color"] = null; - $this->_props["outline_color"] = $this->__get("color"); - } - - return $this->munge_color($this->_props["outline_color"]); - } - - /**#@+ - * Returns the outline width, as it is currently stored - * @return float|string - */ - function get_outline_width() { - $style = $this->__get("outline_style"); - return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["outline_width"]) : 0; - } - - /**#@+ - * Return full outline properties as a string - * - * Outline properties are returned just as specified in CSS: - *
[width] [style] [color]
- * e.g. "1px solid blue" - * - * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties - * @return string - */ - function get_outline() { - $color = $this->__get("outline_color"); - return - $this->__get("outline_width") . " " . - $this->__get("outline_style") . " " . - $color["hex"]; - } - /**#@-*/ - - /** - * Returns border spacing as an array - * - * The array has the format (h_space,v_space) - * - * @link http://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing - * @return array - */ - function get_border_spacing() { - $arr = explode(" ", $this->_props["border_spacing"]); - if ( count($arr) == 1 ) { - $arr[1] = $arr[0]; - } - return $arr; - } - -/*==============================*/ - - /* - !important attribute - For basic functionality of the !important attribute with overloading - of several styles of an element, changes in inherit(), merge() and _parse_properties() - are sufficient [helpers var $_important_props, __construct(), important_set(), important_get()] - - Only for combined attributes extra treatment needed. See below. - - div { border: 1px red; } - div { border: solid; } // Not combined! Only one occurence of same style per context - // - div { border: 1px red; } - div a { border: solid; } // Adding to border style ok by inheritance - // - div { border-style: solid; } // Adding to border style ok because of different styles - div { border: 1px red; } - // - div { border-style: solid; !important} // border: overrides, even though not !important - div { border: 1px dashed red; } - // - div { border: 1px red; !important } - div a { border-style: solid; } // Need to override because not set - - Special treatment: - At individual property like border-top-width need to check whether overriding value is also !important. - Also store the !important condition for later overrides. - Since not known who is initiating the override, need to get passed !important as parameter. - !important Paramter taken as in the original style in the css file. - When property border !important given, do not mark subsets like border_style as important. Only - individual properties. - - Note: - Setting individual property directly from css with e.g. set_border_top_style() is not needed, because - missing set funcions handled by a generic handler __set(), including the !important. - Setting individual property of as sub-property is handled below. - - Implementation see at _set_style_side_type() - Callers _set_style_sides_type(), _set_style_type, _set_style_type_important() - - Related functionality for background, padding, margin, font, list_style - */ - - /* Generalized set function for individual attribute of combined style. - * With check for !important - * Applicable for background, border, padding, margin, font, list_style - * Note: $type has a leading underscore (or is empty), the others not. - */ - protected function _set_style_side_type($style, $side, $type, $val, $important) { - $prop = $style.'_'.$side.$type; - - if ( !isset($this->_important_props[$prop]) || $important) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - if ( $important ) { - $this->_important_props[$prop] = true; - } - $this->_props[$prop] = $val; - } - } - - protected function _set_style_sides_type($style,$top,$right,$bottom,$left,$type,$important) { - $this->_set_style_side_type($style,'top',$type,$top,$important); - $this->_set_style_side_type($style,'right',$type,$right,$important); - $this->_set_style_side_type($style,'bottom',$type,$bottom,$important); - $this->_set_style_side_type($style,'left',$type,$left,$important); - } - - protected function _set_style_type($style,$type,$val,$important) { - $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces - $arr = explode(" ", $val); - - switch (count($arr)) { - case 1: $this->_set_style_sides_type($style,$arr[0],$arr[0],$arr[0],$arr[0],$type,$important); break; - case 2: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[0],$arr[1],$type,$important); break; - case 3: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[2],$arr[1],$type,$important); break; - case 4: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[2],$arr[3],$type,$important); break; - } - - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$style.$type] = null; - $this->_props[$style.$type] = $val; - } - - protected function _set_style_type_important($style,$type,$val) { - $this->_set_style_type($style,$type,$val,isset($this->_important_props[$style.$type])); - } - - /* Anyway only called if _important matches and is assigned - * E.g. _set_style_side_type($style,$side,'',str_replace("none", "0px", $val),isset($this->_important_props[$style.'_'.$side])); - */ - protected function _set_style_side_width_important($style,$side,$val) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$style.'_'.$side] = null; - $this->_props[$style.'_'.$side] = str_replace("none", "0px", $val); - } - - protected function _set_style($style,$val,$important) { - if ( !isset($this->_important_props[$style]) || $important) { - if ( $important ) { - $this->_important_props[$style] = true; - } - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$style] = null; - $this->_props[$style] = $val; - } - } - - protected function _image($val) { - $DEBUGCSS=DEBUGCSS; - $parsed_url = "none"; - - if ( mb_strpos($val, "url") === false ) { - $path = "none"; //Don't resolve no image -> otherwise would prefix path and no longer recognize as none - } - else { - $val = preg_replace("/url\(['\"]?([^'\")]+)['\"]?\)/","\\1", trim($val)); - - // Resolve the url now in the context of the current stylesheet - $parsed_url = explode_url($val); - if ( $parsed_url["protocol"] == "" && $this->_stylesheet->get_protocol() == "" ) { - if ($parsed_url["path"][0] === '/' || $parsed_url["path"][0] === '\\' ) { - $path = $_SERVER["DOCUMENT_ROOT"].'/'; - } - else { - $path = $this->_stylesheet->get_base_path(); - } - - $path .= $parsed_url["path"] . $parsed_url["file"]; - $path = realpath($path); - // If realpath returns FALSE then specifically state that there is no background image - if ( !$path ) { - $path = 'none'; - } - } - else { - $path = build_url($this->_stylesheet->get_protocol(), - $this->_stylesheet->get_host(), - $this->_stylesheet->get_base_path(), - $val); - } - } - if ($DEBUGCSS) { - print "
[_image\n";
-      print_r($parsed_url);
-      print $this->_stylesheet->get_protocol()."\n".$this->_stylesheet->get_base_path()."\n".$path."\n";
-      print "_image]
";; - } - return $path; - } - -/*======================*/ - - /** - * Sets color - * - * The color parameter can be any valid CSS color value - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color - * @param string $color - */ - function set_color($color) { - $col = $this->munge_color($color); - - if ( is_null($col) || !isset($col["hex"]) ) { - $color = "inherit"; - } - else { - $color = $col["hex"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["color"] = null; - $this->_props["color"] = $color; - } - - /** - * Sets the background color - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color - * @param string $color - */ - function set_background_color($color) { - $col = $this->munge_color($color); - - if ( is_null($col) ) { - return; - //$col = self::$_defaults["background_color"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_color"] = null; - $this->_props["background_color"] = is_array($col) ? $col["hex"] : $col; - } - - /** - * Set the background image url - * @link http://www.w3.org/TR/CSS21/colors.html#background-properties - * - * @param string $val - */ - function set_background_image($val) { - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_image"] = null; - $this->_props["background_image"] = $this->_image($val); - } - - /** - * Sets the background repeat - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat - * @param string $val - */ - function set_background_repeat($val) { - if ( is_null($val) ) { - $val = self::$_defaults["background_repeat"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_repeat"] = null; - $this->_props["background_repeat"] = $val; - } - - /** - * Sets the background attachment - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment - * @param string $val - */ - function set_background_attachment($val) { - if ( is_null($val) ) { - $val = self::$_defaults["background_attachment"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_attachment"] = null; - $this->_props["background_attachment"] = $val; - } - - /** - * Sets the background position - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position - * @param string $val - */ - function set_background_position($val) { - if ( is_null($val) ) { - $val = self::$_defaults["background_position"]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background_position"] = null; - $this->_props["background_position"] = $val; - } - - /** - * Sets the background - combined options - * - * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background - * @param string $val - */ - function set_background($val) { - $val = trim($val); - $important = isset($this->_important_props["background"]); - - if ( $val === "none" ) { - $this->_set_style("background_image", "none", $important); - $this->_set_style("background_color", "transparent", $important); - } - else { - $pos = array(); - $tmp = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces - $tmp = preg_split("/\s+/", $tmp); - - foreach($tmp as $attr) { - if ( mb_substr($attr, 0, 3) === "url" || $attr === "none" ) { - $this->_set_style("background_image", $this->_image($attr), $important); - } - elseif ( $attr === "fixed" || $attr === "scroll" ) { - $this->_set_style("background_attachment", $attr, $important); - } - elseif ( $attr === "repeat" || $attr === "repeat-x" || $attr === "repeat-y" || $attr === "no-repeat" ) { - $this->_set_style("background_repeat", $attr, $important); - } - elseif ( ($col = $this->munge_color($attr)) != null ) { - $this->_set_style("background_color", is_array($col) ? $col["hex"] : $col, $important); - } - else { - $pos[] = $attr; - } - } - - if (count($pos)) { - $this->_set_style("background_position", implode(" ", $pos), $important); - } - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["background"] = null; - $this->_props["background"] = $val; - } - - /** - * Sets the font size - * - * $size can be any acceptable CSS size - * - * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size - * @param string|float $size - */ - function set_font_size($size) { - $this->__font_size_calculated = false; - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["font_size"] = null; - $this->_props["font_size"] = $size; - } - - /** - * Sets the font style - * - * combined attributes - * set individual attributes also, respecting !important mark - * exactly this order, separate by space. Multiple fonts separated by comma: - * font-style, font-variant, font-weight, font-size, line-height, font-family - * - * Other than with border and list, existing partial attributes should - * reset when starting here, even when not mentioned. - * If individual attribute is !important and explicite or implicite replacement is not, - * keep individual attribute - * - * require whitespace as delimiters for single value attributes - * On delimiter "/" treat first as font height, second as line height - * treat all remaining at the end of line as font - * font-style, font-variant, font-weight, font-size, line-height, font-family - * - * missing font-size and font-family might be not allowed, but accept it here and - * use default (medium size, enpty font name) - * - * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style - * @param $val - */ - function set_font($val) { - $this->__font_size_calculated = false; - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["font"] = null; - $this->_props["font"] = $val; - - $important = isset($this->_important_props["font"]); - - if ( preg_match("/^(italic|oblique|normal)\s*(.*)$/i",$val,$match) ) { - $this->_set_style("font_style", $match[1], $important); - $val = $match[2]; - } - else { - $this->_set_style("font_style", self::$_defaults["font_style"], $important); - } - - if ( preg_match("/^(small-caps|normal)\s*(.*)$/i",$val,$match) ) { - $this->_set_style("font_variant", $match[1], $important); - $val = $match[2]; - } - else { - $this->_set_style("font_variant", self::$_defaults["font_variant"], $important); - } - - //matching numeric value followed by unit -> this is indeed a subsequent font size. Skip! - if ( preg_match("/^(bold|bolder|lighter|100|200|300|400|500|600|700|800|900|normal)\s*(.*)$/i", $val, $match) && - !preg_match("/^(?:pt|px|pc|em|ex|in|cm|mm|%)/",$match[2]) - ) { - $this->_set_style("font_weight", $match[1], $important); - $val = $match[2]; - } - else { - $this->_set_style("font_weight", self::$_defaults["font_weight"], $important); - } - - if ( preg_match("/^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger|\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))\s*(.*)$/i",$val,$match) ) { - $this->_set_style("font_size", $match[1], $important); - $val = $match[2]; - if ( preg_match("/^\/\s*(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))\s*(.*)$/i", $val, $match ) ) { - $this->_set_style("line_height", $match[1], $important); - $val = $match[2]; - } - else { - $this->_set_style("line_height", self::$_defaults["line_height"], $important); - } - } - else { - $this->_set_style("font_size", self::$_defaults["font_size"], $important); - $this->_set_style("line_height", self::$_defaults["line_height"], $important); - } - - if( strlen($val) != 0 ) { - $this->_set_style("font_family", $val, $important); - } - else { - $this->_set_style("font_family", self::$_defaults["font_family"], $important); - } - } - - /**#@+ - * Sets page break properties - * - * @link http://www.w3.org/TR/CSS21/page.html#page-breaks - * @param string $break - */ - function set_page_break_before($break) { - if ( $break === "left" || $break === "right" ) { - $break = "always"; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["page_break_before"] = null; - $this->_props["page_break_before"] = $break; - } - - function set_page_break_after($break) { - if ( $break === "left" || $break === "right" ) { - $break = "always"; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["page_break_after"] = null; - $this->_props["page_break_after"] = $break; - } - /**#@-*/ - - //........................................................................ - - /**#@+ - * Sets the margin size - * - * @link http://www.w3.org/TR/CSS21/box.html#margin-properties - * @param $val - */ - function set_margin_top($val) { - $this->_set_style_side_width_important('margin','top',$val); - } - - function set_margin_right($val) { - $this->_set_style_side_width_important('margin','right',$val); - } - - function set_margin_bottom($val) { - $this->_set_style_side_width_important('margin','bottom',$val); - } - - function set_margin_left($val) { - $this->_set_style_side_width_important('margin','left',$val); - } - - function set_margin($val) { - $val = str_replace("none", "0px", $val); - $this->_set_style_type_important('margin','',$val); - } - /**#@-*/ - - /**#@+ - * Sets the padding size - * - * @link http://www.w3.org/TR/CSS21/box.html#padding-properties - * @param $val - */ - function set_padding_top($val) { - $this->_set_style_side_width_important('padding','top',$val); - } - - function set_padding_right($val) { - $this->_set_style_side_width_important('padding','right',$val); - } - - function set_padding_bottom($val) { - $this->_set_style_side_width_important('padding','bottom',$val); - } - - function set_padding_left($val) { - $this->_set_style_side_width_important('padding','left',$val); - } - - function set_padding($val) { - $val = str_replace("none", "0px", $val); - $this->_set_style_type_important('padding','',$val); - } - /**#@-*/ - - /** - * Sets a single border - * - * @param string $side - * @param string $border_spec ([width] [style] [color]) - * @param boolean $important - */ - protected function _set_border($side, $border_spec, $important) { - $border_spec = preg_replace("/\s*\,\s*/", ",", $border_spec); - //$border_spec = str_replace(",", " ", $border_spec); // Why did we have this ?? rbg(10, 102, 10) > rgb(10 102 10) - $arr = explode(" ", $border_spec); - - // FIXME: handle partial values - - //For consistency of individal and combined properties, and with ie8 and firefox3 - //reset all attributes, even if only partially given - $this->_set_style_side_type('border',$side,'_style',self::$_defaults['border_'.$side.'_style'],$important); - $this->_set_style_side_type('border',$side,'_width',self::$_defaults['border_'.$side.'_width'],$important); - $this->_set_style_side_type('border',$side,'_color',self::$_defaults['border_'.$side.'_color'],$important); - - foreach ($arr as $value) { - $value = trim($value); - if ( in_array($value, self::$BORDER_STYLES) ) { - $this->_set_style_side_type('border',$side,'_style',$value,$important); - } - else if ( preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value ) ) { - $this->_set_style_side_type('border',$side,'_width',$value,$important); - } - else { - // must be color - $this->_set_style_side_type('border',$side,'_color',$value,$important); - } - } - - //see __set and __get, on all assignments clear cache! - $this->_prop_cache['border_'.$side] = null; - $this->_props['border_'.$side] = $border_spec; - } - - /** - * Sets the border styles - * - * @link http://www.w3.org/TR/CSS21/box.html#border-properties - * @param string $val - */ - function set_border_top($val) { - $this->_set_border("top", $val, isset($this->_important_props['border_top'])); - } - - function set_border_right($val) { - $this->_set_border("right", $val, isset($this->_important_props['border_right'])); - } - - function set_border_bottom($val) { - $this->_set_border("bottom", $val, isset($this->_important_props['border_bottom'])); - } - - function set_border_left($val) { - $this->_set_border("left", $val, isset($this->_important_props['border_left'])); - } - - function set_border($val) { - $important = isset($this->_important_props["border"]); - $this->_set_border("top", $val, $important); - $this->_set_border("right", $val, $important); - $this->_set_border("bottom", $val, $important); - $this->_set_border("left", $val, $important); - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["border"] = null; - $this->_props["border"] = $val; - } - - function set_border_width($val) { - $this->_set_style_type_important('border','_width',$val); - } - - function set_border_color($val) { - $this->_set_style_type_important('border','_color',$val); - } - - function set_border_style($val) { - $this->_set_style_type_important('border','_style',$val); - } - - /** - * Sets the border radius size - * - * http://www.w3.org/TR/css3-background/#corners - */ - function set_border_top_left_radius($val) { - $this->_set_border_radius_corner($val, "top_left"); - } - - function set_border_top_right_radius($val) { - $this->_set_border_radius_corner($val, "top_right"); - } - - function set_border_bottom_left_radius($val) { - $this->_set_border_radius_corner($val, "bottom_left"); - } - - function set_border_bottom_right_radius($val) { - $this->_set_border_radius_corner($val, "bottom_right"); - } - - function set_border_radius($val) { - $val = preg_replace("/\s*\,\s*/", ",", $val); // when border-radius has spaces - $arr = explode(" ", $val); - - switch (count($arr)) { - case 1: $this->_set_border_radii($arr[0],$arr[0],$arr[0],$arr[0]); break; - case 2: $this->_set_border_radii($arr[0],$arr[1],$arr[0],$arr[1]); break; - case 3: $this->_set_border_radii($arr[0],$arr[1],$arr[2],$arr[1]); break; - case 4: $this->_set_border_radii($arr[0],$arr[1],$arr[2],$arr[3]); break; - } - } - - protected function _set_border_radii($val1, $val2, $val3, $val4) { - $this->_set_border_radius_corner($val1, "top_left"); - $this->_set_border_radius_corner($val2, "top_right"); - $this->_set_border_radius_corner($val3, "bottom_right"); - $this->_set_border_radius_corner($val4, "bottom_left"); - } - - protected function _set_border_radius_corner($val, $corner) { - $this->_has_border_radius = true; - - //see __set and __get, on all assignments clear cache! - $this->_prop_cache["border_" . $corner . "_radius"] = null; - - $this->_props["border_" . $corner . "_radius"] = $this->length_in_pt($val); - } - - /** - * Sets the outline styles - * - * @link http://www.w3.org/TR/CSS21/ui.html#dynamic-outlines - * @param string $val - */ - function set_outline($val) { - $important = isset($this->_important_props["outline"]); - - $props = array( - "outline_style", - "outline_width", - "outline_color", - ); - - foreach($props as $prop) { - $_val = self::$_defaults[$prop]; - - if ( !isset($this->_important_props[$prop]) || $important) { - //see __set and __get, on all assignments clear cache! - $this->_prop_cache[$prop] = null; - if ( $important ) { - $this->_important_props[$prop] = true; - } - $this->_props[$prop] = $_val; - } - } - - $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces - $arr = explode(" ", $val); - foreach ($arr as $value) { - $value = trim($value); - - if ( in_array($value, self::$BORDER_STYLES) ) { - $this->set_outline_style($value); - } - else if ( preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value ) ) { - $this->set_outline_width($value); - } - else { - // must be color - $this->set_outline_color($value); - } - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["outline"] = null; - $this->_props["outline"] = $val; - } - - function set_outline_width($val) { - $this->_set_style_type_important('outline','_width',$val); - } - - function set_outline_color($val) { - $this->_set_style_type_important('outline','_color',$val); - } - - function set_outline_style($val) { - $this->_set_style_type_important('outline','_style',$val); - } - - /** - * Sets the border spacing - * - * @link http://www.w3.org/TR/CSS21/box.html#border-properties - * @param float $val - */ - function set_border_spacing($val) { - $arr = explode(" ", $val); - - if ( count($arr) == 1 ) { - $arr[1] = $arr[0]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["border_spacing"] = null; - $this->_props["border_spacing"] = "$arr[0] $arr[1]"; - } - - /** - * Sets the list style image - * - * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image - * @param $val - */ - function set_list_style_image($val) { - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["list_style_image"] = null; - $this->_props["list_style_image"] = $this->_image($val); - } - - /** - * Sets the list style - * - * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style - * @param $val - */ - function set_list_style($val) { - $important = isset($this->_important_props["list_style"]); - $arr = explode(" ", str_replace(",", " ", $val)); - - static $types = array( - "disc", "circle", "square", - "decimal-leading-zero", "decimal", "1", - "lower-roman", "upper-roman", "a", "A", - "lower-greek", - "lower-latin", "upper-latin", - "lower-alpha", "upper-alpha", - "armenian", "georgian", "hebrew", - "cjk-ideographic", "hiragana", "katakana", - "hiragana-iroha", "katakana-iroha", "none" - ); - - static $positions = array("inside", "outside"); - - foreach ($arr as $value) { - /* http://www.w3.org/TR/CSS21/generate.html#list-style - * A value of 'none' for the 'list-style' property sets both 'list-style-type' and 'list-style-image' to 'none' - */ - if ( $value === "none" ) { - $this->_set_style("list_style_type", $value, $important); - $this->_set_style("list_style_image", $value, $important); - continue; - } - - //On setting or merging or inheriting list_style_image as well as list_style_type, - //and url exists, then url has precedence, otherwise fall back to list_style_type - //Firefox is wrong here (list_style_image gets overwritten on explicite list_style_type) - //Internet Explorer 7/8 and dompdf is right. - - if ( mb_substr($value, 0, 3) === "url" ) { - $this->_set_style("list_style_image", $this->_image($value), $important); - continue; - } - - if ( in_array($value, $types) ) { - $this->_set_style("list_style_type", $value, $important); - } - else if ( in_array($value, $positions) ) { - $this->_set_style("list_style_position", $value, $important); - } - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["list_style"] = null; - $this->_props["list_style"] = $val; - } - - function set_size($val) { - $length_re = "/(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))/"; - - $val = mb_strtolower($val); - - if ( $val === "auto" ) { - return; - } - - $parts = preg_split("/\s+/", $val); - - $computed = array(); - if ( preg_match($length_re, $parts[0]) ) { - $computed[] = $this->length_in_pt($parts[0]); - - if ( isset($parts[1]) && preg_match($length_re, $parts[1]) ) { - $computed[] = $this->length_in_pt($parts[1]); - } - else { - $computed[] = $computed[0]; - } - } - elseif ( isset(CPDF_Adapter::$PAPER_SIZES[$parts[0]]) ) { - $computed = array_slice(CPDF_Adapter::$PAPER_SIZES[$parts[0]], 2, 2); - - if ( isset($parts[1]) && $parts[1] === "landscape" ) { - $computed = array_reverse($computed); - } - } - else { - return; - } - - $this->_props["size"] = $computed; - } - - /** - * Sets the CSS3 transform property - * - * @link http://www.w3.org/TR/css3-2d-transforms/#transform-property - * @param string $val - */ - function set_transform($val) { - $number = "\s*([^,\s]+)\s*"; - $tr_value = "\s*([^,\s]+)\s*"; - $angle = "\s*([^,\s]+(?:deg|rad)?)\s*"; - - if ( !preg_match_all("/[a-z]+\([^\)]+\)/i", $val, $parts, PREG_SET_ORDER) ) { - return; - } - - $functions = array( - //"matrix" => "\($number,$number,$number,$number,$number,$number\)", - - "translate" => "\($tr_value(?:,$tr_value)?\)", - "translateX" => "\($tr_value\)", - "translateY" => "\($tr_value\)", - - "scale" => "\($number(?:,$number)?\)", - "scaleX" => "\($number\)", - "scaleY" => "\($number\)", - - "rotate" => "\($angle\)", - - "skew" => "\($angle(?:,$angle)?\)", - "skewX" => "\($angle\)", - "skewY" => "\($angle\)", - ); - - $transforms = array(); - - foreach($parts as $part) { - $t = $part[0]; - - foreach($functions as $name => $pattern) { - if ( preg_match("/$name\s*$pattern/i", $t, $matches) ) { - $values = array_slice($matches, 1); - - switch($name) { - // units - case "rotate": - case "skew": - case "skewX": - case "skewY": - - foreach($values as $i => $value) { - if ( strpos($value, "rad") ) { - $values[$i] = rad2deg(floatval($value)); - } - else { - $values[$i] = floatval($value); - } - } - - switch($name) { - case "skew": - if ( !isset($values[1]) ) { - $values[1] = 0; - } - break; - case "skewX": - $name = "skew"; - $values = array($values[0], 0); - break; - case "skewY": - $name = "skew"; - $values = array(0, $values[0]); - break; - } - break; - - // units - case "translate": - $values[0] = $this->length_in_pt($values[0], $this->width); - - if ( isset($values[1]) ) { - $values[1] = $this->length_in_pt($values[1], $this->height); - } - else { - $values[1] = 0; - } - break; - - case "translateX": - $name = "translate"; - $values = array($this->length_in_pt($values[0], $this->width), 0); - break; - - case "translateY": - $name = "translate"; - $values = array(0, $this->length_in_pt($values[0], $this->height)); - break; - - // units - case "scale": - if ( !isset($values[1]) ) { - $values[1] = $values[0]; - } - break; - - case "scaleX": - $name = "scale"; - $values = array($values[0], 1.0); - break; - - case "scaleY": - $name = "scale"; - $values = array(1.0, $values[0]); - break; - } - - $transforms[] = array( - $name, - $values, - ); - } - } - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["transform"] = null; - $this->_props["transform"] = $transforms; - } - - function set__webkit_transform($val) { - $this->set_transform($val); - } - - function set__webkit_transform_origin($val) { - $this->set_transform_origin($val); - } - - /** - * Sets the CSS3 transform-origin property - * - * @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin - * @param string $val - */ - function set_transform_origin($val) { - $values = preg_split("/\s+/", $val); - - if ( count($values) === 0) { - return; - } - - foreach($values as &$value) { - if ( in_array($value, array("top", "left")) ) { - $value = 0; - } - - if ( in_array($value, array("bottom", "right")) ) { - $value = "100%"; - } - } - - if ( !isset($values[1]) ) { - $values[1] = $values[0]; - } - - //see __set and __get, on all assignments clear cache, not needed on direct set through __set - $this->_prop_cache["transform_origin"] = null; - $this->_props["transform_origin"] = $values; - } - - protected function parse_image_resolution($val) { - // If exif data could be get: - // $re = '/^\s*(\d+|normal|auto)(?:\s*,\s*(\d+|normal))?\s*$/'; - - $re = '/^\s*(\d+|normal|auto)\s*$/'; - - if ( !preg_match($re, $val, $matches) ) { - return null; - } - - return $matches[1]; - } - - // auto | normal | dpi - function set_background_image_resolution($val) { - $parsed = $this->parse_image_resolution($val); - - $this->_prop_cache["background_image_resolution"] = null; - $this->_props["background_image_resolution"] = $parsed; - } - - // auto | normal | dpi - function set_image_resolution($val) { - $parsed = $this->parse_image_resolution($val); - - $this->_prop_cache["image_resolution"] = null; - $this->_props["image_resolution"] = $parsed; - } - - function set__dompdf_background_image_resolution($val) { - $this->set_background_image_resolution($val); - } - - function set__dompdf_image_resolution($val) { - $this->set_image_resolution($val); - } - - function set_z_index($val) { - if ( round($val) != $val && $val !== "auto" ) { - return; - } - - $this->_prop_cache["z_index"] = null; - $this->_props["z_index"] = $val; - } - - function set_counter_increment($val) { - $val = trim($val); - $value = null; - - if ( in_array($val, array("none", "inherit")) ) { - $value = $val; - } - else { - if ( preg_match_all("/(".self::CSS_IDENTIFIER.")(?:\s+(".self::CSS_INTEGER."))?/", $val, $matches, PREG_SET_ORDER) ){ - $value = array(); - foreach($matches as $match) { - $value[$match[1]] = isset($match[2]) ? $match[2] : 1; - } - } - } - - $this->_prop_cache["counter_increment"] = null; - $this->_props["counter_increment"] = $value; - } - - /** - * Generate a string representation of the Style - * - * This dumps the entire property array into a string via print_r. Useful - * for debugging. - * - * @return string - */ - /*DEBUGCSS print: see below additional debugging util*/ - function __toString() { - return print_r(array_merge(array("parent_font_size" => $this->_parent_font_size), - $this->_props), true); - } - -/*DEBUGCSS*/ function debug_print() { -/*DEBUGCSS*/ print "parent_font_size:".$this->_parent_font_size . ";\n"; -/*DEBUGCSS*/ foreach($this->_props as $prop => $val ) { -/*DEBUGCSS*/ print $prop.':'.$val; -/*DEBUGCSS*/ if (isset($this->_important_props[$prop])) { -/*DEBUGCSS*/ print '!important'; -/*DEBUGCSS*/ } -/*DEBUGCSS*/ print ";\n"; -/*DEBUGCSS*/ } -/*DEBUGCSS*/ } -} diff --git a/library/vendor/dompdf/include/stylesheet.cls.php b/library/vendor/dompdf/include/stylesheet.cls.php deleted file mode 100644 index a74956754..000000000 --- a/library/vendor/dompdf/include/stylesheet.cls.php +++ /dev/null @@ -1,1418 +0,0 @@ - - * @author Helmut Tischer - * @author Fabien Ménager - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License - */ - -/** - * The location of the default built-in CSS file. - * {@link Stylesheet::DEFAULT_STYLESHEET} - */ -define('__DEFAULT_STYLESHEET', DOMPDF_LIB_DIR . DIRECTORY_SEPARATOR . "res" . DIRECTORY_SEPARATOR . "html.css"); - -/** - * The master stylesheet class - * - * The Stylesheet class is responsible for parsing stylesheets and style - * tags/attributes. It also acts as a registry of the individual Style - * objects generated by the current set of loaded CSS files and style - * elements. - * - * @see Style - * @package dompdf - */ -class Stylesheet { - - /** - * The location of the default built-in CSS file. - */ - const DEFAULT_STYLESHEET = __DEFAULT_STYLESHEET; - - /** - * User agent stylesheet origin - * - * @var int - */ - const ORIG_UA = 1; - - /** - * User normal stylesheet origin - * - * @var int - */ - const ORIG_USER = 2; - - /** - * Author normal stylesheet origin - * - * @var int - */ - const ORIG_AUTHOR = 3; - - private static $_stylesheet_origins = array( - self::ORIG_UA => -0x0FFFFFFF, // user agent style sheets - self::ORIG_USER => -0x0000FFFF, // user normal style sheets - self::ORIG_AUTHOR => 0x00000000, // author normal style sheets - ); - - /** - * Current dompdf instance - * - * @var DOMPDF - */ - private $_dompdf; - - /** - * Array of currently defined styles - * - * @var Style[] - */ - private $_styles; - - /** - * Base protocol of the document being parsed - * Used to handle relative urls. - * - * @var string - */ - private $_protocol; - - /** - * Base hostname of the document being parsed - * Used to handle relative urls. - * - * @var string - */ - private $_base_host; - - /** - * Base path of the document being parsed - * Used to handle relative urls. - * - * @var string - */ - private $_base_path; - - /** - * The styles defined by @page rules - * - * @var array + $child = $child->nextSibling; + } + } else { + $css = $tag->nodeValue; + } + + $this->css->load_css($css); + break; + } + } + } + + /** + * @param string $cacheId + * @deprecated + */ + public function enable_caching($cacheId) + { + $this->enableCaching($cacheId); + } + + /** + * Enable experimental caching capability + * + * @param string $cacheId + */ + public function enableCaching($cacheId) + { + $this->cacheId = $cacheId; + } + + /** + * @param string $value + * @return bool + * @deprecated + */ + public function parse_default_view($value) + { + return $this->parseDefaultView($value); + } + + /** + * @param string $value + * @return bool + */ + public function parseDefaultView($value) + { + $valid = array("XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV"); + + $options = preg_split("/\s*,\s*/", trim($value)); + $defaultView = array_shift($options); + + if (!in_array($defaultView, $valid)) { + return false; + } + + $this->setDefaultView($defaultView, $options); + return true; + } + + /** + * Renders the HTML to PDF + */ + public function render() + { + $this->saveLocale(); + + $logOutputFile = $this->options->getLogOutputFile(); + if ($logOutputFile) { + if (!file_exists($logOutputFile) && is_writable(dirname($logOutputFile))) { + touch($logOutputFile); + } + + $this->startTime = microtime(true); + ob_start(); + } + + $this->processHtml(); + + $this->css->apply_styles($this->tree); + + // @page style rules : size, margins + $pageStyles = $this->css->get_page_styles(); + + $basePageStyle = $pageStyles["base"]; + unset($pageStyles["base"]); + + foreach ($pageStyles as $pageStyle) { + $pageStyle->inherit($basePageStyle); + } + + if (is_array($basePageStyle->size)) { + $this->setPaper(array(0, 0, $basePageStyle->size[0], $basePageStyle->size[1])); + } + + //TODO: We really shouldn't be doing this; properties were already set in the constructor. We should add Canvas methods to set the page size and orientation after instantiaion (see #1059). + $this->setCanvas(CanvasFactory::get_instance($this, $this->paperSize, $this->paperOrientation)); + $this->setFontMetrics(new FontMetrics($this->pdf, $this->getOptions())); + + if ($this->options->isFontSubsettingEnabled() && $this->pdf instanceof CPDF) { + foreach ($this->tree->get_frames() as $frame) { + $style = $frame->get_style(); + $node = $frame->get_node(); + + // Handle text nodes + if ($node->nodeName === "#text") { + $this->getCanvas()->register_string_subset($style->font_family, $node->nodeValue); + continue; + } + + // Handle generated content (list items) + if ($style->display === "list-item") { + $chars = ListBullet::get_counter_chars($style->list_style_type); + $this->getCanvas()->register_string_subset($style->font_family, $chars); + continue; + } + + // Handle other generated content (pseudo elements) + // FIXME: This only captures the text of the stylesheet declaration, + // not the actual generated content, and forces all possible counter + // values. See notes in issue #750. + if ($frame->get_node()->nodeName == "dompdf_generated") { + // all possible counter values + $chars = ListBullet::get_counter_chars('decimal'); + $this->getCanvas()->register_string_subset($style->font_family, $chars); + $chars = ListBullet::get_counter_chars('upper-alpha'); + $this->getCanvas()->register_string_subset($style->font_family, $chars); + $chars = ListBullet::get_counter_chars('lower-alpha'); + $this->getCanvas()->register_string_subset($style->font_family, $chars); + $chars = ListBullet::get_counter_chars('lower-greek'); + $this->getCanvas()->register_string_subset($style->font_family, $chars); + // the text of the stylesheet declaration + $this->getCanvas()->register_string_subset($style->font_family, $style->content); + continue; + } + } + } + + $root = null; + + foreach ($this->tree->get_frames() as $frame) { + // Set up the root frame + if (is_null($root)) { + $root = Factory::decorate_root($this->tree->get_root(), $this); + continue; + } + + // Create the appropriate decorators, reflowers & positioners. + Factory::decorate_frame($frame, $this, $root); + } + + // Add meta information + $title = $this->dom->getElementsByTagName("title"); + if ($title->length) { + $this->getCanvas()->add_info("Title", trim($title->item(0)->nodeValue)); + } + + $metas = $this->dom->getElementsByTagName("meta"); + $labels = array( + "author" => "Author", + "keywords" => "Keywords", + "description" => "Subject", + ); + foreach ($metas as $meta) { + $name = mb_strtolower($meta->getAttribute("name")); + $value = trim($meta->getAttribute("content")); + + if (isset($labels[$name])) { + $this->pdf->add_info($labels[$name], $value); + continue; + } + + if ($name === "dompdf.view" && $this->parseDefaultView($value)) { + $this->getCanvas()->set_default_view($this->defaultView, $this->defaultViewOptions); + } + } + + $root->set_containing_block(0, 0, $this->getCanvas()->get_width(), $this->getCanvas()->get_height()); + $root->set_renderer(new Renderer($this)); + + // This is where the magic happens: + $root->reflow(); + + // Clean up cached images + Cache::clear(); + + global $_dompdf_warnings, $_dompdf_show_warnings; + if ($_dompdf_show_warnings && isset($_dompdf_warnings)) { + echo 'Dompdf Warnings
';
+            foreach ($_dompdf_warnings as $msg) {
+                echo $msg . "\n";
+            }
+            echo $this->getCanvas()->get_cpdf()->messages;
+            echo '
'; + flush(); + } + + $this->restoreLocale(); + } + + /** + * Add meta information to the PDF after rendering + */ + public function add_info($label, $value) + { + if (!is_null($this->pdf)) { + $this->pdf->add_info($label, $value); + } + } + + /** + * Writes the output buffer in the log file + * + * @return void + */ + private function write_log() + { + $log_output_file = $this->get_option("log_output_file"); + if (!$log_output_file || !is_writable($log_output_file)) { + return; + } + + $frames = Frame::$ID_COUNTER; + $memory = memory_get_peak_usage(true) / 1024; + $time = (microtime(true) - $this->startTime) * 1000; + + $out = sprintf( + "%6d" . + "%10.2f KB" . + "%10.2f ms" . + " " . + ($this->quirksmode ? " ON" : "OFF") . + "
", $frames, $memory, $time); + + $out .= ob_get_clean(); + + $log_output_file = $this->get_option("log_output_file"); + file_put_contents($log_output_file, $out); + } + + /** + * Streams the PDF to the client + * + * The file will open a download dialog by default. The options + * parameter controls the output. Accepted options (array keys) are: + * + * 'Accept-Ranges' => 1 or 0 (=default): Send an 'Accept-Ranges:' + * HTTP header, see https://tools.ietf.org/html/rfc2616#section-14.5 + * This header seems to have caused some problems, despite the fact + * that it is supposed to solve them, so I am leaving it off by default. + * + * 'compress' = > 1 (=default) or 0: + * Apply content stream compression + * + * 'Attachment' => 1 (=default) or 0: + * Set the 'Content-Disposition:' HTTP header to 'attachment' + * (thereby causing the browser to open a download dialog) + * + * @param string $filename the name of the streamed file + * @param array $options header options (see above) + */ + public function stream($filename = 'document.pdf', $options = null) + { + $this->saveLocale(); + + $this->write_log(); + + if (!is_null($this->pdf)) { + $this->pdf->stream($filename, $options); + } + + $this->restoreLocale(); + } + + /** + * Returns the PDF as a string + * + * The file will open a download dialog by default. The options + * parameter controls the output. Accepted options are: + * + * + * 'compress' = > 1 or 0 - apply content stream compression, this is + * on (1) by default + * + * + * @param array $options options (see above) + * + * @return string + */ + public function output($options = null) + { + $this->saveLocale(); + + $this->write_log(); + + if (is_null($this->pdf)) { + return null; + } + + $output = $this->pdf->output($options); + + $this->restoreLocale(); + + return $output; + } + + /** + * @return string + * @deprecated + */ + public function output_html() + { + return $this->outputHtml(); + } + + /** + * Returns the underlying HTML document as a string + * + * @return string + */ + public function outputHtml() + { + return $this->dom->saveHTML(); + } + + /** + * Get the dompdf option value + * + * @param string $key + * @return mixed + * @deprecated + */ + public function get_option($key) + { + return $this->options->get($key); + } + + /** + * @param string $key + * @param mixed $value + * @return $this + * @deprecated + */ + public function set_option($key, $value) + { + $this->options->set($key, $value); + return $this; + } + + /** + * @param array $options + * @return $this + * @deprecated + */ + public function set_options(array $options) + { + $this->options->set($options); + return $this; + } + + /** + * @param string $size + * @param string $orientation + * @deprecated + */ + public function set_paper($size, $orientation = "portrait") + { + $this->setPaper($size, $orientation); + } + + /** + * Sets the paper size & orientation + * + * @param string $size 'letter', 'legal', 'A4', etc. {@link Dompdf\Adapter\CPDF::$PAPER_SIZES} + * @param string $orientation 'portrait' or 'landscape' + * @return $this + */ + public function setPaper($size, $orientation = "portrait") + { + $this->paperSize = $size; + $this->paperOrientation = $orientation; + return $this; + } + + /** + * @param FrameTree $tree + * @return $this + */ + public function setTree(FrameTree $tree) + { + $this->tree = $tree; + return $this; + } + + /** + * @return FrameTree + * @deprecated + */ + public function get_tree() + { + return $this->getTree(); + } + + /** + * Returns the underlying {@link FrameTree} object + * + * @return FrameTree + */ + public function getTree() + { + return $this->tree; + } + + /** + * @param string $protocol + * @return $this + * @deprecated + */ + public function set_protocol($protocol) + { + return $this->setProtocol($protocol); + } + + /** + * Sets the protocol to use + * FIXME validate these + * + * @param string $protocol + * @return $this + */ + public function setProtocol($protocol) + { + $this->protocol = $protocol; + return $this; + } + + /** + * @return string + * @deprecated + */ + public function get_protocol() + { + return $this->getProtocol(); + } + + /** + * Returns the protocol in use + * + * @return string + */ + public function getProtocol() + { + return $this->protocol; + } + + /** + * @param string $host + * @deprecated + */ + public function set_host($host) + { + $this->setBaseHost($host); + } + + /** + * Sets the base hostname + * + * @param string $baseHost + * @return $this + */ + public function setBaseHost($baseHost) + { + $this->baseHost = $baseHost; + return $this; + } + + /** + * @return string + * @deprecated + */ + public function get_host() + { + return $this->getBaseHost(); + } + + /** + * Returns the base hostname + * + * @return string + */ + public function getBaseHost() + { + return $this->baseHost; + } + + /** + * Sets the base path + * + * @param string $path + * @deprecated + */ + public function set_base_path($path) + { + $this->setBasePath($path); + } + + /** + * Sets the base path + * + * @param string $basePath + * @return $this + */ + public function setBasePath($basePath) + { + $this->basePath = $basePath; + return $this; + } + + /** + * @return string + * @deprecated + */ + public function get_base_path() + { + return $this->getBasePath(); + } + + /** + * Returns the base path + * + * @return string + */ + public function getBasePath() + { + return $this->basePath; + } + + /** + * @param string $default_view The default document view + * @param array $options The view's options + * @return $this + * @deprecated + */ + public function set_default_view($default_view, $options) + { + return $this->setDefaultView($default_view, $options); + } + + /** + * Sets the default view + * + * @param string $defaultView The default document view + * @param array $options The view's options + * @return $this + */ + public function setDefaultView($defaultView, $options) + { + $this->defaultView = $defaultView; + $this->defaultViewOptions = $options; + return $this; + } + + /** + * @param resource $http_context + * @return $this + * @deprecated + */ + public function set_http_context($http_context) + { + return $this->setHttpContext($http_context); + } + + /** + * Sets the HTTP context + * + * @param resource $httpContext + * @return $this + */ + public function setHttpContext($httpContext) + { + $this->httpContext = $httpContext; + return $this; + } + + /** + * @return resource + * @deprecated + */ + public function get_http_context() + { + return $this->getHttpContext(); + } + + /** + * Returns the HTTP context + * + * @return resource + */ + public function getHttpContext() + { + return $this->httpContext; + } + + /** + * @param Canvas $canvas + * @return $this + */ + public function setCanvas(Canvas $canvas) + { + $this->pdf = $canvas; + $this->canvas = $canvas; + return $this; + } + + /** + * @return Canvas + * @deprecated + */ + public function get_canvas() + { + return $this->getCanvas(); + } + + /** + * Return the underlying Canvas instance (e.g. Dompdf\Adapter\CPDF, Dompdf\Adapter\GD) + * + * @return Canvas + */ + public function getCanvas() + { + if (null === $this->canvas && null !== $this->pdf) { + return $this->pdf; + } + return $this->canvas; + } + + /** + * @param Stylesheet $css + * @return $this + */ + public function setCss(Stylesheet $css) + { + $this->css = $css; + return $this; + } + + /** + * @return Stylesheet + * @deprecated + */ + public function get_css() + { + return $this->getCss(); + } + + /** + * Returns the stylesheet + * + * @return Stylesheet + */ + public function getCss() + { + return $this->css; + } + + /** + * @param DOMDocument $dom + * @return $this + */ + public function setDom(DOMDocument $dom) + { + $this->dom = $dom; + return $this; + } + + /** + * @return DOMDocument + * @deprecated + */ + public function get_dom() + { + return $this->getDom(); + } + + /** + * @return DOMDocument + */ + public function getDom() + { + return $this->dom; + } + + /** + * @param Options $options + * @return $this + */ + public function setOptions(Options $options) + { + $this->options = $options; + $fontMetrics = $this->getFontMetrics(); + if (isset($fontMetrics)) { + $fontMetrics->setOptions($options); + } + return $this; + } + + /** + * @return Options + */ + public function getOptions() + { + return $this->options; + } + + /** + * @return array + * @deprecated + */ + public function get_callbacks() + { + return $this->getCallbacks(); + } + + /** + * Returns the callbacks array + * + * @return array + */ + public function getCallbacks() + { + return $this->callbacks; + } + + /** + * @param array $callbacks the set of callbacks to set + * @deprecated + */ + public function set_callbacks($callbacks) + { + $this->setCallbacks($callbacks); + } + + /** + * Sets callbacks for events like rendering of pages and elements. + * The callbacks array contains arrays with 'event' set to 'begin_page', + * 'end_page', 'begin_frame', or 'end_frame' and 'f' set to a function or + * object plus method to be called. + * + * The function 'f' must take an array as argument, which contains info + * about the event. + * + * @param array $callbacks the set of callbacks to set + */ + public function setCallbacks($callbacks) + { + if (is_array($callbacks)) { + $this->callbacks = array(); + foreach ($callbacks as $c) { + if (is_array($c) && isset($c['event']) && isset($c['f'])) { + $event = $c['event']; + $f = $c['f']; + if (is_callable($f) && is_string($event)) { + $this->callbacks[$event][] = $f; + } + } + } + } + } + + /** + * @return boolean + * @deprecated + */ + public function get_quirksmode() + { + return $this->getQuirksmode(); + } + + /** + * Get the quirks mode + * + * @return boolean true if quirks mode is active + */ + public function getQuirksmode() + { + return $this->quirksmode; + } + + /** + * @param FontMetrics $fontMetrics + * @return $this + */ + public function setFontMetrics(FontMetrics $fontMetrics) + { + $this->fontMetrics = $fontMetrics; + return $this; + } + + /** + * @return FontMetrics + */ + public function getFontMetrics() + { + return $this->fontMetrics; + } + + /** + * PHP5 overloaded getter + * Along with {@link Dompdf::__set()} __get() provides access to all + * properties directly. Typically __get() is not called directly outside + * of this class. + * + * @param string $prop + * + * @throws Exception + * @return mixed + */ + function __get($prop) + { + switch ($prop) + { + case 'version' : + return $this->version; + default: + throw new Exception( 'Invalid property: ' . $prop ); + } + } +} diff --git a/library/vendor/dompdf/src/Exception.php b/library/vendor/dompdf/src/Exception.php new file mode 100644 index 000000000..9729dc704 --- /dev/null +++ b/library/vendor/dompdf/src/Exception.php @@ -0,0 +1,30 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf; + +/** + * Standard exception thrown by DOMPDF classes + * + * @package dompdf + */ +class Exception extends \Exception +{ + + /** + * Class constructor + * + * @param string $message Error message + * @param int $code Error code + */ + function __construct($message = null, $code = 0) + { + parent::__construct($message, $code); + } + +} diff --git a/library/vendor/dompdf/src/Exception/ImageException.php b/library/vendor/dompdf/src/Exception/ImageException.php new file mode 100644 index 000000000..62b44b1c8 --- /dev/null +++ b/library/vendor/dompdf/src/Exception/ImageException.php @@ -0,0 +1,31 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Exception; + +use Dompdf\Exception; + +/** + * Image exception thrown by DOMPDF + * + * @package dompdf + */ +class ImageException extends Exception +{ + + /** + * Class constructor + * + * @param string $message Error message + * @param int $code Error code + */ + function __construct($message = null, $code = 0) + { + parent::__construct($message, $code); + } + +} diff --git a/library/vendor/dompdf/src/FontMetrics.php b/library/vendor/dompdf/src/FontMetrics.php new file mode 100644 index 000000000..d6601cbb2 --- /dev/null +++ b/library/vendor/dompdf/src/FontMetrics.php @@ -0,0 +1,603 @@ + + * @author Helmut Tischer + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf; + +use FontLib\Font; + +/** + * The font metrics class + * + * This class provides information about fonts and text. It can resolve + * font names into actual installed font files, as well as determine the + * size of text in a particular font and size. + * + * @static + * @package dompdf + */ +class FontMetrics +{ + /** + * Name of the font cache file + * + * This file must be writable by the webserver process only to update it + * with save_font_families() after adding the .afm file references of a new font family + * with FontMetrics::saveFontFamilies(). + * This is typically done only from command line with load_font.php on converting + * ttf fonts to ufm with php-font-lib. + */ + const CACHE_FILE = "dompdf_font_family_cache.php"; + + /** + * @var Canvas + * @deprecated + */ + protected $pdf; + + /** + * Underlying {@link Canvas} object to perform text size calculations + * + * @var Canvas + */ + protected $canvas; + + /** + * Array of font family names to font files + * + * Usually cached by the {@link load_font.php} script + * + * @var array + */ + protected $fontLookup = array(); + + /** + * @var Options + */ + private $options; + + /** + * Class initialization + */ + public function __construct(Canvas $canvas, Options $options) + { + $this->setCanvas($canvas); + $this->setOptions($options); + $this->loadFontFamilies(); + } + + /** + * @deprecated + */ + public function save_font_families() + { + $this->saveFontFamilies(); + } + + /** + * Saves the stored font family cache + * + * The name and location of the cache file are determined by {@link + * FontMetrics::CACHE_FILE}. This file should be writable by the + * webserver process. + * + * @see Font_Metrics::load_font_families() + */ + public function saveFontFamilies() + { + // replace the path to the DOMPDF font directories with the corresponding constants (allows for more portability) + $cacheData = sprintf("fontLookup as $family => $variants) { + $cacheData .= sprintf(" '%s' => array(%s", addslashes($family), PHP_EOL); + foreach ($variants as $variant => $path) { + $path = sprintf("'%s'", $path); + $path = str_replace('\'' . $this->getOptions()->getFontDir() , '$fontDir . \'' , $path); + $path = str_replace('\'' . $this->getOptions()->getRootDir() , '$rootDir . \'' , $path); + $cacheData .= sprintf(" '%s' => %s,%s", $variant, $path, PHP_EOL); + } + $cacheData .= sprintf(" ),%s", PHP_EOL); + } + $cacheData .= ") ?>"; + file_put_contents($this->getCacheFile(), $cacheData); + } + + /** + * @deprecated + */ + public function load_font_families() + { + $this->loadFontFamilies(); + } + + /** + * Loads the stored font family cache + * + * @see save_font_families() + */ + public function loadFontFamilies() + { + $fontDir = $this->getOptions()->getFontDir(); + $rootDir = $this->getOptions()->getRootDir(); + + // FIXME: tempoarary define constants for cache files <= v0.6.2 + if (!defined("DOMPDF_DIR")) { define("DOMPDF_DIR", $rootDir); } + if (!defined("DOMPDF_FONT_DIR")) { define("DOMPDF_FONT_DIR", $fontDir); } + + $file = $rootDir . "/lib/fonts/dompdf_font_family_cache.dist.php"; + $distFonts = require $file; + + // FIXME: temporary step for font cache created before the font cache fix + if (is_readable($fontDir . DIRECTORY_SEPARATOR . "dompdf_font_family_cache")) { + $oldFonts = require $fontDir . DIRECTORY_SEPARATOR . "dompdf_font_family_cache"; + // If the font family cache is still in the old format + if ($oldFonts === 1) { + $cacheData = file_get_contents($fontDir . DIRECTORY_SEPARATOR . "dompdf_font_family_cache"); + file_put_contents($fontDir . DIRECTORY_SEPARATOR . "dompdf_font_family_cache", "<" . "?php return $cacheData ?" . ">"); + $oldFonts = require $fontDir . DIRECTORY_SEPARATOR . "dompdf_font_family_cache"; + } + $distFonts += $oldFonts; + } + + if (!is_readable($this->getCacheFile())) { + $this->fontLookup = $distFonts; + return; + } + + $cacheData = require $this->getCacheFile(); + + // If the font family cache is still in the old format + if ($cacheData === 1) { + $cacheData = file_get_contents($this->getCacheFile()); + file_put_contents($this->getCacheFile(), "<" . "?php return $cacheData ?" . ">"); + $this->fontLookup = require $this->getCacheFile(); + } + + $this->fontLookup = array(); + foreach ($cacheData as $key => $value) { + $this->fontLookup[stripslashes($key)] = $value; + } + + // Merge provided fonts + $this->fontLookup += $distFonts; + } + + /** + * @param array $files + * @return array + * @deprecated + */ + public function install_fonts($files) + { + return $this->installFonts($files); + } + + /** + * @param array $files + * @return array + */ + public function installFonts(array $files) + { + $names = array(); + + foreach ($files as $file) { + $font = Font::load($file); + $records = $font->getData("name", "records"); + $type = $this->getType($records[2]); + $names[mb_strtolower($records[1])][$type] = $file; + $font->close(); + } + + return $names; + } + + /** + * @param array $style + * @param string $remote_file + * @param resource $context + * @return bool + * @deprecated + */ + public function register_font($style, $remote_file, $context = null) + { + return $this->registerFont($style, $remote_file); + } + + /** + * @param array $style + * @param string $remoteFile + * @param resource $context + * @return bool + */ + public function registerFont($style, $remoteFile, $context = null) + { + $fontDir = $this->getOptions()->getFontDir(); + $fontname = mb_strtolower($style["family"]); + $families = $this->getFontFamilies(); + + $entry = array(); + if (isset($families[$fontname])) { + $entry = $families[$fontname]; + } + + $localFile = $fontDir . DIRECTORY_SEPARATOR . md5($remoteFile); + $localTempFile = $this->options->get('tempDir') . "/" . md5($remoteFile); + $cacheEntry = $localFile; + $localFile .= ".ttf"; + + $styleString = $this->getType("{$style['weight']} {$style['style']}"); + + if ( !isset($entry[$styleString]) ) { + $entry[$styleString] = $cacheEntry; + + // Download the remote file + $remoteFileContent = @file_get_contents($remoteFile, null, $context); + if (false === $remoteFileContent) { + return false; + } + file_put_contents($localTempFile, $remoteFileContent); + + $font = Font::load($localTempFile); + + if (!$font) { + unlink($localTempFile); + return false; + } + + $font->parse(); + $font->saveAdobeFontMetrics("$cacheEntry.ufm"); + $font->close(); + + unlink($localTempFile); + + if ( !file_exists("$cacheEntry.ufm") ) { + return false; + } + + // Save the changes + file_put_contents($localFile, file_get_contents($remoteFile, null, $context)); + + if ( !file_exists($localFile) ) { + unlink("$cacheEntry.ufm"); + return false; + } + + $this->setFontFamily($fontname, $entry); + $this->saveFontFamilies(); + } + + return true; + } + + /** + * @param $text + * @param $font + * @param $size + * @param float $word_spacing + * @param float $char_spacing + * @return float + * @deprecated + */ + public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) + { + //return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing); + return $this->getTextWidth($text, $font, $size, $word_spacing, $char_spacing); + } + + /** + * Calculates text size, in points + * + * @param string $text the text to be sized + * @param string $font the desired font + * @param float $size the desired font size + * @param float $wordSpacing + * @param float $charSpacing + * + * @internal param float $spacing word spacing, if any + * @return float + */ + public function getTextWidth($text, $font, $size, $wordSpacing = 0.0, $charSpacing = 0.0) + { + // @todo Make sure this cache is efficient before enabling it + static $cache = array(); + + if ($text === "") { + return 0; + } + + // Don't cache long strings + $useCache = !isset($text[50]); // Faster than strlen + + $key = "$font/$size/$wordSpacing/$charSpacing"; + + if ($useCache && isset($cache[$key][$text])) { + return $cache[$key]["$text"]; + } + + $width = $this->getCanvas()->get_text_width($text, $font, $size, $wordSpacing, $charSpacing); + + if ($useCache) { + $cache[$key][$text] = $width; + } + + return $width; + } + + /** + * @param $font + * @param $size + * @return float + * @deprecated + */ + public function get_font_height($font, $size) + { + return $this->getFontHeight($font, $size); + } + + /** + * Calculates font height + * + * @param string $font + * @param float $size + * + * @return float + */ + public function getFontHeight($font, $size) + { + return $this->getCanvas()->get_font_height($font, $size); + } + + /** + * @param $family_raw + * @param string $subtype_raw + * @return string + * @deprecated + */ + public function get_font($family_raw, $subtype_raw = "normal") + { + return $this->getFont($family_raw, $subtype_raw); + } + + /** + * Resolves a font family & subtype into an actual font file + * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If + * the particular font family has no suitable font file, the default font + * ({@link Options::defaultFont}) is used. The font file returned + * is the absolute pathname to the font file on the system. + * + * @param string $familyRaw + * @param string $subtypeRaw + * + * @return string + */ + public function getFont($familyRaw, $subtypeRaw = "normal") + { + static $cache = array(); + + if (isset($cache[$familyRaw][$subtypeRaw])) { + return $cache[$familyRaw][$subtypeRaw]; + } + + /* Allow calling for various fonts in search path. Therefore not immediately + * return replacement on non match. + * Only when called with NULL try replacement. + * When this is also missing there is really trouble. + * If only the subtype fails, nevertheless return failure. + * Only on checking the fallback font, check various subtypes on same font. + */ + + $subtype = strtolower($subtypeRaw); + + if ($familyRaw) { + $family = str_replace(array("'", '"'), "", strtolower($familyRaw)); + + if (isset($this->fontLookup[$family][$subtype])) { + return $cache[$familyRaw][$subtypeRaw] = $this->fontLookup[$family][$subtype]; + } + + return null; + } + + $family = "serif"; + + if (isset($this->fontLookup[$family][$subtype])) { + return $cache[$familyRaw][$subtypeRaw] = $this->fontLookup[$family][$subtype]; + } + + if (!isset($this->fontLookup[$family])) { + return null; + } + + $family = $this->fontLookup[$family]; + + foreach ($family as $sub => $font) { + if (strpos($subtype, $sub) !== false) { + return $cache[$familyRaw][$subtypeRaw] = $font; + } + } + + if ($subtype !== "normal") { + foreach ($family as $sub => $font) { + if ($sub !== "normal") { + return $cache[$familyRaw][$subtypeRaw] = $font; + } + } + } + + $subtype = "normal"; + + if (isset($family[$subtype])) { + return $cache[$familyRaw][$subtypeRaw] = $family[$subtype]; + } + + return null; + } + + /** + * @param $family + * @return null|string + * @deprecated + */ + public function get_family($family) + { + return $this->getFamily($family); + } + + /** + * @param string $family + * @return null|string + */ + public function getFamily($family) + { + $family = str_replace(array("'", '"'), "", mb_strtolower($family)); + + if (isset($this->fontLookup[$family])) { + return $this->fontLookup[$family]; + } + + return null; + } + + /** + * @param $type + * @return string + * @deprecated + */ + public function get_type($type) + { + return $this->getType($type); + } + + /** + * @param string $type + * @return string + */ + public function getType($type) + { + if (preg_match("/bold/i", $type)) { + if (preg_match("/italic|oblique/i", $type)) { + $type = "bold_italic"; + } else { + $type = "bold"; + } + } elseif (preg_match("/italic|oblique/i", $type)) { + $type = "italic"; + } else { + $type = "normal"; + } + + return $type; + } + + /** + * @return array + * @deprecated + */ + public function get_system_fonts() + { + return $this->getSystemFonts(); + } + + /** + * @return array + */ + public function getSystemFonts() + { + $files = glob("/usr/share/fonts/truetype/*.ttf") + + glob("/usr/share/fonts/truetype/*/*.ttf") + + glob("/usr/share/fonts/truetype/*/*/*.ttf") + + glob("C:\\Windows\\fonts\\*.ttf") + + glob("C:\\WinNT\\fonts\\*.ttf") + + glob("/mnt/c_drive/WINDOWS/Fonts/"); + + return $this->installFonts($files); + } + + /** + * @return array + * @deprecated + */ + public function get_font_families() + { + return $this->getFontFamilies(); + } + + /** + * Returns the current font lookup table + * + * @return array + */ + public function getFontFamilies() + { + return $this->fontLookup; + } + + /** + * @param string $fontname + * @param mixed $entry + * @deprecated + */ + public function set_font_family($fontname, $entry) + { + $this->setFontFamily($fontname, $entry); + } + + /** + * @param string $fontname + * @param mixed $entry + */ + public function setFontFamily($fontname, $entry) + { + $this->fontLookup[mb_strtolower($fontname)] = $entry; + } + + /** + * @return string + */ + public function getCacheFile() + { + return $this->getOptions()->getFontDir() . DIRECTORY_SEPARATOR . self::CACHE_FILE; + } + + /** + * @param Options $options + * @return $this + */ + public function setOptions(Options $options) + { + $this->options = $options; + return $this; + } + + /** + * @return Options + */ + public function getOptions() + { + return $this->options; + } + + /** + * @param Canvas $canvas + * @return $this + */ + public function setCanvas(Canvas $canvas) + { + $this->pdf = $canvas; + $this->canvas = $canvas; + return $this; + } + + /** + * @return Canvas + */ + public function getCanvas() + { + return $this->canvas; + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Frame.php b/library/vendor/dompdf/src/Frame.php new file mode 100644 index 000000000..6c2491d04 --- /dev/null +++ b/library/vendor/dompdf/src/Frame.php @@ -0,0 +1,1109 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +/** + * The main Frame class + * + * This class represents a single HTML element. This class stores + * positioning information as well as containing block location and + * dimensions. Style information for the element is stored in a {@link + * Style} object. Tree structure is maintained via the parent & children + * links. + * + * @package dompdf + */ +class Frame +{ + const WS_TEXT = 1; + const WS_SPACE = 2; + + /** + * The DOMElement or DOMText object this frame represents + * + * @var \DOMElement|\DOMText + */ + protected $_node; + + /** + * Unique identifier for this frame. Used to reference this frame + * via the node. + * + * @var string + */ + protected $_id; + + /** + * Unique id counter + */ + public static $ID_COUNTER = 0; /*protected*/ + + /** + * This frame's calculated style + * + * @var Style + */ + protected $_style; + + /** + * This frame's original style. Needed for cases where frames are + * split across pages. + * + * @var Style + */ + protected $_original_style; + + /** + * This frame's parent in the document tree. + * + * @var Frame + */ + protected $_parent; + + /** + * This frame's children + * + * @var Frame[] + */ + protected $_frame_list; + + /** + * This frame's first child. All children are handled as a + * doubly-linked list. + * + * @var Frame + */ + protected $_first_child; + + /** + * This frame's last child. + * + * @var Frame + */ + protected $_last_child; + + /** + * This frame's previous sibling in the document tree. + * + * @var Frame + */ + protected $_prev_sibling; + + /** + * This frame's next sibling in the document tree. + * + * @var Frame + */ + protected $_next_sibling; + + /** + * This frame's containing block (used in layout): array(x, y, w, h) + * + * @var float[] + */ + protected $_containing_block; + + /** + * Position on the page of the top-left corner of the margin box of + * this frame: array(x,y) + * + * @var float[] + */ + protected $_position; + + /** + * Absolute opacity of this frame + * + * @var float + */ + protected $_opacity; + + /** + * This frame's decorator + * + * @var \Dompdf\FrameDecorator\AbstractFrameDecorator + */ + protected $_decorator; + + /** + * This frame's containing line box + * + * @var LineBox + */ + protected $_containing_line; + + /** + * @var array + */ + protected $_is_cache = array(); + + /** + * Tells wether the frame was already pushed to the next page + * + * @var bool + */ + public $_already_pushed = false; + + /** + * @var bool + */ + public $_float_next_line = false; + + /** + * Tells wether the frame was split + * + * @var bool + */ + public $_splitted; + + /** + * @var int + */ + public static $_ws_state = self::WS_SPACE; + + /** + * Class constructor + * + * @param \DOMNode $node the DOMNode this frame represents + */ + public function __construct(\DOMNode $node) + { + $this->_node = $node; + + $this->_parent = null; + $this->_first_child = null; + $this->_last_child = null; + $this->_prev_sibling = $this->_next_sibling = null; + + $this->_style = null; + $this->_original_style = null; + + $this->_containing_block = array( + "x" => null, + "y" => null, + "w" => null, + "h" => null, + ); + + $this->_containing_block[0] =& $this->_containing_block["x"]; + $this->_containing_block[1] =& $this->_containing_block["y"]; + $this->_containing_block[2] =& $this->_containing_block["w"]; + $this->_containing_block[3] =& $this->_containing_block["h"]; + + $this->_position = array( + "x" => null, + "y" => null, + ); + + $this->_position[0] =& $this->_position["x"]; + $this->_position[1] =& $this->_position["y"]; + + $this->_opacity = 1.0; + $this->_decorator = null; + + $this->set_id(self::$ID_COUNTER++); + } + + /** + * WIP : preprocessing to remove all the unused whitespace + */ + protected function ws_trim() + { + if ($this->ws_keep()) { + return; + } + + if (self::$_ws_state === self::WS_SPACE) { + $node = $this->_node; + + if ($node->nodeName === "#text" && !empty($node->nodeValue)) { + $node->nodeValue = preg_replace("/[ \t\r\n\f]+/u", " ", trim($node->nodeValue)); + self::$_ws_state = self::WS_TEXT; + } + } + } + + /** + * @return bool + */ + protected function ws_keep() + { + $whitespace = $this->get_style()->white_space; + + return in_array($whitespace, array("pre", "pre-wrap", "pre-line")); + } + + /** + * @return bool + */ + protected function ws_is_text() + { + $node = $this->get_node(); + + if ($node->nodeName === "img") { + return true; + } + + if (!$this->is_in_flow()) { + return false; + } + + if ($this->is_text_node()) { + return trim($node->nodeValue) !== ""; + } + + return true; + } + + /** + * "Destructor": forcibly free all references held by this frame + * + * @param bool $recursive if true, call dispose on all children + */ + public function dispose($recursive = false) + { + if ($recursive) { + while ($child = $this->_first_child) { + $child->dispose(true); + } + } + + // Remove this frame from the tree + if ($this->_prev_sibling) { + $this->_prev_sibling->_next_sibling = $this->_next_sibling; + } + + if ($this->_next_sibling) { + $this->_next_sibling->_prev_sibling = $this->_prev_sibling; + } + + if ($this->_parent && $this->_parent->_first_child === $this) { + $this->_parent->_first_child = $this->_next_sibling; + } + + if ($this->_parent && $this->_parent->_last_child === $this) { + $this->_parent->_last_child = $this->_prev_sibling; + } + + if ($this->_parent) { + $this->_parent->get_node()->removeChild($this->_node); + } + + $this->_style->dispose(); + $this->_style = null; + unset($this->_style); + + $this->_original_style->dispose(); + $this->_original_style = null; + unset($this->_original_style); + + } + + /** + * Re-initialize the frame + */ + public function reset() + { + $this->_position["x"] = null; + $this->_position["y"] = null; + + $this->_containing_block["x"] = null; + $this->_containing_block["y"] = null; + $this->_containing_block["w"] = null; + $this->_containing_block["h"] = null; + + $this->_style = null; + unset($this->_style); + $this->_style = clone $this->_original_style; + } + + /** + * @return \DOMElement|\DOMText + */ + public function get_node() + { + return $this->_node; + } + + /** + * @return string + */ + public function get_id() + { + return $this->_id; + } + + /** + * @return Style + */ + public function get_style() + { + return $this->_style; + } + + /** + * @return Style + */ + public function get_original_style() + { + return $this->_original_style; + } + + /** + * @return Frame + */ + public function get_parent() + { + return $this->_parent; + } + + /** + * @return \Dompdf\FrameDecorator\AbstractFrameDecorator + */ + public function get_decorator() + { + return $this->_decorator; + } + + /** + * @return Frame + */ + public function get_first_child() + { + return $this->_first_child; + } + + /** + * @return Frame + */ + public function get_last_child() + { + return $this->_last_child; + } + + /** + * @return Frame + */ + public function get_prev_sibling() + { + return $this->_prev_sibling; + } + + /** + * @return Frame + */ + public function get_next_sibling() + { + return $this->_next_sibling; + } + + /** + * @return FrameList|Frame[] + */ + public function get_children() + { + if (isset($this->_frame_list)) { + return $this->_frame_list; + } + + $this->_frame_list = new FrameList($this); + + return $this->_frame_list; + } + + // Layout property accessors + + /** + * Containing block dimensions + * + * @param $i string The key of the wanted containing block's dimension (x, y, x, h) + * + * @return float[]|float + */ + public function get_containing_block($i = null) + { + if (isset($i)) { + return $this->_containing_block[$i]; + } + + return $this->_containing_block; + } + + /** + * Block position + * + * @param $i string The key of the wanted position value (x, y) + * + * @return array|float + */ + public function get_position($i = null) + { + if (isset($i)) { + return $this->_position[$i]; + } + + return $this->_position; + } + + //........................................................................ + + /** + * Return the height of the margin box of the frame, in pt. Meaningless + * unless the height has been calculated properly. + * + * @return float + */ + public function get_margin_height() + { + $style = $this->_style; + + return $style->length_in_pt(array( + $style->height, + $style->margin_top, + $style->margin_bottom, + $style->border_top_width, + $style->border_bottom_width, + $style->padding_top, + $style->padding_bottom + ), $this->_containing_block["h"]); + } + + /** + * Return the width of the margin box of the frame, in pt. Meaningless + * unless the width has been calculated properly. + * + * @return float + */ + public function get_margin_width() + { + $style = $this->_style; + + return $style->length_in_pt(array( + $style->width, + $style->margin_left, + $style->margin_right, + $style->border_left_width, + $style->border_right_width, + $style->padding_left, + $style->padding_right + ), $this->_containing_block["w"]); + } + + /** + * @return float + */ + public function get_break_margins() + { + $style = $this->_style; + + return $style->length_in_pt(array( + //$style->height, + $style->margin_top, + $style->margin_bottom, + $style->border_top_width, + $style->border_bottom_width, + $style->padding_top, + $style->padding_bottom + ), $this->_containing_block["h"]); + } + + /** + * Return the padding box (x,y,w,h) of the frame + * + * @return array + */ + public function get_padding_box() + { + $style = $this->_style; + $cb = $this->_containing_block; + + $x = $this->_position["x"] + + $style->length_in_pt(array($style->margin_left, + $style->border_left_width), + $cb["w"]); + + $y = $this->_position["y"] + + $style->length_in_pt(array($style->margin_top, + $style->border_top_width), + $cb["h"]); + + $w = $style->length_in_pt(array($style->padding_left, + $style->width, + $style->padding_right), + $cb["w"]); + + $h = $style->length_in_pt(array($style->padding_top, + $style->height, + $style->padding_bottom), + $cb["h"]); + + return array(0 => $x, "x" => $x, + 1 => $y, "y" => $y, + 2 => $w, "w" => $w, + 3 => $h, "h" => $h); + } + + /** + * Return the border box of the frame + * + * @return array + */ + public function get_border_box() + { + $style = $this->_style; + $cb = $this->_containing_block; + + $x = $this->_position["x"] + $style->length_in_pt($style->margin_left, $cb["w"]); + + $y = $this->_position["y"] + $style->length_in_pt($style->margin_top, $cb["h"]); + + $w = $style->length_in_pt(array($style->border_left_width, + $style->padding_left, + $style->width, + $style->padding_right, + $style->border_right_width), + $cb["w"]); + + $h = $style->length_in_pt(array($style->border_top_width, + $style->padding_top, + $style->height, + $style->padding_bottom, + $style->border_bottom_width), + $cb["h"]); + + return array(0 => $x, "x" => $x, + 1 => $y, "y" => $y, + 2 => $w, "w" => $w, + 3 => $h, "h" => $h); + } + + /** + * @param null $opacity + * + * @return float + */ + public function get_opacity($opacity = null) + { + if ($opacity !== null) { + $this->set_opacity($opacity); + } + + return $this->_opacity; + } + + /** + * @return LineBox + */ + public function &get_containing_line() + { + return $this->_containing_line; + } + + //........................................................................ + + // Set methods + /** + * @param $id + */ + public function set_id($id) + { + $this->_id = $id; + + // We can only set attributes of DOMElement objects (nodeType == 1). + // Since these are the only objects that we can assign CSS rules to, + // this shortcoming is okay. + if ($this->_node->nodeType == XML_ELEMENT_NODE) { + $this->_node->setAttribute("frame_id", $id); + } + } + + /** + * @param Style $style + */ + public function set_style(Style $style) + { + if (is_null($this->_style)) { + $this->_original_style = clone $style; + } + + //$style->set_frame($this); + $this->_style = $style; + } + + /** + * @param \Dompdf\FrameDecorator\AbstractFrameDecorator $decorator + */ + public function set_decorator(FrameDecorator\AbstractFrameDecorator $decorator) + { + $this->_decorator = $decorator; + } + + /** + * @param null $x + * @param null $y + * @param null $w + * @param null $h + */ + public function set_containing_block($x = null, $y = null, $w = null, $h = null) + { + if (is_array($x)) { + foreach ($x as $key => $val) { + $$key = $val; + } + } + + if (is_numeric($x)) { + $this->_containing_block["x"] = $x; + } + + if (is_numeric($y)) { + $this->_containing_block["y"] = $y; + } + + if (is_numeric($w)) { + $this->_containing_block["w"] = $w; + } + + if (is_numeric($h)) { + $this->_containing_block["h"] = $h; + } + } + + /** + * @param null $x + * @param null $y + */ + public function set_position($x = null, $y = null) + { + if (is_array($x)) { + list($x, $y) = array($x["x"], $x["y"]); + } + + if (is_numeric($x)) { + $this->_position["x"] = $x; + } + + if (is_numeric($y)) { + $this->_position["y"] = $y; + } + } + + /** + * @param $opacity + */ + public function set_opacity($opacity) + { + $parent = $this->get_parent(); + $base_opacity = (($parent && $parent->_opacity !== null) ? $parent->_opacity : 1.0); + $this->_opacity = $base_opacity * $opacity; + } + + /** + * @param LineBox $line + */ + public function set_containing_line(LineBox $line) + { + $this->_containing_line = $line; + } + + /** + * Tells if the frame is a text node + * + * @return bool + */ + public function is_text_node() + { + if (isset($this->_is_cache["text_node"])) { + return $this->_is_cache["text_node"]; + } + + return $this->_is_cache["text_node"] = ($this->get_node()->nodeName === "#text"); + } + + /** + * @return bool + */ + public function is_positionned() + { + if (isset($this->_is_cache["positionned"])) { + return $this->_is_cache["positionned"]; + } + + $position = $this->get_style()->position; + + return $this->_is_cache["positionned"] = in_array($position, Style::$POSITIONNED_TYPES); + } + + /** + * @return bool + */ + public function is_absolute() + { + if (isset($this->_is_cache["absolute"])) { + return $this->_is_cache["absolute"]; + } + + $position = $this->get_style()->position; + + return $this->_is_cache["absolute"] = ($position === "absolute" || $position === "fixed"); + } + + /** + * @return bool + */ + public function is_block() + { + if (isset($this->_is_cache["block"])) { + return $this->_is_cache["block"]; + } + + return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::$BLOCK_TYPES); + } + + /** + * @return bool + */ + public function is_in_flow() + { + if (isset($this->_is_cache["in_flow"])) { + return $this->_is_cache["in_flow"]; + } + return $this->_is_cache["in_flow"] = !($this->get_style()->float !== "none" || $this->is_absolute()); + } + + /** + * @return bool + */ + public function is_pre() + { + if (isset($this->_is_cache["pre"])) { + return $this->_is_cache["pre"]; + } + + $white_space = $this->get_style()->white_space; + + return $this->_is_cache["pre"] = in_array($white_space, array("pre", "pre-wrap")); + } + + /** + * @return bool + */ + public function is_table() + { + if (isset($this->_is_cache["table"])) { + return $this->_is_cache["table"]; + } + + $display = $this->get_style()->display; + + return $this->_is_cache["table"] = in_array($display, Style::$TABLE_TYPES); + } + + + /** + * Inserts a new child at the beginning of the Frame + * + * @param $child Frame The new Frame to insert + * @param $update_node boolean Whether or not to update the DOM + */ + public function prepend_child(Frame $child, $update_node = true) + { + if ($update_node) { + $this->_node->insertBefore($child->_node, $this->_first_child ? $this->_first_child->_node : null); + } + + // Remove the child from its parent + if ($child->_parent) { + $child->_parent->remove_child($child, false); + } + + $child->_parent = $this; + $child->_prev_sibling = null; + + // Handle the first child + if (!$this->_first_child) { + $this->_first_child = $child; + $this->_last_child = $child; + $child->_next_sibling = null; + } else { + $this->_first_child->_prev_sibling = $child; + $child->_next_sibling = $this->_first_child; + $this->_first_child = $child; + } + } + + /** + * Inserts a new child at the end of the Frame + * + * @param $child Frame The new Frame to insert + * @param $update_node boolean Whether or not to update the DOM + */ + public function append_child(Frame $child, $update_node = true) + { + if ($update_node) { + $this->_node->appendChild($child->_node); + } + + // Remove the child from its parent + if ($child->_parent) { + $child->_parent->remove_child($child, false); + } + + $child->_parent = $this; + $child->_next_sibling = null; + + // Handle the first child + if (!$this->_last_child) { + $this->_first_child = $child; + $this->_last_child = $child; + $child->_prev_sibling = null; + } else { + $this->_last_child->_next_sibling = $child; + $child->_prev_sibling = $this->_last_child; + $this->_last_child = $child; + } + } + + /** + * Inserts a new child immediately before the specified frame + * + * @param $new_child Frame The new Frame to insert + * @param $ref Frame The Frame after the new Frame + * @param $update_node boolean Whether or not to update the DOM + * + * @throws Exception + */ + public function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) + { + if ($ref === $this->_first_child) { + $this->prepend_child($new_child, $update_node); + + return; + } + + if (is_null($ref)) { + $this->append_child($new_child, $update_node); + + return; + } + + if ($ref->_parent !== $this) { + throw new Exception("Reference child is not a child of this node."); + } + + // Update the node + if ($update_node) { + $this->_node->insertBefore($new_child->_node, $ref->_node); + } + + // Remove the child from its parent + if ($new_child->_parent) { + $new_child->_parent->remove_child($new_child, false); + } + + $new_child->_parent = $this; + $new_child->_next_sibling = $ref; + $new_child->_prev_sibling = $ref->_prev_sibling; + + if ($ref->_prev_sibling) { + $ref->_prev_sibling->_next_sibling = $new_child; + } + + $ref->_prev_sibling = $new_child; + } + + /** + * Inserts a new child immediately after the specified frame + * + * @param $new_child Frame The new Frame to insert + * @param $ref Frame The Frame before the new Frame + * @param $update_node boolean Whether or not to update the DOM + * + * @throws Exception + */ + public function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) + { + if ($ref === $this->_last_child) { + $this->append_child($new_child, $update_node); + + return; + } + + if (is_null($ref)) { + $this->prepend_child($new_child, $update_node); + + return; + } + + if ($ref->_parent !== $this) { + throw new Exception("Reference child is not a child of this node."); + } + + // Update the node + if ($update_node) { + if ($ref->_next_sibling) { + $next_node = $ref->_next_sibling->_node; + $this->_node->insertBefore($new_child->_node, $next_node); + } else { + $new_child->_node = $this->_node->appendChild($new_child->_node); + } + } + + // Remove the child from its parent + if ($new_child->_parent) { + $new_child->_parent->remove_child($new_child, false); + } + + $new_child->_parent = $this; + $new_child->_prev_sibling = $ref; + $new_child->_next_sibling = $ref->_next_sibling; + + if ($ref->_next_sibling) { + $ref->_next_sibling->_prev_sibling = $new_child; + } + + $ref->_next_sibling = $new_child; + } + + /** + * Remove a child frame + * + * @param Frame $child + * @param boolean $update_node Whether or not to remove the DOM node + * + * @throws Exception + * @return Frame The removed child frame + */ + public function remove_child(Frame $child, $update_node = true) + { + if ($child->_parent !== $this) { + throw new Exception("Child not found in this frame"); + } + + if ($update_node) { + $this->_node->removeChild($child->_node); + } + + if ($child === $this->_first_child) { + $this->_first_child = $child->_next_sibling; + } + + if ($child === $this->_last_child) { + $this->_last_child = $child->_prev_sibling; + } + + if ($child->_prev_sibling) { + $child->_prev_sibling->_next_sibling = $child->_next_sibling; + } + + if ($child->_next_sibling) { + $child->_next_sibling->_prev_sibling = $child->_prev_sibling; + } + + $child->_next_sibling = null; + $child->_prev_sibling = null; + $child->_parent = null; + + return $child; + } + + //........................................................................ + + // Debugging function: + /** + * @return string + */ + public function __toString() + { + // Skip empty text frames +// if ( $this->is_text_node() && +// preg_replace("/\s/", "", $this->_node->data) === "" ) +// return ""; + + + $str = "" . $this->_node->nodeName . ":
"; + //$str .= spl_object_hash($this->_node) . "
"; + $str .= "Id: " . $this->get_id() . "
"; + $str .= "Class: " . get_class($this) . "
"; + + if ($this->is_text_node()) { + $tmp = htmlspecialchars($this->_node->nodeValue); + $str .= "
'" . mb_substr($tmp, 0, 70) .
+                (mb_strlen($tmp) > 70 ? "..." : "") . "'
"; + } elseif ($css_class = $this->_node->getAttribute("class")) { + $str .= "CSS class: '$css_class'
"; + } + + if ($this->_parent) { + $str .= "\nParent:" . $this->_parent->_node->nodeName . + " (" . spl_object_hash($this->_parent->_node) . ") " . + "
"; + } + + if ($this->_prev_sibling) { + $str .= "Prev: " . $this->_prev_sibling->_node->nodeName . + " (" . spl_object_hash($this->_prev_sibling->_node) . ") " . + "
"; + } + + if ($this->_next_sibling) { + $str .= "Next: " . $this->_next_sibling->_node->nodeName . + " (" . spl_object_hash($this->_next_sibling->_node) . ") " . + "
"; + } + + $d = $this->get_decorator(); + while ($d && $d != $d->get_decorator()) { + $str .= "Decorator: " . get_class($d) . "
"; + $d = $d->get_decorator(); + } + + $str .= "Position: " . Helpers::pre_r($this->_position, true); + $str .= "\nContaining block: " . Helpers::pre_r($this->_containing_block, true); + $str .= "\nMargin width: " . Helpers::pre_r($this->get_margin_width(), true); + $str .= "\nMargin height: " . Helpers::pre_r($this->get_margin_height(), true); + + $str .= "\nStyle:
" . $this->_style->__toString() . "
"; + + if ($this->_decorator instanceof FrameDecorator\Block) { + $str .= "Lines:
";
+            foreach ($this->_decorator->get_line_boxes() as $line) {
+                foreach ($line->get_frames() as $frame) {
+                    if ($frame instanceof FrameDecorator\Text) {
+                        $str .= "\ntext: ";
+                        $str .= "'" . htmlspecialchars($frame->get_text()) . "'";
+                    } else {
+                        $str .= "\nBlock: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")";
+                    }
+                }
+
+                $str .=
+                    "\ny => " . $line->y . "\n" .
+                    "w => " . $line->w . "\n" .
+                    "h => " . $line->h . "\n" .
+                    "left => " . $line->left . "\n" .
+                    "right => " . $line->right . "\n";
+            }
+            $str .= "
"; + } + + $str .= "\n"; + if (php_sapi_name() === "cli") { + $str = strip_tags(str_replace(array("
", "", ""), + array("\n", "", ""), + $str)); + } + + return $str; + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Frame/Factory.php b/library/vendor/dompdf/src/Frame/Factory.php new file mode 100644 index 000000000..431a5c10c --- /dev/null +++ b/library/vendor/dompdf/src/Frame/Factory.php @@ -0,0 +1,262 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Frame; + +use Dompdf\Css\Style; +use Dompdf\Dompdf; +use Dompdf\Exception; +use Dompdf\Frame; +use Dompdf\FrameDecorator\AbstractFrameDecorator; +use DOMXPath; +use Dompdf\FrameDecorator\Page as PageFrameDecorator; +use Dompdf\FrameReflower\Page as PageFrameReflower; + +/** + * Contains frame decorating logic + * + * This class is responsible for assigning the correct {@link AbstractFrameDecorator}, + * {@link AbstractPositioner}, and {@link AbstractFrameReflower} objects to {@link Frame} + * objects. This is determined primarily by the Frame's display type, but + * also by the Frame's node's type (e.g. DomElement vs. #text) + * + * @access private + * @package dompdf + */ +class Factory +{ + + /** + * Decorate the root Frame + * + * @param $root Frame The frame to decorate + * @param $dompdf Dompdf The dompdf instance + * + * @return PageFrameDecorator + */ + static function decorate_root(Frame $root, Dompdf $dompdf) + { + $frame = new PageFrameDecorator($root, $dompdf); + $frame->set_reflower(new PageFrameReflower($frame)); + $root->set_decorator($frame); + + return $frame; + } + + /** + * Decorate a Frame + * + * @param Frame $frame The frame to decorate + * @param Dompdf $dompdf The dompdf instance + * @param Frame $root The frame to decorate + * + * @throws Exception + * @return AbstractFrameDecorator + * FIXME: this is admittedly a little smelly... + */ + static function decorate_frame(Frame $frame, Dompdf $dompdf, Frame $root = null) + { + if (is_null($dompdf)) { + throw new Exception("The DOMPDF argument is required"); + } + + $style = $frame->get_style(); + + // Floating (and more generally out-of-flow) elements are blocks + // http://coding.smashingmagazine.com/2007/05/01/css-float-theory-things-you-should-know/ + if (!$frame->is_in_flow() && in_array($style->display, Style::$INLINE_TYPES)) { + $style->display = "block"; + } + + $display = $style->display; + + switch ($display) { + + case "block": + $positioner = "Block"; + $decorator = "Block"; + $reflower = "Block"; + break; + + case "inline-block": + $positioner = "Inline"; + $decorator = "Block"; + $reflower = "Block"; + break; + + case "inline": + $positioner = "Inline"; + if ($frame->is_text_node()) { + $decorator = "Text"; + $reflower = "Text"; + } else { + if ($style->float !== "none") { + $decorator = "Block"; + $reflower = "Block"; + } else { + $decorator = "Inline"; + $reflower = "Inline"; + } + } + break; + + case "table": + $positioner = "Block"; + $decorator = "Table"; + $reflower = "Table"; + break; + + case "inline-table": + $positioner = "Inline"; + $decorator = "Table"; + $reflower = "Table"; + break; + + case "table-row-group": + case "table-header-group": + case "table-footer-group": + $positioner = "NullPositioner"; + $decorator = "TableRowGroup"; + $reflower = "TableRowGroup"; + break; + + case "table-row": + $positioner = "NullPositioner"; + $decorator = "TableRow"; + $reflower = "TableRow"; + break; + + case "table-cell": + $positioner = "TableCell"; + $decorator = "TableCell"; + $reflower = "TableCell"; + break; + + case "list-item": + $positioner = "Block"; + $decorator = "Block"; + $reflower = "Block"; + break; + + case "-dompdf-list-bullet": + if ($style->list_style_position === "inside") { + $positioner = "Inline"; + } else { + $positioner = "ListBullet"; + } + + if ($style->list_style_image !== "none") { + $decorator = "ListBulletImage"; + } else { + $decorator = "ListBullet"; + } + + $reflower = "ListBullet"; + break; + + case "-dompdf-image": + $positioner = "Inline"; + $decorator = "Image"; + $reflower = "Image"; + break; + + case "-dompdf-br": + $positioner = "Inline"; + $decorator = "Inline"; + $reflower = "Inline"; + break; + + default: + // FIXME: should throw some sort of warning or something? + case "none": + if ($style->_dompdf_keep !== "yes") { + // Remove the node and the frame + $frame->get_parent()->remove_child($frame); + return; + } + + $positioner = "NullPositioner"; + $decorator = "NullFrameDecorator"; + $reflower = "NullFrameReflower"; + break; + } + + // Handle CSS position + $position = $style->position; + + if ($position === "absolute") { + $positioner = "Absolute"; + } else { + if ($position === "fixed") { + $positioner = "Fixed"; + } + } + + $node = $frame->get_node(); + + // Handle nodeName + if ($node->nodeName === "img") { + $style->display = "-dompdf-image"; + $decorator = "Image"; + $reflower = "Image"; + } + + $positioner = "Dompdf\\Positioner\\$positioner"; + $decorator = "Dompdf\\FrameDecorator\\$decorator"; + $reflower = "Dompdf\\FrameReflower\\$reflower"; + + /** @var AbstractFrameDecorator $deco */ + $deco = new $decorator($frame, $dompdf); + + $deco->set_positioner(new $positioner($deco)); + $deco->set_reflower(new $reflower($deco, $dompdf->getFontMetrics())); + + if ($root) { + $deco->set_root($root); + } + + if ($display === "list-item") { + // Insert a list-bullet frame + $xml = $dompdf->get_dom(); + $bullet_node = $xml->createElement("bullet"); // arbitrary choice + $b_f = new Frame($bullet_node); + + $node = $frame->get_node(); + $parent_node = $node->parentNode; + + if ($parent_node) { + if (!$parent_node->hasAttribute("dompdf-children-count")) { + $xpath = new DOMXPath($xml); + $count = $xpath->query("li", $parent_node)->length; + $parent_node->setAttribute("dompdf-children-count", $count); + } + + if (is_numeric($node->getAttribute("value"))) { + $index = intval($node->getAttribute("value")); + } else { + if (!$parent_node->hasAttribute("dompdf-counter")) { + $index = ($parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1); + } else { + $index = $parent_node->getAttribute("dompdf-counter") + 1; + } + } + + $parent_node->setAttribute("dompdf-counter", $index); + $bullet_node->setAttribute("dompdf-counter", $index); + } + + $new_style = $dompdf->get_css()->create_style(); + $new_style->display = "-dompdf-list-bullet"; + $new_style->inherit($style); + $b_f->set_style($new_style); + + $deco->prepend_child(Factory::decorate_frame($b_f, $dompdf, $root)); + } + + return $deco; + } +} diff --git a/library/vendor/dompdf/src/Frame/FrameList.php b/library/vendor/dompdf/src/Frame/FrameList.php new file mode 100644 index 000000000..783600dc7 --- /dev/null +++ b/library/vendor/dompdf/src/Frame/FrameList.php @@ -0,0 +1,34 @@ +_frame = $frame; + } + + /** + * @return FrameListIterator + */ + function getIterator() + { + return new FrameListIterator($this->_frame); + } +} diff --git a/library/vendor/dompdf/src/Frame/FrameListIterator.php b/library/vendor/dompdf/src/Frame/FrameListIterator.php new file mode 100644 index 000000000..ada9dde18 --- /dev/null +++ b/library/vendor/dompdf/src/Frame/FrameListIterator.php @@ -0,0 +1,91 @@ +_parent = $frame; + $this->_cur = $frame->get_first_child(); + $this->_num = 0; + } + + /** + * + */ + public function rewind() + { + $this->_cur = $this->_parent->get_first_child(); + $this->_num = 0; + } + + /** + * @return bool + */ + public function valid() + { + return isset($this->_cur); // && ($this->_cur->get_prev_sibling() === $this->_prev); + } + + /** + * @return int + */ + public function key() + { + return $this->_num; + } + + /** + * @return Frame + */ + public function current() + { + return $this->_cur; + } + + /** + * @return Frame + */ + public function next() + { + $ret = $this->_cur; + if (!$ret) { + return null; + } + + $this->_cur = $this->_cur->get_next_sibling(); + $this->_num++; + return $ret; + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Frame/FrameTree.php b/library/vendor/dompdf/src/Frame/FrameTree.php new file mode 100644 index 000000000..ff882ff33 --- /dev/null +++ b/library/vendor/dompdf/src/Frame/FrameTree.php @@ -0,0 +1,300 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +/** + * Represents an entire document as a tree of frames + * + * The FrameTree consists of {@link Frame} objects each tied to specific + * DOMNode objects in a specific DomDocument. The FrameTree has the same + * structure as the DomDocument, but adds additional capabalities for + * styling and layout. + * + * @package dompdf + */ +class FrameTree +{ + /** + * Tags to ignore while parsing the tree + * + * @var array + */ + protected static $HIDDEN_TAGS = array( + "area", + "base", + "basefont", + "head", + "style", + "meta", + "title", + "colgroup", + "noembed", + "noscript", + "param", + "#comment" + ); + + /** + * The main DomDocument + * + * @see http://ca2.php.net/manual/en/ref.dom.php + * @var DOMDocument + */ + protected $_dom; + + /** + * The root node of the FrameTree. + * + * @var Frame + */ + protected $_root; + + /** + * Subtrees of absolutely positioned elements + * + * @var array of Frames + */ + protected $_absolute_frames; + + /** + * A mapping of {@link Frame} objects to DOMNode objects + * + * @var array + */ + protected $_registry; + + /** + * Class constructor + * + * @param DOMDocument $dom the main DomDocument object representing the current html document + */ + public function __construct(DomDocument $dom) + { + $this->_dom = $dom; + $this->_root = null; + $this->_registry = array(); + } + + /** + * Returns the DOMDocument object representing the curent html document + * + * @return DOMDocument + */ + public function get_dom() + { + return $this->_dom; + } + + /** + * Returns the root frame of the tree + * + * @return \Dompdf\FrameDecorator\Page + */ + public function get_root() + { + return $this->_root; + } + + /** + * Returns a specific frame given its id + * + * @param string $id + * + * @return Frame + */ + public function get_frame($id) + { + return isset($this->_registry[$id]) ? $this->_registry[$id] : null; + } + + /** + * Returns a post-order iterator for all frames in the tree + * + * @return FrameTreeList|Frame[] + */ + public function get_frames() + { + return new FrameTreeList($this->_root); + } + + /** + * Builds the tree + */ + public function build_tree() + { + $html = $this->_dom->getElementsByTagName("html")->item(0); + if (is_null($html)) { + $html = $this->_dom->firstChild; + } + + if (is_null($html)) { + throw new Exception("Requested HTML document contains no data."); + } + + $this->fix_tables(); + + $this->_root = $this->_build_tree_r($html); + } + + /** + * Adds missing TBODYs around TR + */ + protected function fix_tables() + { + $xp = new DOMXPath($this->_dom); + + // Move table caption before the table + // FIXME find a better way to deal with it... + $captions = $xp->query("//table/caption"); + foreach ($captions as $caption) { + $table = $caption->parentNode; + $table->parentNode->insertBefore($caption, $table); + } + + $rows = $xp->query("//table/tr"); + foreach ($rows as $row) { + $tbody = $this->_dom->createElement("tbody"); + $tbody = $row->parentNode->insertBefore($tbody, $row); + $tbody->appendChild($row); + } + } + + // FIXME: temporary hack, preferably we will improve rendering of sequential #text nodes + /** + * Remove a child from a node + * + * Remove a child from a node. If the removed node results in two + * adjacent #text nodes then combine them. + * + * @param DONNode $node the current DOMNode being considered + * @param array $children an array of nodes that are the children of $node + * @param $index index from the $children array of the node to remove + */ + protected function _remove_node(DOMNode $node, array &$children, $index) + { + $child = $children[$index]; + $previousChild = $child->previousSibling; + $nextChild = $child->nextSibling; + $node->removeChild($child); + if (isset($previousChild, $nextChild)) { + if ($previousChild->nodeName === "#text" && $nextChild->nodeName === "#text") + { + $previousChild->nodeValue .= $nextChild->nodeValue; + $this->_remove_node($node, $children, $index+1); + } + } + array_splice($children, $index, 1); + } + + /** + * Recursively adds {@link Frame} objects to the tree + * + * Recursively build a tree of Frame objects based on a dom tree. + * No layout information is calculated at this time, although the + * tree may be adjusted (i.e. nodes and frames for generated content + * and images may be created). + * + * @param DOMNode $node the current DOMNode being considered + * + * @return Frame + */ + protected function _build_tree_r(DOMNode $node) + { + $frame = new Frame($node); + $id = $frame->get_id(); + $this->_registry[$id] = $frame; + + if (!$node->hasChildNodes()) { + return $frame; + } + + // Store the children in an array so that the tree can be modified + $children = array(); + $length = $node->childNodes->length; + for ($i = 0; $i < $length; $i++) { + $children[] = $node->childNodes->item($i); + } + $index = 0; + // INFO: We don't advance $index if a node is removed to avoid skipping nodes + while ($index < count($children)) { + $child = $children[$index]; + $nodeName = strtolower($child->nodeName); + + // Skip non-displaying nodes + if (in_array($nodeName, self::$HIDDEN_TAGS)) { + if ($nodeName !== "head" && $nodeName !== "style") { + $this->_remove_node($node, $children, $index); + } else { + $index++; + } + continue; + } + // Skip empty text nodes + if ($nodeName === "#text" && $child->nodeValue === "") { + $this->_remove_node($node, $children, $index); + continue; + } + // Skip empty image nodes + if ($nodeName === "img" && $child->getAttribute("src") === "") { + $this->_remove_node($node, $children, $index); + continue; + } + + if (is_object($child)) { + $frame->append_child($this->_build_tree_r($child), false); + } + $index++; + } + + return $frame; + } + + /** + * @param DOMNode $node + * @param DOMNode $new_node + * @param string $pos + * + * @return mixed + */ + public function insert_node(DOMNode $node, DOMNode $new_node, $pos) + { + if ($pos === "after" || !$node->firstChild) { + $node->appendChild($new_node); + } else { + $node->insertBefore($new_node, $node->firstChild); + } + + $this->_build_tree_r($new_node); + + $frame_id = $new_node->getAttribute("frame_id"); + $frame = $this->get_frame($frame_id); + + $parent_id = $node->getAttribute("frame_id"); + $parent = $this->get_frame($parent_id); + + if ($parent) { + if ($pos === "before") { + $parent->prepend_child($frame, false); + } else { + $parent->append_child($frame, false); + } + } + + return $frame_id; + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Frame/FrameTreeIterator.php b/library/vendor/dompdf/src/Frame/FrameTreeIterator.php new file mode 100644 index 000000000..ebca7e99f --- /dev/null +++ b/library/vendor/dompdf/src/Frame/FrameTreeIterator.php @@ -0,0 +1,96 @@ +_stack[] = $this->_root = $root; + $this->_num = 0; + } + + /** + * + */ + public function rewind() + { + $this->_stack = array($this->_root); + $this->_num = 0; + } + + /** + * @return bool + */ + public function valid() + { + return count($this->_stack) > 0; + } + + /** + * @return int + */ + public function key() + { + return $this->_num; + } + + /** + * @return Frame + */ + public function current() + { + return end($this->_stack); + } + + /** + * @return Frame + */ + public function next() + { + $b = end($this->_stack); + + // Pop last element + unset($this->_stack[key($this->_stack)]); + $this->_num++; + + // Push all children onto the stack in reverse order + if ($c = $b->get_last_child()) { + $this->_stack[] = $c; + while ($c = $c->get_prev_sibling()) { + $this->_stack[] = $c; + } + } + + return $b; + } +} + diff --git a/library/vendor/dompdf/src/Frame/FrameTreeList.php b/library/vendor/dompdf/src/Frame/FrameTreeList.php new file mode 100644 index 000000000..f8b996c68 --- /dev/null +++ b/library/vendor/dompdf/src/Frame/FrameTreeList.php @@ -0,0 +1,35 @@ +_root = $root; + } + + /** + * @return FrameTreeIterator + */ + public function getIterator() + { + return new FrameTreeIterator($this->_root); + } +} diff --git a/library/vendor/dompdf/src/FrameDecorator/AbstractFrameDecorator.php b/library/vendor/dompdf/src/FrameDecorator/AbstractFrameDecorator.php new file mode 100644 index 000000000..b574991e6 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/AbstractFrameDecorator.php @@ -0,0 +1,808 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +/** + * Base AbstractFrameDecorator class + * + * @package dompdf + */ +abstract class AbstractFrameDecorator extends Frame +{ + const DEFAULT_COUNTER = "-dompdf-default-counter"; + + public $_counters = array(); // array([id] => counter_value) (for generated content) + + /** + * The root node of the DOM tree + * + * @var Frame + */ + protected $_root; + + /** + * The decorated frame + * + * @var Frame + */ + protected $_frame; + + /** + * AbstractPositioner object used to position this frame (Strategy pattern) + * + * @var AbstractPositioner + */ + protected $_positioner; + + /** + * Reflower object used to calculate frame dimensions (Strategy pattern) + * + * @var \Dompdf\FrameReflower\AbstractFrameReflower + */ + protected $_reflower; + + /** + * Reference to the current dompdf instance + * + * @var Dompdf + */ + protected $_dompdf; + + /** + * First block parent + * + * @var Block + */ + private $_block_parent; + + /** + * First positionned parent (position: relative | absolute | fixed) + * + * @var AbstractFrameDecorator + */ + private $_positionned_parent; + + /** + * Class constructor + * + * @param Frame $frame The decoration target + * @param Dompdf $dompdf The Dompdf object + */ + function __construct(Frame $frame, Dompdf $dompdf) + { + $this->_frame = $frame; + $this->_root = null; + $this->_dompdf = $dompdf; + $frame->set_decorator($this); + } + + /** + * "Destructor": foribly free all references held by this object + * + * @param bool $recursive if true, call dispose on all children + */ + function dispose($recursive = false) + { + if ($recursive) { + while ($child = $this->get_first_child()) { + $child->dispose(true); + } + } + + $this->_root = null; + unset($this->_root); + + $this->_frame->dispose(true); + $this->_frame = null; + unset($this->_frame); + + $this->_positioner = null; + unset($this->_positioner); + + $this->_reflower = null; + unset($this->_reflower); + } + + /** + * Return a copy of this frame with $node as its node + * + * @param DOMNode $node + * + * @return Frame + */ + function copy(DOMNode $node) + { + $frame = new Frame($node); + $frame->set_style(clone $this->_frame->get_original_style()); + + return Factory::decorate_frame($frame, $this->_dompdf, $this->_root); + } + + /** + * Create a deep copy: copy this node and all children + * + * @return Frame + */ + function deep_copy() + { + $node = $this->_frame->get_node(); + + if ($node instanceof DOMElement && $node->hasAttribute("id")) { + $node->setAttribute("data-dompdf-original-id", $node->getAttribute("id")); + $node->removeAttribute("id"); + } + + $frame = new Frame($node->cloneNode()); + $frame->set_style(clone $this->_frame->get_original_style()); + + $deco = Factory::decorate_frame($frame, $this->_dompdf, $this->_root); + + foreach ($this->get_children() as $child) { + $deco->append_child($child->deep_copy()); + } + + return $deco; + } + + /** + * Delegate calls to decorated frame object + */ + function reset() + { + $this->_frame->reset(); + + $this->_counters = array(); + + // Reset all children + foreach ($this->get_children() as $child) { + $child->reset(); + } + } + + // Getters ----------- + function get_id() + { + return $this->_frame->get_id(); + } + + /** + * @return Frame + */ + function get_frame() + { + return $this->_frame; + } + + /** + * @return DOMElement|DOMText + */ + function get_node() + { + return $this->_frame->get_node(); + } + + /** + * @return Style + */ + function get_style() + { + return $this->_frame->get_style(); + } + + /** + * @return Style + */ + function get_original_style() + { + return $this->_frame->get_original_style(); + } + + /** + * @param integer $i + * + * @return array|float + */ + function get_containing_block($i = null) + { + return $this->_frame->get_containing_block($i); + } + + /** + * @param integer $i + * + * @return array|float + */ + function get_position($i = null) + { + return $this->_frame->get_position($i); + } + + /** + * @return Dompdf + */ + function get_dompdf() + { + return $this->_dompdf; + } + + /** + * @return float + */ + function get_margin_height() + { + return $this->_frame->get_margin_height(); + } + + /** + * @return float + */ + function get_margin_width() + { + return $this->_frame->get_margin_width(); + } + + /** + * @return array + */ + function get_padding_box() + { + return $this->_frame->get_padding_box(); + } + + /** + * @return array + */ + function get_border_box() + { + return $this->_frame->get_border_box(); + } + + /** + * @param integer $id + */ + function set_id($id) + { + $this->_frame->set_id($id); + } + + /** + * @param Style $style + */ + function set_style(Style $style) + { + $this->_frame->set_style($style); + } + + /** + * @param float $x + * @param float $y + * @param float $w + * @param float $h + */ + function set_containing_block($x = null, $y = null, $w = null, $h = null) + { + $this->_frame->set_containing_block($x, $y, $w, $h); + } + + /** + * @param float $x + * @param float $y + */ + function set_position($x = null, $y = null) + { + $this->_frame->set_position($x, $y); + } + + /** + * @return string + */ + function __toString() + { + return $this->_frame->__toString(); + } + + /** + * @param Frame $child + * @param bool $update_node + */ + function prepend_child(Frame $child, $update_node = true) + { + while ($child instanceof AbstractFrameDecorator) { + $child = $child->_frame; + } + + $this->_frame->prepend_child($child, $update_node); + } + + /** + * @param Frame $child + * @param bool $update_node + */ + function append_child(Frame $child, $update_node = true) + { + while ($child instanceof AbstractFrameDecorator) { + $child = $child->_frame; + } + + $this->_frame->append_child($child, $update_node); + } + + /** + * @param Frame $new_child + * @param Frame $ref + * @param bool $update_node + */ + function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) + { + while ($new_child instanceof AbstractFrameDecorator) { + $new_child = $new_child->_frame; + } + + if ($ref instanceof AbstractFrameDecorator) { + $ref = $ref->_frame; + } + + $this->_frame->insert_child_before($new_child, $ref, $update_node); + } + + /** + * @param Frame $new_child + * @param Frame $ref + * @param bool $update_node + */ + function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) + { + while ($new_child instanceof AbstractFrameDecorator) { + $new_child = $new_child->_frame; + } + + while ($ref instanceof AbstractFrameDecorator) { + $ref = $ref->_frame; + } + + $this->_frame->insert_child_after($new_child, $ref, $update_node); + } + + /** + * @param Frame $child + * @param bool $update_node + * + * @return Frame + */ + function remove_child(Frame $child, $update_node = true) + { + while ($child instanceof AbstractFrameDecorator) { + $child = $child->_frame; + } + + return $this->_frame->remove_child($child, $update_node); + } + + /** + * @return AbstractFrameDecorator + */ + function get_parent() + { + $p = $this->_frame->get_parent(); + if ($p && $deco = $p->get_decorator()) { + while ($tmp = $deco->get_decorator()) { + $deco = $tmp; + } + + return $deco; + } else { + if ($p) { + return $p; + } + } + + return null; + } + + /** + * @return AbstractFrameDecorator + */ + function get_first_child() + { + $c = $this->_frame->get_first_child(); + if ($c && $deco = $c->get_decorator()) { + while ($tmp = $deco->get_decorator()) { + $deco = $tmp; + } + + return $deco; + } else { + if ($c) { + return $c; + } + } + + return null; + } + + /** + * @return AbstractFrameDecorator + */ + function get_last_child() + { + $c = $this->_frame->get_last_child(); + if ($c && $deco = $c->get_decorator()) { + while ($tmp = $deco->get_decorator()) { + $deco = $tmp; + } + + return $deco; + } else { + if ($c) { + return $c; + } + } + + return null; + } + + /** + * @return AbstractFrameDecorator + */ + function get_prev_sibling() + { + $s = $this->_frame->get_prev_sibling(); + if ($s && $deco = $s->get_decorator()) { + while ($tmp = $deco->get_decorator()) { + $deco = $tmp; + } + + return $deco; + } else { + if ($s) { + return $s; + } + } + + return null; + } + + /** + * @return AbstractFrameDecorator + */ + function get_next_sibling() + { + $s = $this->_frame->get_next_sibling(); + if ($s && $deco = $s->get_decorator()) { + while ($tmp = $deco->get_decorator()) { + $deco = $tmp; + } + + return $deco; + } else { + if ($s) { + return $s; + } + } + + return null; + } + + /** + * @return FrameTreeList + */ + function get_subtree() + { + return new FrameTreeList($this); + } + + function set_positioner(AbstractPositioner $posn) + { + $this->_positioner = $posn; + if ($this->_frame instanceof AbstractFrameDecorator) { + $this->_frame->set_positioner($posn); + } + } + + function set_reflower(AbstractFrameReflower $reflower) + { + $this->_reflower = $reflower; + if ($this->_frame instanceof AbstractFrameDecorator) { + $this->_frame->set_reflower($reflower); + } + } + + /** + * @return \Dompdf\FrameReflower\AbstractFrameReflower + */ + function get_reflower() + { + return $this->_reflower; + } + + /** + * @param Frame $root + */ + function set_root(Frame $root) + { + $this->_root = $root; + + if ($this->_frame instanceof AbstractFrameDecorator) { + $this->_frame->set_root($root); + } + } + + /** + * @return Page + */ + function get_root() + { + return $this->_root; + } + + /** + * @return Block + */ + function find_block_parent() + { + // Find our nearest block level parent + $p = $this->get_parent(); + + while ($p) { + if ($p->is_block()) { + break; + } + + $p = $p->get_parent(); + } + + return $this->_block_parent = $p; + } + + /** + * @return AbstractFrameDecorator + */ + function find_positionned_parent() + { + // Find our nearest relative positionned parent + $p = $this->get_parent(); + while ($p) { + if ($p->is_positionned()) { + break; + } + + $p = $p->get_parent(); + } + + if (!$p) { + $p = $this->_root->get_first_child(); // + } + + return $this->_positionned_parent = $p; + } + + /** + * split this frame at $child. + * The current frame is cloned and $child and all children following + * $child are added to the clone. The clone is then passed to the + * current frame's parent->split() method. + * + * @param Frame $child + * @param boolean $force_pagebreak + * + * @throws Exception + * @return void + */ + function split(Frame $child = null, $force_pagebreak = false) + { + // decrement any counters that were incremented on the current node, unless that node is the body + $style = $this->_frame->get_style(); + if ($this->_frame->get_node( + )->nodeName !== "body" && $style->counter_increment && ($decrement = $style->counter_increment) !== "none" + ) { + $this->decrement_counters($decrement); + } + + if (is_null($child)) { + // check for counter increment on :before content (always a child of the selected element @link AbstractFrameReflower::_set_content) + // this can push the current node to the next page before counter rules have bubbled up (but only if + // it's been rendered, thus the position check) + if (!$this->is_text_node() && $this->get_node()->hasAttribute("dompdf_before_frame_id")) { + foreach ($this->_frame->get_children() as $child) { + if ($this->get_node()->getAttribute("dompdf_before_frame_id") == $child->get_id( + ) && $child->get_position('x') !== null + ) { + $style = $child->get_style(); + if ($style->counter_increment && ($decrement = $style->counter_increment) !== "none") { + $this->decrement_counters($decrement); + } + } + } + } + $this->get_parent()->split($this, $force_pagebreak); + + return; + } + + if ($child->get_parent() !== $this) { + throw new Exception("Unable to split: frame is not a child of this one."); + } + + $node = $this->_frame->get_node(); + + if ($node instanceof DOMElement && $node->hasAttribute("id")) { + $node->setAttribute("data-dompdf-original-id", $node->getAttribute("id")); + $node->removeAttribute("id"); + } + + $split = $this->copy($node->cloneNode()); + $split->reset(); + $split->get_original_style()->text_indent = 0; + $split->_splitted = true; + + // The body's properties must be kept + if ($node->nodeName !== "body") { + // Style reset on the first and second parts + $style = $this->_frame->get_style(); + $style->margin_bottom = 0; + $style->padding_bottom = 0; + $style->border_bottom = 0; + + // second + $orig_style = $split->get_original_style(); + $orig_style->text_indent = 0; + $orig_style->margin_top = 0; + $orig_style->padding_top = 0; + $orig_style->border_top = 0; + $orig_style->page_break_before = "auto"; + } + + $this->get_parent()->insert_child_after($split, $this); + + // Add $frame and all following siblings to the new split node + $iter = $child; + while ($iter) { + $frame = $iter; + $iter = $iter->get_next_sibling(); + $frame->reset(); + $split->append_child($frame); + } + + $this->get_parent()->split($split, $force_pagebreak); + + // If this node resets a counter save the current value to use when rendering on the next page + if ($style->counter_reset && ($reset = $style->counter_reset) !== "none") { + $vars = preg_split('/\s+/', trim($reset), 2); + $split->_counters['__' . $vars[0]] = $this->lookup_counter_frame($vars[0])->_counters[$vars[0]]; + } + } + + function reset_counter($id = self::DEFAULT_COUNTER, $value = 0) + { + $this->get_parent()->_counters[$id] = intval($value); + } + + function decrement_counters($counters) + { + foreach ($counters as $id => $increment) { + $this->increment_counter($id, intval($increment) * -1); + } + } + + function increment_counters($counters) + { + foreach ($counters as $id => $increment) { + $this->increment_counter($id, intval($increment)); + } + } + + function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1) + { + $counter_frame = $this->lookup_counter_frame($id); + + if ($counter_frame) { + if (!isset($counter_frame->_counters[$id])) { + $counter_frame->_counters[$id] = 0; + } + + $counter_frame->_counters[$id] += $increment; + } + } + + function lookup_counter_frame($id = self::DEFAULT_COUNTER) + { + $f = $this->get_parent(); + + while ($f) { + if (isset($f->_counters[$id])) { + return $f; + } + $fp = $f->get_parent(); + + if (!$fp) { + return $f; + } + + $f = $fp; + } + } + + // TODO: What version is the best : this one or the one in ListBullet ? + function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal") + { + $type = mb_strtolower($type); + + if (!isset($this->_counters[$id])) { + $this->_counters[$id] = 0; + } + + $value = $this->_counters[$id]; + + switch ($type) { + default: + case "decimal": + return $value; + + case "decimal-leading-zero": + return str_pad($value, 2, "0", STR_PAD_LEFT); + + case "lower-roman": + return Helpers::dec2roman($value); + + case "upper-roman": + return mb_strtoupper(Helpers::dec2roman($value)); + + case "lower-latin": + case "lower-alpha": + return chr(($value % 26) + ord('a') - 1); + + case "upper-latin": + case "upper-alpha": + return chr(($value % 26) + ord('A') - 1); + + case "lower-greek": + return Helpers::unichr($value + 944); + + case "upper-greek": + return Helpers::unichr($value + 912); + } + } + + final function position() + { + $this->_positioner->position(); + } + + final function move($offset_x, $offset_y, $ignore_self = false) + { + $this->_positioner->move($offset_x, $offset_y, $ignore_self); + } + + final function reflow(Block $block = null) + { + // Uncomment this to see the frames before they're laid out, instead of + // during rendering. + //echo $this->_frame; flush(); + $this->_reflower->reflow($block); + } + + final function get_min_max_width() + { + return $this->_reflower->get_min_max_width(); + } +} diff --git a/library/vendor/dompdf/src/FrameDecorator/Block.php b/library/vendor/dompdf/src/FrameDecorator/Block.php new file mode 100644 index 000000000..c51fe2a80 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/Block.php @@ -0,0 +1,252 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\LineBox; + +/** + * Decorates frames for block layout + * + * @access private + * @package dompdf + */ +class Block extends AbstractFrameDecorator +{ + /** + * Current line index + * + * @var int + */ + protected $_cl; + + /** + * The block's line boxes + * + * @var LineBox[] + */ + protected $_line_boxes; + + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + + $this->_line_boxes = array(new LineBox($this)); + $this->_cl = 0; + } + + function reset() + { + parent::reset(); + + $this->_line_boxes = array(new LineBox($this)); + $this->_cl = 0; + } + + /** + * @return LineBox + */ + function get_current_line_box() + { + return $this->_line_boxes[$this->_cl]; + } + + /** + * @return integer + */ + function get_current_line_number() + { + return $this->_cl; + } + + /** + * @return LineBox[] + */ + function get_line_boxes() + { + return $this->_line_boxes; + } + + /** + * @param integer $i + */ + function clear_line($i) + { + if (isset($this->_line_boxes[$i])) { + unset($this->_line_boxes[$i]); + } + } + + /** + * @param Frame $frame + */ + function add_frame_to_line(Frame $frame) + { + if (!$frame->is_in_flow()) { + return; + } + + $style = $frame->get_style(); + + $frame->set_containing_line($this->_line_boxes[$this->_cl]); + + /* + // Adds a new line after a block, only if certain conditions are met + if ((($frame instanceof Inline && $frame->get_node()->nodeName !== "br") || + $frame instanceof Text && trim($frame->get_text())) && + ($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" && + $this->_line_boxes[$this->_cl]->w > 0 )) { + + $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame ); + $this->add_line(); + + // Add each child of the inline frame to the line individually + foreach ($frame->get_children() as $child) + $this->add_frame_to_line( $child ); + } + else*/ + + // Handle inline frames (which are effectively wrappers) + if ($frame instanceof Inline) { + + // Handle line breaks + if ($frame->get_node()->nodeName === "br") { + $this->maximize_line_height($style->length_in_pt($style->line_height), $frame); + $this->add_line(true); + } + + return; + } + + // Trim leading text if this is an empty line. Kinda a hack to put it here, + // but what can you do... + if ($this->get_current_line_box()->w == 0 && + $frame->is_text_node() && + !$frame->is_pre() + ) { + + $frame->set_text(ltrim($frame->get_text())); + $frame->recalculate_width(); + } + + $w = $frame->get_margin_width(); + + if ($w == 0) { + return; + } + + // Debugging code: + /* + Helpers::pre_r("\n

Adding frame to line:

"); + + // Helpers::pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")"); + // Helpers::pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")"); + if ( $frame->is_text_node() ) + Helpers::pre_r('"'.$frame->get_node()->nodeValue.'"'); + + Helpers::pre_r("Line width: " . $this->_line_boxes[$this->_cl]->w); + Helpers::pre_r("Frame: " . get_class($frame)); + Helpers::pre_r("Frame width: " . $w); + Helpers::pre_r("Frame height: " . $frame->get_margin_height()); + Helpers::pre_r("Containing block width: " . $this->get_containing_block("w")); + */ + // End debugging + + $line = $this->_line_boxes[$this->_cl]; + if ($line->left + $line->w + $line->right + $w > $this->get_containing_block("w")) { + $this->add_line(); + } + + $frame->position(); + + $current_line = $this->_line_boxes[$this->_cl]; + $current_line->add_frame($frame); + + if ($frame->is_text_node()) { + $current_line->wc += count(preg_split("/\s+/", trim($frame->get_text()))); + } + + $this->increase_line_width($w); + + $this->maximize_line_height($frame->get_margin_height(), $frame); + } + + function remove_frames_from_line(Frame $frame) + { + // Search backwards through the lines for $frame + $i = $this->_cl; + $j = null; + + while ($i >= 0) { + if (($j = in_array($frame, $this->_line_boxes[$i]->get_frames(), true)) !== false) { + break; + } + + $i--; + } + + if ($j === false) { + return; + } + + // Remove $frame and all frames that follow + while ($j < count($this->_line_boxes[$i]->get_frames())) { + $frames = $this->_line_boxes[$i]->get_frames(); + $f = $frames[$j]; + $frames[$j] = null; + unset($frames[$j]); + $j++; + $this->_line_boxes[$i]->w -= $f->get_margin_width(); + } + + // Recalculate the height of the line + $h = 0; + foreach ($this->_line_boxes[$i]->get_frames() as $f) { + $h = max($h, $f->get_margin_height()); + } + + $this->_line_boxes[$i]->h = $h; + + // Remove all lines that follow + while ($this->_cl > $i) { + $this->_line_boxes[$this->_cl] = null; + unset($this->_line_boxes[$this->_cl]); + $this->_cl--; + } + } + + function increase_line_width($w) + { + $this->_line_boxes[$this->_cl]->w += $w; + } + + function maximize_line_height($val, Frame $frame) + { + if ($val > $this->_line_boxes[$this->_cl]->h) { + $this->_line_boxes[$this->_cl]->tallest_frame = $frame; + $this->_line_boxes[$this->_cl]->h = $val; + } + } + + function add_line($br = false) + { + +// if ( $this->_line_boxes[$this->_cl]["h"] == 0 ) //count($this->_line_boxes[$i]["frames"]) == 0 || +// return; + + $this->_line_boxes[$this->_cl]->br = $br; + $y = $this->_line_boxes[$this->_cl]->y + $this->_line_boxes[$this->_cl]->h; + + $new_line = new LineBox($this, $y); + + $this->_line_boxes[++$this->_cl] = $new_line; + } + + //........................................................................ +} diff --git a/library/vendor/dompdf/src/FrameDecorator/Image.php b/library/vendor/dompdf/src/FrameDecorator/Image.php new file mode 100644 index 000000000..22a308a4a --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/Image.php @@ -0,0 +1,90 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\FontMetrics; +use Dompdf\Image\Cache; + +/** + * Decorates frames for image layout and rendering + * + * @package dompdf + */ +class Image extends AbstractFrameDecorator +{ + + /** + * The path to the image file (note that remote images are + * downloaded locally to Options:tempDir). + * + * @var string + */ + protected $_image_url; + + /** + * The image's file error message + * + * @var string + */ + protected $_image_msg; + + /** + * Class constructor + * + * @param Frame $frame the frame to decorate + * @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls) + */ + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + $url = $frame->get_node()->getAttribute("src"); + + $debug_png = $dompdf->get_option("debug_png"); + if ($debug_png) print '[__construct ' . $url . ']'; + + list($this->_image_url, /*$type*/, $this->_image_msg) = Cache::resolve_url( + $url, + $dompdf->get_protocol(), + $dompdf->get_host(), + $dompdf->get_base_path(), + $dompdf + ); + + if (Cache::is_broken($this->_image_url) && + $alt = $frame->get_node()->getAttribute("alt") + ) { + $style = $frame->get_style(); + $style->width = (4 / 3) * $dompdf->getFontMetrics()->getTextWidth($alt, $style->font_family, $style->font_size, $style->word_spacing); + $style->height = $dompdf->getFontMetrics()->getFontHeight($style->font_family, $style->font_size); + } + } + + /** + * Return the image's url + * + * @return string The url of this image + */ + function get_image_url() + { + return $this->_image_url; + } + + /** + * Return the image's error message + * + * @return string The image's error message + */ + function get_image_msg() + { + return $this->_image_msg; + } + +} diff --git a/library/vendor/dompdf/src/FrameDecorator/Inline.php b/library/vendor/dompdf/src/FrameDecorator/Inline.php new file mode 100644 index 000000000..05920f493 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/Inline.php @@ -0,0 +1,92 @@ + + * @author Helmut Tischer + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\Exception; + +/** + * Decorates frames for inline layout + * + * @access private + * @package dompdf + */ +class Inline extends AbstractFrameDecorator +{ + + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + } + + function split(Frame $frame = null, $force_pagebreak = false) + { + if (is_null($frame)) { + $this->get_parent()->split($this, $force_pagebreak); + return; + } + + if ($frame->get_parent() !== $this) { + throw new Exception("Unable to split: frame is not a child of this one."); + } + + $node = $this->_frame->get_node(); + + if ($node instanceof DOMElement && $node->hasAttribute("id")) { + $node->setAttribute("data-dompdf-original-id", $node->getAttribute("id")); + $node->removeAttribute("id"); + } + + $split = $this->copy($node->cloneNode()); + $this->get_parent()->insert_child_after($split, $this); + + // Unset the current node's right style properties + $style = $this->_frame->get_style(); + $style->margin_right = 0; + $style->padding_right = 0; + $style->border_right_width = 0; + + // Unset the split node's left style properties since we don't want them + // to propagate + $style = $split->get_style(); + $style->margin_left = 0; + $style->padding_left = 0; + $style->border_left_width = 0; + + //On continuation of inline element on next line, + //don't repeat non-vertically repeatble background images + //See e.g. in testcase image_variants, long desriptions + if (($url = $style->background_image) && $url !== "none" + && ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-y" + ) { + $style->background_image = "none"; + } + + // Add $frame and all following siblings to the new split node + $iter = $frame; + while ($iter) { + $frame = $iter; + $iter = $iter->get_next_sibling(); + $frame->reset(); + $split->append_child($frame); + } + + $page_breaks = array("always", "left", "right"); + $frame_style = $frame->get_style(); + if ($force_pagebreak || + in_array($frame_style->page_break_before, $page_breaks) || + in_array($frame_style->page_break_after, $page_breaks) + ) { + + $this->get_parent()->split($split, true); + } + } + +} diff --git a/library/vendor/dompdf/src/FrameDecorator/ListBullet.php b/library/vendor/dompdf/src/FrameDecorator/ListBullet.php new file mode 100644 index 000000000..2f9154625 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/ListBullet.php @@ -0,0 +1,75 @@ + + * @author Helmut Tischer + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; + +/** + * Decorates frames for list bullet rendering + * + * @package dompdf + */ +class ListBullet extends AbstractFrameDecorator +{ + + const BULLET_PADDING = 1; // Distance from bullet to text in pt + // As fraction of font size (including descent). See also DECO_THICKNESS. + const BULLET_THICKNESS = 0.04; // Thickness of bullet outline. Screen: 0.08, print: better less, e.g. 0.04 + const BULLET_DESCENT = 0.3; //descent of font below baseline. Todo: Guessed for now. + const BULLET_SIZE = 0.35; // bullet diameter. For now 0.5 of font_size without descent. + + static $BULLET_TYPES = array("disc", "circle", "square"); + + //........................................................................ + + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + } + + function get_margin_width() + { + $style = $this->_frame->get_style(); + + // Small hack to prevent extra indenting of list text on list_style_position === "inside" + // and on suppressed bullet + if ($style->list_style_position === "outside" || + $style->list_style_type === "none" + ) { + return 0; + } + + return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING; + } + + //hits only on "inset" lists items, to increase height of box + function get_margin_height() + { + $style = $this->_frame->get_style(); + + if ($style->list_style_type === "none") { + return 0; + } + + return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING; + } + + function get_width() + { + return $this->get_margin_height(); + } + + function get_height() + { + return $this->get_margin_height(); + } + + //........................................................................ +} diff --git a/library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php b/library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php new file mode 100644 index 000000000..159eaec13 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/ListBulletImage.php @@ -0,0 +1,155 @@ + + * @author Helmut Tischer + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\Helpers; + +/** + * Decorates frames for list bullets with custom images + * + * @package dompdf + */ +class ListBulletImage extends AbstractFrameDecorator +{ + + /** + * The underlying image frame + * + * @var Image + */ + protected $_img; + + /** + * The image's width in pixels + * + * @var int + */ + protected $_width; + + /** + * The image's height in pixels + * + * @var int + */ + protected $_height; + + /** + * Class constructor + * + * @param Frame $frame the bullet frame to decorate + * @param Dompdf $dompdf the document's dompdf object + */ + function __construct(Frame $frame, Dompdf $dompdf) + { + $style = $frame->get_style(); + $url = $style->list_style_image; + $frame->get_node()->setAttribute("src", $url); + $this->_img = new Image($frame, $dompdf); + parent::__construct($this->_img, $dompdf); + list($width, $height) = Helpers::dompdf_getimagesize($this->_img->get_image_url(), $dompdf->getHttpContext()); + + // Resample the bullet image to be consistent with 'auto' sized images + // See also Image::get_min_max_width + // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary. + $dpi = $this->_dompdf->get_option("dpi"); + $this->_width = ((float)rtrim($width, "px") * 72) / $dpi; + $this->_height = ((float)rtrim($height, "px") * 72) / $dpi; + + //If an image is taller as the containing block/box, the box should be extended. + //Neighbour elements are overwriting the overlapping image areas. + //Todo: Where can the box size be extended? + //Code below has no effect. + //See block_frame_reflower _calculate_restricted_height + //See generated_frame_reflower, Dompdf:render() "list-item", "-dompdf-list-bullet"S. + //Leave for now + //if ($style->min_height < $this->_height ) { + // $style->min_height = $this->_height; + //} + //$style->height = "auto"; + } + + /** + * Return the bullet's width + * + * @return int + */ + function get_width() + { + //ignore image width, use same width as on predefined bullet ListBullet + //for proper alignment of bullet image and text. Allow image to not fitting on left border. + //This controls the distance between bullet image and text + //return $this->_width; + return $this->_frame->get_style()->get_font_size() * ListBullet::BULLET_SIZE + + 2 * ListBullet::BULLET_PADDING; + } + + /** + * Return the bullet's height + * + * @return int + */ + function get_height() + { + //based on image height + return $this->_height; + } + + /** + * Override get_margin_width + * + * @return int + */ + function get_margin_width() + { + //ignore image width, use same width as on predefined bullet ListBullet + //for proper alignment of bullet image and text. Allow image to not fitting on left border. + //This controls the extra indentation of text to make room for the bullet image. + //Here use actual image size, not predefined bullet size + //return $this->_frame->get_style()->get_font_size()*ListBullet::BULLET_SIZE + + // 2 * ListBullet::BULLET_PADDING; + + // Small hack to prevent indenting of list text + // Image Might not exist, then position like on list_bullet_frame_decorator fallback to none. + if ($this->_frame->get_style()->list_style_position === "outside" || + $this->_width == 0 + ) + return 0; + //This aligns the "inside" image position with the text. + //The text starts to the right of the image. + //Between the image and the text there is an added margin of image width. + //Where this comes from is unknown. + //The corresponding ListBullet sets a smaller margin. bullet size? + return $this->_width + 2 * ListBullet::BULLET_PADDING; + } + + /** + * Override get_margin_height() + * + * @return int + */ + function get_margin_height() + { + //Hits only on "inset" lists items, to increase height of box + //based on image height + return $this->_height + 2 * ListBullet::BULLET_PADDING; + } + + /** + * Return image url + * + * @return string + */ + function get_image_url() + { + return $this->_img->get_image_url(); + } + +} diff --git a/library/vendor/dompdf/src/FrameDecorator/NullFrameDecorator.php b/library/vendor/dompdf/src/FrameDecorator/NullFrameDecorator.php new file mode 100644 index 000000000..c9f2d94c4 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/NullFrameDecorator.php @@ -0,0 +1,31 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; + +/** + * Dummy decorator + * + * @package dompdf + */ +class NullFrameDecorator extends AbstractFrameDecorator +{ + + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + $style = $this->_frame->get_style(); + $style->width = 0; + $style->height = 0; + $style->margin = 0; + $style->padding = 0; + } + +} diff --git a/library/vendor/dompdf/src/FrameDecorator/Page.php b/library/vendor/dompdf/src/FrameDecorator/Page.php new file mode 100644 index 000000000..0310f60c2 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/Page.php @@ -0,0 +1,658 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Css\Style; +use Dompdf\Dompdf; +use Dompdf\Helpers; +use Dompdf\Frame; +use Dompdf\Renderer; + +/** + * Decorates frames for page layout + * + * @access private + * @package dompdf + */ +class Page extends AbstractFrameDecorator +{ + + /** + * y value of bottom page margin + * + * @var float + */ + protected $_bottom_page_margin; + + /** + * Flag indicating page is full. + * + * @var bool + */ + protected $_page_full; + + /** + * Number of tables currently being reflowed + * + * @var int + */ + protected $_in_table; + + /** + * The pdf renderer + * + * @var Renderer + */ + protected $_renderer; + + /** + * This page's floating frames + * + * @var array + */ + protected $_floating_frames = array(); + + //........................................................................ + + /** + * Class constructor + * + * @param Frame $frame the frame to decorate + * @param Dompdf $dompdf + */ + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + $this->_page_full = false; + $this->_in_table = 0; + $this->_bottom_page_margin = null; + } + + /** + * Set the renderer used for this pdf + * + * @param Renderer $renderer the renderer to use + */ + function set_renderer($renderer) + { + $this->_renderer = $renderer; + } + + /** + * Return the renderer used for this pdf + * + * @return Renderer + */ + function get_renderer() + { + return $this->_renderer; + } + + /** + * Set the frame's containing block. Overridden to set $this->_bottom_page_margin. + * + * @param float $x + * @param float $y + * @param float $w + * @param float $h + */ + function set_containing_block($x = null, $y = null, $w = null, $h = null) + { + parent::set_containing_block($x, $y, $w, $h); + //$w = $this->get_containing_block("w"); + if (isset($h)) { + $this->_bottom_page_margin = $h; + } // - $this->_frame->get_style()->length_in_pt($this->_frame->get_style()->margin_bottom, $w); + } + + /** + * Returns true if the page is full and is no longer accepting frames. + * + * @return bool + */ + function is_full() + { + return $this->_page_full; + } + + /** + * Start a new page by resetting the full flag. + */ + function next_page() + { + $this->_floating_frames = array(); + $this->_renderer->new_page(); + $this->_page_full = false; + } + + /** + * Indicate to the page that a table is currently being reflowed. + */ + function table_reflow_start() + { + $this->_in_table++; + } + + /** + * Indicate to the page that table reflow is finished. + */ + function table_reflow_end() + { + $this->_in_table--; + } + + /** + * Return whether we are currently in a nested table or not + * + * @return bool + */ + function in_nested_table() + { + return $this->_in_table > 1; + } + + /** + * Check if a forced page break is required before $frame. This uses the + * frame's page_break_before property as well as the preceeding frame's + * page_break_after property. + * + * @link http://www.w3.org/TR/CSS21/page.html#forced + * + * @param Frame $frame the frame to check + * + * @return bool true if a page break occured + */ + function check_forced_page_break(Frame $frame) + { + + // Skip check if page is already split + if ($this->_page_full) { + return null; + } + + $block_types = array("block", "list-item", "table", "inline"); + $page_breaks = array("always", "left", "right"); + + $style = $frame->get_style(); + + if (!in_array($style->display, $block_types)) { + return false; + } + + // Find the previous block-level sibling + $prev = $frame->get_prev_sibling(); + + while ($prev && !in_array($prev->get_style()->display, $block_types)) { + $prev = $prev->get_prev_sibling(); + } + + + if (in_array($style->page_break_before, $page_breaks)) { + + // Prevent cascading splits + $frame->split(null, true); + // We have to grab the style again here because split() resets + // $frame->style to the frame's orignal style. + $frame->get_style()->page_break_before = "auto"; + $this->_page_full = true; + + return true; + } + + if ($prev && in_array($prev->get_style()->page_break_after, $page_breaks)) { + // Prevent cascading splits + $frame->split(null, true); + $prev->get_style()->page_break_after = "auto"; + $this->_page_full = true; + + return true; + } + + if ($prev && $prev->get_last_child() && $frame->get_node()->nodeName != "body") { + $prev_last_child = $prev->get_last_child(); + if (in_array($prev_last_child->get_style()->page_break_after, $page_breaks)) { + $frame->split(null, true); + $prev_last_child->get_style()->page_break_after = "auto"; + $this->_page_full = true; + + return true; + } + } + + + return false; + } + + /** + * Determine if a page break is allowed before $frame + * http://www.w3.org/TR/CSS21/page.html#allowed-page-breaks + * + * In the normal flow, page breaks can occur at the following places: + * + * 1. In the vertical margin between block boxes. When a page + * break occurs here, the used values of the relevant + * 'margin-top' and 'margin-bottom' properties are set to '0'. + * 2. Between line boxes inside a block box. + * + * These breaks are subject to the following rules: + * + * * Rule A: Breaking at (1) is allowed only if the + * 'page-break-after' and 'page-break-before' properties of + * all the elements generating boxes that meet at this margin + * allow it, which is when at least one of them has the value + * 'always', 'left', or 'right', or when all of them are + * 'auto'. + * + * * Rule B: However, if all of them are 'auto' and the + * nearest common ancestor of all the elements has a + * 'page-break-inside' value of 'avoid', then breaking here is + * not allowed. + * + * * Rule C: Breaking at (2) is allowed only if the number of + * line boxes between the break and the start of the enclosing + * block box is the value of 'orphans' or more, and the number + * of line boxes between the break and the end of the box is + * the value of 'widows' or more. + * + * * Rule D: In addition, breaking at (2) is allowed only if + * the 'page-break-inside' property is 'auto'. + * + * If the above doesn't provide enough break points to keep + * content from overflowing the page boxes, then rules B and D are + * dropped in order to find additional breakpoints. + * + * If that still does not lead to sufficient break points, rules A + * and C are dropped as well, to find still more break points. + * + * We will also allow breaks between table rows. However, when + * splitting a table, the table headers should carry over to the + * next page (but they don't yet). + * + * @param Frame $frame the frame to check + * + * @return bool true if a break is allowed, false otherwise + */ + protected function _page_break_allowed(Frame $frame) + { + + $block_types = array("block", "list-item", "table", "-dompdf-image"); + Helpers::dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName . ")"); + $display = $frame->get_style()->display; + + // Block Frames (1): + if (in_array($display, $block_types)) { + + // Avoid breaks within table-cells + if ($this->_in_table) { + Helpers::dompdf_debug("page-break", "In table: " . $this->_in_table); + + return false; + } + + // Rules A & B + + if ($frame->get_style()->page_break_before === "avoid") { + Helpers::dompdf_debug("page-break", "before: avoid"); + + return false; + } + + // Find the preceeding block-level sibling + $prev = $frame->get_prev_sibling(); + while ($prev && !in_array($prev->get_style()->display, $block_types)) { + $prev = $prev->get_prev_sibling(); + } + + // Does the previous element allow a page break after? + if ($prev && $prev->get_style()->page_break_after === "avoid") { + Helpers::dompdf_debug("page-break", "after: avoid"); + + return false; + } + + // If both $prev & $frame have the same parent, check the parent's + // page_break_inside property. + $parent = $frame->get_parent(); + if ($prev && $parent && $parent->get_style()->page_break_inside === "avoid") { + Helpers::dompdf_debug("page-break", "parent inside: avoid"); + + return false; + } + + // To prevent cascading page breaks when a top-level element has + // page-break-inside: avoid, ensure that at least one frame is + // on the page before splitting. + if ($parent->get_node()->nodeName === "body" && !$prev) { + // We are the body's first child + Helpers::dompdf_debug("page-break", "Body's first child."); + + return false; + } + + // If the frame is the first block-level frame, use the value from + // $frame's parent instead. + if (!$prev && $parent) { + return $this->_page_break_allowed($parent); + } + + Helpers::dompdf_debug("page-break", "block: break allowed"); + + return true; + + } // Inline frames (2): + else { + if (in_array($display, Style::$INLINE_TYPES)) { + + // Avoid breaks within table-cells + if ($this->_in_table) { + Helpers::dompdf_debug("page-break", "In table: " . $this->_in_table); + + return false; + } + + // Rule C + $block_parent = $frame->find_block_parent(); + if (count($block_parent->get_line_boxes()) < $frame->get_style()->orphans) { + Helpers::dompdf_debug("page-break", "orphans"); + + return false; + } + + // FIXME: Checking widows is tricky without having laid out the + // remaining line boxes. Just ignore it for now... + + // Rule D + $p = $block_parent; + while ($p) { + if ($p->get_style()->page_break_inside === "avoid") { + Helpers::dompdf_debug("page-break", "parent->inside: avoid"); + + return false; + } + $p = $p->find_block_parent(); + } + + // To prevent cascading page breaks when a top-level element has + // page-break-inside: avoid, ensure that at least one frame with + // some content is on the page before splitting. + $prev = $frame->get_prev_sibling(); + while ($prev && ($prev->is_text_node() && trim($prev->get_node()->nodeValue) == "")) { + $prev = $prev->get_prev_sibling(); + } + + if ($block_parent->get_node()->nodeName === "body" && !$prev) { + // We are the body's first child + Helpers::dompdf_debug("page-break", "Body's first child."); + + return false; + } + + // Skip breaks on empty text nodes + if ($frame->is_text_node() && + $frame->get_node()->nodeValue == "" + ) { + return false; + } + + Helpers::dompdf_debug("page-break", "inline: break allowed"); + + return true; + + // Table-rows + } else { + if ($display === "table-row") { + + // Simply check if the parent table's page_break_inside property is + // not 'avoid' + $p = Table::find_parent_table($frame); + + while ($p) { + if ($p->get_style()->page_break_inside === "avoid") { + Helpers::dompdf_debug("page-break", "parent->inside: avoid"); + + return false; + } + $p = $p->find_block_parent(); + } + + // Avoid breaking after the first row of a table + if ($p && $p->get_first_child() === $frame) { + Helpers::dompdf_debug("page-break", "table: first-row"); + + return false; + } + + // If this is a nested table, prevent the page from breaking + if ($this->_in_table > 1) { + Helpers::dompdf_debug("page-break", "table: nested table"); + + return false; + } + + Helpers::dompdf_debug("page-break", "table-row/row-groups: break allowed"); + + return true; + + } else { + if (in_array($display, Table::$ROW_GROUPS)) { + + // Disallow breaks at row-groups: only split at row boundaries + return false; + + } else { + + Helpers::dompdf_debug("page-break", "? " . $frame->get_style()->display . ""); + + return false; + } + } + } + } + + } + + /** + * Check if $frame will fit on the page. If the frame does not fit, + * the frame tree is modified so that a page break occurs in the + * correct location. + * + * @param Frame $frame the frame to check + * + * @return Frame the frame following the page break + */ + function check_page_break(Frame $frame) + { + // Do not split if we have already or if the frame was already + // pushed to the next page (prevents infinite loops) + if ($this->_page_full || $frame->_already_pushed) { + return false; + } + + // If the frame is absolute of fixed it shouldn't break + $p = $frame; + do { + if ($p->is_absolute()) + return false; + } while ($p = $p->get_parent()); + + $margin_height = $frame->get_margin_height(); + + // FIXME If the row is taller than the page and + // if it the first of the page, we don't break + if ($frame->get_style()->display === "table-row" && + !$frame->get_prev_sibling() && + $margin_height > $this->get_margin_height() + ) + return false; + + // Determine the frame's maximum y value + $max_y = $frame->get_position("y") + $margin_height; + + // If a split is to occur here, then the bottom margins & paddings of all + // parents of $frame must fit on the page as well: + $p = $frame->get_parent(); + while ($p) { + $style = $p->get_style(); + $max_y += $style->length_in_pt( + array( + $style->margin_bottom, + $style->padding_bottom, + $style->border_bottom_width + ) + ); + $p = $p->get_parent(); + } + + + // Check if $frame flows off the page + if ($max_y <= $this->_bottom_page_margin) + // no: do nothing + return false; + + Helpers::dompdf_debug("page-break", "check_page_break"); + Helpers::dompdf_debug("page-break", "in_table: " . $this->_in_table); + + // yes: determine page break location + $iter = $frame; + $flg = false; + + $in_table = $this->_in_table; + + Helpers::dompdf_debug("page-break", "Starting search"); + while ($iter) { + // echo "\nbacktrack: " .$iter->get_node()->nodeName ." ".spl_object_hash($iter->get_node()). ""; + if ($iter === $this) { + Helpers::dompdf_debug("page-break", "reached root."); + // We've reached the root in our search. Just split at $frame. + break; + } + + if ($this->_page_break_allowed($iter)) { + Helpers::dompdf_debug("page-break", "break allowed, splitting."); + $iter->split(null, true); + $this->_page_full = true; + $this->_in_table = $in_table; + $frame->_already_pushed = true; + + return true; + } + + if (!$flg && $next = $iter->get_last_child()) { + Helpers::dompdf_debug("page-break", "following last child."); + + if ($next->is_table()) + $this->_in_table++; + + $iter = $next; + continue; + } + + if ($next = $iter->get_prev_sibling()) { + Helpers::dompdf_debug("page-break", "following prev sibling."); + + if ($next->is_table() && !$iter->is_table()) + $this->_in_table++; + + else if (!$next->is_table() && $iter->is_table()) + $this->_in_table--; + + $iter = $next; + $flg = false; + continue; + } + + if ($next = $iter->get_parent()) { + Helpers::dompdf_debug("page-break", "following parent."); + + if ($iter->is_table()) + $this->_in_table--; + + $iter = $next; + $flg = true; + continue; + } + + break; + } + + $this->_in_table = $in_table; + + // No valid page break found. Just break at $frame. + Helpers::dompdf_debug("page-break", "no valid break found, just splitting."); + + // If we are in a table, backtrack to the nearest top-level table row + if ($this->_in_table) { + $iter = $frame; + while ($iter && $iter->get_style()->display !== "table-row" && $iter->get_style()->display !== 'table-row-group') + $iter = $iter->get_parent(); + + $iter->split(null, true); + } else { + $frame->split(null, true); + } + + $this->_page_full = true; + $frame->_already_pushed = true; + + return true; + } + + //........................................................................ + + function split(Frame $frame = null, $force_pagebreak = false) + { + // Do nothing + } + + /** + * Add a floating frame + * + * @param Frame $frame + * + * @return void + */ + function add_floating_frame(Frame $frame) + { + array_unshift($this->_floating_frames, $frame); + } + + /** + * @return Frame[] + */ + function get_floating_frames() + { + return $this->_floating_frames; + } + + public function remove_floating_frame($key) + { + unset($this->_floating_frames[$key]); + } + + public function get_lowest_float_offset(Frame $child) + { + $style = $child->get_style(); + $side = $style->clear; + $float = $style->float; + + $y = 0; + + foreach ($this->_floating_frames as $key => $frame) { + if ($side === "both" || $frame->get_style()->float === $side) { + $y = max($y, $frame->get_position("y") + $frame->get_margin_height()); + + if ($float !== "none") { + $this->remove_floating_frame($key); + } + } + } + + return $y; + } + +} diff --git a/library/vendor/dompdf/src/FrameDecorator/Table.php b/library/vendor/dompdf/src/FrameDecorator/Table.php new file mode 100644 index 000000000..bdbead59e --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/Table.php @@ -0,0 +1,370 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Cellmap; +use DOMNode; +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\Frame\Factory; + +/** + * Decorates Frames for table layout + * + * @package dompdf + */ +class Table extends AbstractFrameDecorator +{ + public static $VALID_CHILDREN = array( + "table-row-group", + "table-row", + "table-header-group", + "table-footer-group", + "table-column", + "table-column-group", + "table-caption", + "table-cell" + ); + + public static $ROW_GROUPS = array( + 'table-row-group', + 'table-header-group', + 'table-footer-group' + ); + + /** + * The Cellmap object for this table. The cellmap maps table cells + * to rows and columns, and aids in calculating column widths. + * + * @var \Dompdf\Cellmap + */ + protected $_cellmap; + + /** + * The minimum width of the table, in pt + * + * @var float + */ + protected $_min_width; + + /** + * The maximum width of the table, in pt + * + * @var float + */ + protected $_max_width; + + /** + * Table header rows. Each table header is duplicated when a table + * spans pages. + * + * @var array + */ + protected $_headers; + + /** + * Table footer rows. Each table footer is duplicated when a table + * spans pages. + * + * @var array + */ + protected $_footers; + + /** + * Class constructor + * + * @param Frame $frame the frame to decorate + * @param Dompdf $dompdf + */ + public function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + $this->_cellmap = new Cellmap($this); + + if ($frame->get_style()->table_layout === "fixed") { + $this->_cellmap->set_layout_fixed(true); + } + + $this->_min_width = null; + $this->_max_width = null; + $this->_headers = array(); + $this->_footers = array(); + } + + + public function reset() + { + parent::reset(); + $this->_cellmap->reset(); + $this->_min_width = null; + $this->_max_width = null; + $this->_headers = array(); + $this->_footers = array(); + $this->_reflower->reset(); + } + + //........................................................................ + + /** + * split the table at $row. $row and all subsequent rows will be + * added to the clone. This method is overidden in order to remove + * frames from the cellmap properly. + * + * @param Frame $child + * @param bool $force_pagebreak + * + * @return void + */ + public function split(Frame $child = null, $force_pagebreak = false) + { + if (is_null($child)) { + parent::split(); + + return; + } + + // If $child is a header or if it is the first non-header row, do + // not duplicate headers, simply move the table to the next page. + if (count($this->_headers) && !in_array($child, $this->_headers, true) && + !in_array($child->get_prev_sibling(), $this->_headers, true) + ) { + + $first_header = null; + + // Insert copies of the table headers before $child + foreach ($this->_headers as $header) { + + $new_header = $header->deep_copy(); + + if (is_null($first_header)) { + $first_header = $new_header; + } + + $this->insert_child_before($new_header, $child); + } + + parent::split($first_header); + + } elseif (in_array($child->get_style()->display, self::$ROW_GROUPS)) { + + // Individual rows should have already been handled + parent::split($child); + + } else { + + $iter = $child; + + while ($iter) { + $this->_cellmap->remove_row($iter); + $iter = $iter->get_next_sibling(); + } + + parent::split($child); + } + } + + /** + * Return a copy of this frame with $node as its node + * + * @param DOMNode $node + * + * @return Frame + */ + public function copy(DOMNode $node) + { + $deco = parent::copy($node); + + // In order to keep columns' widths through pages + $deco->_cellmap->set_columns($this->_cellmap->get_columns()); + $deco->_cellmap->lock_columns(); + + return $deco; + } + + /** + * Static function to locate the parent table of a frame + * + * @param Frame $frame + * + * @return Table the table that is an ancestor of $frame + */ + public static function find_parent_table(Frame $frame) + { + + while ($frame = $frame->get_parent()) + if ($frame->is_table()) + break; + + return $frame; + } + + /** + * Return this table's Cellmap + * + * @return \Dompdf\Cellmap + */ + public function get_cellmap() + { + return $this->_cellmap; + } + + /** + * Return the minimum width of this table + * + * @return float + */ + public function get_min_width() + { + return $this->_min_width; + } + + /** + * Return the maximum width of this table + * + * @return float + */ + public function get_max_width() + { + return $this->_max_width; + } + + /** + * Set the minimum width of the table + * + * @param float $width the new minimum width + */ + public function set_min_width($width) + { + $this->_min_width = $width; + } + + /** + * Set the maximum width of the table + * + * @param float $width the new maximum width + */ + public function set_max_width($width) + { + $this->_max_width = $width; + } + + /** + * Restructure tree so that the table has the correct structure. + * Invalid children (i.e. all non-table-rows) are moved below the + * table. + */ + public function normalise() + { + // Store frames generated by invalid tags and move them outside the table + $erroneous_frames = array(); + $anon_row = false; + $iter = $this->get_first_child(); + while ($iter) { + $child = $iter; + $iter = $iter->get_next_sibling(); + + $display = $child->get_style()->display; + + if ($anon_row) { + + if ($display === "table-row") { + // Add the previous anonymous row + $this->insert_child_before($table_row, $child); + + $table_row->normalise(); + $child->normalise(); + $anon_row = false; + continue; + } + + // add the child to the anonymous row + $table_row->append_child($child); + continue; + + } else { + + if ($display === "table-row") { + $child->normalise(); + continue; + } + + if ($display === "table-cell") { + // Create an anonymous table row + $tr = $this->get_node()->ownerDocument->createElement("tr"); + + $frame = new Frame($tr); + + $css = $this->get_style()->get_stylesheet(); + $style = $css->create_style(); + $style->inherit($this->get_style()); + + // Lookup styles for tr tags. If the user wants styles to work + // better, they should make the tr explicit... I'm not going to + // try to guess what they intended. + if ($tr_style = $css->lookup("tr")) { + $style->merge($tr_style); + } + + // Okay, I have absolutely no idea why I need this clone here, but + // if it's omitted, php (as of 2004-07-28) segfaults. + $frame->set_style(clone $style); + $table_row = Factory::decorate_frame($frame, $this->_dompdf, $this->_root); + + // Add the cell to the row + $table_row->append_child($child); + + $anon_row = true; + continue; + } + + if (!in_array($display, self::$VALID_CHILDREN)) { + $erroneous_frames[] = $child; + continue; + } + + // Normalise other table parts (i.e. row groups) + foreach ($child->get_children() as $grandchild) { + if ($grandchild->get_style()->display === "table-row") { + $grandchild->normalise(); + } + } + + // Add headers and footers + if ($display === "table-header-group") { + $this->_headers[] = $child; + } elseif ($display === "table-footer-group") { + $this->_footers[] = $child; + } + } + } + + if ($anon_row && $table_row instanceof DOMNode) { + // Add the row to the table + $this->_frame->append_child($table_row); + $table_row->normalise(); + $this->_cellmap->add_row(); + } + + foreach ($erroneous_frames as $frame) { + $this->move_after($frame); + } + } + + //........................................................................ + + /** + * Moves the specified frame and it's corresponding node outside of + * the table. + * + * @param Frame $frame the frame to move + */ + public function move_after(Frame $frame) + { + $this->get_parent()->insert_child_after($frame, $this); + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/FrameDecorator/TableCell.php b/library/vendor/dompdf/src/FrameDecorator/TableCell.php new file mode 100644 index 000000000..b8259d72f --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/TableCell.php @@ -0,0 +1,117 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; + +/** + * Decorates table cells for layout + * + * @package dompdf + */ +class TableCell extends BlockFrameDecorator +{ + + protected $_resolved_borders; + protected $_content_height; + + //........................................................................ + + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + $this->_resolved_borders = array(); + $this->_content_height = 0; + } + + //........................................................................ + + function reset() + { + parent::reset(); + $this->_resolved_borders = array(); + $this->_content_height = 0; + $this->_frame->reset(); + } + + function get_content_height() + { + return $this->_content_height; + } + + function set_content_height($height) + { + $this->_content_height = $height; + } + + function set_cell_height($height) + { + $style = $this->get_style(); + $v_space = $style->length_in_pt(array($style->margin_top, + $style->padding_top, + $style->border_top_width, + $style->border_bottom_width, + $style->padding_bottom, + $style->margin_bottom), + $style->width); + + $new_height = $height - $v_space; + $style->height = $new_height; + + if ($new_height > $this->_content_height) { + $y_offset = 0; + + // Adjust our vertical alignment + switch ($style->vertical_align) { + default: + case "baseline": + // FIXME: this isn't right + + case "top": + // Don't need to do anything + return; + + case "middle": + $y_offset = ($new_height - $this->_content_height) / 2; + break; + + case "bottom": + $y_offset = $new_height - $this->_content_height; + break; + } + + if ($y_offset) { + // Move our children + foreach ($this->get_line_boxes() as $line) { + foreach ($line->get_frames() as $frame) + $frame->move(0, $y_offset); + } + } + } + + } + + function set_resolved_border($side, $border_spec) + { + $this->_resolved_borders[$side] = $border_spec; + } + + //........................................................................ + + function get_resolved_border($side) + { + return $this->_resolved_borders[$side]; + } + + function get_resolved_borders() + { + return $this->_resolved_borders; + } +} diff --git a/library/vendor/dompdf/src/FrameDecorator/TableRow.php b/library/vendor/dompdf/src/FrameDecorator/TableRow.php new file mode 100644 index 000000000..7f33082a2 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/TableRow.php @@ -0,0 +1,52 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\FrameDecorator\Table as TableFrameDecorator; + +/** + * Decorates Frames for table row layout + * + * @package dompdf + */ +class TableRow extends AbstractFrameDecorator +{ + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + } + + //........................................................................ + + /** + * Remove all non table-cell frames from this row and move them after + * the table. + */ + function normalise() + { + + // Find our table parent + $p = TableFrameDecorator::find_parent_table($this); + + $erroneous_frames = array(); + foreach ($this->get_children() as $child) { + $display = $child->get_style()->display; + + if ($display !== "table-cell") + $erroneous_frames[] = $child; + } + + // dump the extra nodes after the table. + foreach ($erroneous_frames as $frame) + $p->move_after($frame); + } + + +} diff --git a/library/vendor/dompdf/src/FrameDecorator/TableRowGroup.php b/library/vendor/dompdf/src/FrameDecorator/TableRowGroup.php new file mode 100644 index 000000000..0f2bb7967 --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/TableRowGroup.php @@ -0,0 +1,72 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; + +/** + * Table row group decorator + * + * Overrides split() method for tbody, thead & tfoot elements + * + * @package dompdf + */ +class TableRowGroup extends AbstractFrameDecorator +{ + + /** + * Class constructor + * + * @param Frame $frame Frame to decorate + * @param Dompdf $dompdf Current dompdf instance + */ + function __construct(Frame $frame, Dompdf $dompdf) + { + parent::__construct($frame, $dompdf); + } + + /** + * Override split() to remove all child rows and this element from the cellmap + * + * @param Frame $child + * @param bool $force_pagebreak + * + * @return void + */ + function split(Frame $child = null, $force_pagebreak = false) + { + + if (is_null($child)) { + parent::split(); + return; + } + + // Remove child & all subsequent rows from the cellmap + $cellmap = $this->get_parent()->get_cellmap(); + $iter = $child; + + while ($iter) { + $cellmap->remove_row($iter); + $iter = $iter->get_next_sibling(); + } + + // If we are splitting at the first child remove the + // table-row-group from the cellmap as well + if ($child === $this->get_first_child()) { + $cellmap->remove_row_group($this); + parent::split(); + return; + } + + $cellmap->update_row_group($this, $child->get_prev_sibling()); + parent::split($child); + + } +} + diff --git a/library/vendor/dompdf/src/FrameDecorator/Text.php b/library/vendor/dompdf/src/FrameDecorator/Text.php new file mode 100644 index 000000000..7a78879cb --- /dev/null +++ b/library/vendor/dompdf/src/FrameDecorator/Text.php @@ -0,0 +1,182 @@ + + * @author Brian Sweeney + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameDecorator; + +use Dompdf\Dompdf; +use Dompdf\Frame; +use Dompdf\Exception; +use DOMText; +use Dompdf\FontMetrics; + +/** + * Decorates Frame objects for text layout + * + * @access private + * @package dompdf + */ +class Text extends AbstractFrameDecorator +{ + + // protected members + protected $_text_spacing; + + function __construct(Frame $frame, Dompdf $dompdf) + { + if (!$frame->is_text_node()) { + throw new Exception("Text_Decorator can only be applied to #text nodes."); + } + + parent::__construct($frame, $dompdf); + $this->_text_spacing = null; + } + + //........................................................................ + + function reset() + { + parent::reset(); + $this->_text_spacing = null; + } + + //........................................................................ + + // Accessor methods + function get_text_spacing() + { + return $this->_text_spacing; + } + + function get_text() + { + // FIXME: this should be in a child class (and is incorrect) +// if ( $this->_frame->get_style()->content !== "normal" ) { +// $this->_frame->get_node()->data = $this->_frame->get_style()->content; +// $this->_frame->get_style()->content = "normal"; +// } + +// Helpers::pre_r("---"); +// $style = $this->_frame->get_style(); +// var_dump($text = $this->_frame->get_node()->data); +// var_dump($asc = utf8_decode($text)); +// for ($i = 0; $i < strlen($asc); $i++) +// Helpers::pre_r("$i: " . $asc[$i] . " - " . ord($asc[$i])); +// Helpers::pre_r("width: " . $this->_dompdf->getFontMetrics()->getTextWidth($text, $style->font_family, $style->font_size)); + + return $this->_frame->get_node()->data; + } + + //........................................................................ + + // Vertical margins & padding do not apply to text frames + + // http://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced: + // + // The vertical padding, border and margin of an inline, non-replaced box + // start at the top and bottom of the content area, not the + // 'line-height'. But only the 'line-height' is used to calculate the + // height of the line box. + function get_margin_height() + { + // This function is called in add_frame_to_line() and is used to + // determine the line height, so we actually want to return the + // 'line-height' property, not the actual margin box + $style = $this->get_parent()->get_style(); + $font = $style->font_family; + $size = $style->font_size; + + /* + Helpers::pre_r('-----'); + Helpers::pre_r($style->line_height); + Helpers::pre_r($style->font_size); + Helpers::pre_r($this->_dompdf->getFontMetrics()->getFontHeight($font, $size)); + Helpers::pre_r(($style->line_height / $size) * $this->_dompdf->getFontMetrics()->getFontHeight($font, $size)); + */ + + return ($style->line_height / ($size > 0 ? $size : 1)) * $this->_dompdf->getFontMetrics()->getFontHeight($font, $size); + + } + + function get_padding_box() + { + $pb = $this->_frame->get_padding_box(); + $pb[3] = $pb["h"] = $this->_frame->get_style()->height; + + return $pb; + } + //........................................................................ + + // Set method + function set_text_spacing($spacing) + { + $style = $this->_frame->get_style(); + + $this->_text_spacing = $spacing; + $char_spacing = $style->length_in_pt($style->letter_spacing); + + // Re-adjust our width to account for the change in spacing + $style->width = $this->_dompdf->getFontMetrics()->getTextWidth($this->get_text(), $style->font_family, $style->font_size, $spacing, $char_spacing); + } + + //........................................................................ + + // Recalculate the text width + function recalculate_width() + { + $style = $this->get_style(); + $text = $this->get_text(); + $size = $style->font_size; + $font = $style->font_family; + $word_spacing = $style->length_in_pt($style->word_spacing); + $char_spacing = $style->length_in_pt($style->letter_spacing); + + return $style->width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font, $size, $word_spacing, $char_spacing); + } + + //........................................................................ + + // Text manipulation methods + + // split the text in this frame at the offset specified. The remaining + // text is added a sibling frame following this one and is returned. + function split_text($offset) + { + if ($offset == 0) { + return null; + } + + $split = $this->_frame->get_node()->splitText($offset); + + $deco = $this->copy($split); + + $p = $this->get_parent(); + $p->insert_child_after($deco, $this, false); + + if ($p instanceof Inline) { + $p->split($deco); + } + + return $deco; + } + + //........................................................................ + + function delete_text($offset, $count) + { + $this->_frame->get_node()->deleteData($offset, $count); + } + + //........................................................................ + + function set_text($text) + { + $this->_frame->get_node()->data = $text; + } + +} diff --git a/library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php b/library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php new file mode 100644 index 000000000..281ae44e4 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/AbstractFrameReflower.php @@ -0,0 +1,460 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\Adapter\CPDF; +use Dompdf\Css\Style; +use Dompdf\Dompdf; +use Dompdf\Helpers; +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block; +use Dompdf\Frame\Factory; + +/** + * Base reflower class + * + * Reflower objects are responsible for determining the width and height of + * individual frames. They also create line and page breaks as necessary. + * + * @package dompdf + */ +abstract class AbstractFrameReflower +{ + + /** + * Frame for this reflower + * + * @var Frame + */ + protected $_frame; + + /** + * Cached min/max size + * + * @var array + */ + protected $_min_max_cache; + + function __construct(Frame $frame) + { + $this->_frame = $frame; + $this->_min_max_cache = null; + } + + function dispose() + { + } + + /** + * @return Dompdf + */ + function get_dompdf() + { + return $this->_frame->get_dompdf(); + } + + /** + * Collapse frames margins + * http://www.w3.org/TR/CSS2/box.html#collapsing-margins + */ + protected function _collapse_margins() + { + $frame = $this->_frame; + $cb = $frame->get_containing_block(); + $style = $frame->get_style(); + + if (!$frame->is_in_flow()) { + return; + } + + $t = $style->length_in_pt($style->margin_top, $cb["h"]); + $b = $style->length_in_pt($style->margin_bottom, $cb["h"]); + + // Handle 'auto' values + if ($t === "auto") { + $style->margin_top = "0pt"; + $t = 0; + } + + if ($b === "auto") { + $style->margin_bottom = "0pt"; + $b = 0; + } + + // Collapse vertical margins: + $n = $frame->get_next_sibling(); + if ($n && !$n->is_block()) { + while ($n = $n->get_next_sibling()) { + if ($n->is_block()) { + break; + } + + if (!$n->get_first_child()) { + $n = null; + break; + } + } + } + + if ($n) { + $n_style = $n->get_style(); + $b = max($b, $n_style->length_in_pt($n_style->margin_top, $cb["h"])); + $n_style->margin_top = "0pt"; + $style->margin_bottom = $b . "pt"; + } + + // Collapse our first child's margin + /*$f = $this->_frame->get_first_child(); + if ( $f && !$f->is_block() ) { + while ( $f = $f->get_next_sibling() ) { + if ( $f->is_block() ) { + break; + } + + if ( !$f->get_first_child() ) { + $f = null; + break; + } + } + } + + // Margin are collapsed only between block elements + if ( $f ) { + $f_style = $f->get_style(); + $t = max($t, $f_style->length_in_pt($f_style->margin_top, $cb["h"])); + $style->margin_top = $t."pt"; + $f_style->margin_bottom = "0pt"; + }*/ + } + + //........................................................................ + + abstract function reflow(Block $block = null); + + //........................................................................ + + // Required for table layout: Returns an array(0 => min, 1 => max, "min" + // => min, "max" => max) of the minimum and maximum widths of this frame. + // This provides a basic implementation. Child classes should override + // this if necessary. + function get_min_max_width() + { + if (!is_null($this->_min_max_cache)) { + return $this->_min_max_cache; + } + + $style = $this->_frame->get_style(); + + // Account for margins & padding + $dims = array($style->padding_left, + $style->padding_right, + $style->border_left_width, + $style->border_right_width, + $style->margin_left, + $style->margin_right); + + $cb_w = $this->_frame->get_containing_block("w"); + $delta = $style->length_in_pt($dims, $cb_w); + + // Handle degenerate case + if (!$this->_frame->get_first_child()) { + return $this->_min_max_cache = array( + $delta, $delta, + "min" => $delta, + "max" => $delta, + ); + } + + $low = array(); + $high = array(); + + for ($iter = $this->_frame->get_children()->getIterator(); + $iter->valid(); + $iter->next()) { + + $inline_min = 0; + $inline_max = 0; + + // Add all adjacent inline widths together to calculate max width + while ($iter->valid() && in_array($iter->current()->get_style()->display, Style::$INLINE_TYPES)) { + + $child = $iter->current(); + + $minmax = $child->get_min_max_width(); + + if (in_array($iter->current()->get_style()->white_space, array("pre", "nowrap"))) { + $inline_min += $minmax["min"]; + } else { + $low[] = $minmax["min"]; + } + + $inline_max += $minmax["max"]; + $iter->next(); + + } + + if ($inline_max > 0) $high[] = $inline_max; + if ($inline_min > 0) $low[] = $inline_min; + + if ($iter->valid()) { + list($low[], $high[]) = $iter->current()->get_min_max_width(); + continue; + } + + } + $min = count($low) ? max($low) : 0; + $max = count($high) ? max($high) : 0; + + // Use specified width if it is greater than the minimum defined by the + // content. If the width is a percentage ignore it for now. + $width = $style->width; + if ($width !== "auto" && !Helpers::is_percent($width)) { + $width = $style->length_in_pt($width, $cb_w); + if ($min < $width) $min = $width; + if ($max < $width) $max = $width; + } + + $min += $delta; + $max += $delta; + return $this->_min_max_cache = array($min, $max, "min" => $min, "max" => $max); + } + + /** + * Parses a CSS string containing quotes and escaped hex characters + * + * @param $string string The CSS string to parse + * @param $single_trim + * @return string + */ + protected function _parse_string($string, $single_trim = false) + { + if ($single_trim) { + $string = preg_replace('/^[\"\']/', "", $string); + $string = preg_replace('/[\"\']$/', "", $string); + } else { + $string = trim($string, "'\""); + } + + $string = str_replace(array("\\\n", '\\"', "\\'"), + array("", '"', "'"), $string); + + // Convert escaped hex characters into ascii characters (e.g. \A => newline) + $string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/", + function ($matches) { return \Dompdf\Helpers::unichr(hexdec($matches[1])); }, + $string); + return $string; + } + + /** + * Parses a CSS "quotes" property + * + * @return array|null An array of pairs of quotes + */ + protected function _parse_quotes() + { + + // Matches quote types + $re = '/(\'[^\']*\')|(\"[^\"]*\")/'; + + $quotes = $this->_frame->get_style()->quotes; + + // split on spaces, except within quotes + if (!preg_match_all($re, "$quotes", $matches, PREG_SET_ORDER)) { + return null; + } + + $quotes_array = array(); + foreach ($matches as $_quote) { + $quotes_array[] = $this->_parse_string($_quote[0], true); + } + + if (empty($quotes_array)) { + $quotes_array = array('"', '"'); + } + + return array_chunk($quotes_array, 2); + } + + /** + * Parses the CSS "content" property + * + * @return string|null The resulting string + */ + protected function _parse_content() + { + + // Matches generated content + $re = "/\n" . + "\s(counters?\\([^)]*\\))|\n" . + "\A(counters?\\([^)]*\\))|\n" . + "\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?_frame->get_style()->content; + + $quotes = $this->_parse_quotes(); + + // split on spaces, except within quotes + if (!preg_match_all($re, $content, $matches, PREG_SET_ORDER)) { + return null; + } + + $text = ""; + + foreach ($matches as $match) { + + if (isset($match[2]) && $match[2] !== "") { + $match[1] = $match[2]; + } + + if (isset($match[6]) && $match[6] !== "") { + $match[4] = $match[6]; + } + + if (isset($match[8]) && $match[8] !== "") { + $match[7] = $match[8]; + } + + if (isset($match[1]) && $match[1] !== "") { + + // counters?(...) + $match[1] = mb_strtolower(trim($match[1])); + + // Handle counter() references: + // http://www.w3.org/TR/CSS21/generate.html#content + + $i = mb_strpos($match[1], ")"); + if ($i === false) { + continue; + } + + preg_match('/(counters?)(^\()*?\(\s*([^\s,]+)\s*(,\s*["\']?([^"\'\)]+)["\']?\s*(,\s*([^\s)]+)\s*)?)?\)/i', $match[1], $args); + $counter_id = $args[3]; + if (strtolower($args[1]) == 'counter') { + // counter(name [,style]) + if (isset($args[5])) { + $type = trim($args[5]); + } else { + $type = null; + } + $p = $this->_frame->lookup_counter_frame($counter_id); + + $text .= $p->counter_value($counter_id, $type); + + } else if (strtolower($args[1]) == 'counters') { + // counters(name, string [,style]) + if (isset($args[5])) { + $string = $this->_parse_string($args[5]); + } else { + $string = ""; + } + + if (isset($args[7])) { + $type = trim($args[7]); + } else { + $type = null; + } + + $p = $this->_frame->lookup_counter_frame($counter_id); + $tmp = array(); + while ($p) { + // We only want to use the counter values when they actually increment the counter + if (array_key_exists($counter_id, $p->_counters)) { + array_unshift($tmp, $p->counter_value($counter_id, $type)); + } + $p = $p->lookup_counter_frame($counter_id); + + } + $text .= implode($string, $tmp); + + } else { + // countertops? + continue; + } + + } else if (isset($match[4]) && $match[4] !== "") { + // String match + $text .= $this->_parse_string($match[4]); + } else if (isset($match[7]) && $match[7] !== "") { + // Directive match + + if ($match[7] === "open-quote") { + // FIXME: do something here + $text .= $quotes[0][0]; + } else if ($match[7] === "close-quote") { + // FIXME: do something else here + $text .= $quotes[0][1]; + } else if ($match[7] === "no-open-quote") { + // FIXME: + } else if ($match[7] === "no-close-quote") { + // FIXME: + } else if (mb_strpos($match[7], "attr(") === 0) { + + $i = mb_strpos($match[7], ")"); + if ($i === false) { + continue; + } + + $attr = mb_substr($match[7], 5, $i - 5); + if ($attr == "") { + continue; + } + + $text .= $this->_frame->get_parent()->get_node()->getAttribute($attr); + } else { + continue; + } + } + } + + return $text; + } + + /** + * Sets the generated content of a generated frame + */ + protected function _set_content() + { + $frame = $this->_frame; + $style = $frame->get_style(); + + // if the element was pushed to a new page use the saved counter value, otherwise use the CSS reset value + if ($style->counter_reset && ($reset = $style->counter_reset) !== "none") { + $vars = preg_split('/\s+/', trim($reset), 2); + $frame->reset_counter($vars[0], (isset($frame->_counters['__' . $vars[0]]) ? $frame->_counters['__' . $vars[0]] : (isset($vars[1]) ? $vars[1] : 0))); + } + + if ($style->counter_increment && ($increment = $style->counter_increment) !== "none") { + $frame->increment_counters($increment); + } + + if ($style->content && !$frame->get_first_child() && $frame->get_node()->nodeName === "dompdf_generated") { + $content = $this->_parse_content(); + // add generated content to the font subset + // FIXME: This is currently too late because the font subset has already been generated. + // See notes in issue #750. + if ($frame->get_dompdf()->get_option("enable_font_subsetting") && $frame->get_dompdf()->get_canvas() instanceof CPDF) { + $frame->get_dompdf()->get_canvas()->register_string_subset($style->font_family, $content); + } + + $node = $frame->get_node()->ownerDocument->createTextNode($content); + + $new_style = $style->get_stylesheet()->create_style(); + $new_style->inherit($style); + + $new_frame = new Frame($node); + $new_frame->set_style($new_style); + + Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root()); + $frame->append_child($new_frame); + } + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/Block.php b/library/vendor/dompdf/src/FrameReflower/Block.php new file mode 100644 index 000000000..db5da39c9 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/Block.php @@ -0,0 +1,793 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FontMetrics; +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Text as TextFrameDecorator; +use Dompdf\Exception; + +/** + * Reflows block frames + * + * @package dompdf + */ +class Block extends AbstractFrameReflower +{ + // Minimum line width to justify, as fraction of available width + const MIN_JUSTIFY_WIDTH = 0.80; + + /** + * @var BlockFrameDecorator + */ + protected $_frame; + + function __construct(BlockFrameDecorator $frame) + { + parent::__construct($frame); + } + + /** + * Calculate the ideal used value for the width property as per: + * http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins + * + * @param float $width + * + * @return array + */ + protected function _calculate_width($width) + { + $frame = $this->_frame; + $style = $frame->get_style(); + $w = $frame->get_containing_block("w"); + + if ($style->position === "fixed") { + $w = $frame->get_parent()->get_containing_block("w"); + } + + $rm = $style->length_in_pt($style->margin_right, $w); + $lm = $style->length_in_pt($style->margin_left, $w); + + $left = $style->length_in_pt($style->left, $w); + $right = $style->length_in_pt($style->right, $w); + + // Handle 'auto' values + $dims = array($style->border_left_width, + $style->border_right_width, + $style->padding_left, + $style->padding_right, + $width !== "auto" ? $width : 0, + $rm !== "auto" ? $rm : 0, + $lm !== "auto" ? $lm : 0); + + // absolutely positioned boxes take the 'left' and 'right' properties into account + if ($frame->is_absolute()) { + $absolute = true; + $dims[] = $left !== "auto" ? $left : 0; + $dims[] = $right !== "auto" ? $right : 0; + } else { + $absolute = false; + } + + $sum = $style->length_in_pt($dims, $w); + + // Compare to the containing block + $diff = $w - $sum; + + if ($diff > 0) { + + if ($absolute) { + + // resolve auto properties: see + // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width + + if ($width === "auto" && $left === "auto" && $right === "auto") { + + if ($lm === "auto") $lm = 0; + if ($rm === "auto") $rm = 0; + + // Technically, the width should be "shrink-to-fit" i.e. based on the + // preferred width of the content... a little too costly here as a + // special case. Just get the width to take up the slack: + $left = 0; + $right = 0; + $width = $diff; + } else if ($width === "auto") { + + if ($lm === "auto") $lm = 0; + if ($rm === "auto") $rm = 0; + if ($left === "auto") $left = 0; + if ($right === "auto") $right = 0; + + $width = $diff; + } else if ($left === "auto") { + + if ($lm === "auto") $lm = 0; + if ($rm === "auto") $rm = 0; + if ($right === "auto") $right = 0; + + $left = $diff; + } else if ($right === "auto") { + + if ($lm === "auto") $lm = 0; + if ($rm === "auto") $rm = 0; + + $right = $diff; + } + + } else { + + // Find auto properties and get them to take up the slack + if ($width === "auto") { + $width = $diff; + } else if ($lm === "auto" && $rm === "auto") { + $lm = $rm = round($diff / 2); + } else if ($lm === "auto") { + $lm = $diff; + } else if ($rm === "auto") { + $rm = $diff; + } + } + + } else if ($diff < 0) { + + // We are over constrained--set margin-right to the difference + $rm = $diff; + + } + + return array( + "width" => $width, + "margin_left" => $lm, + "margin_right" => $rm, + "left" => $left, + "right" => $right, + ); + } + + /** + * Call the above function, but resolve max/min widths + * + * @throws Exception + * @return array + */ + protected function _calculate_restricted_width() + { + $frame = $this->_frame; + $style = $frame->get_style(); + $cb = $frame->get_containing_block(); + + if ($style->position === "fixed") { + $cb = $frame->get_root()->get_containing_block(); + } + + //if ( $style->position === "absolute" ) + // $cb = $frame->find_positionned_parent()->get_containing_block(); + + if (!isset($cb["w"])) { + throw new Exception("Box property calculation requires containing block width"); + } + + // Treat width 100% as auto + if ($style->width === "100%") { + $width = "auto"; + } else { + $width = $style->length_in_pt($style->width, $cb["w"]); + } + + extract($this->_calculate_width($width)); + + // Handle min/max width + $min_width = $style->length_in_pt($style->min_width, $cb["w"]); + $max_width = $style->length_in_pt($style->max_width, $cb["w"]); + + if ($max_width !== "none" && $min_width > $max_width) { + list($max_width, $min_width) = array($min_width, $max_width); + } + + if ($max_width !== "none" && $width > $max_width) { + extract($this->_calculate_width($max_width)); + } + + if ($width < $min_width) { + extract($this->_calculate_width($min_width)); + } + + return array($width, $margin_left, $margin_right, $left, $right); + } + + /** + * Determine the unrestricted height of content within the block + * not by adding each line's height, but by getting the last line's position. + * This because lines could have been pushed lower by a clearing element. + * + * @return float + */ + protected function _calculate_content_height() + { + $lines = $this->_frame->get_line_boxes(); + $height = 0; + + foreach ($lines as $line) { + $height += $line->h; + } + + /* + $first_line = reset($lines); + $last_line = end($lines); + $height2 = $last_line->y + $last_line->h - $first_line->y; + */ + + return $height; + } + + /** + * Determine the frame's restricted height + * + * @return array + */ + protected function _calculate_restricted_height() + { + $frame = $this->_frame; + $style = $frame->get_style(); + $content_height = $this->_calculate_content_height(); + $cb = $frame->get_containing_block(); + + $height = $style->length_in_pt($style->height, $cb["h"]); + + $top = $style->length_in_pt($style->top, $cb["h"]); + $bottom = $style->length_in_pt($style->bottom, $cb["h"]); + + $margin_top = $style->length_in_pt($style->margin_top, $cb["h"]); + $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]); + + if ($frame->is_absolute()) { + + // see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height + + $dims = array($top !== "auto" ? $top : 0, + $style->margin_top !== "auto" ? $style->margin_top : 0, + $style->padding_top, + $style->border_top_width, + $height !== "auto" ? $height : 0, + $style->border_bottom_width, + $style->padding_bottom, + $style->margin_bottom !== "auto" ? $style->margin_bottom : 0, + $bottom !== "auto" ? $bottom : 0); + + $sum = $style->length_in_pt($dims, $cb["h"]); + + $diff = $cb["h"] - $sum; + + if ($diff > 0) { + + if ($height === "auto" && $top === "auto" && $bottom === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $height = $diff; + } else if ($height === "auto" && $top === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $height = $content_height; + $top = $diff - $content_height; + } else if ($height === "auto" && $bottom === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $height = $content_height; + $bottom = $diff - $content_height; + } else if ($top === "auto" && $bottom === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $bottom = $diff; + } else if ($top === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $top = $diff; + } else if ($height === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $height = $diff; + } else if ($bottom === "auto") { + + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + + $bottom = $diff; + } else { + + if ($style->overflow === "visible") { + // set all autos to zero + if ($margin_top === "auto") $margin_top = 0; + if ($margin_bottom === "auto") $margin_bottom = 0; + if ($top === "auto") $top = 0; + if ($bottom === "auto") $bottom = 0; + if ($height === "auto") $height = $content_height; + } + + // FIXME: overflow hidden + } + + } + + } else { + + // Expand the height if overflow is visible + if ($height === "auto" && $content_height > $height /* && $style->overflow === "visible" */) { + $height = $content_height; + } + + // FIXME: this should probably be moved to a seperate function as per + // _calculate_restricted_width + + // Only handle min/max height if the height is independent of the frame's content + if (!($style->overflow === "visible" || + ($style->overflow === "hidden" && $height === "auto")) + ) { + + $min_height = $style->min_height; + $max_height = $style->max_height; + + if (isset($cb["h"])) { + $min_height = $style->length_in_pt($min_height, $cb["h"]); + $max_height = $style->length_in_pt($max_height, $cb["h"]); + + } else if (isset($cb["w"])) { + + if (mb_strpos($min_height, "%") !== false) { + $min_height = 0; + } else { + $min_height = $style->length_in_pt($min_height, $cb["w"]); + } + + if (mb_strpos($max_height, "%") !== false) { + $max_height = "none"; + } else { + $max_height = $style->length_in_pt($max_height, $cb["w"]); + } + } + + if ($max_height !== "none" && $min_height > $max_height) { + // Swap 'em + list($max_height, $min_height) = array($min_height, $max_height); + } + + if ($max_height !== "none" && $height > $max_height) { + $height = $max_height; + } + + if ($height < $min_height) { + $height = $min_height; + } + } + + } + + return array($height, $margin_top, $margin_bottom, $top, $bottom); + + } + + /** + * Adjust the justification of each of our lines. + * http://www.w3.org/TR/CSS21/text.html#propdef-text-align + */ + protected function _text_align() + { + $style = $this->_frame->get_style(); + $w = $this->_frame->get_containing_block("w"); + $width = $style->length_in_pt($style->width, $w); + + switch ($style->text_align) { + default: + case "left": + foreach ($this->_frame->get_line_boxes() as $line) { + if (!$line->left) { + continue; + } + + foreach ($line->get_frames() as $frame) { + if ($frame instanceof BlockFrameDecorator) { + continue; + } + $frame->set_position($frame->get_position("x") + $line->left); + } + } + return; + + case "right": + foreach ($this->_frame->get_line_boxes() as $line) { + // Move each child over by $dx + $dx = $width - $line->w - $line->right; + + foreach ($line->get_frames() as $frame) { + // Block frames are not aligned by text-align + if ($frame instanceof BlockFrameDecorator) { + continue; + } + + $frame->set_position($frame->get_position("x") + $dx); + } + } + break; + + + case "justify": + // We justify all lines except the last one + $lines = $this->_frame->get_line_boxes(); // needs to be a variable (strict standards) + array_pop($lines); + + foreach ($lines as $i => $line) { + if ($line->br) { + unset($lines[$i]); + } + } + + // One space character's width. Will be used to get a more accurate spacing + $space_width = $this->get_dompdf()->getFontMetrics()->getTextWidth(" ", $style->font_family, $style->font_size); + + foreach ($lines as $line) { + if ($line->left) { + foreach ($line->get_frames() as $frame) { + if (!$frame instanceof TextFrameDecorator) { + continue; + } + + $frame->set_position($frame->get_position("x") + $line->left); + } + } + + // Only set the spacing if the line is long enough. This is really + // just an aesthetic choice ;) + //if ( $line["left"] + $line["w"] + $line["right"] > self::MIN_JUSTIFY_WIDTH * $width ) { + + // Set the spacing for each child + if ($line->wc > 1) { + $spacing = ($width - ($line->left + $line->w + $line->right) + $space_width) / ($line->wc - 1); + } else { + $spacing = 0; + } + + $dx = 0; + foreach ($line->get_frames() as $frame) { + if (!$frame instanceof TextFrameDecorator) { + continue; + } + + $text = $frame->get_text(); + $spaces = mb_substr_count($text, " "); + + $char_spacing = $style->length_in_pt($style->letter_spacing); + $_spacing = $spacing + $char_spacing; + + $frame->set_position($frame->get_position("x") + $dx); + $frame->set_text_spacing($_spacing); + + $dx += $spaces * $_spacing; + } + + // The line (should) now occupy the entire width + $line->w = $width; + + //} + } + break; + + case "center": + case "centre": + foreach ($this->_frame->get_line_boxes() as $line) { + // Centre each line by moving each frame in the line by: + $dx = ($width + $line->left - $line->w - $line->right) / 2; + + foreach ($line->get_frames() as $frame) { + // Block frames are not aligned by text-align + if ($frame instanceof BlockFrameDecorator) { + continue; + } + + $frame->set_position($frame->get_position("x") + $dx); + } + } + break; + } + } + + /** + * Align inline children vertically. + * Aligns each child vertically after each line is reflowed + */ + function vertical_align() + { + + $canvas = null; + + foreach ($this->_frame->get_line_boxes() as $line) { + + $height = $line->h; + + foreach ($line->get_frames() as $frame) { + $style = $frame->get_style(); + + if ($style->display !== "inline") { + continue; + } + + $align = $frame->get_parent()->get_style()->vertical_align; + + if (!isset($canvas)) { + $canvas = $frame->get_root()->get_dompdf()->get_canvas(); + } + + $baseline = $canvas->get_font_baseline($style->font_family, $style->font_size); + $y_offset = 0; + + switch ($align) { + case "baseline": + $y_offset = $height * 0.8 - $baseline; // The 0.8 ratio is arbitrary until we find it's meaning + break; + + case "middle": + $y_offset = ($height * 0.8 - $baseline) / 2; + break; + + case "sub": + $y_offset = 0.3 * $height; + break; + + case "super": + $y_offset = -0.2 * $height; + break; + + case "text-top": + case "top": // Not strictly accurate, but good enough for now + break; + + case "text-bottom": + case "bottom": + $y_offset = $height * 0.8 - $baseline; + break; + } + + if ($y_offset) { + $frame->move(0, $y_offset); + } + } + } + } + + /** + * @param Frame $child + */ + function process_clear(Frame $child) + { + $child_style = $child->get_style(); + $root = $this->_frame->get_root(); + + // Handle "clear" + if ($child_style->clear !== "none") { + $lowest_y = $root->get_lowest_float_offset($child); + + // If a float is still applying, we handle it + if ($lowest_y) { + if ($child->is_in_flow()) { + $line_box = $this->_frame->get_current_line_box(); + $line_box->y = $lowest_y + $child->get_margin_height(); + $line_box->left = 0; + $line_box->right = 0; + } + + $child->move(0, $lowest_y - $child->get_position("y")); + } + } + } + + /** + * @param Frame $child + * @param float $cb_x + * @param float $cb_w + */ + function process_float(Frame $child, $cb_x, $cb_w) + { + $child_style = $child->get_style(); + $root = $this->_frame->get_root(); + + // Handle "float" + if ($child_style->float !== "none") { + $root->add_floating_frame($child); + + // Remove next frame's beginning whitespace + $next = $child->get_next_sibling(); + if ($next && $next instanceof TextFrameDecorator) { + $next->set_text(ltrim($next->get_text())); + } + + $line_box = $this->_frame->get_current_line_box(); + list($old_x, $old_y) = $child->get_position(); + + $float_x = $cb_x; + $float_y = $old_y; + $float_w = $child->get_margin_width(); + + if ($child_style->clear === "none") { + switch ($child_style->float) { + case "left": + $float_x += $line_box->left; + break; + case "right": + $float_x += ($cb_w - $line_box->right - $float_w); + break; + } + } else { + if ($child_style->float === "right") { + $float_x += ($cb_w - $float_w); + } + } + + if ($cb_w < $float_x + $float_w - $old_x) { + // TODO handle when floating elements don't fit + } + + $line_box->get_float_offsets(); + + if ($child->_float_next_line) { + $float_y += $line_box->h; + } + + $child->set_position($float_x, $float_y); + $child->move($float_x - $old_x, $float_y - $old_y, true); + } + } + + /** + * @param BlockFrameDecorator $block + */ + function reflow(BlockFrameDecorator $block = null) + { + + // Check if a page break is forced + $page = $this->_frame->get_root(); + $page->check_forced_page_break($this->_frame); + + // Bail if the page is full + if ($page->is_full()) { + return; + } + + // Generated content + $this->_set_content(); + + // Collapse margins if required + $this->_collapse_margins(); + + $style = $this->_frame->get_style(); + $cb = $this->_frame->get_containing_block(); + + if ($style->position === "fixed") { + $cb = $this->_frame->get_root()->get_containing_block(); + } + + // Determine the constraints imposed by this frame: calculate the width + // of the content area: + list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width(); + + // Store the calculated properties + $style->width = $w . "pt"; + $style->margin_left = $left_margin . "pt"; + $style->margin_right = $right_margin . "pt"; + $style->left = $left . "pt"; + $style->right = $right . "pt"; + + // Update the position + $this->_frame->position(); + list($x, $y) = $this->_frame->get_position(); + + // Adjust the first line based on the text-indent property + $indent = $style->length_in_pt($style->text_indent, $cb["w"]); + $this->_frame->increase_line_width($indent); + + // Determine the content edge + $top = $style->length_in_pt(array($style->margin_top, + $style->padding_top, + $style->border_top_width), $cb["h"]); + + $bottom = $style->length_in_pt(array($style->border_bottom_width, + $style->margin_bottom, + $style->padding_bottom), $cb["h"]); + + $cb_x = $x + $left_margin + $style->length_in_pt(array($style->border_left_width, + $style->padding_left), $cb["w"]); + + $cb_y = $y + $top; + + $cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y; + + // Set the y position of the first line in this block + $line_box = $this->_frame->get_current_line_box(); + $line_box->y = $cb_y; + $line_box->get_float_offsets(); + + // Set the containing blocks and reflow each child + foreach ($this->_frame->get_children() as $child) { + + // Bail out if the page is full + if ($page->is_full()) { + break; + } + + $child->set_containing_block($cb_x, $cb_y, $w, $cb_h); + + $this->process_clear($child); + + $child->reflow($this->_frame); + + // Don't add the child to the line if a page break has occurred + if ($page->check_page_break($child)) { + break; + } + + $this->process_float($child, $cb_x, $w); + } + + // Determine our height + list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height(); + $style->height = $height; + $style->margin_top = $margin_top; + $style->margin_bottom = $margin_bottom; + $style->top = $top; + $style->bottom = $bottom; + + $needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto")); + + // Absolute positioning measurement + if ($needs_reposition) { + $orig_style = $this->_frame->get_original_style(); + if ($orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto")) { + $width = 0; + foreach ($this->_frame->get_line_boxes() as $line) { + $width = max($line->w, $width); + } + $style->width = $width; + } + + $style->left = $orig_style->left; + $style->right = $orig_style->right; + } + + $this->_text_align(); + $this->vertical_align(); + + // Absolute positioning + if ($needs_reposition) { + list($x, $y) = $this->_frame->get_position(); + $this->_frame->position(); + list($new_x, $new_y) = $this->_frame->get_position(); + $this->_frame->move($new_x - $x, $new_y - $y, true); + } + + if ($block && $this->_frame->is_in_flow()) { + $block->add_frame_to_line($this->_frame); + + // May be inline-block + if ($style->display === "block") { + $block->add_line(); + } + } + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/Image.php b/library/vendor/dompdf/src/FrameReflower/Image.php new file mode 100644 index 000000000..c59814d5c --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/Image.php @@ -0,0 +1,195 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\Helpers; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Image as ImageFrameDecorator; + +/** + * Image reflower class + * + * @package dompdf + */ +class Image extends AbstractFrameReflower +{ + + function __construct(ImageFrameDecorator $frame) + { + parent::__construct($frame); + } + + function reflow(BlockFrameDecorator $block = null) + { + $this->_frame->position(); + + //FLOAT + //$frame = $this->_frame; + //$page = $frame->get_root(); + + //if ($frame->get_style()->float !== "none" ) { + // $page->add_floating_frame($this); + //} + + // Set the frame's width + $this->get_min_max_width(); + + if ($block) { + $block->add_frame_to_line($this->_frame); + } + } + + function get_min_max_width() + { + if ($this->get_dompdf()->get_option("debugPng")) { + // Determine the image's size. Time consuming. Only when really needed? + list($img_width, $img_height) = Helpers::dompdf_getimagesize($this->_frame->get_image_url(), $this->get_dompdf()->getHttpContext()); + print "get_min_max_width() " . + $this->_frame->get_style()->width . ' ' . + $this->_frame->get_style()->height . ';' . + $this->_frame->get_parent()->get_style()->width . " " . + $this->_frame->get_parent()->get_style()->height . ";" . + $this->_frame->get_parent()->get_parent()->get_style()->width . ' ' . + $this->_frame->get_parent()->get_parent()->get_style()->height . ';' . + $img_width . ' ' . + $img_height . '|'; + } + + $style = $this->_frame->get_style(); + + $width_forced = true; + $height_forced = true; + + //own style auto or invalid value: use natural size in px + //own style value: ignore suffix text including unit, use given number as px + //own style %: walk up parent chain until found available space in pt; fill available space + // + //special ignored unit: e.g. 10ex: e treated as exponent; x ignored; 10e completely invalid ->like auto + + $width = ($style->width > 0 ? $style->width : 0); + if (Helpers::is_percent($width)) { + $t = 0.0; + for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) { + $f_style = $f->get_style(); + $t = $f_style->length_in_pt($f_style->width); + if ($t != 0) { + break; + } + } + $width = ((float)rtrim($width, "%") * $t) / 100; //maybe 0 + } elseif (!mb_strpos($width, 'pt')) { + // Don't set image original size if "%" branch was 0 or size not given. + // Otherwise aspect changed on %/auto combination for width/height + // Resample according to px per inch + // See also ListBulletImage::__construct + $width = $style->length_in_pt($width); + } + + $height = ($style->height > 0 ? $style->height : 0); + if (Helpers::is_percent($height)) { + $t = 0.0; + for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) { + $f_style = $f->get_style(); + $t = $f_style->length_in_pt($f_style->height); + if ($t != 0) { + break; + } + } + $height = ((float)rtrim($height, "%") * $t) / 100; //maybe 0 + } elseif (!mb_strpos($height, 'pt')) { + // Don't set image original size if "%" branch was 0 or size not given. + // Otherwise aspect changed on %/auto combination for width/height + // Resample according to px per inch + // See also ListBulletImage::__construct + $height = $style->length_in_pt($height); + } + + if ($width == 0 || $height == 0) { + // Determine the image's size. Time consuming. Only when really needed! + list($img_width, $img_height) = Helpers::dompdf_getimagesize($this->_frame->get_image_url(), $this->get_dompdf()->getHttpContext()); + + // don't treat 0 as error. Can be downscaled or can be catched elsewhere if image not readable. + // Resample according to px per inch + // See also ListBulletImage::__construct + if ($width == 0 && $height == 0) { + $dpi = $this->_frame->get_dompdf()->get_option("dpi"); + $width = (float)($img_width * 72) / $dpi; + $height = (float)($img_height * 72) / $dpi; + $width_forced = false; + $height_forced = false; + } elseif ($height == 0 && $width != 0) { + $height_forced = false; + $height = ($width / $img_width) * $img_height; //keep aspect ratio + } elseif ($width == 0 && $height != 0) { + $width_forced = false; + $width = ($height / $img_height) * $img_width; //keep aspect ratio + } + } + + // Handle min/max width/height + if ($style->min_width !== "none" || + $style->max_width !== "none" || + $style->min_height !== "none" || + $style->max_height !== "none" + ) { + + list( /*$x*/, /*$y*/, $w, $h) = $this->_frame->get_containing_block(); + + $min_width = $style->length_in_pt($style->min_width, $w); + $max_width = $style->length_in_pt($style->max_width, $w); + $min_height = $style->length_in_pt($style->min_height, $h); + $max_height = $style->length_in_pt($style->max_height, $h); + + if ($max_width !== "none" && $width > $max_width) { + if (!$height_forced) { + $height *= $max_width / $width; + } + + $width = $max_width; + } + + if ($min_width !== "none" && $width < $min_width) { + if (!$height_forced) { + $height *= $min_width / $width; + } + + $width = $min_width; + } + + if ($max_height !== "none" && $height > $max_height) { + if (!$width_forced) { + $width *= $max_height / $height; + } + + $height = $max_height; + } + + if ($min_height !== "none" && $height < $min_height) { + if (!$width_forced) { + $width *= $min_height / $height; + } + + $height = $min_height; + } + } + + if ($this->get_dompdf()->get_option("debugPng")) print $width . ' ' . $height . ';'; + + $style->width = $width . "pt"; + $style->height = $height . "pt"; + + $style->min_width = "none"; + $style->max_width = "none"; + $style->min_height = "none"; + $style->max_height = "none"; + + return array($width, $width, "min" => $width, "max" => $width); + + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/Inline.php b/library/vendor/dompdf/src/FrameReflower/Inline.php new file mode 100644 index 000000000..1f05fa9ef --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/Inline.php @@ -0,0 +1,76 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Text as TextFrameDecorator; + +/** + * Reflows inline frames + * + * @package dompdf + */ +class Inline extends AbstractFrameReflower +{ + + function __construct(Frame $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function reflow(BlockFrameDecorator $block = null) + { + $frame = $this->_frame; + + // Check if a page break is forced + $page = $frame->get_root(); + $page->check_forced_page_break($frame); + + if ($page->is_full()) { + return; + } + + $style = $frame->get_style(); + + // Generated content + $this->_set_content(); + + $frame->position(); + + $cb = $frame->get_containing_block(); + + // Add our margin, padding & border to the first and last children + if (($f = $frame->get_first_child()) && $f instanceof TextFrameDecorator) { + $f_style = $f->get_style(); + $f_style->margin_left = $style->margin_left; + $f_style->padding_left = $style->padding_left; + $f_style->border_left = $style->border_left; + } + + if (($l = $frame->get_last_child()) && $l instanceof TextFrameDecorator) { + $l_style = $l->get_style(); + $l_style->margin_right = $style->margin_right; + $l_style->padding_right = $style->padding_right; + $l_style->border_right = $style->border_right; + } + + if ($block) { + $block->add_frame_to_line($this->_frame); + } + + // Set the containing blocks and reflow each child. The containing + // block is not changed by line boxes. + foreach ($frame->get_children() as $child) { + $child->set_containing_block($cb); + $child->reflow($block); + } + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/ListBullet.php b/library/vendor/dompdf/src/FrameReflower/ListBullet.php new file mode 100644 index 000000000..553195db5 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/ListBullet.php @@ -0,0 +1,41 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Reflows list bullets + * + * @package dompdf + */ +class ListBullet extends AbstractFrameReflower +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function reflow(BlockFrameDecorator $block = null) + { + $style = $this->_frame->get_style(); + + $style->width = $this->_frame->get_width(); + $this->_frame->position(); + + if ($style->list_style_position === "inside") { + $p = $this->_frame->find_block_parent(); + $p->add_frame_to_line($this->_frame); + } + + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/NullFrameReflower.php b/library/vendor/dompdf/src/FrameReflower/NullFrameReflower.php new file mode 100644 index 000000000..1f9d2e2c0 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/NullFrameReflower.php @@ -0,0 +1,32 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\FrameReflower; + +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; + +/** + * Dummy reflower + * + * @package dompdf + */ +class NullFrameReflower extends AbstractFrameReflower +{ + + function __construct(Frame $frame) + { + parent::__construct($frame); + } + + function reflow(BlockFrameDecorator $block = null) + { + return; + } + +} diff --git a/library/vendor/dompdf/src/FrameReflower/Page.php b/library/vendor/dompdf/src/FrameReflower/Page.php new file mode 100644 index 000000000..770eb6c83 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/Page.php @@ -0,0 +1,199 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Page as PageFrameDecorator; + +/** + * Reflows pages + * + * @package dompdf + */ +class Page extends AbstractFrameReflower +{ + + /** + * Cache of the callbacks array + * + * @var array + */ + private $_callbacks; + + /** + * Cache of the canvas + * + * @var \Dompdf\Canvas + */ + private $_canvas; + + function __construct(PageFrameDecorator $frame) + { + parent::__construct($frame); + } + + function apply_page_style(Frame $frame, $page_number) + { + $style = $frame->get_style(); + $page_styles = $style->get_stylesheet()->get_page_styles(); + + // http://www.w3.org/TR/CSS21/page.html#page-selectors + if (count($page_styles) > 1) { + $odd = $page_number % 2 == 1; + $first = $page_number == 1; + + $style = clone $page_styles["base"]; + + // FIXME RTL + if ($odd && isset($page_styles[":right"])) { + $style->merge($page_styles[":right"]); + } + + if ($odd && isset($page_styles[":odd"])) { + $style->merge($page_styles[":odd"]); + } + + // FIXME RTL + if (!$odd && isset($page_styles[":left"])) { + $style->merge($page_styles[":left"]); + } + + if (!$odd && isset($page_styles[":even"])) { + $style->merge($page_styles[":even"]); + } + + if ($first && isset($page_styles[":first"])) { + $style->merge($page_styles[":first"]); + } + + $frame->set_style($style); + } + } + + //........................................................................ + + /** + * Paged layout: + * http://www.w3.org/TR/CSS21/page.html + */ + function reflow(BlockFrameDecorator $block = null) + { + $fixed_children = array(); + $prev_child = null; + $child = $this->_frame->get_first_child(); + $current_page = 0; + + while ($child) { + $this->apply_page_style($this->_frame, $current_page + 1); + + $style = $this->_frame->get_style(); + + // Pages are only concerned with margins + $cb = $this->_frame->get_containing_block(); + $left = $style->length_in_pt($style->margin_left, $cb["w"]); + $right = $style->length_in_pt($style->margin_right, $cb["w"]); + $top = $style->length_in_pt($style->margin_top, $cb["h"]); + $bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]); + + $content_x = $cb["x"] + $left; + $content_y = $cb["y"] + $top; + $content_width = $cb["w"] - $left - $right; + $content_height = $cb["h"] - $top - $bottom; + + // Only if it's the first page, we save the nodes with a fixed position + if ($current_page == 0) { + $children = $child->get_children(); + foreach ($children as $onechild) { + if ($onechild->get_style()->position === "fixed") { + $fixed_children[] = $onechild->deep_copy(); + } + } + $fixed_children = array_reverse($fixed_children); + } + + $child->set_containing_block($content_x, $content_y, $content_width, $content_height); + + // Check for begin reflow callback + $this->_check_callbacks("begin_page_reflow", $child); + + //Insert a copy of each node which have a fixed position + if ($current_page >= 1) { + foreach ($fixed_children as $fixed_child) { + $child->insert_child_before($fixed_child->deep_copy(), $child->get_first_child()); + } + } + + $child->reflow(); + $next_child = $child->get_next_sibling(); + + // Check for begin render callback + $this->_check_callbacks("begin_page_render", $child); + + // Render the page + $this->_frame->get_renderer()->render($child); + + // Check for end render callback + $this->_check_callbacks("end_page_render", $child); + + if ($next_child) { + $this->_frame->next_page(); + } + + // Wait to dispose of all frames on the previous page + // so callback will have access to them + if ($prev_child) { + $prev_child->dispose(true); + } + $prev_child = $child; + $child = $next_child; + $current_page++; + } + + // Dispose of previous page if it still exists + if ($prev_child) { + $prev_child->dispose(true); + } + } + + //........................................................................ + + /** + * Check for callbacks that need to be performed when a given event + * gets triggered on a page + * + * @param string $event the type of event + * @param Frame $frame the frame that event is triggered on + */ + protected function _check_callbacks($event, $frame) + { + if (!isset($this->_callbacks)) { + $dompdf = $this->_frame->get_dompdf(); + $this->_callbacks = $dompdf->get_callbacks(); + $this->_canvas = $dompdf->get_canvas(); + } + + if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) { + $info = array( + 0 => $this->_canvas, "canvas" => $this->_canvas, + 1 => $frame, "frame" => $frame, + ); + $fs = $this->_callbacks[$event]; + foreach ($fs as $f) { + if (is_callable($f)) { + if (is_array($f)) { + $f[0]->{$f[1]}($info); + } else { + $f($info); + } + } + } + } + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/Table.php b/library/vendor/dompdf/src/FrameReflower/Table.php new file mode 100644 index 000000000..a3626d8c7 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/Table.php @@ -0,0 +1,587 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Table as TableFrameDecorator; + +/** + * Reflows tables + * + * @access private + * @package dompdf + */ +class Table extends AbstractFrameReflower +{ + /** + * Frame for this reflower + * + * @var TableFrameDecorator + */ + protected $_frame; + + /** + * Cache of results between call to get_min_max_width and assign_widths + * + * @var array + */ + protected $_state; + + function __construct(TableFrameDecorator $frame) + { + $this->_state = null; + parent::__construct($frame); + } + + /** + * State is held here so it needs to be reset along with the decorator + */ + function reset() + { + $this->_state = null; + $this->_min_max_cache = null; + } + + //........................................................................ + + protected function _assign_widths() + { + $style = $this->_frame->get_style(); + + // Find the min/max width of the table and sort the columns into + // absolute/percent/auto arrays + $min_width = $this->_state["min_width"]; + $max_width = $this->_state["max_width"]; + $percent_used = $this->_state["percent_used"]; + $absolute_used = $this->_state["absolute_used"]; + $auto_min = $this->_state["auto_min"]; + + $absolute =& $this->_state["absolute"]; + $percent =& $this->_state["percent"]; + $auto =& $this->_state["auto"]; + + // Determine the actual width of the table + $cb = $this->_frame->get_containing_block(); + $columns =& $this->_frame->get_cellmap()->get_columns(); + + $width = $style->width; + + // Calculate padding & border fudge factor + $left = $style->margin_left; + $right = $style->margin_right; + + $centered = ($left === "auto" && $right === "auto"); + + $left = $left === "auto" ? 0 : $style->length_in_pt($left, $cb["w"]); + $right = $right === "auto" ? 0 : $style->length_in_pt($right, $cb["w"]); + + $delta = $left + $right; + + if (!$centered) { + $delta += $style->length_in_pt(array( + $style->padding_left, + $style->border_left_width, + $style->border_right_width, + $style->padding_right), + $cb["w"]); + } + + $min_table_width = $style->length_in_pt($style->min_width, $cb["w"] - $delta); + + // min & max widths already include borders & padding + $min_width -= $delta; + $max_width -= $delta; + + if ($width !== "auto") { + + $preferred_width = $style->length_in_pt($width, $cb["w"]) - $delta; + + if ($preferred_width < $min_table_width) + $preferred_width = $min_table_width; + + if ($preferred_width > $min_width) + $width = $preferred_width; + else + $width = $min_width; + + } else { + + if ($max_width + $delta < $cb["w"]) + $width = $max_width; + else if ($cb["w"] - $delta > $min_width) + $width = $cb["w"] - $delta; + else + $width = $min_width; + + if ($width < $min_table_width) + $width = $min_table_width; + + } + + // Store our resolved width + $style->width = $width; + + $cellmap = $this->_frame->get_cellmap(); + + if ($cellmap->is_columns_locked()) { + return; + } + + // If the whole table fits on the page, then assign each column it's max width + if ($width == $max_width) { + + foreach (array_keys($columns) as $i) + $cellmap->set_column_width($i, $columns[$i]["max-width"]); + + return; + } + + // Determine leftover and assign it evenly to all columns + if ($width > $min_width) { + + // We have four cases to deal with: + // + // 1. All columns are auto--no widths have been specified. In this + // case we distribute extra space across all columns weighted by max-width. + // + // 2. Only absolute widths have been specified. In this case we + // distribute any extra space equally among 'width: auto' columns, or all + // columns if no auto columns have been specified. + // + // 3. Only percentage widths have been specified. In this case we + // normalize the percentage values and distribute any remaining % to + // width: auto columns. We then proceed to assign widths as fractions + // of the table width. + // + // 4. Both absolute and percentage widths have been specified. + + $increment = 0; + + // Case 1: + if ($absolute_used == 0 && $percent_used == 0) { + $increment = $width - $min_width; + + foreach (array_keys($columns) as $i) { + $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment * ($columns[$i]["max-width"] / $max_width)); + } + return; + } + + + // Case 2 + if ($absolute_used > 0 && $percent_used == 0) { + + if (count($auto) > 0) + $increment = ($width - $auto_min - $absolute_used) / count($auto); + + // Use the absolutely specified width or the increment + foreach (array_keys($columns) as $i) { + + if ($columns[$i]["absolute"] > 0 && count($auto)) + $cellmap->set_column_width($i, $columns[$i]["min-width"]); + else if (count($auto)) + $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment); + else { + // All absolute columns + $increment = ($width - $absolute_used) * $columns[$i]["absolute"] / $absolute_used; + + $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment); + } + + } + return; + } + + + // Case 3: + if ($absolute_used == 0 && $percent_used > 0) { + + $scale = null; + $remaining = null; + + // Scale percent values if the total percentage is > 100, or if all + // values are specified as percentages. + if ($percent_used > 100 || count($auto) == 0) + $scale = 100 / $percent_used; + else + $scale = 1; + + // Account for the minimum space used by the unassigned auto columns + $used_width = $auto_min; + + foreach ($percent as $i) { + $columns[$i]["percent"] *= $scale; + + $slack = $width - $used_width; + + $w = min($columns[$i]["percent"] * $width / 100, $slack); + + if ($w < $columns[$i]["min-width"]) + $w = $columns[$i]["min-width"]; + + $cellmap->set_column_width($i, $w); + $used_width += $w; + + } + + // This works because $used_width includes the min-width of each + // unassigned column + if (count($auto) > 0) { + $increment = ($width - $used_width) / count($auto); + + foreach ($auto as $i) + $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment); + + } + return; + } + + // Case 4: + + // First-come, first served + if ($absolute_used > 0 && $percent_used > 0) { + + $used_width = $auto_min; + + foreach ($absolute as $i) { + $cellmap->set_column_width($i, $columns[$i]["min-width"]); + $used_width += $columns[$i]["min-width"]; + } + + // Scale percent values if the total percentage is > 100 or there + // are no auto values to take up slack + if ($percent_used > 100 || count($auto) == 0) + $scale = 100 / $percent_used; + else + $scale = 1; + + $remaining_width = $width - $used_width; + + foreach ($percent as $i) { + $slack = $remaining_width - $used_width; + + $columns[$i]["percent"] *= $scale; + $w = min($columns[$i]["percent"] * $remaining_width / 100, $slack); + + if ($w < $columns[$i]["min-width"]) + $w = $columns[$i]["min-width"]; + + $columns[$i]["used-width"] = $w; + $used_width += $w; + } + + if (count($auto) > 0) { + $increment = ($width - $used_width) / count($auto); + + foreach ($auto as $i) + $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment); + + } + + return; + } + + + } else { // we are over constrained + + // Each column gets its minimum width + foreach (array_keys($columns) as $i) + $cellmap->set_column_width($i, $columns[$i]["min-width"]); + + } + } + + //........................................................................ + + // Determine the frame's height based on min/max height + protected function _calculate_height() + { + + $style = $this->_frame->get_style(); + $height = $style->height; + + $cellmap = $this->_frame->get_cellmap(); + $cellmap->assign_frame_heights(); + $rows = $cellmap->get_rows(); + + // Determine our content height + $content_height = 0; + foreach ($rows as $r) + $content_height += $r["height"]; + + $cb = $this->_frame->get_containing_block(); + + if (!($style->overflow === "visible" || + ($style->overflow === "hidden" && $height === "auto")) + ) { + + // Only handle min/max height if the height is independent of the frame's content + + $min_height = $style->min_height; + $max_height = $style->max_height; + + if (isset($cb["h"])) { + $min_height = $style->length_in_pt($min_height, $cb["h"]); + $max_height = $style->length_in_pt($max_height, $cb["h"]); + + } else if (isset($cb["w"])) { + + if (mb_strpos($min_height, "%") !== false) + $min_height = 0; + else + $min_height = $style->length_in_pt($min_height, $cb["w"]); + + if (mb_strpos($max_height, "%") !== false) + $max_height = "none"; + else + $max_height = $style->length_in_pt($max_height, $cb["w"]); + } + + if ($max_height !== "none" && $min_height > $max_height) + // Swap 'em + list($max_height, $min_height) = array($min_height, $max_height); + + if ($max_height !== "none" && $height > $max_height) + $height = $max_height; + + if ($height < $min_height) + $height = $min_height; + + } else { + + // Use the content height or the height value, whichever is greater + if ($height !== "auto") { + $height = $style->length_in_pt($height, $cb["h"]); + + if ($height <= $content_height) + $height = $content_height; + else + $cellmap->set_frame_heights($height, $content_height); + + } else + $height = $content_height; + + } + + return $height; + + } + //........................................................................ + + /** + * @param BlockFrameDecorator $block + */ + function reflow(BlockFrameDecorator $block = null) + { + /** @var TableFrameDecorator */ + $frame = $this->_frame; + + // Check if a page break is forced + $page = $frame->get_root(); + $page->check_forced_page_break($frame); + + // Bail if the page is full + if ($page->is_full()) + return; + + // Let the page know that we're reflowing a table so that splits + // are suppressed (simply setting page-break-inside: avoid won't + // work because we may have an arbitrary number of block elements + // inside tds.) + $page->table_reflow_start(); + + // Collapse vertical margins, if required + $this->_collapse_margins(); + + $frame->position(); + + // Table layout algorithm: + // http://www.w3.org/TR/CSS21/tables.html#auto-table-layout + + if (is_null($this->_state)) + $this->get_min_max_width(); + + $cb = $frame->get_containing_block(); + $style = $frame->get_style(); + + // This is slightly inexact, but should be okay. Add half the + // border-spacing to the table as padding. The other half is added to + // the cells themselves. + if ($style->border_collapse === "separate") { + list($h, $v) = $style->border_spacing; + + $v = $style->length_in_pt($v) / 2; + $h = $style->length_in_pt($h) / 2; + + $style->padding_left = $style->length_in_pt($style->padding_left, $cb["w"]) + $h; + $style->padding_right = $style->length_in_pt($style->padding_right, $cb["w"]) + $h; + $style->padding_top = $style->length_in_pt($style->padding_top, $cb["h"]) + $v; + $style->padding_bottom = $style->length_in_pt($style->padding_bottom, $cb["h"]) + $v; + + } + + $this->_assign_widths(); + + // Adjust left & right margins, if they are auto + $width = $style->width; + $left = $style->margin_left; + $right = $style->margin_right; + + $diff = $cb["w"] - $width; + + if ($left === "auto" && $right === "auto") { + if ($diff < 0) { + $left = 0; + $right = $diff; + } else { + $left = $right = $diff / 2; + } + + $style->margin_left = "$left pt"; + $style->margin_right = "$right pt"; + + } else { + if ($left === "auto") { + $left = $style->length_in_pt($cb["w"] - $right - $width, $cb["w"]); + } + if ($right === "auto") { + $left = $style->length_in_pt($left, $cb["w"]); + } + } + + list($x, $y) = $frame->get_position(); + + // Determine the content edge + $content_x = $x + $left + $style->length_in_pt(array($style->padding_left, + $style->border_left_width), $cb["w"]); + $content_y = $y + $style->length_in_pt(array($style->margin_top, + $style->border_top_width, + $style->padding_top), $cb["h"]); + + if (isset($cb["h"])) + $h = $cb["h"]; + else + $h = null; + + $cellmap = $frame->get_cellmap(); + $col =& $cellmap->get_column(0); + $col["x"] = $content_x; + + $row =& $cellmap->get_row(0); + $row["y"] = $content_y; + + $cellmap->assign_x_positions(); + + // Set the containing block of each child & reflow + foreach ($frame->get_children() as $child) { + + // Bail if the page is full + if (!$page->in_nested_table() && $page->is_full()) + break; + + $child->set_containing_block($content_x, $content_y, $width, $h); + $child->reflow(); + + if (!$page->in_nested_table()) + // Check if a split has occured + $page->check_page_break($child); + + } + + // Assign heights to our cells: + $style->height = $this->_calculate_height(); + + if ($style->border_collapse === "collapse") { + // Unset our borders because our cells are now using them + $style->border_style = "none"; + } + + $page->table_reflow_end(); + + // Debugging: + //echo ($this->_frame->get_cellmap()); + + if ($block && $style->float === "none" && $frame->is_in_flow()) { + $block->add_frame_to_line($frame); + $block->add_line(); + } + } + + //........................................................................ + + function get_min_max_width() + { + + if (!is_null($this->_min_max_cache)) + return $this->_min_max_cache; + + $style = $this->_frame->get_style(); + + $this->_frame->normalise(); + + // Add the cells to the cellmap (this will calcluate column widths as + // frames are added) + $this->_frame->get_cellmap()->add_frame($this->_frame); + + // Find the min/max width of the table and sort the columns into + // absolute/percent/auto arrays + $this->_state = array(); + $this->_state["min_width"] = 0; + $this->_state["max_width"] = 0; + + $this->_state["percent_used"] = 0; + $this->_state["absolute_used"] = 0; + $this->_state["auto_min"] = 0; + + $this->_state["absolute"] = array(); + $this->_state["percent"] = array(); + $this->_state["auto"] = array(); + + $columns =& $this->_frame->get_cellmap()->get_columns(); + foreach (array_keys($columns) as $i) { + $this->_state["min_width"] += $columns[$i]["min-width"]; + $this->_state["max_width"] += $columns[$i]["max-width"]; + + if ($columns[$i]["absolute"] > 0) { + $this->_state["absolute"][] = $i; + $this->_state["absolute_used"] += $columns[$i]["absolute"]; + + } else if ($columns[$i]["percent"] > 0) { + $this->_state["percent"][] = $i; + $this->_state["percent_used"] += $columns[$i]["percent"]; + + } else { + $this->_state["auto"][] = $i; + $this->_state["auto_min"] += $columns[$i]["min-width"]; + } + } + + // Account for margins & padding + $dims = array($style->border_left_width, + $style->border_right_width, + $style->padding_left, + $style->padding_right, + $style->margin_left, + $style->margin_right); + + if ($style->border_collapse !== "collapse") + list($dims[]) = $style->border_spacing; + + $delta = $style->length_in_pt($dims, $this->_frame->get_containing_block("w")); + + $this->_state["min_width"] += $delta; + $this->_state["max_width"] += $delta; + + return $this->_min_max_cache = array( + $this->_state["min_width"], + $this->_state["max_width"], + "min" => $this->_state["min_width"], + "max" => $this->_state["max_width"], + ); + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/TableCell.php b/library/vendor/dompdf/src/FrameReflower/TableCell.php new file mode 100644 index 000000000..0dc74a1f3 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/TableCell.php @@ -0,0 +1,120 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Table as TableFrameDecorator; + +/** + * Reflows table cells + * + * @package dompdf + */ +class TableCell extends Block +{ + function __construct(BlockFrameDecorator $frame) + { + parent::__construct($frame); + } + + function reflow(BlockFrameDecorator $block = null) + { + + $style = $this->_frame->get_style(); + + $table = TableFrameDecorator::find_parent_table($this->_frame); + $cellmap = $table->get_cellmap(); + + list($x, $y) = $cellmap->get_frame_position($this->_frame); + $this->_frame->set_position($x, $y); + + $cells = $cellmap->get_spanned_cells($this->_frame); + + $w = 0; + foreach ($cells["columns"] as $i) { + $col = $cellmap->get_column($i); + $w += $col["used-width"]; + } + + //FIXME? + $h = $this->_frame->get_containing_block("h"); + + $left_space = $style->length_in_pt(array($style->margin_left, + $style->padding_left, + $style->border_left_width), + $w); + + $right_space = $style->length_in_pt(array($style->padding_right, + $style->margin_right, + $style->border_right_width), + $w); + + $top_space = $style->length_in_pt(array($style->margin_top, + $style->padding_top, + $style->border_top_width), + $h); + $bottom_space = $style->length_in_pt(array($style->margin_bottom, + $style->padding_bottom, + $style->border_bottom_width), + $h); + + $style->width = $cb_w = $w - $left_space - $right_space; + + $content_x = $x + $left_space; + $content_y = $line_y = $y + $top_space; + + // Adjust the first line based on the text-indent property + $indent = $style->length_in_pt($style->text_indent, $w); + $this->_frame->increase_line_width($indent); + + $page = $this->_frame->get_root(); + + // Set the y position of the first line in the cell + $line_box = $this->_frame->get_current_line_box(); + $line_box->y = $line_y; + + // Set the containing blocks and reflow each child + foreach ($this->_frame->get_children() as $child) { + + if ($page->is_full()) + break; + + $child->set_containing_block($content_x, $content_y, $cb_w, $h); + + $this->process_clear($child); + + $child->reflow($this->_frame); + + $this->process_float($child, $x + $left_space, $w - $right_space - $left_space); + } + + // Determine our height + $style_height = $style->length_in_pt($style->height, $h); + + $this->_frame->set_content_height($this->_calculate_content_height()); + + $height = max($style_height, $this->_frame->get_content_height()); + + // Let the cellmap know our height + $cell_height = $height / count($cells["rows"]); + + if ($style_height <= $height) + $cell_height += $top_space + $bottom_space; + + foreach ($cells["rows"] as $i) + $cellmap->set_row_height($i, $cell_height); + + $style->height = $height; + + $this->_text_align(); + + $this->vertical_align(); + + } + +} diff --git a/library/vendor/dompdf/src/FrameReflower/TableRow.php b/library/vendor/dompdf/src/FrameReflower/TableRow.php new file mode 100644 index 000000000..7f2de4cff --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/TableRow.php @@ -0,0 +1,68 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Table as TableFrameDecorator; +use Dompdf\FrameDecorator\TableRow as TableRowFrameDecorator; +use Dompdf\Exception; + +/** + * Reflows table rows + * + * @package dompdf + */ +class TableRow extends AbstractFrameReflower +{ + function __construct(TableRowFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function reflow(BlockFrameDecorator $block = null) + { + $page = $this->_frame->get_root(); + + if ($page->is_full()) + return; + + $this->_frame->position(); + $style = $this->_frame->get_style(); + $cb = $this->_frame->get_containing_block(); + + foreach ($this->_frame->get_children() as $child) { + + if ($page->is_full()) + return; + + $child->set_containing_block($cb); + $child->reflow(); + + } + + if ($page->is_full()) + return; + + $table = TableFrameDecorator::find_parent_table($this->_frame); + $cellmap = $table->get_cellmap(); + $style->width = $cellmap->get_frame_width($this->_frame); + $style->height = $cellmap->get_frame_height($this->_frame); + + $this->_frame->set_position($cellmap->get_frame_position($this->_frame)); + + } + + //........................................................................ + + function get_min_max_width() + { + throw new Exception("Min/max width is undefined for table rows"); + } +} diff --git a/library/vendor/dompdf/src/FrameReflower/TableRowGroup.php b/library/vendor/dompdf/src/FrameReflower/TableRowGroup.php new file mode 100644 index 000000000..eb13dd599 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/TableRowGroup.php @@ -0,0 +1,65 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Table as TableFrameDecorator; + +/** + * Reflows table row groups (e.g. tbody tags) + * + * @package dompdf + */ +class TableRowGroup extends AbstractFrameReflower +{ + + function __construct($frame) + { + parent::__construct($frame); + } + + function reflow(BlockFrameDecorator $block = null) + { + $page = $this->_frame->get_root(); + + $style = $this->_frame->get_style(); + + // Our width is equal to the width of our parent table + $table = TableFrameDecorator::find_parent_table($this->_frame); + + $cb = $this->_frame->get_containing_block(); + + foreach ($this->_frame->get_children() as $child) { + // Bail if the page is full + if ($page->is_full()) + return; + + $child->set_containing_block($cb["x"], $cb["y"], $cb["w"], $cb["h"]); + $child->reflow(); + + // Check if a split has occured + $page->check_page_break($child); + + } + + if ($page->is_full()) + return; + + $cellmap = $table->get_cellmap(); + $style->width = $cellmap->get_frame_width($this->_frame); + $style->height = $cellmap->get_frame_height($this->_frame); + + $this->_frame->set_position($cellmap->get_frame_position($this->_frame)); + + if ($table->get_style()->border_collapse === "collapse") + // Unset our borders because our cells are now using them + $style->border_style = "none"; + + } + +} diff --git a/library/vendor/dompdf/src/FrameReflower/Text.php b/library/vendor/dompdf/src/FrameReflower/Text.php new file mode 100644 index 000000000..950f7fae9 --- /dev/null +++ b/library/vendor/dompdf/src/FrameReflower/Text.php @@ -0,0 +1,487 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\FrameReflower; + +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\FrameDecorator\Text as TextFrameDecorator; +use Dompdf\FontMetrics; + +/** + * Reflows text frames. + * + * @package dompdf + */ +class Text extends AbstractFrameReflower +{ + + /** + * @var BlockFrameDecorator + */ + protected $_block_parent; // Nearest block-level ancestor + + /** + * @var TextFrameDecorator + */ + protected $_frame; + + public static $_whitespace_pattern = "/[ \t\r\n\f]+/u"; + + /** + * @var FontMetrics + */ + private $fontMetrics; + + /** + * @param TextFrameDecorator $frame + * @param FontMetrics $fontMetrics + */ + public function __construct(TextFrameDecorator $frame, FontMetrics $fontMetrics) + { + parent::__construct($frame); + $this->setFontMetrics($fontMetrics); + } + + //........................................................................ + + protected function _collapse_white_space($text) + { + //$text = $this->_frame->get_text(); +// if ( $this->_block_parent->get_current_line_box->w == 0 ) +// $text = ltrim($text, " \n\r\t"); + return preg_replace(self::$_whitespace_pattern, " ", $text); + } + + //........................................................................ + + protected function _line_break($text) + { + $style = $this->_frame->get_style(); + $size = $style->font_size; + $font = $style->font_family; + $current_line = $this->_block_parent->get_current_line_box(); + + // Determine the available width + $line_width = $this->_frame->get_containing_block("w"); + $current_line_width = $current_line->left + $current_line->w + $current_line->right; + + $available_width = $line_width - $current_line_width; + + // Account for word-spacing + $word_spacing = $style->length_in_pt($style->word_spacing); + $char_spacing = $style->length_in_pt($style->letter_spacing); + + // Determine the frame width including margin, padding & border + $text_width = $this->getFontMetrics()->getTextWidth($text, $font, $size, $word_spacing, $char_spacing); + $mbp_width = + $style->length_in_pt(array($style->margin_left, + $style->border_left_width, + $style->padding_left, + $style->padding_right, + $style->border_right_width, + $style->margin_right), $line_width); + + $frame_width = $text_width + $mbp_width; + +// Debugging: +// Helpers::pre_r("Text: '" . htmlspecialchars($text). "'"); +// Helpers::pre_r("width: " .$frame_width); +// Helpers::pre_r("textwidth + delta: $text_width + $mbp_width"); +// Helpers::pre_r("font-size: $size"); +// Helpers::pre_r("cb[w]: " .$line_width); +// Helpers::pre_r("available width: " . $available_width); +// Helpers::pre_r("current line width: " . $current_line_width); + +// Helpers::pre_r($words); + + if ($frame_width <= $available_width) + return false; + + // split the text into words + $words = preg_split('/([\s-]+)/u', $text, -1, PREG_SPLIT_DELIM_CAPTURE); + $wc = count($words); + + // Determine the split point + $width = 0; + $str = ""; + reset($words); + + // @todo support , + for ($i = 0; $i < $wc; $i += 2) { + $word = $words[$i] . (isset($words[$i + 1]) ? $words[$i + 1] : ""); + $word_width = $this->getFontMetrics()->getTextWidth($word, $font, $size, $word_spacing, $char_spacing); + if ($width + $word_width + $mbp_width > $available_width) + break; + + $width += $word_width; + $str .= $word; + } + + $break_word = ($style->word_wrap === "break-word"); + + // The first word has overflowed. Force it onto the line + if ($current_line_width == 0 && $width == 0) { + + $s = ""; + $last_width = 0; + + if ($break_word) { + for ($j = 0; $j < strlen($word); $j++) { + $s .= $word[$j]; + $_width = $this->getFontMetrics()->getTextWidth($s, $font, $size, $word_spacing, $char_spacing); + if ($_width > $available_width) { + break; + } + + $last_width = $_width; + } + } + + if ($break_word && $last_width > 0) { + $width += $last_width; + $str .= substr($s, 0, -1); + } else { + $width += $word_width; + $str .= $word; + } + } + + $offset = mb_strlen($str); + +// More debugging: +// var_dump($str); +// print_r("Width: ". $width); +// print_r("Offset: " . $offset); + + return $offset; + + } + + //........................................................................ + + protected function _newline_break($text) + { + + if (($i = mb_strpos($text, "\n")) === false) + return false; + + return $i + 1; + + } + + //........................................................................ + + protected function _layout_line() + { + $frame = $this->_frame; + $style = $frame->get_style(); + $text = $frame->get_text(); + $size = $style->font_size; + $font = $style->font_family; + + // Determine the text height + $style->height = $this->getFontMetrics()->getFontHeight($font, $size); + + $split = false; + $add_line = false; + + // Handle text transform: + // http://www.w3.org/TR/CSS21/text.html#propdef-text-transform + switch (strtolower($style->text_transform)) { + default: + break; + case "capitalize": + $text = mb_convert_case($text, MB_CASE_TITLE); + break; + case "uppercase": + $text = mb_convert_case($text, MB_CASE_UPPER); + break; + case "lowercase": + $text = mb_convert_case($text, MB_CASE_LOWER); + break; + } + + // Handle white-space property: + // http://www.w3.org/TR/CSS21/text.html#propdef-white-space + switch ($style->white_space) { + + default: + case "normal": + $frame->set_text($text = $this->_collapse_white_space($text)); + if ($text == "") + break; + + $split = $this->_line_break($text); + break; + + case "pre": + $split = $this->_newline_break($text); + $add_line = $split !== false; + break; + + case "nowrap": + $frame->set_text($text = $this->_collapse_white_space($text)); + break; + + case "pre-wrap": + $split = $this->_newline_break($text); + + if (($tmp = $this->_line_break($text)) !== false) { + $add_line = $split < $tmp; + $split = min($tmp, $split); + } else + $add_line = true; + + break; + + case "pre-line": + // Collapse white-space except for \n + $frame->set_text($text = preg_replace("/[ \t]+/u", " ", $text)); + + if ($text == "") + break; + + $split = $this->_newline_break($text); + + if (($tmp = $this->_line_break($text)) !== false) { + $add_line = $split < $tmp; + $split = min($tmp, $split); + } else + $add_line = true; + + break; + + } + + // Handle degenerate case + if ($text === "") + return; + + if ($split !== false) { + + // Handle edge cases + if ($split == 0 && $text === " ") { + $frame->set_text(""); + return; + } + + if ($split == 0) { + + // Trim newlines from the beginning of the line + //$this->_frame->set_text(ltrim($text, "\n\r")); + + $this->_block_parent->add_line(); + $frame->position(); + + // Layout the new line + $this->_layout_line(); + + } else if ($split < mb_strlen($frame->get_text())) { + // split the line if required + $frame->split_text($split); + + $t = $frame->get_text(); + + // Remove any trailing newlines + if ($split > 1 && $t[$split - 1] === "\n" && !$frame->is_pre()) + $frame->set_text(mb_substr($t, 0, -1)); + + // Do we need to trim spaces on wrapped lines? This might be desired, however, we + // can't trim the lines here or the layout will be affected if trimming the line + // leaves enough space to fit the next word in the text stream (because pdf layout + // is performed elsewhere). + /*if (!$this->_frame->get_prev_sibling() && !$this->_frame->get_next_sibling()) { + $t = $this->_frame->get_text(); + $this->_frame->set_text( trim($t) ); + }*/ + } + + if ($add_line) { + $this->_block_parent->add_line(); + $frame->position(); + } + + } else { + + // Remove empty space from start and end of line, but only where there isn't an inline sibling + // and the parent node isn't an inline element with siblings + // FIXME: Include non-breaking spaces? + $t = $frame->get_text(); + $parent = $frame->get_parent(); + $is_inline_frame = get_class($parent) === 'Inline_Frame_Decorator'; + + if ((!$is_inline_frame && !$frame->get_next_sibling()) /* || + ( $is_inline_frame && !$parent->get_next_sibling())*/ + ) { // fails BOLD UNDERLINED becomes BOLDUNDERLINED + $t = rtrim($t); + } + + if ((!$is_inline_frame && !$frame->get_prev_sibling()) /* || + ( $is_inline_frame && !$parent->get_prev_sibling())*/ + ) { // AB C fails (the whitespace is removed) + $t = ltrim($t); + } + + $frame->set_text($t); + + } + + // Set our new width + $width = $frame->recalculate_width(); + } + + //........................................................................ + + function reflow(BlockFrameDecorator $block = null) + { + $frame = $this->_frame; + $page = $frame->get_root(); + $page->check_forced_page_break($this->_frame); + + if ($page->is_full()) + return; + + $this->_block_parent = /*isset($block) ? $block : */ + $frame->find_block_parent(); + + // Left trim the text if this is the first text on the line and we're + // collapsing white space +// if ( $this->_block_parent->get_current_line()->w == 0 && +// ($frame->get_style()->white_space !== "pre" || +// $frame->get_style()->white_space !== "pre-wrap") ) { +// $frame->set_text( ltrim( $frame->get_text() ) ); +// } + + $frame->position(); + + $this->_layout_line(); + + if ($block) { + $block->add_frame_to_line($frame); + } + } + + //........................................................................ + + // Returns an array(0 => min, 1 => max, "min" => min, "max" => max) of the + // minimum and maximum widths of this frame + function get_min_max_width() + { + /*if ( !is_null($this->_min_max_cache) ) + return $this->_min_max_cache;*/ + $frame = $this->_frame; + $style = $frame->get_style(); + $this->_block_parent = $frame->find_block_parent(); + $line_width = $frame->get_containing_block("w"); + + $str = $text = $frame->get_text(); + $size = $style->font_size; + $font = $style->font_family; + + $word_spacing = $style->length_in_pt($style->word_spacing); + $char_spacing = $style->length_in_pt($style->letter_spacing); + + switch ($style->white_space) { + + default: + case "normal": + $str = preg_replace(self::$_whitespace_pattern, " ", $str); + case "pre-wrap": + case "pre-line": + + // Find the longest word (i.e. minimum length) + + // This technique (using arrays & an anonymous function) is actually + // faster than doing a single-pass character by character scan. Heh, + // yes I took the time to bench it ;) + $words = array_flip(preg_split("/[\s-]+/u", $str, -1, PREG_SPLIT_DELIM_CAPTURE)); + $root = $this; + array_walk($words, function(&$val, $str) use ($font, $size, $word_spacing, $char_spacing, $root) { + $val = $root->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing); + }); + + arsort($words); + $min = reset($words); + break; + + case "pre": + $lines = array_flip(preg_split("/\n/u", $str)); + $root = $this; + array_walk($lines, function(&$val, $str) use ($font, $size, $word_spacing, $char_spacing, $root) { + $val = $root->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing); + }); + + arsort($lines); + $min = reset($lines); + break; + + case "nowrap": + $min = $this->getFontMetrics()->getTextWidth($this->_collapse_white_space($str), $font, $size, $word_spacing, $char_spacing); + break; + + } + + switch ($style->white_space) { + + default: + case "normal": + case "nowrap": + $str = preg_replace(self::$_whitespace_pattern, " ", $text); + break; + + case "pre-line": + //XXX: Is this correct? + $str = preg_replace("/[ \t]+/u", " ", $text); + + case "pre-wrap": + // Find the longest word (i.e. minimum length) + $lines = array_flip(preg_split("/\n/", $text)); + $root = $this; + array_walk($lines, function(&$val, $str) use ($font, $size, $word_spacing, $char_spacing, $root) { + $val = $root->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing); + }); + arsort($lines); + reset($lines); + $str = key($lines); + break; + + } + + $max = $this->getFontMetrics()->getTextWidth($str, $font, $size, $word_spacing, $char_spacing); + + $delta = $style->length_in_pt(array($style->margin_left, + $style->border_left_width, + $style->padding_left, + $style->padding_right, + $style->border_right_width, + $style->margin_right), $line_width); + $min += $delta; + $max += $delta; + + return $this->_min_max_cache = array($min, $max, "min" => $min, "max" => $max); + + } + + /** + * @param FontMetrics $fontMetrics + * @return $this + */ + public function setFontMetrics(FontMetrics $fontMetrics) + { + $this->fontMetrics = $fontMetrics; + return $this; + } + + /** + * @return FontMetrics + */ + public function getFontMetrics() + { + return $this->fontMetrics; + } +} diff --git a/library/vendor/dompdf/src/Helpers.php b/library/vendor/dompdf/src/Helpers.php new file mode 100644 index 000000000..5b19f6812 --- /dev/null +++ b/library/vendor/dompdf/src/Helpers.php @@ -0,0 +1,733 @@ + tags if the current sapi is not 'cli'. + * Returns the output string instead of displaying it if $return is true. + * + * @param mixed $mixed variable or expression to display + * @param bool $return + * + * @return string + */ + public static function pre_r($mixed, $return = false) + { + if ($return) { + return "
" . print_r($mixed, true) . "
"; + } + + if (php_sapi_name() !== "cli") { + echo "
";
+        }
+
+        print_r($mixed);
+
+        if (php_sapi_name() !== "cli") {
+            echo "
"; + } else { + echo "\n"; + } + + flush(); + } + + /** + * builds a full url given a protocol, hostname, base path and url + * + * @param string $protocol + * @param string $host + * @param string $base_path + * @param string $url + * @return string + * + * Initially the trailing slash of $base_path was optional, and conditionally appended. + * However on dynamically created sites, where the page is given as url parameter, + * the base path might not end with an url. + * Therefore do not append a slash, and **require** the $base_url to ending in a slash + * when needed. + * Vice versa, on using the local file system path of a file, make sure that the slash + * is appended (o.k. also for Windows) + */ + public static function build_url($protocol, $host, $base_path, $url) + { + $protocol = mb_strtolower($protocol); + if (strlen($url) == 0) { + //return $protocol . $host . rtrim($base_path, "/\\") . "/"; + return $protocol . $host . $base_path; + } + + // Is the url already fully qualified, a Data URI, or a reference to a named anchor? + if (mb_strpos($url, "://") !== false || mb_substr($url, 0, 1) === "#" || mb_strpos($url, "data:") === 0 || mb_strpos($url, "mailto:") === 0) { + return $url; + } + + $ret = $protocol; + + if (!in_array(mb_strtolower($protocol), array("http://", "https://", "ftp://", "ftps://"))) { + //On Windows local file, an abs path can begin also with a '\' or a drive letter and colon + //drive: followed by a relative path would be a drive specific default folder. + //not known in php app code, treat as abs path + //($url[1] !== ':' || ($url[2]!=='\\' && $url[2]!=='/')) + if ($url[0] !== '/' && (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' || ($url[0] !== '\\' && $url[1] !== ':'))) { + // For rel path and local acess we ignore the host, and run the path through realpath() + $ret .= realpath($base_path) . '/'; + } + $ret .= $url; + $ret = preg_replace('/\?(.*)$/', "", $ret); + return $ret; + } + + // Protocol relative urls (e.g. "//example.org/style.css") + if (strpos($url, '//') === 0) { + $ret .= substr($url, 2); + //remote urls with backslash in html/css are not really correct, but lets be genereous + } elseif ($url[0] === '/' || $url[0] === '\\') { + // Absolute path + $ret .= $host . $url; + } else { + // Relative path + //$base_path = $base_path !== "" ? rtrim($base_path, "/\\") . "/" : ""; + $ret .= $host . $base_path . $url; + } + + return $ret; + } + + /** + * Converts decimal numbers to roman numerals + * + * @param int $num + * + * @throws Exception + * @return string + */ + public static function dec2roman($num) + { + + static $ones = array("", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"); + static $tens = array("", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"); + static $hund = array("", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"); + static $thou = array("", "m", "mm", "mmm"); + + if (!is_numeric($num)) { + throw new Exception("dec2roman() requires a numeric argument."); + } + + if ($num > 4000 || $num < 0) { + return "(out of range)"; + } + + $num = strrev((string)$num); + + $ret = ""; + switch (mb_strlen($num)) { + case 4: + $ret .= $thou[$num[3]]; + case 3: + $ret .= $hund[$num[2]]; + case 2: + $ret .= $tens[$num[1]]; + case 1: + $ret .= $ones[$num[0]]; + default: + break; + } + + return $ret; + } + + /** + * Determines whether $value is a percentage or not + * + * @param float $value + * + * @return bool + */ + public static function is_percent($value) + { + return false !== mb_strpos($value, "%"); + } + + /** + * Parses a data URI scheme + * http://en.wikipedia.org/wiki/Data_URI_scheme + * + * @param string $data_uri The data URI to parse + * + * @return array The result with charset, mime type and decoded data + */ + public static function parse_data_uri($data_uri) + { + if (!preg_match('/^data:(?P[a-z0-9\/+-.]+)(;charset=(?P[a-z0-9-])+)?(?P;base64)?\,(?P.*)?/is', $data_uri, $match)) { + return false; + } + + $match['data'] = rawurldecode($match['data']); + $result = array( + 'charset' => $match['charset'] ? $match['charset'] : 'US-ASCII', + 'mime' => $match['mime'] ? $match['mime'] : 'text/plain', + 'data' => $match['base64'] ? base64_decode($match['data']) : $match['data'], + ); + + return $result; + } + + /** + * Decoder for RLE8 compression in windows bitmaps + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp + * + * @param string $str Data to decode + * @param integer $width Image width + * + * @return string + */ + public static function rle8_decode($str, $width) + { + $lineWidth = $width + (3 - ($width - 1) % 4); + $out = ''; + $cnt = strlen($str); + + for ($i = 0; $i < $cnt; $i++) { + $o = ord($str[$i]); + switch ($o) { + case 0: # ESCAPE + $i++; + switch (ord($str[$i])) { + case 0: # NEW LINE + $padCnt = $lineWidth - strlen($out) % $lineWidth; + if ($padCnt < $lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line + break; + case 1: # END OF FILE + $padCnt = $lineWidth - strlen($out) % $lineWidth; + if ($padCnt < $lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line + break 3; + case 2: # DELTA + $i += 2; + break; + default: # ABSOLUTE MODE + $num = ord($str[$i]); + for ($j = 0; $j < $num; $j++) + $out .= $str[++$i]; + if ($num % 2) $i++; + } + break; + default: + $out .= str_repeat($str[++$i], $o); + } + } + return $out; + } + + /** + * Decoder for RLE4 compression in windows bitmaps + * see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp + * + * @param string $str Data to decode + * @param integer $width Image width + * + * @return string + */ + public static function rle4_decode($str, $width) + { + $w = floor($width / 2) + ($width % 2); + $lineWidth = $w + (3 - (($width - 1) / 2) % 4); + $pixels = array(); + $cnt = strlen($str); + $c = 0; + + for ($i = 0; $i < $cnt; $i++) { + $o = ord($str[$i]); + switch ($o) { + case 0: # ESCAPE + $i++; + switch (ord($str[$i])) { + case 0: # NEW LINE + while (count($pixels) % $lineWidth != 0) { + $pixels[] = 0; + } + break; + case 1: # END OF FILE + while (count($pixels) % $lineWidth != 0) { + $pixels[] = 0; + } + break 3; + case 2: # DELTA + $i += 2; + break; + default: # ABSOLUTE MODE + $num = ord($str[$i]); + for ($j = 0; $j < $num; $j++) { + if ($j % 2 == 0) { + $c = ord($str[++$i]); + $pixels[] = ($c & 240) >> 4; + } else { + $pixels[] = $c & 15; + } + } + + if ($num % 2 == 0) { + $i++; + } + } + break; + default: + $c = ord($str[++$i]); + for ($j = 0; $j < $o; $j++) { + $pixels[] = ($j % 2 == 0 ? ($c & 240) >> 4 : $c & 15); + } + } + } + + $out = ''; + if (count($pixels) % 2) { + $pixels[] = 0; + } + + $cnt = count($pixels) / 2; + + for ($i = 0; $i < $cnt; $i++) { + $out .= chr(16 * $pixels[2 * $i] + $pixels[2 * $i + 1]); + } + + return $out; + } + + /** + * parse a full url or pathname and return an array(protocol, host, path, + * file + query + fragment) + * + * @param string $url + * @return array + */ + public static function explode_url($url) + { + $protocol = ""; + $host = ""; + $path = ""; + $file = ""; + + $arr = parse_url($url); + if ( isset($arr["scheme"]) ) { + $arr["scheme"] = mb_strtolower($arr["scheme"]); + } + + // Exclude windows drive letters... + if (isset($arr["scheme"]) && $arr["scheme"] !== "file" && strlen($arr["scheme"]) > 1) { + $protocol = $arr["scheme"] . "://"; + + if (isset($arr["user"])) { + $host .= $arr["user"]; + + if (isset($arr["pass"])) { + $host .= ":" . $arr["pass"]; + } + + $host .= "@"; + } + + if (isset($arr["host"])) { + $host .= $arr["host"]; + } + + if (isset($arr["port"])) { + $host .= ":" . $arr["port"]; + } + + if (isset($arr["path"]) && $arr["path"] !== "") { + // Do we have a trailing slash? + if ($arr["path"][mb_strlen($arr["path"]) - 1] === "/") { + $path = $arr["path"]; + $file = ""; + } else { + $path = rtrim(dirname($arr["path"]), '/\\') . "/"; + $file = basename($arr["path"]); + } + } + + if (isset($arr["query"])) { + $file .= "?" . $arr["query"]; + } + + if (isset($arr["fragment"])) { + $file .= "#" . $arr["fragment"]; + } + + } else { + + $i = mb_stripos($url, "file://"); + if ($i !== false) { + $url = mb_substr($url, $i + 7); + } + + $protocol = ""; // "file://"; ? why doesn't this work... It's because of + // network filenames like //COMPU/SHARENAME + + $host = ""; // localhost, really + $file = basename($url); + + $path = dirname($url); + + // Check that the path exists + if ($path !== false) { + $path .= '/'; + + } else { + // generate a url to access the file if no real path found. + $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://'; + + $host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : php_uname("n"); + + if (substr($arr["path"], 0, 1) === '/') { + $path = dirname($arr["path"]); + } else { + $path = '/' . rtrim(dirname($_SERVER["SCRIPT_NAME"]), '/') . '/' . $arr["path"]; + } + } + } + + $ret = array($protocol, $host, $path, $file, + "protocol" => $protocol, + "host" => $host, + "path" => $path, + "file" => $file); + return $ret; + } + + /** + * Print debug messages + * + * @param string $type The type of debug messages to print + * @param string $msg The message to show + */ + public static function dompdf_debug($type, $msg) + { + global $_DOMPDF_DEBUG_TYPES, $_dompdf_show_warnings, $_dompdf_debug; + if (isset($_DOMPDF_DEBUG_TYPES[$type]) && ($_dompdf_show_warnings || $_dompdf_debug)) { + $arr = debug_backtrace(); + + echo basename($arr[0]["file"]) . " (" . $arr[0]["line"] . "): " . $arr[1]["function"] . ": "; + Helpers::pre_r($msg); + } + } + + /** + * Stores warnings in an array for display later + * This function allows warnings generated by the DomDocument parser + * and CSS loader ({@link Stylesheet}) to be captured and displayed + * later. Without this function, errors are displayed immediately and + * PDF streaming is impossible. + * @see http://www.php.net/manual/en/function.set-error_handler.php + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param string $errline + * + * @throws Exception + */ + public static function record_warnings($errno, $errstr, $errfile, $errline) + { + // Not a warning or notice + if (!($errno & (E_WARNING | E_NOTICE | E_USER_NOTICE | E_USER_WARNING))) { + throw new Exception($errstr . " $errno"); + } + + global $_dompdf_warnings; + global $_dompdf_show_warnings; + + if ($_dompdf_show_warnings) { + echo $errstr . "\n"; + } + + $_dompdf_warnings[] = $errstr; + } + + /** + * @param $c + * @return bool|string + */ + public static function unichr($c) + { + if ($c <= 0x7F) { + return chr($c); + } else if ($c <= 0x7FF) { + return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F); + } else if ($c <= 0xFFFF) { + return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F) + . chr(0x80 | $c & 0x3F); + } else if ($c <= 0x10FFFF) { + return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F) + . chr(0x80 | $c >> 6 & 0x3F) + . chr(0x80 | $c & 0x3F); + } + return false; + } + + /** + * Converts a CMYK color to RGB + * + * @param float|float[] $c + * @param float $m + * @param float $y + * @param float $k + * + * @return float[] + */ + public static function cmyk_to_rgb($c, $m = null, $y = null, $k = null) + { + if (is_array($c)) { + list($c, $m, $y, $k) = $c; + } + + $c *= 255; + $m *= 255; + $y *= 255; + $k *= 255; + + $r = (1 - round(2.55 * ($c + $k))); + $g = (1 - round(2.55 * ($m + $k))); + $b = (1 - round(2.55 * ($y + $k))); + + if ($r < 0) $r = 0; + if ($g < 0) $g = 0; + if ($b < 0) $b = 0; + + return array( + $r, $g, $b, + "r" => $r, "g" => $g, "b" => $b + ); + } + + /** + * getimagesize doesn't give a good size for 32bit BMP image v5 + * + * @param string $filename + * @return array The same format as getimagesize($filename) + */ + public static function dompdf_getimagesize($filename, $context = null) + { + static $cache = array(); + + if (isset($cache[$filename])) { + return $cache[$filename]; + } + + list($width, $height, $type) = getimagesize($filename); + + // Custom types + $types = array( + IMAGETYPE_JPEG => "jpeg", + IMAGETYPE_GIF => "gif", + IMAGETYPE_BMP => "bmp", + IMAGETYPE_PNG => "png", + ); + + $type = isset($types[$type]) ? $types[$type] : null; + + if ($width == null || $height == null) { + $data = file_get_contents($filename, null, $context, 0, 26); + + if (substr($data, 0, 2) === "BM") { + $meta = unpack('vtype/Vfilesize/Vreserved/Voffset/Vheadersize/Vwidth/Vheight', $data); + $width = (int)$meta['width']; + $height = (int)$meta['height']; + $type = "bmp"; + } + else { + if (strpos(file_get_contents($filename), "loadFile($filename); + + list($width, $height) = $doc->getDimensions(); + $type = "svg"; + } + } + + } + + return $cache[$filename] = array($width, $height, $type); + } + + /** + * Credit goes to mgutt + * http://www.programmierer-forum.de/function-imagecreatefrombmp-welche-variante-laeuft-t143137.htm + * Modified by Fabien Menager to support RGB555 BMP format + */ + public static function imagecreatefrombmp($filename, $context = null) + { + if (!function_exists("imagecreatetruecolor")) { + trigger_error("The PHP GD extension is required, but is not installed.", E_ERROR); + return false; + } + + // version 1.00 + if (!($fh = fopen($filename, 'rb'))) { + trigger_error('imagecreatefrombmp: Can not open ' . $filename, E_USER_WARNING); + return false; + } + + $bytes_read = 0; + + // read file header + $meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14)); + + // check for bitmap + if ($meta['type'] != 19778) { + trigger_error('imagecreatefrombmp: ' . $filename . ' is not a bitmap!', E_USER_WARNING); + return false; + } + + // read image header + $meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40)); + $bytes_read += 40; + + // read additional bitfield header + if ($meta['compression'] == 3) { + $meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12)); + $bytes_read += 12; + } + + // set bytes and padding + $meta['bytes'] = $meta['bits'] / 8; + $meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4) - floor($meta['width'] * $meta['bytes'] / 4))); + if ($meta['decal'] == 4) { + $meta['decal'] = 0; + } + + // obtain imagesize + if ($meta['imagesize'] < 1) { + $meta['imagesize'] = $meta['filesize'] - $meta['offset']; + // in rare cases filesize is equal to offset so we need to read physical size + if ($meta['imagesize'] < 1) { + $meta['imagesize'] = @filesize($filename) - $meta['offset']; + if ($meta['imagesize'] < 1) { + trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $filename . '!', E_USER_WARNING); + return false; + } + } + } + + // calculate colors + $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors']; + + // read color palette + $palette = array(); + if ($meta['bits'] < 16) { + $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4)); + // in rare cases the color value is signed + if ($palette[1] < 0) { + foreach ($palette as $i => $color) { + $palette[$i] = $color + 16777216; + } + } + } + + // ignore extra bitmap headers + if ($meta['headersize'] > $bytes_read) { + fread($fh, $meta['headersize'] - $bytes_read); + } + + // create gd image + $im = imagecreatetruecolor($meta['width'], $meta['height']); + $data = fread($fh, $meta['imagesize']); + + // uncompress data + switch ($meta['compression']) { + case 1: + $data = Helpers::rle8_decode($data, $meta['width']); + break; + case 2: + $data = Helpers::rle4_decode($data, $meta['width']); + break; + } + + $p = 0; + $vide = chr(0); + $y = $meta['height'] - 1; + $error = 'imagecreatefrombmp: ' . $filename . ' has not enough data!'; + + // loop through the image data beginning with the lower left corner + while ($y >= 0) { + $x = 0; + while ($x < $meta['width']) { + switch ($meta['bits']) { + case 32: + case 24: + if (!($part = substr($data, $p, 3 /*$meta['bytes']*/))) { + trigger_error($error, E_USER_WARNING); + return $im; + } + $color = unpack('V', $part . $vide); + break; + case 16: + if (!($part = substr($data, $p, 2 /*$meta['bytes']*/))) { + trigger_error($error, E_USER_WARNING); + return $im; + } + $color = unpack('v', $part); + + if (empty($meta['rMask']) || $meta['rMask'] != 0xf800) { + $color[1] = (($color[1] & 0x7c00) >> 7) * 65536 + (($color[1] & 0x03e0) >> 2) * 256 + (($color[1] & 0x001f) << 3); // 555 + } else { + $color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3); // 565 + } + break; + case 8: + $color = unpack('n', $vide . substr($data, $p, 1)); + $color[1] = $palette[$color[1] + 1]; + break; + case 4: + $color = unpack('n', $vide . substr($data, floor($p), 1)); + $color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F; + $color[1] = $palette[$color[1] + 1]; + break; + case 1: + $color = unpack('n', $vide . substr($data, floor($p), 1)); + switch (($p * 8) % 8) { + case 0: + $color[1] = $color[1] >> 7; + break; + case 1: + $color[1] = ($color[1] & 0x40) >> 6; + break; + case 2: + $color[1] = ($color[1] & 0x20) >> 5; + break; + case 3: + $color[1] = ($color[1] & 0x10) >> 4; + break; + case 4: + $color[1] = ($color[1] & 0x8) >> 3; + break; + case 5: + $color[1] = ($color[1] & 0x4) >> 2; + break; + case 6: + $color[1] = ($color[1] & 0x2) >> 1; + break; + case 7: + $color[1] = ($color[1] & 0x1); + break; + } + $color[1] = $palette[$color[1] + 1]; + break; + default: + trigger_error('imagecreatefrombmp: ' . $filename . ' has ' . $meta['bits'] . ' bits and this is not supported!', E_USER_WARNING); + return false; + } + imagesetpixel($im, $x, $y, $color[1]); + $x++; + $p += $meta['bytes']; + } + $y--; + $p += $meta['decal']; + } + fclose($fh); + return $im; + } + +} diff --git a/library/vendor/dompdf/src/Image/Cache.php b/library/vendor/dompdf/src/Image/Cache.php new file mode 100644 index 000000000..8e9e70e49 --- /dev/null +++ b/library/vendor/dompdf/src/Image/Cache.php @@ -0,0 +1,186 @@ + + * @author Helmut Tischer + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Image; + +use Dompdf\Dompdf; +use Dompdf\Helpers; +use Dompdf\Exception\ImageException; + +/** + * Static class that resolves image urls and downloads and caches + * remote images if required. + * + * @package dompdf + */ +class Cache +{ + /** + * Array of downloaded images. Cached so that identical images are + * not needlessly downloaded. + * + * @var array + */ + protected static $_cache = array(); + + /** + * The url to the "broken image" used when images can't be loaded + * + * @var string + */ + public static $broken_image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABABAMAAABYR2ztAAAAA3NCSVQICAjb4U/gAAAAHlBMVEWZmZn////g4OCkpKS1tbXv7++9vb2tra3m5ub5+fkFnN6oAAAACXBIWXMAAAsSAAALEgHS3X78AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M0BrLToAAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNC8xMi8xMRPnI58AAAGZSURBVEiJhZbPasMwDMbTw2DHKhDQcbDQPsEge4BAjg0Mxh5gkBcY7Niwkpx32PvOjv9JspX60It/+fxJsqxW1b11gN11rA7N3v6vAd5nfR9fDYCTDiyzAeA6qgKd9QDNoAtsAKyKCxzAAfhdBuyHGwC3oovNvQOaxxJwnSNg3ZQFAlBy4ax7AG6ZBLrgA5Cn038SAPgREiaJHJASwXYEhEQQIACyikTTCWCBJJoANBfpPAKQdBLHFMBYkctcBKIE9lAGggt6gRjgA2GV44CL7m1WgS08fAAdsPHxyyMAIyHujgRwEldHArCKy5cBz90+gNOyf8TTyKOUQN2LPEmgnWWPcKD+sr+rnuqTK1avAcHfRSv3afTgVAbqmCPiggLtGM8aSkBNOidVjADrmIDYebT1PoGsWJEE8Oc0b96aZoe4iMBZPiADB6RAzEUA2vwRmyiAL3Lfv6MBSEmUEg7ALt/3LhxwLgj4QNw4UCbKEsaBNpPsyRbgVRASFig78BIGyJNIJQyQTwIi0RvgT98H+Mi6W67j3X8H/427u5bfpQGVAAAAAElFTkSuQmCC"; + + /** + * Current dompdf instance + * + * @var Dompdf + */ + protected static $_dompdf; + + /** + * Resolve and fetch an image for use. + * + * @param string $url The url of the image + * @param string $protocol Default protocol if none specified in $url + * @param string $host Default host if none specified in $url + * @param string $base_path Default path if none specified in $url + * @param Dompdf $dompdf The Dompdf instance + * + * @throws ImageException + * @return array An array with two elements: The local path to the image and the image extension + */ + static function resolve_url($url, $protocol, $host, $base_path, Dompdf $dompdf) + { + self::$_dompdf = $dompdf; + + $protocol = mb_strtolower($protocol); + $parsed_url = Helpers::explode_url($url); + $message = null; + + $remote = ($protocol && $protocol !== "file://") || ($parsed_url['protocol'] != ""); + + $data_uri = strpos($parsed_url['protocol'], "data:") === 0; + $full_url = null; + $enable_remote = $dompdf->get_option("enable_remote"); + + try { + + // Remote not allowed and is not DataURI + if (!$enable_remote && $remote && !$data_uri) { + throw new ImageException("Remote file access is disabled.", E_WARNING); + } // Remote allowed or DataURI + else { + if ($enable_remote && $remote || $data_uri) { + // Download remote files to a temporary directory + $full_url = Helpers::build_url($protocol, $host, $base_path, $url); + + // From cache + if (isset(self::$_cache[$full_url])) { + $resolved_url = self::$_cache[$full_url]; + } // From remote + else { + $tmp_dir = $dompdf->get_option("temp_dir"); + $resolved_url = tempnam($tmp_dir, "ca_dompdf_img_"); + $image = ""; + + if ($data_uri) { + if ($parsed_data_uri = Helpers::parse_data_uri($url)) { + $image = $parsed_data_uri['data']; + } + } else { + set_error_handler(array("\\Dompdf\\Helpers", "record_warnings")); + $image = file_get_contents($full_url, null, $dompdf->getHttpContext()); + restore_error_handler(); + } + + // Image not found or invalid + if (strlen($image) == 0) { + $msg = ($data_uri ? "Data-URI could not be parsed" : "Image not found"); + throw new ImageException($msg, E_WARNING); + } // Image found, put in cache and process + else { + //e.g. fetch.php?media=url.jpg&cache=1 + //- Image file name might be one of the dynamic parts of the url, don't strip off! + //- a remote url does not need to have a file extension at all + //- local cached file does not have a matching file extension + //Therefore get image type from the content + file_put_contents($resolved_url, $image); + } + } + } // Not remote, local image + else { + $resolved_url = Helpers::build_url($protocol, $host, $base_path, $url); + } + } + + // Check if the local file is readable + if (!is_readable($resolved_url) || !filesize($resolved_url)) { + throw new ImageException("Image not readable or empty", E_WARNING); + } // Check is the file is an image + else { + list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $dompdf->getHttpContext()); + + // Known image type + if ($width && $height && in_array($type, array("gif", "png", "jpeg", "bmp", "svg"))) { + //Don't put replacement image into cache - otherwise it will be deleted on cache cleanup. + //Only execute on successful caching of remote image. + if ($enable_remote && $remote || $data_uri) { + self::$_cache[$full_url] = $resolved_url; + } + } // Unknown image type + else { + throw new ImageException("Image type unknown", E_WARNING); + } + } + } catch (ImageException $e) { + $resolved_url = self::$broken_image; + $type = "png"; + $message = "Image not found or type unknown"; + Helpers::record_warnings($e->getCode(), $e->getMessage() . " \n $url", $e->getFile(), $e->getLine()); + } + + return array($resolved_url, $type, $message); + } + + /** + * Unlink all cached images (i.e. temporary images either downloaded + * or converted) + */ + static function clear() + { + if (empty(self::$_cache) || self::$_dompdf->get_option("debugKeepTemp")) { + return; + } + + foreach (self::$_cache as $file) { + if (self::$_dompdf->get_option("debugPng")) { + print "[clear unlink $file]"; + } + unlink($file); + } + + self::$_cache = array(); + } + + static function detect_type($file, $context = null) + { + list(, , $type) = Helpers::dompdf_getimagesize($file, $context); + + return $type; + } + + static function is_broken($url) + { + return $url === self::$broken_image; + } +} + +if (file_exists(realpath(__DIR__ . "/../../lib/res/broken_image.png"))) { + Cache::$broken_image = realpath(__DIR__ . "/../../lib/res/broken_image.png"); +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/JavascriptEmbedder.php b/library/vendor/dompdf/src/JavascriptEmbedder.php new file mode 100644 index 000000000..1463f2315 --- /dev/null +++ b/library/vendor/dompdf/src/JavascriptEmbedder.php @@ -0,0 +1,43 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf; + +use Dompdf\Frame; + +/** + * Embeds Javascript into the PDF document + * + * @package dompdf + */ +class JavascriptEmbedder +{ + + /** + * @var Dompdf + */ + protected $_dompdf; + + function __construct(Dompdf $dompdf) + { + $this->_dompdf = $dompdf; + } + + function insert($script) + { + $this->_dompdf->get_canvas()->javascript($script); + } + + function render(Frame $frame) + { + if (!$this->_dompdf->get_option("enable_javascript")) { + return; + } + + $this->insert($frame->get_node()->nodeValue); + } +} diff --git a/library/vendor/dompdf/src/LineBox.php b/library/vendor/dompdf/src/LineBox.php new file mode 100644 index 000000000..b94e08917 --- /dev/null +++ b/library/vendor/dompdf/src/LineBox.php @@ -0,0 +1,260 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf; + +use Dompdf\Frame; +use Dompdf\FrameDecorator\Block; +use Dompdf\FrameDecorator\Page; + +/** + * The line box class + * + * This class represents a line box + * http://www.w3.org/TR/CSS2/visuren.html#line-box + * + * @package dompdf + */ +class LineBox +{ + + /** + * @var Block + */ + protected $_block_frame; + + /** + * @var Frame[] + */ + protected $_frames = array(); + + /** + * @var integer + */ + public $wc = 0; + + /** + * @var float + */ + public $y = null; + + /** + * @var float + */ + public $w = 0.0; + + /** + * @var float + */ + public $h = 0.0; + + /** + * @var float + */ + public $left = 0.0; + + /** + * @var float + */ + public $right = 0.0; + + /** + * @var Frame + */ + public $tallest_frame = null; + + /** + * @var bool[] + */ + public $floating_blocks = array(); + + /** + * @var bool + */ + public $br = false; + + /** + * Class constructor + * + * @param Block $frame the Block containing this line + */ + function __construct(Block $frame, $y = 0) + { + $this->_block_frame = $frame; + $this->_frames = array(); + $this->y = $y; + + $this->get_float_offsets(); + } + + /** + * Returns the floating elements inside the first floating parent + * + * @param Page $root + * + * @return Frame[] + */ + function get_floats_inside(Page $root) + { + $floating_frames = $root->get_floating_frames(); + + if (count($floating_frames) == 0) { + return $floating_frames; + } + + // Find nearest floating element + $p = $this->_block_frame; + while ($p->get_style()->float === "none") { + $parent = $p->get_parent(); + + if (!$parent) { + break; + } + + $p = $parent; + } + + if ($p == $root) { + return $floating_frames; + } + + $parent = $p; + + $childs = array(); + + foreach ($floating_frames as $_floating) { + $p = $_floating->get_parent(); + + while (($p = $p->get_parent()) && $p !== $parent) ; + + if ($p) { + $childs[] = $p; + } + } + + return $childs; + } + + function get_float_offsets() + { + static $anti_infinite_loop = 500; // FIXME smelly hack + + $reflower = $this->_block_frame->get_reflower(); + + if (!$reflower) { + return; + } + + $cb_w = null; + + $block = $this->_block_frame; + $root = $block->get_root(); + + if (!$root) { + return; + } + + $floating_frames = $this->get_floats_inside($root); + + foreach ($floating_frames as $child_key => $floating_frame) { + $id = $floating_frame->get_id(); + + if (isset($this->floating_blocks[$id])) { + continue; + } + + $floating_style = $floating_frame->get_style(); + $float = $floating_style->float; + + $floating_width = $floating_frame->get_margin_width(); + + if (!$cb_w) { + $cb_w = $floating_frame->get_containing_block("w"); + } + + $line_w = $this->get_width(); + + if (!$floating_frame->_float_next_line && ($cb_w <= $line_w + $floating_width) && ($cb_w > $line_w)) { + $floating_frame->_float_next_line = true; + continue; + } + + // If the child is still shifted by the floating element + if ($anti_infinite_loop-- > 0 && + $floating_frame->get_position("y") + $floating_frame->get_margin_height() > $this->y && + $block->get_position("x") + $block->get_margin_width() > $floating_frame->get_position("x") + ) { + if ($float === "left") { + $this->left += $floating_width; + } else { + $this->right += $floating_width; + } + + $this->floating_blocks[$id] = true; + } // else, the floating element won't shift anymore + else { + $root->remove_floating_frame($child_key); + } + } + } + + /** + * @return float + */ + function get_width() + { + return $this->left + $this->w + $this->right; + } + + /** + * @return Block + */ + function get_block_frame() + { + return $this->_block_frame; + } + + /** + * @return Frame[] + */ + function &get_frames() + { + return $this->_frames; + } + + /** + * @param Frame $frame + */ + function add_frame(Frame $frame) + { + $this->_frames[] = $frame; + } + + function __toString() + { + $props = array("wc", "y", "w", "h", "left", "right", "br"); + $s = ""; + foreach ($props as $prop) { + $s .= "$prop: " . $this->$prop . "\n"; + } + $s .= count($this->_frames) . " frames\n"; + + return $s; + } + /*function __get($prop) { + if (!isset($this->{"_$prop"})) return; + return $this->{"_$prop"}; + }*/ +} + +/* +class LineBoxList implements Iterator { + private $_p = 0; + private $_lines = array(); + +} +*/ diff --git a/library/vendor/dompdf/src/Options.php b/library/vendor/dompdf/src/Options.php new file mode 100644 index 000000000..d46914517 --- /dev/null +++ b/library/vendor/dompdf/src/Options.php @@ -0,0 +1,974 @@ + ... tags. + * + * ==== IMPORTANT ==== + * Enabling this for documents you do not trust (e.g. arbitrary remote html + * pages) is a security risk. Embedded scripts are run with the same level of + * system access available to dompdf. Set this option to false (recommended) + * if you wish to process untrusted documents. + * + * This setting may increase the risk of system exploit. Do not change + * this settings without understanding the consequences. Additional + * documentation is available on the dompdf wiki at: + * https://github.com/dompdf/dompdf/wiki + * + * @var bool + */ + private $isPhpEnabled = false; + + /** + * Enable remote file access + * + * If this setting is set to true, DOMPDF will access remote sites for + * images and CSS files as required. + * + * ==== IMPORTANT ==== + * This can be a security risk, in particular in combination with isPhpEnabled and + * allowing remote html code to be passed to $dompdf = new DOMPDF(); $dompdf->load_html(...); + * This allows anonymous users to download legally doubtful internet content which on + * tracing back appears to being downloaded by your server, or allows malicious php code + * in remote html pages to be executed by your server with your account privileges. + * + * This setting may increase the risk of system exploit. Do not change + * this settings without understanding the consequences. Additional + * documentation is available on the dompdf wiki at: + * https://github.com/dompdf/dompdf/wiki + * + * @var bool + */ + private $isRemoteEnabled = false; + + /** + * Enable inline Javascript + * + * If this setting is set to true then DOMPDF will automatically insert + * JavaScript code contained within tags. + * + * @var bool + */ + private $isJavascriptEnabled = true; + + /** + * Use the more-than-experimental HTML5 Lib parser + * + * @var bool + */ + private $isHtml5ParserEnabled = false; + + /** + * Whether to enable font subsetting or not. + * + * @var is_bool + */ + private $isFontSubsettingEnabled = false; + + /** + * @var bool + */ + private $debugPng = false; + + /** + * @var bool + */ + private $debugKeepTemp = false; + + /** + * @var bool + */ + private $debugCss = false; + + /** + * @var bool + */ + private $debugLayout = false; + + /** + * @var bool + */ + private $debugLayoutLines = true; + + /** + * @var bool + */ + private $debugLayoutBlocks = true; + + /** + * @var bool + */ + private $debugLayoutInline = true; + + /** + * @var bool + */ + private $debugLayoutPaddingBox = true; + + /** + * The PDF rendering backend to use + * + * Valid settings are 'PDFLib', 'CPDF', 'GD', and 'auto'. 'auto' will + * look for PDFLib and use it if found, or if not it will fall back on + * CPDF. 'GD' renders PDFs to graphic files. {@link Dompdf\CanvasFactory} + * ultimately determines which rendering class to instantiate + * based on this setting. + * + * @var string + */ + private $pdfBackend = "CPDF"; + + /** + * PDFlib license key + * + * If you are using a licensed, commercial version of PDFlib, specify + * your license key here. If you are using PDFlib-Lite or are evaluating + * the commercial version of PDFlib, comment out this setting. + * + * @link http://www.pdflib.com + * + * If pdflib present in web server and auto or selected explicitely above, + * a real license code must exist! + * + * @var string + */ + private $pdflibLicense = ""; + + /** + * @var string + * @deprecated + */ + private $adminUsername = "user"; + + /** + * @var string + * @deprecated + */ + private $adminPassword = "password"; + + /** + * @param array $attributes + */ + public function __construct(array $attributes = null) + { + $this->setChroot(realpath(__DIR__ . "/../")); + $this->setRootDir($this->getChroot()); + $this->setTempDir(sys_get_temp_dir()); + $this->setFontDir($this->chroot . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "fonts"); + $this->setFontCache($this->getFontDir()); + $this->setLogOutputFile($this->getTempDir() . DIRECTORY_SEPARATOR . "log.htm"); + + if (null !== $attributes) { + $this->set($attributes); + } + } + + /** + * @param array|string $attributes + * @param null|mixed $value + * @return $this + */ + public function set($attributes, $value = null) + { + if (!is_array($attributes)) { + $attributes = array($attributes => $value); + } + foreach ($attributes as $key => $value) { + if ($key === 'tempDir' || $key === 'temp_dir') { + $this->setTempDir($value); + } elseif ($key === 'fontDir' || $key === 'font_dir') { + $this->setFontDir($value); + } elseif ($key === 'fontCache' || $key === 'font_cache') { + $this->setFontCache($value); + } elseif ($key === 'chroot') { + $this->setChroot($value); + } elseif ($key === 'logOutputFile' || $key === 'log_output_file') { + $this->setLogOutputFile($value); + } elseif ($key === 'defaultMediaType' || $key === 'default_media_type') { + $this->setDefaultMediaType($value); + } elseif ($key === 'defaultPaperSize' || $key === 'default_paper_size') { + $this->setDefaultPaperSize($value); + } elseif ($key === 'defaultFont' || $key === 'default_font') { + $this->setDefaultFont($value); + } elseif ($key === 'dpi') { + $this->setDpi($value); + } elseif ($key === 'fontHeightRatio' || $key === 'font_height_ratio') { + $this->setFontHeightRatio($value); + } elseif ($key === 'isPhpEnabled' || $key === 'is_php_enabled' || $key === 'enable_php') { + $this->setIsPhpEnabled($value); + } elseif ($key === 'isRemoteEnabled' || $key === 'is_remote_enabled' || $key === 'enable_remote') { + $this->setIsRemoteEnabled($value); + } elseif ($key === 'isJavascriptEnabled' || $key === 'is_javascript_enabled' || $key === 'enable_javascript') { + $this->setIsJavascriptEnabled($value); + } elseif ($key === 'isHtml5ParserEnabled' || $key === 'is_html5_parser_enabled' || $key === 'enable_html5_parser') { + $this->setIsHtml5ParserEnabled($value); + } elseif ($key === 'isFontSubsettingEnabled' || $key === 'is_font_subsetting_enabled' || $key === 'enable_font_subsetting') { + $this->setIsFontSubsettingEnabled($value); + } elseif ($key === 'debugPng' || $key === 'debug_png') { + $this->setDebugPng($value); + } elseif ($key === 'debugKeepTemp' || $key === 'debug_keep_temp') { + $this->setDebugKeepTemp($value); + } elseif ($key === 'debugCss' || $key === 'debug_css') { + $this->setDebugCss($value); + } elseif ($key === 'debugLayout' || $key === 'debug_layout') { + $this->setDebugLayout($value); + } elseif ($key === 'debugLayoutLines' || $key === 'debug_layout_lines') { + $this->setDebugLayoutLines($value); + } elseif ($key === 'debugLayoutBlocks' || $key === 'debug_layout_blocks') { + $this->setDebugLayoutBlocks($value); + } elseif ($key === 'debugLayoutInline' || $key === 'debug_layout_inline') { + $this->setDebugLayoutInline($value); + } elseif ($key === 'debugLayoutPaddingBox' || $key === 'debug_layout_padding_box') { + $this->setDebugLayoutPaddingBox($value); + } elseif ($key === 'pdfBackend' || $key === 'pdf_backend') { + $this->setPdfBackend($value); + } elseif ($key === 'pdflibLicense' || $key === 'pdflib_license') { + $this->setPdflibLicense($value); + } elseif ($key === 'adminUsername' || $key === 'admin_username') { + $this->setAdminUsername($value); + } elseif ($key === 'adminPassword' || $key === 'admin_password') { + $this->setAdminPassword($value); + } + } + return $this; + } + + /** + * @param string $key + * @return mixed + */ + public function get($key) + { + if ($key === 'tempDir' || $key === 'temp_dir') { + return $this->getTempDir(); + } elseif ($key === 'fontDir' || $key === 'font_dir') { + return $this->getFontDir(); + } elseif ($key === 'fontCache' || $key === 'font_cache') { + return $this->getFontCache(); + } elseif ($key === 'chroot') { + return $this->getChroot(); + } elseif ($key === 'logOutputFile' || $key === 'log_output_file') { + return $this->getLogOutputFile(); + } elseif ($key === 'defaultMediaType' || $key === 'default_media_type') { + return $this->getDefaultMediaType(); + } elseif ($key === 'defaultPaperSize' || $key === 'default_paper_size') { + return $this->getDefaultPaperSize(); + } elseif ($key === 'defaultFont' || $key === 'default_font') { + return $this->getDefaultFont(); + } elseif ($key === 'dpi') { + return $this->getDpi(); + } elseif ($key === 'fontHeightRatio' || $key === 'font_height_ratio') { + return $this->getFontHeightRatio(); + } elseif ($key === 'isPhpEnabled' || $key === 'is_php_enabled' || $key === 'enable_php') { + return $this->getIsPhpEnabled(); + } elseif ($key === 'isRemoteEnabled' || $key === 'is_remote_enabled' || $key === 'enable_remote') { + return $this->getIsRemoteEnabled(); + } elseif ($key === 'isJavascriptEnabled' || $key === 'is_javascript_enabled' || $key === 'enable_javascript') { + return $this->getIsJavascriptEnabled(); + } elseif ($key === 'isHtml5ParserEnabled' || $key === 'is_html5_parser_enabled' || $key === 'enable_html5_parser') { + return $this->getIsHtml5ParserEnabled(); + } elseif ($key === 'isFontSubsettingEnabled' || $key === 'is_font_subsetting_enabled' || $key === 'enable_font_subsetting') { + return $this->getIsFontSubsettingEnabled(); + } elseif ($key === 'debugPng' || $key === 'debug_png') { + return $this->getDebugPng(); + } elseif ($key === 'debugKeepTemp' || $key === 'debug_keep_temp') { + return $this->getDebugKeepTemp(); + } elseif ($key === 'debugCss' || $key === 'debug_css') { + return $this->getDebugCss(); + } elseif ($key === 'debugLayout' || $key === 'debug_layout') { + return $this->getDebugLayout(); + } elseif ($key === 'debugLayoutLines' || $key === 'debug_layout_lines') { + return $this->getDebugLayoutLines(); + } elseif ($key === 'debugLayoutBlocks' || $key === 'debug_layout_blocks') { + return $this->getDebugLayoutBlocks(); + } elseif ($key === 'debugLayoutInline' || $key === 'debug_layout_inline') { + return $this->getDebugLayoutInline(); + } elseif ($key === 'debugLayoutPaddingBox' || $key === 'debug_layout_padding_box') { + return $this->getDebugLayoutPaddingBox(); + } elseif ($key === 'pdfBackend' || $key === 'pdf_backend') { + return $this->getPdfBackend(); + } elseif ($key === 'pdflibLicense' || $key === 'pdflib_license') { + return $this->getPdflibLicense(); + } elseif ($key === 'adminUsername' || $key === 'admin_username') { + return $this->getAdminUsername(); + } elseif ($key === 'adminPassword' || $key === 'admin_password') { + return $this->getAdminPassword(); + } + return null; + } + + /** + * @param string $adminPassword + * @return $this + */ + public function setAdminPassword($adminPassword) + { + $this->adminPassword = $adminPassword; + return $this; + } + + /** + * @return string + */ + public function getAdminPassword() + { + return $this->adminPassword; + } + + /** + * @param string $adminUsername + * @return $this + */ + public function setAdminUsername($adminUsername) + { + $this->adminUsername = $adminUsername; + return $this; + } + + /** + * @return string + */ + public function getAdminUsername() + { + return $this->adminUsername; + } + + /** + * @param string $pdfBackend + * @return $this + */ + public function setPdfBackend($pdfBackend) + { + $this->pdfBackend = $pdfBackend; + return $this; + } + + /** + * @return string + */ + public function getPdfBackend() + { + return $this->pdfBackend; + } + + /** + * @param string $pdflibLicense + * @return $this + */ + public function setPdflibLicense($pdflibLicense) + { + $this->pdflibLicense = $pdflibLicense; + return $this; + } + + /** + * @return string + */ + public function getPdflibLicense() + { + return $this->pdflibLicense; + } + + /** + * @param string $chroot + * @return $this + */ + public function setChroot($chroot) + { + $this->chroot = $chroot; + return $this; + } + + /** + * @return string + */ + public function getChroot() + { + return $this->chroot; + } + + /** + * @param boolean $debugCss + * @return $this + */ + public function setDebugCss($debugCss) + { + $this->debugCss = $debugCss; + return $this; + } + + /** + * @return boolean + */ + public function getDebugCss() + { + return $this->debugCss; + } + + /** + * @param boolean $debugKeepTemp + * @return $this + */ + public function setDebugKeepTemp($debugKeepTemp) + { + $this->debugKeepTemp = $debugKeepTemp; + return $this; + } + + /** + * @return boolean + */ + public function getDebugKeepTemp() + { + return $this->debugKeepTemp; + } + + /** + * @param boolean $debugLayout + * @return $this + */ + public function setDebugLayout($debugLayout) + { + $this->debugLayout = $debugLayout; + return $this; + } + + /** + * @return boolean + */ + public function getDebugLayout() + { + return $this->debugLayout; + } + + /** + * @param boolean $debugLayoutBlocks + * @return $this + */ + public function setDebugLayoutBlocks($debugLayoutBlocks) + { + $this->debugLayoutBlocks = $debugLayoutBlocks; + return $this; + } + + /** + * @return boolean + */ + public function getDebugLayoutBlocks() + { + return $this->debugLayoutBlocks; + } + + /** + * @param boolean $debugLayoutInline + * @return $this + */ + public function setDebugLayoutInline($debugLayoutInline) + { + $this->debugLayoutInline = $debugLayoutInline; + return $this; + } + + /** + * @return boolean + */ + public function getDebugLayoutInline() + { + return $this->debugLayoutInline; + } + + /** + * @param boolean $debugLayoutLines + * @return $this + */ + public function setDebugLayoutLines($debugLayoutLines) + { + $this->debugLayoutLines = $debugLayoutLines; + return $this; + } + + /** + * @return boolean + */ + public function getDebugLayoutLines() + { + return $this->debugLayoutLines; + } + + /** + * @param boolean $debugLayoutPaddingBox + * @return $this + */ + public function setDebugLayoutPaddingBox($debugLayoutPaddingBox) + { + $this->debugLayoutPaddingBox = $debugLayoutPaddingBox; + return $this; + } + + /** + * @return boolean + */ + public function getDebugLayoutPaddingBox() + { + return $this->debugLayoutPaddingBox; + } + + /** + * @param boolean $debugPng + * @return $this + */ + public function setDebugPng($debugPng) + { + $this->debugPng = $debugPng; + return $this; + } + + /** + * @return boolean + */ + public function getDebugPng() + { + return $this->debugPng; + } + + /** + * @param string $defaultFont + * @return $this + */ + public function setDefaultFont($defaultFont) + { + $this->defaultFont = $defaultFont; + return $this; + } + + /** + * @return string + */ + public function getDefaultFont() + { + return $this->defaultFont; + } + + /** + * @param string $defaultMediaType + * @return $this + */ + public function setDefaultMediaType($defaultMediaType) + { + $this->defaultMediaType = $defaultMediaType; + return $this; + } + + /** + * @return string + */ + public function getDefaultMediaType() + { + return $this->defaultMediaType; + } + + /** + * @param string $defaultPaperSize + * @return $this + */ + public function setDefaultPaperSize($defaultPaperSize) + { + $this->defaultPaperSize = $defaultPaperSize; + return $this; + } + + /** + * @return string + */ + public function getDefaultPaperSize() + { + return $this->defaultPaperSize; + } + + /** + * @param int $dpi + * @return $this + */ + public function setDpi($dpi) + { + $this->dpi = $dpi; + return $this; + } + + /** + * @return int + */ + public function getDpi() + { + return $this->dpi; + } + + /** + * @param string $fontCache + * @return $this + */ + public function setFontCache($fontCache) + { + $this->fontCache = $fontCache; + return $this; + } + + /** + * @return string + */ + public function getFontCache() + { + return $this->fontCache; + } + + /** + * @param string $fontDir + * @return $this + */ + public function setFontDir($fontDir) + { + $this->fontDir = $fontDir; + return $this; + } + + /** + * @return string + */ + public function getFontDir() + { + return $this->fontDir; + } + + /** + * @param float $fontHeightRatio + * @return $this + */ + public function setFontHeightRatio($fontHeightRatio) + { + $this->fontHeightRatio = $fontHeightRatio; + return $this; + } + + /** + * @return float + */ + public function getFontHeightRatio() + { + return $this->fontHeightRatio; + } + + /** + * @param boolean $isFontSubsettingEnabled + * @return $this + */ + public function setIsFontSubsettingEnabled($isFontSubsettingEnabled) + { + $this->isFontSubsettingEnabled = $isFontSubsettingEnabled; + return $this; + } + + /** + * @return boolean + */ + public function getIsFontSubsettingEnabled() + { + return $this->isFontSubsettingEnabled; + } + + /** + * @return boolean + */ + public function isFontSubsettingEnabled() + { + return $this->getIsFontSubsettingEnabled(); + } + + /** + * @param boolean $isHtml5ParserEnabled + * @return $this + */ + public function setIsHtml5ParserEnabled($isHtml5ParserEnabled) + { + $this->isHtml5ParserEnabled = $isHtml5ParserEnabled; + return $this; + } + + /** + * @return boolean + */ + public function getIsHtml5ParserEnabled() + { + return $this->isHtml5ParserEnabled; + } + + /** + * @return boolean + */ + public function isHtml5ParserEnabled() + { + return $this->getIsHtml5ParserEnabled(); + } + + /** + * @param boolean $isJavascriptEnabled + * @return $this + */ + public function setIsJavascriptEnabled($isJavascriptEnabled) + { + $this->isJavascriptEnabled = $isJavascriptEnabled; + return $this; + } + + /** + * @return boolean + */ + public function getIsJavascriptEnabled() + { + return $this->isJavascriptEnabled; + } + + /** + * @return boolean + */ + public function isJavascriptEnabled() + { + return $this->getIsJavascriptEnabled(); + } + + /** + * @param boolean $isPhpEnabled + * @return $this + */ + public function setIsPhpEnabled($isPhpEnabled) + { + $this->isPhpEnabled = $isPhpEnabled; + return $this; + } + + /** + * @return boolean + */ + public function getIsPhpEnabled() + { + return $this->isPhpEnabled; + } + + /** + * @return boolean + */ + public function isPhpEnabled() + { + return $this->getIsPhpEnabled(); + } + + /** + * @param boolean $isRemoteEnabled + * @return $this + */ + public function setIsRemoteEnabled($isRemoteEnabled) + { + $this->isRemoteEnabled = $isRemoteEnabled; + return $this; + } + + /** + * @return boolean + */ + public function getIsRemoteEnabled() + { + return $this->isRemoteEnabled; + } + + /** + * @return boolean + */ + public function isRemoteEnabled() + { + return $this->getIsRemoteEnabled(); + } + + /** + * @param string $logOutputFile + * @return $this + */ + public function setLogOutputFile($logOutputFile) + { + $this->logOutputFile = $logOutputFile; + return $this; + } + + /** + * @return string + */ + public function getLogOutputFile() + { + return $this->logOutputFile; + } + + /** + * @param string $tempDir + * @return $this + */ + public function setTempDir($tempDir) + { + $this->tempDir = $tempDir; + return $this; + } + + /** + * @return string + */ + public function getTempDir() + { + return $this->tempDir; + } + + /** + * @param string $rootDir + * @return $this + */ + public function setRootDir($rootDir) + { + $this->rootDir = $rootDir; + return $this; + } + + /** + * @return string + */ + public function getRootDir() + { + return $this->rootDir; + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/PhpEvaluator.php b/library/vendor/dompdf/src/PhpEvaluator.php new file mode 100644 index 000000000..c7b877729 --- /dev/null +++ b/library/vendor/dompdf/src/PhpEvaluator.php @@ -0,0 +1,55 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf; + +use Dompdf\Frame; + +/** + * Executes inline PHP code during the rendering process + * + * @package dompdf + */ +class PhpEvaluator +{ + + /** + * @var Canvas + */ + protected $_canvas; + + function __construct(Canvas $canvas) + { + $this->_canvas = $canvas; + } + + function evaluate($code, $vars = array()) + { + if (!$this->_canvas->get_dompdf()->get_option("enable_php")) { + return; + } + + // Set up some variables for the inline code + $pdf = $this->_canvas; + $fontMetrics = $pdf->get_dompdf()->getFontMetrics(); + $PAGE_NUM = $pdf->get_page_number(); + $PAGE_COUNT = $pdf->get_page_count(); + + // Override those variables if passed in + foreach ($vars as $k => $v) { + $$k = $v; + } + + //$code = html_entity_decode($code); // @todo uncomment this when tested + eval($code); + } + + function render(Frame $frame) + { + $this->evaluate($frame->get_node()->nodeValue); + } +} diff --git a/library/vendor/dompdf/src/Positioner/Absolute.php b/library/vendor/dompdf/src/Positioner/Absolute.php new file mode 100644 index 000000000..4feae8208 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/Absolute.php @@ -0,0 +1,123 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Positions absolutely positioned frames + */ +class Absolute extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + function position() + { + + $frame = $this->_frame; + $style = $frame->get_style(); + + $p = $frame->find_positionned_parent(); + + list($x, $y, $w, $h) = $frame->get_containing_block(); + + $top = $style->length_in_pt($style->top, $h); + $right = $style->length_in_pt($style->right, $w); + $bottom = $style->length_in_pt($style->bottom, $h); + $left = $style->length_in_pt($style->left, $w); + + if ($p && !($left === "auto" && $right === "auto")) { + // Get the parent's padding box (see http://www.w3.org/TR/CSS21/visuren.html#propdef-top) + list($x, $y, $w, $h) = $p->get_padding_box(); + } + + list($width, $height) = array($frame->get_margin_width(), $frame->get_margin_height()); + + $orig_style = $this->_frame->get_original_style(); + $orig_width = $orig_style->width; + $orig_height = $orig_style->height; + + /**************************** + * + * Width auto: + * ____________| left=auto | left=fixed | + * right=auto | A | B | + * right=fixed | C | D | + * + * Width fixed: + * ____________| left=auto | left=fixed | + * right=auto | E | F | + * right=fixed | G | H | + *****************************/ + + if ($left === "auto") { + if ($right === "auto") { + // A or E - Keep the frame at the same position + $x = $x + $frame->find_block_parent()->get_current_line_box()->w; + } else { + if ($orig_width === "auto") { + // C + $x += $w - $width - $right; + } else { + // G + $x += $w - $width - $right; + } + } + } else { + if ($right === "auto") { + // B or F + $x += $left; + } else { + if ($orig_width === "auto") { + // D - TODO change width + $x += $left; + } else { + // H - Everything is fixed: left + width win + $x += $left; + } + } + } + + // The same vertically + if ($top === "auto") { + if ($bottom === "auto") { + // A or E - Keep the frame at the same position + $y = $frame->find_block_parent()->get_current_line_box()->y; + } else { + if ($orig_height === "auto") { + // C + $y += $h - $height - $bottom; + } else { + // G + $y += $h - $height - $bottom; + } + } + } else { + if ($bottom === "auto") { + // B or F + $y += $top; + } else { + if ($orig_height === "auto") { + // D - TODO change height + $y += $top; + } else { + // H - Everything is fixed: top + height win + $y += $top; + } + } + } + + $frame->set_position($x, $y); + + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Positioner/AbstractPositioner.php b/library/vendor/dompdf/src/Positioner/AbstractPositioner.php new file mode 100644 index 000000000..1c19356f8 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/AbstractPositioner.php @@ -0,0 +1,52 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Base AbstractPositioner class + * + * Defines postioner interface + * + * @access private + * @package dompdf + */ +abstract class AbstractPositioner +{ + + /** + * @var \Dompdf\FrameDecorator\AbstractFrameDecorator + */ + protected $_frame; + + //........................................................................ + + function __construct(AbstractFrameDecorator $frame) + { + $this->_frame = $frame; + } + + //........................................................................ + + abstract function position(); + + function move($offset_x, $offset_y, $ignore_self = false) + { + list($x, $y) = $this->_frame->get_position(); + + if (!$ignore_self) { + $this->_frame->set_position($x + $offset_x, $y + $offset_y); + } + + foreach ($this->_frame->get_children() as $child) { + $child->move($offset_x, $offset_y); + } + } +} diff --git a/library/vendor/dompdf/src/Positioner/Block.php b/library/vendor/dompdf/src/Positioner/Block.php new file mode 100644 index 000000000..fdb418096 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/Block.php @@ -0,0 +1,63 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Positions block frames + * + * @access private + * @package dompdf + */ +class Block extends AbstractPositioner { + + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function position() + { + $frame = $this->_frame; + $style = $frame->get_style(); + $cb = $frame->get_containing_block(); + $p = $frame->find_block_parent(); + + if ($p) { + $float = $style->float; + + if (!$float || $float === "none") { + $p->add_line(true); + } + $y = $p->get_current_line_box()->y; + + } else { + $y = $cb["y"]; + } + + $x = $cb["x"]; + + // Relative positionning + if ($style->position === "relative") { + $top = $style->length_in_pt($style->top, $cb["h"]); + //$right = $style->length_in_pt($style->right, $cb["w"]); + //$bottom = $style->length_in_pt($style->bottom, $cb["h"]); + $left = $style->length_in_pt($style->left, $cb["w"]); + + $x += $left; + $y += $top; + } + + $frame->set_position($x, $y); + } +} diff --git a/library/vendor/dompdf/src/Positioner/Fixed.php b/library/vendor/dompdf/src/Positioner/Fixed.php new file mode 100644 index 000000000..018f66c57 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/Fixed.php @@ -0,0 +1,95 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Positions fixely positioned frames + */ +class Fixed extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + function position() + { + + $frame = $this->_frame; + $style = $frame->get_original_style(); + $root = $frame->get_root(); + $initialcb = $root->get_containing_block(); + $initialcb_style = $root->get_style(); + + $p = $frame->find_block_parent(); + if ($p) { + $p->add_line(); + } + + // Compute the margins of the @page style + $margin_top = $initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]); + $margin_right = $initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]); + $margin_bottom = $initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]); + $margin_left = $initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]); + + // The needed computed style of the element + $height = $style->length_in_pt($style->height, $initialcb["h"]); + $width = $style->length_in_pt($style->width, $initialcb["w"]); + + $top = $style->length_in_pt($style->top, $initialcb["h"]); + $right = $style->length_in_pt($style->right, $initialcb["w"]); + $bottom = $style->length_in_pt($style->bottom, $initialcb["h"]); + $left = $style->length_in_pt($style->left, $initialcb["w"]); + + $y = $margin_top; + if (isset($top)) { + $y = $top + $margin_top; + if ($top === "auto") { + $y = $margin_top; + if (isset($bottom) && $bottom !== "auto") { + $y = $initialcb["h"] - $bottom - $margin_bottom; + $margin_height = $this->_frame->get_margin_height(); + if ($margin_height !== "auto") { + $y -= $margin_height; + } else { + $y -= $height; + } + } + } + } + + $x = $margin_left; + if (isset($left)) { + $x = $left + $margin_left; + if ($left === "auto") { + $x = $margin_left; + if (isset($right) && $right !== "auto") { + $x = $initialcb["w"] - $right - $margin_right; + $margin_width = $this->_frame->get_margin_width(); + if ($margin_width !== "auto") { + $x -= $margin_width; + } else { + $x -= $width; + } + } + } + } + + $frame->set_position($x, $y); + + $children = $frame->get_children(); + foreach ($children as $child) { + $child->set_position($x, $y); + } + } +} \ No newline at end of file diff --git a/library/vendor/dompdf/src/Positioner/Inline.php b/library/vendor/dompdf/src/Positioner/Inline.php new file mode 100644 index 000000000..80eb677ba --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/Inline.php @@ -0,0 +1,82 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; +use Dompdf\FrameDecorator\Inline as InlineFrameDecorator; +use Dompdf\FrameDecorator\Block as BlockFrameDecorator; +use Dompdf\Exception; + +/** + * Positions inline frames + * + * @package dompdf + */ +class Inline extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function position() + { + /** + * Find our nearest block level parent and access its lines property. + * @var BlockFrameDecorator + */ + $p = $this->_frame->find_block_parent(); + + // Debugging code: + +// Helpers::pre_r("\nPositioning:"); +// Helpers::pre_r("Me: " . $this->_frame->get_node()->nodeName . " (" . spl_object_hash($this->_frame->get_node()) . ")"); +// Helpers::pre_r("Parent: " . $p->get_node()->nodeName . " (" . spl_object_hash($p->get_node()) . ")"); + + // End debugging + + if (!$p) + throw new Exception("No block-level parent found. Not good."); + + $f = $this->_frame; + + $cb = $f->get_containing_block(); + $line = $p->get_current_line_box(); + + // Skip the page break if in a fixed position element + $is_fixed = false; + while ($f = $f->get_parent()) { + if ($f->get_style()->position === "fixed") { + $is_fixed = true; + break; + } + } + + $f = $this->_frame; + + if (!$is_fixed && $f->get_parent() && + $f->get_parent() instanceof InlineFrameDecorator && + $f->is_text_node() + ) { + + $min_max = $f->get_reflower()->get_min_max_width(); + + // If the frame doesn't fit in the current line, a line break occurs + if ($min_max["min"] > ($cb["w"] - $line->left - $line->w - $line->right)) { + $p->add_line(); + } + } + + $f->set_position($cb["x"] + $line->w, $line->y); + + } +} diff --git a/library/vendor/dompdf/src/Positioner/ListBullet.php b/library/vendor/dompdf/src/Positioner/ListBullet.php new file mode 100644 index 000000000..117c52182 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/ListBullet.php @@ -0,0 +1,81 @@ + + * @author Helmut Tischer + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Positions list bullets + * + * @package dompdf + */ +class ListBullet extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function position() + { + + // Bullets & friends are positioned an absolute distance to the left of + // the content edge of their parent element + $cb = $this->_frame->get_containing_block(); + + // Note: this differs from most frames in that we must position + // ourselves after determining our width + $x = $cb["x"] - $this->_frame->get_width(); + + $p = $this->_frame->find_block_parent(); + + $y = $p->get_current_line_box()->y; + + // This is a bit of a hack... + $n = $this->_frame->get_next_sibling(); + if ($n) { + $style = $n->get_style(); + $line_height = $style->length_in_pt($style->line_height, $style->get_font_size()); + $offset = $style->length_in_pt($line_height, $n->get_containing_block("h")) - $this->_frame->get_height(); + $y += $offset / 2; + } + + // Now the position is the left top of the block which should be marked with the bullet. + // We tried to find out the y of the start of the first text character within the block. + // But the top margin/padding does not fit, neither from this nor from the next sibling + // The "bit of a hack" above does not work also. + + // Instead let's position the bullet vertically centered to the block which should be marked. + // But for get_next_sibling() the get_containing_block is all zero, and for find_block_parent() + // the get_containing_block is paper width and the entire list as height. + + // if ($p) { + // //$cb = $n->get_containing_block(); + // $cb = $p->get_containing_block(); + // $y += $cb["h"]/2; + // print 'cb:'.$cb["x"].':'.$cb["y"].':'.$cb["w"].':'.$cb["h"].':'; + // } + + // Todo: + // For now give up on the above. Use Guesswork with font y-pos in the middle of the line spacing + + /*$style = $p->get_style(); + $font_size = $style->get_font_size(); + $line_height = $style->length_in_pt($style->line_height, $font_size); + $y += ($line_height - $font_size) / 2; */ + + //Position is x-end y-top of character position of the bullet. + $this->_frame->set_position($x, $y); + + } +} diff --git a/library/vendor/dompdf/src/Positioner/NullPositioner.php b/library/vendor/dompdf/src/Positioner/NullPositioner.php new file mode 100644 index 000000000..6b27f1949 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/NullPositioner.php @@ -0,0 +1,31 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Dummy positioner + * + * @package dompdf + */ +class NullPositioner extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + function position() + { + return; + } + +} diff --git a/library/vendor/dompdf/src/Positioner/TableCell.php b/library/vendor/dompdf/src/Positioner/TableCell.php new file mode 100644 index 000000000..1fda93d54 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/TableCell.php @@ -0,0 +1,37 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; +use Dompdf\FrameDecorator\Table; + +/** + * Positions table cells + * + * @package dompdf + */ +class TableCell extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function position() + { + + $table = Table::find_parent_table($this->_frame); + $cellmap = $table->get_cellmap(); + $this->_frame->set_position($cellmap->get_frame_position($this->_frame)); + + } +} diff --git a/library/vendor/dompdf/src/Positioner/TableRow.php b/library/vendor/dompdf/src/Positioner/TableRow.php new file mode 100644 index 000000000..4dce49452 --- /dev/null +++ b/library/vendor/dompdf/src/Positioner/TableRow.php @@ -0,0 +1,43 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ + +namespace Dompdf\Positioner; + +use Dompdf\FrameDecorator\AbstractFrameDecorator; + +/** + * Positions table rows + * + * @package dompdf + */ +class TableRow extends AbstractPositioner +{ + + function __construct(AbstractFrameDecorator $frame) + { + parent::__construct($frame); + } + + //........................................................................ + + function position() + { + + $cb = $this->_frame->get_containing_block(); + $p = $this->_frame->get_prev_sibling(); + + if ($p) + $y = $p->get_position("y") + $p->get_margin_height(); + + else + $y = $cb["y"]; + + $this->_frame->set_position($cb["x"], $y); + + } +} diff --git a/library/vendor/dompdf/src/Renderer.php b/library/vendor/dompdf/src/Renderer.php new file mode 100644 index 000000000..492a52364 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer.php @@ -0,0 +1,296 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf; + +use Dompdf\Renderer\AbstractRenderer; +use Dompdf\Renderer\Block; +use Dompdf\Renderer\Image; +use Dompdf\Renderer\ListBullet; +use Dompdf\Renderer\TableCell; +use Dompdf\Renderer\TableRowGroup; +use Dompdf\Renderer\Text; + +use Dompdf\Frame; + +/** + * Concrete renderer + * + * Instantiates several specific renderers in order to render any given frame. + * + * @package dompdf + */ +class Renderer extends AbstractRenderer +{ + + /** + * Array of renderers for specific frame types + * + * @var AbstractRenderer[] + */ + protected $_renderers; + + /** + * Cache of the callbacks array + * + * @var array + */ + private $_callbacks; + + /** + * Advance the canvas to the next page + */ + function new_page() + { + $this->_canvas->new_page(); + } + + /** + * Render frames recursively + * + * @param Frame $frame the frame to render + */ + function render(Frame $frame) + { + global $_dompdf_debug; + + if ($_dompdf_debug) { + echo $frame; + flush(); + } + + $style = $frame->get_style(); + + if (in_array($style->visibility, array("hidden", "collapse"))) { + return; + } + + $display = $style->display; + + // Starts the CSS transformation + if ($style->transform && is_array($style->transform)) { + $this->_canvas->save(); + list($x, $y) = $frame->get_padding_box(); + $origin = $style->transform_origin; + + foreach ($style->transform as $transform) { + list($function, $values) = $transform; + if ($function === "matrix") { + $function = "transform"; + } + + $values = array_map("floatval", $values); + $values[] = $x + $style->length_in_pt($origin[0], $style->width); + $values[] = $y + $style->length_in_pt($origin[1], $style->height); + + call_user_func_array(array($this->_canvas, $function), $values); + } + } + + switch ($display) { + + case "block": + case "list-item": + case "inline-block": + case "table": + case "inline-table": + $this->_render_frame("block", $frame); + break; + + case "inline": + if ($frame->is_text_node()) { + $this->_render_frame("text", $frame); + } else { + $this->_render_frame("inline", $frame); + } + break; + + case "table-cell": + $this->_render_frame("table-cell", $frame); + break; + + case "table-row-group": + case "table-header-group": + case "table-footer-group": + $this->_render_frame("table-row-group", $frame); + break; + + case "-dompdf-list-bullet": + $this->_render_frame("list-bullet", $frame); + break; + + case "-dompdf-image": + $this->_render_frame("image", $frame); + break; + + case "none": + $node = $frame->get_node(); + + if ($node->nodeName === "script") { + if ($node->getAttribute("type") === "text/php" || + $node->getAttribute("language") === "php" + ) { + // Evaluate embedded php scripts + $this->_render_frame("php", $frame); + } elseif ($node->getAttribute("type") === "text/javascript" || + $node->getAttribute("language") === "javascript" + ) { + // Insert JavaScript + $this->_render_frame("javascript", $frame); + } + } + + // Don't render children, so skip to next iter + return; + + default: + break; + + } + + // Starts the overflow: hidden box + if ($style->overflow === "hidden") { + list($x, $y, $w, $h) = $frame->get_padding_box(); + + // get border radii + $style = $frame->get_style(); + list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); + + if ($tl + $tr + $br + $bl > 0) { + $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); + } else { + $this->_canvas->clipping_rectangle($x, $y, $w, $h); + } + } + + $stack = array(); + + foreach ($frame->get_children() as $child) { + // < 0 : nagative z-index + // = 0 : no z-index, no stacking context + // = 1 : stacking context without z-index + // > 1 : z-index + $child_style = $child->get_style(); + $child_z_index = $child_style->z_index; + $z_index = 0; + + if ($child_z_index !== "auto") { + $z_index = intval($child_z_index) + 1; + } elseif ($child_style->float !== "none" || $child->is_positionned()) { + $z_index = 1; + } + + $stack[$z_index][] = $child; + } + + ksort($stack); + + foreach ($stack as $by_index) { + foreach ($by_index as $child) { + $this->render($child); + } + } + + // Ends the overflow: hidden box + if ($style->overflow === "hidden") { + $this->_canvas->clipping_end(); + } + + if ($style->transform && is_array($style->transform)) { + $this->_canvas->restore(); + } + + // Check for end frame callback + $this->_check_callbacks("end_frame", $frame); + } + + /** + * Check for callbacks that need to be performed when a given event + * gets triggered on a frame + * + * @param string $event the type of event + * @param Frame $frame the frame that event is triggered on + */ + protected function _check_callbacks($event, $frame) + { + if (!isset($this->_callbacks)) { + $this->_callbacks = $this->_dompdf->get_callbacks(); + } + + if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) { + $info = array(0 => $this->_canvas, "canvas" => $this->_canvas, + 1 => $frame, "frame" => $frame); + $fs = $this->_callbacks[$event]; + foreach ($fs as $f) { + if (is_callable($f)) { + if (is_array($f)) { + $f[0]->{$f[1]}($info); + } else { + $f($info); + } + } + } + } + } + + /** + * Render a single frame + * + * Creates Renderer objects on demand + * + * @param string $type type of renderer to use + * @param Frame $frame the frame to render + */ + protected function _render_frame($type, $frame) + { + + if (!isset($this->_renderers[$type])) { + + switch ($type) { + case "block": + $this->_renderers[$type] = new Block($this->_dompdf); + break; + + case "inline": + $this->_renderers[$type] = new Renderer\Inline($this->_dompdf); + break; + + case "text": + $this->_renderers[$type] = new Text($this->_dompdf); + break; + + case "image": + $this->_renderers[$type] = new Image($this->_dompdf); + break; + + case "table-cell": + $this->_renderers[$type] = new TableCell($this->_dompdf); + break; + + case "table-row-group": + $this->_renderers[$type] = new TableRowGroup($this->_dompdf); + break; + + case "list-bullet": + $this->_renderers[$type] = new ListBullet($this->_dompdf); + break; + + case "php": + $this->_renderers[$type] = new PhpEvaluator($this->_canvas); + break; + + case "javascript": + $this->_renderers[$type] = new JavascriptEmbedder($this->_dompdf); + break; + + } + } + + $this->_renderers[$type]->render($frame); + + } +} diff --git a/library/vendor/dompdf/src/Renderer/AbstractRenderer.php b/library/vendor/dompdf/src/Renderer/AbstractRenderer.php new file mode 100644 index 000000000..31b595fd4 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/AbstractRenderer.php @@ -0,0 +1,768 @@ + + * @author Helmut Tischer + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Adapter\CPDF; +use Dompdf\Css\Color; +use Dompdf\Css\Style; +use Dompdf\Dompdf; +use Dompdf\Helpers; +use Dompdf\Frame; +use Dompdf\Image\Cache; + +/** + * Base renderer class + * + * @package dompdf + */ +abstract class AbstractRenderer +{ + + /** + * Rendering backend + * + * @var \Dompdf\Canvas + */ + protected $_canvas; + + /** + * Current dompdf instance + * + * @var Dompdf + */ + protected $_dompdf; + + /** + * Class constructor + * + * @param Dompdf $dompdf The current dompdf instance + */ + function __construct(Dompdf $dompdf) + { + $this->_dompdf = $dompdf; + $this->_canvas = $dompdf->get_canvas(); + } + + /** + * Render a frame. + * + * Specialized in child classes + * + * @param Frame $frame The frame to render + */ + abstract function render(Frame $frame); + + //........................................................................ + + /** + * Render a background image over a rectangular area + * + * @param string $url The background image to load + * @param float $x The left edge of the rectangular area + * @param float $y The top edge of the rectangular area + * @param float $width The width of the rectangular area + * @param float $height The height of the rectangular area + * @param Style $style The associated Style object + * + * @throws \Exception + */ + protected function _background_image($url, $x, $y, $width, $height, $style) + { + if (!function_exists("imagecreatetruecolor")) { + throw new \Exception("The PHP GD extension is required, but is not installed."); + } + + $sheet = $style->get_stylesheet(); + + // Skip degenerate cases + if ($width == 0 || $height == 0) { + return; + } + + $box_width = $width; + $box_height = $height; + + //debugpng + if ($this->_dompdf->get_option("debugPng")) print '[_background_image ' . $url . ']'; + + list($img, $type, /*$msg*/) = Cache::resolve_url( + $url, + $sheet->get_protocol(), + $sheet->get_host(), + $sheet->get_base_path(), + $this->_dompdf + ); + + // Bail if the image is no good + if (Cache::is_broken($img)) { + return; + } + + //Try to optimize away reading and composing of same background multiple times + //Postponing read with imagecreatefrom ...() + //final composition parameters and name not known yet + //Therefore read dimension directly from file, instead of creating gd object first. + //$img_w = imagesx($src); $img_h = imagesy($src); + + list($img_w, $img_h) = Helpers::dompdf_getimagesize($img, $this->_dompdf->getHttpContext()); + if (!isset($img_w) || $img_w == 0 || !isset($img_h) || $img_h == 0) { + return; + } + + $repeat = $style->background_repeat; + $dpi = $this->_dompdf->get_option("dpi"); + + //Increase background resolution and dependent box size according to image resolution to be placed in + //Then image can be copied in without resize + $bg_width = round((float)($width * $dpi) / 72); + $bg_height = round((float)($height * $dpi) / 72); + + //Need %bg_x, $bg_y as background pos, where img starts, converted to pixel + + list($bg_x, $bg_y) = $style->background_position; + + if (Helpers::is_percent($bg_x)) { + // The point $bg_x % from the left edge of the image is placed + // $bg_x % from the left edge of the background rectangle + $p = ((float)$bg_x) / 100.0; + $x1 = $p * $img_w; + $x2 = $p * $bg_width; + + $bg_x = $x2 - $x1; + } else { + $bg_x = (float)($style->length_in_pt($bg_x) * $dpi) / 72; + } + + $bg_x = round($bg_x + $style->length_in_pt($style->border_left_width) * $dpi / 72); + + if (Helpers::is_percent($bg_y)) { + // The point $bg_y % from the left edge of the image is placed + // $bg_y % from the left edge of the background rectangle + $p = ((float)$bg_y) / 100.0; + $y1 = $p * $img_h; + $y2 = $p * $bg_height; + + $bg_y = $y2 - $y1; + } else { + $bg_y = (float)($style->length_in_pt($bg_y) * $dpi) / 72; + } + + $bg_y = round($bg_y + $style->length_in_pt($style->border_top_width) * $dpi / 72); + + //clip background to the image area on partial repeat. Nothing to do if img off area + //On repeat, normalize start position to the tile at immediate left/top or 0/0 of area + //On no repeat with positive offset: move size/start to have offset==0 + //Handle x/y Dimensions separately + + if ($repeat !== "repeat" && $repeat !== "repeat-x") { + //No repeat x + if ($bg_x < 0) { + $bg_width = $img_w + $bg_x; + } else { + $x += ($bg_x * 72) / $dpi; + $bg_width = $bg_width - $bg_x; + if ($bg_width > $img_w) { + $bg_width = $img_w; + } + $bg_x = 0; + } + + if ($bg_width <= 0) { + return; + } + + $width = (float)($bg_width * 72) / $dpi; + } else { + //repeat x + if ($bg_x < 0) { + $bg_x = -((-$bg_x) % $img_w); + } else { + $bg_x = $bg_x % $img_w; + if ($bg_x > 0) { + $bg_x -= $img_w; + } + } + } + + if ($repeat !== "repeat" && $repeat !== "repeat-y") { + //no repeat y + if ($bg_y < 0) { + $bg_height = $img_h + $bg_y; + } else { + $y += ($bg_y * 72) / $dpi; + $bg_height = $bg_height - $bg_y; + if ($bg_height > $img_h) { + $bg_height = $img_h; + } + $bg_y = 0; + } + if ($bg_height <= 0) { + return; + } + $height = (float)($bg_height * 72) / $dpi; + } else { + //repeat y + if ($bg_y < 0) { + $bg_y = -((-$bg_y) % $img_h); + } else { + $bg_y = $bg_y % $img_h; + if ($bg_y > 0) { + $bg_y -= $img_h; + } + } + } + + //Optimization, if repeat has no effect + if ($repeat === "repeat" && $bg_y <= 0 && $img_h + $bg_y >= $bg_height) { + $repeat = "repeat-x"; + } + + if ($repeat === "repeat" && $bg_x <= 0 && $img_w + $bg_x >= $bg_width) { + $repeat = "repeat-y"; + } + + if (($repeat === "repeat-x" && $bg_x <= 0 && $img_w + $bg_x >= $bg_width) || + ($repeat === "repeat-y" && $bg_y <= 0 && $img_h + $bg_y >= $bg_height) + ) { + $repeat = "no-repeat"; + } + + //Use filename as indicator only + //different names for different variants to have different copies in the pdf + //This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color) + //Note: Here, bg_* are the start values, not end values after going through the tile loops! + + $filedummy = $img; + + $is_png = false; + $filedummy .= '_' . $bg_width . '_' . $bg_height . '_' . $bg_x . '_' . $bg_y . '_' . $repeat; + + //Optimization to avoid multiple times rendering the same image. + //If check functions are existing and identical image already cached, + //then skip creation of duplicate, because it is not needed by addImagePng + if ($this->_canvas instanceof CPDF && + $this->_canvas->get_cpdf()->image_iscached($filedummy) + ) { + $bg = null; + } else { + + // Create a new image to fit over the background rectangle + $bg = imagecreatetruecolor($bg_width, $bg_height); + + switch (strtolower($type)) { + case "png": + $is_png = true; + imagesavealpha($bg, true); + imagealphablending($bg, false); + $src = imagecreatefrompng($img); + break; + + case "jpeg": + $src = imagecreatefromjpeg($img); + break; + + case "gif": + $src = imagecreatefromgif($img); + break; + + case "bmp": + $src = Helpers::imagecreatefrombmp($img); + break; + + default: + return; // Unsupported image type + } + + if ($src == null) { + return; + } + + //Background color if box is not relevant here + //Non transparent image: box clipped to real size. Background non relevant. + //Transparent image: The image controls the transparency and lets shine through whatever background. + //However on transparent image preset the composed image with the transparency color, + //to keep the transparency when copying over the non transparent parts of the tiles. + $ti = imagecolortransparent($src); + + if ($ti >= 0) { + $tc = imagecolorsforindex($src, $ti); + $ti = imagecolorallocate($bg, $tc['red'], $tc['green'], $tc['blue']); + imagefill($bg, 0, 0, $ti); + imagecolortransparent($bg, $ti); + } + + //This has only an effect for the non repeatable dimension. + //compute start of src and dest coordinates of the single copy + if ($bg_x < 0) { + $dst_x = 0; + $src_x = -$bg_x; + } else { + $src_x = 0; + $dst_x = $bg_x; + } + + if ($bg_y < 0) { + $dst_y = 0; + $src_y = -$bg_y; + } else { + $src_y = 0; + $dst_y = $bg_y; + } + + //For historical reasons exchange meanings of variables: + //start_* will be the start values, while bg_* will be the temporary start values in the loops + $start_x = $bg_x; + $start_y = $bg_y; + + // Copy regions from the source image to the background + if ($repeat === "no-repeat") { + + // Simply place the image on the background + imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h); + + } else if ($repeat === "repeat-x") { + + for ($bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w) { + if ($bg_x < 0) { + $dst_x = 0; + $src_x = -$bg_x; + $w = $img_w + $bg_x; + } else { + $dst_x = $bg_x; + $src_x = 0; + $w = $img_w; + } + imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h); + } + + } else if ($repeat === "repeat-y") { + + for ($bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h) { + if ($bg_y < 0) { + $dst_y = 0; + $src_y = -$bg_y; + $h = $img_h + $bg_y; + } else { + $dst_y = $bg_y; + $src_y = 0; + $h = $img_h; + } + imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h); + + } + + } else if ($repeat === "repeat") { + + for ($bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h) { + for ($bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w) { + + if ($bg_x < 0) { + $dst_x = 0; + $src_x = -$bg_x; + $w = $img_w + $bg_x; + } else { + $dst_x = $bg_x; + $src_x = 0; + $w = $img_w; + } + + if ($bg_y < 0) { + $dst_y = 0; + $src_y = -$bg_y; + $h = $img_h + $bg_y; + } else { + $dst_y = $bg_y; + $src_y = 0; + $h = $img_h; + } + imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h); + } + } + } else { + print 'Unknown repeat!'; + } + + imagedestroy($src); + + } /* End optimize away creation of duplicates */ + + $this->_canvas->clipping_rectangle($x, $y, $box_width, $box_height); + + //img: image url string + //img_w, img_h: original image size in px + //width, height: box size in pt + //bg_width, bg_height: box size in px + //x, y: left/top edge of box on page in pt + //start_x, start_y: placement of image relative to pattern + //$repeat: repeat mode + //$bg: GD object of result image + //$src: GD object of original image + //When using cpdf and optimization to direct png creation from gd object is available, + //don't create temp file, but place gd object directly into the pdf + if (!$is_png && $this->_canvas instanceof CPDF) { + // Note: CPDF_Adapter image converts y position + $this->_canvas->get_cpdf()->addImagePng($filedummy, $x, $this->_canvas->get_height() - $y - $height, $width, $height, $bg); + } else { + $tmp_dir = $this->_dompdf->get_option("temp_dir"); + $tmp_name = tempnam($tmp_dir, "bg_dompdf_img_"); + @unlink($tmp_name); + $tmp_file = "$tmp_name.png"; + + //debugpng + if ($this->_dompdf->get_option("debugPng")) print '[_background_image ' . $tmp_file . ']'; + + imagepng($bg, $tmp_file); + $this->_canvas->image($tmp_file, $x, $y, $width, $height); + imagedestroy($bg); + + //debugpng + if ($this->_dompdf->get_option("debugPng")) print '[_background_image unlink ' . $tmp_file . ']'; + + if (!$this->_dompdf->get_option("debugKeepTemp")) { + unlink($tmp_file); + } + } + + $this->_canvas->clipping_end(); + } + + protected function _get_dash_pattern($style, $width) + { + $pattern = array(); + + switch ($style) { + default: + /*case "solid": + case "double": + case "groove": + case "inset": + case "outset": + case "ridge":*/ + case "none": + break; + + case "dotted": + if ($width <= 1) + $pattern = array($width, $width * 2); + else + $pattern = array($width); + break; + + case "dashed": + $pattern = array(3 * $width); + break; + } + + return $pattern; + } + + protected function _border_none($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + return; + } + + protected function _border_hidden($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + return; + } + + // Border rendering functions + + protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dotted", $r1, $r2); + } + + + protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dashed", $r1, $r2); + } + + + protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + // TODO: Solve rendering where one corner is beveled (radius == 0), one corner isn't. + if ($corner_style !== "bevel" || $r1 > 0 || $r2 > 0) { + // do it the simple way + $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "solid", $r1, $r2); + return; + } + + list($top, $right, $bottom, $left) = $widths; + + // All this polygon business is for beveled corners... + switch ($side) { + case "top": + $points = array($x, $y, + $x + $length, $y, + $x + $length - $right, $y + $top, + $x + $left, $y + $top); + $this->_canvas->polygon($points, $color, null, null, true); + break; + + case "bottom": + $points = array($x, $y, + $x + $length, $y, + $x + $length - $right, $y - $bottom, + $x + $left, $y - $bottom); + $this->_canvas->polygon($points, $color, null, null, true); + break; + + case "left": + $points = array($x, $y, + $x, $y + $length, + $x + $left, $y + $length - $bottom, + $x + $left, $y + $top); + $this->_canvas->polygon($points, $color, null, null, true); + break; + + case "right": + $points = array($x, $y, + $x, $y + $length, + $x - $right, $y + $length - $bottom, + $x - $right, $y + $top); + $this->_canvas->polygon($points, $color, null, null, true); + break; + + default: + return; + } + } + + protected function _apply_ratio($side, $ratio, $top, $right, $bottom, $left, &$x, &$y, &$length, &$r1, &$r2) + { + switch ($side) { + + case "top": + $r1 -= $left * $ratio; + $r2 -= $right * $ratio; + $x += $left * $ratio; + $y += $top * $ratio; + $length -= $left * $ratio + $right * $ratio; + break; + + case "bottom": + $r1 -= $right * $ratio; + $r2 -= $left * $ratio; + $x += $left * $ratio; + $y -= $bottom * $ratio; + $length -= $left * $ratio + $right * $ratio; + break; + + case "left": + $r1 -= $top * $ratio; + $r2 -= $bottom * $ratio; + $x += $left * $ratio; + $y += $top * $ratio; + $length -= $top * $ratio + $bottom * $ratio; + break; + + case "right": + $r1 -= $bottom * $ratio; + $r2 -= $top * $ratio; + $x -= $right * $ratio; + $y += $top * $ratio; + $length -= $top * $ratio + $bottom * $ratio; + break; + + default: + return; + + } + } + + protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + list($top, $right, $bottom, $left) = $widths; + + $third_widths = array($top / 3, $right / 3, $bottom / 3, $left / 3); + + // draw the outer border + $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2); + + $this->_apply_ratio($side, 2 / 3, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); + + $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2); + } + + protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + list($top, $right, $bottom, $left) = $widths; + + $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2); + + $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); + + $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); + + $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); + + } + + protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + list($top, $right, $bottom, $left) = $widths; + + $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2); + + $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); + + $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2); + + $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2); + + } + + protected function _tint($c) + { + if (!is_numeric($c)) + return $c; + + return min(1, $c + 0.16); + } + + protected function _shade($c) + { + if (!is_numeric($c)) + return $c; + + return max(0, $c - 0.33); + } + + protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + switch ($side) { + case "top": + case "left": + $shade = array_map(array($this, "_shade"), $color); + $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2); + break; + + case "bottom": + case "right": + $tint = array_map(array($this, "_tint"), $color); + $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2); + break; + + default: + return; + } + } + + protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) + { + switch ($side) { + case "top": + case "left": + $tint = array_map(array($this, "_tint"), $color); + $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2); + break; + + case "bottom": + case "right": + $shade = array_map(array($this, "_shade"), $color); + $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2); + break; + + default: + return; + } + } + + // Draws a solid, dotted, or dashed line, observing the border radius + protected function _border_line($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $pattern_name, $r1 = 0, $r2 = 0) + { + list($top, $right, $bottom, $left) = $widths; + + $width = $$side; + $pattern = $this->_get_dash_pattern($pattern_name, $width); + + $half_width = $width / 2; + $r1 -= $half_width; + $r2 -= $half_width; + $adjust = $r1 / 80; + $length -= $width; + + switch ($side) { + case "top": + $x += $half_width; + $y += $half_width; + + if ($r1 > 0) { + $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 90 - $adjust, 135 + $adjust, $color, $width, $pattern); + } + + $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern); + + if ($r2 > 0) { + $this->_canvas->arc($x + $length - $r2, $y + $r2, $r2, $r2, 45 - $adjust, 90 + $adjust, $color, $width, $pattern); + } + break; + + case "bottom": + $x += $half_width; + $y -= $half_width; + + if ($r1 > 0) { + $this->_canvas->arc($x + $r1, $y - $r1, $r1, $r1, 225 - $adjust, 270 + $adjust, $color, $width, $pattern); + } + + $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern); + + if ($r2 > 0) { + $this->_canvas->arc($x + $length - $r2, $y - $r2, $r2, $r2, 270 - $adjust, 315 + $adjust, $color, $width, $pattern); + } + break; + + case "left": + $y += $half_width; + $x += $half_width; + + if ($r1 > 0) { + $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 135 - $adjust, 180 + $adjust, $color, $width, $pattern); + } + + $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern); + + if ($r2 > 0) { + $this->_canvas->arc($x + $r2, $y + $length - $r2, $r2, $r2, 180 - $adjust, 225 + $adjust, $color, $width, $pattern); + } + break; + + case "right": + $y += $half_width; + $x -= $half_width; + + if ($r1 > 0) { + $this->_canvas->arc($x - $r1, $y + $r1, $r1, $r1, 0 - $adjust, 45 + $adjust, $color, $width, $pattern); + } + + $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern); + + if ($r2 > 0) { + $this->_canvas->arc($x - $r2, $y + $length - $r2, $r2, $r2, 315 - $adjust, 360 + $adjust, $color, $width, $pattern); + } + break; + } + } + + protected function _set_opacity($opacity) + { + if (is_numeric($opacity) && $opacity <= 1.0 && $opacity >= 0.0) { + $this->_canvas->set_opacity($opacity); + } + } + + protected function _debug_layout($box, $color = "red", $style = array()) + { + $this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], Color::parse($color), 0.1, $style); + } +} diff --git a/library/vendor/dompdf/src/Renderer/Block.php b/library/vendor/dompdf/src/Renderer/Block.php new file mode 100644 index 000000000..d16212307 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/Block.php @@ -0,0 +1,240 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Frame; +use Dompdf\FrameDecorator\AbstractFrameDecorator; +use Dompdf\Helpers; + +/** + * Renders block frames + * + * @package dompdf + */ +class Block extends AbstractRenderer +{ + + //........................................................................ + + function render(Frame $frame) + { + $style = $frame->get_style(); + $node = $frame->get_node(); + + list($x, $y, $w, $h) = $frame->get_border_box(); + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + if ($node->nodeName === "body") { + $h = $frame->get_containing_block("h") - $style->length_in_pt(array( + $style->margin_top, + $style->border_top_width, + $style->border_bottom_width, + $style->margin_bottom), + $style->width); + } + + // Handle anchors & links + if ($node->nodeName === "a" && $href = $node->getAttribute("href")) { + $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href); + $this->_canvas->add_link($href, $x, $y, $w, $h); + } + + // Draw our background, border and content + list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); + + if ($tl + $tr + $br + $bl > 0) { + $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); + } + + if (($bg = $style->background_color) !== "transparent") { + $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg); + } + + if (($url = $style->background_image) && $url !== "none") { + $this->_background_image($url, $x, $y, $w, $h, $style); + } + + if ($tl + $tr + $br + $bl > 0) { + $this->_canvas->clipping_end(); + } + + $border_box = array($x, $y, $w, $h); + $this->_render_border($frame, $border_box); + $this->_render_outline($frame, $border_box); + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutBlocks")) { + $this->_debug_layout($frame->get_border_box(), "red"); + if ($this->_dompdf->get_option("debugLayoutPaddingBox")) { + $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5)); + } + } + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutLines") && $frame->get_decorator()) { + foreach ($frame->get_decorator()->get_line_boxes() as $line) { + $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange"); + } + } + } + + protected function _render_border(AbstractFrameDecorator $frame, $border_box = null, $corner_style = "bevel") + { + $style = $frame->get_style(); + $bp = $style->get_border_properties(); + + if (empty($border_box)) { + $border_box = $frame->get_border_box(); + } + + // find the radius + $radius = $style->get_computed_border_radius($border_box[2], $border_box[3]); // w, h + + // Short-cut: If all the borders are "solid" with the same color and style, and no radius, we'd better draw a rectangle + if ( + in_array($bp["top"]["style"], array("solid", "dashed", "dotted")) && + $bp["top"] == $bp["right"] && + $bp["right"] == $bp["bottom"] && + $bp["bottom"] == $bp["left"] && + array_sum($radius) == 0 + ) { + $props = $bp["top"]; + if ($props["color"] === "transparent" || $props["width"] <= 0) return; + + list($x, $y, $w, $h) = $border_box; + $width = $style->length_in_pt($props["width"]); + $pattern = $this->_get_dash_pattern($props["style"], $width); + $this->_canvas->rectangle($x + $width / 2, $y + $width / 2, $w - $width, $h - $width, $props["color"], $width, $pattern); + return; + } + + // Do it the long way + $widths = array($style->length_in_pt($bp["top"]["width"]), + $style->length_in_pt($bp["right"]["width"]), + $style->length_in_pt($bp["bottom"]["width"]), + $style->length_in_pt($bp["left"]["width"])); + + foreach ($bp as $side => $props) { + list($x, $y, $w, $h) = $border_box; + $length = 0; + $r1 = 0; + $r2 = 0; + + if (!$props["style"] || + $props["style"] === "none" || + $props["width"] <= 0 || + $props["color"] == "transparent" + ) + continue; + + switch ($side) { + case "top": + $length = $w; + $r1 = $radius["top-left"]; + $r2 = $radius["top-right"]; + break; + + case "bottom": + $length = $w; + $y += $h; + $r1 = $radius["bottom-left"]; + $r2 = $radius["bottom-right"]; + break; + + case "left": + $length = $h; + $r1 = $radius["top-left"]; + $r2 = $radius["bottom-left"]; + break; + + case "right": + $length = $h; + $x += $w; + $r1 = $radius["top-right"]; + $r2 = $radius["bottom-right"]; + break; + default: + break; + } + $method = "_border_" . $props["style"]; + + // draw rounded corners + $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style, $r1, $r2); + } + } + + protected function _render_outline(AbstractFrameDecorator $frame, $border_box = null, $corner_style = "bevel") + { + $style = $frame->get_style(); + + $props = array( + "width" => $style->outline_width, + "style" => $style->outline_style, + "color" => $style->outline_color, + ); + + if (!$props["style"] || $props["style"] === "none" || $props["width"] <= 0) + return; + + if (empty($border_box)) { + $border_box = $frame->get_border_box(); + } + + $offset = $style->length_in_pt($props["width"]); + $pattern = $this->_get_dash_pattern($props["style"], $offset); + + // If the outline style is "solid" we'd better draw a rectangle + if (in_array($props["style"], array("solid", "dashed", "dotted"))) { + $border_box[0] -= $offset / 2; + $border_box[1] -= $offset / 2; + $border_box[2] += $offset; + $border_box[3] += $offset; + + list($x, $y, $w, $h) = $border_box; + $this->_canvas->rectangle($x, $y, $w, $h, $props["color"], $offset, $pattern); + return; + } + + $border_box[0] -= $offset; + $border_box[1] -= $offset; + $border_box[2] += $offset * 2; + $border_box[3] += $offset * 2; + + $method = "_border_" . $props["style"]; + $widths = array_fill(0, 4, $props["width"]); + $sides = array("top", "right", "left", "bottom"); + $length = 0; + + foreach ($sides as $side) { + list($x, $y, $w, $h) = $border_box; + + switch ($side) { + case "top": + $length = $w; + break; + + case "bottom": + $length = $w; + $y += $h; + break; + + case "left": + $length = $h; + break; + + case "right": + $length = $h; + $x += $w; + break; + default: + break; + } + + $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style); + } + } +} diff --git a/library/vendor/dompdf/src/Renderer/Image.php b/library/vendor/dompdf/src/Renderer/Image.php new file mode 100644 index 000000000..1630794a4 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/Image.php @@ -0,0 +1,125 @@ + + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Frame; +use Dompdf\Image\Cache; + +/** + * Image renderer + * + * @access private + * @package dompdf + */ +class Image extends Block +{ + + function render(Frame $frame) + { + // Render background & borders + $style = $frame->get_style(); + $cb = $frame->get_containing_block(); + list($x, $y, $w, $h) = $frame->get_border_box(); + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h); + + $has_border_radius = $tl + $tr + $br + $bl > 0; + + if ($has_border_radius) { + $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); + } + + if (($bg = $style->background_color) !== "transparent") { + $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg); + } + + if (($url = $style->background_image) && $url !== "none") { + $this->_background_image($url, $x, $y, $w, $h, $style); + } + + if ($has_border_radius) { + $this->_canvas->clipping_end(); + } + + $this->_render_border($frame); + $this->_render_outline($frame); + + list($x, $y) = $frame->get_padding_box(); + + $x += $style->length_in_pt($style->padding_left, $cb["w"]); + $y += $style->length_in_pt($style->padding_top, $cb["h"]); + + $w = $style->length_in_pt($style->width, $cb["w"]); + $h = $style->length_in_pt($style->height, $cb["h"]); + + if ($has_border_radius) { + list($wt, $wr, $wb, $wl) = array( + $style->border_top_width, + $style->border_right_width, + $style->border_bottom_width, + $style->border_left_width, + ); + + // we have to get the "inner" radius + if ($tl > 0) { + $tl -= ($wt + $wl) / 2; + } + if ($tr > 0) { + $tr -= ($wt + $wr) / 2; + } + if ($br > 0) { + $br -= ($wb + $wr) / 2; + } + if ($bl > 0) { + $bl -= ($wb + $wl) / 2; + } + + $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl); + } + + $src = $frame->get_image_url(); + $alt = null; + + if (Cache::is_broken($src) && + $alt = $frame->get_node()->getAttribute("alt") + ) { + $font = $style->font_family; + $size = $style->font_size; + $spacing = $style->word_spacing; + $this->_canvas->text($x, $y, $alt, + $font, $size, + $style->color, $spacing); + } else { + $this->_canvas->image($src, $x, $y, $w, $h, $style->image_resolution); + } + + if ($has_border_radius) { + $this->_canvas->clipping_end(); + } + + if ($msg = $frame->get_image_msg()) { + $parts = preg_split("/\s*\n\s*/", $msg); + $height = 10; + $_y = $alt ? $y + $h - count($parts) * $height : $y; + + foreach ($parts as $i => $_part) { + $this->_canvas->text($x, $_y + $i * $height, $_part, "times", $height * 0.8, array(0.5, 0.5, 0.5)); + } + } + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutBlocks")) { + $this->_debug_layout($frame->get_border_box(), "blue"); + if ($this->_dompdf->get_option("debugLayoutPaddingBox")) { + $this->_debug_layout($frame->get_padding_box(), "blue", array(0.5, 0.5)); + } + } + } +} diff --git a/library/vendor/dompdf/src/Renderer/Inline.php b/library/vendor/dompdf/src/Renderer/Inline.php new file mode 100644 index 000000000..a7c8fd107 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/Inline.php @@ -0,0 +1,198 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Frame; +use Dompdf\Helpers; + +/** + * Renders inline frames + * + * @access private + * @package dompdf + */ +class Inline extends AbstractRenderer +{ + + //........................................................................ + + function render(Frame $frame) + { + $style = $frame->get_style(); + + if (!$frame->get_first_child()) + return; // No children, no service + + // Draw the left border if applicable + $bp = $style->get_border_properties(); + $widths = array($style->length_in_pt($bp["top"]["width"]), + $style->length_in_pt($bp["right"]["width"]), + $style->length_in_pt($bp["bottom"]["width"]), + $style->length_in_pt($bp["left"]["width"])); + + // Draw the background & border behind each child. To do this we need + // to figure out just how much space each child takes: + list($x, $y) = $frame->get_first_child()->get_position(); + $w = null; + $h = 0; +// $x += $widths[3]; +// $y += $widths[0]; + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + $first_row = true; + + foreach ($frame->get_children() as $child) { + list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box(); + + if (!is_null($w) && $child_x < $x + $w) { + //This branch seems to be supposed to being called on the first part + //of an inline html element, and the part after the if clause for the + //parts after a line break. + //But because $w initially mostly is 0, and gets updated only on the next + //round, this seem to be never executed and the common close always. + + // The next child is on another line. Draw the background & + // borders on this line. + + // Background: + if (($bg = $style->background_color) !== "transparent") + $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg); + + if (($url = $style->background_image) && $url !== "none") { + $this->_background_image($url, $x, $y, $w, $h, $style); + } + + // If this is the first row, draw the left border + if ($first_row) { + + if ($bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $bp["left"]["width"] > 0) { + $method = "_border_" . $bp["left"]["style"]; + $this->$method($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left"); + } + $first_row = false; + } + + // Draw the top & bottom borders + if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $bp["top"]["width"] > 0) { + $method = "_border_" . $bp["top"]["style"]; + $this->$method($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top"); + } + + if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $bp["bottom"]["width"] > 0) { + $method = "_border_" . $bp["bottom"]["style"]; + $this->$method($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom"); + } + + // Handle anchors & links + $link_node = null; + if ($frame->get_node()->nodeName === "a") { + $link_node = $frame->get_node(); + } else if ($frame->get_parent()->get_node()->nodeName === "a") { + $link_node = $frame->get_parent()->get_node(); + } + + if ($link_node && $href = $link_node->getAttribute("href")) { + $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href); + $this->_canvas->add_link($href, $x, $y, $w, $h); + } + + $x = $child_x; + $y = $child_y; + $w = $child_w; + $h = $child_h; + continue; + } + + if (is_null($w)) + $w = $child_w; + else + $w += $child_w; + + $h = max($h, $child_h); + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutInline")) { + $this->_debug_layout($child->get_border_box(), "blue"); + if ($this->_dompdf->get_option("debugLayoutPaddingBox")) { + $this->_debug_layout($child->get_padding_box(), "blue", array(0.5, 0.5)); + } + } + } + + + // Handle the last child + if (($bg = $style->background_color) !== "transparent") + $this->_canvas->filled_rectangle($x + $widths[3], $y + $widths[0], $w, $h, $bg); + + //On continuation lines (after line break) of inline elements, the style got copied. + //But a non repeatable background image should not be repeated on the next line. + //But removing the background image above has never an effect, and removing it below + //removes it always, even on the initial line. + //Need to handle it elsewhere, e.g. on certain ...clone()... usages. + // Repeat not given: default is Style::__construct + // ... && (!($repeat = $style->background_repeat) || $repeat === "repeat" ... + //different position? $this->_background_image($url, $x, $y, $w, $h, $style); + if (($url = $style->background_image) && $url !== "none") + $this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style); + + // Add the border widths + $w += $widths[1] + $widths[3]; + $h += $widths[0] + $widths[2]; + + // make sure the border and background start inside the left margin + $left_margin = $style->length_in_pt($style->margin_left); + $x += $left_margin; + + // If this is the first row, draw the left border too + if ($first_row && $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $widths[3] > 0) { + $method = "_border_" . $bp["left"]["style"]; + $this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left"); + } + + // Draw the top & bottom borders + if ($bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $widths[0] > 0) { + $method = "_border_" . $bp["top"]["style"]; + $this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top"); + } + + if ($bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $widths[2] > 0) { + $method = "_border_" . $bp["bottom"]["style"]; + $this->$method($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom"); + } + + // Helpers::var_dump(get_class($frame->get_next_sibling())); + // $last_row = get_class($frame->get_next_sibling()) !== 'Inline'; + // Draw the right border if this is the last row + if ($bp["right"]["style"] !== "none" && $bp["right"]["color"] !== "transparent" && $widths[1] > 0) { + $method = "_border_" . $bp["right"]["style"]; + $this->$method($x + $w, $y, $h, $bp["right"]["color"], $widths, "right"); + } + + // Only two levels of links frames + $link_node = null; + if ($frame->get_node()->nodeName === "a") { + $link_node = $frame->get_node(); + + if (($name = $link_node->getAttribute("name")) || ($name = $link_node->getAttribute("id"))) { + $this->_canvas->add_named_dest($name); + } + } + + if ($frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a") { + $link_node = $frame->get_parent()->get_node(); + } + + // Handle anchors & links + if ($link_node) { + if ($href = $link_node->getAttribute("href")) { + $href = Helpers::build_url($this->_dompdf->getProtocol(), $this->_dompdf->getBaseHost(), $this->_dompdf->getBasePath(), $href); + $this->_canvas->add_link($href, $x, $y, $w, $h); + } + } + } +} diff --git a/library/vendor/dompdf/src/Renderer/ListBullet.php b/library/vendor/dompdf/src/Renderer/ListBullet.php new file mode 100644 index 000000000..c7fcc89b5 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/ListBullet.php @@ -0,0 +1,248 @@ + + * @author Helmut Tischer + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Helpers; +use Dompdf\FontMetrics; +use Dompdf\Frame; +use Dompdf\Image\Cache; +use Dompdf\FrameDecorator\ListBullet as ListBulletFrameDecorator; + +/** + * Renders list bullets + * + * @access private + * @package dompdf + */ +class ListBullet extends AbstractRenderer +{ + static function get_counter_chars($type) + { + static $cache = array(); + + if (isset($cache[$type])) { + return $cache[$type]; + } + + $uppercase = false; + $text = ""; + + switch ($type) { + case "decimal-leading-zero": + case "decimal": + case "1": + return "0123456789"; + + case "upper-alpha": + case "upper-latin": + case "A": + $uppercase = true; + case "lower-alpha": + case "lower-latin": + case "a": + $text = "abcdefghijklmnopqrstuvwxyz"; + break; + + case "upper-roman": + case "I": + $uppercase = true; + case "lower-roman": + case "i": + $text = "ivxlcdm"; + break; + + case "lower-greek": + for ($i = 0; $i < 24; $i++) { + $text .= Helpers::unichr($i + 944); + } + break; + } + + if ($uppercase) { + $text = strtoupper($text); + } + + return $cache[$type] = "$text."; + } + + /** + * @param integer $n + * @param string $type + * @param integer $pad + * + * @return string + */ + private function make_counter($n, $type, $pad = null) + { + $n = intval($n); + $text = ""; + $uppercase = false; + + switch ($type) { + case "decimal-leading-zero": + case "decimal": + case "1": + if ($pad) + $text = str_pad($n, $pad, "0", STR_PAD_LEFT); + else + $text = $n; + break; + + case "upper-alpha": + case "upper-latin": + case "A": + $uppercase = true; + case "lower-alpha": + case "lower-latin": + case "a": + $text = chr(($n % 26) + ord('a') - 1); + break; + + case "upper-roman": + case "I": + $uppercase = true; + case "lower-roman": + case "i": + $text = Helpers::dec2roman($n); + break; + + case "lower-greek": + $text = Helpers::unichr($n + 944); + break; + } + + if ($uppercase) { + $text = strtoupper($text); + } + + return "$text."; + } + + function render(Frame $frame) + { + $style = $frame->get_style(); + $font_size = $style->get_font_size(); + $line_height = $style->length_in_pt($style->line_height, $frame->get_containing_block("w")); + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + $li = $frame->get_parent(); + + // Don't render bullets twice if if was split + if ($li->_splitted) { + return; + } + + // Handle list-style-image + // If list style image is requested but missing, fall back to predefined types + if ($style->list_style_image !== "none" && + !Cache::is_broken($img = $frame->get_image_url()) + ) { + + list($x, $y) = $frame->get_position(); + + //For expected size and aspect, instead of box size, use image natural size scaled to DPI. + // Resample the bullet image to be consistent with 'auto' sized images + // See also Image::get_min_max_width + // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary. + //$w = $frame->get_width(); + //$h = $frame->get_height(); + list($width, $height) = Helpers::dompdf_getimagesize($img, $this->_dompdf->getHttpContext()); + $dpi = $this->_dompdf->get_option("dpi"); + $w = ((float)rtrim($width, "px") * 72) / $dpi; + $h = ((float)rtrim($height, "px") * 72) / $dpi; + + $x -= $w; + $y -= ($line_height - $font_size) / 2; //Reverse hinting of list_bullet_positioner + + $this->_canvas->image($img, $x, $y, $w, $h); + + } else { + + $bullet_style = $style->list_style_type; + + $fill = false; + + switch ($bullet_style) { + + default: + case "disc": + $fill = true; + + case "circle": + list($x, $y) = $frame->get_position(); + $r = ($font_size * (ListBulletFrameDecorator::BULLET_SIZE /*-ListBulletFrameDecorator::BULLET_THICKNESS*/)) / 2; + $x -= $font_size * (ListBulletFrameDecorator::BULLET_SIZE / 2); + $y += ($font_size * (1 - ListBulletFrameDecorator::BULLET_DESCENT)) / 2; + $o = $font_size * ListBulletFrameDecorator::BULLET_THICKNESS; + $this->_canvas->circle($x, $y, $r, $style->color, $o, null, $fill); + break; + + case "square": + list($x, $y) = $frame->get_position(); + $w = $font_size * ListBulletFrameDecorator::BULLET_SIZE; + $x -= $w; + $y += ($font_size * (1 - ListBulletFrameDecorator::BULLET_DESCENT - ListBulletFrameDecorator::BULLET_SIZE)) / 2; + $this->_canvas->filled_rectangle($x, $y, $w, $w, $style->color); + break; + + case "decimal-leading-zero": + case "decimal": + case "lower-alpha": + case "lower-latin": + case "lower-roman": + case "lower-greek": + case "upper-alpha": + case "upper-latin": + case "upper-roman": + case "1": // HTML 4.0 compatibility + case "a": + case "i": + case "A": + case "I": + $pad = null; + if ($bullet_style === "decimal-leading-zero") { + $pad = strlen($li->get_parent()->get_node()->getAttribute("dompdf-children-count")); + } + + $node = $frame->get_node(); + + if (!$node->hasAttribute("dompdf-counter")) { + return; + } + + $index = $node->getAttribute("dompdf-counter"); + $text = $this->make_counter($index, $bullet_style, $pad); + + if (trim($text) == "") { + return; + } + + $spacing = 0; + $font_family = $style->font_family; + + $line = $li->get_containing_line(); + list($x, $y) = array($frame->get_position("x"), $line->y); + + $x -= $this->_dompdf->getFontMetrics()->getTextWidth($text, $font_family, $font_size, $spacing); + + // Take line-height into account + $line_height = $style->line_height; + $y += ($line_height - $font_size) / 4; // FIXME I thought it should be 2, but 4 gives better results + + $this->_canvas->text($x, $y, $text, + $font_family, $font_size, + $style->color, $spacing); + + case "none": + break; + } + } + } +} diff --git a/library/vendor/dompdf/src/Renderer/TableCell.php b/library/vendor/dompdf/src/Renderer/TableCell.php new file mode 100644 index 000000000..541b9cd7c --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/TableCell.php @@ -0,0 +1,160 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Frame; +use Dompdf\FrameDecorator\Table; + +/** + * Renders table cells + * + * @package dompdf + */ +class TableCell extends Block +{ + + //........................................................................ + + function render(Frame $frame) + { + $style = $frame->get_style(); + + if (trim($frame->get_node()->nodeValue) === "" && $style->empty_cells === "hide") { + return; + } + + $this->_set_opacity($frame->get_opacity($style->opacity)); + list($x, $y, $w, $h) = $frame->get_border_box(); + + // Draw our background, border and content + if (($bg = $style->background_color) !== "transparent") { + $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg); + } + + if (($url = $style->background_image) && $url !== "none") { + $this->_background_image($url, $x, $y, $w, $h, $style); + } + + $table = Table::find_parent_table($frame); + + if ($table->get_style()->border_collapse !== "collapse") { + $this->_render_border($frame); + $this->_render_outline($frame); + return; + } + + // The collapsed case is slightly complicated... + // @todo Add support for outlines here + + $cellmap = $table->get_cellmap(); + $cells = $cellmap->get_spanned_cells($frame); + $num_rows = $cellmap->get_num_rows(); + $num_cols = $cellmap->get_num_cols(); + + // Determine the top row spanned by this cell + $i = $cells["rows"][0]; + $top_row = $cellmap->get_row($i); + + // Determine if this cell borders on the bottom of the table. If so, + // then we draw its bottom border. Otherwise the next row down will + // draw its top border instead. + if (in_array($num_rows - 1, $cells["rows"])) { + $draw_bottom = true; + $bottom_row = $cellmap->get_row($num_rows - 1); + } else + $draw_bottom = false; + + + // Draw the horizontal borders + foreach ($cells["columns"] as $j) { + $bp = $cellmap->get_border_properties($i, $j); + + $y = $top_row["y"] - $bp["top"]["width"] / 2; + + $col = $cellmap->get_column($j); + $x = $col["x"] - $bp["left"]["width"] / 2; + $w = $col["used-width"] + ($bp["left"]["width"] + $bp["right"]["width"]) / 2; + + if ($bp["top"]["style"] !== "none" && $bp["top"]["width"] > 0) { + $widths = array($bp["top"]["width"], + $bp["right"]["width"], + $bp["bottom"]["width"], + $bp["left"]["width"]); + $method = "_border_" . $bp["top"]["style"]; + $this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top", "square"); + } + + if ($draw_bottom) { + $bp = $cellmap->get_border_properties($num_rows - 1, $j); + if ($bp["bottom"]["style"] === "none" || $bp["bottom"]["width"] <= 0) + continue; + + $y = $bottom_row["y"] + $bottom_row["height"] + $bp["bottom"]["width"] / 2; + + $widths = array($bp["top"]["width"], + $bp["right"]["width"], + $bp["bottom"]["width"], + $bp["left"]["width"]); + $method = "_border_" . $bp["bottom"]["style"]; + $this->$method($x, $y, $w, $bp["bottom"]["color"], $widths, "bottom", "square"); + + } + } + + $j = $cells["columns"][0]; + + $left_col = $cellmap->get_column($j); + + if (in_array($num_cols - 1, $cells["columns"])) { + $draw_right = true; + $right_col = $cellmap->get_column($num_cols - 1); + } else + $draw_right = false; + + // Draw the vertical borders + foreach ($cells["rows"] as $i) { + $bp = $cellmap->get_border_properties($i, $j); + + $x = $left_col["x"] - $bp["left"]["width"] / 2; + + $row = $cellmap->get_row($i); + + $y = $row["y"] - $bp["top"]["width"] / 2; + $h = $row["height"] + ($bp["top"]["width"] + $bp["bottom"]["width"]) / 2; + + if ($bp["left"]["style"] !== "none" && $bp["left"]["width"] > 0) { + + $widths = array($bp["top"]["width"], + $bp["right"]["width"], + $bp["bottom"]["width"], + $bp["left"]["width"]); + + $method = "_border_" . $bp["left"]["style"]; + $this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left", "square"); + } + + if ($draw_right) { + $bp = $cellmap->get_border_properties($i, $num_cols - 1); + if ($bp["right"]["style"] === "none" || $bp["right"]["width"] <= 0) + continue; + + $x = $right_col["x"] + $right_col["used-width"] + $bp["right"]["width"] / 2; + + $widths = array($bp["top"]["width"], + $bp["right"]["width"], + $bp["bottom"]["width"], + $bp["left"]["width"]); + + $method = "_border_" . $bp["right"]["style"]; + $this->$method($x, $y, $h, $bp["right"]["color"], $widths, "right", "square"); + + } + } + + } +} diff --git a/library/vendor/dompdf/src/Renderer/TableRowGroup.php b/library/vendor/dompdf/src/Renderer/TableRowGroup.php new file mode 100644 index 000000000..9b29babfd --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/TableRowGroup.php @@ -0,0 +1,44 @@ + + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Frame; + +/** + * Renders block frames + * + * @package dompdf + */ +class TableRowGroup extends Block +{ + + //........................................................................ + + function render(Frame $frame) + { + $style = $frame->get_style(); + + $this->_set_opacity($frame->get_opacity($style->opacity)); + + $this->_render_border($frame); + $this->_render_outline($frame); + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutBlocks")) { + $this->_debug_layout($frame->get_border_box(), "red"); + if ($this->_dompdf->get_option("debugLayoutPaddingBox")) { + $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5)); + } + } + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutLines") && $frame->get_decorator()) { + foreach ($frame->get_decorator()->get_line_boxes() as $line) { + $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange"); + } + } + } +} diff --git a/library/vendor/dompdf/src/Renderer/Text.php b/library/vendor/dompdf/src/Renderer/Text.php new file mode 100644 index 000000000..3072a1792 --- /dev/null +++ b/library/vendor/dompdf/src/Renderer/Text.php @@ -0,0 +1,158 @@ + + * @author Helmut Tischer + * @author Fabien Ménager + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + */ +namespace Dompdf\Renderer; + +use Dompdf\Adapter\CPDF; +use Dompdf\FontMetrics; +use Dompdf\Frame; + +/** + * Renders text frames + * + * @package dompdf + */ +class Text extends AbstractRenderer +{ + + const DECO_THICKNESS = 0.02; // Thickness of underline. Screen: 0.08, print: better less, e.g. 0.04 + + //Tweaking if $base and $descent are not accurate. + //Check method_exists( $this->_canvas, "get_cpdf" ) + //- For cpdf these can and must stay 0, because font metrics are used directly. + //- For other renderers, if different values are wanted, separate the parameter sets. + // But $size and $size-$height seem to be accurate enough + const UNDERLINE_OFFSET = 0.0; // Relative to bottom of text, as fraction of height. + const OVERLINE_OFFSET = 0.0; // Relative to top of text + const LINETHROUGH_OFFSET = 0.0; // Relative to centre of text. + const DECO_EXTENSION = 0.0; // How far to extend lines past either end, in pt + + //........................................................................ + + /** + * @param \Dompdf\FrameDecorator\Text $frame + */ + function render(Frame $frame) + { + $text = $frame->get_text(); + if (trim($text) === "") + return; + + $style = $frame->get_style(); + list($x, $y) = $frame->get_position(); + $cb = $frame->get_containing_block(); + + if (($ml = $style->margin_left) === "auto" || $ml === "none") + $ml = 0; + + if (($pl = $style->padding_left) === "auto" || $pl === "none") + $pl = 0; + + if (($bl = $style->border_left_width) === "auto" || $bl === "none") + $bl = 0; + + $x += $style->length_in_pt(array($ml, $pl, $bl), $cb["w"]); + + $font = $style->font_family; + $size = $frame_font_size = $style->font_size; + $height = $style->height; + $word_spacing = $frame->get_text_spacing() + $style->length_in_pt($style->word_spacing); + $char_spacing = $style->length_in_pt($style->letter_spacing); + $width = $style->width; + + /*$text = str_replace( + array("{PAGE_NUM}"), + array($this->_canvas->get_page_number()), + $text + );*/ + + $this->_canvas->text($x, $y, $text, + $font, $size, + $style->color, $word_spacing, $char_spacing); + + $line = $frame->get_containing_line(); + + // FIXME Instead of using the tallest frame to position, + // the decoration, the text should be well placed + if (false && $line->tallest_frame) { + $base_frame = $line->tallest_frame; + $style = $base_frame->get_style(); + $size = $style->font_size; + $height = $line->h * ($size / $style->line_height); + } + + $line_thickness = $size * self::DECO_THICKNESS; + $underline_offset = $size * self::UNDERLINE_OFFSET; + $overline_offset = $size * self::OVERLINE_OFFSET; + $linethrough_offset = $size * self::LINETHROUGH_OFFSET; + $underline_position = -0.08; + + if ($this->_canvas instanceof CPDF) { + $cpdf_font = $this->_canvas->get_cpdf()->fonts[$style->font_family]; + + if (isset($cpdf_font["UnderlinePosition"])) { + $underline_position = $cpdf_font["UnderlinePosition"] / 1000; + } + + if (isset($cpdf_font["UnderlineThickness"])) { + $line_thickness = $size * ($cpdf_font["UnderlineThickness"] / 1000); + } + } + + $descent = $size * $underline_position; + $base = $size; + + // Handle text decoration: + // http://www.w3.org/TR/CSS21/text.html#propdef-text-decoration + + // Draw all applicable text-decorations. Start with the root and work our way down. + $p = $frame; + $stack = array(); + while ($p = $p->get_parent()) + $stack[] = $p; + + while (isset($stack[0])) { + $f = array_pop($stack); + + if (($text_deco = $f->get_style()->text_decoration) === "none") + continue; + + $deco_y = $y; //$line->y; + $color = $f->get_style()->color; + + switch ($text_deco) { + + default: + continue; + + case "underline": + $deco_y += $base - $descent + $underline_offset + $line_thickness / 2; + break; + + case "overline": + $deco_y += $overline_offset + $line_thickness / 2; + break; + + case "line-through": + $deco_y += $base * 0.7 + $linethrough_offset; + break; + } + + $dx = 0; + $x1 = $x - self::DECO_EXTENSION; + $x2 = $x + $width + $dx + self::DECO_EXTENSION; + $this->_canvas->line($x1, $deco_y, $x2, $deco_y, $color, $line_thickness); + } + + if ($this->_dompdf->get_option("debugLayout") && $this->_dompdf->get_option("debugLayoutLines")) { + $text_width = $this->_dompdf->getFontMetrics()->getTextWidth($text, $font, $frame_font_size); + $this->_debug_layout(array($x, $y, $text_width + ($line->wc - 1) * $word_spacing, $frame_font_size), "orange", array(0.5, 0.5)); + } + } +} From da9e90b1a90ff1a0d524481bded34a96dc140235 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 10 Nov 2016 15:00:47 +0100 Subject: [PATCH 234/337] Don't use border-style: auto because its not supported by dompdf fixes #12776 --- public/css/icinga/base.less | 5 ++--- public/css/icinga/menu.less | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/public/css/icinga/base.less b/public/css/icinga/base.less index 2491dad7a..b2404a873 100644 --- a/public/css/icinga/base.less +++ b/public/css/icinga/base.less @@ -88,9 +88,8 @@ a { a:focus, button:focus, input[type="checkbox"]:focus { - outline-color: @icinga-blue; - outline-style: solid; // IE - outline-style: auto; + outline: 3px solid fade(@icinga-blue, 50%); + outline-offset: 1px; } // Default margin for block text diff --git a/public/css/icinga/menu.less b/public/css/icinga/menu.less index 6da93bb8d..944461cd2 100644 --- a/public/css/icinga/menu.less +++ b/public/css/icinga/menu.less @@ -17,7 +17,7 @@ #menu a { &:focus { color: @icinga-blue; - outline-offset: -0.25em; + outline-offset: -3px; } &:hover { From 2e13754ba1afc034e2bb25ec0e9d54f8e9c29e4e Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 10 Nov 2016 15:01:55 +0100 Subject: [PATCH 235/337] Hide controls in PDF exports --- public/css/pdf/pdfprint.less | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/public/css/pdf/pdfprint.less b/public/css/pdf/pdfprint.less index 39c3985b9..edfb0e516 100644 --- a/public/css/pdf/pdfprint.less +++ b/public/css/pdf/pdfprint.less @@ -1,9 +1,6 @@ /*! Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ -.controls form, -.controls .pagination, -.controls .widgetLimiter, -.controls > .tabs, +.controls, .dontprint, // Compat only, use dont-print instead .dont-print { display: none !important; From 403e8d691278cfa57d770894b86863bbd6fa6214 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 10 Nov 2016 16:04:49 +0100 Subject: [PATCH 236/337] Update HTMLPurifier to version 4.8.0 and let the Web 2 rpm depend on it --- icingaweb2.spec | 4 +- library/vendor/HTMLPurifier.php | 8 +-- .../vendor/HTMLPurifier/AttrCollections.php | 5 ++ library/vendor/HTMLPurifier/AttrDef/CSS.php | 9 +++- .../vendor/HTMLPurifier/AttrDef/CSS/URI.php | 3 ++ .../vendor/HTMLPurifier/AttrDef/HTML/ID.php | 32 +++++++----- .../vendor/HTMLPurifier/AttrDef/URI/Host.php | 23 ++++++--- .../AttrTransform/ImgRequired.php | 3 +- .../AttrTransform/TargetNoreferrer.php | 37 ++++++++++++++ library/vendor/HTMLPurifier/CSSDefinition.php | 13 +++++ library/vendor/HTMLPurifier/ChildDef/List.php | 6 +++ library/vendor/HTMLPurifier/Config.php | 2 +- .../HTMLPurifier/ConfigSchema/schema.ser | Bin 15305 -> 15598 bytes .../ConfigSchema/schema/Attr.ID.HTML5.txt | 10 ++++ .../schema/CSS.AllowDuplicates.txt | 11 +++++ .../schema/Cache.SerializerPermissions.txt | 7 ++- .../schema/HTML.TargetNoreferrer.txt | 9 ++++ .../schema/URI.AllowedSchemes.txt | 1 + .../vendor/HTMLPurifier/DefinitionCache.php | 2 +- .../DefinitionCache/Serializer.php | 40 +++++++++++---- .../HTMLModule/TargetNoreferrer.php | 21 ++++++++ .../vendor/HTMLPurifier/HTMLModuleManager.php | 5 ++ .../vendor/HTMLPurifier/Injector/Linkify.php | 11 +++-- .../HTMLPurifier/Injector/RemoveEmpty.php | 6 +++ .../HTMLPurifier/Injector/SafeObject.php | 7 ++- library/vendor/HTMLPurifier/Lexer.php | 13 +++-- .../HTMLPurifier/Printer/ConfigForm.php | 4 ++ library/vendor/HTMLPurifier/SOURCE | 17 ++++--- .../vendor/HTMLPurifier/URIScheme/data.php | 11 ++++- library/vendor/HTMLPurifier/URIScheme/tel.php | 46 ++++++++++++++++++ library/vendor/HTMLPurifier/VERSION | 2 +- 31 files changed, 309 insertions(+), 59 deletions(-) create mode 100644 library/vendor/HTMLPurifier/AttrTransform/TargetNoreferrer.php create mode 100644 library/vendor/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt create mode 100644 library/vendor/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt create mode 100644 library/vendor/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt create mode 100644 library/vendor/HTMLPurifier/HTMLModule/TargetNoreferrer.php create mode 100644 library/vendor/HTMLPurifier/URIScheme/tel.php diff --git a/icingaweb2.spec b/icingaweb2.spec index 8cf242c50..ab1523014 100644 --- a/icingaweb2.spec +++ b/icingaweb2.spec @@ -42,7 +42,7 @@ Requires: apache2-mod_php5 Requires: %{name}-common = %{version}-%{release} Requires: php-Icinga = %{version}-%{release} Requires: %{name}-vendor-dompdf = 0.7.0-1%{?dist} -Requires: %{name}-vendor-HTMLPurifier +Requires: %{name}-vendor-HTMLPurifier = 4.8.0-1%{?dist} Requires: %{name}-vendor-JShrink Requires: %{name}-vendor-lessphp Requires: %{name}-vendor-Parsedown @@ -118,7 +118,7 @@ Icinga Web 2 vendor library dompdf %package vendor-HTMLPurifier -Version: 4.7.0 +Version: 4.8.0 Release: 1%{?dist} Summary: Icinga Web 2 vendor library HTMLPurifier Group: Development/Libraries diff --git a/library/vendor/HTMLPurifier.php b/library/vendor/HTMLPurifier.php index c6041bc11..38a78e8da 100644 --- a/library/vendor/HTMLPurifier.php +++ b/library/vendor/HTMLPurifier.php @@ -19,7 +19,7 @@ */ /* - HTML Purifier 4.7.0 - Standards Compliant HTML Filtering + HTML Purifier 4.8.0 - Standards Compliant HTML Filtering Copyright (C) 2006-2008 Edward Z. Yang This library is free software; you can redistribute it and/or @@ -58,12 +58,12 @@ class HTMLPurifier * Version of HTML Purifier. * @type string */ - public $version = '4.7.0'; + public $version = '4.8.0'; /** * Constant with version of HTML Purifier. */ - const VERSION = '4.7.0'; + const VERSION = '4.8.0'; /** * Global configuration object. @@ -104,7 +104,7 @@ class HTMLPurifier /** * Initializes the purifier. * - * @param HTMLPurifier_Config $config Optional HTMLPurifier_Config object + * @param HTMLPurifier_Config|mixed $config Optional HTMLPurifier_Config object * for all instances of the purifier, if omitted, a default * configuration is supplied (which can be overridden on a * per-use basis). diff --git a/library/vendor/HTMLPurifier/AttrCollections.php b/library/vendor/HTMLPurifier/AttrCollections.php index 4f6c2e39a..c7b17cf14 100644 --- a/library/vendor/HTMLPurifier/AttrCollections.php +++ b/library/vendor/HTMLPurifier/AttrCollections.php @@ -21,6 +21,11 @@ class HTMLPurifier_AttrCollections * @param HTMLPurifier_HTMLModule[] $modules Hash array of HTMLPurifier_HTMLModule members */ public function __construct($attr_types, $modules) + { + $this->doConstruct($attr_types, $modules); + } + + public function doConstruct($attr_types, $modules) { // load extensions from the modules foreach ($modules as $module) { diff --git a/library/vendor/HTMLPurifier/AttrDef/CSS.php b/library/vendor/HTMLPurifier/AttrDef/CSS.php index 02c1641fb..2b977ca38 100644 --- a/library/vendor/HTMLPurifier/AttrDef/CSS.php +++ b/library/vendor/HTMLPurifier/AttrDef/CSS.php @@ -25,6 +25,7 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef $css = $this->parseCDATA($css); $definition = $config->getCSSDefinition(); + $allow_duplicates = $config->get("CSS.AllowDuplicates"); // we're going to break the spec and explode by semicolons. // This is because semicolon rarely appears in escaped form @@ -34,6 +35,7 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef $declarations = explode(';', $css); $propvalues = array(); + $new_declarations = ''; /** * Name of the current CSS property being validated. @@ -83,7 +85,11 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef if ($result === false) { continue; } - $propvalues[$property] = $result; + if ($allow_duplicates) { + $new_declarations .= "$property:$result;"; + } else { + $propvalues[$property] = $result; + } } $context->destroy('CurrentCSSProperty'); @@ -92,7 +98,6 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef // slightly inefficient, but it's the only way of getting rid of // duplicates. Perhaps config to optimize it, but not now. - $new_declarations = ''; foreach ($propvalues as $prop => $value) { $new_declarations .= "$prop:$value;"; } diff --git a/library/vendor/HTMLPurifier/AttrDef/CSS/URI.php b/library/vendor/HTMLPurifier/AttrDef/CSS/URI.php index f9434230e..6617acace 100644 --- a/library/vendor/HTMLPurifier/AttrDef/CSS/URI.php +++ b/library/vendor/HTMLPurifier/AttrDef/CSS/URI.php @@ -33,6 +33,9 @@ class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI return false; } $uri_string = substr($uri_string, 4); + if (strlen($uri_string) == 0) { + return false; + } $new_length = strlen($uri_string) - 1; if ($uri_string[$new_length] != ')') { return false; diff --git a/library/vendor/HTMLPurifier/AttrDef/HTML/ID.php b/library/vendor/HTMLPurifier/AttrDef/HTML/ID.php index 3d86efb44..4ba45610f 100644 --- a/library/vendor/HTMLPurifier/AttrDef/HTML/ID.php +++ b/library/vendor/HTMLPurifier/AttrDef/HTML/ID.php @@ -72,18 +72,26 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef // we purposely avoid using regex, hopefully this is faster - if (ctype_alpha($id)) { - $result = true; - } else { - if (!ctype_alpha(@$id[0])) { + if ($config->get('Attr.ID.HTML5') === true) { + if (preg_match('/[\t\n\x0b\x0c ]/', $id)) { return false; } - // primitive style of regexps, I suppose - $trim = trim( - $id, - 'A..Za..z0..9:-._' - ); - $result = ($trim === ''); + } else { + if (ctype_alpha($id)) { + // OK + } else { + if (!ctype_alpha(@$id[0])) { + return false; + } + // primitive style of regexps, I suppose + $trim = trim( + $id, + 'A..Za..z0..9:-._' + ); + if ($trim !== '') { + return false; + } + } } $regexp = $config->get('Attr.IDBlacklistRegexp'); @@ -91,14 +99,14 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef return false; } - if (!$this->selector && $result) { + if (!$this->selector) { $id_accumulator->add($id); } // if no change was made to the ID, return the result // else, return the new id if stripping whitespace made it // valid, or return false. - return $result ? $id : false; + return $id; } } diff --git a/library/vendor/HTMLPurifier/AttrDef/URI/Host.php b/library/vendor/HTMLPurifier/AttrDef/URI/Host.php index e7df800b1..151f7aff7 100644 --- a/library/vendor/HTMLPurifier/AttrDef/URI/Host.php +++ b/library/vendor/HTMLPurifier/AttrDef/URI/Host.php @@ -76,24 +76,33 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef // fairly well supported. $underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : ''; + // Based off of RFC 1738, but amended so that + // as per RFC 3696, the top label need only not be all numeric. // The productions describing this are: $a = '[a-z]'; // alpha $an = '[a-z0-9]'; // alphanum $and = "[a-z0-9-$underscore]"; // alphanum | "-" // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum - $domainlabel = "$an($and*$an)?"; - // toplabel = alpha | alpha *( alphanum | "-" ) alphanum - $toplabel = "$a($and*$an)?"; + $domainlabel = "$an(?:$and*$an)?"; + // AMENDED as per RFC 3696 + // toplabel = alphanum | alphanum *( alphanum | "-" ) alphanum + // side condition: not all numeric + $toplabel = "$an(?:$and*$an)?"; // hostname = *( domainlabel "." ) toplabel [ "." ] - if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) { - return $string; + if (preg_match("/^(?:$domainlabel\.)*($toplabel)\.?$/i", $string, $matches)) { + if (!ctype_digit($matches[1])) { + return $string; + } } + // PHP 5.3 and later support this functionality natively + if (function_exists('idn_to_ascii')) { + return idn_to_ascii($string); + // If we have Net_IDNA2 support, we can support IRIs by // punycoding them. (This is the most portable thing to do, // since otherwise we have to assume browsers support - - if ($config->get('Core.EnableIDNA')) { + } elseif ($config->get('Core.EnableIDNA')) { $idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => false, 'strict' => true)); // we need to encode each period separately $parts = explode('.', $string); diff --git a/library/vendor/HTMLPurifier/AttrTransform/ImgRequired.php b/library/vendor/HTMLPurifier/AttrTransform/ImgRequired.php index 7df6cb3e1..235ebb34b 100644 --- a/library/vendor/HTMLPurifier/AttrTransform/ImgRequired.php +++ b/library/vendor/HTMLPurifier/AttrTransform/ImgRequired.php @@ -32,8 +32,7 @@ class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform if ($src) { $alt = $config->get('Attr.DefaultImageAlt'); if ($alt === null) { - // truncate if the alt is too long - $attr['alt'] = substr(basename($attr['src']), 0, 40); + $attr['alt'] = basename($attr['src']); } else { $attr['alt'] = $alt; } diff --git a/library/vendor/HTMLPurifier/AttrTransform/TargetNoreferrer.php b/library/vendor/HTMLPurifier/AttrTransform/TargetNoreferrer.php new file mode 100644 index 000000000..587dc2e07 --- /dev/null +++ b/library/vendor/HTMLPurifier/AttrTransform/TargetNoreferrer.php @@ -0,0 +1,37 @@ +info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid')); + $border_radius = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Percentage(true), // disallow negative + new HTMLPurifier_AttrDef_CSS_Length('0') // disallow negative + )); + + $this->info['border-top-left-radius'] = + $this->info['border-top-right-radius'] = + $this->info['border-bottom-right-radius'] = + $this->info['border-bottom-left-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 2); + // TODO: support SLASH syntax + $this->info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4); + } /** diff --git a/library/vendor/HTMLPurifier/ChildDef/List.php b/library/vendor/HTMLPurifier/ChildDef/List.php index 891b9f6f5..5a53a4b49 100644 --- a/library/vendor/HTMLPurifier/ChildDef/List.php +++ b/library/vendor/HTMLPurifier/ChildDef/List.php @@ -38,6 +38,12 @@ class HTMLPurifier_ChildDef_List extends HTMLPurifier_ChildDef return false; } + // if li is not allowed, delete parent node + if (!isset($config->getHTMLDefinition()->info['li'])) { + trigger_error("Cannot allow ul/ol without allowing li", E_USER_WARNING); + return false; + } + // the new set of children $result = array(); diff --git a/library/vendor/HTMLPurifier/Config.php b/library/vendor/HTMLPurifier/Config.php index 2b2db0c26..7b9dcf0ec 100644 --- a/library/vendor/HTMLPurifier/Config.php +++ b/library/vendor/HTMLPurifier/Config.php @@ -21,7 +21,7 @@ class HTMLPurifier_Config * HTML Purifier's version * @type string */ - public $version = '4.7.0'; + public $version = '4.8.0'; /** * Whether or not to automatically finalize diff --git a/library/vendor/HTMLPurifier/ConfigSchema/schema.ser b/library/vendor/HTMLPurifier/ConfigSchema/schema.ser index 1e6ccd22755dfa27722e17f457a00ea42bdc1d6f..0a7a406e132dd5dfe79e4b52f8f44b7d4f8d5481 100644 GIT binary patch delta 356 zcmX?E{;qO@8IzIG=9fY>j3UNXN{%HZMS7ktdLALZKBh|6e%6~eGHzuOwzN`m4i46H z%*n|wcPTB%$xKe%%q_l>OU}s9N(rP^FC?)jJ+;IyzbG{=wWuhyNXa_M%5d{W#d1bQ z^U0dZaU8`~K;uhNb2c|BUtpZPK**i$)-p0NnofQxB)fTm>PJR#OwVRo>6%+l-pDAi kd71hW?0&Y>y2r? delta 107 zcmaD?d9r+h8Iz&qW9~n27YV2g%?5ll=akGMM9oOdl=K8Dva|I_; diff --git a/library/vendor/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt b/library/vendor/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt new file mode 100644 index 000000000..735d4b7a1 --- /dev/null +++ b/library/vendor/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt @@ -0,0 +1,10 @@ +Attr.ID.HTML5 +TYPE: bool/null +DEFAULT: null +VERSION: 4.8.0 +--DESCRIPTION-- +In HTML5, restrictions on the format of the id attribute have been significantly +relaxed, such that any string is valid so long as it contains no spaces and +is at least one character. In lieu of a general HTML5 compatibility flag, +set this configuration directive to true to use the relaxed rules. +--# vim: et sw=4 sts=4 diff --git a/library/vendor/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt b/library/vendor/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt new file mode 100644 index 000000000..4d054b1f0 --- /dev/null +++ b/library/vendor/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt @@ -0,0 +1,11 @@ +CSS.AllowDuplicates +TYPE: bool +DEFAULT: false +VERSION: 4.8.0 +--DESCRIPTION-- +

+ By default, HTML Purifier removes duplicate CSS properties, + like color:red; color:blue. If this is set to + true, duplicate properties are allowed. +

+--# vim: et sw=4 sts=4 diff --git a/library/vendor/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt b/library/vendor/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt index b2b83d9ab..2e0cc8104 100644 --- a/library/vendor/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt +++ b/library/vendor/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt @@ -1,5 +1,5 @@ Cache.SerializerPermissions -TYPE: int +TYPE: int/null VERSION: 4.3.0 DEFAULT: 0755 --DESCRIPTION-- @@ -8,4 +8,9 @@ DEFAULT: 0755 Directory permissions of the files and directories created inside the DefinitionCache/Serializer or other custom serializer path.

+

+ In HTML Purifier 4.8.0, this also supports NULL, + which means that no chmod'ing or directory creation shall + occur. +

--# vim: et sw=4 sts=4 diff --git a/library/vendor/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt b/library/vendor/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt new file mode 100644 index 000000000..cb5a0b0e5 --- /dev/null +++ b/library/vendor/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt @@ -0,0 +1,9 @@ +HTML.TargetNoreferrer +TYPE: bool +VERSION: 4.8.0 +DEFAULT: TRUE +--DESCRIPTION-- +If enabled, noreferrer rel attributes are added to links which have +a target attribute associated with them. This prevents malicious +destinations from overwriting the original window. +--# vim: et sw=4 sts=4 diff --git a/library/vendor/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/library/vendor/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt index 666635a5f..eb97307e2 100644 --- a/library/vendor/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt +++ b/library/vendor/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt @@ -8,6 +8,7 @@ array ( 'ftp' => true, 'nntp' => true, 'news' => true, + 'tel' => true, ) --DESCRIPTION-- Whitelist that defines the schemes that a URI is allowed to have. This diff --git a/library/vendor/HTMLPurifier/DefinitionCache.php b/library/vendor/HTMLPurifier/DefinitionCache.php index 67bb5b1e6..9aa8ff354 100644 --- a/library/vendor/HTMLPurifier/DefinitionCache.php +++ b/library/vendor/HTMLPurifier/DefinitionCache.php @@ -118,7 +118,7 @@ abstract class HTMLPurifier_DefinitionCache /** * Clears all expired (older version or revision) objects from cache - * @note Be carefuly implementing this method as flush. Flush must + * @note Be careful implementing this method as flush. Flush must * not interfere with other Definition types, and cleanup() * should not be repeatedly called by userland code. * @param HTMLPurifier_Config $config diff --git a/library/vendor/HTMLPurifier/DefinitionCache/Serializer.php b/library/vendor/HTMLPurifier/DefinitionCache/Serializer.php index ce268d91b..f930c6b94 100644 --- a/library/vendor/HTMLPurifier/DefinitionCache/Serializer.php +++ b/library/vendor/HTMLPurifier/DefinitionCache/Serializer.php @@ -97,6 +97,12 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac } $dir = $this->generateDirectoryPath($config); $dh = opendir($dir); + // Apparently, on some versions of PHP, readdir will return + // an empty string if you pass an invalid argument to readdir. + // So you need this test. See #49. + if (false === $dh) { + return false; + } while (false !== ($filename = readdir($dh))) { if (empty($filename)) { continue; @@ -106,6 +112,7 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac } unlink($dir . '/' . $filename); } + return true; } /** @@ -119,6 +126,10 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac } $dir = $this->generateDirectoryPath($config); $dh = opendir($dir); + // See #49 (and above). + if (false === $dh) { + return false; + } while (false !== ($filename = readdir($dh))) { if (empty($filename)) { continue; @@ -131,6 +142,7 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac unlink($dir . '/' . $filename); } } + return true; } /** @@ -186,11 +198,12 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac if ($result !== false) { // set permissions of the new file (no execute) $chmod = $config->get('Cache.SerializerPermissions'); - if (!$chmod) { - $chmod = 0644; // invalid config or simpletest + if ($chmod === null) { + // don't do anything + } else { + $chmod = $chmod & 0666; + chmod($file, $chmod); } - $chmod = $chmod & 0666; - chmod($file, $chmod); } return $result; } @@ -204,9 +217,6 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac { $directory = $this->generateDirectoryPath($config); $chmod = $config->get('Cache.SerializerPermissions'); - if (!$chmod) { - $chmod = 0755; // invalid config or simpletest - } if (!is_dir($directory)) { $base = $this->generateBaseDirectoryPath($config); if (!is_dir($base)) { @@ -219,7 +229,19 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac } elseif (!$this->_testPermissions($base, $chmod)) { return false; } - mkdir($directory, $chmod); + if ($chmod === null) { + trigger_error( + 'Base directory ' . $base . ' does not exist, + please create or change using %Cache.SerializerPath', + E_USER_WARNING + ); + return false; + } + if ($chmod !== null) { + mkdir($directory, $chmod); + } else { + mkdir($directory); + } if (!$this->_testPermissions($directory, $chmod)) { trigger_error( 'Base directory ' . $base . ' does not exist, @@ -256,7 +278,7 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac ); return false; } - if (function_exists('posix_getuid')) { + if (function_exists('posix_getuid') && $chmod !== null) { // POSIX system, we can give more specific advice if (fileowner($dir) === posix_getuid()) { // we can chmod it ourselves diff --git a/library/vendor/HTMLPurifier/HTMLModule/TargetNoreferrer.php b/library/vendor/HTMLPurifier/HTMLModule/TargetNoreferrer.php new file mode 100644 index 000000000..32484d601 --- /dev/null +++ b/library/vendor/HTMLPurifier/HTMLModule/TargetNoreferrer.php @@ -0,0 +1,21 @@ +addBlankElement('a'); + $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoreferrer(); + } +} diff --git a/library/vendor/HTMLPurifier/HTMLModuleManager.php b/library/vendor/HTMLPurifier/HTMLModuleManager.php index f3a17cb03..2546c043c 100644 --- a/library/vendor/HTMLPurifier/HTMLModuleManager.php +++ b/library/vendor/HTMLPurifier/HTMLModuleManager.php @@ -271,6 +271,11 @@ class HTMLPurifier_HTMLModuleManager if ($config->get('HTML.TargetBlank')) { $modules[] = 'TargetBlank'; } + // NB: HTML.TargetNoreferrer must be AFTER HTML.TargetBlank + // so that its post-attr-transform gets run afterwards. + if ($config->get('HTML.TargetNoreferrer')) { + $modules[] = 'TargetNoreferrer'; + } // merge in custom modules $modules = array_merge($modules, $this->userModules); diff --git a/library/vendor/HTMLPurifier/Injector/Linkify.php b/library/vendor/HTMLPurifier/Injector/Linkify.php index 069708c25..74f83eaa7 100644 --- a/library/vendor/HTMLPurifier/Injector/Linkify.php +++ b/library/vendor/HTMLPurifier/Injector/Linkify.php @@ -31,9 +31,14 @@ class HTMLPurifier_Injector_Linkify extends HTMLPurifier_Injector return; } - // there is/are URL(s). Let's split the string: - // Note: this regex is extremely permissive - $bits = preg_split('#((?:https?|ftp)://[^\s\'",<>()]+)#Su', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE); + // there is/are URL(s). Let's split the string. + // We use this regex: + // https://gist.github.com/gruber/249502 + // but with @cscott's backtracking fix and also + // the Unicode characters un-Unicodified. + $bits = preg_split( + '/\\b((?:[a-z][\\w\\-]+:(?:\\/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}\\/)(?:[^\\s()<>]|\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\))+(?:\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'".,<>?\x{00ab}\x{00bb}\x{201c}\x{201d}\x{2018}\x{2019}]))/iu', + $token->data, -1, PREG_SPLIT_DELIM_CAPTURE); $token = array(); diff --git a/library/vendor/HTMLPurifier/Injector/RemoveEmpty.php b/library/vendor/HTMLPurifier/Injector/RemoveEmpty.php index 01353ff1d..0ebc477c6 100644 --- a/library/vendor/HTMLPurifier/Injector/RemoveEmpty.php +++ b/library/vendor/HTMLPurifier/Injector/RemoveEmpty.php @@ -46,6 +46,12 @@ class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector $this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp'); $this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions'); $this->exclude = $config->get('AutoFormat.RemoveEmpty.Predicate'); + foreach ($this->exclude as $key => $attrs) { + if (!is_array($attrs)) { + // HACK, see HTMLPurifier/Printer/ConfigForm.php + $this->exclude[$key] = explode(';', $attrs); + } + } $this->attrValidator = new HTMLPurifier_AttrValidator(); } diff --git a/library/vendor/HTMLPurifier/Injector/SafeObject.php b/library/vendor/HTMLPurifier/Injector/SafeObject.php index 3d17e07af..317f7864d 100644 --- a/library/vendor/HTMLPurifier/Injector/SafeObject.php +++ b/library/vendor/HTMLPurifier/Injector/SafeObject.php @@ -36,6 +36,7 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector ); /** + * These are all lower-case keys. * @type array */ protected $allowedParam = array( @@ -43,7 +44,7 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector 'movie' => true, 'flashvars' => true, 'src' => true, - 'allowFullScreen' => true, // if omitted, assume to be 'false' + 'allowfullscreen' => true, // if omitted, assume to be 'false' ); /** @@ -93,9 +94,11 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector $token->attr['name'] === $this->addParam[$n]) { // keep token, and add to param stack $this->paramStack[$i][$n] = true; - } elseif (isset($this->allowedParam[$n])) { + } elseif (isset($this->allowedParam[strtolower($n)])) { // keep token, don't do anything to it // (could possibly check for duplicates here) + // Note: In principle, parameters should be case sensitive. + // But it seems they are not really; so accept any case. } else { $token = false; } diff --git a/library/vendor/HTMLPurifier/Lexer.php b/library/vendor/HTMLPurifier/Lexer.php index 43732621d..44c5c659d 100644 --- a/library/vendor/HTMLPurifier/Lexer.php +++ b/library/vendor/HTMLPurifier/Lexer.php @@ -345,12 +345,17 @@ class HTMLPurifier_Lexer public function extractBody($html) { $matches = array(); - $result = preg_match('!]*>(.*)!is', $html, $matches); + $result = preg_match('|(.*?)]*>(.*)|is', $html, $matches); if ($result) { - return $matches[1]; - } else { - return $html; + // Make sure it's not in a comment + $comment_start = strrpos($matches[1], ''); + if ($comment_start === false || + ($comment_end !== false && $comment_end > $comment_start)) { + return $matches[2]; + } } + return $html; } } diff --git a/library/vendor/HTMLPurifier/Printer/ConfigForm.php b/library/vendor/HTMLPurifier/Printer/ConfigForm.php index 36100ce73..65a777904 100644 --- a/library/vendor/HTMLPurifier/Printer/ConfigForm.php +++ b/library/vendor/HTMLPurifier/Printer/ConfigForm.php @@ -327,6 +327,10 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer case HTMLPurifier_VarParser::HASH: $nvalue = ''; foreach ($value as $i => $v) { + if (is_array($v)) { + // HACK + $v = implode(";", $v); + } $nvalue .= "$i:$v" . PHP_EOL; } $value = $nvalue; diff --git a/library/vendor/HTMLPurifier/SOURCE b/library/vendor/HTMLPurifier/SOURCE index 44e19b159..bdc316957 100644 --- a/library/vendor/HTMLPurifier/SOURCE +++ b/library/vendor/HTMLPurifier/SOURCE @@ -1,7 +1,10 @@ -curl https://codeload.github.com/ezyang/htmlpurifier/tar.gz/v4.7.0 -o htmlpurifier-4.7.0.tar.gz -tar xzf htmlpurifier-4.7.0.tar.gz --strip-components 1 htmlpurifier-4.7.0/LICENSE -tar xzf htmlpurifier-4.7.0.tar.gz --strip-components 1 htmlpurifier-4.7.0/VERSION -tar xzf htmlpurifier-4.7.0.tar.gz -C ../ --strip-components 2 htmlpurifier-4.7.0/library/HTMLPurifier.php -tar xzf htmlpurifier-4.7.0.tar.gz -C ../ --strip-components 2 htmlpurifier-4.7.0/library/HTMLPurifier.autoload.php -tar xzf htmlpurifier-4.7.0.tar.gz --strip-components 3 htmlpurifier-4.7.0/library/HTMLPurifier/* -rm htmlpurifier-4.7.0.tar.gz +GLOBIGNORE=$0; rm -rf * +rm ../HTMLPurifier*.php + +curl https://codeload.github.com/ezyang/htmlpurifier/tar.gz/v4.8.0 -o htmlpurifier-4.8.0.tar.gz +tar xzf htmlpurifier-4.8.0.tar.gz --strip-components 1 htmlpurifier-4.8.0/LICENSE +tar xzf htmlpurifier-4.8.0.tar.gz --strip-components 1 htmlpurifier-4.8.0/VERSION +tar xzf htmlpurifier-4.8.0.tar.gz -C ../ --strip-components 2 htmlpurifier-4.8.0/library/HTMLPurifier.php +tar xzf htmlpurifier-4.8.0.tar.gz -C ../ --strip-components 2 htmlpurifier-4.8.0/library/HTMLPurifier.autoload.php +tar xzf htmlpurifier-4.8.0.tar.gz --strip-components 3 htmlpurifier-4.8.0/library/HTMLPurifier/* +rm htmlpurifier-4.8.0.tar.gz diff --git a/library/vendor/HTMLPurifier/URIScheme/data.php b/library/vendor/HTMLPurifier/URIScheme/data.php index 6ebca4984..41c49d553 100644 --- a/library/vendor/HTMLPurifier/URIScheme/data.php +++ b/library/vendor/HTMLPurifier/URIScheme/data.php @@ -79,9 +79,18 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme } else { $raw_data = $data; } + if ( strlen($raw_data) < 12 ) { + // error; exif_imagetype throws exception with small files, + // and this likely indicates a corrupt URI/failed parse anyway + return false; + } // XXX probably want to refactor this into a general mechanism // for filtering arbitrary content types - $file = tempnam("/tmp", ""); + if (function_exists('sys_get_temp_dir')) { + $file = tempnam(sys_get_temp_dir(), ""); + } else { + $file = tempnam("/tmp", ""); + } file_put_contents($file, $raw_data); if (function_exists('exif_imagetype')) { $image_code = exif_imagetype($file); diff --git a/library/vendor/HTMLPurifier/URIScheme/tel.php b/library/vendor/HTMLPurifier/URIScheme/tel.php new file mode 100644 index 000000000..8cd193352 --- /dev/null +++ b/library/vendor/HTMLPurifier/URIScheme/tel.php @@ -0,0 +1,46 @@ +userinfo = null; + $uri->host = null; + $uri->port = null; + + // Delete all non-numeric characters, non-x characters + // from phone number, EXCEPT for a leading plus sign. + $uri->path = preg_replace('/(?!^\+)[^\dx]/', '', + // Normalize e(x)tension to lower-case + str_replace('X', 'x', $uri->path)); + + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/library/vendor/HTMLPurifier/VERSION b/library/vendor/HTMLPurifier/VERSION index 1163055e2..6ca6df113 100644 --- a/library/vendor/HTMLPurifier/VERSION +++ b/library/vendor/HTMLPurifier/VERSION @@ -1 +1 @@ -4.7.0 \ No newline at end of file +4.8.0 \ No newline at end of file From 4ec19cab6e4a5ddc1a8c53ec37148b22d42bfbeb Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 10 Nov 2016 16:08:38 +0100 Subject: [PATCH 237/337] Fix JShrink version and let the Web 2 rpm depend on it --- icingaweb2.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icingaweb2.spec b/icingaweb2.spec index ab1523014..9b53f5105 100644 --- a/icingaweb2.spec +++ b/icingaweb2.spec @@ -43,7 +43,7 @@ Requires: %{name}-common = %{version}-%{release} Requires: php-Icinga = %{version}-%{release} Requires: %{name}-vendor-dompdf = 0.7.0-1%{?dist} Requires: %{name}-vendor-HTMLPurifier = 4.8.0-1%{?dist} -Requires: %{name}-vendor-JShrink +Requires: %{name}-vendor-JShrink = 1.1.0-1%{?dist} Requires: %{name}-vendor-lessphp Requires: %{name}-vendor-Parsedown @@ -130,7 +130,7 @@ Icinga Web 2 vendor library HTMLPurifier %package vendor-JShrink -Version: 1.0.1 +Version: 1.1.0 Release: 1%{?dist} Summary: Icinga Web 2 vendor library JShrink Group: Development/Libraries From fd4ef3a5058d5ef69ec26d18bf93404c6e91e6ed Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 10 Nov 2016 16:11:50 +0100 Subject: [PATCH 238/337] RPM: Add version to the lessphp dependency --- icingaweb2.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icingaweb2.spec b/icingaweb2.spec index 9b53f5105..00225154c 100644 --- a/icingaweb2.spec +++ b/icingaweb2.spec @@ -44,7 +44,7 @@ Requires: php-Icinga = %{version}-%{release} Requires: %{name}-vendor-dompdf = 0.7.0-1%{?dist} Requires: %{name}-vendor-HTMLPurifier = 4.8.0-1%{?dist} Requires: %{name}-vendor-JShrink = 1.1.0-1%{?dist} -Requires: %{name}-vendor-lessphp +Requires: %{name}-vendor-lessphp = 0.4.0-1%{?dist} Requires: %{name}-vendor-Parsedown From 6a37d1e6adce9a9118b3caa9e30a014be2a35e09 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 10 Nov 2016 16:14:26 +0100 Subject: [PATCH 239/337] Fix Parsedown version and let the Web 2 rpm depend on it --- icingaweb2.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icingaweb2.spec b/icingaweb2.spec index 00225154c..b6d49b73a 100644 --- a/icingaweb2.spec +++ b/icingaweb2.spec @@ -45,7 +45,7 @@ Requires: %{name}-vendor-dompdf = 0.7.0-1%{?dist} Requires: %{name}-vendor-HTMLPurifier = 4.8.0-1%{?dist} Requires: %{name}-vendor-JShrink = 1.1.0-1%{?dist} Requires: %{name}-vendor-lessphp = 0.4.0-1%{?dist} -Requires: %{name}-vendor-Parsedown +Requires: %{name}-vendor-Parsedown = 1.6.0-1%{?dist} %description @@ -154,7 +154,7 @@ Icinga Web 2 vendor library lessphp %package vendor-Parsedown -Version: 1.0.0 +Version: 1.6.0 Release: 1%{?dist} Summary: Icinga Web 2 vendor library Parsedown Group: Development/Libraries From 634a69aec9c20d0ff2e1b589b0e22b7e2b5bc51b Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 11 Nov 2016 09:17:58 +0100 Subject: [PATCH 240/337] DbConnection: Explicitly check for NULL when rendering unequal filters resolves #12852 --- library/Icinga/Data/Db/DbConnection.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Data/Db/DbConnection.php b/library/Icinga/Data/Db/DbConnection.php index 81afb2c3d..9705bff19 100644 --- a/library/Icinga/Data/Db/DbConnection.php +++ b/library/Icinga/Data/Db/DbConnection.php @@ -444,7 +444,7 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp if ($sign === '=') { return $column . ' IN (' . $this->dbAdapter->quote($value) . ')'; } elseif ($sign === '!=') { - return $column . ' NOT IN (' . $this->dbAdapter->quote($value) . ')'; + return sprintf('(%1$s NOT IN (%2$s) OR %1$s IS NULL)', $column, $this->dbAdapter->quote($value)); } throw new ProgrammingError( @@ -467,9 +467,15 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp return $this->dbAdapter->quote(0); } - return $column . ' NOT LIKE ' . $this->dbAdapter->quote(preg_replace('~\*~', '%', $value)); + return sprintf( + '(%1$s NOT LIKE %2$s OR %1$s IS NULL)', + $column, + $this->dbAdapter->quote(preg_replace('~\*~', '%', $value)) + ); + } elseif ($sign === '!=') { + return sprintf('(%1$s != %2$s OR %1$s IS NULL)', $column, $this->dbAdapter->quote($value)); } else { - return $column . ' ' . $sign . ' ' . $this->dbAdapter->quote($value); + return sprintf('%s %s %s', $column, $sign, $this->dbAdapter->quote($value)); } } From 3a619eb8c72b9570039dede58b4ff3279b23d00c Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 11 Nov 2016 09:18:53 +0100 Subject: [PATCH 241/337] DbConnection: Don't ignore asterisks when rendering filters refs #11885 --- library/Icinga/Data/Db/DbConnection.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/Icinga/Data/Db/DbConnection.php b/library/Icinga/Data/Db/DbConnection.php index 9705bff19..e1068f740 100644 --- a/library/Icinga/Data/Db/DbConnection.php +++ b/library/Icinga/Data/Db/DbConnection.php @@ -9,8 +9,8 @@ use Icinga\Data\Inspection; use PDO; use Iterator; use Zend_Db; +use Zend_Db_Expr; use Icinga\Data\ConfigObject; -use Icinga\Data\Db\DbQuery; use Icinga\Data\Extensible; use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\FilterAnd; @@ -452,10 +452,10 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp ); } elseif ($sign === '=' && strpos($value, '*') !== false) { if ($value === '*') { - // We'll ignore such filters as it prevents index usage and because "*" means anything, anything means - // all whereas all means that whether we use a filter to match anything or no filter at all makes no - // difference, except for performance reasons... - return ''; + // We'll ignore such filters as it prevents index usage and because "*" means anything, so whether we're + // using a real column with a valid comparison here or just an expression which can only be evaluated to + // true makes no difference, except for performance reasons... + return new Zend_Db_Expr('TRUE'); } return $column . ' LIKE ' . $this->dbAdapter->quote(preg_replace('~\*~', '%', $value)); @@ -464,7 +464,7 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp // We'll ignore such filters as it prevents index usage and because "*" means nothing, so whether we're // using a real column with a valid comparison here or just an expression which cannot be evaluated to // true makes no difference, except for performance reasons... - return $this->dbAdapter->quote(0); + return new Zend_Db_Expr('FALSE'); } return sprintf( From f7a8524dcecb69617d5ec7f56ce6608012c4531e Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 11 Nov 2016 09:19:59 +0100 Subject: [PATCH 242/337] DbUserGroupBackend: Group by group.id when joining group memberships Prevents duplicate results in case a group has multiple members. --- library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php b/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php index e30968f98..a1911d916 100644 --- a/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php +++ b/library/Icinga/Authentication/UserGroup/DbUserGroupBackend.php @@ -254,7 +254,7 @@ class DbUserGroupBackend extends DbRepository implements UserGroupBackendInterfa $this->requireTable('group_membership'), 'g.id = gm.group_id', array() - ); + )->group('g.id'); } /** From c72cf257ca85d0cbe33d3b93cbafd333592507e1 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 11 Nov 2016 09:50:37 +0100 Subject: [PATCH 243/337] LdapUserGroupBackendForm: Fix doc of method createHiddenUserConfigElements refs #10401 --- .../forms/Config/UserGroup/LdapUserGroupBackendForm.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php index 357dbaca9..35b32580f 100644 --- a/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php +++ b/application/forms/Config/UserGroup/LdapUserGroupBackendForm.php @@ -281,9 +281,9 @@ class LdapUserGroupBackendForm extends Form } /** - * Create and add all elements to this form required for the user configuration as hidden + * Create and add all elements for the user configuration as hidden inputs * - * This is required to purge already present options when changing the type form LDAP to MS AD. + * This is required to purge already present options when unlinking a group backend with a user backend. */ protected function createHiddenUserConfigElements() { From 252ce5d60de7cd601b92b28e41b67bacb513bca9 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 3 Nov 2016 18:28:44 +0100 Subject: [PATCH 244/337] Revert "Don't show the link for resource removal if the resource is utilized for configuration" This reverts commit 7331904851ec6bbb5d32a40ead3bfed632f10d1e. --- application/controllers/ConfigController.php | 2 -- .../views/scripts/config/resource.phtml | 24 ++++++++----------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index 03750fe24..c65298377 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -315,8 +315,6 @@ class ConfigController extends Controller { $this->assertPermission('config/application/resources'); $this->view->resources = Config::app('resources', true); - $configResource = Config::app()->get('global', 'config_resource'); - $this->view->unremovableResources = $configResource === null ? array() : array($configResource); $this->createApplicationTabs()->activate('resource'); } diff --git a/application/views/scripts/config/resource.phtml b/application/views/scripts/config/resource.phtml index 82f7af0cf..ec9c0c7ea 100644 --- a/application/views/scripts/config/resource.phtml +++ b/application/views/scripts/config/resource.phtml @@ -58,20 +58,16 @@ ) ?>
From 82cc96173d5456c8686da8e4d5631927228d18d7 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 11 Nov 2016 11:01:16 +0100 Subject: [PATCH 245/337] ConfigController: Warn the user if he's about to remove referenced resources refs #9804 --- application/controllers/ConfigController.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index c65298377..d22e3a680 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -395,10 +395,10 @@ class ConfigController extends Controller $authConfig = Config::app('authentication'); foreach ($authConfig as $backendName => $config) { if ($config->get('resource') === $resource) { - $form->addDescription(sprintf( + $form->warning(sprintf( $this->translate( - 'The resource "%s" is currently utilized for authentication by user backend "%s". ' . - 'Removing the resource can result in noone being able to log in any longer.' + 'The resource "%s" is currently utilized for authentication by user backend "%s".' + . ' Removing the resource can result in noone being able to log in any longer.' ), $resource, $backendName @@ -406,6 +406,17 @@ class ConfigController extends Controller } } + // Check if selected resource is currently used as user preferences backend + if (Config::app()->get('global', 'config_resource') === $resource) { + $form->warning(sprintf( + $this->translate( + 'The resource "%s" is currently utilized to store user preferences. Removing the' + . ' resource causes all current user preferences not being available any longer.' + ), + $resource + )); + } + $this->view->form = $form; $this->render('resource/remove'); } From 1cd2cfbdc944901b9379bbd1e1d53f8dbafc46fa Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 20 Oct 2016 18:28:19 +0200 Subject: [PATCH 246/337] Revert "Make hooks respect module permissions" This reverts commit 4d2e6d2d8709da9168af824c36458dd5857b08a8. --- library/Icinga/Application/Modules/Module.php | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index 555127228..de40266e1 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -4,7 +4,6 @@ namespace Icinga\Application\Modules; use Exception; -use Icinga\Authentication\Auth; use Zend_Controller_Router_Route; use Zend_Controller_Router_Route_Abstract; use Zend_Controller_Router_Route_Regex; @@ -1283,22 +1282,19 @@ class Module */ protected function provideHook($name, $implementation = null, $key = null) { - if (Auth::getInstance()->hasPermission('module/' . $this->name)) { - if ($implementation === null) { - $implementation = $name; - } - - if (strpos($implementation, '\\') === false) { - $class = $this->getNamespace() - . '\\ProvidedHook\\' - . $this->slashesToNamespace($implementation); - } else { - $class = $implementation; - } - - Hook::register($name, $class, $class); + if ($implementation === null) { + $implementation = $name; } + if (strpos($implementation, '\\') === false) { + $class = $this->getNamespace() + . '\\ProvidedHook\\' + . $this->slashesToNamespace($implementation); + } else { + $class = $implementation; + } + + Hook::register($name, $class, $class); return $this; } From 5bce7dc9b86e30068cc55e762f259772412081fd Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 11 Nov 2016 16:15:16 +0100 Subject: [PATCH 247/337] Hook: Check a user's module permission before providing it's hook instances refs #12396 --- library/Icinga/Application/Hook.php | 59 ++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/library/Icinga/Application/Hook.php b/library/Icinga/Application/Hook.php index b397be65a..33761a342 100644 --- a/library/Icinga/Application/Hook.php +++ b/library/Icinga/Application/Hook.php @@ -4,7 +4,8 @@ namespace Icinga\Application; use Exception; -use Icinga\Application\Logger; +use Icinga\Authentication\Auth; +use Icinga\Application\Modules\Manager; use Icinga\Exception\ProgrammingError; /** @@ -148,6 +149,46 @@ class Hook ); } + /** + * Extract the Icinga module name from a given namespaced class name + * + * Does no validation, prefix must have been checked before + * + * Shameless copy of ClassLoader::extractModuleName() + * + * @param string $class The hook's class path + * + * @return string + */ + protected static function extractModuleName($class) + { + return lcfirst( + substr( + $class, + ClassLoader::MODULE_PREFIX_LENGTH, + strpos( + $class, + ClassLoader::NAMESPACE_SEPARATOR, + ClassLoader::MODULE_PREFIX_LENGTH + 1 + ) - ClassLoader::MODULE_PREFIX_LENGTH + ) + ); + } + + /** + * Return whether the user has the permission to access the module which provides the given hook + * + * @param string $class The hook's class path + * + * @return bool + */ + protected static function hasPermission($class) + { + return Auth::getInstance()->hasPermission( + Manager::MODULE_PERMISSION_NS . self::extractModuleName($class) + ); + } + /** * Test for a valid class name * @@ -213,17 +254,19 @@ class Hook public static function all($name) { $name = self::normalizeHookName($name); - if (!self::has($name)) { + if (! self::has($name)) { return array(); } foreach (self::$hooks[$name] as $key => $hook) { - if (self::createInstance($name, $key) === null) { - return array(); + if (self::hasPermission($key)) { + if (self::createInstance($name, $key) === null) { + return array(); + } } } - return self::$instances[$name]; + return isset(self::$instances[$name]) ? self::$instances[$name] : array(); } /** @@ -238,7 +281,11 @@ class Hook $name = self::normalizeHookName($name); if (self::has($name)) { - return self::createInstance($name, key(self::$hooks[$name])); + foreach (self::$hooks[$name] as $key => $hook) { + if (self::hasPermission($key)) { + return self::createInstance($name, $key); + } + } } } From f1a1f411922f993e53780f5b5f3b17fc425814b1 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 11 Nov 2016 16:15:59 +0100 Subject: [PATCH 248/337] Module: Drop deprecated method registerHook and arg $key of method provideHook --- library/Icinga/Application/Modules/Module.php | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index de40266e1..b5eafbdb0 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -1242,22 +1242,6 @@ class Module return $this->includeScript($this->configScript); } - /** - * Register a hook - * - * @param string $name Name of the hook - * @param string $class Class of the hook w/ namespace - * @param string $key - * - * @return $this - * - * @deprecated Deprecated in favor of {@link provideHook()}. Will be removed in version 2.2.0 - */ - protected function registerHook($name, $class, $key = null) - { - return $this->provideHook($name, $class, $key); - } - protected function slashesToNamespace($class) { $list = explode('/', $class); @@ -1275,12 +1259,10 @@ class Module * @param string $implementation [optional] Fully qualified name of the class providing the hook implementation. * Defaults to the module's ProvidedHook namespace plus the hook's name for the * class name. Web 2's namespace separator is \\ (double backslash) at the moment - * @param string $key No-op arg for compatibility reasons. This argument is deprecated and will be - * removed in version 2.2.0 * * @return $this */ - protected function provideHook($name, $implementation = null, $key = null) + protected function provideHook($name, $implementation = null) { if ($implementation === null) { $implementation = $name; From bf809ef7c8da23330ae702cc9a410924024b244a Mon Sep 17 00:00:00 2001 From: Florian Strohmaier Date: Mon, 7 Nov 2016 11:44:35 +0100 Subject: [PATCH 249/337] Update font stack Try to use the OS font instead of providing a custom one. Signed-off-by: Eric Lippmann --- public/css/icinga/base.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/css/icinga/base.less b/public/css/icinga/base.less index b2404a873..fc7482de0 100644 --- a/public/css/icinga/base.less +++ b/public/css/icinga/base.less @@ -44,7 +44,7 @@ @tr-hover-color: #F5FDFF; // Font families -@font-family: Calibri, "Lucida Grande", sans-serif; +@font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; @font-family-fixed: "Liberation Mono", "Lucida Console", Courier, monospace; @font-family-wide: Tahoma, Verdana, sans-serif; From 7c83db08a3313bbd6e9979d48144eb7d162b23d2 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 14 Nov 2016 14:01:55 +0100 Subject: [PATCH 250/337] Don't auto-capitalize the username in the login form --- application/forms/Authentication/LoginForm.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/application/forms/Authentication/LoginForm.php b/application/forms/Authentication/LoginForm.php index 437812ceb..537520656 100644 --- a/application/forms/Authentication/LoginForm.php +++ b/application/forms/Authentication/LoginForm.php @@ -39,9 +39,10 @@ class LoginForm extends Form 'text', 'username', array( - 'required' => true, - 'label' => $this->translate('Username'), - 'class' => false === isset($formData['username']) ? 'autofocus' : '' + 'autocapitalize' => 'off', + 'class' => false === isset($formData['username']) ? 'autofocus' : '', + 'label' => $this->translate('Username'), + 'required' => true ) ); $this->addElement( From bdeeec50bb3f331a1afbbf7924fecefb955dbf1b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 14 Nov 2016 15:08:06 +0100 Subject: [PATCH 251/337] CSS: Add icon warning-empty refs #11859 --- application/fonts/fontello-ifont/LICENSE.txt | 2 +- application/fonts/fontello-ifont/README.txt | 4 +- application/fonts/fontello-ifont/config.json | 6 + .../fonts/fontello-ifont/css/animation.css | 0 .../fonts/fontello-ifont/css/ifont-codes.css | 3 +- .../fontello-ifont/css/ifont-embedded.css | 15 +- .../fontello-ifont/css/ifont-ie7-codes.css | 3 +- .../fonts/fontello-ifont/css/ifont-ie7.css | 3 +- .../fonts/fontello-ifont/css/ifont.css | 16 +- application/fonts/fontello-ifont/demo.html | 13 +- public/font/ifont.eot | Bin 31740 -> 42536 bytes public/font/ifont.svg | 390 ++++++++++++------ public/font/ifont.ttf | Bin 31584 -> 42380 bytes public/font/ifont.woff | Bin 19324 -> 25624 bytes 14 files changed, 299 insertions(+), 156 deletions(-) mode change 100644 => 100755 application/fonts/fontello-ifont/LICENSE.txt mode change 100644 => 100755 application/fonts/fontello-ifont/README.txt mode change 100644 => 100755 application/fonts/fontello-ifont/config.json mode change 100644 => 100755 application/fonts/fontello-ifont/css/animation.css mode change 100644 => 100755 application/fonts/fontello-ifont/css/ifont-codes.css mode change 100644 => 100755 application/fonts/fontello-ifont/css/ifont-embedded.css mode change 100644 => 100755 application/fonts/fontello-ifont/css/ifont-ie7-codes.css mode change 100644 => 100755 application/fonts/fontello-ifont/css/ifont-ie7.css mode change 100644 => 100755 application/fonts/fontello-ifont/css/ifont.css mode change 100644 => 100755 application/fonts/fontello-ifont/demo.html mode change 100644 => 100755 public/font/ifont.eot mode change 100644 => 100755 public/font/ifont.svg mode change 100644 => 100755 public/font/ifont.ttf mode change 100644 => 100755 public/font/ifont.woff diff --git a/application/fonts/fontello-ifont/LICENSE.txt b/application/fonts/fontello-ifont/LICENSE.txt old mode 100644 new mode 100755 index 7e9af2aa8..484bddcb2 --- a/application/fonts/fontello-ifont/LICENSE.txt +++ b/application/fonts/fontello-ifont/LICENSE.txt @@ -3,7 +3,7 @@ Font license info ## Font Awesome - Copyright (C) 2012 by Dave Gandy + Copyright (C) 2016 by Dave Gandy Author: Dave Gandy License: SIL () diff --git a/application/fonts/fontello-ifont/README.txt b/application/fonts/fontello-ifont/README.txt old mode 100644 new mode 100755 index a91438a9a..beaab3366 --- a/application/fonts/fontello-ifont/README.txt +++ b/application/fonts/fontello-ifont/README.txt @@ -2,14 +2,14 @@ This webfont is generated by http://fontello.com open source project. ================================================================================ -Please, note, that you should obey original font licences, used to make this +Please, note, that you should obey original font licenses, used to make this webfont pack. Details available in LICENSE.txt file. - Usually, it's enough to publish content of LICENSE.txt file somewhere on your site in "About" section. - If your project is open-source, usually, it will be ok to make LICENSE.txt - file publically available in your repository. + file publicly available in your repository. - Fonts, used in Fontello, don't require a clickable link on your site. But any kind of additional authors crediting is welcome. diff --git a/application/fonts/fontello-ifont/config.json b/application/fonts/fontello-ifont/config.json old mode 100644 new mode 100755 index f68fd5cef..b5ebf4bda --- a/application/fonts/fontello-ifont/config.json +++ b/application/fonts/fontello-ifont/config.json @@ -779,6 +779,12 @@ "css": "flapping", "code": 59485, "src": "mfglabs" + }, + { + "uid": "8f28d948aa6379b1a69d2a090e7531d4", + "css": "warning-empty", + "code": 59521, + "src": "typicons" } ] } \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/animation.css b/application/fonts/fontello-ifont/css/animation.css old mode 100644 new mode 100755 diff --git a/application/fonts/fontello-ifont/css/ifont-codes.css b/application/fonts/fontello-ifont/css/ifont-codes.css old mode 100644 new mode 100755 index 146f097f0..7f53004ce --- a/application/fonts/fontello-ifont/css/ifont-codes.css +++ b/application/fonts/fontello-ifont/css/ifont-codes.css @@ -127,4 +127,5 @@ .icon-info-circled:before { content: '\e87d'; } /* '' */ .icon-twitter:before { content: '\e87e'; } /* '' */ .icon-facebook-squared:before { content: '\e87f'; } /* '' */ -.icon-gplus-squared:before { content: '\e880'; } /* '' */ \ No newline at end of file +.icon-gplus-squared:before { content: '\e880'; } /* '' */ +.icon-warning-empty:before { content: '\e881'; } /* '' */ \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/ifont-embedded.css b/application/fonts/fontello-ifont/css/ifont-embedded.css old mode 100644 new mode 100755 index d8330a657..af68b1ad5 --- a/application/fonts/fontello-ifont/css/ifont-embedded.css +++ b/application/fonts/fontello-ifont/css/ifont-embedded.css @@ -1,15 +1,15 @@ @font-face { font-family: 'ifont'; - src: url('../font/ifont.eot?36375038'); - src: url('../font/ifont.eot?36375038#iefix') format('embedded-opentype'), - url('../font/ifont.svg?36375038#ifont') format('svg'); + src: url('../font/ifont.eot?58366739'); + src: url('../font/ifont.eot?58366739#iefix') format('embedded-opentype'), + url('../font/ifont.svg?58366739#ifont') format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'ifont'; - src: url('data:application/octet-stream;base64,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') format('woff'), - url('data:application/octet-stream;base64,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BjYGFQ4BFS4BNx4BFxYHBgcGFxQGFgGtdMZycsboyG4GerwBEgEIAwECBAMRFRMKAQwEDAMBBwYEBAoFBgQBCAEGAQQEBAIEBgEGAggJBQQFBQMBCAwBBRwHAgIBCAEOAQIHCQMEBAEEAgMBBwoCBAUNBAIUDhMECAYBAgECBQkCARMJAgQGBQYKAwgEBwUDAgYJBAYBBQkEBQMDAgUEAQ4HCw8EEAMDAQgECAEIAwEIBAQEAwMEAgQSBQMMDAEDAwIMGRsDAwgFEwUDCwQNCwEEAgYECAQJBFEyBAUCBgUDARgKAQIHBQQDBAQEAQIBAQECCgcHEgQHCQQDCAQCDgEBAgIOAgQCAg8IAwQDAgMFAQQKCgEECAQFDAcCAwgDCQcWBgYFCAgQBBQKAQIEAgYDDgMEAQoFCBEKAgICAgEFAgQBCgIDDAMCCAECCAMBAwIHCwQBAgIIFAMICgECAQQCAwUCAQIBBAECAgQYAwkDAQEBAw0CDgQCAwEEAwUCBggEAgIBCAQEBwgFBwwEBAICAgYBBQQDAgMFBwQDAhIBBAICBQwCCQICCggFCQIIBAIKCQ0JaXJRAQwBDQEEAxUBAwUCAwICAQUMCAMEBQEKAQMBAQQIBAoBBwYCCgIEAQwBAQICBAsPAQIJCgEDC3TE6sR0dMTqxHT+3QEIAgYGAQQIAwULAQwCAgQMAQoHAgMEAgQBAgYMBQYDCgEGBAEBAgICAQMDAgEDCAQCBgIDAwQFBAYHBAYICgcEBQYFDAMBAgQCAQMMCQ4DBAUHCAUDEQIDDgcGDAMBAwkCBwoDBgEOBAoEAQIFAgIGCgQHBwcBCQUIBwgDAgcDAgQCBgIEBQoDAw4CBQEBAgUEBwIBCggPAQMCAgcEAw4DAgQDBwMGBAQBAS1PBAEIBAMEBg8KAgYEBQQFDgkUCwIBBhoCARcFBAYDBRQDAxAFAgEECAUIBAELFw4FDAICBAQMCA4EDgEKCxQHCAEFAw0CAQIBEgMKBAQJBQYCAwoDAgMFDAIQCRMDAwQEBgIECgcOAQUCBAEEAgIQBQ8FAgUDAgsCCAQEAgIEGA4JDgUJAQQGAQIDAQEBBAMGBwYFAg8KAQQBAgMBAgMIBRcEAggIAwQPAgoKBQECAwQLCQUCAgICBgIKBwYFBAQEAwEECgQGAQcCAQcGBQMEAQEBBQQC/g0VVQIC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IABiAHkAIABzAHYAZwAyAHQAdABmACAAZgByAG8AbQAgAEYAbwBuAHQAZQBsAGwAbwAgAHAAcgBvAGoAZQBjAHQALgBoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAAAAAAIAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAggAAAQIBAwEEAQUBBgEHAQgBCQEKAQsBDAENAQ4BDwEQAREBEgETARQBFQEWARcBGAEZARoBGwEcAR0BHgEfASABIQEiASMBJAElASYBJwEoASkBKgErASwBLQEuAS8BMAExATIBMwE0ATUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAUEBQgFDAUQBRQFGAUcBSAFJAUoBSwFMAU0BTgFPAVABUQFSAVMBVAFVAVYBVwFYAVkBWgFbAVwBXQFeAV8BYAFhAWIBYwFkAWUBZgFnAWgBaQFqAWsBbAFtAW4BbwFwAXEBcgFzAXQBdQF2AXcBeAF5AXoBewF8AX0BfgF/AYABgQGCCWRhc2hib2FyZAR1c2VyBXVzZXJzAm9rBmNhbmNlbARwbHVzBW1pbnVzDGZvbGRlci1lbXB0eQhkb3dubG9hZAZ1cGxvYWQDZ2l0BWN1YmVzCGRhdGFiYXNlBWdhdWdlB3NpdGVtYXAMc29ydC1uYW1lLXVwDnNvcnQtbmFtZS1kb3duCW1lZ2FwaG9uZQNidWcFdGFza3MGZmlsdGVyA29mZgRib29rBXBhc3RlCHNjaXNzb3JzBWdsb2JlBWNsb3VkBWZsYXNoCGJhcmNoYXJ0CGRvd24tZGlyBnVwLWRpcghsZWZ0LWRpcglyaWdodC1kaXIJZG93bi1vcGVuCnJpZ2h0LW9wZW4HdXAtb3BlbglsZWZ0LW9wZW4GdXAtYmlnCXJpZ2h0LWJpZwhsZWZ0LWJpZwhkb3duLWJpZw9yZXNpemUtZnVsbC1hbHQLcmVzaXplLWZ1bGwMcmVzaXplLXNtYWxsBG1vdmURcmVzaXplLWhvcml6b250YWwPcmVzaXplLXZlcnRpY2FsB3pvb20taW4FYmxvY2sIem9vbS1vdXQJbGlnaHRidWxiBWNsb2NrCXZvbHVtZS11cAt2b2x1bWUtZG93bgp2b2x1bWUtb2ZmBG11dGUDbWljB2VuZHRpbWUJc3RhcnR0aW1lDmNhbGVuZGFyLWVtcHR5CGNhbGVuZGFyBndyZW5jaAdzbGlkZXJzCHNlcnZpY2VzB3NlcnZpY2UFcGhvbmUIZmlsZS1wZGYJZmlsZS13b3JkCmZpbGUtZXhjZWwIZG9jLXRleHQFdHJhc2gNY29tbWVudC1lbXB0eQdjb21tZW50BGNoYXQKY2hhdC1lbXB0eQRiZWxsCGJlbGwtYWx0DWF0dGVudGlvbi1hbHQFcHJpbnQEZWRpdAdmb3J3YXJkBXJlcGx5CXJlcGx5LWFsbANleWUDdGFnBHRhZ3MNbG9jay1vcGVuLWFsdAlsb2NrLW9wZW4EbG9jawRob21lBGluZm8EaGVscAZzZWFyY2gIZmxhcHBpbmcGcmV3aW5kCmNoYXJ0LWxpbmUIYmVsbC1vZmYOYmVsbC1vZmYtZW1wdHkEcGx1ZwdleWUtb2ZmCnJlc2NoZWR1bGUCY3cEaG9zdAl0aHVtYnMtdXALdGh1bWJzLWRvd24Hc3Bpbm5lcgZhdHRhY2gIa2V5Ym9hcmQEbWVudQR3aWZpBG1vb24JY2hhcnQtcGllCmNoYXJ0LWFyZWEJY2hhcnQtYmFyBmJlYWtlcgVtYWdpYwVzcGluNgpkb3duLXNtYWxsCmxlZnQtc21hbGwLcmlnaHQtc21hbGwIdXAtc21hbGwDcGluEWFuZ2xlLWRvdWJsZS1sZWZ0EmFuZ2xlLWRvdWJsZS1yaWdodAZjaXJjbGUMaW5mby1jaXJjbGVkB3R3aXR0ZXIQZmFjZWJvb2stc3F1YXJlZA1ncGx1cy1zcXVhcmVkAAAAAAEAAf//AA8AAAAAAAAAAAAAAACwACwgsABVWEVZICBLuAAOUUuwBlNaWLA0G7AoWWBmIIpVWLACJWG5CAAIAGNjI2IbISGwAFmwAEMjRLIAAQBDYEItsAEssCBgZi2wAiwgZCCwwFCwBCZasigBCkNFY0VSW1ghIyEbilggsFBQWCGwQFkbILA4UFghsDhZWSCxAQpDRWNFYWSwKFBYIbEBCkNFY0UgsDBQWCGwMFkbILDAUFggZiCKimEgsApQWGAbILAgUFghsApgGyCwNlBYIbA2YBtgWVlZG7ABK1lZI7AAUFhlWVktsAMsIEUgsAQlYWQgsAVDUFiwBSNCsAYjQhshIVmwAWAtsAQsIyEjISBksQViQiCwBiNCsQEK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') format('truetype'); + src: url('data:application/octet-stream;base64,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') format('woff'), + url('data:application/octet-stream;base64,AAEAAAAPAIAAAwBwR1NVQiCMJXkAAAD8AAAAVE9TLzI+IUl8AAABUAAAAFZjbWFwAgQB6QAAAagAAAh+Y3Z0IAbX/wIAAJl0AAAAIGZwZ22KkZBZAACZlAAAC3BnYXNwAAAAEAAAmWwAAAAIZ2x5ZpnPmdsAAAooAACDemhlYWQMYC7SAACNpAAAADZoaGVhB/cE1wAAjdwAAAAkaG10eL/u/5oAAI4AAAACDGxvY2HLTu4vAACQDAAAAQhtYXhwAfoNvgAAkRQAAAAgbmFtZcQUf/oAAJE0AAACqXBvc3SU8OH1AACT4AAABYxwcmVw5UErvAAApQQAAACGAAEAAAAKADAAPgACbGF0bgAOREZMVAAaAAQAAAAAAAAAAQAAAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAEDawGQAAUAAAJ6ArwAAACMAnoCvAAAAeAAMQECAAACAAUDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBmRWQAQOgA6IEDUv9qAFoDUwCXAAAAAQAAAAAAAAAAAAUAAAADAAAALAAAAAQAAAJWAAEAAAAAAVAAAwABAAAALAADAAoAAAJWAAQBJAAAAAQABAABAADogf//AADoAP//AAAAAQAEAAAAAQACAAMABAAFAAYABwAIAAkACgALAAwADQAOAA8AEAARABIAEwAUABUAFgAXABgAGQAaABsAHAAdAB4AHwAgACEAIgAjACQAJQAmACcAKAApACoAKwAsAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAOgA7ADwAPQA+AD8AQABBAEIAQwBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAWwBcAF0AXgBfAGAAYQBiAGMAZABlAGYAZwBoAGkAagBrAGwAbQBuAG8AcABxAHIAcwB0AHUAdgB3AHgAeQB6AHsAfAB9AH4AfwCAAIEAggAAAQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAGKAAAAAAAAACCAADoAAAA6AAAAAABAADoAQAA6AEAAAACAADoAgAA6AIAAAADAADoAwAA6AMAAAAEAADoBAAA6AQAAAAFAADoBQAA6AUAAAAGAADoBgAA6AYAAAAHAADoBwAA6AcAAAAIAADoCAAA6AgAAAAJAADoCQAA6AkAAAAKAADoCgAA6AoAAAALAADoCwAA6AsAAAAMAADoDAAA6AwAAAANAADoDQAA6A0AAAAOAADoDgAA6A4AAAAPAADoDwAA6A8AAAAQAADoEAAA6BAAAAARAADoEQAA6BEAAAASAADoEgAA6BIAAAATAADoEwAA6BMAAAAUAADoFAAA6BQAAAAVAADoFQAA6BUAAAAWAADoFgAA6BYAAAAXAADoFwAA6BcAAAAYAADoGAAA6BgAAAAZAADoGQAA6BkAAAAaAADoGgAA6BoAAAAbAADoGwAA6BsAAAAcAADoHAAA6BwAAAAdAADoHQAA6B0AAAAeAADoHgAA6B4AAAAfAADoHwAA6B8AAAAgAADoIAAA6CAAAAAhAADoIQAA6CEAAAAiAADoIgAA6CIAAAAjAADoIwAA6CMAAAAkAADoJAAA6CQAAAAlAADoJQAA6CUAAAAmAADoJgAA6CYAAAAnAADoJwAA6CcAAAAoAADoKAAA6CgAAAApAADoKQAA6CkAAAAqAADoKgAA6CoAAAArAADoKwAA6CsAAAAsAADoLAAA6CwAAAAtAADoLQAA6C0AAAAuAADoLgAA6C4AAAAvAADoLwAA6C8AAAAwAADoMAAA6DAAAAAxAADoMQAA6DEAAAAyAADoMgAA6DIAAAAzAADoMwAA6DMAAAA0AADoNAAA6DQAAAA1AADoNQAA6DUAAAA2AADoNgAA6DYAAAA3AADoNwAA6DcAAAA4AADoOAAA6DgAAAA5AADoOQAA6DkAAAA6AADoOgAA6DoAAAA7AADoOwAA6DsAAAA8AADoPAAA6DwAAAA9AADoPQAA6D0AAAA+AADoPgAA6D4AAAA/AADoPwAA6D8AAABAAADoQAAA6EAAAABBAADoQQAA6EEAAABCAADoQgAA6EIAAABDAADoQwAA6EMAAABEAADoRAAA6EQAAABFAADoRQAA6EUAAABGAADoRgAA6EYAAABHAADoRwAA6EcAAABIAADoSAAA6EgAAABJAADoSQAA6EkAAABKAADoSgAA6EoAAABLAADoSwAA6EsAAABMAADoTAAA6EwAAABNAADoTQAA6E0AAABOAADoTgAA6E4AAABPAADoTwAA6E8AAABQAADoUAAA6FAAAABRAADoUQAA6FEAAABSAADoUgAA6FIAAABTAADoUwAA6FMAAABUAADoVAAA6FQAAABVAADoVQAA6FUAAABWAADoVgAA6FYAAABXAADoVwAA6FcAAABYAADoWAAA6FgAAABZAADoWQAA6FkAAABaAADoWgAA6FoAAABbAADoWwAA6FsAAABcAADoXAAA6FwAAABdAADoXQAA6F0AAABeAADoXgAA6F4AAABfAADoXwAA6F8AAABgAADoYAAA6GAAAABhAADoYQAA6GEAAABiAADoYgAA6GIAAABjAADoYwAA6GMAAABkAADoZAAA6GQAAABlAADoZQAA6GUAAABmAADoZgAA6GYAAABnAADoZwAA6GcAAABoAADoaAAA6GgAAABpAADoaQAA6GkAAABqAADoagAA6GoAAABrAADoawAA6GsAAABsAADobAAA6GwAAABtAADobQAA6G0AAABuAADobgAA6G4AAABvAADobwAA6G8AAABwAADocAAA6HAAAABxAADocQAA6HEAAAByAADocgAA6HIAAABzAADocwAA6HMAAAB0AADodAAA6HQAAAB1AADodQAA6HUAAAB2AADodgAA6HYAAAB3AADodwAA6HcAAAB4AADoeAAA6HgAAAB5AADoeQAA6HkAAAB6AADoegAA6HoAAAB7AADoewAA6HsAAAB8AADofAAA6HwAAAB9AADofQAA6H0AAAB+AADofgAA6H4AAAB/AADofwAA6H8AAACAAADogAAA6IAAAACBAADogQAA6IEAAACCAAAACQAA//kD6AMLAA8AHwAvAD8ATwBfAG8AfwCPAE9ATBENAgcQDAIGAwcGYA8JAgMOCAICAQMCYAsFAgEAAAFUCwUCAQEAWAoEAgABAEyOi4aDfnt2c25rZmNeW1ZTTks1NTU1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IxY3Nic2JiczMjU2JyYDNjcmIi8BNiYvASYvASYPASIPARUmJyIuAQ4BDwEmNiYGDwEGNgYVDgEVLgE3HgEXFgcGBwYXFAYWAa10xnJyxujIbgZ6vAETAggDAQIEAxEVEwoBDAIIBgMBBwYEBAoFBgQBCAECAQMDBAQEBAYBBgIICQUEBgIEAwEIDAEFHAQDAgIBCAEOAQIHCQMEBAEEAgMBBwoCBAUNAwMUDhMECAYBAgECBQkCARMJBgQCBQYKAwgEBwUCAwYJBAYBBQkEBQMDAgUEAQ4HCw8EEAMDAQgECAEIAwEIBAMCAgMEAgQSBQMMDAEDAwIMGRsDBgUFEwUDCwQNCwEEAgYECAQJBFEyBAUCBgUDARgKAQIHBQQDBAQEAQIBAQECCgcHEgQHCQQDCAQCDgEBAgIOAgQCAg8IAwQDAgMFAQQKCgEECAQFDAcCAwgDCQcWBgYFCAgQBBQKAQIEAgYDDgMEAQoFCBEKAgICAgEFAgQBCgIDDAMCCAECCAMBAwIHCwQBAgIIFAMICgECAQQCAwUCAQMCAQMBBBgDCQMBAQEDDQIOBAIDAQQDBQIGCAQCAgEIBAQHCAUHDAQEAgICBgEFBAMCAwUMBAISAQQCAgUOCQICCggFCQIGBgcFCQwKaXNQAQwBDQEEAxUBAwUCAwICAQUMCAMGBgYGAQEECAQKAQcGAgoCBAEMAQECAgQLDwECCQoBAwt0xOrEdHTE6sR0/t0BCAIGBgEECAMFCwEMAQMCAgwBCgcCAwQCBAECBgwFBgMDAgQBAQMDBAIEAQMDAgIIBAIGBAEDBAEEBAYHAwgHCgcEBQYFDAMBAgQCAQMMCQ4DBAUHCAUDEQIDDggFDAMBAwkJBgQDBgEOBAoEAQIFAgIGCgQHBwcBCQUIBwgDAgcDAgQCBgIEBQoDAw4CBQICBQQHAgEKCA8CAwMHAwIOAwIDBAYEBgQEAQEtTwQBCAQDBAYPCgIGBAUEBQ4JFAsCAQYaAgEXBQQGAwUUAwMQBQIBBAgFCAQBCxgNBQwCAgQEDAgOBA4BCgsUBwgBBQMNAgECARIDCgQECQUGAgMKAwIDBQwCEAgSAwMEBAYCBAoHDgEFAgQBBAICEAUPBQIFAwILAggEBAICBBgOCQ4FCQEEBgECAwIBBAMGBwYFAg8KAQQBAgMBAgMIBRcEAggIAwUOAgoK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cAByAG8AagBlAGMAdAAuAGgAdAB0AHAAOgAvAC8AZgBvAG4AdABlAGwAbABvAC4AYwBvAG0AAAAAAgAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDAQIBAwEEAQUBBgEHAQgBCQEKAQsBDAENAQ4BDwEQAREBEgETARQBFQEWARcBGAEZARoBGwEcAR0BHgEfASABIQEiASMBJAElASYBJwEoASkBKgErASwBLQEuAS8BMAExATIBMwE0ATUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAUEBQgFDAUQBRQFGAUcBSAFJAUoBSwFMAU0BTgFPAVABUQFSAVMBVAFVAVYBVwFYAVkBWgFbAVwBXQFeAV8BYAFhAWIBYwFkAWUBZgFnAWgBaQFqAWsBbAFtAW4BbwFwAXEBcgFzAXQBdQF2AXcBeAF5AXoBewF8AX0BfgF/AYABgQGCAYMBhAAJZGFzaGJvYXJkBHVzZXIFdXNlcnMCb2sGY2FuY2VsBHBsdXMFbWludXMMZm9sZGVyLWVtcHR5CGRvd25sb2FkBnVwbG9hZANnaXQFY3ViZXMIZGF0YWJhc2UFZ2F1Z2UHc2l0ZW1hcAxzb3J0LW5hbWUtdXAOc29ydC1uYW1lLWRvd24JbWVnYXBob25lA2J1ZwV0YXNrcwZmaWx0ZXIDb2ZmBGJvb2sFcGFzdGUIc2Npc3NvcnMFZ2xvYmUFY2xvdWQFZmxhc2gIYmFyY2hhcnQIZG93bi1kaXIGdXAtZGlyCGxlZnQtZGlyCXJpZ2h0LWRpcglkb3duLW9wZW4KcmlnaHQtb3Blbgd1cC1vcGVuCWxlZnQtb3BlbgZ1cC1iaWcJcmlnaHQtYmlnCGxlZnQtYmlnCGRvd24tYmlnD3Jlc2l6ZS1mdWxsLWFsdAtyZXNpemUtZnVsbAxyZXNpemUtc21hbGwEbW92ZRFyZXNpemUtaG9yaXpvbnRhbA9yZXNpemUtdmVydGljYWwHem9vbS1pbgVibG9jawh6b29tLW91dAlsaWdodGJ1bGIFY2xvY2sJdm9sdW1lLXVwC3ZvbHVtZS1kb3duCnZvbHVtZS1vZmYEbXV0ZQNtaWMHZW5kdGltZQlzdGFydHRpbWUOY2FsZW5kYXItZW1wdHkIY2FsZW5kYXIGd3JlbmNoB3NsaWRlcnMIc2VydmljZXMHc2VydmljZQVwaG9uZQhmaWxlLXBkZglmaWxlLXdvcmQKZmlsZS1leGNlbAhkb2MtdGV4dAV0cmFzaA1jb21tZW50LWVtcHR5B2NvbW1lbnQEY2hhdApjaGF0LWVtcHR5BGJlbGwIYmVsbC1hbHQNYXR0ZW50aW9uLWFsdAVwcmludARlZGl0B2ZvcndhcmQFcmVwbHkJcmVwbHktYWxsA2V5ZQN0YWcEdGFncw1sb2NrLW9wZW4tYWx0CWxvY2stb3BlbgRsb2NrBGhvbWUEaW5mbwRoZWxwBnNlYXJjaAhmbGFwcGluZwZyZXdpbmQKY2hhcnQtbGluZQhiZWxsLW9mZg5iZWxsLW9mZi1lbXB0eQRwbHVnB2V5ZS1vZmYKcmVzY2hlZHVsZQJjdwRob3N0CXRodW1icy11cAt0aHVtYnMtZG93bgdzcGlubmVyBmF0dGFjaAhrZXlib2FyZARtZW51BHdpZmkEbW9vbgljaGFydC1waWUKY2hhcnQtYXJlYQljaGFydC1iYXIGYmVha2VyBW1hZ2ljBXNwaW42CmRvd24tc21hbGwKbGVmdC1zbWFsbAtyaWdodC1zbWFsbAh1cC1zbWFsbANwaW4RYW5nbGUtZG91YmxlLWxlZnQSYW5nbGUtZG91YmxlLXJpZ2h0BmNpcmNsZQxpbmZvLWNpcmNsZWQHdHdpdHRlchBmYWNlYm9vay1zcXVhcmVkDWdwbHVzLXNxdWFyZWQNd2FybmluZy1lbXB0eQAAAAEAAf//AA8AAAAAAAAAAAAAAAAAAAAAABgAGAAYABgDU/9pA1P/abAALCCwAFVYRVkgIEu4AA5RS7AGU1pYsDQbsChZYGYgilVYsAIlYbkIAAgAY2MjYhshIbAAWbAAQyNEsgABAENgQi2wASywIGBmLbACLCBkILDAULAEJlqyKAEKQ0VjRVJbWCEjIRuKWCCwUFBYIbBAWRsgsDhQWCGwOFlZILEBCkNFY0VhZLAoUFghsQEKQ0VjRSCwMFBYIbAwWRsgsMBQWCBmIIqKYSCwClBYYBsgsCBQWCGwCmAbILA2UFghsDZgG2BZWVkbsAErWVkjsABQWGVZWS2wAywgRSCwBCVhZCCwBUNQWLAFI0KwBiNCGyEhWbABYC2wBCwjISMhIGSxBWJCILAGI0KxAQpDRWOxAQpDsAFgRWOwAyoh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WCA8WS6xKwEUKy2wPiywNSsjIC5GsAIlRlJYIDxZLrErARQrLbA/LLA2K4ogIDywBCNCijgjIC5GsAIlRlJYIDxZLrErARQrsARDLrArKy2wQCywABawBCWwBCYgLkcjRyNhsAlDKyMgPCAuIzixKwEUKy2wQSyxCAQlQrAAFrAEJbAEJSAuRyNHI2EgsAQjQrAJQysgsGBQWCCwQFFYswIgAyAbswImAxpZQkIjIEewBEOwAmIgsABQWLBAYFlmsAFjYCCwASsgiophILACQ2BkI7ADQ2FkUFiwAkNhG7ADQ2BZsAMlsAJiILAAUFiwQGBZZrABY2GwAiVGYTgjIDwjOBshICBGI0ewASsjYTghWbErARQrLbBCLLA1Ky6xKwEUKy2wQyywNishIyAgPLAEI0IjOLErARQrsARDLrArKy2wRCywABUgR7AAI0KyAAEBFRQTLrAxKi2wRSywABUgR7AAI0KyAAEBFRQTLrAxKi2wRiyxAAEUE7AyKi2wRyywNCotsEgssAAWRSMgLiBGiiNhOLErARQrLbBJLLAII0KwSCstsEossgAAQSstsEsssgABQSstsEwssgEAQSstsE0ssgEBQSstsE4ssgAAQistsE8ssgABQistsFAssgEAQistsFEssgEBQistsFIssgAAPistsFMssgABPistsFQssgEAPistsFUssgEBPistsFYssgAAQCstsFcssgABQCstsFgssgEAQCstsFkssgEBQCstsFossgAAQystsFsssgABQystsFwssgEAQystsF0ssgEBQystsF4ssgAAPystsF8ssgABPystsGAssgEAPystsGEssgEBPystsGIssDcrLrErARQrLbBjLLA3K7A7Ky2wZCywNyuwPCstsGUssAAWsDcrsD0rLbBmLLA4Ky6xKwEUKy2wZyywOCuwOystsGgssDgrsDwrLbBpLLA4K7A9Ky2waiywOSsusSsBFCstsGsssDkrsDsrLbBsLLA5K7A8Ky2wbSywOSuwPSstsG4ssDorLrErARQrLbBvLLA6K7A7Ky2wcCywOiuwPCstsHEssDorsD0rLbByLLMJBAIDRVghGyMhWUIrsAhlsAMkUHiwARUwLQBLuADIUlixAQGOWbABuQgACABjcLEABUKyAAEAKrEABUKzCgIBCCqxAAVCsw4AAQgqsQAGQroCwAABAAkqsQAHQroAQAABAAkqsQMARLEkAYhRWLBAiFixA2REsSYBiFFYugiAAAEEQIhjVFixAwBEWVlZWbMMAgEMKrgB/4WwBI2xAgBEAAA=') format('truetype'); } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ @@ -17,7 +17,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'ifont'; - src: url('../font/ifont.svg?36375038#ifont') format('svg'); + src: url('../font/ifont.svg?58366739#ifont') format('svg'); } } */ @@ -180,4 +180,5 @@ .icon-info-circled:before { content: '\e87d'; } /* '' */ .icon-twitter:before { content: '\e87e'; } /* '' */ .icon-facebook-squared:before { content: '\e87f'; } /* '' */ -.icon-gplus-squared:before { content: '\e880'; } /* '' */ \ No newline at end of file +.icon-gplus-squared:before { content: '\e880'; } /* '' */ +.icon-warning-empty:before { content: '\e881'; } /* '' */ \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/ifont-ie7-codes.css b/application/fonts/fontello-ifont/css/ifont-ie7-codes.css old mode 100644 new mode 100755 index 660e08a3d..062775e5e --- a/application/fonts/fontello-ifont/css/ifont-ie7-codes.css +++ b/application/fonts/fontello-ifont/css/ifont-ie7-codes.css @@ -127,4 +127,5 @@ .icon-info-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-facebook-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-gplus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file +.icon-gplus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-warning-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/ifont-ie7.css b/application/fonts/fontello-ifont/css/ifont-ie7.css old mode 100644 new mode 100755 index a3c15c2d2..a39a9aebe --- a/application/fonts/fontello-ifont/css/ifont-ie7.css +++ b/application/fonts/fontello-ifont/css/ifont-ie7.css @@ -138,4 +138,5 @@ .icon-info-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-twitter { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-facebook-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-gplus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file +.icon-gplus-squared { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-warning-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/ifont.css b/application/fonts/fontello-ifont/css/ifont.css old mode 100644 new mode 100755 index 9eba001ae..e40ededd8 --- a/application/fonts/fontello-ifont/css/ifont.css +++ b/application/fonts/fontello-ifont/css/ifont.css @@ -1,10 +1,11 @@ @font-face { font-family: 'ifont'; - src: url('../font/ifont.eot?65389432'); - src: url('../font/ifont.eot?65389432#iefix') format('embedded-opentype'), - url('../font/ifont.woff?65389432') format('woff'), - url('../font/ifont.ttf?65389432') format('truetype'), - url('../font/ifont.svg?65389432#ifont') format('svg'); + src: url('../font/ifont.eot?88677687'); + src: url('../font/ifont.eot?88677687#iefix') format('embedded-opentype'), + url('../font/ifont.woff2?88677687') format('woff2'), + url('../font/ifont.woff?88677687') format('woff'), + url('../font/ifont.ttf?88677687') format('truetype'), + url('../font/ifont.svg?88677687#ifont') format('svg'); font-weight: normal; font-style: normal; } @@ -14,7 +15,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'ifont'; - src: url('../font/ifont.svg?65389432#ifont') format('svg'); + src: url('../font/ifont.svg?88677687#ifont') format('svg'); } } */ @@ -182,4 +183,5 @@ .icon-info-circled:before { content: '\e87d'; } /* '' */ .icon-twitter:before { content: '\e87e'; } /* '' */ .icon-facebook-squared:before { content: '\e87f'; } /* '' */ -.icon-gplus-squared:before { content: '\e880'; } /* '' */ \ No newline at end of file +.icon-gplus-squared:before { content: '\e880'; } /* '' */ +.icon-warning-empty:before { content: '\e881'; } /* '' */ \ No newline at end of file diff --git a/application/fonts/fontello-ifont/demo.html b/application/fonts/fontello-ifont/demo.html old mode 100644 new mode 100755 index 2b409f7c8..63c208ee1 --- a/application/fonts/fontello-ifont/demo.html +++ b/application/fonts/fontello-ifont/demo.html @@ -229,11 +229,11 @@ body { } @font-face { font-family: 'ifont'; - src: url('./font/ifont.eot?43849680'); - src: url('./font/ifont.eot?43849680#iefix') format('embedded-opentype'), - url('./font/ifont.woff?43849680') format('woff'), - url('./font/ifont.ttf?43849680') format('truetype'), - url('./font/ifont.svg?43849680#ifont') format('svg'); + src: url('./font/ifont.eot?37604890'); + src: url('./font/ifont.eot?37604890#iefix') format('embedded-opentype'), + url('./font/ifont.woff?37604890') format('woff'), + url('./font/ifont.ttf?37604890') format('truetype'), + url('./font/ifont.svg?37604890#ifont') format('svg'); font-weight: normal; font-style: normal; } @@ -279,7 +279,7 @@ body { -img('img/logo_icinga_big_dark.png', null, array('align' => 'right', 'width' => '75')) ?> +img('img/icinga-logo-big-dark.png', null, array('align' => 'right', 'width' => '75')) ?> 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 Date: Fri, 25 Nov 2016 12:10:46 +0100 Subject: [PATCH 299/337] Replace chat icon w/ chat-empty in the about page Signed-off-by: Eric Lippmann --- application/views/scripts/about/index.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/views/scripts/about/index.phtml b/application/views/scripts/about/index.phtml index 071a1eb31..c55d9bce6 100644 --- a/application/views/scripts/about/index.phtml +++ b/application/views/scripts/about/index.phtml @@ -76,7 +76,7 @@ null, array( 'target' => '_blank', - 'icon' => 'chat', + 'icon' => 'chat-empty', 'title' => $this->translate('Support / Mailinglists') ) ) ?> From 0bf2bb15d54082c265a7453066d29f9cc5f4b45f Mon Sep 17 00:00:00 2001 From: Florian Strohmaier Date: Fri, 25 Nov 2016 12:11:15 +0100 Subject: [PATCH 300/337] Replace reschedule icon w/ arrows-cw for check now Signed-off-by: Eric Lippmann --- .../application/forms/Command/Object/CheckNowCommandForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php b/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php index b7855294b..db0a619ac 100644 --- a/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/CheckNowCommandForm.php @@ -39,7 +39,7 @@ class CheckNowCommandForm extends ObjectsCommandForm ), 'escape' => false, 'ignore' => true, - 'label' => $this->getView()->icon('reschedule') . $this->translate('Check now'), + 'label' => $this->getView()->icon('arrows-cw') . $this->translate('Check now'), 'type' => 'submit', 'title' => $this->translate('Schedule the next active check to run immediately'), 'value' => $this->translate('Check now') From ca8f4d3b7eb069d3319daac57f57b8f1b57ce938 Mon Sep 17 00:00:00 2001 From: Florian Strohmaier Date: Fri, 25 Nov 2016 12:11:52 +0100 Subject: [PATCH 301/337] Replace reschedule icon w/ calendar-empty for reschedule check Signed-off-by: Eric Lippmann --- modules/monitoring/application/views/scripts/hosts/show.phtml | 4 ++-- .../monitoring/application/views/scripts/services/show.phtml | 4 ++-- .../views/scripts/show/components/checkstatistics.phtml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/monitoring/application/views/scripts/hosts/show.phtml b/modules/monitoring/application/views/scripts/hosts/show.phtml index 683680663..3edf0cbcb 100644 --- a/modules/monitoring/application/views/scripts/hosts/show.phtml +++ b/modules/monitoring/application/views/scripts/hosts/show.phtml @@ -189,8 +189,8 @@ $rescheduleAllLink, null, array( - 'icon' => 'reschedule', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'calendar-empty' ) ) ?> diff --git a/modules/monitoring/application/views/scripts/services/show.phtml b/modules/monitoring/application/views/scripts/services/show.phtml index 8e46f2e32..e64bb33cd 100644 --- a/modules/monitoring/application/views/scripts/services/show.phtml +++ b/modules/monitoring/application/views/scripts/services/show.phtml @@ -191,8 +191,8 @@ $rescheduleAllLink, null, array( - 'icon' => 'reschedule', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'calendar-empty' ) ) ?> diff --git a/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml b/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml index 8ae18c899..e37e30ac6 100644 --- a/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml +++ b/modules/monitoring/application/views/scripts/show/components/checkstatistics.phtml @@ -37,7 +37,7 @@ $activeChecksEnabled = (bool) $object->active_checks_enabled; array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'reschedule', + 'icon' => 'calendar-empty', 'title' => $this->translate( 'Schedule the next active check at a different time than the current one' ) @@ -51,7 +51,7 @@ $activeChecksEnabled = (bool) $object->active_checks_enabled; array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'reschedule', + 'icon' => 'calendar-empty', 'title' => $this->translate( 'Schedule the next active check at a different time than the current one' ) From 79ab6157a17702ac4b8d0bf3d6c0af0bfde32806 Mon Sep 17 00:00:00 2001 From: Florian Strohmaier Date: Tue, 22 Nov 2016 10:54:02 +0100 Subject: [PATCH 302/337] Replace reply icon w/ edit for process check result Signed-off-by: Eric Lippmann --- modules/monitoring/application/views/scripts/hosts/show.phtml | 4 ++-- .../monitoring/application/views/scripts/services/show.phtml | 4 ++-- .../application/views/scripts/show/components/command.phtml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/monitoring/application/views/scripts/hosts/show.phtml b/modules/monitoring/application/views/scripts/hosts/show.phtml index 3edf0cbcb..82b935a71 100644 --- a/modules/monitoring/application/views/scripts/hosts/show.phtml +++ b/modules/monitoring/application/views/scripts/hosts/show.phtml @@ -167,8 +167,8 @@ $processCheckResultAllLink, null, array( - 'icon' => 'reply', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'edit' ) ) ?> diff --git a/modules/monitoring/application/views/scripts/services/show.phtml b/modules/monitoring/application/views/scripts/services/show.phtml index e64bb33cd..a6b3a0a8e 100644 --- a/modules/monitoring/application/views/scripts/services/show.phtml +++ b/modules/monitoring/application/views/scripts/services/show.phtml @@ -169,8 +169,8 @@ $processCheckResultAllLink, null, array( - 'icon' => 'reply', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'edit' ) ) ?> diff --git a/modules/monitoring/application/views/scripts/show/components/command.phtml b/modules/monitoring/application/views/scripts/show/components/command.phtml index 2ba5e7bc8..4a47c77b1 100644 --- a/modules/monitoring/application/views/scripts/show/components/command.phtml +++ b/modules/monitoring/application/views/scripts/show/components/command.phtml @@ -24,7 +24,7 @@ if ($showInstance): ?> array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'reply', + 'icon' => 'edit', 'title' => $title ) ); @@ -36,7 +36,7 @@ if ($showInstance): ?> array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'reply', + 'icon' => 'edit', 'title' => $title ) ); From b9e7bb857fa4ce27aed8838cee300f0c97177e36 Mon Sep 17 00:00:00 2001 From: Florian Strohmaier Date: Mon, 21 Nov 2016 13:17:13 +0100 Subject: [PATCH 303/337] Use icon-history for history sidebar menu item Signed-off-by: Eric Lippmann --- modules/monitoring/configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/configuration.php b/modules/monitoring/configuration.php index c2b385c35..e0307afea 100644 --- a/modules/monitoring/configuration.php +++ b/modules/monitoring/configuration.php @@ -210,7 +210,7 @@ $section->add(N_('Downtimes'), array( * History Section */ $section = $this->menuSection(N_('History'), array( - 'icon' => 'rewind', + 'icon' => 'history', 'priority' => 90 )); $section->add(N_('Event Grid'), array( From d664248d9dec0d30aab28e55ea532dc2a80bbd52 Mon Sep 17 00:00:00 2001 From: Florian Strohmaier Date: Mon, 21 Nov 2016 13:50:34 +0100 Subject: [PATCH 304/337] Use icon attention-circled for Problems section Signed-off-by: Eric Lippmann --- modules/monitoring/configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/configuration.php b/modules/monitoring/configuration.php index e0307afea..bf956af76 100644 --- a/modules/monitoring/configuration.php +++ b/modules/monitoring/configuration.php @@ -126,7 +126,7 @@ $section = $this->menuSection(N_('Problems'), array( 'SummaryNavigationItemRenderer', 'state' => 'critical' ), - 'icon' => 'block', + 'icon' => 'attention-circled', 'priority' => 20 )); $section->add(N_('Host Problems'), array( From cfb4cd7ad1ef2d54f58de1f8a7f303b633d885a2 Mon Sep 17 00:00:00 2001 From: Florian Strohmaier Date: Mon, 21 Nov 2016 13:52:29 +0100 Subject: [PATCH 305/337] Use icon binoculars for overview section Signed-off-by: Eric Lippmann --- modules/monitoring/configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/configuration.php b/modules/monitoring/configuration.php index bf956af76..30c777873 100644 --- a/modules/monitoring/configuration.php +++ b/modules/monitoring/configuration.php @@ -166,7 +166,7 @@ $section->add(N_('Current Downtimes'), array( * Overview Section */ $section = $this->menuSection(N_('Overview'), array( - 'icon' => 'sitemap', + 'icon' => 'binoculars', 'priority' => 30 )); $section->add(N_('Tactical Overview'), array( From 3294f60ef4f82a585fbceaca9e8d3d7d9404c7a3 Mon Sep 17 00:00:00 2001 From: Florian Strohmaier Date: Fri, 25 Nov 2016 14:48:42 +0100 Subject: [PATCH 306/337] Replace icon ok w/ check for the acknowledge command Signed-off-by: Eric Lippmann --- modules/monitoring/application/views/scripts/hosts/show.phtml | 4 ++-- .../views/scripts/partials/object/quick-actions.phtml | 4 ++-- .../monitoring/application/views/scripts/services/show.phtml | 4 ++-- .../views/scripts/show/components/acknowledgement.phtml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/monitoring/application/views/scripts/hosts/show.phtml b/modules/monitoring/application/views/scripts/hosts/show.phtml index 82b935a71..d71ef92b3 100644 --- a/modules/monitoring/application/views/scripts/hosts/show.phtml +++ b/modules/monitoring/application/views/scripts/hosts/show.phtml @@ -31,8 +31,8 @@ $acknowledgeLink, null, array( - 'icon' => 'ok', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'check' ) ) ?> diff --git a/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml b/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml index d79c8363f..1b845f3ac 100644 --- a/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml +++ b/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml @@ -18,7 +18,7 @@ array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'ok', + 'icon' => 'edit', 'title' => $this->translate( 'Acknowledge this problem, suppress all future notifications for it and tag it as being handled' ) @@ -32,7 +32,7 @@ array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'ok', + 'icon' => 'edit', 'title' => $this->translate( 'Acknowledge this problem, suppress all future notifications for it and tag it as being handled' ) diff --git a/modules/monitoring/application/views/scripts/services/show.phtml b/modules/monitoring/application/views/scripts/services/show.phtml index a6b3a0a8e..f9b2c7b10 100644 --- a/modules/monitoring/application/views/scripts/services/show.phtml +++ b/modules/monitoring/application/views/scripts/services/show.phtml @@ -30,8 +30,8 @@ $acknowledgeLink, null, array( - 'icon' => 'ok', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'check' ) ) ?> diff --git a/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml b/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml index 568dc9062..9d1e195dc 100644 --- a/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml +++ b/modules/monitoring/application/views/scripts/show/components/acknowledgement.phtml @@ -80,7 +80,7 @@ $acknowledgement = $object->acknowledgement; array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'ok', + 'icon' => 'edit', 'title' => $this->translate( 'Acknowledge this problem, suppress all future notifications for it and tag it as being handled' ) From 6695e437abf2c23ff75d3f7d0e30b355f22fd701 Mon Sep 17 00:00:00 2001 From: Florian Strohmaier Date: Fri, 25 Nov 2016 14:53:50 +0100 Subject: [PATCH 307/337] Replace icon bell-alt w/ bell for the send notification command Signed-off-by: Eric Lippmann --- modules/monitoring/application/views/scripts/hosts/show.phtml | 4 ++-- .../views/scripts/partials/object/quick-actions.phtml | 4 ++-- .../monitoring/application/views/scripts/services/show.phtml | 4 ++-- .../views/scripts/show/components/notifications.phtml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/monitoring/application/views/scripts/hosts/show.phtml b/modules/monitoring/application/views/scripts/hosts/show.phtml index d71ef92b3..440b8f163 100644 --- a/modules/monitoring/application/views/scripts/hosts/show.phtml +++ b/modules/monitoring/application/views/scripts/hosts/show.phtml @@ -145,8 +145,8 @@ $sendCustomNotificationLink, null, array( - 'icon' => 'bell-alt', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'bell' ) ) ?> diff --git a/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml b/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml index 1b845f3ac..de0eb9244 100644 --- a/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml +++ b/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml @@ -83,7 +83,7 @@ array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'bell-alt', + 'icon' => 'bell', 'title' => $this->translate( 'Send a custom notification to contacts responsible for this host' ) @@ -97,7 +97,7 @@ array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'bell-alt', + 'icon' => 'bell', 'title' => $this->translate( 'Send a custom notification to contacts responsible for this service' ) diff --git a/modules/monitoring/application/views/scripts/services/show.phtml b/modules/monitoring/application/views/scripts/services/show.phtml index f9b2c7b10..15db9b758 100644 --- a/modules/monitoring/application/views/scripts/services/show.phtml +++ b/modules/monitoring/application/views/scripts/services/show.phtml @@ -147,8 +147,8 @@ $sendCustomNotificationLink, null, array( - 'icon' => 'bell-alt', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'bell' ) ) ?> diff --git a/modules/monitoring/application/views/scripts/show/components/notifications.phtml b/modules/monitoring/application/views/scripts/show/components/notifications.phtml index 8dbb43f93..3e8c66584 100644 --- a/modules/monitoring/application/views/scripts/show/components/notifications.phtml +++ b/modules/monitoring/application/views/scripts/show/components/notifications.phtml @@ -13,7 +13,7 @@ array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'bell-alt', + 'icon' => 'bell', 'title' => $this->translate( 'Send a custom notification to contacts responsible for this host' ) @@ -28,7 +28,7 @@ array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'bell-alt', + 'icon' => 'bell', 'title' => $this->translate( 'Send a custom notification to contacts responsible for this service' ) From b722f6e1782c4c9d878502908a0b6df0a0697c76 Mon Sep 17 00:00:00 2001 From: Florian Strohmaier Date: Fri, 25 Nov 2016 15:05:46 +0100 Subject: [PATCH 308/337] Replace icon comment w/ comment-empty Signed-off-by: Eric Lippmann --- .../monitoring/application/controllers/CommentController.php | 2 +- .../monitoring/application/controllers/CommentsController.php | 2 +- modules/monitoring/application/views/scripts/hosts/show.phtml | 4 ++-- .../application/views/scripts/partials/event-history.phtml | 2 +- .../views/scripts/partials/object/quick-actions.phtml | 4 ++-- .../monitoring/application/views/scripts/services/show.phtml | 4 ++-- .../application/views/scripts/show/components/comments.phtml | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/monitoring/application/controllers/CommentController.php b/modules/monitoring/application/controllers/CommentController.php index b5d5e6720..4e085c5b9 100644 --- a/modules/monitoring/application/controllers/CommentController.php +++ b/modules/monitoring/application/controllers/CommentController.php @@ -52,7 +52,7 @@ class CommentController extends Controller $this->getTabs()->add( 'comment', array( - 'icon' => 'comment', + 'icon' => 'comment-empty', 'label' => $this->translate('Comment'), 'title' => $this->translate('Display detailed information about a comment.'), 'url' =>'monitoring/comments/show' diff --git a/modules/monitoring/application/controllers/CommentsController.php b/modules/monitoring/application/controllers/CommentsController.php index 9375636dc..6dcf08ec4 100644 --- a/modules/monitoring/application/controllers/CommentsController.php +++ b/modules/monitoring/application/controllers/CommentsController.php @@ -59,7 +59,7 @@ class CommentsController extends Controller $this->getTabs()->add( 'comments', array( - 'icon' => 'comment', + 'icon' => 'comment-empty', 'label' => $this->translate('Comments') . sprintf(' (%d)', $query->count()), 'title' => $this->translate( 'Display detailed information about multiple comments.' diff --git a/modules/monitoring/application/views/scripts/hosts/show.phtml b/modules/monitoring/application/views/scripts/hosts/show.phtml index 440b8f163..48c4b5254 100644 --- a/modules/monitoring/application/views/scripts/hosts/show.phtml +++ b/modules/monitoring/application/views/scripts/hosts/show.phtml @@ -63,8 +63,8 @@ $addCommentLink, null, array( - 'icon' => 'comment', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'comment-empty' ) ) ?> diff --git a/modules/monitoring/application/views/scripts/partials/event-history.phtml b/modules/monitoring/application/views/scripts/partials/event-history.phtml index c83a8b8f8..0d9c64870 100644 --- a/modules/monitoring/application/views/scripts/partials/event-history.phtml +++ b/modules/monitoring/application/views/scripts/partials/event-history.phtml @@ -57,7 +57,7 @@ $lastDate = null; $stateName = $isService ? Service::getStateText($event->state) : Host::getStateText($event->state); break; case 'comment': - $icon = 'comment'; + $icon = 'comment-empty'; $iconTitle = $this->translate('Comment', 'tooltip'); $label = $this->translate('COMMENT'); break; diff --git a/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml b/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml index de0eb9244..5200923b9 100644 --- a/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml +++ b/modules/monitoring/application/views/scripts/partials/object/quick-actions.phtml @@ -54,7 +54,7 @@ array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'comment', + 'icon' => 'comment-empty', 'title' => $this->translate('Add a new comment to this host') ) ); @@ -66,7 +66,7 @@ array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'comment', + 'icon' => 'comment-empty', 'title' => $this->translate('Add a new comment to this service') ) ); diff --git a/modules/monitoring/application/views/scripts/services/show.phtml b/modules/monitoring/application/views/scripts/services/show.phtml index 15db9b758..05f17b871 100644 --- a/modules/monitoring/application/views/scripts/services/show.phtml +++ b/modules/monitoring/application/views/scripts/services/show.phtml @@ -62,8 +62,8 @@ $addCommentLink, null, array( - 'icon' => 'comment', - 'class' => 'action-link' + 'class' => 'action-link', + 'icon' => 'comment-empty' ) ) ?> diff --git a/modules/monitoring/application/views/scripts/show/components/comments.phtml b/modules/monitoring/application/views/scripts/show/components/comments.phtml index 3d5904ef4..d8cbdc910 100644 --- a/modules/monitoring/application/views/scripts/show/components/comments.phtml +++ b/modules/monitoring/application/views/scripts/show/components/comments.phtml @@ -10,7 +10,7 @@ if ($this->hasPermission('monitoring/command/comment/add')) { array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'comment', + 'icon' => 'comment-empty', 'title' => $this->translate('Add a new comment to this host') ) ); @@ -22,7 +22,7 @@ if ($this->hasPermission('monitoring/command/comment/add')) { array( 'class' => 'action-link', 'data-base-target' => '_self', - 'icon' => 'comment', + 'icon' => 'comment-empty', 'title' => $this->translate('Add a new comment to this service') ) ); From e532717226822b0c397627512504ed1140f3b548 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 25 Nov 2016 15:13:59 +0100 Subject: [PATCH 309/337] Fix quick actions icon alignment --- modules/monitoring/public/css/module.less | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/monitoring/public/css/module.less b/modules/monitoring/public/css/module.less index a9542ae73..b4922f635 100644 --- a/modules/monitoring/public/css/module.less +++ b/modules/monitoring/public/css/module.less @@ -145,11 +145,6 @@ border-right: none; padding-right: 0; } - - li i { - font-size: 1.2em; - vertical-align: middle; - } } /* Generic box element */ From 425c6da47f0c84d6334b4962936a744cf6d3933f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Nov 2016 14:53:01 +0100 Subject: [PATCH 310/337] CSS: Change the menu's search placeholder's color only when focussed --- public/css/icinga/menu.less | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/css/icinga/menu.less b/public/css/icinga/menu.less index bfe640890..2f52f67fd 100644 --- a/public/css/icinga/menu.less +++ b/public/css/icinga/menu.less @@ -154,11 +154,11 @@ #layout.hoveredmenu { #main { - z-index: 2; + z-index: 2; } #sidebar { - z-index: 3; + z-index: 3; } #menu { @@ -228,19 +228,19 @@ input[type=text].search-input { transition: none; } -.search-input:-moz-placeholder { // FF 18- +.search-input:focus:-moz-placeholder { // FF 18- color: @gray-light; } -.search-input::-moz-placeholder { // FF 19+ +.search-input:focus::-moz-placeholder { // FF 19+ color: @gray-light; } -.search-input:-ms-input-placeholder { +.search-input:focus:-ms-input-placeholder { color: @gray-light; } -.search-input::-webkit-input-placeholder { +.search-input:focus::-webkit-input-placeholder { color: @gray-light; } From a9461ada54cb0485f11c8a1328f7b28202536a78 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Nov 2016 14:56:17 +0100 Subject: [PATCH 311/337] Only render a menu item when it's not empty A menu item is not empty when it either has children or a defined URL. --- library/Icinga/Web/Navigation/NavigationItem.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Navigation/NavigationItem.php b/library/Icinga/Web/Navigation/NavigationItem.php index 0f31a4209..12979bd75 100644 --- a/library/Icinga/Web/Navigation/NavigationItem.php +++ b/library/Icinga/Web/Navigation/NavigationItem.php @@ -340,7 +340,7 @@ class NavigationItem implements IteratorAggregate */ public function hasChildren() { - return !$this->getChildren()->isEmpty(); + return ! $this->getChildren()->isEmpty(); } /** @@ -791,7 +791,7 @@ class NavigationItem implements IteratorAggregate public function getRender() { if ($this->render === null) { - return true; + return $this->getUrl() !== null; } return $this->render; From f6e0f503b3c0ac62906602138699464aeac7a93d Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 28 Nov 2016 14:57:05 +0100 Subject: [PATCH 312/337] Remove Alert Summary menu entry refs #12728 --- modules/monitoring/configuration.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/monitoring/configuration.php b/modules/monitoring/configuration.php index 30c777873..1b3433992 100644 --- a/modules/monitoring/configuration.php +++ b/modules/monitoring/configuration.php @@ -238,10 +238,6 @@ $section = $this->menuSection(N_('Reporting'), array( 'priority' => 100 )); -$section->add(N_('Alert Summary'), array( - 'url' => 'monitoring/alertsummary/index' -)); - /* * System Section */ From 0c2e8abc0e16f11605437ca0288306c653313651 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 29 Nov 2016 15:23:42 +0100 Subject: [PATCH 313/337] Reintroduce control separation --- modules/monitoring/application/views/scripts/host/show.phtml | 2 +- modules/monitoring/application/views/scripts/service/show.phtml | 2 +- public/css/icinga/layout-structure.less | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/views/scripts/host/show.phtml b/modules/monitoring/application/views/scripts/host/show.phtml index f5beb5a02..72f5af4a8 100644 --- a/modules/monitoring/application/views/scripts/host/show.phtml +++ b/modules/monitoring/application/views/scripts/host/show.phtml @@ -1,5 +1,5 @@ -
+
compact): ?> tabs ?> diff --git a/modules/monitoring/application/views/scripts/service/show.phtml b/modules/monitoring/application/views/scripts/service/show.phtml index 3fcdb2286..bc9c612b2 100644 --- a/modules/monitoring/application/views/scripts/service/show.phtml +++ b/modules/monitoring/application/views/scripts/service/show.phtml @@ -1,4 +1,4 @@ -
+
compact): ?> tabs ?> diff --git a/public/css/icinga/layout-structure.less b/public/css/icinga/layout-structure.less index 1a46d7bce..44d0a4dd7 100644 --- a/public/css/icinga/layout-structure.less +++ b/public/css/icinga/layout-structure.less @@ -112,6 +112,7 @@ html { } } +.controls-separated, .container .controls.separated { box-shadow: 0 3px 4px -4px rgba(0, 0, 0, 0.2); // border-bottom: 1px solid @gray-lightest; From 5a9540aa95a05a683c84666cb70f4a302974af7e Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 29 Nov 2016 15:24:13 +0100 Subject: [PATCH 314/337] Left-align quick actions --- modules/monitoring/public/css/module.less | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/modules/monitoring/public/css/module.less b/modules/monitoring/public/css/module.less index b4922f635..4aec39e23 100644 --- a/modules/monitoring/public/css/module.less +++ b/modules/monitoring/public/css/module.less @@ -129,20 +129,16 @@ // Quick actions .quick-actions { - ul { - // Center horizontally - display: table; - margin: 0 auto; - } - li { - border-right: 1px solid fade(@icinga-blue, 50%); color: @icinga-blue; padding: 0 0.6em; } + li:first-child { + padding-left: 0; + } + li:last-child { - border-right: none; padding-right: 0; } } From 39c7451664f950e6a5a25f72d1ace0f8dcc060d6 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 29 Nov 2016 15:12:33 +0100 Subject: [PATCH 315/337] Implement announcements refs #11198 --- .../controllers/AnnouncementsController.php | 100 +++++++++++ .../ApplicationStateController.php | 22 ++- application/controllers/LayoutController.php | 5 + .../AcknowledgeAnnouncementForm.php | 79 +++++++++ .../forms/Announcement/AnnouncementForm.php | 117 +++++++++++++ application/forms/Security/RoleForm.php | 3 + application/layouts/scripts/body.phtml | 3 + .../views/scripts/announcements/index.phtml | 61 +++++++ .../views/scripts/layout/announcements.phtml | 1 + library/Icinga/Application/Web.php | 9 +- library/Icinga/Web/Announcement.php | 158 +++++++++++++++++ .../Web/Announcement/AnnouncementCookie.php | 137 +++++++++++++++ .../AnnouncementIniRepository.php | 159 ++++++++++++++++++ library/Icinga/Web/Widget/Announcements.php | 54 ++++++ public/css/icinga/forms.less | 5 +- public/css/icinga/widgets.less | 45 +++++ public/js/icinga/loader.js | 4 + 17 files changed, 957 insertions(+), 5 deletions(-) create mode 100644 application/controllers/AnnouncementsController.php create mode 100644 application/forms/Announcement/AcknowledgeAnnouncementForm.php create mode 100644 application/forms/Announcement/AnnouncementForm.php create mode 100644 application/views/scripts/announcements/index.phtml create mode 100644 application/views/scripts/layout/announcements.phtml create mode 100644 library/Icinga/Web/Announcement.php create mode 100644 library/Icinga/Web/Announcement/AnnouncementCookie.php create mode 100644 library/Icinga/Web/Announcement/AnnouncementIniRepository.php create mode 100644 library/Icinga/Web/Widget/Announcements.php diff --git a/application/controllers/AnnouncementsController.php b/application/controllers/AnnouncementsController.php new file mode 100644 index 000000000..458b9a40a --- /dev/null +++ b/application/controllers/AnnouncementsController.php @@ -0,0 +1,100 @@ +getTabs()->add( + 'announcements', + array( + 'active' => true, + 'label' => $this->translate('Announcements'), + 'title' => $this->translate('List All Announcements'), + 'url' => Url::fromPath('announcements') + ) + ); + + $repo = new AnnouncementIniRepository(); + $this->view->announcements = $repo + ->select(array('id', 'author', 'message', 'start', 'end')) + ->order('start'); + } + + /** + * Create an announcement + */ + public function newAction() + { + $this->assertPermission('admin'); + + $form = $this->prepareForm()->add(); + $form->handleRequest(); + $this->renderForm($form, $this->translate('New Announcement')); + } + + /** + * Update an announcement + */ + public function updateAction() + { + $this->assertPermission('admin'); + + $form = $this->prepareForm()->edit($this->params->getRequired('id')); + try { + $form->handleRequest(); + } catch (NotFoundError $_) { + $this->httpNotFound($this->translate('Announcement not found')); + } + $this->renderForm($form, $this->translate('Update Announcement')); + } + + /** + * Remove an announcement + */ + public function removeAction() + { + $this->assertPermission('admin'); + + $form = $this->prepareForm()->remove($this->params->getRequired('id')); + try { + $form->handleRequest(); + } catch (NotFoundError $_) { + $this->httpNotFound($this->translate('Announcement not found')); + } + $this->renderForm($form, $this->translate('Remove Announcement')); + } + + public function acknowledgeAction() + { + $this->assertHttpMethod('POST'); + $this->getResponse()->setHeader('X-Icinga-Container', 'ignore', true); + $form = new AcknowledgeAnnouncementForm(); + $form->handleRequest(); + } + + /** + * Assert permission admin and return a prepared RepositoryForm + * + * @return AnnouncementForm + */ + protected function prepareForm() + { + $form = new AnnouncementForm(); + return $form + ->setRepository(new AnnouncementIniRepository()) + ->setRedirectUrl(Url::fromPath('announcements')); + } +} diff --git a/application/controllers/ApplicationStateController.php b/application/controllers/ApplicationStateController.php index caae26f54..cba019843 100644 --- a/application/controllers/ApplicationStateController.php +++ b/application/controllers/ApplicationStateController.php @@ -4,6 +4,8 @@ namespace Icinga\Controllers; use Icinga\Application\Icinga; +use Icinga\Web\Announcement\AnnouncementCookie; +use Icinga\Web\Announcement\AnnouncementIniRepository; use Icinga\Web\Controller; use Icinga\Web\Session; @@ -14,6 +16,7 @@ class ApplicationStateController extends Controller { public function indexAction() { + $this->_helper->layout()->disableLayout(); if (isset($_COOKIE['icingaweb2-session'])) { $last = (int) $_COOKIE['icingaweb2-session']; } else { @@ -34,6 +37,23 @@ class ApplicationStateController extends Controller ); $_COOKIE['icingaweb2-session'] = $now; } - Icinga::app()->getResponse()->setHeader('X-Icinga-Container', 'ignore', true); + $announcementCookie = new AnnouncementCookie(); + $announcementRepo = new AnnouncementIniRepository(); + if ($announcementCookie->getEtag() !== $announcementRepo->getEtag()) { + $announcementCookie + ->setEtag($announcementRepo->getEtag()) + ->setNextActive($announcementRepo->findNextActive()); + $this->getResponse()->setCookie($announcementCookie); + $this->getResponse()->setHeader('X-Icinga-Announcements', 'refresh', true); + } else { + $nextActive = $announcementCookie->getNextActive(); + if ($nextActive && $nextActive <= $now) { + $announcementCookie->setNextActive($announcementRepo->findNextActive()); + $this->getResponse()->setCookie($announcementCookie); + $this->getResponse()->setHeader('X-Icinga-Announcements', 'refresh', true); + } + } + + $this->getResponse()->setHeader('X-Icinga-Container', 'ignore', true); } } diff --git a/application/controllers/LayoutController.php b/application/controllers/LayoutController.php index f7bbd5102..32295a688 100644 --- a/application/controllers/LayoutController.php +++ b/application/controllers/LayoutController.php @@ -20,4 +20,9 @@ class LayoutController extends ActionController $this->_helper->layout()->disableLayout(); $this->view->menuRenderer = Icinga::app()->getMenu()->getRenderer(); } + + public function announcementsAction() + { + $this->_helper->layout()->disableLayout(); + } } diff --git a/application/forms/Announcement/AcknowledgeAnnouncementForm.php b/application/forms/Announcement/AcknowledgeAnnouncementForm.php new file mode 100644 index 000000000..f4b86fe73 --- /dev/null +++ b/application/forms/Announcement/AcknowledgeAnnouncementForm.php @@ -0,0 +1,79 @@ +setAction('announcements/acknowledge'); + $this->setAttrib('class', 'form-inline acknowledge-announcement-control'); + $this->setRedirectUrl('layout/announcements'); + } + + /** + * {@inheritdoc} + */ + public function addSubmitButton() + { + $this->addElement( + 'button', + 'btn_submit', + array( + 'class' => 'link-button spinner', + 'decorators' => array( + 'ViewHelper', + array('HtmlTag', array('tag' => 'div', 'class' => 'control-group form-controls')) + ), + 'escape' => false, + 'ignore' => true, + 'label' => $this->getView()->icon('cancel'), + 'title' => $this->translate('Acknowledge this announcement'), + 'type' => 'submit' + ) + ); + return $this; + } + + /** + * {@inheritdoc} + */ + public function createElements(array $formData = array()) + { + $this->addElements( + array( + array( + 'hidden', + 'hash', + array( + 'required' => true, + 'validators' => array('NotEmpty'), + 'decorators' => array('ViewHelper') + ) + ) + ) + ); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function onSuccess() + { + $cookie = new AnnouncementCookie(); + $acknowledged = $cookie->getAcknowledged(); + $acknowledged[] = $this->getElement('hash')->getValue(); + $cookie->setAcknowledged($acknowledged); + $this->getResponse()->setCookie($cookie); + return true; + } +} diff --git a/application/forms/Announcement/AnnouncementForm.php b/application/forms/Announcement/AnnouncementForm.php new file mode 100644 index 000000000..2c712337c --- /dev/null +++ b/application/forms/Announcement/AnnouncementForm.php @@ -0,0 +1,117 @@ +addElement( + 'text', + 'author', + array( + 'required' => true, + 'value' => Auth::getInstance()->getUser()->getUsername(), + 'disabled' => true + ) + ); + $this->addElement( + 'textarea', + 'message', + array( + 'required' => true, + 'label' => $this->translate('Message'), + 'description' => $this->translate('The message to display to users') + ) + ); + $this->addElement( + 'dateTimePicker', + 'start', + array( + 'required' => true, + 'label' => $this->translate('Start'), + 'description' => $this->translate('The time to display the announcement from') + ) + ); + $this->addElement( + 'dateTimePicker', + 'end', + array( + 'required' => true, + 'label' => $this->translate('End'), + 'description' => $this->translate('The time to display the announcement until') + ) + ); + + $this->setTitle($this->translate('Create a new announcement')); + $this->setSubmitLabel($this->translate('Create')); + } + + /** + * {@inheritDoc} + */ + protected function createUpdateElements(array $formData) + { + $this->createInsertElements($formData); + $this->setTitle(sprintf($this->translate('Edit announcement %s'), $this->getIdentifier())); + $this->setSubmitLabel($this->translate('Save')); + } + + /** + * {@inheritDoc} + */ + protected function createDeleteElements(array $formData) + { + $this->setTitle(sprintf($this->translate('Remove announcement %s?'), $this->getIdentifier())); + $this->setSubmitLabel($this->translate('Yes')); + } + + /** + * {@inheritDoc} + */ + protected function createFilter() + { + return Filter::where('id', $this->getIdentifier()); + } + + /** + * {@inheritDoc} + */ + protected function getInsertMessage($success) + { + return $success + ? $this->translate('Announcement created') + : $this->translate('Failed to create announcement'); + } + + /** + * {@inheritDoc} + */ + protected function getUpdateMessage($success) + { + return $success + ? $this->translate('Announcement updated') + : $this->translate('Failed to update announcement'); + } + + /** + * {@inheritDoc} + */ + protected function getDeleteMessage($success) + { + return $success + ? $this->translate('Announcement removed') + : $this->translate('Failed to remove announcement'); + } +} diff --git a/application/forms/Security/RoleForm.php b/application/forms/Security/RoleForm.php index 1873123c2..28330130f 100644 --- a/application/forms/Security/RoleForm.php +++ b/application/forms/Security/RoleForm.php @@ -44,6 +44,9 @@ class RoleForm extends ConfigForm ) . ' (application/stacktraces)', 'application/log' => $this->translate('Allow to view the application log') . ' (application/log)', + 'admin' => $this->translate( + 'Grant admin permissions, e.g. manage announcements' + ) . ' (admin)', 'config/*' => $this->translate('Allow config access') . ' (config/*)' ); diff --git a/application/layouts/scripts/body.phtml b/application/layouts/scripts/body.phtml index 0423fc453..792fa69d9 100644 --- a/application/layouts/scripts/body.phtml +++ b/application/layouts/scripts/body.phtml @@ -18,6 +18,9 @@ if ($this->layout()->autorefreshInterval) { ?>
' . t('Type', 'app.config.logging') . '' . $typeText . '' . ($type === 'syslog' ? 'Syslog' : t('File', 'app.config.logging.type')) . '
' . t('Level', 'app.config.logging') . '
' . t('Application Prefix') . '' . $this->data['generalConfig']['logging_application'] . ' - qlink( - '', - 'config/removeresource', - array('resource' => $name), - array( - 'class' => 'action-link', - 'icon' => 'cancel', - 'title' => sprintf($this->translate('Remove resource %s'), $name) - ) - ); - } - ?> + qlink( + '', + 'config/removeresource', + array('resource' => $name), + array( + 'class' => 'action-link', + 'icon' => 'cancel', + 'title' => sprintf($this->translate('Remove resource %s'), $name) + ) + ) ?>
+ + + + + + + + + + + + hasPermission('admin')): ?> + + + + + + + + + hasPermission('admin')): ?> + + + + + +
translate('Author') ?>translate('Message') ?>translate('Start') ?>translate('End') ?>
escape($announcement->author) ?>ellipsis($announcement->message, 100) ?>formatDateTime($announcement->start->getTimestamp()) ?>formatDateTime($announcement->end->getTimestamp()) ?>qlink( + null, + 'announcements/remove', + array('id' => $announcement->id), + array( + 'class' => 'action-link', + 'icon' => 'cancel', + 'title' => $this->translate('Remove this announcement') + ) + ) ?>
+ diff --git a/application/views/scripts/layout/announcements.phtml b/application/views/scripts/layout/announcements.phtml new file mode 100644 index 000000000..3be6b834a --- /dev/null +++ b/application/views/scripts/layout/announcements.phtml @@ -0,0 +1 @@ +widget('announcements') ?> diff --git a/library/Icinga/Application/Web.php b/library/Icinga/Application/Web.php index 1d8097af1..ad1ce30c3 100644 --- a/library/Icinga/Application/Web.php +++ b/library/Icinga/Application/Web.php @@ -304,7 +304,12 @@ class Web extends EmbeddedWeb 'about' => array( 'label' => t('About'), 'url' => 'about', - 'priority' => 701 + 'priority' => 700 + ), + 'announcements' => array( + 'label' => t('Announcements'), + 'url' => 'announcements', + 'priority' => 710 ) ) ), @@ -366,7 +371,7 @@ class Web extends EmbeddedWeb 'label' => t('Application Log'), 'url' => 'list/applicationlog', 'permission' => 'application/log', - 'priority' => 710 + 'priority' => 900 ); } } else { diff --git a/library/Icinga/Web/Announcement.php b/library/Icinga/Web/Announcement.php new file mode 100644 index 000000000..9835ce0ca --- /dev/null +++ b/library/Icinga/Web/Announcement.php @@ -0,0 +1,158 @@ + $value) { + $method = 'set' . ucfirst($key); + if (method_exists($this, $method)) { + $this->$method($value); + } + } + } + + /** + * Get the author of the acknowledged + * + * @return string + */ + public function getAuthor() + { + return $this->author; + } + + /** + * Set the author of the acknowledged + * + * @param string $author + * + * @return $this + */ + public function setAuthor($author) + { + $this->author = $author; + return $this; + } + + /** + * Get the message of the acknowledged + * + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * Set the message of the acknowledged + * + * @param string $message + * + * @return $this + */ + public function setMessage($message) + { + $this->message = $message; + $this->hash = null; + return $this; + } + + /** + * Get the start date and time of the acknowledged + * + * @return int + */ + public function getStart() + { + return $this->start; + } + + /** + * Set the start date and time of the acknowledged + * + * @param int $start + * + * @return $this + */ + public function setStart($start) + { + $this->start = $start; + return $this; + } + + /** + * Get the end date and time of the acknowledged + * + * @return int + */ + public function getEnd() + { + return $this->end; + } + + /** + * Set the end date and time of the acknowledged + * + * @param int $end + * + * @return $this + */ + public function setEnd($end) + { + $this->end = $end; + return $this; + } + + /** + * Get the hash of the acknowledgement + * + * @return string + */ + public function getHash() + { + if ($this->hash === null) { + $this->hash = md5($this->message); + } + return $this->hash; + } +} diff --git a/library/Icinga/Web/Announcement/AnnouncementCookie.php b/library/Icinga/Web/Announcement/AnnouncementCookie.php new file mode 100644 index 000000000..e1f4d479d --- /dev/null +++ b/library/Icinga/Web/Announcement/AnnouncementCookie.php @@ -0,0 +1,137 @@ +setExpire(2147483648); + if (isset($_COOKIE['icingaweb2-announcements'])) { + $cookie = json_decode($_COOKIE['icingaweb2-announcements'], true); + if ($cookie !== null) { + if (isset($cookie['acknowledged'])) { + $this->setAcknowledged($cookie['acknowledged']); + } + if (isset($cookie['etag'])) { + $this->setEtag($cookie['etag']); + } + if (isset($cookie['next'])) { + $this->setNextActive($cookie['next']); + } + } + } + } + + /** + * Get the hashes of the acknowledged announcements + * + * @return string[] + */ + public function getAcknowledged() + { + return $this->acknowledged; + } + + /** + * Set the hashes of the acknowledged announcements + * + * @param string[] $acknowledged + * + * @return $this + */ + public function setAcknowledged(array $acknowledged) + { + $this->acknowledged = $acknowledged; + return $this; + } + + /** + * Get the ETag + * + * @return string + */ + public function getEtag() + { + return $this->etag; + } + + /** + * Set the ETag + * + * @param string $etag + * + * @return $this + */ + public function setEtag($etag) + { + $this->etag = $etag; + return $this; + } + + /** + * Get the timestamp of the next active announcement + * + * @return int + */ + public function getNextActive() + { + return $this->nextActive; + } + + /** + * Set the timestamp of the next active announcement + * + * @param int $nextActive + * + * @return $this + */ + public function setNextActive($nextActive) + { + $this->nextActive = $nextActive; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getValue() + { + return json_encode(array( + 'acknowledged' => $this->getAcknowledged(), + 'etag' => $this->getEtag(), + 'next' => $this->getNextActive() + )); + } +} diff --git a/library/Icinga/Web/Announcement/AnnouncementIniRepository.php b/library/Icinga/Web/Announcement/AnnouncementIniRepository.php new file mode 100644 index 000000000..86ac88571 --- /dev/null +++ b/library/Icinga/Web/Announcement/AnnouncementIniRepository.php @@ -0,0 +1,159 @@ + array('id', 'author', 'message', 'hash', 'start', 'end')); + + /** + * {@inheritdoc} + */ + protected $triggers = array('announcement'); + + /** + * {@inheritDoc} + */ + protected $configs = array('announcement' => array( + 'name' => 'announcements', + 'keyColumn' => 'id' + )); + + /** + * {@inheritDoc} + */ + protected $conversionRules = array('announcement' => array( + 'start' => 'timestamp', + 'end' => 'timestamp' + )); + + /** + * Create a DateTime from a timestamp + * + * @param string $timestamp + * + * @return DateTime|null + */ + protected function retrieveTimestamp($timestamp) + { + if ($timestamp !== null) { + $dateTime = new DateTime(); + $dateTime->setTimestamp($timestamp); + return $dateTime; + } + return null; + } + + /** + * Get a DateTime's timestamp + * + * @param DateTime $datetime + * + * @return int|null + */ + protected function persistTimestamp(DateTime $datetime) + { + return $datetime === null ? null : $datetime->getTimestamp(); + } + + /** + * Before-insert trigger (per row) + * + * @param ConfigObject $new The original data to insert + * + * @return ConfigObject The eventually modified data to insert + */ + protected function onInsertAnnouncement(ConfigObject $new) + { + if (! isset($new->id)) { + $new->id = uniqid(); + } + if (! isset($new->hash)) { + $announcement = new Announcement($new->toArray()); + $new->hash = $announcement->getHash(); + } + + return $new; + } + + /** + * Before-update trigger (per row) + * + * @param ConfigObject $old The original data as currently stored + * @param ConfigObject $new The original data to update + * + * @return ConfigObject The eventually modified data to update + */ + protected function onUpdateAnnouncement(ConfigObject $old, ConfigObject $new) + { + if ($new->message !== $old->message) { + $announcement = new Announcement($new->toArray()); + $new->hash = $announcement->getHash(); + } + + return $new; + } + + /** + * Get the ETag of the announcements.ini file + * + * @return string + */ + public function getEtag() + { + $file = $this->getDataSource('announcement')->getConfigFile(); + $mtime = filemtime($file); + $size = filesize($file); + return hash('crc32', $mtime . $size); + } + + /** + * Get the query for all active announcements + * + * @return SimpleQuery + */ + public function findActive() + { + $now = new DateTime(); + $query = $this + ->select(array('hash', 'message')) + ->setFilter(new FilterAnd(array( + Filter::expression('start', '<=', $now), + Filter::expression('end', '>=', $now) + ))) + ->order('start'); + return $query; + } + + /** + * Get the timestamp of the next active announcement + * + * @return int|null + */ + public function findNextActive() + { + $now = new DateTime(); + $query = $this + ->select(array('start')) + ->setFilter(Filter::expression('start', '>', $now)) + ->order('start') + ->limit(1); + $nextActive = $query->fetchRow(); + return $nextActive !== false ? $nextActive->start->getTimestamp() : null; + } +} diff --git a/library/Icinga/Web/Widget/Announcements.php b/library/Icinga/Web/Widget/Announcements.php new file mode 100644 index 000000000..8481a2856 --- /dev/null +++ b/library/Icinga/Web/Widget/Announcements.php @@ -0,0 +1,54 @@ +getEtag(); + $cookie = new AnnouncementCookie(); + if ($cookie->getEtag() !== $etag) { + $cookie->setEtag($etag); + $cookie->setNextActive($repo->findNextActive()); + Icinga::app()->getResponse()->setCookie($cookie); + } + $acked = array(); + foreach ($cookie->getAcknowledged() as $hash) { + $acked[] = Filter::expression('hash', '!=', $hash); + } + $acked = Filter::matchAll($acked); + $announcements = $repo->findActive(); + $announcements->applyFilter($acked); + if ($announcements->hasResult()) { + $html = ''; + return $html; + } + // Force container update on XHR + return '
'; + } +} diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index 5b65945c5..0ac312314 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -64,8 +64,9 @@ input.search { } } -// TODO(el): .form-inline control-group { display: inline-block; } -form.inline { +// TODO(el): .form-controls-inline control-group { display: inline-block; } +form.inline, +.form-inline { .control-group { padding: 0; } diff --git a/public/css/icinga/widgets.less b/public/css/icinga/widgets.less index 55257e89d..ad5fd7432 100644 --- a/public/css/icinga/widgets.less +++ b/public/css/icinga/widgets.less @@ -1,5 +1,50 @@ /*! Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ +#announcements > ul { + background-color: @body-bg-color; + font-size: @font-size; + list-style: none; + margin: 0; + padding: 0; + + > li:before { + color: @icinga-blue; + content: "\e811"; + font-family: 'ifont'; + left: 1em; + margin-top: -0.8em; + padding: 0.3em; + position: absolute; + text-align: center; + top: 50%; + } + + > li { + border-bottom: 1px solid @gray-lighter; + padding: 1em 3em; + position: relative; + } + + > li .message { + display: inline-block; + vertical-align: middle; + } + + > li:last-child { + border-bottom: none; + } + + .acknowledge-announcement-control { + background: none; + border: none; + display: block; + margin-top: -0.6em; + position: absolute; + right: 1em; + top: 50%; + } +} + table.historycolorgrid { font-size: 1.5em; } diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index 096606291..45ea6e85e 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -445,6 +445,10 @@ return; } + if (req.getResponseHeader('X-Icinga-Announcements') === 'refresh') { + _this.loadUrl(_this.url('/layout/announcements'), $('#announcements')); + } + // div helps getting an XML tree var $resp = $('
' + req.responseText + '
'); var active = false; From e549c4424785e8ba779a3da502567325691808f0 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 29 Nov 2016 16:32:30 +0100 Subject: [PATCH 316/337] Don't show exception when no announcement has been created refs #11198 --- .../Web/Announcement/AnnouncementIniRepository.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Web/Announcement/AnnouncementIniRepository.php b/library/Icinga/Web/Announcement/AnnouncementIniRepository.php index 86ac88571..cda3ee845 100644 --- a/library/Icinga/Web/Announcement/AnnouncementIniRepository.php +++ b/library/Icinga/Web/Announcement/AnnouncementIniRepository.php @@ -117,9 +117,12 @@ class AnnouncementIniRepository extends IniRepository public function getEtag() { $file = $this->getDataSource('announcement')->getConfigFile(); - $mtime = filemtime($file); - $size = filesize($file); - return hash('crc32', $mtime . $size); + if (@is_readable($file)) { + $mtime = filemtime($file); + $size = filesize($file); + return hash('crc32', $mtime . $size); + } + return null; } /** From 2fa854b0a814a13cdd93fed1836c4a41d3194e61 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 1 Dec 2016 10:54:09 +0100 Subject: [PATCH 317/337] ConfigForm: Introduce and utilize method transformEmptyValuesToNull() This utility method serves as alternative for all previous custom solutions to prevent empty values from being persisted to INI files. Since the IniWriter now handles NULL correctly, we're able to refrain from using array_filter for this purpose which was the actual cause for the referenced bug. fixes #13357 --- application/controllers/ConfigController.php | 9 ++------- application/controllers/NavigationController.php | 9 ++------- .../controllers/UsergroupbackendController.php | 9 ++------- application/forms/Config/ResourceConfigForm.php | 4 ++-- application/forms/ConfigForm.php | 14 +++++++++++++- application/forms/Security/RoleForm.php | 2 +- .../application/controllers/ConfigController.php | 4 ++-- 7 files changed, 24 insertions(+), 27 deletions(-) diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index d22e3a680..997c6a708 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -215,7 +215,7 @@ class ConfigController extends Controller $form->setOnSuccess(function (UserBackendConfigForm $form) { try { - $form->add(array_filter($form->getValues())); + $form->add($form::transformEmptyValuesToNull($form->getValues())); } catch (Exception $e) { $form->error($e->getMessage()); return false; @@ -246,12 +246,7 @@ class ConfigController extends Controller $form->setIniConfig(Config::app('authentication')); $form->setOnSuccess(function (UserBackendConfigForm $form) use ($backendName) { try { - $form->edit($backendName, array_map( - function ($v) { - return $v !== '' ? $v : null; - }, - $form->getValues() - )); + $form->edit($backendName, $form::transformEmptyValuesToNull($form->getValues())); } catch (Exception $e) { $form->error($e->getMessage()); return false; diff --git a/application/controllers/NavigationController.php b/application/controllers/NavigationController.php index 221ff39f4..1d2d39f68 100644 --- a/application/controllers/NavigationController.php +++ b/application/controllers/NavigationController.php @@ -219,7 +219,7 @@ class NavigationController extends Controller $form->setDefaultUrl(rawurldecode($this->params->get('url', ''))); $form->setOnSuccess(function (NavigationConfigForm $form) { - $data = array_filter($form->getValues()); + $data = $form::transformEmptyValuesToNull($form->getValues()); try { $form->add($data); @@ -266,12 +266,7 @@ class NavigationController extends Controller $form->setUserConfig(Config::navigation($itemType, $itemOwner)); $form->setRedirectUrl($referrer === 'shared' ? 'navigation/shared' : 'navigation'); $form->setOnSuccess(function (NavigationConfigForm $form) use ($itemName) { - $data = array_map( - function ($v) { - return $v !== '' ? $v : null; - }, - $form->getValues() - ); + $data = $form::transformEmptyValuesToNull($form->getValues()); try { $form->edit($itemName, $data); diff --git a/application/controllers/UsergroupbackendController.php b/application/controllers/UsergroupbackendController.php index 14456fc01..a9e411c54 100644 --- a/application/controllers/UsergroupbackendController.php +++ b/application/controllers/UsergroupbackendController.php @@ -43,7 +43,7 @@ class UsergroupbackendController extends Controller $form->setIniConfig(Config::app('groups')); $form->setOnSuccess(function (UserGroupBackendForm $form) { try { - $form->add(array_filter($form->getValues())); + $form->add($form::transformEmptyValuesToNull($form->getValues())); } catch (Exception $e) { $form->error($e->getMessage()); return false; @@ -73,12 +73,7 @@ class UsergroupbackendController extends Controller $form->setIniConfig(Config::app('groups')); $form->setOnSuccess(function (UserGroupBackendForm $form) use ($backendName) { try { - $form->edit($backendName, array_map( - function ($v) { - return $v !== '' ? $v : null; - }, - $form->getValues() - )); + $form->edit($backendName, $form::transformEmptyValuesToNull($form->getValues())); } catch (Exception $e) { $form->error($e->getMessage()); return false; diff --git a/application/forms/Config/ResourceConfigForm.php b/application/forms/Config/ResourceConfigForm.php index dfa885295..bbf54cb19 100644 --- a/application/forms/Config/ResourceConfigForm.php +++ b/application/forms/Config/ResourceConfigForm.php @@ -181,10 +181,10 @@ class ResourceConfigForm extends ConfigForm return false; } } - $this->add(array_filter($this->getValues())); + $this->add(static::transformEmptyValuesToNull($this->getValues())); $message = $this->translate('Resource "%s" has been successfully created'); } else { // edit existing resource - $this->edit($resource, array_filter($this->getValues())); + $this->edit($resource, static::transformEmptyValuesToNull($this->getValues())); $message = $this->translate('Resource "%s" has been successfully changed'); } } catch (InvalidArgumentException $e) { diff --git a/application/forms/ConfigForm.php b/application/forms/ConfigForm.php index 74164be6f..b69da5a63 100644 --- a/application/forms/ConfigForm.php +++ b/application/forms/ConfigForm.php @@ -58,7 +58,7 @@ class ConfigForm extends Form { $sections = array(); foreach ($this->getValues() as $sectionAndPropertyName => $value) { - if ($value === '') { + if (empty($value)) { $value = null; // Causes the config writer to unset it } @@ -127,4 +127,16 @@ class ConfigForm extends Form { $config->saveIni(); } + + /** + * Transform all empty values of the given array to null + * + * @param array $values + * + * @return array + */ + public static function transformEmptyValuesToNull(array $values) + { + return array_map(function ($v) { return empty($v) ? null : $v; }, $values); + } } diff --git a/application/forms/Security/RoleForm.php b/application/forms/Security/RoleForm.php index 28330130f..de3f98abe 100644 --- a/application/forms/Security/RoleForm.php +++ b/application/forms/Security/RoleForm.php @@ -289,7 +289,7 @@ class RoleForm extends ConfigForm */ public function getValues($suppressArrayNotation = false) { - $values = array_filter(parent::getValues($suppressArrayNotation)); + $values = static::transformEmptyValuesToNull(parent::getValues($suppressArrayNotation)); if (isset($values['permissions'])) { $values['permissions'] = implode(', ', $values['permissions']); } diff --git a/modules/monitoring/application/controllers/ConfigController.php b/modules/monitoring/application/controllers/ConfigController.php index 09aeb7d30..33ddaeacf 100644 --- a/modules/monitoring/application/controllers/ConfigController.php +++ b/modules/monitoring/application/controllers/ConfigController.php @@ -106,7 +106,7 @@ class ConfigController extends Controller $form->setOnSuccess(function (BackendConfigForm $form) { try { - $form->add(array_filter($form->getValues())); + $form->add($form::transformEmptyValuesToNull($form->getValues())); } catch (Exception $e) { $form->error($e->getMessage()); return false; @@ -258,7 +258,7 @@ class ConfigController extends Controller ); $form->setOnSuccess(function (TransportConfigForm $form) { try { - $form->add(array_filter($form->getValues())); + $form->add($form::transformEmptyValuesToNull($form->getValues())); } catch (Exception $e) { $form->error($e->getMessage()); return false; From 65f0a4683f07adfc6d2b273df0b681cbb8dec38b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 2 Dec 2016 15:21:58 +0100 Subject: [PATCH 318/337] Doc: Allow to view documentation of disabled modules fixes #13387 --- modules/doc/application/controllers/ModuleController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/doc/application/controllers/ModuleController.php b/modules/doc/application/controllers/ModuleController.php index fafd94656..030e90465 100644 --- a/modules/doc/application/controllers/ModuleController.php +++ b/modules/doc/application/controllers/ModuleController.php @@ -91,7 +91,7 @@ class ModuleController extends DocController $module = $this->params->getRequired('moduleName'); $this->assertModuleInstalled($module); $moduleManager = Icinga::app()->getModuleManager(); - $name = $moduleManager->getModule($module)->getTitle(); + $name = $moduleManager->getModule($module, false)->getTitle(); try { $this->renderToc( $this->getPath($module, Icinga::app()->getModuleManager()->getModuleDir($module, '/doc')), From 4205eb4cc962536c3334018f65e39dfe32bcb083 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 5 Dec 2016 13:20:02 +0100 Subject: [PATCH 319/337] nl2br helper: convert also raw newlines --- library/Icinga/Web/View/helpers/string.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/View/helpers/string.php b/library/Icinga/Web/View/helpers/string.php index 07ff558f8..09a891d35 100644 --- a/library/Icinga/Web/View/helpers/string.php +++ b/library/Icinga/Web/View/helpers/string.php @@ -10,5 +10,5 @@ $this->addHelperFunction('ellipsis', function ($string, $maxLength, $ellipsis = }); $this->addHelperFunction('nl2br', function ($string) { - return str_replace(array('\r\n', '\r', '\n'), '
', $string); + return nl2br(str_replace(array('\r\n', '\r', '\n'), '
', $string), false); }); From cdc3d43a048800df5037616c4a79cc3c5a791d82 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 5 Dec 2016 13:20:27 +0100 Subject: [PATCH 320/337] Respect newlines in host and service notes fixes #12313 --- .../views/scripts/show/components/notes.phtml | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/modules/monitoring/application/views/scripts/show/components/notes.phtml b/modules/monitoring/application/views/scripts/show/components/notes.phtml index 25c1d8ecb..c4c12f80e 100644 --- a/modules/monitoring/application/views/scripts/show/components/notes.phtml +++ b/modules/monitoring/application/views/scripts/show/components/notes.phtml @@ -2,16 +2,10 @@ use Icinga\Web\Navigation\Navigation; -$navigation = new Navigation(); -//$navigation->load($object->getType() . '-note'); -foreach ($navigation as $item) { - $item->setObject($object); -} +/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */ -$notes = trim($object->getNotes()); -if ($notes) { - $navigation->addItem($notes); -} +$navigation = new Navigation(); +$notes = trim($object->notes); $links = $object->getNotesUrls(); if (! empty($links)) { @@ -35,11 +29,16 @@ if (! empty($links)) { } } -if ($navigation->isEmpty() || !$navigation->hasRenderableItems()) { +if (($navigation->isEmpty() || ! $navigation->hasRenderableItems()) && $notes === '') { return; } ?> - translate('Notes'); ?> - getRenderer()->setElementTag('td')->setCssClass('notes'); ?> + translate('Notes') ?> + + getRenderer() ?> + +

nl2br($this->escape($notes)) ?>

+ + \ No newline at end of file From f1051f0ea55d907fec1e569b69e72e4583901917 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 5 Dec 2016 12:41:35 +0100 Subject: [PATCH 321/337] Drop MonitoredObject::getNotes() --- modules/monitoring/library/Monitoring/Object/Host.php | 5 ----- .../library/Monitoring/Object/MonitoredObject.php | 7 ------- modules/monitoring/library/Monitoring/Object/Service.php | 5 ----- 3 files changed, 17 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Object/Host.php b/modules/monitoring/library/Monitoring/Object/Host.php index 357c775e9..7c6bd5324 100644 --- a/modules/monitoring/library/Monitoring/Object/Host.php +++ b/modules/monitoring/library/Monitoring/Object/Host.php @@ -202,9 +202,4 @@ class Host extends MonitoredObject MonitoredObject::parseAttributeUrls($this->host_notes_url) ); } - - public function getNotes() - { - return $this->host_notes; - } } diff --git a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php index 6fb73112f..421caaa3e 100644 --- a/modules/monitoring/library/Monitoring/Object/MonitoredObject.php +++ b/modules/monitoring/library/Monitoring/Object/MonitoredObject.php @@ -173,13 +173,6 @@ abstract class MonitoredObject implements Filterable */ abstract protected function getDataView(); - /** - * Get the notes for this monitored object - * - * @return string The notes as a string - */ - public abstract function getNotes(); - /** * Get all note urls configured for this monitored object * diff --git a/modules/monitoring/library/Monitoring/Object/Service.php b/modules/monitoring/library/Monitoring/Object/Service.php index 5d03b786d..c712beeb1 100644 --- a/modules/monitoring/library/Monitoring/Object/Service.php +++ b/modules/monitoring/library/Monitoring/Object/Service.php @@ -214,9 +214,4 @@ class Service extends MonitoredObject MonitoredObject::parseAttributeUrls($this->service_notes_url) ); } - - public function getNotes() - { - return $this->service_notes; - } } From 4eb61c2bcf9b86821e7bddb92e004da8c3a31ed6 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 6 Dec 2016 12:41:22 +0100 Subject: [PATCH 322/337] Revert breaking change in Auth::isAuthenticated() refs #12580 fixes #13281 --- library/Icinga/Application/Web.php | 1 - library/Icinga/Authentication/Auth.php | 25 ++++++++----------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/library/Icinga/Application/Web.php b/library/Icinga/Application/Web.php index ad1ce30c3..db480abde 100644 --- a/library/Icinga/Application/Web.php +++ b/library/Icinga/Application/Web.php @@ -416,7 +416,6 @@ class Web extends EmbeddedWeb private function setupUser() { $auth = Auth::getInstance(); - $auth->authenticate(); if (! $this->request->isXmlHttpRequest() && $this->request->isApiRequest() && ! $auth->isAuthenticated()) { $auth->authHttp(); } diff --git a/library/Icinga/Authentication/Auth.php b/library/Icinga/Authentication/Auth.php index 498495997..9723218b0 100644 --- a/library/Icinga/Authentication/Auth.php +++ b/library/Icinga/Authentication/Auth.php @@ -78,22 +78,6 @@ class Auth return new AuthChain(); } - /** - * Authenticate the user - * - * @return bool - */ - public function authenticate() - { - if ($this->user === null) { - $this->authenticateFromSession(); - } - if ($this->user === null && ! $this->authExternal()) { - return false; - } - return true; - } - /** * Get whether the user is authenticated * @@ -101,7 +85,14 @@ class Auth */ public function isAuthenticated() { - return $this->user !== null; + if ($this->user !== null) { + return true; + } + $this->authenticateFromSession(); + if ($this->user === null && ! $this->authExternal()) { + return false; + } + return true; } public function setAuthenticated(User $user, $persist = true) From 5797ce52d73c54b6e00cf2b29f583588634620d6 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Wed, 7 Dec 2016 15:15:06 +0100 Subject: [PATCH 323/337] Update changelog.py (includes ordering by category) --- changelog.py | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/changelog.py b/changelog.py index 7b34f9944..99ccf868a 100755 --- a/changelog.py +++ b/changelog.py @@ -16,7 +16,7 @@ # along with this program; if not, write to the Free Software Foundation # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -import urllib2, json, sys, string +import urllib2, json, sys, string, collections from argparse import ArgumentParser DESCRIPTION="update release changes" @@ -43,14 +43,14 @@ def format_header(text, lvl, ftype = ftype): def format_logentry(log_entry, args = args, issue_url = ISSUE_URL): if args.links: if args.html: - return "
  • {0} {1}: {2}
  • ".format(log_entry[0], log_entry[1], log_entry[2], issue_url) + return "
  • {0} {1} ({2}): {3}
  • ".format(log_entry[0], log_entry[1], log_entry[2], log_entry[3],issue_url) else: - return "* {0} [{1}]({3}{1} \"{0} {1}\"): {2}".format(log_entry[0], log_entry[1], log_entry[2], issue_url) + return "* {0} [{1}]({4}{1} \"{0} {1}\") ({2}): {3}".format(log_entry[0], log_entry[1], log_entry[2], log_entry[3], issue_url) else: if args.html: - return "
  • %s %d: %s
  • " % log_entry + return "
  • %s %d (%s): %s
  • " % log_entry else: - return "* %s %d: %s" % log_entry + return "* %s %d (%s): %s" % log_entry def print_category(category, entries): if len(entries) > 0: @@ -60,8 +60,11 @@ def print_category(category, entries): if args.html: print "
      " - for entry in sorted(entries): - print format_logentry(entry) + tmp_entries = collections.OrderedDict(sorted(entries.items())) + + for cat, entry_list in tmp_entries.iteritems(): + for entry in entry_list: + print format_logentry(entry) if args.html: print "
    " @@ -108,9 +111,10 @@ if changes: offset = 0 -features = [] -bugfixes = [] -support = [] +features = {} +bugfixes = {} +support = {} +category = "" while True: # We could filter using &cf_13=1, however this doesn't currently work because the custom field isn't set @@ -135,14 +139,29 @@ while True: if ignore_issue: continue - entry = (issue["tracker"]["name"], issue["id"], issue["subject"].strip()) + if "category" in issue: + category = str(issue["category"]["name"]) + else: + category = "no category" + + # the order is important for print_category() + entry = (issue["tracker"]["name"], issue["id"], category, issue["subject"].strip()) if issue["tracker"]["name"] == "Feature": - features.append(entry) + try: + features[category].append(entry) + except KeyError: + features[category] = [ entry ] elif issue["tracker"]["name"] == "Bug": - bugfixes.append(entry) + try: + bugfixes[category].append(entry) + except KeyError: + bugfixes[category] = [ entry ] elif issue["tracker"]["name"] == "Support": - support.append(entry) + try: + support[category].append(entry) + except KeyError: + support[category] = [ entry ] print_category("Feature", features) print_category("Bugfixes", bugfixes) From 7253fbcfa44d0d4f2d28d31a12b8a627bb092d02 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 7 Dec 2016 16:53:53 +0100 Subject: [PATCH 324/337] Command forms: make the defaults of some checkboxes configurable refs #11952 --- .../Command/Object/AcknowledgeProblemCommandForm.php | 9 +++++++-- .../forms/Command/Object/AddCommentCommandForm.php | 3 ++- .../Command/Object/ScheduleHostCheckCommandForm.php | 4 ++++ .../Command/Object/ScheduleHostDowntimeCommandForm.php | 3 +-- .../Command/Object/SendCustomNotificationCommandForm.php | 7 +++++-- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php b/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php index 32fb522e2..180719ca7 100644 --- a/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/AcknowledgeProblemCommandForm.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Monitoring\Forms\Command\Object; use DateTime; use DateInterval; +use Icinga\Application\Config; use Icinga\Module\Monitoring\Command\Object\AcknowledgeProblemCommand; use Icinga\Web\Notification; @@ -40,6 +41,8 @@ class AcknowledgeProblemCommandForm extends ObjectsCommandForm */ public function createElements(array $formData = array()) { + $config = Config::module('monitoring'); + $this->addElements(array( array( 'textarea', @@ -59,6 +62,7 @@ class AcknowledgeProblemCommandForm extends ObjectsCommandForm 'persistent', array( 'label' => $this->translate('Persistent Comment'), + 'value' => (bool) $config->get('settings', 'acknowledge_persistent', false), 'description' => $this->translate( 'If you would like the comment to remain even when the acknowledgement is removed, check this' . ' option.' @@ -70,6 +74,7 @@ class AcknowledgeProblemCommandForm extends ObjectsCommandForm 'expire', array( 'label' => $this->translate('Use Expire Time'), + 'value' => (bool) $config->get('settings', 'acknowledge_expire', false), 'description' => $this->translate( 'If the acknowledgement should expire, check this option.' ), @@ -109,7 +114,7 @@ class AcknowledgeProblemCommandForm extends ObjectsCommandForm 'sticky', array( 'label' => $this->translate('Sticky Acknowledgement'), - 'value' => false, + 'value' => (bool) $config->get('settings', 'acknowledge_sticky', false), 'description' => $this->translate( 'If you want the acknowledgement to remain until the host or service recovers even if the host' . ' or service changes state, check this option.' @@ -121,7 +126,7 @@ class AcknowledgeProblemCommandForm extends ObjectsCommandForm 'notify', array( 'label' => $this->translate('Send Notification'), - 'value' => true, + 'value' => (bool) $config->get('settings', 'acknowledge_notify', true), 'description' => $this->translate( 'If you do not want an acknowledgement notification to be sent out to the appropriate contacts,' . ' uncheck this option.' diff --git a/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php b/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php index 4d6124e0c..3cff2cf7c 100644 --- a/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/AddCommentCommandForm.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Monitoring\Forms\Command\Object; +use Icinga\Application\Config; use Icinga\Module\Monitoring\Command\Object\AddCommentCommand; use Icinga\Web\Notification; @@ -53,7 +54,7 @@ class AddCommentCommandForm extends ObjectsCommandForm 'persistent', array( 'label' => $this->translate('Persistent'), - 'value' => true, + 'value' => (bool) Config::module('monitoring')->get('settings', 'comment_persistent', true), 'description' => $this->translate( 'If you uncheck this option, the comment will automatically be deleted the next time Icinga is' . ' restarted.' diff --git a/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php b/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php index b11d2d2d2..6bbd1a7f4 100644 --- a/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/ScheduleHostCheckCommandForm.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Monitoring\Forms\Command\Object; +use Icinga\Application\Config; use Icinga\Module\Monitoring\Command\Object\ScheduleHostCheckCommand; use Icinga\Web\Notification; @@ -17,6 +18,8 @@ class ScheduleHostCheckCommandForm extends ScheduleServiceCheckCommandForm */ public function createElements(array $formData = array()) { + $config = Config::module('monitoring'); + parent::createElements($formData); $this->addElements(array( array( @@ -24,6 +27,7 @@ class ScheduleHostCheckCommandForm extends ScheduleServiceCheckCommandForm 'all_services', array( 'label' => $this->translate('All Services'), + 'value' => (bool) $config->get('settings', 'hostcheck_all_services', false), 'description' => $this->translate( 'Schedule check for all services on the hosts and the hosts themselves.' ) diff --git a/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php b/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php index d1f43972a..53c951644 100644 --- a/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/ScheduleHostDowntimeCommandForm.php @@ -29,8 +29,7 @@ class ScheduleHostDowntimeCommandForm extends ScheduleServiceDowntimeCommandForm 'description' => $this->translate( 'Schedule downtime for all services on the hosts and the hosts themselves.' ), - 'label' => $this->translate('All Services'), - 'value' => false + 'label' => $this->translate('All Services') ) ); diff --git a/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php b/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php index 0d04477f1..ba7a049f6 100644 --- a/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Monitoring\Forms\Command\Object; +use Icinga\Application\Config; use Icinga\Module\Monitoring\Command\Object\SendCustomNotificationCommand; use Icinga\Web\Notification; @@ -34,6 +35,8 @@ class SendCustomNotificationCommandForm extends ObjectsCommandForm */ public function createElements(array $formData = array()) { + $config = Config::module('monitoring'); + $this->addElements(array( array( 'textarea', @@ -53,7 +56,7 @@ class SendCustomNotificationCommandForm extends ObjectsCommandForm 'forced', array( 'label' => $this->translate('Forced'), - 'value' => false, + 'value' => (bool) $config->get('settings', 'custom_notification_forced', false), 'description' => $this->translate( 'If you check this option, the notification is sent out regardless of time restrictions and' . ' whether or not notifications are enabled.' @@ -68,7 +71,7 @@ class SendCustomNotificationCommandForm extends ObjectsCommandForm 'broadcast', array( 'label' => $this->translate('Broadcast'), - 'value' => false, + 'value' => (bool) $config->get('settings', 'custom_notification_broadcast', false), 'description' => $this->translate( 'If you check this option, the notification is sent out to all normal and escalated contacts.' ) From 0665ca387b95c5b99bf61c5bddbe9301fe8274d8 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 7 Dec 2016 17:08:13 +0100 Subject: [PATCH 325/337] Setup: show monitoring config summary even with Icinga 2 API as command transport fixes #13459 --- .../locale/de_DE/LC_MESSAGES/monitoring.mo | Bin 72184 -> 72483 bytes .../locale/de_DE/LC_MESSAGES/monitoring.po | 19 ++- .../library/Monitoring/TransportStep.php | 115 ++++++++++++------ 3 files changed, 90 insertions(+), 44 deletions(-) diff --git a/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.mo b/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.mo index 3560dc336d3fa3ec203b106a7adae4bf2bf9162e..8a508762f4579d036bd4a2d93e9b8449798a1ea5 100644 GIT binary patch delta 15593 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 v_PJMF+U9jtO8)VAAV+Fj*x@w9iB{;c^wItq=q delta 15527 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 AoB#j- diff --git a/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.po b/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.po index 60d02e456..adfad0abe 100644 --- a/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.po +++ b/modules/monitoring/application/locale/de_DE/LC_MESSAGES/monitoring.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: Monitoring Module (2.1.2)\n" "Report-Msgid-Bugs-To: dev@icinga.org\n" "POT-Creation-Date: 2016-02-29 14:40+0000\n" -"PO-Revision-Date: 2016-04-26 12:08+0200\n" +"PO-Revision-Date: 2016-12-07 17:02+0100\n" "Last-Translator: Thomas Gelf \n" "Language: de_DE\n" "MIME-Version: 1.0\n" @@ -16,7 +16,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "Language-Team: \n" -"X-Generator: Poedit 1.8.7.1\n" +"X-Generator: Poedit 1.8.11\n" #: /vagrant/modules/monitoring/application/controllers/ChartController.php:375 msgid " Down Hosts (Handled)" @@ -1816,7 +1816,16 @@ msgstr "" "Backend mit dem Namen “%s” und die weiter unten spezifizierte Ressourcen " "abrufen:" -#: /vagrant/modules/monitoring/library/Monitoring/TransportStep.php:76 +#: /vagrant/modules/monitoring/library/Monitoring/TransportStep.php:87 +msgid "" +"Icinga Web 2 will use the Icinga 2 API to send commands to your monitoring " +"instance by using the connection details listed below:" +msgstr "" +"Icinga Web 2 wird die Icinga 2 API verwenden, um Kommandos an Ihre " +"Monitoringinstanz zu senden. Dazu werden die folgenden " +"Verbindungseinstellungen verwendet:" + +#: /vagrant/modules/monitoring/library/Monitoring/TransportStep.php:49 #, php-format msgid "" "Icinga Web 2 will use the named pipe located at \"%s\" to send commands to " @@ -1919,8 +1928,8 @@ msgstr "" #: /vagrant/modules/monitoring/application/forms/Command/Object/SendCustomNotificationCommandForm.php:45 msgid "" "If you work with other administrators, you may find it useful to share " -"information about the host or service that is having problems. Make sure " -"you enter a brief description of what you are doing." +"information about the host or service that is having problems. Make sure you " +"enter a brief description of what you are doing." msgstr "" "Wenn Sie mit anderen Administratoren zusammenarbeiten, werden Sie es " "nützlich finden, Informationen zu Hosts oder Services mit Problemen " diff --git a/modules/monitoring/library/Monitoring/TransportStep.php b/modules/monitoring/library/Monitoring/TransportStep.php index 76f2d18e9..d138eb407 100644 --- a/modules/monitoring/library/Monitoring/TransportStep.php +++ b/modules/monitoring/library/Monitoring/TransportStep.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Monitoring; use Exception; +use Icinga\Exception\ProgrammingError; use Icinga\Module\Setup\Step; use Icinga\Application\Config; use Icinga\Exception\IcingaException; @@ -40,47 +41,83 @@ class TransportStep extends Step public function getSummary() { - $pageTitle = '

    ' . mt('monitoring', 'Command Transport', 'setup.page.title') . '

    '; - - if (isset($this->data['transportConfig']['host'])) { - $pipeHtml = '

    ' . sprintf( - mt( - 'monitoring', - 'Icinga Web 2 will use the named pipe located on a remote machine at "%s" to send commands' - . ' to your monitoring instance by using the connection details listed below:' - ), - $this->data['transportConfig']['path'] - ) . '

    '; - - $pipeHtml .= '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '
    ' . mt('monitoring', 'Remote Host') . '' . $this->data['transportConfig']['host'] . '
    ' . mt('monitoring', 'Remote SSH Port') . '' . $this->data['transportConfig']['port'] . '
    ' . mt('monitoring', 'Remote SSH User') . '' . $this->data['transportConfig']['user'] . '
    '; - } else { - $pipeHtml = '

    ' . sprintf( - mt( - 'monitoring', - 'Icinga Web 2 will use the named pipe located at "%s"' - . ' to send commands to your monitoring instance.' - ), - $this->data['transportConfig']['path'] - ) . '

    '; + switch ($this->data['transportConfig']['transport']) { + case 'local': + $details = '

    ' . sprintf( + mt( + 'monitoring', + 'Icinga Web 2 will use the named pipe located at "%s"' + . ' to send commands to your monitoring instance.' + ), + $this->data['transportConfig']['path'] + ) . '

    '; + break; + case 'remote': + $details = '

    ' + . sprintf( + mt( + 'monitoring', + 'Icinga Web 2 will use the named pipe located on a remote machine at "%s" to send commands' + . ' to your monitoring instance by using the connection details listed below:' + ), + $this->data['transportConfig']['path'] + ) + . '

    ' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '
    ' . mt('monitoring', 'Remote Host') . '' . $this->data['transportConfig']['host'] . '
    ' . mt('monitoring', 'Remote SSH Port') . '' . $this->data['transportConfig']['port'] . '
    ' . mt('monitoring', 'Remote SSH User') . '' . $this->data['transportConfig']['user'] . '
    '; + break; + case 'api': + $details = '

    ' + . mt( + 'monitoring', + 'Icinga Web 2 will use the Icinga 2 API to send commands' + . ' to your monitoring instance by using the connection details listed below:' + ) + . '

    ' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '
    ' . mt('monitoring', 'Host') . '' . $this->data['transportConfig']['host'] . '
    ' . mt('monitoring', 'Port') . '' . $this->data['transportConfig']['port'] . '
    ' . mt('monitoring', 'Username') . '' . $this->data['transportConfig']['username'] . '
    ' . mt('monitoring', 'Password') . '' . str_repeat('*', strlen($this->data['transportConfig']['password'])) . '
    '; + break; + default: + throw new ProgrammingError( + 'Unknown command transport type: %s', + $this->data['transportConfig']['transport'] + ); } - return $pageTitle . '
    ' . $pipeHtml . '
    '; + return '

    ' . mt('monitoring', 'Command Transport', 'setup.page.title') . '

    ' + . '
    ' . $details . '
    '; } public function getReport() From 69c8ed471b60442557607c650f3f4e8dc82dcef5 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 7 Dec 2016 18:26:45 +0100 Subject: [PATCH 326/337] FilterEditor: add possibility to fold and unfold filter by click refs #12634 Signed-off-by: Alexander A. Klimov --- library/Icinga/Web/Widget/FilterEditor.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Web/Widget/FilterEditor.php b/library/Icinga/Web/Widget/FilterEditor.php index 28e49688b..679fddc90 100644 --- a/library/Icinga/Web/Widget/FilterEditor.php +++ b/library/Icinga/Web/Widget/FilterEditor.php @@ -749,9 +749,12 @@ class FilterEditor extends AbstractWidget $title .= ': ' . $this->view()->escape($this->filter); } } + + $addOrPurgeModifyFilter = !($this->preservedUrl()->getParam('modifyFilter')); + return $html . ' Date: Wed, 7 Dec 2016 18:33:26 +0100 Subject: [PATCH 327/337] Conform to coding guidelines refs #12634 --- library/Icinga/Web/Widget/FilterEditor.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/Icinga/Web/Widget/FilterEditor.php b/library/Icinga/Web/Widget/FilterEditor.php index 679fddc90..3b80f1cce 100644 --- a/library/Icinga/Web/Widget/FilterEditor.php +++ b/library/Icinga/Web/Widget/FilterEditor.php @@ -735,8 +735,10 @@ class FilterEditor extends AbstractWidget public function renderSearch() { + $preservedUrl = $this->preservedUrl(); + $html = ' '; @@ -749,12 +751,10 @@ class FilterEditor extends AbstractWidget $title .= ': ' . $this->view()->escape($this->filter); } } - - $addOrPurgeModifyFilter = !($this->preservedUrl()->getParam('modifyFilter')); - + return $html . '' . $output . ''; } else { $output = '
    ' . $output . '
    '; @@ -91,32 +93,60 @@ class Zend_View_Helper_PluginOutput extends Zend_View_Helper_Abstract } /** - * Replace classic Icinga CGI links with Icinga Web 2 links + * Replace classic Icinga CGI links with Icinga Web 2 links and color state information, if any * * @param string $html * * @return string */ - protected function fixLinks($html) + protected function processHtml($html) { - $dom = new DOMDocument(); - $dom->loadXML('
    ' . $html . '
    ', LIBXML_NOERROR | LIBXML_NOWARNING); - $dom->preserveWhiteSpace = false; - $links = $dom->getElementsByTagName('a'); - foreach ($links as $link) { - /** @var \DOMElement $link */ - $href = $link->getAttribute('href'); - if (preg_match('~^/cgi\-bin/status\.cgi\?(.+)$~', $href, $m)) { - parse_str($m[1], $params); - if (isset($params['host'])) { - $link->setAttribute('href', $this->view->baseUrl( - '/monitoring/host/show?host=' . urlencode($params['host'] - ))); + $pattern = '/[([](OK|WARNING|CRITICAL|UNKNOWN)[)\]]/'; + $doc = new DOMDocument(); + $doc->loadXML('
    ' . $html . '
    ', LIBXML_NOERROR | LIBXML_NOWARNING); + $dom = new RecursiveIteratorIterator(new DomNodeIterator($doc), RecursiveIteratorIterator::SELF_FIRST); + $nodesToRemove = array(); + foreach ($dom as $node) { + /** @var \DOMNode $node */ + if ($node->nodeType === XML_TEXT_NODE) { + $start = 0; + while (preg_match($pattern, $node->nodeValue, $match, PREG_OFFSET_CAPTURE, $start)) { + $offsetLeft = $match[0][1]; + $matchLength = strlen($match[0][0]); + $leftLength = $offsetLeft - $start; + if ($leftLength) { + $text = new DOMText(substr($node->nodeValue, $start, $leftLength)); + $node->parentNode->insertBefore($text, $node); + } + $span = $doc->createElement('span', $match[0][0]); + $span->setAttribute('class', 'state-' . strtolower($match[1][0])); + $node->parentNode->insertBefore($span, $node); + $start = $offsetLeft + $matchLength; + } + if ($start) { + $nodesToRemove[] = $node; + } + } elseif ($node->nodeType === XML_ELEMENT_NODE) { + /** @var \DOMElement $node */ + if ($node->tagName === 'a' + && preg_match('~^/cgi\-bin/status\.cgi\?(.+)$~', $node->getAttribute('href'), $match) + ) { + parse_str($match[1], $params); + if (isset($params['host'])) { + $node->setAttribute( + 'href', + $this->view->baseUrl('/monitoring/host/show?host=' . urlencode($params['host'])) + ); + } } } } + foreach ($nodesToRemove as $node) { + /** @var \DOMNode $node */ + $node->parentNode->removeChild($node); + } - return substr($dom->saveHTML(), 5, -7); + return substr($doc->saveHTML(), 5, -7); } /** From 5661dfa63ce3dac4666536686101706cb1a64e1c Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 7 Dec 2016 12:31:01 +0100 Subject: [PATCH 333/337] Harden import of vendor JavaSript libs fixes #12328 --- library/Icinga/Web/JavaScript.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/JavaScript.php b/library/Icinga/Web/JavaScript.php index e6278c72f..76c25def8 100644 --- a/library/Icinga/Web/JavaScript.php +++ b/library/Icinga/Web/JavaScript.php @@ -117,7 +117,7 @@ class JavaScript // We do not minify vendor files foreach ($vendorFiles as $file) { - $out .= file_get_contents($file); + $out .= ';' . ltrim(trim(file_get_contents($file)), ';') . "\n"; } foreach ($jsFiles as $file) { From 9bff4dd83b8beb991fa8d1e61b8a336663763579 Mon Sep 17 00:00:00 2001 From: Florian Strohmaier Date: Mon, 5 Dec 2016 16:35:23 +0100 Subject: [PATCH 334/337] Enable horizontal scrolling for perfdata table Signed-off-by: Eric Lippmann fixes #11766 --- .../monitoring/application/views/helpers/Perfdata.php | 2 +- modules/monitoring/public/css/tables.less | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/views/helpers/Perfdata.php b/modules/monitoring/application/views/helpers/Perfdata.php index 06443b8f1..6b9113830 100644 --- a/modules/monitoring/application/views/helpers/Perfdata.php +++ b/modules/monitoring/application/views/helpers/Perfdata.php @@ -81,7 +81,7 @@ class Zend_View_Helper_Perfdata extends Zend_View_Helper_Abstract $data []= sprintf( '%s', $text, - StringHelper::ellipsisCenter($text, 24) + $text ); } } diff --git a/modules/monitoring/public/css/tables.less b/modules/monitoring/public/css/tables.less index 5f0d4f49a..a304c666d 100644 --- a/modules/monitoring/public/css/tables.less +++ b/modules/monitoring/public/css/tables.less @@ -108,7 +108,9 @@ // Table for performance data in detail views .performance-data-table { - width: 100%; + display: block; + overflow-x: auto; + position: relative; > thead > tr > th { text-align: left; @@ -119,6 +121,11 @@ // Reset base padding padding-left: 0; } + + > thead > tr > th, + > tbody > tr > td { + white-space: nowrap; + } } // Performance data table column for sparkline pie charts in detail views From 2156eb1a8ca14d9de1d421f84f1f8a97dab5c9d1 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 7 Dec 2016 13:28:33 +0100 Subject: [PATCH 335/337] Remove gap between header and content on site reloads --- public/css/icinga/layout-structure.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/css/icinga/layout-structure.less b/public/css/icinga/layout-structure.less index 44d0a4dd7..6bca47f25 100644 --- a/public/css/icinga/layout-structure.less +++ b/public/css/icinga/layout-structure.less @@ -24,7 +24,7 @@ html { bottom: 0; left: 0; position: fixed; - top: 4em; + top: 3em; width: 12em; } @@ -33,7 +33,7 @@ html { left: 12em; position: fixed; right: 0; - top: 4em; + top: 3em; } .iframe { From 4f6c54e62cb88496d3de5e977a89b13e13c1caa0 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 7 Dec 2016 15:54:56 +0100 Subject: [PATCH 336/337] Support scheduling a downtime for all services of a host w/ the Icinga 2 API as command transport fixes #12810 --- .../Monitoring/Command/IcingaApiCommand.php | 40 +++++++++++++++++++ .../Renderer/IcingaApiCommandRenderer.php | 26 +++++++++--- .../Command/Transport/ApiCommandTransport.php | 29 +++++++++----- 3 files changed, 78 insertions(+), 17 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Command/IcingaApiCommand.php b/modules/monitoring/library/Monitoring/Command/IcingaApiCommand.php index b14f96bc4..c33157f65 100644 --- a/modules/monitoring/library/Monitoring/Command/IcingaApiCommand.php +++ b/modules/monitoring/library/Monitoring/Command/IcingaApiCommand.php @@ -19,6 +19,13 @@ class IcingaApiCommand */ protected $endpoint; + /** + * Next Icinga API command to be sent, if any + * + * @var static + */ + protected $next; + /** * Create a new Icinga 2 API command * @@ -83,4 +90,37 @@ class IcingaApiCommand return $this; } + + /** + * Get whether another Icinga API command should be sent after this one + * + * @return bool + */ + public function hasNext() + { + return $this->next !== null; + } + + /** + * Get the next Icinga API command + * + * @return IcingaApiCommand + */ + public function getNext() + { + return $this->next; + } + + /** + * Set the next Icinga API command + * + * @param IcingaApiCommand $next + * + * @return IcingaApiCommand + */ + public function setNext(IcingaApiCommand $next) + { + $this->next = $next; + return $next; + } } diff --git a/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php b/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php index 7f6b2956f..30dc414dc 100644 --- a/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php +++ b/modules/monitoring/library/Monitoring/Command/Renderer/IcingaApiCommandRenderer.php @@ -12,6 +12,7 @@ use Icinga\Module\Monitoring\Command\Object\DeleteDowntimeCommand; use Icinga\Module\Monitoring\Command\Object\ProcessCheckResultCommand; use Icinga\Module\Monitoring\Command\Object\PropagateHostDowntimeCommand; use Icinga\Module\Monitoring\Command\Object\RemoveAcknowledgementCommand; +use Icinga\Module\Monitoring\Command\Object\ScheduleHostDowntimeCommand; use Icinga\Module\Monitoring\Command\Object\ScheduleServiceCheckCommand; use Icinga\Module\Monitoring\Command\Object\ScheduleServiceDowntimeCommand; use Icinga\Module\Monitoring\Command\Object\SendCustomNotificationCommand; @@ -149,14 +150,27 @@ class IcingaApiCommandRenderer implements IcingaCommandRendererInterface 'fixed' => $command->getFixed(), 'trigger_name' => $command->getTriggerId() ); - if ($command->getObject()->getType() === $command::TYPE_HOST - && $command instanceof PropagateHostDowntimeCommand - ) { + $commandData = $data; + if ($command instanceof PropagateHostDowntimeCommand) { /** @var \Icinga\Module\Monitoring\Command\Object\PropagateHostDowntimeCommand $command */ - $data['child_options'] = $command->getTriggered() ? 1 : 2; + $commandData['child_options'] = $command->getTriggered() ? 1 : 2; } - $this->applyFilter($data, $command->getObject()); - return IcingaApiCommand::create($endpoint, $data); + $this->applyFilter($commandData, $command->getObject()); + $apiCommand = IcingaApiCommand::create($endpoint, $commandData); + if ($command instanceof ScheduleHostDowntimeCommand + /** @var \Icinga\Module\Monitoring\Command\Object\ScheduleHostDowntimeCommand $command */ + && $command->getForAllServices() + ) { + $commandData = $data + array( + 'type' => 'Service', + 'filter' => 'host.name == host_name', + 'filter_vars' => array( + 'host_name' => $command->getObject()->getName() + ) + ); + $apiCommand->setNext(IcingaApiCommand::create($endpoint, $commandData)); + } + return $apiCommand; } public function renderAcknowledgeProblem(AcknowledgeProblemCommand $command) diff --git a/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php b/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php index 1058e9178..8b00bd1b6 100644 --- a/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php +++ b/modules/monitoring/library/Monitoring/Command/Transport/ApiCommandTransport.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Monitoring\Command\Transport; use Icinga\Application\Logger; +use Icinga\Module\Monitoring\Command\IcingaApiCommand; use Icinga\Module\Monitoring\Command\IcingaCommand; use Icinga\Module\Monitoring\Command\Renderer\IcingaApiCommandRenderer; use Icinga\Module\Monitoring\Exception\CommandTransportException; @@ -184,17 +185,8 @@ class ApiCommandTransport implements CommandTransportInterface return sprintf('https://%s:%u/v1/%s', $this->getHost(), $this->getPort(), $endpoint); } - /** - * Send the Icinga command over the Icinga 2 API - * - * @param IcingaCommand $command - * @param int|null $now - * - * @throws CommandTransportException - */ - public function send(IcingaCommand $command, $now = null) + protected function sendCommand(IcingaApiCommand $command) { - $command = $this->renderer->render($command); Logger::debug( 'Sending Icinga command "%s" to the API "%s:%u"', $command->getEndpoint(), @@ -215,7 +207,6 @@ class ApiCommandTransport implements CommandTransportInterface ); } $result = array_pop($response['results']); - if ($result['code'] < 200 || $result['code'] >= 300) { throw new CommandTransportException( 'Can\'t send external Icinga command: %u %s', @@ -223,5 +214,21 @@ class ApiCommandTransport implements CommandTransportInterface $result['status'] ); } + if ($command->hasNext()) { + $this->sendCommand($command->getNext()); + } + } + + /** + * Send the Icinga command over the Icinga 2 API + * + * @param IcingaCommand $command + * @param int|null $now + * + * @throws CommandTransportException + */ + public function send(IcingaCommand $command, $now = null) + { + $this->sendCommand($this->renderer->render($command)); } } From 1b6e7177a38ae4d2328b5acd1feec7a9ee19d79a Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 7 Dec 2016 16:43:43 +0100 Subject: [PATCH 337/337] Allow to export the host and service detail views to JSON resolves #12820 --- .../Controller/MonitoredObjectController.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php b/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php index f4936fd99..9ea05f02b 100644 --- a/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php +++ b/modules/monitoring/library/Monitoring/Web/Controller/MonitoredObjectController.php @@ -75,6 +75,7 @@ abstract class MonitoredObjectController extends Controller } } $this->object->populate(); + $this->handleFormatRequest(); $toggleFeaturesForm = new ToggleObjectFeaturesCommandForm(array( 'backend' => $this->backend, 'objects' => $this->object @@ -134,6 +135,26 @@ abstract class MonitoredObjectController extends Controller return $form; } + /** + * Export to JSON if requested + */ + protected function handleFormatRequest($query = null) + { + if ($this->params->get('format') === 'json' + || $this->getRequest()->getHeader('Accept') === 'application/json' + ) { + $payload = (array) $this->object->properties; + $payload += array( + 'contacts' => $this->object->contacts->fetchPairs(), + 'contact_groups' => $this->object->contactgroups->fetchPairs(), + 'vars' => $this->object->customvars + ); + $groupName = $this->object->getType() . 'groups'; + $payload[$groupName] = $this->object->$groupName; + $this->getResponse()->json()->setSuccessData($payload)->sendResponse(); + } + } + /** * Acknowledge a problem */